VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/posix/serialport-posix.cpp@ 70633

Last change on this file since 70633 was 70633, checked in by vboxsync, 7 years ago

Runtime/r3/serialport-posix: Fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.9 KB
Line 
1/* $Id: serialport-posix.cpp 70633 2018-01-18 15:21:10Z vboxsync $ */
2/** @file
3 * IPRT - Serial Port API, POSIX Implementation.
4 */
5
6/*
7 * Copyright (C) 2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/serialport.h>
32#include "internal/iprt.h"
33
34#include <iprt/asm.h>
35#include <iprt/assert.h>
36#include <iprt/cdefs.h>
37#include <iprt/err.h>
38#include <iprt/mem.h>
39#include <iprt/string.h>
40#include <iprt/thread.h>
41#include <iprt/time.h>
42#include "internal/magics.h"
43
44#include <errno.h>
45#ifdef RT_OS_SOLARIS
46# include <sys/termios.h>
47#else
48# include <termios.h>
49#endif
50#include <sys/types.h>
51#include <fcntl.h>
52#include <string.h>
53#include <unistd.h>
54#ifdef RT_OS_DARWIN
55# include <sys/poll.h>
56#else
57# include <sys/poll.h>
58#endif
59#include <sys/ioctl.h>
60#include <pthread.h>
61
62#ifdef RT_OS_LINUX
63/*
64 * TIOCM_LOOP is not defined in the above header files for some reason but in asm/termios.h.
65 * But inclusion of this file however leads to compilation errors because of redefinition of some
66 * structs. That's why it is defined here until a better solution is found.
67 */
68# ifndef TIOCM_LOOP
69# define TIOCM_LOOP 0x8000
70# endif
71/* For linux custom baudrate code we also need serial_struct */
72# include <linux/serial.h>
73#endif /* linux */
74
75/** Define fallback if not supported. */
76#if !defined(CMSPAR)
77# define CMSPAR 0
78#endif
79
80
81/*********************************************************************************************************************************
82* Structures and Typedefs *
83*********************************************************************************************************************************/
84
85/**
86 * Internal serial port state.
87 */
88typedef struct RTSERIALPORTINTERNAL
89{
90 /** Magic value (RTSERIALPORT_MAGIC). */
91 uint32_t u32Magic;
92 /** Flags given while opening the serial port. */
93 uint32_t fOpenFlags;
94 /** The file descriptor of the serial port. */
95 int iFd;
96 /** The status line monitor thread if enabled. */
97 RTTHREAD hMonThrd;
98 /** Flag whether the monitoring thread should shutdown. */
99 volatile bool fMonThrdShutdown;
100 /** Reading end of wakeup pipe. */
101 int iFdPipeR;
102 /** Writing end of wakeup pipe. */
103 int iFdPipeW;
104 /** Event pending mask. */
105 volatile uint32_t fEvtsPending;
106 /** The current active config (we assume no one changes this behind our back). */
107 struct termios PortCfg;
108} RTSERIALPORTINTERNAL;
109/** Pointer to the internal serial port state. */
110typedef RTSERIALPORTINTERNAL *PRTSERIALPORTINTERNAL;
111
112
113/**
114 * Baud rate conversion table descriptor.
115 */
116typedef struct RTSERIALPORTBRATECONVDESC
117{
118 /** The platform independent baud rate used by the RTSerialPort* API. */
119 uint32_t uBaudRateCfg;
120 /** The speed identifier used in the termios structure. */
121 speed_t iSpeedTermios;
122} RTSERIALPORTBRATECONVDESC;
123/** Pointer to a baud rate converions table descriptor. */
124typedef RTSERIALPORTBRATECONVDESC *PRTSERIALPORTBRATECONVDESC;
125/** Pointer to a const baud rate conversion table descriptor. */
126typedef const RTSERIALPORTBRATECONVDESC *PCRTSERIALPORTBRATECONVDESC;
127
128
129/*********************************************************************************************************************************
130* Defined Constants And Macros *
131*********************************************************************************************************************************/
132
133/** The event poller was woken up due to an external interrupt. */
134#define RTSERIALPORT_WAKEUP_PIPE_REASON_INTERRUPT 0x0
135/** The event poller was woken up due to a change in the monitored status lines. */
136#define RTSERIALPORT_WAKEUP_PIPE_REASON_STS_LINE_CHANGED 0x1
137/** The monitor thread encoutnered repeating errors querying the status lines and terminated. */
138#define RTSERIALPORT_WAKEUP_PIPE_REASON_STS_LINE_MONITOR_FAILED 0x2
139
140
141/*********************************************************************************************************************************
142* Global variables *
143*********************************************************************************************************************************/
144
145/** The baud rate conversion table. */
146static const RTSERIALPORTBRATECONVDESC s_rtSerialPortBaudrateConv[] =
147{
148 { 50, B50 },
149 { 75, B75 },
150 { 110, B110 },
151 { 134, B134 },
152 { 150, B150 },
153 { 200, B200 },
154 { 300, B300 },
155 { 600, B600 },
156 { 1200, B1200 },
157 { 1800, B1800 },
158 { 2400, B2400 },
159 { 4800, B4800 },
160 { 9600, B9600 },
161 { 19200, B19200 },
162 { 38400, B38400 },
163 { 57600, B57600 },
164 { 115200, B115200 }
165};
166
167
168
169/*********************************************************************************************************************************
170* Internal Functions *
171*********************************************************************************************************************************/
172
173/**
174 * Converts the given termios speed identifier to the baud rate used in the API.
175 *
176 * @returns Baud rate or 0 if not a standard baud rate
177 */
178DECLINLINE(uint32_t) rtSerialPortGetBaudrateFromTermiosSpeed(speed_t enmSpeed)
179{
180 for (unsigned i = 0; i < RT_ELEMENTS(s_rtSerialPortBaudrateConv); i++)
181 {
182 if (s_rtSerialPortBaudrateConv[i].iSpeedTermios == enmSpeed)
183 return s_rtSerialPortBaudrateConv[i].uBaudRateCfg;
184 }
185
186 return 0;
187}
188
189
190/**
191 * Converts the given baud rate to proper termios speed identifier.
192 *
193 * @returns Speed identifier if available or B0 if no matching speed for the baud rate
194 * could be found.
195 * @param uBaudRate The baud rate to convert.
196 */
197DECLINLINE(speed_t) rtSerialPortGetTermiosSpeedFromBaudrate(uint32_t uBaudRate)
198{
199 for (unsigned i = 0; i < RT_ELEMENTS(s_rtSerialPortBaudrateConv); i++)
200 {
201 if (s_rtSerialPortBaudrateConv[i].uBaudRateCfg == uBaudRate)
202 return s_rtSerialPortBaudrateConv[i].iSpeedTermios;
203 }
204
205 return B0;
206}
207
208
209/**
210 * Tries to set the default config on the given serial port.
211 *
212 * @returns IPRT status code.
213 * @param pThis The internal serial port instance data.
214 */
215static int rtSerialPortSetDefaultCfg(PRTSERIALPORTINTERNAL pThis)
216{
217 pThis->PortCfg.c_iflag = INPCK; /* Input parity checking. */
218 cfsetispeed(&pThis->PortCfg, B9600);
219 cfsetospeed(&pThis->PortCfg, B9600);
220 pThis->PortCfg.c_cflag |= CS8 | CLOCAL; /* 8 data bits, ignore modem control lines. */
221 if (pThis->fOpenFlags & RTSERIALPORT_OPEN_F_READ)
222 pThis->PortCfg.c_cflag |= CREAD; /* Enable receiver. */
223
224 /* Set to raw input mode. */
225 pThis->PortCfg.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ECHOK | ISIG | IEXTEN);
226 pThis->PortCfg.c_cc[VMIN] = 0; /* Achieve non-blocking behavior. */
227 pThis->PortCfg.c_cc[VTIME] = 0;
228
229 int rc = VINF_SUCCESS;
230 int rcPsx = tcflush(pThis->iFd, TCIOFLUSH);
231 if (!rcPsx)
232 {
233 rcPsx = tcsetattr(pThis->iFd, TCSANOW, &pThis->PortCfg);
234 if (rcPsx == -1)
235 rc = RTErrConvertFromErrno(errno);
236
237 if (RT_SUCCESS(rc))
238 {
239#ifdef RT_OS_LINUX
240 if (pThis->fOpenFlags & RTSERIALPORT_OPEN_F_ENABLE_LOOPBACK)
241 {
242 int fTiocmSet = TIOCM_LOOP;
243 rcPsx = ioctl(pThis->iFd, TIOCMBIS, &fTiocmSet);
244 if (rcPsx == -1)
245 rc = RTErrConvertFromErrno(errno);
246 }
247 else
248 {
249 /* Make sure it is clear. */
250 int fTiocmClear = TIOCM_LOOP;
251 rcPsx = ioctl(pThis->iFd, TIOCMBIC, &fTiocmClear);
252 if (rcPsx == -1)
253 rc = RTErrConvertFromErrno(errno);
254 }
255#else
256 if (pThis->fOpenFlags & RTSERIALPORT_OPEN_F_ENABLE_LOOPBACK)
257 return VERR_NOT_SUPPORTED;
258#endif
259 }
260 }
261 else
262 rc = RTErrConvertFromErrno(errno);
263
264 return rc;
265}
266
267
268/**
269 * Converts the given serial port config to the appropriate termios counterpart.
270 *
271 * @returns IPRT status code.
272 * @param pThis The internal serial port instance data.
273 * @param pCfg Pointer to the serial port config descriptor.
274 * @param pTermios Pointer to the termios structure to fill.
275 * @param pErrInfo Additional error to be set when the conversion fails.
276 */
277static int rtSerialPortCfg2Termios(PRTSERIALPORTINTERNAL pThis, PCRTSERIALPORTCFG pCfg, struct termios *pTermios, PRTERRINFO pErrInfo)
278{
279 RT_NOREF(pErrInfo); /** @todo Make use of the error info. */
280 speed_t enmSpeed = rtSerialPortGetTermiosSpeedFromBaudrate(pCfg->uBaudRate);
281 if (enmSpeed != B0)
282 {
283 tcflag_t const fCFlagMask = (CS5 | CS6 | CS7 | CS8 | CSTOPB | PARENB | PARODD | CMSPAR);
284 tcflag_t fCFlagNew = 0;
285
286 switch (pCfg->enmDataBitCount)
287 {
288 case RTSERIALPORTDATABITS_5BITS:
289 fCFlagNew |= CS5;
290 break;
291 case RTSERIALPORTDATABITS_6BITS:
292 fCFlagNew |= CS6;
293 break;
294 case RTSERIALPORTDATABITS_7BITS:
295 fCFlagNew |= CS7;
296 break;
297 case RTSERIALPORTDATABITS_8BITS:
298 fCFlagNew |= CS8;
299 break;
300 default:
301 AssertFailed();
302 return VERR_INVALID_PARAMETER;
303 }
304
305 switch (pCfg->enmParity)
306 {
307 case RTSERIALPORTPARITY_NONE:
308 break;
309 case RTSERIALPORTPARITY_EVEN:
310 fCFlagNew |= PARENB;
311 break;
312 case RTSERIALPORTPARITY_ODD:
313 fCFlagNew |= PARENB | PARODD;
314 break;
315#if CMSPAR != 0
316 case RTSERIALPORTPARITY_MARK:
317 fCFlagNew |= PARENB | CMSPAR | PARODD;
318 break;
319 case RTSERIALPORTPARITY_SPACE:
320 fCFlagNew |= PARENB | CMSPAR;
321 break;
322#else
323 case RTSERIALPORTPARITY_MARK:
324 case RTSERIALPORTPARITY_SPACE:
325 return VERR_NOT_SUPPORTED;
326#endif
327 default:
328 AssertFailed();
329 return VERR_INVALID_PARAMETER;
330 }
331
332 switch (pCfg->enmStopBitCount)
333 {
334 case RTSERIALPORTSTOPBITS_ONE:
335 break;
336 case RTSERIALPORTSTOPBITS_ONEPOINTFIVE:
337 if (pCfg->enmDataBitCount == RTSERIALPORTDATABITS_5BITS)
338 fCFlagNew |= CSTOPB;
339 else
340 return VERR_NOT_SUPPORTED;
341 break;
342 case RTSERIALPORTSTOPBITS_TWO:
343 if (pCfg->enmDataBitCount != RTSERIALPORTDATABITS_5BITS)
344 fCFlagNew |= CSTOPB;
345 else
346 return VERR_NOT_SUPPORTED;
347 break;
348 default:
349 AssertFailed();
350 return VERR_INVALID_PARAMETER;
351 }
352
353 /* Assign new flags. */
354 if (pThis->fOpenFlags & RTSERIALPORT_OPEN_F_READ)
355 pTermios->c_cflag |= CREAD; /* Enable receiver. */
356 pTermios->c_cflag = (pTermios->c_cflag & ~fCFlagMask) | fCFlagNew;
357 pTermios->c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ECHOK | ISIG | IEXTEN);
358 pTermios->c_iflag = INPCK; /* Input parity checking. */
359 pTermios->c_cc[VMIN] = 0; /* Achieve non-blocking behavior. */
360 pTermios->c_cc[VTIME] = 0;
361 cfsetispeed(pTermios, enmSpeed);
362 cfsetospeed(pTermios, enmSpeed);
363 }
364 else
365 return VERR_SERIALPORT_INVALID_BAUDRATE;
366
367#ifdef RT_OS_LINUX
368 /** @todo Handle custom baudrates supported by Linux. */
369#endif
370
371 return VINF_SUCCESS;
372}
373
374
375/**
376 * Converts the given termios structure to an appropriate serial port config.
377 *
378 * @returns IPRT status code.
379 * @param pTermios The termios structure to convert.
380 * @param pCfg The serial port config to fill in.
381 */
382static int rtSerialPortTermios2Cfg(struct termios *pTermios, PRTSERIALPORTCFG pCfg)
383{
384 int rc = VINF_SUCCESS;
385 bool f5DataBits = false;
386 speed_t enmSpeedIn = cfgetispeed(pTermios);
387 Assert(enmSpeedIn == cfgetospeed(pTermios)); /* Should always be the same. */
388
389 pCfg->uBaudRate = rtSerialPortGetBaudrateFromTermiosSpeed(enmSpeedIn);
390 if (!pCfg->uBaudRate)
391 rc = VERR_SERIALPORT_INVALID_BAUDRATE;
392
393 switch (pTermios->c_cflag & CSIZE)
394 {
395 case CS5:
396 pCfg->enmDataBitCount = RTSERIALPORTDATABITS_5BITS;
397 f5DataBits = true;
398 break;
399 case CS6:
400 pCfg->enmDataBitCount = RTSERIALPORTDATABITS_6BITS;
401 break;
402 case CS7:
403 pCfg->enmDataBitCount = RTSERIALPORTDATABITS_7BITS;
404 break;
405 case CS8:
406 pCfg->enmDataBitCount = RTSERIALPORTDATABITS_8BITS;
407 break;
408 default:
409 AssertFailed(); /* Should not happen. */
410 pCfg->enmDataBitCount = RTSERIALPORTDATABITS_INVALID;
411 rc = RT_FAILURE(rc) ? rc : VERR_INVALID_PARAMETER;
412 }
413
414 /* Convert parity. */
415 if (pTermios->c_cflag & PARENB)
416 {
417 /*
418 * CMSPAR is not supported on all systems, especially OS X. As configuring
419 * mark/space parity there is not supported and we start from a known config
420 * when opening the serial port it is not required to check for this here.
421 */
422#if CMSPAR == 0
423 bool fCmsParSet = RT_BOOL(pTermios->c_cflag & CMSPAR);
424#else
425 bool fCmsParSet = false;
426#endif
427 if (pTermios->c_cflag & PARODD)
428 pCfg->enmParity = fCmsParSet ? RTSERIALPORTPARITY_MARK : RTSERIALPORTPARITY_ODD;
429 else
430 pCfg->enmParity = fCmsParSet ? RTSERIALPORTPARITY_SPACE: RTSERIALPORTPARITY_EVEN;
431 }
432 else
433 pCfg->enmParity = RTSERIALPORTPARITY_NONE;
434
435 /*
436 * 1.5 stop bits are used with a data count of 5 bits when a UART derived from the 8250
437 * is used.
438 */
439 if (pTermios->c_cflag & CSTOPB)
440 pCfg->enmStopBitCount = f5DataBits ? RTSERIALPORTSTOPBITS_ONEPOINTFIVE : RTSERIALPORTSTOPBITS_TWO;
441 else
442 pCfg->enmStopBitCount = RTSERIALPORTSTOPBITS_ONE;
443
444 return rc;
445}
446
447
448/**
449 * Wakes up any thread polling for a serial port event with the given reason.
450 *
451 * @returns IPRT status code.
452 * @param pThis The internal serial port instance data.
453 * @param bWakeupReason The wakeup reason to pass to the event poller.
454 */
455DECLINLINE(int) rtSerialPortWakeupEvtPoller(PRTSERIALPORTINTERNAL pThis, uint8_t bWakeupReason)
456{
457 int rcPsx = write(pThis->iFdPipeW, &bWakeupReason, 1);
458 if (rcPsx != 1)
459 return RTErrConvertFromErrno(errno);
460
461 return VINF_SUCCESS;
462}
463
464
465/**
466 * The status line monitor thread worker.
467 *
468 * @returns IPRT status code.
469 * @param ThreadSelf Thread handle to this thread.
470 * @param pvUser User argument.
471 */
472static DECLCALLBACK(int) rtSerialPortStsLineMonitorThrd(RTTHREAD hThreadSelf, void *pvUser)
473{
474 RT_NOREF(hThreadSelf);
475 PRTSERIALPORTINTERNAL pThis = (PRTSERIALPORTINTERNAL)pvUser;
476 unsigned long const fStsLinesChk = TIOCM_CAR | TIOCM_RNG | TIOCM_DSR | TIOCM_CTS;
477 int rc = VINF_SUCCESS;
478 uint32_t fStsLinesOld = 0;
479 uint32_t cStsLineGetErrors = 0;
480#ifdef RT_OS_LINUX
481 bool fPoll = false;
482#endif
483
484 RTThreadUserSignal(hThreadSelf);
485
486 int rcPsx = ioctl(pThis->iFd, TIOCMGET, &fStsLinesOld);
487 if (rcPsx == -1)
488 {
489 ASMAtomicXchgBool(&pThis->fMonThrdShutdown, true);
490 return RTErrConvertFromErrno(errno);
491 }
492
493 while ( !pThis->fMonThrdShutdown
494 && RT_SUCCESS(rc))
495 {
496# ifdef RT_OS_LINUX
497 /*
498 * Wait for status line change.
499 *
500 * XXX In Linux, if a thread calls tcsetattr while the monitor thread is
501 * waiting in ioctl for a modem status change then 8250.c wrongly disables
502 * modem irqs and so the monitor thread never gets released. The workaround
503 * is to send a signal after each tcsetattr.
504 *
505 * TIOCMIWAIT doesn't work for the DSR line with TIOCM_DSR set
506 * (see http://lxr.linux.no/#linux+v4.7/drivers/usb/class/cdc-acm.c#L949)
507 * However as it is possible to query the line state we will not just clear
508 * the TIOCM_DSR bit from the lines to check but resort to the polling
509 * approach just like on other hosts.
510 */
511 if (!fPoll)
512 {
513 rcPsx = ioctl(pThis->iFd, TIOCMIWAIT, fStsLinesChk);
514 if (!rcPsx)
515 {
516 rc = rtSerialPortWakeupEvtPoller(pThis, RTSERIALPORT_WAKEUP_PIPE_REASON_STS_LINE_CHANGED);
517 if (RT_FAILURE(rc))
518 break;
519 }
520 else if (rcPsx == -1 && errno != EINTR)
521 fPoll = true;
522 }
523 else
524#endif
525 {
526 uint32_t fStsLines = 0;
527 rcPsx = ioctl(pThis->iFd, TIOCMGET, &fStsLines);
528 if (!rcPsx)
529 {
530 cStsLineGetErrors = 0; /* Reset the error counter once we had one successful query. */
531
532 if (((fStsLines ^ fStsLinesOld) & fStsLinesChk))
533 {
534 rc = rtSerialPortWakeupEvtPoller(pThis, RTSERIALPORT_WAKEUP_PIPE_REASON_STS_LINE_CHANGED);
535 if (RT_FAILURE(rc))
536 break;
537
538 fStsLinesOld = fStsLines;
539 }
540 else /* No change, sleep for a bit. */
541 RTThreadSleep(100 /*ms*/);
542 }
543 else if (rcPsx == -1 && errno != EINTR)
544 {
545 /*
546 * If querying the status line fails too often we have to shut down the
547 * thread and notify the user of the serial port.
548 */
549 if (cStsLineGetErrors++ >= 10)
550 {
551 rc = RTErrConvertFromErrno(errno);
552 rtSerialPortWakeupEvtPoller(pThis, RTSERIALPORT_WAKEUP_PIPE_REASON_STS_LINE_MONITOR_FAILED);
553 break;
554 }
555
556 RTThreadSleep(100 /*ms*/);
557 }
558 }
559 }
560
561 ASMAtomicXchgBool(&pThis->fMonThrdShutdown, true);
562 return rc;
563}
564
565
566/**
567 * Creates the status line monitoring thread.
568 *
569 * @returns IPRT status code.
570 * @param pThis The internal serial port instance data.
571 */
572static int rtSerialPortMonitorThreadCreate(PRTSERIALPORTINTERNAL pThis)
573{
574 int rc = VINF_SUCCESS;
575
576 /*
577 * Check whether querying the status lines is supported at all, pseudo terminals
578 * don't support it so an error returned in that case.
579 */
580 uint32_t fStsLines = 0;
581 int rcPsx = ioctl(pThis->iFd, TIOCMGET, &fStsLines);
582 if (!rcPsx)
583 {
584 pThis->fMonThrdShutdown = false;
585 rc = RTThreadCreate(&pThis->hMonThrd, rtSerialPortStsLineMonitorThrd, pThis, 0 /*cbStack*/,
586 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "IPRT-SerPortMon");
587 if (RT_SUCCESS(rc))
588 {
589 /* Wait for the thread to start up. */
590 rc = RTThreadUserWait(pThis->hMonThrd, 20*RT_MS_1SEC);
591 if ( rc == VERR_TIMEOUT
592 || pThis->fMonThrdShutdown)
593 {
594 /* Startup failed, try to reap the thread. */
595 int rcThrd;
596 rc = RTThreadWait(pThis->hMonThrd, 20*RT_MS_1SEC, &rcThrd);
597 if (RT_SUCCESS(rc))
598 rc = rcThrd;
599 else
600 rc = VERR_INTERNAL_ERROR;
601 /* The thread is lost otherwise. */
602 }
603 }
604 }
605 else if (errno == ENOTTY)
606 rc = VERR_NOT_SUPPORTED;
607 else
608 rc = RTErrConvertFromErrno(errno);
609
610 return rc;
611}
612
613
614/**
615 * Shuts down the status line monitor thread.
616 *
617 * @returns nothing.
618 * @param pThis The internal serial port instance data.
619 */
620static void rtSerialPortMonitorThreadShutdown(PRTSERIALPORTINTERNAL pThis)
621{
622 bool fShutDown = ASMAtomicXchgBool(&pThis->fMonThrdShutdown, true);
623 if (!fShutDown)
624 {
625 int rc = RTThreadPoke(pThis->hMonThrd);
626 AssertRC(rc);
627 }
628
629 int rcThrd = VINF_SUCCESS;
630 int rc = RTThreadWait(pThis->hMonThrd, 20*RT_MS_1SEC, &rcThrd);
631 AssertRC(rc);
632 AssertRC(rcThrd);
633}
634
635
636RTDECL(int) RTSerialPortOpen(PRTSERIALPORT phSerialPort, const char *pszPortAddress, uint32_t fFlags)
637{
638 AssertPtrReturn(phSerialPort, VERR_INVALID_POINTER);
639 AssertReturn(VALID_PTR(pszPortAddress) && *pszPortAddress != '\0', VERR_INVALID_PARAMETER);
640 AssertReturn(!(fFlags & ~RTSERIALPORT_OPEN_F_VALID_MASK), VERR_INVALID_PARAMETER);
641 AssertReturn((fFlags & RTSERIALPORT_OPEN_F_READ) || (fFlags & RTSERIALPORT_OPEN_F_WRITE),
642 VERR_INVALID_PARAMETER);
643
644 int rc = VINF_SUCCESS;
645 PRTSERIALPORTINTERNAL pThis = (PRTSERIALPORTINTERNAL)RTMemAllocZ(sizeof(*pThis));
646 if (pThis)
647 {
648 int fPsxFlags = O_NOCTTY | O_NONBLOCK;
649
650 if ((fFlags & RTSERIALPORT_OPEN_F_READ) && !(fFlags & RTSERIALPORT_OPEN_F_WRITE))
651 fPsxFlags |= O_RDONLY;
652 else if (!(fFlags & RTSERIALPORT_OPEN_F_READ) && (fFlags & RTSERIALPORT_OPEN_F_WRITE))
653 fPsxFlags |= O_WRONLY;
654 else
655 fPsxFlags |= O_RDWR;
656
657 pThis->u32Magic = RTSERIALPORT_MAGIC;
658 pThis->fOpenFlags = fFlags;
659 pThis->fEvtsPending = 0;
660 pThis->iFd = open(pszPortAddress, fPsxFlags);
661 if (pThis->iFd != -1)
662 {
663 /* Create wakeup pipe for the event API. */
664 int aPipeFds[2];
665 int rcPsx = pipe(&aPipeFds[0]);
666 if (!rcPsx)
667 {
668 /* Make the pipes close on exec. */
669 pThis->iFdPipeR = aPipeFds[0];
670 pThis->iFdPipeW = aPipeFds[1];
671
672 if (fcntl(pThis->iFdPipeR, F_SETFD, FD_CLOEXEC))
673 rc = RTErrConvertFromErrno(errno);
674
675 if ( RT_SUCCESS(rc)
676 && fcntl(pThis->iFdPipeW, F_SETFD, FD_CLOEXEC))
677 rc = RTErrConvertFromErrno(errno);
678
679 if (RT_SUCCESS(rc))
680 {
681 rc = rtSerialPortSetDefaultCfg(pThis);
682 if ( RT_SUCCESS(rc)
683 && (fFlags & RTSERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING))
684 rc = rtSerialPortMonitorThreadCreate(pThis);
685
686 if (RT_SUCCESS(rc))
687 {
688 *phSerialPort = pThis;
689 return VINF_SUCCESS;
690 }
691 }
692
693 close(pThis->iFdPipeR);
694 close(pThis->iFdPipeW);
695 }
696 else
697 rc = RTErrConvertFromErrno(errno);
698
699 close(pThis->iFd);
700 }
701 else
702 rc = RTErrConvertFromErrno(errno);
703
704 RTMemFree(pThis);
705 }
706 else
707 rc = VERR_NO_MEMORY;
708
709 return rc;
710}
711
712
713RTDECL(int) RTSerialPortClose(RTSERIALPORT hSerialPort)
714{
715 PRTSERIALPORTINTERNAL pThis = hSerialPort;
716 if (pThis == NIL_RTSERIALPORT)
717 return VINF_SUCCESS;
718 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
719 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
720
721 /*
722 * Do the cleanup.
723 */
724 AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSERIALPORT_MAGIC_DEAD, RTSERIALPORT_MAGIC), VERR_INVALID_HANDLE);
725
726 if (pThis->fOpenFlags & RTSERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING)
727 rtSerialPortMonitorThreadShutdown(pThis);
728
729 close(pThis->iFd);
730 close(pThis->iFdPipeR);
731 close(pThis->iFdPipeW);
732 RTMemFree(pThis);
733 return VINF_SUCCESS;
734}
735
736
737RTDECL(RTHCINTPTR) RTSerialPortToNative(RTSERIALPORT hSerialPort)
738{
739 PRTSERIALPORTINTERNAL pThis = hSerialPort;
740 AssertPtrReturn(pThis, -1);
741 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, -1);
742
743 return pThis->iFd;
744}
745
746
747RTDECL(int) RTSerialPortRead(RTSERIALPORT hSerialPort, void *pvBuf, size_t cbToRead, size_t *pcbRead)
748{
749 RT_NOREF(hSerialPort, pvBuf, cbToRead, pcbRead);
750 return VERR_NOT_IMPLEMENTED;
751}
752
753
754RTDECL(int) RTSerialPortReadNB(RTSERIALPORT hSerialPort, void *pvBuf, size_t cbToRead, size_t *pcbRead)
755{
756 PRTSERIALPORTINTERNAL pThis = hSerialPort;
757 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
758 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
759 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
760 AssertReturn(cbToRead > 0, VERR_INVALID_PARAMETER);
761 AssertPtrReturn(pcbRead, VERR_INVALID_POINTER);
762
763 *pcbRead = 0;
764
765 int rc = VINF_SUCCESS;
766 ssize_t cbThisRead = read(pThis->iFd, pvBuf, cbToRead);
767 if (cbThisRead > 0)
768 {
769 /*
770 * The read data needs to be scanned for the BREAK condition marker encoded in the data stream,
771 * if break detection was enabled during open.
772 */
773 if (pThis->fOpenFlags & RTSERIALPORT_OPEN_F_DETECT_BREAK_CONDITION)
774 { /** @todo */ }
775
776 *pcbRead = cbThisRead;
777 }
778 else if (cbThisRead == 0 || errno == EAGAIN || errno == EWOULDBLOCK)
779 rc = VINF_TRY_AGAIN;
780 else
781 rc = RTErrConvertFromErrno(errno);
782
783 return rc;
784}
785
786
787RTDECL(int) RTSerialPortWrite(RTSERIALPORT hSerialPort, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
788{
789 RT_NOREF(hSerialPort, pvBuf, cbToWrite, pcbWritten);
790 return VERR_NOT_IMPLEMENTED;
791}
792
793
794RTDECL(int) RTSerialPortWriteNB(RTSERIALPORT hSerialPort, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
795{
796 PRTSERIALPORTINTERNAL pThis = hSerialPort;
797 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
798 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
799 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
800 AssertReturn(cbToWrite > 0, VERR_INVALID_PARAMETER);
801 AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
802
803 *pcbWritten = 0;
804
805 int rc = VINF_SUCCESS;
806 ssize_t cbThisWrite = write(pThis->iFd, pvBuf, cbToWrite);
807 if (cbThisWrite > 0)
808 *pcbWritten = cbThisWrite;
809 else if (cbThisWrite == 0 || errno == EAGAIN || errno == EWOULDBLOCK)
810 rc = VINF_TRY_AGAIN;
811 else
812 rc = RTErrConvertFromErrno(errno);
813
814 return rc;
815}
816
817
818RTDECL(int) RTSerialPortCfgQueryCurrent(RTSERIALPORT hSerialPort, PRTSERIALPORTCFG pCfg)
819{
820 PRTSERIALPORTINTERNAL pThis = hSerialPort;
821 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
822 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
823
824 return rtSerialPortTermios2Cfg(&pThis->PortCfg, pCfg);
825}
826
827
828RTDECL(int) RTSerialPortCfgSet(RTSERIALPORT hSerialPort, PCRTSERIALPORTCFG pCfg, PRTERRINFO pErrInfo)
829{
830 PRTSERIALPORTINTERNAL pThis = hSerialPort;
831 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
832 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
833
834 struct termios PortCfgNew; RT_ZERO(PortCfgNew);
835 int rc = rtSerialPortCfg2Termios(pThis, pCfg, &PortCfgNew, pErrInfo);
836 if (RT_SUCCESS(rc))
837 {
838 int rcPsx = tcflush(pThis->iFd, TCIOFLUSH);
839 if (!rcPsx)
840 {
841 rcPsx = tcsetattr(pThis->iFd, TCSANOW, &PortCfgNew);
842 if (rcPsx == -1)
843 rc = RTErrConvertFromErrno(errno);
844 else
845 memcpy(&pThis->PortCfg, &PortCfgNew, sizeof(struct termios));
846
847#ifdef RT_OS_LINUX
848 /*
849 * XXX In Linux, if a thread calls tcsetattr while the monitor thread is
850 * waiting in ioctl for a modem status change then 8250.c wrongly disables
851 * modem irqs and so the monitor thread never gets released. The workaround
852 * is to send a signal after each tcsetattr.
853 */
854 if (pThis->fOpenFlags & RTSERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING)
855 RTThreadPoke(pThis->hMonThrd);
856#endif
857 }
858 else
859 rc = RTErrConvertFromErrno(errno);
860 }
861
862 return rc;
863}
864
865
866RTDECL(int) RTSerialPortEvtPoll(RTSERIALPORT hSerialPort, uint32_t fEvtMask, uint32_t *pfEvtsRecv,
867 RTMSINTERVAL msTimeout)
868{
869 PRTSERIALPORTINTERNAL pThis = hSerialPort;
870 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
871 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
872 AssertReturn(!(fEvtMask & ~RTSERIALPORT_EVT_F_VALID_MASK), VERR_INVALID_PARAMETER);
873 AssertPtrReturn(pfEvtsRecv, VERR_INVALID_POINTER);
874
875 *pfEvtsRecv = 0;
876
877 fEvtMask |= RTSERIALPORT_EVT_F_STATUS_LINE_MONITOR_FAILED; /* This will be reported always, no matter what the caller wants. */
878
879 /* Return early if there are events pending from previous calls which weren't fetched yet. */
880 for (;;)
881 {
882 uint32_t fEvtsPending = ASMAtomicReadU32(&pThis->fEvtsPending);
883 if (fEvtsPending & fEvtMask)
884 {
885 *pfEvtsRecv = fEvtsPending & fEvtMask;
886 /* Write back, repeat the whole procedure if someone else raced us. */
887 if (ASMAtomicCmpXchgU32(&pThis->fEvtsPending, fEvtsPending & ~fEvtMask, fEvtsPending))
888 return VINF_SUCCESS;
889 }
890 else
891 break;
892 }
893
894 struct pollfd aPollFds[2]; RT_ZERO(aPollFds);
895 aPollFds[0].fd = pThis->iFd;
896 aPollFds[0].events = POLLERR | POLLHUP;
897 aPollFds[0].revents = 0;
898 if ( (pThis->fOpenFlags & RTSERIALPORT_OPEN_F_READ)
899 && (fEvtMask & RTSERIALPORT_EVT_F_DATA_RX))
900 aPollFds[0].events |= POLLIN;
901 if ( (pThis->fOpenFlags & RTSERIALPORT_OPEN_F_WRITE)
902 && (fEvtMask & RTSERIALPORT_EVT_F_DATA_TX))
903 aPollFds[0].events |= POLLOUT;
904
905 aPollFds[1].fd = pThis->iFdPipeR;
906 aPollFds[1].events = POLLIN | POLLERR | POLLHUP;
907 aPollFds[1].revents = 0;
908
909 int rcPsx = 0;
910 int msTimeoutLeft = msTimeout == RT_INDEFINITE_WAIT ? -1 : msTimeout;
911 while (msTimeoutLeft != 0)
912 {
913 uint64_t tsPollStart = RTTimeMilliTS();
914
915 rcPsx = poll(&aPollFds[0], RT_ELEMENTS(aPollFds), msTimeoutLeft);
916 if (rcPsx != -1 || errno != EINTR)
917 break;
918 /* Restart when getting interrupted. */
919 if (msTimeoutLeft > -1)
920 {
921 uint64_t tsPollEnd = RTTimeMilliTS();
922 uint64_t tsPollSpan = tsPollEnd - tsPollStart;
923 msTimeoutLeft -= RT_MIN(tsPollSpan, (uint32_t)msTimeoutLeft);
924 }
925 }
926
927 int rc = VINF_SUCCESS;
928 uint32_t fEvtsPending = 0;
929 if (rcPsx < 0 && errno != EINTR)
930 rc = RTErrConvertFromErrno(errno);
931 else if (rcPsx > 0)
932 {
933 if (aPollFds[0].revents != 0)
934 {
935 fEvtsPending |= (aPollFds[0].revents & POLLIN) ? RTSERIALPORT_EVT_F_DATA_RX : 0;
936 fEvtsPending |= (aPollFds[0].revents & POLLIN) ? RTSERIALPORT_EVT_F_DATA_TX : 0;
937 /** @todo BREAK condition detection. */
938 }
939
940 if (aPollFds[1].revents != 0)
941 {
942 AssertReturn(!(aPollFds[1].revents & (POLLHUP | POLLERR | POLLNVAL)), VERR_INTERNAL_ERROR);
943 Assert(aPollFds[1].revents & POLLIN);
944
945 uint8_t bWakeupReason = 0;
946 ssize_t cbRead = read(pThis->iFdPipeR, &bWakeupReason, 1);
947 if (cbRead == 1)
948 {
949 switch (bWakeupReason)
950 {
951 case RTSERIALPORT_WAKEUP_PIPE_REASON_INTERRUPT:
952 rc = VERR_INTERRUPTED;
953 break;
954 case RTSERIALPORT_WAKEUP_PIPE_REASON_STS_LINE_CHANGED:
955 fEvtsPending |= RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED;
956 break;
957 case RTSERIALPORT_WAKEUP_PIPE_REASON_STS_LINE_MONITOR_FAILED:
958 fEvtsPending |= RTSERIALPORT_EVT_F_STATUS_LINE_MONITOR_FAILED;
959 break;
960 default:
961 AssertFailed();
962 rc = VERR_INTERNAL_ERROR;
963 }
964 }
965 else
966 rc = VERR_INTERNAL_ERROR;
967 }
968 }
969 else
970 rc = VERR_TIMEOUT;
971
972 *pfEvtsRecv = fEvtsPending & fEvtMask;
973 fEvtsPending &= ~fEvtMask;
974 ASMAtomicOrU32(&pThis->fEvtsPending, fEvtsPending);
975
976 return rc;
977}
978
979
980RTDECL(int) RTSerialPortEvtPollInterrupt(RTSERIALPORT hSerialPort)
981{
982 PRTSERIALPORTINTERNAL pThis = hSerialPort;
983 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
984 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
985
986 return rtSerialPortWakeupEvtPoller(pThis, RTSERIALPORT_WAKEUP_PIPE_REASON_INTERRUPT);
987}
988
989
990RTDECL(int) RTSerialPortChgBreakCondition(RTSERIALPORT hSerialPort, bool fSet)
991{
992 PRTSERIALPORTINTERNAL pThis = hSerialPort;
993 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
994 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
995
996 int rc = VINF_SUCCESS;
997 int rcPsx = ioctl(pThis->iFd, fSet ? TIOCSBRK : TIOCCBRK);
998 if (rcPsx == -1)
999 rc = RTErrConvertFromErrno(errno);
1000
1001 return rc;
1002}
1003
1004
1005RTDECL(int) RTSerialPortChgStatusLines(RTSERIALPORT hSerialPort, uint32_t fClear, uint32_t fSet)
1006{
1007 PRTSERIALPORTINTERNAL pThis = hSerialPort;
1008 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
1009 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
1010
1011 int rc = VINF_SUCCESS;
1012 int fTiocmSet = 0;
1013 int fTiocmClear = 0;
1014
1015 if (fClear & RTSERIALPORT_CHG_STS_LINES_F_RTS)
1016 fTiocmClear |= TIOCM_RTS;
1017 if (fClear & RTSERIALPORT_CHG_STS_LINES_F_DTR)
1018 fTiocmClear |= TIOCM_DTR;
1019
1020 if (fSet & RTSERIALPORT_CHG_STS_LINES_F_RTS)
1021 fTiocmSet |= TIOCM_RTS;
1022 if (fSet & RTSERIALPORT_CHG_STS_LINES_F_DTR)
1023 fTiocmSet |= TIOCM_DTR;
1024
1025 int rcPsx = ioctl(pThis->iFd, TIOCMBIS, &fTiocmSet);
1026 if (!rcPsx)
1027 {
1028 rcPsx = ioctl(pThis->iFd, TIOCMBIC, &fTiocmClear);
1029 if (rcPsx == -1)
1030 rc = RTErrConvertFromErrno(errno);
1031 }
1032 return rc;
1033}
1034
1035
1036RTDECL(int) RTSerialPortQueryStatusLines(RTSERIALPORT hSerialPort, uint32_t *pfStsLines)
1037{
1038 PRTSERIALPORTINTERNAL pThis = hSerialPort;
1039 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
1040 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
1041 AssertPtrReturn(pfStsLines, VERR_INVALID_POINTER);
1042
1043 *pfStsLines = 0;
1044
1045 int rc = VINF_SUCCESS;
1046 int fStsLines = 0;
1047 int rcPsx = ioctl(pThis->iFd, TIOCMGET, &fStsLines);
1048 if (!rcPsx)
1049 {
1050 /* This resets the status line event pending flag. */
1051 for (;;)
1052 {
1053 uint32_t fEvtsPending = ASMAtomicReadU32(&pThis->fEvtsPending);
1054 if (ASMAtomicCmpXchgU32(&pThis->fEvtsPending, fEvtsPending & ~RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED, fEvtsPending))
1055 break;
1056 }
1057
1058 *pfStsLines |= (fStsLines & TIOCM_CAR) ? RTSERIALPORT_STS_LINE_DCD : 0;
1059 *pfStsLines |= (fStsLines & TIOCM_RNG) ? RTSERIALPORT_STS_LINE_RI : 0;
1060 *pfStsLines |= (fStsLines & TIOCM_DSR) ? RTSERIALPORT_STS_LINE_DSR : 0;
1061 *pfStsLines |= (fStsLines & TIOCM_CTS) ? RTSERIALPORT_STS_LINE_CTS : 0;
1062 }
1063 else
1064 rc = RTErrConvertFromErrno(errno);
1065
1066 return rc;
1067}
1068
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette