| /* |
| * Copyright (c) 2016, The OpenThread Authors. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the copyright holder nor the |
| * names of its contributors may be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /** |
| * @file |
| * This file contains definitions for a SPI interface to the OpenThread stack. |
| */ |
| |
| #ifndef NCP_SPI_HPP_ |
| #define NCP_SPI_HPP_ |
| |
| #include "openthread-core-config.h" |
| |
| #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 { |
| |
| /** |
| * This class defines a SPI frame. |
| * |
| */ |
| class SpiFrame |
| { |
| public: |
| enum |
| { |
| kHeaderSize = 5, ///< SPI header size (in bytes). |
| }; |
| |
| /** |
| * This constructor initializes an `SpiFrame` instance. |
| * |
| * @param[in] aBuffer Pointer to buffer containing the frame. |
| * |
| */ |
| explicit SpiFrame(uint8_t *aBuffer) |
| : mBuffer(aBuffer) |
| { |
| } |
| |
| /** |
| * This method gets a pointer to data portion in the SPI frame skipping the header. |
| * |
| * @returns A pointer to data in the SPI frame. |
| * |
| */ |
| uint8_t *GetData(void) { return mBuffer + kHeaderSize; } |
| |
| /** |
| * This method indicates whether or not the frame is valid. |
| * |
| * In a valid frame the flag byte should contain the pattern bits. |
| * |
| * @returns TRUE if the frame is valid, FALSE otherwise. |
| * |
| */ |
| bool IsValid(void) const { return ((mBuffer[kIndexFlagByte] & kFlagPatternMask) == kFlagPattern); } |
| |
| /** |
| * This method indicates whether or not the "RST" bit is set. |
| * |
| * @returns TRUE if the "RST" bit is set, FALSE otherwise. |
| * |
| */ |
| bool IsResetFlagSet(void) const { return ((mBuffer[kIndexFlagByte] & kFlagReset) == kFlagReset); } |
| |
| /** |
| * This method sets the "flag byte" field in the SPI frame header. |
| * |
| * @param[in] aResetFlag The status of reset flag (TRUE to set the flag, FALSE to clear flag). |
| * |
| */ |
| void SetHeaderFlagByte(bool aResetFlag) { mBuffer[kIndexFlagByte] = kFlagPattern | (aResetFlag ? kFlagReset : 0); } |
| |
| /** |
| * This method gets the "flag byte" field in the SPI frame header. |
| * |
| * @returns The flag byte. |
| * |
| */ |
| uint8_t GetHeaderFlagByte(void) const { return mBuffer[kIndexFlagByte]; } |
| |
| /** |
| * This method sets the "accept len" field in the SPI frame header. |
| * |
| * "accept len" specifies number of bytes the sender of the SPI frame can receive. |
| * |
| * @param[in] aAcceptLen The accept length in bytes. |
| * |
| */ |
| void SetHeaderAcceptLen(uint16_t aAcceptLen) |
| { |
| Encoding::LittleEndian::WriteUint16(aAcceptLen, mBuffer + kIndexAcceptLen); |
| } |
| |
| /** |
| * This method gets the "accept len" field in the SPI frame header. |
| * |
| * @returns The accept length in bytes. |
| * |
| */ |
| uint16_t GetHeaderAcceptLen(void) const { return Encoding::LittleEndian::ReadUint16(mBuffer + kIndexAcceptLen); } |
| |
| /** |
| * This method sets the "data len" field in the SPI frame header. |
| * |
| * "Data len" specifies number of data bytes in the transmitted SPI frame. |
| * |
| * @param[in] aDataLen The data length in bytes. |
| * |
| */ |
| void SetHeaderDataLen(uint16_t aDataLen) { Encoding::LittleEndian::WriteUint16(aDataLen, mBuffer + kIndexDataLen); } |
| |
| /** |
| * This method gets the "data len" field in the SPI frame header. |
| * |
| * @returns The data length in bytes. |
| * |
| */ |
| uint16_t GetHeaderDataLen(void) const { return Encoding::LittleEndian::ReadUint16(mBuffer + kIndexDataLen); } |
| |
| private: |
| enum |
| { |
| kIndexFlagByte = 0, // flag byte (uint8_t). |
| kIndexAcceptLen = 1, // accept len (uint16_t little-endian encoding). |
| kIndexDataLen = 3, // data len (uint16_t little-endian encoding). |
| |
| kFlagReset = (1 << 7), // Flag byte RESET bit. |
| kFlagPattern = 0x02, // Flag byte PATTERN bits. |
| kFlagPatternMask = 0x03, // Flag byte PATTERN mask. |
| }; |
| |
| uint8_t *mBuffer; |
| }; |
| |
| class NcpSpi : public NcpBase |
| { |
| public: |
| /** |
| * This constructor initializes the object. |
| * |
| * @param[in] aInstance A pointer to the OpenThread instance structure. |
| * |
| */ |
| explicit NcpSpi(Instance *aInstance); |
| |
| private: |
| enum |
| { |
| /** |
| * SPI tx and rx buffer size (should fit a max length frame + SPI header). |
| * |
| */ |
| kSpiBufferSize = OPENTHREAD_CONFIG_NCP_SPI_BUFFER_SIZE, |
| |
| /** |
| * Size of the SPI header (in bytes). |
| * |
| */ |
| kSpiHeaderSize = SpiFrame::kHeaderSize, |
| }; |
| |
| enum TxState |
| { |
| kTxStateIdle, // No frame to send. |
| kTxStateSending, // A frame is ready to be sent. |
| kTxStateHandlingSendDone // The frame was sent successfully, waiting to prepare the next one (if any). |
| }; |
| |
| typedef uint8_t LargeFrameBuffer[kSpiBufferSize]; |
| typedef uint8_t EmptyFrameBuffer[kSpiHeaderSize]; |
| |
| static bool SpiTransactionComplete(void * aContext, |
| uint8_t *aOutputBuf, |
| uint16_t aOutputLen, |
| uint8_t *aInputBuf, |
| uint16_t aInputLen, |
| uint16_t aTransLen); |
| bool SpiTransactionComplete(uint8_t *aOutputBuf, |
| uint16_t aOutputLen, |
| uint8_t *aInputBuf, |
| uint16_t aInputLen, |
| uint16_t aTransLen); |
| |
| static void SpiTransactionProcess(void *aContext); |
| void SpiTransactionProcess(void); |
| |
| static void HandleFrameAddedToTxBuffer(void * aContext, |
| Spinel::Buffer::FrameTag aFrameTag, |
| Spinel::Buffer::Priority aPriority, |
| Spinel::Buffer * aBuffer); |
| |
| static void PrepareTxFrame(Tasklet &aTasklet); |
| void PrepareTxFrame(void); |
| void HandleRxFrame(void); |
| void PrepareNextSpiSendFrame(void); |
| |
| volatile TxState mTxState; |
| volatile bool mHandlingRxFrame; |
| volatile bool mResetFlag; |
| |
| Tasklet mPrepareTxFrameTask; |
| |
| uint16_t mSendFrameLength; |
| LargeFrameBuffer mSendFrame; |
| EmptyFrameBuffer mEmptySendFrameFullAccept; |
| EmptyFrameBuffer mEmptySendFrameZeroAccept; |
| |
| LargeFrameBuffer mReceiveFrame; |
| EmptyFrameBuffer mEmptyReceiveFrame; |
| }; |
| |
| } // namespace Ncp |
| } // namespace ot |
| |
| #endif // NCP_SPI_HPP_ |