频道栏目
首页 > 资讯 > 其他综合 > 正文

基于led框架的驱动分析

16-09-10        来源:[db:作者]  
收藏   我要投稿

本文的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 
#include  
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

 /*设个全局变量。用来给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等字样,那么路径多半可以确认为等 当我们实在无法确定带有符号链接路径时,我们只需要借鉴其他文件即可。比如我们用到了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注册我们的驱动和模块本身
相关TAG标签
上一篇:一、无线信息传递——user space下的hostapd
下一篇:Win10系统elecworks连不上数据库怎么办?
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站