Android源碼分析二 硬件抽象層(HAL)

一 什麼是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硬件抽象層(HAL)概要介紹和學習計劃

 

Android之 看「馬達」如何貫通Android系統 (從硬件設計 --> 驅動 --> HAL --> JNI --> Framework --> Application)

Android應用層到Framework到HAL再到驅動層的整個流程分析

相關文章
相關標籤/搜索