VirtualBox

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

Last change on this file since 14831 was 13837, checked in by vboxsync, 16 years ago

s/%Vr\([acfs]\)/%Rr\1/g - since I'm upsetting everyone anyway, better make the most of it...

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