深入浅出-Androidシステム移植とプラットフォーム開発(八)-HAL Stubフレームワーク分析


1.HAL Stubフレームワーク分析
HAL stubのフレームワークは比較的簡単で、3つの構造体、2つの定数、1つの関数、321アーキテクチャと略称され、その定義は以下の通りである.
@hardware/libhardware/include/hardware/hardware.h
@hardware/libhardware/hardware.c
/* 
        hw_module_t   ,           。    “  ”  hw_module_t,         ,                :HMI, :Hardware Module Information   ,                  open       ,          open  ,open            Operation interface。
*/
struct hw_module_t{
	uint32_t tag;			//        HARDWARE_MODULE_TAG
	uint16_t version_major;	//     
	uint16_t version_minor; 	//     
	const char *id;			//  id ,    module
	const char *name;		//   module  
	const char * author;		//   
	struct hw_module_methods_t* methods;	//     open        
	void* dso;				// module’s dso
	uint32_t reserved[32-7];	// 128    
};

/* 
     open       ,         :open    
*/
struct hw_module_methods_t{
	//     open    
	int (*open)(const struct hw_module_t* module, const char * id,
		struct hw_device_t** device);
};

/*
    hw_module_t open        Operation interface,  hw_device_t      ,     :        
*/
struct hw_device_t{
	uint32_t tag;				//      HARDWARE_DEVICE_TAG
	uint32_t version;				//    
	struct hw_module_t* module;	//              ,                  
	uint32_t reserved[12];			//     
	int (*close)(struct hw_device_t* device);	//           ,       close  
};

上記の3つの構造間の関係は緊密であり、各ハードウェアオブジェクトは1つのhw_module_tは、このハードウェアオブジェクトを入手すれば、openメソッドを呼び出し、このハードウェアオブジェクトのハードウェア操作インタフェースを返し、これらのハードウェア操作インタフェースを介してハードウェアを間接的に操作することができることを説明する.ただしopenメソッドはstruct hw_module_methods_t構造は一度カプセル化され、ハードウェア操作インタフェースはhw_device_tは一度カプセル化しただけです.
では、ユーザープログラムはどのようにしてハードウェアオブジェクトを手に入れることができますか?
答えはハードウェアid名で取ります.
321アーキテクチャの2つのシンボル定数と1つの関数を見てみましょう.
//     HAL Stub       
#define HAL_MODULE_INFO_SYM         	HMI
//           
#define HAL_MODULE_INFO_SYM_AS_STR  	"HMI"
//               HAL Stub  
int hw_get_module(const char *id, const struct hw_module_t **module);

ユーザがhw_を呼び出すとget_module関数の場合、最初のパラメータがハードウェアid名を渡すと、この関数は現在のシステムに登録されているハードウェアオブジェクトから渡されたid名に対応するハードウェアオブジェクトを検索し、返します.
呼び出し者の観点から、私たちは基本的に障害がありません.では、ハードウェアオブジェクトをどのように登録しますか?
簡単です.構造体を宣言するだけでいいです.次のLed Stub登録の例を見てください.
const struct led_module_t HAL_MODULE_INFO_SYM = {
    common: {	//       hw_module_t  
        tag: HARDWARE_MODULE_TAG,
	    version_major: 1,
	    version_minor: 0,
	    id: LED_HARDWARE_MODULE_ID,
	    name: "led HAL Stub",
	    author: "farsight",
	    methods: &led_module_methods,
	}, 
	//         
};

はい、簡単です.構造体ledを宣言するだけです.moduel_t,HALと名付けてMODULE_INFO_SYM、つまり固定の名前:HMI、それからこの構造体を充填すればいいです.led_module_tはまたどんな構造体のタイプですか?前分析hw_modult_tタイプではhwを「継承」できると言いましたmodule_tタイプ、独自のハードウェアオブジェクトを作成し、独自のプロパティを拡張します.ここでled_module_tは「継承」のhw_module_tタイプ.C言語ではこの概念が継承されていないため、継承に二重引用符が付けられています.
struct led_module_t {
	struct hw_module_t common;
};

構造体led_module_tパッケージhw_module_t構造体、すなわちled_module_tという新しい(子)構造体には古い(親)構造体が含まれており、新しい構造体では新しいメンバーをさらに拡張することができます.構造体自体がパッケージング特性を持っているのは、オブジェクト向けのパッケージングと継承ではないでしょうか.専門的な点を示すために、UMLで説明します.
上のクラス図でhw_module_methods_tにカプセル化されたopen関数ポインタはopenメソッドと書く.
このopenメソッドはmethodsであり、自然に構造体に「継承」され、led_に初期化されます.module_methodsのアドレス、この構造はhw_module_methods_tタイプの宣言コードは以下の通りです.
static struct hw_module_methods_t led_module_methods = {
    open: led_device_open  
};

簡潔で、好きです!!!その中にはopenメンバーだけが関数ポインタであり、led_を指しています.device_Open関数:
static int led_device_open(const struct hw_module_t* module, const char* name,
	struct hw_device_t** device)
{
	struct led_device_t *led_device;
	LOGI("%s E ", __func__);
	led_device = (struct led_device_t *)malloc(sizeof(*led_device));
	memset(led_device, 0, sizeof(*led_device)); 

	// init hw_device_t
	led_device->common.tag= HARDWARE_DEVICE_TAG;
	led_device->common.version = 0;
	led_device->common.module= module;
	led_device->common.close = led_device_close; 

	// init operation interface
	led_device->set_on= led_set_on;
	led_device->set_off= led_set_off;
	led_device->get_led_count = led_getcount;
	*device= (struct hw_device_t *)led_device;

