/* * 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 //-------------------------------------------------------------------------------------------------- // This class' header file #include "IOUSBMassStorageClass.h" #include "IOUSBMassStorageClassTimestamps.h" #include "Debugging.h" //-------------------------------------------------------------------------------------------------- // Macros //-------------------------------------------------------------------------------------------------- // Bulk Only State Machine States enum { kBulkOnlyCommandSent = 1, kBulkOnlyCheckCBWBulkStall, kBulkOnlyClearCBWBulkStall, kBulkOnlyBulkIOComplete, kBulkOnlyCheckBulkStall, kBulkOnlyClearBulkStall, kBulkOnlyCheckBulkStallPostCSW, kBulkOnlyClearBulkStallPostCSW, kBulkOnlyStatusReceived, kBulkOnlyStatusReceived2ndTime, kBulkOnlyResetCompleted, kBulkOnlyClearBulkInCompleted, kBulkOnlyClearBulkOutCompleted }; #pragma mark - #pragma mark Protocol Services Methods #pragma mark - //-------------------------------------------------------------------------------------------------- // AbortSCSICommandForBulkOnlyProtocol - The AbortSCSICommand helper method for Bulk Only protocol // devices. [PROTECTED] //-------------------------------------------------------------------------------------------------- IOReturn IOUSBMassStorageClass::AbortSCSICommandForBulkOnlyProtocol ( SCSITaskIdentifier request ) { UNUSED ( request ); return kIOReturnError; } //-------------------------------------------------------------------------------------------------- // SendSCSICommandForBulkOnlyProtocol - The SendSCSICommand helper method // for Bulk Only protocol devices. // [PROTECTED] //-------------------------------------------------------------------------------------------------- IOReturn IOUSBMassStorageClass::SendSCSICommandForBulkOnlyProtocol ( SCSITaskIdentifier request ) { IOReturn status; BulkOnlyRequestBlock * theBulkOnlyRB; theBulkOnlyRB = GetBulkOnlyRequestBlock(); // Clear out the CBW bzero ( theBulkOnlyRB, sizeof ( BulkOnlyRequestBlock ) ); // Save the SCSI Task theBulkOnlyRB->request = request; // Set up the IOUSBCompletion structure theBulkOnlyRB->boCompletion.target = this; theBulkOnlyRB->boCompletion.action = &this->BulkOnlyUSBCompletionAction; theBulkOnlyRB->boCompletion.parameter = theBulkOnlyRB; STATUS_LOG ( ( 6, "%s[%p]: SendSCSICommandForBulkOnlyProtocol send CBW", getName(), this ) ); status = BulkOnlySendCBWPacket ( theBulkOnlyRB, kBulkOnlyCommandSent ); STATUS_LOG ( ( 5, "%s[%p]: SendSCSICommandForBulkOnlyProtocol send CBW returned %x", getName(), this, status ) ); if ( status != kIOReturnSuccess ) { ReleaseBulkOnlyRequestBlock ( theBulkOnlyRB ); } return status; } #pragma mark - #pragma mark Bulk Only Protocol Specific Commands //-------------------------------------------------------------------------------------------------- // BulkDeviceResetDevice [PROTECTED] //-------------------------------------------------------------------------------------------------- IOReturn IOUSBMassStorageClass::BulkDeviceResetDevice ( BulkOnlyRequestBlock * boRequestBlock, UInt32 nextExecutionState ) { IOReturn status; if ( fTerminating == true ) { // We have an invalid interface, the device has probably been removed. // Nothing else to do except to report an error. return kIOReturnDeviceError; } // Clear out the structure for the request bzero ( &fUSBDeviceRequest, sizeof ( IOUSBDevRequest ) ); // Build the USB command fUSBDeviceRequest.bmRequestType = USBmakebmRequestType ( kUSBNone, kUSBClass, kUSBInterface ); fUSBDeviceRequest.bRequest = 0xFF; fUSBDeviceRequest.wValue = 0; fUSBDeviceRequest.wIndex = GetInterfaceReference()->GetInterfaceNumber(); fUSBDeviceRequest.wLength = 0; fUSBDeviceRequest.pData = NULL; // Set the next state to be executed boRequestBlock->currentState = nextExecutionState; // Send the command over the control endpoint status = GetInterfaceReference()->DeviceRequest ( &fUSBDeviceRequest, &boRequestBlock->boCompletion ); STATUS_LOG ( ( 4, "%s[%p]: BulkDeviceResetDevice returned %x", getName(), this, status ) ); return status; } #pragma mark - #pragma mark SendSCSICommand Helper methods //-------------------------------------------------------------------------------------------------- // BulkOnlyUSBCompletionAction [PROTECTED] //-------------------------------------------------------------------------------------------------- void IOUSBMassStorageClass::BulkOnlyUSBCompletionAction ( void * target, void * parameter, IOReturn status, UInt32 bufferSizeRemaining) { IOUSBMassStorageClass * theMSC; BulkOnlyRequestBlock * boRequestBlock; theMSC = ( IOUSBMassStorageClass * ) target; boRequestBlock = ( BulkOnlyRequestBlock * ) parameter; theMSC->BulkOnlyExecuteCommandCompletion ( boRequestBlock, status, bufferSizeRemaining ); } //-------------------------------------------------------------------------------------------------- // BulkOnlySendCBWPacket - Prepare the Command Block Wrapper packet for Bulk Only Protocol // [PROTECTED] //-------------------------------------------------------------------------------------------------- IOReturn IOUSBMassStorageClass::BulkOnlySendCBWPacket ( BulkOnlyRequestBlock * boRequestBlock, UInt32 nextExecutionState ) { IOReturn status = kIOReturnError; // Set our Bulk-Only phase descriptor. require ( ( fBulkOnlyCBWMemoryDescriptor != NULL ), Exit ); boRequestBlock->boPhaseDesc = fBulkOnlyCBWMemoryDescriptor; boRequestBlock->boCBW.cbwSignature = kCommandBlockWrapperSignature; boRequestBlock->boCBW.cbwTag = GetNextBulkOnlyCommandTag(); boRequestBlock->boCBW.cbwTransferLength = HostToUSBLong( GetRequestedDataTransferCount(boRequestBlock->request)); if (GetDataTransferDirection(boRequestBlock->request) == kSCSIDataTransfer_FromTargetToInitiator) { boRequestBlock->boCBW.cbwFlags = kCBWFlagsDataIn; } else if (GetDataTransferDirection(boRequestBlock->request) == kSCSIDataTransfer_FromInitiatorToTarget) { boRequestBlock->boCBW.cbwFlags = kCBWFlagsDataOut; } else { boRequestBlock->boCBW.cbwFlags = 0; } // Set the LUN (not needed until LUN support is added). boRequestBlock->boCBW.cbwLUN = GetLogicalUnitNumber( boRequestBlock->request ) & kCBWLUNMask; // Bits 0-3: LUN, 4-7: Reserved boRequestBlock->boCBW.cbwCDBLength = GetCommandDescriptorBlockSize( boRequestBlock->request ); // Bits 0-4: CDB Length, 5-7: Reserved GetCommandDescriptorBlock( boRequestBlock->request, &boRequestBlock->boCBW.cbwCDB ); RecordUSBTimeStamp ( UMC_TRACE ( kBOCBWDescription ), ( uintptr_t ) this, ( uintptr_t ) boRequestBlock->request, ( unsigned int ) boRequestBlock->boCBW.cbwLUN, ( unsigned int ) boRequestBlock->boCBW.cbwTag ); // Once timeouts are support, set the timeout value for the request // Set the next state to be executed boRequestBlock->currentState = nextExecutionState; // Send the CBW to the device if ( GetBulkOutPipe() == NULL ) { STATUS_LOG ( ( 4, "%s[%p]: BulkOnlySendCBWPacket Bulk Out is NULL", getName(), this ) ); } STATUS_LOG ( ( 6, "%s[%p]: BulkOnlySendCBWPacket sent", getName(), this ) ); status = GetBulkOutPipe()->Write( boRequestBlock->boPhaseDesc, GetTimeoutDuration( boRequestBlock->request ), // Use the client's timeout for both GetTimeoutDuration( boRequestBlock->request ), &boRequestBlock->boCompletion ); STATUS_LOG ( ( 5, "%s[%p]: BulkOnlySendCBWPacket returned %x", getName(), this, status ) ); RecordUSBTimeStamp ( UMC_TRACE ( kBOCBWBulkOutWriteResult ), ( uintptr_t ) this, status, ( uintptr_t ) boRequestBlock->boCBW.cbwLUN, ( uintptr_t ) boRequestBlock->request ); if ( status == kIOUSBPipeStalled ) { STATUS_LOG ( ( 5, "%s[%p]: BulkOnlySendCBWPacket could not be queued, returned", getName(), this ) ); // The host is reporting a pipe stall. We'll need to address this if we ever wish to attempt a retry. // We're relying on higher elements of the storage stack to iniate the retry. boRequestBlock->currentState = kBulkOnlyCheckCBWBulkStall; status = GetStatusEndpointStatus ( GetBulkOutPipe(), &boRequestBlock->boGetStatusBuffer, &boRequestBlock->boCompletion ); } Exit: return status; } //-------------------------------------------------------------------------------------------------- // BulkOnlyTransferData [PROTECTED] //-------------------------------------------------------------------------------------------------- IOReturn IOUSBMassStorageClass::BulkOnlyTransferData ( BulkOnlyRequestBlock * boRequestBlock, UInt32 nextExecutionState ) { IOReturn status = kIOReturnError; // Set the next state to be executed boRequestBlock->currentState = nextExecutionState; // Start a bulk in or out transaction if ( GetDataTransferDirection ( boRequestBlock->request ) == kSCSIDataTransfer_FromTargetToInitiator ) { status = GetBulkInPipe()->Read( GetDataBuffer( boRequestBlock->request ), GetTimeoutDuration( boRequestBlock->request ), // Use the client's timeout for both GetTimeoutDuration( boRequestBlock->request ), GetRequestedDataTransferCount( boRequestBlock->request ), &boRequestBlock->boCompletion ); } else if ( GetDataTransferDirection(boRequestBlock->request) == kSCSIDataTransfer_FromInitiatorToTarget ) { status = GetBulkOutPipe()->Write( GetDataBuffer ( boRequestBlock->request ), GetTimeoutDuration ( boRequestBlock->request ), // Use the client's timeout for both GetTimeoutDuration ( boRequestBlock->request ), GetRequestedDataTransferCount ( boRequestBlock->request ), &boRequestBlock->boCompletion ); } STATUS_LOG ( ( 5, "%s[%p]: BulkOnlyTransferData returned %x", getName(), this, status ) ); return status; } //-------------------------------------------------------------------------------------------------- // BulkOnlyReceiveCSWPacket - Prepare the Command Status Wrapper packet for Bulk Only Protocol. // [PROTECTED] //-------------------------------------------------------------------------------------------------- // IOReturn IOUSBMassStorageClass::BulkOnlyReceiveCSWPacket ( BulkOnlyRequestBlock * boRequestBlock, UInt32 nextExecutionState ) { IOReturn status = kIOReturnError; // Set our Bulk-Only phase descriptor. require ( ( fBulkOnlyCSWMemoryDescriptor != NULL ), Exit ); boRequestBlock->boPhaseDesc = fBulkOnlyCSWMemoryDescriptor; // Set the next state to be executed boRequestBlock->currentState = nextExecutionState; // Retrieve the CSW from the device status = GetBulkInPipe()->Read ( boRequestBlock->boPhaseDesc, GetTimeoutDuration( boRequestBlock->request ), // Use the client's timeout for both GetTimeoutDuration( boRequestBlock->request ), &boRequestBlock->boCompletion ); STATUS_LOG ( ( 5, "%s[%p]: BulkOnlyReceiveCSWPacket returned %x", getName(), this, status ) ); Exit: return status; } //-------------------------------------------------------------------------------------------------- // BulkOnlyExecuteCommandCompletion [PROTECTED] //-------------------------------------------------------------------------------------------------- void IOUSBMassStorageClass::BulkOnlyExecuteCommandCompletion ( BulkOnlyRequestBlock * boRequestBlock, IOReturn resultingStatus, UInt32 bufferSizeRemaining) { UNUSED ( bufferSizeRemaining ); IOReturn status = kIOReturnError; bool commandInProgress = false; STATUS_LOG(( 4, "%s[%p]: BulkOnlyExecuteCommandCompletion Entered with boRequestBlock=%p resultingStatus=0x%x", getName(), this, boRequestBlock, resultingStatus )); if ( ( boRequestBlock->request == NULL ) || ( fBulkOnlyCommandStructInUse == false ) ) { // The request field is NULL, this appears to be a double callback, do nothing. // OR the command was aborted earlier, do nothing. STATUS_LOG ( ( 4, "%s[%p]: boRequestBlock->request is NULL, returned %x", getName(), this, resultingStatus ) ); RecordUSBTimeStamp ( UMC_TRACE ( kBODoubleCompleteion ), ( uintptr_t ) this, NULL, NULL, NULL ); return; } if ( ( GetInterfaceReference() == NULL ) || ( fTerminating == true ) ) { // Our interface has been closed, probably because of an // unplug, return an error for the command since it can no // longer be executed. SCSITaskIdentifier request = boRequestBlock->request; STATUS_LOG ( ( 4, "%s[%p]: Interface object is NULL, returned %x", getName(), this, resultingStatus ) ); RecordUSBTimeStamp ( UMC_TRACE ( kBOCompletionDuringTermination ), ( uintptr_t ) this, NULL, NULL, NULL ); ReleaseBulkOnlyRequestBlock ( boRequestBlock ); CompleteSCSICommand ( request, status ); return; } RecordUSBTimeStamp ( UMC_TRACE ( kBOCompletion ), ( uintptr_t ) this, resultingStatus, ( uintptr_t ) boRequestBlock->currentState, ( uintptr_t ) boRequestBlock->request ); if ( ( resultingStatus == kIOReturnNotResponding ) || ( resultingStatus == kIOReturnAborted ) ) { STATUS_LOG ( ( 5, "%s[%p]: BulkOnlyExecuteCommandCompletion previous command returned %x", getName(), this, resultingStatus ) ); // The transfer failed mid-transfer or was aborted by the USB layer. Either way the device will // be non-responsive until we reset it, or we discover it has been disconnected. ResetDeviceNow ( false ); commandInProgress = true; goto Exit; } switch ( boRequestBlock->currentState ) { case kBulkOnlyCommandSent: { STATUS_LOG ( ( 5, "%s[%p]: kBulkOnlyCommandSent returned %x", getName(), this, resultingStatus ) ); if ( resultingStatus == kIOUSBPipeStalled ) { // The device is so far gone that we couldn't even retry the CSW. Reset time. if ( fUseUSBResetNotBOReset ) { // By passing this to Finish device recovery we ensure that the driver is still active, // and that the device is still connected to the Mac. ResetDeviceNow ( false ); status = kIOReturnSuccess; } else { // The host is reporting a pipe stall. We'll need to address this if we ever wish to attempt a retry. // We're relying on higher elements of the storage stack to iniate the retry. boRequestBlock->currentState = kBulkOnlyCheckCBWBulkStall; status = GetStatusEndpointStatus( GetBulkOutPipe(), &boRequestBlock->boGetStatusBuffer, &boRequestBlock->boCompletion ); } if ( status == kIOReturnSuccess ) { commandInProgress = true; } break; } if ( resultingStatus != kIOReturnSuccess ) { // An error occurred, probably a timeout error, // and the command was not successfully sent to the device. ResetDeviceNow ( false ); status = kIOReturnSuccess; if( status == kIOReturnSuccess ) { commandInProgress = true; } break; } // If there is to be no data transfer then we are done and can return to the caller. if ( ( GetDataTransferDirection ( boRequestBlock->request ) == kSCSIDataTransfer_NoDataTransfer ) || ( GetRequestedDataTransferCount ( boRequestBlock->request ) == 0 ) ) { // Bulk transfer is done, get the Command Status Wrapper from the device. status = BulkOnlyReceiveCSWPacket ( boRequestBlock, kBulkOnlyStatusReceived ); if ( status == kIOReturnSuccess ) { commandInProgress = true; } } else { // Start a bulk in or out transaction. status = BulkOnlyTransferData ( boRequestBlock, kBulkOnlyBulkIOComplete ); if ( status == kIOReturnSuccess ) { commandInProgress = true; } } } break; case kBulkOnlyCheckCBWBulkStall: { STATUS_LOG ( ( 5, "%s[%p]: IOUSBMassStorageClass::BulkOnlyExecuteCommandCompletion - kBulkOnlyCheckCBWBulkStall returned %x", getName(), this, resultingStatus ) ); // Check to see if the endpoint was stalled if ( ( boRequestBlock->boGetStatusBuffer[0] & 1 ) == 1 ) { STATUS_LOG ( ( 5, "%s[%p]: IOUSBMassStorageClass::BulkOnlyExecuteCommandCompletion - will try to clear endpoint", getName(), this ) ); // The endpoint was stalled. Clear the stall so we'll be able to retry sending the CBW. boRequestBlock->currentState = kBulkOnlyClearCBWBulkStall; status = ClearFeatureEndpointStall ( GetBulkOutPipe(), &boRequestBlock->boCompletion ); STATUS_LOG ( ( 5, "%s[%p]: IOUSBMassStorageClass::BulkOnlyExecuteCommandCompletion - ClearFeatureEndpointStall returned status = %x", getName(), this, status ) ); if ( status == kIOReturnSuccess ) { commandInProgress = true; } } else { STATUS_LOG ( ( 5, "%s[%p]: IOUSBMassStorageClass::BulkOnlyExecuteCommandCompletion - will abort bulk out pipe", getName(), this ) ); // Since the pipe was not stalled, but the host thought it was we should clear the host side of the pipe. GetBulkOutPipe()->Abort(); // As we failed to successfully transmit the BO CBW we return an error up the stack so the command will be retried. SetRealizedDataTransferCount ( boRequestBlock->request, 0 ); status = kIOReturnError; } } break; case kBulkOnlyClearCBWBulkStall: { STATUS_LOG ( ( 5, "%s[%p]: kBulkOnlyClearCBWBulkStall returned %x", getName(), this, resultingStatus ) ); // As we failed to successfully transmit the BO CBW we return an error up the stack so the command will be retried. SetRealizedDataTransferCount ( boRequestBlock->request, 0 ); status = kIOReturnError; } break; case kBulkOnlyBulkIOComplete: { status = resultingStatus; // and status STATUS_LOG ( ( 5, "%s[%p]: kBulkOnlyBulkIOComplete returned %x", getName(), this, resultingStatus ) ); if ( ( resultingStatus == kIOUSBPipeStalled ) || ( resultingStatus == kIOReturnSuccess ) ) { // Save the number of bytes tranferred in the request // Use the amount returned by USB to determine the amount of data transferred instead of // the data residue field in the CSW since some devices will report the wrong value. SetRealizedDataTransferCount ( boRequestBlock->request, GetRequestedDataTransferCount ( boRequestBlock->request ) - bufferSizeRemaining); } if ( resultingStatus == kIOReturnSuccess ) { // Bulk transfer is done, get the Command Status Wrapper from the device status = BulkOnlyReceiveCSWPacket ( boRequestBlock, kBulkOnlyStatusReceived ); if ( status == kIOReturnSuccess ) { commandInProgress = true; } } else if ( resultingStatus == kIOReturnOverrun ) { // We set the data transfered to size of the request because the IOUSBFamily // discards the excess for us. SetRealizedDataTransferCount ( boRequestBlock->request, GetRequestedDataTransferCount ( boRequestBlock->request ) ); // Reset the device. We have to do a full device reset since a fair quantity of // stellar USB devices don't properly handle a mid I/O Bulk-Only device reset. ResetDeviceNow ( false ); commandInProgress = true; } else { // Either an error occurred on transfer or we did not get all the data we requested. // In either case, this transfer is complete, clean up and return an error to the client. if ( ( resultingStatus == kIOReturnDeviceError ) || ( resultingStatus == kIOReturnNotResponding ) || ( resultingStatus == kIOUSBHighSpeedSplitError ) ) { // Was there a device error? The device could have been removed or lost power. ResetDeviceNow ( false ); status = kIOReturnSuccess; if ( status == kIOReturnSuccess ) { commandInProgress = true; } } else if ( resultingStatus == kIOUSBTransactionTimeout ) { // The device is so far gone that we couldn't even retry the CSW. Reset time. if ( fUseUSBResetNotBOReset ) { // By passing this to Finish device recovery we ensure that the driver is still active, // and that the device is still connected to the Mac. ResetDeviceNow ( false ); status = kIOReturnSuccess; } else { status = BulkDeviceResetDevice( boRequestBlock, kBulkOnlyResetCompleted ); } if( status == kIOReturnSuccess ) { commandInProgress = true; } } else { // Check if the bulk endpoint was stalled. if ( GetDataTransferDirection ( boRequestBlock->request ) == kSCSIDataTransfer_FromTargetToInitiator ) { fPotentiallyStalledPipe = GetBulkInPipe(); } else if ( GetDataTransferDirection ( boRequestBlock->request ) == kSCSIDataTransfer_FromInitiatorToTarget ) { fPotentiallyStalledPipe = GetBulkOutPipe(); } else { fPotentiallyStalledPipe = GetControlPipe(); } boRequestBlock->currentState = kBulkOnlyCheckBulkStall; status = GetStatusEndpointStatus ( fPotentiallyStalledPipe, &boRequestBlock->boGetStatusBuffer, &boRequestBlock->boCompletion ); if ( status == kIOReturnSuccess ) { commandInProgress = true; } } } } break; case kBulkOnlyCheckBulkStall: case kBulkOnlyCheckBulkStallPostCSW: { STATUS_LOG ( ( 5, "%s[%p]: kBulkOnlyCheckBulkStall returned %x", getName(), this, resultingStatus ) ); // Check to see if the endpoint was stalled if ( ( boRequestBlock->boGetStatusBuffer[0] & 1 ) == 1 ) { // Is this stall from the data or status phase? if ( boRequestBlock->currentState == kBulkOnlyCheckBulkStall ) { boRequestBlock->currentState = kBulkOnlyClearBulkStall; } else { boRequestBlock->currentState = kBulkOnlyClearBulkStallPostCSW; } status = ClearFeatureEndpointStall ( fPotentiallyStalledPipe, &boRequestBlock->boCompletion ); if ( status == kIOReturnSuccess ) { fPotentiallyStalledPipe = NULL; commandInProgress = true; } } else { // If the endpoint was not stalled, attempt to get the CSW if ( fPotentiallyStalledPipe != NULL ) { // Since the pipe was not stalled, but the host thought it was we should clear the host side of the pipe. fPotentiallyStalledPipe->Abort(); } // Did we think we were stalled following the data or status phase? // We don't want to try to get the CSW again more than once. if ( boRequestBlock->currentState == kBulkOnlyCheckBulkStall ) { // If the endpoint was not stalled, attempt to get the CSW status = BulkOnlyReceiveCSWPacket ( boRequestBlock, kBulkOnlyStatusReceived ); } else { // If the endpoint was not stalled, attempt to get the CSW status = BulkOnlyReceiveCSWPacket ( boRequestBlock, kBulkOnlyStatusReceived2ndTime ); } if ( status == kIOReturnSuccess ) { commandInProgress = true; } } } break; case kBulkOnlyClearBulkStall: case kBulkOnlyClearBulkStallPostCSW: { STATUS_LOG ( ( 5, "%s[%p]: kBulkOnlyClearBulkStall returned %x", getName(), this, resultingStatus ) ); // The pipe was stalled and an attempt to clear it was made // Try to get the CSW. If the pipe was not successfully cleared, this will also // set off a device reset sequence. // If we arlready tried to get the CSW once, only try to get it once again. if ( boRequestBlock->currentState == kBulkOnlyClearBulkStall ) { status = BulkOnlyReceiveCSWPacket( boRequestBlock, kBulkOnlyStatusReceived ); } else { status = BulkOnlyReceiveCSWPacket( boRequestBlock, kBulkOnlyStatusReceived2ndTime ); } if ( status == kIOReturnSuccess ) { commandInProgress = true; } } break; case kBulkOnlyStatusReceived: { STATUS_LOG ( ( 5, "%s[%p]: kBulkOnlyStatusReceived returned %x", getName(), this, resultingStatus ) ); // Bulk transfer is done, get the Command Status Wrapper from the device if ( resultingStatus == kIOUSBPipeStalled) { // An error occurred trying to get the CSW, we should clear any stalls and try to get the CSW again. boRequestBlock->currentState = kBulkOnlyCheckBulkStallPostCSW; status = GetStatusEndpointStatus ( GetBulkInPipe(), &boRequestBlock->boGetStatusBuffer, &boRequestBlock->boCompletion ); if ( status == kIOReturnSuccess ) { fPotentiallyStalledPipe = GetBulkInPipe(); commandInProgress = true; } } else if ( resultingStatus != kIOReturnSuccess) { // An error occurred trying to get the first CSW, we should check and clear the stall, // and then try the CSW again status = BulkOnlyReceiveCSWPacket ( boRequestBlock, kBulkOnlyStatusReceived2ndTime ); if ( status != kIOReturnSuccess ) { // The device is so far gone that we couldn't even retry the CSW. Reset time. if ( fUseUSBResetNotBOReset ) { // By passing this to Finish device recovery we ensure that the driver is still active, // and that the device is still connected to the Mac. ResetDeviceNow ( false ); status = kIOReturnSuccess; } else { status = BulkDeviceResetDevice( boRequestBlock, kBulkOnlyResetCompleted ); } } if ( status == kIOReturnSuccess ) { commandInProgress = true; } } else if ( ( boRequestBlock->boCSW.cswTag == boRequestBlock->boCBW.cbwTag ) || fKnownCSWTagMismatchIssues ) { // Since the CBW and CSW tags match, process // the CSW to determine the appropriate response. switch( boRequestBlock->boCSW.cswStatus ) { case kCSWCommandPassedError: { // The device reports no error on the command, and the realized data count was set after the bulk // data transfer completion state. Return that the command was successfully completed. status = kIOReturnSuccess; } break; case kCSWCommandFailedError: { // The device reported an error for the command. STATUS_LOG(( 4, "%s[%p]: kBulkOnlyStatusReceived kCSWCommandFailedError", getName(), this )); status = kIOReturnError; } break; case kCSWPhaseError: { // The device reported a phase error on the command, perform the // bulk reset on the device. STATUS_LOG(( 4, "%s[%p]: kBulkOnlyStatusReceived kCSWPhaseError", getName(), this )); if ( fUseUSBResetNotBOReset ) { // By passing this to Finish device recovery we ensure that the driver is still active, // and that the device is still connected to the Mac. ResetDeviceNow ( false ); status = kIOReturnSuccess; } else { status = BulkDeviceResetDevice( boRequestBlock, kBulkOnlyResetCompleted ); } if( status == kIOReturnSuccess ) { commandInProgress = true; } } break; default: { STATUS_LOG(( 4, "%s[%p]: kBulkOnlyStatusReceived default", getName(), this )); // We received an unkown status, report an error to the client. status = kIOReturnError; } break; } } else { STATUS_LOG(( 5, "%s[%p]: kBulkOnlyStatusReceived tag mismatch", getName(), this )); // The only way to get to this point is if the command completes successfully, // but the CBW and CSW tags do not match. Report an error to the client. status = kIOReturnError; } } break; case kBulkOnlyStatusReceived2ndTime: { STATUS_LOG(( 5, "%s[%p]: kBulkOnlyStatusReceived2ndTime returned %x", getName(), this, resultingStatus )); // Second try for the CSW is done, if an error occurred, reset device. if ( resultingStatus != kIOReturnSuccess) { if ( fUseUSBResetNotBOReset ) { // By passing this to Finish device recovery we ensure that the driver is still active, // and that the device is still connected to the Mac. ResetDeviceNow ( false ); status = kIOReturnSuccess; } else { status = BulkDeviceResetDevice( boRequestBlock, kBulkOnlyResetCompleted ); } } else { // Our second attempt to retrieve the CSW was successful. // Re-enter the state machine to process the CSW packet. boRequestBlock->currentState = kBulkOnlyStatusReceived; BulkOnlyExecuteCommandCompletion( boRequestBlock, resultingStatus, bufferSizeRemaining ); status = kIOReturnSuccess; } if( status == kIOReturnSuccess ) { commandInProgress = true; } } break; case kBulkOnlyResetCompleted: { STATUS_LOG ( ( 5, "%s[%p]: kBulkOnlyResetCompleted returned %x", getName(), this, resultingStatus ) ); if ( resultingStatus != kIOReturnSuccess) { // The Bulk-Only Reset failed. Try to recover the device. ResetDeviceNow ( false ); commandInProgress = true; break; } boRequestBlock->currentState = kBulkOnlyClearBulkInCompleted; status = ClearFeatureEndpointStall ( GetBulkInPipe(), &boRequestBlock->boCompletion ); if ( status == kIOReturnSuccess ) { commandInProgress = true; } } break; case kBulkOnlyClearBulkInCompleted: { STATUS_LOG ( ( 5, "%s[%p]: kBulkOnlyClearBulkInCompleted returned %x", getName(), this, resultingStatus ) ); boRequestBlock->currentState = kBulkOnlyClearBulkOutCompleted; status = ClearFeatureEndpointStall ( GetBulkOutPipe(), &boRequestBlock->boCompletion ); if ( status == kIOReturnSuccess ) { commandInProgress = true; } } break; case kBulkOnlyClearBulkOutCompleted: { STATUS_LOG ( ( 5, "%s[%p]: kBulkOnlyClearBulkOutCompleted returned %x", getName(), this, resultingStatus ) ); SetRealizedDataTransferCount ( boRequestBlock->request, 0 ); status = kIOReturnError; } break; default: { SetRealizedDataTransferCount ( boRequestBlock->request, 0 ); status = kIOReturnError; } break; } Exit: if ( commandInProgress == false ) { SCSITaskIdentifier request = boRequestBlock->request; ReleaseBulkOnlyRequestBlock ( boRequestBlock ); CompleteSCSICommand ( request, status ); } STATUS_LOG(( 5, "%s[%p]: BulkOnlyExecuteCommandCompletion Returning", getName(), this )); }