IOUSBMassStorageClass.cpp [plain text]
#include <libkern/OSByteOrder.h>
#include <IOKit/usb/IOUSBMassStorageClass.h>
#if (USB_MASS_STORAGE_DEBUG == 1)
#define STATUS_LOG(x) IOLog x
#define PANIC_NOW(x) IOPanic 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;
if( super::start( provider ) == false )
{
STATUS_LOG(("%s: superclass start failure.\n",
getName()));
return false;
}
SetInterfaceReference( OSDynamicCast( IOUSBInterface, provider) );
if ( GetInterfaceReference() == NULL )
{
STATUS_LOG(("%s: the provider is not an IOUSBInterface object\n",
getName()));
return false;
}
STATUS_LOG(("%s: USB Mass Storage @ %d\n",
getName(),
GetInterfaceReference()->GetDevice()->GetAddress()));
if ( GetInterfaceReference()->open( this ) == false)
{
STATUS_LOG(("%s: could not open the interface\n", getName()));
return false;
}
fBulkInPipe = NULL;
fBulkOutPipe = NULL;
fInterruptPipe = NULL;
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(("%s: Preferred Protocol is: %d\n", getName(), fPreferredProtocol));
STATUS_LOG(("%s: Preferred Subclass is: %d\n", getName(), fPreferredSubclass));
STATUS_LOG(("%s: Configure the Storage interface\n", getName()));
switch ( GetInterfaceProtocol() )
{
case kProtocolControlBulkInterrupt:
{
request.type = kUSBInterrupt;
request.direction = kUSBIn;
fInterruptPipe = GetInterfaceReference()->FindNextPipe(NULL, &request);
STATUS_LOG(("%s: find interrupt pipe\n", getName()));
if(( GetInterfaceProtocol() == kProtocolControlBulkInterrupt) && (fInterruptPipe == 0))
{
STATUS_LOG(("%s: No interrupt pipe for CBI, abort\n", getName()));
goto abortStart;
}
}
break;
case kProtocolControlBulk:
case kProtocolBulkOnly:
{
STATUS_LOG(("%s: Bulk Only - skip interrupt pipe\n",
getName()));
fInterruptPipe = NULL;
}
break;
default:
{
goto abortStart;
}
break;
}
STATUS_LOG(("%s: find bulk in pipe\n", getName()));
request.type = kUSBBulk;
request.direction = kUSBIn;
fBulkInPipe = GetInterfaceReference()->FindNextPipe(NULL, &request);
if ( fBulkInPipe == NULL )
{
STATUS_LOG(("%s: No bulk in pipe found, aborting\n",
getName()));
goto abortStart;
}
STATUS_LOG(("%s: find bulk out pipe\n", getName()));
request.type = kUSBBulk;
request.direction = kUSBOut;
fBulkOutPipe = GetInterfaceReference()->FindNextPipe(NULL, &request);
if ( fBulkOutPipe == NULL )
{
STATUS_LOG(("%s: No bulk out pipe found, aborting\n",
getName()));
goto abortStart;
}
STATUS_LOG(("%s: Determine the maximum LUN\n", getName()));
if( GetInterfaceProtocol() == kProtocolBulkOnly )
{
fMaxLogicalUnitNumber = 0;
}
else
{
fMaxLogicalUnitNumber = 0;
}
STATUS_LOG(("%s: Maximum supported LUN is: %d\n",
getName(),
fMaxLogicalUnitNumber));
STATUS_LOG(("%s: successfully configured\n", getName()));
if ( fMaxLogicalUnitNumber == 0 )
{
InitializePowerManagement( GetInterfaceReference() );
BeginProvidedServices();
}
return true;
abortStart:
STATUS_LOG(("%s: aborting startup. Stop the provider.\n", getName() ));
stop( provider );
return false;
}
void
IOUSBMassStorageClass::stop(IOService * provider)
{
STATUS_LOG(("%s: Bye bye!\n", getName()));
EndProvidedServices();
fBulkInPipe = NULL;
fBulkOutPipe = NULL;
fInterruptPipe = NULL;
super::stop(provider);
}
IOReturn
IOUSBMassStorageClass::message( UInt32 type, IOService * provider, void * argument = 0 )
{
IOReturn result;
switch( type )
{
case kIOMessageServiceIsTerminated:
{
IOUSBInterface * currentInterface;
STATUS_LOG(("%s: message kIOMessageServiceIsTerminated.\n", getName() ));
currentInterface = GetInterfaceReference();
if ( currentInterface != NULL )
{
SetInterfaceReference( NULL );
currentInterface->close(this);
SendNotification_DeviceRemoved( );
}
result = kIOReturnSuccess;
}
break;
case kIOMessageServiceIsRequestingClose:
{
IOUSBInterface * currentInterface;
STATUS_LOG(("%s: message kIOMessageServiceIsRequestingClose.\n", 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 )
{
registerService();
return true;
}
bool
IOUSBMassStorageClass::EndProvidedServices( void )
{
return true;
}
#pragma mark -
#pragma mark CDB Transport Methods
bool
IOUSBMassStorageClass::SendSCSICommand(
SCSITaskIdentifier request,
SCSIServiceResponse * serviceResponse,
SCSITaskStatus * taskStatus )
{
IOReturn status;
*taskStatus = kSCSITaskStatus_No_Status;
*serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
STATUS_LOG(("%s: SendSCSICommand was called\n", getName()));
if ( request == NULL )
{
STATUS_LOG(("%s: SendSCSICommand was called with a NULL CDB \n",
getName()));
return true;
}
if ( GetInterfaceReference() == NULL )
{
return false;
}
SCSICommandDescriptorBlock cdbData;
STATUS_LOG(("%s: SendSCSICommand CDB data: ", getName()));
GetCommandDescriptorBlock( request, &cdbData );
for ( int i=0; i < GetCommandDescriptorBlockSize(request); i ++ )
{
STATUS_LOG(("%X : ", cdbData[i]));
}
STATUS_LOG(( "\n" ));
if( GetInterfaceProtocol() == kProtocolBulkOnly)
{
if ( fBulkOnlyCommandStructInUse == true )
{
return false;
}
fBulkOnlyCommandStructInUse = true;
STATUS_LOG(("%s: SendSCSICommandforBulkOnlyProtocol sent \n", getName() ));
status = SendSCSICommandForBulkOnlyProtocol( request );
STATUS_LOG(("%s: SendSCSICommandforBulkOnlyProtocol returned %d\n",
getName(),
status));
}
else
{
if ( fCBICommandStructInUse == true )
{
return false;
}
fCBICommandStructInUse = true;
status = SendSCSICommandForCBIProtocol( request );
STATUS_LOG(("%s: SendSCSICommandforCBIProtocol returned %d\n",
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(("%s: AbortSCSICommand was called\n", getName()));
if ( abortTask == NULL )
{
STATUS_LOG(("%s: AbortSCSICommand was called with a NULL CDB object\n",
getName()));
return kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
}
if ( GetInterfaceReference() == NULL )
{
STATUS_LOG(("%s: AbortSCSICommand was called with a NULL interface\n",
getName()));
return kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
}
if(GetInterfaceReference()->GetInterfaceProtocol() == kProtocolBulkOnly)
{
status = AbortSCSICommandForBulkOnlyProtocol( abortTask );
STATUS_LOG(("%s: abortCDBforBulkOnlyProtocol returned %d\n",
getName(),
status));
}
else
{
status = AbortSCSICommandForCBIProtocol( abortTask );
STATUS_LOG(("%s: abortCDBforCBIProtocol returned %d\n",
getName(),
status));
}
return kSCSIServiceResponse_FUNCTION_REJECTED;
}
bool
IOUSBMassStorageClass::IsProtocolServiceSupported(
SCSIProtocolFeature feature,
void * serviceValue )
{
bool isSupported;
STATUS_LOG(( "IOUSBMassStorageClass::IsProtocolServiceSupported called\n"));
switch( feature )
{
case kSCSIProtocolFeature_GetMaximumLogicalUnitNumber:
{
*((UInt32 *) serviceValue) = fMaxLogicalUnitNumber;
isSupported = true;
}
break;
default:
{
isSupported = false;
}
break;
}
return isSupported;
}
bool
IOUSBMassStorageClass::HandleProtocolServiceFeature(
SCSIProtocolFeature feature,
void * serviceValue )
{
return false;
}
#pragma mark -
#pragma mark Standard USB Command Methods
IOReturn
IOUSBMassStorageClass::ClearFeatureEndpointStall(
IOUSBPipe * thePipe,
IOUSBCompletion * completion )
{
IOReturn status;
if ( GetInterfaceReference() == 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(("%s: ClearFeatureEndpointStall returned %d\n",
getName(),
status));
return status;
}
IOReturn
IOUSBMassStorageClass::GetStatusEndpointStatus(
IOUSBPipe * thePipe,
void * endpointStatus,
IOUSBCompletion * completion )
{
IOReturn status;
if ( GetInterfaceReference() == NULL )
{
return kIOReturnDeviceError;
}
thePipe->Reset();
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(("%s: GetStatusEndpointStatus returned %d\n",
getName(),
status));
return status;
}
#pragma mark -
#pragma mark Accessor Methods For All Protocol Variables
inline IOUSBInterface *
IOUSBMassStorageClass::GetInterfaceReference( void )
{
STATUS_LOG(("%s: GetInterfaceReference \n", getName() ));
if ( fInterface == NULL )
{
STATUS_LOG(("%s: GetInterfaceReference - Interface is NULL.\n", getName() ));
}
return fInterface;
}
inline void
IOUSBMassStorageClass::SetInterfaceReference( IOUSBInterface * newInterface )
{
fInterface = newInterface;
}
inline UInt8
IOUSBMassStorageClass::GetInterfaceSubclass( void )
{
return fPreferredSubclass;
}
inline UInt8
IOUSBMassStorageClass::GetInterfaceProtocol( void )
{
return fPreferredProtocol;
}
inline IOUSBPipe *
IOUSBMassStorageClass::GetControlPipe( void )
{
if ( GetInterfaceReference() == NULL )
{
return NULL;
}
return GetInterfaceReference()->GetDevice()->GetPipeZero();
}
inline IOUSBPipe *
IOUSBMassStorageClass::GetBulkInPipe( void )
{
return fBulkInPipe;
}
inline IOUSBPipe *
IOUSBMassStorageClass::GetBulkOutPipe( void )
{
return fBulkOutPipe;
}
inline IOUSBPipe *
IOUSBMassStorageClass::GetInterruptPipe( void )
{
return fInterruptPipe;
}
#pragma mark -
#pragma mark Accessor Methods For CBI Protocol Variables
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
BulkOnlyRequestBlock *
IOUSBMassStorageClass::GetBulkOnlyRequestBlock( void )
{
return &fBulkOnlyCommandRequestBlock;
}
void
IOUSBMassStorageClass::ReleaseBulkOnlyRequestBlock( BulkOnlyRequestBlock * boRequestBlock )
{
boRequestBlock->request = NULL;
return;
}
UInt32
IOUSBMassStorageClass::GetNextBulkOnlyCommandTag( void )
{
fBulkOnlyCommandTag++;
return fBulkOnlyCommandTag;
}
OSMetaClassDefineReservedUnused( IOUSBMassStorageClass, 1 );
OSMetaClassDefineReservedUnused( IOUSBMassStorageClass, 2 );
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 );