VirtualBox

source: vbox/trunk/src/VBox/Devices/Serial/DrvTCP.cpp@ 69304

Last change on this file since 69304 was 68717, checked in by vboxsync, 7 years ago

Serial/DrvTCP: Fix for Windows hosts where the write event in a poll is only returned once initially and only after a send operation would return WSAEWOULDBLOCK

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 21.4 KB
Line 
1/* $Id: DrvTCP.cpp 68717 2017-09-11 15:55:43Z vboxsync $ */
2/** @file
3 * TCP socket driver implementing the IStream interface.
4 */
5
6/*
7 * Copyright (C) 2006-2017 Oracle Corporation.
8 *
9 * This file was contributed by Alexey Eromenko (derived from DrvNamedPipe)
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
20
21/*********************************************************************************************************************************
22* Header Files *
23*********************************************************************************************************************************/
24#define LOG_GROUP LOG_GROUP_DRV_TCP
25#include <VBox/vmm/pdmdrv.h>
26#include <iprt/assert.h>
27#include <iprt/file.h>
28#include <iprt/stream.h>
29#include <iprt/alloc.h>
30#include <iprt/pipe.h>
31#include <iprt/poll.h>
32#include <iprt/string.h>
33#include <iprt/semaphore.h>
34#include <iprt/socket.h>
35#include <iprt/tcp.h>
36#include <iprt/uuid.h>
37#include <stdlib.h>
38
39#include "VBoxDD.h"
40
41/*********************************************************************************************************************************
42* Defined Constants And Macros *
43*********************************************************************************************************************************/
44
45#define DRVTCP_POLLSET_ID_SOCKET 0
46#define DRVTCP_POLLSET_ID_WAKEUP 1
47
48#define DRVTCP_WAKEUP_REASON_EXTERNAL 0
49#define DRVTCP_WAKEUP_REASON_NEW_CONNECTION 1
50
51/*********************************************************************************************************************************
52* Structures and Typedefs *
53*********************************************************************************************************************************/
54/**
55 * TCP driver instance data.
56 *
57 * @implements PDMISTREAM
58 */
59typedef struct DRVTCP
60{
61 /** The stream interface. */
62 PDMISTREAM IStream;
63 /** Pointer to the driver instance. */
64 PPDMDRVINS pDrvIns;
65 /** Pointer to the TCP server address:port or port only. (Freed by MM) */
66 char *pszLocation;
67 /** Flag whether VirtualBox represents the server or client side. */
68 bool fIsServer;
69
70 /** Handle of the TCP server for incoming connections. */
71 PRTTCPSERVER hTcpServ;
72 /** Socket handle of the TCP socket connection. */
73 RTSOCKET hTcpSock;
74
75 /** Poll set used to wait for I/O events. */
76 RTPOLLSET hPollSet;
77 /** Reading end of the wakeup pipe. */
78 RTPIPE hPipeWakeR;
79 /** Writing end of the wakeup pipe. */
80 RTPIPE hPipeWakeW;
81 /** Flag whether the socket is in the pollset. */
82 bool fTcpSockInPollSet;
83 /** Flag whether the send buffer is full nad it is required to wait for more
84 * space until there is room again. */
85 bool fXmitBufFull;
86
87 /** Thread for listening for new connections. */
88 RTTHREAD ListenThread;
89 /** Flag to signal listening thread to shut down. */
90 bool volatile fShutdown;
91} DRVTCP, *PDRVTCP;
92
93
94/*********************************************************************************************************************************
95* Internal Functions *
96*********************************************************************************************************************************/
97
98
99/**
100 * Kicks any possibly polling thread to get informed about changes.
101 *
102 * @returns VBOx status code.
103 * @param pThis The TCP driver instance.
104 * @param bReason The reason code to handle.
105 */
106static int drvTcpPollerKick(PDRVTCP pThis, uint8_t bReason)
107{
108 size_t cbWritten = 0;
109 return RTPipeWrite(pThis->hPipeWakeW, &bReason, 1, &cbWritten);
110}
111
112
113/** @interface_method_impl{PDMISTREAM,pfnPoll} */
114static DECLCALLBACK(int) drvTcpPoll(PPDMISTREAM pInterface, uint32_t fEvts, uint32_t *pfEvts, RTMSINTERVAL cMillies)
115{
116 int rc = VINF_SUCCESS;
117 PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream);
118
119 if (pThis->hTcpSock != NIL_RTSOCKET)
120 {
121 if (!pThis->fTcpSockInPollSet)
122 {
123 rc = RTPollSetAddSocket(pThis->hPollSet, pThis->hTcpSock,
124 fEvts, DRVTCP_POLLSET_ID_SOCKET);
125 if (RT_SUCCESS(rc))
126 {
127 pThis->fTcpSockInPollSet = true;
128 pThis->fXmitBufFull = false;
129 }
130 }
131 else
132 {
133 /*
134 * Just return if the send buffer wasn't full till now and
135 * the caller wants to check whether writing is possible with
136 * the event set.
137 *
138 * On Windows the write event is only posted after a send operation returned
139 * WSAEWOULDBLOCK. So without this we would block in the poll call below waiting
140 * for an event which would never happen if the buffer has space left.
141 */
142 if ( (fEvts & RTPOLL_EVT_WRITE)
143 && !pThis->fXmitBufFull)
144 {
145 *pfEvts = RTPOLL_EVT_WRITE;
146 return VINF_SUCCESS;
147 }
148
149 /* Always include error event. */
150 fEvts |= RTPOLL_EVT_ERROR;
151 rc = RTPollSetEventsChange(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET, fEvts);
152 AssertRC(rc);
153 }
154 }
155
156 if (RT_SUCCESS(rc))
157 {
158 while (RT_SUCCESS(rc))
159 {
160 uint32_t fEvtsRecv = 0;
161 uint32_t idHnd = 0;
162
163 rc = RTPoll(pThis->hPollSet, cMillies, &fEvtsRecv, &idHnd);
164 if (RT_SUCCESS(rc))
165 {
166 if (idHnd == DRVTCP_POLLSET_ID_WAKEUP)
167 {
168 /* We got woken up, drain the pipe and return. */
169 uint8_t bReason;
170 size_t cbRead = 0;
171 rc = RTPipeRead(pThis->hPipeWakeR, &bReason, 1, &cbRead);
172 AssertRC(rc);
173
174 if (bReason == DRVTCP_WAKEUP_REASON_EXTERNAL)
175 rc = VERR_INTERRUPTED;
176 else if (bReason == DRVTCP_WAKEUP_REASON_NEW_CONNECTION)
177 {
178 Assert(!pThis->fTcpSockInPollSet);
179 rc = RTPollSetAddSocket(pThis->hPollSet, pThis->hTcpSock,
180 fEvts, DRVTCP_POLLSET_ID_SOCKET);
181 if (RT_SUCCESS(rc))
182 pThis->fTcpSockInPollSet = true;
183 }
184 else
185 AssertMsgFailed(("Unknown wakeup reason in pipe %u\n", bReason));
186 }
187 else
188 {
189 Assert(idHnd == DRVTCP_POLLSET_ID_SOCKET);
190
191 /* On error we close the socket here. */
192 if (fEvtsRecv & RTPOLL_EVT_ERROR)
193 {
194 rc = RTPollSetRemove(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET);
195 AssertRC(rc);
196
197 if (pThis->fIsServer)
198 RTTcpServerDisconnectClient2(pThis->hTcpSock);
199 else
200 RTSocketClose(pThis->hTcpSock);
201 pThis->hTcpSock = NIL_RTSOCKET;
202 pThis->fTcpSockInPollSet = false;
203 /* Continue with polling. */
204 }
205 else
206 {
207 if (fEvtsRecv & RTPOLL_EVT_WRITE)
208 pThis->fXmitBufFull = false;
209 *pfEvts = fEvtsRecv;
210 break;
211 }
212 }
213 }
214 }
215 }
216
217 return rc;
218}
219
220
221/** @interface_method_impl{PDMISTREAM,pfnPollInterrupt} */
222static DECLCALLBACK(int) drvTcpPollInterrupt(PPDMISTREAM pInterface)
223{
224 PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream);
225 return drvTcpPollerKick(pThis, DRVTCP_WAKEUP_REASON_EXTERNAL);
226}
227
228
229/** @interface_method_impl{PDMISTREAM,pfnRead} */
230static DECLCALLBACK(int) drvTcpRead(PPDMISTREAM pInterface, void *pvBuf, size_t *pcbRead)
231{
232 int rc = VINF_SUCCESS;
233 PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream);
234 LogFlow(("%s: pvBuf=%p *pcbRead=%#x (%s)\n", __FUNCTION__, pvBuf, *pcbRead, pThis->pszLocation));
235
236 Assert(pvBuf);
237
238 if (pThis->hTcpSock != NIL_RTSOCKET)
239 {
240 size_t cbRead;
241 size_t cbBuf = *pcbRead;
242 rc = RTSocketReadNB(pThis->hTcpSock, pvBuf, cbBuf, &cbRead);
243 if (RT_SUCCESS(rc))
244 {
245 if (!cbRead && rc != VINF_TRY_AGAIN)
246 {
247 rc = RTPollSetRemove(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET);
248 AssertRC(rc);
249
250 if (pThis->fIsServer)
251 RTTcpServerDisconnectClient2(pThis->hTcpSock);
252 else
253 RTSocketClose(pThis->hTcpSock);
254 pThis->hTcpSock = NIL_RTSOCKET;
255 pThis->fTcpSockInPollSet = false;
256 rc = VINF_SUCCESS;
257 }
258 *pcbRead = cbRead;
259 }
260 }
261 else
262 {
263 RTThreadSleep(100);
264 *pcbRead = 0;
265 }
266
267 LogFlow(("%s: *pcbRead=%zu returns %Rrc\n", __FUNCTION__, *pcbRead, rc));
268 return rc;
269}
270
271
272/** @interface_method_impl{PDMISTREAM,pfnWrite} */
273static DECLCALLBACK(int) drvTcpWrite(PPDMISTREAM pInterface, const void *pvBuf, size_t *pcbWrite)
274{
275 int rc = VINF_SUCCESS;
276 PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream);
277 LogFlow(("%s: pvBuf=%p *pcbWrite=%#x (%s)\n", __FUNCTION__, pvBuf, *pcbWrite, pThis->pszLocation));
278
279 Assert(pvBuf);
280 if (pThis->hTcpSock != NIL_RTSOCKET)
281 {
282 size_t cbBuf = *pcbWrite;
283 rc = RTSocketWriteNB(pThis->hTcpSock, pvBuf, cbBuf, pcbWrite);
284 if (rc == VINF_TRY_AGAIN)
285 {
286 Assert(*pcbWrite == 0);
287 pThis->fXmitBufFull = true;
288 rc = VERR_TIMEOUT;
289 }
290 }
291 else
292 *pcbWrite = 0;
293
294 LogFlow(("%s: returns %Rrc *pcbWrite=%zu\n", __FUNCTION__, rc, *pcbWrite));
295 return rc;
296}
297
298
299/**
300 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
301 */
302static DECLCALLBACK(void *) drvTCPQueryInterface(PPDMIBASE pInterface, const char *pszIID)
303{
304 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
305 PDRVTCP pThis = PDMINS_2_DATA(pDrvIns, PDRVTCP);
306 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
307 PDMIBASE_RETURN_INTERFACE(pszIID, PDMISTREAM, &pThis->IStream);
308 return NULL;
309}
310
311
312/* -=-=-=-=- listen thread -=-=-=-=- */
313
314/**
315 * Receive thread loop.
316 *
317 * @returns VINF_SUCCESS
318 * @param hThreadSelf Thread handle to this thread.
319 * @param pvUser User argument.
320 */
321static DECLCALLBACK(int) drvTCPListenLoop(RTTHREAD hThreadSelf, void *pvUser)
322{
323 RT_NOREF(hThreadSelf);
324 PDRVTCP pThis = (PDRVTCP)pvUser;
325
326 while (RT_LIKELY(!pThis->fShutdown))
327 {
328 RTSOCKET hTcpSockNew = NIL_RTSOCKET;
329 int rc = RTTcpServerListen2(pThis->hTcpServ, &hTcpSockNew);
330 if (RT_SUCCESS(rc))
331 {
332 if (pThis->hTcpSock != NIL_RTSOCKET)
333 {
334 LogRel(("DrvTCP%d: only single connection supported\n", pThis->pDrvIns->iInstance));
335 RTTcpServerDisconnectClient2(hTcpSockNew);
336 }
337 else
338 {
339 pThis->hTcpSock = hTcpSockNew;
340 /* Inform the poller about the new socket. */
341 drvTcpPollerKick(pThis, DRVTCP_WAKEUP_REASON_NEW_CONNECTION);
342 }
343 }
344 }
345
346 return VINF_SUCCESS;
347}
348
349/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
350
351/**
352 * Common worker for drvTCPPowerOff and drvTCPDestructor.
353 *
354 * @param pThis The instance data.
355 */
356static void drvTCPShutdownListener(PDRVTCP pThis)
357{
358 /*
359 * Signal shutdown of the listener thread.
360 */
361 pThis->fShutdown = true;
362 if ( pThis->fIsServer
363 && pThis->hTcpServ != NULL)
364 {
365 int rc = RTTcpServerShutdown(pThis->hTcpServ);
366 AssertRC(rc);
367 pThis->hTcpServ = NULL;
368 }
369}
370
371
372/**
373 * Power off a TCP socket stream driver instance.
374 *
375 * This does most of the destruction work, to avoid ordering dependencies.
376 *
377 * @param pDrvIns The driver instance data.
378 */
379static DECLCALLBACK(void) drvTCPPowerOff(PPDMDRVINS pDrvIns)
380{
381 PDRVTCP pThis = PDMINS_2_DATA(pDrvIns, PDRVTCP);
382 LogFlow(("%s: %s\n", __FUNCTION__, pThis->pszLocation));
383
384 drvTCPShutdownListener(pThis);
385}
386
387
388/**
389 * Destruct a TCP socket stream driver instance.
390 *
391 * Most VM resources are freed by the VM. This callback is provided so that
392 * any non-VM resources can be freed correctly.
393 *
394 * @param pDrvIns The driver instance data.
395 */
396static DECLCALLBACK(void) drvTCPDestruct(PPDMDRVINS pDrvIns)
397{
398 PDRVTCP pThis = PDMINS_2_DATA(pDrvIns, PDRVTCP);
399 LogFlow(("%s: %s\n", __FUNCTION__, pThis->pszLocation));
400 PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
401
402 drvTCPShutdownListener(pThis);
403
404 /*
405 * While the thread exits, clean up as much as we can.
406 */
407 if (pThis->hTcpSock != NIL_RTSOCKET)
408 {
409 int rc = RTPollSetRemove(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET);
410 AssertRC(rc);
411
412 rc = RTSocketShutdown(pThis->hTcpSock, true /* fRead */, true /* fWrite */);
413 AssertRC(rc);
414
415 rc = RTSocketClose(pThis->hTcpSock);
416 AssertRC(rc); RT_NOREF(rc);
417
418 pThis->hTcpSock = NIL_RTSOCKET;
419 }
420
421 if (pThis->hPipeWakeR != NIL_RTPIPE)
422 {
423 int rc = RTPipeClose(pThis->hPipeWakeR);
424 AssertRC(rc);
425
426 pThis->hPipeWakeR = NIL_RTPIPE;
427 }
428
429 if (pThis->hPipeWakeW != NIL_RTPIPE)
430 {
431 int rc = RTPipeClose(pThis->hPipeWakeW);
432 AssertRC(rc);
433
434 pThis->hPipeWakeW = NIL_RTPIPE;
435 }
436
437 if (pThis->hPollSet != NIL_RTPOLLSET)
438 {
439 int rc = RTPollSetDestroy(pThis->hPollSet);
440 AssertRC(rc);
441
442 pThis->hPollSet = NIL_RTPOLLSET;
443 }
444
445 MMR3HeapFree(pThis->pszLocation);
446 pThis->pszLocation = NULL;
447
448 /*
449 * Wait for the thread.
450 */
451 if (pThis->ListenThread != NIL_RTTHREAD)
452 {
453 int rc = RTThreadWait(pThis->ListenThread, 30000, NULL);
454 if (RT_SUCCESS(rc))
455 pThis->ListenThread = NIL_RTTHREAD;
456 else
457 LogRel(("DrvTCP%d: listen thread did not terminate (%Rrc)\n", pDrvIns->iInstance, rc));
458 }
459}
460
461
462/**
463 * Construct a TCP socket stream driver instance.
464 *
465 * @copydoc FNPDMDRVCONSTRUCT
466 */
467static DECLCALLBACK(int) drvTCPConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
468{
469 RT_NOREF(fFlags);
470 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
471 PDRVTCP pThis = PDMINS_2_DATA(pDrvIns, PDRVTCP);
472
473 /*
474 * Init the static parts.
475 */
476 pThis->pDrvIns = pDrvIns;
477 pThis->pszLocation = NULL;
478 pThis->fIsServer = false;
479
480 pThis->hTcpServ = NULL;
481 pThis->hTcpSock = NIL_RTSOCKET;
482
483 pThis->hPollSet = NIL_RTPOLLSET;
484 pThis->hPipeWakeR = NIL_RTPIPE;
485 pThis->hPipeWakeW = NIL_RTPIPE;
486 pThis->fTcpSockInPollSet = false;
487
488 pThis->ListenThread = NIL_RTTHREAD;
489 pThis->fShutdown = false;
490 /* IBase */
491 pDrvIns->IBase.pfnQueryInterface = drvTCPQueryInterface;
492 /* IStream */
493 pThis->IStream.pfnPoll = drvTcpPoll;
494 pThis->IStream.pfnPollInterrupt = drvTcpPollInterrupt;
495 pThis->IStream.pfnRead = drvTcpRead;
496 pThis->IStream.pfnWrite = drvTcpWrite;
497
498 /*
499 * Validate and read the configuration.
500 */
501 PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "Location|IsServer", "");
502
503 int rc = CFGMR3QueryStringAlloc(pCfg, "Location", &pThis->pszLocation);
504 if (RT_FAILURE(rc))
505 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
506 N_("Configuration error: querying \"Location\" resulted in %Rrc"), rc);
507 rc = CFGMR3QueryBool(pCfg, "IsServer", &pThis->fIsServer);
508 if (RT_FAILURE(rc))
509 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
510 N_("Configuration error: querying \"IsServer\" resulted in %Rrc"), rc);
511
512 rc = RTPipeCreate(&pThis->hPipeWakeR, &pThis->hPipeWakeW, 0 /* fFlags */);
513 if (RT_FAILURE(rc))
514 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
515 N_("DrvTCP#%d: Failed to create wake pipe"), pDrvIns->iInstance);
516
517 rc = RTPollSetCreate(&pThis->hPollSet);
518 if (RT_FAILURE(rc))
519 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
520 N_("DrvTCP#%d: Failed to create poll set"), pDrvIns->iInstance);
521
522 rc = RTPollSetAddPipe(pThis->hPollSet, pThis->hPipeWakeR,
523 RTPOLL_EVT_READ | RTPOLL_EVT_ERROR,
524 DRVTCP_POLLSET_ID_WAKEUP);
525 if (RT_FAILURE(rc))
526 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
527 N_("DrvTCP#%d failed to add wakeup pipe for %s to poll set"),
528 pDrvIns->iInstance, pThis->pszLocation);
529
530 /*
531 * Create/Open the socket.
532 */
533 if (pThis->fIsServer)
534 {
535 uint32_t uPort = 0;
536 rc = RTStrToUInt32Ex(pThis->pszLocation, NULL, 10, &uPort);
537 if (RT_FAILURE(rc))
538 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
539 N_("DrvTCP#%d: The port part of the location is not a numerical value"),
540 pDrvIns->iInstance);
541
542 /** @todo: Allow binding to distinct interfaces. */
543 rc = RTTcpServerCreateEx(NULL, uPort, &pThis->hTcpServ);
544 if (RT_FAILURE(rc))
545 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
546 N_("DrvTCP#%d failed to create server socket"), pDrvIns->iInstance);
547
548 rc = RTThreadCreate(&pThis->ListenThread, drvTCPListenLoop, (void *)pThis, 0,
549 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "DrvTCPStream");
550 if (RT_FAILURE(rc))
551 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
552 N_("DrvTCP#%d failed to create listening thread"), pDrvIns->iInstance);
553 }
554 else
555 {
556 char *pszPort = strchr(pThis->pszLocation, ':');
557 if (!pszPort)
558 return PDMDrvHlpVMSetError(pDrvIns, VERR_NOT_FOUND, RT_SRC_POS,
559 N_("DrvTCP#%d: The location misses the port to connect to"),
560 pDrvIns->iInstance);
561
562 *pszPort = '\0'; /* Overwrite temporarily to avoid copying the hostname into a temporary buffer. */
563 uint32_t uPort = 0;
564 rc = RTStrToUInt32Ex(pszPort + 1, NULL, 10, &uPort);
565 if (RT_FAILURE(rc))
566 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
567 N_("DrvTCP#%d: The port part of the location is not a numerical value"),
568 pDrvIns->iInstance);
569
570 rc = RTTcpClientConnect(pThis->pszLocation, uPort, &pThis->hTcpSock);
571 *pszPort = ':'; /* Restore delimiter before checking the status. */
572 if (RT_FAILURE(rc))
573 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
574 N_("DrvTCP#%d failed to connect to socket %s"),
575 pDrvIns->iInstance, pThis->pszLocation);
576
577 rc = RTPollSetAddSocket(pThis->hPollSet, pThis->hTcpSock,
578 RTPOLL_EVT_READ | RTPOLL_EVT_WRITE | RTPOLL_EVT_ERROR,
579 DRVTCP_POLLSET_ID_SOCKET);
580 if (RT_FAILURE(rc))
581 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
582 N_("DrvTCP#%d failed to add socket for %s to poll set"),
583 pDrvIns->iInstance, pThis->pszLocation);
584
585 pThis->fTcpSockInPollSet = true;
586 }
587
588 LogRel(("DrvTCP: %s, %s\n", pThis->pszLocation, pThis->fIsServer ? "server" : "client"));
589 return VINF_SUCCESS;
590}
591
592
593/**
594 * TCP stream driver registration record.
595 */
596const PDMDRVREG g_DrvTCP =
597{
598 /* u32Version */
599 PDM_DRVREG_VERSION,
600 /* szName */
601 "TCP",
602 /* szRCMod */
603 "",
604 /* szR0Mod */
605 "",
606 /* pszDescription */
607 "TCP serial stream driver.",
608 /* fFlags */
609 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
610 /* fClass. */
611 PDM_DRVREG_CLASS_STREAM,
612 /* cMaxInstances */
613 ~0U,
614 /* cbInstance */
615 sizeof(DRVTCP),
616 /* pfnConstruct */
617 drvTCPConstruct,
618 /* pfnDestruct */
619 drvTCPDestruct,
620 /* pfnRelocate */
621 NULL,
622 /* pfnIOCtl */
623 NULL,
624 /* pfnPowerOn */
625 NULL,
626 /* pfnReset */
627 NULL,
628 /* pfnSuspend */
629 NULL,
630 /* pfnResume */
631 NULL,
632 /* pfnAttach */
633 NULL,
634 /* pfnDetach */
635 NULL,
636 /* pfnPowerOff */
637 drvTCPPowerOff,
638 /* pfnSoftReset */
639 NULL,
640 /* u32EndVersion */
641 PDM_DRVREG_VERSION
642};
643
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