Linux内核部件分析

设备驱动模型之driver

转载:https://www.linuxidc.com/Linux/2011-10/44627p7.htm
上节我们分析设备驱动模型中的device,主要是drivers/base/core.c,可以说是代码量最大的一个文件。本节要分析的驱动driver,就要相对简单很多。原因也很简单,对于driver,我们能定义的公共部分实在不多,能再sysfs中表达的也很少。本节的分析将围绕drivers/base/driver.c,但头文件仍然是include/linux/device.h和drivers/base/base.h。

先让我们来看看driver的结构。

  1. struct device_driver {
  2. const char      *name;
  3. struct bus_type     *bus;
  4. struct module       *owner;
  5. const char      *mod_name;  /* used for built-in modules */
  6. bool suppress_bind_attrs;   /* disables bind/unbind via sysfs */
  7. int (*probe) (struct device *dev);
  8. int (*remove) (struct device *dev);
  9. void (*shutdown) (struct device *dev);
  10. int (*suspend) (struct device *dev, pm_message_t state);
  11. int (*resume) (struct device *dev);
  12. const struct attribute_group **groups;
  13. const struct dev_pm_ops *pm;
  14. struct driver_private *p;
  15. };

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的指针。

  1. struct driver_private {
  2. struct kobject kobj;
  3. struct klist klist_devices;
  4. struct klist_node knode_bus;
  5. struct module_kobject *mkobj;
  6. struct device_driver *driver;
  7. };
  8. #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的。

  1. struct driver_attribute {
  2. struct attribute attr;
  3. ssize_t (*show)(struct device_driver *driver, char *buf);
  4. ssize_t (*store)(struct device_driver *driver, const char *buf,
  5. size_t count);
  6. };
  7. #define DRIVER_ATTR(_name, _mode, _show, _store)    \
  8. struct driver_attribute driver_attr_##_name =       \
  9. __ATTR(_name, _mode, _show, _store)

除了以上两个结构,还有struct driver_attribute。driver_attribute是driver对struct attribute的封装,添加了两个特用于device_driver的读写函数。这种封装看似简单重复,工作量很小,但在使用时却会造成巨大的便利。

好,结构介绍完毕,下面看driver.c中的实现。

  1. static struct device *next_device(struct klist_iter *i)
  2. {
  3. struct klist_node *n = klist_next(i);
  4. struct device *dev = NULL;
  5. struct device_private *dev_prv;
  6. if (n) {
  7. dev_prv = to_device_private_driver(n);
  8. dev = dev_prv->device;
  9. }
  10. return dev;
  11. }
  12. int driver_for_each_device(struct device_driver *drv, struct device *start,
  13. void *data, int (*fn)(struct device *, void *))
  14. {
  15. struct klist_iter i;
  16. struct device *dev;
  17. int error = 0;
  18. if (!drv)
  19. return -EINVAL;
  20. klist_iter_init_node(&drv->p->klist_devices, &i,
  21. start ? &start->p->knode_driver : NULL);
  22. while ((dev = next_device(&i)) && !error)
  23. error = fn(dev, data);
  24. klist_iter_exit(&i);
  25. return error;
  26. }
  27. struct device *driver_find_device(struct device_driver *drv,
  28. struct device *start, void *data,
  29. int (*match)(struct device *dev, void *data))
  30. {
  31. struct klist_iter i;
  32. struct device *dev;
  33. if (!drv)
  34. return NULL;
  35. klist_iter_init_node(&drv->p->klist_devices, &i,
  36. (start ? &start->p->knode_driver : NULL));
  37. while ((dev = next_device(&i)))
  38. if (match(dev, data) && get_device(dev))
  39. break;
  40. klist_iter_exit(&i);
  41. return dev;
  42. }

driver_for_each_device()是对drv的设备链表中的每个设备调用一次指定函数。

driver_find_device()是在drv的设备链表中寻找一个设备,寻找使用指定的匹配函数。

这两个函数都不陌生,在之前分析device的core.c中已经见到与它们很类似的函数,只不过那里是遍历设备的子设备链表,这里是遍历驱动的设备链表。next_device()同样是辅助用的内部函数。

  1. int driver_create_file(struct device_driver *drv,
  2. struct driver_attribute *attr)
  3. {
  4. int error;
  5. if (drv)
  6. error = sysfs_create_file(&drv->p->kobj, &attr->attr);
  7. else
  8. error = -EINVAL;
  9. return error;
  10. }
  11. void driver_remove_file(struct device_driver *drv,
  12. struct driver_attribute *attr)
  13. {
  14. if (drv)
  15. sysfs_remove_file(&drv->p->kobj, &attr->attr);
  16. }

driver_create_file()创建drv下的属性文件,调用sysfs_create_file()实现。

driver_remove_file()删除drv下的属性文件,调用sysfs_remove_file()实现。

  1. static int driver_add_groups(struct device_driver *drv,
  2. const struct attribute_group **groups)
  3. {
  4. int error = 0;
  5. int i;
  6. if (groups) {
  7. for (i = 0; groups[i]; i++) {
  8. error = sysfs_create_group(&drv->p->kobj, groups[i]);
  9. if (error) {
  10. while (--i >= 0)
  11. sysfs_remove_group(&drv->p->kobj,
  12. groups[i]);
  13. break;
  14. }
  15. }
  16. }
  17. return error;
  18. }
  19. static void driver_remove_groups(struct device_driver *drv,
  20. const struct attribute_group **groups)
  21. {
  22. int i;
  23. if (groups)
  24. for (i = 0; groups[i]; i++)
  25. sysfs_remove_group(&drv->p->kobj, groups[i]);
  26. }

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中只提供管理属性文件的方法,却不提供管理二进制属性文件的方法,这是因为驱动本身没有这种需求,只有部分设备才要求二进制文件表示。

  1. struct device_driver *get_driver(struct device_driver *drv)
  2. {
  3. if (drv) {
  4. struct driver_private *priv;
  5. struct kobject *kobj;
  6. kobj = kobject_get(&drv->p->kobj);
  7. priv = to_driver(kobj);
  8. return priv->driver;
  9. }
  10. return NULL;
  11. }
  12. void put_driver(struct device_driver *drv)
  13. {
  14. kobject_put(&drv->p->kobj);
  15. }

get_driver()增加drv的引用计数,put_driver()减少drv的引用计数。这都是通过drv->p->kobj来做的。

  1. struct device_driver *driver_find(const char *name, struct bus_type *bus)
  2. {
  3. struct kobject *k = kset_find_obj(bus->p->drivers_kset, name);
  4. struct driver_private *priv;
  5. if (k) {
  6. priv = to_driver(k);
  7. return priv->driver;
  8. }
  9. return NULL;
  10. }

driver_find()从bus的驱动链表中寻找特定名称的driver。

  1. /**
  2. * driver_register - register driver with bus
  3. * @drv: driver to register
  4. *
  5. * We pass off most of the work to the bus_add_driver() call,
  6. * since most of the things we have to do deal with the bus
  7. * structures.
  8. */
  9. int driver_register(struct device_driver *drv)
  10. {
  11. int ret;
  12. struct device_driver *other;
  13. BUG_ON(!drv->bus->p);
  14. if ((drv->bus->probe && drv->probe) ||
  15. (drv->bus->remove && drv->remove) ||
  16. (drv->bus->shutdown && drv->shutdown))
  17. printk(KERN_WARNING "Driver '%s' needs updating - please use "
  18. "bus_type methods\n", drv->name);
  19. other = driver_find(drv->name, drv->bus);
  20. if (other) {
  21. put_driver(other);
  22. printk(KERN_ERR "Error: Driver '%s' is already registered, "
  23. "aborting...\n", drv->name);
  24. return -EBUSY;
  25. }
  26. ret = bus_add_driver(drv);
  27. if (ret)
  28. return ret;
  29. ret = driver_add_groups(drv, drv->groups);
  30. if (ret)
  31. bus_remove_driver(drv);
  32. return ret;
  33. }

