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