用户界面

DRM 核心向应用程序导出几个接口,通常旨在通过相应的 libdrm 包装函数使用。此外,驱动程序导出特定于设备的接口,供用户空间驱动程序和设备感知应用程序通过 ioctl 和 sysfs 文件使用。

外部接口包括:内存映射、上下文管理、DMA 操作、AGP 管理、垂直消隐控制、栅栏管理、内存管理和输出管理。

在此处涵盖通用 ioctl 和 sysfs 布局。我们只需要高级信息,因为手册页应涵盖其余部分。

libdrm 设备查找

当心恶龙!注意陷阱!

为了警告其他试图弄清楚这里发生了什么的人,我将尝试总结一下这个故事。首先,让我们理清名称,因为内核内部、libdrm 和 ioctl 的命名都不同

  • 由 drm_getunique 实现的 GET_UNIQUE ioctl 通过 drmGetBusid 函数包装在 libdrm 中。

  • libdrm drmSetBusid 函数由 SET_UNIQUE ioctl 支持。所有这些代码都在内核中通过 drm_invalid_op() 废弃。

  • 内部 set_busid 内核函数和驱动程序回调仅由 SET_VERSION ioctl 使用,因为只有 drm 1.0(已废弃)允许用户空间通过上述 ioctl 设置 busid。

  • 涉及的其他 ioctl 和函数的命名一致。

对于任何想知道 drm 1.1 和 1.4 之间区别的人:正确处理 ppc 上 busid 中的 pci 域。正确执行此操作仅在 2010 年的 libdrm 中实现,因此还不能废弃。没有人知道 drm 1.2 和 1.3 有什么特别之处。

现在讲述 drm 中设备查找的实际恐怖故事。总的来说,有两种不同的方法,一种是通过 busid,另一种是通过设备驱动程序名称。

通过 busid 打开非常简单

  1. 首先调用 SET_VERSION 以确保正确处理 pci 域。作为副作用,这会在主结构中填写唯一名称。

  2. 调用 GET_UNIQUE 以从主结构中读取唯一名称,由于步骤 1,该名称与 busid 匹配。如果不匹配,请继续尝试下一个设备节点。

通过名称打开略有不同

  1. 直接调用 VERSION 以获取版本并与该 ioctl 返回的驱动程序名称匹配。请注意,未调用 SET_VERSION,这意味着刚打开的主节点的唯一名称_未_填写。尽管当前的 drm 设备节点始终绑定到一个设备,并且不能像 drm 1.0 那样在运行时分配。

  2. 匹配驱动程序名称。如果不匹配,请继续下一个设备节点。

  3. 调用 GET_UNIQUE,并检查唯一名称的长度是否为零(通过检查字符串中的第一个字节是否为 0)。如果不是这种情况,libdrm 将跳过并继续下一个设备节点。可能这只是从 drm 1.0 时代复制粘贴过来的,当时设置的唯一名称意味着驱动程序已在使用中,但这只是猜测。

长话短说:为了保持按名称打开逻辑的工作,当尚未调用 SET_VERSION 时,GET_UNIQUE 必须_不_返回唯一字符串,否则 libdrm 会中断。即使该唯一字符串永远无法更改,并且对于实际打开设备完全无关紧要,因为运行时可分配设备实例仅在 drm 1.0 中支持,而 drm 1.0 早已消亡。但是 drmOpenByName 中的 libdrm 代码以某种方式幸存下来,因此不能破坏它。

主节点、DRM Master 和身份验证

struct drm_master 用于跟踪具有打开的主设备节点的客户端组。对于每个至少一次成功成为设备主设备的 struct drm_file(通过 SET_MASTER IOCTL 或在当前没有其他主设备时隐式通过打开主设备节点),都存在一个 struct drm_master。这在 drm_file.is_master 中注明。所有其他客户端只有一个指向与其关联的 drm_master 的指针。

此外,只有一个 drm_master 可以是 drm_device 的当前主设备。可以通过 DROP_MASTER 和 SET_MASTER IOCTL 或隐式通过关闭/打开主设备节点来切换它。另请参见 drm_is_current_master()

客户端可以使用 GETMAGIC 和 AUTHMAGIC IOCTL 对当前主设备(如果它匹配它们自己的)进行身份验证。与交换主设备一起,这允许对整个相互信任的客户端组进行受控访问设备。

bool drm_is_current_master(struct drm_file *fpriv)

检查 priv 是否为当前主设备

参数

struct drm_file *fpriv

DRM 文件私有数据

描述

检查 fpriv 是否为其设备上的当前主设备。这决定了是否允许客户端运行 DRM_MASTER IOCTL。

大多数需要 DRM_MASTER 的现代 IOCTL 用于内核模式设置 - 当前主设备被假定为拥有不可共享的显示硬件。

struct drm_master *drm_master_get(struct drm_master *master)

引用主设备指针

参数

struct drm_master *master

struct drm_master

描述

递增 master 的引用计数并返回指向 master 的指针。

struct drm_master *drm_file_get_master(struct drm_file *file_priv)

引用 file_privdrm_file.master

参数

struct drm_file *file_priv

DRM 文件私有数据

描述

递增 file_privdrm_file.master 的引用计数并返回 drm_file.master。如果 file_priv 没有 drm_file.master,则返回 NULL。

从此函数返回的主设备指针应使用 drm_master_put() 取消引用。

void drm_master_put(struct drm_master **master)

取消引用并清除主设备指针

参数

struct drm_master **master

指向 struct drm_master 指针的指针

描述

这会递减 master 后面的 drm_master 并将其设置为 NULL。

struct drm_master

drm 主设备结构

定义:

struct drm_master {
    struct kref refcount;
    struct drm_device *dev;
    char *unique;
    int unique_len;
    struct idr magic_map;
    void *driver_priv;
    struct drm_master *lessor;
    int lessee_id;
    struct list_head lessee_list;
    struct list_head lessees;
    struct idr leases;
    struct idr lessee_idr;
};

成员

refcount

此主设备对象的引用计数。

dev

链接回 DRM 设备

unique

唯一标识符:例如 busid。受 drm_device.master_mutex 保护。

unique_len

唯一字段的长度。受 drm_device.master_mutex 保护。

magic_map

已使用的身份验证令牌的映射。受 drm_device.master_mutex 保护。

driver_priv

指向驱动程序私有信息的指针。

lessor

租赁授予者,仅当此 struct drm_master 表示持有来自 lessor 的对象租赁的承租人时才设置。设备的完整所有者将其设置为 NULL。

一旦在 drm_lease_create() 中设置了出租人,出租人就不会更改,每个承租人都会持有对其出租人的引用,并在 drm_lease_destroy() 中销毁时释放该引用。

另请参见 关于显示资源租赁的部分

lessee_id

承租人的 ID。所有者(即 lessor 为 NULL)始终具有 ID 0。受 drm_device.mode_configdrm_mode_config.idr_mutex 保护。

lessee_list

lessor 的承租人的列表条目,他们在其中链接到 lessees。不适用于所有者。受 drm_device.mode_configdrm_mode_config.idr_mutex 保护。

lessees

从此租赁的 drm_masters 列表。受 drm_device.mode_configdrm_mode_config.idr_mutex 保护。

如果没有授予租赁,或者所有承租人都已被销毁,则此列表为空。由于出租人被其所有承租人引用,因此除非列表为空,否则无法销毁此主设备。

leases

租赁给此 drm_master 的对象。受 drm_device.mode_configdrm_mode_config.idr_mutex 保护。

对象在 drm_lease_create() 中一起租赁,并在撤销租赁时一起删除。

lessee_idr

此所有者下的所有承租人(仅在 lessor 为 NULL 时使用)。受 drm_device.mode_configdrm_mode_config.idr_mutex 保护。

描述

请注意,主设备结构仅与旧版/主设备节点相关,因此每个设备只能有一个,而不是每个 drm_minor 有一个。

DRM 显示资源租赁

DRM 租赁提供有关 DRM 主设备是否可以控制 DRM 模式设置对象的信息。这使得可以创建管理显示资源子集的多个 DRM 主设备。

设备的原始 DRM 主设备“拥有”可用的 drm 资源。它可以创建额外的 DRM 主设备并“租赁”它控制的资源给新的 DRM 主设备。这使新的 DRM 主设备可以控制租赁的资源,直到所有者撤销租赁或关闭新的 DRM 主设备。一些有用的术语

  • “所有者”是一个 struct drm_master,它不从另一个 struct drm_master 租赁对象,因此“拥有”对象。所有者可以识别为 struct drm_master,对于该 drm_master.lessor 为 NULL。

  • “出租人”是一个 struct drm_master,它将对象租赁给一个或多个其他 struct drm_master。目前,不允许承租人创建子租赁,因此出租人与所有者相同。

  • “承租人”是一个 struct drm_master,它从其他 struct drm_master 租赁对象。每个承租人仅从 drm_master.lessor 中记录的单个出租人租赁资源,并在 drm_master.leases 中保存其租赁的对象集。

  • “租赁”是出租人和承租人之间的合同,用于标识承租人可以控制哪些资源。租赁的所有资源必须由出租人拥有或租赁给出租人,并且不允许出租人将同一对象租赁给多个承租人。

任何 struct drm_master “控制”的对象集仅限于其租赁的对象集(对于承租人)或所有对象(对于所有者)。

不受 struct drm_master 控制的对象无法通过各种状态操作 ioctl 修改,并且报告给用户空间的任何状态都将被编辑,使其看起来空闲和/或无法使用。例如,连接器始终报告“已断开连接”,而编码器报告没有可能的 crtc 或克隆。

由于每个承租人都可以从单个出租人租赁对象,因此显示资源租赁形成 struct drm_master 树。由于当前不允许承租人创建子租赁,因此树深度限制为 1。所有这些都在顶级设备所有者通过 SETMASTER 或 DROPMASTER IOCTL 更改时同时激活,因此 drm_device.master 指向租赁树顶部的所有者(即 struct drm_master,对于该 drm_master.lessor 为 NULL)。可以通过所有者的 drm_master.lessee_idr 搜索从所有者租赁对象的完整承租人列表。

开源用户空间要求

对于新的 uAPI 需要什么样的用户空间端,DRM 子系统比大多数其他内核子系统具有更严格的要求。本节介绍了这些要求的具体内容以及它们存在的原因。

