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