1 /* 2 * Copyright (c) 2019, The OpenThread Authors. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. Neither the name of the copyright holder nor the 13 * names of its contributors may be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /** 30 * @file 31 * This file includes definitions for the SPI interface to radio (RCP). 32 */ 33 34 #ifndef POSIX_APP_SPI_INTERFACE_HPP_ 35 #define POSIX_APP_SPI_INTERFACE_HPP_ 36 37 #include "openthread-posix-config.h" 38 39 #include "platform-posix.h" 40 #include "lib/hdlc/hdlc.hpp" 41 #include "lib/spinel/spinel_interface.hpp" 42 43 #include <openthread/openthread-system.h> 44 45 #if OPENTHREAD_POSIX_CONFIG_RCP_BUS == OT_POSIX_RCP_BUS_SPI 46 47 #include "ncp/ncp_spi.hpp" 48 49 namespace ot { 50 namespace Posix { 51 52 /** 53 * This class defines an SPI interface to the Radio Co-processor (RCP). 54 * 55 */ 56 class SpiInterface 57 { 58 public: 59 /** 60 * This constructor initializes the object. 61 * 62 * @param[in] aCallback A reference to a `Callback` object. 63 * @param[in] aCallbackContext The context pointer passed to the callback. 64 * @param[in] aFrameBuffer A reference to a `RxFrameBuffer` object. 65 * 66 */ 67 SpiInterface(Spinel::SpinelInterface::ReceiveFrameCallback aCallback, 68 void * aCallbackContext, 69 Spinel::SpinelInterface::RxFrameBuffer & aFrameBuffer); 70 71 /** 72 * This destructor deinitializes the object. 73 * 74 */ 75 ~SpiInterface(void); 76 77 /** 78 * This method initializes the interface to the Radio Co-processor (RCP). 79 * 80 * @note This method should be called before reading and sending spinel frames to the interface. 81 * 82 * @param[in] aRadioUrl Arguments parsed from radio url. 83 * 84 * @retval OT_ERROR_NONE The interface is initialized successfully. 85 * @retval OT_ERROR_ALREADY The interface is already initialized. 86 * @retval OT_ERROR_INVALID_ARGS The UART device or executable cannot be found or failed to open/run. 87 * 88 */ 89 otError Init(const Url::Url &aRadioUrl); 90 91 /** 92 * This method deinitializes the interface to the RCP. 93 * 94 */ 95 void Deinit(void); 96 97 /** 98 * This method encodes and sends a spinel frame to Radio Co-processor (RCP) over the socket. 99 * 100 * @param[in] aFrame A pointer to buffer containing the spinel frame to send. 101 * @param[in] aLength The length (number of bytes) in the frame. 102 * 103 * @retval OT_ERROR_NONE Successfully encoded and sent the spinel frame. 104 * @retval OT_ERROR_BUSY Failed due to another operation is on going. 105 * @retval OT_ERROR_NO_BUFS Insufficient buffer space available to encode the frame. 106 * @retval OT_ERROR_FAILED Failed to call the SPI driver to send the frame. 107 * 108 */ 109 otError SendFrame(const uint8_t *aFrame, uint16_t aLength); 110 111 /** 112 * This method waits for receiving part or all of spinel frame within specified interval. 113 * 114 * @param[in] aTimeout The timeout value in microseconds. 115 * 116 * @retval OT_ERROR_NONE Part or all of spinel frame is received. 117 * @retval OT_ERROR_RESPONSE_TIMEOUT No spinel frame is received within @p aTimeout. 118 * 119 */ 120 otError WaitForFrame(uint64_t aTimeoutUs); 121 122 /** 123 * This method updates the file descriptor sets with file descriptors used by the radio driver. 124 * 125 * @param[inout] aReadFdSet A reference to the read file descriptors. 126 * @param[inout] aWriteFdSet A reference to the write file descriptors. 127 * @param[inout] aMaxFd A reference to the max file descriptor. 128 * @param[inout] aTimeout A reference to the timeout. 129 * 130 */ 131 void UpdateFdSet(fd_set &aReadFdSet, fd_set &aWriteFdSet, int &aMaxFd, struct timeval &aTimeout); 132 133 /** 134 * This method performs radio driver processing. 135 * 136 * @param[in] aContext The context containing fd_sets. 137 * 138 */ 139 void Process(const RadioProcessContext &aContext); 140 141 /** 142 * This method returns the bus speed between the host and the radio. 143 * 144 * @returns Bus speed in bits/second. 145 * 146 */ GetBusSpeed(void) const147 uint32_t GetBusSpeed(void) const { return ((mSpiDevFd >= 0) ? mSpiSpeedHz : 0); } 148 149 /** 150 * This method is called when RCP failure detected and resets internal states of the interface. 151 * 152 */ 153 void OnRcpReset(void); 154 155 private: 156 int SetupGpioHandle(int aFd, uint8_t aLine, uint32_t aHandleFlags, const char *aLabel); 157 int SetupGpioEvent(int aFd, uint8_t aLine, uint32_t aHandleFlags, uint32_t aEventFlags, const char *aLabel); 158 void SetGpioValue(int aFd, uint8_t aValue); 159 uint8_t GetGpioValue(int aFd); 160 161 void InitResetPin(const char *aCharDev, uint8_t aLine); 162 void InitIntPin(const char *aCharDev, uint8_t aLine); 163 void InitSpiDev(const char *aPath, uint8_t aMode, uint32_t aSpeed); 164 void TriggerReset(void); 165 166 uint8_t *GetRealRxFrameStart(uint8_t *aSpiRxFrameBuffer, uint8_t aAlignAllowance, uint16_t &aSkipLength); 167 otError DoSpiTransfer(uint8_t *aSpiRxFrameBuffer, uint32_t aTransferLength); 168 otError PushPullSpi(void); 169 170 bool CheckInterrupt(void); 171 void LogStats(void); 172 void LogError(const char *aString); 173 void LogBuffer(const char *aDesc, const uint8_t *aBuffer, uint16_t aLength, bool aForce); 174 175 enum 176 { 177 kSpiModeMax = 3, 178 kSpiAlignAllowanceMax = 16, 179 kSpiFrameHeaderSize = 5, 180 kSpiBitsPerWord = 8, 181 kSpiTxRefuseWarnCount = 30, 182 kSpiTxRefuseExitCount = 100, 183 kImmediateRetryCount = 5, 184 kFastRetryCount = 15, 185 kDebugBytesPerLine = 16, 186 kGpioIntAssertState = 0, 187 kGpioResetAssertState = 0, 188 }; 189 190 enum 191 { 192 kMsecPerSec = 1000, 193 kUsecPerMsec = 1000, 194 kSpiPollPeriodUs = kMsecPerSec * kUsecPerMsec / 30, 195 kSecPerDay = 60 * 60 * 24, 196 kResetHoldOnUsec = 10 * kUsecPerMsec, 197 kImmediateRetryTimeoutUs = 1 * kUsecPerMsec, 198 kFastRetryTimeoutUs = 10 * kUsecPerMsec, 199 kSlowRetryTimeoutUs = 33 * kUsecPerMsec, 200 }; 201 202 enum 203 { 204 kMaxFrameSize = Spinel::SpinelInterface::kMaxFrameSize, 205 }; 206 207 Spinel::SpinelInterface::ReceiveFrameCallback mReceiveFrameCallback; 208 void * mReceiveFrameContext; 209 Spinel::SpinelInterface::RxFrameBuffer & mRxFrameBuffer; 210 211 int mSpiDevFd; 212 int mResetGpioValueFd; 213 int mIntGpioValueFd; 214 215 uint8_t mSpiMode; 216 uint8_t mSpiAlignAllowance; 217 uint32_t mSpiResetDelay; 218 uint16_t mSpiCsDelayUs; 219 uint16_t mSpiSmallPacketSize; 220 uint32_t mSpiSpeedHz; 221 222 uint64_t mSlaveResetCount; 223 uint64_t mSpiFrameCount; 224 uint64_t mSpiValidFrameCount; 225 uint64_t mSpiGarbageFrameCount; 226 uint64_t mSpiDuplexFrameCount; 227 uint64_t mSpiUnresponsiveFrameCount; 228 uint64_t mSpiRxFrameCount; 229 uint64_t mSpiRxFrameByteCount; 230 uint64_t mSpiTxFrameCount; 231 uint64_t mSpiTxFrameByteCount; 232 233 bool mSpiTxIsReady; 234 uint16_t mSpiTxRefusedCount; 235 uint16_t mSpiTxPayloadSize; 236 uint8_t mSpiTxFrameBuffer[kMaxFrameSize + kSpiAlignAllowanceMax]; 237 238 bool mDidPrintRateLimitLog; 239 uint16_t mSpiSlaveDataLen; 240 241 // Non-copyable, intentionally not implemented. 242 SpiInterface(const SpiInterface &); 243 SpiInterface &operator=(const SpiInterface &); 244 }; 245 246 } // namespace Posix 247 } // namespace ot 248 249 #endif // OPENTHREAD_POSIX_CONFIG_RCP_BUS == OT_POSIX_RCP_BUS_SPI 250 #endif // POSIX_APP_SPI_INTERFACE_HPP_ 251