VirtualBox

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

Last change on this file since 15875 was 15836, checked in by vboxsync, 16 years ago

Devices/Serial: Enabled Host serial support for Solaris.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 49.2 KB
Line 
1/* $Id: DrvHostSerial.cpp 15836 2009-01-07 15:45:20Z 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: 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 LogRel(("HostSerial#%d: Read failed with %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, rc));
799 rcThread = rc;
800 break;
801 }
802 cbRemaining = cbRead;
803
804#elif defined(RT_OS_WINDOWS)
805
806 DWORD dwEventMask = 0;
807 DWORD dwNumberOfBytesTransferred;
808
809 memset(&pThis->overlappedRecv, 0, sizeof(pThis->overlappedRecv));
810 pThis->overlappedRecv.hEvent = pThis->hEventRecv;
811
812 if (!WaitCommEvent(pThis->hDeviceFile, &dwEventMask, &pThis->overlappedRecv))
813 {
814 dwRet = GetLastError();
815 if (dwRet == ERROR_IO_PENDING)
816 {
817 dwRet = WaitForMultipleObjects(2, ahWait, FALSE, INFINITE);
818 if (dwRet != WAIT_OBJECT_0)
819 {
820 /* notification to terminate */
821 AssertMsg(pThread->enmState != PDMTHREADSTATE_RUNNING, ("The halt event sempahore is set but the thread is still in running state\n"));
822 break;
823 }
824 }
825 else
826 {
827 rcThread = RTErrConvertFromWin32(dwRet);
828 LogRel(("HostSerial#%d: Wait failed with error %Rrc; terminating the worker thread.\n", pDrvIns->iInstance, rcThread));
829 break;
830 }
831 }
832 /* this might have changed in the meantime */
833 if (pThread->enmState != PDMTHREADSTATE_RUNNING)
834 break;
835
836 /* Check the event */
837 if (dwEventMask & EV_RXCHAR)
838 {
839 if (!ReadFile(pThis->hDeviceFile, abBuffer, sizeof(abBuffer), &dwNumberOfBytesTransferred, &pThis->overlappedRecv))
840 {
841 rcThread = RTErrConvertFromWin32(GetLastError());
842 LogRel(("HostSerial#%d: Read failed with error %Rrc; terminating the worker thread.\n", pDrvIns->iInstance, rcThread));
843 break;
844 }
845 cbRemaining = dwNumberOfBytesTransferred;
846 }
847 else
848 {
849 /* The status lines have changed. Notify the device. */
850 DWORD dwNewStatusLinesState = 0;
851 uint32_t uNewStatusLinesState = 0;
852
853 /* Get the new state */
854 if (GetCommModemStatus(pThis->hDeviceFile, &dwNewStatusLinesState))
855 {
856 if (dwNewStatusLinesState & MS_RLSD_ON)
857 uNewStatusLinesState |= PDM_ICHAR_STATUS_LINES_DCD;
858 if (dwNewStatusLinesState & MS_RING_ON)
859 uNewStatusLinesState |= PDM_ICHAR_STATUS_LINES_RI;
860 if (dwNewStatusLinesState & MS_DSR_ON)
861 uNewStatusLinesState |= PDM_ICHAR_STATUS_LINES_DSR;
862 if (dwNewStatusLinesState & MS_CTS_ON)
863 uNewStatusLinesState |= PDM_ICHAR_STATUS_LINES_CTS;
864 rc = pThis->pDrvCharPort->pfnNotifyStatusLinesChanged(pThis->pDrvCharPort, uNewStatusLinesState);
865 if (RT_FAILURE(rc))
866 {
867 /* Notifying device failed, continue but log it */
868 LogRel(("HostSerial#%d: Notifying device failed with error %Rrc; continuing.\n", pDrvIns->iInstance, rc));
869 }
870 }
871 else
872 {
873 /* Getting new state failed, continue but log it */
874 LogRel(("HostSerial#%d: Getting status lines state failed with error %Rrc; continuing.\n", pDrvIns->iInstance, RTErrConvertFromWin32(GetLastError())));
875 }
876 }
877#endif
878
879 Log(("Read %d bytes.\n", cbRemaining));
880 pbBuffer = abBuffer;
881 }
882 else
883 {
884 /* Send data to the guest. */
885 size_t cbProcessed = cbRemaining;
886 rc = pThis->pDrvCharPort->pfnNotifyRead(pThis->pDrvCharPort, pbBuffer, &cbProcessed);
887 if (RT_SUCCESS(rc))
888 {
889 Assert(cbProcessed); Assert(cbProcessed <= cbRemaining);
890 pbBuffer += cbProcessed;
891 cbRemaining -= cbProcessed;
892 STAM_COUNTER_ADD(&pThis->StatBytesRead, cbProcessed);
893 }
894 else if (rc == VERR_TIMEOUT)
895 {
896 /* Normal case, just means that the guest didn't accept a new
897 * character before the timeout elapsed. Just retry. */
898 rc = VINF_SUCCESS;
899 }
900 else
901 {
902 LogRel(("HostSerial#%d: NotifyRead failed with %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, rc));
903 rcThread = rc;
904 break;
905 }
906 }
907 }
908
909 return rcThread;
910}
911
912/**
913 * Unblock the send thread so it can respond to a state change.
914 *
915 * @returns a VBox status code.
916 * @param pDrvIns The driver instance.
917 * @param pThread The send thread.
918 */
919static DECLCALLBACK(int) drvHostSerialWakeupRecvThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
920{
921 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
922#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
923 return RTFileWrite(pThis->WakeupPipeW, "", 1, NULL);
924#elif defined(RT_OS_WINDOWS)
925 if (!SetEvent(pThis->hHaltEventSem))
926 return RTErrConvertFromWin32(GetLastError());
927 return VINF_SUCCESS;
928#else
929# error adapt me!
930#endif
931}
932
933#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
934/* -=-=-=-=- Monitor thread -=-=-=-=- */
935
936/**
937 * Monitor thread loop.
938 *
939 * This thread monitors the status lines and notifies the device
940 * if they change.
941 *
942 * @returns VINF_SUCCESS.
943 * @param ThreadSelf Thread handle to this thread.
944 * @param pvUser User argument.
945 */
946static DECLCALLBACK(int) drvHostSerialMonitorThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
947{
948 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
949 int rc = VINF_SUCCESS;
950 unsigned uStatusLinesToCheck = 0;
951
952 uStatusLinesToCheck = TIOCM_CAR | TIOCM_RNG | TIOCM_LE | TIOCM_CTS;
953
954 if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
955 return VINF_SUCCESS;
956
957 while (pThread->enmState == PDMTHREADSTATE_RUNNING)
958 {
959 uint32_t newStatusLine = 0;
960 unsigned int statusLines;
961
962# ifdef RT_OS_LINUX
963 /*
964 * Wait for status line change.
965 */
966 rc = ioctl(pThis->DeviceFile, TIOCMIWAIT, &uStatusLinesToCheck);
967 if (pThread->enmState != PDMTHREADSTATE_RUNNING)
968 break;
969 if (rc < 0)
970 {
971ioctl_error:
972 PDMDrvHlpVMSetRuntimeError(pDrvIns, false, "DrvHostSerialFail",
973 N_("Ioctl failed for serial host device '%s' (%Rrc). The device will not work properly"),
974 pThis->pszDevicePath, RTErrConvertFromErrno(errno));
975 break;
976 }
977
978 rc = ioctl(pThis->DeviceFile, TIOCMGET, &statusLines);
979 if (rc < 0)
980 goto ioctl_error;
981# else /* !RT_OS_LINUX */
982 /*
983 * Poll for the status line change.
984 */
985 rc = ioctl(pThis->DeviceFile, TIOCMGET, &statusLines);
986 if (rc < 0)
987 {
988 PDMDrvHlpVMSetRuntimeError(pDrvIns, false, "DrvHostSerialFail",
989 N_("Ioctl failed for serial host device '%s' (%Rrc). The device will not work properly"),
990 pThis->pszDevicePath, RTErrConvertFromErrno(errno));
991 break;
992 }
993 if (!((statusLines ^ pThis->fStatusLines) & uStatusLinesToCheck))
994 {
995 PDMR3ThreadSleep(pThread, 500); /* 0.5 sec */
996 continue;
997 }
998 pThis->fStatusLines = statusLines;
999# endif /* !RT_OS_LINUX */
1000
1001 if (statusLines & TIOCM_CAR)
1002 newStatusLine |= PDM_ICHAR_STATUS_LINES_DCD;
1003 if (statusLines & TIOCM_RNG)
1004 newStatusLine |= PDM_ICHAR_STATUS_LINES_RI;
1005 if (statusLines & TIOCM_LE)
1006 newStatusLine |= PDM_ICHAR_STATUS_LINES_DSR;
1007 if (statusLines & TIOCM_CTS)
1008 newStatusLine |= PDM_ICHAR_STATUS_LINES_CTS;
1009 rc = pThis->pDrvCharPort->pfnNotifyStatusLinesChanged(pThis->pDrvCharPort, newStatusLine);
1010 }
1011
1012 return VINF_SUCCESS;
1013}
1014
1015/**
1016 * Unblock the monitor thread so it can respond to a state change.
1017 * We need to execute this code exactly once during initialization.
1018 * But we don't want to block --- therefore this dedicated thread.
1019 *
1020 * @returns a VBox status code.
1021 * @param pDrvIns The driver instance.
1022 * @param pThread The send thread.
1023 */
1024static DECLCALLBACK(int) drvHostSerialWakeupMonitorThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
1025{
1026# ifdef RT_OS_LINUX
1027 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
1028 int rc = VINF_SUCCESS;
1029# if 0
1030 unsigned int uSerialLineFlags;
1031 unsigned int uSerialLineStatus;
1032 unsigned int uIoctl;
1033# endif
1034
1035 /*
1036 * Linux is a bit difficult as the thread is sleeping in an ioctl call.
1037 * So there is no way to have a wakeup pipe.
1038 *
1039 * 1. Thatswhy we set the serial device into loopback mode and change one of the
1040 * modem control bits.
1041 * This should make the ioctl call return.
1042 *
1043 * 2. We still got reports about long shutdown times. It may bepossible
1044 * that the loopback mode is not implemented on all devices.
1045 * The next possible solution is to close the device file to make the ioctl
1046 * return with EBADF and be able to suspend the thread.
1047 *
1048 * 3. The second approach doesn't work too, the ioctl doesn't return.
1049 * But it seems that the ioctl is interruptible (return code in errno is EINTR).
1050 */
1051
1052# if 0 /* Disabled because it does not work for all. */
1053 /* Get current status of control lines. */
1054 rc = ioctl(pThis->DeviceFile, TIOCMGET, &uSerialLineStatus);
1055 if (rc < 0)
1056 goto ioctl_error;
1057
1058 uSerialLineFlags = TIOCM_LOOP;
1059 rc = ioctl(pThis->DeviceFile, TIOCMBIS, &uSerialLineFlags);
1060 if (rc < 0)
1061 goto ioctl_error;
1062
1063 /*
1064 * Change current level on the RTS pin to make the ioctl call return in the
1065 * monitor thread.
1066 */
1067 uIoctl = (uSerialLineStatus & TIOCM_CTS) ? TIOCMBIC : TIOCMBIS;
1068 uSerialLineFlags = TIOCM_RTS;
1069
1070 rc = ioctl(pThis->DeviceFile, uIoctl, &uSerialLineFlags);
1071 if (rc < 0)
1072 goto ioctl_error;
1073
1074 /* Change RTS back to the previous level. */
1075 uIoctl = (uIoctl == TIOCMBIC) ? TIOCMBIS : TIOCMBIC;
1076
1077 rc = ioctl(pThis->DeviceFile, uIoctl, &uSerialLineFlags);
1078 if (rc < 0)
1079 goto ioctl_error;
1080
1081 /*
1082 * Set serial device into normal state.
1083 */
1084 uSerialLineFlags = TIOCM_LOOP;
1085 rc = ioctl(pThis->DeviceFile, TIOCMBIC, &uSerialLineFlags);
1086 if (rc >= 0)
1087 return VINF_SUCCESS;
1088
1089ioctl_error:
1090 PDMDrvHlpVMSetRuntimeError(pDrvIns, false, "DrvHostSerialFail",
1091 N_("Ioctl failed for serial host device '%s' (%Rrc). The device will not work properly"),
1092 pThis->pszDevicePath, RTErrConvertFromErrno(errno));
1093# endif
1094
1095# if 0
1096 /* Close file to make ioctl return. */
1097 RTFileClose(pData->DeviceFile);
1098 /* Open again to make use after suspend possible again. */
1099 rc = RTFileOpen(&pData->DeviceFile, pData->pszDevicePath, RTFILE_O_OPEN | RTFILE_O_READWRITE);
1100 AssertMsgRC(rc, ("Opening device file again failed rc=%Rrc\n", rc));
1101
1102 if (RT_FAILURE(rc))
1103 PDMDrvHlpVMSetRuntimeError(pDrvIns, false, "DrvHostSerialFail",
1104 N_("Opening failed for serial host device '%s' (%Rrc). The device will not work"),
1105 pData->pszDevicePath, rc);
1106# endif
1107
1108 rc = RTThreadPoke(pThread->Thread);
1109 if (RT_FAILURE(rc))
1110 PDMDrvHlpVMSetRuntimeError(pDrvIns, false, "DrvHostSerialFail",
1111 N_("Suspending serial monitor thread failed for serial device '%s' (%Rrc). The shutdown may take longer than expected"),
1112 pThis->pszDevicePath, RTErrConvertFromErrno(rc));
1113
1114# else /* !RT_OS_LINUX*/
1115
1116 /* In polling mode there is nobody to wake up (PDMThread will cancel the sleep). */
1117 NOREF(pDrvIns);
1118 NOREF(pThread);
1119
1120# endif /* RT_OS_LINUX */
1121
1122 return VINF_SUCCESS;
1123}
1124#endif /* RT_OS_LINUX || RT_OS_DARWIN || RT_OS_SOLARIS */
1125
1126/**
1127 * Set the modem lines.
1128 *
1129 * @returns VBox status code
1130 * @param pInterface Pointer to the interface structure.
1131 * @param RequestToSend Set to true if this control line should be made active.
1132 * @param DataTerminalReady Set to true if this control line should be made active.
1133 */
1134static DECLCALLBACK(int) drvHostSerialSetModemLines(PPDMICHAR pInterface, bool RequestToSend, bool DataTerminalReady)
1135{
1136 PDRVHOSTSERIAL pThis = PDMICHAR_2_DRVHOSTSERIAL(pInterface);
1137
1138#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
1139 int modemStateSet = 0;
1140 int modemStateClear = 0;
1141
1142 if (RequestToSend)
1143 modemStateSet |= TIOCM_RTS;
1144 else
1145 modemStateClear |= TIOCM_RTS;
1146
1147 if (DataTerminalReady)
1148 modemStateSet |= TIOCM_DTR;
1149 else
1150 modemStateClear |= TIOCM_DTR;
1151
1152 if (modemStateSet)
1153 ioctl(pThis->DeviceFile, TIOCMBIS, &modemStateSet);
1154
1155 if (modemStateClear)
1156 ioctl(pThis->DeviceFile, TIOCMBIC, &modemStateClear);
1157#elif defined(RT_OS_WINDOWS)
1158 if (RequestToSend)
1159 EscapeCommFunction(pThis->hDeviceFile, SETRTS);
1160 else
1161 EscapeCommFunction(pThis->hDeviceFile, CLRRTS);
1162
1163 if (DataTerminalReady)
1164 EscapeCommFunction(pThis->hDeviceFile, SETDTR);
1165 else
1166 EscapeCommFunction(pThis->hDeviceFile, CLRDTR);
1167#endif
1168
1169 return VINF_SUCCESS;
1170}
1171
1172/* -=-=-=-=- driver interface -=-=-=-=- */
1173
1174/**
1175 * Construct a char driver instance.
1176 *
1177 * @returns VBox status.
1178 * @param pDrvIns The driver instance data.
1179 * If the registration structure is needed,
1180 * pDrvIns->pDrvReg points to it.
1181 * @param pCfgHandle Configuration node handle for the driver. Use this to
1182 * obtain the configuration of the driver instance. It's
1183 * also found in pDrvIns->pCfgHandle as it's expected to
1184 * be used frequently in this function.
1185 */
1186static DECLCALLBACK(int) drvHostSerialConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
1187{
1188 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
1189 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
1190
1191 /*
1192 * Init basic data members and interfaces.
1193 */
1194#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
1195 pThis->DeviceFile = NIL_RTFILE;
1196# ifdef RT_OS_DARWIN
1197 pThis->DeviceFileR = NIL_RTFILE;
1198# endif
1199 pThis->WakeupPipeR = NIL_RTFILE;
1200 pThis->WakeupPipeW = NIL_RTFILE;
1201#endif
1202 /* IBase. */
1203 pDrvIns->IBase.pfnQueryInterface = drvHostSerialQueryInterface;
1204 /* IChar. */
1205 pThis->IChar.pfnWrite = drvHostSerialWrite;
1206 pThis->IChar.pfnSetParameters = drvHostSerialSetParameters;
1207 pThis->IChar.pfnSetModemLines = drvHostSerialSetModemLines;
1208
1209/** @todo Initialize all members with NIL values!! The destructor is ALWAYS called. */
1210
1211 /*
1212 * Query configuration.
1213 */
1214 /* Device */
1215 int rc = CFGMR3QueryStringAlloc(pCfgHandle, "DevicePath", &pThis->pszDevicePath);
1216 if (RT_FAILURE(rc))
1217 {
1218 AssertMsgFailed(("Configuration error: query for \"DevicePath\" string returned %Rra.\n", rc));
1219 return rc;
1220 }
1221
1222 /*
1223 * Open the device
1224 */
1225#ifdef RT_OS_WINDOWS
1226
1227 pThis->hHaltEventSem = CreateEvent(NULL, FALSE, FALSE, NULL);
1228 AssertReturn(pThis->hHaltEventSem != NULL, VERR_NO_MEMORY);
1229
1230 pThis->hEventRecv = CreateEvent(NULL, FALSE, FALSE, NULL);
1231 AssertReturn(pThis->hEventRecv != NULL, VERR_NO_MEMORY);
1232
1233 pThis->hEventSend = CreateEvent(NULL, FALSE, FALSE, NULL);
1234 AssertReturn(pThis->hEventSend != NULL, VERR_NO_MEMORY);
1235
1236 HANDLE hFile = CreateFile(pThis->pszDevicePath,
1237 GENERIC_READ | GENERIC_WRITE,
1238 0, // must be opened with exclusive access
1239 NULL, // no SECURITY_ATTRIBUTES structure
1240 OPEN_EXISTING, // must use OPEN_EXISTING
1241 FILE_FLAG_OVERLAPPED, // overlapped I/O
1242 NULL); // no template file
1243 if (hFile == INVALID_HANDLE_VALUE)
1244 rc = RTErrConvertFromWin32(GetLastError());
1245 else
1246 {
1247 pThis->hDeviceFile = hFile;
1248 /* for overlapped read */
1249 if (!SetCommMask(hFile, EV_RXCHAR | EV_CTS | EV_DSR | EV_RING | EV_RLSD))
1250 {
1251 LogRel(("HostSerial#%d: SetCommMask failed with error %d.\n", pDrvIns->iInstance, GetLastError()));
1252 return VERR_FILE_IO_ERROR;
1253 }
1254 rc = VINF_SUCCESS;
1255 }
1256
1257#else
1258
1259 rc = RTFileOpen(&pThis->DeviceFile, pThis->pszDevicePath, RTFILE_O_OPEN | RTFILE_O_READWRITE);
1260# ifdef RT_OS_DARWIN
1261 if (RT_SUCCESS(rc))
1262 rc = RTFileOpen(&pThis->DeviceFileR, pThis->pszDevicePath, RTFILE_O_OPEN | RTFILE_O_READ);
1263# endif
1264
1265
1266#endif
1267
1268 if (RT_FAILURE(rc))
1269 {
1270 AssertMsgFailed(("Could not open host device %s, rc=%Rrc\n", pThis->pszDevicePath, rc));
1271 switch (rc)
1272 {
1273 case VERR_ACCESS_DENIED:
1274 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1275#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
1276 N_("Cannot open host device '%s' for read/write access. Check the permissions "
1277 "of that device ('/bin/ls -l %s'): Most probably you need to be member "
1278 "of the device group. Make sure that you logout/login after changing "
1279 "the group settings of the current user"),
1280#else
1281 N_("Cannot open host device '%s' for read/write access. Check the permissions "
1282 "of that device"),
1283#endif
1284 pThis->pszDevicePath, pThis->pszDevicePath);
1285 default:
1286 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
1287 N_("Failed to open host device '%s'"),
1288 pThis->pszDevicePath);
1289 }
1290 }
1291
1292 /* Set to non blocking I/O */
1293#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
1294
1295 fcntl(pThis->DeviceFile, F_SETFL, O_NONBLOCK);
1296# ifdef RT_OS_DARWIN
1297 fcntl(pThis->DeviceFileR, F_SETFL, O_NONBLOCK);
1298# endif
1299 int aFDs[2];
1300 if (pipe(aFDs) != 0)
1301 {
1302 int rc = RTErrConvertFromErrno(errno);
1303 AssertRC(rc);
1304 return rc;
1305 }
1306 pThis->WakeupPipeR = aFDs[0];
1307 pThis->WakeupPipeW = aFDs[1];
1308
1309#elif defined(RT_OS_WINDOWS)
1310
1311 /* Set the COMMTIMEOUTS to get non blocking I/O */
1312 COMMTIMEOUTS comTimeout;
1313
1314 comTimeout.ReadIntervalTimeout = MAXDWORD;
1315 comTimeout.ReadTotalTimeoutMultiplier = 0;
1316 comTimeout.ReadTotalTimeoutConstant = 0;
1317 comTimeout.WriteTotalTimeoutMultiplier = 0;
1318 comTimeout.WriteTotalTimeoutConstant = 0;
1319
1320 SetCommTimeouts(pThis->hDeviceFile, &comTimeout);
1321
1322#endif
1323
1324 /*
1325 * Get the ICharPort interface of the above driver/device.
1326 */
1327 pThis->pDrvCharPort = (PPDMICHARPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_CHAR_PORT);
1328 if (!pThis->pDrvCharPort)
1329 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("HostSerial#%d has no char port interface above"), pDrvIns->iInstance);
1330
1331 /*
1332 * Create the receive, send and monitor threads pluss the related send semaphore.
1333 */
1334 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->pRecvThread, pThis, drvHostSerialRecvThread, drvHostSerialWakeupRecvThread, 0, RTTHREADTYPE_IO, "SerRecv");
1335 if (RT_FAILURE(rc))
1336 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create receive thread"), pDrvIns->iInstance);
1337
1338 rc = RTSemEventCreate(&pThis->SendSem);
1339 AssertRC(rc);
1340
1341 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->pSendThread, pThis, drvHostSerialSendThread, drvHostSerialWakeupSendThread, 0, RTTHREADTYPE_IO, "SerSend");
1342 if (RT_FAILURE(rc))
1343 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create send thread"), pDrvIns->iInstance);
1344
1345#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
1346 /* Linux & darwin needs a separate thread which monitors the status lines. */
1347# ifndef RT_OS_LINUX
1348 ioctl(pThis->DeviceFile, TIOCMGET, &pThis->fStatusLines);
1349# endif
1350 rc = PDMDrvHlpPDMThreadCreate(pDrvIns, &pThis->pMonitorThread, pThis, drvHostSerialMonitorThread, drvHostSerialWakeupMonitorThread, 0, RTTHREADTYPE_IO, "SerMon");
1351 if (RT_FAILURE(rc))
1352 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create monitor thread"), pDrvIns->iInstance);
1353#endif
1354
1355 /*
1356 * Register release statistics.
1357 */
1358 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes written", "/Devices/HostSerial%d/Written", pDrvIns->iInstance);
1359 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes read", "/Devices/HostSerial%d/Read", pDrvIns->iInstance);
1360#ifdef RT_OS_DARWIN /* new Write code, not darwin specific. */
1361 PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatSendOverflows, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes overflowed", "/Devices/HostSerial%d/SendOverflow", pDrvIns->iInstance);
1362#endif
1363
1364 return VINF_SUCCESS;
1365}
1366
1367
1368/**
1369 * Destruct a char driver instance.
1370 *
1371 * Most VM resources are freed by the VM. This callback is provided so that
1372 * any non-VM resources can be freed correctly.
1373 *
1374 * @param pDrvIns The driver instance data.
1375 */
1376static DECLCALLBACK(void) drvHostSerialDestruct(PPDMDRVINS pDrvIns)
1377{
1378 PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL);
1379
1380 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
1381
1382 /* Empty the send queue */
1383 pThis->iSendQueueTail = pThis->iSendQueueHead = 0;
1384
1385 RTSemEventDestroy(pThis->SendSem);
1386 pThis->SendSem = NIL_RTSEMEVENT;
1387
1388#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS)
1389
1390 if (pThis->WakeupPipeW != NIL_RTFILE)
1391 {
1392 int rc = RTFileClose(pThis->WakeupPipeW);
1393 AssertRC(rc);
1394 pThis->WakeupPipeW = NIL_RTFILE;
1395 }
1396 if (pThis->WakeupPipeR != NIL_RTFILE)
1397 {
1398 int rc = RTFileClose(pThis->WakeupPipeR);
1399 AssertRC(rc);
1400 pThis->WakeupPipeR = NIL_RTFILE;
1401 }
1402# if defined(RT_OS_DARWIN)
1403 if (pThis->DeviceFileR != NIL_RTFILE)
1404 {
1405 if (pThis->DeviceFileR != pThis->DeviceFile)
1406 {
1407 int rc = RTFileClose(pThis->DeviceFileR);
1408 AssertRC(rc);
1409 }
1410 pThis->DeviceFileR = NIL_RTFILE;
1411 }
1412# endif
1413 if (pThis->DeviceFile != NIL_RTFILE)
1414 {
1415 int rc = RTFileClose(pThis->DeviceFile);
1416 AssertRC(rc);
1417 pThis->DeviceFile = NIL_RTFILE;
1418 }
1419
1420#elif defined(RT_OS_WINDOWS)
1421
1422 CloseHandle(pThis->hEventRecv);
1423 CloseHandle(pThis->hEventSend);
1424 CancelIo(pThis->hDeviceFile);
1425 CloseHandle(pThis->hDeviceFile);
1426
1427#endif
1428}
1429
1430/**
1431 * Char driver registration record.
1432 */
1433const PDMDRVREG g_DrvHostSerial =
1434{
1435 /* u32Version */
1436 PDM_DRVREG_VERSION,
1437 /* szDriverName */
1438 "Host Serial",
1439 /* pszDescription */
1440 "Host serial driver.",
1441 /* fFlags */
1442 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1443 /* fClass. */
1444 PDM_DRVREG_CLASS_CHAR,
1445 /* cMaxInstances */
1446 ~0,
1447 /* cbInstance */
1448 sizeof(DRVHOSTSERIAL),
1449 /* pfnConstruct */
1450 drvHostSerialConstruct,
1451 /* pfnDestruct */
1452 drvHostSerialDestruct,
1453 /* pfnIOCtl */
1454 NULL,
1455 /* pfnPowerOn */
1456 NULL,
1457 /* pfnReset */
1458 NULL,
1459 /* pfnSuspend */
1460 NULL,
1461 /* pfnResume */
1462 NULL,
1463 /* pfnDetach */
1464 NULL,
1465 /** pfnPowerOff */
1466 NULL
1467};
1468
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