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