此文目前为半半半成品,待完善。
本文以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 <utils/misc.h> #include <utils/Log.h> #include <hardware/hardware.h> //hal头文件 #include <hardware/lights.h> #include <stdio.h> 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 <hardware/hardware.h> #include <cutils/properties.h> #include <dlfcn.h> #include <string.h> #include <pthread.h> #include <errno.h> #include <limits.h> #define LOG_TAG "HAL" #include <utils/Log.h> /** 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 "<MODULE_ID>.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<HAL_VARIANT_KEYS_COUNT; i++) { if (property_get(variant_keys[i], prop, NULL) == 0) { continue; } if (hw_module_exists(path, sizeof(path), name, prop) == 0) { goto found; } } /* Nothing found, try the default */ if (hw_module_exists(path, sizeof(path), name, "default") == 0) { goto found; } return -ENOENT; found: /* load the module, if this fails, we're doomed, and we should not try * to load a different variant. */ return load(class_id, path, module); } //jni的init_native函数会调用这个函数 int hw_get_module(const char *id, const struct hw_module_t **module) { return hw_get_module_by_class(id, NULL, module); }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 <hardware/hardware.h> //#include <hardware/lights.h> #include "lights.h" #include <fcntl.h> #include <errno.h> #include <cutils/atomic.h> /*****************************************************************************/ #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<hw_module_t*>(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直接调用设备驱动文件。从而能对硬件进行操作。