摘要
随着技术的不断进步,系统的拓扑结构也越来越复杂,对热插拔、跨平台移植性的要求也越来越高,2.4内核已经难以满足这些需求。为适应这种形势的需要,从Linux 2.6内核开始提供了全新的设备模型。
ps:热插拔即带电插拔,允许热插拔就是允许用户在不关闭系统,不切断电源的情况下插拔硬件。插上硬件时,系统驱动需要识别出该硬件,拔下硬件时也需要识别。
总线设备驱动模型的要素有三个:总线、设备、驱动。
总线设备驱动模型:有一根总线,总线上挂载着多个设备驱动。当有硬件插入该总线的时候,总线根据它挂载着的驱动去一一匹配处理该硬件,当某个设备驱动匹配该硬件时,总线就调用该设备驱动。当硬件拔下时,总线根据驱动做出相应的处理。ps:总线先挂载有设备,再有驱动添加到总线上时也会进行匹配
以usb总线为例。假设usb总线上挂载着多个驱动,如鼠标驱动,键盘驱动,网卡驱动。当接入usb网卡时,usb总线就根据它挂载的驱动程序去匹配网卡,直至匹配到网卡驱动并调用网卡驱动。
总线
在Linux 内核中,总线由 bus_type 结构表示,该结构定义在<linux/device.h>
。bus_type 结构含有多个结构成员,重要的成员有name和match。
struct bus_type
{
const char *name; /*总线名称*/
int (*match) (struct device *dev, struct device_driver *drv); /*驱动与设备的匹配函数*/
…
}
name为总线名称;match为函数指针,指向的函数的功能为匹配设备与驱动,需要程序员自己编写。当有新设备或者新驱动添加到该总线时,match函数被调用,用于判定指定的程序能够处理指定的设备,若可以,则返回非零。
不同的总线匹配驱动和设备的方法不同。如在usb总线中,每一个usb设备里面都有相应的设备标识id,当有usb设备挂载时,usb总线可通过设备标识id来进行匹配;而对于一些虚拟总线,一般给设备取名时,让其名称与该设备要匹配的驱动名称一样,总线则可通过名称来进行匹配,以下的程序为虚拟总线的程序。
总线的注册使用如下函数:bus_register(struct bus_type *bus)
若成功,新的总线将被添加进系统,并可在/sys/bus 下看到相应的目录。
总线的注销使用:void bus_unregister(struct bus_type *bus)
创建一条总线:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
MODULE_LICENSE("GPL");
int my_match(struct device *dev, struct device_driver *drv)
{
return !strncmp(dev->kobj.name,drv->name,strlen(drv->name));
/*匹配dev->kobj.name和drv->name是否一致,其中dev->kobj.name实质为dev->init_name*/
}
struct bus_type my_bus_type = {
.name = "my_bus",
.match = my_match,
};
EXPORT_SYMBOL(my_bus_type);
int my_bus_init()
{
int ret;
ret = bus_register(&my_bus_type);
return ret;
}
void my_bus_exit()
{
bus_unregister(&my_bus_type);
}
module_init(my_bus_init);
module_exit(my_bus_exit);
驱动
在 Linux内核中,驱动由 device_driver结构表示,该结构拥有多个成员,其中重要的成员有:
struct device_driver
{
const char *name; /*驱动名称*/
struct bus_type *bus; /*驱动程序所在的总线*/
int (*probe)(struct device *dev);
…
}
probe为函数指针。在总线驱动中,总线会一一匹配设备与驱动,当该驱动与某一设备匹配成功后,总线会调用probe指向的函数,该函数的功能需要包括硬件初始化等。
驱动的注册使用:int driver_register(struct device_driver *drv)
驱动的注销使用:void driver_unregister(struct device_driver *drv)
在总线上挂载一个驱动程序:
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
MODULE_LICENSE("GPL");
extern struct bus_type my_bus_type;
int my_probe(struct device *dev)
{
printk("driver found the device it can handle!\n");
return 0;
}
struct device_driver my_driver = {
.name = "my_dev",
.bus = &my_bus_type,
.probe = my_probe,
};
int my_driver_init()
{
int ret;
ret = driver_register(&my_driver);
return ret;
}
void my_driver_exit()
{
driver_unregister(&my_driver);
}
module_init(my_driver_init);
module_exit(my_driver_exit);
设备
在Linux内核中,设备由struct device结构表示。重要的成员有:
struct device
{
const char *init_name; /*设备的名字*/
struct bus_type *bus; /*设备所在的总线*/
…
}
init_name为设备名字,虚拟总线中,设备名字需要与驱动名字一样。
设备的注册使用:int device_register(struct device *dev)
设备的注销使用:void device_unregister(struct device *dev)
在总线上挂载一个设备:
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
MODULE_LICENSE("GPL");
extern struct bus_type my_bus_type;
struct device my_dev = {
.init_name = "my_dev",
.bus = &my_bus_type,
};
int my_device_init()
{
int ret;
ret = device_register(&my_dev);
return ret;
}
void my_device_exit()
{
device_unregister(&my_dev);
}
module_init(my_device_init);
module_exit(my_device_exit);
当先安装总线驱动,再安装驱动程序,最后挂载设备,则会调用函数my_probe,即会打印driver found the device it can handle;
当先安装总线驱动,接着挂载设备,然后才安装驱动程序,依然会调用函数my_probe,即会打印driver found the device it can handle。
由此可知,无论是往总线上添加设备还是添加驱动,总线都会执行匹配函数去匹配新的驱动/设备。