首页 > 代码库 > hw_module_t 加载过程

hw_module_t 加载过程

 每一个HAL模块都有一个ID值,以这些ID值为参数来调用硬件抽象层提供的函数hw_get_module就可以将指定的模块加载到内存来,并且获得 一个hw_module_t接口来打开相应的设备。 函数hw_get_module实现在hardware/libhardware /hardware.c文件中,如下所示:

     /** Base path of the hal modules */  

  1. #define HAL_LIBRARY_PATH1 "/system/lib/hw"  
  2. #define HAL_LIBRARY_PATH2 "/vendor/lib/hw"  
  3.   
  4. /** 
  5.  * There are a set of variant filename for modules. The form of the filename 
  6.  * is "<MODULE_ID>.variant.so" so for the led module the Dream variants  
  7.  * of base "ro.product.board", "ro.board.platform" and "ro.arch" would be: 
  8.  * 
  9.  * led.trout.so 
  10.  * led.msm7k.so 
  11.  * led.ARMV6.so 
  12.  * led.default.so 
  13.  */  
  14.   
  15. static const char *variant_keys[] = {  
  16.     "ro.hardware",  /* This goes first so that it can pick up a different file on the emulator. */  
  17.     "ro.product.board",  
  18.     "ro.board.platform",  
  19.     "ro.arch"  
  20. };  
  21.   
  22. static const int HAL_VARIANT_KEYS_COUNT =  
  23.     (sizeof(variant_keys)/sizeof(variant_keys[0]));  
  24.   
  25. ......  
  26.   
  27. int hw_get_module(const char *id, const struct hw_module_t **module)  
  28. {  
  29.     int status;  
  30.     int i;  
  31.     const struct hw_module_t *hmi = NULL;  
  32.     char prop[PATH_MAX];  
  33.     char path[PATH_MAX];  
  34.   
  35.     /* 
  36.      * Here we rely on the fact that calling dlopen multiple times on 
  37.      * the same .so will simply increment a refcount (and not load 
  38.      * a new copy of the library). 
  39.      * We also assume that dlopen() is thread-safe. 
  40.      */  
  41.   
  42.     /* Loop through the configuration variants looking for a module */  
  43.     for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {  
  44.         if (i < HAL_VARIANT_KEYS_COUNT) {  
  45.             if (property_get(variant_keys[i], prop, NULL) == 0) {  
  46.                 continue;  
  47.             }  
  48.   
  49.             snprintf(path, sizeof(path), "%s/%s.%s.so",  
  50.                     HAL_LIBRARY_PATH1, id, prop);  
  51.             if (access(path, R_OK) == 0) break;  
  52.   
  53.             snprintf(path, sizeof(path), "%s/%s.%s.so",  
  54.                      HAL_LIBRARY_PATH2, id, prop);  
  55.             if (access(path, R_OK) == 0) break;  
  56.         } else {  
  57.             snprintf(path, sizeof(path), "%s/%s.default.so",  
  58.                      HAL_LIBRARY_PATH1, id);  
  59.             if (access(path, R_OK) == 0) break;  
  60.         }  
  61.     }  
  62.   
  63.     status = -ENOENT;  
  64.     if (i < HAL_VARIANT_KEYS_COUNT+1) {  
  65.         /* load the module, if this fails, we‘re doomed, and we should not try 
  66.          * to load a different variant. */  
  67.         status = load(id, path, module);  
  68.     }  
  69.   
  70.     return status;  
  71. }  

        函数hw_get_module依次在目录/system/lib /hw和/vendor/lib/hw中查找一个名称为"<MODULE_ID>.variant.so"的文件,其 中,<MODULE_ID>是一个模块ID,而variant表 示"ro.hardware"、"ro.product.board"、"ro.board.platform"和"ro.arch"四个系统属性值之一。例如,对于Gralloc模块来说,函数hw_get_module依次在目录/system/lib/hw和/vendor/lib/hw中检查是否存在以下四个文件: 

       gralloc.<ro.hardware>.so

       gralloc.<ro.product.board>.so

       gralloc.<ro.board.platform>.so

       gralloc.<ro.arch>.so
      

       只要其中的一个文件存在,  函数hw_get_module就会停止查找过程,并且调用另外一个函数load来将这个文件加载到内存中来。另一方面,如果在/system/lib/hw和/vendor/lib/hw中均不存这些文件,那么函数hw_get_module就会在目录/system/lib/hw中查找是否存在一个名称为gralloc.default.so的文件。如果存在的话,那么也会调用函数load将它加载到内存中来。函数load也是实现在文件hardware/libhardware/hardware.c文件中,如下所示:

 

