/* * Copyright (c) 1998-2001 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This 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 OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include #include #include #include // For debugging, set SCSI_PROTOCOL_SERVICES_DEBUGGING_LEVEL to one // of the following values: // 0 No debugging (GM release level) // 1 PANIC_NOW only // 2 PANIC_NOW and ERROR_LOG // 3 PANIC_NOW, ERROR_LOG and STATUS_LOG #if ( SCSI_PROTOCOL_SERVICES_DEBUGGING_LEVEL >= 1 ) #define PANIC_NOW(x) IOPanic x #else #define PANIC_NOW(x) #endif #if ( SCSI_PROTOCOL_SERVICES_DEBUGGING_LEVEL >= 2 ) #define ERROR_LOG(x) IOLog x #else #define ERROR_LOG(x) #endif #if ( SCSI_PROTOCOL_SERVICES_DEBUGGING_LEVEL >= 3 ) #define STATUS_LOG(x) IOLog x #else #define STATUS_LOG(x) #endif #define super IOSCSIProtocolInterface OSDefineMetaClass ( IOSCSIProtocolServices, IOSCSIProtocolInterface ); OSDefineAbstractStructors ( IOSCSIProtocolServices, IOSCSIProtocolInterface ); // Space reserved for future expansion. OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 1 ); OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 2 ); OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 3 ); OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 4 ); OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 5 ); OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 6 ); OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 7 ); OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 8 ); OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 9 ); OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 10 ); OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 11 ); OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 12 ); OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 13 ); OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 14 ); OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 15 ); OSMetaClassDefineReservedUnused( IOSCSIProtocolServices, 16 ); /* struct IOPMPowerState { UInt32 version; // version number of this struct IOPMPowerFlags capabilityFlags; // bits that describe (to interested drivers) the capability of the device in this state IOPMPowerFlags outputPowerCharacter; // description (to power domain children) of the power provided in this state IOPMPowerFlags inputPowerRequirement; // description (to power domain parent) of input power required in this state UInt32 staticPower; // average consumption in milliwatts UInt32 unbudgetedPower; // additional consumption from separate power supply (mw) UInt32 powerToAttain; // additional power to attain this state from next lower state (in mw) UInt32 timeToAttain; // time required to enter this state from next lower state (in microseconds) UInt32 settleUpTime; // settle time required after entering this state from next lower state (microseconds) UInt32 timeToLower; // time required to enter next lower state from this one (in microseconds) UInt32 settleDownTime; // settle time required after entering next lower state from this state (microseconds) UInt32 powerDomainBudget; // power in mw a domain in this state can deliver to its children }; */ // Used by power manager to figure out what states we support // The default implementation supports two basic states: ON and OFF // ON state means the device can be used on this transport layer // OFF means the device cannot receive any I/O on this transport layer static IOPMPowerState sPowerStates[kSCSIProtocolLayerNumDefaultStates] = { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 1, (IOPMDeviceUsable | IOPMMaxPerformance), IOPMPowerOn, IOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0 } }; UInt32 IOSCSIProtocolServices::GetInitialPowerState ( void ) { STATUS_LOG ( ( "%s%s::%s called%s\n", "\033[33m", getName ( ), __FUNCTION__, "\033[0m" ) ); return fCurrentPowerState; } #pragma mark - #pragma mark Public Methods bool IOSCSIProtocolServices::init ( OSDictionary * propTable ) { if ( super::init ( propTable ) == false ) { return false; } return true; } bool IOSCSIProtocolServices::start ( IOService * provider ) { if ( !super::start ( provider ) ) { return false; } // Setup to allow service requests fAllowServiceRequests = true; // Initialize the head pointer for the SCSI Task Queue. fSCSITaskQueueHead = NULL; // Allocate the mutex for accessing the SCSI Task Queue. fQueueLock = IOSimpleLockAlloc ( ); if ( fQueueLock == NULL ) { PANIC_NOW ( ( "IOSCSIProtocolServices::start Allocate fQueueLock failed." ) ); } return true; } void IOSCSIProtocolServices::stop ( IOService * provider ) { if ( fQueueLock != NULL ) { // Free the SCSI Task queue mutex. IOSimpleLockFree ( fQueueLock ); fQueueLock = NULL; } super::stop ( provider ); } #pragma mark - #pragma mark Power Management Methods void IOSCSIProtocolServices::InitializePowerManagement ( IOService * provider ) { STATUS_LOG ( ( "%s%s::%s called%s\n", "\033[33m", getName ( ), __FUNCTION__, "\033[0m" ) ); fCurrentPowerState = kSCSIProtocolLayerPowerStateOn; // Call our super to initialize PM vars and to join the power // management tree super::InitializePowerManagement ( provider ); // Register this piece with power management as the "policy maker" // i.e. the thing that controls power management for the protocol layer registerPowerDriver ( this, sPowerStates, kSCSIProtocolLayerNumDefaultStates ); // make sure we default to on state changePowerStateTo ( kSCSIProtocolLayerPowerStateOn ); } void IOSCSIProtocolServices::HandlePowerChange ( void ) { IOReturn status; STATUS_LOG ( ( "%s%s::%s called%s\n", "\033[33m", getName ( ), __FUNCTION__, "\033[0m" ) ); STATUS_LOG ( ( "fProposedPowerState = %ld, fCurrentPowerState = %ld\n", fProposedPowerState, fCurrentPowerState ) ); while ( fProposedPowerState != fCurrentPowerState ) { STATUS_LOG ( ( "Looping because power states differ\n" ) ); switch ( fProposedPowerState ) { case kSCSIProtocolLayerPowerStateOff: status = HandlePowerOff ( ); STATUS_LOG ( ( "HandlePowerOff returned status = %d\n", status ) ); if ( status == kIOReturnSuccess ) { fCurrentPowerState = kSCSIProtocolLayerPowerStateOff; } break; case kSCSIProtocolLayerPowerStateOn: status = HandlePowerOn ( ); STATUS_LOG ( ( "HandlePowerOn returned status = %d\n", status ) ); if ( status == kIOReturnSuccess ) { fCurrentPowerState = kSCSIProtocolLayerPowerStateOn; } break; default: PANIC_NOW ( ( "HandlePowerChange: bad proposed power state\n" ) ); break; } } } void IOSCSIProtocolServices::HandleCheckPowerState ( void ) { super::HandleCheckPowerState ( kSCSIProtocolLayerPowerStateOn ); } void IOSCSIProtocolServices::TicklePowerManager ( void ) { super::TicklePowerManager ( kSCSIProtocolLayerPowerStateOn ); } IOReturn IOSCSIProtocolServices::HandlePowerOff ( void ) { return kIOReturnSuccess; } IOReturn IOSCSIProtocolServices::HandlePowerOn ( void ) { return kIOReturnSuccess; } #pragma mark - #pragma mark Status Notification Senders void IOSCSIProtocolServices::SendNotification_DeviceRemoved ( void ) { STATUS_LOG ( ( "%s: SendNotification_DeviceRemoved called\n", getName ( ) ) ); // Set the flag to prevent execution of any other service requests fAllowServiceRequests = false; STATUS_LOG ( ( "%s: SendNotification_DeviceRemoved Reject queued tasks\n", getName ( ) ) ); // Remove all tasks from the queue. RejectSCSITasksCurrentlyQueued ( ); } void IOSCSIProtocolServices::SendNotification_VerifyDeviceState( void ) { STATUS_LOG ( ("%s: SendNotification_VerifyDeviceState called\n", getName ( ) ) ); // Send message up to SCSI Application Layer. messageClients ( kSCSIProtocolNotification_VerifyDeviceState, 0 , 0 ); } #pragma mark - #pragma mark SCSI Task Field Accessors // ---- Utility methods for accessing SCSITask attributes ---- SCSITaskAttribute IOSCSIProtocolServices::GetTaskAttribute ( SCSITaskIdentifier request ) { SCSITask * scsiRequest; scsiRequest = OSDynamicCast ( SCSITask, request ); return scsiRequest->GetTaskAttribute ( ); } bool IOSCSIProtocolServices::SetTaskState ( SCSITaskIdentifier request, SCSITaskState newTaskState ) { SCSITask * scsiRequest; scsiRequest = OSDynamicCast ( SCSITask, request ); return scsiRequest->SetTaskState ( newTaskState ); } SCSITaskState IOSCSIProtocolServices::GetTaskState ( SCSITaskIdentifier request ) { SCSITask * scsiRequest; scsiRequest = OSDynamicCast ( SCSITask, request ); return scsiRequest->GetTaskState ( ); } UInt8 IOSCSIProtocolServices::GetLogicalUnitNumber( SCSITaskIdentifier request ) { SCSITask * scsiRequest; scsiRequest = OSDynamicCast( SCSITask, request ); return scsiRequest->GetLogicalUnitNumber(); } UInt8 IOSCSIProtocolServices::GetCommandDescriptorBlockSize ( SCSITaskIdentifier request ) { SCSITask * scsiRequest; scsiRequest = OSDynamicCast ( SCSITask, request ); // Check to see what the current execution mode is if ( scsiRequest->GetTaskExecutionMode() == kSCSITaskMode_CommandExecution ) { return scsiRequest->GetCommandDescriptorBlockSize ( ); } else { return scsiRequest->GetAutosenseCommandDescriptorBlockSize ( ); } } // This will always return a 16 Byte CDB. If the Protocol Layer driver does not // support 16 Byte CDBs, it will have to create a local SCSICommandDescriptorBlock // variable to get the CDB data and then transfer the needed bytes from there. bool IOSCSIProtocolServices::GetCommandDescriptorBlock ( SCSITaskIdentifier request, SCSICommandDescriptorBlock * cdbData ) { SCSITask * scsiRequest; scsiRequest = OSDynamicCast ( SCSITask, request ); // Check to see what the current execution mode is if ( scsiRequest->GetTaskExecutionMode ( ) == kSCSITaskMode_CommandExecution ) { return scsiRequest->GetCommandDescriptorBlock ( cdbData ); } else { return scsiRequest->GetAutosenseCommandDescriptorBlock ( cdbData ); } } // Get the control information for the transfer, including // the transfer direction and the number of bytes to transfer. UInt8 IOSCSIProtocolServices::GetDataTransferDirection ( SCSITaskIdentifier request ) { SCSITask * scsiRequest; scsiRequest = OSDynamicCast ( SCSITask, request ); // Check to see what the current execution mode is if ( scsiRequest->GetTaskExecutionMode ( ) == kSCSITaskMode_CommandExecution ) { return scsiRequest->GetDataTransferDirection( ); } else { return scsiRequest->GetAutosenseDataTransferDirection ( ); } } UInt64 IOSCSIProtocolServices::GetRequestedDataTransferCount ( SCSITaskIdentifier request ) { SCSITask * scsiRequest; scsiRequest = OSDynamicCast ( SCSITask, request ); // Check to see what the current execution mode is if ( scsiRequest->GetTaskExecutionMode ( ) == kSCSITaskMode_CommandExecution ) { return scsiRequest->GetRequestedDataTransferCount ( ); } else { return scsiRequest->GetAutosenseRequestedDataTransferCount ( ); } } bool IOSCSIProtocolServices::SetRealizedDataTransferCount ( SCSITaskIdentifier request, UInt64 newRealizedDataCount ) { SCSITask * scsiRequest; scsiRequest = OSDynamicCast ( SCSITask, request ); // Check to see what the current execution mode is if ( scsiRequest->GetTaskExecutionMode ( ) == kSCSITaskMode_CommandExecution ) { return scsiRequest->SetRealizedDataTransferCount ( newRealizedDataCount ); } else { return scsiRequest->SetAutosenseRealizedDataCount ( newRealizedDataCount ); } } UInt64 IOSCSIProtocolServices::GetRealizedDataTransferCount ( SCSITaskIdentifier request ) { SCSITask * scsiRequest; scsiRequest = OSDynamicCast ( SCSITask, request ); // Check to see what the current execution mode is if ( scsiRequest->GetTaskExecutionMode ( ) == kSCSITaskMode_CommandExecution ) { return scsiRequest->GetRealizedDataTransferCount ( ); } else { return scsiRequest->GetAutosenseRealizedDataCount ( ); } } IOMemoryDescriptor * IOSCSIProtocolServices::GetDataBuffer ( SCSITaskIdentifier request ) { SCSITask * scsiRequest; scsiRequest = OSDynamicCast ( SCSITask, request ); if ( scsiRequest->GetTaskExecutionMode ( ) == kSCSITaskMode_CommandExecution ) { return scsiRequest->GetDataBuffer ( ); } else { return scsiRequest->GetAutosenseDataBuffer ( ); } } UInt64 IOSCSIProtocolServices::GetDataBufferOffset ( SCSITaskIdentifier request ) { SCSITask * scsiRequest; scsiRequest = OSDynamicCast ( SCSITask, request ); return scsiRequest->GetDataBufferOffset ( ); } UInt32 IOSCSIProtocolServices::GetTimeoutDuration ( SCSITaskIdentifier request ) { SCSITask * scsiRequest; scsiRequest = OSDynamicCast ( SCSITask, request ); return scsiRequest->GetTimeoutDuration ( ); } // Set the auto sense data that was returned for the SCSI Task. // A return value if true indicates that the data copied to the member // sense data structure, false indicates that the data could not be saved. bool IOSCSIProtocolServices::SetAutoSenseData ( SCSITaskIdentifier request, SCSI_Sense_Data * senseData ) { SCSITask * scsiRequest; scsiRequest = OSDynamicCast ( SCSITask, request ); return scsiRequest->SetAutoSenseData ( senseData ); } bool IOSCSIProtocolServices::SetProtocolLayerReference ( SCSITaskIdentifier request, void * newReferenceValue ) { SCSITask * scsiRequest; scsiRequest = OSDynamicCast ( SCSITask, request ); return scsiRequest->SetProtocolLayerReference ( newReferenceValue ); } void * IOSCSIProtocolServices::GetProtocolLayerReference ( SCSITaskIdentifier request ) { SCSITask * scsiRequest; scsiRequest = OSDynamicCast ( SCSITask, request ); return scsiRequest->GetProtocolLayerReference ( ); } bool IOSCSIProtocolServices::SetTaskExecutionMode ( SCSITaskIdentifier request, SCSITaskMode newTaskMode ) { SCSITask * scsiRequest; scsiRequest = OSDynamicCast ( SCSITask, request ); return scsiRequest->SetTaskExecutionMode ( newTaskMode ); } SCSITaskMode IOSCSIProtocolServices::GetTaskExecutionMode ( SCSITaskIdentifier request ) { SCSITask * scsiRequest; scsiRequest = OSDynamicCast ( SCSITask, request ); return scsiRequest->GetTaskExecutionMode ( ); } #pragma mark - #pragma mark SCSI Task Queue Management // Following are the commands used to manipulate the queue of pending SCSI Tasks. // Currently the queuing is strictly first in, first out. This needs to be changed // to support the SCSI queueing model in the SCSI Architecture Model-2 specification. // Add the SCSI Task to the queue. The Task's Attribute determines where in // the queue the Task is placed. void IOSCSIProtocolServices::AddSCSITaskToQueue ( SCSITaskIdentifier request ) { SCSITask * scsiRequest; STATUS_LOG ( ( "%s: AddSCSITaskToQueue called.\n", getName ( ) ) ); IOSimpleLockLock ( fQueueLock ); scsiRequest = OSDynamicCast ( SCSITask, request ); // Make sure that the new request does not have a following task. scsiRequest->EnqueueFollowingSCSITask ( NULL ); // Check to see if there are any tasks currently queued. if ( fSCSITaskQueueHead == NULL ) { // There are no other tasks currently queued, so // save this one as the head. fSCSITaskQueueHead = scsiRequest; } else { // There is at least one task currently in the queue, // Add the current one to the end. SCSITask * currentElement; currentElement = fSCSITaskQueueHead; while ( currentElement->GetFollowingSCSITask ( ) != NULL ) { currentElement = currentElement->GetFollowingSCSITask ( ); } currentElement->EnqueueFollowingSCSITask ( scsiRequest ); } IOSimpleLockUnlock ( fQueueLock ); } // Add the SCSI Task to the head of the queue. This is used when the task // has been removed from the head of the queue, but the subclass indicates // that it can not yet process this task. void IOSCSIProtocolServices::AddSCSITaskToHeadOfQueue ( SCSITask * request ) { IOSimpleLockLock ( fQueueLock ); // Make sure that the new request does not have a following task. request->EnqueueFollowingSCSITask ( fSCSITaskQueueHead ); fSCSITaskQueueHead = request; IOSimpleLockUnlock ( fQueueLock ); } // Remove the next SCSI Task for the queue and return it. SCSITask * IOSCSIProtocolServices::RetrieveNextSCSITaskFromQueue ( void ) { SCSITask * selectedTask; IOSimpleLockLock ( fQueueLock ); // Check to see if there are any tasks currently queued. if ( fSCSITaskQueueHead == NULL ) { // There are currently no tasks queued, return NULL. selectedTask = NULL; } else { // There is at least one task currently in the queue, // Grab the head task selectedTask = fSCSITaskQueueHead; // Set the head pointer to the next task in the queue. If there // is no more tasks, this head pointer will be set to NULL. fSCSITaskQueueHead = selectedTask->GetFollowingSCSITask ( ); // Make sure that the new request does not have a following task. selectedTask->EnqueueFollowingSCSITask ( NULL ); } IOSimpleLockUnlock ( fQueueLock ); return selectedTask; } // Check to see if the SCSI Task resides and abort it if it does. // This currently does nothing since the ABORT TASK and ABORT TASK SET // managment functions are not supported. bool IOSCSIProtocolServices::AbortSCSITaskFromQueue ( SCSITask *request ) { // If the indicated SCSI Task currently resides in the Queue, the SCSI Task // will be removed and no further processing shall occur on that Task. This // method will then return true. // If the SCSI Task does not currently reside in the queue, this method will // return false. return false; } // void IOSCSIProtocolServices::SendSCSITasksFromQueue ( void ) { bool cmdAccepted = false; SCSITask * nextVictim; do { SCSIServiceResponse serviceResponse; SCSITaskStatus taskStatus; // get the next command from the request queue nextVictim = RetrieveNextSCSITaskFromQueue ( ); if ( nextVictim != NULL ) { cmdAccepted = SendSCSICommand ( nextVictim, &serviceResponse, &taskStatus ); if ( cmdAccepted == false ) { // The subclass can not process the command at this time, // add it to the queue and try again at CommandComplete time. AddSCSITaskToHeadOfQueue ( nextVictim ); } else if ( serviceResponse != kSCSIServiceResponse_Request_In_Process ) { // The command was sent and completed, send next Task based on its Attribute. nextVictim->SetServiceResponse ( serviceResponse ); nextVictim->SetTaskStatus ( taskStatus ); nextVictim->SetTaskState ( kSCSITaskState_ENDED ); } } } while ( ( cmdAccepted == true ) && ( nextVictim != NULL ) ); } // void IOSCSIProtocolServices::RejectSCSITasksCurrentlyQueued ( void ) { SCSITask * nextVictim; STATUS_LOG ( ( "%s: RejectSCSITasksCurrentlyQueued called.\n", getName ( ) ) ); do { // get the next command from the request queue nextVictim = RetrieveNextSCSITaskFromQueue ( ); if ( nextVictim != NULL ) { RejectTask ( nextVictim ); } } while ( nextVictim != NULL ); } void IOSCSIProtocolServices::ProcessCompletedTask ( SCSITaskIdentifier request, SCSIServiceResponse serviceResponse, SCSITaskStatus taskStatus ) { SCSITask * scsiRequest; STATUS_LOG ( ( "%s: ProcessCompletedTask called.\n", getName ( ) ) ); scsiRequest = OSDynamicCast ( SCSITask, request ); if ( scsiRequest->GetTaskExecutionMode ( ) == kSCSITaskMode_CommandExecution ) { // The task is currently in Command Execution mode, update the service // response and scsiRequest->SetServiceResponse ( serviceResponse ); // Check to see if this task was successfully transported to the // device server. if ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) { // Save that status into the Task object. scsiRequest->SetTaskStatus ( taskStatus ); // See if the conditions for autosenses has been met: the task completed // with a CHECK_CONDITION. if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( taskStatus == kSCSITaskStatus_CHECK_CONDITION ) ) { // Check if the client wants Autosense data if ( scsiRequest->IsAutosenseRequested ( ) == true ) { // Check if the Protocol has already provided this data if ( scsiRequest->GetAutoSenseData ( NULL ) == false ) { // Put the task into Autosense mode and // add to the head of the queue. scsiRequest->SetTaskExecutionMode ( kSCSITaskMode_Autosense ); AddSCSITaskToHeadOfQueue ( scsiRequest ); // The task can not be completed until the // autosense is done, so exit and wait for that // completion. return; } } } } } else { // the task is in Autosense mode, check to see if the autosense // command completed successfully. if ( ( serviceResponse == kSCSIServiceResponse_TASK_COMPLETE ) && ( taskStatus == kSCSITaskStatus_GOOD ) ) { scsiRequest->SetAutosenseIsValid ( true ); } } scsiRequest->SetTaskState ( kSCSITaskState_ENDED ); // The command is complete, release the retain for this command. release ( ); // The task has completed, execute the callback. scsiRequest->TaskCompletedNotification ( ); } void IOSCSIProtocolServices::RejectTask ( SCSITaskIdentifier request ) { SCSITask * scsiRequest; STATUS_LOG ( ( "%s: RejectTask called.\n", getName ( ) ) ); scsiRequest = OSDynamicCast ( SCSITask, request ); scsiRequest->SetTaskState ( kSCSITaskState_ENDED ); scsiRequest->SetServiceResponse ( kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE ); // Save that status into the Task object. scsiRequest->SetTaskStatus ( kSCSITaskStatus_No_Status ); // The command is complete, release the retain for this command. release ( ); scsiRequest->TaskCompletedNotification ( ); } #pragma mark - #pragma mark Provided Services to the SCSI Protocol Layer Subclasses void IOSCSIProtocolServices::CommandCompleted ( SCSITaskIdentifier request, SCSIServiceResponse serviceResponse, SCSITaskStatus taskStatus ) { STATUS_LOG ( ( "%s: CommandCompleted called.\n", getName ( ) ) ); // Check to see if service requests are allowed if ( fAllowServiceRequests == false ) { // Service requests are not allowed, return the task back // with an error. RejectTask ( request ); return; } ProcessCompletedTask ( request, serviceResponse, taskStatus ); SendSCSITasksFromQueue ( ); } #pragma mark - #pragma mark Provided Services to the SCSI Application Layer // ------------ The Export functions to the SCSI Application Layer -------------- // The ExecuteCommand function will take a SCSI Task and transport // it across the physical wire(s) to the device void IOSCSIProtocolServices::ExecuteCommand ( SCSITaskIdentifier request ) { STATUS_LOG ( ( "%s::%s called.\n", getName ( ), __FUNCTION__ ) ); // Make sure that the protocol driver does not go away // if there are outstanding commands. retain ( ); // Check to see if service requests are allowed if ( fAllowServiceRequests == false ) { // Service requests are not allowed, return the task back // immediately with an error RejectTask ( request ); return; } // Set the task state to ENABLED SetTaskState ( request, kSCSITaskState_ENABLED ); // Set the execution mode to indicate standard command execution. SetTaskExecutionMode ( request, kSCSITaskMode_CommandExecution ); // Add the new request to the queue AddSCSITaskToQueue ( request ); SendSCSITasksFromQueue ( ); } // The AbortCommand function will abort the indicated SCSI Task, // if it is possible and the Task has not already completed. SCSIServiceResponse IOSCSIProtocolServices::AbortCommand ( SCSITaskIdentifier request ) { return kSCSIServiceResponse_FUNCTION_REJECTED;