VirtualBox

Ticket #17116: DrvHostSerial.cpp

File DrvHostSerial.cpp, 54.3 KB (added by ruga, 7 years ago)

Source with log statements

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy