VirtualBox

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

Last change on this file since 86186 was 85279, checked in by vboxsync, 4 years ago

Main/USBProxyBackendUsbIp.cpp: The 'unused' u32Status fields are actually signed and should be called i32Status. bugref:9790

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