从Linux 2.6起引入了一套新的驱动管理和注册机制:platform_device和platform_driver。Linux中大部分的设备驱动,都可以使用这套机制,设备用platform_device表示,驱动用platform_driver进行注册。
Linuxplatform_driver机制和传统的device_driver机制(通过driver_register函数进行注册)相比,一个十分明显的优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platform_device提供的标准接口进行申请并使用。这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性(这些标准接口是安全的)。platform机制的本身使用并不复杂,由两部分组成:platform_device和platfrom_driver。通过platform机制开发底层设备驱动的大致流程如图所示。
platform_device结构体用来描述设备的名称、资源信息等。该结构被定义在include/linux/platform_device.h中,定义原型如下:
structplatform_device
{
const char *name;
intid;
struct devicedev;
u32num_resources;
struct resource *resource;
struct platform_device_id *id_entry;
structpdev_archdata archdata;
};
structdevice {
struct klist klist_children;
struct klist_node knode_parent;
struct klist_node knode_driver;
struct klist_node knode_bus;
struct device *parent;
struct kobject kobj;
char bus_id[BUS_ID_SIZE];
struct device_type *type;
unsigned is_registered:1;
unsigned uevent_suppress:1;
struct semaphore sem;
struct bus_type * bus;
struct device_driver *driver;
void *driver_data;
void *platform_data;
struct dev_pm_info power;
#ifdef CONFIG_NUMA
int numa_node;
#endif
u64 *dma_mask;
u64 coherent_dma_mask;
struct list_head dma_pools;
struct dma_coherent_mem *dma_mem;
struct dev_archdata archdata;
spinlock_t devres_lock;
struct list_head devres_head;
struct list_head node;
struct class *class;
dev_t devt;
struct attribute_group **groups;
void (*release)(struct device * dev);
};
Platform_device通常在BSP的板文件中实现,在板文件中,将platform_device归纳为一个数组,最终调用platform_add_device()函数统一注册。
下面来看一下platform_device结构体中最重要的一个成员structresource * resource。structresource被定义在include/linux/ioport.h中,定义原型如下:
struct resource
{
resource_size_tstart; //定义资源的起始地址
resource_size_tend; //定义资源的结束地址
constchar *name; //定义资源的名称
unsignedlong flags; //定义资源的类型,比如MEM,IO,IRQ,DMA类型
structresource *parent, *sibling, *child; //资源链表指针
};
通过调用函数platform_add_devices()向系统中添加该设备了,该函数内部调用platform_device_register()进行设备注册。要注意的是,这里的platform_device设备的注册过程必须在相应设备驱动加载之前被调用,即执行platform_driver_register()之前,原因是驱动注册时需要匹配内核中所有已注册的设备名。
接下来来看platform_driver结构体的原型定义,在include/linux/platform_device.h中,代码如下:
structplatform_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 (*suspend_late)(structplatform_device *, pm_message_t state);
int (*resume_early)(structplatform_device *);
int (*resume)(struct platform_device*);
struct device_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机制。
do_basic_setup()->driver_init()->platform_bus_init()->...初始化platform bus(虚拟总线)
设备向内核注册的时候platform_device_register()->platform_device_add()->...内核把设备挂在虚拟的platform bus下
驱动注册的时候platform_driver_register()->driver_register()->bus_add_driver()->driver_attach()->bus_for_each_dev()对每个挂在虚拟的platform bus的设备作__driver_attach()->driver_probe_device()->drv->bus->match()==platform_match()->比较strncmp(pdev->name, drv->name, BUS_ID_SIZE),如果相符就调用platform_drv_probe()->driver->probe(),如果probe成功则绑定该设备到该驱动.