1 驱动部分
这部分主要根据驱动源码的初始化部分进行分析
1.1 mtk_leds_drv
路径:/kernel-4.14/drivers/misc/mediatek/leds/mtk_leds_drv.c
mt65xx_leds_probe
→ *cust_led_list = mt_get_cust_led_list
→ get_cust_led_dtsi
//根据leds_name数组以及设备树初始化struct cust_mt65xx_led数组把地址返回给cust_led_list,mode,data参数都在mt_get_cust_led_list进行赋值
→ g_leds_data //根据cut_led_list初始化g_leds_data
→ led_classdev_register(&pdev->dev, &g_leds_data[i]→cdev)
→ device_create_with_groups
//sys/calss/leds下注册leds_name目录以及注册attrs 文件节点
→ list_add_tail(&led_cdev->node, &leds_list);
//添加到led_list,led_cdev是 g_leds_data[i]→cdev
→ led_init_core
//初始化led_cdev的set_brightness_work 回调函数:set_brightness_work
//以及初始化led_cdev的set_brightness_work, 回调函数:led_timer_function
//私有数据设置为led_cdev
→ led_trigger_set_default(led_cdev)
//在led_cdev->default_trigger不为空的情况下遍历trigger_list拿到trigger
//使用led_cdev->default_trigger与trigger->name属性进行匹配并设置
//匹配成功后调用led_trigger_set设置启动对应trigger
//led_cdev->trigger,本驱动中default_trigger为空直接返回
主要的结构体参数初始化如下:
leds_name初始化
char *leds_name[TYPE_TOTAL] = {
"red",
"green",
"blue",
"jogball-backlight",
"keyboard-backlight",
"button-backlight",
"lcd-backlight",
};
cust_led_list 初始化:
struct cust_mt65xx_led {
.name→ leds_name[i]
.mode→ “led_mode”//设备树中定义
.data → “data”
.config_data.clock_source //”pwm_config”的0~4bit分别初始化
config_data.div
config_data.low_duration
config_data.High_duration
config_data.pmic_pad
}
/*当mode是以下的参数结构体中的data会被重新赋值关系如下
mode:MT65XX_LED_MODE_CUST_LCM ,data: mtkfb_set_backlight_level
mode: MT65XX_LED_MODE_CUST_BLS_PWM, data: disp_bls_set_backligh */
g_leds_data 初始化:
struct mt65xx_led_data {
.cdev {
.brightness_set = mt65xx_led_set
.blink_set = mt65xx_blink_set
.name = cust_led_list[i].name
.set_brightness_work
.blink_timer
}
struct cust_mt65xx_led cust → cust_led_list
struct work_struct work → mt_mt65xx_led_work
int level;
int delay_on;
int delay_off;
}
1.2 led-class
路径:/kernel-4.14/drivers/leds/led-class.c
leds_init
→ leds_class = class_create(THIS_MODULE, "leds") //创建类
→ leds_class->pm = &leds_class_dev_pm_ops; //初始化 pm
→ leds_class->dev_groups = led_groups;
/* 初始化dev_groups,mtk_leds_drv驱动 调用led_classdev_register
注册attrs文件节点时使用该参数,led-class主要是创建leds类以及提供attrs操作集合 */
1.3 led-trigger
路径:/kernel-4.14/drivers/leds/trigger/./ledtrig-timer.c
这里主要分析 timer这个trigger,这个模块主要实现led的闪烁功能
led_trigger_register(&timer_led_trigger) //注册led_trigger
→ list_add_tail(&trig->next_trig, &trigger_list); //添加到链表trigger_list
→ list_for_each_entry(led_cdev, &leds_list, node)
→ if (!led_cdev->trigger && led_cdev->default_trigger &&
!strcmp(led_cdev->default_trigger, trig->name))
//遍历leds_list,led_cedv的trigger,default_trigger与trig->name作匹配
→ led_trigger_set(led_cdev, trig); //对 led_cdev->trigger进行设置
/* mtk_leds_drv.c 驱动中g_leds_data[i]→cdev.default_trigger为空所以此处
led_trigger_set函数并未对其操作
ledtrig-timer 主要就是向trigger_list链表里面添加timer_led_trigger */
主要结构体参数:
static struct led_trigger timer_led_trigger {
.name → “timer”
.activate → timer_trig_activate
.deactivate → timer_trig_deactivate
.next_trig → trigger_list
}
框架图
1.4 操作流程
这部分主要集中分析设置背光以及设置闪烁的操作进行分析
1.4.1 设置背光
echo 255 > /sys/class/leds/xxx/brightness //设置背光亮度
→ brightness_store
→ struct led_classdev *led_cdev = dev_get_drvdata(dev)
//从dev->driver_date获取struct led_classdev
→ led_set_brightness(led_cdev, state)
→ led_set_brightness_nosleep(led_cdev, brightness)
→ led_set_brightness_nopm(led_cdev, led_cdev->brightness)
→ __led_set_brightness(led_cdev, value)
→ led_cdev→brightness_set(led_cdev, value)
/* mtk_leds_drv在对g_leds_data初始化时 struct led_classdev cdev 的
brightness_set = mt65xx_led_set*/
即: led_cdev→brightness_set
→ mt65xx_led_set
→ mt_mt65xx_led_set //背光mode:MT65XX_LED_MODE_CUST_BLS_PWM
→ mt_mt65xx_led_set_cust //依据mode: MT65XX_LED_MODE_CUST_BLS_PWM
→ ((cust_brightness_set) (cust->data)) (level, bl_div_hal)
//data参数在调用get_cust_led_dtsi()函数时依据mode进行初始化
即调用:disp_bls_set_backlight :设置背光
1.4.2 设置闪烁
echo timer > ./sys/calss/leds/xxx/trigger //启动定时器触发器
→ led_trigger_store
→ struct led_classdev *led_cdev = dev_get_drvdata(dev)
//从dev->driver_date获取struct led_classdev
→ list_for_each_entry(trig, &trigger_list, next_trig)
/*遍历 trigger_list 使用 ”timer ” 与 led_trigger的name 元素进行配对,
与timer_led_trigger 进行匹配 */
→ led_trigger_set(led_cdev, trig)
→ trig→activate(led_cdev) //调用触发器的 activate
timer_led_trigger
.activate: timer_trig_activate
static void timer_trig_activate(struct led_classdev *led_cdev)
→ device_create_file(led_cdev->dev, &dev_attr_delay_on)
→ device_create_file(led_cdev->dev, &dev_attr_delay_off)
/* 创建delay_on以及delay_off文件节点分别用于设置led_cdev->blink_delay_on 以及
led_cdev->blink_delay_off用于实现开和关的延时时间 */
→ led_blink_set(led_cdev, &led_cdev→blink_delay_on,&led_cdev→blink_delay_off);
led_blink_set()
→ led_blink_setup(led_cdev, delay_on, delay_off)
→ if(..... && !led_cdev→blink_set(led_cdev, delay_on, delay_off))
/*调用led_cdev→blink_set 进行设置 此处直接返回-1调用led_set_software_blink
mtk_leds_drv初始化阶段blink_set为 mt65xx_blink_set*/
→ led_set_software_blink
→ mod_timer(&led_cdev->blink_timer, jiffies + 1) //启动定时器
//mtk65xx_leds_probe阶段的led_init_core函数对blink_timer进行了初始化,
//回调函:led_timer_function
void led_timer_function(unsigned long data)//实现闪烁功能
→ if (!brightness) //根据当前数值进行反向赋值以及设置延迟时间
brightness = led_cdev->blink_brightness;
delay = led_cdev->blink_delay_on;
→ else
brightness = LED_OFF;
delay = led_cdev->blink_delay_off;
→ led_set_brightness_nosleep(led_cdev, brightness); //设置亮灭
→ mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay))
//再次启动定时器进入下一轮反向操作
/* mt65xx_blink_set:
led_data->delay_on 以及led_data->delay_off
其中都不为0或者其中之一不为0会根据mode值经进行设置,当都为0时
直接返回-1,目前支持的mode值为:MT65XX_LED_MODE_PWM以及MT65XX_LED_MODE_PMIC */
操作流程图
2 hal 层
这层主要对驱动生成的 /sys/class/leds/目录下的文件进行封装一些操作接口
2.1 lights.c
路径: vendor/mediatek/proprietary/hardware/liblights/lights.c
主要结构体
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,
}
static struct hw_module_methods_t lights_module_methods = {
.open = open_lights,
};
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);
//定义函数指针
→ if (0 == strcmp(LIGHT_ID_BACKLIGHT, name))
→ set_light = set_light_backlight;
......
→ else if (0 == strcmp(LIGHT_ID_BATTERY, name))
→ set_light = set_light_battery;
//根据传入参数name的不同对 set_light进行不同的赋值
→ dev->set_light = set_light
→ *device = (struct hw_device_t*)dev;
name 与 set_Light回调函数赋值的对应关系:
name | ID | 回调函数 |
“backlight” | LIGHT_ID_BACKLIGHT | set_light_backlight |
“keyboard” | LIGHT_ID_KEYBOARD | set_light_keyboard |
“buttons" | LIGHT_ID_BUTTONS | set_light_buttons |
“battery”: | LIGHT_ID_BATTERY | set_light_battery |
“notification” | LIGHT_ID_NOTIFICATIONS | set_light_notifications |
“attention” | LIGHT_ID_ATTENTION | set_light_attention |
| | |
这里主要分析 LIGHT_ID_BACKLIGHT:set_light_backlight设置背光的接口函数
2.1.1 LIGHT_ID_BACKLIGHT:set_light_backlight
set_light_backlight(struct light_device_t* dev, struct light_state_t const* state)
→ write_int(LCD_FILE, brightness); //write_int 通过标准的文件io:write()操作写入背光值
//LCD_FILE :"/sys/class/leds/lcd-backlight/brightness"
2.1.2 LIGHT_ID_BATTERY:set_light_battery
set_light_battery(struct light_device_t* dev,struct light_state_t const* state)
→ set_speaker_light_locked(dev, state)//调用该接口实现三色灯的亮灭以及闪烁
static int set_speaker_light_locked(__attribute__((__unused__)) struct light_device_t* dev, struct light_state_t const* state)
{
→ switch (state->flashMode) //根据flashMode 对onMS以及offMS两个闪烁亮灭时长进行赋值
case LIGHT_FLASH_TIMED:
onMS = state->flashOnMS;
offMS = state->flashOffMS;
.....
→ colorRGB = state->color;
.....
//解析state→color参数获取三色灯的配置情况 Color: 0xFF FF FF FF: Alpha R G B
→ red = (colorRGB >> 16) & 0xFF;
→ green = (colorRGB >> 8) & 0xFF;
→ blue = colorRGB & 0xFF;
.....
→ if (red) {
blink_green(0, 0, 0);
blink_blue(0, 0, 0);
blink_red(red, onMS, offMS);
}
//根据rgb对应的参数是否为true调用blink_xxx(xxx, onMS, offMS)设置亮灭以及闪烁
…...
}
blink_red(int level, int onMS, int offMS)的内部具体操作如下:
{
→ nowStatus 参数 0: off , 1: blink(配置闪烁), 2常亮
→ if (preStatus == nowStatus) //状态相同时直接返回
return -1;
→ nowStatus == 0 //RED_LED_FILE: "/sys/class/leds/red/brightness"
→ write_int(RED_LED_FILE, 0) //进行灭灯操作
→ nowStatus == 1
→ write_str(RED_TRIGGER_FILE, "timer");
//往/sys/class/leds/red/trigger 写入 "timer" 启动tiemr触发器的activate操作
→ while (((access(RED_DELAY_OFF_FILE, F_OK) == -1) ||
(access(RED_DELAY_OFF_FILE, R_OK|W_OK) == -1))
→ led_wait_delay(5)//等待 delay_off以及delay_on文件的生成
→ write_int(RED_DELAY_OFF_FILE, offMS) //设置delay_off的时长
→ write_int(RED_DELAY_ON_FILE, onMS) //设置delay_on的时长
→ nowStatus其他状态下
→ write_str(RED_TRIGGER_FILE, "none");//关掉所有触发器
→ write_int(RED_LED_FILE, 255); //亮灯操作
}
3 hidl 层
路径 /vendor/mediatek/proprietary/hardware/liblights/2.0/default
3.1 Light.cpp
Light.cpp:对hal :vendor/mediatek/proprietary/hardware/liblights/lights.c文件提供的一些接口进一步封装
3.1.1 hal中hw_module_t的获取
light_device_t* getLightDevice(const char* name)
→ hw_get_module (LIGHTS_HARDWARE_MODULE_ID, &hwModule); //获取hw_module_t
→ hwModule->methods->open(hwModule, name,reinterpret_cast<hw_device_t**>
(&lightDevice))
/* 调用lights_module_methods中的open_lights函数 ,open_lights函数中的(struct hw_device_t*)dev初始化完后赋值给了lightDevice,通过参数name传入methods->open最终让open_lights函数对set_light的回调对象进行初始化 */
getLightDevice的调用:
ILight* HIDL_FETCH_ILight(const char* /* name */)
{
→ for(auto const &pair : kLogicalLights) {
Type type = pair.first
const char* name = pair.second; //name 参数被赋值 LIGHT_ID_xxxx
light_device_t* light = getLightDevice(name);
//通过for循环调用 getLightDevice对所有类型的设备进行初始化
if (light != nullptr) {
lights[type] = light; //初始化的light保存在lights数组中
}
……………
return new Light(std::move(lights));
}
}
Light::Light(std::map<Type, light_device_t*> &&lights)
: mLights(std::move(lights)) {} // 根据lights对mLights
通过for循环 调用getLightDevice(name)函数返回值light_device_t*保存在mLights中,通过不同的type索引号即可拿到对应的light_device_t结构体参数进一步通过结构体参数调用 set_Light回调函数, type索引号对应参数name(LIGHT_ID_xxx)通过getLightDevice函数对应对应的set_Light
type与LIGHT_ID_XXX的对应关系根据kLogicalLights参数的定义 如下表
Type | ID(name) |
Type::BACKLIGHT | LIGHT_ID_BACKLIGHT |
Type::KEYBOARD | LIGHT_ID_KEYBOARD |
Type::BUTTONS | LIGHT_ID_BUTTONS |
Type::BATTERY | LIGHT_ID_BATTERY |
Type::NOTIFICATIONS | LIGHT_ID_NOTIFICATIONS |
Type::ATTENTION | LIGHT_ID_ATTENTION |
Type::BLUETOOTH | LIGHT_ID_BLUETOOTH |
Type::WIFI | LIGHT_ID_WIFI |
3.1.2 hidl 设置灯光的接口
Return<Status> Light::setLight(Type type, const LightState& state)
→ auto it = mLights.find(type); //根据Type定位 mLights
→ light_device_t* hwLight = it→second; //通过 mLights获取light_device_t
→ light_state_t legacyState { //参数进行初始化
.color = state.color,
.flashMode = static_cast<int>(state.flashMode),
.flashOnMS = state.flashOnMs,
.flashOffMS = state.flashOffMs,
.brightnessMode = static_cast<int>(state.brightnessMode),
};
→ hwLight->set_light(hwLight, &legacyState) //通过set_light回调
/*
整个setLight 流程根据Type::XXX 调用对应的 LIGHT_ID_xxx的 set_light回调函数
例:Type type参数: Type::BATTERY
→ 定位 mLights
→ mLight 找到 hwLight
→ 通过 hwLight->set_light 调用到对应LIGHT_ID_BATTERY的set_light回调函数
→ LIGHT_ID_BATTERY的set_light回调函数即hal层的 set_light_battery
*/
4. jni 层
路径frameworks/base/services/core/jni/com_android_server_lights_LightsService.cpp
jni层主要是对package android.hardware.light@2.0 提供的接口进一步封装
tatic const JNINativeMethod method_table[] = {
{ "setLight_native", "(IIIIII)V", (void*)setLight_native },};
//这里主要提供的接口是setLight_native,所以主要对此进行分析
static void setLight_native( JNIEnv* /* env */, jobject /* clazz */,
jint light, jint colorARGB,jint flashMode, jint onMS, jint offMS, jint brightnessMode)
{
→ Type type = static_cast<Type>(light); //类型转换为Type
→ LightState state = constructState( colorARGB, flashMode, onMS, offMS, brightnessMode);
//根据 colorARGB, flashMode, onMS, offMS, brightnessMode这一系列参数对state的元素进行赋值
→ sp<ILight> hal = Ilight::getService()//获取light-hal-2-0 Service用于后续调用其提供的接口
→ Return<Status> ret = hal->setLight(type, state) ; //调用hidl层对接的接口
}
5 java层
路径:framework//base/services/core/java/com/android/server/lights
设置led 灯显示:
setColor(int color)
→ setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, 0)
设置led闪烁:
setFlashing(int color, int mode, int onMS, int offMS)
→ setLightLocked(color, mode, onMS, offMS, BRIGHTNESS_MODE_USER)
private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode)
→ setLight_native(mId, color, mode, onMS, offMS, brightnessMode);
//调用jni的接口,mId会在JNI层转化为Type类型
/* setLight_native第一个参数mId即 hidl层用于定位set_light回调函数的type
mId 与type对应关系通过以下步骤完成 */
1.public LightsService(Context context)
→ for (int i = 0; i < LightsManager.LIGHT_ID_COUNT; i++)
→ mLights[i] = new LightImpl(context, i)
//通过for循环 new LightImpl对象 ,LIGHT_ID_xxx与mLights[i]的i对应
2.private LightImpl(Context context, int id)
→ mId = id
/* LightImpl会对class LightImpl的元素的mId元素进行初始化
即 LightImpl mLights[i]的索引i 与 mLights[i] .mId对应
所以操作流程是先获取对应的 mLights[i]对象再通过 setLightLocked方法设置亮度或者闪烁 */
获取 mLights对象的方法:
public Light getLight(int id)
→ if (0 <= id && id < LIGHT_ID_COUNT)
→ return mLights[id]
/* 以BatteryService.java中这部分操作为例:BatteryService.java中
updateLightsLocked()会根据电量的情况调用mBatteryLight.setFlashing以及mBatteryLight.setColor设置三色灯 */
mBatteryLight操作流程如下:
1 BatteryService初始化阶段获取LIGHT_ID_BATTERY的mLights对象
public BatteryService(Context context)
→ mLed = new Led(context, getLocalService(LightsManager.class))
public Led(Context context, LightsManager lights)
→ mBatteryLight = lights.getLight(LightsManager.LIGHT_ID_BATTERY);
//获取id: LIGHT_ID_BATTERY的 mLights[i]对象
2 updateLightsLocked()中调用setColor以及setFlashing