driver_register()将drv注册到系统中。它真是做得难以预料地简单,所有的工作几乎完全是由bus_add_driver()代为完成的。但你要注意,在调用driver_register()前,drv->bus一定要预先设置。device可以不绑定bus,但driver一定要绑定到bus上。

  1. void driver_unregister(struct device_driver *drv)
  2. {
  3. if (!drv || !drv->p) {
  4. WARN(1, "Unexpected driver unregister!\n");
  5. return;
  6. }
  7. driver_remove_groups(drv, drv->groups);
  8. bus_remove_driver(drv);
  9. }

driver_unregister()将drv从系统中撤销。大部分工作是调用bus_remove_driver()完成的。可以看出bus_add_driver()与bus_remove_driver()相对。driver和bus的联系如此紧密,以至于driver的注册和撤销工作都可以由bus代劳了。我们需要更进一步的分析。

经过调查,我们发现很有一部分driver的代码被移动到了bus.c中。我们本节是以driver为主,所以接下来会尽量在不惊动bus的情况下,分析存在于bus.c中的driver代码。

  1. static ssize_t drv_attr_show(struct kobject *kobj, struct attribute *attr,
  2. char *buf)
  3. {
  4. struct driver_attribute *drv_attr = to_drv_attr(attr);
  5. struct driver_private *drv_priv = to_driver(kobj);
  6. ssize_t ret = -EIO;
  7. if (drv_attr->show)
  8. ret = drv_attr->show(drv_priv->driver, buf);
  9. return ret;
  10. }
  11. static ssize_t drv_attr_store(struct kobject *kobj, struct attribute *attr,
  12. const char *buf, size_t count)
  13. {
  14. struct driver_attribute *drv_attr = to_drv_attr(attr);
  15. struct driver_private *drv_priv = to_driver(kobj);
  16. ssize_t ret = -EIO;
  17. if (drv_attr->store)
  18. ret = drv_attr->store(drv_priv->driver, buf, count);
  19. return ret;
  20. }
  21. static struct sysfs_ops driver_sysfs_ops = {
  22. .show   = drv_attr_show,
  23. .store  = drv_attr_store,
  24. };

看到这里,你终于觉得driver开始正常了,它还要定义sysfs读写时操作的函数。

  1. static void driver_release(struct kobject *kobj)
  2. {
  3. struct driver_private *drv_priv = to_driver(kobj);
  4. pr_debug("driver: '%s': %s\n", kobject_name(kobj), __func__);
  5. kfree(drv_priv);
  6. }
  7. static struct kobj_type driver_ktype = {
  8. .sysfs_ops  = &driver_sysfs_ops,
  9. .release    = driver_release,
  10. };

与device的释放函数device_release不同,driver_release没有提供外界代码运行的机会,只是简单地释放drv_priv函数。

  1. /* Manually detach a device from its associated driver. */
  2. static ssize_t driver_unbind(struct device_driver *drv,
  3. const char *buf, size_t count)
  4. {
  5. struct bus_type *bus = bus_get(drv->bus);
  6. struct device *dev;
  7. int err = -ENODEV;
  8. dev = bus_find_device_by_name(bus, NULL, buf);
  9. if (dev && dev->driver == drv) {
  10. if (dev->parent) /* Needed for USB */
  11. down(&dev->parent->sem);
  12. device_release_driver(dev);
  13. if (dev->parent)
  14. up(&dev->parent->sem);
  15. err = count;
  16. }
  17. put_device(dev);
  18. bus_put(bus);
  19. return err;
  20. }
  21. static DRIVER_ATTR(unbind, S_IWUSR, NULL, driver_unbind);
  22. /*
  23. * Manually attach a device to a driver.
  24. * Note: the driver must want to bind to the device,
  25. * it is not possible to override the driver's id table.
  26. */
  27. static ssize_t driver_bind(struct device_driver *drv,
  28. const char *buf, size_t count)
  29. {
  30. struct bus_type *bus = bus_get(drv->bus);
  31. struct device *dev;
  32. int err = -ENODEV;
  33. dev = bus_find_device_by_name(bus, NULL, buf);
  34. if (dev && dev->driver == NULL && driver_match_device(drv, dev)) {
  35. if (dev->parent) /* Needed for USB */
  36. down(&dev->parent->sem);
  37. down(&dev->sem);
  38. err = driver_probe_device(drv, dev);
  39. up(&dev->sem);
  40. if (dev->parent)
  41. up(&dev->parent->sem);
  42. if (err > 0) {
  43. /* success */
  44. err = count;
  45. else if (err == 0) {
  46. /* driver didn't accept device */
  47. err = -ENODEV;
  48. }
  49. }
  50. put_device(dev);
  51. bus_put(bus);
  52. return err;
  53. }
  54. static DRIVER_ATTR(bind, S_IWUSR, NULL, driver_bind);

上面描述了driver下两个只写的属性文件,unbind和bind。应该是提供用户空间命令是否将设备与驱动挂接的接口。

  1. static int driver_add_attrs(struct bus_type *bus, struct device_driver *drv)
  2. {
  3. int error = 0;
  4. int i;
  5. if (bus->drv_attrs) {
  6. for (i = 0; attr_name(bus->drv_attrs[i]); i++) {
  7. error = driver_create_file(drv, &bus->drv_attrs[i]);
  8. if (error)
  9. goto err;
  10. }
  11. }
  12. done:
  13. return error;
  14. err:
  15. while (--i >= 0)
  16. driver_remove_file(drv, &bus->drv_attrs[i]);
  17. goto done;
  18. }
  19. static void driver_remove_attrs(struct bus_type *bus,
  20. struct device_driver *drv)
  21. {
  22. int i;
  23. if (bus->drv_attrs) {
  24. for (i = 0; attr_name(bus->drv_attrs[i]); i++)
  25. driver_remove_file(drv, &bus->drv_attrs[i]);
  26. }
  27. }

driver_add_attrs()向drv目录下添加属性,只是这些属性都是在bus中定义的drv_attrs[]。

driver_remove_attrs()从drv目录中删除相应的bus->drv_attrs[]。

  1. static int __must_check add_bind_files(struct device_driver *drv)
  2. {
  3. int ret;
  4. ret = driver_create_file(drv, &driver_attr_unbind);
  5. if (ret == 0) {
  6. ret = driver_create_file(drv, &driver_attr_bind);
  7. if (ret)
  8. driver_remove_file(drv, &driver_attr_unbind);
  9. }
  10. return ret;
  11. }
  12. static void remove_bind_files(struct device_driver *drv)
  13. {
  14. driver_remove_file(drv, &driver_attr_bind);
  15. driver_remove_file(drv, &driver_attr_unbind);
  16. }

add_bind_files()在drv目录下增加bind和unbind属性。

remove_bind_files()从drv目录下删除bind和unbind属性。

  1. static ssize_t driver_uevent_store(struct device_driver *drv,
  2. const char *buf, size_t count)
  3. {
  4. enum kobject_action action;
  5. if (kobject_action_type(buf, count, &action) == 0)
  6. kobject_uevent(&drv->p->kobj, action);
  7. return count;
  8. }
  9. static DRIVER_ATTR(uevent, S_IWUSR, NULL, driver_uevent_store);

这是drv目录下地uevent属性文件,提供了从drv发送uevent的方法。

  1. /**
  2. * bus_add_driver - Add a driver to the bus.
  3. * @drv: driver.
  4. */
  5. int bus_add_driver(struct device_driver *drv)
  6. {
  7. struct bus_type *bus;
  8. struct driver_private *priv;
  9. int error = 0;
  10. bus = bus_get(drv->bus);
  11. if (!bus)
  12. return -EINVAL;
  13. pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
  14. priv = kzalloc(sizeof(*priv), GFP_KERNEL);
  15. if (!priv) {
  16. error = -ENOMEM;
  17. goto out_put_bus;
  18. }
  19. klist_init(&priv->klist_devices, NULL, NULL);
  20. priv->driver = drv;
  21. drv->p = priv;
  22. priv->kobj.kset = bus->p->drivers_kset;
  23. error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
  24. "%s", drv->name);
  25. if (error)
  26. goto out_unregister;
  27. if (drv->bus->p->drivers_autoprobe) {
  28. error = driver_attach(drv);
  29. if (error)
  30. goto out_unregister;
  31. }
  32. klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
  33. module_add_driver(drv->owner, drv);
  34. error = driver_create_file(drv, &driver_attr_uevent);
  35. if (error) {
  36. printk(KERN_ERR "%s: uevent attr (%s) failed\n",
  37. __func__, drv->name);
  38. }
  39. error = driver_add_attrs(bus, drv);
  40. if (error) {
  41. /* How the hell do we get out of this pickle? Give up */
  42. printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
  43. __func__, drv->name);
  44. }
  45. if (!drv->suppress_bind_attrs) {
  46. error = add_bind_files(drv);
  47. if (error) {
  48. /* Ditto */
  49. printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
  50. __func__, drv->name);
  51. }
  52. }
  53. kobject_uevent(&priv->kobj, KOBJ_ADD);
  54. return 0;
  55. out_unregister:
  56. kfree(drv->p);
  57. drv->p = NULL;
  58. kobject_put(&priv->kobj);
  59. out_put_bus:
  60. bus_put(bus);
  61. return error;
  62. }

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目录下出现,没什么软链接,需要的属性也不多。

  1. /**
  2. * bus_remove_driver - delete driver from bus's knowledge.
  3. * @drv: driver.
  4. *
  5. * Detach the driver from the devices it controls, and remove
  6. * it from its bus's list of drivers. Finally, we drop the reference
  7. * to the bus we took in bus_add_driver().
  8. */
  9. void bus_remove_driver(struct device_driver *drv)
  10. {
  11. if (!drv->bus)
  12. return;
  13. if (!drv->suppress_bind_attrs)
  14. remove_bind_files(drv);
  15. driver_remove_attrs(drv->bus, drv);
  16. driver_remove_file(drv, &driver_attr_uevent);
  17. klist_remove(&drv->p->knode_bus);
  18. pr_debug("bus: '%s': remove driver %s\n", drv->bus->name, drv->name);
  19. driver_detach(drv);
  20. module_remove_driver(drv);
  21. kobject_put(&drv->p->kobj);
  22. bus_put(drv->bus);
  23. }

