VirtualBox

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

Last change on this file since 33000 was 32678, checked in by vboxsync, 14 years ago

Devices/Serial, Devices/Parallel: minor fix (free alloc'd string)

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

© 2023 Oracle
ContactPrivacy policyTerms of Use