硬件抽象層模塊編寫規(guī)范
硬件抽象層最終都會生成.so文件,放到系統(tǒng)對應的目錄中。在系統(tǒng)使用的時候,系統(tǒng)會去對應目錄下加載so文件,實現(xiàn)硬件抽象層的功能。因此硬件抽象層的加載過程就是我們使用so的一個接口。先了解加載過程從源頭了解抽象層模塊兒的編寫規(guī)范。
1、硬件抽象層加載過程
系統(tǒng)在加載so的過程中,會去兩個目錄下查找對應id的so文件。這兩個目錄分別是/system/lib/hw和/vendor/lib/hw。
so文件的名字分為兩個部分例如id.prop.so,第一部分是模塊id。第二部分是系統(tǒng)prop的值,獲取順序為“ro.hardware”、“ro.producat.board”、“ro.board.platform”、“ro.arch”,如果prop都找不到的話,就用default。(不是找不到prop的值,是找不到prop值對應的so文件)。
負責加載硬件抽象層模塊的函數(shù)是hw_get_module,所在的文件是/hardware/libhardware/hardware.c如下:
/** Base path of the hal modules */
#if defined(__LP64__)
#define HAL_LIBRARY_PATH1 "/system/lib64/hw"
#define HAL_LIBRARY_PATH2 "/vendor/lib64/hw"
#else
#define HAL_LIBRARY_PATH1 "/system/lib/hw"
#define HAL_LIBRARY_PATH2 "/vendor/lib/hw"
#endif
/**
?* There are a set of variant filename for modules. The form of the filename
* is ".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"
};
static const int HAL_VARIANT_KEYS_COUNT =
????(sizeof(variant_keys)/sizeof(variant_keys[0]));
/**
?* 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;
????void *handle;
????struct hw_module_t *hmi;
????/*
?????* 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;
}
/*
?* 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_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;
}
int hw_get_module_by_class(const char *class_id, const char *inst,
???????????????????????????const struct hw_module_t **module)
{
????int i;
????char prop[PATH_MAX];
????char path[PATH_MAX];
????char name[PATH_MAX];
????char prop_name[PATH_MAX];
????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);
}
int hw_get_module(const char *id, const struct hw_module_t **module)
{
????return hw_get_module_by_class(id, NULL, module);
}
找到so文件之后,調(diào)用方法load方法去加載對應的so文件,并返回hw_module_t結構體。load方法源碼在上面程序中。
load方法首先調(diào)用dlopen加載對應的so文件到內(nèi)存中。然后用dlsym方法找到變量HAL_MODULE_INFO_SYM_AS_STR符號對應的地址,這個地址也就是一個hw_module_t結構體,然后從這個結構體中拿出id比對load方法出入的id是否一致,如果是的話表示打開成功。加載過程完成。
HAL_MODULE_INFO_SYM_AS_STR這個符號值為HMI,也就是必須要保證這個符號之后是一個hw_module_t。接下來的規(guī)范中有這個要求。
到此,模塊加載完成
2、硬件抽象層模塊編寫規(guī)范
硬件抽象層有兩個結構體,一個是hw_module_t和hw_device_t,定義在hardware.h中。
首先說一下hw_module_t的編寫規(guī)范。
1、必須要有一個“自定義硬件抽象層結構體”,且結構體第一個變量類型要為hw_module_t。
2、必須存在一個HARDWARE_MODULE_INFO_TAG的符號,且指向“自定義硬件抽象層結構體”。在加載的時候根據(jù)這個符號找到地址,并把地址的轉(zhuǎn)變?yōu)閔w_module_t,這也是為什么第一條中hw_module_t必須要在第一個的原因。
3、hw_module_t的tag必須為HARDWARE_MODULE_TAG
4、結構體中要有一個方法列表,其中要有一個open方法。用open方法獲得hw_device_t
接下來說一下hw_device_t的編寫規(guī)范
1、必須要有一個“自定義硬件設備結構體”,且結構體第一個變量類型要為hw_device_t。
2、hw_device_t的tag必須為HARDWARE_DEVICE_TAG
3、要有一個close函數(shù)指針,來關閉設備
按照上面規(guī)范編寫的硬件抽象層就可以由系統(tǒng)加載并正確獲取到device。具體的應用層邏輯在device中實現(xiàn)。