1/* 2 * Copyright (c) 2004-2007 Apple Inc. All rights reserved. 3 * 4 * IMPORTANT: This Apple software is supplied to you by Apple Inc. ("Apple") in 5 * consideration of your agreement to the following terms, and your use, installation, 6 * modification or redistribution of this Apple software constitutes acceptance of these 7 * terms. If you do not agree with these terms, please do not use, install, modify or 8 * redistribute this Apple software. 9 * 10 * In consideration of your agreement to abide by the following terms, and subject to these 11 * terms, Apple grants you a personal, non exclusive license, under Apple�s copyrights in this 12 * original Apple software (the �Apple Software�), to use, reproduce, modify and redistribute 13 * the Apple Software, with or without modifications, in source and/or binary forms; provided 14 * that if you redistribute the Apple Software in its entirety and without modifications, you 15 * must retain this notice and the following text and disclaimers in all such redistributions 16 * of the Apple Software. Neither the name, trademarks, service marks or logos of Apple 17 * Computer, Inc. may be used to endorse or promote products derived from the Apple Software 18 * without specific prior written permission from Apple. Except as expressly stated in this 19 * notice, no other rights or licenses, express or implied, are granted by Apple herein, 20 * including but not limited to any patent rights that may be infringed by your derivative 21 * works or by other works in which the Apple Software may be incorporated. 22 * 23 * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES, 24 * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON- 25 * INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE 26 * SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 27 * 28 * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 30 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, 31 * REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND 32 * WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR 33 * OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 */ 35 36 37//����������������������������������������������������������������������������� 38// Imports 39//����������������������������������������������������������������������������� 40 41#import "SCSITargetProberAppDelegate.h" 42#import "SCSITargetProberDocument.h" 43#import "SCSITargetProberKeys.h" 44#import "SCSIInitiator.h" 45#import "AppPreferences.h" 46#import "FakeSCSIInitiator.h" 47#import <IOKit/IOKitLib.h> 48 49 50//����������������������������������������������������������������������������� 51// Constants 52//����������������������������������������������������������������������������� 53 54#define kIOSCSIParallelInterfaceControllerClassString "IOSCSIParallelInterfaceController" 55#define kIOSCSITargetDeviceClassString "IOSCSITargetDevice" 56 57 58//����������������������������������������������������������������������������� 59// Prototypes 60//����������������������������������������������������������������������������� 61 62static void 63ClearIterator ( io_iterator_t iterator ); 64 65static int 66domainComparator ( id obj1, id obj2, void * context ); 67 68 69//����������������������������������������������������������������������������� 70// Implementation 71//����������������������������������������������������������������������������� 72 73@implementation SCSITargetProberAppDelegate 74 75 76- ( id ) init 77{ 78 79 self = [ super init ]; 80 81 if ( self != nil ) 82 { 83 84 port = NULL; 85 appearedIterator = MACH_PORT_NULL; 86 disappearedIterator = MACH_PORT_NULL; 87 88 [ self setInitiators: [ [ [ NSMutableArray alloc ] initWithCapacity: 0 ] autorelease ] ]; 89 90 } 91 92 return self; 93 94} 95 96 97- ( NSArray * ) initiators 98{ 99 return initiators; 100} 101 102 103- ( void ) setInitiators: ( NSMutableArray * ) i 104{ 105 106 [ i retain ]; 107 [ initiators release ]; 108 initiators = i; 109 110} 111 112 113- ( void ) applicationDidFinishLaunching: ( NSNotification * ) notification 114{ 115 116#pragma unused ( notification ) 117 118 NSMutableDictionary * dict = [ [ [ NSMutableDictionary alloc ] init ] autorelease ]; 119 120 // Set some initial defaults. If the user doesn't have a customized user 121 // defaults database (they haven't modified the prefs window), we want to 122 // have the basic defaults of "ID" and "Description" visible in the table 123 // view. 124 [ dict setValue: [ NSNumber numberWithBool: YES ] forKey: kShowTargetIDString ]; 125 [ dict setValue: [ NSNumber numberWithBool: YES ] forKey: kShowDescriptionString ]; 126 [ dict setValue: [ NSNumber numberWithBool: NO ] forKey: kShowRevisionString ]; 127 [ dict setValue: [ NSNumber numberWithBool: NO ] forKey: kShowFeaturesString ]; 128 [ dict setValue: [ NSNumber numberWithBool: NO ] forKey: kShowPDTString ]; 129 130 // Set the standard user defaults up. 131 [ [ NSUserDefaultsController sharedUserDefaultsController ] setInitialValues: dict ]; 132 133 // Start our IONotificationPort stuff 134 [ self startNotifications ]; 135 136 // If no controllers are found, tell the user. 137 if ( [ initiators count ] == 0 ) 138 { 139 140 // No cards found. Tell the user then quit. 141 NSRunAlertPanel ( NSLocalizedString ( kNoControllersFoundTitle, "" ), 142 NSLocalizedString ( kNoControllersFoundText, "" ), 143 nil, nil, nil ); 144 [ NSApp terminate: self ]; 145 146 } 147 148 else 149 { 150 // Create a new document window 151 [ self newDocument: self ]; 152 } 153 154} 155 156 157// Action called to instantiate a new document. This is called from 158// -applicationDidFinishLaunching and by the Menu Item File->New 159// and by the Cmd + 'N' key sequence. 160 161- ( IBAction ) newDocument: ( id ) sender 162{ 163#pragma unused ( sender ) 164 [ SCSITargetProberDocument newDocument: initiators ]; 165} 166 167 168// Action called to load the preferences window. This is called from 169// SCSITargetProber->Preferences and from Cmd + ',' key sequence. 170 171- ( IBAction ) showPrefs: ( id ) sender 172{ 173#pragma unused ( sender ) 174 [ AppPreferences showPrefs ]; 175} 176 177 178// Called to start IOKit notifications. We get notifications when 179// new IOSCSIParallelInterfaceController subclasses register 180// and when IOSCSITargetDevice objects register (they don't do 181// that yet but they will soon). Until IOSCSITargetDevice objects 182// register, the UI will not update on a successful reprobe, but 183// the plumbing is already done in the app to make the UI refresh 184// when that changes in the kernel. 185 186- ( void ) startNotifications 187{ 188 189 IOReturn result = kIOReturnSuccess; 190 CFRunLoopSourceRef source = NULL; 191 CFRunLoopRef runLoop = NULL; 192 CFMutableDictionaryRef matchingDict = NULL; 193 194 // Create a notification port on which to receive messages. 195 port = IONotificationPortCreate ( kIOMasterPortDefault ); 196 require ( ( port != NULL ), ErrorExit ); 197 198 // Get the runloop source so we can attach it to our runloop. 199 source = IONotificationPortGetRunLoopSource ( port ); 200 require ( ( source != NULL ), ReleasePort ); 201 202 // Create the matching dictionary. Our matching dictionary uses 203 // the "IOSCSIParallelInterfaceController" class matching - basically 204 // any device which inherits from IOSCSIParallelInterfaceController. 205 matchingDict = IOServiceMatching ( kIOSCSIParallelInterfaceControllerClassString ); 206 require ( ( matchingDict != NULL ), ReleaseSource ); 207 208 // Retain the dictionary as IOServiceAddMatchingNotification() retains one 209 // reference. 210 CFRetain ( matchingDict ); 211 212 // Set up notifications on first matches (plug in of CardBus SCSI cards). 213 result = IOServiceAddMatchingNotification ( port, 214 kIOFirstMatchNotification, 215 matchingDict, 216 AppearedNotificationHandler, 217 ( void * ) self, 218 &appearedIterator ); 219 220 // Since we got back an iterator already from this routine, we call the handler to immediately 221 // dispatch any devices there already 222 AppearedNotificationHandler ( ( void * ) self, appearedIterator ); 223 224 // Set up notifications on termination (unplug of CardBus SCSI cards). 225 result = IOServiceAddMatchingNotification ( port, 226 kIOTerminatedNotification, 227 matchingDict, 228 DisappearedNotificationHandler, 229 ( void * ) self, 230 &disappearedIterator ); 231 232 // Since we got back an iterator already from this routine, we call the handler to immediately 233 // dispatch any devices there already 234 DisappearedNotificationHandler ( ( void * ) self, disappearedIterator ); 235 236 // Create the matching dictionary. Our matching dictionary uses 237 // the "IOSCSITargetDevice" class matching - basically 238 // any device which inherits from IOSCSITargetDevice. 239 matchingDict = IOServiceMatching ( kIOSCSITargetDeviceClassString ); 240 require ( ( matchingDict != NULL ), ReleaseSource ); 241 242 // Retain the dictionary as IOServiceAddMatchingNotification() retains one 243 // reference. 244 CFRetain ( matchingDict ); 245 246 // Set up notifications on first matches (new device arrives when we reprobe). 247 result = IOServiceAddMatchingNotification ( port, 248 kIOFirstMatchNotification, 249 matchingDict, 250 AppearedNotificationHandler, 251 ( void * ) self, 252 &appearedIterator ); 253 254 // Since we got back an iterator already, we want to empty it. We'll find target devices at 255 // launch by iterating over the registry. The dynamic stuff is for when we reprobe... 256 ClearIterator ( appearedIterator ); 257 258 // Set up notifications on termination (device goes away). 259 result = IOServiceAddMatchingNotification ( port, 260 kIOTerminatedNotification, 261 matchingDict, 262 DisappearedNotificationHandler, 263 ( void * ) self, 264 &disappearedIterator ); 265 266 // Since we got back an iterator already, we want to empty it. We'll find target devices at 267 // launch by iterating over the registry. The dynamic stuff is for when we reprobe... 268 ClearIterator ( disappearedIterator ); 269 270 // Get our run loop so we can add the notification's source to it. 271 runLoop = [ [ NSRunLoop currentRunLoop ] getCFRunLoop ]; 272 273 // Add our runLoopSource to our runLoop 274 CFRunLoopAddSource ( runLoop, source, kCFRunLoopDefaultMode ); 275 276 return; 277 278 279ReleaseSource: 280 281 282 CFRelease ( source ); 283 source = NULL; 284 285 286ReleasePort: 287 288 289 IONotificationPortDestroy ( port ); 290 port = NULL; 291 292 293ErrorExit: 294 295 296 return; 297 298} 299 300 301// Called to stop IOKit notifications. 302 303- ( void ) stopNotifications 304{ 305 306 // Destroy the notify port if necessary 307 if ( port != NULL ) 308 { 309 310 IONotificationPortDestroy ( port ); 311 port = NULL; 312 313 } 314 315 // Destroy the iterator used for device appeared notifications 316 if ( appearedIterator != MACH_PORT_NULL ) 317 { 318 319 ( void ) IOObjectRelease ( appearedIterator ); 320 appearedIterator = MACH_PORT_NULL; 321 322 } 323 324 // Destroy the iterator used for device disappeared notifications 325 if ( disappearedIterator != MACH_PORT_NULL ) 326 { 327 328 ( void ) IOObjectRelease ( disappearedIterator ); 329 disappearedIterator = MACH_PORT_NULL; 330 331 } 332 333} 334 335 336// Called when devices we registered an interest in appear. 337 338- ( void ) appearedNotification: ( io_iterator_t ) iterator 339{ 340 341 io_service_t service = MACH_PORT_NULL; 342 343 service = IOIteratorNext ( iterator ); 344 while ( service != MACH_PORT_NULL ) 345 { 346 347 // Is this an IOSCSIParallelInterfaceController? 348 if ( IOObjectConformsTo ( service, kIOSCSIParallelInterfaceControllerClassString ) ) 349 { 350 351 SCSIInitiator * newInitiator = nil; 352 353 // Yes, it is an IOSCSIParallelInterfaceController. Create a 354 // SCSIInitiator object to represent it. 355 newInitiator = [ [ SCSIInitiator alloc ] initWithService: service ]; 356 357 // Add the new object to the list. 358 [ initiators addObject: newInitiator ]; 359 360 // Sort the list. 361 [ initiators sortUsingFunction: domainComparator context: nil ]; 362 363 // Adding to the list retains the object, so we can safely release 364 // our refcount on it. When the object is removed from the list, 365 // it will be released. 366 [ newInitiator release ]; 367 368 } 369 370 // Is this an IOSCSITargetDevice? 371 else if ( IOObjectConformsTo ( service, kIOSCSITargetDeviceClassString ) ) 372 { 373 374 SCSIDevice * newDevice = nil; 375 SCSIInitiator * initiator = nil; 376 io_service_t parent = MACH_PORT_NULL; 377 int domainID = 0; 378 int count = 0; 379 int index = 0; 380 381 // Get the IOSCSIParallelInterfaceDevice object. 382 IORegistryEntryGetParentEntry ( service, kIOServicePlane, &parent ); 383 384 // Create the SCSIDevice object with the IOSCSIParallelInterfaceDevice. 385 newDevice = [ [ SCSIDevice alloc ] initWithService: parent ]; 386 387 // Release the parent, we don't need it any more... 388 IOObjectRelease ( parent ); 389 390 // Get the domainID for this new device. 391 domainID = [ [ newDevice domainIdentifier ] intValue ]; 392 393 // Find out where to add the target device. Look at 394 // each initiator to see if the domainID matches. 395 count = [ initiators count ]; 396 for ( index = 0; index < count; index++ ) 397 { 398 399 initiator = [ initiators objectAtIndex: index ]; 400 401 // Does domainID match? 402 if ( domainID == [ initiator domainID ] ) 403 { 404 405 // Yes, add the target device to this initiator. 406 [ initiator addTargetDevice: newDevice ]; 407 408 } 409 410 } 411 412 // We can safely release this device now. Either it was added 413 // to an initiator, or there wasn't an initator to associate it 414 // with... 415 [ newDevice release ]; 416 417 } 418 419 // On to the next in the list... 420 IOObjectRelease ( service ); 421 service = IOIteratorNext ( iterator ); 422 423 } 424 425} 426 427 428// Called when devices we registered an interest in disappear. 429 430- ( void ) disappearedNotification: ( io_iterator_t ) iterator 431{ 432 433 io_service_t service = MACH_PORT_NULL; 434 435 service = IOIteratorNext ( iterator ); 436 while ( service != MACH_PORT_NULL ) 437 { 438 439 // Is this an IOSCSIParallelInterfaceController? 440 if ( IOObjectConformsTo ( service, kIOSCSIParallelInterfaceControllerClassString ) ) 441 { 442 443 int domainID = 0; 444 NSEnumerator * enumerator = nil; 445 SCSIInitiator * initiator = nil; 446 447 // Yes, get the domainID for this io_service_t. 448 domainID = [ SCSIInitiator domainIDForService: service ]; 449 450 enumerator = [ initiators objectEnumerator ]; 451 initiator = [ enumerator nextObject ]; 452 453 // Find out which initiator to remove from the list. 454 while ( initiator != nil ) 455 { 456 457 // Does the domainID match? 458 if ( [ initiator domainID ] == domainID ) 459 { 460 461 // Yes, remove this one. 462 [ initiators removeObject: initiator ]; 463 break; 464 465 } 466 467 // On to the next object.. 468 initiator = [ enumerator nextObject ]; 469 470 } 471 472 } 473 474 // Is this an IOSCSITargetDevice? 475 else if ( IOObjectConformsTo ( service, kIOSCSITargetDeviceClassString ) ) 476 { 477 478 SCSIInitiator * initiator = nil; 479 int domainID = 0; 480 int count = 0; 481 int index = 0; 482 int targetID = 0; 483 484 // Yes, get the domainID and targetID. 485 domainID = [ SCSIDevice domainIDForService: service ]; 486 targetID = [ SCSIDevice targetIDForService: service ]; 487 488 // Find which initiator has the same domainID. 489 count = [ initiators count ]; 490 for ( index = 0; index < count; index++ ) 491 { 492 493 initiator = [ initiators objectAtIndex: index ]; 494 495 // Does the domainID match? 496 if ( domainID == [ initiator domainID ] ) 497 { 498 499 // Yes, remove the device from this initiator's list. 500 [ initiator removeTargetDevice: targetID ]; 501 break; 502 503 } 504 505 } 506 507 } 508 509 // On to the next in the list... 510 IOObjectRelease ( service ); 511 service = IOIteratorNext ( iterator ); 512 513 } 514 515} 516 517 518- ( void ) dealloc 519{ 520 521 [ self stopNotifications ]; 522 [ self setInitiators: nil ]; 523 524 // Call our superclass 525 [ super dealloc ]; 526 527} 528 529 530@end 531 532 533#if 0 534#pragma mark - 535#pragma mark Static methods 536#pragma mark - 537#endif 538 539 540//����������������������������������������������������������������������������� 541// domainComparator - Compares devices based on deviceIdentifier field 542//����������������������������������������������������������������������������� 543 544static int 545domainComparator ( id obj1, id obj2, void * context ) 546{ 547 548#pragma unused ( context ) 549 550 int result = NSOrderedSame; 551 552 if ( [ obj1 domainID ] < [ obj2 domainID ] ) 553 { 554 result = NSOrderedAscending; 555 } 556 557 if ( [ obj1 domainID ] > [ obj2 domainID ] ) 558 { 559 result = NSOrderedDescending; 560 } 561 562 return result; 563 564} 565 566 567//����������������������������������������������������������������������������� 568// ClearIterator - Clears devices from an iterator without performing 569// any action 570//����������������������������������������������������������������������������� 571 572static void 573ClearIterator ( io_iterator_t iterator ) 574{ 575 576 io_service_t service = MACH_PORT_NULL; 577 578 service = IOIteratorNext ( iterator ); 579 while ( service != MACH_PORT_NULL ) 580 { 581 582 IOObjectRelease ( service ); 583 service = IOIteratorNext ( iterator ); 584 585 } 586 587} 588 589 590#if 0 591#pragma mark - 592#pragma mark C->Obj-C glue 593#pragma mark - 594#endif 595 596 597//����������������������������������������������������������������������������� 598// AppearedNotificationHandler - C->Obj-C glue 599//����������������������������������������������������������������������������� 600 601static void 602AppearedNotificationHandler ( void * refCon, io_iterator_t iterator ) 603{ 604 605 SCSITargetProberAppDelegate * app = ( SCSITargetProberAppDelegate * ) refCon; 606 [ app appearedNotification: iterator ]; 607 608} 609 610 611//����������������������������������������������������������������������������� 612// DisappearedNotificationHandler - C->Obj-C glue 613//����������������������������������������������������������������������������� 614 615static void 616DisappearedNotificationHandler ( void * refCon, io_iterator_t iterator ) 617{ 618 619 SCSITargetProberAppDelegate * app = ( SCSITargetProberAppDelegate * ) refCon; 620 [ app disappearedNotification: iterator ]; 621 622}