AndroidHAL層實現與調用
1.HAL層實現
在這篇文章中我們討論下HAL層代碼的簡單組成,以及jni是如何調用HAL層代碼。文章盡量避免瞭一些無用信息,直接寫有實際的東西。
在這裡我用一個簡單的HAL層代碼(電子防眩目的hal代碼)來開始我們的講解。
在一個hal代碼中主要的工作是實現一個名為HAL_MODULE_INFO_SYM的module實例,其結構定義為
[cpp]
<span xmlns="http://www.w3.org/1999/xhtml" style="">
struct lcd_reflect_module_t {
struct hw_module_t common;
};</span>
可以看到這個結構的名字是隨意的,一般用(模塊名_module_t)來表示,可以看到它隻有一個成員變量hw_module_t,也就是說主要的工作就是填充這個hw_module_t這個結構瞭。
[cpp]
<span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style="">
const struct lcd_reflect_module_t HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.version_major = 1,
.version_minor = 0,
.id = LCD_REFLECT_HARDWARE_MODULE_ID,
.name = "LCD Reflect",
.author = "Archermind Tech.",
.methods = &lcd_reflect_module_methods,
}
};
</span></span>
tag:需要指定為HARDWARE_MODULE_TAG,不能修改
id:指定為HALStub的moduleID,這裡#defineLCD_REFLECT_HARDWARE_MODULE_ID "lcdreflect"
接下來就要實現另外一個重要的結構瞭—structhw_module_methods_t methods.
來看一下這個結構體的定義:
[cpp]
<span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style="">
typedef struct hw_module_methods_t {
/** Open a specific device */
int (*open)(const struct hw_module_t* module, const char* id,
struct hw_device_t** device);
} hw_module_methods_t;
</span></span>
可以看到其中隻有一個open函數指針,作為module的callback。
前面我們已經接觸瞭兩個重要的結構體,hw_module_t和hw_module_methods_t,下面我們還需要來看一下hal層中第三個重要的結構體hw_device_t。
下面我們來看一下hw_module_methods_t中的open函數lcd_reflect_module_methods。
[cpp]
<span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style="">
static int
lcd_reflect_open(const struct hw_module_t *module,
const char *name, struct hw_device_t **device)
{
int status = -EINVAL;
LOGV("lcd_reflect_open\n");
if (!strcmp(name, LCD_REFLECT_HARDWARE)) {
struct lcd_reflect_device_t *dev;
dev = malloc(sizeof (*dev));
memset(dev, 0, sizeof (*dev));
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 0;
dev->common.module = (struct hw_module_t *)module;
dev->common.close = lcd_reflect_close;
*device = &dev->common;
dev->get_level = &lcd_reflect_get_level;
dev->set_level = &lcd_reflect_set_level;
dev->get_state = &lcd_reflect_get_state;
dev->set_state = &lcd_reflect_set_state;
status = 0;
}
return status;
}</span></span>
可以看到hw_device_t作為open的形參傳入open函數中,並且我們還發現瞭另外一個結構體structlcd_reflect_device_t*dev;這個結構是我們模塊自己定義的,用於存放一些我們模塊需要的操作,jni層中就是通過這個結構體來調用hal層中提供的接口的。看一下它的定義。
[cpp]
<span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style="">
struct lcd_reflect_device_t {
struct hw_device_t common;
/**
* Get the mxc_reflect state
*
* Returns: 0 on success, error code on failure
*/
int (*get_state)(struct lcd_reflect_device_t *dev,
int *stat);
/**
* Set the mxc_reflect state
*
* Returns: 0 on success, error code on failure
*/
int (*set_state)(struct lcd_reflect_device_t *dev,
int stat);
/**
* Get the mxc_reflect level
*
* Returns: 0 on success, error code on failure
*/
int (*get_level)(struct lcd_reflect_device_t *dev,
int *level);
/**
* Set the mxc_reflect state
*
* Returns: 0 on success, error code on failure
*/
int (*set_level)(struct lcd_reflect_device_t *dev,
int level);
};</span></span>
裡面有hw_device_t這個成員,並且它是在最前面的一個成員,這個是非常重要的,我們將在下面說明為什麼它需要放在最前面,除瞭hw_device_t就是一些hal層需要提供給jni調用的函數指針。
再回到open函數中,現在我們就能看懂這個open函數所做的工作瞭,它首先註冊瞭一個structlcd_reflect_device_t *dev; lcd_reflect_device_t變量,然後填充common,也就是hw_device_t這個結構,這裡需要註意有幾個成員:
tag:必須指定為HARDWARE_DEVICE_TAG
還需要實現一個close函數,接著*device= &dev->common;,即讓jni的hw_device_t與hal中的lcd_reflect_device_t聯系在一起瞭,由於common這個成員在lcd_reflect_device_t的最前面定義的,那麼也就是lcd_reflect_device_t的地址和common的地址是相同的。隻要知道common的地址就可以知道lcd_reflect_device_t的地址,這樣lcd_reflect_device_t結構就可以傳送到jni層使用瞭,隻需要將common的地址強制轉換一下即可。最後把hal層需要提供給jni的API實現就可以瞭。
這樣一個簡單的hal層代碼框架就有啦。
2.調用HAL層
在jni層,我們通過hw_get_module函數得到hw_module_t結構,如下:
hw_get_module(LCD_REFLECT_HARDWARE_MODULE_ID,(hw_module_t const**)&module);
通過指定LCD_REFLECT_HARDWARE_MODULE_ID來區別module,接著還需要得到lcd_reflect_device_t*device;這個結構,我們可以通過下面這個函數實現:
[cpp]
<span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style=""><span xmlns="http://www.w3.org/1999/xhtml" style="">
static lcd_reflect_device_t *
get_device(hw_module_t *module, char const *name)
{
int err;
hw_device_t *device;
err = module->methods->open(module, name, &device);
if (err == 0) {
return (lcd_reflect_device_t *)device;
} else {
return NULL;
}
}</span></span></span></span></span>
函數返回的是lcd_reflect_device_t結構的地址,在函數中首先定義hw_device_t*device;接著將其通過module->methods->open(module,name, &device);得到hw_device_t這個結構的地址,接著將這個地址返回,返回前需要將地址類型強制轉換一下,(lcd_reflect_device_t*)device,這樣就得到lcd_reflect_device_t這個結構的地址啦。原因我們在上面講過,是因為hw_device_t和lcd_reflect_device_t兩個結構的首地址是相同的。
有瞭lcd_reflect_device_t我們就可以調用hal層中實現的API啦,通過這些API操作硬件。
摘自 android_huber的專欄