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