mirror of
https://github.com/espressif/openthread.git
synced 2026-07-30 23:57:47 +00:00
[spinel] add framing and data type docs in header file (#3903)
This commit also adds the SPI framing protocol documentation in `ncp_spi.h`.
This commit is contained in:
committed by
Jonathan Hui
parent
0ffb4ff1d4
commit
a508e5ebe5
@@ -37,6 +37,88 @@
|
||||
|
||||
#include "ncp/ncp_base.hpp"
|
||||
|
||||
/*
|
||||
* SPI Framing Protocol
|
||||
*
|
||||
* Each SPI frame starts with a 5-byte frame header:
|
||||
*
|
||||
* +---------+-----+----------+----------+
|
||||
* | Octets: | 1 | 2 | 2 |
|
||||
* +---------+-----+----------+----------+
|
||||
* | Fields: | HDR | RECV_LEN | DATA_LEN |
|
||||
* +---------+-----+----------+----------+
|
||||
*
|
||||
* - "HDR": The first byte is the header byte (defined below)
|
||||
* - "RECV_LEN": The second and third bytes indicate the largest frame
|
||||
* size that that device is ready to receive. If zero, then the
|
||||
* other device must not send any data. (Little endian)
|
||||
* - "DATA_LEN": The fourth and fifth bytes indicate the size of the
|
||||
* pending data frame to be sent to the other device. If this value
|
||||
* is equal-to or less-than the number of bytes that the other device
|
||||
* is willing to receive, then the data of the frame is immediately
|
||||
* after the header. (Little Endian)
|
||||
*
|
||||
* The "HDR" byte is defined as:
|
||||
*
|
||||
* 0 1 2 3 4 5 6 7
|
||||
* +---+---+---+---+---+---+---+---+
|
||||
* |RST|CRC|CCF| RESERVED |PATTERN|
|
||||
* +---+---+---+---+---+---+---+---+
|
||||
*
|
||||
* - "RST": This bit is set when that device has been reset since the
|
||||
* last time `CS` (chip select) was asserted.
|
||||
* - "CRC": This bit is set when that device supports writing a 16-bit
|
||||
* CRC at the end of the data. The CRC length is NOT included in
|
||||
* DATA_LEN.
|
||||
* - "CCF": "CRC Check Failure". Set if the CRC check on the last
|
||||
* received frame failed, cleared to zero otherwise. This bit is
|
||||
* only used if both sides support CRC.
|
||||
* - "RESERVED": These bits are all reserved for future used. They
|
||||
* MUST be cleared to zero and MUST be ignored if set.
|
||||
* - "PATTERN": These bits are set to a fixed value to help distinguish
|
||||
* valid SPI frames from garbage (by explicitly making "0xFF" and
|
||||
* "0x00" invalid values). Bit 6 MUST be set to be one and bit 7
|
||||
* MUST be cleared (0). A frame received that has any other values
|
||||
* for these bits MUST be dropped.
|
||||
*
|
||||
* Prior to a sending or receiving a frame, the master MAY send a
|
||||
* 5-octet frame with zeros for both the max receive frame size and the
|
||||
* the contained frame length. This will induce the slave device to
|
||||
* indicate the length of the frame it wants to send (if any) and
|
||||
* indicate the largest frame it is capable of receiving at the moment.
|
||||
* This allows the master to calculate the size of the next transaction.
|
||||
* Alternatively, if the master has a frame to send it can just go ahead
|
||||
* and send a frame of that length and determine if the frame was
|
||||
* accepted by checking that the "RECV_LEN" from the slave frame is
|
||||
* larger than the frame the master just tried to send. If the
|
||||
* "RECV_LEN" is smaller then the frame wasn't accepted and will need to
|
||||
* be transmitted again.
|
||||
*
|
||||
* This protocol can be used either unidirectionally or bidirectionally,
|
||||
* determined by the behavior of the master and the slave.
|
||||
*
|
||||
* If the the master notices "PATTERN" is not set correctly, the master
|
||||
* should consider the transaction to have failed and try again after 10
|
||||
* milliseconds, retrying up to 200 times. After unsuccessfully trying
|
||||
* 200 times in a row, the master MAY take appropriate remedial action
|
||||
* (like a NCP hardware reset, or indicating a communication failure to
|
||||
* a user interface).
|
||||
*
|
||||
* At the end of the data of a frame is an optional 16-bit CRC, support
|
||||
* for which is indicated by the "CRC" bit of the "HDR" byte being set.
|
||||
* If these bits are set for both the master and slave frames, then CRC
|
||||
* checking is enabled on both sides, effectively requiring that frame
|
||||
* sizes be two bytes longer than would be otherwise required. The CRC
|
||||
* is calculated using the same mechanism used for the CRC calculation
|
||||
* in HDLC-Lite (See Appendix A.1.2). When both of the "CRC" bits are
|
||||
* set, both sides must verify that the "CRC" is valid before accepting
|
||||
* the frame. If not enough bytes were clocked out for the CRC to be
|
||||
* read, then the frame must be ignored. If enough bytes were clocked
|
||||
* out to perform a CRC check, but the CRC check fails, then the frame
|
||||
* must be rejected and the "CRC_FAIL" bit on the next frame (and ONLY
|
||||
* the next frame) MUST be set.
|
||||
*/
|
||||
|
||||
namespace ot {
|
||||
namespace Ncp {
|
||||
|
||||
|
||||
+271
-7
@@ -27,21 +27,285 @@
|
||||
|
||||
/**
|
||||
* @file
|
||||
* This file contains definitions of spinel API.
|
||||
* This file contains definitions of spinel.
|
||||
*/
|
||||
|
||||
#ifndef SPINEL_HEADER_INCLUDED
|
||||
#define SPINEL_HEADER_INCLUDED 1
|
||||
|
||||
/*
|
||||
* Spinel definition guideline:
|
||||
* Spinel is a host-controller protocol designed to enable
|
||||
* inter-operation over simple serial connections between general purpose
|
||||
* device operating systems (OS) host and network co-processors (NCP) for
|
||||
* the purpose of controlling and managing the NCP.
|
||||
*
|
||||
* New NCP firmware should work with an older host driver, i.e., NCP implementation should remain backward compatible.
|
||||
* ---------------------------------------------------------------------------
|
||||
*
|
||||
* - Existing fields in the format of an already implemented spinel property or command cannot change.
|
||||
* - New fields may be appended at the end of the format (or the end of a struct) as long as the NCP implementation
|
||||
* treats the new fields as optional (i.e., a driver not aware of and therefore not using the new fields should
|
||||
* continue to function as before).
|
||||
* Frame Format
|
||||
*
|
||||
* A frame is defined simply as the concatenation of
|
||||
*
|
||||
* - A header byte
|
||||
* - A command (up to three bytes)
|
||||
* - An optional command payload
|
||||
*
|
||||
* +---------+--------+-----+-------------+
|
||||
* | Octets: | 1 | 1-3 | n |
|
||||
* +---------+--------+-----+-------------+
|
||||
* | Fields: | HEADER | CMD | CMD_PAYLOAD |
|
||||
* +---------+--------+-----+-------------+
|
||||
*
|
||||
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
*
|
||||
* Header Format
|
||||
*
|
||||
* The header byte is broken down as follows:
|
||||
*
|
||||
* 0 1 2 3 4 5 6 7
|
||||
* +---+---+---+---+---+---+---+---+
|
||||
* | FLG | IID | TID |
|
||||
* +---+---+---+---+---+---+---+---+
|
||||
*
|
||||
*
|
||||
* The flag field of the header byte ("FLG") is always set to the value
|
||||
* two (or "10" in binary). Any frame received with these bits set to
|
||||
* any other value else MUST NOT be considered a Spinel frame.
|
||||
*
|
||||
* This convention allows Spinel to be line compatible with BTLE HCI.
|
||||
* By defining the first two bit in this way we can disambiguate between
|
||||
* Spinel frames and HCI frames (which always start with either "0x01"
|
||||
* or "0x04") without any additional framing overhead.
|
||||
*
|
||||
* The Interface Identifier (IID) is a number between 0 and 3, which
|
||||
* is associated by the OS with a specific NCP. This allows the protocol
|
||||
* to support up to 4 NCPs under same connection.
|
||||
*
|
||||
* The least significant bits of the header represent the Transaction
|
||||
* Identifier (TID). The TID is used for correlating responses to the
|
||||
* commands which generated them.
|
||||
*
|
||||
* When a command is sent from the host, any reply to that command sent
|
||||
* by the NCP will use the same value for the TID. When the host
|
||||
* receives a frame that matches the TID of the command it sent, it can
|
||||
* easily recognize that frame as the actual response to that command.
|
||||
*
|
||||
* The TID value of zero (0) is used for commands to which a correlated
|
||||
* response is not expected or needed, such as for unsolicited update
|
||||
* commands sent to the host from the NCP.
|
||||
*
|
||||
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
*
|
||||
* The command identifier is a 21-bit unsigned integer encoded in up to
|
||||
* three bytes using the packed unsigned integer format described below.
|
||||
* Depending on the semantics of the command in question, a payload MAY
|
||||
* be included in the frame. The exact composition and length of the
|
||||
* payload is defined by the command identifier.
|
||||
*
|
||||
* ---------------------------------------------------------------------------
|
||||
*
|
||||
* Data Packing
|
||||
*
|
||||
* Data serialization for properties is performed using a light-weight
|
||||
* data packing format which was loosely inspired by D-Bus. The format
|
||||
* of a serialization is defined by a specially formatted string.
|
||||
*
|
||||
* This packing format is used for notational convenience. While this
|
||||
* string-based data-type format has been designed so that the strings
|
||||
* may be directly used by a structured data parser, such a thing is not
|
||||
* required to implement Spinel.
|
||||
*
|
||||
* Goals:
|
||||
*
|
||||
* - Be lightweight and favor direct representation of values.
|
||||
* - Use an easily readable and memorable format string.
|
||||
* - Support lists and structures.
|
||||
* - Allow properties to be appended to structures while maintaining
|
||||
* backward compatibility.
|
||||
*
|
||||
* Each primitive data-type has an ASCII character associated with it.
|
||||
* Structures can be represented as strings of these characters. For
|
||||
* example:
|
||||
*
|
||||
* - "C": A single unsigned byte.
|
||||
* - "C6U": A single unsigned byte, followed by a 128-bit IPv6 address,
|
||||
* followed by a zero-terminated UTF8 string.
|
||||
* - "A(6)": An array of concatenated IPv6 addresses
|
||||
*
|
||||
* In each case, the data is represented exactly as described. For
|
||||
* example, an array of 10 IPv6 address is stored as 160 bytes.
|
||||
*
|
||||
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
*
|
||||
* Primitive Types
|
||||
*
|
||||
* +----------+----------------------+---------------------------------+
|
||||
* | Char | Name | Description |
|
||||
* +----------+----------------------+---------------------------------+
|
||||
* | "." | DATATYPE_VOID | Empty data type. Used |
|
||||
* | | | internally. |
|
||||
* | "b" | DATATYPE_BOOL | Boolean value. Encoded in |
|
||||
* | | | 8-bits as either 0x00 or 0x01. |
|
||||
* | | | All other values are illegal. |
|
||||
* | "C" | DATATYPE_UINT8 | Unsigned 8-bit integer. |
|
||||
* | "c" | DATATYPE_INT8 | Signed 8-bit integer. |
|
||||
* | "S" | DATATYPE_UINT16 | Unsigned 16-bit integer. |
|
||||
* | "s" | DATATYPE_INT16 | Signed 16-bit integer. |
|
||||
* | "L" | DATATYPE_UINT32 | Unsigned 32-bit integer. |
|
||||
* | "l" | DATATYPE_INT32 | Signed 32-bit integer. |
|
||||
* | "i" | DATATYPE_UINT_PACKED | Packed Unsigned Integer. See |
|
||||
* | | | description below |
|
||||
* | "6" | DATATYPE_IPv6ADDR | IPv6 Address. (Big-endian) |
|
||||
* | "E" | DATATYPE_EUI64 | EUI-64 Address. (Big-endian) |
|
||||
* | "e" | DATATYPE_EUI48 | EUI-48 Address. (Big-endian) |
|
||||
* | "D" | DATATYPE_DATA | Arbitrary data. See related |
|
||||
* | | | section below for details. |
|
||||
* | "d" | DATATYPE_DATA_WLEN | Arbitrary data with prepended |
|
||||
* | | | length. See below for details |
|
||||
* | "U" | DATATYPE_UTF8 | Zero-terminated UTF8-encoded |
|
||||
* | | | string. |
|
||||
* | "t(...)" | DATATYPE_STRUCT | Structured datatype with |
|
||||
* | | | prepended length. |
|
||||
* | "A(...)" | DATATYPE_ARRAY | Array of datatypes. Compound |
|
||||
* | | | type. |
|
||||
* +----------+----------------------+---------------------------------+
|
||||
*
|
||||
* All multi-byte values are little-endian unless explicitly stated
|
||||
* otherwise.
|
||||
*
|
||||
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
*
|
||||
* Packed Unsigned Integer
|
||||
*
|
||||
* For certain types of integers, such command or property identifiers,
|
||||
* usually have a value on the wire that is less than 127. However, in
|
||||
* order to not preclude the use of values larger than 255, we would
|
||||
* need to add an extra byte. Doing this would add an extra byte to the
|
||||
* majority of instances, which can add up in terms of bandwidth.
|
||||
*
|
||||
* The packed unsigned integer format is based on the unsigned integer
|
||||
* format in EXI, except that we limit the maximum value to the
|
||||
* largest value that can be encoded into three bytes (2,097,151).
|
||||
*
|
||||
* For all values less than 127, the packed form of the number is simply
|
||||
* a single byte which directly represents the number. For values
|
||||
* larger than 127, the following process is used to encode the value:
|
||||
*
|
||||
* 1. The unsigned integer is broken up into _n_ 7-bit chunks and
|
||||
* placed into _n_ octets, leaving the most significant bit of each
|
||||
* octet unused.
|
||||
* 2. Order the octets from least-significant to most-significant.
|
||||
* (Little-endian)
|
||||
* 3. Clear the most significant bit of the most significant octet.
|
||||
* Set the least significant bit on all other octets.
|
||||
*
|
||||
* Where `n` is the smallest number of 7-bit chunks you can use to
|
||||
* represent the given value.
|
||||
*
|
||||
* Take the value 1337, for example:
|
||||
*
|
||||
* 1337 => 0x0539
|
||||
* => [39 0A]
|
||||
* => [B9 0A]
|
||||
*
|
||||
* To decode the value, you collect the 7-bit chunks until you find an
|
||||
* octet with the most significant bit clear.
|
||||
*
|
||||
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
*
|
||||
* Data Blobs
|
||||
*
|
||||
* There are two types for data blobs: "d" and "D".
|
||||
*
|
||||
* - "d" has the length of the data (in bytes) prepended to the data
|
||||
* (with the length encoded as type "S"). The size of the length
|
||||
* field is not included in the length.
|
||||
* - "D" does not have a prepended length: the length of the data is
|
||||
* implied by the bytes remaining to be parsed. It is an error for
|
||||
* "D" to not be the last type in a type in a type signature.
|
||||
*
|
||||
* This dichotomy allows for more efficient encoding by eliminating
|
||||
* redundancy. If the rest of the buffer is a data blob, encoding the
|
||||
* length would be redundant because we already know how many bytes are
|
||||
* in the rest of the buffer.
|
||||
*
|
||||
* In some cases we use "d" even if it is the last field in a type
|
||||
* signature. We do this to allow for us to be able to append
|
||||
* additional fields to the type signature if necessary in the future.
|
||||
* This is usually the case with embedded structs, like in the scan
|
||||
* results.
|
||||
*
|
||||
* For example, let's say we have a buffer that is encoded with the
|
||||
* datatype signature of "CLLD". In this case, it is pretty easy to
|
||||
* tell where the start and end of the data blob is: the start is 9
|
||||
* bytes from the start of the buffer, and its length is the length of
|
||||
* the buffer minus 9. (9 is the number of bytes taken up by a byte and
|
||||
* two longs)
|
||||
*
|
||||
* The datatype signature "CLLDU" is illegal because we can't determine
|
||||
* where the last field (a zero-terminated UTF8 string) starts. But the
|
||||
* datatype "CLLdU" is legal, because the parser can determine the
|
||||
* exact length of the data blob-- allowing it to know where the start
|
||||
* of the next field would be.
|
||||
*
|
||||
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
*
|
||||
* Structured Data
|
||||
*
|
||||
* The structure data type ("t(...)") is a way of bundling together
|
||||
* several fields into a single structure. It can be thought of as a
|
||||
* "d" type except that instead of being opaque, the fields in the
|
||||
* content are known. This is useful for things like scan results where
|
||||
* you have substructures which are defined by different layers.
|
||||
*
|
||||
* For example, consider the type signature "Lt(ES)t(6C)". In this
|
||||
* hypothetical case, the first struct is defined by the MAC layer, and
|
||||
* the second struct is defined by the PHY layer. Because of the use of
|
||||
* structures, we know exactly what part comes from that layer.
|
||||
* Additionally, we can add fields to each structure without introducing
|
||||
* backward compatability problems: Data encoded as "Lt(ESU)t(6C)"
|
||||
* (Notice the extra "U") will decode just fine as "Lt(ES)t(6C)".
|
||||
* Additionally, if we don't care about the MAC layer and only care
|
||||
* about the network layer, we could parse as "Lt()t(6C)".
|
||||
*
|
||||
* Note that data encoded as "Lt(ES)t(6C)" will also parse as "Ldd",
|
||||
* with the structures from both layers now being opaque data blobs.
|
||||
*
|
||||
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
*
|
||||
* Arrays
|
||||
*
|
||||
* An array is simply a concatenated set of _n_ data encodings. For
|
||||
* example, the type "A(6)" is simply a list of IPv6 addresses---one
|
||||
* after the other. The type "A(6E)" likewise a concatenation of IPv6-
|
||||
* address/EUI-64 pairs.
|
||||
*
|
||||
* If an array contains many fields, the fields will often be surrounded
|
||||
* by a structure ("t(...)"). This effectively prepends each item in
|
||||
* the array with its length. This is useful for improving parsing
|
||||
* performance or to allow additional fields to be added in the future
|
||||
* in a backward compatible way. If there is a high certainty that
|
||||
* additional fields will never be added, the struct may be omitted
|
||||
* (saving two bytes per item).
|
||||
*
|
||||
* This specification does not define a way to embed an array as a field
|
||||
* alongside other fields.
|
||||
*
|
||||
* ---------------------------------------------------------------------------
|
||||
*
|
||||
* Spinel definition guideline:
|
||||
*
|
||||
* New NCP firmware should work with an older host driver, i.e., NCP
|
||||
* implementation should remain backward compatible.
|
||||
*
|
||||
* - Existing fields in the format of an already implemented spinel
|
||||
* property or command cannot change.
|
||||
*
|
||||
* - New fields may be appended at the end of the format (or the end of
|
||||
* a struct) as long as the NCP implementation treats the new fields as
|
||||
* optional (i.e., a driver not aware of and therefore not using the
|
||||
* new fields should continue to function as before).
|
||||
*
|
||||
* ---------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifdef SPINEL_PLATFORM_HEADER
|
||||
|
||||
Reference in New Issue
Block a user