15285 views|0 replies

139

Posts

0

Resources
The OP
 

SinlinxA33 development board Linux platform bus device driver [Copy link]

1. What is the platform bus?
Compared to physical buses such as USB, PCI, I2C, and SPI, the platform bus is a virtual, abstracted bus. In reality, there is no such bus. Why do we need the platform bus? In fact, it is a virtual bus created by the Linux device driver model to maintain the unity of device drivers. Because for USB devices, I2C devices, PCI devices, SPI devices, etc., they communicate with the CPU directly under the corresponding bus to interact with our CPU, but in our embedded system, Not all devices can be attributed to these common buses. In embedded systems, independent peripheral controllers integrated in the SoC system and peripherals mounted in the SoC memory space do not depend on such buses. Therefore, in order to maintain integrity, the Linux driver model hangs these devices on a virtual bus (platform bus) to prevent some devices from being hung on the bus while other devices are not.
platform bus related code: driver\base\platform.c file
Related structure definition: in include\linux\platform_device.h file
2. Two major players under platform bus management
(1) Two structures platform_device and platform_driver
94)]For any bus under the Linux device driver model, it consists of two parts: a structure describing the device and a structure describing the driver.
In the platform bus, they are platform_device and platform_driver. The following is an analysis of the elements of the two structures:
Arial]platform_device structure: (include\linux\platform_device.h)
struct platform_device { // platform bus device const char * name; // platform device name int id; // ID is used to distinguish if the device name is the same (by adding a number at the end to represent different devices, because sometimes there is such a requirement) struct device dev; // built-in device structure u32 num_resources; //Number of resource structures struct resource * resource; //Pointing to an array of resource structures const struct platform_device_id *id_entry; //ID_table table used to match device drivers /* arch specific additions */ struct pdev_archdata archdata; //Add your own stuff to your own land }; Analysis of struct resource structure in platform_device structure: struct resource { // Resource structure resource_size_t start; // The starting value of the resource, if it is an address, then it is a physical address, not a virtual address resource_size_t end; // struct resource *parent, *sibling, *child; // Resource pointer, can form a linked list }; platform_driver structure: (include\linux\platform_device.h) struct platform_driver { int (*probe)(struct platform_device *); // This probe function actually has the same function as the one in device_driver, but generally uses the one in device_driver int (*remove)(struct platform_device *); // This function is called when the platform device driver is uninstalled, but there is also one under device_driver. Who calls it specifically needs to be analyzed void (*shutdown)(struct platform_device *); int (*suspend)(struct platform_device *,pm_message_t state); int (*resume)(struct platform_device *); struct device_driver driver; // Built-in device_driver structure const struct platform_device_id *id_table; // List of devices supported by the device driver He uses this pointer to point to an array of platform_device_id type};
(2) Two groups of interface functions (driver\base\platform.c)
94)]int platform_driver_register(struct platform_driver *); // Used to register our device driver
void platform_driver_unregister(struct platform_driver *); // Used to uninstall our device driver
int platform_device_register(struct platform_device *); // Used to register our device
void platform_device_unregister(struct platform_device *); // Used to uninstall our device
[font=Verdana, Helvetica, 3. Initialization of platform bus
(1)Registration initialization of platform bus: platform_bus_init
/*****************************************************************/
[size=1 3px]platform_bus_init
early_platform_cleanup // Do some early platform cleanup
device_register // Register device (create device object corresponding to platform directory in /sys/devices/directory /sys/devices/platform/)
/****************************************************************************/
(2)Related structures
struct bus_type {
const char *name; // Bus name
struct bus_attribute *bus_attrs; // Attributes of the bus
struct device_attribute *dev_attrs; // Attributes of the device under this bus
struct driver_attribute *drv_attrs; // Attributes of the device driver under this bus
int (*match)(struct device *dev, struct device_driver *drv); // Matching function of the device and device driver under this bus
kobj_uevent_env *env); //Event function hot plugging
int (*probe)(struct device *dev); // Probe function under the bus
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; // Power management related
struct bus_type_private *p; // Bus private data p->subsys.kobj represents the object corresponding to the bus in the driver model
};
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

This post is from Embedded System

Guess Your Favourite
Just looking around
Find a datasheet?

EEWorld Datasheet Technical Support

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号
快速回复 返回顶部 Return list