1. 程式人生 > >Android 呼吸燈流程分析(二)

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。
好了,呼吸燈閃爍,基本就是這樣。。。