2. 实现帧缓冲区创建与销毁
任务目标
在Linux内核DRM子系统中,实现帧缓冲区(drm_framebuffer)的创建与销毁逻辑。创建函数需校验像素格式、尺寸及底层内存对象(drm_gem_object)的有效性,并正确初始化帧缓冲区;销毁函数需完成引用计数释放与资源清理。
核心实现
以下代码实现了一个最小但完整的帧缓冲区创建/销毁闭环,可直接嵌入DRM驱动中。核心逻辑包括:
- 格式与尺寸校验:通过
drm_any_plane_has_format检查格式是否被驱动支持,并拒绝零尺寸。 - 内存对象绑定:从
drm_gem_object_lookup获取GEM对象,验证其实际尺寸不小于请求的帧缓冲大小。 - 生命周期管理:销毁时先移除用户空间句柄,再通过引用计数释放GEM对象。
// framebuffer.c — 帧缓冲区创建与销毁最小实现
#include <drm/drm_device.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem.h>
#include <drm/drm_framebuffer.h>
static int validate_fb_request(struct drm_device *dev,
const struct drm_mode_fb_cmd2 *mode_cmd)
{
uint32_t bpp = drm_format_info_bpp(mode_cmd->pixel_format, 0);
/* 1. 像素格式必须被至少一个plane支持 */
if (!drm_any_plane_has_format(dev, mode_cmd->pixel_format,
mode_cmd->modifier[0]))
return -EINVAL;
/* 2. 尺寸必须非零且不超过硬件限制 */
if (mode_cmd->width == 0 || mode_cmd->height == 0 ||
mode_cmd->width > 16384 || mode_cmd->height > 16384)
return -EINVAL;
/* 3. 单缓冲区场景下,仅允许一个plane描述符 */
if (mode_cmd->num_planes != 1)
return -EINVAL;
/* 4. 像素格式的bpp必须与pitches匹配(简化校验) */
if (mode_cmd->pitches[0] < mode_cmd->width * bpp / 8)
return -EINVAL;
return 0;
}
struct drm_framebuffer *my_fb_create(struct drm_device *dev,
struct drm_file *file_priv,
const struct drm_mode_fb_cmd2 *mode_cmd)
{
struct drm_framebuffer *fb;
struct drm_gem_object *gem_obj;
int ret;
ret = validate_fb_request(dev, mode_cmd);
if (ret)
return ERR_PTR(ret);
/* 从用户空间句柄获取GEM对象 */
gem_obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[0]);
if (!gem_obj)
return ERR_PTR(-ENOENT);
/* 验证GEM对象尺寸足够容纳帧缓冲 */
if (gem_obj->size < mode_cmd->pitches[0] * mode_cmd->height) {
drm_gem_object_put(gem_obj);
return ERR_PTR(-EINVAL);
}
/* 分配帧缓冲区结构体 */
fb = kzalloc(sizeof(*fb), GFP_KERNEL);
if (!fb) {
drm_gem_object_put(gem_obj);
return ERR_PTR(-ENOMEM);
}
/* 初始化核心字段 */
fb->dev = dev;
fb->format = drm_format_info(mode_cmd->pixel_format);
fb->width = mode_cmd->width;
fb->height = mode_cmd->height;
fb->pitches[0] = mode_cmd->pitches[0];
fb->modifier = mode_cmd->modifier[0];
fb->obj[0] = gem_obj;
/* 注册到DRM核心(内部会获取额外引用) */
ret = drm_framebuffer_init(dev, fb, &drm_framebuffer_funcs);
if (ret) {
drm_gem_object_put(gem_obj);
kfree(fb);
return ERR_PTR(ret);
}
return fb;
}
static void my_fb_destroy(struct drm_framebuffer *fb)
{
/* 释放GEM对象引用(drm_framebuffer_init时获取的) */
drm_gem_object_put(fb->obj[0]);
/* 释放帧缓冲区自身 */
kfree(fb);
}
static const struct drm_framebuffer_funcs drm_framebuffer_funcs = {
.destroy = my_fb_destroy,
};
驱动接入与验证
在驱动的drm_driver结构体中注册fb_create回调:
static const struct drm_driver my_driver = {
...
.fb_create = my_fb_create,
};
验证路径:
- 编译驱动:
make drivers/gpu/drm/my_driver.ko,确认无编译错误。 - 加载驱动后,使用
modetest -M my_driver(libdrm工具)创建测试帧缓冲:modetest -M my_driver -s 0:1920x1080。若创建成功,命令返回0且无内核报错。 - 销毁验证:执行
modetest -M my_driver -d释放所有资源,观察dmesg无内存泄漏警告(可配合kmemleak扫描)。 - 异常路径测试:使用非法格式(如
DRM_FORMAT_INVALID)调用创建,应返回EINVAL;使用不存在的GEM句柄应返回ENOENT。
通过标准:正常创建/销毁循环100次无错误,且/sys/kernel/debug/dri/0/framebuffers中无残留节点。