linux led sub system 二---------trigger

前面一篇随笔大略的分析了led class设备。其中关于trigger的部分提了一下就略过了。现在具体的做个分析,ledtrigger比led class dev 要复杂的多。做点笔记记录下来以备以后用到。

  trigger 中文的翻译叫做触发。既然叫trigger,一定有一个事件或条件达到时led出现一个状态(点亮,亮度改变,闪烁)。做个事件可以来自userspace的请求,或kenel产生的事件,如休眠,cpu空闲等。而这些事件或条件就是我们要注册的trigger。每个led可以由有若干了trigger。可以在注册led设备时指定默认的trigger ,也可以由userspace指定,切换。当trigger发生时,led会产生相应的trigger定义的动作。

  既然trigger是led的,那么在ledclass dev中一定有些记录。

 
const char  *default_trigger; /* Trigger to use */

1 #ifdef CONFIG_LEDS_TRIGGERS
2     /* Protects the trigger data below */
3     struct rw_semaphore     trigger_lock;
4 
5     struct led_trigger    *trigger;
6     struct list_head     trig_list;
7     void            *trigger_data;
8 #endif

 如上代码,default_trigger 是这个led的默认的trigger名。如果在注册led设备时给予了他值,那么这个led就会在default_trigger 的条件下执行动作。

trigger_lock 一把锁,保护trig_list用的。trigger,一个led可以有许多trigger 这个值指向当前trigger。

trig_list。这个led说拥有的所有trigger的一个链表还是为了把这个led设备挂在trigger中的一个节点,这里得不到任何的信息,只能看后面的代码了。

trigger_data当前trigger的私有数据。

现在可以看一下ledtrigger这个人物了

 

 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的名字,不超过50个字符。

仅接着是trigger激活和取消的函数。从函数参数可以推测,这两个函数是针对特定的led的。

第九行是一个链表头,可以推测他把所有属于他trigge的led通过trig_list都链接起来。第12行应是自身的一个链表,用它把所有向ledtrigger注册的trigger链接起来。

接着我们从具体的一个trigger(定时器触发)出发,一步步的理清思路。

ledtrigger_timer.c

 

 1 static struct led_trigger timer_led_trigger = {
 2     .name     = "timer",
 3     .activate = timer_trig_activate,
 4     .deactivate = timer_trig_deactivate,
 5 };
 6 
 7 static int __init timer_trig_init(void)
 8 {
 9     return led_trigger_register(&timer_led_trigger);
10 }
11 
12 static void __exit timer_trig_exit(void)
13 {
14     led_trigger_unregister(&timer_led_trigger);
15 }
16 
17 module_init(timer_trig_init);
18 module_exit(timer_trig_exit);

这段代码也清楚的说明了如何取写一个trigger。首先定义一个trigger。然后注册就可以了。重点是实现activate和deactivate函数。

这两个函数如何实现,稍后分析,先来看一下注册函数。

 

 1 int led_trigger_register(struct led_trigger *trigger)
 2 {
 3     struct led_classdev *led_cdev;
 4     struct led_trigger *trig;
 5 
 6     rwlock_init(&trigger->leddev_list_lock);
 7     INIT_LIST_HEAD(&trigger->led_cdevs);
 8 
 9     down_write(&triggers_list_lock);
10     /* Make sure the trigger's name isn't already in use */
11     list_for_each_entry(trig, &trigger_list, next_trig) {
12         if (!strcmp(trig->name, trigger->name)) {
13             up_write(&triggers_list_lock);
14             return -EEXIST;
15         }
16     }
17     /* Add to the list of led triggers */
18     list_add_tail(&trigger->next_trig, &trigger_list);
19     up_write(&triggers_list_lock);
20 
21     /* Register with any LEDs that have this as a default trigger */
22     down_read(&leds_list_lock);
23     list_for_each_entry(led_cdev, &leds_list, node) {
24         down_write(&led_cdev->trigger_lock);
25         if (!led_cdev->trigger && led_cdev->default_trigger &&
26                 !strcmp(led_cdev->default_trigger, trigger->name))
27             led_trigger_set(led_cdev, trigger);
28         up_write(&led_cdev->trigger_lock);
29     }
30     up_read(&leds_list_lock);
31 
32     return 0;
33 }

10-20 检查这个trigger是否已经注册,如果注册则返回已存在。

否则,把这个trigger加入到trigger_list列表中。

22-30 遍历所有led设备,如果发现某个led的默认trigger是本trigger,那么就把这个led设备通过他的trig_list 挂在 struct list_head  led_cdevs 上。

这个过程是通过led_trigger_set(led_cdev, trigger);来完成的。

 

 1 void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger)
 2 {
 3     unsigned long flags;
 4 
 5     /* Remove any existing trigger */
 6     if (led_cdev->trigger) {
 7         write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
 8         list_del(&led_cdev->trig_list);
 9         write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock,
10             flags);
11         if (led_cdev->trigger->deactivate)
12             led_cdev->trigger->deactivate(led_cdev);
13         led_cdev->trigger = NULL;
14         led_brightness_set(led_cdev, LED_OFF);
15     }
16     if (trigger) {
17         write_lock_irqsave(&trigger->leddev_list_lock, flags);
18         list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs);
19         write_unlock_irqrestore(&trigger->leddev_list_lock, flags);
20         led_cdev->trigger = trigger;
21         if (trigger->activate)
22             trigger->activate(led_cdev);
23     }
24 }

这个函数做了两件事,一是移除旧的,二是添加新的。通过传递的参数,完成往trigger中添加删除led设备的功能。就是在这里trigger的activate和deactive被执行。到此trigger就算分析完了。

接着ledtrigger_timer分析trigger的两个重量级函数active和deactive

 

 1 static DEVICE_ATTR(delay_on, 0644, led_delay_on_show, led_delay_on_store);
 2 static DEVICE_ATTR(delay_off, 0644, led_delay_off_show, led_delay_off_store);
 3 
 4 static void timer_trig_activate(struct led_classdev *led_cdev)
 5 {
 6     int rc;
 7 
 8     led_cdev->trigger_data = NULL;
 9 
10     rc = device_create_file(led_cdev->dev, &dev_attr_delay_on);
11     if (rc)
12         return;
13     rc = device_create_file(led_cdev->dev, &dev_attr_delay_off);
14     if (rc)
15         goto err_out_delayon;
16 
17     led_blink_set(led_cdev, &led_cdev->blink_delay_on,
18               &led_cdev->blink_delay_off);
19 
20     led_cdev->trigger_data = (void *)1;
21 
22     return;
23 
24 err_out_delayon:
25     device_remove_file(led_cdev->dev, &dev_attr_delay_on);
26 }
27 
28 static void timer_trig_deactivate(struct led_classdev *led_cdev)
29 {
30     if (led_cdev->trigger_data) {
31         device_remove_file(led_cdev->dev, &dev_attr_delay_on);
32         device_remove_file(led_cdev->dev, &dev_attr_delay_off);
33     }
34 
35     /* Stop blinking */
36     led_brightness_set(led_cdev, LED_OFF);
37 }

timer_trig_activate 在本led设备下创建了两个节点,delay_on 和delay_off。在用户空间写这两个文件就会形成led的闪烁。具体原理可分析led_delay_on_store和led_delay_off_store两个属性设置函数。device_create_file(led_cdev->dev, &dev_attr_delay_on);这个函数会在这个设备led_cdev->dev下创建delay_on这个文件。

timer trigger需要user的干预才能触发闪烁,属于用户空间的请求。下面简单分析一个kernel空间事件的触发。

ledtrigger_sleep.c

代码很简单,他再init的时候注册了trigger。并注册了pm通知链。当pm状态变化时,pm通知回调会运行于是

 

 1 static int ledtrig_sleep_pm_callback(struct notifier_block *nfb,
 2                     unsigned long action,
 3                     void *ignored)
 4 {
 5     switch (action) {
 6     case PM_HIBERNATION_PREPARE:
 7     case PM_SUSPEND_PREPARE:
 8         led_trigger_event(ledtrig_sleep, LED_OFF);
 9         return NOTIFY_OK;
10     case PM_POST_HIBERNATION:
11     case PM_POST_SUSPEND:
12         led_trigger_event(ledtrig_sleep, LED_FULL);
13         return NOTIFY_OK;
14     }
15 
16     return NOTIFY_DONE;
17 }


led_trigger_event 被调用,还有一个函数是led_trigger_blink。

 1 void led_trigger_blink(struct led_trigger *trigger,
 2                unsigned long *delay_on,
 3                unsigned long *delay_off)
 4 {
 5     struct list_head *entry;
 6 
 7     if (!trigger)
 8         return;
 9 
10     read_lock(&trigger->leddev_list_lock);
11     list_for_each(entry, &trigger->led_cdevs) {
12         struct led_classdev *led_cdev;
13 
14         led_cdev = list_entry(entry, struct led_classdev, trig_list);
15         led_blink_set(led_cdev, delay_on, delay_off);
16     }
17     read_unlock(&trigger->leddev_list_lock);
18 }

 这个函数遍历所有当前trigger拥有的led设备。并让其闪烁。

系统中可能注册若干trigger,但一个led在某一个时刻有且最多能有一个trigger。那么如何切换led的trigger呢。

前面分析led设备时,在注册led类是有个属性数组,里边有一项就是trigger属性。我们来看一下这个属性。

__ATTR(trigger, 0644, led_trigger_show, led_trigger_store),

看一下这个属性设置函数 led_trigger_store

 

 1 ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
 2         const char *buf, size_t count)
 3 {
 4     struct led_classdev *led_cdev = dev_get_drvdata(dev);
 5     char trigger_name[TRIG_NAME_MAX];
 6     struct led_trigger *trig;
 7     size_t len;
 8 
 9     trigger_name[sizeof(trigger_name) - 1] = '\0';
10     strncpy(trigger_name, buf, sizeof(trigger_name) - 1);
11     len = strlen(trigger_name);
12 
13     if (len && trigger_name[len - 1] == '\n')
14         trigger_name[len - 1] = '\0';
15 
16     if (!strcmp(trigger_name, "none")) {
17         led_trigger_remove(led_cdev);
18         return count;
19     }
20 
21     down_read(&triggers_list_lock);
22     list_for_each_entry(trig, &trigger_list, next_trig) {
23         if (!strcmp(trigger_name, trig->name)) {
24             down_write(&led_cdev->trigger_lock);
25             led_trigger_set(led_cdev, trig);
26             up_write(&led_cdev->trigger_lock);
27 
28             up_read(&triggers_list_lock);
29             return count;
30         }
31     }
32     up_read(&triggers_list_lock);
33 
34     return -EINVAL;
35 }
36 EXPORT_SYMBOL_GPL(led_trigger_store);

当user 写 trigger这个文件时。这个函数会调用。22到29完成切换过程。

他遍历trigger_list。找到trigger_name匹配的trigger。然后调用led_trigger_set把这个设备设置给这个trigger。

posted @ 2012-07-10 14:00  camera&tunning  阅读(3466)  评论(0编辑  收藏  举报