Linux内核中的platform机制
从Linux2.6起引入了一套新的驱动管理和注册机制:platform_device和platform_driver。Linux中大部分的设备驱动,都可以使用这套机制,设备用platform_device表示,驱动用platform_driver进行注册。
Linuxplatformdriver机制和传统的devicedriver机制(通过driver_register函数进行注册)相比,一个十分明显的优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platformdevice提供的标准接口进行申请并使用。这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性(这些标准接口是安全的)。platform机制的本身使用并不复杂,由两部分组成:platform_device和platfrom_driver。通过platform机制开发底层设备的流程是申请platform_device,注册platform_device,注册platform_driver
platform_device结构体用来描述设备的名称、资源信息等。该结构被定义在include/linux/platform_device.h中,定义原型如下:
structplatform_device{
constchar*name;//定义平台设备的名称
intid;
structdevicedev;
u32num_resources;
structresource*resource;//定义平台设备的资源。
};
下面来看一下platform_device结构体中最重要的一个成员structresource*resource。structresource被定义在include/linux/ioport.h中,定义原型如下:
structresource{
resource_size_tstart;//定义资源的起始地址
resource_size_tend;//定义资源的结束地址
constchar*name;//定义资源的名称
unsignedlongflags;//定义资源的类型,比如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)(structplatform_device*);
int(*remove)(structplatform_device*);
void(*shutdown)(structplatform_device*);
int(*suspend)(structplatform_device*,pm_message_tstate);
int(*suspend_late)(structplatform_device*,pm_message_tstate);
int(*resume_early)(structplatform_device*);
int(*resume)(structplatform_device*);
structdevice_driverdriver;
};
内核提供的platform_driver结构体的注册函数为platform_driver_register(),其原型定义在driver/base/platform.c文件中,具体实现代码如下:
intplatform_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);
}
下面举个例子来说明一下:在kernel/arch/arm/mach-pxa/pxa27x.c定义了tatic struct resource pxa27x_ohci_resources[] = {[0] = { .start = 0x4C000000, .end = 0x4C00ff6f, .flags = IORESOURCE_MEM,},[1] = { .start = IRQ_USBH1, .end = IRQ_USBH1, .flags = IORESOURCE_IRQ,},};这里定义了两组resource,它描述了一个usb host设备的资源,第1组描述了这个usb host设备所占用的总线地址范围,IORESOURCE_MEM表示第1组描述的是内存类型的资源信息,第2组描述了这个usb host设备的中断号,IORESOURCE_IRQ表示第2组描述的是中断资源信息。设备驱动会根据flags来获取相应的资源信息。有了resource信息,就可以定义platform_device了:static struct platform_device ohci_device = {.name = “pxa27x-ohci”,.id = -1,.dev = { .dma_mask = &pxa27x_dmamask, .coherent_dma_mask = 0xffffffff,},.num_resources = ARRAY_SIZE(pxa27x_ohci_resources),.resource = pxa27x_ohci_resources,};有了platform_device就可以调用函数platform_add_devices向系统中添加该设备了,这里的实现是static int __init pxa27x_init(void){return platform_add_devices(devices, ARRAY_SIZE(devices));}这里的pxa27x_init必须在设备驱动加载之前被调用,可以把它放到subsys_initcall(pxa27x_init);
驱动程序需要实现结构体struct platform_driver,参考kernel/driver/usb/host/ohci-pxa27.c,
static struct platform_driver ohci_hcd_pxa27x_driver = {.probe = ohci_hcd_pxa27x_drv_probe,.remove = ohci_hcd_pxa27x_drv_remove,#ifdef CONFIG_PM.suspend = ohci_hcd_pxa27x_drv_suspend, .resume = ohci_hcd_pxa27x_drv_resume,#endif.driver = { .name = “pxa27x-ohci”,},};
在驱动初始化函数中调用函数platform_driver_register()注册platform_driver,需要注意的是ohci_device结构中name元素和ohci_hcd_pxa27x_driver结构中driver.name必须是相同的,这样在platform_driver_register()注册时会对所有已注册的所有platform_device中的name和当前注册的platform_driver的driver.name进行比较,只有找到相同的名称的platfomr_device才能注册成功,当注册成功时会调用platform_driver结构元素probe函数指针,这里就是ohci_hcd_pxa27x_drv_probe。
当进入probe函数后,需要获取设备的资源信息,获取资源的函数有:struct resource * platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num);根据参数type所指定类型,例如IORESOURCE_MEM,来获取指定的资源。struct int platform_get_irq(struct platform_device *dev, unsigned int num);获取资源中的中断号。struct resource * platform_get_resource_byname(struct platform_device *dev, unsigned int type, char *name);根据参数name所指定的名称,来获取指定的资源。int platform_get_irq_byname(struct platform_device *dev, char *name);根据参数name所指定的名称,来获取资源中的中断号。
总结,通常情况下只要和内核本身运行依赖性不大的外围设备,相对独立的,拥有各自独自的资源(地址总线和IRQs),都可以用platform_driver实现。如:LCD,网卡、USB、UART等,都可以用platfrom_driver写,而timer,irq等小系统之内的设备则最好不用platfrom_driver机制。
所以你不懂我的选择,也可以不懂我的难过,