驱动框架 传统驱动框架 假如现在有三个平台 A、 B 和 C,这三个平台(这里的平台说的是 SOC)上都有 MPU6050 这个 I2C接口的六轴传感器
每种平台下都有一个主机驱动和设备驱动,主机驱动肯定是必须要的,毕竟不同的平台其 I2C 控制器不同。但是右侧的设备驱动就没必要每个平台都写一个,因为不管对于那个 SOC 来说, MPU6050 都是一样,通过 I2C 接口读写数据就行了,只需要一个 MPU6050 的驱动程序即可。
分离后的驱动框架
相当于将设备信息从设备驱动中剥离开来,驱动使用标准方法去获取到设备信息(比如从设备树中获取到设备信息),然后根据获取到的设备信息来初始化设备。 这样就相当于驱动只负责驱动,设备只负责设备,想办法将两者进行匹配即可。这个就是 Linux 中的总线(bus)、驱动(driver)和设备(device)模型,也就是常说的驱动分离。
paltform是一种虚拟的总线,用于管理外设资源内存 资源中断资源。在硬件上有USB-BUS总线,PCI-BUS总线,这是在物理设备上实际存在的总线。USB-BUS管理USB设备,PCI总线管理PCI设备。但是在实际中,一些设备不属于这些总线,一些SOC上面的控制器或者设备。使用platform统一管理这些设备。
Linux系统内核使用bus_type结构体表示总线
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 1 struct bus_type {2 const char *name; 3 const char *dev_name;4 struct device *dev_root ;5 struct device_attribute *dev_attrs ;6 const struct attribute_group **bus_groups ; 7 const struct attribute_group **dev_groups ; 8 const struct attribute_group **drv_groups ; 9 10 int (*match)(struct device *dev, struct device_driver *drv);11 int (*uevent)(struct device *dev, struct kobj_uevent_env *env);12 int (*probe)(struct device *dev);13 int (*remove)(struct device *dev);14 void (*shutdown)(struct device *dev);15 16 int (*online)(struct device *dev);17 int (*offline)(struct device *dev);18 int (*suspend)(struct device *dev, pm_message_t state);19 int (*resume)(struct device *dev);20 const struct dev_pm_ops *pm ;21 const struct iommu_ops *iommu_ops ;22 struct subsys_private *p ;23 struct lock_class_key lock_key ;24 };
总线就是使用 match 函数来根据注册的设备来查找对应的驱动,或者根据注册的驱动来查找相应的设备,因此每一条总线都必须实现此函数。 match 函数有两个参数: dev 和 drv,这两个参数分别为 device 和 device_driver 类型,也就是设备和驱动。
platform 总线 :
1 2 3 4 5 6 7 1 struct bus_type platform_bus_type = {2 .name = "platform" ,3 .dev_groups = platform_dev_groups,4 .match = platform_match,5 .uevent = platform_uevent,6 .pm = &platform_dev_pm_ops,7 };
platform_bus_type 就是 platform 平台总线,其中 platform_match 就是匹配函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 1 static int platform_match (struct device *dev,struct device_driver *drv) 2 { 3 struct platform_device *pdev = to_platform_device(dev);4 struct platform_driver *pdrv = to_platform_driver(drv);5 6 7 if (pdev->driver_override)8 return !strcmp (pdev->driver_override, drv->name);9 10 11 if (of_driver_match_device(dev, drv))12 return 1 ;13 14 15 if (acpi_driver_match_device(dev, drv))16 return 1 ;17 18 19 if (pdrv->id_table)20 return platform_match_id(pdrv->id_table, pdev) != NULL ;21 22 23 return (strcmp (pdev->name, drv->name) == 0 );24 }
对于支持设备树的 Linux 版本号,一般设备驱动为了兼容性都支持设备树和无设备树两种匹配方式。也就是第一种匹配方式一般都会存在,第三种和第四种只要存在一种就可以 ,一般用的最多的还是第四种,也就是直接比较驱动和设备的 name 字段,毕竟这种方式最简单了。
platform驱动
platform_driver 结 构 体 表 示 platform 驱 动 , 此 结 构 体 定 义 在 文 件 include/linux/platform_device.h 中
1 2 3 4 5 6 7 8 9 10 1 struct platform_driver {2 int (*probe)(struct platform_device *);3 int (*remove)(struct platform_device *);4 void (*shutdown)(struct platform_device *);5 int (*suspend)(struct platform_device *, pm_message_t state);6 int (*resume)(struct platform_device *);7 struct device_driver driver ;8 const struct platform_device_id *id_table ;9 bool prevent_deferred_probe;10 };
第 2 行, probe 函数,当驱动与设备匹配成功以后 probe 函数就会执行,非常重要的函数!! 一般驱动的提供者会编写,如果自己要编写一个全新的驱动,那么 probe 就需要自行实现。 第 7 行, driver 成员,为 device_driver 结构体变量, Linux 内核里面大量使用到了面向对象的思维, device_driver 相当于基类,提供了最基础的驱动框架。 plaform_driver 继承了这个基类,然后在此基础上又添加了一些特有的成员变量。
第 8 行, id_table 表,也就是platform 总线匹配驱动和设备的时候采用的第三种方法, id_table 是个表( 也就是数组) ,每个元素的类型为 platform_device_id。
1 2 3 4 1 struct platform_device_id {2 char name[PLATFORM_NAME_SIZE];3 kernel_ulong_t driver_data;4 };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 struct device_driver { const char *name; struct bus_type *bus ; struct module *owner ; const char *mod_name; bool suppress_bind_attrs; 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 ; };
of_match_table 就是采用设备树的时候驱动使用的匹配表,同样是数组,每个匹配项都为 of_device_id 结构体类型 。
1 2 3 4 5 6 7 struct of_device_id { char name[32 ]; char type[32 ]; char compatible[128 ]; const void *data; };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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; struct mfd_cell *mfd_cell ; struct pdev_archdata archdata ; };
1 2 3 4 5 6 7 struct resource { resource_size_t start; resource_size_t end; const char *name; unsigned long flags; struct resource *parent , *sibling , *child ; };
start 和 end 分别表示资源的起始和终止信息,对于内存类的资源,就表示内存起始和终止地址, name 表示资源名字, flags 表示资源类型。
1)定义一个 platform_driver 结构体变量。
2)实现probe函数。
3)实现remove函数。
4)实现of_match_table。
5)调用platform_driver_register 函数向 Linux 内核注册一个 platform 驱动。
6)调用platform_driver_unregister 函数卸载 platform 驱动。