1 /*
2  *  Copyright (c) 2018, 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 /**
30  * @file
31  *   This file includes definitions for the spinel based radio transceiver.
32  */
33 
34 #ifndef RADIO_SPINEL_HPP_
35 #define RADIO_SPINEL_HPP_
36 
37 #include <openthread/platform/radio.h>
38 
39 #include "openthread-spinel-config.h"
40 #include "spinel.h"
41 #include "spinel_interface.hpp"
42 #include "core/radio/max_power_table.hpp"
43 #include "ncp/ncp_config.h"
44 
45 namespace ot {
46 namespace Spinel {
47 
48 /**
49  * The class for providing a OpenThread radio interface by talking with a radio-only
50  * co-processor(RCP). The InterfaceType template parameter should provide the following
51  * methods:
52  *
53  * class InterfaceType {
54  *
55  *    // This constructor initializes the object.
56  *
57  *    // @param[in] aCallback         Callback on frame received
58  *    // @param[in] aCallbackContext  Callback context
59  *    // @param[in] aFrameBuffer      A reference to a `RxFrameBuffer` object.
60  *
61  *    InterfaceType(Spinel::SpinelInterface::ReceiveFrameCallback aCallback,
62  *                  void *                                        aCallbackContext,
63  *                  Spinel::SpinelInterface::RxFrameBuffer &      aFrameBuffer);
64  *
65  *
66  *    // This method encodes and sends a spinel frame to Radio Co-processor (RCP) over the socket.
67  *
68  *    // This is blocking call, i.e., if the socket is not writable, this method waits for it to become writable for
69  *    // up to `kMaxWaitTime` interval.
70  *
71  *    // @param[in] aFrame     A pointer to buffer containing the spinel frame to send.
72  *    // @param[in] aLength    The length (number of bytes) in the frame.
73  *
74  *    // @retval OT_ERROR_NONE     Successfully encoded and sent the spinel frame.
75  *    // @retval OT_ERROR_NO_BUFS  Insufficient buffer space available to encode the frame.
76  *    // @retval OT_ERROR_FAILED   Failed to send due to socket not becoming writable within `kMaxWaitTime`.
77  *
78  *    otError SendFrame(const uint8_t *aFrame, uint16_t aLength);
79  *
80  *
81  *    // This method waits for receiving part or all of spinel frame within specified interval.
82  *
83  *    // @param[in]  aTimeout  The timeout value in microseconds.
84  *
85  *    // @retval OT_ERROR_NONE             Part or all of spinel frame is received.
86  *    // @retval OT_ERROR_RESPONSE_TIMEOUT No spinel frame is received within @p aTimeout.
87  *
88  *    otError WaitForFrame(uint64_t& aTimeoutUs);
89  *
90  *
91  *    // This method performs radio driver processing.
92  *
93  *    // @param[in]   aContext        The context containing fd_sets.
94  *    //                              The type is specified by the user in template parameters.
95  *
96  *    void Process(const ProcessContextType &aContext);
97  *
98  *
99  *    // This method deinitializes the interface to the RCP.
100  *
101  *    void Deinit(void);
102  * };
103  */
104 template <typename InterfaceType, typename ProcessContextType> class RadioSpinel
105 {
106 public:
107     /**
108      * This constructor initializes the spinel based OpenThread transceiver.
109      *
110      */
111     RadioSpinel(void);
112 
113     /**
114      * Initialize this radio transceiver.
115      *
116      * @param[in]  aResetRadio                 TRUE to reset on init, FALSE to not reset on init.
117      * @param[in]  aRestoreDatasetFromNcp      TRUE to restore dataset to host from non-volatile memory
118      *                                         (only used when attempts to upgrade from NCP to RCP mode),
119      *                                         FALSE otherwise.
120      * @param[in]  aSkipRcpCompatibilityCheck  TRUE to skip RCP compatibility check, FALSE to perform the check.
121      *
122      */
123     void Init(bool aResetRadio, bool aRestoreDataSetFromNcp, bool aSkipRcpCompatibilityCheck);
124 
125     /**
126      * Deinitialize this radio transceiver.
127      *
128      */
129     void Deinit(void);
130 
131     /**
132      * This method gets the status of promiscuous mode.
133      *
134      * @retval true   Promiscuous mode is enabled.
135      * @retval false  Promiscuous mode is disabled.
136      *
137      */
IsPromiscuous(void) const138     bool IsPromiscuous(void) const { return mIsPromiscuous; }
139 
140     /**
141      * This method sets the status of promiscuous mode.
142      *
143      * @param[in]   aEnable     Whether to enable or disable promiscuous mode.
144      *
145      * @retval  OT_ERROR_NONE               Succeeded.
146      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
147      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
148      *
149      */
150     otError SetPromiscuous(bool aEnable);
151 
152     /**
153      * This method sets the Short Address for address filtering.
154      *
155      * @param[in] aShortAddress  The IEEE 802.15.4 Short Address.
156      *
157      * @retval  OT_ERROR_NONE               Succeeded.
158      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
159      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
160      *
161      */
162     otError SetShortAddress(uint16_t aAddress);
163 
164     /**
165      * This method gets the factory-assigned IEEE EUI-64 for this transceiver.
166      *
167      * @param[in]  aInstance   The OpenThread instance structure.
168      * @param[out] aIeeeEui64  A pointer to the factory-assigned IEEE EUI-64.
169      *
170      * @retval  OT_ERROR_NONE               Succeeded.
171      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
172      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
173      *
174      */
175     otError GetIeeeEui64(uint8_t *aIeeeEui64);
176 
177     /**
178      * This method sets the Extended Address for address filtering.
179      *
180      * @param[in] aExtAddress  A pointer to the IEEE 802.15.4 Extended Address stored in little-endian byte order.
181      *
182      * @retval  OT_ERROR_NONE               Succeeded.
183      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
184      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
185      *
186      */
187     otError SetExtendedAddress(const otExtAddress &aAddress);
188 
189     /**
190      * This method sets the PAN ID for address filtering.
191      *
192      * @param[in]   aPanId  The IEEE 802.15.4 PAN ID.
193      *
194      * @retval  OT_ERROR_NONE               Succeeded.
195      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
196      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
197      *
198      */
199     otError SetPanId(uint16_t aPanId);
200 
201     /**
202      * This method gets the radio's transmit power in dBm.
203      *
204      * @param[out]  aPower    The transmit power in dBm.
205      *
206      * @retval  OT_ERROR_NONE               Succeeded.
207      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
208      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
209      *
210      */
211     otError GetTransmitPower(int8_t &aPower);
212 
213     /**
214      * This method sets the radio's transmit power in dBm.
215      *
216      * @param[in]   aPower     The transmit power in dBm.
217      *
218      * @retval  OT_ERROR_NONE               Succeeded.
219      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
220      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
221      *
222      */
223     otError SetTransmitPower(int8_t aPower);
224 
225     /**
226      * This method gets the radio's CCA ED threshold in dBm.
227      *
228      * @param[out]  aThreshold    The CCA ED threshold in dBm.
229      *
230      * @retval  OT_ERROR_NONE               Succeeded.
231      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
232      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
233      *
234      */
235     otError GetCcaEnergyDetectThreshold(int8_t &aThreshold);
236 
237     /**
238      * This method sets the radio's CCA ED threshold in dBm.
239      *
240      * @param[in]   aThreshold     The CCA ED threshold in dBm.
241      *
242      * @retval  OT_ERROR_NONE               Succeeded.
243      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
244      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
245      *
246      */
247     otError SetCcaEnergyDetectThreshold(int8_t aThreshold);
248 
249     /**
250      * This method gets the FEM's Rx LNA gain in dBm.
251      *
252      * @param[out]  aGain    The FEM's Rx LNA gain in dBm.
253      *
254      * @retval  OT_ERROR_NONE               Succeeded.
255      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
256      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
257      *
258      */
259     otError GetFemLnaGain(int8_t &aGain);
260 
261     /**
262      * This method sets the FEM's Rx LNA gain in dBm.
263      *
264      * @param[in]   aGain     The FEM's Rx LNA gain in dBm.
265      *
266      * @retval  OT_ERROR_NONE               Succeeded.
267      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
268      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
269      *
270      */
271     otError SetFemLnaGain(int8_t aGain);
272 
273     /**
274      * This method returns the radio sw version string.
275      *
276      * @returns A pointer to the radio version string.
277      *
278      */
GetVersion(void) const279     const char *GetVersion(void) const { return mVersion; }
280 
281     /**
282      * This method returns the radio capabilities.
283      *
284      * @returns The radio capability bit vector.
285      *
286      */
GetRadioCaps(void) const287     otRadioCaps GetRadioCaps(void) const { return mRadioCaps; }
288 
289     /**
290      * This method gets the most recent RSSI measurement.
291      *
292      * @returns The RSSI in dBm when it is valid.  127 when RSSI is invalid.
293      */
294     int8_t GetRssi(void);
295 
296     /**
297      * This method returns the radio receive sensitivity value.
298      *
299      * @returns The radio receive sensitivity value in dBm.
300      *
301      * @retval  OT_ERROR_NONE               Succeeded.
302      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
303      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
304      *
305      */
GetReceiveSensitivity(void) const306     int8_t GetReceiveSensitivity(void) const { return mRxSensitivity; }
307 
308     /**
309      * This method gets current state of the radio.
310      *
311      * @return  Current state of the radio.
312      *
313      */
314     otRadioState GetState(void) const;
315 
316     /**
317      * This method gets the current receiving channel.
318      *
319      * @returns Current receiving channel.
320      *
321      */
GetChannel(void) const322     uint8_t GetChannel(void) const { return mChannel; }
323 
324 #if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
325     /**
326      * Enable the radio coex.
327      *
328      * @param[in] aInstance  The OpenThread instance structure.
329      * @param[in] aEnabled   TRUE to enable the radio coex, FALSE otherwise.
330      *
331      * @retval OT_ERROR_NONE     Successfully enabled.
332      * @retval OT_ERROR_FAILED   The radio coex could not be enabled.
333      *
334      */
335     otError SetCoexEnabled(bool aEnabled);
336 
337     /**
338      * Check whether radio coex is enabled or not.
339      *
340      * @param[in] aInstance  The OpenThread instance structure.
341      *
342      * @returns TRUE if the radio coex is enabled, FALSE otherwise.
343      *
344      */
345     bool IsCoexEnabled(void);
346 
347     /**
348      * This method retrieves the radio coexistence metrics.
349      *
350      * @param[out] aCoexMetrics  A reference to the coexistence metrics structure.
351      *
352      * @retval OT_ERROR_NONE          Successfully retrieved the coex metrics.
353      * @retval OT_ERROR_INVALID_ARGS  @p aCoexMetrics was nullptr.
354      *
355      */
356     otError GetCoexMetrics(otRadioCoexMetrics &aCoexMetrics);
357 #endif // OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
358 
359     /**
360      * This method returns a reference to the transmit buffer.
361      *
362      * The caller forms the IEEE 802.15.4 frame in this buffer then calls otPlatRadioTransmit() to request transmission.
363      *
364      * @returns A reference to the transmit buffer.
365      *
366      */
GetTransmitFrame(void)367     otRadioFrame &GetTransmitFrame(void) { return mTxRadioFrame; }
368 
369     /**
370      * This method enables or disables source address match feature.
371      *
372      * @param[in]  aEnable     Enable/disable source address match feature.
373      *
374      * @retval  OT_ERROR_NONE               Succeeded.
375      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
376      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
377      *
378      */
379     otError EnableSrcMatch(bool aEnable);
380 
381     /**
382      * This method adds a short address to the source address match table.
383      *
384      * @param[in]  aInstance      The OpenThread instance structure.
385      * @param[in]  aShortAddress  The short address to be added.
386      *
387      * @retval  OT_ERROR_NONE               Successfully added short address to the source match table.
388      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
389      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
390      * @retval  OT_ERROR_NO_BUFS            No available entry in the source match table.
391      */
392     otError AddSrcMatchShortEntry(uint16_t aShortAddress);
393 
394     /**
395      * This method removes a short address from the source address match table.
396      *
397      * @param[in]  aInstance      The OpenThread instance structure.
398      * @param[in]  aShortAddress  The short address to be removed.
399      *
400      * @retval  OT_ERROR_NONE               Successfully removed short address from the source match table.
401      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
402      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
403      * @retval  OT_ERROR_NO_ADDRESS         The short address is not in source address match table.
404      */
405     otError ClearSrcMatchShortEntry(uint16_t aShortAddress);
406 
407     /**
408      * Clear all short addresses from the source address match table.
409      *
410      * @param[in]  aInstance   The OpenThread instance structure.
411      *
412      * @retval  OT_ERROR_NONE               Succeeded.
413      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
414      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
415      *
416      */
417     otError ClearSrcMatchShortEntries(void);
418 
419     /**
420      * Add an extended address to the source address match table.
421      *
422      * @param[in]  aInstance    The OpenThread instance structure.
423      * @param[in]  aExtAddress  The extended address to be added stored in little-endian byte order.
424      *
425      * @retval  OT_ERROR_NONE               Successfully added extended address to the source match table.
426      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
427      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
428      * @retval  OT_ERROR_NO_BUFS            No available entry in the source match table.
429      */
430     otError AddSrcMatchExtEntry(const otExtAddress &aExtAddress);
431 
432     /**
433      * Remove an extended address from the source address match table.
434      *
435      * @param[in]  aInstance    The OpenThread instance structure.
436      * @param[in]  aExtAddress  The extended address to be removed stored in little-endian byte order.
437      *
438      * @retval  OT_ERROR_NONE               Successfully removed the extended address from the source match table.
439      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
440      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
441      * @retval  OT_ERROR_NO_ADDRESS         The extended address is not in source address match table.
442      */
443     otError ClearSrcMatchExtEntry(const otExtAddress &aExtAddress);
444 
445     /**
446      * Clear all the extended/long addresses from source address match table.
447      *
448      * @param[in]  aInstance   The OpenThread instance structure.
449      *
450      * @retval  OT_ERROR_NONE               Succeeded.
451      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
452      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
453      *
454      */
455     otError ClearSrcMatchExtEntries(void);
456 
457     /**
458      * This method begins the energy scan sequence on the radio.
459      *
460      * @param[in]  aScanChannel     The channel to perform the energy scan on.
461      * @param[in]  aScanDuration    The duration, in milliseconds, for the channel to be scanned.
462      *
463      * @retval  OT_ERROR_NONE               Succeeded.
464      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
465      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
466      *
467      */
468     otError EnergyScan(uint8_t aScanChannel, uint16_t aScanDuration);
469 
470     /**
471      * This method switches the radio state from Receive to Transmit.
472      *
473      * @param[in] aFrame     A reference to the transmitted frame.
474      *
475      * @retval  OT_ERROR_NONE               Successfully transitioned to Transmit.
476      * @retval  OT_ERROR_BUSY               Failed due to another transmission is on going.
477      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
478      * @retval  OT_ERROR_INVALID_STATE      The radio was not in the Receive state.
479      */
480     otError Transmit(otRadioFrame &aFrame);
481 
482     /**
483      * This method switches the radio state from Sleep to Receive.
484      *
485      * @param[in]  aChannel   The channel to use for receiving.
486      *
487      * @retval OT_ERROR_NONE          Successfully transitioned to Receive.
488      * @retval OT_ERROR_INVALID_STATE The radio was disabled or transmitting.
489      *
490      */
491     otError Receive(uint8_t aChannel);
492 
493     /**
494      * This method switches the radio state from Receive to Sleep.
495      *
496      * @retval OT_ERROR_NONE          Successfully transitioned to Sleep.
497      * @retval OT_ERROR_BUSY          The radio was transmitting
498      * @retval OT_ERROR_INVALID_STATE The radio was disabled
499      *
500      */
501     otError Sleep(void);
502 
503     /**
504      * Enable the radio.
505      *
506      * @param[in]   aInstance   A pointer to the OpenThread instance.
507      *
508      * @retval OT_ERROR_NONE     Successfully enabled.
509      * @retval OT_ERROR_FAILED   The radio could not be enabled.
510      *
511      */
512     otError Enable(otInstance *aInstance);
513 
514     /**
515      * Disable the radio.
516      *
517      * @retval  OT_ERROR_NONE               Successfully transitioned to Disabled.
518      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
519      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
520      *
521      */
522     otError Disable(void);
523 
524     /**
525      * This method checks whether radio is enabled or not.
526      *
527      * @returns TRUE if the radio is enabled, FALSE otherwise.
528      *
529      */
IsEnabled(void) const530     bool IsEnabled(void) const { return mState != kStateDisabled; }
531 
532     /**
533      * This method indicates whether there is a pending transmission.
534      *
535      * @retval TRUE  There is a pending transmission.
536      * @retval FALSE There is no pending transmission.
537      *
538      */
IsTransmitting(void) const539     bool IsTransmitting(void) const { return mState == kStateTransmitting; }
540 
541     /**
542      * This method indicates whether a transmit has just finished.
543      *
544      * @retval TRUE  The transmission is done.
545      * @retval FALSE The transmission is not done.
546      *
547      */
IsTransmitDone(void) const548     bool IsTransmitDone(void) const { return mState == kStateTransmitDone; }
549 
550     /**
551      * This method returns the timeout timepoint for the pending transmission.
552      *
553      * @returns The timeout timepoint for the pending transmission.
554      *
555      */
GetTxRadioEndUs(void) const556     uint64_t GetTxRadioEndUs(void) const { return mTxRadioEndUs; }
557 
558     /**
559      * This method processes any pending the I/O data.
560      *
561      * @param[in]  aContext   The process context.
562      *
563      */
564     void Process(const ProcessContextType &aContext);
565 
566     /**
567      * This method returns the underlying spinel interface.
568      *
569      * @returns The underlying spinel interface.
570      *
571      */
GetSpinelInterface(void)572     InterfaceType &GetSpinelInterface(void) { return mSpinelInterface; }
573 
574 #if OPENTHREAD_CONFIG_DIAG_ENABLE
575     /**
576      * This method enables/disables the factory diagnostics mode.
577      *
578      * @param[in]  aMode  TRUE to enable diagnostics mode, FALSE otherwise.
579      *
580      */
SetDiagEnabled(bool aMode)581     void SetDiagEnabled(bool aMode) { mDiagMode = aMode; }
582 
583     /**
584      * This method indicates whether or not factory diagnostics mode is enabled.
585      *
586      * @returns TRUE if factory diagnostics mode is enabled, FALSE otherwise.
587      *
588      */
IsDiagEnabled(void) const589     bool IsDiagEnabled(void) const { return mDiagMode; }
590 
591     /**
592      * This method processes platform diagnostics commands.
593      *
594      * @param[in]   aString         A null-terminated input string.
595      * @param[out]  aOutput         The diagnostics execution result.
596      * @param[in]   aOutputMaxLen   The output buffer size.
597      *
598      * @retval  OT_ERROR_NONE               Succeeded.
599      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
600      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
601      *
602      */
603     otError PlatDiagProcess(const char *aString, char *aOutput, size_t aOutputMaxLen);
604 #endif
605 
606     /**
607      * This method returns the radio channel mask.
608      *
609      * @param[in]   aPreferred  TRUE to get preferred channel mask, FALSE to get supported channel mask.
610      *
611      * @returns The radio channel mask according to @aPreferred:
612      *   The radio supported channel mask that the device is allowed to be on.
613      *   The radio preferred channel mask that the device prefers to form on.
614      *
615      */
616     uint32_t GetRadioChannelMask(bool aPreferred);
617 
618     /**
619      * This method processes a received Spinel frame.
620      *
621      * The newly received frame is available in `RxFrameBuffer` from `SpinelInterface::GetRxFrameBuffer()`.
622      *
623      */
624     void HandleReceivedFrame(void);
625 
626     /**
627      * This method sets MAC key and key index to RCP.
628      *
629      * @param[in] aKeyIdMode  The key ID mode.
630      * @param[in] aKeyId      The key index.
631      * @param[in] aPrevKey    The previous MAC key.
632      * @param[in] aCurrKey    The current MAC key.
633      * @param[in] aNextKey    The next MAC key.
634      *
635      * @retval  OT_ERROR_NONE               Succeeded.
636      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
637      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
638      *
639      */
640     otError SetMacKey(uint8_t         aKeyIdMode,
641                       uint8_t         aKeyId,
642                       const otMacKey &aPrevKey,
643                       const otMacKey &aCurrKey,
644                       const otMacKey &aNextKey);
645 
646     /**
647      * This method sets the current MAC Frame Counter value.
648      *
649      * @param[in]   aMacFrameCounter  The MAC Frame Counter value.
650      *
651      */
652     otError SetMacFrameCounter(uint32_t aMacFrameCounter);
653 
654     /**
655      * This method sets the radio region code.
656      *
657      * @param[in]   aRegionCode  The radio region code.
658      *
659      * @retval  OT_ERROR_NONE             Successfully set region code.
660      * @retval  OT_ERROR_FAILED           Other platform specific errors.
661      *
662      */
663     otError SetRadioRegion(uint16_t aRegionCode);
664 
665     /**
666      * This method gets the radio region code.
667      *
668      * @param[out]   aRegionCode  The radio region code.
669      *
670      * @retval  OT_ERROR_INVALID_ARGS     @p aRegionCode is nullptr.
671      * @retval  OT_ERROR_NONE             Successfully got region code.
672      * @retval  OT_ERROR_FAILED           Other platform specific errors.
673      *
674      */
675     otError GetRadioRegion(uint16_t *aRegionCode);
676 
677     /**
678      * This method checks whether the spinel interface is radio-only.
679      *
680      * @param[out] aSupportsRcpApiVersion   A reference to a boolean variable to update whether the list of spinel
681      *                                      capabilities include `SPINEL_CAP_RCP_API_VERSION`.
682      *
683      * @retval  TRUE    The radio chip is in radio-only mode.
684      * @retval  FALSE   Otherwise.
685      *
686      */
687     bool IsRcp(bool &aSupportsRcpApiVersion);
688 
689     /**
690      * This method checks whether there is pending frame in the buffer.
691      *
692      * @returns Whether there is pending frame in the buffer.
693      *
694      */
HasPendingFrame(void) const695     bool HasPendingFrame(void) const { return mRxFrameBuffer.HasSavedFrame(); }
696 
697     /**
698      * This method gets dataset from NCP radio and saves it.
699      *
700      * @retval  OT_ERROR_NONE               Successfully restore dataset.
701      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
702      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the radio.
703      * @retval  OT_ERROR_NOT_FOUND          Failed due to spinel property not supported in radio.
704      * @retval  OT_ERROR_FAILED             Failed due to other reasons.
705      */
706     otError RestoreDatasetFromNcp(void);
707 
708     /**
709      * This method returns the next timepoint to recalculate RCP time offset.
710      *
711      * @returns The timepoint to start the recalculation of RCP time offset.
712      *
713      */
GetNextRadioTimeRecalcStart(void) const714     uint64_t GetNextRadioTimeRecalcStart(void) const { return mRadioTimeRecalcStart; }
715 
716     /**
717      * This method gets the current estimated time on RCP.
718      *
719      * @returns The current estimated RCP time in microseconds.
720      *
721      */
722     uint64_t GetNow(void);
723 
724     /**
725      * This method returns the bus speed between the host and the radio.
726      *
727      * @returns   bus speed in bits/second.
728      *
729      */
730     uint32_t GetBusSpeed(void) const;
731 
732     /**
733      * This method sets the max transmit power.
734      *
735      * @param[in] aChannel    The radio channel.
736      * @param[in] aPower      The max transmit power in dBm.
737      *
738      * @retval  OT_ERROR_NONE           Successfully set the max transmit power.
739      * @retval  OT_ERROR_INVALID_ARGS   Channel is not in valid range.
740      *
741      */
742     otError SetChannelMaxTransmitPower(uint8_t aChannel, int8_t aPower);
743 
744 private:
745     enum
746     {
747         kMaxSpinelFrame        = SpinelInterface::kMaxFrameSize,
748         kMaxWaitTime           = 2000, ///< Max time to wait for response in milliseconds.
749         kVersionStringSize     = 128,  ///< Max size of version string.
750         kCapsBufferSize        = 100,  ///< Max buffer size used to store `SPINEL_PROP_CAPS` value.
751         kChannelMaskBufferSize = 32,   ///< Max buffer size used to store `SPINEL_PROP_PHY_CHAN_SUPPORTED` value.
752     };
753 
754     enum State
755     {
756         kStateDisabled,     ///< Radio is disabled.
757         kStateSleep,        ///< Radio is sleep.
758         kStateReceive,      ///< Radio is in receive mode.
759         kStateTransmitting, ///< Frame passed to radio for transmission, waiting for done event from radio.
760         kStateTransmitDone, ///< Radio indicated frame transmission is done.
761     };
762 
763     typedef otError (RadioSpinel::*ResponseHandler)(const uint8_t *aBuffer, uint16_t aLength);
764 
765     static void HandleReceivedFrame(void *aContext);
766 
767     otError CheckSpinelVersion(void);
768     otError CheckRadioCapabilities(void);
769     otError CheckRcpApiVersion(bool aSupportsRcpApiVersion);
770 
771     /**
772      * This method triggers a state transfer of the state machine.
773      *
774      */
775     void ProcessRadioStateMachine(void);
776 
777     /**
778      * This method processes the frame queue.
779      *
780      */
781     void ProcessFrameQueue(void);
782 
783     /**
784      * This method tries to retrieve a spinel property from OpenThread transceiver.
785      *
786      * @param[in]   aKey        Spinel property key.
787      * @param[in]   aFormat     Spinel formatter to unpack property value.
788      * @param[out]  ...         Variable arguments list.
789      *
790      * @retval  OT_ERROR_NONE               Successfully got the property.
791      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
792      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
793      *
794      */
795     otError Get(spinel_prop_key_t aKey, const char *aFormat, ...);
796 
797     /**
798      * This method tries to retrieve a spinel property from OpenThread transceiver with parameter appended.
799      *
800      * @param[in]   aKey        Spinel property key.
801      * @param[in]   aParam      Parameter appended to spinel command.
802      * @param[in]   aParamSize  Size of parameter appended to spinel command
803      * @param[in]   aFormat     Spinel formatter to unpack property value.
804      * @param[out]  ...         Variable arguments list.
805      *
806      * @retval  OT_ERROR_NONE               Successfully got the property.
807      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
808      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
809      *
810      */
811     otError GetWithParam(spinel_prop_key_t aKey,
812                          const uint8_t *   aParam,
813                          spinel_size_t     aParamSize,
814                          const char *      aFormat,
815                          ...);
816 
817     /**
818      * This method tries to update a spinel property of OpenThread transceiver.
819      *
820      * @param[in]   aKey        Spinel property key.
821      * @param[in]   aFormat     Spinel formatter to pack property value.
822      * @param[in]   ...         Variable arguments list.
823      *
824      * @retval  OT_ERROR_NONE               Successfully set the property.
825      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
826      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
827      *
828      */
829     otError Set(spinel_prop_key_t aKey, const char *aFormat, ...);
830 
831     /**
832      * This method tries to insert a item into a spinel list property of OpenThread transceiver.
833      *
834      * @param[in]   aKey        Spinel property key.
835      * @param[in]   aFormat     Spinel formatter to pack the item.
836      * @param[in]   ...         Variable arguments list.
837      *
838      * @retval  OT_ERROR_NONE               Successfully insert item into the property.
839      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
840      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
841      *
842      */
843     otError Insert(spinel_prop_key_t aKey, const char *aFormat, ...);
844 
845     /**
846      * This method tries to remove a item from a spinel list property of OpenThread transceiver.
847      *
848      * @param[in]   aKey        Spinel property key.
849      * @param[in]   aFormat     Spinel formatter to pack the item.
850      * @param[in]   ...         Variable arguments list.
851      *
852      * @retval  OT_ERROR_NONE               Successfully removed item from the property.
853      * @retval  OT_ERROR_BUSY               Failed due to another operation is on going.
854      * @retval  OT_ERROR_RESPONSE_TIMEOUT   Failed due to no response received from the transceiver.
855      *
856      */
857     otError Remove(spinel_prop_key_t aKey, const char *aFormat, ...);
858 
859     spinel_tid_t GetNextTid(void);
FreeTid(spinel_tid_t tid)860     void         FreeTid(spinel_tid_t tid) { mCmdTidsInUse &= ~(1 << tid); }
861 
862     otError RequestV(uint32_t aCommand, spinel_prop_key_t aKey, const char *aFormat, va_list aArgs);
863     otError Request(uint32_t aCommand, spinel_prop_key_t aKey, const char *aFormat, ...);
864     otError RequestWithPropertyFormat(const char *      aPropertyFormat,
865                                       uint32_t          aCommand,
866                                       spinel_prop_key_t aKey,
867                                       const char *      aFormat,
868                                       ...);
869     otError RequestWithPropertyFormatV(const char *      aPropertyFormat,
870                                        uint32_t          aCommand,
871                                        spinel_prop_key_t aKey,
872                                        const char *      aFormat,
873                                        va_list           aArgs);
874     otError RequestWithExpectedCommandV(uint32_t          aExpectedCommand,
875                                         uint32_t          aCommand,
876                                         spinel_prop_key_t aKey,
877                                         const char *      aFormat,
878                                         va_list           aArgs);
879     otError WaitResponse(void);
880     otError SendReset(void);
881     otError SendCommand(uint32_t          command,
882                         spinel_prop_key_t key,
883                         spinel_tid_t      tid,
884                         const char *      pack_format,
885                         va_list           args);
886     otError ParseRadioFrame(otRadioFrame &aFrame, const uint8_t *aBuffer, uint16_t aLength, spinel_ssize_t &aUnpacked);
887     otError ThreadDatasetHandler(const uint8_t *aBuffer, uint16_t aLength);
888 
889     /**
890      * This method returns if the property changed event is safe to be handled now.
891      *
892      * If a property handler will go up to core stack, it may cause reentrant issue of `Hdlc::Decode()` and
893      * `WaitResponse()`.
894      *
895      * @param[in] aKey The identifier of the property.
896      *
897      * @returns Whether this property is safe to be handled now.
898      *
899      */
IsSafeToHandleNow(spinel_prop_key_t aKey) const900     bool IsSafeToHandleNow(spinel_prop_key_t aKey) const
901     {
902         return !(aKey == SPINEL_PROP_STREAM_RAW || aKey == SPINEL_PROP_MAC_ENERGY_SCAN_RESULT);
903     }
904 
905     void HandleNotification(SpinelInterface::RxFrameBuffer &aFrameBuffer);
906     void HandleNotification(const uint8_t *aBuffer, uint16_t aLength);
907     void HandleValueIs(spinel_prop_key_t aKey, const uint8_t *aBuffer, uint16_t aLength);
908 
909     void HandleResponse(const uint8_t *aBuffer, uint16_t aLength);
910     void HandleTransmitDone(uint32_t aCommand, spinel_prop_key_t aKey, const uint8_t *aBuffer, uint16_t aLength);
911     void HandleWaitingResponse(uint32_t aCommand, spinel_prop_key_t aKey, const uint8_t *aBuffer, uint16_t aLength);
912 
913     void RadioReceive(void);
914 
915     void TransmitDone(otRadioFrame *aFrame, otRadioFrame *aAckFrame, otError aError);
916 
917     void CalcRcpTimeOffset(void);
918 
919     void HandleRcpUnexpectedReset(spinel_status_t aStatus);
920     void HandleRcpTimeout(void);
921     void RecoverFromRcpFailure(void);
922 
923 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
924     void RestoreProperties(void);
925 #endif
926 
927     otInstance *mInstance;
928 
929     SpinelInterface::RxFrameBuffer mRxFrameBuffer;
930 
931     InterfaceType mSpinelInterface;
932 
933     uint16_t          mCmdTidsInUse;    ///< Used transaction ids.
934     spinel_tid_t      mCmdNextTid;      ///< Next available transaction id.
935     spinel_tid_t      mTxRadioTid;      ///< The transaction id used to send a radio frame.
936     spinel_tid_t      mWaitingTid;      ///< The transaction id of current transaction.
937     spinel_prop_key_t mWaitingKey;      ///< The property key of current transaction.
938     const char *      mPropertyFormat;  ///< The spinel property format of current transaction.
939     va_list           mPropertyArgs;    ///< The arguments pack or unpack spinel property of current transaction.
940     uint32_t          mExpectedCommand; ///< Expected response command of current transaction.
941     otError           mError;           ///< The result of current transaction.
942 
943     uint8_t       mRxPsdu[OT_RADIO_FRAME_MAX_SIZE];
944     uint8_t       mTxPsdu[OT_RADIO_FRAME_MAX_SIZE];
945     uint8_t       mAckPsdu[OT_RADIO_FRAME_MAX_SIZE];
946     otRadioFrame  mRxRadioFrame;
947     otRadioFrame  mTxRadioFrame;
948     otRadioFrame  mAckRadioFrame;
949     otRadioFrame *mTransmitFrame; ///< Points to the frame to send
950 
951     otExtAddress mExtendedAddress;
952     uint16_t     mShortAddress;
953     uint16_t     mPanId;
954     otRadioCaps  mRadioCaps;
955     uint8_t      mChannel;
956     int8_t       mRxSensitivity;
957     otError      mTxError;
958     char         mVersion[kVersionStringSize];
959     otExtAddress mIeeeEui64;
960 
961     State mState;
962     bool  mIsPromiscuous : 1;     ///< Promiscuous mode.
963     bool  mIsReady : 1;           ///< NCP ready.
964     bool  mSupportsLogStream : 1; ///< RCP supports `LOG_STREAM` property with OpenThread log meta-data format.
965     bool  mIsTimeSynced : 1;      ///< Host has calculated the time difference between host and RCP.
966 
967 #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
968 
969     bool    mResetRadioOnStartup : 1; ///< Whether should send reset command when init.
970     int16_t mRcpFailureCount;         ///< Count of consecutive RCP failures.
971 
972     // Properties set by core.
973     uint8_t      mKeyIdMode;
974     uint8_t      mKeyId;
975     otMacKey     mPrevKey;
976     otMacKey     mCurrKey;
977     otMacKey     mNextKey;
978     uint16_t     mSrcMatchShortEntries[OPENTHREAD_CONFIG_MLE_MAX_CHILDREN];
979     int16_t      mSrcMatchShortEntryCount;
980     otExtAddress mSrcMatchExtEntries[OPENTHREAD_CONFIG_MLE_MAX_CHILDREN];
981     int16_t      mSrcMatchExtEntryCount;
982     uint8_t      mScanChannel;
983     uint16_t     mScanDuration;
984     int8_t       mCcaEnergyDetectThreshold;
985     int8_t       mTransmitPower;
986     int8_t       mFemLnaGain;
987     bool         mCoexEnabled : 1;
988 
989     bool mMacKeySet : 1;                   ///< Whether MAC key has been set.
990     bool mCcaEnergyDetectThresholdSet : 1; ///< Whether CCA energy detect threshold has been set.
991     bool mTransmitPowerSet : 1;            ///< Whether transmit power has been set.
992     bool mCoexEnabledSet : 1;              ///< Whether coex enabled has been set.
993     bool mFemLnaGainSet : 1;               ///< Whether FEM LNA gain has been set.
994     bool mRcpFailed : 1;                   ///< RCP failure happened, should recover and retry operation.
995     bool mEnergyScanning : 1;              ///< If fails while scanning, restarts scanning.
996 
997 #endif // OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
998 
999 #if OPENTHREAD_CONFIG_DIAG_ENABLE
1000     bool   mDiagMode;
1001     char * mDiagOutput;
1002     size_t mDiagOutputMaxLen;
1003 #endif
1004 
1005     uint64_t mTxRadioEndUs;
1006     uint64_t mRadioTimeRecalcStart; ///< When to recalculate RCP time offset.
1007     int64_t  mRadioTimeOffset;      ///< Time difference with estimated RCP time minus host time.
1008 
1009     MaxPowerTable mMaxPowerTable;
1010 };
1011 
1012 } // namespace Spinel
1013 } // namespace ot
1014 
1015 #include "radio_spinel_impl.hpp"
1016 
1017 #endif // RADIO_SPINEL_HPP_
1018