android button light 流程分析(一) — driver

buttonandroidstructdelaymodulelist

一、led-class

led-class是button light driver的核心层,代码位于drivers/leds/目录下,提供了一些数据结构和接口,其中创建了一个leds设备类,用于管理系统所有的led,代码如下:

  1. static int __init leds_init(void)  
  2. {  
  3.     leds_class = class_create(THIS_MODULE, "leds");  
  4.     if (IS_ERR(leds_class))  
  5.         return PTR_ERR(leds_class);  
  6.     leds_class->suspend = led_suspend;  
  7.     leds_class->resume = led_resume;  
  8.     return 0;  
  9. }  
  10.   
  11. static void __exit leds_exit(void)  
  12. {  
  13.     class_destroy(leds_class);  
  14. }  
  15.   
  16. subsys_initcall(leds_init);  
  17. module_exit(leds_exit);  
创建成功后我们可以在/sys/class目录下面看到leds目录,并注册了统一的休眠和唤醒函数:
  1. /** 
  2.  * led_classdev_suspend - suspend an led_classdev. 
  3.  * @led_cdev: the led_classdev to suspend. 
  4.  */  
  5. void led_classdev_suspend(struct led_classdev *led_cdev)  
  6. {  
  7.     led_cdev->flags |= LED_SUSPENDED;  
  8.     led_cdev->brightness_set(led_cdev, 0);  
  9. }  
  10. EXPORT_SYMBOL_GPL(led_classdev_suspend);  
  11.   
  12. /** 
  13.  * led_classdev_resume - resume an led_classdev. 
  14.  * @led_cdev: the led_classdev to resume. 
  15.  */  
  16. void led_classdev_resume(struct led_classdev *led_cdev)  
  17. {  
  18.     led_cdev->brightness_set(led_cdev, led_cdev->brightness);  
  19.     led_cdev->flags &= ~LED_SUSPENDED;  
  20. }  
  21. EXPORT_SYMBOL_GPL(led_classdev_resume);  
  22.   
  23. static int led_suspend(struct device *dev, pm_message_t state)  
  24. {  
  25.     struct led_classdev *led_cdev = dev_get_drvdata(dev);  
  26.   
  27.     if (led_cdev->flags & LED_CORE_SUSPENDRESUME)  
  28.         led_classdev_suspend(led_cdev);  
  29.   
  30.     return 0;  
  31. }  
  32.   
  33. static int led_resume(struct device *dev)  
  34. {  
  35.     struct led_classdev *led_cdev = dev_get_drvdata(dev);  
  36.   
  37.     if (led_cdev->flags & LED_CORE_SUSPENDRESUME)  
  38.         led_classdev_resume(led_cdev);  
  39.   
  40.     return 0;  
  41. }  
suspend主要设置一下标志并将led关闭,resume主要清除标志并将led恢复之前亮度。同时led-class提供了结构体led_classdev作为统一的led设备模型:
  1. struct led_classdev {  
  2.     const char      *name;  
  3.     int          brightness;  
  4.     int          max_brightness;  
  5.     int          flags;  
  6.   
  7.     /* Lower 16 bits reflect status */  
  8. #define LED_SUSPENDED       (1 << 0)  
  9.     /* Upper 16 bits reflect control information */  
  10. #define LED_CORE_SUSPENDRESUME  (1 << 16)  
  11.   
  12.     /* Set LED brightness level */  
  13.     /* Must not sleep, use a workqueue if needed */  
  14.     void        (*brightness_set)(struct led_classdev *led_cdev,  
  15.                       enum led_brightness brightness);  
  16.     /* Get LED brightness level */  
  17.     enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);  
  18.   
  19.     /* Activate hardware accelerated blink, delays are in 
  20.      * miliseconds and if none is provided then a sensible default 
  21.      * should be chosen. The call can adjust the timings if it can't 
  22.      * match the values specified exactly. */  
  23.     int     (*blink_set)(struct led_classdev *led_cdev,  
  24.                      unsigned long *delay_on,  
  25.                      unsigned long *delay_off);  
  26.   
  27.     struct device       *dev;  
  28.     struct list_head     node;          /* LED Device list */  
  29.     const char      *default_trigger;   /* Trigger to use */  
  30.   
  31. #ifdef CONFIG_LEDS_TRIGGERS  
  32.     /* Protects the trigger data below */  
  33.     struct rw_semaphore  trigger_lock;  
  34.   
  35.     struct led_trigger  *trigger;  
  36.     struct list_head     trig_list;  
  37.     void            *trigger_data;  
  38. #endif  
  39. };  

