在Linux2.6以后的设备驱动模型中,需要关心总线、设备和驱动3个实体,总线将设备和驱动 绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。
platform总线
linux系统内核使用bus_type结构体表示总线,该结构体定义在include/linux/device.h。
重点关注结构体中的函数: int (*match)(struct device *dev, struct device_driver *drv);
该函数完成设备和驱动之间匹配。总线就是使用match函数来根据注册的设备来查找对应的驱动,或根据注册的驱动来查找相应的设备,因此每一条总线都必须实现此函数。match函数的两个参数分别代表设备和驱动。
platform总线是bus_type的一个实例,定义在drivers/base/platform.c 。
struct bus_type platform_bus_type = { ... .match = platform_match, ... };platform_bus_type就是platform平台总线。platform_match就是匹配函数。
static int platform_match(struct device *dev, struct device_driver *drv)platform_match()函数确定了platform_device和platform_driver之间的匹配。
可能有四种匹配情况:
1、基于设备树风格的匹配
if (of_driver_match_device(dev,drv)) return 12、基于ACPI风格的匹配
if (acpi_driver_match_device(dev,drv)) return 13、匹配ID表
if (pdrv->id_table) return platform_match_id(pdrv->id_table, pdev) != NULL;4、匹配platform_device设备名和驱动的名字
return (strcmp(pdev->name, drv->name) == 0);platform驱动
platform_driver结构体
struct platform_driver { int (*probe)(struct platform_device *); int (*remove)(struct platform_device *); ... struct device_driver driver; const struct platform_device_id *id_table; ... };id_table就是platform总线匹配驱动和设备时的第三种方法。
在编写platform驱动时,需要完成probe的编写,比如:
static int led_probe(struct platform_device *dev) { struct resource *ledsource; int ressize; ledsource = platform_get_resource(dev, IORESOURCE_MEM, 0); ressize = resource_size(ledsource); ... return 0; }对resource的定义通常在BSP的板文件中进行,而在具体的设备驱动中通过函数platform_get_resource()获取:
struct resource *platform_get_resource(struct platform_device *, unsigned int, unsigned int);resource_size()是一个内联函数,输入参数const struct resource *res,返回值是资源的大小,
结构体中device_driver的结构体定义在include/linux/device.h
struct device_driver { const char *name; struct bus_type *bus; ... const struct of_device_id *of_match_table; ... };其中of_device_id结构体定义在include/linux/mod_devicetable.h,of_match_table就是采用设备树时驱动使用的匹配表。
struct of_device_id { char name[32]; char type[32]; char compatible[128]; const void *data; };定义及初始化paltform_driver结构体变量后,需要调用platform_driver_register函数向linux内核注册一个platform驱动。
int platform_driver_register(struct platform_driver *driver)在驱动卸载函数中通过platform_driver_unregister函数卸载platform驱动。
void platform_driver_unregister(struct platform_driver *drv)需要增加的platform驱动函数和数据
static int xxx_probe(struct platform_device *dev) { cdev_init(&xxxdev.cdev, &xxx_fops); ... return 0; } static int xxx_remove(struct platfomr_device *dev) { ... cdev_del(&xxxdev.cdev); ... return 0; } static const struct of_device_id xxx_of_match[] = { {.compatible = "xxx-gpio"}, ..., }; static struct platform_driver xxx_driver = { .driver = { .name = "xxx", .of_match_table = xxx_of_match, }, .probe = xxx_probe, .remove = xxx_remove, }; static int __init xxxdriver_init(void) { return platform_driver_register(&xxx_driver); } static void __exit xxxdriver_exit(void) { platform_driver_unregister(&xxx_driver); } module_init(xxxdriver_init); module_exit(xxxdriver_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR();platform设备
platform_device结构体表示platform设备,这个结构体定义在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; ... };其中name要和 paltform驱动的name字段相同,否则设备就无法匹配到对应的驱动。
resource结构体
struct resource { resource_size_t start; resource_size_t end; const char *name; unsigned long flags; struct resource *parent, *sibling, *child; };不支持设备树的linux版本,需要写platform_device变量来描述设备信息,然后使用platform_device_register函数将设备信息注册到linux内核中。
int platform_device_register(struct platform_device *pdev)注销platform设备的函数
void platform_device_unregister(struct platform_device *pdev)platform设备信息框架
/* 寄存器地址定义 */ #define PERIPH1_REGISTER_BASE (0X20000000) #define PERIPH2_REGISTER_BASE (0X20000068) #define REGISTER_LENGTH 4 static struct resource xxx_resources[] = { [0] = { .start = PERIPH1_REGISTER_BASE, .end = (PERIPH1_REGISTER_BASE + REGISTER_LENGTH - 1), .flags = IORESOURCE_MEM, }, [1] = { .start = PERIPH2_REGISTER_BASE, .end = (PERIPH2_REGISTER_BASE + REGISTER_LENGTH - 1), .flags = IORESOURCE_MEM, }, }; static struct platform_device xxxdevice = { .name = "xxx-gpio", .id = -1, .num_resources = ARRAY_SIZE(xxx_resources), .resource = xxx_resources, }; static int __init xxxdevice_init(void) { return platform_device_register(&xxxdevice); } static void __exit xxxdevice_exit(void) { return platform_device_unregister(&xxxdevice); } module_init(xxxdevice_init); module_exit(xxxdevice_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR();参考资料
1. I.MX6U 嵌入式 Linux 驱动开发指南 V2.0.1 正点原子
2. Linux设备驱动开发详解:基于最新的Linux 4.0内核