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