hardware 主要的三个结构体

58 阅读5分钟

硬件抽象层 (HAL) 概览

hardware 主要的三个结构体。

// hardware/libhardware/include_all/hardware/hardware.h

struct hw_module_t;
struct hw_module_methods_t;
struct hw_device_t;

hw_module_t

使用方式:

  • 每个硬件模块都必须有一个名为 HAL_MODULE_INFO_SYM 的数据结构。
  • 并且此数据结构的字段必须以 hw_module_t 开头。
  • 随后是模块特定信息。

举例:输入设备

//  hardware/libhardware/include_all/hardware/input.h

struct input_module {
    // 此数据结构的字段必须以 hw_module_t 开头。
    struct hw_module_t common;

    // 随后是模块特定信息。
    void (*init)(const input_module_t* module, input_host_t* host, input_host_callbacks_t cb);

    void (*notify_report)(const input_module_t* module, input_report_t* report);
};
// hardware/libhardware/modules/input/evdev/EvdevModule.cpp

// 每个硬件模块都必须有一个名为 HAL_MODULE_INFO_SYM 的数据结构,包括输入模块。
input_module_t HAL_MODULE_INFO_SYM = {
};

hw_module_t::tag 要求该值只能是 HARDWARE_MODULE_TAG ,用于标识该结构体属于硬件模块结构体。

举例:输入设备

// hardware/libhardware/modules/input/evdev/EvdevModule.cpp

input_module_t HAL_MODULE_INFO_SYM = {
    .common = {
        // 该值只能是 HARDWARE_MODULE_TAG
        .tag                = HARDWARE_MODULE_TAG,
    },
};

hw_module_t::id 使用 id 来区分不同硬件模块。

hw_module_t::methods

// hardware/libhardware/include_all/hardware/hardware.h

// 硬件模块对应的 方法结构体。
struct hw_module_methods_t* methods;

hw_module_methods_t

只有一个 open 函数指针。

打开后将获取到的硬件设备信息放入双重指针指向的 hw_device_t 结构体。

// hardware/libhardware/include_all/hardware/hardware.h

typedef struct hw_module_methods_t {

    /**
     * 函数指针。
     * 用于打开硬件模块对应的一个硬件设备。
     * 
     * module 为硬件模块。
     * id 为硬件设备 ID。
     * device 为双重指针,打开后获取硬件设备信息。
     */
    int (*open)(const struct hw_module_t* module, const char* id,
            struct hw_device_t** device);

} hw_module_methods_t;

hw_device_t

使用方式:

  • 每个设备数据结构都必须以 hw_device_t 开头。
  • 随后是模块特定的公共方法和属性。

举例:摄像头

// hardware/libhardware/include_all/hardware/camera3.h

// 摄像头硬件设备的结构体
typedef struct camera3_device {
    // 每个设备数据结构都必须以 hw_device_t 开头。
    hw_device_t common;
    
    // 随后是模块特定的公共方法和属性。
    camera3_device_ops_t *ops;
    void *priv;
} camera3_device_t;

hw_device_t::tag 要求该值只能是 HARDWARE_DEVICE_TAG ,用于标识该结构体属于硬件设备结构体。

举例:摄像头

// hardware/libhardware/modules/camera/3_4/camera.cpp

Camera::Camera(int id)
{
    // 该值只能是 HARDWARE_DEVICE_TAG ,用于标识该结构体属于硬件设备结构体。
    mDevice.common.tag    = HARDWARE_DEVICE_TAG;
}

hw_device_t::close

函数指针,用于关闭硬件设备。

// hardware/libhardware/include_all/hardware/hardware.h

typedef struct hw_device_t {
    // 函数指针,用于关闭硬件设备。
    int (*close)(struct hw_device_t* device);
} hw_device_t;

hardware module 接口的使用方式

hw_get_module 获取一个硬件模块的指针。

// hardware/libhardware/tests/camera3/camera3test_fixtures.h

class Camera3Module : public testing::Test {
 protected:
    virtual void SetUp() {
        const hw_module_t *hw_module = NULL;
        // 获取一个硬件模块的指针。
        ASSERT_EQ(0, hw_get_module(CAMERA_HARDWARE_MODULE_ID, &hw_module))
                    << "Can't get camera module";

        cam_module_ = reinterpret_cast<const camera_module_t*>(hw_module);
    }
    const camera_module_t * cam_module() { return cam_module_; }
};

hw_module_methods_t::open 获取一个硬件设备的指针。

// hardware/libhardware/tests/camera3/camera3test_fixtures.h

class Camera3Device : public Camera3Module {
 protected:
    virtual void SetUp() {
        Camera3Module::SetUp();
        hw_device_t *device = NULL;
        // 获取一个硬件设备的指针。
        ASSERT_EQ(0, cam_module()->common.methods->open(
            (const hw_module_t*)cam_module(), "0", &device))
                << "Can't open camera device";

        cam_device_ = reinterpret_cast<camera3_device_t*>(device);
    }
    camera3_device_t* cam_device() { return cam_device_; }
};

