像PCI bus和USB bus,可以发现连接到bus上的设备。有些设备是CPU不能发现的。因此需要通过某种方式显示地告诉有指定的设备。platform devices就是这种设备。platform driver就是处理这些设备的驱动。
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 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); |
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, } }; |
参考:
The platform device API. https://lwn.net/Articles/448499/
Platform devices and device trees. https://lwn.net/Articles/448502/