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