DeviceDataSource.m   [plain text]


/*
 * © Copyright 2001-2003 Apple Computer, Inc. All rights reserved.
 *
 * IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc. (ÒAppleÓ) in 
 * consideration of your agreement to the following terms, and your use, installation, 
 * modification or redistribution of this Apple software constitutes acceptance of these
 * terms.  If you do not agree with these terms, please do not use, install, modify or 
 * redistribute this Apple software.
 *
 * In consideration of your agreement to abide by the following terms, and subject to these 
 * terms, Apple grants you a personal, non exclusive license, under AppleÕs copyrights in this 
 * original Apple software (the ÒApple SoftwareÓ), to use, reproduce, modify and redistribute 
 * the Apple Software, with or without modifications, in source and/or binary forms; provided 
 * that if you redistribute the Apple Software in its entirety and without modifications, you 
 * must retain this notice and the following text and disclaimers in all such redistributions 
 * of the Apple Software.  Neither the name, trademarks, service marks or logos of Apple 
 * Computer, Inc. may be used to endorse or promote products derived from the Apple Software 
 * without specific prior written permission from Apple. Except as expressly stated in this 
 * notice, no other rights or licenses, express or implied, are granted by Apple herein, 
 * including but not limited to any patent rights that may be infringed by your derivative 
 * works or by other works in which the Apple Software may be incorporated.
 * 
 * The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO WARRANTIES, 
 * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-
 * INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE 
 * SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 
 *
 * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL 
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, 
 * REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND 
 * WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR 
 * OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	Includes
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

#import "DeviceDataSource.h"
#import "AuthoringDeviceTester.h"
#import "ImageAndTextCell.h"

#import <CoreFoundation/CoreFoundation.h>
#import <IOKit/IOKitKeys.h>
#import <IOKit/storage/IOStorageDeviceCharacteristics.h>
#import <IOKit/scsi/SCSITaskLib.h>
#import <IOKit/scsi/IOSCSIMultimediaCommandsDevice.h>


//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	Macros
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

#define FILTER_DVD_DRIVES_ONLY			0	/* set to 1 to filter out non-DVD capable drives */
#define FILTER_DVD_BURNERS_ONLY			0	/* set to 1 to filter out non-DVD burners */
#define FILTER_ATAPI_DEVICES_ONLY		0	/* set to 1 to filter out non-ATAPI devices */


//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	Prototypes
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

static void
AppearedNotificationHandler ( void * refCon, io_iterator_t iterator );

static void
DisappearedNotificationHandler ( void * refCon, io_iterator_t iterator );


//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	Constants
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

#define kAppleVendorString								"Apple"


@interface DeviceDataSource(Private)
- ( void ) registerDeviceCallbackHandler;
@end

@implementation DeviceDataSource(Private)


//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	registerDeviceCallbackHandler - Registers this object to get called back
//									when certain devices come online.
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

