VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

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