USBMassStorageClassCBI.cpp [plain text]
#include <IOKit/usb/IOUSBMassStorageClass.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 ( GetInterfaceReference() == NULL )
{
return kIOReturnDeviceError;
}
theCBIRequestBlock = GetCBIRequestBlock();
bzero( theCBIRequestBlock, sizeof(CBIRequestBlock) );
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: SendSCSICommandForCBIProtocol DeviceRequest returned %d",
getName(),
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: CBIProtocolTransferData returned %d",
getName(),
status));
return status;
}
IOReturn
IOUSBMassStorageClass::CBIProtocolReadInterrupt(
CBIRequestBlock * cbiRequestBlock,
UInt32 nextExecutionState )
{
IOReturn status;
cbiRequestBlock->cbiPhaseDesc = IOMemoryDescriptor::withAddress(
&cbiRequestBlock->cbiGetStatusBuffer,
kUSBStorageAutoStatusSize,
kIODirectionIn);
if ( cbiRequestBlock->cbiPhaseDesc == NULL )
{
return kIOReturnNoResources;
}
cbiRequestBlock->currentState = nextExecutionState;
status = GetInterruptPipe()->Read( cbiRequestBlock->cbiPhaseDesc, &cbiRequestBlock->cbiCompletion);
STATUS_LOG((5, "%s: CBIProtocolReadInterrupt returned %d",
getName(),
status));
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: CBIGetStatusEndpointStatus returned %d",
getName(),
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: CBIClearFeatureEndpointStall returned %d",
getName(),
status));
ErrorExit:
return status;
}
void
IOUSBMassStorageClass::CBIProtocolCommandCompletion(
CBIRequestBlock * cbiRequestBlock,
IOReturn resultingStatus,
UInt32 bufferSizeRemaining )
{
IOReturn status = kIOReturnError;
bool commandInProgress = false;
if( cbiRequestBlock->request == NULL )
{
STATUS_LOG((4, "%s: cbiRequestBlock->request is NULL, returned %d", getName(), resultingStatus));
return;
}
if ( GetInterfaceReference() == NULL )
{
SCSITaskIdentifier request = cbiRequestBlock->request;
ReleaseCBIRequestBlock( cbiRequestBlock );
CompleteSCSICommand( request, status );
return;
}
switch( cbiRequestBlock->currentState )
{
case kCBIExecuteCommand: {
STATUS_LOG((5, "%s: kCBIExecuteCommand status %d", getName(), resultingStatus));
if (resultingStatus != kIOReturnSuccess)
{
status = CBIGetStatusEndpointStatus( GetControlPipe(), cbiRequestBlock, kCBIGetStatusControlEndpointComplete);
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
STATUS_LOG((4, "%s: kCBIExecuteCommand GetStatusEndpointStatus status %d", getName(), status));
}
else
{
if ( GetDataTransferDirection( cbiRequestBlock->request ) == kSCSIDataTransfer_NoDataTransfer )
{
status = kIOReturnSuccess;
STATUS_LOG((5, "%s: kCBIExecuteCommand no data to transfer status %d", getName(), status));
break;
}
status = CBIProtocolTransferData( cbiRequestBlock, kCBIBulkIOComplete );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
STATUS_LOG((5, "%s: kCBIExecuteCommand CBIProtocolTransferData status %d", getName(), status));
}
}
break;
case kCBIBulkIOComplete:
{
STATUS_LOG((5, "%s: kCBIBulkIOComplete status %x", getName(), 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 (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: kCBIBulkIOComplete GetStatusEndpointStatus status %d", getName(), 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: kCBIBulkIOComplete CBIProtocolReadInterrupt status %d", getName(), status));
}
else
{
status = kIOReturnSuccess;
}
}
}
break;
case kCBIReadInterruptComplete:
{
STATUS_LOG((5, "%s: kCBIReadInterruptComplete status %d", getName(), resultingStatus));
cbiRequestBlock->cbiPhaseDesc->release();
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: kCBIReadInterruptComplete ending status %d", getName(), status));
}
break;
case kCBIGetStatusControlEndpointComplete:
{
STATUS_LOG((5, "%s: kCBIGetStatusControlEndpointComplete status %d", getName(), resultingStatus));
if ( resultingStatus == kIOReturnSuccess )
{
if ((cbiRequestBlock->cbiGetStatusBuffer[0] & 1) == 1 )
{
status = CBIClearFeatureEndpointStall( GetControlPipe(), cbiRequestBlock, kCBIClearControlEndpointComplete );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
STATUS_LOG((5, "%s: kCBIGetStatusControlEndpointComplete CBIClearFeatureEndpointStall status %d", getName(), 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: kCBIGetStatusControlEndpointComplete CBIGetStatusEndpointStatus status %d", getName(), status));
}
}
}
else
{
status = CBIClearFeatureEndpointStall( GetControlPipe(), cbiRequestBlock, kCBIClearControlEndpointComplete );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
STATUS_LOG((5, "%s: kCBIGetStatusControlEndpointComplete CBIClearFeatureEndpointStall status %d", getName(), status));
}
}
break;
case kCBIClearControlEndpointComplete:
{
STATUS_LOG((5, "%s: kCBIClearControlEndpointComplete status %d", getName(), 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: kCBIClearControlEndpointComplete CBIGetStatusEndpointStatus status %d", getName(), status));
}
}
else
{
status = resultingStatus;
}
}
break;
case kCBIGetStatusBulkEndpointComplete:
{
STATUS_LOG((5, "%s: kCBIGetStatusBulkEndpointComplete status %d", getName(), 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: kCBIGetStatusBulkEndpointComplete CBIClearFeatureEndpointStall status %d", getName(), 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: kCBIGetStatusBulkEndpointComplete CBIClearFeatureEndpointStall status %d", getName(), status));
}
}
break;
case kCBIClearBulkEndpointComplete:
{
STATUS_LOG((5, "%s: kCBIClearBulkEndpointComplete status %d", getName(), resultingStatus));
SetRealizedDataTransferCount( cbiRequestBlock->request, 0 );
status = kIOReturnError;
}
break;
default:
{
STATUS_LOG((5, "%s: default case status %d", getName(), resultingStatus));
SetRealizedDataTransferCount( cbiRequestBlock->request, 0 );
status = kIOReturnError;
}
break;
}
if ( commandInProgress == false )
{
SCSITaskIdentifier request = cbiRequestBlock->request;
ReleaseCBIRequestBlock( cbiRequestBlock );
CompleteSCSICommand( request, status );
}
}