1 /*
2 * Copyright (c) 2018, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /**
30 * @file
31 * This file includes the implementation for the HDLC interface to radio (RCP).
32 */
33
34 #include "hdlc_interface.hpp"
35
36 #include "platform-posix.h"
37
38 #include <assert.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #if OPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE
42 #if defined(__APPLE__) || defined(__NetBSD__)
43 #include <util.h>
44 #elif defined(__FreeBSD__)
45 #include <libutil.h>
46 #else
47 #include <pty.h>
48 #endif
49 #endif
50 #include <stdarg.h>
51 #include <stdlib.h>
52 #include <sys/ioctl.h>
53 #include <sys/resource.h>
54 #include <sys/stat.h>
55 #include <sys/time.h>
56 #include <sys/wait.h>
57 #include <syslog.h>
58 #include <termios.h>
59 #include <unistd.h>
60
61 #include <openthread/logging.h>
62
63 #include "common/code_utils.hpp"
64 #include "lib/spinel/spinel.h"
65
66 #ifdef __APPLE__
67
68 #ifndef B230400
69 #define B230400 230400
70 #endif
71
72 #ifndef B460800
73 #define B460800 460800
74 #endif
75
76 #ifndef B500000
77 #define B500000 500000
78 #endif
79
80 #ifndef B576000
81 #define B576000 576000
82 #endif
83
84 #ifndef B921600
85 #define B921600 921600
86 #endif
87
88 #ifndef B1000000
89 #define B1000000 1000000
90 #endif
91
92 #ifndef B1152000
93 #define B1152000 1152000
94 #endif
95
96 #ifndef B1500000
97 #define B1500000 1500000
98 #endif
99
100 #ifndef B2000000
101 #define B2000000 2000000
102 #endif
103
104 #ifndef B2500000
105 #define B2500000 2500000
106 #endif
107
108 #ifndef B3000000
109 #define B3000000 3000000
110 #endif
111
112 #ifndef B3500000
113 #define B3500000 3500000
114 #endif
115
116 #ifndef B4000000
117 #define B4000000 4000000
118 #endif
119
120 #ifndef IOSSIOSPEED
121 #define IOSSIOSPEED 0x80045402
122 #endif
123
124 #endif // __APPLE__
125
126 #if OPENTHREAD_POSIX_CONFIG_SPINEL_HDLC_INTERFACE_ENABLE
127
128 namespace ot {
129 namespace Posix {
130
131 const char HdlcInterface::kLogModuleName[] = "HdlcIntface";
132
HdlcInterface(const Url::Url & aRadioUrl)133 HdlcInterface::HdlcInterface(const Url::Url &aRadioUrl)
134 : mReceiveFrameCallback(nullptr)
135 , mReceiveFrameContext(nullptr)
136 , mReceiveFrameBuffer(nullptr)
137 , mSockFd(-1)
138 , mBaudRate(0)
139 , mHdlcDecoder()
140 , mRadioUrl(aRadioUrl)
141 {
142 memset(&mInterfaceMetrics, 0, sizeof(mInterfaceMetrics));
143 mInterfaceMetrics.mRcpInterfaceType = kSpinelInterfaceTypeHdlc;
144 }
145
Init(ReceiveFrameCallback aCallback,void * aCallbackContext,RxFrameBuffer & aFrameBuffer)146 otError HdlcInterface::Init(ReceiveFrameCallback aCallback, void *aCallbackContext, RxFrameBuffer &aFrameBuffer)
147 {
148 otError error = OT_ERROR_NONE;
149 struct stat st;
150
151 VerifyOrExit(mSockFd == -1, error = OT_ERROR_ALREADY);
152
153 VerifyOrDie(stat(mRadioUrl.GetPath(), &st) == 0, OT_EXIT_ERROR_ERRNO);
154
155 if (S_ISCHR(st.st_mode))
156 {
157 mSockFd = OpenFile(mRadioUrl);
158 VerifyOrExit(mSockFd != -1, error = OT_ERROR_FAILED);
159 }
160 #if OPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE
161 else if (S_ISREG(st.st_mode))
162 {
163 mSockFd = ForkPty(mRadioUrl);
164 VerifyOrExit(mSockFd != -1, error = OT_ERROR_FAILED);
165 }
166 #endif // OPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE
167 else
168 {
169 LogCrit("Radio file '%s' not supported", mRadioUrl.GetPath());
170 ExitNow(error = OT_ERROR_FAILED);
171 }
172
173 mHdlcDecoder.Init(aFrameBuffer, HandleHdlcFrame, this);
174 mReceiveFrameCallback = aCallback;
175 mReceiveFrameContext = aCallbackContext;
176 mReceiveFrameBuffer = &aFrameBuffer;
177
178 exit:
179 return error;
180 }
181
~HdlcInterface(void)182 HdlcInterface::~HdlcInterface(void) { Deinit(); }
183
Deinit(void)184 void HdlcInterface::Deinit(void)
185 {
186 CloseFile();
187
188 mReceiveFrameCallback = nullptr;
189 mReceiveFrameContext = nullptr;
190 mReceiveFrameBuffer = nullptr;
191 }
192
Read(void)193 void HdlcInterface::Read(void)
194 {
195 uint8_t buffer[kMaxFrameSize];
196 ssize_t rval;
197
198 rval = read(mSockFd, buffer, sizeof(buffer));
199
200 if (rval > 0)
201 {
202 Decode(buffer, static_cast<uint16_t>(rval));
203 }
204 else if ((rval < 0) && (errno != EAGAIN) && (errno != EINTR))
205 {
206 DieNow(OT_EXIT_ERROR_ERRNO);
207 }
208 }
209
Decode(const uint8_t * aBuffer,uint16_t aLength)210 void HdlcInterface::Decode(const uint8_t *aBuffer, uint16_t aLength) { mHdlcDecoder.Decode(aBuffer, aLength); }
211
SendFrame(const uint8_t * aFrame,uint16_t aLength)212 otError HdlcInterface::SendFrame(const uint8_t *aFrame, uint16_t aLength)
213 {
214 otError error = OT_ERROR_NONE;
215 Spinel::FrameBuffer<kMaxFrameSize> encoderBuffer;
216 Hdlc::Encoder hdlcEncoder(encoderBuffer);
217
218 SuccessOrExit(error = hdlcEncoder.BeginFrame());
219 SuccessOrExit(error = hdlcEncoder.Encode(aFrame, aLength));
220 SuccessOrExit(error = hdlcEncoder.EndFrame());
221
222 error = Write(encoderBuffer.GetFrame(), encoderBuffer.GetLength());
223
224 exit:
225 if ((error == OT_ERROR_NONE) && IsSpinelResetCommand(aFrame, aLength))
226 {
227 mHdlcDecoder.Reset();
228 error = ResetConnection();
229 }
230
231 return error;
232 }
233
Write(const uint8_t * aFrame,uint16_t aLength)234 otError HdlcInterface::Write(const uint8_t *aFrame, uint16_t aLength)
235 {
236 otError error = OT_ERROR_NONE;
237 #if OPENTHREAD_POSIX_VIRTUAL_TIME
238 virtualTimeSendRadioSpinelWriteEvent(aFrame, aLength);
239 #else
240 while (aLength)
241 {
242 ssize_t rval = write(mSockFd, aFrame, aLength);
243
244 if (rval == aLength)
245 {
246 break;
247 }
248 else if (rval > 0)
249 {
250 aLength -= static_cast<uint16_t>(rval);
251 aFrame += static_cast<uint16_t>(rval);
252 }
253 else if (rval < 0)
254 {
255 VerifyOrDie((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR), OT_EXIT_ERROR_ERRNO);
256 }
257
258 SuccessOrExit(error = WaitForWritable());
259 }
260
261 exit:
262 #endif // OPENTHREAD_POSIX_VIRTUAL_TIME
263
264 mInterfaceMetrics.mTransferredFrameCount++;
265 if (error == OT_ERROR_NONE)
266 {
267 mInterfaceMetrics.mTxFrameCount++;
268 mInterfaceMetrics.mTxFrameByteCount += aLength;
269 mInterfaceMetrics.mTransferredValidFrameCount++;
270 }
271 else
272 {
273 mInterfaceMetrics.mTransferredGarbageFrameCount++;
274 }
275
276 return error;
277 }
278
WaitForFrame(uint64_t aTimeoutUs)279 otError HdlcInterface::WaitForFrame(uint64_t aTimeoutUs)
280 {
281 otError error = OT_ERROR_NONE;
282 struct timeval timeout;
283 #if OPENTHREAD_POSIX_VIRTUAL_TIME
284 struct VirtualTimeEvent event;
285
286 timeout.tv_sec = static_cast<time_t>(aTimeoutUs / OT_US_PER_S);
287 timeout.tv_usec = static_cast<suseconds_t>(aTimeoutUs % OT_US_PER_S);
288
289 virtualTimeSendSleepEvent(&timeout);
290 virtualTimeReceiveEvent(&event);
291
292 switch (event.mEvent)
293 {
294 case OT_SIM_EVENT_RADIO_SPINEL_WRITE:
295 Decode(event.mData, event.mDataLength);
296 break;
297
298 case OT_SIM_EVENT_ALARM_FIRED:
299 VerifyOrExit(event.mDelay <= aTimeoutUs, error = OT_ERROR_RESPONSE_TIMEOUT);
300 break;
301
302 default:
303 assert(false);
304 break;
305 }
306 #else // OPENTHREAD_POSIX_VIRTUAL_TIME
307 timeout.tv_sec = static_cast<time_t>(aTimeoutUs / OT_US_PER_S);
308 timeout.tv_usec = static_cast<suseconds_t>(aTimeoutUs % OT_US_PER_S);
309
310 fd_set read_fds;
311 fd_set error_fds;
312 int rval;
313
314 FD_ZERO(&read_fds);
315 FD_ZERO(&error_fds);
316 FD_SET(mSockFd, &read_fds);
317 FD_SET(mSockFd, &error_fds);
318
319 rval = select(mSockFd + 1, &read_fds, nullptr, &error_fds, &timeout);
320
321 if (rval > 0)
322 {
323 if (FD_ISSET(mSockFd, &read_fds))
324 {
325 Read();
326 }
327 else if (FD_ISSET(mSockFd, &error_fds))
328 {
329 DieNowWithMessage("NCP error", OT_EXIT_FAILURE);
330 }
331 else
332 {
333 DieNow(OT_EXIT_FAILURE);
334 }
335 }
336 else if (rval == 0)
337 {
338 ExitNow(error = OT_ERROR_RESPONSE_TIMEOUT);
339 }
340 else if (errno != EINTR)
341 {
342 DieNowWithMessage("wait response", OT_EXIT_FAILURE);
343 }
344 #endif // OPENTHREAD_POSIX_VIRTUAL_TIME
345
346 exit:
347 return error;
348 }
349
UpdateFdSet(void * aMainloopContext)350 void HdlcInterface::UpdateFdSet(void *aMainloopContext)
351 {
352 otSysMainloopContext *context = reinterpret_cast<otSysMainloopContext *>(aMainloopContext);
353
354 assert(context != nullptr);
355
356 FD_SET(mSockFd, &context->mReadFdSet);
357
358 if (context->mMaxFd < mSockFd)
359 {
360 context->mMaxFd = mSockFd;
361 }
362 }
363
Process(const void * aMainloopContext)364 void HdlcInterface::Process(const void *aMainloopContext)
365 {
366 #if OPENTHREAD_POSIX_VIRTUAL_TIME
367 /**
368 * Process read data (decode the data).
369 *
370 * Is intended only for virtual time simulation. Its behavior is similar to `Read()` but instead of
371 * reading the data from the radio socket, it uses the given data in @p `event`.
372 */
373 const VirtualTimeEvent *event = reinterpret_cast<const VirtualTimeEvent *>(aMainloopContext);
374
375 assert(event != nullptr);
376
377 Decode(event->mData, event->mDataLength);
378 #else
379 const otSysMainloopContext *context = reinterpret_cast<const otSysMainloopContext *>(aMainloopContext);
380
381 assert(context != nullptr);
382
383 if (FD_ISSET(mSockFd, &context->mReadFdSet))
384 {
385 Read();
386 }
387 #endif
388 }
389
WaitForWritable(void)390 otError HdlcInterface::WaitForWritable(void)
391 {
392 otError error = OT_ERROR_NONE;
393 struct timeval timeout = {kMaxWaitTime / 1000, (kMaxWaitTime % 1000) * 1000};
394 uint64_t now = otPlatTimeGet();
395 uint64_t end = now + kMaxWaitTime * OT_US_PER_MS;
396 fd_set writeFds;
397 fd_set errorFds;
398 int rval;
399
400 while (true)
401 {
402 FD_ZERO(&writeFds);
403 FD_ZERO(&errorFds);
404 FD_SET(mSockFd, &writeFds);
405 FD_SET(mSockFd, &errorFds);
406
407 rval = select(mSockFd + 1, nullptr, &writeFds, &errorFds, &timeout);
408
409 if (rval > 0)
410 {
411 if (FD_ISSET(mSockFd, &writeFds))
412 {
413 ExitNow();
414 }
415 else if (FD_ISSET(mSockFd, &errorFds))
416 {
417 DieNow(OT_EXIT_FAILURE);
418 }
419 else
420 {
421 assert(false);
422 }
423 }
424 else if ((rval < 0) && (errno != EINTR))
425 {
426 DieNow(OT_EXIT_ERROR_ERRNO);
427 }
428
429 now = otPlatTimeGet();
430
431 if (end > now)
432 {
433 uint64_t remain = end - now;
434
435 timeout.tv_sec = static_cast<time_t>(remain / OT_US_PER_S);
436 timeout.tv_usec = static_cast<suseconds_t>(remain % OT_US_PER_S);
437 }
438 else
439 {
440 break;
441 }
442 }
443
444 error = OT_ERROR_FAILED;
445
446 exit:
447 return error;
448 }
449
OpenFile(const Url::Url & aRadioUrl)450 int HdlcInterface::OpenFile(const Url::Url &aRadioUrl)
451 {
452 int fd = -1;
453 int rval = 0;
454
455 fd = open(aRadioUrl.GetPath(), O_RDWR | O_NOCTTY | O_NONBLOCK | O_CLOEXEC);
456 if (fd == -1)
457 {
458 perror("open uart failed");
459 ExitNow();
460 }
461
462 if (isatty(fd))
463 {
464 struct termios tios;
465 const char *value;
466 speed_t speed;
467 uint8_t stopBit = 1;
468 uint32_t baudrate = 115200;
469
470 VerifyOrExit((rval = tcgetattr(fd, &tios)) == 0);
471
472 cfmakeraw(&tios);
473
474 tios.c_cflag = CS8 | HUPCL | CREAD | CLOCAL;
475
476 if ((value = aRadioUrl.GetValue("uart-parity")) != nullptr)
477 {
478 if (strncmp(value, "odd", 3) == 0)
479 {
480 tios.c_cflag |= PARENB;
481 tios.c_cflag |= PARODD;
482 }
483 else if (strncmp(value, "even", 4) == 0)
484 {
485 tios.c_cflag |= PARENB;
486 }
487 else
488 {
489 DieNow(OT_EXIT_INVALID_ARGUMENTS);
490 }
491 }
492
493 IgnoreError(aRadioUrl.ParseUint8("uart-stop", stopBit));
494
495 switch (stopBit)
496 {
497 case 1:
498 tios.c_cflag &= static_cast<unsigned long>(~CSTOPB);
499 break;
500 case 2:
501 tios.c_cflag |= CSTOPB;
502 break;
503 default:
504 DieNow(OT_EXIT_INVALID_ARGUMENTS);
505 break;
506 }
507
508 IgnoreError(aRadioUrl.ParseUint32("uart-baudrate", baudrate));
509
510 switch (baudrate)
511 {
512 case 9600:
513 speed = B9600;
514 break;
515 case 19200:
516 speed = B19200;
517 break;
518 case 38400:
519 speed = B38400;
520 break;
521 case 57600:
522 speed = B57600;
523 break;
524 case 115200:
525 speed = B115200;
526 break;
527 #ifdef B230400
528 case 230400:
529 speed = B230400;
530 break;
531 #endif
532 #ifdef B460800
533 case 460800:
534 speed = B460800;
535 break;
536 #endif
537 #ifdef B500000
538 case 500000:
539 speed = B500000;
540 break;
541 #endif
542 #ifdef B576000
543 case 576000:
544 speed = B576000;
545 break;
546 #endif
547 #ifdef B921600
548 case 921600:
549 speed = B921600;
550 break;
551 #endif
552 #ifdef B1000000
553 case 1000000:
554 speed = B1000000;
555 break;
556 #endif
557 #ifdef B1152000
558 case 1152000:
559 speed = B1152000;
560 break;
561 #endif
562 #ifdef B1500000
563 case 1500000:
564 speed = B1500000;
565 break;
566 #endif
567 #ifdef B2000000
568 case 2000000:
569 speed = B2000000;
570 break;
571 #endif
572 #ifdef B2500000
573 case 2500000:
574 speed = B2500000;
575 break;
576 #endif
577 #ifdef B3000000
578 case 3000000:
579 speed = B3000000;
580 break;
581 #endif
582 #ifdef B3500000
583 case 3500000:
584 speed = B3500000;
585 break;
586 #endif
587 #ifdef B4000000
588 case 4000000:
589 speed = B4000000;
590 break;
591 #endif
592 default:
593 DieNow(OT_EXIT_INVALID_ARGUMENTS);
594 break;
595 }
596
597 mBaudRate = baudrate;
598
599 if (aRadioUrl.HasParam("uart-flow-control"))
600 {
601 tios.c_cflag |= CRTSCTS;
602 }
603 else if (aRadioUrl.HasParam("uart-init-deassert"))
604 {
605 // When flow control is disabled, deassert DTR and RTS on init
606 #ifndef __APPLE__
607 int flags;
608 #endif
609
610 tios.c_cflag &= ~(CRTSCTS);
611
612 #ifndef __APPLE__
613 // Deassert DTR and RTS
614 flags = TIOCM_DTR | TIOCM_RTS;
615 VerifyOrExit(ioctl(fd, TIOCMBIC, &flags) != -1, perror("tiocmbic"));
616 #endif
617 }
618
619 VerifyOrExit((rval = cfsetspeed(&tios, static_cast<speed_t>(speed))) == 0, perror("cfsetspeed"));
620 rval = tcsetattr(fd, TCSANOW, &tios);
621
622 #ifdef __APPLE__
623 if (rval)
624 {
625 struct termios orig_tios;
626 VerifyOrExit((rval = tcgetattr(fd, &orig_tios)) == 0, perror("tcgetattr"));
627 VerifyOrExit((rval = cfsetispeed(&tios, cfgetispeed(&orig_tios))) == 0, perror("cfsetispeed"));
628 VerifyOrExit((rval = cfsetospeed(&tios, cfgetospeed(&orig_tios))) == 0, perror("cfsetospeed"));
629 VerifyOrExit((rval = tcsetattr(fd, TCSANOW, &tios)) == 0, perror("tcsetattr"));
630 VerifyOrExit((rval = ioctl(fd, IOSSIOSPEED, &speed)) == 0, perror("ioctl IOSSIOSPEED"));
631 }
632 #else // __APPLE__
633 VerifyOrExit(rval == 0, perror("tcsetattr"));
634 #endif // __APPLE__
635 VerifyOrExit((rval = tcflush(fd, TCIOFLUSH)) == 0);
636 }
637
638 exit:
639 if (rval != 0)
640 {
641 DieNow(OT_EXIT_FAILURE);
642 }
643
644 return fd;
645 }
646
CloseFile(void)647 void HdlcInterface::CloseFile(void)
648 {
649 VerifyOrExit(mSockFd != -1);
650
651 VerifyOrExit(0 == close(mSockFd), perror("close RCP"));
652 VerifyOrExit(-1 != wait(nullptr) || errno == ECHILD, perror("wait RCP"));
653
654 mSockFd = -1;
655
656 exit:
657 return;
658 }
659
660 #if OPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE
ForkPty(const Url::Url & aRadioUrl)661 int HdlcInterface::ForkPty(const Url::Url &aRadioUrl)
662 {
663 int fd = -1;
664 int pid = -1;
665 int rval = -1;
666
667 {
668 struct termios tios;
669
670 memset(&tios, 0, sizeof(tios));
671 cfmakeraw(&tios);
672 tios.c_cflag = CS8 | HUPCL | CREAD | CLOCAL;
673
674 VerifyOrDie((pid = forkpty(&fd, nullptr, &tios, nullptr)) != -1, OT_EXIT_ERROR_ERRNO);
675 }
676
677 if (0 == pid)
678 {
679 constexpr int kMaxArguments = 32;
680 char *argv[kMaxArguments + 1];
681 size_t index = 0;
682
683 argv[index++] = const_cast<char *>(aRadioUrl.GetPath());
684
685 for (const char *arg = nullptr;
686 index < OT_ARRAY_LENGTH(argv) && (arg = aRadioUrl.GetValue("forkpty-arg", arg)) != nullptr;
687 argv[index++] = const_cast<char *>(arg))
688 {
689 }
690
691 if (index < OT_ARRAY_LENGTH(argv))
692 {
693 argv[index] = nullptr;
694 }
695 else
696 {
697 DieNowWithMessage("Too many arguments!", OT_EXIT_INVALID_ARGUMENTS);
698 }
699
700 VerifyOrDie((rval = execvp(argv[0], argv)) != -1, OT_EXIT_ERROR_ERRNO);
701 }
702 else
703 {
704 VerifyOrDie((rval = fcntl(fd, F_GETFL)) != -1, OT_EXIT_ERROR_ERRNO);
705 VerifyOrDie((rval = fcntl(fd, F_SETFL, rval | O_NONBLOCK | O_CLOEXEC)) != -1, OT_EXIT_ERROR_ERRNO);
706 }
707
708 return fd;
709 }
710 #endif // OPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE
711
HandleHdlcFrame(void * aContext,otError aError)712 void HdlcInterface::HandleHdlcFrame(void *aContext, otError aError)
713 {
714 static_cast<HdlcInterface *>(aContext)->HandleHdlcFrame(aError);
715 }
716
HandleHdlcFrame(otError aError)717 void HdlcInterface::HandleHdlcFrame(otError aError)
718 {
719 VerifyOrExit((mReceiveFrameCallback != nullptr) && (mReceiveFrameBuffer != nullptr));
720
721 mInterfaceMetrics.mTransferredFrameCount++;
722
723 if (aError == OT_ERROR_NONE)
724 {
725 mInterfaceMetrics.mRxFrameCount++;
726 mInterfaceMetrics.mRxFrameByteCount += mReceiveFrameBuffer->GetLength();
727 mInterfaceMetrics.mTransferredValidFrameCount++;
728 mReceiveFrameCallback(mReceiveFrameContext);
729 }
730 else
731 {
732 mInterfaceMetrics.mTransferredGarbageFrameCount++;
733 mReceiveFrameBuffer->DiscardFrame();
734 LogWarn("Error decoding hdlc frame: %s", otThreadErrorToString(aError));
735 }
736
737 exit:
738 return;
739 }
740
ResetConnection(void)741 otError HdlcInterface::ResetConnection(void)
742 {
743 otError error = OT_ERROR_NONE;
744 uint64_t end;
745
746 if (mRadioUrl.HasParam("uart-reset"))
747 {
748 usleep(static_cast<useconds_t>(kRemoveRcpDelay) * OT_US_PER_MS);
749 CloseFile();
750
751 end = otPlatTimeGet() + kResetTimeout * OT_US_PER_MS;
752 do
753 {
754 mSockFd = OpenFile(mRadioUrl);
755 if (mSockFd != -1)
756 {
757 ExitNow();
758 }
759 usleep(static_cast<useconds_t>(kOpenFileDelay) * OT_US_PER_MS);
760 } while (end > otPlatTimeGet());
761
762 LogCrit("Failed to reopen UART connection after resetting the RCP device.");
763 error = OT_ERROR_FAILED;
764 }
765
766 exit:
767 return error;
768 }
769
770 } // namespace Posix
771 } // namespace ot
772 #endif // OPENTHREAD_POSIX_CONFIG_SPINEL_HDLC_INTERFACE_ENABLE
773