SCSITargetProberAppDelegate.m [plain text]
/*
* © Copyright 2004 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.
*/
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
// Imports
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
#import "SCSITargetProberAppDelegate.h"
#import "SCSITargetProberDocument.h"
#import "SCSITargetProberKeys.h"
#import "SCSIInitiator.h"
#import "AppPreferences.h"
#import "FakeSCSIInitiator.h"
#import <IOKit/IOKitLib.h>
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
// Constants
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
#define kIOSCSIParallelInterfaceControllerClassString "IOSCSIParallelInterfaceController"
#define kIOSCSITargetDeviceClassString "IOSCSITargetDevice"
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
// Prototypes
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
static void
ClearIterator ( io_iterator_t iterator );
static int
domainComparator ( id obj1, id obj2, void * context );
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
// Implementation
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
@implementation SCSITargetProberAppDelegate
- ( id ) init
{
self = [ super init ];
if ( self != nil )
{
port = NULL;
appearedIterator = MACH_PORT_NULL;
disappearedIterator = MACH_PORT_NULL;
[ self setInitiators: [ [ [ NSMutableArray alloc ] initWithCapacity: 0 ] autorelease ] ];
}
return self;
}
- ( NSArray * ) initiators
{
return initiators;
}
- ( void ) setInitiators: ( NSMutableArray * ) i
{
[ i retain ];
[ initiators release ];
initiators = i;
}
- ( void ) applicationDidFinishLaunching: ( NSNotification * ) notification
{
#pragma unused ( notification )
NSMutableDictionary * dict = [ [ [ NSMutableDictionary alloc ] init ] autorelease ];
// Set some initial defaults. If the user doesn't have a customized user
// defaults database (they haven't modified the prefs window), we want to
// have the basic defaults of "ID" and "Description" visible in the table
// view.
[ dict setValue: [ NSNumber numberWithBool: YES ] forKey: kShowTargetIDString ];
[ dict setValue: [ NSNumber numberWithBool: YES ] forKey: kShowDescriptionString ];
[ dict setValue: [ NSNumber numberWithBool: NO ] forKey: kShowRevisionString ];
[ dict setValue: [ NSNumber numberWithBool: NO ] forKey: kShowFeaturesString ];
[ dict setValue: [ NSNumber numberWithBool: NO ] forKey: kShowPDTString ];
// Set the standard user defaults up.
[ [ NSUserDefaultsController sharedUserDefaultsController ] setInitialValues: dict ];
// Start our IONotificationPort stuff
[ self startNotifications ];
// If no controllers are found, tell the user.
if ( [ initiators count ] == 0 )
{
// No cards found. Tell the user then quit.
NSRunAlertPanel ( NSLocalizedString ( kNoControllersFoundTitle, "" ),
NSLocalizedString ( kNoControllersFoundText, "" ),
nil, nil, nil );
[ NSApp terminate: self ];
}
else
{
// Create a new document window
[ self newDocument: self ];
}
}
// Action called to instantiate a new document. This is called from
// -applicationDidFinishLaunching and by the Menu Item File->New
// and by the Cmd + 'N' key sequence.
- ( IBAction ) newDocument: ( id ) sender
{
#pragma unused ( sender )
[ SCSITargetProberDocument newDocument: initiators ];
}
// Action called to load the preferences window. This is called from
// SCSITargetProber->Preferences and from Cmd + ',' key sequence.
- ( IBAction ) showPrefs: ( id ) sender
{
#pragma unused ( sender )
[ AppPreferences showPrefs ];
}
// Called to start IOKit notifications. We get notifications when
// new IOSCSIParallelInterfaceController subclasses register
// and when IOSCSITargetDevice objects register (they don't do
// that yet but they will soon). Until IOSCSITargetDevice objects
// register, the UI will not update on a successful reprobe, but
// the plumbing is already done in the app to make the UI refresh
// when that changes in the kernel.
- ( void ) startNotifications
{
IOReturn result = kIOReturnSuccess;
CFRunLoopSourceRef source = NULL;
CFRunLoopRef runLoop = NULL;
CFMutableDictionaryRef matchingDict = NULL;
// Create a notification port on which to receive messages.
port = IONotificationPortCreate ( kIOMasterPortDefault );
require ( ( port != NULL ), ErrorExit );
// Get the runloop source so we can attach it to our runloop.
source = IONotificationPortGetRunLoopSource ( port );
require ( ( source != NULL ), ReleasePort );
// Create the matching dictionary. Our matching dictionary uses
// the "IOSCSIParallelInterfaceController" class matching - basically
// any device which inherits from IOSCSIParallelInterfaceController.
matchingDict = IOServiceMatching ( kIOSCSIParallelInterfaceControllerClassString );
require ( ( matchingDict != NULL ), ReleaseSource );
// Retain the dictionary as IOServiceAddMatchingNotification() retains one
// reference.
CFRetain ( matchingDict );
// Set up notifications on first matches (plug in of CardBus SCSI cards).
result = IOServiceAddMatchingNotification ( port,
kIOFirstMatchNotification,
matchingDict,
AppearedNotificationHandler,
( void * ) self,
&appearedIterator );
// Since we got back an iterator already from this routine, we call the handler to immediately
// dispatch any devices there already
AppearedNotificationHandler ( ( void * ) self, appearedIterator );
// Set up notifications on termination (unplug of CardBus SCSI cards).
result = IOServiceAddMatchingNotification ( port,
kIOTerminatedNotification,
matchingDict,
DisappearedNotificationHandler,
( void * ) self,
&disappearedIterator );
// Since we got back an iterator already from this routine, we call the handler to immediately
// dispatch any devices there already
DisappearedNotificationHandler ( ( void * ) self, disappearedIterator );
// Create the matching dictionary. Our matching dictionary uses
// the "IOSCSITargetDevice" class matching - basically
// any device which inherits from IOSCSITargetDevice.
matchingDict = IOServiceMatching ( kIOSCSITargetDeviceClassString );
require ( ( matchingDict != NULL ), ReleaseSource );
// Retain the dictionary as IOServiceAddMatchingNotification() retains one
// reference.
CFRetain ( matchingDict );
// Set up notifications on first matches (new device arrives when we reprobe).
result = IOServiceAddMatchingNotification ( port,
kIOFirstMatchNotification,
matchingDict,
AppearedNotificationHandler,
( void * ) self,
&appearedIterator );
// Since we got back an iterator already, we want to empty it. We'll find target devices at
// launch by iterating over the registry. The dynamic stuff is for when we reprobe...
ClearIterator ( appearedIterator );
// Set up notifications on termination (device goes away).
result = IOServiceAddMatchingNotification ( port,
kIOTerminatedNotification,
matchingDict,
DisappearedNotificationHandler,
( void * ) self,
&disappearedIterator );
// Since we got back an iterator already, we want to empty it. We'll find target devices at
// launch by iterating over the registry. The dynamic stuff is for when we reprobe...
ClearIterator ( disappearedIterator );
// Get our run loop so we can add the notification's source to it.
runLoop = [ [ NSRunLoop currentRunLoop ] getCFRunLoop ];
// Add our runLoopSource to our runLoop
CFRunLoopAddSource ( runLoop, source, kCFRunLoopDefaultMode );
return;
ReleaseSource:
CFRelease ( source );
source = NULL;
ReleasePort:
IONotificationPortDestroy ( port );
port = NULL;
ErrorExit:
return;
}
// Called to stop IOKit notifications.
- ( void ) stopNotifications
{
// Destroy the notify port if necessary
if ( port != NULL )
{
IONotificationPortDestroy ( port );
port = NULL;
}
// Destroy the iterator used for device appeared notifications
if ( appearedIterator != MACH_PORT_NULL )
{
( void ) IOObjectRelease ( appearedIterator );
appearedIterator = MACH_PORT_NULL;
}
// Destroy the iterator used for device disappeared notifications
if ( disappearedIterator != MACH_PORT_NULL )
{
( void ) IOObjectRelease ( disappearedIterator );
disappearedIterator = MACH_PORT_NULL;
}
}
// Called when devices we registered an interest in appear.
- ( void ) appearedNotification: ( io_iterator_t ) iterator
{
io_service_t service = MACH_PORT_NULL;
service = IOIteratorNext ( iterator );
while ( service != MACH_PORT_NULL )
{
// Is this an IOSCSIParallelInterfaceController?
if ( IOObjectConformsTo ( service, kIOSCSIParallelInterfaceControllerClassString ) )
{
SCSIInitiator * newInitiator = nil;
// Yes, it is an IOSCSIParallelInterfaceController. Create a
// SCSIInitiator object to represent it.
newInitiator = [ [ SCSIInitiator alloc ] initWithService: service ];
// Add the new object to the list.
[ initiators addObject: newInitiator ];
// Sort the list.
[ initiators sortUsingFunction: domainComparator context: nil ];
// Adding to the list retains the object, so we can safely release
// our refcount on it. When the object is removed from the list,
// it will be released.
[ newInitiator release ];
}
// Is this an IOSCSITargetDevice?
else if ( IOObjectConformsTo ( service, kIOSCSITargetDeviceClassString ) )
{
SCSIDevice * newDevice = nil;
SCSIInitiator * initiator = nil;
io_service_t parent = MACH_PORT_NULL;
int domainID = 0;
int count = 0;
int index = 0;
// Get the IOSCSIParallelInterfaceDevice object.
IORegistryEntryGetParentEntry ( service, kIOServicePlane, &parent );
// Create the SCSIDevice object with the IOSCSIParallelInterfaceDevice.
newDevice = [ [ SCSIDevice alloc ] initWithService: parent ];
// Release the parent, we don't need it any more...
IOObjectRelease ( parent );
// Get the domainID for this new device.
domainID = [ [ newDevice domainIdentifier ] intValue ];
// Find out where to add the target device. Look at
// each initiator to see if the domainID matches.
count = [ initiators count ];
for ( index = 0; index < count; index++ )
{
initiator = [ initiators objectAtIndex: index ];
// Does domainID match?
if ( domainID == [ initiator domainID ] )
{
// Yes, add the target device to this initiator.
[ initiator addTargetDevice: newDevice ];
}
}
// We can safely release this device now. Either it was added
// to an initiator, or there wasn't an initator to associate it
// with...
[ newDevice release ];
}
// On to the next in the list...
IOObjectRelease ( service );
service = IOIteratorNext ( iterator );
}
}
// Called when devices we registered an interest in disappear.
- ( void ) disappearedNotification: ( io_iterator_t ) iterator
{
io_service_t service = MACH_PORT_NULL;
service = IOIteratorNext ( iterator );
while ( service != MACH_PORT_NULL )
{
// Is this an IOSCSIParallelInterfaceController?
if ( IOObjectConformsTo ( service, kIOSCSIParallelInterfaceControllerClassString ) )
{
int domainID = 0;
NSEnumerator * enumerator = nil;
SCSIInitiator * initiator = nil;
// Yes, get the domainID for this io_service_t.
domainID = [ SCSIInitiator domainIDForService: service ];
enumerator = [ initiators objectEnumerator ];
initiator = [ enumerator nextObject ];
// Find out which initiator to remove from the list.
while ( initiator != nil )
{
// Does the domainID match?
if ( [ initiator domainID ] == domainID )
{
// Yes, remove this one.
[ initiators removeObject: initiator ];
break;
}
// On to the next object..
initiator = [ enumerator nextObject ];
}
}
// Is this an IOSCSITargetDevice?
else if ( IOObjectConformsTo ( service, kIOSCSITargetDeviceClassString ) )
{
SCSIInitiator * initiator = nil;
int domainID = 0;
int count = 0;
int index = 0;
int targetID = 0;
// Yes, get the domainID and targetID.
domainID = [ SCSIDevice domainIDForService: service ];
targetID = [ SCSIDevice targetIDForService: service ];
// Find which initiator has the same domainID.
count = [ initiators count ];
for ( index = 0; index < count; index++ )
{
initiator = [ initiators objectAtIndex: index ];
// Does the domainID match?
if ( domainID == [ initiator domainID ] )
{
// Yes, remove the device from this initiator's list.
[ initiator removeTargetDevice: targetID ];
break;
}
}
}
// On to the next in the list...
IOObjectRelease ( service );
service = IOIteratorNext ( iterator );
}
}
- ( void ) dealloc
{
[ self stopNotifications ];
[ self setInitiators: nil ];
// Call our superclass
[ super dealloc ];
}
@end
#if 0
#pragma mark -
#pragma mark ¥ Static methods
#pragma mark -
#endif
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
// domainComparator - Compares devices based on deviceIdentifier field
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
static int
domainComparator ( id obj1, id obj2, void * context )
{
#pragma unused ( context )
int result = NSOrderedSame;
if ( [ obj1 domainID ] < [ obj2 domainID ] )
{
result = NSOrderedAscending;
}
if ( [ obj1 domainID ] > [ obj2 domainID ] )
{
result = NSOrderedDescending;
}
return result;
}
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
// ¥ ClearIterator - Clears devices from an iterator without performing
// any action
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
static void
ClearIterator ( io_iterator_t iterator )
{
io_service_t service = MACH_PORT_NULL;
service = IOIteratorNext ( iterator );
while ( service != MACH_PORT_NULL )
{
IOObjectRelease ( service );
service = IOIteratorNext ( iterator );
}
}
#if 0
#pragma mark -
#pragma mark ¥ C->Obj-C glue
#pragma mark -
#endif
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
// ¥ AppearedNotificationHandler - C->Obj-C glue
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
static void
AppearedNotificationHandler ( void * refCon, io_iterator_t iterator )
{
SCSITargetProberAppDelegate * app = ( SCSITargetProberAppDelegate * ) refCon;
[ app appearedNotification: iterator ];
}
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
// ¥ DisappearedNotificationHandler - C->Obj-C glue
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
static void
DisappearedNotificationHandler ( void * refCon, io_iterator_t iterator )
{
SCSITargetProberAppDelegate * app = ( SCSITargetProberAppDelegate * ) refCon;
[ app disappearedNotification: iterator ];
}