3. fbdev 核心数据结构:fb_info、fb_ops、fb_var_screeninfo 详解

在 Linux fbdev 子系统中,三个核心数据结构构成了驱动与用户空间交互的基石:fb_info(帧缓冲设备描述符)、fb_ops(操作函数表)和 fb_var_screeninfo(可变屏幕参数)。理解这三者的关系与字段含义,是编写或移植 fbdev 驱动的第一步。

3.1 整体架构关系

下图展示了三个核心数据结构在 fbdev 驱动模型中的协作关系:

fbdev 核心数据结构关系图 fbdev 核心数据结构协作关系 struct fb_info 帧缓冲设备核心描述符 struct fb_ops 操作函数表(回调集合) struct fb_var_screeninfo 可变屏幕参数(分辨率/位深) struct fb_fix_screeninfo 固定屏幕参数(地址/步长) fb_info 通过 fb_ops 提供操作接口,通过 fb_var_screeninfo 暴露可调参数
关键理解:fb_info 是“身份证”,fb_ops 是“技能手册”,fb_var_screeninfo 是“可调节的配置面板”。驱动注册时,三者必须协同工作。

3.2 struct fb_info —— 帧缓冲设备描述符

struct fb_info 是 fbdev 驱动中最重要的数据结构,每个帧缓冲设备对应一个实例。它包含了设备的所有状态信息、操作函数指针、屏幕参数以及底层内存管理数据。

字段 类型 说明
state int 设备状态:FBINFO_STATE_RUNNING / FBINFO_STATE_SUSPENDED
node int 设备编号,对应 /dev/fb<node>
flags int 设备标志位,如 FBINFO_FLAG_DEFAULT
fbops struct fb_ops * 指向操作函数表的指针
var struct fb_var_screeninfo 当前可变屏幕参数(分辨率、位深、时序)
fix struct fb_fix_screeninfo 当前固定屏幕参数(物理地址、步长)
screen_base char __iomem * 帧缓冲内存的虚拟地址(内核映射)
screen_size unsigned long 帧缓冲内存总大小(字节)
device struct device * 指向底层硬件设备的指针(用于电源管理)
pseudo_palette void * 伪调色板数据(用于 8/16 位色深)
驱动注册流程:调用 framebuffer_alloc() 分配 fb_info → 填充字段 → 调用 register_framebuffer() 注册到系统。注销时使用 unregister_framebuffer() + framebuffer_release()。

3.3 struct fb_ops —— 操作函数表

struct fb_ops 定义了驱动必须实现或可选实现的回调函数集合。用户空间通过 ioctl 系统调用触发这些回调,完成显示控制。

struct fb_ops {
    /* 必须实现的回调 */
    int (*fb_open)(struct fb_info *info, int user);
    int (*fb_release)(struct fb_info *info, int user);
    int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
    int (*fb_set_par)(struct fb_info *info);
    int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
                        unsigned blue, unsigned transp, struct fb_info *info);
    int (*fb_blank)(int blank_mode, struct fb_info *info);
    /* 可选实现:加速操作 */
    int (*fb_fillrect)(struct fb_info *info, const struct fb_fillrect *rect);
    int (*fb_copyarea)(struct fb_info *info, const struct fb_copyarea *area);
    int (*fb_imageblit)(struct fb_info *info, const struct fb_image *image);
    /* 可选实现:I/O 内存映射 */
    int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
    /* 可选实现:电源管理 */
    int (*fb_sync)(struct fb_info *info);
    /* ... 其他回调 */
};
回调函数 触发场景 典型实现
fb_open 用户打开 /dev/fbX 计数增加、硬件上电
fb_release 用户关闭 /dev/fbX 计数减少、硬件下电
fb_check_var FBIOPUT_VSCREENINFO ioctl 验证分辨率/位深是否支持,调整到最近值
fb_set_par 参数验证通过后 配置硬件寄存器、更新时序
fb_setcolreg FBIOPUTCMAP ioctl 设置调色板寄存器
fb_blank FBIOBLANK ioctl 控制显示空白/唤醒
fb_fillrect FBIO_FILLRECT ioctl 硬件加速矩形填充(可选)
常见陷阱:fb_check_var 必须做参数合法性校验,不能直接返回 0。如果驱动不实现 fb_set_par,用户空间设置分辨率将不会生效。fb_fillrect 等加速回调如果未实现,fbdev 会使用软件 fallback 实现,性能下降明显。

