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[in,out] aReadFdSet A reference to the read file descriptors. 126 * @param[in,out] aWriteFdSet A reference to the write file descriptors. 127 * @param[in,out] aMaxFd A reference to the max file descriptor. 128 * @param[in,out] 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 /** 156 * This method is called when RCP is reset to recreate the connection with it. 157 * Intentionally empty. 158 * 159 */ ResetConnection(void)160 otError ResetConnection(void) { return OT_ERROR_NONE; } 161 162 /** 163 * This method returns the RCP interface metrics. 164 * 165 * @returns The RCP interface metrics. 166 * 167 */ GetRcpInterfaceMetrics(void) const168 const otRcpInterfaceMetrics *GetRcpInterfaceMetrics(void) const { return &mInterfaceMetrics; } 169 170 private: 171 int SetupGpioHandle(int aFd, uint8_t aLine, uint32_t aHandleFlags, const char *aLabel); 172 int SetupGpioEvent(int aFd, uint8_t aLine, uint32_t aHandleFlags, uint32_t aEventFlags, const char *aLabel); 173 void SetGpioValue(int aFd, uint8_t aValue); 174 uint8_t GetGpioValue(int aFd); 175 176 void InitResetPin(const char *aCharDev, uint8_t aLine); 177 void InitIntPin(const char *aCharDev, uint8_t aLine); 178 void InitSpiDev(const char *aPath, uint8_t aMode, uint32_t aSpeed); 179 void TriggerReset(void); 180 181 uint8_t *GetRealRxFrameStart(uint8_t *aSpiRxFrameBuffer, uint8_t aAlignAllowance, uint16_t &aSkipLength); 182 otError DoSpiTransfer(uint8_t *aSpiRxFrameBuffer, uint32_t aTransferLength); 183 otError PushPullSpi(void); 184 void Process(const fd_set *aReadFdSet, const fd_set *aWriteFdSet); 185 186 bool CheckInterrupt(void); 187 void LogStats(void); 188 void LogError(const char *aString); 189 void LogBuffer(const char *aDesc, const uint8_t *aBuffer, uint16_t aLength, bool aForce); 190 191 enum 192 { 193 kSpiModeMax = 3, 194 kSpiAlignAllowanceMax = 16, 195 kSpiFrameHeaderSize = 5, 196 kSpiBitsPerWord = 8, 197 kSpiTxRefuseWarnCount = 30, 198 kSpiTxRefuseExitCount = 100, 199 kImmediateRetryCount = 5, 200 kFastRetryCount = 15, 201 kDebugBytesPerLine = 16, 202 kGpioIntAssertState = 0, 203 kGpioResetAssertState = 0, 204 }; 205 206 enum 207 { 208 kMsecPerSec = 1000, 209 kUsecPerMsec = 1000, 210 kSpiPollPeriodUs = kMsecPerSec * kUsecPerMsec / 30, 211 kSecPerDay = 60 * 60 * 24, 212 kResetHoldOnUsec = 10 * kUsecPerMsec, 213 kImmediateRetryTimeoutUs = 1 * kUsecPerMsec, 214 kFastRetryTimeoutUs = 10 * kUsecPerMsec, 215 kSlowRetryTimeoutUs = 33 * kUsecPerMsec, 216 }; 217 218 enum 219 { 220 kMaxFrameSize = Spinel::SpinelInterface::kMaxFrameSize, 221 }; 222 223 Spinel::SpinelInterface::ReceiveFrameCallback mReceiveFrameCallback; 224 void * mReceiveFrameContext; 225 Spinel::SpinelInterface::RxFrameBuffer & mRxFrameBuffer; 226 227 int mSpiDevFd; 228 int mResetGpioValueFd; 229 int mIntGpioValueFd; 230 231 uint8_t mSpiMode; 232 uint8_t mSpiAlignAllowance; 233 uint32_t mSpiResetDelay; 234 uint16_t mSpiCsDelayUs; 235 uint16_t mSpiSmallPacketSize; 236 uint32_t mSpiSpeedHz; 237 238 uint64_t mSlaveResetCount; 239 uint64_t mSpiDuplexFrameCount; 240 uint64_t mSpiUnresponsiveFrameCount; 241 242 bool mSpiTxIsReady; 243 uint16_t mSpiTxRefusedCount; 244 uint16_t mSpiTxPayloadSize; 245 uint8_t mSpiTxFrameBuffer[kMaxFrameSize + kSpiAlignAllowanceMax]; 246 247 bool mDidPrintRateLimitLog; 248 uint16_t mSpiSlaveDataLen; 249 250 bool mDidRxFrame; 251 252 otRcpInterfaceMetrics mInterfaceMetrics; 253 254 // Non-copyable, intentionally not implemented. 255 SpiInterface(const SpiInterface &); 256 SpiInterface &operator=(const SpiInterface &); 257 }; 258 259 } // namespace Posix 260 } // namespace ot 261 262 #endif // OPENTHREAD_POSIX_CONFIG_RCP_BUS == OT_POSIX_RCP_BUS_SPI 263 #endif // POSIX_APP_SPI_INTERFACE_HPP_ 264