mirror of https://github.com/acidanthera/audk.git
3417 lines
92 KiB
C
3417 lines
92 KiB
C
/** @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) (ARRAY_SIZE (gSupportedATAPICommands));
|
|
|
|
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) (ARRAY_SIZE (gSupportedATAPICommands));
|
|
|
|
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;
|
|
}
|