VirtualBox

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

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

Devices: pData -> pThis.

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