4. fbdev核心数据结构深度解析

帧缓冲设备驱动(fbdev)的核心在于三组紧密关联的数据结构:struct fb_info(设备抽象)、struct fb_var_screeninfo(可变参数)和 struct fb_fix_screeninfo(固定参数)。这三者构成了用户空间与内核空间交互的契约,也是驱动开发者必须掌握的第一组核心概念。

核心关系速览:struct fb_info 是帧缓冲设备的“总控”结构体,内部包含指向 fb_var_screeninfo 和 fb_fix_screeninfo 的实例,以及操作函数集 fb_ops、设备私有数据、DMA 缓冲区信息等。用户空间通过 ioctl 系统调用读取/写入可变与固定参数,驱动则通过 fb_info 完成硬件配置与内存映射。

4.1 struct fb_info:帧缓冲设备的“身份证”

struct fb_info 是 fbdev 框架中最顶层的结构体,每个注册到系统的帧缓冲设备都对应一个 fb_info 实例。它聚合了设备的所有属性、操作函数和状态信息。

struct fb_info {
    atomic_t count;                 /* 引用计数 */
    int node;                       /* 设备节点号(如 /dev/fb0 的 0) */
    int flags;                      /* 设备标志位 */
    struct mutex lock;              /* 互斥锁 */
    struct mutex mm_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; /* 延迟IO支持 */
    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 是用户空间 mmap 映射的目标虚拟地址;screen_size 决定了映射范围;fbops 是驱动必须实现的函数集,包括 fb_open、fb_release、fb_setcolreg、fb_check_var、fb_set_par、fb_blank 等。

4.1.1 fb_info 生命周期与注册流程

一个帧缓冲设备从注册到卸载的典型流程如下:

  1. 分配:调用 framebuffer_alloc(size) 分配 fb_info 及私有数据空间。
  2. 初始化:填充 var、fix、fbops、screen_base、screen_size 等字段。
  3. 注册:调用 register_framebuffer(fb_info) 将设备注册到系统。
  4. 用户交互:通过 /dev/fbX 节点进行 read/write/mmap/ioctl 操作。
  5. 注销:调用 unregister_framebuffer(fb_info) 移除设备。
  6. 释放:调用 framebuffer_release(fb_info) 释放内存。
常见陷阱:register_framebuffer 成功后,驱动不应再直接修改 fix 结构体中的 mmio_start 和 mmio_len 字段,因为这些字段已被用于创建 sysfs 属性。修改应在注册前完成。

4.2 struct fb_var_screeninfo:可变参数的“调色盘”

struct fb_var_screeninfo 描述的是用户空间可以动态修改的显示参数,包括分辨率、位深、时序、虚拟屏幕尺寸等。用户通过 FBIOPUT_VSCREENINFO ioctl 写入,通过 FBIOGET_VSCREENINFO 读取。

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;           /* 灰度标志:0=彩色,非0=灰度 */
    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 colorspace;          /* 色彩空间 */
    __u32 reserved[4];         /* 保留字段 */
};

4.2.1 可变参数与显示模式切换

当用户空间修改 var 参数时,驱动必须通过 fb_check_var 验证参数的合法性,然后通过 fb_set_par 将参数应用到硬件。下表列出了常见分辨率对应的典型 var 参数值:

参数 640×480@60Hz 1024×768@60Hz 1920×1080@60Hz
xres / yres 640 / 480 1024 / 768 1920 / 1080
bits_per_pixel 32 32 32
pixclock (ps) 39721 15384 6734
left_margin 48 160 88
right_margin 16 24 44
upper_margin 33 29 4
lower_margin 10 3 5
hsync_len 96 136 148
vsync_len 2 6 36
虚拟屏幕与双缓冲:xres_virtual 和 yres_virtual 可以大于 xres 和 yres,实现虚拟屏幕。通过修改 xoffset/yoffset 实现滚动,或通过分配两倍高度的虚拟屏幕实现双缓冲(page flipping),这是嵌入式图形中常用的优化手段。

4.3 struct fb_fix_screeninfo:固定参数的“硬件铭牌”

struct fb_fix_screeninfo 描述的是硬件固定不变的参数,在设备生命周期内通常只读。用户空间通过 FBIOGET_FSCREENINFO ioctl 获取这些信息。

struct fb_fix_screeninfo {
    char id[16];               /* 设备标识字符串(如 "CLFB") */
    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;         /* 一行占用的字节数(步长) */
    unsigned long mmio_start;  /* 内存映射IO物理起始地址 */
    __u32 mmio_len;            /* 内存映射IO长度 */
    __u32 accel;               /* 硬件加速类型 */
    __u16 capabilities;        /* 能力标志 */
    __u16 reserved[2];         /* 保留字段 */
};

4.3.1 固定参数关键字段解析