bus_remove_driver()将drv从系统中撤销,与bus_add_driver()相对应。

driver真正精彩的地方在于probe函数,对设备的操作,对用户空间提供的接口,可惜这些都是特定的。这里只能将driver与bus联系起来,并在以后与device联系起来。

不过不必失望,下面我们分析下drivers/base/module.c,它显示了与驱动有关的module,在sysfs中的表现情况。

首先介绍使用到的结构。应该说module.c的代码实现很简单,但使用到的结构不简单。

  1. struct module_attribute {
  2. struct attribute attr;
  3. ssize_t (*show)(struct module_attribute *, struct module *, char *);
  4. ssize_t (*store)(struct module_attribute *, struct module *,
  5. const char *, size_t count);
  6. void (*setup)(struct module *, const char *);
  7. int (*test)(struct module *);
  8. void (*free)(struct module *);
  9. };
  10. struct param_attribute
  11. {
  12. struct module_attribute mattr;
  13. struct kernel_param *param;
  14. };
  15. struct module_param_attrs
  16. {
  17. unsigned int num;
  18. struct attribute_group grp;
  19. struct param_attribute attrs[0];
  20. };
  21. struct module_kobject
  22. {
  23. struct kobject kobj;
  24. struct module *mod;
  25. struct kobject *drivers_dir;
  26. struct module_param_attrs *mp;
  27. };

