/** @file | |
Copyright (c) 2006, Intel Corporation. All rights reserved.<BR> | |
This program and the accompanying materials | |
are licensed and made available under the terms and conditions of the BSD License | |
which accompanies this distribution. The full text of the license may be found at | |
http://opensource.org/licenses/bsd-license.php | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
**/ | |
#include "AtapiPassThru.h" | |
SCSI_COMMAND_SET gEndTable = { 0xff, (DATA_DIRECTION) 0xff }; | |
/// | |
/// This table contains all the supported ATAPI commands. | |
/// | |
SCSI_COMMAND_SET gSupportedATAPICommands[] = { | |
{ OP_INQUIRY, DataIn }, | |
{ OP_LOAD_UNLOAD_CD, NoData }, | |
{ OP_MECHANISM_STATUS, DataIn }, | |
{ OP_MODE_SELECT_10, DataOut }, | |
{ OP_MODE_SENSE_10, DataIn }, | |
{ OP_PAUSE_RESUME, NoData }, | |
{ OP_PLAY_AUDIO_10, DataIn }, | |
{ OP_PLAY_AUDIO_MSF, DataIn }, | |
{ OP_PLAY_CD, DataIn }, | |
{ OP_PLAY_CD_MSF, DataIn }, | |
{ OP_PREVENT_ALLOW_MEDIUM_REMOVAL,NoData }, | |
{ OP_READ_10, DataIn }, | |
{ OP_READ_12, DataIn }, | |
{ OP_READ_CAPACITY, DataIn }, | |
{ OP_READ_CD, DataIn }, | |
{ OP_READ_CD_MSF, DataIn }, | |
{ OP_READ_HEADER, DataIn }, | |
{ OP_READ_SUB_CHANNEL, DataIn }, | |
{ OP_READ_TOC, DataIn }, | |
{ OP_REQUEST_SENSE, DataIn }, | |
{ OP_SCAN, NoData }, | |
{ OP_SEEK_10, NoData }, | |
{ OP_SET_CD_SPEED, DataOut }, | |
{ OP_STOPPLAY_SCAN, NoData }, | |
{ OP_START_STOP_UNIT, NoData }, | |
{ OP_TEST_UNIT_READY, NoData }, | |
{ OP_FORMAT_UNIT, DataOut }, | |
{ OP_READ_FORMAT_CAPACITIES, DataIn }, | |
{ OP_VERIFY, DataOut }, | |
{ OP_WRITE_10, DataOut }, | |
{ OP_WRITE_12, DataOut }, | |
{ OP_WRITE_AND_VERIFY, DataOut }, | |
{ 0xff, (DATA_DIRECTION) 0xff } | |
}; | |
GLOBAL_REMOVE_IF_UNREFERENCED EFI_SCSI_PASS_THRU_MODE gScsiPassThruMode = { | |
L"ATAPI Controller", | |
L"ATAPI Channel", | |
4, | |
EFI_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | EFI_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL, | |
0 | |
}; | |
GLOBAL_REMOVE_IF_UNREFERENCED EFI_SCSI_PASS_THRU_PROTOCOL gScsiPassThruProtocolTemplate = { | |
&gScsiPassThruMode, | |
AtapiScsiPassThruFunction, | |
AtapiScsiPassThruGetNextDevice, | |
AtapiScsiPassThruBuildDevicePath, | |
AtapiScsiPassThruGetTargetLun, | |
AtapiScsiPassThruResetChannel, | |
AtapiScsiPassThruResetTarget | |
}; | |
GLOBAL_REMOVE_IF_UNREFERENCED EFI_EXT_SCSI_PASS_THRU_MODE gExtScsiPassThruMode = { | |
4, | |
EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL, | |
0 | |
}; | |
GLOBAL_REMOVE_IF_UNREFERENCED EFI_EXT_SCSI_PASS_THRU_PROTOCOL gExtScsiPassThruProtocolTemplate = { | |
&gExtScsiPassThruMode, | |
AtapiExtScsiPassThruFunction, | |
AtapiExtScsiPassThruGetNextTargetLun, | |
AtapiExtScsiPassThruBuildDevicePath, | |
AtapiExtScsiPassThruGetTargetLun, | |
AtapiExtScsiPassThruResetChannel, | |
AtapiExtScsiPassThruResetTarget, | |
AtapiExtScsiPassThruGetNextTarget | |
}; | |
EFI_DRIVER_BINDING_PROTOCOL gAtapiScsiPassThruDriverBinding = { | |
AtapiScsiPassThruDriverBindingSupported, | |
AtapiScsiPassThruDriverBindingStart, | |
AtapiScsiPassThruDriverBindingStop, | |
0x10, | |
NULL, | |
NULL | |
}; | |
EFI_STATUS | |
EFIAPI | |
AtapiScsiPassThruDriverBindingSupported ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE Controller, | |
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
) | |
/*++ | |
Routine Description: | |
Test to see if this driver supports ControllerHandle. Any ControllerHandle | |
that has gEfiPciIoProtocolGuid installed and is IDE Controller it will be supported. | |
Arguments: | |
This - Protocol instance pointer. | |
Controller - Handle of device to test | |
RemainingDevicePath - Not used | |
Returns: | |
EFI_STATUS | |
--*/ | |
{ | |
EFI_STATUS Status; | |
EFI_PCI_IO_PROTOCOL *PciIo; | |
PCI_TYPE00 Pci; | |
// | |
// Open the IO Abstraction(s) needed to perform the supported test | |
// | |
Status = gBS->OpenProtocol ( | |
Controller, | |
&gEfiPciIoProtocolGuid, | |
(VOID **) &PciIo, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_BY_DRIVER | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Use the PCI I/O Protocol to see if Controller is a IDE Controller that | |
// can be managed by this driver. Read the PCI Configuration Header | |
// for this device. | |
// | |
Status = PciIo->Pci.Read ( | |
PciIo, | |
EfiPciIoWidthUint32, | |
0, | |
sizeof (Pci) / sizeof (UINT32), | |
&Pci | |
); | |
if (EFI_ERROR (Status)) { | |
gBS->CloseProtocol ( | |
Controller, | |
&gEfiPciIoProtocolGuid, | |
This->DriverBindingHandle, | |
Controller | |
); | |
return EFI_UNSUPPORTED; | |
} | |
if (Pci.Hdr.ClassCode[2] != PCI_CLASS_MASS_STORAGE || Pci.Hdr.ClassCode[1] != PCI_CLASS_MASS_STORAGE_IDE) { | |
Status = EFI_UNSUPPORTED; | |
} | |
gBS->CloseProtocol ( | |
Controller, | |
&gEfiPciIoProtocolGuid, | |
This->DriverBindingHandle, | |
Controller | |
); | |
return Status; | |
} | |
EFI_STATUS | |
EFIAPI | |
AtapiScsiPassThruDriverBindingStart ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE Controller, | |
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
) | |
/*++ | |
Routine Description: | |
Create handles for IDE channels specified by RemainingDevicePath. | |
Install SCSI Pass Thru Protocol onto each created handle. | |
Arguments: | |
This - Protocol instance pointer. | |
Controller - Handle of device to test | |
RemainingDevicePath - Not used | |
Returns: | |
EFI_STATUS | |
--*/ | |
{ | |
EFI_STATUS Status; | |
EFI_PCI_IO_PROTOCOL *PciIo; | |
UINT64 Supports; | |
UINT64 OriginalPciAttributes; | |
BOOLEAN PciAttributesSaved; | |
PciIo = NULL; | |
Status = gBS->OpenProtocol ( | |
Controller, | |
&gEfiPciIoProtocolGuid, | |
(VOID **) &PciIo, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_BY_DRIVER | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
PciAttributesSaved = FALSE; | |
// | |
// Save original PCI attributes | |
// | |
Status = PciIo->Attributes ( | |
PciIo, | |
EfiPciIoAttributeOperationGet, | |
0, | |
&OriginalPciAttributes | |
); | |
if (EFI_ERROR (Status)) { | |
goto Done; | |
} | |
PciAttributesSaved = TRUE; | |
Status = PciIo->Attributes ( | |
PciIo, | |
EfiPciIoAttributeOperationSupported, | |
0, | |
&Supports | |
); | |
if (!EFI_ERROR (Status)) { | |
Supports &= (EFI_PCI_DEVICE_ENABLE | | |
EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO | | |
EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO); | |
Status = PciIo->Attributes ( | |
PciIo, | |
EfiPciIoAttributeOperationEnable, | |
Supports, | |
NULL | |
); | |
} | |
if (EFI_ERROR (Status)) { | |
goto Done; | |
} | |
// | |
// Create SCSI Pass Thru instance for the IDE channel. | |
// | |
Status = RegisterAtapiScsiPassThru (This, Controller, PciIo, OriginalPciAttributes); | |
Done: | |
if (EFI_ERROR (Status)) { | |
if (PciAttributesSaved == TRUE) { | |
// | |
// Restore original PCI attributes | |
// | |
PciIo->Attributes ( | |
PciIo, | |
EfiPciIoAttributeOperationSet, | |
OriginalPciAttributes, | |
NULL | |
); | |
} | |
gBS->CloseProtocol ( | |
Controller, | |
&gEfiPciIoProtocolGuid, | |
This->DriverBindingHandle, | |
Controller | |
); | |
} | |
return Status; | |
} | |
EFI_STATUS | |
EFIAPI | |
AtapiScsiPassThruDriverBindingStop ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE Controller, | |
IN UINTN NumberOfChildren, | |
IN EFI_HANDLE *ChildHandleBuffer | |
) | |
/*++ | |
Routine Description: | |
Stop this driver on ControllerHandle. Support stoping any child handles | |
created by this driver. | |
Arguments: | |
This - Protocol instance pointer. | |
Controller - Handle of device to stop driver on | |
NumberOfChildren - Number of Children in the ChildHandleBuffer | |
ChildHandleBuffer - List of handles for the children we need to stop. | |
Returns: | |
EFI_STATUS | |
--*/ | |
{ | |
EFI_STATUS Status; | |
EFI_SCSI_PASS_THRU_PROTOCOL *ScsiPassThru; | |
EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExtScsiPassThru; | |
ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; | |
if (FeaturePcdGet (PcdSupportScsiPassThru)) { | |
Status = gBS->OpenProtocol ( | |
Controller, | |
&gEfiScsiPassThruProtocolGuid, | |
(VOID **) &ScsiPassThru, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (ScsiPassThru); | |
if (FeaturePcdGet (PcdSupportExtScsiPassThru)) { | |
Status = gBS->UninstallMultipleProtocolInterfaces ( | |
Controller, | |
&gEfiScsiPassThruProtocolGuid, | |
&AtapiScsiPrivate->ScsiPassThru, | |
&gEfiExtScsiPassThruProtocolGuid, | |
&AtapiScsiPrivate->ExtScsiPassThru, | |
NULL | |
); | |
} else { | |
Status = gBS->UninstallMultipleProtocolInterfaces ( | |
Controller, | |
&gEfiScsiPassThruProtocolGuid, | |
&AtapiScsiPrivate->ScsiPassThru, | |
NULL | |
); | |
} | |
} else { | |
Status = gBS->OpenProtocol ( | |
Controller, | |
&gEfiExtScsiPassThruProtocolGuid, | |
(VOID **) &ExtScsiPassThru, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
AtapiScsiPrivate = ATAPI_EXT_SCSI_PASS_THRU_DEV_FROM_THIS (ExtScsiPassThru); | |
Status = gBS->UninstallMultipleProtocolInterfaces ( | |
Controller, | |
&gEfiExtScsiPassThruProtocolGuid, | |
&AtapiScsiPrivate->ExtScsiPassThru, | |
NULL | |
); | |
} | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Restore original PCI attributes | |
// | |
AtapiScsiPrivate->PciIo->Attributes ( | |
AtapiScsiPrivate->PciIo, | |
EfiPciIoAttributeOperationSet, | |
AtapiScsiPrivate->OriginalPciAttributes, | |
NULL | |
); | |
gBS->CloseProtocol ( | |
Controller, | |
&gEfiPciIoProtocolGuid, | |
This->DriverBindingHandle, | |
Controller | |
); | |
gBS->FreePool (AtapiScsiPrivate); | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
RegisterAtapiScsiPassThru ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE Controller, | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT64 OriginalPciAttributes | |
) | |
/*++ | |
Routine Description: | |
Attaches SCSI Pass Thru Protocol for specified IDE channel. | |
Arguments: | |
This - Protocol instance pointer. | |
Controller - Parent device handle to the IDE channel. | |
PciIo - PCI I/O protocol attached on the "Controller". | |
Returns: | |
Always return EFI_SUCCESS unless installing SCSI Pass Thru Protocol failed. | |
--*/ | |
{ | |
EFI_STATUS Status; | |
ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; | |
IDE_REGISTERS_BASE_ADDR IdeRegsBaseAddr[ATAPI_MAX_CHANNEL]; | |
AtapiScsiPrivate = AllocateZeroPool (sizeof (ATAPI_SCSI_PASS_THRU_DEV)); | |
if (AtapiScsiPrivate == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
AtapiScsiPrivate->Signature = ATAPI_SCSI_PASS_THRU_DEV_SIGNATURE; | |
AtapiScsiPrivate->Handle = Controller; | |
// | |
// will reset the IoPort inside each API function. | |
// | |
AtapiScsiPrivate->IoPort = NULL; | |
AtapiScsiPrivate->PciIo = PciIo; | |
AtapiScsiPrivate->OriginalPciAttributes = OriginalPciAttributes; | |
// | |
// Obtain IDE IO port registers' base addresses | |
// | |
Status = GetIdeRegistersBaseAddr (PciIo, IdeRegsBaseAddr); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
InitAtapiIoPortRegisters(AtapiScsiPrivate, IdeRegsBaseAddr); | |
// | |
// Initialize the LatestTargetId to MAX_TARGET_ID. | |
// | |
AtapiScsiPrivate->LatestTargetId = MAX_TARGET_ID; | |
AtapiScsiPrivate->LatestLun = 0; | |
Status = InstallScsiPassThruProtocols (&Controller, AtapiScsiPrivate); | |
return Status; | |
} | |
EFI_STATUS | |
EFIAPI | |
AtapiScsiPassThruFunction ( | |
IN EFI_SCSI_PASS_THRU_PROTOCOL *This, | |
IN UINT32 Target, | |
IN UINT64 Lun, | |
IN OUT EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, | |
IN EFI_EVENT Event OPTIONAL | |
) | |
/*++ | |
Routine Description: | |
Implements EFI_SCSI_PASS_THRU_PROTOCOL.PassThru() function. | |
Arguments: | |
This: The EFI_SCSI_PASS_THRU_PROTOCOL instance. | |
Target: The Target ID of the ATAPI device to send the SCSI | |
Request Packet. To ATAPI devices attached on an IDE | |
Channel, Target ID 0 indicates Master device;Target | |
ID 1 indicates Slave device. | |
Lun: The LUN of the ATAPI device to send the SCSI Request | |
Packet. To the ATAPI device, Lun is always 0. | |
Packet: The SCSI Request Packet to send to the ATAPI device | |
specified by Target and Lun. | |
Event: If non-blocking I/O is not supported 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. | |
Returns: | |
EFI_STATUS | |
--*/ | |
{ | |
ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; | |
EFI_STATUS Status; | |
AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (This); | |
// | |
// Target is not allowed beyond MAX_TARGET_ID | |
// | |
if ((Target > MAX_TARGET_ID) || (Lun != 0)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// check the data fields in Packet parameter. | |
// | |
Status = CheckSCSIRequestPacket (Packet); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// If Request Packet targets at the IDE channel itself, | |
// do nothing. | |
// | |
if (Target == This->Mode->AdapterId) { | |
Packet->TransferLength = 0; | |
return EFI_SUCCESS; | |
} | |
// | |
// According to Target ID, reset the Atapi I/O Register mapping | |
// (Target Id in [0,1] area, using AtapiIoPortRegisters[0], | |
// Target Id in [2,3] area, using AtapiIoPortRegisters[1] | |
// | |
if ((Target / 2) == 0) { | |
Target = Target % 2; | |
AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[0]; | |
} else { | |
Target = Target % 2; | |
AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[1]; | |
} | |
// | |
// the ATAPI SCSI interface does not support non-blocking I/O | |
// ignore the Event parameter | |
// | |
// Performs blocking I/O. | |
// | |
Status = SubmitBlockingIoCommand (AtapiScsiPrivate, Target, Packet); | |
return Status; | |
} | |
EFI_STATUS | |
EFIAPI | |
AtapiScsiPassThruGetNextDevice ( | |
IN EFI_SCSI_PASS_THRU_PROTOCOL *This, | |
IN OUT UINT32 *Target, | |
IN OUT UINT64 *Lun | |
) | |
/*++ | |
Routine Description: | |
Used to retrieve the list of legal Target IDs for SCSI devices | |
on a SCSI channel. | |
Arguments: | |
This - Protocol instance pointer. | |
Target - On input, a pointer to the Target ID of a SCSI | |
device present on the SCSI channel. On output, | |
a pointer to the Target ID of the next SCSI device | |
present on a SCSI channel. An input value of | |
0xFFFFFFFF retrieves the Target ID of the first | |
SCSI device present on a SCSI channel. | |
Lun - On input, a pointer to the LUN of a SCSI device | |
present on the SCSI channel. On output, a pointer | |
to the LUN of the next SCSI device present on | |
a SCSI channel. | |
Returns: | |
EFI_SUCCESS - The Target ID and Lun of the next SCSI device | |
on the SCSI channel was returned in Target and Lun. | |
EFI_NOT_FOUND - There are no more SCSI devices on this SCSI channel. | |
EFI_INVALID_PARAMETER - Target is not 0xFFFFFFFF,and Target and Lun were not | |
returned on a previous call to GetNextDevice(). | |
--*/ | |
{ | |
ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; | |
// | |
// Retrieve Device Private Data Structure. | |
// | |
AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (This); | |
// | |
// Check whether Target is valid. | |
// | |
if (Target == NULL || Lun == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((*Target != 0xFFFFFFFF) && | |
((*Target != AtapiScsiPrivate->LatestTargetId) || | |
(*Lun != AtapiScsiPrivate->LatestLun))) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (*Target == MAX_TARGET_ID) { | |
return EFI_NOT_FOUND; | |
} | |
if (*Target == 0xFFFFFFFF) { | |
*Target = 0; | |
} else { | |
*Target = AtapiScsiPrivate->LatestTargetId + 1; | |
} | |
*Lun = 0; | |
// | |
// Update the LatestTargetId. | |
// | |
AtapiScsiPrivate->LatestTargetId = *Target; | |
AtapiScsiPrivate->LatestLun = *Lun; | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
AtapiScsiPassThruBuildDevicePath ( | |
IN EFI_SCSI_PASS_THRU_PROTOCOL *This, | |
IN UINT32 Target, | |
IN UINT64 Lun, | |
IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath | |
) | |
/*++ | |
Routine Description: | |
Used to allocate and build a device path node for a SCSI device | |
on a SCSI channel. Would not build device path for a SCSI Host Controller. | |
Arguments: | |
This - Protocol instance pointer. | |
Target - The Target ID of the SCSI device for which | |
a device path node is to be allocated and built. | |
Lun - The LUN of the SCSI device for which a device | |
path node is to be allocated and built. | |
DevicePath - A pointer to a single device path node that | |
describes the SCSI device specified by | |
Target and Lun. This function is responsible | |
for allocating the buffer DevicePath with the boot | |
service AllocatePool(). It is the caller's | |
responsibility to free DevicePath when the caller | |
is finished with DevicePath. | |
Returns: | |
EFI_SUCCESS - The device path node that describes the SCSI device | |
specified by Target and Lun was allocated and | |
returned in DevicePath. | |
EFI_NOT_FOUND - The SCSI devices specified by Target and Lun does | |
not exist on the SCSI channel. | |
EFI_INVALID_PARAMETER - DevicePath is NULL. | |
EFI_OUT_OF_RESOURCES - There are not enough resources to allocate | |
DevicePath. | |
--*/ | |
{ | |
EFI_DEV_PATH *Node; | |
// | |
// Validate parameters passed in. | |
// | |
if (DevicePath == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// can not build device path for the SCSI Host Controller. | |
// | |
if ((Target > (MAX_TARGET_ID - 1)) || (Lun != 0)) { | |
return EFI_NOT_FOUND; | |
} | |
Node = AllocateZeroPool (sizeof (EFI_DEV_PATH)); | |
if (Node == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
Node->DevPath.Type = MESSAGING_DEVICE_PATH; | |
Node->DevPath.SubType = MSG_ATAPI_DP; | |
SetDevicePathNodeLength (&Node->DevPath, sizeof (ATAPI_DEVICE_PATH)); | |
Node->Atapi.PrimarySecondary = (UINT8) (Target / 2); | |
Node->Atapi.SlaveMaster = (UINT8) (Target % 2); | |
Node->Atapi.Lun = (UINT16) Lun; | |
*DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) Node; | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
AtapiScsiPassThruGetTargetLun ( | |
IN EFI_SCSI_PASS_THRU_PROTOCOL *This, | |
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, | |
OUT UINT32 *Target, | |
OUT UINT64 *Lun | |
) | |
/*++ | |
Routine Description: | |
Used to translate a device path node to a Target ID and LUN. | |
Arguments: | |
This - Protocol instance pointer. | |
DevicePath - A pointer to the device path node that | |
describes a SCSI device on the SCSI channel. | |
Target - A pointer to the Target ID of a SCSI device | |
on the SCSI channel. | |
Lun - A pointer to the LUN of a SCSI device on | |
the SCSI channel. | |
Returns: | |
EFI_SUCCESS - DevicePath was successfully translated to a | |
Target ID and LUN, and they were returned | |
in Target and Lun. | |
EFI_INVALID_PARAMETER - DevicePath/Target/Lun is NULL. | |
EFI_UNSUPPORTED - This driver does not support the device path | |
node type in DevicePath. | |
EFI_NOT_FOUND - A valid translation from DevicePath to a | |
Target ID and LUN does not exist. | |
--*/ | |
{ | |
EFI_DEV_PATH *Node; | |
// | |
// Validate parameters passed in. | |
// | |
if (DevicePath == NULL || Target == NULL || Lun == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Check whether the DevicePath belongs to SCSI_DEVICE_PATH | |
// | |
if ((DevicePath->Type != MESSAGING_DEVICE_PATH) || | |
(DevicePath->SubType != MSG_ATAPI_DP) || | |
(DevicePathNodeLength(DevicePath) != sizeof(ATAPI_DEVICE_PATH))) { | |
return EFI_UNSUPPORTED; | |
} | |
Node = (EFI_DEV_PATH *) DevicePath; | |
*Target = Node->Atapi.PrimarySecondary * 2 + Node->Atapi.SlaveMaster; | |
*Lun = Node->Atapi.Lun; | |
if (*Target > (MAX_TARGET_ID - 1) || *Lun != 0) { | |
return EFI_NOT_FOUND; | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
AtapiScsiPassThruResetChannel ( | |
IN EFI_SCSI_PASS_THRU_PROTOCOL *This | |
) | |
/*++ | |
Routine Description: | |
Resets a SCSI channel.This operation resets all the | |
SCSI devices connected to the SCSI channel. | |
Arguments: | |
This - Protocol instance pointer. | |
Returns: | |
EFI_SUCCESS - The SCSI channel was reset. | |
EFI_UNSUPPORTED - The SCSI channel does not support | |
a channel reset operation. | |
EFI_DEVICE_ERROR - A device error occurred while | |
attempting to reset the SCSI channel. | |
EFI_TIMEOUT - A timeout occurred while attempting | |
to reset the SCSI channel. | |
--*/ | |
{ | |
UINT8 DeviceControlValue; | |
ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; | |
UINT8 Index; | |
BOOLEAN ResetFlag; | |
AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (This); | |
ResetFlag = FALSE; | |
// | |
// Reset both Primary channel and Secondary channel. | |
// so, the IoPort pointer must point to the right I/O Register group | |
// | |
for (Index = 0; Index < 2; Index++) { | |
// | |
// Reset | |
// | |
AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[Index]; | |
DeviceControlValue = 0; | |
// | |
// set SRST bit to initiate soft reset | |
// | |
DeviceControlValue |= SRST; | |
// | |
// disable Interrupt | |
// | |
DeviceControlValue |= BIT1; | |
WritePortB ( | |
AtapiScsiPrivate->PciIo, | |
AtapiScsiPrivate->IoPort->Alt.DeviceControl, | |
DeviceControlValue | |
); | |
// | |
// Wait 10us | |
// | |
gBS->Stall (10); | |
// | |
// Clear SRST bit | |
// 0xfb:1111,1011 | |
// | |
DeviceControlValue &= 0xfb; | |
WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Alt.DeviceControl, DeviceControlValue); | |
// | |
// slave device needs at most 31s to clear BSY | |
// | |
if (StatusWaitForBSYClear (AtapiScsiPrivate, 31000000) != EFI_TIMEOUT) { | |
ResetFlag = TRUE; | |
} | |
} | |
if (ResetFlag) { | |
return EFI_SUCCESS; | |
} | |
return EFI_TIMEOUT; | |
} | |
EFI_STATUS | |
EFIAPI | |
AtapiScsiPassThruResetTarget ( | |
IN EFI_SCSI_PASS_THRU_PROTOCOL *This, | |
IN UINT32 Target, | |
IN UINT64 Lun | |
) | |
/*++ | |
Routine Description: | |
Resets a SCSI device that is connected to a SCSI channel. | |
Arguments: | |
This - Protocol instance pointer. | |
Target - The Target ID of the SCSI device to reset. | |
Lun - The LUN of the SCSI device to reset. | |
Returns: | |
EFI_SUCCESS - The SCSI device specified by Target and | |
Lun was reset. | |
EFI_UNSUPPORTED - The SCSI channel does not support a target | |
reset operation. | |
EFI_INVALID_PARAMETER - Target or Lun are invalid. | |
EFI_DEVICE_ERROR - A device error occurred while attempting | |
to reset the SCSI device specified by Target | |
and Lun. | |
EFI_TIMEOUT - A timeout occurred while attempting to reset | |
the SCSI device specified by Target and Lun. | |
--*/ | |
{ | |
ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; | |
UINT8 Command; | |
UINT8 DeviceSelect; | |
AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (This); | |
if ((Target > MAX_TARGET_ID) || (Lun != 0)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Directly return EFI_SUCCESS if want to reset the host controller | |
// | |
if (Target == This->Mode->AdapterId) { | |
return EFI_SUCCESS; | |
} | |
// | |
// According to Target ID, reset the Atapi I/O Register mapping | |
// (Target Id in [0,1] area, using AtapiIoPortRegisters[0], | |
// Target Id in [2,3] area, using AtapiIoPortRegisters[1] | |
// | |
if ((Target / 2) == 0) { | |
AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[0]; | |
} else { | |
AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[1]; | |
} | |
// | |
// for ATAPI device, no need to wait DRDY ready after device selecting. | |
// | |
// bit7 and bit5 are both set to 1 for backward compatibility | |
// | |
DeviceSelect = (UINT8) (((BIT7 | BIT5) | (Target << 4))); | |
WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Head, DeviceSelect); | |
Command = ATAPI_SOFT_RESET_CMD; | |
WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Reg.Command, Command); | |
// | |
// BSY clear is the only status return to the host by the device | |
// when reset is complete. | |
// slave device needs at most 31s to clear BSY | |
// | |
if (EFI_ERROR (StatusWaitForBSYClear (AtapiScsiPrivate, 31000000))) { | |
return EFI_TIMEOUT; | |
} | |
// | |
// stall 5 seconds to make the device status stable | |
// | |
gBS->Stall (5000000); | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
AtapiExtScsiPassThruFunction ( | |
IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, | |
IN UINT8 *Target, | |
IN UINT64 Lun, | |
IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, | |
IN EFI_EVENT Event OPTIONAL | |
) | |
/*++ | |
Routine Description: | |
Implements EFI_EXT_SCSI_PASS_THRU_PROTOCOL.PassThru() function. | |
Arguments: | |
This: The EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. | |
Target: The Target ID of the ATAPI device to send the SCSI | |
Request Packet. To ATAPI devices attached on an IDE | |
Channel, Target ID 0 indicates Master device;Target | |
ID 1 indicates Slave device. | |
Lun: The LUN of the ATAPI device to send the SCSI Request | |
Packet. To the ATAPI device, Lun is always 0. | |
Packet: The SCSI Request Packet to send to the ATAPI device | |
specified by Target and Lun. | |
Event: If non-blocking I/O is not supported 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. | |
Returns: | |
EFI_STATUS | |
--*/ | |
{ | |
EFI_STATUS Status; | |
ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; | |
UINT8 TargetId; | |
AtapiScsiPrivate = ATAPI_EXT_SCSI_PASS_THRU_DEV_FROM_THIS (This); | |
// | |
// For ATAPI device, UINT8 is enough to represent the SCSI ID on channel. | |
// | |
TargetId = Target[0]; | |
// | |
// Target is not allowed beyond MAX_TARGET_ID | |
// | |
if ((TargetId > MAX_TARGET_ID) || (Lun != 0)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// check the data fields in Packet parameter. | |
// | |
Status = CheckExtSCSIRequestPacket (Packet); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// If Request Packet targets at the IDE channel itself, | |
// do nothing. | |
// | |
if (TargetId == (UINT8)This->Mode->AdapterId) { | |
Packet->InTransferLength = Packet->OutTransferLength = 0; | |
return EFI_SUCCESS; | |
} | |
// | |
// According to Target ID, reset the Atapi I/O Register mapping | |
// (Target Id in [0,1] area, using AtapiIoPortRegisters[0], | |
// Target Id in [2,3] area, using AtapiIoPortRegisters[1] | |
// | |
if ((TargetId / 2) == 0) { | |
TargetId = (UINT8) (TargetId % 2); | |
AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[0]; | |
} else { | |
TargetId = (UINT8) (TargetId % 2); | |
AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[1]; | |
} | |
// | |
// the ATAPI SCSI interface does not support non-blocking I/O | |
// ignore the Event parameter | |
// | |
// Performs blocking I/O. | |
// | |
Status = SubmitExtBlockingIoCommand (AtapiScsiPrivate, TargetId, Packet); | |
return Status; | |
} | |
EFI_STATUS | |
EFIAPI | |
AtapiExtScsiPassThruGetNextTargetLun ( | |
IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, | |
IN OUT UINT8 **Target, | |
IN OUT UINT64 *Lun | |
) | |
/*++ | |
Routine Description: | |
Used to retrieve the list of legal Target IDs for SCSI devices | |
on a SCSI channel. | |
Arguments: | |
This - Protocol instance pointer. | |
Target - On input, a pointer to the Target ID of a SCSI | |
device present on the SCSI channel. On output, | |
a pointer to the Target ID of the next SCSI device | |
present on a SCSI channel. An input value of | |
0xFFFFFFFF retrieves the Target ID of the first | |
SCSI device present on a SCSI channel. | |
Lun - On input, a pointer to the LUN of a SCSI device | |
present on the SCSI channel. On output, a pointer | |
to the LUN of the next SCSI device present on | |
a SCSI channel. | |
Returns: | |
EFI_SUCCESS - The Target ID and Lun of the next SCSI device | |
on the SCSI channel was returned in Target and Lun. | |
EFI_NOT_FOUND - There are no more SCSI devices on this SCSI channel. | |
EFI_INVALID_PARAMETER - Target is not 0xFFFFFFFF,and Target and Lun were not | |
returned on a previous call to GetNextDevice(). | |
--*/ | |
{ | |
UINT8 ByteIndex; | |
UINT8 TargetId; | |
UINT8 ScsiId[TARGET_MAX_BYTES]; | |
ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; | |
// | |
// Retrieve Device Private Data Structure. | |
// | |
AtapiScsiPrivate = ATAPI_EXT_SCSI_PASS_THRU_DEV_FROM_THIS (This); | |
// | |
// Check whether Target is valid. | |
// | |
if (*Target == NULL || Lun == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
SetMem (ScsiId, TARGET_MAX_BYTES, 0xFF); | |
TargetId = (*Target)[0]; | |
// | |
// For ATAPI device, we use UINT8 to represent the SCSI ID on channel. | |
// | |
if (CompareMem(*Target, ScsiId, TARGET_MAX_BYTES) != 0) { | |
for (ByteIndex = 1; ByteIndex < TARGET_MAX_BYTES; ByteIndex++) { | |
if ((*Target)[ByteIndex] != 0) { | |
return EFI_INVALID_PARAMETER; | |
} | |
} | |
} | |
if ((CompareMem(*Target, ScsiId, TARGET_MAX_BYTES) != 0) && | |
((TargetId != AtapiScsiPrivate->LatestTargetId) || | |
(*Lun != AtapiScsiPrivate->LatestLun))) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (TargetId == MAX_TARGET_ID) { | |
return EFI_NOT_FOUND; | |
} | |
if (CompareMem(*Target, ScsiId, TARGET_MAX_BYTES) == 0) { | |
SetMem (*Target, TARGET_MAX_BYTES,0); | |
} else { | |
(*Target)[0] = (UINT8) (AtapiScsiPrivate->LatestTargetId + 1); | |
} | |
*Lun = 0; | |
// | |
// Update the LatestTargetId. | |
// | |
AtapiScsiPrivate->LatestTargetId = (*Target)[0]; | |
AtapiScsiPrivate->LatestLun = *Lun; | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
AtapiExtScsiPassThruBuildDevicePath ( | |
IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, | |
IN UINT8 *Target, | |
IN UINT64 Lun, | |
IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath | |
) | |
/*++ | |
Routine Description: | |
Used to allocate and build a device path node for a SCSI device | |
on a SCSI channel. Would not build device path for a SCSI Host Controller. | |
Arguments: | |
This - Protocol instance pointer. | |
Target - The Target ID of the SCSI device for which | |
a device path node is to be allocated and built. | |
Lun - The LUN of the SCSI device for which a device | |
path node is to be allocated and built. | |
DevicePath - A pointer to a single device path node that | |
describes the SCSI device specified by | |
Target and Lun. This function is responsible | |
for allocating the buffer DevicePath with the boot | |
service AllocatePool(). It is the caller's | |
responsibility to free DevicePath when the caller | |
is finished with DevicePath. | |
Returns: | |
EFI_SUCCESS - The device path node that describes the SCSI device | |
specified by Target and Lun was allocated and | |
returned in DevicePath. | |
EFI_NOT_FOUND - The SCSI devices specified by Target and Lun does | |
not exist on the SCSI channel. | |
EFI_INVALID_PARAMETER - DevicePath is NULL. | |
EFI_OUT_OF_RESOURCES - There are not enough resources to allocate | |
DevicePath. | |
--*/ | |
{ | |
EFI_DEV_PATH *Node; | |
UINT8 TargetId; | |
TargetId = Target[0]; | |
// | |
// Validate parameters passed in. | |
// | |
if (DevicePath == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// can not build device path for the SCSI Host Controller. | |
// | |
if ((TargetId > (MAX_TARGET_ID - 1)) || (Lun != 0)) { | |
return EFI_NOT_FOUND; | |
} | |
Node = AllocateZeroPool (sizeof (EFI_DEV_PATH)); | |
if (Node == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
Node->DevPath.Type = MESSAGING_DEVICE_PATH; | |
Node->DevPath.SubType = MSG_ATAPI_DP; | |
SetDevicePathNodeLength (&Node->DevPath, sizeof (ATAPI_DEVICE_PATH)); | |
Node->Atapi.PrimarySecondary = (UINT8) (TargetId / 2); | |
Node->Atapi.SlaveMaster = (UINT8) (TargetId % 2); | |
Node->Atapi.Lun = (UINT16) Lun; | |
*DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) Node; | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
AtapiExtScsiPassThruGetTargetLun ( | |
IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, | |
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, | |
OUT UINT8 **Target, | |
OUT UINT64 *Lun | |
) | |
/*++ | |
Routine Description: | |
Used to translate a device path node to a Target ID and LUN. | |
Arguments: | |
This - Protocol instance pointer. | |
DevicePath - A pointer to the device path node that | |
describes a SCSI device on the SCSI channel. | |
Target - A pointer to the Target ID of a SCSI device | |
on the SCSI channel. | |
Lun - A pointer to the LUN of a SCSI device on | |
the SCSI channel. | |
Returns: | |
EFI_SUCCESS - DevicePath was successfully translated to a | |
Target ID and LUN, and they were returned | |
in Target and Lun. | |
EFI_INVALID_PARAMETER - DevicePath/Target/Lun is NULL. | |
EFI_UNSUPPORTED - This driver does not support the device path | |
node type in DevicePath. | |
EFI_NOT_FOUND - A valid translation from DevicePath to a | |
Target ID and LUN does not exist. | |
--*/ | |
{ | |
EFI_DEV_PATH *Node; | |
// | |
// Validate parameters passed in. | |
// | |
if (DevicePath == NULL || Target == NULL || Lun == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Check whether the DevicePath belongs to SCSI_DEVICE_PATH | |
// | |
if ((DevicePath->Type != MESSAGING_DEVICE_PATH) || | |
(DevicePath->SubType != MSG_ATAPI_DP) || | |
(DevicePathNodeLength(DevicePath) != sizeof(ATAPI_DEVICE_PATH))) { | |
return EFI_UNSUPPORTED; | |
} | |
ZeroMem (*Target, TARGET_MAX_BYTES); | |
Node = (EFI_DEV_PATH *) DevicePath; | |
(*Target)[0] = (UINT8) (Node->Atapi.PrimarySecondary * 2 + Node->Atapi.SlaveMaster); | |
*Lun = Node->Atapi.Lun; | |
if ((*Target)[0] > (MAX_TARGET_ID - 1) || *Lun != 0) { | |
return EFI_NOT_FOUND; | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
AtapiExtScsiPassThruResetChannel ( | |
IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This | |
) | |
/*++ | |
Routine Description: | |
Resets a SCSI channel.This operation resets all the | |
SCSI devices connected to the SCSI channel. | |
Arguments: | |
This - Protocol instance pointer. | |
Returns: | |
EFI_SUCCESS - The SCSI channel was reset. | |
EFI_UNSUPPORTED - The SCSI channel does not support | |
a channel reset operation. | |
EFI_DEVICE_ERROR - A device error occurred while | |
attempting to reset the SCSI channel. | |
EFI_TIMEOUT - A timeout occurred while attempting | |
to reset the SCSI channel. | |
--*/ | |
{ | |
UINT8 DeviceControlValue; | |
UINT8 Index; | |
ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; | |
BOOLEAN ResetFlag; | |
AtapiScsiPrivate = ATAPI_EXT_SCSI_PASS_THRU_DEV_FROM_THIS (This); | |
ResetFlag = FALSE; | |
// | |
// Reset both Primary channel and Secondary channel. | |
// so, the IoPort pointer must point to the right I/O Register group | |
// And if there is a channel reset successfully, return EFI_SUCCESS. | |
// | |
for (Index = 0; Index < 2; Index++) { | |
// | |
// Reset | |
// | |
AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[Index]; | |
DeviceControlValue = 0; | |
// | |
// set SRST bit to initiate soft reset | |
// | |
DeviceControlValue |= SRST; | |
// | |
// disable Interrupt | |
// | |
DeviceControlValue |= BIT1; | |
WritePortB ( | |
AtapiScsiPrivate->PciIo, | |
AtapiScsiPrivate->IoPort->Alt.DeviceControl, | |
DeviceControlValue | |
); | |
// | |
// Wait 10us | |
// | |
gBS->Stall (10); | |
// | |
// Clear SRST bit | |
// 0xfb:1111,1011 | |
// | |
DeviceControlValue &= 0xfb; | |
WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Alt.DeviceControl, DeviceControlValue); | |
// | |
// slave device needs at most 31s to clear BSY | |
// | |
if (StatusWaitForBSYClear (AtapiScsiPrivate, 31000000) != EFI_TIMEOUT) { | |
ResetFlag = TRUE; | |
} | |
} | |
if (ResetFlag) { | |
return EFI_SUCCESS; | |
} | |
return EFI_TIMEOUT; | |
} | |
EFI_STATUS | |
EFIAPI | |
AtapiExtScsiPassThruResetTarget ( | |
IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, | |
IN UINT8 *Target, | |
IN UINT64 Lun | |
) | |
/*++ | |
Routine Description: | |
Resets a SCSI device that is connected to a SCSI channel. | |
Arguments: | |
This - Protocol instance pointer. | |
Target - The Target ID of the SCSI device to reset. | |
Lun - The LUN of the SCSI device to reset. | |
Returns: | |
EFI_SUCCESS - The SCSI device specified by Target and | |
Lun was reset. | |
EFI_UNSUPPORTED - The SCSI channel does not support a target | |
reset operation. | |
EFI_INVALID_PARAMETER - Target or Lun are invalid. | |
EFI_DEVICE_ERROR - A device error occurred while attempting | |
to reset the SCSI device specified by Target | |
and Lun. | |
EFI_TIMEOUT - A timeout occurred while attempting to reset | |
the SCSI device specified by Target and Lun. | |
--*/ | |
{ | |
UINT8 Command; | |
UINT8 DeviceSelect; | |
UINT8 TargetId; | |
ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; | |
AtapiScsiPrivate = ATAPI_EXT_SCSI_PASS_THRU_DEV_FROM_THIS (This); | |
TargetId = Target[0]; | |
if ((TargetId > MAX_TARGET_ID) || (Lun != 0)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Directly return EFI_SUCCESS if want to reset the host controller | |
// | |
if (TargetId == This->Mode->AdapterId) { | |
return EFI_SUCCESS; | |
} | |
// | |
// According to Target ID, reset the Atapi I/O Register mapping | |
// (Target Id in [0,1] area, using AtapiIoPortRegisters[0], | |
// Target Id in [2,3] area, using AtapiIoPortRegisters[1] | |
// | |
if ((TargetId / 2) == 0) { | |
AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[0]; | |
} else { | |
AtapiScsiPrivate->IoPort = &AtapiScsiPrivate->AtapiIoPortRegisters[1]; | |
} | |
// | |
// for ATAPI device, no need to wait DRDY ready after device selecting. | |
// | |
// bit7 and bit5 are both set to 1 for backward compatibility | |
// | |
DeviceSelect = (UINT8) ((BIT7 | BIT5) | (TargetId << 4)); | |
WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Head, DeviceSelect); | |
Command = ATAPI_SOFT_RESET_CMD; | |
WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Reg.Command, Command); | |
// | |
// BSY clear is the only status return to the host by the device | |
// when reset is complete. | |
// slave device needs at most 31s to clear BSY | |
// | |
if (EFI_ERROR (StatusWaitForBSYClear (AtapiScsiPrivate, 31000000))) { | |
return EFI_TIMEOUT; | |
} | |
// | |
// stall 5 seconds to make the device status stable | |
// | |
gBS->Stall (5000000); | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
AtapiExtScsiPassThruGetNextTarget ( | |
IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, | |
IN OUT UINT8 **Target | |
) | |
/*++ | |
Routine Description: | |
Used to retrieve the list of legal Target IDs for SCSI devices | |
on a SCSI channel. | |
Arguments: | |
This - Protocol instance pointer. | |
Target - On input, a pointer to the Target ID of a SCSI | |
device present on the SCSI channel. On output, | |
a pointer to the Target ID of the next SCSI device | |
present on a SCSI channel. An input value of | |
0xFFFFFFFF retrieves the Target ID of the first | |
SCSI device present on a SCSI channel. | |
Lun - On input, a pointer to the LUN of a SCSI device | |
present on the SCSI channel. On output, a pointer | |
to the LUN of the next SCSI device present on | |
a SCSI channel. | |
Returns: | |
EFI_SUCCESS - The Target ID and Lun of the next SCSI device | |
on the SCSI channel was returned in Target and Lun. | |
EFI_NOT_FOUND - There are no more SCSI devices on this SCSI channel. | |
EFI_INVALID_PARAMETER - Target is not 0xFFFFFFFF,and Target and Lun were not | |
returned on a previous call to GetNextDevice(). | |
--*/ | |
{ | |
UINT8 TargetId; | |
UINT8 ScsiId[TARGET_MAX_BYTES]; | |
ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; | |
UINT8 ByteIndex; | |
// | |
// Retrieve Device Private Data Structure. | |
// | |
AtapiScsiPrivate = ATAPI_EXT_SCSI_PASS_THRU_DEV_FROM_THIS (This); | |
// | |
// Check whether Target is valid. | |
// | |
if (*Target == NULL ) { | |
return EFI_INVALID_PARAMETER; | |
} | |
TargetId = (*Target)[0]; | |
SetMem (ScsiId, TARGET_MAX_BYTES, 0xFF); | |
// | |
// For ATAPI device, we use UINT8 to represent the SCSI ID on channel. | |
// | |
if (CompareMem(*Target, ScsiId, TARGET_MAX_BYTES) != 0) { | |
for (ByteIndex = 1; ByteIndex < TARGET_MAX_BYTES; ByteIndex++) { | |
if ((*Target)[ByteIndex] != 0) { | |
return EFI_INVALID_PARAMETER; | |
} | |
} | |
} | |
if ((CompareMem(*Target, ScsiId, TARGET_MAX_BYTES) != 0) &&(TargetId != AtapiScsiPrivate->LatestTargetId)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (TargetId == MAX_TARGET_ID) { | |
return EFI_NOT_FOUND; | |
} | |
if ((CompareMem(*Target, ScsiId, TARGET_MAX_BYTES) == 0)) { | |
SetMem (*Target, TARGET_MAX_BYTES, 0); | |
} else { | |
(*Target)[0] = (UINT8) (AtapiScsiPrivate->LatestTargetId + 1); | |
} | |
// | |
// Update the LatestTargetId. | |
// | |
AtapiScsiPrivate->LatestTargetId = (*Target)[0]; | |
AtapiScsiPrivate->LatestLun = 0; | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
GetIdeRegistersBaseAddr ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
OUT IDE_REGISTERS_BASE_ADDR *IdeRegsBaseAddr | |
) | |
/*++ | |
Routine Description: | |
Get IDE IO port registers' base addresses by mode. In 'Compatibility' mode, | |
use fixed addresses. In Native-PCI mode, get base addresses from BARs in | |
the PCI IDE controller's Configuration Space. | |
Arguments: | |
PciIo - Pointer to the EFI_PCI_IO_PROTOCOL instance | |
IdeRegsBaseAddr - Pointer to IDE_REGISTERS_BASE_ADDR to | |
receive IDE IO port registers' base addresses | |
Returns: | |
EFI_STATUS | |
--*/ | |
{ | |
EFI_STATUS Status; | |
PCI_TYPE00 PciData; | |
Status = PciIo->Pci.Read ( | |
PciIo, | |
EfiPciIoWidthUint8, | |
0, | |
sizeof (PciData), | |
&PciData | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if ((PciData.Hdr.ClassCode[0] & IDE_PRIMARY_OPERATING_MODE) == 0) { | |
IdeRegsBaseAddr[IdePrimary].CommandBlockBaseAddr = 0x1f0; | |
IdeRegsBaseAddr[IdePrimary].ControlBlockBaseAddr = 0x3f6; | |
} else { | |
// | |
// The BARs should be of IO type | |
// | |
if ((PciData.Device.Bar[0] & BIT0) == 0 || | |
(PciData.Device.Bar[1] & BIT0) == 0) { | |
return EFI_UNSUPPORTED; | |
} | |
IdeRegsBaseAddr[IdePrimary].CommandBlockBaseAddr = | |
(UINT16) (PciData.Device.Bar[0] & 0x0000fff8); | |
IdeRegsBaseAddr[IdePrimary].ControlBlockBaseAddr = | |
(UINT16) ((PciData.Device.Bar[1] & 0x0000fffc) + 2); | |
} | |
if ((PciData.Hdr.ClassCode[0] & IDE_SECONDARY_OPERATING_MODE) == 0) { | |
IdeRegsBaseAddr[IdeSecondary].CommandBlockBaseAddr = 0x170; | |
IdeRegsBaseAddr[IdeSecondary].ControlBlockBaseAddr = 0x376; | |
} else { | |
// | |
// The BARs should be of IO type | |
// | |
if ((PciData.Device.Bar[2] & BIT0) == 0 || | |
(PciData.Device.Bar[3] & BIT0) == 0) { | |
return EFI_UNSUPPORTED; | |
} | |
IdeRegsBaseAddr[IdeSecondary].CommandBlockBaseAddr = | |
(UINT16) (PciData.Device.Bar[2] & 0x0000fff8); | |
IdeRegsBaseAddr[IdeSecondary].ControlBlockBaseAddr = | |
(UINT16) ((PciData.Device.Bar[3] & 0x0000fffc) + 2); | |
} | |
return EFI_SUCCESS; | |
} | |
VOID | |
InitAtapiIoPortRegisters ( | |
IN ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, | |
IN IDE_REGISTERS_BASE_ADDR *IdeRegsBaseAddr | |
) | |
/*++ | |
Routine Description: | |
Initialize each Channel's Base Address of CommandBlock and ControlBlock. | |
Arguments: | |
AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV | |
IdeRegsBaseAddr - The pointer of IDE_REGISTERS_BASE_ADDR | |
Returns: | |
None | |
--*/ | |
{ | |
UINT8 IdeChannel; | |
UINT16 CommandBlockBaseAddr; | |
UINT16 ControlBlockBaseAddr; | |
IDE_BASE_REGISTERS *RegisterPointer; | |
for (IdeChannel = 0; IdeChannel < ATAPI_MAX_CHANNEL; IdeChannel++) { | |
RegisterPointer = &AtapiScsiPrivate->AtapiIoPortRegisters[IdeChannel]; | |
// | |
// Initialize IDE IO port addresses, including Command Block registers | |
// and Control Block registers | |
// | |
CommandBlockBaseAddr = IdeRegsBaseAddr[IdeChannel].CommandBlockBaseAddr; | |
ControlBlockBaseAddr = IdeRegsBaseAddr[IdeChannel].ControlBlockBaseAddr; | |
RegisterPointer->Data = CommandBlockBaseAddr; | |
(*(UINT16 *) &RegisterPointer->Reg1) = (UINT16) (CommandBlockBaseAddr + 0x01); | |
RegisterPointer->SectorCount = (UINT16) (CommandBlockBaseAddr + 0x02); | |
RegisterPointer->SectorNumber = (UINT16) (CommandBlockBaseAddr + 0x03); | |
RegisterPointer->CylinderLsb = (UINT16) (CommandBlockBaseAddr + 0x04); | |
RegisterPointer->CylinderMsb = (UINT16) (CommandBlockBaseAddr + 0x05); | |
RegisterPointer->Head = (UINT16) (CommandBlockBaseAddr + 0x06); | |
(*(UINT16 *) &RegisterPointer->Reg) = (UINT16) (CommandBlockBaseAddr + 0x07); | |
(*(UINT16 *) &RegisterPointer->Alt) = ControlBlockBaseAddr; | |
RegisterPointer->DriveAddress = (UINT16) (ControlBlockBaseAddr + 0x01); | |
} | |
} | |
EFI_STATUS | |
CheckSCSIRequestPacket ( | |
EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet | |
) | |
/*++ | |
Routine Description: | |
Checks the parameters in the SCSI Request Packet to make sure | |
they are valid for a SCSI Pass Thru request. | |
Arguments: | |
Packet - The pointer of EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET | |
Returns: | |
EFI_STATUS | |
--*/ | |
{ | |
if (Packet == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (!ValidCdbLength (Packet->CdbLength)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (Packet->Cdb == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Checks whether the request command is supported. | |
// | |
if (!IsCommandValid (Packet)) { | |
return EFI_UNSUPPORTED; | |
} | |
return EFI_SUCCESS; | |
} | |
BOOLEAN | |
IsCommandValid ( | |
EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet | |
) | |
/*++ | |
Routine Description: | |
Checks the requested SCSI command: | |
Is it supported by this driver? | |
Is the Data transfer direction reasonable? | |
Arguments: | |
Packet - The pointer of EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET | |
Returns: | |
EFI_STATUS | |
--*/ | |
{ | |
UINT8 Index; | |
UINT8 *OpCode; | |
UINT8 ArrayLen; | |
OpCode = (UINT8 *) (Packet->Cdb); | |
ArrayLen = (UINT8) (sizeof (gSupportedATAPICommands) / sizeof (gSupportedATAPICommands[0])); | |
for (Index = 0; (Index < ArrayLen) && (CompareMem (&gSupportedATAPICommands[Index], &gEndTable, sizeof (SCSI_COMMAND_SET)) != 0); Index++) { | |
if (*OpCode == gSupportedATAPICommands[Index].OpCode) { | |
// | |
// Check whether the requested Command is supported by this driver | |
// | |
if (Packet->DataDirection == DataIn) { | |
// | |
// Check whether the requested data direction conforms to | |
// what it should be. | |
// | |
if (gSupportedATAPICommands[Index].Direction == DataOut) { | |
return FALSE; | |
} | |
} | |
if (Packet->DataDirection == DataOut) { | |
// | |
// Check whether the requested data direction conforms to | |
// what it should be. | |
// | |
if (gSupportedATAPICommands[Index].Direction == DataIn) { | |
return FALSE; | |
} | |
} | |
return TRUE; | |
} | |
} | |
return FALSE; | |
} | |
EFI_STATUS | |
SubmitBlockingIoCommand ( | |
ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, | |
UINT32 Target, | |
EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet | |
) | |
/*++ | |
Routine Description: | |
Performs blocking I/O request. | |
Arguments: | |
AtapiScsiPrivate: Private data structure for the specified channel. | |
Target: The Target ID of the ATAPI device to send the SCSI | |
Request Packet. To ATAPI devices attached on an IDE | |
Channel, Target ID 0 indicates Master device;Target | |
ID 1 indicates Slave device. | |
Packet: The SCSI Request Packet to send to the ATAPI device | |
specified by Target. | |
Returns: EFI_STATUS | |
--*/ | |
{ | |
UINT8 PacketCommand[12]; | |
UINT64 TimeoutInMicroSeconds; | |
EFI_STATUS PacketCommandStatus; | |
// | |
// Fill ATAPI Command Packet according to CDB | |
// | |
ZeroMem (&PacketCommand, 12); | |
CopyMem (&PacketCommand, Packet->Cdb, Packet->CdbLength); | |
// | |
// Timeout is 100ns unit, convert it to 1000ns (1us) unit. | |
// | |
TimeoutInMicroSeconds = DivU64x32 (Packet->Timeout, (UINT32) 10); | |
// | |
// Submit ATAPI Command Packet | |
// | |
PacketCommandStatus = AtapiPacketCommand ( | |
AtapiScsiPrivate, | |
Target, | |
PacketCommand, | |
Packet->DataBuffer, | |
&(Packet->TransferLength), | |
(DATA_DIRECTION) Packet->DataDirection, | |
TimeoutInMicroSeconds | |
); | |
if (!EFI_ERROR (PacketCommandStatus) || (Packet->SenseData == NULL)) { | |
Packet->SenseDataLength = 0; | |
return PacketCommandStatus; | |
} | |
// | |
// Return SenseData if PacketCommandStatus matches | |
// the following return codes. | |
// | |
if ((PacketCommandStatus == EFI_BAD_BUFFER_SIZE) || | |
(PacketCommandStatus == EFI_DEVICE_ERROR) || | |
(PacketCommandStatus == EFI_TIMEOUT)) { | |
// | |
// avoid submit request sense command continuously. | |
// | |
if (PacketCommand[0] == OP_REQUEST_SENSE) { | |
Packet->SenseDataLength = 0; | |
return PacketCommandStatus; | |
} | |
RequestSenseCommand ( | |
AtapiScsiPrivate, | |
Target, | |
Packet->Timeout, | |
Packet->SenseData, | |
&Packet->SenseDataLength | |
); | |
} | |
return PacketCommandStatus; | |
} | |
EFI_STATUS | |
RequestSenseCommand ( | |
ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, | |
UINT32 Target, | |
UINT64 Timeout, | |
VOID *SenseData, | |
UINT8 *SenseDataLength | |
) | |
/*++ | |
Routine Description: | |
Sumbit request sense command | |
Arguments: | |
AtapiScsiPrivate - The pionter of ATAPI_SCSI_PASS_THRU_DEV | |
Target - The target ID | |
Timeout - The time to complete the command | |
SenseData - The buffer to fill in sense data | |
SenseDataLength - The length of buffer | |
Returns: | |
EFI_STATUS | |
--*/ | |
{ | |
EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET Packet; | |
UINT8 Cdb[12]; | |
EFI_STATUS Status; | |
ZeroMem (&Packet, sizeof (EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET)); | |
ZeroMem (Cdb, 12); | |
Cdb[0] = OP_REQUEST_SENSE; | |
Cdb[4] = (UINT8) (*SenseDataLength); | |
Packet.Timeout = Timeout; | |
Packet.DataBuffer = SenseData; | |
Packet.SenseData = NULL; | |
Packet.Cdb = Cdb; | |
Packet.TransferLength = *SenseDataLength; | |
Packet.CdbLength = 12; | |
Packet.DataDirection = DataIn; | |
Status = SubmitBlockingIoCommand (AtapiScsiPrivate, Target, &Packet); | |
*SenseDataLength = (UINT8) (Packet.TransferLength); | |
return Status; | |
} | |
EFI_STATUS | |
CheckExtSCSIRequestPacket ( | |
EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet | |
) | |
/*++ | |
Routine Description: | |
Checks the parameters in the SCSI Request Packet to make sure | |
they are valid for a SCSI Pass Thru request. | |
Arguments: | |
Packet - The pointer of EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET | |
Returns: | |
EFI_STATUS | |
--*/ | |
{ | |
if (Packet == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (!ValidCdbLength (Packet->CdbLength)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (Packet->Cdb == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Checks whether the request command is supported. | |
// | |
if (!IsExtCommandValid (Packet)) { | |
return EFI_UNSUPPORTED; | |
} | |
return EFI_SUCCESS; | |
} | |
BOOLEAN | |
IsExtCommandValid ( | |
EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet | |
) | |
/*++ | |
Routine Description: | |
Checks the requested SCSI command: | |
Is it supported by this driver? | |
Is the Data transfer direction reasonable? | |
Arguments: | |
Packet - The pointer of EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET | |
Returns: | |
EFI_STATUS | |
--*/ | |
{ | |
UINT8 Index; | |
UINT8 *OpCode; | |
UINT8 ArrayLen; | |
OpCode = (UINT8 *) (Packet->Cdb); | |
ArrayLen = (UINT8) (sizeof (gSupportedATAPICommands) / sizeof (gSupportedATAPICommands[0])); | |
for (Index = 0; (Index < ArrayLen) && (CompareMem (&gSupportedATAPICommands[Index], &gEndTable, sizeof (SCSI_COMMAND_SET)) != 0); Index++) { | |
if (*OpCode == gSupportedATAPICommands[Index].OpCode) { | |
// | |
// Check whether the requested Command is supported by this driver | |
// | |
if (Packet->DataDirection == DataIn) { | |
// | |
// Check whether the requested data direction conforms to | |
// what it should be. | |
// | |
if (gSupportedATAPICommands[Index].Direction == DataOut) { | |
return FALSE; | |
} | |
} | |
if (Packet->DataDirection == DataOut) { | |
// | |
// Check whether the requested data direction conforms to | |
// what it should be. | |
// | |
if (gSupportedATAPICommands[Index].Direction == DataIn) { | |
return FALSE; | |
} | |
} | |
return TRUE; | |
} | |
} | |
return FALSE; | |
} | |
EFI_STATUS | |
SubmitExtBlockingIoCommand ( | |
ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, | |
UINT8 Target, | |
EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet | |
) | |
/*++ | |
Routine Description: | |
Performs blocking I/O request. | |
Arguments: | |
AtapiScsiPrivate: Private data structure for the specified channel. | |
Target: The Target ID of the ATAPI device to send the SCSI | |
Request Packet. To ATAPI devices attached on an IDE | |
Channel, Target ID 0 indicates Master device;Target | |
ID 1 indicates Slave device. | |
Packet: The SCSI Request Packet to send to the ATAPI device | |
specified by Target. | |
Returns: EFI_STATUS | |
--*/ | |
{ | |
UINT8 PacketCommand[12]; | |
UINT64 TimeoutInMicroSeconds; | |
EFI_STATUS PacketCommandStatus; | |
// | |
// Fill ATAPI Command Packet according to CDB | |
// | |
ZeroMem (&PacketCommand, 12); | |
CopyMem (&PacketCommand, Packet->Cdb, Packet->CdbLength); | |
// | |
// Timeout is 100ns unit, convert it to 1000ns (1us) unit. | |
// | |
TimeoutInMicroSeconds = DivU64x32 (Packet->Timeout, (UINT32) 10); | |
// | |
// Submit ATAPI Command Packet | |
// | |
if (Packet->DataDirection == DataIn) { | |
PacketCommandStatus = AtapiPacketCommand ( | |
AtapiScsiPrivate, | |
Target, | |
PacketCommand, | |
Packet->InDataBuffer, | |
&(Packet->InTransferLength), | |
DataIn, | |
TimeoutInMicroSeconds | |
); | |
} else { | |
PacketCommandStatus = AtapiPacketCommand ( | |
AtapiScsiPrivate, | |
Target, | |
PacketCommand, | |
Packet->OutDataBuffer, | |
&(Packet->OutTransferLength), | |
DataOut, | |
TimeoutInMicroSeconds | |
); | |
} | |
if (!EFI_ERROR (PacketCommandStatus) || (Packet->SenseData == NULL)) { | |
Packet->SenseDataLength = 0; | |
return PacketCommandStatus; | |
} | |
// | |
// Return SenseData if PacketCommandStatus matches | |
// the following return codes. | |
// | |
if ((PacketCommandStatus == EFI_BAD_BUFFER_SIZE) || | |
(PacketCommandStatus == EFI_DEVICE_ERROR) || | |
(PacketCommandStatus == EFI_TIMEOUT)) { | |
// | |
// avoid submit request sense command continuously. | |
// | |
if (PacketCommand[0] == OP_REQUEST_SENSE) { | |
Packet->SenseDataLength = 0; | |
return PacketCommandStatus; | |
} | |
RequestSenseCommand ( | |
AtapiScsiPrivate, | |
Target, | |
Packet->Timeout, | |
Packet->SenseData, | |
&Packet->SenseDataLength | |
); | |
} | |
return PacketCommandStatus; | |
} | |
EFI_STATUS | |
AtapiPacketCommand ( | |
ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, | |
UINT32 Target, | |
UINT8 *PacketCommand, | |
VOID *Buffer, | |
UINT32 *ByteCount, | |
DATA_DIRECTION Direction, | |
UINT64 TimeoutInMicroSeconds | |
) | |
/*++ | |
Routine Description: | |
Submits ATAPI command packet to the specified ATAPI device. | |
Arguments: | |
AtapiScsiPrivate: Private data structure for the specified channel. | |
Target: The Target ID of the ATAPI device to send the SCSI | |
Request Packet. To ATAPI devices attached on an IDE | |
Channel, Target ID 0 indicates Master device;Target | |
ID 1 indicates Slave device. | |
PacketCommand: Points to the ATAPI command packet. | |
Buffer: Points to the transferred data. | |
ByteCount: When input,indicates the buffer size; when output, | |
indicates the actually transferred data size. | |
Direction: Indicates the data transfer direction. | |
TimeoutInMicroSeconds: | |
The timeout, in micro second units, to use for the | |
execution of this ATAPI command. | |
A TimeoutInMicroSeconds value of 0 means that | |
this function will wait indefinitely for the ATAPI | |
command to execute. | |
If TimeoutInMicroSeconds is greater than zero, then | |
this function will return EFI_TIMEOUT if the time | |
required to execute the ATAPI command is greater | |
than TimeoutInMicroSeconds. | |
Returns: | |
EFI_STATUS | |
--*/ | |
{ | |
UINT16 *CommandIndex; | |
UINT8 Count; | |
EFI_STATUS Status; | |
// | |
// Set all the command parameters by fill related registers. | |
// Before write to all the following registers, BSY must be 0. | |
// | |
Status = StatusWaitForBSYClear (AtapiScsiPrivate, TimeoutInMicroSeconds); | |
if (EFI_ERROR (Status)) { | |
return EFI_DEVICE_ERROR; | |
} | |
// | |
// Select device via Device/Head Register. | |
// "Target = 0" indicates device 0; "Target = 1" indicates device 1 | |
// | |
WritePortB ( | |
AtapiScsiPrivate->PciIo, | |
AtapiScsiPrivate->IoPort->Head, | |
(UINT8) ((Target << 4) | DEFAULT_CMD) // DEFAULT_CMD: 0xa0 (1010,0000) | |
); | |
// | |
// Set all the command parameters by fill related registers. | |
// Before write to all the following registers, BSY DRQ must be 0. | |
// | |
Status = StatusDRQClear(AtapiScsiPrivate, TimeoutInMicroSeconds); | |
if (EFI_ERROR (Status)) { | |
if (Status == EFI_ABORTED) { | |
Status = EFI_DEVICE_ERROR; | |
} | |
*ByteCount = 0; | |
return Status; | |
} | |
// | |
// No OVL; No DMA (by setting feature register) | |
// | |
WritePortB ( | |
AtapiScsiPrivate->PciIo, | |
AtapiScsiPrivate->IoPort->Reg1.Feature, | |
0x00 | |
); | |
// | |
// set the transfersize to MAX_ATAPI_BYTE_COUNT to let the device | |
// determine how much data should be transfered. | |
// | |
WritePortB ( | |
AtapiScsiPrivate->PciIo, | |
AtapiScsiPrivate->IoPort->CylinderLsb, | |
(UINT8) (MAX_ATAPI_BYTE_COUNT & 0x00ff) | |
); | |
WritePortB ( | |
AtapiScsiPrivate->PciIo, | |
AtapiScsiPrivate->IoPort->CylinderMsb, | |
(UINT8) (MAX_ATAPI_BYTE_COUNT >> 8) | |
); | |
// | |
// DEFAULT_CTL:0x0a (0000,1010) | |
// Disable interrupt | |
// | |
WritePortB ( | |
AtapiScsiPrivate->PciIo, | |
AtapiScsiPrivate->IoPort->Alt.DeviceControl, | |
DEFAULT_CTL | |
); | |
// | |
// Send Packet command to inform device | |
// that the following data bytes are command packet. | |
// | |
WritePortB ( | |
AtapiScsiPrivate->PciIo, | |
AtapiScsiPrivate->IoPort->Reg.Command, | |
PACKET_CMD | |
); | |
// | |
// Before data transfer, BSY should be 0 and DRQ should be 1. | |
// if they are not in specified time frame, | |
// retrieve Sense Key from Error Register before return. | |
// | |
Status = StatusDRQReady (AtapiScsiPrivate, TimeoutInMicroSeconds); | |
if (EFI_ERROR (Status)) { | |
if (Status == EFI_ABORTED) { | |
Status = EFI_DEVICE_ERROR; | |
} | |
*ByteCount = 0; | |
return Status; | |
} | |
// | |
// Send out command packet | |
// | |
CommandIndex = (UINT16 *) PacketCommand; | |
for (Count = 0; Count < 6; Count++, CommandIndex++) { | |
WritePortW (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Data, *CommandIndex); | |
} | |
// | |
// call AtapiPassThruPioReadWriteData() function to get | |
// requested transfer data form device. | |
// | |
return AtapiPassThruPioReadWriteData ( | |
AtapiScsiPrivate, | |
Buffer, | |
ByteCount, | |
Direction, | |
TimeoutInMicroSeconds | |
); | |
} | |
EFI_STATUS | |
AtapiPassThruPioReadWriteData ( | |
ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, | |
UINT16 *Buffer, | |
UINT32 *ByteCount, | |
DATA_DIRECTION Direction, | |
UINT64 TimeoutInMicroSeconds | |
) | |
/*++ | |
Routine Description: | |
Performs data transfer between ATAPI device and host after the | |
ATAPI command packet is sent. | |
Arguments: | |
AtapiScsiPrivate: Private data structure for the specified channel. | |
Buffer: Points to the transferred data. | |
ByteCount: When input,indicates the buffer size; when output, | |
indicates the actually transferred data size. | |
Direction: Indicates the data transfer direction. | |
TimeoutInMicroSeconds: | |
The timeout, in micro second units, to use for the | |
execution of this ATAPI command. | |
A TimeoutInMicroSeconds value of 0 means that | |
this function will wait indefinitely for the ATAPI | |
command to execute. | |
If TimeoutInMicroSeconds is greater than zero, then | |
this function will return EFI_TIMEOUT if the time | |
required to execute the ATAPI command is greater | |
than TimeoutInMicroSeconds. | |
Returns: | |
EFI_STATUS | |
--*/ | |
{ | |
UINT32 Index; | |
UINT32 RequiredWordCount; | |
UINT32 ActualWordCount; | |
UINT32 WordCount; | |
EFI_STATUS Status; | |
UINT16 *ptrBuffer; | |
Status = EFI_SUCCESS; | |
// | |
// Non Data transfer request is also supported. | |
// | |
if (*ByteCount == 0 || Buffer == NULL) { | |
*ByteCount = 0; | |
if (EFI_ERROR (StatusWaitForBSYClear (AtapiScsiPrivate, TimeoutInMicroSeconds))) { | |
return EFI_DEVICE_ERROR; | |
} | |
} | |
ptrBuffer = Buffer; | |
RequiredWordCount = *ByteCount / 2; | |
// | |
// ActuralWordCount means the word count of data really transfered. | |
// | |
ActualWordCount = 0; | |
while (ActualWordCount < RequiredWordCount) { | |
// | |
// before each data transfer stream, the host should poll DRQ bit ready, | |
// which indicates device's ready for data transfer . | |
// | |
Status = StatusDRQReady (AtapiScsiPrivate, TimeoutInMicroSeconds); | |
if (EFI_ERROR (Status)) { | |
*ByteCount = ActualWordCount * 2; | |
AtapiPassThruCheckErrorStatus (AtapiScsiPrivate); | |
if (ActualWordCount == 0) { | |
return EFI_DEVICE_ERROR; | |
} | |
// | |
// ActualWordCount > 0 | |
// | |
if (ActualWordCount < RequiredWordCount) { | |
return EFI_BAD_BUFFER_SIZE; | |
} | |
} | |
// | |
// get current data transfer size from Cylinder Registers. | |
// | |
WordCount = ReadPortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->CylinderMsb) << 8; | |
WordCount = WordCount | ReadPortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->CylinderLsb); | |
WordCount = WordCount & 0xffff; | |
WordCount /= 2; | |
// | |
// perform a series data In/Out. | |
// | |
for (Index = 0; (Index < WordCount) && (ActualWordCount < RequiredWordCount); Index++, ActualWordCount++) { | |
if (Direction == DataIn) { | |
*ptrBuffer = ReadPortW (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Data); | |
} else { | |
WritePortW (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Data, *ptrBuffer); | |
} | |
ptrBuffer++; | |
} | |
} | |
// | |
// After data transfer is completed, normally, DRQ bit should clear. | |
// | |
StatusDRQClear (AtapiScsiPrivate, TimeoutInMicroSeconds); | |
// | |
// read status register to check whether error happens. | |
// | |
Status = AtapiPassThruCheckErrorStatus (AtapiScsiPrivate); | |
*ByteCount = ActualWordCount * 2; | |
return Status; | |
} | |
UINT8 | |
ReadPortB ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT16 Port | |
) | |
/*++ | |
Routine Description: | |
Read one byte from a specified I/O port. | |
Arguments: | |
PciIo - The pointer of EFI_PCI_IO_PROTOCOL | |
Port - IO port | |
Returns: | |
A byte read out | |
--*/ | |
{ | |
UINT8 Data; | |
Data = 0; | |
PciIo->Io.Read ( | |
PciIo, | |
EfiPciIoWidthUint8, | |
EFI_PCI_IO_PASS_THROUGH_BAR, | |
(UINT64) Port, | |
1, | |
&Data | |
); | |
return Data; | |
} | |
UINT16 | |
ReadPortW ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT16 Port | |
) | |
/*++ | |
Routine Description: | |
Read one word from a specified I/O port. | |
Arguments: | |
PciIo - The pointer of EFI_PCI_IO_PROTOCOL | |
Port - IO port | |
Returns: | |
A word read out | |
--*/ | |
{ | |
UINT16 Data; | |
Data = 0; | |
PciIo->Io.Read ( | |
PciIo, | |
EfiPciIoWidthUint16, | |
EFI_PCI_IO_PASS_THROUGH_BAR, | |
(UINT64) Port, | |
1, | |
&Data | |
); | |
return Data; | |
} | |
VOID | |
WritePortB ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT16 Port, | |
IN UINT8 Data | |
) | |
/*++ | |
Routine Description: | |
Write one byte to a specified I/O port. | |
Arguments: | |
PciIo - The pointer of EFI_PCI_IO_PROTOCOL | |
Port - IO port | |
Data - The data to write | |
Returns: | |
NONE | |
--*/ | |
{ | |
PciIo->Io.Write ( | |
PciIo, | |
EfiPciIoWidthUint8, | |
EFI_PCI_IO_PASS_THROUGH_BAR, | |
(UINT64) Port, | |
1, | |
&Data | |
); | |
} | |
VOID | |
WritePortW ( | |
IN EFI_PCI_IO_PROTOCOL *PciIo, | |
IN UINT16 Port, | |
IN UINT16 Data | |
) | |
/*++ | |
Routine Description: | |
Write one word to a specified I/O port. | |
Arguments: | |
PciIo - The pointer of EFI_PCI_IO_PROTOCOL | |
Port - IO port | |
Data - The data to write | |
Returns: | |
NONE | |
--*/ | |
{ | |
PciIo->Io.Write ( | |
PciIo, | |
EfiPciIoWidthUint16, | |
EFI_PCI_IO_PASS_THROUGH_BAR, | |
(UINT64) Port, | |
1, | |
&Data | |
); | |
} | |
EFI_STATUS | |
StatusDRQClear ( | |
ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, | |
UINT64 TimeoutInMicroSeconds | |
) | |
/*++ | |
Routine Description: | |
Check whether DRQ is clear in the Status Register. (BSY must also be cleared) | |
If TimeoutInMicroSeconds is zero, this routine should wait infinitely for | |
DRQ clear. Otherwise, it will return EFI_TIMEOUT when specified time is | |
elapsed. | |
Arguments: | |
AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV | |
TimeoutInMicroSeconds - The time to wait for | |
Returns: | |
EFI_STATUS | |
--*/ | |
{ | |
UINT64 Delay; | |
UINT8 StatusRegister; | |
UINT8 ErrRegister; | |
if (TimeoutInMicroSeconds == 0) { | |
Delay = 2; | |
} else { | |
Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1; | |
} | |
do { | |
StatusRegister = ReadPortB ( | |
AtapiScsiPrivate->PciIo, | |
AtapiScsiPrivate->IoPort->Reg.Status | |
); | |
// | |
// wait for BSY == 0 and DRQ == 0 | |
// | |
if ((StatusRegister & (DRQ | BSY)) == 0) { | |
break; | |
} | |
// | |
// check whether the command is aborted by the device | |
// | |
if ((StatusRegister & (BSY | ERR)) == ERR) { | |
ErrRegister = ReadPortB ( | |
AtapiScsiPrivate->PciIo, | |
AtapiScsiPrivate->IoPort->Reg1.Error | |
); | |
if ((ErrRegister & ABRT_ERR) == ABRT_ERR) { | |
return EFI_ABORTED; | |
} | |
} | |
// | |
// Stall for 30 us | |
// | |
gBS->Stall (30); | |
// | |
// Loop infinitely if not meeting expected condition | |
// | |
if (TimeoutInMicroSeconds == 0) { | |
Delay = 2; | |
} | |
Delay--; | |
} while (Delay); | |
if (Delay == 0) { | |
return EFI_TIMEOUT; | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
AltStatusDRQClear ( | |
ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, | |
UINT64 TimeoutInMicroSeconds | |
) | |
/*++ | |
Routine Description: | |
Check whether DRQ is clear in the Alternate Status Register. | |
(BSY must also be cleared).If TimeoutInMicroSeconds is zero, this routine should | |
wait infinitely for DRQ clear. Otherwise, it will return EFI_TIMEOUT when specified time is | |
elapsed. | |
Arguments: | |
AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV | |
TimeoutInMicroSeconds - The time to wait for | |
Returns: | |
EFI_STATUS | |
--*/ | |
{ | |
UINT64 Delay; | |
UINT8 AltStatusRegister; | |
UINT8 ErrRegister; | |
if (TimeoutInMicroSeconds == 0) { | |
Delay = 2; | |
} else { | |
Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1; | |
} | |
do { | |
AltStatusRegister = ReadPortB ( | |
AtapiScsiPrivate->PciIo, | |
AtapiScsiPrivate->IoPort->Alt.AltStatus | |
); | |
// | |
// wait for BSY == 0 and DRQ == 0 | |
// | |
if ((AltStatusRegister & (DRQ | BSY)) == 0) { | |
break; | |
} | |
if ((AltStatusRegister & (BSY | ERR)) == ERR) { | |
ErrRegister = ReadPortB ( | |
AtapiScsiPrivate->PciIo, | |
AtapiScsiPrivate->IoPort->Reg1.Error | |
); | |
if ((ErrRegister & ABRT_ERR) == ABRT_ERR) { | |
return EFI_ABORTED; | |
} | |
} | |
// | |
// Stall for 30 us | |
// | |
gBS->Stall (30); | |
// | |
// Loop infinitely if not meeting expected condition | |
// | |
if (TimeoutInMicroSeconds == 0) { | |
Delay = 2; | |
} | |
Delay--; | |
} while (Delay); | |
if (Delay == 0) { | |
return EFI_TIMEOUT; | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
StatusDRQReady ( | |
ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, | |
UINT64 TimeoutInMicroSeconds | |
) | |
/*++ | |
Routine Description: | |
Check whether DRQ is ready in the Status Register. (BSY must also be cleared) | |
If TimeoutInMicroSeconds is zero, this routine should wait infinitely for | |
DRQ ready. Otherwise, it will return EFI_TIMEOUT when specified time is | |
elapsed. | |
Arguments: | |
AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV | |
TimeoutInMicroSeconds - The time to wait for | |
Returns: | |
EFI_STATUS | |
--*/ | |
{ | |
UINT64 Delay; | |
UINT8 StatusRegister; | |
UINT8 ErrRegister; | |
if (TimeoutInMicroSeconds == 0) { | |
Delay = 2; | |
} else { | |
Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1; | |
} | |
do { | |
// | |
// read Status Register will clear interrupt | |
// | |
StatusRegister = ReadPortB ( | |
AtapiScsiPrivate->PciIo, | |
AtapiScsiPrivate->IoPort->Reg.Status | |
); | |
// | |
// BSY==0,DRQ==1 | |
// | |
if ((StatusRegister & (BSY | DRQ)) == DRQ) { | |
break; | |
} | |
if ((StatusRegister & (BSY | ERR)) == ERR) { | |
ErrRegister = ReadPortB ( | |
AtapiScsiPrivate->PciIo, | |
AtapiScsiPrivate->IoPort->Reg1.Error | |
); | |
if ((ErrRegister & ABRT_ERR) == ABRT_ERR) { | |
return EFI_ABORTED; | |
} | |
} | |
// | |
// Stall for 30 us | |
// | |
gBS->Stall (30); | |
// | |
// Loop infinitely if not meeting expected condition | |
// | |
if (TimeoutInMicroSeconds == 0) { | |
Delay = 2; | |
} | |
Delay--; | |
} while (Delay); | |
if (Delay == 0) { | |
return EFI_TIMEOUT; | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
AltStatusDRQReady ( | |
ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, | |
UINT64 TimeoutInMicroSeconds | |
) | |
/*++ | |
Routine Description: | |
Check whether DRQ is ready in the Alternate Status Register. | |
(BSY must also be cleared) | |
If TimeoutInMicroSeconds is zero, this routine should wait infinitely for | |
DRQ ready. Otherwise, it will return EFI_TIMEOUT when specified time is | |
elapsed. | |
Arguments: | |
AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV | |
TimeoutInMicroSeconds - The time to wait for | |
Returns: | |
EFI_STATUS | |
--*/ | |
{ | |
UINT64 Delay; | |
UINT8 AltStatusRegister; | |
UINT8 ErrRegister; | |
if (TimeoutInMicroSeconds == 0) { | |
Delay = 2; | |
} else { | |
Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1; | |
} | |
do { | |
// | |
// read Status Register will clear interrupt | |
// | |
AltStatusRegister = ReadPortB ( | |
AtapiScsiPrivate->PciIo, | |
AtapiScsiPrivate->IoPort->Alt.AltStatus | |
); | |
// | |
// BSY==0,DRQ==1 | |
// | |
if ((AltStatusRegister & (BSY | DRQ)) == DRQ) { | |
break; | |
} | |
if ((AltStatusRegister & (BSY | ERR)) == ERR) { | |
ErrRegister = ReadPortB ( | |
AtapiScsiPrivate->PciIo, | |
AtapiScsiPrivate->IoPort->Reg1.Error | |
); | |
if ((ErrRegister & ABRT_ERR) == ABRT_ERR) { | |
return EFI_ABORTED; | |
} | |
} | |
// | |
// Stall for 30 us | |
// | |
gBS->Stall (30); | |
// | |
// Loop infinitely if not meeting expected condition | |
// | |
if (TimeoutInMicroSeconds == 0) { | |
Delay = 2; | |
} | |
Delay--; | |
} while (Delay); | |
if (Delay == 0) { | |
return EFI_TIMEOUT; | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
StatusWaitForBSYClear ( | |
ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, | |
UINT64 TimeoutInMicroSeconds | |
) | |
/*++ | |
Routine Description: | |
Check whether BSY is clear in the Status Register. | |
If TimeoutInMicroSeconds is zero, this routine should wait infinitely for | |
BSY clear. Otherwise, it will return EFI_TIMEOUT when specified time is | |
elapsed. | |
Arguments: | |
AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV | |
TimeoutInMicroSeconds - The time to wait for | |
Returns: | |
EFI_STATUS | |
--*/ | |
{ | |
UINT64 Delay; | |
UINT8 StatusRegister; | |
if (TimeoutInMicroSeconds == 0) { | |
Delay = 2; | |
} else { | |
Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1; | |
} | |
do { | |
StatusRegister = ReadPortB ( | |
AtapiScsiPrivate->PciIo, | |
AtapiScsiPrivate->IoPort->Reg.Status | |
); | |
if ((StatusRegister & BSY) == 0x00) { | |
break; | |
} | |
// | |
// Stall for 30 us | |
// | |
gBS->Stall (30); | |
// | |
// Loop infinitely if not meeting expected condition | |
// | |
if (TimeoutInMicroSeconds == 0) { | |
Delay = 2; | |
} | |
Delay--; | |
} while (Delay); | |
if (Delay == 0) { | |
return EFI_TIMEOUT; | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
AltStatusWaitForBSYClear ( | |
ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, | |
UINT64 TimeoutInMicroSeconds | |
) | |
/*++ | |
Routine Description: | |
Check whether BSY is clear in the Alternate Status Register. | |
If TimeoutInMicroSeconds is zero, this routine should wait infinitely for | |
BSY clear. Otherwise, it will return EFI_TIMEOUT when specified time is | |
elapsed. | |
Arguments: | |
AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV | |
TimeoutInMicroSeconds - The time to wait for | |
Returns: | |
EFI_STATUS | |
--*/ | |
{ | |
UINT64 Delay; | |
UINT8 AltStatusRegister; | |
if (TimeoutInMicroSeconds == 0) { | |
Delay = 2; | |
} else { | |
Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1; | |
} | |
do { | |
AltStatusRegister = ReadPortB ( | |
AtapiScsiPrivate->PciIo, | |
AtapiScsiPrivate->IoPort->Alt.AltStatus | |
); | |
if ((AltStatusRegister & BSY) == 0x00) { | |
break; | |
} | |
// | |
// Stall for 30 us | |
// | |
gBS->Stall (30); | |
// | |
// Loop infinitely if not meeting expected condition | |
// | |
if (TimeoutInMicroSeconds == 0) { | |
Delay = 2; | |
} | |
Delay--; | |
} while (Delay); | |
if (Delay == 0) { | |
return EFI_TIMEOUT; | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
StatusDRDYReady ( | |
ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, | |
UINT64 TimeoutInMicroSeconds | |
) | |
/*++ | |
Routine Description: | |
Check whether DRDY is ready in the Status Register. | |
(BSY must also be cleared) | |
If TimeoutInMicroSeconds is zero, this routine should wait infinitely for | |
DRDY ready. Otherwise, it will return EFI_TIMEOUT when specified time is | |
elapsed. | |
Arguments: | |
AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV | |
TimeoutInMicroSeconds - The time to wait for | |
Returns: | |
EFI_STATUS | |
--*/ | |
{ | |
UINT64 Delay; | |
UINT8 StatusRegister; | |
UINT8 ErrRegister; | |
if (TimeoutInMicroSeconds == 0) { | |
Delay = 2; | |
} else { | |
Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1; | |
} | |
do { | |
StatusRegister = ReadPortB ( | |
AtapiScsiPrivate->PciIo, | |
AtapiScsiPrivate->IoPort->Reg.Status | |
); | |
// | |
// BSY == 0 , DRDY == 1 | |
// | |
if ((StatusRegister & (DRDY | BSY)) == DRDY) { | |
break; | |
} | |
if ((StatusRegister & (BSY | ERR)) == ERR) { | |
ErrRegister = ReadPortB ( | |
AtapiScsiPrivate->PciIo, | |
AtapiScsiPrivate->IoPort->Reg1.Error | |
); | |
if ((ErrRegister & ABRT_ERR) == ABRT_ERR) { | |
return EFI_ABORTED; | |
} | |
} | |
// | |
// Stall for 30 us | |
// | |
gBS->Stall (30); | |
// | |
// Loop infinitely if not meeting expected condition | |
// | |
if (TimeoutInMicroSeconds == 0) { | |
Delay = 2; | |
} | |
Delay--; | |
} while (Delay); | |
if (Delay == 0) { | |
return EFI_TIMEOUT; | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
AltStatusDRDYReady ( | |
ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, | |
UINT64 TimeoutInMicroSeconds | |
) | |
/*++ | |
Routine Description: | |
Check whether DRDY is ready in the Alternate Status Register. | |
(BSY must also be cleared) | |
If TimeoutInMicroSeconds is zero, this routine should wait infinitely for | |
DRDY ready. Otherwise, it will return EFI_TIMEOUT when specified time is | |
elapsed. | |
Arguments: | |
AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV | |
TimeoutInMicroSeconds - The time to wait for | |
Returns: | |
EFI_STATUS | |
--*/ | |
{ | |
UINT64 Delay; | |
UINT8 AltStatusRegister; | |
UINT8 ErrRegister; | |
if (TimeoutInMicroSeconds == 0) { | |
Delay = 2; | |
} else { | |
Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1; | |
} | |
do { | |
AltStatusRegister = ReadPortB ( | |
AtapiScsiPrivate->PciIo, | |
AtapiScsiPrivate->IoPort->Alt.AltStatus | |
); | |
// | |
// BSY == 0 , DRDY == 1 | |
// | |
if ((AltStatusRegister & (DRDY | BSY)) == DRDY) { | |
break; | |
} | |
if ((AltStatusRegister & (BSY | ERR)) == ERR) { | |
ErrRegister = ReadPortB ( | |
AtapiScsiPrivate->PciIo, | |
AtapiScsiPrivate->IoPort->Reg1.Error | |
); | |
if ((ErrRegister & ABRT_ERR) == ABRT_ERR) { | |
return EFI_ABORTED; | |
} | |
} | |
// | |
// Stall for 30 us | |
// | |
gBS->Stall (30); | |
// | |
// Loop infinitely if not meeting expected condition | |
// | |
if (TimeoutInMicroSeconds == 0) { | |
Delay = 2; | |
} | |
Delay--; | |
} while (Delay); | |
if (Delay == 0) { | |
return EFI_TIMEOUT; | |
} | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
AtapiPassThruCheckErrorStatus ( | |
ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate | |
) | |
/*++ | |
Routine Description: | |
Check Error Register for Error Information. | |
Arguments: | |
AtapiScsiPrivate - The pointer of ATAPI_SCSI_PASS_THRU_DEV | |
Returns: | |
EFI_STATUS | |
--*/ | |
{ | |
UINT8 StatusRegister; | |
UINT8 ErrorRegister; | |
StatusRegister = ReadPortB ( | |
AtapiScsiPrivate->PciIo, | |
AtapiScsiPrivate->IoPort->Reg.Status | |
); | |
DEBUG_CODE_BEGIN (); | |
if (StatusRegister & DWF) { | |
DEBUG ( | |
(EFI_D_BLKIO, | |
"AtapiPassThruCheckErrorStatus()-- %02x : Error : Write Fault\n", | |
StatusRegister) | |
); | |
} | |
if (StatusRegister & CORR) { | |
DEBUG ( | |
(EFI_D_BLKIO, | |
"AtapiPassThruCheckErrorStatus()-- %02x : Error : Corrected Data\n", | |
StatusRegister) | |
); | |
} | |
if (StatusRegister & ERR) { | |
ErrorRegister = ReadPortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Reg1.Error); | |
if (ErrorRegister & BBK_ERR) { | |
DEBUG ( | |
(EFI_D_BLKIO, | |
"AtapiPassThruCheckErrorStatus()-- %02x : Error : Bad Block Detected\n", | |
ErrorRegister) | |
); | |
} | |
if (ErrorRegister & UNC_ERR) { | |
DEBUG ( | |
(EFI_D_BLKIO, | |
"AtapiPassThruCheckErrorStatus()-- %02x : Error : Uncorrectable Data\n", | |
ErrorRegister) | |
); | |
} | |
if (ErrorRegister & MC_ERR) { | |
DEBUG ( | |
(EFI_D_BLKIO, | |
"AtapiPassThruCheckErrorStatus()-- %02x : Error : Media Change\n", | |
ErrorRegister) | |
); | |
} | |
if (ErrorRegister & ABRT_ERR) { | |
DEBUG ( | |
(EFI_D_BLKIO, | |
"AtapiPassThruCheckErrorStatus()-- %02x : Error : Abort\n", | |
ErrorRegister) | |
); | |
} | |
if (ErrorRegister & TK0NF_ERR) { | |
DEBUG ( | |
(EFI_D_BLKIO, | |
"AtapiPassThruCheckErrorStatus()-- %02x : Error : Track 0 Not Found\n", | |
ErrorRegister) | |
); | |
} | |
if (ErrorRegister & AMNF_ERR) { | |
DEBUG ( | |
(EFI_D_BLKIO, | |
"AtapiPassThruCheckErrorStatus()-- %02x : Error : Address Mark Not Found\n", | |
ErrorRegister) | |
); | |
} | |
} | |
DEBUG_CODE_END (); | |
if ((StatusRegister & (ERR | DWF | CORR)) == 0) { | |
return EFI_SUCCESS; | |
} | |
return EFI_DEVICE_ERROR; | |
} | |
/** | |
Installs Scsi Pass Thru and/or Ext Scsi Pass Thru | |
protocols based on feature flags. | |
@param Controller The controller handle to | |
install these protocols on. | |
@param AtapiScsiPrivate A pointer to the protocol private | |
data structure. | |
@retval EFI_SUCCESS The installation succeeds. | |
@retval other The installation fails. | |
**/ | |
EFI_STATUS | |
InstallScsiPassThruProtocols ( | |
IN EFI_HANDLE *ControllerHandle, | |
IN ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_SCSI_PASS_THRU_PROTOCOL *ScsiPassThru; | |
EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExtScsiPassThru; | |
ScsiPassThru = &AtapiScsiPrivate->ScsiPassThru; | |
ExtScsiPassThru = &AtapiScsiPrivate->ExtScsiPassThru; | |
if (FeaturePcdGet (PcdSupportScsiPassThru)) { | |
ScsiPassThru = CopyMem (ScsiPassThru, &gScsiPassThruProtocolTemplate, sizeof (*ScsiPassThru)); | |
if (FeaturePcdGet (PcdSupportExtScsiPassThru)) { | |
ExtScsiPassThru = CopyMem (ExtScsiPassThru, &gExtScsiPassThruProtocolTemplate, sizeof (*ExtScsiPassThru)); | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
ControllerHandle, | |
&gEfiScsiPassThruProtocolGuid, | |
ScsiPassThru, | |
&gEfiExtScsiPassThruProtocolGuid, | |
ExtScsiPassThru, | |
NULL | |
); | |
} else { | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
ControllerHandle, | |
&gEfiScsiPassThruProtocolGuid, | |
ScsiPassThru, | |
NULL | |
); | |
} | |
} else { | |
if (FeaturePcdGet (PcdSupportExtScsiPassThru)) { | |
ExtScsiPassThru = CopyMem (ExtScsiPassThru, &gExtScsiPassThruProtocolTemplate, sizeof (*ExtScsiPassThru)); | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
ControllerHandle, | |
&gEfiExtScsiPassThruProtocolGuid, | |
ExtScsiPassThru, | |
NULL | |
); | |
} else { | |
// | |
// This driver must support either ScsiPassThru or | |
// ExtScsiPassThru protocols | |
// | |
ASSERT (FALSE); | |
Status = EFI_UNSUPPORTED; | |
} | |
} | |
return Status; | |
} | |
/** | |
The user Entry Point for module AtapiPassThru. The user code starts with this function. | |
@param[in] ImageHandle The firmware allocated handle for the EFI image. | |
@param[in] 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 | |
InitializeAtapiPassThru( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
// | |
// Install driver model protocol(s). | |
// | |
Status = EfiLibInstallDriverBindingComponentName2 ( | |
ImageHandle, | |
SystemTable, | |
&gAtapiScsiPassThruDriverBinding, | |
ImageHandle, | |
&gAtapiScsiPassThruComponentName, | |
&gAtapiScsiPassThruComponentName2 | |
); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// Install EFI Driver Supported EFI Version Protocol required for | |
// EFI drivers that are on PCI and other plug in cards. | |
// | |
gAtapiScsiPassThruDriverSupportedEfiVersion.FirmwareVersion = PcdGet32 (PcdDriverSupportedEfiVersion); | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
&ImageHandle, | |
&gEfiDriverSupportedEfiVersionProtocolGuid, | |
&gAtapiScsiPassThruDriverSupportedEfiVersion, | |
NULL | |
); | |
ASSERT_EFI_ERROR (Status); | |
return Status; | |
} |