在介绍IO内存资源的获取和使用之前,首先先介绍一下linux的platform机制。
从Linux 2.6起引入了一套新的驱动管理和注册机制:platform机制,platform机制的本身使用并不复杂,由两部分组成:platform_device和platfrom_driver。Linux中大部分的设备驱动,都可以使用这套机制,设备用platform_device表示,驱动用platform_driver进行注册。
Linux platform driver机制和传统的device driver 机制(通过driver_register函数进行注册)相比,一个十分明显的优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platform device提供的标准接口进行申请并使用。这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性(这些标准接口是安全的)。platform机制的本身使用并不复杂,由两部分组成:platform_device和platfrom_driver。
platform_device结构体用来描述设备的名称、资源信息等。该结构被定义在include/linux/platform_device.h中,定义原型如下:
struct platform_device {
constchar * name; //定义平台设备的名称
int id;
struct device dev;
u32 num_resources;
structresource * resource; //定义平台设备的资源。
};
下面来看一下platform_device结构体中最重要的一个成员struct resource * resource。设备的资源的描述信息存放在 resource 数据结构中, 相同的资源存放在一个树形数据结构中,通过父节点,兄弟节点,子节点相连。 比如中断资源, IO端口资源, IO内存资源, DMA资源有不同资源树。 struct resource被定义在include/linux/ioport.h中,定义原型如下:
struct resource {
resource_size_tstart; //定义资源的起始地址
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)(structplatform_device *);
int(*remove)(struct platform_device *);
void(*shutdown)(struct platform_device *);
int(*suspend)(struct platform_device *, pm_message_t state);
int(*suspend_late)(struct platform_device *, pm_message_t state);
int(*resume_early)(struct platform_device *);
int(*resume)(struct platform_device *);
structdevice_driver driver;
};
内核提供的platform_driver结构体的注册函数为platform_driver_register(),其原型定义在driver/base/platform.c文件中,具体实现代码如下:
int platform_driver_register(structplatform_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;
returndriver_register(&drv->driver);
}
总结,通常情况下只要和内核本身运行依赖性不大的外围设备,相对独立的,拥有各自独自的资源(地址总线和IRQs),都可以用platform_driver实现。如:LCD,网卡、USB、UART等,都可以用platfrom_driver写,而timer,irq等小系统之内的设备则最好不用platfrom_driver机制。
区别:
(1)platform_device_register和device_register的区别主要是有没有resource的区别,前者的结构体包含后面,并且增加了struct resource结构体成员,后者没有。platform_device_register在device_register的基础上增加了struct resource部分的注册。由此。可以看出,platform_device---paltform_driver_register机制与device-driver的主要区别就在于resource。前者适合于具有独立资源设备的描述,后者则不是。
其实linux的各种其他驱动机制的基础都是device_driver。只不过是增加了部分功能,适合于不同的应用场合。
platform_add_device是device_add的衍生
intplatform_device_add(struct platform_device *pdev)
{
int i, ret = 0;
if (!pdev) /*验证指针的有效性 */
return -EINVAL;
if(!pdev->dev.parent)/*都说总线有两个链表,一个是设备链表(通过device 内嵌)一个是驱动链表(通过device_driver内嵌)这里如果pdev->dev.parent为0,说明设备链表还没有设备,因此处理办法是将platform_bus作为设备链表的开始,一直感觉platform_bus和platform_bus_type很难区分,不过在这里清楚了platform_bus是一个设备,platform_bus_type才是真正的总线*/
pdev->dev.parent= &platform_bus;/*device 的父结点*/
pdev->dev.bus= &platform_bus_type;/*device 要挂接在platform_bus_type这个总线上拉,看到了,设备和总线是这么勾搭上滴,很直接,很干脆*/
if (pdev->id!= -1)
snprintf(pdev->dev.bus_id, BUS_ID_SIZE,"%s.%d", pdev->name,
pdev->id);/*这个如果看不懂,可以参考LINUX的格式化输出的相关资料*/
else
strlcpy(pdev->dev.bus_id, pdev->name,BUS_ID_SIZE);
for (i = 0; i< pdev->num_resources; i++) {
struct resource *p, *r =&pdev->resource[i];
if (r->name == NULL) /*name一般为NULL*/
r->name = pdev->dev.bus_id; /*资源的名称赋值为pdev->dev.bus_id,如果一个platform_device有多个resource 则出现同名现象*/
p = r->parent;
if (!p) { /*父资源为0,说明不是从一个大的资源里面切割出来的*/
if (r->flags & IORESOURCE_MEM)
p = &iomem_resource;
else if (r->flags & IORESOURCE_IO)
p = &ioport_resource;
}
if (p && insert_resource(p, r)) { /*如果从父资源里面切割失败,则进行如下处理*/
printk(KERN_ERR
"%s: failed to claim resource%d\n",
pdev->dev.bus_id, i);
ret = -EBUSY;
goto failed;
}
}
pr_debug("Registeringplatform device '%s'. Parent at %s\n",
pdev->dev.bus_id,pdev->dev.parent->bus_id);
ret =device_add(&pdev->dev);/*资源也分配好了,准备工作也做足,终于可以把设备添加到设备链表里面了*/
if (ret == 0)
return ret;
failed: /*失败处理*/
while (--i>= 0)
if (pdev->resource[i].flags &(IORESOURCE_MEM|IORESOURCE_IO))
release_resource(&pdev->resource[i]);
return ret;
}
EXPORT_SYMBOL_GPL(platform_device_add);