struct bus_type_private {
struct kset subsys; // This is the main kset of the bus
struct kset *drivers_kset; // This kset pointer is used to point to the drivers directory of the bus
struct kset *devices_kset; // This kse pointer is used to point to the devices directory of the bus.
struct klist klist_devices; // A linked list header for the devices under the bus
struct klist klist_drivers; // A linked list header for the device drivers under the bus
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1; // Whether to automatically match the device when registering the device driver
struct bus_type *bus; // Point to this bus structure
};
(3) Function detailed explanation
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; // Set the parent device of the platform device to platform_bus (corresponding to the /sys/devices/platform directory)
Arial]
[ size=13px] pdev->dev.bus = &platform_bus_type; // Set the platform device to be connected to the platform bus platform_bus_type
[size=13px ]
if (pdev->id != -1)
dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); // Set the name name.id for the object corresponding to the platform device (if our pdev->id setting is not equal to -1)
else
dev_set_name(&pdev->dev, "%s", pdev ->name);
// The following for loop is some processing of platform device resources
[font =Verdana, Helvetica, Arial] for (i = 0; i < pdev->num_resources; i++) {
struct resource *p, *r = &pdev->resource
;[ /color]
[/color ]
if (r->name == NULL)
[align=left ]
r->name = dev_name(&pdev->dev);[i ]
p = r->parent;
[font =Verdana, Helvetica, Arial] 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)
[font=Verdana, Helvetica,drv->driver.bus = &platform_bus_type; // Set the device driver to be mounted on the platform platform bus
// The following is to fill the function pointer in 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); // Register device driver
}
int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other; // Define a device driver pointer 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 "
[ font=Verdana, Helvetica, Arial] "bus_type methods\n", drv->name);
[align =left]
other = driver_find(drv->name, drv->bus); // This function actually performs a check to see if there is a device driver with the same name as the device driver that needs to be registered under the current bus. ] if (other) {
put_driver(other); // If the names are the same, print an error and exit directly
[align =left] printk(KERN_ERR "Error: Driver '%s' is already registered, "[/font ] "aborting...\n", drv->name);
[color=#0400 ] return -EBUSY;
[size=13px ] }
[/color ]
ret = bus_add_driver(drv); // To attach a device driver to the bus is to establish a relationship between the kobj object corresponding to the device driver and the organization. =#0400] if (ret)
[ size=13px] return ret;
ret = driver_add_groups(drv, drv->groups); //
[size =13px] if (ret)
bus_remove_driver(drv);[ /size]
return ret;[/color ]
}[/align ]
bus_add_driver:[/font ]
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 object type
if (error)
goto out_unregister;
if (drv->bus->p->drivers_autoprobe) { // If the automatic matching device flag is defined, automatic matching is performed below the line
error = driver_attach(drv); // Try to bind the driver to the device, that is, match the device and the device driver through this function
if (error)
goto out_unregister;
}
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); // Link list attachment: priv->knode_bus is attached to the bus->p->klist_drivers link list head
module_add_driver(drv->owner, drv);
error = driver_create_file(drv, &driver_attr_uevent); // Create an attribute file: uevent
if (error) {
printk(KERN_ERR "%s: uevent attr (%s) failed\n",
__func__, drv->name);
}
error = driver_add_attrs(bus, drv); // According to bus->drv_attrs To create a properties file
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);
[ align=left] 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);
[size= 12px] other = driver_find(drv->name, drv->bus); // This function actually performs a check to see if there is a device driver with the same name as the device driver that needs to be registered under the current bus[ /size]
if (other) {
[ color=#b00000] put_driver(other); // If the names are the same, directly print an error and exit
printk(KERN_ERR "Error: Driver '%s' is already registered , "
"aborting...\n", drv->name);
return -EBUSY;
}
[color=# b00000] ret = bus_add_driver(drv); //Attaching a device driver to the bus is to establish a relationship between the kobj object corresponding to the device driver and the organization
[size =12px] if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups); // ret = driver_add_groups(drv, drv->groups); #b00000]
if (ret) bus_remove_driver(drv); return ret; =left]}
Summary: Therefore, no matter whether we register the device first or the device driver first, we will perform a matching process between the device and the device driver. After the match is successful, the probe function will be called. ,
The principle of matching is to traverse the corresponding linked list under the bus to find the device or device driver attached to it, so it can be seen from this that the design of this thing is actually very beautiful. [ /size]
6. Platform bus The matching function under
(1)platform_match function
static int platform_match(struct device *dev, struct device_driver *drv) //The matching function between the device and the device driver under the bus
{
struct platform_device *pdev = to_platform_device(dev); //Get platform_device through device variable
struct platform_driver *pdrv = to_platform_driver(drv); //Through driver Get platform_driver
/* match against the id table first */
if (pdrv->id_table) // If the id_table table in pdrv exists
return platform_match_id(pdrv->id_table, pdev) != NULL; // Match id_table
/* fall-back to driver name match */ // The second one refers to directly matching pdev->name drv->name whether the name is the same as
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]) { // Loop to compare whether each id name in the id_table array matches pdev->name Same
if (strcmp(pdev->name, id->name) == 0) {
pdev->id_entry = id; // Assign the array item pointer that matches the name in the id_table array to pdev->id_entry
return id; // Return this pointer
}
id++;
}
return NULL;
}
Summary: From the above, we can see that the matching principle of devices and device drivers under the platform bus is to match by name. First, match the names in the id_table table in platform_driver with platform_device->name
to see if they are the same. If they are the same, it means the match is successful and returns directly. Otherwise, directly match platform_driver->name with platform_driver->name to see if they are the same. If they are the same, the match is successful, otherwise it fails