VirtualBox

source: vbox/trunk/src/VBox/Devices/Serial/DrvHostSerial.cpp@ 21094

Last change on this file since 21094 was 19809, checked in by vboxsync, 16 years ago

Serial/DrvHostSerial/Solaris: ignore IEXTEN characters in line mode (public #3120).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 49.5 KB
Line 
1/* $Id: DrvHostSerial.cpp 19809 2009-05-19 09:32:03Z vboxsync $ */
2/** @file
3 * VBox stream I/O devices: Host serial driver
4 *
5 * Contributed by: Alexander Eichner
6 */
7
8/*
9 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
20 * Clara, CA 95054 USA or visit http://www.sun.com if you need
21 * additional information or have any questions.
22 */
23
24
25
26/*******************************************************************************
27* Header Files *
28*******************************************************************************/
29#define LOG_GROUP LOG_GROUP_DRV_HOST_SERIAL
30#include <VBox/pdm.h>
31#include <VBox/err.h>
32
33#include <VBox/log.h>
34#include <iprt/asm.h>
35#include <iprt/assert.h>
36#include <iprt/semaphore.h>
37#include <iprt/file.h>
38#include <iprt/alloc.h>
39
40#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
41# include <errno.h>
42# ifdef RT_OS_SOLARIS
43# include <sys/termios.h>
44# else
45# include <termios.h>
46# endif
47# include <sys/types.h>
48# include <fcntl.h>
49# include <string.h>
50# include <unistd.h>
51# ifdef RT_OS_DARWIN
52# include <sys/select.h>
53# else
54# include <sys/poll.h>
55# endif
56# include <sys/ioctl.h>
57# include <pthread.h>
58
59# ifdef RT_OS_LINUX
60/*
61 * TIOCM_LOOP is not defined in the above header files for some reason but in asm/termios.h.
62 * But inclusion of this file however leads to compilation errors because of redefinition of some
63 * structs. Thatswhy it is defined here until a better solution is found.
64 */
65# ifndef TIOCM_LOOP
66# define TIOCM_LOOP 0x8000
67# endif
68# endif /* linux */
69
70#elif defined(RT_OS_WINDOWS)
71# include <Windows.h>
72#endif
73
74#include "../Builtins.h"
75
76
77/** Size of the send fifo queue (in bytes) */
78#ifdef RT_OS_DARWIN
79 /** @todo This is really desperate, but it seriously looks like
80 * the data is arriving way too fast for us to push over. 9600
81 * baud and zoc reports sending at 12000+ chars/sec... */
82# define CHAR_MAX_SEND_QUEUE 0x400
83# define CHAR_MAX_SEND_QUEUE_MASK 0x3ff
84#else
85# define CHAR_MAX_SEND_QUEUE 0x80
86# define CHAR_MAX_SEND_QUEUE_MASK 0x7f
87#endif
88
89/*******************************************************************************
90* Structures and Typedefs *
91*******************************************************************************/
92
93/**
94 * Char driver instance data.
95 */
96typedef struct DRVHOSTSERIAL
97{
98 /** Pointer to the driver instance structure. */
99 PPDMDRVINS pDrvIns;
100 /** Pointer to the char port interface of the driver/device above us. */
101 PPDMICHARPORT pDrvCharPort;
102 /** Our char interface. */
103 PDMICHAR IChar;
104 /** Receive thread. */
105 PPDMTHREAD pRecvThread;
106 /** Send thread. */
107 PPDMTHREAD pSendThread;
108 /** Status lines monitor thread. */
109 PPDMTHREAD pMonitorThread;
110 /** Send event semephore */
111 RTSEMEVENT SendSem;
112
113 /** the device path */
114 char *pszDevicePath;
115
116#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
117 /** the device handle */
118 RTFILE DeviceFile;
119# ifdef RT_OS_DARWIN
120 /** The device handle used for reading.
121 * Used to prevent the read selecto from blocking the writes. */
122 RTFILE DeviceFileR;
123# endif
124 /** The read end of the control pipe */
125 RTFILE WakeupPipeR;
126 /** The write end of the control pipe */
127 RTFILE WakeupPipeW;
128# ifndef RT_OS_LINUX
129 /** The current line status.
130 * Used by the polling version of drvHostSerialMonitorThread. */
131 int fStatusLines;
132# endif
133#elif defined(RT_OS_WINDOWS)
134 /** the device handle */
135 HANDLE hDeviceFile;
136 /** The event semaphore for waking up the receive thread */
137 HANDLE hHaltEventSem;
138 /** The event semaphore for overlapped receiving */
139 HANDLE hEventRecv;
140 /** For overlapped receiving */
141 OVERLAPPED overlappedRecv;
142 /** The event semaphore for overlapped sending */
143 HANDLE hEventSend;
144 /** For overlapped sending */
145 OVERLAPPED overlappedSend;
146#endif
147
148 /** Internal send FIFO queue */
149 uint8_t volatile aSendQueue[CHAR_MAX_SEND_QUEUE];
150 uint32_t volatile iSendQueueHead;
151 uint32_t volatile iSendQueueTail;
152
153 /** Read/write statistics */
154 STAMCOUNTER StatBytesRead;
155 STAMCOUNTER StatBytesWritten;
156#ifdef RT_OS_DARWIN
157 /** The number of bytes we've dropped because the send queue
158 * was full. */
159 STAMCOUNTER StatSendOverflows;
160#endif
161} DRVHOSTSERIAL, *PDRVHOSTSERIAL;
162
163
164/** Converts a pointer to DRVCHAR::IChar to a PDRVHOSTSERIAL. */
165#define PDMICHAR_2_DRVHOSTSERIAL(pInterface) ( (PDRVHOSTSERIAL)((uintptr_t)pInterface - RT_OFFSETOF(DRVHOSTSERIAL, IChar)) )
166
167
168/* -=-=-=-=- IBase -=-=-=-=- */
169
170/**
171 * Queries an interface to the driver.
172 *
173 * @returns Pointer to interface.
174 * @returns NULL if the interface was not supported by the driver.
175 * @param pInterface Pointer to this interface structure.
176 * @param enmInterface The requested interface identification.
177 */
178static DECLCALLBACK(void *) drvHostSerialQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
179{
180 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
181 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
182 switch (enmInterface)
183 {
184 case PDMINTERFACE_BASE:
185 return &pDrvIns->IBase;
186 case PDMINTERFACE_CHAR:
187 return &pThis->IChar;
188 default:
189 return NULL;
190 }
191}
192
193
194/* -=-=-=-=- IChar -=-=-=-=- */
195
196/** @copydoc PDMICHAR::pfnWrite */
197static DECLCALLBACK(int) drvHostSerialWrite(PPDMICHAR pInterface, const void *pvBuf, size_t cbWrite)
198{
199 PDRVHOSTSERIAL pThis = PDMICHAR_2_DRVHOSTSERIAL(pInterface);
200 const uint8_t *pbBuffer = (const uint8_t *)pvBuf;
201
202 LogFlow(("%s: pvBuf=%#p cbWrite=%d\n", __FUNCTION__, pvBuf, cbWrite));
203
204 for (uint32_t i = 0; i < cbWrite; i++)
205 {
206#ifdef RT_OS_DARWIN /* don't wanna break the others here. */
207 uint32_t iHead = ASMAtomicUoReadU32(&pThis->iSendQueueHead);
208 uint32_t iTail = ASMAtomicUoReadU32(&pThis->iSendQueueTail);
209 int32_t cbAvail = iTail > iHead
210 ? iTail - iHead - 1
211 : CHAR_MAX_SEND_QUEUE - (iHead - iTail) - 1;
212 if (cbAvail <= 0)
213 {
214#ifdef DEBUG
215 uint64_t volatile u64Now = RTTimeNanoTS(); NOREF(u64Now);
216#endif
217 Log(("%s: dropping %d chars (cbAvail=%d iHead=%d iTail=%d)\n", __FUNCTION__, cbWrite - i , cbAvail, iHead, iTail));
218 STAM_COUNTER_INC(&pThis->StatSendOverflows);
219 break;
220 }
221
222 pThis->aSendQueue[iHead] = pbBuffer[i];
223 STAM_COUNTER_INC(&pThis->StatBytesWritten);
224 ASMAtomicWriteU32(&pThis->iSendQueueHead, (iHead + 1) & CHAR_MAX_SEND_QUEUE_MASK);
225 if (cbAvail < CHAR_MAX_SEND_QUEUE / 4)
226 {
227 RTSemEventSignal(pThis->SendSem);
228 RTThreadYield();
229 }
230#else /* old code */
231 uint32_t idx = ASMAtomicUoReadU32(&pThis->iSendQueueHead);
232
233 pThis->aSendQueue[idx] = pbBuffer[i];
234 idx = (idx + 1) & CHAR_MAX_SEND_QUEUE_MASK;
235
236 STAM_COUNTER_INC(&pThis->StatBytesWritten);
237 ASMAtomicWriteU32(&pThis->iSendQueueHead, idx);
238#endif /* old code */
239 }
240 RTSemEventSignal(pThis->SendSem);
241 return VINF_SUCCESS;
242}
243
244static DECLCALLBACK(int) drvHostSerialSetParameters(PPDMICHAR pInterface, unsigned Bps, char chParity, unsigned cDataBits, unsigned cStopBits)
245{
246 PDRVHOSTSERIAL pThis = PDMICHAR_2_DRVHOSTSERIAL(pInterface);
247#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
248 struct termios *termiosSetup;
249 int baud_rate;
250#elif defined(RT_OS_WINDOWS)
251 LPDCB comSetup;
252#endif
253
254 LogFlow(("%s: Bps=%u chParity=%c cDataBits=%u cStopBits=%u\n", __FUNCTION__, Bps, chParity, cDataBits, cStopBits));
255
256#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
257 termiosSetup = (struct termios *)RTMemTmpAllocZ(sizeof(struct termios));
258
259 /* Enable receiver */
260 termiosSetup->c_cflag |= (CLOCAL | CREAD);
261
262 switch (Bps) {
263 case 50:
264 baud_rate = B50;
265 break;
266 case 75:
267 baud_rate = B75;
268 break;
269 case 110:
270 baud_rate = B110;
271 break;
272 case 134:
273 baud_rate = B134;
274 break;
275 case 150:
276 baud_rate = B150;
277 break;
278 case 200:
279 baud_rate = B200;
280 break;
281 case 300:
282 baud_rate = B300;
283 break;
284 case 600:
285 baud_rate = B600;
286 break;
287 case 1200:
288 baud_rate = B1200;
289 break;
290 case 1800:
291 baud_rate = B1800;
292 break;
293 case 2400:
294 baud_rate = B2400;
295 break;
296 case 4800:
297 baud_rate = B4800;
298 break;
299 case 9600:
300 baud_rate = B9600;
301 break;
302 case 19200:
303 baud_rate = B19200;
304 break;
305 case 38400:
306 baud_rate = B38400;
307 break;
308 case 57600:
309 baud_rate = B57600;
310 break;
311 case 115200:
312 baud_rate = B115200;
313 break;
314 default:
315 baud_rate = B9600;
316 }
317
318 cfsetispeed(termiosSetup, baud_rate);
319 cfsetospeed(termiosSetup, baud_rate);
320
321 switch (chParity) {
322 case 'E':
323 termiosSetup->c_cflag |= PARENB;
324 break;
325 case 'O':
326 termiosSetup->c_cflag |= (PARENB | PARODD);
327 break;
328 case 'N':
329 break;
330 default:
331 break;
332 }
333
334 switch (cDataBits) {
335 case 5:
336 termiosSetup->c_cflag |= CS5;
337 break;
338 case 6:
339 termiosSetup->c_cflag |= CS6;
340 break;
341 case 7:
342 termiosSetup->c_cflag |= CS7;
343 break;
344 case 8:
345 termiosSetup->c_cflag |= CS8;
346 break;
347 default:
348 break;
349 }
350
351 switch (cStopBits) {
352 case 2:
353 termiosSetup->c_cflag |= CSTOPB;
354 default:
355 break;
356 }
357
358 /* set serial port to raw input */
359 termiosSetup->c_lflag = ~(ICANON | ECHO | ECHOE | ISIG);
360#ifdef RT_OS_SOLARIS
361 /* turn off other extended special characters in line mode */
362 termiosSetup->c_lflag &= ~(IEXTEN);
363#endif
364
365 tcsetattr(pThis->DeviceFile, TCSANOW, termiosSetup);
366 RTMemTmpFree(termiosSetup);
367#elif defined(RT_OS_WINDOWS)
368 comSetup = (LPDCB)RTMemTmpAllocZ(sizeof(DCB));
369
370 comSetup->DCBlength = sizeof(DCB);
371
372 switch (Bps) {
373 case 110:
374 comSetup->BaudRate = CBR_110;
375 break;
376 case 300:
377 comSetup->BaudRate = CBR_300;
378 break;
379 case 600:
380 comSetup->BaudRate = CBR_600;
381 break;
382 case 1200:
383 comSetup->BaudRate = CBR_1200;
384 break;
385 case 2400:
386 comSetup->BaudRate = CBR_2400;
387 break;
388 case 4800:
389 comSetup->BaudRate = CBR_4800;
390 break;
391 case 9600:
392 comSetup->BaudRate = CBR_9600;
393 break;
394 case 14400:
395 comSetup->BaudRate = CBR_14400;
396 break;
397 case 19200:
398 comSetup->BaudRate = CBR_19200;
399 break;
400 case 38400:
401 comSetup->BaudRate = CBR_38400;
402 break;
403 case 57600:
404 comSetup->BaudRate = CBR_57600;
405 break;
406 case 115200:
407 comSetup->BaudRate = CBR_115200;
408 break;
409 default:
410 comSetup->BaudRate = CBR_9600;
411 }
412
413 comSetup->fBinary = TRUE;
414 comSetup->fOutxCtsFlow = FALSE;
415 comSetup->fOutxDsrFlow = FALSE;
416 comSetup->fDtrControl = DTR_CONTROL_DISABLE;
417 comSetup->fDsrSensitivity = FALSE;
418 comSetup->fTXContinueOnXoff = TRUE;
419 comSetup->fOutX = FALSE;
420 comSetup->fInX = FALSE;
421 comSetup->fErrorChar = FALSE;
422 comSetup->fNull = FALSE;
423 comSetup->fRtsControl = RTS_CONTROL_DISABLE;
424 comSetup->fAbortOnError = FALSE;
425 comSetup->wReserved = 0;
426 comSetup->XonLim = 5;
427 comSetup->XoffLim = 5;
428 comSetup->ByteSize = cDataBits;
429
430 switch (chParity) {
431 case 'E':
432 comSetup->Parity = EVENPARITY;
433 break;
434 case 'O':
435 comSetup->Parity = ODDPARITY;
436 break;
437 case 'N':
438 comSetup->Parity = NOPARITY;
439 break;
440 default:
441 break;
442 }
443
444 switch (cStopBits) {
445 case 1:
446 comSetup->StopBits = ONESTOPBIT;
447 break;
448 case 2:
449 comSetup->StopBits = TWOSTOPBITS;
450 break;
451 default:
452 break;
453 }
454
455 comSetup->XonChar = 0;
456 comSetup->XoffChar = 0;
457 comSetup->ErrorChar = 0;
458 comSetup->EofChar = 0;
459 comSetup->EvtChar = 0;
460
461 SetCommState(pThis->hDeviceFile, comSetup);
462 RTMemTmpFree(comSetup);
463#endif /* RT_OS_WINDOWS */
464
465 return VINF_SUCCESS;
466}
467
468/* -=-=-=-=- receive thread -=-=-=-=- */
469
470/**
471 * Send thread loop.
472 *
473 * @returns VINF_SUCCESS.
474 * @param ThreadSelf Thread handle to this thread.
475 * @param pvUser User argument.
476 */
477static DECLCALLBACK(int) drvHostSerialSendThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
478{
479 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
480
481 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
482 return VINF_SUCCESS;
483
484#ifdef RT_OS_WINDOWS
485 /* Make sure that the halt event semaphore is reset. */
486 DWORD dwRet = WaitForSingleObject(pThis->hHaltEventSem, 0);
487
488 HANDLE haWait[2];
489 haWait[0] = pThis->hEventSend;
490 haWait[1] = pThis->hHaltEventSem;
491#endif
492
493 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
494 {
495 int rc = RTSemEventWait(pThis->SendSem, RT_INDEFINITE_WAIT);
496 if (RT_FAILURE(rc))
497 break;
498
499 /*
500 * Write the character to the host device.
501 */
502#ifdef RT_OS_DARWIN
503 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
504 {
505 /* copy the send queue so we get a linear buffer with the maximal size. */
506 uint8_t abBuf[sizeof(pThis->aSendQueue)];
507 uint32_t cb = 0;
508 uint32_t iTail = ASMAtomicUoReadU32(&pThis->iSendQueueTail);
509 uint32_t iHead = ASMAtomicUoReadU32(&pThis->iSendQueueHead);
510 if (iTail == iHead)
511 break;
512 do
513 {
514 abBuf[cb++] = pThis->aSendQueue[iTail];
515 iTail = (iTail + 1) & CHAR_MAX_SEND_QUEUE_MASK;
516 } while (iTail != iHead);
517
518 ASMAtomicWriteU32(&pThis->iSendQueueTail, iTail);
519
520 /* write it. */
521#ifdef DEBUG
522 uint64_t volatile u64Now = RTTimeNanoTS(); NOREF(u64Now);
523#endif
524#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
525
526 size_t cbWritten;
527 rc = RTFileWrite(pThis->DeviceFile, abBuf, cb, &cbWritten);
528 if (rc == VERR_TRY_AGAIN)
529 cbWritten = 0;
530 if (cbWritten < cb && (RT_SUCCESS(rc) || rc == VERR_TRY_AGAIN))
531 {
532 /* ok, block till the device is ready for more (O_NONBLOCK) effect. */
533 rc = VINF_SUCCESS;
534 uint8_t const *pbSrc = &abBuf[0];
535 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
536 {
537 /* advance */
538 cb -= cbWritten;
539 pbSrc += cbWritten;
540
541 /* wait */
542 fd_set WrSet;
543 FD_ZERO(&WrSet);
544 FD_SET(pThis->DeviceFile, &WrSet);
545 fd_set XcptSet;
546 FD_ZERO(&XcptSet);
547 FD_SET(pThis->DeviceFile, &XcptSet);
548 rc = select(pThis->DeviceFile + 1, NULL, &WrSet, &XcptSet, NULL);
549 /** @todo check rc? */
550
551 /* try write more */
552 rc = RTFileWrite(pThis->DeviceFile, pbSrc, cb, &cbWritten);
553 if (rc == VERR_TRY_AGAIN)
554 cbWritten = 0;
555 else if (RT_FAILURE(rc))
556 break;
557 else if (cbWritten >= cb)
558 break;
559 rc = VINF_SUCCESS;
560 } /* wait/write loop */
561 }
562
563#elif defined(RT_OS_WINDOWS)
564 /* perform an overlapped write operation. */
565 DWORD cbWritten;
566 memset(&pThis->overlappedSend, 0, sizeof(pThis->overlappedSend));
567 pThis->overlappedSend.hEvent = pThis->hEventSend;
568 if (!WriteFile(pThis->hDeviceFile, abBuf, cb, &cbWritten, &pThis->overlappedSend))
569 {
570 dwRet = GetLastError();
571 if (dwRet == ERROR_IO_PENDING)
572 {
573 /*
574 * write blocked, wait for completion or wakeup...
575 */
576 dwRet = WaitForMultipleObjects(2, haWait, FALSE, INFINITE);
577 if (dwRet != WAIT_OBJECT_0)
578 {
579 AssertMsg(pThread->enmState != PDMTHREADSTATE_RUNNING, ("The halt event sempahore is set but the thread is still in running state\n"));
580 break;
581 }
582 }
583 else
584 rc = RTErrConvertFromWin32(dwRet);
585 }
586
587#endif /* RT_OS_WINDOWS */
588 if (RT_FAILURE(rc))
589 {
590 LogRel(("HostSerial#%d: Serial Write failed with %Rrc; terminating send thread\n", pDrvIns->iInstance, rc));
591 return rc;
592 }
593 } /* write loop */
594
595#else /* old code */
596 uint32_t iTail;
597 while ( pThread->enmState == PDMTHREADSTATE_RUNNING
598 && (iTail = ASMAtomicUoReadU32(&pThis->iSendQueueTail)) != ASMAtomicUoReadU32(&pThis->iSendQueueHead))
599 {
600 /** @todo process more than one byte? */
601 unsigned cbProcessed = 1;
602 uint8_t abBuf[1];
603 abBuf[0] = pThis->aSendQueue[iTail];
604
605#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
606
607 rc = RTFileWrite(pThis->DeviceFile, abBuf, cbProcessed, NULL);
608
609#elif defined(RT_OS_WINDOWS)
610
611 DWORD cbBytesWritten;
612 memset(&pThis->overlappedSend, 0, sizeof(pThis->overlappedSend));
613 pThis->overlappedSend.hEvent = pThis->hEventSend;
614
615 if (!WriteFile(pThis->hDeviceFile, abBuf, cbProcessed, &cbBytesWritten, &pThis->overlappedSend))
616 {
617 dwRet = GetLastError();
618 if (dwRet == ERROR_IO_PENDING)
619 {
620 /*
621 * write blocked, wait ...
622 */
623 dwRet = WaitForMultipleObjects(2, haWait, FALSE, INFINITE);
624 if (dwRet != WAIT_OBJECT_0)
625 {
626 AssertMsg(pThread->enmState != PDMTHREADSTATE_RUNNING, ("The halt event sempahore is set but the thread is still in running state\n"));
627 break;
628 }
629 }
630 else
631 rc = RTErrConvertFromWin32(dwRet);
632 }
633
634#endif
635
636 if (RT_SUCCESS(rc))
637 {
638 Assert(cbProcessed == 1);
639 ASMAtomicWriteU32(&pThis->iSendQueueTail, (iTail + 1) & CHAR_MAX_SEND_QUEUE_MASK);
640 }
641 else if (RT_FAILURE(rc))
642 {
643 LogRel(("HostSerial#%d: Serial Write failed with %Rrc; terminating send thread\n", pDrvIns->iInstance, rc));
644 return rc;
645 }
646 }
647#endif /* old code */
648 }
649
650 return VINF_SUCCESS;
651}
652
653/**
654 * Unblock the send thread so it can respond to a state change.
655 *
656 * @returns a VBox status code.
657 * @param pDrvIns The driver instance.
658 * @param pThread The send thread.
659 */
660static DECLCALLBACK(int) drvHostSerialWakeupSendThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
661{
662 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
663 int rc;
664
665 rc = RTSemEventSignal(pThis->SendSem);
666 if (RT_FAILURE(rc))
667 return rc;
668
669#ifdef RT_OS_WINDOWS
670 if (!SetEvent(pThis->hHaltEventSem))
671 return RTErrConvertFromWin32(GetLastError());
672#endif
673
674 return VINF_SUCCESS;
675}
676
677/* -=-=-=-=- receive thread -=-=-=-=- */
678
679/**
680 * Receive thread loop.
681 *
682 * This thread pushes data from the host serial device up the driver
683 * chain toward the serial device.
684 *
685 * @returns VINF_SUCCESS.
686 * @param ThreadSelf Thread handle to this thread.
687 * @param pvUser User argument.
688 */
689static DECLCALLBACK(int) drvHostSerialRecvThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
690{
691 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
692 uint8_t abBuffer[256];
693 uint8_t *pbBuffer = NULL;
694 size_t cbRemaining = 0; /* start by reading host data */
695 int rc = VINF_SUCCESS;
696 int rcThread = VINF_SUCCESS;
697
698 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
699 return VINF_SUCCESS;
700
701#ifdef RT_OS_WINDOWS
702 /* Make sure that the halt event semaphore is reset. */
703 DWORD dwRet = WaitForSingleObject(pThis->hHaltEventSem, 0);
704
705 HANDLE ahWait[2];
706 ahWait[0] = pThis->hEventRecv;
707 ahWait[1] = pThis->hHaltEventSem;
708#endif
709
710 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
711 {
712 if (!cbRemaining)
713 {
714 /* Get a block of data from the host serial device. */
715
716#if defined(RT_OS_DARWIN) /* poll is broken on x86 darwin, returns POLLNVAL. */
717 fd_set RdSet;
718 FD_ZERO(&RdSet);
719 FD_SET(pThis->DeviceFileR, &RdSet);
720 FD_SET(pThis->WakeupPipeR, &RdSet);
721 fd_set XcptSet;
722 FD_ZERO(&XcptSet);
723 FD_SET(pThis->DeviceFileR, &XcptSet);
724 FD_SET(pThis->WakeupPipeR, &XcptSet);
725# if 1 /* it seems like this select is blocking the write... */
726 rc = select(RT_MAX(pThis->WakeupPipeR, pThis->DeviceFileR) + 1, &RdSet, NULL, &XcptSet, NULL);
727# else
728 struct timeval tv = { 0, 1000 };
729 rc = select(RT_MAX(pThis->WakeupPipeR, pThis->DeviceFileR) + 1, &RdSet, NULL, &XcptSet, &tv);
730# endif
731 if (rc == -1)
732 {
733 int err = errno;
734 rcThread = RTErrConvertFromErrno(err);
735 LogRel(("HostSerial#%d: select failed with errno=%d / %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, err, rcThread));
736 break;
737 }
738
739 /* this might have changed in the meantime */
740 if (pThread->enmState != PDMTHREADSTATE_RUNNING)
741 break;
742 if (rc == 0)
743 continue;
744
745 /* drain the wakeup pipe */
746 size_t cbRead;
747 if ( FD_ISSET(pThis->WakeupPipeR, &RdSet)
748 || FD_ISSET(pThis->WakeupPipeR, &XcptSet))
749 {
750 rc = RTFileRead(pThis->WakeupPipeR, abBuffer, 1, &cbRead);
751 if (RT_FAILURE(rc))
752 {
753 LogRel(("HostSerial#%d: draining the wakekup pipe failed with %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, rc));
754 rcThread = rc;
755 break;
756 }
757 continue;
758 }
759
760 /* read data from the serial port. */
761 rc = RTFileRead(pThis->DeviceFileR, abBuffer, sizeof(abBuffer), &cbRead);
762 if (RT_FAILURE(rc))
763 {
764 LogRel(("HostSerial#%d: (1) Read failed with %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, rc));
765 rcThread = rc;
766 break;
767 }
768 cbRemaining = cbRead;
769
770#elif defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
771
772 size_t cbRead;
773 struct pollfd aFDs[2];
774 aFDs[0].fd = pThis->DeviceFile;
775 aFDs[0].events = POLLIN;
776 aFDs[0].revents = 0;
777 aFDs[1].fd = pThis->WakeupPipeR;
778 aFDs[1].events = POLLIN | POLLERR | POLLHUP;
779 aFDs[1].revents = 0;
780 rc = poll(aFDs, RT_ELEMENTS(aFDs), -1);
781 if (rc < 0)
782 {
783 int err = errno;
784 rcThread = RTErrConvertFromErrno(err);
785 LogRel(("HostSerial#%d: poll failed with errno=%d / %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, err, rcThread));
786 break;
787 }
788 /* this might have changed in the meantime */
789 if (pThread->enmState != PDMTHREADSTATE_RUNNING)
790 break;
791 if (rc > 0 && aFDs[1].revents)
792 {
793 if (aFDs[1].revents & (POLLHUP | POLLERR | POLLNVAL))
794 break;
795 /* notification to terminate -- drain the pipe */
796 RTFileRead(pThis->WakeupPipeR, &abBuffer, 1, &cbRead);
797 continue;
798 }
799 rc = RTFileRead(pThis->DeviceFile, abBuffer, sizeof(abBuffer), &cbRead);
800 if (RT_FAILURE(rc))
801 {
802 /* don't terminate worker thread when data unavailable */
803 if (rc == VERR_TRY_AGAIN)
804 continue;
805
806 LogRel(("HostSerial#%d: (2) Read failed with %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, rc));
807 rcThread = rc;
808 break;
809 }
810 cbRemaining = cbRead;
811
812#elif defined(RT_OS_WINDOWS)
813
814 DWORD dwEventMask = 0;
815 DWORD dwNumberOfBytesTransferred;
816
817 memset(&pThis->overlappedRecv, 0, sizeof(pThis->overlappedRecv));
818 pThis->overlappedRecv.hEvent = pThis->hEventRecv;
819
820 if (!WaitCommEvent(pThis->hDeviceFile, &dwEventMask, &pThis->overlappedRecv))
821 {
822 dwRet = GetLastError();
823 if (dwRet == ERROR_IO_PENDING)
824 {
825 dwRet = WaitForMultipleObjects(2, ahWait, FALSE, INFINITE);
826 if (dwRet != WAIT_OBJECT_0)
827 {
828 /* notification to terminate */
829 AssertMsg(pThread->enmState != PDMTHREADSTATE_RUNNING, ("The halt event sempahore is set but the thread is still in running state\n"));
830 break;
831 }
832 }
833 else
834 {
835 rcThread = RTErrConvertFromWin32(dwRet);
836 LogRel(("HostSerial#%d: Wait failed with error %Rrc; terminating the worker thread.\n", pDrvIns->iInstance, rcThread));
837 break;
838 }
839 }
840 /* this might have changed in the meantime */
841 if (pThread->enmState != PDMTHREADSTATE_RUNNING)
842 break;
843
844 /* Check the event */
845 if (dwEventMask & EV_RXCHAR)
846 {
847 if (!ReadFile(pThis->hDeviceFile, abBuffer, sizeof(abBuffer), &dwNumberOfBytesTransferred, &pThis->overlappedRecv))
848 {
849 rcThread = RTErrConvertFromWin32(GetLastError());
850 LogRel(("HostSerial#%d: Read failed with error %Rrc; terminating the worker thread.\n", pDrvIns->iInstance, rcThread));
851 break;
852 }
853 cbRemaining = dwNumberOfBytesTransferred;
854 }
855 else
856 {
857 /* The status lines have changed. Notify the device. */
858 DWORD dwNewStatusLinesState = 0;
859 uint32_t uNewStatusLinesState = 0;
860
861 /* Get the new state */
862 if (GetCommModemStatus(pThis->hDeviceFile, &dwNewStatusLinesState))
863 {
864 if (dwNewStatusLinesState & MS_RLSD_ON)
865 uNewStatusLinesState |= PDM_ICHAR_STATUS_LINES_DCD;
866 if (dwNewStatusLinesState & MS_RING_ON)
867 uNewStatusLinesState |= PDM_ICHAR_STATUS_LINES_RI;
868 if (dwNewStatusLinesState & MS_DSR_ON)
869 uNewStatusLinesState |= PDM_ICHAR_STATUS_LINES_DSR;
870 if (dwNewStatusLinesState & MS_CTS_ON)
871 uNewStatusLinesState |= PDM_ICHAR_STATUS_LINES_CTS;
872 rc = pThis->pDrvCharPort->pfnNotifyStatusLinesChanged(pThis->pDrvCharPort, uNewStatusLinesState);
873 if (RT_FAILURE(rc))
874 {
875 /* Notifying device failed, continue but log it */
876 LogRel(("HostSerial#%d: Notifying device failed with error %Rrc; continuing.\n", pDrvIns->iInstance, rc));
877 }
878 }
879 else
880 {
881 /* Getting new state failed, continue but log it */
882 LogRel(("HostSerial#%d: Getting status lines state failed with error %Rrc; continuing.\n", pDrvIns->iInstance, RTErrConvertFromWin32(GetLastError())));
883 }
884 }
885#endif
886
887 Log(("Read %d bytes.\n", cbRemaining));
888 pbBuffer = abBuffer;
889 }
890 else
891 {
892 /* Send data to the guest. */
893 size_t cbProcessed = cbRemaining;
894 rc = pThis->pDrvCharPort->pfnNotifyRead(pThis->pDrvCharPort, pbBuffer, &cbProcessed);
895 if (RT_SUCCESS(rc))
896 {
897 Assert(cbProcessed); Assert(cbProcessed <= cbRemaining);
898 pbBuffer += cbProcessed;
899 cbRemaining -= cbProcessed;
900 STAM_COUNTER_ADD(&pThis->StatBytesRead, cbProcessed);
901 }
902 else if (rc == VERR_TIMEOUT)
903 {
904 /* Normal case, just means that the guest didn't accept a new
905 * character before the timeout elapsed. Just retry. */
906 rc = VINF_SUCCESS;
907 }
908 else
909 {
910 LogRel(("HostSerial#%d: NotifyRead failed with %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, rc));
911 rcThread = rc;
912 break;
913 }
914 }
915 }
916
917 return rcThread;
918}
919
920/**
921 * Unblock the send thread so it can respond to a state change.
922 *
923 * @returns a VBox status code.
924 * @param pDrvIns The driver instance.
925 * @param pThread The send thread.
926 */
927static DECLCALLBACK(int) drvHostSerialWakeupRecvThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
928{
929 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
930#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
931 return RTFileWrite(pThis->WakeupPipeW, "", 1, NULL);
932#elif defined(RT_OS_WINDOWS)
933 if (!SetEvent(pThis->hHaltEventSem))
934 return RTErrConvertFromWin32(GetLastError());
935 return VINF_SUCCESS;
936#else
937# error adapt me!
938#endif
939}
940
941#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
942/* -=-=-=-=- Monitor thread -=-=-=-=- */
943
944/**
945 * Monitor thread loop.
946 *
947 * This thread monitors the status lines and notifies the device
948 * if they change.
949 *
950 * @returns VINF_SUCCESS.
951 * @param ThreadSelf Thread handle to this thread.
952 * @param pvUser User argument.
953 */
954static DECLCALLBACK(int) drvHostSerialMonitorThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
955{
956 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
957 int rc = VINF_SUCCESS;
958 unsigned uStatusLinesToCheck = 0;
959
960 uStatusLinesToCheck = TIOCM_CAR | TIOCM_RNG | TIOCM_LE | TIOCM_CTS;
961
962 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
963 return VINF_SUCCESS;
964
965 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
966 {
967 uint32_t newStatusLine = 0;
968 unsigned int statusLines;
969
970# ifdef RT_OS_LINUX
971 /*
972 * Wait for status line change.
973 */
974 rc = ioctl(pThis->DeviceFile, TIOCMIWAIT, uStatusLinesToCheck);
975 if (pThread->enmState != PDMTHREADSTATE_RUNNING)
976 break;
977 if (rc < 0)
978 {
979ioctl_error:
980 PDMDrvHlpVMSetRuntimeError(pDrvIns, 0 /*fFlags*/, "DrvHostSerialFail",
981 N_("Ioctl failed for serial host device '%s' (%Rrc). The device will not work properly"),
982 pThis->pszDevicePath, RTErrConvertFromErrno(errno));
983 break;
984 }
985
986 rc = ioctl(pThis->DeviceFile, TIOCMGET, &statusLines);
987 if (rc < 0)
988 goto ioctl_error;
989# else /* !RT_OS_LINUX */
990 /*
991 * Poll for the status line change.
992 */
993 rc = ioctl(pThis->DeviceFile, TIOCMGET, &statusLines);
994 if (rc < 0)
995 {
996 PDMDrvHlpVMSetRuntimeError(pDrvIns, 0 /*fFlags*/, "DrvHostSerialFail",
997 N_("Ioctl failed for serial host device '%s' (%Rrc). The device will not work properly"),
998 pThis->pszDevicePath, RTErrConvertFromErrno(errno));
999 break;
1000 }
1001 if (!((statusLines ^ pThis->fStatusLines) & uStatusLinesToCheck))
1002 {
1003 PDMR3ThreadSleep(pThread, 500); /* 0.5 sec */
1004 continue;
1005 }
1006 pThis->fStatusLines = statusLines;
1007# endif /* !RT_OS_LINUX */
1008
1009 if (statusLines & TIOCM_CAR)
1010 newStatusLine |= PDM_ICHAR_STATUS_LINES_DCD;
1011 if (statusLines & TIOCM_RNG)
1012 newStatusLine |= PDM_ICHAR_STATUS_LINES_RI;
1013 if (statusLines & TIOCM_LE)
1014 newStatusLine |= PDM_ICHAR_STATUS_LINES_DSR;
1015 if (statusLines & TIOCM_CTS)
1016 newStatusLine |= PDM_ICHAR_STATUS_LINES_CTS;
1017 rc = pThis->pDrvCharPort->pfnNotifyStatusLinesChanged(pThis->pDrvCharPort, newStatusLine);
1018 }
1019
1020 return VINF_SUCCESS;
1021}
1022
1023/**
1024 * Unblock the monitor thread so it can respond to a state change.
1025 * We need to execute this code exactly once during initialization.
1026 * But we don't want to block --- therefore this dedicated thread.
1027 *
1028 * @returns a VBox status code.
1029 * @param pDrvIns The driver instance.
1030 * @param pThread The send thread.
1031 */
1032static DECLCALLBACK(int) drvHostSerialWakeupMonitorThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
1033{
1034# ifdef RT_OS_LINUX
1035 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
1036 int rc = VINF_SUCCESS;
1037# if 0
1038 unsigned int uSerialLineFlags;
1039 unsigned int uSerialLineStatus;
1040 unsigned int uIoctl;
1041# endif
1042
1043 /*
1044 * Linux is a bit difficult as the thread is sleeping in an ioctl call.
1045 * So there is no way to have a wakeup pipe.
1046 *
1047 * 1. Thatswhy we set the serial device into loopback mode and change one of the
1048 * modem control bits.
1049 * This should make the ioctl call return.
1050 *
1051 * 2. We still got reports about long shutdown times. It may bepossible
1052 * that the loopback mode is not implemented on all devices.
1053 * The next possible solution is to close the device file to make the ioctl
1054 * return with EBADF and be able to suspend the thread.
1055 *
1056 * 3. The second approach doesn't work too, the ioctl doesn't return.
1057 * But it seems that the ioctl is interruptible (return code in errno is EINTR).
1058 */
1059
1060# if 0 /* Disabled because it does not work for all. */
1061 /* Get current status of control lines. */
1062 rc = ioctl(pThis->DeviceFile, TIOCMGET, &uSerialLineStatus);
1063 if (rc < 0)
1064 goto ioctl_error;
1065
1066 uSerialLineFlags = TIOCM_LOOP;
1067 rc = ioctl(pThis->DeviceFile, TIOCMBIS, &uSerialLineFlags);
1068 if (rc < 0)
1069 goto ioctl_error;
1070
1071 /*
1072 * Change current level on the RTS pin to make the ioctl call return in the
1073 * monitor thread.
1074 */
1075 uIoctl = (uSerialLineStatus & TIOCM_CTS) ? TIOCMBIC : TIOCMBIS;
1076 uSerialLineFlags = TIOCM_RTS;
1077
1078 rc = ioctl(pThis->DeviceFile, uIoctl, &uSerialLineFlags);
1079 if (rc < 0)
1080 goto ioctl_error;
1081
1082 /* Change RTS back to the previous level. */
1083 uIoctl = (uIoctl == TIOCMBIC) ? TIOCMBIS : TIOCMBIC;
1084
1085 rc = ioctl(pThis->DeviceFile, uIoctl, &uSerialLineFlags);
1086 if (rc < 0)
1087 goto ioctl_error;
1088
1089 /*
1090 * Set serial device into normal state.
1091 */
1092 uSerialLineFlags = TIOCM_LOOP;
1093 rc = ioctl(pThis->DeviceFile, TIOCMBIC, &uSerialLineFlags);
1094 if (rc >= 0)
1095 return VINF_SUCCESS;
1096
1097ioctl_error:
1098 PDMDrvHlpVMSetRuntimeError(pDrvIns, 0 /*fFlags*/, "DrvHostSerialFail",
1099 N_("Ioctl failed for serial host device '%s' (%Rrc). The device will not work properly"),
1100 pThis->pszDevicePath, RTErrConvertFromErrno(errno));
1101# endif
1102
1103# if 0
1104 /* Close file to make ioctl return. */
1105 RTFileClose(pData->DeviceFile);
1106 /* Open again to make use after suspend possible again. */
1107 rc = RTFileOpen(&pData->DeviceFile, pData->pszDevicePath, RTFILE_O_OPEN | RTFILE_O_READWRITE);
1108 AssertMsgRC(rc, ("Opening device file again failed rc=%Rrc\n", rc));
1109
1110 if (RT_FAILURE(rc))
1111 PDMDrvHlpVMSetRuntimeError(pDrvIns, false, "DrvHostSerialFail",
1112 N_("Opening failed for serial host device '%s' (%Rrc). The device will not work"),
1113 pData->pszDevicePath, rc);
1114# endif
1115
1116 rc = RTThreadPoke(pThread->Thread);
1117 if (RT_FAILURE(rc))
1118 PDMDrvHlpVMSetRuntimeError(pDrvIns, 0 /*fFlags*/, "DrvHostSerialFail",
1119 N_("Suspending serial monitor thread failed for serial device '%s' (%Rrc). The shutdown may take longer than expected"),
1120 pThis->pszDevicePath, RTErrConvertFromErrno(rc));
1121
1122# else /* !RT_OS_LINUX*/
1123
1124 /* In polling mode there is nobody to wake up (PDMThread will cancel the sleep). */
1125 NOREF(pDrvIns);
1126 NOREF(pThread);
1127
1128# endif /* RT_OS_LINUX */
1129
1130 return VINF_SUCCESS;
1131}
1132#endif /* RT_OS_LINUX || RT_OS_DARWIN || RT_OS_SOLARIS */
1133
1134/**
1135 * Set the modem lines.
1136 *
1137 * @returns VBox status code
1138 * @param pInterface Pointer to the interface structure.
1139 * @param RequestToSend Set to true if this control line should be made active.
1140 * @param DataTerminalReady Set to true if this control line should be made active.
1141 */
1142static DECLCALLBACK(int) drvHostSerialSetModemLines(PPDMICHAR pInterface, bool RequestToSend, bool DataTerminalReady)
1143{
1144 PDRVHOSTSERIAL pThis = PDMICHAR_2_DRVHOSTSERIAL(pInterface);
1145
1146#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
1147 int modemStateSet = 0;
1148 int modemStateClear = 0;
1149
1150 if (RequestToSend)
1151 modemStateSet |= TIOCM_RTS;
1152 else
1153 modemStateClear |= TIOCM_RTS;
1154
1155 if (DataTerminalReady)
1156 modemStateSet |= TIOCM_DTR;
1157 else
1158 modemStateClear |= TIOCM_DTR;
1159
1160 if (modemStateSet)
1161 ioctl(pThis->DeviceFile, TIOCMBIS, &modemStateSet);
1162
1163 if (modemStateClear)
1164 ioctl(pThis->DeviceFile, TIOCMBIC, &modemStateClear);
1165#elif defined(RT_OS_WINDOWS)
1166 if (RequestToSend)
1167 EscapeCommFunction(pThis->hDeviceFile, SETRTS);
1168 else
1169 EscapeCommFunction(pThis->hDeviceFile, CLRRTS);
1170
1171 if (DataTerminalReady)
1172 EscapeCommFunction(pThis->hDeviceFile, SETDTR);
1173 else
1174 EscapeCommFunction(pThis->hDeviceFile, CLRDTR);
1175#endif
1176
1177 return VINF_SUCCESS;
1178}
1179
1180/* -=-=-=-=- driver interface -=-=-=-=- */
1181
1182/**
1183 * Construct a char driver instance.
1184 *
1185 * @returns VBox status.
1186 * @param pDrvIns The driver instance data.
1187 * If the registration structure is needed,
1188 * pDrvIns->pDrvReg points to it.
1189 * @param pCfgHandle Configuration node handle for the driver. Use this to
1190 * obtain the configuration of the driver instance. It's
1191 * also found in pDrvIns->pCfgHandle as it's expected to
1192 * be used frequently in this function.
1193 */
1194static DECLCALLBACK(int) drvHostSerialConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
1195{
1196 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
1197 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
1198
1199 /*
1200 * Init basic data members and interfaces.
1201 */
1202#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
1203 pThis->DeviceFile = NIL_RTFILE;
1204# ifdef RT_OS_DARWIN
1205 pThis->DeviceFileR = NIL_RTFILE;
1206# endif
1207 pThis->WakeupPipeR = NIL_RTFILE;
1208 pThis->WakeupPipeW = NIL_RTFILE;
1209#endif
1210 /* IBase. */
1211 pDrvIns->IBase.pfnQueryInterface = drvHostSerialQueryInterface;
1212 /* IChar. */
1213 pThis->IChar.pfnWrite = drvHostSerialWrite;
1214 pThis->IChar.pfnSetParameters = drvHostSerialSetParameters;
1215 pThis->IChar.pfnSetModemLines = drvHostSerialSetModemLines;
1216
1217/** @todo Initialize all members with NIL values!! The destructor is ALWAYS called. */
1218
1219 /*
1220 * Query configuration.
1221 */
1222 /* Device */
1223 int rc = CFGMR3QueryStringAlloc(pCfgHandle, "DevicePath", &pThis->pszDevicePath);
1224 if (RT_FAILURE(rc))
1225 {
1226 AssertMsgFailed(("Configuration error: query for \"DevicePath\" string returned %Rra.\n", rc));
1227 return rc;
1228 }
1229
1230 /*
1231 * Open the device
1232 */
1233#ifdef RT_OS_WINDOWS
1234
1235 pThis->hHaltEventSem = CreateEvent(NULL, FALSE, FALSE, NULL);
1236 AssertReturn(pThis->hHaltEventSem != NULL, VERR_NO_MEMORY);
1237
1238 pThis->hEventRecv = CreateEvent(NULL, FALSE, FALSE, NULL);
1239 AssertReturn(pThis->hEventRecv != NULL, VERR_NO_MEMORY);
1240
1241 pThis->hEventSend = CreateEvent(NULL, FALSE, FALSE, NULL);
1242 AssertReturn(pThis->hEventSend != NULL, VERR_NO_MEMORY);
1243
1244 HANDLE hFile = CreateFile(pThis->pszDevicePath,
1245 GENERIC_READ | GENERIC_WRITE,
1246 0, // must be opened with exclusive access
1247 NULL, // no SECURITY_ATTRIBUTES structure
1248 OPEN_EXISTING, // must use OPEN_EXISTING
1249 FILE_FLAG_OVERLAPPED, // overlapped I/O
1250 NULL); // no template file
1251 if (hFile == INVALID_HANDLE_VALUE)
1252 rc = RTErrConvertFromWin32(GetLastError());
1253 else
1254 {
1255 pThis->hDeviceFile = hFile;
1256 /* for overlapped read */
1257 if (!SetCommMask(hFile, EV_RXCHAR | EV_CTS | EV_DSR | EV_RING | EV_RLSD))
1258 {
1259 LogRel(("HostSerial#%d: SetCommMask failed with error %d.\n", pDrvIns->iInstance, GetLastError()));
1260 return VERR_FILE_IO_ERROR;
1261 }
1262 rc = VINF_SUCCESS;
1263 }
1264
1265#else
1266
1267 rc = RTFileOpen(&pThis->DeviceFile, pThis->pszDevicePath, RTFILE_O_OPEN | RTFILE_O_READWRITE);
1268# ifdef RT_OS_DARWIN
1269 if (RT_SUCCESS(rc))
1270 rc = RTFileOpen(&pThis->DeviceFileR, pThis->pszDevicePath, RTFILE_O_OPEN | RTFILE_O_READ);
1271# endif
1272
1273
1274#endif
1275
1276 if (RT_FAILURE(rc))
1277 {
1278 AssertMsgFailed(("Could not open host device %s, rc=%Rrc\n", pThis->pszDevicePath, rc));
1279 switch (rc)
1280 {
1281 case VERR_ACCESS_DENIED:
1282 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1283#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
1284 N_("Cannot open host device '%s' for read/write access. Check the permissions "
1285 "of that device ('/bin/ls -l %s'): Most probably you need to be member "
1286 "of the device group. Make sure that you logout/login after changing "
1287 "the group settings of the current user"),
1288#else
1289 N_("Cannot open host device '%s' for read/write access. Check the permissions "
1290 "of that device"),
1291#endif
1292 pThis->pszDevicePath, pThis->pszDevicePath);
1293 default:
1294 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1295 N_("Failed to open host device '%s'"),
1296 pThis->pszDevicePath);
1297 }
1298 }
1299
1300 /* Set to non blocking I/O */
1301#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
1302
1303 fcntl(pThis->DeviceFile, F_SETFL, O_NONBLOCK);
1304# ifdef RT_OS_DARWIN
1305 fcntl(pThis->DeviceFileR, F_SETFL, O_NONBLOCK);
1306# endif
1307 int aFDs[2];
1308 if (pipe(aFDs) != 0)
1309 {
1310 int rc = RTErrConvertFromErrno(errno);
1311 AssertRC(rc);
1312 return rc;
1313 }
1314 pThis->WakeupPipeR = aFDs[0];
1315 pThis->WakeupPipeW = aFDs[1];
1316
1317#elif defined(RT_OS_WINDOWS)
1318
1319 /* Set the COMMTIMEOUTS to get non blocking I/O */
1320 COMMTIMEOUTS comTimeout;
1321
1322 comTimeout.ReadIntervalTimeout = MAXDWORD;
1323 comTimeout.ReadTotalTimeoutMultiplier = 0;
1324 comTimeout.ReadTotalTimeoutConstant = 0;
1325 comTimeout.WriteTotalTimeoutMultiplier = 0;
1326 comTimeout.WriteTotalTimeoutConstant = 0;
1327
1328 SetCommTimeouts(pThis->hDeviceFile, &comTimeout);
1329
1330#endif
1331
1332 /*
1333 * Get the ICharPort interface of the above driver/device.
1334 */
1335 pThis->pDrvCharPort = (PPDMICHARPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_CHAR_PORT);
1336 if (!pThis->pDrvCharPort)
1337 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("HostSerial#%d has no char port interface above"), pDrvIns->iInstance);
1338
1339 /*
1340 * Create the receive, send and monitor threads pluss the related send semaphore.
1341 */
1342 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->pRecvThread, pThis, drvHostSerialRecvThread, drvHostSerialWakeupRecvThread, 0, RTTHREADTYPE_IO, "SerRecv");
1343 if (RT_FAILURE(rc))
1344 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create receive thread"), pDrvIns->iInstance);
1345
1346 rc = RTSemEventCreate(&pThis->SendSem);
1347 AssertRC(rc);
1348
1349 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->pSendThread, pThis, drvHostSerialSendThread, drvHostSerialWakeupSendThread, 0, RTTHREADTYPE_IO, "SerSend");
1350 if (RT_FAILURE(rc))
1351 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create send thread"), pDrvIns->iInstance);
1352
1353#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
1354 /* Linux & darwin needs a separate thread which monitors the status lines. */
1355# ifndef RT_OS_LINUX
1356 ioctl(pThis->DeviceFile, TIOCMGET, &pThis->fStatusLines);
1357# endif
1358 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->pMonitorThread, pThis, drvHostSerialMonitorThread, drvHostSerialWakeupMonitorThread, 0, RTTHREADTYPE_IO, "SerMon");
1359 if (RT_FAILURE(rc))
1360 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create monitor thread"), pDrvIns->iInstance);
1361#endif
1362
1363 /*
1364 * Register release statistics.
1365 */
1366 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes written", "/Devices/HostSerial%d/Written", pDrvIns->iInstance);
1367 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes read", "/Devices/HostSerial%d/Read", pDrvIns->iInstance);
1368#ifdef RT_OS_DARWIN /* new Write code, not darwin specific. */
1369 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatSendOverflows, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes overflowed", "/Devices/HostSerial%d/SendOverflow", pDrvIns->iInstance);
1370#endif
1371
1372 return VINF_SUCCESS;
1373}
1374
1375
1376/**
1377 * Destruct a char driver instance.
1378 *
1379 * Most VM resources are freed by the VM. This callback is provided so that
1380 * any non-VM resources can be freed correctly.
1381 *
1382 * @param pDrvIns The driver instance data.
1383 */
1384static DECLCALLBACK(void) drvHostSerialDestruct(PPDMDRVINS pDrvIns)
1385{
1386 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
1387
1388 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
1389
1390 /* Empty the send queue */
1391 pThis->iSendQueueTail = pThis->iSendQueueHead = 0;
1392
1393 RTSemEventDestroy(pThis->SendSem);
1394 pThis->SendSem = NIL_RTSEMEVENT;
1395
1396#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
1397
1398 if (pThis->WakeupPipeW != NIL_RTFILE)
1399 {
1400 int rc = RTFileClose(pThis->WakeupPipeW);
1401 AssertRC(rc);
1402 pThis->WakeupPipeW = NIL_RTFILE;
1403 }
1404 if (pThis->WakeupPipeR != NIL_RTFILE)
1405 {
1406 int rc = RTFileClose(pThis->WakeupPipeR);
1407 AssertRC(rc);
1408 pThis->WakeupPipeR = NIL_RTFILE;
1409 }
1410# if defined(RT_OS_DARWIN)
1411 if (pThis->DeviceFileR != NIL_RTFILE)
1412 {
1413 if (pThis->DeviceFileR != pThis->DeviceFile)
1414 {
1415 int rc = RTFileClose(pThis->DeviceFileR);
1416 AssertRC(rc);
1417 }
1418 pThis->DeviceFileR = NIL_RTFILE;
1419 }
1420# endif
1421 if (pThis->DeviceFile != NIL_RTFILE)
1422 {
1423 int rc = RTFileClose(pThis->DeviceFile);
1424 AssertRC(rc);
1425 pThis->DeviceFile = NIL_RTFILE;
1426 }
1427
1428#elif defined(RT_OS_WINDOWS)
1429
1430 CloseHandle(pThis->hEventRecv);
1431 CloseHandle(pThis->hEventSend);
1432 CancelIo(pThis->hDeviceFile);
1433 CloseHandle(pThis->hDeviceFile);
1434
1435#endif
1436}
1437
1438/**
1439 * Char driver registration record.
1440 */
1441const PDMDRVREG g_DrvHostSerial =
1442{
1443 /* u32Version */
1444 PDM_DRVREG_VERSION,
1445 /* szDriverName */
1446 "Host Serial",
1447 /* pszDescription */
1448 "Host serial driver.",
1449 /* fFlags */
1450 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1451 /* fClass. */
1452 PDM_DRVREG_CLASS_CHAR,
1453 /* cMaxInstances */
1454 ~0,
1455 /* cbInstance */
1456 sizeof(DRVHOSTSERIAL),
1457 /* pfnConstruct */
1458 drvHostSerialConstruct,
1459 /* pfnDestruct */
1460 drvHostSerialDestruct,
1461 /* pfnIOCtl */
1462 NULL,
1463 /* pfnPowerOn */
1464 NULL,
1465 /* pfnReset */
1466 NULL,
1467 /* pfnSuspend */
1468 NULL,
1469 /* pfnResume */
1470 NULL,
1471 /* pfnDetach */
1472 NULL,
1473 /** pfnPowerOff */
1474 NULL
1475};
1476
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