VirtualBox

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

Last change on this file since 86297 was 83520, checked in by vboxsync, 5 years ago

Runtime/r3/serialport-posix: More error checking, read/write methods should return an error if read() or write() return 0 bytes as this indicate an end of file condition which is an error for serial ports, and check for the POLLERR flag first before checking POLLIN (as both get set)

  • 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 83520 2020-04-03 08:33:32Z 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 = 0;
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 AssertReturn(VALID_PTR(pszPortAddress) && *pszPortAddress != '\0', VERR_INVALID_PARAMETER);
701 AssertReturn(!(fFlags & ~RTSERIALPORT_OPEN_F_VALID_MASK), VERR_INVALID_PARAMETER);
702 AssertReturn((fFlags & RTSERIALPORT_OPEN_F_READ) || (fFlags & RTSERIALPORT_OPEN_F_WRITE),
703 VERR_INVALID_PARAMETER);
704
705 int rc = VINF_SUCCESS;
706 PRTSERIALPORTINTERNAL pThis = (PRTSERIALPORTINTERNAL)RTMemAllocZ(sizeof(*pThis));
707 if (pThis)
708 {
709 int fPsxFlags = O_NOCTTY | O_NONBLOCK;
710
711 if ((fFlags & RTSERIALPORT_OPEN_F_READ) && !(fFlags & RTSERIALPORT_OPEN_F_WRITE))
712 fPsxFlags |= O_RDONLY;
713 else if (!(fFlags & RTSERIALPORT_OPEN_F_READ) && (fFlags & RTSERIALPORT_OPEN_F_WRITE))
714 fPsxFlags |= O_WRONLY;
715 else
716 fPsxFlags |= O_RDWR;
717
718 pThis->u32Magic = RTSERIALPORT_MAGIC;
719 pThis->fOpenFlags = fFlags;
720 pThis->fEvtsPending = 0;
721 pThis->iFd = open(pszPortAddress, fPsxFlags);
722 pThis->fBlocking = false;
723 if (pThis->iFd != -1)
724 {
725 /* Create wakeup pipe for the event API. */
726 int aPipeFds[2];
727 int rcPsx = pipe(&aPipeFds[0]);
728 if (!rcPsx)
729 {
730 /* Make the pipes close on exec. */
731 pThis->iFdPipeR = aPipeFds[0];
732 pThis->iFdPipeW = aPipeFds[1];
733
734 if (fcntl(pThis->iFdPipeR, F_SETFD, FD_CLOEXEC))
735 rc = RTErrConvertFromErrno(errno);
736
737 if ( RT_SUCCESS(rc)
738 && fcntl(pThis->iFdPipeW, F_SETFD, FD_CLOEXEC))
739 rc = RTErrConvertFromErrno(errno);
740
741 if (RT_SUCCESS(rc))
742 {
743 rc = rtSerialPortSetDefaultCfg(pThis);
744 if ( RT_SUCCESS(rc)
745 && (fFlags & RTSERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING))
746 rc = rtSerialPortMonitorThreadCreate(pThis);
747
748 if (RT_SUCCESS(rc))
749 {
750 *phSerialPort = pThis;
751 return VINF_SUCCESS;
752 }
753 }
754
755 close(pThis->iFdPipeR);
756 close(pThis->iFdPipeW);
757 }
758 else
759 rc = RTErrConvertFromErrno(errno);
760
761 close(pThis->iFd);
762 }
763 else
764 rc = RTErrConvertFromErrno(errno);
765
766 RTMemFree(pThis);
767 }
768 else
769 rc = VERR_NO_MEMORY;
770
771 return rc;
772}
773
774
775RTDECL(int) RTSerialPortClose(RTSERIALPORT hSerialPort)
776{
777 PRTSERIALPORTINTERNAL pThis = hSerialPort;
778 if (pThis == NIL_RTSERIALPORT)
779 return VINF_SUCCESS;
780 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
781 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
782
783 /*
784 * Do the cleanup.
785 */
786 AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSERIALPORT_MAGIC_DEAD, RTSERIALPORT_MAGIC), VERR_INVALID_HANDLE);
787
788 if (pThis->fOpenFlags & RTSERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING)
789 rtSerialPortMonitorThreadShutdown(pThis);
790
791 close(pThis->iFd);
792 close(pThis->iFdPipeR);
793 close(pThis->iFdPipeW);
794 RTMemFree(pThis);
795 return VINF_SUCCESS;
796}
797
798
799RTDECL(RTHCINTPTR) RTSerialPortToNative(RTSERIALPORT hSerialPort)
800{
801 PRTSERIALPORTINTERNAL pThis = hSerialPort;
802 AssertPtrReturn(pThis, -1);
803 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, -1);
804
805 return pThis->iFd;
806}
807
808
809RTDECL(int) RTSerialPortRead(RTSERIALPORT hSerialPort, void *pvBuf, size_t cbToRead, size_t *pcbRead)
810{
811 PRTSERIALPORTINTERNAL pThis = hSerialPort;
812 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
813 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
814 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
815 AssertReturn(cbToRead > 0, VERR_INVALID_PARAMETER);
816
817 int rc = rtSerialPortSwitchBlockingMode(pThis, true);
818 if (RT_SUCCESS(rc))
819 {
820 /*
821 * Attempt read.
822 */
823 ssize_t cbRead = read(pThis->iFd, pvBuf, cbToRead);
824 if (cbRead > 0)
825 {
826 if (pcbRead)
827 /* caller can handle partial read. */
828 *pcbRead = cbRead;
829 else
830 {
831 /* Caller expects all to be read. */
832 while ((ssize_t)cbToRead > cbRead)
833 {
834 ssize_t cbReadPart = read(pThis->iFd, (uint8_t *)pvBuf + cbRead, cbToRead - cbRead);
835 if (cbReadPart < 0)
836 return RTErrConvertFromErrno(errno);
837 else if (cbReadPart == 0)
838 return VERR_DEV_IO_ERROR;
839
840 cbRead += cbReadPart;
841 }
842 }
843 }
844 else if (cbRead == 0)
845 rc = VERR_DEV_IO_ERROR;
846 else
847 rc = RTErrConvertFromErrno(errno);
848 }
849
850 return rc;
851}
852
853
854RTDECL(int) RTSerialPortReadNB(RTSERIALPORT hSerialPort, void *pvBuf, size_t cbToRead, size_t *pcbRead)
855{
856 PRTSERIALPORTINTERNAL pThis = hSerialPort;
857 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
858 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
859 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
860 AssertReturn(cbToRead > 0, VERR_INVALID_PARAMETER);
861 AssertPtrReturn(pcbRead, VERR_INVALID_POINTER);
862
863 *pcbRead = 0;
864
865 int rc = rtSerialPortSwitchBlockingMode(pThis, false);
866 if (RT_SUCCESS(rc))
867 {
868 ssize_t cbThisRead = read(pThis->iFd, pvBuf, cbToRead);
869 if (cbThisRead > 0)
870 {
871 /*
872 * The read data needs to be scanned for the BREAK condition marker encoded in the data stream,
873 * if break detection was enabled during open.
874 */
875 if (pThis->fOpenFlags & RTSERIALPORT_OPEN_F_DETECT_BREAK_CONDITION)
876 { /** @todo */ }
877
878 *pcbRead = cbThisRead;
879 }
880 else if (cbThisRead == 0)
881 rc = VERR_DEV_IO_ERROR;
882 else if ( errno == EAGAIN
883# ifdef EWOULDBLOCK
884# if EWOULDBLOCK != EAGAIN
885 || errno == EWOULDBLOCK
886# endif
887# endif
888 )
889 rc = VINF_TRY_AGAIN;
890 else
891 rc = RTErrConvertFromErrno(errno);
892 }
893
894 return rc;
895}
896
897
898RTDECL(int) RTSerialPortWrite(RTSERIALPORT hSerialPort, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
899{
900 PRTSERIALPORTINTERNAL pThis = hSerialPort;
901 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
902 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
903 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
904 AssertReturn(cbToWrite > 0, VERR_INVALID_PARAMETER);
905
906 int rc = rtSerialPortSwitchBlockingMode(pThis, true);
907 if (RT_SUCCESS(rc))
908 {
909 /*
910 * Attempt write.
911 */
912 ssize_t cbWritten = write(pThis->iFd, pvBuf, cbToWrite);
913 if (cbWritten > 0)
914 {
915 if (pcbWritten)
916 /* caller can handle partial write. */
917 *pcbWritten = cbWritten;
918 else
919 {
920 /* Caller expects all to be written. */
921 while ((ssize_t)cbToWrite > cbWritten)
922 {
923 ssize_t cbWrittenPart = write(pThis->iFd, (const uint8_t *)pvBuf + cbWritten, cbToWrite - cbWritten);
924 if (cbWrittenPart < 0)
925 return RTErrConvertFromErrno(errno);
926 else if (cbWrittenPart == 0)
927 return VERR_DEV_IO_ERROR;
928 cbWritten += cbWrittenPart;
929 }
930 }
931 }
932 else if (cbWritten == 0)
933 rc = VERR_DEV_IO_ERROR;
934 else
935 rc = RTErrConvertFromErrno(errno);
936 }
937
938 return rc;
939}
940
941
942RTDECL(int) RTSerialPortWriteNB(RTSERIALPORT hSerialPort, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
943{
944 PRTSERIALPORTINTERNAL pThis = hSerialPort;
945 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
946 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
947 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
948 AssertReturn(cbToWrite > 0, VERR_INVALID_PARAMETER);
949 AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
950
951 *pcbWritten = 0;
952
953 int rc = rtSerialPortSwitchBlockingMode(pThis, false);
954 if (RT_SUCCESS(rc))
955 {
956 ssize_t cbThisWrite = write(pThis->iFd, pvBuf, cbToWrite);
957 if (cbThisWrite > 0)
958 *pcbWritten = cbThisWrite;
959 else if (cbThisWrite == 0)
960 rc = VERR_DEV_IO_ERROR;
961 else if ( errno == EAGAIN
962# ifdef EWOULDBLOCK
963# if EWOULDBLOCK != EAGAIN
964 || errno == EWOULDBLOCK
965# endif
966# endif
967 )
968 rc = VINF_TRY_AGAIN;
969 else
970 rc = RTErrConvertFromErrno(errno);
971 }
972
973 return rc;
974}
975
976
977RTDECL(int) RTSerialPortCfgQueryCurrent(RTSERIALPORT hSerialPort, PRTSERIALPORTCFG pCfg)
978{
979 PRTSERIALPORTINTERNAL pThis = hSerialPort;
980 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
981 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
982
983 return rtSerialPortTermios2Cfg(pThis, &pThis->PortCfg, pCfg);
984}
985
986
987RTDECL(int) RTSerialPortCfgSet(RTSERIALPORT hSerialPort, PCRTSERIALPORTCFG pCfg, PRTERRINFO pErrInfo)
988{
989 PRTSERIALPORTINTERNAL pThis = hSerialPort;
990 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
991 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
992
993 struct termios PortCfgNew; RT_ZERO(PortCfgNew);
994 bool fBaudrateCust = false;
995 int rc = rtSerialPortCfg2Termios(pThis, pCfg, &PortCfgNew, &fBaudrateCust, pErrInfo);
996 if (RT_SUCCESS(rc))
997 {
998 int rcPsx = tcflush(pThis->iFd, TCIOFLUSH);
999 if (!rcPsx)
1000 {
1001#ifdef RT_OS_LINUX
1002 if (fBaudrateCust)
1003 {
1004 struct serial_struct SerLnx;
1005 rcPsx = ioctl(pThis->iFd, TIOCGSERIAL, &SerLnx);
1006 if (!rcPsx)
1007 {
1008 SerLnx.custom_divisor = SerLnx.baud_base / pCfg->uBaudRate;
1009 if (!SerLnx.custom_divisor)
1010 SerLnx.custom_divisor = 1;
1011 SerLnx.flags &= ~ASYNC_SPD_MASK;
1012 SerLnx.flags |= ASYNC_SPD_CUST;
1013 rcPsx = ioctl(pThis->iFd, TIOCSSERIAL, &SerLnx);
1014 }
1015 }
1016#else /* !RT_OS_LINUX */
1017 /* Hosts not supporting custom baud rates should already fail in rtSerialPortCfg2Termios(). */
1018 AssertMsgFailed(("Should not get here!\n"));
1019#endif /* !RT_OS_LINUX */
1020 pThis->fBaudrateCust = fBaudrateCust;
1021 pThis->uBaudRateCust = pCfg->uBaudRate;
1022
1023 if (!rcPsx)
1024 rcPsx = tcsetattr(pThis->iFd, TCSANOW, &PortCfgNew);
1025 if (rcPsx == -1)
1026 rc = RTErrConvertFromErrno(errno);
1027 else
1028 memcpy(&pThis->PortCfg, &PortCfgNew, sizeof(struct termios));
1029
1030#ifdef RT_OS_LINUX
1031 /*
1032 * XXX In Linux, if a thread calls tcsetattr while the monitor thread is
1033 * waiting in ioctl for a modem status change then 8250.c wrongly disables
1034 * modem irqs and so the monitor thread never gets released. The workaround
1035 * is to send a signal after each tcsetattr.
1036 */
1037 if (pThis->fOpenFlags & RTSERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING)
1038 RTThreadPoke(pThis->hMonThrd);
1039#endif
1040 }
1041 else
1042 rc = RTErrConvertFromErrno(errno);
1043 }
1044
1045 return rc;
1046}
1047
1048
1049RTDECL(int) RTSerialPortEvtPoll(RTSERIALPORT hSerialPort, uint32_t fEvtMask, uint32_t *pfEvtsRecv,
1050 RTMSINTERVAL msTimeout)
1051{
1052 PRTSERIALPORTINTERNAL pThis = hSerialPort;
1053 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
1054 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
1055 AssertReturn(!(fEvtMask & ~RTSERIALPORT_EVT_F_VALID_MASK), VERR_INVALID_PARAMETER);
1056 AssertPtrReturn(pfEvtsRecv, VERR_INVALID_POINTER);
1057
1058 *pfEvtsRecv = 0;
1059
1060 fEvtMask |= RTSERIALPORT_EVT_F_STATUS_LINE_MONITOR_FAILED; /* This will be reported always, no matter what the caller wants. */
1061
1062 /* Return early if there are events pending from previous calls which weren't fetched yet. */
1063 for (;;)
1064 {
1065 uint32_t fEvtsPending = ASMAtomicReadU32(&pThis->fEvtsPending);
1066 if (fEvtsPending & fEvtMask)
1067 {
1068 *pfEvtsRecv = fEvtsPending & fEvtMask;
1069 /* Write back, repeat the whole procedure if someone else raced us. */
1070 if (ASMAtomicCmpXchgU32(&pThis->fEvtsPending, fEvtsPending & ~fEvtMask, fEvtsPending))
1071 return VINF_SUCCESS;
1072 }
1073 else
1074 break;
1075 }
1076
1077 int rc = rtSerialPortSwitchBlockingMode(pThis, false);
1078 if (RT_SUCCESS(rc))
1079 {
1080 struct pollfd aPollFds[2]; RT_ZERO(aPollFds);
1081 aPollFds[0].fd = pThis->iFd;
1082 aPollFds[0].events = POLLERR | POLLHUP;
1083 aPollFds[0].revents = 0;
1084 if ( (pThis->fOpenFlags & RTSERIALPORT_OPEN_F_READ)
1085 && (fEvtMask & RTSERIALPORT_EVT_F_DATA_RX))
1086 aPollFds[0].events |= POLLIN;
1087 if ( (pThis->fOpenFlags & RTSERIALPORT_OPEN_F_WRITE)
1088 && (fEvtMask & RTSERIALPORT_EVT_F_DATA_TX))
1089 aPollFds[0].events |= POLLOUT;
1090
1091 aPollFds[1].fd = pThis->iFdPipeR;
1092 aPollFds[1].events = POLLIN | POLLERR | POLLHUP;
1093 aPollFds[1].revents = 0;
1094
1095 int rcPsx = 0;
1096 int msTimeoutLeft = msTimeout == RT_INDEFINITE_WAIT ? -1 : msTimeout;
1097 while (msTimeoutLeft != 0)
1098 {
1099 uint64_t tsPollStart = RTTimeMilliTS();
1100
1101 rcPsx = poll(&aPollFds[0], RT_ELEMENTS(aPollFds), msTimeoutLeft);
1102 if (rcPsx != -1 || errno != EINTR)
1103 break;
1104 /* Restart when getting interrupted. */
1105 if (msTimeoutLeft > -1)
1106 {
1107 uint64_t tsPollEnd = RTTimeMilliTS();
1108 uint64_t tsPollSpan = tsPollEnd - tsPollStart;
1109 msTimeoutLeft -= RT_MIN(tsPollSpan, (uint32_t)msTimeoutLeft);
1110 }
1111 }
1112
1113 uint32_t fEvtsPending = 0;
1114 if (rcPsx < 0 && errno != EINTR)
1115 rc = RTErrConvertFromErrno(errno);
1116 else if (rcPsx > 0)
1117 {
1118 if (aPollFds[0].revents != 0)
1119 {
1120 if (aPollFds[0].revents & POLLERR)
1121 rc = VERR_DEV_IO_ERROR;
1122 else
1123 {
1124 fEvtsPending |= (aPollFds[0].revents & POLLIN) ? RTSERIALPORT_EVT_F_DATA_RX : 0;
1125 fEvtsPending |= (aPollFds[0].revents & POLLOUT) ? RTSERIALPORT_EVT_F_DATA_TX : 0;
1126 /** @todo BREAK condition detection. */
1127 }
1128 }
1129
1130 if (aPollFds[1].revents != 0)
1131 {
1132 AssertReturn(!(aPollFds[1].revents & (POLLHUP | POLLERR | POLLNVAL)), VERR_INTERNAL_ERROR);
1133 Assert(aPollFds[1].revents & POLLIN);
1134
1135 uint8_t bWakeupReason = 0;
1136 ssize_t cbRead = read(pThis->iFdPipeR, &bWakeupReason, 1);
1137 if (cbRead == 1)
1138 {
1139 switch (bWakeupReason)
1140 {
1141 case RTSERIALPORT_WAKEUP_PIPE_REASON_INTERRUPT:
1142 rc = VERR_INTERRUPTED;
1143 break;
1144 case RTSERIALPORT_WAKEUP_PIPE_REASON_STS_LINE_CHANGED:
1145 fEvtsPending |= RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED;
1146 break;
1147 case RTSERIALPORT_WAKEUP_PIPE_REASON_STS_LINE_MONITOR_FAILED:
1148 fEvtsPending |= RTSERIALPORT_EVT_F_STATUS_LINE_MONITOR_FAILED;
1149 break;
1150 default:
1151 AssertFailed();
1152 rc = VERR_INTERNAL_ERROR;
1153 }
1154 }
1155 else
1156 rc = VERR_INTERNAL_ERROR;
1157 }
1158 }
1159 else
1160 rc = VERR_TIMEOUT;
1161
1162 *pfEvtsRecv = fEvtsPending & fEvtMask;
1163 fEvtsPending &= ~fEvtMask;
1164 ASMAtomicOrU32(&pThis->fEvtsPending, fEvtsPending);
1165 }
1166
1167 return rc;
1168}
1169
1170
1171RTDECL(int) RTSerialPortEvtPollInterrupt(RTSERIALPORT hSerialPort)
1172{
1173 PRTSERIALPORTINTERNAL pThis = hSerialPort;
1174 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
1175 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
1176
1177 return rtSerialPortWakeupEvtPoller(pThis, RTSERIALPORT_WAKEUP_PIPE_REASON_INTERRUPT);
1178}
1179
1180
1181RTDECL(int) RTSerialPortChgBreakCondition(RTSERIALPORT hSerialPort, bool fSet)
1182{
1183 PRTSERIALPORTINTERNAL pThis = hSerialPort;
1184 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
1185 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
1186
1187 int rc = VINF_SUCCESS;
1188 int rcPsx = ioctl(pThis->iFd, fSet ? TIOCSBRK : TIOCCBRK);
1189 if (rcPsx == -1)
1190 rc = RTErrConvertFromErrno(errno);
1191
1192 return rc;
1193}
1194
1195
1196RTDECL(int) RTSerialPortChgStatusLines(RTSERIALPORT hSerialPort, uint32_t fClear, uint32_t fSet)
1197{
1198 PRTSERIALPORTINTERNAL pThis = hSerialPort;
1199 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
1200 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
1201
1202 int rc = VINF_SUCCESS;
1203 int fTiocmSet = 0;
1204 int fTiocmClear = 0;
1205
1206 if (fClear & RTSERIALPORT_CHG_STS_LINES_F_RTS)
1207 fTiocmClear |= TIOCM_RTS;
1208 if (fClear & RTSERIALPORT_CHG_STS_LINES_F_DTR)
1209 fTiocmClear |= TIOCM_DTR;
1210
1211 if (fSet & RTSERIALPORT_CHG_STS_LINES_F_RTS)
1212 fTiocmSet |= TIOCM_RTS;
1213 if (fSet & RTSERIALPORT_CHG_STS_LINES_F_DTR)
1214 fTiocmSet |= TIOCM_DTR;
1215
1216 int rcPsx = ioctl(pThis->iFd, TIOCMBIS, &fTiocmSet);
1217 if (!rcPsx)
1218 {
1219 rcPsx = ioctl(pThis->iFd, TIOCMBIC, &fTiocmClear);
1220 if (rcPsx == -1)
1221 rc = RTErrConvertFromErrno(errno);
1222 }
1223 return rc;
1224}
1225
1226
1227RTDECL(int) RTSerialPortQueryStatusLines(RTSERIALPORT hSerialPort, uint32_t *pfStsLines)
1228{
1229 PRTSERIALPORTINTERNAL pThis = hSerialPort;
1230 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
1231 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
1232 AssertPtrReturn(pfStsLines, VERR_INVALID_POINTER);
1233
1234 *pfStsLines = 0;
1235
1236 int rc = VINF_SUCCESS;
1237 int fStsLines = 0;
1238 int rcPsx = ioctl(pThis->iFd, TIOCMGET, &fStsLines);
1239 if (!rcPsx)
1240 {
1241 /* This resets the status line event pending flag. */
1242 for (;;)
1243 {
1244 uint32_t fEvtsPending = ASMAtomicReadU32(&pThis->fEvtsPending);
1245 if (ASMAtomicCmpXchgU32(&pThis->fEvtsPending, fEvtsPending & ~RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED, fEvtsPending))
1246 break;
1247 }
1248
1249 *pfStsLines |= (fStsLines & TIOCM_CAR) ? RTSERIALPORT_STS_LINE_DCD : 0;
1250 *pfStsLines |= (fStsLines & TIOCM_RNG) ? RTSERIALPORT_STS_LINE_RI : 0;
1251 *pfStsLines |= (fStsLines & TIOCM_DSR) ? RTSERIALPORT_STS_LINE_DSR : 0;
1252 *pfStsLines |= (fStsLines & TIOCM_CTS) ? RTSERIALPORT_STS_LINE_CTS : 0;
1253 }
1254 else
1255 rc = RTErrConvertFromErrno(errno);
1256
1257 return rc;
1258}
1259
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