VirtualBox

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

Last change on this file since 106061 was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

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