用户空间 EC 接口 (cdev)

surface_aggregator_cdev 模块为 SSAM 控制器提供了一个 misc 设备,允许用户空间与 SAM EC 进行(或多或少)直接连接。它旨在用于开发和调试,因此不应以任何其他方式使用或依赖。请注意,此模块不会自动加载,而是必须手动加载。

提供的接口可通过 /dev/surface/aggregator 设备文件访问。此接口的所有功能都通过 IOCTL 提供。这些 IOCTL 及其各自的输入/输出参数结构在 include/uapi/linux/surface_aggregator/cdev.h 中定义。

访问此接口的小型 Python 库和脚本可以在 https://github.com/linux-surface/surface-aggregator-module/tree/master/scripts/ssam 中找到。

接收事件

可以通过读取设备文件来接收事件。它们由 struct ssam_cdev_event 数据类型表示。

但是,在可以读取事件之前,必须通过 SSAM_CDEV_NOTIF_REGISTER IOCTL 注册所需的通知器。本质上,通知器是在 EC 发送事件时调用的回调。在此接口中,它们与特定的目标类别和设备文件实例关联。它们将此类别的任何事件转发到相应实例的缓冲区,然后可以从中读取。

通知器本身不会在 EC 上启用事件。因此,可能还需要通过 SSAM_CDEV_EVENT_ENABLE IOCTL 启用事件。虽然通知器按客户端工作(即按设备文件实例工作),但事件是全局启用的,适用于 EC 及其所有客户端(无论用户空间还是非用户空间)。SSAM_CDEV_EVENT_ENABLESSAM_CDEV_EVENT_DISABLE IOCTL 负责事件的引用计数,以便只要有客户端请求事件,该事件就会被启用。

请注意,一旦客户端实例关闭,已启用的事件不会自动禁用。因此,任何客户端进程(或进程组)都应平衡其事件启用调用与相应的事件禁用调用。但是,在不同的客户端实例上启用和禁用事件是完全有效的。例如,在客户端实例 A 上设置通知器并读取事件,在实例 B 上启用这些事件(请注意,由于事件是全局启用/禁用的,因此 A 也会收到这些事件),并且在不再需要事件后,通过实例 C 禁用先前启用的事件是有效的。

控制器 IOCTL

提供以下 IOCTL

控制器 IOCTL

类型

编号

方向

名称

描述

0xA5

1

WR

REQUEST

执行同步 SAM 请求。

0xA5

2

W

NOTIF_REGISTER

注册事件通知器。

0xA5

3

W

NOTIF_UNREGISTER

取消注册事件通知器。

0xA5

4

W

EVENT_ENABLE

启用事件源。

0xA5

5

W

EVENT_DISABLE

禁用事件源。

SSAM_CDEV_REQUEST

定义为 _IOWR(0xA5, 1, struct ssam_cdev_request)

执行同步 SAM 请求。请求规范作为 struct ssam_cdev_request 类型的参数传入,然后由 IOCTL 写入/修改以返回请求的状态和结果。

请求负载数据必须单独分配,并通过 payload.datapayload.length 成员传入。如果需要响应,则响应缓冲区必须由调用者分配,并通过 response.data 成员传入。response.length 成员必须设置为此缓冲区的容量,如果不需要响应,则设置为零。请求完成后,调用会将响应写入响应缓冲区(如果其容量允许),并使用响应的实际大小(以字节为单位)覆盖长度字段。

此外,如果请求有响应,则必须通过请求标志来指示,就像内核请求一样。可以通过 flags 成员设置请求标志,值对应于 enum ssam_cdev_request_flags 中的值。

最后,请求本身的状态在 status 成员中返回(负 errno 值表示失败)。请注意,IOCTL 的失败指示与请求的失败指示是分开的:如果请求设置期间出现任何失败 (-EFAULT) 或者如果提供的参数或其任何字段无效 (-EINVAL),则 IOCTL 返回负状态代码。在这种情况下,可以设置请求参数的状态值,提供有关出错原因的更多详细信息(例如,内存不足的 -ENOMEM),但此值也可以为零。如果请求已在 IOCTL 内部成功设置、提交和完成(即返回到用户空间),则 IOCTL 将返回零状态代码,但如果请求的实际执行在提交后失败,则请求 status 成员可能仍为负值。

下面提供了参数结构的完整定义。

SSAM_CDEV_NOTIF_REGISTER

定义为 _IOW(0xA5, 2, struct ssam_cdev_notifier_desc)

使用指定的优先级为给定通知器描述中指定的事件目标类别注册一个通知器。接收事件需要注册通知器,但这不会启用事件本身。在为特定目标类别注册通知器后,该类别的所有事件都将转发给用户空间客户端,然后可以从设备文件实例中读取。请注意,可能需要启用事件,例如通过 SSAM_CDEV_EVENT_ENABLE IOCTL,然后EC才会发送它们。

