IOUSBMassStorageClass.cpp [plain text]
#include <libkern/OSByteOrder.h>
#include "IOUSBMassStorageClass.h"
#include "Debugging.h"
#include <IOKit/scsi/IOSCSIPeripheralDeviceNub.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/IOKitKeys.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;
IOReturn result = kIOReturnError;
if( super::start( provider ) == false )
{
STATUS_LOG(( 1, "%s[%p]: superclass start failure.", getName(), this));
return false;
}
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 ));
return false;
}
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 ));
return false;
}
fBulkInPipe = NULL;
fBulkOutPipe = NULL;
fInterruptPipe = NULL;
fClients = NULL;
SetMaxLogicalUnitNumber( 0 );
fBulkOnlyCommandTag = 0;
fBulkOnlyCommandStructInUse = false;
fCBICommandStructInUse = false;
fUseUSBResetNotBOReset = false;
fKnownCSWTagMismatchIssues = false;
fWaitingForReconfigurationMessage = false;
fTerminating = 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( kIOUSBMassStorageUseStandardUSBReset ) != NULL )
{
fUseUSBResetNotBOReset = true;
}
if ( characterDict->getObject( kIOUSBKnownCSWTagIssues ) != NULL )
{
fKnownCSWTagMismatchIssues = true;
}
if ( characterDict->getObject( kIOUSBMassStoragePreferredSubclass ) == NULL )
{
fPreferredSubclass =
GetInterfaceReference()->GetInterfaceSubClass();
}
else
{
OSNumber * preferredSubclass;
preferredSubclass = OSDynamicCast( OSNumber, characterDict->getObject( kIOUSBMassStoragePreferredSubclass ));
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:
{
request.type = kUSBInterrupt;
request.direction = kUSBIn;
fInterruptPipe = GetInterfaceReference()->FindNextPipe(NULL, &request);
STATUS_LOG(( 7, "%s[%p]: find interrupt pipe", getName(), this));
if(( GetInterfaceProtocol() == kProtocolControlBulkInterrupt)
&& (fInterruptPipe == 0))
{
STATUS_LOG((1, "%s[%p]: No interrupt pipe for CBI, abort", getName(), this));
goto abortStart;
}
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));
fInterruptPipe = 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:
{
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);
if ( fBulkInPipe == NULL )
{
STATUS_LOG((1, "%s[%p]: No bulk in pipe found, aborting", getName(), this));
goto abortStart;
}
STATUS_LOG(( 7, "%s[%p]: find bulk out pipe", getName(), this));
request.type = kUSBBulk;
request.direction = kUSBOut;
fBulkOutPipe = GetInterfaceReference()->FindNextPipe(NULL, &request);
if ( fBulkOutPipe == NULL )
{
STATUS_LOG(( 1, "%s[%p]: No bulk out pipe found, aborting", getName(), this));
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[%p]: successfully configured", getName(), this));
#if defined (__i386__)
{
char usbDeviceAddress[10];
OSNumber * usbDeviceID;
sprintf ( usbDeviceAddress, "%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() );
BeginProvidedServices();
return true;
abortStart:
STATUS_LOG(( 1, "%s[%p]: aborting startup. Stop the provider.", getName(), this ));
if ( fCBIMemoryDescriptor != NULL )
{
fCBIMemoryDescriptor->complete();
fCBIMemoryDescriptor->release();
}
if ( fBulkOnlyCBWMemoryDescriptor != NULL )
{
fBulkOnlyCBWMemoryDescriptor->complete();
fBulkOnlyCBWMemoryDescriptor->release();
}
if ( fBulkOnlyCSWMemoryDescriptor != NULL )
{
fBulkOnlyCSWMemoryDescriptor->complete();
fBulkOnlyCSWMemoryDescriptor->release();
}
stop( provider );
return false;
}
void
IOUSBMassStorageClass::stop(IOService * provider)
{
STATUS_LOG(( 1, "%s[%p]: Bye bye!", getName(), this));
EndProvidedServices();
fBulkInPipe = NULL;
fBulkOutPipe = NULL;
fInterruptPipe = NULL;
super::stop(provider);
}
void
IOUSBMassStorageClass::free ( void )
{
if ( reserved != NULL )
{
if ( fClients != NULL )
{
fClients->release();
fClients = NULL;
}
if ( fCBIMemoryDescriptor != NULL )
{
fCBIMemoryDescriptor->complete();
fCBIMemoryDescriptor->release();
}
if ( fBulkOnlyCBWMemoryDescriptor != NULL )
{
fBulkOnlyCBWMemoryDescriptor->complete();
fBulkOnlyCBWMemoryDescriptor->release();
}
if ( fBulkOnlyCSWMemoryDescriptor != NULL )
{
fBulkOnlyCSWMemoryDescriptor->complete();
fBulkOnlyCSWMemoryDescriptor->release();
}
IOFree ( reserved, sizeof ( ExpansionData ) );
reserved = NULL;
}
super::free ( );
}
IOReturn
IOUSBMassStorageClass::message( UInt32 type, IOService * provider, void * argument )
{
IOReturn result;
STATUS_LOG ( (4, "%s[%p]: message = %lx called", getName(), this, type ) );
switch( type )
{
case kIOUSBMessageCompositeDriverReconfigured:
{
STATUS_LOG((2, "%s[%p]: message kIOUSBMessageCompositeDriverReconfigured.", getName(), this));
if ( fWaitingForReconfigurationMessage )
{
fWaitingForReconfigurationMessage = false;
FinishDeviceRecovery ( kIOReturnSuccess );
}
}
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 )
{
IOUSBInterface * currentInterface;
STATUS_LOG((2, "%s[%p]: willTerminate called.", getName(), this ));
currentInterface = GetInterfaceReference();
if ( currentInterface != NULL )
{
STATUS_LOG((2, "%s[%p]: willTerminate interface is non NULL.", getName(), this));
fTerminating = true;
SendNotification_DeviceRemoved( );
}
return super::willTerminate( provider, options );
}
bool
IOUSBMassStorageClass::didTerminate( IOService * provider, IOOptionBits options, bool * defer )
{
STATUS_LOG ((3 , "%s[%p]::didTerminate isInactive = %d", getName(), this, isInactive() ));
fTerminating = true;
if ( ( GetInterfaceReference() != NULL ) && ( fResetInProgress == false ) )
{
IOUSBInterface * currentInterface;
currentInterface = GetInterfaceReference();
SetInterfaceReference( NULL );
currentInterface->close( this );
}
return super::didTerminate(provider, options, defer);
}
bool
IOUSBMassStorageClass::BeginProvidedServices( void )
{
STATUS_LOG(( 7, "%s[%p]: Determine the maximum LUN", getName(), this ));
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[%p]: Number of LUNs %u.", getName(), this, 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 );
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 ) )
{
retain();
STATUS_LOG(( 4, "%s[%p]: BeginProvidedServices: device not responding, reseting.", getName(), this ));
fResetInProgress = true;
IOCreateThread( IOUSBMassStorageClass::sResetDevice, this );
fCommandGate->runAction ( ( IOCommandGate::Action ) &IOUSBMassStorageClass::sWaitForReset );
triedReset = true;
}
else
{
break;
}
}
}
}
}
else
{
SetMaxLogicalUnitNumber( 0 );
}
STATUS_LOG(( 5, "%s[%p]: Maximum supported LUN is: %d", getName(), this, GetMaxLogicalUnitNumber() ));
STATUS_LOG(( 7, "%s[%p]: successfully configured", getName(), this ));
if ( GetMaxLogicalUnitNumber() == 0 )
{
registerService(kIOServiceAsynchronous);
fClients = NULL;
}
else
{
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 ));
}
}
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[%p]: SendSCSICommand was called", getName(), this ));
if ( isInactive () || ( fDeviceAttached == false ) || ( fTerminating == true ) )
{
return false;
}
#if (USB_MASS_STORAGE_DEBUG == 1)
SCSICommandDescriptorBlock cdbData;
STATUS_LOG(( 4, "%s[%p]: SendSCSICommand CDB data: ", getName(), this ));
GetCommandDescriptorBlock( request, &cdbData );
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)
{
if ( fBulkOnlyCommandStructInUse == true )
{
return false;
}
fBulkOnlyCommandStructInUse = true;
STATUS_LOG(( 6, "%s[%p]: SendSCSICommandforBulkOnlyProtocol sent", getName(), this ));
status = SendSCSICommandForBulkOnlyProtocol( request );
if( status != kIOReturnSuccess )
{
fBulkOnlyCommandStructInUse = false;
}
STATUS_LOG(( 5, "%s[%p]: SendSCSICommandforBulkOnlyProtocol returned %x", getName(), this, status ));
}
else
{
if ( fCBICommandStructInUse == true )
{
return false;
}
fCBICommandStructInUse = true;
status = SendSCSICommandForCBIProtocol( request );
if( status != kIOReturnSuccess )
{
fCBICommandStructInUse = false;
}
STATUS_LOG(( 5, "%s[%p]: SendSCSICommandforCBIProtocol returned %x", getName(), this, 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
{
STATUS_LOG(( 2, "%s[%p]: CompleteSCSICommand kSCSITaskStatus_CHECK_CONDITION", getName(), this ));
CommandCompleted( request, kSCSIServiceResponse_TASK_COMPLETE, kSCSITaskStatus_CHECK_CONDITION );
}
}
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;
}
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;
STATUS_LOG(( 6, "%s[%p]::IsProtocolServiceSupported called", getName(), this ));
switch( feature )
{
case kSCSIProtocolFeature_GetMaximumLogicalUnitNumber:
{
*((UInt32 *) serviceValue) = GetMaxLogicalUnitNumber();
isSupported = true;
}
break;
case kSCSIProtocolFeature_MaximumReadBlockTransferCount:
case kSCSIProtocolFeature_MaximumWriteBlockTransferCount:
case kSCSIProtocolFeature_MaximumReadTransferByteCount:
case kSCSIProtocolFeature_MaximumWriteTransferByteCount:
{
if ( ( getProperty ( kIOPropertySCSIDeviceCharacteristicsKey ) ) != NULL )
{
OSDictionary * characterDict = NULL;
OSNumber * number = NULL;
UInt32 maxBlockCount = 0;
UInt64 maxByteCount = 0;
characterDict = OSDynamicCast ( OSDictionary, ( getProperty ( kIOPropertySCSIDeviceCharacteristicsKey ) ) );
if ( feature == kSCSIProtocolFeature_MaximumReadBlockTransferCount )
{
number = OSDynamicCast ( OSNumber, characterDict->getObject ( kIOMaximumBlockCountReadKey ) );
require ( ( number != NULL ), Exit );
maxBlockCount = number->unsigned32BitValue ( );
require ( ( maxBlockCount != 0 ), Exit );
*((UInt32 *) serviceValue) = maxBlockCount;
isSupported = true;
}
if ( feature == kSCSIProtocolFeature_MaximumWriteBlockTransferCount )
{
number = OSDynamicCast ( OSNumber, characterDict->getObject ( kIOMaximumBlockCountWriteKey ) );
require ( number, Exit );
maxBlockCount = number->unsigned32BitValue ( );
require ( maxBlockCount, Exit );
*((UInt32 *) serviceValue) = maxBlockCount;
isSupported = true;
}
if ( feature == kSCSIProtocolFeature_MaximumReadTransferByteCount )
{
number = OSDynamicCast ( OSNumber, characterDict->getObject ( kIOMaximumByteCountReadKey ) );
require ( number, Exit );
maxByteCount = number->unsigned64BitValue ( );
require ( maxByteCount, Exit );
*((UInt64 *) serviceValue) = maxByteCount;
isSupported = true;
}
if ( feature == kSCSIProtocolFeature_MaximumWriteTransferByteCount )
{
number = OSDynamicCast ( OSNumber, characterDict->getObject ( kIOMaximumByteCountWriteKey ) );
require ( number, Exit );
maxByteCount = number->unsigned64BitValue ( );
require ( maxByteCount, Exit );
*((UInt64 *) serviceValue) = maxByteCount;
isSupported = true;
}
}
}
break;
default:
{
isSupported = false;
}
break;
}
Exit:
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 ( ( fTerminating == true ) ||
( 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[%p]: ClearFeatureEndpointStall returned %x", getName(), this, status ));
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 ));
return status;
}
#pragma mark -
#pragma mark *** Accessor Methods For All Protocol Variables ***
#pragma mark -
IOUSBInterface *
IOUSBMassStorageClass::GetInterfaceReference( void )
{
STATUS_LOG(( 7, "%s[%p]: GetInterfaceReference", getName(), this ));
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;
}
IOReturn
IOUSBMassStorageClass::HandlePowerOn( void )
{
UInt8 eStatus[2];
bool knownResetOnResumeDevice = false;
STATUS_LOG(( 6, "%s[%p]: HandlePowerOn", getName(), this ));
if ( getProperty( kIOUSBMassStorageCharacteristics ) != NULL )
{
OSDictionary * characterDict = OSDynamicCast(
OSDictionary,
getProperty( kIOUSBMassStorageCharacteristics ));
if ( characterDict->getObject( kIOUSBMassStorageResetOnResume ) != NULL )
{
STATUS_LOG(( 4, "%s[%p]: knownResetOnResumeDevice", getName(), this ));
knownResetOnResumeDevice = true;
}
}
if ( ( GetStatusEndpointStatus( GetBulkInPipe(), &eStatus[0], NULL) != kIOReturnSuccess ) ||
( knownResetOnResumeDevice == true ) )
{
ResetDeviceNow( true );
}
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 (( IOUSBMassStorageClass * ) refcon )->GatedWaitForTaskAbort();
}
IOReturn
IOUSBMassStorageClass::GatedWaitForTaskAbort( void )
{
IOReturn status = kIOReturnSuccess;
while ( fAbortCurrentSCSITaskInProgress == true )
{
status = fCommandGate->commandSleep( &fAbortCurrentSCSITaskInProgress, THREAD_UNINT );
}
return status;
}
void
IOUSBMassStorageClass::sResetDevice( void * refcon )
{
IOUSBMassStorageClass * driver;
IOReturn status = kIOReturnError;
IOUSBInterface * interfaceRef = NULL;
IOUSBDevice * deviceRef = NULL;
driver = ( IOUSBMassStorageClass * ) refcon;
STATUS_LOG(( 4, "%s[%p]: sResetDevice", driver->getName(), driver ));
if ( ( driver->fTerminating == true ) ||
( driver->isInactive( ) == true ) )
{
STATUS_LOG(( 2, "%s[%p]: sResetDevice - We are being terminated!", driver->getName(), driver ));
goto ErrorExit;
}
interfaceRef = driver->GetInterfaceReference();
require ( ( interfaceRef != NULL ), ErrorExit );
deviceRef = interfaceRef->GetDevice();
require ( ( deviceRef != NULL ), ErrorExit );
status = deviceRef->message ( kIOUSBMessageHubIsDeviceConnected, NULL, 0 );
STATUS_LOG(( 5, "%s[%p]: kIOUSBMessageHubIsDeviceConnected returned = %x", driver->getName(), driver, status ));
if ( status != kIOReturnNoDevice )
{
status = deviceRef->ResetDevice();
STATUS_LOG(( 5, "%s[%p]: ResetDevice() returned = %x", driver->getName(), driver, status ));
}
if ( status != kIOReturnSuccess )
{
if ( driver->fWaitingForReconfigurationMessage == true )
{
driver->fWaitingForReconfigurationMessage = false;
driver->fDeviceAttached = false;
}
goto ErrorExit;
}
if ( ( driver->fTerminating == true ) ||
( driver->isInactive( ) == true ) )
{
STATUS_LOG(( 2, "%s[%p]: sResetDevice - We are being terminated (ii) !", driver->getName(), driver ));
goto ErrorExit;
}
driver->SendNotification_VerifyDeviceState();
ErrorExit:
if ( status != kIOReturnSuccess )
{
driver->AbortCurrentSCSITask();
}
if ( ( driver->fTerminating == true ) && ( driver->GetInterfaceReference() != NULL ) )
{
IOUSBInterface * currentInterface;
currentInterface = driver->GetInterfaceReference();
driver->SetInterfaceReference( NULL );
currentInterface->close( driver );
}
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();
return;
}
void
IOUSBMassStorageClass::sAbortCurrentSCSITask( void * refcon )
{
IOUSBMassStorageClass * driver;
SCSITaskIdentifier currentTask = NULL;
driver = ( IOUSBMassStorageClass * ) refcon;
STATUS_LOG(( 4, "%s[%p]: sAbortCurrentSCSITask", driver->getName(), driver ));
if( driver->fBulkOnlyCommandStructInUse == true )
{
currentTask = driver->fBulkOnlyCommandRequestBlock.request;
}
else if( driver->fCBICommandStructInUse == true )
{
currentTask = driver->fCBICommandRequestBlock.request;
}
if ( currentTask != NULL )
{
if ( driver->fDeviceAttached == false )
{
STATUS_LOG(( 1, "%s[%p]: sAbortCurrentSCSITask Aborting current SCSITask with device not present.", driver->getName(), driver ));
driver->CommandCompleted( currentTask, kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE, kSCSITaskStatus_DeviceNotPresent );
}
else
{
STATUS_LOG(( 1, "%s[%p]: sAbortCurrentSCSITask Aborting current SCSITask with delivery failure.", driver->getName(), driver ));
driver->CommandCompleted( currentTask, kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE, kSCSITaskStatus_DeliveryFailure );
}
}
driver->fBulkOnlyCommandStructInUse = false;
driver->fCBICommandStructInUse = false;
driver->fAbortCurrentSCSITaskInProgress = false;
driver->fCommandGate->commandWakeup( &driver->fAbortCurrentSCSITaskInProgress, false );
return;
}
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 )
{
SCSITaskIdentifier tempTask = NULL;
STATUS_LOG(( 4, "%s[%p]: + IOUSBMassStorageClass::FinishDeviceRecovery. Status = %x", getName(), this, status ));
if( fBulkOnlyCommandStructInUse == true )
{
tempTask = fBulkOnlyCommandRequestBlock.request;
}
else if ( fCBICommandStructInUse == true )
{
tempTask = fCBICommandRequestBlock.request;
}
if ( ( fTerminating == true ) || isInactive() )
{
goto ErrorExit;
}
if ( status != kIOReturnSuccess)
{
STATUS_LOG(( 4, "%s[%p]: FinishDeviceRecovery reseting device on separate thread.", getName(), this ));
fWaitingForReconfigurationMessage = true;
ResetDeviceNow( false );
}
if ( status == kIOReturnSuccess )
{
SendNotification_VerifyDeviceState();
if( fBulkOnlyCommandStructInUse == true )
{
STATUS_LOG(( 6, "%s[%p]: FinishDeviceRecovery SendSCSICommandforBulkOnlyProtocol sent", getName(), this ));
status = SendSCSICommandForBulkOnlyProtocol( tempTask );
STATUS_LOG(( 5, "%s[%p]: FinishDeviceRecovery SendSCSICommandforBulkOnlyProtocol returned %x", getName(), this, status));
fBulkOnlyCommandRequestBlock.request = tempTask;
require ( ( status == kIOReturnSuccess ), ErrorExit );
}
else if ( fCBICommandStructInUse == true )
{
STATUS_LOG(( 6, "%s[%p]: FinishDeviceRecovery SendSCSICommandforCBIProtocol sent", getName(), this ));
status = SendSCSICommandForCBIProtocol( tempTask );
STATUS_LOG(( 5, "%s[%p]: FinishDeviceRecovery SendSCSICommandforCBIProtocol returned %x", getName(), this, status));
fCBICommandRequestBlock.request = tempTask;
require ( ( status == kIOReturnSuccess ), ErrorExit );
}
}
STATUS_LOG(( 4, "%s[%p]: - IOUSBMassStorageClass::FinishDeviceRecovery", getName(), this ));
return;
ErrorExit:
if ( tempTask != NULL )
{
AbortCurrentSCSITask();
}
STATUS_LOG(( 4, "%s[%p]: - IOUSBMassStorageClass::FinishDeviceRecovery - AbortCurrentSCSITask", getName(), this ));
}
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 )
{
if ( ( fTerminating == false ) && ( isInactive() == false ) )
{
retain();
STATUS_LOG(( 4, "%s[%p]: ResetDeviceNow", getName(), this ));
fResetInProgress = true;
IOCreateThread( IOUSBMassStorageClass::sResetDevice, this );
if ( waitForReset == true )
{
STATUS_LOG(( 4, "%s[%p]: kIOMessageServiceIsResumed Waiting for Reset.", getName(), this ));
fCommandGate->runAction ( ( IOCommandGate::Action ) &IOUSBMassStorageClass::sWaitForReset );
}
}
}
void
IOUSBMassStorageClass::AbortCurrentSCSITask( void )
{
retain();
STATUS_LOG(( 4, "%s[%p]: AbortCurrentSCSITask called!", getName(), this ));
fAbortCurrentSCSITaskInProgress = true;
IOCreateThread( IOUSBMassStorageClass::sAbortCurrentSCSITask, this );
fCommandGate->runAction ( ( IOCommandGate::Action ) &IOUSBMassStorageClass::sWaitForTaskAbort );
Exit:
release();
STATUS_LOG(( 4, "%s[%p]: AbortCurrentSCSITask Exiting", getName(), this ));
}
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 );