您的位置:首页 > 运维架构 > Linux

linux Device Drivers platform

2015-07-22 11:08 429 查看

何谓platform bus

在Linux3.4的设备驱动模型中,关心总线、设备和驱动这3个实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。一个现实的Linux设备和驱动通常都需要挂接在一种总线上,对于本身依附于PCI、USB、I2 C、SPI等的设备而言,这自然不是问题,但是在嵌入式系统里面,SoC系统中集成的独立的外设控制器、挂接在SoC内存空间的外设等确不依附于此类总线。基于这一背景,Linux发明了一种虚拟的总线,称为platform总线,相应的设备称为platform_device,而驱动成为platform_driver。
Platform bus 是一种虚拟的总线,而不是物理上存在的,为了方便内核对设备的管理而出现的。

platform device与platform driver

Platform_device和Platform_driver是从Linux 2.6起引入了一套新的驱动管理和注册机制,在现在的Linux中大部分的设备驱动,都可以使用这套机制, 设备用Platform_device表示,驱动用Platform_driver进行注册。 platform driver机制和传统的device driver 机制(通过driver_register函数进行注册)相比,一个十分明显的优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platform
device提供的标准接口进行申请并使用。这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性。这里的platform driver并不是与字符设备、块设备和网络设备并列的概念,而是linux系统提供的一种附加手段,其本身可能即是字符设备。Platform device定义如下:

struct platform_device {
const char * name; //定义平台设备的名称,此处设备的命名应和相应驱动程序命名一致
int	 id;
struct device dev;
u32  num_resources;
struct resource * resource; //定义平台设备的资源
};

Platform driver结构体当中,包含了probe()、remove()、shutdown()、suspend()、resume()等函数,需要由驱动实现,定义如下:

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 (*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 device是通过platform bus联系起来的,在Platform drive注册的时候,会遍历platform bus下所有的device,将当前driver和总线上的device进行match,如果匹配成功,则将device和driver绑定起来调用。Platform bus定义如下:

struct bus_type platform_bus_type = {
.name      = "platform",
.dev_attrs = platform_dev_attrs,
.match     = platform_match,
.uevent    = platform_uevent,
.pm        = PLATFORM_PM_OPS_PTR,
};

我们需要关注其Match成员函数,正是此函数确定了Platform driver和platform device之间如果匹配的,代码如下:

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驱动开发的时候,需要注意 platform_device和platform_driver中的name属性要保持一致。在platform device结构体中,描述了platform_device的资源,由resource结构体来描述,定义如下:

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;
};

这个结构表示设备所拥有的资源,即I/O端口、I/O映射内存、中断及DMA等。这里的地址指的是物理地址。在设备驱动当中,我们可以通过platform_get_resource()这样的API来获取设备资源,原型为:

Struct resource *platform_get_resource(struct platform_device*, unsigned int,unsigned int);
int platofrm_get_irq(struct platform_device*, unsigned int num);

哪些适用于plarform驱动

platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过 platform device提供的标准接口进行申请并使用。这样提高了驱动和资源管理的独立性,这样拥有更好的可移植性。platform机制的本身使用并不复杂,由两部分组成:platform_device和platfrom_driver。Platform driver通过platform bus获取platform_device。通常情况下只要和内核本身运行依赖性不大的外围设备,相对独立的,拥有各自独立的资源(地址总线和IRQs),都可以用
platform_driver来管理,而timer,irq等小系统之内的设备则最好不用platfrom_driver机制。 platform_device最大的特定是CPU直接寻址设备的寄存器空间,即使对于其他总线设备,设备本身的寄存器无法通过CPU总线访问,但总线的controller仍然需要通过platform bus来管理。总之,platfrom_driver的根本目的是为了统一管理系统的外设资源,为驱动程序提供统一的接口来访问系统资源,将驱动和资源分离,提高程序的可移植性。

基于platform总线的驱动开发流程

定义platform_device

定义platform_device,并集中定义设备资源。示例如下

struct platform_device disp_device =
{
.name           = "disp",
.id		        = -1,
.num_resources  = ARRAY_SIZE(disp_resource),
.resource	    = disp_resource,
.dev            = {}
};
static struct resource disp_resource[DISP_IO_NUM] =
{
[DISP_IO_SCALER0] = {
.start = 0x01e00000,
.end   = 0x01e0077f,
.flags = IORESOURCE_MEM,
},
[DISP_IO_SCALER1] = {
.start = 0x01e20000,
.end   = 0x01e2077f,
.flags = IORESOURCE_MEM,
},
//…
};

注册platform_device

定义好了platform_device,调用platform_device_register()向系统注册,连同资源统一注册进内核,原型如下:

int platform_device_register(struct platform_device *pdev);

在此函数中,会调用void device_initialize(struct device *dev),初始化platform_device的device结构,然后调用int platform_device_add(struct platform_device *pdev),将设备所有资源注册进系统,并生成设备结点。

定义实现platform_driver

定义platform_driver以驱动该设备,示例如下:

static struct platform_driver disp_driver =
{
.probe		= disp_probe,
.remove		= disp_remove,
.suspend    = disp_suspend,
.resume    = disp_resume,
.shutdown   = disp_shutdown,
.driver		=
{
.name	= "disp",
.owner	= THIS_MODULE,
},
};

实现platform_driver中的probe、remove、suspend、resume等函数。注意.name与设备的.name一致,不然无法匹配成功。

注册platform_driver

调用platform_driver_register()注册platform_driver,函数代码如下:

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_bus上的所有设备,调用platform_match函数,发现匹配设备,即调用该驱动的probe函数。

probe函数

Probe即探测函数,是platform设备驱动match之后执行的第一个函数,到这里,说明platform设备驱动已经注册加载了。一般在Probe函数中去获取platform设备注册的资源及进行设备驱动的初始化。

操作设备

进行了platform_device_register 和platform_driver_register后,驱动的相应信息就出现在sys目录的相应文件夹下,然后,我们该如何调用设备呢?怎么对设备进行打开读写等操作呢? Platform总线只是为了方便系统管理设备而已(比如统一的sysfs结点、电源管理等),想要与用户空间交互,如读写还需要利用file_operations,即需要创建一个基本类型的设备,如字符设备。通过字符设备与用户空间进行交互,同时系统可以通过platform bus对挂载在其上的该设备进行管理。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: