VirtualBox

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

Last change on this file since 92723 was 90803, checked in by vboxsync, 3 years ago

Runtime: More VALID_PTR -> RT_VALID_PTR/AssertPtr.

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