/* * Copyright (c) 1998-2009 Apple 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 //----------------------------------------------------------------------------- // BSD includes #include // FireWire includes #include #include #include #include // FireWire Transport includes #include "IOFireWireSerialBusProtocolTransport.h" #include "IOFireWireSerialBusProtocolTransportTimestamps.h" //----------------------------------------------------------------------------- // Macros //----------------------------------------------------------------------------- #define DEBUG 0 #define DEBUG_ASSERT_COMPONENT_NAME_STRING "FireWire SBP2 Transport" #include "IOFireWireSerialBusProtocolTransportDebugging.h" #if DEBUG #define FIREWIRE_SBP_TRANSPORT_DEBUGGING_LEVEL 0 #endif #if ( FIREWIRE_SBP_TRANSPORT_DEBUGGING_LEVEL >= 1 ) #define PANIC_NOW(x) panic x #else #define PANIC_NOW(x) #endif #if ( FIREWIRE_SBP_TRANSPORT_DEBUGGING_LEVEL >= 2 ) #define ERROR_LOG(x) IOLog x #else #define ERROR_LOG(x) #endif #if ( FIREWIRE_SBP_TRANSPORT_DEBUGGING_LEVEL >= 3 ) #define DLOG(x) IOLog x #else #define DLOG(x) #endif #define super IOSCSIProtocolServices OSDefineMetaClassAndStructors ( IOFireWireSerialBusProtocolTransport, IOSCSIProtocolServices ) #define TRANSPORT_FAILURE_RETRIES 1 //----------------------------------------------------------------------------- // Structs //----------------------------------------------------------------------------- // SCSI Status Block as defined in Annex B of SBP-2. NB: This is a big-endian // representation of the status block, not a host memory representation. typedef struct FWSBP2SCSIStatusBlock { UInt8 details; UInt8 sbpStatus; UInt16 orbOffsetHi; UInt32 orbOffsetLo; UInt8 status; UInt8 senseKey; UInt8 ASC; UInt8 ASCQ; UInt32 information; UInt32 commandSpecificInformation; UInt32 senseKeyDependent; } FWSBP2SCSIStatusBlock; enum { kSBP2StatusBlock_Details_DeadBit = 3, kSBP2StatusBlock_Details_LenMask = 0x7, kSBP2StatusBlock_Details_DeadMask = (1 << kSBP2StatusBlock_Details_DeadBit), kSBP2StatusBlock_Details_RespMask = 0x30, kSBP2StatusBlock_Details_SrcMask = 0xC0 }; enum { kSBP2StatusBlock_Resp_REQUEST_COMPLETE = 0, kSBP2StatusBlock_Resp_TRANSPORT_FAILURE = 1, kSBP2StatusBlock_Resp_ILLEGAL_REQUEST = 2, kSBP2StatusBlock_Resp_VENDOR_DEPENDENT = 3 }; enum { kSBP2StatusBlock_SBPStatus_NoAdditionalInformation = 0, kSBP2StatusBlock_SBPStatus_RequestTypeUnsupported = 1, kSBP2StatusBlock_SBPStatus_SpeedUnsupported = 2, kSBP2StatusBlock_SBPStatus_PageSizeUnsupported = 3, kSBP2StatusBlock_SBPStatus_AccessDenied = 4, kSBP2StatusBlock_SBPStatus_LogicalUnitNotSupported = 5, kSBP2StatusBlock_SBPStatus_MaxPayloadTooSmall = 6, // 7 - reserved kSBP2StatusBlock_SBPStatus_ResourcesUnavailable = 8, kSBP2StatusBlock_SBPStatus_FunctionRejected = 9, kSBP2StatusBlock_SBPStatus_LoginIDNotRecognized = 10, kSBP2StatusBlock_SBPStatus_DummyORBCompleted = 11, kSBP2StatusBlock_SBPStatus_RequestAborted = 12, kSBP2StatusBlock_SBPStatus_UnspecifiedError = 0xFF }; enum { kFWSBP2SCSIStatusBlock_StatusBlockFormatCurrentError = (0 << 6), kFWSBP2SCSIStatusBlock_StatusBlockFormatDeferredError = (1 << 6), kFWSBP2SCSIStatusBlock_StatusBlockFormatMask = 0xC0, // reserved 2 // Vendor defined 3 kFWSBP2SCSIStatusBlock_StatusBlockStatusMask = 0x3F }; enum { kFWSBP2SCSIStatusBlock_SenseKeyMask = 0x0F, kFWSBP2SCSIStatusBlock_ILI_Bit = 4, kFWSBP2SCSIStatusBlock_ILI_Mask = (1 << kFWSBP2SCSIStatusBlock_ILI_Bit), kFWSBP2SCSIStatusBlock_EOM_Bit = 5, kFWSBP2SCSIStatusBlock_EOM_Mask = (1 << kFWSBP2SCSIStatusBlock_EOM_Bit), kFWSBP2SCSIStatusBlock_FILEMARK_Bit = 6, kFWSBP2SCSIStatusBlock_FILEMARK_Mask = (1 << kFWSBP2SCSIStatusBlock_FILEMARK_Bit), kFWSBP2SCSIStatusBlock_VALID_Bit = 7, kFWSBP2SCSIStatusBlock_VALID_Mask = (1 << kFWSBP2SCSIStatusBlock_VALID_Bit), kFWSBP2SCSIStatusBlock_MEI_Mask = kFWSBP2SCSIStatusBlock_ILI_Mask | kFWSBP2SCSIStatusBlock_EOM_Mask | kFWSBP2SCSIStatusBlock_FILEMARK_Mask }; //----------------------------------------------------------------------------- // Constants //----------------------------------------------------------------------------- #define kPreferredNameKey "Preferred Name" #define kFireWireGUIDKey "GUID" #define kFireWireVendorNameKey "FireWire Vendor Name" #define kSBP2ReceiveBufferByteCountKey "SBP2ReceiveBufferByteCount" #define kAlwaysSetAutoSenseData "Always Set AutoSense Data" #define kDontUsePTPacketLimitKey "Do Not Use PT Packet Limit" #define kDefaultIOBlockCount 256 #define kCRSModelInfo_ValidBitsMask 0x00FFFFFF #define kCRSModelInfo_TargetDiskMode 0x0054444D #define kIOFireWireMessageServiceIsRequestingClose kIOFWMessageServiceIsRequestingClose #define kDefaultTimeOutValue 30000 #define kCommandPoolOrbCount 1 #define kFWSBP2DefaultPageTableEntriesCount 512 #define kDoubleBufferCommandSizeCheckThreshold 512 #define kLoginRetryCount 32 #define kLoginDelayTime 1000000 enum { kFireWireSBP2CommandTransferDataToTarget = 0L, kFireWireSBP2CommandTransferDataFromTarget = kFWSBP2CommandTransferDataFromTarget }; //----------------------------------------------------------------------------- // Class //----------------------------------------------------------------------------- class FWSBP2TransportGlobals { public: // Constructor FWSBP2TransportGlobals ( void ); // Destructor virtual ~FWSBP2TransportGlobals ( void ); }; //----------------------------------------------------------------------------- // Prototypes //----------------------------------------------------------------------------- static inline void RecordFireWireTimeStamp ( unsigned int code, unsigned int a = 0, unsigned int b = 0, unsigned int c = 0, unsigned int d = 0 ); static int FirewireSBPTransportSysctl ( struct sysctl_oid * oidp, void * arg1, int arg2, struct sysctl_req * req ); //----------------------------------------------------------------------------- // Globals //----------------------------------------------------------------------------- static FWSBP2TransportGlobals gFWGlobals; UInt32 gSBP2DiskDebugFlags = 0; SYSCTL_PROC ( _debug, OID_AUTO, FirewireSBPTransport, CTLFLAG_RW, 0, 0, FirewireSBPTransportSysctl, "FirewireSBPTransport", "FireWire SBP2 Transport debug interface" ); //----------------------------------------------------------------------------- // FirewireSBPTransportSysctl - Sysctl handler. [PRIVATE] //----------------------------------------------------------------------------- static int FirewireSBPTransportSysctl ( struct sysctl_oid * oidp, void * arg1, int arg2, struct sysctl_req * req ) { int error = 0; FWSysctlArgs fwArgs; DEBUG_UNUSED ( oidp ); DEBUG_UNUSED ( arg1 ); DEBUG_UNUSED ( arg2 ); DLOG ( ( "+FirewireSBPTransportSysctl: gSBP2DiskDebugFlags = 0x%08X\n", ( unsigned int ) gSBP2DiskDebugFlags ) ); error = SYSCTL_IN ( req, &fwArgs, sizeof ( fwArgs ) ); if ( ( error == 0 ) && ( fwArgs.type == kFWTypeDebug ) ) { if ( fwArgs.operation == kFWOperationGetFlags ) { fwArgs.debugFlags = gSBP2DiskDebugFlags; error = SYSCTL_OUT ( req, &fwArgs, sizeof ( fwArgs ) ); } else if ( fwArgs.operation == kFWOperationSetFlags ) { gSBP2DiskDebugFlags = fwArgs.debugFlags; } } DLOG ( ( "-FirewireSBPTransportSysctl: gSBP2DiskDebugFlags = 0x%08X\n", ( unsigned int ) gSBP2DiskDebugFlags ) ); return error; } //----------------------------------------------------------------------------- // Default Constructor //----------------------------------------------------------------------------- FWSBP2TransportGlobals::FWSBP2TransportGlobals ( void ) { int debugFlags; DLOG ( ( "+FWSBP2TransportGlobals::FWSBP2TransportGlobals\n" ) ); if ( PE_parse_boot_argn ( "sbp2disk", &debugFlags, sizeof ( debugFlags ) ) ) { gSBP2DiskDebugFlags = debugFlags; } // Register our sysctl interface sysctl_register_oid ( &sysctl__debug_FirewireSBPTransport ); DLOG ( ( "-FWSBP2TransportGlobals::FWSBP2TransportGlobals\n" ) ); } //----------------------------------------------------------------------------- // Destructor //----------------------------------------------------------------------------- FWSBP2TransportGlobals::~FWSBP2TransportGlobals ( void ) { DLOG ( ( "+~FWSBP2TransportGlobals::FWSBP2TransportGlobals\n" ) ); // Unregister our sysctl interface sysctl_unregister_oid ( &sysctl__debug_FirewireSBPTransport ); DLOG ( ( "-~FWSBP2TransportGlobals::FWSBP2TransportGlobals\n" ) ); } #if 0 #pragma mark - #pragma mark Public Methods #pragma mark - #endif //----------------------------------------------------------------------------- // init - Called by IOKit to initialize us. [PUBLIC] //----------------------------------------------------------------------------- bool IOFireWireSerialBusProtocolTransport::init ( OSDictionary * propTable ) { fDeferRegisterService = true; return super::init ( propTable ); } //----------------------------------------------------------------------------- // start - Called by IOKit to start our services. [PUBLIC] //----------------------------------------------------------------------------- bool IOFireWireSerialBusProtocolTransport::start ( IOService * provider ) { IOReturn status = kIOReturnSuccess; bool returnValue = false; bool openSucceeded = false; OSNumber * number = NULL; OSDictionary * dict = NULL; // See if there is a read time out duration passed in the property dictionary - if not // set the default to 30 seconds. number = OSDynamicCast ( OSNumber, getProperty ( kIOPropertyReadTimeOutDurationKey ) ); if ( number == NULL ) { number = OSNumber::withNumber ( kDefaultTimeOutValue, 32 ); require ( number, exit ); setProperty ( kIOPropertyReadTimeOutDurationKey, number ); number->release ( ); number = OSDynamicCast ( OSNumber, getProperty ( kIOPropertyReadTimeOutDurationKey ) ); } DLOG ( ( "%s: start read time out = %ld\n", getName ( ), number->unsigned32BitValue ( ) ) ); // See if there is a write time out duration passed in the property dictionary - if not // set the default to 30 seconds. number = OSDynamicCast ( OSNumber, getProperty ( kIOPropertyWriteTimeOutDurationKey ) ); if ( number == NULL ) { number = OSNumber::withNumber ( kDefaultTimeOutValue, 32 ); require ( number, exit ); setProperty ( kIOPropertyWriteTimeOutDurationKey, number ); number->release ( ); number = OSDynamicCast ( OSNumber, getProperty ( kIOPropertyWriteTimeOutDurationKey ) ); } DLOG ( ( "%s: start read time out = %ld\n", getName ( ), number->unsigned32BitValue ( ) ) ); // Set the default maximum page table constraints for SBP2 // $$$ ( this should probably be moved down to SBP2 - ping Collin ) setProperty ( kIOMaximumSegmentCountReadKey, kFWSBP2DefaultPageTableEntriesCount, 32 ); setProperty ( kIOMaximumSegmentCountWriteKey, kFWSBP2DefaultPageTableEntriesCount, 32 ); setProperty ( kIOMaximumSegmentByteCountReadKey, kFWSBP2MaxPageClusterSize, 32 ); setProperty ( kIOMaximumSegmentByteCountWriteKey, kFWSBP2MaxPageClusterSize, 32 ); fSBPTarget = OSDynamicCast ( IOFireWireSBP2LUN, provider ); require ( fSBPTarget, exit ); // Add a retain here so we can keep IOFireWireSBP2LUN from doing garbage collection on us // when we are in the middle of our finalize method. fSBPTarget->retain ( ); openSucceeded = super::start ( provider ); require ( openSucceeded, exit ); openSucceeded = provider->open ( this ); require ( openSucceeded, exit ); fUnit = fSBPTarget->getFireWireUnit ( ); require ( fUnit, exit ); // Explicitly set the "enable retry on ack d" flag. fUnit->setNodeFlags ( kIOFWEnableRetryOnAckD ); number = OSDynamicCast ( OSNumber, getProperty ( kFireWireGUIDKey, gIOServicePlane ) ); if ( number != NULL ) { UInt64 GUID = 0; GUID = number->unsigned64BitValue ( ); RecordFireWireTimeStamp ( FW_TRACE ( kGUID ), ( uintptr_t ) this, ( unsigned int ) ( ( GUID >> 32 ) & 0xFFFFFFFF ), ( unsigned int ) ( GUID & 0xFFFFFFFF ), 0 ); } status = AllocateResources ( ); require_noerr ( status, exit ); // Get us on the workloop so we can sleep the start thread. fCommandGate->runAction ( ConnectToDeviceStatic ); if ( reserved->fLoginState == kLogginSucceededState ) { registerService ( ); } DLOG ( ( "%s: start complete\n", getName ( ) ) ); returnValue = true; // Copy some values to the Protocol Characteristics Dictionary. dict = OSDynamicCast ( OSDictionary, getProperty ( kIOPropertyProtocolCharacteristicsKey ) ); if ( dict != NULL ) { OSDictionary * protocolDict = NULL; OSString * string = NULL; // Make a copy of the existing Protocol Characteristics Dictionary to Modify. protocolDict = OSDictionary::withDictionary ( dict ); check ( protocolDict ); string = OSString::withCString ( kFireWireGUIDKey ); if ( string != NULL ) { protocolDict->setObject ( string, getProperty ( kFireWireGUIDKey, gIOServicePlane ) ); string->release ( ); string = NULL; } string = OSString::withCString ( kPreferredNameKey ); if ( string != NULL ) { protocolDict->setObject ( kPreferredNameKey, getProperty ( kFireWireVendorNameKey, gIOServicePlane ) ); string->release ( ); string = NULL; } // Replace the existing Protocol Characteristics Dictionary with our new one. setProperty ( kIOPropertyProtocolCharacteristicsKey, protocolDict ); protocolDict->release ( ); protocolDict = NULL; } // False by default. reserved->fAutonomousSpinDownWorkAround = false; if ( ( getProperty ( kIOPropertySCSIDeviceCharacteristicsKey ) ) != NULL ) { OSDictionary * characterDict = NULL; characterDict = OSDynamicCast ( OSDictionary, getProperty ( kIOPropertySCSIDeviceCharacteristicsKey ) ); if ( characterDict->getObject ( kIOPropertyAutonomousSpinDownKey ) != NULL ) { reserved->fAutonomousSpinDownWorkAround = true; } } if ( ( getProperty ( kDontUsePTPacketLimitKey ) ) != NULL ) { IOFireWireSBP2Target * target = NULL; target = OSDynamicCast ( IOFireWireSBP2Target, fSBPTarget->getProvider( ) ); if ( target != NULL ) { target->setTargetFlags ( kIOFWSBP2DontUsePTPacketLimit ); } } InitializePowerManagement ( provider ); exit: if ( returnValue == false ) { DLOG ( ( "%s: start failed. status = %x\n", getName ( ), status) ); // Call the cleanUp method to clean up any allocated resources. cleanUp ( ); } return returnValue; } //----------------------------------------------------------------------------- // cleanUp - This is misleadingly named due to binary compatibility burdens. // The cleanUp method actually just closes the SBP2LUN. fSBPTarget // is actually a SBP2LUN object. [PUBLIC] //----------------------------------------------------------------------------- void IOFireWireSerialBusProtocolTransport::cleanUp ( void ) { DLOG ( ( "%s: cleanUp called\n", getName ( ) ) ); if ( fSBPTarget != NULL ) { // Close SBP2 if we have opened it. if ( fSBPTarget->isOpen ( this ) ) { OSNumber * number = NULL; number = OSDynamicCast ( OSNumber, getProperty ( kFireWireGUIDKey, gIOServicePlane ) ); if ( number != NULL ) { UInt64 GUID = 0; GUID = number->unsigned64BitValue ( ); RecordFireWireTimeStamp ( FW_TRACE ( kGUID ), ( uintptr_t ) this, ( unsigned int ) ( ( GUID >> 32 ) & 0xFFFFFFFF ), ( unsigned int ) ( GUID & 0xFFFFFFFF ), 1 ); } fSBPTarget->close ( this ); } } } //----------------------------------------------------------------------------- // finalize - Terminates all power management. [PROTECTED] //----------------------------------------------------------------------------- bool IOFireWireSerialBusProtocolTransport::finalize ( IOOptionBits options ) { DeallocateResources ( ); // Release the retain we took to keep IOFireWireSBP2LUN from doing garbage collection on us // when we are in the middle of DeallocateResources. if ( fSBPTarget != NULL ) { fSBPTarget->release ( ); fSBPTarget = NULL; } return super::finalize ( options ); } //----------------------------------------------------------------------------- // free - Called to deallocate ExpansionData. [PUBLIC] //----------------------------------------------------------------------------- void IOFireWireSerialBusProtocolTransport::free ( void ) { if ( reserved != NULL ) { if ( reserved->fCommandPool != NULL ) { reserved->fCommandPool->release ( ); reserved->fCommandPool = NULL; } IOFree ( reserved, sizeof ( ExpansionData ) ); reserved = NULL; } super::free ( ); } #if 0 #pragma mark - #pragma mark Protected Methods #pragma mark - #endif //----------------------------------------------------------------------------- // CommandORBAccessor - Retrieves command orb. [PROTECTED] //----------------------------------------------------------------------------- IOFireWireSBP2ORB * IOFireWireSerialBusProtocolTransport::CommandORBAccessor ( void ) { return fORB; } //----------------------------------------------------------------------------- // SBP2LoginAccessor - Retrieves login orb. [PROTECTED] //----------------------------------------------------------------------------- IOFireWireSBP2Login * IOFireWireSerialBusProtocolTransport::SBP2LoginAccessor ( void ) { return fLogin; } //----------------------------------------------------------------------------- // message - Called by IOKit to deliver messages. [PROTECTED] //----------------------------------------------------------------------------- IOReturn IOFireWireSerialBusProtocolTransport::message ( UInt32 type, IOService * nub, void * arg ) { IOFireWireSBP2ORB * orb = NULL; SBP2ClientOrbData * clientData = NULL; IOReturn status = kIOReturnSuccess; switch ( type ) { case kIOMessageServiceIsSuspended: { DLOG ( ( "%s: kIOMessageServiceIsSuspended\n", getName ( ) ) ); // Bus reset started - set flag to stop submitting orbs. fLoggedIn = false; } break; case kIOMessageServiceIsResumed: { DLOG ( ( "%s: kIOMessageServiceIsResumed\n", getName ( ) ) ); // Bus reset finished - if we have failed to log in previously, try again. if ( fNeedLogin == true ) { fNeedLogin = false; fLoginRetryCount = 0; // In case we are resumed after a terminate. if ( fLogin != NULL ) { login ( ); } } } break; case kIOMessageFWSBP2ReconnectComplete: { // As of this writing FireWireSBP2LUN will message all multi-LUN instances with this // message. So qualify this message with our instance variable fLogin and ignore others. if ( ( ( FWSBP2ReconnectParams * ) arg )->login == fLogin ) { DLOG ( ( "%s: kIOMessageFWSBP2ReconnectComplete\n", getName ( ) ) ); fLoggedIn = true; if ( fReconnectCount < kMaxReconnectCount) { DLOG ( ( "%s: resubmit orb \n", getName ( ) ) ); fReconnectCount++; submitOrbFromQueue ( ); } else { // Unable to recover from bus reset storm. We have exhausted the // fReconnectCount - punt... if ( fORB != NULL ) { clientData = ( SBP2ClientOrbData * ) fORB->getRefCon ( ); if ( clientData != NULL ) { clientData->serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; clientData->taskStatus = kSCSITaskStatus_DeliveryFailure; CompleteSCSITask ( fORB ); } } } } } break; case kIOMessageFWSBP2ReconnectFailed: { // As of this writing FireWireSBP2LUN will message all multi-LUN instances with this // message. So qualify this message with our instance variable fLogin and ignore others. if ( ( ( FWSBP2ReconnectParams * ) arg )->login == fLogin ) { DLOG ( ( "%s: kIOMessageFWSBP2ReconnectFailed\n", getName ( ) ) ); // Try to reestablish log in. fLoginRetryCount = 0; login ( ); } } break; case kIOFireWireMessageServiceIsRequestingClose: { DLOG ( ( "%s: kIOFireWireMessageServiceIsRequestingClose\n", getName ( ) ) ); // Tell our super to message it's clients that the device is gone. SendNotification_DeviceRemoved ( ); // We need to drain the queued commands. See if there is an in flight orb (e.g. fORB) // if not pull the first one out of the submit queue if there are any. orb = fORB; do { if ( orb != NULL ) { clientData = ( SBP2ClientOrbData * ) orb->getRefCon ( ); if ( clientData != NULL ) { if ( clientData->scsiTask != NULL ) { clientData->serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; clientData->taskStatus = kSCSITaskStatus_DeviceNotPresent; CompleteSCSITask ( orb ); } } } } while ( ( orb = ( IOFireWireSBP2ORB * ) reserved->fSubmitQueue->getCommand ( false ) ) ); cleanUp ( ); } break; case kIOMessageServiceIsTerminated: { DLOG ( ( "%s: kIOMessageServiceIsTerminated\n", getName ( ) ) ); // Let go of memory and what not. cleanUp ( ); } break; default: { status = IOService::message ( type, nub, arg ); } break; } return status; } //----------------------------------------------------------------------------- // SendSCSICommand - Converts a SCSITask to an ORB. [PROTECTED] //----------------------------------------------------------------------------- bool IOFireWireSerialBusProtocolTransport::SendSCSICommand ( SCSITaskIdentifier request, SCSIServiceResponse * serviceResponse, SCSITaskStatus * taskStatus ) { SBP2ClientOrbData * clientData = NULL; IOFireWireSBP2ORB * orb = NULL; SCSICommandDescriptorBlock cdb = { 0 }; UInt8 commandLength = 0; UInt32 commandFlags = 0; UInt32 timeOut = 0; bool commandProcessed = false; DLOG ( ( "%s: SendSCSICommand called\n", getName ( ) ) ); *serviceResponse = kSCSIServiceResponse_Request_In_Process; *taskStatus = kSCSITaskStatus_No_Status; if ( isInactive ( ) == true ) { // Device is disconnected - we can not service command requests. *serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; commandProcessed = true; goto exit; } // Get an orb from our orb pool and do not block until we get one. orb = ( IOFireWireSBP2ORB * ) reserved->fCommandPool->getCommand ( false ); require ( orb, exit ); GetCommandDescriptorBlock ( request, &cdb ); RecordFireWireTimeStamp ( FW_TRACE ( kSendSCSICommand1 ), ( uintptr_t ) this, ( uintptr_t ) request, cdb[0] | ( cdb[1] << 8 ) | ( cdb[2] << 16 ) | ( cdb[3] << 24 ), cdb[4] | ( cdb[5] << 8 ) | ( cdb[6] << 16 ) | ( cdb[7] << 24 ) ); RecordFireWireTimeStamp ( FW_TRACE ( kSendSCSICommand2 ), ( uintptr_t ) this, ( uintptr_t ) request, cdb[ 8] | ( cdb[ 9] << 8 ) | ( cdb[10] << 16 ) | ( cdb[11] << 24 ), cdb[12] | ( cdb[13] << 8 ) | ( cdb[14] << 16 ) | ( cdb[15] << 24 ) ); clientData = ( SBP2ClientOrbData * ) orb->getRefCon ( ); require ( clientData, exit ); commandLength = GetCommandDescriptorBlockSize ( request ); commandFlags = ( GetDataTransferDirection ( request ) == kSCSIDataTransfer_FromTargetToInitiator ) ? kFireWireSBP2CommandTransferDataFromTarget : kFireWireSBP2CommandTransferDataToTarget; orb->setCommandFlags ( commandFlags | kFWSBP2CommandCompleteNotify | kFWSBP2CommandImmediate | kFWSBP2CommandNormalORB ); require ( ( SetCommandBuffers ( orb, request ) == kIOReturnSuccess ), exit ); orb->setCommandBlock ( cdb, commandLength ); // SBP-2 needs a non-zero timeout to fire completion routines if timeout is not expressed // default to 0xFFFFFFFF. timeOut = GetTimeoutDuration ( request ); if ( timeOut == 0 ) { timeOut = 0xFFFFFFFF; } orb->setCommandTimeout ( timeOut ); #if TRANSPORT_FAILURE_RETRIES reserved->fLUNResetCount = 3; #endif // Close the gate here to eliminate potenially rare double append of orb. If on a DP machine // and a bus reset occurs the login thread can append the orb as well as here. fCommandGate->runAction ( CriticalOrbSubmissionStatic, orb, request ); commandProcessed = true; exit: DLOG ( ( "%s: SendSCSICommand exit, Service Response = %x\n", getName ( ), *serviceResponse ) ); return commandProcessed; } //----------------------------------------------------------------------------- // SetCommandBuffers - Sets the command buffers in the ORB. [PROTECTED] //----------------------------------------------------------------------------- IOReturn IOFireWireSerialBusProtocolTransport::SetCommandBuffers ( IOFireWireSBP2ORB * orb, SCSITaskIdentifier request ) { SBP2ClientOrbData * clientData = NULL; IOReturn status = kIOReturnError; clientData = ( SBP2ClientOrbData * ) orb->getRefCon ( ); require ( clientData, Exit ); clientData->quadletAlignedBuffer = NULL; if ( GetDataBuffer ( request ) != NULL ) { // Does this command require double buffering in order to ensure quadlet alignment? if ( ( GetDataBuffer ( request )->getLength ( ) < kDoubleBufferCommandSizeCheckThreshold ) && ( ( GetDataBuffer ( request )->getLength ( ) & 3 ) != 0 ) ) { // Create quadlet aligned IOBufferMemoryDescriptor, to be released in CompleteSCSITask(). clientData->quadletAlignedBuffer = IOBufferMemoryDescriptor::withOptions ( kIODirectionOutIn, GetDataBuffer ( request )->getLength ( ), 4 ); require ( clientData->quadletAlignedBuffer, Exit ); // If necessary copy data from the non-aligned buffer to the aligned buffer. if ( GetDataTransferDirection ( request ) == kSCSIDataTransfer_FromInitiatorToTarget ) { GetDataBuffer ( request )->readBytes ( GetDataBufferOffset ( request ), clientData->quadletAlignedBuffer->getBytesNoCopy ( ), GetDataBuffer ( request )->getLength ( ) ); } status = orb->setCommandBuffers ( clientData->quadletAlignedBuffer, GetDataBufferOffset ( request ), GetRequestedDataTransferCount ( request ) ); require_success ( status, Exit ); } } if ( clientData->quadletAlignedBuffer == NULL ) { status = orb->setCommandBuffers ( GetDataBuffer ( request ), GetDataBufferOffset ( request ), GetRequestedDataTransferCount ( request ) ); } Exit: return status; } //----------------------------------------------------------------------------- // CompleteSCSITask - Completes a task. [PROTECTED] //----------------------------------------------------------------------------- void IOFireWireSerialBusProtocolTransport::CompleteSCSITask ( IOFireWireSBP2ORB * orb ) { SBP2ClientOrbData * clientData = NULL; DLOG ( ( "%s: CompleteSCSITask called\n", getName ( ) ) ); clientData = ( SBP2ClientOrbData * ) orb->getRefCon ( ); if ( clientData != NULL ) { if ( clientData->scsiTask != NULL ) { SCSITaskIdentifier scsiTask = NULL; SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; SCSITaskStatus taskStatus = kSCSITaskStatus_No_Status; IOByteCount bytesTransfered = 0; // /!\ WARNING - because SBP-2 can send status information at different // stage of an orb's life ( or send none at all ) the caller of this routine // has determined that the orb is indeed done. So we need to explicitly tell // SBP-2 to let go of the buffer reference by calling releaseCommandBuffers. orb->releaseCommandBuffers ( ); if ( clientData->taskStatus == kSCSITaskStatus_GOOD ) { bytesTransfered = GetRequestedDataTransferCount ( clientData->scsiTask ); } SetRealizedDataTransferCount ( clientData->scsiTask, bytesTransfered ); // Did we double buffer this command to ensure quadlet alignment? // If so, copy over memory if necessary. if ( clientData->quadletAlignedBuffer != NULL ) { if ( GetDataTransferDirection ( clientData->scsiTask ) == kSCSIDataTransfer_FromTargetToInitiator ) { GetDataBuffer ( clientData->scsiTask )->writeBytes ( GetDataBufferOffset ( clientData->scsiTask ), clientData->quadletAlignedBuffer->getBytesNoCopy ( ), clientData->quadletAlignedBuffer->getLength ( ) ); } clientData->quadletAlignedBuffer->release ( ); clientData->quadletAlignedBuffer = NULL; } // Re-entrancy protection. scsiTask = clientData->scsiTask; serviceResponse = clientData->serviceResponse; taskStatus = clientData->taskStatus; clientData->scsiTask = NULL; fORB = NULL; submitOrbFromQueue ( ); reserved->fCommandPool->returnCommand ( orb ); RecordFireWireTimeStamp ( FW_TRACE ( kCompleteSCSICommand ), ( uintptr_t ) this, ( uintptr_t ) scsiTask, ( serviceResponse << 8 ) | taskStatus ); CommandCompleted ( scsiTask, serviceResponse, taskStatus ); } } } //----------------------------------------------------------------------------- // AbortSCSICommand - Aborts an outstanding I/O. [PROTECTED] //----------------------------------------------------------------------------- SCSIServiceResponse IOFireWireSerialBusProtocolTransport::AbortSCSICommand ( SCSITaskIdentifier request ) { SCSIServiceResponse serviceResponse; DEBUG_UNUSED ( request ); DLOG ( ( "%s: AbortSCSICommand called\n", getName ( ) ) ); serviceResponse = kSCSIServiceResponse_FUNCTION_REJECTED; return serviceResponse; } //----------------------------------------------------------------------------- // IsProtocolServiceSupported - Checks for protocol services supported by // this device. [PROTECTED] //----------------------------------------------------------------------------- bool IOFireWireSerialBusProtocolTransport::IsProtocolServiceSupported ( SCSIProtocolFeature feature, void * serviceValue ) { bool isSupported = false; OSDictionary * characterDict = NULL; characterDict = OSDynamicCast ( OSDictionary, ( getProperty ( kIOPropertySCSIDeviceCharacteristicsKey ) ) ); DLOG ( ( "IOFireWireSerialBusProtocolTransport::IsProtocolServiceSupported called\n" ) ); switch ( feature ) { case kSCSIProtocolFeature_CPUInDiskMode: { isSupported = IsDeviceCPUInDiskMode ( ); } break; case kSCSIProtocolFeature_MaximumReadBlockTransferCount: { OSNumber * number = NULL; // Start with our default value. * ( ( UInt32 * ) serviceValue ) = kDefaultIOBlockCount; if ( characterDict != NULL ) { number = OSDynamicCast ( OSNumber, characterDict->getObject ( kIOMaximumByteCountReadKey ) ); if ( number != NULL ) { *( ( UInt32 * ) serviceValue ) = number->unsigned32BitValue ( ); } } isSupported = true; } break; case kSCSIProtocolFeature_MaximumWriteBlockTransferCount: { OSNumber * number = NULL; // Start with our default value. * ( ( UInt32 * ) serviceValue ) = kDefaultIOBlockCount; if ( characterDict != NULL ) { number = OSDynamicCast ( OSNumber, characterDict->getObject ( kIOMaximumBlockCountWriteKey ) ); if ( number != NULL ) { *( ( UInt32 * ) serviceValue ) = number->unsigned32BitValue ( ); } } isSupported = true; } break; case kSCSIProtocolFeature_MaximumReadTransferByteCount: { OSNumber * number = NULL; // If the property SBP2ReceiveBufferByteCount exists we have a FireWire host // with the physical unit off and there is a software FIFO. ( i.e. Lynx ) // We should tell clients to deblock on the SBP2ReceiveBufferByteCount. // bounds to avoid stalled I/O. number = OSDynamicCast ( OSNumber, getProperty ( kSBP2ReceiveBufferByteCountKey, gIOServicePlane ) ); if ( number != NULL ) { * ( ( UInt64 * ) serviceValue ) = number->unsigned32BitValue ( ); isSupported = true; } } break; case kSCSIProtocolFeature_GetMaximumLogicalUnitNumber: { * ( ( UInt32 * ) serviceValue ) = kMaxFireWireLUN; isSupported = true; } break; case kSCSIProtocolFeature_ProtocolAlwaysReportsAutosenseData: { isSupported = true; } break; case kSCSIProtocolFeature_ProtocolSpecificPowerControl: { if ( reserved->fAutonomousSpinDownWorkAround == true ) { isSupported = true; } } break; default: { isSupported = false; } break; } return isSupported; } //----------------------------------------------------------------------------- // HandleProtocolServiceFeature - Handles protocol service features. // [PROTECTED] //----------------------------------------------------------------------------- bool IOFireWireSerialBusProtocolTransport::HandleProtocolServiceFeature ( SCSIProtocolFeature feature, void * serviceValue ) { bool returnValue = false; switch ( feature ) { case kSCSIProtocolFeature_ProtocolSpecificPowerControl: { // This a workaround for devices which spin themselves up/down and can't // properly handle START_STOP commands from the host when the drive is // already in the requested state. if ( reserved->fAutonomousSpinDownWorkAround == true ) { // This essentially NOPs the spin up/down request. returnValue = true; } } break; default: { returnValue = false; } break; } return returnValue; } //----------------------------------------------------------------------------- // IsDeviceCPUInDiskMode - Checks if device is a CPU in FireWire Target // Disk Mode. [PROTECTED] //----------------------------------------------------------------------------- bool IOFireWireSerialBusProtocolTransport::IsDeviceCPUInDiskMode ( void ) { UInt32 csrModelInfo = 0;; IOConfigDirectory * directory = NULL; IOFireWireDevice * device = NULL; IOReturn status = kIOReturnSuccess; bool isCPUDiskMode = false; DLOG ( ( "%s: IsDeviceCPUInDiskMode was called\n", getName ( ) ) ); device = OSDynamicCast ( IOFireWireDevice, fUnit->getProvider ( ) ); require ( device, Exit ); status = device->getConfigDirectory ( directory ); require_success ( status, Exit ); status = directory->getKeyValue ( kCSRModelInfoKey, csrModelInfo ); require_success ( status, Exit ); if ( ( csrModelInfo & kCRSModelInfo_ValidBitsMask ) == kCRSModelInfo_TargetDiskMode ) { isCPUDiskMode = true; } Exit: DLOG ( ( "%s: CPU Disk Mode = %d\n", getName ( ), isCPUDiskMode ) ); return isCPUDiskMode; } //----------------------------------------------------------------------------- // StatusNotifyStatic - C->C++ glue method. [PROTECTED] //----------------------------------------------------------------------------- void IOFireWireSerialBusProtocolTransport::StatusNotifyStatic ( void * refCon, FWSBP2NotifyParams * params ) { ( ( IOFireWireSerialBusProtocolTransport * ) refCon )->StatusNotify ( params ); } //----------------------------------------------------------------------------- // StatusNotify - Status notify handler. [PROTECTED] //----------------------------------------------------------------------------- void IOFireWireSerialBusProtocolTransport::StatusNotify ( FWSBP2NotifyParams * params ) { IOFireWireSBP2ORB * orb = NULL; FWSBP2StatusBlock * statusBlock = NULL; SBP2ClientOrbData * clientData = NULL; SCSI_Sense_Data * targetData = NULL; UInt8 senseData[kSenseDefaultSize + 8] = { 0 }; targetData = ( SCSI_Sense_Data * ) &senseData [ 0 ]; if ( ( params->message != NULL ) && ( params->length != 0 ) ) { statusBlock = ( FWSBP2StatusBlock * ) params->message; } orb = ( IOFireWireSBP2ORB * ) params->commandObject; if ( orb != NULL ) { clientData = ( SBP2ClientOrbData * ) orb->getRefCon ( ); } RecordFireWireTimeStamp ( FW_TRACE ( kStatusNotify ), ( uintptr_t ) this, ( uintptr_t ) orb, params->notificationEvent ); switch ( params->notificationEvent ) { case kFWSBP2NormalCommandStatus: { if ( clientData != NULL ) { // Read the status block details bits. See SBP-2 spec section 5.3. // Check the dead bit ( i.e. that the 'd' field == 1 ). if ( statusBlock->details & kSBP2StatusBlock_Details_DeadMask ) { SetValidAutoSenseData ( clientData, statusBlock, targetData ); // Wait for fetch agent to reset before calling CompleteSCSITask which will // be called in FetchAgentResetComplete. RecordFireWireTimeStamp ( FW_TRACE ( kFetchAgentReset ), ( uintptr_t ) this, ( uintptr_t ) orb ); fLogin->submitFetchAgentReset ( ); } else if ( ( ( statusBlock->details & kSBP2StatusBlock_Details_RespMask ) == kSBP2StatusBlock_Resp_REQUEST_COMPLETE ) && ( statusBlock->sbpStatus == kSBP2StatusBlock_SBPStatus_FunctionRejected ) ) { SetValidAutoSenseData ( clientData, statusBlock, targetData ); // Complete the SCSI request without a retry for devices that return a // SBP function rejected status but in actuality have successfully // processed the SCSI request with a CHECK condition // and have valid sense data. // // Certain Oxford 911 based devices seem to fall under this category if ( clientData->taskStatus == kSCSITaskStatus_CHECK_CONDITION ) { CompleteSCSITask ( orb ); } else { clientData->serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; clientData->taskStatus = kSCSITaskStatus_DeviceNotResponding; reserved->fLUNResetPathFlag = true; fLUNResetORB->submit ( ); } } else if ( ( ( statusBlock->details & kSBP2StatusBlock_Details_RespMask ) == kSBP2StatusBlock_Resp_REQUEST_COMPLETE ) && ( ( statusBlock->details & kSBP2StatusBlock_Details_LenMask ) == 1 ) && ( statusBlock->sbpStatus == kSBP2StatusBlock_SBPStatus_NoAdditionalInformation ) ) { clientData->serviceResponse = kSCSIServiceResponse_TASK_COMPLETE; clientData->taskStatus = kSCSITaskStatus_GOOD; CompleteSCSITask ( orb ); DLOG ( ( "%s: StatusNotify normal complete \n", getName ( ) ) ); } else { SetValidAutoSenseData ( clientData, statusBlock, targetData ); CompleteSCSITask ( orb ); DLOG ( ( "%s: StatusNotify have sense data or an unexpected error? \n", getName ( ) ) ); } } } break; case kFWSBP2NormalCommandTimeout: { DLOG ( ( "%s: kFWSBP2NormalCommandTimeout \n", getName ( ) ) ); if ( clientData != NULL ) { if ( clientData->scsiTask != NULL ) { clientData->serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; clientData->taskStatus = kSCSITaskStatus_ProtocolTimeoutOccurred; } } // Set flag so FetchAgentReset knows it is being called from a timeout path. // reserved->fLUNResetPathFlag = true; RecordFireWireTimeStamp ( FW_TRACE ( kLogicalUnitReset ), ( uintptr_t ) this, ( uintptr_t ) fLUNResetORB ); // We reset the LUN as good measure in case device is wedged. The LUN reset completion // handler will call the fetch agent to be reset. The FetchAgentReset completion handler // will call CompleteSCSITask() and complete the command with the approrpiate task status. fLUNResetORB->submit ( ); } break; case kFWSBP2NormalCommandReset: { DLOG ( ( "%s: kFWSBP2NormalCommandReset\n", getName ( ) ) ); // kFWSBP2NormalCommandReset - is a misleading definition. A pending command has // failed so we need notify the upper layers to complete failed command. if ( clientData != NULL ) { if ( clientData->scsiTask != NULL ) { clientData->serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; clientData->taskStatus = kSCSITaskStatus_DeliveryFailure; CompleteSCSITask ( orb ); } } } break; default: { DLOG ( ( "%s: StatusNotify with unknown notificationEvent\n", getName ( ) ) ); } break; } } //----------------------------------------------------------------------------- // SetValidAutoSenseData - Sets any valid sense data. [PROTECTED] //----------------------------------------------------------------------------- void IOFireWireSerialBusProtocolTransport::SetValidAutoSenseData ( SBP2ClientOrbData * clientData, FWSBP2StatusBlock * statusBlock, SCSI_Sense_Data * targetData ) { UInt8 quadletCount = 0; clientData->serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; clientData->taskStatus = kSCSITaskStatus_No_Status; quadletCount = ( statusBlock->details & kSBP2StatusBlock_Details_LenMask ) - 1; // See if we have any valid sense data. if ( ( statusBlock->details & kSBP2StatusBlock_Details_RespMask ) == kSBP2StatusBlock_Resp_REQUEST_COMPLETE ) { clientData->serviceResponse = kSCSIServiceResponse_TASK_COMPLETE; clientData->taskStatus = CoalesceSenseData ( statusBlock, quadletCount, targetData ); } if ( ( clientData->taskStatus == kSCSITaskStatus_CHECK_CONDITION ) || ( reserved->fAlwaysSetSenseData ) ) { if ( clientData->scsiTask != NULL ) { SetAutoSenseData ( clientData->scsiTask, targetData, kSenseDefaultSize + 8 ); } } RecordFireWireTimeStamp ( FW_TRACE ( kSCSICommandSenseData ), ( uintptr_t ) this, ( uintptr_t ) clientData->scsiTask, targetData->SENSE_KEY & kSENSE_KEY_Mask, ( targetData->ADDITIONAL_SENSE_CODE << 8 ) | targetData->ADDITIONAL_SENSE_CODE_QUALIFIER ); } //----------------------------------------------------------------------------- // CoalesceSenseData - Sets sense data in the data buffer. [PROTECTED] //----------------------------------------------------------------------------- SCSITaskStatus IOFireWireSerialBusProtocolTransport::CoalesceSenseData ( FWSBP2StatusBlock * sourceData, UInt8 quadletCount, SCSI_Sense_Data * targetData ) { SCSITaskStatus returnValue = kSCSITaskStatus_GOOD; UInt8 statusBlockFormat = 0; FWSBP2SCSIStatusBlock * scsiStatusBlock = NULL; scsiStatusBlock = ( FWSBP2SCSIStatusBlock * ) sourceData; // Pull bits out of SBP-2 status block ( see SBP-2 Annex B section B.2 ) // and copy them into sense data block ( see SPC-2 section 7.23.2 ) if ( quadletCount > 0 ) { statusBlockFormat = scsiStatusBlock->status & kFWSBP2SCSIStatusBlock_StatusBlockFormatMask; returnValue = ( SCSITaskStatus ) ( scsiStatusBlock->status & kFWSBP2SCSIStatusBlock_StatusBlockStatusMask ); if ( statusBlockFormat == kFWSBP2SCSIStatusBlock_StatusBlockFormatCurrentError ) { targetData->VALID_RESPONSE_CODE = kSENSE_RESPONSE_CODE_Current_Errors; } else if ( statusBlockFormat == kFWSBP2SCSIStatusBlock_StatusBlockFormatDeferredError ) { targetData->VALID_RESPONSE_CODE = kSENSE_RESPONSE_CODE_Deferred_Errors; } if ( statusBlockFormat < 2 ) { targetData->VALID_RESPONSE_CODE |= ( scsiStatusBlock->senseKey & kFWSBP2SCSIStatusBlock_VALID_Mask ); targetData->ADDITIONAL_SENSE_CODE = scsiStatusBlock->ASC; targetData->ADDITIONAL_SENSE_CODE_QUALIFIER = scsiStatusBlock->ASCQ; targetData->SENSE_KEY = scsiStatusBlock->senseKey & kFWSBP2SCSIStatusBlock_SenseKeyMask; // Set the M, E, I bits: M->FileMark, E->EOM, I->ILI. targetData->SENSE_KEY |= ( scsiStatusBlock->senseKey & kFWSBP2SCSIStatusBlock_MEI_Mask ) << 1; if ( quadletCount > 1 ) { scsiStatusBlock->information = OSSwapBigToHostInt32 ( scsiStatusBlock->information ); targetData->INFORMATION_1 = ( scsiStatusBlock->information >> 24 ) & 0xFF; targetData->INFORMATION_2 = ( scsiStatusBlock->information >> 16 ) & 0xFF; targetData->INFORMATION_3 = ( scsiStatusBlock->information >> 8 ) & 0xFF; targetData->INFORMATION_4 = scsiStatusBlock->information & 0xFF; targetData->ADDITIONAL_SENSE_LENGTH = 6; } if ( quadletCount > 2 ) { scsiStatusBlock->commandSpecificInformation = OSSwapBigToHostInt32 ( scsiStatusBlock->commandSpecificInformation ); targetData->COMMAND_SPECIFIC_INFORMATION_1 = ( scsiStatusBlock->commandSpecificInformation >> 24 ) & 0xFF; targetData->COMMAND_SPECIFIC_INFORMATION_2 = ( scsiStatusBlock->commandSpecificInformation >> 16 ) & 0xFF; targetData->COMMAND_SPECIFIC_INFORMATION_3 = ( scsiStatusBlock->commandSpecificInformation >> 8 ) & 0xFF; targetData->COMMAND_SPECIFIC_INFORMATION_4 = scsiStatusBlock->commandSpecificInformation & 0xFF; targetData->ADDITIONAL_SENSE_LENGTH = 6; } if ( quadletCount > 3 ) { UInt8 count = 0; // Get bytes to copy and clip if greater than sizeof SCSI_Sense_Data. scsiStatusBlock->senseKeyDependent = OSSwapBigToHostInt32 ( scsiStatusBlock->senseKeyDependent ); count = ( quadletCount - 3 ) * sizeof ( UInt32 ); if ( count > 4 ) count = 4; bcopy ( &scsiStatusBlock->senseKeyDependent, &targetData->FIELD_REPLACEABLE_UNIT_CODE, count ); targetData->ADDITIONAL_SENSE_LENGTH = count + 6; } } } return returnValue; } //----------------------------------------------------------------------------- // LoginCompletionStatic - C->C++ glue method. [PROTECTED] //----------------------------------------------------------------------------- void IOFireWireSerialBusProtocolTransport::LoginCompletionStatic ( void * refCon, FWSBP2LoginCompleteParams * params ) { ( ( IOFireWireSerialBusProtocolTransport * ) refCon )->LoginCompletion ( params ); } //----------------------------------------------------------------------------- // LoginCompletion - Login completion handler. [PROTECTED] //----------------------------------------------------------------------------- void IOFireWireSerialBusProtocolTransport::LoginCompletion ( FWSBP2LoginCompleteParams * params ) { DLOG ( ( "%s: LoginCompletion complete \n", getName ( ) ) ); // If login failed, retry if we haven't already exceeded max retry count. require_success ( params->status, Retry ); // We only have a valid SBP2 login params block if the login completed successfully. // Otherwise the block is NULL. require ( params->statusBlock, Retry ); RecordFireWireTimeStamp ( FW_TRACE ( kLoginCompletion ), ( uintptr_t ) this, params->status, params->statusBlock->details, params->statusBlock->sbpStatus ); if ( ( ( params->statusBlock->details & kSBP2StatusBlock_Details_RespMask ) == kSBP2StatusBlock_Resp_REQUEST_COMPLETE ) && ( params->statusBlock->sbpStatus == kSBP2StatusBlock_SBPStatus_NoAdditionalInformation ) ) { fLoginRetryCount = 0; fLoggedIn = true; fNeedLogin = false; if ( reserved->fLoginState == kFirstTimeLoggingInState ) { reserved->fLoginState = kLogginSucceededState; fCommandGate->commandWakeup ( ( void * ) &reserved->fLoginState ); } submitOrbFromQueue ( ); RecordFireWireTimeStamp ( FW_TRACE ( kLoginResumed ), ( uintptr_t ) this ); loginResumed ( ); return; } Retry: RecordFireWireTimeStamp ( FW_TRACE ( kLoginCompletion ), ( uintptr_t ) this, params->status, NULL, NULL ); if ( fLoginRetryCount < kMaxLoginRetryCount ) { IOReturn status = kIOReturnSuccess; fLoginRetryCount++; DLOG ( ( "%s: resubmitting Login\n", getName ( ) ) ); status = login ( ); if ( status != kIOReturnSuccess ) { if ( reserved->fLoginState == kFirstTimeLoggingInState ) { reserved->fLoginState = kLogginFailedState; // Wake up sleeping start thread. fCommandGate->commandWakeup ( ( void * ) &reserved->fLoginState ); } } } else { // Device can not be logged into after kMaxLoginRetryCount attemptes let's reset // the need login flag in case the device was unplugged during login. fNeedLogin = true; RecordFireWireTimeStamp ( FW_TRACE ( kLoginLost ), ( uintptr_t ) this, reserved->fLoginState ); loginLost ( ); if ( reserved->fLoginState == kFirstTimeLoggingInState ) { reserved->fLoginState = kLogginFailedState; // Wake up sleeping start thread. fCommandGate->commandWakeup ( ( void * ) &reserved->fLoginState ); } } } //----------------------------------------------------------------------------- // LogoutCompletionStatic - C->C++ glue. [PROTECTED] //----------------------------------------------------------------------------- void IOFireWireSerialBusProtocolTransport::LogoutCompletionStatic ( void * refCon, FWSBP2LogoutCompleteParams * params ) { ( ( IOFireWireSerialBusProtocolTransport * ) refCon )->LogoutCompletion ( params ); } //----------------------------------------------------------------------------- // LogoutCompletion - Logout completion handler. [PROTECTED] //----------------------------------------------------------------------------- void IOFireWireSerialBusProtocolTransport::LogoutCompletion ( FWSBP2LogoutCompleteParams * params ) { DEBUG_UNUSED ( params ); DLOG ( ( "%s: LogoutCompletion complete \n", getName ( ) ) ); } //----------------------------------------------------------------------------- // UnsolicitedStatusNotifyStatic - C->C++ glue. [PROTECTED] //----------------------------------------------------------------------------- void IOFireWireSerialBusProtocolTransport::UnsolicitedStatusNotifyStatic ( void * refCon, FWSBP2NotifyParams * params ) { ( ( IOFireWireSerialBusProtocolTransport * ) refCon )->UnsolicitedStatusNotify ( params ); } //----------------------------------------------------------------------------- // UnsolicitedStatusNotify - Unsolicited status handler. [PROTECTED] //----------------------------------------------------------------------------- void IOFireWireSerialBusProtocolTransport::UnsolicitedStatusNotify ( FWSBP2NotifyParams * params ) { DEBUG_UNUSED ( params ); DLOG ( ( "%s: UnsolicitedStatusNotify called\n", getName ( ) ) ); // Parse and handle unsolicited status. fLogin->enableUnsolicitedStatus ( ); } //----------------------------------------------------------------------------- // FetchAgentResetCompleteStatic - C->C++ glue. [PROTECTED] //----------------------------------------------------------------------------- void IOFireWireSerialBusProtocolTransport::FetchAgentResetCompleteStatic ( void * refCon, IOReturn status ) { ( ( IOFireWireSerialBusProtocolTransport * ) refCon )->FetchAgentResetComplete ( status ); } //----------------------------------------------------------------------------- // FetchAgentResetComplete - Fetch agent reset handler. [PROTECTED] //----------------------------------------------------------------------------- void IOFireWireSerialBusProtocolTransport::FetchAgentResetComplete ( IOReturn status ) { SBP2ClientOrbData * clientData = NULL; DEBUG_UNUSED ( status ); DLOG ( ( "%s: FetchAgentResetComplete called\n", getName ( ) ) ); require ( fORB, exit ); // When orb chaining is implemented we will notify upper layer // to reconfigure device state and resubmitting commands clientData = ( SBP2ClientOrbData * ) fORB->getRefCon ( ); if ( clientData != NULL ) { if ( clientData->scsiTask != NULL ) { RecordFireWireTimeStamp ( FW_TRACE ( kFetchAgentResetComplete ), ( uintptr_t ) this, ( uintptr_t ) fORB ); #if TRANSPORT_FAILURE_RETRIES if ( reserved->fLUNResetPathFlag && ( reserved->fLUNResetCount > 0 ) ) { reserved->fLUNResetCount--; submitOrbFromQueue ( ); } else #endif { CompleteSCSITask ( fORB ); } } } exit: reserved->fLUNResetPathFlag = false; } //----------------------------------------------------------------------------- // LunResetCompleteStatic - C->C++ glue. [PROTECTED] //----------------------------------------------------------------------------- void IOFireWireSerialBusProtocolTransport::LunResetCompleteStatic ( void * refCon, IOReturn status, IOFireWireSBP2ManagementORB * orb ) { ( ( IOFireWireSerialBusProtocolTransport * ) refCon )->LunResetComplete ( status, orb ); } //----------------------------------------------------------------------------- // LunResetComplete - LUN reset completion handler. [PROTECTED] //----------------------------------------------------------------------------- void IOFireWireSerialBusProtocolTransport::LunResetComplete ( IOReturn status, IOFireWireSBP2ManagementORB * orb ) { DEBUG_UNUSED ( status ); DEBUG_UNUSED ( orb ); DLOG ( ( "%s: LunResetComplete called\n", getName ( ) ) ); RecordFireWireTimeStamp ( FW_TRACE ( kLogicalUnitResetComplete ), ( uintptr_t ) this, ( uintptr_t ) orb, status ); RecordFireWireTimeStamp ( FW_TRACE ( kFetchAgentReset ), ( uintptr_t ) this, ( uintptr_t ) orb ); fLogin->submitFetchAgentReset ( ); } //----------------------------------------------------------------------------- // ConnectToDeviceStatic - C->C++ glue. [PROTECTED] //----------------------------------------------------------------------------- IOReturn IOFireWireSerialBusProtocolTransport::ConnectToDeviceStatic ( OSObject * refCon, void * val1, void * val2, void * val3, void * val4 ) { DEBUG_UNUSED ( val1 ); DEBUG_UNUSED ( val2 ); DEBUG_UNUSED ( val3 ); DEBUG_UNUSED ( val4 ); ( ( IOFireWireSerialBusProtocolTransport * ) refCon )->ConnectToDevice ( ); return kIOReturnSuccess; } //----------------------------------------------------------------------------- // ConnectToDevice - Connects to the device. [PROTECTED] //----------------------------------------------------------------------------- void IOFireWireSerialBusProtocolTransport::ConnectToDevice ( void ) { IOReturn status = kIOReturnSuccess; DLOG ( ( "%s: ConnectToDevice called\n", getName ( ) ) ); // Avoid double logins during login phase. fNeedLogin = false; fLoginRetryCount = 0; status = login ( ); if ( status == kIOReturnSuccess ) { // Sleep the start thread - we'll wake it up on login completion. fCommandGate->commandSleep ( ( void * ) &reserved->fLoginState, THREAD_UNINT ); } } //----------------------------------------------------------------------------- // DisconnectFromDevice - Disconnects from device. [PROTECTED] //----------------------------------------------------------------------------- void IOFireWireSerialBusProtocolTransport::DisconnectFromDevice ( void ) { DLOG ( ( "%s: DisconnectFromDevice called\n", getName ( ) ) ); fLoggedIn = false; // Avoid logins during a logout phase. fNeedLogin = false; fLogin->submitLogout ( ); } //----------------------------------------------------------------------------- // CriticalOrbSubmissionStatic - C->C++ glue. [PROTECTED] //----------------------------------------------------------------------------- IOReturn IOFireWireSerialBusProtocolTransport::CriticalOrbSubmissionStatic ( OSObject * refCon, void * val1, void * val2, void * val3, void * val4 ) { DEBUG_UNUSED ( val3 ); DEBUG_UNUSED ( val4 ); ( ( IOFireWireSerialBusProtocolTransport * ) refCon )->CriticalOrbSubmission ( ( IOFireWireSBP2ORB * ) val1, ( SCSITaskIdentifier ) val2 ); return kIOReturnSuccess; } //----------------------------------------------------------------------------- // CriticalOrbSubmission - add command to queue on workloop. [PROTECTED] //----------------------------------------------------------------------------- void IOFireWireSerialBusProtocolTransport::CriticalOrbSubmission ( IOFireWireSBP2ORB * orb, SCSITaskIdentifier request ) { SBP2ClientOrbData * clientData; clientData = ( SBP2ClientOrbData * ) orb->getRefCon ( ); require ( clientData, exit ); clientData->scsiTask = request; reserved->fSubmitQueue->returnCommand ( orb ); // Avoid double appending an active orb ( not this one ). if ( fORB == NULL ) { submitOrbFromQueue ( ); } exit: return; } //----------------------------------------------------------------------------- // submitOrbFromQueue - submitORB on workloop. [PROTECTED] //----------------------------------------------------------------------------- OSMetaClassDefineReservedUsed ( IOFireWireSerialBusProtocolTransport, 6 ); void IOFireWireSerialBusProtocolTransport::submitOrbFromQueue ( void ) { DLOG ( ( "%s: submitOrbFromQueue called\n", getName ( ) ) ); // This check is necessary because we may be draining the queue on the requesting close path. if ( fLoggedIn == true ) { if ( fORB == NULL ) { fORB = ( IOFireWireSBP2ORB * ) reserved->fSubmitQueue->getCommand ( false ); fReconnectCount = 0; } if ( fORB != NULL ) { RecordFireWireTimeStamp ( FW_TRACE ( kSubmitOrb ), ( uintptr_t ) this, ( uintptr_t ) fORB ); fLogin->submitORB ( fORB ); } } } //----------------------------------------------------------------------------- // AllocateResources - Allocates resources. [PROTECTED] //----------------------------------------------------------------------------- IOReturn IOFireWireSerialBusProtocolTransport::AllocateResources ( void ) { IOReturn status = kIOReturnSuccess; IOWorkLoop * workLoop = NULL; DLOG ( ( "%s: AllocateResources called\n", getName ( ) ) ); fLogin = fSBPTarget->createLogin ( ); require_action ( fLogin, exit, status = kIOReturnNoMemory ); fLogin->setLoginFlags ( kFWSBP2ExclusiveLogin ); fLogin->setLoginRetryCountAndDelayTime ( kLoginRetryCount, kLoginDelayTime ); fLogin->setMaxPayloadSize ( kMaxFireWirePayload ); fLogin->setStatusNotifyProc ( this, StatusNotifyStatic ); fLogin->setUnsolicitedStatusNotifyProc ( this, UnsolicitedStatusNotifyStatic ); fLogin->setLoginCompletion ( this, LoginCompletionStatic ); fLogin->setLogoutCompletion ( this, LogoutCompletionStatic ); fLogin->setFetchAgentResetCompletion ( this, FetchAgentResetCompleteStatic ); // Set BUSY_TIMEOUT register value see SBP-2 spec section 6.2 // also see IEEE Std 1394-1995 section 8.3.2.3.5 ( no I am not kidding ) fLogin->setBusyTimeoutRegisterValue ( kDefaultBusyTimeoutValue ); fLUNResetORB = fSBPTarget->createManagementORB ( this, LunResetCompleteStatic ); require_action ( fLUNResetORB, exit, status = kIOReturnNoMemory ); fLUNResetORB->setCommandFunction ( kFWSBP2LogicalUnitReset ); fLUNResetORB->setManageeCommand ( fLogin ); // Allocate expansion data. reserved = ( ExpansionData * ) IOMalloc ( sizeof ( ExpansionData ) ); require_action ( reserved, exit, status = kIOReturnNoMemory ); bzero ( reserved, sizeof ( ExpansionData ) ); reserved->fLoginState = kFirstTimeLoggingInState; // Cache this as a member variable since we don't want to do this on every command. reserved->fAlwaysSetSenseData = getProperty ( kAlwaysSetAutoSenseData, gIOServicePlane ) ? true : false; workLoop = getWorkLoop ( ); require_action ( workLoop, exit, status = kIOReturnNoMemory ); reserved->fCommandPool = IOCommandPool::withWorkLoop ( workLoop ); require_action ( reserved->fCommandPool, exit, status = kIOReturnNoMemory ); reserved->fSubmitQueue = IOCommandPool::withWorkLoop ( workLoop ); require_action ( reserved->fSubmitQueue, exit, status = kIOReturnNoMemory ); for ( UInt32 i = 0; i < kCommandPoolOrbCount; ++i ) { IOFireWireSBP2ORB * orb = NULL; SBP2ClientOrbData * clientData = NULL; orb = fLogin->createORB ( ); require_action ( orb, exit, status = kIOReturnNoMemory ); clientData = ( SBP2ClientOrbData * ) IOMalloc ( sizeof ( SBP2ClientOrbData ) ); require_action ( clientData, exit, status = kIOReturnNoMemory ); bzero ( clientData, sizeof ( SBP2ClientOrbData ) ); clientData->orb = orb; orb->setRefCon ( ( void * ) clientData ); // Enqueue the command in the free list. reserved->fCommandPool->returnCommand ( orb ); } status = kIOReturnSuccess; exit: return status; } //----------------------------------------------------------------------------- // DeallocateResources - Deallocates resources. [PROTECTED] //----------------------------------------------------------------------------- void IOFireWireSerialBusProtocolTransport::DeallocateResources ( void ) { IOFireWireSBP2ORB * orb = NULL; SBP2ClientOrbData * clientData = NULL; DLOG ( ( "%s: DeallocateResources called\n", getName ( ) ) ); // /!\ WARNING - always release orb's before logins. if ( fLUNResetORB != NULL ) { fLUNResetORB->release ( ); fLUNResetORB = NULL; } if ( reserved != NULL ) { // Drain the queue when requesting close so there are no in use commands when this is called. while ( ( orb = ( IOFireWireSBP2ORB * ) reserved->fCommandPool->getCommand ( false ) ) ) { clientData = ( SBP2ClientOrbData * ) orb->getRefCon ( ); if ( clientData != NULL ) { IOFree ( clientData, sizeof ( SBP2ClientOrbData ) ); clientData = NULL; } orb->release ( ); orb = NULL; } reserved->fCommandPool->release ( ); reserved->fCommandPool = NULL; reserved->fSubmitQueue->release ( ); reserved->fSubmitQueue = NULL; } if ( fLogin != NULL ) { fLogin->release ( ); fLogin = NULL; } } //----------------------------------------------------------------------------- // login - login bottleneck to track retries. [PROTECTED] //----------------------------------------------------------------------------- OSMetaClassDefineReservedUsed ( IOFireWireSerialBusProtocolTransport, 1 ); IOReturn IOFireWireSerialBusProtocolTransport::login ( void ) { IOReturn status = kIOReturnError; DLOG ( ( "%s: submitting login.\n", getName ( ) ) ); fNeedLogin = false; fLoggedIn = false; // If we enter this again and fLoginRetryCount is already // at kMaxLoginRetryCount - we should default status to an error. for ( ; fLoginRetryCount < kMaxLoginRetryCount; ++fLoginRetryCount ) { RecordFireWireTimeStamp ( FW_TRACE ( kLoginRequest ), ( uintptr_t ) this, reserved->fLoginState, fLoginRetryCount ); status = submitLogin ( ); if ( status == kIOReturnSuccess ) { break; } } if ( status != kIOReturnSuccess ) { fNeedLogin = true; fLoggedIn = false; RecordFireWireTimeStamp ( FW_TRACE ( kLoginLost ), ( uintptr_t ) this, reserved->fLoginState ); loginLost ( ); } return status; } //----------------------------------------------------------------------------- // submitLogin - submitLogin bottleneck for subclass. [PROTECTED] //----------------------------------------------------------------------------- OSMetaClassDefineReservedUsed ( IOFireWireSerialBusProtocolTransport, 2 ); IOReturn IOFireWireSerialBusProtocolTransport::submitLogin ( void ) { IOReturn status = kIOReturnSuccess; DLOG ( ( "%s: submitting login.\n", getName ( ) ) ); status = fLogin->submitLogin ( ); return status; } //----------------------------------------------------------------------------- // loginLost - login lost bottleneck for subclass. [PROTECTED] //----------------------------------------------------------------------------- OSMetaClassDefineReservedUsed ( IOFireWireSerialBusProtocolTransport, 3 ); void IOFireWireSerialBusProtocolTransport::loginLost ( void ) { DLOG ( ( "%s: login lost.\n", getName ( ) ) ); // Notification that the login is lost. } //----------------------------------------------------------------------------- // loginSuspended - login suspended bottleneck for subclass. [PROTECTED] //----------------------------------------------------------------------------- OSMetaClassDefineReservedUsed ( IOFireWireSerialBusProtocolTransport, 4 ); void IOFireWireSerialBusProtocolTransport::loginSuspended ( void ) { DLOG ( ( "%s: login suspended.\n", getName ( ) ) ); // A successful reconnect orb is required. } //----------------------------------------------------------------------------- // loginResumed - login resumed bottleneck for subclass. [PROTECTED] //----------------------------------------------------------------------------- OSMetaClassDefineReservedUsed ( IOFireWireSerialBusProtocolTransport, 5 ); void IOFireWireSerialBusProtocolTransport::loginResumed ( void ) { DLOG ( ( "%s: login resumed.\n", getName ( ) ) ); // A reconnect orb has succeeded. } #if 0 #pragma mark - #pragma mark Static Debug Assertion Method #pragma mark - #endif //----------------------------------------------------------------------------- // IOFireWireSerialBusProtocolTransportDebugAssert [STATIC] //----------------------------------------------------------------------------- #if !DEBUG_ASSERT_PRODUCTION_CODE void IOFireWireSerialBusProtocolTransportDebugAssert ( const char * componentNameString, const char * assertionString, const char * exceptionLabelString, const char * errorString, const char * fileName, long lineNumber, int errorCode ) { IOLog ( "%s Assert failed: %s ", componentNameString, assertionString ); if ( exceptionLabelString != NULL ) { IOLog ( "%s ", exceptionLabelString ); } if ( errorString != NULL ) { IOLog ( "%s ", errorString ); } if ( fileName != NULL ) { IOLog ( "file: %s ", fileName ); } if ( lineNumber != 0 ) { IOLog ( "line: %ld ", lineNumber ); } if ( ( long ) errorCode != 0 ) { IOLog ( "error: %ld ( 0x%08lx )", ( long ) errorCode, ( long ) errorCode ); } IOLog ( "\n" ); } #endif //----------------------------------------------------------------------------- // RecordFireWireTimeStamp [STATIC] //----------------------------------------------------------------------------- static inline void RecordFireWireTimeStamp ( unsigned int code, unsigned int a, unsigned int b, unsigned int c, unsigned int d ) { if ( gSBP2DiskDebugFlags & kSBP2DiskEnableTracePointsMask ) { IOTimeStampConstant ( code, a, b, c, d ); } } #if 0 #pragma mark - #pragma mark VTable Padding #pragma mark - #endif OSMetaClassDefineReservedUnused ( IOFireWireSerialBusProtocolTransport, 7 ); OSMetaClassDefineReservedUnused ( IOFireWireSerialBusProtocolTransport, 8 ); OSMetaClassDefineReservedUnused ( IOFireWireSerialBusProtocolTransport, 9 ); OSMetaClassDefineReservedUnused ( IOFireWireSerialBusProtocolTransport, 10 ); OSMetaClassDefineReservedUnused ( IOFireWireSerialBusProtocolTransport, 11 ); OSMetaClassDefineReservedUnused ( IOFireWireSerialBusProtocolTransport, 12 ); OSMetaClassDefineReservedUnused ( IOFireWireSerialBusProtocolTransport, 13 ); OSMetaClassDefineReservedUnused ( IOFireWireSerialBusProtocolTransport, 14 ); OSMetaClassDefineReservedUnused ( IOFireWireSerialBusProtocolTransport, 15 ); OSMetaClassDefineReservedUnused ( IOFireWireSerialBusProtocolTransport, 16 );