/* * Copyright (c) 1998-2002 Apple Computer, 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 "Debugging.h" //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // Macros //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // Macros for printing debugging information #if (USB_MASS_STORAGE_DEBUG == 1) // Override the debug level for USBLog to make sure our logs make it out and then import // the logging header. #define DEBUG_LEVEL 1 #include #define STATUS_LOG(x) USBLog x #else #define STATUS_LOG(x) #endif // Bulk Only State Machine States enum { kBulkOnlyCommandSent = 1, kBulkOnlyBulkIOComplete, kBulkOnlyCheckBulkStall, kBulkOnlyClearBulkStall, 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: SendSCSICommandForBulkOnlyProtocol send CBW", getName())); status = BulkOnlySendCBWPacket( theBulkOnlyRB, kBulkOnlyCommandSent ); STATUS_LOG((5, "%s: SendSCSICommandForBulkOnlyProtocol send CBW returned %x", getName(), 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 ( GetInterfaceReference() == NULL ) { // 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: BulkDeviceResetDevice returned %x", getName(), 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; // Allocate the memory descriptor needed to send the CBW out boRequestBlock->boPhaseDesc = IOMemoryDescriptor::withAddress( &boRequestBlock->boCBW, kByteCountOfCBW, kIODirectionOut); if ( boRequestBlock->boPhaseDesc == NULL ) { // The memory descriptor could not be allocated and so the command // can not be sent to the device, return an error. 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; } // 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 ); // 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: BulkOnlySendCBWPacket Bulk Out is NULL", getName() )); } STATUS_LOG((6, "%s: BulkOnlySendCBWPacket sent", getName() )); status = GetBulkOutPipe()->Write( boRequestBlock->boPhaseDesc, GetTimeoutDuration( boRequestBlock->request ), // Use the client's timeout for both GetTimeoutDuration( boRequestBlock->request ), &boRequestBlock->boCompletion ); STATUS_LOG((5, "%s: BulkOnlySendCBWPacket returned %x", getName(), status)); 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: BulkOnlyTransferData returned %x", getName(), status)); return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ BulkOnlyReceiveCSWPacket - Prepare the Command Status Wrapper packet // for Bulk Only Protocol // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // IOReturn IOUSBMassStorageClass::BulkOnlyReceiveCSWPacket( BulkOnlyRequestBlock * boRequestBlock, UInt32 nextExecutionState ) { IOReturn status; // Allocate the memory descriptor needed to send the CBW out boRequestBlock->boPhaseDesc = IOMemoryDescriptor::withAddress(&boRequestBlock->boCSW, kByteCountOfCSW, kIODirectionIn); if ( boRequestBlock->boPhaseDesc == NULL ) { // The memory descriptor could not be allocated and so the command // can not be sent to the device, return an error. return kIOReturnNoResources; } // 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: BulkOnlyReceiveCSWPacket returned %x", getName(), status)); return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ BulkOnlyExecuteCommandCompletion [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOUSBMassStorageClass::BulkOnlyExecuteCommandCompletion( BulkOnlyRequestBlock * boRequestBlock, IOReturn resultingStatus, UInt32 bufferSizeRemaining) { UNUSED( bufferSizeRemaining ); IOReturn status = kIOReturnError; bool commandInProgress = false; if ( boRequestBlock->request == NULL ) { // The request field is NULL, this appears to // be a double callback, do nothing. STATUS_LOG((4, "%s: boRequestBlock->request is NULL, returned %x", getName(), resultingStatus)); return; } if ( GetInterfaceReference() == NULL ) { // 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: Interface object is NULL, returned %x", getName(), resultingStatus)); ReleaseBulkOnlyRequestBlock( boRequestBlock ); CompleteSCSICommand( request, status ); return; } switch( boRequestBlock->currentState ) { case kBulkOnlyCommandSent: { STATUS_LOG((5, "%s: kBulkOnlyCommandSent returned %x", getName(), resultingStatus)); // Release the memory descriptor for the CBW boRequestBlock->boPhaseDesc->release(); if(( resultingStatus == kIOReturnDeviceError ) || ( resultingStatus == kIOReturnNotResponding )) { // An error occurred, probably a timeout error, // and the command was not successfully sent to the device. status = StartDeviceRecovery(); 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 ) { if ( resultingStatus != kIOReturnSuccess) { status = resultingStatus; } else { // Bulk transfer is done, get the Command Status Wrapper from the device status = BulkOnlyReceiveCSWPacket( boRequestBlock, kBulkOnlyStatusReceived ); if ( status == kIOReturnSuccess ) { commandInProgress = true; } } // There is no data to be transfered break; } if ( resultingStatus != kIOReturnSuccess ) { // An error occurred, probably a timeout error, // and the command was not successfully sent to the device. status = resultingStatus; break; } // Start a bulk in or out transaction status = BulkOnlyTransferData( boRequestBlock, kBulkOnlyBulkIOComplete ); if ( status == kIOReturnSuccess ) { commandInProgress = true; } } break; case kBulkOnlyBulkIOComplete: { status = resultingStatus; // and status STATUS_LOG((5, "%s: kBulkOnlyBulkIOComplete returned %x", getName(), resultingStatus)); if ( 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); // 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 ) { UInt8 deviceSpeed; deviceSpeed = GetInterfaceReference()->GetDevice()->GetSpeed(); if ( deviceSpeed == kUSBDeviceSpeedHigh ) // Is this a high speed device? { // Yes, simply clear the halt bit and get the CSW and re-enter kBulkOnlyBulkIOComplete. GetBulkInPipe()->Abort(); status = BulkOnlyReceiveCSWPacket( boRequestBlock, kBulkOnlyBulkIOComplete ); } else { // No, this is either a full speed or low speed device. // The data overrun leaves the bulk in pipe stalled. Clear the stall and move on. fPotentiallyStalledPipe = GetBulkInPipe(); boRequestBlock->currentState = kBulkOnlyClearBulkStall; status = ClearFeatureEndpointStall( fPotentiallyStalledPipe, &boRequestBlock->boCompletion ); } if ( status == kIOReturnSuccess ) { 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 )) { // Was there a device error? The device could have been removed or lost power. // An error occurred, probably a timeout error, // and the command was not successfully sent to the device. status = StartDeviceRecovery(); 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: { STATUS_LOG((5, "%s: kBulkOnlyCheckBulkStall returned %x", getName(), resultingStatus)); // Check to see if the endpoint was stalled if ( (boRequestBlock->boGetStatusBuffer[0] & 1) == 1 ) { boRequestBlock->currentState = kBulkOnlyClearBulkStall; 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(); } status = BulkOnlyReceiveCSWPacket( boRequestBlock, kBulkOnlyStatusReceived ); if ( status == kIOReturnSuccess ) { commandInProgress = true; } } } break; case kBulkOnlyClearBulkStall: { STATUS_LOG((5, "%s: kBulkOnlyClearBulkStall returned %x", getName(), 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 status = BulkOnlyReceiveCSWPacket( boRequestBlock, kBulkOnlyStatusReceived ); if ( status == kIOReturnSuccess ) { commandInProgress = true; } } break; case kBulkOnlyStatusReceived: { STATUS_LOG((5, "%s: kBulkOnlyStatusReceived returned %x", getName(), resultingStatus)); // Release the memory descriptor for the CSW boRequestBlock->boPhaseDesc->release(); // Bulk transfer is done, get the Command Status Wrapper from the device if ( resultingStatus == kIOUSBPipeStalled) { // The device reported an error on the command // report an error to the client boRequestBlock->currentState = kBulkOnlyCheckBulkStall; 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 ) { commandInProgress = true; } } else if( boRequestBlock->boCSW.cswTag == boRequestBlock->boCBW.cbwTag) { // 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 = kIOReturnError; } break; case kCSWPhaseError: { // The device reported a phase error on the command, perform the // bulk reset on the device. if ( fUseUSBResetNotBOReset ) { ResetDeviceNow(); boRequestBlock->currentState = kBulkOnlyClearBulkInCompleted; status = ClearFeatureEndpointStall( GetBulkInPipe(), &boRequestBlock->boCompletion ); } else { status = BulkDeviceResetDevice( boRequestBlock, kBulkOnlyResetCompleted ); } if( status == kIOReturnSuccess ) { commandInProgress = true; } } break; default: { // We received an unkown status, report an error to the client. status = kIOReturnError; } break; } } else { // 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: kBulkOnlyStatusReceived2ndTime returned %x", getName(), resultingStatus)); // Second try for the CSW is done, if an error occurred, reset device. if ( resultingStatus != kIOReturnSuccess) { if ( fUseUSBResetNotBOReset ) { ResetDeviceNow(); boRequestBlock->currentState = kBulkOnlyClearBulkInCompleted; status = ClearFeatureEndpointStall( GetBulkInPipe(), &boRequestBlock->boCompletion ); } else { status = BulkDeviceResetDevice( boRequestBlock, kBulkOnlyResetCompleted ); } if( status == kIOReturnSuccess ) { commandInProgress = true; } } else { status = kIOReturnError; } } break; case kBulkOnlyResetCompleted: { STATUS_LOG((5, "%s: kBulkOnlyResetCompleted returned %x", getName(), resultingStatus)); boRequestBlock->currentState = kBulkOnlyClearBulkInCompleted; status = ClearFeatureEndpointStall( GetBulkInPipe(), &boRequestBlock->boCompletion ); if ( status == kIOReturnSuccess ) { commandInProgress = true; } } break; case kBulkOnlyClearBulkInCompleted: { STATUS_LOG((5, "%s: kBulkOnlyClearBulkInCompleted returned %x", getName(), resultingStatus)); boRequestBlock->currentState = kBulkOnlyClearBulkOutCompleted; status = ClearFeatureEndpointStall( GetBulkOutPipe(), &boRequestBlock->boCompletion ); if ( status == kIOReturnSuccess ) { commandInProgress = true; } } break; case kBulkOnlyClearBulkOutCompleted: { STATUS_LOG((5, "%s: kBulkOnlyClearBulkOutCompleted returned %x", 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 ); } }