3. fbdev 核心数据结构:struct fb_info、struct fb_var_screeninfo、struct fb_fix_screeninfo

帧缓冲设备驱动(fbdev)的核心在于三个相互关联的数据结构,它们共同构成了用户空间与硬件帧缓冲之间的抽象层。理解这三个结构体是掌握 fbdev 框架的基石。

核心关系:struct fb_info 是驱动层的“总控”结构,它内部包含了描述可变参数的 struct fb_var_screeninfo 和描述固定参数的 struct fb_fix_screeninfo。用户空间通过 ioctl 操作 fb_info,进而读写 var 和 fix 信息。

3.1 struct fb_info:帧缓冲设备的主控结构

struct fb_info 是 fbdev 框架中最重要的数据结构,每个注册的帧缓冲设备都对应一个该结构体的实例。它聚合了设备的所有属性、操作函数指针、底层内存管理以及私有数据。

struct fb_info {
    atomic_t count;                 // 引用计数
    int node;                       // 设备节点号 (如 /dev/fb0 的 0)
    int flags;                      // 设备标志位
    struct mutex lock;              // 互斥锁,保护并发访问
    struct fb_var_screeninfo var;   // 可变屏幕信息(用户可修改)
    struct fb_fix_screeninfo fix;   // 固定屏幕信息(硬件决定)
    struct fb_ops *fbops;           // 操作函数集(关键)
    struct device *device;          // 内核设备模型指针
    struct device *dev;             // 父设备
    int class_flag;                 // 类标志
    struct fb_deferred_io *fbdefio; // 延迟 I/O 支持
    struct fb_pixmap pixmap;        // 像素图辅助
    struct fb_pixmap sprite;        // 光标/精灵图
    struct fb_cursor cursor;        // 光标状态
    struct fb_monspecs monspecs;    // 显示器规格
    struct fb_videomode *mode;      // 当前视频模式
    struct fb_videomode *modelist;  // 支持的模式列表
    struct fb_ops *fbops;           // 操作函数集
    char __iomem *screen_base;      // 帧缓冲内存的虚拟基地址
    unsigned long screen_size;      // 帧缓冲内存总大小(字节)
    void *pseudo_palette;           // 伪调色板
    struct fb_info *par;            // 驱动私有数据指针
    // ... 其他辅助字段
};
关键字段解读:
  • screen_base / screen_size:这是驱动映射后的虚拟地址和大小,用户空间通过 mmap 最终映射的就是这片内存。
  • fbops:函数指针结构体,驱动必须实现其中的 fb_open、fb_release、fb_setcolreg、fb_blank、fb_pan_display 等回调。
  • var / fix:这两个内嵌结构体是用户空间与内核交互的主要数据载体。
  • par:驱动私有数据指针,通常指向驱动自定义的结构体,用于存放硬件寄存器地址、中断号等。
struct fb_info 核心结构示意图 struct fb_info 核心结构示意图 struct fb_info (主控结构体) struct fb_var_screeninfo xres, yres, bits_per_pixel pixclock, left_margin, right_margin upper_margin, lower_margin, hsync_len vsync_len, vmode, activate, nonstd struct fb_fix_screeninfo smem_start, smem_len type, type_aux, visual xpanstep, ypanstep, ywrapstep line_length, mmio_start, mmio_len screen_base 虚拟基地址 fbops 操作函数集指针 par 驱动私有数据指针 每个 fb_info 实例对应一个 /dev/fbX 设备节点,驱动通过 register_framebuffer() 注册

3.2 struct fb_var_screeninfo:可变屏幕信息

该结构体描述的是用户空间可以动态修改的显示参数,例如分辨率、颜色深度、时序参数等。当用户通过 FBIOPUT_VSCREENINFO ioctl 修改这些值时,驱动需要重新配置硬件。

struct fb_var_screeninfo {
    __u32 xres;             // 可见分辨率:水平像素数
    __u32 yres;             // 可见分辨率:垂直像素数
    __u32 xres_virtual;     // 虚拟分辨率:水平(可大于 xres,用于双缓冲)
    __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 nonstd;           // 非标准像素格式标志
    __u32 activate;         // 激活模式标志
    __u32 height;           // 显示器物理高度 (mm)
    __u32 width;            // 显示器物理宽度 (mm)
    __u32 accel_flags;      // 硬件加速标志
    __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;           // 旋转角度
    __u32 reserved[5];      // 保留字段
};
常见陷阱:修改 var 参数时,驱动必须验证时序参数的有效性(如 pixclock 不能为 0,分辨率不能超过硬件限制)。如果驱动不支持动态修改,应在 fb_ops->fb_check_var 中返回 -EINVAL。

3.3 struct fb_fix_screeninfo:固定屏幕信息

该结构体描述的是硬件决定的、用户空间通常不能修改的固定属性,例如帧缓冲物理地址、内存大小、像素格式类型、行跨度等。用户通过 FBIOGET_FSCREENINFO ioctl 获取这些信息。

