文章出处: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.h
  • hardware.c

 

hardware.h

1、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_TAG

2)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,也就是辨认都是通过id

6)author

author/owner/implementor 的标记

7)methods

变量是结构体指针 hw_module_methods_t*,里面是一个open 函数,device 的open 就靠这个methods

8)dso

在hardware.c中会解释,dlopen so 时候的handle

 

2、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_TAG
2)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 实现真正的调用,实现与内核的通信

 

 

 

 

 

 

 

 

 

 

 

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