VirtualBox

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

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

Runtime/RTSerialPort: Implement synchronous read/write methods for POSIX and Windows, allows the serial transport layer in TestExecService to work

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