国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

HAL 詳解之 hardware 詳解

2019-11-09 15:04:26
字體:
來源:轉載
供稿:網友

文章出處:http://blog.csdn.net/shift_wwx/article/details/54969612

請轉載的朋友標明出處~~

《Android 系統HAL 簡介》一文和《Android,在爭議中逃離 linux 內核的 GPL 約束》中對HAL 做了簡單的簡介,這一文中對HAL 進行詳細的分析。

android HAL 主要框架來源于:

/hardware/libhardware/hardware.c

/hardware/libhardware/include/hardware/hardware.h

本文主要對這兩部分分析:

hardware.hhardware.c一、hardware.h1、hw_module_t
/** * 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;    /**     * The API version of the implemented module. The module owner is     * responsible for updating the version when a module interface has     * changed.     *     * The derived modules such as gralloc and audio own and manage this field.     * The module user must interPRet the version field to decide whether or     * not to inter-Operate with the supplied module implementation.     * For example, SurfaceFlinger is responsible for making sure that     * it knows how to manage different versions of the gralloc-module API,     * and AudioFlinger must know how to do the same for audio-module API.     *     * The module API version should include a major and a minor component.     * For example, version 1.0 could be represented as 0x0100. This format     * implies that versions 0x0100-0x01ff are all API-compatible.     *     * In the future, libhardware will expose a hw_get_module_version()     * (or equivalent) function that will take minimum/maximum supported     * versions as arguments and would be able to reject modules with     * versions outside of the supplied range.     */    uint16_t module_api_version;#define version_major module_api_version    /**     * version_major/version_minor defines are supplied here for temporary     * source code compatibility. They will be removed in the next version.     * ALL clients must convert to the new version format.     */    /**     * The API version of the HAL module interface. This is meant to     * version the hw_module_t, hw_module_methods_t, and hw_device_t     * structures and definitions.     *     * The HAL interface owns this field. Module users/implementations     * must NOT rely on this value for version information.     *     * Presently, 0 is the only valid value.     */    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} hw_module_t;首先來看結構體的解釋:每一個硬件模塊必須有一個命名為HAL_MODULE_INFO_SYM的數據結構體。這個結構體成員必須以hw_module_t 開頭。這個命名在頭文件中有定義:
#define HAL_MODULE_INFO_SYM         HMI例如hdmi cec:
typedef struct hdmi_cec_module {    /**     * Common methods of the HDMI CEC module.  This *must* be the first member of     * hdmi_cec_module as users of this structure will cast a hw_module_t to hdmi_cec_module     * pointer in contexts where it's known the hw_module_t references a hdmi_cec_module.     */    struct hw_module_t common;} hdmi_module_t;hdmi cec 就定義了一個自己的結構體hdmi_module_t,而開頭也必須是hw_module_t下面來詳細解釋hw_module_t 的數據成員:1)tag這個tag 必須初始化為 HARDWARE_MODULE_TAG2)module_api_version標記硬件模塊的接口api 版本,當模塊的接口變化的時候由module 自身更新這個值。例如hdmi cec 就自己定義了一個版本:
#define HDMI_CEC_MODULE_API_VERSION_1_0 HARDWARE_MODULE_API_VERSION(1, 0)3)hal_api_version標記HAL api 的版本,主要標記hw_module_t,hw_module_methods_t,hw_device_t 等結構體的版本目前都用宏HARDWARE_HAL_API_VERSION表示:
#define HARDWARE_HAL_API_VERSION HARDWARE_MAKE_API_VERSION(1, 0)4)id硬件模塊的標記,查找module 的時候就是通過這個查找,很關鍵例如hdmi cec:
#define HDMI_CEC_HARDWARE_MODULE_ID "hdmi_cec"5)name硬件模塊的名字,與id 不同,id 原意是Identifier,也就是辨認都是通過id6)authorauthor/owner/implementor 的標記7)methods變量是結構體指針 hw_module_methods_t*,里面是一個open 函數,device 的open 就靠這個methods8)dso在hardware.c中會解釋,dlopen so 時候的handle2、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;hw_module_t 的成員,放的是函數指針,成員是open,也就是硬件模塊自身要有這樣的open 函數,用于實現硬件模塊對相應的設備進行初始化3、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;    /**     * Version of the module-specific device API. This value is used by     * the derived-module user to manage different device implementations.     *     * The module user is responsible for checking the module_api_version     * and device version fields to ensure that the user is capable of     * communicating with the specific module implementation.     *     * One module can support multiple devices with different versions. This     * can be useful when a device interface changes in an incompatible way     * but it is still necessary to support older implementations at the same     * time. One such example is the Camera 2.0 API.     *     * This field is interpreted by the module user and is ignored by the     * HAL interface itself.     */    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;上面hw_module_t 的時候說到,每個硬件模塊有個名字為HAL_MODULE_INFO_SYM 的數據結構,而且成員必須是hw_module_t 開頭。這里一樣,在硬件模塊定義自身設備結構的時候,數據成員必須以hw_device_t 開頭。例如hdmi cec,在定義自身設備成員的時候就是以這個開頭:
typedef struct hdmi_cec_device {    /**     * Common methods of the HDMI CEC device.  This *must* be the first member of     * hdmi_cec_device as users of this structure will cast a hw_device_t to hdmi_cec_device     * pointer in contexts where it's known the hw_device_t references a hdmi_cec_device.     */    struct hw_device_t common;    /*     * (*add_logical_address)() passes the logical address that will be used     * in this system.     *     * HAL may use it to configure the hardware so that the CEC commands addressed     * the given logical address can be filtered in. This method can be called     * as many times as necessary in order to support multiple logical devices.     * addr should be in the range of valid logical addresses for the call     * to succeed.     *     * Returns 0 on success or -errno on error.     */    int (*add_logical_address)(const struct hdmi_cec_device* dev, cec_logical_address_t addr);    /*     * (*clear_logical_address)() tells HAL to reset all the logical addresses.     *     * It is used when the system doesn't need to process CEC command any more,     * hence to tell HAL to stop receiving commands from the CEC bus, and change     * the state back to the beginning.     */    void (*clear_logical_address)(const struct hdmi_cec_device* dev);    /*     * (*get_physical_address)() returns the CEC physical address. The     * address is written to addr.     *     * The physical address depends on the topology of the network formed     * by connected HDMI devices. It is therefore likely to change if the cable     * is plugged off and on again. It is advised to call get_physical_address     * to get the updated address when hot plug event takes place.     *     * Returns 0 on success or -errno on error.     */    int (*get_physical_address)(const struct hdmi_cec_device* dev, uint16_t* addr);    /*     * (*send_message)() transmits HDMI-CEC message to other HDMI device.     *     * The method should be designed to return in a certain amount of time not     * hanging forever, which can happen if CEC signal line is pulled low for     * some reason. HAL implementation should take the situation into account     * so as not to wait forever for the message to get sent out.     *     * It should try retransmission at least once as specified in the standard.     *     * Returns error code. See HDMI_RESULT_SUCCESS, HDMI_RESULT_NACK, and     * HDMI_RESULT_BUSY.     */    int (*send_message)(const struct hdmi_cec_device* dev, const cec_message_t*);    /*     * (*register_event_callback)() registers a callback that HDMI-CEC HAL     * can later use for incoming CEC messages or internal HDMI events.     * When calling from C++, use the argument arg to pass the calling object.     * It will be passed back when the callback is invoked so that the context     * can be retrieved.     */    void (*register_event_callback)(const struct hdmi_cec_device* dev,            event_callback_t callback, void* arg);    /*     * (*get_version)() returns the CEC version supported by underlying hardware.     */    void (*get_version)(const struct hdmi_cec_device* dev, int* version);    /*     * (*get_vendor_id)() returns the identifier of the vendor. It is     * the 24-bit unique company ID obtained from the IEEE Registration     * Authority Committee (RAC).     */    void (*get_vendor_id)(const struct hdmi_cec_device* dev, uint32_t* vendor_id);    /*     * (*get_port_info)() returns the hdmi port information of underlying hardware.     * info is the list of HDMI port information, and 'total' is the number of     * HDMI ports in the system.     */    void (*get_port_info)(const struct hdmi_cec_device* dev,            struct hdmi_port_info* list[], int* total);    /*     * (*set_option)() passes flags controlling the way HDMI-CEC service works down     * to HAL implementation. Those flags will be used in case the feature needs     * update in HAL itself, firmware or microcontroller.     */    void (*set_option)(const struct hdmi_cec_device* dev, int flag, int value);    /*     * (*set_audio_return_channel)() configures ARC circuit in the hardware logic     * to start or stop the feature. Flag can be either 1 to start the feature     * or 0 to stop it.     *     * Returns 0 on success or -errno on error.     */    void (*set_audio_return_channel)(const struct hdmi_cec_device* dev, int port_id, int flag);    /*     * (*is_connected)() returns the connection status of the specified port.     * Returns HDMI_CONNECTED if a device is connected, otherwise HDMI_NOT_CONNECTED.     * The HAL should watch for +5V power signal to determine the status.     */    int (*is_connected)(const struct hdmi_cec_device* dev, int port_id);    /* Reserved for future use to maximum 16 functions. Must be NULL. */    void* reserved[16 - 11];} hdmi_cec_device_t;下面來詳細解釋hw_device_t 的數據成員:1)tag必須初始化為HARDWARE_DEVICE_TAG2)version

硬件設備接口的版本

3)module

標記該設備屬于哪個硬件模塊,例如audio 可能會有多種device

4)reserved

暫時沒用到

5)close

函數指針,也就是說需要實現close 用于后面device 設備close 回收

4、兩個對外接口

hw_get_module、hw_get_module_by_class

下面hardware.c 中詳細說明

小結:android HAL 在創建的時候,需要實現四個部分:

1、定義一個名字叫HMI 或HAL_MODULE_INFO_SYM 的模塊數據結構,開頭必須是hw_module_t

2、定義hw_module_methods_t 的結構體,實現open

3、定義一個設備的數據結構,里面可以設備自身相關的數據成員,但開頭必須是hw_device_t

4、設備自身的成員的處理

二、hardware.c

主要目的就是通過函數hw_get_module、hw_get_module_by_class 找到對應的模組

1、hw_get_module

int hw_get_module(const char *id, const struct hw_module_t **module){    return hw_get_module_by_class(id, NULL, module);}可以看到hw_get_module 和hw_get_module_by_class 的根本區別第二個參數是否為NULL

在說明hardware.h 的時候說明過,HAL 首先要定義一個數據結構,這個數據結構開頭必須是hw_module_t,也就是說hw_module_t 可能是共同的,但是模塊自身的數據結構實現的接口可能不同。例如audio 就分primary、a2dp,這兩個模組的數據結構開頭hw_module_t 可以是相同的。

因此,hw_get_module_by_class 的第一個參數是hw_module_t 的id,第二個參數是區分模組。

2、hw_get_module_by_class

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);}1)首先確定模組的全名name

class_id.inst 組成的,如果inst 是NULL 模組的全名就是id

2) 根據name 找到這個so 是否存在,如果存在調用load

從這里看出來,HAL 的so 命名是有規定的:

a、ro.hardware.name 已經定義好的,這里的name 就是1)中說的全名

如果定義了,so 的名字應該是name.prop.so

b、ro.hardware 或 ro.produce.board 或 ro.board.platform 或 ro.arch 中定義了prop

組合后so 的名字是name.prop.so

c、如果沒有這樣的prop定義,那so 的名字是name.default.so

例如目前我們平臺的hdmi cec 的so 就是來源ro.hardware 中的prop

3、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     */    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;}1)dlopen 對應path 的so

2)dlsym 獲取HMI 的地址至此,hardware 相關的部分就講解完。

總結:

1、獲取module 方式有兩種

hw_get_module_by_class、hw_get_module

前者是針對公用module 的模塊,后者是單個的,當然用前者找單個的module 也是可以的,第二個參數傳NULL 即可

注意最后一個參數是hw_module_t **module

2、通過1 中獲取到module 數據結構指針變量在內存中地址

3、根據2 中的module 就可以調用module 中open 初始化硬件設備,最終獲得hw_device_t

4、根據3 中的device 實現真正的調用,實現與內核的通信


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 鱼台县| 昌宁县| 辛集市| 英吉沙县| 琼海市| 逊克县| 新昌县| 金昌市| 垫江县| 永清县| 白河县| 安阳县| 遂宁市| 巢湖市| 大同市| 东乡族自治县| 叶城县| 铜山县| 仁化县| 昌黎县| 阜平县| 桐柏县| 雷波县| 凭祥市| 祁阳县| 肥西县| 青神县| 庄河市| 凌云县| 晋江市| 漳浦县| 普兰店市| 合江县| 抚顺市| 班玛县| 旅游| 铜梁县| 土默特左旗| 肇东市| 胶州市| 巩留县|