2. Framebuffer 核心数据结构
在 Linux Framebuffer 驱动模型中,用户空间通过 ioctl 系统调用与内核交换显示参数。三个核心数据结构构成了整个帧缓冲的配置骨架:可变信息、固定信息 和 颜色映射表。理解它们的字段含义与协作关系,是正确实现 mmap 映射和颜色空间转换的前提。
关键认知:
fb_var_screeninfo 描述“当前可变的显示模式”(分辨率、位深、时序),fb_fix_screeninfo 描述“硬件固定的内存布局”(起始地址、步长、I/O 地址),fb_cmap 描述“调色板映射”(仅对索引色模式有效)。三者通过 FBIOGET_VSCREENINFO / FBIOGET_FSCREENINFO / FBIOGETCMAP 获取。
2.1 struct fb_var_screeninfo — 可变显示参数
该结构体定义在 linux/fb.h 中,存储用户空间可动态修改的显示模式参数。每次切换分辨率或位深时,驱动通过 FBIO_SET_VSCREENINFO 更新此结构。
| 字段 | 类型 | 说明 | 典型值 |
|---|---|---|---|
xres | __u32 | 可见区域水平像素 | 1024 |
yres | __u32 | 可见区域垂直像素 | 768 |
xres_virtual | __u32 | 虚拟缓冲区水平像素(可大于 xres) | 2048 |
yres_virtual | __u32 | 虚拟缓冲区垂直像素(支持双缓冲) | 1536 |
bits_per_pixel | __u32 | 每个像素的位数(bpp) | 32 |
red/green/blue/transp | struct fb_bitfield | 各颜色分量的位偏移与长度 | R:16/8, G:8/8, B:0/8 |
pixclock | __u32 | 像素时钟(皮秒) | 10000 |
left_margin/right_margin | __u32 | 行同步前后肩 | 48/16 |
upper_margin/lower_margin | __u32 | 帧同步前后肩 | 33/10 |
hsync_len/vsync_len | __u32 | 同步脉冲宽度 | 96/2 |
vmode | __u32 | 显示模式(非隔行/隔行/双扫描) | FB_VMODE_NONINTERLACED |
调优关注点: 颜色空间转换时,
bits_per_pixel 和 red/green/blue 的位域布局决定了像素格式。例如 32bpp 下常见 RGBA8888(R 偏移 16、G 偏移 8、B 偏移 0)或 BGRA8888。转换前必须读取这些字段,否则会产生颜色错乱。
2.2 struct fb_fix_screeninfo — 固定硬件信息
该结构体描述帧缓冲的物理内存布局,在驱动初始化时确定,用户空间通常只能读取不能修改。
| 字段 | 类型 | 说明 | mmap 映射中的角色 |
|---|---|---|---|
smem_start | unsigned long | 帧缓冲物理起始地址 | 作为 mmap 的偏移参数(需页对齐) |
smem_len | __u32 | 帧缓冲总长度(字节) | mmap 映射长度不能超过此值 |
type | __u32 | 帧缓冲类型(包/平面/其他) | FB_TYPE_PACKED_PIXELS 最常用 |
visual | __u32 | 颜色模式(真彩/伪彩/直接色) | 决定是否使用 fb_cmap |
line_length | __u16 | 一行像素的字节数(步长) | 计算像素偏移:offset = y * line_length + x * (bpp/8) |
mmio_start | unsigned long | MMIO 物理起始地址 | 用于硬件寄存器映射(非像素数据) |
mmio_len | __u32 | MMIO 区域长度 | — |
常见陷阱:
smem_start 是物理地址,用户空间不能直接访问。mmap 时使用 MAP_SHARED 标志,并将 smem_start 作为 offset 参数传入。但某些嵌入式平台(如 ARM)的帧缓冲物理地址可能不在页边界上,需要先 mmap 到页对齐的地址再计算偏移。
2.3 struct fb_cmap — 颜色映射表(调色板)
当 fb_fix_screeninfo.visual 为 FB_VISUAL_PSEUDOCOLOR 或 FB_VISUAL_DIRECTCOLOR 时,像素值不直接表示 RGB,而是作为索引查表。此时 fb_cmap 定义了索引到实际颜色的映射关系。
| 字段 | 类型 | 说明 |
|---|---|---|
start | __u32 | 起始索引号(通常为 0) |
len | __u32 | 映射表长度(颜色数) |
red | __u16 * | 红色分量数组(每个值 0~65535) |
green | __u16 * | 绿色分量数组 |
blue | __u16 * | 蓝色分量数组 |
transp | __u16 * | 透明度分量数组(可为 NULL) |
颜色空间转换场景: 在 8bpp 伪彩模式下,像素值 0x3F 对应索引 63,实际颜色为
red[63]、green[63]、blue[63]。若要将伪彩转换为 32bpp 真彩,必须通过 FBIOGETCMAP 读取整个调色板,然后逐像素查表替换。调优时注意 len 通常为 256(8bpp)或 16(4bpp)。
2.4 三个结构体的协作流程
在实际的 Framebuffer 编程中,三个结构体按以下顺序协同工作:
- 打开设备:
int fd = open("/dev/fb0", O_RDWR); - 获取可变信息:
ioctl(fd, FBIOGET_VSCREENINFO, &var);— 得到分辨率、位深、颜色位域。 - 获取固定信息:
ioctl(fd, FBIOGET_FSCREENINFO, &fix);— 得到物理地址、步长、内存长度。 - 获取颜色映射(仅伪彩/直接色):
ioctl(fd, FBIOGETCMAP, &cmap);— 读取调色板。 - mmap 映射:
mmap(NULL, fix.smem_len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); - 像素操作: 根据
var.bits_per_pixel和fix.line_length计算偏移,直接读写映射内存。
性能提示: 颜色空间转换时,
var 中的 red.offset、green.offset、blue.offset 决定了像素格式的字节序。例如在 ARM 小端平台上,32bpp 的 RGBA8888 实际内存布局为 B、G、R、A。转换前务必打印这些位域值,避免“红蓝颠倒”。
2.5 数据结构对比总结
| 特性 | fb_var_screeninfo | fb_fix_screeninfo | fb_cmap |
|---|---|---|---|
| 可变性 | 可读可写(用户可修改) | 只读(驱动初始化固定) | 可读可写(通过 FBIO_SETCMAP) |
| 主要用途 | 描述当前显示模式 | 描述内存布局 | 描述索引色映射 |
| mmap 相关 | 间接(决定像素格式) | 直接(提供地址和步长) | 无关(仅查表用) |
| 颜色空间转换 | 提供位域布局 | 提供行对齐信息 | 提供索引→RGB 映射 |
| 获取 ioctl | FBIOGET_VSCREENINFO | FBIOGET_FSCREENINFO | FBIOGETCMAP |