/* * Copyright (c) 1998-2006 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // Includes //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // General OS Services header files #include // This class' header file #include "IOUSBMassStorageClass.h" #include "Debugging.h" #include #include //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // Macros //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // Macros for printing debugging information #if (USB_MASS_STORAGE_DEBUG == 1) #define PANIC_NOW(x) IOPanic x // Override the debug level for USBLog to make sure our logs make it out and then import // the logging header. #define DEBUG_LEVEL 1 #include #define STATUS_LOG(x) USBLog x #else #define STATUS_LOG(x) #define PANIC_NOW(x) #endif #define DEBUGGING_LEVEL 0 #define DEBUGLOG kprintf #define super IOSCSIProtocolServices OSDefineMetaClassAndStructors( IOUSBMassStorageClass, IOSCSIProtocolServices ) //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ init - Called at initialization time [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOUSBMassStorageClass::init( OSDictionary * propTable ) { if( super::init( propTable ) == false) { return false; } return true; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ start - Called at services start time (after successful matching) // [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ 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; } // Allocate data for our expansion data. reserved = ( ExpansionData * ) IOMalloc ( sizeof ( ExpansionData ) ); bzero ( reserved, sizeof ( ExpansionData ) ); // Save the reference to the interface on the device that will be // the provider for this object. SetInterfaceReference( OSDynamicCast( IOUSBInterface, provider) ); if ( GetInterfaceReference() == NULL ) { STATUS_LOG(( 1, "%s[%p]: the provider is not an IOUSBInterface object", getName(), this )); // If our provider is not a IOUSBInterface object, return false // to indicate that the object could not be correctly // instantiated. // The USB Mass Storage Class specification requires that all // devices be a composite device with a Mass Storage interface // so this object will always be an interface driver. 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; } // Set the IOUSBPipe object pointers to NULL so that the driver can // release these objects if instantition is not successful. fBulkInPipe = NULL; fBulkOutPipe = NULL; fInterruptPipe = NULL; // Default is to have no clients fClients = NULL; // Default is to have a max lun of 0. SetMaxLogicalUnitNumber( 0 ); // Initialize all Bulk Only related member variables to their default // states. fBulkOnlyCommandTag = 0; fBulkOnlyCommandStructInUse = false; // Initialize all CBI related member variables to their default // states. fCBICommandStructInUse = false; // Flag we use to indicate whether or not the device requires the standard // USB device reset instead of the BO reset. This applies to BO devices only. fUseUSBResetNotBOReset = false; fWaitingForReconfigurationMessage = false; fTerminating = false; // Check if the personality for this device specifies a preferred protocol if ( getProperty( kIOUSBMassStorageCharacteristics ) == NULL ) { // This device does not specify a preferred protocol, use the protocol // defined in the descriptor. fPreferredProtocol = GetInterfaceReference()->GetInterfaceProtocol(); fPreferredSubclass = GetInterfaceReference()->GetInterfaceSubClass(); } else { OSDictionary * characterDict; characterDict = OSDynamicCast( OSDictionary, getProperty( kIOUSBMassStorageCharacteristics )); // Check if the personality for this device specifies a preferred // protocol if ( characterDict->getObject( kIOUSBMassStoragePreferredProtocol ) == NULL ) { // This device does not specify a preferred protocol, use the // protocol defined in the interface descriptor. fPreferredProtocol = GetInterfaceReference()->GetInterfaceProtocol(); } else { OSNumber * preferredProtocol; preferredProtocol = OSDynamicCast( OSNumber, characterDict->getObject( kIOUSBMassStoragePreferredProtocol )); // This device has a preferred protocol, use that. fPreferredProtocol = preferredProtocol->unsigned32BitValue(); } // Check if this device is known not to support the bulk-only USB reset. if ( characterDict->getObject( kIOUSBMassStorageUseStandardUSBReset ) != NULL ) { fUseUSBResetNotBOReset = true; } // Check if the personality for this device specifies a preferred // subclass if ( characterDict->getObject( kIOUSBMassStoragePreferredSubclass ) == NULL ) { // This device does not specify a preferred subclass, use the // subclass defined in the interface descriptor. fPreferredSubclass = GetInterfaceReference()->GetInterfaceSubClass(); } else { OSNumber * preferredSubclass; preferredSubclass = OSDynamicCast( OSNumber, characterDict->getObject( kIOUSBMassStoragePreferredSubclass )); // This device has a preferred protocol, use that. 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)); // Verify that the device has a supported interface type and configure that // Interrupt pipe if the protocol requires one. STATUS_LOG(( 7, "%s[%p]: Configure the Storage interface", getName(), this)); switch ( GetInterfaceProtocol() ) { case kProtocolControlBulkInterrupt: { // Find the interrupt pipe for the device 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)) { // This is a CBI device and must have an interrupt pipe, // halt configuration since one could not be found 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: // Since all the CB devices I have seen do not use the interrupt // endpoint, even if it exists, ignore it if present. case kProtocolBulkOnly: { STATUS_LOG(( 7, "%s[%p]: Bulk Only - skip interrupt pipe", getName(), this)); // Since this is a Bulk Only device, do not look for // interrupt and set the pipe object to NULL so that the // driver can not try to use it. fInterruptPipe = NULL; // Allocate the memory descriptor needed to send the CBW out. fBulkOnlyCBWMemoryDescriptor = IOMemoryDescriptor::withAddress( &fBulkOnlyCommandRequestBlock.boCBW, kByteCountOfCBW, kIODirectionOut); require_nonzero ( fBulkOnlyCBWMemoryDescriptor, abortStart ); result = fBulkOnlyCBWMemoryDescriptor->prepare(); require_success ( result, abortStart ); // Allocate the memory descriptor needed to retrieve the CSW. fBulkOnlyCSWMemoryDescriptor = IOMemoryDescriptor::withAddress( &fBulkOnlyCommandRequestBlock.boCSW, kByteCountOfCSW, kIODirectionIn); require_nonzero ( fBulkOnlyCSWMemoryDescriptor, abortStart ); result = fBulkOnlyCSWMemoryDescriptor->prepare(); require_success ( result, abortStart ); } break; default: { // The device has a protocol that the driver does not // support. Return false to indicate that instantiation was // not successful. goto abortStart; } break; } // Find the Bulk In pipe for the device 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 ) { // We could not find the bulk in pipe, not much a bulk transfer // device can do without this, so fail the configuration. STATUS_LOG((1, "%s[%p]: No bulk in pipe found, aborting", getName(), this)); goto abortStart; } // Find the Bulk Out pipe for the device 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 ) { // We could not find the bulk out pipe, not much a bulk transfer // device can do without this, so fail the configuration. STATUS_LOG(( 1, "%s[%p]: No bulk out pipe found, aborting", getName(), this)); goto abortStart; } // Build the Protocol Charactersitics dictionary since not all devices will have a // SCSI Peripheral Device Nub to guarantee its existance. 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)); // Device has been successfully configured. Mark it as being attached. 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(); } // Call the stop method to clean up any allocated resources. stop( provider ); return false; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ stop - Called at stop time [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOUSBMassStorageClass::stop(IOService * provider) { // I am logging this as a 1 because if anything is logging after this we want to know about it. // This should be the last message we see. STATUS_LOG(( 1, "%s[%p]: Bye bye!", getName(), this)); EndProvidedServices(); // Tell the interface object to close all pipes since the driver is // going away. fBulkInPipe = NULL; fBulkOutPipe = NULL; fInterruptPipe = NULL; super::stop(provider); } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ free - Called by IOKit to free any resources. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOUSBMassStorageClass::free ( void ) { if ( reserved != NULL ) { // Since fClients is defined as reserved->fClients we don't want // to dereference it unless reserved is non-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 ( ); } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ message - Called by IOKit to deliver messages. [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOUSBMassStorageClass::message( UInt32 type, IOService * provider, void * argument ) { IOReturn result; STATUS_LOG ( (4, "%s[%p]: message = %lx called", getName(), this, type ) ); switch( type ) { // Waiting for kIOUSBMessageCompositeDriverReconfigured to be define in USB.h 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; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ willTerminate [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ 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)); // Mark ourselves as termination so we don't accept any additional IO. fTerminating = true; // Let the clients know that the device is gone. SendNotification_DeviceRemoved( ); } return super::willTerminate( provider, options ); } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ didTerminate [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOUSBMassStorageClass::didTerminate( IOService * provider, IOOptionBits options, bool * defer ) { // this method comes at the end of the termination sequence. Hopefully, all of our outstanding IO is complete // in which case we can just close our provider and IOKit will take care of the rest. Otherwise, we need to // hold on to the device and IOKit will terminate us when we close it later // STATUS_LOG ((3 , "%s[%p]::didTerminate isInactive = %d", getName(), this, isInactive() )); // This should already be set to true, but can't hurt ... fTerminating = true; if ( ( fTerminating == true ) && ( GetInterfaceReference() != NULL ) && ( fResetInProgress == false ) ) { IOUSBInterface * currentInterface; currentInterface = GetInterfaceReference(); // We set this to NULL first chance we get to prevent a potential race between // our reset thread and our didTeriminate(). SetInterfaceReference( NULL ); // Close and nullify our USB Interface. currentInterface->close( this ); } return super::didTerminate(provider, options, defer); } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ BeginProvidedServices [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOUSBMassStorageClass::BeginProvidedServices( void ) { // If this is a BO device that supports multiple LUNs, we will need // to spawn off a nub for each valid LUN. If this is a CBI/CB // device or a BO device that only supports LUN 0, this object can // register itself as the nub. STATUS_LOG(( 7, "%s[%p]: Determine the maximum LUN", getName(), this )); if( GetInterfaceProtocol() == kProtocolBulkOnly ) { IOReturn status; bool maxLUNDetermined = false; // Before we issue the GetMaxLUN call let's check if this device // specifies a MaxLogicalUnitNumber as part of its personality. if( getProperty( kIOUSBMassStorageCharacteristics ) != NULL ) { OSDictionary * characterDict = OSDynamicCast( OSDictionary, getProperty( kIOUSBMassStorageCharacteristics )); // Check if this device is known to have problems when waking from sleep 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 ) { // The device is a Bulk Only transport device, issue the // GetMaxLUN call to determine what the maximum value is. bool triedReset = false; UInt8 clearPipeAttempts = 0; // We want to loop until we get a satisfactory response to GetMaxLUN, either an answer or failure. while ( status != kIOReturnSuccess ) { // Build the USB command 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 )); // Send the command over the control endpoint 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 )); // Throw in an extra Get Status to clear up devices that stall the // control pipe like the early Iomega devices. GetStatusEndpointStatus( GetControlPipe(), &eStatus[0], NULL); clearPipeAttempts++; } else if ( ( status == kIOReturnNotResponding ) && ( triedReset == false ) ) { // The device isn't responding. Let us reset the device, and try again. // We need to retain ourselves so we stick around after the bus reset. retain(); // The endpoint status could not be retrieved meaning that the device has // stopped responding. Or this could be a device we know needs a reset. // Begin the device reset sequence. STATUS_LOG(( 4, "%s[%p]: BeginProvidedServices: device not responding, reseting.", getName(), this )); // Reset the device on its own thread so we don't deadlock. fResetInProgress = true; IOCreateThread( IOUSBMassStorageClass::sResetDevice, this ); fCommandGate->runAction ( ( IOCommandGate::Action ) &IOUSBMassStorageClass::sWaitForReset ); triedReset = true; } else { break; } } } } } else { // CBI and CB protocols do not support LUNs so for these the // maximum LUN will always be zero. 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 this is a BO device that supports multiple LUNs, we will need // to spawn off a nub for each valid LUN. If this is a CBI/CB // device or a BO device that only supports LUN 0, this object can // register itself as the nub. if ( GetMaxLogicalUnitNumber() == 0 ) { registerService(kIOServiceAsynchronous); fClients = NULL; } else { // Allocate space for our set that will keep track of the LUNs. 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 ) { // Release our nub before we return so we don't leak... nub->release(); // We didn't init successfully so we should return false. return false; } if( nub->attach( this ) == false ) { if( isInactive() == false ) { // panic since the nub can't attach and we are active PANIC_NOW(( "IOUSBMassStorageClass::CreatePeripheralDeviceNubForLUN unable to attach nub" )); } // Release our nub before we return so we don't leak... nub->release(); // We didn't attach so we should return false. 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; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ EndProvidedServices [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOUSBMassStorageClass::EndProvidedServices( void ) { return true; } #pragma mark - #pragma mark *** CDB Transport Methods *** #pragma mark - //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ SendSCSICommand [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOUSBMassStorageClass::SendSCSICommand( SCSITaskIdentifier request, SCSIServiceResponse * serviceResponse, SCSITaskStatus * taskStatus ) { IOReturn status; // Set the defaults to an error state. *taskStatus = kSCSITaskStatus_No_Status; *serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE; STATUS_LOG(( 6, "%s[%p]: SendSCSICommand was called", getName(), this )); // If we have been marked as inactive, or no longer have the device, return false. 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 ) { // If the command fails we want to make sure that we // don't hold up other commands. 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 ) { // If the command fails we want to make sure that we // don't hold up other commands. fCBICommandStructInUse = false; } STATUS_LOG(( 5, "%s[%p]: SendSCSICommandforCBIProtocol returned %x", getName(), this, status )); } if ( status == kIOReturnSuccess ) { *serviceResponse = kSCSIServiceResponse_Request_In_Process; } return true; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ CompleteSCSICommand [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ 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 ); } } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ AbortSCSICommand [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ SCSIServiceResponse IOUSBMassStorageClass::AbortSCSICommand( SCSITaskIdentifier abortTask ) { IOReturn status = kIOReturnSuccess; STATUS_LOG(( 6, "%s[%p]: AbortSCSICommand was called", getName(), this )); if ( abortTask == NULL ) { // We were given an invalid SCSI Task object. Let the client know. 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 ) { // We have an invalid interface, the device has probably been removed. // Nothing else to do except to report an error. 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 )); } // Since the driver currently does not support abort, return an error return kSCSIServiceResponse_FUNCTION_REJECTED; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ IsProtocolServiceSupported [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOUSBMassStorageClass::IsProtocolServiceSupported( SCSIProtocolFeature feature, void * serviceValue ) { bool isSupported = false; STATUS_LOG(( 6, "%s[%p]::IsProtocolServiceSupported called", getName(), this )); // all features just return false switch( feature ) { case kSCSIProtocolFeature_GetMaximumLogicalUnitNumber: { *((UInt32 *) serviceValue) = GetMaxLogicalUnitNumber(); isSupported = true; } break; case kSCSIProtocolFeature_MaximumReadBlockTransferCount: case kSCSIProtocolFeature_MaximumWriteBlockTransferCount: case kSCSIProtocolFeature_MaximumReadTransferByteCount: case kSCSIProtocolFeature_MaximumWriteTransferByteCount: { // Do we have SCSI Device Characteristics dictionary which may state preferences for // max transfer sizes? if ( ( getProperty ( kIOPropertySCSIDeviceCharacteristicsKey ) ) != NULL ) { OSDictionary * characterDict = NULL; OSNumber * number = NULL; UInt32 maxBlockCount = 0; UInt64 maxByteCount = 0; // Get the SCSI Characteristics Dictionary. characterDict = OSDynamicCast ( OSDictionary, ( getProperty ( kIOPropertySCSIDeviceCharacteristicsKey ) ) ); if ( feature == kSCSIProtocolFeature_MaximumReadBlockTransferCount ) { // Does the IOKit personality specify a maximum number of blocks? 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 ) { // Does the IOKit personality specify a maximum number of blocks? number = OSDynamicCast ( OSNumber, characterDict->getObject ( kIOMaximumBlockCountWriteKey ) ); require ( number, Exit ); maxBlockCount = number->unsigned32BitValue ( ); require ( maxBlockCount, Exit ); *((UInt32 *) serviceValue) = maxBlockCount; isSupported = true; } if ( feature == kSCSIProtocolFeature_MaximumReadTransferByteCount ) { // Does the IOKit personality specify a maximum number of bytes? number = OSDynamicCast ( OSNumber, characterDict->getObject ( kIOMaximumByteCountReadKey ) ); require ( number, Exit ); maxByteCount = number->unsigned64BitValue ( ); require ( maxByteCount, Exit ); *((UInt64 *) serviceValue) = maxByteCount; isSupported = true; } if ( feature == kSCSIProtocolFeature_MaximumWriteTransferByteCount ) { // Does the IOKit personality specify a maximum number of bytes? 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; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ HandleProtocolServiceFeature [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOUSBMassStorageClass::HandleProtocolServiceFeature( SCSIProtocolFeature feature, void * serviceValue ) { UNUSED( feature ); UNUSED( serviceValue ); return false; } #pragma mark - #pragma mark *** Standard USB Command Methods *** #pragma mark - //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ClearFeatureEndpointStall - Method to do the CLEAR_FEATURE command for // an ENDPOINT_STALL feature. // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOUSBMassStorageClass::ClearFeatureEndpointStall( IOUSBPipe * thePipe, IOUSBCompletion * completion ) { IOReturn status; if ( ( fTerminating == true ) || ( thePipe == NULL ) ) { // We're terminating, the device has probably been removed. // Nothing else to do except to report an error. return kIOReturnDeviceError; } // Make sure that the Data Toggles are reset before doing the // Clear Stall. thePipe->Reset(); // Clear out the structure for the request bzero( &fUSBDeviceRequest, sizeof(IOUSBDevRequest)); // Build the USB command fUSBDeviceRequest.bmRequestType = USBmakebmRequestType(kUSBNone, kUSBStandard, kUSBEndpoint); fUSBDeviceRequest.bRequest = kUSBRqClearFeature; fUSBDeviceRequest.wValue = 0; // Zero is EndpointStall fUSBDeviceRequest.wIndex = thePipe->GetEndpointNumber(); if ( thePipe == GetBulkInPipe() ) { fUSBDeviceRequest.wIndex |= 0x80; } // Send the command over the control endpoint status = GetInterfaceReference()->DeviceRequest( &fUSBDeviceRequest, completion ); STATUS_LOG(( 5, "%s[%p]: ClearFeatureEndpointStall returned %x", getName(), this, status )); return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetStatusEndpointStatus - Method to do the GET_STATUS command for the // endpoint that the IOUSBPipe is connected to. // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOUSBMassStorageClass::GetStatusEndpointStatus( IOUSBPipe * thePipe, void * endpointStatus, IOUSBCompletion * completion ) { IOReturn status; if ( ( fTerminating == true ) || ( thePipe == NULL ) ) { // We're terminating, the device has probably been removed. // Nothing else to do except to report an error. return kIOReturnDeviceError; } // Clear out the structure for the request bzero( &fUSBDeviceRequest, sizeof(IOUSBDevRequest)); // Build the USB command fUSBDeviceRequest.bmRequestType = USBmakebmRequestType( kUSBIn, kUSBStandard, kUSBEndpoint); fUSBDeviceRequest.bRequest = kUSBRqGetStatus; fUSBDeviceRequest.wValue = 0; // Zero is EndpointStall fUSBDeviceRequest.wIndex = thePipe->GetEndpointNumber(); if ( thePipe == GetBulkInPipe() ) { fUSBDeviceRequest.wIndex |= 0x80; } fUSBDeviceRequest.wLength = 2; fUSBDeviceRequest.pData = endpointStatus; // Send the command over the control endpoint 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 - /* The following methods are for use only by this class */ //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetInterfaceReference - Method to do the GET_STATUS command for the // endpoint that the IOUSBPipe is connected to. // [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOUSBInterface * IOUSBMassStorageClass::GetInterfaceReference( void ) { // Making this a 7 since it gets called A LOT. STATUS_LOG(( 7, "%s[%p]: GetInterfaceReference", getName(), this )); if ( fInterface == NULL ) { STATUS_LOG(( 2, "%s[%p]: GetInterfaceReference - Interface is NULL.", getName(), this )); } return fInterface; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ SetInterfaceReference [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOUSBMassStorageClass::SetInterfaceReference( IOUSBInterface * newInterface ) { fInterface = newInterface; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetInterfaceSubClass [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ UInt8 IOUSBMassStorageClass::GetInterfaceSubclass( void ) { return fPreferredSubclass; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetInterfaceProtocol [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ UInt8 IOUSBMassStorageClass::GetInterfaceProtocol( void ) { return fPreferredProtocol; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetControlPipe [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOUSBPipe * IOUSBMassStorageClass::GetControlPipe( void ) { if ( fTerminating == true ) { return NULL; } return GetInterfaceReference()->GetDevice()->GetPipeZero(); } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetBulkInPipe [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOUSBPipe * IOUSBMassStorageClass::GetBulkInPipe( void ) { return fBulkInPipe; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetBulkOutPipe [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOUSBPipe * IOUSBMassStorageClass::GetBulkOutPipe( void ) { return fBulkOutPipe; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetInterruptPipe [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOUSBPipe * IOUSBMassStorageClass::GetInterruptPipe( void ) { return fInterruptPipe; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetMaxLogicalUnitNumber [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ UInt8 IOUSBMassStorageClass::GetMaxLogicalUnitNumber( void ) const { return fMaxLogicalUnitNumber; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ SetMaxLogicalUnitNumber [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOUSBMassStorageClass::SetMaxLogicalUnitNumber( UInt8 maxLUN ) { fMaxLogicalUnitNumber = maxLUN; } #pragma mark - #pragma mark *** Accessor Methods For CBI Protocol Variables *** #pragma mark - //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetCBIRequestBlock [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ CBIRequestBlock * IOUSBMassStorageClass::GetCBIRequestBlock( void ) { // Return a pointer to the CBIRequestBlock return &fCBICommandRequestBlock; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ReleaseCBIRequestBlock [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOUSBMassStorageClass::ReleaseCBIRequestBlock( CBIRequestBlock * cbiRequestBlock ) { // Clear the request and completion to avoid possible double callbacks. cbiRequestBlock->request = NULL; // Since we only allow one command and the CBIRequestBlock is // a member variable, no need to do anything. return; } #pragma mark - #pragma mark *** Accessor Methods For Bulk Only Protocol Variables *** #pragma mark - //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetBulkOnlyRequestBlock [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ BulkOnlyRequestBlock * IOUSBMassStorageClass::GetBulkOnlyRequestBlock( void ) { // Return a pointer to the BulkOnlyRequestBlock return &fBulkOnlyCommandRequestBlock; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ReleaseBulkOnlyRequestBlock [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOUSBMassStorageClass::ReleaseBulkOnlyRequestBlock( BulkOnlyRequestBlock * boRequestBlock ) { // Clear the request and completion to avoid possible double callbacks. boRequestBlock->request = NULL; // Since we only allow one command and the BulkOnlyRequestBlock is // a member variable, no need to do anything. return; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GetNextBulkOnlyCommandTag [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ UInt32 IOUSBMassStorageClass::GetNextBulkOnlyCommandTag( void ) { fBulkOnlyCommandTag++; return fBulkOnlyCommandTag; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ HandlePowerOn - Will get called when a device has been resumed [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOUSBMassStorageClass::HandlePowerOn( void ) { UInt8 eStatus[2]; bool knownResetOnResumeDevice = false; // The USB hub port that the device is connected to has been resumed, // check to see if the device is still responding correctly and if not, // fix it so that it is. STATUS_LOG(( 6, "%s[%p]: HandlePowerOn", getName(), this )); if ( getProperty( kIOUSBMassStorageCharacteristics ) != NULL ) { OSDictionary * characterDict = OSDynamicCast( OSDictionary, getProperty( kIOUSBMassStorageCharacteristics )); // Check if this device is known to have problems when waking from sleep 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; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ handleOpen [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOUSBMassStorageClass::handleOpen( IOService * client, IOOptionBits options, void * arg ) { bool result = false; // If this is a normal open on a single LUN device. if ( GetMaxLogicalUnitNumber() == 0 ) { result = super::handleOpen ( client, options, arg ); goto Exit; } // It's an open from a multi-LUN client require_nonzero ( fClients, ErrorExit ); require_nonzero ( OSDynamicCast ( IOSCSILogicalUnitNub, client ), ErrorExit ); result = fClients->setObject ( client ); Exit: ErrorExit: return result; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ handleClose [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ 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; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ handleIsOpen [PUBLIC] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ bool IOUSBMassStorageClass::handleIsOpen( const IOService * client ) const { bool result = false; UInt8 lun = GetMaxLogicalUnitNumber(); require_nonzero ( lun, CallSuperClass ); require_nonzero ( fClients, CallSuperClass ); // General case (is anybody open) if ( ( client == NULL ) && ( fClients->getCount ( ) != 0 ) ) { result = true; } else { // specific case (is this client open) result = fClients->containsObject ( client ); } return result; CallSuperClass: result = super::handleIsOpen ( client ); return result; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ sWaitForReset [STATIC][PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOUSBMassStorageClass::sWaitForReset( void * refcon ) { return (( IOUSBMassStorageClass * ) refcon )->GatedWaitForReset(); } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GatedWaitForReset [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOUSBMassStorageClass::GatedWaitForReset( void ) { IOReturn status = kIOReturnSuccess; while ( fResetInProgress == true ) { status = fCommandGate->commandSleep( &fResetInProgress, THREAD_UNINT ); } return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ sWaitForTaskAbort [STATIC][PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOUSBMassStorageClass::sWaitForTaskAbort( void * refcon ) { return (( IOUSBMassStorageClass * ) refcon )->GatedWaitForTaskAbort(); } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ GatedWaitForTaskAbort [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ-- IOReturn IOUSBMassStorageClass::GatedWaitForTaskAbort( void ) { IOReturn status = kIOReturnSuccess; while ( fAbortCurrentSCSITaskInProgress == true ) { status = fCommandGate->commandSleep( &fAbortCurrentSCSITaskInProgress, THREAD_UNINT ); } // We retained the driver in AbortCurrentSCSITask() when // we created a thread for sAbortCurrentSCSITask() release(); return status; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ sResetDevice [STATIC][PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ 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 )); // Check if we should bail out because we are // being terminated. 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(); if ( interfaceRef == NULL ) { STATUS_LOG(( 1, "%s[%p]: sResetDevice - Interface Ref NULL!", driver->getName(), driver )); goto ErrorExit; } deviceRef = interfaceRef->GetDevice(); if ( deviceRef == NULL ) { STATUS_LOG (( 1, "%s[%p]: sResetDevice - Device Ref NULL!", driver->getName(), driver )); goto ErrorExit; } // Are we still connected to the hub? We only need to check this for full and low speed devices. status = deviceRef->message ( kIOUSBMessageHubIsDeviceConnected, NULL, 0 ); STATUS_LOG(( 5, "%s[%p]: kIOUSBMessageHubIsDeviceConnected returned = %x", driver->getName(), driver, status )); if ( status != kIOReturnNoDevice ) { // Device is still attached. Lets try resetting. status = deviceRef->ResetDevice(); STATUS_LOG(( 5, "%s[%p]: ResetDevice() returned = %x", driver->getName(), driver, status )); } if ( status != kIOReturnSuccess ) { // Device reset failed, or the device has been disconnected. // Were we attempting to recover the device after a major device error? if ( driver->fWaitingForReconfigurationMessage == true ) { // Yes, so we need to abort the command we'll not be able to retry. // We set the device state to detached so the proper status for the // device is returned along with the aborted SCSITask. driver->fWaitingForReconfigurationMessage = false; driver->fDeviceAttached = false; driver->AbortCurrentSCSITask(); } goto ErrorExit; } // We may get terminated during the call to ResetDevice() since it is synchronous. We should // check again whether we should bail or not. if ( ( driver->fTerminating == true ) || ( driver->isInactive( ) == true ) ) { STATUS_LOG(( 2, "%s[%p]: sResetDevice - We are being terminated (ii) !", driver->getName(), driver )); goto ErrorExit; } // Once the device has been reset, send notification to the client so that the // device can be reconfigured for use. driver->SendNotification_VerifyDeviceState(); ErrorExit: if ( ( driver->fTerminating == true ) && ( driver->GetInterfaceReference() != NULL ) ) { IOUSBInterface * currentInterface; currentInterface = driver->GetInterfaceReference(); // We set this to NULL first chance we get to prevent a potential race between // our reset thread and our didTeriminate(). driver->SetInterfaceReference( NULL ); // Close and nullify our USB Interface. 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 )); // We retained the driver in HandlePowerOn() when // we created a thread for sResetDevice() driver->release(); return; } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ sAbortCurrentSCSITask [STATIC][PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ 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_TASK_COMPLETE, kSCSITaskStatus_CHECK_CONDITION ); } 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 ); //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ StartDeviceRecovery - The recovery sequence to restore functionality for // devices that stop responding (like many devices // after a Suspend/Resume). [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ IOReturn IOUSBMassStorageClass::StartDeviceRecovery( void ) { // First check to see if the device is still connected. UInt8 eStatus[2]; IOReturn status = kIOReturnError; // The USB hub port that the device is connected to has been resumed, // check to see if the device is still responding correctly and if not, // fix it so that it is. STATUS_LOG(( 5, "%s[%p]: StartDeviceRecovery", getName(), this )); if( fBulkOnlyCommandStructInUse == true ) { // Set up the IOUSBCompletion structure fBulkOnlyCommandRequestBlock.boCompletion.target = this; fBulkOnlyCommandRequestBlock.boCompletion.action = &this->DeviceRecoveryCompletionAction; status = GetStatusEndpointStatus( GetBulkInPipe(), &eStatus[0], &fBulkOnlyCommandRequestBlock.boCompletion); } else if( fCBICommandStructInUse == true ) { // Set up the IOUSBCompletion structure fCBICommandRequestBlock.cbiCompletion.target = this; fCBICommandRequestBlock.cbiCompletion.action = &this->DeviceRecoveryCompletionAction; status = GetStatusEndpointStatus( GetBulkInPipe(), &eStatus[0], &fCBICommandRequestBlock.cbiCompletion); } return status; } OSMetaClassDefineReservedUsed( IOUSBMassStorageClass, 2 ); //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ FinishDeviceRecovery [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOUSBMassStorageClass::FinishDeviceRecovery( IOReturn status ) { SCSITaskIdentifier tempTask = NULL; IOReturn isDeviceConnectedStatus = kIOReturnSuccess; STATUS_LOG(( 4, "%s[%p]: + IOUSBMassStorageClass::FinishDeviceRecovery", getName(), this )); if( fBulkOnlyCommandStructInUse == true ) { tempTask = fBulkOnlyCommandRequestBlock.request; } else if ( fCBICommandStructInUse == true ) { tempTask = fCBICommandRequestBlock.request; } if ( status != kIOReturnSuccess) { // The endpoint status could not be retrieved meaning that the device has // stopped responding. Begin the device reset sequence. STATUS_LOG(( 4, "%s[%p]: StartDeviceRecovery GetStatusEndpointStatus error. status = %x", getName(), this, status )); if ( ( fTerminating == true ) || isInactive() ) { // We're being terminated. Abort the outstanding command so the system can clean up. goto ErrorExit; } // Are we still connected to the hub? We only need to check this for full and low speed devices. if ( GetInterfaceReference()->GetDevice()->GetSpeed() != kUSBDeviceSpeedHigh ) { isDeviceConnectedStatus = GetInterfaceReference()->GetDevice()->message ( kIOUSBMessageHubIsDeviceConnected, NULL, 0 ); } if ( isDeviceConnectedStatus == kIOReturnNoDevice ) { STATUS_LOG(( 4, "%s[%p]: FinishDeviceRecovery Device has been removed! status = %x", getName(), this, isDeviceConnectedStatus )); // The device is no longer attached or we're being terminated! // Mark the device as being no longer attached. fDeviceAttached = false; // Return outstanding commands so we don't wedge the system. goto ErrorExit; } STATUS_LOG(( 4, "%s[%p]: FinishDeviceRecovery reseting device on separate thread.", getName(), this )); fWaitingForReconfigurationMessage = true; ResetDeviceNow( false ); } // If the device is responding correctly or has been reset, retry the command. if ( status == kIOReturnSuccess ) { // Once the device has been reset, send notification to the client so that the // device can be reconfigured for use. 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)); // SendSCSICommandForBulkOnlyProtocol clears out fBulkOnlyCommandRequestBlock.request if the command fails // to be sent correctly. We need to reset this value so we can abort the command. 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)); // SendSCSICommandForCBIProtocol clears out fBulkOnlyCommandRequestBlock.request if the command fails // to be sent correctly. We need to reset this value so we can abort the command. 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 )); } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ DeviceRecoveryCompletionAction [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOUSBMassStorageClass::DeviceRecoveryCompletionAction( void * target, void * parameter, IOReturn status, UInt32 bufferSizeRemaining) { UNUSED( parameter ); UNUSED( bufferSizeRemaining ); IOUSBMassStorageClass * theMSC; theMSC = (IOUSBMassStorageClass *) target; theMSC->FinishDeviceRecovery( status ); } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ ResetDeviceNow [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOUSBMassStorageClass::ResetDeviceNow( bool waitForReset ) { // Make sure we aren't terminating. if ( ( fTerminating == false ) && ( isInactive() == false ) ) { // We call retain here so that the driver will stick around long enough for // sResetDevice() to do it's thing in case we are being terminated. The // retain() is balanced with a release in sResetDevice(). retain(); // The endpoint status could not be retrieved meaning that the device has // stopped responding. Or this could be a device we know needs a reset. // Begin the device reset sequence. STATUS_LOG(( 4, "%s[%p]: kIOMessageServiceIsResumed GetStatusEndpointStatus error or knownResetOnResumeDevice.", getName(), this )); // Reset the device on its own thread so we don't deadlock. 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 ); } } } //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ // ₯ AbortCurrentSCSITask [PROTECTED] //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ void IOUSBMassStorageClass::AbortCurrentSCSITask( void ) { // We call retain here so that the driver will stick around long enough for // sAbortCurrentSCSITask() to do it's thing in case we are being terminated. // The retain() is balanced with a release in sAbortCurrentSCSITask(). retain(); // The endpoint status could not be retrieved meaning that the device has // stopped responding. Or this could be a device we know needs a reset. // Begin the device reset sequence. STATUS_LOG(( 4, "%s[%p]: AbortCurrentSCSITask called!", getName(), this )); // Abort the SCSITask on a separate thread so we don't deadlock. fAbortCurrentSCSITaskInProgress = true; IOCreateThread( IOUSBMassStorageClass::sAbortCurrentSCSITask, this ); fCommandGate->runAction ( ( IOCommandGate::Action ) &IOUSBMassStorageClass::sWaitForTaskAbort ); // Make sure we aren't terminating. if ( ( fTerminating == false ) && ( isInactive() == false ) ) { fBulkInPipe->Abort(); fBulkOutPipe->Abort(); if ( fInterruptPipe != NULL ) { fInterruptPipe->Abort(); } } STATUS_LOG(( 4, "%s[%p]: AbortCurrentSCSITask Exiting", getName(), this )); } // Space reserved for future expansion. 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 );