简短的总结是,添加任何 DRM uAPI 都需要相应的开源用户空间补丁,并且这些补丁必须经过审查并准备好合并到合适的和规范的上游项目中。

GFX 设备(显示和渲染/GPU 侧)是非常复杂的硬件,用户空间和内核必须紧密协作。渲染和模式设置的接口必须非常广泛和灵活,因此几乎不可能为每种可能的极端情况精确定义它们。反过来,这使得实际上几乎不可能区分用户空间要求的行为(必须不更改以避免回归)和仅是当前实现偶然产物的行为。

如果没有访问所有用户空间用户的完整源代码,这意味着不可能更改实现细节,因为用户空间可能会在细节上依赖于当前实现的偶然行为。并且在没有访问源代码的情况下调试此类回归几乎是不可能的。因此,这意味着

  • Linux 内核的“无回归”策略实际上仅适用于 DRM 子系统的开源用户空间。如果用户空间中的闭源 blob 驱动程序使用与开源驱动程序相同的 uAPI,DRM 开发人员完全可以接受,但它们必须以与开源驱动程序完全相同的方式执行此操作。创造性地(滥)用接口将导致破坏,并且在过去经常导致破坏。

  • 任何新的用户空间接口都必须具有一个开源实现作为演示工具。

需要开源用户空间的另一个原因是 uAPI 审查。由于 GFX 堆栈的内核和用户空间部分必须紧密协作,因此代码审查只能通过查看双方来评估新接口是否实现了其目标。确保接口确实完全涵盖了用例导致了一些额外的要求

  • 开源用户空间必须不是玩具/测试应用程序,而是真实的东西。具体来说,它需要处理所有常见的错误和极端情况。这些通常是新的 uAPI 分崩离析的地方,因此对于评估拟议接口的适用性至关重要。

  • 用户空间端必须经过充分审查和测试,以符合该用户空间项目的标准。例如,mesa 这意味着 piglit 测试用例和邮件列表上的审查。这再次是为了确保新接口实际上完成了工作。用户空间侧审查员还应在内核 uAPI 补丁上提供 Acked-by,表明他们认为拟议的 uAPI 是合理的,并且经过充分记录和验证以供用户空间使用。

  • 用户空间补丁必须针对规范上游,而不是某些供应商分支。这是为了确保没有人通过进行快速分支来作弊审查和测试要求。

  • 内核补丁只有在满足上述所有要求后才能合并,但它_必须_在用户空间补丁落地_之前_合并到 drm-next 或 drm-misc-next 中。uAPI 始终从内核流出,以另一种方式做事会冒着 uAPI 定义和头文件分歧的风险。

这些都是相当严格的要求,但多年来,人们对匆忙添加的 uAPI 积累了共同的痛苦和经验,并且几乎总是很快就后悔了。GFX 设备变化非常快,至少每隔几年就需要进行范式转变和全新的 uAPI 接口集。连同 Linux 内核保证保持现有用户空间运行 10 多年的保证,这对 DRM 子系统来说已经相当痛苦,同一事物存在多个不同的 uAPI。如果我们在每年的组合中再添加一些完整的错误,这将完全无法管理。

渲染节点

DRM 核心为用户空间提供了多个字符设备供使用。根据打开的设备,用户空间可以执行不同的操作集(主要是 ioctl)。主节点始终创建并调用 card。此外,还会创建一个当前未使用的控制节点,称为 controlD。主节点提供所有旧版操作,并且历史上是用户空间使用的唯一接口。通过 KMS,引入了控制节点。但是,计划的 KMS 控制接口尚未编写,因此控制节点至今仍未使用。

随着屏幕外渲染器和 GPGPU 应用程序的使用增加,客户端不再需要运行合成器或图形服务器来使用 GPU。但是 DRM API 要求非特权客户端在获得 GPU 访问权限之前向 DRM-Master 进行身份验证。为了避免此步骤并授予客户端 GPU 访问权限而无需进行身份验证,引入了渲染节点。渲染节点仅服务于渲染客户端,也就是说,无法在渲染节点上发出模式设置或特权 ioctl。仅允许非全局渲染命令。如果驱动程序支持渲染节点,则必须通过 DRIVER_RENDER DRM 驱动程序功能来声明它。如果不支持,则必须将主节点用于渲染客户端以及旧版 drmAuth 身份验证过程。

如果驱动程序声明了渲染节点支持,则 DRM 核心将创建一个单独的渲染节点,称为 renderD。每个设备将有一个渲染节点。除 PRIME 相关的 ioctl 外,此节点上将不允许任何 ioctl。特别是,将明确禁止 GEM_OPEN。有关可以在渲染节点上使用的独立于驱动程序的 ioctl 的完整列表,请参见 drm_ioctl.c 中标记为 DRM_RENDER_ALLOW 的 ioctl。渲染节点旨在避免缓冲区泄漏,如果客户端猜测旧版接口上的 flink 名称或 mmap 偏移,则会发生缓冲区泄漏。除了此基本接口外,驱动程序还必须将其依赖于驱动程序的仅渲染 ioctl 标记为 DRM_RENDER_ALLOW,以便渲染客户端可以使用它们。驱动程序作者必须小心不要允许渲染节点上的任何特权 ioctl。

通过渲染节点,用户空间现在可以通过基本文件系统访问模式控制对渲染节点的访问。不再需要运行在特权主节点/旧版节点上对客户端进行身份验证的图形服务器。相反,客户端可以打开渲染节点,并且立即获得 GPU 访问权限。客户端(或服务器)之间的通信通过 PRIME 完成。不支持从渲染节点到旧版节点的 FLINK。新客户端不得使用不安全的 FLINK 接口。

除了删除所有模式设置/全局 ioctl 外,渲染节点还删除了 DRM-Master 概念。没有理由将渲染客户端与 DRM-Master 关联,因为它们独立于任何图形服务器。此外,无论如何,它们都必须在没有任何正在运行的主设备的情况下工作。如果驱动程序支持渲染节点,则必须能够在没有主设备对象的情况下运行。另一方面,如果驱动程序需要客户端之间共享状态,该状态对用户空间可见且可超出打开文件边界访问,则它们不支持渲染节点。

设备热插拔

注意

以下是计划。实现尚未完成(2020 年 5 月)。

图形设备(显示和/或渲染)可以通过 USB(例如,显示适配器或扩展坞)或 Thunderbolt(例如,eGPU)连接。最终用户可以在使用此类设备时热插拔它们,并期望机器至少不会崩溃。热插拔 DRM 设备造成的任何损坏都需要尽可能地限制,并且必须给用户空间处理它的机会(如果用户空间想要这样做)。理想情况下,拔掉 DRM 设备仍然可以让桌面继续运行,但这需要在整个图形堆栈中获得明确的支持:从内核和用户空间驱动程序,到显示服务器,再到窗口系统协议,以及应用程序和库。

其他应该导致相同结果的情况包括:不可恢复的 GPU 崩溃、PCI 设备从总线上消失,或者强制从物理设备上解除绑定驱动程序。

换句话说,从用户空间的角度来看,一切都需要或多或少地保持工作状态,直到用户空间停止使用消失的 DRM 设备并完全关闭它。用户空间将从设备移除的 uevent、返回 ENODEV 的 ioctl(或返回驱动程序特定内容的驱动程序特定 ioctl)或返回 ENXIO 的 open() 中得知设备消失。

只有在用户空间关闭了所有相关的 DRM 设备和 dmabuf 文件描述符并移除了所有 mmaps 之后,DRM 驱动程序才能拆除其不再存在的设备的实例。如果同一物理设备以某种方式在中间恢复,它应该是一个新的 DRM 设备。

与 PID 类似,字符设备次设备号不会立即回收。与之前分配的次设备号相比,新的 DRM 设备总是选择下一个空闲的次设备号,并在次设备号耗尽时回绕。

该目标至少对内核和驱动程序提出了以下要求。

KMS UAPI 的要求

  • KMS 连接器必须将其状态更改为已断开连接。

  • 传统模式设置和页面翻转,以及原子提交,无论是真实的还是 TEST_ONLY,以及任何其他 ioctl,要么失败并返回 ENODEV,要么假装成功。

  • 挂起的非阻塞 KMS 操作会传递用户空间期望的 DRM 事件。这也适用于假装成功的 ioctl。

  • 对底层设备已消失的设备节点执行 open() 将失败并返回 ENXIO。

  • 尝试在消失的 DRM 设备上创建 DRM 租赁将失败并返回 ENODEV。现有的 DRM 租赁仍然存在并按上述方式工作。

渲染和跨设备 UAPI 的要求

  • 所有无法再运行的 GPU 作业都必须强制发出其 fence 信号,以避免在用户空间中造成挂起。相关的错误代码是 ENODEV。

  • 一些用户空间 API 已经定义了设备消失时应该发生的情况(OpenGL、GL ES:GL_KHR_robustnessVulkan:VK_ERROR_DEVICE_LOST;等等)。DRM 驱动程序可以自由地以他们认为最好的方式实现此行为,例如,在驱动程序特定的 ioctl 中返回失败并在用户空间驱动程序中处理这些失败,或者依赖 uevent,等等。

  • 指向已消失内存的 dmabuf 要么无法导入并返回 ENODEV,要么继续成功导入(如果它在消失之前会成功导入)。另请参阅下面关于已导入 dmabuf 的内存映射。

  • 尝试将 dmabuf 导入到消失的设备将失败并返回 ENODEV,或者如果它在没有消失的情况下会成功导入,则导入将成功。

  • 对底层设备已消失的设备节点执行 open() 将失败并返回 ENXIO。

内存映射的要求

内存映射有进一步的要求,这些要求适用于现有映射和设备消失后创建的映射。如果底层内存消失,则创建或修改映射,以便读取和写入仍然可以成功完成,但结果是未定义的。这适用于用户空间 mmap() 的内存和 dmabuf 指向的内存,dmabuf 可能映射到其他设备(跨设备 dmabuf 导入)。

引发 SIGBUS 不是一个选项,因为用户空间无法实际处理它。信号处理程序是全局的,这使得从 Mesa 生成的库等库中正确使用它们极其困难。信号处理程序是不可组合的,你不能为来自不同供应商的 GPU1 和 GPU2 设置不同的处理程序,也不能为 mmapped 常规文件设置第三个处理程序。线程也会给信号处理带来额外的麻烦。

设备重置

