本文主要实现JNI层和HAL层,通过这两部分的实现来完成具体的硬件的调用过程。
一、JNI层的实现
JNI层的作用主要就是实现上层Java编写的服务层和下层C/C++编写的硬件访问层的相互调用。为了实现Java层和C/C++层之间的调用,需要实现一个C语言的函数和上文实现的Dht11Service.java这个文件当中的本地方法相对应映射,这个映射的实现具体如下:
static const JNINativeMethod methods[] = {
{"native_dht11GetDatas", "()[B", (void *)dht11GetDatas},
{"native_dht11Open", "()I", (void *)dht11Open},
{"native_dht11Close", "()V", (void *)dht11Close},
};
通过这个数组可以看出,每一个java实现的本地方法,都有一个相应的C语言实现的函数与其相对应,这样调用Java的本地方法就调用了相应的本地函数继而可以实现对硬件的访问操作,关于JNI是如何实现这一过程不是本文的重点,感兴趣的读者可以自己去查阅相关的数据和文章。
JNI层实现的这个文件的名为:com_android_server_Dht11Service.cpp,它的具体内容如下:
#define LOG_TAG "Dht11Service"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include
#include
#include
#include
#include
#include
#include
#include
struct dht11_device_t* dht11_device;
namespace android
{
JNIEXPORT jbyteArray JNICALL dht11GetDatas(JNIEnv *, jclass);
JNIEXPORT jint JNICALL dht11Open(JNIEnv *, jclass);
JNIEXPORT void JNICALL dht11Close(JNIEnv *, jclass);
/* Define a array to implement a map for Java methods and C functions */
static const JNINativeMethod methods[] = {
{"native_dht11GetDatas", "()[B", (void *)dht11GetDatas},
{"native_dht11Open", "()I", (void *)dht11Open},
{"native_dht11Close", "()V", (void *)dht11Close},
};
/* Open dht11 device */
JNIEXPORT jint JNICALL dht11Open(JNIEnv *env, jclass cls)
{
jint err;
hw_module_t* module;
hw_device_t* device;
/* get HAL hw_module_t object */
err = hw_get_module("dht11", (hw_module_t const**)&module);
if(err == 0)
{
/* call open from hw_module_t object */
err = module->methods->open(module, NULL, &device);
if(err == 0)
{
dht11_device = (struct dht11_device_t*)device;
dht11_device->dht11_open(dht11_device); // open dht11 device
return 0;
}
else
{
return -1;
}
}
return -1;
}
/* close dht11 device */
JNIEXPORT void JNICALL dht11Close(JNIEnv *env, jclass cls)
{
dht11_device->dht11_close(dht11_device);
return;
}
/* get dht11 datas */
JNIEXPORT jbyteArray JNICALL dht11GetDatas(JNIEnv *env, jclass cls)
{
unsigned char buffer[6];
jbyteArray bytes = 0;
dht11_device->dht11_read(dht11_device, buffer, 6);
bytes = env->NewByteArray(6);
if (bytes != NULL) {
env->SetByteArrayRegion(bytes, 0, 6, (jbyte *)buffer);
}
else
{
return NULL;
}
return bytes;
}
/* register JNI methods to Android System */
int register_android_server_Dht11Service(JNIEnv *env)
{
return jniRegisterNativeMethods(env, "com/android/server/Dht11Service",
methods, NELEM(methods));
}
};
这个代码中主要实现的两个函数分别是:register_android_server_Dht11Service()和dht11Open()这两个函数:
register_android_server_Dht11Service()函数将实现的C语言函数和Java实现的本地接口的映射注册到Android系统当中,这样就可以实现java方法和C/C++本地函数的映射。
dht11Open()通过调用hw_get_module("dht11", (hw_module_t const**)&module);来获取一个hw_module_t结构体的指针,然后调用module->methods->open(module, NULL, &device);这个函数获得一个hw_device_t结构体的指针,然后通过它来调用设备的打开、关闭、获取数据的函数。这些打开、关闭、获取数据的函数都应该定义在HAL层当中,所以由此可以看出HAL层的实现的大概步骤:
a、实现一个hw_module_t类型的结构体
b、填充它的methods字段,并填充methods字段中的open字段
c、实现一个包含hw_device_t这个结构体的结构体,试下DHT11设备的打开、关闭、获取数据的函数。
把com_android_server_Dht11Service.cpp这个文件添加到Android系统当中:首先修改frameworks/base/services/core/jni/onload.cpp这个文件,修改如下:
int register_android_server_Dht11Service(JNIEnv *env);
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved){
register_android_server_Dht11Service(env);
然后把com_android_server_Dht11Service.cpp这个文件放入frameworks/base/services/core/jni这个目录当中,最后修改frameworks/base/services/core/jni/Android.mk这个文件,具体修改如下:
LOCAL_SRC_FILES += \
$(LOCAL_REL_DIR)/com_android_server_Dht11Service.cpp \
HAL层的作用主要就是实现上层的JNI层和驱动层之间的连接作用,但HAL层还有一个作用就是保密,因为Linux内核基于的是GPL协议,它是开源的,而Android系统基于的是Apache协议,它可以保存自己的代码而不必开源。很多厂商都采用这样的做法:他们实现简单的驱动,而把具体的细节放在HAL层当中来实现,最后把它编译成一个动态链接库发给用户,即保密又实现了相应的功能。
HAL层的代码的具体实现包括两个文件:dht11_hal.h和dht11_hal.c。dht11_hal.h的具体实现如下所示:
#ifndef _HARDWARE_DHT11_HAL_H
#define _HARDWARE_DHT11_HAL_H
#include
__BEGIN_DECLS
struct dht11_device_t{
struct hw_device_t common;
void (*dht11_close)(struct dht11_device_t *);
int (*dht11_open)(struct dht11_device_t *);
int (*dht11_read)(struct dht11_device_t *, unsigned char *, int);
};
__END_DECLS
它里面实现了一个dht11_device_t结构体,这个结构体中包含一个子结构体hw_device_t和设备的打开、关闭、获取数据的函数。
dht11_hal.c这个文件的具体实现如下:
#define LOG_TAG "Dht11Hal"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static int fd;
static void dht11_close(struct dht11_device_t *dev)
{
close(fd);
ALOGI("dht11_close fd : %d", fd);
return;
}
static int dht11_open(struct dht11_device_t *dev)
{
fd = open("/dev/dht11", O_RDWR);
ALOGI("dht11_open fd : %d", fd);
return 0;
}
static int dht11_read(struct dht11_device_t *dev, unsigned char *buffer, int count)
{
int ret = read(fd, buffer, count);
ALOGI("dht11_read");
return ret;
}
struct dht11_device_t dht11_dev = {
.common = {
.tag = HARDWARE_DEVICE_TAG,
},
.dht11_close = dht11_close,
.dht11_open = dht11_open,
.dht11_read = dht11_read,
};
static int dht11_device_open(const struct hw_module_t* module, const char* id, struct hw_device_t** device)
{
*device = &dht11_dev;
return 0;
}
static struct hw_module_methods_t dht11_module_methods = {
.open = dht11_device_open,
};
struct hw_module_t HAL_MODULE_INFO_SYM = {
.id = "dht11",
.tag = HARDWARE_MODULE_TAG,
.methods = &dht11_module_methods,
};
它的具体实现如下先定义了一个hw_module_t的结构体变量HAL_MODULE_INFO_SYM并填充了它的methods字段,然后填充了methods字段的open字段,在open字段所对应的函数dht11_device_open()中返回了一个dht11_device_t结构体的指针,在dht11_device_t这个结构体定义的变量dht11_dev中包含了DHT11设备的打开、关闭、获取温度的方法,它的具体实现如下:
struct dht11_device_t dht11_dev = {
.common = {
.tag = HARDWARE_DEVICE_TAG,
},
.dht11_close = dht11_close,
.dht11_open = dht11_open,
.dht11_read = dht11_read,
};
具体的HAL层实现已经完毕,下面把它添加到Android系统当中:
把dht11_hal.h这个文件放到hardware/libhardware/include/hardware这个目录当中
在hardware/libhardware/modules目录当中建立一个dht11的子目录,在这个子目录中加入dht11_hal.c文件,并编写一个Android.mk编译文件,这个文件的内容如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := dht11.default
# HAL module implementation stored in
# hw/.default.so
LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_C_INCLUDES := hardware/libhardware
LOCAL_SRC_FILES := dht11_hal.c
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_MODULE_TAGS := eng
include $(BUILD_SHARED_LIBRARY)
$ mmm hardware/libhardware/modules/dht11
$ mmm frameworks/base/services
$ make snod
$ ./gen-img.sh