从Linux2.6内核起,引入一套新的驱动管理和注册机制:platform_device 和 platform_driver 。Linux 中大部分的设备驱动,都可以使用这套机制,设备用 platform_device 表示;驱动用 platform_driver 进行注册。
Linux platform driver 机制和传统的device driver机制(即:通过 driver_register 函数进行注册)相比,一个十分明显的优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中用使用这些资源时,通过platform device提供的标准接口进行申请并使用。
platform 是一个虚拟的地址总线,相比 PCI、USB,它主要用于描述SOC上的片上资源。platform 所描述的资源有一个共同点:在CPU 的总线上直接取址。平台设备会分到一个名称(用在驱动绑定中)以及一系列诸如地址和中断请求号(IRQ)之类的资源。
一. platform 虚拟总线
系统中为platform总线定义了一个bus_type的实例platform_bus_type, 其代码如下:
struct bus_type platform_bus_type
= {
.name = "platform", //platform 总线名称
.dev_attrs = platform_dev_attrs, //platform device 属性
.match = platform_match, //platform 匹配函数,这个结构的重点
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
处理函数match()表明了platform_device和platform_driver之间如何匹配代码如下:
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev;
pdev = container_of(dev, struct platform_device, dev);
return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
}
匹配platform_device和platform_driver主要看二者的name字段是否相同。
二. platform device
1. 数据结构
struct platform_device
{
const char * name; //platform 设备名称
int id;
//id表示,如果值为-1表示设备名是唯一的,如果非负值,表示同名字的第几个设备,比如name="a", id=0, 表示设备a/0。
struct device dev;
//一般设备对象,具有一般设备的属性
u32 num_resources; //资源总数
struct resource * resource; //具体资源指针
const struct platform_device_id *id_entry;
//platform device ID表
struct mfd_cell *mfd_cell;
struct pdev_archdata archdata;
};
可以把platform devce理解理解为device的一个子类,它具有一般device的特性,只是多了resource管理相关的一些属性,把相关资源注册到内核进行管理。
2. 相关函数
(1). platform_add_devices
int platform_add_devices(struct platform_device **devs, int num)
{
int i, ret = 0;
for (i = 0; i < num; i++) {
ret = platform_device_register(devs[i]);
if (ret) {
while (--i >= 0)
platform_device_unregister(devs[i]);
break;
}
}
return ret;
}
(2). platform_device_register
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev);
arch_setup_pdev_archdata(pdev);
return platform_device_add(pdev);
}
这个函数调用platform_device_add把platform device加入系统,它跟device_add最主要的区别是多了一步insert_resource(p, r),即将platform资源(resource)添加进内核,由内核统一管理。
三. platform driver
1. 数据结构
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;
};
当platform driver注册成功时,会调用 platform_driver 结构元素 probe 函数指针。
2. 相关函数
int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type;
if (drv->probe)
drv->driver.probe = platform_drv_probe;
if (drv->remove)
drv->driver.remove = platform_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = platform_drv_shutdown;
return driver_register(&drv->driver);
}
在驱动程序的初始化函数中,调用了platform_driver_register()注册 platform_driver 。需要注意的是:platform_driver 和 platform_device 中的 name 变量的值必须是相同的。这样在 platform_driver_register() 注册时,会将当前注册的 platform_driver 中的 name 变量的值和已注册的所有 platform_device 中的 name 变量的值进行比较,只有找到具有相同名称的
platform_device 才能注册成功。
platform_driver_register()的注册过程:
1 platform_driver_register(&s3c2410fb_driver)
2 driver_register(&drv->driver)
3 bus_add_driver(drv)
4 driver_attach(drv)
5 bus_for_each_dev(drv->bus, NULL, drv, __driver_attach)
6 __driver_attach(struct device * dev, void * data)
7 driver_probe_device(drv, dev)
8 really_probe(dev, drv)
在really_probe()中,为设备指派管理该设备的驱动:dev->driver = drv, 调用probe()函数初始化设备:drv->probe(dev)
四. platform总线上设备驱动开发步骤
1. 包含头文件
#include <linux/platform_device.h>
2. 在板文件中初始化 resource 结构变量
static struct resource msm_fb_resources[] = {
{
.flags = IORESOURCE_DMA,
}
};
3. 在板文件中初始化 platform_device 结构变量
static struct platform_device msm_fb_device
= {
.name = "msm_fb",
.id = 0,
.num_resources = ARRAY_SIZE(msm_fb_resources),
.resource = msm_fb_resources,
.dev.platform_data = &msm_fb_pdata,
};
4. 向系统注册设备:platform_device_register
platform_device_register(&msm_fb_device);
5. 实现的结构体platform_driver
static struct platform_driver msm_fb_driver
= {
.probe = msm_fb_probe,
.remove = msm_fb_remove,
#ifndef CONFIG_HAS_EARLYSUSPEND
.suspend = msm_fb_suspend,
.resume = msm_fb_resume,
#endif
.shutdown = NULL,
.driver = {
/* Driver name must match the device name added in platform.c. */
.name = "msm_fb",
.pm = &msm_fb_dev_pm_ops,
},
};
6. 调用 platform_driver_register 注册platform driver
static int msm_fb_register_driver(void)
{
return platform_driver_register(&msm_fb_driver);
}
Platform device 和 Platform driver实际上是cpu总线可以直接寻址的设备和驱动,他们挂载在一个虚拟的总线platform_bus_type上,是一种bus-specific设备和驱动。与其他bus-specific驱动比如pci是一样的。所有的设备通过bus_id挂在总线上,多个device可以共用一个driver,但是一个device不可以对应多个driver。驱动去注册时候会根据设备名寻找设备,没有设备会注册失败,注册的过程会通过probe来进行相应资源的申请,以及硬件的初始化,如果probe执行成功,则device和driver的绑定就成功了。设备注册的时候同样会在总线上寻找相应的驱动,如果找到他也会试图绑定,绑定的过程同样是执行probe。