2. Linux 帧缓冲驱动架构:fbdev 子系统层次结构、核心数据结构与操作函数集
Linux 帧缓冲(fbdev)子系统为上层应用提供了一个统一的、与硬件无关的显示设备抽象层。本章节深入剖析 fbdev 的层次结构、核心数据结构 struct fb_info 以及驱动开发者必须实现的操作函数集 struct fb_ops,为后续编写实际驱动和实现屏幕旋转打下坚实基础。
2.1 fbdev 子系统层次结构
fbdev 子系统采用分层设计,从下至上依次为:硬件层、驱动层、核心层和用户空间接口层。这种分层结构有效地分离了硬件相关的实现与通用的逻辑管理。
架构要点: 驱动开发者主要关注驱动层,通过填充
struct fb_info 和 struct fb_ops 来适配具体硬件,而核心层负责注册、文件操作接口和事件通知。
2.2 核心数据结构:struct fb_info
struct fb_info 是 fbdev 驱动中最重要的数据结构,它描述了一个帧缓冲设备的所有属性和状态。每个帧缓冲设备对应一个 struct fb_info 实例。该结构体定义在 linux/fb.h 头文件中。
| 字段名 | 类型 | 描述 |
|---|---|---|
fbops |
struct fb_ops * |
指向操作函数集的指针,驱动必须实现 |
screen_base |
char __iomem * |
显存虚拟地址基址,用于 CPU 访问显存 |
screen_size |
unsigned long |
显存总大小(字节) |
fb_fix_screeninfo |
struct fb_fix_screeninfo |
固定屏幕信息,如显存物理地址、行长度、类型 |
fb_var_screeninfo |
struct fb_var_screeninfo |
可变屏幕信息,如分辨率、位深、时序参数 |
pseudo_palette |
void * |
伪调色板缓冲区,用于 8/16 位色深模式 |
state |
int |
设备状态:FBINFO_STATE_RUNNING / SUSPENDED |
node |
int |
设备编号,对应 /dev/fb<node> |
device |
struct device * |
指向 Linux 设备模型中的 device 结构 |
开发提示: 在驱动初始化时,通常使用
framebuffer_alloc() 分配 struct fb_info,然后填充各个字段,最后通过 register_framebuffer() 注册到核心层。释放时使用 framebuffer_release() 和 unregister_framebuffer()。
2.3 操作函数集:struct fb_ops
struct fb_ops 定义了驱动需要实现的一系列回调函数,fbdev 核心层在用户空间发起系统调用时,会通过 fb_info->fbops 调用对应的驱动函数。这些函数是驱动与硬件交互的桥梁。
2.4 关键回调函数详解
以下表格列出了 struct fb_ops 中最重要的回调函数及其用途,驱动开发者必须根据硬件特性实现这些函数。
| 回调函数 | 原型 | 触发时机 | 典型实现 |
|---|---|---|---|
fb_check_var |
int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info) |
用户设置分辨率/位深时 | 验证参数是否在硬件支持范围内,调整到最接近的有效值 |
fb_set_par |
int (*fb_set_par)(struct fb_info *info) |
参数验证通过后,应用新参数 | 根据 fb_var_screeninfo 配置硬件寄存器(时序、分辨率、位深) |
fb_setcolreg |
int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *info) |
用户设置调色板颜色 | 将 RGB 值写入硬件颜色查找表(LUT) |
fb_blank |
int (*fb_blank)(int blank_mode, struct fb_info *info) |
屏幕空闲或用户请求 | 控制 LCD 背光或关闭显示控制器时钟 |
fb_pan_display |
int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info) |
用户请求显示偏移 | 调整显存起始地址寄存器,实现无撕裂滚动 |
fb_mmap |
int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma) |
用户调用 mmap | 将显存物理地址映射到用户空间虚拟地址 |
2.5 固定信息与可变信息
struct fb_info 中包含两个重要的子结构体,分别描述设备的固定属性和可变属性。
- struct fb_fix_screeninfo:描述硬件固定不变的属性,如显存物理地址、显存大小、行字节长度(line_length)、像素格式类型等。用户空间通过
FBIOGET_FSCREENINFOioctl 获取。 - struct fb_var_screeninfo:描述用户可调整的属性,如可见分辨率(xres/yres)、虚拟分辨率(xres_virtual/yres_virtual)、位深(bits_per_pixel)、时序参数(pixclock、left_margin 等)。用户空间通过
FBIOGET_VSCREENINFO和FBIOPUT_VSCREENINFOioctl 读写。
常见误区: 初学者容易混淆
screen_base 和 fb_fix_screeninfo.smem_start。前者是内核虚拟地址,用于 CPU 直接访问;后者是物理地址,用于 DMA 或用户空间 mmap。两者指向同一块物理显存,但地址空间不同。
2.6 驱动注册与注销流程
一个典型的 fbdev 驱动在模块加载和卸载时遵循以下步骤:
- 分配 fb_info: 调用
framebuffer_alloc(sizeof(struct my_private_data), &pdev->dev),额外分配私有数据空间。 - 填充固定信息: 设置
fb_fix_screeninfo中的smem_start、smem_len、line_length、type等字段。 - 填充可变信息: 设置
fb_var_screeninfo的默认分辨率、位深和时序。 - 设置操作函数集: 将
fb_info->fbops指向一个静态的struct fb_ops实例,实现必要的回调。 - 硬件初始化: 配置 LCD 控制器寄存器、分配显存(dma_alloc_coherent)、映射到
screen_base。 - 注册设备: 调用
register_framebuffer(fb_info),核心层创建设备文件/dev/fbX。 - 注销设备: 在模块卸载时,先调用
unregister_framebuffer(fb_info),然后释放显存,最后调用framebuffer_release(fb_info)。