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 807852fe3aSRandy Dunlapequals 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