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