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。

转载于:https://www.cnblogs.com/soc-linux-driver/archive/2012/07/10/2584337.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值