#include <linux/init.h> #include <linux/module.h> #include <linux/device.h> #include <linux/string.h> MODULE_LICENSE("GPL"); static int my_bus_match(struct device *dev, struct device_driver *drv) { return !strncmp(dev_name(dev), drv->name, strlen(drv->name)); } struct bus_type my_bus_type = { .name = "my_bus", .match = my_bus_match, }; EXPORT_SYMBOL_GPL(my_bus_type); static void my_bus_device_release(struct device *dev) { printk("My bus device released."); } struct device my_bus_device = { .init_name = "my_bus_device", .release = my_bus_device_release, }; EXPORT_SYMBOL_GPL(my_bus_device); static int __init my_bus_init(void) { int ret = 0; ret = bus_register(&my_bus_type); if (ret) { printk("Can't register my bus!\n"); return ret; } ret = device_register(&my_bus_device); if (ret) { printk("Can't register my bus device!\n"); bus_unregister(&my_bus_type); } return ret; } static void __exit my_bus_exit(void) { device_unregister(&my_bus_device); bus_unregister(&my_bus_type); } module_init(my_bus_init); module_exit(my_bus_exit);这里注册了一条my_bus_type的总线,同时注册了一个设备,该设备为该总线设备,可以在sys/devices下看到。该总线的match方法很简单,就是比较设备的名字和驱动的名字是否一样。
二、设备
设备使用struct device来描述,其中init_name表示设备的名字,需要注意的是,该字段在初始化完成之后被设置为null,所以以后都不能使用该字段,需要获取设备的名字使用dev_name函数,例如上面的总线中就使用了该函数。bus表示总线,即该设备挂载到什么总线上的。注册一个设备使用device_register函数,原型如下:
int device_register(struct device *dev);
注销设备使用device_unregister函数,原型如下:
void device_unregister(struct device *dev);
设备操作实例如下:
#include <linux/init.h> #include <linux/module.h> #include <linux/device.h> MODULE_LICENSE("GPL"); static void my_device_release(struct device *dev) { printk("My device released.\n"); } extern struct bus_type my_bus_type; extern struct device my_bus_device; struct device my_device = { .init_name = "my_device", .bus = &my_bus_type, .parent = &my_bus_device, .release = my_device_release, }; static int __init my_device_init(void) { int ret = 0; ret = device_register(&my_device); if (ret) { printk("Can't register my device!\n"); } return ret; } static void __exit my_device_exit(void) { device_unregister(&my_device); } module_init(my_device_init); module_exit(my_device_exit);这里还指定了parent成员为总线设备,注册成功之后将在总线设备的目录下看到该设备。由于这里使用了总线当中的my_bus_type和my_bus_device两个成员,所以这里需要使用extern声明这两个成员,同时还需记住的是在总线代码当中必须使用EXPORT_SYMBOL_GPL导出这两个符号,这样才能其它地方使用这些符号,导出符号还有一个宏EXPORT_SYMBOL,其中EXPORT_SYMBOL_GPL只能被GPL许可证下的模块使用。
#include <linux/init.h> #include <linux/module.h> #include <linux/device.h> MODULE_LICENSE("GPL"); static int my_driver_probe(struct device *dev) { printk("Driver found device which my driver can handle.\n"); return 0; } static int my_driver_remove(struct device *dev) { printk("Driver founc device unpluged.\n"); return 0; } extern struct bus_type my_bus_type; struct device_driver my_driver = { .name = "my_device", .bus = &my_bus_type, .probe = my_driver_probe, .remove = my_driver_remove, }; static int __init my_driver_init(void) { int ret = 0; ret = driver_register(&my_driver); if (ret) { printk("Can't register my driver!\n"); } return ret; } static void __exit my_driver_exit(void) { driver_unregister(&my_driver); } module_init(my_driver_init); module_exit(my_driver_exit);关于总线、设备和驱动需要特别注意的是总线的match方法在匹配设备和驱动成功之后调用驱动的probe方法,设备或驱动移除时调用驱动的remove方法。