VirtualBox

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

Last change on this file since 99739 was 99739, checked in by vboxsync, 17 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

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