函数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)
-
{
-
// already initialized...
-
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)
-
/*
-
* Explicitly request 5/6/5
-
*/
-
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
-
-
/*
-
* Request NUM_BUFFERS screens (at lest 2 for page flipping)
-
*/
-
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) {
-
// we need at least 2 for page-flipping
-
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
-
);
-
-
/* Beware, info.pixclock might be 0 under emulation, so avoid a
-
* division-by-0 here (SIGFPE on ARM) */
-
int refreshRate = refreshQuotient > 0 ? (int)(1000000000000000LLU / refreshQuotient) : 0;
-
-
if (refreshRate == 0) {
-
// bleagh, bad info from the driver
-
refreshRate = 60*1000; // 60 Hz
-
}
这段代码再次通过IO控制命令FBIOGET_VSCREENINFO来获得系统帧缓冲区的可变属性信息,并且保存在fb_var_screeninfo结构体info中,接下来再计算设备显示屏的刷新频率。
本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/967078,如需转载请自行联系原作者