此文目前为半半半成品,待完善。
本文以light为例,分析android调用底层驱动的流程,今天先从LightsService开始,应用的调用部分后面再补充。
平台:rk3128
android版本:android 5.1
内核版本:3.10
一、LightsService服务
源码路径:frameworks/base/services/core/java/com/android/server/lights/LightsService.java
/*
* 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.
*/
package com.android.server.lights;
import com.android.server.SystemService;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.IHardwareService;
import android.os.Message;
import android.os.Trace;
import android.util.Slog;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class LightsService extends SystemService {
static final String TAG = "LightsService";
static final boolean DEBUG = false;
final LightImpl mLights[] = new LightImpl[LightsManager.LIGHT_ID_COUNT];
private final class LightImpl extends Light {
private LightImpl(int id) {
mId = id;
}
@Override
public void setBrightness(int brightness) {
setBrightness(brightness, BRIGHTNESS_MODE_USER);
}
@Override
public void setBrightness(int brightness, int brightnessMode) {
synchronized (this) {
int color = brightness & 0x000000ff;
color = 0xff000000 | (color << 16) | (color << 8) | color;
setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, brightnessMode);
}
}
@Override
public void setColor(int color) {
synchronized (this) {
setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, 0);
}
}
@Override
public void setFlashing(int color, int mode, int onMS, int offMS) {
synchronized (this) {
setLightLocked(color, mode, onMS, offMS, BRIGHTNESS_MODE_USER);
}
}
@Override
public void pulse() {
pulse(0x00ffffff, 7);
}
@Override
public void pulse(int color, int onMS) {
synchronized (this) {
if (mColor == 0 && !mFlashing) {
setLightLocked(color, LIGHT_FLASH_HARDWARE, onMS, 1000, BRIGHTNESS_MODE_USER);
mColor = 0;
mH.sendMessageDelayed(Message.obtain(mH, 1, this), onMS);
}
}
}
@Override
public void turnOff() {
synchronized (this) {
setLightLocked(0, LIGHT_FLASH_NONE, 0, 0, 0);
}
}
private void stopFlashing() {
synchronized (this) {
setLightLocked(mColor, LIGHT_FLASH_NONE, 0, 0, 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);//此处调用jni
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
}
}
private int mId;
private int mColor;
private int mMode;
private int mOnMS;
private int mOffMS;
private boolean mFlashing;
}
/* This class implements an obsolete API that was removed after eclair and re-added during the
* final moments of the froyo release to support flashlight apps that had been using the private
* IHardwareService API. This is expected to go away in the next release.
*/
private final IHardwareService.Stub mLegacyFlashlightHack = new IHardwareService.Stub() {
private static final String FLASHLIGHT_FILE = "/sys/class/leds/spotlight/brightness";
public boolean getFlashlightEnabled() {
try {
FileInputStream fis = new FileInputStream(FLASHLIGHT_FILE);
int result = fis.read();
fis.close();
return (result != '0');
} catch (Exception e) {
return false;
}
}
public void setFlashlightEnabled(boolean on) {
final Context context = getContext();
if (context.checkCallingOrSelfPermission(android.Manifest.permission.FLASHLIGHT)
!= PackageManager.PERMISSION_GRANTED &&
context.checkCallingOrSelfPermission(android.Manifest.permission.HARDWARE_TEST)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires FLASHLIGHT or HARDWARE_TEST permission");
}
try {
FileOutputStream fos = new FileOutputStream(FLASHLIGHT_FILE);
byte[] bytes = new byte[2];
bytes[0] = (byte)(on ? '1' : '0');
bytes[1] = '\n';
fos.write(bytes);
fos.close();
} catch (Exception e) {
// fail silently
}
}
};
public LightsService(Context context) {
super(context);
mNativePointer = init_native();//jni方法
for (int i = 0; i < LightsManager.LIGHT_ID_COUNT; i++) {
mLights[i] = new LightImpl(i);
}
}
@Override
public void onStart() {
publishBinderService("hardware", mLegacyFlashlightHack);
publishLocalService(LightsManager.class, mService);
}
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;
}
}
};
@Override
protected void finalize() throws Throwable {
finalize_native(mNativePointer);
super.finalize();
}
private Handler mH = new Handler() {
@Override
public void handleMessage(Message msg) {
LightImpl light = (LightImpl)msg.obj;
light.stopFlashing();
}
};
private static native long init_native();
private static native void finalize_native(long ptr);
static native void setLight_native(long ptr, int light, int color, int mode,
int onMS, int offMS, int brightnessMode);
private long mNativePointer;
}
接下来看jni的实现
frameworks/base/services/core/jni/com_android_server_lights_LightsService.cpp
/*
* Copyright (C) 2009 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 "LightsService"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include
#include
#include //hal头文件
#include
#include
namespace android
{
// These values must correspond with the LIGHT_ID constants in
// LightsService.java
enum {
LIGHT_INDEX_BACKLIGHT = 0,
LIGHT_INDEX_KEYBOARD = 1,
LIGHT_INDEX_BUTTONS = 2,
LIGHT_INDEX_BATTERY = 3,
LIGHT_INDEX_NOTIFICATIONS = 4,
LIGHT_INDEX_ATTENTION = 5,
LIGHT_INDEX_BLUETOOTH = 6,
LIGHT_INDEX_WIFI = 7,
LIGHT_COUNT
};
struct Devices {
light_device_t* lights[LIGHT_COUNT];
};
static light_device_t* get_device(hw_module_t* module, char const* name)
{
int err;
hw_device_t* device;
err = module->methods->open(module, name, &device);//调用hal层接口,打开设备文件
if (err == 0) {
return (light_device_t*)device;
} else {
return NULL;
}
}
static jlong init_native(JNIEnv *env, jobject clazz)
{
int err;
hw_module_t* module;
Devices* devices;
devices = (Devices*)malloc(sizeof(Devices));
err = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);//调用hal,传入的参数是设备ID和module的地址
if (err == 0) {
devices->lights[LIGHT_INDEX_BACKLIGHT]
= get_device(module, LIGHT_ID_BACKLIGHT);
devices->lights[LIGHT_INDEX_KEYBOARD]
= get_device(module, LIGHT_ID_KEYBOARD);
devices->lights[LIGHT_INDEX_BUTTONS]
= get_device(module, LIGHT_ID_BUTTONS);
devices->lights[LIGHT_INDEX_BATTERY]
= get_device(module, LIGHT_ID_BATTERY);
devices->lights[LIGHT_INDEX_NOTIFICATIONS]
= get_device(module, LIGHT_ID_NOTIFICATIONS);
devices->lights[LIGHT_INDEX_ATTENTION]
= get_device(module, LIGHT_ID_ATTENTION);
devices->lights[LIGHT_INDEX_BLUETOOTH]
= get_device(module, LIGHT_ID_BLUETOOTH);
devices->lights[LIGHT_INDEX_WIFI]
= get_device(module, LIGHT_ID_WIFI);
} else {
memset(devices, 0, sizeof(Devices));
}
return (jlong)devices;
}
static void finalize_native(JNIEnv *env, jobject clazz, jlong ptr)
{
Devices* devices = (Devices*)ptr;
if (devices == NULL) {
return;
}
free(devices);
}
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);
}
}
//jni方法,供上层调用
static JNINativeMethod method_table[] = {
{ "init_native", "()J", (void*)init_native },
{ "finalize_native", "(J)V", (void*)finalize_native },
{ "setLight_native", "(JIIIIII)V", (void*)setLight_native },
};
int register_android_server_LightsService(JNIEnv *env)
{
return jniRegisterNativeMethods(env, "com/android/server/lights/LightsService",
method_table, NELEM(method_table));
}
};
再看hal层代码
hardware/libhardware/hardware.c
/*
* 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.
*/
#include
#include
#include
#include
#include
#include
#include
#define LOG_TAG "HAL"
#include
/** Base path of the hal modules */
#if defined(__LP64__)
#define HAL_LIBRARY_PATH1 "/system/lib64/hw"
#define HAL_LIBRARY_PATH2 "/vendor/lib64/hw"
#else
#define HAL_LIBRARY_PATH1 "/system/lib/hw"
#define HAL_LIBRARY_PATH2 "/vendor/lib/hw"
#endif
/**
* There are a set of variant filename for modules. The form of the filename
* is ".variant.so" so for the led module the Dream variants
* of base "ro.product.board", "ro.board.platform" and "ro.arch" would be:
*
* led.trout.so
* led.msm7k.so
* led.ARMV6.so
* led.default.so
*/
static const char *variant_keys[] = {
"ro.hardware", /* This goes first so that it can pick up a different
file on the emulator. */
"ro.product.board",
"ro.board.platform",
"ro.arch"
};
static const int HAL_VARIANT_KEYS_COUNT =
(sizeof(variant_keys)/sizeof(variant_keys[0]));
/**
* Load the file defined by the variant and if successful
* return the dlopen handle and the hmi.
* @return 0 = success, !0 = failure.
*/
//这里就是加载lights.rk312x.so
static int load(const char *id,
const char *path,
const struct hw_module_t **pHmi)
{
int status;
void *handle;
struct hw_module_t *hmi;
/*
* load the symbols resolving undefined symbols before
* dlopen returns. Since RTLD_GLOBAL is not or'd in with
* RTLD_NOW the external symbols will not be global
*/
handle = dlopen(path, RTLD_NOW);
if (handle == NULL) {
char const *err_str = dlerror();
ALOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");
status = -EINVAL;
goto done;
}
/* Get the address of the struct hal_module_info. */
const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
hmi = (struct hw_module_t *)dlsym(handle, sym);
if (hmi == NULL) {
ALOGE("load: couldn't find symbol %s", sym);
status = -EINVAL;
goto done;
}
/* Check that the id matches */
if (strcmp(id, hmi->id) != 0) {
ALOGE("load: id=%s != hmi->id=%s", id, hmi->id);
status = -EINVAL;
goto done;
}
hmi->dso = handle;
/* success */
status = 0;
done:
if (status != 0) {
hmi = NULL;
if (handle != NULL) {
dlclose(handle);
handle = NULL;
}
} else {
ALOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",
id, path, *pHmi, handle);
}
*pHmi = hmi;
return status;
}
/*
* Check if a HAL with given name and subname exists, if so return 0, otherwise
* otherwise return negative. On success path will contain the path to the HAL.
*/
static int hw_module_exists(char *path, size_t path_len, const char *name,
const char *subname)
{
snprintf(path, path_len, "%s/%s.%s.so",
HAL_LIBRARY_PATH2, name, subname);
if (access(path, R_OK) == 0)
return 0;
snprintf(path, path_len, "%s/%s.%s.so",
HAL_LIBRARY_PATH1, name, subname);
if (access(path, R_OK) == 0)
return 0;
return -ENOENT;
}
//根据class_id来找到hw module,这里仍然有疑问,在瑞芯微平台,light对应的模块名应为lights.rk312x.so,
//但在variant_keys中并没有这个选项,也不是default,所以是怎么找到lights.rk312x.so并load进来的?留作以后研究
int hw_get_module_by_class(const char *class_id, const char *inst,
const struct hw_module_t **module)
{
int i;
char prop[PATH_MAX];
char path[PATH_MAX];
char name[PATH_MAX];
char prop_name[PATH_MAX];
if (inst)
snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
else
strlcpy(name, class_id, PATH_MAX);
/*
* Here we rely on the fact that calling dlopen multiple times on
* the same .so will simply increment a refcount (and not load
* a new copy of the library).
* We also assume that dlopen() is thread-safe.
*/
/* First try a property specific to the class and possibly instance */
snprintf(prop_name, sizeof(prop_name), "ro.hardware.%s", name);
if (property_get(prop_name, prop, NULL) > 0) {
if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
goto found;
}
}
/* Loop through the configuration variants looking for a module */
for (i=0 ; i
hardware/rockchip/liblights/lights.cpp
/******************************************************************/
/* Copyright (C) ROCK-CHIPS FUZHOU . All Rights Reserved. */
/*******************************************************************
* File : lights.cpp
* Desc : Implement lights adjust HAL
* Author : CMY
* Date : 2009-07-22
* Notes : ..............
*
* Revision 1.00 2009/07/22 CMY
* Revision 2.00 2012/01/08 yxj
* support button charge lights
*
* ...................
* ********************************************************************/
#define LOG_TAG "Lights"
//#include
//#include
#include "lights.h"
#include
#include
#include
/*****************************************************************************/
#define BACKLIGHT_PATH "/sys/class/backlight/rk28_bl/brightness"
#define BUTTON_LED_PATH "sys/class/leds/rk29_key_led/brightness"
#define BATTERY_LED_PATH "sys/class/leds/battery_led/brightness"
int g_bl_fd = 0; //backlight fd
int g_btn_fd = 0; //button light fd
int g_bat_fd = 0; //battery charger fd
static pthread_once_t g_init = PTHREAD_ONCE_INIT;
static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
static int light_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device);
static struct hw_module_methods_t light_module_methods = {
open: light_device_open
};
/*注册一个硬件对象,即Light Stub
light_module_t定义如下:
struct light_module_t {
struct hw_module_t common;
};
每个硬件模块都必须有一个名为HAL_MODULE_INFO_SYM的数据结构
并且此数据结构的字段必须以hw_module_t开头
其次是模块具体信息
*/
struct light_module_t HAL_MODULE_INFO_SYM = {
common: { //初始化父结构体即hw_module_t成员
tag: HARDWARE_MODULE_TAG, //TAG必须为HARDWARE_MODULE_TAG
version_major: 1, //主版本号
version_minor: 0, //次版本号
id: LIGHTS_HARDWARE_MODULE_ID, //硬件ID,就是通过这个ID找到对应的硬件设备的
name: "Lights module", //硬件module名字
author: "Rockchip", //作者
methods: &light_module_methods, //指向封装有open函数指针的结构体
}
//如果有扩展属性,在此处初始化
};
static void init_g_lock(void)
{
pthread_mutex_init(&g_lock, NULL);
}
static int write_int(char const *path, int value)
{
int fd;
static int already_warned;
already_warned = 0;
LOGV("write_int: path %s, value %d", path, value);
fd = open(path, O_RDWR);
if (fd >= 0) {
char buffer[20];
int bytes = sprintf(buffer, "%d\n", value);
int amt = write(fd, buffer, bytes);
close(fd);
return amt == -1 ? -errno : 0;
} else {
if (already_warned == 0) {
LOGE("write_int failed to open %s\n", path);
already_warned = 1;
}
return -errno;
}
}
static int rgb_to_brightness(struct light_state_t const *state)
{
unsigned int color = state->color;
unsigned char brightness = ((77*((color>>16)&0x00ff)) +
(150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
return brightness;
}
int set_backlight_light(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(BACKLIGHT_PATH, brightness);
pthread_mutex_unlock(&g_lock);
return 0;
}
int set_keyboard_light(struct light_device_t* dev, struct light_state_t const* state)
{
LOGI(">>> Enter set_keyboard_light");
return 0;
}
int set_buttons_light(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(BUTTON_LED_PATH, brightness?1:0);
pthread_mutex_unlock(&g_lock);
return 0;
}
int set_battery_light(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(BATTERY_LED_PATH, brightness?1:0);
pthread_mutex_unlock(&g_lock);
return 0;
}
int set_notifications_light(struct light_device_t* dev, struct light_state_t const* state)
{
LOGI(">>> Enter set_notifications_light");
return 0;
}
int set_attention_light(struct light_device_t* dev, struct light_state_t const* state)
{
LOGI(">>> Enter set_attention_light");
return 0;
}
static int light_device_close(struct hw_device_t *dev)
{
struct light_device_t* ctx = (struct light_device_t*)dev;
LOGI(">>> Enter light_device_close");
if (ctx)
free(ctx);
return 0;
}
//打开设备
static int light_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device)
{
int status = 0;
LOGI(">>> Enter light_device_open:%s\n",name);
struct light_device_t *dev;
dev = (light_device_t*)malloc(sizeof(*dev));
/* initialize our state here */
memset(dev, 0, sizeof(*dev));
/*
填充light_device_t结构体
定义:
struct light_device_t {
struct hw_device_t common;
int (*set_light)(struct light_device_t* dev,
struct light_state_t const* state);
};
light_device_t“继承”自hw_device_t,并且扩展了自己的函数set_light
*/
/* initialize the procs */
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 0;
dev->common.module = const_cast(module);
dev->common.close = light_device_close;
*device = &dev->common;
if (!strcmp(name, LIGHT_ID_BACKLIGHT)) {
dev->set_light = set_backlight_light;
}else if(!strcmp(name, LIGHT_ID_KEYBOARD)) {
dev->set_light = set_keyboard_light;
}else if(!strcmp(name, LIGHT_ID_BUTTONS)) {
dev->set_light = set_buttons_light;
}else if(!strcmp(name, LIGHT_ID_BATTERY)) {
dev->set_light = set_battery_light;
}else if(!strcmp(name, LIGHT_ID_NOTIFICATIONS)) {
dev->set_light = set_notifications_light;
}else if(!strcmp(name, LIGHT_ID_ATTENTION)) {
dev->set_light = set_attention_light;
}else{
LOGI(">>> undefine light id");
free(dev);
*device = NULL;
status = -EINVAL;
}
pthread_once(&g_init,init_g_lock);
return status;
}
接下来先看下初始化流程:
LightsService构造方法->init_native(jni)->hw_get_module(hal)->hw_get_module_by_class->load ,这里相当于初始化的过程,主要是传入模块ID,根据模块ID加载对应的so库。并且根据硬件ID即LIGHTS_HARDWARE_MODULE_ID找到我们注册的结构体。
然后再回到init_native(jni)->get_device->module->methods->open(module, name, &device);这里调用到light.cpp中light_device_open,在light_device_open中根据open的第二个参数确定light的类型并绑定到dev->set_light。
再看调节过程:
setBrightness->setLightLocked(android)->setLight_native(jni)->devices->lights[light]->set_light(devices->lights[light], &state); set_light在上一步中已经绑定。
以set_buttons_light为例的话,最终会通过write_int直接调用设备驱动文件。从而能对硬件进行操作。