DRM KMS 子系统(5)Device/demo

文章目录

  • [1. drm_device概述](#1. drm_device概述)
  • [1.1 核心功能与结构](#1.1 核心功能与结构)
  • [1.2 设备生命周期与用户空间交互](#1.2 设备生命周期与用户空间交互)
  • [1.3 与 tinydrm 的关系](#1.3 与 tinydrm 的关系)
  • [1.4 struct drm_driver](#1.4 struct drm_driver)
  • [2. single-buffer demo](#2. single-buffer demo)
  • [2.1 main函数](#2.1 main函数)
  • [2.2 modeset_create_fb](#2.2 modeset_create_fb)
  • [2.3 buffer_object 和 modeset_destroy_fb](#2.3 buffer_object 和 modeset_destroy_fb)
  • [3 引用](#3 引用)

1. drm_device概述

drm_device 是 Linux 内核中 DRM(Direct Rendering Manager)子系统的核心数据结构,用于统一描述和管理一个图形显示设备。它充当用户空间应用程序与内核空间 KMS(Kernel Mode Setting)驱动之间的桥梁,负责设备的初始化、注册以及提供用户空间访问接口。

1.1 核心功能与结构

  • 设备标识:drm_device 本质上是一个主设备号为 DRM_MAJOR (226) 的字符设备。每个 drm_device 实例通过次设备号区分不同的功能接口。
  • 三类次设备:一个 drm_device 通常关联三个次设备节点,分别用于不同的访问目的:
    • Primary (主设备):DRM_MINOR_PRIMARY,对应 /dev/dri/cardX,是图形应用(如 X Server、Wayland compositor)进行 2D/3D 渲染和模式设置的主要接口。
    • Control (控制设备):DRM_MINOR_CONTROL,对应 /dev/dri/controlDX,主要用于模式设置(Mode Setting)等管理操作,通常由显示服务器在初始化时独占使用。
    • Render (渲染设备):DRM_MINOR_RENDER,对应 /dev/dri/renderDX,专为无状态的渲染 API(如 OpenGL、Vulkan)提供接口,允许多个应用并发渲染,无需独占设备。
  • 关键成员:其核心结构包含指向对应 drm_driver 的指针、设备私有数据指针、指向三个次设备的指针以及引用计数等。

1.2 设备生命周期与用户空间交互

  • 初始化与注册:通过 drm_dev_init() 函数创建并初始化 drm_device 结构,随后调用 drm_dev_register() 完成注册。注册成功后,内核会在 /dev/dri/ 目录下自动创建上述三个设备节点。
  • 用户接口:用户空间程序通过标准的系统调用(如 open()ioctl()mmap())操作这些设备节点。这些调用最终会由 drm_driver 中定义的回调函数(如 drm_opendrm_ioctl)处理。
  • 调试与状态监控:注册后,内核还会在 /sys/class/drm/ 目录下为每个连接器(connector)创建一系列属性文件,用于监控设备状态,例如:
    • status:显示连接状态(connected/disconnected/unknown)。
    • enabled:指示连接器是否启用。
    • dpms:控制或查询显示器的电源状态(On/Standby/Suspend/Off)。
    • modes:列出显示器支持的分辨率模式。

1.3 与 tinydrm 的关系

drm_device 是 DRM 框架的基础,而 tinydrm 是一个为小型显示控制器(如通过 SPI 接口连接的 LCD 屏幕)设计的辅助库。tinydrm 驱动会内部使用 drm_devicedrm_simple_display_pipe 等结构来实现其功能,但其代码被设计得极为精简,以便于在资源受限的嵌入式设备上使用。

1.4 struct drm_driver

通过 struct drm_driver 实现其功能

c 复制代码
struct drm_driver {
	int (*load) (struct drm_device *, unsigned long flags);
		[...]
	int (*unload) (struct drm_device *);
		[...]
	u32 driver_features;
		[...]
};
  • 调用 drm_dev_alloc接口分配drm_device内存并使用drm_dev_init初始化,然后调用drm_dev_register注册一个DRM设备。
  • load和unload负责实例化和销毁连接到DRM设备的DRM组件。
  • driver_features应该包含DRM_RENDER或DRM_MODE_SET或同时包含两者,具体依赖于DRM设备的features。

2. single-buffer demo

2.1 main函数

主函数如下:

c 复制代码
int main(int argc, char **argv)
{
	int fd;
	drmModeConnector *conn;
	drmModeRes *res;
	uint32_t conn_id;
	uint32_t crtc_id;
	
	fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
	
	res = drmModeGetResources(fd);
	crtc_id = res->crtcs[0];
	conn_id = res->connectors[0];
	conn = drmModeGetConnector(fd, conn_id);
	buf.width = conn->modes[0].hdisplay;
	buf.height = conn->modes[0].vdisplay;
	
	modeset_create_fb(fd, &buf);
	drmModeSetCrtc(fd, crtc_id, buf.fb_id, 
	0, 0, &conn_id, 1, &conn->modes[0]);
	
	getchar();
	
	modeset_destroy_fb(fd, &buf);
	drmModeFreeConnector(conn);
	drmModeFreeResources(res);
	
	close(fd);
	return 0;
}

流程非常简单,open一个显卡设备,获得其句柄fd,利用drmModeGetResources获取其资源指针res,里面包含了crtc_id、conn_id,选第一个connectors的conn_id,利用drmModeGetConnector获取conn指针,里面包含了相关的显示模式信息,选第一个模式modes[0],设置对应的framebuffer所需的width和height参数,通过modeset_create_fb(fd, &buf)设置下下去,将会通过内核的系统接口创建一个对应modes[0]的width和height的像素的framebuffer:

2.2 modeset_create_fb

c 复制代码
static int modeset_create_fb(int fd, struct buffer_object *bo)
{
	struct drm_mode_create_dumb create = {};
	struct drm_mode_map_dumb map = {};
	
	/* create a dumb-buffer, the pixel format is XRGB888 */
	create.width = bo->width;
	create.height = bo->height;
	create.bpp = 32;
	drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create); 			//(1)
	
	/* bind the dumb-buffer to an FB object */
	bo->pitch = create.pitch;
	bo->size = create.size;
	bo->handle = create.handle;
	drmModeAddFB(fd, bo->width, bo->height, 24, 32, bo->pitch,	//(2)
	bo->handle, &bo->fb_id);
	
	/* map the dumb-buffer to userspace */
	map.handle = create.handle;
	drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map);				//(3)
	bo->vaddr = mmap(0, create.size, PROT_READ | PROT_WRITE,	//(4)
	MAP_SHARED, fd, map.offset);
	
	/* initialize the dumb-buffer with white-color */
	memset(bo->vaddr, 0xff, bo->size);							//(5)
	return 0;
}

(1)创建了一个widthheight 4个字节内存(显存),其句柄返回在create.handle

(2)创建FB object并通过create.handle传递来绑定(1)中创建的内存(显存),返回为fb_id

(3)通过DRM_IOCTL_MODE_MAP_DUMB,将create.handle指定的内存(显存)相关数据结构与map.offset相关联

(4)利用map.offset完成create.handle指定的内存的映射,映射到用户空间地址为bo->vaddr

(5)用户可以往bo->vaddr写入任何想要设置的颜色值

2.3 buffer_object 和 modeset_destroy_fb

相关的数据结构struct buffer_object如下:

c 复制代码
struct buffer_object {
	uint32_t width;
	uint32_t height;
	uint32_t pitch;
	uint32_t handle;
	uint32_t size;
	uint8_t *vaddr;
	uint32_t fb_id;
};

pitch为一行像素,一般按照4字节对齐,可能和width不相等。

销毁函数如下:

c 复制代码
static void modeset_destroy_fb(int fd, struct buffer_object *bo)
{
	struct drm_mode_destroy_dumb destroy = {};
	drmModeRmFB(fd, bo->fb_id);
	munmap(bo->vaddr, bo->size);
	destroy.handle = bo->handle;
	drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);
}

3 引用

drm-howto/modeset.c from kernel DRM maintainer David Herrmann

相关推荐
senijusene10 小时前
Linux软件编程:IO编程,标准IO(1)
linux·运维·服务器
忧郁的橙子.10 小时前
02-本地部署Ollama、Python
linux·运维·服务器
醇氧10 小时前
【linux】查看发行版信息
linux·运维·服务器
No8g攻城狮10 小时前
【Linux】Windows11 安装 WSL2 并运行 Ubuntu 22.04 详细操作步骤
linux·运维·ubuntu
XiaoFan01211 小时前
免密批量抓取日志并集中输出
java·linux·服务器
souyuanzhanvip11 小时前
ServerBox v1.0.1316 跨平台 Linux 服务器管理工具
linux·运维·服务器
HalvmånEver12 小时前
Linux:线程互斥
java·linux·运维
番茄灭世神12 小时前
Linux应用编程介绍
linux·嵌入式
wdfk_prog12 小时前
[Linux]学习笔记系列 -- [drivers][mmc][mmc_sdio]
linux·笔记·学习
Forsete13 小时前
LINUX驱动开发#9——定时器
linux·驱动开发·单片机