VirtualBox

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

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

status fixes.

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