调用具体的hal实现方法。

// hardware/libhardware/tests/camera3/camera3tests.cpp

TEST_F(Camera3Device, DefaultSettingsStillCaptureHasAndroidControlMode) {
    // 调用具体的hal实现方法。
    const camera_metadata_t *default_settings =
        cam_device()->ops->construct_default_request_settings(cam_device(),
            CAMERA3_TEMPLATE_STILL_CAPTURE);
}

hw_get_module

// hardware/libhardware/hardware.c

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_by_class

  • 将硬件模块 ID 赋值给 name
  • 将 name 拼接到 ro.hardware. 后面,赋值给 prop_name
  • 查询 prop_name 对应的动态库,存在的话,直接加载。
  • 变体属性。
  • 默认属性。
// hardware/libhardware/hardware.c

static const char *variant_keys[] = {
    "ro.hardware",
    "ro.product.board",
    "ro.board.platform",
    "ro.arch"
};

static const int HAL_VARIANT_KEYS_COUNT =
    (sizeof(variant_keys)/sizeof(variant_keys[0]));

int hw_get_module_by_class(const char *class_id, const char *inst,
                           const struct hw_module_t **module)
{
    // 将硬件模块 ID 赋值给 name
    strlcpy(name, class_id, PATH_MAX);

    // 将 name 拼接到 ro.hardware. 后面,赋值给 prop_name
    snprintf(prop_name, sizeof(prop_name), "ro.hardware.%s", name);
    // 查询该属性。
    if (property_get(prop_name, prop, NULL) > 0) {
        // 该属性对应的 so 动态库是否存在。
        if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
            goto found;
        }
    }

    // 遍历变体属性。
    for (i=0 ; i<HAL_VARIANT_KEYS_COUNT; i++) {
        if (property_get(variant_keys[i], prop, NULL) == 0) {
            continue;
        }
        // 变体属性对应的 so 动态库是否存在。
        if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
            goto found;
        }
    }

    // 默认属性对应的 so 动态库是否存在。
    if (hw_module_exists(path, sizeof(path), name, "default") == 0) {
        goto found;
    }

    return -ENOENT;

found:
    // 加载动态库。
    return load(class_id, path, module);
}

hw_module_exists

  • 检查是否存在具有给定名称和子名称的HAL,如果存在,则返回0,否则返回负数。
  • /odm/lib64/hw
  • /vendor/lib64/hw
  • /system/lib64/hw
// hardware/libhardware/hardware.c

#define HAL_LIBRARY_SUBDIR "lib64/hw"

#define HAL_LIBRARY_PATH1 "/system/" HAL_LIBRARY_SUBDIR
#define HAL_LIBRARY_PATH2 "/vendor/" HAL_LIBRARY_SUBDIR
#define HAL_LIBRARY_PATH3 "/odm/" HAL_LIBRARY_SUBDIR

/*
 * 检查是否存在具有给定名称和子名称的HAL,如果存在,则返回0,否则返回负数。
 */
static int hw_module_exists(char *path, size_t path_len, const char *name,
                            const char *subname)
{
    // /odm/lib64/hw
    if (path_in_path(path, HAL_LIBRARY_PATH3) && access(path, R_OK) == 0)
        return 0;

    // /vendor/lib64/hw
    if (path_in_path(path, HAL_LIBRARY_PATH2) && access(path, R_OK) == 0)
        return 0;

    // /system/lib64/hw
    if (path_in_path(path, HAL_LIBRARY_PATH1) && access(path, R_OK) == 0)
        return 0;

    return -ENOENT;
}

load

  • 运行时加载硬件的动态库
  • dlopen 打开动态库。通过它,可以在程序运行时加载动态链接库(.so 文件)
  • dlsym 获取结构体 hal_module_info 的地址。通过它,可以在运行时加载和使用动态链接库中的函数或变量。
// hardware/libhardware/hardware.c

/**
 * 加载变量定义的文件,如果成功,返回 dlopen 句柄和 hmi
 */
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;

    // dlopen
    handle = dlopen(path, RTLD_NOW);

    // 获取结构体 hal_module_info 的地址。
    const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
    // dlsym
    hmi = (struct hw_module_t *)dlsym(handle, sym);

    // dlopen 句柄保存在 hal_module_info中。
    hmi->dso = handle;

    // 标记成功。
    status = 0;

    *pHmi = hmi;

    return status;
}

HAL 层动态库的意义

使用动态库的方式目的是保护硬件厂商的利益,适配 Android 平台无需将操作硬件的代码开源出来。

参考链接: