VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/generic/USBProxyBackendUsbIp.cpp@ 98110

Last change on this file since 98110 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.5 KB
Line 
1/* $Id: USBProxyBackendUsbIp.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * VirtualBox USB Proxy Backend, USB/IP.
4 */
5
6/*
7 * Copyright (C) 2015-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_MAIN_USBPROXYBACKEND
33#include "USBProxyService.h"
34#include "USBGetDevices.h"
35#include "LoggingNew.h"
36
37#include <VBox/usb.h>
38#include <VBox/usblib.h>
39#include <VBox/err.h>
40
41#include <iprt/string.h>
42#include <iprt/alloc.h>
43#include <iprt/assert.h>
44#include <iprt/ctype.h>
45#include <iprt/tcp.h>
46#include <iprt/env.h>
47#include <iprt/err.h>
48#include <iprt/mem.h>
49#include <iprt/pipe.h>
50#include <iprt/asm.h>
51#include <iprt/cdefs.h>
52#include <iprt/time.h>
53
54/** The USB/IP default port to connect to. */
55#define USBIP_PORT_DEFAULT 3240
56/** The USB version number used for the protocol. */
57#define USBIP_VERSION UINT16_C(0x0111)
58/** Request indicator in the command code. */
59#define USBIP_INDICATOR_REQ RT_BIT(15)
60
61/** Command/Reply code for OP_REQ/RET_DEVLIST. */
62#define USBIP_REQ_RET_DEVLIST UINT16_C(5)
63
64/** @todo Duplicate code in USBProxyDevice-usbip.cpp */
65/**
66 * Exported device entry in the OP_RET_DEVLIST reply.
67 */
68#pragma pack(1)
69typedef struct UsbIpExportedDevice
70{
71 /** Path of the device, zero terminated string. */
72 char szPath[256];
73 /** Bus ID of the exported device, zero terminated string. */
74 char szBusId[32];
75 /** Bus number. */
76 uint32_t u32BusNum;
77 /** Device number. */
78 uint32_t u32DevNum;
79 /** Speed indicator of the device. */
80 uint32_t u32Speed;
81 /** Vendor ID of the device. */
82 uint16_t u16VendorId;
83 /** Product ID of the device. */
84 uint16_t u16ProductId;
85 /** Device release number. */
86 uint16_t u16BcdDevice;
87 /** Device class. */
88 uint8_t bDeviceClass;
89 /** Device Subclass. */
90 uint8_t bDeviceSubClass;
91 /** Device protocol. */
92 uint8_t bDeviceProtocol;
93 /** Configuration value. */
94 uint8_t bConfigurationValue;
95 /** Current configuration value of the device. */
96 uint8_t bNumConfigurations;
97 /** Number of interfaces for the device. */
98 uint8_t bNumInterfaces;
99} UsbIpExportedDevice;
100/** Pointer to a exported device entry. */
101typedef UsbIpExportedDevice *PUsbIpExportedDevice;
102#pragma pack()
103AssertCompileSize(UsbIpExportedDevice, 312);
104
105/**
106 * Interface descriptor entry for an exported device.
107 */
108#pragma pack(1)
109typedef struct UsbIpDeviceInterface
110{
111 /** Intefrace class. */
112 uint8_t bInterfaceClass;
113 /** Interface sub class. */
114 uint8_t bInterfaceSubClass;
115 /** Interface protocol identifier. */
116 uint8_t bInterfaceProtocol;
117 /** Padding byte for alignment. */
118 uint8_t bPadding;
119} UsbIpDeviceInterface;
120/** Pointer to an interface descriptor entry. */
121typedef UsbIpDeviceInterface *PUsbIpDeviceInterface;
122#pragma pack()
123
124/**
125 * USB/IP device list request.
126 */
127#pragma pack(1)
128typedef struct UsbIpReqDevList
129{
130 /** Protocol version number. */
131 uint16_t u16Version;
132 /** Command code. */
133 uint16_t u16Cmd;
134 /** Status field, unused. */
135 int32_t i32Status;
136} UsbIpReqDevList;
137/** Pointer to a device list request. */
138typedef UsbIpReqDevList *PUsbIpReqDevList;
139#pragma pack()
140
141/**
142 * USB/IP Import reply.
143 *
144 * This is only the header, for successful
145 * requests the device details are sent to as
146 * defined in UsbIpExportedDevice.
147 */
148#pragma pack(1)
149typedef struct UsbIpRetDevList
150{
151 /** Protocol version number. */
152 uint16_t u16Version;
153 /** Command code. */
154 uint16_t u16Cmd;
155 /** Status field, unused. */
156 int32_t i32Status;
157 /** Number of exported devices. */
158 uint32_t u32DevicesExported;
159} UsbIpRetDevList;
160/** Pointer to a import reply. */
161typedef UsbIpRetDevList *PUsbIpRetDevList;
162#pragma pack()
163
164/** Pollset id of the socket. */
165#define USBIP_POLL_ID_SOCKET 0
166/** Pollset id of the pipe. */
167#define USBIP_POLL_ID_PIPE 1
168
169/** @name USB/IP error codes.
170 * @{ */
171/** Success indicator. */
172#define USBIP_STATUS_SUCCESS INT32_C(0)
173/** @} */
174
175/** @name USB/IP device speeds.
176 * @{ */
177/** Unknown speed. */
178#define USBIP_SPEED_UNKNOWN 0
179/** Low (1.0) speed. */
180#define USBIP_SPEED_LOW 1
181/** Full (1.1) speed. */
182#define USBIP_SPEED_FULL 2
183/** High (2.0) speed. */
184#define USBIP_SPEED_HIGH 3
185/** Variable (CWUSB) speed. */
186#define USBIP_SPEED_WIRELESS 4
187/** Super (3.0) speed. */
188#define USBIP_SPEED_SUPER 5
189/** @} */
190
191/**
192 * Private USB/IP proxy backend data.
193 */
194struct USBProxyBackendUsbIp::Data
195{
196 Data()
197 : hSocket(NIL_RTSOCKET),
198 hWakeupPipeR(NIL_RTPIPE),
199 hWakeupPipeW(NIL_RTPIPE),
200 hPollSet(NIL_RTPOLLSET),
201 uPort(USBIP_PORT_DEFAULT),
202 pszHost(NULL),
203 hMtxDevices(NIL_RTSEMFASTMUTEX),
204 cUsbDevicesCur(0),
205 pUsbDevicesCur(NULL),
206 enmRecvState(kUsbIpRecvState_Invalid),
207 cbResidualRecv(0),
208 pbRecvBuf(NULL),
209 cDevicesLeft(0),
210 pHead(NULL),
211 ppNext(&pHead)
212 { }
213
214 /** Socket handle to the server. */
215 RTSOCKET hSocket;
216 /** Pipe used to interrupt wait(), the read end. */
217 RTPIPE hWakeupPipeR;
218 /** Pipe used to interrupt wait(), the write end. */
219 RTPIPE hWakeupPipeW;
220 /** Pollset for the socket and wakeup pipe. */
221 RTPOLLSET hPollSet;
222 /** Port of the USB/IP host to connect to. */
223 uint32_t uPort;
224 /** USB/IP host address. */
225 char *pszHost;
226 /** Mutex protecting the device list against concurrent access. */
227 RTSEMFASTMUTEX hMtxDevices;
228 /** Number of devices in the list. */
229 uint32_t cUsbDevicesCur;
230 /** The current list of devices to compare with. */
231 PUSBDEVICE pUsbDevicesCur;
232 /** Current receive state. */
233 USBIPRECVSTATE enmRecvState;
234 /** Scratch space for holding the data until it was completely received.
235 * Which one to access is based on the current receive state. */
236 union
237 {
238 UsbIpRetDevList RetDevList;
239 UsbIpExportedDevice ExportedDevice;
240 UsbIpDeviceInterface DeviceInterface;
241 /** Byte view. */
242 uint8_t abRecv[1];
243 } Scratch;
244 /** Residual number of bytes to receive before we can work with the data. */
245 size_t cbResidualRecv;
246 /** Current pointer into the scratch buffer. */
247 uint8_t *pbRecvBuf;
248 /** Number of devices left to receive for the current request. */
249 uint32_t cDevicesLeft;
250 /** Number of interfaces to skip during receive. */
251 uint32_t cInterfacesLeft;
252 /** The current head pointer for the new device list. */
253 PUSBDEVICE pHead;
254 /** The next pointer to add a device to. */
255 PUSBDEVICE *ppNext;
256 /** Current amount of devices in the list. */
257 uint32_t cDevicesCur;
258 /** Timestamp of the last time we successfully connected. */
259 uint64_t tsConnectSuccessLast;
260};
261
262/**
263 * Convert the given exported device structure from host to network byte order.
264 *
265 * @returns nothing.
266 * @param pDevice The device structure to convert.
267 */
268DECLINLINE(void) usbProxyBackendUsbIpExportedDeviceN2H(PUsbIpExportedDevice pDevice)
269{
270 pDevice->u32BusNum = RT_N2H_U32(pDevice->u32BusNum);
271 pDevice->u32DevNum = RT_N2H_U32(pDevice->u32DevNum);
272 pDevice->u32Speed = RT_N2H_U32(pDevice->u32Speed);
273 pDevice->u16VendorId = RT_N2H_U16(pDevice->u16VendorId);
274 pDevice->u16ProductId = RT_N2H_U16(pDevice->u16ProductId);
275 pDevice->u16BcdDevice = RT_N2H_U16(pDevice->u16BcdDevice);
276}
277
278/**
279 * Initialize data members.
280 */
281USBProxyBackendUsbIp::USBProxyBackendUsbIp()
282 : USBProxyBackend()
283{
284}
285
286USBProxyBackendUsbIp::~USBProxyBackendUsbIp()
287{
288
289}
290
291/**
292 * Initializes the object (called right after construction).
293 *
294 * @returns S_OK on success and non-fatal failures, some COM error otherwise.
295 */
296int USBProxyBackendUsbIp::init(USBProxyService *pUsbProxyService, const com::Utf8Str &strId,
297 const com::Utf8Str &strAddress, bool fLoadingSettings)
298{
299 int rc = VINF_SUCCESS;
300
301 USBProxyBackend::init(pUsbProxyService, strId, strAddress, fLoadingSettings);
302
303 unconst(m_strBackend) = Utf8Str("USBIP");
304
305 m = new Data;
306
307 m->tsConnectSuccessLast = 0;
308
309 /* Split address into hostname and port. */
310 RTCList<RTCString> lstAddress = strAddress.split(":");
311 if (lstAddress.size() < 1)
312 return VERR_INVALID_PARAMETER;
313 m->pszHost = RTStrDup(lstAddress[0].c_str());
314 if (!m->pszHost)
315 return VERR_NO_STR_MEMORY;
316 if (lstAddress.size() == 2)
317 {
318 m->uPort = lstAddress[1].toUInt32();
319 if (!m->uPort)
320 return VERR_INVALID_PARAMETER;
321 }
322
323 /* Setup wakeup pipe and poll set first. */
324 rc = RTSemFastMutexCreate(&m->hMtxDevices);
325 if (RT_SUCCESS(rc))
326 {
327 rc = RTPipeCreate(&m->hWakeupPipeR, &m->hWakeupPipeW, 0);
328 if (RT_SUCCESS(rc))
329 {
330 rc = RTPollSetCreate(&m->hPollSet);
331 if (RT_SUCCESS(rc))
332 {
333 rc = RTPollSetAddPipe(m->hPollSet, m->hWakeupPipeR,
334 RTPOLL_EVT_READ, USBIP_POLL_ID_PIPE);
335 if (RT_SUCCESS(rc))
336 {
337 /*
338 * Connect to the USB/IP host. Be more graceful to connection errors
339 * if we are instantiated while the settings are loaded to let
340 * VBoxSVC start.
341 *
342 * The worker thread keeps trying to connect every few seconds until
343 * either the USB source is removed by the user or the USB server is
344 * reachable.
345 */
346 rc = reconnect();
347 if (RT_SUCCESS(rc) || fLoadingSettings)
348 rc = start(); /* Start service thread. */
349 }
350
351 if (RT_FAILURE(rc))
352 {
353 RTPollSetRemove(m->hPollSet, USBIP_POLL_ID_PIPE);
354 int rc2 = RTPollSetDestroy(m->hPollSet);
355 AssertRC(rc2);
356 m->hPollSet = NIL_RTPOLLSET;
357 }
358 }
359
360 if (RT_FAILURE(rc))
361 {
362 int rc2 = RTPipeClose(m->hWakeupPipeR);
363 AssertRC(rc2);
364 rc2 = RTPipeClose(m->hWakeupPipeW);
365 AssertRC(rc2);
366 m->hWakeupPipeR = m->hWakeupPipeW = NIL_RTPIPE;
367 }
368 }
369 if (RT_FAILURE(rc))
370 {
371 RTSemFastMutexDestroy(m->hMtxDevices);
372 m->hMtxDevices = NIL_RTSEMFASTMUTEX;
373 }
374 }
375
376 return rc;
377}
378
379/**
380 * Stop all service threads and free the device chain.
381 */
382void USBProxyBackendUsbIp::uninit()
383{
384 LogFlowThisFunc(("\n"));
385
386 /*
387 * Stop the service.
388 */
389 if (isActive())
390 stop();
391
392 /*
393 * Free resources.
394 */
395 if (m->hPollSet != NIL_RTPOLLSET)
396 {
397 disconnect();
398
399 int rc = RTPollSetRemove(m->hPollSet, USBIP_POLL_ID_PIPE);
400 AssertRC(rc);
401 rc = RTPollSetDestroy(m->hPollSet);
402 AssertRC(rc);
403 rc = RTPipeClose(m->hWakeupPipeR);
404 AssertRC(rc);
405 rc = RTPipeClose(m->hWakeupPipeW);
406 AssertRC(rc);
407
408 m->hPollSet = NIL_RTPOLLSET;
409 m->hWakeupPipeR = NIL_RTPIPE;
410 m->hWakeupPipeW = NIL_RTPIPE;
411 }
412
413 if (m->pszHost)
414 RTStrFree(m->pszHost);
415 if (m->hMtxDevices != NIL_RTSEMFASTMUTEX)
416 {
417 RTSemFastMutexDestroy(m->hMtxDevices);
418 m->hMtxDevices = NIL_RTSEMFASTMUTEX;
419 }
420
421 delete m;
422 USBProxyBackend::uninit();
423}
424
425
426int USBProxyBackendUsbIp::captureDevice(HostUSBDevice *aDevice)
427{
428 AssertReturn(aDevice, VERR_GENERAL_FAILURE);
429 AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
430
431 AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
432 LogFlowThisFunc(("aDevice=%s\n", aDevice->i_getName().c_str()));
433
434 /*
435 * We don't need to do anything when the device is held... fake it.
436 */
437 Assert(aDevice->i_getUnistate() == kHostUSBDeviceState_Capturing);
438 devLock.release();
439
440 return VINF_SUCCESS;
441}
442
443
444int USBProxyBackendUsbIp::releaseDevice(HostUSBDevice *aDevice)
445{
446 AssertReturn(aDevice, VERR_GENERAL_FAILURE);
447 AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
448
449 AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
450 LogFlowThisFunc(("aDevice=%s\n", aDevice->i_getName().c_str()));
451
452 /*
453 * We're not really holding it atm., just fake it.
454 */
455 Assert(aDevice->i_getUnistate() == kHostUSBDeviceState_ReleasingToHost);
456 devLock.release();
457
458 return VINF_SUCCESS;
459}
460
461
462bool USBProxyBackendUsbIp::isFakeUpdateRequired()
463{
464 return true;
465}
466
467
468int USBProxyBackendUsbIp::wait(RTMSINTERVAL aMillies)
469{
470 int rc = VINF_SUCCESS;
471 bool fDeviceListChangedOrWokenUp = false;
472
473 /* Don't start any possibly lengthy operation if we are supposed to return immediately again. */
474 if (!aMillies)
475 return VINF_SUCCESS;
476
477 /* Try to reconnect once when we enter if we lost the connection earlier. */
478 if (m->hSocket == NIL_RTSOCKET)
479 reconnect();
480
481 /* Query a new device list upon entering. */
482 if ( m->hSocket != NIL_RTSOCKET
483 && m->enmRecvState == kUsbIpRecvState_None)
484 {
485 rc = startListExportedDevicesReq();
486 if (RT_FAILURE(rc))
487 disconnect();
488 }
489
490 /*
491 * Because the USB/IP protocol doesn't specify a way to get notified about
492 * new or removed exported devices we have to poll the host periodically for
493 * a new device list and compare it with the previous one notifying the proxy
494 * service about changes.
495 */
496 while ( !fDeviceListChangedOrWokenUp
497 && (aMillies == RT_INDEFINITE_WAIT || aMillies > 0)
498 && RT_SUCCESS(rc))
499 {
500 RTMSINTERVAL msWait = aMillies;
501 uint64_t msPollStart = RTTimeMilliTS();
502 uint32_t uIdReady = 0;
503 uint32_t fEventsRecv = 0;
504
505 /* Limit the waiting time to 3sec so we can either reconnect or get a new device list. */
506 if (m->hSocket == NIL_RTSOCKET || m->enmRecvState == kUsbIpRecvState_None)
507 msWait = RT_MIN(3000, aMillies);
508
509 rc = RTPoll(m->hPollSet, msWait, &fEventsRecv, &uIdReady);
510 if (RT_SUCCESS(rc))
511 {
512 if (uIdReady == USBIP_POLL_ID_PIPE)
513 {
514 /* Drain the wakeup pipe. */
515 char bRead = 0;
516 size_t cbRead = 0;
517
518 rc = RTPipeRead(m->hWakeupPipeR, &bRead, 1, &cbRead);
519 Assert(RT_SUCCESS(rc) && cbRead == 1);
520 fDeviceListChangedOrWokenUp = true;
521 }
522 else if (uIdReady == USBIP_POLL_ID_SOCKET)
523 {
524 if (fEventsRecv & RTPOLL_EVT_READ)
525 rc = receiveData();
526 if ( RT_SUCCESS(rc)
527 && (fEventsRecv & RTPOLL_EVT_ERROR))
528 rc = VERR_NET_SHUTDOWN;
529
530 /*
531 * If we are in the none state again we received the previous request
532 * and have a new device list to compare the old against.
533 */
534 if (m->enmRecvState == kUsbIpRecvState_None)
535 {
536 if (hasDevListChanged(m->pHead))
537 fDeviceListChangedOrWokenUp = true;
538
539 /* Update to the new list in any case now that we have it anyway. */
540 RTSemFastMutexRequest(m->hMtxDevices);
541 freeDeviceList(m->pUsbDevicesCur);
542 m->cUsbDevicesCur = m->cDevicesCur;
543 m->pUsbDevicesCur = m->pHead;
544 RTSemFastMutexRelease(m->hMtxDevices);
545
546 m->pHead = NULL;
547 resetRecvState();
548 }
549
550 /* Current USB/IP server closes the connection after each request, don't abort but try again. */
551 if (rc == VERR_NET_SHUTDOWN || rc == VERR_BROKEN_PIPE || rc == VERR_NET_CONNECTION_RESET_BY_PEER)
552 {
553 Log(("USB/IP: Lost connection to host \"%s\", trying to reconnect...\n", m->pszHost));
554 disconnect();
555 rc = VINF_SUCCESS;
556 }
557 }
558 else
559 {
560 AssertMsgFailed(("Invalid poll ID returned\n"));
561 rc = VERR_INVALID_STATE;
562 }
563 aMillies -= (RTMSINTERVAL)(RTTimeMilliTS() - msPollStart);
564 }
565 else if (rc == VERR_TIMEOUT)
566 {
567 aMillies -= msWait;
568 if (aMillies)
569 {
570 /* Try to reconnect and start a new request if we lost the connection before. */
571 if (m->hSocket == NIL_RTSOCKET)
572 {
573 rc = reconnect();
574 if (RT_SUCCESS(rc))
575 rc = startListExportedDevicesReq();
576 else if ( rc == VERR_NET_SHUTDOWN
577 || rc == VERR_BROKEN_PIPE
578 || rc == VERR_NET_CONNECTION_RESET_BY_PEER
579 || rc == VERR_NET_CONNECTION_REFUSED)
580 {
581 if (hasDevListChanged(m->pHead))
582 fDeviceListChangedOrWokenUp = true;
583 rc = VINF_SUCCESS;
584 }
585 }
586 }
587 }
588 }
589
590 LogFlowFunc(("return rc=%Rrc\n", rc));
591 return rc;
592}
593
594
595int USBProxyBackendUsbIp::interruptWait(void)
596{
597 AssertReturn(!isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
598
599 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
600
601 int rc = RTPipeWriteBlocking(m->hWakeupPipeW, "", 1, NULL);
602 if (RT_SUCCESS(rc))
603 RTPipeFlush(m->hWakeupPipeW);
604 LogFlowFunc(("returning %Rrc\n", rc));
605 return rc;
606}
607
608
609PUSBDEVICE USBProxyBackendUsbIp::getDevices(void)
610{
611 PUSBDEVICE pFirst = NULL;
612 PUSBDEVICE *ppNext = &pFirst;
613
614 LogFlowThisFunc(("\n"));
615
616 /* Create a deep copy of the device list. */
617 RTSemFastMutexRequest(m->hMtxDevices);
618 PUSBDEVICE pCur = m->pUsbDevicesCur;
619 while (pCur)
620 {
621 PUSBDEVICE pNew = (PUSBDEVICE)RTMemAllocZ(sizeof(USBDEVICE));
622 if (pNew)
623 {
624 pNew->pszManufacturer = RTStrDup(pCur->pszManufacturer);
625 pNew->pszProduct = RTStrDup(pCur->pszProduct);
626 if (pCur->pszSerialNumber)
627 pNew->pszSerialNumber = RTStrDup(pCur->pszSerialNumber);
628 pNew->pszBackend = RTStrDup(pCur->pszBackend);
629 pNew->pszAddress = RTStrDup(pCur->pszAddress);
630
631 pNew->idVendor = pCur->idVendor;
632 pNew->idProduct = pCur->idProduct;
633 pNew->bcdDevice = pCur->bcdDevice;
634 pNew->bcdUSB = pCur->bcdUSB;
635 pNew->bDeviceClass = pCur->bDeviceClass;
636 pNew->bDeviceSubClass = pCur->bDeviceSubClass;
637 pNew->bDeviceProtocol = pCur->bDeviceProtocol;
638 pNew->bNumConfigurations = pCur->bNumConfigurations;
639 pNew->enmState = pCur->enmState;
640 pNew->u64SerialHash = pCur->u64SerialHash;
641 pNew->bBus = pCur->bBus;
642 pNew->bPort = pCur->bPort;
643 pNew->enmSpeed = pCur->enmSpeed;
644
645 /* link it */
646 pNew->pNext = NULL;
647 pNew->pPrev = *ppNext;
648 *ppNext = pNew;
649 ppNext = &pNew->pNext;
650 }
651
652 pCur = pCur->pNext;
653 }
654 RTSemFastMutexRelease(m->hMtxDevices);
655
656 LogFlowThisFunc(("returning %#p\n", pFirst));
657 return pFirst;
658}
659
660/**
661 * Frees a given device list.
662 *
663 * @returns nothing.
664 * @param pHead The head of the device list to free.
665 */
666void USBProxyBackendUsbIp::freeDeviceList(PUSBDEVICE pHead)
667{
668 PUSBDEVICE pNext = pHead;
669 while (pNext)
670 {
671 PUSBDEVICE pFree = pNext;
672 pNext = pNext->pNext;
673 freeDevice(pFree);
674 }
675}
676
677/**
678 * Resets the receive state to the idle state.
679 *
680 * @returns nothing.
681 */
682void USBProxyBackendUsbIp::resetRecvState()
683{
684 LogFlowFunc(("\n"));
685 freeDeviceList(m->pHead);
686 m->pHead = NULL;
687 m->ppNext = &m->pHead;
688 m->cDevicesCur = 0;
689 m->enmRecvState = kUsbIpRecvState_None;
690 m->cbResidualRecv = 0;
691 m->pbRecvBuf = &m->Scratch.abRecv[0];
692 m->cDevicesLeft = 0;
693 LogFlowFunc(("\n"));
694}
695
696/**
697 * Disconnects from the host and resets the receive state.
698 *
699 * @returns nothing.
700 */
701void USBProxyBackendUsbIp::disconnect()
702{
703 LogFlowFunc(("\n"));
704
705 if (m->hSocket != NIL_RTSOCKET)
706 {
707 int rc = RTPollSetRemove(m->hPollSet, USBIP_POLL_ID_SOCKET);
708 NOREF(rc);
709 Assert(RT_SUCCESS(rc) || rc == VERR_POLL_HANDLE_ID_NOT_FOUND);
710
711 RTTcpClientCloseEx(m->hSocket, false /*fGracefulShutdown*/);
712 m->hSocket = NIL_RTSOCKET;
713 }
714
715 resetRecvState();
716 LogFlowFunc(("returns\n"));
717}
718
719/**
720 * Tries to reconnect to the USB/IP host.
721 *
722 * @returns VBox status code.
723 */
724int USBProxyBackendUsbIp::reconnect()
725{
726 LogFlowFunc(("\n"));
727
728 /* Make sure we are disconnected. */
729 disconnect();
730
731 /* Connect to the USB/IP host. */
732 int rc = RTTcpClientConnect(m->pszHost, m->uPort, &m->hSocket);
733 if (RT_SUCCESS(rc))
734 {
735 rc = RTTcpSetSendCoalescing(m->hSocket, false);
736 if (RT_FAILURE(rc))
737 LogRelMax(5, ("USB/IP: Disabling send coalescing failed (rc=%Rrc), continuing nevertheless but expect increased latency\n", rc));
738
739 rc = RTPollSetAddSocket(m->hPollSet, m->hSocket, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR,
740 USBIP_POLL_ID_SOCKET);
741 if (RT_FAILURE(rc))
742 {
743 RTTcpClientCloseEx(m->hSocket, false /*fGracefulShutdown*/);
744 m->hSocket = NIL_RTSOCKET;
745 }
746 else
747 {
748 LogFlowFunc(("Connected to host \"%s\"\n", m->pszHost));
749 m->tsConnectSuccessLast = RTTimeMilliTS();
750 }
751 }
752 else if (m->tsConnectSuccessLast + 10 * RT_MS_1SEC < RTTimeMilliTS())
753 {
754 /* Make sure the device list is clear if we failed to reconnect for some time. */
755 RTSemFastMutexRequest(m->hMtxDevices);
756 if (m->pUsbDevicesCur)
757 {
758 freeDeviceList(m->pUsbDevicesCur);
759 m->cUsbDevicesCur = 0;
760 m->pUsbDevicesCur = NULL;
761 }
762 RTSemFastMutexRelease(m->hMtxDevices);
763 }
764
765 LogFlowFunc(("returns rc=%Rrc\n", rc));
766 return rc;
767}
768
769/**
770 * Initiates a new List Exported Devices request.
771 *
772 * @returns VBox status code.
773 */
774int USBProxyBackendUsbIp::startListExportedDevicesReq()
775{
776 int rc = VINF_SUCCESS;
777
778 LogFlowFunc(("\n"));
779
780 /*
781 * Reset the current state and reconnect in case we were called in the middle
782 * of another transfer (which should not happen).
783 */
784 Assert(m->enmRecvState == kUsbIpRecvState_None);
785 if (m->enmRecvState != kUsbIpRecvState_None)
786 rc = reconnect();
787
788 if (RT_SUCCESS(rc))
789 {
790 /* Send of the request. */
791 UsbIpReqDevList ReqDevList;
792 ReqDevList.u16Version = RT_H2N_U16(USBIP_VERSION);
793 ReqDevList.u16Cmd = RT_H2N_U16(USBIP_INDICATOR_REQ | USBIP_REQ_RET_DEVLIST);
794 ReqDevList.i32Status = RT_H2N_S32(0);
795
796 rc = RTTcpWrite(m->hSocket, &ReqDevList, sizeof(ReqDevList));
797 if (RT_SUCCESS(rc))
798 advanceState(kUsbIpRecvState_Hdr);
799 }
800
801 LogFlowFunc(("returns rc=%Rrc\n", rc));
802 return rc;
803}
804
805/**
806 * Advances the state machine to the given state.
807 *
808 * @returns nothing.
809 * @param enmRecvState The new receive state.
810 */
811void USBProxyBackendUsbIp::advanceState(USBIPRECVSTATE enmRecvState)
812{
813 LogFlowFunc(("enmRecvState=%u\n", enmRecvState));
814
815 switch (enmRecvState)
816 {
817 case kUsbIpRecvState_None:
818 break;
819 case kUsbIpRecvState_Hdr:
820 {
821 m->cbResidualRecv = sizeof(UsbIpRetDevList);
822 m->pbRecvBuf = (uint8_t *)&m->Scratch.RetDevList;
823 break;
824 }
825 case kUsbIpRecvState_ExportedDevice:
826 {
827 m->cbResidualRecv = sizeof(UsbIpExportedDevice);
828 m->pbRecvBuf = (uint8_t *)&m->Scratch.ExportedDevice;
829 break;
830 }
831 case kUsbIpRecvState_DeviceInterface:
832 {
833 m->cbResidualRecv = sizeof(UsbIpDeviceInterface);
834 m->pbRecvBuf = (uint8_t *)&m->Scratch.DeviceInterface;
835 break;
836 }
837 default:
838 AssertMsgFailed(("Invalid USB/IP receive state %d\n", enmRecvState));
839 return;
840 }
841
842 m->enmRecvState = enmRecvState;
843 LogFlowFunc(("returns\n"));
844}
845
846/**
847 * Receives data from the USB/IP host and processes it when everything for the current
848 * state was received.
849 *
850 * @returns VBox status code.
851 */
852int USBProxyBackendUsbIp::receiveData()
853{
854 int rc = VINF_SUCCESS;
855 size_t cbRecvd = 0;
856
857 LogFlowFunc(("\n"));
858
859 do
860 {
861 rc = RTTcpReadNB(m->hSocket, m->pbRecvBuf, m->cbResidualRecv, &cbRecvd);
862
863 LogFlowFunc(("RTTcpReadNB(%#p, %#p, %zu, %zu) -> %Rrc\n",
864 m->hSocket, m->pbRecvBuf, m->cbResidualRecv, cbRecvd, rc));
865
866 if ( rc == VINF_SUCCESS
867 && cbRecvd > 0)
868 {
869 m->cbResidualRecv -= cbRecvd;
870 m->pbRecvBuf += cbRecvd;
871 /* In case we received everything for the current state process the data. */
872 if (!m->cbResidualRecv)
873 {
874 rc = processData();
875 if ( RT_SUCCESS(rc)
876 && m->enmRecvState == kUsbIpRecvState_None)
877 break;
878 }
879 }
880 else if (rc == VINF_TRY_AGAIN)
881 Assert(!cbRecvd);
882
883 } while (rc == VINF_SUCCESS && cbRecvd > 0);
884
885 if (rc == VINF_TRY_AGAIN)
886 rc = VINF_SUCCESS;
887
888 LogFlowFunc(("returns rc=%Rrc\n", rc));
889 return rc;
890}
891
892/**
893 * Processes the data in the scratch buffer based on the current state.
894 *
895 * @returns VBox status code.
896 */
897int USBProxyBackendUsbIp::processData()
898{
899 int rc = VINF_SUCCESS;
900
901 switch (m->enmRecvState)
902 {
903 case kUsbIpRecvState_Hdr:
904 {
905 /* Check that the reply matches our expectations. */
906 if ( RT_N2H_U16(m->Scratch.RetDevList.u16Version) == USBIP_VERSION
907 && RT_N2H_U16(m->Scratch.RetDevList.u16Cmd) == USBIP_REQ_RET_DEVLIST
908 && RT_N2H_S32(m->Scratch.RetDevList.i32Status) == USBIP_STATUS_SUCCESS)
909
910 {
911 /* Populate the number of exported devices in the list and go to the next state. */
912 m->cDevicesLeft = RT_N2H_U32(m->Scratch.RetDevList.u32DevicesExported);
913 if (m->cDevicesLeft)
914 advanceState(kUsbIpRecvState_ExportedDevice);
915 else
916 advanceState(kUsbIpRecvState_None);
917 }
918 else
919 {
920 LogRelMax(10, ("USB/IP: Host sent an invalid reply to the list exported device request (Version: %#x Cmd: %#x Status: %#x)\n",
921 RT_N2H_U16(m->Scratch.RetDevList.u16Version), RT_N2H_U16(m->Scratch.RetDevList.u16Cmd),
922 RT_N2H_S32(m->Scratch.RetDevList.i32Status)));
923 /* Disconnect and start over. */
924 advanceState(kUsbIpRecvState_None);
925 disconnect();
926 rc = VERR_NET_SHUTDOWN;
927 }
928 break;
929 }
930 case kUsbIpRecvState_ExportedDevice:
931 {
932 /* Create a new device and add it to the list. */
933 usbProxyBackendUsbIpExportedDeviceN2H(&m->Scratch.ExportedDevice);
934 rc = addDeviceToList(&m->Scratch.ExportedDevice);
935 if (RT_SUCCESS(rc))
936 {
937 m->cInterfacesLeft = m->Scratch.ExportedDevice.bNumInterfaces;
938 if (m->cInterfacesLeft)
939 advanceState(kUsbIpRecvState_DeviceInterface);
940 else
941 {
942 m->cDevicesLeft--;
943 if (m->cDevicesLeft)
944 advanceState(kUsbIpRecvState_ExportedDevice);
945 else
946 advanceState(kUsbIpRecvState_None);
947 }
948 }
949 break;
950 }
951 case kUsbIpRecvState_DeviceInterface:
952 {
953 /*
954 * If all interfaces for the current device were received receive the next device
955 * if there is another one left, if not we are done with the current request.
956 */
957 m->cInterfacesLeft--;
958 if (m->cInterfacesLeft)
959 advanceState(kUsbIpRecvState_DeviceInterface);
960 else
961 {
962 m->cDevicesLeft--;
963 if (m->cDevicesLeft)
964 advanceState(kUsbIpRecvState_ExportedDevice);
965 else
966 advanceState(kUsbIpRecvState_None);
967 }
968 break;
969 }
970 case kUsbIpRecvState_None:
971 default:
972 AssertMsgFailed(("Invalid USB/IP receive state %d\n", m->enmRecvState));
973 return VERR_INVALID_STATE;
974 }
975
976 return rc;
977}
978
979/**
980 * Creates a new USB device and adds it to the list.
981 *
982 * @returns VBox status code.
983 * @param pDev Pointer to the USB/IP exported device structure to take
984 * the information for the new device from.
985 */
986int USBProxyBackendUsbIp::addDeviceToList(PUsbIpExportedDevice pDev)
987{
988 int rc = VINF_SUCCESS;
989 PUSBDEVICE pNew = (PUSBDEVICE)RTMemAllocZ(sizeof(USBDEVICE));
990 if (!pNew)
991 return VERR_NO_MEMORY;
992
993 pNew->pszManufacturer = RTStrDup("");
994 pNew->pszProduct = RTStrDup("");
995 pNew->pszSerialNumber = NULL;
996 pNew->pszBackend = RTStrDup("usbip");
997
998 /* Make sure the Bus id is 0 terminated. */
999 pDev->szBusId[31] = '\0';
1000 pNew->pszAddress = RTStrAPrintf2("usbip://%s:%u:%s", m->pszHost, m->uPort, &pDev->szBusId[0]);
1001 if (RT_LIKELY(pNew->pszAddress))
1002 {
1003 pNew->idVendor = pDev->u16VendorId;
1004 pNew->idProduct = pDev->u16ProductId;
1005 pNew->bcdDevice = pDev->u16BcdDevice;
1006 pNew->bDeviceClass = pDev->bDeviceClass;
1007 pNew->bDeviceSubClass = pDev->bDeviceSubClass;
1008 pNew->bDeviceProtocol = pDev->bDeviceProtocol;
1009 pNew->bNumConfigurations = pDev->bNumConfigurations;
1010 pNew->enmState = USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
1011 pNew->u64SerialHash = 0;
1012 /** @todo The following is not correct but is required to to get USB testing working
1013 * because only the port can be part of a filter (adding the required attributes for the bus
1014 * breaks API and ABI compatibility).
1015 * Filtering by port number is required for USB testing to connect to the correct device
1016 * in case there are multiple ones.
1017 */
1018 pNew->bBus = (uint8_t)pDev->u32DevNum;
1019 pNew->bPort = (uint8_t)pDev->u32BusNum;
1020
1021 switch (pDev->u32Speed)
1022 {
1023 case USBIP_SPEED_LOW:
1024 pNew->enmSpeed = USBDEVICESPEED_LOW;
1025 pNew->bcdUSB = 1 << 8;
1026 break;
1027 case USBIP_SPEED_FULL:
1028 pNew->enmSpeed = USBDEVICESPEED_FULL;
1029 pNew->bcdUSB = 1 << 8;
1030 break;
1031 case USBIP_SPEED_HIGH:
1032 pNew->enmSpeed = USBDEVICESPEED_HIGH;
1033 pNew->bcdUSB = 2 << 8;
1034 break;
1035 case USBIP_SPEED_WIRELESS:
1036 pNew->enmSpeed = USBDEVICESPEED_VARIABLE;
1037 pNew->bcdUSB = 1 << 8;
1038 break;
1039 case USBIP_SPEED_SUPER:
1040 pNew->enmSpeed = USBDEVICESPEED_SUPER;
1041 pNew->bcdUSB = 3 << 8;
1042 break;
1043 case USBIP_SPEED_UNKNOWN:
1044 default:
1045 pNew->bcdUSB = 1 << 8;
1046 pNew->enmSpeed = USBDEVICESPEED_UNKNOWN;
1047 }
1048
1049 /* link it */
1050 pNew->pNext = NULL;
1051 pNew->pPrev = *m->ppNext;
1052 *m->ppNext = pNew;
1053 m->ppNext = &pNew->pNext;
1054 m->cDevicesCur++;
1055 }
1056 else
1057 rc = VERR_NO_STR_MEMORY;
1058
1059 if (RT_FAILURE(rc))
1060 {
1061 if (pNew->pszManufacturer)
1062 RTStrFree((char *)pNew->pszManufacturer);
1063 if (pNew->pszProduct)
1064 RTStrFree((char *)pNew->pszProduct);
1065 if (pNew->pszBackend)
1066 RTStrFree((char *)pNew->pszBackend);
1067 if (pNew->pszAddress)
1068 RTStrFree((char *)pNew->pszAddress);
1069 RTMemFree(pNew);
1070 }
1071
1072 return rc;
1073}
1074
1075/**
1076 * Compares the given device list with the current one and returns whether it has
1077 * changed.
1078 *
1079 * @returns flag whether the device list has changed compared to the current one.
1080 * @param pDevices The device list to compare the current one against.
1081 */
1082bool USBProxyBackendUsbIp::hasDevListChanged(PUSBDEVICE pDevices)
1083{
1084 /** @todo */
1085 NOREF(pDevices);
1086 return true;
1087}
1088
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