深入浅出-Androidシステム移植とプラットフォーム開発(八)-HAL Stubフレームワーク分析
1.HAL Stubフレームワーク分析
HAL stubのフレームワークは比較的簡単で、3つの構造体、2つの定数、1つの関数、321アーキテクチャと略称され、その定義は以下の通りである.
@hardware/libhardware/include/hardware/hardware.h
@hardware/libhardware/hardware.c
上記の3つの構造間の関係は緊密であり、各ハードウェアオブジェクトは1つのhw_module_tは、このハードウェアオブジェクトを入手すれば、openメソッドを呼び出し、このハードウェアオブジェクトのハードウェア操作インタフェースを返し、これらのハードウェア操作インタフェースを介してハードウェアを間接的に操作することができることを説明する.ただしopenメソッドはstruct hw_module_methods_t構造は一度カプセル化され、ハードウェア操作インタフェースはhw_device_tは一度カプセル化しただけです.
では、ユーザープログラムはどのようにしてハードウェアオブジェクトを手に入れることができますか?
答えはハードウェアid名で取ります.
321アーキテクチャの2つのシンボル定数と1つの関数を見てみましょう.
ユーザがhw_を呼び出すとget_module関数の場合、最初のパラメータがハードウェアid名を渡すと、この関数は現在のシステムに登録されているハードウェアオブジェクトから渡されたid名に対応するハードウェアオブジェクトを検索し、返します.
呼び出し者の観点から、私たちは基本的に障害がありません.では、ハードウェアオブジェクトをどのように登録しますか?
簡単です.構造体を宣言するだけでいいです.次のLed Stub登録の例を見てください.
はい、簡単です.構造体ledを宣言するだけです.moduel_t,HALと名付けてMODULE_INFO_SYM、つまり固定の名前:HMI、それからこの構造体を充填すればいいです.led_module_tはまたどんな構造体のタイプですか?前分析hw_modult_tタイプではhwを「継承」できると言いましたmodule_tタイプ、独自のハードウェアオブジェクトを作成し、独自のプロパティを拡張します.ここでled_module_tは「継承」のhw_module_tタイプ.C言語ではこの概念が継承されていないため、継承に二重引用符が付けられています.
構造体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タイプの宣言コードは以下の通りです.
簡潔で、好きです!!!その中にはopenメンバーだけが関数ポインタであり、led_を指しています.device_Open関数:
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の関係:
UMLクラス図で表します.
クラス図から分かるようにled_device_tは3つのインタフェースを拡張しました:seton()、setoff()、get_led_count().
残りの作業は、サブ構造の新しい拡張を実現する3つのインタフェースです.
この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
上記のコードの注釈解析から,ハードウェアオブジェクト宣言の構造体コードが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の知識を悪用しましょう.
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の知識を悪用しましょう.