字段 含义 典型值/说明
smem_start 帧缓冲物理地址 用于 mmap 建立 DMA 映射,用户空间不可直接访问
smem_len 帧缓冲总大小 通常 = xres_virtual × yres_virtual × (bpp/8)
line_length 一行像素的字节数 可能大于 xres × (bpp/8),因硬件对齐要求
visual 颜色模式 FB_VISUAL_TRUECOLOR / FB_VISUAL_PSEUDOCOLOR / FB_VISUAL_DIRECTCOLOR
type 像素排列类型 FB_TYPE_PACKED_PIXELS(最常见)/ FB_TYPE_PLANES / FB_TYPE_INTERLEAVED_PLANES
xpanstep / ypanstep 滚动步长 0 表示不支持硬件滚动,非0表示步长对齐要求

4.4 三者的协作关系与数据流

下图展示了 struct fb_info、fb_var_screeninfo、fb_fix_screeninfo 三者之间的协作关系,以及用户空间与内核空间的交互路径:

fbdev 核心数据结构协作关系图 用户空间应用程序 /dev/fbX read / write / mmap / ioctl 内核 fbdev 核心层 fbmem.c / fbcon.c ioctl 分发与参数校验 struct fb_info screen_base / screen_size fbops 操作函数集 par 驱动私有数据 pseudo_palette fbdefio 延迟IO fb_var_screeninfo 分辨率 / 位深 / 时序 虚拟屏幕 / 偏移 fb_fix_screeninfo 物理地址 / 步长 颜色模式 / 类型 硬件设备 LCD 控制器 / GPU / 显示引擎 系统调用 查找/引用 包含 包含 驱动控制 说明: 用户空间通过 ioctl 读取/写入 var 和 fix;内核核心层通过 fb_info 调用驱动 fbops;驱动通过 fb_info.par 访问硬件寄存器。

4.5 数据结构间的计算关系

fb_var_screeninfo 和 fb_fix_screeninfo 之间存在紧密的计算依赖关系,驱动在 fb_set_par 中必须根据 var 参数重新计算 fix 中的 line_length 和 smem_len:

/* 典型计算逻辑 */
static int myfb_set_par(struct fb_info *info)
{
    struct fb_var_screeninfo *var = &info->var;
    struct fb_fix_screeninfo *fix = &info->fix;

    /* 计算每行字节数(需硬件对齐) */
    int bpp = var->bits_per_pixel;
    int line_length = var->xres_virtual * bpp / 8;
    /* 硬件可能要求 4 字节、8 字节或 16 字节对齐 */
    line_length = ALIGN(line_length, 16);

    fix->line_length = line_length;

    /* 计算帧缓冲总大小 */
    fix->smem_len = line_length * var->yres_virtual;

    /* 配置硬件时序寄存器 */
    write_hw_timing(var->pixclock, var->left_margin, /* ... */);

    /* 更新屏幕基地址(如果支持双缓冲) */
    info->screen_base = info->par->framebuffer_base;
    info->screen_size = fix->smem_len;

    return 0;
}
对齐的重要性:许多显示控制器要求每行像素的起始地址对齐到 4 字节、8 字节甚至 16 字节。如果 line_length 未正确对齐,可能导致显示错位或性能下降。xpanstep 和 ypanstep 字段反映了硬件对滚动步长的对齐要求。

4.6 颜色模式与位域解析

struct fb_bitfield 用于描述每个颜色通道在像素中的位置和宽度,是理解真彩色/直接色模式的关键:

struct fb_bitfield {
    __u32 offset;   /* 通道在像素中的起始位偏移 */
    __u32 length;   /* 通道占用的位数 */
    __u32 msb_right;/* 最高有效位是否在右侧(通常为0) */
};

对于常见的 32 位 ARGB 格式,位域配置如下:

通道 offset length 说明
red 16 8 红色通道位于 bit[23:16]
green 8 8 绿色通道位于 bit[15:8]
blue 0 8 蓝色通道位于 bit[7:0]
transp 24 8 透明度通道位于 bit[31:24]
字节序陷阱:fb_bitfield 的定义与 CPU 字节序无关,它描述的是像素数据在内存中的位布局。在 little-endian 系统上,32 位像素 0xAABBCCDD 在内存中存储为 DD CC BB AA,此时 blue 通道 offset=0 对应最低字节。驱动和用户空间必须就像素格式达成一致,否则会出现颜色错乱。

4.7 本章小结

  • struct fb_info 是帧缓冲设备的统一抽象,聚合了可变参数、固定参数、操作函数集和硬件状态。
  • struct fb_var_screeninfo 提供用户空间可动态修改的显示参数,驱动需通过 fb_check_var 和 fb_set_par 完成验证与生效。
  • struct fb_fix_screeninfo 描述硬件固定属性,用户空间通过它获取帧缓冲的物理布局和颜色模式。
  • 三者的协作构成了 fbdev 框架的数据契约,理解它们之间的关系是编写正确帧缓冲驱动的基础。
  • line_length 的对齐计算、位域的正确配置、虚拟屏幕与双缓冲的实现是实际开发中的关键难点。