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 #include "spinel_driver.hpp"
30 
31 #include <assert.h>
32 
33 #include <openthread/platform/time.h>
34 
35 #include "lib/platform/exit_code.h"
36 #include "lib/spinel/spinel.h"
37 #include "lib/utils/math.hpp"
38 #include "lib/utils/utils.hpp"
39 
40 namespace ot {
41 namespace Spinel {
42 
43 constexpr spinel_tid_t sTid = 1; ///< In Spinel Driver, only use Tid as 1.
44 
SpinelDriver(void)45 SpinelDriver::SpinelDriver(void)
46     : Logger("SpinelDriver")
47     , mSpinelInterface(nullptr)
48     , mWaitingKey(SPINEL_PROP_LAST_STATUS)
49     , mIsWaitingForResponse(false)
50     , mIid(SPINEL_HEADER_INVALID_IID)
51     , mSpinelVersionMajor(-1)
52     , mSpinelVersionMinor(-1)
53     , mIsCoprocessorReady(false)
54 {
55     memset(mVersion, 0, sizeof(mVersion));
56 
57     mReceivedFrameHandler = &HandleInitialFrame;
58     mFrameHandlerContext  = this;
59 }
60 
Init(SpinelInterface & aSpinelInterface,bool aSoftwareReset,const spinel_iid_t * aIidList,uint8_t aIidListLength)61 CoprocessorType SpinelDriver::Init(SpinelInterface    &aSpinelInterface,
62                                    bool                aSoftwareReset,
63                                    const spinel_iid_t *aIidList,
64                                    uint8_t             aIidListLength)
65 {
66     CoprocessorType coprocessorType;
67 
68     mSpinelInterface = &aSpinelInterface;
69     mRxFrameBuffer.Clear();
70     SuccessOrDie(mSpinelInterface->Init(HandleReceivedFrame, this, mRxFrameBuffer));
71 
72     VerifyOrDie(aIidList != nullptr, OT_EXIT_INVALID_ARGUMENTS);
73     VerifyOrDie(aIidListLength != 0 && aIidListLength <= mIidList.GetMaxSize(), OT_EXIT_INVALID_ARGUMENTS);
74 
75     for (uint8_t i = 0; i < aIidListLength; i++)
76     {
77         SuccessOrDie(mIidList.PushBack(aIidList[i]));
78     }
79     mIid = aIidList[0];
80 
81     ResetCoprocessor(aSoftwareReset);
82     SuccessOrDie(CheckSpinelVersion());
83     SuccessOrDie(GetCoprocessorVersion());
84     SuccessOrDie(GetCoprocessorCaps());
85 
86     coprocessorType = GetCoprocessorType();
87     if (coprocessorType == OT_COPROCESSOR_UNKNOWN)
88     {
89         LogCrit("The coprocessor mode is unknown!");
90         DieNow(OT_EXIT_FAILURE);
91     }
92 
93     return coprocessorType;
94 }
95 
Deinit(void)96 void SpinelDriver::Deinit(void)
97 {
98     // This allows implementing pseudo reset.
99     new (this) SpinelDriver();
100 }
101 
SendReset(uint8_t aResetType)102 otError SpinelDriver::SendReset(uint8_t aResetType)
103 {
104     otError        error = OT_ERROR_NONE;
105     uint8_t        buffer[kMaxSpinelFrame];
106     spinel_ssize_t packed;
107 
108     // Pack the header, command and key
109     packed = spinel_datatype_pack(buffer, sizeof(buffer), SPINEL_DATATYPE_COMMAND_S SPINEL_DATATYPE_UINT8_S,
110                                   SPINEL_HEADER_FLAG | SPINEL_HEADER_IID(mIid), SPINEL_CMD_RESET, aResetType);
111 
112     EXPECT(packed > 0 && static_cast<size_t>(packed) <= sizeof(buffer), error = OT_ERROR_NO_BUFS);
113 
114     EXPECT_NO_ERROR(error = mSpinelInterface->SendFrame(buffer, static_cast<uint16_t>(packed)));
115     LogSpinelFrame(buffer, static_cast<uint16_t>(packed), true /* aTx */);
116 
117 exit:
118     return error;
119 }
120 
ResetCoprocessor(bool aSoftwareReset)121 void SpinelDriver::ResetCoprocessor(bool aSoftwareReset)
122 {
123     bool hardwareReset;
124     bool resetDone = false;
125 
126     // Avoid resetting the device twice in a row in Multipan RCP architecture
127     EXPECT(!mIsCoprocessorReady, resetDone = true);
128 
129     mWaitingKey = SPINEL_PROP_LAST_STATUS;
130 
131     if (aSoftwareReset && (SendReset(SPINEL_RESET_STACK) == OT_ERROR_NONE) && (WaitResponse() == OT_ERROR_NONE))
132     {
133         EXPECT(mIsCoprocessorReady, resetDone = false);
134         LogCrit("Software reset co-processor successfully");
135         EXIT_NOW(resetDone = true);
136     }
137 
138     hardwareReset = (mSpinelInterface->HardwareReset() == OT_ERROR_NONE);
139 
140     if (hardwareReset)
141     {
142         EXPECT_NO_ERROR(WaitResponse());
143     }
144 
145     resetDone = true;
146 
147     if (hardwareReset)
148     {
149         LogInfo("Hardware reset co-processor successfully");
150     }
151     else
152     {
153         LogInfo("co-processor self reset successfully");
154     }
155 
156 exit:
157     if (!resetDone)
158     {
159         LogCrit("Failed to reset co-processor!");
160         DieNow(OT_EXIT_FAILURE);
161     }
162 }
163 
Process(const void * aContext)164 void SpinelDriver::Process(const void *aContext)
165 {
166     if (mRxFrameBuffer.HasSavedFrame())
167     {
168         ProcessFrameQueue();
169     }
170 
171     mSpinelInterface->Process(aContext);
172 
173     if (mRxFrameBuffer.HasSavedFrame())
174     {
175         ProcessFrameQueue();
176     }
177 }
178 
SendCommand(uint32_t aCommand,spinel_prop_key_t aKey,spinel_tid_t aTid)179 otError SpinelDriver::SendCommand(uint32_t aCommand, spinel_prop_key_t aKey, spinel_tid_t aTid)
180 {
181     otError        error = OT_ERROR_NONE;
182     uint8_t        buffer[kMaxSpinelFrame];
183     spinel_ssize_t packed;
184     uint16_t       offset;
185 
186     // Pack the header, command and key
187     packed = spinel_datatype_pack(buffer, sizeof(buffer), "Cii", SPINEL_HEADER_FLAG | SPINEL_HEADER_IID(mIid) | aTid,
188                                   aCommand, aKey);
189 
190     EXPECT(packed > 0 && static_cast<size_t>(packed) <= sizeof(buffer), error = OT_ERROR_NO_BUFS);
191 
192     offset = static_cast<uint16_t>(packed);
193 
194     EXPECT_NO_ERROR(error = mSpinelInterface->SendFrame(buffer, offset));
195     LogSpinelFrame(buffer, offset, true /* aTx */);
196 
197 exit:
198     return error;
199 }
200 
SendCommand(uint32_t aCommand,spinel_prop_key_t aKey,spinel_tid_t aTid,const char * aFormat,va_list aArgs)201 otError SpinelDriver::SendCommand(uint32_t          aCommand,
202                                   spinel_prop_key_t aKey,
203                                   spinel_tid_t      aTid,
204                                   const char       *aFormat,
205                                   va_list           aArgs)
206 {
207     otError        error = OT_ERROR_NONE;
208     uint8_t        buffer[kMaxSpinelFrame];
209     spinel_ssize_t packed;
210     uint16_t       offset;
211 
212     // Pack the header, command and key
213     packed = spinel_datatype_pack(buffer, sizeof(buffer), "Cii", SPINEL_HEADER_FLAG | SPINEL_HEADER_IID(mIid) | aTid,
214                                   aCommand, aKey);
215 
216     EXPECT(packed > 0 && static_cast<size_t>(packed) <= sizeof(buffer), error = OT_ERROR_NO_BUFS);
217 
218     offset = static_cast<uint16_t>(packed);
219 
220     // Pack the data (if any)
221     if (aFormat)
222     {
223         packed = spinel_datatype_vpack(buffer + offset, sizeof(buffer) - offset, aFormat, aArgs);
224         EXPECT(packed > 0 && static_cast<size_t>(packed + offset) <= sizeof(buffer), error = OT_ERROR_NO_BUFS);
225 
226         offset += static_cast<uint16_t>(packed);
227     }
228 
229     EXPECT_NO_ERROR(error = mSpinelInterface->SendFrame(buffer, offset));
230     LogSpinelFrame(buffer, offset, true /* aTx */);
231 
232 exit:
233     return error;
234 }
235 
SetFrameHandler(ReceivedFrameHandler aReceivedFrameHandler,SavedFrameHandler aSavedFrameHandler,void * aContext)236 void SpinelDriver::SetFrameHandler(ReceivedFrameHandler aReceivedFrameHandler,
237                                    SavedFrameHandler    aSavedFrameHandler,
238                                    void                *aContext)
239 {
240     mReceivedFrameHandler = aReceivedFrameHandler;
241     mSavedFrameHandler    = aSavedFrameHandler;
242     mFrameHandlerContext  = aContext;
243 }
244 
WaitResponse(void)245 otError SpinelDriver::WaitResponse(void)
246 {
247     otError  error = OT_ERROR_NONE;
248     uint64_t end   = otPlatTimeGet() + kMaxWaitTime * kUsPerMs;
249 
250     LogDebg("Waiting response: key=%lu", Lib::Utils::ToUlong(mWaitingKey));
251 
252     do
253     {
254         uint64_t now = otPlatTimeGet();
255 
256         if ((end <= now) || (mSpinelInterface->WaitForFrame(end - now) != OT_ERROR_NONE))
257         {
258             LogWarn("Wait for response timeout");
259             EXIT_NOW(error = OT_ERROR_RESPONSE_TIMEOUT);
260         }
261     } while (mIsWaitingForResponse || !mIsCoprocessorReady);
262 
263     mWaitingKey = SPINEL_PROP_LAST_STATUS;
264 
265 exit:
266     return error;
267 }
268 
HandleReceivedFrame(void * aContext)269 void SpinelDriver::HandleReceivedFrame(void *aContext) { static_cast<SpinelDriver *>(aContext)->HandleReceivedFrame(); }
270 
HandleReceivedFrame(void)271 void SpinelDriver::HandleReceivedFrame(void)
272 {
273     otError        error = OT_ERROR_NONE;
274     uint8_t        header;
275     spinel_ssize_t unpacked;
276     bool           shouldSave = true;
277     spinel_iid_t   iid;
278 
279     LogSpinelFrame(mRxFrameBuffer.GetFrame(), mRxFrameBuffer.GetLength(), false);
280     unpacked = spinel_datatype_unpack(mRxFrameBuffer.GetFrame(), mRxFrameBuffer.GetLength(), "C", &header);
281 
282     // Accept spinel messages with the correct IID or broadcast IID.
283     iid = SPINEL_HEADER_GET_IID(header);
284 
285     if (!mIidList.Contains(iid))
286     {
287         mRxFrameBuffer.DiscardFrame();
288         EXIT_NOW();
289     }
290 
291     EXPECT(unpacked > 0 && (header & SPINEL_HEADER_FLAG) == SPINEL_HEADER_FLAG, error = OT_ERROR_PARSE);
292 
293     assert(mReceivedFrameHandler != nullptr && mFrameHandlerContext != nullptr);
294     mReceivedFrameHandler(mRxFrameBuffer.GetFrame(), mRxFrameBuffer.GetLength(), header, shouldSave,
295                           mFrameHandlerContext);
296 
297     if (shouldSave)
298     {
299         error = mRxFrameBuffer.SaveFrame();
300     }
301     else
302     {
303         mRxFrameBuffer.DiscardFrame();
304     }
305 
306 exit:
307     if (error != OT_ERROR_NONE)
308     {
309         mRxFrameBuffer.DiscardFrame();
310         LogWarn("Error handling spinel frame: %s", otThreadErrorToString(error));
311     }
312 }
313 
HandleInitialFrame(const uint8_t * aFrame,uint16_t aLength,uint8_t aHeader,bool & aSave,void * aContext)314 void SpinelDriver::HandleInitialFrame(const uint8_t *aFrame,
315                                       uint16_t       aLength,
316                                       uint8_t        aHeader,
317                                       bool          &aSave,
318                                       void          *aContext)
319 {
320     static_cast<SpinelDriver *>(aContext)->HandleInitialFrame(aFrame, aLength, aHeader, aSave);
321 }
322 
HandleInitialFrame(const uint8_t * aFrame,uint16_t aLength,uint8_t aHeader,bool & aSave)323 void SpinelDriver::HandleInitialFrame(const uint8_t *aFrame, uint16_t aLength, uint8_t aHeader, bool &aSave)
324 {
325     spinel_prop_key_t key;
326     uint8_t          *data   = nullptr;
327     spinel_size_t     len    = 0;
328     uint8_t           header = 0;
329     uint32_t          cmd    = 0;
330     spinel_ssize_t    rval   = 0;
331     spinel_ssize_t    unpacked;
332     otError           error = OT_ERROR_NONE;
333 
334     OT_UNUSED_VARIABLE(aHeader);
335 
336     rval = spinel_datatype_unpack(aFrame, aLength, "CiiD", &header, &cmd, &key, &data, &len);
337     EXPECT(rval > 0 && cmd >= SPINEL_CMD_PROP_VALUE_IS && cmd <= SPINEL_CMD_PROP_VALUE_REMOVED, error = OT_ERROR_PARSE);
338 
339     EXPECT(cmd == SPINEL_CMD_PROP_VALUE_IS, error = OT_ERROR_DROP);
340 
341     if (key == SPINEL_PROP_LAST_STATUS)
342     {
343         spinel_status_t status = SPINEL_STATUS_OK;
344 
345         unpacked = spinel_datatype_unpack(data, len, "i", &status);
346         EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
347 
348         if (status >= SPINEL_STATUS_RESET__BEGIN && status <= SPINEL_STATUS_RESET__END)
349         {
350             // this clear is necessary in case the RCP has sent messages between disable and reset
351             mRxFrameBuffer.Clear();
352 
353             LogInfo("co-processor reset: %s", spinel_status_to_cstr(status));
354             mIsCoprocessorReady = true;
355         }
356         else
357         {
358             LogInfo("co-processor last status: %s", spinel_status_to_cstr(status));
359             EXIT_NOW();
360         }
361     }
362     else
363     {
364         // Drop other frames when the key isn't waiting key.
365         EXPECT(mWaitingKey == key, error = OT_ERROR_DROP);
366 
367         if (key == SPINEL_PROP_PROTOCOL_VERSION)
368         {
369             unpacked = spinel_datatype_unpack(data, len, (SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT_PACKED_S),
370                                               &mSpinelVersionMajor, &mSpinelVersionMinor);
371 
372             EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
373         }
374         else if (key == SPINEL_PROP_NCP_VERSION)
375         {
376             unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_UTF8_S, mVersion, sizeof(mVersion));
377 
378             EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
379         }
380         else if (key == SPINEL_PROP_CAPS)
381         {
382             uint8_t        capsBuffer[kCapsBufferSize];
383             spinel_size_t  capsLength = sizeof(capsBuffer);
384             const uint8_t *capsData   = capsBuffer;
385 
386             unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_DATA_S, capsBuffer, &capsLength);
387 
388             EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
389 
390             while (capsLength > 0)
391             {
392                 unsigned int capability;
393 
394                 unpacked = spinel_datatype_unpack(capsData, capsLength, SPINEL_DATATYPE_UINT_PACKED_S, &capability);
395                 EXPECT(unpacked > 0, error = OT_ERROR_PARSE);
396 
397                 EXPECT_NO_ERROR(error = mCoprocessorCaps.PushBack(capability));
398 
399                 capsData += unpacked;
400                 capsLength -= static_cast<spinel_size_t>(unpacked);
401             }
402         }
403 
404         mIsWaitingForResponse = false;
405     }
406 
407 exit:
408     aSave = false;
409     LogIfFail("Error processing frame", error);
410 }
411 
CheckSpinelVersion(void)412 otError SpinelDriver::CheckSpinelVersion(void)
413 {
414     otError error = OT_ERROR_NONE;
415 
416     EXPECT_NO_ERROR(error = SendCommand(SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_PROTOCOL_VERSION, sTid));
417     mIsWaitingForResponse = true;
418     mWaitingKey           = SPINEL_PROP_PROTOCOL_VERSION;
419 
420     EXPECT_NO_ERROR(error = WaitResponse());
421 
422     if ((mSpinelVersionMajor != SPINEL_PROTOCOL_VERSION_THREAD_MAJOR) ||
423         (mSpinelVersionMinor != SPINEL_PROTOCOL_VERSION_THREAD_MINOR))
424     {
425         LogCrit("Spinel version mismatch - Posix:%d.%d, co-processor:%d.%d", SPINEL_PROTOCOL_VERSION_THREAD_MAJOR,
426                 SPINEL_PROTOCOL_VERSION_THREAD_MINOR, mSpinelVersionMajor, mSpinelVersionMinor);
427         DieNow(OT_EXIT_RADIO_SPINEL_INCOMPATIBLE);
428     }
429 
430 exit:
431     return error;
432 }
433 
GetCoprocessorVersion(void)434 otError SpinelDriver::GetCoprocessorVersion(void)
435 {
436     otError error = OT_ERROR_NONE;
437 
438     EXPECT_NO_ERROR(error = SendCommand(SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_NCP_VERSION, sTid));
439     mIsWaitingForResponse = true;
440     mWaitingKey           = SPINEL_PROP_NCP_VERSION;
441 
442     EXPECT_NO_ERROR(error = WaitResponse());
443 exit:
444     return error;
445 }
446 
GetCoprocessorCaps(void)447 otError SpinelDriver::GetCoprocessorCaps(void)
448 {
449     otError error = OT_ERROR_NONE;
450 
451     EXPECT_NO_ERROR(error = SendCommand(SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_CAPS, sTid));
452     mIsWaitingForResponse = true;
453     mWaitingKey           = SPINEL_PROP_CAPS;
454 
455     EXPECT_NO_ERROR(error = WaitResponse());
456 exit:
457     return error;
458 }
459 
GetCoprocessorType(void)460 CoprocessorType SpinelDriver::GetCoprocessorType(void)
461 {
462     CoprocessorType type = OT_COPROCESSOR_UNKNOWN;
463 
464     if (CoprocessorHasCap(SPINEL_CAP_CONFIG_RADIO))
465     {
466         type = OT_COPROCESSOR_RCP;
467     }
468     else if (CoprocessorHasCap(SPINEL_CAP_CONFIG_FTD) || CoprocessorHasCap(SPINEL_CAP_CONFIG_MTD))
469     {
470         type = OT_COPROCESSOR_NCP;
471     }
472 
473     return type;
474 }
475 
ProcessFrameQueue(void)476 void SpinelDriver::ProcessFrameQueue(void)
477 {
478     uint8_t *frame = nullptr;
479     uint16_t length;
480 
481     assert(mSavedFrameHandler != nullptr && mFrameHandlerContext != nullptr);
482 
483     while (mRxFrameBuffer.GetNextSavedFrame(frame, length) == OT_ERROR_NONE)
484     {
485         mSavedFrameHandler(frame, length, mFrameHandlerContext);
486     }
487 
488     mRxFrameBuffer.ClearSavedFrames();
489 }
490 
491 } // namespace Spinel
492 } // namespace ot
493