VirtualBox

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

Last change on this file was 100108, checked in by vboxsync, 12 months ago

*: Fix build issues when setting VBOX_WITH_WARNINGS_AS_ERRORS=1 on darwin.arm64 and make it a default, bugref:10469

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

© 2023 Oracle
ContactPrivacy policyTerms of Use