MyDocument.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 "MyDocument.h"
#import "AuthoringDeviceTester.h"
#import "AuthoringDevice.h"

#import <sys/mount.h>
#import <IOKit/IOBSD.h>
#import <IOKit/storage/IOMedia.h>
#import <IOKit/scsi/SCSICommandOperationCodes.h>
#import <IOKit/scsi/SCSITask.h>
#import <Carbon/Carbon.h>


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

enum
{
	kUnmountFailed		= -2,
	kUnmountCancelled 	= -1,
	kStatusOK			= 0
};

#define	kNumBlocks				(1)
#define	kRequestSize			(2048 * kNumBlocks)
#define	kMyDocumentString		@"MyDocument"
#define kAlertString			@"Alert"
#define kOKString				@"OK"
#define kCancelString			@"Cancel"


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

#define	DEBUG									0
#define DEBUG_ASSERT_COMPONENT_NAME_STRING		"MyDocument"
#include <AssertMacros.h>


@implementation MyDocument


//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	+myDocument - factory method
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

+ ( MyDocument * ) myDocument
{
	return [ [ [ self alloc ] init ] autorelease ];
}


//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
// init
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

- ( id ) init
{
	
	[ super init ];
	[ NSBundle loadNibNamed : [ self windowNibName] owner : self ];

	return self;
	
}


//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
// dealloc
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

- ( void ) dealloc
{
	
	[ self setTheAuthoringDeviceTester: nil ];
	[ super dealloc ];
	
}


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


- ( NSWindow * ) theWindow { return theWindow; }
- ( AuthoringDeviceTester * ) theAuthoringDeviceTester { return theAuthoringDeviceTester; }
- ( void ) setTheAuthoringDeviceTester: ( AuthoringDeviceTester * ) value
{

	[ value retain ];
	[ theAuthoringDeviceTester release ];
	theAuthoringDeviceTester = value;

}

- ( AuthoringDevice * ) theAuthoringDevice { return theAuthoringDevice; }
- ( void ) setTheAuthoringDevice: ( AuthoringDevice * ) value
{
	
	NSMutableString *	titleString = [ NSMutableString stringWithCapacity: 0 ];
	
	[ value retain ];
	[ theAuthoringDevice release ];
	theAuthoringDevice = value;
	
	[ self setTheAuthoringDeviceTester: [ AuthoringDeviceTester authoringDeviceTesterForDevice: value withParentDoc: self ] ];

	[ testButton setTarget: [ self theAuthoringDeviceTester ] ];
	
	[ titleString setString: [ theAuthoringDevice theVendorName ] ];
	[ titleString appendString: @" " ];
	[ titleString appendString: [ theAuthoringDevice theProductName ] ];
	[ [ self theWindow ] setTitle: titleString ];
	
}


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


//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	switchTest:
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

- ( IBAction ) switchTest: ( id ) sender
{
	
	NSString	* testString = [ sender titleOfSelectedItem ];
	
	// Calls the function "testBlah" where Blah represents the
	// text of the popup menu (e.g. testModeSense10). The outlet
	// for the testButton is set to be the AuthoringDeviceTester 
	[ testButton setAction: NSSelectorFromString ( [ NSString stringWithFormat:@"%@%@", @"test", testString ] ) ];
	
}


//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	isExclusiveAccessAvailable:
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

- ( IBAction ) isExclusiveAccessAvailable: ( id ) sender
{
	
	MMCDeviceInterface **		theInterface	= NULL;
	SCSITaskDeviceInterface **	devInterface	= NULL;
	Boolean						status			= false;
	
	theInterface = [ [ self theAuthoringDeviceTester ] interface ];
	require ( ( theInterface != NULL ), ErrorExit );
	
	devInterface = ( *theInterface )->GetSCSITaskDeviceInterface ( theInterface );
	require ( ( devInterface != NULL ), ErrorExit );
	
	// Ok. We have the SCSITaskDeviceInterface, let's see if
	// exclusive access is available.
	status = ( *devInterface )->IsExclusiveAccessAvailable ( devInterface );
	if ( status == true )
	{
		[ self appendLogText: @"Exclusive Access Is Available.\n\n" ];
	}
	
	else
	{
		[ self appendLogText: @"Exclusive Access Is Not Available.\n\n" ];
	}
	
	( *devInterface )->Release ( devInterface );
	
	
ErrorExit:
	
	
	return;
	
}


//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	getExclusiveAccess:
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

- ( IBAction ) getExclusiveAccess: ( id ) sender
{
	
	MMCDeviceInterface **	theInterface	= NULL;
	int						result			= 0;
	
	// Try to unmount the media if there is any.
	result = [ self unmountMedia ];
	
	// If the dialog was cancelled just return.
	if ( result == kUnmountCancelled )
	{
		return;
	}
	
	else if ( result == kUnmountFailed )
	{
		[ self appendLogText: @"*********** ERROR Unable to unmount media ***********\n\n" ];
	}
	
	theInterface = [ [ self theAuthoringDeviceTester ] interface ];
	
	// Else, we can proceed because no media was present. This allows us to just do our test.
	if ( theInterface != NULL )
	{
		
		SCSITaskDeviceInterface **	devInterface = NULL;
		
		devInterface = ( *theInterface )->GetSCSITaskDeviceInterface ( theInterface );
		if ( devInterface != NULL )
		{
			
			// Ok. We have the SCSITaskDeviceInterface, let's use it to get exclusive access
			// and run our test suite.
			[ self runExclusiveTestSuite: devInterface ];
			
			( *devInterface )->Release ( devInterface );
			
		}
		
	}
	
}


//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	clearLog:
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

- ( IBAction ) clearLog: ( id ) sender
{
	
	if ( testOutputTextView )
	{
		
		NSRange	range = NSMakeRange ( 0, [ [ testOutputTextView string ] length ] );
		
		// Replace the range with an empty string
		[ testOutputTextView replaceCharactersInRange: range withString: @"" ];
		
	}
	
}


//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	unmountMedia
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

- ( int ) unmountMedia
{
	
	io_service_t	service 	= MACH_PORT_NULL;
	io_service_t	child 		= MACH_PORT_NULL;
	IOReturn		err			= kIOReturnSuccess;
	int				result		= kStatusOK;
	
	// Get a handle to the io_service_t that represents our MMC Device
	service = [ [ self theAuthoringDeviceTester ] getServiceObject: [ self theAuthoringDevice ] ];
	require ( ( service != MACH_PORT_NULL ), ErrorExit );
	
	// Get the IO*BlockStorageDriver node
	err = IORegistryEntryGetChildEntry ( service, kIOServicePlane, &child );
	( void ) IOObjectRelease ( service );
	require ( ( err == kIOReturnSuccess ), ErrorExit );
	
	// Check for existence of an IOMedia node below the IO*BlockStorageDriver node
	err = IORegistryEntryGetChildEntry ( child, kIOServicePlane, &service );
	( void ) IOObjectRelease ( child );
	require ( ( err == kIOReturnSuccess ), ErrorExit );
	require ( ( service != NULL ), ErrorExit );
	
	// There is an IOMedia object. We should find its name, and present a dialog
	// to see if the user wants to unmount the volume.
	result = [ self findWholeMediaBSDName: service ];
	( void ) IOObjectRelease ( service );
	
	
ErrorExit:
	
	
	return result;
	
}


//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	findWholeMediaBSDName
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

- ( int ) findWholeMediaBSDName : ( io_service_t ) ioMediaNub
{
	
	CFMutableDictionaryRef	dict	= NULL;
	CFStringRef				bsdName	= NULL;
	IOReturn				err		= kIOReturnSuccess;
	int						result	= kUnmountFailed;
	
	// Sanity Check
	require ( ( IOObjectConformsTo ( ioMediaNub, kIOMediaClass ) ), ErrorExit );
	
	// Get the CF Properties for the IOMedia object
	err = IORegistryEntryCreateCFProperties ( ioMediaNub, &dict, kCFAllocatorDefault, kNilOptions );
	require ( ( err == kIOReturnSuccess ), ErrorExit );
	
	// Check for the BSD Name key
	if ( CFDictionaryContainsKey ( dict, CFSTR ( kIOBSDNameKey ) ) )
	{
		
		// Get the value for it
		bsdName = ( CFStringRef ) CFDictionaryGetValue ( dict, CFSTR ( kIOBSDNameKey ) );
		result = [ self unmountAllPartitions: bsdName ];
		
	}
	
	CFRelease ( dict );
	
	
ErrorExit:
	
	
	return result;
	
}


//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	unmountAllPartitions
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

- ( int ) unmountAllPartitions: ( CFStringRef ) bsdName
{
	
	OSStatus			error		= noErr;
	struct statfs *		fsStats		= NULL;
	int					result		= kUnmountFailed;
	int					numMounts	= 0;
	unsigned int		index		= 0;
	FSRef				volFSRef	= { { 0 } };
	FSCatalogInfo		volumeInfo	= { 0 };
	
	// Get the mount info for all the mountpoints. We will traverse that data
	// and try and find any mountpoints which have the diskX identifier in them.
	numMounts = getmntinfo ( &fsStats, MNT_NOWAIT );
	require ( ( numMounts != 0 ), ErrorExit );
	
	for ( index = 0; index < numMounts; index++ )
	{
		
		// make a CFStringRef with the name of the BSD dev node/file descriptor
		CFStringRef cfStringMntOnName  = CFStringCreateWithCString (	NULL,
																		&fsStats[index].f_mntfromname[5] /* Starting at [5] gets rid of the /dev/ */,
																		CFStringGetSystemEncoding ( ) );
		
		// Compare the BSD dev node to the one we have. This is akin to doing a strncmp.
		if ( CFStringCompareWithOptions ( cfStringMntOnName,
										  bsdName,
										  CFRangeMake ( 0, CFStringGetLength ( bsdName ) ),
										  0 ) == kCFCompareEqualTo )
		{
			
			// Pop up a simple alert panel to warn the user that the disc will be unmounted
			result = NSRunAlertPanel ( kAlertString,
									   [ NSString stringWithFormat: @"Are you sure you want to unmount the disc: %s?", &fsStats[index].f_mntonname[9] ],
									   kOKString,
									   kCancelString,
									   nil );
			
			if ( result == NSAlertAlternateReturn )
			{
				
				// User cancelled unmount.
				result = kUnmountCancelled;
				CFRelease ( cfStringMntOnName );
				cfStringMntOnName = NULL;
				goto ErrorExit;
				
			}
			
			// Ask Carbon to unmount the partition.
			error = FSPathMakeRef ( fsStats[index].f_mntonname,
									&volFSRef,
									NULL );
			if ( error != noErr )
			{				
				
				result = kUnmountFailed;
				CFRelease ( cfStringMntOnName );
				cfStringMntOnName = NULL;
				goto ErrorExit;
				
			}
			
			error = FSGetCatalogInfo (	&volFSRef,
										kFSCatInfoVolume,
										&volumeInfo,
										NULL,
										NULL,
										NULL );
			if ( error != noErr )
			{
				
				result = kUnmountFailed;
				CFRelease ( cfStringMntOnName );
				cfStringMntOnName = NULL;
				goto ErrorExit;
				
			}
			
			error = FSUnmountVolumeSync ( volumeInfo.volume, 0, NULL );
			if ( error != noErr )
			{
				
				result = kUnmountFailed;
				CFRelease ( cfStringMntOnName );
				cfStringMntOnName = NULL;
				goto ErrorExit;
				
			}
			
		}
		
		CFRelease ( cfStringMntOnName );
		cfStringMntOnName = NULL;
		
	}
	
	result = kStatusOK;
	
	
ErrorExit:
	
	
	return result;
	
}


//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	runExclusiveTestSuite
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

