此篇笔记基于sc7731 - android 5.1,对lcd的gralloc库做一个简明笔记。
第一部分 调用gralloc.sc8830.so
所谓的Gralloc模块,它就是一个模块,一个操作kernel层framebuffer驱动的动态库模块,它属于大名鼎鼎的HAL层。
用的时候就加载到内存空间,不用的时候就从内存空间中卸载掉。下面看下系统如何将该模块加载到内存空间的。
在Android系统中,所有访问HAL层模块的应用,都需要通过一个叫 hw_get_module() 的方法去获得需要的HAL 模块。
一、hw_get_module() 声明说明
定义在 7731_5.1/hardware/libhardware/hardware.c 文件中:
1 /* 2 id : 模块ID 3 module : 对应ID的模块地址 4 */ 5 int hw_get_module(const char *id, const struct hw_module_t **module);
说明:
每个模块都有自己的ID,比如gralloc 模块ID 就是 GRALLOC_HARDWARE_MODULE_ID,它是一个字符串的宏:
#define GRALLOC_HARDWARE_MODULE_ID "gralloc"
模块地址: 如果一切都正确,那么根据调用者给定的ID,就一定能找到一个对应的模块。hw_get_module() 就会把这个模块的地址返回给调用者。
二、hw_get_module() 实现
可以先猜想一下,加载一个动态库?
第一步,需要先在指定的路径下(PATH)找到该库;
第二步,加载该库到内存(解析库的内容)
1. 找HAL 库
1 int hw_get_module(const char *id, const struct hw_module_t **module) 2 { 3 return hw_get_module_by_class(id, NULL, module); 4 } 5 6 7 int hw_get_module_by_class(const char *class_id, const char *inst, 8 const struct hw_module_t **module) 9 { 10 //.... 11 12 //property_get(); 13 14 //去对应的路径找相应的库 15 if (hw_module_exists(path, sizeof(path), name, prop) == 0){ 16 goto found; 17 } 18 19 //.... 20 21 found: 22 /* load the module, if this fails, we're doomed, and we should not try 23 * to load a different variant. */ 24 //如果找到,就开始执行加载库的动作 25 return load(class_id, path, module); 26 } 27 28 29 /* 30 * Check if a HAL with given name and subname exists, if so return 0, otherwise 31 * otherwise return negative. On success path will contain the path to the HAL. 32 */ 33 static int hw_module_exists(char *path, size_t path_len, const char *name, 34 const char *subname) 35 { 36 snprintf(path, path_len, "%s/%s.%s.so", 37 HAL_LIBRARY_PATH2, name, subname); 38 if (access(path, R_OK) == 0) 39 return 0; 40 41 snprintf(path, path_len, "%s/%s.%s.so", 42 HAL_LIBRARY_PATH1, name, subname); 43 if (access(path, R_OK) == 0) 44 return 0; 45 46 return -ENOENT; 47 }
hw_module_exists()就是找库的接口。
库的路径:
1 /** Base path of the hal modules */ 2 #if defined(__LP64__) 3 #define HAL_LIBRARY_PATH1 "/system/lib64/hw" 4 #define HAL_LIBRARY_PATH2 "/vendor/lib64/hw" 5 #else 6 #define HAL_LIBRARY_PATH1 "/system/lib/hw" 7 #define HAL_LIBRARY_PATH2 "/vendor/lib/hw" 8 #endif
很显然,库的路径是可以修改的。
access() 是测试该路径下的该库,是否可用,以及其具体权限如何(读写)。
man access 的结果:
1 NAME 2 access - check real user's permissions for a file 3 4 SYNOPSIS 5 #include <unistd.h> 6 7 int access(const char *pathname, int mode); 8 ....
说明:
在找库的过程中,会使用 property_get()接口去获取相关的属性。对于该接口,涉及到了Android property系统,暂时对这块未做探究。
2. 加载HAL 库( 解析HAL库)
如果找到该库,而且该库也可以读,那就load 到内存空间吧。
1 /** 2 * Load the file defined by the variant and if successful 3 * return the dlopen handle and the hmi. 4 * @return 0 = success, !0 = failure. 5 */ 6 static int load(const char *id, 7 const char *path, 8 const struct hw_module_t **pHmi) 9 { 10 int status; 11 void *handle; 12 struct hw_module_t *hmi; 13 14 /* 15 * load the symbols resolving undefined symbols before 16 * dlopen returns. Since RTLD_GLOBAL is not or'd in with 17 * RTLD_NOW the external symbols will not be global 18 */ 19 handle = dlopen(path, RTLD_NOW); 20 21 //... 22 23 /* Get the address of the struct hal_module_info. */ 24 // #define HAL_MODULE_INFO_SYM_AS_STR "HMI" 25 const char *sym = HAL_MODULE_INFO_SYM_AS_STR; 26 hmi = (struct hw_module_t *)dlsym(handle, sym); 27 28 /* Check that the id matches */ 29 //比较模块的ID,是否符合要求 30 if (strcmp(id, hmi->id) != 0) { 31 //... 32 } 33 34 hmi->dso = handle; 35 36 /* success */ 37 status = 0; 38 39 done: 40 if (status != 0) { 41 hmi = NULL; 42 if (handle != NULL) { 43 dlclose(handle); 44 handle = NULL; 45 } 46 } else { 47 //.... 48 } 49 50 //通过指针的方式,将对应的HAL module的地址返回给hw_get_module()函数调用者 51 *pHmi = hmi; 52 53 return status; 54 }
(1) Linux 下对应动态库的操作函数列表:
1 #include <dlfcn.h> 2 3 void *dlopen(const char *filename, int flag); 4 5 char *dlerror(void); 6 7 void *dlsym(void *handle, const char *symbol); 8 9 int dlclose(void *handle);
Link with -ldl.
注意使用这些库函数的时候,需要 -l 的方式连接 dl 库
(2) 模块的symbol(重要)
在上述代码片段中,dlsym()函数的操作需要注意下:
使用dlsym()函数 需要两个参数:一个是dlopen()函数打开的模块句柄;另外一个是模块的symbol。而关键的就是这个 symbol。
从上面可以看见,HAL 层所有模块的symbol 都被宏定义成了"HMI" 这个字符串。换句话说,所有的HAL模块都需要导出一个 "HMI"的 symbol。而dlsym()函数会根据"HMI" 这个symbol找到模块真正的地址。
在Linux下,所有的HAL层模块都是elf格式的二进制文件,使用readelf可以查看:
readelf -s ./out/target/product/w830_0203/system/lib/hw/gralloc.sc8830.so
1 Symbol table '.dynsym' contains 66 entries: 2 Num: Value Size Type Bind Vis Ndx Name 3 0: 00000000 0 NOTYPE LOCAL DEFAULT UND 4 1: 00000000 0 FUNC GLOBAL DEFAULT UND __cxa_finalize 5 2: 00000000 0 FUNC GLOBAL DEFAULT UND __cxa_atexit 6 3: 00000000 0 FUNC GLOBAL DEFAULT UND strncmp 7 4: 00001d3d 152 FUNC GLOBAL DEFAULT 8 _Z17alloc_device_openPK11 8 5: 000027c1 228 FUNC GLOBAL DEFAULT 8 _Z23framebuffer_device_op 9 6: 00000000 0 FUNC GLOBAL DEFAULT UND __aeabi_unwind_cpp_pr0 10 7: 00000000 0 FUNC GLOBAL DEFAULT UND __android_log_print 11 8: 00000000 0 FUNC GLOBAL DEFAULT UND ion_sync_fd 12 9: 00000000 0 FUNC GLOBAL DEFAULT UND memset 13 10: 00000000 0 FUNC GLOBAL DEFAULT UND ion_invalidate_fd 14 11: 00000000 0 FUNC GLOBAL DEFAULT UND getpid 15 12: 00000000 0 FUNC GLOBAL DEFAULT UND pthread_mutex_lock 16 13: 00000000 0 FUNC GLOBAL DEFAULT UND munmap 17 14: 00000000 0 FUNC GLOBAL DEFAULT UND __errno 18 15: 00000000 0 FUNC GLOBAL DEFAULT UND strerror 19 16: 00000000 0 FUNC GLOBAL DEFAULT UND pthread_mutex_unlock 20 17: 00000000 0 FUNC GLOBAL DEFAULT UND ion_open 21 18: 00000000 0 FUNC GLOBAL DEFAULT UND mmap 22 19: 000015dd 224 FUNC GLOBAL DEFAULT 8 _ZN16private_module_tC2Ev 23 20: 00000000 0 FUNC GLOBAL DEFAULT UND pthread_mutex_init 24 21: 000015dd 224 FUNC GLOBAL DEFAULT 8 _ZN16private_module_tC1Ev 25 22: 0000501c 444 OBJECT GLOBAL DEFAULT 17 HMI 26 23: 00000000 0 FUNC GLOBAL DEFAULT UND ion_close 27 24: 00000000 0 FUNC GLOBAL DEFAULT UND close 28 25: 00000000 0 FUNC GLOBAL DEFAULT UND _ZdlPv 29 26: 00000000 0 FUNC GLOBAL DEFAULT UND ion_free 30 27: 00000000 0 FUNC GLOBAL DEFAULT UND ion_alloc 31 28: 00000000 0 FUNC GLOBAL DEFAULT UND ion_share 32 29: 00000000 0 FUNC GLOBAL DEFAULT UND _Znwj 33 30: 00002351 1132 FUNC GLOBAL DEFAULT 8 _Z24init_frame_buffer_loc 34 31: 00000000 0 FUNC GLOBAL DEFAULT UND dup 35 32: 00001df1 10 FUNC GLOBAL DEFAULT 8 _Z19compositionCompleteP2 36 33: 00000000 0 FUNC GLOBAL DEFAULT UND glFinish 37 34: 00000000 0 FUNC GLOBAL DEFAULT UND __aeabi_unwind_cpp_pr1 38 35: 00000000 0 FUNC GLOBAL DEFAULT UND __sprintf_chk 39 36: 00000000 0 FUNC GLOBAL DEFAULT UND fopen 40 37: 00000000 0 FUNC GLOBAL DEFAULT UND fseek 41 38: 00000000 0 FUNC GLOBAL DEFAULT UND __strlen_chk 42 39: 00000000 0 FUNC GLOBAL DEFAULT UND fwrite 43 40: 00000000 0 FUNC GLOBAL DEFAULT UND fclose 44 41: 00000000 0 FUNC GLOBAL DEFAULT UND __stack_chk_fail 45 42: 00000000 0 OBJECT GLOBAL DEFAULT UND __stack_chk_guard 46 43: 00000000 0 FUNC GLOBAL DEFAULT UND clock_gettime 47 44: 00001f05 168 FUNC GLOBAL DEFAULT 8 _Z17getApctFpsSupportv 48 45: 00000000 0 FUNC GLOBAL DEFAULT UND fread 49 46: 00000000 0 FUNC GLOBAL DEFAULT UND atol 50 47: 000051d8 1 OBJECT GLOBAL DEFAULT 17 gIsApctRead 51 48: 00005218 1 OBJECT GLOBAL DEFAULT 17 gIsApctFpsShow 52 49: 00000000 0 FUNC GLOBAL DEFAULT UND __aeabi_l2f 53 50: 00000000 0 FUNC GLOBAL DEFAULT UND __aeabi_l2d 54 51: 00000000 0 FUNC GLOBAL DEFAULT UND setitimer 55 52: 00000000 0 FUNC GLOBAL DEFAULT UND signal 56 53: 00002a7d 316 FUNC GLOBAL DEFAULT 8 _Z7dump_fbPvP17fb_var_scr 57 54: 00000000 0 FUNC GLOBAL DEFAULT UND ioctl 58 55: 00000000 0 FUNC GLOBAL DEFAULT UND memcpy 59 56: 00000000 0 FUNC GLOBAL DEFAULT UND __aeabi_uldivmod 60 57: 00000000 0 FUNC GLOBAL DEFAULT UND snprintf 61 58: 00000000 0 FUNC GLOBAL DEFAULT UND open 62 59: 00000000 0 FUNC GLOBAL DEFAULT UND property_get 63 60: 00000000 0 FUNC GLOBAL DEFAULT UND atoi 64 61: 000028a5 472 FUNC GLOBAL DEFAULT 8 _Z8dump_bmpPKcPvP10buffer 65 62: 00000000 0 FUNC GLOBAL DEFAULT UND fscanf 66 63: 00005018 0 NOTYPE GLOBAL DEFAULT ABS _edata 67 64: 00005018 0 NOTYPE GLOBAL DEFAULT ABS __bss_start 68 65: 00005254 0 NOTYPE GLOBAL DEFAULT ABS _end
看第 22行:
22: 0000501c 444 OBJECT GLOBAL DEFAULT 17 HMI
那么,dlsym()根据传入的"HMI" 就会匹配到该地址。至于HAL 模块怎么导出这个"HMI" 的symbol呢? 待后续分析。
这里需要注意的是,dlopen() 和 dlsym() 的返回值是一个万能指针。
特别是dlsym()返回值是一个动态库的地址。在该处调用中,它的返回值类型做了一个强制转换,转成了 struct hw_module_t 的指针形式---说明,HAL层模块需要提供相同的数据结构,起码前面结构体大小的字节要一一对应
也即是,接下来要分析的Gralloc模块中,要在模块的前 sizeof(struct hw_module_t) 个字节内(这里的前多少个字节,要除开elf文件的一系列其他说明信息,具体elf文件格式,此处略),提供一样的数据结构才行。(ps: 指针指向的是某段内存的开始地址嘛)
第二部分 Gralloc库的实现框架
既然调用者已经通过hw_get_module()函数找到了我们grallo.so 的具体地址,也就加载到了内存中。Gralloc模块就该开始工作了。
接下来简略分析下Gralloc实现。
1. 文件结构
展讯的Gralloc 库实现位于:
7731_5.1/vendor/sprd/open-source/libs/gralloc/utgard/
包含的文件如下:
其中, gralloc_module.cpp 是核心文件,其他文件是接口的封装,同gralloc_module.cpp形成调用关系。
2 代码分析
(1) 入口对象
在 gralloc_module.cpp 中,定义了一个对象,也即是展讯的Gralloc模块,就是一个HAL对象。
1 /* 2 * HAL_MODULE_INFO_SYM will be initialized using the default constructor 3 * implemented above 4 */ 5 struct private_module_t HAL_MODULE_INFO_SYM;
HAL_MODULE_INFO_SYM 实质上是一个宏,定义在 7731_5.1/hardware/libhardware/include/hardware/hardware.h 文件中
1 #define HAL_MODULE_INFO_SYM HMI
这个前面在分析 模块的symbol 的时候,已经分析到了 dlsym() 需要依赖模块的导出为 "HMI"的 symbol。在这里给出来了。
至于网上有部分说法是,每个HAL 模块必须有一个 HAL_MODULE_INFO_SYM 这个东西。正确也不完全正确,我完全可以写成 struct private_module_t HMI 也没错。
(2) struct private_module_t 类
定义在了gralloc_priv.h文件中:
1 struct private_module_t { 2 gralloc_module_t base; /*很重要的一个成员,这里需要注册许多关键性的东西*///在该类的构造函数中分析 3 4 private_handle_t* framebuffer; /* 指向图形缓冲区的句柄 */ 5 uint32_t flags; /* 用来标志系统帧缓冲区是否支持双缓冲 */ 6 uint32_t numBuffers; /* 表示系统帧缓冲的个数 */ 7 uint32_t bufferMask; /* 记录系统帧缓冲的使用情况 */ 8 pthread_mutex_t lock; /* 保护结构体private_module_t的并行访问 */ 9 buffer_handle_t currentBuffer; /* 描述当前正在被渲染的图形缓冲区 */ 10 int pmem_master; /* pmem设备节点的描述符 */ 11 void* pmem_master_base; /* pmem的起始虚拟地址 */ 12 13 struct fb_var_screeninfo info; /* lcd的可变参数 */ 14 struct fb_fix_screeninfo finfo; /* lcd的固定参数 */ 15 float xdpi; /* x方向上每英寸的像素数量 */ 16 float ydpi; /* y方向上每英寸的像素数量 */ 17 float fps; /* lcd的刷新率 */ 18 };
(注释参考 http://blog.csdn.net/g_salamander/article/details/8424334)
base 成员非常重要,其一是需要注册许多关键性东西;其二,已经在前面说过,它内部的第一个变量需要是 struct hw_module_t 类型的---否则,dlsym()返回值的强制转换,将转成什么玩意儿呢?
1 typedef struct gralloc_module_t { 2 struct hw_module_t common; //这里必须是 struct hw_module_t 类型的变量(成员) 3 4 /*注册一个图形缓冲区*/ 5 int (*registerBuffer)(struct gralloc_module_t const* module, 6 buffer_handle_t handle); 7 8 /*注销一个图形缓冲区*/ 9 int (*unregisterBuffer)(struct gralloc_module_t const* module, 10 buffer_handle_t handle); 11 12 /*在使用图形缓冲区前,先调用该函数加锁*/ 13 int (*lock)(struct gralloc_module_t const* module, 14 buffer_handle_t handle, int usage, 15 int l, int t, int w, int h, 16 void** vaddr); 17 18 /*图形缓冲器使用完毕,调用该函数解锁*/ 19 int (*unlock)(struct gralloc_module_t const* module, 20 buffer_handle_t handle); 21 22 /* reserved for future use */ 23 int (*perform)(struct gralloc_module_t const* module, 24 int operation, ... ); 25 26 int (*lock_ycbcr)(struct gralloc_module_t const* module, 27 buffer_handle_t handle, int usage, 28 int l, int t, int w, int h, 29 struct android_ycbcr *ycbcr); 30 31 int (*lockAsync)(struct gralloc_module_t const* module, 32 buffer_handle_t handle, int usage, 33 int l, int t, int w, int h, 34 void** vaddr, int fenceFd); 35 36 int (*unlockAsync)(struct gralloc_module_t const* module, 37 buffer_handle_t handle, int* fenceFd); 38 39 int (*lockAsync_ycbcr)(struct gralloc_module_t const* module, 40 buffer_handle_t handle, int usage, 41 int l, int t, int w, int h, 42 struct android_ycbcr *ycbcr, int fenceFd); 43 44 /* reserved for future use */ 45 void* reserved_proc[3]; 46 } gralloc_module_t;
(3) struct private_module_t 类的构造函数
c++中,一个对象的建立,都会调用其构造函数。展讯在这里借助类的构造函数,搞定了gralloc模块的一切注册
1 private_module_t::private_module_t() 2 { 3 #define INIT_ZERO(obj) (memset(&(obj),0,sizeof((obj)))) 4 5 base.common.tag = HARDWARE_MODULE_TAG; 6 base.common.version_major = 1; 7 base.common.version_minor = 0; 8 base.common.id = GRALLOC_HARDWARE_MODULE_ID; //Gralloc 的模块ID 9 base.common.name = "Graphics Memory Allocator Module"; 10 base.common.author = "ARM Ltd."; 11 base.common.methods = &gralloc_module_methods; 12 base.common.dso = NULL; 13 INIT_ZERO(base.common.reserved); 14 15 base.registerBuffer = gralloc_register_buffer; 16 base.unregisterBuffer = gralloc_unregister_buffer; 17 base.lock = gralloc_lock; 18 base.lock_ycbcr = gralloc_lock_ycbcr; 19 base.unlock = gralloc_unlock; 20 base.perform = NULL; 21 INIT_ZERO(base.reserved_proc); 22 23 framebuffer = NULL; 24 flags = 0; 25 numBuffers = 0; 26 bufferMask = 0; 27 pthread_mutex_init(&(lock), NULL); 28 currentBuffer = NULL; 29 INIT_ZERO(info); 30 INIT_ZERO(finfo); 31 xdpi = 0.0f; 32 ydpi = 0.0f; 33 fps = 0.0f; 34 swapInterval = 1; 35 36 initialize_blk_conf(); 37 38 #undef INIT_ZERO 39 };
一个gralloc或者说一个HAL模块的模型,就上面那个构造函数里的样子。而需要我们自己实现的是:
gralloc_module_methods (base.common.methods)
gralloc_register_buffer(base.registerBuffer)
gralloc_unregister_buffer(base.unregisterBuffer)
gralloc_lock(base.lock)
base.unlock(gralloc_unlock)
(4) gralloc_module_methods
1 static struct hw_module_methods_t gralloc_module_methods = 2 { 3 open: gralloc_device_open 4 };
(4.1)
1 static int gralloc_device_open(const hw_module_t* module, const char* name, hw_device_t** device) 2 { 3 int status = -EINVAL; 4 5 if (!strncmp(name, GRALLOC_HARDWARE_GPU0, MALI_GRALLOC_HARDWARE_MAX_STR_LEN)) //GPU0 6 { 7 status = alloc_device_open(module, name, device); 8 } 9 else if (!strncmp(name, GRALLOC_HARDWARE_FB0, MALI_GRALLOC_HARDWARE_MAX_STR_LEN)) //FB 10 { 11 status = framebuffer_device_open(module, name, device); 12 } 13 14 return status; 15 }
展讯这里经过一个循环调用,最终会进入的alloc_device_open()的调用。该函数处于 alloc_device.cpp 文件。
1 int alloc_device_open(hw_module_t const *module, const char *name, hw_device_t **device) 2 { 3 //.. 4 alloc_device_t *dev; 5 6 dev = new alloc_device_t; 7 //... 8 dev->alloc = alloc_device_alloc; 9 dev->free = alloc_device_free; 10 //... 11 }
alloc_device_alloc() 函数是与Framebuffer设备文件进行交互的。里面会调用gralloc_alloc_framebuffer() 根据LCD采用的颜色模式分配不同大小的显存空间。
(4.2)
1 int framebuffer_device_open(hw_module_t const *module, const char *name, hw_device_t **device) 2 { 3 //... 4 status = gralloc_open(module, &gralloc_device); 5 6 private_module_t *m = (private_module_t *)module; 7 status = init_frame_buffer(m); 8 9 //... 10 framebuffer_device_t *dev = new framebuffer_device_t(); 11 12 //... 13 }
以上这些函数都是以注册的方式放在了Gralloc模块中,当一个应用把gralloc模块加载到了内存时,可以通过钩子函数调用的方式,在顶层使用它们。
其他函数代码分析不再贴了,毕竟只是一种逻辑而已了。
(over)
2016-1-07
总结:
通过对展讯LCD 底层框架的简略性学习,做了三个简略性的笔记。第一篇是一个总的笔记,第二篇是针对framebuffer做了一个简略的分析,第三篇也就会这篇对HAL层gralloc做了一个简略性的总结。
以前未学驱动之前,或许驱动是如此的神秘,kernel是如此的神秘。但是经过几个月kernel/driver代码分析下来后,这种神秘的面纱或许是可以揭开了。
而这次从底层到上层的一个比较完整性的分析,更是拓宽了知识面,也改变了某些想法。也有可能,对职业发展方向有一定的影响。
lcd驱动->framebuffer驱动->gralloc, 这里没有算完。gralloc 的上面是更加宽广的世界,比如mutilplay,opengl等等。精彩的世界,未知的世界,等待着去探索。
于此,记之。