VirtualBox

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

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

Main/USBProxyBackendUsbIp: Follow up fix, only clear the device list if a connection to the server couldn't be established for some time. Increase the timespan between tries from 1 second to 3 seconds to avoid unnecessary network traffic

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