VirtualBox

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

Last change on this file since 4143 was 4143, checked in by vboxsync, 17 years ago

Contribution

File size: 15.8 KB
Line 
1/** @file
2 *
3 * VBox stream I/O devices:
4 * Host serial driver
5 *
6 * Contributed by: Alexander Eichner
7 */
8
9/*
10 * Copyright (C) 2006-2007 innotek GmbH
11 *
12 * This file is part of VirtualBox Open Source Edition (OSE), as
13 * available from http://www.virtualbox.org. This file is free software;
14 * you can redistribute it and/or modify it under the terms of the GNU
15 * General Public License as published by the Free Software Foundation,
16 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
17 * distribution. VirtualBox OSE is distributed in the hope that it will
18 * be useful, but WITHOUT ANY WARRANTY of any kind.
19 *
20 * If you received this file as part of a commercial VirtualBox
21 * distribution, then only the terms of your commercial VirtualBox
22 * license agreement apply instead of the previous paragraph.
23 */
24
25
26
27/*******************************************************************************
28* Header Files *
29*******************************************************************************/
30#define LOG_GROUP LOG_GROUP_DRV_CHAR
31#include <VBox/pdm.h>
32#include <VBox/err.h>
33
34#include <VBox/log.h>
35#include <iprt/asm.h>
36#include <iprt/assert.h>
37#include <iprt/stream.h>
38#include <iprt/semaphore.h>
39
40#ifdef RT_OS_LINUX
41#include <termios.h>
42#include <sys/types.h>
43#include <fcntl.h>
44#include <string.h>
45#include <unistd.h>
46#endif
47
48#include "Builtins.h"
49
50
51/** Size of the send fifo queue (in bytes) */
52#define CHAR_MAX_SEND_QUEUE 0x80
53#define CHAR_MAX_SEND_QUEUE_MASK 0x7f
54
55/*******************************************************************************
56* Structures and Typedefs *
57*******************************************************************************/
58
59/**
60 * Char driver instance data.
61 */
62typedef struct DRVHOSTSERIAL
63{
64 /** Pointer to the driver instance structure. */
65 PPDMDRVINS pDrvIns;
66 /** Pointer to the char port interface of the driver/device above us. */
67 PPDMICHARPORT pDrvCharPort;
68 /** Our char interface. */
69 PDMICHAR IChar;
70 /** Flag to notify the receive thread it should terminate. */
71 volatile bool fShutdown;
72 /** Receive thread ID. */
73 RTTHREAD ReceiveThread;
74 /** Send thread ID. */
75 RTTHREAD SendThread;
76 /** Send event semephore */
77 RTSEMEVENT SendSem;
78
79 /** the device path */
80 char *pszDevicePath;
81 /** the device handle */
82 int DeviceFile;
83
84 /** Internal send FIFO queue */
85 uint8_t aSendQueue[CHAR_MAX_SEND_QUEUE];
86 uint32_t iSendQueueHead;
87 uint32_t iSendQueueTail;
88
89 /** Read/write statistics */
90 STAMCOUNTER StatBytesRead;
91 STAMCOUNTER StatBytesWritten;
92} DRVHOSTSERIAL, *PDRVHOSTSERIAL;
93
94
95/** Converts a pointer to DRVCHAR::IChar to a PDRVHOSTSERIAL. */
96#define PDMICHAR_2_DRVHOSTSERIAL(pInterface) ( (PDRVHOSTSERIAL)((uintptr_t)pInterface - RT_OFFSETOF(DRVHOSTSERIAL, IChar)) )
97
98
99/* -=-=-=-=- IBase -=-=-=-=- */
100
101/**
102 * Queries an interface to the driver.
103 *
104 * @returns Pointer to interface.
105 * @returns NULL if the interface was not supported by the driver.
106 * @param pInterface Pointer to this interface structure.
107 * @param enmInterface The requested interface identification.
108 */
109static DECLCALLBACK(void *) drvHostSerialQueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface)
110{
111 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
112 PDRVHOSTSERIAL pData = PDMINS2DATA(pDrvIns, PDRVHOSTSERIAL);
113 switch (enmInterface)
114 {
115 case PDMINTERFACE_BASE:
116 return &pDrvIns->IBase;
117 case PDMINTERFACE_CHAR:
118 return &pData->IChar;
119 default:
120 return NULL;
121 }
122}
123
124
125/* -=-=-=-=- IChar -=-=-=-=- */
126
127/** @copydoc PDMICHAR::pfnWrite */
128static DECLCALLBACK(int) drvHostSerialWrite(PPDMICHAR pInterface, const void *pvBuf, size_t cbWrite)
129{
130 PDRVHOSTSERIAL pData = PDMICHAR_2_DRVHOSTSERIAL(pInterface);
131 const char *pBuffer = (const char *)pvBuf;
132
133 LogFlow(("%s: pvBuf=%#p cbWrite=%d\n", __FUNCTION__, pvBuf, cbWrite));
134
135 for (uint32_t i=0;i<cbWrite;i++)
136 {
137 uint32_t idx = pData->iSendQueueHead;
138
139 pData->aSendQueue[idx] = pBuffer[i];
140 idx = (idx + 1) & CHAR_MAX_SEND_QUEUE_MASK;
141
142 STAM_COUNTER_INC(&pData->StatBytesWritten);
143 ASMAtomicXchgU32(&pData->iSendQueueHead, idx);
144 }
145 RTSemEventSignal(pData->SendSem);
146 return VINF_SUCCESS;
147}
148
149static DECLCALLBACK(int) drvHostSerialSetParameters(PPDMICHAR pInterface, int speed, int parity, int data_bits, int stop_bits)
150{
151 PDRVHOSTSERIAL pData = PDMICHAR_2_DRVHOSTSERIAL(pInterface);
152 struct termios termiosSetup;
153 int baud_rate;
154
155 LogFlow(("%s: speed=%d parity=%c data_bits=%d stop_bits=%d\n", __FUNCTION__, speed, parity, data_bits, stop_bits));
156
157 memset(&termiosSetup, 0, sizeof(termiosSetup));
158
159 /* Enable receiver */
160 termiosSetup.c_cflag |= (CLOCAL | CREAD);
161
162 switch (speed) {
163 case 50:
164 baud_rate = B50;
165 break;
166 case 75:
167 baud_rate = B75;
168 break;
169 case 110:
170 baud_rate = B110;
171 break;
172 case 134:
173 baud_rate = B134;
174 break;
175 case 150:
176 baud_rate = B150;
177 break;
178 case 200:
179 baud_rate = B200;
180 break;
181 case 300:
182 baud_rate = B300;
183 break;
184 case 600:
185 baud_rate = B600;
186 break;
187 case 1200:
188 baud_rate = B1200;
189 break;
190 case 1800:
191 baud_rate = B1800;
192 break;
193 case 2400:
194 baud_rate = B2400;
195 break;
196 case 4800:
197 baud_rate = B4800;
198 break;
199 case 9600:
200 baud_rate = B9600;
201 break;
202 case 19200:
203 baud_rate = B19200;
204 break;
205 case 38400:
206 baud_rate = B38400;
207 break;
208 case 57600:
209 baud_rate = B57600;
210 break;
211 case 115200:
212 baud_rate = B115200;
213 break;
214 default:
215 baud_rate = B9600;
216 }
217
218 cfsetispeed(&termiosSetup, baud_rate);
219 cfsetospeed(&termiosSetup, baud_rate);
220
221 switch (parity) {
222 case 'E':
223 termiosSetup.c_cflag |= PARENB;
224 break;
225 case 'O':
226 termiosSetup.c_cflag |= (PARENB | PARODD);
227 break;
228 case 'N':
229 break;
230 default:
231 break;
232 }
233
234 switch (data_bits) {
235 case 5:
236 termiosSetup.c_cflag |= CS5;
237 break;
238 case 6:
239 termiosSetup.c_cflag |= CS6;
240 break;
241 case 7:
242 termiosSetup.c_cflag |= CS7;
243 break;
244 case 8:
245 termiosSetup.c_cflag |= CS8;
246 break;
247 default:
248 break;
249 }
250
251 switch (stop_bits) {
252 case 2:
253 termiosSetup.c_cflag |= CSTOPB;
254 default:
255 break;
256 }
257
258 /* set serial port to raw input */
259 termiosSetup.c_lflag = ~(ICANON | ECHO | ECHOE | ISIG);
260
261 tcsetattr(pData->DeviceFile, TCSANOW, &termiosSetup);
262}
263
264/* -=-=-=-=- receive thread -=-=-=-=- */
265
266/**
267 * Send thread loop.
268 *
269 * @returns 0 on success.
270 * @param ThreadSelf Thread handle to this thread.
271 * @param pvUser User argument.
272 */
273static DECLCALLBACK(int) drvHostSerialSendLoop(RTTHREAD ThreadSelf, void *pvUser)
274{
275 PDRVHOSTSERIAL pData = (PDRVHOSTSERIAL)pvUser;
276
277 for(;;)
278 {
279 int rc = RTSemEventWait(pData->SendSem, RT_INDEFINITE_WAIT);
280 if (VBOX_FAILURE(rc))
281 break;
282
283 /*
284 * Write the character to the host device.
285 */
286 if (!pData->fShutdown)
287 {
288 while (pData->iSendQueueTail != pData->iSendQueueHead)
289 {
290 size_t cbProcessed = 1;
291
292 rc = write(pData->DeviceFile, &pData->aSendQueue[pData->iSendQueueTail], cbProcessed);
293 if (rc > 0)
294 {
295 Assert(cbProcessed);
296 pData->iSendQueueTail++;
297 pData->iSendQueueTail &= CHAR_MAX_SEND_QUEUE_MASK;
298 }
299 else if (rc < 0)
300 {
301 LogFlow(("Write failed with %Vrc; skipping\n", rc));
302 break;
303 }
304 }
305 }
306 else
307 break;
308 }
309
310 pData->SendThread = NIL_RTTHREAD;
311
312 return VINF_SUCCESS;
313}
314
315
316/* -=-=-=-=- receive thread -=-=-=-=- */
317
318/**
319 * Receive thread loop.
320 *
321 * @returns 0 on success.
322 * @param ThreadSelf Thread handle to this thread.
323 * @param pvUser User argument.
324 */
325static DECLCALLBACK(int) drvHostSerialReceiveLoop(RTTHREAD ThreadSelf, void *pvUser)
326{
327 PDRVHOSTSERIAL pData = (PDRVHOSTSERIAL)pvUser;
328 char aBuffer[256], *pBuffer;
329 size_t cbRemaining, cbProcessed;
330 int rc;
331
332 cbRemaining = 0;
333 pBuffer = aBuffer;
334 while (!pData->fShutdown)
335 {
336 if (!cbRemaining)
337 {
338 /* Get block of data from stream driver. */
339 cbRemaining = sizeof(aBuffer);
340 rc = read(pData->DeviceFile, aBuffer, cbRemaining);
341 if (rc < 0)
342 {
343 LogFlow(("Read failed with %Vrc\n", rc));
344 break;
345 } else {
346 cbRemaining = rc;
347 }
348 pBuffer = aBuffer;
349 }
350 else
351 {
352 /* Send data to guest. */
353 cbProcessed = cbRemaining;
354 rc = pData->pDrvCharPort->pfnNotifyRead(pData->pDrvCharPort, pBuffer, &cbProcessed);
355 if (VBOX_SUCCESS(rc))
356 {
357 Assert(cbProcessed);
358 pBuffer += cbProcessed;
359 cbRemaining -= cbProcessed;
360 STAM_COUNTER_ADD(&pData->StatBytesRead, cbProcessed);
361 }
362 else if (rc == VERR_TIMEOUT)
363 {
364 /* Normal case, just means that the guest didn't accept a new
365 * character before the timeout elapsed. Just retry. */
366 rc = VINF_SUCCESS;
367 }
368 else
369 {
370 LogFlow(("NotifyRead failed with %Vrc\n", rc));
371 break;
372 }
373 }
374 }
375
376 pData->ReceiveThread = NIL_RTTHREAD;
377
378 return VINF_SUCCESS;
379}
380
381
382/* -=-=-=-=- driver interface -=-=-=-=- */
383
384/**
385 * Construct a char driver instance.
386 *
387 * @returns VBox status.
388 * @param pDrvIns The driver instance data.
389 * If the registration structure is needed,
390 * pDrvIns->pDrvReg points to it.
391 * @param pCfgHandle Configuration node handle for the driver. Use this to
392 * obtain the configuration of the driver instance. It's
393 * also found in pDrvIns->pCfgHandle as it's expected to
394 * be used frequently in this function.
395 */
396static DECLCALLBACK(int) drvHostSerialConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle)
397{
398 PDRVHOSTSERIAL pData = PDMINS2DATA(pDrvIns, PDRVHOSTSERIAL);
399 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
400
401 /*
402 * Init basic data members and interfaces.
403 */
404 pData->ReceiveThread = NIL_RTTHREAD;
405 pData->fShutdown = false;
406 /* IBase. */
407 pDrvIns->IBase.pfnQueryInterface = drvHostSerialQueryInterface;
408 /* IChar. */
409 pData->IChar.pfnWrite = drvHostSerialWrite;
410 pData->IChar.pfnSetParameters = drvHostSerialSetParameters;
411
412 /*
413 * Query configuration.
414 */
415 /* Device */
416 int rc = CFGMR3QueryStringAlloc(pCfgHandle, "DevicePath", &pData->pszDevicePath);
417 if (VBOX_FAILURE(rc))
418 {
419 AssertMsgFailed(("Configuration error: query for \"DevicePath\" string returned %Vra.\n", rc));
420 return rc;
421 }
422
423 /*
424 * Open the device
425 */
426 pData->DeviceFile = open(pData->pszDevicePath, O_RDWR | O_NONBLOCK);
427 if (pData->DeviceFile < 0) {
428
429 }
430
431 /*
432 * Get the ICharPort interface of the above driver/device.
433 */
434 pData->pDrvCharPort = (PPDMICHARPORT)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_CHAR_PORT);
435 if (!pData->pDrvCharPort)
436 return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("Char#%d has no char port interface above"), pDrvIns->iInstance);
437
438 rc = RTThreadCreate(&pData->ReceiveThread, drvHostSerialReceiveLoop, (void *)pData, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "Char Receive");
439 if (VBOX_FAILURE(rc))
440 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create receive thread"), pDrvIns->iInstance);
441
442 rc = RTSemEventCreate(&pData->SendSem);
443 AssertRC(rc);
444
445 rc = RTThreadCreate(&pData->SendThread, drvHostSerialSendLoop, (void *)pData, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "Char Send");
446 if (VBOX_FAILURE(rc))
447 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create send thread"), pDrvIns->iInstance);
448
449
450 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes written", "/Devices/Char%d/Written", pDrvIns->iInstance);
451 PDMDrvHlpSTAMRegisterF(pDrvIns, &pData->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes read", "/Devices/Char%d/Read", pDrvIns->iInstance);
452
453 return VINF_SUCCESS;
454}
455
456
457/**
458 * Destruct a char driver instance.
459 *
460 * Most VM resources are freed by the VM. This callback is provided so that
461 * any non-VM resources can be freed correctly.
462 *
463 * @param pDrvIns The driver instance data.
464 */
465static DECLCALLBACK(void) drvHostSerialDestruct(PPDMDRVINS pDrvIns)
466{
467 PDRVHOSTSERIAL pData = PDMINS2DATA(pDrvIns, PDRVHOSTSERIAL);
468
469 LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance));
470
471 pData->fShutdown = true;
472 if (pData->ReceiveThread)
473 {
474 RTThreadWait(pData->ReceiveThread, 1000, NULL);
475 if (pData->ReceiveThread != NIL_RTTHREAD)
476 LogRel(("Char%d: receive thread did not terminate\n", pDrvIns->iInstance));
477 }
478
479 /* Empty the send queue */
480 pData->iSendQueueTail = pData->iSendQueueHead = 0;
481
482 RTSemEventSignal(pData->SendSem);
483 RTSemEventDestroy(pData->SendSem);
484 pData->SendSem = NIL_RTSEMEVENT;
485
486 if (pData->SendThread)
487 {
488 RTThreadWait(pData->SendThread, 1000, NULL);
489 if (pData->SendThread != NIL_RTTHREAD)
490 LogRel(("Char%d: send thread did not terminate\n", pDrvIns->iInstance));
491 }
492}
493
494/**
495 * Char driver registration record.
496 */
497const PDMDRVREG g_DrvHostSerial =
498{
499 /* u32Version */
500 PDM_DRVREG_VERSION,
501 /* szDriverName */
502 "Host Serial",
503 /* pszDescription */
504 "Host serial driver.",
505 /* fFlags */
506 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
507 /* fClass. */
508 PDM_DRVREG_CLASS_CHAR,
509 /* cMaxInstances */
510 ~0,
511 /* cbInstance */
512 sizeof(DRVHOSTSERIAL),
513 /* pfnConstruct */
514 drvHostSerialConstruct,
515 /* pfnDestruct */
516 drvHostSerialDestruct,
517 /* pfnIOCtl */
518 NULL,
519 /* pfnPowerOn */
520 NULL,
521 /* pfnReset */
522 NULL,
523 /* pfnSuspend */
524 NULL,
525 /* pfnResume */
526 NULL,
527 /* pfnDetach */
528 NULL,
529 /** pfnPowerOff */
530 NULL
531};
532
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