其中提供了设置亮度、获取亮度、闪烁led几个接口、还有trigger域以及相关链表节点。在led-class中还定义了brightness、max_brightness两个属性提供给HAL层使用:

  1. static void led_update_brightness(struct led_classdev *led_cdev)  
  2. {  
  3.     if (led_cdev->brightness_get)  
  4.         led_cdev->brightness = led_cdev->brightness_get(led_cdev);  
  5. }  
  6.   
  7. static ssize_t led_brightness_show(struct device *dev,   
  8.         struct device_attribute *attr, char *buf)  
  9. {  
  10.     struct led_classdev *led_cdev = dev_get_drvdata(dev);  
  11.   
  12.     /* no lock needed for this */  
  13.     led_update_brightness(led_cdev);  
  14.   
  15.     return sprintf(buf, "%u\n", led_cdev->brightness);  
  16. }  
  17.   
  18. static ssize_t led_brightness_store(struct device *dev,  
  19.         struct device_attribute *attr, const char *buf, size_t size)  
  20. {  
  21.     struct led_classdev *led_cdev = dev_get_drvdata(dev);  
  22.     ssize_t ret = -EINVAL;  
  23.     char *after;  
  24.     unsigned long state = simple_strtoul(buf, &after, 10);  
  25.     size_t count = after - buf;  
  26.   
  27.     if (*after && isspace(*after))  
  28.         count++;  
  29.   
  30.     if (count == size) {  
  31.         ret = count;  
  32.   
  33.         if (state == LED_OFF)  
  34.             led_trigger_remove(led_cdev);  
  35.         led_set_brightness(led_cdev, state);  
  36.     }  
  37.   
  38.     return ret;  
  39. }  
  40.   
  41. static ssize_t led_max_brightness_show(struct device *dev,  
  42.         struct device_attribute *attr, char *buf)  
  43. {  
  44.     struct led_classdev *led_cdev = dev_get_drvdata(dev);  
  45.   
  46.     return sprintf(buf, "%u\n", led_cdev->max_brightness);  
  47. }  
  48.   
  49. static DEVICE_ATTR(brightness, 0644, led_brightness_show, led_brightness_store);  
  50. static DEVICE_ATTR(max_brightness, 0444, led_max_brightness_show, NULL);  
其中brightness主要用于设置和读取亮度值,max_brightness则用于读取硬件支持的最大亮度值。led-class还提供了注册led_classdev的统一接口:
  1. /** 
  2.  * led_classdev_register - register a new object of led_classdev class. 
  3.  * @parent: The device to register. 
  4.  * @led_cdev: the led_classdev structure for this device. 
  5.  */  
  6. int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)  
  7. {  
  8.     int rc;  
  9.   
  10.     led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,  
  11.                       "%s", led_cdev->name);  // 在leds_class下创建设备  
  12.     if (IS_ERR(led_cdev->dev))  
  13.         return PTR_ERR(led_cdev->dev);  
  14.   
  15.     /* 创建brightness属性文件 */  
  16.     rc = device_create_file(led_cdev->dev, &dev_attr_brightness);  
  17.     if (rc)  
  18.         goto err_out;  
  19.   
  20. #ifdef CONFIG_LEDS_TRIGGERS  
  21.     init_rwsem(&led_cdev->trigger_lock);  
  22. #endif  
  23.     /* 将该设备添加到链表leds_list */  
  24.     down_write(&leds_list_lock);  
  25.     list_add_tail(&led_cdev->node, &leds_list);  
  26.     up_write(&leds_list_lock);  
  27.   
  28.     if (!led_cdev->max_brightness)  
  29.         led_cdev->max_brightness = LED_FULL;  
  30.   
  31.     rc = device_create_file(led_cdev->dev, &dev_attr_max_brightness);  
  32.     if (rc)  
  33.         goto err_out_attr_max;  
  34.   
  35.     led_update_brightness(led_cdev);  // 更新亮度值  
  36.   
  37. #ifdef CONFIG_LEDS_TRIGGERS  
  38.     rc = device_create_file(led_cdev->dev, &dev_attr_trigger);  
  39.     if (rc)  
  40.         goto err_out_led_list;  
  41.   
  42.     led_trigger_set_default(led_cdev);  
  43. #endif  
  44.   
  45.     printk(KERN_INFO "Registered led device: %s\n",  
  46.             led_cdev->name);  
  47.   
  48.     return 0;  
  49.   
  50. #ifdef CONFIG_LEDS_TRIGGERS  
  51. err_out_led_list:  
  52.     device_remove_file(led_cdev->dev, &dev_attr_max_brightness);  
  53. #endif  
  54. err_out_attr_max:  
  55.     device_remove_file(led_cdev->dev, &dev_attr_brightness);  
  56.     list_del(&led_cdev->node);  
  57. err_out:  
  58.     device_unregister(led_cdev->dev);  
  59.     return rc;  
  60. }  
  61. EXPORT_SYMBOL_GPL(led_classdev_register);  
