linux内核部件分析(七)——设备驱动模型之driver
2011-10-08 01:41
627 查看
上节我们分析设备驱动模型中的device,主要是drivers/base/core.c,可以说是代码量最大的一个文件。本节要分析的驱动driver,就要相对简单很多。原因也很简单,对于driver,我们能定义的公共部分实在不多,能再sysfs中表达的也很少。本节的分析将围绕drivers/base/driver.c,但头文件仍然是include/linux/device.h和drivers/base/base.h。
先让我们来看看driver的结构。
struct device_driver就是模型定义的通用驱动结构。name是驱动名称,但这个name也只是在静态定义的初始名称,实际使用的名称还是由kobject中保管的。bus执行驱动所在的总线,owner是驱动所在的模块,还有一个所在模块名称mod_name,suppress_bind_attrs定义是否允许驱动通过sysfs决定挂载还是卸载设备。下面是一系列函数指针,probe是在驱动刚与设备挂接时调用的,remove是在设备卸载时调用的,shutdown是在设备关闭时调用的(说实话我现在还不知道remove和shutdown的区别),suspend是设备休眠时调用的,resume是设备恢复时调用的。group是属性集合,pm是电源管理的函数集合,p是指向driver_private的指针。
与device类似,device_driver把与其它组件联系的大部分结构变量移到struct driver_private中来。首先是kobj,在sysfs中代表driver目录本身。klist_devices是驱动下的设备链表,knode_bus是要挂载在总线的驱动链表上的节点。mkobj是driver与相关module的联系,之前在device_driver结构中已经有指向module的指针,但这还不够,在/sys下你能发现一个module目录,所以驱动所属的模块在sysfs中也有显示,具体留到代码中再看。driver指针自然是从driver_private指回struct
device_driver的。
除了以上两个结构,还有struct driver_attribute。driver_attribute是driver对struct attribute的封装,添加了两个特用于device_driver的读写函数。这种封装看似简单重复,工作量很小,但在使用时却会造成巨大的便利。
好,结构介绍完毕,下面看driver.c中的实现。
driver_for_each_device()是对drv的设备链表中的每个设备调用一次指定函数。
driver_find_device()是在drv的设备链表中寻找一个设备,寻找使用指定的匹配函数。
这两个函数都不陌生,在之前分析device的core.c中已经见到与它们很类似的函数,只不过那里是遍历设备的子设备链表,这里是遍历驱动的设备链表。next_device()同样是辅助用的内部函数。
driver_create_file()创建drv下的属性文件,调用sysfs_create_file()实现。
driver_remove_file()删除drv下的属性文件,调用sysfs_remove_file()实现。
driver_add_groups()在drv目录下添加属性集合,调用sysfs_create_groups()实现。
driver_remove_groups()在drv目录下删除属性集合,调用sysfs_remove_groups()实现。
发现两点问题:第一,是不是觉得driver_add_groups()不太合适,最好改为driver_create_groups()才搭调。但不只是driver用driver_add_groups(),device也使用device_add_groups(),不知一处这样做。第二,有没有发现driver_create_file()是外部函数,driver_add_groups()就是内部函数,也就是说driver只对外提供添加属性的接口,却不提供添加属性集合的接口。理由吗?在struct device_driver()已经专门定义了一个groups变量来添加属性集合,后面就不易再重复提供接口,而且创建属性集合需要的操作远比创建属性费时。在device中也是这样做的。
另外,driver中只提供管理属性文件的方法,却不提供管理二进制属性文件的方法,这是因为驱动本身没有这种需求,只有部分设备才要求二进制文件表示。
get_driver()增加drv的引用计数,put_driver()减少drv的引用计数。这都是通过drv->p->kobj来做的。
driver_find()从bus的驱动链表中寻找特定名称的driver。
driver_register()将drv注册到系统中。它真是做得难以预料地简单,所有的工作几乎完全是由bus_add_driver()代为完成的。但你要注意,在调用driver_register()前,drv->bus一定要预先设置。device可以不绑定bus,但driver一定要绑定到bus上。
driver_unregister()将drv从系统中撤销。大部分工作是调用bus_remove_driver()完成的。可以看出bus_add_driver()与bus_remove_driver()相对。driver和bus的联系如此紧密,以至于driver的注册和撤销工作都可以由bus代劳了。我们需要更进一步的分析。
经过调查,我们发现很有一部分driver的代码被移动到了bus.c中。我们本节是以driver为主,所以接下来会尽量在不惊动bus的情况下,分析存在于bus.c中的driver代码。
看到这里,你终于觉得driver开始正常了,它还要定义sysfs读写时操作的函数。
与device的释放函数device_release不同,driver_release没有提供外界代码运行的机会,只是简单地释放drv_priv函数。
上面描述了driver下两个只写的属性文件,unbind和bind。应该是提供用户空间命令是否将设备与驱动挂接的接口。
driver_add_attrs()向drv目录下添加属性,只是这些属性都是在bus中定义的drv_attrs[]。
driver_remove_attrs()从drv目录中删除相应的bus->drv_attrs[]。
add_bind_files()在drv目录下增加bind和unbind属性。
remove_bind_files()从drv目录下删除bind和unbind属性。
这是drv目录下地uevent属性文件,提供了从drv发送uevent的方法。
bus_add_driver()看似是把drv与bus联系起来,其实是完成driver加入系统的大部分操作。
首先调用bus_get(drv->bus)增加对bus的引用。
分配并初始化drv->p,即driver_private结构。
调用kobject_init_and_add()将drv加入sysfs,之前只是设置了priv->obj.kset为bus->p->drivers_kset,所以drv目录会出现在bus目录的drivers子目录中。如果总线允许自动probe,就会调用driver_attach()将驱动和总线上的设备进行匹配,这个过程先略过。
然后调用klist_add_tail()将drv挂入总线的驱动链表。
调用module_add_driver()创建driver相关的模块在sysfs中的表示。后面专门描述。
调用driver_create_file()在drv目录下创建uevent属性文件。
调用driver_add_attrs()在drv目录下添加bus->driver_attrs[]中定义的属性。
如果drv->suppress_bind_attrs为零,即允许用户空间决定驱动何时链接和卸载设备,则调用add_bind_files()添加bind和unbind属性文件。
调用kobject_uevent()向用户空间发布KOBJ_ADD消息。
从bus_add_driver()的处理过程来看,driver只在bus的drivers目录下出现,没什么软链接,需要的属性也不多。
bus_remove_driver()将drv从系统中撤销,与bus_add_driver()相对应。
driver真正精彩的地方在于probe函数,对设备的操作,对用户空间提供的接口,可惜这些都是特定的。这里只能将driver与bus联系起来,并在以后与device联系起来。
不过不必失望,下面我们分析下drivers/base/module.c,它显示了与驱动有关的module,在sysfs中的表现情况。
首先介绍使用到的结构。应该说module.c的代码实现很简单,但使用到的结构不简单。
可以看到module_attribute结构除了包含struct attribute,还多增加了好几条函数指针。而这只是最简单的,struct param_attribute除了包含module_attribute,还有一个指向kernel_param的指针param。这个kernel_param就太复杂了,是外界向module提供参数用的窗口,这里忽略。后面还有struct module_param_attrs和struct module_kobject。
make_driver_name()将drv的名字和drv->bus的名字合起来,不过这是一个内部函数,具体使用还要看后面。
module_create_drivers_dir()在mk所在的目录下创建一个drivers的目录。不过因为是使用kobject_create_and_add(),所以这个kobject使用默认的dynamic_kobj_ktype。
module_add_drivers()在module下添加与driver的联系。
开始调用kset_find_obj()从module_kset下寻找drv所属的module对应的kobj。说明每个module在加载时都会在/sys/module中创建一个kobject目录。这里找到后只是将其赋给drv->p->kmobj,并调用kobject_put()释放找到时加上的引用计数。至于为什么driver不保留对module的引用计数,或许是不需要,或许是已经存在了。
接下来调用sysfs_create_link()在驱动目录中添加指向module目录的软链接,名称就是module。
调用module_create_drivers_dir()在module目录下建立drivers子目录。
调用sysfs_create_link()在drivers子目录下建立指向驱动目录的软链接,名称使用make_driver_name()的返回结果。
module_remove_driver()消除driver与相应module之间的软链接关系。
对于module,应该是另一个议题了,这里只是简单涉及,下节我们将涉及到总线bus,并深入分析device和driver的关系。
先让我们来看看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 */ 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; };
struct device_driver就是模型定义的通用驱动结构。name是驱动名称,但这个name也只是在静态定义的初始名称,实际使用的名称还是由kobject中保管的。bus执行驱动所在的总线,owner是驱动所在的模块,还有一个所在模块名称mod_name,suppress_bind_attrs定义是否允许驱动通过sysfs决定挂载还是卸载设备。下面是一系列函数指针,probe是在驱动刚与设备挂接时调用的,remove是在设备卸载时调用的,shutdown是在设备关闭时调用的(说实话我现在还不知道remove和shutdown的区别),suspend是设备休眠时调用的,resume是设备恢复时调用的。group是属性集合,pm是电源管理的函数集合,p是指向driver_private的指针。
struct driver_private { struct kobject kobj; struct klist klist_devices; struct klist_node knode_bus; struct module_kobject *mkobj; struct device_driver *driver; }; #define to_driver(obj) container_of(obj, struct driver_private, kobj)
与device类似,device_driver把与其它组件联系的大部分结构变量移到struct driver_private中来。首先是kobj,在sysfs中代表driver目录本身。klist_devices是驱动下的设备链表,knode_bus是要挂载在总线的驱动链表上的节点。mkobj是driver与相关module的联系,之前在device_driver结构中已经有指向module的指针,但这还不够,在/sys下你能发现一个module目录,所以驱动所属的模块在sysfs中也有显示,具体留到代码中再看。driver指针自然是从driver_private指回struct
device_driver的。
struct driver_attribute { struct attribute attr; ssize_t (*show)(struct device_driver *driver, char *buf); ssize_t (*store)(struct device_driver *driver, const char *buf, size_t count); }; #define DRIVER_ATTR(_name, _mode, _show, _store) \ struct driver_attribute driver_attr_##_name = \ __ATTR(_name, _mode, _show, _store)
除了以上两个结构,还有struct driver_attribute。driver_attribute是driver对struct attribute的封装,添加了两个特用于device_driver的读写函数。这种封装看似简单重复,工作量很小,但在使用时却会造成巨大的便利。
好,结构介绍完毕,下面看driver.c中的实现。
static struct device *next_device(struct klist_iter *i) { struct klist_node *n = klist_next(i); struct device *dev = NULL; struct device_private *dev_prv; if (n) { dev_prv = to_device_private_driver(n); dev = dev_prv->device; } return dev; } int driver_for_each_device(struct device_driver *drv, struct device *start, void *data, int (*fn)(struct device *, void *)) { struct klist_iter i; struct device *dev; int error = 0; if (!drv) return -EINVAL; klist_iter_init_node(&drv->p->klist_devices, &i, start ? &start->p->knode_driver : NULL); while ((dev = next_device(&i)) && !error) error = fn(dev, data); klist_iter_exit(&i); return error; } struct device *driver_find_device(struct device_driver *drv, struct device *start, void *data, int (*match)(struct device *dev, void *data)) { struct klist_iter i; struct device *dev; if (!drv) return NULL; klist_iter_init_node(&drv->p->klist_devices, &i, (start ? &start->p->knode_driver : NULL)); while ((dev = next_device(&i))) if (match(dev, data) && get_device(dev)) break; klist_iter_exit(&i); return dev; }
driver_for_each_device()是对drv的设备链表中的每个设备调用一次指定函数。
driver_find_device()是在drv的设备链表中寻找一个设备,寻找使用指定的匹配函数。
这两个函数都不陌生,在之前分析device的core.c中已经见到与它们很类似的函数,只不过那里是遍历设备的子设备链表,这里是遍历驱动的设备链表。next_device()同样是辅助用的内部函数。
int driver_create_file(struct device_driver *drv, struct driver_attribute *attr) { int error; if (drv) error = sysfs_create_file(&drv->p->kobj, &attr->attr); else error = -EINVAL; return error; } void driver_remove_file(struct device_driver *drv, struct driver_attribute *attr) { if (drv) sysfs_remove_file(&drv->p->kobj, &attr->attr); }
driver_create_file()创建drv下的属性文件,调用sysfs_create_file()实现。
driver_remove_file()删除drv下的属性文件,调用sysfs_remove_file()实现。
static int driver_add_groups(struct device_driver *drv, const struct attribute_group **groups) { int error = 0; int i; if (groups) { for (i = 0; groups[i]; i++) { error = sysfs_create_group(&drv->p->kobj, groups[i]); if (error) { while (--i >= 0) sysfs_remove_group(&drv->p->kobj, groups[i]); break; } } } return error; } static void driver_remove_groups(struct device_driver *drv, const struct attribute_group **groups) { int i; if (groups) for (i = 0; groups[i]; i++) sysfs_remove_group(&drv->p->kobj, groups[i]); }
driver_add_groups()在drv目录下添加属性集合,调用sysfs_create_groups()实现。
driver_remove_groups()在drv目录下删除属性集合,调用sysfs_remove_groups()实现。
发现两点问题:第一,是不是觉得driver_add_groups()不太合适,最好改为driver_create_groups()才搭调。但不只是driver用driver_add_groups(),device也使用device_add_groups(),不知一处这样做。第二,有没有发现driver_create_file()是外部函数,driver_add_groups()就是内部函数,也就是说driver只对外提供添加属性的接口,却不提供添加属性集合的接口。理由吗?在struct device_driver()已经专门定义了一个groups变量来添加属性集合,后面就不易再重复提供接口,而且创建属性集合需要的操作远比创建属性费时。在device中也是这样做的。
另外,driver中只提供管理属性文件的方法,却不提供管理二进制属性文件的方法,这是因为驱动本身没有这种需求,只有部分设备才要求二进制文件表示。
struct device_driver *get_driver(struct device_driver *drv) { if (drv) { struct driver_private *priv; struct kobject *kobj; kobj = kobject_get(&drv->p->kobj); priv = to_driver(kobj); return priv->driver; } return NULL; } void put_driver(struct device_driver *drv) { kobject_put(&drv->p->kobj); }
get_driver()增加drv的引用计数,put_driver()减少drv的引用计数。这都是通过drv->p->kobj来做的。
struct device_driver *driver_find(const char *name, struct bus_type *bus) { struct kobject *k = kset_find_obj(bus->p->drivers_kset, name); struct driver_private *priv; if (k) { priv = to_driver(k); return priv->driver; } return NULL; }
driver_find()从bus的驱动链表中寻找特定名称的driver。
/** * driver_register - register driver with bus * @drv: driver to register * * We pass off most of the work to the bus_add_driver() call, * since most of the things we have to do deal with the bus * structures. */ int driver_register(struct device_driver *drv) { int ret; struct device_driver *other; BUG_ON(!drv->bus->p); if ((drv->bus->probe && drv->probe) || (drv->bus->remove && drv->remove) || (drv->bus->shutdown && drv->shutdown)) printk(KERN_WARNING "Driver '%s' needs updating - please use " "bus_type methods\n", drv->name); other = driver_find(drv->name, drv->bus); if (other) { put_driver(other); printk(KERN_ERR "Error: Driver '%s' is already registered, " "aborting...\n", drv->name); return -EBUSY; } ret = bus_add_driver(drv); if (ret) return ret; ret = driver_add_groups(drv, drv->groups); if (ret) bus_remove_driver(drv); return ret; }
driver_register()将drv注册到系统中。它真是做得难以预料地简单,所有的工作几乎完全是由bus_add_driver()代为完成的。但你要注意,在调用driver_register()前,drv->bus一定要预先设置。device可以不绑定bus,但driver一定要绑定到bus上。
void driver_unregister(struct device_driver *drv) { if (!drv || !drv->p) { WARN(1, "Unexpected driver unregister!\n"); return; } driver_remove_groups(drv, drv->groups); bus_remove_driver(drv); }
driver_unregister()将drv从系统中撤销。大部分工作是调用bus_remove_driver()完成的。可以看出bus_add_driver()与bus_remove_driver()相对。driver和bus的联系如此紧密,以至于driver的注册和撤销工作都可以由bus代劳了。我们需要更进一步的分析。
经过调查,我们发现很有一部分driver的代码被移动到了bus.c中。我们本节是以driver为主,所以接下来会尽量在不惊动bus的情况下,分析存在于bus.c中的driver代码。
static ssize_t drv_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct driver_attribute *drv_attr = to_drv_attr(attr); struct driver_private *drv_priv = to_driver(kobj); ssize_t ret = -EIO; if (drv_attr->show) ret = drv_attr->show(drv_priv->driver, buf); return ret; } static ssize_t drv_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { struct driver_attribute *drv_attr = to_drv_attr(attr); struct driver_private *drv_priv = to_driver(kobj); ssize_t ret = -EIO; if (drv_attr->store) ret = drv_attr->store(drv_priv->driver, buf, count); return ret; } static struct sysfs_ops driver_sysfs_ops = { .show = drv_attr_show, .store = drv_attr_store, };
看到这里,你终于觉得driver开始正常了,它还要定义sysfs读写时操作的函数。
static void driver_release(struct kobject *kobj) { struct driver_private *drv_priv = to_driver(kobj); pr_debug("driver: '%s': %s\n", kobject_name(kobj), __func__); kfree(drv_priv); } static struct kobj_type driver_ktype = { .sysfs_ops = &driver_sysfs_ops, .release = driver_release, };
与device的释放函数device_release不同,driver_release没有提供外界代码运行的机会,只是简单地释放drv_priv函数。
/* Manually detach a device from its associated driver. */ static ssize_t driver_unbind(struct device_driver *drv, const char *buf, size_t count) { struct bus_type *bus = bus_get(drv->bus); struct device *dev; int err = -ENODEV; dev = bus_find_device_by_name(bus, NULL, buf); if (dev && dev->driver == drv) { if (dev->parent) /* Needed for USB */ down(&dev->parent->sem); device_release_driver(dev); if (dev->parent) up(&dev->parent->sem); err = count; } put_device(dev); bus_put(bus); return err; } static DRIVER_ATTR(unbind, S_IWUSR, NULL, driver_unbind); /* * Manually attach a device to a driver. * Note: the driver must want to bind to the device, * it is not possible to override the driver's id table. */ static ssize_t driver_bind(struct device_driver *drv, const char *buf, size_t count) { struct bus_type *bus = bus_get(drv->bus); struct device *dev; int err = -ENODEV; dev = bus_find_device_by_name(bus, NULL, buf); if (dev && dev->driver == NULL && driver_match_device(drv, dev)) { if (dev->parent) /* Needed for USB */ down(&dev->parent->sem); down(&dev->sem); err = driver_probe_device(drv, dev); up(&dev->sem); if (dev->parent) up(&dev->parent->sem); if (err > 0) { /* success */ err = count; } else if (err == 0) { /* driver didn't accept device */ err = -ENODEV; } } put_device(dev); bus_put(bus); return err; } static DRIVER_ATTR(bind, S_IWUSR, NULL, driver_bind);
上面描述了driver下两个只写的属性文件,unbind和bind。应该是提供用户空间命令是否将设备与驱动挂接的接口。
static int driver_add_attrs(struct bus_type *bus, struct device_driver *drv) { int error = 0; int i; if (bus->drv_attrs) { for (i = 0; attr_name(bus->drv_attrs[i]); i++) { error = driver_create_file(drv, &bus->drv_attrs[i]); if (error) goto err; } } done: return error; err: while (--i >= 0) driver_remove_file(drv, &bus->drv_attrs[i]); goto done; } static void driver_remove_attrs(struct bus_type *bus, struct device_driver *drv) { int i; if (bus->drv_attrs) { for (i = 0; attr_name(bus->drv_attrs[i]); i++) driver_remove_file(drv, &bus->drv_attrs[i]); } }
driver_add_attrs()向drv目录下添加属性,只是这些属性都是在bus中定义的drv_attrs[]。
driver_remove_attrs()从drv目录中删除相应的bus->drv_attrs[]。
static int __must_check add_bind_files(struct device_driver *drv) { int ret; ret = driver_create_file(drv, &driver_attr_unbind); if (ret == 0) { ret = driver_create_file(drv, &driver_attr_bind); if (ret) driver_remove_file(drv, &driver_attr_unbind); } return ret; } static void remove_bind_files(struct device_driver *drv) { driver_remove_file(drv, &driver_attr_bind); driver_remove_file(drv, &driver_attr_unbind); }
add_bind_files()在drv目录下增加bind和unbind属性。
remove_bind_files()从drv目录下删除bind和unbind属性。
static ssize_t driver_uevent_store(struct device_driver *drv, const char *buf, size_t count) { enum kobject_action action; if (kobject_action_type(buf, count, &action) == 0) kobject_uevent(&drv->p->kobj, action); return count; } static DRIVER_ATTR(uevent, S_IWUSR, NULL, driver_uevent_store);
这是drv目录下地uevent属性文件,提供了从drv发送uevent的方法。
/** * bus_add_driver - Add a driver to the bus. * @drv: driver. */ int bus_add_driver(struct device_driver *drv) { struct bus_type *bus; struct driver_private *priv; int error = 0; bus = bus_get(drv->bus); if (!bus) return -EINVAL; pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name); priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { error = -ENOMEM; goto out_put_bus; } klist_init(&priv->klist_devices, NULL, NULL); priv->driver = drv; drv->p = priv; priv->kobj.kset = bus->p->drivers_kset; error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, "%s", drv->name); if (error) goto out_unregister; if (drv->bus->p->drivers_autoprobe) { error = driver_attach(drv); if (error) goto out_unregister; } klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); module_add_driver(drv->owner, drv); error = driver_create_file(drv, &driver_attr_uevent); if (error) { printk(KERN_ERR "%s: uevent attr (%s) failed\n", __func__, drv->name); } error = driver_add_attrs(bus, drv); if (error) { /* How the hell do we get out of this pickle? Give up */ printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n", __func__, drv->name); } if (!drv->suppress_bind_attrs) { error = add_bind_files(drv); if (error) { /* Ditto */ printk(KERN_ERR "%s: add_bind_files(%s) failed\n", __func__, drv->name); } } kobject_uevent(&priv->kobj, KOBJ_ADD); return 0; out_unregister: kfree(drv->p); drv->p = NULL; kobject_put(&priv->kobj); out_put_bus: bus_put(bus); return error; }
bus_add_driver()看似是把drv与bus联系起来,其实是完成driver加入系统的大部分操作。
首先调用bus_get(drv->bus)增加对bus的引用。
分配并初始化drv->p,即driver_private结构。
调用kobject_init_and_add()将drv加入sysfs,之前只是设置了priv->obj.kset为bus->p->drivers_kset,所以drv目录会出现在bus目录的drivers子目录中。如果总线允许自动probe,就会调用driver_attach()将驱动和总线上的设备进行匹配,这个过程先略过。
然后调用klist_add_tail()将drv挂入总线的驱动链表。
调用module_add_driver()创建driver相关的模块在sysfs中的表示。后面专门描述。
调用driver_create_file()在drv目录下创建uevent属性文件。
调用driver_add_attrs()在drv目录下添加bus->driver_attrs[]中定义的属性。
如果drv->suppress_bind_attrs为零,即允许用户空间决定驱动何时链接和卸载设备,则调用add_bind_files()添加bind和unbind属性文件。
调用kobject_uevent()向用户空间发布KOBJ_ADD消息。
从bus_add_driver()的处理过程来看,driver只在bus的drivers目录下出现,没什么软链接,需要的属性也不多。
/** * bus_remove_driver - delete driver from bus's knowledge. * @drv: driver. * * Detach the driver from the devices it controls, and remove * it from its bus's list of drivers. Finally, we drop the reference * to the bus we took in bus_add_driver(). */ void bus_remove_driver(struct device_driver *drv) { if (!drv->bus) return; if (!drv->suppress_bind_attrs) remove_bind_files(drv); driver_remove_attrs(drv->bus, drv); driver_remove_file(drv, &driver_attr_uevent); klist_remove(&drv->p->knode_bus); pr_debug("bus: '%s': remove driver %s\n", drv->bus->name, drv->name); driver_detach(drv); module_remove_driver(drv); kobject_put(&drv->p->kobj); bus_put(drv->bus); }
bus_remove_driver()将drv从系统中撤销,与bus_add_driver()相对应。
driver真正精彩的地方在于probe函数,对设备的操作,对用户空间提供的接口,可惜这些都是特定的。这里只能将driver与bus联系起来,并在以后与device联系起来。
不过不必失望,下面我们分析下drivers/base/module.c,它显示了与驱动有关的module,在sysfs中的表现情况。
首先介绍使用到的结构。应该说module.c的代码实现很简单,但使用到的结构不简单。
struct module_attribute { struct attribute attr; ssize_t (*show)(struct module_attribute *, struct module *, char *); ssize_t (*store)(struct module_attribute *, struct module *, const char *, size_t count); void (*setup)(struct module *, const char *); int (*test)(struct module *); void (*free)(struct module *); }; struct param_attribute { struct module_attribute mattr; struct kernel_param *param; }; struct module_param_attrs { unsigned int num; struct attribute_group grp; struct param_attribute attrs[0]; }; struct module_kobject { struct kobject kobj; struct module *mod; struct kobject *drivers_dir; struct module_param_attrs *mp; };
可以看到module_attribute结构除了包含struct attribute,还多增加了好几条函数指针。而这只是最简单的,struct param_attribute除了包含module_attribute,还有一个指向kernel_param的指针param。这个kernel_param就太复杂了,是外界向module提供参数用的窗口,这里忽略。后面还有struct module_param_attrs和struct module_kobject。
static char *make_driver_name(struct device_driver *drv) { char *driver_name; driver_name = kmalloc(strlen(drv->name) + strlen(drv->bus->name) + 2, GFP_KERNEL); if (!driver_name) return NULL; sprintf(driver_name, "%s:%s", drv->bus->name, drv->name); return driver_name; }
make_driver_name()将drv的名字和drv->bus的名字合起来,不过这是一个内部函数,具体使用还要看后面。
static void module_create_drivers_dir(struct module_kobject *mk) { if (!mk || mk->drivers_dir) return; mk->drivers_dir = kobject_create_and_add("drivers", &mk->kobj); }
module_create_drivers_dir()在mk所在的目录下创建一个drivers的目录。不过因为是使用kobject_create_and_add(),所以这个kobject使用默认的dynamic_kobj_ktype。
void module_add_driver(struct module *mod, struct device_driver *drv) { char *driver_name; int no_warn; struct module_kobject *mk = NULL; if (!drv) return; if (mod) mk = &mod->mkobj; else if (drv->mod_name) { struct kobject *mkobj; /* Lookup built-in module entry in /sys/modules */ mkobj = kset_find_obj(module_kset, drv->mod_name); if (mkobj) { mk = container_of(mkobj, struct module_kobject, kobj); /* remember our module structure */ drv->p->mkobj = mk; /* kset_find_obj took a reference */ kobject_put(mkobj); } } if (!mk) return; /* Don't check return codes; these calls are idempotent */ no_warn = sysfs_create_link(&drv->p->kobj, &mk->kobj, "module"); driver_name = make_driver_name(drv); if (driver_name) { module_create_drivers_dir(mk); no_warn = sysfs_create_link(mk->drivers_dir, &drv->p->kobj, driver_name); kfree(driver_name); } }
module_add_drivers()在module下添加与driver的联系。
开始调用kset_find_obj()从module_kset下寻找drv所属的module对应的kobj。说明每个module在加载时都会在/sys/module中创建一个kobject目录。这里找到后只是将其赋给drv->p->kmobj,并调用kobject_put()释放找到时加上的引用计数。至于为什么driver不保留对module的引用计数,或许是不需要,或许是已经存在了。
接下来调用sysfs_create_link()在驱动目录中添加指向module目录的软链接,名称就是module。
调用module_create_drivers_dir()在module目录下建立drivers子目录。
调用sysfs_create_link()在drivers子目录下建立指向驱动目录的软链接,名称使用make_driver_name()的返回结果。
void module_remove_driver(struct device_driver *drv) { struct module_kobject *mk = NULL; char *driver_name; if (!drv) return; sysfs_remove_link(&drv->p->kobj, "module"); if (drv->owner) mk = &drv->owner->mkobj; else if (drv->p->mkobj) mk = drv->p->mkobj; if (mk && mk->drivers_dir) { driver_name = make_driver_name(drv); if (driver_name) { sysfs_remove_link(mk->drivers_dir, driver_name); kfree(driver_name); } } }
module_remove_driver()消除driver与相应module之间的软链接关系。
对于module,应该是另一个议题了,这里只是简单涉及,下节我们将涉及到总线bus,并深入分析device和driver的关系。
相关文章推荐
- linux内核部件分析(九)——设备驱动模型之device-driver
- Linux内核部件分析--设备驱动模型之device-driver
- linux内核部件分析(七)——设备驱动模型之driver
- Linux内核部件分析 设备驱动模型之device-driver
- linux内核部件分析(七)——设备驱动模型之driver
- Linux内核部件分析<5> 设备驱动模型的基石kobject
- Linux内核部件分析--设备驱动模型之device
- Linux内核部件分析--设备驱动模型之bus
- Linux内核部件分析 设备驱动模型的基石kobject
- Linux内核部件分析 设备驱动模型之device
- linux内核部件分析之——设备驱动模型之class
- Linux内核部件分析--设备驱动模型之device
- linux内核部件分析(十)——设备驱动模型之class
- linux内核部件分析(五)——设备驱动模型的基石kobject
- linux内核部件分析之——设备驱动模型之class
- linux内核部件分析之——设备驱动模型之class
- Linux内核部件分析--设备驱动模型之bus
- linux内核部件分析之----设备驱动模型之device
- linux内核部件分析(六)——设备驱动模型之device
- linux内核部件分析(五)——设备驱动模型的基石kobject