ILD

Platform driver
作者:YUAN JIANPENG 邮箱:yuanjp@hust.edu.cn
发布时间:2018-7-22 站点:Inside Linux Development

像PCI bus和USB bus,可以发现连接到bus上的设备。有些设备是CPU不能发现的。因此需要通过某种方式显示地告诉有指定的设备。platform devices就是这种设备。platform driver就是处理这些设备的驱动。


Platform drivers

struct platform_driver定义在<linux/pltform_device.h>,其它platform相关的声明也在这个头文件。

1
2
3
4
5
6
7
8
9
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;};


至少需要提供probe和remove回调函数,其它几个回调函数是用来做电源管理。另外,必须提供一种方式,使bus能绑定device到driver。


第一种方式是 id_table参数。id_table是下述结构体的指针,

1
2
3
4
struct platform_device_id {
    char name[PLATFORM_NAME_SIZE];
    kernel_ulong_t driver_data;
};


如果给出了id_table,新的platform device可以通过id来匹配,如果device的名字匹配id,则交给该driver管理,同时一个指向匹配的id table 条目的指针,驱动可以读取到。


但是大多数platform driver不提供id table,它们只提供driver本身的名字,这也是可以匹配的:

1
2
3
4
5
6
7
8
    static struct platform_driver i2c_gpio_driver = {
    .driver     = {
        .name   = "i2c-gpio",
        .owner  = THIS_MODULE,
    },
    .probe      = i2c_gpio_probe,
    .remove     = __devexit_p(i2c_gpio_remove),
    };

叫"i2c-gpio"的device将被绑定到上述driver,不需要提供ID table。


使用下述接口注册platform_driver到内核:

1
2
3
4
#define platform_driver_register(drv) \
    __platform_driver_register(drv, THIS_MODULE)
extern int __platform_driver_register(struct platform_driver *,
                    struct module *);


当有新的device绑定到driver时,probe回调函数将会被调用,参数是一个指向platform_device结构体的指针,描述想要实例化的device。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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;
    char *driver_override; /* Driver name to force a match */
 
    /* MFD cell pointer */
    struct mfd_cell *mfd_cell;
 
    /* arch specific additions */
    struct pdev_archdata   archdata;
};


如果device匹配一个id table entry,则id_entry指向那个entry。


resource数组,可以用来获取各种资源,包括memory-mapped I/O,interrupt lines等。有很多helper函数来获取resource数组中的数据:

1
2
3
4
5
struct resource *platform_get_resource(struct platform_device *pdev, 
                       unsigned int type, unsigned int n);
struct resource *platform_get_resource_byname(struct platform_device *pdev,
                       unsigned int type, const char *name);
int platform_get_irq(struct platform_device *pdev, unsigned int n);


Platform devices

platform device是不可发现的,必须通过其它方式告诉内核它的存在。典型地,使用一个静态定义的platform_device结构。最少需要提供一个name。一个简单的设备可以这样提供:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    static struct resource foomatic_resources[] = {
    {
        .start  = 0x10000000,
        .end    = 0x10001000,
        .flags  = IORESOURCE_MEM,
        .name   = "io-memory"
    },
    {
        .start  = 20,
        .end    = 20,
        .flags  = IORESOURCE_IRQ,
        .name   = "irq",
    }
    };
 
    static struct platform_device my_foomatic = {
    .name      = "foomatic",
    .resource   = foomatic_resources,
    .num_resources  = ARRAY_SIZE(foomatic_resources),
    };


要注册device,使用下述接口:

1
int platform_device_register(struct platform_device *pdev);


Platform data

resource是固定格式的,可能不能包含所有的信息,因此在struct device dev中,提供一个platform data的void *类型的指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    #include <linux/i2c-gpio.h>
 
    static struct i2c_gpio_platform_data my_i2c_plat_data = {
    .scl_pin    = 100,
    .sda_pin    = 101,
    };
 
    static struct platform_device my_gpio_i2c = {
    .name       = "i2c-gpio",
    .id     = 0,
    .dev = {
        .platform_data = &my_i2c_plat_data,
    }
    };


参考:

https://stackoverflow.com/questions/15610570/what-is-the-difference-between-a-linux-platform-driver-and-normal-device-driver

The platform device API. https://lwn.net/Articles/448499/

Platform devices and device trees. https://lwn.net/Articles/448502/


Copyright © insidelinuxdev.net 2017-2021. Some Rights Reserved.