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