此篇筆記基於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等等。精彩的世界,未知的世界,等待着去探索。
於此,記之。