本例将根据上一篇 《Linux下led子系统之分析篇》分析的led子系统框架去讲述如何在子系统中注册一个 led 设备,前提是通过 make menucon 将 device driver 下面的 LED Support 和它下面的 LED class support 及相应的 trigger 打开.
具体步骤分三步:

1. 分配 led_classdev 结构体所需的资源

     static struct led_classdev *led_devs;
     led_devs = kzalloc(sizeof(struct led_classdev), GFP_KERNEL);
     if (led_devs == NULL)
     {
         printk("alex.han %s:%d led_devs alloc error\n", __func__, __LINE__);
         return -1;
     }

2. 初始化这个结构体变量

     //设置led的最大亮度  LED_FULL 在 leds.h 中定义,为 255(有些led是可以通过控制电流来控制亮度的)
     led_devs->max_brightness = LED_FULL;
     //设置led的默认亮度,LED_HALF在leds.h中定义,为127,如果不设置默认为0
     led_devs->brightness = LED_HALF;
     led_devs->flags = LED_CORE_SUSPENDRESUME;
     //这个led设备的名字,注册后将会在/sys/class/leds/目录下创建 xxx 设备目录
     led_devs->name = "xxx";
     //设置默认的trigger,如果不设置则默认trigger为0, 如果不需要trigger,这个地方可以不设置
     led_devs->default_trigger = "timer"; //默认trigger为timer
     //设置亮度的函数,当我们通过sys文件系统来调节led亮度的时候,会调用这个函数,当我们设置了trigger,对应的trigger也会调用这个函数
     led_devs->brightness_set = my_brightness_set;
     //delay_on和delay_off表示默认led闪烁的频率,只有在使用timer这个trigger的时候才有效,表示led亮的时间和灭的时间,从而来控制闪烁频率,单位是ms
     led_devs->blink_delay_on = 1000;
     led_devs->blink_delay_off = 2000;
     //设置闪烁时led的亮度
     led_devs->blink_brightness = 100;
static void my_brightness_set(struct led_classdev * led_cdev, enum led_brightness brightness)
{
  struct led_device * dev = (struct led_device *)led_cdev;
  led_cdev->brightness = brightness;

  printk("alex.han %s %d brightness = %d gpio = %d\n", __func__, __LINE__, brightness, dev->gpio);
  /*
     这个地方要实现你自己的的led设备的亮和灭或者是设置亮度操作
     比如:
      如果你的led设备是用一个gpio进行简单控制,那么这个地方对你来说brightness就是亮和灭的开个,brightness=0就设置灯亮,否则就设置led灭
      如果你的led设备使用一个中间芯片来控制的(比如lp5523,可以通过iic控制lp5523芯片从而来控制led的亮度),同时又是通过控制电流来控制亮度,那么就需要调用i2c_write将需要设置的内容写到对应的芯片中,
  */
}

3. 注册这个结构体

//调用led_class.c中的注册函数,将初始化的led_classdev结构体注册到led子系统中,创建对应的设备节点
led_classdev_register(NULL, led_devs);

测试:

将上述框架添加到一个模块中,编译到 kernel 中,并 make menuconfig 打开相应的宏,重新烧写 image , 进入 /sys/class/ 目录会发现有 leds 目录,进入 leds 目录会发现我们注册的 xxx 设备,进入 xxx 目录会发现有 brightness max_brightness trigger 等属性。

cat brightness 会打印出我们设置的默认的brightness值,
echo 100 > brightness根据log会发现我们驱动的my_brightness_set函数被调用,
cat trigger会发现打印出很多的触发器(如果你在make menuconfig去将相应的trigger添加的话),并且如果你没有设置default_trigger的话在none的这个触发器上加了[],表示我们当前没有添加触发器, 这时如何你echo timer > trigger然后在cat trigger会发现[]加在了timer上面,表示当前的触发器是timer,并且在当前目录下生成了delay_on和delay_off两个文件,分别cat会发现打印的值和我们设置的值一样,同时看log会发现我们的my_brightness_set函数被不断的调用,brightness的值是0和default_brightness的值(表示led处于闪烁状态).

最后附上我自己的实例代码,虚拟了3个led:

/*************************************************************************
    > File Name: led-test.c
    > Author:
    > Mail:
    > Created Time: 2018年01月02日 星期二 18时37分17秒
 ************************************************************************/

#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/cdev.h>
#include <linux/delay.h>
#include <linux/leds.h>

struct led_desc {
    int gpio;
    char * name;
};
/* 虚拟了3个led */
static struct led_desc led_gpios[] = {
    {1, "led1"},
    {2, "led2"},
    {3, "led3"},
};

struct led_device {
    struct led_classdev cdev;
    int gpio;
};
static struct led_device * led_devs = NULL;

static void my_brightness_set(struct led_classdev * led_cdev, enum led_brightness brightness)
{
    struct led_device * dev = (struct led_device *)led_cdev;
    led_cdev->brightness = brightness;

    printk("alex.han %s %d brightness = %d gpio = %d\n", __func__, __LINE__, brightness, dev->gpio);
}

static int myled_init(void)
{
    int i;
    int ret;

    printk("alex.han %s %d\n", __func__, __LINE__);

    led_devs = kzalloc(sizeof(struct led_device) * sizeof(led_gpios) / sizeof(led_gpios[0]), GFP_KERNEL);
    if (led_devs == NULL)
    {
        printk("alex.han %s:%d led_devs alloc error\n", __func__, __LINE__);
        return -1;
    }

    for (i = 0; i < (sizeof(led_gpios) / sizeof(led_gpios[0])); i++)
    {
        led_devs[i].cdev.max_brightness = LED_FULL;
        led_devs[i].cdev.brightness = LED_HALF;
        led_devs[i].cdev.flags = LED_CORE_SUSPENDRESUME;
        led_devs[i].cdev.name = led_gpios[i].name;
        led_devs[i].cdev.default_trigger = "timer"; //默认trigger为timer
        led_devs[i].gpio = led_gpios[i].gpio; // gpio端口号
        led_devs[i].cdev.brightness_set = my_brightness_set;
        led_devs[i].cdev.blink_delay_on = 1000;
        led_devs[i].cdev.blink_delay_off = 2000;
        led_devs[i].cdev.blink_brightness = 100;

        ret = led_classdev_register(NULL, &led_devs[i].cdev);
        if (ret < 0)
        {
            i--;
            while (i >= 0)
            {
                i--;

                printk("alex.han %s %d register err\n", __func__, __LINE__);
                led_classdev_unregister(&led_devs[i].cdev);
            }
            kfree(led_devs);

            return -1;
        }
    }



    return 0;
}

static void myled_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(myled_init);
module_exit(myled_exit);

作者: hanp_linux
https://blog.csdn.net/hanp_linux/article/details/79037684