VirtualBox

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

Last change on this file since 61888 was 61127, checked in by vboxsync, 9 years ago

format specifier fix

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