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的设备和驱动使用和注册。

platform总线,设备,驱动的注册的更多相关文章

  1. i2c总线驱动,总线设备(适配器),从设备,从设备驱动的注册以及匹配

    常用链接 我的随笔 我的评论 我的参与 最新评论 我的标签 随笔分类 ARM裸机(13) C(8) C++(8) GNU-ARM汇编 Linux驱动(24) Linux应用编程(5) Makefile ...

  2. 总线设备驱动模型---platform篇

    总线设备驱动模型----驱动篇 http://blog.chinaunix.net/uid-27664726-id-3334923.html http://blog.chinaunix.net/uid ...

  3. Linux中总线设备驱动模型及平台设备驱动实例

    本文将简要地介绍Linux总线设备驱动模型及其实现方式,并不会过多地涉及其在内核中的具体实现,最后,本文将会以平台总线为例介绍设备和驱动程序的实现过程. 目录: 一.总线设备驱动模型总体介绍及其实现方 ...

  4. 芯灵思SinlinxA33开发板 Linux平台总线设备驱动

    1.什么是platform(平台)总线? 相对于USB.PCI.I2C.SPI等物理总线来说,platform总线是一种虚拟.抽象出来的总线,实际中并不存在这样的总线. 那为什么需要platform总 ...

  5. Linux学习 : 总线-设备-驱动模型

    platform总线是一种虚拟的总线,相应的设备则为platform_device,而驱动则为platform_driver.Linux 2.6的设备驱动模型中,把I2C.RTC.LCD等都归纳为pl ...

  6. Linux的总线设备驱动模型

    裸机编写驱动比较自由,按照手册实现其功能即可,每个人写出来都有很大不同: 而Linux中还需要按照Linux的驱动模型来编写,也就是需要按照"模板"来写,写出来的驱动就比较统一. ...

  7. usb驱动开发4之总线设备驱动模型

    在上文说usb_init函数,却给我们留下了很多岔路口.这次就来好好聊聊关于总线设备驱动模型.这节只讲理论,不讲其中的函数方法,关于函数方法使用参考其他资料. 总线.设备.驱动对应内核结构体分别为bu ...

  8. Linux平台总线设备驱动

    1. 平台总线(Platform bus)是linux2.6内核加入的一种虚拟总线,其优势在于采用了总线的模型对设备(没有挂到真实总线的设备)与驱动进行了管理,这样提高了程序的可移植性. 2. 平台总 ...

  9. Linux总线设备驱动模型

    1. Linux2.6内核引入总线.设备.驱动模型来描述各种总线(PCI.USB.I2C.SPI)与外围设备及其驱动之间的关系. 2. 在Linux内核中,总线用bus_type结构来描述,定义于文件 ...

  10. Linux I2C总线设备驱动模型分析(ov7740)

    1. 框架1.1 硬件协议简介1.2 驱动框架1.3 bus-drv-dev模型及写程序a. 设备的4种构建方法a.1 定义一个i2c_board_info, 里面有:名字, 设备地址 然后i2c_r ...

随机推荐

  1. Server2003+IIS6+TP-Link+花生壳配置

    Server2003+IIS6+TP-Link+花生壳配置外网一共分四步: 固定Server2003电脑的局域网IP地址. 设置IIS网站中的TCP端口. 在TP-Link中设置转发规则. 申请花生壳 ...

  2. JFinal Web开发学习(三)前后台路由设计

    效果图: 一.写控制器 1.在controller包中新建AdminController后台控制器,继承Controller,实现一个index方法,作为的处理方法. /admin 后面,这个控制器中 ...

  3. <history> 特别报道:Google离职富翁们都在干什么?

    特别报道:Google离职富翁们都在干什么? 时间:2008-01-23 10:16:47作者:CNET科技资讯网 本文关键词:Google CNET科技资讯网1月23日国际报道 假如你拥有1千万或1 ...

  4. python any() all()

    any() 函数用于判断给定的可迭代参数 iterable 是否全部为 False,则返回 False,如果有一个为 True,则返回 True. 元素除了是 0.空.FALSE 外都算 TRUE. ...

  5. Python.Books

    Flask 1. Flask Web Development Miguel Grinberg April 2014 2. Flask Framework Cookbook Shalabh Aggarw ...

  6. gulp ( http://markpop.github.io/2014/09/17/Gulp入门教程 )

    前言 最近流行前端构建工具,苦于之前使用Grunt,代码很难阅读,现在出了Gulp,真是摆脱了痛苦.发现了一篇很好的Gulp英文教程,整理翻译给大家看看. 为什么使用Gulp Gulp基于Node.j ...

  7. 常用MFC宏

    最近我在用MFC开发一个智能家居监控平台的软件(用到了MSCOMM串口通信控件),当我通过在一个对话框类A中定义另一个对话框类B的对象访问B的public成员时,提示不可访问.后来经过多天的向朋友求救 ...

  8. 无法将参数 1 从“WCHAR [256]”转换为“const char *”

    https://blog.csdn.net/zhangxuechao_/article/details/81064037 字符集 修改为未设置 然后再修改回来unicode  居然好了

  9. MySQL连接、登录、密码等

    官方教程:https://dev.mysql.com/doc/refman/8.0/en/ 链接数据库,通过指定 -h 参数可以连接网络上的数据库 mysql -u 用户名 -h 服务器IP -P 端 ...

  10. How To Configure SAMBA Server And Transfer Files Between Linux & Windows

    If you are reading this article it means you have a network at home or office with Windows and Linux ...