2013-06-17 16:28 287人閱讀 評論(0) 收藏 舉報
目錄(?)[+]
- Android的Setting
- Android的背光JNI層
- Android的背光HAL層
- Linux的背光核心層
- Linux的背光驅動層
- 總結
其實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
- public class BrightnessPreference extends SeekBarDialogPreference implements
- SeekBar.OnSeekBarChangeListener, CheckBox.OnCheckedChangeListener {
- private SeekBar mSeekBar;
- private CheckBox mCheckBox;
- private int mOldBrightness;
- private int mOldAutomatic;
- private boolean mAutomaticAvailable;
- private boolean mRestoredOldState;
- // Backlight range is from 0 - 255. Need to make sure that user
- // doesn't set the backlight to 0 and get stuck
- private int mScreenBrightnessDim =
- ...
[java] view plain copy
- public class BrightnessPreference extends SeekBarDialogPreference implements
- SeekBar.OnSeekBarChangeListener, CheckBox.OnCheckedChangeListener {
- private SeekBar mSeekBar;
- private CheckBox mCheckBox;
- private int mOldBrightness;
- private int mOldAutomatic;
- private boolean mAutomaticAvailable;
- private boolean mRestoredOldState;
- // Backlight range is from 0 - 255. Need to make sure that user
- // doesn't set the backlight to 0 and get stuck
- private int mScreenBrightnessDim =
- ...
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
- #define LOG_TAG "lights"
- #include <cutils/log.h>
- #include <stdint.h>
- #include <string.h>
- #include <unistd.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <pthread.h>
- #include <sys/ioctl.h>
- #include <sys/types.h>
- #include <hardware/lights.h>
- static pthread_once_t g_init = PTHREAD_ONCE_INIT;
- static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
- char const*const LCD_FILE
- = "/sys/class/leds/lcd-backlight/brightness";
- char const*const KEYBOARD_FILE
- = "/sys/class/leds/keyboard-backlight/brightness";
- char const*const CHARGING_LED_FILE
- = "/sys/class/leds/battery-led/brightness";
- char const*const RED_LED_FILE
- = "/sys/class/leds/red/brightness";
- char const*const RED_DELAY_ON_FILE
- = "/sys/class/leds/red/delay_on";
- char const*const RED_DELAY_OFF_FILE
- = "/sys/class/leds/red/delay_off";
- char const*const GREEN_LED_FILE
- = "/sys/class/leds/green/brightness";
- char const*const GREEN_DELAY_ON_FILE
- = "/sys/class/leds/green/delay_on";
- char const*const GREEN_DELAY_OFF_FILE
- = "/sys/class/leds/green/delay_off";
- char const*const BLUE_LED_FILE
- = "/sys/class/leds/blue/brightness";
- char const*const BLUE_DELAY_ON_FILE
- = "/sys/class/leds/blue/delay_on";
- char const*const BLUE_DELAY_OFF_FILE
- = "/sys/class/leds/blue/delay_off";
- ...
[cpp] view plain copy
- #define LOG_TAG "lights"
- #include <cutils/log.h>
- #include <stdint.h>
- #include <string.h>
- #include <unistd.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <pthread.h>
- #include <sys/ioctl.h>
- #include <sys/types.h>
- #include <hardware/lights.h>
- static pthread_once_t g_init = PTHREAD_ONCE_INIT;
- static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
- char const*const LCD_FILE
- = "/sys/class/leds/lcd-backlight/brightness";
- char const*const KEYBOARD_FILE
- = "/sys/class/leds/keyboard-backlight/brightness";
- char const*const CHARGING_LED_FILE
- = "/sys/class/leds/battery-led/brightness";
- char const*const RED_LED_FILE
- = "/sys/class/leds/red/brightness";
- char const*const RED_DELAY_ON_FILE
- = "/sys/class/leds/red/delay_on";
- char const*const RED_DELAY_OFF_FILE
- = "/sys/class/leds/red/delay_off";
- char const*const GREEN_LED_FILE
- = "/sys/class/leds/green/brightness";
- char const*const GREEN_DELAY_ON_FILE
- = "/sys/class/leds/green/delay_on";
- char const*const GREEN_DELAY_OFF_FILE
- = "/sys/class/leds/green/delay_off";
- char const*const BLUE_LED_FILE
- = "/sys/class/leds/blue/brightness";
- char const*const BLUE_DELAY_ON_FILE
- = "/sys/class/leds/blue/delay_on";
- char const*const BLUE_DELAY_OFF_FILE
- = "/sys/class/leds/blue/delay_off";
- ...
[cpp] view plain copy
- static int
- set_light_backlight(struct light_device_t* dev,
- struct light_state_t const* state)
- {
- int err = 0;
- int brightness = rgb_to_brightness(state);
- pthread_mutex_lock(&g_lock);
- err = write_int(LCD_FILE, brightness);
- pthread_mutex_unlock(&g_lock);
- return err;
- }
[cpp] view plain copy
- static int
- set_light_backlight(struct light_device_t* dev,
- struct light_state_t const* state)
- {
- int err = 0;
- int brightness = rgb_to_brightness(state);
- pthread_mutex_lock(&g_lock);
- err = write_int(LCD_FILE, brightness);
- pthread_mutex_unlock(&g_lock);
- return err;
- }
這裡關注一下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
- static int __init leds_init(void)
- {
- leds_class = class_create(THIS_MODULE, "leds");
- if (IS_ERR(leds_class))
- return PTR_ERR(leds_class);
- leds_class->suspend = led_suspend;
- leds_class->resume = led_resume;
- leds_class->dev_attrs = led_class_attrs;
- return 0;
- }
[cpp] view plain copy
- static int __init leds_init(void)
- {
- leds_class = class_create(THIS_MODULE, "leds");
- if (IS_ERR(leds_class))
- return PTR_ERR(leds_class);
- leds_class->suspend = led_suspend;
- leds_class->resume = led_resume;
- leds_class->dev_attrs = led_class_attrs;
- return 0;
- }
好明顯,正是由于它建立了sys class,名字為”leds“,是以上面的背光sys檔案節點"/sys/class/leds/”就有了來由,那麼剩下的“../lcd-backlight/brightness"又是怎麼來的呢?看看一個注冊led裝置類的函數. [cpp] view plain copy
- int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
- {
- led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
- "%s", led_cdev->name);
- if (IS_ERR(led_cdev->dev))
- return PTR_ERR(led_cdev->dev);
- #ifdef CONFIG_LEDS_TRIGGERS
- init_rwsem(&led_cdev->trigger_lock);
- ...
[cpp] view plain copy
- int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
- {
- led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
- "%s", led_cdev->name);
- if (IS_ERR(led_cdev->dev))
- return PTR_ERR(led_cdev->dev);
- #ifdef CONFIG_LEDS_TRIGGERS
- init_rwsem(&led_cdev->trigger_lock);
- ...
這個注冊函數的接口最終會被我們要開發的背光驅動調用,這個接口在/sys/class/leds/下又建立了一個裝置接口,名字是led_cdev->name。好明顯這裡的led_cdev->name應該就是”lcd-backlight“,究竟是不是真的這樣呢?繼續看。 這個led-class.c在實作兩個裝置屬性,看代碼: [cpp] view plain copy
- static struct device_attribute led_class_attrs[] = {
- __ATTR(brightness, 0644, led_brightness_show, led_brightness_store),
- __ATTR(max_brightness, 0444, led_max_brightness_show, NULL),
- #ifdef CONFIG_LEDS_TRIGGERS
- __ATTR(trigger, 0644, led_trigger_show, led_trigger_store),
- #endif
- __ATTR_NULL,
- };
[cpp] view plain copy
- static struct device_attribute led_class_attrs[] = {
- __ATTR(brightness, 0644, led_brightness_show, led_brightness_store),
- __ATTR(max_brightness, 0444, led_max_brightness_show, NULL),
- #ifdef CONFIG_LEDS_TRIGGERS
- __ATTR(trigger, 0644, led_trigger_show, led_trigger_store),
- #endif
- __ATTR_NULL,
- };
看到了屬性名字為”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
- struct display_led_data {
- struct led_classdev pri_display_class_dev;
- struct led_classdev sec_display_class_dev;
- struct omap4_disp_led_platform_data *led_pdata;
- struct mutex pri_disp_lock;
- struct mutex sec_disp_lock;
- };
- static int omap4_XXX_display_probe(struct platform_device *pdev)
- {
- int ret;
- struct display_led_data *info;
- pr_info("%s:Enter\n", __func__);
- if (pdev->dev.platform_data == NULL) {
- pr_err("%s: platform data required\n", __func__);
- return -ENODEV;
- }
- info = kzalloc(sizeof(struct display_led_data), GFP_KERNEL);
- if (info == NULL) {
- ret = -ENOMEM;
- return ret;
- }
- info->led_pdata = pdev->dev.platform_data;
- platform_set_drvdata(pdev, info);
- info->pri_display_class_dev.name = "lcd-backlight";
- info->pri_display_class_dev.brightness_set = omap4_xxx_primary_disp_store;
- ...
- ret = led_classdev_register(&pdev->dev,
- &info->pri_display_class_dev);
- if (ret < 0) {
- pr_err("%s: Register led class failed\n", __func__);
- kfree(info);
- return ret;
- }
- if (info->led_pdata->flags & LEDS_CTRL_AS_TWO_DISPLAYS) {
- pr_info("%s: Configuring the secondary LED\n", __func__);
- info->sec_display_class_dev.name = "lcd-backlight2";
- info->sec_display_class_dev.brightness_set =
- omap4_mirage_secondary_disp_store;
- info->sec_display_class_dev.max_brightness = LED_OFF;
- mutex_init(&info->sec_disp_lock);
- ret = led_classdev_register(&pdev->dev,
- &info->sec_display_class_dev);
- ...
[cpp] view plain copy
- struct display_led_data {
- struct led_classdev pri_display_class_dev;
- struct led_classdev sec_display_class_dev;
- struct omap4_disp_led_platform_data *led_pdata;
- struct mutex pri_disp_lock;
- struct mutex sec_disp_lock;
- };
- static int omap4_XXX_display_probe(struct platform_device *pdev)
- {
- int ret;
- struct display_led_data *info;
- pr_info("%s:Enter\n", __func__);
- if (pdev->dev.platform_data == NULL) {
- pr_err("%s: platform data required\n", __func__);
- return -ENODEV;
- }
- info = kzalloc(sizeof(struct display_led_data), GFP_KERNEL);
- if (info == NULL) {
- ret = -ENOMEM;
- return ret;
- }
- info->led_pdata = pdev->dev.platform_data;
- platform_set_drvdata(pdev, info);
- info->pri_display_class_dev.name = "lcd-backlight";
- info->pri_display_class_dev.brightness_set = omap4_xxx_primary_disp_store;
- ...
- ret = led_classdev_register(&pdev->dev,
- &info->pri_display_class_dev);
- if (ret < 0) {
- pr_err("%s: Register led class failed\n", __func__);
- kfree(info);
- return ret;
- }
- if (info->led_pdata->flags & LEDS_CTRL_AS_TWO_DISPLAYS) {
- pr_info("%s: Configuring the secondary LED\n", __func__);
- info->sec_display_class_dev.name = "lcd-backlight2";
- info->sec_display_class_dev.brightness_set =
- omap4_mirage_secondary_disp_store;
- info->sec_display_class_dev.max_brightness = LED_OFF;
- mutex_init(&info->sec_disp_lock);
- ret = led_classdev_register(&pdev->dev,
- &info->sec_display_class_dev);
- ...
需關注這裡設定了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裝置。