一、Android呼吸灯Driver实现

1、注册驱动

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

602static struct platform_driver mt65xx_leds_driver = {

603 .driver     = {

604     .name   = "leds-mt65xx",

605     .owner  = THIS_MODULE,

606 },

607 .probe      = mt65xx_leds_probe,

608 .remove     = mt65xx_leds_remove,

609 //.suspend  = mt65xx_leds_suspend,

610 .shutdown   = mt65xx_leds_shutdown,

611};

2、闪烁控制

在probe函数中,对于呼吸灯的闪烁,重点是函数:

466     g_leds_data[i]->cdev.brightness_set = mt65xx_led_set;

467     g_leds_data[i]->cdev.blink_set = mt65xx_blink_set;    //控制呼吸灯闪烁

468

469     INIT_WORK(&g_leds_data[i]->work, mt_mt65xx_led_work);

470

471     ret = led_classdev_register(&pdev->dev, &g_leds_data[i]->cdev); //注册相关设备文件

472

函数:mt65xx_blink_set主要是通过如下流程来控制呼吸灯闪烁:

mt65xx_blink_set ----> mt_mt65xx_blink_set -----> mt_led_blink_pmic

268#define PMIC_PERIOD_NUM (sizeof(pmic_freqsel_array)/sizeof(pmic_freqsel_array[0]))

269// 100 * period, ex: 0.01 Hz -> 0.01 * 100 = 1

270int pmic_period_array[] = {250,500,1000,1250,1666,2000,2500,10000};

271//int pmic_freqsel_array[] = {99999, 9999, 4999, 1999, 999, 499, 199, 4, 0};

272int pmic_freqsel_array[] = {0, 4, 199, 499, 999, 1999, 1999, 1999};

274static int find_time_index_pmic(int time_ms) {

275 int i;

276 for(i=0;i

277     if(time_ms<=pmic_period_array[i]) {

278         return i;

279     } else {

280         continue;

281     }

282 }

283 return PMIC_PERIOD_NUM-1;

284}

286int mt_led_blink_pmic(enum mt65xx_led_pmic pmic_type, struct nled_setting* led) {

287 int time_index = 0;

288 int duty = 0;

289 LEDS_DEBUG("[LED]led_blink_pmic: pmic_type=%d\n", pmic_type);

290

291 if((pmic_type != MT65XX_LED_PMIC_NLED_ISINK0 && pmic_type!= MT65XX_LED_PMIC_NLED_ISINK1 &&

292     pmic_type!= MT65XX_LED_PMIC_NLED_ISINK2 && pmic_type!= MT65XX_LED_PMIC_NLED_ISINK3) || led->nled_mode != NLED_BLINK) {

293     return -1;

294 }

295

296 LEDS_DEBUG("[LED]LED blink on time = %d offtime = %d\n",led->blink_on_time,led->blink_off_time);

297 time_index = find_time_index_pmic(led->blink_on_time + led->blink_off_time);

298 LEDS_DEBUG("[LED]LED index is %d  freqsel=%d\n", time_index, pmic_freqsel_array[time_index]);

299 duty=32*led->blink_on_time/(led->blink_on_time + led->blink_off_time);

300 //upmu_set_rg_drv_2m_ck_pdn(0x0); // Disable power down (Indicator no need)

301     upmu_set_rg_drv_32k_ck_pdn(0x0); // Disable power down

302 switch(pmic_type){

303     case MT65XX_LED_PMIC_NLED_ISINK0:

304         upmu_set_rg_isink0_ck_pdn(0);

305         upmu_set_rg_isink0_ck_sel(0);

306         upmu_set_isink_ch0_mode(PMIC_PWM_0);

307         upmu_set_isink_ch0_step(0x0);//4mA

308         upmu_set_isink_dim0_duty(duty);

309         upmu_set_isink_dim0_fsel(pmic_freqsel_array[time_index]);

310         upmu_set_isink_breath0_trf_sel(0x0);

311         upmu_set_isink_breath0_ton_sel(0x02);

312         upmu_set_isink_breath0_toff_sel(0x05);

313         upmu_set_isink_ch0_en(0x01);

314         break;

315     case MT65XX_LED_PMIC_NLED_ISINK1:

316         upmu_set_rg_isink1_ck_pdn(0);

317         upmu_set_rg_isink1_ck_sel(0);

318         upmu_set_isink_ch1_mode(PMIC_PWM_0);

319         upmu_set_isink_ch1_step(0x0);//4mA

320         upmu_set_isink_dim1_duty(duty);

321         upmu_set_isink_dim1_fsel(pmic_freqsel_array[time_index]);

322         upmu_set_isink_breath1_trf_sel(0x0);

323         upmu_set_isink_breath1_ton_sel(0x02);

324         upmu_set_isink_breath1_toff_sel(0x05);

325         upmu_set_isink_ch1_en(0x01);

326         break;

327     case MT65XX_LED_PMIC_NLED_ISINK2:

328         upmu_set_rg_isink2_ck_pdn(0);

329         upmu_set_rg_isink2_ck_sel(0);

330         upmu_set_isink_ch2_mode(PMIC_PWM_0);

331         upmu_set_isink_ch2_step(0x0);//4mA

332         upmu_set_isink_dim2_duty(duty);

333         upmu_set_isink_dim2_fsel(pmic_freqsel_array[time_index]);

334         upmu_set_isink_breath2_trf_sel(0x0);

335         upmu_set_isink_breath2_ton_sel(0x02);

336         upmu_set_isink_breath2_toff_sel(0x05);

337         upmu_set_isink_ch2_en(0x01);

338         break;

339     case MT65XX_LED_PMIC_NLED_ISINK3:

340         upmu_set_rg_isink3_ck_pdn(0);

341         upmu_set_rg_isink3_ck_sel(0);

342         upmu_set_isink_ch3_mode(PMIC_PWM_0);

343         upmu_set_isink_ch3_step(0x3);//16mA

344         upmu_set_isink_dim3_duty(duty);

345         upmu_set_isink_dim3_fsel(pmic_freqsel_array[time_index]);

346         upmu_set_isink_breath3_trf_sel(0x0);

347         upmu_set_isink_breath3_ton_sel(0x02);

348         upmu_set_isink_breath3_toff_sel(0x05);

349         upmu_set_isink_ch3_en(0x01);

350         break;

351     default:

352     break;

353 }

354 return 0;

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

160int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)

161{

162 led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,

163                   "%s", led_cdev->name);

164 if (IS_ERR(led_cdev->dev))

165     return PTR_ERR(led_cdev->dev);

166

167#ifdef CONFIG_LEDS_TRIGGERS

168 init_rwsem(&led_cdev->trigger_lock);

169#endif

170 /* add to the list of leds */

171 down_write(&leds_list_lock);

172 list_add_tail(&led_cdev->node, &leds_list);

173 up_write(&leds_list_lock);

174

175 if (!led_cdev->max_brightness)

176     led_cdev->max_brightness = LED_FULL;

177

178 led_update_brightness(led_cdev);

179

180 init_timer(&led_cdev->blink_timer);

181 led_cdev->blink_timer.function = led_timer_function;

182 led_cdev->blink_timer.data = (unsigned long)led_cdev;

183

184#ifdef CONFIG_LEDS_TRIGGERS

185 led_trigger_set_default(led_cdev);

186#endif

187

188 printk(KERN_DEBUG "Registered led device: %s\n",

189         led_cdev->name);

190

191 return 0;

192}

注册的设备文件关联在leds_class中:

228 leds_class->dev_attrs = led_class_attrs;

73

74 static struct device_attribute led_class_attrs[] = {

75  __ATTR(brightness, 0644, led_brightness_show, led_brightness_store),

76  __ATTR(max_brightness, 0444, led_max_brightness_show, NULL),

77 #ifdef CONFIG_LEDS_TRIGGERS

78  __ATTR(trigger, 0644, led_trigger_show, led_trigger_store),

79 #endif

80  __ATTR_NULL,

81};

