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 OT_POSIX_PLATFORM_SPI_INTERFACE_HPP_ 35 #define OT_POSIX_PLATFORM_SPI_INTERFACE_HPP_ 36 37 #include "openthread-posix-config.h" 38 39 #include "logger.hpp" 40 #include "platform-posix.h" 41 #include "lib/hdlc/hdlc.hpp" 42 #include "lib/spinel/multi_frame_buffer.hpp" 43 #include "lib/spinel/spi_frame.hpp" 44 #include "lib/spinel/spinel_interface.hpp" 45 46 #include <openthread/openthread-system.h> 47 48 namespace ot { 49 namespace Posix { 50 51 /** 52 * Defines an SPI interface to the Radio Co-processor (RCP). 53 * 54 */ 55 class SpiInterface : public ot::Spinel::SpinelInterface, public Logger<SpiInterface> 56 { 57 public: 58 static const char kLogModuleName[]; ///< Module name used for logging. 59 60 /** 61 * Initializes the object. 62 * 63 * @param[in] aRadioUrl RadioUrl parsed from radio url. 64 * 65 */ 66 SpiInterface(const Url::Url &aRadioUrl); 67 68 /** 69 * This destructor deinitializes the object. 70 * 71 */ 72 ~SpiInterface(void); 73 74 /** 75 * Initializes the interface to the Radio Co-processor (RCP). 76 * 77 * @note This method should be called before reading and sending spinel frames to the interface. 78 * 79 * @param[in] aCallback Callback on frame received 80 * @param[in] aCallbackContext Callback context 81 * @param[in] aFrameBuffer A reference to a `RxFrameBuffer` object. 82 * 83 * @retval OT_ERROR_NONE The interface is initialized successfully 84 * @retval OT_ERROR_ALREADY The interface is already initialized. 85 * @retval OT_ERROR_FAILED Failed to initialize the interface. 86 * 87 */ 88 otError Init(ReceiveFrameCallback aCallback, void *aCallbackContext, RxFrameBuffer &aFrameBuffer); 89 90 /** 91 * Deinitializes the interface to the RCP. 92 * 93 */ 94 void Deinit(void); 95 96 /** 97 * Encodes and sends a spinel frame to Radio Co-processor (RCP) over the socket. 98 * 99 * @param[in] aFrame A pointer to buffer containing the spinel frame to send. 100 * @param[in] aLength The length (number of bytes) in the frame. 101 * 102 * @retval OT_ERROR_NONE Successfully encoded and sent the spinel frame. 103 * @retval OT_ERROR_BUSY Failed due to another operation is on going. 104 * @retval OT_ERROR_NO_BUFS Insufficient buffer space available to encode the frame. 105 * @retval OT_ERROR_FAILED Failed to call the SPI driver to send the frame. 106 * 107 */ 108 otError SendFrame(const uint8_t *aFrame, uint16_t aLength); 109 110 /** 111 * Waits for receiving part or all of spinel frame within specified interval. 112 * 113 * @param[in] aTimeout The timeout value in microseconds. 114 * 115 * @retval OT_ERROR_NONE Part or all of spinel frame is received. 116 * @retval OT_ERROR_RESPONSE_TIMEOUT No spinel frame is received within @p aTimeout. 117 * 118 */ 119 otError WaitForFrame(uint64_t aTimeoutUs); 120 121 /** 122 * Updates the file descriptor sets with file descriptors used by the radio driver. 123 * 124 * @param[in,out] aMainloopContext A pointer to the mainloop context containing fd_sets. 125 * 126 */ 127 void UpdateFdSet(void *aMainloopContext); 128 129 /** 130 * Performs radio driver processing. 131 * 132 * @param[in] aMainloopContext A pointer to the mainloop context containing fd_sets. 133 * 134 */ 135 void Process(const void *aMainloopContext); 136 137 /** 138 * Returns the bus speed between the host and the radio. 139 * 140 * @returns Bus speed in bits/second. 141 * 142 */ GetBusSpeed(void) const143 uint32_t GetBusSpeed(void) const { return ((mSpiDevFd >= 0) ? mSpiSpeedHz : 0); } 144 145 /** 146 * Hardware resets the RCP. 147 * 148 * @retval OT_ERROR_NONE Successfully reset the RCP. 149 * @retval OT_ERROR_NOT_IMPLEMENT The hardware reset is not implemented. 150 * 151 */ 152 otError HardwareReset(void); 153 154 /** 155 * Returns the RCP interface metrics. 156 * 157 * @returns The RCP interface metrics. 158 * 159 */ GetRcpInterfaceMetrics(void) const160 const otRcpInterfaceMetrics *GetRcpInterfaceMetrics(void) const { return &mInterfaceMetrics; } 161 162 /** 163 * Indicates whether or not the given interface matches this interface name. 164 * 165 * @param[in] aInterfaceName A pointer to the interface name. 166 * 167 * @retval TRUE The given interface name matches this interface name. 168 * @retval FALSE The given interface name doesn't match this interface name. 169 */ IsInterfaceNameMatch(const char * aInterfaceName)170 static bool IsInterfaceNameMatch(const char *aInterfaceName) 171 { 172 static const char kInterfaceName[] = "spinel+spi"; 173 return (strncmp(aInterfaceName, kInterfaceName, strlen(kInterfaceName)) == 0); 174 } 175 176 private: 177 void ResetStates(void); 178 int SetupGpioHandle(int aFd, uint8_t aLine, uint32_t aHandleFlags, const char *aLabel); 179 int SetupGpioEvent(int aFd, uint8_t aLine, uint32_t aHandleFlags, uint32_t aEventFlags, const char *aLabel); 180 void SetGpioValue(int aFd, uint8_t aValue); 181 uint8_t GetGpioValue(int aFd); 182 183 void InitResetPin(const char *aCharDev, uint8_t aLine); 184 void InitIntPin(const char *aCharDev, uint8_t aLine); 185 void InitSpiDev(const char *aPath, uint8_t aMode, uint32_t aSpeed); 186 void TriggerReset(void); 187 188 uint8_t *GetRealRxFrameStart(uint8_t *aSpiRxFrameBuffer, uint8_t aAlignAllowance, uint16_t &aSkipLength); 189 otError DoSpiTransfer(uint8_t *aSpiRxFrameBuffer, uint32_t aTransferLength); 190 otError PushPullSpi(void); 191 192 bool CheckInterrupt(void); 193 void LogStats(void); 194 void LogError(const char *aString); 195 void LogBuffer(const char *aDesc, const uint8_t *aBuffer, uint16_t aLength, bool aForce); 196 197 enum 198 { 199 kSpiModeMax = 3, 200 kSpiAlignAllowanceMax = 16, 201 kSpiFrameHeaderSize = 5, 202 kSpiBitsPerWord = 8, 203 kSpiTxRefuseWarnCount = 30, 204 kSpiTxRefuseExitCount = 100, 205 kImmediateRetryCount = 5, 206 kFastRetryCount = 15, 207 kDebugBytesPerLine = 16, 208 kGpioIntAssertState = 0, 209 kGpioResetAssertState = 0, 210 }; 211 212 enum 213 { 214 kMsecPerSec = 1000, 215 kUsecPerMsec = 1000, 216 kSpiPollPeriodUs = kMsecPerSec * kUsecPerMsec / 30, 217 kSecPerDay = 60 * 60 * 24, 218 kResetHoldOnUsec = 10 * kUsecPerMsec, 219 kImmediateRetryTimeoutUs = 1 * kUsecPerMsec, 220 kFastRetryTimeoutUs = 10 * kUsecPerMsec, 221 kSlowRetryTimeoutUs = 33 * kUsecPerMsec, 222 }; 223 224 ReceiveFrameCallback mReceiveFrameCallback; 225 void *mReceiveFrameContext; 226 RxFrameBuffer *mRxFrameBuffer; 227 const Url::Url &mRadioUrl; 228 229 int mSpiDevFd; 230 int mResetGpioValueFd; 231 int mIntGpioValueFd; 232 233 uint8_t mSpiMode; 234 uint8_t mSpiAlignAllowance; 235 uint32_t mSpiResetDelay; 236 uint16_t mSpiCsDelayUs; 237 uint16_t mSpiSmallPacketSize; 238 uint32_t mSpiSpeedHz; 239 240 uint64_t mSlaveResetCount; 241 uint64_t mSpiDuplexFrameCount; 242 uint64_t mSpiUnresponsiveFrameCount; 243 244 bool mSpiTxIsReady; 245 uint16_t mSpiTxRefusedCount; 246 uint16_t mSpiTxPayloadSize; 247 uint8_t mSpiTxFrameBuffer[kMaxFrameSize + kSpiAlignAllowanceMax]; 248 249 bool mDidPrintRateLimitLog; 250 uint16_t mSpiSlaveDataLen; 251 252 bool mDidRxFrame; 253 254 otRcpInterfaceMetrics mInterfaceMetrics; 255 256 // Non-copyable, intentionally not implemented. 257 SpiInterface(const SpiInterface &); 258 SpiInterface &operator=(const SpiInterface &); 259 }; 260 261 } // namespace Posix 262 } // namespace ot 263 264 #endif // OT_POSIX_PLATFORM_SPI_INTERFACE_HPP_ 265