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

相关推荐
物理与数学2 小时前
linux内核常用hook机制
linux·linux内核
智者知已应修善业2 小时前
【输出一个N*N的01矩阵,表示最后的汉字点阵图】2024-10-22
c语言·数据结构·c++·经验分享·笔记·算法·矩阵
周公挚友2 小时前
centos 7.9 防火墙
linux·运维·centos
梁正雄2 小时前
linux服务-麒麟10安装sqlserver
linux·运维·sqlserver
老鱼说AI2 小时前
深入理解计算机系统1.5:抽象的重要性:操作系统与虚拟机
c语言·开发语言·汇编
飞Link2 小时前
cmd、powershell、linux下命令对比
linux·运维·服务器
爱上猫de鱼3 小时前
linux环境docker部署前后端应用
linux·运维·docker
EverydayJoy^v^3 小时前
RH134简单知识点——第5章——调优系统性能
linux·运维·服务器
RisunJan3 小时前
Linux命令-lastlog(显示系统中所有用户的最近一次登录信息)
linux·运维·服务器