可以看到module_attribute结构除了包含struct attribute,还多增加了好几条函数指针。而这只是最简单的,struct param_attribute除了包含module_attribute,还有一个指向kernel_param的指针param。这个kernel_param就太复杂了,是外界向module提供参数用的窗口,这里忽略。后面还有struct module_param_attrs和struct module_kobject。

  1. static char *make_driver_name(struct device_driver *drv)
  2. {
  3. char *driver_name;
  4. driver_name = kmalloc(strlen(drv->name) + strlen(drv->bus->name) + 2,
  5. GFP_KERNEL);
  6. if (!driver_name)
  7. return NULL;
  8. sprintf(driver_name, "%s:%s", drv->bus->name, drv->name);
  9. return driver_name;
  10. }

make_driver_name()将drv的名字和drv->bus的名字合起来,不过这是一个内部函数,具体使用还要看后面。

  1. static void module_create_drivers_dir(struct module_kobject *mk)
  2. {
  3. if (!mk || mk->drivers_dir)
  4. return;
  5. mk->drivers_dir = kobject_create_and_add("drivers", &mk->kobj);
  6. }

module_create_drivers_dir()在mk所在的目录下创建一个drivers的目录。不过因为是使用kobject_create_and_add(),所以这个kobject使用默认的dynamic_kobj_ktype。

  1. void module_add_driver(struct module *mod, struct device_driver *drv)
  2. {
  3. char *driver_name;
  4. int no_warn;
  5. struct module_kobject *mk = NULL;
  6. if (!drv)
  7. return;
  8. if (mod)
  9. mk = &mod->mkobj;
  10. else if (drv->mod_name) {
  11. struct kobject *mkobj;
  12. /* Lookup built-in module entry in /sys/modules */
  13. mkobj = kset_find_obj(module_kset, drv->mod_name);
  14. if (mkobj) {
  15. mk = container_of(mkobj, struct module_kobject, kobj);
  16. /* remember our module structure */
  17. drv->p->mkobj = mk;
  18. /* kset_find_obj took a reference */
  19. kobject_put(mkobj);
  20. }
  21. }
  22. if (!mk)
  23. return;
  24. /* Don't check return codes; these calls are idempotent */
  25. no_warn = sysfs_create_link(&drv->p->kobj, &mk->kobj, "module");
  26. driver_name = make_driver_name(drv);
  27. if (driver_name) {
  28. module_create_drivers_dir(mk);
  29. no_warn = sysfs_create_link(mk->drivers_dir, &drv->p->kobj,
  30. driver_name);
  31. kfree(driver_name);
  32. }
  33. }

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()的返回结果。

  1. void module_remove_driver(struct device_driver *drv)
  2. {
  3. struct module_kobject *mk = NULL;
  4. char *driver_name;
  5. if (!drv)
  6. return;
  7. sysfs_remove_link(&drv->p->kobj, "module");
  8. if (drv->owner)
  9. mk = &drv->owner->mkobj;
  10. else if (drv->p->mkobj)
  11. mk = drv->p->mkobj;
  12. if (mk && mk->drivers_dir) {
  13. driver_name = make_driver_name(drv);
  14. if (driver_name) {
  15. sysfs_remove_link(mk->drivers_dir, driver_name);
  16. kfree(driver_name);
  17. }
  18. }
  19. }

module_remove_driver()消除driver与相应module之间的软链接关系。

对于module,应该是另一个议题了,这里只是简单涉及,下节我们将涉及到总线bus,并深入分析device和driver的关系。

Linux内核部件分析 设备驱动模型之driver ---mark 详细相关推荐

  1. [转载]Linux内核大讲堂 (一) 设备驱动的基石驱动模型(1)

    [转载]Linux内核大讲堂 (一) 设备驱动的基石驱动模型(1) 2011年09月13日 可能把驱动模型放在第一章讲会会有点难度,但是只要能跨过这道坎,后面就会轻松很多,驱动模型是整个linux设备 ...

  2. linux内核部件分析之——设备驱动模型之class

    前面看过了设备驱动模型中的bus.device.driver,这三种都是有迹可循的.其中bus代表实际的总线,device代表实际的设备和接口,而driver则对应存在的驱动.但本节要介绍的class ...

  3. linux内核部件分析(十)——设备驱动模型之class,linux内核部件分析(十)——设备驱动模型之class...

    前面看过了设备驱动模型中的bus.device.driver,这三种都是有迹可循的.其中bus代表实际的总线,device代表实际的设备和接口,而driver则对应存在的驱动.但本节要介绍的class ...

  4. linux内核的块设备驱动框架详解

    1.块设备和字符设备的差异 (1)块设备只能以块为单位接受输入和返回输出,而字符设备则以字节为单位.大多数设备是字符设备,因为它们不需要缓冲而且不以固定块大小进行操作; (2)块设备对于 I/O 请求 ...

  5. Linux内核大讲堂之设备驱动的基石驱动模型(1)

    转自 http://blog.csdn.net/gqb_driver/article/details/8449588 转自:无为和尚的Linux内核大讲堂系列,并对个别地方进行了补充更正(见标红处). ...

  6. Linux内核大讲堂 (一) 设备驱动的基石驱动模型(1)

    可能把驱动模型放在第一章讲会会有点难度,但是只要能跨过这道坎,后面就会轻松很多,驱动模型是整个linux设备驱动的基石.大部分人把驱动模型叫做设备模型,但是我查了linux的帮助文档,就是在下载源码路 ...

  7. 【转】Linux内核大讲堂 (一) 设备驱动的基石驱动模型(1)

    原文传送门http://blog.csdn.net/z2007b/archive/2011/05/03/6388753.aspx 可能把驱动模型放在第一章讲会会有点难度,但是只要能跨过这道坎,后面就会 ...

  8. linux设备驱动模型-linux驱动开发第5部分-朱有鹏-专题视频课程

    linux设备驱动模型-linux驱动开发第5部分-4285人已学习 课程介绍         本课程是linux驱动开发的第5个课程,主要内容是linux的设备驱动模型,包括总线.类.设备.驱动等概 ...

  9. linux 内核驱动模型,linux设备驱动模型架构分析 一

    linux设备驱动模型架构分析 一 发布时间:2018-07-04 15:14, 浏览次数:584 , 标签: linux 概述 LDD3中说:"Linux内核需要一个对系统结构的一般性描述 ...

最新文章

  1. 新冠疫情下的AI顶会,ACL、ICLR、ICML等均改为线上会议!
  2. Cisco 路由器防火墙配置命令及实例
  3. [ 一起学React系列 -- 6 ] 秘术之时间旅行-1
  4. multisim 12.0安装教程
  5. 递归函数斐波那契数列python_使用Python函数递归实现斐波那契数列时为什么运行速度很慢?...
  6. C Programming Language
  7. Linux多线程编程实例解析
  8. SpringBootDubboZookeeper远程调用项目搭建
  9. 文件fluent_Win10 中解决FLUENT中UDF 的方法
  10. python汉字编码错误_python爬虫中文编码的问题?
  11. php下载apk文件源码下载,php文件下载功能简单源码示例
  12. jfinal 和bjui 常用前后交互方式
  13. 无缘无故的推荐弹窗,wps的锅,解决办法
  14. win7系统如何加速计算机启动,怎么使win7系统开机加速
  15. Python图形处理
  16. 二维线段树(线段树套线段树)
  17. 高颜值蓝牙耳机有哪些?音质好颜值高的蓝牙耳机推荐
  18. 做相关性分析时,如何排除奇异值Outliers,以增加相关分析的准确性
  19. stc c语言编程,谁有STC 的 EEPROM C程序
  20. java编程有哪些优点呢

热门文章

  1. 回忆篇,那些抹不去的童年记忆
  2. 4.再模仿一个算术题
  3. 怎样才能使你的Mac桌面干净整洁?
  4. 领域驱动设计,为何又死灰复燃了?
  5. 【简单】求英文句子的平均单词长度
  6. 使用sklearn进行增量学习
  7. 百度地图API调用实现获取经纬度以及标注
  8. 简化 Spring 控制器:只须写接口即可
  9. ORA-27301: OS failure message: No buffer space available
  10. 股票作手操盘术-勘误