基于led框架的驱动分析
2016-09-02 08:09
399 查看
基于led框架的驱动分析
本文的led驱动使用了内核提供的led框架接口,这种驱动实现与普通字符设备驱动有着本质的区别。此外还融合了platform和gpiolib,需要结合这两者来分析本驱动。该驱动本质是:通过读写/sys/class/leds/xxx内的文件,触发led_classdev(设备体)内的函数,从而实现操作硬件。
整体关系:
1.led驱动框架接口的使用条件
如果要使用内核的框架来写驱动的话,必须要在menuconfig中添加框架模块,这样才能够调用框架接口函数比如添加led的框架。Device Drivers —>LED Support 选Y —> LED Class Support 选Y
LED Class Support下还有很多板级的led支持。我们就不用去勾选了
2.led驱动框架接口的实现原理
led-class_comment.c这个文件提供了有关led驱动框架的接口,里面是led框架模块leds_init是框架模块的加载函数,它主要负责创建led设备类。类的名字是“leds”,而类的本质是一个leds_class类型的结构体
static struct class *leds_class;//先定义指针 static int __init leds_init(void) { leds_class = class_create(THIS_MODULE, "leds");//创建、实例化leds类 if (IS_ERR(leds_class)) return PTR_ERR(leds_class); leds_class->suspend = led_suspend; leds_class->resume = led_resume; leds_class->dev_attrs = led_class_attrs; return 0; }
当“leds”这个类创建完后 ,我们就能够在/sys/class/中找到leds这个类,创建完类我们才能创建“属于这个类的设备”,可以认为类是创建设备的前提
这里主要是通过 leds_class->dev_attrs规定设备文件的种类和样式。dev_attrs是设备属性的意思,通过给它赋一个特殊的数据结构(里面包含了外设的硬件操作),我们就能够在应用层通过/sys/class/leds/xxx里的文件间接访问这个特殊的数据结构从而达到操作硬件的目的。代码为:
static struct device_attribute led_class_attrs[] = { __ATTR(brightness, 0644, led_brightness_show, led_brightness_store), __ATTR(max_brightness, 0444, led_max_brightness_show, NULL), #ifdef CONFIG_LEDS_TRIGGERS __ATTR(trigger, 0644, led_trigger_show, led_trigger_store), #endif __ATTR_NULL, };
该结构体数组设置了led硬件操作的对象和方法。分析可知,led类设备的操作对象一共由3个brightness、max_brightness、trigger。意思是”led的亮灭状态“、”led最高亮度值“、”led闪烁状态“。对应的操作规则有读写,即show和store。除了led最高亮度值,它只能读不能写,因为它只是一个参数罢了,而不是可以操作硬件。这些操作规则内部其实调用了设备体led_classdev内的具体操作函数,也就是说当用户层试图写brightness这个对象时,会触发操作规则led_brightness_store。这个规则内部会调用我们设备体内的具体函数。由前面那副图可以很好的理解这个原理
3.驱动代码分析
#include <linux/module.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/string.h> #include <linux/ioport.h> #include <linux/device.h> #include <linux/cdev.h> #include <linux/leds.h> #include <linux/gpio.h> #include <linux/platform_device.h> #include <mach/gpio.h> #include <plat/map-base.h> #include <plat/map-s5p.h> #include <asm/uaccess.h> #include <asm/io.h> /*设个全局变量。用来给s5pv210_led_set传参*/ unsigned int gpio_num = 0; /*设备体led_classdev内操作函数的具体实现*/ static void s5pv210_led_set(struct led_classdev *led_cdev, enum led_brightness value)//参数value是枚举。0、127、255分别代表灭、半亮、全亮 { if (value == 0) { gpio_set_value(gpio_num, 1); }else{ gpio_set_value(gpio_num, 0); } } /*创建(实例化)设备体*/ static struct led_classdev x210led; /*自留地格式...提供给probe和release函数..让它们可以解析platdata*/ extern struct s5pv210_led_platdata { unsigned int gpio; unsigned int flags; char *name; char *def_trigger; }; /*platform驱动的probe(探测)函数与remove函数*/ static int s5pv210_led_probe(struct platform_device *pdev) { struct s5pv210_led_platdata *pdata = pdev->dev.platform_data; int ret = -1; gpio_num = pdata->gpio; /*填充设备体。即led_classdev类型的结构体*/ x210led.name = pdata->name; x210led.brightness = 0;//led的亮灭状态 x210led.brightness_set = s5pv210_led_set;//设置led的亮灭 /*注册一个属于led类的设备体*/ ret = led_classdev_register(NULL, &x210led); if (ret < 0) { printk(KERN_INFO "led_classdev_register failed\n"); goto out_err_0; } /*申请一个gpio引脚资源*/ ret = gpio_request(pdata->gpio, pdata->name); if (ret){ printk(KERN_INFO "gpio_request failed\n"); goto out_err_1; } /*申请完后可以利用接口设置该gpio。也可以直接操作寄存器来设置*/ gpio_direction_output(pdata->gpio, 1); return 0; /*倒影式错误处理流程*/ out_err_1: led_classdev_unregister(&x210led); out_err_0: return -EINVAL; } static int s5pv210_led_remove(struct platform_device *pdev) { struct s5pv210_led_platdata *pdata = pdev->dev.platform_data; gpio_free(pdata->gpio); led_classdev_unregister(&x210led); return 0; } /*定义我们的platform_driver。注意name要和platform_device中相同*/ static struct platform_driver s5pv210_led_driver = { .probe = s5pv210_led_probe, .remove = s5pv210_led_remove, .driver = { .name = "s5pv210_led", .owner = THIS_MODULE, }, }; /*模块与卸载加载函数*/ static int __init s5pv210_led_init(void) { return platform_driver_register(&s5pv210_led_driver); } static void __exit s5pv210_led_exit(void) { platform_driver_unregister(&s5pv210_led_driver); } module_init(s5pv210_led_init); module_exit(s5pv210_led_exit); /*模块描述信息*/ MODULE_LICENSE("GPL"); MODULE_AUTHOR("taurenking"); MODULE_DESCRIPTION("S5PV210 LED driver"); MODULE_ALIAS("S5PV210 LED driver");
头文件解析
内核的头文件默认搜索路径在根目录下include文件夹内,所以#include这些头文件时路径的确认非常方便当头文件不在根目录下include文件夹内呢?基本上这些头文件#include时都包含了符号链接。确定带有符号链接路径的方法是,如果路径不在include文件夹内,但是里面包含了mach、asm等字样,那么路径多半可以确认为
<asm/xxxx>、<mach/xxxx>等
当我们实在无法确定带有符号链接路径时,我们只需要借鉴其他文件即可。比如我们用到了copy_from_user。我们知道它在uaccess.h中。但是不知道路径怎么写,这时只需要搜索一下同样调用“copy_from_user”的那些文件,直接抄他们的头文件路径即可
设备体led_classdev内操作函数的具体实现
值得注意的是。读led 状态的函数是不需要的,当APP尝试读取led状态时,系统会直接读取我们上次的写入值来返回给APP这看似很不合理,如果led寄存器的值被意外改变了呢?但是这其实非常合理,因为led这种设备只是输出设备,寄存器的值不可能被外界改变。所以系统采用了这种简便的方法,反正这个框架只是给led用的,不可能给其他设备用。所以我们也不难得出结论,在其他有输入功能的设备框架(例如串口)中,是不可能省略掉”读取函数“的
此处函数还利用了gpiolib接口来操作了gpio的数据寄存器 ,其实不一定要用gpiolib,直接操作寄存器也行,关于gpiolib详见gpiolib详尽分析
probe和release函数,及其内部的各种注册操作
有关probe和release,详见platform总线驱动详尽分析首先填充设备体,这个设备体就是我们定义的一个led_classdev类型结构体,它里面集成了所有的操作和参数,其实就相当普通字符设备驱动中的cdev及其内部的file_operations。只不过led_classdev针对的是属于“leds”类的设备,而cdev针对的是普通字符设备。由于 led_classdev_register内部并不会对这个结构体指针实例化,所以我们定义的时候还是定义结构体比较好
然后利用led_classdev_register注册这个设备体。led_classdev_register 就是框架给我们提供的接口,其第一个参数是父设备,第二个参数是指向led_classdev类型结构体的指针。经过了这一步 在/sys/class/leds/ 下就会自动出现一个目录,这个目录就是我们的设备文件目录。 对于我们这,/sys/class/leds/ 下就会出现特定led名字的目录,进入这个目录就会发现里面有各种文件、文件夹,如图
那么这些文件、文件夹从何而来?又代表了什么呢?其实这些文件的出现就是前文所说的dev_attrs的杰作 。只要用户创建注册的设备体使用了led框架的接口、属于leds这个类,那么设备体注册完生成的目录中就会自动包含这些文件、文件夹
由于用到了gpio,所以要先使用gpiolib向内核申请gpio资源 。关于gpiolib详见gpiolib详尽分析
创建(实例化)我们的platform_driver
这一步要绑定probe和release函数注意name要和platform_device中相同,这样才能platform总线正确匹配两者
和模块相关的代码
模块加载和卸载函数中分别调用 platform_driver_register和platform_driver_unregiste注册我们的驱动和模块本身有关的知识详见内核驱动的本质——模块
相关文章推荐
- 基于“事件”驱动的领域驱动设计(DDD)框架分析
- 基于misc框架的驱动分析
- 基于事件驱动的领域模型实现框架 - 分析框架如何解决各种典型业务逻辑场景
- 一个基于v4l2框架的输出驱动分析
- 友坚4412开发板基于Timed_out框架的GPIO驱动分析
- 驱动框架3——初步分析led驱动框架源码
- 基于网络设备框架的驱动分析
- 基于事件驱动的领域模型实现框架 - 分析框架如何解决各种典型业务逻辑场景
- [连载]基于消息驱动的面向对象通用C/S应用框架(十一)
- Linux USB驱动框架分析(一)(转)
- 基于消息驱动的面向对象通用C/S应用框架(三)
- [连载]基于消息驱动的面向对象通用C/S应用框架(七)
- 基于消息驱动的面向对象通用C/S应用框架(六)
- Linux USB驱动框架分析
- 基于消息驱动的面向对象通用C/S应用框架(一)
- 基于消息驱动的面向对象通用C/S应用框架(二)
- [连载]基于消息驱动的面向对象通用C/S应用框架(九)
- 基于VxW0rks操作系统的USB 2.0驱动分析
- [连载]基于消息驱动的面向对象通用C/S应用框架(八)
- [连载]基于消息驱动的面向对象通用C/S应用框架(二)