VirtualBox

source: vbox/trunk/src/VBox/Devices/USB/solaris/USBProxyDevice-solaris.cpp@ 93115

Last change on this file since 93115 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.2 KB
Line 
1/* $Id: USBProxyDevice-solaris.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * USB device proxy - the Solaris backend.
4 */
5
6/*
7 * Copyright (C) 2009-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DRV_USBPROXY
23#include <sys/poll.h>
24#include <errno.h>
25#include <strings.h>
26#include <limits.h>
27
28#include <VBox/log.h>
29#include <VBox/err.h>
30#include <VBox/vmm/pdm.h>
31
32#include <iprt/string.h>
33#include <iprt/critsect.h>
34#include <iprt/time.h>
35#include <iprt/file.h>
36#include <iprt/mem.h>
37#include <iprt/pipe.h>
38#include "../USBProxyDevice.h"
39#include <VBox/usblib.h>
40
41
42/*********************************************************************************************************************************
43* Defined Constants And Macros *
44*********************************************************************************************************************************/
45/** Log Prefix. */
46#define USBPROXY "USBProxy"
47
48
49/*********************************************************************************************************************************
50* Structures and Typedefs *
51*********************************************************************************************************************************/
52/**
53 * Wrapper around the solaris urb request structure.
54 * This is required to track in-flight and landed URBs.
55 */
56typedef struct USBPROXYURBSOL
57{
58 /** Pointer to the Solaris device. */
59 struct USBPROXYDEVSOL *pDevSol;
60 /** Pointer to the VUSB URB (set to NULL if canceled). */
61 PVUSBURB pVUsbUrb;
62 /** Pointer to the next solaris URB. */
63 struct USBPROXYURBSOL *pNext;
64 /** Pointer to the previous solaris URB. */
65 struct USBPROXYURBSOL *pPrev;
66} USBPROXYURBSOL, *PUSBPROXYURBSOL;
67
68/**
69 * Data for the solaris usb proxy backend.
70 */
71typedef struct USBPROXYDEVSOL
72{
73 /** Path of the USB device in the devices tree (persistent). */
74 char *pszDevicePath;
75 /** The connection to the client driver. */
76 RTFILE hFile;
77 /** Pointer to the proxy device instance. */
78 PUSBPROXYDEV pProxyDev;
79 /** Critical section protecting the two lists. */
80 RTCRITSECT CritSect;
81 /** The list of free solaris URBs. Singly linked. */
82 PUSBPROXYURBSOL pFreeHead;
83 /** The list of active solaris URBs. Doubly linked.
84 * We must maintain this so we can properly reap URBs of a detached device.
85 * Only the split head will appear in this list. */
86 PUSBPROXYURBSOL pInFlightHead;
87 /** The list of landed solaris URBs. Doubly linked.
88 * Only the split head will appear in this list. */
89 PUSBPROXYURBSOL pTaxingHead;
90 /** The tail of the landed solaris URBs. */
91 PUSBPROXYURBSOL pTaxingTail;
92 /** Pipe handle for waking up - writing end. */
93 RTPIPE hPipeWakeupW;
94 /** Pipe handle for waking up - reading end. */
95 RTPIPE hPipeWakeupR;
96} USBPROXYDEVSOL, *PUSBPROXYDEVSOL;
97
98static PVUSBURB usbProxySolarisUrbComplete(PUSBPROXYDEVSOL pDevSol);
99
100
101/**
102 * Allocates a Solaris URB request structure.
103 *
104 * @returns Pointer to an active URB request.
105 * @returns NULL on failure.
106 *
107 * @param pDevSol The solaris USB device.
108 */
109static PUSBPROXYURBSOL usbProxySolarisUrbAlloc(PUSBPROXYDEVSOL pDevSol)
110{
111 PUSBPROXYURBSOL pUrbSol;
112
113 RTCritSectEnter(&pDevSol->CritSect);
114
115 /*
116 * Try remove a Solaris URB from the free list, if none there allocate a new one.
117 */
118 pUrbSol = pDevSol->pFreeHead;
119 if (pUrbSol)
120 pDevSol->pFreeHead = pUrbSol->pNext;
121 else
122 {
123 RTCritSectLeave(&pDevSol->CritSect);
124 pUrbSol = (PUSBPROXYURBSOL)RTMemAlloc(sizeof(*pUrbSol));
125 if (!pUrbSol)
126 return NULL;
127 RTCritSectEnter(&pDevSol->CritSect);
128 }
129 pUrbSol->pVUsbUrb = NULL;
130 pUrbSol->pDevSol = pDevSol;
131
132 /*
133 * Link it into the active list
134 */
135 pUrbSol->pPrev = NULL;
136 pUrbSol->pNext = pDevSol->pInFlightHead;
137 if (pUrbSol->pNext)
138 pUrbSol->pNext->pPrev = pUrbSol;
139 pDevSol->pInFlightHead = pUrbSol;
140
141 RTCritSectLeave(&pDevSol->CritSect);
142 return pUrbSol;
143}
144
145
146/**
147 * Frees a Solaris URB request structure.
148 *
149 * @param pDevSol The Solaris USB device.
150 * @param pUrbSol The Solaris URB to free.
151 */
152static void usbProxySolarisUrbFree(PUSBPROXYDEVSOL pDevSol, PUSBPROXYURBSOL pUrbSol)
153{
154 RTCritSectEnter(&pDevSol->CritSect);
155
156 /*
157 * Remove from the active or taxing list.
158 */
159 if (pUrbSol->pNext)
160 pUrbSol->pNext->pPrev = pUrbSol->pPrev;
161 else if (pDevSol->pTaxingTail == pUrbSol)
162 pDevSol->pTaxingTail = pUrbSol->pPrev;
163
164 if (pUrbSol->pPrev)
165 pUrbSol->pPrev->pNext = pUrbSol->pNext;
166 else if (pDevSol->pTaxingHead == pUrbSol)
167 pDevSol->pTaxingHead = pUrbSol->pNext;
168 else if (pDevSol->pInFlightHead == pUrbSol)
169 pDevSol->pInFlightHead = pUrbSol->pNext;
170 else
171 AssertFailed();
172
173 /*
174 * Link it into the free list.
175 */
176 pUrbSol->pPrev = NULL;
177 pUrbSol->pNext = pDevSol->pFreeHead;
178 pDevSol->pFreeHead = pUrbSol;
179
180 pUrbSol->pVUsbUrb = NULL;
181 pUrbSol->pDevSol = NULL;
182
183 RTCritSectLeave(&pDevSol->CritSect);
184}
185
186
187/*
188 * Close the connection to the USB client driver.
189 *
190 * This is required because our userland enumeration relies on drivers/device trees
191 * to recognize active devices, and hence if this device is unplugged we should no
192 * longer keep the client driver loaded.
193 */
194static void usbProxySolarisCloseFile(PUSBPROXYDEVSOL pDevSol)
195{
196 RTFileClose(pDevSol->hFile);
197 pDevSol->hFile = NIL_RTFILE;
198}
199
200
201/**
202 * The client driver IOCtl Wrapper function.
203 *
204 * @returns VBox status code.
205 * @param pDevSol The Solaris device instance.
206 * @param Function The Function.
207 * @param pvData Opaque pointer to the data.
208 * @param cbData Size of the data pointed to by pvData.
209 */
210static int usbProxySolarisIOCtl(PUSBPROXYDEVSOL pDevSol, unsigned Function, void *pvData, size_t cbData)
211{
212 if (RT_UNLIKELY(pDevSol->hFile == NIL_RTFILE))
213 {
214 LogFlow((USBPROXY ":usbProxySolarisIOCtl: Connection to driver gone!\n"));
215 return VERR_VUSB_DEVICE_NOT_ATTACHED;
216 }
217
218 VBOXUSBREQ Req;
219 Req.u32Magic = VBOXUSB_MAGIC;
220 Req.rc = -1;
221 Req.cbData = cbData;
222 Req.pvDataR3 = pvData;
223
224 int Ret = -1;
225 int rc = RTFileIoCtl(pDevSol->hFile, Function, &Req, sizeof(Req), &Ret);
226 if (RT_SUCCESS(rc))
227 {
228 if (RT_FAILURE(Req.rc))
229 {
230 if (Req.rc == VERR_VUSB_DEVICE_NOT_ATTACHED)
231 {
232 pDevSol->pProxyDev->fDetached = true;
233 usbProxySolarisCloseFile(pDevSol);
234 LogRel((USBPROXY ": Command %#x failed, USB Device '%s' disconnected!\n", Function,
235 pDevSol->pProxyDev->pUsbIns->pszName));
236 }
237 else
238 LogRel((USBPROXY ": Command %#x failed. Req.rc=%Rrc\n", Function, Req.rc));
239 }
240
241 return Req.rc;
242 }
243
244 LogRel((USBPROXY ": Function %#x failed. rc=%Rrc\n", Function, rc));
245 return rc;
246}
247
248
249/**
250 * Get the active configuration from the device. The first time this is called
251 * our client driver would returned the cached configuration since the device is first plugged in.
252 * Subsequent get configuration requests are passed on to the device.
253 *
254 * @returns VBox status code.
255 * @param pDevSol The Solaris device instance.
256 *
257 */
258static inline int usbProxySolarisGetActiveConfig(PUSBPROXYDEVSOL pDevSol)
259{
260 VBOXUSBREQ_GET_CONFIG GetConfigReq;
261 bzero(&GetConfigReq, sizeof(GetConfigReq));
262 int rc = usbProxySolarisIOCtl(pDevSol, VBOXUSB_IOCTL_GET_CONFIG, &GetConfigReq, sizeof(GetConfigReq));
263 if (RT_SUCCESS(rc))
264 {
265 pDevSol->pProxyDev->iActiveCfg = GetConfigReq.bConfigValue;
266 pDevSol->pProxyDev->cIgnoreSetConfigs = 0;
267 }
268 else
269 {
270 if (rc != VERR_VUSB_DEVICE_NOT_ATTACHED)
271 LogRel((USBPROXY ": Failed to get configuration. rc=%Rrc\n", rc));
272
273 pDevSol->pProxyDev->iActiveCfg = -1;
274 pDevSol->pProxyDev->cIgnoreSetConfigs = 0;
275 }
276 return rc;
277}
278
279
280/**
281 * Opens the USB device.
282 *
283 * @returns VBox status code.
284 * @param pProxyDev The device instance.
285 * @param pszAddress The unique device identifier.
286 * The format of this string is "VendorId:ProducIt:Release:StaticPath".
287 * @param pvBackend Backend specific pointer, unused for the solaris backend.
288 */
289static DECLCALLBACK(int) usbProxySolarisOpen(PUSBPROXYDEV pProxyDev, const char *pszAddress, void *pvBackend)
290{
291 PUSBPROXYDEVSOL pDevSol = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVSOL);
292
293 LogFlowFunc((USBPROXY ":usbProxySolarisOpen: pProxyDev=%p pszAddress=%s pvBackend=%p\n", pProxyDev, pszAddress, pvBackend));
294
295 /*
296 * Initialize our USB R3 lib.
297 */
298 int rc = USBLibInit();
299 if (RT_SUCCESS(rc))
300 {
301 /*
302 * Allocate and initialize the solaris backend data.
303 */
304 AssertCompile(PATH_MAX >= MAXPATHLEN);
305 char szDeviceIdent[PATH_MAX+48];
306 rc = RTStrPrintf(szDeviceIdent, sizeof(szDeviceIdent), "%s", pszAddress);
307 if (RT_SUCCESS(rc))
308 {
309 rc = RTCritSectInit(&pDevSol->CritSect);
310 if (RT_SUCCESS(rc))
311 {
312 /*
313 * Create wakeup pipe.
314 */
315 rc = RTPipeCreate(&pDevSol->hPipeWakeupR, &pDevSol->hPipeWakeupW, 0);
316 if (RT_SUCCESS(rc))
317 {
318 int Instance;
319 char *pszDevicePath = NULL;
320 rc = USBLibGetClientInfo(szDeviceIdent, &pszDevicePath, &Instance);
321 if (RT_SUCCESS(rc))
322 {
323 pDevSol->pszDevicePath = pszDevicePath;
324
325 /*
326 * Open the client driver.
327 */
328 RTFILE hFile;
329 rc = RTFileOpen(&hFile, pDevSol->pszDevicePath, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
330 if (RT_SUCCESS(rc))
331 {
332 pDevSol->hFile = hFile;
333 pDevSol->pProxyDev = pProxyDev;
334
335 /*
336 * Verify client driver version.
337 */
338 VBOXUSBREQ_GET_VERSION GetVersionReq;
339 bzero(&GetVersionReq, sizeof(GetVersionReq));
340 rc = usbProxySolarisIOCtl(pDevSol, VBOXUSB_IOCTL_GET_VERSION, &GetVersionReq, sizeof(GetVersionReq));
341 if (RT_SUCCESS(rc))
342 {
343 if ( GetVersionReq.u32Major == VBOXUSB_VERSION_MAJOR
344 && GetVersionReq.u32Minor >= VBOXUSB_VERSION_MINOR)
345 {
346 /*
347 * Try & get the current cached config from Solaris.
348 */
349 usbProxySolarisGetActiveConfig(pDevSol);
350 return VINF_SUCCESS;
351 }
352 else
353 {
354 LogRel((USBPROXY ": Version mismatch, Driver v%d.%d expecting ~v%d.%d\n", GetVersionReq.u32Major,
355 GetVersionReq.u32Minor, VBOXUSB_VERSION_MAJOR, VBOXUSB_VERSION_MINOR));
356 rc = VERR_VERSION_MISMATCH;
357 }
358 }
359 else
360 LogRel((USBPROXY ": Failed to query driver version. rc=%Rrc\n", rc));
361
362 RTFileClose(pDevSol->hFile);
363 pDevSol->hFile = NIL_RTFILE;
364 pDevSol->pProxyDev = NULL;
365 }
366 else
367 LogRel((USBPROXY ": Failed to open device. rc=%Rrc pszDevicePath=%s\n", rc, pDevSol->pszDevicePath));
368
369 RTStrFree(pDevSol->pszDevicePath);
370 pDevSol->pszDevicePath = NULL;
371 }
372 else
373 {
374 LogRel((USBPROXY ": Failed to get client info. rc=%Rrc szDeviceIdent=%s\n", rc, szDeviceIdent));
375 if (rc == VERR_NOT_FOUND)
376 rc = VERR_OPEN_FAILED;
377 }
378 RTPipeClose(pDevSol->hPipeWakeupR);
379 RTPipeClose(pDevSol->hPipeWakeupW);
380 }
381
382 RTCritSectDelete(&pDevSol->CritSect);
383 }
384 else
385 LogRel((USBPROXY ": RTCritSectInit failed. rc=%Rrc pszAddress=%s\n", rc, pszAddress));
386 }
387 else
388 LogRel((USBPROXY ": RTStrAPrintf failed. rc=%Rrc pszAddress=%s\n", rc, pszAddress));
389 }
390 else
391 LogRel((USBPROXY ": USBLibInit failed. rc=%Rrc\n", rc));
392
393 USBLibTerm();
394 return rc;
395}
396
397
398/**
399 * Close the USB device.
400 *
401 * @param pProxyDev The device instance.
402 */
403static DECLCALLBACK(void) usbProxySolarisClose(PUSBPROXYDEV pProxyDev)
404{
405 LogFlow((USBPROXY ":usbProxySolarisClose: pProxyDev=%p\n", pProxyDev));
406
407 PUSBPROXYDEVSOL pDevSol = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVSOL);
408
409 /* Close the device (do not re-enumerate). */
410 VBOXUSBREQ_CLOSE_DEVICE CloseReq;
411 CloseReq.ResetLevel = VBOXUSB_RESET_LEVEL_CLOSE;
412 usbProxySolarisIOCtl(pDevSol, VBOXUSB_IOCTL_CLOSE_DEVICE, &CloseReq, sizeof(CloseReq));
413
414 pProxyDev->fDetached = true;
415 usbProxySolarisCloseFile(pDevSol);
416
417 /*
418 * Now we can close it and free all the resources.
419 */
420 RTCritSectDelete(&pDevSol->CritSect);
421
422 PUSBPROXYURBSOL pUrbSol = NULL;
423 while ((pUrbSol = pDevSol->pInFlightHead) != NULL)
424 {
425 pDevSol->pInFlightHead = pUrbSol->pNext;
426 RTMemFree(pUrbSol);
427 }
428
429 while ((pUrbSol = pDevSol->pFreeHead) != NULL)
430 {
431 pDevSol->pFreeHead = pUrbSol->pNext;
432 RTMemFree(pUrbSol);
433 }
434
435 RTPipeClose(pDevSol->hPipeWakeupR);
436 RTPipeClose(pDevSol->hPipeWakeupW);
437
438 RTStrFree(pDevSol->pszDevicePath);
439 pDevSol->pszDevicePath = NULL;
440
441 USBLibTerm();
442}
443
444
445/**
446 * Reset the device.
447 *
448 * @returns VBox status code.
449 * @param pProxyDev The device to reset.
450 * @param fRootHubReset Is this a root hub reset or device specific reset request.
451 */
452static DECLCALLBACK(int) usbProxySolarisReset(PUSBPROXYDEV pProxyDev, bool fRootHubReset)
453{
454 LogFlowFunc((USBPROXY ": usbProxySolarisReset: pProxyDev=%s fRootHubReset=%d\n", pProxyDev->pUsbIns->pszName, fRootHubReset));
455
456 /** Pass all resets to the device. The Trekstor USB (1.1) stick requires this to work. */
457 PUSBPROXYDEVSOL pDevSol = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVSOL);
458
459 /* Soft reset the device. */
460 VBOXUSBREQ_CLOSE_DEVICE CloseReq;
461 CloseReq.ResetLevel = VBOXUSB_RESET_LEVEL_SOFT;
462 int rc = usbProxySolarisIOCtl(pDevSol, VBOXUSB_IOCTL_CLOSE_DEVICE, &CloseReq, sizeof(CloseReq));
463 if (RT_SUCCESS(rc))
464 {
465 /* Get the active config. Solaris USBA sets a default config. */
466 usbProxySolarisGetActiveConfig(pDevSol);
467 }
468 else if (rc != VERR_VUSB_DEVICE_NOT_ATTACHED)
469 LogRel((USBPROXY ": usbProxySolarisReset: Failed! rc=%d\n", rc));
470
471 return rc;
472}
473
474
475/**
476 * Set the active configuration.
477 *
478 * The caller makes sure that it's not called first time after open or reset
479 * with the active interface.
480 *
481 * @returns success indicator.
482 * @param pProxyDev The device instance data.
483 * @param iCfg The configuration value to set.
484 */
485static DECLCALLBACK(int) usbProxySolarisSetConfig(PUSBPROXYDEV pProxyDev, int iCfg)
486{
487 LogFlowFunc((USBPROXY ": usbProxySolarisSetConfig: pProxyDev=%p iCfg=%#x\n", pProxyDev, iCfg));
488
489 PUSBPROXYDEVSOL pDevSol = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVSOL);
490 AssertPtrReturn(pDevSol, VERR_INVALID_POINTER);
491
492 VBOXUSBREQ_SET_CONFIG SetConfigReq;
493 SetConfigReq.bConfigValue = iCfg;
494 int rc = usbProxySolarisIOCtl(pDevSol, VBOXUSB_IOCTL_SET_CONFIG, &SetConfigReq, sizeof(SetConfigReq));
495 if ( RT_FAILURE(rc)
496 && rc != VERR_VUSB_DEVICE_NOT_ATTACHED)
497 LogRel((USBPROXY ": usbProxySolarisSetConfig: Failed! rc=%Rrc\n", rc));
498
499 return rc;
500}
501
502
503/**
504 * Claims an interface.
505 *
506 * This is a stub on Solaris since we release/claim all interfaces at
507 * as and when required with endpoint opens.
508 *
509 * @returns success indicator (always true).
510 */
511static DECLCALLBACK(int) usbProxySolarisClaimInterface(PUSBPROXYDEV pProxyDev, int iIf)
512{
513 return VINF_SUCCESS;
514}
515
516
517/**
518 * Releases an interface.
519 *
520 * This is a stub on Solaris since we release/claim all interfaces at
521 * as and when required with endpoint opens.
522 *
523 * @returns success indicator.
524 */
525static DECLCALLBACK(int) usbProxySolarisReleaseInterface(PUSBPROXYDEV pProxyDev, int iIf)
526{
527 return VINF_SUCCESS;
528}
529
530
531/**
532 * Specify an alternate setting for the specified interface of the current configuration.
533 *
534 * @returns success indicator.
535 */
536static DECLCALLBACK(int) usbProxySolarisSetInterface(PUSBPROXYDEV pProxyDev, int bIf, int bAlt)
537{
538 LogFlowFunc((USBPROXY ": usbProxySolarisSetInterface: pProxyDev=%p bIf=%#x iAlt=%#x\n", pProxyDev, bIf, bAlt));
539
540 PUSBPROXYDEVSOL pDevSol = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVSOL);
541 AssertPtrReturn(pDevSol, VERR_INVALID_POINTER);
542
543 VBOXUSBREQ_SET_INTERFACE SetInterfaceReq;
544 SetInterfaceReq.bInterface = bIf;
545 SetInterfaceReq.bAlternate = bAlt;
546 int rc = usbProxySolarisIOCtl(pDevSol, VBOXUSB_IOCTL_SET_INTERFACE, &SetInterfaceReq, sizeof(SetInterfaceReq));
547 if ( RT_FAILURE(rc)
548 && rc != VERR_VUSB_DEVICE_NOT_ATTACHED)
549 LogRel((USBPROXY ": usbProxySolarisSetInterface: Failed! rc=%Rrc\n", rc));
550
551 return rc;
552}
553
554
555/**
556 * Clears the halted endpoint 'EndPt'.
557 */
558static DECLCALLBACK(int) usbProxySolarisClearHaltedEp(PUSBPROXYDEV pProxyDev, unsigned int EndPt)
559{
560 LogFlowFunc((USBPROXY ": usbProxySolarisClearHaltedEp: pProxyDev=%p EndPt=%#x\n", pProxyDev, EndPt));
561
562 PUSBPROXYDEVSOL pDevSol = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVSOL);
563 AssertPtrReturn(pDevSol, VERR_INVALID_POINTER);
564
565 VBOXUSBREQ_CLEAR_EP ClearEpReq;
566 ClearEpReq.bEndpoint = EndPt;
567 int rc = usbProxySolarisIOCtl(pDevSol, VBOXUSB_IOCTL_CLEAR_EP, &ClearEpReq, sizeof(ClearEpReq));
568 if ( RT_FAILURE(rc)
569 && rc != VERR_VUSB_DEVICE_NOT_ATTACHED)
570 LogRel((USBPROXY ": usbProxySolarisClearHaltedEp: Failed! rc=%Rrc\n", rc));
571
572 return rc;
573}
574
575
576/**
577 * @interface_method_impl{USBPROXYBACK,pfnUrbQueue}
578 */
579static DECLCALLBACK(int) usbProxySolarisUrbQueue(PUSBPROXYDEV pProxyDev, PVUSBURB pUrb)
580{
581 PUSBPROXYDEVSOL pDevSol = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVSOL);
582
583 LogFlowFunc((USBPROXY ": usbProxySolarisUrbQueue: pProxyDev=%s pUrb=%p pszDesc=%s EndPt=%#x enmDir=%d cbData=%d pvData=%p\n",
584 pProxyDev->pUsbIns->pszName, pUrb, pUrb->pszDesc, pUrb->EndPt, pUrb->enmDir, pUrb->cbData, pUrb->abData));
585
586 PUSBPROXYURBSOL pUrbSol = usbProxySolarisUrbAlloc(pDevSol);
587 if (RT_UNLIKELY(!pUrbSol))
588 {
589 LogRel((USBPROXY ": usbProxySolarisUrbQueue: Failed to allocate URB\n"));
590 return VERR_NO_MEMORY;
591 }
592
593 pUrbSol->pVUsbUrb = pUrb;
594 pUrbSol->pDevSol = pDevSol;
595
596 uint8_t EndPt = pUrb->EndPt;
597 if (EndPt)
598 EndPt |= pUrb->enmDir == VUSBDIRECTION_IN ? VUSB_DIR_TO_HOST : VUSB_DIR_TO_DEVICE;
599
600 VBOXUSBREQ_URB UrbReq;
601 UrbReq.pvUrbR3 = pUrbSol;
602 UrbReq.bEndpoint = EndPt;
603 UrbReq.enmType = pUrb->enmType;
604 UrbReq.enmDir = pUrb->enmDir;
605 UrbReq.enmStatus = pUrb->enmStatus;
606 UrbReq.fShortOk = !pUrb->fShortNotOk;
607 UrbReq.cbData = pUrb->cbData;
608 UrbReq.pvData = &pUrb->abData[0];
609
610 Log6((USBPROXY ": Sending: EndPt=%#x Dir=%d cbData=%u\n", pUrb->EndPt, pUrb->enmDir, pUrb->cbData));
611
612 if (pUrb->enmType == VUSBXFERTYPE_ISOC)
613 {
614 UrbReq.cIsocPkts = pUrb->cIsocPkts;
615 for (unsigned i = 0; i < pUrb->cIsocPkts; i++)
616 {
617 UrbReq.aIsocPkts[i].cbPkt = pUrb->aIsocPkts[i].cb;
618 UrbReq.aIsocPkts[i].cbActPkt = 0;
619 UrbReq.aIsocPkts[i].enmStatus = VUSBSTATUS_INVALID;
620 }
621 }
622
623 int rc = usbProxySolarisIOCtl(pDevSol, VBOXUSB_IOCTL_SEND_URB, &UrbReq, sizeof(UrbReq));
624 if (RT_SUCCESS(rc))
625 {
626 if (pUrb->enmType == VUSBXFERTYPE_ISOC)
627 LogFlow((USBPROXY ":usbProxySolarisUrbQueue: Success cbData=%d\n", pUrb->cbData));
628 pUrb->Dev.pvPrivate = pUrbSol;
629 return VINF_SUCCESS;
630 }
631
632 if (rc != VERR_VUSB_DEVICE_NOT_ATTACHED)
633 {
634 LogRel((USBPROXY ": usbProxySolarisUrbQueue: Failed! pProxyDev=%s pUrb=%p EndPt=%#x bEndpoint=%#x enmType=%d "
635 "enmDir=%d cbData=%u rc=%Rrc\n", pProxyDev->pUsbIns->pszName, pUrb, pUrb->EndPt,
636 UrbReq.bEndpoint, pUrb->enmType, pUrb->enmDir, pUrb->cbData, rc));
637 }
638
639 return rc;
640}
641
642
643/**
644 * Cancels a URB.
645 *
646 * The URB requires reaping, so we don't change its state.
647 * @remark There isn't any way to cancel a specific asynchronous request
648 * on Solaris. So we just abort pending URBs on the pipe.
649 */
650static DECLCALLBACK(int) usbProxySolarisUrbCancel(PUSBPROXYDEV pProxyDev, PVUSBURB pUrb)
651{
652 PUSBPROXYURBSOL pUrbSol = (PUSBPROXYURBSOL)pUrb->Dev.pvPrivate;
653 PUSBPROXYDEVSOL pDevSol = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVSOL);
654 AssertPtrReturn(pDevSol, VERR_INVALID_POINTER);
655
656 LogFlowFunc((USBPROXY ": usbProxySolarisUrbCancel: pUrb=%p pUrbSol=%p pDevSol=%p\n", pUrb, pUrbSol, pUrbSol->pDevSol));
657
658 /* Aborting the control pipe isn't supported, pretend success. */
659 if (!pUrb->EndPt)
660 return VINF_SUCCESS;
661
662 VBOXUSBREQ_ABORT_PIPE AbortPipeReq;
663 AbortPipeReq.bEndpoint = pUrb->EndPt | (pUrb->enmDir == VUSBDIRECTION_IN ? VUSB_DIR_TO_HOST : VUSB_DIR_TO_DEVICE);
664 int rc = usbProxySolarisIOCtl(pDevSol, VBOXUSB_IOCTL_ABORT_PIPE, &AbortPipeReq, sizeof(AbortPipeReq));
665 if ( RT_FAILURE(rc)
666 && rc != VERR_VUSB_DEVICE_NOT_ATTACHED)
667 LogRel((USBPROXY ": usbProxySolarisUrbCancel: Failed to abort pipe. rc=%Rrc\n", rc));
668
669 LogFlow((USBPROXY ": usbProxySolarisUrbCancel: returns rc=%Rrc\n", rc));
670 return rc;
671}
672
673
674/**
675 * Reap URBs in-flight on a device.
676 *
677 * @returns Pointer to a completed URB.
678 * @returns NULL if no URB was completed.
679 * @param pProxyDev The device.
680 * @param cMillies Number of milliseconds to wait. Use 0 to not wait at all.
681 */
682static DECLCALLBACK(PVUSBURB) usbProxySolarisUrbReap(PUSBPROXYDEV pProxyDev, RTMSINTERVAL cMillies)
683{
684 LogFlowFunc((USBPROXY ":usbProxySolarisUrbReap pProxyDev=%p cMillies=%u\n", pProxyDev, cMillies));
685
686 PUSBPROXYDEVSOL pDevSol = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVSOL);
687
688 /*
689 * Don't block if nothing is in the air.
690 */
691 if (!pDevSol->pInFlightHead)
692 return NULL;
693
694 /*
695 * Deque URBs inflight or those landed.
696 */
697 if (cMillies > 0)
698 {
699 for (;;)
700 {
701 int cMilliesWait = cMillies == RT_INDEFINITE_WAIT ? -1 : (int)cMillies;
702
703 struct pollfd aFd[2];
704 size_t const cFds = RT_ELEMENTS(aFd);
705
706 aFd[0].fd = RTFileToNative(pDevSol->hFile);
707 aFd[0].events = POLLIN;
708 aFd[0].revents = 0;
709
710 aFd[1].fd = RTPipeToNative(pDevSol->hPipeWakeupR);
711 aFd[1].events = POLLIN;
712 aFd[1].revents = 0;
713
714 int rc = poll(&aFd[0], cFds, cMilliesWait);
715 if (rc > 0)
716 {
717 if (aFd[0].revents & POLLHUP)
718 {
719 LogRel((USBPROXY ": USB Device '%s' disconnected!\n", pDevSol->pProxyDev->pUsbIns->pszName));
720 pProxyDev->fDetached = true;
721 usbProxySolarisCloseFile(pDevSol);
722 }
723
724 if (aFd[1].revents & POLLIN)
725 {
726 /* Got woken up, drain pipe. */
727 uint8_t bRead;
728 size_t cbIgnored = 0;
729 RTPipeRead(pDevSol->hPipeWakeupR, &bRead, 1, &cbIgnored);
730
731 /*
732 * It is possible that we got woken up and have an URB pending
733 * for completion. Do it on the way out. Otherwise return
734 * immediately to the caller.
735 */
736 if (!(aFd[0].revents & POLLIN))
737 return NULL;
738 }
739 break;
740 }
741 else if (rc == 0)
742 return NULL;
743 else if (errno != EAGAIN)
744 {
745 LogFlow((USBPROXY ":usbProxySolarisUrbReap Poll rc=%d errno=%d\n", rc, errno));
746 return NULL;
747 }
748 }
749 }
750
751 usbProxySolarisUrbComplete(pDevSol);
752
753 /*
754 * Any URBs pending delivery?
755 */
756 PVUSBURB pUrb = NULL;
757 while ( pDevSol->pTaxingHead
758 && !pUrb)
759 {
760 RTCritSectEnter(&pDevSol->CritSect);
761
762 PUSBPROXYURBSOL pUrbSol = pDevSol->pTaxingHead;
763 if (pUrbSol)
764 {
765 pUrb = pUrbSol->pVUsbUrb;
766 if (pUrb)
767 {
768 /*
769 * Remove it from the taxing list and move it to the free list.
770 */
771 pUrb->Dev.pvPrivate = NULL;
772 usbProxySolarisUrbFree(pDevSol, pUrbSol);
773 }
774 }
775 RTCritSectLeave(&pDevSol->CritSect);
776 }
777
778 return pUrb;
779}
780
781
782/**
783 * Reads a completed/error'd URB from the client driver (no waiting).
784 *
785 * @param pDevSol The Solaris device instance.
786 */
787static PVUSBURB usbProxySolarisUrbComplete(PUSBPROXYDEVSOL pDevSol)
788{
789 LogFlowFunc((USBPROXY ": usbProxySolarisUrbComplete: pDevSol=%p\n", pDevSol));
790
791 VBOXUSBREQ_URB UrbReq;
792 bzero(&UrbReq, sizeof(UrbReq));
793
794 int rc = usbProxySolarisIOCtl(pDevSol, VBOXUSB_IOCTL_REAP_URB, &UrbReq, sizeof(UrbReq));
795 if (RT_SUCCESS(rc))
796 {
797 if (UrbReq.pvUrbR3)
798 {
799 PUSBPROXYURBSOL pUrbSol = (PUSBPROXYURBSOL)UrbReq.pvUrbR3;
800 PVUSBURB pUrb = pUrbSol->pVUsbUrb;
801 if (RT_LIKELY(pUrb))
802 {
803 Assert(pUrb->u32Magic == VUSBURB_MAGIC);
804
805 /*
806 * Update the URB.
807 */
808 if ( pUrb->enmType == VUSBXFERTYPE_ISOC
809 && pUrb->enmDir == VUSBDIRECTION_IN)
810 {
811 size_t cbData = 0;
812 for (unsigned i = 0; i < UrbReq.cIsocPkts; i++)
813 {
814 pUrb->aIsocPkts[i].cb = UrbReq.aIsocPkts[i].cbActPkt;
815 cbData += UrbReq.aIsocPkts[i].cbActPkt;
816 pUrb->aIsocPkts[i].enmStatus = UrbReq.aIsocPkts[i].enmStatus;
817 }
818
819 LogFlow((USBPROXY ":usbProxySolarisUrbComplete: Isoc cbData=%d cbActPktSum=%d\n", pUrb->cbData, cbData));
820 pUrb->cbData = cbData;
821 pUrb->enmStatus = UrbReq.enmStatus;
822 }
823 else
824 {
825 pUrb->cbData = UrbReq.cbData;
826 pUrb->enmStatus = UrbReq.enmStatus;
827 }
828
829 RTCritSectEnter(&pDevSol->CritSect);
830
831 /*
832 * Remove from the active list.
833 */
834 if (pUrbSol->pNext)
835 pUrbSol->pNext->pPrev = pUrbSol->pPrev;
836 if (pUrbSol->pPrev)
837 pUrbSol->pPrev->pNext = pUrbSol->pNext;
838 else
839 {
840 Assert(pDevSol->pInFlightHead == pUrbSol);
841 pDevSol->pInFlightHead = pUrbSol->pNext;
842 }
843
844 /*
845 * Add to the tail of the taxing list.
846 */
847 pUrbSol->pNext = NULL;
848 pUrbSol->pPrev = pDevSol->pTaxingTail;
849 if (pDevSol->pTaxingTail)
850 pDevSol->pTaxingTail->pNext = pUrbSol;
851 else
852 pDevSol->pTaxingHead = pUrbSol;
853 pDevSol->pTaxingTail = pUrbSol;
854
855 RTCritSectLeave(&pDevSol->CritSect);
856
857 Log6((USBPROXY ": Reaping: EndPt=%#x Dir=%d cbData=%u\n", pUrb->EndPt, pUrb->enmDir, pUrb->cbData));
858 if (pUrb->cbData < 1024)
859 Log6(("%.*Rhxd\n", pUrb->cbData, pUrb->abData));
860 return pUrb;
861 }
862 }
863 }
864 else
865 {
866 if (rc != VERR_VUSB_DEVICE_NOT_ATTACHED)
867 LogRel((USBPROXY ": Reaping URB failed. rc=%Rrc\n", rc));
868 }
869
870 return NULL;
871}
872
873
874static DECLCALLBACK(int) usbProxySolarisWakeup(PUSBPROXYDEV pProxyDev)
875{
876 PUSBPROXYDEVSOL pDevSol = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVSOL);
877 size_t cbIgnored;
878
879 LogFlowFunc(("pProxyDev=%p\n", pProxyDev));
880
881 return RTPipeWrite(pDevSol->hPipeWakeupW, "", 1, &cbIgnored);
882}
883
884
885/**
886 * The Solaris USB Proxy Backend.
887 */
888extern const USBPROXYBACK g_USBProxyDeviceHost =
889{
890 /* pszName */
891 "host",
892 /* cbBackend */
893 sizeof(USBPROXYDEVSOL),
894 usbProxySolarisOpen,
895 NULL,
896 usbProxySolarisClose,
897 usbProxySolarisReset,
898 usbProxySolarisSetConfig,
899 usbProxySolarisClaimInterface,
900 usbProxySolarisReleaseInterface,
901 usbProxySolarisSetInterface,
902 usbProxySolarisClearHaltedEp,
903 usbProxySolarisUrbQueue,
904 usbProxySolarisUrbCancel,
905 usbProxySolarisUrbReap,
906 usbProxySolarisWakeup,
907 0
908};
909
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