2. Linux 帧缓冲驱动架构:fbdev 子系统层次结构、核心数据结构与操作函数集

Linux 帧缓冲(fbdev)子系统为上层应用提供了一个统一的、与硬件无关的显示设备抽象层。本章节深入剖析 fbdev 的层次结构、核心数据结构 struct fb_info 以及驱动开发者必须实现的操作函数集 struct fb_ops,为后续编写实际驱动和实现屏幕旋转打下坚实基础。

2.1 fbdev 子系统层次结构

fbdev 子系统采用分层设计,从下至上依次为:硬件层、驱动层、核心层和用户空间接口层。这种分层结构有效地分离了硬件相关的实现与通用的逻辑管理。

架构要点: 驱动开发者主要关注驱动层,通过填充 struct fb_info 和 struct fb_ops 来适配具体硬件,而核心层负责注册、文件操作接口和事件通知。
fbdev 子系统层次结构 用户空间接口层 /dev/fb* 设备文件 · mmap · ioctl · 读写操作 fbdev 核心层 (fbmem.c) 设备注册/注销 · 文件操作分发 · 事件通知 · 软拷贝缓冲区管理 驱动层 (具体 fb 驱动) struct fb_info · struct fb_ops · 硬件初始化 · 显存管理 硬件层 (LCD 控制器 / GPU / 显示引擎) 寄存器配置 · 时序控制 · DMA 传输 · 像素时钟 驱动层是开发者主要工作的层次,通过标准接口与核心层交互

2.2 核心数据结构:struct fb_info

struct fb_info 是 fbdev 驱动中最重要的数据结构,它描述了一个帧缓冲设备的所有属性和状态。每个帧缓冲设备对应一个 struct fb_info 实例。该结构体定义在 linux/fb.h 头文件中。

struct fb_info 核心字段说明
字段名 类型 描述
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 调用对应的驱动函数。这些函数是驱动与硬件交互的桥梁。

struct fb_ops 回调函数调用流程 用户空间应用 open/read/write/ioctl/mmap fbdev 核心层 通过 fbops 调用 驱动 fb_ops 实现 关键回调函数 fb_open / fb_release : 设备打开/关闭时的初始化与清理 fb_read / fb_write : 直接读写显存数据 fb_setcolreg : 设置颜色寄存器(调色板模式) fb_pan_display : 实现显示偏移(硬件滚动) fb_blank : 控制屏幕开关(节能模式)

2.4 关键回调函数详解

以下表格列出了 struct fb_ops 中最重要的回调函数及其用途,驱动开发者必须根据硬件特性实现这些函数。

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_FSCREENINFO ioctl 获取。
  • struct fb_var_screeninfo:描述用户可调整的属性,如可见分辨率(xres/yres)、虚拟分辨率(xres_virtual/yres_virtual)、位深(bits_per_pixel)、时序参数(pixclock、left_margin 等)。用户空间通过 FBIOGET_VSCREENINFO 和 FBIOPUT_VSCREENINFO ioctl 读写。
常见误区: 初学者容易混淆 screen_base 和 fb_fix_screeninfo.smem_start。前者是内核虚拟地址,用于 CPU 直接访问;后者是物理地址,用于 DMA 或用户空间 mmap。两者指向同一块物理显存,但地址空间不同。

2.6 驱动注册与注销流程

一个典型的 fbdev 驱动在模块加载和卸载时遵循以下步骤:

  1. 分配 fb_info: 调用 framebuffer_alloc(sizeof(struct my_private_data), &pdev->dev),额外分配私有数据空间。
  2. 填充固定信息: 设置 fb_fix_screeninfo 中的 smem_start、smem_len、line_length、type 等字段。
  3. 填充可变信息: 设置 fb_var_screeninfo 的默认分辨率、位深和时序。
  4. 设置操作函数集: 将 fb_info->fbops 指向一个静态的 struct fb_ops 实例,实现必要的回调。
  5. 硬件初始化: 配置 LCD 控制器寄存器、分配显存(dma_alloc_coherent)、映射到 screen_base。
  6. 注册设备: 调用 register_framebuffer(fb_info),核心层创建设备文件 /dev/fbX。
  7. 注销设备: 在模块卸载时,先调用 unregister_framebuffer(fb_info),然后释放显存,最后调用 framebuffer_release(fb_info)。
fbdev 驱动注册与注销流程 注册流程 (module_init) 1. framebuffer_alloc() 2. 填充 fix/var screeninfo 3. 设置 fbops 回调函数 4. register_framebuffer() 注销流程 (module_exit) 1. unregister_framebuffer() 2. 释放显存 (dma_free_coherent) 3. 释放 IO 资源/中断 4. framebuffer_release()

2.7 本章小结