Android对FrameBuffer驱动的使用方式是标准的,在Android的GUI系统中,通过调用FrameBuffer驱动的标准接口,实现显示设备的抽象。
FrameBuffer驱动是Android中的标准设备,但是路径稍有不同,如下所示:
/dev/graphics/fb0;
Android中直接使用标准飞FrameBuffer驱动,/dev/graphisc/中的FrameBuffer设备节点由init进程自动创建,被libui库调用。
帧缓冲设备驱动程序结构图大致如下:
计算机研究者从很早开始已经开始讨论理论上FrameBuffer的优点,但却一直苦于没有能力生产一台拥有足够内存的计算机。1969年,贝尔实验室的JoanMiller试验了第一个已知的FrameBuffer。该设备显示了一幅3位位深的图片。然而,直到20世纪70年代,集成电路的内存芯片上的进展才使得制造一个可以显示标准视频图像的FrameBuffer成为可能。
1972年,Richard Shoup在施乐帕洛阿尔托研究所里设计了SuperPaint系统,该系统拥有311,040字节的内存,并能存储640*480像素8位位深的图片。这些内存分散在16个回路电板上,每个电板上装有多个2千比特的片选芯片。要使得整个系统可以正常工作,Richard Shoup的设计必须保证整个FrameBuffer实现为307,200字节的片选注册芯片并保证能和电视信号同步。这种设计最初的缺点已经显露出来,即内存不能随机访问。当然,指定位置的内存在要求的扫描线开始扫描的时候可以访问。这样使得该系统存在着极大的潜在缺陷,导致写入数据到FrameBuffer时有33毫秒的延迟。
同时,Richard Shoup可以用SuperPaint系统制造一个早期的数字视频捕获系统。通过将输出信号同步到输入信号,Richard Shoup可以重写每个像素点的数据。Richard Shoup还曾经试验用色彩表来修改输出信号,这些色彩表允许SuperPaint系统产生多种多样的超出8位数据所包含色彩限制之外的颜色。这种体制在不久的将来成为计算机帧缓冲区中司空见惯的体制。
1974年,Evans和Sutherland发布了第一个商业版的FrameBuffer,花费15,000美元。它能够在8位灰度级模式下产生512*512个像素点的分辨率,并成为那些没有资源为自己建立FrameBuffer的研究者们的福利。不久的将来,美国纽约理工大学用3个Evans和Sutherland的系统制造了第一个24位的色彩系统,每一个FrameBuffer被连接到一个RGB色彩输出,用一个微型计算机控制三个设备作为一个设备。
20世纪70年代末,集成电路技术的飞速发展使得家庭计算机包含低色彩FrameBuffer成为可能。虽然相比在某些计算机中使用的类似于Atari 400这种更加精确的图形设备,低色彩FrameBuffer因其差劲的表现总是被嘲笑,FrameBuffer却因此在个人计算机中成为一种标准。今天,几乎所有有图形处理能力的计算机都利用FrameBuffer来产生视频信号。
80年代,FrameBuffer在高端智能终端开始流行。SGI公司,Sun公司,HP公司,DEC公司,IBM公司都为他们的计算机发布了FrameBuffer版本,这些FrameBuffer通常情况下比大部分家庭计算机具有更高的质量,应用于电视,印刷,计算机建模和3D图形技术等。
Amiga计算机由于其在图形处理方面的出色能力,创造了一个基于图形卡的广阔的FrameBuffer市场。值得一提的是,在Amiga A2500 Unix计算机中使用的图形卡,它是1991年出现的第一台实现以X11服务器作为图形环境和Open Look GUI高分辨率(1024*1024或1024*768,256色)图形界面的宿主机服务器的计算机。这块被装在A2500 Unix上的图形卡被称作A2400,它是以德州仪器公司TMS34010图形处理器为基础的图形板,时钟频率为50MHz,是一个完全只能的协处理器。A2410图形卡是Amiga公司和Lowell大学共同研发的。其他值得一提的是,Amiga的FrameBuffer是基于GVP公司的一个很有趣的视频集成系列中的Impact Vision IV24图形卡实现的,它有能力将同步锁信号、色度和电视信号混合到24位的FrameBuffer中,直到今天,它还一直在市场中活跃着,如图形卡视频捕获系统DCTV,32位图形卡Firecracker,Harlequin卡等等一直还在使用它。
FrameBuffer中文译名为帧缓冲驱动,它是出现在2.2.xx内核中的一种驱动程序接口。
Linux是工作在保护模式下,所以用户态进程是无法象DOS那样使用显卡BIOS里提供的中断调用来实现直接写屏,Linux抽象出FrameBuffer这个设备来供用户态进程实现直接写屏。FrameBuffer机制模仿显卡的功能,将显卡硬件结构抽象掉,可以通过FrameBuffer的读写直接对显存进行操作。用户可以将FrameBuffer看成是显示内存的一个映像,将其映射到进程地址空间之后,就可以直接进行读写操作,而写操作可以立即反应在屏幕上。这种操作是抽象的,统一的。用户不必关心物理显存的位置、换页机制等等具体细节,这些都是由FrameBuffer设备驱动来完成的。
但FrameBuffer本身不具备任何运算数据的能力,就只好比是一个暂时存放水的水池。CPU将运算后的结果放到这个水池,水池再将结果流到显示器,中间不会对数据做处理。应用程序也可以直接读写这个水池的内容在这种机制下,尽管FrameBuffer需要真正的显卡驱动的支持,但所有显示任务都有CPU完成,因此CPU负担很重。
帧缓冲驱动应用广泛,在 linux 的桌面系统中,X window 服务器就是利用帧缓冲进行窗口的绘制。尤其是通过帧缓冲可显示汉字点阵,成为Linux 汉化的唯一可行方案。
在开发者看来,FrameBuffer 本质上是一块显示缓存,往显示缓存中写入特定格式的数据就意味着向屏幕输出内容。所以说FrameBuffer就是一块白板。例如对于初始化为16位色的FrameBuffer来说,FrameBuffer 中的两个字节代表屏幕上一个点,从上到下,从左至右,屏幕位置与内存地址是顺序的线性关系。
帧缓存可以在系统存储器(内存)的任意位置,视频控制器通过访问帧缓存来刷新屏幕。帧缓存也叫刷新缓存FrameBuffer或RefreshBuffer,这里的帧(frame)是指整个屏幕范围。
帧缓存有个地址,是在内存里。我们通过不停的向 FrameBuffer 中写入数据, 显示控制器就自动的从FrameBuffer中取数据并显示出来。全部的图形都共享内存中同一个帧缓存。
CPU 指定显示控制器工作,则显示控制器根据CPU的控制到指定的地方去取数据和指令,目前的数据一般是从显存里取,如果显存里存不下,则从内存里取, 内存也放不下,则从硬盘里取,当然也不是内存放不下,而是为了节省内存的话, 可以放在硬盘里,然后通过指令控制显示控制器去取。帧缓存 FrameBuffer里面存储的东西是一帧一帧的, 显卡会不停的刷新 FrameBuffer, 这每一帧如果不捕获的话,则会被丢弃,也就是说是实时的。这每一帧不管是保存在内存还是显存里, 都是一个显性的信息,这每一帧假设是800x600的分辨率,则保存的是800x600个像素点和颜色值。
framebuffer的设备文件一般是 /dev/fb0、/dev/fb1 等等。
可以用命令: #dd if=/dev/zero of=/dev/fb 清空屏幕。
如果显示模式是 1024x768-8 位色,用命令:$ dd if=/dev/zero of=/dev/fb0 bs=1024 count=768 清空屏幕;
用命令: #dd if=/dev/fb of=fbfile 可以将fb中的内容保存下来;
可以重新写回屏幕: #dd if=fbfile of=/dev/fb;
在使用Framebuffer时,Linux是将显卡置于图形模式下的。
在应用程序中,一般通过将 FrameBuffer 设备映射到进程地址空间的方式使用,比如下面的程序就打开 /dev/fb0 设备,并通过 mmap 系统调用进行地址映射,随后用memset 将屏幕清空(这里假设显示模式是 1024x768-8 位色模式,线性内存模式):
int fb;
unsigned char*fb_mem;
fb = open("/dev/fb0", O_RDWR);
fb_mem = mmap(NULL, 1024*768, PROT_READ|PROT_WRITE,MAP_SHARED,fb,0);
memset (fb_mem,0, 1024*768); //这个命令应该只有在root可以执
FrameBuffer 设备还提供了若干 ioctl 命令,通过这些命令,可以获得显示设备的一些固定信息(比如显示内存大小)、与显示模式相关的可变信息(比如分辨率、象素结构、每扫描线的字节宽度),以及伪彩色模式下的调色板信息等等。
通过 FrameBuffer 设备,还可以获得当前内核所支持的加速显示卡的类型(通过固定信息得到),这种类型通常是和特定显示芯片相关的。比如目前最新的内核(2.4.9)中,就包含有对 S3、Matrox、nVidia、3Dfx 等等流行显示芯片的加速支持。在获得了加速芯片类型之后,应用程序就可以将 PCI 设备的内存I/O(memio)映射到进程的地址空间。这些 memio 一般是用来控制显示卡的寄存器,通过对这些寄存器的操作,应用程序就可以控制特定显卡的加速功能。
PCI 设备可以将自己的控制寄存器映射到物理内存空间,而后,对这些控制寄存器的访问,给变成了对物理内存的访问。因此,这些寄存器又被称为"memio"。一旦被映射到物理内存,Linux 的普通进程就可以通过mmap 将这些内存 I/O 映射到进程地址空间,这样就可以直接访问这些寄存器了。
当然,因为不同的显示芯片具有不同的加速能力,对memio 的使用和定义也各自不同,这时,就需要针对加速芯片的不同类型来编写实现不同的加速功能。比如大多数芯片都提供了对矩形填充的硬件加速支持,但不同的芯片实现方式不同,这时,就需要针对不同的芯片类型编写不同的用来完成填充矩形的函数。
FrameBuffer 只是一个提供显示内存和显示芯片寄存器从物理内存映射到进程地址空间中的设备。所以,对于应用程序而言,如果希望在 FrameBuffer 之上进行图形编程,还需要自己动手完成其他许多工作。
Framebuffer的具体操作流程如下:
1)打开/dev/fb设备文件;
2)用ioctrl操作取得当前显示屏幕的参数,如屏幕分辨率,每个像素点的比特数。根据屏幕参数可计算屏幕缓冲区的大小。
3)将屏幕缓冲区映射到用户空间。
4)映射后就可以直接读写屏幕缓冲区,进行绘图和图片显示了。
FrameBuffer设备驱动基于如下两个文件:
1)linux/include/linux/fb.h
2)linux/drivers/video/fbmem.c
下面分析这两个文件。
1. fb.h
几乎主要的结构都是在这个中文件定义的。这些结构包括:
1)fb_var_screeninfo
这个结构描述了显示卡的特性。
记录了帧缓冲设备和指定显示模式的可修改信息。它包括显示屏幕的分辨率、每个像素的比特数和一些时序变量。其中变量xres定义了屏幕一行所占的像素数,yres定义了屏幕一列所占的像素数,bits_per_pixel定义了每个像素用多少个位来表示。
fb_var_screeninfo结构
struct fb_var_screeninfo{
__u32 xres; /* visible resolution */可见分辨率宽的像素个数
__u32 yres;
__u32 xres_virtual; /* virtual resolution */虚拟分辨率
__u32 yres_virtual;
__u32 xoffset; /* offset from virtual to visible */从虚拟到可见分辨率的偏移
__u32 yoffset; /* resolution */
__u32 bits_per_pixel; /* guess what */每一象素的bit数
__u32 grayscale; /* != 0 Graylevels instead of colors */等于零就成黑白
struct fb_bitfield red; /* bitfield in fb mem if true color, */真彩的bit机构
struct fb_bitfield green; /* else only length is significant */
struct fb_bitfield blue;
struct fb_bitfield transp; /* transparency */ 透明
__u32 nonstd; /* != 0 Non standard pixel format */不是标准格式
__u32 activate; /* see FB_ACTIVATE_* */
__u32 height; /* height of picture in mm */内存中的图像高度
__u32 width; /* width of picture in mm */内存中的图像宽度
__u32 accel_flags; /* (OBSOLETE) see fb_info.flags */加速标志
/* Timing: All values in pixclocks, except pixclock (of course) */时序-_-这些部分就是显示器的显示方法了,可以找相关的资料看看
__u32 pixclock; /* pixel clock in ps (pico seconds) */
__u32 left_margin; /* time from sync to picture */
__u32 right_margin; /* time from picture to sync */
__u32 upper_margin; /* time from sync to picture */
__u32 lower_margin;
__u32 hsync_len; /* length of horizontal sync */水平可视区域
__u32 vsync_len; /* length of vertical sync */垂直可视区域
__u32 sync; /* see FB_SYNC_* */
__u32 vmode; /* see FB_VMODE_* */
__u32 rotate; /* angle we rotate counter clockwise */
__u32 reserved[5]; /* Reserved for future compatibility */备留以后开发
}
2)fb_fix_screeninfon
这个结构在显卡被设定模式后创建,它描述显示卡的属性,并且系统运行时不能被修改;比如FrameBuffer内存的起始地址。它依赖于被设定的模式,当一个模式被设定后,内存信息由显示卡硬件给出,内存的位置等信息就不可以修改。其记录用户不能修改的显示控制器的参数,如屏幕缓冲区的物理地址,长度。当对帧缓冲设备进行映射操作的时候,就是从fb_fix_screeninfo中取得缓冲区物理地址的。
fb_fix_screeninfo结构
struct fb_fix_screeninfo {
char id[16]; /* identification string eg "TT Builtin" */
unsigned long smem_start; /* Start of frame buffer mem */描述缓冲区起始地址
/* (physical address) */物理地址
__u32 smem_len; /* Length of frame buffer mem */描述缓冲区长度
__u32 type; /* see FB_TYPE_* */描述fb类型,比如TFT或STN类型
__u32 type_aux; /* Interleave for interleaved Planes */
__u32 visual; /* see FB_VISUAL_* */ 描述显示颜色是真彩色,伪彩色还是单色
__u16 xpanstep; /* zero if no hardware panning */
__u16 ypanstep; /* zero if no hardware panning */
__u16 ywrapstep; /* zero if no hardware ywrap */
__u32 line_length; /* length of a line in bytes */一行的字节表示
unsigned long mmio_start; /* Start of Memory Mapped I/O */内存映射的I/O起始
/* (physical address) */物理地址
__u32 mmio_len; /* Length of Memory Mapped I/O */I/O的大小
__u32 accel; /* Indicate to driver which */可用的加速类型
/* specific chip/card we have */
__u16 reserved[3]; /* Reserved for future compatibility */
};
3) fb_cmap
描述设备无关的颜色映射信息。可以通过FBIOGETCMAP 和 FBIOPUTCMAP 对应的ioctl操作设定或获取颜色映射信息。
struct fb_cmap {
__u32 start; /* First entry */ 第一个入口
__u32 len; /* Number of entries */ 入口的数字
__u16 *red; /* Red values */ 红
__u16 *green;
__u16 *blue;
__u16 *transp; /* transparency, can be NULL */ 透明,可以为零
};
4) fb_info
定义当显卡的当前状态;fb_info结构仅在内核中可见,在这个结构中有一个fb_ops指针, 指向驱动设备工作所需的函数集。
struct fb_info {
int node;
int flags;
struct mutex lock; /* Lock for open/release/ioctl funcs */
struct mutex mm_lock; /* Lock for fb_mmap and smem_* fields */
struct fb_var_screeninfo var; /* Current var */
struct fb_fix_screeninfo fix; /* Current fix */
struct fb_monspecs monspecs; /* Current Monitor specs */
struct work_struct queue; /* Framebuffer event queue */
struct fb_pixmap pixmap; /* Image hardware mapper */
struct fb_pixmap sprite; /* Cursor hardware mapper */
struct fb_cmap cmap; /* Current cmap */
struct list_head modelist; /* mode list */
struct fb_videomode *mode; /* current mode */
#ifdef CONFIG_FB_BACKLIGHT
/* assigned backlight device */
/* set before framebuffer registration,
remove after unregister */
struct backlight_device *bl_dev;
/* Backlight level curve */
struct mutex bl_curve_mutex;
u8 bl_curve[FB_BACKLIGHT_LEVELS];
#endif
#ifdef CONFIG_FB_DEFERRED_IO
struct delayed_work deferred_work;
struct fb_deferred_io *fbdefio;
#endif
struct fb_ops *fbops;
struct device *device; /* This is the parent */
struct device *dev; /* This is this fb device */
int class_flag; /* private sysfs flags */
#ifdef CONFIG_FB_TILEBLITTING
struct fb_tile_ops *tileops; /* Tile Blitting */
#endif
char __iomem *screen_base; /* Virtual address */
unsigned long screen_size; /* Amount of ioremapped VRAM or 0 */
void *pseudo_palette; /* Fake palette of 16 colors */
#define FBINFO_STATE_RUNNING 0
#define FBINFO_STATE_SUSPENDED 1
u32 state; /* Hardware state i.e suspend */
void *fbcon_par; /* fbcon use-only private area */
/* From here on everything is device dependent */
void *par;
/* we need the PCI or similiar aperture base/size not
smem_start/size as smem_start may just be an object
allocated inside the aperture so may not actually overlap */
struct apertures_struct {
unsigned int count;
struct aperture {
resource_size_t base;
resource_size_t size;
} ranges[0];
} *apertures;
}
fb_info结构是Linux为帧缓冲设备定义的驱动层接口,用于用户在内核空间的调用。它不仅包含了底层函数,而且还有记录设备状态的数据。每个帧缓冲设备都有一个fb_info结构相对应。
5) struct fb_ops
用户应用可以使用ioctl()系统调用来操作设备,这个结构就是用一支持ioctl()的这些操作的。
它是提供给底层设备驱动的一个接口。通常我们编写字符驱动的时候,要填写一个file_operations结构体,并使用register_chrdev()注册之,以告诉Linux如何操控驱动。当我们编写一个FrameBuffer的时候,就要依照Linux FrameBuffer编程的套路,填写fb_ops结构体。这个fb_ops也就相当于通常的file_operations结构体。
帧缓冲设备也属于字符设备(文件设备的一种,还有块设备),要实现“文件层-驱动层”的接口方式来对LCD进行驱动就必须定义一个类似于File_operationes可实现文件设备操作数据结构fb_ops,然后编写子函数对fb_ops的各个域进行填充:
struct fb_ops {
struct module *owner;
int (*fb_open)(struct fb_info *info, int user); //打开帧缓冲设备,类似文件操作的open方法
int (*fb_release)(struct fb_info *info, int user); //关闭帧缓冲设备,类似文件操作的release方法
ssize_t (*fb_read)(struct file *file, char __user *buf, size_t count, loff_t *ppos); //帧缓冲设备的读操作,类似文件操作的read方法
ssize_t (*fb_write)(struct file *file, const char __user *buf,size_t count, loff_t *ppos); //帧缓冲设备的写操作,类似文件操作的write方法
int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info); //检查帧缓冲设备的可变参数,如果不合法,然后修正这些参数
int (*fb_set_par)(struct fb_info *info); //设置视频模式根据info->var成员
/* set color register */
int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,unsigned blue, unsigned transp, struct fb_info *info);
/* set color registers in batch */
int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);
/* blank display */
int (*fb_blank)(int blank, struct fb_info *info);
/* pan display */
int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);
/* Draws a rectangle */画一个矩形区域
void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
/* Copy data from area to another */拷贝数据从一个区域到另一个区域
void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
/* Draws a image to the display */把控制台过来的字符等信息画在lcd上
void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);
/* Draws cursor */用于控制光标的位置,是和鼠标相关
int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);
/* Rotates the display */
void (*fb_rotate)(struct fb_info *info, int angle);
/* wait for blit idle, optional */
int (*fb_sync)(struct fb_info *info);
/* perform fb specific ioctl (optional) */帧缓冲设备的具体ioctl操作
int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,unsigned long arg);
/* Handle 32bit compat ioctl (optional) */
int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,unsigned long arg);
/* perform fb specific mmap */帧缓冲设备的内存映射操作
int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
/* get capability given var */
void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,struct fb_var_screeninfo *var);
/* teardown any resources to do with this framebuffer */
void (*fb_destroy)(struct fb_info *info);
};
这个结构中的每一个字段都必须指向驱动程序中实现特定操作的函数,对于不支持的操作,对应的字段可以被置为NULL,或留到后续开发时载添加。
6) structfbgen_hwswitch
这个结构 fbgen_hwswitch抽象了硬件的操作.虽然它不是必需的,但有时候很有用。
2. fbmem.c
fbmem.c 处于FrameBuffer设备驱动技术的中心位置。它为上层应用程序提供系统调用也为下一层的特定硬件驱动提供接口;那些底层硬件驱动需要用到这儿的接口来向系统内核注册它们自己。fbmem.c 为所有支持FrameBuffer的设备驱动提供了通用的接口,避免重复工作。
1) 全局变量
struct fb_info*registered_fb[FB_MAX];
intnum_registered_fb;
这两变量记录了所有fb_info 结构的实例,fb_info 结构描述显卡的当前状态,所有设备对应的fb_info 结构都保存在这个数组中,当一个FrameBuffer设备驱动向系统注册自己时,其对应的fb_info 结构就会添加到这个结构中,同时num_registered_fb 为自动加1。
2)fbmem.c 实现了如下函数
fbmem.c文件实现了帧缓冲驱动公共的调用 (核心)
int register_framebuffer(structfb_info *fb_info); //帧缓冲设备的注册函数
int unregister_framebuffer(structfb_info *fb_info); //帧缓冲设备的注销函数
这两个是提供给下层FrameBuffer设备驱动的接口,设备驱动通过这两函数向系统注册或注销自己。几乎底层设备驱动所要做的所有事情就是填充fb_info结构然后向系统注册或注销它。
下面是fbmem.c主要程序结构以及代码
1、FrameBuffer驱动的统一管理
fbmem.c实现了Linux FrameBuffer的中间层,任何一个FrameBuffer驱动,在系统初始化时,必须向fbmem.c注册,即需要调用register_framebuffer()函数,在这个过程中,设备驱动的信息将会存放入名称为registered_fb数组中,这个数组定义为
struct fb_info *registered_fb[FB_MAX];
int num_registered_fb;
它是类型为fb_info的数组,另外num_register_fb则存放了注册过的设备数量。
我们分析一下register_framebuffer的代码。
int register_framebuffer(struct fb_info *fb_info)
{
int i;
struct fb_event event;
struct fb_videomode mode;
if (num_registered_fb == FB_MAX) return -ENXIO; /* 超过最大数量 */
num_registered_fb++;
for (i = 0 ; i < FB_MAX; i++)
if (!registered_fb[i]) break; /* 找到空余的数组空间 */
fb_info->node = i;
fb_info->dev = device_create(fb_class, fb_info->device,
MKDEV(FB_MAJOR, i), "fb%d", i); /* 为设备建立设备节点 */
if (IS_ERR(fb_info->dev)) {
…………
} else{
fb_init_device(fb_info); /* 初始化改设备 */
}
…………
return 0;
}
从上面的代码可知,当FrameBuffer驱动进行注册的时候,它将驱动的fb_info结构体记录到全局数组registered_fb中,并动态建立设备节点,进行设备的初始化。注意,这里建立的设备节点的次设备号就是该驱动信息在registered_fb存放的位置,即数组下标i 。在完成注册之后,fbmem.c就记录了驱动的fb_info。这样我们就有可能实现fbmem.c对全部FrameBuffer驱动的统一处理。
2 、 实现消息的分派
fbmem.c实现了对系统全部FrameBuffer设备的统一管理。当用户尝试使用一个特定的FrameBuffer时,fbmem.c怎么知道该调用那个特定的设备驱动呢?
我们知道,Linux是通过主设备号和次设备号,对设备进行唯一标识。不同的FrameBuffer设备向fbmem.c注册时,程序分配给它们的主设备号是一样的,而次设备号是不一样的。于是我们就可以通过用户指明的次设备号,来觉得具体该调用哪一个FrameBuffer驱动。下面通过分析fbmem.c的fb_open()函数来说明。(注:一般我们写FrameBuffer驱动不需要实现open函数,这里只是说明函数流程。)
static int fb_open(struct inode *inode, struct file *file){
int fbidx = iminor(inode);
struct fb_info *info;
int res;
/* 得到真正驱动的函数指针 */
if (!(info = registered_fb[fbidx])) return -ENODEV;
if (info->fbops->fb_open) {
res = info->fbops->fb_open(info,1); //调用驱动的open()
if (res) module_put(info->fbops->owner);
}
return res;
}
当用户打开一个FrameBuffer设备的时,将调用这里的fb_open()函数。传进来的inode就是欲打开设备的设备号,包括主设备和次设备号。fb_open函数首先通过iminor()函数取得次设备号,然后查全局数组registered_fb得到设备的fb_info信息,而这里面存放了设备的操作函数集fb_ops。这样,我们就可以调用具体驱动的fb_open() 函数,实现open的操作。下面给出了一个LCD驱动的open() 函数的调用流程图,用以说明上面的步骤。
下图给出了Linux FrameBuffer的总体结构,作为这一部分的总结。
最早解释多缓冲区如何工作的方式,是通过一个现实生活中的实例来解释的。在一个阳光明媚的日子,你想将水池里的水换掉,而又找不到水管的时候,你就只能用木桶来灌满水池。当木桶被水龙头注满的,关掉水龙头,走到水池旁边,将水到进去,然后走回到水龙头旁边继续重复上述工作,如此往复直到将水池灌满。这就类似单缓冲工作过程。当你想将木桶里的水倒出的时候,你必须关掉水龙头。
现在假设你用两个木桶来做上面的工作。你会注满第一个木桶然后将第二个木桶换到水龙头下面,这样,在第二个水桶注满的时间内,你就可以将第一个木桶里面的水倒进水池里面,当你回来的时候,你只需要再将第一个木桶换下第二个注满水木桶,当第一个木桶开始注水的时候你就将第二个木桶里面的水倒进水池里面。重复这个过程直到水池被注满。很容易看得到用这种技术注满水池将会更快,同时也节省了很多等待木桶被注满的时间,而这段时间里你什么也做不了,而水龙头也就不用等待从木桶被注满到你回来的这段时间了。
当你雇佣另外一个人来搬运一个被注满的木桶时,这就有点类似于三个缓冲区的工作原理。如果将搬运木桶的的时间很长,你可以用更多的木桶,雇佣更多的人,这样水龙头就会一直开着注满木桶了。
在计算机图形学中,双缓冲是一种画图技术,使用这种技术可以使得画图没有(至少是减少)闪烁、撕裂等不良效果,并减少等待时间。
双缓冲机制的原理大概是:所有画图操作将它们画图的结果保存在一块系统内存区域中,这块区域通常被称作“后缓冲区(back buffer)”,当所有的绘图操作结束之后,将整块区域复制到显示内存中,这个复制操作通常要跟显示器的光栈束同步,以避免撕裂。双缓冲机制必须要求有比单缓冲更多的显示内存和CPU消耗时间,因为“后缓冲区”需要显示内存,而复制操作和等待同步需要CPU时间。
基于双缓冲机制可以实现页交换。
页交换初始状态如图所示:
如上图所示,此时由于处于初始状态,画图操作的结果都在后缓冲区中,而屏幕上显示的则是前缓冲区中的内容。此时画图操作尚未完成。
画图操作完成之后,页转换操作开始执行,示意图如图所示:
如上图所示,画图操作结束,下一个画图操作的结果保存对象指向前缓冲区,屏幕的显示对象指向后缓冲区,此时前缓冲区变成实际意义上的后缓冲区,后缓冲区变成实际意义上的前缓冲去,即实现“页交换”操作。