platform 设备驱动:平台设备驱动,是一条虚拟的总线,而不是像IIC、SPI这种的物理总线,是驱动分离和分层思想的产物,适用于那些无法使用IIC、SPI、USB等总线的外设。
1.驱动的分离与分层
1.1 前因
嵌入式系统中有许多的总线,例如IIC、SPI等,而如果按照之前写驱动的方法,假设有ABC三个平台,都有一个IIC接口的传感器,那么在编写驱动时,最终的驱动框架将会是下图所示,会产生大量的重复代码。
于是便有了驱动分离和分层的思想,在这里引入了IIC总线、SPI总线、USB总线等概念,而有些外设是不属于这些总线,但他也要使用这个设备模型,于是加入了platform这个虚拟总线,给这些设备使用。
1.2 驱动分离与分层概念
1)驱动分离:
将主机驱动(主机上的IIC等)和设备驱动(采用IIC通信的设备)分隔开,总线作为它们建立联系的桥梁,形成总线(bus) 、驱动(driver) 、设备(device) 模型,关系如下图。
当注册驱动时,总线会在右侧的设备中寻找与之匹配的设备,反之注册设备,总线会在左边查找匹配的驱动,寻找到后将他们建立联系。
这里借鉴网上找到的一张图,详细介绍了平台设备驱动分离与函数调用堆栈,留着慢慢参考研究
2)驱动分层:
为了在不同层处理不同的内容,例如Input子系统负责管理所有与输入相关的驱动,input 核心层会处理各种 IO 模型,各个层级各司其职。
2.platform驱动模型简介
2.1 platform bus
Linux系统内核使用bus_type 结构体表示总线,该结构体定义在文include/linux/device.h
struct bus_type {
const char *name;
const char *dev_name;
struct device *dev_root;
struct device_attribute *dev_attrs; /* use dev_groups instead */
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;
};
其中的match函数用于总线完成设备和驱动之间匹配 dev为设备,drv为驱动,所以每一条总线都必须实现这个函数。
platform总线是bus_type的一个示例,在 drivers/base/platform.c 文件,内容如下:
struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
其中使用platform_match进行驱动和设备之间的匹配。
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);
/* When driver_override is set, only bind to the matching driver */
if (pdev->driver_override)
return !strcmp(pdev->driver_override, drv->name);
/* Attempt an OF style match first */
if (of_driver_match_device(dev, drv))
return 1;
/* Then try ACPI style match */
if (acpi_driver_match_device(dev, drv))
return 1;
/* Then try to match against the id table */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0);
}
of_driver_match_device:设备树的匹配方式,通过compatible属性在OF表匹配,匹配后执行probe函数
acpi_driver_match_device:ACPI匹配方式
platform_match_id:id_table方式
(strcmp(pdev->name, drv->name):直接比较驱动和设备的name字段进行匹配
依次执行上面的四种方式,完成驱动和设备的匹配
2.2 platform驱动
Linux系统内核使用 platform_driver 结构体表示platform驱动,该结构体定义在文include/linux/ platform_device.h
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;
};
struct device_driver {
const char *name;
struct bus_type *bus;
struct module *owner;
const char *mod_name; /* used for built-in modules */
bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
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;
};
struct platform_device_id {
char name[PLATFORM_NAME_SIZE];
kernel_ulong_t driver_data;
};
probe函数:驱动和设备匹配成功执行
driver:提供最基本的驱动框架
id_tabel:id_table方式匹配时,便是匹配这个
of_match_table: OF匹配表,设备树的匹配方式,通过compatible属性在OF表匹配,匹配后执行probe函数
2.3 platform设备
Linux系统内核使用 platform_device 结构体表示platform设备,该结构体定义在**include/linux/ ** include/linux/platform_device.h 。
如果内核不支持设备树,则使用 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; /* Driver name to force a match */
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
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;
};
name:设备名字,与platform_driver的name字段相同
num_resources:资源数量
resource:设备信息资源
start:资源起始信息
end:资源终止信息
name:资源名字
flags:资源类型
资源信息例如下面的宏定义
#define IORESOURCE_BITS 0x000000ff /* Bus-specific bits */
#define IORESOURCE_TYPE_BITS 0x00001f00 /* Resource type */
#define IORESOURCE_IO 0x00000100 /* PCI/ISA I/O ports */
#define IORESOURCE_MEM 0x00000200
#define IORESOURCE_REG 0x00000300 /* Register offsets */
#define IORESOURCE_IRQ 0x00000400
#define IORESOURCE_DMA 0x00000800
#define IORESOURCE_BUS 0x00001000
其实就是一系列的偏移地址
3.platform驱动API
如果是内核支持设备树的情况下,则不需要使用platform device描述设备,不使用platform_device的注册及注销函数。
void platform_device_unregister(struct platform_device *pdev) //注销platform设备
int platform_device_register(struct platform_device *pdev) //注册platform设备
int platform_driver_register (struct platform_driver *driver) //注册platform驱动
void platform_driver_unregister(struct platform_driver *drv) //注销platform驱动