VirtualBox

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

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

USB: Purge strings.

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