struct fb_fix_screeninfo {
    char id[16];            // 设备标识字符串 (如 "radeonfb")
    unsigned long smem_start;   // 帧缓冲物理起始地址
    __u32 smem_len;         // 帧缓冲内存总长度 (字节)
    __u32 type;             // 显示类型 (如 FB_TYPE_PACKED_PIXELS)
    __u32 type_aux;         // 辅助类型
    __u32 visual;           // 视觉类型 (真彩色/伪彩色/直接色)
    __u16 xpanstep;         // 水平 pan 步进 (0 表示不支持)
    __u16 ypanstep;         // 垂直 pan 步进
    __u16 ywrapstep;        // 垂直 wrap 步进
    __u32 line_length;      // 一行像素的字节数 (跨度)
    unsigned long mmio_start;   // MMIO 区域物理起始地址
    __u32 mmio_len;         // MMIO 区域长度
    __u32 accel;            // 加速器类型
    __u16 capabilities;     // 能力标志
    __u16 reserved[2];      // 保留
};

3.4 var 与 fix 的对比与协作

对比维度 struct fb_var_screeninfo struct fb_fix_screeninfo
修改权限 用户空间可通过 ioctl 修改 只读,由驱动在初始化时设置
典型内容 分辨率、bpp、时序、偏移 物理地址、内存大小、行跨度、视觉类型
更新时机 运行时动态切换(如 fbset 命令) 驱动 probe 阶段固定,一般不变化
关联 ioctl FBIOGET_VSCREENINFO / FBIOPUT_VSCREENINFO FBIOGET_FSCREENINFO
硬件依赖 部分依赖(时序需硬件支持) 强依赖(物理地址、内存布局)
var 与 fix 协作流程图 var 与 fix 协作流程图 用户空间应用 fbset / Xorg / Wayland ioctl fbdev 核心层 fbmem.c / fbcon.c struct fb_var_screeninfo fb_check_var → fb_set_par 验证并应用新分辨率/时序 struct fb_fix_screeninfo 驱动 probe 时填充 用户空间只读查询 用户空间通过 mmap 映射 screen_base 后,直接读写帧缓冲内存

3.5 驱动初始化中的典型填充流程

在驱动 probe 函数中,通常按照以下步骤填充这三个核心结构体:

  1. 分配 fb_info:调用 framebuffer_alloc() 分配 struct fb_info 实例,同时分配私有数据 par。
  2. 填充 fix 信息:设置 smem_start(物理地址)、smem_len(内存大小)、type、visual、line_length 等固定参数。
  3. 填充 var 默认值:设置默认分辨率、bpp、时序参数,确保 fb_check_var 能通过验证。
  4. 注册 fbops:将驱动实现的 fb_ops 函数集赋值给 fb_info->fbops。
  5. 映射帧缓冲内存:通过 ioremap() 或 dma_alloc_coherent() 映射物理内存到 screen_base。
  6. 注册设备:调用 register_framebuffer(fb_info) 将设备注册到内核。
static int myfb_probe(struct platform_device *pdev)
{
    struct fb_info *info;
    struct myfb_par *par;

    // 1. 分配 fb_info 和私有数据
    info = framebuffer_alloc(sizeof(struct myfb_par), &pdev->dev);
    if (!info)
        return -ENOMEM;
    par = info->par;

    // 2. 填充固定信息
    strcpy(info->fix.id, "myfb");
    info->fix.smem_start = par->fb_phys;
    info->fix.smem_len = SZ_4M;
    info->fix.type = FB_TYPE_PACKED_PIXELS;
    info->fix.visual = FB_VISUAL_TRUECOLOR;
    info->fix.line_length = 1024 * (32 / 8);  // 1024x768, 32bpp

    // 3. 填充可变信息默认值
    info->var.xres = 1024;
    info->var.yres = 768;
    info->var.bits_per_pixel = 32;
    info->var.xres_virtual = 1024;
    info->var.yres_virtual = 768;
    // ... 填充时序参数

    // 4. 设置操作函数集
    info->fbops = &myfb_ops;

    // 5. 映射帧缓冲内存
    info->screen_base = ioremap_wc(par->fb_phys, SZ_4M);
    info->screen_size = SZ_4M;

    // 6. 注册设备
    if (register_framebuffer(info) < 0) {
        iounmap(info->screen_base);
        framebuffer_release(info);
        return -EINVAL;
    }

    platform_set_drvdata(pdev, info);
    return 0;
}
初始化顺序要点:
  • 必须先填充 fix 和 var,再调用 register_framebuffer(),因为注册时内核会立即调用 fb_set_par() 应用参数。
  • screen_base 必须在注册前映射完成,否则用户空间 mmap 会失败。
  • fb_check_var 回调必须验证 var 中的所有参数,包括时序、分辨率、bpp 的合法性。

3.6 用户空间视角:通过 ioctl 访问

用户空间程序通过打开 /dev/fbX 设备文件,使用 ioctl 系统调用与这三个结构体交互:

ioctl 命令 操作对象 功能描述
FBIOGET_FSCREENINFO struct fb_fix_screeninfo 获取固定信息(物理地址、行跨度等)
FBIOGET_VSCREENINFO struct fb_var_screeninfo 获取当前可变参数
FBIOPUT_VSCREENINFO struct fb_var_screeninfo 设置新的可变参数(触发模式切换)
FBIOGET_VBLANK struct fb_vblank 获取垂直消隐状态(用于同步)
FBIO_WAITFORVSYNC __u32 crtc 等待垂直同步信号
实用技巧:使用 fbset 命令可以快速查看和修改 var 参数。例如 fbset -fb /dev/fb0 -xres 1920 -yres 1080 -v 会触发 FBIOPUT_VSCREENINFO,驱动需要实现对应的 fb_set_par 回调来重新配置硬件时序。