linux设备驱动之平台总线实践环节
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux设备驱动之平台总线实践环节相关的知识,希望对你有一定的参考价值。
1、在平台总线的实践环节的一和二中,分别将led的platform_driver和platform_device初步完成,接下来看下platform_device和platform_driver同时存在时的效果,就是led的平台设备已经注册到内核中,并且在根文件系统中ismod加载上led的平台总线驱动。看一下platform_device和platform_driver两者相遇会怎么样,根据platform平台总线的逻辑,因为我们已经将led的platform_device注册到了内核中,这个时候我们只需要加载led的platform_driver,按照设备和驱动的逻辑,当led的驱动加载上后,就会执行那个ismod时对应的函数,会将led的platform_driver进行注册,这个时候在平台总线下led的platform_device和platform_driver都已经被注册到平台总线中了,那么match函数就会根据设备和驱动的名字进行匹配设备和驱动,当led的设备和驱动匹配上后,驱动的probe函数就会得以执行,我们在目前的不完整的led的驱动代码中的probe函数中加入一个打印信息,probe函数代码如下
static int why_led_probe(struct platform_device *dev) { int ret = -1; printk(KERN_INFO "why_led_probe\n"); //led1 //填充我们要注册的struct led_classdev类型的结构体 myled1.name = "led1"; //将来sys/class/leds/目录下的那个led文件的名字。leds这个目录在led-class.c中内核帮我们实现好了、 myled1.brightness = 255; myled1.brightness_set = whyx210_led1_set; //去调用led驱动框架中为我们提供的led注册函数led_classdev_register去注册驱动 //在led-class.c中int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) ret = led_classdev_register(NULL, &myled1); if (ret < 0) { printk(KERN_ERR "led_classdev_register errro\n"); return ret; } return 0; }
在该代码的第一句中加入了打印信息,打印probe函数的名字,接下来我们就可以验证一下,当内核中存在led的platform_device的时候,我们在加载led的platform_driver时,是不是会通过平台总线的match函数用设备和驱动的名字将两者匹配上并执行驱动代码的probe函数,如果执行了probe函数,那么probe函数开始的打印信息就一定会打印出来。
启动系统,加载led的platform_driver的驱动模块后,在控制台中确实打印了打印信息,并且在/sys/bus/platform/driver/why_led/目录中多了一个why_led.1目录,说明我们的名字为why_led的平台总线驱动匹配上了一个设备,这个设备的名字叫做why_led.1,就是我们已经注册到总线下的led1设备。
因为probe函数被执行了,所probe函数中的
ret = led_classdev_register(NULL, &myled1);
注册函数会被执行,因为执行成功了,所以在/sys/class/leds/目录下我们会看到有一个led1这么一个目录,这个目录的名字就是我们填充myled1这个结构体时的name,led1目录中有一个brightness,就是myled1结构体中的brightness,来表示led的亮度的,我们的myled1结构体中有一个成员函数brightness_set绑定的是whyx210_led1_set函数,whyx210_led1_set函数如下
static void whyx210_led1_set(struct led_classdev *led_cdev, enum led_brightness value) { //printk(KERN_INFO "whyx210_led_set\n"); writel(0x11111111, GPJ0CON); if (value == LED_OFF) { //用户输入0时灭 对应用户输入的是echo 0 > brightness writel(readl(GPJ0DAT) | (1 << 3), GPJ0DAT); }else if (value == LED_FULL){ //用户输入255时亮 对应用户输入的是echo 255 > brightness writel(readl(GPJ0DAT) & ~(1 << 3), GPJ0DAT); //这样操作保持其他位值不变 } }
当我们向/sys/class/leds/led1/brightness文件中echo 0或者echo 1 时就会调用沙面的这个函数,从而操作led1的亮度。echo 0或者echo 1向这个brightness文件的时候,0或1会作为上面函数的形参value传进去。从而控制硬件。
这个时候去echo brightness文件的时候,确实是可以控制led的亮度的,但是这个时候我们的驱动还不完美,因为我们驱动中的probe函数关于led的数据是写死的,包括name、对应的操作函数、和led的对应引脚号,这些东西在我们probe函数是写死的,所以我们的驱动代码是不好的,我们要利用probe函数中的platform_device类型的参数,将这个设备变量得到,获取到设备变量中的platform_data,从这个data中从而知道这个led设备的gpio号是多少、led的名字是什么、以及其他的数据,这样从可以做到有led设备和这个led驱动匹配上后,执行probe函数时是实时获取到设备数据,然后填充到myled1结构体变量中,进行注册class。这样灵活,具有一般性。
我们需要在probe函数中定义一个我们写led设备时填充设备数据时用的那个类型的结构体变量,得到probe函数的platform_device类型的变量中的platform_data部分,就是得到这个led设备的数据部分,然后在probe函数中进行注册的时候,myled的成员值不在使用写死的led名字,而是通过platform_data中得到name然后进行注册,这样就是可以实时匹配实时注册了。
static int why_led_probe(struct platform_device *dev) { int ret = -1; struct s5pv210_led_platdata *pdata = dev->platform_data; //得到匹配上的设备的数据部分 //填充我们要注册的struct led_classdev类型的结构体 myled.name = pdata->name; //当前匹配上的led设备的名字 myled.brightness = 255; myled.brightness_set = whyx210_led1_set;//这个myled的brightness_set方法绑定的其实就不能是固定的whyx210_led1_set这个方法了,因为我们 //的驱动和设备的匹配不是固定是led1的,有可能led2的设备也和这个驱动匹配上了,总不能led2的 //操作方法使用的还是led1的方法吧 //去调用led驱动框架中为我们提供的led注册函数led_classdev_register去注册驱动 //在led-class.c中int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) ret = led_classdev_register(NULL, &myled); if (ret < 0) { printk(KERN_ERR "led_classdev_register errro\n"); return ret; } return 0; }
上面代码关于myled的birghtness_set方法绑定的注释中提到,这个函数是不能写死的,因此要用别的设计逻辑思路进行解决。下一篇博客在说。
本文出自 “whylinux” 博客,谢绝转载!
以上是关于linux设备驱动之平台总线实践环节的主要内容,如果未能解决你的问题,请参考以下文章
Linux——Linux驱动之基于平台总线platform的设备驱动编写实战(手把手教你以platform形式利用GPIO控制蜂鸣器)
Linux——Linux驱动之基于平台总线platform的设备驱动编写实战(手把手教你以platform形式利用GPIO控制蜂鸣器)