文章目录
- [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)提供接口,允许多个应用并发渲染,无需独占设备。
- Primary (主设备):
- 关键成员:其核心结构包含指向对应
drm_driver的指针、设备私有数据指针、指向三个次设备的指针以及引用计数等。
1.2 设备生命周期与用户空间交互
- 初始化与注册:通过
drm_dev_init()函数创建并初始化drm_device结构,随后调用drm_dev_register()完成注册。注册成功后,内核会在/dev/dri/目录下自动创建上述三个设备节点。 - 用户接口:用户空间程序通过标准的系统调用(如
open()、ioctl()、mmap())操作这些设备节点。这些调用最终会由drm_driver中定义的回调函数(如drm_open、drm_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_device 和 drm_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