VirtualBox

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

Last change on this file since 82781 was 76553, checked in by vboxsync, 5 years ago

scm --update-copyright-year

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

© 2023 Oracle
ContactPrivacy policyTerms of Use