GPU 堆栈非常复杂,并且容易出错,从硬件错误、错误的应用程序到许多层之间的所有内容。某些错误需要重置设备才能使设备再次可用。本节描述了 DRM 和用户模式驱动程序在设备重置时的预期行为,以及如何传播重置状态。

如果不污染内核,则无法禁用设备重置,这可能会通过收缩器/mmu_notifiers 挂起整个内核。用户空间在设备重置中的作用是将消息传播到应用程序,并应用任何特殊的策略来阻止有问题的应用程序(如果有)。推论是,调试挂起的 GPU 上下文需要硬件支持,以便能够在 GPU 上下文停止时抢占它。

内核模式驱动程序

KMD 负责检查设备是否需要重置,并根据需要执行重置。通常,当作业执行卡住时,会检测到挂起。

事实证明,将错误传播到用户空间非常棘手,因为它与通常的命令流方向相反。因此, vendor 独立错误处理已添加到 &dma_fence 对象中,这样驱动程序可以在发出 fence 信号之前将错误代码添加到其 fence 中。有关如何执行此操作以及要使用的错误代码示例,请参阅函数 dma_fence_set_error()

DRM 调度程序还允许在重置后重新启动硬件提交时,在所有挂起的 fence 上设置错误代码。错误代码还会从硬件 fence 转发到调度程序 fence,以便将错误传播到堆栈的更高层,最终到达用户空间。

用户空间可以通过通用 SYNC_IOC_FILE_INFO IOCTL 以及通过驱动程序特定的接口查询 Fence 错误。

除了设置 fence 错误之外,驱动程序还应跟踪每个上下文的重置,DRM 调度程序提供了 drm_sched_entity_error() 函数作为此用例的助手。重置后,KMD 应拒绝受影响上下文的新命令提交。

用户模式驱动程序

提交命令后,UMD 应检查提交是否被接受或拒绝。重置后,KMD 应拒绝提交,UMD 可以向 KMD 发出 ioctl 以检查重置状态,如果 UMD 需要,可以更频繁地检查此状态。检测到重置后,UMD 将使用适当的 API 错误代码将其报告给应用程序,如下面关于鲁棒性的部分中所述。

鲁棒性

在重置后尝试保持图形 API 上下文工作的唯一方法是,如果它符合它正在使用的图形 API 的鲁棒性方面。

图形 API 为应用程序提供了处理设备重置的方法。但是,不能保证应用程序会正确使用这些功能,并且不支持鲁棒接口的用户空间(例如非鲁棒 OpenGL 上下文或没有任何鲁棒性支持的 API(如 libva))会将鲁棒性处理完全留给用户空间驱动程序。对于用户空间驱动程序在这种情况下应该做什么,没有强烈的社区共识,因为所有合理的方法都有一些明显的缺点。

OpenGL

使用 OpenGL 的应用程序应使用可用的鲁棒接口,例如扩展 GL_ARB_robustness(或 OpenGL ES 的 GL_EXT_robustness)。此接口会告知是否发生了重置,如果是,则所有上下文状态都被视为丢失,应用程序会通过创建新的上下文来继续。对于不使用鲁棒性的情况下该怎么做,没有共识。

Vulkan

使用 Vulkan 的应用程序应检查提交的 VK_ERROR_DEVICE_LOST。此错误代码表示,除其他外,发生了设备重置,并且需要重新创建上下文才能继续。

报告重置原因

除了通过堆栈传播重置以使应用程序可以恢复之外,对于驱动程序开发人员来说,更多地了解导致重置的原因确实非常有用。为此,驱动程序可以利用 devcoredump 来存储有关重置的相关信息,并发送恢复方法为 none 的设备 wedged 事件(如“Device Wedging”一章中所述)来通知用户空间,以便可以收集此信息并将其添加到用户错误报告中。

设备 Wedging

驱动程序可以选择使用设备 wedged 事件(在 DRM 子系统中实现为 drm_dev_wedged_event()),该事件通过 uevent 通知用户空间 DRM 设备的“wedged”(挂起/不可用)状态。这在设备不再按预期运行并且从驱动程序上下文中无法恢复的情况下尤其有用。此实现的目的是为驱动程序提供一种通用方法,以在用户空间干预的帮助下恢复设备,而无需在驱动程序中采取任何激烈的措施(例如重置或重新枚举完整的总线,底层物理设备位于其上)。

“wedged”设备基本上是指驱动程序在耗尽所有可能的尝试以从驱动程序上下文中恢复设备后声明已死的设备。uevent 是发送到用户空间的通知,其中包含关于可能尝试从用户空间恢复设备并使其恢复到可用状态的提示。不同的驱动程序可能对“wedged”设备有不同的想法,具体取决于底层物理设备的硬件实现,因此事件是供应商不可知的。驱动程序可以自行决定何时需要设备恢复,以及他们希望如何从可用的方法中恢复。

驱动程序先决条件

驱动程序在选择恢复之前,需要通过注意先决条件来确保“wedged”设备不会对整个系统造成损害。必要的措施必须包括禁用 DMA 到系统内存以及与其他设备的任何通信通道。此外,驱动程序必须确保所有 dma_fences 都已发出信号,并且核心内核可能依赖的任何设备状态都已清理干净。所有现有的 mmaps 都应该失效,并且页面错误应该重定向到虚拟页面。发送事件后,设备必须保持“wedged”状态,直到执行恢复。应该拒绝新的设备访问(IOCTL),最好使用类似于设备遇到的故障类型的错误代码。这将表明 wedging 的原因,如果需要,可以将其报告给应用程序。

恢复

当前的实现定义了三种恢复方法,驱动程序可以从中选择任何一种、多种或不选择。选择的方法将按副作用从小到大的顺序在 uevent 环境中以 WEDGED=<method1>[,..,<methodN>] 的形式发送。如果驱动程序不确定恢复或方法未知(例如软/硬系统重启、固件刷新、物理设备更换或任何其他无法立即尝试的过程),则会发送 WEDGED=unknown

用户空间消费者可以解析此事件并根据以下期望尝试恢复。

恢复方法

消费者期望

none

可选的遥测收集

rebind

unbind + bind 驱动程序

bus-reset

unbind + 总线重置/重新枚举 + bind

unknown

消费者策略

唯一的例外是 WEDGED=none,它表示设备在某个时候暂时“wedged”,但已使用设备特定的方法(如重置)从驱动程序上下文中恢复。在这种情况下,不期望消费者进行显式恢复,但它仍然可以采取额外的步骤,例如收集遥测信息(devcoredump、syslog)。这很有用,因为第一次挂起通常是最关键的一次,它可能导致连续的挂起或完全 wedging。

消费者先决条件

消费者有责任确保在尝试恢复之前,任何进程都没有使用该设备或其资源。由于 IOCTL 发生错误,因此应取消映射所有设备内存并关闭文件描述符,以防止泄漏或未定义的行为。这里的想法是在事先清除设备的所有用户上下文,并为干净的恢复做好准备。

示例

Udev 规则

SUBSYSTEM=="drm", ENV{WEDGED}=="rebind", DEVPATH=="*/drm/card[0-9]",
RUN+="/path/to/rebind.sh $env{DEVPATH}"

恢复脚本

#!/bin/sh

DEVPATH=$(readlink -f /sys/$1/device)
DEVICE=$(basename $DEVPATH)
DRIVER=$(readlink -f $DEVPATH/driver)

echo -n $DEVICE > $DRIVER/unbind
echo -n $DEVICE > $DRIVER/bind

自定义

虽然可以使用简单的脚本进行基本恢复,但消费者可以围绕恢复定义自定义策略。例如,如果驱动程序支持多种恢复方法,则消费者可以根据重复违规或供应商特定的故障等情况选择合适的方法。消费者还可以选择使设备可用于调试或遥测收集,并根据调查结果做出恢复决定。当驱动程序不确定恢复或方法未知时,这尤其有用。

设备节点上的 IOCTL 支持

首先,驱动程序私有 IOCTL 应该只需要支持渲染的驱动程序。内核模式设置已全部标准化,并通过属性进行扩展。在一些现有驱动程序中存在一些例外情况,这些驱动程序定义了供显示 DRM master 使用的 IOCTL,但它们都早于属性。

现在,如果你有一个渲染驱动程序,你总是必须通过驱动程序私有属性来支持它。需要几个步骤才能连接所有内容。

首先,你需要在你驱动程序私有的 UAPI 头文件 include/uapi/drm/my_driver_drm.h 中定义你的 IOCTL 的结构。

struct my_driver_operation {
        u32 some_thing;
        u32 another_thing;
};

请确保你遵循 Documentation/process/botching-up-ioctls.rst 中的所有最佳实践。请注意,drm_ioctl() 会自动零扩展结构,因此请确保你可以在末尾添加更多内容,即不要在那里放置可变大小的数组。

然后,你需要使用 DRM_IO()、DRM_IOR()、DRM_IOW() 或 DRM_IOWR() 之一定义你的 IOCTL 编号。它必须以 DRM_IOCTL_ 前缀开头。

##define DRM_IOCTL_MY_DRIVER_OPERATION \
    DRM_IOW(DRM_COMMAND_BASE, struct my_driver_operation)

DRM 驱动程序私有 IOCTL 必须在 DRM_COMMAND_BASE 到 DRM_COMMAND_END 的范围内。最后,你需要一个 struct drm_ioctl_desc 数组来连接处理程序并设置访问权限。

static const struct drm_ioctl_desc my_driver_ioctls[] = {
    DRM_IOCTL_DEF_DRV(MY_DRIVER_OPERATION, my_driver_operation,
            DRM_AUTH|DRM_RENDER_ALLOW),
};

然后将此分配给你的驱动程序结构中的 drm_driver.ioctls 字段。

有关驱动程序特定的 IOCTL 如何连接的详细信息,请参阅关于 文件操作 的单独章节。

测试和验证

用户空间 API 的测试要求

新的跨驱动程序用户空间接口扩展,例如新的 IOCTL、新的 KMS 属性、sysfs 中的新文件或构成 API 更改的任何其他内容,如果可以使用 IGT 为目标硬件合理地进行此类测试,则应在 IGT 中针对该功能进行驱动程序不可知的测试。

使用 IGT 验证更改

