VirtualBox

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

Last change on this file since 25976 was 25971, checked in by vboxsync, 15 years ago

pdmifs.h: another batch of _IID changes.

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