VirtualBox

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

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

Host Serial: Hardware independent way of wking up the status line monitor thread

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

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