本文转载自:http://blog.chinaunix.net/uid-25014876-id-111745.html

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

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

这一节可以理解是第八章的延伸,从这节开始介绍platform设备驱动。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

一、什么是paltform总线

一个现实的linux设备和驱动通常都需要挂接在一种总线上,比较常见的总线有USB、PCI总线等。但是,在嵌入式系统里面,SoC系统中集成的独立的外设控制器、挂接在SoC内存空间的外设却不依附与此类总线。基于这样的背景下,2.6内核加入了platform虚拟总线。platform机制将设备本身的资源注册进内核,有内核统一管理,在驱动程序使用这些资源时使用统一的接口,这样提高了程序的可移植性。

如果简单的说,就像我在第八章第三节模拟的usb总线一样(源代码路径:8th_devModule_3/2nd),platform总线对加入到该总线的设备和驱动分别封装了两个结构体——platform_device和platform_driver。并且提供了对应的注册函数。当然,前提是要包含头文件。

来个图:

由上面两个的关系我们可以看出来,需要在platform总线上注册设备和驱动,只要定义指定的结构体后调用platform给出的注册函数就可以了。

下面就介绍一下platform总线、设备和驱动。

、platform总线:

和我之前虚拟的usb总线一样,linux在系统启动时就注册了platform总线,看内核代码:

/*drivers/base/platform.c*/

604 static int platform_match(struct device *dev, struct device_driver *drv)

605 {

606     struct platform_device *pdev;

607

608     pdev = container_of(dev, struct platform_device, dev);

609     return (strcmp(pdev->name, drv->name) == 0); //配对函数检验名字是否一致

610 }

。。。。。

949 struct bus_type platform_bus_type = {

950     .name = "platform", //定义了总线名字为platform,总线注册后新建目录sys/bus/platform

951     .dev_attrs = platform_dev_attrs,

952     .match = platform_match, //指定配对函数

953     .uevent = platform_uevent,

954     .pm = PLATFORM_PM_OPS_PTR,

955 };

可以看到,和我的usb虚拟总线一样,总线中定义了成员名字和match函数,当有总线或者设备注册到platform总线时,内核自动调用match函数,判断设备和驱动的name是否一致。

、platform设备:

同样的,先看一下platform设备对应的结构体paltform_device:

/*linux/platform_device.h*/

16 struct platform_device {

17     const char * name; //设备的名字,这将代替device->dev_id,用作sys/device下显示的目录名

18     int id; //设备id,用于给插入给该总线并且具有相同name的设备编号,如果只有一个设备的话填-1。

19     struct device dev; //结构体中内嵌的device结构体。

20     u32 num_resources; //资源数。

21     struct resource * resource; //用于存放资源的数组。

22 };

上面的结构体中先不介绍id、num_resources和resource。可以看到,platform_device的封装就是指定了一个目录的名字name,并且内嵌device。

platform_device的注册和注销使用以下函数:

/*drivers/base/platform.c*/

322 int platform_device_register(struct platform_device *pdev) //同样的,需要判断返回值

。。。

337 void platform_device_unregister(struct platform_device *pdev)

注册后,同样会在/sys/device/目录下创建一个以name命名的目录,并且创建软连接到/sys/bus/platform/device下。

、platform驱动:

先看一下platform驱动对应的结构体paltform_driver:

/*linux/platform_device.h*/

50 struct platform_driver {

51     int (*probe)(struct platform_device *);

52     int (*remove)(struct platform_device *);

53     void (*shutdown)(struct platform_device *);

54     int (*suspend)(struct platform_device *, pm_message_t state);

55     int (*suspend_late)(struct platform_device *, pm_message_t state);

56     int (*resume_early)(struct platform_device *);

57     int (*resume)(struct platform_device *);

58     struct device_driver driver;

59 };

可以看到,platform_driver结构体内嵌了device_driver,并且实现了prob、remove等操作。其实,当内核需要调用probe函数时,它会调用driver->probe,在dricer->probe中再调用platform_driver->probe。如果想了解清楚的话建议查看内核源代码。

platform_driver的注册和注销使用以下函数:

/*drivers/base/platform.c*/

492 int platform_driver_register(struct platform_driver *drv)

。。。。。

513 void platform_driver_unregister(struct platform_driver *drv)

注册成功后内核会在/sys/bus/platform/driver/目录下创建一个名字为driver->name的目录。

介绍完后,那我就根据第八章第三节(linux设备驱动归纳总结(八):3.设备管理的分层与面向对象思想)的源程序(8th_devModule_3/2nd),将我想象出来的usb鼠标设备和驱动添加到platform总线上:

/*9th_platform_1/1st/device.c*/

4 #include

5

6 void usb_dev_release(struct device *dev)

7 {

8     printk(" release\n");

9 }

10 struct platform_device mouse_dev = {

11     .name = "plat_usb_mouse", //将以这个名字创建目录

12     .dev = {

13         .bus_id = "usb_mouse", //不会用这个名字创建目录了,这里不设置bus_id也行的。

14         .release = usb_dev_release,

15     },

16 };

17

18 static int __init usb_device_init(void)

19 {

20     int ret;

21

22     ret = platform_device_register(&mouse_dev);

23     if(ret){

24         printk("device register failed!\n");

25         return ret;

26     }

27

28     printk("usb device init\n");

29     return 0;

30 }

31

32 static void __exit usb_device_exit(void)

33 {

34     platform_device_unregister(&mouse_dev);

35     printk("usb device bye!\n");

36 }

一看就很简单,将之前usb结构体和注册函数更改为platform类型就可以了。dirver.c也是一样:

/*9th_platform_1/1st/driver.c */

25 struct platform_driver mouse_drv = {

26     .probe = usb_driver_probe,

27     .remove = usb_driver_remove,

28     .driver = {

29         .name = "plat_usb_mouse", //在/sys/中的驱动目录名字

30     },

31 };

32

33 static int __init usb_driver_init(void)

34 {

35     int ret;

36     /*驱动注册,注册成功后在/sys/platform/usb/driver目录下创建目录

37     * plat_usb_mouse*/

38     ret = platform_driver_register(&mouse_drv);

39     if(ret){

40         printk("driver register failed!\n");

41         return ret;

42     }

43     printk("usb driver init\n");

44     return 0;

45 }

46

47 static void __exit usb_driver_exit(void)

48 {

49     platform_driver_unregister(&mouse_drv);

50     printk("usb driver bye!\n");

51 }

由上面的程序看到,设备和驱动都以”plat_usb_mouse”命名,这样的话match函数也就能配对成功。

看效果:

[root: 1st]# insmod device.ko

usb device init

[root: 1st]# insmod driver.ko

init usb mouse

usb driver init

[root: 1st]# lsmod

driver 1604 0 - Live 0xbf006000

device 1584 0 - Live 0xbf000000

[root: 1st]# cd /

[root: /]# find -name "*usb_mouse*"

./sys/devices/platform/plat_usb_mouse.0

./sys/bus/platform/devices/plat_usb_mouse.0

./sys/bus/platform/drivers/plat_usb_mouse

./sys/bus/platform/drivers/plat_usb_mouse/plat_usb_mouse.0

当我查找usb_mouse时出现了四个目录,但是为什么后面有个“0”?这个0代表设备的编号,是由paltform_device->id指定的。我的程序没有设备,所以默认为0。如果你不想的你的目录名字没有后缀,那你就设置platform_device->id = -1;

platform_device->id = 3的效果:

[root: /]# find -name "*usb_mouse*"

./sys/devices/platform/plat_usb_mouse.3

./sys/bus/platform/devices/plat_usb_mouse.3

./sys/bus/platform/drivers/plat_usb_mouse

./sys/bus/platform/drivers/plat_usb_mouse/plat_usb_mouse.3

platform_device->id = -1的效果:

[root: /]# find -name "*usb_mouse*"

./sys/devices/platform/plat_usb_mouse

./sys/bus/platform/devices/plat_usb_mouse

./sys/bus/platform/drivers/plat_usb_mouse

./sys/bus/platform/drivers/plat_usb_mouse/plat_usb_mouse

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

二、platform设备的资源和数据

上面讲的usb鼠标都是假的,接下来简单实现一个led驱动,实现的功能很简单,加载后灯亮,卸载后灯灭,我的led灯对应管脚GPE12。顺便介绍一些platform_device中的resource和platform_data。

再看一下platform_device:

16 struct platform_device {

17     const char * name;

18     int id;

19     struct device dev;

20     u32 num_resources;

21     struct resource * resource;

22 };

前三个已经介绍了,现在来介绍一下最后两个。

resource是一个指向platform资源数组的指针,该数组中有num_resource个资源,看一下资源结构体:

/*linux/ioport.h"*/

18 struct resource {

19     resource_size_t start;

20     resource_size_t end;

21     const char *name;

22     unsigned long flags;

23     struct resource *parent, *sibling, *child;

24 };

常用的就是红色标记的三个,分别是资源的开始值,结束值和类型。

常见的flags有IORESOURCE_MEM和IORESOURCE_IRQ。其他的可以自己查看include/linux/ioport.h

如果fiags为IORESOURCE_MEM,start和end分别是该设备的连续的开始和结束地址,如果不连续你可以定义两个或者更多的资源结构体。

如果flags为IORESOURCE_IRQ,start和end分别是该设备连续的开始和结束的连续中断号,如果不连续可以分开定义。

当然,如果地址或者中断只有一个,你可以将start和end定义成一样。

在device.c定义了led的资源:

/*9th_platform_1/2nd/device.c*/

10 struct resource s3c_led_res[1] = {

11     [0] = {

12         .start = 0x56000000,

13         .end = 0x560000ff,

14         .flags = IORESOURCE_MEM,

15     },

16 };

17

18 struct platform_device s3c_led_dev = {

19     .name = "plat_led",

20     .id = -1,

21     .dev = {

22         .release = led_dev_release,

23     },

24     .num_resources = ARRAY_SIZE(s3c_led_res), //platform资源的数量,为1

25     .resource = s3c_led_res,

26 };

同时,还要修改一下driver.c中的的probe和remove,probe函数中点亮led,remove灭掉led。

/*9th_platform_1/2nd/driver.c*/

9 struct _plat_led_t {

10     unsigned long phys, virt;

11     unsigned long gpecon, gpedat, gpeup;

12     unsigned long reg;

13 };

14

15 struct _plat_led_t pled;

16

17 int led_driver_probe(struct platform_device *pdev)

18 {

19     pled.phys = pdev->resource[0].start;

20     pled.virt = ioremap(pled.phys, SZ_4K);

21     pled.gpecon = pled.virt + 0x40;

22     pled.gpedat = pled.virt + 0x44;

23     pled.gpeup = pled.virt + 0x48;

24

25     //config

26     pled.reg = ioread32(pled.gpecon);

27     pled.reg &= ~(3 << 24);

28     pled.reg |= (1 << 24);

29     iowrite32(pled.reg, pled.gpecon);

30

31     //up

32     pled.reg = ioread32(pled.gpeup);

33     pled.reg |= (1 << 12);

34     iowrite32(pled.reg, pled.gpeup);

35

36     //dat

37     pled.reg = ioread32(pled.gpedat);

38     pled.reg &= ~(1 << 12);

39     iowrite32(pled.reg, pled.gpedat);

40

41     printk("led on\n");

42     return 0;

43 }

上面的probe只要看红色标记就可以了,在platform_device的资源中获取资源的start,而其他的都是之前介绍过的led操作。

45 int led_driver_remove(struct platform_device *pdev)

46 {

47     pled.reg = ioread32(pled.gpedat);

48     pled.reg |= (1 << 12);

49     iowrite32(pled.reg, pled.gpedat);

50

51     printk("led off\n");

52     return 0;

53 }

接下来验证一下:

[root: 2nd]# insmod device.ko

led device init

[root: 2nd]# insmod driver.ko

led on

led driver init

[root: 2nd]# rmmod driver

led off

led driver bye!

[root: 2nd]# rmmod device

release

led device bye!

最后在介绍一下paltform设备的数据:

在device结构体下有一个paltform_data:

390 void *platform_data; /* Platform specific data, device

391 core doesn't touch it */

它也说明了,这是用与platform,device的代码不会使用该结构体。

