2. 帧缓冲(FBDEV)基础:Linux帧缓冲子系统架构、fb_ops结构体、核心数据结构

帧缓冲(Frame Buffer, FBDEV)是 Linux 内核中最早用于显示输出的子系统,它为用户空间提供了一个简单的内存映射接口,使得应用程序可以直接向显示缓冲区写入像素数据。虽然现代 DRM/KMS 已经取代了 FBDEV 的大部分功能,但理解 FBDEV 的架构和核心数据结构,是掌握 Linux 显示驱动演进的关键一步,也是后续迁移到 KMS 的基础。

2.1 帧缓冲子系统整体架构

FBDEV 子系统采用分层设计,从上到下依次为:用户空间接口层、核心框架层、硬件驱动层。其核心思想是将显示缓冲区抽象为一个字符设备(/dev/fbX),用户态通过 mmap 直接操作显存。

架构要点: FBDEV 的核心是 struct fb_info,它串联了所有关键组件,包括设备属性、操作函数集、可变/固定参数、以及底层硬件回调。
FBDEV 子系统分层架构 FBDEV 子系统分层架构 用户空间层 应用程序 / Xorg / Wayland / fbdev 工具 系统调用接口 (open/read/write/mmap/ioctl) 核心框架层 (drivers/video/fbdev/core/) fbcon (控制台) / fbmem (字符设备管理) / fbmon (EDID) 核心数据结构: struct fb_info, struct fb_ops, struct fb_var_screeninfo 硬件驱动层 (drivers/video/fbdev/*/) 具体芯片驱动 (如 mxsfb, imxfb, udlfb, simplefb) 硬件层 (Display Controller + Framebuffer Memory)

2.2 核心数据结构:struct fb_info

struct fb_info 是帧缓冲驱动的核心枢纽,每个帧缓冲设备对应一个 fb_info 实例。它包含了设备的所有状态信息、操作函数指针、以及屏幕参数。

成员 类型 描述
fbops struct fb_ops * 硬件操作函数集,驱动必须实现
var struct fb_var_screeninfo 可变屏幕参数(分辨率、位深、时序等)
fix struct fb_fix_screeninfo 固定屏幕参数(物理地址、行长、内存大小等)
screen_base char __iomem * 帧缓冲内存的虚拟地址(用于 mmap)
screen_size unsigned long 帧缓冲内存总大小(字节)
device struct device * 关联的 Linux 设备模型节点
par void * 驱动私有数据指针,用于扩展
pseudo_palette void * 伪调色板,用于 8/16 位色深模式
关键理解: var 和 fix 是用户空间通过 FBIOGET_VSCREENINFO / FBIOGET_FSCREENINFO ioctl 获取的参数。驱动在 fb_ops.fb_check_var 中验证用户设置的 var 参数是否合法。

2.3 操作函数集:struct fb_ops

struct fb_ops 定义了驱动需要实现的回调函数,是驱动与核心框架交互的接口。每个驱动必须至少实现 fb_check_var 和 fb_set_par,其他函数根据硬件能力选择性实现。

fb_ops 核心回调函数关系 fb_ops 核心回调函数关系 struct fb_ops fb_check_var (验证参数) fb_set_par (设置参数) fb_setcolreg (设置颜色) fb_fillrect (矩形填充) fb_copyarea (区域拷贝) fb_imageblit (图像渲染) fb_blank (开关显示) fb_pan_display (平移显示)

2.3.1 必须实现的回调函数

  • fb_check_var:验证用户传入的 fb_var_screeninfo 参数是否合法,如分辨率是否支持、位深是否正确、时序是否在硬件范围内。如果参数不合法,驱动应将其调整为最接近的合法值。
  • fb_set_par:根据 fb_info.var 中的参数配置硬件寄存器,包括设置显示控制器时序、帧缓冲地址、颜色格式等。这是驱动中最关键的硬件配置函数。
  • fb_setcolreg:设置颜色寄存器,用于伪彩色模式(8/16 bpp)。对于真彩色模式(24/32 bpp),此函数可以为空或仅更新伪调色板。

2.3.2 可选加速回调函数

FBDEV 提供了三个加速函数,用于在硬件支持的情况下加速图形操作,避免通过 CPU 逐像素操作:

函数 作用 典型硬件支持
fb_fillrect 用指定颜色填充矩形区域 2D 加速引擎、Blitter
fb_copyarea 在帧缓冲内拷贝矩形区域 2D 加速引擎、Blitter
fb_imageblit 将图像数据渲染到帧缓冲 2D 加速引擎、光标硬件
迁移注意: 在 DRM/KMS 中,这些加速操作由 drm_fb_helper 或用户空间的 libdrm/Mesa 接管,不再需要驱动实现。FBDEV 的加速函数在迁移时通常会被移除或重写为 DRM 的 drm_plane 操作。

2.4 屏幕参数结构体

FBDEV 使用两个结构体来描述屏幕参数:可变参数和固定参数。用户空间通过 ioctl 读取和设置这些参数。

2.4.1 struct fb_var_screeninfo(可变参数)

该结构体包含用户可修改的显示参数,如分辨率、位深、时序等。驱动在 fb_check_var 中验证其合法性。

struct fb_var_screeninfo {
    __u32 xres;           /* 可见水平分辨率(像素) */
    __u32 yres;           /* 可见垂直分辨率(像素) */
    __u32 xres_virtual;   /* 虚拟水平分辨率(支持滚屏) */
    __u32 yres_virtual;   /* 虚拟垂直分辨率 */
    __u32 xoffset;        /* 虚拟到可见的水平偏移 */
    __u32 yoffset;        /* 虚拟到可见的垂直偏移 */
    __u32 bits_per_pixel; /* 每像素位数(bpp) */
    __u32 grayscale;      /* 灰度标志 */
    struct fb_bitfield red;   /* 红色通道位域 */
    struct fb_bitfield green; /* 绿色通道位域 */
    struct fb_bitfield blue;  /* 蓝色通道位域 */
    struct fb_bitfield transp;/* 透明度通道位域 */
    __u32 pixclock;       /* 像素时钟(皮秒) */
    __u32 left_margin;    /* 行同步前沿 */
    __u32 right_margin;   /* 行同步后沿 */
    __u32 upper_margin;   /* 帧同步前沿 */
    __u32 lower_margin;   /* 帧同步后沿 */
    __u32 hsync_len;      /* 行同步脉冲长度 */
    __u32 vsync_len;      /* 帧同步脉冲长度 */
    __u32 sync;           /* 同步极性标志 */
    __u32 vmode;          /* 显示模式(隔行/逐行) */
    __u32 rotate;         /* 旋转角度 */
};

