原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://21cnbao.blog.51cto.com/109393/337609
1.1 platform总线、设备与驱动
在Linux 2.6的设备驱动模型中,关心总线、设备和驱动这3个实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。
一个现实的Linux设备和驱动通常都需要挂接在一种总线上,对于本身依附于PCI、USB、I2 C、SPI等的设备而言,这自然不是问题,但是在嵌入式系统里面,SoC系统中集成的独立的外设控制器、挂接在SoC内存空间的外设等确不依附于此类总 线。基于这一背景,Linux发明了一种虚拟的总线,称为platform总线,相应的设备称为platform_device,而驱动成为 platform_driver。
注意,所谓的platform_device并不是与字符设备、块设备和网络设备并列的概念,而是Linux系统提供的一种附加手段,例如,在S3C6410处理器中,把内部集成的I2 C、RTC、SPI、LCD、看门狗等控制器都归纳为platform_device,而它们本身就是字符设备。platform_device结构体的定义如代码清单1所示。
代码清单1 platform_device结构体
1 struct platform_device {
2 const char * name;/* 设备名 */
3 u32 id;
4 struct device dev;
5 u32 num_resources;/* 设备所使用各类资源数量 */
6 struct resource * resource;/* 资源 */
7 };
platform_driver这个结构体中包含probe()、remove()、shutdown()、suspend()、resume()函数,通常也需要由驱动实现,如代码清单2。
代码清单2 platform_driver结构体
1 struct platform_driver {
2 int (*probe)(struct platform_device *);
3 int (*remove)(struct platform_device *);
4 void (*shutdown)(struct platform_device *);
5 int (*suspend)(struct platform_device *, pm_message_t state);
6 int (*suspend_late)(struct platform_device *, pm_message_t state);
7 int (*resume_early)(struct platform_device *);
8 int (*resume)(struct platform_device *);
9 struct pm_ext_ops *pm;
10 struct device_driver driver;
11};
系统中为platform总线定义了一个bus_type的实例platform_bus_type,其定义如代码清单15.3。
代码清单15.3 platform总线的bus_type 实例platform_bus_type
1 struct bus_type platform_bus_type = {
2 .name = "platform",
3 .dev_attrs = platform_dev_attrs,
4 .match = platform_match,
5 .uevent = platform_uevent,
6 .pm = PLATFORM_PM_OPS_PTR,
7 };
8 EXPORT_SYMBOL_GPL(platform_bus_type);
这里要重点关注其match()成员函数,正是此成员表明了platform_device和platform_driver之间如何匹配,如代码清单4所示。
代码清单4 platform_bus_type的match()成员函数
1 static int platform_match(struct device *dev, struct device_driver *drv)
2 {
3 struct platform_device *pdev;
4
5 pdev = container_of(dev, struct platform_device, dev);
6 return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
7 }
从代码清单4的第6行可以看出,匹配platform_device和platform_driver主要看二者的name字段是否相同。
对platform_device的定义通常在BSP的板文件中实现,在板文件中,将platform_device归纳为一个数组,最终通过 platform_add_devices()函数统一注册。platform_add_devices()函数可以将平台设备添加到系统中,这个函数的 原型为:
int platform_add_devices(struct platform_device **devs, int num);
该函数的第一个参数为平台设备数组的指针,第二个参数为平台设备的数量,它内部调用了platform_device_register()函数用于注册单个的平台设备。
1.2 将globalfifo作为platform设备
现在我们将前面章节的globalfifo驱动挂接到platform总线上,要完成2个工作:
1. 将globalfifo移植为platform驱动。
2. 在板文件中添加globalfifo这个platform设备。
为完成将globalfifo移植到platform驱动的工作,需要在原始的globalfifo字符设备驱动中套一层 platform_driver的外壳,如代码清单5。注意进行这一工作后,并没有改变globalfifo是字符设备的本质,只是将其挂接到了 platform总线。
代码清单5 为globalfifo添加platform_driver
1 static int __devinit globalfifo_probe(struct platform_device *pdev)
2 {
3 int ret;
4 dev_t devno = MKDEV(globalfifo_major, 0);
5
6 /* 申请设备号*/
7 if (globalfifo_major)
8 ret = register_chrdev_region(devno, 1, "globalfifo");
9 else { /* 动态申请设备号 */
10 ret = alloc_chrdev_region(&devno, 0, 1, "globalfifo");
11 globalfifo_major = MAJOR(devno);
12 }
13 if (ret < 0)
14 return ret;
15 /* 动态申请设备结构体的内存*/
16 globalfifo_devp = kmalloc(sizeof(struct globalfifo_dev), GFP_KERNEL);
17 if (!globalfifo_devp) { /*申请失败*/
18 ret = - ENOMEM;
19 goto fail_malloc;
20 }
21
22 memset(globalfifo_devp, 0, sizeof(struct globalfifo_dev));
23
24 globalfifo_setup_cdev(globalfifo_devp, 0);
25
26 init_MUTEX(&globalfifo_devp->sem); /*初始化信号量*/
27 init_waitqueue_head(&globalfifo_devp->r_wait); /*初始化读等待队列头*/
28 init_waitqueue_head(&globalfifo_devp->w_wait); /*初始化写等待队列头*/
29
30 return 0;
31
32 fail_malloc: unregister_chrdev_region(devno, 1);
33 return ret;
34 }
35
36 static int __devexit globalfifo_remove(struct platform_device *pdev)
37 {
38 cdev_del(&globalfifo_devp->cdev); /*注销cdev*/
39 kfree(globalfifo_devp); /*释放设备结构体内存*/
40 unregister_chrdev_region(MKDEV(globalfifo_major, 0), 1); /*释放设备号*/
41 return 0;
42 }
43
44 static struct platform_driver globalfifo_device_driver = {
45 .probe = globalfifo_probe,
46 .remove = __devexit_p(globalfifo_remove),
47 .driver = {
48 .name = "globalfifo",
49 .owner = THIS_MODULE,
50 }
51 };
52
53 static int __init globalfifo_init(void)
54 {
55 return platform_driver_register(&globalfifo_device_driver);
56 }
57
58 static void __exit globalfifo_exit(void)
59 {
60 platform_driver_unregister(&globalfifo_device_driver);
61 }
62
63 module_init(globalfifo_init);
64 module_exit(globalfifo_exit);
在代码清单5中,模块加载和卸载函数仅仅通过platform_driver_register()、 platform_driver_unregister()函数进行platform_driver的注册与注销,而原先注册和注销字符设备的工作已经被 移交到platform_driver的probe()和remove()成员函数中。
代码清单5未列出的部分与原始的globalfifo驱动相同,都是实现作为字符设备驱动核心的file_operations的成员函数。
为了完成在板文件中添加globalfifo这个platform设备的工作,需要在板文件(对于LDD6410而言,为arch/arm/mach-s3c6410/ mach-ldd6410.c)中添加相应的代码,如代码清单6。
代码清单6 globalfifo对应的platform_device
1 static struct platform_device globalfifo_device = {
2 .name = "globalfifo",
3 .id = -1,
4 };
对于LDD6410开发板而言,为了完成上述globalfifo_device这一platform_device的注册,只需要将其地址放入 arch/arm/mach-s3c6410/ mach-ldd6410.c中定义的ldd6410_devices数组,如:
static struct platform_device *ldd6410_devices[] __initdata = {
+ & globalfifo_device,
#ifdef CONFIG_FB_S3C_V2
&s3c_device_fb,
#endif
&s3c_device_hsmmc0,
...
}
在加载LDD6410驱动后,在sysfs中会发现如下结点:
/sys/bus/platform/devices/globalfifo/
/sys/devices/platform/globalfifo/
留意一下代码清单5的第48行和代码清单6的第2行,platform_device和platform_driver的name一致,这是二者得以匹配的前提。
1.3 platform设备资源和数据
留意一下代码清单1中platform_device结构体定义的第5~6行,描述了platform_device的资源,资源本身由resource结构体描述,其定义如代码清单7。
代码清单7 resouce结构体定义
1 struct resource {
2 resource_size_t start;
3 resource_size_t end;
4 const char *name;
5 unsigned long flags;
6 struct resource *parent, *sibling, *child;
7 };
我们通常关心start、end和flags这3个字段,分别标明资源的开始值、结束值和类型,flags可以为IORESOURCE_IO、 IORESOURCE_MEM、IORESOURCE_IRQ、IORESOURCE_DMA等。start、end的含义会随着flags而变更,如当 flags为IORESOURCE_MEM时,start、end分别表示该platform_device占据的内存的开始地址和结束地址;当 flags为IORESOURCE_IRQ时,start、end分别表示该platform_device使用的中断号的开始值和结束值,如果只使用了 1个中断号,开始和结束值相同。对于同种类型的资源而言,可以有多份,譬如说某设备占据了2个内存区域,则可以定义2个IORESOURCE_MEM资 源。
对resource的定义也通常在BSP的板文件中进行,而在具体的设备驱动中透过platform_get_resource()这样的API来获取,此API的原型为:
struct resource *platform_get_resource(struct platform_device *, unsigned int, unsigned int);
譬如在LDD6410开发板的板文件中为DM9000网卡定义了如下resouce:
static struct resource ldd6410_dm9000_resource[] = {
[0] = {
.start = 0x18000000,
.end = 0x18000000 + 3,
.flags = IORESOURCE_MEM
},
[1] = {
.start = 0x18000000 + 0x4,
.end = 0x18000000 + 0x7,
.flags = IORESOURCE_MEM
},
[2] = {
.start = IRQ_EINT(7),
.end = IRQ_EINT(7),
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL,
}
};
在DM9000网卡的驱动中则是通过如下办法拿到这3份资源:
db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
对于IRQ而言,platform_get_resource()还有一个进行了封装的变体platform_get_irq(),其原型为:
int platform_get_irq(struct platform_device *dev, unsigned int num);
它实际上调用了“platform_get_resource(dev, IORESOURCE_IRQ, num);”。
设备除了可以在BSP中定义资源以外,还可以附加一些数据信息,因为对设备的硬件描述除了中断、内存、DMA通道以外,可能还会有一些配置信息,而 这些配置信息也依赖于板,不适宜直接放置在设备驱动本身,因此,platform也提供了platform_data的支持。platform_data 的形式是自定义的,如对于DM9000网卡而言,platform_data为一个dm9000_plat_data结构体,我们就可以将MAC地址、总 线宽度、有无EEPROM信息放入platform_data:
static struct dm9000_plat_data ldd6410_dm9000_platdata = {
.flags = DM9000_PLATF_16BITONLY | DM9000_PLATF_NO_EEPROM,
.dev_addr = { 0x0, 0x16, 0xd4, 0x9f, 0xed, 0xa4 },
};
static struct platform_device ldd6410_dm9000 = {
.name = "dm9000",
.id = 0,
.num_resources = ARRAY_SIZE(ldd6410_dm9000_resource),
.resource = ldd6410_dm9000_resource,
.dev = {
.platform_data = &ldd6410_dm9000_platdata,
}
};
而在DM9000网卡的驱动中,通过如下方式就拿到了platform_data:
struct dm9000_plat_data *pdata = pdev->dev.platform_data;
其中,pdev为platform_device的指针。
由以上分析可知,设备驱动中引入platform的概念至少有如下2大好处:
1. 使得设备被挂接在一个总线上,因此,符合Linux 2.6的设备模型。其结果是,配套的sysfs结点、设备电源管理都成为可能。
2. 隔离BSP和驱动。在BSP中定义platform设备和设备使用的资源、设备的具体配置信息,而在驱动中,只需要通过通用API去获取资源和数据,做到了板相关代码和驱动代码的分离,使得驱动具有更好的可扩展性和跨平台性。

