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 读取固定参数。
fbdev 核心数据结构关系图 用户空间 (ioctl) struct fb_info 设备实例 / 操作函数集 / 私有数据 fb_var_screeninfo 分辨率 / 位深 / 时序 / 虚拟屏 fb_fix_screeninfo 物理地址 / 行长 / 内存大小 用户通过 FBIOGET_VSCREENINFO / FBIOGET_FSCREENINFO 读取,通过 FBIOPUT_VSCREENINFO 修改 var

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 显存时可能出现段错误。
fb_var_screeninfo 时序参数示意 水平扫描线时序(单位:像素时钟) left_margin (168) | hsync_len (8) | right_margin (8) | xres (1024) 垂直帧时序(单位:行) upper_margin (23) | vsync_len (2) | lower_margin (4) | yres (768) 总水平像素 = left_margin + hsync_len + right_margin + xres;总垂直行 = upper_margin + vsync_len + lower_margin + yres

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 与这三个结构交互,典型流程如下:

  1. 打开设备:fd = open("/dev/fb0", O_RDWR)
  2. 读取固定参数:ioctl(fd, FBIOGET_FSCREENINFO, &fix),获取 smem_start、line_length、smem_len
  3. 读取可变参数:ioctl(fd, FBIOGET_VSCREENINFO, &var),获取当前分辨率、位深、时序
  4. 修改模式(可选):修改 var 字段后调用 ioctl(fd, FBIOPUT_VSCREENINFO, &var)
  5. 映射显存:mmap(NULL, fix.smem_len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)
  6. 绘制与刷新:直接操作映射内存,必要时调用 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。
用户空间与内核数据结构交互流程 用户程序 ioctl / mmap struct fb_info fb_var_screeninfo fb_fix_screeninfo fb_ops 函数集 显示控制器硬件 / 显存 用户通过 ioctl 读写 var/fix,通过 mmap 直接访问显存,驱动通过 fb_ops 控制硬件

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 工具验证数据结构内容。