以下内容源于朱有鹏《物联网大讲堂》课程的学习,如有侵权,请告知删除。

参考http://www.cnblogs.com/deng-tao/p/6033571.html

一、平台总线实践环节1

(编写X210下基于平台总线的LED驱动,本节把上个课程的LED驱动源码拿来改写成平台总线制式,先实现platform_driver。)

1、回顾

2、先初步改造添加platform_driver

(1)第1步:先修改原来的代码到只有led1

(2)第2步:将init的改到probe函数里。

3、实验现象分析

  • 在/sys/bus/platform/driver下有此驱动(见图),因为驱动程序有如下片段。
  • 但probe函数不会被执行,因为这里只是driver单方面的注册,没有设备。
static int __init s5pv210_led_init(void)
{return platform_driver_register(&s5pv210_led_driver);
}

二、平台总线实践环节2

(分析系统移植时的mach文件,然后找到并添加LED相关的platform_device的注册。)

1、检查mach-x210.c中是否有led相关的platform_device

  • 查找platform_device数组,发现没有。

2、因此参考mach-mini2440.c中添加led的platform_device定义

(1)先写此设备的结构体

  • a、先写头文件(主要是struct s5pv210_led_platdata这个结构体的声明)
  • b、实例化struct s5pv210_led_platdata这个结构体(注意修改.flags,截图的没有改)

  • c、最后写此设备的结构体

(2)再添加到platform_device数组中

3、测试只有platform_device没有platform_driver时是怎样的?

  • 因为没有加载driver,因此只有platform_device(它不用加载,而是集成到内核中)

三、平台总线实践环节3

(本节同时调试platform_device和platform_driver,进行问题排除和代码测试、讲解。)

1、测试platform_device和platform_driver相遇时会怎样?

  • 设备已经集成到内核,只要加载驱动就好。
  • dplatform_driver只要装载,按理应该执行dplatform_driver的probe函数。(验证方法,通过在probe函数中添加printk信息验证)

现象1:

分析:

(1)三个led的设备名字一样,按理都能匹配上的。

(2)第一次打印出来的,应该是正确的。

(3)第二次打印出来的,是失败的,因为led1申请的gpio资源,led2又申请相同的资源。

现象2:

2、probe函数

(1)probe函数应该做什么

(2)probe函数的数据从哪里来

(3)编程实践

四、平台总线实践环节4

(硬件配置信息中的数据如何从device端传递到driver端,并且被driver接收用于硬件的操作方法中。)

(1)两个接口函数:platform_device_register和platform_driver_register

-------------------------------------------------转载文————————

PLATFORM总线驱动代码分析

/************************************************************************/

Linux内核版本:2.6.35.7

运行平台:三星s5pv210

/************************************************************************/

1、本例中通过使用Linux驱动模型中的platform总线和led驱动框架编写出来的led驱动代码来分析platform总线的工作原理,本代码是我自己将近快一天的时间编写出来的,

已经通过运行验证代码是能够正常运行的。对此对代码做如下分析:

在platform总线(任意总线)下的驱动代码都是要分为两部分:设备和驱动,在platform总线下是platform_device和platform_driver。

关于这个问题,我在我的上一篇博客中已经做了很好的说明。对于设备部分的注册:

(1)一般是内核的移植工程师在做移植的时候添加的,在我的这个移植好的内核中是放在arch/arm/mach-s5pv210/mach-x210.c文件中,

所以如果移植工程师没有添加你需要编写的驱动对应的设备,那么就需要你自己添加,你可以直接在这个文件中添加,系统启动的时候就会

直接加载这个文件的代码,所以设备就会被注册到我们的platform总线下,那么就会添加到platform总线管理下的device设备管理相关的数

据结构中去(链表),此时platform总线下的match函数就会自动进行匹配(每注册一个设备或者驱动match函数都会被调用),因为此时还

相应的驱动被注册,所以匹配肯定是失败的;当我们把驱动也注册之后,也会把驱动添加到platform总线管理下的drive驱动管理相关的数据

结构中去(也是一个链表),platform总线将会再次执行match函数,此时match函数就会匹配成功,platform总线下的设备和驱动就建立了对应

关系了,那么设备就能够工作了。

(2)设备注册部分也可以单独编写, 我们下驱动的时候提供 xxxxx_device.c(用来编写设备部分)和xxx_driver(用来编写驱动部分),将他们

