VirtualBox

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

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

DrvHostSerial: darwin hacking.

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