总线驱动模型

来源:互联网 发布:mindly mac 编辑:程序博客网 时间:2024/05/29 11:47

设备驱动有两个成员device和driver,这两个成员在驱动模型中扮演了重要的角色,且这两个成员之间是匹配的关系,既然是匹配关系,自然会有有东西对它们进行管理并为它们提供匹配机制,这就产生了一个总线的概念,内核中用一个结构体来对一个总线进行描述

struct bus_type {    const char      *name;//总线的名字    const char      *dev_name;//设备名    struct device       *dev_root;    struct bus_attribute    *bus_attrs;//总线属性文件    struct device_attribute *dev_attrs;    struct driver_attribute *drv_attrs;    int (*match)(struct device *dev, struct device_driver *drv);//为device和driver提供匹配机制    int (*uevent)(struct device *dev, struct kobj_uevent_env *env);    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 dev_pm_ops *pm;    struct iommu_ops *iommu_ops;    struct subsys_private *p;//用来管理总线下的device和driver};

由上可见,struct bus_type的成员函数传进去的都是struct device、struct device_driver这样的结构,也就是说总线只能对这两个成员进行管理,这是每个总线共有的资源,但是每个总线的特性都不一样,这就需要不同的结构来描述,因此在具体的总线中,会对struct device和struct device_driver进行再次封装成符合自己特性的device和driver结构,对应的有struct platform_device、i2c_device、spi_device和platform_driver、i2c_driver、spi_driver等等,它们通常的做法是把struct device_driver或者struct device当作一个成员变量放进自己私有的device或者driver结构中,然后在上面那些成员函数中传进来的都是struct device、struct device_driver这样的结构,那么在具体的总线中怎么得到自己私有的device和driver结构,内核提供了一个container_of,这个宏可通过一个结构体中的任何一个成员变量就能找到该结构体的地址,因为具体的总线封装自己私有的device或者driver结构都会包含一个struct device或者struct device_driver,因此通过传进来的struct device或者struct device_driver结构就能找到自己私有的device和driver结构。这里还需关注一下struct bus_type结构中的match成员,上面提到device和driver是匹配的关系,在总线中match成员就是为了给注册到总线的device和driver成员提供匹配机制,总线又分为两种:虚拟的和真实存在的,总线一般的匹配机制是比较两个成员的name,另外总线一般还提供了一个用来匹配的id_table,看下虚拟的platform总线的match函数

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 to match against the id table */    if (pdrv->id_table)//再到平台设备的id_table表的匹配        return platform_match_id(pdrv->id_table, pdev) != NULL;    /* fall-back to driver name match */    return (strcmp(pdev->name, drv->name) == 0);//最后在匹配设备跟驱动的名字}

在总线的私有device和driver结构下一般有一个id_table和name成员变量,这是供总线的匹配机制提供的。总线除了给device和driver提供匹配机制外,还会提供一些注册接口给驱动人员进行调用注册;如 platform总线的platform_driver_register、i2c_register_driver等等

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(&drv->driver);(这是总线注册一个driver的统一接口,即是每个总线注册一个driver最终都会调用这个函数来注册一个driver进总线里面),这里有必要了解下device_driver的注册流程,代码比较多,这里通过主要的函数调用给予说明:(同一缩进表示在同一函数被调用)

driver_register    bus_add_driver        driver_attach//匹配driver            bus_for_each_dev//遍历总线下的所有device,在总线结构描述结构里面有个struct subsys_private *p成员,这个成员里面有两个链表,分别为klist_devices,klist_drivers,所有注册到总线的device和driver都放在这两个链表进行管理                __driver_attach//和一个device进行匹配                    driver_match_device//判断总线是否存在匹配match函数,有即调用,匹配成功继续往下执行,失败返回                        return drv->bus->match ? drv->bus->match(dev, drv) : 1;                    driver_probe_device//匹配成功调用driver的probe函数

在总线注册一个driver会去遍历所在总线的klist_devices下的所有device进行匹配,同样总线也会驱动人员提供注册device的接口,在注册一个device也会去遍历总线的klist_drivers下的所有driver进行匹配,即采用双匹配机制,并不用担心device和driver哪个先注册。