Linux 系统要考虑到驱动的可重用性,因此提出了驱动的分离与分层这样的软件思路。因此提出驱动、总线和设备的驱动架构,总线负责管理驱动和设备。系统中有很多的物理总线:I2c、SPI、USB等,有部分设备不存在物理总线,为了驱动架构统一,因此引入 platform 虚拟总线。
platform 虚拟总线用 platform_driver 结构体表示驱动,用 platform_device 结构体描述设备。对于所有设备都使用 bus_type 结构体类型。
1、bus_typestruct bus_type { const char *name; const char *dev_name; struct device *dev_root; struct device_attribute *dev_attrs; const struct attribute_group **bus_groups; const struct attribute_group **dev_groups; const struct attribute_group **drv_groups; int (*match)(struct device *dev, struct device_driver *drv); int (*uevent)(struct device *dev, struct kobj_uevent_env *env); int (*probe)(struct device *dev); int (*remove)(struct device *dev); void (*shutdown)(struct device *dev); int (*online)(struct device *dev); int (*offline)(struct device *dev); int (*suspend)(struct device *dev, pm_message_t state); int (*resume)(struct device *dev); const struct dev_pm_ops *pm; const struct iommu_ops *iommu_ops; struct subsys_private *p; struct lock_class_key lock_key; };
platform总线是 bus_type 的一个具体实例,定义在文件 drivers/base/platform.c,platform总线定义如下:
struct bus_type platform_bus_type = { .name = "platform", .dev_groups = platform_dev_groups, .match = platform_match, .uevent = platform_uevent, .pm = &platform_dev_pm_ops, }; EXPORT_SYMBOL_GPL(platform_bus_type);
platform_bus_type 就是 platform平台总线,其中 platform_match 就是匹配函数。
platform_match 函数定义在文件 drivers/base/platform.c 中,函数内容如下所示:
static int platform_match(struct device *dev, struct device_driver *drv) { struct platform_device *pdev = to_platform_device(dev); struct platform_driver *pdrv = to_platform_driver(drv); if (pdev->driver_override) return !strcmp(pdev->driver_override, drv->name); if (of_driver_match_device(dev, drv)) return 1; if (acpi_driver_match_device(dev, drv)) return 1; if (pdrv->id_table) return platform_match_id(pdrv->id_table, pdev) != NULL; return (strcmp(pdev->name, drv->name) == 0); }
platform_match 函数讲述驱动和设备的匹配有四种方法:
1、OF 类型的匹配,也就是设备树采用的匹配方式,of_driver_match_device 函数定义在文件 include/linux/of_device.h 中。
2、ACPI 匹配方式。
3、id_table 匹配,每个 platform_driver 结构体有一个 id_table 成员变量。
4、直接比较驱动和设备的 name 字段,看看是不是相等,如果相等的话就匹配成功。
2、platform_driverstruct 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; };
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; };3、platform_device
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; };
struct resource { resource_size_t start; resource_size_t end; const char *name; unsigned long flags; struct resource *parent, *sibling, *child; };二、platform 驱动相关 API 1、platform_driver_register
int platform_driver_register(struct platform_driver *drv);2、platform_driver_unregister
void platform_driver_unregister(struct platform_driver *drv)三、platform 驱动源码编写思路
1、定义 platform 驱动结构体。
2、在 platform 驱动结构体中指定 probe 函数(驱动和设备匹配成功后执行)和 remove 函数(设备注销或驱动注销)。
3、在驱动模块加载函数中调用 platform_driver_register 函数。
4、在驱动模块卸载函数中调用 platform_driver_unregister 函数。
5、probe 函数完成驱动初始化。
#include "linux/init.h" #include "linux/module.h" #include "linux/platform_device.h" static int led_probe(struct platform_device *dev) { printk("led probe!rn"); return 0; } static int led_remove(struct platform_device *dev) { printk("led remove!rn"); return 0; } static struct platform_driver led_driver = { .driver = { .name = "imx6ul-led", }, .probe = led_probe, .remove = led_remove, }; static int __init led_driver_init(void) { return platform_driver_register(&led_driver); } static void __exit led_driver_exit(void) { platform_driver_unregister(&led_driver); } module_init(led_driver_init); module_exit(led_driver_exit); MODULE_LICENSE("GPL");四、platform 设备相关 API 1、platform_device_register
int platform_device_register(struct platform_device *pdev)2、platform_device_unregister
void platform_device_unregister(struct platform_device *pdev)五、platform 设备源码编写思路
1、定义 platform 设备结构体(包括设备资源信息)。
2、在设备模块加载函数中调用 platform_device_register 函数。
3、在设备模块注销函数中调用 leddevice_exit 函数。
#include "linux/init.h" #include "linux/module.h" #include "linux/platform_device.h" static struct resource led_resources[] = { }; static void led_release(struct device *dev) { printk("led device released!rn"); } static struct platform_device led_device = { .name = "imx6ul-led", .id = -1, .dev = { .release = &led_release, }, .num_resources = ARRAY_SIZE(led_resources), .resource = led_resources, }; static int __init led_device_init(void) { return platform_device_register(&led_device); } static void __exit led_device_exit(void) { platform_device_unregister(&led_device); } module_init(led_device_init); module_exit(led_device_exit); MODULE_LICENSE("GPL");六、platform 总结
platform 平台包括驱动、总线和设备 3 部分。总线负责管理驱动和设备,这部分有内核进行维护。设备主要描述硬件相关信息,和设备树作用相同,因此有设备树可以不需要这部分。驱动主要用于实现对硬件的 *** 作,向应用层提供一些 API 接口,供上层应用使用。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)