MTK LIGHT 代码分析
项目上需要做些客制化的东西,需要用到light 一块的东西,好久以前看过,但是没有记录下来,这次重新看看,然后记录下来。
lightservice start
private void startCoreServices() {
// Manages LEDs and display backlight.
mSystemServiceManager.startService(LightsService.class);
应用中怎么用
get service
final LightsManager lights = getLocalService(LightsManager.class);
mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
返回一个 lightmanager, 只有一个方法。
public abstract class LightsManager {
public static final int LIGHT_ID_BACKLIGHT = 0;
public static final int LIGHT_ID_KEYBOARD = 1;
public static final int LIGHT_ID_BUTTONS = 2;
public static final int LIGHT_ID_BATTERY = 3;
public static final int LIGHT_ID_NOTIFICATIONS = 4;
public static final int LIGHT_ID_ATTENTION = 5;
public static final int LIGHT_ID_BLUETOOTH = 6;
public static final int LIGHT_ID_WIFI = 7;
public static final int LIGHT_ID_COUNT = 8;
public abstract Light getLight(int id);
}
关掉
mNotificationLight.turnOff();
blink
NotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,ledOnMS, ledOffMS);
setFlashing 从service 到 HAL分析
LightsManager getLight 实现
private final LightsManager mService = new LightsManager() {
@Override
public com.android.server.lights.Light getLight(int id) {
if (id < LIGHT_ID_COUNT) {
return mLights[id];
} else {
return null;
}
}
};
final LightImpl mLights[] = new LightImpl[LightsManager.LIGHT_ID_COUNT]; LightsService 成员变量
setFlashing在 LightImpl 中实现 ###
private final class LightImpl extends Light {
public void setFlashing(int color, int mode, int onMS, int offMS) {
synchronized (this) {
setLightLocked(color, mode, onMS, offMS, BRIGHTNESS_MODE_USER);
}
}
————》
private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) {
if (color != mColor || mode != mMode || onMS != mOnMS || offMS != mOffMS) {
if (DEBUG) Slog.v(TAG, "setLight #" + mId + ": color=#"
+ Integer.toHexString(color));
mColor = color;
mMode = mode;
mOnMS = onMS;
mOffMS = offMS;
Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLight(" + mId + ", " + color + ")");
try {
setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
}
}
————》
native 端 setLight_native, 在文件 com_android_server_lights_LightsService.cpp (35_smt\frameworks\base\services\core\jni) 中
static void setLight_native(JNIEnv *env, jobject clazz, jlong ptr,
jint light, jint colorARGB, jint flashMode, jint onMS, jint offMS, jint brightnessMode)
{
Devices* devices = (Devices*)ptr;
light_state_t state;
if (light < 0 || light >= LIGHT_COUNT || devices->lights[light] == NULL) {
return ;
}
memset(&state, 0, sizeof(light_state_t));
state.color = colorARGB;
state.flashMode = flashMode;
state.flashOnMS = onMS;
state.flashOffMS = offMS;
state.brightnessMode = brightnessMode;
{
ALOGD_IF_SLOW(50, "Excessive delay setting light");
**devices->lights[light]->set_light(devices->lights[light], &state);**
}
}
————–》
Lights.c (35_smt\vendor\mediatek\proprietary\hardware\liblights)
static int open_lights(const struct hw_module_t* module, char const* name,
struct hw_device_t** device)
{
int (*set_light)(struct light_device_t* dev,
struct light_state_t const* state);
// 根据NAME 来分辨操作, #define LIGHT_ID_BACKLIGHT "backlight" 在light.h 中定义。
if (0 == strcmp(LIGHT_ID_BACKLIGHT, name)) {
set_light = set_light_backlight;
}
else if (0 == strcmp(LIGHT_ID_KEYBOARD, name)) {
set_light = set_light_keyboard;
}
else if (0 == strcmp(LIGHT_ID_BUTTONS, name)) {
set_light = set_light_buttons;
}
else if (0 == strcmp(LIGHT_ID_BATTERY, name)) {
set_light = set_light_battery;
}
else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name)) {
set_light = set_light_notifications;
}
else if (0 == strcmp(LIGHT_ID_ATTENTION, name)) {
set_light = set_light_attention;
}
else {
return -EINVAL;
}
pthread_once(&g_init, init_globals);
struct light_device_t *dev = malloc(sizeof(struct light_device_t));
memset(dev, 0, sizeof(*dev));
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 0;
dev->common.module = (struct hw_module_t*)module;
dev->common.close = (int (*)(struct hw_device_t*))close_lights;
dev->set_light = set_light;
*device = (struct hw_device_t*)dev;
return 0;
}
static struct hw_module_methods_t lights_module_methods = {
.open = open_lights,
};
/*
* The lights Module
*/
struct hw_module_t HAL_MODULE_INFO_SYM = {
.tag = HARDWARE_MODULE_TAG,
//.version_major = 1,
//.version_minor = 0,
.id = LIGHTS_HARDWARE_MODULE_ID,
.name = "MTK lights Module",
.author = "MediaTek",
.methods = &lights_module_methods,
};
————》
我们的是LIGHT_ID_NOTIFICATIONS,接下来我们来看 set_light_notifications
static int set_light_notifications(struct light_device_t* dev,
struct light_state_t const* state)
{
pthread_mutex_lock(&g_lock);
g_notification = *state;
ALOGV("set_light_notifications g_trackball=%d color=0x%08x",
g_trackball, state->color);
if (g_haveTrackballLight) {
handle_trackball_light_locked(dev);
}
handle_speaker_battery_locked(dev);
pthread_mutex_unlock(&g_lock);
return 0;
}
static void handle_speaker_battery_locked(struct light_device_t* dev)
{
if (is_lit(&g_battery)) {
set_speaker_light_locked(dev, &g_battery);
} else {
set_speaker_light_locked(dev, &g_battery); /*Turkey workaround: notification and Low battery case, IPO bootup, NLED cannot blink*/ 处理低电压下情况
set_speaker_light_locked(dev, &g_notification);
}
}
我们的是LIGHT_ID_NOTIFICATIONS 会比较复杂,如果是 LIGHT_ID_KEYBOARD ,则非常简单:
static int set_light_keyboard(struct light_device_t* dev,
struct light_state_t const* state)
{
int err = 0;
int on = is_lit(state);
pthread_mutex_lock(&g_lock);
err = write_int(KEYBOARD_FILE, on?255:0);
pthread_mutex_unlock(&g_lock);
return err;
}
直接写sys file, 然后就到kernel中去了。
——-》 对于notification light, 不管低电的特殊情况,我们来关注 set_speaker_light_locked(dev, &g_notification);
static int set_speaker_light_locked(struct light_device_t* dev,
struct light_state_t const* state)
{
int len;
int alpha, red, green, blue;
int onMS, offMS;
unsigned int colorRGB;
switch (state->flashMode) {
case LIGHT_FLASH_TIMED:
onMS = state->flashOnMS;
offMS = state->flashOffMS;
break;
case LIGHT_FLASH_NONE:
default:
onMS = 0;
offMS = 0;
break;
}
colorRGB = state->color;
#ifdef LIGHTS_DBG_ON
ALOGD("set_led_state colorRGB=%08X, onMS=%d, offMS=%d\n",
colorRGB, onMS, offMS);
#endif
alpha = (colorRGB >> 24) & 0xFF;
if (alpha) {
red = (colorRGB >> 16) & 0xFF;
green = (colorRGB >> 8) & 0xFF;
blue = colorRGB & 0xFF;
} else { // alpha = 0 means turn the LED off
red = green = blue = 0;
}
if (red) {
blink_green(0, 0, 0);
blink_blue(0, 0, 0);
blink_red(red, onMS, offMS);
}
else if (green) {
blink_red(0, 0, 0);
blink_blue(0, 0, 0);
blink_green(green, onMS, offMS);
}
else if (blue) {
blink_red(0, 0, 0);
blink_green(0, 0, 0);
blink_blue(blue, onMS, offMS);
}
else {
blink_red(0, 0, 0);
blink_green(0, 0, 0);
blink_blue(0, 0, 0);
}
return 0;
}
————》 接下来我们抽一个来看
static int
blink_blue(int level, int onMS, int offMS)
{
static int preStatus = 0; // 0: off, 1: blink, 2: no blink
int nowStatus;
int i = 0;
if (level == 0)
nowStatus = 0;
else if (onMS && offMS)
nowStatus = 1;
else
nowStatus = 2;
if (preStatus == nowStatus)
return -1;
#ifdef LIGHTS_DBG_ON
ALOGD("blink_blue, level=%d, onMS=%d, offMS=%d\n", level, onMS, offMS);
#endif
if (nowStatus == 0) {
write_int(BLUE_LED_FILE, 0);
}
else if (nowStatus == 1) {
// write_int(BLUE_LED_FILE, level); // default full brightness
write_str(BLUE_TRIGGER_FILE, "timer"); // 先写此 /sys/class/leds/blue/trigger sys文件路径
while (((access(BLUE_DELAY_OFF_FILE, F_OK) == -1) || (access(BLUE_DELAY_OFF_FILE, R_OK|W_OK) == -1)) && i<10) {
ALOGD("BLUE_DELAY_OFF_FILE doesn't exist or cannot write!!\n");
led_wait_delay(5);//sleep 5ms for wait kernel LED class create led delay_off/delay_on node of fs
i++;
}
write_int(BLUE_DELAY_OFF_FILE, offMS); "/sys/class/leds/blue/delay_off";
write_int(BLUE_DELAY_ON_FILE, onMS); /sys/class/leds/blue/delay_on
}
else {
write_str(BLUE_TRIGGER_FILE, "none");
write_int(BLUE_LED_FILE, 255); // default full brightness
}
preStatus = nowStatus;
return 0;
}
kernel light 分析
probe 分析
Leds_drv.c (35_smt\kernel-3.10\drivers\misc\mediatek\leds)
static int __init mt65xx_leds_probe(struct platform_device *pdev)
{
int i;
int ret, rc;
struct cust_mt65xx_led *cust_led_list = mt_get_cust_led_list();
****************************************************************************************************************
static struct cust_mt65xx_led cust_led_list[MT65XX_LED_TYPE_TOTAL] = {
{"red", MT65XX_LED_MODE_NONE, -1,{0}},
{"green", MT65XX_LED_MODE_NONE, -1,{0}},
{"blue", MT65XX_LED_MODE_NONE, -1,{0}},
{"jogball-backlight", MT65XX_LED_MODE_NONE, -1,{0}},
{"keyboard-backlight",MT65XX_LED_MODE_NONE, -1,{0}},
{"button-backlight", MT65XX_LED_MODE_NONE, -1,{0}},
{"lcd-backlight", MT65XX_LED_MODE_CUST_BLS_PWM, (long)disp_bls_set_backlight,{0}},
};
****************************************************************************************************************
LEDS_DRV_DEBUG("[LED]%s\n", __func__);
get_div_array();
for (i = 0; i < MT65XX_LED_TYPE_TOTAL; i++) {
if (cust_led_list[i].mode == MT65XX_LED_MODE_NONE) {
g_leds_data[i] = NULL;
continue;
}
g_leds_data[i] = kzalloc(sizeof(struct mt65xx_led_data), GFP_KERNEL);
if (!g_leds_data[i]) {
ret = -ENOMEM;
goto err;
}
g_leds_data[i]->cust.mode = cust_led_list[i].mode;
g_leds_data[i]->cust.data = cust_led_list[i].data;
g_leds_data[i]->cust.name = cust_led_list[i].name;
g_leds_data[i]->cdev.name = cust_led_list[i].name;
g_leds_data[i]->cust.config_data = cust_led_list[i].config_data; /* bei add */
g_leds_data[i]->cdev.brightness_set = mt65xx_led_set; // 所有的操作都在这3个函数中,后面来详细分析。
g_leds_data[i]->cdev.blink_set = mt65xx_blink_set;
INIT_WORK(&g_leds_data[i]->work, mt_mt65xx_led_work);
ret = led_classdev_register(&pdev->dev, &g_leds_data[i]->cdev);
if (strcmp(g_leds_data[i]->cdev.name, "lcd-backlight") == 0) {
rc = device_create_file(g_leds_data[i]->cdev.dev, &dev_attr_duty);
if (rc) {
LEDS_DRV_DEBUG("[LED]device_create_file duty fail!\n");
}
rc = device_create_file(g_leds_data[i]->cdev.dev, &dev_attr_div);
if (rc) {
LEDS_DRV_DEBUG("[LED]device_create_file duty fail!\n");
}
rc = device_create_file(g_leds_data[i]->cdev.dev, &dev_attr_frequency);
if (rc) {
LEDS_DRV_DEBUG("[LED]device_create_file duty fail!\n");
}
rc = device_create_file(g_leds_data[i]->cdev.dev, &dev_attr_pwm_register);
if (rc) {
LEDS_DRV_DEBUG("[LED]device_create_file duty fail!\n");
}
bl_setting = &g_leds_data[i]->cust;
}
if (ret)
goto err;
}
#ifdef CONTROL_BL_TEMPERATURE
last_level = 0;
limit = 255;
limit_flag = 0;
current_level = 0;
LEDS_DRV_DEBUG
("[LED]led probe last_level = %d, limit = %d, limit_flag = %d, current_level = %d\n",
last_level, limit, limit_flag, current_level);
#endif
return 0;
err:
if (i) {
for (i = i - 1; i >= 0; i--) {
if (!g_leds_data[i])
continue;
led_classdev_unregister(&g_leds_data[i]->cdev);
cancel_work_sync(&g_leds_data[i]->work);
kfree(g_leds_data[i]);
g_leds_data[i] = NULL;
}
}
return ret;
}
——–》 下面来详细分析上面3个函数,先看mt65xx_led_set
static void mt65xx_led_set(struct led_classdev *led_cdev, enum led_brightness level)
{
struct mt65xx_led_data *led_data = container_of(led_cdev, struct mt65xx_led_data, cdev);
if (strcmp(led_data->cust.name, "lcd-backlight") == 0) { 如果是背光,做些特殊处理
#ifdef CONTROL_BL_TEMPERATURE
mutex_lock(&bl_level_limit_mutex);
current_level = level;
/* LEDS_DRV_DEBUG("brightness_set_cust:current_level=%d\n", current_level); */
if (0 == limit_flag) {
last_level = level;
/* LEDS_DRV_DEBUG("brightness_set_cust:last_level=%d\n", last_level); */
} else {
if (limit < current_level) {
level = limit;
LEDS_DRV_DEBUG("backlight_set_cust: control level=%d\n", level);
}
}
mutex_unlock(&bl_level_limit_mutex);
#endif
}
mt_mt65xx_led_set(led_cdev, level);
}
———》mt_mt65xx_led_set
void mt_mt65xx_led_set(struct led_classdev *led_cdev, enum led_brightness level)
{
struct mt65xx_led_data *led_data =
container_of(led_cdev, struct mt65xx_led_data, cdev);
//unsigned long flags;
//spin_lock_irqsave(&leds_lock, flags);
#ifdef CONFIG_MTK_AAL_SUPPORT
if(led_data->level != level)
{
led_data->level = level;
if(strcmp(led_data->cust.name,"lcd-backlight") != 0)
{
LEDS_DEBUG("[LED]Set NLED directly %d at time %lu\n",led_data->level,jiffies);
schedule_work(&led_data->work);
}
else
{
LEDS_DEBUG("[LED]Set Backlight directly %d at time %lu\n",led_data->level,jiffies);
//mt_mt65xx_led_set_cust(&led_data->cust, led_data->level);
disp_aal_notify_backlight_changed( (((1 << MT_LED_INTERNAL_LEVEL_BIT_CNT) - 1)*level + 127)/255 );
}
}
#else
// do something only when level is changed
if(led_data->level != level)
{
led_data->level = level;
if(strcmp(led_data->cust.name,"lcd-backlight") != 0) 非背光,直接到 在工作队列中处理,WORK
{
LEDS_DEBUG("[LED]Set NLED directly %d at time %lu\n",led_data->level,jiffies);
schedule_work(&led_data->work);
}
else
{
LEDS_DEBUG("[LED]Set Backlight directly %d at time %lu\n",led_data->level,jiffies);
if(MT65XX_LED_MODE_CUST_BLS_PWM == led_data->cust.mode) 背光,直接设置。 背光相应快。
{
mt_mt65xx_led_set_cust(&led_data->cust, ((((1 << MT_LED_INTERNAL_LEVEL_BIT_CNT) - 1)*level + 127)/255));
}
else
{
mt_mt65xx_led_set_cust(&led_data->cust, led_data->level);
}
}
}
//spin_unlock_irqrestore(&leds_lock, flags);
#endif
// if(0!=aee_kernel_Powerkey_is_press())
// aee_kernel_wdt_kick_Powkey_api("mt_mt65xx_led_set",WDT_SETBY_Backlight);
}
—–》调用WORK,会直接到WORK函数中处理,即mt_mt65xx_led_work
void mt_mt65xx_led_work(struct work_struct *work)
{
struct mt65xx_led_data *led_data =
container_of(work, struct mt65xx_led_data, work);
LEDS_DEBUG("[LED]%s:%d\n", led_data->cust.name, led_data->level);
mutex_lock(&leds_mutex);
mt_mt65xx_led_set_cust(&led_data->cust, led_data->level);
mutex_unlock(&leds_mutex);;
}
可以看出 实际上还是调用的 mt_mt65xx_led_set_cust处理,上面背光的特殊处理确实只是为了速度更快写而已。
———-》
int mt_mt65xx_led_set_cust(struct cust_mt65xx_led *cust, int level)
{
switch (cust->mode) { 根据不同的模式来操作
case MT65XX_LED_MODE_PWM:
if(strcmp(cust->name,"lcd-backlight") == 0)
{
bl_brightness_hal = level;
if(level == 0)
{
mt_pwm_disable(cust->data, cust->config_data.pmic_pad);
}else
{
if (BacklightLevelSupport == BACKLIGHT_LEVEL_PWM_256_SUPPORT)
level = brightness_mapping(tmp_level);
else
level = brightness_mapto64(tmp_level);
mt_backlight_set_pwm(cust->data, level, bl_div_hal,&cust->config_data);
}
bl_duty_hal = level;
}else
{
if(level == 0)
{
led_tmp_setting.nled_mode = NLED_OFF;
mt_led_set_pwm(cust->data,&led_tmp_setting);
mt_pwm_disable(cust->data, cust->config_data.pmic_pad);
}else
{
led_tmp_setting.nled_mode = NLED_ON;
mt_led_set_pwm(cust->data,&led_tmp_setting);
}
}
return 1;
case MT65XX_LED_MODE_GPIO: 用函数指针去做相应的操作
LEDS_DEBUG("brightness_set_cust:go GPIO mode!!!!!\n");
return ((cust_set_brightness)(cust->data))(level);
case MT65XX_LED_MODE_PMIC:
//for button baclight used SINK channel, when set button ISINK, don't do disable other ISINK channel
if((strcmp(cust->name,"button-backlight") == 0)) {
if(button_flag==false) {
switch (cust->data) {
case MT65XX_LED_PMIC_NLED_ISINK0:
button_flag_isink0 = 1;
break;
case MT65XX_LED_PMIC_NLED_ISINK1:
button_flag_isink1 = 1;
break;
case MT65XX_LED_PMIC_NLED_ISINK2:
button_flag_isink2 = 1;
break;
case MT65XX_LED_PMIC_NLED_ISINK3:
button_flag_isink3 = 1;
break;
default:
break;
}
button_flag=true;
}
}
return mt_brightness_set_pmic(cust->data, level, bl_div_hal); PMIC的
case MT65XX_LED_MODE_CUST_LCM:
if (strcmp(cust->name, "lcd-backlight") == 0) {
bl_brightness_hal = level;
}
LEDS_DEBUG("brightness_set_cust:backlight control by LCM\n");
return ((cust_brightness_set)(cust->data))(level, bl_div_hal); 可以自定义背光的
case MT65XX_LED_MODE_CUST_BLS_PWM: 实际上背光用的是 这个: MT65XX_LED_MODE_CUST_BLS_PWM
if (strcmp(cust->name, "lcd-backlight") == 0) {
bl_brightness_hal = level;
}
return ((cust_set_brightness)(cust->data))(level);
case MT65XX_LED_MODE_NONE:
default:
break;
}
}
————-》 mt65xx_blink_set
分析
static int mt65xx_blink_set(struct led_classdev *led_cdev,
unsigned long *delay_on, unsigned long *delay_off)
{
if (mt_mt65xx_blink_set(led_cdev, delay_on, delay_off)) {
return -1;
} else {
return 0;
}
}
———-》
int mt_mt65xx_blink_set(struct led_classdev *led_cdev,
unsigned long *delay_on,
unsigned long *delay_off)
{
struct mt65xx_led_data *led_data =
container_of(led_cdev, struct mt65xx_led_data, cdev);
static int got_wake_lock = 0;
struct nled_setting nled_tmp_setting = {0,0,0};
// only allow software blink when delay_on or delay_off changed
if (*delay_on != led_data->delay_on || *delay_off != led_data->delay_off) {
led_data->delay_on = *delay_on;
led_data->delay_off = *delay_off;
if (led_data->delay_on && led_data->delay_off) { // enable blink
led_data->level = 255; // when enable blink then to set the level (255)
//AP PWM all support OLD mode
if(led_data->cust.mode == MT65XX_LED_MODE_PWM)
{
nled_tmp_setting.nled_mode = NLED_BLINK;
nled_tmp_setting.blink_off_time = led_data->delay_off;
nled_tmp_setting.blink_on_time = led_data->delay_on;
mt_led_set_pwm(led_data->cust.data,&nled_tmp_setting); PWM硬件模块能有此模式,配置好后不需要CPU参与。
return 0;
}
else if((led_data->cust.mode == MT65XX_LED_MODE_PMIC) && (led_data->cust.data == MT65XX_LED_PMIC_NLED_ISINK0
|| led_data->cust.data == MT65XX_LED_PMIC_NLED_ISINK1 || led_data->cust.data == MT65XX_LED_PMIC_NLED_ISINK2
|| led_data->cust.data == MT65XX_LED_PMIC_NLED_ISINK3))
{
nled_tmp_setting.nled_mode = NLED_BLINK;
nled_tmp_setting.blink_off_time = led_data->delay_off;
nled_tmp_setting.blink_on_time = led_data->delay_on;
mt_led_blink_pmic(led_data->cust.data, &nled_tmp_setting);
return 0;
}
else if (!got_wake_lock) { 没有实现,需要CPU参与的,需要获取锁,实际上GPIO BLINK可以在这里实现。
wake_lock(&leds_suspend_lock);
got_wake_lock = 1;
}
}
else if (!led_data->delay_on && !led_data->delay_off) { // disable blink
//AP PWM all support OLD mode
if(led_data->cust.mode == MT65XX_LED_MODE_PWM)
{
nled_tmp_setting.nled_mode = NLED_OFF;
mt_led_set_pwm(led_data->cust.data,&nled_tmp_setting);
return 0;
}
else if((led_data->cust.mode == MT65XX_LED_MODE_PMIC) && (led_data->cust.data == MT65XX_LED_PMIC_NLED_ISINK0
|| led_data->cust.data == MT65XX_LED_PMIC_NLED_ISINK1 || led_data->cust.data == MT65XX_LED_PMIC_NLED_ISINK2
|| led_data->cust.data == MT65XX_LED_PMIC_NLED_ISINK3))
{
mt_brightness_set_pmic(led_data->cust.data, 0, 0);
return 0;
}
else if (got_wake_lock) {
wake_unlock(&leds_suspend_lock);
got_wake_lock = 0;
}
}
return -1;
}
// delay_on and delay_off are not changed
return 0;
}