Android硬件抽象层HAL(Hardware abstraction layer)分析
2016-03-24 16:45
543 查看
Android硬件抽象层(HAL)定义了一个标准的接口,这个接口需要硬件厂商来实现,HAL使Android和底层的驱动隔离开来,HAL实现被打包成so文件,由Android系统在适当的时候加载。设备厂商必须实现对应硬件的HAL和驱动,HAL lib位于
结构体
open函数用于打开一个设备,每一个特定的硬件HAL实现一般扩展
实际的设备实现会扩展这个结构,并包含硬件特有功能的函数指针。
每一个硬件模块必须包含一个名字为
我们看一下Audio HAL被加载的过程,Audio HAL由AudioFlinger加载,
获取到HAL_MODULE_INFO_SYM的地址以后整个Audio模块的加载过程就算完成了,然后AudioFlinger会调用
这里调用了module的open函数,来完成Audio设备的打开动作,我们看一下audio hal中open的实现,
open动态分配一个
/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<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); }
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_SYM HMI /** * 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就可以使用这些函数。
相关文章推荐
- Android开发_如何调用系统默认浏览器访问
- [Android]ListView中分割线的设置
- Android中的pix,sp,dp相关概念
- Android开发之MD5加密
- Android图片处理-图片压缩处理
- android 屏幕分辨率
- 由Android HAL想到的
- Android的RelativeLayout的view的layout_marginBottom不起作用
- 设计模式 —— 观察者
- android 的分发、拦截、处理机制
- (4.1.36.11)Android 一张图理解getWidth和getMeasuredWidth
- (4.1.36.10)[Android]Android字体高度的研究
- Android调试驱动抓log的方法
- Android中Volley框架Get,POST封装使用及自动解析JSON
- 通常在使用Android的getActionBar()的时候报空指针异常,通常有5种原因。
- Android系统关机或重启的几种实现方式
- android 数据传递——实例化对象调用方法
- Android图片处理-相机、相处简单调用
- ListView和GridView与ScrollView冲突只显示一行的问题
- Android中退出多个Activity应用