有一个测试集合旨在覆盖 DRM 驱动程序的全部功能,并且可以用于检查对 DRM 驱动程序或核心的更改是否会使现有功能退化。此测试套件称为 IGT,其代码和构建及运行说明可以在 https://gitlab.freedesktop.org/drm/igt-gpu-tools/ 中找到。

使用 VKMS 测试 DRM API

VKMS 是一个仅软件的 KMS 驱动程序模型,可用于测试和运行合成器。 VKMS 旨在实现虚拟显示,而无需硬件显示功能。这些特性使 VKMS 成为验证 DRM 核心行为以及支持合成器开发人员的完美工具。 VKMS 使得可以在没有显示的虚拟机中测试 DRM 功能,从而简化了一些核心更改的验证。

要使用 VKMS 验证 DRM API 中的更改,请开始设置内核:确保启用 VKMS 模块;使用启用的 VKMS 编译内核并将其安装在目标机器中。 VKMS 可以在虚拟机(QEMU、virtme 或类似)中运行。建议使用具有至少 1GB RAM 和四个核心的 KVM。

可以通过两种方式在 VM 中运行 IGT 测试

  1. 在 VM 中使用 IGT

  2. 从主机使用 IGT 并将结果写入共享目录。

以下是使用带有与主机共享目录的 VM 来运行 igt-tests 的示例。此示例使用 virtme

$ virtme-run --rwdir /path/for/shared_dir --kdir=path/for/kernel/directory --mods=auto

在访客机器中运行 igt-tests。此示例运行“kms_flip”测试

$ /path/for/igt-gpu-tools/scripts/run-tests.sh -p -s -t "kms_flip.*" -v

在此示例中,Piglit 用于代替构建 igt_runner(-p 选项)。它创建了测试结果的 HTML 摘要,并将其保存在“igt-gpu-tools/results”文件夹中。它仅执行与 -t 选项匹配的 igt-tests。

显示 CRC 支持

DRM 设备驱动程序可以向用户空间提供每个帧的 CRC 信息,因为它到达给定的硬件组件(CRC 采样“源”)。

用户空间可以通过写入 debugfs 中的 dri/0/crtc-N/crc/control 文件来控制给定 CRTC 中 CRC 的生成,其中 N 是 CRTC 的索引。可接受的值是源名称(驱动程序特定的)和“auto”关键字,这将使驱动程序为此 CRTC 选择帧 CRC 的默认源。

一旦启用了帧 CRC 生成,用户空间就可以通过读取 dri/0/crtc-N/crc/data 文件来捕获它们。该文件中的每一行都包含第一个字段中的帧编号,然后是一些包含 CRC 数据的无符号整数字段。字段用单个空格分隔,CRC 字段的数量是源特定的。

请注意,尽管在某些情况下,CRC 是以指定的方式并在用户空间提供的帧内容上计算的 (eDP 1.3),但通常 CRC 计算是以未指定的方式并在已以未指定的方式处理过的帧内容上执行的,因此用户空间无法依赖于为其提交的帧内容生成匹配的 CRC 值。在这种一般情况下,用户空间最多可以做的是比较应该具有相同内容的帧的报告 CRC。

在驱动程序端,实现工作量是最小的,驱动程序只需要实现 drm_crtc_funcs.set_crc_sourcedrm_crtc_funcs.verify_crc_source。如果设置了这些 vfuncs,则会自动设置 debugfs 文件。 CRC 样本需要在驱动程序中通过调用 drm_crtc_add_crc_entry() 来捕获。根据驱动程序和硬件要求,drm_crtc_funcs.set_crc_source 可能会导致提交(甚至是完整的模式设置)。

CRC 结果必须在非完整模式设置原子提交中可靠,因此如果通过 DRM_IOCTL_MODE_ATOMIC 的提交将禁用或以其他方式干扰 CRC 生成,则驱动程序必须将该提交标记为完整模式设置(drm_atomic_crtc_needs_modeset() 应返回 true)。因此,为了确保一致的结果,通用用户空间必须在传统的 SETCRTC 或带有 DRM_MODE_ATOMIC_ALLOW_MODESET 的原子提交后重新设置 CRC 生成。

int drm_crtc_add_crc_entry(struct drm_crtc *crtc, bool has_frame, uint32_t frame, uint32_t *crcs)

添加带有帧 CRC 信息的条目

参数

struct drm_crtc *crtc

帧所属的 CRTC

bool has_frame

此条目是否具有帧编号

uint32_t frame

这些 CRC 相关的帧编号

uint32_t *crcs

CRC 值数组,其长度与 #drm_crtc_crc.values_cnt 匹配

描述

对于每一帧,驱动程序轮询 CRC 源以获取新数据,并调用此函数将它们添加到用户空间从中读取的缓冲区。

Debugfs 支持

DRM_DEBUGFS_GPUVA_INFO

DRM_DEBUGFS_GPUVA_INFO (show, data)

drm_info_list 条目以转储 GPU VA 空间

参数

show

drm_info_list 的 show 回调

data

驱动程序私有数据

描述

驱动程序应使用此宏来定义 drm_info_list 条目,以提供一个 debugfs 文件来转储 GPU VA 空间区域和映射。

对于每个 DRM GPU VA 空间,驱动程序应从其 show 回调中调用 drm_debugfs_gpuva_info()

struct drm_info_list

debugfs 信息列表条目

定义:

struct drm_info_list {
    const char *name;
    int (*show)(struct seq_file*, void*);
    u32 driver_features;
    void *data;
};

成员

name

文件名

show

Show 回调。 seq_file->private 将设置为 struct drm_info_node,对应于给定 struct drm_minor 上此信息的实例。

driver_features

此条目所需的驱动程序功能

data

驱动程序私有数据,不应是设备特定的。

描述

此结构表示要由 DRM 核心创建的 debugfs 文件。

struct drm_info_node

每个次要节点的 debugfs 节点结构

定义:

struct drm_info_node {
    struct drm_minor *minor;
    const struct drm_info_list *info_ent;
};

成员

minor

此节点的 struct drm_minor

info_ent

此节点的模板。

描述

此结构表示 debugfs 文件,作为 struct drm_info_liststruct drm_minor 上的实例化。

FIXME

不,我们为渲染和主节点复制 debugfs 条目并没有太大的意义,但这就是它有机增长的方式。如果需要,应该使用兼容性链接对其进行修复。

struct drm_debugfs_info

debugfs 信息列表条目

定义:

struct drm_debugfs_info {
    const char *name;
    int (*show)(struct seq_file*, void*);
    u32 driver_features;
    void *data;
};

成员

name

文件名

show

Show 回调。 seq_file->private 将设置为 struct drm_debugfs_entry,对应于给定 struct drm_device 上此信息的实例。

driver_features

此条目所需的驱动程序功能。

data

驱动程序私有数据,不应是设备特定的。

描述

此结构表示要由 DRM 核心创建的 debugfs 文件。

struct drm_debugfs_entry

每个设备节点的 debugfs 节点结构

定义:

struct drm_debugfs_entry {
    struct drm_device *dev;
    struct drm_debugfs_info file;
    struct list_head list;
};

成员

dev

此节点的 struct drm_device

file

此节点的模板。

list

所有设备节点的链表。

描述

此结构表示 debugfs 文件,作为 struct drm_debugfs_infostruct drm_device 上的实例化。

int drm_debugfs_gpuva_info(struct seq_file *m, struct drm_gpuvm *gpuvm)

转储给定的 DRM GPU VA 空间

参数

struct seq_file *m

指向要写入的 seq_file 的指针

struct drm_gpuvm *gpuvm

表示 GPU VA 空间的 drm_gpuvm

描述

转储给定 DRM GPU VA 管理器的 GPU VA 映射。

对于每个 DRM GPU VA 空间,驱动程序应从其 drm_info_list 的 show 回调中调用此函数。

返回

成功时为 0,如果未初始化 gpuvm 则为 -ENODEV

void drm_debugfs_create_files(const struct drm_info_list *files, int count, struct dentry *root, struct drm_minor *minor)

初始化 DRM 次要节点的给定 debugfs 文件集

参数

const struct drm_info_list *files

要创建的文件数组

int count

给定的文件数

struct dentry *root

DRI debugfs 目录条目。

struct drm_minor *minor

设备次要编号

描述

在给定的根目录中创建由 struct drm_info_list 数组表示的给定 debugfs 文件集。这些文件将在 drm_debugfs_dev_fini() 上自动删除。

void drm_debugfs_add_file(struct drm_device *dev, const char *name, int (*show)(struct seq_file*, void*), void *data)

将给定的文件添加到 DRM 设备 debugfs 文件列表

参数

struct drm_device *dev

ioctl 的 DRM 设备

const char *name

debugfs 文件名

int (*show)(struct seq_file*, void*)

show 回调

void *data

驱动程序私有数据,不应是设备特定的

描述

将给定的文件条目添加到 DRM 设备 debugfs 文件列表中,以便在 drm_debugfs_init 上创建。

void drm_debugfs_add_files(struct drm_device *dev, const struct drm_debugfs_info *files, int count)

将文件数组添加到 DRM 设备 debugfs 文件列表

参数

struct drm_device *dev

ioctl 的 DRM 设备

const struct drm_debugfs_info *files

要创建的文件数组

int count

给定的文件数

描述

将由 struct drm_debugfs_info 数组表示的给定 debugfs 文件集添加到 DRM 设备 debugfs 文件列表中。

Sysfs 支持

除了所有标准内容之外,DRM 为驱动程序提供的 sysfs 交互支持很少。想要公开其他 sysfs 属性和属性组的驱动程序可以将其附加到 drm_device.devdrm_connector.kdev

在调用 drm_dev_register()drm_connector_register()(对于热插拔连接器)时,会自动处理注册。取消注册也由 drm_dev_unregister()drm_connector_unregister() 自动处理。

void drm_sysfs_hotplug_event(struct drm_device *dev)

生成 DRM uevent

参数

struct drm_device *dev

DRM 设备

描述

dev 指定的 DRM 设备发送 uevent。目前,我们仅在 uevent 环境中设置 HOTPLUG=1,但这可以扩展以处理其他类型的事件。

任何新的 uapi 都应该使用 drm_sysfs_connector_status_event() 来处理连接器状态更改的 uevent。

void drm_sysfs_connector_hotplug_event(struct drm_connector *connector)

为任何连接器更改生成 DRM uevent

参数

struct drm_connector *connector

已更改的连接器

描述

