android HAL 硬件抽象層

1. 開發 Android 硬件抽象層

1.1 HAL 層模塊編寫規範

Android 系統的硬件抽象層以模塊的形式來管理各個硬件訪問接口。每一個硬件模塊都對應有一個動態鏈接庫文件。

在系統內部,每一個硬件抽象層模塊都使用結構體 hw_moudle_t 描述,而硬件設備則使用結構體 hw_device_t 來描述。下面分別描述硬件抽象層模塊文件的命令規範以及結構體 hw_moudle_t 和 hw_device_t 的含義。

1.1.1 硬件抽象層模塊編寫規範

硬件抽象層的模塊文件的命令規範定義在 hardware/libhardware/hardware.c 文件中

/**
 * 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"
};
  • 硬件抽象層模塊文件的命令規範:<MOUDLE_ID>.variant.so
    • MOUDLE_ID 表示模塊的ID
    • variant 表示variant_keys四個系統屬性值的其中一個,順序依次
    • variant 如果四個屬性值都不存在,則將 variant 的值設爲 default

variant變量取值過程:

ro.hardware: /prop/cpuinfo 的 Hardware 字段

其他三個在:/system/build.prop 中查找

1.1.2 硬件抽象層模塊結構體定義規範

結構體 hw_device_t 和 hw_moudle_t 及其它相關結構體定義在文件 hardware/hardware.h 中

/**
 * 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;

    uint16_t module_api_version;
    
    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
}
  1. 硬件抽象層中的每一個模塊都必須自定義一個硬件抽象層模塊結構體,而且它的第一個成員變量的類型必須是 hw_moudle_t
  2. 硬件抽象層的每一個模塊都必須存在一個導出符號 HAL_MODULE_INFO_SYM,它指向一個自定義的硬件抽象層模塊結構體。
  3. 結構體 hw_moudle_t 的成員變量 tag 值必須設置爲 HARDWARE_MODULE_TAG,用來標識這是一個硬件抽象層模塊結構體
  4. 結構體 hw_moudle_t 的成員變量 dso 用來保存加載硬件設備層模塊後得到的句柄值。我們知道每一個硬件抽象層模塊都對應有一個動態鏈接庫文件。加載硬件抽象層模塊的過程實際上就是調用 dlopen 函數來加載與其對應的動態鏈接庫文件的過程。在調用 dlclose 函數來卸載這個硬件抽象層模塊時,需要用到這個句柄值。
  5. hw_module_methods_t 變量定義了硬件抽象層模塊的操作方法列表。
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;
  1. 結構體 hw_device_t 用來描述一個已經打開的硬件設備。
typedef struct hw_device_t {
    /** tag must be initialized to HARDWARE_DEVICE_TAG */
    uint32_t tag;

    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;

###################################
(1) 硬件抽象層模塊中的每一個硬件設備必須自定義一個硬件設備結構體
    而且它的第一個成員變量的類型必須爲 hw_device_t
(2) 結構體 hw_device_t 的成員變量 tag 的值必須設置爲 HARDWARE_DEVICE_TAG
    用來標識這是一個硬件抽象層中的硬件設備結構體
(3) close 是一個函數指針,用來關閉一個硬件設備

1.2 編寫硬件抽象層模塊接口

每一個硬件抽象層模塊在內核中都對應一個驅動程序,硬件抽象層模塊就是通過這些驅動程序來訪問硬件設備的,他們通過讀寫設備文件來進行通信

硬件抽象層的模塊接口源文件一般保存在 harware/libhardware 中,以 freg模塊 寄存器操作爲例,進行說明,它的目錄結構如下:

~ Android/hardware/libhardware
---- include
    ----- hardware
        ---- freg.h
        
---- moudles
    ---- power
        ---- freg.c
        ---- Android.mk

其中 freg.h 和 freg.c 是源文件,Android.mk 是模塊的編譯腳本文件

—> frag.h

#ifndef ANDROID_INCLUDE_HARDWARE_FRAG_H
#define ANDROID_INCLUDE_HARDWARE_FRAG_H

#include <hardware/hardware.h>

__BEGIN_DECLS

// 硬件模塊ID
#define FRAG_HARDWARE_MODULE_ID "freg"

// 硬件設備ID
#define FRAG_HARDWARE_DEVICE_ID "freg"

// 自定義硬件模塊結構體
typedef struct freg_moudle_t {
    struct hw_module_t common;
}

// 自定義設備結構體
typedef struct freg_device_t {
    struct hw_device_t common;
    int fd;
    int (*set_val)(struct freg_device_t* dev, int val);
    int (*get_val)(struct freg_device_t* dev, int* val);
}

__END_DECLS

#endif  // ANDROID_INCLUDE_HARDWARE_FRAG_H

這個文件中的常量結構體都是按照硬件抽象層模塊編寫規範來定義的。

  1. 宏 FRAG_HARDWARE_MODULE_ID 描述模塊ID
  2. 宏 FRAG_HARDWARE_DEVICE_ID 描述設備ID
  3. 結構體 freg_moudle_t 描述自定義的模塊結構體
  4. 結構體 freg_device_t 描述虛擬硬件設備,其中 fd 用來描述打開的文件設備 /dev/frag,成員變量 set_val 和 get_val 是函數指針,用來寫和讀虛擬硬件設備freg的寄存器地址。

—> frag.c

...
#define DEVICE_NAME "/dev/frag"
#define DMOUDLE_NAME "frag"
#define MOUDLE_AUTHOR "kevin"

/* 設備打開與關閉接口 */
static int freg_device_open(const struct hw_moudle_t* moudle, const char* id, struct hw_device_t** device);
static int freg_device_open( struct hw_device_t** device);

/* 設備寄存器讀寫接口 */
static int freg_get_val(struct freg_device_t* dev, int* val);
static int freg_set_val(struct freg_device_t* dev, int val);

/* 定義模塊操作方法結構體變量  */
static struct hw_moudle_methonds_t freg_moudle_methods = {
    open: freg_device_open
}

/* 定義模塊結構體變量 */
struct freg_module_t HAL_MODULE_INFO_SYM = {
    .common = {
        .tag = HARDWARE_MODULE_TAG,
        .module_api_version = 1,
        .hal_api_version = 1,
        .id = POWER_HARDWARE_MODULE_ID,
        .name = MOUDLE_NAME,
        .author = MOUDLE_AUTHOR,
        .methods = &freg_moudle_methods,
    },

    .init = power_init,
    .setInteractive = power_set_interactive,
    .powerHint = power_hint,
};
...

在這段代碼中,最值得關注的是模塊變量 HAL_MODULE_INFO_SYM 的定義,按照硬件抽象層模塊編寫規範,每一個硬件抽象層模塊必須導出一個名稱爲 HAL_MODULE_INFO_SYM 的符號,它指向一個自定義的硬件抽象層模塊結構體。

—> Android.mk

1. LOCAL_PATH := $(call my-dir)
2. include $(CLEAR_VARS)
3. LOCAL_MODULE_TAGS := optional
4. LOCAL_PROPRIETARY_MODULE := true
5. LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
6. LOCAL_SHARED_LIBRARIES := liblog
7. LOCAL_SRC_FILES := freg.cpp
8. LOCAL_MODULE := freg.default
9. include $(BUILD_SHARED_LIBRARY)
  1. 這是硬件抽象層的編譯腳本文件,第九行指定將模塊編譯成一個動態鏈接庫文件
  2. LOCAL_MODULE_PATH 指定了庫保存的路徑(8.1: /vendor/lib/hw)
  3. 將硬件抽象層模塊 freg 對應的文件命令爲 freg.default,編譯成功後在指定的位置成功 freg.default.so,當我們要加載 freg 抽象層模塊 freg 時,只需要指定它的ID值,就能找到對應的 so 文件

1.3 硬件抽象層模塊的加載過程

在 Android 硬件抽象層,負責加載硬件抽象模塊的函數時 hw_get_moudle,它的原型如下:

# Android/hardware/libhardware/include/hardware/hardware.h

/**
 * 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);

===================================================
id:輸入參數,表示要加載的硬件抽象層模塊ID
moudle:輸出參數,如果加載成功,它指向一個自定義的硬件抽象層模塊結構體

函數返回值等於0代表加載成功,小於0則表示加載失敗

簡單闡述一下 hw_get_module 的實現過程,代碼在 hardware.c 中

  1. 從 variant_keys 中查找對應註冊的模塊,獲取動態鏈接庫路徑
  2. 調用 dlopen 函數將動態連接庫加載到內存中,成功則返回 handler 句柄
  3. 使用 dlsym 函數獲取名稱爲 HAL_MODULE_INFO_SYM_AS_STR 的符號,這個 HAL_MODULE_INFO_SYM_AS_STR 符號指向一個自定義的抽象層模塊結構體,它包含了模塊的所有信息。
  4. HAL_MODULE_INFO_SYM_AS_STR 自定義抽象層模塊結構體的第一個成員變量爲 hw_moudle_t,將 hw_moudle_t.id 與所要加載的模塊ID做比較,返回結果

2. Android上層訪問硬件抽象層

應用層使用 JNI 訪問硬件模塊接口

因爲上層應用框架使用java語言開發,而硬件抽象層是使用C++語言進行開發,所以必須通過Java本地接口(JNI)來調用硬件抽象層模塊的接口

應用層使用 binder進程間通信機制 實現跨進程通信

Android 系統的硬件訪問服務通常運行在系統進程 System 中,應用程序所在的進程要想訪問硬件訪問服務就需要通過一種進程間通信機制。

Android 提供了一種高效的進程間通信機制 —— Binder進程間通信,應用程序通過它來訪問運行在系統進程System中的硬件訪問服務。因此實現硬件硬件訪問服務之前,首先要定義它的服務接口。

從應用層往下分析,以PoweManager爲例:

1. PoweManager.java

該文件中爲用戶提供了很多關於電源管理方法的操作,這些操作都是通過 Android Binder 進程間通信實現,簡單來說就是通過 PowerManagerService 服務提供的接口來實現的

// mService = IPowerManager.Stub.asInterface("---");
final IPowerManager mService;


public boolean isInteractive() {
    try {
        return mService.isInteractive();
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

===========================================
- 1. mService 是PoweManagerService服務的Binder代理對象
- 2. 使用 IPowerManager.Stub.asInterface 獲取電源管理服務的 Binder 代理對象
- 3. 使用 mService 代理對象進行電源管理相關的操作,所以使用遠程電源管理服務提供的接口
2. IPowerMnager.aidl

該文件定義了電源硬件訪問的服務接口

Android 接口定義語言(AIDL)是 Android 系統提供的一種描述語言來定義具體跨進程訪問能力的服務接口。在編譯時,ADIL文件會被轉換成 java 語言,這個轉換 java 語言中就包含 Binder 本地對象類 Stub,它實現了 IPowerManager 接口。

前面說到服務接口是用來進程間通信的,提供服務的進程稱爲Service進程,而使用服務的進程稱爲 Client 進程(APP),在Service進程中,每一個服務都對應有一個本地 Binder 對象,它通過一個樁(Stub)來等待客戶端連接。Client 客戶端進程在訪問服務之前,首先獲取它的一個 Binder 代理對象(Proxy),然後通過這個代理對象接口訪問服務接口。

3. 應用層 java 服務接口實現文件 —— PowerManagerService.java

前面的 AIDL 文件提供了服務接口,但是還沒有一個具體的地方去實現這些接口,在 PowerManagerService.java 中從 IPowerManager.Stub 類繼承下來,並且實現了這一些接口

private final class BinderService extends IPowerManager.Stub {
    ...
}
4. 實現硬件訪問服務接口 —— com_android_server_power_PowerManagerService.cpp
#include <android/hardware/power/1.1/IPower.h>
#include <hardware/power.h>

// Check validity of current handle to the power HAL service, and call getService() if necessary.
// The caller must be holding gPowerHalMutex.
bool getPowerHal() {
    if (gPowerHalExists && gPowerHalV1_0 == nullptr) {
        gPowerHalV1_0 = android::hardware::power::V1_0::IPower::getService();
        if (gPowerHalV1_0 != nullptr) {
            gPowerHalV1_1 =  android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
            ALOGI("Loaded power HAL service");
        } else {
            ALOGI("Couldn't load power HAL service");
            gPowerHalExists = false;
        }
    }
    return gPowerHalV1_0 != nullptr;
}

int register_android_server_PowerManagerService(JNIEnv* env) {
    int res = jniRegisterNativeMethods(env, "com/android/server/power/PowerManagerService",
            gPowerManagerServiceMethods, NELEM(gPowerManagerServiceMethods));
    (void) res;  // Faked use when LOG_NDEBUG.
    LOG_FATAL_IF(res < 0, "Unable to register native methods.");

    // Callbacks

    jclass clazz;
    FIND_CLASS(clazz, "com/android/server/power/PowerManagerService");

    GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivityFromNative, clazz,
            "userActivityFromNative", "(JII)V");

    // Initialize
    for (int i = 0; i <= USER_ACTIVITY_EVENT_LAST; i++) {
        gLastEventTime[i] = LLONG_MIN;
    }
    gPowerManagerServiceObj = NULL;
    return 0;
}
  • getPowerHal 方法獲取硬件抽象層的 hw_device_t 對象 gPowerHalV1_1
  • 獲取到硬件設備 gPowerHalV1_1之後,使用這個對象訪問硬件
  • register_android_server_PowerManagerService 方法中調用 jniRegisterNativeMethods 方法將所有實現的方法註冊到Java虛擬機中
5. onload.java —— 增加電源管理函數的聲明和調用

register_android_server_PowerManagerService 函數時上一小節中編寫爲了註冊到 Java 虛擬機中去的。

onload.cpp 文件實現在 libandroid_service 模塊中,當系統加載 libandroid_service 模塊時,就會調用 onload.cpp 中的 JNI_Load 函數,添加 native 方法的 Java 虛擬機註冊

extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
{
    ...
    register_android_server_PowerManagerService(env);
    ...
}
6. SystemService.java —— 啓動電源管理硬件訪問服務

前面說到,System 系統進程中會啓動各種硬件訪問服務,可以在該文件中添加電源模塊服務的啓動,這樣系統一啓動,服務就開啓了

private void startOtherServices() {
    ....
    mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
    ....
    
    private void startOtherServices() {
        ...
        try {
            // TODO: use boot phase
            mPowerManagerService.systemReady(mActivityManagerService.getAppOpsService());
        } catch (Throwable e) {
            reportWtf("making Power Manager Service ready", e);
        }
        ...
    }
    
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章