2. fb_info 结构体成员总览:主要字段分类
帧缓冲驱动的核心数据结构是 fb_info,它位于 include/linux/fb.h 中,是驱动与内核帧缓冲层之间的唯一契约。一个完整的 fb_info 实例包含了描述硬件能力、当前显示模式、操作接口以及驱动私有状态的全部信息。本章按功能将其成员划分为四大类:固定信息、可变信息、操作函数 和 私有数据,并逐一说明每类字段的配置要点。
配置原则:固定信息在驱动生命周期内只赋值一次;可变信息随显示模式切换而更新;操作函数必须全部实现,否则内核可能拒绝注册;私有数据是驱动与硬件交互的桥梁,分配与释放必须配对。
2.1 固定信息(fb_fix_screeninfo)
fb_fix_screeninfo 描述显示控制器的硬件固有属性,这些属性在驱动加载后不会随显示模式改变。驱动初始化时通过 fb_info.fix 字段赋值。
| 成员 | 类型 | 说明 | 配置要点 |
|---|---|---|---|
id | char[16] | 驱动名称标识 | 不超过16字符,用于 /proc/fb 显示 |
smem_start | unsigned long | 帧缓冲物理基地址 | 必须与硬件寄存器一致,非PCI设备可为0 |
smem_len | __u32 | 帧缓冲总长度(字节) | 至少覆盖最大分辨率所需显存 |
type | __u32 | 显示类型(FB_TYPE_PACKED_PIXELS等) | 绝大多数使用 FB_TYPE_PACKED_PIXELS |
visual | __u32 | 颜色模式(FB_VISUAL_TRUECOLOR等) | 根据硬件颜色格式选择 |
line_length | __u32 | 一行像素的字节数 | 由驱动根据分辨率计算,通常为 xres_virtual * bpp / 8 |
mmio_start | unsigned long | MMIO 物理基地址 | 控制寄存器映射地址 |
mmio_len | __u32 | MMIO 区域长度 | 必须与硬件寄存器空间匹配 |
accel | __u16 | 硬件加速类型 | 无加速填 FB_ACCEL_NONE |
常见错误:忘记设置
line_length 或设置错误会导致用户空间计算帧缓冲偏移时出现花屏。建议在 fb_set_par() 回调中根据当前虚拟分辨率重新计算并更新该字段。
2.2 可变信息(fb_var_screeninfo)
fb_var_screeninfo 描述当前显示模式的可配置参数,包括分辨率、颜色深度、时序等。用户空间通过 FBIOPUT_VSCREENINFO ioctl 修改这些值,驱动需在 fb_ops.fb_set_par() 中响应变化。
| 成员 | 类型 | 说明 | 配置要点 |
|---|---|---|---|
xres / yres | __u32 | 可见分辨率 | 必须 ≤ xres_virtual / yres_virtual |
xres_virtual / yres_virtual | __u32 | 虚拟分辨率(含显存中未显示部分) | 用于双缓冲或滚动,乘积 * bpp ≤ smem_len * 8 |
bits_per_pixel | __u32 | 每像素位数(bpp) | 常见值:16、24、32 |
red / green / blue / transp | struct fb_bitfield | 颜色通道位域 | TRUECOLOR 模式必须正确设置偏移和长度 |
pixclock | __u32 | 像素时钟(皮秒) | 决定刷新率,需根据硬件 PLL 计算 |
left_margin / right_margin / upper_margin / lower_margin | __u32 | 显示时序消隐参数 | 必须符合显示器规格,否则黑屏 |
hsync_len / vsync_len | __u32 | 同步脉冲宽度 | 单位:像素/行 |
vmode | __u32 | 显示模式(FB_VMODE_NONINTERLACED等) | 通常为 FB_VMODE_NONINTERLACED |
时序验证:不要假设用户空间传入的时序参数一定合法。驱动应在
fb_check_var() 回调中校验所有时序值,超出硬件范围时返回 -EINVAL 或自动修正到最近的有效值。
2.3 操作函数(fb_ops)
fb_ops 是驱动向内核帧缓冲层注册的函数指针集合,所有与硬件交互的操作都通过这个结构体间接调用。内核帧缓冲层在 ioctl 路径中自动分发请求到对应的回调。
| 回调函数 | 原型 | 必须实现 | 典型职责 |
|---|---|---|---|
fb_open | int (*)(struct fb_info *, int) | 否 | 打开计数、资源引用 |
fb_release | int (*)(struct fb_info *, int) | 否 | 释放用户态引用 |
fb_read / fb_write | ssize_t (*)(...) | 否 | 直接读写帧缓冲(默认使用 memcpy) |
fb_check_var | int (*)(struct fb_var_screeninfo *, struct fb_info *) | 是 | 校验并修正可变参数 |
fb_set_par | int (*)(struct fb_info *) | 是 | 根据 var 配置硬件寄存器 |
fb_setcolreg | int (*)(unsigned, unsigned, unsigned, unsigned, unsigned, struct fb_info *) | 伪彩色必须 | 设置颜色查找表(CLUT) |
fb_blank | int (*)(int, struct fb_info *) | 否 | 电源管理:关闭/开启显示 |
fb_pan_display | int (*)(struct fb_var_screeninfo *, struct fb_info *) | 否 | 虚拟显示滚动 |
fb_fillrect / fb_copyarea / fb_imageblit | void (*)(...) | 否 | 图形加速(无加速可填 cfb_* 系列) |
fb_ioctl | int (*)(struct fb_info *, unsigned int, unsigned long) | 否 | 自定义 ioctl 扩展 |
// 最小化 fb_ops 配置示例
static struct fb_ops myfb_ops = {
.owner = THIS_MODULE,
.fb_check_var = myfb_check_var,
.fb_set_par = myfb_set_par,
.fb_setcolreg = myfb_setcolreg,
.fb_fillrect = cfb_fillrect, // 使用内核通用实现
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
};
加速回调:如果硬件不支持 2D 加速,直接使用
cfb_fillrect、cfb_copyarea、cfb_imageblit 这三个内核提供的软件实现,避免留下空指针导致内核崩溃。
2.4 私有数据(par / pseudo_palette / screen_base)
私有数据是驱动与硬件之间的上下文通道,帧缓冲层不解析其内容,完全由驱动自主管理。合理组织私有数据可以大幅提高代码可维护性。
| 成员 | 类型 | 用途 | 配置要点 |
|---|---|---|---|
par | void * | 驱动私有数据结构指针 | 通常包含硬件寄存器虚拟地址、时钟配置、电源状态等 |
pseudo_palette | __u32 * | 伪彩色/真彩色调色板缓存 | TRUECOLOR 模式时用于缓存颜色转换值,大小通常为 16 个 __u32 |
screen_base | char __iomem * | 帧缓冲虚拟地址 | 由 ioremap() 或 dma_alloc_coherent() 返回 |
screen_size | unsigned long | 帧缓冲虚拟地址空间大小 | 必须与 smem_len 一致 |
fbops | struct fb_ops * | 指向操作函数结构体 | 通常指向静态全局的 fb_ops 实例 |
device | struct device * | 关联的 Linux 设备结构体 | 由 platform_device 或 pci_dev 传入 |
node | int | 帧缓冲设备号(/dev/fbN) | 由 register_framebuffer() 自动分配 |
// 私有数据结构体典型定义
struct myfb_par {
void __iomem *regs; // 控制寄存器虚拟地址
unsigned long regs_phys; // 控制寄存器物理地址
unsigned long regs_size; // 寄存器空间大小
int irq; // 中断号
struct clk *pixel_clk; // 像素时钟
struct regulator *power; // 电源控制
u32 pseudo_palette[16]; // 调色板缓存
};
内存管理:
par 指向的内存通常在 probe() 中使用 devm_kzalloc() 分配,由设备驱动模型自动释放。screen_base 如果是通过 ioremap() 映射的,必须在 remove() 中调用 iounmap() 释放。
2.5 字段配置优先级与常见陷阱
在初始化 fb_info 时,字段之间存在隐式依赖关系。以下列表总结了配置顺序和常见错误:
- 先 fix 后 var:
smem_len必须在设置xres_virtual * yres_virtual * bpp / 8之前确定,否则可能超出显存范围。 - screen_base 与 smem_start 一致:如果使用
ioremap()映射物理显存,screen_base必须指向映射后的虚拟地址,smem_start保持物理地址不变。 - pseudo_palette 生命周期:该指针指向的缓存必须在
fb_info注销前有效。推荐将其嵌入par结构体或使用静态数组。 - fbops 不能为 NULL:即使所有回调都使用默认实现,
fbops指针也必须指向一个有效的fb_ops实例,且.owner必须设置为THIS_MODULE。 - node 由内核赋值:驱动不要手动修改
node字段,register_framebuffer()会自动分配设备号。
致命错误:如果
fb_check_var 或 fb_set_par 为 NULL,register_framebuffer() 会直接返回 -EINVAL,驱动加载失败。这是内核帧缓冲层最严格的校验之一,务必在注册前确认这两个回调已赋值。