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