天天看點

android中的背光驅動層次分析1.Android的Setting2.Android的背光JNI層3.Android的背光HAL層4.Linux的背光核心層5.Linux的背光驅動層6.總結

2013-06-17 16:28  287人閱讀  評論(0)  收藏  舉報

目錄(?)[+]

  1. Android的Setting
  2. Android的背光JNI層
  3. Android的背光HAL層
  4. Linux的背光核心層
  5. Linux的背光驅動層
  6. 總結

其實Android的底層就是Linux,是以其驅動本質就是Linux驅動,但是這些Linux驅動是服務上層Android的,是以需遵循上Android的一些接口規範。是以涉及到的Android驅動都應應密切關注上層傳遞的接口。本文介紹的LCD背光驅動就是從上層一直往下層展現,但是筆者畢竟不是專注于Android上層,礙于知識不充裕,是以對上層的東西介紹得相對簡單。

1.Android的Setting

Android的設定裡面管理了Andoird系統的所有設定,其中當然包括了螢幕亮度設定。 Setting的源碼目錄在: mydroid/packages/apps/Settings/src/com/android/settings

亮度設定的java源檔案在: mydroid/packages/apps/Settings/src/com/android/settings/BrightnessPreference.java

打開這個檔案看到: [java]  view plain copy

  1. public class BrightnessPreference extends SeekBarDialogPreference implements  
  2.         SeekBar.OnSeekBarChangeListener, CheckBox.OnCheckedChangeListener {  
  3.     private SeekBar mSeekBar;  
  4.     private CheckBox mCheckBox;  
  5.     private int mOldBrightness;  
  6.     private int mOldAutomatic;  
  7.     private boolean mAutomaticAvailable;  
  8.     private boolean mRestoredOldState;  
  9.     // Backlight range is from 0 - 255. Need to make sure that user   
  10.     // doesn't set the backlight to 0 and get stuck   
  11.     private int mScreenBrightnessDim =  
  12. ...  

[java]  view plain copy

  1. public class BrightnessPreference extends SeekBarDialogPreference implements  
  2.         SeekBar.OnSeekBarChangeListener, CheckBox.OnCheckedChangeListener {  
  3.     private SeekBar mSeekBar;  
  4.     private CheckBox mCheckBox;  
  5.     private int mOldBrightness;  
  6.     private int mOldAutomatic;  
  7.     private boolean mAutomaticAvailable;  
  8.     private boolean mRestoredOldState;  
  9.     // Backlight range is from 0 - 255. Need to make sure that user  
  10.     // doesn't set the backlight to 0 and get stuck  
  11.     private int mScreenBrightnessDim =  
  12. ...  

Android的最上層已經将背光亮度量化為了[0,255]個等級,并且提示注意不要設定為0,是以在進行最低層的背光驅動編寫時,可以合理按這個範圍部署背光的亮度。

2.Android的背光JNI層

背光的JNI層源碼在: mydroid/frameworks/base/services/jni/com_android_server_LightsService.cpp

這一層就是調用HAL層的方法,為上一層實作一個設定亮度接口。

3.Android的背光HAL層

Java App和JNI一般是google維護的,是以源碼位置相對固定,HAL有産品商開發維護的,是以位置是不固定的,看産品上喜好,筆者使用的TI OMAP4平台,背光的HAL層代碼就在: mydroid/device/ti/xxx_product/liblights/light.c

先浏覽欣賞一下light.c先 [cpp]  view plain copy

  1. #define LOG_TAG "lights"   
  2. #include <cutils/log.h>   
  3. #include <stdint.h>   
  4. #include <string.h>   
  5. #include <unistd.h>   
  6. #include <errno.h>   
  7. #include <fcntl.h>   
  8. #include <pthread.h>   
  9. #include <sys/ioctl.h>   
  10. #include <sys/types.h>   
  11. #include <hardware/lights.h>   
  12. static pthread_once_t g_init = PTHREAD_ONCE_INIT;  
  13. static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;  
  14. char const*const LCD_FILE  
  15.         = "/sys/class/leds/lcd-backlight/brightness";  
  16. char const*const KEYBOARD_FILE  
  17.         = "/sys/class/leds/keyboard-backlight/brightness";  
  18. char const*const CHARGING_LED_FILE  
  19.         = "/sys/class/leds/battery-led/brightness";  
  20. char const*const RED_LED_FILE  
  21.         = "/sys/class/leds/red/brightness";  
  22. char const*const RED_DELAY_ON_FILE  
  23.         = "/sys/class/leds/red/delay_on";  
  24. char const*const RED_DELAY_OFF_FILE  
  25.         = "/sys/class/leds/red/delay_off";  
  26. char const*const GREEN_LED_FILE  
  27.         = "/sys/class/leds/green/brightness";  
  28. char const*const GREEN_DELAY_ON_FILE  
  29.         = "/sys/class/leds/green/delay_on";  
  30. char const*const GREEN_DELAY_OFF_FILE  
  31.         = "/sys/class/leds/green/delay_off";  
  32. char const*const BLUE_LED_FILE  
  33.         = "/sys/class/leds/blue/brightness";  
  34. char const*const BLUE_DELAY_ON_FILE  
  35.         = "/sys/class/leds/blue/delay_on";  
  36. char const*const BLUE_DELAY_OFF_FILE  
  37.         = "/sys/class/leds/blue/delay_off";  
  38. ...  

[cpp]  view plain copy

  1. #define LOG_TAG "lights"  
  2. #include <cutils/log.h>  
  3. #include <stdint.h>  
  4. #include <string.h>  
  5. #include <unistd.h>  
  6. #include <errno.h>  
  7. #include <fcntl.h>  
  8. #include <pthread.h>  
  9. #include <sys/ioctl.h>  
  10. #include <sys/types.h>  
  11. #include <hardware/lights.h>  
  12. static pthread_once_t g_init = PTHREAD_ONCE_INIT;  
  13. static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;  
  14. char const*const LCD_FILE  
  15.         = "/sys/class/leds/lcd-backlight/brightness";  
  16. char const*const KEYBOARD_FILE  
  17.         = "/sys/class/leds/keyboard-backlight/brightness";  
  18. char const*const CHARGING_LED_FILE  
  19.         = "/sys/class/leds/battery-led/brightness";  
  20. char const*const RED_LED_FILE  
  21.         = "/sys/class/leds/red/brightness";  
  22. char const*const RED_DELAY_ON_FILE  
  23.         = "/sys/class/leds/red/delay_on";  
  24. char const*const RED_DELAY_OFF_FILE  
  25.         = "/sys/class/leds/red/delay_off";  
  26. char const*const GREEN_LED_FILE  
  27.         = "/sys/class/leds/green/brightness";  
  28. char const*const GREEN_DELAY_ON_FILE  
  29.         = "/sys/class/leds/green/delay_on";  
  30. char const*const GREEN_DELAY_OFF_FILE  
  31.         = "/sys/class/leds/green/delay_off";  
  32. char const*const BLUE_LED_FILE  
  33.         = "/sys/class/leds/blue/brightness";  
  34. char const*const BLUE_DELAY_ON_FILE  
  35.         = "/sys/class/leds/blue/delay_on";  
  36. char const*const BLUE_DELAY_OFF_FILE  
  37.         = "/sys/class/leds/blue/delay_off";  
  38. ...  

[cpp]  view plain copy

  1. static int  
  2. set_light_backlight(struct light_device_t* dev,  
  3.         struct light_state_t const* state)  
  4. {  
  5.     int err = 0;  
  6.     int brightness = rgb_to_brightness(state);  
  7.     pthread_mutex_lock(&g_lock);  
  8.     err = write_int(LCD_FILE, brightness);  
  9.     pthread_mutex_unlock(&g_lock);  
  10.     return err;  
  11. }  

[cpp]  view plain copy

  1. static int  
  2. set_light_backlight(struct light_device_t* dev,  
  3.         struct light_state_t const* state)  
  4. {  
  5.     int err = 0;  
  6.     int brightness = rgb_to_brightness(state);  
  7.     pthread_mutex_lock(&g_lock);  
  8.     err = write_int(LCD_FILE, brightness);  
  9.     pthread_mutex_unlock(&g_lock);  
  10.     return err;  
  11. }  

這裡關注一下LCD背光的sys檔案節點:"/sys/class/leds/lcd-backlight/brightness" 疑問1:這個sys接口是誰約定的呢? 疑問2:可不可以改這個接口呢? 疑問3:為什麼這裡還有其他led的很多sys檔案節點呢: 先解釋疑問3,因為一個Android裝置,不隻是有LCD背光的LED,還有可以有其他很多LED(當然也可以沒有),是以這裡一并實作了這些LED的HAL,産品商可以沿用這些HAL。 先來試驗一把: 啟動Android裝置,在Setting裡更改亮度,然後在序列槽指令行中或“adb shell"中運作指令cat /sys/class/leds/lcd-backlight/brightness,會發現Setting的更改後可以通過cat 顯示Setting的更改。是以更加确認了這個"/sys/class/leds/lcd-backlight/brightness"接口是正确的。 因為HAL層是和Linux Kernel互動的,是以這裡如果能cat實作讀取亮度等級,那麼在Linux kernel層就一定實作了sysfs接口。

4.Linux的背光核心層

背光的核心層源碼在:driver/leds/led-class.c 這一層由核心開發者去維護,不用我們操心。看看它的init函數 [cpp]  view plain copy

  1. static int __init leds_init(void)  
  2. {  
  3.     leds_class = class_create(THIS_MODULE, "leds");  
  4.     if (IS_ERR(leds_class))  
  5.         return PTR_ERR(leds_class);  
  6.     leds_class->suspend = led_suspend;  
  7.     leds_class->resume = led_resume;  
  8.     leds_class->dev_attrs = led_class_attrs;  
  9.     return 0;  
  10. }  

[cpp]  view plain copy

  1. static int __init leds_init(void)  
  2. {  
  3.     leds_class = class_create(THIS_MODULE, "leds");  
  4.     if (IS_ERR(leds_class))  
  5.         return PTR_ERR(leds_class);  
  6.     leds_class->suspend = led_suspend;  
  7.     leds_class->resume = led_resume;  
  8.     leds_class->dev_attrs = led_class_attrs;  
  9.     return 0;  
  10. }  

好明顯,正是由于它建立了sys class,名字為”leds“,是以上面的背光sys檔案節點"/sys/class/leds/”就有了來由,那麼剩下的“../lcd-backlight/brightness"又是怎麼來的呢?看看一個注冊led裝置類的函數. [cpp]  view plain copy

  1. int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)  
  2. {  
  3.     led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,  
  4.                       "%s", led_cdev->name);  
  5.     if (IS_ERR(led_cdev->dev))  
  6.         return PTR_ERR(led_cdev->dev);  
  7. #ifdef CONFIG_LEDS_TRIGGERS   
  8.     init_rwsem(&led_cdev->trigger_lock);  
  9. ...  

[cpp]  view plain copy

  1. int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)  
  2. {  
  3.     led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,  
  4.                       "%s", led_cdev->name);  
  5.     if (IS_ERR(led_cdev->dev))  
  6.         return PTR_ERR(led_cdev->dev);  
  7. #ifdef CONFIG_LEDS_TRIGGERS  
  8.     init_rwsem(&led_cdev->trigger_lock);  
  9. ...  

這個注冊函數的接口最終會被我們要開發的背光驅動調用,這個接口在/sys/class/leds/下又建立了一個裝置接口,名字是led_cdev->name。好明顯這裡的led_cdev->name應該就是”lcd-backlight“,究竟是不是真的這樣呢?繼續看。 這個led-class.c在實作兩個裝置屬性,看代碼: [cpp]  view plain copy

  1. static struct device_attribute led_class_attrs[] = {  
  2.     __ATTR(brightness, 0644, led_brightness_show, led_brightness_store),  
  3.     __ATTR(max_brightness, 0444, led_max_brightness_show, NULL),  
  4. #ifdef CONFIG_LEDS_TRIGGERS   
  5.     __ATTR(trigger, 0644, led_trigger_show, led_trigger_store),  
  6. #endif   
  7.     __ATTR_NULL,  
  8. };  

[cpp]  view plain copy

  1. static struct device_attribute led_class_attrs[] = {  
  2.     __ATTR(brightness, 0644, led_brightness_show, led_brightness_store),  
  3.     __ATTR(max_brightness, 0444, led_max_brightness_show, NULL),  
  4. #ifdef CONFIG_LEDS_TRIGGERS  
  5.     __ATTR(trigger, 0644, led_trigger_show, led_trigger_store),  
  6. #endif  
  7.     __ATTR_NULL,  
  8. };  

