Android 呼吸燈流程分析


一、Android呼吸燈Driver實現

      1、注冊驅動

      代碼位置:mediatek/kernel/drivers/leds/leds_drv.c

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. 602static struct platform_driver mt65xx_leds_driver = {  
  2. 603 .driver     = {  
  3. 604     .name   = "leds-mt65xx",  
  4. 605     .owner  = THIS_MODULE,  
  5. 606 },  
  6. 607 .probe      = mt65xx_leds_probe,  
  7. 608 .remove     = mt65xx_leds_remove,  
  8. 609 //.suspend  = mt65xx_leds_suspend,  
  9. 610 .shutdown   = mt65xx_leds_shutdown,  
  10. 611};  

       2、閃爍控制

 

       在probe函數中,對於呼吸燈的閃爍,重點是函數:

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. 466     g_leds_data[i]->cdev.brightness_set = mt65xx_led_set;  
  2. 467     g_leds_data[i]->cdev.blink_set = mt65xx_blink_set;    //控制呼吸燈閃爍  
  3. 468  
  4. 469     INIT_WORK(&g_leds_data[i]->work, mt_mt65xx_led_work);  
  5. 470  
  6. 471     ret = led_classdev_register(&pdev->dev, &g_leds_data[i]->cdev); //注冊相關設備文件  
  7. 472  

      函數:mt65xx_blink_set主要是通過如下流程來控制呼吸燈閃爍:
                  mt65xx_blink_set ----> mt_mt65xx_blink_set -----> mt_led_blink_pmic
 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. 268#define PMIC_PERIOD_NUM (sizeof(pmic_freqsel_array)/sizeof(pmic_freqsel_array[0]))  
  2. 269// 100 * period, ex: 0.01 Hz -> 0.01 * 100 = 1  
  3. 270int pmic_period_array[] = {250,500,1000,1250,1666,2000,2500,10000};  
  4. 271//int pmic_freqsel_array[] = {99999, 9999, 4999, 1999, 999, 499, 199, 4, 0};  
  5. 272int pmic_freqsel_array[] = {0, 4, 199, 499, 999, 1999, 1999, 1999};  
  6.   
  7. 274static int find_time_index_pmic(int time_ms) {  
  8. 275 int i;  
  9. 276 for(i=0;i<PMIC_PERIOD_NUM;i++) {  
  10. 277     if(time_ms<=pmic_period_array[i]) {  
  11. 278         return i;  
  12. 279     } else {  
  13. 280         continue;  
  14. 281     }  
  15. 282 }  
  16. 283 return PMIC_PERIOD_NUM-1;  
  17. 284}  
  18.   
  19. 286int mt_led_blink_pmic(enum mt65xx_led_pmic pmic_type, struct nled_setting* led) {  
  20. 287 int time_index = 0;  
  21. 288 int duty = 0;  
  22. 289 LEDS_DEBUG("[LED]led_blink_pmic: pmic_type=%d\n", pmic_type);  
  23. 290  
  24. 291 if((pmic_type != MT65XX_LED_PMIC_NLED_ISINK0 && pmic_type!= MT65XX_LED_PMIC_NLED_ISINK1 &&  
  25. 292     pmic_type!= MT65XX_LED_PMIC_NLED_ISINK2 && pmic_type!= MT65XX_LED_PMIC_NLED_ISINK3) || led->nled_mode != NLED_BLINK) {  
  26. 293     return -1;  
  27. 294 }  
  28. 295  
  29. 296 LEDS_DEBUG("[LED]LED blink on time = %d offtime = %d\n",led->blink_on_time,led->blink_off_time);  
  30. 297 time_index = find_time_index_pmic(led->blink_on_time + led->blink_off_time);  
  31. 298 LEDS_DEBUG("[LED]LED index is %d  freqsel=%d\n", time_index, pmic_freqsel_array[time_index]);  
  32. 299 duty=32*led->blink_on_time/(led->blink_on_time + led->blink_off_time);  
  33. 300 //upmu_set_rg_drv_2m_ck_pdn(0x0); // Disable power down (Indicator no need)  
  34. 301     upmu_set_rg_drv_32k_ck_pdn(0x0); // Disable power down  
  35. 302 switch(pmic_type){  
  36. 303     case MT65XX_LED_PMIC_NLED_ISINK0:  
  37. 304         upmu_set_rg_isink0_ck_pdn(0);  
  38. 305         upmu_set_rg_isink0_ck_sel(0);  
  39. 306         upmu_set_isink_ch0_mode(PMIC_PWM_0);  
  40. 307         upmu_set_isink_ch0_step(0x0);//4mA  
  41. 308         upmu_set_isink_dim0_duty(duty);  
  42. 309         upmu_set_isink_dim0_fsel(pmic_freqsel_array[time_index]);  
  43. 310         upmu_set_isink_breath0_trf_sel(0x0);  
  44. 311         upmu_set_isink_breath0_ton_sel(0x02);  
  45. 312         upmu_set_isink_breath0_toff_sel(0x05);  
  46. 313         upmu_set_isink_ch0_en(0x01);  
  47. 314         break;  
  48. 315     case MT65XX_LED_PMIC_NLED_ISINK1:  
  49. 316         upmu_set_rg_isink1_ck_pdn(0);  
  50. 317         upmu_set_rg_isink1_ck_sel(0);  
  51. 318         upmu_set_isink_ch1_mode(PMIC_PWM_0);  
  52. 319         upmu_set_isink_ch1_step(0x0);//4mA  
  53. 320         upmu_set_isink_dim1_duty(duty);  
  54. 321         upmu_set_isink_dim1_fsel(pmic_freqsel_array[time_index]);  
  55. 322         upmu_set_isink_breath1_trf_sel(0x0);  
  56. 323         upmu_set_isink_breath1_ton_sel(0x02);  
  57. 324         upmu_set_isink_breath1_toff_sel(0x05);  
  58. 325         upmu_set_isink_ch1_en(0x01);  
  59. 326         break;  
  60. 327     case MT65XX_LED_PMIC_NLED_ISINK2:  
  61. 328         upmu_set_rg_isink2_ck_pdn(0);  
  62. 329         upmu_set_rg_isink2_ck_sel(0);  
  63. 330         upmu_set_isink_ch2_mode(PMIC_PWM_0);  
  64. 331         upmu_set_isink_ch2_step(0x0);//4mA  
  65. 332         upmu_set_isink_dim2_duty(duty);  
  66. 333         upmu_set_isink_dim2_fsel(pmic_freqsel_array[time_index]);  
  67. 334         upmu_set_isink_breath2_trf_sel(0x0);  
  68. 335         upmu_set_isink_breath2_ton_sel(0x02);  
  69. 336         upmu_set_isink_breath2_toff_sel(0x05);  
  70. 337         upmu_set_isink_ch2_en(0x01);  
  71. 338         break;  
  72. 339     case MT65XX_LED_PMIC_NLED_ISINK3:  
  73. 340         upmu_set_rg_isink3_ck_pdn(0);  
  74. 341         upmu_set_rg_isink3_ck_sel(0);  
  75. 342         upmu_set_isink_ch3_mode(PMIC_PWM_0);  
  76. 343         upmu_set_isink_ch3_step(0x3);//16mA  
  77. 344         upmu_set_isink_dim3_duty(duty);  
  78. 345         upmu_set_isink_dim3_fsel(pmic_freqsel_array[time_index]);  
  79. 346         upmu_set_isink_breath3_trf_sel(0x0);  
  80. 347         upmu_set_isink_breath3_ton_sel(0x02);  
  81. 348         upmu_set_isink_breath3_toff_sel(0x05);  
  82. 349         upmu_set_isink_ch3_en(0x01);  
  83. 350         break;  
  84. 351     default:  
  85. 352     break;  
  86. 353 }  
  87. 354 return 0;  
  88. 355}  

      相關流程為:led->blink_on_time 和 led->blink_off_time 是我們傳下來的呼吸燈的Led_on 和 Led_off的值。
      通過find_time_index_pmic函數計算呼吸燈的頻率:假設我們傳下來的的值為Led_on=350,Led_off=300 ,則Led_on+Led_off = 650, 650<1000,所find_time_index_pmic返回i=2;對應在數組int pmic_freqsel_array[]中為199.所以呼吸燈的閃爍頻率就是 1000/199 = 5HZ。

 

     3、設備文件注冊

      對應函數為:
     ret = led_classdev_register(&pdev->dev, &g_leds_data[i]->cdev); //注冊相關設備文件
    代碼位置:kernel/drivers/leds/led-class.c

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. 160int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)  
  2. 161{  
  3. 162 led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,  
  4. 163                   "%s", led_cdev->name);  
  5. 164 if (IS_ERR(led_cdev->dev))  
  6. 165     return PTR_ERR(led_cdev->dev);  
  7. 166  
  8. 167#ifdef CONFIG_LEDS_TRIGGERS  
  9. 168 init_rwsem(&led_cdev->trigger_lock);  
  10. 169#endif  
  11. 170 /* add to the list of leds */  
  12. 171 down_write(&leds_list_lock);  
  13. 172 list_add_tail(&led_cdev->node, &leds_list);  
  14. 173 up_write(&leds_list_lock);  
  15. 174  
  16. 175 if (!led_cdev->max_brightness)  
  17. 176     led_cdev->max_brightness = LED_FULL;  
  18. 177  
  19. 178 led_update_brightness(led_cdev);  
  20. 179  
  21. 180 init_timer(&led_cdev->blink_timer);  
  22. 181 led_cdev->blink_timer.function = led_timer_function;  
  23. 182 led_cdev->blink_timer.data = (unsigned long)led_cdev;  
  24. 183  
  25. 184#ifdef CONFIG_LEDS_TRIGGERS  
  26. 185 led_trigger_set_default(led_cdev);  
  27. 186#endif  
  28. 187  
  29. 188 printk(KERN_DEBUG "Registered led device: %s\n",  
  30. 189         led_cdev->name);  
  31. 190  
  32. 191 return 0;  
  33. 192}  

      注冊的設備文件關聯在leds_class中:    

 

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. 228 leds_class->dev_attrs = led_class_attrs;  
  2.   
  3. 73  
  4. 74 static struct device_attribute led_class_attrs[] = {  
  5. 75  __ATTR(brightness, 0644, led_brightness_show, led_brightness_store),  
  6. 76  __ATTR(max_brightness, 0444, led_max_brightness_show, NULL),  
  7. 77 #ifdef CONFIG_LEDS_TRIGGERS  
  8. 78  __ATTR(trigger, 0644, led_trigger_show, led_trigger_store),  
  9. 79 #endif  
  10. 80  __ATTR_NULL,  
  11. 81};  

      然后通過:init_timer(&led_cdev->blink_timer);注冊了軟件控制呼吸燈閃爍的辦法。
      控制呼吸燈閃爍的辦法;而是mt65xx_blink_set。
      在上層調用mt65xx_blink_set函數來控制呼吸燈閃爍,主要是通過trigger觸發器接口的辦法實現的。
    

 

     4、trigger觸發器

    看上面AndroidHAL層控制呼吸燈閃爍的流程中,最后是打開了設備文件:/sys/class/leds/red/trigger

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. 94 char const*const RED_TRIGGER_FILE  
  2. 95        = "/sys/class/leds/red/trigger";  
  3.   
  4. 253 write_str(RED_TRIGGER_FILE, "timer");  

    很顯然我們驅動中對應的響應函數為:led_trigger_store,往該函數傳入的參數為:"timer"
    代碼位置:kernel/drivers/leds/led-triggers.c

 

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. 34ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,  
  2. 35      const char *buf, size_t count)  
  3. 36{  
  4. 37  struct led_classdev *led_cdev = dev_get_drvdata(dev);  
  5. 38  char trigger_name[TRIG_NAME_MAX];  
  6. 39  struct led_trigger *trig;  
  7. 40  size_t len;  
  8. 41  
  9. 42  trigger_name[sizeof(trigger_name) - 1] = '\0';  
  10. 43  strncpy(trigger_name, buf, sizeof(trigger_name) - 1);  
  11. 44  len = strlen(trigger_name);  
  12. 45  
  13. 46  if (len && trigger_name[len - 1] == '\n')  
  14. 47      trigger_name[len - 1] = '\0';  
  15. 48  
  16. 49  if (!strcmp(trigger_name, "none")) {  
  17. 50      led_trigger_remove(led_cdev);  
  18. 51      return count;  
  19. 52  }  
  20. 53  
  21. 54  down_read(&triggers_list_lock);  
  22. 55  list_for_each_entry(trig, &trigger_list, next_trig) {  
  23. 56      if (!strcmp(trigger_name, trig->name)) {  
  24. 57          down_write(&led_cdev->trigger_lock);  
  25. 58          led_trigger_set(led_cdev, trig);  
  26. 59          up_write(&led_cdev->trigger_lock);  
  27. 60  
  28. 61          up_read(&triggers_list_lock);  
  29. 62          return count;  
  30. 63      }  
  31. 64  }  
  32. 65  up_read(&triggers_list_lock);  
  33. 66  
  34. 67  return -EINVAL;  
  35. 68}  

        如果觸發器名字trigger_name是none的話,就移除掉該觸發器,不是的話,就遍歷trigger_list,比較trigger_name是“timer”的單元。找到了該單元之后,通過
       led_trigger_set(led_cdev, trig);更新它。
       led_trigger_set首先清除掉舊的name="timer"的觸發器,然后用新的name="timer"觸發器代替它,最后調用該觸發器的trigger->activate(led_cdev)函數。
       在開機時候,系統會自動創建一個trigger_name為“timer”的觸發器。代碼如下:
               kernel/drivers/leds/ledtrig-timer.c

 

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. 119 static struct led_trigger timer_led_trigger = {  
  2. 120 .name     = "timer",  
  3. 121 .activate = timer_trig_activate,  
  4. 122 .deactivate = timer_trig_deactivate,  
  5. 123};  
  6. 124  
  7. 125 static int __init timer_trig_init(void)  
  8. 126 {  
  9. 127 return led_trigger_register(&timer_led_trigger);  
  10. 128 }  
  11. 129  
  12. 130 static void __exit timer_trig_exit(void)  
  13. 131 {  
  14. 132 led_trigger_unregister(&timer_led_trigger);  
  15. 133 }  

        在timer_trig_activate中創建了兩個設備文件delay_on和delay_off。
        所以我們總結出來:在HAl層中,函數write_str(RED_TRIGGER_FILE, "timer");的作用就是更新trigger_name=“timer”的觸發器,然后調用該觸發器的activate函數,創建設備文件:delay_on和delay_off;

 

         5、呼吸燈閃爍的實現

      在HAL層中,閃爍的時候,做了如下處理:

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. 253     write_str(RED_TRIGGER_FILE, "timer");      
  2. 254     while (((access(RED_DELAY_OFF_FILE, F_OK) == -1) || (access(RED_DELAY_OFF_FILE, R_OK|W_OK) == -1)) && i<10) {  
  3. 255         ALOGD("RED_DELAY_OFF_FILE doesn't exist or cannot write!!\n");  
  4. 256         led_wait_delay(5);//sleep 5ms for wait kernel LED class create led delay_off/delay_on node of fs  
  5. 257         i++;  
  6. 258     }  
  7. 259     write_int(RED_DELAY_OFF_FILE, offMS);  
  8. 260     write_int(RED_DELAY_ON_FILE, onMS);  

        從剛才分析我們知道:以上代碼會首先更新timer的觸發器,然后等待5ms,創建delay_on和delay_off的設備文件,最后往該設備文件中分別寫入offMs和onMs.很顯然,最后我們要找的就是delay_on和delay_off對應的處理函數函數。

 

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. 59static ssize_t led_delay_off_store(struct device *dev,  
  2. 60      struct device_attribute *attr, const char *buf, size_t size)  
  3. 61{  
  4. 62  struct led_classdev *led_cdev = dev_get_drvdata(dev);  
  5. 63  int ret = -EINVAL;  
  6. 64  char *after;  
  7. 65  unsigned long state = simple_strtoul(buf, &after, 10);  
  8. 66  size_t count = after - buf;  
  9. 67  
  10. 68  if (isspace(*after))  
  11. 69      count++;  
  12. 70  
  13. 71  if (count == size) {  
  14. 72      led_blink_set(led_cdev, &led_cdev->blink_delay_on, &state);  
  15. 73      led_cdev->blink_delay_off = state;  
  16. 74      ret = count;  
  17. 75  }  
  18. 76  
  19. 77  return ret;  
  20. 78}  

        HAL層中首先寫入的是delay_off的時間,對應處理函數如上,之后進入了函數led_blink_set中:

 

 

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. 71void led_blink_set(struct led_classdev *led_cdev,  
  2. 72         unsigned long *delay_on,  
  3. 73         unsigned long *delay_off)  
  4. 74{  
  5. 75  del_timer_sync(&led_cdev->blink_timer);  
  6. 76  
  7. 77  if (led_cdev->blink_set &&  
  8. 78      !led_cdev->blink_set(led_cdev, delay_on, delay_off))  
  9. 79      return;  
  10. 80  
  11. 81  /* blink with 1 Hz as default if nothing specified */  
  12. 82  if (!*delay_on && !*delay_off)  
  13. 83      *delay_on = *delay_off = 500;  
  14. 84  
  15. 85  led_set_software_blink(led_cdev, *delay_on, *delay_off);  
  16. 86}  
  17. 87EXPORT_SYMBOL(led_blink_set);  

       該函數首先刪除掉軟件方法閃爍的定時器,然后調用了led_cdev->blink_set,在blink_set函數中,因為delay_on為0,而delay_off為300,所以會返回-1,從而進入函數led_set_software_blink。

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. 35static void led_set_software_blink(struct led_classdev *led_cdev,  
  2. 36                 unsigned long delay_on,  
  3. 37                 unsigned long delay_off)  
  4. 38{  
  5. 39  int current_brightness;  
  6. 40  
  7. 41  current_brightness = led_get_brightness(led_cdev);  
  8. 42  if (current_brightness)  
  9. 43      led_cdev->blink_brightness = current_brightness;  
  10. 44  if (!led_cdev->blink_brightness)  
  11. 45      led_cdev->blink_brightness = led_cdev->max_brightness;  
  12. 46  
  13. 47  if (led_get_trigger_data(led_cdev) &&  
  14. 48      delay_on == led_cdev->blink_delay_on &&  
  15. 49      delay_off == led_cdev->blink_delay_off)  
  16. 50      return;  
  17. 51  
  18. 52  led_stop_software_blink(led_cdev);  
  19. 53  
  20. 54  led_cdev->blink_delay_on = delay_on;  
  21. 55  led_cdev->blink_delay_off = delay_off;  
  22. 56  
  23. 57  /* never on - don't blink */  
  24. 58  if (!delay_on)  
  25. 59      return;  
  26. 60  
  27. 61  /* never off - just set to brightness */  
  28. 62  if (!delay_off) {  
  29. 63      led_set_brightness(led_cdev, led_cdev->blink_brightness);  
  30. 64      return;  
  31. 65  }  
  32. 66  
  33. 67  mod_timer(&led_cdev->blink_timer, jiffies + 1);  
  34. 68}  

         在該函數中更新了led_cdev->blink_delay_off為我們傳入的delay_off,也就是300,然后又因為delay_on為0,所以中途退出,不會啟動最后的呼吸燈閃爍的軟件控制定時器。之后,HAL繼續write_int(RED_DELAY_ON_FILE, onMS);往delay_off接口中寫入了onMS,也就是上面的350.類似的:

[plain]  view plain copy 在CODE上查看代碼片 派生到我的代碼片
 
  1. 30static ssize_t led_delay_on_store(struct device *dev,  
  2. 31      struct device_attribute *attr, const char *buf, size_t size)  
  3. 32{  
  4. 33  struct led_classdev *led_cdev = dev_get_drvdata(dev);  
  5. 34  int ret = -EINVAL;  
  6. 35  char *after;  
  7. 36  unsigned long state = simple_strtoul(buf, &after, 10);  
  8. 37  size_t count = after - buf;  
  9. 38  
  10. 39  if (isspace(*after))  
  11. 40      count++;  
  12. 41  
  13. 42  if (count == size) {  
  14. 43      led_blink_set(led_cdev, &state, &led_cdev->blink_delay_off);  
  15. 44      led_cdev->blink_delay_on = state;   
  16. 45      ret = count;  
  17. 46  }  
  18. 47  
  19. 48  return ret;  
  20. 49}  

         該函數最后調用了led_blink_set,傳入了onMs(350)和上一步保存的offMs(300)。
繼續進入
      led_blink_set ---->led_cdev->blink_set  ---> mt65xx_blink_set  --->   mt65xx_blink_set -->  mt_mt65xx_blink_set --->  mt_led_blink_pmic
也就是上面分析的第一種讓呼吸燈閃爍的函數:mt_led_blink_pmic。
好了,呼吸燈閃爍,基本就是這樣。。。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM