.. packets.rst -- transporting message parts Copyright © 2019 Olaf Meeuwissen SPDX-License-Identifier: CC-BY-SA-4.0 .. _protocol-packets: Protocol Packets ================ The Ambit protocol messages need to be transferred between application and watch. The transfer method of choice is that of USB-HID interrupt transfers. These interrupt transfers have one important requirement that needs to be satisfied: every single transfer *must* send a fixed amount of bytes. The value for that amount is available for each USB interrupt endpoint in its :math:`wMaxPacketSize` field. .. So far only values of 64 bytes in either direction have been seen in the wild for :math:`wMaxPacketSize` for supported watches. This means that :ref:`messages ` can to be too short or too long for a single USB interrupt transfer. To address that, the Ambit protocol messages are "stuffed" into Ambit protocol packets. Each such protocol packet is exactly :math:`wMaxPacketSize` bytes long. Protocol messages that are too short are padded, those that are too long are split up over multiple packets. Hence, messages always correspond to one or more packets. Conversely, each packet always contains (part of) a message. Structure --------- For administrative purposes, each packet starts with a small header of its own followed by a `cyclic redundancy check`_ (CRC). Next comes a packet payload followed by a second CRC. The CRC values are included so that header and payload can be checked for errors. Left over space is padding and should be ignored. Each packet but the last holds as much data as will fit. Only the last packet for a message will have any padding. Writing the packet structure down in `Augmented Backus–Naur Form`_ (ABNF) gives .. productionlist:: packet: packet-header `CRC` packet-payload `CRC` padding packet-header: `marker` `offset` `type` `size` `index` packet-payload: *OCTET padding: *OCTET Taking into account the sizes of the various components, the maximum payload size for a single packet works out to :math:`wMaxPacketSize - 10` bytes. Illustrating the layout for a 64 byte packet containing 42 bytes of a message payload gives the picture below. As far as the *packets* are concerned, only the red outlined parts are of interest. .. image:: packet.svg Marker ------ .. productionlist:: marker: %x3f The first byte of every Ambit protocol packet is always ``0x3f``. In ASCII that translates to a question mark. Any payload from a USB-HID transfer that starts with something else is most probably *not* an Ambit protocol packet. .. _crc-offset: Offset ------ .. productionlist:: offset: %d8-%d254 The single byte integer in the second byte of the packet tells where one can find the second of the cyclic redundancy checks (CRC-2). As payload sizes vary, the check value is not located at a fixed position. This byte makes it easy to find. The offset is always equal to the sum of header and payload size, per packet layout. From the packet layout it also follows that its value is always between :math:`8`, the size of a packet header and the first CRC, and :math:`wMaxPacketSize - 2`, both inclusive. Combined with the one byte nature for this offset, this means that :math:`256` is the largest :math:`wMaxPacketSize` value that can be accommodated. .. note:: The :math:`wMaxPacketSize` value has to be a power of 2 per USB specification. .. _packet-type: Type ---- .. productionlist:: type: starter | trailer starter: %x5d trailer: %x5e Packets come in two different types: starter and trailer packets. A starter packet has a type byte equal to ``0x5d``, a trailer packet uses ``0x5e``. The type byte is at an offset of 2. When Ambit protocol :ref:`messages ` are "stuffed" into packets, only the first part of the message travels in a starter packet. Any other packets that may be necessary to send the whole message travel in trailer packets. .. _packet-size: Size ---- .. productionlist:: size: %d0-%d246 Although very easily computed from the check value :ref:`offset `, the payload size is also stored at offset 3 in the packet. It gives the number of bytes of the payload that is sandwiched between the two check values (CRC-1 and CRC-2). Notwithstanding the ABNF, its value is always between :math:`0` and :math:`wMaxPacketSize - 10`, both values inclusive. So for a watch with a :math:`wMaxPacketSize` of :math:`64`, the ABNF would become .. productionlist:: size: %d0-%d54 Index ----- .. productionlist:: index: %d1-%d65535 All packets include a little-endian, two byte integer index at offsets 4 and 5. The index of the :ref:`starter packet ` counts the number of packets that make up a single message. Indices for the message's remaining :ref:`trailer packets ` increase from one to one less than the index value in the starter packet. So for an index value of :math:`n` in the starter packet, the indices of trailer packets will run from :math:`1` through :math:`n - 1`. Combined with the :ref:`payload size ` constraints, this means that the Ambit protocol message size has an upper bound of :math:`65535 (wMaxPacketSize - 10)`, where :math:`wMaxPacketSize` is :math:`256` at most, per :ref:`offset ` constraints. CRC --- .. productionlist:: CRC: 2OCTET They have been mentioned a few times above already, but Ambit protocol packets use a `cyclic redundancy check`_ (CRC) pair to guard against packet corruption going unnoticed. Many CRC algorithms exist but the algorithm used by the Ambits goes by the name of `CRC-16/CCITT FALSE`_ (or something similar). .. object:: CRC-1 The first CRC, CRC-1, covers the type, size and index, that is, the four bytes starting at offset 2. .. object:: CRC-2 The second CRC, CRC-2, covers the payload as well as everything covered by CRC-1. In practice, this means that one would normally compute CRC-1 first and use that as the seed when continuing the computation over the payload bytes to get CRC-2. .. note:: Code for this widely used algorithm should be easy to find. The Python `crcmod`_ module supports it as one of its predefined CRC algorithms and can generate code for C/C++. The Wireshark_ code includes a ``crc16_x25_ccitt`` or ``crc16_x25_ccitt_seed`` function (depending on the version) that implements it as well. .. _Augmented Backus–Naur Form: https://en.wikipedia.org/wiki/Augmented_Backus–Naur_Form .. _CRC-16/CCITT FALSE: http://reveng.sourceforge.net/crc-catalogue/all.htm#crc.cat.crc-16-ccitt-false .. _Wireshark: https://www.wireshark.org/ .. _crcmod: http://crcmod.sourceforge.net/ .. _cyclic redundancy check: https://en.wikipedia.org/wiki/Cyclic_redundancy_check