SCSIInitiator.m   [plain text]

/*
* 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 "SCSIInitiator.h"
#import "SCSITargetProberKeys.h"
#import <IOKit/IOKitLib.h>
#import <IOKit/storage/IOStorageProtocolCharacteristics.h>

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

#define kSCSIInitiatorIdentifierString "SCSI Initiator Identifier"
#define kSocketNumberString "Socket Number"

#define kPCIKeyModel "model"
#define kPCIKeyVendorID "vendor-id"
#define kPCIKeyVersion "version"
#define kPCIKeyName "name"

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

static int
deviceComparator ( id obj1, id obj2, void * context );

//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
// Implementation
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

@implementation SCSIInitiator

- ( NSArray * ) devices { return devices; }

- ( void ) dealloc
{

[ self setInitiatorDevice: nil ];
[ self setDevices: nil ];

}

- ( void ) setInitiatorDevice: ( SCSIDevice * ) i
{

[ i retain ];
[ initiatorDevice release ];
initiatorDevice = i;

}

- ( void ) setDevices: ( NSMutableArray * ) d
{

[ d retain ];
[ devices release ];
devices = d;

}

- ( int ) initiatorID
{
return initiatorID;
}

- ( int ) domainID
{
return domainID;
}

- ( NSString * ) title
{

NSString * string = @"No Controllers Found";

if ( initiatorDevice != nil )
{
string = [ NSString stringWithFormat: @"%d: %@", domainID, [ initiatorDevice title ]];
}

return string;

}

- ( id ) init
{

self = [ super init ];

if ( self != nil )
{

[ self setDevices: [ [ [ NSMutableArray alloc ] init ] autorelease ] ];
[ self setInitiatorDevice: nil ];

}

return self;

}

- ( id ) initWithService: ( io_service_t ) service
{

self = [ super init ];

if ( self != nil )
{

[ self setDevices: [ [ [ NSMutableArray alloc ] init ] autorelease ] ];
[ self setInitiatorDevice: nil ];
[ self setInitiatorProperties: service ];
[ self setTargetProperties: service ];

}

return self;

}

// Called to get the SCSI Domain ID for the io_service_t.

+ ( int ) domainIDForService: ( io_service_t ) service
{

int domain = 0;
id value = nil;

// Get the protocol characteristics key.
value = ( id ) IORegistryEntrySearchCFProperty ( service,
kIOServicePlane,
CFSTR ( kIOPropertyProtocolCharacteristicsKey ),
kCFAllocatorDefault,
0 );

// Get the SCSI Domain ID.
domain = [ [ value objectForKey: @kIOPropertySCSIDomainIdentifierKey ] intValue ];
[ value release ];

return domain;

}

// Called to add a target device to the devices array.

- ( void ) addTargetDevice: ( SCSIDevice * ) newDevice
{

int count = 0;
int index = 0;
SCSIDevice * device = nil;

count = [ devices count ];

// Broadcast to any listeners that we are updating the devices array.
[ self willChangeValueForKey: kDevicesKeyPath ];

for ( index = 0; index < count; index++ )
{

device = [ devices objectAtIndex: index ];

// Is this the correct device?
if ( [ [ device deviceIdentifier ] intValue ] == [ [ newDevice deviceIdentifier ] intValue ] )
{

// Yes, replace the device at this location with the updated one.
[ devices replaceObjectAtIndex: index withObject: newDevice ];
break;

}

}

// Broadcast to any listeners that we finished updating the devices array.
[ self didChangeValueForKey: kDevicesKeyPath ];

}

- ( void ) removeTargetDevice: ( int ) targetID
{

int count = 0;
int index = 0;
SCSIDevice * device = nil;

count = [ devices count ];

// Broadcast to any listeners that we are updating the devices array.
[ self willChangeValueForKey: kDevicesKeyPath ];

for ( index = 0; index < count; index++ )
{

// Is this the correct device?
device = [ devices objectAtIndex: index ];
if ( [ [ device deviceIdentifier ] intValue ] == targetID )
{

// Yes, force the information to get cleared since the
// target no longer exists.
[ device clearInformation ];

}

}

// Broadcast to any listeners that we finished updating the devices array.
[ self didChangeValueForKey: kDevicesKeyPath ];

}

// Called to set the properties up for the initiator.

- ( void ) setInitiatorProperties: ( io_service_t ) service
{

id value = nil;

// Create the device which represents the initiator.
initiatorDevice = [ [ SCSIDevice alloc ] init ];

// Set some initial defaults.
[ initiatorDevice setDevicePresent: YES ];
[ initiatorDevice setIsInitiator: YES ];

// Find the initiator identifier.
value = ( id ) IORegistryEntrySearchCFProperty ( service,
kIOServicePlane,
CFSTR ( kSCSIInitiatorIdentifierString ),
kCFAllocatorDefault,
0 );

initiatorID = [ value intValue ];
[ value release ];

// Find the SCSI Domain Identifier.
value = ( id ) IORegistryEntrySearchCFProperty ( service,
kIOServicePlane,
CFSTR ( kIOPropertyProtocolCharacteristicsKey ),
kCFAllocatorDefault,
0 );

domainID = [ [ value objectForKey: @kIOPropertySCSIDomainIdentifierKey ] intValue ];

// Set the physical interconnect.
[ initiatorDevice setPhysicalInterconnect: [ value objectForKey: @kIOPropertyPhysicalInterconnectTypeKey ] ];
[ value release ];

// Set the domainID and deviceID for the initiator device.
[ initiatorDevice setDomainIdentifier: [ NSNumber numberWithInt: domainID ] ];
[ initiatorDevice setDeviceIdentifier: [ NSNumber numberWithInt: initiatorID ] ];

// Cheesy hack to find out if this is Card Bus or PCI,
// need a better way to determine this in the future.
value = ( id ) IORegistryEntrySearchCFProperty ( service,
kIOServicePlane,
CFSTR ( kSocketNumberString ),
kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents );
if ( value == nil )
[ initiatorDevice setImage: [ NSImage imageNamed: kPCICardImageString ] ];
else
[ initiatorDevice setImage: [ NSImage imageNamed: kPCCardImageString ] ];

// The card doesn't have any features. Maybe we can add some later...
[ initiatorDevice setFeatures: nil ];

// Not all PCI/CardBus devices have these keys. Need to look at more
// cards and see if we can glean more information for things like Firmware Revision,
// FCode revision, etc. Maybe in the next release...
value = ( id ) IORegistryEntrySearchCFProperty ( service,
kIOServicePlane,
CFSTR ( kPCIKeyVersion ),
kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents );

[ initiatorDevice setRevision: [ NSString stringWithCString: value ? [ value bytes ] : "Unknown" ] ];

value = ( id ) IORegistryEntrySearchCFProperty ( service,
kIOServicePlane,
CFSTR ( kPCIKeyModel ),
kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents );

[ initiatorDevice setModel: [ NSString stringWithCString: value ? [ value bytes ] : "Unknown" ] ];

value = ( id ) IORegistryEntrySearchCFProperty ( service,
kIOServicePlane,
CFSTR ( kPCIKeyName ),
kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents );
[ initiatorDevice setManufacturer: [ NSString stringWithCString: value ? [ value bytes ] : "Unknown" ] ];

// Add the initiator device to the list of devices.
[ devices addObject: initiatorDevice ];

}

// Called to set the properties up for the target devices.

- ( void ) setTargetProperties: ( io_service_t ) service
{

IOReturn result = kIOReturnSuccess;
io_iterator_t iterator = MACH_PORT_NULL;
SCSIDevice * device = nil;

// Get a child iterator.
result = IORegistryEntryGetChildIterator ( service,
kIOServicePlane,
&iterator );

if ( result == kIOReturnSuccess )
{

io_service_t child = MACH_PORT_NULL;

child = IOIteratorNext ( iterator );

while ( child != MACH_PORT_NULL )
{

// Create a SCSIDevice object to represent this device.
device = [ [ SCSIDevice alloc ] initWithService: child ];

// Add the device to the list.
[ devices addObject: device ];

// Proceed to the next...
IOObjectRelease ( child );
child = IOIteratorNext ( iterator );

}

IOObjectRelease ( iterator );
iterator = MACH_PORT_NULL;

}

// Sort the devices we found. The sorting function uses the
// targetID to sort. This is arbitrary, but works well in this
// case since we display stuff from lowest targetID to highest.
[ devices sortUsingFunction: deviceComparator context: nil ];

}

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

//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
// deviceComparator - Compares devices based on deviceIdentifier field
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

int
deviceComparator ( id obj1, id obj2, void * context )
{

#pragma unused ( context )

int result = NSOrderedSame;

if ( [ [ obj1 deviceIdentifier ] intValue ] < [ [ obj2 deviceIdentifier ] intValue ] )
{
result = NSOrderedAscending;
}

else if ( [ [ obj1 deviceIdentifier ] intValue ] > [ [ obj2 deviceIdentifier ] intValue ] )
{
result = NSOrderedDescending;
}

return result;

}

@end