VirtualBox

source: vbox/trunk/src/VBox/Devices/USB/darwin/USBProxyDevice-darwin.cpp@ 104503

Last change on this file since 104503 was 104503, checked in by vboxsync, 9 months ago

Devices/USB/USBProxyDevice-darwin.cpp: Resolve IOServiceAuthorize() dynamically because it is not exposed in older versions of macOS, bugref:10684

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 75.7 KB
Line 
1/* $Id: USBProxyDevice-darwin.cpp 104503 2024-05-03 12:33:29Z vboxsync $ */
2/** @file
3 * USB device proxy - the Darwin backend.
4 */
5
6/*
7 * Copyright (C) 2006-2023 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#define __STDC_LIMIT_MACROS
34#define __STDC_CONSTANT_MACROS
35
36#include <mach/mach.h>
37#include <Carbon/Carbon.h>
38#include <IOKit/IOKitLib.h>
39#include <mach/mach_error.h>
40#include <IOKit/usb/IOUSBLib.h>
41#include <IOKit/IOCFPlugIn.h>
42#ifndef __MAC_10_10 /* Quick hack: The following two masks appeared in 10.10. */
43# define kUSBReEnumerateReleaseDeviceMask RT_BIT_32(29)
44# define kUSBReEnumerateCaptureDeviceMask RT_BIT_32(30)
45#endif
46
47#include <VBox/log.h>
48#include <VBox/err.h>
49#include <VBox/vmm/pdm.h>
50
51#include <iprt/assert.h>
52#include <iprt/critsect.h>
53#include <iprt/ldr.h>
54#include <iprt/list.h>
55#include <iprt/mem.h>
56#include <iprt/once.h>
57#include <iprt/string.h>
58#include <iprt/time.h>
59
60#include "../USBProxyDevice.h"
61#include <VBox/usblib.h>
62
63
64/*********************************************************************************************************************************
65* Defined Constants And Macros *
66*********************************************************************************************************************************/
67/** An experiment... */
68//#define USE_LOW_LATENCY_API 1
69
70
71/*********************************************************************************************************************************
72* Structures and Typedefs *
73*********************************************************************************************************************************/
74/** Forward declaration of the Darwin interface structure. */
75typedef struct USBPROXYIFOSX *PUSBPROXYIFOSX;
76
77
78/** @name IOKitLib declarations and definitions for IOServiceAuthorize() which is not available in all SDKs.
79 * @{ */
80/** IOServiceAuthorize in IOKit. */
81typedef kern_return_t (* PFNIOSERVICEAUTHORIZE)(io_service_t, uint32_t options);
82
83#ifndef kIOServiceInteractionAllowed
84# define kIOServiceInteractionAllowed 0x00000001
85#endif
86/** @} */
87
88
89/**
90 * A low latency isochronous buffer.
91 *
92 * These are allocated in chunks on an interface level, see USBPROXYISOCBUFCOL.
93 */
94typedef struct USBPROXYISOCBUF
95{
96 /** Whether this buffer is in use or not. */
97 bool volatile fUsed;
98 /** Pointer to the buffer. */
99 void *pvBuf;
100 /** Pointer to an array of 8 frames. */
101 IOUSBLowLatencyIsocFrame *paFrames;
102} USBPROXYISOCBUF, *PUSBPROXYISOCBUF;
103
104
105/**
106 * Isochronous buffer collection (associated with an interface).
107 *
108 * These are allocated in decent sized chunks and there isn't supposed
109 * to be too many of these per interface.
110 */
111typedef struct USBPROXYISOCBUFCOL
112{
113 /** Write or Read buffers? */
114 USBLowLatencyBufferType enmType;
115 /** The next buffer collection on this interface. */
116 struct USBPROXYISOCBUFCOL *pNext;
117 /** The buffer. */
118 void *pvBuffer;
119 /** The frame. */
120 void *pvFrames;
121 /** The buffers.
122 * The number of buffers here is decided by pvFrame begin allocated in
123 * GUEST_PAGE_SIZE chunks. The size of IOUSBLowLatencyIsocFrame is 16 bytes
124 * and we require 8 of those per buffer. GUEST_PAGE_SIZE / (16 * 8) = 32.
125 * @remarks Don't allocate too many as it may temporarily halt the system if
126 * some pool is low / exhausted. (Contiguous memory woes on mach.)
127 */
128 USBPROXYISOCBUF aBuffers[/*32*/ 4];
129} USBPROXYISOCBUFCOL, *PUSBPROXYISOCBUFCOL;
130
131AssertCompileSize(IOUSBLowLatencyIsocFrame, 16);
132
133/**
134 * Per-urb data for the Darwin usb proxy backend.
135 *
136 * This is required to track in-flight and landed URBs
137 * since we take down the URBs in a different thread (perhaps).
138 */
139typedef struct USBPROXYURBOSX
140{
141 /** Pointer to the next Darwin URB. */
142 struct USBPROXYURBOSX *pNext;
143 /** Pointer to the previous Darwin URB. */
144 struct USBPROXYURBOSX *pPrev;
145 /** The millisecond timestamp when this URB was submitted. */
146 uint64_t u64SubmitTS;
147 /** Pointer to the VUSB URB.
148 * This is set to NULL if canceled. */
149 PVUSBURB pVUsbUrb;
150 /** Pointer to the Darwin device. */
151 struct USBPROXYDEVOSX *pDevOsX;
152 /** The transfer type. */
153 VUSBXFERTYPE enmType;
154 /** Union with data depending on transfer type. */
155 union
156 {
157 /** The control message. */
158 IOUSBDevRequest ControlMsg;
159 /** The Isochronous Data. */
160 struct
161 {
162#ifdef USE_LOW_LATENCY_API
163 /** The low latency isochronous buffer. */
164 PUSBPROXYISOCBUF pBuf;
165 /** Array of frames parallel to the one in VUSBURB. (Same as pBuf->paFrames.) */
166 IOUSBLowLatencyIsocFrame *aFrames;
167#else
168 /** Array of frames parallel to the one in VUSBURB. */
169 IOUSBIsocFrame aFrames[8];
170#endif
171 } Isoc;
172 } u;
173} USBPROXYURBOSX, *PUSBPROXYURBOSX;
174
175/**
176 * Per-pipe data for the Darwin usb proxy backend.
177 */
178typedef struct USBPROXYPIPEOSX
179{
180 /** The endpoint number. */
181 uint8_t u8Endpoint;
182 /** The IOKit pipe reference. */
183 uint8_t u8PipeRef;
184 /** The pipe Transfer type type. */
185 uint8_t u8TransferType;
186 /** The pipe direction. */
187 uint8_t u8Direction;
188 /** The endpoint interval. (interrupt) */
189 uint8_t u8Interval;
190 /** Full-speed device indicator (isochronous pipes only). */
191 bool fIsFullSpeed;
192 /** The max packet size. */
193 uint16_t u16MaxPacketSize;
194 /** The next frame number (isochronous pipes only). */
195 uint64_t u64NextFrameNo;
196} USBPROXYPIPEOSX, *PUSBPROXYPIPEOSX, **PPUSBPROXYPIPEOSX;
197
198typedef struct RUNLOOPREFLIST
199{
200 RTLISTNODE List;
201 CFRunLoopRef RunLoopRef;
202} RUNLOOPREFLIST, *PRUNLOOPREFLIST;
203typedef RUNLOOPREFLIST **PPRUNLOOPREFLIST;
204
205/**
206 * Per-interface data for the Darwin usb proxy backend.
207 */
208typedef struct USBPROXYIFOSX
209{
210 /** Pointer to the next interface. */
211 struct USBPROXYIFOSX *pNext;
212 /** The interface number. */
213 uint8_t u8Interface;
214 /** The current alternative interface setting.
215 * This is used to skip unnecessary SetAltInterface calls. */
216 uint8_t u8AltSetting;
217 /** The interface class. (not really used) */
218 uint8_t u8Class;
219 /** The interface protocol. (not really used) */
220 uint8_t u8Protocol;
221 /** The number of pipes. */
222 uint8_t cPipes;
223 /** Array containing all the pipes. (Currently unsorted.) */
224 USBPROXYPIPEOSX aPipes[kUSBMaxPipes];
225 /** The IOUSBDeviceInterface. */
226 IOUSBInterfaceInterface245 **ppIfI;
227 /** The run loop source for the async operations on the interface level. */
228 CFRunLoopSourceRef RunLoopSrcRef;
229 /** List of isochronous buffer collections.
230 * These are allocated on demand by the URB queuing routine and then recycled until the interface is destroyed. */
231 RTLISTANCHOR HeadOfRunLoopLst;
232 PUSBPROXYISOCBUFCOL pIsocBufCols;
233} USBPROXYIFOSX, *PUSBPROXYIFOSX, **PPUSBPROXYIFOSX;
234/** Pointer to a pointer to an darwin interface. */
235typedef USBPROXYIFOSX **PPUSBPROXYIFOSX;
236
237/**
238 * Per-device Data for the Darwin usb proxy backend.
239 */
240typedef struct USBPROXYDEVOSX
241{
242 /** The USB Device IOService object. */
243 io_object_t USBDevice;
244 /** The IOUSBDeviceInterface. */
245 IOUSBDeviceInterface245 **ppDevI;
246 /** The run loop source for the async operations on the device level
247 * (i.e. the default control pipe stuff). */
248 CFRunLoopSourceRef RunLoopSrcRef;
249 /** we want to add and remove RunLoopSourceRefs to run loop's of
250 * every EMT thread participated in USB processing. */
251 RTLISTANCHOR HeadOfRunLoopLst;
252 /** Pointer to the proxy device instance. */
253 PUSBPROXYDEV pProxyDev;
254
255 /** Pointer to the first interface. */
256 PUSBPROXYIFOSX pIfHead;
257 /** Pointer to the last interface. */
258 PUSBPROXYIFOSX pIfTail;
259
260 /** Critical section protecting the lists. */
261 RTCRITSECT CritSect;
262 /** The list of free Darwin URBs. Singly linked. */
263 PUSBPROXYURBOSX pFreeHead;
264 /** The list of landed Darwin URBs. Doubly linked.
265 * Only the split head will appear in this list. */
266 PUSBPROXYURBOSX pTaxingHead;
267 /** The tail of the landed Darwin URBs. */
268 PUSBPROXYURBOSX pTaxingTail;
269 /** Last reaper runloop reference, there can be only one runloop at a time. */
270 CFRunLoopRef hRunLoopReapingLast;
271 /** Runloop source for waking up the reaper thread. */
272 CFRunLoopSourceRef hRunLoopSrcWakeRef;
273 /** List of threads used for reaping which can be woken up. */
274 RTLISTANCHOR HeadOfRunLoopWakeLst;
275 /** Runloop reference of the thread reaping. */
276 volatile CFRunLoopRef hRunLoopReaping;
277 /** Flag whether the reaping thread is about the be waked. */
278 volatile bool fReapingThreadWake;
279} USBPROXYDEVOSX, *PUSBPROXYDEVOSX;
280
281
282/*********************************************************************************************************************************
283* Global Variables *
284*********************************************************************************************************************************/
285static RTONCE g_usbProxyDarwinOnce = RTONCE_INITIALIZER;
286/** The runloop mode we use.
287 * Since it's difficult to remove this, we leak it to prevent crashes.
288 * @bugref{4407} */
289static CFStringRef g_pRunLoopMode = NULL;
290/** The IO Master Port.
291 * Not worth cleaning up. */
292static mach_port_t g_MasterPort = MACH_PORT_NULL;
293/** Pointer to the IOServiceAuthorize() method. */
294static PFNIOSERVICEAUTHORIZE g_pfnIOServiceAuthorize = NULL;
295
296
297/**
298 * Init once callback that sets up g_MasterPort and g_pRunLoopMode.
299 *
300 * @returns IPRT status code.
301 *
302 * @param pvUser1 NULL, ignored.
303 */
304static DECLCALLBACK(int32_t) usbProxyDarwinInitOnce(void *pvUser1)
305{
306 RT_NOREF(pvUser1);
307
308 RTLDRMOD hMod = NIL_RTLDRMOD;
309 int rc = RTLdrLoadEx("/System/Library/Frameworks/IOKit.framework/Versions/Current/IOKit", &hMod, RTLDRLOAD_FLAGS_NO_SUFFIX | RTLDRLOAD_FLAGS_NO_UNLOAD,
310 NULL);
311 if (RT_SUCCESS(rc))
312 {
313 rc = RTLdrGetSymbol(hMod, "IOServiceAuthorize", (void **)&g_pfnIOServiceAuthorize);
314 if (RT_FAILURE(rc))
315 LogRel(("USB: Failed to resolve IOServiceAuthorize(), capturing USB devices might not work (%Rrc)\n", rc));
316 RTLdrClose(hMod);
317 }
318
319 RT_GCC_NO_WARN_DEPRECATED_BEGIN
320 kern_return_t krc = IOMasterPort(MACH_PORT_NULL, &g_MasterPort); /* Deprecated since 12.0. */
321 RT_GCC_NO_WARN_DEPRECATED_END
322 if (krc == KERN_SUCCESS)
323 {
324 g_pRunLoopMode = CFStringCreateWithCString(kCFAllocatorDefault, "VBoxUsbProxyMode", kCFStringEncodingUTF8);
325 if (g_pRunLoopMode)
326 return VINF_SUCCESS;
327 rc = VERR_INTERNAL_ERROR_5;
328 }
329 else
330 rc = RTErrConvertFromDarwin(krc);
331 return rc;
332}
333
334/**
335 * Kicks the reaper thread if it sleeps currently to respond to state changes
336 * or to pick up completed URBs.
337 *
338 * @param pDevOsX The darwin device instance data.
339 */
340static void usbProxyDarwinReaperKick(PUSBPROXYDEVOSX pDevOsX)
341{
342 CFRunLoopRef hRunLoopWake = (CFRunLoopRef)ASMAtomicReadPtr((void * volatile *)&pDevOsX->hRunLoopReaping);
343 if (hRunLoopWake)
344 {
345 LogFlowFunc(("Waking runloop %p\n", hRunLoopWake));
346 CFRunLoopSourceSignal(pDevOsX->hRunLoopSrcWakeRef);
347 CFRunLoopWakeUp(hRunLoopWake);
348 }
349}
350
351/**
352 * Adds Source ref to current run loop and adds it the list of runloops.
353 */
354static int usbProxyDarwinAddRunLoopRef(PRTLISTANCHOR pListHead,
355 CFRunLoopSourceRef SourceRef)
356{
357 AssertPtrReturn(pListHead, VERR_INVALID_PARAMETER);
358 AssertReturn(CFRunLoopSourceIsValid(SourceRef), VERR_INVALID_PARAMETER);
359
360 if (CFRunLoopContainsSource(CFRunLoopGetCurrent(), SourceRef, g_pRunLoopMode))
361 return VINF_SUCCESS;
362
363 /* Add to the list */
364 PRUNLOOPREFLIST pListNode = (PRUNLOOPREFLIST)RTMemAllocZ(sizeof(RUNLOOPREFLIST));
365 if (!pListNode)
366 return VERR_NO_MEMORY;
367
368 pListNode->RunLoopRef = CFRunLoopGetCurrent();
369
370 CFRetain(pListNode->RunLoopRef);
371 CFRetain(SourceRef); /* We want to be aware of releasing */
372
373 CFRunLoopAddSource(pListNode->RunLoopRef, SourceRef, g_pRunLoopMode);
374
375 RTListInit(&pListNode->List);
376
377 RTListAppend((PRTLISTNODE)pListHead, &pListNode->List);
378
379 return VINF_SUCCESS;
380}
381
382
383/*
384 * Removes all source reference from mode of run loop's we've registered them.
385 *
386 */
387static int usbProxyDarwinRemoveSourceRefFromAllRunLoops(PRTLISTANCHOR pHead,
388 CFRunLoopSourceRef SourceRef)
389{
390 AssertPtrReturn(pHead, VERR_INVALID_PARAMETER);
391
392 while (!RTListIsEmpty(pHead))
393 {
394 PRUNLOOPREFLIST pNode = RTListGetFirst(pHead, RUNLOOPREFLIST, List);
395 /* XXX: Should Release Reference? */
396 Assert(CFGetRetainCount(pNode->RunLoopRef));
397
398 CFRunLoopRemoveSource(pNode->RunLoopRef, SourceRef, g_pRunLoopMode);
399 CFRelease(SourceRef);
400 CFRelease(pNode->RunLoopRef);
401
402 RTListNodeRemove(&pNode->List);
403
404 RTMemFree(pNode);
405 }
406
407 return VINF_SUCCESS;
408}
409
410
411/**
412 * Allocates a Darwin URB request structure.
413 *
414 * @returns Pointer to an active URB request.
415 * @returns NULL on failure.
416 *
417 * @param pDevOsX The darwin proxy device.
418 */
419static PUSBPROXYURBOSX usbProxyDarwinUrbAlloc(PUSBPROXYDEVOSX pDevOsX)
420{
421 PUSBPROXYURBOSX pUrbOsX;
422
423 RTCritSectEnter(&pDevOsX->CritSect);
424
425 /*
426 * Try remove a Darwin URB from the free list, if none there allocate a new one.
427 */
428 pUrbOsX = pDevOsX->pFreeHead;
429 if (pUrbOsX)
430 {
431 pDevOsX->pFreeHead = pUrbOsX->pNext;
432 RTCritSectLeave(&pDevOsX->CritSect);
433 }
434 else
435 {
436 RTCritSectLeave(&pDevOsX->CritSect);
437 pUrbOsX = (PUSBPROXYURBOSX)RTMemAlloc(sizeof(*pUrbOsX));
438 if (!pUrbOsX)
439 return NULL;
440 }
441 pUrbOsX->pVUsbUrb = NULL;
442 pUrbOsX->pDevOsX = pDevOsX;
443 pUrbOsX->enmType = VUSBXFERTYPE_INVALID;
444
445 return pUrbOsX;
446}
447
448
449#ifdef USE_LOW_LATENCY_API
450/**
451 * Allocates an low latency isochronous buffer.
452 *
453 * @returns VBox status code.
454 * @param pUrbOsX The OsX URB to allocate it for.
455 * @param pIf The interface to allocated it from.
456 */
457static int usbProxyDarwinUrbAllocIsocBuf(PUSBPROXYURBOSX pUrbOsX, PUSBPROXYIFOSX pIf)
458{
459 USBLowLatencyBufferType enmLLType = pUrbOsX->pVUsbUrb->enmDir == VUSBDIRECTION_IN
460 ? kUSBLowLatencyWriteBuffer : kUSBLowLatencyReadBuffer;
461
462 /*
463 * Walk the buffer collection list and look for an unused one.
464 */
465 pUrbOsX->u.Isoc.pBuf = NULL;
466 for (PUSBPROXYISOCBUFCOL pCur = pIf->pIsocBufCols; pCur; pCur = pCur->pNext)
467 if (pCur->enmType == enmLLType)
468 for (unsigned i = 0; i < RT_ELEMENTS(pCur->aBuffers); i++)
469 if (!pCur->aBuffers[i].fUsed)
470 {
471 pCur->aBuffers[i].fUsed = true;
472 pUrbOsX->u.Isoc.pBuf = &pCur->aBuffers[i];
473 AssertPtr(pUrbOsX->u.Isoc.pBuf);
474 AssertPtr(pUrbOsX->u.Isoc.pBuf->pvBuf);
475 pUrbOsX->u.Isoc.aFrames = pCur->aBuffers[i].paFrames;
476 AssertPtr(pUrbOsX->u.Isoc.aFrames);
477 return VINF_SUCCESS;
478 }
479
480 /*
481 * Didn't find an empty one, create a new buffer collection and take the first buffer.
482 */
483 PUSBPROXYISOCBUFCOL pNew = (PUSBPROXYISOCBUFCOL)RTMemAllocZ(sizeof(*pNew));
484 AssertReturn(pNew, VERR_NO_MEMORY);
485
486 IOReturn irc = (*pIf->ppIfI)->LowLatencyCreateBuffer(pIf->ppIfI, &pNew->pvBuffer, 8192 * RT_ELEMENTS(pNew->aBuffers), enmLLType);
487 if ((irc == kIOReturnSuccess) != RT_VALID_PTR(pNew->pvBuffer))
488 {
489 AssertPtr(pNew->pvBuffer);
490 irc = kIOReturnNoMemory;
491 }
492 if (irc == kIOReturnSuccess)
493 {
494 /** @todo GUEST_PAGE_SIZE or HOST_PAGE_SIZE or just 4K? */
495 irc = (*pIf->ppIfI)->LowLatencyCreateBuffer(pIf->ppIfI, &pNew->pvFrames, GUEST_PAGE_SIZE, kUSBLowLatencyFrameListBuffer);
496 if ((irc == kIOReturnSuccess) != RT_VALID_PTR(pNew->pvFrames))
497 {
498 AssertPtr(pNew->pvFrames);
499 irc = kIOReturnNoMemory;
500 }
501 if (irc == kIOReturnSuccess)
502 {
503 for (unsigned i = 0; i < RT_ELEMENTS(pNew->aBuffers); i++)
504 {
505 //pNew->aBuffers[i].fUsed = false;
506 pNew->aBuffers[i].paFrames = &((IOUSBLowLatencyIsocFrame *)pNew->pvFrames)[i * 8];
507 pNew->aBuffers[i].pvBuf = (uint8_t *)pNew->pvBuffer + i * 8192;
508 }
509
510 pNew->aBuffers[0].fUsed = true;
511 pUrbOsX->u.Isoc.aFrames = pNew->aBuffers[0].paFrames;
512 pUrbOsX->u.Isoc.pBuf = &pNew->aBuffers[0];
513
514 pNew->enmType = enmLLType;
515 pNew->pNext = pIf->pIsocBufCols;
516 pIf->pIsocBufCols = pNew;
517
518#if 0 /* doesn't help :-/ */
519 /*
520 * If this is the first time we're here, try mess with the policy?
521 */
522 if (!pNew->pNext)
523 for (unsigned iPipe = 0; iPipe < pIf->cPipes; iPipe++)
524 if (pIf->aPipes[iPipe].u8TransferType == kUSBIsoc)
525 {
526 irc = (*pIf->ppIfI)->SetPipePolicy(pIf->ppIfI, pIf->aPipes[iPipe].u8PipeRef,
527 pIf->aPipes[iPipe].u16MaxPacketSize, pIf->aPipes[iPipe].u8Interval);
528 AssertMsg(irc == kIOReturnSuccess, ("%#x\n", irc));
529 }
530#endif
531
532 return VINF_SUCCESS;
533 }
534
535 /* bail out */
536 (*pIf->ppIfI)->LowLatencyDestroyBuffer(pIf->ppIfI, pNew->pvBuffer);
537 }
538 AssertMsgFailed(("%#x\n", irc));
539 RTMemFree(pNew);
540
541 return RTErrConvertFromDarwin(irc);
542}
543#endif /* USE_LOW_LATENCY_API */
544
545
546/**
547 * Frees a Darwin URB request structure.
548 *
549 * @param pDevOsX The darwin proxy device.
550 * @param pUrbOsX The Darwin URB to free.
551 */
552static void usbProxyDarwinUrbFree(PUSBPROXYDEVOSX pDevOsX, PUSBPROXYURBOSX pUrbOsX)
553{
554 RTCritSectEnter(&pDevOsX->CritSect);
555
556#ifdef USE_LOW_LATENCY_API
557 /*
558 * Free low latency stuff.
559 */
560 if ( pUrbOsX->enmType == VUSBXFERTYPE_ISOC
561 && pUrbOsX->u.Isoc.pBuf)
562 {
563 pUrbOsX->u.Isoc.pBuf->fUsed = false;
564 pUrbOsX->u.Isoc.pBuf = NULL;
565 }
566#endif
567
568 /*
569 * Link it into the free list.
570 */
571 pUrbOsX->pPrev = NULL;
572 pUrbOsX->pNext = pDevOsX->pFreeHead;
573 pDevOsX->pFreeHead = pUrbOsX;
574
575 pUrbOsX->pVUsbUrb = NULL;
576 pUrbOsX->pDevOsX = NULL;
577 pUrbOsX->enmType = VUSBXFERTYPE_INVALID;
578
579 RTCritSectLeave(&pDevOsX->CritSect);
580}
581
582/**
583 * Translate the IOKit status code to a VUSB status.
584 *
585 * @returns VUSB URB status code.
586 * @param irc IOKit status code.
587 */
588static VUSBSTATUS vusbProxyDarwinStatusToVUsbStatus(IOReturn irc)
589{
590 switch (irc)
591 {
592 /* IOKit OHCI VUSB */
593 case kIOReturnSuccess: /* 0 */ return VUSBSTATUS_OK;
594 case kIOUSBCRCErr: /* 1 */ return VUSBSTATUS_CRC;
595 //case kIOUSBBitstufErr: /* 2 */ return VUSBSTATUS_;
596 //case kIOUSBDataToggleErr: /* 3 */ return VUSBSTATUS_;
597 case kIOUSBPipeStalled: /* 4 */ return VUSBSTATUS_STALL;
598 case kIOReturnNotResponding: /* 5 */ return VUSBSTATUS_DNR;
599 //case kIOUSBPIDCheckErr: /* 6 */ return VUSBSTATUS_;
600 //case kIOUSBWrongPIDErr: /* 7 */ return VUSBSTATUS_;
601 case kIOReturnOverrun: /* 8 */ return VUSBSTATUS_DATA_OVERRUN;
602 case kIOReturnUnderrun: /* 9 */ return VUSBSTATUS_DATA_UNDERRUN;
603 //case kIOUSBReserved1Err: /* 10 */ return VUSBSTATUS_;
604 //case kIOUSBReserved2Err: /* 11 */ return VUSBSTATUS_;
605 //case kIOUSBBufferOverrunErr: /* 12 */ return VUSBSTATUS_;
606 //case kIOUSBBufferUnderrunErr: /* 13 */ return VUSBSTATUS_;
607 case kIOUSBNotSent1Err: /* 14 */ return VUSBSTATUS_NOT_ACCESSED/*VUSBSTATUS_OK*/;
608 case kIOUSBNotSent2Err: /* 15 */ return VUSBSTATUS_NOT_ACCESSED/*VUSBSTATUS_OK*/;
609
610 /* Other errors */
611 case kIOUSBTransactionTimeout: return VUSBSTATUS_DNR;
612 //case kIOReturnAborted: return VUSBSTATUS_CRC; - see on SET_INTERFACE...
613
614 default:
615 Log(("vusbProxyDarwinStatusToVUsbStatus: irc=%#x!!\n", irc));
616 return VUSBSTATUS_STALL;
617 }
618}
619
620
621/**
622 * Completion callback for an async URB transfer.
623 *
624 * @param pvUrbOsX The Darwin URB.
625 * @param irc The status of the operation.
626 * @param Size Possible the transfer size.
627 */
628static void usbProxyDarwinUrbAsyncComplete(void *pvUrbOsX, IOReturn irc, void *Size)
629{
630 PUSBPROXYURBOSX pUrbOsX = (PUSBPROXYURBOSX)pvUrbOsX;
631 PUSBPROXYDEVOSX pDevOsX = pUrbOsX->pDevOsX;
632 const uint32_t cb = (uintptr_t)Size;
633
634 /*
635 * Do status updates.
636 */
637 PVUSBURB pUrb = pUrbOsX->pVUsbUrb;
638 if (pUrb)
639 {
640 Assert(pUrb->u32Magic == VUSBURB_MAGIC);
641 if (pUrb->enmType == VUSBXFERTYPE_ISOC)
642 {
643#ifdef USE_LOW_LATENCY_API
644 /* copy the data. */
645 //if (pUrb->enmDir == VUSBDIRECTION_IN)
646 memcpy(pUrb->abData, pUrbOsX->u.Isoc.pBuf->pvBuf, pUrb->cbData);
647#endif
648 Log3(("AsyncComplete isoc - raw data (%d bytes):\n"
649 "%16.*Rhxd\n", pUrb->cbData, pUrb->cbData, pUrb->abData));
650 uint32_t off = 0;
651 for (unsigned i = 0; i < pUrb->cIsocPkts; i++)
652 {
653#ifdef USE_LOW_LATENCY_API
654 Log2((" %d{%d/%d-%x-%RX64}", i, pUrbOsX->u.Isoc.aFrames[i].frActCount, pUrb->aIsocPkts[i].cb, pUrbOsX->u.Isoc.aFrames[i].frStatus,
655 RT_MAKE_U64(pUrbOsX->u.Isoc.aFrames[i].frTimeStamp.lo, pUrbOsX->u.Isoc.aFrames[i].frTimeStamp.hi) ));
656#else
657 Log2((" %d{%d/%d-%x}", i, pUrbOsX->u.Isoc.aFrames[i].frActCount, pUrb->aIsocPkts[i].cb, pUrbOsX->u.Isoc.aFrames[i].frStatus));
658#endif
659 pUrb->aIsocPkts[i].enmStatus = vusbProxyDarwinStatusToVUsbStatus(pUrbOsX->u.Isoc.aFrames[i].frStatus);
660 pUrb->aIsocPkts[i].cb = pUrbOsX->u.Isoc.aFrames[i].frActCount;
661 off += pUrbOsX->u.Isoc.aFrames[i].frActCount;
662 }
663 Log2(("\n"));
664#if 0 /** @todo revisit this, wasn't working previously. */
665 for (int i = (int)pUrb->cIsocPkts - 1; i >= 0; i--)
666 Assert( !pUrbOsX->u.Isoc.aFrames[i].frActCount
667 && !pUrbOsX->u.Isoc.aFrames[i].frReqCount
668 && !pUrbOsX->u.Isoc.aFrames[i].frStatus);
669#endif
670 pUrb->cbData = off; /* 'Size' seems to be pointing at an error code or something... */
671 pUrb->enmStatus = VUSBSTATUS_OK; /* Don't use 'irc'. OHCI expects OK unless it's a really bad error. */
672 }
673 else
674 {
675 pUrb->cbData = cb;
676 pUrb->enmStatus = vusbProxyDarwinStatusToVUsbStatus(irc);
677 if (pUrb->enmType == VUSBXFERTYPE_MSG)
678 pUrb->cbData += sizeof(VUSBSETUP);
679 }
680 }
681
682 RTCritSectEnter(&pDevOsX->CritSect);
683
684 /*
685 * Link it into the taxing list.
686 */
687 pUrbOsX->pNext = NULL;
688 pUrbOsX->pPrev = pDevOsX->pTaxingTail;
689 if (pDevOsX->pTaxingTail)
690 pDevOsX->pTaxingTail->pNext = pUrbOsX;
691 else
692 pDevOsX->pTaxingHead = pUrbOsX;
693 pDevOsX->pTaxingTail = pUrbOsX;
694
695 RTCritSectLeave(&pDevOsX->CritSect);
696
697 LogFlow(("%s: usbProxyDarwinUrbAsyncComplete: cb=%d EndPt=%#x irc=%#x (%d)\n",
698 pUrb->pszDesc, cb, pUrb ? pUrb->EndPt : 0xff, irc, pUrb ? pUrb->enmStatus : 0xff));
699}
700
701/**
702 * Release all interfaces (current config).
703 *
704 * @param pDevOsX The darwin proxy device.
705 */
706static void usbProxyDarwinReleaseAllInterfaces(PUSBPROXYDEVOSX pDevOsX)
707{
708 RTCritSectEnter(&pDevOsX->CritSect);
709
710 /* Kick the reaper thread out of sleep. */
711 usbProxyDarwinReaperKick(pDevOsX);
712
713 PUSBPROXYIFOSX pIf = pDevOsX->pIfHead;
714 pDevOsX->pIfHead = pDevOsX->pIfTail = NULL;
715
716 while (pIf)
717 {
718 PUSBPROXYIFOSX pNext = pIf->pNext;
719 IOReturn irc;
720
721 if (pIf->RunLoopSrcRef)
722 {
723 int rc = usbProxyDarwinRemoveSourceRefFromAllRunLoops((PRTLISTANCHOR)&pIf->HeadOfRunLoopLst, pIf->RunLoopSrcRef);
724 AssertRC(rc);
725
726 CFRelease(pIf->RunLoopSrcRef);
727 pIf->RunLoopSrcRef = NULL;
728 RTListInit((PRTLISTNODE)&pIf->HeadOfRunLoopLst);
729 }
730
731 while (pIf->pIsocBufCols)
732 {
733 PUSBPROXYISOCBUFCOL pCur = pIf->pIsocBufCols;
734 pIf->pIsocBufCols = pCur->pNext;
735 pCur->pNext = NULL;
736
737 irc = (*pIf->ppIfI)->LowLatencyDestroyBuffer(pIf->ppIfI, pCur->pvBuffer);
738 AssertMsg(irc == kIOReturnSuccess || irc == MACH_SEND_INVALID_DEST, ("%#x\n", irc));
739 pCur->pvBuffer = NULL;
740
741 irc = (*pIf->ppIfI)->LowLatencyDestroyBuffer(pIf->ppIfI, pCur->pvFrames);
742 AssertMsg(irc == kIOReturnSuccess || irc == MACH_SEND_INVALID_DEST, ("%#x\n", irc));
743 pCur->pvFrames = NULL;
744
745 RTMemFree(pCur);
746 }
747
748 irc = (*pIf->ppIfI)->USBInterfaceClose(pIf->ppIfI);
749 AssertMsg(irc == kIOReturnSuccess || irc == kIOReturnNoDevice, ("%#x\n", irc));
750
751 (*pIf->ppIfI)->Release(pIf->ppIfI);
752 pIf->ppIfI = NULL;
753
754 RTMemFree(pIf);
755
756 pIf = pNext;
757 }
758 RTCritSectLeave(&pDevOsX->CritSect);
759}
760
761
762/**
763 * Get the properties all the pipes associated with an interface.
764 *
765 * This is used when we seize all the interface and after SET_INTERFACE.
766 *
767 * @returns VBox status code.
768 * @param pDevOsX The darwin proxy device.
769 * @param pIf The interface to get pipe props for.
770 */
771static int usbProxyDarwinGetPipeProperties(PUSBPROXYDEVOSX pDevOsX, PUSBPROXYIFOSX pIf)
772{
773 /*
774 * Get the pipe (endpoint) count (it might have changed - even on open).
775 */
776 int rc = VINF_SUCCESS;
777 bool fFullSpeed;
778 UInt32 u32UsecInFrame;
779 UInt8 cPipes;
780 IOReturn irc = (*pIf->ppIfI)->GetNumEndpoints(pIf->ppIfI, &cPipes);
781 if (irc != kIOReturnSuccess)
782 {
783 pIf->cPipes = 0;
784 if (irc == kIOReturnNoDevice)
785 rc = VERR_VUSB_DEVICE_NOT_ATTACHED;
786 else
787 rc = RTErrConvertFromDarwin(irc);
788 return rc;
789 }
790 AssertRelease(cPipes < RT_ELEMENTS(pIf->aPipes));
791 pIf->cPipes = cPipes + 1;
792
793 /* Find out if this is a full-speed interface (needed for isochronous support). */
794 irc = (*pIf->ppIfI)->GetFrameListTime(pIf->ppIfI, &u32UsecInFrame);
795 if (irc != kIOReturnSuccess)
796 {
797 pIf->cPipes = 0;
798 if (irc == kIOReturnNoDevice)
799 rc = VERR_VUSB_DEVICE_NOT_ATTACHED;
800 else
801 rc = RTErrConvertFromDarwin(irc);
802 return rc;
803 }
804 fFullSpeed = u32UsecInFrame == kUSBFullSpeedMicrosecondsInFrame;
805
806 /*
807 * Get the properties of each pipe.
808 */
809 for (unsigned i = 0; i < pIf->cPipes; i++)
810 {
811 pIf->aPipes[i].u8PipeRef = i;
812 pIf->aPipes[i].fIsFullSpeed = fFullSpeed;
813 pIf->aPipes[i].u64NextFrameNo = 0;
814 irc = (*pIf->ppIfI)->GetPipeProperties(pIf->ppIfI, i,
815 &pIf->aPipes[i].u8Direction,
816 &pIf->aPipes[i].u8Endpoint,
817 &pIf->aPipes[i].u8TransferType,
818 &pIf->aPipes[i].u16MaxPacketSize,
819 &pIf->aPipes[i].u8Interval);
820 if (irc != kIOReturnSuccess)
821 {
822 LogRel(("USB: Failed to query properties for pipe %#d / interface %#x on device '%s'. (prot=%#x class=%#x)\n",
823 i, pIf->u8Interface, pDevOsX->pProxyDev->pUsbIns->pszName, pIf->u8Protocol, pIf->u8Class));
824 if (irc == kIOReturnNoDevice)
825 rc = VERR_VUSB_DEVICE_NOT_ATTACHED;
826 else
827 rc = RTErrConvertFromDarwin(irc);
828 pIf->cPipes = i;
829 break;
830 }
831 /* reconstruct bEndpoint */
832 if (pIf->aPipes[i].u8Direction == kUSBIn)
833 pIf->aPipes[i].u8Endpoint |= 0x80;
834 Log2(("usbProxyDarwinGetPipeProperties: #If=%d EndPt=%#x Dir=%d Type=%d PipeRef=%#x MaxPktSize=%#x Interval=%#x\n",
835 pIf->u8Interface, pIf->aPipes[i].u8Endpoint, pIf->aPipes[i].u8Direction, pIf->aPipes[i].u8TransferType,
836 pIf->aPipes[i].u8PipeRef, pIf->aPipes[i].u16MaxPacketSize, pIf->aPipes[i].u8Interval));
837 }
838
839 /** @todo sort or hash these for speedy lookup... */
840 return VINF_SUCCESS;
841}
842
843
844/**
845 * Seize all interfaces (current config).
846 *
847 * @returns VBox status code.
848 * @param pDevOsX The darwin proxy device.
849 * @param fMakeTheBestOfIt If set we will not give up on error. This is for
850 * use during SET_CONFIGURATION and similar.
851 */
852static int usbProxyDarwinSeizeAllInterfaces(PUSBPROXYDEVOSX pDevOsX, bool fMakeTheBestOfIt)
853{
854 PUSBPROXYDEV pProxyDev = pDevOsX->pProxyDev;
855
856 RTCritSectEnter(&pDevOsX->CritSect);
857
858 /*
859 * Create a interface enumerator for all the interface (current config).
860 */
861 io_iterator_t Interfaces = IO_OBJECT_NULL;
862 IOUSBFindInterfaceRequest Req;
863 Req.bInterfaceClass = kIOUSBFindInterfaceDontCare;
864 Req.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
865 Req.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
866 Req.bAlternateSetting = kIOUSBFindInterfaceDontCare;
867 IOReturn irc = (*pDevOsX->ppDevI)->CreateInterfaceIterator(pDevOsX->ppDevI, &Req, &Interfaces);
868 int rc;
869 if (irc == kIOReturnSuccess)
870 {
871 /*
872 * Iterate the interfaces.
873 */
874 io_object_t Interface;
875 rc = VINF_SUCCESS;
876 while ((Interface = IOIteratorNext(Interfaces)))
877 {
878 /*
879 * Create a plug-in and query the IOUSBInterfaceInterface (cute name).
880 */
881 IOCFPlugInInterface **ppPlugInInterface = NULL;
882 kern_return_t krc;
883 SInt32 Score = 0;
884 krc = IOCreatePlugInInterfaceForService(Interface, kIOUSBInterfaceUserClientTypeID,
885 kIOCFPlugInInterfaceID, &ppPlugInInterface, &Score);
886 IOObjectRelease(Interface);
887 Interface = IO_OBJECT_NULL;
888 if (krc == KERN_SUCCESS)
889 {
890 IOUSBInterfaceInterface245 **ppIfI;
891 HRESULT hrc = (*ppPlugInInterface)->QueryInterface(ppPlugInInterface,
892 CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID245),
893 (LPVOID *)&ppIfI);
894 krc = IODestroyPlugInInterface(ppPlugInInterface); Assert(krc == KERN_SUCCESS);
895 ppPlugInInterface = NULL;
896 if (hrc == S_OK)
897 {
898 /*
899 * Query some basic properties first.
900 * (This means we can print more informative messages on failure
901 * to seize the interface.)
902 */
903 UInt8 u8Interface = 0xff;
904 irc = (*ppIfI)->GetInterfaceNumber(ppIfI, &u8Interface);
905 UInt8 u8AltSetting = 0xff;
906 if (irc == kIOReturnSuccess)
907 irc = (*ppIfI)->GetAlternateSetting(ppIfI, &u8AltSetting);
908 UInt8 u8Class = 0xff;
909 if (irc == kIOReturnSuccess)
910 irc = (*ppIfI)->GetInterfaceClass(ppIfI, &u8Class);
911 UInt8 u8Protocol = 0xff;
912 if (irc == kIOReturnSuccess)
913 irc = (*ppIfI)->GetInterfaceProtocol(ppIfI, &u8Protocol);
914 UInt8 cEndpoints = 0;
915 if (irc == kIOReturnSuccess)
916 irc = (*ppIfI)->GetNumEndpoints(ppIfI, &cEndpoints);
917 if (irc == kIOReturnSuccess)
918 {
919 /*
920 * Try seize the interface.
921 */
922 irc = (*ppIfI)->USBInterfaceOpenSeize(ppIfI);
923 if (irc == kIOReturnSuccess)
924 {
925 PUSBPROXYIFOSX pIf = (PUSBPROXYIFOSX)RTMemAllocZ(sizeof(*pIf));
926 if (pIf)
927 {
928 /*
929 * Create the per-interface entry and query the
930 * endpoint data.
931 */
932 /* initialize the entry */
933 pIf->u8Interface = u8Interface;
934 pIf->u8AltSetting = u8AltSetting;
935 pIf->u8Class = u8Class;
936 pIf->u8Protocol = u8Protocol;
937 pIf->cPipes = cEndpoints;
938 pIf->ppIfI = ppIfI;
939
940 /* query pipe/endpoint properties. */
941 rc = usbProxyDarwinGetPipeProperties(pDevOsX, pIf);
942 if (RT_SUCCESS(rc))
943 {
944 /*
945 * Create the async event source and add it to the
946 * default current run loop.
947 * (Later: Add to the worker thread run loop instead.)
948 */
949 irc = (*ppIfI)->CreateInterfaceAsyncEventSource(ppIfI, &pIf->RunLoopSrcRef);
950 if (irc == kIOReturnSuccess)
951 {
952 RTListInit((PRTLISTNODE)&pIf->HeadOfRunLoopLst);
953 usbProxyDarwinAddRunLoopRef(&pIf->HeadOfRunLoopLst,
954 pIf->RunLoopSrcRef);
955
956 /*
957 * Just link the interface into the list and we're good.
958 */
959 pIf->pNext = NULL;
960 Log(("USB: Seized interface %#x (alt=%d prot=%#x class=%#x)\n",
961 u8Interface, u8AltSetting, u8Protocol, u8Class));
962 if (pDevOsX->pIfTail)
963 pDevOsX->pIfTail = pDevOsX->pIfTail->pNext = pIf;
964 else
965 pDevOsX->pIfTail = pDevOsX->pIfHead = pIf;
966 continue;
967 }
968 rc = RTErrConvertFromDarwin(irc);
969 }
970
971 /* failure cleanup. */
972 RTMemFree(pIf);
973 }
974 }
975 else if (irc == kIOReturnExclusiveAccess)
976 {
977 LogRel(("USB: Interface %#x on device '%s' is being used by another process. (prot=%#x class=%#x)\n",
978 u8Interface, pProxyDev->pUsbIns->pszName, u8Protocol, u8Class));
979 rc = VERR_SHARING_VIOLATION;
980 }
981 else
982 {
983 LogRel(("USB: Failed to open interface %#x on device '%s'. (prot=%#x class=%#x) krc=%#x\n",
984 u8Interface, pProxyDev->pUsbIns->pszName, u8Protocol, u8Class, irc));
985 rc = VERR_OPEN_FAILED;
986 }
987 }
988 else
989 {
990 rc = RTErrConvertFromDarwin(irc);
991 LogRel(("USB: Failed to query interface properties on device '%s', irc=%#x.\n",
992 pProxyDev->pUsbIns->pszName, irc));
993 }
994 (*ppIfI)->Release(ppIfI);
995 ppIfI = NULL;
996 }
997 else if (RT_SUCCESS(rc))
998 rc = RTErrConvertFromDarwinCOM(hrc);
999 }
1000 else if (RT_SUCCESS(rc))
1001 rc = RTErrConvertFromDarwin(krc);
1002 if (!fMakeTheBestOfIt)
1003 {
1004 usbProxyDarwinReleaseAllInterfaces(pDevOsX);
1005 break;
1006 }
1007 } /* iterate */
1008 IOObjectRelease(Interfaces);
1009 }
1010 else if (irc == kIOReturnNoDevice)
1011 rc = VERR_VUSB_DEVICE_NOT_ATTACHED;
1012 else
1013 {
1014 AssertMsgFailed(("%#x\n", irc));
1015 rc = VERR_GENERAL_FAILURE;
1016 }
1017
1018 RTCritSectLeave(&pDevOsX->CritSect);
1019 return rc;
1020}
1021
1022
1023/**
1024 * Find a particular interface.
1025 *
1026 * @returns The requested interface or NULL if not found.
1027 * @param pDevOsX The darwin proxy device.
1028 * @param u8Interface The interface number.
1029 */
1030static PUSBPROXYIFOSX usbProxyDarwinGetInterface(PUSBPROXYDEVOSX pDevOsX, uint8_t u8Interface)
1031{
1032 if (!pDevOsX->pIfHead)
1033 usbProxyDarwinSeizeAllInterfaces(pDevOsX, true /* make the best out of it */);
1034
1035 PUSBPROXYIFOSX pIf;
1036 for (pIf = pDevOsX->pIfHead; pIf; pIf = pIf->pNext)
1037 if (pIf->u8Interface == u8Interface)
1038 return pIf;
1039
1040/* AssertMsgFailed(("Cannot find If#=%d\n", u8Interface)); - the 3rd quickcam interface is capture by the ****ing audio crap. */
1041 return NULL;
1042}
1043
1044
1045/**
1046 * Find a particular endpoint.
1047 *
1048 * @returns The requested interface or NULL if not found.
1049 * @param pDevOsX The darwin proxy device.
1050 * @param u8Endpoint The endpoint.
1051 * @param pu8PipeRef Where to store the darwin pipe ref.
1052 * @param ppPipe Where to store the darwin pipe pointer. (optional)
1053 */
1054static PUSBPROXYIFOSX usbProxyDarwinGetInterfaceForEndpoint(PUSBPROXYDEVOSX pDevOsX, uint8_t u8Endpoint,
1055 uint8_t *pu8PipeRef, PPUSBPROXYPIPEOSX ppPipe)
1056{
1057 if (!pDevOsX->pIfHead)
1058 usbProxyDarwinSeizeAllInterfaces(pDevOsX, true /* make the best out of it */);
1059
1060 PUSBPROXYIFOSX pIf;
1061 for (pIf = pDevOsX->pIfHead; pIf; pIf = pIf->pNext)
1062 {
1063 unsigned i = pIf->cPipes;
1064 while (i-- > 0)
1065 if (pIf->aPipes[i].u8Endpoint == u8Endpoint)
1066 {
1067 *pu8PipeRef = pIf->aPipes[i].u8PipeRef;
1068 if (ppPipe)
1069 *ppPipe = &pIf->aPipes[i];
1070 return pIf;
1071 }
1072 }
1073
1074 AssertMsgFailed(("Cannot find EndPt=%#x\n", u8Endpoint));
1075 return NULL;
1076}
1077
1078
1079/**
1080 * Gets an unsigned 32-bit integer value.
1081 *
1082 * @returns Success indicator (true/false).
1083 * @param DictRef The dictionary.
1084 * @param KeyStrRef The key name.
1085 * @param pu32 Where to store the key value.
1086 */
1087static bool usbProxyDarwinDictGetU32(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, uint32_t *pu32)
1088{
1089 CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
1090 if (ValRef)
1091 {
1092 if (CFNumberGetValue((CFNumberRef)ValRef, kCFNumberSInt32Type, pu32))
1093 return true;
1094 }
1095 *pu32 = 0;
1096 return false;
1097}
1098
1099
1100/**
1101 * Gets an unsigned 64-bit integer value.
1102 *
1103 * @returns Success indicator (true/false).
1104 * @param DictRef The dictionary.
1105 * @param KeyStrRef The key name.
1106 * @param pu64 Where to store the key value.
1107 */
1108static bool usbProxyDarwinDictGetU64(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, uint64_t *pu64)
1109{
1110 CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
1111 if (ValRef)
1112 {
1113 if (CFNumberGetValue((CFNumberRef)ValRef, kCFNumberSInt64Type, pu64))
1114 return true;
1115 }
1116 *pu64 = 0;
1117 return false;
1118}
1119
1120
1121static DECLCALLBACK(void) usbProxyDarwinPerformWakeup(void *pInfo)
1122{
1123 RT_NOREF(pInfo);
1124 return;
1125}
1126
1127
1128/* -=-=-=-=-=- The exported methods -=-=-=-=-=- */
1129
1130/**
1131 * Opens the USB Device.
1132 *
1133 * @returns VBox status code.
1134 * @param pProxyDev The device instance.
1135 * @param pszAddress The session id and/or location id of the device to open.
1136 * The format of this string is something iokit.c in Main defines, currently
1137 * it's sequences of "[l|s]=<value>" separated by ";".
1138 */
1139static DECLCALLBACK(int) usbProxyDarwinOpen(PUSBPROXYDEV pProxyDev, const char *pszAddress)
1140{
1141 LogFlow(("usbProxyDarwinOpen: pProxyDev=%p pszAddress=%s\n", pProxyDev, pszAddress));
1142
1143 /*
1144 * Init globals once.
1145 */
1146 int vrc = RTOnce(&g_usbProxyDarwinOnce, usbProxyDarwinInitOnce, NULL);
1147 AssertRCReturn(vrc, vrc);
1148
1149 PUSBPROXYDEVOSX pDevOsX = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVOSX);
1150
1151 /*
1152 * The idea here was to create a matching directory with the sessionID
1153 * and locationID included, however this doesn't seem to work. So, we'll
1154 * use the product id and vendor id to limit the set of matching device
1155 * and manually match these two properties. sigh.
1156 * (Btw. vendor and product id must be used *together* apparently.)
1157 *
1158 * Wonder if we could use the entry path? Docs indicates says we must
1159 * use IOServiceGetMatchingServices and I'm not in a mood to explore
1160 * this subject further right now. Maybe check this later.
1161 */
1162 CFMutableDictionaryRef RefMatchingDict = IOServiceMatching(kIOUSBDeviceClassName);
1163 AssertReturn(RefMatchingDict != NULL, VERR_OPEN_FAILED);
1164
1165 uint64_t u64SessionId = 0;
1166 uint32_t u32LocationId = 0;
1167 const char *psz = pszAddress;
1168 do
1169 {
1170 const char chValue = *psz;
1171 AssertReleaseReturn(psz[1] == '=', VERR_INTERNAL_ERROR);
1172 uint64_t u64Value;
1173 int rc = RTStrToUInt64Ex(psz + 2, (char **)&psz, 0, &u64Value);
1174 AssertReleaseRCReturn(rc, rc);
1175 AssertReleaseReturn(!*psz || *psz == ';', rc);
1176 switch (chValue)
1177 {
1178 case 'l':
1179 u32LocationId = (uint32_t)u64Value;
1180 break;
1181 case 's':
1182 u64SessionId = u64Value;
1183 break;
1184 case 'p':
1185 case 'v':
1186 {
1187#if 0 /* Guess what, this doesn't 'ing work either! */
1188 SInt32 i32 = (int16_t)u64Value;
1189 CFNumberRef Num = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i32);
1190 AssertBreak(Num);
1191 CFDictionarySetValue(RefMatchingDict, chValue == 'p' ? CFSTR(kUSBProductID) : CFSTR(kUSBVendorID), Num);
1192 CFRelease(Num);
1193#endif
1194 break;
1195 }
1196 default:
1197 AssertReleaseMsgFailedReturn(("chValue=%#x\n", chValue), VERR_INTERNAL_ERROR);
1198 }
1199 if (*psz == ';')
1200 psz++;
1201 } while (*psz);
1202
1203 io_iterator_t USBDevices = IO_OBJECT_NULL;
1204 IOReturn irc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &USBDevices);
1205 AssertMsgReturn(irc == kIOReturnSuccess, ("irc=%#x\n", irc), RTErrConvertFromDarwinIO(irc));
1206 RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
1207
1208 unsigned cMatches = 0;
1209 io_object_t USBDevice;
1210 while ((USBDevice = IOIteratorNext(USBDevices)))
1211 {
1212 cMatches++;
1213 CFMutableDictionaryRef PropsRef = 0;
1214 kern_return_t krc = IORegistryEntryCreateCFProperties(USBDevice, &PropsRef, kCFAllocatorDefault, kNilOptions);
1215 if (krc == KERN_SUCCESS)
1216 {
1217 uint64_t u64CurSessionId;
1218 uint32_t u32CurLocationId;
1219 if ( ( !u64SessionId
1220 || ( usbProxyDarwinDictGetU64(PropsRef, CFSTR("sessionID"), &u64CurSessionId)
1221 && u64CurSessionId == u64SessionId))
1222 && ( !u32LocationId
1223 || ( usbProxyDarwinDictGetU32(PropsRef, CFSTR(kUSBDevicePropertyLocationID), &u32CurLocationId)
1224 && u32CurLocationId == u32LocationId))
1225 )
1226 {
1227 CFRelease(PropsRef);
1228 break;
1229 }
1230 CFRelease(PropsRef);
1231 }
1232 IOObjectRelease(USBDevice);
1233 }
1234 IOObjectRelease(USBDevices);
1235 USBDevices = IO_OBJECT_NULL;
1236 if (!USBDevice)
1237 {
1238 LogRel(("USB: Device '%s' not found (%d pid+vid matches)\n", pszAddress, cMatches));
1239 IOObjectRelease(USBDevices);
1240 return VERR_VUSB_DEVICE_NAME_NOT_FOUND;
1241 }
1242
1243 /*
1244 * Ask for authorization (which only works with the com.apple.vm.device-access entitlement).
1245 */
1246 if (g_pfnIOServiceAuthorize)
1247 {
1248 irc = g_pfnIOServiceAuthorize(USBDevice, kIOServiceInteractionAllowed);
1249 if (irc != kIOReturnSuccess)
1250 LogRel(("USB: Failed to get authorization for device '%s', capturing the device might now work: irc=%#x\n",
1251 pszAddress, irc));
1252 }
1253
1254 /*
1255 * Create a plugin interface for the device and query its IOUSBDeviceInterface.
1256 */
1257 SInt32 Score = 0;
1258 IOCFPlugInInterface **ppPlugInInterface = NULL;
1259 irc = IOCreatePlugInInterfaceForService(USBDevice, kIOUSBDeviceUserClientTypeID,
1260 kIOCFPlugInInterfaceID, &ppPlugInInterface, &Score);
1261 if (irc == kIOReturnSuccess)
1262 {
1263 IOUSBDeviceInterface245 **ppDevI = NULL;
1264 HRESULT hrc = (*ppPlugInInterface)->QueryInterface(ppPlugInInterface,
1265 CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID245),
1266 (LPVOID *)&ppDevI);
1267 irc = IODestroyPlugInInterface(ppPlugInInterface); Assert(irc == kIOReturnSuccess);
1268 ppPlugInInterface = NULL;
1269 if (hrc == S_OK)
1270 {
1271 /*
1272 * Try open the device for exclusive access.
1273 * If we fail, we'll try figure out who is using the device and
1274 * convince them to let go of it...
1275 */
1276 irc = (*ppDevI)->USBDeviceReEnumerate(ppDevI, kUSBReEnumerateCaptureDeviceMask);
1277 Log(("USBDeviceReEnumerate (capture) returned irc=%#x\n", irc));
1278
1279 irc = (*ppDevI)->USBDeviceOpenSeize(ppDevI);
1280 if (irc == kIOReturnExclusiveAccess)
1281 {
1282 RTThreadSleep(20);
1283 irc = (*ppDevI)->USBDeviceOpenSeize(ppDevI);
1284 }
1285 if (irc == kIOReturnSuccess)
1286 {
1287 /*
1288 * Init a proxy device instance.
1289 */
1290 RTListInit((PRTLISTNODE)&pDevOsX->HeadOfRunLoopLst);
1291 vrc = RTCritSectInit(&pDevOsX->CritSect);
1292 if (RT_SUCCESS(vrc))
1293 {
1294 pDevOsX->USBDevice = USBDevice;
1295 pDevOsX->ppDevI = ppDevI;
1296 pDevOsX->pProxyDev = pProxyDev;
1297 pDevOsX->pTaxingHead = NULL;
1298 pDevOsX->pTaxingTail = NULL;
1299 pDevOsX->hRunLoopReapingLast = NULL;
1300
1301 /*
1302 * Try seize all the interface.
1303 */
1304 char *pszDummyName = pProxyDev->pUsbIns->pszName;
1305 pProxyDev->pUsbIns->pszName = (char *)pszAddress;
1306 vrc = usbProxyDarwinSeizeAllInterfaces(pDevOsX, false /* give up on failure */);
1307 pProxyDev->pUsbIns->pszName = pszDummyName;
1308 if (RT_SUCCESS(vrc))
1309 {
1310 /*
1311 * Create the async event source and add it to the run loop.
1312 */
1313 irc = (*ppDevI)->CreateDeviceAsyncEventSource(ppDevI, &pDevOsX->RunLoopSrcRef);
1314 if (irc == kIOReturnSuccess)
1315 {
1316 /*
1317 * Determine the active configuration.
1318 * Can cause hangs, so drop it for now.
1319 */
1320 /** @todo test Palm. */
1321 //uint8_t u8Cfg;
1322 //irc = (*ppDevI)->GetConfiguration(ppDevI, &u8Cfg);
1323 if (irc != kIOReturnNoDevice)
1324 {
1325 CFRunLoopSourceContext CtxRunLoopSource;
1326 CtxRunLoopSource.version = 0;
1327 CtxRunLoopSource.info = NULL;
1328 CtxRunLoopSource.retain = NULL;
1329 CtxRunLoopSource.release = NULL;
1330 CtxRunLoopSource.copyDescription = NULL;
1331 CtxRunLoopSource.equal = NULL;
1332 CtxRunLoopSource.hash = NULL;
1333 CtxRunLoopSource.schedule = NULL;
1334 CtxRunLoopSource.cancel = NULL;
1335 CtxRunLoopSource.perform = usbProxyDarwinPerformWakeup;
1336 pDevOsX->hRunLoopSrcWakeRef = CFRunLoopSourceCreate(NULL, 0, &CtxRunLoopSource);
1337 if (CFRunLoopSourceIsValid(pDevOsX->hRunLoopSrcWakeRef))
1338 {
1339 //pProxyDev->iActiveCfg = irc == kIOReturnSuccess ? u8Cfg : -1;
1340 RTListInit(&pDevOsX->HeadOfRunLoopWakeLst);
1341 pProxyDev->iActiveCfg = -1;
1342 pProxyDev->cIgnoreSetConfigs = 1;
1343
1344 usbProxyDarwinAddRunLoopRef(&pDevOsX->HeadOfRunLoopLst, pDevOsX->RunLoopSrcRef);
1345 return VINF_SUCCESS; /* return */
1346 }
1347 else
1348 {
1349 LogRel(("USB: Device '%s' out of memory allocating runloop source\n", pszAddress));
1350 vrc = VERR_NO_MEMORY;
1351 }
1352 }
1353 vrc = VERR_VUSB_DEVICE_NOT_ATTACHED;
1354 }
1355 else
1356 vrc = RTErrConvertFromDarwin(irc);
1357
1358 usbProxyDarwinReleaseAllInterfaces(pDevOsX);
1359 }
1360 /* else: already bitched */
1361
1362 RTCritSectDelete(&pDevOsX->CritSect);
1363 }
1364
1365 irc = (*ppDevI)->USBDeviceClose(ppDevI);
1366 AssertMsg(irc == kIOReturnSuccess, ("%#x\n", irc));
1367 }
1368 else if (irc == kIOReturnExclusiveAccess)
1369 {
1370 LogRel(("USB: Device '%s' is being used by another process\n", pszAddress));
1371 vrc = VERR_SHARING_VIOLATION;
1372 }
1373 else
1374 {
1375 LogRel(("USB: Failed to open device '%s', irc=%#x.\n", pszAddress, irc));
1376 vrc = VERR_OPEN_FAILED;
1377 }
1378 }
1379 else
1380 {
1381 LogRel(("USB: Failed to create plugin interface for device '%s', hrc=%#x.\n", pszAddress, hrc));
1382 vrc = VERR_OPEN_FAILED;
1383 }
1384
1385 (*ppDevI)->Release(ppDevI);
1386 }
1387 else
1388 {
1389 LogRel(("USB: Failed to open device '%s', plug-in creation failed with irc=%#x.\n", pszAddress, irc));
1390 vrc = RTErrConvertFromDarwin(irc);
1391 }
1392
1393 return vrc;
1394}
1395
1396
1397/**
1398 * Closes the proxy device.
1399 */
1400static DECLCALLBACK(void) usbProxyDarwinClose(PUSBPROXYDEV pProxyDev)
1401{
1402 LogFlow(("usbProxyDarwinClose: pProxyDev=%s\n", pProxyDev->pUsbIns->pszName));
1403 PUSBPROXYDEVOSX pDevOsX = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVOSX);
1404 AssertPtrReturnVoid(pDevOsX);
1405
1406 /*
1407 * Release interfaces we've laid claim to, then reset the device
1408 * and finally close it.
1409 */
1410 RTCritSectEnter(&pDevOsX->CritSect);
1411 /* ?? */
1412 RTCritSectLeave(&pDevOsX->CritSect);
1413
1414 usbProxyDarwinReleaseAllInterfaces(pDevOsX);
1415
1416 if (pDevOsX->RunLoopSrcRef)
1417 {
1418 int rc = usbProxyDarwinRemoveSourceRefFromAllRunLoops(&pDevOsX->HeadOfRunLoopLst, pDevOsX->RunLoopSrcRef);
1419 AssertRC(rc);
1420
1421 RTListInit((PRTLISTNODE)&pDevOsX->HeadOfRunLoopLst);
1422
1423 CFRelease(pDevOsX->RunLoopSrcRef);
1424 pDevOsX->RunLoopSrcRef = NULL;
1425 }
1426
1427 if (pDevOsX->hRunLoopSrcWakeRef)
1428 {
1429 int rc = usbProxyDarwinRemoveSourceRefFromAllRunLoops(&pDevOsX->HeadOfRunLoopWakeLst, pDevOsX->hRunLoopSrcWakeRef);
1430 AssertRC(rc);
1431
1432 RTListInit((PRTLISTNODE)&pDevOsX->HeadOfRunLoopWakeLst);
1433
1434 CFRelease(pDevOsX->hRunLoopSrcWakeRef);
1435 pDevOsX->hRunLoopSrcWakeRef = NULL;
1436 }
1437
1438 IOReturn irc = (*pDevOsX->ppDevI)->ResetDevice(pDevOsX->ppDevI);
1439
1440 irc = (*pDevOsX->ppDevI)->USBDeviceClose(pDevOsX->ppDevI);
1441 if (irc != kIOReturnSuccess && irc != kIOReturnNoDevice)
1442 {
1443 LogRel(("USB: USBDeviceClose -> %#x\n", irc));
1444 AssertMsgFailed(("irc=%#x\n", irc));
1445 }
1446
1447 irc = (*pDevOsX->ppDevI)->USBDeviceReEnumerate(pDevOsX->ppDevI, kUSBReEnumerateReleaseDeviceMask);
1448 Log(("USBDeviceReEnumerate (release) returned irc=%#x\n", irc));
1449
1450 (*pDevOsX->ppDevI)->Release(pDevOsX->ppDevI);
1451 pDevOsX->ppDevI = NULL;
1452 kern_return_t krc = IOObjectRelease(pDevOsX->USBDevice); Assert(krc == KERN_SUCCESS); NOREF(krc);
1453 pDevOsX->USBDevice = IO_OBJECT_NULL;
1454 pDevOsX->pProxyDev = NULL;
1455
1456 /*
1457 * Free all the resources.
1458 */
1459 RTCritSectDelete(&pDevOsX->CritSect);
1460
1461 PUSBPROXYURBOSX pUrbOsX;
1462 while ((pUrbOsX = pDevOsX->pFreeHead) != NULL)
1463 {
1464 pDevOsX->pFreeHead = pUrbOsX->pNext;
1465 RTMemFree(pUrbOsX);
1466 }
1467
1468 LogFlow(("usbProxyDarwinClose: returns\n"));
1469}
1470
1471
1472/** @interface_method_impl{USBPROXYBACK,pfnReset}*/
1473static DECLCALLBACK(int) usbProxyDarwinReset(PUSBPROXYDEV pProxyDev, bool fResetOnLinux)
1474{
1475 RT_NOREF(fResetOnLinux);
1476 PUSBPROXYDEVOSX pDevOsX = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVOSX);
1477 LogFlow(("usbProxyDarwinReset: pProxyDev=%s\n", pProxyDev->pUsbIns->pszName));
1478
1479 IOReturn irc = (*pDevOsX->ppDevI)->ResetDevice(pDevOsX->ppDevI);
1480 int rc;
1481 if (irc == kIOReturnSuccess)
1482 {
1483 /** @todo Some docs say that some drivers will do a default config, check this out ... */
1484 pProxyDev->cIgnoreSetConfigs = 0;
1485 pProxyDev->iActiveCfg = -1;
1486
1487 rc = VINF_SUCCESS;
1488 }
1489 else if (irc == kIOReturnNoDevice)
1490 rc = VERR_VUSB_DEVICE_NOT_ATTACHED;
1491 else
1492 {
1493 AssertMsgFailed(("irc=%#x\n", irc));
1494 rc = VERR_GENERAL_FAILURE;
1495 }
1496
1497 LogFlow(("usbProxyDarwinReset: returns success %Rrc\n", rc));
1498 return rc;
1499}
1500
1501
1502/**
1503 * SET_CONFIGURATION.
1504 *
1505 * The caller makes sure that it's not called first time after open or reset
1506 * with the active interface.
1507 *
1508 * @returns success indicator.
1509 * @param pProxyDev The device instance data.
1510 * @param iCfg The configuration to set.
1511 */
1512static DECLCALLBACK(int) usbProxyDarwinSetConfig(PUSBPROXYDEV pProxyDev, int iCfg)
1513{
1514 PUSBPROXYDEVOSX pDevOsX = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVOSX);
1515 LogFlow(("usbProxyDarwinSetConfig: pProxyDev=%s cfg=%#x\n",
1516 pProxyDev->pUsbIns->pszName, iCfg));
1517
1518 IOReturn irc = (*pDevOsX->ppDevI)->SetConfiguration(pDevOsX->ppDevI, (uint8_t)iCfg);
1519 if (irc != kIOReturnSuccess)
1520 {
1521 Log(("usbProxyDarwinSetConfig: Set configuration -> %#x\n", irc));
1522 return RTErrConvertFromDarwin(irc);
1523 }
1524
1525 usbProxyDarwinReleaseAllInterfaces(pDevOsX);
1526 usbProxyDarwinSeizeAllInterfaces(pDevOsX, true /* make the best out of it */);
1527 return VINF_SUCCESS;
1528}
1529
1530
1531/**
1532 * Claims an interface.
1533 *
1534 * This is a stub on Darwin since we release/claim all interfaces at
1535 * open/reset/setconfig time.
1536 *
1537 * @returns success indicator (always VINF_SUCCESS).
1538 */
1539static DECLCALLBACK(int) usbProxyDarwinClaimInterface(PUSBPROXYDEV pProxyDev, int iIf)
1540{
1541 RT_NOREF(pProxyDev, iIf);
1542 return VINF_SUCCESS;
1543}
1544
1545
1546/**
1547 * Releases an interface.
1548 *
1549 * This is a stub on Darwin since we release/claim all interfaces at
1550 * open/reset/setconfig time.
1551 *
1552 * @returns success indicator.
1553 */
1554static DECLCALLBACK(int) usbProxyDarwinReleaseInterface(PUSBPROXYDEV pProxyDev, int iIf)
1555{
1556 RT_NOREF(pProxyDev, iIf);
1557 return VINF_SUCCESS;
1558}
1559
1560
1561/**
1562 * SET_INTERFACE.
1563 *
1564 * @returns success indicator.
1565 */
1566static DECLCALLBACK(int) usbProxyDarwinSetInterface(PUSBPROXYDEV pProxyDev, int iIf, int iAlt)
1567{
1568 PUSBPROXYDEVOSX pDevOsX = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVOSX);
1569 IOReturn irc = kIOReturnSuccess;
1570 PUSBPROXYIFOSX pIf = usbProxyDarwinGetInterface(pDevOsX, iIf);
1571 LogFlow(("usbProxyDarwinSetInterface: pProxyDev=%s iIf=%#x iAlt=%#x iCurAlt=%#x\n",
1572 pProxyDev->pUsbIns->pszName, iIf, iAlt, pIf ? pIf->u8AltSetting : 0xbeef));
1573 if (pIf)
1574 {
1575 /* Avoid SetAlternateInterface when possible as it will recreate the pipes. */
1576 if (iAlt != pIf->u8AltSetting)
1577 {
1578 irc = (*pIf->ppIfI)->SetAlternateInterface(pIf->ppIfI, iAlt);
1579 if (irc == kIOReturnSuccess)
1580 {
1581 usbProxyDarwinGetPipeProperties(pDevOsX, pIf);
1582 return VINF_SUCCESS;
1583 }
1584 }
1585 else
1586 {
1587 /*
1588 * Just send the request anyway?
1589 */
1590 IOUSBDevRequest Req;
1591 Req.bmRequestType = 0x01;
1592 Req.bRequest = 0x0b; /* SET_INTERFACE */
1593 Req.wIndex = iIf;
1594 Req.wValue = iAlt;
1595 Req.wLength = 0;
1596 Req.wLenDone = 0;
1597 Req.pData = NULL;
1598 irc = (*pDevOsX->ppDevI)->DeviceRequest(pDevOsX->ppDevI, &Req);
1599 Log(("usbProxyDarwinSetInterface: SET_INTERFACE(%d,%d) -> irc=%#x\n", iIf, iAlt, irc));
1600 return VINF_SUCCESS;
1601 }
1602 }
1603
1604 LogFlow(("usbProxyDarwinSetInterface: pProxyDev=%s eiIf=%#x iAlt=%#x - failure - pIf=%p irc=%#x\n",
1605 pProxyDev->pUsbIns->pszName, iIf, iAlt, pIf, irc));
1606 return RTErrConvertFromDarwin(irc);
1607}
1608
1609
1610/**
1611 * Clears the halted endpoint 'EndPt'.
1612 */
1613static DECLCALLBACK(int) usbProxyDarwinClearHaltedEp(PUSBPROXYDEV pProxyDev, unsigned int EndPt)
1614{
1615 PUSBPROXYDEVOSX pDevOsX = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVOSX);
1616 LogFlow(("usbProxyDarwinClearHaltedEp: pProxyDev=%s EndPt=%#x\n", pProxyDev->pUsbIns->pszName, EndPt));
1617
1618 /*
1619 * Clearing the zero control pipe doesn't make sense and isn't
1620 * supported by the API. Just ignore it.
1621 */
1622 if (EndPt == 0)
1623 return VINF_SUCCESS;
1624
1625 /*
1626 * Find the interface/pipe combination and invoke the ClearPipeStallBothEnds
1627 * method. (The ResetPipe and ClearPipeStall methods will not send the
1628 * CLEAR_FEATURE(ENDPOINT_HALT) request that this method implements.)
1629 */
1630 IOReturn irc = kIOReturnSuccess;
1631 uint8_t u8PipeRef;
1632 PUSBPROXYIFOSX pIf = usbProxyDarwinGetInterfaceForEndpoint(pDevOsX, EndPt, &u8PipeRef, NULL);
1633 if (pIf)
1634 {
1635 irc = (*pIf->ppIfI)->ClearPipeStallBothEnds(pIf->ppIfI, u8PipeRef);
1636 if (irc == kIOReturnSuccess)
1637 return VINF_SUCCESS;
1638 AssertMsg(irc == kIOReturnNoDevice || irc == kIOReturnNotResponding, ("irc=#x (control pipe?)\n", irc));
1639 }
1640
1641 LogFlow(("usbProxyDarwinClearHaltedEp: pProxyDev=%s EndPt=%#x - failure - pIf=%p irc=%#x\n",
1642 pProxyDev->pUsbIns->pszName, EndPt, pIf, irc));
1643 return RTErrConvertFromDarwin(irc);
1644}
1645
1646
1647/**
1648 * @interface_method_impl{USBPROXYBACK,pfnUrbQueue}
1649 */
1650static DECLCALLBACK(int) usbProxyDarwinUrbQueue(PUSBPROXYDEV pProxyDev, PVUSBURB pUrb)
1651{
1652 PUSBPROXYDEVOSX pDevOsX = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVOSX);
1653 LogFlow(("%s: usbProxyDarwinUrbQueue: pProxyDev=%s pUrb=%p EndPt=%d cbData=%d\n",
1654 pUrb->pszDesc, pProxyDev->pUsbIns->pszName, pUrb, pUrb->EndPt, pUrb->cbData));
1655
1656 /*
1657 * Find the target interface / pipe.
1658 */
1659 uint8_t u8PipeRef = 0xff;
1660 PUSBPROXYIFOSX pIf = NULL;
1661 PUSBPROXYPIPEOSX pPipe = NULL;
1662 if (pUrb->EndPt)
1663 {
1664 /* Make sure the interface is there. */
1665 const uint8_t EndPt = pUrb->EndPt | (pUrb->enmDir == VUSBDIRECTION_IN ? 0x80 : 0);
1666 pIf = usbProxyDarwinGetInterfaceForEndpoint(pDevOsX, EndPt, &u8PipeRef, &pPipe);
1667 if (!pIf)
1668 {
1669 LogFlow(("%s: usbProxyDarwinUrbQueue: pProxyDev=%s EndPt=%d cbData=%d - can't find interface / pipe!!!\n",
1670 pUrb->pszDesc, pProxyDev->pUsbIns->pszName, pUrb->EndPt, pUrb->cbData));
1671 return VERR_NOT_FOUND;
1672 }
1673 }
1674 /* else: pIf == NULL -> default control pipe.*/
1675
1676 /*
1677 * Allocate a Darwin urb.
1678 */
1679 PUSBPROXYURBOSX pUrbOsX = usbProxyDarwinUrbAlloc(pDevOsX);
1680 if (!pUrbOsX)
1681 return VERR_NO_MEMORY;
1682
1683 pUrbOsX->u64SubmitTS = RTTimeMilliTS();
1684 pUrbOsX->pVUsbUrb = pUrb;
1685 pUrbOsX->pDevOsX = pDevOsX;
1686 pUrbOsX->enmType = pUrb->enmType;
1687
1688 /*
1689 * Submit the request.
1690 */
1691 IOReturn irc = kIOReturnError;
1692 switch (pUrb->enmType)
1693 {
1694 case VUSBXFERTYPE_MSG:
1695 {
1696 AssertMsgBreak(pUrb->cbData >= sizeof(VUSBSETUP), ("cbData=%d\n", pUrb->cbData));
1697 PVUSBSETUP pSetup = (PVUSBSETUP)&pUrb->abData[0];
1698 pUrbOsX->u.ControlMsg.bmRequestType = pSetup->bmRequestType;
1699 pUrbOsX->u.ControlMsg.bRequest = pSetup->bRequest;
1700 pUrbOsX->u.ControlMsg.wValue = pSetup->wValue;
1701 pUrbOsX->u.ControlMsg.wIndex = pSetup->wIndex;
1702 pUrbOsX->u.ControlMsg.wLength = pSetup->wLength;
1703 pUrbOsX->u.ControlMsg.pData = pSetup + 1;
1704 pUrbOsX->u.ControlMsg.wLenDone = pSetup->wLength;
1705
1706 if (pIf)
1707 irc = (*pIf->ppIfI)->ControlRequestAsync(pIf->ppIfI, u8PipeRef, &pUrbOsX->u.ControlMsg,
1708 usbProxyDarwinUrbAsyncComplete, pUrbOsX);
1709 else
1710 irc = (*pDevOsX->ppDevI)->DeviceRequestAsync(pDevOsX->ppDevI, &pUrbOsX->u.ControlMsg,
1711 usbProxyDarwinUrbAsyncComplete, pUrbOsX);
1712 break;
1713 }
1714
1715 case VUSBXFERTYPE_BULK:
1716 case VUSBXFERTYPE_INTR:
1717 {
1718 AssertBreak(pIf);
1719 Assert(pUrb->enmDir == VUSBDIRECTION_IN || pUrb->enmDir == VUSBDIRECTION_OUT);
1720 if (pUrb->enmDir == VUSBDIRECTION_OUT)
1721 irc = (*pIf->ppIfI)->WritePipeAsync(pIf->ppIfI, u8PipeRef, pUrb->abData, pUrb->cbData,
1722 usbProxyDarwinUrbAsyncComplete, pUrbOsX);
1723 else
1724 irc = (*pIf->ppIfI)->ReadPipeAsync(pIf->ppIfI, u8PipeRef, pUrb->abData, pUrb->cbData,
1725 usbProxyDarwinUrbAsyncComplete, pUrbOsX);
1726
1727 break;
1728 }
1729
1730 case VUSBXFERTYPE_ISOC:
1731 {
1732 AssertBreak(pIf);
1733 Assert(pUrb->enmDir == VUSBDIRECTION_IN || pUrb->enmDir == VUSBDIRECTION_OUT);
1734
1735#ifdef USE_LOW_LATENCY_API
1736 /* Allocate an isochronous buffer and copy over the data. */
1737 AssertBreak(pUrb->cbData <= 8192);
1738 int rc = usbProxyDarwinUrbAllocIsocBuf(pUrbOsX, pIf);
1739 AssertRCBreak(rc);
1740 if (pUrb->enmDir == VUSBDIRECTION_OUT)
1741 memcpy(pUrbOsX->u.Isoc.pBuf->pvBuf, pUrb->abData, pUrb->cbData);
1742 else
1743 memset(pUrbOsX->u.Isoc.pBuf->pvBuf, 0xfe, pUrb->cbData);
1744#endif
1745
1746 /* Get the current frame number (+2) and make sure it doesn't
1747 overlap with the previous request. See WARNING in
1748 ApplUSBUHCI::CreateIsochTransfer for details on the +2. */
1749 UInt64 FrameNo;
1750 AbsoluteTime FrameTime;
1751 irc = (*pIf->ppIfI)->GetBusFrameNumber(pIf->ppIfI, &FrameNo, &FrameTime);
1752 AssertMsg(irc == kIOReturnSuccess, ("GetBusFrameNumber -> %#x\n", irc));
1753 FrameNo += 2;
1754 if (FrameNo <= pPipe->u64NextFrameNo)
1755 FrameNo = pPipe->u64NextFrameNo;
1756
1757 for (unsigned j = 0; ; j++)
1758 {
1759 unsigned i;
1760 for (i = 0; i < pUrb->cIsocPkts; i++)
1761 {
1762 pUrbOsX->u.Isoc.aFrames[i].frReqCount = pUrb->aIsocPkts[i].cb;
1763 pUrbOsX->u.Isoc.aFrames[i].frActCount = 0;
1764 pUrbOsX->u.Isoc.aFrames[i].frStatus = kIOUSBNotSent1Err;
1765#ifdef USE_LOW_LATENCY_API
1766 pUrbOsX->u.Isoc.aFrames[i].frTimeStamp.hi = 0;
1767 pUrbOsX->u.Isoc.aFrames[i].frTimeStamp.lo = 0;
1768#endif
1769 }
1770 for (; i < RT_ELEMENTS(pUrbOsX->u.Isoc.aFrames); i++)
1771 {
1772 pUrbOsX->u.Isoc.aFrames[i].frReqCount = 0;
1773 pUrbOsX->u.Isoc.aFrames[i].frActCount = 0;
1774 pUrbOsX->u.Isoc.aFrames[i].frStatus = kIOReturnError;
1775#ifdef USE_LOW_LATENCY_API
1776 pUrbOsX->u.Isoc.aFrames[i].frTimeStamp.hi = 0;
1777 pUrbOsX->u.Isoc.aFrames[i].frTimeStamp.lo = 0;
1778#endif
1779 }
1780
1781#ifdef USE_LOW_LATENCY_API
1782 if (pUrb->enmDir == VUSBDIRECTION_OUT)
1783 irc = (*pIf->ppIfI)->LowLatencyWriteIsochPipeAsync(pIf->ppIfI, u8PipeRef,
1784 pUrbOsX->u.Isoc.pBuf->pvBuf, FrameNo, pUrb->cIsocPkts, 0, pUrbOsX->u.Isoc.aFrames,
1785 usbProxyDarwinUrbAsyncComplete, pUrbOsX);
1786 else
1787 irc = (*pIf->ppIfI)->LowLatencyReadIsochPipeAsync(pIf->ppIfI, u8PipeRef,
1788 pUrbOsX->u.Isoc.pBuf->pvBuf, FrameNo, pUrb->cIsocPkts, 0, pUrbOsX->u.Isoc.aFrames,
1789 usbProxyDarwinUrbAsyncComplete, pUrbOsX);
1790#else
1791 if (pUrb->enmDir == VUSBDIRECTION_OUT)
1792 irc = (*pIf->ppIfI)->WriteIsochPipeAsync(pIf->ppIfI, u8PipeRef,
1793 pUrb->abData, FrameNo, pUrb->cIsocPkts, &pUrbOsX->u.Isoc.aFrames[0],
1794 usbProxyDarwinUrbAsyncComplete, pUrbOsX);
1795 else
1796 irc = (*pIf->ppIfI)->ReadIsochPipeAsync(pIf->ppIfI, u8PipeRef,
1797 pUrb->abData, FrameNo, pUrb->cIsocPkts, &pUrbOsX->u.Isoc.aFrames[0],
1798 usbProxyDarwinUrbAsyncComplete, pUrbOsX);
1799#endif
1800 if ( irc != kIOReturnIsoTooOld
1801 || j >= 5)
1802 {
1803 Log(("%s: usbProxyDarwinUrbQueue: isoc: u64NextFrameNo=%RX64 FrameNo=%RX64 #Frames=%d j=%d (pipe=%d)\n",
1804 pUrb->pszDesc, pPipe->u64NextFrameNo, FrameNo, pUrb->cIsocPkts, j, u8PipeRef));
1805 if (irc == kIOReturnSuccess)
1806 {
1807 if (pPipe->fIsFullSpeed)
1808 pPipe->u64NextFrameNo = FrameNo + pUrb->cIsocPkts;
1809 else
1810 pPipe->u64NextFrameNo = FrameNo + 1;
1811 }
1812 break;
1813 }
1814
1815 /* try again... */
1816 irc = (*pIf->ppIfI)->GetBusFrameNumber(pIf->ppIfI, &FrameNo, &FrameTime);
1817 if (FrameNo <= pPipe->u64NextFrameNo)
1818 FrameNo = pPipe->u64NextFrameNo;
1819 FrameNo += j;
1820 }
1821 break;
1822 }
1823
1824 default:
1825 AssertMsgFailed(("%s: enmType=%#x\n", pUrb->pszDesc, pUrb->enmType));
1826 break;
1827 }
1828
1829 /*
1830 * Success?
1831 */
1832 if (RT_LIKELY(irc == kIOReturnSuccess))
1833 {
1834 Log(("%s: usbProxyDarwinUrbQueue: success\n", pUrb->pszDesc));
1835 return VINF_SUCCESS;
1836 }
1837 switch (irc)
1838 {
1839 case kIOUSBPipeStalled:
1840 {
1841 /* Increment in flight counter because the completion handler will decrease it always. */
1842 usbProxyDarwinUrbAsyncComplete(pUrbOsX, kIOUSBPipeStalled, 0);
1843 Log(("%s: usbProxyDarwinUrbQueue: pProxyDev=%s EndPt=%d cbData=%d - failed irc=%#x! (stall)\n",
1844 pUrb->pszDesc, pProxyDev->pUsbIns->pszName, pUrb->EndPt, pUrb->cbData, irc));
1845 return VINF_SUCCESS;
1846 }
1847 }
1848
1849 usbProxyDarwinUrbFree(pDevOsX, pUrbOsX);
1850 Log(("%s: usbProxyDarwinUrbQueue: pProxyDev=%s EndPt=%d cbData=%d - failed irc=%#x!\n",
1851 pUrb->pszDesc, pProxyDev->pUsbIns->pszName, pUrb->EndPt, pUrb->cbData, irc));
1852 return RTErrConvertFromDarwin(irc);
1853}
1854
1855
1856/**
1857 * Reap URBs in-flight on a device.
1858 *
1859 * @returns Pointer to a completed URB.
1860 * @returns NULL if no URB was completed.
1861 * @param pProxyDev The device.
1862 * @param cMillies Number of milliseconds to wait. Use 0 to not wait at all.
1863 */
1864static DECLCALLBACK(PVUSBURB) usbProxyDarwinUrbReap(PUSBPROXYDEV pProxyDev, RTMSINTERVAL cMillies)
1865{
1866 PVUSBURB pUrb = NULL;
1867 PUSBPROXYDEVOSX pDevOsX = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVOSX);
1868 CFRunLoopRef hRunLoopRef = CFRunLoopGetCurrent();
1869
1870 Assert(!pDevOsX->hRunLoopReaping);
1871
1872 /*
1873 * If the last seen runloop for reaping differs we have to check whether the
1874 * the runloop sources are in the new runloop.
1875 */
1876 if (pDevOsX->hRunLoopReapingLast != hRunLoopRef)
1877 {
1878 RTCritSectEnter(&pDevOsX->CritSect);
1879
1880 /* Every pipe. */
1881 if (!pDevOsX->pIfHead)
1882 usbProxyDarwinSeizeAllInterfaces(pDevOsX, true /* make the best out of it */);
1883
1884 PUSBPROXYIFOSX pIf;
1885 for (pIf = pDevOsX->pIfHead; pIf; pIf = pIf->pNext)
1886 {
1887 if (!CFRunLoopContainsSource(hRunLoopRef, pIf->RunLoopSrcRef, g_pRunLoopMode))
1888 usbProxyDarwinAddRunLoopRef(&pIf->HeadOfRunLoopLst, pIf->RunLoopSrcRef);
1889 }
1890
1891 /* Default control pipe. */
1892 if (!CFRunLoopContainsSource(hRunLoopRef, pDevOsX->RunLoopSrcRef, g_pRunLoopMode))
1893 usbProxyDarwinAddRunLoopRef(&pDevOsX->HeadOfRunLoopLst, pDevOsX->RunLoopSrcRef);
1894
1895 /* Runloop wakeup source. */
1896 if (!CFRunLoopContainsSource(hRunLoopRef, pDevOsX->hRunLoopSrcWakeRef, g_pRunLoopMode))
1897 usbProxyDarwinAddRunLoopRef(&pDevOsX->HeadOfRunLoopWakeLst, pDevOsX->hRunLoopSrcWakeRef);
1898 RTCritSectLeave(&pDevOsX->CritSect);
1899
1900 pDevOsX->hRunLoopReapingLast = hRunLoopRef;
1901 }
1902
1903 ASMAtomicXchgPtr((void * volatile *)&pDevOsX->hRunLoopReaping, hRunLoopRef);
1904
1905 if (ASMAtomicXchgBool(&pDevOsX->fReapingThreadWake, false))
1906 {
1907 /* Return immediately. */
1908 ASMAtomicXchgPtr((void * volatile *)&pDevOsX->hRunLoopReaping, NULL);
1909 return NULL;
1910 }
1911
1912 /*
1913 * Excercise the runloop until we get an URB or we time out.
1914 */
1915 if ( !pDevOsX->pTaxingHead
1916 && cMillies)
1917 CFRunLoopRunInMode(g_pRunLoopMode, cMillies / 1000.0, true);
1918
1919 ASMAtomicXchgPtr((void * volatile *)&pDevOsX->hRunLoopReaping, NULL);
1920 ASMAtomicXchgBool(&pDevOsX->fReapingThreadWake, false);
1921
1922 /*
1923 * Any URBs pending delivery?
1924 */
1925 while ( pDevOsX->pTaxingHead
1926 && !pUrb)
1927 {
1928 RTCritSectEnter(&pDevOsX->CritSect);
1929
1930 PUSBPROXYURBOSX pUrbOsX = pDevOsX->pTaxingHead;
1931 if (pUrbOsX)
1932 {
1933 /*
1934 * Remove from the taxing list.
1935 */
1936 if (pUrbOsX->pNext)
1937 pUrbOsX->pNext->pPrev = pUrbOsX->pPrev;
1938 else if (pDevOsX->pTaxingTail == pUrbOsX)
1939 pDevOsX->pTaxingTail = pUrbOsX->pPrev;
1940
1941 if (pUrbOsX->pPrev)
1942 pUrbOsX->pPrev->pNext = pUrbOsX->pNext;
1943 else if (pDevOsX->pTaxingHead == pUrbOsX)
1944 pDevOsX->pTaxingHead = pUrbOsX->pNext;
1945 else
1946 AssertFailed();
1947
1948 pUrb = pUrbOsX->pVUsbUrb;
1949 if (pUrb)
1950 {
1951 pUrb->Dev.pvPrivate = NULL;
1952 usbProxyDarwinUrbFree(pDevOsX, pUrbOsX);
1953 }
1954 }
1955 RTCritSectLeave(&pDevOsX->CritSect);
1956 }
1957
1958 if (pUrb)
1959 LogFlowFunc(("LEAVE: %s: pProxyDev=%s returns %p\n", pUrb->pszDesc, pProxyDev->pUsbIns->pszName, pUrb));
1960 else
1961 LogFlowFunc(("LEAVE: NULL pProxyDev=%s returns NULL\n", pProxyDev->pUsbIns->pszName));
1962
1963 return pUrb;
1964}
1965
1966
1967/**
1968 * Cancels a URB.
1969 *
1970 * The URB requires reaping, so we don't change its state.
1971 *
1972 * @remark There isn't any way to cancel a specific async request
1973 * on darwin. The interface only supports the aborting of
1974 * all URBs pending on an interface / pipe pair. Provided
1975 * the card does the URB cancelling before submitting new
1976 * requests, we should probably be fine...
1977 */
1978static DECLCALLBACK(int) usbProxyDarwinUrbCancel(PUSBPROXYDEV pProxyDev, PVUSBURB pUrb)
1979{
1980 PUSBPROXYDEVOSX pDevOsX = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVOSX);
1981 LogFlow(("%s: usbProxyDarwinUrbCancel: pProxyDev=%s EndPt=%d\n",
1982 pUrb->pszDesc, pProxyDev->pUsbIns->pszName, pUrb->EndPt));
1983
1984 /*
1985 * Determine the interface / endpoint ref and invoke AbortPipe.
1986 */
1987 IOReturn irc = kIOReturnSuccess;
1988 if (!pUrb->EndPt)
1989 irc = (*pDevOsX->ppDevI)->USBDeviceAbortPipeZero(pDevOsX->ppDevI);
1990 else
1991 {
1992 uint8_t u8PipeRef;
1993 const uint8_t EndPt = pUrb->EndPt | (pUrb->enmDir == VUSBDIRECTION_IN ? 0x80 : 0);
1994 PUSBPROXYIFOSX pIf = usbProxyDarwinGetInterfaceForEndpoint(pDevOsX, EndPt, &u8PipeRef, NULL);
1995 if (pIf)
1996 irc = (*pIf->ppIfI)->AbortPipe(pIf->ppIfI, u8PipeRef);
1997 else /* this may happen if a device reset, set configuration or set interface has been performed. */
1998 Log(("usbProxyDarwinUrbCancel: pProxyDev=%s pUrb=%p EndPt=%d - cannot find the interface / pipe!\n",
1999 pProxyDev->pUsbIns->pszName, pUrb, pUrb->EndPt));
2000 }
2001
2002 int rc = VINF_SUCCESS;
2003 if (irc != kIOReturnSuccess)
2004 {
2005 Log(("usbProxyDarwinUrbCancel: pProxyDev=%s pUrb=%p EndPt=%d -> %#x!\n",
2006 pProxyDev->pUsbIns->pszName, pUrb, pUrb->EndPt, irc));
2007 rc = RTErrConvertFromDarwin(irc);
2008 }
2009
2010 return rc;
2011}
2012
2013
2014static DECLCALLBACK(int) usbProxyDarwinWakeup(PUSBPROXYDEV pProxyDev)
2015{
2016 PUSBPROXYDEVOSX pDevOsX = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVOSX);
2017
2018 LogFlow(("usbProxyDarwinWakeup: pProxyDev=%p\n", pProxyDev));
2019
2020 ASMAtomicXchgBool(&pDevOsX->fReapingThreadWake, true);
2021 usbProxyDarwinReaperKick(pDevOsX);
2022 return VINF_SUCCESS;
2023}
2024
2025
2026/**
2027 * The Darwin USB Proxy Backend.
2028 */
2029extern const USBPROXYBACK g_USBProxyDeviceHost =
2030{
2031 /* pszName */
2032 "host",
2033 /* cbBackend */
2034 sizeof(USBPROXYDEVOSX),
2035 usbProxyDarwinOpen,
2036 NULL,
2037 usbProxyDarwinClose,
2038 usbProxyDarwinReset,
2039 usbProxyDarwinSetConfig,
2040 usbProxyDarwinClaimInterface,
2041 usbProxyDarwinReleaseInterface,
2042 usbProxyDarwinSetInterface,
2043 usbProxyDarwinClearHaltedEp,
2044 usbProxyDarwinUrbQueue,
2045 usbProxyDarwinUrbCancel,
2046 usbProxyDarwinUrbReap,
2047 usbProxyDarwinWakeup,
2048 0
2049};
2050
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