从Linux2.6开始Linux加入了一套驱动管理和注册机制—platform平台总线驱动模型。platform平台总线是一条虚拟总线,platform_device为相应的设备,platform_driver为相应的驱动。与传统的bus/device/driver机制相比,platform由内核统一进行管理,提高了代码的可移植性和安全性。所谓的platform_device并不是与字符设备、块设备和网络设备并列的概念,而是Linux系统提供的一种附加手段。Linux总线设备驱动模型的框架如下图所示:
从图中我们可以很清楚的看出Linux平台总线设备驱动模型的整体架构。在总线设备驱动模型中,需关心总线、设备和驱动这3个实体,总线将设备和驱动绑定。当系统向内核注册每一个驱动程序时,都要通过调用platform_driver_register函数将驱动程序注册到总线,并将其放入所属总线的drv链表中,注册驱动的时候会调用所属总线的match函数寻找该总线上与之匹配的每一个设备,如果找到与之匹配的设备则会调用相应的PRobe函数将相应的设备和驱动进行绑定;同样的当系统向内核注册每一个设备时,都要通过调用platform_device_register函数将设备注册到总线,并将其放入所属总线的dev链表中,注册设备的时候同样也会调用所属总线的match函数寻找该总线上与之匹配的每一个驱动程序,如果找到与之匹配的驱动程序时会调用相应的probe函数将相应的设备和驱动进行绑定;而这一匹配的过程是由总线自动完成的。 接下来我们就以Linux-3.14.27版本的内核,分析一下总线设备驱动模型的执行机制。
struct platform_driver { int (*probe)(struct platform_device *); int (*remove)(struct platform_device *); void (*shutdown)(struct platform_device *); int (*suspend)(struct platform_device *, pm_message_t state); int (*resume)(struct platform_device *); struct device_driver driver; const struct platform_device_id *id_table; bool prevent_deferred_probe;}; 可以看到platform_driver结构体中包含了probe和remove等相关操作,同时还内嵌了device_driver结构体。2. device_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 */ const struct of_device_id *of_match_table; const struct acpi_device_id *acpi_match_table; 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;}; 其中有一个指向driver_private的指针p,一些与其他的组件相关的联系都被移到这个结构变量中。3. driver_private结构体
struct driver_private { struct kobject kobj; // 在sysfs中代表目录本身 struct klist klist_devices; // 驱动链表即我们上面所说的drv链表 struct klist_node knode_bus; // 挂载在总线上的驱动链表的节点 struct module_kobject *mkobj; // driver与相关的module之间的联系 struct device_driver *driver;};#define to_driver(obj) container_of(obj, struct driver_private, kobj)三、platform设备的相关数据结构
1. platform设备对应的结构体platform_device
struct platform_device { const char *name; // 设备的名字这将代替device->dev_id,用作sys/device下显示的目录名 int id; // 设备id,用于给插入给该总线并且具有相同name的设备编号,如果只有一个设备的话填-1。 bool id_auto; struct device dev; // 内嵌device结构 u32 num_resources; // 资源的数目 struct resource *resource; // 资源 const struct platform_device_id *id_entry; /* MFD cell pointer */ struct mfd_cell *mfd_cell; /* arch specific additions */ struct pdev_archdata archdata;}; 可以看出platform_device结构体中包含了名字、id、资源和内嵌device结构体。2. resource结构体
struct resource { resource_size_t start; // 资源的起始地址 resource_size_t end; // 资源的结束地址 const char *name; unsigned long flags; // 资源的类型 struct resource *parent, *sibling, *child;}; 该结构体非常重要,用于存放设备的资源信息,如IO地址、中断号等。四、platform_driver的注册过程
platform_driver的注册过程可以简化为如下过程:platform_driver *drv; // 定义一个平台驱动并初始化platform_driver_register(drv) // 注册 ->>__platform_driver_register(drv, THIS_MODULE) ->>driver_register(&drv->driver) ->>bus_add_driver(&drv->driver) ->>driver_attach(&drv->driver) ->>bus_for_each_dev((&drv->driver)->bus, NULL, &drv->driver, __driver_attach); ->>__driver_attach(&drv->driver, dev) ->>driver_probe_device(&drv->driver, dev) ->>really_probe(dev, &drv->driver) ->>dev->bus->probe(dev) 或 ->>&drv->driver->probe(dev)将此过程展开如下:1. 分配一个platform_driver结构并调用platform_driver_register函数进行注册
#define platform_driver_register(drv) / __platform_driver_register(drv, THIS_MODULE)int __platform_driver_register(struct platform_driver *drv, struct module *owner){ drv->driver.owner = owner; drv->driver.bus = &platform_bus_type; // 设置driver的bus的类型为platform_bus_type if (drv->probe) // 如果drv含有probe(device_driver类型)则driver上的probe指向总线的probe函数 drv->driver.probe = platform_drv_probe; if (drv->remove) // 如果drv含有remove(device_driver类型)则driver上的remove指向总线的remove函数 drv->driver.remove = platform_drv_remove; if (drv->shutdown) // 如果drv含有shutdown(device_driver类型)则driver上的shutdown指向总线的shutdown函数 drv->driver.shutdown = platform_drv_shutdown; return driver_register(&drv->driver); // 注册平台驱动}2. 调用driver_register函数将驱动添加到总线的drv链表(其中大部分工作通过调用bus_add_driver函数来完成)
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) { printk(KERN_ERR "Error: Driver '%s' is already registered, " "aborting.../n", drv->name); return -EBUSY; } ret = bus_add_driver(drv); // 添加驱动到总线驱动链表即我们上面所说的drv链表 if (ret) return ret; ret = driver_add_groups(drv, drv->groups); if (ret) { bus_remove_driver(drv); return ret; } kobject_uevent(&drv->p->kobj, KOBJ_ADD); return ret;}3. 调用bus_add_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); // 分配一个driver_private结构并初始化(也就是drv->p) 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); //将drv加入sysfs if (error) goto out_unregister; klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); //将drv挂入到总线的链表中 if (drv->bus->p->drivers_autoprobe) { error = driver_attach(drv); //如果总线可以自动的probe,就会调用匹配函数 if (error) goto out_unregister; } module_add_driver(drv->owner, drv); //创建driver相关的模块 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_groups(drv, bus->drv_groups); if (error) { /* How the hell do we get out of this pickle? Give up */ printk(KERN_ERR "%s: driver_create_groups(%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); } } return 0;out_unregister: kobject_put(&priv->kobj); kfree(drv->p); drv->p = NULL;out_put_bus: bus_put(bus); return error;}4. 调用driver_attach函数,对总线drv链表中的驱动与总线dev链表中的设备进行匹配
int driver_attach(struct device_driver *drv){ return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);}(1)bus_for_each_dev函数源码:
int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, int (*fn)(struct device *, void *)){ struct klist_iter i; struct device *dev; int error = 0; if (!bus || !bus->p) return -EINVAL; klist_iter_init_node(&bus->p->klist_devices, &i, (start ? &start->p->knode_bus : NULL)); while ((dev = next_device(&i)) && !error) // 遍历总线dev链表中的所有设备 error = fn(dev, data); // 判断驱动与设备是否匹配,若匹配则将二者绑定 klist_iter_exit(&i); return error;}(2)__driver_attach函数源码:
static int __driver_attach(struct device *dev, void *data){ struct device_driver *drv = data; /* * Lock device and try to bind to it. We drop the error * here and always return 0, because we need to keep trying * to bind to devices and some drivers will return an error * simply if it didn't support the device. * * driver_probe_device() will spit a warning if there * is an error. */ if (!driver_match_device(drv, dev)) // 调用bus的match函数对设备和驱动进行匹配,若不匹配driver_match_device函数的返回值为1,则程序立即返回,若匹配则继续向下执行 return 0; if (dev->parent) /* Needed for USB */ device_lock(dev->parent); device_lock(dev); if (!dev->driver) driver_probe_device(drv, dev); // 若设备和驱动匹配且设备的驱动程序为空,则将该驱动程序绑定到该设备(调用驱动程序的probe函数) device_unlock(dev); if (dev->parent) device_unlock(dev->parent); return 0;}5. 调用driver_probe_device函数,将设备与驱动程序进行绑定(调用probe函数)
int driver_probe_device(struct device_driver *drv, struct device *dev){ int ret = 0; if (!device_is_registered(dev)) return -ENODEV; pr_debug("bus: '%s': %s: matched device %s with driver %s/n", drv->bus->name, __func__, dev_name(dev), drv->name); pm_runtime_barrier(dev); ret = really_probe(dev, drv); // 调用probe pm_request_idle(dev); 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 using pinctrl, bind pins now before probing */ ret = pinctrl_bind_pins(dev); if (ret) goto probe_failed; if (driver_sysfs_add(dev)) { printk(KERN_ERR "%s: driver_sysfs_add(%s) failed/n", __func__, dev_name(dev)); goto probe_failed; } if (dev->bus->probe) { // 如果设备本身含有probe函数,那么久调用设备的probe函数 ret = dev->bus->probe(dev); if (ret) goto probe_failed; } else if (drv->probe) { // 如果设备不含有probe函数,那么就调用驱动程序的probe函数 ret = drv->probe(dev); if (ret) goto probe_failed; } 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;probe_failed: devres_release_all(dev); driver_sysfs_remove(dev); dev->driver = NULL; dev_set_drvdata(dev, NULL); if (ret == -EPROBE_DEFER) { /* Driver requested deferred probing */ dev_info(dev, "Driver %s requests probe deferral/n", drv->name); driver_deferred_probe_add(dev); /* Did a trigger occur while probing? Need to re-trigger if yes */ if (local_trigger_count != atomic_read(&deferred_trigger_count)) driver_deferred_probe_trigger(); } else if (ret != -ENODEV && ret != -ENXIO) { /* driver matched but the probe failed */ printk(KERN_WARNING "%s: probe of %s failed with error %d/n", drv->name, dev_name(dev), ret); } else { pr_debug("%s: probe of %s rejects match %d/n", drv->name, dev_name(dev), ret); } /* * Ignore errors returned by ->probe so that the next driver can try * its luck. */ ret = 0;done: atomic_dec(&probe_count); wake_up(&probe_waitqueue); return ret;}注:若调用probe函数时为drv->probe(dev),则实际调用的函数为platform_drv_probe其源码为static int platform_drv_probe(struct device *_dev){ struct platform_driver *drv = to_platform_driver(_dev->driver); struct platform_device *dev = to_platform_device(_dev); int ret; if (ACPI_HANDLE(_dev)) acpi_dev_pm_attach(_dev, true); ret = drv->probe(dev); // 此处间接地调用了platform_driver提供的probe函数 if (ret && ACPI_HANDLE(_dev)) acpi_dev_pm_detach(_dev, true); if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) { dev_warn(_dev, "probe deferral not supported/n"); ret = -ENXIO; } return ret;}注:platform_driver提供的probe函数即为注册平台驱动时程序员自己编写的probe函数五、platform_device的注册过程
platform_device的注册过程可以简化为以下过程:struct platform_device *pdev; // 定义一个平台设备并初始化platform_device_register(pdev) // 注册 ->>platform_device_add(pdev) ->>device_add(&pdev->dev) ->>bus_probe_device(&pdev->dev) ->>device_attach(&pdev->dev) ->>bus_for_each_drv(&(pdev->dev)->bus, NULL, &pdev->dev, __device_attach) ->>__device_attach(drv, &pdev->dev) ->>driver_probe_device(drv, &pdev->dev) ->>really_probe(&pdev->dev, drv) ->>&pdev->dev->bus->probe(dev) 或 drv->probe(dev)将此过程展开如下:1. 分配一个platform_device结构并调用platform_device_register函数进行注册
int platform_device_register(struct platform_device *pdev){ device_initialize(&pdev->dev); // 初始化platform_device的device成员 arch_setup_pdev_archdata(pdev); return platform_device_add(pdev); // 向内核添加一个平台设备}2. 调用platform_device_add函数向内核添加一个平台设备
int platform_device_add(struct platform_device *pdev){ int i, ret; if (!pdev) return -EINVAL; if (!pdev->dev.parent) //如果pdev->dev.parent为空则将pdev->dev.parent设置为platform_bus pdev->dev.parent = &platform_bus; pdev->dev.bus = &platform_bus_type; // 设置总线类型为platform_bus_type switch (pdev->id) { // 分配名字 default: dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); break; case PLATFORM_DEVID_NONE: dev_set_name(&pdev->dev, "%s", pdev->name); break; case PLATFORM_DEVID_AUTO: /* * Automatically allocated device ID. We mark it as such so * that we remember it must be freed, and we append a suffix * to avoid namespace collision with explicit IDs. */ ret = ida_simple_get(&platform_devid_ida, 0, 0, GFP_KERNEL); if (ret < 0) goto err_out; pdev->id = ret; pdev->id_auto = true; dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id); break; } for (i = 0; i < pdev->num_resources; i++) { // 获取资源 struct resource *p, *r = &pdev->resource[i]; if (r->name == NULL) r->name = dev_name(&pdev->dev); p = r->parent; if (!p) { // 设置资源类型 if (resource_type(r) == IORESOURCE_MEM) p = &iomem_resource; else if (resource_type(r) == IORESOURCE_IO) p = &ioport_resource; } if (p && insert_resource(p, r)) { dev_err(&pdev->dev, "failed to claim resource %d/n", i); ret = -EBUSY; goto failed; } } pr_debug("Registering platform device '%s'. Parent at %s/n", dev_name(&pdev->dev), dev_name(pdev->dev.parent)); ret = device_add(&pdev->dev); // 向内核添加一个device if (ret == 0) return ret; failed: if (pdev->id_auto) { ida_simple_remove(&platform_devid_ida, pdev->id); pdev->id = PLATFORM_DEVID_AUTO; } while (--i >= 0) { struct resource *r = &pdev->resource[i]; unsigned long type = resource_type(r); if (type == IORESOURCE_MEM || type == IORESOURCE_IO) release_resource(r); } err_out: return ret;}3. 调用device_add函数向内核添加一个device
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; 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->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); if (error) goto Error; /* notify platform of device entry */ if (platform_notify) platform_notify(dev); error = device_create_file(dev, &dev_attr_uevent); if (error) goto attrError; if (MAJOR(dev->devt)) { error = device_create_file(dev, &dev_attr_dev); if (error) goto ueventattrError; error = device_create_sys_dev_entry(dev); if (error) goto devtattrError; devtmpfs_create_node(dev); } error = device_add_class_symlinks(dev); if (error) goto SymlinkError; error = device_add_attrs(dev); if (error) goto AttrsError; error = bus_add_device(dev); if (error) goto BusError; error = dpm_sysfs_add(dev); if (error) goto DPMError; device_pm_add(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; DPMError: bus_remove_device(dev); BusError: device_remove_attrs(dev); AttrsError: device_remove_class_symlinks(dev); SymlinkError: if (MAJOR(dev->devt)) devtmpfs_delete_node(dev); if (MAJOR(dev->devt)) device_remove_sys_dev_entry(dev); devtattrError: if (MAJOR(dev->devt)) device_remove_file(dev, &dev_attr_dev); ueventattrError: device_remove_file(dev, &dev_attr_uevent); attrError: kobject_uevent(&dev->kobj, KOBJ_REMOVE); kobject_del(&dev->kobj); Error: cleanup_device_parent(dev); if (parent) put_device(parent);name_error: kfree(dev->p); dev->p = NULL; goto done;}4. 调用bus_probe_device函数为总线上的设备寻找驱动
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); // 调用device_attach()进行实际的寻找 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);}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); // 遍历bus的drv链表为设备寻找驱动 pm_request_idle(dev); }out_unlock: device_unlock(dev); return ret;}(1) bus_for_each_drv函数源码:
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) // 遍历整个drv链表 error = fn(drv, data); // 寻找该设备匹配的驱动程序,若匹配则将二者绑定 klist_iter_exit(&i); return error;}(2)__device_attach函数源码:
static int __device_attach(struct device_driver *drv, void *data){ struct device *dev = data; if (!driver_match_device(drv, dev)) // 调用bus的match函数对设备和驱动进行匹配,若不匹配driver_match_device函数的返回值为1,则程序立即返回,若匹配则继续向下执行 return 0; return driver_probe_device(drv, dev); // 若设备和驱动匹配,则将该驱动程序绑定到该设备}static inline int driver_match_device(struct device_driver *drv, struct device *dev){ return drv->bus->match ? drv->bus->match(dev, drv) : 1;}5. 调用driver_probe_device将设备与驱动程序进行绑定(调用probe函数)
int driver_probe_device(struct device_driver *drv, struct device *dev){ int ret = 0; if (!device_is_registered(dev)) return -ENODEV; pr_debug("bus: '%s': %s: matched device %s with driver %s/n", drv->bus->name, __func__, dev_name(dev), drv->name); pm_runtime_barrier(dev); ret = really_probe(dev, drv); // 调用probe pm_request_idle(dev); 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 using pinctrl, bind pins now before probing */ ret = pinctrl_bind_pins(dev); if (ret) goto probe_failed; if (driver_sysfs_add(dev)) { printk(KERN_ERR "%s: driver_sysfs_add(%s) failed/n", __func__, dev_name(dev)); goto probe_failed; } if (dev->bus->probe) { // 如果设备本身含有probe函数,那么久调用设备的probe函数 ret = dev->bus->probe(dev); if (ret) goto probe_failed; } else if (drv->probe) { // 如果设备不含有probe函数,那么就调用驱动程序的probe函数 ret = drv->probe(dev); if (ret) goto probe_failed; } 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;probe_failed: devres_release_all(dev); driver_sysfs_remove(dev); dev->driver = NULL; dev_set_drvdata(dev, NULL); if (ret == -EPROBE_DEFER) { /* Driver requested deferred probing */ dev_info(dev, "Driver %s requests probe deferral/n", drv->name); driver_deferred_probe_add(dev); /* Did a trigger occur while probing? Need to re-trigger if yes */ if (local_trigger_count != atomic_read(&deferred_trigger_count)) driver_deferred_probe_trigger(); } else if (ret != -ENODEV && ret != -ENXIO) { /* driver matched but the probe failed */ printk(KERN_WARNING "%s: probe of %s failed with error %d/n", drv->name, dev_name(dev), ret); } else { pr_debug("%s: probe of %s rejects match %d/n", drv->name, dev_name(dev), ret); } /* * Ignore errors returned by ->probe so that the next driver can try * its luck. */ ret = 0;done: atomic_dec(&probe_count); wake_up(&probe_waitqueue); return ret;} 到此Linux内核的总线设备驱动模型分析完毕。从上面的分析过程可以看出,所谓的platform_device并不是与字符设备、块设备和网络设备并列的概念,而是Linux系统提供的一种附加手段。
新闻热点
疑难解答