一、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. css 样式设计(一)( 在线150个例子 | 背景 | 文本 | 字体 | 链接 | 列表 | 表格 | 盒模型 | 边框 | 轮廓 | 边距 | 填充 |分组和嵌套 | 尺寸 | 定位 | 浮动 |对齐 )

    一.css在线150个例子 http://www.w3cschool.cc/css/css-examples.html 二.背景图片水平方向重复 : body { background-image:u ...

  2. js笔记---封装一般运动

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  3. Spring AOP:面向切面编程,AspectJ,是基于spring 的xml文件的方法

    导包等不在赘述: 建立一个接口:ArithmeticCalculator,没有实例化的方法: package com.atguigu.spring.aop.impl.panpan; public in ...

  4. JQuery知识快览之二—事件

    事件是脚本语言的核心.本文将为大家介绍JQuery支持的一些事件和如何自定义事件 JQuery内置事件 1.Document加载事件 JQuery提供了ready,load,unload三个Docum ...

  5. andriod 新建 Activity_ Form (详细设置)

    参考: Starting Another Activity 去创建Activity New->Other->Android->Android Activity->BlankAc ...

  6. Uva 12186 工人的请愿书

    题目链接:https://uva.onlinejudge.org/external/121/12186.pdf 题意: 给出一个树状关系图,公司里只有一个老板编号为0,其他人员从1开始编号.除了老板, ...

  7. error: command 'x86_64-linux-gnu-gcc' failed with exit status 1

    转载自:http://blog.csdn.net/wang1144/article/details/42277179 在ubuntu14.04版本上安装lxml,老是出错,在一番艰辛的搜索之后 ,终于 ...

  8. Spring资源访问

    资源访问 1.Resource Jdk提供的访问资源的类并不能很好地满足各种底层的资源访问需求, 比如缺少从类路径或者web容器的上下文中获取资源的操作类. 鉴于此, Spring设计了一个Resou ...

  9. HDU 5640 King's Cake

    King's Cake Problem Description It is the king's birthday before the military parade . The ministers ...

  10. 【leetcode❤python】 88. Merge Sorted Array

    #-*- coding: UTF-8 -*-class Solution(object):    def merge(self, nums1, m, nums2, n):        "& ...