VirtualBox

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

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

Devices/USB/USBProxyDevice-darwin.cpp: Request authorization for capturing the device from the user, bugref:10684, second attempt

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