platform_driver平台驱动注册和注销过程 I2C设备与驱动的关联 作者:leeoo 联系方式:neu_linuxer@hotmail.com 在Linux操作系统中,驱动程序的加载分为两种:内核启动时自动加载和用户手动加载;硬件设备也可以采用两种方式添加到系统中:在系统启动前及系统运行时的热插拨。下面,我们以arm体系结构下的at91处理器中的I2C控制器为例,介绍一下硬件设备及相关的驱动程序是如何绑定及松绑的。 1. 平台驱动注册过程 1.1 at91_i2c_init()函数 在文件drivers/i2c/busses/i2c-at91.c中,定义了结构体struct platform_driver并进行了初始化,通过使用module_init()宏进行声明,当模块被加载到内核时会调用 at91_i2c_init()函数。在此函数中,调用了platform_driver_register()函数来完成注册。 static struct platform_driver at91_i2c_driver = { .probe = at91_i2c_probe, .remove = __devexit_p(at91_i2c_remove), .suspend = at91_i2c_suspend, .resume = at91_i2c_resume, .driver = { .name = "at91_i2c", .owner = THIS_MODULE, }, }; static int __init at91_i2c_init(void) { return platform_driver_register(&at91_i2c_driver); } 1.2 platform_driver_register()函数 在文件drivers/base/platform.c中,实现并导出了platform_driver_register()函数,以便使其他模块中的函数可以调用此函数。它在完成简单的包装后,调用了driver_register()函数,完成了从平台实现到Linux内核实现的过渡。 在此,我们需要关注一下platform_match()和platform_drv_probe()函数。platform_match() 函数确定驱动与设备的关联,而platform_drv_probe()函数会在随后介绍的函数中被调用。 //比较驱动信息中的name与设备信息中的name两者是否一致 static int platform_match(struct device * dev, struct device_driver * drv) { struct platform_device *pdev = container_of(dev, struct platform_device, dev); return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0); } struct bus_type platform_bus_type = { .name = "platform", .dev_attrs = platform_dev_attrs, .match = platform_match, .uevent = platform_uevent, .suspend = platform_suspend, .suspend_late = platform_suspend_late, .resume_early = platform_resume_early, .resume = platform_resume, }; EXPORT_SYMBOL_GPL(platform_bus_type); /** * platform_driver_register * @drv: platform driver structure */ int platform_driver_register(struct platform_driver *drv) { drv->driver.bus = &platform_bus_type; //在really_probe函数中,回调了platform_drv_probe函数 if (drv->probe) drv->driver.probe = platform_drv_probe; if (drv->remove) drv->driver.remove = platform_drv_remove; if (drv->shutdown) drv->driver.shutdown = platform_drv_shutdown; if (drv->suspend) drv->driver.suspend = platform_drv_suspend; if (drv->resume) drv->driver.resume = platform_drv_resume; return driver_register(&drv->driver); } EXPORT_SYMBOL_GPL(platform_driver_register); 1.3 driver_register()函数 在文件drivers/base/driver.c中,实现了driver_register()函数。在此函数中,初始化结构体struct device_driver中的klist_device和unloaded字段,通过klist_device字段,可以保存此驱动支持的设备链表,通过“完成”接口机制,完成线程间的同步。链表和“完成”接口的详细信息可以参考文献[1]。返回bus_add_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. * * The one interesting aspect is that we setup @drv->unloaded * as a completion that gets complete when the driver reference * count reaches 0. */ int driver_register(struct device_driver * drv) { 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); } klist_init(&drv->klist_devices, NULL, NULL); init_completion(&drv->unloaded); return bus_add_driver(drv); } 1.4 bus_add_driver()函数 在文件drivers/base/bus.c中实现了bus_add_driver()函数,它通过语句klist_add_tail(&drv->knode_bus, &bus->klist_drivers); 将驱动信息保存到总线结构中,在设备注册过程中,我们就可以明白此语句的作用了。在此语句之前,调用了driver_attach()函数。 /** * bus_add_driver - Add a driver to the bus. * @drv: driver. * */ int bus_add_driver(struct device_driver *drv) { struct bus_type * bus = get_bus(drv->bus); int error = 0; if (!bus) return 0; pr_debug("bus %s: add driver %s/n", bus->name, drv->name); error = kobject_set_name(&drv->kobj, "%s", drv->name); if (error) goto out_put_bus; drv->kobj.kset = &bus->drivers; if ((error = kobject_register(&drv->kobj))) goto out_put_bus; error = driver_attach(drv); if (error) goto out_unregister; klist_add_tail(&drv->knode_bus, &bus->klist_drivers); module_add_driver(drv->owner, drv); 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", __FUNCTION__, drv->name); } error = add_bind_files(drv); if (error) { /* Ditto */ printk(KERN_ERR "%s: add_bind_files(%s) failed/n", __FUNCTION__, drv->name); } return error; out_unregister: kobject_unregister(&drv->kobj); out_put_bus: put_bus(bus); return error; } 1.5 dd.c文件 在文件drivers/base/dd.c中,实现了设备与驱动交互的核心函数。 1.5.1 driver_attach()函数 函数driver_attach()返回bus_for_each_dev()函数的运行结果。bus_for_each_dev()函数的原型如下: int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, int (*fn) (struct device *, void *)); 该函数迭代了在总线上的每个设备,将相关的device结构传递给fn,同时传递data值。如果start是NULL,将从总线上的第一个设备开始迭代;否则将从start后的第一个设备开始迭代。如果fn返回一个非零值,将停止迭代,而这个值也会从该函数返回(摘自<>第三版)。 该函数是如何知道总线上的每个设备的呢?在设备注册过程中,我会详细介绍。 /* * drivers/base/dd.c - The core device/driver interactions. * * This file contains the (sometimes tricky) code that controls the * interactions between devices and drivers, which primarily includes * driver binding and unbinding. */ /** * driver_attach - try to bind driver to devices. * @drv: driver. * * Walk the list of devices that the bus has on it and try to * match the driver with each one. If driver_probe_device() * returns 0 and the @dev->driver is set, we've found a * compatible pair. */ int driver_attach(struct device_driver * drv) { return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); } 1.5.2 __driver_attach()函数 函数__driver_attach()在调用driver_probe_device()函数前,需要进行线程间的互斥处理。 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 (dev->parent) /* Needed for USB */ down(&dev->parent->sem); down(&dev->sem); if (!dev->driver) driver_probe_device(drv, dev); up(&dev->sem); if (dev->parent) up(&dev->parent->sem); return 0; } 1.5.3 driver_probe_device()函数 在driver_probe_device()函数中,调用了match函数platform_match(),如果它返回0,表示驱动与设备不一致,函数返回;否则,调用really_probe()函数。 /** * driver_probe_device - attempt to bind device & driver together * @drv: driver to bind a device to * @dev: device to try to bind to the driver * * First, we call the bus's match function, if one present, which should * compare the device IDs the driver supports with the device IDs of the * device. Note we don't do this ourselves because we don't know the * format of the ID structures, nor what is to be considered a match and * what is not. * * This function returns 1 if a match is found, an error if one occurs * (that is not -ENODEV or -ENXIO), and 0 otherwise. * * This function must be called with @dev->sem held. When called for a * USB interface, @dev->parent->sem must be held as well. */ int driver_probe_device(struct device_driver * drv, struct device * dev) { struct stupid_thread_structure *data; struct task_struct *probe_task; int ret = 0; if (!device_is_registered(dev)) return -ENODEV; if (drv->bus->match && !drv->bus->match(dev, drv)) goto done; pr_debug("%s: Matched Device %s with Driver %s/n", drv->bus->name, dev->bus_id, drv->name); data = kmalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; data->drv = drv; data->dev = dev; if (drv->multithread_probe) { probe_task = kthread_run(really_probe, data, "probe-%s", dev->bus_id); if (IS_ERR(probe_task)) ret = really_probe(data); } else ret = really_probe(data); done: return ret; } struct stupid_thread_structure { struct device_driver *drv; struct device *dev; }; 1.5.4 really_probe()函数 在really_probe()函数中,实现了设备与驱动的绑定。语句如下:dev->driver = drv;和 ret = drv->probe(dev); probe()函数的实现如下: include/linux/platform_device.h #define to_platform_device(x) container_of((x), struct platform_device, dev) drivers/base/platform.c #define to_platform_driver(drv) (container_of((drv), struct platform_driver, driver)) 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); return drv->probe(dev); } 在此函数中,回调了我们在i2c-at91.c文件中实现的探测函数at91_i2c_probe(),至此,平台驱动的注册过程结束。 static atomic_t probe_count = ATOMIC_INIT(0); static DECLARE_WAIT_QUEUE_HEAD(probe_waitqueue); static int really_probe(void *void_data) { struct stupid_thread_structure *data = void_data; struct device_driver *drv = data->drv; struct device *dev = data->dev; int ret = 0; atomic_inc(&probe_count); pr_debug("%s: Probing driver %s with device %s/n", drv->bus->name, drv->name, dev->bus_id); WARN_ON(!list_empty(&dev->devres_head)); dev->driver = drv; if (driver_sysfs_add(dev)) { printk(KERN_ERR "%s: driver_sysfs_add(%s) failed/n", __FUNCTION__, dev->bus_id); goto probe_failed; } 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; } //设备与驱动绑定后,对系统中已注册的组件进行事件通知。 driver_bound(dev); ret = 1; pr_debug("%s: Bound Device %s to Driver %s/n", drv->bus->name, dev->bus_id, drv->name); goto done; probe_failed: devres_release_all(dev); driver_sysfs_remove(dev); dev->driver = NULL; 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->bus_id, ret); } /* * Ignore errors returned by ->probe so that the next driver can try * its luck. */ ret = 0; done: kfree(data); atomic_dec(&probe_count); wake_up(&probe_waitqueue); return ret; } 2. 平台驱动卸载过程 平台驱动卸载过程是注册的逆过程,详细信息可参考注册过程进行分析。 2.1 at91_i2c_exit()函数 static void __exit at91_i2c_exit(void) { platform_driver_unregister(&at91_i2c_driver); } 2.2 platform_driver_unregister()函数 /** * platform_driver_unregister * @drv: platform driver structure */ void platform_driver_unregister(struct platform_driver *drv) { driver_unregister(&drv->driver); } EXPORT_SYMBOL_GPL(platform_driver_unregister); 2.3 driver_unregister()函数 /** * driver_unregister - remove driver from system. * @drv: driver. * * Again, we pass off most of the work to the bus-level call. * * Though, once that is done, we wait until @drv->unloaded is completed. * This will block until the driver refcount reaches 0, and it is * released. Only modular drivers will call this function, and we * have to guarantee that it won't complete, letting the driver * unload until all references are gone. */ void driver_unregister(struct device_driver * drv) { bus_remove_driver(drv); /* * If the driver is a module, we are probably in * the module unload path, and we want to wait * for everything to unload before we can actually * finish the unload. */ if (drv->owner) wait_for_completion(&drv->unloaded); } 2.4 bus_remove_driver()函数 /** * 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; remove_bind_files(drv); driver_remove_attrs(drv->bus, drv); klist_remove(&drv->knode_bus); pr_debug("bus %s: remove driver %s/n", drv->bus->name, drv->name); driver_detach(drv); module_remove_driver(drv); kobject_unregister(&drv->kobj); put_bus(drv->bus); } 2.5 driver_detach()函数 /** * driver_detach - detach driver from all devices it controls. * @drv: driver. */ void driver_detach(struct device_driver * drv) { struct device * dev; for (;;) { spin_lock(&drv->klist_devices.k_lock); if (list_empty(&drv->klist_devices.k_list)) { spin_unlock(&drv->klist_devices.k_lock); break; } dev = list_entry(drv->klist_devices.k_list.prev, struct device, knode_driver.n_node); get_device(dev); spin_unlock(&drv->klist_devices.k_lock); if (dev->parent) /* Needed for USB */ down(&dev->parent->sem); down(&dev->sem); if (dev->driver == drv) __device_release_driver(dev); up(&dev->sem); if (dev->parent) up(&dev->parent->sem); put_device(dev); } } 2.6 __device_release_driver()函数 /** * device_release_driver - manually detach device from driver. * @dev: device. * * Manually detach device from driver. * * __device_release_driver() must be called with @dev->sem held. * When called for a USB interface, @dev->parent->sem must be held * as well. */ static void __device_release_driver(struct device * dev) { struct device_driver * drv; drv = dev->driver; if (drv) { get_driver(drv); driver_sysfs_remove(dev); sysfs_remove_link(&dev->kobj, "driver"); klist_remove(&dev->knode_driver); if (dev->bus) blocking_notifier_call_chain(&dev->bus->bus_notifier, BUS_NOTIFY_UNBIND_DRIVER, dev); if (dev->bus && dev->bus->remove) dev->bus->remove(dev); else if (drv->remove) drv->remove(dev); devres_release_all(dev); dev->driver = NULL; put_driver(drv); } } 2.7 platform_drv_remove()函数 static int platform_drv_remove(struct device *_dev) { struct platform_driver *drv = to_platform_driver(_dev->driver); struct platform_device *dev = to_platform_device(_dev); return drv->remove(dev); } 在此函数中,回调了我们在i2c-at91.c文件中实现的移除函数at91_i2c_remove(),至此,平台驱动的卸载过程结束。 在注册和卸载过程中,Linux采用了一些变量来保存相关的信息,比如引用计数、通知链等,感兴趣的人员,可以详细的阅读此部分内容。 3. 设备注册过程 3.1 at91_add_device_i2c()函数 在文件arch/arm/mach-at91/at91sam9263_devices.c中,对I2C设备进行了定义。 /* -------------------------------------------------------------------- * TWI (i2c) * -------------------------------------------------------------------- */ #if defined(CONFIG_I2C_AT91) || defined(CONFIG_I2C_AT91_MODULE) static struct resource twi_resources[] = { [0] = { .start = AT91SAM9263_BASE_TWI, .end = AT91SAM9263_BASE_TWI + SZ_16K - 1, .flags = IORESOURCE_MEM, }, [1] = { .start = AT91SAM9263_ID_TWI, .end = AT91SAM9263_ID_TWI, .flags = IORESOURCE_IRQ, }, }; static struct platform_device at91sam9263_twi_device = { //此名字与驱动信息中的名字"at91_i2c"是一致的,在match()函数调用中,会返回1。 .name = "at91_i2c", .id = -1, .resource = twi_resources, .num_resources = ARRAY_SIZE(twi_resources), }; void __init at91_add_device_i2c(void) { /* pins used for TWI interface */ at91_set_A_periph(AT91_PIN_PB4, 0); /* TWD */ at91_set_multi_drive(AT91_PIN_PB4, 1); at91_set_A_periph(AT91_PIN_PB5, 0); /* TWCK */ at91_set_multi_drive(AT91_PIN_PB5, 1); //对设备进行注册 platform_device_register(&at91sam9263_twi_device); } #else void __init at91_add_device_i2c(void) {} #endif 3.2 platform.c文件 在文件drivers/base/platform.c中,实现了下面的函数。 3.2.1 platform_device_register()函数 /** * platform_device_register - add a platform-level device * @pdev: platform device we're adding * */ int platform_device_register(struct platform_device * pdev) { device_initialize(&pdev->dev); return platform_device_add(pdev); } EXPORT_SYMBOL_GPL(platform_device_register); 3.2.2 platform_device_add()函数 /** * platform_device_add - add a platform device to device hierarchy * @pdev: platform device we're adding * * This is part 2 of platform_device_register(), though may be called * separately _iff_ pdev was allocated by platform_device_alloc(). */ int platform_device_add(struct platform_device *pdev) { int i, ret = 0; if (!pdev) return -EINVAL; if (!pdev->dev.parent) pdev->dev.parent = &platform_bus; pdev->dev.bus = &platform_bus_type; if (pdev->id != -1) snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%u", pdev->name, pdev->id); else strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE); for (i = 0; i < pdev->num_resources; i++) { struct resource *p, *r = &pdev->resource[i]; if (r->name == NULL) r->name = pdev->dev.bus_id; p = r->parent; if (!p) { if (r->flags & IORESOURCE_MEM) p = &iomem_resource; else if (r->flags & IORESOURCE_IO) p = &ioport_resource; } if (p && insert_resource(p, r)) { printk(KERN_ERR "%s: failed to claim resource %d/n", pdev->dev.bus_id, i); ret = -EBUSY; goto failed; } } pr_debug("Registering platform device '%s'. Parent at %s/n", pdev->dev.bus_id, pdev->dev.parent->bus_id); ret = device_add(&pdev->dev); if (ret == 0) return ret; failed: while (--i >= 0) if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO)) release_resource(&pdev->resource[i]); return ret; } EXPORT_SYMBOL_GPL(platform_device_add); 3.3 device_add()函数 在文件drivers/base/core.c中实现了device_add()函数。 /** * device_add - add device to device hierarchy. * @dev: device. * * This is part 2 of device_register(), though may be called * separately _iff_ device_initialize() has been called separately. * * This adds it to the kobject hierarchy via kobject_add(), adds it * to the global and sibling lists for the device, then * adds it to the other relevant subsystems of the driver model. */ int device_add(struct device *dev) { struct device *parent = NULL; char *class_name = NULL; struct class_interface *class_intf; int error = -EINVAL; dev = get_device(dev); if (!dev || !strlen(dev->bus_id)) goto Error; pr_debug("DEV: registering device: ID = '%s'/n", dev->bus_id); parent = get_device(dev->parent); error = setup_parent(dev, parent); if (error) goto Error; /* first, register with generic layer. */ kobject_set_name(&dev->kobj, "%s", dev->bus_id); error = kobject_add(&dev->kobj); if (error) goto Error; /* notify platform of device entry */ if (platform_notify) platform_notify(dev); /* notify clients of device entry (new way) */ if (dev->bus) blocking_notifier_call_chain(&dev->bus->bus_notifier, BUS_NOTIFY_ADD_DEVICE, dev); dev->uevent_attr.attr.name = "uevent"; dev->uevent_attr.attr.mode = S_IWUSR; if (dev->driver) dev->uevent_attr.attr.owner = dev->driver->owner; dev->uevent_attr.store = store_uevent; error = device_create_file(dev, &dev->uevent_attr); if (error) goto attrError; if (MAJOR(dev->devt)) { struct device_attribute *attr; attr = kzalloc(sizeof(*attr), GFP_KERNEL); if (!attr) { error = -ENOMEM; goto ueventattrError; } attr->attr.name = "dev"; attr->attr.mode = S_IRUGO; if (dev->driver) attr->attr.owner = dev->driver->owner; attr->show = show_dev; error = device_create_file(dev, attr); if (error) { kfree(attr); goto ueventattrError; } dev->devt_attr = attr; } if (dev->class) { sysfs_create_link(&dev->kobj, &dev->class->subsys.kset.kobj, "subsystem"); /* If this is not a "fake" compatible device, then create the * symlink from the class to the device. */ if (dev->kobj.parent != &dev->class->subsys.kset.kobj) sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj, dev->bus_id); if (parent) { sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device"); #ifdef CONFIG_SYSFS_DEPRECATED class_name = make_class_name(dev->class->name, &dev->kobj); if (class_name) sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name); #endif } } if ((error = device_add_attrs(dev))) goto AttrsError; if ((error = device_add_groups(dev))) goto GroupError; if ((error = device_pm_add(dev))) goto PMError; if ((error = bus_add_device(dev))) goto BusError; if (!dev->uevent_suppress) kobject_uevent(&dev->kobj, KOBJ_ADD); if ((error = bus_attach_device(dev))) goto AttachError; if (parent) klist_add_tail(&dev->knode_parent, &parent->klist_children); if (dev->class) { down(&dev->class->sem); /* tie the class to the device */ list_add_tail(&dev->node, &dev->class->devices); /* notify any interfaces that the device is here */ list_for_each_entry(class_intf, &dev->class->interfaces, node) if (class_intf->add_dev) class_intf->add_dev(dev, class_intf); up(&dev->class->sem); } Done: kfree(class_name); put_device(dev); return error; AttachError: bus_remove_device(dev); BusError: device_pm_remove(dev); PMError: if (dev->bus) blocking_notifier_call_chain(&dev->bus->bus_notifier, BUS_NOTIFY_DEL_DEVICE, dev); device_remove_groups(dev); GroupError: device_remove_attrs(dev); AttrsError: if (dev->devt_attr) { device_remove_file(dev, dev->devt_attr); kfree(dev->devt_attr); } if (dev->class) { sysfs_remove_link(&dev->kobj, "subsystem"); /* If this is not a "fake" compatible device, remove the * symlink from the class to the device. */ if (dev->kobj.parent != &dev->class->subsys.kset.kobj) sysfs_remove_link(&dev->class->subsys.kset.kobj, dev->bus_id); if (parent) { #ifdef CONFIG_SYSFS_DEPRECATED char *class_name = make_class_name(dev->class->name, &dev->kobj); if (class_name) sysfs_remove_link(&dev->parent->kobj, class_name); kfree(class_name); #endif sysfs_remove_link(&dev->kobj, "device"); } down(&dev->class->sem); /* notify any interfaces that the device is now gone */ list_for_each_entry(class_intf, &dev->class->interfaces, node) if (class_intf->remove_dev) class_intf->remove_dev(dev, class_intf); /* remove the device from the class list */ list_del_init(&dev->node); up(&dev->class->sem); } ueventattrError: device_remove_file(dev, &dev->uevent_attr); attrError: kobject_uevent(&dev->kobj, KOBJ_REMOVE); kobject_del(&dev->kobj); Error: if (parent) put_device(parent); goto Done; } 3.4 bus_attach_device()函数 在文件drivers/base/bus.c中实现了bus_attach_device()函数。 /** * bus_attach_device - add device to bus * @dev: device tried to attach to a driver * * - Add device to bus's list of devices. * - Try to attach to driver. */ int bus_attach_device(struct device * dev) { struct bus_type *bus = dev->bus; int ret = 0; if (bus) { dev->is_registered = 1; ret = device_attach(dev); if (ret >= 0) { //将设备信息存储在bus结构中。在驱动注册后,利用bus_for_each_dev()函数可以得到所有设备的 //信息,完成驱动与设备之间的绑定。 klist_add_tail(&dev->knode_bus, &bus->klist_devices); ret = 0; } else dev->is_registered = 0; } return ret; } 3.5 dd.c文件 在文件drivers/base/dd.c中实现了device_attach()函数。 3.5.1 device_attach()函数 /** * device_attach - try to attach device to a driver. * @dev: device. * * Walk the list of drivers that the bus has and call * driver_probe_device() for each pair. If a compatible * pair is found, break out and return. * * Returns 1 if the device was bound to a driver; * 0 if no matching device was found; error code otherwise. * * When called for a USB interface, @dev->parent->sem must be held. */ int device_attach(struct device * dev) { int ret = 0; down(&dev->sem); if (dev->driver) { ret = device_bind_driver(dev); if (ret == 0) ret = 1; } else ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); up(&dev->sem); return ret; } static int __device_attach(struct device_driver * drv, void * data) { struct device * dev = data; //在此之后,利用驱动信息来探测设备,参考第一部分的介绍 return driver_probe_device(drv, dev); } 3.5.2 device_bind_driver()函数 /** * device_bind_driver - bind a driver to one device. * @dev: device. * * Allow manual attachment of a driver to a device. * Caller must have already set @dev->driver. * * Note that this does not modify the bus reference count * nor take the bus's rwsem. Please verify those are accounted * for before calling this. (It is ok to call with no other effort * from a driver's probe() method.) * * This function must be called with @dev->sem held. */ int device_bind_driver(struct device *dev) { int ret; ret = driver_sysfs_add(dev); if (!ret) driver_bound(dev); //通知链 return ret; } 到此,设备与驱动的两种绑定方式:在设备注册时进行绑定及在驱动注册时进行绑定就介绍完了。
platform_driver平台驱动注册和注销过程
最新推荐文章于 2021-10-25 11:24:51 发布