3. 帧缓冲数据结构:struct fb_info、struct fb_var_screeninfo、struct fb_fix_screeninfo

在 Linux 帧缓冲驱动框架中,三个核心数据结构构成了用户态与内核态、硬件与软件之间的桥梁。理解 struct fb_info、struct fb_var_screeninfo 和 struct fb_fix_screeninfo 的职责与关系,是掌握 mmap 映射与图层合成调试的基础。

3.1 数据结构总览与职责划分

三个结构体分别承担不同的抽象层次:

结构体 职责 存储位置 主要字段
struct fb_info 帧缓冲设备的核心控制块,管理整个设备实例 内核内存(动态分配) var、fix、fbops、screen_base、screen_size、par
struct fb_var_screeninfo 可变参数:分辨率、位深、时序、虚拟屏幕 用户态可读写(通过 FBIOGET_VSCREENINFO / FBIOPUT_VSCREENINFO) xres、yres、bits_per_pixel、pixclock、left_margin、right_margin
struct fb_fix_screeninfo 固定参数:物理地址、内存长度、行宽、颜色格式 用户态只读(通过 FBIOGET_FSCREENINFO) smem_start、smem_len、line_length、type、visual
调试要点: 用户态程序通过 ioctl 获取 fix 信息来确定 mmap 映射的物理基址和长度;通过 var 信息来配置图层分辨率与像素格式。驱动开发者则主要操作 fb_info 中的 fbops 函数指针表。

3.2 struct fb_info:帧缓冲设备的核心枢纽

struct fb_info 是帧缓冲驱动中最重要的数据结构,每个注册的帧缓冲设备对应一个 fb_info 实例。它聚合了所有操作所需的信息:

struct fb_info {
    struct fb_var_screeninfo var;   /* 可变参数 */
    struct fb_fix_screeninfo fix;   /* 固定参数 */
    struct fb_ops *fbops;           /* 操作函数表 */
    struct device *device;          /* 关联的设备 */
    char __iomem *screen_base;      /* 显存虚拟地址(内核映射) */
    unsigned long screen_size;      /* 显存大小(字节) */
    void *par;                      /* 驱动私有数据 */
    struct fb_pixmap pixmap;        /* 辅助像素缓冲区 */
    struct fb_deferred_io deferred_io; /* 延迟IO机制 */
    /* ... 其他字段 */
};
关键字段解读:
  • screen_base:内核通过 ioremap 或 dma_alloc_coherent 映射后的虚拟地址,mmap 实现中需要将其映射到用户空间。
  • screen_size:决定了 mmap 的最大映射长度,用户态 mmap 时 length 参数不能超过此值。
  • fbops:包含 fb_open、fb_release、fb_read、fb_write、fb_mmap、fb_ioctl 等函数指针,驱动开发者通过填充此结构体实现具体硬件操作。
  • par:驱动私有数据指针,通常指向包含硬件寄存器基址、时钟配置、图层控制寄存器地址的自定义结构体。

3.3 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;           /* 灰度标志 */
    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;              /* 旋转角度 */
    /* ... 其他字段 */
};
常见调试陷阱: 在图层合成场景中,xres_virtual 和 yres_virtual 通常设置为 xres 和 2 * yres 以实现双缓冲。如果 mmap 后写入数据但屏幕无变化,请检查 yoffset 是否指向了正确的显示缓冲区起始位置。

3.4 struct fb_fix_screeninfo:硬件固定的内存布局

此结构体描述硬件固定不变的参数,用户态通过 FBIOGET_FSCREENINFO 获取,用于指导 mmap 映射:

struct fb_fix_screeninfo {
    char id[16];               /* 设备标识字符串,如 "s3cfb" */
    unsigned long smem_start;  /* 显存物理起始地址 */
    __u32 smem_len;            /* 显存总长度(字节) */
    __u32 type;                /* 显示类型:FB_TYPE_PACKED_PIXELS 等 */
    __u32 type_aux;            /* 辅助类型 */
    __u32 visual;              /* 颜色模式:FB_VISUAL_TRUECOLOR 等 */
    __u16 xpanstep;            /* 水平硬件平移步长 */
    __u16 ypanstep;            /* 垂直硬件平移步长 */
    __u16 ywrapstep;           /* 垂直硬件环绕步长 */
    __u32 line_length;         /* 一行像素的字节数 */
    unsigned long mmio_start;  /* 内存映射IO物理起始地址 */
    __u32 mmio_len;            /* 内存映射IO长度 */
    __u32 accel;               /* 硬件加速类型 */
    /* ... 其他字段 */
};
mmap 映射关键参数:
  • smem_start:用户态 mmap 时 offset 参数通常设为 0,映射从此物理地址开始。
  • smem_len:映射的最大长度,用户态 mmap 的 length 不能超过此值。
  • line_length:计算每行像素在内存中的实际占用字节数,用于正确计算像素偏移地址。
  • visual:指示颜色格式,如 FB_VISUAL_TRUECOLOR(真彩色)或 FB_VISUAL_DIRECTCOLOR(直接色)。