二、led-driver
在平台的驱动中我们注册一个platform led设备作为所有led设备的父设备,注册代码如下:
  1. static struct platform_driver atxxled_driver = {  
  2.     .probe      = atxxled_probe,  
  3.     .remove     = atxxled_remove,  
  4.     .driver     = {  
  5.         .name       = "led",  
  6.         .owner      = THIS_MODULE,  
  7.     },  
  8. };  
  9.   
  10. static int __init atxxled_init(void)  
  11. {  
  12.     return platform_driver_register(&atxxled_driver);  
  13. }  
  14.   
  15. static void __exit atxxled_exit(void)  
  16. {  
  17.     platform_driver_unregister(&atxxled_driver);  
  18. }  
  19.   
  20. module_init(atxxled_init);  
  21. module_exit(atxxled_exit);  
然后再分别注册每一个led:
  1. static void atxxled_set(struct led_classdev *led_cdev, enum ATXX_LED led,  
  2.                    enum led_brightness value)  
  3. {  
  4.     int is_on = (value == LED_OFF) ? 0 : 1;  
  5.     enum pmu_led_module led_module;  
  6.     switch (led) {   
  7.     case RED_LED:  
  8.         led_module = PMU_LED_MODULE_2;  
  9.         break;  
  10.     case GREEN_LED:  
  11.         led_module = PMU_LED_MODULE_1;  
  12.         break;  
  13.     default:  
  14.         return;  
  15.         break;  
  16.     }  
  17.     pmu_led_on_off_set(led_module, is_on, 0);  // led电源控制接口  
  18. }  
  19.   
  20. static void atxxled_green_set(struct led_classdev *led_cdev,  
  21.                    enum led_brightness value)  
  22. {  
  23.     atxxled_set(led_cdev, GREEN_LED, value);  
  24. }  
  25.   
  26. static void atxxled_red_set(struct led_classdev *led_cdev,  
  27.                  enum led_brightness value)  
  28. {  
  29.     atxxled_set(led_cdev, RED_LED, value);  
  30. }  
  31.   
  32. static void atxxled_buttons_set(struct led_classdev *led_cdev,  
  33.                  enum led_brightness value)  
  34. {  
  35.     enum power_supply_mode is_on = (value == LED_OFF) ? PPS_OFF : PPS_ON;  
  36.     pmu_led_on_off_set(PMU_LED_KEY_1, is_on, 40);  
  37. }  
  38.   
  39. static int atxxled_blink_set(struct led_classdev *led_cdev, enum ATXX_LED led,  
  40.             unsigned long *delay_on, unsigned long *delay_off)  
  41. {  
  42.     enum pmu_led_module led_module;  
  43.     switch (led) {   
  44.     case RED_LED:  
  45.         led_module = PMU_LED_MODULE_2;  
  46.         break;  
  47.     case GREEN_LED:  
  48.         led_module = PMU_LED_MODULE_1;  
  49.         break;  
  50.     default:  
  51.         return 0;  
  52.         break;  
  53.     }  
  54.     pmu_led_blink_set(led_module, delay_on, delay_off);  // led闪烁控制接口  
  55.     return 0;  
  56. }  
  57.   
  58. static int atxxled_green_blink_set(struct led_classdev *led_cdev,  
  59.             unsigned long *delay_on, unsigned long *delay_off)  
  60. {  
  61.     return atxxled_blink_set(led_cdev, GREEN_LED, delay_on, delay_off);  
  62. }  
  63.   
  64. static int atxxled_red_blink_set(struct led_classdev *led_cdev,  
  65.             unsigned long *delay_on, unsigned long *delay_off)  
  66. {  
  67.     return atxxled_blink_set(led_cdev, RED_LED, delay_on, delay_off);  
  68. }  
  69.   
  70. static int atxxled_buttons_blink_set(struct led_classdev *led_cdev,  
  71.             unsigned long *delay_on, unsigned long *delay_off)  
  72. {  
  73.     return -1;  
  74. }  
  75.   
  76. static struct led_classdev atxx_red_led = {  
  77.     .name           = "red",  
  78.     .brightness_set = atxxled_red_set,  
  79.     .blink_set      = atxxled_red_blink_set,  
  80.     .flags          = 0,  
  81. };  
  82.   
  83. static struct led_classdev atxx_green_led = {  
  84.     .name           = "green",  
  85.     .brightness_set = atxxled_green_set,  
  86.     .blink_set      = atxxled_green_blink_set,  
  87.     .flags          = 0,  
  88. };  
  89.   
  90. static struct led_classdev atxx_buttons_led = {  
  91.     .name           = "button-backlight",  
  92.     .brightness_set = atxxled_buttons_set,  // led亮度调节  
  93.     .blink_set      = atxxled_buttons_blink_set,  // led闪烁  
  94.     .flags          = 0,  
  95. };  
  96.   
  97. static int atxxled_probe(struct platform_device *pdev)  
  98. {  
  99.     int ret;  
  100.   
  101.     ret = led_classdev_register(&pdev->dev, &atxx_buttons_led);  
  102.     if (ret < 0)  
  103.         return ret;  
  104.   
  105.     ret = led_classdev_register(&pdev->dev, &atxx_red_led);  
  106.     if (ret < 0)  
  107.         return ret;  
  108.       
  109.     ret = led_classdev_register(&pdev->dev, &atxx_green_led);  
  110.     if (ret < 0)  
  111.         led_classdev_unregister(&atxx_red_led);  
  112.   
  113.     return ret;  
  114. }  

