3. fbdev 核心数据结构:fb_info、fb_ops、fb_var_screeninfo 详解
在 Linux fbdev 子系统中,三个核心数据结构构成了驱动与用户空间交互的基石:fb_info(帧缓冲设备描述符)、fb_ops(操作函数表)和 fb_var_screeninfo(可变屏幕参数)。理解这三者的关系与字段含义,是编写或移植 fbdev 驱动的第一步。
3.1 整体架构关系
下图展示了三个核心数据结构在 fbdev 驱动模型中的协作关系:
关键理解:fb_info 是“身份证”,fb_ops 是“技能手册”,fb_var_screeninfo 是“可调节的配置面板”。驱动注册时,三者必须协同工作。
3.2 struct fb_info —— 帧缓冲设备描述符
struct fb_info 是 fbdev 驱动中最重要的数据结构,每个帧缓冲设备对应一个实例。它包含了设备的所有状态信息、操作函数指针、屏幕参数以及底层内存管理数据。
| 字段 | 类型 | 说明 |
|---|---|---|
state |
int | 设备状态:FBINFO_STATE_RUNNING / FBINFO_STATE_SUSPENDED |
node |
int | 设备编号,对应 /dev/fb<node> |
flags |
int | 设备标志位,如 FBINFO_FLAG_DEFAULT |
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 * | 指向底层硬件设备的指针(用于电源管理) |
pseudo_palette |
void * | 伪调色板数据(用于 8/16 位色深) |
驱动注册流程:调用
framebuffer_alloc() 分配 fb_info → 填充字段 → 调用 register_framebuffer() 注册到系统。注销时使用 unregister_framebuffer() + framebuffer_release()。
3.3 struct fb_ops —— 操作函数表
struct fb_ops 定义了驱动必须实现或可选实现的回调函数集合。用户空间通过 ioctl 系统调用触发这些回调,完成显示控制。
struct fb_ops {
/* 必须实现的回调 */
int (*fb_open)(struct fb_info *info, int user);
int (*fb_release)(struct fb_info *info, int user);
int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
int (*fb_set_par)(struct fb_info *info);
int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp, struct fb_info *info);
int (*fb_blank)(int blank_mode, struct fb_info *info);
/* 可选实现:加速操作 */
int (*fb_fillrect)(struct fb_info *info, const struct fb_fillrect *rect);
int (*fb_copyarea)(struct fb_info *info, const struct fb_copyarea *area);
int (*fb_imageblit)(struct fb_info *info, const struct fb_image *image);
/* 可选实现:I/O 内存映射 */
int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
/* 可选实现:电源管理 */
int (*fb_sync)(struct fb_info *info);
/* ... 其他回调 */
};
| 回调函数 | 触发场景 | 典型实现 |
|---|---|---|
fb_open |
用户打开 /dev/fbX | 计数增加、硬件上电 |
fb_release |
用户关闭 /dev/fbX | 计数减少、硬件下电 |
fb_check_var |
FBIOPUT_VSCREENINFO ioctl | 验证分辨率/位深是否支持,调整到最近值 |
fb_set_par |
参数验证通过后 | 配置硬件寄存器、更新时序 |
fb_setcolreg |
FBIOPUTCMAP ioctl | 设置调色板寄存器 |
fb_blank |
FBIOBLANK ioctl | 控制显示空白/唤醒 |
fb_fillrect |
FBIO_FILLRECT ioctl | 硬件加速矩形填充(可选) |
常见陷阱:fb_check_var 必须做参数合法性校验,不能直接返回 0。如果驱动不实现 fb_set_par,用户空间设置分辨率将不会生效。fb_fillrect 等加速回调如果未实现,fbdev 会使用软件 fallback 实现,性能下降明显。
3.4 struct fb_var_screeninfo —— 可变屏幕参数
struct fb_var_screeninfo 是用户空间与内核驱动之间交换显示参数的核心结构。它包含了分辨率、位深、像素时钟、时序等可动态调整的参数。
struct fb_var_screeninfo {
__u32 xres; /* 可见水平分辨率(像素) */
__u32 yres; /* 可见垂直分辨率(像素) */
__u32 xres_virtual; /* 虚拟水平分辨率(支持滚屏) */
__u32 yres_virtual; /* 虚拟垂直分辨率(支持滚屏) */
__u32 xoffset; /* 虚拟到可见的水平偏移 */
__u32 yoffset; /* 虚拟到可见的垂直偏移 */
__u32 bits_per_pixel; /* 每像素位数(bpp) */
__u32 grayscale; /* 灰度标志:0=彩色,非0=灰度 */
/* 颜色位域:描述 RGB 各分量在像素中的位置和宽度 */
struct fb_bitfield red; /* 红色通道位域 */
struct fb_bitfield green; /* 绿色通道位域 */
struct fb_bitfield blue; /* 蓝色通道位域 */
struct fb_bitfield transp; /* 透明度通道位域 */
/* 时序参数(像素时钟单位:皮秒) */
__u32 pixclock; /* 像素时钟周期(皮秒) */
__u32 left_margin; /* 行同步前沿(HBP) */
__u32 right_margin; /* 行同步后沿(HFP) */
__u32 upper_margin; /* 帧同步前沿(VBP) */
__u32 lower_margin; /* 帧同步后沿(VFP) */
__u32 hsync_len; /* 行同步脉冲宽度 */
__u32 vsync_len; /* 帧同步脉冲宽度 */
__u32 sync; /* 同步极性标志 */
__u32 vmode; /* 显示模式:FB_VMODE_NONINTERLACED 等 */
__u32 rotate; /* 旋转角度:0/90/180/270 */
/* ... 其他字段 */
};
3.5 参数验证流程
当用户空间通过 FBIOPUT_VSCREENINFO ioctl 设置新参数时,内核会执行以下验证流程:
3.6 颜色位域详解
struct fb_bitfield 用于描述颜色通道在像素数据中的编码方式,是理解不同色深格式的关键:
| 色深 | 格式 | red | green | blue | transp |
|---|---|---|---|---|---|
| 32bpp | ARGB8888 | offset=16, length=8 | offset=8, length=8 | offset=0, length=8 | offset=24, length=8 |
| 24bpp | RGB888 | offset=16, length=8 | offset=8, length=8 | offset=0, length=8 | offset=0, length=0 |
| 16bpp | RGB565 | offset=11, length=5 | offset=5, length=6 | offset=0, length=5 | offset=0, length=0 |
| 8bpp | 索引色 | offset=0, length=8 | offset=0, length=8 | offset=0, length=8 | offset=0, length=0 |
调试技巧:使用
fbset -i 命令可以查看当前 fb_var_screeninfo 的所有字段值,包括分辨率、位深、时序和颜色位域。这是排查显示参数问题的第一手段。
3.7 虚拟分辨率与滚屏机制
xres_virtual 和 yres_virtual 支持比可见区域更大的虚拟画布,通过 xoffset / yoffset 实现滚屏。这种机制在控制台终端滚动时非常有用:
/* 设置虚拟分辨率大于可见分辨率,实现双缓冲滚屏 */
info->var.xres = 1024;
info->var.yres = 768;
info->var.xres_virtual = 1024;
info->var.yres_virtual = 1536; /* 两倍高度 */
info->var.xoffset = 0;
info->var.yoffset = 0; /* 显示上半部分 */
/* 用户空间通过 FBIOPAN_DISPLAY ioctl 调整 yoffset 实现滚屏 */
性能优势:滚屏操作只需要修改显示控制器的起始地址寄存器,不需要拷贝帧缓冲内存数据,极大提升滚动效率。这是 fbdev 在嵌入式系统中仍然被广泛使用的重要原因之一。