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/resource.h>
53 #include <sys/stat.h>
54 #include <sys/time.h>
55 #include <sys/wait.h>
56 #include <syslog.h>
57 #include <termios.h>
58 #include <unistd.h>
59 
60 #include <openthread/logging.h>
61 
62 #include "common/code_utils.hpp"
63 #include "lib/spinel/spinel.h"
64 
65 #ifdef __APPLE__
66 
67 #ifndef B230400
68 #define B230400 230400
69 #endif
70 
71 #ifndef B460800
72 #define B460800 460800
73 #endif
74 
75 #ifndef B500000
76 #define B500000 500000
77 #endif
78 
79 #ifndef B576000
80 #define B576000 576000
81 #endif
82 
83 #ifndef B921600
84 #define B921600 921600
85 #endif
86 
87 #ifndef B1000000
88 #define B1000000 1000000
89 #endif
90 
91 #ifndef B1152000
92 #define B1152000 1152000
93 #endif
94 
95 #ifndef B1500000
96 #define B1500000 1500000
97 #endif
98 
99 #ifndef B2000000
100 #define B2000000 2000000
101 #endif
102 
103 #ifndef B2500000
104 #define B2500000 2500000
105 #endif
106 
107 #ifndef B3000000
108 #define B3000000 3000000
109 #endif
110 
111 #ifndef B3500000
112 #define B3500000 3500000
113 #endif
114 
115 #ifndef B4000000
116 #define B4000000 4000000
117 #endif
118 
119 #endif // __APPLE__
120 
121 #if OPENTHREAD_POSIX_CONFIG_RCP_BUS == OT_POSIX_RCP_BUS_UART
122 
123 using ot::Spinel::SpinelInterface;
124 
125 namespace ot {
126 namespace Posix {
127 
HdlcInterface(SpinelInterface::ReceiveFrameCallback aCallback,void * aCallbackContext,SpinelInterface::RxFrameBuffer & aFrameBuffer)128 HdlcInterface::HdlcInterface(SpinelInterface::ReceiveFrameCallback aCallback,
129                              void                                 *aCallbackContext,
130                              SpinelInterface::RxFrameBuffer       &aFrameBuffer)
131     : mReceiveFrameCallback(aCallback)
132     , mReceiveFrameContext(aCallbackContext)
133     , mReceiveFrameBuffer(aFrameBuffer)
134     , mSockFd(-1)
135     , mBaudRate(0)
136     , mHdlcDecoder(aFrameBuffer, HandleHdlcFrame, this)
137     , mRadioUrl(nullptr)
138 {
139     memset(&mInterfaceMetrics, 0, sizeof(mInterfaceMetrics));
140     mInterfaceMetrics.mRcpInterfaceType = OT_POSIX_RCP_BUS_UART;
141 }
142 
Init(const Url::Url & aRadioUrl)143 otError HdlcInterface::Init(const Url::Url &aRadioUrl)
144 {
145     otError     error = OT_ERROR_NONE;
146     struct stat st;
147 
148     VerifyOrExit(mSockFd == -1, error = OT_ERROR_ALREADY);
149 
150     VerifyOrDie(stat(aRadioUrl.GetPath(), &st) == 0, OT_EXIT_INVALID_ARGUMENTS);
151 
152     if (S_ISCHR(st.st_mode))
153     {
154         mSockFd = OpenFile(aRadioUrl);
155         VerifyOrExit(mSockFd != -1, error = OT_ERROR_INVALID_ARGS);
156     }
157 #if OPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE
158     else if (S_ISREG(st.st_mode))
159     {
160         mSockFd = ForkPty(aRadioUrl);
161         VerifyOrExit(mSockFd != -1, error = OT_ERROR_INVALID_ARGS);
162     }
163 #endif // OPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE
164     else
165     {
166         otLogCritPlat("Radio file '%s' not supported", aRadioUrl.GetPath());
167         ExitNow(error = OT_ERROR_INVALID_ARGS);
168     }
169 
170     mRadioUrl = &aRadioUrl;
171 
172 exit:
173     return error;
174 }
175 
~HdlcInterface(void)176 HdlcInterface::~HdlcInterface(void) { Deinit(); }
177 
Deinit(void)178 void HdlcInterface::Deinit(void) { CloseFile(); }
179 
Read(void)180 void HdlcInterface::Read(void)
181 {
182     uint8_t buffer[kMaxFrameSize];
183     ssize_t rval;
184 
185     rval = read(mSockFd, buffer, sizeof(buffer));
186 
187     if (rval > 0)
188     {
189         Decode(buffer, static_cast<uint16_t>(rval));
190     }
191     else if ((rval < 0) && (errno != EAGAIN) && (errno != EINTR))
192     {
193         DieNow(OT_EXIT_ERROR_ERRNO);
194     }
195 }
196 
Decode(const uint8_t * aBuffer,uint16_t aLength)197 void HdlcInterface::Decode(const uint8_t *aBuffer, uint16_t aLength) { mHdlcDecoder.Decode(aBuffer, aLength); }
198 
SendFrame(const uint8_t * aFrame,uint16_t aLength)199 otError HdlcInterface::SendFrame(const uint8_t *aFrame, uint16_t aLength)
200 {
201     otError                          error = OT_ERROR_NONE;
202     Hdlc::FrameBuffer<kMaxFrameSize> encoderBuffer;
203     Hdlc::Encoder                    hdlcEncoder(encoderBuffer);
204 
205     SuccessOrExit(error = hdlcEncoder.BeginFrame());
206     SuccessOrExit(error = hdlcEncoder.Encode(aFrame, aLength));
207     SuccessOrExit(error = hdlcEncoder.EndFrame());
208 
209     error = Write(encoderBuffer.GetFrame(), encoderBuffer.GetLength());
210 
211 exit:
212     if ((error == OT_ERROR_NONE) && ot::Spinel::SpinelInterface::IsSpinelResetCommand(aFrame, aLength))
213     {
214         mHdlcDecoder.Reset();
215         error = ResetConnection();
216     }
217 
218     return error;
219 }
220 
Write(const uint8_t * aFrame,uint16_t aLength)221 otError HdlcInterface::Write(const uint8_t *aFrame, uint16_t aLength)
222 {
223     otError error = OT_ERROR_NONE;
224 #if OPENTHREAD_POSIX_VIRTUAL_TIME
225     virtualTimeSendRadioSpinelWriteEvent(aFrame, aLength);
226 #else
227     while (aLength)
228     {
229         ssize_t rval = write(mSockFd, aFrame, aLength);
230 
231         if (rval == aLength)
232         {
233             break;
234         }
235         else if (rval > 0)
236         {
237             aLength -= static_cast<uint16_t>(rval);
238             aFrame += static_cast<uint16_t>(rval);
239         }
240         else if (rval < 0)
241         {
242             VerifyOrDie((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR), OT_EXIT_ERROR_ERRNO);
243         }
244 
245         SuccessOrExit(error = WaitForWritable());
246     }
247 
248 exit:
249 #endif // OPENTHREAD_POSIX_VIRTUAL_TIME
250 
251     mInterfaceMetrics.mTransferredFrameCount++;
252     if (error == OT_ERROR_NONE)
253     {
254         mInterfaceMetrics.mTxFrameCount++;
255         mInterfaceMetrics.mTxFrameByteCount += aLength;
256         mInterfaceMetrics.mTransferredValidFrameCount++;
257     }
258     else
259     {
260         mInterfaceMetrics.mTransferredGarbageFrameCount++;
261     }
262 
263     return error;
264 }
265 
WaitForFrame(uint64_t aTimeoutUs)266 otError HdlcInterface::WaitForFrame(uint64_t aTimeoutUs)
267 {
268     otError        error = OT_ERROR_NONE;
269     struct timeval timeout;
270 #if OPENTHREAD_POSIX_VIRTUAL_TIME
271     struct VirtualTimeEvent event;
272 
273     timeout.tv_sec  = static_cast<time_t>(aTimeoutUs / US_PER_S);
274     timeout.tv_usec = static_cast<suseconds_t>(aTimeoutUs % US_PER_S);
275 
276     virtualTimeSendSleepEvent(&timeout);
277     virtualTimeReceiveEvent(&event);
278 
279     switch (event.mEvent)
280     {
281     case OT_SIM_EVENT_RADIO_SPINEL_WRITE:
282         Decode(event.mData, event.mDataLength);
283         break;
284 
285     case OT_SIM_EVENT_ALARM_FIRED:
286         VerifyOrExit(event.mDelay <= aTimeoutUs, error = OT_ERROR_RESPONSE_TIMEOUT);
287         break;
288 
289     default:
290         assert(false);
291         break;
292     }
293 #else  // OPENTHREAD_POSIX_VIRTUAL_TIME
294     timeout.tv_sec = static_cast<time_t>(aTimeoutUs / US_PER_S);
295     timeout.tv_usec = static_cast<suseconds_t>(aTimeoutUs % US_PER_S);
296 
297     fd_set read_fds;
298     fd_set error_fds;
299     int rval;
300 
301     FD_ZERO(&read_fds);
302     FD_ZERO(&error_fds);
303     FD_SET(mSockFd, &read_fds);
304     FD_SET(mSockFd, &error_fds);
305 
306     rval = select(mSockFd + 1, &read_fds, nullptr, &error_fds, &timeout);
307 
308     if (rval > 0)
309     {
310         if (FD_ISSET(mSockFd, &read_fds))
311         {
312             Read();
313         }
314         else if (FD_ISSET(mSockFd, &error_fds))
315         {
316             DieNowWithMessage("NCP error", OT_EXIT_FAILURE);
317         }
318         else
319         {
320             DieNow(OT_EXIT_FAILURE);
321         }
322     }
323     else if (rval == 0)
324     {
325         ExitNow(error = OT_ERROR_RESPONSE_TIMEOUT);
326     }
327     else if (errno != EINTR)
328     {
329         DieNowWithMessage("wait response", OT_EXIT_FAILURE);
330     }
331 #endif // OPENTHREAD_POSIX_VIRTUAL_TIME
332 
333 exit:
334     return error;
335 }
336 
UpdateFdSet(fd_set & aReadFdSet,fd_set & aWriteFdSet,int & aMaxFd,struct timeval & aTimeout)337 void HdlcInterface::UpdateFdSet(fd_set &aReadFdSet, fd_set &aWriteFdSet, int &aMaxFd, struct timeval &aTimeout)
338 {
339     OT_UNUSED_VARIABLE(aWriteFdSet);
340     OT_UNUSED_VARIABLE(aTimeout);
341 
342     FD_SET(mSockFd, &aReadFdSet);
343 
344     if (aMaxFd < mSockFd)
345     {
346         aMaxFd = mSockFd;
347     }
348 }
349 
Process(const RadioProcessContext & aContext)350 void HdlcInterface::Process(const RadioProcessContext &aContext)
351 {
352     if (FD_ISSET(mSockFd, aContext.mReadFdSet))
353     {
354         Read();
355     }
356 }
357 
WaitForWritable(void)358 otError HdlcInterface::WaitForWritable(void)
359 {
360     otError        error   = OT_ERROR_NONE;
361     struct timeval timeout = {kMaxWaitTime / 1000, (kMaxWaitTime % 1000) * 1000};
362     uint64_t       now     = otPlatTimeGet();
363     uint64_t       end     = now + kMaxWaitTime * US_PER_MS;
364     fd_set         writeFds;
365     fd_set         errorFds;
366     int            rval;
367 
368     while (true)
369     {
370         FD_ZERO(&writeFds);
371         FD_ZERO(&errorFds);
372         FD_SET(mSockFd, &writeFds);
373         FD_SET(mSockFd, &errorFds);
374 
375         rval = select(mSockFd + 1, nullptr, &writeFds, &errorFds, &timeout);
376 
377         if (rval > 0)
378         {
379             if (FD_ISSET(mSockFd, &writeFds))
380             {
381                 ExitNow();
382             }
383             else if (FD_ISSET(mSockFd, &errorFds))
384             {
385                 DieNow(OT_EXIT_FAILURE);
386             }
387             else
388             {
389                 assert(false);
390             }
391         }
392         else if ((rval < 0) && (errno != EINTR))
393         {
394             DieNow(OT_EXIT_ERROR_ERRNO);
395         }
396 
397         now = otPlatTimeGet();
398 
399         if (end > now)
400         {
401             uint64_t remain = end - now;
402 
403             timeout.tv_sec  = static_cast<time_t>(remain / US_PER_S);
404             timeout.tv_usec = static_cast<suseconds_t>(remain % US_PER_S);
405         }
406         else
407         {
408             break;
409         }
410     }
411 
412     error = OT_ERROR_FAILED;
413 
414 exit:
415     return error;
416 }
417 
OpenFile(const Url::Url & aRadioUrl)418 int HdlcInterface::OpenFile(const Url::Url &aRadioUrl)
419 {
420     int fd   = -1;
421     int rval = 0;
422 
423     fd = open(aRadioUrl.GetPath(), O_RDWR | O_NOCTTY | O_NONBLOCK | O_CLOEXEC);
424     if (fd == -1)
425     {
426         perror("open uart failed");
427         ExitNow();
428     }
429 
430     if (isatty(fd))
431     {
432         struct termios tios;
433         const char    *value;
434         speed_t        speed;
435 
436         int      stopBit  = 1;
437         uint32_t baudrate = 115200;
438 
439         VerifyOrExit((rval = tcgetattr(fd, &tios)) == 0);
440 
441         cfmakeraw(&tios);
442 
443         tios.c_cflag = CS8 | HUPCL | CREAD | CLOCAL;
444 
445         if ((value = aRadioUrl.GetValue("uart-parity")) != nullptr)
446         {
447             if (strncmp(value, "odd", 3) == 0)
448             {
449                 tios.c_cflag |= PARENB;
450                 tios.c_cflag |= PARODD;
451             }
452             else if (strncmp(value, "even", 4) == 0)
453             {
454                 tios.c_cflag |= PARENB;
455             }
456             else
457             {
458                 DieNow(OT_EXIT_INVALID_ARGUMENTS);
459             }
460         }
461 
462         if ((value = aRadioUrl.GetValue("uart-stop")) != nullptr)
463         {
464             stopBit = atoi(value);
465         }
466 
467         switch (stopBit)
468         {
469         case 1:
470             tios.c_cflag &= static_cast<unsigned long>(~CSTOPB);
471             break;
472         case 2:
473             tios.c_cflag |= CSTOPB;
474             break;
475         default:
476             DieNow(OT_EXIT_INVALID_ARGUMENTS);
477             break;
478         }
479 
480         if ((value = aRadioUrl.GetValue("uart-baudrate")))
481         {
482             baudrate = static_cast<uint32_t>(atoi(value));
483         }
484 
485         switch (baudrate)
486         {
487         case 9600:
488             speed = B9600;
489             break;
490         case 19200:
491             speed = B19200;
492             break;
493         case 38400:
494             speed = B38400;
495             break;
496         case 57600:
497             speed = B57600;
498             break;
499         case 115200:
500             speed = B115200;
501             break;
502 #ifdef B230400
503         case 230400:
504             speed = B230400;
505             break;
506 #endif
507 #ifdef B460800
508         case 460800:
509             speed = B460800;
510             break;
511 #endif
512 #ifdef B500000
513         case 500000:
514             speed = B500000;
515             break;
516 #endif
517 #ifdef B576000
518         case 576000:
519             speed = B576000;
520             break;
521 #endif
522 #ifdef B921600
523         case 921600:
524             speed = B921600;
525             break;
526 #endif
527 #ifdef B1000000
528         case 1000000:
529             speed = B1000000;
530             break;
531 #endif
532 #ifdef B1152000
533         case 1152000:
534             speed = B1152000;
535             break;
536 #endif
537 #ifdef B1500000
538         case 1500000:
539             speed = B1500000;
540             break;
541 #endif
542 #ifdef B2000000
543         case 2000000:
544             speed = B2000000;
545             break;
546 #endif
547 #ifdef B2500000
548         case 2500000:
549             speed = B2500000;
550             break;
551 #endif
552 #ifdef B3000000
553         case 3000000:
554             speed = B3000000;
555             break;
556 #endif
557 #ifdef B3500000
558         case 3500000:
559             speed = B3500000;
560             break;
561 #endif
562 #ifdef B4000000
563         case 4000000:
564             speed = B4000000;
565             break;
566 #endif
567         default:
568             DieNow(OT_EXIT_INVALID_ARGUMENTS);
569             break;
570         }
571 
572         mBaudRate = baudrate;
573 
574         if (aRadioUrl.GetValue("uart-flow-control") != nullptr)
575         {
576             tios.c_cflag |= CRTSCTS;
577         }
578 
579         VerifyOrExit((rval = cfsetspeed(&tios, static_cast<speed_t>(speed))) == 0, perror("cfsetspeed"));
580         VerifyOrExit((rval = tcsetattr(fd, TCSANOW, &tios)) == 0, perror("tcsetattr"));
581         VerifyOrExit((rval = tcflush(fd, TCIOFLUSH)) == 0);
582     }
583 
584 exit:
585     if (rval != 0)
586     {
587         DieNow(OT_EXIT_FAILURE);
588     }
589 
590     return fd;
591 }
592 
CloseFile(void)593 void HdlcInterface::CloseFile(void)
594 {
595     VerifyOrExit(mSockFd != -1);
596 
597     VerifyOrExit(0 == close(mSockFd), perror("close RCP"));
598     VerifyOrExit(-1 != wait(nullptr) || errno == ECHILD, perror("wait RCP"));
599 
600     mSockFd = -1;
601 
602 exit:
603     return;
604 }
605 
606 #if OPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE
ForkPty(const Url::Url & aRadioUrl)607 int HdlcInterface::ForkPty(const Url::Url &aRadioUrl)
608 {
609     int fd   = -1;
610     int pid  = -1;
611     int rval = -1;
612 
613     {
614         struct termios tios;
615 
616         memset(&tios, 0, sizeof(tios));
617         cfmakeraw(&tios);
618         tios.c_cflag = CS8 | HUPCL | CREAD | CLOCAL;
619 
620         VerifyOrDie((pid = forkpty(&fd, nullptr, &tios, nullptr)) != -1, OT_EXIT_ERROR_ERRNO);
621     }
622 
623     if (0 == pid)
624     {
625         constexpr int kMaxArguments = 32;
626         char         *argv[kMaxArguments + 1];
627         size_t        index = 0;
628 
629         argv[index++] = const_cast<char *>(aRadioUrl.GetPath());
630 
631         for (const char *arg = nullptr;
632              index < OT_ARRAY_LENGTH(argv) && (arg = aRadioUrl.GetValue("forkpty-arg", arg)) != nullptr;
633              argv[index++] = const_cast<char *>(arg))
634         {
635         }
636 
637         if (index < OT_ARRAY_LENGTH(argv))
638         {
639             argv[index] = nullptr;
640         }
641         else
642         {
643             DieNowWithMessage("Too many arguments!", OT_EXIT_INVALID_ARGUMENTS);
644         }
645 
646         VerifyOrDie((rval = execvp(argv[0], argv)) != -1, OT_EXIT_ERROR_ERRNO);
647     }
648     else
649     {
650         VerifyOrDie((rval = fcntl(fd, F_GETFL)) != -1, OT_EXIT_ERROR_ERRNO);
651         VerifyOrDie((rval = fcntl(fd, F_SETFL, rval | O_NONBLOCK | O_CLOEXEC)) != -1, OT_EXIT_ERROR_ERRNO);
652     }
653 
654     return fd;
655 }
656 #endif // OPENTHREAD_POSIX_CONFIG_RCP_PTY_ENABLE
657 
HandleHdlcFrame(void * aContext,otError aError)658 void HdlcInterface::HandleHdlcFrame(void *aContext, otError aError)
659 {
660     static_cast<HdlcInterface *>(aContext)->HandleHdlcFrame(aError);
661 }
662 
HandleHdlcFrame(otError aError)663 void HdlcInterface::HandleHdlcFrame(otError aError)
664 {
665     mInterfaceMetrics.mTransferredFrameCount++;
666 
667     if (aError == OT_ERROR_NONE)
668     {
669         mInterfaceMetrics.mRxFrameCount++;
670         mInterfaceMetrics.mRxFrameByteCount += mReceiveFrameBuffer.GetLength();
671         mInterfaceMetrics.mTransferredValidFrameCount++;
672         mReceiveFrameCallback(mReceiveFrameContext);
673     }
674     else
675     {
676         mInterfaceMetrics.mTransferredGarbageFrameCount++;
677         mReceiveFrameBuffer.DiscardFrame();
678         otLogWarnPlat("Error decoding hdlc frame: %s", otThreadErrorToString(aError));
679     }
680 }
681 
ResetConnection(void)682 otError HdlcInterface::ResetConnection(void)
683 {
684     otError  error = OT_ERROR_NONE;
685     uint64_t end;
686 
687     if (mRadioUrl->GetValue("uart-reset") != nullptr)
688     {
689         usleep(static_cast<useconds_t>(kRemoveRcpDelay) * US_PER_MS);
690         CloseFile();
691 
692         end = otPlatTimeGet() + kResetTimeout * US_PER_MS;
693         do
694         {
695             mSockFd = OpenFile(*mRadioUrl);
696             if (mSockFd != -1)
697             {
698                 ExitNow();
699             }
700             usleep(static_cast<useconds_t>(kOpenFileDelay) * US_PER_MS);
701         } while (end > otPlatTimeGet());
702 
703         otLogCritPlat("Failed to reopen UART connection after resetting the RCP device.");
704         error = OT_ERROR_FAILED;
705     }
706 
707 exit:
708     return error;
709 }
710 
711 } // namespace Posix
712 } // namespace ot
713 #endif // OPENTHREAD_POSIX_CONFIG_RCP_BUS == OT_POSIX_RCP_BUS_UART
714