转自:http://blog.csdn.net/jiajie961/archive/2010/11/22/6026585.aspx
Android HAL层,即硬件抽象层,是Google响应厂家“希望不公开源码”的要求推出的新概念 1,源代码和目标位置 源代码: /hardware/libhardware目录,该目录的目录结构如下: /hardware/libhardware/hardware.c编译成libhardware.so,目标位置为/system/lib目录 /hardware/libhardware/include/hardware目录下包含如下头文件: hardware.h 通用硬件模块头文件 copybit.h copybit模块头文件 gralloc.h gralloc模块头文件 lights.h 背光模块头文件 overlay.h overlay模块头文件 qemud.h qemud模块头文件 sensors.h 传感器模块头文件 /hardware/libhardware/modules目录下定义了很多硬件模块 这些硬件模块都编译成xxx.xxx.so,目标位置为/system/lib/hw目录 2,HAL层的实现方式 JNI->通用硬件模块->硬件模块->内核驱动接口 具体一点:JNI->libhardware.so->xxx.xxx.so->kernel 具体来说:android frameworks中JNI调用/hardware/libhardware/hardware.c中定义的hw_get_module函数来获取硬件模块, 然后调用硬件模块中的方法,硬件模块中的方法直接调用内核接口完成相关功能 3,通用硬件模块(libhardware.so) (1)头文件为:/hardware/libhardware/include/hardware/hardware.h 头文件中主要定义了通用硬件模块结构体hw_module_t,声明了JNI调用的接口函数hw_get_module hw_module_t定义如下: typedef struct hw_module_t { /** tag must be initialized to HARDWARE_MODULE_TAG */ uint32_t tag; /** major version number for the module */ uint16_t version_major; /** minor version number of the module */ uint16_t version_minor; /** 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; /** padding to 128 bytes, reserved for future use */ uint32_t reserved[32-7]; } hw_module_t; 硬件模块方法结构体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; 只定义了一个open方法,其中调用的设备结构体参数hw_device_t定义如下: typedef struct hw_device_t { /** tag must be initialized to HARDWARE_DEVICE_TAG */ uint32_t tag; /** version number for hw_device_t */ uint32_t version; /** reference to the module this device belongs to */ struct hw_module_t* module; /** padding reserved for future use */ uint32_t reserved[12]; /** Close this device */ int (*close)(struct hw_device_t* device); } hw_device_t; hw_get_module函数声明如下: int hw_get_module(const char *id, const struct hw_module_t **module); 参数id为模块标识,定义在/hardware/libhardware/include/hardware目录下的硬件模块头文件中, 参数module是硬件模块地址,定义了/hardware/libhardware/include/hardware/hardware.h中 (2)hardware.c中主要是定义了hw_get_module函数如下: #define HAL_LIBRARY_PATH "/system/lib/hw" 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(const char *id, const struct hw_module_t **module) { int status; int i; const struct hw_module_t *hmi = NULL; char prop[PATH_MAX]; char path[PATH_MAX]; for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) { if (i < HAL_VARIANT_KEYS_COUNT) { if (property_get(variant_keys[i], prop, NULL) == 0) { continue; } snprintf(path, sizeof(path), "%s/%s.%s.so", HAL_LIBRARY_PATH, id, prop); } else { snprintf(path, sizeof(path), "%s/%s.default.so", HAL_LIBRARY_PATH, id); } if (access(path, R_OK)) { continue; } /* we found a library matching this id/variant */ break; } status = -ENOENT; if (i < HAL_VARIANT_KEYS_COUNT+1) { /* load the module, if this fails, we're doomed, and we should not try * to load a different variant. */ status = load(id, path, module); } return status; } 从源代码我们可以看出,hw_get_module完成的主要工作是根据模块id寻找硬件模块动态连接库地址,然后调用load函数去打开动态连接库 并从动态链接库中获取硬件模块结构体地址。硬件模块路径格式如下: HAL_LIBRARY_PATH/id.prop.so HAL_LIBRARY_PATH定义为/system/lib/hw id是hw_get_module函数的第一个参数所传入,prop部分首先按照variant_keys数组中的名称逐一调用property_get获取对应的系统属性, 然后访问HAL_LIBRARY_PATH/id.prop.so,如果找到能访问的就结束,否则就访问HAL_LIBRARY_PATH/id.default.so 举例如下: 假定访问的是背光模块,id定义为"lights"则系统会按照如下的顺序去访问文件: /system/lib/hw/lights.[ro.hardware属性值].so /system/lib/hw/lights.[ro.product.board属性值].so /system/lib/hw/lights.[ro.board.platform属性值].so /system/lib/hw/lights.[ro.arch属性值].so /system/lib/hw/lights.default.so 所以开发硬件模块的时候Makefile文件(Android.mk)中模块的命名LOCAL_MODULE要参考上面的内容,否则就会访问不到没作用了。 load函数的关键部分代码如下: handle = dlopen(path, RTLD_NOW); //打开动态链接库 if (handle == NULL) { char const *err_str = dlerror(); LOGE("load: module=%s/n%s", path, err_str?err_str:"unknown"); status = -EINVAL; goto done; } const char *sym = HAL_MODULE_INFO_SYM_AS_STR; hmi = (struct hw_module_t *)dlsym(handle, sym); //从动态链接库中获取硬件模块结构体的指针 if (hmi == NULL) { LOGE("load: couldn't find symbol %s", sym); status = -EINVAL; goto done; } HAL_MODULE_INFO_SYM_AS_STR是硬件模块在动态链接库中的标志,定义在hardware.h中如下: #define HAL_MODULE_INFO_SYM HMI #define HAL_MODULE_INFO_SYM_AS_STR "HMI" 4,硬件模块 硬件模块的开发主要是完成/hardware/libhardware/include/hardware目录下对应的头文件中的内容,主要是硬件模块头文件和hardware.h中 的结构体中定义了一些函数指针,调用内核提供的接口将具体的函数实现,然后编译成指定名称的动态链接库放到/system/lib/hw目录下即可。 用一句话来概括:硬件模块的开发就是定义一个hardware.h中定义的hw_module_t结构体,结构体名称为宏HAL_MODULE_INFO_SYM,然后实现结构体 的相关内容即可。 5,内核驱动 主要是要向用户层开放接口,让硬件模块和内核可以交互。
转自:http://blogold.chinaunix.net/u/22630/showart_2190386.html
HAL module 执行过程分析
1. 其初始化过程如下:
System.loadLibrary("led_runtime")->JNI_OnLoad()->registerMethods()-> ->env->RegisterNatives(clazz,gMethods,sizeof(gMethods)/sizeof(gMethods[0])) ;
onCreate () -> led_srv = new LedService() ->_init()->led_init() -> hw_get_module(LED_HARDWARE_MODULE_ID, (const hw_module_t**)&module) ->
led_control_open(&module->common,&sLedDevice)-> module->methods->open(module,LED_HARDWARE_MODULE_ID,(struct hw_device_t**)device) -> led_device_open ()
如此就完成了 app 到底层的初始化工作。
2. 接下来看一下 hw_get_module() 函数。
hw_get_module()->property_get(variant_keys[i],prop,NULL)->__system_property_get(key, value) ->__system_property_find(const char *name)-> __system_property_read(pi, 0, value)-> load(id, prop, &hmi)->snprintf(path,sizeof(path), "%s/%s.%s.so", HAL_LIBRARY_PATH, id, variant )-> handle = dlopen (path, RTLD_NOW)-> const char *sym = HAL_MODULE_INFO_SYM_AS_STR ; hmi = (const struct hw_module_t *) dlsym (handle, sym)
3. 最后我们看一下 property_get(variant_keys[i],prop,NULL)
这个其实就是获取 ro.hardware 属性,我们关心的就是这个值是什么。在 system/core/init/init.c 文件中的 main 函数中有这么一句: property_set("ro.hardware", hardware); 无疑 ro.hardware 的值就是 hardware 中的内容了。我们再找一下 hardware 赋的什么值,就 ok 了。其实就是在这个函数中完成的 get_hardware_name() 。
open("/proc/cpuinfo", O_RDONLY)
hw = strstr(data, "/nHardware")
while (*x && !isspace(*x))
{
hardware[n++] = tolower(*x);
x++;
if (n == 31) break;
}
Ok ,我们看到了,它是从 /proc/cpuinfo 中读出来的。我们再打开 cpuinfo 文件看一下: Hardware : Goldfish 。好了,我们确定了 property_get ()得到的是 goldfish ,那么 snprintf(path, sizeof(path), "%s/%s.%s.so", HAL_LIBRARY_PATH, id, variant ) 构造出来的 path 就是 /system/lib/hw/led.goldfish.so 。终于找到咱们的 so 文件了。
hmi = (struct hw_module_t *)dlsym(handle, sym);
//dlsym在bionic/linker/dlfcn.c里定义
lookup_in_library-> //bionic/linker/linker.c
_elf_lookup->//bionic/linker/linker.c
static Elf32_Sym *_elf_lookup(soinfo *si, unsigned hash, const char *name) { Elf32_Sym *s; Elf32_Sym *symtab = si->symtab; const char *strtab = si->strtab; unsigned n; TRACE_TYPE(LOOKUP, "%5d SEARCH %s in %s@0x%08x %08x %d/n", pid, name, si->name, si->base, hash, hash % si->nbucket); n = hash % si->nbucket; for(n = si->bucket[hash % si->nbucket]; n != 0; n = si->chain[n]){ s = symtab + n; if(strcmp(strtab + s->st_name, name)) continue; /* only concern ourselves with global and weak symbol definitions */ switch(ELF32_ST_BIND(s->st_info)){ case STB_GLOBAL: case STB_WEAK: /* no section == undefined */ if(s->st_shndx == 0) continue; TRACE_TYPE(LOOKUP, "%5d FOUND %s in %s (%08x) %d/n", pid, name, si->name, s->st_value, s->st_size); return s; } } return NULL; }
**********************dlopen
handle = dlopen(path, RTLD_NOW);
//dlopen 在bionic/linker/dlfcn.c里定义
->find_library / /bionic/linker/linker.c
->load_library
->init_library