VirtualBox

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

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

Implemented USB attach and detach notifications. (darwin)

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