static int load(const char *id,  
  1.         const char *path,  
  2.         const struct hw_module_t **pHmi)  
  3. {  
  4.     int status;  
  5.     void *handle;  
  6.     struct hw_module_t *hmi;  
  7.   
  8.     /* 
  9.      * load the symbols resolving undefined symbols before 
  10.      * dlopen returns. Since RTLD_GLOBAL is not or‘d in with 
  11.      * RTLD_NOW the external symbols will not be global 
  12.      */  
  13.     handle = dlopen(path, RTLD_NOW);  
  14.     if (handle == NULL) {  
  15.         char const *err_str = dlerror();  
  16.         LOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");  
  17.         status = -EINVAL;  
  18.         goto done;  
  19.     }  
  20.   
  21.     /* Get the address of the struct hal_module_info. */  
  22.    每个模块定义了一个hw_module_t或hw_module_t子类型变量HAL_MODULE_INFO_SYM,通过这个导出符号可以得到hw_module_t变量对象。
  23.     const char *sym = HAL_MODULE_INFO_SYM_AS_STR;  
  24.     hmi = (struct hw_module_t *)dlsym(handle, sym);  
  25.     if (hmi == NULL) {  
  26.         LOGE("load: couldn‘t find symbol %s", sym);  
  27.         status = -EINVAL;  
  28.         goto done;  
  29.     }  
  30.   
  31.     /* Check that the id matches */  
  32.     if (strcmp(id, hmi->id) != 0) {  
  33.         LOGE("load: id=%s != hmi->id=%s", id, hmi->id);  
  34.         status = -EINVAL;  
  35.         goto done;  
  36.     }  
  37.   
  38.     hmi->dso = handle;  
  39.   
  40.     /* success */  
  41.     status = 0;  
  42.   
  43.     done:  
  44.     if (status != 0) {  
  45.         hmi = NULL;  
  46.         if (handle != NULL) {  
  47.             dlclose(handle);  
  48.             handle = NULL;  
  49.         }  
  50.     } else {  
  51.         LOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",  
  52.                 id, path, *pHmi, handle);  
  53.     }  
  54.   
  55.     *pHmi = hmi;  
  56.   
  57.     return status;  
  58. }  


        在Linux系统中,后缀名为"so"的文件为动态链接库文件,可能通过函数dlopen来加载到内存中。硬件抽象层模块编写规范规定每一个硬件抽象层模块都必须导出一个符号名称为HAL_MODULE_INFO_SYM_AS_STR的符号,而且这个符号必须是用来描述一个类型为hw_module_t的结构体的。

 

        HAL_MODULE_INFO_SYM_AS_STR是一个宏,定义在文件hardware/libhardware/include/hardware/hardware.h文件中,如下所示:

 

 
  1. #define HAL_MODULE_INFO_SYM_AS_STR  "HMI"  

       将模块加载到内存中来之后,就可以调用函数dlsym来获得它所导出的符号HMI。由于这个符号指向的是一个hw_module_t结构体,因此,最后函 数load就可以强制地将这个符号转换为一个hw_module_t结构体指针,并且保存在输出参数pHmi中返回给调用者。

hw_module_t 加载过程