IOUSBMassStorageClass.cpp [plain text]
#include <libkern/OSByteOrder.h>
#include "IOUSBMassStorageClass.h"
#include "Debugging.h"
#include <IOKit/scsi-commands/IOSCSIPeripheralDeviceNub.h>
#if (USB_MASS_STORAGE_DEBUG == 1)
#define PANIC_NOW(x) IOPanic x
#define DEBUG_LEVEL 1
#include <IOKit/usb/IOUSBLog.h>
#define STATUS_LOG(x) USBLog x
#else
#define STATUS_LOG(x)
#define PANIC_NOW(x)
#endif
#define DEBUGGING_LEVEL 0
#define DEBUGLOG kprintf
#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;
if( super::start( provider ) == false )
{
STATUS_LOG((1, "%s: superclass start failure.", getName()));
return false;
}
reserved = ( ExpansionData * ) IOMalloc ( sizeof ( ExpansionData ) );
bzero ( reserved, sizeof ( ExpansionData ) );
SetInterfaceReference( OSDynamicCast( IOUSBInterface, provider) );
if ( GetInterfaceReference() == NULL )
{
STATUS_LOG((1, "%s: the provider is not an IOUSBInterface object",
getName()));
return false;
}
STATUS_LOG((6, "%s: USB Mass Storage @ %d",
getName(),
GetInterfaceReference()->GetDevice()->GetAddress()));
if ( GetInterfaceReference()->open( this ) == false)
{
STATUS_LOG((1, "%s: could not open the interface", getName()));
return false;
}
fBulkInPipe = NULL;
fBulkOutPipe = NULL;
fInterruptPipe = NULL;
fClients = NULL;
SetMaxLogicalUnitNumber( 0 );
fBulkOnlyCommandTag = 0;
fBulkOnlyCommandStructInUse = false;
fCBICommandStructInUse = false;
if ( getProperty( kIOUSBMassStorageCharacteristics ) == NULL )
{
fPreferredProtocol = GetInterfaceReference()->GetInterfaceProtocol();
fPreferredSubclass = GetInterfaceReference()->GetInterfaceSubClass();
}
else
{
OSDictionary * characterDict;
characterDict = OSDynamicCast( OSDictionary,
getProperty( kIOUSBMassStorageCharacteristics ));
if ( characterDict->getObject( kIOUSBMassStoragePreferredProtocol )
== NULL )
{
fPreferredProtocol =
GetInterfaceReference()->GetInterfaceProtocol();
}
else
{
OSNumber * preferredProtocol;
preferredProtocol = OSDynamicCast( OSNumber,
characterDict->getObject( kIOUSBMassStoragePreferredProtocol ));
fPreferredProtocol = preferredProtocol->unsigned32BitValue();
}
if ( characterDict->getObject( kIOUSBMassStoragePreferredSubclass ) == NULL )
{
fPreferredSubclass =
GetInterfaceReference()->GetInterfaceSubClass();
}
else
{
OSNumber * preferredSubclass;
preferredSubclass = OSDynamicCast( OSNumber, characterDict->getObject( kIOUSBMassStoragePreferredSubclass ));
fPreferredSubclass = preferredSubclass->unsigned32BitValue();
}
}
STATUS_LOG((6, "%s: Preferred Protocol is: %d", getName(), fPreferredProtocol));
STATUS_LOG((6, "%s: Preferred Subclass is: %d", getName(), fPreferredSubclass));
STATUS_LOG((7, "%s: Configure the Storage interface", getName()));
switch ( GetInterfaceProtocol() )
{
case kProtocolControlBulkInterrupt:
{
request.type = kUSBInterrupt;
request.direction = kUSBIn;
fInterruptPipe = GetInterfaceReference()->FindNextPipe(NULL, &request);
STATUS_LOG((7, "%s: find interrupt pipe", getName()));
if(( GetInterfaceProtocol() == kProtocolControlBulkInterrupt)
&& (fInterruptPipe == 0))
{
STATUS_LOG((1, "%s: No interrupt pipe for CBI, abort", getName()));
goto abortStart;
}
}
break;
case kProtocolControlBulk:
case kProtocolBulkOnly:
{
STATUS_LOG((7, "%s: Bulk Only - skip interrupt pipe",
getName()));
fInterruptPipe = NULL;
}
break;
default:
{
goto abortStart;
}
break;
}
STATUS_LOG((7, "%s: find bulk in pipe", getName()));
request.type = kUSBBulk;
request.direction = kUSBIn;
fBulkInPipe = GetInterfaceReference()->FindNextPipe(NULL, &request);
if ( fBulkInPipe == NULL )
{
STATUS_LOG((1, "%s: No bulk in pipe found, aborting",
getName()));
goto abortStart;
}
STATUS_LOG((7, "%s: find bulk out pipe", getName()));
request.type = kUSBBulk;
request.direction = kUSBOut;
fBulkOutPipe = GetInterfaceReference()->FindNextPipe(NULL, &request);
if ( fBulkOutPipe == NULL )
{
STATUS_LOG((1, "%s: No bulk out pipe found, aborting",
getName()));
goto abortStart;
}
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 );
}
obj = getProperty ( kIOPropertyReadTimeOutDurationKey );
if ( obj != NULL );
{
characterDict->setObject ( kIOPropertyReadTimeOutDurationKey, obj );
}
obj = getProperty ( kIOPropertyWriteTimeOutDurationKey );
if ( obj != NULL );
{
characterDict->setObject ( kIOPropertyWriteTimeOutDurationKey, obj );
}
setProperty ( kIOPropertyProtocolCharacteristicsKey, characterDict );
characterDict->release ( );
STATUS_LOG((6, "%s: successfully configured", getName()));
InitializePowerManagement( GetInterfaceReference() );
BeginProvidedServices();
return true;
abortStart:
STATUS_LOG((1, "%s: aborting startup. Stop the provider.", getName() ));
stop( provider );
return false;
}
void
IOUSBMassStorageClass::stop(IOService * provider)
{
STATUS_LOG((1, "%s: Bye bye!", getName()));
EndProvidedServices();
fBulkInPipe = NULL;
fBulkOutPipe = NULL;
fInterruptPipe = NULL;
super::stop(provider);
}
void
IOUSBMassStorageClass::free ( void )
{
if ( reserved != NULL )
{
if ( fClients != NULL )
{
fClients->release();
fClients = NULL;
}
IOFree ( reserved, sizeof ( ExpansionData ) );
reserved = NULL;
}
super::free ( );
}
IOReturn
IOUSBMassStorageClass::message( UInt32 type, IOService * provider, void * argument )
{
IOReturn result;
STATUS_LOG ( (4, "%s: message = %lx called", getName(), type ) );
switch( type )
{
case kIOMessageServiceIsTerminated:
{
IOUSBInterface * currentInterface;
STATUS_LOG((2, "%s: message kIOMessageServiceIsTerminated.", getName() ));
currentInterface = GetInterfaceReference();
if ( currentInterface != NULL )
{
STATUS_LOG((2, "%s: message kIOMessageServiceIsTerminated interface is non NULL.", getName() ));
SetInterfaceReference( NULL );
currentInterface->close(this);
SendNotification_DeviceRemoved( );
}
result = kIOReturnSuccess;
}
break;
case kIOMessageServiceIsRequestingClose:
{
IOUSBInterface * currentInterface;
STATUS_LOG((2, "%s: message kIOMessageServiceIsRequestingClose.", getName() ));
SendNotification_DeviceRemoved( );
currentInterface = GetInterfaceReference();
if ( currentInterface != NULL )
{
SetInterfaceReference( NULL );
currentInterface->close(this);
}
result = kIOReturnSuccess;
}
break;
default:
{
result = super::message( type, provider, argument );
}
}
return result;
}
bool
IOUSBMassStorageClass::BeginProvidedServices( void )
{
STATUS_LOG((7, "%s: Determine the maximum LUN", getName()));
if( GetInterfaceProtocol() == kProtocolBulkOnly )
{
IOReturn status;
bool maxLUNDetermined = false;
if( getProperty( kIOUSBMassStorageCharacteristics ) != NULL )
{
OSDictionary * characterDict = OSDynamicCast( OSDictionary,
getProperty( kIOUSBMassStorageCharacteristics ));
if( characterDict->getObject( kIOUSBMassStorageMaxLogicalUnitNumber ) != NULL )
{
OSNumber * maxLUN = OSDynamicCast( OSNumber,
characterDict->getObject( kIOUSBMassStorageMaxLogicalUnitNumber ) );
if( maxLUN != NULL )
{
SetMaxLogicalUnitNumber( maxLUN->unsigned8BitValue() );
STATUS_LOG((4, "%s: Number of LUNs %u.", getName(), 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: Issuing device request to find max LUN", getName() ));
status = GetInterfaceReference()->DeviceRequest( &fUSBDeviceRequest );
STATUS_LOG((4, "%s: DeviceRequest returned status = %x", getName(), status ));
if ( status != kIOReturnSuccess )
{
SetMaxLogicalUnitNumber( 0 );
if( ( status == kIOUSBPipeStalled ) && ( clearPipeAttempts < 3 ) )
{
UInt8 eStatus[2];
STATUS_LOG((4, "%s: calling GetStatusEndpointStatus to clear stall", getName()));
GetStatusEndpointStatus( GetControlPipe(), &eStatus[0], NULL);
clearPipeAttempts++;
}
else if ( ( status == kIOReturnNotResponding ) && ( triedReset == false ) )
{
retain();
STATUS_LOG((4, "%s: BeginProvidedServices: device not responding, reseting.", getName() ));
fResetInProgress = true;
IOCreateThread( IOUSBMassStorageClass::sResetDevice, this );
fCommandGate->runAction ( ( IOCommandGate::Action ) &IOUSBMassStorageClass::sWaitForReset );
triedReset = true;
}
else
{
break;
}
}
}
}
}
else
{
SetMaxLogicalUnitNumber( 0 );
}
STATUS_LOG((5, "%s: Maximum supported LUN is: %d",
getName(),
GetMaxLogicalUnitNumber() ));
STATUS_LOG((7, "%s: successfully configured", getName()));
if ( GetMaxLogicalUnitNumber() == 0 )
{
registerService(kIOServiceAsynchronous);
fClients = NULL;
}
else
{
fClients = OSSet::withCapacity( GetMaxLogicalUnitNumber() + 1 );
for( int loopLUN = 0; loopLUN <= GetMaxLogicalUnitNumber(); loopLUN++ )
{
STATUS_LOG((6, "IOUSBMassStorageClass::CreatePeripheralDeviceNubForLUN entering." ));
IOSCSILogicalUnitNub * nub = OSTypeAlloc( IOSCSILogicalUnitNub );
if( nub == NULL )
{
PANIC_NOW(( "IOUSBMassStorageClass::CreatePeripheralDeviceNubForLUN failed" ));
return false;
}
nub->init( 0 );
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();
STATUS_LOG((6, "IOUSBMassStorageClass::CreatePeripheralDeviceNubForLUN exiting." ));
}
}
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;
*taskStatus = kSCSITaskStatus_No_Status;
*serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
STATUS_LOG((6, "%s: SendSCSICommand was called", getName()));
if ( isInactive () )
{
return false;
}
if ( request == NULL )
{
STATUS_LOG((1, "%s: SendSCSICommand was called with a NULL CDB",
getName()));
return true;
}
if ( GetInterfaceReference() == NULL )
{
return true;
}
#if (USB_MASS_STORAGE_DEBUG == 1)
SCSICommandDescriptorBlock cdbData;
STATUS_LOG((4, "%s: SendSCSICommand CDB data: ", getName()));
GetCommandDescriptorBlock( request, &cdbData );
if ( GetCommandDescriptorBlockSize ( request ) == kSCSICDBSize_6Byte )
STATUS_LOG((4, "%X : %X : %X : %X : %X : %X",
cdbData[0], cdbData[1], cdbData[2], cdbData[3],
cdbData[4], cdbData[5]));
else if ( GetCommandDescriptorBlockSize ( request ) == kSCSICDBSize_10Byte )
STATUS_LOG((4, "%X : %X : %X : %X : %X : %X : %X : %X : %X : %X",
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, "%X : %X : %X : %X : %X : %X : %X : %X : %X : %X : %X : %X",
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_6Byte )
STATUS_LOG((4, "%X : %X : %X : %X : %X : %X : %X : %X : %X : %X : %X : %X : %X : %X : %X : %X",
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)
{
if ( fBulkOnlyCommandStructInUse == true )
{
return false;
}
fBulkOnlyCommandStructInUse = true;
STATUS_LOG((6, "%s: SendSCSICommandforBulkOnlyProtocol sent", getName() ));
status = SendSCSICommandForBulkOnlyProtocol( request );
if( status != kIOReturnSuccess )
{
fBulkOnlyCommandStructInUse = false;
}
STATUS_LOG((5, "%s: SendSCSICommandforBulkOnlyProtocol returned %x",
getName(),
status));
}
else
{
if ( fCBICommandStructInUse == true )
{
return false;
}
fCBICommandStructInUse = true;
status = SendSCSICommandForCBIProtocol( request );
if( status != kIOReturnSuccess )
{
fCBICommandStructInUse = false;
}
STATUS_LOG((5, "%s: SendSCSICommandforCBIProtocol returned %x",
getName(),
status));
}
if ( status == kIOReturnSuccess )
{
*serviceResponse = kSCSIServiceResponse_Request_In_Process;
}
return true;
}
void
IOUSBMassStorageClass::CompleteSCSICommand( SCSITaskIdentifier request, IOReturn status )
{
fBulkOnlyCommandStructInUse = false;
fCBICommandStructInUse = false;
if ( status == kIOReturnSuccess )
{
CommandCompleted( request, kSCSIServiceResponse_TASK_COMPLETE, kSCSITaskStatus_GOOD );
}
else
{
CommandCompleted( request, kSCSIServiceResponse_TASK_COMPLETE, kSCSITaskStatus_CHECK_CONDITION );
}
}
SCSIServiceResponse
IOUSBMassStorageClass::AbortSCSICommand( SCSITaskIdentifier abortTask )
{
IOReturn status = kIOReturnSuccess;
STATUS_LOG((6, "%s: AbortSCSICommand was called", getName()));
if ( abortTask == NULL )
{
STATUS_LOG((1, "%s: AbortSCSICommand was called with a NULL CDB object",
getName()));
return kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
}
if ( GetInterfaceReference() == NULL )
{
STATUS_LOG((1, "%s: AbortSCSICommand was called with a NULL interface",
getName()));
return kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
}
if(GetInterfaceReference()->GetInterfaceProtocol() == kProtocolBulkOnly)
{
status = AbortSCSICommandForBulkOnlyProtocol( abortTask );
STATUS_LOG((5, "%s: abortCDBforBulkOnlyProtocol returned %x",
getName(),
status));
}
else
{
status = AbortSCSICommandForCBIProtocol( abortTask );
STATUS_LOG((5, "%s: abortCDBforCBIProtocol returned %x",
getName(),
status));
}
return kSCSIServiceResponse_FUNCTION_REJECTED;
}
bool
IOUSBMassStorageClass::IsProtocolServiceSupported(
SCSIProtocolFeature feature,
void * serviceValue )
{
bool isSupported;
STATUS_LOG((6, "IOUSBMassStorageClass::IsProtocolServiceSupported called"));
switch( feature )
{
case kSCSIProtocolFeature_GetMaximumLogicalUnitNumber:
{
*((UInt32 *) serviceValue) = GetMaxLogicalUnitNumber();
isSupported = true;
}
break;
default:
{
isSupported = false;
}
break;
}
return isSupported;
}
bool
IOUSBMassStorageClass::HandleProtocolServiceFeature(
SCSIProtocolFeature feature,
void * serviceValue )
{
UNUSED( feature );
UNUSED( serviceValue );
return false;
}
#pragma mark -
#pragma mark *** Standard USB Command Methods ***
#pragma mark -
IOReturn
IOUSBMassStorageClass::ClearFeatureEndpointStall(
IOUSBPipe * thePipe,
IOUSBCompletion * completion )
{
IOReturn status;
if ( ( GetInterfaceReference() == NULL ) ||
( thePipe == NULL ) )
{
return kIOReturnDeviceError;
}
thePipe->Reset();
bzero( &fUSBDeviceRequest, sizeof(IOUSBDevRequest));
fUSBDeviceRequest.bmRequestType = USBmakebmRequestType(kUSBNone, kUSBStandard, kUSBEndpoint);
fUSBDeviceRequest.bRequest = kUSBRqClearFeature;
fUSBDeviceRequest.wValue = 0; fUSBDeviceRequest.wIndex = thePipe->GetEndpointNumber();
if ( thePipe == GetBulkInPipe() )
{
fUSBDeviceRequest.wIndex |= 0x80;
}
status = GetInterfaceReference()->DeviceRequest( &fUSBDeviceRequest, completion );
STATUS_LOG((5, "%s: ClearFeatureEndpointStall returned %x",
getName(),
status));
return status;
}
IOReturn
IOUSBMassStorageClass::GetStatusEndpointStatus(
IOUSBPipe * thePipe,
void * endpointStatus,
IOUSBCompletion * completion )
{
IOReturn status;
if ( ( GetInterfaceReference() == NULL ) ||
( 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: GetStatusEndpointStatus returned %x",
getName(),
status));
return status;
}
#pragma mark -
#pragma mark *** Accessor Methods For All Protocol Variables ***
#pragma mark -
IOUSBInterface *
IOUSBMassStorageClass::GetInterfaceReference( void )
{
STATUS_LOG((7, "%s: GetInterfaceReference", getName() ));
if ( fInterface == NULL )
{
STATUS_LOG((2, "%s: GetInterfaceReference - Interface is NULL.", getName() ));
}
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 ( GetInterfaceReference() == NULL )
{
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;
}
IOReturn
IOUSBMassStorageClass::HandlePowerOn( void )
{
UInt8 eStatus[2];
bool knownResetOnResumeDevice = false;
STATUS_LOG((6, "%s: HandlePowerOn", getName() ));
if ( getProperty( kIOUSBMassStorageCharacteristics ) != NULL )
{
OSDictionary * characterDict = OSDynamicCast(
OSDictionary,
getProperty( kIOUSBMassStorageCharacteristics ));
if ( characterDict->getObject( kIOUSBMassStorageResetOnResume ) != NULL )
{
STATUS_LOG((4, "%s: knownResetOnResumeDevice", getName() ));
knownResetOnResumeDevice = true;
}
}
if ( ( ( GetStatusEndpointStatus( GetBulkInPipe(), &eStatus[0], NULL) != kIOReturnSuccess ) ||
( knownResetOnResumeDevice == true ) ) &&
( isInactive() == false ) )
{
retain();
STATUS_LOG((4, "%s: kIOMessageServiceIsResumed GetStatusEndpointStatus error or knownResetOnResumeDevice.", getName() ));
fResetInProgress = true;
IOCreateThread( IOUSBMassStorageClass::sResetDevice, this );
fCommandGate->runAction ( ( IOCommandGate::Action ) &IOUSBMassStorageClass::sWaitForReset );
}
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;
if ( fResetInProgress == true )
{
status = fCommandGate->commandSleep( &fResetInProgress, THREAD_UNINT );
}
return status;
}
void
IOUSBMassStorageClass::sResetDevice( void * refcon )
{
IOUSBMassStorageClass * driver;
IOReturn status = kIOReturnSuccess;
driver = ( IOUSBMassStorageClass * ) refcon;
STATUS_LOG((4, "IOUSBMassStorageClass: sResetDevice" ));
if ( ( driver->GetInterfaceReference() == NULL ) ||
( driver->isInactive( ) == true ) )
{
STATUS_LOG((2, "IOUSBMassStorageClass: sResetDevice - We are being terminated!" ));
goto ErrorExit;
}
status = driver->GetInterfaceReference()->GetDevice()->ResetDevice();
STATUS_LOG((5, "IOUSBMassStorageClass: ResetDevice() returned status = %d", status ));
if ( ( driver->GetInterfaceReference() == NULL ) ||
( driver->isInactive( ) == true ) ||
( status != kIOReturnSuccess ) )
{
STATUS_LOG((2, "IOUSBMassStorageClass: sResetDevice - We are being terminated!" ));
goto ErrorExit;
}
if ( driver->GetBulkInPipe() != NULL )
{
driver->GetBulkInPipe()->Reset();
}
if ( driver->GetBulkOutPipe() != NULL )
{
driver->GetBulkOutPipe()->Reset();
}
driver->SendNotification_VerifyDeviceState();
ErrorExit:
driver->fResetInProgress = false;
driver->fCommandGate->commandWakeup( &driver->fResetInProgress, false );
driver->release();
STATUS_LOG((6,"IOUSBMassStorageClass: sResetDevice returned" ));
return;
}
OSMetaClassDefineReservedUsed( IOUSBMassStorageClass, 1 );
IOReturn
IOUSBMassStorageClass::StartDeviceRecovery( void )
{
UInt8 eStatus[2];
IOReturn status = kIOReturnError;
STATUS_LOG((5, "%s: StartDeviceRecovery", getName() ));
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 )
{
SCSITaskIdentifier tempTask = NULL;
if ( status != kIOReturnSuccess)
{
STATUS_LOG((4, "%s: StartDeviceRecovery GetStatusEndpointStatus error. status = %d", getName(), status ));
if ( GetInterfaceReference() == NULL )
{
if( fBulkOnlyCommandStructInUse == true )
{
tempTask = fBulkOnlyCommandRequestBlock.request;
}
else if ( fCBICommandStructInUse == true )
{
tempTask = fCBICommandRequestBlock.request;
}
goto ErrorExit;
}
status = (GetInterfaceReference()->GetDevice())->ResetDevice();
SendNotification_VerifyDeviceState();
}
if( status == kIOReturnSuccess )
{
if( fBulkOnlyCommandStructInUse == true )
{
tempTask = fBulkOnlyCommandRequestBlock.request;
STATUS_LOG((6, "%s: FinishDeviceRecovery SendSCSICommandforBulkOnlyProtocol sent", getName() ));
status = SendSCSICommandForBulkOnlyProtocol( tempTask );
STATUS_LOG((5, "%s: FinishDeviceRecovery SendSCSICommandforBulkOnlyProtocol returned %x",
getName(),
status));
if( status != kIOReturnSuccess)
{
STATUS_LOG((6, "%s: FinishDeviceRecovery SendSCSICommandforBulkOnlyProtocol status != kIOReturnSuccess ", getName() ));
goto ErrorExit;
}
}
else if ( fCBICommandStructInUse == true )
{
tempTask = fCBICommandRequestBlock.request;
STATUS_LOG((6, "%s: FinishDeviceRecovery SendSCSICommandforCBIProtocol sent", getName() ));
status = SendSCSICommandForCBIProtocol( tempTask );
STATUS_LOG((5, "%s: FinishDeviceRecovery SendSCSICommandforCBIProtocol returned %x",
getName(),
status));
if( status != kIOReturnSuccess )
{
goto ErrorExit;
}
}
}
return;
ErrorExit:
if ( tempTask != NULL )
{
RejectTask( tempTask );
}
}
void
IOUSBMassStorageClass::DeviceRecoveryCompletionAction(
void * target,
void * parameter,
IOReturn status,
UInt32 bufferSizeRemaining)
{
UNUSED( parameter );
UNUSED( bufferSizeRemaining );
IOUSBMassStorageClass * theMSC;
theMSC = (IOUSBMassStorageClass *) target;
theMSC->FinishDeviceRecovery( status );
}
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 );