2. FrameBuffer 核心数据结构详解
FrameBuffer 子系统的核心围绕三个关键数据结构展开:fb_info(设备描述符)、fb_ops(操作函数表)、fb_var_screeninfo(可变参数)和 fb_fix_screeninfo(固定参数)。理解这些结构是掌握 FrameBuffer 驱动开发的基础,也是后续迁移到 DRM 时对比差异的关键。
学习提示: 这四个结构体定义在
include/linux/fb.h 中。建议在阅读本节时,同时打开内核源码中的该头文件对照学习。
2.1 整体架构关系
下图展示了四个核心数据结构在 FrameBuffer 驱动中的组织关系:
2.2 struct fb_info — 设备描述符
struct fb_info 是 FrameBuffer 驱动的核心结构体,每个 Framebuffer 设备对应一个实例。它包含了设备的所有信息:操作函数、屏幕参数、设备状态、私有数据等。
2.2.1 关键字段
| 字段名 | 类型 | 描述 |
|---|---|---|
fbops |
struct fb_ops * |
指向操作函数表的指针 |
var |
struct fb_var_screeninfo |
可变屏幕参数(分辨率、位深等) |
fix |
struct fb_fix_screeninfo |
固定屏幕参数(显存地址、行长等) |
screen_base |
char __iomem * |
显存虚拟地址基址 |
screen_size |
unsigned long |
显存大小(字节) |
device |
struct device * |
关联的 Linux 设备结构 |
par |
void * |
驱动私有数据指针 |
state |
int |
设备状态(如 FBINFO_STATE_RUNNING) |
flags |
u32 |
设备标志位 |
node |
int |
设备编号(如 /dev/fb0 的 0) |
2.2.2 驱动初始化示例
static struct fb_info *my_fb_info;
static int myfb_probe(struct platform_device *pdev)
{
int ret;
// 1. 分配 fb_info 结构
my_fb_info = framebuffer_alloc(0, &pdev->dev);
if (!my_fb_info)
return -ENOMEM;
// 2. 设置操作函数表
my_fb_info->fbops = &myfb_ops;
// 3. 设置固定参数
strcpy(my_fb_info->fix.id, "myfb");
my_fb_info->fix.smem_start = dma_alloc_coherent(...);
my_fb_info->fix.smem_len = 800 * 600 * 4; // 32bpp
my_fb_info->fix.line_length = 800 * 4;
// 4. 设置可变参数
my_fb_info->var.xres = 800;
my_fb_info->var.yres = 600;
my_fb_info->var.bits_per_pixel = 32;
// 5. 设置显存地址
my_fb_info->screen_base = ioremap(my_fb_info->fix.smem_start,
my_fb_info->fix.smem_len);
my_fb_info->screen_size = my_fb_info->fix.smem_len;
// 6. 注册 FrameBuffer 设备
ret = register_framebuffer(my_fb_info);
if (ret) {
framebuffer_release(my_fb_info);
return ret;
}
return 0;
}
注意事项: 在驱动卸载时,必须先调用
unregister_framebuffer() 注销设备,再调用 framebuffer_release() 释放内存。顺序不可颠倒,否则可能导致内核崩溃。
2.3 struct fb_ops — 操作函数表
struct fb_ops 定义了 FrameBuffer 设备的所有操作接口。驱动开发者需要根据硬件特性实现其中的回调函数。内核通过这个函数表来操作具体的显示硬件。
2.3.1 核心回调函数
| 回调函数 | 原型 | 说明 |
|---|---|---|
fb_open |
int (*fb_open)(struct fb_info *, int) |
设备打开时调用,可用于初始化 |
fb_release |
int (*fb_release)(struct fb_info *, int) |
设备关闭时调用,可用于清理 |
fb_read |
ssize_t (*fb_read)(struct fb_info *, char __user *, size_t, loff_t *) |
从显存读取数据 |
fb_write |
ssize_t (*fb_write)(struct fb_info *, const char __user *, size_t, loff_t *) |
向显存写入数据 |
fb_check_var |
int (*fb_check_var)(struct fb_var_screeninfo *, struct fb_info *) |
检查可变参数是否合法 |
fb_set_par |
int (*fb_set_par)(struct fb_info *) |
根据 var 设置硬件寄存器 |
fb_setcolreg |
int (*fb_setcolreg)(unsigned, unsigned, unsigned, unsigned, unsigned, struct fb_info *) |
设置颜色寄存器(伪彩色模式) |
fb_blank |
int (*fb_blank)(int, struct fb_info *) |
控制屏幕开关/省电模式 |
fb_pan_display |
int (*fb_pan_display)(struct fb_var_screeninfo *, struct fb_info *) |
实现显示平移(panning) |
fb_mmap |
int (*fb_mmap)(struct fb_info *, struct vm_area_struct *) |
实现显存 mmap 映射 |
fb_ioctl |
int (*fb_ioctl)(struct fb_info *, unsigned int, unsigned long) |
处理自定义 ioctl 命令 |
2.3.2 典型实现模式
static struct fb_ops myfb_ops = {
.owner = THIS_MODULE,
.fb_open = myfb_open,
.fb_release = myfb_release,
.fb_read = myfb_read,
.fb_write = myfb_write,
.fb_check_var = myfb_check_var,
.fb_set_par = myfb_set_par,
.fb_setcolreg = myfb_setcolreg,
.fb_blank = myfb_blank,
.fb_fillrect = cfb_fillrect, // 使用通用辅助函数
.fb_copyarea = cfb_copyarea, // 使用通用辅助函数
.fb_imageblit = cfb_imageblit, // 使用通用辅助函数
};
static int myfb_check_var(struct fb_var_screeninfo *var,
struct fb_info *info)
{
// 检查分辨率是否在硬件支持范围内
if (var->xres > 1920 || var->yres > 1080)
return -EINVAL;
// 检查位深是否支持
if (var->bits_per_pixel != 16 && var->bits_per_pixel != 32)
return -EINVAL;
// 调整 xres_virtual 和 yres_virtual
if (var->xres_virtual < var->xres)
var->xres_virtual = var->xres;
if (var->yres_virtual < var->yres)
var->yres_virtual = var->yres;
return 0;
}
最佳实践: 对于
fb_fillrect、fb_copyarea、fb_imageblit 这三个图形加速操作,如果硬件没有专门的加速引擎,可以直接使用内核提供的通用软件实现(cfb_fillrect、cfb_copyarea、cfb_imageblit)。这些函数在 include/linux/fb.h 中声明。
2.4 struct fb_var_screeninfo — 可变屏幕参数
struct fb_var_screeninfo 描述用户空间可以通过 ioctl 修改的显示参数。应用程序通过 FBIOPUT_VSCREENINFO 和 FBIOGET_VSCREENINFO 命令来设置和获取这些参数。
2.4.1 关键字段
| 字段名 | 类型 | 说明 |
|---|---|---|
xres |
__u32 |
可见分辨率:宽度(像素) |
yres |
__u32 |
可见分辨率:高度(像素) |
xres_virtual |
__u32 |
虚拟分辨率:宽度(像素),≥ xres |
yres_virtual |
__u32 |
虚拟分辨率:高度(像素),≥ yres |
xoffset |
__u32 |
虚拟到可见的 X 偏移 |
yoffset |
__u32 |
虚拟到可见的 Y 偏移 |
bits_per_pixel |
__u32 |
每像素位数(如 16、24、32) |
grayscale |
__u32 |
灰度标志:0=彩色,非0=灰度 |
red |
struct fb_bitfield |
红色通道位域信息 |
green |
struct fb_bitfield |
绿色通道位域信息 |
blue |
struct fb_bitfield |
蓝色通道位域信息 |
transp |
struct fb_bitfield |
透明度/Alpha 通道位域信息 |
nonstd |
__u32 |
非标准格式标志(0=标准 RGB) |
activate |
__u32 |
激活模式(立即/下次刷新) |
height |
__u32 |
物理高度(毫米) |
width |
__u32 |
物理宽度(毫米) |
pixclock |
__u32 |
像素时钟(皮秒) |
left_margin |
__u32 |
行同步前沿(像素) |
right_margin |
__u32 |
行同步后沿(像素) |
upper_margin |
__u32 |
帧同步前沿(行) |
lower_margin |
__u32 |
帧同步后沿(行) |
hsync_len |
__u32 |
行同步脉冲长度(像素) |
vsync_len |
__u32 |
帧同步脉冲长度(行) |
sync |
__u32 |
同步标志(FB_SYNC_*) |
vmode |
__u32 |
视频模式(FB_VMODE_*) |
rotate |
__u32 |
旋转角度(0/90/180/270) |
2.4.2 位域结构 fb_bitfield
struct fb_bitfield {
__u32 offset; // 颜色分量在像素中的起始位偏移
__u32 length; // 颜色分量的位数
__u32 msb_right; // MSB 在右侧标志(非0表示 MSB 在最低位)
};
// 示例:32bpp RGB888 格式
// red: offset=16, length=8, msb_right=0
// green: offset=8, length=8, msb_right=0
// blue: offset=0, length=8, msb_right=0
2.5 struct fb_fix_screeninfo — 固定屏幕参数
struct fb_fix_screeninfo 描述硬件固定的参数,用户空间只能读取(通过 FBIOGET_FSCREENINFO),不能修改。这些参数在驱动初始化时设置,通常在整个生命周期内保持不变。
2.5.1 关键字段
| 字段名 | 类型 | 说明 |
|---|---|---|
id |
char[16] |
设备名称标识符 |
smem_start |
unsigned long |
显存物理起始地址 |
smem_len |
__u32 |
显存总大小(字节) |
type |
__u32 |
显示类型(FB_TYPE_*) |
type_aux |
__u32 |
类型辅助信息 |
visual |
__u32 |
视觉类型(FB_VISUAL_*) |
xpanstep |
__u16 |
水平平移步长(像素),0=不支持 |
ypanstep |
__u16 |
垂直平移步长(像素),0=不支持 |
ywrapstep |
__u16 |
垂直环绕步长(像素),0=不支持 |
line_length |
__u32 |
每行字节数(行距) |
mmio_start |
unsigned long |
MMIO 物理起始地址 |
mmio_len |
__u32 |
MMIO 区域大小(字节) |
accel |
__u32 |
硬件加速类型(FB_ACCEL_*) |
capabilities |
__u16 |
硬件能力标志 |
2.5.2 常用视觉类型
| 宏定义 | 值 | 说明 |
|---|---|---|
FB_VISUAL_TRUECOLOR |
2 | 真彩色(RGB 直接编码) |
FB_VISUAL_PSEUDOCOLOR |
3 | 伪彩色(通过颜色查找表) |
FB_VISUAL_DIRECTCOLOR |
4 | 直接彩色(每个通道有独立查找表) |
FB_VISUAL_STATIC_PSEUDOCOLOR |
8 | 静态伪彩色(查找表不可修改) |
FB_VISUAL_MONO01 |
0 | 单色:0=白,1=黑 |
FB_VISUAL_MONO10 |
1 | 单色:0=黑,1=白 |
2.6 数据结构对比总结
下表从多个维度对比四个核心数据结构:
| 对比维度 | fb_info | fb_ops | fb_var_screeninfo | fb_fix_screeninfo |
|---|---|---|---|---|
| 角色 | 设备描述符 | 操作函数表 | 可变参数 | 固定参数 |
| 用户访问 | 内核内部使用 | 内核内部使用 | 可读可写(ioctl) | 只读(ioctl) |
| 修改时机 | 驱动初始化 | 驱动初始化 | 运行时动态修改 | 驱动初始化固定 |
| 包含内容 | 所有子结构 + 设备状态 | 回调函数指针 | 分辨率、位深、时序 | 显存地址、行长、类型 |
| 对应 ioctl | — | — | FBIOPUT_VSCREENINFO FBIOGET_VSCREENINFO |
FBIOGET_FSCREENINFO |
| 驱动实现重点 | 分配、填充、注册 | 实现回调函数 | check_var + set_par | 初始化时正确填充 |
2.7 用户空间交互流程
下图展示了用户空间应用程序通过 ioctl 与 FrameBuffer 核心数据结构交互的完整流程: