VirtualBox

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

Last change on this file since 17802 was 16527, checked in by vboxsync, 16 years ago

Linux HostSerial: typo

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 49.3 KB
Line 
1/* $Id: DrvHostSerial.cpp 16527 2009-02-05 13:44:51Z 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
361 tcsetattr(pThis->DeviceFile, TCSANOW, termiosSetup);
362 RTMemTmpFree(termiosSetup);
363#elif defined(RT_OS_WINDOWS)
364 comSetup = (LPDCB)RTMemTmpAllocZ(sizeof(DCB));
365
366 comSetup->DCBlength = sizeof(DCB);
367
368 switch (Bps) {
369 case 110:
370 comSetup->BaudRate = CBR_110;
371 break;
372 case 300:
373 comSetup->BaudRate = CBR_300;
374 break;
375 case 600:
376 comSetup->BaudRate = CBR_600;
377 break;
378 case 1200:
379 comSetup->BaudRate = CBR_1200;
380 break;
381 case 2400:
382 comSetup->BaudRate = CBR_2400;
383 break;
384 case 4800:
385 comSetup->BaudRate = CBR_4800;
386 break;
387 case 9600:
388 comSetup->BaudRate = CBR_9600;
389 break;
390 case 14400:
391 comSetup->BaudRate = CBR_14400;
392 break;
393 case 19200:
394 comSetup->BaudRate = CBR_19200;
395 break;
396 case 38400:
397 comSetup->BaudRate = CBR_38400;
398 break;
399 case 57600:
400 comSetup->BaudRate = CBR_57600;
401 break;
402 case 115200:
403 comSetup->BaudRate = CBR_115200;
404 break;
405 default:
406 comSetup->BaudRate = CBR_9600;
407 }
408
409 comSetup->fBinary = TRUE;
410 comSetup->fOutxCtsFlow = FALSE;
411 comSetup->fOutxDsrFlow = FALSE;
412 comSetup->fDtrControl = DTR_CONTROL_DISABLE;
413 comSetup->fDsrSensitivity = FALSE;
414 comSetup->fTXContinueOnXoff = TRUE;
415 comSetup->fOutX = FALSE;
416 comSetup->fInX = FALSE;
417 comSetup->fErrorChar = FALSE;
418 comSetup->fNull = FALSE;
419 comSetup->fRtsControl = RTS_CONTROL_DISABLE;
420 comSetup->fAbortOnError = FALSE;
421 comSetup->wReserved = 0;
422 comSetup->XonLim = 5;
423 comSetup->XoffLim = 5;
424 comSetup->ByteSize = cDataBits;
425
426 switch (chParity) {
427 case 'E':
428 comSetup->Parity = EVENPARITY;
429 break;
430 case 'O':
431 comSetup->Parity = ODDPARITY;
432 break;
433 case 'N':
434 comSetup->Parity = NOPARITY;
435 break;
436 default:
437 break;
438 }
439
440 switch (cStopBits) {
441 case 1:
442 comSetup->StopBits = ONESTOPBIT;
443 break;
444 case 2:
445 comSetup->StopBits = TWOSTOPBITS;
446 break;
447 default:
448 break;
449 }
450
451 comSetup->XonChar = 0;
452 comSetup->XoffChar = 0;
453 comSetup->ErrorChar = 0;
454 comSetup->EofChar = 0;
455 comSetup->EvtChar = 0;
456
457 SetCommState(pThis->hDeviceFile, comSetup);
458 RTMemTmpFree(comSetup);
459#endif /* RT_OS_WINDOWS */
460
461 return VINF_SUCCESS;
462}
463
464/* -=-=-=-=- receive thread -=-=-=-=- */
465
466/**
467 * Send thread loop.
468 *
469 * @returns VINF_SUCCESS.
470 * @param ThreadSelf Thread handle to this thread.
471 * @param pvUser User argument.
472 */
473static DECLCALLBACK(int) drvHostSerialSendThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
474{
475 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
476
477 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
478 return VINF_SUCCESS;
479
480#ifdef RT_OS_WINDOWS
481 /* Make sure that the halt event semaphore is reset. */
482 DWORD dwRet = WaitForSingleObject(pThis->hHaltEventSem, 0);
483
484 HANDLE haWait[2];
485 haWait[0] = pThis->hEventSend;
486 haWait[1] = pThis->hHaltEventSem;
487#endif
488
489 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
490 {
491 int rc = RTSemEventWait(pThis->SendSem, RT_INDEFINITE_WAIT);
492 if (RT_FAILURE(rc))
493 break;
494
495 /*
496 * Write the character to the host device.
497 */
498#ifdef RT_OS_DARWIN
499 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
500 {
501 /* copy the send queue so we get a linear buffer with the maximal size. */
502 uint8_t abBuf[sizeof(pThis->aSendQueue)];
503 uint32_t cb = 0;
504 uint32_t iTail = ASMAtomicUoReadU32(&pThis->iSendQueueTail);
505 uint32_t iHead = ASMAtomicUoReadU32(&pThis->iSendQueueHead);
506 if (iTail == iHead)
507 break;
508 do
509 {
510 abBuf[cb++] = pThis->aSendQueue[iTail];
511 iTail = (iTail + 1) & CHAR_MAX_SEND_QUEUE_MASK;
512 } while (iTail != iHead);
513
514 ASMAtomicWriteU32(&pThis->iSendQueueTail, iTail);
515
516 /* write it. */
517#ifdef DEBUG
518 uint64_t volatile u64Now = RTTimeNanoTS(); NOREF(u64Now);
519#endif
520#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
521
522 size_t cbWritten;
523 rc = RTFileWrite(pThis->DeviceFile, abBuf, cb, &cbWritten);
524 if (rc == VERR_TRY_AGAIN)
525 cbWritten = 0;
526 if (cbWritten < cb && (RT_SUCCESS(rc) || rc == VERR_TRY_AGAIN))
527 {
528 /* ok, block till the device is ready for more (O_NONBLOCK) effect. */
529 rc = VINF_SUCCESS;
530 uint8_t const *pbSrc = &abBuf[0];
531 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
532 {
533 /* advance */
534 cb -= cbWritten;
535 pbSrc += cbWritten;
536
537 /* wait */
538 fd_set WrSet;
539 FD_ZERO(&WrSet);
540 FD_SET(pThis->DeviceFile, &WrSet);
541 fd_set XcptSet;
542 FD_ZERO(&XcptSet);
543 FD_SET(pThis->DeviceFile, &XcptSet);
544 rc = select(pThis->DeviceFile + 1, NULL, &WrSet, &XcptSet, NULL);
545 /** @todo check rc? */
546
547 /* try write more */
548 rc = RTFileWrite(pThis->DeviceFile, pbSrc, cb, &cbWritten);
549 if (rc == VERR_TRY_AGAIN)
550 cbWritten = 0;
551 else if (RT_FAILURE(rc))
552 break;
553 else if (cbWritten >= cb)
554 break;
555 rc = VINF_SUCCESS;
556 } /* wait/write loop */
557 }
558
559#elif defined(RT_OS_WINDOWS)
560 /* perform an overlapped write operation. */
561 DWORD cbWritten;
562 memset(&pThis->overlappedSend, 0, sizeof(pThis->overlappedSend));
563 pThis->overlappedSend.hEvent = pThis->hEventSend;
564 if (!WriteFile(pThis->hDeviceFile, abBuf, cb, &cbWritten, &pThis->overlappedSend))
565 {
566 dwRet = GetLastError();
567 if (dwRet == ERROR_IO_PENDING)
568 {
569 /*
570 * write blocked, wait for completion or wakeup...
571 */
572 dwRet = WaitForMultipleObjects(2, haWait, FALSE, INFINITE);
573 if (dwRet != WAIT_OBJECT_0)
574 {
575 AssertMsg(pThread->enmState != PDMTHREADSTATE_RUNNING, ("The halt event sempahore is set but the thread is still in running state\n"));
576 break;
577 }
578 }
579 else
580 rc = RTErrConvertFromWin32(dwRet);
581 }
582
583#endif /* RT_OS_WINDOWS */
584 if (RT_FAILURE(rc))
585 {
586 LogRel(("HostSerial#%d: Serial Write failed with %Rrc; terminating send thread\n", pDrvIns->iInstance, rc));
587 return rc;
588 }
589 } /* write loop */
590
591#else /* old code */
592 uint32_t iTail;
593 while ( pThread->enmState == PDMTHREADSTATE_RUNNING
594 && (iTail = ASMAtomicUoReadU32(&pThis->iSendQueueTail)) != ASMAtomicUoReadU32(&pThis->iSendQueueHead))
595 {
596 /** @todo process more than one byte? */
597 unsigned cbProcessed = 1;
598 uint8_t abBuf[1];
599 abBuf[0] = pThis->aSendQueue[iTail];
600
601#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
602
603 rc = RTFileWrite(pThis->DeviceFile, abBuf, cbProcessed, NULL);
604
605#elif defined(RT_OS_WINDOWS)
606
607 DWORD cbBytesWritten;
608 memset(&pThis->overlappedSend, 0, sizeof(pThis->overlappedSend));
609 pThis->overlappedSend.hEvent = pThis->hEventSend;
610
611 if (!WriteFile(pThis->hDeviceFile, abBuf, cbProcessed, &cbBytesWritten, &pThis->overlappedSend))
612 {
613 dwRet = GetLastError();
614 if (dwRet == ERROR_IO_PENDING)
615 {
616 /*
617 * write blocked, wait ...
618 */
619 dwRet = WaitForMultipleObjects(2, haWait, FALSE, INFINITE);
620 if (dwRet != WAIT_OBJECT_0)
621 {
622 AssertMsg(pThread->enmState != PDMTHREADSTATE_RUNNING, ("The halt event sempahore is set but the thread is still in running state\n"));
623 break;
624 }
625 }
626 else
627 rc = RTErrConvertFromWin32(dwRet);
628 }
629
630#endif
631
632 if (RT_SUCCESS(rc))
633 {
634 Assert(cbProcessed == 1);
635 ASMAtomicWriteU32(&pThis->iSendQueueTail, (iTail + 1) & CHAR_MAX_SEND_QUEUE_MASK);
636 }
637 else if (RT_FAILURE(rc))
638 {
639 LogRel(("HostSerial#%d: Serial Write failed with %Rrc; terminating send thread\n", pDrvIns->iInstance, rc));
640 return rc;
641 }
642 }
643#endif /* old code */
644 }
645
646 return VINF_SUCCESS;
647}
648
649/**
650 * Unblock the send thread so it can respond to a state change.
651 *
652 * @returns a VBox status code.
653 * @param pDrvIns The driver instance.
654 * @param pThread The send thread.
655 */
656static DECLCALLBACK(int) drvHostSerialWakeupSendThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
657{
658 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
659 int rc;
660
661 rc = RTSemEventSignal(pThis->SendSem);
662 if (RT_FAILURE(rc))
663 return rc;
664
665#ifdef RT_OS_WINDOWS
666 if (!SetEvent(pThis->hHaltEventSem))
667 return RTErrConvertFromWin32(GetLastError());
668#endif
669
670 return VINF_SUCCESS;
671}
672
673/* -=-=-=-=- receive thread -=-=-=-=- */
674
675/**
676 * Receive thread loop.
677 *
678 * This thread pushes data from the host serial device up the driver
679 * chain toward the serial device.
680 *
681 * @returns VINF_SUCCESS.
682 * @param ThreadSelf Thread handle to this thread.
683 * @param pvUser User argument.
684 */
685static DECLCALLBACK(int) drvHostSerialRecvThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
686{
687 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
688 uint8_t abBuffer[256];
689 uint8_t *pbBuffer = NULL;
690 size_t cbRemaining = 0; /* start by reading host data */
691 int rc = VINF_SUCCESS;
692 int rcThread = VINF_SUCCESS;
693
694 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
695 return VINF_SUCCESS;
696
697#ifdef RT_OS_WINDOWS
698 /* Make sure that the halt event semaphore is reset. */
699 DWORD dwRet = WaitForSingleObject(pThis->hHaltEventSem, 0);
700
701 HANDLE ahWait[2];
702 ahWait[0] = pThis->hEventRecv;
703 ahWait[1] = pThis->hHaltEventSem;
704#endif
705
706 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
707 {
708 if (!cbRemaining)
709 {
710 /* Get a block of data from the host serial device. */
711
712#if defined(RT_OS_DARWIN) /* poll is broken on x86 darwin, returns POLLNVAL. */
713 fd_set RdSet;
714 FD_ZERO(&RdSet);
715 FD_SET(pThis->DeviceFileR, &RdSet);
716 FD_SET(pThis->WakeupPipeR, &RdSet);
717 fd_set XcptSet;
718 FD_ZERO(&XcptSet);
719 FD_SET(pThis->DeviceFileR, &XcptSet);
720 FD_SET(pThis->WakeupPipeR, &XcptSet);
721# if 1 /* it seems like this select is blocking the write... */
722 rc = select(RT_MAX(pThis->WakeupPipeR, pThis->DeviceFileR) + 1, &RdSet, NULL, &XcptSet, NULL);
723# else
724 struct timeval tv = { 0, 1000 };
725 rc = select(RT_MAX(pThis->WakeupPipeR, pThis->DeviceFileR) + 1, &RdSet, NULL, &XcptSet, &tv);
726# endif
727 if (rc == -1)
728 {
729 int err = errno;
730 rcThread = RTErrConvertFromErrno(err);
731 LogRel(("HostSerial#%d: select failed with errno=%d / %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, err, rcThread));
732 break;
733 }
734
735 /* this might have changed in the meantime */
736 if (pThread->enmState != PDMTHREADSTATE_RUNNING)
737 break;
738 if (rc == 0)
739 continue;
740
741 /* drain the wakeup pipe */
742 size_t cbRead;
743 if ( FD_ISSET(pThis->WakeupPipeR, &RdSet)
744 || FD_ISSET(pThis->WakeupPipeR, &XcptSet))
745 {
746 rc = RTFileRead(pThis->WakeupPipeR, abBuffer, 1, &cbRead);
747 if (RT_FAILURE(rc))
748 {
749 LogRel(("HostSerial#%d: draining the wakekup pipe failed with %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, rc));
750 rcThread = rc;
751 break;
752 }
753 continue;
754 }
755
756 /* read data from the serial port. */
757 rc = RTFileRead(pThis->DeviceFileR, abBuffer, sizeof(abBuffer), &cbRead);
758 if (RT_FAILURE(rc))
759 {
760 LogRel(("HostSerial#%d: (1) Read failed with %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, rc));
761 rcThread = rc;
762 break;
763 }
764 cbRemaining = cbRead;
765
766#elif defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
767
768 size_t cbRead;
769 struct pollfd aFDs[2];
770 aFDs[0].fd = pThis->DeviceFile;
771 aFDs[0].events = POLLIN;
772 aFDs[0].revents = 0;
773 aFDs[1].fd = pThis->WakeupPipeR;
774 aFDs[1].events = POLLIN | POLLERR | POLLHUP;
775 aFDs[1].revents = 0;
776 rc = poll(aFDs, RT_ELEMENTS(aFDs), -1);
777 if (rc < 0)
778 {
779 int err = errno;
780 rcThread = RTErrConvertFromErrno(err);
781 LogRel(("HostSerial#%d: poll failed with errno=%d / %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, err, rcThread));
782 break;
783 }
784 /* this might have changed in the meantime */
785 if (pThread->enmState != PDMTHREADSTATE_RUNNING)
786 break;
787 if (rc > 0 && aFDs[1].revents)
788 {
789 if (aFDs[1].revents & (POLLHUP | POLLERR | POLLNVAL))
790 break;
791 /* notification to terminate -- drain the pipe */
792 RTFileRead(pThis->WakeupPipeR, &abBuffer, 1, &cbRead);
793 continue;
794 }
795 rc = RTFileRead(pThis->DeviceFile, abBuffer, sizeof(abBuffer), &cbRead);
796 if (RT_FAILURE(rc))
797 {
798 /* don't terminate worker thread when data unavailable */
799 if (rc == VERR_TRY_AGAIN)
800 continue;
801
802 LogRel(("HostSerial#%d: (2) Read failed with %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, rc));
803 rcThread = rc;
804 break;
805 }
806 cbRemaining = cbRead;
807
808#elif defined(RT_OS_WINDOWS)
809
810 DWORD dwEventMask = 0;
811 DWORD dwNumberOfBytesTransferred;
812
813 memset(&pThis->overlappedRecv, 0, sizeof(pThis->overlappedRecv));
814 pThis->overlappedRecv.hEvent = pThis->hEventRecv;
815
816 if (!WaitCommEvent(pThis->hDeviceFile, &dwEventMask, &pThis->overlappedRecv))
817 {
818 dwRet = GetLastError();
819 if (dwRet == ERROR_IO_PENDING)
820 {
821 dwRet = WaitForMultipleObjects(2, ahWait, FALSE, INFINITE);
822 if (dwRet != WAIT_OBJECT_0)
823 {
824 /* notification to terminate */
825 AssertMsg(pThread->enmState != PDMTHREADSTATE_RUNNING, ("The halt event sempahore is set but the thread is still in running state\n"));
826 break;
827 }
828 }
829 else
830 {
831 rcThread = RTErrConvertFromWin32(dwRet);
832 LogRel(("HostSerial#%d: Wait failed with error %Rrc; terminating the worker thread.\n", pDrvIns->iInstance, rcThread));
833 break;
834 }
835 }
836 /* this might have changed in the meantime */
837 if (pThread->enmState != PDMTHREADSTATE_RUNNING)
838 break;
839
840 /* Check the event */
841 if (dwEventMask & EV_RXCHAR)
842 {
843 if (!ReadFile(pThis->hDeviceFile, abBuffer, sizeof(abBuffer), &dwNumberOfBytesTransferred, &pThis->overlappedRecv))
844 {
845 rcThread = RTErrConvertFromWin32(GetLastError());
846 LogRel(("HostSerial#%d: Read failed with error %Rrc; terminating the worker thread.\n", pDrvIns->iInstance, rcThread));
847 break;
848 }
849 cbRemaining = dwNumberOfBytesTransferred;
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 |= PDM_ICHAR_STATUS_LINES_DCD;
862 if (dwNewStatusLinesState & MS_RING_ON)
863 uNewStatusLinesState |= PDM_ICHAR_STATUS_LINES_RI;
864 if (dwNewStatusLinesState & MS_DSR_ON)
865 uNewStatusLinesState |= PDM_ICHAR_STATUS_LINES_DSR;
866 if (dwNewStatusLinesState & MS_CTS_ON)
867 uNewStatusLinesState |= PDM_ICHAR_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)
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)
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, false, "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, false, "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 |= PDM_ICHAR_STATUS_LINES_DCD;
1007 if (statusLines & TIOCM_RNG)
1008 newStatusLine |= PDM_ICHAR_STATUS_LINES_RI;
1009 if (statusLines & TIOCM_LE)
1010 newStatusLine |= PDM_ICHAR_STATUS_LINES_DSR;
1011 if (statusLines & TIOCM_CTS)
1012 newStatusLine |= PDM_ICHAR_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, false, "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, false, "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(PPDMICHAR 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)
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/* -=-=-=-=- driver interface -=-=-=-=- */
1177
1178/**
1179 * Construct a char driver instance.
1180 *
1181 * @returns VBox status.
1182 * @param pDrvIns The driver instance data.
1183 * If the registration structure is needed,
1184 * pDrvIns->pDrvReg points to it.
1185 * @param pCfgHandle Configuration node handle for the driver. Use this to
1186 * obtain the configuration of the driver instance. It's
1187 * also found in pDrvIns->pCfgHandle as it's expected to
1188 * be used frequently in this function.
1189 */
1190static DECLCALLBACK(int) drvHostSerialConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
1191{
1192 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
1193 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
1194
1195 /*
1196 * Init basic data members and interfaces.
1197 */
1198#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
1199 pThis->DeviceFile = NIL_RTFILE;
1200# ifdef RT_OS_DARWIN
1201 pThis->DeviceFileR = NIL_RTFILE;
1202# endif
1203 pThis->WakeupPipeR = NIL_RTFILE;
1204 pThis->WakeupPipeW = NIL_RTFILE;
1205#endif
1206 /* IBase. */
1207 pDrvIns->IBase.pfnQueryInterface = drvHostSerialQueryInterface;
1208 /* IChar. */
1209 pThis->IChar.pfnWrite = drvHostSerialWrite;
1210 pThis->IChar.pfnSetParameters = drvHostSerialSetParameters;
1211 pThis->IChar.pfnSetModemLines = drvHostSerialSetModemLines;
1212
1213/** @todo Initialize all members with NIL values!! The destructor is ALWAYS called. */
1214
1215 /*
1216 * Query configuration.
1217 */
1218 /* Device */
1219 int rc = CFGMR3QueryStringAlloc(pCfgHandle, "DevicePath", &pThis->pszDevicePath);
1220 if (RT_FAILURE(rc))
1221 {
1222 AssertMsgFailed(("Configuration error: query for \"DevicePath\" string returned %Rra.\n", rc));
1223 return rc;
1224 }
1225
1226 /*
1227 * Open the device
1228 */
1229#ifdef RT_OS_WINDOWS
1230
1231 pThis->hHaltEventSem = CreateEvent(NULL, FALSE, FALSE, NULL);
1232 AssertReturn(pThis->hHaltEventSem != NULL, VERR_NO_MEMORY);
1233
1234 pThis->hEventRecv = CreateEvent(NULL, FALSE, FALSE, NULL);
1235 AssertReturn(pThis->hEventRecv != NULL, VERR_NO_MEMORY);
1236
1237 pThis->hEventSend = CreateEvent(NULL, FALSE, FALSE, NULL);
1238 AssertReturn(pThis->hEventSend != NULL, VERR_NO_MEMORY);
1239
1240 HANDLE hFile = CreateFile(pThis->pszDevicePath,
1241 GENERIC_READ | GENERIC_WRITE,
1242 0, // must be opened with exclusive access
1243 NULL, // no SECURITY_ATTRIBUTES structure
1244 OPEN_EXISTING, // must use OPEN_EXISTING
1245 FILE_FLAG_OVERLAPPED, // overlapped I/O
1246 NULL); // no template file
1247 if (hFile == INVALID_HANDLE_VALUE)
1248 rc = RTErrConvertFromWin32(GetLastError());
1249 else
1250 {
1251 pThis->hDeviceFile = hFile;
1252 /* for overlapped read */
1253 if (!SetCommMask(hFile, EV_RXCHAR | EV_CTS | EV_DSR | EV_RING | EV_RLSD))
1254 {
1255 LogRel(("HostSerial#%d: SetCommMask failed with error %d.\n", pDrvIns->iInstance, GetLastError()));
1256 return VERR_FILE_IO_ERROR;
1257 }
1258 rc = VINF_SUCCESS;
1259 }
1260
1261#else
1262
1263 rc = RTFileOpen(&pThis->DeviceFile, pThis->pszDevicePath, RTFILE_O_OPEN | RTFILE_O_READWRITE);
1264# ifdef RT_OS_DARWIN
1265 if (RT_SUCCESS(rc))
1266 rc = RTFileOpen(&pThis->DeviceFileR, pThis->pszDevicePath, RTFILE_O_OPEN | RTFILE_O_READ);
1267# endif
1268
1269
1270#endif
1271
1272 if (RT_FAILURE(rc))
1273 {
1274 AssertMsgFailed(("Could not open host device %s, rc=%Rrc\n", pThis->pszDevicePath, rc));
1275 switch (rc)
1276 {
1277 case VERR_ACCESS_DENIED:
1278 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1279#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
1280 N_("Cannot open host device '%s' for read/write access. Check the permissions "
1281 "of that device ('/bin/ls -l %s'): Most probably you need to be member "
1282 "of the device group. Make sure that you logout/login after changing "
1283 "the group settings of the current user"),
1284#else
1285 N_("Cannot open host device '%s' for read/write access. Check the permissions "
1286 "of that device"),
1287#endif
1288 pThis->pszDevicePath, pThis->pszDevicePath);
1289 default:
1290 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1291 N_("Failed to open host device '%s'"),
1292 pThis->pszDevicePath);
1293 }
1294 }
1295
1296 /* Set to non blocking I/O */
1297#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
1298
1299 fcntl(pThis->DeviceFile, F_SETFL, O_NONBLOCK);
1300# ifdef RT_OS_DARWIN
1301 fcntl(pThis->DeviceFileR, F_SETFL, O_NONBLOCK);
1302# endif
1303 int aFDs[2];
1304 if (pipe(aFDs) != 0)
1305 {
1306 int rc = RTErrConvertFromErrno(errno);
1307 AssertRC(rc);
1308 return rc;
1309 }
1310 pThis->WakeupPipeR = aFDs[0];
1311 pThis->WakeupPipeW = aFDs[1];
1312
1313#elif defined(RT_OS_WINDOWS)
1314
1315 /* Set the COMMTIMEOUTS to get non blocking I/O */
1316 COMMTIMEOUTS comTimeout;
1317
1318 comTimeout.ReadIntervalTimeout = MAXDWORD;
1319 comTimeout.ReadTotalTimeoutMultiplier = 0;
1320 comTimeout.ReadTotalTimeoutConstant = 0;
1321 comTimeout.WriteTotalTimeoutMultiplier = 0;
1322 comTimeout.WriteTotalTimeoutConstant = 0;
1323
1324 SetCommTimeouts(pThis->hDeviceFile, &comTimeout);
1325
1326#endif
1327
1328 /*
1329 * Get the ICharPort interface of the above driver/device.
1330 */
1331 pThis->pDrvCharPort = (PPDMICHARPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_CHAR_PORT);
1332 if (!pThis->pDrvCharPort)
1333 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("HostSerial#%d has no char port interface above"), pDrvIns->iInstance);
1334
1335 /*
1336 * Create the receive, send and monitor threads pluss the related send semaphore.
1337 */
1338 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->pRecvThread, pThis, drvHostSerialRecvThread, drvHostSerialWakeupRecvThread, 0, RTTHREADTYPE_IO, "SerRecv");
1339 if (RT_FAILURE(rc))
1340 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create receive thread"), pDrvIns->iInstance);
1341
1342 rc = RTSemEventCreate(&pThis->SendSem);
1343 AssertRC(rc);
1344
1345 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->pSendThread, pThis, drvHostSerialSendThread, drvHostSerialWakeupSendThread, 0, RTTHREADTYPE_IO, "SerSend");
1346 if (RT_FAILURE(rc))
1347 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create send thread"), pDrvIns->iInstance);
1348
1349#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
1350 /* Linux & darwin needs a separate thread which monitors the status lines. */
1351# ifndef RT_OS_LINUX
1352 ioctl(pThis->DeviceFile, TIOCMGET, &pThis->fStatusLines);
1353# endif
1354 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->pMonitorThread, pThis, drvHostSerialMonitorThread, drvHostSerialWakeupMonitorThread, 0, RTTHREADTYPE_IO, "SerMon");
1355 if (RT_FAILURE(rc))
1356 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create monitor thread"), pDrvIns->iInstance);
1357#endif
1358
1359 /*
1360 * Register release statistics.
1361 */
1362 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes written", "/Devices/HostSerial%d/Written", pDrvIns->iInstance);
1363 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes read", "/Devices/HostSerial%d/Read", pDrvIns->iInstance);
1364#ifdef RT_OS_DARWIN /* new Write code, not darwin specific. */
1365 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatSendOverflows, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes overflowed", "/Devices/HostSerial%d/SendOverflow", pDrvIns->iInstance);
1366#endif
1367
1368 return VINF_SUCCESS;
1369}
1370
1371
1372/**
1373 * Destruct a char driver instance.
1374 *
1375 * Most VM resources are freed by the VM. This callback is provided so that
1376 * any non-VM resources can be freed correctly.
1377 *
1378 * @param pDrvIns The driver instance data.
1379 */
1380static DECLCALLBACK(void) drvHostSerialDestruct(PPDMDRVINS pDrvIns)
1381{
1382 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
1383
1384 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
1385
1386 /* Empty the send queue */
1387 pThis->iSendQueueTail = pThis->iSendQueueHead = 0;
1388
1389 RTSemEventDestroy(pThis->SendSem);
1390 pThis->SendSem = NIL_RTSEMEVENT;
1391
1392#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
1393
1394 if (pThis->WakeupPipeW != NIL_RTFILE)
1395 {
1396 int rc = RTFileClose(pThis->WakeupPipeW);
1397 AssertRC(rc);
1398 pThis->WakeupPipeW = NIL_RTFILE;
1399 }
1400 if (pThis->WakeupPipeR != NIL_RTFILE)
1401 {
1402 int rc = RTFileClose(pThis->WakeupPipeR);
1403 AssertRC(rc);
1404 pThis->WakeupPipeR = NIL_RTFILE;
1405 }
1406# if defined(RT_OS_DARWIN)
1407 if (pThis->DeviceFileR != NIL_RTFILE)
1408 {
1409 if (pThis->DeviceFileR != pThis->DeviceFile)
1410 {
1411 int rc = RTFileClose(pThis->DeviceFileR);
1412 AssertRC(rc);
1413 }
1414 pThis->DeviceFileR = NIL_RTFILE;
1415 }
1416# endif
1417 if (pThis->DeviceFile != NIL_RTFILE)
1418 {
1419 int rc = RTFileClose(pThis->DeviceFile);
1420 AssertRC(rc);
1421 pThis->DeviceFile = NIL_RTFILE;
1422 }
1423
1424#elif defined(RT_OS_WINDOWS)
1425
1426 CloseHandle(pThis->hEventRecv);
1427 CloseHandle(pThis->hEventSend);
1428 CancelIo(pThis->hDeviceFile);
1429 CloseHandle(pThis->hDeviceFile);
1430
1431#endif
1432}
1433
1434/**
1435 * Char driver registration record.
1436 */
1437const PDMDRVREG g_DrvHostSerial =
1438{
1439 /* u32Version */
1440 PDM_DRVREG_VERSION,
1441 /* szDriverName */
1442 "Host Serial",
1443 /* pszDescription */
1444 "Host serial driver.",
1445 /* fFlags */
1446 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1447 /* fClass. */
1448 PDM_DRVREG_CLASS_CHAR,
1449 /* cMaxInstances */
1450 ~0,
1451 /* cbInstance */
1452 sizeof(DRVHOSTSERIAL),
1453 /* pfnConstruct */
1454 drvHostSerialConstruct,
1455 /* pfnDestruct */
1456 drvHostSerialDestruct,
1457 /* pfnIOCtl */
1458 NULL,
1459 /* pfnPowerOn */
1460 NULL,
1461 /* pfnReset */
1462 NULL,
1463 /* pfnSuspend */
1464 NULL,
1465 /* pfnResume */
1466 NULL,
1467 /* pfnDetach */
1468 NULL,
1469 /** pfnPowerOff */
1470 NULL
1471};
1472
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