/** @file | |
SCSI Bus driver that layers on every SCSI Pass Thru and | |
Extended SCSI Pass Thru protocol in the system. | |
Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR> | |
Copyright (c) 1985 - 2022, American Megatrends International LLC.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "ScsiBus.h" | |
EFI_DRIVER_BINDING_PROTOCOL gSCSIBusDriverBinding = { | |
SCSIBusDriverBindingSupported, | |
SCSIBusDriverBindingStart, | |
SCSIBusDriverBindingStop, | |
0xa, | |
NULL, | |
NULL | |
}; | |
VOID *mWorkingBuffer; | |
/** | |
Convert EFI_SCSI_IO_SCSI_REQUEST_PACKET packet to EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET packet. | |
@param Packet The pointer of EFI_SCSI_IO_SCSI_REQUEST_PACKET | |
@param CommandPacket The pointer of EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET | |
**/ | |
EFI_STATUS | |
EFIAPI | |
ScsiioToPassThruPacket ( | |
IN EFI_SCSI_IO_SCSI_REQUEST_PACKET *Packet, | |
OUT EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *CommandPacket | |
); | |
/** | |
Convert EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET packet to EFI_SCSI_IO_SCSI_REQUEST_PACKET packet. | |
@param ScsiPacket The pointer of EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET | |
@param Packet The pointer of EFI_SCSI_IO_SCSI_REQUEST_PACKET | |
**/ | |
EFI_STATUS | |
EFIAPI | |
PassThruToScsiioPacket ( | |
IN EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *ScsiPacket, | |
OUT EFI_SCSI_IO_SCSI_REQUEST_PACKET *Packet | |
); | |
/** | |
Notify Function in which convert EFI1.0 PassThru Packet back to UEF2.0 | |
SCSI IO Packet. | |
@param Event The instance of EFI_EVENT. | |
@param Context The parameter passed in. | |
**/ | |
VOID | |
EFIAPI | |
NotifyFunction ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
); | |
/** | |
Allocates an aligned buffer for SCSI device. | |
This function allocates an aligned buffer for the SCSI device to perform | |
SCSI pass through operations. The alignment requirement is from SCSI pass | |
through interface. | |
@param ScsiIoDevice The SCSI child device involved for the operation. | |
@param BufferSize The request buffer size. | |
@return A pointer to the aligned buffer or NULL if the allocation fails. | |
**/ | |
VOID * | |
AllocateAlignedBuffer ( | |
IN SCSI_IO_DEV *ScsiIoDevice, | |
IN UINTN BufferSize | |
) | |
{ | |
return AllocateAlignedPages (EFI_SIZE_TO_PAGES (BufferSize), ScsiIoDevice->ScsiIo.IoAlign); | |
} | |
/** | |
Frees an aligned buffer for SCSI device. | |
This function frees an aligned buffer for the SCSI device to perform | |
SCSI pass through operations. | |
@param Buffer The aligned buffer to be freed. | |
@param BufferSize The request buffer size. | |
**/ | |
VOID | |
FreeAlignedBuffer ( | |
IN VOID *Buffer, | |
IN UINTN BufferSize | |
) | |
{ | |
if (Buffer != NULL) { | |
FreeAlignedPages (Buffer, EFI_SIZE_TO_PAGES (BufferSize)); | |
} | |
} | |
/** | |
The user Entry Point for module ScsiBus. The user code starts with this function. | |
@param ImageHandle The firmware allocated handle for the EFI image. | |
@param SystemTable A pointer to the EFI System Table. | |
@retval EFI_SUCCESS The entry point is executed successfully. | |
@retval other Some error occurs when executing this entry point. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
InitializeScsiBus ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
// | |
// Install driver model protocol(s). | |
// | |
Status = EfiLibInstallDriverBindingComponentName2 ( | |
ImageHandle, | |
SystemTable, | |
&gSCSIBusDriverBinding, | |
ImageHandle, | |
&gScsiBusComponentName, | |
&gScsiBusComponentName2 | |
); | |
ASSERT_EFI_ERROR (Status); | |
return Status; | |
} | |
/** | |
Test to see if this driver supports ControllerHandle. | |
This service is called by the EFI boot service ConnectController(). In order | |
to make drivers as small as possible, there are a few calling restrictions for | |
this service. ConnectController() must follow these calling restrictions. If | |
any other agent wishes to call Supported() it must also follow these calling | |
restrictions. | |
@param This Protocol instance pointer. | |
@param ControllerHandle Handle of device to test | |
@param RemainingDevicePath Optional parameter use to pick a specific child | |
device to start. | |
@retval EFI_SUCCESS This driver supports this device | |
@retval EFI_ALREADY_STARTED This driver is already running on this device | |
@retval other This driver does not support this device | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SCSIBusDriverBindingSupported ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE Controller, | |
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_SCSI_PASS_THRU_PROTOCOL *PassThru; | |
EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExtPassThru; | |
UINT64 Lun; | |
UINT8 *TargetId; | |
SCSI_TARGET_ID ScsiTargetId; | |
TargetId = &ScsiTargetId.ScsiId.ExtScsi[0]; | |
SetMem (TargetId, TARGET_MAX_BYTES, 0xFF); | |
// | |
// To keep backward compatibility, UEFI ExtPassThru Protocol is supported as well as | |
// EFI PassThru Protocol. From priority perspective, ExtPassThru Protocol is firstly | |
// tried to open on host controller handle. If fails, then PassThru Protocol is tried instead. | |
// | |
Status = gBS->OpenProtocol ( | |
Controller, | |
&gEfiExtScsiPassThruProtocolGuid, | |
(VOID **)&ExtPassThru, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_BY_DRIVER | |
); | |
if (Status == EFI_ALREADY_STARTED) { | |
return EFI_SUCCESS; | |
} else if (!EFI_ERROR (Status)) { | |
// | |
// Check if RemainingDevicePath is NULL or the End of Device Path Node, | |
// if yes, return EFI_SUCCESS. | |
// | |
if ((RemainingDevicePath == NULL) || IsDevicePathEnd (RemainingDevicePath)) { | |
// | |
// Close protocol regardless of RemainingDevicePath validation | |
// | |
gBS->CloseProtocol ( | |
Controller, | |
&gEfiExtScsiPassThruProtocolGuid, | |
This->DriverBindingHandle, | |
Controller | |
); | |
return EFI_SUCCESS; | |
} else { | |
// | |
// If RemainingDevicePath isn't the End of Device Path Node, check its validation | |
// | |
Status = ExtPassThru->GetTargetLun (ExtPassThru, RemainingDevicePath, &TargetId, &Lun); | |
// | |
// Close protocol regardless of RemainingDevicePath validation | |
// | |
gBS->CloseProtocol ( | |
Controller, | |
&gEfiExtScsiPassThruProtocolGuid, | |
This->DriverBindingHandle, | |
Controller | |
); | |
if (!EFI_ERROR (Status)) { | |
return EFI_SUCCESS; | |
} | |
} | |
} | |
// | |
// Come here in 2 condition: | |
// 1. ExtPassThru doesn't exist. | |
// 2. ExtPassThru exists but RemainingDevicePath is invalid. | |
// | |
Status = gBS->OpenProtocol ( | |
Controller, | |
&gEfiScsiPassThruProtocolGuid, | |
(VOID **)&PassThru, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_BY_DRIVER | |
); | |
if (Status == EFI_ALREADY_STARTED) { | |
return EFI_SUCCESS; | |
} | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Test RemainingDevicePath is valid or not. | |
// | |
if ((RemainingDevicePath != NULL) && !IsDevicePathEnd (RemainingDevicePath)) { | |
Status = PassThru->GetTargetLun (PassThru, RemainingDevicePath, &ScsiTargetId.ScsiId.Scsi, &Lun); | |
} | |
gBS->CloseProtocol ( | |
Controller, | |
&gEfiScsiPassThruProtocolGuid, | |
This->DriverBindingHandle, | |
Controller | |
); | |
return Status; | |
} | |
/** | |
Start this driver on ControllerHandle. | |
This service is called by the EFI boot service ConnectController(). In order | |
to make drivers as small as possible, there are a few calling restrictions for | |
this service. ConnectController() must follow these calling restrictions. If | |
any other agent wishes to call Start() it must also follow these calling | |
restrictions. | |
@param This Protocol instance pointer. | |
@param ControllerHandle Handle of device to bind driver to | |
@param RemainingDevicePath Optional parameter use to pick a specific child | |
device to start. | |
@retval EFI_SUCCESS This driver is added to ControllerHandle | |
@retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle | |
@retval other This driver does not support this device | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SCSIBusDriverBindingStart ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE Controller, | |
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
) | |
{ | |
UINT64 Lun; | |
UINT8 *TargetId; | |
BOOLEAN ScanOtherPuns; | |
BOOLEAN FromFirstTarget; | |
BOOLEAN ExtScsiSupport; | |
EFI_STATUS Status; | |
EFI_STATUS DevicePathStatus; | |
EFI_STATUS PassThruStatus; | |
SCSI_BUS_DEVICE *ScsiBusDev; | |
SCSI_TARGET_ID ScsiTargetId; | |
EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; | |
EFI_SCSI_PASS_THRU_PROTOCOL *ScsiInterface; | |
EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExtScsiInterface; | |
EFI_SCSI_BUS_PROTOCOL *BusIdentify; | |
TargetId = NULL; | |
ScanOtherPuns = TRUE; | |
FromFirstTarget = FALSE; | |
ExtScsiSupport = FALSE; | |
PassThruStatus = EFI_SUCCESS; | |
TargetId = &ScsiTargetId.ScsiId.ExtScsi[0]; | |
SetMem (TargetId, TARGET_MAX_BYTES, 0xFF); | |
DevicePathStatus = gBS->OpenProtocol ( | |
Controller, | |
&gEfiDevicePathProtocolGuid, | |
(VOID **)&ParentDevicePath, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_BY_DRIVER | |
); | |
if (EFI_ERROR (DevicePathStatus) && (DevicePathStatus != EFI_ALREADY_STARTED)) { | |
return DevicePathStatus; | |
} | |
// | |
// Report Status Code to indicate SCSI bus starts | |
// | |
REPORT_STATUS_CODE_WITH_DEVICE_PATH ( | |
EFI_PROGRESS_CODE, | |
(EFI_IO_BUS_SCSI | EFI_IOB_PC_INIT), | |
ParentDevicePath | |
); | |
// | |
// To keep backward compatibility, UEFI ExtPassThru Protocol is supported as well as | |
// EFI PassThru Protocol. From priority perspective, ExtPassThru Protocol is firstly | |
// tried to open on host controller handle. If fails, then PassThru Protocol is tried instead. | |
// | |
Status = gBS->OpenProtocol ( | |
Controller, | |
&gEfiExtScsiPassThruProtocolGuid, | |
(VOID **)&ExtScsiInterface, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_BY_DRIVER | |
); | |
// | |
// Fail to open UEFI ExtendPassThru Protocol, then try to open EFI PassThru Protocol instead. | |
// | |
if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { | |
Status = gBS->OpenProtocol ( | |
Controller, | |
&gEfiScsiPassThruProtocolGuid, | |
(VOID **)&ScsiInterface, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_BY_DRIVER | |
); | |
// | |
// Fail to open EFI PassThru Protocol, Close the DevicePathProtocol if it is opened by this time. | |
// | |
if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { | |
if (!EFI_ERROR (DevicePathStatus)) { | |
gBS->CloseProtocol ( | |
Controller, | |
&gEfiDevicePathProtocolGuid, | |
This->DriverBindingHandle, | |
Controller | |
); | |
} | |
return Status; | |
} | |
} else { | |
// | |
// Succeed to open ExtPassThru Protocol, and meanwhile open PassThru Protocol | |
// with BY_DRIVER if it is also present on the handle. The intent is to prevent | |
// another SCSI Bus Driver to work on the same host handle. | |
// | |
ExtScsiSupport = TRUE; | |
PassThruStatus = gBS->OpenProtocol ( | |
Controller, | |
&gEfiScsiPassThruProtocolGuid, | |
(VOID **)&ScsiInterface, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_BY_DRIVER | |
); | |
} | |
if (Status != EFI_ALREADY_STARTED) { | |
// | |
// Go through here means either ExtPassThru or PassThru Protocol is successfully opened | |
// on this handle for this time. Then construct Host controller private data. | |
// | |
ScsiBusDev = NULL; | |
ScsiBusDev = AllocateZeroPool (sizeof (SCSI_BUS_DEVICE)); | |
if (ScsiBusDev == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto ErrorExit; | |
} | |
ScsiBusDev->Signature = SCSI_BUS_DEVICE_SIGNATURE; | |
ScsiBusDev->ExtScsiSupport = ExtScsiSupport; | |
ScsiBusDev->DevicePath = ParentDevicePath; | |
if (ScsiBusDev->ExtScsiSupport) { | |
ScsiBusDev->ExtScsiInterface = ExtScsiInterface; | |
} else { | |
ScsiBusDev->ScsiInterface = ScsiInterface; | |
} | |
// | |
// Install EFI_SCSI_BUS_PROTOCOL to the controller handle, So ScsiBusDev could be | |
// retrieved on this controller handle. With ScsiBusDev, we can know which PassThru | |
// Protocol is present on the handle, UEFI ExtPassThru Protocol or EFI PassThru Protocol. | |
// | |
Status = gBS->InstallProtocolInterface ( | |
&Controller, | |
&gEfiCallerIdGuid, | |
EFI_NATIVE_INTERFACE, | |
&ScsiBusDev->BusIdentify | |
); | |
if (EFI_ERROR (Status)) { | |
goto ErrorExit; | |
} | |
} else { | |
// | |
// Go through here means Start() is re-invoked again, nothing special is required to do except | |
// picking up Host controller private information. | |
// | |
Status = gBS->OpenProtocol ( | |
Controller, | |
&gEfiCallerIdGuid, | |
(VOID **)&BusIdentify, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
ScsiBusDev = SCSI_BUS_CONTROLLER_DEVICE_FROM_THIS (BusIdentify); | |
} | |
// | |
// Report Status Code to indicate detecting devices on bus | |
// | |
REPORT_STATUS_CODE_WITH_DEVICE_PATH ( | |
EFI_PROGRESS_CODE, | |
(EFI_IO_BUS_SCSI | EFI_IOB_PC_DETECT), | |
ParentDevicePath | |
); | |
Lun = 0; | |
if (RemainingDevicePath == NULL) { | |
// | |
// If RemainingDevicePath is NULL, | |
// must enumerate all SCSI devices anyway | |
// | |
FromFirstTarget = TRUE; | |
} else if (!IsDevicePathEnd (RemainingDevicePath)) { | |
// | |
// If RemainingDevicePath isn't the End of Device Path Node, | |
// only scan the specified device by RemainingDevicePath | |
// | |
if (ScsiBusDev->ExtScsiSupport) { | |
Status = ScsiBusDev->ExtScsiInterface->GetTargetLun (ScsiBusDev->ExtScsiInterface, RemainingDevicePath, &TargetId, &Lun); | |
} else { | |
Status = ScsiBusDev->ScsiInterface->GetTargetLun (ScsiBusDev->ScsiInterface, RemainingDevicePath, &ScsiTargetId.ScsiId.Scsi, &Lun); | |
} | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} else { | |
// | |
// If RemainingDevicePath is the End of Device Path Node, | |
// skip enumerate any device and return EFI_SUCCESS | |
// | |
ScanOtherPuns = FALSE; | |
} | |
while (ScanOtherPuns) { | |
if (FromFirstTarget) { | |
// | |
// Remaining Device Path is NULL, scan all the possible Puns in the | |
// SCSI Channel. | |
// | |
if (ScsiBusDev->ExtScsiSupport) { | |
Status = ScsiBusDev->ExtScsiInterface->GetNextTargetLun (ScsiBusDev->ExtScsiInterface, &TargetId, &Lun); | |
} else { | |
Status = ScsiBusDev->ScsiInterface->GetNextDevice (ScsiBusDev->ScsiInterface, &ScsiTargetId.ScsiId.Scsi, &Lun); | |
} | |
if (EFI_ERROR (Status)) { | |
// | |
// no legal Pun and Lun found any more | |
// | |
break; | |
} | |
} else { | |
ScanOtherPuns = FALSE; | |
} | |
// | |
// Avoid creating handle for the host adapter. | |
// | |
if (ScsiBusDev->ExtScsiSupport) { | |
if ((ScsiTargetId.ScsiId.Scsi) == ScsiBusDev->ExtScsiInterface->Mode->AdapterId) { | |
continue; | |
} | |
} else { | |
if ((ScsiTargetId.ScsiId.Scsi) == ScsiBusDev->ScsiInterface->Mode->AdapterId) { | |
continue; | |
} | |
} | |
// | |
// Scan for the scsi device, if it attaches to the scsi bus, | |
// then create handle and install scsi i/o protocol. | |
// | |
Status = ScsiScanCreateDevice (This, Controller, &ScsiTargetId, Lun, ScsiBusDev); | |
if (Status == EFI_OUT_OF_RESOURCES) { | |
goto ErrorExit; | |
} | |
} | |
return EFI_SUCCESS; | |
ErrorExit: | |
if (ScsiBusDev != NULL) { | |
FreePool (ScsiBusDev); | |
} | |
if (ExtScsiSupport) { | |
gBS->CloseProtocol ( | |
Controller, | |
&gEfiExtScsiPassThruProtocolGuid, | |
This->DriverBindingHandle, | |
Controller | |
); | |
if (!EFI_ERROR (PassThruStatus)) { | |
gBS->CloseProtocol ( | |
Controller, | |
&gEfiScsiPassThruProtocolGuid, | |
This->DriverBindingHandle, | |
Controller | |
); | |
} | |
} else { | |
gBS->CloseProtocol ( | |
Controller, | |
&gEfiScsiPassThruProtocolGuid, | |
This->DriverBindingHandle, | |
Controller | |
); | |
} | |
return Status; | |
} | |
/** | |
Stop this driver on ControllerHandle. | |
This service is called by the EFI boot service DisconnectController(). | |
In order to make drivers as small as possible, there are a few calling | |
restrictions for this service. DisconnectController() must follow these | |
calling restrictions. If any other agent wishes to call Stop() it must also | |
follow these calling restrictions. | |
@param This Protocol instance pointer. | |
@param ControllerHandle Handle of device to stop driver on | |
@param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of | |
children is zero stop the entire bus driver. | |
@param ChildHandleBuffer List of Child Handles to Stop. | |
@retval EFI_SUCCESS This driver is removed ControllerHandle | |
@retval other This driver was not removed from this device | |
**/ | |
EFI_STATUS | |
EFIAPI | |
SCSIBusDriverBindingStop ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE Controller, | |
IN UINTN NumberOfChildren, | |
IN EFI_HANDLE *ChildHandleBuffer | |
) | |
{ | |
EFI_STATUS Status; | |
BOOLEAN AllChildrenStopped; | |
UINTN Index; | |
EFI_SCSI_IO_PROTOCOL *ScsiIo; | |
SCSI_IO_DEV *ScsiIoDevice; | |
VOID *ScsiPassThru; | |
EFI_SCSI_BUS_PROTOCOL *Scsidentifier; | |
SCSI_BUS_DEVICE *ScsiBusDev; | |
if (NumberOfChildren == 0) { | |
// | |
// Get the SCSI_BUS_DEVICE | |
// | |
Status = gBS->OpenProtocol ( | |
Controller, | |
&gEfiCallerIdGuid, | |
(VOID **)&Scsidentifier, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_DEVICE_ERROR; | |
} | |
ScsiBusDev = SCSI_BUS_CONTROLLER_DEVICE_FROM_THIS (Scsidentifier); | |
// | |
// Uninstall SCSI Bus Protocol | |
// | |
gBS->UninstallProtocolInterface ( | |
Controller, | |
&gEfiCallerIdGuid, | |
&ScsiBusDev->BusIdentify | |
); | |
// | |
// Close the bus driver | |
// | |
if (ScsiBusDev->ExtScsiSupport) { | |
// | |
// Close ExtPassThru Protocol from this controller handle | |
// | |
gBS->CloseProtocol ( | |
Controller, | |
&gEfiExtScsiPassThruProtocolGuid, | |
This->DriverBindingHandle, | |
Controller | |
); | |
// | |
// When Start() succeeds to open ExtPassThru, it always tries to open PassThru BY_DRIVER. | |
// Its intent is to prevent another SCSI Bus Driver from working on the same host handle. | |
// So Stop() needs to try to close PassThru if present here. | |
// | |
gBS->CloseProtocol ( | |
Controller, | |
&gEfiScsiPassThruProtocolGuid, | |
This->DriverBindingHandle, | |
Controller | |
); | |
} else { | |
gBS->CloseProtocol ( | |
Controller, | |
&gEfiScsiPassThruProtocolGuid, | |
This->DriverBindingHandle, | |
Controller | |
); | |
} | |
gBS->CloseProtocol ( | |
Controller, | |
&gEfiDevicePathProtocolGuid, | |
This->DriverBindingHandle, | |
Controller | |
); | |
FreePool (ScsiBusDev); | |
return EFI_SUCCESS; | |
} | |
AllChildrenStopped = TRUE; | |
for (Index = 0; Index < NumberOfChildren; Index++) { | |
Status = gBS->OpenProtocol ( | |
ChildHandleBuffer[Index], | |
&gEfiScsiIoProtocolGuid, | |
(VOID **)&ScsiIo, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
); | |
if (EFI_ERROR (Status)) { | |
AllChildrenStopped = FALSE; | |
continue; | |
} | |
ScsiIoDevice = SCSI_IO_DEV_FROM_THIS (ScsiIo); | |
// | |
// Close the child handle | |
// | |
if (ScsiIoDevice->ExtScsiSupport) { | |
Status = gBS->CloseProtocol ( | |
Controller, | |
&gEfiExtScsiPassThruProtocolGuid, | |
This->DriverBindingHandle, | |
ChildHandleBuffer[Index] | |
); | |
} else { | |
Status = gBS->CloseProtocol ( | |
Controller, | |
&gEfiScsiPassThruProtocolGuid, | |
This->DriverBindingHandle, | |
ChildHandleBuffer[Index] | |
); | |
} | |
Status = gBS->UninstallMultipleProtocolInterfaces ( | |
ChildHandleBuffer[Index], | |
&gEfiDevicePathProtocolGuid, | |
ScsiIoDevice->DevicePath, | |
&gEfiScsiIoProtocolGuid, | |
&ScsiIoDevice->ScsiIo, | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
AllChildrenStopped = FALSE; | |
if (ScsiIoDevice->ExtScsiSupport) { | |
gBS->OpenProtocol ( | |
Controller, | |
&gEfiExtScsiPassThruProtocolGuid, | |
&ScsiPassThru, | |
This->DriverBindingHandle, | |
ChildHandleBuffer[Index], | |
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER | |
); | |
} else { | |
gBS->OpenProtocol ( | |
Controller, | |
&gEfiScsiPassThruProtocolGuid, | |
&ScsiPassThru, | |
This->DriverBindingHandle, | |
ChildHandleBuffer[Index], | |
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER | |
); | |
} | |
} else { | |
FreePool (ScsiIoDevice); | |
} | |
} | |
if (!AllChildrenStopped) { | |
return EFI_DEVICE_ERROR; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Retrieves the device type information of the SCSI Controller. | |
@param This Protocol instance pointer. | |
@param DeviceType A pointer to the device type information retrieved from | |
the SCSI Controller. | |
@retval EFI_SUCCESS Retrieves the device type information successfully. | |
@retval EFI_INVALID_PARAMETER The DeviceType is NULL. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
ScsiGetDeviceType ( | |
IN EFI_SCSI_IO_PROTOCOL *This, | |
OUT UINT8 *DeviceType | |
) | |
{ | |
SCSI_IO_DEV *ScsiIoDevice; | |
if (DeviceType == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
ScsiIoDevice = SCSI_IO_DEV_FROM_THIS (This); | |
*DeviceType = ScsiIoDevice->ScsiDeviceType; | |
return EFI_SUCCESS; | |
} | |
/** | |
Retrieves the device location in the SCSI channel. | |
@param This Protocol instance pointer. | |
@param Target A pointer to the Target ID of a SCSI device | |
on the SCSI channel. | |
@param Lun A pointer to the LUN of the SCSI device on | |
the SCSI channel. | |
@retval EFI_SUCCESS Retrieves the device location successfully. | |
@retval EFI_INVALID_PARAMETER The Target or Lun is NULL. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
ScsiGetDeviceLocation ( | |
IN EFI_SCSI_IO_PROTOCOL *This, | |
IN OUT UINT8 **Target, | |
OUT UINT64 *Lun | |
) | |
{ | |
SCSI_IO_DEV *ScsiIoDevice; | |
if ((Target == NULL) || (Lun == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
ScsiIoDevice = SCSI_IO_DEV_FROM_THIS (This); | |
CopyMem (*Target, &ScsiIoDevice->Pun, TARGET_MAX_BYTES); | |
*Lun = ScsiIoDevice->Lun; | |
return EFI_SUCCESS; | |
} | |
/** | |
Resets the SCSI Bus that the SCSI Controller is attached to. | |
@param This Protocol instance pointer. | |
@retval EFI_SUCCESS The SCSI bus is reset successfully. | |
@retval EFI_DEVICE_ERROR Errors encountered when resetting the SCSI bus. | |
@retval EFI_UNSUPPORTED The bus reset operation is not supported by the | |
SCSI Host Controller. | |
@retval EFI_TIMEOUT A timeout occurred while attempting to reset | |
the SCSI bus. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
ScsiResetBus ( | |
IN EFI_SCSI_IO_PROTOCOL *This | |
) | |
{ | |
SCSI_IO_DEV *ScsiIoDevice; | |
ScsiIoDevice = SCSI_IO_DEV_FROM_THIS (This); | |
// | |
// Report Status Code to indicate reset happens | |
// | |
REPORT_STATUS_CODE_WITH_DEVICE_PATH ( | |
EFI_PROGRESS_CODE, | |
(EFI_IO_BUS_ATA_ATAPI | EFI_IOB_PC_RESET), | |
ScsiIoDevice->ScsiBusDeviceData->DevicePath | |
); | |
if (ScsiIoDevice->ExtScsiSupport) { | |
return ScsiIoDevice->ExtScsiPassThru->ResetChannel (ScsiIoDevice->ExtScsiPassThru); | |
} else { | |
return ScsiIoDevice->ScsiPassThru->ResetChannel (ScsiIoDevice->ScsiPassThru); | |
} | |
} | |
/** | |
Resets the SCSI Controller that the device handle specifies. | |
@param This Protocol instance pointer. | |
@retval EFI_SUCCESS Reset the SCSI controller successfully. | |
@retval EFI_DEVICE_ERROR Errors are encountered when resetting the SCSI Controller. | |
@retval EFI_UNSUPPORTED The SCSI bus does not support a device reset operation. | |
@retval EFI_TIMEOUT A timeout occurred while attempting to reset the | |
SCSI Controller. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
ScsiResetDevice ( | |
IN EFI_SCSI_IO_PROTOCOL *This | |
) | |
{ | |
SCSI_IO_DEV *ScsiIoDevice; | |
UINT8 Target[TARGET_MAX_BYTES]; | |
ScsiIoDevice = SCSI_IO_DEV_FROM_THIS (This); | |
// | |
// Report Status Code to indicate reset happens | |
// | |
REPORT_STATUS_CODE_WITH_DEVICE_PATH ( | |
EFI_PROGRESS_CODE, | |
(EFI_IO_BUS_ATA_ATAPI | EFI_IOB_PC_RESET), | |
ScsiIoDevice->ScsiBusDeviceData->DevicePath | |
); | |
CopyMem (Target, &ScsiIoDevice->Pun, TARGET_MAX_BYTES); | |
if (ScsiIoDevice->ExtScsiSupport) { | |
return ScsiIoDevice->ExtScsiPassThru->ResetTargetLun ( | |
ScsiIoDevice->ExtScsiPassThru, | |
Target, | |
ScsiIoDevice->Lun | |
); | |
} else { | |
return ScsiIoDevice->ScsiPassThru->ResetTarget ( | |
ScsiIoDevice->ScsiPassThru, | |
ScsiIoDevice->Pun.ScsiId.Scsi, | |
ScsiIoDevice->Lun | |
); | |
} | |
} | |
/** | |
Sends a SCSI Request Packet to the SCSI Controller for execution. | |
@param This Protocol instance pointer. | |
@param CommandPacket The SCSI request packet to send to the SCSI | |
Controller specified by the device handle. | |
@param Event If the SCSI bus where the SCSI device is attached | |
does not support non-blocking I/O, then Event is | |
ignored, and blocking I/O is performed. | |
If Event is NULL, then blocking I/O is performed. | |
If Event is not NULL and non-blocking I/O is | |
supported, then non-blocking I/O is performed, | |
and Event will be signaled when the SCSI Request | |
Packet completes. | |
@retval EFI_SUCCESS The SCSI Request Packet was sent by the host | |
successfully, and TransferLength bytes were | |
transferred to/from DataBuffer.See | |
HostAdapterStatus, TargetStatus, | |
SenseDataLength, and SenseData in that order | |
for additional status information. | |
@retval EFI_BAD_BUFFER_SIZE The SCSI Request Packet was executed, | |
but the entire DataBuffer could not be transferred. | |
The actual number of bytes transferred is returned | |
in TransferLength. See HostAdapterStatus, | |
TargetStatus, SenseDataLength, and SenseData in | |
that order for additional status information. | |
@retval EFI_NOT_READY The SCSI Request Packet could not be sent because | |
there are too many SCSI Command Packets already | |
queued.The caller may retry again later. | |
@retval EFI_DEVICE_ERROR A device error occurred while attempting to send | |
the SCSI Request Packet. See HostAdapterStatus, | |
TargetStatus, SenseDataLength, and SenseData in | |
that order for additional status information. | |
@retval EFI_INVALID_PARAMETER The contents of CommandPacket are invalid. | |
The SCSI Request Packet was not sent, so no | |
additional status information is available. | |
@retval EFI_UNSUPPORTED The command described by the SCSI Request Packet | |
is not supported by the SCSI initiator(i.e., SCSI | |
Host Controller). The SCSI Request Packet was not | |
sent, so no additional status information is | |
available. | |
@retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI | |
Request Packet to execute. See HostAdapterStatus, | |
TargetStatus, SenseDataLength, and SenseData in | |
that order for additional status information. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
ScsiExecuteSCSICommand ( | |
IN EFI_SCSI_IO_PROTOCOL *This, | |
IN OUT EFI_SCSI_IO_SCSI_REQUEST_PACKET *Packet, | |
IN EFI_EVENT Event OPTIONAL | |
) | |
{ | |
SCSI_IO_DEV *ScsiIoDevice; | |
EFI_STATUS Status; | |
UINT8 Target[TARGET_MAX_BYTES]; | |
EFI_EVENT PacketEvent; | |
EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *ExtRequestPacket; | |
SCSI_EVENT_DATA EventData; | |
PacketEvent = NULL; | |
if (Packet == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
ScsiIoDevice = SCSI_IO_DEV_FROM_THIS (This); | |
CopyMem (Target, &ScsiIoDevice->Pun, TARGET_MAX_BYTES); | |
if (ScsiIoDevice->ExtScsiSupport) { | |
ExtRequestPacket = (EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *)Packet; | |
if (((ScsiIoDevice->ExtScsiPassThru->Mode->Attributes & EFI_SCSI_PASS_THRU_ATTRIBUTES_NONBLOCKIO) != 0) && (Event != NULL)) { | |
Status = ScsiIoDevice->ExtScsiPassThru->PassThru ( | |
ScsiIoDevice->ExtScsiPassThru, | |
Target, | |
ScsiIoDevice->Lun, | |
ExtRequestPacket, | |
Event | |
); | |
} else { | |
// | |
// If there's no event or the SCSI Device doesn't support NON-BLOCKING, | |
// let the 'Event' parameter for PassThru() be NULL. | |
// | |
Status = ScsiIoDevice->ExtScsiPassThru->PassThru ( | |
ScsiIoDevice->ExtScsiPassThru, | |
Target, | |
ScsiIoDevice->Lun, | |
ExtRequestPacket, | |
NULL | |
); | |
if ((!EFI_ERROR (Status)) && (Event != NULL)) { | |
// | |
// Signal Event to tell caller to pick up the SCSI IO packet if the | |
// PassThru() succeeds. | |
// | |
gBS->SignalEvent (Event); | |
} | |
} | |
} else { | |
mWorkingBuffer = AllocatePool (sizeof (EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET)); | |
if (mWorkingBuffer == NULL) { | |
return EFI_DEVICE_ERROR; | |
} | |
// | |
// Convert package into EFI1.0, EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET. | |
// | |
Status = ScsiioToPassThruPacket (Packet, (EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *)mWorkingBuffer); | |
if (EFI_ERROR (Status)) { | |
FreePool (mWorkingBuffer); | |
return Status; | |
} | |
if (((ScsiIoDevice->ScsiPassThru->Mode->Attributes & EFI_SCSI_PASS_THRU_ATTRIBUTES_NONBLOCKIO) != 0) && (Event != NULL)) { | |
EventData.Data1 = (VOID *)Packet; | |
EventData.Data2 = Event; | |
// | |
// Create Event | |
// | |
Status = gBS->CreateEvent ( | |
EVT_NOTIFY_SIGNAL, | |
TPL_NOTIFY, | |
NotifyFunction, | |
&EventData, | |
&PacketEvent | |
); | |
if (EFI_ERROR (Status)) { | |
FreePool (mWorkingBuffer); | |
return Status; | |
} | |
Status = ScsiIoDevice->ScsiPassThru->PassThru ( | |
ScsiIoDevice->ScsiPassThru, | |
ScsiIoDevice->Pun.ScsiId.Scsi, | |
ScsiIoDevice->Lun, | |
mWorkingBuffer, | |
PacketEvent | |
); | |
if (EFI_ERROR (Status)) { | |
FreePool (mWorkingBuffer); | |
gBS->CloseEvent (PacketEvent); | |
return Status; | |
} | |
} else { | |
// | |
// If there's no event or SCSI Device doesn't support NON-BLOCKING, just convert | |
// EFI1.0 PassThru packet back to UEFI2.0 SCSI IO Packet. | |
// | |
Status = ScsiIoDevice->ScsiPassThru->PassThru ( | |
ScsiIoDevice->ScsiPassThru, | |
ScsiIoDevice->Pun.ScsiId.Scsi, | |
ScsiIoDevice->Lun, | |
mWorkingBuffer, | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
FreePool (mWorkingBuffer); | |
return Status; | |
} | |
PassThruToScsiioPacket ((EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *)mWorkingBuffer, Packet); | |
// | |
// After converting EFI1.0 PassThru Packet back to UEFI2.0 SCSI IO Packet, | |
// free mWorkingBuffer. | |
// | |
FreePool (mWorkingBuffer); | |
// | |
// Signal Event to tell caller to pick up the SCSI IO Packet. | |
// | |
if (Event != NULL) { | |
gBS->SignalEvent (Event); | |
} | |
} | |
} | |
return Status; | |
} | |
/** | |
Scan SCSI Bus to discover the device, and attach ScsiIoProtocol to it. | |
@param This Protocol instance pointer | |
@param Controller Controller handle | |
@param TargetId Target to be scanned | |
@param Lun The Lun of the SCSI device on the SCSI channel. | |
@param ScsiBusDev The pointer of SCSI_BUS_DEVICE | |
@retval EFI_SUCCESS Successfully to discover the device and attach | |
ScsiIoProtocol to it. | |
@retval EFI_NOT_FOUND Fail to discover the device. | |
@retval EFI_OUT_OF_RESOURCES Fail to allocate memory resources. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
ScsiScanCreateDevice ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE Controller, | |
IN SCSI_TARGET_ID *TargetId, | |
IN UINT64 Lun, | |
IN OUT SCSI_BUS_DEVICE *ScsiBusDev | |
) | |
{ | |
EFI_STATUS Status; | |
SCSI_IO_DEV *ScsiIoDevice; | |
EFI_DEVICE_PATH_PROTOCOL *ScsiDevicePath; | |
EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath; | |
EFI_HANDLE DeviceHandle; | |
DevicePath = NULL; | |
RemainingDevicePath = NULL; | |
ScsiDevicePath = NULL; | |
ScsiIoDevice = NULL; | |
// | |
// Build Device Path | |
// | |
if (ScsiBusDev->ExtScsiSupport) { | |
Status = ScsiBusDev->ExtScsiInterface->BuildDevicePath ( | |
ScsiBusDev->ExtScsiInterface, | |
&TargetId->ScsiId.ExtScsi[0], | |
Lun, | |
&ScsiDevicePath | |
); | |
} else { | |
Status = ScsiBusDev->ScsiInterface->BuildDevicePath ( | |
ScsiBusDev->ScsiInterface, | |
TargetId->ScsiId.Scsi, | |
Lun, | |
&ScsiDevicePath | |
); | |
} | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
DevicePath = AppendDevicePathNode ( | |
ScsiBusDev->DevicePath, | |
ScsiDevicePath | |
); | |
if (DevicePath == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto ErrorExit; | |
} | |
DeviceHandle = NULL; | |
RemainingDevicePath = DevicePath; | |
Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &RemainingDevicePath, &DeviceHandle); | |
if (!EFI_ERROR (Status) && (DeviceHandle != NULL) && IsDevicePathEnd (RemainingDevicePath)) { | |
// | |
// The device has been started, directly return to fast boot. | |
// | |
Status = EFI_ALREADY_STARTED; | |
goto ErrorExit; | |
} | |
ScsiIoDevice = AllocateZeroPool (sizeof (SCSI_IO_DEV)); | |
if (ScsiIoDevice == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto ErrorExit; | |
} | |
ScsiIoDevice->Signature = SCSI_IO_DEV_SIGNATURE; | |
ScsiIoDevice->ScsiBusDeviceData = ScsiBusDev; | |
CopyMem (&ScsiIoDevice->Pun, TargetId, TARGET_MAX_BYTES); | |
ScsiIoDevice->Lun = Lun; | |
if (ScsiBusDev->ExtScsiSupport) { | |
ScsiIoDevice->ExtScsiPassThru = ScsiBusDev->ExtScsiInterface; | |
ScsiIoDevice->ExtScsiSupport = TRUE; | |
ScsiIoDevice->ScsiIo.IoAlign = ScsiIoDevice->ExtScsiPassThru->Mode->IoAlign; | |
} else { | |
ScsiIoDevice->ScsiPassThru = ScsiBusDev->ScsiInterface; | |
ScsiIoDevice->ExtScsiSupport = FALSE; | |
ScsiIoDevice->ScsiIo.IoAlign = ScsiIoDevice->ScsiPassThru->Mode->IoAlign; | |
} | |
ScsiIoDevice->ScsiIo.GetDeviceType = ScsiGetDeviceType; | |
ScsiIoDevice->ScsiIo.GetDeviceLocation = ScsiGetDeviceLocation; | |
ScsiIoDevice->ScsiIo.ResetBus = ScsiResetBus; | |
ScsiIoDevice->ScsiIo.ResetDevice = ScsiResetDevice; | |
ScsiIoDevice->ScsiIo.ExecuteScsiCommand = ScsiExecuteSCSICommand; | |
// | |
// Report Status Code here since the new SCSI device will be discovered | |
// | |
REPORT_STATUS_CODE_WITH_DEVICE_PATH ( | |
EFI_PROGRESS_CODE, | |
(EFI_IO_BUS_SCSI | EFI_IOB_PC_ENABLE), | |
ScsiBusDev->DevicePath | |
); | |
Status = DiscoverScsiDevice (ScsiIoDevice); | |
if (EFI_ERROR (Status)) { | |
goto ErrorExit; | |
} | |
ScsiIoDevice->DevicePath = DevicePath; | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
&ScsiIoDevice->Handle, | |
&gEfiDevicePathProtocolGuid, | |
ScsiIoDevice->DevicePath, | |
&gEfiScsiIoProtocolGuid, | |
&ScsiIoDevice->ScsiIo, | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
goto ErrorExit; | |
} else { | |
if (ScsiBusDev->ExtScsiSupport) { | |
gBS->OpenProtocol ( | |
Controller, | |
&gEfiExtScsiPassThruProtocolGuid, | |
(VOID **)&(ScsiBusDev->ExtScsiInterface), | |
This->DriverBindingHandle, | |
ScsiIoDevice->Handle, | |
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER | |
); | |
} else { | |
gBS->OpenProtocol ( | |
Controller, | |
&gEfiScsiPassThruProtocolGuid, | |
(VOID **)&(ScsiBusDev->ScsiInterface), | |
This->DriverBindingHandle, | |
ScsiIoDevice->Handle, | |
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER | |
); | |
} | |
} | |
return EFI_SUCCESS; | |
ErrorExit: | |
// | |
// The memory space for ScsiDevicePath is allocated in | |
// ScsiPassThru->BuildDevicePath() function; It is no longer used | |
// after AppendDevicePathNode,so free the memory it occupies. | |
// | |
FreePool (ScsiDevicePath); | |
if (DevicePath != NULL) { | |
FreePool (DevicePath); | |
} | |
if (ScsiIoDevice != NULL) { | |
FreePool (ScsiIoDevice); | |
} | |
return Status; | |
} | |
/** | |
Discovery SCSI Device | |
@param ScsiIoDevice The pointer of SCSI_IO_DEV | |
@retval EFI_SUCCESS Find SCSI Device and verify it. | |
@retval EFI_NOT_FOUND Unable to find SCSI Device. | |
@retval EFI_OUT_OF_RESOURCES Fail to allocate memory resources. | |
**/ | |
EFI_STATUS | |
DiscoverScsiDevice ( | |
IN OUT SCSI_IO_DEV *ScsiIoDevice | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 InquiryDataLength; | |
UINT8 SenseDataLength; | |
UINT8 HostAdapterStatus; | |
UINT8 TargetStatus; | |
EFI_SCSI_INQUIRY_DATA *InquiryData; | |
EFI_SCSI_SENSE_DATA *SenseData; | |
UINT8 MaxRetry; | |
UINT8 Index; | |
HostAdapterStatus = 0; | |
TargetStatus = 0; | |
SenseData = NULL; | |
InquiryData = AllocateAlignedBuffer (ScsiIoDevice, sizeof (EFI_SCSI_INQUIRY_DATA)); | |
if (InquiryData == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto Done; | |
} | |
SenseData = AllocateAlignedBuffer ( | |
ScsiIoDevice, | |
sizeof (EFI_SCSI_SENSE_DATA) | |
); | |
if (SenseData == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto Done; | |
} | |
// | |
// Using Inquiry command to scan for the device | |
// | |
InquiryDataLength = sizeof (EFI_SCSI_INQUIRY_DATA); | |
SenseDataLength = sizeof (EFI_SCSI_SENSE_DATA); | |
ZeroMem (InquiryData, InquiryDataLength); | |
ZeroMem (SenseData, SenseDataLength); | |
MaxRetry = 2; | |
for (Index = 0; Index < MaxRetry; Index++) { | |
Status = ScsiInquiryCommand ( | |
&ScsiIoDevice->ScsiIo, | |
SCSI_BUS_TIMEOUT, | |
SenseData, | |
&SenseDataLength, | |
&HostAdapterStatus, | |
&TargetStatus, | |
(VOID *)InquiryData, | |
&InquiryDataLength, | |
FALSE | |
); | |
if (!EFI_ERROR (Status)) { | |
if ((HostAdapterStatus == EFI_SCSI_IO_STATUS_HOST_ADAPTER_OK) && | |
(TargetStatus == EFI_SCSI_IO_STATUS_TARGET_CHECK_CONDITION) && | |
(SenseData->Error_Code == 0x70) && | |
(SenseData->Sense_Key == EFI_SCSI_SK_ILLEGAL_REQUEST)) | |
{ | |
Status = EFI_NOT_FOUND; | |
goto Done; | |
} | |
break; | |
} | |
if ((Status == EFI_BAD_BUFFER_SIZE) || | |
(Status == EFI_INVALID_PARAMETER) || | |
(Status == EFI_UNSUPPORTED)) | |
{ | |
Status = EFI_NOT_FOUND; | |
goto Done; | |
} | |
} | |
if (Index == MaxRetry) { | |
Status = EFI_NOT_FOUND; | |
goto Done; | |
} | |
// | |
// Retrieved inquiry data successfully | |
// | |
if (InquiryData->Peripheral_Qualifier != 0) { | |
Status = EFI_NOT_FOUND; | |
goto Done; | |
} | |
if ((InquiryData->Peripheral_Type >= EFI_SCSI_TYPE_RESERVED_LOW) && | |
(InquiryData->Peripheral_Type <= EFI_SCSI_TYPE_RESERVED_HIGH)) | |
{ | |
Status = EFI_NOT_FOUND; | |
goto Done; | |
} | |
// | |
// valid device type and peripheral qualifier combination. | |
// | |
ScsiIoDevice->ScsiDeviceType = InquiryData->Peripheral_Type; | |
ScsiIoDevice->RemovableDevice = InquiryData->Rmb; | |
if (InquiryData->Version == 0) { | |
ScsiIoDevice->ScsiVersion = 0; | |
} else { | |
// | |
// ANSI-approved version | |
// | |
ScsiIoDevice->ScsiVersion = (UINT8)(InquiryData->Version & 0x07); | |
} | |
Status = EFI_SUCCESS; | |
Done: | |
FreeAlignedBuffer (SenseData, sizeof (EFI_SCSI_SENSE_DATA)); | |
FreeAlignedBuffer (InquiryData, sizeof (EFI_SCSI_INQUIRY_DATA)); | |
return Status; | |
} | |
/** | |
Convert EFI_SCSI_IO_SCSI_REQUEST_PACKET packet to EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET packet. | |
@param Packet The pointer of EFI_SCSI_IO_SCSI_REQUEST_PACKET | |
@param CommandPacket The pointer of EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET | |
**/ | |
EFI_STATUS | |
EFIAPI | |
ScsiioToPassThruPacket ( | |
IN EFI_SCSI_IO_SCSI_REQUEST_PACKET *Packet, | |
OUT EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *CommandPacket | |
) | |
{ | |
// | |
// EFI 1.10 doesn't support Bi-Direction Command. | |
// | |
if (Packet->DataDirection == EFI_SCSI_IO_DATA_DIRECTION_BIDIRECTIONAL) { | |
return EFI_UNSUPPORTED; | |
} | |
ZeroMem (CommandPacket, sizeof (EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET)); | |
CommandPacket->Timeout = Packet->Timeout; | |
CommandPacket->Cdb = Packet->Cdb; | |
CommandPacket->CdbLength = Packet->CdbLength; | |
CommandPacket->DataDirection = Packet->DataDirection; | |
CommandPacket->HostAdapterStatus = Packet->HostAdapterStatus; | |
CommandPacket->TargetStatus = Packet->TargetStatus; | |
CommandPacket->SenseData = Packet->SenseData; | |
CommandPacket->SenseDataLength = Packet->SenseDataLength; | |
if (Packet->DataDirection == EFI_SCSI_IO_DATA_DIRECTION_READ) { | |
CommandPacket->DataBuffer = Packet->InDataBuffer; | |
CommandPacket->TransferLength = Packet->InTransferLength; | |
} else if (Packet->DataDirection == EFI_SCSI_IO_DATA_DIRECTION_WRITE) { | |
CommandPacket->DataBuffer = Packet->OutDataBuffer; | |
CommandPacket->TransferLength = Packet->OutTransferLength; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Convert EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET packet to EFI_SCSI_IO_SCSI_REQUEST_PACKET packet. | |
@param ScsiPacket The pointer of EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET | |
@param Packet The pointer of EFI_SCSI_IO_SCSI_REQUEST_PACKET | |
**/ | |
EFI_STATUS | |
EFIAPI | |
PassThruToScsiioPacket ( | |
IN EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *ScsiPacket, | |
OUT EFI_SCSI_IO_SCSI_REQUEST_PACKET *Packet | |
) | |
{ | |
Packet->Timeout = ScsiPacket->Timeout; | |
Packet->Cdb = ScsiPacket->Cdb; | |
Packet->CdbLength = ScsiPacket->CdbLength; | |
Packet->DataDirection = ScsiPacket->DataDirection; | |
Packet->HostAdapterStatus = ScsiPacket->HostAdapterStatus; | |
Packet->TargetStatus = ScsiPacket->TargetStatus; | |
Packet->SenseData = ScsiPacket->SenseData; | |
Packet->SenseDataLength = ScsiPacket->SenseDataLength; | |
if (ScsiPacket->DataDirection == EFI_SCSI_IO_DATA_DIRECTION_READ) { | |
Packet->InDataBuffer = ScsiPacket->DataBuffer; | |
Packet->InTransferLength = ScsiPacket->TransferLength; | |
} else if (Packet->DataDirection == EFI_SCSI_IO_DATA_DIRECTION_WRITE) { | |
Packet->OutDataBuffer = ScsiPacket->DataBuffer; | |
Packet->OutTransferLength = ScsiPacket->TransferLength; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Notify Function in which convert EFI1.0 PassThru Packet back to UEF2.0 | |
SCSI IO Packet. | |
@param Event The instance of EFI_EVENT. | |
@param Context The parameter passed in. | |
**/ | |
VOID | |
EFIAPI | |
NotifyFunction ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
EFI_SCSI_IO_SCSI_REQUEST_PACKET *Packet; | |
EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *ScsiPacket; | |
EFI_EVENT CallerEvent; | |
SCSI_EVENT_DATA *PassData; | |
PassData = (SCSI_EVENT_DATA *)Context; | |
Packet = (EFI_SCSI_IO_SCSI_REQUEST_PACKET *)PassData->Data1; | |
ScsiPacket = (EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *)mWorkingBuffer; | |
// | |
// Convert EFI1.0 PassThru packet to UEFI2.0 SCSI IO Packet. | |
// | |
PassThruToScsiioPacket (ScsiPacket, Packet); | |
// | |
// After converting EFI1.0 PassThru Packet back to UEFI2.0 SCSI IO Packet, | |
// free mWorkingBuffer. | |
// | |
gBS->FreePool (mWorkingBuffer); | |
// | |
// Signal Event to tell caller to pick up UEFI2.0 SCSI IO Packet. | |
// | |
CallerEvent = PassData->Data2; | |
gBS->CloseEvent (Event); | |
gBS->SignalEvent (CallerEvent); | |
} |