platform设备驱动

platform总线、设备与驱动

在linux2.6以后的设备驱动模型中,总线将设备与驱动绑定,它们的匹配由总线完成。
一个现实的linux设备和驱动通常都需要挂接在一种总线上,对本身依附于PCI,USB,I2C,SPI等的设备而言,这自然不是问题。但在嵌入式系统里,SOC系统中集成的独立外设控制器、挂接在soc内存空间的外设等却不依附于此类总线。为此,linux发明了一种虚拟的总线,成为platform总线,响应的设备成为platform_device,驱动称为platform_driver.

(1)platform_device 位于 /include/linux/Platform_device.h

struct platform_device {
    const char  *name;
    int     id;
    bool        id_auto;
    struct device   dev;
    u32     num_resources;
    struct resource *resource;

    const struct platform_device_id *id_entry;

    /* MFD cell pointer */
    struct mfd_cell *mfd_cell;

    /* arch specific additions */
    struct pdev_archdata    archdata;
};

(2)platform_driver 位于 /include/linux/Platform_device.h

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;
    bool prevent_deferred_probe;
};

platform_driver结构体中包含 probe(),remove(),一个device_driver实例,电源管理函数suspend()、resume()。

device_driver结构体,位于/include/linux/Device.h

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_drvier地位对等的i2c_driver、spi_driver、usb_driver、pci_driver中都包含了device_driver结构体实例成员。它其实描述了各种xxx_driver(xxx是总线名)在驱动意义上的一些共性。

系统为platform总线定义了一个bus_type的实例platform_bus_type,位于drivers/base/paltform.c下,

struct bus_type platform_bus_type = {
    .name       = "platform",
    .dev_groups = platform_dev_groups,
    .match      = platform_match,
    .uevent     = platform_uevent,
    .pm     = &platform_dev_pm_ops,
};

重点关注match()成员函数,次函数确定了platform_device和platform_driver之间是如何进行匹配,platform_match函数代码:

static int platform_match(struct device *dev, struct device_driver *drv)
{
    struct platform_device *pdev = to_platform_device(dev);//获得platform_device 结构体地址
    struct platform_driver *pdrv = to_platform_driver(drv);//获得platform_driver 结构体地址
    /* 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;

    /* Then try to match against the id table */
    if (pdrv->id_table)
        return platform_match_id(pdrv->id_table, pdev) != NULL;

    /* fall-back to driver name match */
    return (strcmp(pdev->name, drv->name) == 0);
}

从上面代码中可以看出,匹配platform_driver 和platform_device有4中可能性,一是基于设备树风格的匹配,二十基于ACPI风格的匹配,三是匹配ID表,四是匹配platform_device设备名和驱动的名字。

(3)platform_device的定义通常在BSP的板文件中实现,在板文件中,将platform_device归纳为一个数组,最终通过platform_add_devices()函数同一注册

int platform_add_devices(struct platform_device **devs, int num)
{
    int i, ret = 0;

    for (i = 0; i < num; i++) {
        ret = platform_device_register(devs[i]);
        if (ret) {
            while (--i >= 0)
                platform_device_unregister(devs[i]);
            break;
        }
    }

    return ret;
}

第一个参数为平台设备数组指针,第二个参数为平台设备的数量,它内被调用了platform_device_register()函数以注册单个的平台设备。

Linux3.x之后,ARM Linux不太喜欢人们以编码的形式去填充platform_device和注册,而倾向于根据设备树中的内容自动展开platform_device。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值