Loading...
1.. SPDX-License-Identifier: GPL-2.0+
2
3.. |u8| replace:: :c:type:`u8 <u8>`
4.. |u16| replace:: :c:type:`u16 <u16>`
5.. |TYPE| replace:: ``TYPE``
6.. |LEN| replace:: ``LEN``
7.. |SEQ| replace:: ``SEQ``
8.. |SYN| replace:: ``SYN``
9.. |NAK| replace:: ``NAK``
10.. |ACK| replace:: ``ACK``
11.. |DATA| replace:: ``DATA``
12.. |DATA_SEQ| replace:: ``DATA_SEQ``
13.. |DATA_NSQ| replace:: ``DATA_NSQ``
14.. |TC| replace:: ``TC``
15.. |TID| replace:: ``TID``
16.. |IID| replace:: ``IID``
17.. |RQID| replace:: ``RQID``
18.. |CID| replace:: ``CID``
19
20===========================
21Surface Serial Hub Protocol
22===========================
23
24The Surface Serial Hub (SSH) is the central communication interface for the
25embedded Surface Aggregator Module controller (SAM or EC), found on newer
26Surface generations. We will refer to this protocol and interface as
27SAM-over-SSH, as opposed to SAM-over-HID for the older generations.
28
29On Surface devices with SAM-over-SSH, SAM is connected to the host via UART
30and defined in ACPI as device with ID ``MSHW0084``. On these devices,
31significant functionality is provided via SAM, including access to battery
32and power information and events, thermal read-outs and events, and many
33more. For Surface Laptops, keyboard input is handled via HID directed
34through SAM, on the Surface Laptop 3 and Surface Book 3 this also includes
35touchpad input.
36
37Note that the standard disclaimer for this subsystem also applies to this
38document: All of this has been reverse-engineered and may thus be erroneous
39and/or incomplete.
40
41All CRCs used in the following are two-byte ``crc_ccitt_false(0xffff, ...)``.
42All multi-byte values are little-endian, there is no implicit padding between
43values.
44
45
46SSH Packet Protocol: Definitions
47================================
48
49The fundamental communication unit of the SSH protocol is a frame
50(:c:type:`struct ssh_frame <ssh_frame>`). A frame consists of the following
51fields, packed together and in order:
52
53.. flat-table:: SSH Frame
54 :widths: 1 1 4
55 :header-rows: 1
56
57 * - Field
58 - Type
59 - Description
60
61 * - |TYPE|
62 - |u8|
63 - Type identifier of the frame.
64
65 * - |LEN|
66 - |u16|
67 - Length of the payload associated with the frame.
68
69 * - |SEQ|
70 - |u8|
71 - Sequence ID (see explanation below).
72
73Each frame structure is followed by a CRC over this structure. The CRC over
74the frame structure (|TYPE|, |LEN|, and |SEQ| fields) is placed directly
75after the frame structure and before the payload. The payload is followed by
76its own CRC (over all payload bytes). If the payload is not present (i.e.
77the frame has ``LEN=0``), the CRC of the payload is still present and will
78evaluate to ``0xffff``. The |LEN| field does not include any of the CRCs, it
79equals the number of bytes inbetween the CRC of the frame and the CRC of the
80payload.
81
82Additionally, the following fixed two-byte sequences are used:
83
84.. flat-table:: SSH Byte Sequences
85 :widths: 1 1 4
86 :header-rows: 1
87
88 * - Name
89 - Value
90 - Description
91
92 * - |SYN|
93 - ``[0xAA, 0x55]``
94 - Synchronization bytes.
95
96A message consists of |SYN|, followed by the frame (|TYPE|, |LEN|, |SEQ| and
97CRC) and, if specified in the frame (i.e. ``LEN > 0``), payload bytes,
98followed finally, regardless if the payload is present, the payload CRC. The
99messages corresponding to an exchange are, in part, identified by having the
100same sequence ID (|SEQ|), stored inside the frame (more on this in the next
101section). The sequence ID is a wrapping counter.
102
103A frame can have the following types
104(:c:type:`enum ssh_frame_type <ssh_frame_type>`):
105
106.. flat-table:: SSH Frame Types
107 :widths: 1 1 4
108 :header-rows: 1
109
110 * - Name
111 - Value
112 - Short Description
113
114 * - |NAK|
115 - ``0x04``
116 - Sent on error in previously received message.
117
118 * - |ACK|
119 - ``0x40``
120 - Sent to acknowledge receival of |DATA| frame.
121
122 * - |DATA_SEQ|
123 - ``0x80``
124 - Sent to transfer data. Sequenced.
125
126 * - |DATA_NSQ|
127 - ``0x00``
128 - Same as |DATA_SEQ|, but does not need to be ACKed.
129
130Both |NAK|- and |ACK|-type frames are used to control flow of messages and
131thus do not carry a payload. |DATA_SEQ|- and |DATA_NSQ|-type frames on the
132other hand must carry a payload. The flow sequence and interaction of
133different frame types will be described in more depth in the next section.
134
135
136SSH Packet Protocol: Flow Sequence
137==================================
138
139Each exchange begins with |SYN|, followed by a |DATA_SEQ|- or
140|DATA_NSQ|-type frame, followed by its CRC, payload, and payload CRC. In
141case of a |DATA_NSQ|-type frame, the exchange is then finished. In case of a
142|DATA_SEQ|-type frame, the receiving party has to acknowledge receival of
143the frame by responding with a message containing an |ACK|-type frame with
144the same sequence ID of the |DATA| frame. In other words, the sequence ID of
145the |ACK| frame specifies the |DATA| frame to be acknowledged. In case of an
146error, e.g. an invalid CRC, the receiving party responds with a message
147containing an |NAK|-type frame. As the sequence ID of the previous data
148frame, for which an error is indicated via the |NAK| frame, cannot be relied
149upon, the sequence ID of the |NAK| frame should not be used and is set to
150zero. After receival of an |NAK| frame, the sending party should re-send all
151outstanding (non-ACKed) messages.
152
153Sequence IDs are not synchronized between the two parties, meaning that they
154are managed independently for each party. Identifying the messages
155corresponding to a single exchange thus relies on the sequence ID as well as
156the type of the message, and the context. Specifically, the sequence ID is
157used to associate an ``ACK`` with its ``DATA_SEQ``-type frame, but not
158``DATA_SEQ``- or ``DATA_NSQ``-type frames with other ``DATA``- type frames.
159
160An example exchange might look like this:
161
162::
163
164 tx: -- SYN FRAME(D) CRC(F) PAYLOAD CRC(P) -----------------------------
165 rx: ------------------------------------- SYN FRAME(A) CRC(F) CRC(P) --
166
167where both frames have the same sequence ID (``SEQ``). Here, ``FRAME(D)``
168indicates a |DATA_SEQ|-type frame, ``FRAME(A)`` an ``ACK``-type frame,
169``CRC(F)`` the CRC over the previous frame, ``CRC(P)`` the CRC over the
170previous payload. In case of an error, the exchange would look like this:
171
172::
173
174 tx: -- SYN FRAME(D) CRC(F) PAYLOAD CRC(P) -----------------------------
175 rx: ------------------------------------- SYN FRAME(N) CRC(F) CRC(P) --
176
177upon which the sender should re-send the message. ``FRAME(N)`` indicates an
178|NAK|-type frame. Note that the sequence ID of the |NAK|-type frame is fixed
179to zero. For |DATA_NSQ|-type frames, both exchanges are the same:
180
181::
182
183 tx: -- SYN FRAME(DATA_NSQ) CRC(F) PAYLOAD CRC(P) ----------------------
184 rx: -------------------------------------------------------------------
185
186Here, an error can be detected, but not corrected or indicated to the
187sending party. These exchanges are symmetric, i.e. switching ``rx`` and
188``tx`` results again in a valid exchange. Currently, no longer exchanges are
189known.
190
191
192Commands: Requests, Responses, and Events
193=========================================
194
195Commands are sent as payload inside a data frame. Currently, this is the
196only known payload type of |DATA| frames, with a payload-type value of
197``0x80`` (:c:type:`SSH_PLD_TYPE_CMD <ssh_payload_type>`).
198
199The command-type payload (:c:type:`struct ssh_command <ssh_command>`)
200consists of an eight-byte command structure, followed by optional and
201variable length command data. The length of this optional data is derived
202from the frame payload length given in the corresponding frame, i.e. it is
203``frame.len - sizeof(struct ssh_command)``. The command struct contains the
204following fields, packed together and in order:
205
206.. flat-table:: SSH Command
207 :widths: 1 1 4
208 :header-rows: 1
209
210 * - Field
211 - Type
212 - Description
213
214 * - |TYPE|
215 - |u8|
216 - Type of the payload. For commands always ``0x80``.
217
218 * - |TC|
219 - |u8|
220 - Target category.
221
222 * - |TID| (out)
223 - |u8|
224 - Target ID for outgoing (host to EC) commands.
225
226 * - |TID| (in)
227 - |u8|
228 - Target ID for incoming (EC to host) commands.
229
230 * - |IID|
231 - |u8|
232 - Instance ID.
233
234 * - |RQID|
235 - |u16|
236 - Request ID.
237
238 * - |CID|
239 - |u8|
240 - Command ID.
241
242The command struct and data, in general, does not contain any failure
243detection mechanism (e.g. CRCs), this is solely done on the frame level.
244
245Command-type payloads are used by the host to send commands and requests to
246the EC as well as by the EC to send responses and events back to the host.
247We differentiate between requests (sent by the host), responses (sent by the
248EC in response to a request), and events (sent by the EC without a preceding
249request).
250
251Commands and events are uniquely identified by their target category
252(``TC``) and command ID (``CID``). The target category specifies a general
253category for the command (e.g. system in general, vs. battery and AC, vs.
254temperature, and so on), while the command ID specifies the command inside
255that category. Only the combination of |TC| + |CID| is unique. Additionally,
256commands have an instance ID (``IID``), which is used to differentiate
257between different sub-devices. For example ``TC=3`` ``CID=1`` is a
258request to get the temperature on a thermal sensor, where |IID| specifies
259the respective sensor. If the instance ID is not used, it should be set to
260zero. If instance IDs are used, they, in general, start with a value of one,
261whereas zero may be used for instance independent queries, if applicable. A
262response to a request should have the same target category, command ID, and
263instance ID as the corresponding request.
264
265Responses are matched to their corresponding request via the request ID
266(``RQID``) field. This is a 16 bit wrapping counter similar to the sequence
267ID on the frames. Note that the sequence ID of the frames for a
268request-response pair does not match. Only the request ID has to match.
269Frame-protocol wise these are two separate exchanges, and may even be
270separated, e.g. by an event being sent after the request but before the
271response. Not all commands produce a response, and this is not detectable by
272|TC| + |CID|. It is the responsibility of the issuing party to wait for a
273response (or signal this to the communication framework, as is done in
274SAN/ACPI via the ``SNC`` flag).
275
276Events are identified by unique and reserved request IDs. These IDs should
277not be used by the host when sending a new request. They are used on the
278host to, first, detect events and, second, match them with a registered
279event handler. Request IDs for events are chosen by the host and directed to
280the EC when setting up and enabling an event source (via the
281enable-event-source request). The EC then uses the specified request ID for
282events sent from the respective source. Note that an event should still be
283identified by its target category, command ID, and, if applicable, instance
284ID, as a single event source can send multiple different event types. In
285general, however, a single target category should map to a single reserved
286event request ID.
287
288Furthermore, requests, responses, and events have an associated target ID
289(``TID``). This target ID is split into output (host to EC) and input (EC to
290host) fields, with the respecting other field (e.g. output field on incoming
291messages) set to zero. Two ``TID`` values are known: Primary (``0x01``) and
292secondary (``0x02``). In general, the response to a request should have the
293same ``TID`` value, however, the field (output vs. input) should be used in
294accordance to the direction in which the response is sent (i.e. on the input
295field, as responses are generally sent from the EC to the host).
296
297Note that, even though requests and events should be uniquely identifiable
298by target category and command ID alone, the EC may require specific
299target ID and instance ID values to accept a command. A command that is
300accepted for ``TID=1``, for example, may not be accepted for ``TID=2``
301and vice versa.
302
303
304Limitations and Observations
305============================
306
307The protocol can, in theory, handle up to ``U8_MAX`` frames in parallel,
308with up to ``U16_MAX`` pending requests (neglecting request IDs reserved for
309events). In practice, however, this is more limited. From our testing
310(although via a python and thus a user-space program), it seems that the EC
311can handle up to four requests (mostly) reliably in parallel at a certain
312time. With five or more requests in parallel, consistent discarding of
313commands (ACKed frame but no command response) has been observed. For five
314simultaneous commands, this reproducibly resulted in one command being
315dropped and four commands being handled.
316
317However, it has also been noted that, even with three requests in parallel,
318occasional frame drops happen. Apart from this, with a limit of three
319pending requests, no dropped commands (i.e. command being dropped but frame
320carrying command being ACKed) have been observed. In any case, frames (and
321possibly also commands) should be re-sent by the host if a certain timeout
322is exceeded. This is done by the EC for frames with a timeout of one second,
323up to two re-tries (i.e. three transmissions in total). The limit of
324re-tries also applies to received NAKs, and, in a worst case scenario, can
325lead to entire messages being dropped.
326
327While this also seems to work fine for pending data frames as long as no
328transmission failures occur, implementation and handling of these seems to
329depend on the assumption that there is only one non-acknowledged data frame.
330In particular, the detection of repeated frames relies on the last sequence
331number. This means that, if a frame that has been successfully received by
332the EC is sent again, e.g. due to the host not receiving an |ACK|, the EC
333will only detect this if it has the sequence ID of the last frame received
334by the EC. As an example: Sending two frames with ``SEQ=0`` and ``SEQ=1``
335followed by a repetition of ``SEQ=0`` will not detect the second ``SEQ=0``
336frame as such, and thus execute the command in this frame each time it has
337been received, i.e. twice in this example. Sending ``SEQ=0``, ``SEQ=1`` and
338then repeating ``SEQ=1`` will detect the second ``SEQ=1`` as repetition of
339the first one and ignore it, thus executing the contained command only once.
340
341In conclusion, this suggests a limit of at most one pending un-ACKed frame
342(per party, effectively leading to synchronous communication regarding
343frames) and at most three pending commands. The limit to synchronous frame
344transfers seems to be consistent with behavior observed on Windows.
1.. SPDX-License-Identifier: GPL-2.0+
2
3.. |u8| replace:: :c:type:`u8 <u8>`
4.. |u16| replace:: :c:type:`u16 <u16>`
5.. |TYPE| replace:: ``TYPE``
6.. |LEN| replace:: ``LEN``
7.. |SEQ| replace:: ``SEQ``
8.. |SYN| replace:: ``SYN``
9.. |NAK| replace:: ``NAK``
10.. |ACK| replace:: ``ACK``
11.. |DATA| replace:: ``DATA``
12.. |DATA_SEQ| replace:: ``DATA_SEQ``
13.. |DATA_NSQ| replace:: ``DATA_NSQ``
14.. |TC| replace:: ``TC``
15.. |TID| replace:: ``TID``
16.. |SID| replace:: ``SID``
17.. |IID| replace:: ``IID``
18.. |RQID| replace:: ``RQID``
19.. |CID| replace:: ``CID``
20
21===========================
22Surface Serial Hub Protocol
23===========================
24
25The Surface Serial Hub (SSH) is the central communication interface for the
26embedded Surface Aggregator Module controller (SAM or EC), found on newer
27Surface generations. We will refer to this protocol and interface as
28SAM-over-SSH, as opposed to SAM-over-HID for the older generations.
29
30On Surface devices with SAM-over-SSH, SAM is connected to the host via UART
31and defined in ACPI as device with ID ``MSHW0084``. On these devices,
32significant functionality is provided via SAM, including access to battery
33and power information and events, thermal read-outs and events, and many
34more. For Surface Laptops, keyboard input is handled via HID directed
35through SAM, on the Surface Laptop 3 and Surface Book 3 this also includes
36touchpad input.
37
38Note that the standard disclaimer for this subsystem also applies to this
39document: All of this has been reverse-engineered and may thus be erroneous
40and/or incomplete.
41
42All CRCs used in the following are two-byte ``crc_itu_t(0xffff, ...)``.
43All multi-byte values are little-endian, there is no implicit padding between
44values.
45
46
47SSH Packet Protocol: Definitions
48================================
49
50The fundamental communication unit of the SSH protocol is a frame
51(:c:type:`struct ssh_frame <ssh_frame>`). A frame consists of the following
52fields, packed together and in order:
53
54.. flat-table:: SSH Frame
55 :widths: 1 1 4
56 :header-rows: 1
57
58 * - Field
59 - Type
60 - Description
61
62 * - |TYPE|
63 - |u8|
64 - Type identifier of the frame.
65
66 * - |LEN|
67 - |u16|
68 - Length of the payload associated with the frame.
69
70 * - |SEQ|
71 - |u8|
72 - Sequence ID (see explanation below).
73
74Each frame structure is followed by a CRC over this structure. The CRC over
75the frame structure (|TYPE|, |LEN|, and |SEQ| fields) is placed directly
76after the frame structure and before the payload. The payload is followed by
77its own CRC (over all payload bytes). If the payload is not present (i.e.
78the frame has ``LEN=0``), the CRC of the payload is still present and will
79evaluate to ``0xffff``. The |LEN| field does not include any of the CRCs, it
80equals the number of bytes between the CRC of the frame and the CRC of the
81payload.
82
83Additionally, the following fixed two-byte sequences are used:
84
85.. flat-table:: SSH Byte Sequences
86 :widths: 1 1 4
87 :header-rows: 1
88
89 * - Name
90 - Value
91 - Description
92
93 * - |SYN|
94 - ``[0xAA, 0x55]``
95 - Synchronization bytes.
96
97A message consists of |SYN|, followed by the frame (|TYPE|, |LEN|, |SEQ| and
98CRC) and, if specified in the frame (i.e. ``LEN > 0``), payload bytes,
99followed finally, regardless if the payload is present, the payload CRC. The
100messages corresponding to an exchange are, in part, identified by having the
101same sequence ID (|SEQ|), stored inside the frame (more on this in the next
102section). The sequence ID is a wrapping counter.
103
104A frame can have the following types
105(:c:type:`enum ssh_frame_type <ssh_frame_type>`):
106
107.. flat-table:: SSH Frame Types
108 :widths: 1 1 4
109 :header-rows: 1
110
111 * - Name
112 - Value
113 - Short Description
114
115 * - |NAK|
116 - ``0x04``
117 - Sent on error in previously received message.
118
119 * - |ACK|
120 - ``0x40``
121 - Sent to acknowledge receival of |DATA| frame.
122
123 * - |DATA_SEQ|
124 - ``0x80``
125 - Sent to transfer data. Sequenced.
126
127 * - |DATA_NSQ|
128 - ``0x00``
129 - Same as |DATA_SEQ|, but does not need to be ACKed.
130
131Both |NAK|- and |ACK|-type frames are used to control flow of messages and
132thus do not carry a payload. |DATA_SEQ|- and |DATA_NSQ|-type frames on the
133other hand must carry a payload. The flow sequence and interaction of
134different frame types will be described in more depth in the next section.
135
136
137SSH Packet Protocol: Flow Sequence
138==================================
139
140Each exchange begins with |SYN|, followed by a |DATA_SEQ|- or
141|DATA_NSQ|-type frame, followed by its CRC, payload, and payload CRC. In
142case of a |DATA_NSQ|-type frame, the exchange is then finished. In case of a
143|DATA_SEQ|-type frame, the receiving party has to acknowledge receival of
144the frame by responding with a message containing an |ACK|-type frame with
145the same sequence ID of the |DATA| frame. In other words, the sequence ID of
146the |ACK| frame specifies the |DATA| frame to be acknowledged. In case of an
147error, e.g. an invalid CRC, the receiving party responds with a message
148containing an |NAK|-type frame. As the sequence ID of the previous data
149frame, for which an error is indicated via the |NAK| frame, cannot be relied
150upon, the sequence ID of the |NAK| frame should not be used and is set to
151zero. After receival of an |NAK| frame, the sending party should re-send all
152outstanding (non-ACKed) messages.
153
154Sequence IDs are not synchronized between the two parties, meaning that they
155are managed independently for each party. Identifying the messages
156corresponding to a single exchange thus relies on the sequence ID as well as
157the type of the message, and the context. Specifically, the sequence ID is
158used to associate an ``ACK`` with its ``DATA_SEQ``-type frame, but not
159``DATA_SEQ``- or ``DATA_NSQ``-type frames with other ``DATA``- type frames.
160
161An example exchange might look like this:
162
163::
164
165 tx: -- SYN FRAME(D) CRC(F) PAYLOAD CRC(P) -----------------------------
166 rx: ------------------------------------- SYN FRAME(A) CRC(F) CRC(P) --
167
168where both frames have the same sequence ID (``SEQ``). Here, ``FRAME(D)``
169indicates a |DATA_SEQ|-type frame, ``FRAME(A)`` an ``ACK``-type frame,
170``CRC(F)`` the CRC over the previous frame, ``CRC(P)`` the CRC over the
171previous payload. In case of an error, the exchange would look like this:
172
173::
174
175 tx: -- SYN FRAME(D) CRC(F) PAYLOAD CRC(P) -----------------------------
176 rx: ------------------------------------- SYN FRAME(N) CRC(F) CRC(P) --
177
178upon which the sender should re-send the message. ``FRAME(N)`` indicates an
179|NAK|-type frame. Note that the sequence ID of the |NAK|-type frame is fixed
180to zero. For |DATA_NSQ|-type frames, both exchanges are the same:
181
182::
183
184 tx: -- SYN FRAME(DATA_NSQ) CRC(F) PAYLOAD CRC(P) ----------------------
185 rx: -------------------------------------------------------------------
186
187Here, an error can be detected, but not corrected or indicated to the
188sending party. These exchanges are symmetric, i.e. switching ``rx`` and
189``tx`` results again in a valid exchange. Currently, no longer exchanges are
190known.
191
192
193Commands: Requests, Responses, and Events
194=========================================
195
196Commands are sent as payload inside a data frame. Currently, this is the
197only known payload type of |DATA| frames, with a payload-type value of
198``0x80`` (:c:type:`SSH_PLD_TYPE_CMD <ssh_payload_type>`).
199
200The command-type payload (:c:type:`struct ssh_command <ssh_command>`)
201consists of an eight-byte command structure, followed by optional and
202variable length command data. The length of this optional data is derived
203from the frame payload length given in the corresponding frame, i.e. it is
204``frame.len - sizeof(struct ssh_command)``. The command struct contains the
205following fields, packed together and in order:
206
207.. flat-table:: SSH Command
208 :widths: 1 1 4
209 :header-rows: 1
210
211 * - Field
212 - Type
213 - Description
214
215 * - |TYPE|
216 - |u8|
217 - Type of the payload. For commands always ``0x80``.
218
219 * - |TC|
220 - |u8|
221 - Target category.
222
223 * - |TID|
224 - |u8|
225 - Target ID for commands/messages.
226
227 * - |SID|
228 - |u8|
229 - Source ID for commands/messages.
230
231 * - |IID|
232 - |u8|
233 - Instance ID.
234
235 * - |RQID|
236 - |u16|
237 - Request ID.
238
239 * - |CID|
240 - |u8|
241 - Command ID.
242
243The command struct and data, in general, does not contain any failure
244detection mechanism (e.g. CRCs), this is solely done on the frame level.
245
246Command-type payloads are used by the host to send commands and requests to
247the EC as well as by the EC to send responses and events back to the host.
248We differentiate between requests (sent by the host), responses (sent by the
249EC in response to a request), and events (sent by the EC without a preceding
250request).
251
252Commands and events are uniquely identified by their target category
253(``TC``) and command ID (``CID``). The target category specifies a general
254category for the command (e.g. system in general, vs. battery and AC, vs.
255temperature, and so on), while the command ID specifies the command inside
256that category. Only the combination of |TC| + |CID| is unique. Additionally,
257commands have an instance ID (``IID``), which is used to differentiate
258between different sub-devices. For example ``TC=3`` ``CID=1`` is a
259request to get the temperature on a thermal sensor, where |IID| specifies
260the respective sensor. If the instance ID is not used, it should be set to
261zero. If instance IDs are used, they, in general, start with a value of one,
262whereas zero may be used for instance independent queries, if applicable. A
263response to a request should have the same target category, command ID, and
264instance ID as the corresponding request.
265
266Responses are matched to their corresponding request via the request ID
267(``RQID``) field. This is a 16 bit wrapping counter similar to the sequence
268ID on the frames. Note that the sequence ID of the frames for a
269request-response pair does not match. Only the request ID has to match.
270Frame-protocol wise these are two separate exchanges, and may even be
271separated, e.g. by an event being sent after the request but before the
272response. Not all commands produce a response, and this is not detectable by
273|TC| + |CID|. It is the responsibility of the issuing party to wait for a
274response (or signal this to the communication framework, as is done in
275SAN/ACPI via the ``SNC`` flag).
276
277Events are identified by unique and reserved request IDs. These IDs should
278not be used by the host when sending a new request. They are used on the
279host to, first, detect events and, second, match them with a registered
280event handler. Request IDs for events are chosen by the host and directed to
281the EC when setting up and enabling an event source (via the
282enable-event-source request). The EC then uses the specified request ID for
283events sent from the respective source. Note that an event should still be
284identified by its target category, command ID, and, if applicable, instance
285ID, as a single event source can send multiple different event types. In
286general, however, a single target category should map to a single reserved
287event request ID.
288
289Furthermore, requests, responses, and events have an associated target ID
290(``TID``) and source ID (``SID``). These two fields indicate where a message
291originates from (``SID``) and what the intended target of the message is
292(``TID``). Note that a response to a specific request therefore has the source
293and target IDs swapped when compared to the original request (i.e. the request
294target is the response source and the request source is the response target).
295See (:c:type:`enum ssh_request_id <ssh_request_id>`) for possible values of
296both.
297
298Note that, even though requests and events should be uniquely identifiable by
299target category and command ID alone, the EC may require specific target ID and
300instance ID values to accept a command. A command that is accepted for
301``TID=1``, for example, may not be accepted for ``TID=2`` and vice versa. While
302this may not always hold in reality, you can think of different target/source
303IDs indicating different physical ECs with potentially different feature sets.
304
305
306Limitations and Observations
307============================
308
309The protocol can, in theory, handle up to ``U8_MAX`` frames in parallel,
310with up to ``U16_MAX`` pending requests (neglecting request IDs reserved for
311events). In practice, however, this is more limited. From our testing
312(although via a python and thus a user-space program), it seems that the EC
313can handle up to four requests (mostly) reliably in parallel at a certain
314time. With five or more requests in parallel, consistent discarding of
315commands (ACKed frame but no command response) has been observed. For five
316simultaneous commands, this reproducibly resulted in one command being
317dropped and four commands being handled.
318
319However, it has also been noted that, even with three requests in parallel,
320occasional frame drops happen. Apart from this, with a limit of three
321pending requests, no dropped commands (i.e. command being dropped but frame
322carrying command being ACKed) have been observed. In any case, frames (and
323possibly also commands) should be re-sent by the host if a certain timeout
324is exceeded. This is done by the EC for frames with a timeout of one second,
325up to two re-tries (i.e. three transmissions in total). The limit of
326re-tries also applies to received NAKs, and, in a worst case scenario, can
327lead to entire messages being dropped.
328
329While this also seems to work fine for pending data frames as long as no
330transmission failures occur, implementation and handling of these seems to
331depend on the assumption that there is only one non-acknowledged data frame.
332In particular, the detection of repeated frames relies on the last sequence
333number. This means that, if a frame that has been successfully received by
334the EC is sent again, e.g. due to the host not receiving an |ACK|, the EC
335will only detect this if it has the sequence ID of the last frame received
336by the EC. As an example: Sending two frames with ``SEQ=0`` and ``SEQ=1``
337followed by a repetition of ``SEQ=0`` will not detect the second ``SEQ=0``
338frame as such, and thus execute the command in this frame each time it has
339been received, i.e. twice in this example. Sending ``SEQ=0``, ``SEQ=1`` and
340then repeating ``SEQ=1`` will detect the second ``SEQ=1`` as repetition of
341the first one and ignore it, thus executing the contained command only once.
342
343In conclusion, this suggests a limit of at most one pending un-ACKed frame
344(per party, effectively leading to synchronous communication regarding
345frames) and at most three pending commands. The limit to synchronous frame
346transfers seems to be consistent with behavior observed on Windows.