2.4.2 struct fb_fix_screeninfo(固定参数)

该结构体包含硬件固定的参数,用户空间只能读取,不能修改。

struct fb_fix_screeninfo {
    char id[16];          /* 驱动标识字符串 */
    unsigned long smem_start;  /* 帧缓冲物理起始地址 */
    __u32 smem_len;       /* 帧缓冲内存总长度(字节) */
    __u32 type;           /* 显示类型(如 FB_TYPE_PACKED_PIXELS) */
    __u32 type_aux;       /* 辅助类型 */
    __u32 visual;         /* 颜色模式(真彩色/伪彩色/直接彩色) */
    __u16 xpanstep;       /* 水平滚屏步长 */
    __u16 ypanstep;       /* 垂直滚屏步长 */
    __u16 ywrapstep;      /* 垂直回绕步长 */
    __u32 line_length;    /* 一行像素的字节数(stride) */
    unsigned long mmio_start; /* 内存映射 IO 物理起始地址 */
    __u32 mmio_len;       /* 内存映射 IO 长度 */
    __u32 accel;          /* 加速器类型 */
    __u16 capabilities;   /* 能力标志 */
};
关键差异: smem_start 是物理地址,用户空间通过 mmap 映射到虚拟地址空间。line_length 是每行像素占用的实际字节数,通常大于 xres * (bits_per_pixel / 8),因为硬件可能要求行对齐。

2.5 帧缓冲设备注册流程

一个典型的 FBDEV 驱动在 probe 函数中完成以下步骤:

  1. 分配 fb_info:调用 framebuffer_alloc() 分配 struct fb_info 实例。
  2. 设置固定参数:填充 fix 结构体,包括物理地址、内存大小、行长等。
  3. 设置可变参数默认值:填充 var 结构体,设置默认分辨率、位深、时序。
  4. 注册 fb_ops:将驱动实现的 fb_ops 赋值给 fb_info->fbops。
  5. 分配帧缓冲内存:通过 dma_alloc_coherent() 或 ioremap() 分配/映射显存。
  6. 注册设备:调用 register_framebuffer() 将设备注册到系统。
FBDEV 驱动注册流程 FBDEV 驱动注册流程 1. framebuffer_alloc() 2. 填充 fix / var 参数 3. 设置 fb_ops 回调 4. 分配帧缓冲内存 5. 初始化硬件 (set_par) 6. register_framebuffer() 注意:步骤 4 和 5 的顺序可能因硬件而异,部分硬件在分配内存前需要先配置控制器

2.6 用户空间接口

FBDEV 通过字符设备 /dev/fbX 向用户空间提供以下操作:

操作 系统调用 描述
打开设备 open() 打开 /dev/fb0 等设备节点
获取固定信息 ioctl(FBIOGET_FSCREENINFO) 读取 fb_fix_screeninfo
获取可变信息 ioctl(FBIOGET_VSCREENINFO) 读取 fb_var_screeninfo
设置可变信息 ioctl(FBIOPUT_VSCREENINFO) 设置新的显示参数
映射帧缓冲 mmap() 将显存映射到用户空间
刷写显示 ioctl(FBIOPAN_DISPLAY) 平移显示起始位置(滚屏)
调试技巧: 使用 fbset 工具可以查看和修改当前帧缓冲参数:fbset -i 显示当前配置,fbset -xres 1024 -yres 768 -depth 32 修改分辨率。使用 cat /dev/urandom > /dev/fb0 可以快速测试帧缓冲是否工作(会显示噪点)。

2.7 FBDEV 的局限性

虽然 FBDEV 架构简单,但在现代显示需求下存在明显不足:

  • 单缓冲限制:FBDEV 原生只支持单缓冲,虽然可以通过虚拟分辨率实现软件双缓冲,但效率低下且无法避免撕裂。
  • 无硬件合成:不支持硬件图层(plane)合成,所有图层必须在软件中合成后再写入帧缓冲。
  • 无垂直同步控制:缺乏对 VBlank 的精确控制,无法实现无撕裂的页面翻转。
  • 无连接器管理:不支持热插拔检测、EDID 读取、多显示器配置等现代显示管理功能。
  • 无 Modset 接口:不支持原子模式设置(Atomic Modesetting),配置切换可能产生闪烁。
迁移动机: 上述局限性正是 DRM/KMS 要解决的核心问题。在后续章节中,我们将看到 DRM 如何通过 drm_crtc、drm_plane、drm_connector 等对象优雅地解决这些问题。

2.8 本章小结