buttonandroidstructdelaymodulelist
一、led-class
led-class是button light driver的核心层,代码位于drivers/leds/目录下,提供了一些数据结构和接口,其中创建了一个leds设备类,用于管理系统所有的led,代码如下:
- 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;
- return 0;
- }
- static void __exit leds_exit(void)
- {
- class_destroy(leds_class);
- }
- subsys_initcall(leds_init);
- module_exit(leds_exit);
- /**
- * led_classdev_suspend - suspend an led_classdev.
- * @led_cdev: the led_classdev to suspend.
- */
- void led_classdev_suspend(struct led_classdev *led_cdev)
- {
- led_cdev->flags |= LED_SUSPENDED;
- led_cdev->brightness_set(led_cdev, 0);
- }
- EXPORT_SYMBOL_GPL(led_classdev_suspend);
- /**
- * led_classdev_resume - resume an led_classdev.
- * @led_cdev: the led_classdev to resume.
- */
- void led_classdev_resume(struct led_classdev *led_cdev)
- {
- led_cdev->brightness_set(led_cdev, led_cdev->brightness);
- led_cdev->flags &= ~LED_SUSPENDED;
- }
- EXPORT_SYMBOL_GPL(led_classdev_resume);
- static int led_suspend(struct device *dev, pm_message_t state)
- {
- struct led_classdev *led_cdev = dev_get_drvdata(dev);
- if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
- led_classdev_suspend(led_cdev);
- return 0;
- }
- static int led_resume(struct device *dev)
- {
- struct led_classdev *led_cdev = dev_get_drvdata(dev);
- if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
- led_classdev_resume(led_cdev);
- return 0;
- }
- 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
- };
其中提供了设置亮度、获取亮度、闪烁led几个接口、还有trigger域以及相关链表节点。在led-class中还定义了brightness、max_brightness两个属性提供给HAL层使用:
- static void led_update_brightness(struct led_classdev *led_cdev)
- {
- if (led_cdev->brightness_get)
- led_cdev->brightness = led_cdev->brightness_get(led_cdev);
- }
- 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);
- }
- 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 (*after && 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;
- }
- static ssize_t led_max_brightness_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct led_classdev *led_cdev = dev_get_drvdata(dev);
- return sprintf(buf, "%u\n", led_cdev->max_brightness);
- }
- static DEVICE_ATTR(brightness, 0644, led_brightness_show, led_brightness_store);
- static DEVICE_ATTR(max_brightness, 0444, led_max_brightness_show, NULL);
- /**
- * led_classdev_register - register a new object of led_classdev class.
- * @parent: The device to register.
- * @led_cdev: the led_classdev structure for this device.
- */
- int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
- {
- int rc;
- led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
- "%s", led_cdev->name); // 在leds_class下创建设备
- if (IS_ERR(led_cdev->dev))
- return PTR_ERR(led_cdev->dev);
- /* 创建brightness属性文件 */
- rc = device_create_file(led_cdev->dev, &dev_attr_brightness);
- if (rc)
- goto err_out;
- #ifdef CONFIG_LEDS_TRIGGERS
- init_rwsem(&led_cdev->trigger_lock);
- #endif
- /* 将该设备添加到链表leds_list */
- 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;
- rc = device_create_file(led_cdev->dev, &dev_attr_max_brightness);
- if (rc)
- goto err_out_attr_max;
- led_update_brightness(led_cdev); // 更新亮度值
- #ifdef CONFIG_LEDS_TRIGGERS
- rc = device_create_file(led_cdev->dev, &dev_attr_trigger);
- if (rc)
- goto err_out_led_list;
- led_trigger_set_default(led_cdev);
- #endif
- printk(KERN_INFO "Registered led device: %s\n",
- led_cdev->name);
- return 0;
- #ifdef CONFIG_LEDS_TRIGGERS
- err_out_led_list:
- device_remove_file(led_cdev->dev, &dev_attr_max_brightness);
- #endif
- err_out_attr_max:
- device_remove_file(led_cdev->dev, &dev_attr_brightness);
- list_del(&led_cdev->node);
- err_out:
- device_unregister(led_cdev->dev);
- return rc;
- }
- EXPORT_SYMBOL_GPL(led_classdev_register);
在平台的驱动中我们注册一个platform led设备作为所有led设备的父设备,注册代码如下:
- static struct platform_driver atxxled_driver = {
- .probe = atxxled_probe,
- .remove = atxxled_remove,
- .driver = {
- .name = "led",
- .owner = THIS_MODULE,
- },
- };
- static int __init atxxled_init(void)
- {
- return platform_driver_register(&atxxled_driver);
- }
- static void __exit atxxled_exit(void)
- {
- platform_driver_unregister(&atxxled_driver);
- }
- module_init(atxxled_init);
- module_exit(atxxled_exit);
- static void atxxled_set(struct led_classdev *led_cdev, enum ATXX_LED led,
- enum led_brightness value)
- {
- int is_on = (value == LED_OFF) ? 0 : 1;
- enum pmu_led_module led_module;
- switch (led) {
- case RED_LED:
- led_module = PMU_LED_MODULE_2;
- break;
- case GREEN_LED:
- led_module = PMU_LED_MODULE_1;
- break;
- default:
- return;
- break;
- }
- pmu_led_on_off_set(led_module, is_on, 0); // led电源控制接口
- }
- static void atxxled_green_set(struct led_classdev *led_cdev,
- enum led_brightness value)
- {
- atxxled_set(led_cdev, GREEN_LED, value);
- }
- static void atxxled_red_set(struct led_classdev *led_cdev,
- enum led_brightness value)
- {
- atxxled_set(led_cdev, RED_LED, value);
- }
- static void atxxled_buttons_set(struct led_classdev *led_cdev,
- enum led_brightness value)
- {
- enum power_supply_mode is_on = (value == LED_OFF) ? PPS_OFF : PPS_ON;
- pmu_led_on_off_set(PMU_LED_KEY_1, is_on, 40);
- }
- static int atxxled_blink_set(struct led_classdev *led_cdev, enum ATXX_LED led,
- unsigned long *delay_on, unsigned long *delay_off)
- {
- enum pmu_led_module led_module;
- switch (led) {
- case RED_LED:
- led_module = PMU_LED_MODULE_2;
- break;
- case GREEN_LED:
- led_module = PMU_LED_MODULE_1;
- break;
- default:
- return 0;
- break;
- }
- pmu_led_blink_set(led_module, delay_on, delay_off); // led闪烁控制接口
- return 0;
- }
- static int atxxled_green_blink_set(struct led_classdev *led_cdev,
- unsigned long *delay_on, unsigned long *delay_off)
- {
- return atxxled_blink_set(led_cdev, GREEN_LED, delay_on, delay_off);
- }
- static int atxxled_red_blink_set(struct led_classdev *led_cdev,
- unsigned long *delay_on, unsigned long *delay_off)
- {
- return atxxled_blink_set(led_cdev, RED_LED, delay_on, delay_off);
- }
- static int atxxled_buttons_blink_set(struct led_classdev *led_cdev,
- unsigned long *delay_on, unsigned long *delay_off)
- {
- return -1;
- }
- static struct led_classdev atxx_red_led = {
- .name = "red",
- .brightness_set = atxxled_red_set,
- .blink_set = atxxled_red_blink_set,
- .flags = 0,
- };
- static struct led_classdev atxx_green_led = {
- .name = "green",
- .brightness_set = atxxled_green_set,
- .blink_set = atxxled_green_blink_set,
- .flags = 0,
- };
- static struct led_classdev atxx_buttons_led = {
- .name = "button-backlight",
- .brightness_set = atxxled_buttons_set, // led亮度调节
- .blink_set = atxxled_buttons_blink_set, // led闪烁
- .flags = 0,
- };
- static int atxxled_probe(struct platform_device *pdev)
- {
- int ret;
- ret = led_classdev_register(&pdev->dev, &atxx_buttons_led);
- if (ret < 0)
- return ret;
- ret = led_classdev_register(&pdev->dev, &atxx_red_led);
- if (ret < 0)
- return ret;
- ret = led_classdev_register(&pdev->dev, &atxx_green_led);
- if (ret < 0)
- led_classdev_unregister(&atxx_red_led);
- return ret;
- }
三、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的数据结构如下:
- struct led_trigger {
- /* Trigger Properties */
- const char *name;
- void (*activate)(struct led_classdev *led_cdev);
- void (*deactivate)(struct led_classdev *led_cdev);
- /* LEDs under control by this trigger (for simple triggers) */
- rwlock_t leddev_list_lock;
- struct list_head led_cdevs;
- /* Link to next registered trigger */
- struct list_head next_trig;
- };
分别为trigger名称、激活以及分离接口、读写锁、led设备链表头、trigger_list链表节点。下面是led-trigger核心部分源码的分析:
- static DECLARE_RWSEM(triggers_list_lock); // trigger链表读写锁
- static LIST_HEAD(trigger_list); // trigger链表
- // 用户空间写操作函数
- ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
- {
- struct led_classdev *led_cdev = dev_get_drvdata(dev);
- char trigger_name[TRIG_NAME_MAX];
- struct led_trigger *trig;
- size_t len;
- // 获取trigger_name的有效长度
- trigger_name[sizeof(trigger_name) - 1] = '\0';
- strncpy(trigger_name, buf, sizeof(trigger_name) - 1);
- len = strlen(trigger_name);
- if (len && trigger_name[len - 1] == '\n')
- trigger_name[len - 1] = '\0';
- // 比较是否为none,如果是则将led_cdev->trigger清除
- if (!strcmp(trigger_name, "none")) {
- led_trigger_remove(led_cdev);
- return count;
- }
- down_read(&triggers_list_lock);
- // 遍历trigger_list,如果本trigger在trigger_list中有注册
- // 则调用led_trigger_set()将本设备添加到trigger的设备链表中
- list_for_each_entry(trig, &trigger_list, next_trig) {
- if (!strcmp(trigger_name, trig->name)) {
- down_write(&led_cdev->trigger_lock);
- led_trigger_set(led_cdev, trig);
- up_write(&led_cdev->trigger_lock);
- up_read(&triggers_list_lock);
- return count;
- }
- }
- up_read(&triggers_list_lock);
- return -EINVAL;
- }
- EXPORT_SYMBOL_GPL(led_trigger_store);
- // 用户空间读操作函数
- ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
- char *buf)
- {
- struct led_classdev *led_cdev = dev_get_drvdata(dev);
- struct led_trigger *trig;
- int len = 0;
- down_read(&triggers_list_lock);
- down_read(&led_cdev->trigger_lock);
- // 如果本设备不存在trigger,则显示[none]
- if (!led_cdev->trigger)
- len += sprintf(buf+len, "[none] ");
- else
- len += sprintf(buf+len, "none ");
- // 遍历trigger_list,如果本设备当前trigger在链表中存在则用"[xxx]"显示
- // 如果不存在则用"xxx"显示链表中其余的trigger
- list_for_each_entry(trig, &trigger_list, next_trig) {
- if (led_cdev->trigger && !strcmp(led_cdev->trigger->name,
- trig->name))
- len += sprintf(buf+len, "[%s] ", trig->name);
- else
- len += sprintf(buf+len, "%s ", trig->name);
- }
- up_read(&led_cdev->trigger_lock);
- up_read(&triggers_list_lock);
- len += sprintf(len+buf, "\n");
- return len;
- }
- EXPORT_SYMBOL_GPL(led_trigger_show);
- // trigger设置函数,调用者需要持有led_cdev->trigger_lock锁
- void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger)
- {
- unsigned long flags;
- // 如果当前设备存在trigger,则清除,保证一个设备只有一个trigger
- if (led_cdev->trigger) {
- write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
- list_del(&led_cdev->trig_list); // 将当前设备从旧trigger的设备链表中删除
- write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock,
- flags);
- if (led_cdev->trigger->deactivate)
- led_cdev->trigger->deactivate(led_cdev); // 执行旧trigger分离函数
- led_cdev->trigger = NULL; // 将当前设备的trigger域清空
- led_set_brightness(led_cdev, LED_OFF); // 关闭设备
- }
- // 为设备添加一个新trigger
- if (trigger) {
- write_lock_irqsave(&trigger->leddev_list_lock, flags);
- // 将当前设备添加到新trigger的设备链表中
- list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs);
- write_unlock_irqrestore(&trigger->leddev_list_lock, flags);
- // 设置设备的trigger域并执行新trigger的激活函数
- led_cdev->trigger = trigger;
- if (trigger->activate)
- trigger->activate(led_cdev);
- }
- }
- EXPORT_SYMBOL_GPL(led_trigger_set);
- // trigger移除函数
- void led_trigger_remove(struct led_classdev *led_cdev)
- {
- down_write(&led_cdev->trigger_lock);
- led_trigger_set(led_cdev, NULL);
- up_write(&led_cdev->trigger_lock);
- }
- EXPORT_SYMBOL_GPL(led_trigger_remove);
- // 设置设备的默认trigger
- void led_trigger_set_default(struct led_classdev *led_cdev)
- {
- struct led_trigger *trig;
- if (!led_cdev->default_trigger)
- return;
- down_read(&triggers_list_lock);
- down_write(&led_cdev->trigger_lock);
- // 如果设备默认的trigger在trigger_list有注册,则设置trigger
- list_for_each_entry(trig, &trigger_list, next_trig) {
- if (!strcmp(led_cdev->default_trigger, trig->name))
- led_trigger_set(led_cdev, trig);
- }
- up_write(&led_cdev->trigger_lock);
- up_read(&triggers_list_lock);
- }
- EXPORT_SYMBOL_GPL(led_trigger_set_default);
- // 注册一个新的trigger到trigger_list中
- int led_trigger_register(struct led_trigger *trigger)
- {
- struct led_classdev *led_cdev;
- struct led_trigger *trig;
- rwlock_init(&trigger->leddev_list_lock); // 初始化读写锁
- INIT_LIST_HEAD(&trigger->led_cdevs); // 初始化设备链表
- down_write(&triggers_list_lock);
- // 确认trigger是否注册过
- list_for_each_entry(trig, &trigger_list, next_trig) {
- if (!strcmp(trig->name, trigger->name)) {
- up_write(&triggers_list_lock);
- return -EEXIST;
- }
- }
- // 将trigger添加到trigger_list中
- list_add_tail(&trigger->next_trig, &trigger_list);
- up_write(&triggers_list_lock);
- // 遍历当前已经注册的led,如果led的默认trigger是当前注册的trigger
- // 则调用led_trigger_set()将led添加到trigger的设备链表中
- down_read(&leds_list_lock);
- list_for_each_entry(led_cdev, &leds_list, node) {
- down_write(&led_cdev->trigger_lock);
- if (!led_cdev->trigger && led_cdev->default_trigger &&
- !strcmp(led_cdev->default_trigger, trigger->name))
- led_trigger_set(led_cdev, trigger);
- up_write(&led_cdev->trigger_lock);
- }
- up_read(&leds_list_lock);
- return 0;
- }
- EXPORT_SYMBOL_GPL(led_trigger_register);
- // 注销trigger
- void led_trigger_unregister(struct led_trigger *trigger)
- {
- struct led_classdev *led_cdev;
- // 将trigger从trigger_list中删除
- down_write(&triggers_list_lock);
- list_del(&trigger->next_trig);
- up_write(&triggers_list_lock);
- // 遍历当前已经注册的led,如果led使用的trigger是当前要删除的trigger
- // 则调用led_trigger_set()将led的trigger域清空
- down_read(&leds_list_lock);
- list_for_each_entry(led_cdev, &leds_list, node) {
- down_write(&led_cdev->trigger_lock);
- if (led_cdev->trigger == trigger)
- led_trigger_set(led_cdev, NULL);
- up_write(&led_cdev->trigger_lock);
- }
- up_read(&leds_list_lock);
- }
- EXPORT_SYMBOL_GPL(led_trigger_unregister);
- // 统一设置本trigger下所有led的亮度
- void led_trigger_event(struct led_trigger *trigger,
- enum led_brightness brightness)
- {
- struct list_head *entry;
- if (!trigger)
- return;
- read_lock(&trigger->leddev_list_lock);
- // 遍历当前trigger的设备链表,并设置每个设备的亮度
- list_for_each(entry, &trigger->led_cdevs) {
- struct led_classdev *led_cdev;
- led_cdev = list_entry(entry, struct led_classdev, trig_list);
- led_set_brightness(led_cdev, brightness);
- }
- read_unlock(&trigger->leddev_list_lock);
- }
- EXPORT_SYMBOL_GPL(led_trigger_event);
- // 简单注册一个新的trigger
- void led_trigger_register_simple(const char *name, struct led_trigger **tp)
- {
- struct led_trigger *trigger;
- int err;
- trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
- if (trigger) {
- trigger->name = name;
- err = led_trigger_register(trigger);
- if (err < 0)
- printk(KERN_WARNING "LED trigger %s failed to register"
- " (%d)\n", name, err);
- } else
- printk(KERN_WARNING "LED trigger %s failed to register"
- " (no memory)\n", name);
- *tp = trigger;
- }
- EXPORT_SYMBOL_GPL(led_trigger_register_simple);
- // 简单注销一个trigger
- void led_trigger_unregister_simple(struct led_trigger *trigger)
- {
- if (trigger)
- led_trigger_unregister(trigger);
- kfree(trigger);
- }
- EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);
核心层主要维护了trigger_list链表,保存了系统中注册的所有trigger;每个trigger都有自己的led设备链表,用于统一控制该trigger下所有led的亮度;同时每个led设备都有自己的trigger域,用于指向相应的trigger,如果trigger被触发则会调用相应trigger的激活函数。
四、ledtrig-timerledtrig-timer作为timer触发器,用于控制led闪烁。led-trigtimer通过led-trigger核心层调用会在/sys/class/leds/button-backlight创建delay_on、delay_off属性文件来控制闪烁时间。代码分析如下:
- struct timer_trig_data {
- int brightness_on; /* LED brightness during "on" period.
- * (LED_OFF < brightness_on <= LED_FULL)
- */
- unsigned long delay_on; /* milliseconds on */
- unsigned long delay_off; /* milliseconds off */
- struct timer_list timer;
- };
- static void led_timer_function(unsigned long data)
- {
- struct led_classdev *led_cdev = (struct led_classdev *) data;
- struct timer_trig_data *timer_data = led_cdev->trigger_data;
- unsigned long brightness;
- unsigned long delay;
- // 如果delay_on或者delay_off为0则关闭led
- if (!timer_data->delay_on || !timer_data->delay_off) {
- led_set_brightness(led_cdev, LED_OFF);
- return;
- }
- // 获取当前亮度值
- brightness = led_get_brightness(led_cdev);
- if (!brightness) {
- // 如果当前led为关闭状态则设置led到默认亮度值
- brightness = timer_data->brightness_on;
- delay = timer_data->delay_on;
- } else {
- // 如果当前led为开启状态则关闭led并保存当前亮度值
- timer_data->brightness_on = brightness;
- brightness = LED_OFF;
- delay = timer_data->delay_off;
- }
- // 设置亮度值,因为此时在soft irq中,所以不能调用耗时函数
- led_set_brightness(led_cdev, brightness);
- // 在delay后更新led状态,实现闪烁
- mod_timer(&timer_data->timer, jiffies + msecs_to_jiffies(delay));
- }
- // 显示delay_on参数
- static ssize_t led_delay_on_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct led_classdev *led_cdev = dev_get_drvdata(dev);
- struct timer_trig_data *timer_data = led_cdev->trigger_data;
- return sprintf(buf, "%lu\n", timer_data->delay_on);
- }
- // 存储delay_on参数
- static ssize_t led_delay_on_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
- {
- struct led_classdev *led_cdev = dev_get_drvdata(dev);
- struct timer_trig_data *timer_data = led_cdev->trigger_data;
- int ret = -EINVAL;
- char *after;
- unsigned long state = simple_strtoul(buf, &after, 10);
- size_t count = after - buf;
- if (*after && isspace(*after))
- count++;
- if (count == size) {
- if (timer_data->delay_on != state) {
- // 如果delay_on值不同则保存新值
- timer_data->delay_on = state;
- // 删除之前的timer
- del_timer_sync(&timer_data->timer);
- // 尝试调用blink_set()设置led
- if (!led_cdev->blink_set ||
- led_cdev->blink_set(led_cdev,
- &timer_data->delay_on, &timer_data->delay_off)) {
- // 如果没有或者设置失败就调用timer设置led
- mod_timer(&timer_data->timer, jiffies + 1);
- }
- }
- ret = count;
- }
- return ret;
- }
- // 显示delay_off参数
- static ssize_t led_delay_off_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct led_classdev *led_cdev = dev_get_drvdata(dev);
- struct timer_trig_data *timer_data = led_cdev->trigger_data;
- return sprintf(buf, "%lu\n", timer_data->delay_off);
- }
- // 存储delay_off参数
- static ssize_t led_delay_off_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
- {
- struct led_classdev *led_cdev = dev_get_drvdata(dev);
- struct timer_trig_data *timer_data = led_cdev->trigger_data;
- int ret = -EINVAL;
- char *after;
- unsigned long state = simple_strtoul(buf, &after, 10);
- size_t count = after - buf;
- if (*after && isspace(*after))
- count++;
- if (count == size) {
- if (timer_data->delay_off != state) {
- /* the new value differs from the previous */
- timer_data->delay_off = state;
- /* deactivate previous settings */
- del_timer_sync(&timer_data->timer);
- /* try to activate hardware acceleration, if any */
- if (!led_cdev->blink_set ||
- led_cdev->blink_set(led_cdev,
- &timer_data->delay_on, &timer_data->delay_off)) {
- /* no hardware acceleration, blink via timer */
- mod_timer(&timer_data->timer, jiffies + 1);
- }
- }
- ret = count;
- }
- return ret;
- }
- // 属性文件delay_on,delay_off
- static DEVICE_ATTR(delay_on, 0644, led_delay_on_show, led_delay_on_store);
- static DEVICE_ATTR(delay_off, 0644, led_delay_off_show, led_delay_off_store);
- // timer_led_trigger的激活函数
- static void timer_trig_activate(struct led_classdev *led_cdev)
- {
- struct timer_trig_data *timer_data;
- int rc;
- // 申请timer_trig_data的内存空间
- timer_data = kzalloc(sizeof(struct timer_trig_data), GFP_KERNEL);
- if (!timer_data)
- return;
- // 获取led亮度
- timer_data->brightness_on = led_get_brightness(led_cdev);
- // 如果为0为设置为最大值
- if (timer_data->brightness_on == LED_OFF)
- timer_data->brightness_on = led_cdev->max_brightness;
- // 赋值led的trigger_data域
- led_cdev->trigger_data = timer_data;
- // 初始化timer
- init_timer(&timer_data->timer);
- timer_data->timer.function = led_timer_function;
- timer_data->timer.data = (unsigned long) led_cdev;
- // 创建属性文件delay_on,delay_off
- rc = device_create_file(led_cdev->dev, &dev_attr_delay_on);
- if (rc)
- goto err_out;
- rc = device_create_file(led_cdev->dev, &dev_attr_delay_off);
- if (rc)
- goto err_out_delayon;
- /* If there is hardware support for blinking, start one
- * user friendly blink rate chosen by the driver.
- */
- // 如果硬件支持led闪烁,则开始使用默认值闪烁
- if (led_cdev->blink_set)
- led_cdev->blink_set(led_cdev,
- &timer_data->delay_on, &timer_data->delay_off);
- return;
- err_out_delayon:
- device_remove_file(led_cdev->dev, &dev_attr_delay_on);
- err_out:
- led_cdev->trigger_data = NULL;
- kfree(timer_data);
- }
- // timer_led_trigger的分离函数
- static void timer_trig_deactivate(struct led_classdev *led_cdev)
- {
- struct timer_trig_data *timer_data = led_cdev->trigger_data;
- unsigned long on = 0, off = 0;
- // 删除属性文件、timer、释放内存
- if (timer_data) {
- device_remove_file(led_cdev->dev, &dev_attr_delay_on);
- device_remove_file(led_cdev->dev, &dev_attr_delay_off);
- del_timer_sync(&timer_data->timer);
- kfree(timer_data);
- }
- // 停止闪烁
- if (led_cdev->blink_set)
- led_cdev->blink_set(led_cdev, &on, &off);
- }
- // timer_led_trigger注册函数
- static struct led_trigger timer_led_trigger = {
- .name = "timer",
- .activate = timer_trig_activate,
- .deactivate = timer_trig_deactivate,
- };
- static int __init timer_trig_init(void)
- {
- return led_trigger_register(&timer_led_trigger);
- }
- static void __exit timer_trig_exit(void)
- {
- led_trigger_unregister(&timer_led_trigger);
- }
这个timer触发器的实现有个bug,是在soft irq函数中去设置亮度,如果平台控制led亮度比较复杂,比如说通过i2c设备,将会引起内核crash,所以需要创建一个工作队列来实现亮度控制。