VirtualBox

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

最後變更 在這個檔案從3670是 3665,由 vboxsync 提交於 18 年 前

Detect HID and in-use MSDs. Currently polling for changes every 5 seconds.

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

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