然后通过:init_timer(&led_cdev->blink_timer);注册了软件控制呼吸灯闪烁的办法。

控制呼吸灯闪烁的办法;而是mt65xx_blink_set。

在上层调用mt65xx_blink_set函数来控制呼吸灯闪烁,主要是通过trigger触发器接口的办法实现的。

4、trigger触发器

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

94 char const*const RED_TRIGGER_FILE

95        = "/sys/class/leds/red/trigger";

253 write_str(RED_TRIGGER_FILE, "timer");

很显然我们驱动中对应的响应函数为:led_trigger_store,往该函数传入的参数为:"timer"

代码位置:kernel/drivers/leds/led-triggers.c

34ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,

35      const char *buf, size_t count)

36{

37  struct led_classdev *led_cdev = dev_get_drvdata(dev);

38  char trigger_name[TRIG_NAME_MAX];

39  struct led_trigger *trig;

40  size_t len;

41

42  trigger_name[sizeof(trigger_name) - 1] = '\0';

43  strncpy(trigger_name, buf, sizeof(trigger_name) - 1);

44  len = strlen(trigger_name);

45

46  if (len && trigger_name[len - 1] == '\n')

47      trigger_name[len - 1] = '\0';

48

49  if (!strcmp(trigger_name, "none")) {

50      led_trigger_remove(led_cdev);

51      return count;

52  }

53

54  down_read(&triggers_list_lock);

55  list_for_each_entry(trig, &trigger_list, next_trig) {

56      if (!strcmp(trigger_name, trig->name)) {

57          down_write(&led_cdev->trigger_lock);

58          led_trigger_set(led_cdev, trig);

59          up_write(&led_cdev->trigger_lock);

60

61          up_read(&triggers_list_lock);

62          return count;

63      }

64  }

65  up_read(&triggers_list_lock);

66

67  return -EINVAL;

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

119 static struct led_trigger timer_led_trigger = {

120 .name     = "timer",

121 .activate = timer_trig_activate,

122 .deactivate = timer_trig_deactivate,

123};

124

125 static int __init timer_trig_init(void)

126 {

127 return led_trigger_register(&timer_led_trigger);

128 }

129

130 static void __exit timer_trig_exit(void)

131 {

132 led_trigger_unregister(&timer_led_trigger);

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

253     write_str(RED_TRIGGER_FILE, "timer");

254     while (((access(RED_DELAY_OFF_FILE, F_OK) == -1) || (access(RED_DELAY_OFF_FILE, R_OK|W_OK) == -1)) && i<10) {

255         ALOGD("RED_DELAY_OFF_FILE doesn't exist or cannot write!!\n");

256         led_wait_delay(5);//sleep 5ms for wait kernel LED class create led delay_off/delay_on node of fs

257         i++;

258     }

259     write_int(RED_DELAY_OFF_FILE, offMS);

260     write_int(RED_DELAY_ON_FILE, onMS);

从刚才分析我们知道:以上代码会首先更新timer的触发器,然后等待5ms,创建delay_on和delay_off的设备文件,最后往该设备文件中分别写入offMs和onMs.很显然,最后我们要找的就是delay_on和delay_off对应的处理函数函数。

59static ssize_t led_delay_off_store(struct device *dev,

60      struct device_attribute *attr, const char *buf, size_t size)

61{

62  struct led_classdev *led_cdev = dev_get_drvdata(dev);

63  int ret = -EINVAL;

64  char *after;

65  unsigned long state = simple_strtoul(buf, &after, 10);

66  size_t count = after - buf;

67

68  if (isspace(*after))

69      count++;

70

71  if (count == size) {

72      led_blink_set(led_cdev, &led_cdev->blink_delay_on, &state);

73      led_cdev->blink_delay_off = state;

74      ret = count;

75  }

76

77  return ret;

78}

HAL层中首先写入的是delay_off的时间,对应处理函数如上,之后进入了函数led_blink_set中:

71void led_blink_set(struct led_classdev *led_cdev,

72         unsigned long *delay_on,

73         unsigned long *delay_off)

74{

75  del_timer_sync(&led_cdev->blink_timer);

76

77  if (led_cdev->blink_set &&

78      !led_cdev->blink_set(led_cdev, delay_on, delay_off))

79      return;

80

81  /* blink with 1 Hz as default if nothing specified */

82  if (!*delay_on && !*delay_off)

83      *delay_on = *delay_off = 500;

84

85  led_set_software_blink(led_cdev, *delay_on, *delay_off);

86}

87EXPORT_SYMBOL(led_blink_set);

该函数首先删除掉软件方法闪烁的定时器,然后调用了led_cdev->blink_set,在blink_set函数中,因为delay_on为0,而delay_off为300,所以会返回-1,从而进入函数led_set_software_blink。

35static void led_set_software_blink(struct led_classdev *led_cdev,

36                 unsigned long delay_on,

37                 unsigned long delay_off)

38{

39  int current_brightness;

40

41  current_brightness = led_get_brightness(led_cdev);

42  if (current_brightness)

43      led_cdev->blink_brightness = current_brightness;

44  if (!led_cdev->blink_brightness)

45      led_cdev->blink_brightness = led_cdev->max_brightness;

46

47  if (led_get_trigger_data(led_cdev) &&

48      delay_on == led_cdev->blink_delay_on &&

49      delay_off == led_cdev->blink_delay_off)

50      return;

51

52  led_stop_software_blink(led_cdev);

53

54  led_cdev->blink_delay_on = delay_on;

55  led_cdev->blink_delay_off = delay_off;

56

57  /* never on - don't blink */

58  if (!delay_on)

59      return;

60

61  /* never off - just set to brightness */

62  if (!delay_off) {

63      led_set_brightness(led_cdev, led_cdev->blink_brightness);

64      return;

65  }

66

67  mod_timer(&led_cdev->blink_timer, jiffies + 1);

68}

在该函数中更新了led_cdev->blink_delay_off为我们传入的delay_off,也就是300,然后又因为delay_on为0,所以中途退出,不会启动最后的呼吸灯闪烁的软件控制定时器。之后,HAL继续write_int(RED_DELAY_ON_FILE, onMS);往delay_off接口中写入了onMS,也就是上面的350.类似的:

30static ssize_t led_delay_on_store(struct device *dev,

31      struct device_attribute *attr, const char *buf, size_t size)

32{

33  struct led_classdev *led_cdev = dev_get_drvdata(dev);

34  int ret = -EINVAL;

35  char *after;

36  unsigned long state = simple_strtoul(buf, &after, 10);

37  size_t count = after - buf;

38

39  if (isspace(*after))

40      count++;

41

42  if (count == size) {

43      led_blink_set(led_cdev, &state, &led_cdev->blink_delay_off);

44      led_cdev->blink_delay_on = state;

45      ret = count;

46  }

47

48  return ret;

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 呼吸灯权限_Android 呼吸灯流程分析相关推荐

  1. 高通Android智能平台环境搭建_编译流程分析

    高通Android智能平台环境搭建_编译流程分析 高通平台环境搭建,编译,系统引导流程分析 TOC \o \h \z \u 1. 高通平台android开发总结. 7 1.1 搭建高通平台环境开发环境 ...

  2. android 关机 流程_android系统关机流程分析

    关机动作从按键触发中断,linux kernel层给android framework层返回按键事件进入  framework层,再从 framework层到kernel层执行kernel层关机任务. ...

  3. Android 4.0按键事件以及系统流程分析

    Android 4.0中按键的处理流程 按键在Android系统中,有着不同的代表意义.以前的全键盘的手机代码没有阅读过,所以也不是很了解.本人介绍的是在触摸屏的手机上的按键消息的处理流程. 在现在触 ...

  4. 高通android智能平台环境搭建_编译流程分析,高通平台环境搭建,编译,系统引导流程分析参考...

    高通有两个cpu,他们分别跑不同的系统,应用程序(ap)端是android系统,modem 端是高通自己的系统. 要编译出可供烧写使用的镜像文件需要三部分代码: 1) 获取经过高通打补丁的 andro ...

  5. android 申请sdcard权限_Android sdcard读写权限问题之中的一个

    博主在刚刚在学习过程中发现了一个关于android往sdcard读写的问题, 配置了该配置的提示无读写权限. 在AndroidManifest.xml文件里配置清单例如以下 package=" ...

  6. Android系统的心脏-Zygote进程启动流程分析

    简介: Android中,Zygote是整个Android系统的核心进程,是Android系统的心脏.所有的Android应用程序,包括Android框架层所在的进程system_server,都是由 ...

  7. Android 5.1 长按power键流程分析

    安全模式简述 android平台,在长按power / menu键时会快速进入一个模式选择,部分定制的平台是直接进入安装模式,也可以定制成公司需要的一些特定功能模式,比如报警 ... power 也属 ...

  8. Android 5.1 Phone 挂断电话流程分析

    写在前面的话 本文主要分析Android挂断电话的流程,研究的代码是Android 5.1的,以CDMA为例,GSM同理. 挂断电话主要分两种情况: 本地主动挂断电话 \color{red}{本地主动 ...

  9. android cliptopadding java代码_android:clipToPadding属性的分析——以ListView的别样padding为例...

    package cn.com.bravesoft.testlistviewloadmore; import java.util.ArrayList; import java.util.HashMap; ...

  10. android权限控制泄露,Android应用的权限泄露分析

    摘要: 随着智能移动终端功能和用户体验的日益完善,智能手机已经被越来越多的用户使用.研究数据表明,Android手机的购买量正在逐步超越个人电脑.Android系统被应用在越来越多的智能手机上面,但是 ...

最新文章

  1. 一个小团队使用的知识管理方案与工具
  2. EasyNet.Solr架构
  3. html解析器编译原理,编译原理实验报告词法分析器(内含源代码).docx
  4. php汉字转换拼音,PHP汉字转换拼音的函数代码
  5. 51nod 1649 齐头并进 (djikstra求最短路径,只用跑一次)
  6. model中的数据如何显示在html上,django – 使用DRF ModelViewSet和TemplateHTMLRenderer时如何访问模板中的数据?...
  7. 【易实战】SpringCloud Greenwich架构概览深度详解
  8. 黑龙江工程学院锐捷校园网连接路由器免认证
  9. 社会工程学之黑客番外——密码学
  10. Wilcoxon符号秩检验及其matlab代码
  11. 人生哲理看完醍醐灌顶茅塞顿开(大部分技术都不懂所以爬不上去)
  12. SSM整合APP项目
  13. 2021-12-01 WPF上位机 103-西门子S7协议之V区,DB区读数据方法流程解析
  14. Prometheus监控神器-Alertmanager篇(1)
  15. 在高分辨率或者扩展屏下微信截图出现放大问题的解决
  16. 数组和字符串赋值的问题(定义时不初始化)
  17. [转载]屏蔽双显卡笔记本的独显
  18. JAVA AIO编程
  19. building workspace js validation
  20. 使用FakeAPP进行AI换脸必看!!

热门文章

  1. 小程序之H5游戏砸金蛋
  2. C# Winform右下角弹窗方式
  3. pandas面板(Panel)
  4. 如何下载ScienceDirect(Elsevier)文献的补充材料
  5. 实体消歧方法(1)__BOOTLEG
  6. 通常所说的微型计算机的主机主要包括(),微型计算机的主机主要包括
  7. 林炳文Evankaka原创作品之mybatis的增删改查简单操作
  8. python中计算ln和lg的方法
  9. 深入理解快速排序和 STL 的 sort 算法
  10. php导出生成word,php导出生成word的方法