VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 2 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.4 KB
Line 
1/* $Id: USBProxyBackendUsbIp.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * VirtualBox USB Proxy Backend, USB/IP.
4 */
5
6/*
7 * Copyright (C) 2015-2024 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 * @param pDevice The device structure to convert.
266 */
267DECLINLINE(void) usbProxyBackendUsbIpExportedDeviceN2H(PUsbIpExportedDevice pDevice)
268{
269 pDevice->u32BusNum = RT_N2H_U32(pDevice->u32BusNum);
270 pDevice->u32DevNum = RT_N2H_U32(pDevice->u32DevNum);
271 pDevice->u32Speed = RT_N2H_U32(pDevice->u32Speed);
272 pDevice->u16VendorId = RT_N2H_U16(pDevice->u16VendorId);
273 pDevice->u16ProductId = RT_N2H_U16(pDevice->u16ProductId);
274 pDevice->u16BcdDevice = RT_N2H_U16(pDevice->u16BcdDevice);
275}
276
277/**
278 * Initialize data members.
279 */
280USBProxyBackendUsbIp::USBProxyBackendUsbIp()
281 : USBProxyBackend()
282{
283}
284
285USBProxyBackendUsbIp::~USBProxyBackendUsbIp()
286{
287
288}
289
290/**
291 * Initializes the object (called right after construction).
292 *
293 * @returns S_OK on success and non-fatal failures, some COM error otherwise.
294 */
295int USBProxyBackendUsbIp::init(USBProxyService *pUsbProxyService, const com::Utf8Str &strId,
296 const com::Utf8Str &strAddress, bool fLoadingSettings)
297{
298 int vrc = VINF_SUCCESS;
299
300 USBProxyBackend::init(pUsbProxyService, strId, strAddress, fLoadingSettings);
301
302 unconst(m_strBackend) = Utf8Str("USBIP");
303
304 m = new Data;
305
306 m->tsConnectSuccessLast = 0;
307
308 /* Split address into hostname and port. */
309 RTCList<RTCString> lstAddress = strAddress.split(":");
310 if (lstAddress.size() < 1)
311 return VERR_INVALID_PARAMETER;
312 m->pszHost = RTStrDup(lstAddress[0].c_str());
313 if (!m->pszHost)
314 return VERR_NO_STR_MEMORY;
315 if (lstAddress.size() == 2)
316 {
317 m->uPort = lstAddress[1].toUInt32();
318 if (!m->uPort)
319 return VERR_INVALID_PARAMETER;
320 }
321
322 /* Setup wakeup pipe and poll set first. */
323 vrc = RTSemFastMutexCreate(&m->hMtxDevices);
324 if (RT_SUCCESS(vrc))
325 {
326 vrc = RTPipeCreate(&m->hWakeupPipeR, &m->hWakeupPipeW, 0);
327 if (RT_SUCCESS(vrc))
328 {
329 vrc = RTPollSetCreate(&m->hPollSet);
330 if (RT_SUCCESS(vrc))
331 {
332 vrc = RTPollSetAddPipe(m->hPollSet, m->hWakeupPipeR, RTPOLL_EVT_READ, USBIP_POLL_ID_PIPE);
333 if (RT_SUCCESS(vrc))
334 {
335 /*
336 * Connect to the USB/IP host. Be more graceful to connection errors
337 * if we are instantiated while the settings are loaded to let
338 * VBoxSVC start.
339 *
340 * The worker thread keeps trying to connect every few seconds until
341 * either the USB source is removed by the user or the USB server is
342 * reachable.
343 */
344 vrc = reconnect();
345 if (RT_SUCCESS(vrc) || fLoadingSettings)
346 vrc = start(); /* Start service thread. */
347 }
348
349 if (RT_FAILURE(vrc))
350 {
351 RTPollSetRemove(m->hPollSet, USBIP_POLL_ID_PIPE);
352 int vrc2 = RTPollSetDestroy(m->hPollSet);
353 AssertRC(vrc2);
354 m->hPollSet = NIL_RTPOLLSET;
355 }
356 }
357
358 if (RT_FAILURE(vrc))
359 {
360 int vrc2 = RTPipeClose(m->hWakeupPipeR);
361 AssertRC(vrc2);
362 vrc2 = RTPipeClose(m->hWakeupPipeW);
363 AssertRC(vrc2);
364 m->hWakeupPipeR = m->hWakeupPipeW = NIL_RTPIPE;
365 }
366 }
367 if (RT_FAILURE(vrc))
368 {
369 RTSemFastMutexDestroy(m->hMtxDevices);
370 m->hMtxDevices = NIL_RTSEMFASTMUTEX;
371 }
372 }
373
374 return vrc;
375}
376
377/**
378 * Stop all service threads and free the device chain.
379 */
380void USBProxyBackendUsbIp::uninit()
381{
382 LogFlowThisFunc(("\n"));
383
384 /*
385 * Stop the service.
386 */
387 if (isActive())
388 stop();
389
390 /*
391 * Free resources.
392 */
393 if (m->hPollSet != NIL_RTPOLLSET)
394 {
395 disconnect();
396
397 int vrc = RTPollSetRemove(m->hPollSet, USBIP_POLL_ID_PIPE);
398 AssertRC(vrc);
399 vrc = RTPollSetDestroy(m->hPollSet);
400 AssertRC(vrc);
401 vrc = RTPipeClose(m->hWakeupPipeR);
402 AssertRC(vrc);
403 vrc = RTPipeClose(m->hWakeupPipeW);
404 AssertRC(vrc);
405
406 m->hPollSet = NIL_RTPOLLSET;
407 m->hWakeupPipeR = NIL_RTPIPE;
408 m->hWakeupPipeW = NIL_RTPIPE;
409 }
410
411 if (m->pszHost)
412 RTStrFree(m->pszHost);
413 if (m->hMtxDevices != NIL_RTSEMFASTMUTEX)
414 {
415 RTSemFastMutexDestroy(m->hMtxDevices);
416 m->hMtxDevices = NIL_RTSEMFASTMUTEX;
417 }
418
419 delete m;
420 USBProxyBackend::uninit();
421}
422
423
424int USBProxyBackendUsbIp::captureDevice(HostUSBDevice *aDevice)
425{
426 AssertReturn(aDevice, VERR_GENERAL_FAILURE);
427 AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
428
429 AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
430 LogFlowThisFunc(("aDevice=%s\n", aDevice->i_getName().c_str()));
431
432 /*
433 * We don't need to do anything when the device is held... fake it.
434 */
435 Assert(aDevice->i_getUnistate() == kHostUSBDeviceState_Capturing);
436 devLock.release();
437
438 return VINF_SUCCESS;
439}
440
441
442int USBProxyBackendUsbIp::releaseDevice(HostUSBDevice *aDevice)
443{
444 AssertReturn(aDevice, VERR_GENERAL_FAILURE);
445 AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
446
447 AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
448 LogFlowThisFunc(("aDevice=%s\n", aDevice->i_getName().c_str()));
449
450 /*
451 * We're not really holding it atm., just fake it.
452 */
453 Assert(aDevice->i_getUnistate() == kHostUSBDeviceState_ReleasingToHost);
454 devLock.release();
455
456 return VINF_SUCCESS;
457}
458
459
460bool USBProxyBackendUsbIp::isFakeUpdateRequired()
461{
462 return true;
463}
464
465
466int USBProxyBackendUsbIp::wait(RTMSINTERVAL aMillies)
467{
468 int vrc = VINF_SUCCESS;
469 bool fDeviceListChangedOrWokenUp = false;
470
471 /* Don't start any possibly lengthy operation if we are supposed to return immediately again. */
472 if (!aMillies)
473 return VINF_SUCCESS;
474
475 /* Try to reconnect once when we enter if we lost the connection earlier. */
476 if (m->hSocket == NIL_RTSOCKET)
477 reconnect();
478
479 /* Query a new device list upon entering. */
480 if ( m->hSocket != NIL_RTSOCKET
481 && m->enmRecvState == kUsbIpRecvState_None)
482 {
483 vrc = startListExportedDevicesReq();
484 if (RT_FAILURE(vrc))
485 disconnect();
486 }
487
488 /*
489 * Because the USB/IP protocol doesn't specify a way to get notified about
490 * new or removed exported devices we have to poll the host periodically for
491 * a new device list and compare it with the previous one notifying the proxy
492 * service about changes.
493 */
494 while ( !fDeviceListChangedOrWokenUp
495 && aMillies > 0
496 && RT_SUCCESS(vrc))
497 {
498 RTMSINTERVAL msWait = aMillies;
499 uint64_t msPollStart = RTTimeMilliTS();
500 uint32_t uIdReady = 0;
501 uint32_t fEventsRecv = 0;
502
503 /* Limit the waiting time to 3sec so we can either reconnect or get a new device list. */
504 if (m->hSocket == NIL_RTSOCKET || m->enmRecvState == kUsbIpRecvState_None)
505 msWait = RT_MIN(3000, aMillies);
506
507 vrc = RTPoll(m->hPollSet, msWait, &fEventsRecv, &uIdReady);
508 if (RT_SUCCESS(vrc))
509 {
510 if (uIdReady == USBIP_POLL_ID_PIPE)
511 {
512 /* Drain the wakeup pipe. */
513 char bRead = 0;
514 size_t cbRead = 0;
515
516 vrc = RTPipeRead(m->hWakeupPipeR, &bRead, 1, &cbRead);
517 Assert(RT_SUCCESS(vrc) && cbRead == 1);
518 fDeviceListChangedOrWokenUp = true;
519 }
520 else if (uIdReady == USBIP_POLL_ID_SOCKET)
521 {
522 if (fEventsRecv & RTPOLL_EVT_READ)
523 vrc = receiveData();
524 if ( RT_SUCCESS(vrc)
525 && (fEventsRecv & RTPOLL_EVT_ERROR))
526 vrc = VERR_NET_SHUTDOWN;
527
528 /*
529 * If we are in the none state again we received the previous request
530 * and have a new device list to compare the old against.
531 */
532 if (m->enmRecvState == kUsbIpRecvState_None)
533 {
534 if (hasDevListChanged(m->pHead))
535 fDeviceListChangedOrWokenUp = true;
536
537 /* Update to the new list in any case now that we have it anyway. */
538 RTSemFastMutexRequest(m->hMtxDevices);
539 freeDeviceList(m->pUsbDevicesCur);
540 m->cUsbDevicesCur = m->cDevicesCur;
541 m->pUsbDevicesCur = m->pHead;
542 RTSemFastMutexRelease(m->hMtxDevices);
543
544 m->pHead = NULL;
545 resetRecvState();
546 }
547
548 /* Current USB/IP server closes the connection after each request, don't abort but try again. */
549 if (vrc == VERR_NET_SHUTDOWN || vrc == VERR_BROKEN_PIPE || vrc == VERR_NET_CONNECTION_RESET_BY_PEER)
550 {
551 Log(("USB/IP: Lost connection to host \"%s\", trying to reconnect...\n", m->pszHost));
552 disconnect();
553 vrc = VINF_SUCCESS;
554 }
555 }
556 else
557 {
558 AssertMsgFailed(("Invalid poll ID returned\n"));
559 vrc = VERR_INVALID_STATE;
560 }
561 aMillies -= (RTMSINTERVAL)(RTTimeMilliTS() - msPollStart);
562 }
563 else if (vrc == VERR_TIMEOUT)
564 {
565 aMillies -= msWait;
566 if (aMillies)
567 {
568 /* Try to reconnect and start a new request if we lost the connection before. */
569 if (m->hSocket == NIL_RTSOCKET)
570 {
571 vrc = reconnect();
572 if (RT_SUCCESS(vrc))
573 vrc = startListExportedDevicesReq();
574 else if ( vrc == VERR_NET_SHUTDOWN
575 || vrc == VERR_BROKEN_PIPE
576 || vrc == VERR_NET_CONNECTION_RESET_BY_PEER
577 || vrc == VERR_NET_CONNECTION_REFUSED)
578 {
579 if (hasDevListChanged(m->pHead))
580 fDeviceListChangedOrWokenUp = true;
581 vrc = VINF_SUCCESS;
582 }
583 }
584 }
585 }
586 }
587
588 LogFlowFunc(("return vrc=%Rrc\n", vrc));
589 return vrc;
590}
591
592
593int USBProxyBackendUsbIp::interruptWait(void)
594{
595 AssertReturn(!isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
596
597 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
598
599 int vrc = RTPipeWriteBlocking(m->hWakeupPipeW, "", 1, NULL);
600 if (RT_SUCCESS(vrc))
601 RTPipeFlush(m->hWakeupPipeW);
602 LogFlowFunc(("returning %Rrc\n", vrc));
603 return vrc;
604}
605
606
607PUSBDEVICE USBProxyBackendUsbIp::getDevices(void)
608{
609 PUSBDEVICE pFirst = NULL;
610 PUSBDEVICE *ppNext = &pFirst;
611
612 LogFlowThisFunc(("\n"));
613
614 /* Create a deep copy of the device list. */
615 RTSemFastMutexRequest(m->hMtxDevices);
616 PUSBDEVICE pCur = m->pUsbDevicesCur;
617 while (pCur)
618 {
619 PUSBDEVICE pNew = (PUSBDEVICE)RTMemAllocZ(sizeof(USBDEVICE));
620 if (pNew)
621 {
622 pNew->pszManufacturer = RTStrDup(pCur->pszManufacturer);
623 pNew->pszProduct = RTStrDup(pCur->pszProduct);
624 if (pCur->pszSerialNumber)
625 pNew->pszSerialNumber = RTStrDup(pCur->pszSerialNumber);
626 pNew->pszBackend = RTStrDup(pCur->pszBackend);
627 pNew->pszAddress = RTStrDup(pCur->pszAddress);
628
629 pNew->idVendor = pCur->idVendor;
630 pNew->idProduct = pCur->idProduct;
631 pNew->bcdDevice = pCur->bcdDevice;
632 pNew->bcdUSB = pCur->bcdUSB;
633 pNew->bDeviceClass = pCur->bDeviceClass;
634 pNew->bDeviceSubClass = pCur->bDeviceSubClass;
635 pNew->bDeviceProtocol = pCur->bDeviceProtocol;
636 pNew->bNumConfigurations = pCur->bNumConfigurations;
637 pNew->enmState = pCur->enmState;
638 pNew->u64SerialHash = pCur->u64SerialHash;
639 pNew->bBus = pCur->bBus;
640 pNew->bPort = pCur->bPort;
641 pNew->enmSpeed = pCur->enmSpeed;
642
643 /* link it */
644 pNew->pNext = NULL;
645 pNew->pPrev = *ppNext;
646 *ppNext = pNew;
647 ppNext = &pNew->pNext;
648 }
649
650 pCur = pCur->pNext;
651 }
652 RTSemFastMutexRelease(m->hMtxDevices);
653
654 LogFlowThisFunc(("returning %#p\n", pFirst));
655 return pFirst;
656}
657
658/**
659 * Frees a given device list.
660 *
661 * @param pHead The head of the device list to free.
662 */
663void USBProxyBackendUsbIp::freeDeviceList(PUSBDEVICE pHead)
664{
665 PUSBDEVICE pNext = pHead;
666 while (pNext)
667 {
668 PUSBDEVICE pFree = pNext;
669 pNext = pNext->pNext;
670 freeDevice(pFree);
671 }
672}
673
674/**
675 * Resets the receive state to the idle state.
676 */
677void USBProxyBackendUsbIp::resetRecvState()
678{
679 LogFlowFunc(("\n"));
680 freeDeviceList(m->pHead);
681 m->pHead = NULL;
682 m->ppNext = &m->pHead;
683 m->cDevicesCur = 0;
684 m->enmRecvState = kUsbIpRecvState_None;
685 m->cbResidualRecv = 0;
686 m->pbRecvBuf = &m->Scratch.abRecv[0];
687 m->cDevicesLeft = 0;
688 LogFlowFunc(("\n"));
689}
690
691/**
692 * Disconnects from the host and resets the receive state.
693 */
694void USBProxyBackendUsbIp::disconnect()
695{
696 LogFlowFunc(("\n"));
697
698 if (m->hSocket != NIL_RTSOCKET)
699 {
700 int vrc = RTPollSetRemove(m->hPollSet, USBIP_POLL_ID_SOCKET);
701 NOREF(vrc);
702 Assert(RT_SUCCESS(vrc) || vrc == VERR_POLL_HANDLE_ID_NOT_FOUND);
703
704 RTTcpClientCloseEx(m->hSocket, false /*fGracefulShutdown*/);
705 m->hSocket = NIL_RTSOCKET;
706 }
707
708 resetRecvState();
709 LogFlowFunc(("returns\n"));
710}
711
712/**
713 * Tries to reconnect to the USB/IP host.
714 *
715 * @returns VBox status code.
716 */
717int USBProxyBackendUsbIp::reconnect()
718{
719 LogFlowFunc(("\n"));
720
721 /* Make sure we are disconnected. */
722 disconnect();
723
724 /* Connect to the USB/IP host. */
725 int vrc = RTTcpClientConnect(m->pszHost, m->uPort, &m->hSocket);
726 if (RT_SUCCESS(vrc))
727 {
728 vrc = RTTcpSetSendCoalescing(m->hSocket, false);
729 if (RT_FAILURE(vrc))
730 LogRelMax(5, ("USB/IP: Disabling send coalescing failed (vrc=%Rrc), continuing nevertheless but expect increased latency\n", vrc));
731
732 vrc = RTPollSetAddSocket(m->hPollSet, m->hSocket, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, USBIP_POLL_ID_SOCKET);
733 if (RT_FAILURE(vrc))
734 {
735 RTTcpClientCloseEx(m->hSocket, false /*fGracefulShutdown*/);
736 m->hSocket = NIL_RTSOCKET;
737 }
738 else
739 {
740 LogFlowFunc(("Connected to host \"%s\"\n", m->pszHost));
741 m->tsConnectSuccessLast = RTTimeMilliTS();
742 }
743 }
744 else if (m->tsConnectSuccessLast + 10 * RT_MS_1SEC < RTTimeMilliTS())
745 {
746 /* Make sure the device list is clear if we failed to reconnect for some time. */
747 RTSemFastMutexRequest(m->hMtxDevices);
748 if (m->pUsbDevicesCur)
749 {
750 freeDeviceList(m->pUsbDevicesCur);
751 m->cUsbDevicesCur = 0;
752 m->pUsbDevicesCur = NULL;
753 }
754 RTSemFastMutexRelease(m->hMtxDevices);
755 }
756
757 LogFlowFunc(("returns vrc=%Rrc\n", vrc));
758 return vrc;
759}
760
761/**
762 * Initiates a new List Exported Devices request.
763 *
764 * @returns VBox status code.
765 */
766int USBProxyBackendUsbIp::startListExportedDevicesReq()
767{
768 int vrc = VINF_SUCCESS;
769
770 LogFlowFunc(("\n"));
771
772 /*
773 * Reset the current state and reconnect in case we were called in the middle
774 * of another transfer (which should not happen).
775 */
776 Assert(m->enmRecvState == kUsbIpRecvState_None);
777 if (m->enmRecvState != kUsbIpRecvState_None)
778 vrc = reconnect();
779
780 if (RT_SUCCESS(vrc))
781 {
782 /* Send of the request. */
783 UsbIpReqDevList ReqDevList;
784 ReqDevList.u16Version = RT_H2N_U16(USBIP_VERSION);
785 ReqDevList.u16Cmd = RT_H2N_U16(USBIP_INDICATOR_REQ | USBIP_REQ_RET_DEVLIST);
786 ReqDevList.i32Status = RT_H2N_S32(0);
787
788 vrc = RTTcpWrite(m->hSocket, &ReqDevList, sizeof(ReqDevList));
789 if (RT_SUCCESS(vrc))
790 advanceState(kUsbIpRecvState_Hdr);
791 }
792
793 LogFlowFunc(("returns vrc=%Rrc\n", vrc));
794 return vrc;
795}
796
797/**
798 * Advances the state machine to the given state.
799 *
800 * @param enmRecvState The new receive state.
801 */
802void USBProxyBackendUsbIp::advanceState(USBIPRECVSTATE enmRecvState)
803{
804 LogFlowFunc(("enmRecvState=%u\n", enmRecvState));
805
806 switch (enmRecvState)
807 {
808 case kUsbIpRecvState_None:
809 break;
810 case kUsbIpRecvState_Hdr:
811 {
812 m->cbResidualRecv = sizeof(UsbIpRetDevList);
813 m->pbRecvBuf = (uint8_t *)&m->Scratch.RetDevList;
814 break;
815 }
816 case kUsbIpRecvState_ExportedDevice:
817 {
818 m->cbResidualRecv = sizeof(UsbIpExportedDevice);
819 m->pbRecvBuf = (uint8_t *)&m->Scratch.ExportedDevice;
820 break;
821 }
822 case kUsbIpRecvState_DeviceInterface:
823 {
824 m->cbResidualRecv = sizeof(UsbIpDeviceInterface);
825 m->pbRecvBuf = (uint8_t *)&m->Scratch.DeviceInterface;
826 break;
827 }
828 default:
829 AssertMsgFailed(("Invalid USB/IP receive state %d\n", enmRecvState));
830 return;
831 }
832
833 m->enmRecvState = enmRecvState;
834 LogFlowFunc(("returns\n"));
835}
836
837/**
838 * Receives data from the USB/IP host and processes it when everything for the current
839 * state was received.
840 *
841 * @returns VBox status code.
842 */
843int USBProxyBackendUsbIp::receiveData()
844{
845 int vrc = VINF_SUCCESS;
846 size_t cbRecvd = 0;
847
848 LogFlowFunc(("\n"));
849
850 do
851 {
852 vrc = RTTcpReadNB(m->hSocket, m->pbRecvBuf, m->cbResidualRecv, &cbRecvd);
853
854 LogFlowFunc(("RTTcpReadNB(%#p, %#p, %zu, %zu) -> %Rrc\n", m->hSocket, m->pbRecvBuf, m->cbResidualRecv, cbRecvd, vrc));
855
856 if ( vrc == VINF_SUCCESS
857 && cbRecvd > 0)
858 {
859 m->cbResidualRecv -= cbRecvd;
860 m->pbRecvBuf += cbRecvd;
861 /* In case we received everything for the current state process the data. */
862 if (!m->cbResidualRecv)
863 {
864 vrc = processData();
865 if ( RT_SUCCESS(vrc)
866 && m->enmRecvState == kUsbIpRecvState_None)
867 break;
868 }
869 }
870 else if (vrc == VINF_TRY_AGAIN)
871 Assert(!cbRecvd);
872
873 } while (vrc == VINF_SUCCESS && cbRecvd > 0);
874
875 if (vrc == VINF_TRY_AGAIN)
876 vrc = VINF_SUCCESS;
877
878 LogFlowFunc(("returns vrc=%Rrc\n", vrc));
879 return vrc;
880}
881
882/**
883 * Processes the data in the scratch buffer based on the current state.
884 *
885 * @returns VBox status code.
886 */
887int USBProxyBackendUsbIp::processData()
888{
889 int vrc = VINF_SUCCESS;
890
891 switch (m->enmRecvState)
892 {
893 case kUsbIpRecvState_Hdr:
894 {
895 /* Check that the reply matches our expectations. */
896 if ( RT_N2H_U16(m->Scratch.RetDevList.u16Version) == USBIP_VERSION
897 && RT_N2H_U16(m->Scratch.RetDevList.u16Cmd) == USBIP_REQ_RET_DEVLIST
898 && RT_N2H_S32(m->Scratch.RetDevList.i32Status) == USBIP_STATUS_SUCCESS)
899
900 {
901 /* Populate the number of exported devices in the list and go to the next state. */
902 m->cDevicesLeft = RT_N2H_U32(m->Scratch.RetDevList.u32DevicesExported);
903 if (m->cDevicesLeft)
904 advanceState(kUsbIpRecvState_ExportedDevice);
905 else
906 advanceState(kUsbIpRecvState_None);
907 }
908 else
909 {
910 LogRelMax(10, ("USB/IP: Host sent an invalid reply to the list exported device request (Version: %#x Cmd: %#x Status: %#x)\n",
911 RT_N2H_U16(m->Scratch.RetDevList.u16Version), RT_N2H_U16(m->Scratch.RetDevList.u16Cmd),
912 RT_N2H_S32(m->Scratch.RetDevList.i32Status)));
913 /* Disconnect and start over. */
914 advanceState(kUsbIpRecvState_None);
915 disconnect();
916 vrc = VERR_NET_SHUTDOWN;
917 }
918 break;
919 }
920 case kUsbIpRecvState_ExportedDevice:
921 {
922 /* Create a new device and add it to the list. */
923 usbProxyBackendUsbIpExportedDeviceN2H(&m->Scratch.ExportedDevice);
924 vrc = addDeviceToList(&m->Scratch.ExportedDevice);
925 if (RT_SUCCESS(vrc))
926 {
927 m->cInterfacesLeft = m->Scratch.ExportedDevice.bNumInterfaces;
928 if (m->cInterfacesLeft)
929 advanceState(kUsbIpRecvState_DeviceInterface);
930 else
931 {
932 m->cDevicesLeft--;
933 if (m->cDevicesLeft)
934 advanceState(kUsbIpRecvState_ExportedDevice);
935 else
936 advanceState(kUsbIpRecvState_None);
937 }
938 }
939 break;
940 }
941 case kUsbIpRecvState_DeviceInterface:
942 {
943 /*
944 * If all interfaces for the current device were received receive the next device
945 * if there is another one left, if not we are done with the current request.
946 */
947 m->cInterfacesLeft--;
948 if (m->cInterfacesLeft)
949 advanceState(kUsbIpRecvState_DeviceInterface);
950 else
951 {
952 m->cDevicesLeft--;
953 if (m->cDevicesLeft)
954 advanceState(kUsbIpRecvState_ExportedDevice);
955 else
956 advanceState(kUsbIpRecvState_None);
957 }
958 break;
959 }
960 case kUsbIpRecvState_None:
961 default:
962 AssertMsgFailed(("Invalid USB/IP receive state %d\n", m->enmRecvState));
963 return VERR_INVALID_STATE;
964 }
965
966 return vrc;
967}
968
969/**
970 * Creates a new USB device and adds it to the list.
971 *
972 * @returns VBox status code.
973 * @param pDev Pointer to the USB/IP exported device structure to take
974 * the information for the new device from.
975 */
976int USBProxyBackendUsbIp::addDeviceToList(PUsbIpExportedDevice pDev)
977{
978 int vrc = VINF_SUCCESS;
979 PUSBDEVICE pNew = (PUSBDEVICE)RTMemAllocZ(sizeof(USBDEVICE));
980 if (!pNew)
981 return VERR_NO_MEMORY;
982
983 pNew->pszManufacturer = RTStrDup("");
984 pNew->pszProduct = RTStrDup("");
985 pNew->pszSerialNumber = NULL;
986 pNew->pszBackend = RTStrDup("usbip");
987
988 /* Make sure the Bus id is 0 terminated. */
989 pDev->szBusId[31] = '\0';
990 pNew->pszAddress = RTStrAPrintf2("usbip://%s:%u:%s", m->pszHost, m->uPort, &pDev->szBusId[0]);
991 if (RT_LIKELY(pNew->pszAddress))
992 {
993 pNew->idVendor = pDev->u16VendorId;
994 pNew->idProduct = pDev->u16ProductId;
995 pNew->bcdDevice = pDev->u16BcdDevice;
996 pNew->bDeviceClass = pDev->bDeviceClass;
997 pNew->bDeviceSubClass = pDev->bDeviceSubClass;
998 pNew->bDeviceProtocol = pDev->bDeviceProtocol;
999 pNew->bNumConfigurations = pDev->bNumConfigurations;
1000 pNew->enmState = USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
1001 pNew->u64SerialHash = 0;
1002 /** @todo The following is not correct but is required to to get USB testing working
1003 * because only the port can be part of a filter (adding the required attributes for the bus
1004 * breaks API and ABI compatibility).
1005 * Filtering by port number is required for USB testing to connect to the correct device
1006 * in case there are multiple ones.
1007 */
1008 pNew->bBus = (uint8_t)pDev->u32DevNum;
1009 pNew->bPort = (uint8_t)pDev->u32BusNum;
1010
1011 switch (pDev->u32Speed)
1012 {
1013 case USBIP_SPEED_LOW:
1014 pNew->enmSpeed = USBDEVICESPEED_LOW;
1015 pNew->bcdUSB = 1 << 8;
1016 break;
1017 case USBIP_SPEED_FULL:
1018 pNew->enmSpeed = USBDEVICESPEED_FULL;
1019 pNew->bcdUSB = 1 << 8;
1020 break;
1021 case USBIP_SPEED_HIGH:
1022 pNew->enmSpeed = USBDEVICESPEED_HIGH;
1023 pNew->bcdUSB = 2 << 8;
1024 break;
1025 case USBIP_SPEED_WIRELESS:
1026 pNew->enmSpeed = USBDEVICESPEED_VARIABLE;
1027 pNew->bcdUSB = 1 << 8;
1028 break;
1029 case USBIP_SPEED_SUPER:
1030 pNew->enmSpeed = USBDEVICESPEED_SUPER;
1031 pNew->bcdUSB = 3 << 8;
1032 break;
1033 case USBIP_SPEED_UNKNOWN:
1034 default:
1035 pNew->bcdUSB = 1 << 8;
1036 pNew->enmSpeed = USBDEVICESPEED_UNKNOWN;
1037 }
1038
1039 /* link it */
1040 pNew->pNext = NULL;
1041 pNew->pPrev = *m->ppNext;
1042 *m->ppNext = pNew;
1043 m->ppNext = &pNew->pNext;
1044 m->cDevicesCur++;
1045 }
1046 else
1047 vrc = VERR_NO_STR_MEMORY;
1048
1049 if (RT_FAILURE(vrc))
1050 {
1051 if (pNew->pszManufacturer)
1052 RTStrFree((char *)pNew->pszManufacturer);
1053 if (pNew->pszProduct)
1054 RTStrFree((char *)pNew->pszProduct);
1055 if (pNew->pszBackend)
1056 RTStrFree((char *)pNew->pszBackend);
1057 if (pNew->pszAddress)
1058 RTStrFree((char *)pNew->pszAddress);
1059 RTMemFree(pNew);
1060 }
1061
1062 return vrc;
1063}
1064
1065/**
1066 * Compares the given device list with the current one and returns whether it has
1067 * changed.
1068 *
1069 * @returns flag whether the device list has changed compared to the current one.
1070 * @param pDevices The device list to compare the current one against.
1071 */
1072bool USBProxyBackendUsbIp::hasDevListChanged(PUSBDEVICE pDevices)
1073{
1074 /** @todo */
1075 NOREF(pDevices);
1076 return true;
1077}
1078
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