VirtualBox

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

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

Runtime/serialport-posix: Add custom baud rate support on Linux hosts

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 41.7 KB
Line 
1/* $Id: serialport-posix.cpp 71956 2018-04-22 11:50:09Z vboxsync $ */
2/** @file
3 * IPRT - Serial Port API, POSIX Implementation.
4 */
5
6/*
7 * Copyright (C) 2017-2018 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)
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)
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
838 cbRead += cbReadPart;
839 }
840 }
841 }
842 else
843 rc = RTErrConvertFromErrno(errno);
844 }
845
846 return rc;
847}
848
849
850RTDECL(int) RTSerialPortReadNB(RTSERIALPORT hSerialPort, void *pvBuf, size_t cbToRead, size_t *pcbRead)
851{
852 PRTSERIALPORTINTERNAL pThis = hSerialPort;
853 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
854 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
855 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
856 AssertReturn(cbToRead > 0, VERR_INVALID_PARAMETER);
857 AssertPtrReturn(pcbRead, VERR_INVALID_POINTER);
858
859 *pcbRead = 0;
860
861 int rc = rtSerialPortSwitchBlockingMode(pThis, false);
862 if (RT_SUCCESS(rc))
863 {
864 ssize_t cbThisRead = read(pThis->iFd, pvBuf, cbToRead);
865 if (cbThisRead > 0)
866 {
867 /*
868 * The read data needs to be scanned for the BREAK condition marker encoded in the data stream,
869 * if break detection was enabled during open.
870 */
871 if (pThis->fOpenFlags & RTSERIALPORT_OPEN_F_DETECT_BREAK_CONDITION)
872 { /** @todo */ }
873
874 *pcbRead = cbThisRead;
875 }
876 else if (cbThisRead == 0 || errno == EAGAIN || errno == EWOULDBLOCK)
877 rc = VINF_TRY_AGAIN;
878 else
879 rc = RTErrConvertFromErrno(errno);
880 }
881
882 return rc;
883}
884
885
886RTDECL(int) RTSerialPortWrite(RTSERIALPORT hSerialPort, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
887{
888 PRTSERIALPORTINTERNAL pThis = hSerialPort;
889 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
890 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
891 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
892 AssertReturn(cbToWrite > 0, VERR_INVALID_PARAMETER);
893
894 int rc = rtSerialPortSwitchBlockingMode(pThis, true);
895 if (RT_SUCCESS(rc))
896 {
897 /*
898 * Attempt write.
899 */
900 ssize_t cbWritten = write(pThis->iFd, pvBuf, cbToWrite);
901 if (cbWritten >= 0)
902 {
903 if (pcbWritten)
904 /* caller can handle partial write. */
905 *pcbWritten = cbWritten;
906 else
907 {
908 /* Caller expects all to be written. */
909 while ((ssize_t)cbToWrite > cbWritten)
910 {
911 ssize_t cbWrittenPart = write(pThis->iFd, (const uint8_t *)pvBuf + cbWritten, cbToWrite - cbWritten);
912 if (cbWrittenPart < 0)
913 return RTErrConvertFromErrno(errno);
914 cbWritten += cbWrittenPart;
915 }
916 }
917 }
918 }
919
920 return rc;
921}
922
923
924RTDECL(int) RTSerialPortWriteNB(RTSERIALPORT hSerialPort, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
925{
926 PRTSERIALPORTINTERNAL pThis = hSerialPort;
927 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
928 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
929 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
930 AssertReturn(cbToWrite > 0, VERR_INVALID_PARAMETER);
931 AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
932
933 *pcbWritten = 0;
934
935 int rc = rtSerialPortSwitchBlockingMode(pThis, false);
936 if (RT_SUCCESS(rc))
937 {
938 ssize_t cbThisWrite = write(pThis->iFd, pvBuf, cbToWrite);
939 if (cbThisWrite > 0)
940 *pcbWritten = cbThisWrite;
941 else if (cbThisWrite == 0 || errno == EAGAIN || errno == EWOULDBLOCK)
942 rc = VINF_TRY_AGAIN;
943 else
944 rc = RTErrConvertFromErrno(errno);
945 }
946
947 return rc;
948}
949
950
951RTDECL(int) RTSerialPortCfgQueryCurrent(RTSERIALPORT hSerialPort, PRTSERIALPORTCFG pCfg)
952{
953 PRTSERIALPORTINTERNAL pThis = hSerialPort;
954 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
955 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
956
957 return rtSerialPortTermios2Cfg(pThis, &pThis->PortCfg, pCfg);
958}
959
960
961RTDECL(int) RTSerialPortCfgSet(RTSERIALPORT hSerialPort, PCRTSERIALPORTCFG pCfg, PRTERRINFO pErrInfo)
962{
963 PRTSERIALPORTINTERNAL pThis = hSerialPort;
964 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
965 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
966
967 struct termios PortCfgNew; RT_ZERO(PortCfgNew);
968 bool fBaudrateCust = false;
969 int rc = rtSerialPortCfg2Termios(pThis, pCfg, &PortCfgNew, &fBaudrateCust, pErrInfo);
970 if (RT_SUCCESS(rc))
971 {
972 int rcPsx = tcflush(pThis->iFd, TCIOFLUSH);
973 if (!rcPsx)
974 {
975#ifdef RT_OS_LINUX
976 if (fBaudrateCust)
977 {
978 struct serial_struct SerLnx;
979 rcPsx = ioctl(pThis->iFd, TIOCGSERIAL, &SerLnx);
980 if (!rcPsx)
981 {
982 SerLnx.custom_divisor = SerLnx.baud_base / pCfg->uBaudRate;
983 if (!SerLnx.custom_divisor)
984 SerLnx.custom_divisor = 1;
985 SerLnx.flags &= ~ASYNC_SPD_MASK;
986 SerLnx.flags |= ASYNC_SPD_CUST;
987 rcPsx = ioctl(pThis->iFd, TIOCSSERIAL, &SerLnx);
988 }
989 }
990#else /* !RT_OS_LINUX */
991 /* Hosts not supporting custom baud rates should already fail in rtSerialPortCfg2Termios(). */
992 AssertMsgFailed(("Should not get here!\n"));
993#endif /* !RT_OS_LINUX */
994 pThis->fBaudrateCust = fBaudrateCust;
995 pThis->uBaudRateCust = pCfg->uBaudRate;
996
997 if (!rcPsx)
998 rcPsx = tcsetattr(pThis->iFd, TCSANOW, &PortCfgNew);
999 if (rcPsx == -1)
1000 rc = RTErrConvertFromErrno(errno);
1001 else
1002 memcpy(&pThis->PortCfg, &PortCfgNew, sizeof(struct termios));
1003
1004#ifdef RT_OS_LINUX
1005 /*
1006 * XXX In Linux, if a thread calls tcsetattr while the monitor thread is
1007 * waiting in ioctl for a modem status change then 8250.c wrongly disables
1008 * modem irqs and so the monitor thread never gets released. The workaround
1009 * is to send a signal after each tcsetattr.
1010 */
1011 if (pThis->fOpenFlags & RTSERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING)
1012 RTThreadPoke(pThis->hMonThrd);
1013#endif
1014 }
1015 else
1016 rc = RTErrConvertFromErrno(errno);
1017 }
1018
1019 return rc;
1020}
1021
1022
1023RTDECL(int) RTSerialPortEvtPoll(RTSERIALPORT hSerialPort, uint32_t fEvtMask, uint32_t *pfEvtsRecv,
1024 RTMSINTERVAL msTimeout)
1025{
1026 PRTSERIALPORTINTERNAL pThis = hSerialPort;
1027 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
1028 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
1029 AssertReturn(!(fEvtMask & ~RTSERIALPORT_EVT_F_VALID_MASK), VERR_INVALID_PARAMETER);
1030 AssertPtrReturn(pfEvtsRecv, VERR_INVALID_POINTER);
1031
1032 *pfEvtsRecv = 0;
1033
1034 fEvtMask |= RTSERIALPORT_EVT_F_STATUS_LINE_MONITOR_FAILED; /* This will be reported always, no matter what the caller wants. */
1035
1036 /* Return early if there are events pending from previous calls which weren't fetched yet. */
1037 for (;;)
1038 {
1039 uint32_t fEvtsPending = ASMAtomicReadU32(&pThis->fEvtsPending);
1040 if (fEvtsPending & fEvtMask)
1041 {
1042 *pfEvtsRecv = fEvtsPending & fEvtMask;
1043 /* Write back, repeat the whole procedure if someone else raced us. */
1044 if (ASMAtomicCmpXchgU32(&pThis->fEvtsPending, fEvtsPending & ~fEvtMask, fEvtsPending))
1045 return VINF_SUCCESS;
1046 }
1047 else
1048 break;
1049 }
1050
1051 int rc = rtSerialPortSwitchBlockingMode(pThis, false);
1052 if (RT_SUCCESS(rc))
1053 {
1054 struct pollfd aPollFds[2]; RT_ZERO(aPollFds);
1055 aPollFds[0].fd = pThis->iFd;
1056 aPollFds[0].events = POLLERR | POLLHUP;
1057 aPollFds[0].revents = 0;
1058 if ( (pThis->fOpenFlags & RTSERIALPORT_OPEN_F_READ)
1059 && (fEvtMask & RTSERIALPORT_EVT_F_DATA_RX))
1060 aPollFds[0].events |= POLLIN;
1061 if ( (pThis->fOpenFlags & RTSERIALPORT_OPEN_F_WRITE)
1062 && (fEvtMask & RTSERIALPORT_EVT_F_DATA_TX))
1063 aPollFds[0].events |= POLLOUT;
1064
1065 aPollFds[1].fd = pThis->iFdPipeR;
1066 aPollFds[1].events = POLLIN | POLLERR | POLLHUP;
1067 aPollFds[1].revents = 0;
1068
1069 int rcPsx = 0;
1070 int msTimeoutLeft = msTimeout == RT_INDEFINITE_WAIT ? -1 : msTimeout;
1071 while (msTimeoutLeft != 0)
1072 {
1073 uint64_t tsPollStart = RTTimeMilliTS();
1074
1075 rcPsx = poll(&aPollFds[0], RT_ELEMENTS(aPollFds), msTimeoutLeft);
1076 if (rcPsx != -1 || errno != EINTR)
1077 break;
1078 /* Restart when getting interrupted. */
1079 if (msTimeoutLeft > -1)
1080 {
1081 uint64_t tsPollEnd = RTTimeMilliTS();
1082 uint64_t tsPollSpan = tsPollEnd - tsPollStart;
1083 msTimeoutLeft -= RT_MIN(tsPollSpan, (uint32_t)msTimeoutLeft);
1084 }
1085 }
1086
1087 uint32_t fEvtsPending = 0;
1088 if (rcPsx < 0 && errno != EINTR)
1089 rc = RTErrConvertFromErrno(errno);
1090 else if (rcPsx > 0)
1091 {
1092 if (aPollFds[0].revents != 0)
1093 {
1094 fEvtsPending |= (aPollFds[0].revents & POLLIN) ? RTSERIALPORT_EVT_F_DATA_RX : 0;
1095 fEvtsPending |= (aPollFds[0].revents & POLLOUT) ? RTSERIALPORT_EVT_F_DATA_TX : 0;
1096 /** @todo BREAK condition detection. */
1097 }
1098
1099 if (aPollFds[1].revents != 0)
1100 {
1101 AssertReturn(!(aPollFds[1].revents & (POLLHUP | POLLERR | POLLNVAL)), VERR_INTERNAL_ERROR);
1102 Assert(aPollFds[1].revents & POLLIN);
1103
1104 uint8_t bWakeupReason = 0;
1105 ssize_t cbRead = read(pThis->iFdPipeR, &bWakeupReason, 1);
1106 if (cbRead == 1)
1107 {
1108 switch (bWakeupReason)
1109 {
1110 case RTSERIALPORT_WAKEUP_PIPE_REASON_INTERRUPT:
1111 rc = VERR_INTERRUPTED;
1112 break;
1113 case RTSERIALPORT_WAKEUP_PIPE_REASON_STS_LINE_CHANGED:
1114 fEvtsPending |= RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED;
1115 break;
1116 case RTSERIALPORT_WAKEUP_PIPE_REASON_STS_LINE_MONITOR_FAILED:
1117 fEvtsPending |= RTSERIALPORT_EVT_F_STATUS_LINE_MONITOR_FAILED;
1118 break;
1119 default:
1120 AssertFailed();
1121 rc = VERR_INTERNAL_ERROR;
1122 }
1123 }
1124 else
1125 rc = VERR_INTERNAL_ERROR;
1126 }
1127 }
1128 else
1129 rc = VERR_TIMEOUT;
1130
1131 *pfEvtsRecv = fEvtsPending & fEvtMask;
1132 fEvtsPending &= ~fEvtMask;
1133 ASMAtomicOrU32(&pThis->fEvtsPending, fEvtsPending);
1134 }
1135
1136 return rc;
1137}
1138
1139
1140RTDECL(int) RTSerialPortEvtPollInterrupt(RTSERIALPORT hSerialPort)
1141{
1142 PRTSERIALPORTINTERNAL pThis = hSerialPort;
1143 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
1144 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
1145
1146 return rtSerialPortWakeupEvtPoller(pThis, RTSERIALPORT_WAKEUP_PIPE_REASON_INTERRUPT);
1147}
1148
1149
1150RTDECL(int) RTSerialPortChgBreakCondition(RTSERIALPORT hSerialPort, bool fSet)
1151{
1152 PRTSERIALPORTINTERNAL pThis = hSerialPort;
1153 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
1154 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
1155
1156 int rc = VINF_SUCCESS;
1157 int rcPsx = ioctl(pThis->iFd, fSet ? TIOCSBRK : TIOCCBRK);
1158 if (rcPsx == -1)
1159 rc = RTErrConvertFromErrno(errno);
1160
1161 return rc;
1162}
1163
1164
1165RTDECL(int) RTSerialPortChgStatusLines(RTSERIALPORT hSerialPort, uint32_t fClear, uint32_t fSet)
1166{
1167 PRTSERIALPORTINTERNAL pThis = hSerialPort;
1168 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
1169 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
1170
1171 int rc = VINF_SUCCESS;
1172 int fTiocmSet = 0;
1173 int fTiocmClear = 0;
1174
1175 if (fClear & RTSERIALPORT_CHG_STS_LINES_F_RTS)
1176 fTiocmClear |= TIOCM_RTS;
1177 if (fClear & RTSERIALPORT_CHG_STS_LINES_F_DTR)
1178 fTiocmClear |= TIOCM_DTR;
1179
1180 if (fSet & RTSERIALPORT_CHG_STS_LINES_F_RTS)
1181 fTiocmSet |= TIOCM_RTS;
1182 if (fSet & RTSERIALPORT_CHG_STS_LINES_F_DTR)
1183 fTiocmSet |= TIOCM_DTR;
1184
1185 int rcPsx = ioctl(pThis->iFd, TIOCMBIS, &fTiocmSet);
1186 if (!rcPsx)
1187 {
1188 rcPsx = ioctl(pThis->iFd, TIOCMBIC, &fTiocmClear);
1189 if (rcPsx == -1)
1190 rc = RTErrConvertFromErrno(errno);
1191 }
1192 return rc;
1193}
1194
1195
1196RTDECL(int) RTSerialPortQueryStatusLines(RTSERIALPORT hSerialPort, uint32_t *pfStsLines)
1197{
1198 PRTSERIALPORTINTERNAL pThis = hSerialPort;
1199 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
1200 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
1201 AssertPtrReturn(pfStsLines, VERR_INVALID_POINTER);
1202
1203 *pfStsLines = 0;
1204
1205 int rc = VINF_SUCCESS;
1206 int fStsLines = 0;
1207 int rcPsx = ioctl(pThis->iFd, TIOCMGET, &fStsLines);
1208 if (!rcPsx)
1209 {
1210 /* This resets the status line event pending flag. */
1211 for (;;)
1212 {
1213 uint32_t fEvtsPending = ASMAtomicReadU32(&pThis->fEvtsPending);
1214 if (ASMAtomicCmpXchgU32(&pThis->fEvtsPending, fEvtsPending & ~RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED, fEvtsPending))
1215 break;
1216 }
1217
1218 *pfStsLines |= (fStsLines & TIOCM_CAR) ? RTSERIALPORT_STS_LINE_DCD : 0;
1219 *pfStsLines |= (fStsLines & TIOCM_RNG) ? RTSERIALPORT_STS_LINE_RI : 0;
1220 *pfStsLines |= (fStsLines & TIOCM_DSR) ? RTSERIALPORT_STS_LINE_DSR : 0;
1221 *pfStsLines |= (fStsLines & TIOCM_CTS) ? RTSERIALPORT_STS_LINE_CTS : 0;
1222 }
1223 else
1224 rc = RTErrConvertFromErrno(errno);
1225
1226 return rc;
1227}
1228
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