一,理解设备驱动模型对linux驱动的编写是大有裨益的,下面会结合一个简单的实例对总线这一块作一些分析。(基于TCC8900 + linux-2.6.28)
二,实例代码如下:
#include <linux/device.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/string.h>
#define COMMENT_SIZE 1024 static char comment[COMMENT_SIZE] = "this bus is for test"; static int bus_match(struct device *dev,struct device_driver * driver) { return !strncmp(dev->bus_id,driver->name, strlen(driver->name)); } static void test_bus_release(struct device * dev) { printk(KERN_INFO "test bus release\n"); } struct device test_bus ={ .bus_id = "test_bus0", .release = test_bus_release }; struct bus_type test_bus_type = { /*这个名字会出现在/sys/bus/目录下*/ .name = "test_bus", .match = bus_match, }; static ssize_t show_bus_comment(struct bus_type * bus, char * buf) { return snprintf(buf, COMMENT_SIZE, "%s\n", comment); } static ssize_t store_bus_comment(struct bus_type *bus, const char *buf, size_t count) { memset(comment, 0, COMMENT_SIZE); if(count>COMMENT_SIZE){ strncpy(comment,buf,COMMENT_SIZE-1); return COMMENT_SIZE-1; }else{ strncpy(comment,buf,count); return count; } } static BUS_ATTR(comment, S_IWUSR | S_IRUGO, show_bus_comment, store_bus_comment);
static int __init test_bus_init(void) { int ret; /*注册总线设备*/ ret = device_register(&test_bus); if (ret){ printk(KERN_WARNING "test_bus device_register error: %d\n", ret); return ret; } /*注册总线*/ ret=bus_register(&test_bus_type); if(ret){ printk(KERN_WARNING "bus_register error: %d\n", ret); device_unregister(&test_bus); return ret; } /*创建属性文件*/ if(bus_create_file(&test_bus_type, &bus_attr_comment)) printk(KERN_INFO "cannot create comment attribute!\n"); return ret; } static void __exit test_bus_exit(void) { bus_unregister(&test_bus_type); device_unregister(&test_bus); }
EXPORT_SYMBOL(test_bus); EXPORT_SYMBOL(test_bus_type); module_init(test_bus_init); module_exit(test_bus_exit); MODULE_AUTHOR("J.H.Luo"); MODULE_LICENSE("Dual BSD/GPL");
|
1,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 (*suspend_late)(struct device *dev, pm_message_t state); int (*resume_early)(struct device *dev); int (*resume)(struct device *dev);
struct pm_ext_ops *pm; /*下面这个结构体可以说是一个容器,内嵌了sysfs文件系统最基本的结构体kobject*/ struct bus_type_private *p; };
|
在本例中,我们只定义了bus_type的name和match成员,其中match函数只是简单地匹配device和device_driver的ID是不是一样。如果一样则返回非零,说明匹配成功。
2,
int bus_register(struct bus_type *bus) { int retval; struct bus_type_private *priv; /* 申请一个bus_type_private结构体,就是上面我们提到的“容器”*/ priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL); if (!priv) return -ENOMEM; /*我中有你,你中有我的思想在linux内核中随处可见*/ priv->bus = bus; bus->p = priv;
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
/*把总线的名称设置为kobject的名称,这就是为什么我们在/sys/bus/下可以看到一个目录项跟新注册的总线名称一样的原因*/ retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); if (retval) goto out; /*初始化kset,并注册它*/ priv->subsys.kobj.kset = bus_kset; priv->subsys.kobj.ktype = &bus_ktype; priv->drivers_autoprobe = 1;
retval = kset_register(&priv->subsys); if (retval) goto out; /*创建属性文件uevent ,支持热插拔之用*/ retval = bus_create_file(bus, &bus_attr_uevent); if (retval) goto bus_uevent_fail; /*在每条bus总线下创建两个目录项devices和drivers 以存放挂在这条总线上的设备和驱动*/ priv->devices_kset = kset_create_and_add("devices", NULL, &priv->subsys.kobj); if (!priv->devices_kset) { retval = -ENOMEM; goto bus_devices_fail; } priv->drivers_kset = kset_create_and_add("drivers", NULL, &priv->subsys.kobj); if (!priv->drivers_kset) { retval = -ENOMEM; goto bus_drivers_fail; } /*初始化klist, klist跟引用计数有关系, klist_devices_get和klist_devices_put分别是 引用计数增加和减小的操作函数*/ klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); klist_init(&priv->klist_drivers, NULL, NULL); /*创建两个属性:drivers_probe,drivers_autoprobe*/ retval = add_probe_files(bus); if (retval) goto bus_probe_files_fail; /*如果总线中还有其它属性列表,则在这里创建*/ retval = bus_add_attrs(bus); if (retval) goto bus_attrs_fail;
pr_debug("bus: '%s': registered\n", bus->name); return 0;
bus_attrs_fail: remove_probe_files(bus); bus_probe_files_fail: kset_unregister(bus->p->drivers_kset); bus_drivers_fail: kset_unregister(bus->p->devices_kset); bus_devices_fail: bus_remove_file(bus, &bus_attr_uevent); bus_uevent_fail: kset_unregister(&bus->p->subsys); kfree(bus->p); out: return retval; }
|
整个总线的注册过程大部分工作都是在处理bus_type_private这个结构体,最终的结果是在/sys/bus/下产生一个新的总线分支。
3,在本例中,用bus_create_file()创建一个名为comment的属性,bus_attr_comment由宏BUS_ATTR定义,读写这个属性时分别会调用这个属性的函数:show_bus_comment和store_bus_comment。
其实,由上面注册bus的过程可知,我们也可以直接把这个属性bus_attr_comment放在bus_type的bus_attrs的成员里,这样bus_register的时候它自会为我们创建这个属性。
驱动代码稍作修改即可,如下:
#include <linux/device.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/string.h>
#define COMMENT_SIZE 1024 static char comment[COMMENT_SIZE] = "this bus is for test";
static void test_bus_release(struct device * dev) { printk(KERN_INFO "test bus release\n"); } struct device test_bus ={ .bus_id = "test_bus0", .release = test_bus_release };
static int bus_match(struct device *dev,struct device_driver * driver) { return !strncmp(dev->bus_id,driver->name, strlen(driver->name)); } static ssize_t show_bus_comment(struct bus_type * bus, char * buf) { return snprintf(buf, COMMENT_SIZE, "%s\n", comment); } static ssize_t store_bus_comment(struct bus_type *bus, const char *buf, size_t count) { memset(comment, 0, COMMENT_SIZE); if(count>COMMENT_SIZE){ strncpy(comment,buf,COMMENT_SIZE-1); return COMMENT_SIZE-1; }else{ strncpy(comment,buf,count); return count; } } static BUS_ATTR(comment, S_IWUSR | S_IRUGO, show_bus_comment, store_bus_comment);
struct bus_type test_bus_type = { /*这个名字会出现在/sys/bus/目录下*/ .name = "test_bus", .match = bus_match, .bus_attrs = &bus_attr_comment, };
static int __init test_bus_init(void) { int ret; /*注册总线设备*/ ret = device_register(&test_bus); if (ret){ printk(KERN_WARNING "test_bus device_register error: %d\n", ret); return ret; } /*注册总线*/ ret=bus_register(&test_bus_type); if(ret){ printk(KERN_WARNING "bus_register error: %d\n", ret); device_unregister(&test_bus); return ret; } return ret; } static void __exit test_bus_exit(void) { bus_unregister(&test_bus_type); device_unregister(&test_bus); }
EXPORT_SYMBOL(test_bus); EXPORT_SYMBOL(test_bus_type); module_init(test_bus_init); module_exit(test_bus_exit); MODULE_AUTHOR("J.H.Luo"); MODULE_LICENSE("Dual BSD/GPL");
|
三,最后给出这个实例的测试过程:
/nand2 # insmod bus_test.ko /nand2 # cd /sys/bus/test_bus/ /sys/bus/test_bus # ls comment drivers drivers_probe devices drivers_autoprobe uevent /sys/bus/test_bus # cat comment this bus is for test /sys/bus/test_bus # cat > comment /sys/bus/test_bus # echo hey,guys > comment /sys/bus/test_bus # cat comment hey,guys
|
------------------------------------------
本文乃原创!
转载请注明出处:http://sparklecliz.cublog.cn/
------------------------------------------
阅读(1919) | 评论(0) | 转发(0) |