VirtualBox

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

Last change on this file since 98110 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

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

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