从Linux 2.6起引入了一套新的驱动管理和注册机制latform_device和Platform_driver。
Linux中大部分的设备驱动,都可以使用这套机制, 设备用Platform_device表示,驱动用Platform_driver进行注册。
Linux platform driver机制和传统的devicedriver 机制(通过driver_register函数进行注册)相比,一个十分明显的优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platform device提供的标准接口进行申请并使用。这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性(这些标准接口是安全的)。
Platform机制的本身使用并不复杂,由两部分组成:platform_device和platfrom_driver。
通过Platform机制开发发底层驱动的大致流程为: 定义platform_device à 注册 platform_device à 定义platform_driver à注册 platform_driver。
首先要确认的就是设备的资源信息,例如设备的地址,中断号等。
在2.6内核中platform设备用结构体platform_device来描述,该结构体定义在kernel\include\linux\platform_device.h中,
struct platform_device {
const char * name;
u32 id;
struct device dev;
u32 num_resources;
struct resource * resource;
};
该结构一个重要的元素是resource,该元素存入了最为重要的设备资源信息,定义在kernel\include\linux\ioport.h中,
struct resource {
const char *name;
unsigned long start, end;
unsigned long flags;
struct resource *parent, *sibling, *child;
};
下面举s3c2410平台的i2c驱动作为例子来说明:
/* arch/arm/mach-s3c2410/devs.c */
/* I2C */
static struct resource s3c_i2c_resource[] = {
[0] = {
.start = S3C24XX_PA_IIC,
.end = S3C24XX_PA_IIC + S3C24XX_SZ_IIC - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_IIC, //S3C2410_IRQ(27)
.end = IRQ_IIC,
.flags = IORESOURCE_IRQ,
}
};
这里定义了两组resource,它描述了一个I2C设备的资源,第1组描述了这个I2C设备所占用的总线地址范围,IORESOURCE_MEM表示第1组描述的是内存类型的资源信息,第2组描述了这个I2C设备的中断号,IORESOURCE_IRQ表示第2组描述的是中断资源信息。设备驱动会根据flags来获取相应的资源信息。
有了resource信息,就可以定义platform_device了:
struct platform_device s3c_device_i2c = {
.name = "s3c2410-i2c",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_i2c_resource),
.resource = s3c_i2c_resource,
};
定义好了platform_device结构体后就可以调用函数platform_add_devices向系统中添加该设备了,之后可以调用platform_driver_register()进行设备注册。要注意的是,这里的platform_device设备的注册过程必须在相应设备驱动加载之前被调用,即执行platform_driver_register之前,原因是因为驱动注册时需要匹配内核中所以已注册的设备名。
s3c2410-i2c的platform_device是在系统启动时,在cpu.c里的s3c_arch_init()函数里进行注册的,这个函数申明为arch_initcall(s3c_arch_init);会在系统初始化阶段被调用。
arch_initcall的优先级高于module_init。所以会在Platform驱动注册之前调用。(详细参考include/linux/init.h)
s3c_arch_init函数如下:
/* arch/arm/mach-3sc2410/cpu.c */
static int __init s3c_arch_init(void)
{
int ret;
……
/* 这里board指针指向在mach-smdk2410.c里的定义的smdk2410_board,里面包含了预先定义的I2C Platform_device等. */
if (board != NULL) {
struct platform_device **ptr = board->devices;
int i;
for (i = 0; i < board->devices_count; i++, ptr++) {
ret = platform_device_register(*ptr); //在这里进行注册
if (ret) {
printk(KERN_ERR "s3c24xx: failed to add board device %s(%d) @%p\n", (*ptr)->name,
ret, *ptr);
}
}
/* mask any error, we may not need all these board
* devices */
ret = 0;
}
return ret;
}
从Linux 2.6起引入了一套新的驱动管理和注册机制:platform_device和platform_driver。Linux中大部分的设备驱动,都可以使用这套机制,设备用platform_device表示,驱动用platform_driver进行注册。
Linux platform. driver机制和传统的devicedriver 机制(通过driver_register函数进行注册)相比,一个十分明显的优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platform. device提供的标准接口进 行申请并使用。这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性(这些标准接口是安全的)。platform机制的本身使用并不复杂,由两部分组成:platform_device和platfrom_driver。通过platform机制开发底层设备驱动的大致流程如图所示。
图 platform机制开发驱动流程
定义platform_device -----》注册platform_device -----》定义platform_driver -----》注册platform_driver
platform_device结构体用来描述设备的名称、资源信息等。该结构被定义在include/linux/platform_device.h中,定义原型如下:
struct platform_device {
const char * name; //定义平台设备的名称
int id;
struct device dev;
u32 num_resources;
struct resource * resource; //定义平台设备的资源。
};
下面来看一下platform_device结构体中最重要的一个成员structresource * resource。struct resource被定义在include/linux/ioport.h中,定义原型如下:
struct resource {
resource_size_t start; //定义资源的起始地址
resource_size_t end; //定义资源的结束地址
const char *name; //定义资源的名称
unsigned long flags; //定义资源的类型,比如MEM,IO,IRQ,DMA类型
struct resource *parent, *sibling, *child; //资源链表指针
};
通过调用函数platform_add_devices()向系统中添加该设备了,该函数内部调用 platform_device_register( )进行设备注册。要注意的是,这里的platform_device设备的注册过程必须在相应设备驱动加载之前被调用,即执行 platform_driver_register()之前,原因是驱动注册时需要匹配内核中所有已注册的设备名。
接下来来看platform_driver结构体的原型定义,在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_tstate);
int (*suspend_late)(struct platform_device *,pm_message_t state);
int (*resume_early)(struct platform_device *);
int (*resume)(struct platform_device *);
struct device_driver driver;
};
内核提供的platform_driver结构体的注册函数为platform_driver_register(),其原型定义在driver/base/platform.c文件中,具体实现代码如下:
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;
if (drv->suspend)
drv->driver.suspend = platform_drv_suspend;
if (drv->resume)
drv->driver.resume = platform_drv_resume;
return driver_register(&drv->driver);
}
总结,通常情况下只要和内核本身运行依赖性不大的外围设备,相对独立的,拥有各自独自的资源(地址总线和IRQs),都可以用platform_driver实现。如:LCD,网卡、USB、UART等,都可以用platfrom_driver写,而timer,irq等小系统之内的设备则最好不用platfrom_driver机制。