看到了屬性名字為”brightness“,這似乎越來越接近解釋"/sys/class/leds/lcd-backlight/brightness"的由來了。

5.Linux的背光驅動層

Linux的背光驅動層就是完全由開發者去實作了,其實這層很簡單,無非就是通過pwm實作設定背光。在這一層要注意将背光亮度量化為0~255,這是Android上層約定的。

這一層的采用Linux的platform device/driver 模型。它最終會調用Linux核心層leds-class.c的接口。 看看它的probe函數片段: [cpp]  view plain copy

  1. struct display_led_data {  
  2.     struct led_classdev pri_display_class_dev;  
  3.     struct led_classdev sec_display_class_dev;  
  4.     struct omap4_disp_led_platform_data *led_pdata;  
  5.     struct mutex pri_disp_lock;  
  6.     struct mutex sec_disp_lock;  
  7. };  
  8. static int omap4_XXX_display_probe(struct platform_device *pdev)  
  9. {  
  10.     int ret;  
  11.     struct display_led_data *info;  
  12.     pr_info("%s:Enter\n", __func__);  
  13.     if (pdev->dev.platform_data == NULL) {  
  14.         pr_err("%s: platform data required\n", __func__);  
  15.         return -ENODEV;  
  16.     }  
  17.     info = kzalloc(sizeof(struct display_led_data), GFP_KERNEL);  
  18.     if (info == NULL) {  
  19.         ret = -ENOMEM;  
  20.         return ret;  
  21.     }  
  22.     info->led_pdata = pdev->dev.platform_data;  
  23.     platform_set_drvdata(pdev, info);  
  24.     info->pri_display_class_dev.name = "lcd-backlight";  
  25.     info->pri_display_class_dev.brightness_set = omap4_xxx_primary_disp_store;  
  26. ...  
  27.     ret = led_classdev_register(&pdev->dev,  
  28.                     &info->pri_display_class_dev);  
  29.     if (ret < 0) {  
  30.         pr_err("%s: Register led class failed\n", __func__);  
  31.         kfree(info);  
  32.         return ret;  
  33.     }  
  34.     if (info->led_pdata->flags & LEDS_CTRL_AS_TWO_DISPLAYS) {  
  35.         pr_info("%s: Configuring the secondary LED\n", __func__);  
  36.         info->sec_display_class_dev.name = "lcd-backlight2";  
  37.         info->sec_display_class_dev.brightness_set =  
  38.             omap4_mirage_secondary_disp_store;  
  39.         info->sec_display_class_dev.max_brightness = LED_OFF;  
  40.         mutex_init(&info->sec_disp_lock);  
  41.         ret = led_classdev_register(&pdev->dev,  
  42.                         &info->sec_display_class_dev);  
  43. ...  

[cpp]  view plain copy

  1. struct display_led_data {  
  2.     struct led_classdev pri_display_class_dev;  
  3.     struct led_classdev sec_display_class_dev;  
  4.     struct omap4_disp_led_platform_data *led_pdata;  
  5.     struct mutex pri_disp_lock;  
  6.     struct mutex sec_disp_lock;  
  7. };  
  8. static int omap4_XXX_display_probe(struct platform_device *pdev)  
  9. {  
  10.     int ret;  
  11.     struct display_led_data *info;  
  12.     pr_info("%s:Enter\n", __func__);  
  13.     if (pdev->dev.platform_data == NULL) {  
  14.         pr_err("%s: platform data required\n", __func__);  
  15.         return -ENODEV;  
  16.     }  
  17.     info = kzalloc(sizeof(struct display_led_data), GFP_KERNEL);  
  18.     if (info == NULL) {  
  19.         ret = -ENOMEM;  
  20.         return ret;  
  21.     }  
  22.     info->led_pdata = pdev->dev.platform_data;  
  23.     platform_set_drvdata(pdev, info);  
  24.     info->pri_display_class_dev.name = "lcd-backlight";  
  25.     info->pri_display_class_dev.brightness_set = omap4_xxx_primary_disp_store;  
  26. ...  
  27.     ret = led_classdev_register(&pdev->dev,  
  28.                     &info->pri_display_class_dev);  
  29.     if (ret < 0) {  
  30.         pr_err("%s: Register led class failed\n", __func__);  
  31.         kfree(info);  
  32.         return ret;  
  33.     }  
  34.     if (info->led_pdata->flags & LEDS_CTRL_AS_TWO_DISPLAYS) {  
  35.         pr_info("%s: Configuring the secondary LED\n", __func__);  
  36.         info->sec_display_class_dev.name = "lcd-backlight2";  
  37.         info->sec_display_class_dev.brightness_set =  
  38.             omap4_mirage_secondary_disp_store;  
  39.         info->sec_display_class_dev.max_brightness = LED_OFF;  
  40.         mutex_init(&info->sec_disp_lock);  
  41.         ret = led_classdev_register(&pdev->dev,  
  42.                         &info->sec_display_class_dev);  
  43. ...  

需關注這裡設定了info->pri_display_class_dev.name = "lcd-backlight";,然後調用led_classdev_register函數注冊,是以完美解釋了 led_cdev->name就是 "lcd-backlight",完美解釋了"/sys/class/leds/lcd-backlight/brightness"的由來。

另外info->pri_display_class_dev.brightness_set = omap4_xxx_primary_disp_store;中的omap4_xxx_primary_disp_store()就是我們需要實作的設定背光亮度函數,函數裡面就是pwm操作。 到這裡又有一個疑問4:為什麼隻實作設定背光亮度的接口,而沒有實作讀取目前亮度量化值的接口? 其實Android上層自行處理了這個擷取亮度量化值的事情,也就是說Android上層設定了亮度是多少,上層會執行保留設定結果。無需再通過下層讀取。

6.總結

6.1針對之前的疑問1/2,其實LCD背光的sys接口路徑是可以改的,但是需要Linux核心層和Android HAL層配合來改,單單改一方都是會導緻Android Setting無法調節背光。 6.2需注意在實作Linux背光驅動時的亮度量化關系,也就是注意上層傳遞下來的亮度設定範圍是0~255。 6.3Android底層的Linux驅動都是服務于上層Java的,在做Android底層的Linux驅動時需要明确和上層的接口依賴關系,否則無法重用google或晶片廠商實作的接口,進而導緻功能無法用。 6.4.再一次證明了Android系統服務運作效率極低。設定背光,其實最終就是設定了一下pwm寄存器即可,但是從Android最頂層一層層調用下來,真是“費盡周折”,怪不得Android裝置的硬體配置明顯遠優于ios裝置,但是流暢體驗性卻不明顯優于ios裝置。