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