VirtualBox

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

Last change on this file since 107491 was 107460, checked in by vboxsync, 6 weeks ago

Main/src-server/generic/USBProxyBackendUsbIp.cpp: Missing field initialisers, bugref:3409

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