sc7731 Android 5.1 LCD驱动简明笔记之三

此篇笔记基于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等等。精彩的世界,未知的世界,等待着去探索。

   于此,记之。

 

转载于:https://www.cnblogs.com/chineseboy/p/5110721.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值