4. 实现屏幕的填充和清屏功能
任务说明
在帧缓冲驱动中实现两个自定义 ioctl 命令:FBIO_FILL(用指定颜色填充整个屏幕)和 FBIO_CLEAR(清屏,等效于填充黑色)。通过用户态测试程序调用这些命令,验证驱动的基本功能。
环境与依赖
- Linux 内核源码树(本案例基于 5.10+,接口兼容即可)
- 帧缓冲设备框架(
linux/fb.h、linux/uaccess.h) - 测试程序编译需要
linux/fb.h(用户态头文件)
驱动实现
以下代码实现了一个最小帧缓冲驱动,注册 fb_info,在 fb_ioctl 中处理自定义命令。填充/清屏操作直接操作 screen_base 指向的视频内存。
// fb_fill_clear.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fb.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#define FBIO_FILL _IOW('F', 0x21, unsigned int) // 填充颜色值
#define FBIO_CLEAR _IO('F', 0x22) // 清屏(填黑)
static struct fb_info *fb_info;
static void *screen_base;
static unsigned long screen_size;
static int myfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
{
unsigned int color;
switch (cmd) {
case FBIO_FILL:
if (copy_from_user(&color, (void __user *)arg, sizeof(color)))
return -EFAULT;
memset(screen_base, color, screen_size);
break;
case FBIO_CLEAR:
memset(screen_base, 0, screen_size);
break;
default:
return -ENOTTY;
}
return 0;
}
static int myfb_probe(struct platform_device *pdev)
{
// 分配 1MB 视频内存(演示用)
screen_size = SZ_1M;
screen_base = kzalloc(screen_size, GFP_KERNEL);
if (!screen_base)
return -ENOMEM;
fb_info = framebuffer_alloc(0, &pdev->dev);
if (!fb_info) {
kfree(screen_base);
return -ENOMEM;
}
fb_info->screen_base = screen_base;
fb_info->screen_size = screen_size;
fb_info->fbops = &myfb_ops;
fb_info->var.xres = 800;
fb_info->var.yres = 600;
fb_info->var.bits_per_pixel = 32;
fb_info->fix.smem_start = virt_to_phys(screen_base);
fb_info->fix.smem_len = screen_size;
strcpy(fb_info->fix.id, "myfb");
if (register_framebuffer(fb_info) < 0) {
framebuffer_release(fb_info);
kfree(screen_base);
return -EINVAL;
}
return 0;
}
static int myfb_remove(struct platform_device *pdev)
{
unregister_framebuffer(fb_info);
framebuffer_release(fb_info);
kfree(screen_base);
return 0;
}
static struct platform_device *myfb_device;
static struct platform_driver myfb_driver = {
.probe = myfb_probe,
.remove = myfb_remove,
.driver = {
.name = "myfb",
},
};
static struct fb_ops myfb_ops = {
.owner = THIS_MODULE,
.fb_read = fb_sys_read,
.fb_write = fb_sys_write,
.fb_fillrect = sys_fillrect,
.fb_copyarea = sys_copyarea,
.fb_imageblit = sys_imageblit,
.fb_ioctl = myfb_ioctl,
};
static int __init myfb_init(void)
{
myfb_device = platform_device_register_simple("myfb", -1, NULL, 0);
return platform_driver_register(&myfb_driver);
}
static void __exit myfb_exit(void)
{
platform_driver_unregister(&myfb_driver);
platform_device_unregister(myfb_device);
}
module_init(myfb_init);
module_exit(myfb_exit);
MODULE_LICENSE("GPL");
用户态测试程序
// test_fb.c
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/fb.h>
#include <unistd.h>
#define FBIO_FILL _IOW('F', 0x21, unsigned int)
#define FBIO_CLEAR _IO('F', 0x22)
int main(void)
{
int fd = open("/dev/fb0", O_RDWR);
if (fd < 0) { perror("open"); return 1; }
unsigned int red = 0x00FF0000; // 红色(小端:B=0, G=0, R=255)
if (ioctl(fd, FBIO_FILL, &red) < 0) { perror("fill"); return 1; }
printf("Fill red: OK\n");
if (ioctl(fd, FBIO_CLEAR) < 0) { perror("clear"); return 1; }
printf("Clear: OK\n");
close(fd);
return 0;
}
编译与运行
- 编译驱动(内核树内):将驱动源码放入
drivers/video/fbdev/,在 Kconfig 中添加配置项,然后:
make menuconfig # 启用 CONFIG_FB_MYFB
make drivers/video/fbdev/
- 加载模块:
insmod fb_fill_clear.ko
- 编译并运行测试程序:
gcc -o test_fb test_fb.c
./test_fb
验证方法
- 测试程序输出
Fill red: OK和Clear: OK,说明 ioctl 命令被驱动正确接收并执行。 - 在驱动加载后,通过
cat /dev/fb0 | hexdump -C | head可观察填充后视频内存首字节为00 00 FF 00(红色),清屏后变为全00。 - 若系统有真实显示设备连接,填充红色时屏幕应变为红色,清屏后变黑。
常见问题
- ioctl 返回
-ENOTTY:确认命令码与驱动中定义一致,且fb_ioctl回调已正确注册。 - 填充颜色与预期不符:检查
bits_per_pixel和颜色字节序。32bpp 下小端格式为0x00RRGGBB。 - 屏幕无变化:确认
screen_base指向的内存确实映射到显示控制器(真实硬件需额外配置 DMA 地址,本案例为演示分配内存)。