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 #include <assert.h>
32 #include <openthread/tasklet.h>
33 #include <openthread/platform/alarm-milli.h>
34 #include <common/logging.hpp>
35 #include <common/code_utils.hpp>
36 #include "hdlc_interface.hpp"
37
38 namespace ot
39 {
40
41 namespace Hdlc
42 {
43
HdlcInterface(const Url::Url & aRadioUrl)44 HdlcInterface::HdlcInterface(const Url::Url &aRadioUrl)
45 : mEncoderBuffer(), mHdlcEncoder(mEncoderBuffer), hdlc_rx_callback(nullptr),
46 mReceiveFrameBuffer(nullptr), mReceiveFrameCallback(nullptr),
47 mReceiveFrameContext(nullptr), mHdlcSpinelDecoder(), mIsInitialized(false),
48 mSavedFrame(nullptr), mSavedFrameLen(0), mIsSpinelBufferFull(false), mRadioUrl(aRadioUrl)
49 {
50 bool is_device_ready;
51
52 hdlc_rx_callback = HdlcRxCallback;
53
54 radio_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_hdlc_rcp_if));
55 is_device_ready = device_is_ready(radio_dev);
56 __ASSERT(is_device_ready == true, "Radio device is not ready");
57
58 hdlc_api = (struct hdlc_api *)radio_dev->api;
59 __ASSERT(hdlc_api != NULL, "Radio device initialization failed");
60 }
61
~HdlcInterface(void)62 HdlcInterface::~HdlcInterface(void)
63 {
64 }
65
Init(ReceiveFrameCallback aCallback,void * aCallbackContext,RxFrameBuffer & aFrameBuffer)66 otError HdlcInterface::Init(ReceiveFrameCallback aCallback, void *aCallbackContext,
67 RxFrameBuffer &aFrameBuffer)
68 {
69 int status;
70 otError error = OT_ERROR_NONE;
71
72 if (!mIsInitialized) {
73 /* Event initialization */
74 k_event_init(&spinel_hdlc_event);
75
76 /* Read/Write semaphores initialization */
77 k_mutex_init(&spinel_hdlc_wr_lock);
78 k_mutex_init(&spinel_hdlc_rd_lock);
79
80 /* Message queue initialization */
81 k_msgq_init(&spinel_hdlc_msgq, &spinel_hdlc_msgq_buffer, 1, 1);
82
83 mHdlcSpinelDecoder.Init(mRxSpinelFrameBuffer, HandleHdlcFrame, this);
84 mReceiveFrameCallback = aCallback;
85 mReceiveFrameContext = aCallbackContext;
86 mReceiveFrameBuffer = &aFrameBuffer;
87
88 /* Initialize the HDLC interface */
89 status = hdlc_api->register_rx_cb(hdlc_rx_callback, this);
90 if (status == 0) {
91 mIsInitialized = true;
92 } else {
93 otLogDebgPlat("Failed to initialize HDLC interface %d", status);
94 error = OT_ERROR_FAILED;
95 }
96 }
97
98 return error;
99 }
100
Deinit(void)101 void HdlcInterface::Deinit(void)
102 {
103 int status;
104
105 status = hdlc_api->deinit();
106 if (status == 0) {
107 mIsInitialized = false;
108 } else {
109 otLogDebgPlat("Failed to terminate HDLC interface %d", status);
110 }
111
112 mReceiveFrameCallback = nullptr;
113 mReceiveFrameContext = nullptr;
114 mReceiveFrameBuffer = nullptr;
115 }
116
Process(const void * aInstance)117 void HdlcInterface::Process(const void *aInstance)
118 {
119 OT_UNUSED_VARIABLE(aInstance);
120 TryReadAndDecode(false);
121 }
122
SendFrame(const uint8_t * aFrame,uint16_t aLength)123 otError HdlcInterface::SendFrame(const uint8_t *aFrame, uint16_t aLength)
124 {
125 otError error = OT_ERROR_NONE;
126
127 /* Protect concurrent Send operation to avoid any buffer corruption */
128 if (k_mutex_lock(&spinel_hdlc_wr_lock, K_FOREVER) != 0) {
129 error = OT_ERROR_FAILED;
130 goto exit;
131 }
132
133 assert(mEncoderBuffer.IsEmpty());
134
135 SuccessOrExit(error = mHdlcEncoder.BeginFrame());
136 SuccessOrExit(error = mHdlcEncoder.Encode(aFrame, aLength));
137 SuccessOrExit(error = mHdlcEncoder.EndFrame());
138 otLogDebgPlat("frame len to send = %d/%d", mEncoderBuffer.GetLength(), aLength);
139 SuccessOrExit(error = Write(mEncoderBuffer.GetFrame(), mEncoderBuffer.GetLength()));
140
141 exit:
142
143 k_mutex_unlock(&spinel_hdlc_wr_lock);
144
145 if (error != OT_ERROR_NONE) {
146 otLogCritPlat("error = 0x%x", error);
147 }
148
149 return error;
150 }
151
WaitForFrame(uint64_t aTimeoutUs)152 otError HdlcInterface::WaitForFrame(uint64_t aTimeoutUs)
153 {
154 otError error = OT_ERROR_RESPONSE_TIMEOUT;
155 uint32_t event_bits;
156
157 do {
158 /* Wait for k_spinel_hdlc_frame_ready_event indicating a frame has been received */
159 event_bits = k_event_wait(&spinel_hdlc_event,
160 HdlcInterface::k_spinel_hdlc_frame_ready_event, false,
161 K_USEC(aTimeoutUs));
162 k_event_clear(&spinel_hdlc_event, HdlcInterface::k_spinel_hdlc_frame_ready_event);
163 if ((event_bits & HdlcInterface::k_spinel_hdlc_frame_ready_event) != 0) {
164 /* Event is set, it means a frame has been received and can be decoded
165 * Note: The event is set whenever a frame is received, even when ot task is
166 * not blocked in WaitForFrame it means the event could have been set for a
167 * previous frame If TryReadAndDecode returns 0, it means the event was set
168 * for a previous frame, so we loop and wait again If TryReadAndDecode
169 * returns anything else, it means there's real data to process, so we can
170 * exit from WaitForFrame */
171
172 if (TryReadAndDecode(true) != 0) {
173 error = OT_ERROR_NONE;
174 break;
175 }
176 } else {
177 /* The event wasn't set within the timeout range, we exit with a timeout
178 * error */
179 otLogDebgPlat("WaitForFrame timeout");
180 break;
181 }
182 } while (true);
183
184 return error;
185 }
186
ProcessRxData(const uint8_t * data,uint16_t len)187 void HdlcInterface::ProcessRxData(const uint8_t *data, uint16_t len)
188 {
189 uint8_t event;
190 uint32_t remainingRxBufferSize = 0;
191
192 k_mutex_lock(&spinel_hdlc_rd_lock, K_FOREVER);
193
194 do {
195 /* Check if we have enough space to store the frame in the buffer */
196 remainingRxBufferSize =
197 mRxSpinelFrameBuffer.GetFrameMaxLength() - mRxSpinelFrameBuffer.GetLength();
198 otLogDebgPlat("remainingRxBufferSize = %u", remainingRxBufferSize);
199
200 if (remainingRxBufferSize >= len) {
201 mHdlcSpinelDecoder.Decode(data, len);
202 break;
203 } else {
204 mIsSpinelBufferFull = true;
205 otLogDebgPlat("Spinel buffer full remainingRxLen = %u",
206 remainingRxBufferSize);
207
208 /* Send a signal to the openthread task to indicate to empty the spinel
209 * buffer */
210 otTaskletsSignalPending(NULL);
211
212 /* Give the mutex */
213 k_mutex_unlock(&spinel_hdlc_rd_lock);
214
215 /* Lock the task here until the spinel buffer becomes empty */
216 k_msgq_get(&spinel_hdlc_msgq, &event, K_FOREVER);
217
218 /* take the mutex again */
219 k_mutex_lock(&spinel_hdlc_rd_lock, K_FOREVER);
220 }
221 } while (true);
222
223 k_mutex_unlock(&spinel_hdlc_rd_lock);
224 }
225
Write(const uint8_t * aFrame,uint16_t aLength)226 otError HdlcInterface::Write(const uint8_t *aFrame, uint16_t aLength)
227 {
228 otError otResult = OT_ERROR_NONE;
229 int ret;
230
231 otLogDebgPlat("Send tx frame len = %d", aLength);
232
233 ret = hdlc_api->send((uint8_t *)aFrame, aLength);
234 if (ret != 0) {
235 otResult = OT_ERROR_FAILED;
236 otLogCritPlat("Error send %d", ret);
237 }
238
239 /* Always clear the encoder */
240 mEncoderBuffer.Clear();
241 return otResult;
242 }
243
TryReadAndDecode(bool fullRead)244 uint32_t HdlcInterface::TryReadAndDecode(bool fullRead)
245 {
246 uint32_t totalBytesRead = 0;
247 uint32_t i = 0;
248 uint8_t event = 1;
249 uint8_t *oldFrame = mSavedFrame;
250 uint16_t oldLen = mSavedFrameLen;
251 otError savedFrameFound = OT_ERROR_NONE;
252
253 (void)fullRead;
254
255 k_mutex_lock(&spinel_hdlc_rd_lock, K_FOREVER);
256
257 savedFrameFound = mRxSpinelFrameBuffer.GetNextSavedFrame(mSavedFrame, mSavedFrameLen);
258
259 while (savedFrameFound == OT_ERROR_NONE) {
260 /* Copy the data to the ot frame buffer */
261 for (i = 0; i < mSavedFrameLen; i++) {
262 if (mReceiveFrameBuffer->WriteByte(mSavedFrame[i]) != OT_ERROR_NONE) {
263 mReceiveFrameBuffer->UndoLastWrites(i);
264 /* No more space restore the mSavedFrame to the previous frame */
265 mSavedFrame = oldFrame;
266 mSavedFrameLen = oldLen;
267 /* Signal the ot task to re-try later */
268 otTaskletsSignalPending(NULL);
269 otLogDebgPlat("No more space");
270 k_mutex_unlock(&spinel_hdlc_rd_lock);
271 return totalBytesRead;
272 }
273 totalBytesRead++;
274 }
275 otLogDebgPlat("Frame len %d consumed", mSavedFrameLen);
276 mReceiveFrameCallback(mReceiveFrameContext);
277 oldFrame = mSavedFrame;
278 oldLen = mSavedFrameLen;
279 savedFrameFound =
280 mRxSpinelFrameBuffer.GetNextSavedFrame(mSavedFrame, mSavedFrameLen);
281 }
282
283 if (savedFrameFound != OT_ERROR_NONE) {
284 /* No more frame saved clear the buffer */
285 mRxSpinelFrameBuffer.ClearSavedFrames();
286 /* If the spinel queue was locked */
287 if (mIsSpinelBufferFull) {
288 mIsSpinelBufferFull = false;
289 /* Send an event to unlock the task */
290 k_msgq_put(&spinel_hdlc_msgq, (void *)&event, K_FOREVER);
291 }
292 }
293
294 k_mutex_unlock(&spinel_hdlc_rd_lock);
295
296 return totalBytesRead;
297 }
298
HandleHdlcFrame(void * aContext,otError aError)299 void HdlcInterface::HandleHdlcFrame(void *aContext, otError aError)
300 {
301 static_cast<HdlcInterface *>(aContext)->HandleHdlcFrame(aError);
302 }
303
HandleHdlcFrame(otError aError)304 void HdlcInterface::HandleHdlcFrame(otError aError)
305 {
306 uint8_t *buf = mRxSpinelFrameBuffer.GetFrame();
307 uint16_t bufLength = mRxSpinelFrameBuffer.GetLength();
308
309 otDumpDebgPlat("RX FRAME", buf, bufLength);
310
311 if (aError == OT_ERROR_NONE && bufLength > 0) {
312 if ((buf[0] & SPINEL_HEADER_FLAG) == SPINEL_HEADER_FLAG) {
313 otLogDebgPlat("Frame correctly received %d", bufLength);
314 /* Save the frame */
315 mRxSpinelFrameBuffer.SaveFrame();
316
317 /* Send a signal to the openthread task to indicate that a spinel data is
318 * pending */
319 otTaskletsSignalPending(NULL);
320
321 /* Notify WaitForFrame that a frame is ready */
322 /* TBC: k_event_post or k_event_set */
323 k_event_set(&spinel_hdlc_event,
324 HdlcInterface::k_spinel_hdlc_frame_ready_event);
325 } else {
326 /* Give a chance to a child class to process this HDLC content
327 * The current class treats it as an error case because it's supposed to
328 * receive only Spinel frames If there's a need to share a same transport
329 * interface with another protocol, a child class must override this method
330 */
331 HandleUnknownHdlcContent(buf, bufLength);
332
333 /* Not a Spinel frame, discard */
334 mRxSpinelFrameBuffer.DiscardFrame();
335 }
336 } else {
337 otLogCritPlat("Frame will be discarded error = 0x%x", aError);
338 mRxSpinelFrameBuffer.DiscardFrame();
339 }
340 }
341
HdlcRxCallback(const uint8_t * data,uint16_t len,void * param)342 void HdlcInterface::HdlcRxCallback(const uint8_t *data, uint16_t len, void *param)
343 {
344 static_cast<HdlcInterface *>(param)->ProcessRxData(data, len);
345 }
346
HandleUnknownHdlcContent(uint8_t * buffer,uint16_t len)347 void HdlcInterface::HandleUnknownHdlcContent(uint8_t *buffer, uint16_t len)
348 {
349 OT_UNUSED_VARIABLE(buffer);
350 OT_UNUSED_VARIABLE(len);
351 otLogCritPlat("Unsupported HDLC content received (not Spinel)");
352 assert(0);
353 }
354
OnRcpReset(void)355 void HdlcInterface::OnRcpReset(void)
356 {
357 mHdlcSpinelDecoder.Reset();
358 }
359
360 } // namespace Hdlc
361
362 } /* namespace ot */
363