Platform设备驱动
2015-06-15 20:27
375 查看
Platform设备驱动
没有分析platform_device端的注册过程
1、概述:
在Linux 2.6内核的设备驱动模型中,有总线(bus)、设备(device)和驱动(driver)3个实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;同样的,在系统每注册一个驱动时,会寻找与之匹配的设备,而匹配由总线完成。
一个现实的Linux设备和驱动通常都需要挂接在一种总线上,比如:PCI、USB、I²C、SPI等的设备。但是SOC的片上资源:比如片上集成的外设控制器(如LCD控制器)、Watchdog、RTC、挂接在内存空间的外设等却不依赖与此类总线。基于这一背景,从2.6内核起,引入了一套虚拟总线Platform总线,相应的设备称为platform_device,驱动称为platform_driver。
该虚拟机制与传统的总线机制相比,优先在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时,通过platform_device提供的标准接口进行申请并使用。
1、平台设备platform_device
1)platform_device涉及的主要结构体
平台设备注册时,需要实现platform_device结构体。
struct platform_device { //include\linux\Platform_device.h
const char * name;
int id;
struct device dev; //设备结构体
u32 num_resources; //资源数目
struct resource * resource; //资源结构体
const struct platform_device_id *id_entry;
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;
};
该结构体里面需要定义具体设备占用的硬件资源(如:地址空间、中断号等)。
struct resource { //\include\linux\Ioport.h
resource_size_t start; //资源的开始地址
resource_size_t end; //资源的结束地址
const char *name; //资源的名字
unsigned long flags; //资源类型
struct resource *parent, *sibling, *child;
};
Resource结构体中的flags表示资源类型,可以为IORESOURCE_IO(IO类资源)、IORESOURCE_MEM(内存资源)、IORESOURCE_IRQ(中断资源)、IORESOURCE_DMA(DMA资源)。
2)主要开发步骤
(1)初始化platform_device结构体。
(2)注册该platform_device。
3)典型例子分析
platform_device设备是板文件相关的。以\arch\arm\mach-s3c24xx\Mach-mini2440.c文件为例进行说明。
//dm9000以太网设备platform_device结构体初始化。
static struct platform_device mini2440_device_eth = {
.name = "dm9000",
.id = -1,
.num_resources = ARRAY_SIZE(mini2440_dm9k_resource), //资源数目=3
.resource = mini2440_dm9k_resource, //资源结构体
.dev = { //设备结构体
.platform_data = &mini2440_dm9k_pdata, },//附加的信息
};
设备除了定义资源外,还附加了一些数据信息,因为对设备的硬件描述除了中断、内存、DMA通道以外,可能还会有一些配置信息,而这些信息也依赖于板。因此,platform也提供了platform_data的支持,它的形式是自定义的。如对于DM9000网卡来说,platform_data为一个dm9000_plat_data结构体,我们可以将MAC地址、总线宽度、板上有无EEPROM信息放入platform_data。
(1)资源结构体结构体中定义了3个资源
/* DM9000AEP 10/100 ethernet controller */
static struct resource mini2440_dm9k_resource[] = {
[0] = { //第一个资源
.start = MACH_MINI2440_DM9K_BASE, //内存的开始地址
.end = MACH_MINI2440_DM9K_BASE + 3, //内的结束地址
.flags = IORESOURCE_MEM //内存类资源
},
[1] = { //第二个资源
.start = MACH_MINI2440_DM9K_BASE + 4, //内存的开始地址
.end = MACH_MINI2440_DM9K_BASE + 7, //内的结束地址
.flags = IORESOURCE_MEM //内存类资源
},
[2] = {
.start = IRQ_EINT7, //中断的开始值
.end = IRQ_EINT7, //中断的结束值
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE, //中断类资源
}
};
(2)设置DM9000附加描述信息:16位、没有EEPROM
static struct dm9000_plat_data mini2440_dm9k_pdata = {
.flags = (DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM),
};
2、platform_driver
1)结构体定义
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 (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
};
该结构体需要驱动编写者自己完成。
2)典型例子分析
static struct platform_driver dm9000_driver = {
.driver = {
.name = "dm9000", //名字
.owner = THIS_MODULE,
.pm = &dm9000_drv_pm_ops,
},
.probe = dm9000_probe, //probe函数
.remove = __devexit_p(dm9000_drv_remove), //remove函数
};
此外还有一个id_table结构体,用于更宽泛的匹配(稍后涉及)。
3、platform总线
struct bus_type platform_bus_type = { //drivers\base\Platform.c
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
重点关注match()成员函数,它确定了platform_device和platform_driver之间是如何匹配的。该函数会顺序匹配of_match_table、id_table、name字段,其中之一匹配成功就返回。DM9000网卡中name字段会匹配成功。
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
/* Attempt an OF style match first */
if (of_driver_match_device(dev, drv)) //匹配of_match_table
return 1;
/* Then try to match against the id table */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL; //匹配id_table
/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0); //匹配名字
}
4、注册驱动顺序
驱动注册时的函数调用过程:
Platform_driver_register()-->driver_register()-->bus_add_driver()-->driver_attach()-->bus_for_each_dev()。将每个挂在虚拟platform bus的设备和driver匹配,如果匹配成功则调用platform_driver->probe(platform_device)。
5、总结
由以上分析可知,设备驱动中引入platform的概念的优势有:
(1)使得设备被挂接在一个总线上。其结果是配套的sysfs节点、设备电源管理都成为可能。
(2)隔离BSP和驱动。在BSP中定义platform_device设备和设备使用的资源、具体配置信息。而在驱动中,只需通过通用的API去获取资源和数据,做到开发板相关代码和驱动代码的分离,使得驱动具有更好的可扩展性和跨平台型。
可参考《Linux设备驱动开发详解》(第二版)。
没有分析platform_device端的注册过程
1、概述:
在Linux 2.6内核的设备驱动模型中,有总线(bus)、设备(device)和驱动(driver)3个实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;同样的,在系统每注册一个驱动时,会寻找与之匹配的设备,而匹配由总线完成。
一个现实的Linux设备和驱动通常都需要挂接在一种总线上,比如:PCI、USB、I²C、SPI等的设备。但是SOC的片上资源:比如片上集成的外设控制器(如LCD控制器)、Watchdog、RTC、挂接在内存空间的外设等却不依赖与此类总线。基于这一背景,从2.6内核起,引入了一套虚拟总线Platform总线,相应的设备称为platform_device,驱动称为platform_driver。
该虚拟机制与传统的总线机制相比,优先在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时,通过platform_device提供的标准接口进行申请并使用。
1、平台设备platform_device
1)platform_device涉及的主要结构体
平台设备注册时,需要实现platform_device结构体。
struct platform_device { //include\linux\Platform_device.h
const char * name;
int id;
struct device dev; //设备结构体
u32 num_resources; //资源数目
struct resource * resource; //资源结构体
const struct platform_device_id *id_entry;
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;
};
该结构体里面需要定义具体设备占用的硬件资源(如:地址空间、中断号等)。
struct resource { //\include\linux\Ioport.h
resource_size_t start; //资源的开始地址
resource_size_t end; //资源的结束地址
const char *name; //资源的名字
unsigned long flags; //资源类型
struct resource *parent, *sibling, *child;
};
Resource结构体中的flags表示资源类型,可以为IORESOURCE_IO(IO类资源)、IORESOURCE_MEM(内存资源)、IORESOURCE_IRQ(中断资源)、IORESOURCE_DMA(DMA资源)。
2)主要开发步骤
(1)初始化platform_device结构体。
(2)注册该platform_device。
3)典型例子分析
platform_device设备是板文件相关的。以\arch\arm\mach-s3c24xx\Mach-mini2440.c文件为例进行说明。
//dm9000以太网设备platform_device结构体初始化。
static struct platform_device mini2440_device_eth = {
.name = "dm9000",
.id = -1,
.num_resources = ARRAY_SIZE(mini2440_dm9k_resource), //资源数目=3
.resource = mini2440_dm9k_resource, //资源结构体
.dev = { //设备结构体
.platform_data = &mini2440_dm9k_pdata, },//附加的信息
};
设备除了定义资源外,还附加了一些数据信息,因为对设备的硬件描述除了中断、内存、DMA通道以外,可能还会有一些配置信息,而这些信息也依赖于板。因此,platform也提供了platform_data的支持,它的形式是自定义的。如对于DM9000网卡来说,platform_data为一个dm9000_plat_data结构体,我们可以将MAC地址、总线宽度、板上有无EEPROM信息放入platform_data。
(1)资源结构体结构体中定义了3个资源
/* DM9000AEP 10/100 ethernet controller */
static struct resource mini2440_dm9k_resource[] = {
[0] = { //第一个资源
.start = MACH_MINI2440_DM9K_BASE, //内存的开始地址
.end = MACH_MINI2440_DM9K_BASE + 3, //内的结束地址
.flags = IORESOURCE_MEM //内存类资源
},
[1] = { //第二个资源
.start = MACH_MINI2440_DM9K_BASE + 4, //内存的开始地址
.end = MACH_MINI2440_DM9K_BASE + 7, //内的结束地址
.flags = IORESOURCE_MEM //内存类资源
},
[2] = {
.start = IRQ_EINT7, //中断的开始值
.end = IRQ_EINT7, //中断的结束值
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE, //中断类资源
}
};
(2)设置DM9000附加描述信息:16位、没有EEPROM
static struct dm9000_plat_data mini2440_dm9k_pdata = {
.flags = (DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM),
};
2、platform_driver
1)结构体定义
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 (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
};
该结构体需要驱动编写者自己完成。
2)典型例子分析
static struct platform_driver dm9000_driver = {
.driver = {
.name = "dm9000", //名字
.owner = THIS_MODULE,
.pm = &dm9000_drv_pm_ops,
},
.probe = dm9000_probe, //probe函数
.remove = __devexit_p(dm9000_drv_remove), //remove函数
};
此外还有一个id_table结构体,用于更宽泛的匹配(稍后涉及)。
3、platform总线
struct bus_type platform_bus_type = { //drivers\base\Platform.c
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
重点关注match()成员函数,它确定了platform_device和platform_driver之间是如何匹配的。该函数会顺序匹配of_match_table、id_table、name字段,其中之一匹配成功就返回。DM9000网卡中name字段会匹配成功。
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
/* Attempt an OF style match first */
if (of_driver_match_device(dev, drv)) //匹配of_match_table
return 1;
/* Then try to match against the id table */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL; //匹配id_table
/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0); //匹配名字
}
4、注册驱动顺序
驱动注册时的函数调用过程:
Platform_driver_register()-->driver_register()-->bus_add_driver()-->driver_attach()-->bus_for_each_dev()。将每个挂在虚拟platform bus的设备和driver匹配,如果匹配成功则调用platform_driver->probe(platform_device)。
5、总结
由以上分析可知,设备驱动中引入platform的概念的优势有:
(1)使得设备被挂接在一个总线上。其结果是配套的sysfs节点、设备电源管理都成为可能。
(2)隔离BSP和驱动。在BSP中定义platform_device设备和设备使用的资源、具体配置信息。而在驱动中,只需通过通用的API去获取资源和数据,做到开发板相关代码和驱动代码的分离,使得驱动具有更好的可扩展性和跨平台型。
可参考《Linux设备驱动开发详解》(第二版)。
相关文章推荐
- JS显示动态显示当前时间代码
- 黑马程序员——Java基础之函数与数组
- Picture控件显示jpg
- 十、迭代器和组合模式
- 【Scala】高阶函数和柯里化
- 新手学习编程的最佳方式是什么?
- Android: 一个两点触控的案例
- 《编程珠玑》阅读小记(9) — 取样问题
- oracle中rowid解决数据重复的问题
- WebX学习二——URL请求流程分析
- 【Scala】头等函数与函数即对象
- poj2509---抽k根烟就换一支,求能抽烟的总数
- 《编程珠玑》阅读小记(9) — 取样问题
- Android布局优化之Merge Include ViewStub使用与源码分析
- 文本编辑器vi和vim(2)
- mac 下查找文件以及文件夹
- 【Scala】单例对象与伴生对象
- 大数据时代的技术hive:hive的数据类型和数据模型
- STL用法
- Android的ADB工具使用