700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > Linux设备驱动模型4——平台总线实践

Linux设备驱动模型4——平台总线实践

时间:2022-04-02 16:40:02

相关推荐

Linux设备驱动模型4——平台总线实践

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

参考/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 { 14unsigned int gpio; // led设备用到的GPIO 15char *device_name; // led设备在/sys/class/leds/目录下的名字 16char *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 { 49printk(KERN_INFO "x210_led1_release\n"); 50 } 51 52 void x210_led2_release(struct device *dev) 53 { 54printk(KERN_INFO "x210_led1_release\n"); 55 } 56 57 void x210_led3_release(struct device *dev) 58 { 59printk(KERN_INFO "x210_led1_release\n"); 60 } 61 62 void x210_led4_release(struct device *dev) 63 { 64printk(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 {117if (platform_add_devices(x210_devices, ARRAY_SIZE(x210_devices))) // 循环注册platform平台设备118{119 printk(KERN_ERR "platform_add_devices failed.\n");120 return -1;121}122123return 0;124 }125 126 // 出口函数(卸载)127 static void __exit leds_x210_exit(void)128 {129int i = 0;130for (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@"); // 描述模块的作者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{ 14LED_ON_ = 0, 15LED_OFF_ = 1, 16 }; 17 18 struct x210_led_platdata { 19unsigned int gpio; 20char *device_name; 21char *gpio_name; 22 }; 23 24 struct x210_platform { 25struct led_classdev led_class; 26unsigned int gpio; 27 }; 28 29 static inline struct x210_platform *pdev_to_gpio(struct platform_device *dev) 30 { 31return platform_get_drvdata(dev); 32 } 33 34 static void x210_led_set(struct led_classdev *led_cdev, enum led_brightness brightness) 35 { 36struct x210_platform *pdata = container_of(led_cdev, struct x210_platform, led_class); // 使用led_classdev结构体反推得到x210_platform结构体 3738if (0 < brightness) 39 gpio_set_value(pdata->gpio, LED_ON_); // 使led小灯发亮 40else 41 gpio_set_value(pdata->gpio, LED_OFF_);// 使led小灯熄灭 42 } 43 44 static int x210_led_probe(struct platform_device *dev) 45 { 46// 定义变量 47int ret = 0; 48struct x210_led_platdata *platdata = dev->dev.platform_data; 49struct x210_platform *pform = NULL; 5051// 分配空间 52pform = kzalloc(sizeof(struct x210_platform), GFP_KERNEL); // 将pform指针指向系统分配的地址空间 53if (NULL == pform) { 54 printk(KERN_ERR "kzalloc failed.\n"); 55 return -ENOMEM; 56} 5758platform_set_drvdata(dev, pform); // 将pform值写入到传进来的platform_device结构体中的device结构体下的私有数据区中去,以便后面释放内存时用 5960//驱动框架接口填充 61pform->led_class.name = platdata->device_name; 62pform->led_class.brightness = 0;63pform->led_class.brightness_set = x210_led_set; 6465// 保存gpio管脚信息 66pform->gpio = platdata->gpio; 6768//注册led驱动 69ret = led_classdev_register(NULL, &pform->led_class); 70if (ret < 0) { 71 printk(KERN_ERR "led_classdev_register failed.\n"); 72 goto err_led_classdev_register; 73} 7475// 向gpiolib管理器申请gpio资源 76if (gpio_request(platdata->gpio, platdata->gpio_name)) 77{ 78 printk(KERN_ERR "gpio_request failed.\n"); 79 goto err_gpio_request; 80} 8182// 设置GPIO输出高电平,关闭LED 83gpio_direction_output(platdata->gpio, LED_OFF_); 8485printk(KERN_INFO "x210_led_probe succeseful.\n"); 86 87return 0; 88 89 err_gpio_request: 90led_classdev_unregister(&pform->led_class); 9192 err_led_classdev_register: 93 94return -1; 95 } 96 97 static int x210_led_remove(struct platform_device *dev) 98 { 99struct x210_led_platdata *platdata = dev->dev.platform_data; 100struct x210_platform *pform = pdev_to_gpio(dev);// 取出platform_device结构体中的device结构体中的私有数据区的x210_platform指针101102// 卸载led驱动103led_classdev_unregister(&pform->led_class);104 105// 关闭所有led106gpio_set_value(platdata->gpio, LED_OFF_);107108// 释放申请的GPIO资源109gpio_free(platdata->gpio);110111// 释放内存112kfree(pform); // 这个一定要放在最后113114printk(KERN_INFO "x210_led_remove succeseful.\n");115116return 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 {130int ret = 0;131132ret = platform_driver_register(&x210_led_driver);133if (ret)134 printk(KERN_ERR "platform_driver_register failed.\n");135136return ret; 137 }138 139 static void __exit leds_x210_exit(void)140 {141platform_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@"); // 描述模块的作者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中执行。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。