VirtualBox

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

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

Runtime: New serial port API [build fix for non Linux hosts]

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.6 KB
Line 
1/* $Id: serialport-posix.cpp 69894 2017-11-30 22:38:24Z 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.215389.xyz. 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 "internal/magics.h"
42
43#include <errno.h>
44#ifdef RT_OS_SOLARIS
45# include <sys/termios.h>
46#else
47# include <termios.h>
48#endif
49#include <sys/types.h>
50#include <fcntl.h>
51#include <string.h>
52#include <unistd.h>
53#ifdef RT_OS_DARWIN
54# include <sys/select.h>
55#else
56# include <sys/poll.h>
57#endif
58#include <sys/ioctl.h>
59#include <pthread.h>
60
61#ifdef RT_OS_LINUX
62/*
63 * TIOCM_LOOP is not defined in the above header files for some reason but in asm/termios.h.
64 * But inclusion of this file however leads to compilation errors because of redefinition of some
65 * structs. That's why it is defined here until a better solution is found.
66 */
67# ifndef TIOCM_LOOP
68# define TIOCM_LOOP 0x8000
69# endif
70/* For linux custom baudrate code we also need serial_struct */
71# include <linux/serial.h>
72#endif /* linux */
73
74/** Define fallback if not supported. */
75#if !defined(CMSPAR)
76# define CMSPAR 0
77#endif
78
79
80/*********************************************************************************************************************************
81* Structures and Typedefs *
82*********************************************************************************************************************************/
83
84/**
85 * Internal serial port state.
86 */
87typedef struct RTSERIALPORTINTERNAL
88{
89 /** Magic value (RTSERIALPORT_MAGIC). */
90 uint32_t u32Magic;
91 /** Flags given while opening the serial port. */
92 uint32_t fOpenFlags;
93 /** The file descriptor of the serial port. */
94 int iFd;
95 /** The status line monitor thread if enabled. */
96 RTTHREAD hMonThrd;
97 /** Flag whether the monitoring thread should shutdown. */
98 volatile bool fMonThrdShutdown;
99 /** The current active config (we assume no one changes this behind our back). */
100 struct termios PortCfg;
101} RTSERIALPORTINTERNAL;
102/** Pointer to the internal serial port state. */
103typedef RTSERIALPORTINTERNAL *PRTSERIALPORTINTERNAL;
104
105
106/**
107 * Baud rate conversion table descriptor.
108 */
109typedef struct RTSERIALPORTBRATECONVDESC
110{
111 /** The platform independent baud rate used by the RTSerialPort* API. */
112 uint32_t uBaudRateCfg;
113 /** The speed identifier used in the termios structure. */
114 speed_t iSpeedTermios;
115} RTSERIALPORTBRATECONVDESC;
116/** Pointer to a baud rate converions table descriptor. */
117typedef RTSERIALPORTBRATECONVDESC *PRTSERIALPORTBRATECONVDESC;
118/** Pointer to a const baud rate conversion table descriptor. */
119typedef const RTSERIALPORTBRATECONVDESC *PCRTSERIALPORTBRATECONVDESC;
120
121/*********************************************************************************************************************************
122* Defined Constants And Macros *
123*********************************************************************************************************************************/
124
125
126
127/*********************************************************************************************************************************
128* Global variables *
129*********************************************************************************************************************************/
130
131/** The baud rate conversion table. */
132static const RTSERIALPORTBRATECONVDESC s_rtSerialPortBaudrateConv[] =
133{
134 { 50, B50 },
135 { 75, B75 },
136 { 110, B110 },
137 { 134, B134 },
138 { 150, B150 },
139 { 200, B200 },
140 { 300, B300 },
141 { 600, B600 },
142 { 1200, B1200 },
143 { 1800, B1800 },
144 { 2400, B2400 },
145 { 4800, B4800 },
146 { 9600, B9600 },
147 { 19200, B19200 },
148 { 38400, B38400 },
149 { 57600, B57600 },
150 { 115200, B115200 }
151};
152
153
154
155/*********************************************************************************************************************************
156* Internal Functions *
157*********************************************************************************************************************************/
158
159/**
160 * Converts the given termios speed identifier to the baud rate used in the API.
161 *
162 * @returns Baud rate or 0 if not a standard baud rate
163 */
164DECLINLINE(uint32_t) rtSerialPortGetBaudrateFromTermiosSpeed(speed_t enmSpeed)
165{
166 for (unsigned i = 0; i < RT_ELEMENTS(s_rtSerialPortBaudrateConv); i++)
167 {
168 if (s_rtSerialPortBaudrateConv[i].iSpeedTermios == enmSpeed)
169 return s_rtSerialPortBaudrateConv[i].uBaudRateCfg;
170 }
171
172 return 0;
173}
174
175
176/**
177 * Converts the given baud rate to proper termios speed identifier.
178 *
179 * @returns Speed identifier if available or B0 if no matching speed for the baud rate
180 * could be found.
181 * @param uBaudRate The baud rate to convert.
182 */
183DECLINLINE(speed_t) rtSerialPortGetTermiosSpeedFromBaudrate(uint32_t uBaudRate)
184{
185 for (unsigned i = 0; i < RT_ELEMENTS(s_rtSerialPortBaudrateConv); i++)
186 {
187 if (s_rtSerialPortBaudrateConv[i].uBaudRateCfg == uBaudRate)
188 return s_rtSerialPortBaudrateConv[i].iSpeedTermios;
189 }
190
191 return B0;
192}
193
194
195/**
196 * Tries to set the default config on the given serial port.
197 *
198 * @returns IPRT status code.
199 * @param pThis The internal serial port instance data.
200 */
201static int rtSerialPortSetDefaultCfg(PRTSERIALPORTINTERNAL pThis)
202{
203 pThis->PortCfg.c_iflag = INPCK; /* Input parity checking. */
204 cfsetispeed(&pThis->PortCfg, B9600);
205 cfsetospeed(&pThis->PortCfg, B9600);
206 pThis->PortCfg.c_cflag = CS8 | CLOCAL; /* 8 data bits, ignore modem control lines. */
207 if (pThis->fOpenFlags & RT_SERIALPORT_OPEN_F_READ)
208 pThis->PortCfg.c_cflag |= CREAD; /* Enable receiver. */
209
210 /* Set to raw input mode. */
211 pThis->PortCfg.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ECHOK | ISIG | IEXTEN);
212 pThis->PortCfg.c_cc[VMIN] = 0; /* Achieve non-blocking behavior. */
213 pThis->PortCfg.c_cc[VTIME] = 0;
214
215 int rc = VINF_SUCCESS;
216 int rcPsx = tcflush(pThis->iFd, TCIOFLUSH);
217 if (!rcPsx)
218 {
219 rcPsx = tcsetattr(pThis->iFd, TCSANOW, &pThis->PortCfg);
220 if (rcPsx == -1)
221 rc = RTErrConvertFromErrno(errno);
222
223 if (RT_SUCCESS(rc))
224 {
225#ifdef RT_OS_LINUX
226 if (pThis->fOpenFlags & RT_SERIALPORT_OPEN_F_ENABLE_LOOPBACK)
227 {
228 int fTiocmSet = TIOCM_LOOP;
229 rcPsx = ioctl(pThis->iFd, TIOCMBIS, &fTiocmSet);
230 if (rcPsx == -1)
231 rc = RTErrConvertFromErrno(errno);
232 }
233 else
234 {
235 /* Make sure it is clear. */
236 int fTiocmClear = TIOCM_LOOP;
237 rcPsx = ioctl(pThis->iFd, TIOCMBIC, &fTiocmClear);
238 if (rcPsx == -1)
239 rc = RTErrConvertFromErrno(errno);
240 }
241#else
242 if (pThis->fOpenFlags & RT_SERIALPORT_OPEN_F_ENABLE_LOOPBACK)
243 return VERR_NOT_SUPPORTED;
244#endif
245 }
246 }
247 else
248 rc = RTErrConvertFromErrno(errno);
249
250 return rc;
251}
252
253
254/**
255 * Converts the given serial port config to the appropriate termios counterpart.
256 *
257 * @returns IPRT status code.
258 * @param pCfg Pointer to the serial port config descriptor.
259 * @param pTermios Pointer to the termios structure to fill.
260 * @param pErrInfo Additional error to be set when the conversion fails.
261 */
262static int rtSerialPortCfg2Termios(PCRTSERIALPORTCFG pCfg, struct termios *pTermios, PRTERRINFO pErrInfo)
263{
264 RT_NOREF(pErrInfo); /** @todo: Make use of the error info. */
265 speed_t enmSpeed = rtSerialPortGetTermiosSpeedFromBaudrate(pCfg->uBaudRate);
266 if (enmSpeed != B0)
267 {
268 tcflag_t const fCFlagMask = (CS5 | CS6 | CS7 | CS8 | CSTOPB | PARENB | PARODD | CMSPAR);
269 tcflag_t fCFlagNew = 0;
270
271 switch (pCfg->enmDataBitCount)
272 {
273 case RTSERIALPORTDATABITS_5BITS:
274 fCFlagNew |= CS5;
275 break;
276 case RTSERIALPORTDATABITS_6BITS:
277 fCFlagNew |= CS6;
278 break;
279 case RTSERIALPORTDATABITS_7BITS:
280 fCFlagNew |= CS7;
281 break;
282 case RTSERIALPORTDATABITS_8BITS:
283 fCFlagNew |= CS8;
284 break;
285 default:
286 AssertFailed();
287 return VERR_INVALID_PARAMETER;
288 }
289
290 switch (pCfg->enmParity)
291 {
292 case RTSERIALPORTPARITY_NONE:
293 break;
294 case RTSERIALPORTPARITY_EVEN:
295 fCFlagNew |= PARENB;
296 break;
297 case RTSERIALPORTPARITY_ODD:
298 fCFlagNew |= PARENB | PARODD;
299 break;
300#if CMSPAR != 0
301 case RTSERIALPORTPARITY_MARK:
302 fCFlagNew |= PARENB | CMSPAR | PARODD;
303 break;
304 case RTSERIALPORTPARITY_SPACE:
305 fCFlagNew |= PARENB | CMSPAR;
306 break;
307#else
308 case RTSERIALPORTPARITY_MARK:
309 case RTSERIALPORTPARITY_SPACE:
310 return VERR_NOT_SUPPORTED;
311#endif
312 default:
313 AssertFailed();
314 return VERR_INVALID_PARAMETER;
315 }
316
317 switch (pCfg->enmStopBitCount)
318 {
319 case RTSERIALPORTSTOPBITS_ONE:
320 break;
321 case RTSERIALPORTSTOPBITS_ONEPOINTFIVE:
322 if (pCfg->enmDataBitCount == RTSERIALPORTDATABITS_5BITS)
323 fCFlagNew |= CSTOPB;
324 else
325 return VERR_NOT_SUPPORTED;
326 break;
327 case RTSERIALPORTSTOPBITS_TWO:
328 if (pCfg->enmDataBitCount != RTSERIALPORTDATABITS_5BITS)
329 fCFlagNew |= CSTOPB;
330 else
331 return VERR_NOT_SUPPORTED;
332 break;
333 default:
334 AssertFailed();
335 return VERR_INVALID_PARAMETER;
336 }
337
338 /* Assign new flags. */
339 pTermios->c_cflag = (pTermios->c_cflag & ~fCFlagMask) | fCFlagNew;
340 }
341 else
342 return VERR_SERIALPORT_INVALID_BAUDRATE;
343
344#ifdef RT_OS_LINUX
345 /** @todo: Handle custom baudrates supported by Linux. */
346#endif
347
348 return VINF_SUCCESS;
349}
350
351
352/**
353 * Converts the given termios structure to an appropriate serial port config.
354 *
355 * @returns IPRT status code.
356 * @param pTermios The termios structure to convert.
357 * @param pCfg The serial port config to fill in.
358 */
359static int rtSerialPortTermios2Cfg(struct termios *pTermios, PRTSERIALPORTCFG pCfg)
360{
361 int rc = VINF_SUCCESS;
362 bool f5DataBits = false;
363 speed_t enmSpeedIn = cfgetispeed(pTermios);
364 Assert(enmSpeedIn == cfgetospeed(pTermios)); /* Should always be the same. */
365
366 pCfg->uBaudRate = rtSerialPortGetBaudrateFromTermiosSpeed(enmSpeedIn);
367 if (!pCfg->uBaudRate)
368 rc = VERR_SERIALPORT_INVALID_BAUDRATE;
369
370 switch (pTermios->c_cflag & CSIZE)
371 {
372 case CS5:
373 pCfg->enmDataBitCount = RTSERIALPORTDATABITS_5BITS;
374 f5DataBits = true;
375 break;
376 case CS6:
377 pCfg->enmDataBitCount = RTSERIALPORTDATABITS_6BITS;
378 break;
379 case CS7:
380 pCfg->enmDataBitCount = RTSERIALPORTDATABITS_7BITS;
381 break;
382 case CS8:
383 pCfg->enmDataBitCount = RTSERIALPORTDATABITS_8BITS;
384 break;
385 default:
386 AssertFailed(); /* Should not happen. */
387 pCfg->enmDataBitCount = RTSERIALPORTDATABITS_INVALID;
388 rc = RT_FAILURE(rc) ? rc : VERR_INVALID_PARAMETER;
389 }
390
391 /* Convert parity. */
392 if (pTermios->c_cflag & PARENB)
393 {
394 /*
395 * CMSPAR is not supported on all systems, especially OS X. As configuring
396 * mark/space parity there is not supported and we start from a known config
397 * when opening the serial port it is not required to check for this here.
398 */
399#if CMSPAR == 0
400 bool fCmsParSet = RT_BOOL(pTermios->c_cflag & CMSPAR);
401#else
402 bool fCmsParSet = false;
403#endif
404 if (pTermios->c_cflag & PARODD)
405 pCfg->enmParity = fCmsParSet ? RTSERIALPORTPARITY_MARK : RTSERIALPORTPARITY_ODD;
406 else
407 pCfg->enmParity = fCmsParSet ? RTSERIALPORTPARITY_SPACE: RTSERIALPORTPARITY_EVEN;
408 }
409 else
410 pCfg->enmParity = RTSERIALPORTPARITY_NONE;
411
412 /*
413 * 1.5 stop bits are used with a data count of 5 bits when a UART derived from the 8250
414 * is used.
415 */
416 if (pTermios->c_cflag & CSTOPB)
417 pCfg->enmStopBitCount = f5DataBits ? RTSERIALPORTSTOPBITS_ONEPOINTFIVE : RTSERIALPORTSTOPBITS_TWO;
418 else
419 pCfg->enmStopBitCount = RTSERIALPORTSTOPBITS_ONE;
420
421 return rc;
422}
423
424
425/**
426 * The status line monitor thread worker.
427 *
428 * @returns IPRT status code.
429 * @param ThreadSelf Thread handle to this thread.
430 * @param pvUser User argument.
431 */
432static DECLCALLBACK(int) rtSerialPortStsLineMonitorThrd(RTTHREAD hThreadSelf, void *pvUser)
433{
434 RT_NOREF(hThreadSelf);
435 PRTSERIALPORTINTERNAL pThis = (PRTSERIALPORTINTERNAL)pvUser;
436 unsigned long const fStsLinesChk = TIOCM_CAR | TIOCM_RNG | TIOCM_DSR | TIOCM_CTS;
437 int rc = VINF_SUCCESS;
438 uint32_t fStsLinesOld = 0;
439 uint32_t cStsLineGetErrors = 0;
440#ifdef RT_OS_LINUX
441 bool fPoll = false;
442#endif
443
444 int rcPsx = ioctl(pThis->iFd, TIOCMGET, &fStsLinesOld);
445 if (rcPsx == -1)
446 {
447 ASMAtomicXchgBool(&pThis->fMonThrdShutdown, true);
448 return RTErrConvertFromErrno(errno);
449 }
450
451 while ( !pThis->fMonThrdShutdown
452 && RT_SUCCESS(rc))
453 {
454# ifdef RT_OS_LINUX
455 /*
456 * Wait for status line change.
457 *
458 * XXX In Linux, if a thread calls tcsetattr while the monitor thread is
459 * waiting in ioctl for a modem status change then 8250.c wrongly disables
460 * modem irqs and so the monitor thread never gets released. The workaround
461 * is to send a signal after each tcsetattr.
462 *
463 * TIOCMIWAIT doesn't work for the DSR line with TIOCM_DSR set
464 * (see http://lxr.linux.no/#linux+v4.7/drivers/usb/class/cdc-acm.c#L949)
465 * However as it is possible to query the line state we will not just clear
466 * the TIOCM_DSR bit from the lines to check but resort to the polling
467 * approach just like on other hosts.
468 */
469 if (!fPoll)
470 {
471 rcPsx = ioctl(pThis->iFd, TIOCMIWAIT, fStsLinesChk);
472 if (!rcPsx)
473 { /** @todo: Notify any event waiter. */ }
474 else if (rcPsx == -1 && errno != EINTR)
475 fPoll = true;
476 }
477 else
478#endif
479 {
480 uint32_t fStsLines = 0;
481 rcPsx = ioctl(pThis->iFd, TIOCMGET, &fStsLines);
482 if (!rcPsx)
483 {
484 cStsLineGetErrors = 0; /* Reset the error counter once we had one successful query. */
485
486 if (((fStsLines ^ fStsLinesOld) & fStsLinesChk))
487 {
488 /** @todo: Notify any event waiter. */
489 fStsLinesOld = fStsLines;
490 }
491 else /* No change, sleep for a bit. */
492 RTThreadSleep(100 /*ms*/);
493 }
494 else if (rcPsx == -1 && errno != EINTR)
495 {
496 /*
497 * If querying the status line fails too often we have to shut down the
498 * thread and notify the user of the serial port.
499 */
500 if (cStsLineGetErrors++ >= 10)
501 { /** @todo: Send error notification */ }
502
503 RTThreadSleep(100 /*ms*/);
504 }
505 }
506 }
507
508 ASMAtomicXchgBool(&pThis->fMonThrdShutdown, true);
509 return rc;
510}
511
512
513/**
514 * Creates the status line monitoring thread.
515 *
516 * @returns IPRT status code.
517 * @param pThis The internal serial port instance data.
518 */
519static int rtSerialPortMonitorThreadCreate(PRTSERIALPORTINTERNAL pThis)
520{
521 pThis->fMonThrdShutdown = false;
522 int rc = RTThreadCreate(&pThis->hMonThrd, rtSerialPortStsLineMonitorThrd, pThis, 0 /*cbStack*/,
523 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "IPRT-SerPortMon");
524 if (RT_SUCCESS(rc))
525 {
526 /* Wait for the thread to start up. */
527 rc = RTThreadUserWait(pThis->hMonThrd, 20*RT_MS_1SEC);
528 if ( rc == VERR_TIMEOUT
529 || pThis->fMonThrdShutdown)
530 {
531 /* Startup failed, try to reap the thread. */
532 int rcThrd;
533 rc = RTThreadWait(pThis->hMonThrd, 20*RT_MS_1SEC, &rcThrd);
534 if (RT_SUCCESS(rc))
535 rc = rcThrd;
536 else
537 rc = VERR_INTERNAL_ERROR;
538 /* The thread is lost otherwise. */
539 }
540 }
541
542 return rc;
543}
544
545
546/**
547 * Shuts down the status line monitor thread.
548 *
549 * @returns nothing.
550 * @param pThis The internal serial port instance data.
551 */
552static void rtSerialPortMonitorThreadShutdown(PRTSERIALPORTINTERNAL pThis)
553{
554 bool fShutDown = ASMAtomicXchgBool(&pThis->fMonThrdShutdown, true);
555 if (!fShutDown)
556 {
557 int rc = RTThreadPoke(pThis->hMonThrd);
558 AssertRC(rc);
559 }
560
561 int rcThrd = VINF_SUCCESS;
562 int rc = RTThreadWait(pThis->hMonThrd, 20*RT_MS_1SEC, &rcThrd);
563 AssertRC(rc);
564 AssertRC(rcThrd);
565}
566
567
568RTDECL(int) RTSerialPortOpen(PRTSERIALPORT phSerialPort, const char *pszPortAddress, uint32_t fFlags)
569{
570 AssertPtrReturn(phSerialPort, VERR_INVALID_POINTER);
571 AssertReturn(VALID_PTR(pszPortAddress) && *pszPortAddress != '\0', VERR_INVALID_PARAMETER);
572 AssertReturn(!(fFlags & ~RT_SERIALPORT_OPEN_F_VALID_MASK), VERR_INVALID_PARAMETER);
573 AssertReturn((fFlags & RT_SERIALPORT_OPEN_F_READ) || (fFlags & RT_SERIALPORT_OPEN_F_WRITE),
574 VERR_INVALID_PARAMETER);
575
576 int rc = VINF_SUCCESS;
577 PRTSERIALPORTINTERNAL pThis = (PRTSERIALPORTINTERNAL)RTMemAllocZ(sizeof(*pThis));
578 if (pThis)
579 {
580 int fPsxFlags = O_NOCTTY;
581
582 if ((fFlags & RT_SERIALPORT_OPEN_F_READ) && !(fFlags & RT_SERIALPORT_OPEN_F_WRITE))
583 fPsxFlags |= O_RDONLY;
584 else if (!(fFlags & RT_SERIALPORT_OPEN_F_READ) && (fFlags & RT_SERIALPORT_OPEN_F_WRITE))
585 fPsxFlags |= O_WRONLY;
586 else
587 fPsxFlags |= O_RDWR;
588
589 pThis->fOpenFlags = fFlags;
590 pThis->iFd = open(pszPortAddress, fPsxFlags);
591 if (pThis->iFd != -1)
592 {
593 rc = rtSerialPortSetDefaultCfg(pThis);
594 if ( RT_SUCCESS(rc)
595 && (fFlags & RT_SERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING))
596 rc = rtSerialPortMonitorThreadCreate(pThis);
597
598 if (RT_SUCCESS(rc))
599 {
600 *phSerialPort = pThis;
601 return VINF_SUCCESS;
602 }
603
604 close(pThis->iFd);
605 }
606 else
607 rc = RTErrConvertFromErrno(errno);
608
609 RTMemFree(pThis);
610 }
611 else
612 rc = VERR_NO_MEMORY;
613
614 return rc;
615}
616
617
618RTDECL(int) RTSerialPortClose(RTSERIALPORT hSerialPort)
619{
620 PRTSERIALPORTINTERNAL pThis = hSerialPort;
621 if (pThis == NIL_RTSERIALPORT)
622 return VINF_SUCCESS;
623 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
624 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
625
626 /*
627 * Do the cleanup.
628 */
629 AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSERIALPORT_MAGIC_DEAD, RTSERIALPORT_MAGIC), VERR_INVALID_HANDLE);
630
631 if (pThis->fOpenFlags & RT_SERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING)
632 rtSerialPortMonitorThreadShutdown(pThis);
633
634 close(pThis->iFd);
635 RTMemFree(pThis);
636 return VINF_SUCCESS;
637}
638
639
640RTDECL(RTHCINTPTR) RTSerialPortToNative(RTSERIALPORT hSerialPort)
641{
642 PRTSERIALPORTINTERNAL pThis = hSerialPort;
643 AssertPtrReturn(pThis, -1);
644 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, -1);
645
646 return pThis->iFd;
647}
648
649
650RTDECL(int) RTSerialPortRead(RTSERIALPORT hSerialPort, void *pvBuf, size_t cbToRead, size_t *pcbRead)
651{
652 RT_NOREF(hSerialPort, pvBuf, cbToRead, pcbRead);
653 return VERR_NOT_IMPLEMENTED;
654}
655
656
657RTDECL(int) RTSerialPortReadNB(RTSERIALPORT hSerialPort, void *pvBuf, size_t cbToRead, size_t *pcbRead)
658{
659 PRTSERIALPORTINTERNAL pThis = hSerialPort;
660 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
661 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
662 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
663 AssertReturn(cbToRead > 0, VERR_INVALID_PARAMETER);
664 AssertPtrReturn(pcbRead, VERR_INVALID_POINTER);
665
666 *pcbRead = 0;
667
668 int rc = VINF_SUCCESS;
669 ssize_t cbThisRead = read(pThis->iFd, pvBuf, cbToRead);
670 if (cbThisRead > 0)
671 {
672 /*
673 * The read data needs to be scanned for the BREAK condition marker encoded in the data stream,
674 * if break detection was enabled during open.
675 */
676 if (pThis->fOpenFlags & RT_SERIALPORT_OPEN_F_DETECT_BREAK_CONDITION)
677 { /** @todo */ }
678
679 *pcbRead = cbThisRead;
680 }
681 else if (cbThisRead == 0 || errno == EAGAIN || errno == EWOULDBLOCK)
682 rc = VINF_TRY_AGAIN;
683 else
684 rc = RTErrConvertFromErrno(errno);
685
686 return rc;
687}
688
689
690RTDECL(int) RTSerialPortWrite(RTSERIALPORT hSerialPort, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
691{
692 RT_NOREF(hSerialPort, pvBuf, cbToWrite, pcbWritten);
693 return VERR_NOT_IMPLEMENTED;
694}
695
696
697RTDECL(int) RTSerialPortWriteNB(RTSERIALPORT hSerialPort, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
698{
699 PRTSERIALPORTINTERNAL pThis = hSerialPort;
700 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
701 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
702 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
703 AssertReturn(cbToWrite > 0, VERR_INVALID_PARAMETER);
704 AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
705
706 *pcbWritten = 0;
707
708 int rc = VINF_SUCCESS;
709 ssize_t cbThisWrite = write(pThis->iFd, pvBuf, cbToWrite);
710 if (cbThisWrite > 0)
711 *pcbWritten = cbThisWrite;
712 else if (cbThisWrite == 0 || errno == EAGAIN || errno == EWOULDBLOCK)
713 rc = VINF_TRY_AGAIN;
714 else
715 rc = RTErrConvertFromErrno(errno);
716
717 return rc;
718}
719
720
721RTDECL(int) RTSerialPortCfgQueryCurrent(RTSERIALPORT hSerialPort, PRTSERIALPORTCFG pCfg)
722{
723 PRTSERIALPORTINTERNAL pThis = hSerialPort;
724 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
725 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
726
727 return rtSerialPortTermios2Cfg(&pThis->PortCfg, pCfg);
728}
729
730
731RTDECL(int) RTSerialPortCfgSet(RTSERIALPORT hSerialPort, PCRTSERIALPORTCFG pCfg, PRTERRINFO pErrInfo)
732{
733 PRTSERIALPORTINTERNAL pThis = hSerialPort;
734 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
735 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
736
737 struct termios PortCfgNew; RT_ZERO(PortCfgNew);
738 int rc = rtSerialPortCfg2Termios(pCfg, &PortCfgNew, pErrInfo);
739 if (RT_SUCCESS(rc))
740 {
741 int rcPsx = tcsetattr(pThis->iFd, TCSANOW, &PortCfgNew);
742 if (rcPsx == -1)
743 rc = RTErrConvertFromErrno(errno);
744 else
745 memcpy(&pThis->PortCfg, &PortCfgNew, sizeof(struct termios));
746
747#ifdef RT_OS_LINUX
748 /*
749 * XXX In Linux, if a thread calls tcsetattr while the monitor thread is
750 * waiting in ioctl for a modem status change then 8250.c wrongly disables
751 * modem irqs and so the monitor thread never gets released. The workaround
752 * is to send a signal after each tcsetattr.
753 */
754 if (pThis->fOpenFlags & RT_SERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING)
755 RTThreadPoke(pThis->hMonThrd);
756#endif
757 }
758
759 return rc;
760}
761
762
763RTDECL(int) RTSerialPortEvtPoll(RTSERIALPORT hSerialPort, RTSERIALPORTEVT *penmEvt, RTMSINTERVAL msTimeout)
764{
765 RT_NOREF(hSerialPort, penmEvt, msTimeout);
766 return VERR_NOT_IMPLEMENTED;
767}
768
769
770RTDECL(int) RTSerialPortEvtPollInterrupt(RTSERIALPORT hSerialPort)
771{
772 RT_NOREF(hSerialPort);
773 return VERR_NOT_IMPLEMENTED;
774}
775
776
777RTDECL(int) RTSerialPortChgBreakCondition(RTSERIALPORT hSerialPort, bool fSet)
778{
779 PRTSERIALPORTINTERNAL pThis = hSerialPort;
780 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
781 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
782
783 int rc = VINF_SUCCESS;
784 int rcPsx = ioctl(pThis->iFd, fSet ? TIOCSBRK : TIOCCBRK);
785 if (rcPsx == -1)
786 rc = RTErrConvertFromErrno(errno);
787
788 return rc;
789}
790
791
792RTDECL(int) RTSerialPortChgStatusLines(RTSERIALPORT hSerialPort, uint32_t fClear, uint32_t fSet)
793{
794 PRTSERIALPORTINTERNAL pThis = hSerialPort;
795 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
796 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
797
798 int rc = VINF_SUCCESS;
799 int fTiocmSet = 0;
800 int fTiocmClear = 0;
801
802 if (fClear & RT_SERIALPORT_CHG_STS_LINES_F_RTS)
803 fTiocmClear |= TIOCM_RTS;
804 if (fClear & RT_SERIALPORT_CHG_STS_LINES_F_DTR)
805 fTiocmClear |= TIOCM_DTR;
806
807 if (fSet & RT_SERIALPORT_CHG_STS_LINES_F_RTS)
808 fTiocmSet |= TIOCM_RTS;
809 if (fSet & RT_SERIALPORT_CHG_STS_LINES_F_DTR)
810 fTiocmSet |= TIOCM_DTR;
811
812 int rcPsx = ioctl(pThis->iFd, TIOCMBIS, &fTiocmSet);
813 if (!rcPsx)
814 {
815 rcPsx = ioctl(pThis->iFd, TIOCMBIC, &fTiocmClear);
816 if (rcPsx == -1)
817 rc = RTErrConvertFromErrno(errno);
818 }
819 return rc;
820}
821
822
823RTDECL(int) RTSerialPortQueryStatusLines(RTSERIALPORT hSerialPort, uint32_t *pfStsLines)
824{
825 PRTSERIALPORTINTERNAL pThis = hSerialPort;
826 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
827 AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
828 AssertPtrReturn(pfStsLines, VERR_INVALID_POINTER);
829
830 *pfStsLines = 0;
831
832 int rc = VINF_SUCCESS;
833 int fStsLines = 0;
834 int rcPsx = ioctl(pThis->iFd, TIOCMGET, &fStsLines);
835 if (!rcPsx)
836 {
837 *pfStsLines |= (fStsLines & TIOCM_CAR) ? RT_SERIALPORT_STS_LINE_DCD : 0;
838 *pfStsLines |= (fStsLines & TIOCM_RNG) ? RT_SERIALPORT_STS_LINE_RI : 0;
839 *pfStsLines |= (fStsLines & TIOCM_DSR) ? RT_SERIALPORT_STS_LINE_DSR : 0;
840 *pfStsLines |= (fStsLines & TIOCM_CTS) ? RT_SERIALPORT_STS_LINE_CTS : 0;
841 }
842 else
843 rc = RTErrConvertFromErrno(errno);
844
845 return rc;
846}
847
848
849
850
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette