1 /*
2  *  Copyright (c) 2021, The OpenThread Authors.
3  *  Copyright (c) 2022-2024, NXP.
4  *
5  *  All rights reserved.
6  *
7  *  Redistribution and use in source and binary forms, with or without
8  *  modification, are permitted provided that the following conditions are met:
9  *  1. Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer.
11  *  2. Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution.
14  *  3. Neither the name of the copyright holder nor the
15  *     names of its contributors may be used to endorse or promote products
16  *     derived from this software without specific prior written permission.
17  *
18  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  *  POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #ifndef OT_SPINEL_HDLC_HPP_
32 #define OT_SPINEL_HDLC_HPP_
33 
34 #include <zephyr/kernel.h>
35 #include <zephyr/net/hdlc_rcp_if/hdlc_rcp_if.h>
36 
37 #include "lib/url/url.hpp"
38 #include "lib/hdlc/hdlc.hpp"
39 #include "lib/spinel/spinel.h"
40 #include "lib/spinel/spinel_interface.hpp"
41 
42 namespace ot {
43 
44 namespace Hdlc {
45 
46 typedef uint8_t HdlcSpinelContext;
47 
48 /**
49  * This class defines an HDLC spinel interface to the Radio Co-processor (RCP).
50  *
51  */
52 class HdlcInterface : public ot::Spinel::SpinelInterface
53 {
54 public:
55     /**
56      * This constructor initializes the object.
57      *
58      * @param[in] aRadioUrl Radio url
59      *
60      */
61     HdlcInterface(const Url::Url &aRadioUrl);
62 
63     /**
64      * This destructor deinitializes the object.
65      *
66      */
67     virtual ~HdlcInterface(void);
68 
69     /**
70      * This method initializes the HDLC interface.
71      *
72      */
73     otError Init(ReceiveFrameCallback aCallback, void *aCallbackContext, RxFrameBuffer &aFrameBuffer);
74 
75     /**
76      * This method deinitializes the HDLC interface.
77      *
78      */
79     void Deinit(void);
80 
81     /**
82      * This method performs radio driver processing.
83      *
84      * @param[in]  aInstance  The ot instance
85      *
86      */
87     void Process(const void *aInstance);
88 
89     /**
90      * This method encodes and sends a spinel frame to Radio Co-processor (RCP) over the socket.
91      *
92      * This is blocking call, i.e., if the socket is not writable, this method waits for it to become writable for
93      * up to `kMaxWaitTime` interval.
94      *
95      * @param[in] aFrame     A pointer to buffer containing the spinel frame to send.
96      * @param[in] aLength    The length (number of bytes) in the frame.
97      *
98      * @retval OT_ERROR_NONE     Successfully encoded and sent the spinel frame.
99      * @retval OT_ERROR_NO_BUFS  Insufficient buffer space available to encode the frame.
100      * @retval OT_ERROR_FAILED   Failed to send due to socket not becoming writable within `kMaxWaitTime`.
101      *
102      */
103     otError SendFrame(const uint8_t *aFrame, uint16_t aLength);
104 
105     /**
106      * This method waits for receiving part or all of spinel frame within specified timeout.
107      *
108      * @param[in]  aTimeoutUs  The timeout value in microseconds.
109      *
110      * @retval OT_ERROR_NONE             Part or all of spinel frame is received.
111      * @retval OT_ERROR_RESPONSE_TIMEOUT No spinel frame is received within @p aTimeoutUs.
112      *
113      */
114     otError WaitForFrame(uint64_t aTimeoutUs);
115 
116     /**
117      * This method is called by the HDLC RX Callback when a HDLC message has been received
118      *
119      * It will decode and store the Spinel frames in a temporary Spinel frame buffer (mRxSpinelFrameBuffer)
120      * This buffer will be then copied to the OpenThread Spinel frame buffer, from the OpenThread task context
121      *
122      * @param[in] data A pointer to buffer containing the HDLC message to decode.
123      * @param[in] len  The length (number of bytes) in the message.
124      */
125     void ProcessRxData(uint8_t *data, uint16_t len);
126 
127     /**
128      * This method is called when RCP failure detected and resets internal states of the interface.
129      *
130      */
131     void OnRcpReset(void);
132 
133     /**
134      * This method is called when RCP is reset to recreate the connection with it.
135      * Intentionally empty.
136      *
137      */
ResetConnection(void)138     otError ResetConnection(void) { return OT_ERROR_NONE; }
139 
140     /**
141      * This method hardware resets the RCP.
142      *
143      * @retval OT_ERROR_NONE             Successfully reset the RCP.
144      * @retval OT_ERROR_NOT_IMPLEMENTED  The hardware reset is not implemented.
145      *
146      */
HardwareReset(void)147     otError HardwareReset(void) { return OT_ERROR_NOT_IMPLEMENTED; }
148 
149 private:
150 
151     enum
152     {
153         /* HDLC encoder buffer must be larger than the max spinel frame size to be able to handle the HDLC overhead
154          * As a host, a TX frame will always contain only 1 spinel frame + HDLC overhead
155          * Sizing the buffer for 2 spinel frames should be large enough to handle this */
156         kEncoderBufferSize         = SPINEL_FRAME_MAX_SIZE * 2,
157         kMaxMultiFrameSize         = 2048,
158         k_spinel_hdlc_frame_ready_event = 1 << 0,
159     };
160 
161     ot::Spinel::FrameBuffer<kEncoderBufferSize>         mEncoderBuffer;
162     ot::Hdlc::Encoder                                   mHdlcEncoder;
163     hdlc_rx_callback_t                                  hdlc_rx_callback;
164     ot::Spinel::SpinelInterface::RxFrameBuffer          *mReceiveFrameBuffer;
165     ot::Spinel::SpinelInterface::ReceiveFrameCallback   mReceiveFrameCallback;
166     void                                                *mReceiveFrameContext;
167     ot::Spinel::MultiFrameBuffer<kMaxMultiFrameSize>    mRxSpinelFrameBuffer;
168     ot::Hdlc::Decoder                                   mHdlcSpinelDecoder;
169     bool                                                mIsInitialized;
170     uint8_t                                             *mSavedFrame;
171     uint16_t                                            mSavedFrameLen;
172     bool                                                mIsSpinelBufferFull;
173     const Url::Url                                      &mRadioUrl;
174 
175     /* Spinel HDLC interface */
176     const struct device *radio_dev;
177     struct hdlc_api     *hdlc_api;
178 
179     struct k_event  spinel_hdlc_event;
180     struct k_mutex  spinel_hdlc_wr_lock;
181     struct k_mutex  spinel_hdlc_rd_lock;
182     struct k_msgq   spinel_hdlc_msgq;
183     char            spinel_hdlc_msgq_buffer;
184 
185     otError     Write(const uint8_t *aFrame, uint16_t aLength);
186     uint32_t    TryReadAndDecode(bool fullRead);
187     void        HandleHdlcFrame(otError aError);
188     static void HandleHdlcFrame(void *aContext, otError aError);
189     static void HdlcRxCallback(uint8_t *data, uint16_t len, void *param);
190 
GetRcpInterfaceMetrics(void) const191     const otRcpInterfaceMetrics *GetRcpInterfaceMetrics(void) const { return nullptr;}
GetBusSpeed(void) const192     uint32_t                     GetBusSpeed(void) const { return 0; }
UpdateFdSet(void * aMainloopContext)193     void                         UpdateFdSet(void *aMainloopContext)
194     {
195         (void)aMainloopContext;
196     }
197 
198 protected:
199     virtual void HandleUnknownHdlcContent(uint8_t *buffer, uint16_t len);
200 };
201 
202 } // namespace Zephyr
203 
204 } // namespace ot
205 
206 #endif // OT_SPINEL_HDLC_HPP_
207