1. 初始化帧缓冲设备

初始化帧缓冲设备 学习卡片

任务说明

编写一个Linux内核模块,完成帧缓冲设备的注册,并实现最基本的open/release文件操作。模块加载时动态分配帧缓冲设备,卸载时正确释放。通过/dev/fb*设备节点验证模块是否注册成功。

环境依赖

  • Linux内核源码树(版本需与运行内核一致),路径如/lib/modules/$(uname -r)/build
  • 内核配置开启CONFIG_FB支持
  • 具备root权限用于模块加载与设备节点操作

完整代码

// fb_init.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fb.h>
#include <linux/platform_device>
#include <linux/uaccess.h>

static struct fb_info *fb_info;
static struct fb_fix_screeninfo fix_info = {
    .id = "myfb",
    .type = FB_TYPE_PACKED_PIXELS,
    .visual = FB_VISUAL_TRUECOLOR,
    .line_length = 1024 * 4,
    .smem_len = 1024 * 768 * 4,
};

static int myfb_open(struct fb_info *info, int user)
{
    pr_info("myfb: open (user=%d)\n", user);
    return 0;
}

static int myfb_release(struct fb_info *info, int user)
{
    pr_info("myfb: release (user=%d)\n", user);
    return 0;
}

static struct fb_ops myfb_ops = {
    .owner = THIS_MODULE,
    .fb_open = myfb_open,
    .fb_release = myfb_release,
    // 其余操作由fbdev核心提供默认实现
};

static int __init myfb_init(void)
{
    fb_info = framebuffer_alloc(0, NULL);
    if (!fb_info)
        return -ENOMEM;

    fb_info->fix = fix_info;
    fb_info->var.xres = 1024;
    fb_info->var.yres = 768;
    fb_info->var.bits_per_pixel = 32;
    fb_info->fbops = &myfb_ops;
    fb_info->screen_base = NULL;  // 无真实显存,仅注册设备

    if (register_framebuffer(fb_info) < 0) {
        framebuffer_release(fb_info);
        return -EINVAL;
    }
    pr_info("myfb: registered as /dev/fb%d\n", fb_info->node);
    return 0;
}

static void __exit myfb_exit(void)
{
    if (fb_info) {
        unregister_framebuffer(fb_info);
        framebuffer_release(fb_info);
        pr_info("myfb: unregistered\n");
    }
}

module_init(myfb_init);
module_exit(myfb_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Minimal framebuffer device with open/release");

Makefile

obj-m := fb_init.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

all:
	$(MAKE) -C $(KDIR) M=$(PWD) modules

clean:
	$(MAKE) -C $(KDIR) M=$(PWD) clean

编译与加载

make
sudo insmod fb_init.ko
dmesg | tail -5   # 应看到 "myfb: registered as /dev/fbN"
ls -l /dev/fb*    # 确认新设备节点存在

验证方法

  1. 注册验证:加载模块后执行cat /proc/fb,输出中应包含0 myfb(或对应编号)。
  2. open/release验证:执行sudo cat /dev/fbN(N为实际编号),该操作会触发内核的open调用;随后用dmesg | tail查看,应依次出现myfb: open (user=1)和myfb: release (user=1)两条日志。由于未分配真实显存,cat读取可能返回错误,这属于预期行为——本案例仅验证设备注册与open/release回调被正确调用。
  3. 卸载验证:sudo rmmod fb_init后再次执行cat /proc/fb,确认myfb条目消失。

常见问题

  • 注册失败(register_framebuffer返回负值):检查内核是否支持CONFIG_FB,以及系统是否已有帧缓冲设备占用编号。可尝试在myfb_init中指定fb_info->node = -1让内核自动分配。
  • insmod报错“Invalid module format”:确认Makefile中的KDIR指向的内核源码树与当前运行内核版本完全一致。
  • open回调未被调用:某些用户态程序(如cat)可能因设备无显存而提前失败。可用sudo dd if=/dev/fbN of=/dev/null bs=1 count=1触发open,或编写简单的C程序调用open()后立即close()来验证。