IOUSBMassStorageClass.cpp [plain text]
#include <libkern/OSByteOrder.h>
#include "IOUSBMassStorageClass.h"
#include "IOUSBMassStorageClassTimestamps.h"
#include "Debugging.h"
#include <IOKit/scsi/IOSCSIPeripheralDeviceNub.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/IOKitKeys.h>
enum
{
kDefaultReadTimeoutDuration = 30000,
kDefaultWriteTimeoutDuration = 30000
};
enum
{
kDefaultMaximumByteCountRead = 131072,
kDefaultMaximumByteCountWrite = 131072
};
enum
{
kMaxConsecutiveResets = 5
};
#define fWorkLoop fIOSCSIProtocolInterfaceReserved->fWorkLoop
class USBMassStorageClassGlobals
{
public:
USBMassStorageClassGlobals ( void );
virtual ~USBMassStorageClassGlobals ( void );
};
UInt32 gUSBDebugFlags = 0; static USBMassStorageClassGlobals gUSBGlobals;
static int USBMassStorageClassSysctl ( struct sysctl_oid * oidp, void * arg1, int arg2, struct sysctl_req * req );
SYSCTL_PROC ( _debug, OID_AUTO, USBMassStorageClass, CTLFLAG_RW, 0, 0, USBMassStorageClassSysctl, "USBMassStorageClass", "USBMassStorageClass debug interface" );
static int
USBMassStorageClassSysctl ( struct sysctl_oid * oidp, void * arg1, int arg2, struct sysctl_req * req )
{
int error = 0;
USBSysctlArgs usbArgs;
UNUSED ( oidp );
UNUSED ( arg1 );
UNUSED ( arg2 );
STATUS_LOG ( ( 1, "+USBMassStorageClassGlobals: gUSBDebugFlags = 0x%08X\n", ( unsigned int ) gUSBDebugFlags ) );
error = SYSCTL_IN ( req, &usbArgs, sizeof ( usbArgs ) );
if ( ( error == 0 ) && ( usbArgs.type == kUSBTypeDebug ) )
{
if ( usbArgs.operation == kUSBOperationGetFlags )
{
usbArgs.debugFlags = gUSBDebugFlags;
error = SYSCTL_OUT ( req, &usbArgs, sizeof ( usbArgs ) );
}
else if ( usbArgs.operation == kUSBOperationSetFlags )
{
gUSBDebugFlags = usbArgs.debugFlags;
}
}
STATUS_LOG ( ( 1, "-USBMassStorageClassGlobals: gUSBDebugFlags = 0x%08X\n", ( unsigned int ) gUSBDebugFlags ) );
return error;
}
USBMassStorageClassGlobals::USBMassStorageClassGlobals ( void )
{
int debugFlags;
STATUS_LOG ( ( 1, "+USBMassStorageClassGlobals::USBMassStorageClassGlobals\n" ) );
if ( PE_parse_boot_argn ( "USB-MassStorage", &debugFlags, sizeof ( debugFlags ) ) )
{
gUSBDebugFlags = debugFlags;
}
sysctl_register_oid ( &sysctl__debug_USBMassStorageClass );
STATUS_LOG ( ( 1, "-USBMassStorageClassGlobals::USBMassStorageClassGlobals\n" ) );
}
USBMassStorageClassGlobals::~USBMassStorageClassGlobals ( void )
{
STATUS_LOG ( ( 1, "+~USBMassStorageClassGlobals::USBMassStorageClassGlobals\n" ) );
sysctl_unregister_oid ( &sysctl__debug_USBMassStorageClass );
STATUS_LOG ( ( 1, "-~USBMassStorageClassGlobals::USBMassStorageClassGlobals\n" ) );
}
#define super IOSCSIProtocolServices
OSDefineMetaClassAndStructors( IOUSBMassStorageClass, IOSCSIProtocolServices )
bool
IOUSBMassStorageClass::init ( OSDictionary * propTable )
{
if ( super::init( propTable ) == false)
{
return false;
}
return true;
}
bool
IOUSBMassStorageClass::start ( IOService * provider )
{
IOUSBFindEndpointRequest request;
OSDictionary * characterDict = NULL;
OSObject * obj = NULL;
IOReturn result = kIOReturnError;
bool retVal = false;
OSNumber * number = NULL;
bool success = false;
if ( super::start( provider ) == false )
{
STATUS_LOG(( 1, "%s[%p]: superclass start failure.", getName(), this));
return false;
}
RecordUSBTimeStamp ( UMC_TRACE ( kIOUSBMassStorageClassStart ),
( uintptr_t ) this, NULL,
NULL, NULL );
reserved = ( ExpansionData * ) IOMalloc ( sizeof ( ExpansionData ) );
bzero ( reserved, sizeof ( ExpansionData ) );
SetInterfaceReference( OSDynamicCast( IOUSBInterface, provider) );
if ( GetInterfaceReference() == NULL )
{
STATUS_LOG ( ( 1, "%s[%p]: the provider is not an IOUSBInterface object",
getName(), this ) );
goto Exit;
}
if ( provider->getProperty( kIOUSBMassStorageDoNotMatch ) != NULL )
{
goto abortStart;
}
RecordUSBTimeStamp ( UMC_TRACE ( kAtUSBAddress ),
( uintptr_t ) this, ( unsigned int ) GetInterfaceReference()->GetDevice()->GetAddress(),
NULL, NULL );
STATUS_LOG ( ( 6, "%s[%p]: USB Mass Storage @ %d",
getName(), this,
GetInterfaceReference()->GetDevice()->GetAddress() ) );
if ( GetInterfaceReference()->open( this ) == false)
{
STATUS_LOG ( ( 1, "%s[%p]: could not open the interface", getName(), this ) );
goto Exit;
}
fBulkInPipe = NULL;
fBulkOutPipe = NULL;
fInterruptPipe = NULL;
fClients = NULL;
SetMaxLogicalUnitNumber ( 0 );
fBulkOnlyCommandTag = 0;
fBulkOnlyCommandStructInUse = false;
fCBICommandStructInUse = false;
fUseUSBResetNotBOReset = false;
fKnownCSWTagMismatchIssues = false;
fWaitingForReconfigurationMessage = false;
fTerminating = false;
fPortSuspendResumeForPMEnabled = false;
fAutonomousSpinDownWorkAround = false;
characterDict = OSDynamicCast ( OSDictionary, getProperty( kIOUSBMassStorageCharacteristics ) );
if ( characterDict == NULL )
{
fPreferredProtocol = GetInterfaceReference()->GetInterfaceProtocol();
fPreferredSubclass = GetInterfaceReference()->GetInterfaceSubClass();
}
else
{
OSNumber * preferredProtocol;
OSNumber * preferredSubclass;
RecordUSBTimeStamp ( UMC_TRACE ( kIOUMCStorageCharacDictFound ),
( uintptr_t ) this, NULL, NULL, NULL );
preferredProtocol = OSDynamicCast ( OSNumber, characterDict->getObject( kIOUSBMassStoragePreferredProtocol ) );
if ( preferredProtocol == NULL )
{
fPreferredProtocol = GetInterfaceReference()->GetInterfaceProtocol();
}
else
{
fPreferredProtocol = preferredProtocol->unsigned32BitValue();
}
if ( characterDict->getObject( kIOUSBMassStorageDoNotOperate ) != NULL )
{
goto abortStart;
}
if ( characterDict->getObject ( kIOUSBMassStorageUseStandardUSBReset ) != NULL )
{
fUseUSBResetNotBOReset = true;
}
if ( characterDict->getObject ( kIOUSBKnownCSWTagIssues ) != NULL )
{
fKnownCSWTagMismatchIssues = true;
}
if ( characterDict->getObject( kIOUSBMassStorageEnableSuspendResumePM ) != NULL )
{
fPortSuspendResumeForPMEnabled = true;
}
if ( characterDict->getObject( kIOUSBMassStorageResetOnResume ) != NULL )
{
STATUS_LOG ( ( 4, "%s[%p]: knownResetOnResumeDevice", getName(), this ) );
fRequiresResetOnResume = true;
}
preferredSubclass = OSDynamicCast ( OSNumber, characterDict->getObject( kIOUSBMassStoragePreferredSubclass ));
if ( preferredSubclass == NULL )
{
fPreferredSubclass = GetInterfaceReference()->GetInterfaceSubClass();
}
else
{
fPreferredSubclass = preferredSubclass->unsigned32BitValue();
}
}
STATUS_LOG ( ( 6, "%s[%p]: Preferred Protocol is: %d", getName(), this, fPreferredProtocol ) );
STATUS_LOG ( ( 6, "%s[%p]: Preferred Subclass is: %d", getName(), this, fPreferredSubclass ) );
STATUS_LOG ( ( 7, "%s[%p]: Configure the Storage interface", getName(), this ) );
switch ( GetInterfaceProtocol() )
{
case kProtocolControlBulkInterrupt:
{
RecordUSBTimeStamp ( UMC_TRACE ( kCBIProtocolDeviceDetected ),
( uintptr_t ) this, NULL,
NULL, NULL );
request.type = kUSBInterrupt;
request.direction = kUSBIn;
fInterruptPipe = GetInterfaceReference()->FindNextPipe ( NULL, &request );
STATUS_LOG ( ( 7, "%s[%p]: find interrupt pipe", getName(), this ) );
require_nonzero ( fInterruptPipe, abortStart );
fInterruptPipe->retain ( );
fCBIMemoryDescriptor = IOMemoryDescriptor::withAddress (
&fCBICommandRequestBlock.cbiGetStatusBuffer,
kUSBStorageAutoStatusSize,
kIODirectionIn );
require_nonzero ( fCBIMemoryDescriptor, abortStart );
result = fCBIMemoryDescriptor->prepare();
require_success ( result, abortStart );
}
break;
case kProtocolControlBulk:
case kProtocolBulkOnly:
{
STATUS_LOG ( ( 7, "%s[%p]: Bulk Only - skip interrupt pipe", getName(), this ) );
RecordUSBTimeStamp ( UMC_TRACE ( kBODeviceDetected ),
( uintptr_t ) this, NULL, NULL, NULL );
fBulkOnlyCBWMemoryDescriptor = IOMemoryDescriptor::withAddress (
&fBulkOnlyCommandRequestBlock.boCBW,
kByteCountOfCBW,
kIODirectionOut );
require_nonzero ( fBulkOnlyCBWMemoryDescriptor, abortStart );
result = fBulkOnlyCBWMemoryDescriptor->prepare();
require_success ( result, abortStart );
fBulkOnlyCSWMemoryDescriptor = IOMemoryDescriptor::withAddress (
&fBulkOnlyCommandRequestBlock.boCSW,
kByteCountOfCSW,
kIODirectionIn );
require_nonzero ( fBulkOnlyCSWMemoryDescriptor, abortStart );
result = fBulkOnlyCSWMemoryDescriptor->prepare();
require_success ( result, abortStart );
}
break;
default:
{
RecordUSBTimeStamp ( UMC_TRACE ( kNoProtocolForDevice ),
( uintptr_t ) this, NULL, NULL, NULL );
goto abortStart;
}
break;
}
STATUS_LOG ( ( 7, "%s[%p]: find bulk in pipe", getName(), this ) );
request.type = kUSBBulk;
request.direction = kUSBIn;
fBulkInPipe = GetInterfaceReference()->FindNextPipe ( NULL, &request );
require_nonzero ( fBulkInPipe, abortStart );
fBulkInPipe->retain ( );
STATUS_LOG ( ( 7, "%s[%p]: find bulk out pipe", getName(), this ) );
request.type = kUSBBulk;
request.direction = kUSBOut;
fBulkOutPipe = GetInterfaceReference()->FindNextPipe ( NULL, &request );
require_nonzero ( fBulkOutPipe, abortStart );
fBulkOutPipe->retain ( );
characterDict = OSDynamicCast ( OSDictionary, getProperty ( kIOPropertyProtocolCharacteristicsKey ) );
if ( characterDict == NULL )
{
characterDict = OSDictionary::withCapacity ( 1 );
}
else
{
characterDict->retain ( );
}
obj = getProperty ( kIOPropertyPhysicalInterconnectTypeKey );
if ( obj != NULL )
{
characterDict->setObject ( kIOPropertyPhysicalInterconnectTypeKey, obj );
}
obj = getProperty ( kIOPropertyPhysicalInterconnectLocationKey );
if ( obj != NULL )
{
characterDict->setObject ( kIOPropertyPhysicalInterconnectLocationKey, obj );
}
if ( IsPhysicalInterconnectLocationInternal ( ) )
{
OSString * internalString = NULL;
internalString = OSString::withCString ( kIOPropertyInternalKey );
if ( internalString != NULL )
{
characterDict->setObject ( kIOPropertyPhysicalInterconnectLocationKey, internalString );
internalString->release ( );
internalString = NULL;
}
}
obj = getProperty ( kIOPropertyReadTimeOutDurationKey );
if ( obj != NULL )
{
characterDict->setObject ( kIOPropertyReadTimeOutDurationKey, obj );
}
else
{
number = OSNumber::withNumber ( kDefaultReadTimeoutDuration, 32 );
if ( number != NULL )
{
characterDict->setObject ( kIOPropertyReadTimeOutDurationKey, number );
number->release ( );
number = NULL;
}
}
obj = getProperty ( kIOPropertyWriteTimeOutDurationKey );
if ( obj != NULL )
{
characterDict->setObject ( kIOPropertyWriteTimeOutDurationKey, obj );
}
else
{
number = OSNumber::withNumber ( kDefaultWriteTimeoutDuration, 32 );
if ( number != NULL )
{
characterDict->setObject ( kIOPropertyWriteTimeOutDurationKey, number );
number->release ( );
number = NULL;
}
}
setProperty ( kIOPropertyProtocolCharacteristicsKey, characterDict );
characterDict->release ( );
characterDict = NULL;
STATUS_LOG ( ( 6, "%s[%p]: successfully configured", getName(), this ) );
#if defined (__i386__)
{
char usbDeviceAddress [ kUSBDAddressLength ];
OSNumber * usbDeviceID;
snprintf ( usbDeviceAddress, kUSBDAddressLength, "%x", ( int ) GetInterfaceReference()->GetDevice()->GetAddress() );
usbDeviceID = OSNumber::withNumber ( ( int ) GetInterfaceReference()->GetDevice()->GetAddress(), 64 );
if ( usbDeviceID != NULL )
{
setProperty ( kIOPropertyIOUnitKey, usbDeviceID );
setLocation ( ( const char * ) usbDeviceAddress, gIODTPlane );
usbDeviceID->release ( );
}
}
#endif
fDeviceAttached = true;
InitializePowerManagement ( GetInterfaceReference() );
success = BeginProvidedServices();
require ( success, abortStart );
retVal = true;
goto Exit;
abortStart:
STATUS_LOG ( ( 1, "%s[%p]: aborting startup. Stop the provider.", getName(), this ) );
if ( IsPowerManagementIntialized() )
{
PMstop();
}
{
IOUSBInterface * currentInterface;
currentInterface = GetInterfaceReference();
if ( currentInterface != NULL )
{
SetInterfaceReference( NULL );
currentInterface->close( this );
}
}
if ( fCBIMemoryDescriptor != NULL )
{
fCBIMemoryDescriptor->complete();
fCBIMemoryDescriptor->release();
fCBIMemoryDescriptor = NULL;
}
if ( fBulkOnlyCBWMemoryDescriptor != NULL )
{
fBulkOnlyCBWMemoryDescriptor->complete();
fBulkOnlyCBWMemoryDescriptor->release();
fBulkOnlyCBWMemoryDescriptor = NULL;
}
if ( fBulkOnlyCSWMemoryDescriptor != NULL )
{
fBulkOnlyCSWMemoryDescriptor->complete();
fBulkOnlyCSWMemoryDescriptor->release();
fBulkOnlyCSWMemoryDescriptor = NULL;
}
stop ( provider );
Exit:
return retVal;
}
void
IOUSBMassStorageClass::stop ( IOService * provider )
{
STATUS_LOG ( ( 1, "%s[%p]: stop: Called", getName(), this ) );
RecordUSBTimeStamp ( UMC_TRACE ( kIOUSBMassStorageClassStop ),
( uintptr_t ) this, NULL, NULL, NULL );
EndProvidedServices ( );
if ( fBulkInPipe != NULL )
{
fBulkInPipe->release ( );
fBulkInPipe = NULL;
}
if ( fBulkOutPipe != NULL )
{
fBulkOutPipe->release ( );
fBulkOutPipe = NULL;
}
if ( fInterruptPipe != NULL )
{
fInterruptPipe->release ( );
fInterruptPipe = NULL;
}
super::stop ( provider );
}
void
IOUSBMassStorageClass::free ( void )
{
STATUS_LOG ( ( 1, "%s[%p]: free: Called", getName(), this ) );
require_nonzero ( reserved, Exit );
if ( fClients != NULL )
{
fClients->release ( );
fClients = NULL;
}
if ( fCBIMemoryDescriptor != NULL )
{
fCBIMemoryDescriptor->complete ( );
fCBIMemoryDescriptor->release ( );
fCBIMemoryDescriptor = NULL;
}
if ( fBulkOnlyCBWMemoryDescriptor != NULL )
{
fBulkOnlyCBWMemoryDescriptor->complete ( );
fBulkOnlyCBWMemoryDescriptor->release ( );
fBulkOnlyCBWMemoryDescriptor = NULL;
}
if ( fBulkOnlyCSWMemoryDescriptor != NULL )
{
fBulkOnlyCSWMemoryDescriptor->complete ( );
fBulkOnlyCSWMemoryDescriptor->release ( );
fBulkOnlyCSWMemoryDescriptor = NULL;
}
IOFree ( reserved, sizeof ( ExpansionData ) );
reserved = NULL;
Exit:
super::free ( );
}
IOReturn
IOUSBMassStorageClass::message ( UInt32 type, IOService * provider, void * argument )
{
IOReturn result = kIOReturnSuccess;
RecordUSBTimeStamp ( UMC_TRACE ( kMessagedCalled ), ( uintptr_t ) this, type, NULL, NULL );
STATUS_LOG ( ( 4, "%s[%p]: message = %lx called", getName ( ), this, type ) );
switch ( type )
{
case kIOUSBMessagePortHasBeenResumed:
{
STATUS_LOG ( ( 2, "%s[%p]: message kIOUSBMessagePortHasBeenResumed.", getName ( ), this ) );
if ( fRequiresResetOnResume == true )
{
ResetDeviceNow ( true );
}
}
break;
default:
{
STATUS_LOG ( ( 2, "%s[%p]: message default case.", getName ( ), this ) );
result = super::message ( type, provider, argument );
}
}
return result;
}
bool
IOUSBMassStorageClass::willTerminate ( IOService * provider,
IOOptionBits options )
{
STATUS_LOG ( ( 2, "%s[%p]: willTerminate called.", getName ( ), this ) );
RecordUSBTimeStamp ( UMC_TRACE ( kWillTerminateCalled ),
( uintptr_t ) this, ( uintptr_t ) GetInterfaceReference ( ),
( unsigned int ) isInactive ( ), NULL );
fTerminating = true;
SendNotification_DeviceRemoved ( );
return super::willTerminate ( provider, options );
}
bool
IOUSBMassStorageClass::didTerminate ( IOService * provider, IOOptionBits options, bool * defer )
{
IOUSBInterface * currentInterface;
IOReturn status;
bool success;
STATUS_LOG ( ( 3 , "%s[%p]::didTerminate: Entered with options=0x%x defer=%d", getName ( ), this, options, ( defer ? *defer : false ) ) );
RecordUSBTimeStamp ( UMC_TRACE ( kDidTerminateCalled ),
( uintptr_t ) this, ( uintptr_t ) GetInterfaceReference ( ),
( unsigned int ) isInactive(), ( unsigned int ) fResetInProgress );
fTerminationDeferred = fBulkOnlyCommandStructInUse | fCBICommandStructInUse;
while ( fTerminationDeferred == true )
{
STATUS_LOG ( ( 3 , "%s[%p]::didTerminate: Sleeping on fTerminationDeferred", getName ( ), this ) );
status = fCommandGate->commandSleep ( &fTerminationDeferred, THREAD_UNINT );
STATUS_LOG ( ( 3 , "%s[%p]::didTerminate: Awoke with status=0x%x fTerminationDeferred=%d", getName ( ), this, status, fTerminationDeferred ) );
}
currentInterface = GetInterfaceReference ( );
require_nonzero ( currentInterface, ErrorExit );
STATUS_LOG ( ( 3 , "%s[%p]::didTerminate: Closing provider", getName ( ), this ) );
SetInterfaceReference ( NULL );
currentInterface->close ( this );
STATUS_LOG ( ( 3 , "%s[%p]::didTerminate: Closed provider", getName ( ), this ) );
ErrorExit:
success = super::didTerminate ( provider, options, defer );
STATUS_LOG ( ( 3 , "%s[%p]::didTerminate: Returning success=%d defer=%d", getName ( ), this, success, ( defer ? *defer : false ) ) );
return success;
}
bool
IOUSBMassStorageClass::BeginProvidedServices ( void )
{
STATUS_LOG ( ( 7, "%s[%p]: Determine the maximum LUN", getName(), this ) );
if ( GetInterfaceProtocol() == kProtocolBulkOnly )
{
IOReturn status = kIOReturnError;
bool maxLUNDetermined = false;
OSDictionary * characterDict = NULL;
characterDict = OSDynamicCast ( OSDictionary, getProperty ( kIOUSBMassStorageCharacteristics ) );
if ( characterDict != NULL )
{
OSNumber * maxLUN = OSDynamicCast ( OSNumber,
characterDict->getObject ( kIOUSBMassStorageMaxLogicalUnitNumber ) );
if( maxLUN != NULL )
{
RecordUSBTimeStamp ( UMC_TRACE ( kBOPreferredMaxLUN ),
( uintptr_t ) this, maxLUN->unsigned8BitValue(), NULL, NULL );
STATUS_LOG ( ( 4, "%s[%p]: Number of LUNs %u.", getName(), this, maxLUN->unsigned8BitValue() ) );
SetMaxLogicalUnitNumber ( maxLUN->unsigned8BitValue() );
maxLUNDetermined = true;
}
}
if( maxLUNDetermined == false )
{
bool triedReset = false;
UInt8 clearPipeAttempts = 0;
while ( status != kIOReturnSuccess )
{
fUSBDeviceRequest.bmRequestType = USBmakebmRequestType( kUSBIn, kUSBClass, kUSBInterface );
fUSBDeviceRequest.bRequest = 0xFE;
fUSBDeviceRequest.wValue = 0;
fUSBDeviceRequest.wIndex = GetInterfaceReference()->GetInterfaceNumber();
fUSBDeviceRequest.wLength = 1;
fUSBDeviceRequest.pData = &fMaxLogicalUnitNumber;
STATUS_LOG ( ( 4, "%s[%p]: Issuing device request to find max LUN", getName(), this ) );
status = GetInterfaceReference()->DeviceRequest ( &fUSBDeviceRequest );
RecordUSBTimeStamp ( UMC_TRACE ( kBOGetMaxLUNReturned ),
( uintptr_t ) this, status, fMaxLogicalUnitNumber, ( unsigned int ) triedReset );
STATUS_LOG ( ( 4, "%s[%p]: DeviceRequest GetMaxLUN returned status = %x", getName(), this, status ) );
if ( status != kIOReturnSuccess )
{
SetMaxLogicalUnitNumber( 0 );
if( ( status == kIOUSBPipeStalled ) && ( clearPipeAttempts < 3 ) )
{
UInt8 eStatus[2];
STATUS_LOG ( ( 4, "%s[%p]: calling GetStatusEndpointStatus to clear stall", getName(), this ) );
GetStatusEndpointStatus( GetControlPipe(), &eStatus[0], NULL);
clearPipeAttempts++;
}
else if ( ( status == kIOReturnNotResponding ) && ( triedReset == false ) )
{
STATUS_LOG ( ( 4, "%s[%p]: BeginProvidedServices: device not responding, reseting.", getName(), this ) );
ResetDeviceNow ( true );
triedReset = true;
}
else
{
break;
}
}
}
}
}
else
{
SetMaxLogicalUnitNumber( 0 );
}
RecordUSBTimeStamp ( UMC_TRACE ( kLUNConfigurationComplete ),
( uintptr_t ) this, GetMaxLogicalUnitNumber ( ), NULL, NULL );
STATUS_LOG ( ( 5, "%s[%p]: Configured, Max LUN = %d", getName(), this, GetMaxLogicalUnitNumber() ) );
if ( GetMaxLogicalUnitNumber() > 0 )
{
fClients = OSSet::withCapacity ( GetMaxLogicalUnitNumber() + 1 );
for( int loopLUN = 0; loopLUN <= GetMaxLogicalUnitNumber(); loopLUN++ )
{
STATUS_LOG ( ( 6, "%s[%p]::CreatePeripheralDeviceNubForLUN entering.", getName(), this ) );
IOSCSILogicalUnitNub * nub = OSTypeAlloc( IOSCSILogicalUnitNub );
if( nub == NULL )
{
PANIC_NOW ( ( "IOUSBMassStorageClass::CreatePeripheralDeviceNubForLUN failed" ) );
return false;
}
if ( nub->init( 0 ) == false )
{
nub->release();
return false;
}
if ( nub->attach( this ) == false )
{
if( isInactive() == false )
{
PANIC_NOW ( ( "IOUSBMassStorageClass::CreatePeripheralDeviceNubForLUN unable to attach nub" ) );
}
nub->release();
return false;
}
nub->SetLogicalUnitNumber ( loopLUN );
if ( nub->start ( this ) == false )
{
nub->detach ( this );
}
else
{
nub->registerService ( kIOServiceAsynchronous );
}
nub->release();
nub = NULL;
STATUS_LOG ( ( 6, "%s[%p]::CreatePeripheralDeviceNubForLUN exiting.", getName(), this ) );
}
}
registerService ( kIOServiceAsynchronous );
return true;
}
bool
IOUSBMassStorageClass::EndProvidedServices
( void )
{
return true;
}
#pragma mark -
#pragma mark *** CDB Transport Methods ***
#pragma mark -
bool
IOUSBMassStorageClass::SendSCSICommand (
SCSITaskIdentifier request,
SCSIServiceResponse * serviceResponse,
SCSITaskStatus * taskStatus )
{
IOReturn status;
SCSICommandDescriptorBlock cdbData;
bool accepted = false;
*taskStatus = kSCSITaskStatus_No_Status;
*serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
STATUS_LOG ( ( 6, "%s[%p]: SendSCSICommand Entered with request=%p", getName ( ), this, request ) );
fCommandGate->runAction ( OSMemberFunctionCast ( IOCommandGate::Action,
this,
&IOUSBMassStorageClass::AcceptSCSITask ),
request,
&accepted );
require_quiet ( accepted, Exit );
if ( fPortIsSuspended == true )
{
status = SuspendPort ( false );
require_success ( status, Exit );
}
GetCommandDescriptorBlock( request, &cdbData );
RecordUSBTimeStamp ( UMC_TRACE ( kCDBLog1 ),
( uintptr_t ) this, ( uintptr_t ) request,
( cdbData[ 0] ) | ( cdbData[ 1] << 8 ) | ( cdbData[ 2] << 16 ) | ( cdbData[ 3] << 24 ),
( cdbData[ 4] ) | ( cdbData[ 5] << 8 ) | ( cdbData[ 6] << 16 ) | ( cdbData[ 7] << 24 ) );
RecordUSBTimeStamp ( UMC_TRACE ( kCDBLog2 ),
( uintptr_t ) this, ( uintptr_t ) request,
( cdbData[ 8] ) | ( cdbData[ 9] << 8 ) | ( cdbData[10] << 16 ) | ( cdbData[11] << 24 ),
( cdbData[12] ) | ( cdbData[13] << 8 ) | ( cdbData[14] << 16 ) | ( cdbData[15] << 24 ) );
#if USB_MASS_STORAGE_DEBUG
STATUS_LOG ( ( 4, "%s[%p]: SendSCSICommand CDB data: ", getName(), this ) );
if ( GetCommandDescriptorBlockSize ( request ) == kSCSICDBSize_6Byte )
STATUS_LOG ( ( 4, "%s[%p]: %X : %X : %X : %X : %X : %X",
getName(), this, cdbData[0], cdbData[1],
cdbData[2], cdbData[3], cdbData[4], cdbData[5] ) );
else if ( GetCommandDescriptorBlockSize ( request ) == kSCSICDBSize_10Byte )
STATUS_LOG ( ( 4, "%s[%p]: %X : %X : %X : %X : %X : %X : %X : %X : %X : %X",
getName(), this, cdbData[0], cdbData[1],
cdbData[2], cdbData[3], cdbData[4], cdbData[5],
cdbData[6], cdbData[7], cdbData[8], cdbData[9] ) );
else if ( GetCommandDescriptorBlockSize ( request ) == kSCSICDBSize_12Byte )
STATUS_LOG ( ( 4, "%s[%p]: %X : %X : %X : %X : %X : %X : %X : %X : %X : %X : %X : %X",
getName(), this, cdbData[0], cdbData[1],
cdbData[2], cdbData[3], cdbData[4], cdbData[5],
cdbData[6], cdbData[7], cdbData[8], cdbData[9],
cdbData[10], cdbData[11] ) );
else if ( GetCommandDescriptorBlockSize ( request ) == kSCSICDBSize_16Byte )
STATUS_LOG ( ( 4, "%s[%p]: %X : %X : %X : %X : %X : %X : %X : %X : %X : %X : %X : %X : %X : %X : %X : %X",
getName(), this, cdbData[0], cdbData[1],
cdbData[2], cdbData[3], cdbData[4], cdbData[5],
cdbData[6], cdbData[7], cdbData[8], cdbData[9],
cdbData[10], cdbData[11], cdbData[12], cdbData[13],
cdbData[14], cdbData[15] ) );
#endif
if ( GetInterfaceProtocol() == kProtocolBulkOnly )
{
status = SendSCSICommandForBulkOnlyProtocol ( request );
RecordUSBTimeStamp ( UMC_TRACE ( kBOSendSCSICommandReturned ),
( uintptr_t ) this, ( uintptr_t ) request, status, NULL );
STATUS_LOG ( ( 5, "%s[%p]: SendSCSICommandforBulkOnlyProtocol returned %x", getName ( ), this, status ) );
}
else
{
status = SendSCSICommandForCBIProtocol ( request );
RecordUSBTimeStamp ( UMC_TRACE ( kCBISendSCSICommandReturned ),
( uintptr_t ) this, ( uintptr_t ) request, status, NULL );
STATUS_LOG ( ( 5, "%s[%p]: SendSCSICommandforCBIProtocol returned %x", getName ( ), this, status ) );
}
if ( status == kIOReturnSuccess )
{
*serviceResponse = kSCSIServiceResponse_Request_In_Process;
}
else
{
SCSIServiceResponse serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
SCSITaskStatus taskStatus = kSCSITaskStatus_DeliveryFailure;
fCommandGate->runAction (
OSMemberFunctionCast ( IOCommandGate::Action,
this,
&IOUSBMassStorageClass::GatedCompleteSCSICommand ),
request,
( void * ) &serviceResponse,
( void * ) &taskStatus );
}
Exit:
STATUS_LOG ( ( 6, "%s[%p]: SendSCSICommand returning accepted=%d", getName ( ), this, accepted ) );
return accepted;
}
void
IOUSBMassStorageClass::CompleteSCSICommand ( SCSITaskIdentifier request, IOReturn status )
{
SCSITaskStatus taskStatus;
check ( fWorkLoop->inGate ( ) == true );
fBulkOnlyCommandStructInUse = false;
fCBICommandStructInUse = false;
fConsecutiveResetCount = 0;
RecordUSBTimeStamp ( UMC_TRACE ( kCompleteSCSICommand ),
( uintptr_t ) this, ( uintptr_t ) request,
status, NULL );
if ( status == kIOReturnSuccess )
{
taskStatus = kSCSITaskStatus_GOOD;
}
else
{
taskStatus = kSCSITaskStatus_CHECK_CONDITION;
}
STATUS_LOG ( ( 6, "%s[%p]: CompleteSCSICommand request=%p taskStatus=0x%02x", getName(), this, request, taskStatus ) );
CommandCompleted ( request, kSCSIServiceResponse_TASK_COMPLETE, taskStatus );
CheckDeferredTermination ( );
}
SCSIServiceResponse
IOUSBMassStorageClass::AbortSCSICommand ( SCSITaskIdentifier abortTask )
{
IOReturn status = kIOReturnSuccess;
STATUS_LOG ( ( 6, "%s[%p]: AbortSCSICommand was called", getName(), this ) );
if ( abortTask == NULL )
{
STATUS_LOG ( ( 1, "%s[%p]: AbortSCSICommand was called with a NULL CDB object", getName(), this ) );
return kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
}
if ( fTerminating == true )
{
STATUS_LOG ( ( 1, "%s[%p]: AbortSCSICommand was called with a NULL interface", getName(), this ) );
return kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
}
RecordUSBTimeStamp ( UMC_TRACE( kAbortedTask ),
( uintptr_t ) this, ( uintptr_t ) abortTask, NULL, NULL );
if ( GetInterfaceReference()->GetInterfaceProtocol() == kProtocolBulkOnly )
{
status = AbortSCSICommandForBulkOnlyProtocol( abortTask );
STATUS_LOG ( ( 5, "%s[%p]: abortCDBforBulkOnlyProtocol returned %x", getName(), this, status ) );
}
else
{
status = AbortSCSICommandForCBIProtocol( abortTask );
STATUS_LOG ( ( 5, "%s[%p]: abortCDBforCBIProtocol returned %x", getName(), this, status ) );
}
return kSCSIServiceResponse_FUNCTION_REJECTED;
}
bool
IOUSBMassStorageClass::IsProtocolServiceSupported (
SCSIProtocolFeature feature,
void * serviceValue )
{
bool isSupported = false;
OSDictionary * characterDict = NULL;
STATUS_LOG ( ( 6, "%s[%p]::IsProtocolServiceSupported called", getName(), this ) );
characterDict = OSDynamicCast ( OSDictionary, ( getProperty ( kIOPropertySCSIDeviceCharacteristicsKey ) ) );
switch ( feature )
{
case kSCSIProtocolFeature_GetMaximumLogicalUnitNumber:
{
* ( ( UInt32 * ) serviceValue ) = GetMaxLogicalUnitNumber ( );
isSupported = true;
}
break;
case kSCSIProtocolFeature_MaximumReadTransferByteCount:
{
UInt32 maxByteCount = kDefaultMaximumByteCountRead;
if ( characterDict != NULL )
{
OSNumber * number = OSDynamicCast ( OSNumber, characterDict->getObject ( kIOMaximumByteCountReadKey ) );
if ( number != NULL)
{
maxByteCount = number->unsigned32BitValue ( );
}
}
*( ( UInt32 * ) serviceValue ) = maxByteCount;
isSupported = true;
}
break;
case kSCSIProtocolFeature_MaximumWriteTransferByteCount:
{
UInt32 maxByteCount = kDefaultMaximumByteCountWrite;
if ( characterDict != NULL )
{
OSNumber * number = OSDynamicCast ( OSNumber, characterDict->getObject ( kIOMaximumByteCountWriteKey ) );
if ( number != NULL)
{
maxByteCount = number->unsigned32BitValue ( );
}
}
*( ( UInt32 * ) serviceValue ) = maxByteCount;
isSupported = true;
}
break;
case kSCSIProtocolFeature_MaximumReadBlockTransferCount:
{
OSNumber * number = NULL;
require_quiet ( characterDict, Exit );
number = OSDynamicCast ( OSNumber, characterDict->getObject ( kIOMaximumBlockCountReadKey ) );
require_quiet ( number, Exit );
*( ( UInt32 * ) serviceValue ) = number->unsigned32BitValue ( );
isSupported = true;
}
break;
case kSCSIProtocolFeature_MaximumWriteBlockTransferCount:
{
OSNumber * number = NULL;
require_quiet ( characterDict, Exit );
number = OSDynamicCast ( OSNumber, characterDict->getObject ( kIOMaximumBlockCountWriteKey ) );
require_quiet ( number, Exit );
*( ( UInt32 * ) serviceValue ) = number->unsigned32BitValue ( );
isSupported = true;
}
break;
case kSCSIProtocolFeature_ProtocolSpecificPowerControl:
{
if ( fPortSuspendResumeForPMEnabled == true )
{
STATUS_LOG ( ( 6, "%s[%p]::IsProtocolServiceSupported - fPortSuspendResumeForPMEnabled enabled", getName ( ), this ) );
isSupported = true;
}
if ( characterDict != NULL )
{
if ( characterDict->getObject ( kIOPropertyAutonomousSpinDownKey ) != NULL )
{
STATUS_LOG ( ( 6, "%s[%p]::IsProtocolServiceSupported - fAutonomousSpinDownWorkAround enabled", getName ( ), this ) );
fAutonomousSpinDownWorkAround = true;
isSupported = true;
}
}
}
break;
default:
break;
}
Exit:
return isSupported;
}
bool
IOUSBMassStorageClass::HandleProtocolServiceFeature (
SCSIProtocolFeature feature,
void * serviceValue )
{
bool isSupported = false;
STATUS_LOG ( ( 6, "%s[%p]::HandleProtocolServiceFeature called, feature=%lu", getName(), this, ( UInt32 ) feature ) );
switch ( feature )
{
case kSCSIProtocolFeature_ProtocolSpecificPowerControl:
{
IOReturn status = kIOReturnError;
UInt32 value = * ( UInt32 * ) serviceValue;
if ( fPortSuspendResumeForPMEnabled == true )
{
if ( value == kSCSIProtocolPowerStateOff )
{
STATUS_LOG ( ( 6, "%s[%p]::HandleProtocolServiceFeature suspend port", getName(), this ) );
status = SuspendPort ( true );
require ( ( status == kIOReturnSuccess ), Exit );
}
if ( value == kSCSIProtocolPowerStateOn )
{
STATUS_LOG ( ( 6, "%s[%p]::HandleProtocolServiceFeature resume port", getName(), this ) );
status = SuspendPort ( false );
require ( ( status == kIOReturnSuccess ), Exit );
}
isSupported = true;
}
else if ( fAutonomousSpinDownWorkAround == true )
{
STATUS_LOG ( ( 6, "%s[%p]::HandleProtocolServiceFeature NOP START_STOP", getName(), this ) );
isSupported = true;
}
}
break;
default:
{
STATUS_LOG ( ( 6, "%s[%p]::HandleProtocolServiceFeature called for a feature we do not support", getName(), this ) );
}
break;
}
Exit:
return isSupported;
}
#pragma mark -
#pragma mark *** Standard USB Command Methods ***
#pragma mark -
IOReturn
IOUSBMassStorageClass::ClearFeatureEndpointStall (
IOUSBPipe * thePipe,
IOUSBCompletion * completion )
{
IOReturn status = kIOReturnInternalError;
thread_t thread = NULL;
STATUS_LOG((5, "%s[%p]: ClearFeatureEndpointStall Entered with thePipe=%p", getName(), this, thePipe ));
if ( ( fTerminating == true ) ||
( thePipe == NULL ) )
{
status = kIOReturnDeviceError;
goto Exit;
}
fPotentiallyStalledPipe = thePipe;
if ( GetInterfaceProtocol() == kProtocolBulkOnly )
{
require ( completion == &GetBulkOnlyRequestBlock()->boCompletion, Exit );
}
else {
require ( completion == &GetCBIRequestBlock()->cbiCompletion, Exit );
}
retain();
status = kernel_thread_start (
OSMemberFunctionCast ( thread_continue_t, this, &IOUSBMassStorageClass::ClearPipeStall ),
this,
&thread );
if ( status != kIOReturnSuccess )
{
release();
}
Exit:
STATUS_LOG ( ( 5, "%s[%p]: ClearFeatureEndpointStall returning status=0x%x thread=%p", getName(), this, status, thread ) );
return status;
}
IOReturn
IOUSBMassStorageClass::GetStatusEndpointStatus (
IOUSBPipe * thePipe,
void * endpointStatus,
IOUSBCompletion * completion )
{
IOReturn status;
if ( ( fTerminating == true ) ||
( thePipe == NULL ) )
{
return kIOReturnDeviceError;
}
bzero( &fUSBDeviceRequest, sizeof ( IOUSBDevRequest ) );
fUSBDeviceRequest.bmRequestType = USBmakebmRequestType ( kUSBIn, kUSBStandard, kUSBEndpoint );
fUSBDeviceRequest.bRequest = kUSBRqGetStatus;
fUSBDeviceRequest.wValue = 0; fUSBDeviceRequest.wIndex = thePipe->GetEndpointNumber();
if ( thePipe == GetBulkInPipe() )
{
fUSBDeviceRequest.wIndex |= 0x80;
}
fUSBDeviceRequest.wLength = 2;
fUSBDeviceRequest.pData = endpointStatus;
status = GetInterfaceReference()->DeviceRequest ( &fUSBDeviceRequest, completion );
STATUS_LOG ( ( 5, "%s[%p]: GetStatusEndpointStatus returned %x", getName(), this, status ) );
RecordUSBTimeStamp ( UMC_TRACE ( kGetEndPointStatus ),
( uintptr_t ) this, status,
thePipe->GetEndpointNumber(), NULL );
return status;
}
#pragma mark -
#pragma mark *** Accessor Methods For All Protocol Variables ***
#pragma mark -
IOUSBInterface *
IOUSBMassStorageClass::GetInterfaceReference ( void )
{
if ( fInterface == NULL )
{
STATUS_LOG ( ( 2, "%s[%p]: GetInterfaceReference - Interface is NULL.", getName(), this ) );
}
return fInterface;
}
void
IOUSBMassStorageClass::SetInterfaceReference ( IOUSBInterface * newInterface )
{
fInterface = newInterface;
}
UInt8
IOUSBMassStorageClass::GetInterfaceSubclass ( void )
{
return fPreferredSubclass;
}
UInt8
IOUSBMassStorageClass::GetInterfaceProtocol ( void )
{
return fPreferredProtocol;
}
IOUSBPipe *
IOUSBMassStorageClass::GetControlPipe ( void )
{
if ( fTerminating == true )
{
return NULL;
}
return GetInterfaceReference()->GetDevice()->GetPipeZero();
}
IOUSBPipe *
IOUSBMassStorageClass::GetBulkInPipe ( void )
{
return fBulkInPipe;
}
IOUSBPipe *
IOUSBMassStorageClass::GetBulkOutPipe ( void )
{
return fBulkOutPipe;
}
IOUSBPipe *
IOUSBMassStorageClass::GetInterruptPipe ( void )
{
return fInterruptPipe;
}
UInt8
IOUSBMassStorageClass::GetMaxLogicalUnitNumber ( void ) const
{
return fMaxLogicalUnitNumber;
}
void
IOUSBMassStorageClass::SetMaxLogicalUnitNumber ( UInt8 maxLUN )
{
fMaxLogicalUnitNumber = maxLUN;
}
#pragma mark -
#pragma mark *** Accessor Methods For CBI Protocol Variables ***
#pragma mark -
CBIRequestBlock *
IOUSBMassStorageClass::GetCBIRequestBlock ( void )
{
return &fCBICommandRequestBlock;
}
void
IOUSBMassStorageClass::ReleaseCBIRequestBlock ( CBIRequestBlock * cbiRequestBlock )
{
cbiRequestBlock->request = NULL;
return;
}
#pragma mark -
#pragma mark *** Accessor Methods For Bulk Only Protocol Variables ***
#pragma mark -
BulkOnlyRequestBlock *
IOUSBMassStorageClass::GetBulkOnlyRequestBlock ( void )
{
return &fBulkOnlyCommandRequestBlock;
}
void
IOUSBMassStorageClass::ReleaseBulkOnlyRequestBlock ( BulkOnlyRequestBlock * boRequestBlock )
{
boRequestBlock->request = NULL;
return;
}
UInt32
IOUSBMassStorageClass::GetNextBulkOnlyCommandTag ( void )
{
fBulkOnlyCommandTag++;
return fBulkOnlyCommandTag;
}
#pragma mark -
#pragma mark *** Miscellaneous Methods ***
#pragma mark -
IOReturn
IOUSBMassStorageClass::AcceptSCSITask ( SCSITaskIdentifier request, bool * accepted )
{
*accepted = false;
if ( isInactive ( ) || ( fDeviceAttached == false ) || ( fTerminating == true ) )
{
RecordUSBTimeStamp ( UMC_TRACE ( kNewCommandWhileTerminating ),
( uintptr_t ) this, ( uintptr_t ) request, NULL, NULL );
goto Exit;
}
if ( GetInterfaceProtocol ( ) == kProtocolBulkOnly )
{
if ( fBulkOnlyCommandStructInUse == true )
{
RecordUSBTimeStamp ( UMC_TRACE ( kBOCommandAlreadyInProgress ),
( uintptr_t ) this, ( uintptr_t ) request, NULL, NULL );
goto Exit;
}
fBulkOnlyCommandStructInUse = true;
}
else
{
if ( fCBICommandStructInUse == true )
{
RecordUSBTimeStamp ( UMC_TRACE ( kCBICommandAlreadyInProgress ),
( uintptr_t ) this, ( uintptr_t ) request, NULL, NULL );
goto Exit;
}
fCBICommandStructInUse = true;
}
*accepted = true;
Exit:
return kIOReturnSuccess;
}
void
IOUSBMassStorageClass::CheckDeferredTermination ( void )
{
require ( fWorkLoop->inGate ( ) == true, Exit );
if ( fTerminationDeferred == true )
{
fTerminationDeferred = false;
STATUS_LOG ( ( 6, "%s[%p]: CheckDeferredTermination: Waking didTerminate thread", getName(), this ) );
fCommandGate->commandWakeup ( &fTerminationDeferred, false );
}
Exit:
return;
}
void
IOUSBMassStorageClass::GatedCompleteSCSICommand (
SCSITaskIdentifier request,
SCSIServiceResponse * serviceResponse,
SCSITaskStatus * taskStatus )
{
require ( request != NULL, Exit );
require ( serviceResponse != NULL, Exit );
require ( taskStatus != NULL, Exit );
fBulkOnlyCommandStructInUse = false;
fCBICommandStructInUse = false;
fConsecutiveResetCount = 0;
STATUS_LOG ( ( 4, "%s[%p]: GatedCompleteSCSICommand request=%p serviceResponse=%d taskStatus=0x%02x", getName(), this, request, *serviceResponse, *taskStatus ) );
CommandCompleted ( request, *serviceResponse, *taskStatus );
CheckDeferredTermination ( );
Exit:
return;
}
IOReturn
IOUSBMassStorageClass::HandlePowerOn ( void )
{
UInt8 eStatus[2];
STATUS_LOG(( 6, "%s[%p]: HandlePowerOn", getName(), this ));
if ( ( GetStatusEndpointStatus ( GetBulkInPipe(), &eStatus[0], NULL ) != kIOReturnSuccess ) ||
( fRequiresResetOnResume == true ) )
{
RecordUSBTimeStamp ( UMC_TRACE ( kHandlePowerOnUSBReset ), ( uintptr_t ) this, NULL, NULL, NULL );
ResetDeviceNow( true );
}
fPortIsSuspended = false;
return kIOReturnSuccess;
}
bool
IOUSBMassStorageClass::handleOpen ( IOService * client,
IOOptionBits options,
void * arg )
{
bool result = false;
if ( GetMaxLogicalUnitNumber() == 0 )
{
result = super::handleOpen ( client, options, arg );
goto Exit;
}
require_nonzero ( fClients, ErrorExit );
require_nonzero ( OSDynamicCast ( IOSCSILogicalUnitNub, client ), ErrorExit );
result = fClients->setObject ( client );
Exit:
ErrorExit:
return result;
}
void
IOUSBMassStorageClass::handleClose ( IOService * client,
IOOptionBits options )
{
if ( GetMaxLogicalUnitNumber() == 0 )
{
super::handleClose ( client, options );
return;
}
require_nonzero ( fClients, Exit );
if ( fClients->containsObject ( client ) )
{
fClients->removeObject( client );
if ( ( fClients->getCount() == 0 ) && isInactive() )
{
message ( kIOMessageServiceIsRequestingClose, getProvider(), 0 );
}
}
Exit:
return;
}
bool
IOUSBMassStorageClass::handleIsOpen ( const IOService * client ) const
{
bool result = false;
UInt8 lun = GetMaxLogicalUnitNumber();
require_nonzero ( lun, CallSuperClass );
require_nonzero ( fClients, CallSuperClass );
if ( ( client == NULL ) && ( fClients->getCount ( ) != 0 ) )
{
result = true;
}
else
{
result = fClients->containsObject ( client );
}
return result;
CallSuperClass:
result = super::handleIsOpen ( client );
return result;
}
IOReturn
IOUSBMassStorageClass::sWaitForReset ( void * refcon )
{
return ( ( IOUSBMassStorageClass * ) refcon )->GatedWaitForReset();
}
IOReturn
IOUSBMassStorageClass::GatedWaitForReset ( void )
{
IOReturn status = kIOReturnSuccess;
while ( fResetInProgress == true )
{
status = fCommandGate->commandSleep ( &fResetInProgress, THREAD_UNINT );
}
return status;
}
IOReturn
IOUSBMassStorageClass::sWaitForTaskAbort ( void * refcon )
{
return kIOReturnUnsupported;
}
IOReturn
IOUSBMassStorageClass::GatedWaitForTaskAbort ( void )
{
return kIOReturnUnsupported;
}
void
IOUSBMassStorageClass::sResetDevice ( void * refcon )
{
IOUSBMassStorageClass * driver = NULL;
IOUSBInterface * interfaceRef = NULL;
IOUSBDevice * deviceRef = NULL;
IOReturn status = kIOReturnError;
thread_t thread = THREAD_NULL;
UInt32 deviceInfo = 0;
driver = ( IOUSBMassStorageClass * ) refcon;
require ( ( driver != NULL ), Exit );
STATUS_LOG ( ( 4, "%s[%p]: sResetDevice Entered", driver->getName ( ), driver ) );
if ( ( driver->fTerminating == true ) ||
( driver->isInactive ( ) == true ) )
{
STATUS_LOG ( ( 2, "%s[%p]: sResetDevice - We are being terminated!", driver->getName ( ), driver ) );
RecordUSBTimeStamp ( ( UMC_TRACE ( kUSBDeviceResetWhileTerminating ) | DBG_FUNC_START ), ( uintptr_t ) driver,
( unsigned int ) driver->fTerminating, ( unsigned int ) driver->isInactive ( ), NULL );
goto ErrorExit;
}
interfaceRef = driver->GetInterfaceReference ( );
require ( ( interfaceRef != NULL ), ErrorExit );
deviceRef = interfaceRef->GetDevice ( );
require ( ( deviceRef != NULL ), ErrorExit );
status = deviceRef->GetDeviceInformation ( &deviceInfo );
STATUS_LOG ( ( 5, "%s[%p]: GetDeviceInfo returned status = %x deviceInfo = %x", driver->getName ( ), driver, status, deviceInfo ) );
require_noerr ( status, ErrorExit );
if ( ( ( deviceInfo & kUSBInformationDeviceIsConnectedMask ) == 0 ) ||
( ( ( deviceInfo & kUSBInformationDeviceIsEnabledMask ) == 0 ) && ( ( deviceInfo & kUSBInformationDeviceIsInternalMask ) == 0 ) ) )
{
status = kIOReturnNoDevice;
RecordUSBTimeStamp ( UMC_TRACE ( kUSBDeviceResetAfterDisconnect ), ( uintptr_t ) driver, NULL, NULL, NULL );
goto ErrorExit;
}
if ( deviceInfo & kUSBInformationDeviceIsSuspendedMask )
{
driver->SuspendPort ( false );
}
status = deviceRef->ResetDevice();
STATUS_LOG ( ( 5, "%s[%p]: ResetDevice() returned = %x", driver->getName ( ), driver, status ) );
RecordUSBTimeStamp ( UMC_TRACE ( kUSBDeviceResetReturned ), ( uintptr_t ) driver, status, NULL, NULL );
require ( ( status == kIOReturnSuccess ), ErrorExit );
if ( driver->fBulkInPipe != NULL )
{
driver->fBulkInPipe->ClearPipeStall ( false );
}
if ( driver->fBulkOutPipe != NULL )
{
driver->fBulkOutPipe->ClearPipeStall ( false );
}
if ( driver->fInterruptPipe != NULL )
{
driver->fInterruptPipe->ClearPipeStall ( false );
}
ErrorExit:
STATUS_LOG ( ( 2, "%s[%p]: sResetDevice status=0x%x fResetInProgress=%d", driver->getName ( ), driver, status, driver->fResetInProgress ) );
if ( status != kIOReturnSuccess )
{
driver->fDeviceAttached = false;
driver->SendNotification_DeviceRemoved ( );
}
driver->AbortCurrentSCSITask ( );
if ( driver->fDeviceAttached == false && ( driver->isInactive() == false ) )
{
driver->terminate();
}
driver->fResetInProgress = false;
if ( driver->fWaitingForReconfigurationMessage == false )
{
driver->fCommandGate->commandWakeup ( &driver->fResetInProgress, false );
}
STATUS_LOG ( ( 6, "%s[%p]: sResetDevice exiting.", driver->getName ( ), driver ) );
driver->release();
thread = current_thread ( );
require ( ( thread != THREAD_NULL ), Exit );
thread_deallocate ( thread );
thread_terminate ( thread );
Exit:
return;
}
void
IOUSBMassStorageClass::sAbortCurrentSCSITask ( void * refcon )
{
}
void
IOUSBMassStorageClass::ClearPipeStall( void )
{
IOReturn status = kIOReturnBadArgument;
IOUSBPipe * pipe;
UInt8 endpointNumber;
IOUSBCompletion * completion;
IOUSBInterface * interfaceRef;
thread_t thread;
STATUS_LOG ( ( 4, "%s[%p]: ClearPipeStall Entered with endpoint %d", getName ( ), this, fPotentiallyStalledPipe ? fPotentiallyStalledPipe->GetEndpointNumber() : -1 ) );
pipe = fPotentiallyStalledPipe;
require ( ( pipe != NULL ), Exit );
endpointNumber = pipe->GetEndpointNumber();
if ( pipe->GetDirection() == kUSBIn )
{
endpointNumber |= 0x80;
}
if ( fTerminating == true )
{
status = kIOReturnOffline;
goto Exit;
}
status = pipe->ClearPipeStall ( false );
require_success ( status, Exit );
bzero ( &fUSBDeviceRequest, sizeof ( IOUSBDevRequest ) );
fUSBDeviceRequest.bmRequestType = USBmakebmRequestType ( kUSBNone, kUSBStandard, kUSBEndpoint );
fUSBDeviceRequest.bRequest = kUSBRqClearFeature;
fUSBDeviceRequest.wValue = kUSBFeatureEndpointStall;
fUSBDeviceRequest.wIndex = endpointNumber;
if ( GetInterfaceProtocol() == kProtocolBulkOnly )
{
completion = &GetBulkOnlyRequestBlock()->boCompletion;
}
else
{
completion = &GetCBIRequestBlock()->cbiCompletion;
}
interfaceRef = GetInterfaceReference();
require ( interfaceRef != NULL, Exit );
status = interfaceRef->DeviceRequest( &fUSBDeviceRequest, completion );
RecordUSBTimeStamp ( UMC_TRACE ( kClearEndPointStall ), ( uintptr_t ) this, status,
pipe->GetEndpointNumber(), NULL );
Exit:
STATUS_LOG ( ( 5, "%s[%p]: ClearPipeStall Returning with status=0x%x", getName(), this, status ) );
release();
thread = current_thread ( );
thread_deallocate ( thread );
thread_terminate ( thread );
}
OSMetaClassDefineReservedUsed ( IOUSBMassStorageClass, 1 );
IOReturn
IOUSBMassStorageClass::StartDeviceRecovery ( void )
{
UInt8 eStatus[2];
IOReturn status = kIOReturnError;
STATUS_LOG ( ( 5, "%s[%p]: StartDeviceRecovery", getName(), this ) );
if ( fBulkOnlyCommandStructInUse == true )
{
fBulkOnlyCommandRequestBlock.boCompletion.target = this;
fBulkOnlyCommandRequestBlock.boCompletion.action = &this->DeviceRecoveryCompletionAction;
status = GetStatusEndpointStatus ( GetBulkInPipe(), &eStatus[0], &fBulkOnlyCommandRequestBlock.boCompletion);
}
else if ( fCBICommandStructInUse == true )
{
fCBICommandRequestBlock.cbiCompletion.target = this;
fCBICommandRequestBlock.cbiCompletion.action = &this->DeviceRecoveryCompletionAction;
status = GetStatusEndpointStatus ( GetBulkInPipe(), &eStatus[0], &fCBICommandRequestBlock.cbiCompletion);
}
return status;
}
OSMetaClassDefineReservedUsed ( IOUSBMassStorageClass, 2 );
void
IOUSBMassStorageClass::FinishDeviceRecovery ( IOReturn status )
{
ResetDeviceNow( false );
}
void
IOUSBMassStorageClass::DeviceRecoveryCompletionAction (
void * target,
void * parameter,
IOReturn status,
UInt32 bufferSizeRemaining)
{
UNUSED ( parameter );
UNUSED ( bufferSizeRemaining );
IOUSBMassStorageClass * theMSC;
theMSC = ( IOUSBMassStorageClass * ) target;
theMSC->FinishDeviceRecovery ( status );
}
void
IOUSBMassStorageClass::ResetDeviceNow ( bool waitForReset )
{
thread_t thread = THREAD_NULL;
kern_return_t result = KERN_FAILURE;
require ( ( fTerminating == false ), Exit );
require ( ( isInactive ( ) == false ), Exit );
retain ( );
STATUS_LOG ( ( 4, "%s[%p]: ResetDeviceNow waitForReset=%d fConsecutiveResetCount=%d", getName(), this, waitForReset, fConsecutiveResetCount ) );
fResetInProgress = true;
fWaitingForReconfigurationMessage = !waitForReset;
result = kernel_thread_start ( ( thread_continue_t ) &IOUSBMassStorageClass::sResetDevice,
this,
&thread );
require ( ( result == KERN_SUCCESS ), ErrorExit );
if ( waitForReset == true )
{
fCommandGate->runAction ( ( IOCommandGate::Action ) &IOUSBMassStorageClass::sWaitForReset );
}
Exit:
return;
ErrorExit:
fResetInProgress = false;
release ( );
}
void
IOUSBMassStorageClass::AbortCurrentSCSITask ( void )
{
SCSITaskIdentifier currentTask = NULL;
if ( fWorkLoop->inGate ( ) == false )
{
fCommandGate->runAction (
OSMemberFunctionCast (
IOCommandGate::Action,
this,
&IOUSBMassStorageClass::AbortCurrentSCSITask ) );
return;
}
STATUS_LOG ( ( 4, "%s[%p]: AbortCurrentSCSITask Entered", getName(), this ) );
if( fBulkOnlyCommandStructInUse == true )
{
currentTask = fBulkOnlyCommandRequestBlock.request;
}
else if( fCBICommandStructInUse == true )
{
currentTask = fCBICommandRequestBlock.request;
}
if ( currentTask != NULL )
{
fBulkOnlyCommandStructInUse = false;
fCBICommandStructInUse = false;
fBulkOnlyCommandRequestBlock.request = NULL;
fCBICommandRequestBlock.request = NULL;
fConsecutiveResetCount++;
if ( ( fConsecutiveResetCount > kMaxConsecutiveResets ) && ( fDeviceAttached == true ) )
{
IOLog ( "%s[%p]: The device is still unresponsive after %u consecutive USB Device Resets; it will be terminated.\n", getName(), this, fConsecutiveResetCount );
fDeviceAttached = false;
}
else
{
STATUS_LOG ( ( 4, "%s[%p]: AbortCurrentSCSITask fConsecutiveResetCount=%u", getName(), this, fConsecutiveResetCount ) );
}
if ( fDeviceAttached == false )
{
STATUS_LOG ( ( 1, "%s[%p]: AbortCurrentSCSITask Aborting currentTask=%p with device not present.", getName(), this, currentTask ) );
fTerminating = true;
SendNotification_DeviceRemoved ( );
CommandCompleted ( currentTask, kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE, kSCSITaskStatus_DeviceNotPresent );
}
else
{
STATUS_LOG ( ( 1, "%s[%p]: AbortCurrentSCSITask Aborting currentTask=%p with delivery failure.", getName(), this, currentTask ) );
CommandCompleted ( currentTask, kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE, kSCSITaskStatus_DeliveryFailure );
}
CheckDeferredTermination ( );
}
STATUS_LOG ( ( 4, "%s[%p]: AbortCurrentSCSITask Exiting", getName(), this ) );
}
bool
IOUSBMassStorageClass::IsPhysicalInterconnectLocationInternal ( void )
{
IOReturn status = kIOReturnError;
IOUSBInterface * usbInterface = NULL;
IOUSBDevice * usbDevice = NULL;
UInt32 deviceInformation = 0;
bool internal = false;
usbInterface = GetInterfaceReference();
require ( ( usbInterface != NULL ), ErrorExit );
usbDevice = usbInterface->GetDevice();
require ( ( usbDevice != NULL ), ErrorExit );
status = usbDevice->GetDeviceInformation ( &deviceInformation );
require_success ( status, ErrorExit );
if ( ( deviceInformation & ( 1 << kUSBInformationDeviceIsCaptiveBit ) ) ||
( deviceInformation & ( 1 << kUSBInformationDeviceIsInternalBit ) ) )
{
internal = true;
}
ErrorExit:
return internal;
}
IOReturn
IOUSBMassStorageClass::SuspendPort ( bool suspend )
{
STATUS_LOG ( ( 4, "%s[%p]: SuspendPort called!", getName(), this ) );
IOReturn status = kIOReturnError;
IOUSBInterface * usbInterfaceRef = NULL;
IOUSBDevice * usbDeviceRef = NULL;
if ( suspend == fPortIsSuspended )
{
STATUS_LOG ( ( 4, "%s[%p]: SuspendPort !!!ALREADY!!! in desired state.", getName(), this ) );
status = kIOReturnSuccess;
goto Exit;
}
usbInterfaceRef = GetInterfaceReference();
require ( usbInterfaceRef, Exit );
usbDeviceRef = usbInterfaceRef->GetDevice();
require ( usbDeviceRef, Exit );
if ( suspend == true )
{
STATUS_LOG ( ( 6, "%s[%p]::SuspendPort suspend port", getName(), this ) );
status = usbDeviceRef->SuspendDevice ( true );
require ( ( status == kIOReturnSuccess ), Exit );
fPortIsSuspended = true;
}
if ( suspend == false )
{
STATUS_LOG ( ( 6, "%s[%p]::SuspendPort resume port", getName(), this ) );
status = usbDeviceRef->SuspendDevice ( false );
require ( ( status == kIOReturnSuccess ), Exit );
IOSleep ( 15 );
fPortIsSuspended = false;
}
Exit:
return status;
}
#pragma mark
#pragma mark *** Reserved for future expansion ***
#pragma mark
OSMetaClassDefineReservedUnused( IOUSBMassStorageClass, 3 );
OSMetaClassDefineReservedUnused( IOUSBMassStorageClass, 4 );
OSMetaClassDefineReservedUnused( IOUSBMassStorageClass, 5 );
OSMetaClassDefineReservedUnused( IOUSBMassStorageClass, 6 );
OSMetaClassDefineReservedUnused( IOUSBMassStorageClass, 7 );
OSMetaClassDefineReservedUnused( IOUSBMassStorageClass, 8 );
OSMetaClassDefineReservedUnused( IOUSBMassStorageClass, 9 );
OSMetaClassDefineReservedUnused( IOUSBMassStorageClass, 10 );
OSMetaClassDefineReservedUnused( IOUSBMassStorageClass, 11 );
OSMetaClassDefineReservedUnused( IOUSBMassStorageClass, 12 );
OSMetaClassDefineReservedUnused( IOUSBMassStorageClass, 13 );
OSMetaClassDefineReservedUnused( IOUSBMassStorageClass, 14 );
OSMetaClassDefineReservedUnused( IOUSBMassStorageClass, 15 );
OSMetaClassDefineReservedUnused( IOUSBMassStorageClass, 16 );