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

466g_leds_data[i]->cdev.brightness_set = mt65xx_led_set;467g_leds_data[i]->cdev.blink_set = mt65xx_blink_set;    //控制呼吸灯闪烁468469INIT_WORK(&g_leds_data[i]->work, mt_mt65xx_led_work);470471ret = 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 = 1270int 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) {275int i;276for(i=0;i<PMIC_PERIOD_NUM;i++) {277if(time_ms<=pmic_period_array[i]) {278return i;279} else {280continue;281}282}283return PMIC_PERIOD_NUM-1;284}286int mt_led_blink_pmic(enum mt65xx_led_pmic pmic_type, struct nled_setting* led) {287int time_index = 0;288int duty = 0;289LEDS_DEBUG("[LED]led_blink_pmic: pmic_type=%d\n", pmic_type);290291if((pmic_type != MT65XX_LED_PMIC_NLED_ISINK0 && pmic_type!= MT65XX_LED_PMIC_NLED_ISINK1 &&292pmic_type!= MT65XX_LED_PMIC_NLED_ISINK2 && pmic_type!= MT65XX_LED_PMIC_NLED_ISINK3) || led->nled_mode != NLED_BLINK) {293return -1;294}295296LEDS_DEBUG("[LED]LED blink on time = %d offtime = %d\n",led->blink_on_time,led->blink_off_time);297time_index = find_time_index_pmic(led->blink_on_time + led->blink_off_time);298LEDS_DEBUG("[LED]LED index is %d  freqsel=%d\n", time_index, pmic_freqsel_array[time_index]);299duty=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 down302switch(pmic_type){303case MT65XX_LED_PMIC_NLED_ISINK0:304upmu_set_rg_isink0_ck_pdn(0);305upmu_set_rg_isink0_ck_sel(0);306upmu_set_isink_ch0_mode(PMIC_PWM_0);307upmu_set_isink_ch0_step(0x0);//4mA308upmu_set_isink_dim0_duty(duty);309upmu_set_isink_dim0_fsel(pmic_freqsel_array[time_index]);310upmu_set_isink_breath0_trf_sel(0x0);311upmu_set_isink_breath0_ton_sel(0x02);312upmu_set_isink_breath0_toff_sel(0x05);313upmu_set_isink_ch0_en(0x01);314break;315case MT65XX_LED_PMIC_NLED_ISINK1:316upmu_set_rg_isink1_ck_pdn(0);317upmu_set_rg_isink1_ck_sel(0);318upmu_set_isink_ch1_mode(PMIC_PWM_0);319upmu_set_isink_ch1_step(0x0);//4mA320upmu_set_isink_dim1_duty(duty);321upmu_set_isink_dim1_fsel(pmic_freqsel_array[time_index]);322upmu_set_isink_breath1_trf_sel(0x0);323upmu_set_isink_breath1_ton_sel(0x02);324upmu_set_isink_breath1_toff_sel(0x05);325upmu_set_isink_ch1_en(0x01);326break;327case MT65XX_LED_PMIC_NLED_ISINK2:328upmu_set_rg_isink2_ck_pdn(0);329upmu_set_rg_isink2_ck_sel(0);330upmu_set_isink_ch2_mode(PMIC_PWM_0);331upmu_set_isink_ch2_step(0x0);//4mA332upmu_set_isink_dim2_duty(duty);333upmu_set_isink_dim2_fsel(pmic_freqsel_array[time_index]);334upmu_set_isink_breath2_trf_sel(0x0);335upmu_set_isink_breath2_ton_sel(0x02);336upmu_set_isink_breath2_toff_sel(0x05);337upmu_set_isink_ch2_en(0x01);338break;339case MT65XX_LED_PMIC_NLED_ISINK3:340upmu_set_rg_isink3_ck_pdn(0);341upmu_set_rg_isink3_ck_sel(0);342upmu_set_isink_ch3_mode(PMIC_PWM_0);343upmu_set_isink_ch3_step(0x3);//16mA344upmu_set_isink_dim3_duty(duty);345upmu_set_isink_dim3_fsel(pmic_freqsel_array[time_index]);346upmu_set_isink_breath3_trf_sel(0x0);347upmu_set_isink_breath3_ton_sel(0x02);348upmu_set_isink_breath3_toff_sel(0x05);349upmu_set_isink_ch3_en(0x01);350break;351default:352break;353}354return 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{162led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,163      "%s", led_cdev->name);164if (IS_ERR(led_cdev->dev))165return PTR_ERR(led_cdev->dev);166167#ifdef CONFIG_LEDS_TRIGGERS168init_rwsem(&led_cdev->trigger_lock);169#endif170/* add to the list of leds */171down_write(&leds_list_lock);172list_add_tail(&led_cdev->node, &leds_list);173up_write(&leds_list_lock);174175if (!led_cdev->max_brightness)176led_cdev->max_brightness = LED_FULL;177178led_update_brightness(led_cdev);179180init_timer(&led_cdev->blink_timer);181led_cdev->blink_timer.function = led_timer_function;182led_cdev->blink_timer.data = (unsigned long)led_cdev;183184#ifdef CONFIG_LEDS_TRIGGERS185led_trigger_set_default(led_cdev);186#endif187188printk(KERN_DEBUG "Registered led device: %s\n",189led_cdev->name);190191return 0;192}
注册的设备文件关联在leds_class中:

