VirtualBox

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

Last change on this file since 25776 was 23973, checked in by vboxsync, 15 years ago

*,RTFileOpen: Fixing RTFileOpen flag misdesign: The deny, access and action flags are mandatory now.

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