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 timeout_ms = (uint32_t)(aTimeoutUs / 1000U);
156 uint32_t event_bits;
157
158 do {
159 /* Wait for k_spinel_hdlc_frame_ready_event indicating a frame has been received */
160 event_bits = k_event_wait(&spinel_hdlc_event,
161 HdlcInterface::k_spinel_hdlc_frame_ready_event, false,
162 K_MSEC(timeout_ms));
163 k_event_clear(&spinel_hdlc_event, HdlcInterface::k_spinel_hdlc_frame_ready_event);
164 if ((event_bits & HdlcInterface::k_spinel_hdlc_frame_ready_event) != 0) {
165 /* Event is set, it means a frame has been received and can be decoded
166 * Note: The event is set whenever a frame is received, even when ot task is
167 * not blocked in WaitForFrame it means the event could have been set for a
168 * previous frame If TryReadAndDecode returns 0, it means the event was set
169 * for a previous frame, so we loop and wait again If TryReadAndDecode
170 * returns anything else, it means there's real data to process, so we can
171 * exit from WaitForFrame */
172
173 if (TryReadAndDecode(true) != 0) {
174 error = OT_ERROR_NONE;
175 break;
176 }
177 } else {
178 /* The event wasn't set within the timeout range, we exit with a timeout
179 * error */
180 otLogDebgPlat("WaitForFrame timeout");
181 break;
182 }
183 } while (true);
184
185 return error;
186 }
187
ProcessRxData(uint8_t * data,uint16_t len)188 void HdlcInterface::ProcessRxData(uint8_t *data, uint16_t len)
189 {
190 uint8_t event;
191 uint32_t remainingRxBufferSize = 0;
192
193 k_mutex_lock(&spinel_hdlc_rd_lock, K_FOREVER);
194
195 do {
196 /* Check if we have enough space to store the frame in the buffer */
197 remainingRxBufferSize =
198 mRxSpinelFrameBuffer.GetFrameMaxLength() - mRxSpinelFrameBuffer.GetLength();
199 otLogDebgPlat("remainingRxBufferSize = %u", remainingRxBufferSize);
200
201 if (remainingRxBufferSize >= len) {
202 mHdlcSpinelDecoder.Decode(data, len);
203 break;
204 } else {
205 mIsSpinelBufferFull = true;
206 otLogDebgPlat("Spinel buffer full remainingRxLen = %u",
207 remainingRxBufferSize);
208
209 /* Send a signal to the openthread task to indicate to empty the spinel
210 * buffer */
211 otTaskletsSignalPending(NULL);
212
213 /* Give the mutex */
214 k_mutex_unlock(&spinel_hdlc_rd_lock);
215
216 /* Lock the task here until the spinel buffer becomes empty */
217 k_msgq_get(&spinel_hdlc_msgq, &event, K_FOREVER);
218
219 /* take the mutex again */
220 k_mutex_lock(&spinel_hdlc_rd_lock, K_FOREVER);
221 }
222 } while (true);
223
224 k_mutex_unlock(&spinel_hdlc_rd_lock);
225 }
226
Write(const uint8_t * aFrame,uint16_t aLength)227 otError HdlcInterface::Write(const uint8_t *aFrame, uint16_t aLength)
228 {
229 otError otResult = OT_ERROR_NONE;
230 int ret;
231
232 otLogDebgPlat("Send tx frame len = %d", aLength);
233
234 ret = hdlc_api->send((uint8_t *)aFrame, aLength);
235 if (ret != 0) {
236 otResult = OT_ERROR_FAILED;
237 otLogCritPlat("Error send %d", ret);
238 }
239
240 /* Always clear the encoder */
241 mEncoderBuffer.Clear();
242 return otResult;
243 }
244
TryReadAndDecode(bool fullRead)245 uint32_t HdlcInterface::TryReadAndDecode(bool fullRead)
246 {
247 uint32_t totalBytesRead = 0;
248 uint32_t i = 0;
249 uint8_t event = 1;
250 uint8_t *oldFrame = mSavedFrame;
251 uint16_t oldLen = mSavedFrameLen;
252 otError savedFrameFound = OT_ERROR_NONE;
253
254 (void)fullRead;
255
256 k_mutex_lock(&spinel_hdlc_rd_lock, K_FOREVER);
257
258 savedFrameFound = mRxSpinelFrameBuffer.GetNextSavedFrame(mSavedFrame, mSavedFrameLen);
259
260 while (savedFrameFound == OT_ERROR_NONE) {
261 /* Copy the data to the ot frame buffer */
262 for (i = 0; i < mSavedFrameLen; i++) {
263 if (mReceiveFrameBuffer->WriteByte(mSavedFrame[i]) != OT_ERROR_NONE) {
264 mReceiveFrameBuffer->UndoLastWrites(i);
265 /* No more space restore the mSavedFrame to the previous frame */
266 mSavedFrame = oldFrame;
267 mSavedFrameLen = oldLen;
268 /* Signal the ot task to re-try later */
269 otTaskletsSignalPending(NULL);
270 otLogDebgPlat("No more space");
271 k_mutex_unlock(&spinel_hdlc_rd_lock);
272 return totalBytesRead;
273 }
274 totalBytesRead++;
275 }
276 otLogDebgPlat("Frame len %d consumed", mSavedFrameLen);
277 mReceiveFrameCallback(mReceiveFrameContext);
278 oldFrame = mSavedFrame;
279 oldLen = mSavedFrameLen;
280 savedFrameFound =
281 mRxSpinelFrameBuffer.GetNextSavedFrame(mSavedFrame, mSavedFrameLen);
282 }
283
284 if (savedFrameFound != OT_ERROR_NONE) {
285 /* No more frame saved clear the buffer */
286 mRxSpinelFrameBuffer.ClearSavedFrames();
287 /* If the spinel queue was locked */
288 if (mIsSpinelBufferFull) {
289 mIsSpinelBufferFull = false;
290 /* Send an event to unlock the task */
291 k_msgq_put(&spinel_hdlc_msgq, (void *)&event, K_FOREVER);
292 }
293 }
294
295 k_mutex_unlock(&spinel_hdlc_rd_lock);
296
297 return totalBytesRead;
298 }
299
HandleHdlcFrame(void * aContext,otError aError)300 void HdlcInterface::HandleHdlcFrame(void *aContext, otError aError)
301 {
302 static_cast<HdlcInterface *>(aContext)->HandleHdlcFrame(aError);
303 }
304
HandleHdlcFrame(otError aError)305 void HdlcInterface::HandleHdlcFrame(otError aError)
306 {
307 uint8_t *buf = mRxSpinelFrameBuffer.GetFrame();
308 uint16_t bufLength = mRxSpinelFrameBuffer.GetLength();
309
310 otDumpDebgPlat("RX FRAME", buf, bufLength);
311
312 if (aError == OT_ERROR_NONE && bufLength > 0) {
313 if ((buf[0] & SPINEL_HEADER_FLAG) == SPINEL_HEADER_FLAG) {
314 otLogDebgPlat("Frame correctly received %d", bufLength);
315 /* Save the frame */
316 mRxSpinelFrameBuffer.SaveFrame();
317
318 /* Send a signal to the openthread task to indicate that a spinel data is
319 * pending */
320 otTaskletsSignalPending(NULL);
321
322 /* Notify WaitForFrame that a frame is ready */
323 /* TBC: k_event_post or k_event_set */
324 k_event_set(&spinel_hdlc_event,
325 HdlcInterface::k_spinel_hdlc_frame_ready_event);
326 } else {
327 /* Give a chance to a child class to process this HDLC content
328 * The current class treats it as an error case because it's supposed to
329 * receive only Spinel frames If there's a need to share a same transport
330 * interface with another protocol, a child class must override this method
331 */
332 HandleUnknownHdlcContent(buf, bufLength);
333
334 /* Not a Spinel frame, discard */
335 mRxSpinelFrameBuffer.DiscardFrame();
336 }
337 } else {
338 otLogCritPlat("Frame will be discarded error = 0x%x", aError);
339 mRxSpinelFrameBuffer.DiscardFrame();
340 }
341 }
342
HdlcRxCallback(uint8_t * data,uint16_t len,void * param)343 void HdlcInterface::HdlcRxCallback(uint8_t *data, uint16_t len, void *param)
344 {
345 static_cast<HdlcInterface *>(param)->ProcessRxData(data, len);
346 }
347
HandleUnknownHdlcContent(uint8_t * buffer,uint16_t len)348 void HdlcInterface::HandleUnknownHdlcContent(uint8_t *buffer, uint16_t len)
349 {
350 OT_UNUSED_VARIABLE(buffer);
351 OT_UNUSED_VARIABLE(len);
352 otLogCritPlat("Unsupported HDLC content received (not Spinel)");
353 assert(0);
354 }
355
OnRcpReset(void)356 void HdlcInterface::OnRcpReset(void)
357 {
358 mHdlcSpinelDecoder.Reset();
359 }
360
361 } // namespace Hdlc
362
363 } /* namespace ot */
364