Android硬件抽象层HAL(Hardware abstraction layer)分析

2016-12-12 14:12:28来源:网络收集作者:Max人点击

Android硬件抽象层(HAL)定义了一个标准的接口,这个接口需要硬件厂商来实现,HAL使Android和底层的驱动隔离开来,HAL实现被打包成so文件,由Android系统在适当的时候加载。设备厂商必须实现对应硬件的HAL和驱动,HAL lib位于/system/lib/hw目录下。

标准的HAL结构

HAL模块包含两个通用的组件:模块(module)和设备(device),它们在hardware/libhardware/include/hardware/hardware.h中定义。

typedef struct hw_module_t {
uint32_t tag;
uint16_t module_api_version;
#define version_major 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
} hw_module_t;

结构体hw_module_t代表一个模块,它包含了模块的版本、名字、作者等信息,Android利用这些信息来发现和加载正确的模块。另外,hw_module_t中包含一个指向hw_module_methods_t结构的指针,这个结构包含一个指向模块open函数的指针,

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;

open函数用于打开一个设备,每一个特定的硬件HAL实现一般扩展hw_module_t结构,增加模块自己特有的信息。一个设备抽象了实际的硬件,结构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;

实际的设备实现会扩展这个结构,并包含硬件特有功能的函数指针。

实现

我们以Audio HAL为例来说明HAL的实现以及如何被系统加载。我们看一下Audio的module和device的定义,

struct audio_module {
struct hw_module_t common;
};
struct audio_hw_device {
struct hw_device_t common;
uint32_t (*get_supported_devices)(const struct audio_hw_device *dev);
int (*init_check)(const struct audio_hw_device *dev);
...

每一个硬件模块必须包含一个名字为HAL_MODULE_INFO_SYM的结构,并且这个结构必须以hw_module_t结构开头,device定义必须以hw_device_t结构开头。

static struct hw_module_methods_t hal_module_methods = {
.open = adev_open,
};
struct audio_module HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = AUDIO_MODULE_API_VERSION_0_1,
.hal_api_version = HARDWARE_HAL_API_VERSION,
.id = AUDIO_HARDWARE_MODULE_ID,
.name = "Default audio HW HAL",
.author = "The Android Open Source Project",
.methods = &hal_module_methods,
},
};

我们看一下Audio HAL被加载的过程,Audio HAL由AudioFlinger加载,

static int load_audio_interface(const char *if_name, audio_hw_device_t **dev)
{
const hw_module_t *mod;
int rc;
rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, &mod);
ALOGE_IF(rc, "%s couldn't load audio hw module %s.%s (%s)", __func__,
AUDIO_HARDWARE_MODULE_ID, if_name, strerror(-rc));
if (rc) {
goto out;
}
rc = audio_hw_device_open(mod, dev);
ALOGE_IF(rc, "%s couldn't open audio hw device in %s.%s (%s)", __func__,
AUDIO_HARDWARE_MODULE_ID, if_name, strerror(-rc));
if (rc) {
goto out;
}
if ((*dev)->common.version < AUDIO_DEVICE_API_VERSION_MIN) {
ALOGE("%s wrong audio hw device version %04x", __func__, (*dev)->common.version);
rc = BAD_VALUE;
goto out;
}
return 0;
out:
*dev = NULL;
return rc;
}

hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, &mod)用于加载指定的模块,我们看一下它的实现,

int hw_get_module_by_class(const char *class_id, const char *inst,
const struct hw_module_t **module)
{
int i;
char prop[PATH_MAX];
char path[PATH_MAX];
char name[PATH_MAX];
char prop_name[PATH_MAX];
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 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);
}

hw_get_module_by_class根据传入的class_id和if_name来构建模块的名字,这里为audio.primary并判断/system/lib/hw/或者/vendor/lib/hw目录下是否存在audio.primary.so,如果存在调用load(class_id, path, module)加载模块,

static int load(const char *id,
const char *path,
const struct hw_module_t **pHmi)
{
int status;
void *handle;
struct hw_module_t *hmi;
/*
* 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;
}

load首先调用handle = dlopen(path, RTLD_NOW)加载audio.primary.so,注意参数RTLD_NOW,如果audio.primay.so依赖其它的so,那么其他的so在dlopen返回前会自动加载;然后调用hmi = (struct hw_module_t *)dlsym(handle, sym)来获取sym的地址,sym就是我们之前定义的模块结构的名字HAL_MODULE_INFO_SYM的字符串形式,HAL_MODULE_INFO_SYM_AS_STR,

/**
* Name of the hal_module_info
*/
#define HAL_MODULE_INFO_SYMHMI
/**
* Name of the hal_module_info as a string
*/
#define HAL_MODULE_INFO_SYM_AS_STR"HMI"

获取到HAL_MODULE_INFO_SYM的地址以后整个Audio模块的加载过程就算完成了,然后AudioFlinger会调用audio_hw_device_open来打开设备,获取设备结构hw_device_t地址,因为hw_deivce_t是audio_hw_device结构的第一个成员,所以通过强制指针转换就可以获取audio_hw_device的指针。

static inline int audio_hw_device_open(const struct hw_module_t* module,
struct audio_hw_device** device)
{
return module->methods->open(module, AUDIO_HARDWARE_INTERFACE,
(struct hw_device_t**)device);
}

这里调用了module的open函数,来完成Audio设备的打开动作,我们看一下audio hal中open的实现,

static int adev_open(const hw_module_t* module, const char* name,
hw_device_t** device)
{
struct stub_audio_device *adev;
int ret;
if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0)
return -EINVAL;
adev = calloc(1, sizeof(struct stub_audio_device));
if (!adev)
return -ENOMEM;
adev->device.common.tag = HARDWARE_DEVICE_TAG;
adev->device.common.version = AUDIO_DEVICE_API_VERSION_2_0;
adev->device.common.module = (struct hw_module_t *) module;
adev->device.common.close = adev_close;
adev->device.init_check = adev_init_check;
adev->device.set_voice_volume = adev_set_voice_volume;
adev->device.set_master_volume = adev_set_master_volume;
adev->device.get_master_volume = adev_get_master_volume;
adev->device.set_master_mute = adev_set_master_mute;
adev->device.get_master_mute = adev_get_master_mute;
adev->device.set_mode = adev_set_mode;
adev->device.set_mic_mute = adev_set_mic_mute;
adev->device.get_mic_mute = adev_get_mic_mute;
adev->device.set_parameters = adev_set_parameters;
adev->device.get_parameters = adev_get_parameters;
adev->device.get_input_buffer_size = adev_get_input_buffer_size;
adev->device.open_output_stream = adev_open_output_stream;
adev->device.close_output_stream = adev_close_output_stream;
adev->device.open_input_stream = adev_open_input_stream;
adev->device.close_input_stream = adev_close_input_stream;
adev->device.dump = adev_dump;
*device = &adev->device.common;
return 0;
}

open动态分配一个audio_hw_device结构,并初始化各个结构成员,然后把地址返回给AudioFlinger,audio_hw_device结构中定义了很多跟Audio相关的函数,open返回以后,AudioFlinger就可以使用这些函数。

微信扫一扫

第七城市微信公众平台