用户空间 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 开始。在此之后
EC 打开分离按钮上的指示灯 LED,发送 分离请求 事件 (
SDTX_EVENT_REQUEST
),并等待进一步的指令/命令。如果锁存器已解锁,则 LED 将闪烁绿色。如果锁存器已被锁定,则 LED 将常亮红色该事件通过
surface_dtx
驱动程序中继到用户空间,在那里适当的用户空间守护程序可以处理它并通过此驱动程序提供的 IOCTL 将指令发送回 EC。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
) 完成的。
用户空间接口文档¶
错误代码和状态值¶
错误和状态代码分为不同的类别,可用于确定状态代码是否为错误,如果是,则确定该错误的严重性和类型。当前的类别是
名称 |
值 |
简短描述 |
---|---|---|
|
|
非错误状态代码。 |
|
|
非关键运行时错误。 |
|
|
关键硬件故障。 |
|
|
未知错误代码。 |
其他类别保留供将来使用。SDTX_CATEGORY()
宏可用于确定任何状态值的类别。SDTX_SUCCESS()
宏可用于检查状态值是否为成功值 (SDTX_CATEGORY_STATUS
),或者是否表示失败。
EC 发送的未知状态或错误代码由驱动程序分配到 UNKNOWN
类别,并且将来可能会通过它们自己的代码来实现。
当前使用的错误代码是
名称 |
类别 |
值 |
简短描述 |
---|---|---|---|
|
|
|
由于剪贴板电池电量不足,无法分离。 |
|
|
|
在锁闩锁定时,分离过程超时。 |
|
|
|
无法打开锁闩。 |
|
|
|
无法保持锁闩打开。 |
|
|
|
无法关闭锁闩。 |
其他错误代码保留供将来使用。非错误状态代码可能会重叠,并且通常仅在其用例中是唯一的
名称 |
类别 |
值 |
简短描述 |
---|---|---|---|
|
|
|
锁闩已关闭/已关闭。 |
|
|
|
锁闩已打开/已打开。 |
名称 |
类别 |
值 |
简短描述 |
---|---|---|---|
|
|
|
底座已分离/不存在。 |
|
|
|
底座已连接/存在。 |
同样,其他代码保留供将来使用。
事件¶
可以通过从设备文件读取来接收事件。默认情况下它们处于禁用状态,必须先执行 SDTX_IOCTL_EVENTS_ENABLE
才能启用。所有事件都遵循 struct sdtx_event
规定的布局。特定的事件类型可以通过它们的事件代码来识别,该事件代码在 enum sdtx_event_code
中进行了描述。请注意,其他事件代码保留供将来使用,因此事件解析器必须能够通过依赖事件标头中给定的有效负载长度来优雅地处理任何未知/不支持的事件类型。
当前提供的事件类型是
名称 |
代码 |
有效负载 |
简短描述 |
---|---|---|---|
|
|
|
分离过程已启动/中止。 |
|
|
|
EC 取消了分离过程。 |
|
|
|
底座连接状态已更改。 |
|
|
|
锁闩状态已更改。 |
|
|
|
设备模式已更改。 |
更详细的个别事件
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
),如果未满足某些分离的前提条件,后者可能会在正常操作期间发生。
字段 |
类型 |
描述 |
---|---|---|
|
|
取消原因。 |
SDTX_EVENT_BASE_CONNECTION
¶
当底座连接状态发生变化时发送,即当底座已连接、分离或由于剪贴板电池电量不足而无法分离时发送。新状态以及(如果连接了底座)底座的 ID 将作为 struct sdtx_base_info
类型的有效负载提供,其布局如下所示
字段 |
类型 |
描述 |
---|---|---|
|
|
底座连接状态。 |
|
|
连接的底座类型(如果无底座,则为零)。 |
state
的可能值是
SDTX_BASE_DETACHED
,SDTX_BASE_ATTACHED
,以及SDTX_DETACH_NOT_FEASIBLE
.
其他值保留供将来使用。
SDTX_EVENT_LATCH_STATUS
¶
当锁闩状态发生变化时发送,即当锁闩已打开、关闭或发生错误时发送。当前状态作为有效负载提供
字段 |
类型 |
描述 |
---|---|---|
|
|
锁闩状态。 |
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
¶
当设备模式发生变化时发送。新的设备模式作为有效负载提供
字段 |
类型 |
描述 |
---|---|---|
|
|
设备操作模式。 |
mode
的可能值是
SDTX_DEVICE_MODE_TABLET
,SDTX_DEVICE_MODE_LAPTOP
,以及SDTX_DEVICE_MODE_STUDIO
.
其他值保留供将来使用。
IOCTL¶
提供了以下 IOCTL
类型 |
编号 |
方向 |
名称 |
描述 |
---|---|---|---|---|
|
|
|
|
为当前文件描述符启用事件。 |
|
|
|
|
为当前文件描述符禁用事件。 |
|
|
|
|
锁定闩锁。 |
|
|
|
|
解锁闩锁。 |
|
|
|
|
请求剪贴板分离。 |
|
|
|
|
确认剪贴板分离请求。 |
|
|
|
|
向 EC 发送心跳信号。 |
|
|
|
|
取消分离过程。 |
|
|
|
|
获取当前基座/连接信息。 |
|
|
|
|
获取当前设备操作模式。 |
|
|
|
|
获取当前设备闩锁状态。 |
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_CONNECTION
或 SDTX_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_DETACHED
、SDTX_BASE_ATTACHED
和SDTX_DETACH_NOT_FEASIBLE
(如果连接了基座但剪贴板电池电量不足以防止分离)。目前保留其他值。base_id
连接的基座类型。如果未连接基座,则为零。
API 用户¶
可以在 https://github.com/linux-surface/surface-dtx-daemon 找到使用此 API 的用户空间守护进程。