- ( void ) registerDeviceCallbackHandler
{
	
	IOReturn	 			kernErr;
	CFRunLoopSourceRef		runLoopSource;
	CFMutableDictionaryRef	matchingDict;
	CFMutableDictionaryRef	subDict;
	CFRunLoopRef			runLoop;
	
	// Create the dictionaries
	matchingDict = CFDictionaryCreateMutable ( kCFAllocatorDefault,
											   0,
											   &kCFTypeDictionaryKeyCallBacks,
											   &kCFTypeDictionaryValueCallBacks );
	
	subDict = CFDictionaryCreateMutable ( kCFAllocatorDefault,
										  0,
										  &kCFTypeDictionaryKeyCallBacks,
										  &kCFTypeDictionaryValueCallBacks );
	
	//
	//	Note: We are setting up a matching dictionary which looks like the following:
	//
	//	<dict>
	//		<key>IOPropertyMatch</key>
	//		<dict>
	//			<key>SCSITaskDeviceCategory</key>
	//			<string>SCSITaskAuthoringDevice</string>
	//		</dict>
	// </dict>
	//
	
	// Create the port on which we will receive notifications. We'll wrap it in a runLoopSource
	// which we then feed into the runLoop for async event notifications.
	deviceNotifyPort = IONotificationPortCreate ( kIOMasterPortDefault );
	if ( deviceNotifyPort == NULL )
	{
		return;		
	}
	
	// Get a runLoopSource for our mach port.
	runLoopSource = IONotificationPortGetRunLoopSource ( deviceNotifyPort );
	
	// Create a dictionary with the "SCSITaskDeviceCategory" key = "SCSITaskAuthoringDevice"
	CFDictionarySetValue ( 	subDict,
							CFSTR ( kIOPropertySCSITaskDeviceCategory ),
							CFSTR ( kIOPropertySCSITaskAuthoringDevice ) );
	
	// This shows you how to fine-tune the matching.
	
	#if FILTER_DVD_DRIVES_ONLY
	
	// Insert the "device-type" key = "DVD"
	CFDictionarySetValue ( 	subDict,
							CFSTR ( "device-type" ),
							CFSTR ( "DVD" ) );
	
	#endif /* FILTER_DVD_DRIVES_ONLY */
	
	// Add the dictionary to the main dictionary with the key "IOPropertyMatch" to
	// narrow the search to the above dictionary.
	CFDictionarySetValue ( 	matchingDict,
							CFSTR ( kIOPropertyMatchKey ),
							subDict );
	
	// Retain a reference since we arm both the appearance and disappearance notifications
	// and the call to IOServiceAddMatchingNotification() consumes a reference each time.
	matchingDict = ( CFMutableDictionaryRef ) CFRetain ( matchingDict );
	
	kernErr = IOServiceAddMatchingNotification ( deviceNotifyPort,
												 kIOFirstMatchNotification,
												 matchingDict,
												 AppearedNotificationHandler,
												 ( void * ) self,
												 &deviceAppearedIterator );
	
	// Since we got back an iterator already from this routine, we call the handler to immediately
	// dispatch any devices there already
	AppearedNotificationHandler ( ( void * ) self, deviceAppearedIterator );
	
	kernErr = IOServiceAddMatchingNotification ( deviceNotifyPort,
												 kIOTerminatedNotification,
												 matchingDict,
												 DisappearedNotificationHandler,
												 ( void * ) self,
												 &deviceDisappearedIterator );
	
	// Since we got back an iterator already from this routine, we call the handler to immediately
	// dispatch any devices removed already
	DisappearedNotificationHandler ( ( void * ) self, deviceDisappearedIterator );
	
	// Get our runLoop
	runLoop = [ [ NSRunLoop currentRunLoop ] getCFRunLoop ];
	
	// Add our runLoopSource to our runLoop
	CFRunLoopAddSource ( runLoop, runLoopSource, kCFRunLoopDefaultMode );
	
	// Release the dictionary
	CFRelease ( subDict );
	
}


@end


@implementation DeviceDataSource


//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	init:
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

- ( id ) init
{

	[ super init ];
	
	deviceNotifyPort			= MACH_PORT_NULL;
	deviceAppearedIterator 		= 0;
	deviceDisappearedIterator 	= 0;
	
	// Create the mutable array which represents our list of
	// devices
	[ self setTheDeviceList: [ NSMutableArray arrayWithCapacity: 0 ] ];
	
	// Register ourself for callbacks when the devices we want
	// to know about come online/leave
	[ self registerDeviceCallbackHandler ];
		
	return self;
	
}


//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	dealloc:
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

- ( void ) dealloc
{
	
	// Free the array
	[ self setTheDeviceList: nil ];
	
	// Destroy the notify port if necessary
	if ( deviceNotifyPort != NULL )
	{
		IONotificationPortDestroy ( deviceNotifyPort );
		deviceNotifyPort = NULL;
	}
	
	// Destroy the iterator used for device appeared notifications
	if ( deviceAppearedIterator != 0 )
	{
		( void ) IOObjectRelease ( deviceAppearedIterator );
		deviceAppearedIterator = 0;
	}

	// Destroy the iterator used for device disappeared notifications
	if ( deviceDisappearedIterator != 0 )
	{
		( void ) IOObjectRelease ( deviceDisappearedIterator );
		deviceDisappearedIterator = 0;
	}
	
	// call our superclass
	[ super dealloc ];
	
}


#if 0
#pragma mark -
#pragma mark Accessor Methods
#pragma mark -
#endif


- ( NSTableView * ) theTableView { return theTableView; }
- ( NSMutableArray * ) theDeviceList { return theDeviceList; }
- ( void ) setTheDeviceList : ( NSMutableArray * ) value
{

	[ value retain ];
	[ theDeviceList release ];
	theDeviceList = value;

}
- ( int ) numberOfRowsInTableView: ( NSTableView * ) theTableView
{
	return [ theDeviceList count ];
}


#if 0
#pragma mark -
#pragma mark Delegate Methods
#pragma mark -
#endif


//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	tableView:objectValueForTableColumn:row
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

- ( id ) 		tableView: ( NSTableView * ) theTableView
objectValueForTableColumn: ( NSTableColumn * ) theTableColumn
					  row: ( int ) rowIndex
{
	
	// This looks complicated but really isn't. It gets an element from our list of
	// devices and calls the method related to the identifier on that object. For instance,
	// say the column identifier is "theProductName" in the nib file, then that identifier
	// is passed and the object is asked for the value for that key. The method [ object theProductName ]
	// is invoked and the value returned is the member variable theProductName (an NSString *). This
	// is key-value coding and it is very cool. Learn it. Live it. Love it. :)
	return [ [ [ self theDeviceList ] objectAtIndex: rowIndex ] valueForKey: [ theTableColumn identifier ] ];
	
}


@end


#if 0
#pragma mark -
#pragma mark Device Matching Callback Methods
#pragma mark -
#endif


//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	AppearedNotificationHandler
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

static void
AppearedNotificationHandler ( void * refCon, io_iterator_t iterator )
{
	
	io_service_t		theService	= MACH_PORT_NULL;
	DeviceDataSource *	dataSource	= ( DeviceDataSource * ) refCon;
	
	while ( theService = IOIteratorNext ( iterator ) )
	{
		
		CFMutableDictionaryRef	theDict 	= 0;
		AuthoringDevice * 		newDevice	= nil;
		IOReturn				theErr 		= 0;
		
		newDevice = [ AuthoringDevice device ];
		
		// Get the CF Properties for the io_service_t
		theErr = IORegistryEntryCreateCFProperties ( theService,
													 &theDict,
													 kCFAllocatorDefault,
													 0 );
		
		if ( theErr == kIOReturnSuccess )
		{
			
			// Check for the GUID key
			if ( CFDictionaryContainsKey ( theDict, CFSTR ( kIOPropertySCSITaskUserClientInstanceGUID ) ) )
			{
				
				NSData *	theData = nil;
				
				theData = ( NSData * ) CFDictionaryGetValue ( theDict, CFSTR ( kIOPropertySCSITaskUserClientInstanceGUID ) );
				[ newDevice setTheGUID: theData ];
				
			}
			
			// Check for the Protocol Characteristics key
			if ( CFDictionaryContainsKey ( theDict, CFSTR ( kIOPropertyProtocolCharacteristicsKey ) ) )
			{
				
				CFStringRef			physicalInterconnectString	= NULL;
				CFDictionaryRef		protocolDict				= NULL;
				
				protocolDict = ( CFDictionaryRef ) CFDictionaryGetValue ( theDict,
						CFSTR ( kIOPropertyProtocolCharacteristicsKey ) );
				
				physicalInterconnectString = ( CFStringRef ) CFDictionaryGetValue ( protocolDict,
						CFSTR ( kIOPropertyPhysicalInterconnectTypeKey ) );
				
				#if FILTER_ATAPI_DEVICES_ONLY
				
				if ( CFStringCompare ( physicalInterconnectString, CFSTR ( kIOPropertyPhysicalInterconnectTypeATAPI ), 0 ) != kCFCompareEqualTo )
				{
					
					// Not an ATAPI device. Clean up and bail. Do not call release on the AuthoringDevice as it was
					// given to you autoreleased.
					CFRelease ( theDict );
					theDict = 0;
					( void ) IOObjectRelease ( theService );
					continue;
					
				}
				
				#endif /* FILTER_ATAPI_DEVICES_ONLY */
				
				[ newDevice setThePhysicalInterconnect: ( NSString * ) physicalInterconnectString ];
				
			}
			
			// Check for the Device Characteristics key
			if ( CFDictionaryContainsKey ( theDict, CFSTR ( kIOPropertyDeviceCharacteristicsKey ) ) )
			{
				
				CFStringRef			string			= NULL;
				CFDictionaryRef		deviceDict		= NULL;
				CFNumberRef			features	 	= NULL;
				UInt32				value		 	= 0;
				
				#if FILTER_DVD_BURNERS_ONLY
								
				#endif /* FILTER_DVD_BURNERS_ONLY */
				
				deviceDict = ( CFDictionaryRef ) CFDictionaryGetValue ( theDict,
																		CFSTR ( kIOPropertyDeviceCharacteristicsKey ) );
				
				// Get the vendor, product, and firmware info for display
				string = ( CFStringRef ) CFDictionaryGetValue ( deviceDict, CFSTR ( kIOPropertyVendorNameKey ) );
				[ newDevice setTheVendorName: ( NSString * ) string ];
				
				string = ( CFStringRef ) CFDictionaryGetValue ( deviceDict, CFSTR ( kIOPropertyProductNameKey ) );
				[ newDevice setTheProductName: ( NSString * ) string ];
				
				string = ( CFStringRef ) CFDictionaryGetValue ( deviceDict, CFSTR ( kIOPropertyProductRevisionLevelKey ) );
				[ newDevice setTheProductRevisionLevel: ( NSString * ) string ];
				
				features = ( CFNumberRef ) CFDictionaryGetValue ( deviceDict, CFSTR ( kIOPropertySupportedCDFeatures ) );
				CFNumberGetValue ( features, kCFNumberLongType, &value );
				[ newDevice setCDFeatures: value ];

				features = ( CFNumberRef ) CFDictionaryGetValue ( deviceDict, CFSTR ( kIOPropertySupportedDVDFeatures ) );
				CFNumberGetValue ( features, kCFNumberLongType, &value );
				[ newDevice setDVDFeatures: value ];
				
				#if FILTER_DVD_BURNERS_ONLY
				
				if ( ( ( value & kDVDFeaturesWriteOnceMask ) == 0 ) &&
					 ( ( value & kDVDFeaturesReWriteableMask ) == 0 ) &&
					 ( ( value & kDVDFeaturesPlusRMask ) == 0 ) &&
					 ( ( value & kDVDFeaturesPlusRWMask ) == 0 ) )
				{
					
					// Not a burner. Clean up and bail. Do not call release on the AuthoringDevice as it was
					// given to you autoreleased.
					CFRelease ( theDict );
					theDict = 0;
					( void ) IOObjectRelease ( theService );
					continue;
					
				}
				
				#endif /* FILTER_DVD_BURNERS_ONLY */
				
			}
			
			// We're done with this dictionary, so release it.
			CFRelease ( theDict );
			theDict = 0;
			
			// Add the object to the data source so that it knows about this device
			[ [ dataSource theDeviceList ] addObject: newDevice ];
			
		}
		
		// Release the object.
		( void ) IOObjectRelease ( theService );
		
	}
	
	// Reload the data source once we've traversed the entire iterator
	[ [ dataSource theTableView ] reloadData ];
	
}