本文出自 “宋宝华的博客” 博客,请务必保留此出处http://21cnbao.blog.51cto.com/109393/337609

platform设备驱动全透析相关推荐

  1. platform设备驱动全透析(转自宋宝华老师)

    platform设备驱动全透析(转自宋宝华老师) 2013-04-12 09:58 384人阅读 评论(0) 收藏 举报 分类: linux kernel(22) 1.1 platform总线.设备与 ...

  2. platform 设备驱动实验

    目录 Linux 驱动的分离与分层 驱动的分隔与分离 驱动的分层 platform 平台驱动模型简介 platform 总线 platform 驱动 platform 设备 硬件原理图分析 试验程序编 ...

  3. Linux 设备驱动开发 —— platform 设备驱动

    一.platform总线.设备与驱动         在Linux 2.6 的设备驱动模型中,关心总线.设备和驱动3个实体,总线将设备和驱动绑定.在系统每注册一个设备的时候,会寻找与之匹配的驱动:相反 ...

  4. Linux 设备驱动开发 —— 设备树在platform设备驱动中的使用

    关与设备树的概念,我们在Exynos4412 内核移植(六)-- 设备树解析 里面已经学习过,下面看一下设备树在设备驱动开发中起到的作用 Device Tree是一种描述硬件的数据结构,设备树源(De ...

  5. Linux驱动之platform设备驱动

    当我们在一块开发板上写好了驱动,但换一块不同芯片的开发板,我们就需要重新写一个驱动.其中主要是硬件连接也就是接口发生了改变,而软件框架几乎不用通用的.所以为了更加方便地移植,能够仅修改很小的内容就达到 ...

  6. Linux驱动-platform设备驱动

    #1 platform设备驱动模型数据结构 一般总线驱动设备数据结构 驱动 总线 设备 device_driver bus_type device 一般总线驱动设备数据结构 驱动 总线 设备 plat ...

  7. Linux platform 设备驱动实验-基于正点原子IMX6ULL开发板

    我们以前的设备驱动都非常的简单,都是对IO进行最简单的读写操作.像I2C. SPI.LCD 这些复杂外设的驱动就不能这么去写了,Linux 系统要考虑到驱动的可重用性,因此提出了驱动的分离与分层这样的 ...

  8. linux设备驱动归纳总结(九):1.platform设备驱动

    http://blog.chinaunix.net/uid-25014876-id-111745.html 这一节可以理解是第八章的延伸,从这节开始介绍platform设备驱动. 一.什么是paltf ...

  9. Linux 设备驱动开发 —— platform设备驱动应用实例解析

    前面我们已经学习了platform设备的理论知识Linux 设备驱动开发 -- platform 设备驱动 ,下面将通过一个实例来深入我们的学习. 一.platform 驱动的工作过程 platfor ...

最新文章

  1. Opencv进行人脸检测(改进)
  2. SSO 单点登录会话管理
  3. hdu 1166 敌兵布阵 (线段树)
  4. SAP特性,物料特性,批次特性建立的BAPI函數
  5. 随机手机号码_骗妹子手机号码,还记得iPhone自带计算器的小魔术吗?,看教程...
  6. 表格和表单的结合示例
  7. linux下面的挂载点讲解
  8. windows编译libevent
  9. 解码(四):avcodec_send_packet和avcodec_receive_frame函数讲解
  10. Activity的LaunchMode情景思考
  11. 彪悍榴莲姐再袭 这次一口气吃掉10斤
  12. MAC 微信表情包 导出
  13. python-爬取贴吧的时候表情的处理。
  14. [译]关于Android图形系统的一些事实真相
  15. Angular-路由设置
  16. 结合NBA主题,Niantic再寻LBS AR游戏破局之道
  17. 2018-3-25至2018-8-9的日语笔记
  18. 2019.5.5 qq音乐数据源m4a抓取(新)
  19. 树莓派基础实验18:声音传感器实验
  20. Vue之引用第三方JS插件,CKPlayer使用

热门文章

  1. 弹跳机器人 桌游_有哪些适合 10-15 人的桌游值得推荐?
  2. IDEA HTTP状态 404 - 未找到 请求的资源[/]不可用
  3. 042_Popconfirm气泡确认框
  4. java.lang包怎么用_java.lang.io包的使用
  5. 员工培训案例分析答案_在职员工培训管理办法案例
  6. 将源码包打包成RPM包
  7. labview将产生数据存入数组 并保存成Excel
  8. matlab中龙贝格积分,龙贝格积分-matlab通用程序
  9. 大学物理规范作业25稳恒磁场_山东一地出台规定:严禁家长代批作业,违反规定将被一票否决...
  10. 设计一个名为complex的类来表示复数_天线波束设计