Android 呼吸灯流程分析
一、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<PMIC_PERIOD_NUM;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。
好了,呼吸灯闪烁,基本就是这样。。。