18d779282SMaximilian Luz.. SPDX-License-Identifier: GPL-2.0+
28d779282SMaximilian Luz
38d779282SMaximilian Luz.. |u8| replace:: :c:type:`u8 <u8>`
48d779282SMaximilian Luz.. |u16| replace:: :c:type:`u16 <u16>`
58d779282SMaximilian Luz.. |TYPE| replace:: ``TYPE``
68d779282SMaximilian Luz.. |LEN| replace:: ``LEN``
78d779282SMaximilian Luz.. |SEQ| replace:: ``SEQ``
88d779282SMaximilian Luz.. |SYN| replace:: ``SYN``
98d779282SMaximilian Luz.. |NAK| replace:: ``NAK``
108d779282SMaximilian Luz.. |ACK| replace:: ``ACK``
118d779282SMaximilian Luz.. |DATA| replace:: ``DATA``
128d779282SMaximilian Luz.. |DATA_SEQ| replace:: ``DATA_SEQ``
138d779282SMaximilian Luz.. |DATA_NSQ| replace:: ``DATA_NSQ``
148d779282SMaximilian Luz.. |TC| replace:: ``TC``
158d779282SMaximilian Luz.. |TID| replace:: ``TID``
16*3f88b459SMaximilian Luz.. |SID| replace:: ``SID``
178d779282SMaximilian Luz.. |IID| replace:: ``IID``
188d779282SMaximilian Luz.. |RQID| replace:: ``RQID``
198d779282SMaximilian Luz.. |CID| replace:: ``CID``
208d779282SMaximilian Luz
218d779282SMaximilian Luz===========================
228d779282SMaximilian LuzSurface Serial Hub Protocol
238d779282SMaximilian Luz===========================
248d779282SMaximilian Luz
258d779282SMaximilian LuzThe Surface Serial Hub (SSH) is the central communication interface for the
268d779282SMaximilian Luzembedded Surface Aggregator Module controller (SAM or EC), found on newer
278d779282SMaximilian LuzSurface generations. We will refer to this protocol and interface as
288d779282SMaximilian LuzSAM-over-SSH, as opposed to SAM-over-HID for the older generations.
298d779282SMaximilian Luz
308d779282SMaximilian LuzOn Surface devices with SAM-over-SSH, SAM is connected to the host via UART
318d779282SMaximilian Luzand defined in ACPI as device with ID ``MSHW0084``. On these devices,
328d779282SMaximilian Luzsignificant functionality is provided via SAM, including access to battery
338d779282SMaximilian Luzand power information and events, thermal read-outs and events, and many
348d779282SMaximilian Luzmore. For Surface Laptops, keyboard input is handled via HID directed
358d779282SMaximilian Luzthrough SAM, on the Surface Laptop 3 and Surface Book 3 this also includes
368d779282SMaximilian Luztouchpad input.
378d779282SMaximilian Luz
388d779282SMaximilian LuzNote that the standard disclaimer for this subsystem also applies to this
398d779282SMaximilian Luzdocument: All of this has been reverse-engineered and may thus be erroneous
408d779282SMaximilian Luzand/or incomplete.
418d779282SMaximilian Luz
428d779282SMaximilian LuzAll CRCs used in the following are two-byte ``crc_ccitt_false(0xffff, ...)``.
438d779282SMaximilian LuzAll multi-byte values are little-endian, there is no implicit padding between
448d779282SMaximilian Luzvalues.
458d779282SMaximilian Luz
468d779282SMaximilian Luz
478d779282SMaximilian LuzSSH Packet Protocol: Definitions
488d779282SMaximilian Luz================================
498d779282SMaximilian Luz
508d779282SMaximilian LuzThe fundamental communication unit of the SSH protocol is a frame
518d779282SMaximilian Luz(:c:type:`struct ssh_frame <ssh_frame>`). A frame consists of the following
528d779282SMaximilian Luzfields, packed together and in order:
538d779282SMaximilian Luz
548d779282SMaximilian Luz.. flat-table:: SSH Frame
558d779282SMaximilian Luz   :widths: 1 1 4
568d779282SMaximilian Luz   :header-rows: 1
578d779282SMaximilian Luz
588d779282SMaximilian Luz   * - Field
598d779282SMaximilian Luz     - Type
608d779282SMaximilian Luz     - Description
618d779282SMaximilian Luz
628d779282SMaximilian Luz   * - |TYPE|
638d779282SMaximilian Luz     - |u8|
648d779282SMaximilian Luz     - Type identifier of the frame.
658d779282SMaximilian Luz
668d779282SMaximilian Luz   * - |LEN|
678d779282SMaximilian Luz     - |u16|
688d779282SMaximilian Luz     - Length of the payload associated with the frame.
698d779282SMaximilian Luz
708d779282SMaximilian Luz   * - |SEQ|
718d779282SMaximilian Luz     - |u8|
728d779282SMaximilian Luz     - Sequence ID (see explanation below).
738d779282SMaximilian Luz
748d779282SMaximilian LuzEach frame structure is followed by a CRC over this structure. The CRC over
758d779282SMaximilian Luzthe frame structure (|TYPE|, |LEN|, and |SEQ| fields) is placed directly
768d779282SMaximilian Luzafter the frame structure and before the payload. The payload is followed by
778d779282SMaximilian Luzits own CRC (over all payload bytes). If the payload is not present (i.e.
788d779282SMaximilian Luzthe frame has ``LEN=0``), the CRC of the payload is still present and will
798d779282SMaximilian Luzevaluate to ``0xffff``. The |LEN| field does not include any of the CRCs, it
808d779282SMaximilian Luzequals the number of bytes between the CRC of the frame and the CRC of the
818d779282SMaximilian Luzpayload.
828d779282SMaximilian Luz
838d779282SMaximilian LuzAdditionally, the following fixed two-byte sequences are used:
848d779282SMaximilian Luz
858d779282SMaximilian Luz.. flat-table:: SSH Byte Sequences
868d779282SMaximilian Luz   :widths: 1 1 4
878d779282SMaximilian Luz   :header-rows: 1
888d779282SMaximilian Luz
898d779282SMaximilian Luz   * - Name
908d779282SMaximilian Luz     - Value
918d779282SMaximilian Luz     - Description
928d779282SMaximilian Luz
938d779282SMaximilian Luz   * - |SYN|
948d779282SMaximilian Luz     - ``[0xAA, 0x55]``
958d779282SMaximilian Luz     - Synchronization bytes.
968d779282SMaximilian Luz
978d779282SMaximilian LuzA message consists of |SYN|, followed by the frame (|TYPE|, |LEN|, |SEQ| and
988d779282SMaximilian LuzCRC) and, if specified in the frame (i.e. ``LEN > 0``), payload bytes,
998d779282SMaximilian Luzfollowed finally, regardless if the payload is present, the payload CRC. The
1008d779282SMaximilian Luzmessages corresponding to an exchange are, in part, identified by having the
1018d779282SMaximilian Luzsame sequence ID (|SEQ|), stored inside the frame (more on this in the next
1028d779282SMaximilian Luzsection). The sequence ID is a wrapping counter.
1038d779282SMaximilian Luz
1048d779282SMaximilian LuzA frame can have the following types
1058d779282SMaximilian Luz(:c:type:`enum ssh_frame_type <ssh_frame_type>`):
1068d779282SMaximilian Luz
1078d779282SMaximilian Luz.. flat-table:: SSH Frame Types
1088d779282SMaximilian Luz   :widths: 1 1 4
1098d779282SMaximilian Luz   :header-rows: 1
1108d779282SMaximilian Luz
1118d779282SMaximilian Luz   * - Name
1128d779282SMaximilian Luz     - Value
1138d779282SMaximilian Luz     - Short Description
1148d779282SMaximilian Luz
1158d779282SMaximilian Luz   * - |NAK|
1168d779282SMaximilian Luz     - ``0x04``
1178d779282SMaximilian Luz     - Sent on error in previously received message.
1188d779282SMaximilian Luz
1198d779282SMaximilian Luz   * - |ACK|
1208d779282SMaximilian Luz     - ``0x40``
1218d779282SMaximilian Luz     - Sent to acknowledge receival of |DATA| frame.
1228d779282SMaximilian Luz
1238d779282SMaximilian Luz   * - |DATA_SEQ|
1248d779282SMaximilian Luz     - ``0x80``
1258d779282SMaximilian Luz     - Sent to transfer data. Sequenced.
1268d779282SMaximilian Luz
1278d779282SMaximilian Luz   * - |DATA_NSQ|
1288d779282SMaximilian Luz     - ``0x00``
1298d779282SMaximilian Luz     - Same as |DATA_SEQ|, but does not need to be ACKed.
1308d779282SMaximilian Luz
1318d779282SMaximilian LuzBoth |NAK|- and |ACK|-type frames are used to control flow of messages and
1328d779282SMaximilian Luzthus do not carry a payload. |DATA_SEQ|- and |DATA_NSQ|-type frames on the
1338d779282SMaximilian Luzother hand must carry a payload. The flow sequence and interaction of
1348d779282SMaximilian Luzdifferent frame types will be described in more depth in the next section.
1358d779282SMaximilian Luz
1368d779282SMaximilian Luz
1378d779282SMaximilian LuzSSH Packet Protocol: Flow Sequence
1388d779282SMaximilian Luz==================================
1398d779282SMaximilian Luz
1408d779282SMaximilian LuzEach exchange begins with |SYN|, followed by a |DATA_SEQ|- or
1418d779282SMaximilian Luz|DATA_NSQ|-type frame, followed by its CRC, payload, and payload CRC. In
1428d779282SMaximilian Luzcase of a |DATA_NSQ|-type frame, the exchange is then finished. In case of a
1438d779282SMaximilian Luz|DATA_SEQ|-type frame, the receiving party has to acknowledge receival of
1448d779282SMaximilian Luzthe frame by responding with a message containing an |ACK|-type frame with
1458d779282SMaximilian Luzthe same sequence ID of the |DATA| frame. In other words, the sequence ID of
1468d779282SMaximilian Luzthe |ACK| frame specifies the |DATA| frame to be acknowledged. In case of an
1478d779282SMaximilian Luzerror, e.g. an invalid CRC, the receiving party responds with a message
1488d779282SMaximilian Luzcontaining an |NAK|-type frame. As the sequence ID of the previous data
1498d779282SMaximilian Luzframe, for which an error is indicated via the |NAK| frame, cannot be relied
1508d779282SMaximilian Luzupon, the sequence ID of the |NAK| frame should not be used and is set to
1518d779282SMaximilian Luzzero. After receival of an |NAK| frame, the sending party should re-send all
1528d779282SMaximilian Luzoutstanding (non-ACKed) messages.
1538d779282SMaximilian Luz
1548d779282SMaximilian LuzSequence IDs are not synchronized between the two parties, meaning that they
1558d779282SMaximilian Luzare managed independently for each party. Identifying the messages
1568d779282SMaximilian Luzcorresponding to a single exchange thus relies on the sequence ID as well as
1578d779282SMaximilian Luzthe type of the message, and the context. Specifically, the sequence ID is
1588d779282SMaximilian Luzused to associate an ``ACK`` with its ``DATA_SEQ``-type frame, but not
1598d779282SMaximilian Luz``DATA_SEQ``- or ``DATA_NSQ``-type frames with other ``DATA``- type frames.
1608d779282SMaximilian Luz
1618d779282SMaximilian LuzAn example exchange might look like this:
1628d779282SMaximilian Luz
1638d779282SMaximilian Luz::
1648d779282SMaximilian Luz
1658d779282SMaximilian Luz    tx: -- SYN FRAME(D) CRC(F) PAYLOAD CRC(P) -----------------------------
1668d779282SMaximilian Luz    rx: ------------------------------------- SYN FRAME(A) CRC(F) CRC(P) --
1678d779282SMaximilian Luz
1688d779282SMaximilian Luzwhere both frames have the same sequence ID (``SEQ``). Here, ``FRAME(D)``
1698d779282SMaximilian Luzindicates a |DATA_SEQ|-type frame, ``FRAME(A)`` an ``ACK``-type frame,
1708d779282SMaximilian Luz``CRC(F)`` the CRC over the previous frame, ``CRC(P)`` the CRC over the
1718d779282SMaximilian Luzprevious payload. In case of an error, the exchange would look like this:
1728d779282SMaximilian Luz
1738d779282SMaximilian Luz::
1748d779282SMaximilian Luz
1758d779282SMaximilian Luz    tx: -- SYN FRAME(D) CRC(F) PAYLOAD CRC(P) -----------------------------
1768d779282SMaximilian Luz    rx: ------------------------------------- SYN FRAME(N) CRC(F) CRC(P) --
1778d779282SMaximilian Luz
1788d779282SMaximilian Luzupon which the sender should re-send the message. ``FRAME(N)`` indicates an
1798d779282SMaximilian Luz|NAK|-type frame. Note that the sequence ID of the |NAK|-type frame is fixed
1808d779282SMaximilian Luzto zero. For |DATA_NSQ|-type frames, both exchanges are the same:
1818d779282SMaximilian Luz
1828d779282SMaximilian Luz::
1838d779282SMaximilian Luz
1848d779282SMaximilian Luz    tx: -- SYN FRAME(DATA_NSQ) CRC(F) PAYLOAD CRC(P) ----------------------
1858d779282SMaximilian Luz    rx: -------------------------------------------------------------------
1868d779282SMaximilian Luz
1878d779282SMaximilian LuzHere, an error can be detected, but not corrected or indicated to the
1888d779282SMaximilian Luzsending party. These exchanges are symmetric, i.e. switching ``rx`` and
1898d779282SMaximilian Luz``tx`` results again in a valid exchange. Currently, no longer exchanges are
1908d779282SMaximilian Luzknown.
1918d779282SMaximilian Luz
1928d779282SMaximilian Luz
1938d779282SMaximilian LuzCommands: Requests, Responses, and Events
1948d779282SMaximilian Luz=========================================
1958d779282SMaximilian Luz
1968d779282SMaximilian LuzCommands are sent as payload inside a data frame. Currently, this is the
1978d779282SMaximilian Luzonly known payload type of |DATA| frames, with a payload-type value of
1988d779282SMaximilian Luz``0x80`` (:c:type:`SSH_PLD_TYPE_CMD <ssh_payload_type>`).
1998d779282SMaximilian Luz
2008d779282SMaximilian LuzThe command-type payload (:c:type:`struct ssh_command <ssh_command>`)
2018d779282SMaximilian Luzconsists of an eight-byte command structure, followed by optional and
2028d779282SMaximilian Luzvariable length command data. The length of this optional data is derived
2038d779282SMaximilian Luzfrom the frame payload length given in the corresponding frame, i.e. it is
2048d779282SMaximilian Luz``frame.len - sizeof(struct ssh_command)``. The command struct contains the
2058d779282SMaximilian Luzfollowing fields, packed together and in order:
2068d779282SMaximilian Luz
2078d779282SMaximilian Luz.. flat-table:: SSH Command
2088d779282SMaximilian Luz   :widths: 1 1 4
2098d779282SMaximilian Luz   :header-rows: 1
2108d779282SMaximilian Luz
2118d779282SMaximilian Luz   * - Field
2128d779282SMaximilian Luz     - Type
2138d779282SMaximilian Luz     - Description
2148d779282SMaximilian Luz
2158d779282SMaximilian Luz   * - |TYPE|
2168d779282SMaximilian Luz     - |u8|
2178d779282SMaximilian Luz     - Type of the payload. For commands always ``0x80``.
2188d779282SMaximilian Luz
2198d779282SMaximilian Luz   * - |TC|
2208d779282SMaximilian Luz     - |u8|
2218d779282SMaximilian Luz     - Target category.
2228d779282SMaximilian Luz
223*3f88b459SMaximilian Luz   * - |TID|
2248d779282SMaximilian Luz     - |u8|
225*3f88b459SMaximilian Luz     - Target ID for commands/messages.
2268d779282SMaximilian Luz
227*3f88b459SMaximilian Luz   * - |SID|
2288d779282SMaximilian Luz     - |u8|
229*3f88b459SMaximilian Luz     - Source ID for commands/messages.
2308d779282SMaximilian Luz
2318d779282SMaximilian Luz   * - |IID|
2328d779282SMaximilian Luz     - |u8|
2338d779282SMaximilian Luz     - Instance ID.
2348d779282SMaximilian Luz
2358d779282SMaximilian Luz   * - |RQID|
2368d779282SMaximilian Luz     - |u16|
2378d779282SMaximilian Luz     - Request ID.
2388d779282SMaximilian Luz
2398d779282SMaximilian Luz   * - |CID|
2408d779282SMaximilian Luz     - |u8|
2418d779282SMaximilian Luz     - Command ID.
2428d779282SMaximilian Luz
2438d779282SMaximilian LuzThe command struct and data, in general, does not contain any failure
2448d779282SMaximilian Luzdetection mechanism (e.g. CRCs), this is solely done on the frame level.
2458d779282SMaximilian Luz
2468d779282SMaximilian LuzCommand-type payloads are used by the host to send commands and requests to
2478d779282SMaximilian Luzthe EC as well as by the EC to send responses and events back to the host.
2488d779282SMaximilian LuzWe differentiate between requests (sent by the host), responses (sent by the
2498d779282SMaximilian LuzEC in response to a request), and events (sent by the EC without a preceding
2508d779282SMaximilian Luzrequest).
2518d779282SMaximilian Luz
2528d779282SMaximilian LuzCommands and events are uniquely identified by their target category
2538d779282SMaximilian Luz(``TC``) and command ID (``CID``). The target category specifies a general
2548d779282SMaximilian Luzcategory for the command (e.g. system in general, vs. battery and AC, vs.
2558d779282SMaximilian Luztemperature, and so on), while the command ID specifies the command inside
2568d779282SMaximilian Luzthat category. Only the combination of |TC| + |CID| is unique. Additionally,
2578d779282SMaximilian Luzcommands have an instance ID (``IID``), which is used to differentiate
2588d779282SMaximilian Luzbetween different sub-devices. For example ``TC=3`` ``CID=1`` is a
2598d779282SMaximilian Luzrequest to get the temperature on a thermal sensor, where |IID| specifies
2608d779282SMaximilian Luzthe respective sensor. If the instance ID is not used, it should be set to
2618d779282SMaximilian Luzzero. If instance IDs are used, they, in general, start with a value of one,
2628d779282SMaximilian Luzwhereas zero may be used for instance independent queries, if applicable. A
2638d779282SMaximilian Luzresponse to a request should have the same target category, command ID, and
2648d779282SMaximilian Luzinstance ID as the corresponding request.
2658d779282SMaximilian Luz
2668d779282SMaximilian LuzResponses are matched to their corresponding request via the request ID
2678d779282SMaximilian Luz(``RQID``) field. This is a 16 bit wrapping counter similar to the sequence
2688d779282SMaximilian LuzID on the frames. Note that the sequence ID of the frames for a
2698d779282SMaximilian Luzrequest-response pair does not match. Only the request ID has to match.
2708d779282SMaximilian LuzFrame-protocol wise these are two separate exchanges, and may even be
2718d779282SMaximilian Luzseparated, e.g. by an event being sent after the request but before the
2728d779282SMaximilian Luzresponse. Not all commands produce a response, and this is not detectable by
2738d779282SMaximilian Luz|TC| + |CID|. It is the responsibility of the issuing party to wait for a
2748d779282SMaximilian Luzresponse (or signal this to the communication framework, as is done in
2758d779282SMaximilian LuzSAN/ACPI via the ``SNC`` flag).
2768d779282SMaximilian Luz
2778d779282SMaximilian LuzEvents are identified by unique and reserved request IDs. These IDs should
2788d779282SMaximilian Luznot be used by the host when sending a new request. They are used on the
2798d779282SMaximilian Luzhost to, first, detect events and, second, match them with a registered
2808d779282SMaximilian Luzevent handler. Request IDs for events are chosen by the host and directed to
2818d779282SMaximilian Luzthe EC when setting up and enabling an event source (via the
2828d779282SMaximilian Luzenable-event-source request). The EC then uses the specified request ID for
2838d779282SMaximilian Luzevents sent from the respective source. Note that an event should still be
2848d779282SMaximilian Luzidentified by its target category, command ID, and, if applicable, instance
2858d779282SMaximilian LuzID, as a single event source can send multiple different event types. In
2868d779282SMaximilian Luzgeneral, however, a single target category should map to a single reserved
2878d779282SMaximilian Luzevent request ID.
2888d779282SMaximilian Luz
2898d779282SMaximilian LuzFurthermore, requests, responses, and events have an associated target ID
290*3f88b459SMaximilian Luz(``TID``) and source ID (``SID``). These two fields indicate where a message
291*3f88b459SMaximilian Luzoriginates from (``SID``) and what the intended target of the message is
292*3f88b459SMaximilian Luz(``TID``). Note that a response to a specific request therefore has the source
293*3f88b459SMaximilian Luzand target IDs swapped when compared to the original request (i.e. the request
294*3f88b459SMaximilian Luztarget is the response source and the request source is the response target).
295*3f88b459SMaximilian LuzSee (:c:type:`enum ssh_request_id <ssh_request_id>`) for possible values of
296*3f88b459SMaximilian Luzboth.
2978d779282SMaximilian Luz
298*3f88b459SMaximilian LuzNote that, even though requests and events should be uniquely identifiable by
299*3f88b459SMaximilian Luztarget category and command ID alone, the EC may require specific target ID and
300*3f88b459SMaximilian Luzinstance ID values to accept a command. A command that is accepted for
301*3f88b459SMaximilian Luz``TID=1``, for example, may not be accepted for ``TID=2`` and vice versa. While
302*3f88b459SMaximilian Luzthis may not always hold in reality, you can think of different target/source
303*3f88b459SMaximilian LuzIDs indicating different physical ECs with potentially different feature sets.
3048d779282SMaximilian Luz
3058d779282SMaximilian Luz
3068d779282SMaximilian LuzLimitations and Observations
3078d779282SMaximilian Luz============================
3088d779282SMaximilian Luz
3098d779282SMaximilian LuzThe protocol can, in theory, handle up to ``U8_MAX`` frames in parallel,
3108d779282SMaximilian Luzwith up to ``U16_MAX`` pending requests (neglecting request IDs reserved for
3118d779282SMaximilian Luzevents). In practice, however, this is more limited. From our testing
3128d779282SMaximilian Luz(although via a python and thus a user-space program), it seems that the EC
3138d779282SMaximilian Luzcan handle up to four requests (mostly) reliably in parallel at a certain
3148d779282SMaximilian Luztime. With five or more requests in parallel, consistent discarding of
3158d779282SMaximilian Luzcommands (ACKed frame but no command response) has been observed. For five
3168d779282SMaximilian Luzsimultaneous commands, this reproducibly resulted in one command being
3178d779282SMaximilian Luzdropped and four commands being handled.
3188d779282SMaximilian Luz
3198d779282SMaximilian LuzHowever, it has also been noted that, even with three requests in parallel,
3208d779282SMaximilian Luzoccasional frame drops happen. Apart from this, with a limit of three
3218d779282SMaximilian Luzpending requests, no dropped commands (i.e. command being dropped but frame
3228d779282SMaximilian Luzcarrying command being ACKed) have been observed. In any case, frames (and
3238d779282SMaximilian Luzpossibly also commands) should be re-sent by the host if a certain timeout
3248d779282SMaximilian Luzis exceeded. This is done by the EC for frames with a timeout of one second,
3258d779282SMaximilian Luzup to two re-tries (i.e. three transmissions in total). The limit of
3268d779282SMaximilian Luzre-tries also applies to received NAKs, and, in a worst case scenario, can
3278d779282SMaximilian Luzlead to entire messages being dropped.
3288d779282SMaximilian Luz
3298d779282SMaximilian LuzWhile this also seems to work fine for pending data frames as long as no
3308d779282SMaximilian Luztransmission failures occur, implementation and handling of these seems to
3318d779282SMaximilian Luzdepend on the assumption that there is only one non-acknowledged data frame.
3328d779282SMaximilian LuzIn particular, the detection of repeated frames relies on the last sequence
3338d779282SMaximilian Luznumber. This means that, if a frame that has been successfully received by
3348d779282SMaximilian Luzthe EC is sent again, e.g. due to the host not receiving an |ACK|, the EC
3358d779282SMaximilian Luzwill only detect this if it has the sequence ID of the last frame received
3368d779282SMaximilian Luzby the EC. As an example: Sending two frames with ``SEQ=0`` and ``SEQ=1``
3378d779282SMaximilian Luzfollowed by a repetition of ``SEQ=0`` will not detect the second ``SEQ=0``
3388d779282SMaximilian Luzframe as such, and thus execute the command in this frame each time it has
3398d779282SMaximilian Luzbeen received, i.e. twice in this example. Sending ``SEQ=0``, ``SEQ=1`` and
3408d779282SMaximilian Luzthen repeating ``SEQ=1`` will detect the second ``SEQ=1`` as repetition of
3418d779282SMaximilian Luzthe first one and ignore it, thus executing the contained command only once.
3428d779282SMaximilian Luz
3438d779282SMaximilian LuzIn conclusion, this suggests a limit of at most one pending un-ACKed frame
3448d779282SMaximilian Luz(per party, effectively leading to synchronous communication regarding
3458d779282SMaximilian Luzframes) and at most three pending commands. The limit to synchronous frame
3468d779282SMaximilian Luztransfers seems to be consistent with behavior observed on Windows.
347