linux内核总线驱动模型-设备篇

tech2024-07-24  55

上篇主要对总线进行了一些梳理,现在来看看内核对于设备的处理过程。

struct device { struct device *parent; /*指向父上游的指针*/ struct device_private *p; struct kobject kobj; /*用它联系到sysfs中*/ const char *init_name; /* initial name of the device */ const struct device_type *type; /*设备属性文件*/ struct mutex mutex; /* mutex to synchronize calls to * its driver. */ struct bus_type *bus; /*属于那条总线*/ struct device_driver *driver; /*那个驱动能匹配该设备*/ void *platform_data; /* Platform specific data, device . . . void (*release)(struct device *dev); };

其中比较重要的是释放函数,当这个设备被删除时,内核调用该方法,所有的注册到内核的设备结构必须有也给释放方法。和总线一样,device也有一个联系总线,设备,驱动程序的数据结构。

/* interface for exporting device attributes */ struct device_attribute { struct attribute attr; ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf); ssize_t (*store)(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); }; //定义DEVICE_ATTR ( _name , _mode , _show , _store ) \ struct device_attribute dev_attr _ ## _ name = __ATTR ( _name , _mode , _show , _store )

该接口用于向外暴露设备属性。

同总线过程一样,device的初始化也是在driver_init完成初始化的:

int __init devices_init(void) { /*创建/sys目录下的devices目录*/ devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL); if (!devices_kset) return -ENOMEM; /*创建/sys目录下的dev目录*/ dev_kobj = kobject_create_and_add("dev", NULL); if (!dev_kobj) goto dev_kobj_err; /*创建dev目录下的block目录*/ sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj); if (!sysfs_dev_block_kobj) goto block_kobj_err; /*创建dev目录的char目录*/ sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj); if (!sysfs_dev_char_kobj) goto char_kobj_err; return 0; char_kobj_err: kobject_put(sysfs_dev_block_kobj); block_kobj_err: kobject_put(dev_kobj); dev_kobj_err: kset_unregister(devices_kset); return -ENOMEM; }

这个函数干的事情很明显,就是创建在sysfs中的设备目录和dev目录,还在dev目录下创建了block和char两个子目录。

下面分析下设备的注册过程:

int device_register(struct device *dev) { /*设备初始化*/ device_initialize(dev); /*将设备添加到系统中*/ return device_add(dev); } void device_initialize(struct device *dev) { dev->kobj.kset = devices_kset; kobject_init(&dev->kobj, &device_ktype); INIT_LIST_HEAD(&dev->dma_pools); mutex_init(&dev->mutex); lockdep_set_novalidate_class(&dev->mutex); spin_lock_init(&dev->devres_lock); INIT_LIST_HEAD(&dev->devres_head); device_pm_init(dev); set_dev_node(dev, -1); }

device_initialize主要是完成设备结构的初始化,把设备中能初始化的部分全部初始化。

int device_add(struct device *dev) { struct device *parent = NULL; struct kobject *kobj; struct class_interface *class_intf; int error = -EINVAL; /*增加设备的应用计数*/ dev = get_device(dev); if (!dev) goto done; /*分配device_private 空间并初始化*/ if (!dev->p) { error = device_private_init(dev); if (error) goto done; } /* * for statically allocated devices, which should all be converted * some day, we need to initialize the name. We prevent reading back * the name, and force the use of dev_name() */ if (dev->init_name) { dev_set_name(dev, "%s", dev->init_name);/*设置dev的名字*/ dev->init_name = NULL; } /* subsystems can specify simple device enumeration */ if (!dev_name(dev) && dev->bus && dev->bus->dev_name) dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id); if (!dev_name(dev)) { error = -EINVAL; goto name_error; } pr_debug("device: '%s': %s\n", dev_name(dev), __func__); parent = get_device(dev->parent); kobj = get_device_parent(dev, parent); if (kobj) dev->kobj.parent = kobj; /* use parent numa_node */ if (parent) set_dev_node(dev, dev_to_node(parent)); /* first, register with generic layer. */ /* we require the name to be set before, and pass NULL */ error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);/*将设备加入到sys/中*/ if (error) goto Error; /* notify platform of device entry */ if (platform_notify) platform_notify(dev); /*创建uevent文件*/ error = device_create_file(dev, &dev_attr_uevent); if (error) goto attrError; /*dev与class创建软链接*/ error = device_add_class_symlinks(dev); if (error) goto SymlinkError; error = device_add_attrs(dev); if (error) goto AttrsError; /*将设备加入到bus的设备链表中*/ error = bus_add_device(dev); if (error) goto BusError; error = dpm_sysfs_add(dev); if (error) goto DPMError; device_pm_add(dev); /*没有设置主设备号,就创建dev*/ if (MAJOR(dev->devt)) { error = device_create_file(dev, &dev_attr_dev); if (error) goto DevAttrError; /*创建软链接*/ error = device_create_sys_dev_entry(dev); if (error) goto SysEntryError; devtmpfs_create_node(dev); } /* Notify clients of device addition. This call must come * after dpm_sysfs_add() and before kobject_uevent(). */ if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_ADD_DEVICE, dev); kobject_uevent(&dev->kobj, KOBJ_ADD); /*为设备寻找合适的驱动*/ bus_probe_device(dev); if (parent) klist_add_tail(&dev->p->knode_parent, &parent->p->klist_children); if (dev->class) { mutex_lock(&dev->class->p->mutex); /* tie the class to the device */ klist_add_tail(&dev->knode_class, &dev->class->p->klist_devices); /* notify any interfaces that the device is here */ list_for_each_entry(class_intf, &dev->class->p->interfaces, node) if (class_intf->add_dev) class_intf->add_dev(dev, class_intf); mutex_unlock(&dev->class->p->mutex); } done: put_device(dev); return error; }

