1 /* 2 * Copyright (c) 2024, 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 #ifndef SPINEL_DRIVER_HPP_ 30 #define SPINEL_DRIVER_HPP_ 31 32 #include <openthread/instance.h> 33 34 #include "lib/spinel/coprocessor_type.h" 35 #include "lib/spinel/logger.hpp" 36 #include "lib/spinel/spinel.h" 37 #include "lib/spinel/spinel_interface.hpp" 38 39 namespace ot { 40 namespace Spinel { 41 42 /** 43 * Maximum number of Spinel Interface IDs. 44 * 45 */ 46 #if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE 47 static constexpr uint8_t kSpinelHeaderMaxNumIid = 4; 48 #else 49 static constexpr uint8_t kSpinelHeaderMaxNumIid = 1; 50 #endif 51 52 class SpinelDriver : public Logger 53 { 54 public: 55 typedef void ( 56 *ReceivedFrameHandler)(const uint8_t *aFrame, uint16_t aLength, uint8_t aHeader, bool &aSave, void *aContext); 57 typedef void (*SavedFrameHandler)(const uint8_t *aFrame, uint16_t aLength, void *aContext); 58 59 /** 60 * Constructor of the SpinelDriver. 61 * 62 */ 63 SpinelDriver(void); 64 65 /** 66 * Initialize this SpinelDriver Instance. 67 * 68 * @param[in] aSpinelInterface A reference to the Spinel interface. 69 * @param[in] aSoftwareReset TRUE to reset on init, FALSE to not reset on init. 70 * @param[in] aIidList A Pointer to the list of IIDs to receive spinel frame from. 71 * First entry must be the IID of the Host Application. 72 * @param[in] aIidListLength The Length of the @p aIidList. 73 * 74 * @retval OT_COPROCESSOR_UNKNOWN The initialization fails. 75 * @retval OT_COPROCESSOR_RCP The Co-processor is a RCP. 76 * @retval OT_COPROCESSOR_NCP The Co-processor is a NCP. 77 * 78 */ 79 CoprocessorType Init(SpinelInterface &aSpinelInterface, 80 bool aSoftwareReset, 81 const spinel_iid_t *aIidList, 82 uint8_t aIidListLength); 83 84 /** 85 * Deinitialize this SpinelDriver Instance. 86 * 87 */ 88 void Deinit(void); 89 90 /** 91 * Clear the rx frame buffer. 92 * 93 */ ClearRxBuffer(void)94 void ClearRxBuffer(void) { mRxFrameBuffer.Clear(); } 95 96 /** 97 * Set the internal state of co-processor as ready. 98 * 99 * This method is used to skip a reset. 100 */ SetCoprocessorReady(void)101 void SetCoprocessorReady(void) { mIsCoprocessorReady = true; } 102 103 /** 104 * Send a reset command to the co-processor. 105 * 106 * @prarm[in] aResetType The reset type, SPINEL_RESET_PLATFORM, SPINEL_RESET_STACK, or SPINEL_RESET_BOOTLOADER. 107 * 108 * @retval OT_ERROR_NONE Successfully removed item from the property. 109 * @retval OT_ERROR_BUSY Failed due to another operation is on going. 110 * 111 */ 112 otError SendReset(uint8_t aResetType); 113 114 /** 115 * Reset the co-processor. 116 * 117 * This method will reset the co-processor and wait until the co-process is ready (receiving SPINEL_PROP_LAST_STATUS 118 * from the it). The reset will be either a software or hardware reset. If `aSoftwareReset` is `true`, then the 119 * method will first try a software reset. If the software reset succeeds, the method exits. Otherwise the method 120 * will then try a hardware reset. If `aSoftwareReset` is `false`, then method will directly try a hardware reset. 121 * 122 * @param[in] aSoftwareReset TRUE to try SW reset first, FALSE to directly try HW reset. 123 * 124 */ 125 void ResetCoprocessor(bool aSoftwareReset); 126 127 /** 128 * Processes any pending the I/O data. 129 * 130 * The method should be called by the system loop to process received spinel frames. 131 * 132 * @param[in] aContext The process context. 133 * 134 */ 135 void Process(const void *aContext); 136 137 /** 138 * Checks whether there is pending frame in the buffer. 139 * 140 * The method is required by the system loop to update timer fd. 141 * 142 * @returns Whether there is pending frame in the buffer. 143 * 144 */ HasPendingFrame(void) const145 bool HasPendingFrame(void) const { return mRxFrameBuffer.HasSavedFrame(); } 146 147 /** 148 * Returns the co-processor sw version string. 149 * 150 * @returns A pointer to the co-processor version string. 151 * 152 */ GetVersion(void) const153 const char *GetVersion(void) const { return mVersion; } 154 155 /* 156 * Sends a spinel command to the co-processor. 157 * 158 * @param[in] aCommand The spinel command. 159 * @param[in] aKey The spinel property key. 160 * @param[in] aTid The spinel transaction id. 161 * @param[in] aFormat The format string of the arguments to send. 162 * @param[in] aArgs The argument list. 163 * 164 * @retval OT_ERROR_NONE Successfully sent the command through spinel interface. 165 * @retval OT_ERROR_INVALID_STATE The spinel interface is in an invalid state. 166 * @retval OT_ERROR_NO_BUFS The spinel interface doesn't have enough buffer. 167 * 168 */ 169 otError SendCommand(uint32_t aCommand, 170 spinel_prop_key_t aKey, 171 spinel_tid_t aTid, 172 const char *aFormat, 173 va_list aArgs); 174 175 /* 176 * Sets the handler to process the received spinel frame. 177 * 178 * @param[in] aReceivedFrameHandler The handler to process received spinel frames. 179 * @param[in] aSavedFrameHandler The handler to process saved spinel frames. 180 * @param[in] aContext The context to call the handler. 181 * 182 */ 183 void SetFrameHandler(ReceivedFrameHandler aReceivedFrameHandler, 184 SavedFrameHandler aSavedFrameHandler, 185 void *aContext); 186 187 /* 188 * Returns the spinel interface. 189 * 190 * @returns A pointer to the spinel interface object. 191 * 192 */ GetSpinelInterface(void) const193 SpinelInterface *GetSpinelInterface(void) const { return mSpinelInterface; } 194 195 /** 196 * Returns if the co-processor has some capability 197 * 198 * @param[in] aCapability The capability queried. 199 * 200 * @returns `true` if the co-processor has the capability. `false` otherwise. 201 * 202 */ CoprocessorHasCap(unsigned int aCapability)203 bool CoprocessorHasCap(unsigned int aCapability) { return mCoprocessorCaps.Contains(aCapability); } 204 205 private: 206 static constexpr uint16_t kMaxSpinelFrame = SPINEL_FRAME_MAX_SIZE; 207 static constexpr uint16_t kVersionStringSize = 128; 208 static constexpr uint32_t kUsPerMs = 1000; ///< Microseconds per millisecond. 209 static constexpr uint32_t kMaxWaitTime = 2000; ///< Max time to wait for response in milliseconds. 210 static constexpr uint16_t kCapsBufferSize = 100; ///< Max buffer size used to store `SPINEL_PROP_CAPS` value. 211 212 /** 213 * Represents an array of elements with a fixed max size. 214 * 215 * @tparam Type The array element type. 216 * @tparam kMaxSize Specifies the max array size (maximum number of elements in the array). 217 * 218 */ 219 template <typename Type, uint16_t kMaxSize> class Array 220 { 221 static_assert(kMaxSize != 0, "Array `kMaxSize` cannot be zero"); 222 223 public: Array(void)224 Array(void) 225 : mLength(0) 226 { 227 } 228 GetMaxSize(void) const229 uint16_t GetMaxSize(void) const { return kMaxSize; } 230 IsFull(void) const231 bool IsFull(void) const { return (mLength == GetMaxSize()); } 232 PushBack(const Type & aEntry)233 otError PushBack(const Type &aEntry) 234 { 235 return IsFull() ? OT_ERROR_NO_BUFS : (mElements[mLength++] = aEntry, OT_ERROR_NONE); 236 } 237 Find(const Type & aEntry) const238 const Type *Find(const Type &aEntry) const 239 { 240 const Type *matched = nullptr; 241 242 for (const Type &element : *this) 243 { 244 if (element == aEntry) 245 { 246 matched = &element; 247 break; 248 } 249 } 250 251 return matched; 252 } 253 Contains(const Type & aEntry) const254 bool Contains(const Type &aEntry) const { return Find(aEntry) != nullptr; } 255 begin(void)256 Type *begin(void) { return &mElements[0]; } end(void)257 Type *end(void) { return &mElements[mLength]; } begin(void) const258 const Type *begin(void) const { return &mElements[0]; } end(void) const259 const Type *end(void) const { return &mElements[mLength]; } 260 261 private: 262 Type mElements[kMaxSize]; 263 uint16_t mLength; 264 }; 265 266 otError WaitResponse(void); 267 268 static void HandleReceivedFrame(void *aContext); 269 void HandleReceivedFrame(void); 270 271 static void HandleInitialFrame(const uint8_t *aFrame, 272 uint16_t aLength, 273 uint8_t aHeader, 274 bool &aSave, 275 void *aContext); 276 void HandleInitialFrame(const uint8_t *aFrame, uint16_t aLength, uint8_t aHeader, bool &aSave); 277 278 otError SendCommand(uint32_t aCommand, spinel_prop_key_t aKey, spinel_tid_t aTid); 279 280 otError CheckSpinelVersion(void); 281 otError GetCoprocessorVersion(void); 282 otError GetCoprocessorCaps(void); 283 CoprocessorType GetCoprocessorType(void); 284 285 void ProcessFrameQueue(void); 286 287 SpinelInterface::RxFrameBuffer mRxFrameBuffer; 288 SpinelInterface *mSpinelInterface; 289 290 spinel_prop_key_t mWaitingKey; ///< The property key of current transaction. 291 bool mIsWaitingForResponse; 292 293 spinel_iid_t mIid; 294 Array<spinel_iid_t, kSpinelHeaderMaxNumIid> mIidList; 295 296 ReceivedFrameHandler mReceivedFrameHandler; 297 SavedFrameHandler mSavedFrameHandler; 298 void *mFrameHandlerContext; 299 300 int mSpinelVersionMajor; 301 int mSpinelVersionMinor; 302 303 bool mIsCoprocessorReady; 304 char mVersion[kVersionStringSize]; 305 306 Array<unsigned int, kCapsBufferSize> mCoprocessorCaps; 307 }; 308 309 } // namespace Spinel 310 } // namespace ot 311 312 #endif // SPINEL_DRIVER_HPP_ 313