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