VirtualBox

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

Last change on this file since 78273 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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

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