• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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