Linux总线驱动模型主要在2.6以后内核提出
功能:主要对热插拔、跨平台移植性的要求支持
当外部设备接入总线后,总线会与挂载到总线上的每个驱动一一进行匹配,如果匹配上就调用被匹配上的驱动。
Linux内核中,总线由bos_type结构表示,定义在<linux/device.h>
struct bus_type
{
const char *name; 总线名称
int (*match)(struct device*dev,stuuctdevice_driver*drv);驱动与设备的匹配函数
......
}
int (*match)(structdevice*dev,stuuctdevice_driver*drv)
当一个新设备或者新驱动被添加到这个总线时,该函数被调用。用于判断指定的驱动程序是否能处理指定的设备。若可以,则返回非0.
总线的注册使用如下函数:
bus_register(struct bus_type*bus)
若成功,新的总线将被添加进系统,并可在/sys/bus下看到相应的目录。
/sys/bus目录下面存放的是总线文件
在相应总线文件里面存放有devices、drivers两个文件
devices文件存放挂载到此总线上的设备
drivers文件存放挂载到此总线上的驱动
总线的注销使用:
void bus_unregister(structbus_type*bus)
Linux内核中,驱动由device_driver结构体表示
struct device_driver
{
const char *name; 驱动名称
struct bus_type *bus;驱动程序所在的总线
int (*probe)(struct device*dev);当总线上的设备与此驱动匹配上就调用此函数,处理匹配上的设备
......
}
驱动的注册使用如下函数:
int driver_register(structdevice_driver*drv)
驱动的注销使用:
void driver_uinregister(structdevice_driver*drv)
Linux内核中,设备由struct device结构表示
struct device
{
const char *init_name;设备的名字
struct bus tpye *bus; 设备所在的总线
.......
}
当给init_name赋值后内核会把init_name的值赋给dev->kobj.name变量,并且把init_name赋值为NULL,所以在match函数中要使用dev->kobj.name与驱动名匹配
设备的注册使用如下函数
int device_register(structdevice*dev)
设备的注销使用
void device_unregister(structdevice*dev)
挂载到同一总线的设备名与驱动名需要一样,才能使用(*match)(structdevice*dev,stuuctdevice_driver*drv)函数通过通过匹配他们的名字匹配上
虚拟设备一般使用名字匹配
USB等设备使用设备特定硬件ID匹配
bus.c:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
/* 需要遵循GPL协议才能调用bus_register、bus_unregister函数 */
MODULE_LICENSE("GPL");
/* 驱动与设备的匹配函数:
当给init_name赋值后内核会把init_name的值赋给dev->kobj.name
并且把init_name赋值为NULL,所以在match函数中要使用dev->kobj.name
与驱动名匹配*/
int my_match(struct device *dev, struct device_driver *drv)
{
return !strncmp(dev->kobj.name,drv->name,strlen(drv->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);
device.c:
#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;
/* 当给init_name赋值后内核会把init_name的值赋给dev->kobj.name
并且把init_name赋值为NULL,所以在match函数中要使用dev->kobj.name
与驱动名匹配*/
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);
driver.c:
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
MODULE_LICENSE("GPL");
//extern指明后面的变量来自外部
extern struct bus_type my_bus_type; //调用bus.c文件中声明过的总线变量
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);