一、Android呼吸灯Driver实现

1、注册驱动

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

  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函数中,对于呼吸灯的闪烁,重点是函数:

  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

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

  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中:

  1. 228 leds_class->dev_attrs = led_class_attrs;
  2. 73
  3. 74 static struct device_attribute led_class_attrs[] = {
  4. 75  __ATTR(brightness, 0644, led_brightness_show, led_brightness_store),
  5. 76  __ATTR(max_brightness, 0444, led_max_brightness_show, NULL),
  6. 77 #ifdef CONFIG_LEDS_TRIGGERS
  7. 78  __ATTR(trigger, 0644, led_trigger_show, led_trigger_store),
  8. 79 #endif
  9. 80  __ATTR_NULL,
  10. 81};

然后通过:init_timer(&led_cdev->blink_timer);注册了软件控制呼吸灯闪烁的办法。
      控制呼吸灯闪烁的办法;而是mt65xx_blink_set。
      在上层调用mt65xx_blink_set函数来控制呼吸灯闪烁,主要是通过trigger触发器接口的办法实现的。

4、trigger触发器

看上面AndroidHAL层控制呼吸灯闪烁的流程中,最后是打开了设备文件:/sys/class/leds/red/trigger

  1. 94 char const*const RED_TRIGGER_FILE
  2. 95        = "/sys/class/leds/red/trigger";
  3. 253 write_str(RED_TRIGGER_FILE, "timer");

很显然我们驱动中对应的响应函数为:led_trigger_store,往该函数传入的参数为:"timer"
    代码位置:kernel/drivers/leds/led-triggers.c

  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

  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层中,闪烁的时候,做了如下处理:

  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对应的处理函数函数。

  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中:

  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。

  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.类似的:

  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。
好了,呼吸灯闪烁,基本就是这样。。。

Android 呼吸灯流程分析的更多相关文章

  1. Android呼吸灯效果实现

    最近需要做一个搜索动画的呼吸灯效果,在网上查了下有以下两种实现方式,现记录下来. 实现呼吸灯效果本质上都是使用ImageView动画,实现alpha值变化. 第一种方式,直接使用动画实现,代码如下: ...

  2. Android呼吸灯添加

    平台:mtk 一.hal层入口    Lights.c (vendor\mediatek\proprietary\hardware\liblights)     char const*const RE ...

  3. android Camera 数据流程分析

    这篇文章主要针对其数据流程进行分析.Camera一般用于图像浏览.拍照和视频录制.这里先对图像浏览和拍照的数据流进行分析,后面再对视频电话部分进行分析. 1.针对HAL层对摄像头数据处理补充一下 Li ...

  4. Android SDCard Mount 流程分析

    前段时间对Android 的SDCard unmount 流程进行了几篇简短的分析,由于当时只是纸上谈兵,没有实际上的跟进,可能会有一些误导人或者小错误.今天重新梳理了头绪,针对mount的流程再重新 ...

  5. android PakageManagerService启动流程分析

    PakageManagerService的启动流程图 1.PakageManagerService概述 PakageManagerService是android系统中一个核心的服务,它负责系统中Pac ...

  6. android添加账户流程分析涉及漏洞修复

    android修复了添加账户代码中的2处bug,retme取了很酷炫的名字launchAnyWhere.broadAnywhere(参考资料1.2).本文顺着前辈的思路学习bug的原理和利用思路. 我 ...

  7. Android WiFi 扫描流程分析(wpa_supplicant选择网络)

    扫描流程 1.如果之前就已经有相关记录,优化扫描,扫描记录部分的频率信道. 2.如果1中的扫描没有结果,清除黑名单中的进行选择. 3.如果2中没有结果,进行所有频率的信道进行扫描 相关log参考: h ...

  8. [旧][Android] ButterKnifeProcessor 工作流程分析

    备注 原发表于2016.05.21,资料已过时,仅作备份,谨慎参考 前言 在 [Android] ButterKnife 浅析 中,我们了解了 ButterKnife 的用法,比较简单. 本次文章我们 ...

  9. Android WiFi 扫描流程分析(wpa_supplicant)

    void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec) { int res; if (wpa_s-& ...

随机推荐

  1. 【PHP设计模式 02_JieKou.php】面向接口开发

    <?php /** * [面向接口开发] * */ header("Content-type: text/html; charset=utf-8"); /*共同接口--连接数 ...

  2. 编译器 perforSelecter时 警告去除

    #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks&quo ...

  3. PHP高级架构技术年度大盘点

    2015年1月的最后一个周末,上海的冬季虽不如北方的雪窖冰天,但腊月的寒风也足以让人猫 在家中不愿出门.可是,在华美达酒店的一个会议室中,却人声鼎沸.春意融融,第三期商派技术沙龙正在火热进行,本期沙龙 ...

  4. discuz阅读权限的设置作用

    为什么要有阅读权限?偶想很多新手有这个疑问吧,所以特开此帖说明下. 阅读权限的设置是帖子作者为了部分限制帖子的读者群.虽然网上发帖重在分享,但帖子(尤其精华帖子)是作者花时间和经历而写成的,不加阅读权 ...

  5. [HTML]表格的一切

    如何设置HTML页面自适应宽度的table(表格): <table width="95%" border="1" cellpadding="2& ...

  6. 每日一九度之 题目1031:xxx定律

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:6870 解决:4302 题目描述:     对于一个数n,如果是偶数,就把n砍掉一半:如果是奇数,把n变成 3*n+ 1后砍掉一半,直到该数 ...

  7. Java学习之路(二)

    什么是变量? 1:计算机是一种嫉妒精确的机器 2:要将信息存储在计算机当中,就必须指明信息存储的位置和所需的内存空间: 3:在Java当中 使用声明语句来完成上述任务 变量的类型:

  8. Poj(1511),SPFA

    题目链接:http://poj.org/problem?id=1511 嗯,最后一次写SPFA了,以后就套模板了. 题意:给出n个点和n条有向边,求所有点到源点1的来回最短路之和(保证每个点都可以往返 ...

  9. linux之eventfd()

    参考:http://www.man7.org/linux/man-pages/man2/eventfd.2.html 一.简介 简单来说,这个函数就是创建一个用于事件通知的文件描述符.它类似于pipe ...

  10. Axure一点2

    4:找茬:先安装图片,在不同的地方安上动态面板(命名为左一),在动态面板上双击,添加动态,进去动态后添加矩形改色,设置透明度为0,回到主页面.并设为隐藏,复制到另一面(命名为右一).在右一上添加图像热 ...