VirtualBox

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

Last change on this file since 75265 was 73618, checked in by vboxsync, 6 years ago

Devices/Serial/DrvTCP: Fixed stalling reads on Windows hosts caused by slightly different behavior of RTPoll() there, also don't let the guest starve if there is data to read by exiting to early if the transmit buffer isn't full

  • 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 73618 2018-08-10 14:16:56Z 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-2017 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.

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