• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 / US_PER_S);
287     timeout.tv_usec = static_cast<suseconds_t>(aTimeoutUs % 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 / US_PER_S);
308     timeout.tv_usec = static_cast<suseconds_t>(aTimeoutUs % 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 * 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 / US_PER_S);
436             timeout.tv_usec = static_cast<suseconds_t>(remain % 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 
604         VerifyOrExit((rval = cfsetspeed(&tios, static_cast<speed_t>(speed))) == 0, perror("cfsetspeed"));
605         rval = tcsetattr(fd, TCSANOW, &tios);
606 
607 #ifdef __APPLE__
608         if (rval)
609         {
610             struct termios orig_tios;
611             VerifyOrExit((rval = tcgetattr(fd, &orig_tios)) == 0, perror("tcgetattr"));
612             VerifyOrExit((rval = cfsetispeed(&tios, cfgetispeed(&orig_tios))) == 0, perror("cfsetispeed"));
613             VerifyOrExit((rval = cfsetospeed(&tios, cfgetospeed(&orig_tios))) == 0, perror("cfsetospeed"));
614             VerifyOrExit((rval = tcsetattr(fd, TCSANOW, &tios)) == 0, perror("tcsetattr"));
615             VerifyOrExit((rval = ioctl(fd, IOSSIOSPEED, &speed)) == 0, perror("ioctl IOSSIOSPEED"));
616         }
617 #else  // __APPLE__
618         VerifyOrExit(rval == 0, perror("tcsetattr"));
619 #endif // __APPLE__
620         VerifyOrExit((rval = tcflush(fd, TCIOFLUSH)) == 0);
621     }
622 
623 exit:
624     if (rval != 0)
625     {
626         DieNow(OT_EXIT_FAILURE);
627     }
628 
629     return fd;
630 }
631 
CloseFile(void)632 void HdlcInterface::CloseFile(void)
633 {
634     VerifyOrExit(mSockFd != -1);
635 
636     VerifyOrExit(0 == close(mSockFd), perror("close RCP"));
637     VerifyOrExit(-1 != wait(nullptr) || errno == ECHILD, perror("wait RCP"));
638 
639     mSockFd = -1;
640 
641 exit:
642     return;
643 }
644 
645 #if OPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE
ForkPty(const Url::Url & aRadioUrl)646 int HdlcInterface::ForkPty(const Url::Url &aRadioUrl)
647 {
648     int fd   = -1;
649     int pid  = -1;
650     int rval = -1;
651 
652     {
653         struct termios tios;
654 
655         memset(&tios, 0, sizeof(tios));
656         cfmakeraw(&tios);
657         tios.c_cflag = CS8 | HUPCL | CREAD | CLOCAL;
658 
659         VerifyOrDie((pid = forkpty(&fd, nullptr, &tios, nullptr)) != -1, OT_EXIT_ERROR_ERRNO);
660     }
661 
662     if (0 == pid)
663     {
664         constexpr int kMaxArguments = 32;
665         char         *argv[kMaxArguments + 1];
666         size_t        index = 0;
667 
668         argv[index++] = const_cast<char *>(aRadioUrl.GetPath());
669 
670         for (const char *arg = nullptr;
671              index < OT_ARRAY_LENGTH(argv) && (arg = aRadioUrl.GetValue("forkpty-arg", arg)) != nullptr;
672              argv[index++] = const_cast<char *>(arg))
673         {
674         }
675 
676         if (index < OT_ARRAY_LENGTH(argv))
677         {
678             argv[index] = nullptr;
679         }
680         else
681         {
682             DieNowWithMessage("Too many arguments!", OT_EXIT_INVALID_ARGUMENTS);
683         }
684 
685         VerifyOrDie((rval = execvp(argv[0], argv)) != -1, OT_EXIT_ERROR_ERRNO);
686     }
687     else
688     {
689         VerifyOrDie((rval = fcntl(fd, F_GETFL)) != -1, OT_EXIT_ERROR_ERRNO);
690         VerifyOrDie((rval = fcntl(fd, F_SETFL, rval | O_NONBLOCK | O_CLOEXEC)) != -1, OT_EXIT_ERROR_ERRNO);
691     }
692 
693     return fd;
694 }
695 #endif // OPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE
696 
HandleHdlcFrame(void * aContext,otError aError)697 void HdlcInterface::HandleHdlcFrame(void *aContext, otError aError)
698 {
699     static_cast<HdlcInterface *>(aContext)->HandleHdlcFrame(aError);
700 }
701 
HandleHdlcFrame(otError aError)702 void HdlcInterface::HandleHdlcFrame(otError aError)
703 {
704     VerifyOrExit((mReceiveFrameCallback != nullptr) && (mReceiveFrameBuffer != nullptr));
705 
706     mInterfaceMetrics.mTransferredFrameCount++;
707 
708     if (aError == OT_ERROR_NONE)
709     {
710         mInterfaceMetrics.mRxFrameCount++;
711         mInterfaceMetrics.mRxFrameByteCount += mReceiveFrameBuffer->GetLength();
712         mInterfaceMetrics.mTransferredValidFrameCount++;
713         mReceiveFrameCallback(mReceiveFrameContext);
714     }
715     else
716     {
717         mInterfaceMetrics.mTransferredGarbageFrameCount++;
718         mReceiveFrameBuffer->DiscardFrame();
719         LogWarn("Error decoding hdlc frame: %s", otThreadErrorToString(aError));
720     }
721 
722 exit:
723     return;
724 }
725 
ResetConnection(void)726 otError HdlcInterface::ResetConnection(void)
727 {
728     otError  error = OT_ERROR_NONE;
729     uint64_t end;
730 
731     if (mRadioUrl.HasParam("uart-reset"))
732     {
733         usleep(static_cast<useconds_t>(kRemoveRcpDelay) * US_PER_MS);
734         CloseFile();
735 
736         end = otPlatTimeGet() + kResetTimeout * US_PER_MS;
737         do
738         {
739             mSockFd = OpenFile(mRadioUrl);
740             if (mSockFd != -1)
741             {
742                 ExitNow();
743             }
744             usleep(static_cast<useconds_t>(kOpenFileDelay) * US_PER_MS);
745         } while (end > otPlatTimeGet());
746 
747         LogCrit("Failed to reopen UART connection after resetting the RCP device.");
748         error = OT_ERROR_FAILED;
749     }
750 
751 exit:
752     return error;
753 }
754 
755 } // namespace Posix
756 } // namespace ot
757 #endif // OPENTHREAD_POSIX_CONFIG_SPINEL_HDLC_INTERFACE_ENABLE
758