编译成模块,系统启动之后分别使用insmod装载设备和驱动(顺序无所谓)。这种情况一般使用在调试阶段,如果确定我们的驱动是没有bug的情况下

,最好还是把驱动编译进内核,把他们放在他们应该在的位置。

2、led驱动代码

本例子采用的是单独编写编译的方式,代码分析如下:

(1)设备部分:leds-x210-device.c

  1 #include <linux/module.h>        // module_init  module_exit
  2 #include <linux/init.h>            // __init   __exit
  3 #include <linux/fs.h>
  4 #include <linux/leds.h>
  5 #include <mach/regs-gpio.h>
  6 #include <mach/gpio-bank.h>
  7 #include <linux/io.h>
  8 #include <linux/ioport.h>
  9 #include <mach/gpio.h>
 10 #include <linux/platform_device.h>
 11
 12 // 定义一个结构体用于放置本设备的私有数据
 13 struct x210_led_platdata {
 14     unsigned int gpio;    // led设备用到的GPIO
 15     char *device_name;    // led设备在/sys/class/leds/目录下的名字
 16     char *gpio_name;      // 使用gpiolob申请gpio资源时分配的名字
 17 };
 18
 19
 20 // 定义x210_led_platdata类型的变量,分别对应板子上的4颗led小灯
 21 static struct x210_led_platdata x210_led1_pdata = {
 22     .gpio = S5PV210_GPJ0(3),
 23     .device_name = "led1",
 24     .gpio_name = "led1-gpj0_3",
 25 };
 26
 27 static struct x210_led_platdata x210_led2_pdata = {
 28     .gpio = S5PV210_GPJ0(4),
 29     .device_name = "led2",
 30     .gpio_name = "led2-gpj0_4",
 31 };
 32
 33 static struct x210_led_platdata x210_led3_pdata = {
 34     .gpio = S5PV210_GPJ0(5),
 35     .device_name = "led3",
 36     .gpio_name = "led3-gpj0_5",
 37 };
 38
 39 static struct x210_led_platdata x210_led4_pdata = {
 40     .gpio = S5PV210_GPD0(1),
 41     .device_name = "led4",
 42     .gpio_name = "led4-gpd0_1",
 43 };
 44
 45
 46 // 定义4个release函数,当我们卸载设备时会调用platform_device结构体中的device结构体下的release函数
 47 void x210_led1_release(struct device *dev)
 48 {
 49     printk(KERN_INFO "x210_led1_release\n");
 50 }
 51
 52 void x210_led2_release(struct device *dev)
 53 {
 54     printk(KERN_INFO "x210_led1_release\n");
 55 }
 56
 57 void x210_led3_release(struct device *dev)
 58 {
 59     printk(KERN_INFO "x210_led1_release\n");
 60 }
 61
 62 void x210_led4_release(struct device *dev)
 63 {
 64     printk(KERN_INFO "x210_led1_release\n");
 65 }
 66
 67
 68 // 定义4个platform_device结构体
 69 static struct platform_device x210_led1 = {
 70     .name        = "x210_led",
 71     .id        = 0,
 72     .dev        = {
 73         .platform_data    = &x210_led1_pdata,
 74         .release = x210_led1_release,
 75     },
 76 };
 77
 78 static struct platform_device x210_led2 = {
 79     .name        = "x210_led",
 80     .id        = 1,
 81     .dev        = {
 82         .platform_data    = &x210_led2_pdata,
 83         .release = x210_led2_release,
 84     },
 85 };
 86
 87 static struct platform_device x210_led3 = {
 88     .name        = "x210_led",
 89     .id        = 2,
 90     .dev        = {
 91         .platform_data    = &x210_led3_pdata,
 92         .release = x210_led3_release,
 93     },
 94 };
 95
 96 static struct platform_device x210_led4 = {
 97     .name        = "x210_led",
 98     .id        = 3,
 99     .dev        = {
100         .platform_data    = &x210_led4_pdata,
101         .release = x210_led4_release,
102     },
103 };
104
105
106 // 将4个platform_device结构体地址放入一个数组中
107 static struct platform_device *x210_devices[] = {
108     &x210_led1,
109     &x210_led2,
110     &x210_led3,
111     &x210_led4,
112 };
113
114 // 入口函数
115 static int __init leds_x210_init(void)
116 {
117     if (platform_add_devices(x210_devices, ARRAY_SIZE(x210_devices)))  // 循环注册platform平台设备
118     {
119         printk(KERN_ERR "platform_add_devices failed.\n");
120         return -1;
121     }
122
123     return 0;
124 }
125
126 // 出口函数(卸载)
127 static void __exit leds_x210_exit(void)
128 {
129     int i = 0;
130     for (i = 0; i < 4; i++)
131        platform_device_unregister(x210_devices[i]);    // 当执行到这个函数的时候就会去执行platform_device结构体中的device结构体下的release函数
132 }
133
134 /*函数入口和出口修饰*/
135 module_init(leds_x210_init);
136 module_exit(leds_x210_exit);
137
138 /*描述模块信息*/
139 MODULE_LICENSE("GPL");                // 描述模块的许可证
140 MODULE_AUTHOR("Tao Deng <> 773904075@qq.com");            // 描述模块的作者
141 MODULE_DESCRIPTION("led device for x210.");    // 描述模块的介绍信息
142 MODULE_ALIAS("alias DT");            // 描述模块的别名信息

(2)驱动部分:leds-x210-driver.c

  1 #include <linux/module.h>        // module_init  module_exit
  2 #include <linux/init.h>            // __init   __exit
  3 #include <linux/fs.h>
  4 #include <linux/leds.h>
  5 #include <mach/regs-gpio.h>
  6 #include <mach/gpio-bank.h>
  7 #include <linux/io.h>
  8 #include <linux/ioport.h>
  9 #include <mach/gpio.h>
 10 #include <linux/platform_device.h>
 11 #include <linux/slab.h>
 12
 13 enum LED{
 14     LED_ON_ = 0,
 15     LED_OFF_ = 1,
 16 };
 17
 18 struct x210_led_platdata {
 19     unsigned int gpio;
 20     char *device_name;
 21     char *gpio_name;
 22 };
 23
 24 struct x210_platform {
 25     struct led_classdev led_class;
 26     unsigned int gpio;
 27 };
 28
 29 static inline struct x210_platform *pdev_to_gpio(struct platform_device *dev)
 30 {
 31     return platform_get_drvdata(dev);
 32 }
 33
 34 static void x210_led_set(struct led_classdev *led_cdev, enum led_brightness brightness)
 35 {
 36     struct x210_platform *pdata = container_of(led_cdev, struct x210_platform, led_class);  // 使用led_classdev结构体反推得到x210_platform结构体
 37
 38     if (0 < brightness)
 39         gpio_set_value(pdata->gpio, LED_ON_);       // 使led小灯发亮
 40     else
 41         gpio_set_value(pdata->gpio, LED_OFF_);      // 使led小灯熄灭
 42 }
 43
 44 static int x210_led_probe(struct platform_device *dev)
 45 {
 46     // 定义变量
 47     int ret = 0;
 48     struct x210_led_platdata *platdata = dev->dev.platform_data;
 49     struct x210_platform *pform = NULL;
 50
 51     // 分配空间
 52     pform = kzalloc(sizeof(struct x210_platform), GFP_KERNEL);    // 将pform指针指向系统分配的地址空间
 53     if (NULL == pform) {
 54         printk(KERN_ERR "kzalloc failed.\n");
 55         return -ENOMEM;
 56     }
 57
 58     platform_set_drvdata(dev, pform);    // 将pform值写入到传进来的platform_device结构体中的device结构体下的私有数据区中去,以便后面释放内存时用
 59
 60     //驱动框架接口填充
 61     pform->led_class.name = platdata->device_name;
 62     pform->led_class.brightness = 0;
 63     pform->led_class.brightness_set = x210_led_set;
 64
 65     // 保存gpio管脚信息
 66     pform->gpio = platdata->gpio;
 67
 68     //注册led驱动
 69     ret =  led_classdev_register(NULL, &pform->led_class);
 70     if (ret < 0) {
 71         printk(KERN_ERR "led_classdev_register failed.\n");
 72         goto err_led_classdev_register;
 73     }
 74
 75     // 向gpiolib管理器申请gpio资源
 76     if (gpio_request(platdata->gpio, platdata->gpio_name))
 77     {
 78         printk(KERN_ERR "gpio_request failed.\n");
 79         goto err_gpio_request;
 80     }
 81
 82     // 设置GPIO输出高电平,关闭LED
 83     gpio_direction_output(platdata->gpio, LED_OFF_);
 84
 85     printk(KERN_INFO "x210_led_probe succeseful.\n");
 86
 87     return 0;
 88
 89 err_gpio_request:
 90     led_classdev_unregister(&pform->led_class);
 91
 92 err_led_classdev_register:
 93
 94     return -1;
 95 }
 96
 97 static int x210_led_remove(struct platform_device *dev)
 98 {
 99     struct x210_led_platdata *platdata = dev->dev.platform_data;
100     struct x210_platform *pform = pdev_to_gpio(dev);     // 取出platform_device结构体中的device结构体中的私有数据区的x210_platform指针
101
102     // 卸载led驱动
103     led_classdev_unregister(&pform->led_class);
104
105     // 关闭所有led
106     gpio_set_value(platdata->gpio, LED_OFF_);
107
108     // 释放申请的GPIO资源
109     gpio_free(platdata->gpio);
110
111     // 释放内存
112     kfree(pform);         // 这个一定要放在最后
113
114     printk(KERN_INFO "x210_led_remove succeseful.\n");
115
116     return 0;
117 }
118
119 static struct platform_driver x210_led_driver = {
120     .probe        = x210_led_probe,
121     .remove        = x210_led_remove,
122     .driver        = {
123         .name        = "x210_led",
124         .owner        = THIS_MODULE,
125     },
126 };
127
128 static int __init leds_x210_init(void)
129 {
130     int ret = 0;
131
132     ret = platform_driver_register(&x210_led_driver);
133     if (ret)
134        printk(KERN_ERR "platform_driver_register failed.\n");
135
136     return ret;
137 }
138
139 static void __exit leds_x210_exit(void)
140 {
141     platform_driver_unregister(&x210_led_driver);
142 }
143
144 /*函数入口和出口*/
145 module_init(leds_x210_init);
146 module_exit(leds_x210_exit);
147
148 /*描述模块信息*/
149 MODULE_LICENSE("GPL");                // 描述模块的许可证
150 MODULE_AUTHOR("Tao Deng <> 773904075@qq.com");            // 描述模块的作者
151 MODULE_DESCRIPTION("led driver for x210.");    // 描述模块的介绍信息
152 MODULE_ALIAS("alias DT");            // 描述模块的别名信息

3、platform总线工作原理

(1)insmod之后的现象

当我们执行第一个insmod的时候,并没有出现后面的4条打印信息,只是执行了leds_x210_init函数(一次insmod只能对应执行一次xxx_init函数,

一次rmmod只能对应执行一次xxx_exit函数),这里并没有放置打印语句的信息,因为此时匹配并不会成功,所以不会执行驱动层probe函数;

而当执行第二个insmod的时候,此时platform总线下的match函数匹配就会成功,因为对应有4个led设备,所以就会匹配4次,执行了4次probe

函数打印出相应的信息。

(2)rmmod时的现象

当我们卸载调驱动或者是设备中的任何一个,都会执行驱动层的remove函数;如果是先rmmod设备,那么先执行驱动层中的remove函数,之后就

会执行设备层中的platform_device结构体中的device结构体下的release函数,每一个设备都会执行一次,因为这里卸载了4个设备,所以就会执行

4次;如果是先rmmod驱动,那么直接就会执行驱动层的remove函数。

(3)总结:

insmod注册执行入口函数leds_x210_init -> platform总线下match函数进行匹配 -> 匹配成功则执行驱动层platform_driver结构体下的probe函数(失败就没什么可说的了)->

初始化驱动。

rmmod卸载执行出口函数leds_x210_exit -> 执行驱动层platform_driver结构体下的remove函数 -> 如果是设备则还需要执行设备层platform_device结构体中的device结构体下的

release函数。

(4)当我们注册了设备之后,在platform总线的device中会出现我们注册的设备;当我们注册了驱动之后,在platform总线的driver中会出现驱动名;

但是只要match函数没有匹配成功就不会执行probe函数,那么属于操作led驱动框架下的接口/sys/class/leds下的设备接口就不会出现,因为

led_classdev_register函数在probe中执行。

