VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/darwin/iokit.cpp@ 59436

Last change on this file since 59436 was 59436, checked in by vboxsync, 9 years ago

iokit.cpp: Nits, try use IO_OBJECT_NULL.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette