一 什麼是HAL
HAL 可定義一個標準接口以供硬件供應商實現,這可以讓 Android 忽略較低級別的驅動程序實現。藉助 HAL,您能夠順利實現相關功能,而不會影響或更改更高級別的系統。HAL 實現會被封裝成模塊,並由 Android 系統適時地加載。html
硬件抽象層是介於android內核kernel和上層之間的抽象出來的一層結構。他是對linux驅動的一個封裝,對上層提供統一接口,上層應用沒必要知道下層硬件具體怎麼實現工做的,它屏蔽了底層的實現細節。linux
您必須爲您的產品所提供的特定硬件實現相應的 HAL(和驅動程序)。HAL 實現一般會內置在共享庫模塊(.so
文件)中,但 Android 並不要求 HAL 實現與設備驅動程序之間進行標準交互,所以您能夠視狀況採起適當的作法。不過,要使 Android 系統可以與您的硬件正確互動,您必須遵照各個特定於硬件的 HAL 接口中定義的合同。android
爲了保證 HAL 具備可預測的結構,每一個特定於硬件的 HAL 接口都要具備 hardware/libhardware/include/hardware/hardware.h
中定義的屬性。這類接口可以讓 Android 系統以一致的方式加載 HAL 模塊的正確版本。HAL 接口包含兩個組件:模塊和設備。express
1.1 HAL 模塊
模塊表示被封裝且存儲爲共享庫 (.so file
) 的 HAL 實現。hardware/libhardware/include/hardware/hardware.h
標頭文件會定義一個表示模塊的結構體 (hw_module_t
),其中包含模塊的版本、名稱和做者等元數據。Android 會根據這些元數據來找到並正確加載 HAL 模塊。apache
/** * 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; //tag,根據引文註釋能夠看到必須被初始化爲HARDWARE_MODULE_TAG
/** major version number for the module */ uint16_t version_major;//主版本號
/** minor version number of the module */ uint16_t version_minor;//次版本號
/** Identifier of module */
const char *id;//模塊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;//打開硬件模塊的庫時獲得的句柄
/** padding to 128 bytes, reserved for future use */ uint32_t reserved[32-7]; } hw_module_t;
另外,hw_module_t
結構體還包含指向另外一個結構體 hw_module_methods_t
的指針,後面這個結構體會包含一個指向相應模塊的 open 函數的指針。此 open 函數用於與相關硬件(此 HAL 是其抽象形式)創建通訊。api
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_device_t,這個結構體主要是用來描述模塊中硬件設備的屬性信息什麼的。一個硬件模塊可能有多個硬件設備。數組
好比說,傳感器模塊,sensor_module,是一個硬件模塊,可是手機中的傳感器就對應的有好多種,好比加速度acc_sensor,磁傳感器M_sensor等,那麼他們都屬於sensor_module,可是他們有都有本身的hw_device_t結構體來描述。hw_device_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; //設備tag
/** version number for hw_device_t */ uint32_t version;//版本
/** reference to the module this device belongs to */
struct hw_module_t* module;//本設備歸屬的硬件模塊
/** padding reserved for future use */ uint32_t reserved[12];//保留
/** Close this device */
int (*close)(struct hw_device_t* device);//關閉設備的函數指針
} hw_device_t;
第三個成員module指向的是這個設備歸屬的硬件模塊結構體。app
每一個特定於硬件的 HAL 一般都會使用附加信息爲該特定硬件擴展通用的 hw_module_t
結構體。例如,在相機 HAL 中,camera_module_t
結構體會包含一個 hw_module_t
結構體以及其餘特定於相機的函數指針:框架
typedef struct camera_module { hw_module_t common; int (*get_number_of_cameras)(void); int (*get_camera_info)(int camera_id, struct camera_info *info); } camera_module_t;
實現 HAL 並建立模塊結構體時,您必須將其命名爲 HAL_MODULE_INFO_SYM
。如下是 Nexus 9 音頻 HAL 的示例:
struct audio_module HAL_MODULE_INFO_SYM = { .common = { .tag = HARDWARE_MODULE_TAG, .module_api_version = AUDIO_MODULE_API_VERSION_0_1, .hal_api_version = HARDWARE_HAL_API_VERSION, .id = AUDIO_HARDWARE_MODULE_ID, .name = "NVIDIA Tegra Audio HAL", .author = "The Android Open Source Project", .methods = &hal_module_methods, }, };
1.2 HAL 設備
設備是產品硬件的抽象表示。例如,一個音頻模塊可能包含主音頻設備、USB 音頻設備或藍牙 A2DP 音頻設備。
設備由 hw_device_t
結構體表示。與模塊相似,每類設備都定義了一個通用 hw_device_t
的詳細版本,其中包含指向硬件特定功能的函數指針。例如,audio_hw_device_t
結構體類型會包含指向音頻設備操做的函數指針:
struct audio_hw_device { struct hw_device_t common; /** * used by audio flinger to enumerate what devices are supported by * each audio_hw_device implementation. * * Return value is a bitmask of 1 or more values of audio_devices_t */ uint32_t (*get_supported_devices)(const struct audio_hw_device *dev); ... }; typedef struct audio_hw_device audio_hw_device_t;
除了這些標準屬性以外,每一個特定於硬件的 HAL 接口均可以定義更多的自有功能和要求。有關詳情,請參閱 HAL 參考文檔以及各 HAL 的單獨說明。
1.3 編譯 HAL 模塊
HAL 實現會內置在模塊 (.so
) 文件中,並由 Android 適時地動態連接。您能夠爲每一個 HAL 實現建立 Android.mk
文件並指向源文件,從而編譯模塊。通常來講,您的共享庫必須以特定格式命名,以方便找到並正確加載。各模塊的命名方案略有不一樣,但它們都遵循如下通用模式:<module_type>.<device_name>
。
1.4 HAL 類型
爲了更好地實現模塊化,Android 8.0 對 Android 操做系統底層進行了從新架構。做爲此變化的一部分,運行 Android 8.0 的設備必須支持綁定式或直通式 HAL:
- 綁定式 HAL。以 HAL 接口定義語言 (HIDL) 表示的 HAL。這些 HAL 取代了早期 Android 版本中使用的傳統 HAL 和舊版 HAL。在綁定式 HAL 中,Android 框架和 HAL 之間經過 Binder 進程間通訊 (IPC) 調用進行通訊。全部在推出時即搭載了 Android 8.0 或後續版本的設備都必須只支持綁定式 HAL。
- 直通式 HAL。以 HIDL 封裝的傳統 HAL 或舊版 HAL。這些 HAL 封裝了現有的 HAL,可在綁定模式和 Same-Process(直通)模式下使用。升級到 Android 8.0 的設備可使用直通式 HAL。
1.4.1 HAL 模式要求
設備 |
直通式 |
綁定式 |
搭載 Android 8.0 的設備 |
直通式 HAL 中列出的 HAL 必須爲直通式。 |
全部其餘 HAL 均爲綁定式(包括做爲供應商擴展程序的 HAL)。 |
升級到 Android 8.0 的設備 |
直通式 HAL 中列出的 HAL 必須爲直通式。 |
綁定式 HAL 中列出的 HAL 必須爲綁定式。 |
供應商映像提供的全部其餘 HAL 既能夠在直通模式下使用,也能夠在綁定模式下使用。 |
1.4.2 綁定式 HAL
Android 要求全部 Android 設備(不管是搭載 Android O 的設備仍是升級到 Android O 的設備)上的下列 HAL 均爲綁定式:
android.hardware.biometrics.fingerprint@2.1
。取代 Android 8.0 中已不存在的 fingerprintd
。
android.hardware.configstore@1.0
。Android 8.0 中的新 HAL。
android.hardware.dumpstate@1.0
。此 HAL 提供的原始接口可能沒法繼續使用,而且已更改。所以,dumpstate_board
必須在指定的設備上從新實現(這是一個可選的 HAL)。
android.hardware.graphics.allocator@2.0
。在 Android 8.0 中,此 HAL 必須爲綁定式,所以無需在可信進程和不可信進程之間分享文件描述符。
android.hardware.radio@1.0
。取代由存活於自身進程中的 rild
提供的接口。
android.hardware.usb@1.0
。Android 8.0 中的新 HAL。
android.hardware.wifi@1.0
。Android 8.0 中的新 HAL,可取代此前加載到 system_server
的舊版 WLAN HAL 庫。
android.hardware.wifi.supplicant@1.0
。在現有 wpa_supplicant
進程之上的 HIDL 接口
注意:Android 提供的如下 HIDL 接口將一概在綁定模式下使用:android.frameworks.*
、android.system.*
和 android.hidl.*
(不包括下文所述的 android.hidl.memory@1.0
)。
1.4.3 直通式 HAL
Android 要求全部 Android 設備(不管是搭載 Android O 的設備仍是升級到 Android O 的設備)上的下列 HAL 均在直通模式下使用:
android.hardware.graphics.mapper@1.0
。將內存映射到其所屬的進程中。
android.hardware.renderscript@1.0
。在同一進程中傳遞項(等同於 openGL
)。
上方未列出的全部 HAL 在搭載 Android O 的設備上都必須爲綁定式。
Same-Process HAL
Same-Process HAL (SP-HAL) 一概在使用它們的進程中打開,其中包括未以 HIDL 表示的全部 HAL,以及那些非綁定式的 HAL。SP-HAL 集的成員只能由 Google 控制,這一點沒有例外。
SP-HAL 包括如下 HAL:
openGL
Vulkan
android.hidl.memory@1.0
(由 Android 系統提供,一概爲直通式)
android.hardware.graphics.mapper@1.0
。
android.hardware.renderscript@1.0
1.5 傳統 HAL 和舊版 HAL
傳統 HAL(在 Android 8.0 中已棄用)是指與具備特定名稱及版本號的應用二進制接口 (ABI) 標準相符的接口。大部分 Android 系統接口(相機、音頻和傳感器等)都採用傳統 HAL 形式(已在 hardware/libhardware/include/hardware 下進行定義)。
舊版 HAL(也已在 Android 8.0 中棄用)是指早於傳統 HAL 的接口。一些重要的子系統(WLAN、無線接口層和藍牙)採用的就是舊版 HAL。雖然沒有統一或標準化的方式來指明是否爲舊版 HAL,但若是 HAL 早於 Android 8.0 而出現,那麼這種 HAL 若是不是傳統 HAL,就是舊版 HAL。有些舊版 HAL 的一部分包含在 libhardware_legacy 中,而其餘部分則分散在整個代碼庫中。
2、操做硬件
HAL層的主要的兩個結構體hw_module_t(硬件模塊)和hw_device_t(硬件設備)的成員,下面咱們來具體看看上層app究竟是怎麼實現操做硬件的?
一些硬件廠商不肯意將本身的一些核心代碼開放出去,因此將這些代碼放到HAL層,可是怎麼保證它不開放呢?HAL層代碼不是也讓你們知道下載嗎?其實硬件廠商的HAL核心代碼是以共享庫的形式出現的,每次在須要的時候,hal會自動加載調用相關共享庫。那麼是怎麼加載找到某一硬件設備對應的共享庫的呢?這也是咱們這篇都要說的。
上層app經過jni調用hal層的hw_get_module函數獲取硬件模塊,這個函數是上層與hal打交道的入口。因此若是咱們以程序調用執行的流程去看源碼的話,這個函數就是hal層第一個被調用的函數,下面咱們就從這個函數開始,沿着程序執行的流程走下去。
hw_get_module函數定義在/hardware/libhardware/hardware.c中,打開這個文件能夠看到定義以下:
/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <hardware/hardware.h> #include <cutils/properties.h> #include <dlfcn.h> #include <string.h> #include <pthread.h> #include <errno.h> #include <limits.h>
#define LOG_TAG "HAL" #include <utils/Log.h>
/** 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 "<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"
}; 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 = -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; } /* * 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 = 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); } int hw_get_module(const char *id, const struct hw_module_t **module) { return hw_get_module_by_class(id, NULL, module); }
在dlopen的()函數以指定模式打開指定的動態鏈接庫文件,並返回一個句柄給調用進程。使用dlclose()來卸載打開的庫。
hw_get_module
int hw_get_module(const char *id, const struct hw_module_t **module) { int status; int i; const struct hw_module_t *hmi = NULL; char prop[PATH_MAX]; char path[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. */
/* Loop through the configuration variants looking for a module */
for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) { if (i < HAL_VARIANT_KEYS_COUNT) { if (property_get(variant_keys[i], prop, NULL) == 0) {//獲取屬性
continue; } snprintf(path, sizeof(path), "%s/%s.%s.so", HAL_LIBRARY_PATH1, id, prop); if (access(path, R_OK) == 0) break;//檢查system路徑是否有庫文件
snprintf(path, sizeof(path), "%s/%s.%s.so", HAL_LIBRARY_PATH2, id, prop); if (access(path, R_OK) == 0) break;//檢查vender路徑是否有庫文件
} else { snprintf(path, sizeof(path), "%s/%s.default.so",//若是都沒有,則使用缺省的
HAL_LIBRARY_PATH1, id); if (access(path, R_OK) == 0) break; } } status = -ENOENT; if (i < HAL_VARIANT_KEYS_COUNT+1) { /* load the module, if this fails, we're doomed, and we should not try * to load a different variant. */ status = load(id, path, module);//裝載庫,獲得module
} return status; }
snprintf(),函數原型爲int snprintf(char *str, size_t size, const char *format, ...)。將可變參數 「…」 按照format的格式格式化爲字符串,而後再將其拷貝至str中。
access(filename, 0) 表示判斷文件是否存在 access 返回值是0的時候,表示存在,而返回-1的時候,表示失敗。
property_set/property_get位於libcutils.so庫。任何進程若要調用這兩個函數,須要連接libcutils.so(https://blog.csdn.net/decisiveness/article/details/49852295)。
看第一行咱們知道有兩個參數,第一參數id就是要獲取的硬件模塊的id,第二個參數module就是咱們想獲得的硬件模塊結構體的指針。因此能夠看出,上層首先給hal須要獲取的硬件模塊的id,hw_get_module函數根據這個id去查找匹配和這個id對應的硬件模塊結構體的。
下面看看怎麼找的:
17行有個for循環,上限是HAL_VARIANT_KEYS_COUNT+1,那麼這個HAL_VARIANT_KEYS_COUNT是什麼呢?查看同文件下找到有:
static const int HAL_VARIANT_KEYS_COUNT = (sizeof(variant_keys)/sizeof(variant_keys[0]));
原來它是ariant_keys這個數組的元素個數。那麼這個數組又是什麼呢?在本文件找,有:
/** * 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" };
能夠看到它實際上是個字符串數組。暫且不知道幹什麼的。繼續看hw_get_module函數,進入for循環裏面,看22行,其實它是將HAL_LIBRARY_PATH1, id, prop這三個串拼湊一個路徑出來,
HAL_LIBRARY_PATH1定義以下:
/** Base path of the hal modules */
#define HAL_LIBRARY_PATH1 "/system/lib/hw"
#define HAL_LIBRARY_PATH2 "/vendor/lib/hw"
id是上層提供的,prop這個變量的值是前面19行property_get(variant_keys[i], prop, NULL)函數獲取到的,其實這個函數是經過ariant_keys數組的的屬性查找到系統中對應的變種名稱。不一樣的平臺獲取到prop值是不同的。
假如在獲取到的prop值是tout,須要獲取的硬件模塊的id是leds,那麼最後path組成的串是/system/lib/hw/leds.tout.so。後面24行access是檢查這個路徑下是否存在,若是有就break,跳出循環。若是沒有,繼續走下面,
能夠看到下面幾行和剛纔形式差很少,
snprintf(path, sizeof(path), "%s/%s.%s.so", HAL_LIBRARY_PATH2, id, prop);
if (access(path, R_OK) == 0) break;//檢查vender路徑是否有庫文件
結合 HAL_LIBRARY_PATH2 爲"/vendor/lib/hw",假設一樣獲取到的prop值是tout,須要獲取的硬件模塊的id是leds,這種狀況下path拼出來的值是/vender/lib/hw/leds.tout.so,而後在判斷文件是否存在。若是存在跳出循環。
從以上分析,其實這就是hal層搜索動態共享庫的方式,從中咱們能夠獲得兩點:
- 1.動態共享庫通常放在 "/system/lib/hw"和"/vendor/lib/hw"這兩個路徑下。
- 2.動態庫的名稱是以"id.variant.so"的形式命名的,其中id爲上層提供,中間variant爲變種名稱,是隨系統平臺變化的。
接着,從29到32行咱們能夠看到,當全部變種名稱形式的包都不存在時,就以"id.default.so"形式包名查找是否存在。
37行, if (i < HAL_VARIANT_KEYS_COUNT+1),若是i小於變種名稱數組的話,表示找到了對應的庫,那麼38行load(id, path, module);//裝載庫,獲得module。
三 如何實現加載共享庫
如下爲load函數定義,一樣在/hardware/libhardware/hardware.c中實現的。
/** * 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) {//傳入硬件模塊id和庫所在路徑,獲取到硬件模塊結構體
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(); LOGE("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) { LOGE("load: couldn't find symbol %s", sym); status = -EINVAL; goto done; } /* Check that the id matches */
if (strcmp(id, hmi->id) != 0) {//匹配解析出硬件模塊的id和傳入咱們實際想要獲得的模塊id是否一致
LOGE("load: id=%s != hmi->id=%s", id, hmi->id); status = -EINVAL; goto done; } hmi->dso = handle; //將打開庫獲得句柄傳給硬件模塊的dso
/* success */ status = 0; done: if (status != 0) { hmi = NULL; if (handle != NULL) { dlclose(handle); handle = NULL; } } else { LOGV("loaded HAL id=%s path=%s hmi=%p handle=%p", id, path, *pHmi, handle); } *pHmi = hmi;//將獲得的module的結果經過第三個參數傳給hw_module_t
return status; }
Linux提供了一套API來動態裝載庫。下面列出了這些API:
- dlopen,打開一個庫,併爲使用該庫作些準備。
- dlsym,在打開的庫中查找符號的值。
- dlclose,關閉庫。
- dlerror,返回一個描述最後一次調用dlopen、dlsym,或dlclose的錯誤信息的字符串。
C語言用戶須要包含頭文件dlfcn.h才能使用上述API。glibc還增長了兩個POSIX標準中沒有的API:
- dladdr,從函數指針解析符號名稱和所在的文件。
- dlvsym,與dlsym相似,只是多了一個版本字符串參數。
能夠看到load函數傳入的幾個參數:
第一個參數就是須要加載的硬件模塊對應動態庫的硬件模塊的id,
第二個參數就是動態庫存放的路徑,就是在hw_get_module函數前部分搜索庫獲得的path,
第三個參數就是咱們須要獲得的硬件模塊結構體,經過它傳給hw_get_module,hw_get_module函數在經過參數傳給jni。
第19行,首先調用dlopen打開共享庫,該函數經過傳入的庫的路徑找到庫,而且打開它,傳回一個操做句柄handle,而後再調用dlsym函數解析這個打開的庫,下面第29行,獲得庫中包含的硬件模塊結構體,並將它返回回來。因此硬件廠商或者硬件移植者都必須根據hal的這個架構去實現填充這個和本身硬件相關的硬件模塊結構體hw_module_t,供使用。
經過dlsym解析以後就獲得了hw_module_t,隨後第37行,將從庫中解析獲得的結構體中的id和傳入的id作比較,看是否一致。若是一致則證實就是獲得正確的硬件模塊了。最後第60行,將hw_module_t結構體指針傳給第三個參數,傳給hw_get_module函數。
到此,hw_get_module函數就獲得了硬件模塊結構體hw_module_t.有了hw_module_t,那麼經過其內部的method open就能打開硬件模塊對應的設備了,經過結構體中的一些方法就能操做硬件設備了。
四 實踐
見番外篇
參考:
https://www.cnblogs.com/y041039/archive/2013/05/22/3092774.html
https://www.cnblogs.com/y041039/archive/2013/05/22/3092868.html
https://www.cnblogs.com/y041039/archive/2013/05/23/3094579.html
https://www.cnblogs.com/y041039/archive/2013/05/23/3094928.html
https://www.cnblogs.com/y041039/archive/2013/05/23/3095621.html
https://www.linuxidc.com/Linux/2011-07/38983.htm
進一步瞭解 Linux內核 -> HAL -> JNI -> framework -> app:
別人的實例
Android之 看「馬達」如何貫通Android系統 (從硬件設計 --> 驅動 --> HAL --> JNI --> Framework --> Application)