一、代码讲解
1、 编写HAL层代码
一般来说HAL moudle需要涉及的是三个关键结构体:
struct hw_module_t;
struct hw_module_methods_t;
struct hw_device_t;
下面结合代码说明这3个结构的用法
文件:weiyan/hardware/modules/include/weiyan/led.h
//HAL 规定不能直接使用hw_module_t结构 //因此需要做这么一个继承 struct led_module_t { struct hw_module_t common; }; struct led_control_device_t { //自定义一个针对led控制的结构 //包含hw_device_t和支持的API操作 struct hw_device_t common; /* supporting control APIs go here */ int (*set_on)(struct led_control_device_t *dev, int32_t led); int (*set_off)(struct led_control_device_t *dev, int32_t led); }; /*****************************************************************************/ struct led_control_context_t { struct led_control_device_t device; }; //定义一个MODULE_ID //HAL层可以根据这个ID找到我们这个HAL Stub #define LED_HARDWARE_MODULE_ID "led"
文件:weiyan/hardware/modules/led/led.c
int led_device_close(struct hw_device_t* device) { struct led_control_device_t* ctx = (struct led_control_device_t*)device; if (ctx) { free(ctx); } return 0; } int led_on(struct led_control_device_t *dev, int32_t led) { int fd; char buff[3] = ""; int size = 0; if ((fd = open(DEVICE_NAME, O_RDWR)) == -1) { LOGI("open leds fail"); return 0; } //memset(buff,'1',5); memset(buff,0,3); buff[0]=1;//灯开 buff[1]=(char)led; //leds 灯号 size = write(fd, buff, sizeof(buff)); LOGI("LED Stub: set %d on.", led); close(fd); return 0; } int led_off(struct led_control_device_t *dev, int32_t led) { int fd; char buff[5] = ""; int size = 0; if ((fd = open(DEVICE_NAME, O_RDWR)) == -1) { LOGI("open leds fail"); return 0; } memset(buff,0,3); buff[0]=0;//灯关 buff[1]=(char)led; //leds 灯号 size = write(fd, buff, sizeof(buff)); LOGI("LED Stub: set %d off.", led); close(fd); return 0; } static int led_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device) { struct led_control_device_t *dev; dev = (struct led_control_device_t *)malloc(sizeof(*dev)); memset(dev, 0, sizeof(*dev)); dev->common.tag = HARDWARE_DEVICE_TAG; dev->common.version = 0; dev->common.module = module; dev->common.close = led_device_close; dev->set_on = led_on;//实例化支持的操作 dev->set_off = led_off; //将实例化后的led_control_deivce_t地址返回给jni层 //这样jni层就可以直接调用led_on、led_off、led_device_close 方法 *device = &dev->common; success: return 0; } static struct hw_module_methods_t led_module_methods = { open: led_device_open }; //向系统注册一个iD为LED_HARDWARE_MODULE_ID的stub //注意这里的HAL_MODULE_INFO_SYM不能修改 const struct led_module_t HAL_MODULE_INFO_SYM = { common: { tag: HARDWARE_MODULE_TAG, version_major: 1, version_minor: 0, id: LED_HARDWARE_MODULE_ID, name: "Sample LED Stub", author: "The Mokoid Open Source Project", methods: &led_module_methods, } /* supporting APIs go here */ };
2.JNI层
文件:weiyan/frameworks/base/service/jni/com_mokoid_server_LedService.cpp
struct led_control_device_t *sLedDevice = NULL; static jboolean weityan_setOn(JNIEnv* env, jobject thiz, jint led) { LOGI("LedService JNI: weityan_setOn() is invoked."); if (sLedDevice == NULL) { LOGI("LedService JNI: sLedDevice was not fetched correctly."); return -1; } else { return sLedDevice->set_on(sLedDevice, led);//调用HAL层的方法 } } static jboolean weiyan_setOff(JNIEnv* env, jobject thiz, jint led) { LOGI("LedService JNI: weiyan_setOff() is invoked."); if (sLedDevice == NULL) { LOGI("LedService JNI: sLedDevice was not fetched correctly."); return -1; } else { return sLedDevice->set_off(sLedDevice, led);//调用HAL层的方法 } } /** helper APIs */ static inline int led_control_open(const struct hw_module_t* module, struct led_control_device_t** device) { //这个过程非常重要 //JNI通过LED_HARDWARE_MODULE_ID 找到对应的Stub return module->methods->open(module, LED_HARDWARE_MODULE_ID, (struct hw_device_t**)device); } static jboolean weiyan_init(JNIEnv *env, jclass clazz) { led_module_t* module; //根据LED_HARDWARE_MODULE_ID找到对应的hw_module_t if (hw_get_module(LED_HARDWARE_MODULE_ID, (const hw_module_t**)&module) == 0) { LOGI("LedService JNI: LED Stub found."); if (led_control_open(&module->common, &sLedDevice) == 0) { LOGI("LedService JNI: Got Stub operations."); return 0; } } LOGE("LedService JNI: Get Stub operations failed."); return -1; } // ---------------------------------------------------------------------------- /* *JNINativeMethod是JNI层注册的方法 *Framework层可以使用这些方法 * _init,_set_on,_set_off是Framework层调用的方法 * ()Z 无参数返回值为bool型 * (I)Z整形参数返回值为bool型 */ static const JNINativeMethod gMethods[] = { {"_init", "()Z", (void*)weiyan_init},//framework层调用_init 时促发 { "_set_on", "(I)Z", (void*)weityan_setOn }, { "_set_off", "(I)Z", (void*)weiyan_setOff }, }; static int registerMethods(JNIEnv* env) { static const char* const kClassName = "com/weiyan/server/LedService";//必须与Frameword层的service类名相同 jclass clazz; /* look up the class */ clazz = env->FindClass(kClassName); if (clazz == NULL) { LOGE("Can't find class %s/n", kClassName); return -1; } /* register all the methods */ if (env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK) { LOGE("Failed registering methods for %s/n", kClassName); return -1; } /* fill out the rest of the ID cache */ return 0; } // ---------------------------------------------------------------------------- /* * Framework层加载JNI库时调用 */ jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env = NULL; jint result = -1; if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { LOGE("ERROR: GetEnv failed/n"); goto bail; } assert(env != NULL); //注册JNINavtiveMethod方法 if (registerMethods(env) != 0) { LOGE("ERROR: PlatformLibrary native registration failed/n"); goto bail; } /* success -- return valid version number */ result = JNI_VERSION_1_4; bail: return result; }
3.Framework层的service
文件:weiyan/frameworks/base/service/java/com/weiyan/server
public final class LedService extends ILedService.Stub { static { System.load("/system/lib/libmokoid_runtime.so");//加载JNI动态 库 } public LedService() { Log.i("LedService", "Go to get LED Stub..."); _init(); } /* * Mokoid LED native methods. */ public boolean setOn(int led) { Log.i("MokoidPlatform", "LED On"); return _set_on(led); } public boolean setOff(int led) { Log.i("MokoidPlatform", "LED Off"); return _set_off(led); } //声明jni可以使用的方法 private static native boolean _init(); private static native boolean _set_on(int led); private static native boolean _set_off(int led); }
4.APP 测试程序 (属于APP层)
APP层两种调用模式
(1)Android的app可以直接通过service调用.so格式的jni
(2)经过Manager调用service
Manager (属于Framework层)
public class LedManager { private static final String TAG = "LedManager"; private ILedService mLedService; public LedManager() { //利用ServiceManager获取LedService,从而调用它 //提供的方法,这要求LedService必须已经增加 //到ServiceManager中,这个过程将在App的一个 //Service进程中完成 mLedService = ILedService.Stub.asInterface( ServiceManager.getService("led")); if (mLedService != null) { Log.i(TAG, "The LedManager object is ready."); } } public boolean LedOn(int n) { boolean result = false; try { result = mLedService.setOn(n); } catch (RemoteException e) { Log.e(TAG, "RemoteException in LedManager.LedOn:", e); } return result; } public boolean LedOff(int n) { boolean result = false; try { result = mLedService.setOff(n); } catch (RemoteException e) { Log.e(TAG, "RemoteException in LedManager.LedOff:", e); } return result; } }
因为LedService和LedManager在不同的进程,所以要考虑到进程通讯的问题。Manager通过增加一个aidl文件来描述通讯接口
文件:weiyan/frameworks/base/core/java/weiyan/hardware/ILedService.aidl
package mokoid.hardware;
interface ILedService
{
boolean setOn(int led);
boolean setOff(int led);
}
系统的aidl工具会将ILedService.aidl生成ILedService.java文件,实现IledService
SystemServer (属于APP层)
文件: weiyan/apps/LedTest/src/com/weiyan/LedTest/LedSystemServer.java
public class LedSystemServer extends Service { @Override public IBinder onBind(Intent intent) { return null; } public void onStart(Intent intent, int startId) { Log.i("LedSystemServer", "Start LedService..."); /* Please also see SystemServer.java for your interests. */ LedService ls = new LedService(); try { //将LedService添加到ServiceManager ServiceManager.addService("led", ls); } catch (RuntimeException e) { Log.e("LedSystemServer", "Start LedService failed."); } } }
二、 加载方法
1、把weiyan.tar.gz解压到/opt/ android_froyo_smdk
$ cd /opt/ android_froyo_smdk
$ tar -jxvf weiyan.tar.bz2
2、 修改build/core/config.mk文件防止编译找不到led.h头文件
$cd /opt/ android_froyo_smdk
$gedit build/core/config.mk
找到SRC_HEADERS := /
$(TOPDIR)system/core/include /
在后面加入
$(TOPDIR)weiyan/hardware/modules/include
3、编译工程
$ source /opt/android_froyo_smdk/build/envsetup.sh
$ export TARGET_PRODUCT=sec_smdkv210
$mmm /opt/android_froyo_smdk/weiyan
编译成功后会如下路径生成apk文件,库文件,jar包等
/opt/android_froyo_smdk/out/target/product/smdkv210/system/app/LedClient.apk
/opt/android_froyo_smdk/out/target/product/smdkv210/system/app/LedTest.apk
/opt/android_froyo_smdk/out/target/product/smdkv210/system/framework/ledctl.jar
/opt/android_froyo_smdk/out/target/product/smdkv210/system/lib/hw/led.smdkv210.so
/opt/android_froyo_smdk/out/target/product/smdkv210/system/lib/libled.so
/opt/android_froyo_smdk/out/target/product/smdkv210/system/lib/libmokoid_runtime.so
把LedClient.apk, LedTest.apk放到android的system/app目录,把ledctl.jar放到system/framework目录,把led.smdkv210.so放到system/lib/hw目录,把ibled.so,
libmokoid_runtime.so放到system/lib目录下
4、为了android桌面能显示我们的LedClient.apk, LedTest.apk程序,把weiyan/frameworks/base/service/com.weiyan.server.xml放到android的
system/etc/permissions目录下
5、加载led驱动模块
把leds.ko复制到android 的system目录下,执行
#insmod leds.ko
#chmod 666 /dev/leds
6、运行LedClient.apk, LedTest.apk