用户空间 DTX(剪贴板分离系统)接口

surface_dtx 驱动程序负责正确的剪贴板分离和重新连接处理。为此,它提供 /dev/surface/dtx 设备文件,通过该文件它可以与用户空间守护程序进行接口。然后,该守护程序最终负责确定并采取必要的行动,例如卸载连接到基座的设备、卸载/重新加载图形驱动程序、用户通知等。

此驱动程序中使用了两个基本的通信原则:命令(在文档的其他部分也称为请求)和事件。命令发送到 EC,并且在不同的上下文中可能具有不同的含义。事件由 EC 在某些内部状态更改时发送。命令始终由驱动程序发起,而事件始终由 EC 发起。

命名

  • 剪贴板:Surface Book 的可拆卸上部,容纳屏幕和 CPU。

  • 基座:Surface Book 的下部,剪贴板可以从中分离,可选(取决于型号)容纳独立 GPU (dGPU)。

  • 锁存器:将剪贴板在正常操作中保持连接到基座的机制,并允许在请求时分离。

  • 静默忽略的命令:EC 接受该命令为有效命令并确认(遵循标准通信协议),但 EC 不会对其执行操作,即忽略它。

分离过程

警告:此部分文档基于逆向工程和测试,因此可能包含错误或不完整。

锁存状态

锁存机制具有两个主要状态:打开关闭。在 关闭 状态(默认)下,剪贴板固定到基座,而在 打开 状态下,用户可以移除剪贴板。

此外,锁存器可以被锁定和相应地解锁,这可能会影响分离过程。具体而言,此锁定机制旨在防止位于设备基座中的 dGPU 在使用时热插拔。有关更多详细信息,请参阅以下分离过程的文档。默认情况下,锁存器是解锁的。

分离程序

请注意,分离过程完全由 EC 控制。 surface_dtx 驱动程序仅将事件从 EC 中继到用户空间,并将命令从用户空间中继到 EC,即它不影响此过程。

分离过程从用户按下设备底座上的分离按钮或执行 SDTX_IOCTL_LATCH_REQUEST IOCTL 开始。在此之后

  1. EC 打开分离按钮上的指示灯 LED,发送 分离请求 事件 (SDTX_EVENT_REQUEST),并等待进一步的指令/命令。如果锁存器已解锁,则 LED 将闪烁绿色。如果锁存器已被锁定,则 LED 将常亮红色

  2. 该事件通过 surface_dtx 驱动程序中继到用户空间,在那里适当的用户空间守护程序可以处理它并通过此驱动程序提供的 IOCTL 将指令发送回 EC。

  3. EC 等待来自用户空间的指令并根据指令执行操作。如果 EC 在给定时间内未收到任何指令,它将超时并按如下方式继续

    • 如果锁存器已解锁,EC 将打开锁存器,并且可以将剪贴板与基座分离。这与没有此驱动程序或任何用户空间守护程序的行为完全相同。有关 EC 的后续行为的更多详细信息,请参阅下面的 SDTX_IOCTL_LATCH_CONFIRM 说明。

    • 如果锁存器被锁定,EC 将不会打开锁存器,这意味着无法将剪贴板与基座分离。此外,EC 会发送一个取消事件 (SDTX_EVENT_CANCEL),详细说明取消原因 SDTX_DETACH_TIMEDOUT(有关详细信息,请参阅事件)。

用户空间守护程序对分离请求事件的有效响应包括

  • 执行 SDTX_IOCTL_LATCH_REQUEST。这将立即中止分离过程。此外,EC 将发送一个分离请求事件,类似于用户按下分离按钮以取消上述过程(见下文)。

  • 执行 SDTX_IOCTL_LATCH_CONFIRM。这将导致 EC 打开锁存器,之后用户可以分离剪贴板和基座。

    由于这会更改锁存状态,因此一旦锁存器成功打开,就会发送一个锁存状态事件 (SDTX_EVENT_LATCH_STATUS)。如果 EC 未能打开锁存器,例如由于硬件错误或电池电量不足,则会发送一个锁存器取消事件 (SDTX_EVENT_CANCEL),其中取消原因指示特定故障。

    如果锁存器当前被锁定,则锁存器在打开之前会自动解锁。

  • 执行 SDTX_IOCTL_LATCH_HEARTBEAT。这将重置内部超时。不会执行其他操作,即分离过程既不会完成也不会取消,并且 EC 仍将等待进一步的响应。

  • 执行 SDTX_IOCTL_LATCH_CANCEL。这将中止分离过程,类似于上面描述的 SDTX_IOCTL_LATCH_REQUEST 或下面描述的按钮按下。会发送一个通用请求事件(SDTX_EVENT_REQUEST)以响应此操作。但是,与这些操作相反,如果没有正在进行的分离过程,则此命令不会触发新的分离过程。

  • 不执行任何操作。分离过程最终会如第 3 点所述超时。