- ( void ) runExclusiveTestSuite: ( SCSITaskDeviceInterface ** ) interface
{
	
	SCSIServiceResponse				serviceResponse	= kSCSIServiceResponse_Request_In_Process;
	SCSITaskStatus					taskStatus		= kSCSITaskStatus_No_Status;
	SCSI_Sense_Data					senseData		= { 0 };
	SCSICommandDescriptorBlock		cdb				= { 0 };
	SCSITaskInterface **			task			= NULL;
	IOReturn						err	 			= kIOReturnSuccess;
	UInt64							transferCount 	= 0;
	UInt32							transferCountHi = 0;
	UInt32							transferCountLo = 0;				
	UInt8 *							buffer			= NULL;
	IOVirtualRange					range;
	
	[ self appendLogText: @"runExclusiveTestSuite\n\n" ];
	
	// Get exclusive access to the device
	err = ( *interface )->ObtainExclusiveAccess ( interface );
	require ( ( err == kIOReturnSuccess ), ErrorExit );
	
	// Create a task now that we have exclusive access
	task = ( *interface )->CreateSCSITask ( interface );
	require ( ( task != NULL ), ReleaseExclusiveAccess );
	
	// Allocate a buffer the size of our request
	buffer = ( UInt8 * ) calloc ( 1, kRequestSize );
	require ( ( buffer != NULL ), ReleaseTask );
	
	// Set up the range.
	range.address = ( IOVirtualAddress ) buffer;
	range.length  = kRequestSize;
	
	// We're going to execute a READ_10 to the device as a
	// test of exclusive commands.
	cdb[0] = kSCSICmd_READ_10;
	
	// Set the block to be the block zero
	*( UInt32 * ) &cdb[2] = 0;
	
	// Tell the drive we only want this many blocks
	*( UInt16 * ) &cdb[7] = kNumBlocks;
	
	// Set the actual cdb in the task
	err = ( *task )->SetCommandDescriptorBlock ( task, cdb, kSCSICDBSize_6Byte );
	require ( ( err == kIOReturnSuccess ), ReleaseBuffer );
	
	// Set the scatter-gather entry in the task
	err = ( *task )->SetScatterGatherEntries ( task,
											   &range,
											   1,
											   kRequestSize,
											   kSCSIDataTransfer_FromTargetToInitiator );
	require ( ( err == kIOReturnSuccess ), ReleaseBuffer );
	
	// Set the timeout in the task
	err = ( *task )->SetTimeoutDuration ( task, 15000 );
	require ( ( err == kIOReturnSuccess ), ReleaseBuffer );
	
	[ self appendLogText: @"Requesting read from block zero\n\n" ];
	
	// Send it!
	err = ( *task )->ExecuteTaskSync ( task, &senseData, &taskStatus, &transferCount );
	require ( ( err == kIOReturnSuccess ), ReleaseBuffer );
	
	// Get the SCSI service response
	err = ( *task )->GetSCSIServiceResponse ( task, &serviceResponse );
	require ( ( err == kIOReturnSuccess ), ReleaseBuffer );
	
	// Get the transfer counts
	transferCountHi = ( ( transferCount >> 32 ) & 0xFFFFFFFF );
	transferCountLo = ( transferCount & 0xFFFFFFFF );
	
	[ self appendLogText: [ NSString stringWithFormat:
		@"serviceResponse = %d (%@), taskStatus = %d (%@)\n",
		serviceResponse,
		[ [ self theAuthoringDeviceTester ] stringFromServiceResponse: serviceResponse ],
		taskStatus,
		[ [ self theAuthoringDeviceTester ] stringFromTaskStatus: taskStatus ] ] ];
	
	require ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ), ReleaseBuffer );
	
	if ( taskStatus == kSCSITaskStatus_GOOD )
	{
		
		int		index = 0;
		
		[ self appendLogText: [ NSString stringWithFormat:
			@"transferCountHi = 0x%08lx, transferCountLo = 0x%08lx\n\n", transferCountHi, transferCountLo ] ];
		[ self appendLogText: @"Data from read:\n\n" ];
		
		for ( index = 0; index < kRequestSize; index += 8 )
		{
			
			// Print hex values
			[ self appendLogText: [ NSString stringWithFormat: @"0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x \t\t\t",
									buffer[0+index], buffer[1+index], buffer[2+index], buffer[3+index],
									buffer[4+index], buffer[5+index], buffer[6+index], buffer[7+index] ] ];
			
			// Print ascii values
			[ self appendLogText: [ NSString stringWithFormat: @"%c%c%c%c%c%c%c%c\n",
									( ( ( buffer[0+index] > 0x7F ) || ( buffer[0+index] == 0x0a ) )  ? '.' : buffer[0+index] ),
									( ( ( buffer[1+index] > 0x7F ) || ( buffer[1+index] == 0x0a ) )  ? '.' : buffer[1+index] ),
									( ( ( buffer[2+index] > 0x7F ) || ( buffer[2+index] == 0x0a ) )  ? '.' : buffer[2+index] ),
									( ( ( buffer[3+index] > 0x7F ) || ( buffer[3+index] == 0x0a ) )  ? '.' : buffer[3+index] ),
									( ( ( buffer[4+index] > 0x7F ) || ( buffer[4+index] == 0x0a ) )  ? '.' : buffer[4+index] ),
									( ( ( buffer[5+index] > 0x7F ) || ( buffer[5+index] == 0x0a ) )  ? '.' : buffer[5+index] ),
									( ( ( buffer[6+index] > 0x7F ) || ( buffer[6+index] == 0x0a ) )  ? '.' : buffer[6+index] ),
									( ( ( buffer[7+index] > 0x7F ) || ( buffer[7+index] == 0x0a ) )  ? '.' : buffer[7+index] ) ] ];
			
		}
		
	}
	
	else if ( taskStatus == kSCSITaskStatus_CHECK_CONDITION )
	{
		[ [ self theAuthoringDeviceTester ] printSenseString: &senseData addRawValues: true ];
	}
	
	