每个目标类别和客户端实例只能注册一个通知器。如果已注册通知器,则此 IOCTL 将失败并返回 -EEXIST

当设备文件实例关闭时,通知器将自动被删除。

SSAM_CDEV_NOTIF_UNREGISTER

定义为 _IOW(0xA5, 3, struct ssam_cdev_notifier_desc)

取消注册与指定目标类别关联的通知器。此 IOCTL 将忽略优先级字段。如果未为此客户端实例和给定类别注册任何通知器,则此 IOCTL 将失败并返回 -ENOENT

SSAM_CDEV_EVENT_ENABLE

定义为 _IOW(0xA5, 4, struct ssam_cdev_event_desc)

启用与给定事件描述符关联的事件。

请注意,此调用本身不会注册通知器,它只会启用控制器上的事件。如果要通过读取设备文件来接收事件,则需要在该实例上注册相应的通知器。

当设备文件关闭时,事件不会自动禁用。必须通过调用 SSAM_CDEV_EVENT_DISABLE IOCTL 手动完成此操作。

SSAM_CDEV_EVENT_DISABLE

定义为 _IOW(0xA5, 5, struct ssam_cdev_event_desc)

禁用与给定事件描述符关联的事件。

请注意,这不会取消注册任何通知器。在此调用之后,仍可能接收到事件并将其转发到用户空间。停止接收事件的唯一安全方法是取消注册所有先前注册的通知器。

结构体和枚举

enum ssam_cdev_request_flags

SSAM cdev 请求 IOCTL 的请求标志。

常量

SSAM_CDEV_REQUEST_HAS_RESPONSE

指定请求期望收到响应。如果未设置,则在传输完底层数据包后将直接完成请求。如果设置,则请求传输系统会等待请求的响应。

SSAM_CDEV_REQUEST_UNSEQUENCED

指定应通过无序数据包传输请求。如果设置,则请求不得有响应,这意味着此标志和 SSAM_CDEV_REQUEST_HAS_RESPONSE 标志是互斥的。

struct ssam_cdev_request

控制器请求 IOCTL 参数。

定义:

struct ssam_cdev_request {
    __u8 target_category;
    __u8 target_id;
    __u8 command_id;
    __u8 instance_id;
    __u16 flags;
    __s16 status;
    struct {
        __u64 data;
        __u16 length;
        __u8 __pad[6];
    } payload;
    struct {
        __u64 data;
        __u16 length;
        __u8 __pad[6];
    } response;
};

成员

target_category

SAM 请求的目标类别。

target_id

SAM 请求的目标 ID。

command_id

SAM 请求的命令 ID。

instance_id

SAM 请求的实例 ID。

flags

请求标志(请参见 enum ssam_cdev_request_flags)。

status

请求状态(输出)。

payload

请求有效负载(输入数据)。

payload.data

指向请求有效负载数据的指针。

payload.length

请求有效负载数据的长度(以字节为单位)。

response

请求响应(输出数据)。

response.data

指向响应缓冲区的指针。

response.length

输入时:响应缓冲区的容量(以字节为单位)。输出时:请求响应的长度(缓冲区中实际使用的字节数)。

struct ssam_cdev_notifier_desc

通知器描述符。

定义:

struct ssam_cdev_notifier_desc {
    __s32 priority;
    __u8 target_category;
};

成员

priority

确定调用通知器回调顺序的优先级值。值越高表示优先级越高,即关联的回调将比其他(较低优先级)回调更早执行。

target_category

此通知器应接收事件的事件目标类别。

描述

指定应注册或取消注册的通知器,特别是其优先级和事件的目标类别。

struct ssam_cdev_event_desc

事件描述符。

定义:

struct ssam_cdev_event_desc {
    struct {
        __u8 target_category;
        __u8 target_id;
        __u8 cid_enable;
        __u8 cid_disable;
    } reg;
    struct {
        __u8 target_category;
        __u8 instance;
    } id;
    __u8 flags;
};

成员

reg

将通过其启用/禁用事件的注册表。

reg.target_category

事件注册表请求的目标类别。

reg.target_id

事件注册表请求的目标 ID。

reg.cid_enable

事件启用请求的命令 ID。

reg.cid_disable

事件禁用请求的命令 ID。

id

指定事件的 ID。

id.target_category

事件源的目标类别。

id.instance

事件源的实例 ID。

flags

用于启用事件的标志。

描述

指定应启用/禁用哪个事件以及如何执行此操作。

struct ssam_cdev_event

EC 发送的 SSAM 事件。

定义:

struct ssam_cdev_event {
    __u8 target_category;
    __u8 target_id;
    __u8 command_id;
    __u8 instance_id;
    __u16 length;
    __u8 data[];
};

成员

target_category

事件源的目标类别。请参见 enum ssam_ssh_tc

target_id

事件源的目标 ID。

command_id

事件的命令 ID。

instance_id

事件源的实例 ID。

length

事件有效负载的长度(以字节为单位)。

data

事件有效负载数据。