//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	DisappearedNotificationHandler
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

static void
DisappearedNotificationHandler ( void * refCon, io_iterator_t iterator )
{
	
	io_service_t		theService	= MACH_PORT_NULL;
	DeviceDataSource *	dataSource	= ( DeviceDataSource * ) refCon;
	
	while ( theService = IOIteratorNext ( iterator ) )
	{
		
		CFMutableDictionaryRef	theDict		= NULL;
		AuthoringDevice * 		newDevice	= nil;
		IOReturn				theErr 		= kIOReturnSuccess;
		
		newDevice = [ AuthoringDevice device ];
		
		// Get the CF Properties for this node
		theErr = IORegistryEntryCreateCFProperties ( theService,
													 &theDict,
													 kCFAllocatorDefault,
													 0 );
		
		if ( theErr == kIOReturnSuccess )
		{
			
			// Get the GUID key
			if ( CFDictionaryContainsKey ( theDict, CFSTR ( kIOPropertySCSITaskUserClientInstanceGUID ) ) )
			{
				
				NSData *	theData = nil;
				
				theData = ( NSData * ) CFDictionaryGetValue ( theDict, CFSTR ( kIOPropertySCSITaskUserClientInstanceGUID ) );
				[ newDevice setTheGUID: theData ];
				
				// Since the GUID is used to distinguish between two objects (the object overrides isEqual),
				// we just create a new object and fill in the GUID key above, and then tell the data source
				// to remove it. If you decide to use more criteria than the GUID (like the physical interconnect)
				// be sure to add those fields to the object before calling removeObject on the theDeviceList
				[ [ dataSource theDeviceList ] removeObject: newDevice ];
				
			}
			
		}
		
		( void ) IOObjectRelease ( theService );
		
	}
	
	// Reload the data source once we've traversed the entire iterator
	[ [ dataSource theTableView ] reloadData ];
	
}