三、led-trigger

trigger直译为触发器,即用户有操作时就触发led点亮或者熄灭,通过输入命令:

cat /sys/class/leds/button-backlight/trigger

可以得到系统已经注册的trigger

none nand-disk ac-online USB-online Battery-charging-or-full Battery-charging Battery-full mmc0 mmc1 [timer] rfkill0

用户空间可以直接写值到trigger文件改变led的触发方式。trigger的数据结构如下:

  1. struct led_trigger {  
  2.     /* Trigger Properties */  
  3.     const char   *name;  
  4.     void        (*activate)(struct led_classdev *led_cdev);  
  5.     void        (*deactivate)(struct led_classdev *led_cdev);  
  6.   
  7.     /* LEDs under control by this trigger (for simple triggers) */  
  8.     rwlock_t      leddev_list_lock;  
  9.     struct list_head  led_cdevs;  
  10.   
  11.     /* Link to next registered trigger */  
  12.     struct list_head  next_trig;  
  13. };  

分别为trigger名称、激活以及分离接口、读写锁、led设备链表头、trigger_list链表节点。下面是led-trigger核心部分源码的分析:

  1. static DECLARE_RWSEM(triggers_list_lock);  // trigger链表读写锁  
  2. static LIST_HEAD(trigger_list);  // trigger链表  
  3.   
  4. // 用户空间写操作函数  
  5. ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,  
  6.         const char *buf, size_t count)  
  7. {  
  8.     struct led_classdev *led_cdev = dev_get_drvdata(dev);  
  9.     char trigger_name[TRIG_NAME_MAX];  
  10.     struct led_trigger *trig;  
  11.     size_t len;  
  12.     // 获取trigger_name的有效长度  
  13.     trigger_name[sizeof(trigger_name) - 1] = '\0';  
  14.     strncpy(trigger_name, buf, sizeof(trigger_name) - 1);  
  15.     len = strlen(trigger_name);  
  16.   
  17.     if (len && trigger_name[len - 1] == '\n')  
  18.         trigger_name[len - 1] = '\0';  
  19.     // 比较是否为none,如果是则将led_cdev->trigger清除  
  20.     if (!strcmp(trigger_name, "none")) {  
  21.         led_trigger_remove(led_cdev);  
  22.         return count;  
  23.     }  
  24.   
  25.     down_read(&triggers_list_lock);  
  26.     // 遍历trigger_list,如果本trigger在trigger_list中有注册  
  27.     // 则调用led_trigger_set()将本设备添加到trigger的设备链表中  
  28.     list_for_each_entry(trig, &trigger_list, next_trig) {  
  29.         if (!strcmp(trigger_name, trig->name)) {  
  30.             down_write(&led_cdev->trigger_lock);  
  31.             led_trigger_set(led_cdev, trig);  
  32.             up_write(&led_cdev->trigger_lock);  
  33.   
  34.             up_read(&triggers_list_lock);  
  35.             return count;  
  36.         }  
  37.     }  
  38.     up_read(&triggers_list_lock);  
  39.   
  40.     return -EINVAL;  
  41. }  
  42. EXPORT_SYMBOL_GPL(led_trigger_store);  
  43. // 用户空间读操作函数  
  44. ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,  
  45.         char *buf)  
  46. {  
  47.     struct led_classdev *led_cdev = dev_get_drvdata(dev);  
  48.     struct led_trigger *trig;  
  49.     int len = 0;  
  50.   
  51.     down_read(&triggers_list_lock);  
  52.     down_read(&led_cdev->trigger_lock);  
  53.     // 如果本设备不存在trigger,则显示[none]  
  54.     if (!led_cdev->trigger)  
  55.         len += sprintf(buf+len, "[none] ");  
  56.     else  
  57.         len += sprintf(buf+len, "none ");  
  58.     // 遍历trigger_list,如果本设备当前trigger在链表中存在则用"[xxx]"显示  
  59.     // 如果不存在则用"xxx"显示链表中其余的trigger  
  60.     list_for_each_entry(trig, &trigger_list, next_trig) {  
  61.         if (led_cdev->trigger && !strcmp(led_cdev->trigger->name,  
  62.                             trig->name))  
  63.             len += sprintf(buf+len, "[%s] ", trig->name);  
  64.         else  
  65.             len += sprintf(buf+len, "%s ", trig->name);  
  66.     }  
  67.     up_read(&led_cdev->trigger_lock);  
  68.     up_read(&triggers_list_lock);  
  69.   
  70.     len += sprintf(len+buf, "\n");  
  71.     return len;  
  72. }  
  73. EXPORT_SYMBOL_GPL(led_trigger_show);  
  74.   
  75. // trigger设置函数,调用者需要持有led_cdev->trigger_lock锁  
  76. void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger)  
  77. {  
  78.     unsigned long flags;  
  79.   
  80.     // 如果当前设备存在trigger,则清除,保证一个设备只有一个trigger  
  81.     if (led_cdev->trigger) {  
  82.         write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);  
  83.         list_del(&led_cdev->trig_list);  // 将当前设备从旧trigger的设备链表中删除  
  84.         write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock,  
  85.             flags);  
  86.         if (led_cdev->trigger->deactivate)  
  87.             led_cdev->trigger->deactivate(led_cdev);  // 执行旧trigger分离函数  
  88.         led_cdev->trigger = NULL;  // 将当前设备的trigger域清空  
  89.         led_set_brightness(led_cdev, LED_OFF);  // 关闭设备  
  90.     }  
  91.     // 为设备添加一个新trigger  
  92.     if (trigger) {  
  93.         write_lock_irqsave(&trigger->leddev_list_lock, flags);  
  94.         // 将当前设备添加到新trigger的设备链表中  
  95.         list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs);  
  96.         write_unlock_irqrestore(&trigger->leddev_list_lock, flags);  
  97.         // 设置设备的trigger域并执行新trigger的激活函数  
  98.         led_cdev->trigger = trigger;  
  99.         if (trigger->activate)  
  100.             trigger->activate(led_cdev);  
  101.     }  
  102. }  
  103. EXPORT_SYMBOL_GPL(led_trigger_set);  
  104. // trigger移除函数  
  105. void led_trigger_remove(struct led_classdev *led_cdev)  
  106. {  
  107.     down_write(&led_cdev->trigger_lock);  
  108.     led_trigger_set(led_cdev, NULL);  
  109.     up_write(&led_cdev->trigger_lock);  
  110. }  
  111. EXPORT_SYMBOL_GPL(led_trigger_remove);  
  112. // 设置设备的默认trigger  
  113. void led_trigger_set_default(struct led_classdev *led_cdev)  
  114. {  
  115.     struct led_trigger *trig;  
  116.   
  117.     if (!led_cdev->default_trigger)  
  118.         return;  
  119.   
  120.     down_read(&triggers_list_lock);  
  121.     down_write(&led_cdev->trigger_lock);  
  122.     // 如果设备默认的trigger在trigger_list有注册,则设置trigger  
  123.     list_for_each_entry(trig, &trigger_list, next_trig) {  
  124.         if (!strcmp(led_cdev->default_trigger, trig->name))  
  125.             led_trigger_set(led_cdev, trig);  
  126.     }  
  127.     up_write(&led_cdev->trigger_lock);  
  128.     up_read(&triggers_list_lock);  
  129. }  
  130. EXPORT_SYMBOL_GPL(led_trigger_set_default);  
  131.   
  132. // 注册一个新的trigger到trigger_list中  
  133. int led_trigger_register(struct led_trigger *trigger)  
  134. {  
  135.     struct led_classdev *led_cdev;  
  136.     struct led_trigger *trig;  
  137.   
  138.     rwlock_init(&trigger->leddev_list_lock);  // 初始化读写锁  
  139.     INIT_LIST_HEAD(&trigger->led_cdevs);  // 初始化设备链表  
  140.   
  141.     down_write(&triggers_list_lock);  
  142.     // 确认trigger是否注册过  
  143.     list_for_each_entry(trig, &trigger_list, next_trig) {  
  144.         if (!strcmp(trig->name, trigger->name)) {  
  145.             up_write(&triggers_list_lock);  
  146.             return -EEXIST;  
  147.         }  
  148.     }  
  149.     // 将trigger添加到trigger_list中  
  150.     list_add_tail(&trigger->next_trig, &trigger_list);  
  151.     up_write(&triggers_list_lock);  
  152.   
  153.     // 遍历当前已经注册的led,如果led的默认trigger是当前注册的trigger  
  154.     // 则调用led_trigger_set()将led添加到trigger的设备链表中  
  155.     down_read(&leds_list_lock);  
  156.     list_for_each_entry(led_cdev, &leds_list, node) {  
  157.         down_write(&led_cdev->trigger_lock);  
  158.         if (!led_cdev->trigger && led_cdev->default_trigger &&  
  159.                 !strcmp(led_cdev->default_trigger, trigger->name))  
  160.             led_trigger_set(led_cdev, trigger);  
  161.         up_write(&led_cdev->trigger_lock);  
  162.     }  
  163.     up_read(&leds_list_lock);  
  164.   
  165.     return 0;  
  166. }  
  167. EXPORT_SYMBOL_GPL(led_trigger_register);  
  168. // 注销trigger  
  169. void led_trigger_unregister(struct led_trigger *trigger)  
  170. {  
  171.     struct led_classdev *led_cdev;  
  172.   
  173.     // 将trigger从trigger_list中删除  
  174.     down_write(&triggers_list_lock);  
  175.     list_del(&trigger->next_trig);  
  176.     up_write(&triggers_list_lock);  
  177.   
  178.     // 遍历当前已经注册的led,如果led使用的trigger是当前要删除的trigger  
  179.     // 则调用led_trigger_set()将led的trigger域清空  
  180.     down_read(&leds_list_lock);  
  181.     list_for_each_entry(led_cdev, &leds_list, node) {  
  182.         down_write(&led_cdev->trigger_lock);  
  183.         if (led_cdev->trigger == trigger)  
  184.             led_trigger_set(led_cdev, NULL);  
  185.         up_write(&led_cdev->trigger_lock);  
  186.     }  
  187.     up_read(&leds_list_lock);  
  188. }  
  189. EXPORT_SYMBOL_GPL(led_trigger_unregister);  
  190.   
  191. // 统一设置本trigger下所有led的亮度  
  192. void led_trigger_event(struct led_trigger *trigger,  
  193.             enum led_brightness brightness)  
  194. {  
  195.     struct list_head *entry;  
  196.   
  197.     if (!trigger)  
  198.         return;  
  199.   
  200.     read_lock(&trigger->leddev_list_lock);  
  201.     // 遍历当前trigger的设备链表,并设置每个设备的亮度  
  202.     list_for_each(entry, &trigger->led_cdevs) {  
  203.         struct led_classdev *led_cdev;  
  204.   
  205.         led_cdev = list_entry(entry, struct led_classdev, trig_list);  
  206.         led_set_brightness(led_cdev, brightness);  
  207.     }  
  208.     read_unlock(&trigger->leddev_list_lock);  
  209. }  
  210. EXPORT_SYMBOL_GPL(led_trigger_event);  
  211. // 简单注册一个新的trigger  
  212. void led_trigger_register_simple(const char *name, struct led_trigger **tp)  
  213. {  
  214.     struct led_trigger *trigger;  
  215.     int err;  
  216.   
  217.     trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);  
  218.   
  219.     if (trigger) {  
  220.         trigger->name = name;  
  221.         err = led_trigger_register(trigger);  
  222.         if (err < 0)  
  223.             printk(KERN_WARNING "LED trigger %s failed to register"  
  224.                 " (%d)\n", name, err);  
  225.     } else  
  226.         printk(KERN_WARNING "LED trigger %s failed to register"  
  227.             " (no memory)\n", name);  
  228.   
  229.     *tp = trigger;  
  230. }  
  231. EXPORT_SYMBOL_GPL(led_trigger_register_simple);  
  232. // 简单注销一个trigger  
  233. void led_trigger_unregister_simple(struct led_trigger *trigger)  
  234. {  
  235.     if (trigger)  
  236.         led_trigger_unregister(trigger);  
  237.     kfree(trigger);  
  238. }  
  239. EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);  

