linux中platform总线解析(三)(platform驱动的注册)

来源:互联网 发布:mac 终端 路径查找 编辑:程序博客网 时间:2024/06/11 17:40


直接看代码。

其中注册过程中用到的一些重要的结构体:

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_attachwhile ((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;}

到这里驱动的注册就完成了


原创粉丝点击