其实Android的底层就是Linux,所以其驱动本质就是Linux驱动,但是这些Linux驱动是服务上层Android的,所以需遵循上Android的一些接口规范。所以涉及到的Android驱动都应应密切关注上层传递的接口。本文介绍的LCD背光驱动就是从上层一直往下层展现,但是笔者毕竟不是专注于Android上层,碍于知识不充裕,所以对上层的东西介绍得相对简单。
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,所以在进行最低层的背光驱动编写时,可以合理按这个范围部署背光的亮度。
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "lights"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/******************************************************************************/
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";
/*RGB file descriptors */
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";
...
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"
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设备类的函数.
/**
* led_classdev_register - register a new object of led_classdev class.
* @parent: The device to register.
* @led_cdev: the led_classdev structure for this device.
*/
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“,究竟是不是真的这样呢?继续看。
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"的由来了。
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);
...