VirtualBox

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

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

Main/iokit.cpp: DarwinSubscribeUSBNotifications was missing a return code check in the last IOServiceAddMatchingNotification call. shouldn't break anything, but you know... rc->hrc/vrc/krc. bugref:10223

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 79.1 KB
Line 
1/* $Id: iokit.cpp 98287 2023-01-24 13:40:47Z 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 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, &szVersion[0], sizeof(szVersion));
129 if (RT_SUCCESS(vrc))
130 {
131 vrc = RTStrToUInt32Ex(&szVersion[0], NULL, 10, &g_uMajorDarwin);
132 AssertLogRelMsg(vrc == VINF_SUCCESS || vrc == VWRN_TRAILING_CHARS,
133 ("Failed to convert the major part of the version string '%s' into an integer: %Rrc\n",
134 szVersion, vrc));
135 }
136 else
137 AssertLogRelMsgFailed(("Failed to query the OS release version with %Rrc\n", vrc));
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 krc = IOServiceAddMatchingNotification(pNotify->NotifyPort,
662 kIOPublishNotification,
663 IOServiceMatching(kIOUSBDeviceClassName),
664 darwinUSBAttachNotification1,
665 pNotify,
666 &pNotify->AttachIterator);
667 if (krc == KERN_SUCCESS)
668 {
669 darwinDrainIterator(pNotify->AttachIterator);
670 krc = IOServiceAddMatchingNotification(pNotify->NotifyPort,
671 kIOMatchedNotification,
672 IOServiceMatching(kIOUSBDeviceClassName),
673 darwinUSBAttachNotification2,
674 pNotify,
675 &pNotify->AttachIterator2);
676 if (krc == KERN_SUCCESS)
677 {
678 darwinDrainIterator(pNotify->AttachIterator2);
679 krc = IOServiceAddMatchingNotification(pNotify->NotifyPort,
680 kIOTerminatedNotification,
681 IOServiceMatching(kIOUSBDeviceClassName),
682 darwinUSBDetachNotification,
683 pNotify,
684 &pNotify->DetachIterator);
685 if (krc == KERN_SUCCESS)
686 {
687 darwinDrainIterator(pNotify->DetachIterator);
688 return pNotify;
689 }
690 IOObjectRelease(pNotify->AttachIterator2);
691 }
692 IOObjectRelease(pNotify->AttachIterator);
693 }
694 CFRunLoopRemoveSource(RunLoopRef, pNotify->NotifyRLSrc, CFSTR(VBOX_IOKIT_MODE_STRING));
695 }
696 IONotificationPortDestroy(pNotify->NotifyPort);
697 }
698
699 RTMemFree(pNotify);
700 return NULL;
701}
702
703
704/**
705 * Unsubscribe the run loop from USB notification subscribed to
706 * by DarwinSubscribeUSBNotifications.
707 *
708 * @param pvOpaque The return value from DarwinSubscribeUSBNotifications.
709 */
710void DarwinUnsubscribeUSBNotifications(void *pvOpaque)
711{
712 PDARWINUSBNOTIFY pNotify = (PDARWINUSBNOTIFY)pvOpaque;
713 if (!pNotify)
714 return;
715
716 IOObjectRelease(pNotify->AttachIterator);
717 pNotify->AttachIterator = IO_OBJECT_NULL;
718 IOObjectRelease(pNotify->AttachIterator2);
719 pNotify->AttachIterator2 = IO_OBJECT_NULL;
720 IOObjectRelease(pNotify->DetachIterator);
721 pNotify->DetachIterator = IO_OBJECT_NULL;
722
723 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), pNotify->NotifyRLSrc, CFSTR(VBOX_IOKIT_MODE_STRING));
724 IONotificationPortDestroy(pNotify->NotifyPort);
725 pNotify->NotifyRLSrc = NULL;
726 pNotify->NotifyPort = NULL;
727
728 RTMemFree(pNotify);
729}
730
731
732/**
733 * Descends recursively into a IORegistry tree locating the first object of a given class.
734 *
735 * The search is performed depth first.
736 *
737 * @returns Object reference if found, NULL if not.
738 * @param Object The current tree root.
739 * @param pszClass The name of the class we're looking for.
740 * @param pszNameBuf A scratch buffer for query the class name in to avoid
741 * wasting 128 bytes on an io_name_t object for every recursion.
742 */
743static io_object_t darwinFindObjectByClass(io_object_t Object, const char *pszClass, io_name_t pszNameBuf)
744{
745 io_iterator_t Children;
746 kern_return_t krc = IORegistryEntryGetChildIterator(Object, kIOServicePlane, &Children);
747 if (krc != KERN_SUCCESS)
748 return IO_OBJECT_NULL;
749 io_object_t Child;
750 while ((Child = IOIteratorNext(Children)) != IO_OBJECT_NULL)
751 {
752 krc = IOObjectGetClass(Child, pszNameBuf);
753 if ( krc == KERN_SUCCESS
754 && !strcmp(pszNameBuf, pszClass))
755 break;
756
757 io_object_t GrandChild = darwinFindObjectByClass(Child, pszClass, pszNameBuf);
758 IOObjectRelease(Child);
759 if (GrandChild)
760 {
761 Child = GrandChild;
762 break;
763 }
764 }
765 IOObjectRelease(Children);
766 return Child;
767}
768
769
770/**
771 * Descends recursively into IOUSBMassStorageClass tree to check whether
772 * the MSD is mounted or not.
773 *
774 * The current heuristic is to look for the IOMedia class.
775 *
776 * @returns true if mounted, false if not.
777 * @param MSDObj The IOUSBMassStorageClass object.
778 * @param pszNameBuf A scratch buffer for query the class name in to avoid
779 * wasting 128 bytes on an io_name_t object for every recursion.
780 */
781static bool darwinIsMassStorageInterfaceInUse(io_object_t MSDObj, io_name_t pszNameBuf)
782{
783 io_object_t MediaObj = darwinFindObjectByClass(MSDObj, kIOMediaClass, pszNameBuf);
784 if (MediaObj)
785 {
786 CFMutableDictionaryRef pProperties;
787 kern_return_t krc;
788 bool fInUse = true;
789
790 krc = IORegistryEntryCreateCFProperties(MediaObj, &pProperties, kCFAllocatorDefault, kNilOptions);
791 if (krc == KERN_SUCCESS)
792 {
793 CFBooleanRef pBoolValue = (CFBooleanRef)CFDictionaryGetValue(pProperties, CFSTR(kIOMediaOpenKey));
794 if (pBoolValue)
795 fInUse = CFBooleanGetValue(pBoolValue);
796
797 CFRelease(pProperties);
798 }
799
800 /* more checks? */
801 IOObjectRelease(MediaObj);
802 return fInUse;
803 }
804
805 return false;
806}
807
808
809/**
810 * Finds the matching IOUSBHostDevice registry entry for the given legacy USB device interface (IOUSBDevice).
811 *
812 * @returns kern_return_t error code.
813 * @param USBDeviceLegacy The legacy device I/O Kit object.
814 * @param pUSBDevice Where to store the IOUSBHostDevice object on success.
815 */
816static kern_return_t darwinGetUSBHostDeviceFromLegacyDevice(io_object_t USBDeviceLegacy, io_object_t *pUSBDevice)
817{
818 kern_return_t krc = KERN_SUCCESS;
819 uint64_t uIoRegEntryId = 0;
820
821 *pUSBDevice = 0;
822
823 /* Get the registry entry ID to match against. */
824 krc = IORegistryEntryGetRegistryEntryID(USBDeviceLegacy, &uIoRegEntryId);
825 if (krc != KERN_SUCCESS)
826 return krc;
827
828 /*
829 * Create a matching dictionary for searching for USB Devices in the IOKit.
830 */
831 CFMutableDictionaryRef RefMatchingDict = IOServiceMatching(kIOUSBHostDeviceClassName);
832 AssertReturn(RefMatchingDict, KERN_FAILURE);
833
834 /*
835 * Perform the search and get a collection of USB Device back.
836 */
837 io_iterator_t USBDevices = IO_OBJECT_NULL;
838 IOReturn rc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &USBDevices);
839 AssertMsgReturn(rc == kIOReturnSuccess, ("rc=%d\n", rc), KERN_FAILURE);
840 RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
841
842 /*
843 * Walk the devices and check for the matching alternate registry entry ID.
844 */
845 io_object_t USBDevice;
846 while ((USBDevice = IOIteratorNext(USBDevices)) != IO_OBJECT_NULL)
847 {
848 DARWIN_IOKIT_DUMP_OBJ(USBDevice);
849
850 CFMutableDictionaryRef PropsRef = 0;
851 krc = IORegistryEntryCreateCFProperties(USBDevice, &PropsRef, kCFAllocatorDefault, kNilOptions);
852 if (krc == KERN_SUCCESS)
853 {
854 uint64_t uAltRegId = 0;
855 if ( darwinDictGetU64(PropsRef, CFSTR("AppleUSBAlternateServiceRegistryID"), &uAltRegId)
856 && uAltRegId == uIoRegEntryId)
857 {
858 *pUSBDevice = USBDevice;
859 CFRelease(PropsRef);
860 break;
861 }
862
863 CFRelease(PropsRef);
864 }
865 IOObjectRelease(USBDevice);
866 }
867 IOObjectRelease(USBDevices);
868
869 return krc;
870}
871
872
873static bool darwinUSBDeviceIsGrabbedDetermineState(PUSBDEVICE pCur, io_object_t USBDevice)
874{
875 /*
876 * Iterate the interfaces (among the children of the IOUSBDevice object).
877 */
878 io_iterator_t Interfaces;
879 kern_return_t krc = IORegistryEntryGetChildIterator(USBDevice, kIOServicePlane, &Interfaces);
880 if (krc != KERN_SUCCESS)
881 return false;
882
883 bool fHaveOwner = false;
884 RTPROCESS Owner = NIL_RTPROCESS;
885 bool fHaveClient = false;
886 RTPROCESS Client = NIL_RTPROCESS;
887 io_object_t Interface;
888 while ((Interface = IOIteratorNext(Interfaces)) != IO_OBJECT_NULL)
889 {
890 io_name_t szName;
891 krc = IOObjectGetClass(Interface, szName);
892 if ( krc == KERN_SUCCESS
893 && !strcmp(szName, VBOXUSBDEVICE_CLASS_NAME))
894 {
895 CFMutableDictionaryRef PropsRef = 0;
896 krc = IORegistryEntryCreateCFProperties(Interface, &PropsRef, kCFAllocatorDefault, kNilOptions);
897 if (krc == KERN_SUCCESS)
898 {
899 fHaveOwner = darwinDictGetProcess(PropsRef, CFSTR(VBOXUSB_OWNER_KEY), &Owner);
900 fHaveClient = darwinDictGetProcess(PropsRef, CFSTR(VBOXUSB_CLIENT_KEY), &Client);
901 CFRelease(PropsRef);
902 }
903 }
904
905 IOObjectRelease(Interface);
906 }
907 IOObjectRelease(Interfaces);
908
909 /*
910 * Calc the status.
911 */
912 if (fHaveOwner)
913 {
914 if (Owner == RTProcSelf())
915 pCur->enmState = !fHaveClient || Client == NIL_RTPROCESS || !Client
916 ? USBDEVICESTATE_HELD_BY_PROXY
917 : USBDEVICESTATE_USED_BY_GUEST;
918 else
919 pCur->enmState = USBDEVICESTATE_USED_BY_HOST;
920 }
921
922 return fHaveOwner;
923}
924
925
926/**
927 * Worker for determining the USB device state for devices which are not captured by the VBoxUSB driver
928 * Works for both, IOUSBDevice (legacy on release >= El Capitan) and IOUSBHostDevice (available on >= El Capitan).
929 *
930 * @returns nothing.
931 * @param pCur The USB device data.
932 * @param USBDevice I/O Kit USB device object (either IOUSBDevice or IOUSBHostDevice).
933 */
934static void darwinDetermineUSBDeviceStateWorker(PUSBDEVICE pCur, io_object_t USBDevice)
935{
936 /*
937 * Iterate the interfaces (among the children of the IOUSBDevice object).
938 */
939 io_iterator_t Interfaces;
940 kern_return_t krc = IORegistryEntryGetChildIterator(USBDevice, kIOServicePlane, &Interfaces);
941 if (krc != KERN_SUCCESS)
942 return;
943
944 bool fUserClientOnly = true;
945 bool fConfigured = false;
946 bool fInUse = false;
947 bool fSeizable = true;
948 io_object_t Interface;
949 while ((Interface = IOIteratorNext(Interfaces)) != IO_OBJECT_NULL)
950 {
951 io_name_t szName;
952 krc = IOObjectGetClass(Interface, szName);
953 if ( krc == KERN_SUCCESS
954 && ( !strcmp(szName, "IOUSBInterface")
955 || !strcmp(szName, "IOUSBHostInterface")))
956 {
957 fConfigured = true;
958
959 /*
960 * Iterate the interface children looking for stuff other than
961 * IOUSBUserClientInit objects.
962 */
963 io_iterator_t Children1;
964 krc = IORegistryEntryGetChildIterator(Interface, kIOServicePlane, &Children1);
965 if (krc == KERN_SUCCESS)
966 {
967 io_object_t Child1;
968 while ((Child1 = IOIteratorNext(Children1)) != IO_OBJECT_NULL)
969 {
970 krc = IOObjectGetClass(Child1, szName);
971 if ( krc == KERN_SUCCESS
972 && strcmp(szName, "IOUSBUserClientInit"))
973 {
974 fUserClientOnly = false;
975
976 if ( !strcmp(szName, "IOUSBMassStorageClass")
977 || !strcmp(szName, "IOUSBMassStorageInterfaceNub"))
978 {
979 /* Only permit capturing MSDs that aren't mounted, at least
980 until the GUI starts poping up warnings about data loss
981 and such when capturing a busy device. */
982 fSeizable = false;
983 fInUse |= darwinIsMassStorageInterfaceInUse(Child1, szName);
984 }
985 else if (!strcmp(szName, "IOUSBHIDDriver")
986 || !strcmp(szName, "AppleHIDMouse")
987 /** @todo more? */)
988 {
989 /* For now, just assume that all HID devices are inaccessible
990 because of the greedy HID service. */
991 fSeizable = false;
992 fInUse = true;
993 }
994 else
995 fInUse = true;
996 }
997 IOObjectRelease(Child1);
998 }
999 IOObjectRelease(Children1);
1000 }
1001 }
1002
1003 IOObjectRelease(Interface);
1004 }
1005 IOObjectRelease(Interfaces);
1006
1007 /*
1008 * Calc the status.
1009 */
1010 if (!fInUse)
1011 pCur->enmState = USBDEVICESTATE_UNUSED;
1012 else
1013 pCur->enmState = fSeizable
1014 ? USBDEVICESTATE_USED_BY_HOST_CAPTURABLE
1015 : USBDEVICESTATE_USED_BY_HOST;
1016}
1017
1018
1019/**
1020 * Worker function for DarwinGetUSBDevices() that tries to figure out
1021 * what state the device is in and set enmState.
1022 *
1023 * This is mostly a matter of distinguishing between devices that nobody
1024 * uses, devices that can be seized and devices that cannot be grabbed.
1025 *
1026 * @param pCur The USB device data.
1027 * @param USBDevice The USB device object.
1028 * @param PropsRef The USB device properties.
1029 */
1030static void darwinDeterminUSBDeviceState(PUSBDEVICE pCur, io_object_t USBDevice, CFMutableDictionaryRef /* PropsRef */)
1031{
1032
1033 if (!darwinUSBDeviceIsGrabbedDetermineState(pCur, USBDevice))
1034 {
1035 /*
1036 * The USB stack was completely reworked on El Capitan and the IOUSBDevice and IOUSBInterface
1037 * are deprecated and don't return the information required for the additional checks below.
1038 * We also can't directly make use of the new classes (IOUSBHostDevice and IOUSBHostInterface)
1039 * because VBoxUSB only exposes the legacy interfaces. Trying to use the new classes results in errors
1040 * because the I/O Kit USB library wants to use the new interfaces. The result is us losing the device
1041 * form the list when VBoxUSB has attached to the USB device.
1042 *
1043 * To make the checks below work we have to get hold of the IOUSBHostDevice and IOUSBHostInterface
1044 * instances for the current device. Fortunately the IOUSBHostDevice instance contains a
1045 * "AppleUSBAlternateServiceRegistryID" which points to the legacy class instance for the same device.
1046 * So just iterate over the list of IOUSBHostDevice instances and check whether the
1047 * AppleUSBAlternateServiceRegistryID property matches with the legacy instance.
1048 *
1049 * The upside is that we can keep VBoxUSB untouched and still compatible with older OS X releases.
1050 */
1051 if (g_uMajorDarwin >= VBOX_OSX_EL_CAPTIAN_VER)
1052 {
1053 io_object_t IOUSBDeviceNew = IO_OBJECT_NULL;
1054 kern_return_t krc = darwinGetUSBHostDeviceFromLegacyDevice(USBDevice, &IOUSBDeviceNew);
1055 if ( krc == KERN_SUCCESS
1056 && IOUSBDeviceNew != IO_OBJECT_NULL)
1057 {
1058 darwinDetermineUSBDeviceStateWorker(pCur, IOUSBDeviceNew);
1059 IOObjectRelease(IOUSBDeviceNew);
1060 }
1061 }
1062 else
1063 darwinDetermineUSBDeviceStateWorker(pCur, USBDevice);
1064 }
1065}
1066
1067
1068/**
1069 * Enumerate the USB devices returning a FIFO of them.
1070 *
1071 * @returns Pointer to the head.
1072 * USBProxyService::freeDevice is expected to free each of the list elements.
1073 */
1074PUSBDEVICE DarwinGetUSBDevices(void)
1075{
1076 AssertReturn(darwinOpenMasterPort(), NULL);
1077 //DARWIN_IOKIT_LOG(("DarwinGetUSBDevices\n"));
1078
1079 /*
1080 * Create a matching dictionary for searching for USB Devices in the IOKit.
1081 */
1082 CFMutableDictionaryRef RefMatchingDict = IOServiceMatching(kIOUSBDeviceClassName);
1083 AssertReturn(RefMatchingDict, NULL);
1084
1085 /*
1086 * Perform the search and get a collection of USB Device back.
1087 */
1088 io_iterator_t USBDevices = IO_OBJECT_NULL;
1089 IOReturn rc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &USBDevices);
1090 AssertMsgReturn(rc == kIOReturnSuccess, ("rc=%d\n", rc), NULL);
1091 RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
1092
1093 /*
1094 * Enumerate the USB Devices.
1095 */
1096 PUSBDEVICE pHead = NULL;
1097 PUSBDEVICE pTail = NULL;
1098 unsigned i = 0;
1099 io_object_t USBDevice;
1100 while ((USBDevice = IOIteratorNext(USBDevices)) != IO_OBJECT_NULL)
1101 {
1102 DARWIN_IOKIT_DUMP_OBJ(USBDevice);
1103
1104 /*
1105 * Query the device properties from the registry.
1106 *
1107 * We could alternatively use the device and such, but that will be
1108 * slower and we would have to resort to the registry for the three
1109 * string anyway.
1110 */
1111 CFMutableDictionaryRef PropsRef = 0;
1112 kern_return_t krc = IORegistryEntryCreateCFProperties(USBDevice, &PropsRef, kCFAllocatorDefault, kNilOptions);
1113 if (krc == KERN_SUCCESS)
1114 {
1115 bool fOk = false;
1116 PUSBDEVICE pCur = (PUSBDEVICE)RTMemAllocZ(sizeof(*pCur));
1117 do /* loop for breaking out of on failure. */
1118 {
1119 AssertBreak(pCur);
1120
1121 /*
1122 * Mandatory
1123 */
1124 pCur->bcdUSB = 0; /* we've no idea. */
1125 pCur->enmState = USBDEVICESTATE_USED_BY_HOST_CAPTURABLE; /* just a default, we'll try harder in a bit. */
1126
1127 /* Skip hubs. On 10.11 beta 3, the root hub simulations does not have a USBDeviceClass property, so
1128 simply ignore failures to retrieve it. */
1129 if (!darwinDictGetU8(PropsRef, CFSTR(kUSBDeviceClass), &pCur->bDeviceClass))
1130 {
1131#ifdef VBOX_STRICT
1132 char szTmp[80];
1133 Assert( darwinDictGetString(PropsRef, CFSTR("IOClassNameOverride"), szTmp, sizeof(szTmp))
1134 && strcmp(szTmp, "IOUSBRootHubDevice") == 0);
1135#endif
1136 break;
1137 }
1138 if (pCur->bDeviceClass == 0x09 /* hub, find a define! */)
1139 break;
1140 AssertBreak(darwinDictGetU8(PropsRef, CFSTR(kUSBDeviceSubClass), &pCur->bDeviceSubClass));
1141 AssertBreak(darwinDictGetU8(PropsRef, CFSTR(kUSBDeviceProtocol), &pCur->bDeviceProtocol));
1142 AssertBreak(darwinDictGetU16(PropsRef, CFSTR(kUSBVendorID), &pCur->idVendor));
1143 AssertBreak(darwinDictGetU16(PropsRef, CFSTR(kUSBProductID), &pCur->idProduct));
1144 AssertBreak(darwinDictGetU16(PropsRef, CFSTR(kUSBDeviceReleaseNumber), &pCur->bcdDevice));
1145 uint32_t u32LocationId;
1146 AssertBreak(darwinDictGetU32(PropsRef, CFSTR(kUSBDevicePropertyLocationID), &u32LocationId));
1147 uint64_t u64SessionId;
1148 AssertBreak(darwinDictGetU64(PropsRef, CFSTR("sessionID"), &u64SessionId));
1149 char szAddress[64];
1150 RTStrPrintf(szAddress, sizeof(szAddress), "p=0x%04RX16;v=0x%04RX16;s=0x%016RX64;l=0x%08RX32",
1151 pCur->idProduct, pCur->idVendor, u64SessionId, u32LocationId);
1152 pCur->pszAddress = RTStrDup(szAddress);
1153 AssertBreak(pCur->pszAddress);
1154 pCur->bBus = u32LocationId >> 24;
1155 darwinDictGetU8(PropsRef, CFSTR("PortNum"), &pCur->bPort); /* Not present in 10.11 beta 3, so ignore failure. (Is set to zero.) */
1156 uint8_t bSpeed;
1157 AssertBreak(darwinDictGetU8(PropsRef, CFSTR(kUSBDevicePropertySpeed), &bSpeed));
1158 Assert(bSpeed <= 3);
1159 pCur->enmSpeed = bSpeed == 3 ? USBDEVICESPEED_SUPER
1160 : bSpeed == 2 ? USBDEVICESPEED_HIGH
1161 : bSpeed == 1 ? USBDEVICESPEED_FULL
1162 : bSpeed == 0 ? USBDEVICESPEED_LOW
1163 : USBDEVICESPEED_UNKNOWN;
1164
1165 /*
1166 * Optional.
1167 * There are some nameless device in the iMac, apply names to them.
1168 */
1169 darwinDictDupString(PropsRef, CFSTR("USB Vendor Name"), (char **)&pCur->pszManufacturer);
1170 if ( !pCur->pszManufacturer
1171 && pCur->idVendor == kIOUSBVendorIDAppleComputer)
1172 pCur->pszManufacturer = RTStrDup("Apple Computer, Inc.");
1173 darwinDictDupString(PropsRef, CFSTR("USB Product Name"), (char **)&pCur->pszProduct);
1174 if ( !pCur->pszProduct
1175 && pCur->bDeviceClass == 224 /* Wireless */
1176 && pCur->bDeviceSubClass == 1 /* Radio Frequency */
1177 && pCur->bDeviceProtocol == 1 /* Bluetooth */)
1178 pCur->pszProduct = RTStrDup("Bluetooth");
1179 darwinDictDupString(PropsRef, CFSTR("USB Serial Number"), (char **)&pCur->pszSerialNumber);
1180
1181 pCur->pszBackend = RTStrDup("host");
1182 AssertBreak(pCur->pszBackend);
1183
1184#if 0 /* leave the remainder as zero for now. */
1185 /*
1186 * Create a plugin interface for the service and query its USB Device interface.
1187 */
1188 SInt32 Score = 0;
1189 IOCFPlugInInterface **ppPlugInInterface = NULL;
1190 rc = IOCreatePlugInInterfaceForService(USBDevice, kIOUSBDeviceUserClientTypeID,
1191 kIOCFPlugInInterfaceID, &ppPlugInInterface, &Score);
1192 if (rc == kIOReturnSuccess)
1193 {
1194 IOUSBDeviceInterface245 **ppUSBDevI = NULL;
1195 HRESULT hrc = (*ppPlugInInterface)->QueryInterface(ppPlugInInterface,
1196 CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID245),
1197 (LPVOID *)&ppUSBDevI);
1198 rc = IODestroyPlugInInterface(ppPlugInInterface); Assert(rc == kIOReturnSuccess);
1199 ppPlugInInterface = NULL;
1200 if (hrc == S_OK)
1201 {
1202 /** @todo enumerate configurations and interfaces if we actually need them. */
1203 //IOReturn (*GetNumberOfConfigurations)(void *self, UInt8 *numConfig);
1204 //IOReturn (*GetConfigurationDescriptorPtr)(void *self, UInt8 configIndex, IOUSBConfigurationDescriptorPtr *desc);
1205 //IOReturn (*CreateInterfaceIterator)(void *self, IOUSBFindInterfaceRequest *req, io_iterator_t *iter);
1206 }
1207 long cReft = (*ppUSBDeviceInterface)->Release(ppUSBDeviceInterface); MY_CHECK_CREFS(cRefs);
1208 }
1209#endif
1210 /*
1211 * Try determine the state.
1212 */
1213 darwinDeterminUSBDeviceState(pCur, USBDevice, PropsRef);
1214
1215 /*
1216 * We're good. Link the device.
1217 */
1218 pCur->pPrev = pTail;
1219 if (pTail)
1220 pTail = pTail->pNext = pCur;
1221 else
1222 pTail = pHead = pCur;
1223 fOk = true;
1224 } while (0);
1225
1226 /* cleanup on failure / skipped device. */
1227 if (!fOk && pCur)
1228 DarwinFreeUSBDeviceFromIOKit(pCur);
1229
1230 CFRelease(PropsRef);
1231 }
1232 else
1233 AssertMsgFailed(("krc=%#x\n", krc));
1234
1235 IOObjectRelease(USBDevice);
1236 i++;
1237 }
1238
1239 IOObjectRelease(USBDevices);
1240 //DARWIN_IOKIT_LOG_FLUSH();
1241
1242 /*
1243 * Some post processing. There are a couple of things we have to
1244 * make 100% sure about, and that is that the (Apple) keyboard
1245 * and mouse most likely to be in use by the user aren't available
1246 * for capturing. If there is no Apple mouse or keyboard we'll
1247 * take the first one from another vendor.
1248 */
1249 /* As it turns out, the HID service will take all keyboards and mice
1250 and we're not currently able to seize them. */
1251 PUSBDEVICE pMouse = NULL;
1252 PUSBDEVICE pKeyboard = NULL;
1253 for (PUSBDEVICE pCur = pHead; pCur; pCur = pCur->pNext)
1254 if (pCur->idVendor == kIOUSBVendorIDAppleComputer)
1255 {
1256 /*
1257 * This test is a bit rough, should check device class/protocol but
1258 * we don't have interface info yet so that might be a bit tricky.
1259 */
1260 if ( ( !pKeyboard
1261 || pKeyboard->idVendor != kIOUSBVendorIDAppleComputer)
1262 && pCur->pszProduct
1263 && strstr(pCur->pszProduct, " Keyboard"))
1264 pKeyboard = pCur;
1265 else if ( ( !pMouse
1266 || pMouse->idVendor != kIOUSBVendorIDAppleComputer)
1267 && pCur->pszProduct
1268 && strstr(pCur->pszProduct, " Mouse")
1269 )
1270 pMouse = pCur;
1271 }
1272 else if (!pKeyboard || !pMouse)
1273 {
1274 if ( pCur->bDeviceClass == 3 /* HID */
1275 && pCur->bDeviceProtocol == 1 /* Keyboard */)
1276 pKeyboard = pCur;
1277 else if ( pCur->bDeviceClass == 3 /* HID */
1278 && pCur->bDeviceProtocol == 2 /* Mouse */)
1279 pMouse = pCur;
1280 /** @todo examin interfaces */
1281 }
1282
1283 if (pKeyboard)
1284 pKeyboard->enmState = USBDEVICESTATE_USED_BY_HOST;
1285 if (pMouse)
1286 pMouse->enmState = USBDEVICESTATE_USED_BY_HOST;
1287
1288 return pHead;
1289}
1290
1291#endif /* VBOX_WITH_USB */
1292
1293
1294/**
1295 * Enumerate the CD, DVD and BlueRay drives returning a FIFO of device name strings.
1296 *
1297 * @returns Pointer to the head.
1298 * The caller is responsible for calling RTMemFree() on each of the nodes.
1299 */
1300PDARWINDVD DarwinGetDVDDrives(void)
1301{
1302 AssertReturn(darwinOpenMasterPort(), NULL);
1303
1304 /*
1305 * Create a matching dictionary for searching for CD, DVD and BlueRay services in the IOKit.
1306 *
1307 * The idea is to find all the devices which are of class IOCDBlockStorageDevice.
1308 * CD devices are represented by IOCDBlockStorageDevice class itself, while DVD and BlueRay ones
1309 * have it as a parent class.
1310 */
1311 CFMutableDictionaryRef RefMatchingDict = IOServiceMatching("IOCDBlockStorageDevice");
1312 AssertReturn(RefMatchingDict, NULL);
1313
1314 /*
1315 * Perform the search and get a collection of DVD services.
1316 */
1317 io_iterator_t DVDServices = IO_OBJECT_NULL;
1318 IOReturn rc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &DVDServices);
1319 AssertMsgReturn(rc == kIOReturnSuccess, ("rc=%d\n", rc), NULL);
1320 RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
1321
1322 /*
1323 * Enumerate the matching services.
1324 * (This enumeration must be identical to the one performed in DrvHostBase.cpp.)
1325 */
1326 PDARWINDVD pHead = NULL;
1327 PDARWINDVD pTail = NULL;
1328 unsigned i = 0;
1329 io_object_t DVDService;
1330 while ((DVDService = IOIteratorNext(DVDServices)) != IO_OBJECT_NULL)
1331 {
1332 DARWIN_IOKIT_DUMP_OBJ(DVDService);
1333
1334 /*
1335 * Get the properties we use to identify the DVD drive.
1336 *
1337 * While there is a (weird 12 byte) GUID, it isn't persistent
1338 * across boots. So, we have to use a combination of the
1339 * vendor name and product name properties with an optional
1340 * sequence number for identification.
1341 */
1342 CFMutableDictionaryRef PropsRef = 0;
1343 kern_return_t krc = IORegistryEntryCreateCFProperties(DVDService, &PropsRef, kCFAllocatorDefault, kNilOptions);
1344 if (krc == KERN_SUCCESS)
1345 {
1346 /* Get the Device Characteristics dictionary. */
1347 CFDictionaryRef DevCharRef = (CFDictionaryRef)CFDictionaryGetValue(PropsRef, CFSTR(kIOPropertyDeviceCharacteristicsKey));
1348 if (DevCharRef)
1349 {
1350 /* The vendor name. */
1351 char szVendor[128];
1352 char *pszVendor = &szVendor[0];
1353 CFTypeRef ValueRef = CFDictionaryGetValue(DevCharRef, CFSTR(kIOPropertyVendorNameKey));
1354 if ( ValueRef
1355 && CFGetTypeID(ValueRef) == CFStringGetTypeID()
1356 && CFStringGetCString((CFStringRef)ValueRef, szVendor, sizeof(szVendor), kCFStringEncodingUTF8))
1357 pszVendor = RTStrStrip(szVendor);
1358 else
1359 *pszVendor = '\0';
1360
1361 /* The product name. */
1362 char szProduct[128];
1363 char *pszProduct = &szProduct[0];
1364 ValueRef = CFDictionaryGetValue(DevCharRef, CFSTR(kIOPropertyProductNameKey));
1365 if ( ValueRef
1366 && CFGetTypeID(ValueRef) == CFStringGetTypeID()
1367 && CFStringGetCString((CFStringRef)ValueRef, szProduct, sizeof(szProduct), kCFStringEncodingUTF8))
1368 pszProduct = RTStrStrip(szProduct);
1369 else
1370 *pszProduct = '\0';
1371
1372 /* Construct the name and check for duplicates. */
1373 char szName[256 + 32];
1374 if (*pszVendor || *pszProduct)
1375 {
1376 if (*pszVendor && *pszProduct)
1377 RTStrPrintf(szName, sizeof(szName), "%s %s", pszVendor, pszProduct);
1378 else
1379 strcpy(szName, *pszVendor ? pszVendor : pszProduct);
1380
1381 for (PDARWINDVD pCur = pHead; pCur; pCur = pCur->pNext)
1382 {
1383 if (!strcmp(szName, pCur->szName))
1384 {
1385 if (*pszVendor && *pszProduct)
1386 RTStrPrintf(szName, sizeof(szName), "%s %s (#%u)", pszVendor, pszProduct, i);
1387 else
1388 RTStrPrintf(szName, sizeof(szName), "%s (#%u)", *pszVendor ? pszVendor : pszProduct, i);
1389 break;
1390 }
1391 }
1392 }
1393 else
1394 RTStrPrintf(szName, sizeof(szName), "(#%u)", i);
1395
1396 /* Create the device. */
1397 size_t cbName = strlen(szName) + 1;
1398 PDARWINDVD pNew = (PDARWINDVD)RTMemAlloc(RT_UOFFSETOF_DYN(DARWINDVD, szName[cbName]));
1399 if (pNew)
1400 {
1401 pNew->pNext = NULL;
1402 memcpy(pNew->szName, szName, cbName);
1403 if (pTail)
1404 pTail = pTail->pNext = pNew;
1405 else
1406 pTail = pHead = pNew;
1407 }
1408 }
1409 CFRelease(PropsRef);
1410 }
1411 else
1412 AssertMsgFailed(("krc=%#x\n", krc));
1413
1414 IOObjectRelease(DVDService);
1415 i++;
1416 }
1417
1418 IOObjectRelease(DVDServices);
1419
1420 return pHead;
1421}
1422
1423
1424/**
1425 * Enumerate the fixed drives (HDDs, SSD, ++) returning a FIFO of device paths
1426 * strings and model strings separated by ':'.
1427 *
1428 * @returns Pointer to the head.
1429 * The caller is responsible for calling RTMemFree() on each of the nodes.
1430 */
1431PDARWINFIXEDDRIVE DarwinGetFixedDrives(void)
1432{
1433 AssertReturn(darwinOpenMasterPort(), NULL);
1434
1435 /*
1436 * Create a matching dictionary for searching drives in the IOKit.
1437 *
1438 * The idea is to find all the IOMedia objects with "Whole"="True" which identify the disks but
1439 * not partitions.
1440 */
1441 CFMutableDictionaryRef RefMatchingDict = IOServiceMatching("IOMedia");
1442 AssertReturn(RefMatchingDict, NULL);
1443 CFDictionaryAddValue(RefMatchingDict, CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
1444
1445 /*
1446 * Perform the search and get a collection of IOMedia objects.
1447 */
1448 io_iterator_t MediaServices = IO_OBJECT_NULL;
1449 IOReturn rc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &MediaServices);
1450 AssertMsgReturn(rc == kIOReturnSuccess, ("rc=%d\n", rc), NULL);
1451 RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
1452
1453 /*
1454 * Enumerate the matching services.
1455 * (This enumeration must be identical to the one performed in DrvHostBase.cpp.)
1456 */
1457 PDARWINFIXEDDRIVE pHead = NULL;
1458 PDARWINFIXEDDRIVE pTail = NULL;
1459 unsigned i = 0;
1460 io_object_t MediaService;
1461 while ((MediaService = IOIteratorNext(MediaServices)) != IO_OBJECT_NULL)
1462 {
1463 DARWIN_IOKIT_DUMP_OBJ(MediaService);
1464
1465 /*
1466 * Find the IOMedia parents having the IOBlockStorageDevice type and check they have "device-type" = "Generic".
1467 * If the IOMedia object hasn't IOBlockStorageDevices with such device-type in parents the one is not general
1468 * disk but either CDROM-like device or some another device which has no interest for the function.
1469 */
1470
1471 /*
1472 * Just avoid parents enumeration if the IOMedia is IOCDMedia, i.e. CDROM-like disk
1473 */
1474 if (IOObjectConformsTo(MediaService, kIOCDMediaClass))
1475 {
1476 IOObjectRelease(MediaService);
1477 continue;
1478 }
1479
1480 bool fIsGenericStorage = false;
1481 io_registry_entry_t ChildEntry = MediaService;
1482 io_registry_entry_t ParentEntry = IO_OBJECT_NULL;
1483 kern_return_t krc = KERN_SUCCESS;
1484 while ( !fIsGenericStorage
1485 && (krc = IORegistryEntryGetParentEntry(ChildEntry, kIOServicePlane, &ParentEntry)) == KERN_SUCCESS)
1486 {
1487 if (!IOObjectIsEqualTo(ChildEntry, MediaService))
1488 IOObjectRelease(ChildEntry);
1489
1490 DARWIN_IOKIT_DUMP_OBJ(ParentEntry);
1491 if (IOObjectConformsTo(ParentEntry, kIOBlockStorageDeviceClass))
1492 {
1493 CFTypeRef DeviceTypeValueRef = IORegistryEntryCreateCFProperty(ParentEntry,
1494 CFSTR("device-type"),
1495 kCFAllocatorDefault, 0);
1496 if ( DeviceTypeValueRef
1497 && CFGetTypeID(DeviceTypeValueRef) == CFStringGetTypeID()
1498 && CFStringCompare((CFStringRef)DeviceTypeValueRef, CFSTR("Generic"),
1499 kCFCompareCaseInsensitive) == kCFCompareEqualTo)
1500 fIsGenericStorage = true;
1501
1502 if (DeviceTypeValueRef != NULL)
1503 CFRelease(DeviceTypeValueRef);
1504 }
1505 ChildEntry = ParentEntry;
1506 }
1507 if (ChildEntry != IO_OBJECT_NULL && !IOObjectIsEqualTo(ChildEntry, MediaService))
1508 IOObjectRelease(ChildEntry);
1509
1510 if (!fIsGenericStorage)
1511 {
1512 IOObjectRelease(MediaService);
1513 continue;
1514 }
1515
1516 CFTypeRef DeviceName;
1517 DeviceName = IORegistryEntryCreateCFProperty(MediaService,
1518 CFSTR(kIOBSDNameKey),
1519 kCFAllocatorDefault,0);
1520 if (DeviceName)
1521 {
1522 char szDeviceFilePath[MAXPATHLEN];
1523 strcpy(szDeviceFilePath, _PATH_DEV);
1524 size_t cchPathSize = strlen(szDeviceFilePath);
1525 if (CFStringGetCString((CFStringRef)DeviceName,
1526 &szDeviceFilePath[cchPathSize],
1527 (CFIndex)(sizeof(szDeviceFilePath) - cchPathSize),
1528 kCFStringEncodingUTF8))
1529 {
1530 PDARWINFIXEDDRIVE pDuplicate = pHead;
1531 while (pDuplicate && strcmp(szDeviceFilePath, pDuplicate->szName) != 0)
1532 pDuplicate = pDuplicate->pNext;
1533 if (pDuplicate == NULL)
1534 {
1535 /* Get model for the IOMedia object.
1536 *
1537 * Due to vendor and product property names are different and
1538 * depend on interface and device type, the best way to get a drive
1539 * model is get IORegistry name for the IOMedia object. Usually,
1540 * it takes "<vendor> <product> <revision> Media" form. Noticed,
1541 * such naming are used by only IOMedia objects having
1542 * "Whole" = True and "BSDName" properties set.
1543 */
1544 io_name_t szEntryName = { 0 };
1545 if ((krc = IORegistryEntryGetName(MediaService, szEntryName)) == KERN_SUCCESS)
1546 {
1547 /* remove " Media" from the end of the name */
1548 char *pszMedia = strrchr(szEntryName, ' ');
1549 if ( pszMedia != NULL
1550 && (uintptr_t)pszMedia < (uintptr_t)&szEntryName[sizeof(szEntryName)]
1551 && strcmp(pszMedia, " Media") == 0)
1552 {
1553 *pszMedia = '\0';
1554 RTStrPurgeEncoding(szEntryName);
1555 }
1556 }
1557 /* Create the device path and model name in form "/device/path:model". */
1558 cchPathSize = strlen(szDeviceFilePath);
1559 size_t const cchModelSize = strlen(szEntryName);
1560 size_t const cbExtra = cchPathSize + 1 + cchModelSize + !!cchModelSize;
1561 PDARWINFIXEDDRIVE pNew = (PDARWINFIXEDDRIVE)RTMemAlloc(RT_UOFFSETOF_DYN(DARWINFIXEDDRIVE, szName[cbExtra]));
1562 if (pNew)
1563 {
1564 pNew->pNext = NULL;
1565 memcpy(pNew->szName, szDeviceFilePath, cchPathSize + 1);
1566 pNew->pszModel = NULL;
1567 if (cchModelSize)
1568 pNew->pszModel = (const char *)memcpy(&pNew->szName[cchPathSize + 1], szEntryName, cchModelSize + 1);
1569
1570 if (pTail)
1571 pTail = pTail->pNext = pNew;
1572 else
1573 pTail = pHead = pNew;
1574 }
1575 }
1576 }
1577 CFRelease(DeviceName);
1578 }
1579 IOObjectRelease(MediaService);
1580 i++;
1581 }
1582 IOObjectRelease(MediaServices);
1583
1584 return pHead;
1585}
1586
1587
1588/**
1589 * Enumerate the ethernet capable network devices returning a FIFO of them.
1590 *
1591 * @returns Pointer to the head.
1592 */
1593PDARWINETHERNIC DarwinGetEthernetControllers(void)
1594{
1595 AssertReturn(darwinOpenMasterPort(), NULL);
1596
1597 /*
1598 * Create a matching dictionary for searching for ethernet controller
1599 * services in the IOKit.
1600 *
1601 * For some really stupid reason I don't get all the controllers if I look for
1602 * objects that are instances of IOEthernetController or its descendants (only
1603 * get the AirPort on my mac pro). But fortunately using IOEthernetInterface
1604 * seems to work. Weird s**t!
1605 */
1606 //CFMutableDictionaryRef RefMatchingDict = IOServiceMatching("IOEthernetController"); - this doesn't work :-(
1607 CFMutableDictionaryRef RefMatchingDict = IOServiceMatching("IOEthernetInterface");
1608 AssertReturn(RefMatchingDict, NULL);
1609
1610 /*
1611 * Perform the search and get a collection of ethernet controller services.
1612 */
1613 io_iterator_t EtherIfServices = IO_OBJECT_NULL;
1614 IOReturn rc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &EtherIfServices);
1615 AssertMsgReturn(rc == kIOReturnSuccess, ("rc=%d\n", rc), NULL);
1616 RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
1617
1618 /*
1619 * Get a copy of the current network interfaces from the system configuration service.
1620 * We'll use this for looking up the proper interface names.
1621 */
1622 CFArrayRef IfsRef = SCNetworkInterfaceCopyAll();
1623 CFIndex cIfs = IfsRef ? CFArrayGetCount(IfsRef) : 0;
1624
1625 /*
1626 * Get the current preferences and make a copy of the network services so we
1627 * can look up the right interface names. The IfsRef is just for fallback.
1628 */
1629 CFArrayRef ServicesRef = NULL;
1630 CFIndex cServices = 0;
1631 SCPreferencesRef PrefsRef = SCPreferencesCreate(kCFAllocatorDefault, CFSTR("org.virtualbox.VBoxSVC"), NULL);
1632 if (PrefsRef)
1633 {
1634 SCNetworkSetRef SetRef = SCNetworkSetCopyCurrent(PrefsRef);
1635 CFRelease(PrefsRef);
1636 if (SetRef)
1637 {
1638 ServicesRef = SCNetworkSetCopyServices(SetRef);
1639 CFRelease(SetRef);
1640 cServices = ServicesRef ? CFArrayGetCount(ServicesRef) : 0;
1641 }
1642 }
1643
1644 /*
1645 * Enumerate the ethernet controller services.
1646 */
1647 PDARWINETHERNIC pHead = NULL;
1648 PDARWINETHERNIC pTail = NULL;
1649 io_object_t EtherIfService;
1650 while ((EtherIfService = IOIteratorNext(EtherIfServices)) != IO_OBJECT_NULL)
1651 {
1652 /*
1653 * Dig up the parent, meaning the IOEthernetController.
1654 */
1655 io_object_t EtherNICService;
1656 kern_return_t krc = IORegistryEntryGetParentEntry(EtherIfService, kIOServicePlane, &EtherNICService);
1657 /*krc = IORegistryEntryGetChildEntry(EtherNICService, kIOServicePlane, &EtherIfService); */
1658 if (krc == KERN_SUCCESS)
1659 {
1660 DARWIN_IOKIT_DUMP_OBJ(EtherNICService);
1661 /*
1662 * Get the properties we use to identify and name the Ethernet NIC.
1663 * We need the both the IOEthernetController and it's IONetworkInterface child.
1664 */
1665 CFMutableDictionaryRef PropsRef = 0;
1666 krc = IORegistryEntryCreateCFProperties(EtherNICService, &PropsRef, kCFAllocatorDefault, kNilOptions);
1667 if (krc == KERN_SUCCESS)
1668 {
1669 CFMutableDictionaryRef IfPropsRef = 0;
1670 krc = IORegistryEntryCreateCFProperties(EtherIfService, &IfPropsRef, kCFAllocatorDefault, kNilOptions);
1671 if (krc == KERN_SUCCESS)
1672 {
1673 /*
1674 * Gather the required data.
1675 * We'll create a UUID from the MAC address and the BSD name.
1676 */
1677 char szTmp[256];
1678 do
1679 {
1680 /* Check if airport (a bit heuristical - it's com.apple.driver.AirPortBrcm43xx here). */
1681 darwinDictGetString(PropsRef, CFSTR("CFBundleIdentifier"), szTmp, sizeof(szTmp));
1682 bool fWireless;
1683 bool fAirPort = fWireless = strstr(szTmp, ".AirPort") != NULL;
1684
1685 /* Check if it's USB. */
1686 darwinDictGetString(PropsRef, CFSTR("IOProviderClass"), szTmp, sizeof(szTmp));
1687 bool fUSB = strstr(szTmp, "USB") != NULL;
1688
1689
1690 /* Is it builtin? */
1691 bool fBuiltin;
1692 darwinDictGetBool(IfPropsRef, CFSTR("IOBuiltin"), &fBuiltin);
1693
1694 /* Is it the primary interface */
1695 bool fPrimaryIf;
1696 darwinDictGetBool(IfPropsRef, CFSTR("IOPrimaryInterface"), &fPrimaryIf);
1697
1698 /* Get the MAC address. */
1699 RTMAC Mac;
1700 AssertBreak(darwinDictGetData(PropsRef, CFSTR("IOMACAddress"), &Mac, sizeof(Mac)));
1701
1702 /* The BSD Name from the interface dictionary. No assert here as the belkin USB-C gadget
1703 does not always end up with a BSD name, typically requiring replugging. */
1704 char szBSDName[RT_SIZEOFMEMB(DARWINETHERNIC, szBSDName)];
1705 if (RT_UNLIKELY(!darwinDictGetString(IfPropsRef, CFSTR("BSD Name"), szBSDName, sizeof(szBSDName))))
1706 {
1707 LogRelMax(32, ("DarwinGetEthernetControllers: Warning! Failed to get 'BSD Name'; provider class %s\n", szTmp));
1708 break;
1709 }
1710
1711 /* Check if it's really wireless. */
1712 if ( darwinDictIsPresent(IfPropsRef, CFSTR("IO80211CountryCode"))
1713 || darwinDictIsPresent(IfPropsRef, CFSTR("IO80211DriverVersion"))
1714 || darwinDictIsPresent(IfPropsRef, CFSTR("IO80211HardwareVersion"))
1715 || darwinDictIsPresent(IfPropsRef, CFSTR("IO80211Locale")))
1716 fWireless = true;
1717 else
1718 fAirPort = fWireless = false;
1719
1720 /** @todo IOPacketFilters / IONetworkFilterGroup? */
1721 /*
1722 * Create the interface name.
1723 *
1724 * Note! The ConsoleImpl2.cpp code ASSUMES things about the name. It is also
1725 * stored in the VM config files. (really bright idea)
1726 */
1727 strcpy(szTmp, szBSDName);
1728 char *psz = strchr(szTmp, '\0');
1729 *psz++ = ':';
1730 *psz++ = ' ';
1731 size_t cchLeft = sizeof(szTmp) - (size_t)(psz - &szTmp[0]) - (sizeof(" (Wireless)") - 1);
1732 bool fFound = false;
1733 CFIndex i;
1734
1735 /* look it up among the current services */
1736 for (i = 0; i < cServices; i++)
1737 {
1738 SCNetworkServiceRef ServiceRef = (SCNetworkServiceRef)CFArrayGetValueAtIndex(ServicesRef, i);
1739 SCNetworkInterfaceRef IfRef = SCNetworkServiceGetInterface(ServiceRef);
1740 if (IfRef)
1741 {
1742 CFStringRef BSDNameRef = SCNetworkInterfaceGetBSDName(IfRef);
1743 if ( BSDNameRef
1744 && CFStringGetCString(BSDNameRef, psz, (CFIndex)cchLeft, kCFStringEncodingUTF8)
1745 && !strcmp(psz, szBSDName))
1746 {
1747 CFStringRef ServiceNameRef = SCNetworkServiceGetName(ServiceRef);
1748 if ( ServiceNameRef
1749 && CFStringGetCString(ServiceNameRef, psz, (CFIndex)cchLeft, kCFStringEncodingUTF8))
1750 {
1751 fFound = true;
1752 break;
1753 }
1754 }
1755 }
1756 }
1757 /* Look it up in the interface list. */
1758 if (!fFound)
1759 for (i = 0; i < cIfs; i++)
1760 {
1761 SCNetworkInterfaceRef IfRef = (SCNetworkInterfaceRef)CFArrayGetValueAtIndex(IfsRef, i);
1762 CFStringRef BSDNameRef = SCNetworkInterfaceGetBSDName(IfRef);
1763 if ( BSDNameRef
1764 && CFStringGetCString(BSDNameRef, psz, (CFIndex)cchLeft, kCFStringEncodingUTF8)
1765 && !strcmp(psz, szBSDName))
1766 {
1767 CFStringRef DisplayNameRef = SCNetworkInterfaceGetLocalizedDisplayName(IfRef);
1768 if ( DisplayNameRef
1769 && CFStringGetCString(DisplayNameRef, psz, (CFIndex)cchLeft, kCFStringEncodingUTF8))
1770 {
1771 fFound = true;
1772 break;
1773 }
1774 }
1775 }
1776 /* Generate a half plausible name if we for some silly reason didn't find the interface. */
1777 if (!fFound)
1778 RTStrPrintf(szTmp, sizeof(szTmp), "%s: %s%s(?)",
1779 szBSDName,
1780 fUSB ? "USB " : "",
1781 fWireless ? fAirPort ? "AirPort " : "Wireless" : "Ethernet");
1782 /* If we did find it and it's wireless but without "AirPort" or "Wireless", fix it */
1783 else if ( fWireless
1784 && !strstr(psz, "AirPort")
1785 && !strstr(psz, "Wireless"))
1786 strcat(szTmp, fAirPort ? " (AirPort)" : " (Wireless)");
1787
1788 /*
1789 * Create the list entry.
1790 */
1791 DARWIN_IOKIT_LOG(("Found: if=%s mac=%.6Rhxs fWireless=%RTbool fAirPort=%RTbool fBuiltin=%RTbool fPrimaryIf=%RTbool fUSB=%RTbool\n",
1792 szBSDName, &Mac, fWireless, fAirPort, fBuiltin, fPrimaryIf, fUSB));
1793
1794 size_t cchName = strlen(szTmp);
1795 PDARWINETHERNIC pNew = (PDARWINETHERNIC)RTMemAlloc(RT_UOFFSETOF_DYN(DARWINETHERNIC, szName[cchName + 1]));
1796 if (pNew)
1797 {
1798 strncpy(pNew->szBSDName, szBSDName, sizeof(pNew->szBSDName)); /* the '\0' padding is intentional! */
1799
1800 RTUuidClear(&pNew->Uuid);
1801 memcpy(&pNew->Uuid, pNew->szBSDName, RT_MIN(sizeof(pNew->szBSDName), sizeof(pNew->Uuid)));
1802 pNew->Uuid.Gen.u8ClockSeqHiAndReserved = (pNew->Uuid.Gen.u8ClockSeqHiAndReserved & 0x3f) | 0x80;
1803 pNew->Uuid.Gen.u16TimeHiAndVersion = (pNew->Uuid.Gen.u16TimeHiAndVersion & 0x0fff) | 0x4000;
1804 pNew->Uuid.Gen.au8Node[0] = Mac.au8[0];
1805 pNew->Uuid.Gen.au8Node[1] = Mac.au8[1];
1806 pNew->Uuid.Gen.au8Node[2] = Mac.au8[2];
1807 pNew->Uuid.Gen.au8Node[3] = Mac.au8[3];
1808 pNew->Uuid.Gen.au8Node[4] = Mac.au8[4];
1809 pNew->Uuid.Gen.au8Node[5] = Mac.au8[5];
1810
1811 pNew->Mac = Mac;
1812 pNew->fWireless = fWireless;
1813 pNew->fAirPort = fAirPort;
1814 pNew->fBuiltin = fBuiltin;
1815 pNew->fUSB = fUSB;
1816 pNew->fPrimaryIf = fPrimaryIf;
1817 memcpy(pNew->szName, szTmp, cchName + 1);
1818
1819 /*
1820 * Link it into the list, keep the list sorted by fPrimaryIf and the BSD name.
1821 */
1822 if (pTail)
1823 {
1824 PDARWINETHERNIC pPrev = pTail;
1825 if (strcmp(pNew->szBSDName, pPrev->szBSDName) < 0)
1826 {
1827 pPrev = NULL;
1828 for (PDARWINETHERNIC pCur = pHead; pCur; pPrev = pCur, pCur = pCur->pNext)
1829 if ( (int)pNew->fPrimaryIf - (int)pCur->fPrimaryIf > 0
1830 || ( (int)pNew->fPrimaryIf - (int)pCur->fPrimaryIf == 0
1831 && strcmp(pNew->szBSDName, pCur->szBSDName) >= 0))
1832 break;
1833 }
1834 if (pPrev)
1835 {
1836 /* tail or in list. */
1837 pNew->pNext = pPrev->pNext;
1838 pPrev->pNext = pNew;
1839 if (pPrev == pTail)
1840 pTail = pNew;
1841 }
1842 else
1843 {
1844 /* head */
1845 pNew->pNext = pHead;
1846 pHead = pNew;
1847 }
1848 }
1849 else
1850 {
1851 /* empty list */
1852 pNew->pNext = NULL;
1853 pTail = pHead = pNew;
1854 }
1855 }
1856 } while (0);
1857
1858 CFRelease(IfPropsRef);
1859 }
1860 CFRelease(PropsRef);
1861 }
1862 IOObjectRelease(EtherNICService);
1863 }
1864 else
1865 AssertMsgFailed(("krc=%#x\n", krc));
1866 IOObjectRelease(EtherIfService);
1867 }
1868
1869 IOObjectRelease(EtherIfServices);
1870 if (ServicesRef)
1871 CFRelease(ServicesRef);
1872 if (IfsRef)
1873 CFRelease(IfsRef);
1874 return pHead;
1875}
1876
1877#ifdef STANDALONE_TESTCASE
1878/**
1879 * This file can optionally be compiled into a testcase, this is the main function.
1880 * To build:
1881 * 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
1882 */
1883int main(int argc, char **argv)
1884{
1885 RTR3InitExe(argc, &argv, 0);
1886
1887 if (1)
1888 {
1889 /*
1890 * Network preferences.
1891 */
1892 RTPrintf("Preferences: Network Services\n");
1893 SCPreferencesRef PrefsRef = SCPreferencesCreate(kCFAllocatorDefault, CFSTR("org.virtualbox.VBoxSVC"), NULL);
1894 if (PrefsRef)
1895 {
1896 CFDictionaryRef NetworkServiceRef = (CFDictionaryRef)SCPreferencesGetValue(PrefsRef, kSCPrefNetworkServices);
1897 darwinDumpDict(NetworkServiceRef, 4);
1898 CFRelease(PrefsRef);
1899 }
1900 }
1901
1902 if (1)
1903 {
1904 /*
1905 * Network services interfaces in the current config.
1906 */
1907 RTPrintf("Preferences: Network Service Interfaces\n");
1908 SCPreferencesRef PrefsRef = SCPreferencesCreate(kCFAllocatorDefault, CFSTR("org.virtualbox.VBoxSVC"), NULL);
1909 if (PrefsRef)
1910 {
1911 SCNetworkSetRef SetRef = SCNetworkSetCopyCurrent(PrefsRef);
1912 if (SetRef)
1913 {
1914 CFArrayRef ServicesRef = SCNetworkSetCopyServices(SetRef);
1915 CFIndex cServices = CFArrayGetCount(ServicesRef);
1916 for (CFIndex i = 0; i < cServices; i++)
1917 {
1918 SCNetworkServiceRef ServiceRef = (SCNetworkServiceRef)CFArrayGetValueAtIndex(ServicesRef, i);
1919 char szServiceName[128] = {0};
1920 CFStringGetCString(SCNetworkServiceGetName(ServiceRef), szServiceName, sizeof(szServiceName), kCFStringEncodingUTF8);
1921
1922 SCNetworkInterfaceRef IfRef = SCNetworkServiceGetInterface(ServiceRef);
1923 char szBSDName[16] = {0};
1924 if (SCNetworkInterfaceGetBSDName(IfRef))
1925 CFStringGetCString(SCNetworkInterfaceGetBSDName(IfRef), szBSDName, sizeof(szBSDName), kCFStringEncodingUTF8);
1926 char szDisplayName[128] = {0};
1927 if (SCNetworkInterfaceGetLocalizedDisplayName(IfRef))
1928 CFStringGetCString(SCNetworkInterfaceGetLocalizedDisplayName(IfRef), szDisplayName, sizeof(szDisplayName), kCFStringEncodingUTF8);
1929
1930 RTPrintf(" #%u ServiceName=\"%s\" IfBSDName=\"%s\" IfDisplayName=\"%s\"\n",
1931 i, szServiceName, szBSDName, szDisplayName);
1932 }
1933
1934 CFRelease(ServicesRef);
1935 CFRelease(SetRef);
1936 }
1937
1938 CFRelease(PrefsRef);
1939 }
1940 }
1941
1942 if (1)
1943 {
1944 /*
1945 * Network interfaces.
1946 */
1947 RTPrintf("Preferences: Network Interfaces\n");
1948 CFArrayRef IfsRef = SCNetworkInterfaceCopyAll();
1949 if (IfsRef)
1950 {
1951 CFIndex cIfs = CFArrayGetCount(IfsRef);
1952 for (CFIndex i = 0; i < cIfs; i++)
1953 {
1954 SCNetworkInterfaceRef IfRef = (SCNetworkInterfaceRef)CFArrayGetValueAtIndex(IfsRef, i);
1955 char szBSDName[16] = {0};
1956 if (SCNetworkInterfaceGetBSDName(IfRef))
1957 CFStringGetCString(SCNetworkInterfaceGetBSDName(IfRef), szBSDName, sizeof(szBSDName), kCFStringEncodingUTF8);
1958 char szDisplayName[128] = {0};
1959 if (SCNetworkInterfaceGetLocalizedDisplayName(IfRef))
1960 CFStringGetCString(SCNetworkInterfaceGetLocalizedDisplayName(IfRef), szDisplayName, sizeof(szDisplayName), kCFStringEncodingUTF8);
1961 RTPrintf(" #%u BSDName=\"%s\" DisplayName=\"%s\"\n",
1962 i, szBSDName, szDisplayName);
1963 }
1964
1965 CFRelease(IfsRef);
1966 }
1967 }
1968
1969 if (1)
1970 {
1971 /*
1972 * Get and display the ethernet controllers.
1973 */
1974 RTPrintf("Ethernet controllers:\n");
1975 PDARWINETHERNIC pEtherNICs = DarwinGetEthernetControllers();
1976 for (PDARWINETHERNIC pCur = pEtherNICs; pCur; pCur = pCur->pNext)
1977 {
1978 RTPrintf("%s\n", pCur->szName);
1979 RTPrintf(" szBSDName=%s\n", pCur->szBSDName);
1980 RTPrintf(" UUID=%RTuuid\n", &pCur->Uuid);
1981 RTPrintf(" Mac=%.6Rhxs\n", &pCur->Mac);
1982 RTPrintf(" fWireless=%RTbool\n", pCur->fWireless);
1983 RTPrintf(" fAirPort=%RTbool\n", pCur->fAirPort);
1984 RTPrintf(" fBuiltin=%RTbool\n", pCur->fBuiltin);
1985 RTPrintf(" fUSB=%RTbool\n", pCur->fUSB);
1986 RTPrintf(" fPrimaryIf=%RTbool\n", pCur->fPrimaryIf);
1987 }
1988 }
1989
1990
1991 return 0;
1992}
1993#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