ReleaseBuffer:
	
	
	require_quiet ( ( buffer != NULL ), ReleaseTask );
	free ( buffer );
	buffer = NULL;
	
	
ReleaseTask:
	
	
	require_quiet ( ( task != NULL ), ReleaseExclusiveAccess );
	( *task )->Release ( task );
	task = NULL;
	
	
ReleaseExclusiveAccess:
	
	
	err = ( *interface )->ReleaseExclusiveAccess ( interface );
	require ( ( err == kIOReturnSuccess ), ReleaseTask );
	
	
ErrorExit:
	
	
	[ self appendLogText: @"\n" ];
	
}


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


//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ
//	appendLogText:
//ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ

- ( void ) appendLogText: ( NSString * ) logString;
{

	if ( testOutputTextView && logString )
	{

        NSScrollView *	logScrollView = [ testOutputTextView enclosingScrollView ];		
		NSRange      	range 	= NSMakeRange ( [ [ testOutputTextView string ] length ], 0 );
        NSRange      	newVisibleRange;

		// First, append the string
		[ testOutputTextView replaceCharactersInRange: range withString: logString ];
		
		// Now, scroll to the end if that's where we were before, or if there's no selection
		if ( logScrollView )
		{
			
			NSRect   visibleRect = [ logScrollView documentVisibleRect ];
			
			if ( floor ( NSMaxY ( [ testOutputTextView bounds] ) - NSMaxY ( visibleRect ) ) >= 0 )
			{
				
				newVisibleRange = NSMakeRange ( [ [ testOutputTextView string ] length ], 0 );
                [ testOutputTextView scrollRangeToVisible: newVisibleRange ];
			
			}
            
			else
			{
				newVisibleRange = [ testOutputTextView selectedRange ];
            }
        
		}
        
		else
		{
			newVisibleRange = [ testOutputTextView selectedRange ];            
		}
		
		if ( ( newVisibleRange.location == NSNotFound ) || ( newVisibleRange.length == 0 ) )
		{
			newVisibleRange = range;
		}
		
		[ testOutputTextView scrollRangeToVisible: newVisibleRange ];
		
	}
	
}


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


- ( NSString * ) windowNibName
{
    // Override returning the nib file name of the document
    // If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead.
    return kMyDocumentString;
}


- ( void ) windowControllerDidLoadNib: ( NSWindowController * ) aController
{
    [ super windowControllerDidLoadNib:aController ];
    // Add any code here that need to be executed once the windowController has loaded the document's window.
}


- ( NSData * ) dataRepresentationOfType: ( NSString * ) aType
{
    // Insert code here to write your document from the given data.  You can also choose to override -fileWrapperRepresentationOfType: or -writeToFile:ofType: instead.
    return nil;
}


- ( BOOL ) loadDataRepresentation: ( NSData * ) data ofType: ( NSString * ) aType
{
    // Insert code here to read your document from the given data.  You can also choose to override -loadFileWrapperRepresentation:ofType: or -readFromFile:ofType: instead.
    return YES;
}


- ( void ) awakeFromNib
{
	
	NSString	* testString = [ testPopUpButton titleOfSelectedItem ];
	
	[ testButton setAction: NSSelectorFromString ( [ NSString stringWithFormat:@"%@%@", @"test", testString ] ) ];
	
}


@end