/* * Copyright (c) 2004-2007 Apple Inc. All rights reserved. * * IMPORTANT: This Apple software is supplied to you by Apple 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 //ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ // 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 ]; }