sc7731 Android 5.1 LCD驅動簡明筆記之三

此篇筆記基於sc7731 - android 5.1,對lcd的gralloc庫作一個簡明筆記。android

第一部分 調用gralloc.sc8830.so
所謂的Gralloc模塊,它就是一個模塊,一個操做kernel層framebuffer驅動的動態庫模塊,它屬於大名鼎鼎的HAL層。
用的時候就加載到內存空間,不用的時候就從內存空間中卸載掉。下面看下系統如何將該模塊加載到內存空間的。
在Android系統中,全部訪問HAL層模塊的應用,都須要經過一個叫 hw_get_module() 的方法去得到須要的HAL 模塊。c++

1、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() 就會把這個模塊的地址返回給調用者。學習

 

2、hw_get_module() 實現
能夠先猜測一下,加載一個動態庫?
第一步,須要先在指定的路徑下(PATH)找到該庫;
第二步,加載該庫到內存(解析庫的內容)測試

1. 找HAL 庫ui

 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()就是找庫的接口。
庫的路徑:this

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

很顯然,庫的路徑是能夠修改的。spa

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等等。精彩的世界,未知的世界,等待着去探索。

   於此,記之。

相關文章
相關標籤/搜索