228 leds_class->dev_attrs = led_class_attrs;7374 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_TRIGGERS78__ATTR(trigger, 0644, led_trigger_show, led_trigger_store),79 #endif80__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_FILE95        = "/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,35const char *buf, size_t count)36{37struct led_classdev *led_cdev = dev_get_drvdata(dev);38char trigger_name[TRIG_NAME_MAX];39struct led_trigger *trig;40size_t len;4142trigger_name[sizeof(trigger_name) - 1] = '\0';43strncpy(trigger_name, buf, sizeof(trigger_name) - 1);44len = strlen(trigger_name);4546if (len && trigger_name[len - 1] == '\n')47trigger_name[len - 1] = '\0';4849if (!strcmp(trigger_name, "none")) {50led_trigger_remove(led_cdev);51return count;52}5354down_read(&triggers_list_lock);55list_for_each_entry(trig, &trigger_list, next_trig) {56if (!strcmp(trigger_name, trig->name)) {57down_write(&led_cdev->trigger_lock);58led_trigger_set(led_cdev, trig);59up_write(&led_cdev->trigger_lock);6061up_read(&triggers_list_lock);62return count;63}64}65up_read(&triggers_list_lock);6667return -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};124125 static int __init timer_trig_init(void)126 {127return led_trigger_register(&timer_led_trigger);128 }129130 static void __exit timer_trig_exit(void)131 {132led_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层中,闪烁的时候,做了如下处理:

253write_str(RED_TRIGGER_FILE, "timer");    254while (((access(RED_DELAY_OFF_FILE, F_OK) == -1) || (access(RED_DELAY_OFF_FILE, R_OK|W_OK) == -1)) && i<10) {255ALOGD("RED_DELAY_OFF_FILE doesn't exist or cannot write!!\n");256led_wait_delay(5);//sleep 5ms for wait kernel LED class create led delay_off/delay_on node of fs257i++;258}259write_int(RED_DELAY_OFF_FILE, offMS);260write_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,60struct device_attribute *attr, const char *buf, size_t size)61{62struct led_classdev *led_cdev = dev_get_drvdata(dev);63int ret = -EINVAL;64char *after;65unsigned long state = simple_strtoul(buf, &after, 10);66size_t count = after - buf;6768if (isspace(*after))69count++;7071if (count == size) {72led_blink_set(led_cdev, &led_cdev->blink_delay_on, &state);73led_cdev->blink_delay_off = state;74ret = count;75}7677return 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{75del_timer_sync(&led_cdev->blink_timer);7677if (led_cdev->blink_set &&78    !led_cdev->blink_set(led_cdev, delay_on, delay_off))79return;8081/* blink with 1 Hz as default if nothing specified */82if (!*delay_on && !*delay_off)83*delay_on = *delay_off = 500;8485led_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{39int current_brightness;4041current_brightness = led_get_brightness(led_cdev);42if (current_brightness)43led_cdev->blink_brightness = current_brightness;44if (!led_cdev->blink_brightness)45led_cdev->blink_brightness = led_cdev->max_brightness;4647if (led_get_trigger_data(led_cdev) &&48    delay_on == led_cdev->blink_delay_on &&49    delay_off == led_cdev->blink_delay_off)50return;5152led_stop_software_blink(led_cdev);5354led_cdev->blink_delay_on = delay_on;55led_cdev->blink_delay_off = delay_off;5657/* never on - don't blink */58if (!delay_on)59return;6061/* never off - just set to brightness */62if (!delay_off) {63led_set_brightness(led_cdev, led_cdev->blink_brightness);64return;65}6667mod_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,31struct device_attribute *attr, const char *buf, size_t size)32{33struct led_classdev *led_cdev = dev_get_drvdata(dev);34int ret = -EINVAL;35char *after;36unsigned long state = simple_strtoul(buf, &after, 10);37size_t count = after - buf;3839if (isspace(*after))40count++;4142if (count == size) {43led_blink_set(led_cdev, &state, &led_cdev->blink_delay_off);44led_cdev->blink_delay_on = state; 45ret = count;46}4748return 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。
好了,呼吸灯闪烁,基本就是这样。。。






更多相关文章

  1. Android应用程序组件Content Provider的共享数据更新通知机制分
  2. Android应用程序键盘(Keyboard)消息处理机制分析(10)
  3. Android应用程序键盘(Keyboard)消息处理机制分析(21)
  4. Android(安卓)Universal Image Loader 源码分析
  5. android framework
  6. Android应用程序组件Content Provider的启动过程源代码分析(6)
  7. Android系统进程Zygote启动过程的源代码分析(3)
  8. IntentService简介
  9. [Android(安卓)Studio] Android(安卓)Studio如何提示函数用法

随机推荐

  1. 什么是JWT?Token与Session的区别?
  2. 用PHP写一个用户注册验证
  3. 技术人员如何评估物流信息API服务商合不
  4. 国内外7款好用的快递物流查询API工具
  5. zy0810
  6. vue--SEO
  7. 返回数组中所有的值并给其建立从0开始递
  8. 实例演绎对回调函数与递归函数的理解?
  9. zy0809
  10. 如何使用array_values()函数