	if((fd=open("/dev/leds",O_RDWR))==-1)
	{
		LOGI("open error");
		return -1;
	}else
	LOGI("open ok
"); return 0; }

led_device_Open関数の機能:
Øハードウェアデバイス操作構造体ledの割り当てdevice_t,この構造体はハードウェアの動作を記述する
Ø初期化led_device_tの親構造体hw_device_tメンバー
Ø初期化led_device_tに拡張された操作インタフェース
Øデバイスを開き、led_device_t構造体は親構造体タイプで返される(オブジェクト内のマルチステート向け)
hw_module_tとhw_module_methods_tおよびハードウェアopen関数の関係は以下の通りである.
ledを見てみましょうdevice_tとその親構造体hw_device_tの関係:
struct led_device_t {
	struct hw_device_t common;   // led_devict_t    ,       close  
	//             led_device_t    hw_device_t   ,                   
	int (*getcount_led)(struct led_device_t *dev);
	int (*set_on)(struct led_device_t *dev);
	int (*set_off)(struct led_device_t *dev);
};

UMLクラス図で表します.
クラス図から分かるようにled_device_tは3つのインタフェースを拡張しました:seton()、setoff()、get_led_count().
残りの作業は、サブ構造の新しい拡張を実現する3つのインタフェースです.
static int led_getcount(struct led_control_device_t*dev)
{
         LOGI("led_getcount");
         return 4;
}
 
static int led_set_on(struct led_control_device_t *dev)
{   
         LOGI("led_set_on");
         ioctl(fd,GPG3DAT2_ON,NULL);
         return 0;
}
 
static int led_set_off(struct led_control_device_t*dev)
{
         LOGI("led_set_off");
         ioctl(fd,GPG3DAT2_OFF,NULL);
         return 0;
}


この3つのインタフェース関数は、ハードウェアを制御するために下層駆動と直接付き合っています.具体的な駆動部分は言いません.それは別のシステムです.
まとめてみます.
ハードウェアid名があります.このidでhw_を呼び出します.get_module(char*id,struct hw_module_t**module)は、現在のシステムに登録されているidに対応するハードウェアオブジェクトを検索して返します.ハードウェアオブジェクトにはhw_module_methods_t構造パッケージのopen関数ポインタ、このopen関数をコールバックし、ハードウェア操作インタフェースがパッケージされたled_を返します.device_t構造体は、このハードウェアインタフェースを介してハードウェアに間接的にアクセスすることができます.
この過程でhw_get_moduleはサブ構造体タイプledを返します.module_t,関数の2番目のパラメータタイプはhw_であるがmodule_tの親タイプで,ここではオブジェクト向けの多態の概念を用いた.
次の問題はまだ解決されていません.なぜHMI構造体という名前を宣言した後、システムに登録しましたか?hw_get_module関数はどのようにled_を見つけて返しますか?module_t説明のハードウェアオブジェクトの?
鶏を殺して卵を取ってHAL Stubを探します
宣言構造体を介してHALStubをシステムに登録する理由を知るには、hw_を介してどのように登録するかを知ることが最善の方法です.get_module_tは、登録されたハードウェアオブジェクトを見つけます.
hw_を分析しますget_module関数の実装:
@hardware/libhardware/hardware.c
static const char *variant_keys[] = {
	“ro.hardware”,
	“ro.product.board”,
	“ro.board.platform”,
	“ro.arch”
};
//              ,HAL_VARIANT_KEYS_COUNT   4
struct constint HAL_VARIANT_KEYS_COUNT = (sizeof(variant_keys)/sizeof(variant_keys[0]));

int hw_get_module(const char *id, const struct hw_module_t **module){
	//   3    hw_get_module_by_class  
return hw_get_module_by_class(id, NULL, module);
}

int hw_get_module_by_class(const char *class_id, const char *inst, 
const struct hw_module_t **module){
	int status;
	int i;
	//     hw_module_t    hmi
	const struct hw_module_t *hmi = NULL;
	char prop[PATH_MAX};
	char path[PATH_MAX];
	char name[PATH_MAX];
	//          ,inst = NULL,  else  ,   id    name   
	if(inst)
		snprintf(name, PATH_MAX, “%s.%s”, class_id, inst);
	else
		strlcpy(name, class_id, PATH_MAX);
	// i   5 
	for(i=0; i) != 0){
		//       
	}
	//         hmi     dso   
	hmi->dso = handle;
	//          load   ,           hw_get_module    
	*pHmi = hmi;
	//     
}

上記のコードの注釈解析から,ハードウェアオブジェクト宣言の構造体コードがsoライブラリにコンパイルされていることが分かった.この構造体はconstタイプと宣言されているため,soライブラリはその静的コードセグメントに含まれている.ハードウェアオブジェクトを見つけるには,まず対応するsoライブラリを見つけ,dlopen,dlsymという「鶏を殺して卵を取る」方法でハードウェアオブジェクトを見つける.もちろんここの「鶏」とは、soライブラリ、「卵」とは、ハードウェアオブジェクトled_module_t構造.
宣言構造体led_module_tの場合、その名前はHMIとして統一的に定義され、dlsymによってled HAL Stubソースで生成されたsoライブラリの「HMI」記号を検索することを目的とする.明らかに、私たちが書いたHAL Stubコードは最終的にsoライブラリファイルをコンパイルし、ライブラリファイル名は:led.default.so(もちろん、4つのシステム属性の1つを設定して名前を指定できます:led.属性値.so)、ライブラリのディレクトリは:/system/lib/hw/です.
今では底の実装部分がほぼ浸透していますが、今は呼び出し者に目を向けています.本章の冒頭で紹介したように、上層がローカルコードを呼び出すにはJNI技術を使う必要があります.まずJNIの知識を悪用しましょう.