connector 指定的 DRM 连接器发送 uevent。这将发送一个带有属性 HOTPLUG=1 和 CONNECTOR 的 uevent。

void drm_sysfs_connector_property_event(struct drm_connector *connector, struct drm_property *property)

为连接器属性更改生成 DRM uevent

参数

struct drm_connector *connector

属性已更改的连接器

struct drm_property *property

已更改的连接器属性。

描述

为指定的 DRM 连接器和属性发送 uevent。目前,我们设置 HOTPLUG=1 和连接器 ID,以及与更改相关的附加属性 ID。

int drm_class_device_register(struct device *dev)

在 DRM sysfs 类中注册新设备

参数

struct device *dev

要注册的设备

描述

在 DRM sysfs 类中注册一个新的 struct device。本质上仅由 ttm 用于拥有其全局设置的位置。驱动程序不应使用此功能。

void drm_class_device_unregister(struct device *dev)

从 DRM sysfs 类中注销设备

参数

struct device *dev

要注销的设备

描述

从 DRM sysfs 类中注销 struct device。本质上仅由 ttm 用于拥有其全局设置的位置。驱动程序不应使用此功能。

VBlank 事件处理

DRM 核心公开两个垂直消隐相关 ioctl

DRM_IOCTL_WAIT_VBLANK

这采用 struct drm_wait_vblank 结构作为其参数,它用于在发生指定的 vblank 事件时阻止或请求信号。

DRM_IOCTL_MODESET_CTL

这仅用于用户模式设置驱动程序,用于模式设置更改,以允许内核在模式设置后更新 vblank 中断,因为在许多设备上,垂直消隐计数器在模式设置期间的某个时间点重置为 0。现代驱动程序不应再调用此函数,因为使用内核模式设置,它是一个空操作。

用户空间 API 结构

DRM 公开许多 UAPI 和结构定义,以具有与用户的一致且标准化的接口。用户空间可以参考这些结构定义和 UAPI 格式来与驱动程序进行通信。

CRTC 索引

CRTC 既有对象 ID 也有索引,但它们不是同一件事。索引用于需要 CRTC 的密集打包标识符的情况,例如 CRTC 的位掩码。struct drm_mode_get_plane 的成员 possible_crtcs 就是一个例子。

DRM_IOCTL_MODE_GETRESOURCES 用 CRTC ID 数组填充结构,CRTC 索引是其在此数组中的位置。

DRM_CAP_DUMB_BUFFER

DRM_CAP_DUMB_BUFFER

描述

如果设置为 1,则驱动程序支持通过 DRM_IOCTL_MODE_CREATE_DUMB ioctl 创建哑缓冲区。

DRM_CAP_VBLANK_HIGH_CRTC

DRM_CAP_VBLANK_HIGH_CRTC

描述

如果设置为 1,则内核支持在 drm_wait_vblank_request.type 的高位中指定 CRTC 索引

从内核版本 2.6.39 开始,此功能始终设置为 1。

DRM_CAP_DUMB_PREFERRED_DEPTH

DRM_CAP_DUMB_PREFERRED_DEPTH

描述

哑缓冲区的首选位深度。

位深度是用于指示单个像素颜色的位数,不包括任何填充。这与每个像素的位数不同。例如,XRGB8888 的位深度为 24,但每个像素有 32 位。

请注意,此首选项仅适用于哑缓冲区,与其他类型的缓冲区无关。

DRM_CAP_DUMB_PREFER_SHADOW

DRM_CAP_DUMB_PREFER_SHADOW

描述

如果设置为 1,则驱动程序首选用户空间渲染到阴影缓冲区,而不是直接渲染到哑缓冲区。为了获得最佳速度,用户空间应执行流式有序内存副本到哑缓冲区中,并且永远不要从中读取。

请注意,此首选项仅适用于哑缓冲区,与其他类型的缓冲区无关。

DRM_CAP_PRIME

DRM_CAP_PRIME

描述

支持的 PRIME 共享功能的位字段。请参阅 DRM_PRIME_CAP_IMPORTDRM_PRIME_CAP_EXPORT

从内核版本 6.6 开始,DRM_PRIME_CAP_IMPORTDRM_PRIME_CAP_EXPORT 始终会进行通告。

PRIME 缓冲区公开为 dma-buf 文件描述符。请参阅 PRIME 缓冲区共享

DRM_PRIME_CAP_IMPORT

DRM_PRIME_CAP_IMPORT

描述

如果此位在 DRM_CAP_PRIME 中设置,则驱动程序支持通过 DRM_IOCTL_PRIME_FD_TO_HANDLE ioctl 导入 PRIME 缓冲区。

从内核版本 6.6 开始,此位始终在 DRM_CAP_PRIME 中设置。

DRM_PRIME_CAP_EXPORT

DRM_PRIME_CAP_EXPORT

描述

如果此位在 DRM_CAP_PRIME 中设置,则驱动程序支持通过 DRM_IOCTL_PRIME_HANDLE_TO_FD ioctl 导出 PRIME 缓冲区。

从内核版本 6.6 开始,此位始终在 DRM_CAP_PRIME 中设置。

DRM_CAP_TIMESTAMP_MONOTONIC

DRM_CAP_TIMESTAMP_MONOTONIC

描述

如果设置为 0,则内核将在 struct drm_event_vblank 中使用 CLOCK_REALTIME 报告时间戳。如果设置为 1,则内核将使用 CLOCK_MONOTONIC 报告时间戳。有关这些时钟的定义,请参阅 clock_gettime(2)

从内核版本 2.6.39 开始,此功能的默认值为 1。从内核版本 4.15 开始,此功能始终设置为 1。

DRM_CAP_ASYNC_PAGE_FLIP

DRM_CAP_ASYNC_PAGE_FLIP

描述

如果设置为 1,驱动程序支持传统页面翻转的 DRM_MODE_PAGE_FLIP_ASYNC

DRM_CAP_CURSOR_WIDTH

DRM_CAP_CURSOR_WIDTH

描述

CURSOR_WIDTHCURSOR_HEIGHT 功能返回硬件光标的有效宽度 x 高度组合。目的是硬件无关的用户空间可以查询要使用的光标平面大小。

请注意,跨驱动程序的约定仅仅是返回一个有效的大小;驱动程序可以自由地添加其他含义,例如,i915 返回最大平面大小。

DRM_CAP_CURSOR_HEIGHT

DRM_CAP_CURSOR_HEIGHT

描述

参见 DRM_CAP_CURSOR_WIDTH

DRM_CAP_ADDFB2_MODIFIERS

DRM_CAP_ADDFB2_MODIFIERS

描述

如果设置为 1,驱动程序支持在 DRM_IOCTL_MODE_ADDFB2 ioctl 中提供修饰符。

DRM_CAP_PAGE_FLIP_TARGET

DRM_CAP_PAGE_FLIP_TARGET

描述

如果设置为 1,驱动程序支持 drm_mode_crtc_page_flip_target.flagsDRM_MODE_PAGE_FLIP_TARGET_ABSOLUTEDRM_MODE_PAGE_FLIP_TARGET_RELATIVE 标志,用于 DRM_IOCTL_MODE_PAGE_FLIP ioctl。

DRM_CAP_CRTC_IN_VBLANK_EVENT

DRM_CAP_CRTC_IN_VBLANK_EVENT

描述

如果设置为 1,内核支持在 drm_event_vblank.crtc_id 中报告 CRTC ID,用于 DRM_EVENT_VBLANKDRM_EVENT_FLIP_COMPLETE 事件。

从内核版本 4.12 开始,此功能始终设置为 1。

DRM_CAP_SYNCOBJ

DRM_CAP_SYNCOBJ

描述

如果设置为 1,驱动程序支持同步对象。参见 DRM 同步对象

DRM_CAP_SYNCOBJ_TIMELINE

DRM_CAP_SYNCOBJ_TIMELINE

描述

如果设置为 1,驱动程序支持同步对象上的时间线操作。参见 DRM 同步对象

DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP

DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP

描述

如果设置为 1,驱动程序支持原子提交的 DRM_MODE_PAGE_FLIP_ASYNC

DRM_CLIENT_CAP_STEREO_3D

DRM_CLIENT_CAP_STEREO_3D

描述

如果设置为 1,DRM 核心将通过在 struct drm_mode_modeinfo 的标志中公布支持的 3D 布局来公开监视器的立体 3D 功能。参见 DRM_MODE_FLAG_3D_*

从内核版本 3.13 开始,所有驱动程序都始终支持此功能。

DRM_CLIENT_CAP_UNIVERSAL_PLANES

DRM_CLIENT_CAP_UNIVERSAL_PLANES

描述

如果设置为 1,DRM 核心会将所有平面(覆盖、主平面和光标)公开给用户空间。

此功能已在内核版本 3.15 中引入。从内核版本 3.17 开始,所有驱动程序都始终支持此功能。

DRM_CLIENT_CAP_ATOMIC

DRM_CLIENT_CAP_ATOMIC

描述

如果设置为 1,DRM 核心会将原子属性公开给用户空间。这隐式地启用了 DRM_CLIENT_CAP_UNIVERSAL_PLANESDRM_CLIENT_CAP_ASPECT_RATIO

如果驱动程序不支持原子模式设置,启用此功能将失败并显示 -EOPNOTSUPP。

此功能已在内核版本 4.0 中引入。从内核版本 4.2 开始,原子功能的驱动程序始终支持此功能。

DRM_CLIENT_CAP_ASPECT_RATIO

DRM_CLIENT_CAP_ASPECT_RATIO

描述

如果设置为 1,DRM 核心将在模式中提供纵横比信息。参见 DRM_MODE_FLAG_PIC_AR_*

从内核版本 4.18 开始,所有驱动程序都始终支持此功能。

DRM_CLIENT_CAP_WRITEBACK_CONNECTORS

DRM_CLIENT_CAP_WRITEBACK_CONNECTORS

描述

如果设置为 1,DRM 核心将公开特殊连接器,用于将提交中设置的场景写回内存。客户端必须首先启用 DRM_CLIENT_CAP_ATOMIC

从内核版本 4.19 开始,原子功能的驱动程序始终支持此功能。

DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT

DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT

描述

