VirtualBox

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

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

D'oh

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