Android HAL 硬件抽象層加載過程

硬件廠商處于保護核心代碼,會將核心實現(xiàn)以so庫的形式出現(xiàn)在HAL層穷当,當需要時HAL會自動調(diào)用相關(guān)的共享庫。

共享庫的格式

<MODULE_ID>.variant.so

  • id: 為硬件模塊的唯一編號
  • variant:為變種名稱矛双。這個值從系統(tǒng)屬性中獲取牛哺。獲取順序保存在variant_keys數(shù)組中。
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"
};

流程
app通過jni調(diào)用hal層hw_get_module函數(shù)獲取硬件模塊篙耗,hw_get_module通過模塊ID查詢對應(yīng)的模塊的共享庫迫筑,調(diào)用load打開共享庫宪赶,獲取硬件結(jié)構(gòu)地址,根據(jù)固定符號HAL_MODULE_INFO_SYM查找結(jié)構(gòu)體hw_module_t脯燃,調(diào)用hw_module_methods_t中的open方法打開硬件搂妻。在open時傳入hw_device_t二級指針,將模塊的操作函數(shù)保存在hw_device_t中辕棚,實現(xiàn)與硬件的交互欲主。

hw_get_module

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;
        }
    }

    //在hal層搜索動態(tài)共享庫的方式
    /* 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); ////裝載庫,得到module
}

//根據(jù)硬件id逝嚎,獲取id對應(yīng)的硬件模塊結(jié)構(gòu)體
int hw_get_module(const char *id, const struct hw_module_t **module)
{   
    //id將作為路徑一部分扁瓢,下面代碼的name
    return hw_get_module_by_class(id, NULL, module);
}
  • hw_get_module 調(diào)用hw_get_module_by_class完成加載過程
  • hw_get_module_by_class根據(jù)傳入的變量class_id,查詢ro.hardware.<id>獲取屬相值,如果存在作為variant值补君,調(diào)用hw_module_exit檢查目標共享庫是否存在引几,存在調(diào)用load加載
  • 不存在,循環(huán)遍歷variant_keys數(shù)組定義的key獲取對應(yīng)的屬性值挽铁,并判斷是否存在對應(yīng)的共享庫伟桅,存在調(diào)用load加載,否則返回錯誤

hw_module_exists方法根據(jù)拼接的路徑path叽掘,去查詢是否存在共享庫楣铁。共享庫的路徑由HAL_LIBRARY_PATH1(系統(tǒng)存放路徑) ,id(moudle ID)更扁,variant(屬性)組成盖腕。

共享庫存放的路徑

#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
  • 共享庫存放的位置位于 /system/lib64/hw,/system/lib/hw 和/vendor/lib64/hw 疯潭,/vendor/lib/hw 路徑下
  • 共享庫以<MODULE_ID>.variant.so命名,id為模塊名稱面殖,variant為變種名稱竖哩,隨系統(tǒng)平臺變化。

load

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
     */
     //調(diào)用dlopen打開path定義的目標共享庫脊僚,得到庫文件的句柄handle
    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;
    //調(diào)用dlsym獲取符號HAL_MODULE_INFO_SYM_AS_STR的地址相叁,賦值給hmi
    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);
    }

    //返回得到的hw_module_t結(jié)構(gòu)體的指針
    *pHmi = hmi;

    return status;
}
  • load 函數(shù)調(diào)用dlopen打開目標模塊共享庫,使用dlsym獲取HMI地址(HMI為模塊hw_module_t結(jié)構(gòu)體的名字)辽幌,得到對應(yīng)模塊hw_module_t指針增淹;通過hw_module_t指針,可以對硬件進行操作乌企。

通過dlopen和dlsym獲取模塊hw_module_t指針虑润,操作硬件。

藍牙HAL加載

硬件抽象層中每個模塊必須自定義一個硬件抽象層模塊結(jié)構(gòu)加酵,第一個成員必須是hw_module_t拳喻,其次才是模塊的一此相關(guān)信息哭当;還必須包含HAL_MODULE_INFO_SYM 。

藍牙HAL加載在 com_android_bluetooth_btservice_AdapterService.cpp的classInitNative方法中冗澈,

static void classInitNative(JNIEnv* env, jclass clazz) {
    int err;
    hw_module_t* module;
    // ........中間省略.....
    char value[PROPERTY_VALUE_MAX];
    property_get("bluetooth.mock_stack", value, "");

    const char *id = (strcmp(value, "1")? BT_STACK_MODULE_ID : BT_STACK_TEST_MODULE_ID);

    //獲取藍牙模塊hw_module_t指針
    err = hw_get_module(id, (hw_module_t const**)&module);

    if (err == 0) {
        hw_device_t* abstraction;
    //調(diào)用open方法钦勘,獲取藍牙設(shè)備hw_device_t指針
        err = module->methods->open(module, id, &abstraction);
        if (err == 0) {
            //藍牙實現(xiàn)中將藍牙設(shè)備bluetooth_device_t 和 bluetooth_module_t 定義成同一個值
            bluetooth_module_t* btStack = (bluetooth_module_t *)abstraction;
      //獲取藍牙模塊interface接口,通過sBluetoothInterface操作藍牙設(shè)備
            sBluetoothInterface = btStack->get_bluetooth_interface();
        } else {
           ALOGE("Error while opening Bluetooth library");
        }
    } else {
        ALOGE("No Bluetooth Library found");
    }
}

BT_STACK_MODULE_ID 定義在bluetooth.h文件中亚亲,

 #define BT_HARDWARE_MODULE_ID "bluetooth"
#define BT_STACK_MODULE_ID "bluetooth"

hw_get_module獲取藍牙模塊hw_module_t指針彻采,
藍牙自定義硬件模塊hw_module_t定義

struct hw_module_t HAL_MODULE_INFO_SYM = {
    .tag = HARDWARE_MODULE_TAG,
    .version_major = 1,
    .version_minor = 0,
    .id = BT_HARDWARE_MODULE_ID,
    .name = "Bluetooth Stack",
    .author = "The Android Open Source Project",
    .methods = &bt_stack_module_methods
};

藍牙調(diào)用自定義模塊hw_module_t中open方法,獲取hw_device_t指針捌归,

static struct hw_module_methods_t bt_stack_module_methods = {
    .open = open_bluetooth_stack,
};

static int open_bluetooth_stack(const struct hw_module_t *module, UNUSED_ATTR char const *name, struct hw_device_t **abstraction) {
  //給bluetooth_device_t賦值
  static bluetooth_device_t device = {
    //common變量賦值
    .common = {     
      .tag = HARDWARE_DEVICE_TAG,
      .version = 0,
      .close = close_bluetooth_stack,
    },
    //獲取藍牙模塊接口
    .get_bluetooth_interface = bluetooth__get_bluetooth_interface
  };

  device.common.module = (struct hw_module_t *)module;
  //將bluetooth_device_t指針強制轉(zhuǎn)換為hw_device_t指針賦值給abstraction
  *abstraction = (struct hw_device_t *)&device;
  return 0;
}

open_bluetooth_stack創(chuàng)建bluetooth_device_t 結(jié)構(gòu)體肛响,初始化,轉(zhuǎn)換成hw_device_t陨溅。module->methods->open得到bluetooth_device_t 結(jié)構(gòu)體的指針终惑,也是bluetooth_module_t結(jié)構(gòu)體的指針。

typedef struct {
    struct hw_device_t common;
    const bt_interface_t* (*get_bluetooth_interface)();
} bluetooth_device_t;

typedef bluetooth_device_t bluetooth_module_t;

bluetooth_device_t 包含hw_device_t门扇;bluetooth_device_t 重命名為bluetooth_module_t雹有。

open方法(open_bluetooth_stack)獲取藍牙設(shè)備hw_device_t指針,轉(zhuǎn)化為bluetooth_module_t 指針臼寄,在open_bluetooth_stack中將bluetooth_get_bluetooth_interface賦值給get_bluetooth_interface霸奕,再賦值給sBluetoothInterface。

bluetooth_get_bluetooth_interface定義了藍牙模塊的基本接口吉拳,bluetooth_get_bluetooth_interface最后賦值給sBluetoothInterface质帅,調(diào)用sBluetoothInterface可以操作藍牙模塊,與硬件交互留攒。具體接口實現(xiàn)和平臺相關(guān)煤惩。

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市炼邀,隨后出現(xiàn)的幾起案子魄揉,更是在濱河造成了極大的恐慌,老刑警劉巖拭宁,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件洛退,死亡現(xiàn)場離奇詭異,居然都是意外死亡杰标,警方通過查閱死者的電腦和手機兵怯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來腔剂,“玉大人媒区,你說我怎么就攤上這事。” “怎么了驻仅?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵谅畅,是天一觀的道長。 經(jīng)常有香客問我噪服,道長毡泻,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任粘优,我火速辦了婚禮仇味,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘雹顺。我一直安慰自己丹墨,他們只是感情好,可當我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布嬉愧。 她就那樣靜靜地躺著贩挣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪没酣。 梳的紋絲不亂的頭發(fā)上王财,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天,我揣著相機與錄音裕便,去河邊找鬼绒净。 笑死,一個胖子當著我的面吹牛偿衰,可吹牛的內(nèi)容都是我干的挂疆。 我是一名探鬼主播,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼下翎,長吁一口氣:“原來是場噩夢啊……” “哼佛点!你這毒婦竟也來了双揪?” 一聲冷哼從身側(cè)響起恕沫,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤挑宠,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后郑口,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鸳碧,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡盾鳞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年犬性,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片腾仅。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡乒裆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出推励,到底是詐尸還是另有隱情鹤耍,我是刑警寧澤肉迫,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站稿黄,受9級特大地震影響喊衫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜杆怕,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一族购、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧陵珍,春花似錦寝杖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至留潦,卻和暖如春只盹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背愤兵。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工鹿霸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人秆乳。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓懦鼠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親屹堰。 傳聞我的和親對象是個殘疾皇子肛冶,可洞房花燭夜當晚...
    茶點故事閱讀 45,870評論 2 361

推薦閱讀更多精彩內(nèi)容