/* * Copyright (c) 1998-2005 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // Includes //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // Libkern includes #include #include #include #include #include #include // Generic IOKit related headers #include #include #include #include #include #include #include // IOKit storage headers #include // SCSI Architecture Model Family includes #include "SCSITaskDefinition.h" #include "SCSILibraryRoutines.h" #include "SCSIPrimaryCommands.h" #include "SCSICmds_INQUIRY_Definitions.h" #include "SCSICmds_REPORT_LUNS_Definitions.h" #include "SCSICmds_REQUEST_SENSE_Defs.h" #include "IOSCSITargetDevice.h" #include "IOSCSITargetDeviceHashTable.h" #include "SCSITargetDevicePathManager.h" #include "SCSIPathManagers.h" //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // Macros //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ #define DEBUG 0 #define DEBUG_ASSERT_COMPONENT_NAME_STRING "SCSI Target Device" #if DEBUG #define SCSI_TARGET_DEVICE_DEBUGGING_LEVEL 0 #endif #include "IOSCSIArchitectureModelFamilyDebugging.h" #if ( SCSI_TARGET_DEVICE_DEBUGGING_LEVEL >= 1 ) #define PANIC_NOW(x) IOPanic x #else #define PANIC_NOW(x) #endif #if ( SCSI_TARGET_DEVICE_DEBUGGING_LEVEL >= 2 ) #define ERROR_LOG(x) IOLog x; IOSleep (1) #else #define ERROR_LOG(x) #endif #if ( SCSI_TARGET_DEVICE_DEBUGGING_LEVEL >= 3 ) #define STATUS_LOG(x) IOLog x; IOSleep (1) #else #define STATUS_LOG(x) #endif #define kSCSICmd_INQUIRY_StandardDataAllString "SCSICmd_INQUIRY_StandardDataAll" #define super IOSCSIPrimaryCommandsDevice OSDefineMetaClassAndStructors ( IOSCSITargetDevice, IOSCSIPrimaryCommandsDevice ); //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // Constants //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ #define kTURMaxRetries 1 #define kMaxInquiryAttempts 2 #define kSCSILogicalUnitZero 0 #if 0 #pragma mark - #pragma mark ₯ Public Methods #pragma mark - #endif //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ Create - Creates a new target device. [PUBLIC][STATIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOSCSITargetDevice::Create ( IOSCSIProtocolServices * provider ) { bool result = false; IOSCSITargetDevice * newDevice = NULL; // Allocate the object. newDevice = OSTypeAlloc ( IOSCSITargetDevice ); require_nonzero ( newDevice, ErrorExit ); // Init the object. result = newDevice->init ( 0 ); require ( result, ReleaseTargetExit ); // Attach the object. result = newDevice->attach ( provider ); require ( result, DetachTargetExit ); // Start the object. result = newDevice->start ( provider ); require ( result, DetachTargetExit ); // Create the target device or path to a previously existing // target (in the case of multipathing). result = newDevice->CreateTargetDeviceOrPath ( provider ); require ( result, DetachTargetExit ); newDevice->release ( ); newDevice = NULL; return result; DetachTargetExit: newDevice->stop ( provider ); newDevice->detach ( provider ); ReleaseTargetExit: require_nonzero_quiet ( newDevice, ErrorExit ); newDevice->release ( ); newDevice = NULL; ErrorExit: return result; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ CreateTargetDeviceOrPath - Creates a new target device or path to an // existing target device. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOSCSITargetDevice::CreateTargetDeviceOrPath ( IOSCSIProtocolServices * provider ) { bool result = true; // Verify that a target is indeed connected for this instantiation. result = VerifyTargetPresence ( ); require ( result, ErrorExit ); // Set all appropriate Registry Properties so that they are available if needed. RetrieveCharacteristicsFromProvider ( ); // Determine the SCSI Target Device characteristics for the target // that is represented by this object. result = DetermineTargetCharacteristics ( ); require ( result, ErrorExit ); result = IsProviderAnotherPathToTarget ( provider ); if ( result == false ) { // Create a path manager. fPathManager = SCSIPressurePathManager::Create ( this, provider ); check ( fPathManager ); // Finally, perform a LUN scan. ScanForLogicalUnits ( ); } else { // Our provider is a path to an existing target, so we must // detach ourselves from our provider here (our provider has // already been attach'ed to the existing target). detach ( provider ); } result = true; ErrorExit: return result; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ IsProviderAnotherPathToTarget - Checks if we have this target device // in global hash table. If so, it simply // adds a new path and returns false, // otherwise, returns true. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOSCSITargetDevice::IsProviderAnotherPathToTarget ( IOSCSIProtocolServices * provider ) { bool result = false; OSObject * nodeUniqueIdentifier = NULL; STATUS_LOG ( ( "+IOSCSITargetDevice::IsProviderAnotherPathToTarget\n" ) ); nodeUniqueIdentifier = GetNodeUniqueIdentifier ( ); if ( nodeUniqueIdentifier != NULL ) { IOSCSITargetDeviceHashTable * ht = NULL; ht = IOSCSITargetDeviceHashTable::GetSharedInstance ( ); if ( OSDynamicCast ( OSData, nodeUniqueIdentifier ) ) result = ht->IsProviderPathToExistingTarget ( this, provider, ht->Hash ( ( OSData * ) nodeUniqueIdentifier ) ); else result = ht->IsProviderPathToExistingTarget ( this, provider, ht->Hash ( ( OSString * ) nodeUniqueIdentifier ) ); } STATUS_LOG ( ( "-IOSCSITargetDevice::IsProviderAnotherPathToTarget\n" ) ); return result; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ SetHashEntry - Set the hash entry corresponding to this target device. // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSITargetDevice::SetHashEntry ( void * newEntry ) { fTargetHashEntry = newEntry; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetNodeUniqueIdentifier - Gets the node's unique identifier (unit // serial number, EUI-64, or FCNameIdentifier) // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ OSObject * IOSCSITargetDevice::GetNodeUniqueIdentifier ( void ) { return fNodeUniqueIdentifier; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ SetNodeUniqueIdentifier - Sets the node's unique identifier (unit // serial number, EUI-64, or FCNameIdentifier) // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSITargetDevice::SetNodeUniqueIdentifier ( OSObject * uniqueID ) { require_nonzero ( uniqueID, ErrorExit ); uniqueID->retain ( ); if ( fNodeUniqueIdentifier != NULL ) fNodeUniqueIdentifier->release ( ); fNodeUniqueIdentifier = uniqueID; #if DEBUG setProperty ( "Node Unique ID", fNodeUniqueIdentifier ); #endif /* DEBUG */ ErrorExit: return; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ AddPath - Adds a path to the target device [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSITargetDevice::AddPath ( IOSCSIProtocolServices * provider ) { STATUS_LOG ( ( "+IOSCSITargetDevice::AddPath\n" ) ); attach ( provider ); provider->open ( this ); if ( fPathManager != NULL ) { fPathManager->AddPath ( provider ); messageClients ( kIOMessageServicePropertyChange ); } STATUS_LOG ( ( "-IOSCSITargetDevice::AddPath\n" ) ); } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ handleOpen - Handles opens on the object. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOSCSITargetDevice::handleOpen ( IOService * client, IOOptionBits options, void * arg ) { bool result = false; // It's an open from a multi-LUN client require_nonzero ( fClients, ErrorExit ); require_nonzero ( OSDynamicCast ( IOSCSILogicalUnitNub, client ), ErrorExit ); result = fClients->setObject ( client ); ErrorExit: return result; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ handleClose - Handles closes on the object. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSITargetDevice::handleClose ( IOService * client, IOOptionBits options ) { require_nonzero ( fClients, Exit ); if ( fClients->containsObject ( client ) ) { fClients->removeObject ( client ); if ( ( fClients->getCount ( ) == 0 ) && isInactive ( ) ) { message ( kIOMessageServiceIsRequestingClose, getProvider ( ), 0 ); } } Exit: return; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ handleIsOpen - Figures out if there are any opens on this object. // [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOSCSITargetDevice::handleIsOpen ( const IOService * client ) const { bool result = false; require_nonzero ( fClients, CallSuperClassError ); // General case (is anybody open) if ( ( client == NULL ) && ( fClients->getCount ( ) != 0 ) ) { result = true; } else { // specific case (is this client open) result = fClients->containsObject ( client ); } return result; CallSuperClassError: result = super::handleIsOpen ( client ); return result; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ message - Called for broadcasted messages. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOSCSITargetDevice::message ( UInt32 type, IOService * nub, void * arg ) { IOReturn result = kIOReturnSuccess; switch ( type ) { case kSCSIPort_NotificationStatusChange: { IOSCSIProtocolServices * path = NULL; path = OSDynamicCast ( IOSCSIProtocolServices, nub ); // Port status is changing, let path manager object know // about it. if ( ( fPathManager != NULL ) && ( path != NULL ) ) { fPathManager->PathStatusChanged ( path, ( UInt32 ) arg ); messageClients ( kIOMessageServicePropertyChange ); } } break; default: { STATUS_LOG ( ( "IOSCSITargetDevice::Unknown message type = 0x%08x\n", type ) ); result = super::message ( type, nub, arg ); } break; } return result; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ detach - Detaches a path from the target device. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSITargetDevice::detach ( IOService * provider ) { STATUS_LOG ( ( "+IOSCSITargetDevice::detach\n" ) ); if ( fPathManager != NULL ) { fPathManager->RemovePath ( ( IOSCSIProtocolServices * ) provider ); messageClients ( kIOMessageServicePropertyChange ); } super::detach ( provider ); STATUS_LOG ( ( "-IOSCSITargetDevice::detach\n" ) ); } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ free - Called to free any resource before object destruction. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSITargetDevice::free ( void ) { STATUS_LOG ( ( "+IOSCSITargetDevice::free\n" ) ); if ( fTargetHashEntry != NULL ) { IOSCSITargetDeviceHashTable * ht = NULL; ht = IOSCSITargetDeviceHashTable::GetSharedInstance ( ); ht->DestroyHashReference ( fTargetHashEntry ); fTargetHashEntry = NULL; } if ( fPathManager != NULL ) { fPathManager->release ( ); fPathManager = NULL; } if ( fNodeUniqueIdentifier != NULL ) { fNodeUniqueIdentifier->release ( ); fNodeUniqueIdentifier = NULL; } super::free ( ); STATUS_LOG ( ( "-IOSCSITargetDevice::free\n" ) ); } #if 0 #pragma mark - #pragma mark ₯ Protected Methods - Methods used by this class and subclasses #pragma mark - #endif //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ InitializeDeviceSupport - Initializes device support [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOSCSITargetDevice::InitializeDeviceSupport ( void ) { bool result = false; // Allocate space for our set that will keep track of the LUNs. fClients = OSSet::withCapacity ( 8 ); require_nonzero ( fClients, ErrorExit ); fClients->setCapacityIncrement ( 8 ); result = true; ErrorExit: return result; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ StartDeviceSupport - Starts device support. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSITargetDevice::StartDeviceSupport ( void ) { return; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ScanForLogicalUnits - Scans for SCSI Logical Units in the Target Device. // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSITargetDevice::ScanForLogicalUnits ( void ) { UInt64 countLU = 0; UInt64 loopLU = 0; OSData * data = NULL; bool supportsREPORTLUNS = false; bool result = false; STATUS_LOG ( ( "+IOSCSITargetDevice::ScanForLogicalUnits\n" ) ); // Try to determine the available Logical Units by using the REPORT_LUNS // command. // Create the Logical Unit nodes for each valid LUN. If the number // of valid LUNs could not be determined, should we create a Logical Unit // node for each possible LUN and have it determine if there is a valid // Logical Unit for its LUN? // Determine the maximum number of Logical Units supported by the device // and protocol. countLU = DetermineMaximumLogicalUnitNumber ( ); // Try to determine the available Logical Units by issuing the // REPORT_LUNS command to the target device. supportsREPORTLUNS = PerformREPORTLUNS ( ); if ( supportsREPORTLUNS == false ) { ERROR_LOG ( ( "Device does not support REPORT_LUNS command, creating LUNs by brute-force\n" ) ); // No, the device doesn't support REPORT_LUNS. Do a brute force // LUN scan starting at Logical Unit zero and going up to the highest // supported LUN the physical interconnect reported that it // supports. // Optimization: We don't have to verify Logical Unit zero exists. We've already done // that by verifying this target device exists. CreateLogicalUnit ( kSCSILogicalUnitZero ); for ( loopLU = 1; loopLU <= countLU; loopLU++ ) { bool LUNPresent = false; // Verify the LUN exists. LUNPresent = VerifyLogicalUnitPresence ( loopLU ); if ( LUNPresent == true ) { // It exists, create an object to represent it. CreateLogicalUnit ( loopLU ); } } } // Get the standard INQUIRY data. data = OSDynamicCast ( OSData, getProperty ( kSCSICmd_INQUIRY_StandardDataAllString ) ); // Check if we removed the standard INQUIRY data already. If so, // then don't submit the data to the lower layer driver for requesting // physical interconnect specific features. if ( data != NULL ) { // Check if the protocol layer driver needs the inquiry data for // any reason. SCSI Parallel uses this to determine Wide, // Sync, DT, QAS, IU, etc. result = IsProtocolServiceSupported ( kSCSIProtocolFeature_SubmitDefaultInquiryData, NULL ); if ( result == true ) { HandleProtocolServiceFeature ( kSCSIProtocolFeature_SubmitDefaultInquiryData, ( void * ) data->getBytesNoCopy ( ) ); } // Remove the property to free up the memory. removeProperty ( kSCSICmd_INQUIRY_StandardDataAllString ); // Make target device visible in the IORegistry. registerService ( ); } STATUS_LOG ( ( "-IOSCSITargetDevice::ScanForLogicalUnits\n" ) ); } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ PerformREPORTLUNS - Issues the REPORT_LUNS command. Returns true if the // device supports the command and valid data is // returned. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOSCSITargetDevice::PerformREPORTLUNS ( void ) { bool result = false; bool supportsREPORTLUNS = false; SCSICmd_REPORT_LUNS_Header * header = NULL; SCSICmd_REPORT_LUNS_Header * buffer = NULL; UInt32 length = 0; STATUS_LOG ( ( "+IOSCSITargetDevice::PerformREPORTLUNS\n" ) ); // Check to see the specification that this device claims compliance with // and if it is after SPC (SCSI-3), see if it supports the REPORT_LUNS // command. require ( ( fTargetANSIVersion >= kINQUIRY_ANSI_VERSION_SCSI_SPC_Compliant ), ErrorExit ); // Allocate the header so we can see how much data we really need to // retrieve from the device. header = IONew ( SCSICmd_REPORT_LUNS_Header, 1 ); require_nonzero ( header, ErrorExit ); // Zero it. bzero ( header, sizeof ( SCSICmd_REPORT_LUNS_Header ) ); // Retrieve REPORT_LUNS data. result = RetrieveReportLUNsData ( kSCSILogicalUnitZero, ( UInt8 * ) header, ( UInt32 ) sizeof ( SCSICmd_REPORT_LUNS_Header ) ); require ( result, ReleaseHeader ); // Get the full length. length = OSSwapBigToHostInt32 ( header->LUN_LIST_LENGTH ) + kREPORT_LUNS_HeaderSize; STATUS_LOG ( ( "length = %ld\n", length ) ); require ( ( length >= sizeof ( SCSICmd_REPORT_LUNS_Header ) ), ReleaseHeader ); // Allocate the buffer for the full LUN data. buffer = ( SCSICmd_REPORT_LUNS_Header * ) IOMalloc ( length ); require_nonzero ( buffer, ReleaseHeader ); result = RetrieveReportLUNsData ( kSCSILogicalUnitZero, ( UInt8 * ) buffer, length ); require ( result, ReleaseBuffer ); // Sanity checks on buffer passed back. The device should respond with the same data, // but devices aren't always trustworthy... require ( ( header->LUN_LIST_LENGTH == buffer->LUN_LIST_LENGTH ), ReleaseBuffer ); // Parse the REPORT_LUNS information and build the LUNs reported. ParseReportLUNsInformation ( buffer ); supportsREPORTLUNS = true; ReleaseBuffer: require_nonzero_quiet ( buffer, ReleaseHeader ); IOFree ( buffer, length ); buffer = NULL; ReleaseHeader: require_nonzero_quiet ( header, ErrorExit ); IODelete ( header, SCSICmd_REPORT_LUNS_Header, 1 ); header = NULL; ErrorExit: return supportsREPORTLUNS; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ParseReportLUNsInformation - Parses REPORT_LUNS data and creates logical // units. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSITargetDevice::ParseReportLUNsInformation ( SCSICmd_REPORT_LUNS_Header * buffer ) { UInt32 count = 0; UInt32 index = 0; SCSICmd_REPORT_LUNS_LUN_ENTRY * LUN = NULL; bool LUNPresent = false; STATUS_LOG ( ( "+IOSCSITargetDevice::ParseReportLUNsInformation\n" ) ); require_nonzero ( buffer, ErrorExit ); count = OSSwapBigToHostInt32 ( buffer->LUN_LIST_LENGTH ) / ( sizeof ( SCSICmd_REPORT_LUNS_LUN_ENTRY ) ); require ( count, ErrorExit ); STATUS_LOG ( ( "count = %ld\n", count ) ); for ( index = 0; index < count; index++ ) { UInt8 addressMethod = 0; UInt8 logicalUnitNumber = 0; STATUS_LOG ( ( "Processing item %ld\n", index ) ); LUN = &buffer->LUN[index]; LUN->FIRST_LEVEL_ADDRESSING = OSSwapBigToHostInt16 ( LUN->FIRST_LEVEL_ADDRESSING ); addressMethod = LUN->FIRST_LEVEL_ADDRESSING >> kREPORT_LUNS_ADDRESS_METHOD_OFFSET; STATUS_LOG ( ( "Data: 0x%04x : 0x%04x : 0x%04x : 0x%04x\n", OSSwapBigToHostInt16 ( LUN->FIRST_LEVEL_ADDRESSING ), OSSwapBigToHostInt16 ( LUN->SECOND_LEVEL_ADDRESSING ), OSSwapBigToHostInt16 ( LUN->THIRD_LEVEL_ADDRESSING ), OSSwapBigToHostInt16 ( LUN->FOURTH_LEVEL_ADDRESSING ) ) ); STATUS_LOG ( ( "addressMethod = %d\n", addressMethod ) ); // For now, we only support single level LUN addressing using the PERIPHERAL_DEVICE // method of address and with BUS_IDENTIFIER field set to zero (meaning all LUNs are // relative to this device and non-hierarchical) if ( addressMethod == kREPORT_LUNS_ADDRESS_METHOD_PERIPHERAL_DEVICE ) { REPORT_LUNS_PERIPHERAL_DEVICE_ADDRESSING * p; p = ( REPORT_LUNS_PERIPHERAL_DEVICE_ADDRESSING * ) &LUN->FIRST_LEVEL_ADDRESSING; // Make sure the BUS_IDENTIFIER field is zero. We don't support hierarchical LUNs // yet... if ( p->BUS_IDENTIFIER == 0 ) { logicalUnitNumber = p->TARGET_LUN; STATUS_LOG ( ( "logicalUnitNumber = %d\n", logicalUnitNumber ) ); // Don't create more than one logical unit object to // represent the same LUN. LUNPresent = DoesLUNObjectExist ( logicalUnitNumber ); if ( LUNPresent == false ) { LUNPresent = VerifyLogicalUnitPresence ( logicalUnitNumber ); if ( LUNPresent == true ) { CreateLogicalUnit ( logicalUnitNumber ); } } } else { ERROR_LOG ( ( "BUS_IDENTIFIER is non-zero, not creating LUN\n" ) ); } } else { ERROR_LOG ( ( "Not kREPORT_LUNS_ADDRESS_METHOD_PERIPHERAL_DEVICE, not creating LUN\n" ) ); } } // According to SPC-2, Logical Unit zero must always be present. Logical Unit zero has the // option of presenting itself in the REPORT_LUNS LUN list. Some RAID controllers omit // Logical Unit zero from this list since they claim HiSup and have a PERIPHERAL_QUALIFIER // field of 001b or 011b. // // So, create Logical Unit zero if the LUN object doesn't currently exist for it. LUNPresent = DoesLUNObjectExist ( kSCSILogicalUnitZero ); if ( LUNPresent == false ) { CreateLogicalUnit ( kSCSILogicalUnitZero ); } ErrorExit: STATUS_LOG ( ( "-IOSCSITargetDevice::ParseReportLUNsInformation\n" ) ); return; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ExecuteCommand - Executes a command using path manager (if one exists). // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSITargetDevice::ExecuteCommand ( SCSITaskIdentifier request ) { SetTargetLayerReference ( request, ( void * ) this ); if ( fPathManager != NULL ) { fPathManager->ExecuteCommand ( request ); } else { super::ExecuteCommand ( request ); } } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ AbortTask - Aborts a task. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ SCSIServiceResponse IOSCSITargetDevice::AbortTask ( UInt8 logicalUnit, SCSITaggedTaskIdentifier theTag ) { if ( fPathManager != NULL ) { return fPathManager->AbortTask ( logicalUnit, theTag ); } else { return super::AbortTask ( logicalUnit, theTag ); } } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ AbortTaskSet - Aborts a task set. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ SCSIServiceResponse IOSCSITargetDevice::AbortTaskSet ( UInt8 logicalUnit ) { if ( fPathManager != NULL ) { return fPathManager->AbortTaskSet ( logicalUnit ); } else { return super::AbortTaskSet ( logicalUnit ); } } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ClearACA - Clears an ACA condition. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ SCSIServiceResponse IOSCSITargetDevice::ClearACA ( UInt8 logicalUnit ) { if ( fPathManager != NULL ) { return fPathManager->ClearACA ( logicalUnit ); } else { return super::ClearACA ( logicalUnit ); } } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ClearTaskSet - Clears a task set. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ SCSIServiceResponse IOSCSITargetDevice::ClearTaskSet ( UInt8 logicalUnit ) { if ( fPathManager != NULL ) { return fPathManager->ClearTaskSet ( logicalUnit ); } else { return super::ClearTaskSet ( logicalUnit ); } } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ LogicalUnitReset - Resets a particular logical unit. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ SCSIServiceResponse IOSCSITargetDevice::LogicalUnitReset ( UInt8 logicalUnit ) { if ( fPathManager != NULL ) { return fPathManager->LogicalUnitReset ( logicalUnit ); } else { return super::LogicalUnitReset ( logicalUnit ); } } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ TargetReset - Resets the target device. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ SCSIServiceResponse IOSCSITargetDevice::TargetReset ( void ) { if ( fPathManager != NULL ) { return fPathManager->TargetReset ( ); } else { return super::TargetReset ( ); } } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ SuspendDeviceSupport - Suspends device support. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSITargetDevice::SuspendDeviceSupport ( void ) { } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ResumeDeviceSupport - Resumes device support. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSITargetDevice::ResumeDeviceSupport ( void ) { } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ StopDeviceSupport - Stops device support. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSITargetDevice::StopDeviceSupport ( void ) { } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ TerminateDeviceSupport - Terminates device support. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSITargetDevice::TerminateDeviceSupport ( void ) { } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetNumberOfPowerStateTransitions - Unused. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ UInt32 IOSCSITargetDevice::GetNumberOfPowerStateTransitions ( void ) { return 0; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ClearNotReadyStatus - Unused. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOSCSITargetDevice::ClearNotReadyStatus ( void ) { return true; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ SetTargetLayerReference - Sets target layer reference value in task. // [PROTECTED][STATIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOSCSITargetDevice::SetTargetLayerReference ( SCSITaskIdentifier request, void * value ) { SCSITask * scsiRequest = NULL; bool result = false; scsiRequest = OSDynamicCast ( SCSITask, request ); if ( scsiRequest != NULL ) { result = scsiRequest->SetTargetLayerReference ( value ); } return result; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetTargetLayerReference - Gets target layer reference value in task. // [PROTECTED][STATIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void * IOSCSITargetDevice::GetTargetLayerReference ( SCSITaskIdentifier request ) { SCSITask * scsiRequest = NULL; void * result = NULL; scsiRequest = OSDynamicCast ( SCSITask, request ); if ( scsiRequest != NULL ) { result = scsiRequest->GetTargetLayerReference ( ); } return result; } #if 0 #pragma mark - #pragma mark ₯ Power Management Utility Methods #pragma mark - #endif //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetInitialPowerState - Unused. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ UInt32 IOSCSITargetDevice::GetInitialPowerState ( void ) { return 0; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ HandlePowerChange - Unused. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSITargetDevice::HandlePowerChange ( void ) { } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ HandleCheckPowerState - Unused. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSITargetDevice::HandleCheckPowerState ( void ) { } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ TicklePowerManager - Unused. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSITargetDevice::TicklePowerManager ( void ) { } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ RetrieveCharacteristicsFromProvider - Gets characteristics from the // provider. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSITargetDevice::RetrieveCharacteristicsFromProvider ( void ) { OSDictionary * characterDict = NULL; OSObject * obj = NULL; fDefaultInquiryCount = 0; // Check if the personality for this device specifies a preferred protocol obj = GetProtocolDriver ( )->getProperty ( kIOPropertySCSIDeviceCharacteristicsKey ); if ( obj != NULL ) { characterDict = OSDynamicCast ( OSDictionary, obj ); if ( characterDict != NULL ) { setProperty ( kIOPropertySCSIDeviceCharacteristicsKey, characterDict ); // Check if the personality for this device specifies a preferred INQUIRY count. if ( characterDict->getObject ( kIOPropertySCSIInquiryLengthKey ) != NULL ) { OSNumber * defaultInquiry = NULL; STATUS_LOG ( ( "%s: Get Inquiry Length property.\n", getName ( ) ) ); obj = characterDict->getObject ( kIOPropertySCSIInquiryLengthKey ); defaultInquiry = OSDynamicCast ( OSNumber, obj ); if ( defaultInquiry != NULL ) { // This device has a preferred INQUIRY count, use that. fDefaultInquiryCount = defaultInquiry->unsigned32BitValue ( ); } } } } STATUS_LOG ( ( "%s: default inquiry count is: %d\n", getName ( ), fDefaultInquiryCount ) ); obj = GetProtocolDriver ( )->getProperty ( kIOPropertyProtocolCharacteristicsKey ); characterDict = OSDynamicCast ( OSDictionary, obj ); if ( characterDict == NULL ) { characterDict = OSDictionary::withCapacity ( 1 ); } else { characterDict->retain ( ); } if ( characterDict != NULL ) { obj = GetProtocolDriver ( )->getProperty ( kIOPropertyPhysicalInterconnectTypeKey ); if ( obj != NULL ) { characterDict->setObject ( kIOPropertyPhysicalInterconnectTypeKey, obj ); } obj = GetProtocolDriver ( )->getProperty ( kIOPropertyPhysicalInterconnectLocationKey ); if ( obj != NULL ) { characterDict->setObject ( kIOPropertyPhysicalInterconnectLocationKey, obj ); } setProperty ( kIOPropertyProtocolCharacteristicsKey, characterDict ); #if 0 // Set default device usage based on protocol characteristics obj = GetProtocolDriver ( )->getProperty ( kIOPropertySCSIProtocolMultiInitKey ); if ( obj != NULL ) { // The protocol allows for multiple initiators in the SCSI Domain, set device // usage to "shared" to prevent power down a drive that appears idle to this system // but may be in use by others. } #endif characterDict->release ( ); } } #if 0 #pragma mark - #pragma mark ₯ Target Related Member Routines #pragma mark - #endif //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ DetermineTargetCharacteristics - Method to publish INQUIRY VPD data. // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOSCSITargetDevice::DetermineTargetCharacteristics ( void ) { bool result = false; OSObject * obj = NULL; OSDictionary * protocolDict = NULL; result = PublishDefaultINQUIRYInformation ( ); require ( result, ErrorExit ); PublishINQUIRYVitalProductDataInformation ( this, kSCSILogicalUnitZero ); //PublishReportDeviceIdentifierData ( this, kSCSILogicalUnitZero ); // Check to see if the HBA inserted a property by calling SetTargetProperty(). protocolDict = OSDynamicCast ( OSDictionary, getProperty ( kIOPropertyProtocolCharacteristicsKey ) ); require_nonzero ( protocolDict, ErrorExit ); // Check if the Node WWN property is there. obj = protocolDict->getObject ( kIOPropertyFibreChannelNodeWorldWideNameKey ); require_nonzero ( obj, ErrorExit ); // Set the Node Unique Identifier. SetNodeUniqueIdentifier ( obj ); ErrorExit: return result; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ PublishDefaultINQUIRYInformation - Publishes default INQUIRY information. // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOSCSITargetDevice::PublishDefaultINQUIRYInformation ( void ) { bool result = false; UInt8 length = 0; OSData * data = NULL; SCSICmd_INQUIRY_StandardDataAll * inqData = NULL; inqData = IONew ( SCSICmd_INQUIRY_StandardDataAll, 1 ); require_nonzero ( inqData, ErrorExit ); // Determine the total amount of data that this target has available for // the INQUIRY command. if ( fDefaultInquiryCount != 0 ) { // There is a default INQUIRY size for this device, just report that // value back. length = fDefaultInquiryCount; } else { length = kINQUIRY_StandardDataHeaderSize + 1; // Since there is not a default INQUIRY size for this device, determine what // the INQUIRY data size should be by sending an INQUIRY command for 6 bytes // (In actuality, only 5 bytes are needed, but asking for 6 alleviates the // problem that some FireWire and USB to ATAPI bridges exhibit). result = RetrieveDefaultINQUIRYData ( kSCSILogicalUnitZero, ( UInt8 * ) inqData, length ); require ( result, ReleaseBuffer ); length = inqData->ADDITIONAL_LENGTH + kINQUIRY_StandardDataHeaderSize; } // Before we register ourself as a nub, we need to find out what // type of device we want to connect to us. // Do an Inquiry command and parse the data to determine the peripheral // device type. result = RetrieveDefaultINQUIRYData ( kSCSILogicalUnitZero, ( UInt8 * ) inqData, length ); require ( result, ReleaseBuffer ); SetCharacteristicsFromINQUIRY ( inqData ); data = OSData::withBytes ( ( const void * ) inqData, sizeof ( SCSICmd_INQUIRY_StandardDataAll ) ); require_nonzero ( data, ReleaseBuffer ); setProperty ( kSCSICmd_INQUIRY_StandardDataAllString, data ); data->release ( ); data = NULL; IODelete ( inqData, SCSICmd_INQUIRY_StandardDataAll, 1 ); inqData = NULL; result = true; return result; ReleaseBuffer: require_nonzero_quiet ( inqData, ErrorExit ); IODelete ( inqData, SCSICmd_INQUIRY_StandardDataAll, 1 ); inqData = NULL; ErrorExit: result = false; return result; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ PublishINQUIRYVitalProductDataInformation - Check Page 00h for VPD pages // supported by this target. Then, publish interesting pages (80h, 83h) // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSITargetDevice::PublishINQUIRYVitalProductDataInformation ( IOService * object, SCSILogicalUnitNumber logicalUnit ) { bool result = false; UInt8 length = 0; UInt8 index = 0; UInt8 pageLength = 0; SCSICmd_INQUIRY_Page00_Header * data = NULL; UInt8 * bytes = NULL; IOBufferMemoryDescriptor * buffer = NULL; STATUS_LOG ( ( "+IOSCSITargetDevice::PublishINQUIRYVitalProductDataInformation\n" ) ); buffer = IOBufferMemoryDescriptor::withCapacity ( kINQUIRY_MaximumDataSize, kIODirectionIn ); require_nonzero ( buffer, ErrorExit ); length = sizeof ( SCSICmd_INQUIRY_Page00_Header ); data = ( SCSICmd_INQUIRY_Page00_Header * ) buffer->getBytesNoCopy ( ); bytes = ( UInt8 * ) data; require_nonzero ( data, ReleaseBuffer ); bzero ( data, kINQUIRY_MaximumDataSize ); result = RetrieveINQUIRYDataPage ( logicalUnit, bytes, kINQUIRY_Page00_PageCode, length ); require ( result, ReleaseBuffer ); STATUS_LOG ( ( "PERIPHERAL_DEVICE_TYPE = 0x%02x\n", data->PERIPHERAL_DEVICE_TYPE ) ); STATUS_LOG ( ( "PAGE_CODE = 0x%02x\n", data->PAGE_CODE ) ); STATUS_LOG ( ( "PAGE_LENGTH = %d\n", data->PAGE_LENGTH ) ); pageLength = data->PAGE_LENGTH; length = data->PAGE_LENGTH + sizeof ( SCSICmd_INQUIRY_Page00_Header ); STATUS_LOG ( ( "length = %d\n", length ) ); result = RetrieveINQUIRYDataPage ( logicalUnit, bytes, kINQUIRY_Page00_PageCode, length ); require ( result, ReleaseBuffer ); require ( ( data->PAGE_CODE == kINQUIRY_Page00_PageCode ), ReleaseBuffer ); require ( ( data->PAGE_LENGTH == pageLength ), ReleaseBuffer ); // If Vital Data Page zero was successfully retrieved, check to see if page 80h or 83h // the Device Identification page is supported and if so, retrieve that data. for ( index = sizeof ( SCSICmd_INQUIRY_Page00_Header ); index < length; index++ ) { STATUS_LOG ( ( "PAGE_CODE_ENTRY[%d] = 0x%02x\n", ( int ) ( index - sizeof ( SCSICmd_INQUIRY_Page00_Header ) ), bytes[index] ) ); if ( bytes[index] == kINQUIRY_Page80_PageCode ) { PublishUnitSerialNumber ( object, logicalUnit ); } if ( bytes[index] == kINQUIRY_Page83_PageCode ) { PublishDeviceIdentification ( object, logicalUnit ); } } ReleaseBuffer: require_nonzero_quiet ( buffer, ErrorExit ); buffer->release ( ); buffer = NULL; ErrorExit: STATUS_LOG ( ( "-IOSCSITargetDevice::PublishINQUIRYVitalProductDataInformation\n" ) ); return; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ VerifyTargetPresence - Verifies target presence. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOSCSITargetDevice::VerifyTargetPresence ( void ) { bool presenceVerified = false; STATUS_LOG ( ( "+IOSCSITargetDevice::VerifyTargetPresence\n" ) ); presenceVerified = VerifyLogicalUnitPresence ( kSCSILogicalUnitZero ); return presenceVerified; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ SetCharacteristicsFromINQUIRY - Sets characteristics from INQUIRY // data. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOSCSITargetDevice::SetCharacteristicsFromINQUIRY ( SCSICmd_INQUIRY_StandardDataAll * inquiryBuffer ) { OSString * string = NULL; char tempString[17] = { 0 }; // Maximum + 1 for null char // Set target characteristics // Save the target's Peripheral Device Type fTargetPeripheralDeviceType = ( inquiryBuffer->PERIPHERAL_DEVICE_TYPE & kINQUIRY_PERIPHERAL_TYPE_Mask ); // Save the SCSI ANSI version that the device to which the device claims compliance. fTargetANSIVersion = ( inquiryBuffer->VERSION & kINQUIRY_ANSI_VERSION_Mask ); // Set the other supported features fTargetHasHiSup = ( inquiryBuffer->RESPONSE_DATA_FORMAT & kINQUIRY_Byte3_HISUP_Mask ); fTargetHasSCCS = ( inquiryBuffer->SCCSReserved & kINQUIRY_Byte5_SCCS_Mask ); fTargetHasEncServs = ( inquiryBuffer->flags1 & kINQUIRY_Byte6_ENCSERV_Mask ); fTargetHasMultiPorts = ( inquiryBuffer->flags1 & kINQUIRY_Byte6_MULTIP_Mask ); fTargetHasMChanger = ( inquiryBuffer->flags1 & kINQUIRY_Byte6_MCHNGR_Mask ); #if DEBUG setProperty ( "ANSI Version", fTargetANSIVersion, 8 ); setProperty ( "HiSup", fTargetHasHiSup ); setProperty ( "SCCS", fTargetHasSCCS ); setProperty ( "EncServs", fTargetHasEncServs ); setProperty ( "MultiPorts", fTargetHasMultiPorts ); setProperty ( "MChanger", fTargetHasMChanger ); #endif /* DEBUG */ // Set the Peripheral Device Type property for the device. setProperty ( kIOPropertySCSIPeripheralDeviceType, ( UInt64 ) fTargetPeripheralDeviceType, kIOPropertySCSIPeripheralDeviceTypeSize ); // Set the Vendor Identification property for the device. bcopy ( inquiryBuffer->VENDOR_IDENTIFICATION, tempString, kINQUIRY_VENDOR_IDENTIFICATION_Length ); tempString[kINQUIRY_VENDOR_IDENTIFICATION_Length] = 0; StripWhiteSpace ( tempString, kINQUIRY_VENDOR_IDENTIFICATION_Length ); string = OSString::withCString ( tempString ); if ( string != NULL ) { setProperty ( kIOPropertySCSIVendorIdentification, string ); string->release ( ); string = NULL; } // Set the Product Identification property for the device. bcopy ( inquiryBuffer->PRODUCT_IDENTIFICATION, tempString, kINQUIRY_PRODUCT_IDENTIFICATION_Length ); tempString[kINQUIRY_PRODUCT_IDENTIFICATION_Length] = 0; StripWhiteSpace ( tempString, kINQUIRY_PRODUCT_IDENTIFICATION_Length ); string = OSString::withCString ( tempString ); if ( string != NULL ) { setProperty ( kIOPropertySCSIProductIdentification, string ); string->release ( ); string = NULL; } // Set the Product Revision Level property for the device. bcopy ( inquiryBuffer->PRODUCT_REVISION_LEVEL, tempString, kINQUIRY_PRODUCT_REVISION_LEVEL_Length ); tempString[kINQUIRY_PRODUCT_REVISION_LEVEL_Length] = 0; StripWhiteSpace ( tempString, kINQUIRY_PRODUCT_REVISION_LEVEL_Length ); string = OSString::withCString ( tempString ); if ( string != NULL ) { setProperty ( kIOPropertySCSIProductRevisionLevel, string ); string->release ( ); string = NULL; } return true; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ TargetTaskCompletion - Hook to check for Unit Attention conditions. // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSITargetDevice::TargetTaskCompletion ( SCSITaskIdentifier request ) { if ( ( GetServiceResponse ( request ) == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_CHECK_CONDITION ) ) { bool validSense = false; SCSI_Sense_Data senseBuffer = { 0 }; validSense = GetAutoSenseData ( request, &senseBuffer, sizeof ( senseBuffer ) ); if ( validSense ) { ERROR_LOG ( ( "IOSCSITargetDevice::TargetTaskCompletion - SENSE_KEY = %d, ASC = 0x%02x, ASCQ = 0x%02x\n", senseBuffer.SENSE_KEY & kSENSE_KEY_Mask, senseBuffer.ADDITIONAL_SENSE_CODE, senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER ) ); // Check for Unit Attention errors. if ( ( senseBuffer.SENSE_KEY & kSENSE_KEY_Mask ) == kSENSE_KEY_UNIT_ATTENTION ) { if ( ( senseBuffer.ADDITIONAL_SENSE_CODE == 0x3F ) && ( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x0E ) ) { // REPORT_LUNS DATA HAS CHANGED IOLog ( "REPORT_LUNS DATA HAS CHANGED\n" ); } else if ( ( senseBuffer.ADDITIONAL_SENSE_CODE == 0x3F ) && ( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x03 ) ) { // INQUIRY DATA HAS CHANGED IOLog ( "INQUIRY DATA HAS CHANGED\n" ); } } } } TaskCompletedNotification ( request ); } #if 0 #pragma mark - #pragma mark ₯ Logical Unit Related Member Routines #pragma mark - #endif //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ RetrieveReportLUNsData - OBSOLETE. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOSCSITargetDevice::RetrieveReportLUNsData ( SCSILogicalUnitNumber logicalUnit, UInt8 * dataBuffer, UInt8 dataSize ) { return RetrieveReportLUNsData ( logicalUnit, dataBuffer, ( UInt32 ) dataSize ); } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ RetrieveReportLUNsData - Gets REPORT_LUNS data from device. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOSCSITargetDevice::RetrieveReportLUNsData ( SCSILogicalUnitNumber logicalUnit, UInt8 * dataBuffer, UInt32 dataSize ) { SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; IOMemoryDescriptor * bufferDesc = NULL; SCSITaskIdentifier request = NULL; bool result = false; bufferDesc = IOMemoryDescriptor::withAddress ( ( void * ) dataBuffer, dataSize, kIODirectionIn ); require_nonzero ( bufferDesc, ErrorExit ); request = GetSCSITask ( ); require_nonzero ( request, ReleaseBuffer ); if ( REPORT_LUNS ( request, bufferDesc, dataSize, 0 ) == true ) { SetLogicalUnitNumber ( request, logicalUnit ); serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS ); if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { result = true; } else { bool validSense = false; SCSI_Sense_Data senseBuffer = { 0 }; validSense = GetAutoSenseData ( request, &senseBuffer, sizeof ( senseBuffer ) ); if ( validSense ) { ERROR_LOG ( ( "REPORT_LUNS failed, serviceResponse = %d, taskStatus = %d\n", serviceResponse, GetTaskStatus ( request ) ) ); ERROR_LOG ( ( "SENSE_KEY = %d, ASC = 0x%02x, ASCQ = 0x%02x\n", senseBuffer.SENSE_KEY & kSENSE_KEY_Mask, senseBuffer.ADDITIONAL_SENSE_CODE, senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER ) ); } } } ReleaseSCSITask ( request ); request = NULL; ReleaseBuffer: require_nonzero_quiet ( bufferDesc, ErrorExit ); bufferDesc->release ( ); bufferDesc = NULL; ErrorExit: return result; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ DetermineMaximumLogicalUnitNumber - Determins Max LUN supported. // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ UInt64 IOSCSITargetDevice::DetermineMaximumLogicalUnitNumber ( void ) { UInt32 protocolLUNCount = 0; bool supported = false; // If this is a SCSI-3 device or later, issue the REPORT_LUNS command // to determine the possible Logical Units supported by the device. // else get the maxmium supported value from the Protocol Services // driver and scan to that limit. supported = IsProtocolServiceSupported ( kSCSIProtocolFeature_GetMaximumLogicalUnitNumber, ( void * ) &protocolLUNCount ); if ( supported == false ) { // Since the protocol does not report that it supports a maximum // Logical Unit Number, it does not support Logical Units at all // meaning that only LUN 0 is valid. protocolLUNCount = 0; } return protocolLUNCount; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ VerifyLogicalUnitPresence - Verifies the presence of a logical unit. // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOSCSITargetDevice::VerifyLogicalUnitPresence ( SCSILogicalUnitNumber logicalUnit ) { bool presenceVerified = false; SCSITaskIdentifier request = NULL; UInt8 TURCount = 0; STATUS_LOG ( ( "+IOSCSITargetDevice::VerifyLogicalUnitPresence\n" ) ); request = GetSCSITask ( ); require_nonzero ( request, ErrorExit ); do { SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; TEST_UNIT_READY ( request, 0x00 ); STATUS_LOG ( ( "Sending TEST_UNIT_READY %d to LUN %d\n", TURCount + 1, ( int ) logicalUnit ) ); SetLogicalUnitNumber ( request, logicalUnit ); // The command was successfully built, now send it serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS ); if ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) { if ( GetTaskStatus ( request ) == kSCSITaskStatus_CHECK_CONDITION ) { bool validSense = false; SCSI_Sense_Data senseBuffer = { 0 }; validSense = GetAutoSenseData ( request, &senseBuffer, sizeof ( senseBuffer ) ); if ( validSense == false ) { IOMemoryDescriptor * bufferDesc = NULL; bufferDesc = IOMemoryDescriptor::withAddress ( ( void * ) &senseBuffer, sizeof ( SCSI_Sense_Data ), kIODirectionIn ); if ( bufferDesc != NULL ) { REQUEST_SENSE ( request, bufferDesc, kSenseDefaultSize, 0 ); serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS ); bufferDesc->release ( ); } } // Check the sense data to see if the TUR was sent to an invalid LUN and if so, // abort trying to access this Logical Unit. We used to check the sense key for // ILLEGAL_REQUEST, but some devices which aren't spun up yet will set NOT_READY // for the SENSE_KEY. Might as well not use it... if ( ( senseBuffer.ADDITIONAL_SENSE_CODE == 0x25 ) && ( senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER == 0x00 ) ) { ERROR_LOG ( ( "Logical unit = %lld not valid\n", logicalUnit ) ); break; } ERROR_LOG ( ( "SENSE_KEY = %d, ASC/ASCQ = 0x%02x/0x%02x", senseBuffer.SENSE_KEY & kSENSE_KEY_Mask, senseBuffer.ADDITIONAL_SENSE_CODE, senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER ) ); } // The SCSI Task completed with status meaning that a target was found // set that the presence was verified. presenceVerified = true; } else if ( ( serviceResponse == kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_DeviceNotResponding ) ) { ERROR_LOG ( ( "taskStatus = DeviceNotResponding\n" ) ); } else if ( ( serviceResponse == kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_DeviceNotPresent ) ) { ERROR_LOG ( ( "taskStatus = DeviceNotPresent\n" ) ); } TURCount++; } while ( ( presenceVerified == false ) && ( TURCount < kTURMaxRetries ) ); ReleaseSCSITask ( request ); ErrorExit: STATUS_LOG ( ( "+IOSCSITargetDevice::VerifyLogicalUnitPresence, LUN %lld present = %s\n", logicalUnit, presenceVerified ? "yes" : "no" ) ); return presenceVerified; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ CreateLogicalUnit - Creates a logical unit. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOSCSITargetDevice::CreateLogicalUnit ( SCSILogicalUnitNumber logicalUnit ) { bool result = false; IOSCSILogicalUnitNub * nub = NULL; nub = OSTypeAlloc ( IOSCSILogicalUnitNub ); require_nonzero ( nub, ErrorExit ); result = nub->init ( 0 ); require ( result, ReleaseNub ); nub->SetLogicalUnitNumber ( ( UInt8 ) logicalUnit ); result = nub->attach ( this ); require ( result, ReleaseNub ); PublishINQUIRYVitalProductDataInformation ( nub, logicalUnit ); result = nub->start ( this ); require_action_quiet ( result, ReleaseNub, nub->detach ( this ) ); ReleaseNub: require_nonzero_quiet ( nub, ErrorExit ); nub->release ( ); nub = NULL; ErrorExit: return result; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ SetLogicalUnitNumber - Sets the LUN for a request. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSITargetDevice::SetLogicalUnitNumber ( SCSITaskIdentifier request, SCSILogicalUnitNumber logicalUnit ) { if ( logicalUnit != 0 ) { SCSITask * scsiRequest = NULL; scsiRequest = OSDynamicCast ( SCSITask, request ); if ( scsiRequest != NULL ) { scsiRequest->SetLogicalUnitNumber ( logicalUnit ); } } } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ DoesLUNObjectExist - Check if a LUN object exists. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOSCSITargetDevice::DoesLUNObjectExist ( SCSILogicalUnitNumber logicalUnit ) { bool result = false; OSCollectionIterator * iter = NULL; IOSCSILogicalUnitNub * lun = NULL; OSObject * obj = NULL; iter = OSCollectionIterator::withCollection ( fClients ); if ( iter != NULL ) { BeginLoop: obj = iter->getNextObject ( ); while ( obj != NULL ) { lun = OSDynamicCast ( IOSCSILogicalUnitNub, obj ); if ( ( lun != NULL ) && ( lun->GetLogicalUnitNumber ( ) == logicalUnit ) ) { result = true; break; } if ( iter->isValid ( ) ) { obj = iter->getNextObject ( ); } else { iter->reset ( ); ERROR_LOG ( ( "IOSCSITargetDevice::DoesLUNObjectExist collection changed from underneath iterator\n" ) ); goto BeginLoop; } } iter->release ( ); } return result; } #if 0 #pragma mark - #pragma mark ₯ INQUIRY Utility Member Routines #pragma mark - #endif //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ RetrieveDefaultINQUIRYData - Retrieves as much INQUIRY data as // requested. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOSCSITargetDevice::RetrieveDefaultINQUIRYData ( SCSILogicalUnitNumber logicalUnit, UInt8 * inquiryBuffer, UInt8 inquirySize ) { SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; IOMemoryDescriptor * bufferDesc = NULL; SCSITaskIdentifier request = NULL; int index = 0; bool result = false; bufferDesc = IOMemoryDescriptor::withAddress ( ( void * ) inquiryBuffer, inquirySize, kIODirectionIn ); require_nonzero ( bufferDesc, ErrorExit ); request = GetSCSITask ( ); require_nonzero ( request, ReleaseBuffer ); for ( index = 0; index < kMaxInquiryAttempts; index++ ) { result = INQUIRY ( request, bufferDesc, 0, 0, 0, inquirySize, 0 ); require ( result, ReleaseTask ); SetLogicalUnitNumber ( request, logicalUnit ); serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS ); if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { result = true; break; } else { result = false; IOSleep ( 1000 ); } } ReleaseTask: require_nonzero_quiet ( request, ReleaseBuffer ); ReleaseSCSITask ( request ); request = NULL; ReleaseBuffer: require_nonzero_quiet ( bufferDesc, ErrorExit ); bufferDesc->release ( ); bufferDesc = NULL; ErrorExit: return result; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ RetrieveINQUIRYDataPage - Retrieves as much INQUIRY data for a given // page as requested. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOSCSITargetDevice::RetrieveINQUIRYDataPage ( SCSILogicalUnitNumber logicalUnit, UInt8 * inquiryBuffer, UInt8 inquiryPage, UInt8 inquirySize ) { SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; SCSITaskIdentifier request = NULL; IOMemoryDescriptor * bufferDesc = NULL; bool result = false; bufferDesc = IOMemoryDescriptor::withAddress ( ( void * ) inquiryBuffer, inquirySize, kIODirectionIn ); require_nonzero ( bufferDesc, ErrorExit ); request = GetSCSITask ( ); require_nonzero ( request, ReleaseBuffer ); if ( INQUIRY ( request, bufferDesc, 0, 1, inquiryPage, inquirySize, 0 ) == true ) { SetLogicalUnitNumber ( request, logicalUnit ); serviceResponse = SendCommand ( request, kTenSecondTimeoutInMS ); if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( GetTaskStatus ( request ) == kSCSITaskStatus_GOOD ) ) { result = true; } else { bool validSense = false; SCSI_Sense_Data senseBuffer = { 0 }; ERROR_LOG ( ( "RetrieveINQUIRYDataPage failed, page = 0x%02x, serviceResponse = %d, taskStatus = %d\n", inquiryPage, serviceResponse, GetTaskStatus ( request ) ) ); validSense = GetAutoSenseData ( request, &senseBuffer, sizeof ( senseBuffer ) ); if ( validSense ) { ERROR_LOG ( ( "SENSE_KEY = %d, ASC = 0x%02x, ASCQ = 0x%02x\n", senseBuffer.SENSE_KEY & kSENSE_KEY_Mask, senseBuffer.ADDITIONAL_SENSE_CODE, senseBuffer.ADDITIONAL_SENSE_CODE_QUALIFIER ) ); } } } ReleaseSCSITask ( request ); ReleaseBuffer: require_nonzero_quiet ( bufferDesc, ErrorExit ); bufferDesc->release ( ); bufferDesc = NULL; ErrorExit: return result; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ PublishUnitSerialNumber - Publishes unit serial number // (page 0x80) data. [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSITargetDevice::PublishUnitSerialNumber ( IOService * object, SCSILogicalUnitNumber logicalUnit ) { bool result = false; SCSICmd_INQUIRY_Page80_Header * data = NULL; IOBufferMemoryDescriptor * buffer = NULL; OSString * string = NULL; char serialNumber[kINQUIRY_MaximumDataSize] = { 0 }; UInt8 length = 0; UInt8 pageLength = 0; SInt16 serialLength = 0; STATUS_LOG ( ( "+IOSCSITargetDevice::PublishUnitSerialNumber\n" ) ); buffer = IOBufferMemoryDescriptor::withCapacity ( kINQUIRY_MaximumDataSize, kIODirectionIn ); require_nonzero ( buffer, ErrorExit ); // Reading header should not include PRODUCT_SERIAL_NUMBER field. Adjust // length accordingly. length = sizeof ( SCSICmd_INQUIRY_Page80_Header ) - sizeof ( UInt8 ); data = ( SCSICmd_INQUIRY_Page80_Header * ) buffer->getBytesNoCopy ( ); require_nonzero ( data, ReleaseBuffer ); bzero ( data, kINQUIRY_MaximumDataSize ); // This device reports that it supports Inquiry Page 80 // determine the length of the unit serial number. result = RetrieveINQUIRYDataPage ( logicalUnit, ( UInt8 * ) data, kINQUIRY_Page80_PageCode, length ); require ( result, ReleaseBuffer ); // Verify that the device does indeed have Device Identification information, by checking // that the additional length field is not zero. require_nonzero ( data->PAGE_LENGTH, ReleaseBuffer ); STATUS_LOG ( ( "PERIPHERAL_DEVICE_TYPE = 0x%02x\n", data->PERIPHERAL_DEVICE_TYPE ) ); STATUS_LOG ( ( "PAGE_CODE = 0x%02x\n", data->PAGE_CODE ) ); STATUS_LOG ( ( "PAGE_LENGTH = %d\n", data->PAGE_LENGTH ) ); pageLength = data->PAGE_LENGTH; length = sizeof ( SCSICmd_INQUIRY_Page80_Header ) - sizeof ( UInt8 ) + data->PAGE_LENGTH; STATUS_LOG ( ( "length = %d\n", length ) ); result = RetrieveINQUIRYDataPage ( logicalUnit, ( UInt8 * ) data, kINQUIRY_Page80_PageCode, length ); require ( result, ReleaseBuffer ); require ( ( data->PAGE_CODE == kINQUIRY_Page80_PageCode ), ReleaseBuffer ); require ( ( data->PAGE_LENGTH == pageLength ), ReleaseBuffer ); bcopy ( &data->PRODUCT_SERIAL_NUMBER, serialNumber, data->PAGE_LENGTH ); // €8.4.6 of SPC-2 indicates that if the product serial number is not available, // the device server shall return ASCII spaces in the field. // We treat this as though the device has no unit serial number. Get rid of // all spaces at the end and turn them into NULL characters. StripWhiteSpace ( serialNumber, data->PAGE_LENGTH ); serialLength = strlen ( serialNumber ); // Was the serial number field totally blank? if ( serialLength > 0 ) { // No it wasn't blank, so set the property in the registry. string = OSString::withCString ( serialNumber ); if ( string != NULL ) { object->setProperty ( kIOPropertySCSIINQUIRYUnitSerialNumber, string ); string->release ( ); string = NULL; } } ReleaseBuffer: require_nonzero_quiet ( buffer, ErrorExit ); buffer->release ( ); buffer = NULL; ErrorExit: STATUS_LOG ( ( "-IOSCSITargetDevice::PublishUnitSerialNumber\n" ) ); return; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ PublishDeviceIdentification - Publishes device ID page (page 0x83) data. // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOSCSITargetDevice::PublishDeviceIdentification ( IOService * object, SCSILogicalUnitNumber logicalUnit ) { OSArray * deviceIDs = NULL; SCSICmd_INQUIRY_Page83_Identification_Descriptor * descriptor = NULL; SCSICmd_INQUIRY_Page83_Header * data = NULL; IOBufferMemoryDescriptor * buffer = NULL; UInt8 * bytes = NULL; UInt8 length = 0; UInt8 offset = 0; UInt8 pageLength = 0; bool result = false; STATUS_LOG ( ( "+IOSCSITargetDevice::PublishDeviceIdentification\n" ) ); buffer = IOBufferMemoryDescriptor::withCapacity ( kINQUIRY_MaximumDataSize, kIODirectionIn ); require_nonzero ( buffer, ErrorExit ); length = sizeof ( SCSICmd_INQUIRY_Page83_Header ); data = ( SCSICmd_INQUIRY_Page83_Header * ) buffer->getBytesNoCopy ( ); bytes = ( UInt8 * ) data; require_nonzero ( data, ReleaseBuffer ); bzero ( data, kINQUIRY_MaximumDataSize ); // This device reports that it supports Inquiry Page 83h // determine the length of all descriptors result = RetrieveINQUIRYDataPage ( logicalUnit, bytes, kINQUIRY_Page83_PageCode, length ); require ( result, ReleaseBuffer ); // Verify that the device does indeed have Device Identification information, by checking // that the additional length field is not zero. require_nonzero ( data->PAGE_LENGTH, ReleaseBuffer ); STATUS_LOG ( ( "PAGE_LENGTH = %d\n", data->PAGE_LENGTH ) ); pageLength = data->PAGE_LENGTH; length = data->PAGE_LENGTH + sizeof ( SCSICmd_INQUIRY_Page83_Header ); result = RetrieveINQUIRYDataPage ( logicalUnit, bytes, kINQUIRY_Page83_PageCode, length ); require ( result, ReleaseBuffer ); require ( ( data->PAGE_CODE == kINQUIRY_Page83_PageCode ), ReleaseBuffer ); require ( ( pageLength == data->PAGE_LENGTH ), ReleaseBuffer ); // Create the array to hold the ID dictionaries deviceIDs = OSArray::withCapacity ( 1 ); require_nonzero ( deviceIDs, ReleaseBuffer ); // Set the index to the first ID byte offset = sizeof ( SCSICmd_INQUIRY_Page83_Header ); STATUS_LOG ( ( "PERIPHERAL_DEVICE_TYPE = 0x%02x\n", data->PERIPHERAL_DEVICE_TYPE ) ); STATUS_LOG ( ( "PAGE_CODE = 0x%02x\n", data->PAGE_CODE ) ); STATUS_LOG ( ( "PAGE_LENGTH = %d\n", data->PAGE_LENGTH ) ); STATUS_LOG ( ( "length = %d\n", length ) ); STATUS_LOG ( ( "offset = %d\n", offset ) ); while ( offset < length ) { UInt8 codeSet = 0; UInt8 idType = 0; UInt8 association = 0; OSDictionary * idDictionary = NULL; OSNumber * number = NULL; STATUS_LOG ( ( "Processing SCSICmd_INQUIRY_Page83_Identification_Descriptor\n" ) ); // Create the dictionary for the current device ID idDictionary = OSDictionary::withCapacity ( 1 ); require_nonzero ( idDictionary, ReleaseDeviceIDs ); descriptor = ( SCSICmd_INQUIRY_Page83_Identification_Descriptor * ) &bytes[offset]; STATUS_LOG ( ( "CODE_SET = 0x%02x\n", descriptor->CODE_SET ) ); STATUS_LOG ( ( "IDENTIFIER_TYPE = 0x%02x\n", descriptor->IDENTIFIER_TYPE ) ); STATUS_LOG ( ( "IDENTIFIER_LENGTH = %d\n", descriptor->IDENTIFIER_LENGTH ) ); // Move offset. offset += descriptor->IDENTIFIER_LENGTH + offsetof ( SCSICmd_INQUIRY_Page83_Identification_Descriptor, IDENTIFIER ); // Sanity check the length to make sure it is within bounds of the page data. require_action ( ( offset <= length ), ReleaseDeviceIDs, idDictionary->release ( ) ); #if DEBUG_MORE UInt8 index = 0; for ( index = 0; index < descriptor->IDENTIFIER_LENGTH; index++ ) { UInt8 * identifier = ( UInt8 * ) &descriptor->IDENTIFIER; STATUS_LOG ( ( "identifier[%d] = 0x%02x : ", index, identifier[index] ) ); } STATUS_LOG ( ( "\n" ) ); #endif /* DEBUG_MORE */ // Process identification header codeSet = descriptor->CODE_SET & kINQUIRY_Page83_CodeSetMask; number = OSNumber::withNumber ( codeSet, 8 ); if ( number != NULL ) { idDictionary->setObject ( kIOPropertySCSIINQUIRYDeviceIdCodeSet, number ); number->release ( ); number = NULL; } idType = descriptor->IDENTIFIER_TYPE & kINQUIRY_Page83_IdentifierTypeMask; number = OSNumber::withNumber ( idType, 8 ); if ( number != NULL ) { idDictionary->setObject ( kIOPropertySCSIINQUIRYDeviceIdType, number ); number->release ( ); number = NULL; } association = ( ( descriptor->IDENTIFIER_TYPE & kINQUIRY_Page83_AssociationMask ) >> 4 ); number = OSNumber::withNumber ( association, 8 ); if ( number != NULL ) { idDictionary->setObject ( kIOPropertySCSIINQUIRYDeviceIdAssociation, number ); number->release ( ); number = NULL; } if ( codeSet == kINQUIRY_Page83_CodeSetASCIIData ) { OSString * string = NULL; char identifier[kINQUIRY_MaximumDataSize] = { 0 }; bcopy ( &descriptor->IDENTIFIER, identifier, descriptor->IDENTIFIER_LENGTH ); string = OSString::withCString ( identifier ); if ( string != NULL ) { idDictionary->setObject ( kIOPropertySCSIINQUIRYDeviceIdentifier, string ); string->release ( ); string = NULL; } } else { OSData * idData = NULL; idData = OSData::withBytes ( &descriptor->IDENTIFIER, descriptor->IDENTIFIER_LENGTH ); if ( idData != NULL ) { idDictionary->setObject ( kIOPropertySCSIINQUIRYDeviceIdentifier, idData ); // Are we getting the data for the target device? if ( object == this ) { // Yes, this is the target device, so set the node unique // identifier if this identifier is the EUI-64 or FCNameIdentifier. if ( ( codeSet == kINQUIRY_Page83_CodeSetBinaryData ) && ( ( idType == kINQUIRY_Page83_IdentifierTypeIEEE_EUI64 ) || ( idType == kINQUIRY_Page83_IdentifierTypeFCNameIdentifier ) ) && ( association == ( kINQUIRY_Page83_AssociationDevice >> 4 ) ) ) { SetNodeUniqueIdentifier ( idData ); } } idData->release ( ); idData = NULL; } } STATUS_LOG ( ( "offset = %d\n", offset ) ); deviceIDs->setObject ( idDictionary ); idDictionary->release ( ); idDictionary = NULL; } object->setProperty ( kIOPropertySCSIINQUIRYDeviceIdentification, deviceIDs ); ReleaseDeviceIDs: require_nonzero_quiet ( deviceIDs, ErrorExit ); deviceIDs->release ( ); deviceIDs = NULL; ReleaseBuffer: require_nonzero_quiet ( buffer, ErrorExit ); buffer->release ( ); buffer = NULL; ErrorExit: STATUS_LOG ( ( "-IOSCSITargetDevice::PublishDeviceIdentification\n" ) ); return; } #if 0 #pragma mark - #pragma mark ₯ VTable Padding #pragma mark - #endif // Space reserved for future expansion. OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 1 ); OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 2 ); OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 3 ); OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 4 ); OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 5 ); OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 6 ); OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 7 ); OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 8 ); OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 9 ); OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 10 ); OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 11 ); OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 12 ); OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 13 ); OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 14 ); OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 15 ); OSMetaClassDefineReservedUnused ( IOSCSITargetDevice, 16 );