第8行:增加设备的引用计数

第12-17行:分配device_private空间并初始化

第24-36行:设置驱动的名字

第51行:将设备加入到sys文件系统中

第59行:创建设备属性uevent文件

第63行:创建dev和class的软链接

第70行:将设备加入到bus的设备链表中

第99行:为设备寻找合适的驱动,我们重点看下这个函数

void bus_probe_device(struct device *dev) { struct bus_type *bus = dev->bus; struct subsys_interface *sif; int ret; if (!bus) return; /*允许设备自动探测驱动*/ if (bus->p->drivers_autoprobe) { ret = device_attach(dev); WARN_ON(ret < 0); } mutex_lock(&bus->p->mutex); list_for_each_entry(sif, &bus->p->interfaces, node) if (sif->add_dev) sif->add_dev(dev, sif); mutex_unlock(&bus->p->mutex); }

第10行:drivers_autoprobe在总线注册时被设置为1,所以这里执行device_attach进行设备和驱动的匹配

int device_attach(struct device *dev) { int ret = 0; device_lock(dev); if (dev->driver) { if (klist_node_attached(&dev->p->knode_driver)) { ret = 1; goto out_unlock; } ret = device_bind_driver(dev); if (ret == 0) ret = 1; else { dev->driver = NULL; ret = 0; } } else {/*进行驱动和设备的匹配*/ ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); pm_request_idle(dev); } out_unlock: device_unlock(dev); return ret; }

device_attach函数中走到19行,进行设备和驱动的匹配:

int bus_for_each_drv(struct bus_type *bus, struct device_driver *start, void *data, int (*fn)(struct device_driver *, void *)) { struct klist_iter i; struct device_driver *drv; int error = 0; if (!bus) return -EINVAL; klist_iter_init_node(&bus->p->klist_drivers, &i, start ? &start->p->knode_bus : NULL); while ((drv = next_driver(&i)) && !error) error = fn(drv, data); klist_iter_exit(&i); return error; }

第13-14行:遍历bus上所有的驱动,调用__device_attach进行匹配,为设备找驱动。

static int __device_attach(struct device_driver *drv, void *data) { struct device *dev = data; /*调用bus-match函数进行匹配*/ if (!driver_match_device(drv, dev)) return 0; /* 匹配成功以后执行probe 如果bus有probe函数,优先执行, 否则执行drvier的probe函数 */ return driver_probe_device(drv, dev); }

__device_attach调用driver_match_device进行匹配:

static inline int driver_match_device(struct device_driver *drv, struct device *dev) { return drv->bus->match ? drv->bus->match(dev, drv) : 1; }

匹配函数是在总线注册时,实现的match函数。在匹配成功后,调用probe函数:

int driver_probe_device(struct device_driver *drv, struct device *dev) { int ret = 0; . ret = really_probe(dev, drv); . return ret; } static int really_probe(struct device *dev, struct device_driver *drv) { int ret = 0; int local_trigger_count = atomic_read(&deferred_trigger_count); atomic_inc(&probe_count); pr_debug("bus: '%s': %s: probing driver %s with device %s\n", drv->bus->name, __func__, drv->name, dev_name(dev)); WARN_ON(!list_empty(&dev->devres_head)); dev->driver = drv; . . if (dev->bus->probe) { ret = dev->bus->probe(dev); if (ret) goto probe_failed; } else if (drv->probe) { ret = drv->probe(dev); if (ret) goto probe_failed; } if (dev->pm_domain && dev->pm_domain->sync) dev->pm_domain->sync(dev); driver_bound(dev); ret = 1; pr_debug("bus: '%s': %s: bound device %s to driver %s\n", drv->bus->name, __func__, dev_name(dev), drv->name); goto done; . . }

really_probe函数中重点看17-25行,如果bus->probe不为空,则执行总线的probe函数,如果不存在,则查看drv->probe,不为空的话,执行驱动的probe函数。

下面来看下例子:

extern bus_type my_bus_type; extern struct device my_bus; static void my_dev_release(struct device *dev) { } struct device my_dev = { .bus = &my_bus_type, .parent = &my_bus, .release = my_dev_release, }; static ssize_t mydev_show(struct device *dev, char * buf) { return sprintf(buf, "%s \n", "这是我的设备"); } static DEVICE_ATTR(dev, S_IRUGO, mydev_show, NULL); static int __init my_device_init(void) { int ret = 0 ; / *初始化设备* / strncpy(my_dev.bus_id , “my_dev” , BUS_ID_SIZE ); / *注册设备* / device_register(&my_dev); device_create_file (&my_dev, &dev_attr_dev); return ret; }
最新回复(0)