Android開發之HAL層
本文摘自 羅昇陽的《Anroid系統原始碼情景分析》,更新至Android7.0分析
一、概念
一、Android系統為硬體抽象層中的模組介面定義了編寫規範,我們必須按照這個規範來編寫自己的硬體模組介面。 二、Android系統的硬體抽象層以模組的形式來管理各個硬體訪問介面。每一個硬體模組都對應有一個動態連結庫檔案,這些動態連結庫檔案的命名需要符合一定的規範。同時,在系統內部,每一個硬體抽象層模組都是用結構體hw_module_t來描述,而硬體裝置則使用結構體hw_device_t來描述。
二、硬體抽象層模組檔案命名規範
硬體抽象層模組檔案的命名規範定義在
hardware/libhardware/hardware.c,基於android 7.0
/**
* There are a set of variant filename for modules. The form of the filename
* is "<MODULE_ID>.variant.so" so for the led module the Dream variants
* of base "ro.product.board", "ro.board.platform" and "ro.arch" would be:
*
* led.trout.so
* led.msm7k.so
* led.ARMV6.so
* led.default.so
*/
static const char *variant_keys[] = {
"ro.hardware" , /* This goes first so that it can pick up a different
file on the emulator. */
"ro.product.board",
"ro.board.platform",
"ro.arch"
};
以上程式碼和註釋的意思是:硬體抽象模組檔案的命名規範為“<MODULE_ID>.variant.so”,其中MODULE_ID 標識模組的ID,variant 表示四個系統屬性
ro.hardware
、ro.product.board
、ro.board.platform
、ro.arch
之一。 size=3 系統在載入硬體抽象層模組時,依次按照ro.hardware.MODULE_ID
、ro.hardware
、ro.product.board
、ro.board.platform
、ro.arch
的順序來取他們的屬性值。 如果其中一個系統屬性存在,那麼就把它的值作為variant的值,然後在檢查對應的檔案是否存在,如果存在,那麼就找到要載入的硬體抽象層檔案了; 如果四個屬性都不存在,或者四個屬性對應的系統硬體抽象層檔案不存在,就是用 “<MODULE_ID>.default.so” 來作為要載入的硬體抽象層模組檔案的名稱
三、硬體抽象層模組結構體定義規範
結構體 hw_module_t
和 hw_device_t
及其相關的其他結構體定義在檔案 hardware/libhardware/include/hardware/hardware.h,基於android 7.0
中。
struct hw_module_t
/*
* Value for the hw_module_t.tag field
*/
#define MAKE_TAG_CONSTANT(A,B,C,D) (((A) << 24) | ((B) << 16) | ((C) << 8) | (D))
#define HARDWARE_MODULE_TAG MAKE_TAG_CONSTANT('H', 'W', 'M', 'T')
/**
* Every hardware module must have a data structure named HAL_MODULE_INFO_SYM
* and the fields of this data structure must begin with hw_module_t
* followed by module specific information.
*/
typedef struct hw_module_t {
/** tag must be initialized to HARDWARE_MODULE_TAG */
uint32_t tag;
/**
* The API version of the implemented module. The module owner is
* responsible for updating the version when a module interface has
* changed.
*
* The derived modules such as gralloc and audio own and manage this field.
* The module user must interpret the version field to decide whether or
* not to inter-operate with the supplied module implementation.
* For example, SurfaceFlinger is responsible for making sure that
* it knows how to manage different versions of the gralloc-module API,
* and AudioFlinger must know how to do the same for audio-module API.
*
* The module API version should include a major and a minor component.
* For example, version 1.0 could be represented as 0x0100. This format
* implies that versions 0x0100-0x01ff are all API-compatible.
*
* In the future, libhardware will expose a hw_get_module_version()
* (or equivalent) function that will take minimum/maximum supported
* versions as arguments and would be able to reject modules with
* versions outside of the supplied range.
*/
uint16_t module_api_version;
#define version_major module_api_version
/**
* version_major/version_minor defines are supplied here for temporary
* source code compatibility. They will be removed in the next version.
* ALL clients must convert to the new version format.
*/
/**
* The API version of the HAL module interface. This is meant to
* version the hw_module_t, hw_module_methods_t, and hw_device_t
* structures and definitions.
*
* The HAL interface owns this field. Module users/implementations
* must NOT rely on this value for version information.
*
* Presently, 0 is the only valid value.
*/
uint16_t hal_api_version;
#define version_minor hal_api_version
/** Identifier of module */
const char *id;
/** Name of this module */
const char *name;
/** Author/owner/implementor of the module */
const char *author;
/** Modules methods */
struct hw_module_methods_t* methods;
/** module's dso */
void* dso;
#ifdef __LP64__
uint64_t reserved[32-7];
#else
/** padding to 128 bytes, reserved for future use */
uint32_t reserved[32-7];
#endif
} hw_module_t;
一、從hw_module_t 註釋可以看出,每個硬體抽象層模組都必須有一個名稱為 HAL_MODULE_INFO_SYM 的結構體,且這個結構體的第一個欄位必須是
hw_module_t
二、
hw_module_t.tag
的值必須是 HARDWARE_MODULE_TAG ,即(H) << 24) | ((W) << 16) | ((M) << 8) | (T)
,用來標識硬體抽象層模組結構體。三、hw_module_t.methods定義了一個硬體抽象層模組的操作列表:
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;
hw_module_methods_t 只有一個成員變數,他是一個函式指標,用來開啟硬體抽閒層模組中的硬體裝置。其中引數module表示要開啟的硬體裝置所在模組;引數id表示要開啟的硬體裝置的ID;引數device是一個輸出引數,用來描述一個已經開啟的硬體裝置
struct hw_device_t
#define HARDWARE_DEVICE_TAG MAKE_TAG_CONSTANT('H', 'W', 'D', 'T')
/**
* Every device data structure must begin with hw_device_t
* followed by module specific public methods and attributes.
*/
typedef struct hw_device_t {
/** tag must be initialized to HARDWARE_DEVICE_TAG */
uint32_t tag;
/**
* Version of the module-specific device API. This value is used by
* the derived-module user to manage different device implementations.
*
* The module user is responsible for checking the module_api_version
* and device version fields to ensure that the user is capable of
* communicating with the specific module implementation.
*
* One module can support multiple devices with different versions. This
* can be useful when a device interface changes in an incompatible way
* but it is still necessary to support older implementations at the same
* time. One such example is the Camera 2.0 API.
*
* This field is interpreted by the module user and is ignored by the
* HAL interface itself.
*/
uint32_t version;
/** reference to the module this device belongs to */
struct hw_module_t* module;
/** padding reserved for future use */
#ifdef __LP64__
uint64_t reserved[12];
#else
uint32_t reserved[12];
#endif
/** Close this device */
int (*close)(struct hw_device_t* device);
} hw_device_t;
一、從hw_device_t 註釋可以看出硬體抽象層模組的每一個硬體裝置都必須自定義一個硬體裝置結構體,且他的第一個成員變數的型別必須是
hw_device_t
二、
hw_device_t.tag
的值必須是 HARDWARE_DEVICE_TAG ,即(H) << 24) | ((W) << 16) | ((D) << 8) | (T)
,用來標識硬體抽象中的硬體裝置結構體。結構體
hw_device_t
的成員變數close是一個函式指標,它用來關閉一個硬體裝置。
注意:硬體抽象層中的硬體裝置是由其所在的模組提供介面來開啟的,而關閉是由硬體裝置自身提供介面來完成的
四、硬體抽象層模組的載入過程
在Android硬體抽象層中,負責載入硬體抽象層模組的函式是,它的原型如下:
/**
* Get the module info associated with a module by id.
*
* @return: 0 == success, <0 == error and *module == NULL
*/
int hw_get_module(const char *id, const struct hw_module_t **module);
/**
* Get the module info associated with a module instance by class 'class_id'
* and instance 'inst'.
*
* Some modules types necessitate multiple instances. For example audio supports
* multiple concurrent interfaces and thus 'audio' is the module class
* and 'primary' or 'a2dp' are module interfaces. This implies that the files
* providing these modules would be named audio.primary.<variant>.so and
* audio.a2dp.<variant>.so
*
* @return: 0 == success, <0 == error and *module == NULL
*/
int hw_get_module_by_class(const char *class_id, const char *inst,
const struct hw_module_t **module);
返回0表示載入成功,<0則表示失敗,class_id 表示具體要載入的模組名稱,例如audio,inst 為NULL,hw_module_t為輸出引數。
接下來分析具體實現:
int hw_get_module_by_class(const char *class_id, const char *inst,
const struct hw_module_t **module)
{
int i = 0;
char prop[PATH_MAX] = {0};
char path[PATH_MAX] = {0};
char name[PATH_MAX] = {0};
char prop_name[PATH_MAX] = {0};
if (inst)
snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
else
strlcpy(name, class_id, PATH_MAX);
/*
* Here we rely on the fact that calling dlopen multiple times on
* the same .so will simply increment a refcount (and not load
* a new copy of the library).
* We also assume that dlopen() is thread-safe.
*/
/* First try a property specific to the class and possibly instance */
snprintf(prop_name, sizeof(prop_name), "ro.hardware.%s", name);
if (property_get(prop_name, prop, NULL) > 0) {
if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
goto found;
}
}
/* Loop through the configuration variants looking for a module */
for (i=0 ; i<HAL_VARIANT_KEYS_COUNT; i++) {
if (property_get(variant_keys[i], prop, NULL) == 0) {
continue;
}
if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
goto found;
}
}
/* Nothing found, try the default */
if (hw_module_exists(path, sizeof(path), name, "default") == 0) {
goto found;
}
return -ENOENT;
found:
/* load the module, if this fails, we're doomed, and we should not try
* to load a different variant. */
return load(class_id, path, module);
}
在第三小節我們已經說過了,硬體抽象層模組動態庫的命名規範,首先系統會按照規範去獲取ro.hardware.ID
以及variant_keys
中的屬性值 ,判斷是否存在,不存在使用default
,接著通過hw_module_exists
去拼接完整的動態庫路徑和名稱,並訪問,訪問成功通過load函式
去載入該硬體抽象層模組動態庫,首先看到hw_module_exists
這個函式:
/** Base path of the hal modules */
#if defined(__LP64__)
#define HAL_LIBRARY_PATH1 "/system/lib64/hw"
#define HAL_LIBRARY_PATH2 "/vendor/lib64/hw"
#define HAL_LIBRARY_PATH3 "/odm/lib64/hw"
#else
#define HAL_LIBRARY_PATH1 "/system/lib/hw"
#define HAL_LIBRARY_PATH2 "/vendor/lib/hw"
#define HAL_LIBRARY_PATH3 "/odm/lib/hw"
#endif
/*
* Check if a HAL with given name and subname exists, if so return 0, otherwise
* otherwise return negative. On success path will contain the path to the HAL.
*/
static int hw_module_exists(char *path, size_t path_len, const char *name,
const char *subname)
{
snprintf(path, path_len, "%s/%s.%s.so",
HAL_LIBRARY_PATH3, name, subname);
if (access(path, R_OK) == 0)
return 0;
snprintf(path, path_len, "%s/%s.%s.so",
HAL_LIBRARY_PATH2, name, subname);
if (access(path, R_OK) == 0)
return 0;
snprintf(path, path_len, "%s/%s.%s.so",
HAL_LIBRARY_PATH1, name, subname);
if (access(path, R_OK) == 0)
return 0;
return -ENOENT;
}
訪問路徑優先順序分別是:HAL_LIBRARY_PATH3(/odm/lib/hw)、 HAL_LIBRARY_PATH2(/vendor/lib/hw)、HAL_LIBRARY_PATH1 (/vendor/lib/hw)
最後通過access函式
去訪問該路徑名,訪問成功返回0,否則<0
最後訪問成功,來到load函式:
/**
* Load the file defined by the variant and if successful
* return the dlopen handle and the hmi.
* @return 0 = success, !0 = failure.
*/
static int load(const char *id,
const char *path,
const struct hw_module_t **pHmi)
{
int status = -EINVAL;
void *handle = NULL;
struct hw_module_t *hmi = NULL;
/*
* load the symbols resolving undefined symbols before
* dlopen returns. Since RTLD_GLOBAL is not or'd in with
* RTLD_NOW the external symbols will not be global
*/
handle = dlopen(path, RTLD_NOW);
if (handle == NULL) {
char const *err_str = dlerror();
ALOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");
status = -EINVAL;
goto done;
}
/* Get the address of the struct hal_module_info. */
const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
hmi = (struct hw_module_t *)dlsym(handle, sym);
if (hmi == NULL) {
ALOGE("load: couldn't find symbol %s", sym);
status = -EINVAL;
goto done;
}
/* Check that the id matches */
if (strcmp(id, hmi->id) != 0) {
ALOGE("load: id=%s != hmi->id=%s", id, hmi->id);
status = -EINVAL;
goto done;
}
hmi->dso = handle;
/* success */
status = 0;
done:
if (status != 0) {
hmi = NULL;
if (handle != NULL) {
dlclose(handle);
handle = NULL;
}
} else {
ALOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",
id, path, *pHmi, handle);
}
*pHmi = hmi;
return status;
}
硬體抽象層模組檔案實際上是一個動態連結庫檔案,即so庫檔案。因此,首先呼叫
dlopen函式
將他載入到記憶體中接著通過
dlsym函式
來獲得裡面名稱為HAL_MODULE_INFO_SYM_AS_STR的符號。這個HAL_MODULE_INFO_SYM_AS_STR指向的是一個自定義硬體抽象層模組結構體,它包含了對應的硬體抽象層模組的所有資訊。HAL_MODULE_INFO_SYM_AS_STR是一個巨集,它的值定義為“HMI”
根據硬體抽象層的編寫規範,每一個硬體抽象層模組都必須包含一個名稱為
HMI
的符號,而且這個符號的第一個成員變數的型別必須是hw_module_t
,因此,可以安全的將模組中的HMI
符號轉換成一個hw_module_t
結構體指標。得到
hw_module_t
指標之後,通過strcmp函式
來驗證載入得到的硬體抽象層ID是否與所要求載入的硬體抽象層模組ID一致。如果不一致,就說明出錯了,函式返回一個錯誤值:-EINVAL
、最後將成功載入後得到的模組控制代碼值
handle
儲存在hw_module_t
結構體指標hmi
的成員變數dso
中,然後將它返回給呼叫者。