USBMassStorageClassBulkOnly.cpp [plain text]
#include <IOKit/usb/IOUSBMassStorageClass.h>
#if (USB_MASS_STORAGE_DEBUG == 1)
#define STATUS_LOG(x) IOLog x
#else
#define STATUS_LOG(x)
#endif
enum
{
kBulkOnlyCommandSent = 1,
kBulkOnlyBulkIOComplete,
kBulkOnlyCheckBulkStall,
kBulkOnlyClearBulkStall,
kBulkOnlyStatusReceived,
kBulkOnlyStatusReceived2ndTime,
kBulkOnlyResetCompleted,
kBulkOnlyClearBulkInCompleted,
kBulkOnlyClearBulkOutCompleted
};
#pragma mark -
#pragma mark Protocol Services Methods
#pragma mark -
IOReturn IOUSBMassStorageClass::AbortSCSICommandForBulkOnlyProtocol(
SCSITaskIdentifier 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(("%s: SendSCSICommandForBulkOnlyProtocol send CBW \n", getName()));
status = BulkOnlySendCBWPacket( theBulkOnlyRB, kBulkOnlyCommandSent );
STATUS_LOG(("%s: SendSCSICommandForBulkOnlyProtocol send CBW returned %d\n",
getName(),
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 ( GetInterfaceReference() == NULL )
{
return kIOReturnDeviceError;
}
bzero( &fUSBDeviceRequest, sizeof(IOUSBDevRequest));
fUSBDeviceRequest.bmRequestType = USBmakebmRequestType(kUSBNone, kUSBClass, kUSBInterface);
fUSBDeviceRequest.bRequest = 0xFF;
fUSBDeviceRequest.wValue = 0;
fUSBDeviceRequest.wIndex = 0;
fUSBDeviceRequest.wLength = 0;
fUSBDeviceRequest.pData = NULL;
boRequestBlock->currentState = nextExecutionState;
status = GetInterfaceReference()->DeviceRequest( &fUSBDeviceRequest, &boRequestBlock->boCompletion );
STATUS_LOG(("%s: BulkDeviceResetDevice returned %d\n",
getName(),
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;
boRequestBlock->boPhaseDesc = IOMemoryDescriptor::withAddress(
&boRequestBlock->boCBW,
kByteCountOfCBW,
kIODirectionOut);
if ( boRequestBlock->boPhaseDesc == NULL )
{
return kIOReturnNoResources;
}
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(("%s: BulkOnlySendCBWPacket Bulk Out is NULL\n", getName() ));
}
STATUS_LOG(("%s: BulkOnlySendCBWPacket sent\n", getName() ));
status = GetBulkOutPipe()->Write( boRequestBlock->boPhaseDesc,
GetTimeoutDuration( boRequestBlock->request ), GetTimeoutDuration( boRequestBlock->request ),
&boRequestBlock->boCompletion );
STATUS_LOG(("%s: BulkOnlySendCBWPacket returned %d\n",
getName(),
status));
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(("%s: BulkOnlyTransferData returned %d\n",
getName(),
status));
return status;
}
IOReturn
IOUSBMassStorageClass::BulkOnlyReceiveCSWPacket(
BulkOnlyRequestBlock * boRequestBlock,
UInt32 nextExecutionState )
{
IOReturn status;
boRequestBlock->boPhaseDesc = IOMemoryDescriptor::withAddress(&boRequestBlock->boCSW,
kByteCountOfCSW,
kIODirectionIn);
if ( boRequestBlock->boPhaseDesc == NULL )
{
return kIOReturnNoResources;
}
boRequestBlock->currentState = nextExecutionState;
status = GetBulkInPipe()->Read( boRequestBlock->boPhaseDesc,
GetTimeoutDuration( boRequestBlock->request ), GetTimeoutDuration( boRequestBlock->request ),
&boRequestBlock->boCompletion );
STATUS_LOG(("%s: BulkOnlyReceiveCSWPacket returned %d\n",
getName(),
status));
return status;
}
void
IOUSBMassStorageClass::BulkOnlyExecuteCommandCompletion(
BulkOnlyRequestBlock * boRequestBlock,
IOReturn resultingStatus,
UInt32 bufferSizeRemaining)
{
IOReturn status = kIOReturnError;
bool commandInProgress = false;
if ( boRequestBlock->request == NULL )
{
STATUS_LOG(("%s: boRequestBlock->request is NULL, returned %d\n", getName(), resultingStatus));
return;
}
if ( GetInterfaceReference() == NULL )
{
SCSITaskIdentifier request = boRequestBlock->request;
STATUS_LOG(("%s: Interface object is NULL, returned %d\n", getName(), resultingStatus));
ReleaseBulkOnlyRequestBlock( boRequestBlock );
CompleteSCSICommand( request, status );
return;
}
switch( boRequestBlock->currentState )
{
case kBulkOnlyCommandSent:
{
STATUS_LOG(("%s: kBulkOnlyCommandSent returned %d\n", getName(), resultingStatus));
boRequestBlock->boPhaseDesc->release();
if(( resultingStatus == kIOReturnDeviceError )
|| ( resultingStatus == kIOReturnNotResponding ))
{
status = StartDeviceRecovery();
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
break;
}
if ( GetDataTransferDirection( boRequestBlock->request ) == kSCSIDataTransfer_NoDataTransfer )
{
if ( resultingStatus != kIOReturnSuccess)
{
status = resultingStatus;
}
else
{
status = BulkOnlyReceiveCSWPacket( boRequestBlock, kBulkOnlyStatusReceived );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
}
break;
}
if ( resultingStatus != kIOReturnSuccess )
{
status = resultingStatus;
break;
}
status = BulkOnlyTransferData( boRequestBlock, kBulkOnlyBulkIOComplete );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
}
break;
case kBulkOnlyBulkIOComplete:
{
status = resultingStatus;
STATUS_LOG(("%s: kBulkOnlyBulkIOComplete returned %d\n", getName(), resultingStatus));
if ( resultingStatus == kIOReturnSuccess)
{
status = BulkOnlyReceiveCSWPacket( boRequestBlock, kBulkOnlyStatusReceived );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
}
else
{
IOUSBPipe * thePipe = NULL;
if ( GetDataTransferDirection( boRequestBlock->request ) == kSCSIDataTransfer_FromTargetToInitiator )
{
thePipe = GetBulkInPipe();
}
else if ( GetDataTransferDirection( boRequestBlock->request ) == kSCSIDataTransfer_FromInitiatorToTarget )
{
thePipe = GetBulkOutPipe();
}
else
{
thePipe = GetControlPipe();
}
boRequestBlock->currentState = kBulkOnlyCheckBulkStall;
status = GetStatusEndpointStatus( thePipe, &boRequestBlock->boGetStatusBuffer, &boRequestBlock->boCompletion );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
}
}
break;
case kBulkOnlyCheckBulkStall:
{
STATUS_LOG(("%s: kBulkOnlyCheckBulkStall returned %d\n", getName(), resultingStatus));
if ( (boRequestBlock->boGetStatusBuffer[0] & 1) == 1 )
{
IOUSBPipe * thePipe = NULL;
if ( GetDataTransferDirection( boRequestBlock->request ) == kSCSIDataTransfer_FromTargetToInitiator )
{
thePipe = GetBulkInPipe();
}
else if ( GetDataTransferDirection( boRequestBlock->request ) == kSCSIDataTransfer_FromInitiatorToTarget )
{
thePipe = GetBulkOutPipe();
}
else
{
thePipe = GetControlPipe();
}
boRequestBlock->currentState = kBulkOnlyClearBulkStall;
status = ClearFeatureEndpointStall( thePipe, &boRequestBlock->boCompletion );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
}
else
{
status = BulkOnlyReceiveCSWPacket( boRequestBlock, kBulkOnlyStatusReceived );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
}
}
break;
case kBulkOnlyClearBulkStall:
{
STATUS_LOG(("%s: kBulkOnlyClearBulkStall returned %d\n", getName(), resultingStatus));
status = BulkOnlyReceiveCSWPacket( boRequestBlock, kBulkOnlyStatusReceived );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
}
break;
case kBulkOnlyStatusReceived:
{
STATUS_LOG(("%s: kBulkOnlyStatusReceived returned %d\n", getName(), resultingStatus));
boRequestBlock->boPhaseDesc->release();
if ( resultingStatus == kIOUSBPipeStalled)
{
boRequestBlock->currentState = kBulkOnlyCheckBulkStall;
status = GetStatusEndpointStatus( GetBulkInPipe(), &boRequestBlock->boGetStatusBuffer, &boRequestBlock->boCompletion );
if ( status == kIOReturnSuccess )
{
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:
{
if( boRequestBlock->boCSW.cswDataResidue != 0 )
{
STATUS_LOG(("%s: kBulkOnlyStatusReceived data residue error %d\n", getName(),
USBToHostLong( boRequestBlock->boCSW.cswDataResidue )));
SetRealizedDataTransferCount( boRequestBlock->request,
GetRequestedDataTransferCount( boRequestBlock->request ) - USBToHostLong( boRequestBlock->boCSW.cswDataResidue ));
status = kIOReturnSuccess;
}
else if( SetRealizedDataTransferCount( boRequestBlock->request, GetRequestedDataTransferCount( boRequestBlock->request )) )
{
status = kIOReturnSuccess;
}
else
{
status = kIOReturnError;
}
}
break;
case kCSWCommandFailedError:
{
status = kIOReturnError;
}
break;
case kCSWPhaseError:
{
status = BulkDeviceResetDevice( boRequestBlock, kBulkOnlyResetCompleted );
if( status == kIOReturnSuccess )
{
commandInProgress = true;
}
}
break;
default:
{
status = kIOReturnError;
}
break;
}
}
else
{
status = kIOReturnError;
}
}
break;
case kBulkOnlyStatusReceived2ndTime:
{
STATUS_LOG(("%s: kBulkOnlyStatusReceived2ndTime returned %d\n", getName(), resultingStatus));
if ( resultingStatus != kIOReturnSuccess)
{
status = BulkDeviceResetDevice( boRequestBlock, kBulkOnlyResetCompleted );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
}
else
{
status = kIOReturnError;
}
}
break;
case kBulkOnlyResetCompleted:
{
STATUS_LOG(("%s: kBulkOnlyResetCompleted returned %d\n", getName(), resultingStatus));
boRequestBlock->currentState = kBulkOnlyClearBulkInCompleted;
status = ClearFeatureEndpointStall( GetBulkInPipe(), &boRequestBlock->boCompletion );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
}
break;
case kBulkOnlyClearBulkInCompleted:
{
STATUS_LOG(("%s: kBulkOnlyClearBulkInCompleted returned %d\n", getName(), resultingStatus));
boRequestBlock->currentState = kBulkOnlyClearBulkOutCompleted;
status = ClearFeatureEndpointStall( GetBulkOutPipe(), &boRequestBlock->boCompletion );
if ( status == kIOReturnSuccess )
{
commandInProgress = true;
}
}
break;
case kBulkOnlyClearBulkOutCompleted:
{
STATUS_LOG(("%s: kBulkOnlyClearBulkOutCompleted returned %d\n", getName(), resultingStatus));
SetRealizedDataTransferCount( boRequestBlock->request, 0 );
status = kIOReturnError;
}
break;
default:
{
SetRealizedDataTransferCount( boRequestBlock->request, 0 );
status = kIOReturnError;
}
break;
}
if ( commandInProgress == false )
{
SCSITaskIdentifier request = boRequestBlock->request;
ReleaseBulkOnlyRequestBlock( boRequestBlock );
CompleteSCSICommand( request, status );
}
}