1.1 fb_info 结构体概述

fb_info 是 Linux 帧缓冲(Frame Buffer)驱动中最重要的核心数据结构,定义在 include/linux/fb.h 中。它承载了帧缓冲设备的所有关键信息,包括硬件参数、内存映射、操作函数集、设备状态等。每一个帧缓冲设备在系统中都对应一个 fb_info 实例,驱动开发者通过填充该结构体的各个字段来完成设备的初始化与注册。

核心地位:fb_info 是帧缓冲驱动与用户空间之间的桥梁,也是驱动开发者与内核框架之间的契约。没有正确配置的 fb_info,帧缓冲设备无法正常工作。

1.1.1 fb_info 的定义与关键字段

fb_info 结构体包含数十个成员,按功能可分为以下几类:

分类 关键字段 作用描述
设备标识 dev, node 关联内核设备结构体,用于设备模型注册与 sysfs 接口
硬件参数 var, fix 可变参数(分辨率、位深、时序)与固定参数(内存起始、长度、步长)
内存管理 screen_base, screen_size 帧缓冲内存的虚拟地址与物理大小,驱动通过 DMA 或 ioremap 映射
操作函数 fbops 指向 fb_ops 结构体,包含 open/release/ioctl/pan_display 等回调
颜色管理 cmap 颜色查找表(LUT),用于伪彩色模式下的颜色映射
驱动私有数据 par 指向驱动自定义的私有数据结构,用于扩展硬件特定信息
开发提示:在初始化 fb_info 时,var 和 fix 是最先需要填充的两个字段,它们直接决定了用户空间看到的显示参数和内存布局。

1.1.2 fb_info 在帧缓冲驱动中的核心地位

帧缓冲驱动的初始化流程可以概括为:分配 fb_info → 填充字段 → 注册设备。fb_info 处于整个流程的中心位置,其核心作用体现在以下三个方面:

  1. 数据承载中心:所有与显示相关的硬件参数、内存地址、操作函数都集中存储在 fb_info 中,驱动其他模块通过访问 fb_info 获取所需信息。
  2. 用户空间接口:fb_info 通过 fb_ops 中的回调函数实现 open()、ioctl()、mmap() 等系统调用,用户程序通过 /dev/fbX 设备节点间接操作 fb_info 中的数据。
  3. 内核框架集成:fb_info 通过 register_framebuffer() 注册到内核帧缓冲子系统,子系统依赖 fb_info 中的字段进行设备管理、事件通知和电源管理。
常见误区:初学者容易将 fb_info 视为一个简单的“参数容器”,而忽略了其作为驱动生命周期管理核心的角色。fb_info 中的 fbops 和 par 字段在设备热插拔、休眠唤醒等场景中同样关键。

1.1.3 fb_info 结构体关系图

下图展示了 fb_info 与帧缓冲子系统中其他关键组件之间的依赖关系:

fb_info 核心地位关系图 fb_info 在帧缓冲驱动中的核心地位 fb_info 核心数据结构 用户空间 /dev/fbX fb_ops open/ioctl/mmap var + fix 参数与内存信息 screen_base 帧缓冲内存 par 驱动私有数据 内核帧缓冲子系统 register_framebuffer / fbmem 说明:fb_info 通过 fb_ops 连接用户空间,通过 var/fix 描述硬件参数,通过 screen_base 管理显存,通过 par 扩展私有数据

1.1.4 fb_info 的生命周期

在帧缓冲驱动中,fb_info 的典型生命周期包含以下阶段:

  • 分配阶段:使用 framebuffer_alloc() 分配 fb_info 实例,该函数会一并分配私有数据区域。
  • 初始化阶段:填充 var、fix、fbops、screen_base、par 等关键字段。
  • 注册阶段:调用 register_framebuffer() 将 fb_info 注册到内核子系统,生成 /dev/fbX 设备节点。
  • 运行阶段:用户空间通过文件操作接口访问 fb_info 中的数据,驱动通过 fbops 回调响应请求。
  • 注销阶段:调用 unregister_framebuffer() 移除设备节点,释放 fb_info 内存。
关键代码示例:分配与初始化 fb_info 的基本框架
#include <linux/fb.h>
#include <linux/module.h>

static struct fb_info *my_fb_info;

static int my_fb_probe(struct platform_device *pdev)
{
    int ret;

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

    /* 2. 填充固定参数 fix */
    strcpy(my_fb_info->fix.id, "my_fb");
    my_fb_info->fix.smem_start = phys_addr;
    my_fb_info->fix.smem_len = screen_size;
    my_fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
    my_fb_info->fix.visual = FB_VISUAL_TRUECOLOR;
    my_fb_info->fix.line_length = line_length;

    /* 3. 填充可变参数 var */
    my_fb_info->var.xres = 1024;
    my_fb_info->var.yres = 600;
    my_fb_info->var.bits_per_pixel = 32;
    my_fb_info->var.activate = FB_ACTIVATE_NOW;

    /* 4. 设置操作函数集 */
    my_fb_info->fbops = &my_fb_ops;

    /* 5. 映射帧缓冲内存 */
    my_fb_info->screen_base = ioremap_wc(phys_addr, screen_size);
    my_fb_info->screen_size = screen_size;

    /* 6. 注册帧缓冲设备 */
    ret = register_framebuffer(my_fb_info);
    if (ret) {
        iounmap(my_fb_info->screen_base);
        framebuffer_release(my_fb_info);
        return ret;
    }

    return 0;
}
最佳实践:在填充 fix.smem_start 时,务必使用物理地址而非虚拟地址;screen_base 则使用 ioremap_wc() 映射后的虚拟地址,以支持写合并(Write Combining)提升性能。

1.1.5 本章小结