Linux设备驱动模型4——平台总线实践相关推荐

  1. Linux设备驱动模型之platform总线

    1 平台设备和驱动初识 platform是一个虚拟的地址总线,相比pci,usb,它主要用于描述SOC上的片上资源,比如s3c2410上集成的控制器(lcd,watchdog,rtc等),platfo ...

  2. Linux设备驱动模型之platform(平台)总线详解

    /********************************************************/ 内核版本:2.6.35.7 运行平台:三星s5pv210 /*********** ...

  3. linux平台设备驱动模型是什么意思,Linux设备驱动模型之我理解

    点击(此处)折叠或打开 /* my_bus.c   */ #include #include #include #include #include #include "my_bus.h&qu ...

  4. RK3568平台开发系列讲解(驱动篇) linux设备驱动模型

    文章目录 linux设备驱动模型 为什么需要设备驱动模型 sysfs概述 设备驱动模型基本元素 驱动模型一 驱动模型二 kobject kset kobj_type linux设备驱动模型 为什么需要 ...

  5. linux设备驱动模型-linux驱动开发第5部分-朱有鹏-专题视频课程

    linux设备驱动模型-linux驱动开发第5部分-4285人已学习 课程介绍         本课程是linux驱动开发的第5个课程,主要内容是linux的设备驱动模型,包括总线.类.设备.驱动等概 ...

  6. 整理--linux设备驱动模型

    知识整理–linux设备驱动模型 以kobject为底层,组织类class.总线bus.设备device.驱动driver等高级数据结构,同时实现对象引用计数.维护对象链表.对象上锁.对用户空间的表示 ...

  7. linux 统一设备模型 pci,Linux设备驱动模型摘抄

    Linux设备驱动模型摘抄Linux设备驱动模型摘抄Linux设备驱动模型摘抄Linux设备驱动模型摘抄Linux设备驱动模型摘抄 Linux设备驱动模型摘抄(1) Linux统一设备模型 简介 Li ...

  8. Linux设备驱动模型1——简介和底层架构

    以下内容源于朱有鹏<物联网大讲堂>课程的学习整理,如有侵权,请告知删除. 一.linux设备驱动模型简介 1.什么是设备驱动模型? (1)类class.总线bus.设备device.驱动d ...

  9. linux设备驱动模型及其他,Linux设备驱动模型

    Linux设备驱动模型,主要函数分析 整个驱动模型中,最核心的三个函数分别是 __bus_register.driver_register.device_register int __bus_regi ...

最新文章

  1. OpenCV 笔记(05)— opencv.hpp 头文件作用(是其它所有头文件的全集)
  2. 代码如何获取单反相机拍摄的照片_看看如何拍摄出更好的照片
  3. 头像裁剪上传_微信新功能:11月微信新玩法!给微信换上“皇冠头像”,还能加姓氏?...
  4. [转帖]Mootools源码分析-03 -- Hash
  5. JAVA并行框架:Fork/Join
  6. mysql 参照完整性规则_详解mysql数据库审计特点、实现方案及审计插件部署演示...
  7. VS编译报错Error2019
  8. 拉格朗日插值的优缺点_对拉格朗日插值法与牛顿插值法的学习和比较
  9. c#判断字符串是否为空或null
  10. 以后不该吃的不能吃了。。。
  11. php随机点名代码怎么做,html座位表随机点名的实例代码
  12. MDX示例:求解中位数、四分位数(median、quartile)
  13. pywifi连接中文wifi名称(乱码)连接不上问题解决方案
  14. 在线支付系列【2】支付宝和微信支付发展史
  15. 电脑数据丢失如何恢复呢?
  16. js图片转base64
  17. CAD图案填充:什么是CAD线图案?
  18. Las Vegas 与回溯组合法解八皇后问题
  19. The King's Ups and Downs UVALive - 6177
  20. 盖大楼的地基打好了(Model)

热门文章

  1. [Python]小甲鱼Python视频第026课(字典:当索引不好用时2)课后题及参考解答
  2. JDBC数据对象存储
  3. java XML解析防止外部实体注入
  4. 魔兽世界客户端数据研究(三)
  5. 保护视力,我写的一个定时提醒的小玩意。
  6. 表单oninput和onchange事件区别
  7. vue新手入门——vue-cli搭建
  8. jmeter 压测duobbo接口,施压客户端自己把自己压死了
  9. Vue2.0王者荣耀助手
  10. which 命令