VirtualBox

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

Last change on this file since 97698 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

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