有关这些响应的更多详细信息,请参阅 IOCTLs

请务必注意,如果用户在分离操作正在进行时(即在 EC 发送初始分离请求事件(SDTX_EVENT_REQUEST)之后,并且在收到相应的响应结束该过程之前)的任何时间按下分离按钮,则会在 EC 级别取消分离过程,并发送相同的事件。因此,分离请求事件本身并不表示分离过程的开始。

由于硬件故障或剪贴板电池电量不足,EC 可能会进一步取消分离过程。这是通过带有相应取消原因的取消事件 (SDTX_EVENT_CANCEL) 完成的。

用户空间接口文档

错误代码和状态值

错误和状态代码分为不同的类别,可用于确定状态代码是否为错误,如果是,则确定该错误的严重性和类型。当前的类别是

状态/错误类别概述。

名称

简短描述

状态

0x0000

非错误状态代码。

RUNTIME_ERROR

0x1000

非关键运行时错误。

HARDWARE_ERROR

0x2000

关键硬件故障。

未知

0xF000

未知错误代码。

其他类别保留供将来使用。SDTX_CATEGORY() 宏可用于确定任何状态值的类别。SDTX_SUCCESS() 宏可用于检查状态值是否为成功值 (SDTX_CATEGORY_STATUS),或者是否表示失败。

EC 发送的未知状态或错误代码由驱动程序分配到 UNKNOWN 类别,并且将来可能会通过它们自己的代码来实现。

当前使用的错误代码是

错误代码概述。

名称

类别

简短描述

SDTX_DETACH_NOT_FEASIBLE

RUNTIME

0x1001

由于剪贴板电池电量不足,无法分离。

SDTX_DETACH_TIMEDOUT

RUNTIME

0x1002

在锁闩锁定时,分离过程超时。

SDTX_ERR_FAILED_TO_OPEN

硬件

0x2001

无法打开锁闩。

SDTX_ERR_FAILED_TO_REMAIN_OPEN

硬件

0x2002

无法保持锁闩打开。

SDTX_ERR_FAILED_TO_CLOSE

硬件

0x2003

无法关闭锁闩。

其他错误代码保留供将来使用。非错误状态代码可能会重叠,并且通常仅在其用例中是唯一的

锁闩状态代码。

名称

类别

简短描述

SDTX_LATCH_CLOSED

状态

0x0000

锁闩已关闭/已关闭。

SDTX_LATCH_OPENED

状态

0x0001

锁闩已打开/已打开。

底座状态代码。

名称

类别

简短描述

SDTX_BASE_DETACHED

状态

0x0000

底座已分离/不存在。

SDTX_BASE_ATTACHED

状态

0x0001

底座已连接/存在。

同样,其他代码保留供将来使用。

事件

可以通过从设备文件读取来接收事件。默认情况下它们处于禁用状态,必须先执行 SDTX_IOCTL_EVENTS_ENABLE 才能启用。所有事件都遵循 struct sdtx_event 规定的布局。特定的事件类型可以通过它们的事件代码来识别,该事件代码在 enum sdtx_event_code 中进行了描述。请注意,其他事件代码保留供将来使用,因此事件解析器必须能够通过依赖事件标头中给定的有效负载长度来优雅地处理任何未知/不支持的事件类型。

当前提供的事件类型是

DTX 事件概述。

名称

代码

有效负载

简短描述

SDTX_EVENT_REQUEST

