VirtualBox

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

Last change on this file since 59154 was 59117, checked in by vboxsync, 9 years ago

USB,Main: Rework USBProxyService. Split it into a USBProxyService and USBProxyBackend class, USBProxyService can use multiple USBProxyBackend instances as sources for USB devices to attach to a VM which will be used for USB/IP support. Change the PDM USB API to contain a backend parameter instead of a remote flag to indicate the USB backend to use for the given device.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.7 KB
Line 
1/* $Id: USBProxyBackendUsbIp.cpp 59117 2015-12-14 14:04:37Z vboxsync $ */
2/** @file
3 * VirtualBox USB Proxy Backend, USB/IP.
4 */
5
6/*
7 * Copyright (C) 2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. 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_U16(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(USBProxyService *aUsbProxyService)
268 : USBProxyBackend(aUsbProxyService)
269{
270 LogFlowThisFunc(("aUsbProxyService=%p\n", aUsbProxyService));
271}
272
273/**
274 * Initializes the object (called right after construction).
275 *
276 * @returns S_OK on success and non-fatal failures, some COM error otherwise.
277 */
278int USBProxyBackendUsbIp::init(void)
279{
280 int rc = VINF_SUCCESS;
281
282 m = new Data;
283
284 /** @todo: Pass in some config like host and port to connect to. */
285
286 /* Setup wakeup pipe and poll set first. */
287 rc = RTSemFastMutexCreate(&m->hMtxDevices);
288 if (RT_SUCCESS(rc))
289 {
290 rc = RTPipeCreate(&m->hWakeupPipeR, &m->hWakeupPipeW, 0);
291 if (RT_SUCCESS(rc))
292 {
293 rc = RTPollSetCreate(&m->hPollSet);
294 if (RT_SUCCESS(rc))
295 {
296 rc = RTPollSetAddPipe(m->hPollSet, m->hWakeupPipeR,
297 RTPOLL_EVT_READ, USBIP_POLL_ID_PIPE);
298 if (RT_SUCCESS(rc))
299 {
300 /* Connect to the USB/IP host. */
301 rc = reconnect();
302 if (RT_SUCCESS(rc))
303 rc = start(); /* Start service thread. */
304 }
305
306 if (RT_FAILURE(rc))
307 {
308 RTPollSetRemove(m->hPollSet, USBIP_POLL_ID_PIPE);
309 int rc2 = RTPollSetDestroy(m->hPollSet);
310 AssertRC(rc2);
311 }
312 }
313
314 if (RT_FAILURE(rc))
315 {
316 int rc2 = RTPipeClose(m->hWakeupPipeR);
317 AssertRC(rc2);
318 rc2 = RTPipeClose(m->hWakeupPipeW);
319 AssertRC(rc2);
320 }
321 }
322 if (RT_FAILURE(rc))
323 RTSemFastMutexDestroy(m->hMtxDevices);
324 }
325
326 return rc;
327}
328
329/**
330 * Stop all service threads and free the device chain.
331 */
332USBProxyBackendUsbIp::~USBProxyBackendUsbIp()
333{
334 LogFlowThisFunc(("\n"));
335
336 /*
337 * Stop the service.
338 */
339 if (isActive())
340 stop();
341
342 /*
343 * Free resources.
344 */
345 if (m->hPollSet != NIL_RTPOLLSET)
346 {
347 disconnect();
348
349 int rc = RTPollSetRemove(m->hPollSet, USBIP_POLL_ID_PIPE);
350 AssertRC(rc);
351 rc = RTPollSetDestroy(m->hPollSet);
352 AssertRC(rc);
353 rc = RTPipeClose(m->hWakeupPipeR);
354 AssertRC(rc);
355 rc = RTPipeClose(m->hWakeupPipeW);
356 AssertRC(rc);
357 }
358
359 if (m->pszHost)
360 RTStrFree(m->pszHost);
361 if (m->hMtxDevices != NIL_RTSEMFASTMUTEX)
362 RTSemFastMutexDestroy(m->hMtxDevices);
363
364 delete m;
365}
366
367
368int USBProxyBackendUsbIp::captureDevice(HostUSBDevice *aDevice)
369{
370 AssertReturn(aDevice, VERR_GENERAL_FAILURE);
371 AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
372
373 AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
374 LogFlowThisFunc(("aDevice=%s\n", aDevice->i_getName().c_str()));
375
376 /*
377 * We don't need to do anything when the device is held... fake it.
378 */
379 Assert(aDevice->i_getUnistate() == kHostUSBDeviceState_Capturing);
380 devLock.release();
381 interruptWait();
382
383 return VINF_SUCCESS;
384}
385
386
387int USBProxyBackendUsbIp::releaseDevice(HostUSBDevice *aDevice)
388{
389 AssertReturn(aDevice, VERR_GENERAL_FAILURE);
390 AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
391
392 AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
393 LogFlowThisFunc(("aDevice=%s\n", aDevice->i_getName().c_str()));
394
395 /*
396 * We're not really holding it atm., just fake it.
397 */
398 Assert(aDevice->i_getUnistate() == kHostUSBDeviceState_ReleasingToHost);
399 devLock.release();
400 interruptWait();
401
402 return VINF_SUCCESS;
403}
404
405
406bool USBProxyBackendUsbIp::updateDeviceState(HostUSBDevice *aDevice, PUSBDEVICE aUSBDevice, bool *aRunFilters,
407 SessionMachine **aIgnoreMachine)
408{
409 AssertReturn(aDevice, false);
410 AssertReturn(!aDevice->isWriteLockOnCurrentThread(), false);
411 AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
412 if ( aUSBDevice->enmState == USBDEVICESTATE_USED_BY_HOST_CAPTURABLE
413 && aDevice->i_getUsbData()->enmState == USBDEVICESTATE_USED_BY_HOST)
414 LogRel(("USBProxy: Device %04x:%04x (%s) has become accessible.\n",
415 aUSBDevice->idVendor, aUSBDevice->idProduct, aUSBDevice->pszAddress));
416 devLock.release();
417 return updateDeviceStateFake(aDevice, aUSBDevice, aRunFilters, aIgnoreMachine);
418}
419
420
421int USBProxyBackendUsbIp::wait(RTMSINTERVAL aMillies)
422{
423 int rc = VINF_SUCCESS;
424 bool fDeviceListChangedOrWokenUp = false;
425
426 /* Try to reconnect once when we enter if we lost the connection earlier. */
427 if (m->hSocket == NIL_RTSOCKET)
428 rc = reconnect();
429
430 /* Query a new device list upon entering. */
431 if (m->enmRecvState == kUsbIpRecvState_None)
432 {
433 rc = startListExportedDevicesReq();
434 if (RT_FAILURE(rc))
435 disconnect();
436 }
437
438 /*
439 * Because the USB/IP protocol doesn't specify a way to get notified about
440 * new or removed exported devices we have to poll the host periodically for
441 * a new device list and compare it with the previous one notifying the proxy
442 * service about changes.
443 */
444 while ( !fDeviceListChangedOrWokenUp
445 && (aMillies == RT_INDEFINITE_WAIT || aMillies > 0)
446 && RT_SUCCESS(rc))
447 {
448 RTMSINTERVAL msWait = aMillies;
449 uint64_t msPollStart = RTTimeMilliTS();
450 uint32_t uIdReady = 0;
451 uint32_t fEventsRecv = 0;
452
453 /* Limit the waiting time to 1sec so we can either reconnect or get a new device list. */
454 if (m->hSocket == NIL_RTSOCKET || m->enmRecvState == kUsbIpRecvState_None)
455 msWait = RT_MIN(1000, aMillies);
456
457 rc = RTPoll(m->hPollSet, msWait, &fEventsRecv, &uIdReady);
458 if (RT_SUCCESS(rc))
459 {
460 if (uIdReady == USBIP_POLL_ID_PIPE)
461 {
462 /* Drain the wakeup pipe. */
463 char bRead = 0;
464 size_t cbRead = 0;
465
466 rc = RTPipeRead(m->hWakeupPipeR, &bRead, 1, &cbRead);
467 Assert(RT_SUCCESS(rc) && cbRead == 1);
468 fDeviceListChangedOrWokenUp = true;
469 }
470 else if (uIdReady == USBIP_POLL_ID_SOCKET)
471 {
472 if (fEventsRecv & RTPOLL_EVT_ERROR)
473 rc = VERR_NET_SHUTDOWN;
474 else
475 rc = receiveData();
476 if (RT_SUCCESS(rc))
477 {
478 /*
479 * If we are in the none state again we received the previous request
480 * and have a new device list to compare the old against.
481 */
482 if (m->enmRecvState == kUsbIpRecvState_None)
483 {
484 if (hasDevListChanged(m->pHead))
485 fDeviceListChangedOrWokenUp = true;
486
487 /* Update to the new list in any case now that we have it anyway. */
488 RTSemFastMutexRequest(m->hMtxDevices);
489 freeDeviceList(m->pUsbDevicesCur);
490 m->cUsbDevicesCur = m->cDevicesCur;
491 m->pUsbDevicesCur = m->pHead;
492 RTSemFastMutexRelease(m->hMtxDevices);
493
494 m->pHead = NULL;
495 resetRecvState();
496 }
497 }
498 else if (rc == VERR_NET_SHUTDOWN || rc == VERR_BROKEN_PIPE)
499 {
500 LogRelMax(10, ("USB/IP: Lost connection to host \"%s\", trying to reconnect...\n", m->pszHost));
501 disconnect();
502 rc = VINF_SUCCESS;
503 }
504 }
505 else
506 {
507 AssertMsgFailed(("Invalid poll ID returned\n"));
508 rc = VERR_INVALID_STATE;
509 }
510 aMillies -= (RTTimeMilliTS() - msPollStart);
511 }
512 else if (rc == VERR_TIMEOUT)
513 {
514 aMillies -= msWait;
515 if (aMillies)
516 {
517 /* Try to reconnect and start a new request if we lost the connection before. */
518 if (m->hSocket == NIL_RTSOCKET)
519 rc = reconnect();
520
521 if (RT_SUCCESS(rc))
522 rc = startListExportedDevicesReq();
523 }
524 }
525 }
526
527 return rc;
528}
529
530
531int USBProxyBackendUsbIp::interruptWait(void)
532{
533 AssertReturn(!isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
534
535 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
536
537 int rc = RTPipeWriteBlocking(m->hWakeupPipeW, "", 1, NULL);
538 if (RT_SUCCESS(rc))
539 RTPipeFlush(m->hWakeupPipeW);
540 LogFlowFunc(("returning %Rrc\n", rc));
541 return rc;
542}
543
544
545PUSBDEVICE USBProxyBackendUsbIp::getDevices(void)
546{
547 PUSBDEVICE pFirst = NULL;
548 PUSBDEVICE *ppNext = &pFirst;
549
550 /* Create a deep copy of the device list. */
551 RTSemFastMutexRequest(m->hMtxDevices);
552 PUSBDEVICE pCur = m->pUsbDevicesCur;
553 while (pCur)
554 {
555 PUSBDEVICE pNew = (PUSBDEVICE)RTMemAllocZ(sizeof(USBDEVICE));
556 if (pNew)
557 {
558 pNew->pszManufacturer = RTStrDup(pCur->pszManufacturer);
559 pNew->pszProduct = RTStrDup(pCur->pszProduct);
560 if (pCur->pszSerialNumber)
561 pNew->pszSerialNumber = RTStrDup(pCur->pszSerialNumber);
562 pNew->pszBackend = RTStrDup(pCur->pszBackend);
563 pNew->pszAddress = RTStrDup(pCur->pszAddress);
564
565 pNew->idVendor = pCur->idVendor;
566 pNew->idProduct = pCur->idProduct;
567 pNew->bcdDevice = pCur->bcdDevice;
568 pNew->bcdUSB = pCur->bcdUSB;
569 pNew->bDeviceClass = pCur->bDeviceClass;
570 pNew->bDeviceSubClass = pCur->bDeviceSubClass;
571 pNew->bDeviceProtocol = pCur->bDeviceProtocol;
572 pNew->bNumConfigurations = pCur->bNumConfigurations;
573 pNew->enmState = pCur->enmState;
574 pNew->u64SerialHash = pCur->u64SerialHash;
575 pNew->bBus = pCur->bBus;
576 pNew->bPort = pCur->bPort;
577 pNew->enmSpeed = pCur->enmSpeed;
578
579 /* link it */
580 pNew->pNext = NULL;
581 pNew->pPrev = *ppNext;
582 *ppNext = pNew;
583 ppNext = &pNew->pNext;
584 }
585
586 pCur = pCur->pNext;
587 }
588 RTSemFastMutexRelease(m->hMtxDevices);
589
590 return pFirst;
591}
592
593/**
594 * Frees a given device list.
595 *
596 * @returns nothing.
597 * @param pHead The head of the device list to free.
598 */
599void USBProxyBackendUsbIp::freeDeviceList(PUSBDEVICE pHead)
600{
601 PUSBDEVICE pNext = pHead;
602 while (pNext)
603 {
604 PUSBDEVICE pFree = pNext;
605 pNext = pNext->pNext;
606 freeDevice(pFree);
607 }
608}
609
610/**
611 * Resets the receive state to the idle state.
612 *
613 * @returns nothing.
614 */
615void USBProxyBackendUsbIp::resetRecvState()
616{
617 freeDeviceList(m->pHead);
618 m->pHead = NULL;
619 m->ppNext = &m->pHead;
620 m->cDevicesCur = 0;
621 m->enmRecvState = kUsbIpRecvState_None;
622 m->cbResidualRecv = 0;
623 m->pbRecvBuf = &m->Scratch.abRecv[0];
624 m->cDevicesLeft = 0;
625}
626
627/**
628 * Disconnects from the host and resets the receive state.
629 *
630 * @returns nothing.
631 */
632void USBProxyBackendUsbIp::disconnect()
633{
634 if (m->hSocket != NIL_RTSOCKET)
635 {
636 int rc = RTPollSetRemove(m->hPollSet, USBIP_POLL_ID_SOCKET);
637 Assert(RT_SUCCESS(rc) || rc == VERR_POLL_HANDLE_ID_NOT_FOUND);
638
639 RTTcpClientCloseEx(m->hSocket, false /*fGracefulShutdown*/);
640 m->hSocket = NIL_RTSOCKET;
641 }
642
643 resetRecvState();
644}
645
646/**
647 * Tries to reconnect to the USB/IP host.
648 *
649 * @returns VBox status code.
650 */
651int USBProxyBackendUsbIp::reconnect()
652{
653 /* Make sure we are disconnected. */
654 disconnect();
655
656 /* Connect to the USB/IP host. */
657 int rc = RTTcpClientConnect(m->pszHost, m->uPort, &m->hSocket);
658 if (RT_SUCCESS(rc))
659 {
660 rc = RTTcpSetSendCoalescing(m->hSocket, false);
661 if (RT_FAILURE(rc))
662 LogRel(("USB/IP: Disabling send coalescing failed (rc=%Rrc), continuing nevertheless but expect increased latency\n", rc));
663
664 rc = RTPollSetAddSocket(m->hPollSet, m->hSocket, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR,
665 USBIP_POLL_ID_SOCKET);
666 if (RT_FAILURE(rc))
667 {
668 RTTcpClientCloseEx(m->hSocket, false /*fGracefulShutdown*/);
669 m->hSocket = NIL_RTSOCKET;
670 }
671 }
672
673 return rc;
674}
675
676/**
677 * Initiates a new List Exported Devices request.
678 *
679 * @returns VBox status code.
680 */
681int USBProxyBackendUsbIp::startListExportedDevicesReq()
682{
683 int rc = VINF_SUCCESS;
684
685 /*
686 * Reset the current state and reconnect in case we were called in the middle
687 * of another transfer (which should not happen).
688 */
689 Assert(m->enmRecvState == kUsbIpRecvState_None);
690 if (m->enmRecvState != kUsbIpRecvState_None)
691 rc = reconnect();
692
693 if (RT_SUCCESS(rc))
694 {
695 /* Send of the request. */
696 UsbIpReqDevList ReqDevList;
697 ReqDevList.u16Version = RT_H2N_U16(USBIP_VERSION);
698 ReqDevList.u16Cmd = RT_H2N_U16(USBIP_INDICATOR_REQ | USBIP_REQ_RET_DEVLIST);
699 ReqDevList.u32Status = RT_H2N_U32(0);
700 rc = RTTcpWrite(m->hSocket, &ReqDevList, sizeof(ReqDevList));
701 if (RT_SUCCESS(rc))
702 advanceState(kUsbIpRecvState_Hdr);
703 }
704
705 return rc;
706}
707
708/**
709 * Advances the state machine to the given state.
710 *
711 * @returns nothing.
712 * @param enmRecvState The new receive state.
713 */
714void USBProxyBackendUsbIp::advanceState(USBIPRECVSTATE enmRecvState)
715{
716 switch (enmRecvState)
717 {
718 case kUsbIpRecvState_None:
719 break;
720 case kUsbIpRecvState_Hdr:
721 {
722 m->cbResidualRecv = sizeof(UsbIpRetDevList);
723 m->pbRecvBuf = (uint8_t *)&m->Scratch.RetDevList;
724 break;
725 }
726 case kUsbIpRecvState_ExportedDevice:
727 {
728 m->cbResidualRecv = sizeof(UsbIpExportedDevice);
729 m->pbRecvBuf = (uint8_t *)&m->Scratch.ExportedDevice;
730 break;
731 }
732 case kUsbIpRecvState_DeviceInterface:
733 {
734 m->cbResidualRecv = sizeof(UsbIpDeviceInterface);
735 m->pbRecvBuf = (uint8_t *)&m->Scratch.DeviceInterface;
736 break;
737 }
738 default:
739 AssertMsgFailed(("Invalid USB/IP receive state %d\n", enmRecvState));
740 return;
741 }
742
743 m->enmRecvState = enmRecvState;
744}
745
746/**
747 * Receives data from the USB/IP host and processes it when everything for the current
748 * state was received.
749 *
750 * @returns VBox status code.
751 */
752int USBProxyBackendUsbIp::receiveData()
753{
754 size_t cbRecvd = 0;
755 int rc = RTTcpReadNB(m->hSocket, m->pbRecvBuf, m->cbResidualRecv, &cbRecvd);
756 if (RT_SUCCESS(rc))
757 {
758 m->cbResidualRecv -= cbRecvd;
759 m->pbRecvBuf += cbRecvd;
760 /* In case we received everything for the current state process the data. */
761 if (!m->cbResidualRecv)
762 rc = processData();
763 }
764
765 return rc;
766}
767
768/**
769 * Processes the data in the scratch buffer based on the current state.
770 *
771 * @returns VBox status code.
772 */
773int USBProxyBackendUsbIp::processData()
774{
775 int rc = VINF_SUCCESS;
776
777 switch (m->enmRecvState)
778 {
779 case kUsbIpRecvState_Hdr:
780 {
781 /* Check that the reply matches our expectations. */
782 if ( RT_N2H_U16(m->Scratch.RetDevList.u16Version) == USBIP_VERSION
783 && RT_N2H_U16(m->Scratch.RetDevList.u16Cmd) == USBIP_REQ_RET_DEVLIST
784 && RT_N2H_U32(m->Scratch.RetDevList.u32Status) == USBIP_STATUS_SUCCESS)
785 {
786 /* Populate the number of exported devices in the list and go to the next state. */
787 m->cDevicesLeft = RT_N2H_U32(m->Scratch.RetDevList.u32DevicesExported);
788 if (m->cDevicesLeft)
789 advanceState(kUsbIpRecvState_ExportedDevice);
790 else
791 advanceState(kUsbIpRecvState_None);
792 }
793 else
794 {
795 LogRelMax(10, ("USB/IP: Host sent an invalid reply to the list exported device request (Version: %#x Cmd: %#x Status: %#x)\n",
796 RT_N2H_U16(m->Scratch.RetDevList.u16Version), RT_N2H_U16(m->Scratch.RetDevList.u16Cmd),
797 RT_N2H_U32(m->Scratch.RetDevList.u32Status)));
798 rc = VERR_INVALID_STATE;
799 }
800 break;
801 }
802 case kUsbIpRecvState_ExportedDevice:
803 {
804 /* Create a new device and add it to the list. */
805 usbProxyBackendUsbIpExportedDeviceN2H(&m->Scratch.ExportedDevice);
806 rc = addDeviceToList(&m->Scratch.ExportedDevice);
807 if (RT_SUCCESS(rc))
808 {
809 m->cInterfacesLeft = m->Scratch.ExportedDevice.bNumInterfaces;
810 if (m->cInterfacesLeft)
811 advanceState(kUsbIpRecvState_DeviceInterface);
812 else
813 {
814 m->cDevicesLeft--;
815 if (m->cDevicesLeft)
816 advanceState(kUsbIpRecvState_ExportedDevice);
817 else
818 advanceState(kUsbIpRecvState_None);
819 }
820 }
821 break;
822 }
823 case kUsbIpRecvState_DeviceInterface:
824 {
825 /*
826 * If all interfaces for the current device were received receive the next device
827 * if there is another one left, if not we are done with the current request.
828 */
829 m->cInterfacesLeft--;
830 if (m->cInterfacesLeft)
831 advanceState(kUsbIpRecvState_DeviceInterface);
832 else
833 {
834 m->cDevicesLeft--;
835 if (m->cDevicesLeft)
836 advanceState(kUsbIpRecvState_ExportedDevice);
837 else
838 advanceState(kUsbIpRecvState_None);
839 }
840 break;
841 }
842 case kUsbIpRecvState_None:
843 default:
844 AssertMsgFailed(("Invalid USB/IP receive state %d\n", m->enmRecvState));
845 return VERR_INVALID_STATE;
846 }
847
848 return rc;
849}
850
851/**
852 * Creates a new USB device and adds it to the list.
853 *
854 * @returns VBox status code.
855 * @param pDev Pointer to the USB/IP exported device structure to take
856 * the information for the new device from.
857 */
858int USBProxyBackendUsbIp::addDeviceToList(PUsbIpExportedDevice pDev)
859{
860 PUSBDEVICE pNew = (PUSBDEVICE)RTMemAllocZ(sizeof(USBDEVICE));
861 if (!pNew)
862 return VERR_NO_MEMORY;
863
864 pNew->pszManufacturer = RTStrDup("");
865 pNew->pszProduct = RTStrDup("");
866 pNew->pszSerialNumber = NULL;
867 pNew->pszBackend = RTStrDup("usbip");
868
869 /* Make sure the Bus id is 0 terminated. */
870 pDev->szBusId[31] = '\0';
871 RTStrAPrintf((char **)&pNew->pszAddress, "usbip://%s:%u:%s", m->pszHost, m->uPort, &pDev->szBusId[0]);
872
873 pNew->idVendor = pDev->u16VendorId;
874 pNew->idProduct = pDev->u16ProductId;
875 pNew->bcdDevice = pDev->u16BcdDevice;
876 pNew->bDeviceClass = pDev->bDeviceClass;
877 pNew->bDeviceSubClass = pDev->bDeviceSubClass;
878 pNew->bDeviceProtocol = pDev->bDeviceProtocol;
879 pNew->bNumConfigurations = pDev->bNumConfigurations;
880 pNew->enmState = USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
881 pNew->u64SerialHash = 0;
882 pNew->bBus = (uint8_t)pDev->u32BusNum;
883 pNew->bPort = (uint8_t)pDev->u32DevNum;
884
885 switch (pDev->u32Speed)
886 {
887 case USBIP_SPEED_LOW:
888 pNew->enmSpeed = USBDEVICESPEED_LOW;
889 pNew->bcdUSB = 1 << 8;
890 break;
891 case USBIP_SPEED_FULL:
892 pNew->enmSpeed = USBDEVICESPEED_FULL;
893 pNew->bcdUSB = 1 << 8;
894 break;
895 case USBIP_SPEED_HIGH:
896 pNew->enmSpeed = USBDEVICESPEED_HIGH;
897 pNew->bcdUSB = 2 << 8;
898 break;
899 case USBIP_SPEED_WIRELESS:
900 pNew->enmSpeed = USBDEVICESPEED_VARIABLE;
901 pNew->bcdUSB = 1 << 8;
902 break;
903 case USBIP_SPEED_SUPER:
904 pNew->enmSpeed = USBDEVICESPEED_SUPER;
905 pNew->bcdUSB = 3 << 8;
906 break;
907 case USBIP_SPEED_UNKNOWN:
908 default:
909 pNew->bcdUSB = 1 << 8;
910 pNew->enmSpeed = USBDEVICESPEED_UNKNOWN;
911 }
912
913 /* link it */
914 pNew->pNext = NULL;
915 pNew->pPrev = *m->ppNext;
916 *m->ppNext = pNew;
917 m->ppNext = &pNew->pNext;
918 m->cDevicesCur++;
919
920 return VINF_SUCCESS;
921}
922
923/**
924 * Compares the given device list with the current one and returns whether it has
925 * changed.
926 *
927 * @returns flag whether the device list has changed compared to the current one.
928 * @param pDevices The device list to compare the current one against.
929 */
930bool USBProxyBackendUsbIp::hasDevListChanged(PUSBDEVICE pDevices)
931{
932 /** @todo */
933 return true;
934}
935
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