3.5 数据结构关系与 mmap 映射流程

下图展示了三个结构体在帧缓冲驱动中的协作关系,以及用户态 mmap 调用时的数据流向:

帧缓冲数据结构关系与 mmap 映射流程 用户态应用程序 open("/dev/fb0") ioctl(FBIOGET_FSCREENINFO) ioctl(FBIOGET_VSCREENINFO) mmap(0, smem_len, ...) 内核帧缓冲核心 struct fb_info ├─ var (可变参数) ├─ fix (固定参数) └─ fbops (操作函数表) 硬件设备 显存物理地址 LCD 控制器 图层寄存器 ioctl / mmap ioremap / 寄存器 mmap 映射数据流 ① 用户态调用 mmap(0, smem_len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0) ② 内核调用 fb_mmap 回调(在 fbops 中注册) ③ fb_mmap 从 fb_info->fix.smem_start 获取物理地址,从 fb_info->screen_size 获取长度 ④ 调用 remap_pfn_range 将物理显存映射到用户进程地址空间 struct fb_var_screeninfo 分辨率 / 位深 / 时序 虚拟屏幕 / 偏移 颜色位域 / 像素时钟 用户态可读写 struct fb_fix_screeninfo 物理地址 / 内存长度 行宽 / 显示类型 颜色模式 / 加速类型 用户态只读 struct fb_info 包含 var 和 fix 实例 screen_base / screen_size fbops 函数指针表 驱动私有数据 par

3.6 图层合成场景中的数据结构应用

在多图层合成调试中,三个结构体各自承担关键角色:

  • 主图层(Layer 0): 通常使用 fb0 设备,通过 fb_var_screeninfo 配置分辨率为屏幕原生分辨率,bits_per_pixel 设为 32(RGBA8888)。
  • 叠加图层(Layer 1~N): 每个图层对应独立的 fb_info 实例,通过 fb_fix_screeninfo.smem_start 获取各自的显存物理地址,用于硬件图层合成器的源地址寄存器配置。
  • 透明度合成: fb_var_screeninfo.transp 字段定义了透明度通道的位域偏移和长度,硬件图层合成器据此解析 Alpha 值进行混合。
  • 虚拟屏幕滚动: 通过修改 fb_var_screeninfo.yoffset 实现双缓冲切换,无需重新 mmap,仅需更新显示起始地址。
调试技巧: 在图层合成调试中,使用 cat /sys/class/graphics/fb0/fb_info 或 fbset -i 命令可以快速查看当前 var 和 fix 参数的实际值,验证配置是否正确。

3.7 数据结构初始化示例(驱动开发视角)

以下代码片段展示了驱动中如何初始化三个结构体并注册帧缓冲设备:

static int myfb_probe(struct platform_device *pdev)
{
    struct fb_info *fbi;
    struct myfb_par *par;

    /* 1. 分配 fb_info 结构体 */
    fbi = framebuffer_alloc(sizeof(struct myfb_par), &pdev->dev);
    if (!fbi)
        return -ENOMEM;

    par = fbi->par;

    /* 2. 设置固定参数 */
    strscpy(fbi->fix.id, "myfb", 16);
    fbi->fix.smem_start = par->phys_addr;   /* 物理显存地址 */
    fbi->fix.smem_len = SZ_8M;              /* 8MB 显存 */
    fbi->fix.type = FB_TYPE_PACKED_PIXELS;
    fbi->fix.visual = FB_VISUAL_TRUECOLOR;
    fbi->fix.line_length = 1920 * 4;        /* 1920x1080, 32bpp */

    /* 3. 设置可变参数 */
    fbi->var.xres = 1920;
    fbi->var.yres = 1080;
    fbi->var.xres_virtual = 1920;
    fbi->var.yres_virtual = 2160;           /* 双缓冲 */
    fbi->var.bits_per_pixel = 32;
    fbi->var.red.offset = 16;
    fbi->var.red.length = 8;
    fbi->var.green.offset = 8;
    fbi->var.green.length = 8;
    fbi->var.blue.offset = 0;
    fbi->var.blue.length = 8;
    fbi->var.transp.offset = 24;
    fbi->var.transp.length = 8;

    /* 4. 设置操作函数表 */
    fbi->fbops = &myfb_ops;

    /* 5. 映射显存到内核虚拟地址 */
    fbi->screen_base = ioremap_wc(par->phys_addr, SZ_8M);
    fbi->screen_size = SZ_8M;

    /* 6. 注册帧缓冲设备 */
    if (register_framebuffer(fbi) < 0) {
        iounmap(fbi->screen_base);
        framebuffer_release(fbi);
        return -EINVAL;
    }

    platform_set_drvdata(pdev, fbi);
    return 0;
}
注意事项: 在图层合成硬件中,每个图层的 smem_start 必须指向物理连续的显存区域。如果使用 dma_alloc_coherent 分配内存,需要确保分配的大小与 smem_len 一致,并且物理地址正确写入 fix.smem_start。