核心层主要维护了trigger_list链表,保存了系统中注册的所有trigger;每个trigger都有自己的led设备链表,用于统一控制该trigger下所有led的亮度;同时每个led设备都有自己的trigger域,用于指向相应的trigger,如果trigger被触发则会调用相应trigger的激活函数。

四、ledtrig-timer
ledtrig-timer作为timer触发器,用于控制led闪烁。led-trigtimer通过led-trigger核心层调用会在/sys/class/leds/button-backlight创建delay_on、delay_off属性文件来控制闪烁时间。代码分析如下:
  1. struct timer_trig_data {  
  2.     int brightness_on;      /* LED brightness during "on" period. 
  3.                      * (LED_OFF < brightness_on <= LED_FULL) 
  4.                      */  
  5.     unsigned long delay_on;     /* milliseconds on */  
  6.     unsigned long delay_off;    /* milliseconds off */  
  7.     struct timer_list timer;  
  8. };  
  9.   
  10. static void led_timer_function(unsigned long data)  
  11. {  
  12.     struct led_classdev *led_cdev = (struct led_classdev *) data;  
  13.     struct timer_trig_data *timer_data = led_cdev->trigger_data;  
  14.     unsigned long brightness;  
  15.     unsigned long delay;  
  16.     // 如果delay_on或者delay_off为0则关闭led  
  17.     if (!timer_data->delay_on || !timer_data->delay_off) {  
  18.         led_set_brightness(led_cdev, LED_OFF);  
  19.         return;  
  20.     }  
  21.     // 获取当前亮度值  
  22.     brightness = led_get_brightness(led_cdev);  
  23.     if (!brightness) {  
  24.         // 如果当前led为关闭状态则设置led到默认亮度值  
  25.         brightness = timer_data->brightness_on;  
  26.         delay = timer_data->delay_on;  
  27.     } else {  
  28.         // 如果当前led为开启状态则关闭led并保存当前亮度值  
  29.         timer_data->brightness_on = brightness;  
  30.         brightness = LED_OFF;  
  31.         delay = timer_data->delay_off;  
  32.     }  
  33.     // 设置亮度值,因为此时在soft irq中,所以不能调用耗时函数  
  34.     led_set_brightness(led_cdev, brightness);  
  35.     // 在delay后更新led状态,实现闪烁  
  36.     mod_timer(&timer_data->timer, jiffies + msecs_to_jiffies(delay));  
  37. }  
  38. // 显示delay_on参数  
  39. static ssize_t led_delay_on_show(struct device *dev,  
  40.         struct device_attribute *attr, char *buf)  
  41. {  
  42.     struct led_classdev *led_cdev = dev_get_drvdata(dev);  
  43.     struct timer_trig_data *timer_data = led_cdev->trigger_data;  
  44.   
  45.     return sprintf(buf, "%lu\n", timer_data->delay_on);  
  46. }  
  47. // 存储delay_on参数  
  48. static ssize_t led_delay_on_store(struct device *dev,  
  49.         struct device_attribute *attr, const char *buf, size_t size)  
  50. {  
  51.     struct led_classdev *led_cdev = dev_get_drvdata(dev);  
  52.     struct timer_trig_data *timer_data = led_cdev->trigger_data;  
  53.     int ret = -EINVAL;  
  54.     char *after;  
  55.     unsigned long state = simple_strtoul(buf, &after, 10);  
  56.     size_t count = after - buf;  
  57.   
  58.     if (*after && isspace(*after))  
  59.         count++;  
  60.   
  61.     if (count == size) {  
  62.         if (timer_data->delay_on != state) {  
  63.             // 如果delay_on值不同则保存新值  
  64.             timer_data->delay_on = state;  
  65.   
  66.             // 删除之前的timer  
  67.             del_timer_sync(&timer_data->timer);  
  68.   
  69.             // 尝试调用blink_set()设置led  
  70.             if (!led_cdev->blink_set ||  
  71.                 led_cdev->blink_set(led_cdev,  
  72.                   &timer_data->delay_on, &timer_data->delay_off)) {  
  73.                 // 如果没有或者设置失败就调用timer设置led  
  74.                 mod_timer(&timer_data->timer, jiffies + 1);  
  75.             }  
  76.         }  
  77.         ret = count;  
  78.     }  
  79.   
  80.     return ret;  
  81. }  
  82. // 显示delay_off参数  
  83. static ssize_t led_delay_off_show(struct device *dev,  
  84.         struct device_attribute *attr, char *buf)  
  85. {  
  86.     struct led_classdev *led_cdev = dev_get_drvdata(dev);  
  87.     struct timer_trig_data *timer_data = led_cdev->trigger_data;  
  88.   
  89.     return sprintf(buf, "%lu\n", timer_data->delay_off);  
  90. }  
  91. // 存储delay_off参数  
  92. static ssize_t led_delay_off_store(struct device *dev,  
  93.         struct device_attribute *attr, const char *buf, size_t size)  
  94. {  
  95.     struct led_classdev *led_cdev = dev_get_drvdata(dev);  
  96.     struct timer_trig_data *timer_data = led_cdev->trigger_data;  
  97.     int ret = -EINVAL;  
  98.     char *after;  
  99.     unsigned long state = simple_strtoul(buf, &after, 10);  
  100.     size_t count = after - buf;  
  101.   
  102.     if (*after && isspace(*after))  
  103.         count++;  
  104.   
  105.     if (count == size) {  
  106.         if (timer_data->delay_off != state) {  
  107.             /* the new value differs from the previous */  
  108.             timer_data->delay_off = state;  
  109.   
  110.             /* deactivate previous settings */  
  111.             del_timer_sync(&timer_data->timer);  
  112.   
  113.             /* try to activate hardware acceleration, if any */  
  114.             if (!led_cdev->blink_set ||  
  115.                 led_cdev->blink_set(led_cdev,  
  116.                   &timer_data->delay_on, &timer_data->delay_off)) {  
  117.                 /* no hardware acceleration, blink via timer */  
  118.                 mod_timer(&timer_data->timer, jiffies + 1);  
  119.             }  
  120.         }  
  121.         ret = count;  
  122.     }  
  123.   
  124.     return ret;  
  125. }  
  126. // 属性文件delay_on,delay_off  
  127. static DEVICE_ATTR(delay_on, 0644, led_delay_on_show, led_delay_on_store);  
  128. static DEVICE_ATTR(delay_off, 0644, led_delay_off_show, led_delay_off_store);  
  129. // timer_led_trigger的激活函数  
  130. static void timer_trig_activate(struct led_classdev *led_cdev)  
  131. {  
  132.     struct timer_trig_data *timer_data;  
  133.     int rc;  
  134.     // 申请timer_trig_data的内存空间  
  135.     timer_data = kzalloc(sizeof(struct timer_trig_data), GFP_KERNEL);  
  136.     if (!timer_data)  
  137.         return;  
  138.     // 获取led亮度  
  139.     timer_data->brightness_on = led_get_brightness(led_cdev);  
  140.     // 如果为0为设置为最大值  
  141.     if (timer_data->brightness_on == LED_OFF)  
  142.         timer_data->brightness_on = led_cdev->max_brightness;  
  143.     // 赋值led的trigger_data域  
  144.     led_cdev->trigger_data = timer_data;  
  145.     // 初始化timer  
  146.     init_timer(&timer_data->timer);  
  147.     timer_data->timer.function = led_timer_function;  
  148.     timer_data->timer.data = (unsigned long) led_cdev;  
  149.     // 创建属性文件delay_on,delay_off  
  150.     rc = device_create_file(led_cdev->dev, &dev_attr_delay_on);  
  151.     if (rc)  
  152.         goto err_out;  
  153.     rc = device_create_file(led_cdev->dev, &dev_attr_delay_off);  
  154.     if (rc)  
  155.         goto err_out_delayon;  
  156.   
  157.     /* If there is hardware support for blinking, start one 
  158.      * user friendly blink rate chosen by the driver. 
  159.      */  
  160.     // 如果硬件支持led闪烁,则开始使用默认值闪烁  
  161.     if (led_cdev->blink_set)  
  162.         led_cdev->blink_set(led_cdev,  
  163.             &timer_data->delay_on, &timer_data->delay_off);  
  164.   
  165.     return;  
  166.   
  167. err_out_delayon:  
  168.     device_remove_file(led_cdev->dev, &dev_attr_delay_on);  
  169. err_out:  
  170.     led_cdev->trigger_data = NULL;  
  171.     kfree(timer_data);  
  172. }  
  173. // timer_led_trigger的分离函数  
  174. static void timer_trig_deactivate(struct led_classdev *led_cdev)  
  175. {  
  176.     struct timer_trig_data *timer_data = led_cdev->trigger_data;  
  177.     unsigned long on = 0, off = 0;  
  178.     // 删除属性文件、timer、释放内存  
  179.     if (timer_data) {  
  180.         device_remove_file(led_cdev->dev, &dev_attr_delay_on);  
  181.         device_remove_file(led_cdev->dev, &dev_attr_delay_off);  
  182.         del_timer_sync(&timer_data->timer);  
  183.         kfree(timer_data);  
  184.     }  
  185.   
  186.     // 停止闪烁  
  187.     if (led_cdev->blink_set)  
  188.         led_cdev->blink_set(led_cdev, &on, &off);  
  189. }  
  190. // timer_led_trigger注册函数  
  191. static struct led_trigger timer_led_trigger = {  
  192.     .name     = "timer",  
  193.     .activate = timer_trig_activate,  
  194.     .deactivate = timer_trig_deactivate,  
  195. };  
  196.   
  197. static int __init timer_trig_init(void)  
  198. {  
  199.     return led_trigger_register(&timer_led_trigger);  
  200. }  
  201.   
  202. static void __exit timer_trig_exit(void)  
  203. {  
  204.     led_trigger_unregister(&timer_led_trigger);  
  205. }  

这个timer触发器的实现有个bug,是在soft irq函数中去设置亮度,如果平台控制led亮度比较复杂,比如说通过i2c设备,将会引起内核crash,所以需要创建一个工作队列来实现亮度控制。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值