VirtualBox

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

Last change on this file since 86136 was 85929, checked in by vboxsync, 5 years ago

Main: bugref:9224: Main+VBoxManageDisk+doc part

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

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