/** @file | |
This file implements ATA_PASSTHRU_PROTOCOL and EXT_SCSI_PASSTHRU_PROTOCOL interfaces | |
for managed ATA controllers. | |
Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "AtaAtapiPassThru.h" | |
// | |
// EFI_DRIVER_BINDING_PROTOCOL instance | |
// | |
EFI_DRIVER_BINDING_PROTOCOL gAtaAtapiPassThruDriverBinding = { | |
AtaAtapiPassThruSupported, | |
AtaAtapiPassThruStart, | |
AtaAtapiPassThruStop, | |
0x10, | |
NULL, | |
NULL | |
}; | |
ATA_ATAPI_PASS_THRU_INSTANCE gAtaAtapiPassThruInstanceTemplate = { | |
ATA_ATAPI_PASS_THRU_SIGNATURE, | |
0, // Controller Handle | |
NULL, // PciIo Protocol | |
NULL, // IdeControllerInit Protocol | |
{ // AtaPassThruMode | |
// | |
// According to UEFI2.3 spec Section 12.10, Drivers for non-RAID ATA controllers should set | |
// both EFI_ATA_PASS_THRU_ATTRIBUTES_PHYSICAL and EFI_ATA_PASS_THRU_ATTRIBUTES_LOGICAL | |
// bits. | |
// Note that the driver doesn't support AtaPassThru non blocking I/O. | |
// | |
EFI_ATA_PASS_THRU_ATTRIBUTES_PHYSICAL | EFI_ATA_PASS_THRU_ATTRIBUTES_LOGICAL | EFI_ATA_PASS_THRU_ATTRIBUTES_NONBLOCKIO, | |
// | |
// IoAlign | |
// | |
sizeof (UINTN) | |
}, | |
{ // AtaPassThru | |
NULL, | |
AtaPassThruPassThru, | |
AtaPassThruGetNextPort, | |
AtaPassThruGetNextDevice, | |
AtaPassThruBuildDevicePath, | |
AtaPassThruGetDevice, | |
AtaPassThruResetPort, | |
AtaPassThruResetDevice | |
}, | |
{ // ExtScsiPassThruMode | |
// | |
// AdapterId | |
// | |
0, | |
// | |
// According to UEFI2.3 spec Section 14.7, Drivers for non-RAID SCSI controllers should set | |
// both EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL and EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL | |
// bits. | |
// Note that the driver doesn't support ExtScsiPassThru non blocking I/O. | |
// | |
EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL, | |
// | |
// IoAlign | |
// | |
sizeof (UINTN) | |
}, | |
{ // ExtScsiPassThru | |
NULL, | |
ExtScsiPassThruPassThru, | |
ExtScsiPassThruGetNextTargetLun, | |
ExtScsiPassThruBuildDevicePath, | |
ExtScsiPassThruGetTargetLun, | |
ExtScsiPassThruResetChannel, | |
ExtScsiPassThruResetTargetLun, | |
ExtScsiPassThruGetNextTarget | |
}, | |
EfiAtaUnknownMode, // Work Mode | |
{ // IdeRegisters | |
{ 0 }, | |
{ 0 } | |
}, | |
{ // AhciRegisters | |
0 | |
}, | |
{ // DeviceList | |
NULL, | |
NULL | |
}, | |
0, // EnabledPciAttributes | |
0, // OriginalAttributes | |
0, // PreviousPort | |
0, // PreviousPortMultiplier | |
0, // PreviousTargetId | |
0, // PreviousLun | |
NULL, // Timer event | |
{ // NonBlocking TaskList | |
NULL, | |
NULL | |
} | |
}; | |
ATAPI_DEVICE_PATH mAtapiDevicePathTemplate = { | |
{ | |
MESSAGING_DEVICE_PATH, | |
MSG_ATAPI_DP, | |
{ | |
(UINT8)(sizeof (ATAPI_DEVICE_PATH)), | |
(UINT8)((sizeof (ATAPI_DEVICE_PATH)) >> 8) | |
} | |
}, | |
0, | |
0, | |
0 | |
}; | |
SATA_DEVICE_PATH mSataDevicePathTemplate = { | |
{ | |
MESSAGING_DEVICE_PATH, | |
MSG_SATA_DP, | |
{ | |
(UINT8)(sizeof (SATA_DEVICE_PATH)), | |
(UINT8)((sizeof (SATA_DEVICE_PATH)) >> 8) | |
} | |
}, | |
0, | |
0, | |
0 | |
}; | |
UINT8 mScsiId[TARGET_MAX_BYTES] = { | |
0xFF, 0xFF, 0xFF, 0xFF, | |
0xFF, 0xFF, 0xFF, 0xFF, | |
0xFF, 0xFF, 0xFF, 0xFF, | |
0xFF, 0xFF, 0xFF, 0xFF | |
}; | |
EDKII_ATA_ATAPI_POLICY_PROTOCOL *mAtaAtapiPolicy; | |
EDKII_ATA_ATAPI_POLICY_PROTOCOL mDefaultAtaAtapiPolicy = { | |
EDKII_ATA_ATAPI_POLICY_VERSION, | |
2, // PuisEnable | |
0, // DeviceSleepEnable | |
0, // AggressiveDeviceSleepEnable | |
0 // Reserved | |
}; | |
/** | |
Sends an ATA command to an ATA device that is attached to the ATA controller. This function | |
supports both blocking I/O and non-blocking I/O. The blocking I/O functionality is required, | |
and the non-blocking I/O functionality is optional. | |
@param[in] Port The port number of the ATA device to send the command. | |
@param[in] PortMultiplierPort The port multiplier port number of the ATA device to send the command. | |
If there is no port multiplier, then specify 0xFFFF. | |
@param[in, out] Packet A pointer to the ATA command to send to the ATA device specified by Port | |
and PortMultiplierPort. | |
@param[in] Instance Pointer to the ATA_ATAPI_PASS_THRU_INSTANCE. | |
@param[in] Task Optional. Pointer to the ATA_NONBLOCK_TASK | |
used by non-blocking mode. | |
@retval EFI_SUCCESS The ATA command was sent by the host. For | |
bi-directional commands, InTransferLength bytes | |
were transferred from InDataBuffer. For | |
write and bi-directional commands, OutTransferLength | |
bytes were transferred by OutDataBuffer. | |
@retval EFI_BAD_BUFFER_SIZE The ATA command was not executed. The number | |
of bytes that could be transferred is returned | |
in InTransferLength. For write and bi-directional | |
commands, OutTransferLength bytes were transferred | |
by OutDataBuffer. | |
@retval EFI_NOT_READY The ATA command could not be sent because | |
there are too many ATA commands already | |
queued. The caller may retry again later. | |
@retval EFI_DEVICE_ERROR A device error occurred while attempting | |
to send the ATA command. | |
@retval EFI_INVALID_PARAMETER Port, PortMultiplierPort, or the contents | |
of Acb are invalid. The ATA command was | |
not sent, so no additional status information | |
is available. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
AtaPassThruPassThruExecute ( | |
IN UINT16 Port, | |
IN UINT16 PortMultiplierPort, | |
IN OUT EFI_ATA_PASS_THRU_COMMAND_PACKET *Packet, | |
IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance, | |
IN ATA_NONBLOCK_TASK *Task OPTIONAL | |
) | |
{ | |
EFI_ATA_PASS_THRU_CMD_PROTOCOL Protocol; | |
EFI_ATA_HC_WORK_MODE Mode; | |
EFI_STATUS Status; | |
Protocol = Packet->Protocol; | |
Mode = Instance->Mode; | |
switch (Mode) { | |
case EfiAtaIdeMode: | |
// | |
// Reassign IDE mode io port registers' base addresses | |
// | |
Status = GetIdeRegisterIoAddr (Instance->PciIo, Instance->IdeRegisters); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
switch (Protocol) { | |
case EFI_ATA_PASS_THRU_PROTOCOL_ATA_NON_DATA: | |
Status = AtaNonDataCommandIn ( | |
Instance->PciIo, | |
&Instance->IdeRegisters[Port], | |
Packet->Acb, | |
Packet->Asb, | |
Packet->Timeout, | |
Task | |
); | |
break; | |
case EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_IN: | |
Status = AtaPioDataInOut ( | |
Instance->PciIo, | |
&Instance->IdeRegisters[Port], | |
Packet->InDataBuffer, | |
Packet->InTransferLength, | |
TRUE, | |
Packet->Acb, | |
Packet->Asb, | |
Packet->Timeout, | |
Task | |
); | |
break; | |
case EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_OUT: | |
Status = AtaPioDataInOut ( | |
Instance->PciIo, | |
&Instance->IdeRegisters[Port], | |
Packet->OutDataBuffer, | |
Packet->OutTransferLength, | |
FALSE, | |
Packet->Acb, | |
Packet->Asb, | |
Packet->Timeout, | |
Task | |
); | |
break; | |
case EFI_ATA_PASS_THRU_PROTOCOL_UDMA_DATA_IN: | |
Status = AtaUdmaInOut ( | |
Instance, | |
&Instance->IdeRegisters[Port], | |
TRUE, | |
Packet->InDataBuffer, | |
Packet->InTransferLength, | |
Packet->Acb, | |
Packet->Asb, | |
Packet->Timeout, | |
Task | |
); | |
break; | |
case EFI_ATA_PASS_THRU_PROTOCOL_UDMA_DATA_OUT: | |
Status = AtaUdmaInOut ( | |
Instance, | |
&Instance->IdeRegisters[Port], | |
FALSE, | |
Packet->OutDataBuffer, | |
Packet->OutTransferLength, | |
Packet->Acb, | |
Packet->Asb, | |
Packet->Timeout, | |
Task | |
); | |
break; | |
default: | |
return EFI_UNSUPPORTED; | |
} | |
break; | |
case EfiAtaAhciMode: | |
if (PortMultiplierPort == 0xFFFF) { | |
// | |
// If there is no port multiplier, PortMultiplierPort will be 0xFFFF | |
// according to UEFI spec. Here, we convert its value to 0 to follow | |
// AHCI spec. | |
// | |
PortMultiplierPort = 0; | |
} | |
switch (Protocol) { | |
case EFI_ATA_PASS_THRU_PROTOCOL_ATA_NON_DATA: | |
Status = AhciNonDataTransfer ( | |
Instance->PciIo, | |
&Instance->AhciRegisters, | |
(UINT8)Port, | |
(UINT8)PortMultiplierPort, | |
NULL, | |
0, | |
Packet->Acb, | |
Packet->Asb, | |
Packet->Timeout, | |
Task | |
); | |
break; | |
case EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_IN: | |
Status = AhciPioTransfer ( | |
Instance->PciIo, | |
&Instance->AhciRegisters, | |
(UINT8)Port, | |
(UINT8)PortMultiplierPort, | |
NULL, | |
0, | |
TRUE, | |
Packet->Acb, | |
Packet->Asb, | |
Packet->InDataBuffer, | |
Packet->InTransferLength, | |
Packet->Timeout, | |
Task | |
); | |
break; | |
case EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_OUT: | |
Status = AhciPioTransfer ( | |
Instance->PciIo, | |
&Instance->AhciRegisters, | |
(UINT8)Port, | |
(UINT8)PortMultiplierPort, | |
NULL, | |
0, | |
FALSE, | |
Packet->Acb, | |
Packet->Asb, | |
Packet->OutDataBuffer, | |
Packet->OutTransferLength, | |
Packet->Timeout, | |
Task | |
); | |
break; | |
case EFI_ATA_PASS_THRU_PROTOCOL_UDMA_DATA_IN: | |
Status = AhciDmaTransfer ( | |
Instance, | |
&Instance->AhciRegisters, | |
(UINT8)Port, | |
(UINT8)PortMultiplierPort, | |
NULL, | |
0, | |
TRUE, | |
Packet->Acb, | |
Packet->Asb, | |
Packet->InDataBuffer, | |
Packet->InTransferLength, | |
Packet->Timeout, | |
Task | |
); | |
break; | |
case EFI_ATA_PASS_THRU_PROTOCOL_UDMA_DATA_OUT: | |
Status = AhciDmaTransfer ( | |
Instance, | |
&Instance->AhciRegisters, | |
(UINT8)Port, | |
(UINT8)PortMultiplierPort, | |
NULL, | |
0, | |
FALSE, | |
Packet->Acb, | |
Packet->Asb, | |
Packet->OutDataBuffer, | |
Packet->OutTransferLength, | |
Packet->Timeout, | |
Task | |
); | |
break; | |
default: | |
return EFI_UNSUPPORTED; | |
} | |
break; | |
default: | |
Status = EFI_DEVICE_ERROR; | |
break; | |
} | |
return Status; | |
} | |
/** | |
Call back function when the timer event is signaled. | |
@param[in] Event The Event this notify function registered to. | |
@param[in] Context Pointer to the context data registered to the | |
Event. | |
**/ | |
VOID | |
EFIAPI | |
AsyncNonBlockingTransferRoutine ( | |
EFI_EVENT Event, | |
VOID *Context | |
) | |
{ | |
LIST_ENTRY *Entry; | |
LIST_ENTRY *EntryHeader; | |
ATA_NONBLOCK_TASK *Task; | |
EFI_STATUS Status; | |
ATA_ATAPI_PASS_THRU_INSTANCE *Instance; | |
Instance = (ATA_ATAPI_PASS_THRU_INSTANCE *)Context; | |
EntryHeader = &Instance->NonBlockingTaskList; | |
// | |
// Get the Tasks from the Tasks List and execute it, until there is | |
// no task in the list or the device is busy with task (EFI_NOT_READY). | |
// | |
while (TRUE) { | |
if (!IsListEmpty (EntryHeader)) { | |
Entry = GetFirstNode (EntryHeader); | |
Task = ATA_NON_BLOCK_TASK_FROM_ENTRY (Entry); | |
} else { | |
return; | |
} | |
Status = AtaPassThruPassThruExecute ( | |
Task->Port, | |
Task->PortMultiplier, | |
Task->Packet, | |
Instance, | |
Task | |
); | |
// | |
// If the data transfer meet a error, remove all tasks in the list since these tasks are | |
// associated with one task from Ata Bus and signal the event with error status. | |
// | |
if ((Status != EFI_NOT_READY) && (Status != EFI_SUCCESS)) { | |
DestroyAsynTaskList (Instance, TRUE); | |
break; | |
} | |
// | |
// For Non blocking mode, the Status of EFI_NOT_READY means the operation | |
// is not finished yet. Otherwise the operation is successful. | |
// | |
if (Status == EFI_NOT_READY) { | |
break; | |
} else { | |
RemoveEntryList (&Task->Link); | |
gBS->SignalEvent (Task->Event); | |
FreePool (Task); | |
} | |
} | |
} | |
/** | |
The Entry Point of module. | |
@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 | |
InitializeAtaAtapiPassThru ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
// | |
// Install driver model protocol(s). | |
// | |
Status = EfiLibInstallDriverBindingComponentName2 ( | |
ImageHandle, | |
SystemTable, | |
&gAtaAtapiPassThruDriverBinding, | |
ImageHandle, | |
&gAtaAtapiPassThruComponentName, | |
&gAtaAtapiPassThruComponentName2 | |
); | |
ASSERT_EFI_ERROR (Status); | |
return Status; | |
} | |
/** | |
Tests to see if this driver supports a given controller. If a child device is provided, | |
it further tests to see if this driver supports creating a handle for the specified child device. | |
This function checks to see if the driver specified by This supports the device specified by | |
ControllerHandle. Drivers will typically use the device path attached to | |
ControllerHandle and/or the services from the bus I/O abstraction attached to | |
ControllerHandle to determine if the driver supports ControllerHandle. This function | |
may be called many times during platform initialization. In order to reduce boot times, the tests | |
performed by this function must be very small, and take as little time as possible to execute. This | |
function must not change the state of any hardware devices, and this function must be aware that the | |
device specified by ControllerHandle may already be managed by the same driver or a | |
different driver. This function must match its calls to AllocatePages() with FreePages(), | |
AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol(). | |
Because ControllerHandle may have been previously started by the same driver, if a protocol is | |
already in the opened state, then it must not be closed with CloseProtocol(). This is required | |
to guarantee the state of ControllerHandle is not modified by this function. | |
@param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. | |
@param[in] ControllerHandle The handle of the controller to test. This handle | |
must support a protocol interface that supplies | |
an I/O abstraction to the driver. | |
@param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This | |
parameter is ignored by device drivers, and is optional for bus | |
drivers. For bus drivers, if this parameter is not NULL, then | |
the bus driver must determine if the bus controller specified | |
by ControllerHandle and the child controller specified | |
by RemainingDevicePath are both supported by this | |
bus driver. | |
@retval EFI_SUCCESS The device specified by ControllerHandle and | |
RemainingDevicePath is supported by the driver specified by This. | |
@retval EFI_ALREADY_STARTED The device specified by ControllerHandle and | |
RemainingDevicePath is already being managed by the driver | |
specified by This. | |
@retval EFI_ACCESS_DENIED The device specified by ControllerHandle and | |
RemainingDevicePath is already being managed by a different | |
driver or an application that requires exclusive access. | |
Currently not implemented. | |
@retval EFI_UNSUPPORTED The device specified by ControllerHandle and | |
RemainingDevicePath is not supported by the driver specified by This. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
AtaAtapiPassThruSupported ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE Controller, | |
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; | |
EFI_PCI_IO_PROTOCOL *PciIo; | |
PCI_TYPE00 PciData; | |
EFI_IDE_CONTROLLER_INIT_PROTOCOL *IdeControllerInit; | |
// | |
// SATA Controller is a device driver, and should ignore the | |
// "RemainingDevicePath" according to UEFI spec | |
// | |
Status = gBS->OpenProtocol ( | |
Controller, | |
&gEfiDevicePathProtocolGuid, | |
(VOID *)&ParentDevicePath, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_BY_DRIVER | |
); | |
if (EFI_ERROR (Status)) { | |
// | |
// EFI_ALREADY_STARTED is also an error | |
// | |
return Status; | |
} | |
// | |
// Close the protocol because we don't use it here | |
// | |
gBS->CloseProtocol ( | |
Controller, | |
&gEfiDevicePathProtocolGuid, | |
This->DriverBindingHandle, | |
Controller | |
); | |
Status = gBS->OpenProtocol ( | |
Controller, | |
&gEfiIdeControllerInitProtocolGuid, | |
(VOID **)&IdeControllerInit, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_BY_DRIVER | |
); | |
if (EFI_ERROR (Status)) { | |
// | |
// EFI_ALREADY_STARTED is also an error | |
// | |
return Status; | |
} | |
// | |
// Close the I/O Abstraction(s) used to perform the supported test | |
// | |
gBS->CloseProtocol ( | |
Controller, | |
&gEfiIdeControllerInitProtocolGuid, | |
This->DriverBindingHandle, | |
Controller | |
); | |
// | |
// Now test the EfiPciIoProtocol | |
// | |
Status = gBS->OpenProtocol ( | |
Controller, | |
&gEfiPciIoProtocolGuid, | |
(VOID **)&PciIo, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Now further check the PCI header: Base class (offset 0x0B) and | |
// Sub Class (offset 0x0A). This controller should be an ATA controller | |
// | |
Status = PciIo->Pci.Read ( | |
PciIo, | |
EfiPciIoWidthUint8, | |
PCI_CLASSCODE_OFFSET, | |
sizeof (PciData.Hdr.ClassCode), | |
PciData.Hdr.ClassCode | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_UNSUPPORTED; | |
} | |
if (IS_PCI_IDE (&PciData) || IS_PCI_SATADPA (&PciData)) { | |
return EFI_SUCCESS; | |
} | |
return EFI_UNSUPPORTED; | |
} | |
/** | |
Starts a device controller or a bus controller. | |
The Start() function is designed to be invoked from the EFI boot service ConnectController(). | |
As a result, much of the error checking on the parameters to Start() has been moved into this | |
common boot service. It is legal to call Start() from other locations, | |
but the following calling restrictions must be followed, or the system behavior will not be deterministic. | |
1. ControllerHandle must be a valid EFI_HANDLE. | |
2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned | |
EFI_DEVICE_PATH_PROTOCOL. | |
3. Prior to calling Start(), the Supported() function for the driver specified by This must | |
have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS. | |
@param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. | |
@param[in] ControllerHandle The handle of the controller to start. This handle | |
must support a protocol interface that supplies | |
an I/O abstraction to the driver. | |
@param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This | |
parameter is ignored by device drivers, and is optional for bus | |
drivers. For a bus driver, if this parameter is NULL, then handles | |
for all the children of Controller are created by this driver. | |
If this parameter is not NULL and the first Device Path Node is | |
not the End of Device Path Node, then only the handle for the | |
child device specified by the first Device Path Node of | |
RemainingDevicePath is created by this driver. | |
If the first Device Path Node of RemainingDevicePath is | |
the End of Device Path Node, no child handle is created by this | |
driver. | |
@retval EFI_SUCCESS The device was started. | |
@retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented. | |
@retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. | |
@retval Others The driver failed to start the device. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
AtaAtapiPassThruStart ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE Controller, | |
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_IDE_CONTROLLER_INIT_PROTOCOL *IdeControllerInit; | |
ATA_ATAPI_PASS_THRU_INSTANCE *Instance; | |
EFI_PCI_IO_PROTOCOL *PciIo; | |
UINT64 EnabledPciAttributes; | |
UINT64 OriginalPciAttributes; | |
Status = EFI_SUCCESS; | |
IdeControllerInit = NULL; | |
Instance = NULL; | |
OriginalPciAttributes = 0; | |
DEBUG ((DEBUG_INFO, "==AtaAtapiPassThru Start== Controller = %x\n", Controller)); | |
Status = gBS->OpenProtocol ( | |
Controller, | |
&gEfiIdeControllerInitProtocolGuid, | |
(VOID **)&IdeControllerInit, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_BY_DRIVER | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "Open Ide_Controller_Init Error, Status=%r", Status)); | |
goto ErrorExit; | |
} | |
Status = gBS->OpenProtocol ( | |
Controller, | |
&gEfiPciIoProtocolGuid, | |
(VOID **)&PciIo, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "Get Pci_Io Protocol Error, Status=%r", Status)); | |
goto ErrorExit; | |
} | |
Status = PciIo->Attributes ( | |
PciIo, | |
EfiPciIoAttributeOperationGet, | |
0, | |
&OriginalPciAttributes | |
); | |
if (EFI_ERROR (Status)) { | |
goto ErrorExit; | |
} | |
Status = PciIo->Attributes ( | |
PciIo, | |
EfiPciIoAttributeOperationSupported, | |
0, | |
&EnabledPciAttributes | |
); | |
if (!EFI_ERROR (Status)) { | |
EnabledPciAttributes &= (UINT64)EFI_PCI_DEVICE_ENABLE; | |
Status = PciIo->Attributes ( | |
PciIo, | |
EfiPciIoAttributeOperationEnable, | |
EnabledPciAttributes, | |
NULL | |
); | |
} | |
if (EFI_ERROR (Status)) { | |
goto ErrorExit; | |
} | |
Status = gBS->LocateProtocol (&gEdkiiAtaAtapiPolicyProtocolGuid, NULL, (VOID **)&mAtaAtapiPolicy); | |
if (EFI_ERROR (Status)) { | |
// | |
// If there is no AtaAtapiPolicy exposed, use the default policy. | |
// | |
mAtaAtapiPolicy = &mDefaultAtaAtapiPolicy; | |
} | |
// | |
// Allocate a buffer to store the ATA_ATAPI_PASS_THRU_INSTANCE data structure | |
// | |
Instance = AllocateCopyPool (sizeof (ATA_ATAPI_PASS_THRU_INSTANCE), &gAtaAtapiPassThruInstanceTemplate); | |
if (Instance == NULL) { | |
goto ErrorExit; | |
} | |
Instance->ControllerHandle = Controller; | |
Instance->IdeControllerInit = IdeControllerInit; | |
Instance->PciIo = PciIo; | |
Instance->EnabledPciAttributes = EnabledPciAttributes; | |
Instance->OriginalPciAttributes = OriginalPciAttributes; | |
Instance->AtaPassThru.Mode = &Instance->AtaPassThruMode; | |
Instance->ExtScsiPassThru.Mode = &Instance->ExtScsiPassThruMode; | |
InitializeListHead (&Instance->DeviceList); | |
InitializeListHead (&Instance->NonBlockingTaskList); | |
Instance->TimerEvent = NULL; | |
Status = gBS->CreateEvent ( | |
EVT_TIMER | EVT_NOTIFY_SIGNAL, | |
TPL_NOTIFY, | |
AsyncNonBlockingTransferRoutine, | |
Instance, | |
&Instance->TimerEvent | |
); | |
if (EFI_ERROR (Status)) { | |
goto ErrorExit; | |
} | |
// | |
// Set 1ms timer. | |
// | |
Status = gBS->SetTimer (Instance->TimerEvent, TimerPeriodic, 10000); | |
if (EFI_ERROR (Status)) { | |
goto ErrorExit; | |
} | |
// | |
// Enumerate all inserted ATA devices. | |
// | |
Status = EnumerateAttachedDevice (Instance); | |
if (EFI_ERROR (Status)) { | |
goto ErrorExit; | |
} | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
&Controller, | |
&gEfiAtaPassThruProtocolGuid, | |
&(Instance->AtaPassThru), | |
&gEfiExtScsiPassThruProtocolGuid, | |
&(Instance->ExtScsiPassThru), | |
NULL | |
); | |
ASSERT_EFI_ERROR (Status); | |
return Status; | |
ErrorExit: | |
if (IdeControllerInit != NULL) { | |
gBS->CloseProtocol ( | |
Controller, | |
&gEfiIdeControllerInitProtocolGuid, | |
This->DriverBindingHandle, | |
Controller | |
); | |
} | |
if ((Instance != NULL) && (Instance->TimerEvent != NULL)) { | |
gBS->CloseEvent (Instance->TimerEvent); | |
} | |
if (Instance != NULL) { | |
// | |
// Remove all inserted ATA devices. | |
// | |
DestroyDeviceInfoList (Instance); | |
FreePool (Instance); | |
} | |
return EFI_UNSUPPORTED; | |
} | |
/** | |
Stops a device controller or a bus controller. | |
The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). | |
As a result, much of the error checking on the parameters to Stop() has been moved | |
into this common boot service. It is legal to call Stop() from other locations, | |
but the following calling restrictions must be followed, or the system behavior will not be deterministic. | |
1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this | |
same driver's Start() function. | |
2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid | |
EFI_HANDLE. In addition, all of these handles must have been created in this driver's | |
Start() function, and the Start() function must have called OpenProtocol() on | |
ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. | |
@param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance. | |
@param[in] ControllerHandle A handle to the device being stopped. The handle must | |
support a bus specific I/O protocol for the driver | |
to use to stop the device. | |
@param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer. | |
@param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL | |
if NumberOfChildren is 0. | |
@retval EFI_SUCCESS The device was stopped. | |
@retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
AtaAtapiPassThruStop ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE Controller, | |
IN UINTN NumberOfChildren, | |
IN EFI_HANDLE *ChildHandleBuffer | |
) | |
{ | |
EFI_STATUS Status; | |
ATA_ATAPI_PASS_THRU_INSTANCE *Instance; | |
EFI_ATA_PASS_THRU_PROTOCOL *AtaPassThru; | |
EFI_PCI_IO_PROTOCOL *PciIo; | |
EFI_AHCI_REGISTERS *AhciRegisters; | |
DEBUG ((DEBUG_INFO, "==AtaAtapiPassThru Stop== Controller = %x\n", Controller)); | |
Status = gBS->OpenProtocol ( | |
Controller, | |
&gEfiAtaPassThruProtocolGuid, | |
(VOID **)&AtaPassThru, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_DEVICE_ERROR; | |
} | |
Instance = ATA_PASS_THRU_PRIVATE_DATA_FROM_THIS (AtaPassThru); | |
Status = gBS->UninstallMultipleProtocolInterfaces ( | |
Controller, | |
&gEfiAtaPassThruProtocolGuid, | |
&(Instance->AtaPassThru), | |
&gEfiExtScsiPassThruProtocolGuid, | |
&(Instance->ExtScsiPassThru), | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_DEVICE_ERROR; | |
} | |
// | |
// Close protocols opened by AtaAtapiPassThru controller driver | |
// | |
gBS->CloseProtocol ( | |
Controller, | |
&gEfiIdeControllerInitProtocolGuid, | |
This->DriverBindingHandle, | |
Controller | |
); | |
// | |
// Close Non-Blocking timer and free Task list. | |
// | |
if (Instance->TimerEvent != NULL) { | |
gBS->CloseEvent (Instance->TimerEvent); | |
Instance->TimerEvent = NULL; | |
} | |
DestroyAsynTaskList (Instance, FALSE); | |
// | |
// Free allocated resource | |
// | |
DestroyDeviceInfoList (Instance); | |
PciIo = Instance->PciIo; | |
// | |
// Disable this ATA host controller. | |
// | |
PciIo->Attributes ( | |
PciIo, | |
EfiPciIoAttributeOperationDisable, | |
Instance->EnabledPciAttributes, | |
NULL | |
); | |
// | |
// If the current working mode is AHCI mode, then pre-allocated resource | |
// for AHCI initialization should be released. | |
// | |
if (Instance->Mode == EfiAtaAhciMode) { | |
AhciRegisters = &Instance->AhciRegisters; | |
PciIo->Unmap ( | |
PciIo, | |
AhciRegisters->MapCommandTable | |
); | |
PciIo->FreeBuffer ( | |
PciIo, | |
EFI_SIZE_TO_PAGES ((UINTN)AhciRegisters->MaxCommandTableSize), | |
AhciRegisters->AhciCommandTable | |
); | |
PciIo->Unmap ( | |
PciIo, | |
AhciRegisters->MapCmdList | |
); | |
PciIo->FreeBuffer ( | |
PciIo, | |
EFI_SIZE_TO_PAGES ((UINTN)AhciRegisters->MaxCommandListSize), | |
AhciRegisters->AhciCmdList | |
); | |
PciIo->Unmap ( | |
PciIo, | |
AhciRegisters->MapRFis | |
); | |
PciIo->FreeBuffer ( | |
PciIo, | |
EFI_SIZE_TO_PAGES ((UINTN)AhciRegisters->MaxReceiveFisSize), | |
AhciRegisters->AhciRFis | |
); | |
} | |
// | |
// Restore original PCI attributes | |
// | |
Status = PciIo->Attributes ( | |
PciIo, | |
EfiPciIoAttributeOperationSet, | |
Instance->OriginalPciAttributes, | |
NULL | |
); | |
ASSERT_EFI_ERROR (Status); | |
FreePool (Instance); | |
return Status; | |
} | |
/** | |
Traverse the attached ATA devices list to find out the device to access. | |
@param[in] Instance A pointer to the ATA_ATAPI_PASS_THRU_INSTANCE instance. | |
@param[in] Port The port number of the ATA device to send the command. | |
@param[in] PortMultiplierPort The port multiplier port number of the ATA device to send the command. | |
If there is no port multiplier, then specify 0xFFFF. | |
@param[in] DeviceType The device type of the ATA device. | |
@retval The pointer to the data structure of the device info to access. | |
**/ | |
LIST_ENTRY * | |
EFIAPI | |
SearchDeviceInfoList ( | |
IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance, | |
IN UINT16 Port, | |
IN UINT16 PortMultiplier, | |
IN EFI_ATA_DEVICE_TYPE DeviceType | |
) | |
{ | |
EFI_ATA_DEVICE_INFO *DeviceInfo; | |
LIST_ENTRY *Node; | |
Node = GetFirstNode (&Instance->DeviceList); | |
while (!IsNull (&Instance->DeviceList, Node)) { | |
DeviceInfo = ATA_ATAPI_DEVICE_INFO_FROM_THIS (Node); | |
// | |
// For CD-ROM working in the AHCI mode, only 8 bits are used to record | |
// the PortMultiplier information. If the CD-ROM is directly attached | |
// on a SATA port, the PortMultiplier should be translated from 0xFF | |
// to 0xFFFF according to the UEFI spec. | |
// | |
if ((Instance->Mode == EfiAtaAhciMode) && | |
(DeviceInfo->Type == EfiIdeCdrom) && | |
(PortMultiplier == 0xFF)) | |
{ | |
PortMultiplier = 0xFFFF; | |
} | |
if ((DeviceInfo->Type == DeviceType) && | |
(Port == DeviceInfo->Port) && | |
(PortMultiplier == DeviceInfo->PortMultiplier)) | |
{ | |
return Node; | |
} | |
Node = GetNextNode (&Instance->DeviceList, Node); | |
} | |
return NULL; | |
} | |
/** | |
Allocate device info data structure to contain device info. | |
And insert the data structure to the tail of device list for tracing. | |
@param[in] Instance A pointer to the ATA_ATAPI_PASS_THRU_INSTANCE instance. | |
@param[in] Port The port number of the ATA device to send the command. | |
@param[in] PortMultiplierPort The port multiplier port number of the ATA device to send the command. | |
If there is no port multiplier, then specify 0xFFFF. | |
@param[in] DeviceType The device type of the ATA device. | |
@param[in] IdentifyData The data buffer to store the output of the IDENTIFY cmd. | |
@retval EFI_SUCCESS Successfully insert the ata device to the tail of device list. | |
@retval EFI_OUT_OF_RESOURCES Can not allocate enough resource for use. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
CreateNewDeviceInfo ( | |
IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance, | |
IN UINT16 Port, | |
IN UINT16 PortMultiplier, | |
IN EFI_ATA_DEVICE_TYPE DeviceType, | |
IN EFI_IDENTIFY_DATA *IdentifyData | |
) | |
{ | |
EFI_ATA_DEVICE_INFO *DeviceInfo; | |
DeviceInfo = AllocateZeroPool (sizeof (EFI_ATA_DEVICE_INFO)); | |
if (DeviceInfo == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
DeviceInfo->Signature = ATA_ATAPI_DEVICE_SIGNATURE; | |
DeviceInfo->Port = Port; | |
DeviceInfo->PortMultiplier = PortMultiplier; | |
DeviceInfo->Type = DeviceType; | |
if (IdentifyData != NULL) { | |
DeviceInfo->IdentifyData = AllocateCopyPool (sizeof (EFI_IDENTIFY_DATA), IdentifyData); | |
if (DeviceInfo->IdentifyData == NULL) { | |
FreePool (DeviceInfo); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
} | |
InsertTailList (&Instance->DeviceList, &DeviceInfo->Link); | |
return EFI_SUCCESS; | |
} | |
/** | |
Destroy all attached ATA devices info. | |
@param[in] Instance A pointer to the ATA_ATAPI_PASS_THRU_INSTANCE instance. | |
**/ | |
VOID | |
EFIAPI | |
DestroyDeviceInfoList ( | |
IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance | |
) | |
{ | |
EFI_ATA_DEVICE_INFO *DeviceInfo; | |
LIST_ENTRY *Node; | |
Node = GetFirstNode (&Instance->DeviceList); | |
while (!IsNull (&Instance->DeviceList, Node)) { | |
DeviceInfo = ATA_ATAPI_DEVICE_INFO_FROM_THIS (Node); | |
Node = GetNextNode (&Instance->DeviceList, Node); | |
RemoveEntryList (&DeviceInfo->Link); | |
if (DeviceInfo->IdentifyData != NULL) { | |
FreePool (DeviceInfo->IdentifyData); | |
} | |
FreePool (DeviceInfo); | |
} | |
} | |
/** | |
Destroy all pending non blocking tasks. | |
@param[in] Instance A pointer to the ATA_ATAPI_PASS_THRU_INSTANCE instance. | |
@param[in] IsSigEvent Indicate whether signal the task event when remove the | |
task. | |
**/ | |
VOID | |
EFIAPI | |
DestroyAsynTaskList ( | |
IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance, | |
IN BOOLEAN IsSigEvent | |
) | |
{ | |
LIST_ENTRY *Entry; | |
LIST_ENTRY *DelEntry; | |
ATA_NONBLOCK_TASK *Task; | |
EFI_TPL OldTpl; | |
OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
if (!IsListEmpty (&Instance->NonBlockingTaskList)) { | |
// | |
// Free the Subtask list. | |
// | |
for (Entry = (&Instance->NonBlockingTaskList)->ForwardLink; | |
Entry != (&Instance->NonBlockingTaskList); | |
) | |
{ | |
DelEntry = Entry; | |
Entry = Entry->ForwardLink; | |
Task = ATA_NON_BLOCK_TASK_FROM_ENTRY (DelEntry); | |
RemoveEntryList (DelEntry); | |
if (IsSigEvent) { | |
Task->Packet->Asb->AtaStatus = 0x01; | |
gBS->SignalEvent (Task->Event); | |
} | |
FreePool (Task); | |
} | |
} | |
gBS->RestoreTPL (OldTpl); | |
} | |
/** | |
Enumerate all attached ATA devices at IDE mode or AHCI mode separately. | |
The function is designed to enumerate all attached ATA devices. | |
@param[in] Instance A pointer to the ATA_ATAPI_PASS_THRU_INSTANCE instance. | |
@retval EFI_SUCCESS Successfully enumerate attached ATA devices. | |
@retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EnumerateAttachedDevice ( | |
IN ATA_ATAPI_PASS_THRU_INSTANCE *Instance | |
) | |
{ | |
EFI_STATUS Status; | |
PCI_TYPE00 PciData; | |
UINT8 ClassCode; | |
Status = EFI_SUCCESS; | |
Status = Instance->PciIo->Pci.Read ( | |
Instance->PciIo, | |
EfiPciIoWidthUint8, | |
PCI_CLASSCODE_OFFSET, | |
sizeof (PciData.Hdr.ClassCode), | |
PciData.Hdr.ClassCode | |
); | |
ASSERT_EFI_ERROR (Status); | |
ClassCode = PciData.Hdr.ClassCode[1]; | |
switch (ClassCode) { | |
case PCI_CLASS_MASS_STORAGE_IDE: | |
// | |
// The ATA controller is working at IDE mode | |
// | |
Instance->Mode = EfiAtaIdeMode; | |
Status = IdeModeInitialization (Instance); | |
if (EFI_ERROR (Status)) { | |
Status = EFI_DEVICE_ERROR; | |
goto Done; | |
} | |
break; | |
case PCI_CLASS_MASS_STORAGE_SATADPA: | |
// | |
// The ATA controller is working at AHCI mode | |
// | |
Instance->Mode = EfiAtaAhciMode; | |
Status = AhciModeInitialization (Instance); | |
if (EFI_ERROR (Status)) { | |
Status = EFI_DEVICE_ERROR; | |
goto Done; | |
} | |
break; | |
default: | |
Status = EFI_UNSUPPORTED; | |
} | |
Done: | |
return Status; | |
} | |
/** | |
Sends an ATA command to an ATA device that is attached to the ATA controller. This function | |
supports both blocking I/O and non-blocking I/O. The blocking I/O functionality is required, | |
and the non-blocking I/O functionality is optional. | |
@param[in] This A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance. | |
@param[in] Port The port number of the ATA device to send the command. | |
@param[in] PortMultiplierPort The port multiplier port number of the ATA device to send the command. | |
If there is no port multiplier, then specify 0xFFFF. | |
@param[in, out] Packet A pointer to the ATA command to send to the ATA device specified by Port | |
and PortMultiplierPort. | |
@param[in] 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 ATA command completes. | |
@retval EFI_SUCCESS The ATA command was sent by the host. For bi-directional commands, | |
InTransferLength bytes were transferred from InDataBuffer. For write and | |
bi-directional commands, OutTransferLength bytes were transferred by OutDataBuffer. | |
@retval EFI_BAD_BUFFER_SIZE The ATA command was not executed. The number of bytes that could be transferred | |
is returned in InTransferLength. For write and bi-directional commands, | |
OutTransferLength bytes were transferred by OutDataBuffer. | |
@retval EFI_NOT_READY The ATA command could not be sent because there are too many ATA commands | |
already queued. The caller may retry again later. | |
@retval EFI_DEVICE_ERROR A device error occurred while attempting to send the ATA command. | |
@retval EFI_INVALID_PARAMETER Port, PortMultiplierPort, or the contents of Acb are invalid. The ATA | |
command was not sent, so no additional status information is available. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
AtaPassThruPassThru ( | |
IN EFI_ATA_PASS_THRU_PROTOCOL *This, | |
IN UINT16 Port, | |
IN UINT16 PortMultiplierPort, | |
IN OUT EFI_ATA_PASS_THRU_COMMAND_PACKET *Packet, | |
IN EFI_EVENT Event OPTIONAL | |
) | |
{ | |
ATA_ATAPI_PASS_THRU_INSTANCE *Instance; | |
LIST_ENTRY *Node; | |
EFI_ATA_DEVICE_INFO *DeviceInfo; | |
EFI_IDENTIFY_DATA *IdentifyData; | |
UINT64 Capacity; | |
UINT32 MaxSectorCount; | |
ATA_NONBLOCK_TASK *Task; | |
EFI_TPL OldTpl; | |
UINT32 BlockSize; | |
Instance = ATA_PASS_THRU_PRIVATE_DATA_FROM_THIS (This); | |
if ((This->Mode->IoAlign > 1) && !ADDRESS_IS_ALIGNED (Packet->InDataBuffer, This->Mode->IoAlign)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((This->Mode->IoAlign > 1) && !ADDRESS_IS_ALIGNED (Packet->OutDataBuffer, This->Mode->IoAlign)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((This->Mode->IoAlign > 1) && !ADDRESS_IS_ALIGNED (Packet->Asb, This->Mode->IoAlign)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Node = SearchDeviceInfoList (Instance, Port, PortMultiplierPort, EfiIdeHarddisk); | |
if (Node == NULL) { | |
Node = SearchDeviceInfoList (Instance, Port, PortMultiplierPort, EfiIdeCdrom); | |
if (Node == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
} | |
// | |
// Check whether this device needs 48-bit addressing (ATAPI-6 ata device). | |
// Per ATA-6 spec, word83: bit15 is zero and bit14 is one. | |
// If bit10 is one, it means the ata device support 48-bit addressing. | |
// | |
DeviceInfo = ATA_ATAPI_DEVICE_INFO_FROM_THIS (Node); | |
IdentifyData = DeviceInfo->IdentifyData; | |
MaxSectorCount = 0x100; | |
if ((IdentifyData->AtaData.command_set_supported_83 & (BIT10 | BIT15 | BIT14)) == 0x4400) { | |
Capacity = *((UINT64 *)IdentifyData->AtaData.maximum_lba_for_48bit_addressing); | |
if (Capacity > 0xFFFFFFF) { | |
// | |
// Capacity exceeds 120GB. 48-bit addressing is really needed | |
// In this case, the max sector count is 0x10000 | |
// | |
MaxSectorCount = 0x10000; | |
} | |
} | |
BlockSize = 0x200; | |
if ((IdentifyData->AtaData.phy_logic_sector_support & (BIT14 | BIT15)) == BIT14) { | |
// | |
// Check logical block size | |
// | |
if ((IdentifyData->AtaData.phy_logic_sector_support & BIT12) != 0) { | |
BlockSize = (UINT32)(((UINT32)(IdentifyData->AtaData.logic_sector_size_hi << 16) | IdentifyData->AtaData.logic_sector_size_lo) * sizeof (UINT16)); | |
} | |
} | |
// | |
// convert the transfer length from sector count to byte. | |
// | |
if (((Packet->Length & EFI_ATA_PASS_THRU_LENGTH_BYTES) == 0) && | |
(Packet->InTransferLength != 0)) | |
{ | |
Packet->InTransferLength = Packet->InTransferLength * BlockSize; | |
} | |
// | |
// convert the transfer length from sector count to byte. | |
// | |
if (((Packet->Length & EFI_ATA_PASS_THRU_LENGTH_BYTES) == 0) && | |
(Packet->OutTransferLength != 0)) | |
{ | |
Packet->OutTransferLength = Packet->OutTransferLength * BlockSize; | |
} | |
// | |
// If the data buffer described by InDataBuffer/OutDataBuffer and InTransferLength/OutTransferLength | |
// is too big to be transferred in a single command, then no data is transferred and EFI_BAD_BUFFER_SIZE | |
// is returned. | |
// | |
if (((Packet->InTransferLength != 0) && (Packet->InTransferLength > MaxSectorCount * BlockSize)) || | |
((Packet->OutTransferLength != 0) && (Packet->OutTransferLength > MaxSectorCount * BlockSize))) | |
{ | |
return EFI_BAD_BUFFER_SIZE; | |
} | |
// | |
// For non-blocking mode, queue the Task into the list. | |
// | |
if (Event != NULL) { | |
Task = AllocateZeroPool (sizeof (ATA_NONBLOCK_TASK)); | |
if (Task == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
Task->Signature = ATA_NONBLOCKING_TASK_SIGNATURE; | |
Task->Port = Port; | |
Task->PortMultiplier = PortMultiplierPort; | |
Task->Packet = Packet; | |
Task->Event = Event; | |
Task->IsStart = FALSE; | |
Task->RetryTimes = DivU64x32 (Packet->Timeout, 1000) + 1; | |
if (Packet->Timeout == 0) { | |
Task->InfiniteWait = TRUE; | |
} else { | |
Task->InfiniteWait = FALSE; | |
} | |
OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
InsertTailList (&Instance->NonBlockingTaskList, &Task->Link); | |
gBS->RestoreTPL (OldTpl); | |
return EFI_SUCCESS; | |
} else { | |
return AtaPassThruPassThruExecute ( | |
Port, | |
PortMultiplierPort, | |
Packet, | |
Instance, | |
NULL | |
); | |
} | |
} | |
/** | |
Used to retrieve the list of legal port numbers for ATA devices on an ATA controller. | |
These can either be the list of ports where ATA devices are actually present or the | |
list of legal port numbers for the ATA controller. Regardless, the caller of this | |
function must probe the port number returned to see if an ATA device is actually | |
present at that location on the ATA controller. | |
The GetNextPort() function retrieves the port number on an ATA controller. If on input | |
Port is 0xFFFF, then the port number of the first port on the ATA controller is returned | |
in Port and EFI_SUCCESS is returned. | |
If Port is a port number that was returned on a previous call to GetNextPort(), then the | |
port number of the next port on the ATA controller is returned in Port, and EFI_SUCCESS | |
is returned. If Port is not 0xFFFF and Port was not returned on a previous call to | |
GetNextPort(), then EFI_INVALID_PARAMETER is returned. | |
If Port is the port number of the last port on the ATA controller, then EFI_NOT_FOUND is | |
returned. | |
@param[in] This A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance. | |
@param[in, out] Port On input, a pointer to the port number on the ATA controller. | |
On output, a pointer to the next port number on the ATA | |
controller. An input value of 0xFFFF retrieves the first port | |
number on the ATA controller. | |
@retval EFI_SUCCESS The next port number on the ATA controller was returned in Port. | |
@retval EFI_NOT_FOUND There are no more ports on this ATA controller. | |
@retval EFI_INVALID_PARAMETER Port is not 0xFFFF and Port was not returned on a previous call | |
to GetNextPort(). | |
**/ | |
EFI_STATUS | |
EFIAPI | |
AtaPassThruGetNextPort ( | |
IN EFI_ATA_PASS_THRU_PROTOCOL *This, | |
IN OUT UINT16 *Port | |
) | |
{ | |
ATA_ATAPI_PASS_THRU_INSTANCE *Instance; | |
LIST_ENTRY *Node; | |
EFI_ATA_DEVICE_INFO *DeviceInfo; | |
Instance = ATA_PASS_THRU_PRIVATE_DATA_FROM_THIS (This); | |
if (Port == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (*Port == 0xFFFF) { | |
// | |
// If the Port is all 0xFF's, start to traverse the device list from the beginning | |
// | |
Node = GetFirstNode (&Instance->DeviceList); | |
while (!IsNull (&Instance->DeviceList, Node)) { | |
DeviceInfo = ATA_ATAPI_DEVICE_INFO_FROM_THIS (Node); | |
if (DeviceInfo->Type == EfiIdeHarddisk) { | |
*Port = DeviceInfo->Port; | |
goto Exit; | |
} | |
Node = GetNextNode (&Instance->DeviceList, Node); | |
} | |
return EFI_NOT_FOUND; | |
} else if (*Port == Instance->PreviousPort) { | |
Node = GetFirstNode (&Instance->DeviceList); | |
while (!IsNull (&Instance->DeviceList, Node)) { | |
DeviceInfo = ATA_ATAPI_DEVICE_INFO_FROM_THIS (Node); | |
if ((DeviceInfo->Type == EfiIdeHarddisk) && | |
(DeviceInfo->Port > *Port)) | |
{ | |
*Port = DeviceInfo->Port; | |
goto Exit; | |
} | |
Node = GetNextNode (&Instance->DeviceList, Node); | |
} | |
return EFI_NOT_FOUND; | |
} else { | |
// | |
// Port is not equal to 0xFFFF and also not equal to previous return value | |
// | |
return EFI_INVALID_PARAMETER; | |
} | |
Exit: | |
// | |
// Update the PreviousPort and PreviousPortMultiplier. | |
// | |
Instance->PreviousPort = *Port; | |
return EFI_SUCCESS; | |
} | |
/** | |
Used to retrieve the list of legal port multiplier port numbers for ATA devices on a port of an ATA | |
controller. These can either be the list of port multiplier ports where ATA devices are actually | |
present on port or the list of legal port multiplier ports on that port. Regardless, the caller of this | |
function must probe the port number and port multiplier port number returned to see if an ATA | |
device is actually present. | |
The GetNextDevice() function retrieves the port multiplier port number of an ATA device | |
present on a port of an ATA controller. | |
If PortMultiplierPort points to a port multiplier port number value that was returned on a | |
previous call to GetNextDevice(), then the port multiplier port number of the next ATA device | |
on the port of the ATA controller is returned in PortMultiplierPort, and EFI_SUCCESS is | |
returned. | |
If PortMultiplierPort points to 0xFFFF, then the port multiplier port number of the first | |
ATA device on port of the ATA controller is returned in PortMultiplierPort and | |
EFI_SUCCESS is returned. | |
If PortMultiplierPort is not 0xFFFF and the value pointed to by PortMultiplierPort | |
was not returned on a previous call to GetNextDevice(), then EFI_INVALID_PARAMETER | |
is returned. | |
If PortMultiplierPort is the port multiplier port number of the last ATA device on the port of | |
the ATA controller, then EFI_NOT_FOUND is returned. | |
@param[in] This A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance. | |
@param[in] Port The port number present on the ATA controller. | |
@param[in, out] PortMultiplierPort On input, a pointer to the port multiplier port number of an | |
ATA device present on the ATA controller. | |
If on input a PortMultiplierPort of 0xFFFF is specified, | |
then the port multiplier port number of the first ATA device | |
is returned. On output, a pointer to the port multiplier port | |
number of the next ATA device present on an ATA controller. | |
@retval EFI_SUCCESS The port multiplier port number of the next ATA device on the port | |
of the ATA controller was returned in PortMultiplierPort. | |
@retval EFI_NOT_FOUND There are no more ATA devices on this port of the ATA controller. | |
@retval EFI_INVALID_PARAMETER PortMultiplierPort is not 0xFFFF, and PortMultiplierPort was not | |
returned on a previous call to GetNextDevice(). | |
**/ | |
EFI_STATUS | |
EFIAPI | |
AtaPassThruGetNextDevice ( | |
IN EFI_ATA_PASS_THRU_PROTOCOL *This, | |
IN UINT16 Port, | |
IN OUT UINT16 *PortMultiplierPort | |
) | |
{ | |
ATA_ATAPI_PASS_THRU_INSTANCE *Instance; | |
LIST_ENTRY *Node; | |
EFI_ATA_DEVICE_INFO *DeviceInfo; | |
Instance = ATA_PASS_THRU_PRIVATE_DATA_FROM_THIS (This); | |
if (PortMultiplierPort == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (Instance->PreviousPortMultiplier == 0xFFFF) { | |
// | |
// If a device is directly attached on a port, previous call to this | |
// function will return the value 0xFFFF for PortMultiplierPort. In | |
// this case, there should be no more device on the port multiplier. | |
// | |
Instance->PreviousPortMultiplier = 0; | |
return EFI_NOT_FOUND; | |
} | |
if (*PortMultiplierPort == Instance->PreviousPortMultiplier) { | |
Node = GetFirstNode (&Instance->DeviceList); | |
while (!IsNull (&Instance->DeviceList, Node)) { | |
DeviceInfo = ATA_ATAPI_DEVICE_INFO_FROM_THIS (Node); | |
if ((DeviceInfo->Type == EfiIdeHarddisk) && | |
(DeviceInfo->Port == Port) && | |
(DeviceInfo->PortMultiplier > *PortMultiplierPort)) | |
{ | |
*PortMultiplierPort = DeviceInfo->PortMultiplier; | |
goto Exit; | |
} | |
Node = GetNextNode (&Instance->DeviceList, Node); | |
} | |
return EFI_NOT_FOUND; | |
} else if (*PortMultiplierPort == 0xFFFF) { | |
// | |
// If the PortMultiplierPort is all 0xFF's, start to traverse the device list from the beginning | |
// | |
Node = GetFirstNode (&Instance->DeviceList); | |
while (!IsNull (&Instance->DeviceList, Node)) { | |
DeviceInfo = ATA_ATAPI_DEVICE_INFO_FROM_THIS (Node); | |
if ((DeviceInfo->Type == EfiIdeHarddisk) && | |
(DeviceInfo->Port == Port)) | |
{ | |
*PortMultiplierPort = DeviceInfo->PortMultiplier; | |
goto Exit; | |
} | |
Node = GetNextNode (&Instance->DeviceList, Node); | |
} | |
return EFI_NOT_FOUND; | |
} else { | |
// | |
// PortMultiplierPort is not equal to 0xFFFF and also not equal to previous return value | |
// | |
return EFI_INVALID_PARAMETER; | |
} | |
Exit: | |
// | |
// Update the PreviousPort and PreviousPortMultiplier. | |
// | |
Instance->PreviousPortMultiplier = *PortMultiplierPort; | |
return EFI_SUCCESS; | |
} | |
/** | |
Used to allocate and build a device path node for an ATA device on an ATA controller. | |
The BuildDevicePath() function allocates and builds a single device node for the ATA | |
device specified by Port and PortMultiplierPort. If the ATA device specified by Port and | |
PortMultiplierPort is not present on the ATA controller, then EFI_NOT_FOUND is returned. | |
If DevicePath is NULL, then EFI_INVALID_PARAMETER is returned. If there are not enough | |
resources to allocate the device path node, then EFI_OUT_OF_RESOURCES is returned. | |
Otherwise, DevicePath is allocated with the boot service AllocatePool(), the contents of | |
DevicePath are initialized to describe the ATA device specified by Port and PortMultiplierPort, | |
and EFI_SUCCESS is returned. | |
@param[in] This A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance. | |
@param[in] Port Port specifies the port number of the ATA device for which a | |
device path node is to be allocated and built. | |
@param[in] PortMultiplierPort The port multiplier port number of the ATA device for which a | |
device path node is to be allocated and built. If there is no | |
port multiplier, then specify 0xFFFF. | |
@param[in, out] DevicePath A pointer to a single device path node that describes the ATA | |
device specified by Port and PortMultiplierPort. 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. | |
@retval EFI_SUCCESS The device path node that describes the ATA device specified by | |
Port and PortMultiplierPort was allocated and returned in DevicePath. | |
@retval EFI_NOT_FOUND The ATA device specified by Port and PortMultiplierPort does not | |
exist on the ATA controller. | |
@retval EFI_INVALID_PARAMETER DevicePath is NULL. | |
@retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate DevicePath. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
AtaPassThruBuildDevicePath ( | |
IN EFI_ATA_PASS_THRU_PROTOCOL *This, | |
IN UINT16 Port, | |
IN UINT16 PortMultiplierPort, | |
IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath | |
) | |
{ | |
EFI_DEV_PATH *DevicePathNode; | |
ATA_ATAPI_PASS_THRU_INSTANCE *Instance; | |
LIST_ENTRY *Node; | |
Instance = ATA_PASS_THRU_PRIVATE_DATA_FROM_THIS (This); | |
// | |
// Validate parameters passed in. | |
// | |
if (DevicePath == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Node = SearchDeviceInfoList (Instance, Port, PortMultiplierPort, EfiIdeHarddisk); | |
if (Node == NULL) { | |
return EFI_NOT_FOUND; | |
} | |
if (Instance->Mode == EfiAtaIdeMode) { | |
DevicePathNode = AllocateCopyPool (sizeof (ATAPI_DEVICE_PATH), &mAtapiDevicePathTemplate); | |
if (DevicePathNode == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
DevicePathNode->Atapi.PrimarySecondary = (UINT8)Port; | |
DevicePathNode->Atapi.SlaveMaster = (UINT8)PortMultiplierPort; | |
DevicePathNode->Atapi.Lun = 0; | |
} else { | |
DevicePathNode = AllocateCopyPool (sizeof (SATA_DEVICE_PATH), &mSataDevicePathTemplate); | |
if (DevicePathNode == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
DevicePathNode->Sata.HBAPortNumber = Port; | |
DevicePathNode->Sata.PortMultiplierPortNumber = PortMultiplierPort; | |
DevicePathNode->Sata.Lun = 0; | |
} | |
*DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)DevicePathNode; | |
return EFI_SUCCESS; | |
} | |
/** | |
Used to translate a device path node to a port number and port multiplier port number. | |
The GetDevice() function determines the port and port multiplier port number associated with | |
the ATA device described by DevicePath. If DevicePath is a device path node type that the | |
ATA Pass Thru driver supports, then the ATA Pass Thru driver will attempt to translate the contents | |
DevicePath into a port number and port multiplier port number. | |
If this translation is successful, then that port number and port multiplier port number are returned | |
in Port and PortMultiplierPort, and EFI_SUCCESS is returned. | |
If DevicePath, Port, or PortMultiplierPort are NULL, then EFI_INVALID_PARAMETER is returned. | |
If DevicePath is not a device path node type that the ATA Pass Thru driver supports, then | |
EFI_UNSUPPORTED is returned. | |
If DevicePath is a device path node type that the ATA Pass Thru driver supports, but there is not | |
a valid translation from DevicePath to a port number and port multiplier port number, then | |
EFI_NOT_FOUND is returned. | |
@param[in] This A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance. | |
@param[in] DevicePath A pointer to the device path node that describes an ATA device on the | |
ATA controller. | |
@param[out] Port On return, points to the port number of an ATA device on the ATA controller. | |
@param[out] PortMultiplierPort On return, points to the port multiplier port number of an ATA device | |
on the ATA controller. | |
@retval EFI_SUCCESS DevicePath was successfully translated to a port number and port multiplier | |
port number, and they were returned in Port and PortMultiplierPort. | |
@retval EFI_INVALID_PARAMETER DevicePath is NULL. | |
@retval EFI_INVALID_PARAMETER Port is NULL. | |
@retval EFI_INVALID_PARAMETER PortMultiplierPort is NULL. | |
@retval EFI_UNSUPPORTED This driver does not support the device path node type in DevicePath. | |
@retval EFI_NOT_FOUND A valid translation from DevicePath to a port number and port multiplier | |
port number does not exist. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
AtaPassThruGetDevice ( | |
IN EFI_ATA_PASS_THRU_PROTOCOL *This, | |
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, | |
OUT UINT16 *Port, | |
OUT UINT16 *PortMultiplierPort | |
) | |
{ | |
EFI_DEV_PATH *DevicePathNode; | |
ATA_ATAPI_PASS_THRU_INSTANCE *Instance; | |
LIST_ENTRY *Node; | |
Instance = ATA_PASS_THRU_PRIVATE_DATA_FROM_THIS (This); | |
// | |
// Validate parameters passed in. | |
// | |
if ((DevicePath == NULL) || (Port == NULL) || (PortMultiplierPort == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Check whether the DevicePath belongs to SCSI_DEVICE_PATH or ATAPI_DEVICE_PATH | |
// | |
if ((DevicePath->Type != MESSAGING_DEVICE_PATH) || | |
((DevicePath->SubType != MSG_SATA_DP) && | |
(DevicePath->SubType != MSG_ATAPI_DP)) || | |
((DevicePathNodeLength (DevicePath) != sizeof (ATAPI_DEVICE_PATH)) && | |
(DevicePathNodeLength (DevicePath) != sizeof (SATA_DEVICE_PATH)))) | |
{ | |
return EFI_UNSUPPORTED; | |
} | |
DevicePathNode = (EFI_DEV_PATH *)DevicePath; | |
if (Instance->Mode == EfiAtaIdeMode) { | |
*Port = DevicePathNode->Atapi.PrimarySecondary; | |
*PortMultiplierPort = DevicePathNode->Atapi.SlaveMaster; | |
} else { | |
*Port = DevicePathNode->Sata.HBAPortNumber; | |
*PortMultiplierPort = DevicePathNode->Sata.PortMultiplierPortNumber; | |
} | |
Node = SearchDeviceInfoList (Instance, *Port, *PortMultiplierPort, EfiIdeHarddisk); | |
if (Node == NULL) { | |
return EFI_NOT_FOUND; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Resets a specific port on the ATA controller. This operation also resets all the ATA devices | |
connected to the port. | |
The ResetChannel() function resets an a specific port on an ATA controller. This operation | |
resets all the ATA devices connected to that port. If this ATA controller does not support | |
a reset port operation, then EFI_UNSUPPORTED is returned. | |
If a device error occurs while executing that port reset operation, then EFI_DEVICE_ERROR is | |
returned. | |
If a timeout occurs during the execution of the port reset operation, then EFI_TIMEOUT is returned. | |
If the port reset operation is completed, then EFI_SUCCESS is returned. | |
@param[in] This A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance. | |
@param[in] Port The port number on the ATA controller. | |
@retval EFI_SUCCESS The ATA controller port was reset. | |
@retval EFI_UNSUPPORTED The ATA controller does not support a port reset operation. | |
@retval EFI_DEVICE_ERROR A device error occurred while attempting to reset the ATA port. | |
@retval EFI_TIMEOUT A timeout occurred while attempting to reset the ATA port. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
AtaPassThruResetPort ( | |
IN EFI_ATA_PASS_THRU_PROTOCOL *This, | |
IN UINT16 Port | |
) | |
{ | |
// | |
// Return success directly then upper layer driver could think reset port operation is done. | |
// | |
return EFI_SUCCESS; | |
} | |
/** | |
Resets an ATA device that is connected to an ATA controller. | |
The ResetDevice() function resets the ATA device specified by Port and PortMultiplierPort. | |
If this ATA controller does not support a device reset operation, then EFI_UNSUPPORTED is | |
returned. | |
If Port or PortMultiplierPort are not in a valid range for this ATA controller, then | |
EFI_INVALID_PARAMETER is returned. | |
If a device error occurs while executing that device reset operation, then EFI_DEVICE_ERROR | |
is returned. | |
If a timeout occurs during the execution of the device reset operation, then EFI_TIMEOUT is | |
returned. | |
If the device reset operation is completed, then EFI_SUCCESS is returned. | |
@param[in] This A pointer to the EFI_ATA_PASS_THRU_PROTOCOL instance. | |
@param[in] Port Port represents the port number of the ATA device to be reset. | |
@param[in] PortMultiplierPort The port multiplier port number of the ATA device to reset. | |
If there is no port multiplier, then specify 0xFFFF. | |
@retval EFI_SUCCESS The ATA device specified by Port and PortMultiplierPort was reset. | |
@retval EFI_UNSUPPORTED The ATA controller does not support a device reset operation. | |
@retval EFI_INVALID_PARAMETER Port or PortMultiplierPort are invalid. | |
@retval EFI_DEVICE_ERROR A device error occurred while attempting to reset the ATA device | |
specified by Port and PortMultiplierPort. | |
@retval EFI_TIMEOUT A timeout occurred while attempting to reset the ATA device | |
specified by Port and PortMultiplierPort. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
AtaPassThruResetDevice ( | |
IN EFI_ATA_PASS_THRU_PROTOCOL *This, | |
IN UINT16 Port, | |
IN UINT16 PortMultiplierPort | |
) | |
{ | |
ATA_ATAPI_PASS_THRU_INSTANCE *Instance; | |
LIST_ENTRY *Node; | |
Instance = ATA_PASS_THRU_PRIVATE_DATA_FROM_THIS (This); | |
Node = SearchDeviceInfoList (Instance, Port, PortMultiplierPort, EfiIdeHarddisk); | |
if (Node == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Return success directly then upper layer driver could think reset device operation is done. | |
// | |
return EFI_SUCCESS; | |
} | |
/** | |
Submit ATAPI request sense command. | |
@param[in] This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. | |
@param[in] Target The Target is an array of size TARGET_MAX_BYTES and it represents | |
the id of the SCSI device to send the SCSI Request Packet. Each | |
transport driver may choose to utilize a subset of this size to suit the needs | |
of transport target representation. For example, a Fibre Channel driver | |
may use only 8 bytes (WWN) to represent an FC target. | |
@param[in] Lun The LUN of the SCSI device to send the SCSI Request Packet. | |
@param[in] SenseData A pointer to store sense data. | |
@param[in] SenseDataLength The sense data length. | |
@param[in] Timeout The timeout value to execute this cmd, uses 100ns as a unit. | |
@retval EFI_SUCCESS Send out the ATAPI packet command successfully. | |
@retval EFI_DEVICE_ERROR The device failed to send data. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
AtaPacketRequestSense ( | |
IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, | |
IN UINT8 *Target, | |
IN UINT64 Lun, | |
IN VOID *SenseData, | |
IN UINT8 SenseDataLength, | |
IN UINT64 Timeout | |
) | |
{ | |
EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET Packet; | |
UINT8 Cdb[12]; | |
EFI_STATUS Status; | |
ZeroMem (&Packet, sizeof (EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET)); | |
ZeroMem (Cdb, 12); | |
Cdb[0] = ATA_CMD_REQUEST_SENSE; | |
Cdb[4] = SenseDataLength; | |
Packet.Timeout = Timeout; | |
Packet.Cdb = Cdb; | |
Packet.CdbLength = 12; | |
Packet.DataDirection = EFI_EXT_SCSI_DATA_DIRECTION_READ; | |
Packet.InDataBuffer = SenseData; | |
Packet.InTransferLength = SenseDataLength; | |
Status = ExtScsiPassThruPassThru (This, Target, Lun, &Packet, NULL); | |
return Status; | |
} | |
/** | |
Sends a SCSI Request Packet to a SCSI device that is attached to the SCSI channel. This function | |
supports both blocking I/O and nonblocking I/O. The blocking I/O functionality is required, and the | |
nonblocking I/O functionality is optional. | |
@param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. | |
@param Target The Target is an array of size TARGET_MAX_BYTES and it represents | |
the id of the SCSI device to send the SCSI Request Packet. Each | |
transport driver may choose to utilize a subset of this size to suit the needs | |
of transport target representation. For example, a Fibre Channel driver | |
may use only 8 bytes (WWN) to represent an FC target. | |
@param Lun The LUN of the SCSI device to send the SCSI Request Packet. | |
@param Packet A pointer to the SCSI Request Packet to send to the SCSI device | |
specified by Target and Lun. | |
@param Event If nonblocking 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 | |
nonblocking I/O is performed, and Event will be signaled when the | |
SCSI Request Packet completes. | |
@retval EFI_SUCCESS The SCSI Request Packet was sent by the host. For bi-directional | |
commands, InTransferLength bytes were transferred from | |
InDataBuffer. For write and bi-directional commands, | |
OutTransferLength bytes were transferred by | |
OutDataBuffer. | |
@retval EFI_BAD_BUFFER_SIZE The SCSI Request Packet was not executed. The number of bytes that | |
could be transferred is returned in InTransferLength. For write | |
and bi-directional commands, OutTransferLength bytes were | |
transferred by OutDataBuffer. | |
@retval EFI_NOT_READY The SCSI Request Packet could not be sent because there are too many | |
SCSI Request Packets already queued. The caller may retry again later. | |
@retval EFI_DEVICE_ERROR A device error occurred while attempting to send the SCSI Request | |
Packet. | |
@retval EFI_INVALID_PARAMETER Target, Lun, or the contents of ScsiRequestPacket are invalid. | |
@retval EFI_UNSUPPORTED The command described by the SCSI Request Packet is not supported | |
by the host adapter. This includes the case of Bi-directional SCSI | |
commands not supported by the implementation. The SCSI Request | |
Packet was not sent, so no additional status information is available. | |
@retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI Request Packet to execute. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
ExtScsiPassThruPassThru ( | |
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 | |
) | |
{ | |
EFI_STATUS Status; | |
ATA_ATAPI_PASS_THRU_INSTANCE *Instance; | |
UINT8 Port; | |
UINT8 PortMultiplier; | |
EFI_ATA_HC_WORK_MODE Mode; | |
LIST_ENTRY *Node; | |
EFI_ATA_DEVICE_INFO *DeviceInfo; | |
BOOLEAN SenseReq; | |
EFI_SCSI_SENSE_DATA *PtrSenseData; | |
UINTN SenseDataLen; | |
EFI_STATUS SenseStatus; | |
SenseDataLen = 0; | |
Instance = EXT_SCSI_PASS_THRU_PRIVATE_DATA_FROM_THIS (This); | |
if ((Packet == NULL) || (Packet->Cdb == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Don't support variable length CDB | |
// | |
if ((Packet->CdbLength != 6) && (Packet->CdbLength != 10) && | |
(Packet->CdbLength != 12) && (Packet->CdbLength != 16)) | |
{ | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((Packet->SenseDataLength != 0) && (Packet->SenseData == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((This->Mode->IoAlign > 1) && !ADDRESS_IS_ALIGNED (Packet->InDataBuffer, This->Mode->IoAlign)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((This->Mode->IoAlign > 1) && !ADDRESS_IS_ALIGNED (Packet->OutDataBuffer, This->Mode->IoAlign)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((This->Mode->IoAlign > 1) && !ADDRESS_IS_ALIGNED (Packet->SenseData, This->Mode->IoAlign)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// For ATAPI device, doesn't support multiple LUN device. | |
// | |
if (Lun != 0) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// The layout of Target array: | |
// ________________________________________________________________________ | |
// | Byte 0 | Byte 1 | ... | TARGET_MAX_BYTES - 1 | | |
// |_____________________|_____________________|_____|______________________| | |
// | | The port multiplier | | | | |
// | The port number | port number | N/A | N/A | | |
// |_____________________|_____________________|_____|______________________| | |
// | |
// For ATAPI device, 2 bytes is enough to represent the location of SCSI device. | |
// | |
Port = Target[0]; | |
PortMultiplier = Target[1]; | |
Node = SearchDeviceInfoList (Instance, Port, PortMultiplier, EfiIdeCdrom); | |
if (Node == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
DeviceInfo = ATA_ATAPI_DEVICE_INFO_FROM_THIS (Node); | |
// | |
// ATA_CMD_IDENTIFY_DEVICE cmd is a ATA cmd but not a SCSI cmd. | |
// Normally it should NOT be passed down through ExtScsiPassThru protocol interface. | |
// But to response EFI_DISK_INFO.Identify() request from ScsiDisk, we should handle this command. | |
// | |
if (*((UINT8 *)Packet->Cdb) == ATA_CMD_IDENTIFY_DEVICE) { | |
CopyMem (Packet->InDataBuffer, DeviceInfo->IdentifyData, sizeof (EFI_IDENTIFY_DATA)); | |
// | |
// For IDENTIFY DEVICE cmd, we don't need to get sense data. | |
// | |
Packet->SenseDataLength = 0; | |
return EFI_SUCCESS; | |
} | |
Mode = Instance->Mode; | |
switch (Mode) { | |
case EfiAtaIdeMode: | |
// | |
// Reassign IDE mode io port registers' base addresses | |
// | |
Status = GetIdeRegisterIoAddr (Instance->PciIo, Instance->IdeRegisters); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = AtaPacketCommandExecute (Instance->PciIo, &Instance->IdeRegisters[Port], Port, PortMultiplier, Packet); | |
break; | |
case EfiAtaAhciMode: | |
if (PortMultiplier == 0xFF) { | |
// | |
// If there is no port multiplier, the PortMultiplier will be 0xFF | |
// Here, we convert its value to 0 to follow the AHCI spec. | |
// | |
PortMultiplier = 0; | |
} | |
Status = AhciPacketCommandExecute (Instance->PciIo, &Instance->AhciRegisters, Port, PortMultiplier, Packet); | |
break; | |
default: | |
Status = EFI_DEVICE_ERROR; | |
break; | |
} | |
// | |
// If the cmd doesn't get executed correctly, then check sense data. | |
// | |
if (EFI_ERROR (Status) && (Packet->SenseDataLength != 0) && (*((UINT8 *)Packet->Cdb) != ATA_CMD_REQUEST_SENSE)) { | |
PtrSenseData = AllocateAlignedPages (EFI_SIZE_TO_PAGES (sizeof (EFI_SCSI_SENSE_DATA)), This->Mode->IoAlign); | |
if (PtrSenseData == NULL) { | |
return EFI_DEVICE_ERROR; | |
} | |
for (SenseReq = TRUE; SenseReq;) { | |
SenseStatus = AtaPacketRequestSense ( | |
This, | |
Target, | |
Lun, | |
PtrSenseData, | |
sizeof (EFI_SCSI_SENSE_DATA), | |
Packet->Timeout | |
); | |
if (EFI_ERROR (SenseStatus)) { | |
break; | |
} | |
CopyMem ((UINT8 *)Packet->SenseData + SenseDataLen, PtrSenseData, sizeof (EFI_SCSI_SENSE_DATA)); | |
SenseDataLen += sizeof (EFI_SCSI_SENSE_DATA); | |
// | |
// no more sense key or number of sense keys exceeds predefined, | |
// skip the loop. | |
// | |
if ((PtrSenseData->Sense_Key == EFI_SCSI_SK_NO_SENSE) || | |
(SenseDataLen + sizeof (EFI_SCSI_SENSE_DATA) > Packet->SenseDataLength)) | |
{ | |
SenseReq = FALSE; | |
} | |
} | |
FreeAlignedPages (PtrSenseData, EFI_SIZE_TO_PAGES (sizeof (EFI_SCSI_SENSE_DATA))); | |
} | |
// | |
// Update the SenseDataLength field to the data length received. | |
// | |
Packet->SenseDataLength = (UINT8)SenseDataLen; | |
return Status; | |
} | |
/** | |
Used to retrieve the list of legal Target IDs and LUNs for SCSI devices on a SCSI channel. These | |
can either be the list SCSI devices that are actually present on the SCSI channel, or the list of legal | |
Target Ids and LUNs for the SCSI channel. Regardless, the caller of this function must probe the | |
Target ID and LUN returned to see if a SCSI device is actually present at that location on the SCSI | |
channel. | |
@param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. | |
@param Target On input, a pointer to the Target ID (an array of size | |
TARGET_MAX_BYTES) of a SCSI device present on the SCSI channel. | |
On output, a pointer to the Target ID (an array of | |
TARGET_MAX_BYTES) of the next SCSI device present on a SCSI | |
channel. An input value of 0xF(all bytes in the array are 0xF) in the | |
Target array retrieves the Target ID of the first SCSI device present on a | |
SCSI channel. | |
@param 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. | |
@retval EFI_SUCCESS The Target ID and LUN of the next SCSI device on the SCSI | |
channel was returned in Target and Lun. | |
@retval EFI_INVALID_PARAMETER Target array is not all 0xF, and Target and Lun were | |
not returned on a previous call to GetNextTargetLun(). | |
@retval EFI_NOT_FOUND There are no more SCSI devices on this SCSI channel. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
ExtScsiPassThruGetNextTargetLun ( | |
IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, | |
IN OUT UINT8 **Target, | |
IN OUT UINT64 *Lun | |
) | |
{ | |
ATA_ATAPI_PASS_THRU_INSTANCE *Instance; | |
LIST_ENTRY *Node; | |
EFI_ATA_DEVICE_INFO *DeviceInfo; | |
UINT8 *Target8; | |
UINT16 *Target16; | |
Instance = EXT_SCSI_PASS_THRU_PRIVATE_DATA_FROM_THIS (This); | |
if ((Target == NULL) || (Lun == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (*Target == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Target8 = *Target; | |
Target16 = (UINT16 *)*Target; | |
if (CompareMem (Target8, mScsiId, TARGET_MAX_BYTES) != 0) { | |
// | |
// For ATAPI device, we use 2 least significant bytes to represent the location of SCSI device. | |
// So the higher bytes in Target array should be 0xFF. | |
// | |
if (CompareMem (&Target8[2], &mScsiId[2], TARGET_MAX_BYTES - 2) != 0) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// When Target is not all 0xFF's, compare 2 least significant bytes with | |
// previous target id to see if it is returned by previous call. | |
// | |
if ((*Target16 != Instance->PreviousTargetId) || | |
(*Lun != Instance->PreviousLun)) | |
{ | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Traverse the whole device list to find the next cdrom closed to | |
// the device signified by Target[0] and Target[1]. | |
// | |
// Note that we here use a tricky way to find the next cdrom : | |
// All ata devices are detected and inserted into the device list | |
// sequentially. | |
// | |
Node = GetFirstNode (&Instance->DeviceList); | |
while (!IsNull (&Instance->DeviceList, Node)) { | |
DeviceInfo = ATA_ATAPI_DEVICE_INFO_FROM_THIS (Node); | |
if ((DeviceInfo->Type == EfiIdeCdrom) && | |
((Target8[0] < DeviceInfo->Port) || | |
((Target8[0] == DeviceInfo->Port) && | |
(Target8[1] < (UINT8)DeviceInfo->PortMultiplier)))) | |
{ | |
Target8[0] = (UINT8)DeviceInfo->Port; | |
Target8[1] = (UINT8)DeviceInfo->PortMultiplier; | |
goto Exit; | |
} | |
Node = GetNextNode (&Instance->DeviceList, Node); | |
} | |
return EFI_NOT_FOUND; | |
} else { | |
// | |
// If the array is all 0xFF's, start to traverse the device list from the beginning | |
// | |
Node = GetFirstNode (&Instance->DeviceList); | |
while (!IsNull (&Instance->DeviceList, Node)) { | |
DeviceInfo = ATA_ATAPI_DEVICE_INFO_FROM_THIS (Node); | |
if (DeviceInfo->Type == EfiIdeCdrom) { | |
Target8[0] = (UINT8)DeviceInfo->Port; | |
Target8[1] = (UINT8)DeviceInfo->PortMultiplier; | |
goto Exit; | |
} | |
Node = GetNextNode (&Instance->DeviceList, Node); | |
} | |
return EFI_NOT_FOUND; | |
} | |
Exit: | |
*Lun = 0; | |
// | |
// Update the PreviousTargetId. | |
// | |
Instance->PreviousTargetId = *Target16; | |
Instance->PreviousLun = *Lun; | |
return EFI_SUCCESS; | |
} | |
/** | |
Used to allocate and build a device path node for a SCSI device on a SCSI channel. | |
@param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. | |
@param Target The Target is an array of size TARGET_MAX_BYTES and it specifies the | |
Target ID of the SCSI device for which a device path node is to be | |
allocated and built. Transport drivers may chose to utilize a subset of | |
this size to suit the representation of targets. For example, a Fibre | |
Channel driver may use only 8 bytes (WWN) in the array to represent a | |
FC target. | |
@param Lun The LUN of the SCSI device for which a device path node is to be | |
allocated and built. | |
@param 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. | |
@retval EFI_SUCCESS The device path node that describes the SCSI device specified by | |
Target and Lun was allocated and returned in | |
DevicePath. | |
@retval EFI_INVALID_PARAMETER DevicePath is NULL. | |
@retval EFI_NOT_FOUND The SCSI devices specified by Target and Lun does not exist | |
on the SCSI channel. | |
@retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate DevicePath. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
ExtScsiPassThruBuildDevicePath ( | |
IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, | |
IN UINT8 *Target, | |
IN UINT64 Lun, | |
IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath | |
) | |
{ | |
EFI_DEV_PATH *DevicePathNode; | |
ATA_ATAPI_PASS_THRU_INSTANCE *Instance; | |
UINT8 Port; | |
UINT8 PortMultiplier; | |
Instance = EXT_SCSI_PASS_THRU_PRIVATE_DATA_FROM_THIS (This); | |
Port = Target[0]; | |
PortMultiplier = Target[1]; | |
// | |
// Validate parameters passed in. | |
// | |
if (DevicePath == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// can not build device path for the SCSI Host Controller. | |
// | |
if (Lun != 0) { | |
return EFI_NOT_FOUND; | |
} | |
if (SearchDeviceInfoList (Instance, Port, PortMultiplier, EfiIdeCdrom) == NULL) { | |
return EFI_NOT_FOUND; | |
} | |
if (Instance->Mode == EfiAtaIdeMode) { | |
DevicePathNode = AllocateCopyPool (sizeof (ATAPI_DEVICE_PATH), &mAtapiDevicePathTemplate); | |
if (DevicePathNode == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
DevicePathNode->Atapi.PrimarySecondary = Port; | |
DevicePathNode->Atapi.SlaveMaster = PortMultiplier; | |
DevicePathNode->Atapi.Lun = (UINT16)Lun; | |
} else { | |
DevicePathNode = AllocateCopyPool (sizeof (SATA_DEVICE_PATH), &mSataDevicePathTemplate); | |
if (DevicePathNode == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
DevicePathNode->Sata.HBAPortNumber = Port; | |
// | |
// For CD-ROM working in the AHCI mode, only 8 bits are used to record | |
// the PortMultiplier information. If the CD-ROM is directly attached | |
// on a SATA port, the PortMultiplier should be translated from 0xFF | |
// to 0xFFFF according to the UEFI spec. | |
// | |
DevicePathNode->Sata.PortMultiplierPortNumber = PortMultiplier == 0xFF ? 0xFFFF : PortMultiplier; | |
DevicePathNode->Sata.Lun = (UINT16)Lun; | |
} | |
*DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)DevicePathNode; | |
return EFI_SUCCESS; | |
} | |
/** | |
Used to translate a device path node to a Target ID and LUN. | |
@param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. | |
@param DevicePath A pointer to a single device path node that describes the SCSI device | |
on the SCSI channel. | |
@param Target A pointer to the Target Array which represents the ID of a SCSI device | |
on the SCSI channel. | |
@param Lun A pointer to the LUN of a SCSI device on the SCSI channel. | |
@retval EFI_SUCCESS DevicePath was successfully translated to a Target ID and | |
LUN, and they were returned in Target and Lun. | |
@retval EFI_INVALID_PARAMETER DevicePath or Target or Lun is NULL. | |
@retval EFI_NOT_FOUND A valid translation from DevicePath to a Target ID and LUN | |
does not exist. | |
@retval EFI_UNSUPPORTED This driver does not support the device path node type in | |
DevicePath. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
ExtScsiPassThruGetTargetLun ( | |
IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, | |
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, | |
OUT UINT8 **Target, | |
OUT UINT64 *Lun | |
) | |
{ | |
EFI_DEV_PATH *DevicePathNode; | |
ATA_ATAPI_PASS_THRU_INSTANCE *Instance; | |
LIST_ENTRY *Node; | |
Instance = EXT_SCSI_PASS_THRU_PRIVATE_DATA_FROM_THIS (This); | |
// | |
// Validate parameters passed in. | |
// | |
if ((DevicePath == NULL) || (Target == NULL) || (Lun == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (*Target == 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) && | |
(DevicePath->SubType != MSG_SATA_DP)) || | |
((DevicePathNodeLength (DevicePath) != sizeof (ATAPI_DEVICE_PATH)) && | |
(DevicePathNodeLength (DevicePath) != sizeof (SATA_DEVICE_PATH)))) | |
{ | |
return EFI_UNSUPPORTED; | |
} | |
SetMem (*Target, TARGET_MAX_BYTES, 0xFF); | |
DevicePathNode = (EFI_DEV_PATH *)DevicePath; | |
if (Instance->Mode == EfiAtaIdeMode) { | |
(*Target)[0] = (UINT8)DevicePathNode->Atapi.PrimarySecondary; | |
(*Target)[1] = (UINT8)DevicePathNode->Atapi.SlaveMaster; | |
*Lun = (UINT8)DevicePathNode->Atapi.Lun; | |
} else { | |
(*Target)[0] = (UINT8)DevicePathNode->Sata.HBAPortNumber; | |
(*Target)[1] = (UINT8)DevicePathNode->Sata.PortMultiplierPortNumber; | |
*Lun = (UINT8)DevicePathNode->Sata.Lun; | |
} | |
Node = SearchDeviceInfoList (Instance, (*Target)[0], (*Target)[1], EfiIdeCdrom); | |
if (Node == NULL) { | |
return EFI_NOT_FOUND; | |
} | |
if (*Lun != 0) { | |
return EFI_NOT_FOUND; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Resets a SCSI channel. This operation resets all the SCSI devices connected to the SCSI channel. | |
@param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. | |
@retval EFI_SUCCESS The SCSI channel was reset. | |
@retval EFI_DEVICE_ERROR A device error occurred while attempting to reset the SCSI channel. | |
@retval EFI_TIMEOUT A timeout occurred while attempting to reset the SCSI channel. | |
@retval EFI_UNSUPPORTED The SCSI channel does not support a channel reset operation. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
ExtScsiPassThruResetChannel ( | |
IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This | |
) | |
{ | |
// | |
// Return success directly then upper layer driver could think reset channel operation is done. | |
// | |
return EFI_SUCCESS; | |
} | |
/** | |
Resets a SCSI logical unit that is connected to a SCSI channel. | |
@param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. | |
@param Target The Target is an array of size TARGET_MAX_BYTE and it represents the | |
target port ID of the SCSI device containing the SCSI logical unit to | |
reset. Transport drivers may chose to utilize a subset of this array to suit | |
the representation of their targets. | |
@param Lun The LUN of the SCSI device to reset. | |
@retval EFI_SUCCESS The SCSI device specified by Target and Lun was reset. | |
@retval EFI_INVALID_PARAMETER Target or Lun is NULL. | |
@retval EFI_TIMEOUT A timeout occurred while attempting to reset the SCSI device | |
specified by Target and Lun. | |
@retval EFI_UNSUPPORTED The SCSI channel does not support a target reset operation. | |
@retval EFI_DEVICE_ERROR A device error occurred while attempting to reset the SCSI device | |
specified by Target and Lun. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
ExtScsiPassThruResetTargetLun ( | |
IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, | |
IN UINT8 *Target, | |
IN UINT64 Lun | |
) | |
{ | |
ATA_ATAPI_PASS_THRU_INSTANCE *Instance; | |
LIST_ENTRY *Node; | |
UINT8 Port; | |
UINT8 PortMultiplier; | |
Instance = EXT_SCSI_PASS_THRU_PRIVATE_DATA_FROM_THIS (This); | |
// | |
// For ATAPI device, doesn't support multiple LUN device. | |
// | |
if (Lun != 0) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// The layout of Target array: | |
// ________________________________________________________________________ | |
// | Byte 0 | Byte 1 | ... | TARGET_MAX_BYTES - 1 | | |
// |_____________________|_____________________|_____|______________________| | |
// | | The port multiplier | | | | |
// | The port number | port number | N/A | N/A | | |
// |_____________________|_____________________|_____|______________________| | |
// | |
// For ATAPI device, 2 bytes is enough to represent the location of SCSI device. | |
// | |
Port = Target[0]; | |
PortMultiplier = Target[1]; | |
Node = SearchDeviceInfoList (Instance, Port, PortMultiplier, EfiIdeCdrom); | |
if (Node == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Return success directly then upper layer driver could think reset target LUN operation is done. | |
// | |
return EFI_SUCCESS; | |
} | |
/** | |
Used to retrieve the list of legal Target IDs for SCSI devices on a SCSI channel. These can either | |
be the list SCSI devices that are actually present on the SCSI channel, or the list of legal Target IDs | |
for the SCSI channel. Regardless, the caller of this function must probe the Target ID returned to | |
see if a SCSI device is actually present at that location on the SCSI channel. | |
@param This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance. | |
@param Target (TARGET_MAX_BYTES) of a SCSI device present on the SCSI channel. | |
On output, a pointer to the Target ID (an array of | |
TARGET_MAX_BYTES) of the next SCSI device present on a SCSI | |
channel. An input value of 0xF(all bytes in the array are 0xF) in the | |
Target array retrieves the Target ID of the first SCSI device present on a | |
SCSI channel. | |
@retval EFI_SUCCESS The Target ID of the next SCSI device on the SCSI | |
channel was returned in Target. | |
@retval EFI_INVALID_PARAMETER Target or Lun is NULL. | |
@retval EFI_TIMEOUT Target array is not all 0xF, and Target was not | |
returned on a previous call to GetNextTarget(). | |
@retval EFI_NOT_FOUND There are no more SCSI devices on this SCSI channel. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
ExtScsiPassThruGetNextTarget ( | |
IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, | |
IN OUT UINT8 **Target | |
) | |
{ | |
ATA_ATAPI_PASS_THRU_INSTANCE *Instance; | |
LIST_ENTRY *Node; | |
EFI_ATA_DEVICE_INFO *DeviceInfo; | |
UINT8 *Target8; | |
UINT16 *Target16; | |
Instance = EXT_SCSI_PASS_THRU_PRIVATE_DATA_FROM_THIS (This); | |
if ((Target == NULL) || (*Target == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Target8 = *Target; | |
Target16 = (UINT16 *)*Target; | |
if (CompareMem (Target8, mScsiId, TARGET_MAX_BYTES) != 0) { | |
// | |
// For ATAPI device, we use 2 least significant bytes to represent the location of SCSI device. | |
// So the higher bytes in Target array should be 0xFF. | |
// | |
if (CompareMem (&Target8[2], &mScsiId[2], TARGET_MAX_BYTES - 2) != 0) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// When Target is not all 0xFF's, compare 2 least significant bytes with | |
// previous target id to see if it is returned by previous call. | |
// | |
if (*Target16 != Instance->PreviousTargetId) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Traverse the whole device list to find the next cdrom closed to | |
// the device signified by Target[0] and Target[1]. | |
// | |
// Note that we here use a tricky way to find the next cdrom : | |
// All ata devices are detected and inserted into the device list | |
// sequentially. | |
// | |
Node = GetFirstNode (&Instance->DeviceList); | |
while (!IsNull (&Instance->DeviceList, Node)) { | |
DeviceInfo = ATA_ATAPI_DEVICE_INFO_FROM_THIS (Node); | |
if ((DeviceInfo->Type == EfiIdeCdrom) && | |
((Target8[0] < DeviceInfo->Port) || | |
((Target8[0] == DeviceInfo->Port) && | |
(Target8[1] < (UINT8)DeviceInfo->PortMultiplier)))) | |
{ | |
Target8[0] = (UINT8)DeviceInfo->Port; | |
Target8[1] = (UINT8)DeviceInfo->PortMultiplier; | |
goto Exit; | |
} | |
Node = GetNextNode (&Instance->DeviceList, Node); | |
} | |
return EFI_NOT_FOUND; | |
} else { | |
// | |
// If the array is all 0xFF's, start to traverse the device list from the beginning | |
// | |
Node = GetFirstNode (&Instance->DeviceList); | |
while (!IsNull (&Instance->DeviceList, Node)) { | |
DeviceInfo = ATA_ATAPI_DEVICE_INFO_FROM_THIS (Node); | |
if (DeviceInfo->Type == EfiIdeCdrom) { | |
Target8[0] = (UINT8)DeviceInfo->Port; | |
Target8[1] = (UINT8)DeviceInfo->PortMultiplier; | |
goto Exit; | |
} | |
Node = GetNextNode (&Instance->DeviceList, Node); | |
} | |
return EFI_NOT_FOUND; | |
} | |
Exit: | |
// | |
// Update the PreviousTargetId. | |
// | |
Instance->PreviousTargetId = *Target16; | |
return EFI_SUCCESS; | |
} |