Android led_class driver

Linux的led class驱动

echo 255 > /sys/class/leds/led1/brightness
cat /sys/class/leds/led1/brightness
cat /sys/class/leds/led1/max_brightness

闪烁
echo timer > /sys/class/leds/led1/trigger
echo 100 > /sys/class/leds/led1/delay_on
echo 200 > /sys/class/leds/led1/delay_off

关闭
echo 0 > /sys/class/leds/led1/delay_on
echo 0 > /sys/class/leds/led1/brightness

怎么写驱动:
a1. 分配led_classdev
a2. 设置 : 
led_cdev->max_brightness
led_cdev->brightness_set
led_cdev->flags
led_cdev->brightness
led_cdev->name

led_class 子系统简要分析
static int __init leds_init(void)
{
        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_class->dev_attrs = led_class_attrs;
        return 0;
}
用户空间 sys 中的接口,可以设置 brightness 、获取最大背光亮度 max_brightness 、使能闪烁 trigger
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_class 设备的 set_brightness 函数
简要分析一下闪烁:
int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
{
        led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
                                      "%s", led_cdev->name);

#ifdef CONFIG_LEDS_TRIGGERS
        init_rwsem(&led_cdev->trigger_lock);
#endif
        /* add to the list of leds */
        down_write(&leds_list_lock);
        list_add_tail(&led_cdev->node, &leds_list);
        up_write(&leds_list_lock);

        if (!led_cdev->max_brightness)
                led_cdev->max_brightness = LED_FULL;

        led_update_brightness(led_cdev);

        init_timer(&led_cdev->blink_timer);
        // 注册 led_class 设备时会初始化一个定时器
        led_cdev->blink_timer.function = led_timer_function;
        led_cdev->blink_timer.data = (unsigned long)led_cdev;

#ifdef CONFIG_LEDS_TRIGGERS
        led_trigger_set_default(led_cdev);
#endif
        return 0;
}
//定时器的功能自然就是为了实现led的闪烁了
static void led_timer_function(unsigned long data)
{
        struct led_classdev *led_cdev = (void *)data;
        unsigned long brightness;
        unsigned long delay;

        brightness = led_get_brightness(led_cdev);
        if (!brightness) {
                /* Time to switch the LED on. */
                brightness = led_cdev->blink_brightness;
                delay = led_cdev->blink_delay_on;
        } else {
                /* Store the current brightness value to be able
                 * to restore it when the delay_off period is over.
                 */
                led_cdev->blink_brightness = brightness;
                brightness = LED_OFF;
                delay = led_cdev->blink_delay_off;
        }

        led_set_brightness(led_cdev, brightness);

        mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay));
}



echo timer > /sys/class/leds/led1/trigger  // timer对应 ledtrig-timer.c

led_trigger_store // 1. 从trigger_list找出名为"timer"的trigger
        list_for_each_entry(trig, &trigger_list, next_trig) {
                if (!strcmp(trigger_name, trig->name)) {
                    // 2. 调用
                    led_trigger_set(led_cdev, trig);
                        // 3. 把trigger放入led_classdev的trig_list链表里
                        list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs);
                        led_cdev->trigger = trigger;
                        ...
                        trigger->activate(led_cdev);
                           // 4. 对于"timer"
                           timer_trig_activate
                               // 6. 创建2个文件: delay_on, delay_off
                               device_create_file
                               device_create_file                       
                }
        }

echo 100 > /sys/class/leds/led1/delay_on
led_delay_on_store
  state = simple_strtoul(buf, &after, 10);
        led_blink_set  // // 让LED闪烁
            led_set_software_blink
                mod_timer(&led_cdev->blink_timer, jiffies + 1);
        led_cdev->blink_delay_on = state;

echo 200 > /sys/class/leds/led1/delay_off
led_delay_off_store
        state = simple_strtoul(buf, &after, 10);
        led_blink_set // 让LED闪烁
            led_set_software_blink
                mod_timer(&led_cdev->blink_timer, jiffies + 1);
        led_cdev->blink_delay_off = state;

通过 led_class 来实现标准的灯光系统驱动,用户空间可以获取、更新led亮度值,使能led闪烁。
驱动中只需要设置“set_brightness”函数,至于闪烁led子系统使用定时器已经实现。 

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/delay.h> 
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>

#include <linux/leds.h>

struct led_desc {
        int gpio;
        char *name;
};

static struct led_desc led_gpios[] = {
        {EXYNOS4212_GPM4(0), "led1"},
        {EXYNOS4212_GPM4(1), "led2"},
        {EXYNOS4212_GPM4(2), "led3"},
        {EXYNOS4212_GPM4(3), "led4"},
};

struct led_classdev_4412 {
        struct led_classdev cdev;
        int gpio;
};


static struct led_classdev_4412 *led_devs;
static void brightness_set_4412(struct led_classdev *led_cdev,
                          enum led_brightness brightness)
{
     struct led_classdev_4412 *dev = (struct led_classdev_4412 *)led_cdev;
        led_cdev->brightness = brightness;
        if (brightness != LED_OFF)
                gpio_set_value(dev->gpio, 0);
        else
                gpio_set_value(dev->gpio, 1);
}

static int leds_init(void)
{
        int i;
        int ret;
    
        /* 1. alloc led_classdev */
        led_devs = kzalloc(sizeof(struct led_classdev_4412) * sizeof(led_gpios)/sizeof(led_gpios[0]), GFP_KERNEL);
        if (led_devs == NULL) {
                printk("No memory for device\n");
                return -ENOMEM;
        }
        
        for (i = 0; i < sizeof(led_gpios)/sizeof(led_gpios[0]); i++)
        {
                s3c_gpio_cfgpin(led_gpios[i].gpio, S3C_GPIO_OUTPUT);
                gpio_set_value(led_gpios[i].gpio, 1);

                /* 2. set */
                led_devs[i].cdev.max_brightness = LED_FULL;
                led_devs[i].cdev.brightness_set = brightness_set_4412;
                led_devs[i].cdev.flags = LED_CORE_SUSPENDRESUME;
                led_devs[i].cdev.brightness = LED_OFF;
                led_devs[i].cdev.name = led_gpios[i].name;
                //led_devs[i].cdev.default_trigger = "timer";
                led_devs[i].gpio = led_gpios[i].gpio;

                /* 3. led_classdev_register */
                ret = led_classdev_register(NULL, &led_devs[i].cdev);
                if (ret) {
                        i--;
                        while (i >= 0) {
                                led_classdev_unregister(&led_devs[i].cdev);
                                i--;
                        }
                        kfree(led_devs);
                        return -EIO;
                }
        }
        
        return 0;
}

static void leds_exit(void)
{
        int i;
        for (i = 0; i < sizeof(led_gpios)/sizeof(led_gpios[0]); i++)
        {
                led_classdev_unregister(&led_devs[i].cdev);
        }
     kfree(led_devs);
}

module_init(leds_init);
module_exit(leds_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("www.100ask.net");




  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值