para 虚拟化硬件(例如 vmwgfx、qxl、virtio 和 virtualbox)的驱动程序对光标平面有额外的限制(因此使得这些驱动程序上的光标平面不是真正通用的),例如,它们需要光标平面的行为像鼠标光标一样,并且具有正确设置的热点属性。如果未设置此客户端上限,DRM 核心将隐藏这些虚拟化驱动程序上的光标平面,因为未设置它意味着客户端无法处理这些额外的限制。设置光标热点并将光标平面视为鼠标光标的客户端应设置此属性。客户端必须首先启用 DRM_CLIENT_CAP_ATOMIC

在未特殊处理光标平面的驱动程序(即非虚拟化驱动程序)上设置此属性将返回 EOPNOTSUPP,用户空间可以使用它来衡量其运行的硬件/驱动程序的要求。

从内核版本 6.6 开始,原子功能的虚拟化驱动程序始终支持此功能。

struct drm_syncobj_eventfd

定义:

struct drm_syncobj_eventfd {
    __u32 handle;
    __u32 flags;
    __u64 point;
    __s32 fd;
    __u32 pad;
};

成员

handle

syncobj 句柄。

flags

零表示等待点被发出信号,或者 DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE 表示等待可用于该点的栅栏。

point

syncobj 时间线点(对于二进制 syncobjs 设置为零)。

fd

现有 eventfd 以将事件发送到。

pad

必须为零。

描述

注册一个 eventfd,使其被 syncobj 发出信号。eventfd 计数器将递增 1。

DRM_IOCTL_GEM_CLOSE

DRM_IOCTL_GEM_CLOSE

关闭 GEM 句柄。

描述

内核不会对 GEM 句柄进行引用计数。用户空间负责管理其生命周期。例如,如果用户空间在同一个 DRM 文件描述符上两次导入同一个内存对象,则两次导入都返回同一个 GEM 句柄,用户空间需要确保只执行一次 DRM_IOCTL_GEM_CLOSE。当分配一个内存对象,然后导出并在同一个 DRM 文件描述符上再次导入时,也会发生相同的情况。DRM_IOCTL_MODE_GETFB2 IOCTL 是一个例外,即使在执行 IOCTL 之前已经存在另一个 GEM 句柄引用同一个内存对象,它也总是返回新的 GEM 句柄。

DRM_IOCTL_PRIME_HANDLE_TO_FD

DRM_IOCTL_PRIME_HANDLE_TO_FD

将 GEM 句柄转换为 DMA-BUF FD。

描述

用户空间使用要导出的 GEM 句柄设置 drm_prime_handle.handledrm_prime_handle.flags,并在 drm_prime_handle.fd 中获取 DMA-BUF 文件描述符。

导出可能由于任何驱动程序特定的原因而失败,例如,不支持为此特定 GEM 句柄导出(但可能支持其他 GEM 句柄)。

对 DMA-BUF 导出的支持通过 DRM_PRIME_CAP_EXPORT 公布。

DRM_IOCTL_PRIME_FD_TO_HANDLE

DRM_IOCTL_PRIME_FD_TO_HANDLE

将 DMA-BUF FD 转换为 GEM 句柄。

描述

用户空间使用要导入的 DMA-BUF 文件描述符设置 drm_prime_handle.fd,并在 drm_prime_handle.handle 中获取 GEM 句柄。drm_prime_handle.flags 未使用。

如果现有的 GEM 句柄引用 DMA-BUF 后面的内存对象,则返回该 GEM 句柄。因此,需要处理任意 DMA-BUF 的用户空间必须具有用户空间查找数据结构来手动引用计数重复的 GEM 句柄。有关更多信息,请参见 DRM_IOCTL_GEM_CLOSE

导入可能由于任何驱动程序特定的原因而失败,例如,仅支持为在此 DRM 设备上分配的 DMA-BUF 导入。

对 DMA-BUF 导入的支持通过 DRM_PRIME_CAP_IMPORT 公布。

DRM_IOCTL_MODE_RMFB

DRM_IOCTL_MODE_RMFB

删除帧缓冲。

描述

这将删除先前通过 ADDFB/ADDFB2 添加的帧缓冲。IOCTL 参数是帧缓冲对象 ID。

警告:删除当前在已启用平面上使用的帧缓冲将禁用该平面。链接到该平面的 CRTC 也可能被禁用(取决于驱动程序功能)。

DRM_IOCTL_MODE_CREATE_DUMB

DRM_IOCTL_MODE_CREATE_DUMB

创建一个新的 dumb 缓冲区对象。

描述

KMS dumb 缓冲区提供了一种非常原始的方式来分配适合扫描输出的缓冲区对象,并将其映射以进行软件渲染。KMS dumb 缓冲区不适用于硬件加速渲染或视频解码。KMS dumb 缓冲区不适合在从其分配的 KMS 设备以外的任何其他设备上显示。另请参见 Dumb 缓冲区对象

IOCTL 参数是 struct drm_mode_create_dumb

用户空间应通过此 IOCTL 创建 KMS dumb 缓冲区,然后通过 DRM_IOCTL_MODE_ADDFB 将其添加为 KMS 帧缓冲,并通过 DRM_IOCTL_MODE_MAP_DUMB 映射它。

DRM_CAP_DUMB_BUFFER 指示是否支持此 IOCTL。DRM_CAP_DUMB_PREFERRED_DEPTHDRM_CAP_DUMB_PREFER_SHADOW 指示驱动程序对 dumb 缓冲区的首选项。

DRM_IOCTL_MODE_GETFB2

DRM_IOCTL_MODE_GETFB2

获取帧缓冲元数据。

描述

这会查询有关帧缓冲的元数据。用户空间将 drm_mode_fb_cmd2.fb_id 作为输入填充,内核将结构的其余部分作为输出填充。

如果客户端是 DRM 主设备或具有 CAP_SYS_ADMINdrm_mode_fb_cmd2.handles 将填充 GEM 缓冲区句柄。始终返回新的 GEM 句柄,即使在 DRM 文件描述符上已经存在另一个 GEM 句柄引用同一个内存对象。调用者负责删除新句柄,例如,通过 DRM_IOCTL_GEM_CLOSE IOCTL。如果多个平面使用同一个内存对象,将为多个平面返回相同的新句柄。平面有效,直到一个平面具有零句柄为止——这可用于计算平面的数量。

否则,drm_mode_fb_cmd2.handles 将被置零,并且平面有效,直到一个平面具有零 drm_mode_fb_cmd2.pitches

如果帧缓冲具有格式修饰符,DRM_MODE_FB_MODIFIERS 将在 drm_mode_fb_cmd2.flags 中设置,drm_mode_fb_cmd2.modifier 将包含修饰符。否则,用户空间必须忽略 drm_mode_fb_cmd2.modifier

要获取每个平面的 DMA-BUF FD 而不泄漏 GEM 句柄,用户空间可以通过 DRM_IOCTL_PRIME_HANDLE_TO_FD 导出每个句柄,然后立即通过 DRM_IOCTL_GEM_CLOSE 关闭每个唯一句柄,确保不双重关闭数组中多次指定的句柄。

DRM_IOCTL_MODE_CLOSEFB

DRM_IOCTL_MODE_CLOSEFB

关闭帧缓冲。

描述

这将关闭先前通过 ADDFB/ADDFB2 添加的帧缓冲。IOCTL 参数是帧缓冲对象 ID。

此 IOCTL 类似于 DRM_IOCTL_MODE_RMFB,只是它不会禁用平面和 CRTC。只要平面使用帧缓冲,它就会保持活动状态。当平面不再使用帧缓冲时(因为帧缓冲被另一个帧缓冲替换,或者平面被禁用),帧缓冲将被清理。

这对于实现两个进程之间的无闪烁转换很有用。

根据威胁模型,用户空间可能希望确保帧缓冲不暴露任何敏感的用户信息:附加到平面的已关闭帧缓冲可以被下一个 DRM 主设备读回。

DRM_IOCTL_SET_CLIENT_NAME

DRM_IOCTL_SET_CLIENT_NAME

将名称附加到 drm_file

描述

拥有名称可以更容易地进行跟踪和调试。名称的长度(不包括空终止符)必须 <= DRM_CLIENT_NAME_MAX_LEN。如果名称包含空格或不可打印字符,调用将失败。

struct drm_event

DRM 事件的标头

定义:

struct drm_event {
    __u32 type;
    __u32 length;
};

成员

type

事件类型。

length

有效负载字节总数(包括标头)。

描述

此结构是在 DRM FD 上写回给用户空间的事件的标头。在 DRM FD 上的读取将始终仅返回完整事件:例如,如果读取缓冲区的大小为 100 字节,并且有两个 64 字节的事件挂起,则只会返回一个事件。

事件类型 0 - 0x7fffffff 是通用 DRM 事件,0x80000000 及以上是芯片组特定的。通用 DRM 事件包括 DRM_EVENT_VBLANKDRM_EVENT_FLIP_COMPLETEDRM_EVENT_CRTC_SEQUENCE

DRM_EVENT_VBLANK

DRM_EVENT_VBLANK

垂直消隐事件

描述

此事件是在设置了 _DRM_VBLANK_EVENT 标志的情况下,响应 DRM_IOCTL_WAIT_VBLANK 发送的。

事件有效负载是一个 struct drm_event_vblank。

DRM_EVENT_FLIP_COMPLETE

DRM_EVENT_FLIP_COMPLETE

页面翻转完成事件

描述

此事件是在设置了 DRM_MODE_PAGE_FLIP_EVENT 标志的情况下,响应原子提交或传统页面翻转发送的。

事件有效负载是一个 struct drm_event_vblank。

DRM_EVENT_CRTC_SEQUENCE

DRM_EVENT_CRTC_SEQUENCE

CRTC 序列事件

描述

此事件是在响应 DRM_IOCTL_CRTC_QUEUE_SEQUENCE 发送的。

事件有效负载是一个 struct drm_event_crtc_sequence。

struct drm_mode_modeinfo

显示模式信息。

定义:

struct drm_mode_modeinfo {
    __u32 clock;
    __u16 hdisplay;
    __u16 hsync_start;
    __u16 hsync_end;
    __u16 htotal;
    __u16 hskew;
    __u16 vdisplay;
    __u16 vsync_start;
    __u16 vsync_end;
    __u16 vtotal;
    __u16 vscan;
    __u32 vrefresh;
    __u32 flags;
    __u32 type;
    char name[DRM_DISPLAY_MODE_LEN];
};

成员

clock

像素时钟(以 kHz 为单位)

hdisplay

水平显示大小

