linux中platform总线解析(三)(platform驱动的注册)
2017-11-17 17:21
561 查看
直接看代码。
其中注册过程中用到的一些重要的结构体:
platform_driver结构:
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; };
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; };
platform驱动的注册函数时pkatform_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; //注册驱动 return driver_register(&drv->driver); }
可以看到这里的具体注册工作是由函数driver_register来进行的。
int driver_register(struct device_driver *drv) { int ret; struct device_driver *other; BUG_ON(!drv->bus->p); //如果驱动和总线都不存在probe,remove,shutdown函数就报警告信息 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; } //把驱动加入到bus中 ret = bus_add_driver(drv); if (ret) return ret; //sysfs相关 ret = driver_add_groups(drv, drv->groups); if (ret) { bus_remove_driver(drv); return ret; } //sysfs相关 kobject_uevent(&drv->p->kobj, KOBJ_ADD); return ret; }
这里首先进行了一些条件判定,之后就调用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; //分配驱动私有数据类型(设备,驱动等链表信息) 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; //把驱动加入到bus上的klist_drivers链表中 //这就真正意义上的把驱动加入到系统中了 klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); //如果总线设置了自动匹配标志,就为驱动匹配绑定涉笔 if (drv->bus->p->drivers_autoprobe) { error = driver_attach(drv); if (error) goto out_unregister; } //这个函数到这里就完成了注册工作 //下面大多就是跟sysfs相关的操作,跟本篇相关性不大,先不分析 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 = driver_add_groups(drv, bus->drv_groups); if (error) if (!drv->suppress_bind_attrs) { error = add_bind_files(drv); } } return 0; out_unregister: kobject_put(&priv->kobj); kfree(drv->p); drv->p = NULL; out_put_bus: bus_put(bus); return error; }
关于sysfs操作先放到一边不取关注。
这里把驱动加入到bus的链表中后,就是真正意义上的把驱动加入到了系统中。
接下来就为把驱动绑定到设备上。
//为驱动绑定设备 int driver_attach(struct device_driver *drv) { //遍历bus上的设备链表,为驱动查找设备 return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); }
//遍历总线上设备链表为驱动查找适应的设备 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; //把总线的设备链表放入到i中 klist_iter_init_node(&bus->p->klist_devices, &i, (start ? &start->p->knode_bus : NULL)); //遍历链表,为每个设备调用fn函数进行匹配 //这里的fn就是函数__driver_attach while ((dev = next_device(&i)) && !error) error = fn(dev, data); klist_iter_exit(&i); return error; }
看一下这里的具体执行的__driver_attach函数
static int __driver_attach(struct device *dev, void *data) { struct device_driver *drv = data; //把设备和驱动进行匹配 if (!driver_match_device(drv, dev)) return 0; if (dev->parent) /* Needed for USB */ device_lock(dev->parent); device_lock(dev); //如果设备没有绑定驱动,就调用driver_probe_device函数 if (!dev->driver) driver_probe_device(drv, dev); device_unlock(dev); if (dev->parent) device_unlock(dev->parent); return 0; }
这里首先调用platfrom_match来对设备和驱动进行匹配,如果匹配完成后,就调用函数driver_probe_device进行绑定。
//调用总线上的match函数进行匹配操作,这里是platform_match函数 static inline int driver_match_device(struct device_driver *drv, struct device *dev) { return drv->bus->match ? drv->bus->match(dev, drv) : 1; }
//这里的匹配函数就和device注册时一样啦。总共有四种情况 static int platform_match(struct device *dev, struct device_driver *drv) { struct platform_device *pdev = to_platform_device(dev); struct platform_driver *pdrv = to_platform_driver(drv); /* Attempt an OF style match first */ if (of_driver_match_device(dev, drv)) return 1; /* Then try ACPI style match */ if (acpi_driver_match_device(dev, drv)) return 1; //device和driver中的id_table一致就匹配 if (pdrv->id_table) return platform_match_id(pdrv->id_table, pdev) != NULL; //名字一致,就是匹配 return (strcmp(pdev->name, drv->name) == 0); }匹配一致后,调用函数driver_probe_device进行绑定。这里就和device一致了。具体的分析看platform_device的注册。
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); if (dev->parent) pm_runtime_get_sync(dev->parent); //调用really_probe进行真正的绑定 pm_runtime_barrier(dev); ret = really_probe(dev, drv); pm_request_idle(dev); if (dev->parent) pm_runtime_put(dev->parent); return ret; }
到这里驱动的注册就完成了
相关文章推荐
- linux中platform总线解析(四)(platform设备注册后自动匹配驱动)
- linux中platform总线解析(二)(platform设备的注册)
- Linux驱动下的platform总线架构(转)
- 迅为4412开发板Linux驱动教程——总线_设备_驱动注册流程详解
- linux 下platform设备和驱动注册的先后顺序
- linux内核模型---总线,设备,驱动在展讯平台上I2C设备的实例解析
- 基于S3C2440的嵌入式Linux驱动——DS18B20温度传感器(添加使用platform总线机制)
- Linux下的platform总线驱动(一)
- Linux驱动编程——platform总线的设备和驱动
- Linux下的platform总线驱动(二)
- 迅为Linux驱动教程——总线_设备_驱动注册流程详解
- linux IDE驱动分析之IDE总线、驱动注册(二)
- 1、Linux驱动中的platform总线
- Linux下的platform总线驱动(二)
- 迅为4412开发板Linux驱动教程——总线_设备_驱动注册流程详解
- linux驱动模型开发——linux platform总线机制讲解与实例开发
- linux驱动模型开发——linux platform总线机制讲解与实例开发
- Linux虚拟总线platform驱动框架之地址对齐省内存
- Linux驱动下的platform总线架构
- linux驱动模型开发——linux platform总线机制讲解与实例开发