VirtualBox

source: vbox/trunk/src/VBox/Main/darwin/iokit.cpp@ 11100

Last change on this file since 11100 was 11100, checked in by vboxsync, 16 years ago

Main: darwin HIF comment.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 61.9 KB
Line 
1/* $Id: iokit.cpp 11100 2008-08-04 12:59:25Z vboxsync $ */
2/** @file
3 * Main - Darwin IOKit Routines.
4 *
5 * Because IOKit makes use of COM like interfaces, it does not mix very
6 * well with COM/XPCOM and must therefore be isolated from it using a
7 * simpler C interface.
8 */
9
10/*
11 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
12 *
13 * This file is part of VirtualBox Open Source Edition (OSE), as
14 * available from http://www.virtualbox.org. This file is free software;
15 * you can redistribute it and/or modify it under the terms of the GNU
16 * General Public License (GPL) as published by the Free Software
17 * Foundation, in version 2 as it comes in the "COPYING" file of the
18 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
19 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
22 * Clara, CA 95054 USA or visit http://www.sun.com if you need
23 * additional information or have any questions.
24 */
25
26
27/*******************************************************************************
28* Header Files *
29*******************************************************************************/
30#define LOG_GROUP LOG_GROUP_MAIN
31#ifdef STANDALONE_TESTCASE
32# define VBOX_WITH_USB
33#endif
34
35#include <mach/mach.h>
36#include <Carbon/Carbon.h>
37#include <IOKit/IOKitLib.h>
38#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
39#include <IOKit/scsi-commands/SCSITaskLib.h>
40#include <mach/mach_error.h>
41#ifdef VBOX_WITH_USB
42# include <IOKit/usb/IOUSBLib.h>
43# include <IOKit/IOCFPlugIn.h>
44#endif
45
46#include <VBox/log.h>
47#include <VBox/err.h>
48#include <iprt/mem.h>
49#include <iprt/string.h>
50#include <iprt/process.h>
51#include <iprt/assert.h>
52#include <iprt/thread.h>
53#include <iprt/uuid.h>
54#ifdef STANDALONE_TESTCASE
55# include <iprt/initterm.h>
56# include <iprt/stream.h>
57#endif
58
59#include "iokit.h"
60
61/* A small hack... */
62#ifdef STANDALONE_TESTCASE
63# define DarwinFreeUSBDeviceFromIOKit(a) do { } while (0)
64#endif
65
66
67/*******************************************************************************
68* Defined Constants And Macros *
69*******************************************************************************/
70/** An attempt at catching reference leaks. */
71#define MY_CHECK_CREFS(cRefs) do { AssertMsg(cRefs < 25, ("%ld\n", cRefs)); NOREF(cRefs); } while (0)
72
73/** Contains the pid of the current client. If 0, the kernel is the current client. */
74#define VBOXUSB_CLIENT_KEY "VBoxUSB-Client"
75/** Contains the pid of the filter owner (i.e. the VBoxSVC pid). */
76#define VBOXUSB_OWNER_KEY "VBoxUSB-Owner"
77/** The VBoxUSBDevice class name. */
78#define VBOXUSBDEVICE_CLASS_NAME "org_virtualbox_VBoxUSBDevice"
79
80
81/*******************************************************************************
82* Global Variables *
83*******************************************************************************/
84/** The IO Master Port. */
85static mach_port_t g_MasterPort = NULL;
86
87
88/**
89 * Lazily opens the master port.
90 *
91 * @returns true if the port is open, false on failure (very unlikely).
92 */
93static bool darwinOpenMasterPort(void)
94{
95 if (!g_MasterPort)
96 {
97 kern_return_t krc = IOMasterPort(MACH_PORT_NULL, &g_MasterPort);
98 AssertReturn(krc == KERN_SUCCESS, false);
99 }
100 return true;
101}
102
103
104#ifdef VBOX_WITH_USB
105
106/**
107 * Checks whether the value exists.
108 *
109 * @returns true / false accordingly.
110 * @param DictRef The dictionary.
111 * @param KeyStrRef The key name.
112 */
113static bool darwinDictIsPresent(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef)
114{
115 return !!CFDictionaryGetValue(DictRef, KeyStrRef);
116}
117
118
119/**
120 * Gets a boolean value.
121 *
122 * @returns Success indicator (true/false).
123 * @param DictRef The dictionary.
124 * @param KeyStrRef The key name.
125 * @param pf Where to store the key value.
126 */
127static bool darwinDictGetBool(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, bool *pf)
128{
129 CFTypeRef BoolRef = CFDictionaryGetValue(DictRef, KeyStrRef);
130 if ( BoolRef
131 && CFGetTypeID(BoolRef) == CFBooleanGetTypeID())
132 {
133 *pf = CFBooleanGetValue((CFBooleanRef)BoolRef);
134 return true;
135 }
136 *pf = false;
137 return false;
138}
139
140
141/**
142 * Gets an unsigned 8-bit integer value.
143 *
144 * @returns Success indicator (true/false).
145 * @param DictRef The dictionary.
146 * @param KeyStrRef The key name.
147 * @param pu8 Where to store the key value.
148 */
149static bool darwinDictGetU8(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, uint8_t *pu8)
150{
151 CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
152 if (ValRef)
153 {
154 if (CFNumberGetValue((CFNumberRef)ValRef, kCFNumberSInt8Type, pu8))
155 return true;
156 }
157 *pu8 = 0;
158 return false;
159}
160
161
162/**
163 * Gets an unsigned 16-bit integer value.
164 *
165 * @returns Success indicator (true/false).
166 * @param DictRef The dictionary.
167 * @param KeyStrRef The key name.
168 * @param pu16 Where to store the key value.
169 */
170static bool darwinDictGetU16(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, uint16_t *pu16)
171{
172 CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
173 if (ValRef)
174 {
175 if (CFNumberGetValue((CFNumberRef)ValRef, kCFNumberSInt16Type, pu16))
176 return true;
177 }
178 *pu16 = 0;
179 return false;
180}
181
182
183/**
184 * Gets an unsigned 32-bit integer value.
185 *
186 * @returns Success indicator (true/false).
187 * @param DictRef The dictionary.
188 * @param KeyStrRef The key name.
189 * @param pu32 Where to store the key value.
190 */
191static bool darwinDictGetU32(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, uint32_t *pu32)
192{
193 CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
194 if (ValRef)
195 {
196 if (CFNumberGetValue((CFNumberRef)ValRef, kCFNumberSInt32Type, pu32))
197 return true;
198 }
199 *pu32 = 0;
200 return false;
201}
202
203
204/**
205 * Gets an unsigned 64-bit integer value.
206 *
207 * @returns Success indicator (true/false).
208 * @param DictRef The dictionary.
209 * @param KeyStrRef The key name.
210 * @param pu64 Where to store the key value.
211 */
212static bool darwinDictGetU64(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, uint64_t *pu64)
213{
214 CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
215 if (ValRef)
216 {
217 if (CFNumberGetValue((CFNumberRef)ValRef, kCFNumberSInt64Type, pu64))
218 return true;
219 }
220 *pu64 = 0;
221 return false;
222}
223
224
225/**
226 * Gets a RTPROCESS value.
227 *
228 * @returns Success indicator (true/false).
229 * @param DictRef The dictionary.
230 * @param KeyStrRef The key name.
231 * @param pProcess Where to store the key value.
232 */
233static bool darwinDictGetProccess(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, PRTPROCESS pProcess)
234{
235 switch (sizeof(*pProcess))
236 {
237 case sizeof(uint16_t): return darwinDictGetU16(DictRef, KeyStrRef, (uint16_t *)pProcess);
238 case sizeof(uint32_t): return darwinDictGetU32(DictRef, KeyStrRef, (uint32_t *)pProcess);
239 case sizeof(uint64_t): return darwinDictGetU64(DictRef, KeyStrRef, (uint64_t *)pProcess);
240 default:
241 AssertMsgFailedReturn(("%d\n", sizeof(*pProcess)), false);
242 }
243}
244
245
246/**
247 * Gets string value, converted to UTF-8 and put in user buffer.
248 *
249 * @returns Success indicator (true/false).
250 * @param DictRef The dictionary.
251 * @param KeyStrRef The key name.
252 * @param psz The string buffer. On failure this will be an empty string ("").
253 * @param cch The size of the buffer.
254 */
255static bool darwinDictGetString(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, char *psz, size_t cch)
256{
257 CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
258 if (ValRef)
259 {
260 if (CFStringGetCString((CFStringRef)ValRef, psz, cch, kCFStringEncodingUTF8))
261 return true;
262 }
263 Assert(cch > 0);
264 *psz = '\0';
265 return false;
266}
267
268
269/**
270 * Gets string value, converted to UTF-8 and put in a IPRT string buffer.
271 *
272 * @returns Success indicator (true/false).
273 * @param DictRef The dictionary.
274 * @param KeyStrRef The key name.
275 * @param ppsz Where to store the key value. Free with RTStrFree. Set to NULL on failure.
276 */
277static bool darwinDictDupString(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, char **ppsz)
278{
279 char szBuf[512];
280 if (darwinDictGetString(DictRef, KeyStrRef, szBuf, sizeof(szBuf)))
281 {
282 *ppsz = RTStrDup(RTStrStrip(szBuf));
283 if (*ppsz)
284 return true;
285 }
286 *ppsz = NULL;
287 return false;
288}
289
290
291/**
292 * Gets a byte string (data) of a specific size.
293 *
294 * @returns Success indicator (true/false).
295 * @param DictRef The dictionary.
296 * @param KeyStrRef The key name.
297 * @param pvBuf The buffer to store the bytes in.
298 * @param cbBuf The size of the buffer. This must exactly match the data size.
299 */
300static bool darwinDictGetData(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, void *pvBuf, size_t cbBuf)
301{
302 CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
303 if (ValRef)
304 {
305 CFIndex cbActual = CFDataGetLength((CFDataRef)ValRef);
306 if (cbActual >= 0 && cbBuf == (size_t)cbActual)
307 {
308 CFDataGetBytes((CFDataRef)ValRef, CFRangeMake(0, cbBuf), (uint8_t *)pvBuf);
309 return true;
310 }
311 }
312 memset(pvBuf, '\0', cbBuf);
313 return false;
314}
315
316
317#if 1 && !defined(STANDALONE_TESTCASE) /* dumping disabled */
318# define DARWIN_IOKIT_LOG(a) Log(a)
319# define DARWIN_IOKIT_LOG_FLUSH() do {} while (0)
320# define DARWIN_IOKIT_DUMP_OBJ(o) do {} while (0)
321#else
322# if defined(STANDALONE_TESTCASE)
323# include <iprt/stream.h>
324# define DARWIN_IOKIT_LOG(a) RTPrintf a
325# define DARWIN_IOKIT_LOG_FLUSH() RTStrmFlush(g_pStdOut)
326# else
327# define DARWIN_IOKIT_LOG(a) RTLogPrintf a
328# define DARWIN_IOKIT_LOG(a) RTLogFlush()
329# endif
330# define DARWIN_IOKIT_DUMP_OBJ(o) darwinDumpObj(o)
331
332/**
333 * Callback for dumping a dictionary key.
334 *
335 * @param pvKey The key name.
336 * @param pvValue The key value
337 * @param pvUser The recursion depth.
338 */
339static void darwinDumpDictCallback(const void *pvKey, const void *pvValue, void *pvUser)
340{
341 /* display the key name. */
342 char *pszKey = (char *)RTMemTmpAlloc(1024);
343 if (!CFStringGetCString((CFStringRef)pvKey, pszKey, 1024, kCFStringEncodingUTF8))
344 strcpy(pszKey, "CFStringGetCString failure");
345 DARWIN_IOKIT_LOG(("%+*s%s", (int)(uintptr_t)pvUser, "", pszKey));
346 RTMemTmpFree(pszKey);
347
348 /* display the value type */
349 CFTypeID Type = CFGetTypeID(pvValue);
350 DARWIN_IOKIT_LOG((" [%d-", Type));
351
352 /* display the value */
353 if (Type == CFDictionaryGetTypeID())
354 {
355 DARWIN_IOKIT_LOG(("dictionary] =\n"
356 "%-*s{\n", (int)(uintptr_t)pvUser, ""));
357 CFDictionaryApplyFunction((CFDictionaryRef)pvValue, darwinDumpDictCallback, (void *)((uintptr_t)pvUser + 4));
358 DARWIN_IOKIT_LOG(("%-*s}\n", (int)(uintptr_t)pvUser, ""));
359 }
360 else if (Type == CFBooleanGetTypeID())
361 DARWIN_IOKIT_LOG(("bool] = %s\n", CFBooleanGetValue((CFBooleanRef)pvValue) ? "true" : "false"));
362 else if (Type == CFNumberGetTypeID())
363 {
364 union
365 {
366 SInt8 s8;
367 SInt16 s16;
368 SInt32 s32;
369 SInt64 s64;
370 Float32 rf32;
371 Float64 rd64;
372 char ch;
373 short s;
374 int i;
375 long l;
376 long long ll;
377 float rf;
378 double rd;
379 CFIndex iCF;
380 } u;
381 memset(&u, 0, sizeof(u));
382 CFNumberType NumType = CFNumberGetType((CFNumberRef)pvValue);
383 if (CFNumberGetValue((CFNumberRef)pvValue, NumType, &u))
384 {
385 switch (CFNumberGetType((CFNumberRef)pvValue))
386 {
387 case kCFNumberSInt8Type: DARWIN_IOKIT_LOG(("SInt8] = %RI8 (%#RX8)\n", NumType, u.s8, u.s8)); break;
388 case kCFNumberSInt16Type: DARWIN_IOKIT_LOG(("SInt16] = %RI16 (%#RX16)\n", NumType, u.s16, u.s16)); break;
389 case kCFNumberSInt32Type: DARWIN_IOKIT_LOG(("SInt32] = %RI32 (%#RX32)\n", NumType, u.s32, u.s32)); break;
390 case kCFNumberSInt64Type: DARWIN_IOKIT_LOG(("SInt64] = %RI64 (%#RX64)\n", NumType, u.s64, u.s64)); break;
391 case kCFNumberFloat32Type: DARWIN_IOKIT_LOG(("float32] = %#lx\n", NumType, u.l)); break;
392 case kCFNumberFloat64Type: DARWIN_IOKIT_LOG(("float64] = %#llx\n", NumType, u.ll)); break;
393 case kCFNumberFloatType: DARWIN_IOKIT_LOG(("float] = %#lx\n", NumType, u.l)); break;
394 case kCFNumberDoubleType: DARWIN_IOKIT_LOG(("double] = %#llx\n", NumType, u.ll)); break;
395 case kCFNumberCharType: DARWIN_IOKIT_LOG(("char] = %hhd (%hhx)\n", NumType, u.ch, u.ch)); break;
396 case kCFNumberShortType: DARWIN_IOKIT_LOG(("short] = %hd (%hx)\n", NumType, u.s, u.s)); break;
397 case kCFNumberIntType: DARWIN_IOKIT_LOG(("int] = %d (%#x)\n", NumType, u.i, u.i)); break;
398 case kCFNumberLongType: DARWIN_IOKIT_LOG(("long] = %ld (%#lx)\n", NumType, u.l, u.l)); break;
399 case kCFNumberLongLongType: DARWIN_IOKIT_LOG(("long long] = %lld (%#llx)\n", NumType, u.ll, u.ll)); break;
400 case kCFNumberCFIndexType: DARWIN_IOKIT_LOG(("CFIndex] = %lld (%#llx)\n", NumType, (long long)u.iCF, (long long)u.iCF)); break;
401 break;
402 default: DARWIN_IOKIT_LOG(("%d?] = %lld (%llx)\n", NumType, u.ll, u.ll)); break;
403 }
404 }
405 else
406 DARWIN_IOKIT_LOG(("number] = CFNumberGetValue failed\n"));
407 }
408 else if (Type == CFBooleanGetTypeID())
409 DARWIN_IOKIT_LOG(("boolean] = %RTbool\n", CFBooleanGetValue((CFBooleanRef)pvValue)));
410 else if (Type == CFStringGetTypeID())
411 {
412 DARWIN_IOKIT_LOG(("string] = "));
413 char *pszValue = (char *)RTMemTmpAlloc(16*_1K);
414 if (!CFStringGetCString((CFStringRef)pvValue, pszValue, 16*_1K, kCFStringEncodingUTF8))
415 strcpy(pszValue, "CFStringGetCString failure");
416 DARWIN_IOKIT_LOG(("\"%s\"\n", pszValue));
417 RTMemTmpFree(pszValue);
418 }
419 else if (Type == CFDataGetTypeID())
420 {
421 CFIndex cb = CFDataGetLength((CFDataRef)pvValue);
422 DARWIN_IOKIT_LOG(("%zu bytes] =", (size_t)cb));
423 void *pvData = RTMemTmpAlloc(cb + 8);
424 CFDataGetBytes((CFDataRef)pvValue, CFRangeMake(0, cb), (uint8_t *)pvData);
425 if (!cb)
426 DARWIN_IOKIT_LOG((" \n"));
427 else if (cb <= 32)
428 DARWIN_IOKIT_LOG((" %.*Rhxs\n", cb, pvData));
429 else
430 DARWIN_IOKIT_LOG(("\n%.*Rhxd\n", cb, pvData));
431 RTMemTmpFree(pvData);
432 }
433 else
434 DARWIN_IOKIT_LOG(("??] = %p\n", pvValue));
435}
436
437
438/**
439 * Dumps a dictionary to the log.
440 *
441 * @param DictRef The dictionary to dump.
442 */
443static void darwinDumpDict(CFMutableDictionaryRef DictRef, unsigned cIndents)
444{
445 CFDictionaryApplyFunction(DictRef, darwinDumpDictCallback, (void *)(uintptr_t)cIndents);
446 DARWIN_IOKIT_LOG_FLUSH();
447}
448
449
450/**
451 * Dumps an I/O kit registry object and all it children.
452 * @param Object The object to dump.
453 * @param cIndents The number of indents to use.
454 */
455static void darwinDumpObjInt(io_object_t Object, unsigned cIndents)
456{
457 static io_string_t s_szPath;
458 kern_return_t krc = IORegistryEntryGetPath(Object, kIOServicePlane, s_szPath);
459 if (krc != KERN_SUCCESS)
460 strcpy(s_szPath, "IORegistryEntryGetPath failed");
461 DARWIN_IOKIT_LOG(("Dumping %p - %s:\n", (const void *)Object, s_szPath));
462
463 CFMutableDictionaryRef PropsRef = 0;
464 krc = IORegistryEntryCreateCFProperties(Object, &PropsRef, kCFAllocatorDefault, kNilOptions);
465 if (krc == KERN_SUCCESS)
466 {
467 darwinDumpDict(PropsRef, cIndents + 4);
468 CFRelease(PropsRef);
469 }
470
471 /*
472 * Children.
473 */
474 io_iterator_t Children;
475 krc = IORegistryEntryGetChildIterator(Object, kIOServicePlane, &Children);
476 if (krc == KERN_SUCCESS)
477 {
478 io_object_t Child;
479 while ((Child = IOIteratorNext(Children)))
480 {
481 darwinDumpObjInt(Child, cIndents + 4);
482 IOObjectRelease(Child);
483 }
484 IOObjectRelease(Children);
485 }
486 else
487 DARWIN_IOKIT_LOG(("IORegistryEntryGetChildIterator -> %#x\n", krc));
488}
489
490/**
491 * Dumps an I/O kit registry object and all it children.
492 * @param Object The object to dump.
493 */
494static void darwinDumpObj(io_object_t Object)
495{
496 darwinDumpObjInt(Object, 0);
497}
498
499#endif /* helpers for dumping registry dictionaries */
500
501
502/**
503 * Notification data created by DarwinSubscribeUSBNotifications, used by
504 * the callbacks and finally freed by DarwinUnsubscribeUSBNotifications.
505 */
506typedef struct DARWINUSBNOTIFY
507{
508 /** The notification port.
509 * It's shared between the notification callbacks. */
510 IONotificationPortRef NotifyPort;
511 /** The run loop source for NotifyPort. */
512 CFRunLoopSourceRef NotifyRLSrc;
513 /** The attach notification iterator. */
514 io_iterator_t AttachIterator;
515 /** The 2nd attach notification iterator. */
516 io_iterator_t AttachIterator2;
517 /** The detach notificaiton iterator. */
518 io_iterator_t DetachIterator;
519} DARWINUSBNOTIFY, *PDARWINUSBNOTIFY;
520
521
522/**
523 * Run thru an interrator.
524 *
525 * The docs says this is necessary to start getting notifications,
526 * so this function is called in the callbacks and right after
527 * registering the notification.
528 *
529 * @param pIterator The iterator reference.
530 */
531static void darwinDrainIterator(io_iterator_t pIterator)
532{
533 io_object_t Object;
534 while ((Object = IOIteratorNext(pIterator)))
535 {
536 DARWIN_IOKIT_DUMP_OBJ(Object);
537 IOObjectRelease(Object);
538 }
539}
540
541
542/**
543 * Callback for the 1st attach notification.
544 *
545 * @param pvNotify Our data.
546 * @param NotifyIterator The notification iterator.
547 */
548static void darwinUSBAttachNotification1(void *pvNotify, io_iterator_t NotifyIterator)
549{
550 DARWIN_IOKIT_LOG(("USB Attach Notification1\n"));
551 NOREF(pvNotify); //PDARWINUSBNOTIFY pNotify = (PDARWINUSBNOTIFY)pvNotify;
552 darwinDrainIterator(NotifyIterator);
553}
554
555
556/**
557 * Callback for the 2nd attach notification.
558 *
559 * @param pvNotify Our data.
560 * @param NotifyIterator The notification iterator.
561 */
562static void darwinUSBAttachNotification2(void *pvNotify, io_iterator_t NotifyIterator)
563{
564 DARWIN_IOKIT_LOG(("USB Attach Notification2\n"));
565 NOREF(pvNotify); //PDARWINUSBNOTIFY pNotify = (PDARWINUSBNOTIFY)pvNotify;
566 darwinDrainIterator(NotifyIterator);
567}
568
569
570/**
571 * Callback for the detach notifications.
572 *
573 * @param pvNotify Our data.
574 * @param NotifyIterator The notification iterator.
575 */
576static void darwinUSBDetachNotification(void *pvNotify, io_iterator_t NotifyIterator)
577{
578 DARWIN_IOKIT_LOG(("USB Detach Notification\n"));
579 NOREF(pvNotify); //PDARWINUSBNOTIFY pNotify = (PDARWINUSBNOTIFY)pvNotify;
580 darwinDrainIterator(NotifyIterator);
581}
582
583
584/**
585 * Subscribes the run loop to USB notification events relevant to
586 * device attach/detach.
587 *
588 * The source mode for these events is defined as VBOX_IOKIT_MODE_STRING
589 * so that the caller can listen to events from this mode only and
590 * re-evalutate the list of attached devices whenever an event arrives.
591 *
592 * @returns opaque for passing to the unsubscribe function. If NULL
593 * something unexpectedly failed during subscription.
594 */
595void *DarwinSubscribeUSBNotifications(void)
596{
597 AssertReturn(darwinOpenMasterPort(), NULL);
598
599 PDARWINUSBNOTIFY pNotify = (PDARWINUSBNOTIFY)RTMemAllocZ(sizeof(*pNotify));
600 AssertReturn(pNotify, NULL);
601
602 /*
603 * Create the notification port, bake it into a runloop source which we
604 * then add to our run loop.
605 */
606 pNotify->NotifyPort = IONotificationPortCreate(g_MasterPort);
607 Assert(pNotify->NotifyPort);
608 if (pNotify->NotifyPort)
609 {
610 pNotify->NotifyRLSrc = IONotificationPortGetRunLoopSource(pNotify->NotifyPort);
611 Assert(pNotify->NotifyRLSrc);
612 if (pNotify->NotifyRLSrc)
613 {
614 CFRunLoopRef RunLoopRef = CFRunLoopGetCurrent();
615 CFRetain(RunLoopRef); /* Workaround for crash when cleaning up the TLS / runloop((sub)mode). See #2807. */
616 CFRunLoopAddSource(RunLoopRef, pNotify->NotifyRLSrc, CFSTR(VBOX_IOKIT_MODE_STRING));
617
618 /*
619 * Create the notifcation callbacks.
620 */
621 kern_return_t rc = IOServiceAddMatchingNotification(pNotify->NotifyPort,
622 kIOPublishNotification,
623 IOServiceMatching(kIOUSBDeviceClassName),
624 darwinUSBAttachNotification1,
625 pNotify,
626 &pNotify->AttachIterator);
627 if (rc == KERN_SUCCESS)
628 {
629 darwinDrainIterator(pNotify->AttachIterator);
630 rc = IOServiceAddMatchingNotification(pNotify->NotifyPort,
631 kIOMatchedNotification,
632 IOServiceMatching(kIOUSBDeviceClassName),
633 darwinUSBAttachNotification2,
634 pNotify,
635 &pNotify->AttachIterator2);
636 if (rc == KERN_SUCCESS)
637 {
638 darwinDrainIterator(pNotify->AttachIterator2);
639 rc = IOServiceAddMatchingNotification(pNotify->NotifyPort,
640 kIOTerminatedNotification,
641 IOServiceMatching(kIOUSBDeviceClassName),
642 darwinUSBDetachNotification,
643 pNotify,
644 &pNotify->DetachIterator);
645 {
646 darwinDrainIterator(pNotify->DetachIterator);
647 return pNotify;
648 }
649 IOObjectRelease(pNotify->AttachIterator2);
650 }
651 IOObjectRelease(pNotify->AttachIterator);
652 }
653 CFRunLoopRemoveSource(RunLoopRef, pNotify->NotifyRLSrc, CFSTR(VBOX_IOKIT_MODE_STRING));
654 }
655 IONotificationPortDestroy(pNotify->NotifyPort);
656 }
657
658 RTMemFree(pNotify);
659 return NULL;
660}
661
662
663/**
664 * Unsubscribe the run loop from USB notification subscribed to
665 * by DarwinSubscribeUSBNotifications.
666 *
667 * @param pvOpaque The return value from DarwinSubscribeUSBNotifications.
668 */
669void DarwinUnsubscribeUSBNotifications(void *pvOpaque)
670{
671 PDARWINUSBNOTIFY pNotify = (PDARWINUSBNOTIFY)pvOpaque;
672 if (!pNotify)
673 return;
674
675 IOObjectRelease(pNotify->AttachIterator);
676 pNotify->AttachIterator = NULL;
677 IOObjectRelease(pNotify->AttachIterator2);
678 pNotify->AttachIterator2 = NULL;
679 IOObjectRelease(pNotify->DetachIterator);
680 pNotify->DetachIterator = NULL;
681
682 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), pNotify->NotifyRLSrc, CFSTR(VBOX_IOKIT_MODE_STRING));
683 IONotificationPortDestroy(pNotify->NotifyPort);
684 pNotify->NotifyRLSrc = NULL;
685 pNotify->NotifyPort = NULL;
686
687 RTMemFree(pNotify);
688}
689
690
691/**
692 * Decends recursivly into a IORegistry tree locating the first object of a given class.
693 *
694 * The search is performed depth first.
695 *
696 * @returns Object reference if found, NULL if not.
697 * @param Object The current tree root.
698 * @param pszClass The name of the class we're looking for.
699 * @param pszNameBuf A scratch buffer for query the class name in to avoid
700 * wasting 128 bytes on an io_name_t object for every recursion.
701 */
702static io_object_t darwinFindObjectByClass(io_object_t Object, const char *pszClass, io_name_t pszNameBuf)
703{
704 io_iterator_t Children;
705 kern_return_t krc = IORegistryEntryGetChildIterator(Object, kIOServicePlane, &Children);
706 if (krc != KERN_SUCCESS)
707 return NULL;
708 io_object_t Child;
709 while ((Child = IOIteratorNext(Children)))
710 {
711 krc = IOObjectGetClass(Child, pszNameBuf);
712 if ( krc == KERN_SUCCESS
713 && !strcmp(pszNameBuf, pszClass))
714 break;
715
716 io_object_t GrandChild = darwinFindObjectByClass(Child, pszClass, pszNameBuf);
717 IOObjectRelease(Child);
718 if (GrandChild)
719 {
720 Child = GrandChild;
721 break;
722 }
723 }
724 IOObjectRelease(Children);
725 return Child;
726}
727
728
729/**
730 * Decends recursivly into IOUSBMassStorageClass tree to check whether
731 * the MSD is mounted or not.
732 *
733 * The current heuristic is to look for the IOMedia class.
734 *
735 * @returns true if mounted, false if not.
736 * @param MSDObj The IOUSBMassStorageClass object.
737 * @param pszNameBuf A scratch buffer for query the class name in to avoid
738 * wasting 128 bytes on an io_name_t object for every recursion.
739 */
740static bool darwinIsMassStorageInterfaceInUse(io_object_t MSDObj, io_name_t pszNameBuf)
741{
742 io_object_t MediaObj = darwinFindObjectByClass(MSDObj, "IOMedia", pszNameBuf);
743 if (MediaObj)
744 {
745 /* more checks? */
746 IOObjectRelease(MediaObj);
747 return true;
748 }
749 return false;
750}
751
752
753/**
754 * Worker function for DarwinGetUSBDevices() that tries to figure out
755 * what state the device is in and set enmState.
756 *
757 * This is mostly a matter of distinguishing between devices that nobody
758 * uses, devices that can be seized and devices that cannot be grabbed.
759 *
760 * @param pCur The USB device data.
761 * @param USBDevice The USB device object.
762 * @param PropsRef The USB device properties.
763 */
764static void darwinDeterminUSBDeviceState(PUSBDEVICE pCur, io_object_t USBDevice, CFMutableDictionaryRef PropsRef)
765{
766 /*
767 * Iterate the interfaces (among the children of the IOUSBDevice object).
768 */
769 io_iterator_t Interfaces;
770 kern_return_t krc = IORegistryEntryGetChildIterator(USBDevice, kIOServicePlane, &Interfaces);
771 if (krc != KERN_SUCCESS)
772 return;
773
774 bool fHaveOwner = false;
775 RTPROCESS Owner = NIL_RTPROCESS;
776 bool fHaveClient = false;
777 RTPROCESS Client = NIL_RTPROCESS;
778 bool fUserClientOnly = true;
779 bool fConfigured = false;
780 bool fInUse = false;
781 bool fSeizable = true;
782 io_object_t Interface;
783 while ((Interface = IOIteratorNext(Interfaces)))
784 {
785 io_name_t szName;
786 krc = IOObjectGetClass(Interface, szName);
787 if ( krc == KERN_SUCCESS
788 && !strcmp(szName, "IOUSBInterface"))
789 {
790 fConfigured = true;
791
792 /*
793 * Iterate the interface children looking for stuff other than
794 * IOUSBUserClientInit objects.
795 */
796 io_iterator_t Children1;
797 krc = IORegistryEntryGetChildIterator(Interface, kIOServicePlane, &Children1);
798 if (krc == KERN_SUCCESS)
799 {
800 io_object_t Child1;
801 while ((Child1 = IOIteratorNext(Children1)))
802 {
803 krc = IOObjectGetClass(Child1, szName);
804 if ( krc == KERN_SUCCESS
805 && strcmp(szName, "IOUSBUserClientInit"))
806 {
807 fUserClientOnly = false;
808
809 if (!strcmp(szName, "IOUSBMassStorageClass"))
810 {
811 /* Only permit capturing MSDs that aren't mounted, at least
812 until the GUI starts poping up warnings about data loss
813 and such when capturing a busy device. */
814 fSeizable = false;
815 fInUse |= darwinIsMassStorageInterfaceInUse(Child1, szName);
816 }
817 else if (!strcmp(szName, "IOUSBHIDDriver")
818 || !strcmp(szName, "AppleHIDMouse")
819 /** @todo more? */)
820 {
821 /* For now, just assume that all HID devices are inaccessible
822 because of the greedy HID service. */
823 fSeizable = false;
824 fInUse = true;
825 }
826 else
827 fInUse = true;
828 }
829 IOObjectRelease(Child1);
830 }
831 IOObjectRelease(Children1);
832 }
833 }
834 /*
835 * Not an interface, could it be VBoxUSBDevice?
836 * If it is, get the owner and client properties.
837 */
838 else if ( krc == KERN_SUCCESS
839 && !strcmp(szName, VBOXUSBDEVICE_CLASS_NAME))
840 {
841 CFMutableDictionaryRef PropsRef = 0;
842 krc = IORegistryEntryCreateCFProperties(Interface, &PropsRef, kCFAllocatorDefault, kNilOptions);
843 if (krc == KERN_SUCCESS)
844 {
845 fHaveOwner = darwinDictGetProccess(PropsRef, CFSTR(VBOXUSB_OWNER_KEY), &Owner);
846 fHaveClient = darwinDictGetProccess(PropsRef, CFSTR(VBOXUSB_CLIENT_KEY), &Client);
847 CFRelease(PropsRef);
848 }
849 }
850
851 IOObjectRelease(Interface);
852 }
853 IOObjectRelease(Interfaces);
854
855 /*
856 * Calc the status.
857 */
858 if (fHaveOwner)
859 {
860 if (Owner == RTProcSelf())
861 pCur->enmState = !fHaveClient || Client == NIL_RTPROCESS || !Client
862 ? USBDEVICESTATE_HELD_BY_PROXY
863 : USBDEVICESTATE_USED_BY_GUEST;
864 else
865 pCur->enmState = USBDEVICESTATE_USED_BY_HOST;
866 }
867 else if (fUserClientOnly)
868 /** @todo how to detect other user client?!? - Look for IOUSBUserClient! */
869 pCur->enmState = !fConfigured
870 ? USBDEVICESTATE_UNUSED
871 : USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
872 else if (!fInUse)
873 pCur->enmState = USBDEVICESTATE_UNUSED;
874 else
875 pCur->enmState = fSeizable
876 ? USBDEVICESTATE_USED_BY_HOST_CAPTURABLE
877 : USBDEVICESTATE_USED_BY_HOST;
878}
879
880
881/**
882 * Enumerate the USB devices returning a FIFO of them.
883 *
884 * @returns Pointer to the head.
885 * USBProxyService::freeDevice is expected to free each of the list elements.
886 */
887PUSBDEVICE DarwinGetUSBDevices(void)
888{
889 AssertReturn(darwinOpenMasterPort(), NULL);
890 //DARWIN_IOKIT_LOG(("DarwinGetUSBDevices\n"));
891
892 /*
893 * Create a matching dictionary for searching for USB Devices in the IOKit.
894 */
895 CFMutableDictionaryRef RefMatchingDict = IOServiceMatching(kIOUSBDeviceClassName);
896 AssertReturn(RefMatchingDict, NULL);
897
898 /*
899 * Perform the search and get a collection of USB Device back.
900 */
901 io_iterator_t USBDevices = NULL;
902 IOReturn rc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &USBDevices);
903 AssertMsgReturn(rc == kIOReturnSuccess, ("rc=%d\n", rc), NULL);
904 RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
905
906 /*
907 * Enumerate the USB Devices.
908 */
909 PUSBDEVICE pHead = NULL;
910 PUSBDEVICE pTail = NULL;
911 unsigned i = 0;
912 io_object_t USBDevice;
913 while ((USBDevice = IOIteratorNext(USBDevices)) != 0)
914 {
915 DARWIN_IOKIT_DUMP_OBJ(USBDevice);
916
917 /*
918 * Query the device properties from the registry.
919 *
920 * We could alternatively use the device and such, but that will be
921 * slower and we would have to resort to the registry for the three
922 * string anyway.
923 */
924 CFMutableDictionaryRef PropsRef = 0;
925 kern_return_t krc = IORegistryEntryCreateCFProperties(USBDevice, &PropsRef, kCFAllocatorDefault, kNilOptions);
926 if (krc == KERN_SUCCESS)
927 {
928 bool fOk = false;
929 PUSBDEVICE pCur = (PUSBDEVICE)RTMemAllocZ(sizeof(*pCur));
930 do /* loop for breaking out of on failure. */
931 {
932 AssertBreak(pCur);
933
934 /*
935 * Mandatory
936 */
937 pCur->bcdUSB = 0; /* we've no idea. */
938 pCur->enmState = USBDEVICESTATE_USED_BY_HOST_CAPTURABLE; /* just a default, we'll try harder in a bit. */
939
940 AssertBreak(darwinDictGetU8(PropsRef, CFSTR(kUSBDeviceClass), &pCur->bDeviceClass));
941 /* skip hubs */
942 if (pCur->bDeviceClass == 0x09 /* hub, find a define! */)
943 break;
944 AssertBreak(darwinDictGetU8(PropsRef, CFSTR(kUSBDeviceSubClass), &pCur->bDeviceSubClass));
945 AssertBreak(darwinDictGetU8(PropsRef, CFSTR(kUSBDeviceProtocol), &pCur->bDeviceProtocol));
946 AssertBreak(darwinDictGetU16(PropsRef, CFSTR(kUSBVendorID), &pCur->idVendor));
947 AssertBreak(darwinDictGetU16(PropsRef, CFSTR(kUSBProductID), &pCur->idProduct));
948 AssertBreak(darwinDictGetU16(PropsRef, CFSTR(kUSBDeviceReleaseNumber), &pCur->bcdDevice));
949 uint32_t u32LocationId;
950 AssertBreak(darwinDictGetU32(PropsRef, CFSTR(kUSBDevicePropertyLocationID), &u32LocationId));
951 uint64_t u64SessionId;
952 AssertBreak(darwinDictGetU64(PropsRef, CFSTR("sessionID"), &u64SessionId));
953 char szAddress[64];
954 RTStrPrintf(szAddress, sizeof(szAddress), "p=0x%04RX16;v=0x%04RX16;s=0x%016RX64;l=0x%08RX32",
955 pCur->idProduct, pCur->idVendor, u64SessionId, u32LocationId);
956 pCur->pszAddress = RTStrDup(szAddress);
957 AssertBreak(pCur->pszAddress);
958 pCur->bBus = u32LocationId >> 24;
959 AssertBreak(darwinDictGetU8(PropsRef, CFSTR("PortNum"), &pCur->bPort));
960 uint8_t bSpeed;
961 AssertBreak(darwinDictGetU8(PropsRef, CFSTR(kUSBDevicePropertySpeed), &bSpeed));
962 Assert(bSpeed <= 2);
963 pCur->enmSpeed = bSpeed == 2 ? USBDEVICESPEED_HIGH
964 : bSpeed == 1 ? USBDEVICESPEED_FULL
965 : bSpeed == 0 ? USBDEVICESPEED_LOW
966 : USBDEVICESPEED_UNKNOWN;
967
968 /*
969 * Optional.
970 * There are some nameless device in the iMac, apply names to them.
971 */
972 darwinDictDupString(PropsRef, CFSTR("USB Vendor Name"), (char **)&pCur->pszManufacturer);
973 if ( !pCur->pszManufacturer
974 && pCur->idVendor == kIOUSBVendorIDAppleComputer)
975 pCur->pszManufacturer = RTStrDup("Apple Computer, Inc.");
976 darwinDictDupString(PropsRef, CFSTR("USB Product Name"), (char **)&pCur->pszProduct);
977 if ( !pCur->pszProduct
978 && pCur->bDeviceClass == 224 /* Wireless */
979 && pCur->bDeviceSubClass == 1 /* Radio Frequency */
980 && pCur->bDeviceProtocol == 1 /* Bluetooth */)
981 pCur->pszProduct = RTStrDup("Bluetooth");
982 darwinDictDupString(PropsRef, CFSTR("USB Serial Number"), (char **)&pCur->pszSerialNumber);
983
984#if 0 /* leave the remainder as zero for now. */
985 /*
986 * Create a plugin interface for the service and query its USB Device interface.
987 */
988 SInt32 Score = 0;
989 IOCFPlugInInterface **ppPlugInInterface = NULL;
990 rc = IOCreatePlugInInterfaceForService(USBDevice, kIOUSBDeviceUserClientTypeID,
991 kIOCFPlugInInterfaceID, &ppPlugInInterface, &Score);
992 if (rc == kIOReturnSuccess)
993 {
994 IOUSBDeviceInterface245 **ppUSBDevI = NULL;
995 HRESULT hrc = (*ppPlugInInterface)->QueryInterface(ppPlugInInterface,
996 CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID245),
997 (LPVOID *)&ppUSBDevI);
998 rc = IODestroyPlugInInterface(ppPlugInInterface); Assert(rc == kIOReturnSuccess);
999 ppPlugInInterface = NULL;
1000 if (hrc == S_OK)
1001 {
1002 /** @todo enumerate configurations and interfaces if we actually need them. */
1003 //IOReturn (*GetNumberOfConfigurations)(void *self, UInt8 *numConfig);
1004 //IOReturn (*GetConfigurationDescriptorPtr)(void *self, UInt8 configIndex, IOUSBConfigurationDescriptorPtr *desc);
1005 //IOReturn (*CreateInterfaceIterator)(void *self, IOUSBFindInterfaceRequest *req, io_iterator_t *iter);
1006 }
1007 long cReft = (*ppUSBDeviceInterface)->Release(ppUSBDeviceInterface); MY_CHECK_CREFS(cRefs);
1008 }
1009#endif
1010 /*
1011 * Try determin the state.
1012 */
1013 darwinDeterminUSBDeviceState(pCur, USBDevice, PropsRef);
1014
1015 /*
1016 * We're good. Link the device.
1017 */
1018 pCur->pPrev = pTail;
1019 if (pTail)
1020 pTail = pTail->pNext = pCur;
1021 else
1022 pTail = pHead = pCur;
1023 fOk = true;
1024 } while (0);
1025
1026 /* cleanup on failure / skipped device. */
1027 if (!fOk && pCur)
1028 DarwinFreeUSBDeviceFromIOKit(pCur);
1029
1030 CFRelease(PropsRef);
1031 }
1032 else
1033 AssertMsgFailed(("krc=%#x\n", krc));
1034
1035 IOObjectRelease(USBDevice);
1036 i++;
1037 }
1038
1039 IOObjectRelease(USBDevices);
1040 //DARWIN_IOKIT_LOG_FLUSH();
1041
1042 /*
1043 * Some post processing. There are a couple of things we have to
1044 * make 100% sure about, and that is that the (Apple) keyboard
1045 * and mouse most likely to be in use by the user aren't available
1046 * for capturing. If there is no Apple mouse or keyboard we'll
1047 * take the first one from another vendor.
1048 */
1049 /* As it turns out, the HID service will take all keyboards and mice
1050 and we're not currently able to seize them. */
1051 PUSBDEVICE pMouse = NULL;
1052 PUSBDEVICE pKeyboard = NULL;
1053 for (PUSBDEVICE pCur = pHead; pCur; pCur = pCur->pNext)
1054 if (pCur->idVendor == kIOUSBVendorIDAppleComputer)
1055 {
1056 /*
1057 * This test is a bit rough, should check device class/protocol but
1058 * we don't have interface info yet so that might be a bit tricky.
1059 */
1060 if ( ( !pKeyboard
1061 || pKeyboard->idVendor != kIOUSBVendorIDAppleComputer)
1062 && pCur->pszProduct
1063 && strstr(pCur->pszProduct, " Keyboard"))
1064 pKeyboard = pCur;
1065 else if ( ( !pMouse
1066 || pMouse->idVendor != kIOUSBVendorIDAppleComputer)
1067 && pCur->pszProduct
1068 && strstr(pCur->pszProduct, " Mouse")
1069 )
1070 pMouse = pCur;
1071 }
1072 else if (!pKeyboard || !pMouse)
1073 {
1074 if ( pCur->bDeviceClass == 3 /* HID */
1075 && pCur->bDeviceProtocol == 1 /* Keyboard */)
1076 pKeyboard = pCur;
1077 else if ( pCur->bDeviceClass == 3 /* HID */
1078 && pCur->bDeviceProtocol == 2 /* Mouse */)
1079 pMouse = pCur;
1080 /** @todo examin interfaces */
1081 }
1082
1083 if (pKeyboard)
1084 pKeyboard->enmState = USBDEVICESTATE_USED_BY_HOST;
1085 if (pMouse)
1086 pMouse->enmState = USBDEVICESTATE_USED_BY_HOST;
1087
1088 return pHead;
1089}
1090
1091
1092/**
1093 * Triggers re-enumeration of a device.
1094 *
1095 * @returns VBox status code.
1096 * @param pCur The USBDEVICE structure for the device.
1097 */
1098int DarwinReEnumerateUSBDevice(PCUSBDEVICE pCur)
1099{
1100 int vrc;
1101 const char *pszAddress = pCur->pszAddress;
1102 AssertPtrReturn(pszAddress, VERR_INVALID_POINTER);
1103 AssertReturn(darwinOpenMasterPort(), VERR_GENERAL_FAILURE);
1104
1105 /*
1106 * This code is a short version of the Open method in USBProxyDevice-darwin.cpp stuff.
1107 * Fixes made to this code probably applies there too!
1108 */
1109
1110 CFMutableDictionaryRef RefMatchingDict = IOServiceMatching(kIOUSBDeviceClassName);
1111 AssertReturn(RefMatchingDict, NULL);
1112
1113 uint64_t u64SessionId = 0;
1114 uint32_t u32LocationId = 0;
1115 const char *psz = pszAddress;
1116 do
1117 {
1118 const char chValue = *psz;
1119 AssertReleaseReturn(psz[1] == '=', VERR_INTERNAL_ERROR);
1120 uint64_t u64Value;
1121 int rc = RTStrToUInt64Ex(psz + 2, (char **)&psz, 0, &u64Value);
1122 AssertReleaseRCReturn(rc, rc);
1123 AssertReleaseReturn(!*psz || *psz == ';', rc);
1124 switch (chValue)
1125 {
1126 case 'l':
1127 u32LocationId = (uint32_t)u64Value;
1128 break;
1129 case 's':
1130 u64SessionId = u64Value;
1131 break;
1132 case 'p':
1133 case 'v':
1134 {
1135#if 0 /* Guess what, this doesn't 'ing work either! */
1136 SInt32 i32 = (int16_t)u64Value;
1137 CFNumberRef Num = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i32);
1138 AssertBreak(Num);
1139 CFDictionarySetValue(RefMatchingDict, chValue == 'p' ? CFSTR(kUSBProductID) : CFSTR(kUSBVendorID), Num);
1140 CFRelease(Num);
1141#endif
1142 break;
1143 }
1144 default:
1145 AssertReleaseMsgFailedReturn(("chValue=%#x\n", chValue), VERR_INTERNAL_ERROR);
1146 }
1147 if (*psz == ';')
1148 psz++;
1149 } while (*psz);
1150
1151 io_iterator_t USBDevices = NULL;
1152 IOReturn irc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &USBDevices);
1153 AssertMsgReturn(irc == kIOReturnSuccess, ("irc=%#x\n", irc), NULL);
1154 RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
1155
1156 unsigned cMatches = 0;
1157 io_object_t USBDevice;
1158 while ((USBDevice = IOIteratorNext(USBDevices)))
1159 {
1160 cMatches++;
1161 CFMutableDictionaryRef PropsRef = 0;
1162 kern_return_t krc = IORegistryEntryCreateCFProperties(USBDevice, &PropsRef, kCFAllocatorDefault, kNilOptions);
1163 if (krc == KERN_SUCCESS)
1164 {
1165 uint64_t u64CurSessionId;
1166 uint32_t u32CurLocationId;
1167 if ( ( !u64SessionId
1168 || ( darwinDictGetU64(PropsRef, CFSTR("sessionID"), &u64CurSessionId)
1169 && u64CurSessionId == u64SessionId))
1170 && ( !u32LocationId
1171 || ( darwinDictGetU32(PropsRef, CFSTR(kUSBDevicePropertyLocationID), &u32CurLocationId)
1172 && u32CurLocationId == u32LocationId))
1173 )
1174 {
1175 CFRelease(PropsRef);
1176 break;
1177 }
1178 CFRelease(PropsRef);
1179 }
1180 IOObjectRelease(USBDevice);
1181 }
1182 IOObjectRelease(USBDevices);
1183 USBDevices = NULL;
1184 if (!USBDevice)
1185 {
1186 LogRel(("USB: Device '%s' not found (%d pid+vid matches)\n", pszAddress, cMatches));
1187 IOObjectRelease(USBDevices);
1188 return VERR_VUSB_DEVICE_NAME_NOT_FOUND;
1189 }
1190
1191 /*
1192 * Create a plugin interface for the device and query its IOUSBDeviceInterface.
1193 */
1194 SInt32 Score = 0;
1195 IOCFPlugInInterface **ppPlugInInterface = NULL;
1196 irc = IOCreatePlugInInterfaceForService(USBDevice, kIOUSBDeviceUserClientTypeID,
1197 kIOCFPlugInInterfaceID, &ppPlugInInterface, &Score);
1198 if (irc == kIOReturnSuccess)
1199 {
1200 IOUSBDeviceInterface245 **ppDevI = NULL;
1201 HRESULT hrc = (*ppPlugInInterface)->QueryInterface(ppPlugInInterface,
1202 CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID245),
1203 (LPVOID *)&ppDevI);
1204 irc = IODestroyPlugInInterface(ppPlugInInterface); Assert(irc == kIOReturnSuccess);
1205 ppPlugInInterface = NULL;
1206 if (hrc == S_OK)
1207 {
1208 /*
1209 * Try open the device for exclusive access.
1210 */
1211 irc = (*ppDevI)->USBDeviceOpenSeize(ppDevI);
1212 if (irc == kIOReturnExclusiveAccess)
1213 {
1214 RTThreadSleep(20);
1215 irc = (*ppDevI)->USBDeviceOpenSeize(ppDevI);
1216 }
1217 if (irc == kIOReturnSuccess)
1218 {
1219 /*
1220 * Re-enumerate the device and bail out.
1221 */
1222 irc = (*ppDevI)->USBDeviceReEnumerate(ppDevI, 0);
1223 if (irc == kIOReturnSuccess)
1224 vrc = VINF_SUCCESS;
1225 else
1226 {
1227 LogRel(("USB: Failed to open device '%s', plug-in creation failed with irc=%#x.\n", pszAddress, irc));
1228 vrc = RTErrConvertFromDarwinIO(irc);
1229 }
1230
1231 (*ppDevI)->USBDeviceClose(ppDevI);
1232 }
1233 else if (irc == kIOReturnExclusiveAccess)
1234 {
1235 LogRel(("USB: Device '%s' is being used by another process\n", pszAddress));
1236 vrc = VERR_SHARING_VIOLATION;
1237 }
1238 else
1239 {
1240 LogRel(("USB: Failed to open device '%s', irc=%#x.\n", pszAddress, irc));
1241 vrc = VERR_OPEN_FAILED;
1242 }
1243 }
1244 else
1245 {
1246 LogRel(("USB: Failed to create plugin interface for device '%s', hrc=%#x.\n", pszAddress, hrc));
1247 vrc = VERR_OPEN_FAILED;
1248 }
1249
1250 (*ppDevI)->Release(ppDevI);
1251 }
1252 else
1253 {
1254 LogRel(("USB: Failed to open device '%s', plug-in creation failed with irc=%#x.\n", pszAddress, irc));
1255 vrc = RTErrConvertFromDarwinIO(irc);
1256 }
1257
1258 return vrc;
1259}
1260
1261#endif /* VBOX_WITH_USB */
1262
1263
1264/**
1265 * Enumerate the DVD drives returning a FIFO of device name strings.
1266 *
1267 * @returns Pointer to the head.
1268 * The caller is responsible for calling RTMemFree() on each of the nodes.
1269 */
1270PDARWINDVD DarwinGetDVDDrives(void)
1271{
1272 AssertReturn(darwinOpenMasterPort(), NULL);
1273
1274 /*
1275 * Create a matching dictionary for searching for DVD services in the IOKit.
1276 *
1277 * [If I understand this correctly, plain CDROMs doesn't show up as
1278 * IODVDServices. Too keep things simple, we will only support DVDs
1279 * until somebody complains about it and we get hardware to test it on.
1280 * (Unless I'm much mistaken, there aren't any (orignal) intel macs with
1281 * plain cdroms.)]
1282 */
1283 CFMutableDictionaryRef RefMatchingDict = IOServiceMatching("IODVDServices");
1284 AssertReturn(RefMatchingDict, NULL);
1285
1286 /*
1287 * Perform the search and get a collection of DVD services.
1288 */
1289 io_iterator_t DVDServices = NULL;
1290 IOReturn rc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &DVDServices);
1291 AssertMsgReturn(rc == kIOReturnSuccess, ("rc=%d\n", rc), NULL);
1292 RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
1293
1294 /*
1295 * Enumerate the DVD services.
1296 * (This enumeration must be identical to the one performed in DrvHostBase.cpp.)
1297 */
1298 PDARWINDVD pHead = NULL;
1299 PDARWINDVD pTail = NULL;
1300 unsigned i = 0;
1301 io_object_t DVDService;
1302 while ((DVDService = IOIteratorNext(DVDServices)) != 0)
1303 {
1304 DARWIN_IOKIT_DUMP_OBJ(DVDService);
1305
1306 /*
1307 * Get the properties we use to identify the DVD drive.
1308 *
1309 * While there is a (weird 12 byte) GUID, it isn't persistent
1310 * accross boots. So, we have to use a combination of the
1311 * vendor name and product name properties with an optional
1312 * sequence number for identification.
1313 */
1314 CFMutableDictionaryRef PropsRef = 0;
1315 kern_return_t krc = IORegistryEntryCreateCFProperties(DVDService, &PropsRef, kCFAllocatorDefault, kNilOptions);
1316 if (krc == KERN_SUCCESS)
1317 {
1318 /* Get the Device Characteristics dictionary. */
1319 CFDictionaryRef DevCharRef = (CFDictionaryRef)CFDictionaryGetValue(PropsRef, CFSTR(kIOPropertyDeviceCharacteristicsKey));
1320 if (DevCharRef)
1321 {
1322 /* The vendor name. */
1323 char szVendor[128];
1324 char *pszVendor = &szVendor[0];
1325 CFTypeRef ValueRef = CFDictionaryGetValue(DevCharRef, CFSTR(kIOPropertyVendorNameKey));
1326 if ( ValueRef
1327 && CFGetTypeID(ValueRef) == CFStringGetTypeID()
1328 && CFStringGetCString((CFStringRef)ValueRef, szVendor, sizeof(szVendor), kCFStringEncodingUTF8))
1329 pszVendor = RTStrStrip(szVendor);
1330 else
1331 *pszVendor = '\0';
1332
1333 /* The product name. */
1334 char szProduct[128];
1335 char *pszProduct = &szProduct[0];
1336 ValueRef = CFDictionaryGetValue(DevCharRef, CFSTR(kIOPropertyProductNameKey));
1337 if ( ValueRef
1338 && CFGetTypeID(ValueRef) == CFStringGetTypeID()
1339 && CFStringGetCString((CFStringRef)ValueRef, szProduct, sizeof(szProduct), kCFStringEncodingUTF8))
1340 pszProduct = RTStrStrip(szProduct);
1341 else
1342 *pszProduct = '\0';
1343
1344 /* Construct the name and check for duplicates. */
1345 char szName[256 + 32];
1346 if (*pszVendor || *pszProduct)
1347 {
1348 if (*pszVendor && *pszProduct)
1349 RTStrPrintf(szName, sizeof(szName), "%s %s", pszVendor, pszProduct);
1350 else
1351 strcpy(szName, *pszVendor ? pszVendor : pszProduct);
1352
1353 for (PDARWINDVD pCur = pHead; pCur; pCur = pCur->pNext)
1354 {
1355 if (!strcmp(szName, pCur->szName))
1356 {
1357 if (*pszVendor && *pszProduct)
1358 RTStrPrintf(szName, sizeof(szName), "%s %s (#%u)", pszVendor, pszProduct, i);
1359 else
1360 RTStrPrintf(szName, sizeof(szName), "%s %s (#%u)", *pszVendor ? pszVendor : pszProduct, i);
1361 break;
1362 }
1363 }
1364 }
1365 else
1366 RTStrPrintf(szName, sizeof(szName), "(#%u)", i);
1367
1368 /* Create the device. */
1369 size_t cbName = strlen(szName) + 1;
1370 PDARWINDVD pNew = (PDARWINDVD)RTMemAlloc(RT_OFFSETOF(DARWINDVD, szName[cbName]));
1371 if (pNew)
1372 {
1373 pNew->pNext = NULL;
1374 memcpy(pNew->szName, szName, cbName);
1375 if (pTail)
1376 pTail = pTail->pNext = pNew;
1377 else
1378 pTail = pHead = pNew;
1379 }
1380 }
1381 CFRelease(PropsRef);
1382 }
1383 else
1384 AssertMsgFailed(("krc=%#x\n", krc));
1385
1386 IOObjectRelease(DVDService);
1387 i++;
1388 }
1389
1390 IOObjectRelease(DVDServices);
1391
1392 return pHead;
1393}
1394
1395
1396/**
1397 * Enumerate the ethernet capable network devices returning a FIFO of them.
1398 *
1399 * @returns Pointer to the head.
1400 */
1401PDARWINETHERNIC DarwinGetEthernetControllers(void)
1402{
1403 AssertReturn(darwinOpenMasterPort(), NULL);
1404
1405 /*
1406 * Create a matching dictionary for searching for ethernet controller
1407 * services in the IOKit.
1408 *
1409 * For some really stupid reason I don't get all the controllers if I look for
1410 * objects that are instances of IOEthernetController or its decendants (only
1411 * get the AirPort on my mac pro). But fortunately using IOEthernetInterface
1412 * seems to work. Weird s**t!
1413 */
1414 //CFMutableDictionaryRef RefMatchingDict = IOServiceMatching("IOEthernetController"); - this doesn't work :-(
1415 CFMutableDictionaryRef RefMatchingDict = IOServiceMatching("IOEthernetInterface");
1416 AssertReturn(RefMatchingDict, NULL);
1417
1418 /*
1419 * Perform the search and get a collection of ethernet controller services.
1420 */
1421 io_iterator_t EtherIfServices = NULL;
1422 IOReturn rc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &EtherIfServices);
1423 AssertMsgReturn(rc == kIOReturnSuccess, ("rc=%d\n", rc), NULL);
1424 RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
1425
1426 /*
1427 * Enumerate the ethernet controller services.
1428 */
1429 PDARWINETHERNIC pHead = NULL;
1430 PDARWINETHERNIC pTail = NULL;
1431 unsigned acCategories[8] = { 0,0,0,0, 0,0,0,0 };
1432 io_object_t EtherIfService;
1433 while ((EtherIfService = IOIteratorNext(EtherIfServices)) != 0)
1434 {
1435 /*
1436 * Dig up the parent, meaning the IOEthernetController.
1437 */
1438 io_object_t EtherNICService;
1439 kern_return_t krc = IORegistryEntryGetParentEntry(EtherIfService, kIOServicePlane, &EtherNICService);
1440 /*krc = IORegistryEntryGetChildEntry(EtherNICService, kIOServicePlane, &EtherIfService); */
1441 if (krc == KERN_SUCCESS)
1442 {
1443 DARWIN_IOKIT_DUMP_OBJ(EtherNICService);
1444 /*
1445 * Get the properties we use to identify and name the Ethernet NIC.
1446 * We need the both the IOEthernetController and it's IONetworkInterface child.
1447 */
1448 CFMutableDictionaryRef PropsRef = 0;
1449 krc = IORegistryEntryCreateCFProperties(EtherNICService, &PropsRef, kCFAllocatorDefault, kNilOptions);
1450 if (krc == KERN_SUCCESS)
1451 {
1452 CFMutableDictionaryRef IfPropsRef = 0;
1453 kern_return_t krc = IORegistryEntryCreateCFProperties(EtherIfService, &IfPropsRef, kCFAllocatorDefault, kNilOptions);
1454 if (krc == KERN_SUCCESS)
1455 {
1456 /*
1457 * Gather the required data.
1458 * We'll create a UUID from the MAC address and the BSD name.
1459 */
1460 char szTmp[256];
1461 do
1462 {
1463 /* Check if airport (a bit heuristical - it's com.apple.driver.AirPortBrcm43xx here). */
1464 darwinDictGetString(PropsRef, CFSTR("CFBundleIdentifier"), szTmp, sizeof(szTmp));
1465 bool fWireless;
1466 bool fAirPort = fWireless = strstr(szTmp, ".AirPort") != NULL;
1467
1468 /* Check if it's USB. */
1469 darwinDictGetString(PropsRef, CFSTR("IOProviderClass"), szTmp, sizeof(szTmp));
1470 bool fUSB = strstr(szTmp, "USB") != NULL;
1471
1472
1473 /* Is it builtin? */
1474 bool fBuiltin;
1475 darwinDictGetBool(IfPropsRef, CFSTR("IOBuiltin"), &fBuiltin);
1476
1477 /* Is it the primary interface */
1478 bool fPrimaryIf;
1479 darwinDictGetBool(IfPropsRef, CFSTR("IOPrimaryInterface"), &fPrimaryIf);
1480
1481 /* Get the MAC address. */
1482 PDMMAC Mac;
1483 AssertBreak(darwinDictGetData(PropsRef, CFSTR("IOMACAddress"), &Mac, sizeof(Mac)));
1484
1485 /* The BSD Name from the interface dictionary. */
1486 char szBSDName[RT_SIZEOFMEMB(DARWINETHERNIC, szBSDName)];
1487 AssertBreak(darwinDictGetString(IfPropsRef, CFSTR("BSD Name"), szBSDName, sizeof(szBSDName)));
1488
1489 /* Check if it's really wireless. */
1490 if ( darwinDictIsPresent(IfPropsRef, CFSTR("IO80211CountryCode"))
1491 || darwinDictIsPresent(IfPropsRef, CFSTR("IO80211DriverVersion"))
1492 || darwinDictIsPresent(IfPropsRef, CFSTR("IO80211HardwareVersion"))
1493 || darwinDictIsPresent(IfPropsRef, CFSTR("IO80211Locale")))
1494 fWireless = true;
1495 else
1496 fAirPort = fWireless = false;
1497
1498 /** @todo IOPacketFilters / IONetworkFilterGroup? */
1499
1500 /*
1501 * Create a base name for it, we'll sort it later and add numbers where required.
1502 * Note! The ConsoleImpl2.cpp code ASSUMES things about the name. It is also
1503 * stored in the VM config files. (really bright idea)
1504 */
1505 /** @todo not sure about "Wireless" here, none of my wireless sticks work on the mac. */
1506 size_t cchName = RTStrPrintf(szTmp, sizeof(szTmp), "%s: %s%s",
1507 szBSDName,
1508 fUSB ? "USB " : "",
1509 fWireless ? fAirPort ? "AirPort " : "Wireless" : "Ethernet");
1510 int iCat = fUSB * 4 | fWireless * 2 | fAirPort;
1511 acCategories[iCat]++;
1512
1513 /*
1514 * Create the list entry.
1515 */
1516 DARWIN_IOKIT_LOG(("Found: if=%s mac=%.6Rhxs fWireless=%RTbool fAirPort=%RTbool fBuiltin=%RTbool fPrimaryIf=%RTbool fUSB=%RTbool\n",
1517 szBSDName, &Mac, fWireless, fAirPort, fBuiltin, fPrimaryIf, fUSB));
1518
1519 PDARWINETHERNIC pNew = (PDARWINETHERNIC)RTMemAlloc(RT_OFFSETOF(DARWINETHERNIC, szName[cchName + 8])); /* extra for the number */
1520 if (pNew)
1521 {
1522 strncpy(pNew->szBSDName, szBSDName, sizeof(pNew->szBSDName)); /* the '\0' padding is intentional! */
1523
1524 RTUuidClear(&pNew->Uuid);
1525 memcpy(&pNew->Uuid, pNew->szBSDName, RT_MIN(sizeof(pNew->szBSDName), sizeof(pNew->Uuid)));
1526 pNew->Uuid.Gen.u16ClockSeq = (pNew->Uuid.Gen.u16ClockSeq & 0x3fff) | 0x8000;
1527 pNew->Uuid.Gen.u16TimeHiAndVersion = (pNew->Uuid.Gen.u16TimeHiAndVersion & 0x0fff) | 0x4000;
1528 pNew->Uuid.Gen.au8Node[0] = Mac.au8[0];
1529 pNew->Uuid.Gen.au8Node[1] = Mac.au8[1];
1530 pNew->Uuid.Gen.au8Node[2] = Mac.au8[2];
1531 pNew->Uuid.Gen.au8Node[3] = Mac.au8[3];
1532 pNew->Uuid.Gen.au8Node[4] = Mac.au8[4];
1533 pNew->Uuid.Gen.au8Node[5] = Mac.au8[5];
1534
1535 pNew->Mac = Mac;
1536 pNew->iCat = iCat;
1537 pNew->fWireless = fWireless;
1538 pNew->fAirPort = fAirPort;
1539 pNew->fBuiltin = fBuiltin;
1540 pNew->fUSB = fUSB;
1541 pNew->fPrimaryIf = fPrimaryIf;
1542 memcpy(pNew->szName, szTmp, cchName + 1);
1543
1544 /*
1545 * Link it into the list, keep the list sorted by the BSD name.
1546 */
1547 if (pTail)
1548 {
1549 PDARWINETHERNIC pPrev = pTail;
1550 if (strcmp(pNew->szBSDName, pPrev->szBSDName) < 0)
1551 {
1552 pPrev = NULL;
1553 for (PDARWINETHERNIC pCur = pHead; pCur; pPrev = pCur, pCur = pCur->pNext)
1554 if (strcmp(pNew->szBSDName, pCur->szBSDName) >= 0)
1555 break;
1556 Assert(pPrev);
1557 }
1558 pNew->pNext = pPrev->pNext;
1559 pPrev->pNext = pNew;
1560 if (pPrev == pTail)
1561 pTail = pNew;
1562 }
1563 else
1564 {
1565 pNew->pNext = NULL;
1566 pTail = pHead = pNew;
1567 }
1568 }
1569 } while (0);
1570
1571 CFRelease(IfPropsRef);
1572 }
1573 CFRelease(PropsRef);
1574 }
1575 IOObjectRelease(EtherNICService);
1576 }
1577 else
1578 AssertMsgFailed(("krc=%#x\n", krc));
1579 IOObjectRelease(EtherIfService);
1580 }
1581
1582 IOObjectRelease(EtherIfServices);
1583
1584 /*
1585 * Add numbers if required.
1586 */
1587 if ( acCategories[0] > 1
1588 || acCategories[1] > 1
1589 || acCategories[2] > 1
1590 || acCategories[3] > 1
1591 || acCategories[4] > 1
1592 || acCategories[5] > 1
1593 || acCategories[6] > 1
1594 || acCategories[7] > 1)
1595 {
1596 unsigned aiCategories[8] = { 0,0,0,0, 0,0,0,0 };
1597 for (PDARWINETHERNIC pCur = pHead; pCur; pCur = pCur->pNext)
1598 {
1599 aiCategories[pCur->iCat]++;
1600 if (acCategories[pCur->iCat] > 1)
1601 RTStrPrintf(strchr(pCur->szName, '\0'), 7, " %u", aiCategories[pCur->iCat]);
1602 }
1603 }
1604
1605 return pHead;
1606}
1607
1608#ifdef STANDALONE_TESTCASE
1609/**
1610 * This file can optionally be compiled into a testcase, this is the main function.
1611 * To build:
1612 * g++ -I ../../../../include -D IN_RING3 iokit.cpp ../../../../out/darwin.x86/debug/lib/RuntimeR3.a ../../../../out/darwin.x86/debug/lib/SUPR3.a ../../../../out/darwin.x86/debug/lib/RuntimeR3.a ../../../../out/darwin.x86/debug/lib/VBox-kStuff.a ../../../../out/darwin.x86/debug/lib/RuntimeR3.a -framework CoreFoundation -framework IOKit -liconv -D STANDALONE_TESTCASE
1613 */
1614int main(int argc, char **argv)
1615{
1616 RTR3Init(false);
1617
1618 /*
1619 * Get and display the ethernet controllers.
1620 */
1621 RTPrintf("Ethernet controllers:\n");
1622 PDARWINETHERNIC pEtherNICs = DarwinGetEthernetControllers();
1623 for (PDARWINETHERNIC pCur = pEtherNICs; pCur; pCur = pCur->pNext)
1624 {
1625 RTPrintf("%s\n", pCur->szName);
1626 RTPrintf(" szBSDName=%d\n", pCur->szBSDName);
1627 RTPrintf(" UUID=%RTuuid\n", &pCur->Uuid);
1628 RTPrintf(" Mac=%.6Rhxs\n", &pCur->Mac);
1629 RTPrintf(" fWireless=%RTbool\n", pCur->fWireless);
1630 RTPrintf(" fAirPort=%RTbool\n", pCur->fAirPort);
1631 RTPrintf(" fBuiltin=%RTbool\n", pCur->fBuiltin);
1632 RTPrintf(" fUSB=%RTbool\n", pCur->fUSB);
1633 RTPrintf(" fPrimaryIf=%RTbool\n", pCur->fPrimaryIf);
1634 }
1635
1636
1637
1638 return 0;
1639}
1640#endif
1641
1642
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