3. fbdev 核心数据结构详解
Framebuffer 设备驱动通过三个核心数据结构向用户空间暴露显示能力:struct fb_info(设备实例)、struct fb_var_screeninfo(可变参数)和 struct fb_fix_screeninfo(固定参数)。理解这三者的关系与字段含义,是调试 fbdev 故障的基础。
核心关系:每个 Framebuffer 设备对应一个
fb_info,它内部包含一个 fb_var_screeninfo(运行时可修改)和一个 fb_fix_screeninfo(硬件固定)。用户空间通过 FBIOGET_VSCREENINFO / FBIOPUT_VSCREENINFO 操作可变参数,通过 FBIOGET_FSCREENINFO 读取固定参数。
3.1 struct fb_info:设备实例核心
struct fb_info 是 Framebuffer 驱动的顶层结构,每个注册的设备对应一个实例。它聚合了所有控制信息、操作函数指针和状态标志。
| 字段 | 类型 | 说明 |
|---|---|---|
state |
int | 设备状态:FBINFO_STATE_RUNNING / FBINFO_STATE_SUSPENDED |
node |
int | 设备编号,对应 /dev/fb<node> |
flags |
int | 标志位,如 FBINFO_FLAG_DEFAULT / FBINFO_HWACCEL_* |
var |
struct fb_var_screeninfo | 可变参数(分辨率、位深、时序) |
fix |
struct fb_fix_screeninfo | 固定参数(物理地址、行长、内存大小) |
fbops |
struct fb_ops * | 操作函数集(open/release/ioctl/pan_display 等) |
screen_base |
void __iomem * | 内核虚拟地址,用于直接操作显存 |
screen_size |
unsigned long | 显存总大小(字节) |
pseudo_palette |
void * | 伪调色板,用于 8/16 位色深模式 |
fbcon_ops |
struct fbcon_ops * | 控制台操作接口(可选) |
调试要点:当
fbset -i 或 cat /sys/class/graphics/fb0/state 返回异常时,首先检查 fb_info.state 是否为 FBINFO_STATE_RUNNING。若为 SUSPENDED,则设备被挂起,所有 ioctl 将返回错误。
3.2 struct fb_var_screeninfo:可变参数详解
该结构描述用户可动态调整的显示参数,包括分辨率、颜色深度、虚拟屏大小和时序。每次模式切换(如 fbset 1024x768-60)都会修改此结构。
| 字段 | 类型 | 说明 | 典型值 |
|---|---|---|---|
xres / yres |
__u32 | 可见分辨率(像素) | 1024 / 768 |
xres_virtual / yres_virtual |
__u32 | 虚拟屏分辨率(可大于可见区域,用于双缓冲) | 1024 / 1536 |
xoffset / yoffset |
__u32 | 虚拟屏滚动偏移(配合 pan_display) | 0 / 0 |
bits_per_pixel |
__u32 | 每像素位数(bpp) | 16 / 24 / 32 |
grayscale |
__u32 | 灰度标志(0=彩色,非0=灰度) | 0 |
red / green / blue / transp |
struct fb_bitfield | 各颜色通道的位偏移和长度 | RGB565 / RGB888 |
pixclock |
__u32 | 像素时钟(皮秒),决定刷新率 | 15384 (65MHz) |
left_margin / right_margin |
__u32 | 行同步前后肩(像素) | 168 / 8 |
upper_margin / lower_margin |
__u32 | 帧同步前后肩(行) | 23 / 4 |
hsync_len / vsync_len |
__u32 | 行/帧同步脉冲长度 | 8 / 2 |
vmode |
__u32 | 显示模式:FB_VMODE_NONINTERLACED / FB_VMODE_INTERLACED / FB_VMODE_DOUBLE |
0 |
sync |
__u32 | 同步极性:FB_SYNC_HOR_HIGH_ACT / FB_SYNC_VERT_HIGH_ACT |
0 |
常见故障:设置
pixclock 值过小(像素时钟过高)会导致显示器“超出范围”或黑屏。调试时先用 fbset -i 查看当前时序,再逐步调整。若 xres_virtual 与 yres_virtual 设置不当,mmap 显存时可能出现段错误。
3.3 struct fb_fix_screeninfo:固定参数详解
该结构描述硬件固定的显示属性,用户空间只能读取不能修改。它提供了显存物理地址、行字节长度和内存布局等关键信息。
| 字段 | 类型 | 说明 |
|---|---|---|
id |
char[16] | 驱动名称标识,如 s3c-fb / simplefb |
smem_start |
unsigned long | 显存物理起始地址(用于 DMA 或 mmap) |
smem_len |
__u32 | 显存总长度(字节) |
type |
__u32 | 显示类型:FB_TYPE_PACKED_PIXELS / FB_TYPE_PLANES / FB_TYPE_INTERLEAVED_PLANES |
type_aux |
__u32 | 辅助类型(用于 Planar 模式) |
visual |
__u32 | 颜色模式:FB_VISUAL_TRUECOLOR / FB_VISUAL_PSEUDOCOLOR / FB_VISUAL_DIRECTCOLOR / FB_VISUAL_MONO01 |
xpanstep / ypanstep |
__u16 | 硬件支持的 pan 步进(0 表示不支持) |
ywrapstep |
__u16 | 硬件支持的 wrap 步进 |
line_length |
__u32 | 一行像素占用的字节数(含对齐填充) |
mmio_start / mmio_len |
unsigned long / __u32 | MMIO 寄存器物理地址和长度(用于硬件加速) |
accel |
__u16 | 硬件加速类型:FB_ACCEL_NONE / FB_ACCEL_S3C_LCD 等 |
capabilities |
__u16 | 能力标志:FB_CAP_FOURCC(支持 FOURCC 格式) |
关键调试指标:
•
•
•
•
line_length 必须 ≥ xres * bits_per_pixel / 8,若大于则存在行对齐填充,mmap 计算偏移时需使用 line_length 而非 xres * bpp / 8。•
smem_len 应 ≥ line_length * yres_virtual,否则虚拟屏无法正常工作。•
visual 为 FB_VISUAL_PSEUDOCOLOR 时,需通过 FBIOGETCMAP / FBIOPUTCMAP 设置调色板。
3.4 三结构协同工作流程
用户空间程序通过 ioctl 与这三个结构交互,典型流程如下:
- 打开设备:
fd = open("/dev/fb0", O_RDWR) - 读取固定参数:
ioctl(fd, FBIOGET_FSCREENINFO, &fix),获取smem_start、line_length、smem_len - 读取可变参数:
ioctl(fd, FBIOGET_VSCREENINFO, &var),获取当前分辨率、位深、时序 - 修改模式(可选):修改
var字段后调用ioctl(fd, FBIOPUT_VSCREENINFO, &var) - 映射显存:
mmap(NULL, fix.smem_len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0) - 绘制与刷新:直接操作映射内存,必要时调用
FBIOPAN_DISPLAY切换显示偏移
/* 典型用户空间读取流程示例 */
#include <linux/fb.h>
#include <sys/mman.h>
#include <fcntl.h>
int fd;
struct fb_var_screeninfo var;
struct fb_fix_screeninfo fix;
char *fbp;
fd = open("/dev/fb0", O_RDWR);
if (fd < 0) { perror("open"); return -1; }
/* 读取固定参数 */
ioctl(fd, FBIOGET_FSCREENINFO, &fix);
printf("line_length: %u\n", fix.line_length);
printf("smem_len: %u\n", fix.smem_len);
/* 读取可变参数 */
ioctl(fd, FBIOGET_VSCREENINFO, &var);
printf("resolution: %ux%u @ %u bpp\n",
var.xres, var.yres, var.bits_per_pixel);
/* 映射显存 */
fbp = mmap(NULL, fix.smem_len,
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (fbp == MAP_FAILED) { perror("mmap"); close(fd); return -1; }
/* 绘制像素(将屏幕左上角设为红色) */
*(unsigned int *)(fbp + 0) = 0x00FF0000; /* 小端 RGB */
munmap(fbp, fix.smem_len);
close(fd);
故障排查提示:若
FBIOGET_FSCREENINFO 返回的 smem_start 为 0,说明驱动未正确初始化显存物理地址,常见于未加载固件或 IOMMU 未配置。此时 mmap 会失败,需检查内核日志 dmesg | grep fb。
3.5 常见故障与数据结构关联
| 故障现象 | 相关结构 | 排查方向 |
|---|---|---|
| fbset 显示分辨率与实际不符 | fb_var_screeninfo |
检查 xres / yres 是否被驱动 clamp;查看 dmesg 中模式验证信息 |
| mmap 返回 MAP_FAILED | fb_fix_screeninfo |
确认 smem_start 非零;检查 smem_len 是否合理;确认 /dev/fb0 权限 |
| 屏幕显示偏移或撕裂 | fb_var_screeninfo |
检查 xoffset / yoffset;确认 yres_virtual 与 line_length 匹配 |
| 颜色显示异常(偏色/条纹) | fb_var_screeninfo |
检查 bits_per_pixel 和 red/green/blue/transp 位域定义;确认 visual 类型 |
| ioctl 调用返回 EINVAL | fb_info.fbops |
驱动未实现对应 ioctl 分支;检查 fb_ops.fb_ioctl 是否注册 |
| 控制台无法切换回 fb | fb_info.fbcon_ops |
检查 fbcon_ops 是否初始化;尝试 chvt 1 强制切换 |
重要提醒:在嵌入式平台(如 i.MX6、RK3288)上,
smem_start 可能为 0 但 smem_len 非零,这是因为驱动使用 IOMMU 动态映射。此时不能直接使用 smem_start 做物理地址操作,必须通过 mmap 获取虚拟地址。调试时优先使用 fbset -i 和 fbdev_test 工具验证数据结构内容。