3.4 struct fb_var_screeninfo —— 可变屏幕参数

struct fb_var_screeninfo 是用户空间与内核驱动之间交换显示参数的核心结构。它包含了分辨率、位深、像素时钟、时序等可动态调整的参数。

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=灰度 */
    /* 颜色位域:描述 RGB 各分量在像素中的位置和宽度 */
    struct fb_bitfield red;     /* 红色通道位域 */
    struct fb_bitfield green;   /* 绿色通道位域 */
    struct fb_bitfield blue;    /* 蓝色通道位域 */
    struct fb_bitfield transp;  /* 透明度通道位域 */
    /* 时序参数(像素时钟单位:皮秒) */
    __u32 pixclock;       /* 像素时钟周期(皮秒) */
    __u32 left_margin;    /* 行同步前沿(HBP) */
    __u32 right_margin;   /* 行同步后沿(HFP) */
    __u32 upper_margin;   /* 帧同步前沿(VBP) */
    __u32 lower_margin;   /* 帧同步后沿(VFP) */
    __u32 hsync_len;      /* 行同步脉冲宽度 */
    __u32 vsync_len;      /* 帧同步脉冲宽度 */
    __u32 sync;           /* 同步极性标志 */
    __u32 vmode;          /* 显示模式:FB_VMODE_NONINTERLACED 等 */
    __u32 rotate;         /* 旋转角度:0/90/180/270 */
    /* ... 其他字段 */
};

3.5 参数验证流程

当用户空间通过 FBIOPUT_VSCREENINFO ioctl 设置新参数时,内核会执行以下验证流程:

fb_var_screeninfo 参数验证流程 fb_var_screeninfo 参数验证与生效流程 用户空间设置参数 fb_check_var 验证参数 参数无效 → 返回 -EINVAL 参数有效 → 更新 var fb_set_par 硬件生效 fb_check_var 必须对 xres、yres、bits_per_pixel 等关键字段做范围校验

3.6 颜色位域详解

struct fb_bitfield 用于描述颜色通道在像素数据中的编码方式,是理解不同色深格式的关键:

色深 格式 red green blue transp
32bpp ARGB8888 offset=16, length=8 offset=8, length=8 offset=0, length=8 offset=24, length=8
24bpp RGB888 offset=16, length=8 offset=8, length=8 offset=0, length=8 offset=0, length=0
16bpp RGB565 offset=11, length=5 offset=5, length=6 offset=0, length=5 offset=0, length=0
8bpp 索引色 offset=0, length=8 offset=0, length=8 offset=0, length=8 offset=0, length=0
调试技巧:使用 fbset -i 命令可以查看当前 fb_var_screeninfo 的所有字段值,包括分辨率、位深、时序和颜色位域。这是排查显示参数问题的第一手段。

3.7 虚拟分辨率与滚屏机制

xres_virtual 和 yres_virtual 支持比可见区域更大的虚拟画布,通过 xoffset / yoffset 实现滚屏。这种机制在控制台终端滚动时非常有用:

/* 设置虚拟分辨率大于可见分辨率,实现双缓冲滚屏 */
info->var.xres = 1024;
info->var.yres = 768;
info->var.xres_virtual = 1024;
info->var.yres_virtual = 1536;  /* 两倍高度 */
info->var.xoffset = 0;
info->var.yoffset = 0;          /* 显示上半部分 */
/* 用户空间通过 FBIOPAN_DISPLAY ioctl 调整 yoffset 实现滚屏 */
性能优势:滚屏操作只需要修改显示控制器的起始地址寄存器,不需要拷贝帧缓冲内存数据,极大提升滚动效率。这是 fbdev 在嵌入式系统中仍然被广泛使用的重要原因之一。