一、总线模型(转自国嵌论坛)
1.随着技术的进步,对热插拔的要求以及可移植性的要求越来越高,从Linux2.4开始虽然有了模型但是正式提出是在Linux2.6。
2.关键词是总线,驱动,设备
3.总线能够感知设备的插拔:
(1)插入新设备的时候知道有设备插入,那么就去总线上已有的驱动里面查找能够处理该新设备的驱动,一旦匹配,该驱动就有了该设备的控制权
(2)拔出的时候,总线也能感知,并且告诉相应的驱动从而使得驱动能够处理拔出事件
二、总线
1、描述结构
linux内核中总线由bus_type结构描述
struct bus_type { const char *name; struct bus_attribute *bus_attrs; struct device_attribute *dev_attrs; struct driver_attribute *drv_attrs; int (*match)(struct device *dev, struct device_driver *drv); int (*uevent)(struct device *dev, struct kobj_uevent_env *env); int (*probe)(struct device *dev); int (*remove)(struct device *dev); void (*shutdown)(struct device *dev); int (*suspend)(struct device *dev, pm_message_t state); int (*resume)(struct device *dev); const struct dev_pm_ops *pm; struct subsys_private *p; };
2、重要成员
a、 const char *name; 总线名称
b、 int (*match)(struct device *dev, struct device_driver *drv); match函数,用来匹配挂载在总线上的设备与驱动
3、注册总线
函数:bus_register (struct bus_type*)
若成功,新总线被添加进系统,可在/sys/bus目录下查看到相应目录
4、注销总线
函数:bus_unregister (struct bus_type*)
三、驱动
1、描述结构
device_driver
struct device_driver { const char *name; struct bus_type *bus; struct module *owner; const char *mod_name; /* used for built-in modules */ bool suppress_bind_attrs; /* disables bind/unbind via sysfs */ const struct of_device_id *of_match_table; int (*probe) (struct device *dev); int (*remove) (struct device *dev); void (*shutdown) (struct device *dev); int (*suspend) (struct device *dev, pm_message_t state); int (*resume) (struct device *dev); const struct attribute_group **groups; const struct dev_pm_ops *pm; struct driver_private *p; };
2、重要成员
a、const char *name; 驱动名称
b、struct bus_type *bus; 要挂载到的总线名称
c、 int (*probe) (struct device *dev); 当驱动和设备匹配成功时调用这个函数
3、注册
函数:driver_register (struct bus_type*)
若成功,新驱动被添加进系统,可在对应总线目录下的drivers查看到相应目录
4、注销
函数:driver_unregister (struct bus_type*)
四、设备
1、描述结构device
struct device { struct device *parent; struct device_private *p; struct kobject kobj; const char *init_name; /* initial name of the device */ struct device_type *type; struct mutex mutex; /* mutex to synchronize calls to * its driver. */ struct bus_type *bus; /* type of bus device is on */ struct device_driver *driver; /* which driver has allocated this device */ void *platform_data; /* Platform specific data, device core doesn't touch it */ struct dev_pm_info power; struct dev_power_domain *pwr_domain; #ifdef CONFIG_NUMA int numa_node; /* NUMA node this device is close to */ #endif u64 *dma_mask; /* dma mask (if dma'able device) */ u64 coherent_dma_mask;/* Like dma_mask, but for alloc_coherent mappings as not all hardware supports 64 bit addresses for consistent allocations such descriptors. */ struct device_dma_parameters *dma_parms; struct list_head dma_pools; /* dma pools (if dma'ble) */ struct dma_coherent_mem *dma_mem; /* internal for coherent mem override */ /* arch specific additions */ struct dev_archdata archdata; struct device_node *of_node; /* associated device tree node */ dev_t devt; /* dev_t, creates the sysfs "dev" */ spinlock_t devres_lock; struct list_head devres_head; struct klist_node knode_class; struct class *class; const struct attribute_group **groups; /* optional groups */ void (*release)(struct device *dev); };
2、重要成员
a、const char *init_name; 设备名称
b、struct bus_type *bus; 要挂载到的总线名称
3、注册
函数:device_register (struct bus_type*)
若成功,新驱动被添加进系统,可在对应总线目录下的drivers查看到相应目录
4、注销
函数:device_unregister (struct bus_type*)
五、实例验证将驱动和设备挂在到总线中并进行匹配
1、当实际硬件设备和驱动程序进行匹配时会通过设备ID等来完成,我们此处所使用的是一个模拟的硬件,没有设备ID,所以用设备名称和驱动名称来匹配,这就要求我们在初始化device_driver和device时两者名字要一致。
struct device wsk_dev = { .init_name = "my driver", .bus = &bus, };
struct device_driver dev = { .name = "my driver", .bus = &bus, .probe = my_probe, };
2、match函数实现
/*设备匹配函数*/ int my_match(struct device *dev, struct device_driver *drv) { return !strncmp(dev->kobj.name,drv->name,strlen(dev->kobj.name)); }
这里将驱动名称和设备名称进行字符串比较,若一致strncmp返回0表示成功,但是match返回值非0才会调用probe函数,所以在前面加上叹号。
注意这里使用的是dev->kobj.name而不是dev->init_name,原因是在注册设备函数中将dev->init_name赋值给了dev->kobj.name,而将dev->init_name清空,所以此时dev->init_name是一个空指针。以下是内核代码中清空的过程。
所以用 dev->init_name进行匹配时会提示使用空指针从而导致内核崩溃。
3、符号导出
定义设备和驱动描述结构时要初始化总线名称这个成员,要用到bus.c文件里的bus符号,所以要将它导出,然后在driver.c和device.c中将它输出
4、probe函数
打印一串信息来提示匹配成功
int my_probe (struct device *dev) { printk("driver find the device is can handle "); return 0; }
关于probe函数在什么时候被谁调用这个问题,引用一篇博文,里面解释得很清楚了
链接:http://www.cnblogs.com/hoys/archive/2011/04/01/2002299.html
bus.c:
#include <linux/module.h> #include <linux/init.h> #include <linux/device.h> /*设备匹配函数*/ int my_match(struct device *dev, struct device_driver *drv) { return !strncmp(dev->kobj.name,drv->name,strlen(dev->kobj.name)); } /*定义总线设备*/ struct bus_type bus = { .name = "my bus", .match = my_match, }; static int bus_init() { /*注册总线设备驱动*/ bus_register(&bus); return 0; } void bus_exit() { /*注销总线设备驱动*/ bus_unregister(&bus); } MODULE_LICENSE("GPL"); EXPORT_SYMBOL(bus); module_init(bus_init); module_exit(bus_exit);
driver.c:
#include <linux/module.h> #include <linux/init.h> #include <linux/device.h> extern struct bus_type bus; int my_probe (struct device *dev) { printk("driver find the device is can handle "); return 0; } struct device_driver dev = { .name = "my driver", .bus = &bus, .probe = my_probe, }; static int my_driver_init() { /*注册驱动*/ driver_register(&dev); return 0; } void my_driver_exit() { /*注销驱动*/ driver_unregister(&dev); } MODULE_LICENSE("GPL"); module_init(my_driver_init); module_exit(my_driver_exit);
devices.c:
#include <linux/module.h> #include <linux/init.h> #include <linux/device.h> extern struct bus_type bus; struct device wsk_dev = { .init_name = "my driver", .bus = &bus, }; static int my_device_init() { /*注册设备*/ device_register(&wsk_dev); return 0; } void my_device_exit() { /*注销设备*/ device_unregister(&wsk_dev); } MODULE_LICENSE("GPL"); module_init(my_device_init); module_exit(my_device_exit);
如果有疑问或建议,欢迎指出。