hsync_start

水平同步开始

hsync_end

水平同步结束

htotal

水平总大小

hskew

水平倾斜

vdisplay

垂直显示大小

vsync_start

垂直同步开始

vsync_end

垂直同步结束

vtotal

垂直总大小

vscan

垂直扫描

vrefresh

近似垂直刷新率(以 Hz 为单位)

flags

misc. 标志的位掩码,参见 DRM_MODE_FLAG_* 定义

type

类型标志的位掩码,参见 DRM_MODE_TYPE_* 定义

name

描述模式分辨率的字符串

描述

这是用户空间 API 显示模式信息结构。对于内核版本,请参见 struct drm_display_mode

struct drm_mode_get_plane

获取平面元数据。

定义:

struct drm_mode_get_plane {
    __u32 plane_id;
    __u32 crtc_id;
    __u32 fb_id;
    __u32 possible_crtcs;
    __u32 gamma_size;
    __u32 count_format_types;
    __u64 format_type_ptr;
};

成员

plane_id

应检索其信息的平面的对象 ID。由调用者设置。

crtc_id

当前 CRTC 的对象 ID。

fb_id

当前 fb 的对象 ID。

possible_crtcs

与该平面兼容的 CRTC 的位掩码。CRTC 是创建的,它们接收一个索引,该索引对应于它们在位掩码中的位置。位 N 对应于 CRTC 索引 N。

gamma_size

从未使用过。

count_format_types

格式数。

format_type_ptr

指向平面支持的格式的 __u32 数组的指针。这些格式不需要修饰符。

描述

用户空间可以执行 GETPLANE ioctl 来检索有关平面的信息。

要检索支持的格式数,请将 count_format_types 设置为零并调用 ioctl。count_format_types 将使用该值进行更新。

要检索这些格式,请分配一个数组,其中包含存储 count_format_types 格式所需的内存。将 format_type_ptr 指向此数组,然后再次调用 ioctl(其中 count_format_types 仍设置为在第一次 ioctl 调用中返回的值)。

struct drm_mode_get_connector

获取连接器元数据。

定义:

struct drm_mode_get_connector {
    __u64 encoders_ptr;
    __u64 modes_ptr;
    __u64 props_ptr;
    __u64 prop_values_ptr;
    __u32 count_modes;
    __u32 count_props;
    __u32 count_encoders;
    __u32 encoder_id;
    __u32 connector_id;
    __u32 connector_type;
    __u32 connector_type_id;
    __u32 connection;
    __u32 mm_width;
    __u32 mm_height;
    __u32 subpixel;
    __u32 pad;
};

成员

encoders_ptr

指向 __u32 对象 ID 数组的指针。

modes_ptr

指向 struct drm_mode_modeinfo 数组的指针。

props_ptr

指向 __u32 属性 ID 数组的指针。

prop_values_ptr

指向 __u64 属性值数组的指针。

count_modes

模式数。

count_props

属性数。

count_encoders

编码器数。

encoder_id

当前编码器的对象 ID。

connector_id

连接器的对象 ID。

connector_type

连接器的类型。

参见 DRM_MODE_CONNECTOR_* 定义。

connector_type_id

类型特定的连接器编号。

这不是对象 ID。这是一个每个类型的连接器编号。每个(类型,type_id)组合在 DRM 设备的所有连接器中都是唯一的。

(类型,type_id)组合不是一个稳定的标识符:type_id 可能会根据驱动程序探测顺序而更改。

connection

连接器的状态。

参见 enum drm_connector_status

mm_width

连接的接收器的宽度(以毫米为单位)。

mm_height

连接的接收器的高度(以毫米为单位)。

subpixel

连接的接收器的子像素顺序。

参见 enum subpixel_order。

pad

填充,必须为零。

描述

用户空间可以执行 GETCONNECTOR ioctl 来检索有关连接器的信息。用户空间应通过至少执行两次此 ioctl 来检索编码器、模式和属性:第一次检索元素数量,第二次检索元素本身。

要检索元素数量,请将 count_propscount_encoders 设置为零,将 count_modes 设置为 1,并将 modes_ptr 设置为临时的 struct drm_mode_modeinfo 元素。

要检索元素,请为 encoders_ptrmodes_ptrprops_ptrprop_values_ptr 分配数组,然后将 count_modescount_propscount_encoders 设置为其容量。

仅执行两次 ioctl 可能会出现竞争情况:元素数量可能已在两个 ioctl 之间因热插拔事件而更改。用户空间应重试上次 ioctl,直到元素数量稳定为止。内核不会填充任何不具有预期长度的数组。

强制探测连接器

如果 count_modes 字段设置为零,并且 DRM 客户端是当前的 DRM 主设备,则内核将对连接器执行强制探测以刷新连接器状态、模式和 EDID。强制探测可能很慢,可能会导致闪烁,并且 ioctl 将阻塞。

用户空间需要强制探测连接器,以确保它们的元数据在启动时和接收到热插拔事件后是最新的。用户空间可能会在用户明确请求时执行强制探测。在其他情况下,用户空间不应执行强制探测。

struct drm_mode_property_enum

枚举/位域条目的描述。

定义:

struct drm_mode_property_enum {
    __u64 value;
    char name[DRM_PROP_NAME_LEN];
};

成员

value

此枚举条目的数值。

name

name

描述

有关详细信息,请参见 struct drm_property_enum

struct drm_mode_get_property

获取属性元数据。

定义:

struct drm_mode_get_property {
    __u64 values_ptr;
    __u64 enum_blob_ptr;
    __u32 prop_id;
    __u32 flags;
    char name[DRM_PROP_NAME_LEN];
    __u32 count_values;
    __u32 count_enum_blobs;
};

成员

values_ptr

指向 __u64 数组的指针。

enum_blob_ptr

指向 struct drm_mode_property_enum 数组的指针。

prop_id

应检索的属性的对象 ID。由调用者设置。

flags

DRM_MODE_PROP_* 位域。有关标志的定义,请参见 drm_property.flags

name

符号属性名称。用户空间应使用此字段来识别属性。

count_values

values_ptr 中的元素数。

count_enum_blobs

enum_blob_ptr 中的元素数。

描述

用户空间可以执行 GETPROPERTY ioctl 来检索有关属性的信息。同一个属性可以附加到多个对象,请参见“模式设置基本对象抽象”。

values_ptr 字段的含义根据属性类型而变化。有关更多详细信息,请参见 drm_property.flags

仅当属性的类型为 DRM_MODE_PROP_ENUMDRM_MODE_PROP_BITMASK 时,enum_blob_ptrcount_enum_blobs 字段才有意义。为了向后兼容,当属性的类型为 DRM_MODE_PROP_BLOB 时,内核将始终将 count_enum_blobs 设置为零。如果属性具有不同的类型,则用户空间必须忽略这两个字段。

用户空间应通过至少执行两次此 ioctl 来检索值和枚举:第一次检索元素数量,第二次检索元素本身。

要检索元素数量,请将 count_valuescount_enum_blobs 设置为零,然后调用 ioctl。count_values 将使用元素数量进行更新。如果属性的类型为 DRM_MODE_PROP_ENUMDRM_MODE_PROP_BITMASK,则 count_enum_blobs 也将更新。

要检索元素本身,请为 values_ptr 分配一个数组,并将 count_values 设置为其容量。如果属性的类型为 DRM_MODE_PROP_ENUMDRM_MODE_PROP_BITMASK,则为 enum_blob_ptr 分配一个数组,并将 count_enum_blobs 设置为其容量。再次调用 ioctl 将填充数组。

struct drm_mode_fb_cmd2

帧缓冲元数据。

定义:

struct drm_mode_fb_cmd2 {
    __u32 fb_id;
    __u32 width;
    __u32 height;
    __u32 pixel_format;
    __u32 flags;
    __u32 handles[4];
    __u32 pitches[4];
    __u32 offsets[4];
    __u64 modifier[4];
};

成员

fb_id

帧缓冲的对象 ID。

width

帧缓冲的宽度。

height

帧缓冲的高度。

pixel_format

FourCC 格式代码,请参见 drm_fourcc.h 中的 DRM_FORMAT_* 常量。

flags

帧缓冲标志(请参见 DRM_MODE_FB_INTERLACEDDRM_MODE_FB_MODIFIERS)。

handles

GEM 缓冲区句柄,每个平面一个。如果未使用该平面,则设置为 0。同一个句柄可以用于多个平面。

pitches

步幅(也称为 stride),以字节为单位,每个平面一个。

offsets

到缓冲区中的偏移量,以字节为单位,每个平面一个。

modifier

格式修饰符,每个平面一个。请参见 drm_fourcc.h 中的 DRM_FORMAT_MOD_* 常量。所有平面必须使用相同的修饰符。除非在 flags 中设置了 DRM_MODE_FB_MODIFIERS,否则将被忽略。

描述

此结构保存帧缓冲元数据。有两种使用它的方法

  • 用户空间可以填充此结构并执行 DRM_IOCTL_MODE_ADDFB2 ioctl 以注册新的帧缓冲。新的帧缓冲对象 ID 将由内核在 fb_id 中设置。

  • 用户空间可以设置 fb_id 并执行 DRM_IOCTL_MODE_GETFB2 ioctl 以获取有关现有帧缓冲的元数据。

在平面格式的情况下,此结构最多允许 4 个缓冲区对象,每个平面都有偏移量和步幅。步幅和偏移量顺序由 drm_fourcc.h 定义的格式 FourCC 指定,例如,NV12 被描述为

YUV 4:2:0 图像,具有一个 8 位 Y 样本的平面,后跟一个包含 8 位 2x2 子采样的色差样本的交错 U/V 平面。

因此,它将由 offsets[0] 处的 Y 平面和 offsets[1] 处的 UV 平面组成。

为了适应平铺、压缩等格式,可以指定修饰符。 更多信息请参见“格式修饰符”部分。 请注意,即使看起来我们每个平面都有一个修饰符,但实际上并非如此。 每个平面的修饰符必须相同。 因此,多平面格式的不同数据布局的所有组合都必须枚举为单独的修饰符。

当未使用时,handlespitchesoffsetsmodifier 中的所有条目必须为零。 警告:对于 offsetsmodifier,零不能用于判断条目是否被使用,因为它是一个有效值(零偏移很常见,零修饰符是 DRM_FORMAT_MOD_LINEAR)。

