VirtualBox

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

最後變更 在這個檔案從69908是 69908,由 vboxsync 提交於 7 年 前

Build fix

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

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