这是一个void指针类型,用于存放platform的数据地址,类似字符设备时介绍的private_data。这里就不写代码了。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

三、platform设备的静态注册

在上面的程序中,设备和驱动都是通过手动加载的,但有些情况下,内核已经为设备注册到platform总线上,只需要我们注册驱动就可以了。接下来介绍一下设备的静态注册。

先看一下内核是从哪里获取静态注册的platform_device。

内核是从 arch/arm/mach-s3c2440/mach-mini2440.c文件中获取到platform_device的信息:

/*arch/arm/mach-s3c2440/mach-mini2440.c*/

250 static struct platform_device *mini2440_devices[] __initdata = {

251     &s3c_device_usb,

252     &s3c_device_rtc,

253     &s3c_device_lcd,

254     &s3c_device_wdt,

255     &s3c_device_led, //这是我新加的

257     &s3c_device_i2c0,

258     &s3c_device_iis,

259     &s3c_device_dm9k,

260     &net_device_cs8900,

261     &s3c24xx_uda134x,

262 };

可以看到,这个数组存放着所有静态注册的platform_device信息,我按照它们的格式,也添加了一个s3c_device_led结构体指针在这个数组中。接下来就要看看是在哪里定义了。

我全局搜索内核代码中含有s3c_device_wdt的文件,然后在这结构体后面依样画葫芦。

让我搜到两个相关的文件:

)arch/arm/plat-s3c24xx/devs.c

可以看到,内核在在这个文件中声明并定义s3c_device_wdt的platform_device结构体,所以,我也在这里定义的个strucr platform_device s3c_device_led:

/*arch/arm/plat-s3c24xx/devs.c*/

359 /*test by xiaobai*/

360 /* led_test */

361

362 static struct resource s3c_led_resource[] = {

363     [0] = {

364         .start = 0x56000000,

365         .end = 0x560000ff,

366         .flags = IORESOURCE_MEM,

367     }

368 };

369

370 struct platform_device s3c_device_led = {

371     .name = "plat_led",

372     .id = -1,

373     .num_resources = ARRAY_SIZE(s3c_led_resource),

374     .resource = s3c_led_resource,

375 };

376

377 EXPORT_SYMBOL(s3c_device_led);

会发现,上面的内容跟我前面卸载device.c的代码一模一样。

)第二个文件是arch/arm/plat-s3c/include/plat/devs.h:

platform_match函数是通过包含该文件后读取里面的platform_device信息来跟platform_driver匹配。所以,必须在这里加上一行代码:

/*arch/arm/plat-s3c/include/plat/devs.h*/

32 extern struct platform_device s3c_device_wdt;

33 extern struct platform_device s3c_device_led; //这是我新添加的

修改完上面的3个文件后,重新编译内核后就实现了静态注册platform设备,在内核启动时会自动注册s3c_device_led。所以,我们只需要注册platform驱动就可以了,代码在driver.c中,和上一个程序一模一样(2nd/driver.c),我就不贴出来了,可以自己看3th/driver.c。接下来看效果:

Please press Enter to activate this console.

[root: /]# find -name "*plat_led*" //开机后查找plat_led

./sys/devices/platform/plat_led //发现led设备已经被静态注册上

./sys/bus/platform/devices/plat_led

[root: /]# cd review_driver/9th_platform/9th_platform_1/3rd/

[root: 3rd]# insmod driver.ko //加载led驱动

led on //灯亮

led driver init

[root: 3rd]# rmmod driver

led off

led driver bye!

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

四、总结

这节由第八章的usb虚拟总线的延伸开始介绍platform的设备和驱动使用和注册。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

linux设备驱动归纳总结(九):1.platform总线的设备和驱动【转】的更多相关文章

  1. 【Linux开发】linux设备驱动归纳总结(九):1.platform总线的设备和驱动

    linux设备驱动归纳总结(九):1.platform总线的设备和驱动 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  2. linux设备驱动归纳总结(三):3.设备驱动面向对象思想和lseek的实现【转】

    本文转自自:http://blog.chinaunix.net/uid-25014876-id-59418.html linux设备驱动归纳总结(三):3.设备驱动面向对象思想和lseek的实现 一. ...

  3. 【Linux开发】linux设备驱动归纳总结(三):3.设备驱动面向对象思想和lseek的实现

    linux设备驱动归纳总结(三):3.设备驱动面向对象思想和lseek的实现 一.结构体struct file和struct inode 在之前写的函数,全部是定义了一些零散的全局变量.有没有办法整合 ...

  4. platform总线,设备,驱动的注册

    linux设备驱动归纳总结(九):1.platform总线的设备和驱动 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  5. 树莓派 - platform总线,设备和驱动

    以树莓派为例子,分析一下其中LED的 platform device 和 platform driver. 查看LED设备,被挂载在/sys/devices/platform下. 注意其中的drive ...

  6. linux设备驱动归纳总结(三):3面向对象思想和lseek、container_of、write、read 【转】

    linux设备驱动归纳总结(三):3.设备驱动面向对象思想和lseek的实现 转自:http://blog.chinaunix.net/uid-25014876-id-59418.html 一.结构体 ...

  7. fl2440 platform总线led字符设备驱动

    首先需要知道的是,设备跟驱动是分开的.设备通过struct device来定义,也可以自己将结构体封装到自己定义的device结构体中: 例如:struct platform_device: 在inc ...

  8. linux设备驱动归纳总结(十二):简单的数码相框【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-116926.html linux设备驱动归纳总结(十二):简单的数码相框 xxxxxxxxxxxxxx ...

  9. linux设备驱动归纳总结(十一):写个简单的看门狗驱动【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-112879.html linux设备驱动归纳总结(十一):写个简单的看门狗驱动 xxxxxxxxxxx ...

随机推荐

  1. 转载:如何运用VI编辑器进行查找替换

    使用vi编辑器编辑长文件时,常常是头昏眼花,也找不到需要更改的内容. 这时,使用查找功能尤为重要. 方法如下: 1.命令模式下输入“/字符串”,例如“/Section 3”. 2.如果查找下一个,按“ ...

  2. OPENCV3.1+VS 坑我笔记!

    1.调用findContours()函数程序崩溃. 原因: >>分析opencv源代码,跟踪测试,进入工程:opencv_imgproc 发现findContours函数 是调用 _fin ...

  3. SQLServer如何处理数据集的维度变化

    Student表有三列,分别是姓名.课程.成绩 Name Curricula Mark 张三 语文 70 李四 数学 80 王朝 英语 59 城南 马哲 70 王朝 语文 90 我想得到的效果是,列出 ...

  4. kinect for windows sdk

    https://msdn.microsoft.com/library/dn799271.aspx

  5. BeanUtils设置字段值失败问题

    package org.apache.commons.beanutils; import static org.junit.Assert.*; import java.beans.BeanInfo; ...

  6. Java基础之写文件——在通道写入过程中的缓冲区状态(BufferStateTrace)

    控制台程序,在Junk目录中将字符串“Garbage in, garbage out\n”写入到名为charData.txt的文件中. import static java.nio.file.Stan ...

  7. Protostuff序列化

    前言: Java序列化是Java技术体系当中的一个重要议题,序列化的意义在于信息的交换和存储,通常会和io.持久化.rmi技术有关(eg:一些orm框架会要求持久化的对象类型实现Serializabl ...

  8. Singlton设计模式

    单例定义: 确保一个类只有一个实例,并提供全局访问点. 适用场景: 1.) 当系统中某个类必须仅有一个实例对象,同时访问该系统的所有访问者必须访问同一个实例对象时,且该对象实例自身占用资源又不大时. ...

  9. log4net面面观之工作原理

    转自:逗逼的博客:http://itrust.cnblogs.com/archive/2005/01/25/97225.html 要知道Log4net究竟是咋干活的,咱们可以从下面这个脉络简图入手.你 ...

  10. UVa10025-The ? 1 ? 2 ? ... ? n = k problem

    分析:因为数字之间只有加减变换,所以-k和k是一样的,都可以当成整数来考虑,只要找到最小的n满足sum=n*(n+1)/2>=k:且sum和k同奇同偶即可,做法是用二分查找,然后在就近查找 因为 ...