VirtualBox

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

Last change on this file since 65875 was 65875, checked in by vboxsync, 8 years ago

USBProxyBackendUsbIp: Disable code which makes the testcase fail (revisiting atm.)

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

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