linux设备驱动模型——总线、设备、设备驱动
2015-04-19 18:23
447 查看
linux设备驱动模型如下所示(摘自《linux设备驱动开发详解》):
在Linux内核中,分别使用bus_type、device和device_driver来描述总线、设备和设备驱动之间的关系。首先根据SOC内部的总线关系分别分类出USB总线、PCI总线、I2C总线、SPI总线等常见总线,另外考虑到SOC系统中一些独立的外设控制器以及挂接在SOC内存空间的外设不依附前面这些总线,linux发明了一种虚拟总线,即platform总线,相应的设备称为platform_device,而驱动称为platform_driver。linux设备模型中,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻找与之区域的设备,而匹配由总线完成。接下来,以platform总线、platform_device和platform_driver三者的关系为例进行说明linux设备模型是如何通过总线管理设备与设备驱动的。
先来看下platform_device的注册,以s3c2440平台为例,在mach-smdk2440.c对平台设备进行了注册,如下:
分析好了platform_device的注册过程,接下来看下platform_driver的注册过程,以s3c2440平台的s3c_device_wdt平台设备的平台驱动为例,平台驱动通过platform_driver_register注册平台驱动,先看下s3c_device_wdt的平台驱动定义,如下:
在Linux内核中,分别使用bus_type、device和device_driver来描述总线、设备和设备驱动之间的关系。首先根据SOC内部的总线关系分别分类出USB总线、PCI总线、I2C总线、SPI总线等常见总线,另外考虑到SOC系统中一些独立的外设控制器以及挂接在SOC内存空间的外设不依附前面这些总线,linux发明了一种虚拟总线,即platform总线,相应的设备称为platform_device,而驱动称为platform_driver。linux设备模型中,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻找与之区域的设备,而匹配由总线完成。接下来,以platform总线、platform_device和platform_driver三者的关系为例进行说明linux设备模型是如何通过总线管理设备与设备驱动的。
先来看下platform_device的注册,以s3c2440平台为例,在mach-smdk2440.c对平台设备进行了注册,如下:
首先根据SOC中有哪些设备定义平台设备数组: static struct platform_device *smdk2440_devices[] __initdata = { &s3c_device_usb, &s3c_device_lcd, &s3c_device_wdt, &s3c_device_i2c0, &s3c_device_iis, }; 平台设备的结构体定义如下: struct platform_device { const char * name; int id; struct device dev; u32 num_resources; struct resource * resource; }; 接着通过platform_add_devices()注册到platform总线上,分别通过platform_device_register()单个注册每一个平台设备,其源码如下: int platform_device_register(struct platform_device *pdev) { device_initialize(&pdev->dev); return platform_device_add(pdev); } 详细看下platform_device_add的源码: 首先将dev.parent = &platform_bus; pdev->dev.bus = &platform_bus_type; 然后根据设备的num_resources往设备的parent上insert_resource(),最后调用device_add()将设备注册到device hierarchy。device_add的源码如下: int device_add(struct device *dev) { struct device *parent = NULL; struct class_interface *class_intf; int error = -EINVAL; dev = get_device(dev); if (!dev) goto done; /* Temporarily support init_name if it is set. * It will override bus_id for now */ if (dev->init_name) dev_set_name(dev, "%s", dev->init_name); if (!strlen(dev->bus_id)) goto done; pr_debug("device: '%s': %s\n", dev_name(dev), __func__); parent = get_device(dev->parent); setup_parent(dev, parent); /* use parent numa_node */ if (parent) set_dev_node(dev, dev_to_node(parent)); /* first, register with generic layer. */ error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev_name(dev)); if (error) goto Error; /* notify platform of device entry */ if (platform_notify) platform_notify(dev); error = device_create_file(dev, &uevent_attr); if (error) goto attrError; if (MAJOR(dev->devt)) { error = device_create_file(dev, &devt_attr); if (error) goto ueventattrError; error = device_create_sys_dev_entry(dev); if (error) goto devtattrError; } 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_sysf_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_attach_device(dev); if (parent) klist_add_tail(&dev->knode_parent, &parent->klist_children); if (dev->class) { mutex_lock(&dev->class->p->class_mutex); /* tie the class to the device */ klist_add_tail(&dev->knode_class, &dev->class->p->class_devices); /* notify any interfaces that the device is here */ list_for_each_entry(class_intf, &dev->class->p->class_interfaces, node) if (class_intf->add_dev) class_intf->add_dev(dev, class_intf); mutex_unlock(&dev->class->p->class_mutex); } done: ...... } 该函数先判断dev是否为空,再调用setup_parent()为dev寻找父节点,再调用kobject_add()往父节点添加kobject节点, 然后分别调用device_create_file为设备节点创建sysfs的设备节点,再创建一些软连接符号,接着执行bus_attach_device(dev)为设备 寻找对应的设备驱动,详细看下该函数的内容: void bus_attach_device(struct device *dev) { struct bus_type *bus = dev->bus; int ret = 0; if (bus) { if (bus->p->drivers_autoprobe) ret = device_attach(dev); WARN_ON(ret < 0); if (ret >= 0) klist_add_tail(&dev->knode_bus, &bus->p->klist_devices); } } 首先会判断drivers_autoprobe是否为真,然后调用device_attach(),成功后再调用 klist_add_tail(&dev->knode_bus, &bus->p->klist_devices);将设备 添加到klist_devices链表中,下面详细看下device_attach函数的内容: 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 { dev->driver = NULL; ret = 0; } } else { ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); } up(&dev->sem); return ret; } 如果dev已经有驱动关联了,就调用device_bind_driver,否则就调用bus_for_each_drv()从设备 所依附的总线上遍历已注册的驱动。该函数中会回调__device_attach()来为设备寻找匹配的驱动,其源码如下: static int __device_attach(struct device_driver *drv, void *data) { struct device *dev = data; return driver_probe_device(drv, dev); } int driver_probe_device(struct device_driver *drv, struct device *dev) { int ret = 0; if (!device_is_registered(dev)) return -ENODEV; if (drv->bus->match && !drv->bus->match(dev, drv)) goto done; pr_debug("bus: '%s': %s: matched device %s with driver %s\n", drv->bus->name, __func__, dev_name(dev), drv->name); ret = really_probe(dev, drv); done: return ret; } 可以看到,最后匹配dev与drv的函数是在drv所依附的bus->match()中匹配的,由于这里注册的是platform总线,看下 platform总线的定义,如下: struct bus_type platform_bus_type = { .name = "platform", .dev_attrs = platform_dev_attrs, .match = platform_match, .uevent = platform_uevent, .pm = PLATFORM_PM_OPS_PTR, }; 其match方法如下: static int platform_match(struct device *dev, struct device_driver *drv) { struct platform_device *pdev; pdev = container_of(dev, struct platform_device, dev); return (strcmp(pdev->name, drv->name) == 0); } match()方法只是判断dev->name与drv->name是否相同。现在回过来看driver_probe_device()匹配成功的内容, 若dev与drv匹配成功,则会调用really_probe(dev, drv),其源码如下: static int really_probe(struct device *dev, struct device_driver *drv) { int ret = 0; atomic_inc(&probe_count); dev->driver = drv; 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) { 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("bus: '%s': %s: bound device %s to driver %s\n", drv->bus->name, __func__, dev_name(dev), drv->name); goto done; probe_failed: ...... return ret; } 首先将dev->driver指向drv,然后判断dev->bus->probe与drv->probe方法是否存在,在platform总线上没有probe方法, 因此会执行drv->probe()方法,即platform_driver的probe方法。
分析好了platform_device的注册过程,接下来看下platform_driver的注册过程,以s3c2440平台的s3c_device_wdt平台设备的平台驱动为例,平台驱动通过platform_driver_register注册平台驱动,先看下s3c_device_wdt的平台驱动定义,如下:
static struct platform_driver s3c2410wdt_driver = { .probe = s3c2410wdt_probe, .remove = s3c2410wdt_remove, .shutdown = s3c2410wdt_shutdown, .suspend = s3c2410wdt_suspend, .resume = s3c2410wdt_resume, .driver = { .owner = THIS_MODULE, .name = "s3c2410-wdt", }, }; 可以看到.driver.name字段与s3c_device_wdt的平台设备的.name字段相同,当platform总线的match方法匹配出平台设备与平台驱动后就调用平台驱动 的probe方法了,本例就是s3c2410wdt_driver的.probe方法。下面来详细看下平台驱动的注册,即platform_driver_register函数的内容: int platform_driver_register(struct platform_driver *drv) { drv->driver.bus = &platform_bus_type; 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); } 首先将drv->driver.bus设为platform_bus_type,然后判断drv的probe、remove、shutdown、suspend和resume字段是否为空,若为空,则使用平台总线对应的 默认平台驱动接口,然后调用driver_register注册平台驱动,其源码如下: int driver_register(struct device_driver *drv) { int ret; struct device_driver *other; 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 -EEXIST; } ret = bus_add_driver(drv); if (ret) return ret; ret = driver_add_groups(drv, drv->groups); if (ret) bus_remove_driver(drv); return ret; } 首先根据drv->bus和drv->name从drv依附的bus中寻找是否已经注册了drv->name的平台驱动,若没注册,则调用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); 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); } 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 error; out_unregister: ...... } 该函数首先根据drv->bus获得drv依附的bus,再创建一个priv结构体来存放drv在内核中需要表示的相关数据结构,接着判断 drv->bus->p->drivers_autoprobe是否为真,若为真,则调用driver_attach将驱动与某个平台设备进行关联。然后通过klist_add_tail将 priv所指向的drv添加到klist_drivers链表中,最后会调用driver_create_file等为drv创建sysfs对应的设备驱动节点。 下面来详细看下driver_attach中是如何将驱动与某个平台设备进行关联的,其源码如下: int driver_attach(struct device_driver *drv) { return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); } 在bus_for_each_dev中会回调__driver_attach()函数,其源码如下: static int __driver_attach(struct device *dev, void *data) { struct device_driver *drv = data; if (drv->bus->match && !drv->bus->match(dev, drv)) return 0; 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; } 该函数首先判断drv所依附的bus->match()函数是否存在,若存在,则调用其match()函数来匹配设备与设备驱动。由于platform总线的 match()方法只是判断platform_device与platform_driver的名字是否相同,若相同,则判断dev->driver是否已关联。若没有关联, 则调用driver_probe_device(drv, dev),该函数在上面platform_device注册过程中已分析过,其最终调用really_probe(dev, drv), 继而执行drv的probe方法。分析完platform_device与platform_driver之间是如何通过platform bus进行关联的过程后,下面再来分析platform bus的注册流程,即platform_bus_init()的源码(在platform.c文件中):
int __init platform_bus_init(void) { int error; error = device_register(&platform_bus); if (error) return error; error = bus_register(&platform_bus_type); if (error) device_unregister(&platform_bus); return error; } struct device platform_bus = { .init_name = "platform", }; struct bus_type platform_bus_type = { .name = "platform", .dev_attrs = platform_dev_attrs, .match = platform_match, .uevent = platform_uevent, .pm = PLATFORM_PM_OPS_PTR, }; 在platform_bus的初始化函数中,首先会通过device_register()注册platform_bus,再通过bus_register()注册platform_bus_type。分别来看下两个注册 函数的内容。device_register()的源码如下: int device_register(struct device *dev) { device_initialize(dev); return device_add(dev); } 该函数的内容与平台设备的注册过程没什么区别,只不过此时注册的是platform_bus,没有指明parent节点,因此将platform_bus作为一个节点。接着再来看下 bus_register()的内容,如下: int bus_register(struct bus_type *bus) { int retval; struct bus_type_private *priv; priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL); if (!priv) return -ENOMEM; priv->bus = bus; bus->p = priv; BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier); retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name); if (retval) goto out; priv->subsys.kobj.kset = bus_kset; priv->subsys.kobj.ktype = &bus_ktype; priv->drivers_autoprobe = 1; retval = kset_register(&priv->subsys); if (retval) goto out; retval = bus_create_file(bus, &bus_attr_uevent); if (retval) goto bus_uevent_fail; priv->devices_kset = kset_create_and_add("devices", NULL, &priv->subsys.kobj); if (!priv->devices_kset) { retval = -ENOMEM; goto bus_devices_fail; } priv->drivers_kset = kset_create_and_add("drivers", NULL, &priv->subsys.kobj); if (!priv->drivers_kset) { retval = -ENOMEM; goto bus_drivers_fail; } klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put); klist_init(&priv->klist_drivers, NULL, NULL); retval = add_probe_files(bus); if (retval) goto bus_probe_files_fail; retval = bus_add_attrs(bus); if (retval) goto bus_attrs_fail; pr_debug("bus: '%s': registered\n", bus->name); return 0; bus_attrs_fail: ...... } 该函数中会申请priv结构体来存储bus在内核中需要表示的数据结构,可以看到: priv->subsys.kobj.kset = bus_kset; priv->subsys.kobj.ktype = &bus_ktype; priv->drivers_autoprobe = 1; 这里将drivers_autoprobe设为1,且kset为bus_kset,ktype为bus_ktype。然后调用kset_register()注册platform_bus的kset,接着调用 bus_create_file()为bus创建sysfs的总线节点,然后调用priv->devices_kset = kset_create_and_add("devices", NULL,&priv->subsys.kobj); 和priv->drivers_kset = kset_create_and_add("drivers", NULL,&priv->subsys.kobj)为总线分别创建device和driver的kset容器。然后分别 初始化priv->klist_devices和priv->klist_drivers链表,后面设备和设备驱动的注册会分别添加到klist_devices和klist_drivers两个链表中。在驱动注册过程中 的bus_for_each_dev()函数中会遍历klist_devices来寻找该驱动对应的device;在设备注册过程中的bus_for_each_drv()函数会遍历klist_drivers来寻找该设备 对应的driver。从上述的platform_bus的注册过程可以看到,其实platform_bus的注册分为platform_bus设备注册和platform_bus_type注册。平台总线本身也是一种设备。相当于一个顶层设备,然后其他子设备挂接到该设备下。由于linux设备驱动模型与sysfs的关系,后序需要详细分析kobject和kset在整个linux设备驱动模型中的管理流程。
相关文章推荐
- linux设备驱动模型之总线、设备、驱动三者的关系
- Linux设备驱动程式学习(13)-Linux设备模型(总线、设备、驱动程式和类)
- Linux设备驱动之总线,设备,驱动模型
- 总线设备驱动模型
- 木其工作室(专业程序代写服务)[原]ok6410学习笔记(15.platform平台总线驱动模型之混杂设备驱动led)
- 平台总线设备驱动模型——代码分析
- 总线设备模型-LED驱动
- [快速上手Linux设备驱动]之我看Linux设备模型(总线篇)
- Linux SPI总线设备驱动模型详解
- 总线设备驱动模型
- platform总线、设备、驱动模型之led驱动实例
- linux设备模型——总线,驱动,设备间的关系
- 分层分离之platform总线设备驱动模型
- 总线设备驱动模型——platform篇
- 设备驱动基础1:设备模型之总线,驱动,设备
- 16.总线设备驱动模型学习
- Linux总线设备驱动模型相关理论和实例(TQ2440)
- 平台总线设备驱动模型——基础知识
- linux总线设备驱动模型之Kobject&Kset
- 嵌入式linux平台设备驱动(设备驱动模型)开发之linux内核中bus总线