LED驱动框架解析

注:我看的内核是三星官方提供的2.6.35版本的内核
注:我的博客都是我在学习时候为了增强我对一些内容的印象而写的,不喜勿喷
一、驱动框架基础
1.框架,框架首先要有框架的思想,也就是要明白我们自己要写哪一部分的代码,哪一部分代码是别人帮我们写好的(我们直接用就行)。

2.首先要明白写一个驱动,需要两波人的参与。
(1)内核维护者提供的框架相同的部分。
(2)驱动开发工程师根据不同的硬件,使用内核维护者提供的框架部分的接口来开发硬件的驱动。

二、内核维护者提供的LED驱动部分的文件
1.led-core.c和led-class.c,两个文件就是内核维护者提供的。主要的内容在led-class.c里面,所以接下来我们分析led-class.c文件。

2.看驱动部分的代码依旧按照从下往上的顺序看。
(1)led框架的初始化代码

static int __init leds_init(void)
{
    //在/sys/class/目录下创建leds目录
    leds_class = class_create(THIS_MODULE, "leds");
    if (IS_ERR(leds_class))
        return PTR_ERR(leds_class);
    leds_class->suspend = led_suspend;
    leds_class->resume = led_resume;
    //指定leds类下属性方法。
    leds_class->dev_attrs = led_class_attrs;
    return 0;
}

1)在我们cat查看brightness文件的时候则会调用led_brightness_show方法来显示,在echo xxx > brightness文件的时候则会调用led_brightness_store方法来存储。
2)max_brightness也是一样,但是它只有cat方法,没有echo的方法

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驱动框架,在内核启动的时候就会在/sys/class/下自动创建leds目录。添加方法自己去搞懂menuconfig和Kconfig的关系。

(2)接下来我们看内核维护者提供的led的注册接口,如果我们要写led的驱动,要完成led的注册直接调用这个函数就行。

int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
{
    //在/sys/class/leds目录下创建设备。也就是我们自己写的led设备驱动
    led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
                      "%s", led_cdev->name);
    if (IS_ERR(led_cdev->dev))
        return PTR_ERR(led_cdev->dev);
    //不懂trigger是什么
#ifdef CONFIG_LEDS_TRIGGERS
    init_rwsem(&led_cdev->trigger_lock);
#endif
    /* add to the list of leds */
    //上锁
    down_write(&leds_list_lock);
    //把要注册的led添加到已经注册的链表当中
    list_add_tail(&led_cdev->node, &leds_list);
    //解锁
    up_write(&leds_list_lock);

    //如果没设置led的max_brightness,则设置为LED_FULL
    if (!led_cdev->max_brightness)
        led_cdev->max_brightness = LED_FULL;
    //如果你设置了led_cdev里面的brightness_get函数指针,重新跟新brightness
    led_update_brightness(led_cdev);

#ifdef CONFIG_LEDS_TRIGGERS
    led_trigger_set_default(led_cdev);
#endif

    printk(KERN_DEBUG "Registered led device: %s\n",
            led_cdev->name);

    return 0;
}

(3)注册完了接下来就是注销LED驱动

void led_classdev_unregister(struct led_classdev *led_cdev)
{
#ifdef CONFIG_LEDS_TRIGGERS
    down_write(&led_cdev->trigger_lock);
    if (led_cdev->trigger)
        led_trigger_set(led_cdev, NULL);
    up_write(&led_cdev->trigger_lock);
#endif
    //移除在/sys/class/leds目录下创建的设备驱动目录
    device_unregister(led_cdev->dev);

    down_write(&leds_list_lock);
    //删除已经注册的LED设备驱动的链表。
    list_del(&led_cdev->node);
    up_write(&leds_list_lock);
}

(4)还有两个跟操作LED相关的函数

//这个函数就是当我们cat读取brightness时候调用的函数
static ssize_t led_brightness_show(struct device *dev, 
        struct device_attribute *attr, char *buf)
{
    struct led_classdev *led_cdev = dev_get_drvdata(dev);

    /* no lock needed for this */
    led_update_brightness(led_cdev);

    return sprintf(buf, "%u\n", led_cdev->brightness);
}
//这个函数就是当我们echo写brightness时候调用的函数,实际是有用的函数是led_set_brightness,接下来我们看led_set_brightness函数
static ssize_t led_brightness_store(struct device *dev,
        struct device_attribute *attr, const char *buf, size_t size)
{
    struct led_classdev *led_cdev = dev_get_drvdata(dev);
    ssize_t ret = -EINVAL;
    char *after;
    unsigned long state = simple_strtoul(buf, &after, 10);
    size_t count = after - buf;

    if (isspace(*after))
        count++;

    if (count == size) {
        ret = count;

        if (state == LED_OFF)
            led_trigger_remove(led_cdev);
        led_set_brightness(led_cdev, state);
    }

    return ret;
}
//这个函数实际就是调用了led_cdev结构体里面的brightness_set函数指针,这就是我们自己要实现的函数
static inline void led_set_brightness(struct led_classdev *led_cdev,
                    enum led_brightness value)
{
    if (value > led_cdev->max_brightness)
        value = led_cdev->max_brightness;
    led_cdev->brightness = value;
    if (!(led_cdev->flags & LED_SUSPENDED))
        led_cdev->brightness_set(led_cdev, value);
}

三、实际写LED驱动的步骤
1.调用led_classdev_register来注册led驱动,根据它的参数,首先我们要填充led_classdev类型结构体变量的元素
(1)实际需要填充的很少。name、brightness(初始亮度)、brightness_set(这个比较重要,也就是实现LED的亮灭的函数)等。重要的就这几个。

struct led_classdev {
    const char      *name;
    int          brightness;
    int          max_brightness;
    int          flags;

    /* Lower 16 bits reflect status */
#define LED_SUSPENDED       (1 << 0)
    /* Upper 16 bits reflect control information */
#define LED_CORE_SUSPENDRESUME  (1 << 16)

    /* Set LED brightness level */
    /* Must not sleep, use a workqueue if needed */
    void        (*brightness_set)(struct led_classdev *led_cdev,
                      enum led_brightness brightness);
    /* Get LED brightness level */
    enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);

    /* Activate hardware accelerated blink, delays are in
     * miliseconds and if none is provided then a sensible default
     * should be chosen. The call can adjust the timings if it can't
     * match the values specified exactly. */
    int     (*blink_set)(struct led_classdev *led_cdev,
                     unsigned long *delay_on,
                     unsigned long *delay_off);

    struct device       *dev;
    struct list_head     node;          /* LED Device list */
    const char      *default_trigger;   /* Trigger to use */

#ifdef CONFIG_LEDS_TRIGGERS
    /* Protects the trigger data below */
    struct rw_semaphore  trigger_lock;

    struct led_trigger  *trigger;
    struct list_head     trig_list;
    void            *trigger_data;
#endif
};
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值