USBMassStorageClassCBI.cpp [plain text]
#include "IOUSBMassStorageClass.h"
#include "IOUSBMassStorageClassTimestamps.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
{
kCBIExecuteCommand = 1, kCBIExecuteCommandCompletion, kCBIBulkIOComplete, kCBIReadInterruptComplete,
kCBIGetStatusControlEndpointComplete,
kCBIClearControlEndpointComplete,
kCBIGetStatusBulkEndpointComplete,
kCBIClearBulkEndpointComplete
};
#pragma mark -
#pragma mark Protocol Services Methods
IOReturn
IOUSBMassStorageClass::AbortSCSICommandForCBIProtocol (
SCSITaskIdentifier abortTask )
{
UNUSED( abortTask );
return kIOReturnError;
}
IOReturn
IOUSBMassStorageClass::SendSCSICommandForCBIProtocol ( SCSITaskIdentifier request )
{
IOReturn status;
CBIRequestBlock * theCBIRequestBlock;
if ( fTerminating == true )
{
return kIOReturnDeviceError;
}
theCBIRequestBlock = GetCBIRequestBlock();
bzero ( theCBIRequestBlock, sizeof ( CBIRequestBlock ) );
fCBICommandRequestBlock.cbiPhaseDesc = fCBIMemoryDescriptor;
GetCommandDescriptorBlock ( request, &theCBIRequestBlock->cbiCDB );
theCBIRequestBlock->request = request;
theCBIRequestBlock->cbiCompletion.target = this;
theCBIRequestBlock->cbiCompletion.action = &this->CBIProtocolUSBCompletionAction;
theCBIRequestBlock->cbiCompletion.parameter = theCBIRequestBlock;
theCBIRequestBlock->currentState = kCBIExecuteCommand;
theCBIRequestBlock->cbiDevRequest.bmRequestType = USBmakebmRequestType ( kUSBOut, kUSBClass, kUSBInterface );
theCBIRequestBlock->cbiDevRequest.bRequest = 0;
theCBIRequestBlock->cbiDevRequest.wValue = 0;
theCBIRequestBlock->cbiDevRequest.wIndex = GetInterfaceReference()->GetInterfaceNumber();
theCBIRequestBlock->cbiDevRequest.wLength = 12; theCBIRequestBlock->cbiDevRequest.pData = &theCBIRequestBlock->cbiCDB;
status = GetInterfaceReference()->GetDevice()->DeviceRequest (
&theCBIRequestBlock->cbiDevRequest,
GetTimeoutDuration( theCBIRequestBlock->request ), GetTimeoutDuration( theCBIRequestBlock->request ), &theCBIRequestBlock->cbiCompletion );
STATUS_LOG ( ( 5, "%s[%p]: SendSCSICommandForCBIProtocol DeviceRequest returned %x", getName(), this, status ) );
if ( status != kIOReturnSuccess )
{
ReleaseCBIRequestBlock ( theCBIRequestBlock );
}
return status;
}
#pragma mark -
#pragma mark SendSCSICommand Helper methods
void
IOUSBMassStorageClass::CBIProtocolUSBCompletionAction (
void * target,
void * parameter,
IOReturn status,
UInt32 bufferSizeRemaining)
{
IOUSBMassStorageClass * theMSC;
CBIRequestBlock * cbiRequestBlock;
theMSC = ( IOUSBMassStorageClass * ) target;
cbiRequestBlock = ( CBIRequestBlock * ) parameter;
theMSC->CBIProtocolCommandCompletion ( cbiRequestBlock,
status,
bufferSizeRemaining );
}
IOReturn
IOUSBMassStorageClass::CBIProtocolTransferData (
CBIRequestBlock * cbiRequestBlock,
UInt32 nextExecutionState )
{
IOReturn status = kIOReturnError;
cbiRequestBlock->currentState = nextExecutionState;
if ( GetDataTransferDirection ( cbiRequestBlock->request ) == kSCSIDataTransfer_FromTargetToInitiator )
{
status = GetBulkInPipe()->Read (
GetDataBuffer ( cbiRequestBlock->request ),
GetTimeoutDuration ( cbiRequestBlock->request ), GetTimeoutDuration ( cbiRequestBlock->request ),
GetRequestedDataTransferCount ( cbiRequestBlock->request ),
&cbiRequestBlock->cbiCompletion );
}
else if ( GetDataTransferDirection ( cbiRequestBlock->request ) == kSCSIDataTransfer_FromInitiatorToTarget )
{
status = GetBulkOutPipe()->Write (
GetDataBuffer ( cbiRequestBlock->request ),
GetTimeoutDuration ( cbiRequestBlock->request ), GetTimeoutDuration ( cbiRequestBlock->request ),
GetRequestedDataTransferCount ( cbiRequestBlock->request ),
&cbiRequestBlock->cbiCompletion );
}
STATUS_LOG ( ( 5, "%s[%p]: CBIProtocolTransferData returned %x", getName(), this, status ) );
return status;
}
IOReturn
IOUSBMassStorageClass::CBIProtocolReadInterrupt (
CBIRequestBlock * cbiRequestBlock,
UInt32 nextExecutionState )
{
IOReturn status = kIOReturnError;
require ( ( cbiRequestBlock->cbiPhaseDesc != NULL ), Exit );
cbiRequestBlock->currentState = nextExecutionState;
status = GetInterruptPipe()->Read ( cbiRequestBlock->cbiPhaseDesc, &cbiRequestBlock->cbiCompletion);
STATUS_LOG ( ( 5, "%s[%p]: CBIProtocolReadInterrupt returned %x", getName(), this, status ) );
Exit:
return status;
}
IOReturn
IOUSBMassStorageClass::CBIGetStatusEndpointStatus(
IOUSBPipe * targetPipe,
CBIRequestBlock * cbiRequestBlock,
UInt32 nextExecutionState )
{
IOReturn status;
if( targetPipe == NULL )
{
status = kIOReturnError;
goto ErrorExit;
}
cbiRequestBlock->currentState = nextExecutionState;
status = GetStatusEndpointStatus ( targetPipe, &cbiRequestBlock->cbiGetStatusBuffer, &cbiRequestBlock->cbiCompletion );
STATUS_LOG ( ( 5, "%s[%p]: CBIGetStatusEndpointStatus returned %x", getName(), this, status ) );
ErrorExit:
return status;
}
IOReturn
IOUSBMassStorageClass::CBIClearFeatureEndpointStall(
IOUSBPipe * targetPipe,
CBIRequestBlock * cbiRequestBlock,
UInt32 nextExecutionState )
{
IOReturn status;
if( targetPipe == NULL )
{
status = kIOReturnError;
goto ErrorExit;
}
cbiRequestBlock->currentState = nextExecutionState;
status = ClearFeatureEndpointStall ( targetPipe, &cbiRequestBlock->cbiCompletion );
STATUS_LOG ( ( 5, "%s[%p]: CBIClearFeatureEndpointStall returned %x", getName(), this, status ) );
ErrorExit:
return status;
}
void
IOUSBMassStorageClass::CBIProtocolCommandCompletion(
CBIRequestBlock * cbiRequestBlock,
IOReturn resultingStatus,
UInt32 bufferSizeRemaining )
{
IOReturn status = kIOReturnError;
bool commandInProgress = false;
if ( ( cbiRequestBlock->request == NULL ) || ( fCBICommandStructInUse == false ) )
{
STATUS_LOG(( 4, "%s[%p]: cbiRequestBlock->request is NULL, returned %x", getName(), this, resultingStatus ));
return;
}
if ( ( GetInterfaceReference() == NULL ) || ( fTerminating == true ) )
{
SCSITaskIdentifier request = cbiRequestBlock->request;
ReleaseCBIRequestBlock( cbiRequestBlock );
CompleteSCSICommand( request, status );
return;
}
if ( ( resultingStatus == kIOReturnNotResponding ) || ( resultingStatus == kIOReturnAborted ) )
{
STATUS_LOG(( 5, "%s[%p]: CBIProtocolCommandCompletion previous command returned %x", getName(), this, resultingStatus ));
ResetDeviceNow ( false );
commandInProgress = true;
goto Exit;
}
RecordUSBTimeStamp ( UMC_TRACE ( kCBICompletion ), ( uintptr_t ) this, resultingStatus,
( unsigned int ) cbiRequestBlock->currentState, ( uintptr_t ) cbiRequestBlock->request );
switch ( cbiRequestBlock->currentState )
{
case kCBIExecuteCommand: {
STATUS_LOG(( 5, "%s[%p]: kCBIExecuteCommand status %x", getName(), this, resultingStatus ));
#if defined (__i386__)
if ( resultingStatus == kIOUSBPipeStalled )
{
status = CBIClearFeatureEndpointStall ( GetControlPipe(), cbiRequestBlock, kCBIClearBulkEndpointComplete );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
break;
}
#endif
if ( resultingStatus != kIOReturnSuccess )
{
status = CBIGetStatusEndpointStatus ( GetControlPipe(), cbiRequestBlock, kCBIGetStatusControlEndpointComplete );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
STATUS_LOG ( ( 4, "%s[%p]: kCBIExecuteCommand GetStatusEndpointStatus status %x", getName(), this, status ) );
}
else
{
if ( GetDataTransferDirection ( cbiRequestBlock->request ) == kSCSIDataTransfer_NoDataTransfer )
{
status = kIOReturnSuccess;
STATUS_LOG ( ( 5, "%s[%p]: kCBIExecuteCommand no data to transfer status %x", getName(), this, status ) );
break;
}
status = CBIProtocolTransferData ( cbiRequestBlock, kCBIBulkIOComplete );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
STATUS_LOG ( ( 5, "%s[%p]: kCBIExecuteCommand CBIProtocolTransferData status %x", getName(), this, status ) );
}
}
break;
case kCBIBulkIOComplete:
{
STATUS_LOG ( ( 5, "%s[%p]: kCBIBulkIOComplete status %x", getName(), this, resultingStatus ) );
if ( resultingStatus == kIOReturnOverrun )
{
resultingStatus = kIOReturnSuccess;
SetRealizedDataTransferCount ( cbiRequestBlock->request, GetRequestedDataTransferCount( cbiRequestBlock->request ) );
if ( GetDataTransferDirection ( cbiRequestBlock->request ) == kSCSIDataTransfer_FromTargetToInitiator )
{
GetBulkInPipe()->Reset();
}
else if ( GetDataTransferDirection ( cbiRequestBlock->request ) == kSCSIDataTransfer_FromInitiatorToTarget )
{
GetBulkOutPipe()->Reset();
}
}
else
{
SetRealizedDataTransferCount ( cbiRequestBlock->request, GetRequestedDataTransferCount( cbiRequestBlock->request ) - bufferSizeRemaining );
}
#if defined (__i386__)
if ( resultingStatus == kIOUSBPipeStalled )
{
IOUSBPipe * thePipe = NULL;
if ( GetDataTransferDirection ( cbiRequestBlock->request ) == kSCSIDataTransfer_FromTargetToInitiator )
{
thePipe = GetBulkInPipe();
}
else if ( GetDataTransferDirection ( cbiRequestBlock->request ) == kSCSIDataTransfer_FromInitiatorToTarget )
{
thePipe = GetBulkOutPipe();
}
if ( thePipe != NULL )
{
status = CBIClearFeatureEndpointStall ( thePipe, cbiRequestBlock, kCBIClearBulkEndpointComplete );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
break;
}
}
#endif
if ( resultingStatus != kIOReturnSuccess )
{
IOUSBPipe * thePipe = NULL;
if ( GetDataTransferDirection( cbiRequestBlock->request ) == kSCSIDataTransfer_FromTargetToInitiator )
{
thePipe = GetBulkInPipe();
}
else if ( GetDataTransferDirection( cbiRequestBlock->request ) == kSCSIDataTransfer_FromInitiatorToTarget )
{
thePipe = GetBulkOutPipe();
}
else
{
thePipe = GetControlPipe();
}
status = CBIGetStatusEndpointStatus ( thePipe, cbiRequestBlock, kCBIGetStatusBulkEndpointComplete );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
STATUS_LOG ( ( 5, "%s[%p]: kCBIBulkIOComplete GetStatusEndpointStatus status %x", getName(), this, status ) );
}
else
{
if ( ( GetInterruptPipe() != NULL ) && ( GetDataTransferDirection ( cbiRequestBlock->request ) == kSCSIDataTransfer_FromInitiatorToTarget )
&& ( ( GetInterfaceSubclass() == kUSBStorageSFF8070iSubclass ) || ( GetInterfaceSubclass() == kUSBStorageUFISubclass ) ) )
{
status = CBIProtocolReadInterrupt ( cbiRequestBlock, kCBIReadInterruptComplete );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
STATUS_LOG(( 5, "%s[%p]: kCBIBulkIOComplete CBIProtocolReadInterrupt status %x", getName(), this, status ));
}
else
{
status = kIOReturnSuccess;
}
}
}
break;
case kCBIReadInterruptComplete:
{
STATUS_LOG(( 5, "%s[%p]: kCBIReadInterruptComplete status %x", getName(), this, resultingStatus ));
if ( ( resultingStatus == kIOReturnSuccess ) && ( ( GetInterfaceSubclass() == kUSBStorageSFF8070iSubclass ) || ( GetInterfaceSubclass() == kUSBStorageUFISubclass ) ) )
{
if ( GetInterfaceSubclass() == kUSBStorageUFISubclass )
{
if ( ( cbiRequestBlock->cbiGetStatusBuffer[0] == 0x00 ) && ( cbiRequestBlock->cbiGetStatusBuffer[1] == 0x00 ) )
{
status = kIOReturnSuccess;
}
else
{
status = kIOReturnError;
}
}
else {
if ( ( cbiRequestBlock->cbiGetStatusBuffer[0] == 0x00 ) &&
( ( cbiRequestBlock->cbiGetStatusBuffer[1] & 0x3 ) != 0 ) )
{
status = kIOReturnError;
}
else
{
status = kIOReturnSuccess;
}
}
}
else
{
status = kIOReturnError;
}
STATUS_LOG ( ( 5, "%s[%p]: kCBIReadInterruptComplete ending status %x", getName(), this, status ) );
}
break;
case kCBIGetStatusControlEndpointComplete:
{
STATUS_LOG ( ( 5, "%s[%p]: kCBIGetStatusControlEndpointComplete status %x", getName(), this, resultingStatus ) );
if ( resultingStatus == kIOReturnSuccess )
{
if ( ( cbiRequestBlock->cbiGetStatusBuffer[0] & 1 ) == 1 )
{
status = CBIClearFeatureEndpointStall ( GetControlPipe(), cbiRequestBlock, kCBIClearControlEndpointComplete );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
STATUS_LOG ( ( 5, "%s[%p]: kCBIGetStatusControlEndpointComplete CBIClearFeatureEndpointStall status %x", getName(), this, status ) );
}
else
{
if ( GetDataTransferDirection ( cbiRequestBlock->request ) == kSCSIDataTransfer_NoDataTransfer )
{
SetRealizedDataTransferCount ( cbiRequestBlock->request, 0 );
status = kIOReturnError;
}
else
{
IOUSBPipe * thePipe = NULL;
if ( GetDataTransferDirection ( cbiRequestBlock->request ) == kSCSIDataTransfer_FromTargetToInitiator )
{
thePipe = GetBulkInPipe();
}
else if ( GetDataTransferDirection ( cbiRequestBlock->request ) == kSCSIDataTransfer_FromInitiatorToTarget )
{
thePipe = GetBulkOutPipe();
}
else
{
thePipe = GetControlPipe();
}
status = CBIGetStatusEndpointStatus ( GetControlPipe(), cbiRequestBlock, kCBIGetStatusBulkEndpointComplete );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
STATUS_LOG ( ( 5, "%s[%p]: kCBIGetStatusControlEndpointComplete CBIGetStatusEndpointStatus status %x", getName(), this, status ) );
}
}
}
else
{
status = CBIClearFeatureEndpointStall ( GetControlPipe(), cbiRequestBlock, kCBIClearControlEndpointComplete );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
STATUS_LOG(( 5, "%s[%p]: kCBIGetStatusControlEndpointComplete CBIClearFeatureEndpointStall status %x", getName(), this, status ));
}
}
break;
case kCBIClearControlEndpointComplete:
{
STATUS_LOG ( ( 5, "%s[%p]: kCBIClearControlEndpointComplete status %x", getName(), this, resultingStatus ) );
if (resultingStatus == kIOReturnSuccess)
{
if ( GetDataTransferDirection ( cbiRequestBlock->request ) == kSCSIDataTransfer_NoDataTransfer )
{
SetRealizedDataTransferCount ( cbiRequestBlock->request, 0 );
status = kIOReturnError;
}
else
{
IOUSBPipe * thePipe = NULL;
if ( GetDataTransferDirection ( cbiRequestBlock->request ) == kSCSIDataTransfer_FromTargetToInitiator )
{
thePipe = GetBulkInPipe();
}
else if ( GetDataTransferDirection ( cbiRequestBlock->request ) == kSCSIDataTransfer_FromInitiatorToTarget )
{
thePipe = GetBulkOutPipe();
}
else
{
thePipe = GetControlPipe();
}
status = CBIGetStatusEndpointStatus ( thePipe, cbiRequestBlock, kCBIGetStatusBulkEndpointComplete );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
STATUS_LOG ( ( 5, "%s[%p]: kCBIClearControlEndpointComplete CBIGetStatusEndpointStatus status %x", getName(), this, status ) );
}
}
else
{
status = resultingStatus;
}
}
break;
case kCBIGetStatusBulkEndpointComplete:
{
STATUS_LOG ( ( 5, "%s[%p]: kCBIGetStatusBulkEndpointComplete status %x", getName(), this, resultingStatus ) );
if ( resultingStatus == kIOReturnSuccess )
{
if ( ( cbiRequestBlock->cbiGetStatusBuffer[0] & 1 ) == 1 )
{
IOUSBPipe * thePipe = NULL;
if ( GetDataTransferDirection( cbiRequestBlock->request ) == kSCSIDataTransfer_FromTargetToInitiator )
{
thePipe = GetBulkInPipe();
}
else if ( GetDataTransferDirection( cbiRequestBlock->request ) == kSCSIDataTransfer_FromInitiatorToTarget )
{
thePipe = GetBulkOutPipe();
}
else
{
thePipe = GetControlPipe();
}
status = CBIClearFeatureEndpointStall ( thePipe, cbiRequestBlock, kCBIClearBulkEndpointComplete );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
STATUS_LOG(( 5, "%s[%p]: kCBIGetStatusBulkEndpointComplete CBIClearFeatureEndpointStall status %x", getName(), this, status ));
}
else
{
SetRealizedDataTransferCount( cbiRequestBlock->request, 0 );
status = kIOReturnError;
}
}
else
{
IOUSBPipe * thePipe = NULL;
if ( GetDataTransferDirection( cbiRequestBlock->request ) == kSCSIDataTransfer_FromTargetToInitiator )
{
thePipe = GetBulkInPipe();
}
else if ( GetDataTransferDirection( cbiRequestBlock->request ) == kSCSIDataTransfer_FromInitiatorToTarget )
{
thePipe = GetBulkOutPipe();
}
else
{
thePipe = GetControlPipe();
}
status = CBIClearFeatureEndpointStall ( thePipe, cbiRequestBlock, kCBIClearBulkEndpointComplete );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
STATUS_LOG(( 5, "%s[%p]: kCBIGetStatusBulkEndpointComplete CBIClearFeatureEndpointStall status %x", getName(), this, status ));
}
}
break;
case kCBIClearBulkEndpointComplete:
{
STATUS_LOG ( ( 5, "%s[%p]: kCBIClearBulkEndpointComplete status %x", getName(), this, resultingStatus ) );
SetRealizedDataTransferCount ( cbiRequestBlock->request, 0 );
status = kIOReturnError;
}
break;
default:
{
STATUS_LOG ( ( 5, "%s[%p]: default case status %x", getName(), this, resultingStatus ) );
SetRealizedDataTransferCount ( cbiRequestBlock->request, 0 );
status = kIOReturnError;
}
break;
}
Exit:
if ( commandInProgress == false )
{
SCSITaskIdentifier request = cbiRequestBlock->request;
ReleaseCBIRequestBlock ( cbiRequestBlock );
CompleteSCSICommand ( request, status );
}
}