VirtualBox

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

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

Main/IOKit: Fixes for El Capitan, use the new IOUSBHostDevice class to get access to all required data like checking whether a USB MSD is mounted on the host and marking it as unavailable

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