/** * Tries to reconnect to the USB/IP host. * * @returns VBox status code. */ int USBProxyBackendUsbIp::reconnect() { /* Make sure we are disconnected. */ disconnect(); /* Connect to the USB/IP host. */ int rc = RTTcpClientConnect(m->pszHost, m->uPort, &m->hSocket); if (RT_SUCCESS(rc)) { rc = RTTcpSetSendCoalescing(m->hSocket, false); if (RT_FAILURE(rc)) LogRel(("USB/IP: Disabling send coalescing failed (rc=%Rrc), continuing nevertheless but expect increased latency\n", rc)); rc = RTPollSetAddSocket(m->hPollSet, m->hSocket, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, USBIP_POLL_ID_SOCKET); if (RT_FAILURE(rc)) { RTTcpClientCloseEx(m->hSocket, false /*fGracefulShutdown*/); m->hSocket = NIL_RTSOCKET; } } return rc; }
/** * @interface_method_impl{TXSTRANSPORT,pfnPollSetAdd} */ static DECLCALLBACK(int) txsTcpPollSetAdd(RTPOLLSET hPollSet, uint32_t idStart) { return RTPollSetAddSocket(hPollSet, g_hTcpClient, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, idStart); }
/** * Construct a TCP socket stream driver instance. * * @copydoc FNPDMDRVCONSTRUCT */ static DECLCALLBACK(int) drvTCPConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags) { RT_NOREF(fFlags); PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns); PDRVTCP pThis = PDMINS_2_DATA(pDrvIns, PDRVTCP); /* * Init the static parts. */ pThis->pDrvIns = pDrvIns; pThis->pszLocation = NULL; pThis->fIsServer = false; pThis->hTcpServ = NULL; pThis->hTcpSock = NIL_RTSOCKET; pThis->hPollSet = NIL_RTPOLLSET; pThis->hPipeWakeR = NIL_RTPIPE; pThis->hPipeWakeW = NIL_RTPIPE; pThis->fTcpSockInPollSet = false; pThis->ListenThread = NIL_RTTHREAD; pThis->fShutdown = false; /* IBase */ pDrvIns->IBase.pfnQueryInterface = drvTCPQueryInterface; /* IStream */ pThis->IStream.pfnPoll = drvTcpPoll; pThis->IStream.pfnPollInterrupt = drvTcpPollInterrupt; pThis->IStream.pfnRead = drvTcpRead; pThis->IStream.pfnWrite = drvTcpWrite; /* * Validate and read the configuration. */ PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "Location|IsServer", ""); int rc = CFGMR3QueryStringAlloc(pCfg, "Location", &pThis->pszLocation); if (RT_FAILURE(rc)) return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Configuration error: querying \"Location\" resulted in %Rrc"), rc); rc = CFGMR3QueryBool(pCfg, "IsServer", &pThis->fIsServer); if (RT_FAILURE(rc)) return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Configuration error: querying \"IsServer\" resulted in %Rrc"), rc); rc = RTPipeCreate(&pThis->hPipeWakeR, &pThis->hPipeWakeW, 0 /* fFlags */); if (RT_FAILURE(rc)) return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("DrvTCP#%d: Failed to create wake pipe"), pDrvIns->iInstance); rc = RTPollSetCreate(&pThis->hPollSet); if (RT_FAILURE(rc)) return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("DrvTCP#%d: Failed to create poll set"), pDrvIns->iInstance); rc = RTPollSetAddPipe(pThis->hPollSet, pThis->hPipeWakeR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, DRVTCP_POLLSET_ID_WAKEUP); if (RT_FAILURE(rc)) return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("DrvTCP#%d failed to add wakeup pipe for %s to poll set"), pDrvIns->iInstance, pThis->pszLocation); /* * Create/Open the socket. */ if (pThis->fIsServer) { uint32_t uPort = 0; rc = RTStrToUInt32Ex(pThis->pszLocation, NULL, 10, &uPort); if (RT_FAILURE(rc)) return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("DrvTCP#%d: The port part of the location is not a numerical value"), pDrvIns->iInstance); /** @todo Allow binding to distinct interfaces. */ rc = RTTcpServerCreateEx(NULL, uPort, &pThis->hTcpServ); if (RT_FAILURE(rc)) return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("DrvTCP#%d failed to create server socket"), pDrvIns->iInstance); rc = RTThreadCreate(&pThis->ListenThread, drvTCPListenLoop, (void *)pThis, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "DrvTCPStream"); if (RT_FAILURE(rc)) return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("DrvTCP#%d failed to create listening thread"), pDrvIns->iInstance); } else { char *pszPort = strchr(pThis->pszLocation, ':'); if (!pszPort) return PDMDrvHlpVMSetError(pDrvIns, VERR_NOT_FOUND, RT_SRC_POS, N_("DrvTCP#%d: The location misses the port to connect to"), pDrvIns->iInstance); *pszPort = '\0'; /* Overwrite temporarily to avoid copying the hostname into a temporary buffer. */ uint32_t uPort = 0; rc = RTStrToUInt32Ex(pszPort + 1, NULL, 10, &uPort); if (RT_FAILURE(rc)) return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("DrvTCP#%d: The port part of the location is not a numerical value"), pDrvIns->iInstance); rc = RTTcpClientConnect(pThis->pszLocation, uPort, &pThis->hTcpSock); *pszPort = ':'; /* Restore delimiter before checking the status. */ if (RT_FAILURE(rc)) return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("DrvTCP#%d failed to connect to socket %s"), pDrvIns->iInstance, pThis->pszLocation); rc = RTPollSetAddSocket(pThis->hPollSet, pThis->hTcpSock, RTPOLL_EVT_READ | RTPOLL_EVT_WRITE | RTPOLL_EVT_ERROR, DRVTCP_POLLSET_ID_SOCKET); if (RT_FAILURE(rc)) return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("DrvTCP#%d failed to add socket for %s to poll set"), pDrvIns->iInstance, pThis->pszLocation); pThis->fTcpSockInPollSet = true; } LogRel(("DrvTCP: %s, %s\n", pThis->pszLocation, pThis->fIsServer ? "server" : "client")); return VINF_SUCCESS; }
/** @interface_method_impl{PDMISTREAM,pfnPoll} */ static DECLCALLBACK(int) drvTcpPoll(PPDMISTREAM pInterface, uint32_t fEvts, uint32_t *pfEvts, RTMSINTERVAL cMillies) { int rc = VINF_SUCCESS; PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream); if (pThis->hTcpSock != NIL_RTSOCKET) { if (!pThis->fTcpSockInPollSet) { rc = RTPollSetAddSocket(pThis->hPollSet, pThis->hTcpSock, fEvts, DRVTCP_POLLSET_ID_SOCKET); if (RT_SUCCESS(rc)) { pThis->fTcpSockInPollSet = true; pThis->fXmitBufFull = false; } } else { /* Always include error event. */ fEvts |= RTPOLL_EVT_ERROR; rc = RTPollSetEventsChange(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET, fEvts); AssertRC(rc); } } if (RT_SUCCESS(rc)) { while (RT_SUCCESS(rc)) { uint32_t fEvtsRecv = 0; uint32_t idHnd = 0; /* * Just check for data available to be read if the send buffer wasn't full till now and * the caller wants to check whether writing is possible with the event set. * * On Windows the write event is only posted after a send operation returned * WSAEWOULDBLOCK. So without this we would block in the poll call below waiting * for an event which would never happen if the buffer has space left. */ if ( (fEvts & RTPOLL_EVT_WRITE) && !pThis->fXmitBufFull && pThis->fTcpSockInPollSet) cMillies = 0; rc = RTPoll(pThis->hPollSet, cMillies, &fEvtsRecv, &idHnd); if (RT_SUCCESS(rc)) { if (idHnd == DRVTCP_POLLSET_ID_WAKEUP) { /* We got woken up, drain the pipe and return. */ uint8_t bReason; size_t cbRead = 0; rc = RTPipeRead(pThis->hPipeWakeR, &bReason, 1, &cbRead); AssertRC(rc); if (bReason == DRVTCP_WAKEUP_REASON_EXTERNAL) rc = VERR_INTERRUPTED; else if (bReason == DRVTCP_WAKEUP_REASON_NEW_CONNECTION) { Assert(!pThis->fTcpSockInPollSet); rc = RTPollSetAddSocket(pThis->hPollSet, pThis->hTcpSock, fEvts, DRVTCP_POLLSET_ID_SOCKET); if (RT_SUCCESS(rc)) pThis->fTcpSockInPollSet = true; } else AssertMsgFailed(("Unknown wakeup reason in pipe %u\n", bReason)); } else { Assert(idHnd == DRVTCP_POLLSET_ID_SOCKET); /* On error we close the socket here. */ if (fEvtsRecv & RTPOLL_EVT_ERROR) { rc = RTPollSetRemove(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET); AssertRC(rc); if (pThis->fIsServer) RTTcpServerDisconnectClient2(pThis->hTcpSock); else RTSocketClose(pThis->hTcpSock); pThis->hTcpSock = NIL_RTSOCKET; pThis->fTcpSockInPollSet = false; /* Continue with polling. */ } else { if (fEvtsRecv & RTPOLL_EVT_WRITE) pThis->fXmitBufFull = false; else if (!pThis->fXmitBufFull) fEvtsRecv |= RTPOLL_EVT_WRITE; *pfEvts = fEvtsRecv; break; } } } else if ( rc == VERR_TIMEOUT && !pThis->fXmitBufFull) { *pfEvts = RTPOLL_EVT_WRITE; rc = VINF_SUCCESS; break; } } } return rc; }