VirtualBox

source: vbox/trunk/src/VBox/Main/darwin/iokit.cpp@ 8569

Last change on this file since 8569 was 8569, checked in by vboxsync, 17 years ago

AssertBreak -> AssertBreakStmt.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 47.4 KB
Line 
1/* $Id: iokit.cpp 8569 2008-05-05 12:32:51Z 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-2007 Sun Microsystems, Inc.
12 *
13 * This file is part of VirtualBox Open Source Edition (OSE), as
14 * available from http://www.virtualbox.org. 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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
22 * Clara, CA 95054 USA or visit http://www.sun.com if you need
23 * additional information or have any questions.
24 */
25
26
27/*******************************************************************************
28* Header Files *
29*******************************************************************************/
30#define LOG_GROUP LOG_GROUP_MAIN
31
32#include <mach/mach.h>
33#include <Carbon/Carbon.h>
34#include <IOKit/IOKitLib.h>
35#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
36#include <IOKit/scsi-commands/SCSITaskLib.h>
37#include <mach/mach_error.h>
38#ifdef VBOX_WITH_USB
39# include <IOKit/usb/IOUSBLib.h>
40# include <IOKit/IOCFPlugIn.h>
41#endif
42
43#include <VBox/log.h>
44#include <VBox/err.h>
45#include <iprt/mem.h>
46#include <iprt/string.h>
47#include <iprt/process.h>
48#include <iprt/assert.h>
49#include <iprt/thread.h>
50
51#include "iokit.h"
52
53
54/*******************************************************************************
55* Defined Constants And Macros *
56*******************************************************************************/
57/** An attempt at catching reference leaks. */
58#define MY_CHECK_CREFS(cRefs) do { AssertMsg(cRefs < 25, ("%ld\n", cRefs)); NOREF(cRefs); } while (0)
59
60/** Contains the pid of the current client. If 0, the kernel is the current client. */
61#define VBOXUSB_CLIENT_KEY "VBoxUSB-Client"
62/** Contains the pid of the filter owner (i.e. the VBoxSVC pid). */
63#define VBOXUSB_OWNER_KEY "VBoxUSB-Owner"
64/** The VBoxUSBDevice class name. */
65#define VBOXUSBDEVICE_CLASS_NAME "org_virtualbox_VBoxUSBDevice"
66
67
68/*******************************************************************************
69* Global Variables *
70*******************************************************************************/
71/** The IO Master Port. */
72static mach_port_t g_MasterPort = NULL;
73
74
75/**
76 * Lazily opens the master port.
77 *
78 * @returns true if the port is open, false on failure (very unlikely).
79 */
80static bool darwinOpenMasterPort(void)
81{
82 if (!g_MasterPort)
83 {
84 kern_return_t krc = IOMasterPort(MACH_PORT_NULL, &g_MasterPort);
85 AssertReturn(krc == KERN_SUCCESS, false);
86 }
87 return true;
88}
89
90
91#ifdef VBOX_WITH_USB
92
93/**
94 * Gets an unsigned 8-bit integer value.
95 *
96 * @returns Success indicator (true/false).
97 * @param DictRef The dictionary.
98 * @param KeyStrRef The key name.
99 * @param pu8 Where to store the key value.
100 */
101static bool darwinDictGetU8(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, uint8_t *pu8)
102{
103 CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
104 if (ValRef)
105 {
106 if (CFNumberGetValue((CFNumberRef)ValRef, kCFNumberSInt8Type, pu8))
107 return true;
108 }
109 *pu8 = 0;
110 return false;
111}
112
113
114/**
115 * Gets an unsigned 16-bit integer value.
116 *
117 * @returns Success indicator (true/false).
118 * @param DictRef The dictionary.
119 * @param KeyStrRef The key name.
120 * @param pu16 Where to store the key value.
121 */
122static bool darwinDictGetU16(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, uint16_t *pu16)
123{
124 CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
125 if (ValRef)
126 {
127 if (CFNumberGetValue((CFNumberRef)ValRef, kCFNumberSInt16Type, pu16))
128 return true;
129 }
130 *pu16 = 0;
131 return false;
132}
133
134
135/**
136 * Gets an unsigned 32-bit integer value.
137 *
138 * @returns Success indicator (true/false).
139 * @param DictRef The dictionary.
140 * @param KeyStrRef The key name.
141 * @param pu32 Where to store the key value.
142 */
143static bool darwinDictGetU32(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, uint32_t *pu32)
144{
145 CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
146 if (ValRef)
147 {
148 if (CFNumberGetValue((CFNumberRef)ValRef, kCFNumberSInt32Type, pu32))
149 return true;
150 }
151 *pu32 = 0;
152 return false;
153}
154
155
156/**
157 * Gets an unsigned 64-bit integer value.
158 *
159 * @returns Success indicator (true/false).
160 * @param DictRef The dictionary.
161 * @param KeyStrRef The key name.
162 * @param pu64 Where to store the key value.
163 */
164static bool darwinDictGetU64(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, uint64_t *pu64)
165{
166 CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
167 if (ValRef)
168 {
169 if (CFNumberGetValue((CFNumberRef)ValRef, kCFNumberSInt64Type, pu64))
170 return true;
171 }
172 *pu64 = 0;
173 return false;
174}
175
176
177/**
178 * Gets a RTPROCESS value.
179 *
180 * @returns Success indicator (true/false).
181 * @param DictRef The dictionary.
182 * @param KeyStrRef The key name.
183 * @param pProcess Where to store the key value.
184 */
185static bool darwinDictGetProccess(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, PRTPROCESS pProcess)
186{
187 switch (sizeof(*pProcess))
188 {
189 case sizeof(uint16_t): return darwinDictGetU16(DictRef, KeyStrRef, (uint16_t *)pProcess);
190 case sizeof(uint32_t): return darwinDictGetU32(DictRef, KeyStrRef, (uint32_t *)pProcess);
191 case sizeof(uint64_t): return darwinDictGetU64(DictRef, KeyStrRef, (uint64_t *)pProcess);
192 default:
193 AssertMsgFailedReturn(("%d\n", sizeof(*pProcess)), false);
194 }
195}
196
197
198/**
199 * Gets string value, converted to UTF-8 and put in a IPRT string buffer.
200 *
201 * @returns Success indicator (true/false).
202 * @param DictRef The dictionary.
203 * @param KeyStrRef The key name.
204 * @param ppsz Where to store the key value. Free with RTStrFree. Set to NULL on failure.
205 */
206static bool darwinDictGetString(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, char **ppsz)
207{
208 CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
209 if (ValRef)
210 {
211 char szBuf[512];
212 if (CFStringGetCString((CFStringRef)ValRef, szBuf, sizeof(szBuf), kCFStringEncodingUTF8))
213 {
214 *ppsz = RTStrDup(RTStrStrip(szBuf));
215 if (*ppsz)
216 return true;
217 }
218 }
219 *ppsz = NULL;
220 return false;
221}
222
223
224#if 1 /* dumping disabled */
225# define DARWIN_IOKIT_LOG(a) Log(a)
226# define DARWIN_IOKIT_LOG_FLUSH() do {} while (0)
227# define DARWIN_IOKIT_DUMP_OBJ(o) do {} while (0)
228#else
229# if 0
230# include <iprt/stream.h>
231# define DARWIN_IOKIT_LOG(a) RTPrintf a
232# define DARWIN_IOKIT_LOG_FLUSH() RTStrmFlush(g_pStdOut)
233# else
234# define DARWIN_IOKIT_LOG(a) RTLogPrintf a
235# define DARWIN_IOKIT_LOG(a) RTLogFlush()
236# endif
237# define DARWIN_IOKIT_DUMP_OBJ(o) darwinDumpObj(o)
238
239/**
240 * Callback for dumping a dictionary key.
241 *
242 * @param pvKey The key name.
243 * @param pvValue The key value
244 * @param pvUser The recursion depth.
245 */
246static void darwinDumpDictCallback(const void *pvKey, const void *pvValue, void *pvUser)
247{
248 /* display the key name. */
249 char *pszKey = (char *)RTMemTmpAlloc(1024);
250 if (!CFStringGetCString((CFStringRef)pvKey, pszKey, 1024, kCFStringEncodingUTF8))
251 strcpy(pszKey, "CFStringGetCString failure");
252 DARWIN_IOKIT_LOG(("%+*s%s", (int)(uintptr_t)pvUser, "", pszKey));
253 RTMemTmpFree(pszKey);
254
255 /* display the value type */
256 CFTypeID Type = CFGetTypeID(pvValue);
257 DARWIN_IOKIT_LOG((" [%d-", Type));
258
259 /* display the value */
260 if (Type == CFDictionaryGetTypeID())
261 {
262 DARWIN_IOKIT_LOG(("dictionary] =\n"
263 "%-*s{\n", (int)(uintptr_t)pvUser, ""));
264 CFDictionaryApplyFunction((CFDictionaryRef)pvValue, darwinDumpDictCallback, (void *)((uintptr_t)pvUser + 4));
265 DARWIN_IOKIT_LOG(("%-*s}\n", (int)(uintptr_t)pvUser, ""));
266 }
267 else if (Type == CFNumberGetTypeID())
268 {
269 union
270 {
271 SInt8 s8;
272 SInt16 s16;
273 SInt32 s32;
274 SInt64 s64;
275 Float32 rf32;
276 Float64 rd64;
277 char ch;
278 short s;
279 int i;
280 long l;
281 long long ll;
282 float rf;
283 double rd;
284 CFIndex iCF;
285 } u;
286 memset(&u, 0, sizeof(u));
287 CFNumberType NumType = CFNumberGetType((CFNumberRef)pvValue);
288 if (CFNumberGetValue((CFNumberRef)pvValue, NumType, &u))
289 {
290 switch (CFNumberGetType((CFNumberRef)pvValue))
291 {
292 case kCFNumberSInt8Type: DARWIN_IOKIT_LOG(("SInt8] = %RI8 (%#RX8)\n", NumType, u.s8, u.s8)); break;
293 case kCFNumberSInt16Type: DARWIN_IOKIT_LOG(("SInt16] = %RI16 (%#RX16)\n", NumType, u.s16, u.s16)); break;
294 case kCFNumberSInt32Type: DARWIN_IOKIT_LOG(("SInt32] = %RI32 (%#RX32)\n", NumType, u.s32, u.s32)); break;
295 case kCFNumberSInt64Type: DARWIN_IOKIT_LOG(("SInt64] = %RI64 (%#RX64)\n", NumType, u.s64, u.s64)); break;
296 case kCFNumberFloat32Type: DARWIN_IOKIT_LOG(("float32] = %#lx\n", NumType, u.l)); break;
297 case kCFNumberFloat64Type: DARWIN_IOKIT_LOG(("float64] = %#llx\n", NumType, u.ll)); break;
298 case kCFNumberFloatType: DARWIN_IOKIT_LOG(("float] = %#lx\n", NumType, u.l)); break;
299 case kCFNumberDoubleType: DARWIN_IOKIT_LOG(("double] = %#llx\n", NumType, u.ll)); break;
300 case kCFNumberCharType: DARWIN_IOKIT_LOG(("char] = %hhd (%hhx)\n", NumType, u.ch, u.ch)); break;
301 case kCFNumberShortType: DARWIN_IOKIT_LOG(("short] = %hd (%hx)\n", NumType, u.s, u.s)); break;
302 case kCFNumberIntType: DARWIN_IOKIT_LOG(("int] = %d (%#x)\n", NumType, u.i, u.i)); break;
303 case kCFNumberLongType: DARWIN_IOKIT_LOG(("long] = %ld (%#lx)\n", NumType, u.l, u.l)); break;
304 case kCFNumberLongLongType: DARWIN_IOKIT_LOG(("long long] = %lld (%#llx)\n", NumType, u.ll, u.ll)); break;
305 case kCFNumberCFIndexType: DARWIN_IOKIT_LOG(("CFIndex] = %lld (%#llx)\n", NumType, (long long)u.iCF, (long long)u.iCF)); break;
306 break;
307 default: DARWIN_IOKIT_LOG(("%d?] = %lld (%llx)\n", NumType, u.ll, u.ll)); break;
308 }
309 }
310 else
311 DARWIN_IOKIT_LOG(("number] = CFNumberGetValue failed\n"));
312 }
313 else if (Type == CFBooleanGetTypeID())
314 DARWIN_IOKIT_LOG(("boolean] = %RTbool\n", CFBooleanGetValue((CFBooleanRef)pvValue)));
315 else if (Type == CFStringGetTypeID())
316 {
317 DARWIN_IOKIT_LOG(("string] = "));
318 char *pszValue = (char *)RTMemTmpAlloc(16*_1K);
319 if (!CFStringGetCString((CFStringRef)pvValue, pszValue, 16*_1K, kCFStringEncodingUTF8))
320 strcpy(pszValue, "CFStringGetCString failure");
321 DARWIN_IOKIT_LOG(("\"%s\"\n", pszValue));
322 RTMemTmpFree(pszValue);
323 }
324 else
325 DARWIN_IOKIT_LOG(("??] = %p\n", pvValue));
326}
327
328
329/**
330 * Dumps a dictionary to the log.
331 *
332 * @param DictRef The dictionary to dump.
333 */
334static void darwinDumpDict(CFMutableDictionaryRef DictRef, unsigned cIndents)
335{
336 CFDictionaryApplyFunction(DictRef, darwinDumpDictCallback, (void *)(uintptr_t)cIndents);
337 DARWIN_IOKIT_LOG_FLUSH();
338}
339
340
341/**
342 * Dumps an I/O kit registry object and all it children.
343 * @param Object The object to dump.
344 * @param cIndents The number of indents to use.
345 */
346static void darwinDumpObjInt(io_object_t Object, unsigned cIndents)
347{
348 static io_string_t s_szPath;
349 kern_return_t krc = IORegistryEntryGetPath(Object, kIOServicePlane, s_szPath);
350 if (krc != KERN_SUCCESS)
351 strcpy(s_szPath, "IORegistryEntryGetPath failed");
352 DARWIN_IOKIT_LOG(("Dumping %p - %s:\n", (const void *)Object, s_szPath));
353
354 CFMutableDictionaryRef PropsRef = 0;
355 krc = IORegistryEntryCreateCFProperties(Object, &PropsRef, kCFAllocatorDefault, kNilOptions);
356 if (krc == KERN_SUCCESS)
357 {
358 darwinDumpDict(PropsRef, cIndents + 4);
359 CFRelease(PropsRef);
360 }
361
362 /*
363 * Children.
364 */
365 io_iterator_t Children;
366 krc = IORegistryEntryGetChildIterator(Object, kIOServicePlane, &Children);
367 if (krc == KERN_SUCCESS)
368 {
369 io_object_t Child;
370 while ((Child = IOIteratorNext(Children)))
371 {
372 darwinDumpObjInt(Child, cIndents + 4);
373 IOObjectRelease(Child);
374 }
375 IOObjectRelease(Children);
376 }
377 else
378 DARWIN_IOKIT_LOG(("IORegistryEntryGetChildIterator -> %#x\n", krc));
379}
380
381/**
382 * Dumps an I/O kit registry object and all it children.
383 * @param Object The object to dump.
384 */
385static void darwinDumpObj(io_object_t Object)
386{
387 darwinDumpObjInt(Object, 0);
388}
389
390#endif
391
392
393/**
394 * Notification data created by DarwinSubscribeUSBNotifications, used by
395 * the callbacks and finally freed by DarwinUnsubscribeUSBNotifications.
396 */
397typedef struct DARWINUSBNOTIFY
398{
399 /** The notification port.
400 * It's shared between the notification callbacks. */
401 IONotificationPortRef NotifyPort;
402 /** The run loop source for NotifyPort. */
403 CFRunLoopSourceRef NotifyRLSrc;
404 /** The attach notification iterator. */
405 io_iterator_t AttachIterator;
406 /** The 2nd attach notification iterator. */
407 io_iterator_t AttachIterator2;
408 /** The detach notificaiton iterator. */
409 io_iterator_t DetachIterator;
410} DARWINUSBNOTIFY, *PDARWINUSBNOTIFY;
411
412
413/**
414 * Run thru an interrator.
415 *
416 * The docs says this is necessary to start getting notifications,
417 * so this function is called in the callbacks and right after
418 * registering the notification.
419 *
420 * @param pIterator The iterator reference.
421 */
422static void darwinDrainIterator(io_iterator_t pIterator)
423{
424 io_object_t Object;
425 while ((Object = IOIteratorNext(pIterator)))
426 {
427 DARWIN_IOKIT_DUMP_OBJ(Object);
428 IOObjectRelease(Object);
429 }
430}
431
432
433/**
434 * Callback for the 1st attach notification.
435 *
436 * @param pvNotify Our data.
437 * @param NotifyIterator The notification iterator.
438 */
439static void darwinUSBAttachNotification1(void *pvNotify, io_iterator_t NotifyIterator)
440{
441 DARWIN_IOKIT_LOG(("USB Attach Notification1\n"));
442 NOREF(pvNotify); //PDARWINUSBNOTIFY pNotify = (PDARWINUSBNOTIFY)pvNotify;
443 darwinDrainIterator(NotifyIterator);
444}
445
446
447/**
448 * Callback for the 2nd attach notification.
449 *
450 * @param pvNotify Our data.
451 * @param NotifyIterator The notification iterator.
452 */
453static void darwinUSBAttachNotification2(void *pvNotify, io_iterator_t NotifyIterator)
454{
455 DARWIN_IOKIT_LOG(("USB Attach Notification2\n"));
456 NOREF(pvNotify); //PDARWINUSBNOTIFY pNotify = (PDARWINUSBNOTIFY)pvNotify;
457 darwinDrainIterator(NotifyIterator);
458}
459
460
461/**
462 * Callback for the detach notifications.
463 *
464 * @param pvNotify Our data.
465 * @param NotifyIterator The notification iterator.
466 */
467static void darwinUSBDetachNotification(void *pvNotify, io_iterator_t NotifyIterator)
468{
469 DARWIN_IOKIT_LOG(("USB Detach Notification\n"));
470 NOREF(pvNotify); //PDARWINUSBNOTIFY pNotify = (PDARWINUSBNOTIFY)pvNotify;
471 darwinDrainIterator(NotifyIterator);
472}
473
474
475/**
476 * Subscribes the run loop to USB notification events relevant to
477 * device attach/detach.
478 *
479 * The source mode for these events is defined as VBOX_IOKIT_MODE_STRING
480 * so that the caller can listen to events from this mode only and
481 * re-evalutate the list of attached devices whenever an event arrives.
482 *
483 * @returns opaque for passing to the unsubscribe function. If NULL
484 * something unexpectedly failed during subscription.
485 */
486void *DarwinSubscribeUSBNotifications(void)
487{
488 AssertReturn(darwinOpenMasterPort(), NULL);
489
490 PDARWINUSBNOTIFY pNotify = (PDARWINUSBNOTIFY)RTMemAllocZ(sizeof(*pNotify));
491 AssertReturn(pNotify, NULL);
492
493 /*
494 * Create the notification port, bake it into a runloop source which we
495 * then add to our run loop.
496 */
497 pNotify->NotifyPort = IONotificationPortCreate(g_MasterPort);
498 Assert(pNotify->NotifyPort);
499 if (pNotify->NotifyPort)
500 {
501 pNotify->NotifyRLSrc = IONotificationPortGetRunLoopSource(pNotify->NotifyPort);
502 Assert(pNotify->NotifyRLSrc);
503 if (pNotify->NotifyRLSrc)
504 {
505 CFRunLoopRef RunLoopRef = CFRunLoopGetCurrent();
506 CFRetain(RunLoopRef); /* Workaround for crash when cleaning up the TLS / runloop((sub)mode). See #2807. */
507 CFRunLoopAddSource(RunLoopRef, pNotify->NotifyRLSrc, CFSTR(VBOX_IOKIT_MODE_STRING));
508
509 /*
510 * Create the notifcation callbacks.
511 */
512 kern_return_t rc = IOServiceAddMatchingNotification(pNotify->NotifyPort,
513 kIOPublishNotification,
514 IOServiceMatching(kIOUSBDeviceClassName),
515 darwinUSBAttachNotification1,
516 pNotify,
517 &pNotify->AttachIterator);
518 if (rc == KERN_SUCCESS)
519 {
520 darwinDrainIterator(pNotify->AttachIterator);
521 rc = IOServiceAddMatchingNotification(pNotify->NotifyPort,
522 kIOMatchedNotification,
523 IOServiceMatching(kIOUSBDeviceClassName),
524 darwinUSBAttachNotification2,
525 pNotify,
526 &pNotify->AttachIterator2);
527 if (rc == KERN_SUCCESS)
528 {
529 darwinDrainIterator(pNotify->AttachIterator2);
530 rc = IOServiceAddMatchingNotification(pNotify->NotifyPort,
531 kIOTerminatedNotification,
532 IOServiceMatching(kIOUSBDeviceClassName),
533 darwinUSBDetachNotification,
534 pNotify,
535 &pNotify->DetachIterator);
536 {
537 darwinDrainIterator(pNotify->DetachIterator);
538 return pNotify;
539 }
540 IOObjectRelease(pNotify->AttachIterator2);
541 }
542 IOObjectRelease(pNotify->AttachIterator);
543 }
544 CFRunLoopRemoveSource(RunLoopRef, pNotify->NotifyRLSrc, CFSTR(VBOX_IOKIT_MODE_STRING));
545 }
546 IONotificationPortDestroy(pNotify->NotifyPort);
547 }
548
549 RTMemFree(pNotify);
550 return NULL;
551}
552
553
554/**
555 * Unsubscribe the run loop from USB notification subscribed to
556 * by DarwinSubscribeUSBNotifications.
557 *
558 * @param pvOpaque The return value from DarwinSubscribeUSBNotifications.
559 */
560void DarwinUnsubscribeUSBNotifications(void *pvOpaque)
561{
562 PDARWINUSBNOTIFY pNotify = (PDARWINUSBNOTIFY)pvOpaque;
563 if (!pNotify)
564 return;
565
566 IOObjectRelease(pNotify->AttachIterator);
567 pNotify->AttachIterator = NULL;
568 IOObjectRelease(pNotify->AttachIterator2);
569 pNotify->AttachIterator2 = NULL;
570 IOObjectRelease(pNotify->DetachIterator);
571 pNotify->DetachIterator = NULL;
572
573 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), pNotify->NotifyRLSrc, CFSTR(VBOX_IOKIT_MODE_STRING));
574 IONotificationPortDestroy(pNotify->NotifyPort);
575 pNotify->NotifyRLSrc = NULL;
576 pNotify->NotifyPort = NULL;
577
578 RTMemFree(pNotify);
579}
580
581
582/**
583 * Decends recursivly into a IORegistry tree locating the first object of a given class.
584 *
585 * The search is performed depth first.
586 *
587 * @returns Object reference if found, NULL if not.
588 * @param Object The current tree root.
589 * @param pszClass The name of the class we're looking for.
590 * @param pszNameBuf A scratch buffer for query the class name in to avoid
591 * wasting 128 bytes on an io_name_t object for every recursion.
592 */
593static io_object_t darwinFindObjectByClass(io_object_t Object, const char *pszClass, io_name_t pszNameBuf)
594{
595 io_iterator_t Children;
596 kern_return_t krc = IORegistryEntryGetChildIterator(Object, kIOServicePlane, &Children);
597 if (krc != KERN_SUCCESS)
598 return NULL;
599 io_object_t Child;
600 while ((Child = IOIteratorNext(Children)))
601 {
602 krc = IOObjectGetClass(Child, pszNameBuf);
603 if ( krc == KERN_SUCCESS
604 && !strcmp(pszNameBuf, pszClass))
605 break;
606
607 io_object_t GrandChild = darwinFindObjectByClass(Child, pszClass, pszNameBuf);
608 IOObjectRelease(Child);
609 if (GrandChild)
610 {
611 Child = GrandChild;
612 break;
613 }
614 }
615 IOObjectRelease(Children);
616 return Child;
617}
618
619
620/**
621 * Decends recursivly into IOUSBMassStorageClass tree to check whether
622 * the MSD is mounted or not.
623 *
624 * The current heuristic is to look for the IOMedia class.
625 *
626 * @returns true if mounted, false if not.
627 * @param MSDObj The IOUSBMassStorageClass object.
628 * @param pszNameBuf A scratch buffer for query the class name in to avoid
629 * wasting 128 bytes on an io_name_t object for every recursion.
630 */
631static bool darwinIsMassStorageInterfaceInUse(io_object_t MSDObj, io_name_t pszNameBuf)
632{
633 io_object_t MediaObj = darwinFindObjectByClass(MSDObj, "IOMedia", pszNameBuf);
634 if (MediaObj)
635 {
636 /* more checks? */
637 IOObjectRelease(MediaObj);
638 return true;
639 }
640 return false;
641}
642
643
644/**
645 * Worker function for DarwinGetUSBDevices() that tries to figure out
646 * what state the device is in and set enmState.
647 *
648 * This is mostly a matter of distinguishing between devices that nobody
649 * uses, devices that can be seized and devices that cannot be grabbed.
650 *
651 * @param pCur The USB device data.
652 * @param USBDevice The USB device object.
653 * @param PropsRef The USB device properties.
654 */
655static void darwinDeterminUSBDeviceState(PUSBDEVICE pCur, io_object_t USBDevice, CFMutableDictionaryRef PropsRef)
656{
657 /*
658 * Iterate the interfaces (among the children of the IOUSBDevice object).
659 */
660 io_iterator_t Interfaces;
661 kern_return_t krc = IORegistryEntryGetChildIterator(USBDevice, kIOServicePlane, &Interfaces);
662 if (krc != KERN_SUCCESS)
663 return;
664
665 bool fHaveOwner = false;
666 RTPROCESS Owner = NIL_RTPROCESS;
667 bool fHaveClient = false;
668 RTPROCESS Client = NIL_RTPROCESS;
669 bool fUserClientOnly = true;
670 bool fConfigured = false;
671 bool fInUse = false;
672 bool fSeizable = true;
673 io_object_t Interface;
674 while ((Interface = IOIteratorNext(Interfaces)))
675 {
676 io_name_t szName;
677 krc = IOObjectGetClass(Interface, szName);
678 if ( krc == KERN_SUCCESS
679 && !strcmp(szName, "IOUSBInterface"))
680 {
681 fConfigured = true;
682
683 /*
684 * Iterate the interface children looking for stuff other than
685 * IOUSBUserClientInit objects.
686 */
687 io_iterator_t Children1;
688 krc = IORegistryEntryGetChildIterator(Interface, kIOServicePlane, &Children1);
689 if (krc == KERN_SUCCESS)
690 {
691 io_object_t Child1;
692 while ((Child1 = IOIteratorNext(Children1)))
693 {
694 krc = IOObjectGetClass(Child1, szName);
695 if ( krc == KERN_SUCCESS
696 && strcmp(szName, "IOUSBUserClientInit"))
697 {
698 fUserClientOnly = false;
699
700 if (!strcmp(szName, "IOUSBMassStorageClass"))
701 {
702 /* Only permit capturing MSDs that aren't mounted, at least
703 until the GUI starts poping up warnings about data loss
704 and such when capturing a busy device. */
705 fSeizable = false;
706 fInUse |= darwinIsMassStorageInterfaceInUse(Child1, szName);
707 }
708 else if (!strcmp(szName, "IOUSBHIDDriver")
709 || !strcmp(szName, "AppleHIDMouse")
710 /** @todo more? */)
711 {
712 /* For now, just assume that all HID devices are inaccessible
713 because of the greedy HID service. */
714 fSeizable = false;
715 fInUse = true;
716 }
717 else
718 fInUse = true;
719 }
720 IOObjectRelease(Child1);
721 }
722 IOObjectRelease(Children1);
723 }
724 }
725 /*
726 * Not an interface, could it be VBoxUSBDevice?
727 * If it is, get the owner and client properties.
728 */
729 else if ( krc == KERN_SUCCESS
730 && !strcmp(szName, VBOXUSBDEVICE_CLASS_NAME))
731 {
732 CFMutableDictionaryRef PropsRef = 0;
733 krc = IORegistryEntryCreateCFProperties(Interface, &PropsRef, kCFAllocatorDefault, kNilOptions);
734 if (krc == KERN_SUCCESS)
735 {
736 fHaveOwner = darwinDictGetProccess(PropsRef, CFSTR(VBOXUSB_OWNER_KEY), &Owner);
737 fHaveClient = darwinDictGetProccess(PropsRef, CFSTR(VBOXUSB_CLIENT_KEY), &Client);
738 CFRelease(PropsRef);
739 }
740 }
741
742 IOObjectRelease(Interface);
743 }
744 IOObjectRelease(Interfaces);
745
746 /*
747 * Calc the status.
748 */
749 if (fHaveOwner)
750 {
751 if (Owner == RTProcSelf())
752 pCur->enmState = !fHaveClient || Client == NIL_RTPROCESS || !Client
753 ? USBDEVICESTATE_HELD_BY_PROXY
754 : USBDEVICESTATE_USED_BY_GUEST;
755 else
756 pCur->enmState = USBDEVICESTATE_USED_BY_HOST;
757 }
758 else if (fUserClientOnly)
759 /** @todo how to detect other user client?!? - Look for IOUSBUserClient! */
760 pCur->enmState = !fConfigured
761 ? USBDEVICESTATE_UNUSED
762 : USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
763 else if (!fInUse)
764 pCur->enmState = USBDEVICESTATE_UNUSED;
765 else
766 pCur->enmState = fSeizable
767 ? USBDEVICESTATE_USED_BY_HOST_CAPTURABLE
768 : USBDEVICESTATE_USED_BY_HOST;
769}
770
771
772/**
773 * Enumerate the USB devices returning a FIFO of them.
774 *
775 * @returns Pointer to the head.
776 * USBProxyService::freeDevice is expected to free each of the list elements.
777 */
778PUSBDEVICE DarwinGetUSBDevices(void)
779{
780 AssertReturn(darwinOpenMasterPort(), NULL);
781 //DARWIN_IOKIT_LOG(("DarwinGetUSBDevices\n"));
782
783 /*
784 * Create a matching dictionary for searching for USB Devices in the IOKit.
785 */
786 CFMutableDictionaryRef RefMatchingDict = IOServiceMatching(kIOUSBDeviceClassName);
787 AssertReturn(RefMatchingDict, NULL);
788
789 /*
790 * Perform the search and get a collection of USB Device back.
791 */
792 io_iterator_t USBDevices = NULL;
793 IOReturn rc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &USBDevices);
794 AssertMsgReturn(rc == kIOReturnSuccess, ("rc=%d\n", rc), NULL);
795 RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
796
797 /*
798 * Enumerate the USB Devices.
799 */
800 PUSBDEVICE pHead = NULL;
801 PUSBDEVICE pTail = NULL;
802 unsigned i = 0;
803 io_object_t USBDevice;
804 while ((USBDevice = IOIteratorNext(USBDevices)) != 0)
805 {
806 //DARWIN_IOKIT_DUMP_OBJ(USBDevice);
807
808 /*
809 * Query the device properties from the registry.
810 *
811 * We could alternatively use the device and such, but that will be
812 * slower and we would have to resort to the registry for the three
813 * string anyway.
814 */
815 CFMutableDictionaryRef PropsRef = 0;
816 kern_return_t krc = IORegistryEntryCreateCFProperties(USBDevice, &PropsRef, kCFAllocatorDefault, kNilOptions);
817 if (krc == KERN_SUCCESS)
818 {
819 bool fOk = false;
820 PUSBDEVICE pCur = (PUSBDEVICE)RTMemAllocZ(sizeof(*pCur));
821 do /* loop for breaking out of on failure. */
822 {
823 AssertBreakVoid(pCur);
824
825 /*
826 * Mandatory
827 */
828 pCur->bcdUSB = 0; /* we've no idea. */
829 pCur->enmState = USBDEVICESTATE_USED_BY_HOST_CAPTURABLE; /* just a default, we'll try harder in a bit. */
830
831 AssertBreakVoid(darwinDictGetU8(PropsRef, CFSTR(kUSBDeviceClass), &pCur->bDeviceClass));
832 /* skip hubs */
833 if (pCur->bDeviceClass == 0x09 /* hub, find a define! */)
834 break;
835 AssertBreakVoid(darwinDictGetU8(PropsRef, CFSTR(kUSBDeviceSubClass), &pCur->bDeviceSubClass));
836 AssertBreakVoid(darwinDictGetU8(PropsRef, CFSTR(kUSBDeviceProtocol), &pCur->bDeviceProtocol));
837 AssertBreakVoid(darwinDictGetU16(PropsRef, CFSTR(kUSBVendorID), &pCur->idVendor));
838 AssertBreakVoid(darwinDictGetU16(PropsRef, CFSTR(kUSBProductID), &pCur->idProduct));
839 AssertBreakVoid(darwinDictGetU16(PropsRef, CFSTR(kUSBDeviceReleaseNumber), &pCur->bcdDevice));
840 uint32_t u32LocationId;
841 AssertBreakVoid(darwinDictGetU32(PropsRef, CFSTR(kUSBDevicePropertyLocationID), &u32LocationId));
842 uint64_t u64SessionId;
843 AssertBreakVoid(darwinDictGetU64(PropsRef, CFSTR("sessionID"), &u64SessionId));
844 char szAddress[64];
845 RTStrPrintf(szAddress, sizeof(szAddress), "p=0x%04RX16;v=0x%04RX16;s=0x%016RX64;l=0x%08RX32",
846 pCur->idProduct, pCur->idVendor, u64SessionId, u32LocationId);
847 pCur->pszAddress = RTStrDup(szAddress);
848 AssertBreakVoid(pCur->pszAddress);
849 pCur->bBus = u32LocationId >> 24;
850 AssertBreakVoid(darwinDictGetU8(PropsRef, CFSTR("PortNum"), &pCur->bPort));
851 uint8_t bSpeed;
852 AssertBreakVoid(darwinDictGetU8(PropsRef, CFSTR(kUSBDevicePropertySpeed), &bSpeed));
853 Assert(bSpeed <= 2);
854 pCur->enmSpeed = bSpeed == 2 ? USBDEVICESPEED_HIGH
855 : bSpeed == 1 ? USBDEVICESPEED_FULL
856 : bSpeed == 0 ? USBDEVICESPEED_LOW
857 : USBDEVICESPEED_UNKNOWN;
858
859 /*
860 * Optional.
861 * There are some nameless device in the iMac, apply names to them.
862 */
863 darwinDictGetString(PropsRef, CFSTR("USB Vendor Name"), (char **)&pCur->pszManufacturer);
864 if ( !pCur->pszManufacturer
865 && pCur->idVendor == kIOUSBVendorIDAppleComputer)
866 pCur->pszManufacturer = RTStrDup("Apple Computer, Inc.");
867 darwinDictGetString(PropsRef, CFSTR("USB Product Name"), (char **)&pCur->pszProduct);
868 if ( !pCur->pszProduct
869 && pCur->bDeviceClass == 224 /* Wireless */
870 && pCur->bDeviceSubClass == 1 /* Radio Frequency */
871 && pCur->bDeviceProtocol == 1 /* Bluetooth */)
872 pCur->pszProduct = RTStrDup("Bluetooth");
873 darwinDictGetString(PropsRef, CFSTR("USB Serial Number"), (char **)&pCur->pszSerialNumber);
874
875#if 0 /* leave the remainder as zero for now. */
876 /*
877 * Create a plugin interface for the service and query its USB Device interface.
878 */
879 SInt32 Score = 0;
880 IOCFPlugInInterface **ppPlugInInterface = NULL;
881 rc = IOCreatePlugInInterfaceForService(USBDevice, kIOUSBDeviceUserClientTypeID,
882 kIOCFPlugInInterfaceID, &ppPlugInInterface, &Score);
883 if (rc == kIOReturnSuccess)
884 {
885 IOUSBDeviceInterface245 **ppUSBDevI = NULL;
886 HRESULT hrc = (*ppPlugInInterface)->QueryInterface(ppPlugInInterface,
887 CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID245),
888 (LPVOID *)&ppUSBDevI);
889 rc = IODestroyPlugInInterface(ppPlugInInterface); Assert(rc == kIOReturnSuccess);
890 ppPlugInInterface = NULL;
891 if (hrc == S_OK)
892 {
893 /** @todo enumerate configurations and interfaces if we actually need them. */
894 //IOReturn (*GetNumberOfConfigurations)(void *self, UInt8 *numConfig);
895 //IOReturn (*GetConfigurationDescriptorPtr)(void *self, UInt8 configIndex, IOUSBConfigurationDescriptorPtr *desc);
896 //IOReturn (*CreateInterfaceIterator)(void *self, IOUSBFindInterfaceRequest *req, io_iterator_t *iter);
897 }
898 long cReft = (*ppUSBDeviceInterface)->Release(ppUSBDeviceInterface); MY_CHECK_CREFS(cRefs);
899 }
900#endif
901 /*
902 * Try determin the state.
903 */
904 darwinDeterminUSBDeviceState(pCur, USBDevice, PropsRef);
905
906 /*
907 * We're good. Link the device.
908 */
909 pCur->pPrev = pTail;
910 if (pTail)
911 pTail = pTail->pNext = pCur;
912 else
913 pTail = pHead = pCur;
914 fOk = true;
915 } while (0);
916
917 /* cleanup on failure / skipped device. */
918 if (!fOk && pCur)
919 DarwinFreeUSBDeviceFromIOKit(pCur);
920
921 CFRelease(PropsRef);
922 }
923 else
924 AssertMsgFailed(("krc=%#x\n", krc));
925
926 IOObjectRelease(USBDevice);
927 i++;
928 }
929
930 IOObjectRelease(USBDevices);
931 //DARWIN_IOKIT_LOG_FLUSH();
932
933 /*
934 * Some post processing. There are a couple of things we have to
935 * make 100% sure about, and that is that the (Apple) keyboard
936 * and mouse most likely to be in use by the user aren't available
937 * for capturing. If there is no Apple mouse or keyboard we'll
938 * take the first one from another vendor.
939 */
940 /* As it turns out, the HID service will take all keyboards and mice
941 and we're not currently able to seize them. */
942 PUSBDEVICE pMouse = NULL;
943 PUSBDEVICE pKeyboard = NULL;
944 for (PUSBDEVICE pCur = pHead; pCur; pCur = pCur->pNext)
945 if (pCur->idVendor == kIOUSBVendorIDAppleComputer)
946 {
947 /*
948 * This test is a bit rough, should check device class/protocol but
949 * we don't have interface info yet so that might be a bit tricky.
950 */
951 if ( ( !pKeyboard
952 || pKeyboard->idVendor != kIOUSBVendorIDAppleComputer)
953 && pCur->pszProduct
954 && strstr(pCur->pszProduct, " Keyboard"))
955 pKeyboard = pCur;
956 else if ( ( !pMouse
957 || pMouse->idVendor != kIOUSBVendorIDAppleComputer)
958 && pCur->pszProduct
959 && strstr(pCur->pszProduct, " Mouse")
960 )
961 pMouse = pCur;
962 }
963 else if (!pKeyboard || !pMouse)
964 {
965 if ( pCur->bDeviceClass == 3 /* HID */
966 && pCur->bDeviceProtocol == 1 /* Keyboard */)
967 pKeyboard = pCur;
968 else if ( pCur->bDeviceClass == 3 /* HID */
969 && pCur->bDeviceProtocol == 2 /* Mouse */)
970 pMouse = pCur;
971 /** @todo examin interfaces */
972 }
973
974 if (pKeyboard)
975 pKeyboard->enmState = USBDEVICESTATE_USED_BY_HOST;
976 if (pMouse)
977 pMouse->enmState = USBDEVICESTATE_USED_BY_HOST;
978
979 return pHead;
980}
981
982
983/**
984 * Triggers re-enumeration of a device.
985 *
986 * @returns VBox status code.
987 * @param pCur The USBDEVICE structure for the device.
988 */
989int DarwinReEnumerateUSBDevice(PCUSBDEVICE pCur)
990{
991 int vrc;
992 const char *pszAddress = pCur->pszAddress;
993 AssertPtrReturn(pszAddress, VERR_INVALID_POINTER);
994 AssertReturn(darwinOpenMasterPort(), VERR_GENERAL_FAILURE);
995
996 /*
997 * This code is a short version of the Open method in USBProxyDevice-darwin.cpp stuff.
998 * Fixes made to this code probably applies there too!
999 */
1000
1001 CFMutableDictionaryRef RefMatchingDict = IOServiceMatching(kIOUSBDeviceClassName);
1002 AssertReturn(RefMatchingDict, NULL);
1003
1004 uint64_t u64SessionId = 0;
1005 uint32_t u32LocationId = 0;
1006 const char *psz = pszAddress;
1007 do
1008 {
1009 const char chValue = *psz;
1010 AssertReleaseReturn(psz[1] == '=', VERR_INTERNAL_ERROR);
1011 uint64_t u64Value;
1012 int rc = RTStrToUInt64Ex(psz + 2, (char **)&psz, 0, &u64Value);
1013 AssertReleaseRCReturn(rc, rc);
1014 AssertReleaseReturn(!*psz || *psz == ';', rc);
1015 switch (chValue)
1016 {
1017 case 'l':
1018 u32LocationId = (uint32_t)u64Value;
1019 break;
1020 case 's':
1021 u64SessionId = u64Value;
1022 break;
1023 case 'p':
1024 case 'v':
1025 {
1026#if 0 /* Guess what, this doesn't 'ing work either! */
1027 SInt32 i32 = (int16_t)u64Value;
1028 CFNumberRef Num = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &i32);
1029 AssertBreakVoid(Num);
1030 CFDictionarySetValue(RefMatchingDict, chValue == 'p' ? CFSTR(kUSBProductID) : CFSTR(kUSBVendorID), Num);
1031 CFRelease(Num);
1032#endif
1033 break;
1034 }
1035 default:
1036 AssertReleaseMsgFailedReturn(("chValue=%#x\n", chValue), VERR_INTERNAL_ERROR);
1037 }
1038 if (*psz == ';')
1039 psz++;
1040 } while (*psz);
1041
1042 io_iterator_t USBDevices = NULL;
1043 IOReturn irc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &USBDevices);
1044 AssertMsgReturn(irc == kIOReturnSuccess, ("irc=%#x\n", irc), NULL);
1045 RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
1046
1047 unsigned cMatches = 0;
1048 io_object_t USBDevice;
1049 while ((USBDevice = IOIteratorNext(USBDevices)))
1050 {
1051 cMatches++;
1052 CFMutableDictionaryRef PropsRef = 0;
1053 kern_return_t krc = IORegistryEntryCreateCFProperties(USBDevice, &PropsRef, kCFAllocatorDefault, kNilOptions);
1054 if (krc == KERN_SUCCESS)
1055 {
1056 uint64_t u64CurSessionId;
1057 uint32_t u32CurLocationId;
1058 if ( ( !u64SessionId
1059 || ( darwinDictGetU64(PropsRef, CFSTR("sessionID"), &u64CurSessionId)
1060 && u64CurSessionId == u64SessionId))
1061 && ( !u32LocationId
1062 || ( darwinDictGetU32(PropsRef, CFSTR(kUSBDevicePropertyLocationID), &u32CurLocationId)
1063 && u32CurLocationId == u32LocationId))
1064 )
1065 {
1066 CFRelease(PropsRef);
1067 break;
1068 }
1069 CFRelease(PropsRef);
1070 }
1071 IOObjectRelease(USBDevice);
1072 }
1073 IOObjectRelease(USBDevices);
1074 USBDevices = NULL;
1075 if (!USBDevice)
1076 {
1077 LogRel(("USB: Device '%s' not found (%d pid+vid matches)\n", pszAddress, cMatches));
1078 IOObjectRelease(USBDevices);
1079 return VERR_VUSB_DEVICE_NAME_NOT_FOUND;
1080 }
1081
1082 /*
1083 * Create a plugin interface for the device and query its IOUSBDeviceInterface.
1084 */
1085 SInt32 Score = 0;
1086 IOCFPlugInInterface **ppPlugInInterface = NULL;
1087 irc = IOCreatePlugInInterfaceForService(USBDevice, kIOUSBDeviceUserClientTypeID,
1088 kIOCFPlugInInterfaceID, &ppPlugInInterface, &Score);
1089 if (irc == kIOReturnSuccess)
1090 {
1091 IOUSBDeviceInterface245 **ppDevI = NULL;
1092 HRESULT hrc = (*ppPlugInInterface)->QueryInterface(ppPlugInInterface,
1093 CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID245),
1094 (LPVOID *)&ppDevI);
1095 irc = IODestroyPlugInInterface(ppPlugInInterface); Assert(irc == kIOReturnSuccess);
1096 ppPlugInInterface = NULL;
1097 if (hrc == S_OK)
1098 {
1099 /*
1100 * Try open the device for exclusive access.
1101 */
1102 irc = (*ppDevI)->USBDeviceOpenSeize(ppDevI);
1103 if (irc == kIOReturnExclusiveAccess)
1104 {
1105 RTThreadSleep(20);
1106 irc = (*ppDevI)->USBDeviceOpenSeize(ppDevI);
1107 }
1108 if (irc == kIOReturnSuccess)
1109 {
1110 /*
1111 * Re-enumerate the device and bail out.
1112 */
1113 irc = (*ppDevI)->USBDeviceReEnumerate(ppDevI, 0);
1114 if (irc == kIOReturnSuccess)
1115 vrc = VINF_SUCCESS;
1116 else
1117 {
1118 LogRel(("USB: Failed to open device '%s', plug-in creation failed with irc=%#x.\n", pszAddress, irc));
1119 vrc = RTErrConvertFromDarwinIO(irc);
1120 }
1121
1122 (*ppDevI)->USBDeviceClose(ppDevI);
1123 }
1124 else if (irc == kIOReturnExclusiveAccess)
1125 {
1126 LogRel(("USB: Device '%s' is being used by another process\n", pszAddress));
1127 vrc = VERR_SHARING_VIOLATION;
1128 }
1129 else
1130 {
1131 LogRel(("USB: Failed to open device '%s', irc=%#x.\n", pszAddress, irc));
1132 vrc = VERR_OPEN_FAILED;
1133 }
1134 }
1135 else
1136 {
1137 LogRel(("USB: Failed to create plugin interface for device '%s', hrc=%#x.\n", pszAddress, hrc));
1138 vrc = VERR_OPEN_FAILED;
1139 }
1140
1141 (*ppDevI)->Release(ppDevI);
1142 }
1143 else
1144 {
1145 LogRel(("USB: Failed to open device '%s', plug-in creation failed with irc=%#x.\n", pszAddress, irc));
1146 vrc = RTErrConvertFromDarwinIO(irc);
1147 }
1148
1149 return vrc;
1150}
1151
1152#endif /* VBOX_WITH_USB */
1153
1154
1155/**
1156 * Enumerate the DVD drives returning a FIFO of device name strings.
1157 *
1158 * @returns Pointer to the head.
1159 * The caller is responsible for calling RTMemFree() on each of the nodes.
1160 */
1161PDARWINDVD DarwinGetDVDDrives(void)
1162{
1163 AssertReturn(darwinOpenMasterPort(), NULL);
1164
1165 /*
1166 * Create a matching dictionary for searching for DVD services in the IOKit.
1167 *
1168 * [If I understand this correctly, plain CDROMs doesn't show up as
1169 * IODVDServices. Too keep things simple, we will only support DVDs
1170 * until somebody complains about it and we get hardware to test it on.
1171 * (Unless I'm much mistaken, there aren't any (orignal) intel macs with
1172 * plain cdroms.)]
1173 */
1174 CFMutableDictionaryRef RefMatchingDict = IOServiceMatching("IODVDServices");
1175 AssertReturn(RefMatchingDict, NULL);
1176
1177 /*
1178 * Perform the search and get a collection of DVD services.
1179 */
1180 io_iterator_t DVDServices = NULL;
1181 IOReturn rc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &DVDServices);
1182 AssertMsgReturn(rc == kIOReturnSuccess, ("rc=%d\n", rc), NULL);
1183 RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
1184
1185 /*
1186 * Enumerate the DVD services.
1187 * (This enumeration must be identical to the one performed in DrvHostBase.cpp.)
1188 */
1189 PDARWINDVD pHead = NULL;
1190 PDARWINDVD pTail = NULL;
1191 unsigned i = 0;
1192 io_object_t DVDService;
1193 while ((DVDService = IOIteratorNext(DVDServices)) != 0)
1194 {
1195 /*
1196 * Get the properties we use to identify the DVD drive.
1197 *
1198 * While there is a (weird 12 byte) GUID, it isn't persistent
1199 * accross boots. So, we have to use a combination of the
1200 * vendor name and product name properties with an optional
1201 * sequence number for identification.
1202 */
1203 CFMutableDictionaryRef PropsRef = 0;
1204 kern_return_t krc = IORegistryEntryCreateCFProperties(DVDService, &PropsRef, kCFAllocatorDefault, kNilOptions);
1205 if (krc == KERN_SUCCESS)
1206 {
1207 /* Get the Device Characteristics dictionary. */
1208 CFDictionaryRef DevCharRef = (CFDictionaryRef)CFDictionaryGetValue(PropsRef, CFSTR(kIOPropertyDeviceCharacteristicsKey));
1209 if (DevCharRef)
1210 {
1211 /* The vendor name. */
1212 char szVendor[128];
1213 char *pszVendor = &szVendor[0];
1214 CFTypeRef ValueRef = CFDictionaryGetValue(DevCharRef, CFSTR(kIOPropertyVendorNameKey));
1215 if ( ValueRef
1216 && CFGetTypeID(ValueRef) == CFStringGetTypeID()
1217 && CFStringGetCString((CFStringRef)ValueRef, szVendor, sizeof(szVendor), kCFStringEncodingUTF8))
1218 pszVendor = RTStrStrip(szVendor);
1219 else
1220 *pszVendor = '\0';
1221
1222 /* The product name. */
1223 char szProduct[128];
1224 char *pszProduct = &szProduct[0];
1225 ValueRef = CFDictionaryGetValue(DevCharRef, CFSTR(kIOPropertyProductNameKey));
1226 if ( ValueRef
1227 && CFGetTypeID(ValueRef) == CFStringGetTypeID()
1228 && CFStringGetCString((CFStringRef)ValueRef, szProduct, sizeof(szProduct), kCFStringEncodingUTF8))
1229 pszProduct = RTStrStrip(szProduct);
1230 else
1231 *pszProduct = '\0';
1232
1233 /* Construct the name and check for duplicates. */
1234 char szName[256 + 32];
1235 if (*pszVendor || *pszProduct)
1236 {
1237 if (*pszVendor && *pszProduct)
1238 RTStrPrintf(szName, sizeof(szName), "%s %s", pszVendor, pszProduct);
1239 else
1240 strcpy(szName, *pszVendor ? pszVendor : pszProduct);
1241
1242 for (PDARWINDVD pCur = pHead; pCur; pCur = pCur->pNext)
1243 {
1244 if (!strcmp(szName, pCur->szName))
1245 {
1246 if (*pszVendor && *pszProduct)
1247 RTStrPrintf(szName, sizeof(szName), "%s %s (#%u)", pszVendor, pszProduct, i);
1248 else
1249 RTStrPrintf(szName, sizeof(szName), "%s %s (#%u)", *pszVendor ? pszVendor : pszProduct, i);
1250 break;
1251 }
1252 }
1253 }
1254 else
1255 RTStrPrintf(szName, sizeof(szName), "(#%u)", i);
1256
1257 /* Create the device. */
1258 size_t cbName = strlen(szName) + 1;
1259 PDARWINDVD pNew = (PDARWINDVD)RTMemAlloc(RT_OFFSETOF(DARWINDVD, szName[cbName]));
1260 if (pNew)
1261 {
1262 pNew->pNext = NULL;
1263 memcpy(pNew->szName, szName, cbName);
1264 if (pTail)
1265 pTail = pTail->pNext = pNew;
1266 else
1267 pTail = pHead = pNew;
1268 }
1269 }
1270 CFRelease(PropsRef);
1271 }
1272 else
1273 AssertMsgFailed(("krc=%#x\n", krc));
1274
1275 IOObjectRelease(DVDService);
1276 i++;
1277 }
1278
1279 IOObjectRelease(DVDServices);
1280
1281 return pHead;
1282}
1283
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