相关结构体定义:include\linux\platform_device.h 文件中
struct platform_device { // platform总线设备
const char * name; // 平台设备的名字
int id; // ID 是用来区分如果设备名字相同的时候(通过在后面添加一个数字来代表不同的设备,因为有时候有这种需求)
struct device dev; // 内置的device结构体
u32 num_resources; // 资源结构体数量
struct resource * resource; // 指向一个资源结构体数组
const struct platform_device_id *id_entry; // 用来进行与设备驱动匹配用的id_table表
/* arch specific additions */
struct pdev_archdata archdata; // 自留地 添加自己的东西
};
platform_device结构体中的struct resource结构体分析:
struct resource { // 资源结构体
resource_size_t start; // 资源的起始值,如果是地址,那么是物理地址,不是虚拟地址
resource_size_t end; // 资源的结束值,如果是地址,那么是物理地址,不是虚拟地址
const char *name; // 资源名
unsigned long flags; // 资源的标示,用来识别不同的资源
struct resource *parent, *sibling, *child; // 资源指针,可以构成链表
};
platform_driver结构体:(include\linux\platform_device.h)
struct platform_driver {
int (*probe)(struct platform_device *); // 这个probe函数其实和 device_driver中的是一样的功能,但是一般是使用device_driver中的那个
int (*remove)(struct platform_device *); // 卸载平台设备驱动的时候会调用这个函数,但是device_driver下面也有,具体调用的是谁这个就得分析了
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver; // 内置的device_driver 结构体
const struct platform_device_id *id_table; // 该设备驱动支持的设备的列表 他是通过这个指针去指向 platform_device_id 类型的数组
};
int (*match)(struct device *dev, struct device_driver *drv); // 该总线下设备与设备驱动的匹配函数 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 bus_type_private *p; // 总线的私有数据 p->subsys.kobj 表示该总线在驱动模型中对应的对象};struct bus_type_private { struct kset subsys; // 这个是bus主要的kset struct kset *drivers_kset; // 这个kset指针用来指向该总线的 drivers目录的 struct kset *devices_kset; // 这个kse指针用来指向该总线的devices目录的 struct klist klist_devices; // 用来挂接该总线下的设备的一个链表头 struct klist klist_drivers; // 用来挂接该总线下的设备驱动的一个链表头 struct blocking_notifier_head bus_notifier; unsigned int drivers_autoprobe:1; // 是否需要在设备驱动注册时候子自动匹配设备 struct bus_type *bus; // 指向本bus结构体};(3)函数详解bus_register: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; // 将平台设备的父设备设置为 platform_bus (对应的就是 /sys/devices/platform 这个目录)
pdev->dev.bus = &platform_bus_type; // 设置平台设备挂接在 platform总线下 platform_bus_type
if (pdev->id != -1) dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); // 给平台设备对应的对象设置名字 name.id (如果我们的 pdev->id 设置不等于-1时) else dev_set_name(&pdev->dev, "%s", pdev->name);
// 下面的for 循环是对平台设备资源的一些处理 for (i = 0; i < pdev->num_resources; i++) { struct resource *p, *r = &pdev->resource;
if (r->name == NULL) r->name = dev_name(&pdev->dev);
p = r->parent; if (!p) { if (resource_type(r) == IORESOURCE_MEM) p = &iomem_resource; else if (resource_type(r) == IORESOURCE_IO) p = &ioport_resource; }
if (p && insert_resource(p, r)) { printk(KERN_ERR "%s: failed to claim resource %d\n", dev_name(&pdev->dev), i); ret = -EBUSY; goto failed; } }//////////////////////////////////////////////////////////////////
pr_debug("Registering platform device '%s'. Parent at %s\n", dev_name(&pdev->dev), dev_name(pdev->dev.parent));
ret = device_add(&pdev->dev); // 将平台设备添加到系统中去 /sys/devices/platform/xxx if (ret == 0) return ret;
failed: while (--i >= 0) { struct resource *r = &pdev->resource; unsigned long type = resource_type(r);
if (type == IORESOURCE_MEM || type == IORESOURCE_IO) release_resource(r); }
return ret;}5、platform平台设备驱动注册(1)platform平台设备驱动注册函数: platform_driver_register/*********************************************************************/platform_driver_register driver_register driver_find bus_add_driver kobject_init_and_add driver_attach klist_add_tail module_add_driver driver_create_file driver_add_attrs driver_add_groups/************************************************************/(2)函数详解platform_driver_register:int platform_driver_register(struct platform_driver *drv){ drv->driver.bus = &platform_bus_type; // 设置设备驱动 挂接在 platform平台总线下
// 下面做的就是对 drv 中的函数指针进行填充 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); // 注册设备驱动}int driver_register(struct device_driver *drv){ int ret; struct device_driver *other; // 定义一个设备驱动指针 other
BUG_ON(!drv->bus->p);
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 -EBUSY; }
ret = bus_add_driver(drv); // 在总线挂接设备驱动 就是将设备驱动对应的kobj对象与组织建立关系 if (ret) return ret; ret = driver_add_groups(drv, drv->groups); // if (ret) bus_remove_driver(drv); return ret;}bus_add_driver:int bus_add_driver(struct device_driver *drv){ struct bus_type *bus; // 定义一个bus_type 结构体指针 struct driver_private *priv; // 定义一个 driver_private 指针 int error = 0;
bus = bus_get(drv->bus); // 获取 drv的bus if (!bus) return -EINVAL;
pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
priv = kzalloc(sizeof(*priv), GFP_KERNEL); // 给priv 申请分配内存空间 if (!priv) { error = -ENOMEM; goto out_put_bus; } klist_init(&priv->klist_devices, NULL, NULL); // 初始化 priv->klist_devices 链表 priv->driver = drv; // 使用 priv->driver 指向 drv drv->p = priv; // 使用drv->p 指向 priv 这两步见多了 ,跟之前分析的是一样的意思 就是建立关系 priv->kobj.kset = bus->p->drivers_kset; // 设置设备驱动对象的父对象( 也就是指向一个 kset ) 父对象就是 /sys/bus/bus_type/drivers/ 这个目录对应的对象 error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, // 添加kobject 对象到目录层次中 就能够在 /sys/bus/bus_type/drivers/ 目录中看到设备驱动对应的文件了 "%s", drv->name); // priv->kobj->ktype = driver_ktype 对象类型 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); // 链表挂接: priv->knode_bus 挂接到 bus->p->klist_drivers 链表头上去 module_add_driver(drv->owner, drv);
error = driver_create_file(drv, &driver_attr_uevent); // 建立属性文件: uevent if (error) { printk(KERN_ERR "%s: uevent attr (%s) failed\n", __func__, drv->name); } error = driver_add_attrs(bus, drv); // 根据总线的 bus->drv_attrs 来建立属性文件 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); }
if (!drv->suppress_bind_attrs) { 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 0;
out_unregister: kobject_put(&priv->kobj); kfree(drv->p); drv->p = NULL;out_put_bus: bus_put(bus); return error;}
driver_attach:
上面说到了当注册platform平台设备驱动时会进行自动匹配的原理,那么当我们注册platform平台设备时进行自动匹配的代码在哪里呢?其实这个之前在分析device_create函数时就已经分析过了,只不过没有去详细的分析:/**********************************************/platform_device_add device_add bus_probe_device // 关键就在这个函数/*********************************************/函数分析:
int driver_register(struct device_driver *drv){ int ret; struct device_driver *other; // 定义一个设备驱动指针 other
BUG_ON(!drv->bus->p);
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 -EBUSY; }
ret = bus_add_driver(drv); // 在总线挂接设备驱动 就是将设备驱动对应的kobj对象与组织建立关系 if (ret) return ret; ret = driver_add_groups(drv, drv->groups); // if (ret) bus_remove_driver(drv); return ret;}
总结: 所以由此可知,当我们不管是先注册设备还是先注册设备驱动都会进行一次设备与设备驱动的匹配过程,匹配成功之后就会调用probe函数,匹配的原理就是去遍历总线下的相应的链表来找到挂接在他下面的设备或者设备驱动,所以由此可以看出来,这个东西的设计其实是很美的。
6、platform总线下的匹配函数(1)platform_match函数static int platform_match(struct device *dev, struct device_driver *drv) // 总线下的设备与设备驱动的匹配函数{ struct platform_device *pdev = to_platform_device(dev); // 通过device 变量获取到 platform_device struct platform_driver *pdrv = to_platform_driver(drv); // 通过 driver 获取 platform_driver
/* match against the id table first */ if (pdrv->id_table) // 如果pdrv中的id_table 表存在 return platform_match_id(pdrv->id_table, pdev) != NULL; // 匹配id_table /* fall-back to driver name match */ // 第二个就是指直接匹配 pdev->name drv->name 名字是否形同 return (strcmp(pdev->name, drv->name) == 0);}
static const struct platform_device_id *platform_match_id( const struct platform_device_id *id, struct platform_device *pdev){ while (id->name[0]) { // 循环去比较id_table数组中的各个id名字是否与pdev->name 相同 if (strcmp(pdev->name, id->name) == 0) { pdev->id_entry = id; // 将id_table数组中的名字匹配上的 这个数组项 指针赋值给 pdev->id_entry return id; // 返回这个指针 } id++; } return NULL;}总结: 由上面可知platform总线下设备与设备驱动的匹配原理就是通过名字进行匹配的,先去匹配platform_driver中的id_table表中的各个名字与platform_device->name名字是否相同,如果相同表示匹配成功直接返回,否则直接匹配platform_driver->name与platform_driver->name是否相同,相同则匹配成功,否则失败
参考来源:https://www.cnblogs.com/deng-tao/p/6026373.html