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