VirtualBox

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

Last change on this file since 3354 was 2981, checked in by vboxsync, 18 years ago

InnoTek -> innotek: all the headers and comments.

  • Property svn:eol-style set to native
File size: 25.3 KB
Line 
1/** $Id: $ */
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.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 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#include <mach/mach.h>
31#include <Carbon/Carbon.h>
32#include <IOKit/IOKitLib.h>
33#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
34#include <IOKit/scsi-commands/SCSITaskLib.h>
35#include <mach/mach_error.h>
36#ifdef VBOX_WITH_USB
37# include <IOKit/usb/IOUSBLib.h>
38# include <IOKit/IOCFPlugIn.h>
39#endif
40
41#include <iprt/mem.h>
42#include <iprt/string.h>
43#include <iprt/assert.h>
44
45#include "iokit.h"
46
47
48/*******************************************************************************
49* Defined Constants And Macros *
50*******************************************************************************/
51/** An attempt at catching reference leaks. */
52#define MY_CHECK_CREFS(cRefs) do { AssertMsg(cRefs < 25, ("%ld\n", cRefs)); NOREF(cRefs); } while (0)
53
54
55/*******************************************************************************
56* Global Variables *
57*******************************************************************************/
58/** The IO Master Port. */
59static mach_port_t g_MasterPort = NULL;
60
61
62/**
63 * Lazily opens the master port.
64 *
65 * @returns true if the port is open, false on failure (very unlikely).
66 */
67static bool darwinOpenMasterPort(void)
68{
69 if (!g_MasterPort)
70 {
71 kern_return_t krc = IOMasterPort(MACH_PORT_NULL, &g_MasterPort);
72 AssertReturn(krc == KERN_SUCCESS, false);
73 }
74 return true;
75}
76
77
78#ifdef VBOX_WITH_USB
79
80/**
81 * Gets an unsigned 8-bit integer value.
82 *
83 * @returns Success indicator (true/false).
84 * @param DictRef The dictionary.
85 * @param KeyStrRef The key name.
86 * @param pu8 Where to store the key value.
87 */
88static bool darwinDictGetU8(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, uint8_t *pu8)
89{
90 CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
91 if (ValRef)
92 {
93 if (CFNumberGetValue((CFNumberRef)ValRef, kCFNumberSInt8Type, pu8))
94 return true;
95 }
96 *pu8 = 0;
97 return false;
98}
99
100
101/**
102 * Gets an unsigned 16-bit integer value.
103 *
104 * @returns Success indicator (true/false).
105 * @param DictRef The dictionary.
106 * @param KeyStrRef The key name.
107 * @param pu16 Where to store the key value.
108 */
109static bool darwinDictGetU16(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, uint16_t *pu16)
110{
111 CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
112 if (ValRef)
113 {
114 if (CFNumberGetValue((CFNumberRef)ValRef, kCFNumberSInt16Type, pu16))
115 return true;
116 }
117 *pu16 = 0;
118 return false;
119}
120
121
122/**
123 * Gets an unsigned 32-bit integer value.
124 *
125 * @returns Success indicator (true/false).
126 * @param DictRef The dictionary.
127 * @param KeyStrRef The key name.
128 * @param pu32 Where to store the key value.
129 */
130static bool darwinDictGetU32(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, uint32_t *pu32)
131{
132 CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
133 if (ValRef)
134 {
135 if (CFNumberGetValue((CFNumberRef)ValRef, kCFNumberSInt32Type, pu32))
136 return true;
137 }
138 *pu32 = 0;
139 return false;
140}
141
142
143/**
144 * Gets an unsigned 64-bit integer value.
145 *
146 * @returns Success indicator (true/false).
147 * @param DictRef The dictionary.
148 * @param KeyStrRef The key name.
149 * @param pu64 Where to store the key value.
150 */
151static bool darwinDictGetU64(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, uint64_t *pu64)
152{
153 CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
154 if (ValRef)
155 {
156 if (CFNumberGetValue((CFNumberRef)ValRef, kCFNumberSInt64Type, pu64))
157 return true;
158 }
159 *pu64 = 0;
160 return false;
161}
162
163
164/**
165 * Gets string value, converted to UTF-8 and put in a IPRT string buffer.
166 *
167 * @returns Success indicator (true/false).
168 * @param DictRef The dictionary.
169 * @param KeyStrRef The key name.
170 * @param ppsz Where to store the key value. Free with RTStrFree. Set to NULL on failure.
171 */
172static bool darwinDictGetString(CFMutableDictionaryRef DictRef, CFStringRef KeyStrRef, char **ppsz)
173{
174 CFTypeRef ValRef = CFDictionaryGetValue(DictRef, KeyStrRef);
175 if (ValRef)
176 {
177 char szBuf[512];
178 if (CFStringGetCString((CFStringRef)ValRef, szBuf, sizeof(szBuf), kCFStringEncodingUTF8))
179 {
180 *ppsz = RTStrDup(RTStrStrip(szBuf));
181 if (*ppsz)
182 return true;
183 }
184 }
185 *ppsz = NULL;
186 return false;
187}
188
189
190/**
191 * Notification data created by DarwinSubscribeUSBNotifications, used by
192 * the callbacks and finally freed by DarwinUnsubscribeUSBNotifications.
193 */
194typedef struct DARWINUSBNOTIFY
195{
196 /** The notification port.
197 * It's shared between the notification callbacks. */
198 IONotificationPortRef NotifyPort;
199 /** The run loop source for NotifyPort. */
200 CFRunLoopSourceRef NotifyRLSrc;
201 /** The attach notification iterator. */
202 io_iterator_t AttachIterator;
203 /** The 2nd attach notification iterator. */
204 io_iterator_t AttachIterator2;
205 /** The detach notificaiton iterator. */
206 io_iterator_t DetachIterator;
207} DARWINUSBNOTIFY, *PDARWINUSBNOTIFY;
208
209
210/**
211 * Run thru an interrator.
212 *
213 * The docs says this is necessary to start getting notifications,
214 * so this function is called in the callbacks and right after
215 * registering the notification.
216 *
217 * @param pIterator The iterator reference.
218 */
219static void darwinDrainIterator(io_iterator_t pIterator)
220{
221 io_object_t Object;
222 while ((Object = IOIteratorNext(pIterator)))
223 IOObjectRelease(Object);
224}
225
226
227/**
228 * Callback for the two attach notifications.
229 *
230 * @param pvNotify Our data.
231 * @param NotifyIterator The notification iterator.
232 */
233static void darwinUSBAttachNotification(void *pvNotify, io_iterator_t NotifyIterator)
234{
235 NOREF(pvNotify); //PDARWINUSBNOTIFY pNotify = (PDARWINUSBNOTIFY)pvNotify;
236 darwinDrainIterator(NotifyIterator);
237}
238
239
240/**
241 * Callback for the detach notifications.
242 *
243 * @param pvNotify Our data.
244 * @param NotifyIterator The notification iterator.
245 */
246static void darwinUSBDetachNotification(void *pvNotify, io_iterator_t NotifyIterator)
247{
248 NOREF(pvNotify); //PDARWINUSBNOTIFY pNotify = (PDARWINUSBNOTIFY)pvNotify;
249 darwinDrainIterator(NotifyIterator);
250}
251
252
253/**
254 * Subscribes the run loop to USB notification events relevant to
255 * device attach/detach.
256 *
257 * The source mode for these events is defined as VBOX_IOKIT_MODE_STRING
258 * so that the caller can listen to events from this mode only and
259 * re-evalutate the list of attached devices whenever an event arrives.
260 *
261 * @returns opaque for passing to the unsubscribe function. If NULL
262 * something unexpectedly failed during subscription.
263 */
264void *DarwinSubscribeUSBNotifications(void)
265{
266 AssertReturn(darwinOpenMasterPort(), NULL);
267
268 PDARWINUSBNOTIFY pNotify = (PDARWINUSBNOTIFY)RTMemAllocZ(sizeof(*pNotify));
269 AssertReturn(pNotify, NULL);
270
271 /*
272 * Create the notification port, bake it into a runloop source which we
273 * then add to our run loop.
274 */
275 pNotify->NotifyPort = IONotificationPortCreate(g_MasterPort);
276 Assert(pNotify->NotifyPort);
277 if (pNotify->NotifyPort)
278 {
279 pNotify->NotifyRLSrc = IONotificationPortGetRunLoopSource(pNotify->NotifyPort);
280 Assert(pNotify->NotifyRLSrc);
281 if (pNotify->NotifyRLSrc)
282 {
283 CFRunLoopAddSource(CFRunLoopGetCurrent(), pNotify->NotifyRLSrc, CFSTR(VBOX_IOKIT_MODE_STRING));
284
285 /*
286 * Create the notifcation callbacks.
287 */
288 kern_return_t rc = IOServiceAddMatchingNotification(pNotify->NotifyPort,
289 kIOPublishNotification,
290 IOServiceMatching(kIOUSBDeviceClassName),
291 darwinUSBAttachNotification,
292 pNotify,
293 &pNotify->AttachIterator);
294 if (rc == KERN_SUCCESS)
295 {
296 darwinDrainIterator(pNotify->AttachIterator);
297 rc = IOServiceAddMatchingNotification(pNotify->NotifyPort,
298 kIOMatchedNotification,
299 IOServiceMatching(kIOUSBDeviceClassName),
300 darwinUSBAttachNotification,
301 pNotify,
302 &pNotify->AttachIterator2);
303 if (rc == KERN_SUCCESS)
304 {
305 darwinDrainIterator(pNotify->AttachIterator2);
306 rc = IOServiceAddMatchingNotification(pNotify->NotifyPort,
307 kIOTerminatedNotification,
308 IOServiceMatching(kIOUSBDeviceClassName),
309 darwinUSBDetachNotification,
310 pNotify,
311 &pNotify->DetachIterator);
312 {
313 darwinDrainIterator(pNotify->DetachIterator);
314 return pNotify;
315 }
316 IOObjectRelease(pNotify->AttachIterator2);
317 }
318 IOObjectRelease(pNotify->AttachIterator);
319 }
320 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), pNotify->NotifyRLSrc, CFSTR(VBOX_IOKIT_MODE_STRING));
321 }
322 IONotificationPortDestroy(pNotify->NotifyPort);
323 }
324
325 RTMemFree(pNotify);
326 return NULL;
327}
328
329
330/**
331 * Unsubscribe the run loop from USB notification subscribed to
332 * by DarwinSubscribeUSBNotifications.
333 *
334 * @param pvOpaque The return value from DarwinSubscribeUSBNotifications.
335 */
336void DarwinUnsubscribeUSBNotifications(void *pvOpaque)
337{
338 PDARWINUSBNOTIFY pNotify = (PDARWINUSBNOTIFY)pvOpaque;
339 if (!pNotify)
340 return;
341
342 IOObjectRelease(pNotify->AttachIterator);
343 pNotify->AttachIterator = NULL;
344 IOObjectRelease(pNotify->AttachIterator2);
345 pNotify->AttachIterator2 = NULL;
346 IOObjectRelease(pNotify->DetachIterator);
347 pNotify->DetachIterator = NULL;
348
349 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), pNotify->NotifyRLSrc, CFSTR(VBOX_IOKIT_MODE_STRING));
350 IONotificationPortDestroy(pNotify->NotifyPort);
351 pNotify->NotifyRLSrc = NULL;
352 pNotify->NotifyPort = NULL;
353
354 RTMemFree(pNotify);
355}
356
357
358/**
359 * Enumerate the USB devices returning a FIFO of them.
360 *
361 * @returns Pointer to the head.
362 * USBProxyService::freeDevice is expected to free each of the list elements.
363 */
364PUSBDEVICE DarwinGetUSBDevices(void)
365{
366 AssertReturn(darwinOpenMasterPort(), NULL);
367
368 /*
369 * Create a matching dictionary for searching for USB Devices in the IOKit.
370 */
371 CFMutableDictionaryRef RefMatchingDict = IOServiceMatching(kIOUSBDeviceClassName);
372 AssertReturn(RefMatchingDict, NULL);
373
374 /*
375 * Perform the search and get a collection of USB Device back.
376 */
377 io_iterator_t USBDevices = NULL;
378 IOReturn rc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &USBDevices);
379 AssertMsgReturn(rc == kIOReturnSuccess, ("rc=%d\n", rc), NULL);
380 RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
381
382 /*
383 * Enumerate the USB Devices.
384 */
385 PUSBDEVICE pHead = NULL;
386 PUSBDEVICE pTail = NULL;
387 unsigned i = 0;
388 io_object_t USBDevice;
389 while ((USBDevice = IOIteratorNext(USBDevices)) != 0)
390 {
391 /*
392 * Query the device properties from the registry.
393 *
394 * We could alternatively use the device and such, but that will be
395 * slower and we would have to resort to the registry for the three
396 * string anyway.
397 */
398 CFMutableDictionaryRef PropsRef = 0;
399 kern_return_t krc = IORegistryEntryCreateCFProperties(USBDevice, &PropsRef, kCFAllocatorDefault, kNilOptions);
400 if (krc == KERN_SUCCESS)
401 {
402 bool fOk = false;
403 PUSBDEVICE pCur = (PUSBDEVICE)RTMemAllocZ(sizeof(*pCur));
404 do /* loop for breaking out of on failure. */
405 {
406 AssertBreak(pCur,);
407
408 /*
409 * Mandatory
410 */
411 pCur->bcdUSB = 0; /* we've no idea. */
412 pCur->enmState = USBDEVICESTATE_USED_BY_HOST_CAPTURABLE; /* ditto. */
413
414 AssertBreak(darwinDictGetU8(PropsRef, CFSTR(kUSBDeviceClass), &pCur->bDeviceClass),);
415 /* skip hubs */
416 if (pCur->bDeviceClass == 0x09 /* hub, find a define! */)
417 break;
418 AssertBreak(darwinDictGetU8(PropsRef, CFSTR(kUSBDeviceSubClass), &pCur->bDeviceSubClass),);
419 AssertBreak(darwinDictGetU8(PropsRef, CFSTR(kUSBDeviceProtocol), &pCur->bDeviceProtocol),);
420 AssertBreak(darwinDictGetU16(PropsRef, CFSTR(kUSBVendorID), &pCur->idVendor),);
421 AssertBreak(darwinDictGetU16(PropsRef, CFSTR(kUSBProductID), &pCur->idProduct),);
422 AssertBreak(darwinDictGetU16(PropsRef, CFSTR(kUSBDeviceReleaseNumber), &pCur->bcdDevice),);
423 uint32_t u32LocationId;
424 AssertBreak(darwinDictGetU32(PropsRef, CFSTR(kUSBDevicePropertyLocationID), &u32LocationId),);
425 uint64_t u64SessionId;
426 AssertBreak(darwinDictGetU64(PropsRef, CFSTR("sessionID"), &u64SessionId),);
427 char szAddress[64];
428 RTStrPrintf(szAddress, sizeof(szAddress), "p=0x%04RX16;v=0x%04RX16;s=0x%016RX64;l=0x%08RX32",
429 pCur->idProduct, pCur->idVendor, u64SessionId, u32LocationId);
430 pCur->pszAddress = RTStrDup(szAddress);
431 AssertBreak(pCur->pszAddress,);
432
433 /*
434 * Optional.
435 * There are some nameless device in the iMac, apply names to them.
436 */
437 darwinDictGetString(PropsRef, CFSTR("USB Vendor Name"), (char **)&pCur->pszManufacturer);
438 if ( !pCur->pszManufacturer
439 && pCur->idVendor == kIOUSBVendorIDAppleComputer)
440 pCur->pszManufacturer = RTStrDup("Apple Computer, Inc.");
441 darwinDictGetString(PropsRef, CFSTR("USB Product Name"), (char **)&pCur->pszProduct);
442 if ( !pCur->pszProduct
443 && pCur->bDeviceClass == 224 /* Wireless */
444 && pCur->bDeviceSubClass == 1 /* Radio Frequency */
445 && pCur->bDeviceProtocol == 1 /* Bluetooth */)
446 pCur->pszProduct = RTStrDup("Bluetooth");
447 darwinDictGetString(PropsRef, CFSTR("USB Serial Number"), (char **)&pCur->pszSerialNumber);
448
449#if 0 /* leave the remainder as zero for now. */
450 /*
451 * Create a plugin interface for the service and query its USB Device interface.
452 */
453 SInt32 Score = 0;
454 IOCFPlugInInterface **ppPlugInInterface = NULL;
455 rc = IOCreatePlugInInterfaceForService(USBDevice, kIOUSBDeviceUserClientTypeID,
456 kIOCFPlugInInterfaceID, &ppPlugInInterface, &Score);
457 if (rc == kIOReturnSuccess)
458 {
459 IOUSBDeviceInterface245 **ppUSBDevI = NULL;
460 HRESULT hrc = (*ppPlugInInterface)->QueryInterface(ppPlugInInterface,
461 CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID245),
462 (LPVOID *)&ppUSBDevI);
463 rc = IODestroyPlugInInterface(ppPlugInInterface); Assert(rc == kIOReturnSuccess);
464 ppPlugInInterface = NULL;
465 if (hrc == S_OK)
466 {
467 /** @todo enumerate configurations and interfaces if we actually need them. */
468 //IOReturn (*GetNumberOfConfigurations)(void *self, UInt8 *numConfig);
469 //IOReturn (*GetConfigurationDescriptorPtr)(void *self, UInt8 configIndex, IOUSBConfigurationDescriptorPtr *desc);
470 //IOReturn (*CreateInterfaceIterator)(void *self, IOUSBFindInterfaceRequest *req, io_iterator_t *iter);
471 }
472 long cReft = (*ppUSBDeviceInterface)->Release(ppUSBDeviceInterface); MY_CHECK_CREFS(cRefs);
473 }
474#endif
475
476 /*
477 * We're good. Link the device.
478 */
479 pCur->pPrev = pTail;
480 if (pTail)
481 pTail = pTail->pNext = pCur;
482 else
483 pTail = pHead = pCur;
484 fOk = true;
485 } while (0);
486
487 /* cleanup on failure / skipped device. */
488 if (!fOk && pCur)
489 {
490 /** @todo */
491 }
492
493 CFRelease(PropsRef);
494 }
495 else
496 AssertMsgFailed(("krc=%#x\n", krc));
497
498 IOObjectRelease(USBDevice);
499 i++;
500 }
501
502 IOObjectRelease(USBDevices);
503
504 /*
505 * Some post processing. There are a couple of things we have to
506 * make 100% sure about, and that is that the (Apple) keyboard
507 * and mouse most likely to be in use by the user aren't available
508 * for capturing. If there is no Apple mouse or keyboard we'll
509 * take the first one from another vendor.
510 */
511 PUSBDEVICE pMouse = NULL;
512 PUSBDEVICE pKeyboard = NULL;
513 for (PUSBDEVICE pCur = pHead; pCur; pCur = pCur->pNext)
514 if (pCur->idVendor == kIOUSBVendorIDAppleComputer)
515 {
516 /*
517 * This test is a bit rough, should check device class/protocol but
518 * we don't have interface info yet so that might be a bit tricky.
519 */
520 if ( ( !pKeyboard
521 || pKeyboard->idVendor != kIOUSBVendorIDAppleComputer)
522 && pCur->pszProduct
523 && strstr(pCur->pszProduct, " Keyboard"))
524 pKeyboard = pCur;
525 else if ( ( !pMouse
526 || pMouse->idVendor != kIOUSBVendorIDAppleComputer)
527 && pCur->pszProduct
528 && strstr(pCur->pszProduct, " Mouse")
529 )
530 pMouse = pCur;
531 }
532 else if (!pKeyboard || !pMouse)
533 {
534 if ( pCur->bDeviceClass == 3 /* HID */
535 && pCur->bDeviceProtocol == 1 /* Keyboard */)
536 pKeyboard = pCur;
537 else if ( pCur->bDeviceClass == 3 /* HID */
538 && pCur->bDeviceProtocol == 2 /* Mouse */)
539 pMouse = pCur;
540 /** @todo examin interfaces */
541 }
542
543 if (pKeyboard)
544 pKeyboard->enmState = USBDEVICESTATE_USED_BY_HOST;
545 if (pMouse)
546 pMouse->enmState = USBDEVICESTATE_USED_BY_HOST;
547
548 return pHead;
549}
550
551#endif /* VBOX_WITH_USB */
552
553
554/**
555 * Enumerate the DVD drives returning a FIFO of device name strings.
556 *
557 * @returns Pointer to the head.
558 * The caller is responsible for calling RTMemFree() on each of the nodes.
559 */
560PDARWINDVD DarwinGetDVDDrives(void)
561{
562 AssertReturn(darwinOpenMasterPort(), NULL);
563
564 /*
565 * Create a matching dictionary for searching for DVD services in the IOKit.
566 *
567 * [If I understand this correctly, plain CDROMs doesn't show up as
568 * IODVDServices. Too keep things simple, we will only support DVDs
569 * until somebody complains about it and we get hardware to test it on.
570 * (Unless I'm much mistaken, there aren't any (orignal) intel macs with
571 * plain cdroms.)]
572 */
573 CFMutableDictionaryRef RefMatchingDict = IOServiceMatching("IODVDServices");
574 AssertReturn(RefMatchingDict, NULL);
575
576 /*
577 * Perform the search and get a collection of DVD services.
578 */
579 io_iterator_t DVDServices = NULL;
580 IOReturn rc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &DVDServices);
581 AssertMsgReturn(rc == kIOReturnSuccess, ("rc=%d\n", rc), NULL);
582 RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
583
584 /*
585 * Enumerate the DVD services.
586 * (This enumeration must be identical to the one performed in DrvHostBase.cpp.)
587 */
588 PDARWINDVD pHead = NULL;
589 PDARWINDVD pTail = NULL;
590 unsigned i = 0;
591 io_object_t DVDService;
592 while ((DVDService = IOIteratorNext(DVDServices)) != 0)
593 {
594 /*
595 * Get the properties we use to identify the DVD drive.
596 *
597 * While there is a (weird 12 byte) GUID, it isn't persistent
598 * accross boots. So, we have to use a combination of the
599 * vendor name and product name properties with an optional
600 * sequence number for identification.
601 */
602 CFMutableDictionaryRef PropsRef = 0;
603 kern_return_t krc = IORegistryEntryCreateCFProperties(DVDService, &PropsRef, kCFAllocatorDefault, kNilOptions);
604 if (krc == KERN_SUCCESS)
605 {
606 /* Get the Device Characteristics dictionary. */
607 CFDictionaryRef DevCharRef = (CFDictionaryRef)CFDictionaryGetValue(PropsRef, CFSTR(kIOPropertyDeviceCharacteristicsKey));
608 if (DevCharRef)
609 {
610 /* The vendor name. */
611 char szVendor[128];
612 char *pszVendor = &szVendor[0];
613 CFTypeRef ValueRef = CFDictionaryGetValue(DevCharRef, CFSTR(kIOPropertyVendorNameKey));
614 if ( ValueRef
615 && CFGetTypeID(ValueRef) == CFStringGetTypeID()
616 && CFStringGetCString((CFStringRef)ValueRef, szVendor, sizeof(szVendor), kCFStringEncodingUTF8))
617 pszVendor = RTStrStrip(szVendor);
618 else
619 *pszVendor = '\0';
620
621 /* The product name. */
622 char szProduct[128];
623 char *pszProduct = &szProduct[0];
624 ValueRef = CFDictionaryGetValue(DevCharRef, CFSTR(kIOPropertyProductNameKey));
625 if ( ValueRef
626 && CFGetTypeID(ValueRef) == CFStringGetTypeID()
627 && CFStringGetCString((CFStringRef)ValueRef, szProduct, sizeof(szProduct), kCFStringEncodingUTF8))
628 pszProduct = RTStrStrip(szProduct);
629 else
630 *pszProduct = '\0';
631
632 /* Construct the name and check for duplicates. */
633 char szName[256 + 32];
634 if (*pszVendor || *pszProduct)
635 {
636 if (*pszVendor && *pszProduct)
637 RTStrPrintf(szName, sizeof(szName), "%s %s", pszVendor, pszProduct);
638 else
639 strcpy(szName, *pszVendor ? pszVendor : pszProduct);
640
641 for (PDARWINDVD pCur = pHead; pCur; pCur = pCur->pNext)
642 {
643 if (!strcmp(szName, pCur->szName))
644 {
645 if (*pszVendor && *pszProduct)
646 RTStrPrintf(szName, sizeof(szName), "%s %s (#%u)", pszVendor, pszProduct, i);
647 else
648 RTStrPrintf(szName, sizeof(szName), "%s %s (#%u)", *pszVendor ? pszVendor : pszProduct, i);
649 break;
650 }
651 }
652 }
653 else
654 RTStrPrintf(szName, sizeof(szName), "(#%u)", i);
655
656 /* Create the device. */
657 size_t cbName = strlen(szName) + 1;
658 PDARWINDVD pNew = (PDARWINDVD)RTMemAlloc(RT_OFFSETOF(DARWINDVD, szName[cbName]));
659 if (pNew)
660 {
661 pNew->pNext = NULL;
662 memcpy(pNew->szName, szName, cbName);
663 if (pTail)
664 pTail = pTail->pNext = pNew;
665 else
666 pTail = pHead = pNew;
667 }
668 }
669 CFRelease(PropsRef);
670 }
671 else
672 AssertMsgFailed(("krc=%#x\n", krc));
673
674 IOObjectRelease(DVDService);
675 i++;
676 }
677
678 IOObjectRelease(DVDServices);
679
680 return pHead;
681}
682
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