162306a36Sopenharmony_ci.. SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci.. |u8| replace:: :c:type:`u8 <u8>` 462306a36Sopenharmony_ci.. |u16| replace:: :c:type:`u16 <u16>` 562306a36Sopenharmony_ci.. |TYPE| replace:: ``TYPE`` 662306a36Sopenharmony_ci.. |LEN| replace:: ``LEN`` 762306a36Sopenharmony_ci.. |SEQ| replace:: ``SEQ`` 862306a36Sopenharmony_ci.. |SYN| replace:: ``SYN`` 962306a36Sopenharmony_ci.. |NAK| replace:: ``NAK`` 1062306a36Sopenharmony_ci.. |ACK| replace:: ``ACK`` 1162306a36Sopenharmony_ci.. |DATA| replace:: ``DATA`` 1262306a36Sopenharmony_ci.. |DATA_SEQ| replace:: ``DATA_SEQ`` 1362306a36Sopenharmony_ci.. |DATA_NSQ| replace:: ``DATA_NSQ`` 1462306a36Sopenharmony_ci.. |TC| replace:: ``TC`` 1562306a36Sopenharmony_ci.. |TID| replace:: ``TID`` 1662306a36Sopenharmony_ci.. |SID| replace:: ``SID`` 1762306a36Sopenharmony_ci.. |IID| replace:: ``IID`` 1862306a36Sopenharmony_ci.. |RQID| replace:: ``RQID`` 1962306a36Sopenharmony_ci.. |CID| replace:: ``CID`` 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci=========================== 2262306a36Sopenharmony_ciSurface Serial Hub Protocol 2362306a36Sopenharmony_ci=========================== 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ciThe Surface Serial Hub (SSH) is the central communication interface for the 2662306a36Sopenharmony_ciembedded Surface Aggregator Module controller (SAM or EC), found on newer 2762306a36Sopenharmony_ciSurface generations. We will refer to this protocol and interface as 2862306a36Sopenharmony_ciSAM-over-SSH, as opposed to SAM-over-HID for the older generations. 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ciOn Surface devices with SAM-over-SSH, SAM is connected to the host via UART 3162306a36Sopenharmony_ciand defined in ACPI as device with ID ``MSHW0084``. On these devices, 3262306a36Sopenharmony_cisignificant functionality is provided via SAM, including access to battery 3362306a36Sopenharmony_ciand power information and events, thermal read-outs and events, and many 3462306a36Sopenharmony_cimore. For Surface Laptops, keyboard input is handled via HID directed 3562306a36Sopenharmony_cithrough SAM, on the Surface Laptop 3 and Surface Book 3 this also includes 3662306a36Sopenharmony_citouchpad input. 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ciNote that the standard disclaimer for this subsystem also applies to this 3962306a36Sopenharmony_cidocument: All of this has been reverse-engineered and may thus be erroneous 4062306a36Sopenharmony_ciand/or incomplete. 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ciAll CRCs used in the following are two-byte ``crc_ccitt_false(0xffff, ...)``. 4362306a36Sopenharmony_ciAll multi-byte values are little-endian, there is no implicit padding between 4462306a36Sopenharmony_civalues. 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ciSSH Packet Protocol: Definitions 4862306a36Sopenharmony_ci================================ 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ciThe fundamental communication unit of the SSH protocol is a frame 5162306a36Sopenharmony_ci(:c:type:`struct ssh_frame <ssh_frame>`). A frame consists of the following 5262306a36Sopenharmony_cifields, packed together and in order: 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci.. flat-table:: SSH Frame 5562306a36Sopenharmony_ci :widths: 1 1 4 5662306a36Sopenharmony_ci :header-rows: 1 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci * - Field 5962306a36Sopenharmony_ci - Type 6062306a36Sopenharmony_ci - Description 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci * - |TYPE| 6362306a36Sopenharmony_ci - |u8| 6462306a36Sopenharmony_ci - Type identifier of the frame. 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci * - |LEN| 6762306a36Sopenharmony_ci - |u16| 6862306a36Sopenharmony_ci - Length of the payload associated with the frame. 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci * - |SEQ| 7162306a36Sopenharmony_ci - |u8| 7262306a36Sopenharmony_ci - Sequence ID (see explanation below). 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ciEach frame structure is followed by a CRC over this structure. The CRC over 7562306a36Sopenharmony_cithe frame structure (|TYPE|, |LEN|, and |SEQ| fields) is placed directly 7662306a36Sopenharmony_ciafter the frame structure and before the payload. The payload is followed by 7762306a36Sopenharmony_ciits own CRC (over all payload bytes). If the payload is not present (i.e. 7862306a36Sopenharmony_cithe frame has ``LEN=0``), the CRC of the payload is still present and will 7962306a36Sopenharmony_cievaluate to ``0xffff``. The |LEN| field does not include any of the CRCs, it 8062306a36Sopenharmony_ciequals the number of bytes between the CRC of the frame and the CRC of the 8162306a36Sopenharmony_cipayload. 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ciAdditionally, the following fixed two-byte sequences are used: 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci.. flat-table:: SSH Byte Sequences 8662306a36Sopenharmony_ci :widths: 1 1 4 8762306a36Sopenharmony_ci :header-rows: 1 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci * - Name 9062306a36Sopenharmony_ci - Value 9162306a36Sopenharmony_ci - Description 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci * - |SYN| 9462306a36Sopenharmony_ci - ``[0xAA, 0x55]`` 9562306a36Sopenharmony_ci - Synchronization bytes. 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ciA message consists of |SYN|, followed by the frame (|TYPE|, |LEN|, |SEQ| and 9862306a36Sopenharmony_ciCRC) and, if specified in the frame (i.e. ``LEN > 0``), payload bytes, 9962306a36Sopenharmony_cifollowed finally, regardless if the payload is present, the payload CRC. The 10062306a36Sopenharmony_cimessages corresponding to an exchange are, in part, identified by having the 10162306a36Sopenharmony_cisame sequence ID (|SEQ|), stored inside the frame (more on this in the next 10262306a36Sopenharmony_cisection). The sequence ID is a wrapping counter. 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ciA frame can have the following types 10562306a36Sopenharmony_ci(:c:type:`enum ssh_frame_type <ssh_frame_type>`): 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci.. flat-table:: SSH Frame Types 10862306a36Sopenharmony_ci :widths: 1 1 4 10962306a36Sopenharmony_ci :header-rows: 1 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci * - Name 11262306a36Sopenharmony_ci - Value 11362306a36Sopenharmony_ci - Short Description 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci * - |NAK| 11662306a36Sopenharmony_ci - ``0x04`` 11762306a36Sopenharmony_ci - Sent on error in previously received message. 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci * - |ACK| 12062306a36Sopenharmony_ci - ``0x40`` 12162306a36Sopenharmony_ci - Sent to acknowledge receival of |DATA| frame. 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci * - |DATA_SEQ| 12462306a36Sopenharmony_ci - ``0x80`` 12562306a36Sopenharmony_ci - Sent to transfer data. Sequenced. 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci * - |DATA_NSQ| 12862306a36Sopenharmony_ci - ``0x00`` 12962306a36Sopenharmony_ci - Same as |DATA_SEQ|, but does not need to be ACKed. 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ciBoth |NAK|- and |ACK|-type frames are used to control flow of messages and 13262306a36Sopenharmony_cithus do not carry a payload. |DATA_SEQ|- and |DATA_NSQ|-type frames on the 13362306a36Sopenharmony_ciother hand must carry a payload. The flow sequence and interaction of 13462306a36Sopenharmony_cidifferent frame types will be described in more depth in the next section. 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ciSSH Packet Protocol: Flow Sequence 13862306a36Sopenharmony_ci================================== 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ciEach exchange begins with |SYN|, followed by a |DATA_SEQ|- or 14162306a36Sopenharmony_ci|DATA_NSQ|-type frame, followed by its CRC, payload, and payload CRC. In 14262306a36Sopenharmony_cicase of a |DATA_NSQ|-type frame, the exchange is then finished. In case of a 14362306a36Sopenharmony_ci|DATA_SEQ|-type frame, the receiving party has to acknowledge receival of 14462306a36Sopenharmony_cithe frame by responding with a message containing an |ACK|-type frame with 14562306a36Sopenharmony_cithe same sequence ID of the |DATA| frame. In other words, the sequence ID of 14662306a36Sopenharmony_cithe |ACK| frame specifies the |DATA| frame to be acknowledged. In case of an 14762306a36Sopenharmony_cierror, e.g. an invalid CRC, the receiving party responds with a message 14862306a36Sopenharmony_cicontaining an |NAK|-type frame. As the sequence ID of the previous data 14962306a36Sopenharmony_ciframe, for which an error is indicated via the |NAK| frame, cannot be relied 15062306a36Sopenharmony_ciupon, the sequence ID of the |NAK| frame should not be used and is set to 15162306a36Sopenharmony_cizero. After receival of an |NAK| frame, the sending party should re-send all 15262306a36Sopenharmony_cioutstanding (non-ACKed) messages. 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ciSequence IDs are not synchronized between the two parties, meaning that they 15562306a36Sopenharmony_ciare managed independently for each party. Identifying the messages 15662306a36Sopenharmony_cicorresponding to a single exchange thus relies on the sequence ID as well as 15762306a36Sopenharmony_cithe type of the message, and the context. Specifically, the sequence ID is 15862306a36Sopenharmony_ciused to associate an ``ACK`` with its ``DATA_SEQ``-type frame, but not 15962306a36Sopenharmony_ci``DATA_SEQ``- or ``DATA_NSQ``-type frames with other ``DATA``- type frames. 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ciAn example exchange might look like this: 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci:: 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci tx: -- SYN FRAME(D) CRC(F) PAYLOAD CRC(P) ----------------------------- 16662306a36Sopenharmony_ci rx: ------------------------------------- SYN FRAME(A) CRC(F) CRC(P) -- 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ciwhere both frames have the same sequence ID (``SEQ``). Here, ``FRAME(D)`` 16962306a36Sopenharmony_ciindicates a |DATA_SEQ|-type frame, ``FRAME(A)`` an ``ACK``-type frame, 17062306a36Sopenharmony_ci``CRC(F)`` the CRC over the previous frame, ``CRC(P)`` the CRC over the 17162306a36Sopenharmony_ciprevious payload. In case of an error, the exchange would look like this: 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci:: 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci tx: -- SYN FRAME(D) CRC(F) PAYLOAD CRC(P) ----------------------------- 17662306a36Sopenharmony_ci rx: ------------------------------------- SYN FRAME(N) CRC(F) CRC(P) -- 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ciupon which the sender should re-send the message. ``FRAME(N)`` indicates an 17962306a36Sopenharmony_ci|NAK|-type frame. Note that the sequence ID of the |NAK|-type frame is fixed 18062306a36Sopenharmony_cito zero. For |DATA_NSQ|-type frames, both exchanges are the same: 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci:: 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci tx: -- SYN FRAME(DATA_NSQ) CRC(F) PAYLOAD CRC(P) ---------------------- 18562306a36Sopenharmony_ci rx: ------------------------------------------------------------------- 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ciHere, an error can be detected, but not corrected or indicated to the 18862306a36Sopenharmony_cisending party. These exchanges are symmetric, i.e. switching ``rx`` and 18962306a36Sopenharmony_ci``tx`` results again in a valid exchange. Currently, no longer exchanges are 19062306a36Sopenharmony_ciknown. 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ciCommands: Requests, Responses, and Events 19462306a36Sopenharmony_ci========================================= 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ciCommands are sent as payload inside a data frame. Currently, this is the 19762306a36Sopenharmony_cionly known payload type of |DATA| frames, with a payload-type value of 19862306a36Sopenharmony_ci``0x80`` (:c:type:`SSH_PLD_TYPE_CMD <ssh_payload_type>`). 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ciThe command-type payload (:c:type:`struct ssh_command <ssh_command>`) 20162306a36Sopenharmony_ciconsists of an eight-byte command structure, followed by optional and 20262306a36Sopenharmony_civariable length command data. The length of this optional data is derived 20362306a36Sopenharmony_cifrom the frame payload length given in the corresponding frame, i.e. it is 20462306a36Sopenharmony_ci``frame.len - sizeof(struct ssh_command)``. The command struct contains the 20562306a36Sopenharmony_cifollowing fields, packed together and in order: 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci.. flat-table:: SSH Command 20862306a36Sopenharmony_ci :widths: 1 1 4 20962306a36Sopenharmony_ci :header-rows: 1 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci * - Field 21262306a36Sopenharmony_ci - Type 21362306a36Sopenharmony_ci - Description 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci * - |TYPE| 21662306a36Sopenharmony_ci - |u8| 21762306a36Sopenharmony_ci - Type of the payload. For commands always ``0x80``. 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci * - |TC| 22062306a36Sopenharmony_ci - |u8| 22162306a36Sopenharmony_ci - Target category. 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci * - |TID| 22462306a36Sopenharmony_ci - |u8| 22562306a36Sopenharmony_ci - Target ID for commands/messages. 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci * - |SID| 22862306a36Sopenharmony_ci - |u8| 22962306a36Sopenharmony_ci - Source ID for commands/messages. 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci * - |IID| 23262306a36Sopenharmony_ci - |u8| 23362306a36Sopenharmony_ci - Instance ID. 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci * - |RQID| 23662306a36Sopenharmony_ci - |u16| 23762306a36Sopenharmony_ci - Request ID. 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci * - |CID| 24062306a36Sopenharmony_ci - |u8| 24162306a36Sopenharmony_ci - Command ID. 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ciThe command struct and data, in general, does not contain any failure 24462306a36Sopenharmony_cidetection mechanism (e.g. CRCs), this is solely done on the frame level. 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ciCommand-type payloads are used by the host to send commands and requests to 24762306a36Sopenharmony_cithe EC as well as by the EC to send responses and events back to the host. 24862306a36Sopenharmony_ciWe differentiate between requests (sent by the host), responses (sent by the 24962306a36Sopenharmony_ciEC in response to a request), and events (sent by the EC without a preceding 25062306a36Sopenharmony_cirequest). 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ciCommands and events are uniquely identified by their target category 25362306a36Sopenharmony_ci(``TC``) and command ID (``CID``). The target category specifies a general 25462306a36Sopenharmony_cicategory for the command (e.g. system in general, vs. battery and AC, vs. 25562306a36Sopenharmony_citemperature, and so on), while the command ID specifies the command inside 25662306a36Sopenharmony_cithat category. Only the combination of |TC| + |CID| is unique. Additionally, 25762306a36Sopenharmony_cicommands have an instance ID (``IID``), which is used to differentiate 25862306a36Sopenharmony_cibetween different sub-devices. For example ``TC=3`` ``CID=1`` is a 25962306a36Sopenharmony_cirequest to get the temperature on a thermal sensor, where |IID| specifies 26062306a36Sopenharmony_cithe respective sensor. If the instance ID is not used, it should be set to 26162306a36Sopenharmony_cizero. If instance IDs are used, they, in general, start with a value of one, 26262306a36Sopenharmony_ciwhereas zero may be used for instance independent queries, if applicable. A 26362306a36Sopenharmony_ciresponse to a request should have the same target category, command ID, and 26462306a36Sopenharmony_ciinstance ID as the corresponding request. 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ciResponses are matched to their corresponding request via the request ID 26762306a36Sopenharmony_ci(``RQID``) field. This is a 16 bit wrapping counter similar to the sequence 26862306a36Sopenharmony_ciID on the frames. Note that the sequence ID of the frames for a 26962306a36Sopenharmony_cirequest-response pair does not match. Only the request ID has to match. 27062306a36Sopenharmony_ciFrame-protocol wise these are two separate exchanges, and may even be 27162306a36Sopenharmony_ciseparated, e.g. by an event being sent after the request but before the 27262306a36Sopenharmony_ciresponse. Not all commands produce a response, and this is not detectable by 27362306a36Sopenharmony_ci|TC| + |CID|. It is the responsibility of the issuing party to wait for a 27462306a36Sopenharmony_ciresponse (or signal this to the communication framework, as is done in 27562306a36Sopenharmony_ciSAN/ACPI via the ``SNC`` flag). 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ciEvents are identified by unique and reserved request IDs. These IDs should 27862306a36Sopenharmony_cinot be used by the host when sending a new request. They are used on the 27962306a36Sopenharmony_cihost to, first, detect events and, second, match them with a registered 28062306a36Sopenharmony_cievent handler. Request IDs for events are chosen by the host and directed to 28162306a36Sopenharmony_cithe EC when setting up and enabling an event source (via the 28262306a36Sopenharmony_cienable-event-source request). The EC then uses the specified request ID for 28362306a36Sopenharmony_cievents sent from the respective source. Note that an event should still be 28462306a36Sopenharmony_ciidentified by its target category, command ID, and, if applicable, instance 28562306a36Sopenharmony_ciID, as a single event source can send multiple different event types. In 28662306a36Sopenharmony_cigeneral, however, a single target category should map to a single reserved 28762306a36Sopenharmony_cievent request ID. 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ciFurthermore, requests, responses, and events have an associated target ID 29062306a36Sopenharmony_ci(``TID``) and source ID (``SID``). These two fields indicate where a message 29162306a36Sopenharmony_cioriginates from (``SID``) and what the intended target of the message is 29262306a36Sopenharmony_ci(``TID``). Note that a response to a specific request therefore has the source 29362306a36Sopenharmony_ciand target IDs swapped when compared to the original request (i.e. the request 29462306a36Sopenharmony_citarget is the response source and the request source is the response target). 29562306a36Sopenharmony_ciSee (:c:type:`enum ssh_request_id <ssh_request_id>`) for possible values of 29662306a36Sopenharmony_ciboth. 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ciNote that, even though requests and events should be uniquely identifiable by 29962306a36Sopenharmony_citarget category and command ID alone, the EC may require specific target ID and 30062306a36Sopenharmony_ciinstance ID values to accept a command. A command that is accepted for 30162306a36Sopenharmony_ci``TID=1``, for example, may not be accepted for ``TID=2`` and vice versa. While 30262306a36Sopenharmony_cithis may not always hold in reality, you can think of different target/source 30362306a36Sopenharmony_ciIDs indicating different physical ECs with potentially different feature sets. 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ciLimitations and Observations 30762306a36Sopenharmony_ci============================ 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ciThe protocol can, in theory, handle up to ``U8_MAX`` frames in parallel, 31062306a36Sopenharmony_ciwith up to ``U16_MAX`` pending requests (neglecting request IDs reserved for 31162306a36Sopenharmony_cievents). In practice, however, this is more limited. From our testing 31262306a36Sopenharmony_ci(although via a python and thus a user-space program), it seems that the EC 31362306a36Sopenharmony_cican handle up to four requests (mostly) reliably in parallel at a certain 31462306a36Sopenharmony_citime. With five or more requests in parallel, consistent discarding of 31562306a36Sopenharmony_cicommands (ACKed frame but no command response) has been observed. For five 31662306a36Sopenharmony_cisimultaneous commands, this reproducibly resulted in one command being 31762306a36Sopenharmony_cidropped and four commands being handled. 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ciHowever, it has also been noted that, even with three requests in parallel, 32062306a36Sopenharmony_cioccasional frame drops happen. Apart from this, with a limit of three 32162306a36Sopenharmony_cipending requests, no dropped commands (i.e. command being dropped but frame 32262306a36Sopenharmony_cicarrying command being ACKed) have been observed. In any case, frames (and 32362306a36Sopenharmony_cipossibly also commands) should be re-sent by the host if a certain timeout 32462306a36Sopenharmony_ciis exceeded. This is done by the EC for frames with a timeout of one second, 32562306a36Sopenharmony_ciup to two re-tries (i.e. three transmissions in total). The limit of 32662306a36Sopenharmony_cire-tries also applies to received NAKs, and, in a worst case scenario, can 32762306a36Sopenharmony_cilead to entire messages being dropped. 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ciWhile this also seems to work fine for pending data frames as long as no 33062306a36Sopenharmony_citransmission failures occur, implementation and handling of these seems to 33162306a36Sopenharmony_cidepend on the assumption that there is only one non-acknowledged data frame. 33262306a36Sopenharmony_ciIn particular, the detection of repeated frames relies on the last sequence 33362306a36Sopenharmony_cinumber. This means that, if a frame that has been successfully received by 33462306a36Sopenharmony_cithe EC is sent again, e.g. due to the host not receiving an |ACK|, the EC 33562306a36Sopenharmony_ciwill only detect this if it has the sequence ID of the last frame received 33662306a36Sopenharmony_ciby the EC. As an example: Sending two frames with ``SEQ=0`` and ``SEQ=1`` 33762306a36Sopenharmony_cifollowed by a repetition of ``SEQ=0`` will not detect the second ``SEQ=0`` 33862306a36Sopenharmony_ciframe as such, and thus execute the command in this frame each time it has 33962306a36Sopenharmony_cibeen received, i.e. twice in this example. Sending ``SEQ=0``, ``SEQ=1`` and 34062306a36Sopenharmony_cithen repeating ``SEQ=1`` will detect the second ``SEQ=1`` as repetition of 34162306a36Sopenharmony_cithe first one and ignore it, thus executing the contained command only once. 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ciIn conclusion, this suggests a limit of at most one pending un-ACKed frame 34462306a36Sopenharmony_ci(per party, effectively leading to synchronous communication regarding 34562306a36Sopenharmony_ciframes) and at most three pending commands. The limit to synchronous frame 34662306a36Sopenharmony_citransfers seems to be consistent with behavior observed on Windows. 347