Skip to main content
 首页 » 编程设计

Android灯光系统(1)——led_class驱动实现

2022年07月19日22Leo_wl

1.对灯光的要求

(1)亮度可调节,如背光灯
(2)颜色可变化,如电池电量指示灯
(3)能闪烁,如通知灯


2.Linux内核对led的支持

在Linux内核中已经实现了一个led class, 它里面已经实现了brightness的调节和blink,文件为:drivers/leds/led-class.c
在ledinit()中的led_class_attrs在/sys/class/leds下创建了brightness max_brightness trigger文件。


3.使用Linux内核中的led class
(1)配置内核
make menuconfig选中:
CONFIG_LEDS_CLASS
CONFIG_LEDS_TRIGGERS
CONFIG_LEDS_TRIGGER_TIMER
(2)驱动编写流程
a.分配led_classdev结构体
b.设置
c.注册
可以参考leds-s3c24xx.c,驱动Demo如下:

#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; 
}; 
 
/*三星的goio引脚根本就不需要获取就可以直接使用*/ 
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; /*每一个led绑定的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); /*打开led*/ 
    else 
        gpio_set_value(dev->gpio, 1); /*关闭led*/ 
} 
 
 
static int leds_init(void) 
{ 
    int i; 
    int ret; 
 
    /* 1. alloc led_classdev */ 
    led_devs = kzalloc(sizeof(struct led_classdev_4412) * ARRAY_SIZE(led_gpios), GFP_KERNEL); 
    if (led_devs == NULL) { 
        printk("No memory for device\n"); 
        return -ENOMEM; 
    } 
 
    for (i = 0; i < ARRAY_SIZE(led_gpios); i++) 
    { 
        s3c_gpio_cfgpin(led_gpios[i].gpio, S3C_GPIO_OUTPUT); 
        /*默认led全部关闭*/ 
        gpio_set_value(led_gpios[i].gpio, 1); 
 
        /* 2. set 这里面哪些域需要设置由led-class.c的实现决定的*/ 
        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].gpio = led_gpios[i].gpio; 
 
        //led_devs[i].cdev.default_trigger = "timer"; 
        //led_devs[i].cdev.blink_delay_on = 100; 
        //led_devs[i].cdev.blink_delay_off = 900; 
 
        /* 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 < ARRAY_SIZE(led_gpios); i++) 
    { 
        led_classdev_unregister(&led_devs[i].cdev); 
    } 
    kfree(led_devs); 
} 
 
module_init(leds_init); 
module_exit(leds_exit); 
 
MODULE_LICENSE("GPL"); 
MODULE_AUTHOR("Mr.Sun");

重新编译内核启动后,会在/sys/class/leds下看到led1——led4

4.应用程序设置方法
应用程序是通过sysfs文件来控制led的。

# echo 255 > /sys/class/leds/led1/brightness 点亮
# echo 0 > /sys/class/leds/led1/brightness 熄灭

# echo timmer > /sys/class/leds/led1/trigger 此时led1就应该在以1HZ频率闪烁了。之后此目录下会多出delay_on/delay_off两个文件
配置它两个可以配置闪烁行为。

echo none > trigger 和 echo 0 > brightness 都会使led不在依附trigger。

5.也可以在驱动代码里面设置默认的trigger,让led一上电就开始闪动。

6.这个显示效果还是可以借鉴一下的,由led_trigger_show()实现的,括起来的是led目前attach的trigger,其它是系统中还存在的trigger。
shell@tiny4412:/sys/class/leds/led1 # cat trigger
none mmc0 mmc1 mmc2 [timer]


7.从这里看不但可以指定class的sysfs属性文件,还可以指定dev的!

struct class { 
    ... 
    struct class_attribute  *class_attrs; 
    struct device_attribute *dev_attrs; 
    struct bin_attribute    *dev_bin_attrs; 
    ... 
}

class_create()时指定的属性在每一个依此class创建的设备文件的sysfs目录中都存在。见leds-class.c中的leds_init()。

8.device_create()只是创建设备模型,并不会在/dev/下创建设备文件
如led_classdev_register中调用了device_create但是并没有/dev/下的设备文件。
led_cdev->dev = device_create(leds_class, parent, 0, led_cdev, "%s", led_cdev->name); //无论arg3是否为0测试都不会创建设备文件。


9.list_for_each_entry的使用

来自led_trigger_show():
trig: 空闲变量指针
trigger_list: 单链表的链表头
next_trig: 构成链表的next指针成员
list_for_each_entry(trig, &trigger_list, next_trig) {use trig...}


本文参考链接:https://www.cnblogs.com/hellokitty2/p/10812183.html