DHT 11温湿度センサに基づくAndroidハードウェアアクセスサービスの簡単な実現(三)


本稿では主にJNI層とHAL層を実現し,この2つの部分の実現によって具体的なハードウェアの呼び出しプロセスを完了する.
一、JNI層の実現
JNI層の役割は主に上位Java作成のサービス層と下位C/C++作成のハードウェアアクセス層の相互呼び出しを実現することである.Java層とC/C++層との間の呼び出しを実現するためには、1つのC言語の関数と前述のDht 11 Servicesとを実現する必要がある.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));
	}

};

このコードで主に実装される2つの関数は、register_です.android_server_Dht 11 Service()とdht 11 Open()の2つの関数:
register_android_server_Dht 11 Service()関数は実装されたC言語関数とJava実装のローカルインタフェースのマッピングをAndroidシステムに登録し,javaメソッドとC/C++ローカル関数のマッピングを実現する.
dht 11 Open()呼び出し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この構造体の構造体は、DHT 11デバイスのオン、オフ、データ取得の関数を試してみましょう.
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層の実現
HAL層の役割は主に上位層のJNI層と駆動層との接続を実現することであるが、HAL層のもう一つの役割は秘密保持である.LinuxカーネルはGPLプロトコルに基づいており、オープンソースであり、AndroidシステムはApacheプロトコルに基づいており、オープンソースではなく独自のコードを保存することができるからだ.多くのメーカーはこのようなやり方を採用しています.彼らは簡単な駆動を実現し、具体的な詳細をHAL層の中に置いて実現し、最後にそれを動的リンクライブラリにコンパイルしてユーザーに送り、つまり秘密保持と相応の機能を実現しました.
HALレイヤのコードの具体的な実装は、2つのファイルを含む:dht 11_hal.hとdht 11_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

dht 11が実現しました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フィールドに対応する関数dht 11_device_Open()でdht 11が返されました.device_t構造体のポインタ、dht 11_device_tこの構造体定義変数dht 11_devにはDHT 11装置の開閉・温度取得方法が含まれており、その具体的な実現は以下の通りである.
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システムに追加します.
dht 11_をhal.hこのファイルはhardware/libhardware/include/hardwareというディレクトリに格納されます
hardware/libhardware/modulesディレクトリにdht 11のサブディレクトリを作成し、このサブディレクトリにdht 11_を追加します.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)

最後に、上で実装したJNIレイヤとHALレイヤをコンパイルし、新しいカーネルミラーを生成し、開発ボードにダウンロードします.
$ mmm hardware/libhardware/modules/dht11
$ mmm frameworks/base/services
$ make snod
$ ./gen-img.sh