struct drm_plane_size_hint

平面大小提示

定义:

struct drm_plane_size_hint {
    __u16 width;
    __u16 height;
};

成员

width

平面像素宽度

height

平面像素高度

描述

平面 SIZE_HINTS 属性 blob 包含 struct drm_plane_size_hint 的数组。

struct hdr_metadata_infoframe

HDR 元数据信息帧数据。

定义:

struct hdr_metadata_infoframe {
    __u8 eotf;
    __u8 metadata_type;
    struct {
        __u16 x, y;
    } display_primaries[3];
    struct {
        __u16 x, y;
    } white_point;
    __u16 max_display_mastering_luminance;
    __u16 min_display_mastering_luminance;
    __u16 max_cll;
    __u16 max_fall;
};

成员

eotf

流中使用的光电转换函数 (EOTF)。

metadata_type

Static_Metadata_Descriptor_ID。

display_primaries

数据的色度坐标。 这些值编码为无符号 16 位值,单位为 0.00002,其中 0x0000 表示零,0xC350 表示 1.0000。 display_primaries.x:色度坐标的 X 坐标。 display_primaries.y:色度坐标的 Y 坐标。

white_point

色彩空间数据的白点。 这些值编码为无符号 16 位值,单位为 0.00002,其中 0x0000 表示零,0xC350 表示 1.0000。 white_point.x:色度坐标白点的 X 坐标。 white_point.y:色度坐标白点的 Y 坐标。

max_display_mastering_luminance

Max Mastering Display Luminance。 此值编码为无符号 16 位值,单位为 1 cd/m2,其中 0x0001 表示 1 cd/m2,0xFFFF 表示 65535 cd/m2。

min_display_mastering_luminance

Min Mastering Display Luminance。 此值编码为无符号 16 位值,单位为 0.0001 cd/m2,其中 0x0001 表示 0.0001 cd/m2,0xFFFF 表示 6.5535 cd/m2。

max_cll

Max Content Light Level。 此值编码为无符号 16 位值,单位为 1 cd/m2,其中 0x0001 表示 1 cd/m2,0xFFFF 表示 65535 cd/m2。

max_fall

Max Frame Average Light Level。 此值编码为无符号 16 位值,单位为 1 cd/m2,其中 0x0001 表示 1 cd/m2,0xFFFF 表示 65535 cd/m2。

描述

根据 CTA 861.G 规范的 HDR 元数据信息帧。 这预计与规范完全匹配。

用户空间应按照此结构中描述的格式传递元数据信息。

struct hdr_output_metadata

HDR 输出元数据

定义:

struct hdr_output_metadata {
    __u32 metadata_type;
    union {
        struct hdr_metadata_infoframe hdmi_metadata_type1;
    };
};

成员

metadata_type

Static_Metadata_Descriptor_ID。

{unnamed_union}

anonymous

hdmi_metadata_type1

HDR 元数据信息帧。

描述

要从用户空间传递的元数据信息

DRM_MODE_PAGE_FLIP_EVENT

DRM_MODE_PAGE_FLIP_EVENT

描述

请求内核在页面翻转完成后发回一个带有 DRM_EVENT_FLIP_COMPLETE 类型的 vblank 事件(请参阅 struct drm_event_vblank)。

DRM_MODE_PAGE_FLIP_ASYNC

DRM_MODE_PAGE_FLIP_ASYNC

描述

请求尽快执行页面翻转,即没有因等待 vblank 而造成的延迟。 这可能会导致屏幕上出现撕裂现象。

与 atomic uAPI 一起使用时,如果硬件不支持为此更新执行异步页面翻转,驱动程序将返回错误。 用户空间应处理此问题,例如,通过回退到常规页面翻转。

注意,某些硬件可能需要在切换到异步页面翻转之前执行最后一次同步页面翻转。 作为例外,即使第一次页面翻转不是异步的,驱动程序也会返回成功。

DRM_MODE_PAGE_FLIP_FLAGS

DRM_MODE_PAGE_FLIP_FLAGS

描述

适用于 drm_mode_crtc_page_flip_target.flags 的标志位掩码。

struct drm_mode_create_dumb

创建用于扫描输出的 KMS dumb 缓冲区。

定义:

struct drm_mode_create_dumb {
    __u32 height;
    __u32 width;
    __u32 bpp;
    __u32 flags;
    __u32 handle;
    __u32 pitch;
    __u64 size;
};

成员

height

缓冲区高度,以像素为单位

width

缓冲区宽度,以像素为单位

bpp

每个像素的位数

flags

必须为零

handle

缓冲区对象句柄

pitch

两个连续行之间的字节数

size

整个缓冲区的大小,以字节为单位

描述

用户空间填充 heightwidthbppflags。 如果 IOCTL 成功,则内核填充 handlepitchsize

DRM_MODE_ATOMIC_TEST_ONLY

DRM_MODE_ATOMIC_TEST_ONLY

描述

不应用原子提交,而是检查硬件是否支持此配置。

有关仅测试提交的更多详细信息,请参见 drm_mode_config_funcs.atomic_check

DRM_MODE_ATOMIC_NONBLOCK

DRM_MODE_ATOMIC_NONBLOCK

描述

应用原子提交时不阻塞。DRM_IOCTL_MODE_ATOMIC IOCTL 立即返回,而不是等待硬件应用更改。 请注意,驱动程序仍将检查是否可以在返回之前应用更新。

DRM_MODE_ATOMIC_ALLOW_MODESET

DRM_MODE_ATOMIC_ALLOW_MODESET

描述

允许更新在应用时导致临时或瞬时可见伪影。 应用更新也可能比页面翻转花费更多的时间。 所有视觉伪影将在更新完成时消失,并通过 vblank 事件的时间戳发出信号(请参见 struct drm_event_vblank)。

当 KMS 更新可能导致可见伪影时,必须设置此标志。 如果没有此标志,此类 KMS 更新将返回 EINVAL 错误。 什么类型的更新可能导致可见伪影取决于驱动程序和硬件。 需要预先知道更新是否可能导致可见伪影的用户空间可以使用 DRM_MODE_ATOMIC_TEST_ONLY 而不使用 DRM_MODE_ATOMIC_ALLOW_MODESET 来查看它是否失败。

就驱动程序所知,如果不设置此标志,则保证不会出现视觉伪影。 某些接收器可能会显示驱动程序无法控制的视觉伪影。

DRM_MODE_ATOMIC_FLAGS

DRM_MODE_ATOMIC_FLAGS

描述

DRM_IOCTL_MODE_ATOMIC IOCTL 在 drm_mode_atomic.flags 中接受的标志位域。

struct drm_mode_create_blob

创建新的 blob 属性

定义:

struct drm_mode_create_blob {
    __u64 data;
    __u32 length;
    __u32 blob_id;
};

成员

data

指向要复制的数据的指针。

length

要复制的数据的长度。

blob_id

返回:新的属性 ID。

描述

创建一个新的“blob”数据属性,从数据指针复制 length 字节,并返回新的 blob ID。

struct drm_mode_destroy_blob

销毁用户 blob

定义:

struct drm_mode_destroy_blob {
    __u32 blob_id;
};

成员

blob_id

要销毁的 blob_id

描述

销毁用户创建的 blob 属性。

用户空间可以尽快释放 blob,因为它们不需要通过其 blob 对象 ID 来引用它们。 例如,如果您在原子提交中使用 MODE_ID blob,并且您不会进行另一个重用相同 ID 的提交,则可以在发出提交后立即销毁 blob,而无需等待其完成。

struct drm_mode_create_lease

创建租约

定义:

struct drm_mode_create_lease {
    __u64 object_ids;
    __u32 object_count;
    __u32 flags;
    __u32 lessee_id;
    __u32 fd;
};

成员

object_ids

指向对象 ID 数组的指针 (__u32)

object_count

对象 ID 的数量

flags

新 FD 的标志 (O_CLOEXEC 等)

lessee_id

返回:承租人的唯一标识符。

fd

返回:指向新 drm_master 文件的文件描述符

描述

租赁模式资源,创建另一个 drm_master。

如果启用了 DRM_CLIENT_CAP_UNIVERSAL_PLANESobject_ids 数组必须至少引用一个 CRTC、一个连接器和一个平面。 或者,租赁可以完全为空。

struct drm_mode_list_lessees

列出租户

定义:

struct drm_mode_list_lessees {
    __u32 count_lessees;
    __u32 pad;
    __u64 lessees_ptr;
};

成员

count_lessees

承租人数量。

在输入时,提供数组的长度。 在输出时,提供总数。 不会写回超过输入数字的数字,因此可以使用两个调用来获取大小,然后获取数据。

pad

填充。

lessees_ptr

指向承租人的指针。

指向承租人 ID 的 __u64 数组的指针

描述

从 drm_master 列出较少的资源。

struct drm_mode_get_lease

获取租赁

定义:

struct drm_mode_get_lease {
    __u32 count_objects;
    __u32 pad;
    __u64 objects_ptr;
};

成员

count_objects

租赁对象的数量。

在输入时,提供数组的长度。 在输出时,提供总数。 不会写回超过输入数字的数字,因此可以使用两个调用来获取大小,然后获取数据。

pad

填充。

objects_ptr

指向对象的指针。

指向对象 ID 的 __u32 数组的指针。

描述

获取租赁对象。

struct drm_mode_revoke_lease

撤销租赁

定义:

struct drm_mode_revoke_lease {
    __u32 lessee_id;
};

成员

lessee_id

承租人的唯一 ID

struct drm_mode_rect

二维矩形。

定义:

struct drm_mode_rect {
    __s32 x1;
    __s32 y1;
    __s32 x2;
    __s32 y2;
};

成员

x1

水平起始坐标(含)。

y1

垂直起始坐标(含)。

x2

水平结束坐标(不含)。

y2

垂直结束坐标(不含)。

描述

drm 子系统使用 struct drm_rect 来管理矩形区域,从而将其导出到用户空间。

当前由 drm_mode_atomic blob 属性 FB_DAMAGE_CLIPS 使用。

struct drm_mode_closefb

定义:

struct drm_mode_closefb {
    __u32 fb_id;
    __u32 pad;
};

成员

fb_id

帧缓冲区 ID。

pad

必须为零。

dma-buf 互操作性

请参见 交换像素缓冲区,了解有关 dma-buf 如何在 DRM 中集成和公开的信息。