3. struct fb_fix_screeninfo 详解:id、smem_start、smem_len、type、visual 等字段配置

struct fb_fix_screeninfo 是帧缓冲驱动中描述“固定硬件特性”的核心结构体。与可变参数结构体 fb_var_screeninfo 不同,fb_fix_screeninfo 中的字段在硬件初始化后通常不再改变,它向用户空间暴露了帧缓冲设备的内存映射地址、长度、显示类型等底层物理属性。正确配置该结构体是驱动能够被 fbmem.c 核心层正确注册、用户空间能够通过 mmap 直接操作显存的前提。

核心原则: fb_fix_screeninfo 描述的是“硬件是什么”,而 fb_var_screeninfo 描述的是“当前模式是什么”。前者在 fb_setup 或 fb_probe 阶段一次性填充,后者可能随 FBIOGET_VSCREENINFO / FBIOPUT_VSCREENINFO 动态变化。

3.1 结构体全景与关键字段速查

该结构体定义在 include/uapi/linux/fb.h 中。以下表格列出了驱动初始化时必须关注的字段及其配置要点:

字段名 类型 配置要点 典型值 / 说明
id char[16] 设备标识字符串,必须唯一且以 '\0' 结尾 "mydrvfb"、"s3cfb"
smem_start unsigned long 显存物理起始地址,用于 mmap 映射 dma_alloc_coherent 返回的物理地址
smem_len __u32 显存总长度(字节),必须 ≥ 屏幕所需帧缓冲区大小 screen_size 或 width * height * bpp / 8
type __u32 帧缓冲类型,决定像素排列与访问方式 FB_TYPE_PACKED_PIXELS(最常用)
visual __u32 颜色视觉模式,决定颜色映射方式 FB_VISUAL_TRUECOLOR、FB_VISUAL_PSEUDOCOLOR
line_length __u16 一行像素占用的字节数(含对齐填充) width * bpp / 8 向上对齐到 4/8 字节
mmio_start unsigned long 控制寄存器 MMIO 物理地址(可选) platform_get_resource 获取的物理地址
mmio_len __u32 MMIO 区域长度 寄存器地址范围大小
accel __u16 硬件加速能力标志 FB_ACCEL_NONE(无加速)
避坑提示: id 字段长度限制为 16 字节(含终止符),超过 15 个字符的字符串会导致内核缓冲区溢出。建议使用固定短名称,如 "fb_ili9341"。

3.2 字段深度解析与配置场景

3.2.1 id:设备标识的“身份证”

id 字段在用户空间通过 FBIOGET_FSCREENINFO 读取,用于识别具体硬件驱动。它不参与内核逻辑判断,但影响调试与用户态工具(如 fbset -i)的输出。配置时需注意:

  • 必须使用 strncpy 或 snprintf 确保不超过 15 个字符。
  • 建议与驱动名称或设备树 compatible 属性对应,便于故障定位。
  • 示例:strncpy(fix->id, "mylcd_drv", sizeof(fix->id) - 1);

3.2.2 smem_start 与 smem_len:显存映射的基石

这两个字段是帧缓冲驱动最关键的配置项。smem_start 必须是物理地址,用户空间通过 mmap 将该物理地址映射到进程虚拟空间。配置流程如下:

  1. 在 probe 函数中分配 DMA 缓冲区:dma_alloc_coherent(dev, size, &dma_phys, GFP_KERNEL)。
  2. 将返回的物理地址赋值给 smem_start:fix->smem_start = dma_phys。
  3. 将分配的大小赋值给 smem_len:fix->smem_len = size。
  4. 将虚拟地址保存到 info->screen_base,供驱动内部操作。
常见错误: 直接使用 kmalloc 分配内存并赋值给 smem_start。kmalloc 返回的是虚拟地址,不是物理地址,会导致 mmap 失败或访问错误内存。必须使用 dma_alloc_coherent 或 ioremap 处理物理地址。

3.2.3 type:像素排列方式

type 字段定义了帧缓冲中像素数据的组织格式。最常用的枚举值如下:

枚举值 含义 适用场景
FB_TYPE_PACKED_PIXELS 像素数据连续紧密排列,无间隔 绝大多数 LCD、HDMI 显示设备
FB_TYPE_PLANES 像素数据按位平面分离存储 早期 VGA 显卡、某些单色显示器
FB_TYPE_INTERLEAVED_PLANES 位平面交错排列 某些特殊硬件帧缓冲
FB_TYPE_TEXT 非图形模式,仅文本字符 终端控制台帧缓冲

对于现代嵌入式 LCD 控制器,几乎总是配置为 FB_TYPE_PACKED_PIXELS。

3.2.4 visual:颜色视觉模型

visual 字段决定了颜色映射方式,直接影响用户空间如何解释像素值。常见配置:

  • FB_VISUAL_TRUECOLOR:每个像素直接包含 RGB 分量,无需查色表。适用于 16/24/32 bpp 的 LCD。
  • FB_VISUAL_PSEUDOCOLOR:像素值是查色表索引,需要设置 fb_cmap。适用于 8 bpp 及以下的显示设备。
  • FB_VISUAL_MONO01 / FB_VISUAL_MONO10:单色显示,0 代表黑或白取决于具体值。
  • FB_VISUAL_DIRECTCOLOR:每个像素的 R/G/B 分量各自独立查表,较少使用。
配置决策树: 如果硬件支持 16 bpp 以上且 RGB 分量直接编码 → 使用 FB_VISUAL_TRUECOLOR;如果硬件仅支持 8 bpp 且需要调色板 → 使用 FB_VISUAL_PSEUDOCOLOR 并实现 fb_ops.fb_setcolreg。

3.3 字段配置关系与初始化流程 SVG

下图展示了在驱动 probe 函数中,fb_fix_screeninfo 各字段的配置顺序与依赖关系:

fb_fix_screeninfo 字段配置流程 1. dma_alloc_coherent() 2. fix->smem_start / smem_len 3. fix->type / fix->visual 4. fix->line_length 5. fix->id / accel / mmio_* 6. register_framebuffer(info) 关键依赖 smem_start 必须是 物理地址(DMA 或 ioremap) type/visual 需匹配硬件 常见错误 id 超长导致缓冲区溢出 line_length 未对齐 smem_len 小于实际需求

3.4 完整配置代码示例

以下代码片段展示了在 probe 函数中配置 fb_fix_screeninfo 的典型实现:

static int myfb_probe(struct platform_device *pdev)
{
    struct fb_info *info;
    struct fb_fix_screeninfo *fix;
    struct resource *res;
    dma_addr_t dma_phys;
    void __iomem *vaddr;
    size_t screen_size;

    // 1. 分配 fb_info
    info = framebuffer_alloc(0, &pdev->dev);
    if (!info)
        return -ENOMEM;

    fix = &info->fix;

    // 2. 分配 DMA 显存(物理连续)
    screen_size = 800 * 480 * 32 / 8;  // 800x480, 32bpp
    vaddr = dma_alloc_coherent(&pdev->dev, screen_size,
                               &dma_phys, GFP_KERNEL);
    if (!vaddr) {
        framebuffer_release(info);
        return -ENOMEM;
    }
    info->screen_base = vaddr;
    info->screen_size = screen_size;

    // 3. 配置 fix 固定信息
    strncpy(fix->id, "my800x480", sizeof(fix->id) - 1);
    fix->smem_start  = (unsigned long)dma_phys;
    fix->smem_len    = screen_size;
    fix->type        = FB_TYPE_PACKED_PIXELS;
    fix->visual      = FB_VISUAL_TRUECOLOR;
    fix->line_length = 800 * 32 / 8;  // 3200 bytes per line
    fix->accel       = FB_ACCEL_NONE;

    // 4. 可选:MMIO 寄存器区域
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (res) {
        fix->mmio_start = res->start;
        fix->mmio_len   = resource_size(res);
    }

    // 5. 配置可变参数与操作函数(略)
    info->var = myfb_default_var;
    info->fbops = &myfb_ops;
    info->flags = FBINFO_DEFAULT;

    // 6. 注册帧缓冲
    if (register_framebuffer(info) < 0) {
        dma_free_coherent(&pdev->dev, screen_size,
                          vaddr, dma_phys);
        framebuffer_release(info);
        return -EINVAL;
    }

    platform_set_drvdata(pdev, info);
    return 0;
}

3.5 字段配置检查清单

在提交驱动代码前,建议逐项核对以下配置项:

检查项 通过标准 失败后果
id 长度 ≤ 15 字符,以 '\0' 结尾 内核字符串操作越界
smem_start 物理地址 来自 dma_alloc_coherent 或 ioremap mmap 失败或访问错误内存
smem_len 大小 ≥ var.xres * var.yres * var.bits_per_pixel / 8 屏幕显示不全或内存越界
type 与硬件匹配 使用 FB_TYPE_PACKED_PIXELS(非平面模式) 用户空间无法正确解析像素
visual 与颜色深度匹配 ≥16bpp 用 TRUECOLOR;≤8bpp 用 PSEUDOCOLOR 颜色显示完全错误
line_length 对齐 通常 4 字节或 8 字节对齐 部分硬件 DMA 传输异常
调试技巧: 在 register_framebuffer 之后,可以通过 cat /sys/class/graphics/fb0/fix 查看当前 fix 信息,或使用 fbset -i 检查 id、smem_len、line_length 等字段是否与预期一致。