USBMassStorageClassBulkOnly.cpp [plain text]
#include "IOUSBMassStorageClass.h"
#include "Debugging.h"
#if (USB_MASS_STORAGE_DEBUG == 1)
#define DEBUG_LEVEL 1
#include <IOKit/usb/IOUSBLog.h>
#define STATUS_LOG(x) USBLog x
#else
#define STATUS_LOG(x)
#endif
enum
{
kBulkOnlyCommandSent = 1,
kBulkOnlyCheckCBWBulkStall,
kBulkOnlyClearCBWBulkStall,
kBulkOnlyBulkIOComplete,
kBulkOnlyCheckBulkStall,
kBulkOnlyClearBulkStall,
kBulkOnlyStatusReceived,
kBulkOnlyStatusReceived2ndTime,
kBulkOnlyResetCompleted,
kBulkOnlyClearBulkInCompleted,
kBulkOnlyClearBulkOutCompleted
};
#define kBulkOnlyCommandPhaseTimeoutValue 5000
#pragma mark -
#pragma mark Protocol Services Methods
#pragma mark -
IOReturn IOUSBMassStorageClass::AbortSCSICommandForBulkOnlyProtocol(
SCSITaskIdentifier request )
{
UNUSED( request );
return kIOReturnError;
}
IOReturn IOUSBMassStorageClass::SendSCSICommandForBulkOnlyProtocol(
SCSITaskIdentifier request )
{
IOReturn status;
BulkOnlyRequestBlock * theBulkOnlyRB;
theBulkOnlyRB = GetBulkOnlyRequestBlock();
bzero( theBulkOnlyRB, sizeof( BulkOnlyRequestBlock ) );
theBulkOnlyRB->request = request;
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
IOReturn
IOUSBMassStorageClass::BulkDeviceResetDevice(
BulkOnlyRequestBlock * boRequestBlock,
UInt32 nextExecutionState )
{
IOReturn status;
if ( fTerminating == true )
{
return kIOReturnDeviceError;
}
bzero( &fUSBDeviceRequest, sizeof(IOUSBDevRequest));
fUSBDeviceRequest.bmRequestType = USBmakebmRequestType(kUSBNone, kUSBClass, kUSBInterface);
fUSBDeviceRequest.bRequest = 0xFF;
fUSBDeviceRequest.wValue = 0;
fUSBDeviceRequest.wIndex = GetInterfaceReference()->GetInterfaceNumber();
fUSBDeviceRequest.wLength = 0;
fUSBDeviceRequest.pData = NULL;
boRequestBlock->currentState = nextExecutionState;
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
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 );
}
IOReturn
IOUSBMassStorageClass::BulkOnlySendCBWPacket(
BulkOnlyRequestBlock * boRequestBlock,
UInt32 nextExecutionState )
{
IOReturn status = kIOReturnError;
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;
}
boRequestBlock->boCBW.cbwLUN = GetLogicalUnitNumber( boRequestBlock->request ) & kCBWLUNMask; boRequestBlock->boCBW.cbwCDBLength = GetCommandDescriptorBlockSize( boRequestBlock->request ); GetCommandDescriptorBlock( boRequestBlock->request, &boRequestBlock->boCBW.cbwCDB );
boRequestBlock->currentState = nextExecutionState;
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,
kBulkOnlyCommandPhaseTimeoutValue, kBulkOnlyCommandPhaseTimeoutValue,
&boRequestBlock->boCompletion );
STATUS_LOG(( 5, "%s[%p]: BulkOnlySendCBWPacket returned %x", getName(), this, status ));
if ( status == kIOUSBPipeStalled )
{
STATUS_LOG(( 5, "%s[%p]: BulkOnlySendCBWPacket could not be queued, returned", getName(), this ));
boRequestBlock->currentState = kBulkOnlyCheckCBWBulkStall;
status = GetStatusEndpointStatus( GetBulkOutPipe(), &boRequestBlock->boGetStatusBuffer, &boRequestBlock->boCompletion );
}
Exit:
return status;
}
IOReturn
IOUSBMassStorageClass::BulkOnlyTransferData(
BulkOnlyRequestBlock * boRequestBlock,
UInt32 nextExecutionState )
{
IOReturn status = kIOReturnError;
boRequestBlock->currentState = nextExecutionState;
if (GetDataTransferDirection(boRequestBlock->request) == kSCSIDataTransfer_FromTargetToInitiator)
{
status = GetBulkInPipe()->Read(
GetDataBuffer( boRequestBlock->request ),
GetTimeoutDuration( boRequestBlock->request ), GetTimeoutDuration( boRequestBlock->request ),
GetRequestedDataTransferCount( boRequestBlock->request ),
&boRequestBlock->boCompletion );
}
else if (GetDataTransferDirection(boRequestBlock->request) == kSCSIDataTransfer_FromInitiatorToTarget)
{
status = GetBulkOutPipe()->Write(
GetDataBuffer( boRequestBlock->request ),
GetTimeoutDuration( boRequestBlock->request ), GetTimeoutDuration( boRequestBlock->request ),
GetRequestedDataTransferCount( boRequestBlock->request ),
&boRequestBlock->boCompletion );
}
STATUS_LOG(( 5, "%s[%p]: BulkOnlyTransferData returned %x", getName(), this, status ));
return status;
}
IOReturn
IOUSBMassStorageClass::BulkOnlyReceiveCSWPacket(
BulkOnlyRequestBlock * boRequestBlock,
UInt32 nextExecutionState )
{
IOReturn status;
require ( ( fBulkOnlyCSWMemoryDescriptor != NULL ), Exit );
boRequestBlock->boPhaseDesc = fBulkOnlyCSWMemoryDescriptor;
boRequestBlock->currentState = nextExecutionState;
status = GetBulkInPipe()->Read( boRequestBlock->boPhaseDesc,
GetTimeoutDuration( boRequestBlock->request ), GetTimeoutDuration( boRequestBlock->request ),
&boRequestBlock->boCompletion );
STATUS_LOG(( 5, "%s[%p]: BulkOnlyReceiveCSWPacket returned %x", getName(), this, status ));
Exit:
return status;
}
void
IOUSBMassStorageClass::BulkOnlyExecuteCommandCompletion(
BulkOnlyRequestBlock * boRequestBlock,
IOReturn resultingStatus,
UInt32 bufferSizeRemaining)
{
UNUSED( bufferSizeRemaining );
IOReturn status = kIOReturnError;
bool commandInProgress = false;
if ( ( boRequestBlock->request == NULL ) || ( fBulkOnlyCommandStructInUse == false ) )
{
STATUS_LOG(( 4, "%s[%p]: boRequestBlock->request is NULL, returned %x", getName(), this, resultingStatus ));
return;
}
if ( ( GetInterfaceReference() == NULL ) || ( fTerminating == true ) )
{
SCSITaskIdentifier request = boRequestBlock->request;
STATUS_LOG(( 4, "%s[%p]: Interface object is NULL, returned %x", getName(), this, resultingStatus ));
ReleaseBulkOnlyRequestBlock( boRequestBlock );
CompleteSCSICommand( request, status );
return;
}
if ( resultingStatus == kIOReturnNotResponding )
{
STATUS_LOG(( 5, "%s[%p]: BulkOnlyExecuteCommandCompletion previous command returned %x", getName(), this, resultingStatus ));
FinishDeviceRecovery ( resultingStatus );
commandInProgress = true;
goto Exit;
}
switch( boRequestBlock->currentState )
{
case kBulkOnlyCommandSent:
{
STATUS_LOG(( 5, "%s[%p]: kBulkOnlyCommandSent returned %x", getName(), this, resultingStatus ));
if(( resultingStatus == kIOReturnDeviceError )
|| ( resultingStatus == kIOReturnNotResponding )
|| ( resultingStatus == kIOUSBHighSpeedSplitError ))
{
status = StartDeviceRecovery();
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
break;
}
if ( resultingStatus == kIOUSBPipeStalled )
{
boRequestBlock->currentState = kBulkOnlyCheckCBWBulkStall;
status = GetStatusEndpointStatus( GetBulkOutPipe(), &boRequestBlock->boGetStatusBuffer, &boRequestBlock->boCompletion );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
break;
}
if ( resultingStatus != kIOReturnSuccess )
{
status = resultingStatus;
break;
}
if ( GetDataTransferDirection( boRequestBlock->request ) == kSCSIDataTransfer_NoDataTransfer )
{
status = BulkOnlyReceiveCSWPacket( boRequestBlock, kBulkOnlyStatusReceived );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
}
else
{
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 ));
if ( (boRequestBlock->boGetStatusBuffer[0] & 1) == 1 )
{
STATUS_LOG(( 5, "%s[%p]: IOUSBMassStorageClass::BulkOnlyExecuteCommandCompletion - will try to clear endpoint", getName(), this ));
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 ));
GetBulkOutPipe()->Abort();
SetRealizedDataTransferCount( boRequestBlock->request, 0 );
status = kIOReturnError;
}
}
break;
case kBulkOnlyClearCBWBulkStall:
{
STATUS_LOG(( 5, "%s[%p]: kBulkOnlyClearCBWBulkStall returned %x", getName(), this, resultingStatus ));
SetRealizedDataTransferCount( boRequestBlock->request, 0 );
status = kIOReturnError;
}
break;
case kBulkOnlyBulkIOComplete:
{
status = resultingStatus;
STATUS_LOG(( 5, "%s[%p]: kBulkOnlyBulkIOComplete returned %x", getName(), this, resultingStatus ));
if ( ( resultingStatus == kIOUSBPipeStalled ) || ( resultingStatus == kIOReturnSuccess ) )
{
SetRealizedDataTransferCount( boRequestBlock->request,
GetRequestedDataTransferCount( boRequestBlock->request ) - bufferSizeRemaining);
}
if ( resultingStatus == kIOReturnSuccess )
{
status = BulkOnlyReceiveCSWPacket( boRequestBlock, kBulkOnlyStatusReceived );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
}
else if ( resultingStatus == kIOReturnOverrun )
{
SetRealizedDataTransferCount( boRequestBlock->request,
GetRequestedDataTransferCount( boRequestBlock->request ) );
FinishDeviceRecovery ( kIOReturnError );
}
else
{
if(( resultingStatus == kIOReturnDeviceError )
|| ( resultingStatus == kIOReturnNotResponding )
|| ( resultingStatus == kIOUSBHighSpeedSplitError ) )
{
status = StartDeviceRecovery();
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
}
else if ( resultingStatus == kIOUSBTransactionTimeout )
{
status = BulkDeviceResetDevice( boRequestBlock, kBulkOnlyResetCompleted );
if( status == kIOReturnSuccess )
{
commandInProgress = true;
}
}
else
{
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:
{
STATUS_LOG(( 5, "%s[%p]: kBulkOnlyCheckBulkStall returned %x", getName(), this, resultingStatus ));
if ( (boRequestBlock->boGetStatusBuffer[0] & 1) == 1 )
{
boRequestBlock->currentState = kBulkOnlyClearBulkStall;
status = ClearFeatureEndpointStall( fPotentiallyStalledPipe, &boRequestBlock->boCompletion );
if ( status == kIOReturnSuccess )
{
fPotentiallyStalledPipe = NULL;
commandInProgress = true;
}
}
else
{
if ( fPotentiallyStalledPipe != NULL )
{
fPotentiallyStalledPipe->Abort();
}
status = BulkOnlyReceiveCSWPacket( boRequestBlock, kBulkOnlyStatusReceived );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
}
}
break;
case kBulkOnlyClearBulkStall:
{
STATUS_LOG(( 5, "%s[%p]: kBulkOnlyClearBulkStall returned %x", getName(), this, resultingStatus ));
status = BulkOnlyReceiveCSWPacket( boRequestBlock, kBulkOnlyStatusReceived );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
}
break;
case kBulkOnlyStatusReceived:
{
STATUS_LOG(( 5, "%s[%p]: kBulkOnlyStatusReceived returned %x", getName(), this, resultingStatus ));
if ( resultingStatus == kIOUSBPipeStalled )
{
boRequestBlock->currentState = kBulkOnlyCheckBulkStall;
status = GetStatusEndpointStatus( GetBulkInPipe(), &boRequestBlock->boGetStatusBuffer, &boRequestBlock->boCompletion );
if ( status == kIOReturnSuccess )
{
fPotentiallyStalledPipe = GetBulkInPipe();
commandInProgress = true;
}
}
else if ( resultingStatus != kIOReturnSuccess )
{
status = BulkOnlyReceiveCSWPacket( boRequestBlock, kBulkOnlyStatusReceived2ndTime );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
}
else if( boRequestBlock->boCSW.cswTag == boRequestBlock->boCBW.cbwTag)
{
switch( boRequestBlock->boCSW.cswStatus )
{
case kCSWCommandPassedError:
{
STATUS_LOG(( 6, "%s[%p]: kBulkOnlyStatusReceived kCSWCommandPassedError", getName(), this ));
status = kIOReturnSuccess;
}
break;
case kCSWCommandFailedError:
{
STATUS_LOG(( 4, "%s[%p]: kBulkOnlyStatusReceived kCSWCommandFailedError", getName(), this ));
status = kIOReturnError;
}
break;
case kCSWPhaseError:
{
STATUS_LOG(( 4, "%s[%p]: kBulkOnlyStatusReceived kCSWPhaseError", getName(), this ));
if ( fUseUSBResetNotBOReset )
{
FinishDeviceRecovery ( kIOReturnError );
status = kIOReturnSuccess;
}
else
{
status = BulkDeviceResetDevice( boRequestBlock, kBulkOnlyResetCompleted );
}
if( status == kIOReturnSuccess )
{
commandInProgress = true;
}
}
break;
default:
{
STATUS_LOG(( 4, "%s[%p]: kBulkOnlyStatusReceived default", getName(), this ));
status = kIOReturnError;
}
break;
}
}
else
{
STATUS_LOG(( 5, "%s[%p]: kBulkOnlyStatusReceived tag mismatch", getName(), this ));
status = kIOReturnError;
}
}
break;
case kBulkOnlyStatusReceived2ndTime:
{
STATUS_LOG(( 5, "%s[%p]: kBulkOnlyStatusReceived2ndTime returned %x", getName(), this, resultingStatus ));
if ( resultingStatus != kIOReturnSuccess)
{
if ( fUseUSBResetNotBOReset )
{
FinishDeviceRecovery ( kIOReturnError );
status = kIOReturnSuccess;
}
else
{
status = BulkDeviceResetDevice( boRequestBlock, kBulkOnlyResetCompleted );
}
}
else
{
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)
{
FinishDeviceRecovery( resultingStatus );
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 );
}
}