目录
简介
FrameBuffer,帧缓冲,简称fb,也叫显存,下文以fb代表framebuffer。该子系统是内核针对显示系统提供出来的一个统一接口,应用层可以直接操作显存更改显示。
下面是一张示意图,左侧的图片通过framebuffer显示到panel上。写到framebuffer的数据是RGB数据或者ARGB数据。
本文将从以下2个层面说明fb:
- 驱动层面
- 应用层面
驱动层
驱动层,主要从probe函数出发,重点分析2个问题:
- 设备树中配置的时序参数整个“生命”历程
- 整个架构
时序数据流
1、fb架构主要涉及4个结构体,层层包含的关系:
struct mxsfb_info:平台私有结构体
struct fb_info *fb_info
struct fb_var_screeninfo var:设备树中的时序最后会存在 struct fb_var_screeninfo var 结构体中,应用层可以通过接口获取
struct fb_fix_screeninfo fix:包含fb物理地址、长度等信息
2、结构体之间的关系
2.1、从设备中解析出来,放到 struct display_timings结构体中,经过一系列的转换最后放到struct fb_info结构体中
2.2、解析出来的消隐参数配置到寄存器中
小结:
分析完整个数据流可以发现:fb在内核中被抽象成一个fb_info结构体
架构
1、一个/dev/fb0、一个framebuffer、一套时序参数,这3者是怎么进行绑定的?
1.1、从open函数开始跟踪,发现如下逻辑:
1.2、答案是:通过次设备号绑定
驱动注册的时候是根据次设备号进行注册的,那打开的时候就可以根据次设备号找到对应的fb_info,进而可以获取到对应的帧缓冲区、对应的操作函数集。
2、已经知道整个框架的数据结构、以及之间的联系。下面看下整个架构图,图是网上的,链接是:framebuffer 入门介绍
2.1、基于上图,再补充一二,如下:
2.2、核心层和平台对应的函数集如下:
static const struct file_operations fb_fops = {
.owner = THIS_MODULE,
.read = fb_read,
.write = fb_write,
.unlocked_ioctl = fb_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = fb_compat_ioctl,
#endif
.mmap = fb_mmap,
.open = fb_open,
.release = fb_release,
#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
.get_unmapped_area = get_fb_unmapped_area,
#endif
#ifdef CONFIG_FB_DEFERRED_IO
.fsync = fb_deferred_io_fsync,
#endif
.llseek = default_llseek,
};
static struct fb_ops mxsfb_ops = {
.owner = THIS_MODULE,
.fb_check_var = mxsfb_check_var,
.fb_set_par = mxsfb_set_par,
.fb_setcolreg = mxsfb_setcolreg,
.fb_ioctl = mxsfb_ioctl,
.fb_blank = mxsfb_blank,
.fb_pan_display = mxsfb_pan_display,
.fb_mmap = mxsfb_mmap,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
};
3、如前文所述,应用调用fb接口可以直接更改显存、更改画面显示。该功能主要在于fb_mmp函数,下面是fb_mmp函数的特点:
- 显存的物理地址会被强制进行页对齐
- 映射的长度也会被强制性调整为页的整数倍
- 前2点受限于内核只能在页表一级上对虚拟地址进行管理
应用层
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <errno.h>
#include <string.h>
#define BOOT_LOGO_SHOW "/mnt/nfs0/logo"
#define BOOT_LOGO1_SHOW "/mnt/nfs0/logo1"
#define BOOT_LOGO2_SHOW "/mnt/nfs0/logo2"
#define BOOT_LOGO3_SHOW "/mnt/nfs0/logo3"
unsigned char hilogo[4][1280*720*4];
int main()
{
int fp = 0;
int lfd[4];
int ret;
int i =0;
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
int screensize = 0;
char *fbp = 0;
int x = 0, y = 0;
int location = 0;
int bytes_per_pixel;
fp = open("/dev/fb0", O_RDWR); //cyy commment: 打开要操作的fb
if(fp < 0) {
printf("open failed\n");
}
if(ioctl(fp, FBIOGET_FSCREENINFO, &finfo)) { //cyy commment: 获取一行长度等信息
perror("ioctl");
}
if(ioctl(fp, FBIOGET_VSCREENINFO, &vinfo)) { //cyy comment: 获取分辨率等信息
perror("ioctl");
}
printf("x = %d y = %d vinfo.xoffset=%d, vinfo.yoffset=%d, finfo.line_length=%d\n", vinfo.xres, vinfo.yres, vinfo.xoffset, vinfo.yoffset, finfo.line_length);
bytes_per_pixel = vinfo.bits_per_pixel/8;
screensize = vinfo.xres * vinfo.yres * bytes_per_pixel;
printf("x = %d y = %d bytes_per_pixel = %d\n", vinfo.xres, vinfo.yres, bytes_per_pixel);
printf("scrrensize = %d\n", screensize);
fbp = (char*) mmap(0, screensize, PROT_READ |PROT_WRITE, MAP_SHARED, fp, 0x0); //cyy commment: 将显存的物理地址映射到应用空间
if(fbp < 0) {
perror("mmap");
}
for(y = 0; y < vinfo.yres; y++) { //cyy commment: 对显存进行更改,显示不同图片
for(x=0 ; x<vinfo.xres / 2; x++) {
location = x * bytes_per_pixel + y * finfo.line_length;
*(fbp + location) = 0xff;
*(fbp +location + 1) = 0xff;
*(fbp + location + 2) = 0;
*(fbp + location + 3) = 0xff;
}
for(; x<vinfo.xres; x++) {
location = x * bytes_per_pixel + y * finfo.line_length;
*(fbp + location) = 0x00;
*(fbp +location + 1) = 0x0;
*(fbp + location + 2) = 0xff;
*(fbp + location + 3) = 0xff;
}
}
munmap(fbp, screensize); //cyy commment: 取消映射
close(fp); //cyy commment: 关闭fb
}
实例
实际测试时,左侧图片会显示到右侧,如下所示:
原因如下:
fb架构优劣势
- fb的优势是:使用户态可以直接写屏;LCD的HAL<Hard Abstraction Layer>
- fb的劣势:如果写的慢的话,图像显示会闪烁