1

0 字节

分离过程已启动/中止。

SDTX_EVENT_CANCEL

2

2 字节

EC 取消了分离过程。

SDTX_EVENT_BASE_CONNECTION

3

4 字节

底座连接状态已更改。

SDTX_EVENT_LATCH_STATUS

4

2 字节

锁闩状态已更改。

SDTX_EVENT_DEVICE_MODE

5

2 字节

设备模式已更改。

更详细的个别事件

SDTX_EVENT_REQUEST

当分离过程启动时,或者如果正在进行中,则由用户通过按下分离按钮或从用户空间发送分离请求 (SDTX_IOCTL_LATCH_REQUEST) 来中止时发送。

没有任何有效负载。

SDTX_EVENT_CANCEL

当 EC 由于未满足的前提条件(例如,剪贴板电池电量过低而无法分离)或硬件故障而取消分离过程时发送。取消的原因在下面详细说明的事件有效负载中给出,并且可以是以下之一

  • SDTX_DETACH_TIMEDOUT:在锁闩锁定时,分离超时。锁闩既没有打开也没有解锁。

  • SDTX_DETACH_NOT_FEASIBLE:由于剪贴板电池电量不足,无法分离。

  • SDTX_ERR_FAILED_TO_OPEN:无法打开锁闩(硬件故障)。

  • SDTX_ERR_FAILED_TO_REMAIN_OPEN:无法保持锁闩打开(硬件故障)。

  • SDTX_ERR_FAILED_TO_CLOSE:无法关闭锁闩(硬件故障)。

此上下文中的其他错误代码保留供将来使用。

这些代码可以通过 SDTX_CATEGORY() 宏进行分类,以区分关键硬件错误 (SDTX_CATEGORY_HARDWARE_ERROR) 或运行时错误 (SDTX_CATEGORY_RUNTIME_ERROR),如果未满足某些分离的前提条件,后者可能会在正常操作期间发生。

分离取消事件有效负载

字段

类型

描述

原因

__u16

取消原因。

SDTX_EVENT_BASE_CONNECTION

当底座连接状态发生变化时发送,即当底座已连接、分离或由于剪贴板电池电量不足而无法分离时发送。新状态以及(如果连接了底座)底座的 ID 将作为 struct sdtx_base_info 类型的有效负载提供,其布局如下所示

底座连接更改事件有效负载

字段

类型

描述

状态

__u16

底座连接状态。

base_id

__u16

连接的底座类型(如果无底座,则为零)。

state 的可能值是

  • SDTX_BASE_DETACHED,

  • SDTX_BASE_ATTACHED,以及

  • SDTX_DETACH_NOT_FEASIBLE.

其他值保留供将来使用。

SDTX_EVENT_LATCH_STATUS

当锁闩状态发生变化时发送,即当锁闩已打开、关闭或发生错误时发送。当前状态作为有效负载提供

锁闩状态更改事件有效负载

字段

类型

描述

状态

__u16

锁闩状态。

status 的可能值是

  • SDTX_LATCH_CLOSED,

  • SDTX_LATCH_OPENED,

  • SDTX_ERR_FAILED_TO_OPEN,

  • SDTX_ERR_FAILED_TO_REMAIN_OPEN,以及

  • SDTX_ERR_FAILED_TO_CLOSE.

其他值保留供将来使用。

SDTX_EVENT_DEVICE_MODE

当设备模式发生变化时发送。新的设备模式作为有效负载提供

设备模式更改事件有效负载

字段

类型

描述

模式

__u16

设备操作模式。

mode 的可能值是

  • SDTX_DEVICE_MODE_TABLET,

  • SDTX_DEVICE_MODE_LAPTOP,以及

  • SDTX_DEVICE_MODE_STUDIO.

其他值保留供将来使用。

IOCTL

提供了以下 IOCTL

DTX IOCTL 概述

类型

编号

方向

名称

描述

0xA5

0x21

-

EVENTS_ENABLE

为当前文件描述符启用事件。

0xA5

0x22

-

EVENTS_DISABLE

为当前文件描述符禁用事件。

0xA5

0x23

-

LATCH_LOCK

锁定闩锁。

0xA5

0x24

-

LATCH_UNLOCK

解锁闩锁。

0xA5

0x25

-

LATCH_REQUEST

请求剪贴板分离。

0xA5

0x26

-

LATCH_CONFIRM

确认剪贴板分离请求。

0xA5

0x27

-

LATCH_HEARTBEAT

向 EC 发送心跳信号。

0xA5

0x28

-

LATCH_CANCEL

取消分离过程。

0xA5

0x29

R

GET_BASE_INFO

获取当前基座/连接信息。

0xA5

0x2A

R

GET_DEVICE_MODE

获取当前设备操作模式。

0xA5

0x2B

R

GET_LATCH_STATUS

获取当前设备闩锁状态。

SDTX_IOCTL_EVENTS_ENABLE

定义为 _IO(0xA5, 0x22)

为当前文件描述符启用事件。如果启用,可以通过从设备读取来获取事件。事件默认禁用。

SDTX_IOCTL_EVENTS_DISABLE

定义为 _IO(0xA5, 0x22)

为当前文件描述符禁用事件。如果启用,可以通过从设备读取来获取事件。事件默认禁用。

SDTX_IOCTL_LATCH_LOCK

定义为 _IO(0xA5, 0x23)

锁定闩锁,导致分离过程在超时时中止,而不会打开闩锁。闩锁默认解锁。如果闩锁已锁定,此命令将被静默忽略。

SDTX_IOCTL_LATCH_UNLOCK

定义为 _IO(0xA5, 0x24)

解锁闩锁,导致分离过程在超时时打开闩锁。闩锁默认解锁。在正在进行的分离过程中发送此命令不会打开闩锁。如果闩锁已解锁,此命令将被静默忽略。

SDTX_IOCTL_LATCH_REQUEST

定义为 _IO(0xA5, 0x25)

通用闩锁请求。行为取决于上下文:如果没有活动的分离过程,则请求分离。否则,当前活动的分离过程将被中止。

如果分离过程被此操作取消,将发送一个通用的分离请求事件 (SDTX_EVENT_REQUEST)。

这本质上与按下分离按钮的行为相同。

SDTX_IOCTL_LATCH_CONFIRM

定义为 _IO(0xA5, 0x26)

确认并确认闩锁请求。如果在正在进行的分离过程中发送,此命令会导致闩锁立即打开。如果闩锁已被锁定,也会打开。在这种情况下,闩锁锁将被重置为解锁状态。

如果没有正在进行的分离过程,此命令将被静默忽略。

SDTX_IOCTL_LATCH_HEARTBEAT

定义为 _IO(0xA5, 0x27)

发送心跳信号,本质上是重置分离超时。当成功分离所需的工作仍在进行中时,可以使用此命令来保持分离过程的活动状态。

如果没有正在进行的分离过程,此命令将被静默忽略。

SDTX_IOCTL_LATCH_CANCEL

定义为 _IO(0xA5, 0x28)

取消正在进行的分离(如果有)。如果分离过程被此操作取消,将发送一个通用的分离请求事件 (SDTX_EVENT_REQUEST)。

如果没有正在进行的分离过程,此命令将被静默忽略。

SDTX_IOCTL_GET_BASE_INFO

定义为 _IOR(0xA5, 0x29, struct sdtx_base_info)

获取当前基座连接状态(即已连接/已分离)以及连接到剪贴板的基座类型。此命令本质上提供了一种查询基座连接更改事件 (SDTX_EVENT_BASE_CONNECTION) 提供的信息的方法。

struct sdtx_base_info.state 的可能值有

  • SDTX_BASE_DETACHED,

  • SDTX_BASE_ATTACHED,以及

  • SDTX_DETACH_NOT_FEASIBLE.

其他值保留供将来使用。

SDTX_IOCTL_GET_DEVICE_MODE

定义为 _IOR(0xA5, 0x2A, __u16)

返回设备操作模式,指示基座是否以及如何连接到剪贴板。此命令本质上提供了一种查询设备模式更改事件 (SDTX_EVENT_DEVICE_MODE) 提供的信息的方法。

返回的值是

  • SDTX_DEVICE_MODE_LAPTOP

  • SDTX_DEVICE_MODE_TABLET

  • SDTX_DEVICE_MODE_STUDIO

有关详细信息,请参阅 struct sdtx_device_mode。其他值保留以供将来使用。

SDTX_IOCTL_GET_LATCH_STATUS

定义为 _IOR(0xA5, 0x2B, __u16)

获取当前闩锁状态或(大概)尝试打开/关闭闩锁时遇到的最后一个错误。此命令本质上提供了一种查询闩锁状态更改事件 (SDTX_EVENT_LATCH_STATUS) 提供的信息的方法。

返回的值是

  • SDTX_LATCH_CLOSED,

  • SDTX_LATCH_OPENED,

  • SDTX_ERR_FAILED_TO_OPEN,

  • SDTX_ERR_FAILED_TO_REMAIN_OPEN,以及

  • SDTX_ERR_FAILED_TO_CLOSE.

其他值保留供将来使用。

关于基座 ID 的说明

通过 SDTX_EVENT_BASE_CONNECTIONSDTX_IOCTL_GET_BASE_INFO 提供的基座类型/ID 直接从 EC 中组合 __u16 值的低字节转发,驱动程序将此 ID 来自的 EC 类型存储在高字节中(如果没有这个,来自不同类型的 EC 的基座 ID 可能会重叠)。

可以使用 SDTX_DEVICE_TYPE() 宏来确定 EC 设备类型。这可以是以下之一

  • SDTX_DEVICE_TYPE_HID,用于通过 HID 的 Surface 聚合器模块,以及

  • SDTX_DEVICE_TYPE_SSH,用于通过 Surface 串行集线器的 Surface 聚合器模块。

请注意,目前仅支持 SSH 类型的 EC,但是 HID 类型保留以供将来使用。

结构体和枚举

enum sdtx_device_mode

描述剪贴板如何(以及是否)连接到设备基座的模式。

常量

SDTX_DEVICE_MODE_TABLET

剪贴板与基座分离,设备作为平板电脑运行。

SDTX_DEVICE_MODE_LAPTOP

剪贴板正常连接到基座,设备作为笔记本电脑运行。

SDTX_DEVICE_MODE_STUDIO

剪贴板反向连接到基座。设备作为平板电脑运行,键盘和触摸板停用,但是,基座电池以及(如果特定设备型号中存在)dGPU 可供系统使用。

struct sdtx_event

通过从 DTX 设备文件读取提供的事件。

定义:

struct sdtx_event {
    __u16 length;
    __u16 code;
    __u8 data[];
};

成员

length

事件负载的长度,以字节为单位。

code

事件代码,详细说明此事件的类型。

data

事件的负载,包含 length 字节。

描述

有关当前有效的事件代码,请参阅 enum sdtx_event_code

enum sdtx_event_code

描述事件类型的代码。

常量

SDTX_EVENT_REQUEST

分离请求事件类型。

SDTX_EVENT_CANCEL

取消分离过程事件类型。

SDTX_EVENT_BASE_CONNECTION

基座/剪贴板连接更改事件类型。

SDTX_EVENT_LATCH_STATUS

闩锁状态更改事件类型。

SDTX_EVENT_DEVICE_MODE

设备模式更改事件类型。

描述

struct sdtx_event 中用于描述事件类型。保留更多事件代码以供将来使用。任何事件解析器都应该能够优雅地处理未知事件,例如通过简单地跳过它们。

有关各个事件类型的详细信息,请参阅 DTX 用户空间接口文档。

struct sdtx_base_info

描述是否连接了基座以及连接了哪种类型的基座。

定义:

struct sdtx_base_info {
    __u16 state;
    __u16 base_id;
};

成员

状态

连接状态。有效值为 SDTX_BASE_DETACHEDSDTX_BASE_ATTACHEDSDTX_DETACH_NOT_FEASIBLE(如果连接了基座但剪贴板电池电量不足以防止分离)。目前保留其他值。

base_id

连接的基座类型。如果未连接基座,则为零。

API 用户

可以在 https://github.com/linux-surface/surface-dtx-daemon 找到使用此 API 的用户空间守护进程。