函數fb_device_open在打開fb設備的過程當中,會調用另一個函數mapFrameBuffer來得到系統幀緩衝區的信息,而且將這些信息保存在參數module所描述的一個private_module_t結構體的各個成員變量中。有了系統幀緩衝區的信息以後,函數fb_device_open接下來就能夠對前面所打開的一個fb設備的各個成員變量進行初始化。這些成員變量的含義能夠參考前面對結構體framebuffer_device_t的介紹。接下來咱們只簡單介紹一下結構體framebuffer_device_t的成員變量stride和format的初始化過程。
變量m的成員變量finfo的類型爲fb_fix_screeninfo,它是在函數mapFrameBuffer中被始化的。fb_fix_screeninfo是在內核中定義的一個結構體,用來描述設備顯示屏的固定屬性信息,其中,它的成員變量line_length用來描述顯示屏一行像素總共所佔用的字節數。
變量m的另一個成員變量info的類型爲fb_var_screeninfo,它也是在函數mapFrameBuffer中被始化的。fb_var_screeninfo也是內核中定義的一個結構體,用來描述能夠動態設置的顯示屏屬性信息,其中,它的成員變量bits_per_pixel用來描述顯示屏每個像素所佔用的位數。
這樣,咱們將m->info.bits_per_pixel的值向右移3位,就能夠獲得顯示屏每個像素所佔用的字節數。用顯示屏每個像素所佔用的字節數去除顯示屏一行像素總共所佔用的字節數m->finfo.line_length,就能夠獲得顯示屏一行有多少個像素點。這個值最終就能夠保存在前面所打開的fb設備的成員變量stride中。
當顯示屏每個像素所佔用的位數等於32的時候,那麼前面所打開的fb設備的像素格式format就會被設置爲HAL_PIXEL_FORMAT_RGBX_8888,不然的話,就會被設置爲HAL_PIXEL_FORMAT_RGB_565。另外一方面,若是在編譯的時候定義了NO_32BPP宏,即不要使用32位來描述一個像素,那麼函數fb_device_open就會強制將前面所打開的fb設備的像素格式format設置爲HAL_PIXEL_FORMAT_RGB_565。
函數mapFrameBuffer除了用來得到系統幀緩衝區的信息以外,還會將系統幀緩衝區映射到當前進程的地址空間來。在Android系統中,Gralloc模塊中的fb設備是由SurfaceFlinger服務來負責打開和管理的,而SurfaceFlinger服是運行System進程中的,所以,系統幀緩衝區其實是映射到System進程的地址空間中的。
函數mapFrameBuffer實如今文件hardware/libhardware/modules/gralloc/framebuffer.cpp,以下所示:
-
static int mapFrameBuffer(struct private_module_t* module)
-
{
-
pthread_mutex_lock(&module->lock);
-
int err = mapFrameBufferLocked(module);
-
pthread_mutex_unlock(&module->lock);
-
return err;
-
}
這個函數調用了同一個文件中的另一個函數mapFrameBufferLocked來初始化參數module以及將系統幀緩衝區映射到當前進程的地址空間來。
函數mapFrameBufferLocked的實現比較長,咱們分段來閱讀:
-
int mapFrameBufferLocked(struct private_module_t* module)
-
{
-
-
if (module->framebuffer) {
-
return 0;
-
}
-
-
char const * const device_template[] = {
-
"/dev/graphics/fb%u",
-
"/dev/fb%u",
-
0 };
-
-
int fd = -1;
-
int i=0;
-
char name[64];
-
-
while ((fd==-1) && device_template[i]) {
-
snprintf(name, 64, device_template[i], 0);
-
fd = open(name, O_RDWR, 0);
-
i++;
-
}
-
if (fd < 0)
-
return -errno;
這段代碼在首先在系統中檢查是否存在設備文件/dev/graphics/fb0或者/dev/fb0。若是存在的話,那麼就調用函數open來打開它,而且將獲得的文件描述符保存在變量fd中。這樣,接下來函數mapFrameBufferLocked就能夠經過文件描述符fd來與內核中的幀緩衝區驅動程序交互。
繼續往下看函數mapFrameBufferLocked:
-
struct fb_fix_screeninfo finfo;
-
if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1)
-
return -errno;
-
-
struct fb_var_screeninfo info;
-
if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1)
-
return -errno;
這幾行代碼分別經過IO控制命令FBIOGET_FSCREENINFO和FBIOGET_VSCREENINFO來得到系統幀緩衝區的信息,分別保存在fb_fix_screeninfo結構體finfo和fb_var_screeninfo結構體info中。
再往下看函數mapFrameBufferLocked:
-
info.reserved[0] = 0;
-
info.reserved[1] = 0;
-
info.reserved[2] = 0;
-
info.xoffset = 0;
-
info.yoffset = 0;
-
info.activate = FB_ACTIVATE_NOW;
-
-
#if defined(NO_32BPP)
-
-
-
-
info.bits_per_pixel = 16;
-
info.red.offset = 11;
-
info.red.length = 5;
-
info.green.offset = 5;
-
info.green.length = 6;
-
info.blue.offset = 0;
-
info.blue.length = 5;
-
info.transp.offset = 0;
-
info.transp.length = 0;
-
#endif
-
-
-
-
-
info.yres_virtual = info.yres * NUM_BUFFERS;
-
-
-
uint32_t flags = PAGE_FLIP;
-
if (ioctl(fd, FBIOPUT_VSCREENINFO, &info) == -1) {
-
info.yres_virtual = info.yres;
-
flags &= ~PAGE_FLIP;
-
LOGW("FBIOPUT_VSCREENINFO failed, page flipping not supported");
-
}
-
-
if (info.yres_virtual < info.yres * 2) {
-
-
info.yres_virtual = info.yres;
-
flags &= ~PAGE_FLIP;
-
LOGW("page flipping not supported (yres_virtual=%d, requested=%d)",
-
info.yres_virtual, info.yres*2);
-
}
這段代碼主要是用來設置設備顯示屏的虛擬分辨率。在前面
Android系統的開機畫面顯示過程分析
一文提到,結構體fb_var_screeninfo的成員變量xres和yres用來描述顯示屏的可視分辨率,而成員變量xres_virtual和yres_virtual用來描述顯示屏的虛擬分辨率。這裏保持可視分辨率以及虛擬分辨率的寬度值不變,而將虛擬分辨率的高度值設置爲可視分辨率的高度值的NUM_BUFFERS倍。NUM_BUFFERS是一個宏,它的值被定義爲2。這樣,咱們就能夠將系統幀緩衝區劃分爲兩個圖形緩衝區來使用,便可以經過硬件來實現雙緩衝技術。
在結構體fb_var_screeninfo中,與顯示屏的可視分辨率和虛擬分辨率相關的另外兩個成員變量是xoffset和yoffset,它們用來告訴幀緩衝區當前要渲染的圖形緩衝區是哪個,它們的使用方法能夠參考前面
Android系統的開機畫面顯示過程分析
一文。
這段代碼在設置設備顯示屏的虛擬分辨率以前,還會檢查是否認義了宏NO_32BPP。若是定義了的話,那麼就說明系統顯式地要求將幀緩衝區的像素格式設置爲HAL_PIXEL_FORMAT_RGB_565。在這種狀況下,這段代碼就會經過fb_var_screeninfo結構體info的成員變量bits_per_pixel、red、green、blue和transp來通知幀緩衝區驅動程序使用HAL_PIXEL_FORMAT_RGB_565像素格式來渲染顯示屏。
這段代碼最終是經過IO控制命令FBIOPUT_VSCREENINFO來設置設備顯示屏的虛擬分辨率以及像素格式的。若是設置失敗,即調用函數ioctl的返回值等於-1,那麼極可能是由於系統幀緩衝區在硬件上不支持雙緩衝,所以,接下來的代碼就會從新將顯示屏的虛擬分辨率的高度值設置爲可視分辨率的高度值,而且將變量flags的PAGE_FLIP位置爲0。
另外一方面,若是調用函數ioctl成功,可是最終得到的顯示屏的虛擬分辨率的高度值小於可視分辨率的高度值的2倍,那麼也說明系統幀緩衝區在硬件上不支持雙緩衝。在這種狀況下,接下來的代碼也會從新將顯示屏的虛擬分辨率的高度值設置爲可視分辨率的高度值,而且將變量flags的PAGE_FLIP位置爲0。
再繼續往下看函數mapFrameBufferLocked:
-
if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1)
-
return -errno;
-
-
uint64_t refreshQuotient =
-
(
-
uint64_t( info.upper_margin + info.lower_margin + info.yres )
-
* ( info.left_margin + info.right_margin + info.xres )
-
* info.pixclock
-
);
-
-
-
-
int refreshRate = refreshQuotient > 0 ? (int)(1000000000000000LLU / refreshQuotient) : 0;
-
-
if (refreshRate == 0) {
-
-
refreshRate = 60*1000;
-
}
這段代碼再次經過IO控制命令FBIOGET_VSCREENINFO來得到系統幀緩衝區的可變屬性信息,而且保存在fb_var_screeninfo結構體info中,接下來再計算設備顯示屏的刷新頻率。