| /** @file | |
| Copyright (c) 2006, Intel Corporation | |
| All rights reserved. This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include "AtapiPassThru.h" | |
| /// | |
| /// IDE registers' fixed address | |
| /// | |
| static IDE_BASE_REGISTERS gAtapiIoPortRegisters[2] = { | |
| { 0x1f0, { 0x1f1 }, 0x1f2, 0x1f3, 0x1f4, 0x1f5, 0x1f6, { 0x1f7 }, { 0x3f6 }, 0x3f7, 0 }, | |
| { 0x170, { 0x171 }, 0x172, 0x173, 0x174, 0x175, 0x176, { 0x177 }, { 0x376 }, 0x377, 0 } | |
| }; | |
| static SCSI_COMMAND_SET gEndTable = { 0xff, (DATA_DIRECTION) 0xff }; | |
| /// | |
| /// This table contains all the supported ATAPI commands. | |
| /// | |
| static SCSI_COMMAND_SET gSupportedATAPICommands[] = { | |
| { OP_INQUIRY, DataIn }, | |
| { OP_LOAD_UNLOAD_CD, NoData }, | |
| { OP_MECHANISM_STATUS, DataIn }, | |
| { OP_MODE_SELECT_10, DataOut }, | |
| { OP_MODE_SENSE_10, DataIn }, | |
| { OP_PAUSE_RESUME, NoData }, | |
| { OP_PLAY_AUDIO_10, DataIn }, | |
| { OP_PLAY_AUDIO_MSF, DataIn }, | |
| { OP_PLAY_CD, DataIn }, | |
| { OP_PLAY_CD_MSF, DataIn }, | |
| { OP_PREVENT_ALLOW_MEDIUM_REMOVAL,NoData }, | |
| { OP_READ_10, DataIn }, | |
| { OP_READ_12, DataIn }, | |
| { OP_READ_CAPACITY, DataIn }, | |
| { OP_READ_CD, DataIn }, | |
| { OP_READ_CD_MSF, DataIn }, | |
| { OP_READ_HEADER, DataIn }, | |
| { OP_READ_SUB_CHANNEL, DataIn }, | |
| { OP_READ_TOC, DataIn }, | |
| { OP_REQUEST_SENSE, DataIn }, | |
| { OP_SCAN, NoData }, | |
| { OP_SEEK_10, NoData }, | |
| { OP_SET_CD_SPEED, DataOut }, | |
| { OP_STOPPLAY_SCAN, NoData }, | |
| { OP_START_STOP_UNIT, NoData }, | |
| { OP_TEST_UNIT_READY, NoData }, | |
| { OP_FORMAT_UNIT, DataOut }, | |
| { OP_READ_FORMAT_CAPACITIES, DataIn }, | |
| { OP_VERIFY, DataOut }, | |
| { OP_WRITE_10, DataOut }, | |
| { OP_WRITE_12, DataOut }, | |
| { OP_WRITE_AND_VERIFY, DataOut }, | |
| { 0xff, (DATA_DIRECTION) 0xff } | |
| }; | |
| static CHAR16 *gControllerNameString = (CHAR16 *) L"ATAPI Controller"; | |
| static CHAR16 *gAtapiChannelString = (CHAR16 *) L"ATAPI Channel"; | |
| EFI_DRIVER_BINDING_PROTOCOL gAtapiScsiPassThruDriverBinding = { | |
| AtapiScsiPassThruDriverBindingSupported, | |
| AtapiScsiPassThruDriverBindingStart, | |
| AtapiScsiPassThruDriverBindingStop, | |
| 0xa, | |
| NULL, | |
| NULL | |
| }; | |
| /** | |
| Supported. | |
| (Standard DriverBinding Protocol Supported() function) | |
| @return EFI_STATUS | |
| @todo This - add argument and description to function comment | |
| @todo Controller - add argument and description to function comment | |
| @todo RemainingDevicePath - add argument and description to function comment | |
| @todo EFI_UNSUPPORTED - add return value to function comment | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| AtapiScsiPassThruDriverBindingSupported ( | |
| IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
| IN EFI_HANDLE Controller, | |
| IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_PCI_IO_PROTOCOL *PciIo; | |
| PCI_TYPE00 Pci; | |
| // | |
| // Open the IO Abstraction(s) needed to perform the supported test | |
| // | |
| Status = gBS->OpenProtocol ( | |
| Controller, | |
| &gEfiPciIoProtocolGuid, | |
| (VOID **) &PciIo, | |
| This->DriverBindingHandle, | |
| Controller, | |
| EFI_OPEN_PROTOCOL_BY_DRIVER | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // Use the PCI I/O Protocol to see if Controller is a IDE Controller that | |
| // can be managed by this driver. Read the PCI Configuration Header | |
| // for this device. | |
| // | |
| Status = PciIo->Pci.Read ( | |
| PciIo, | |
| EfiPciIoWidthUint32, | |
| 0, | |
| sizeof (Pci) / sizeof (UINT32), | |
| &Pci | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| gBS->CloseProtocol ( | |
| Controller, | |
| &gEfiPciIoProtocolGuid, | |
| This->DriverBindingHandle, | |
| Controller | |
| ); | |
| return EFI_UNSUPPORTED; | |
| } | |
| if (Pci.Hdr.ClassCode[2] != PCI_CLASS_MASS_STORAGE || Pci.Hdr.ClassCode[1] != PCI_CLASS_IDE) { | |
| Status = EFI_UNSUPPORTED; | |
| } | |
| gBS->CloseProtocol ( | |
| Controller, | |
| &gEfiPciIoProtocolGuid, | |
| This->DriverBindingHandle, | |
| Controller | |
| ); | |
| return Status; | |
| } | |
| /** | |
| Create handles for IDE channels specified by RemainingDevicePath. | |
| Install SCSI Pass Thru Protocol onto each created handle. | |
| (Standard DriverBinding Protocol Start() function) | |
| @return EFI_STATUS | |
| @todo This - add argument and description to function comment | |
| @todo Controller - add argument and description to function comment | |
| @todo RemainingDevicePath - add argument and description to function comment | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| AtapiScsiPassThruDriverBindingStart ( | |
| IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
| IN EFI_HANDLE Controller, | |
| IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_PCI_IO_PROTOCOL *PciIo; | |
| PciIo = NULL; | |
| Status = gBS->OpenProtocol ( | |
| Controller, | |
| &gEfiPciIoProtocolGuid, | |
| (VOID **) &PciIo, | |
| This->DriverBindingHandle, | |
| Controller, | |
| EFI_OPEN_PROTOCOL_BY_DRIVER | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| Status = PciIo->Attributes ( | |
| PciIo, | |
| EfiPciIoAttributeOperationEnable, | |
| EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO | EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO | EFI_PCI_DEVICE_ENABLE, | |
| NULL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| goto Done; | |
| } | |
| // | |
| // Create SCSI Pass Thru instance for the IDE channel. | |
| // | |
| Status = RegisterAtapiScsiPassThru (This, Controller, PciIo); | |
| Done: | |
| if (EFI_ERROR (Status)) { | |
| if (PciIo) { | |
| PciIo->Attributes ( | |
| PciIo, | |
| EfiPciIoAttributeOperationDisable, | |
| EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO | EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO | EFI_PCI_DEVICE_ENABLE, | |
| NULL | |
| ); | |
| } | |
| gBS->CloseProtocol ( | |
| Controller, | |
| &gEfiPciIoProtocolGuid, | |
| This->DriverBindingHandle, | |
| Controller | |
| ); | |
| } | |
| return Status; | |
| } | |
| /** | |
| Stop. | |
| (Standard DriverBinding Protocol Stop() function) | |
| @return EFI_STATUS | |
| @todo This - add argument and description to function comment | |
| @todo Controller - add argument and description to function comment | |
| @todo NumberOfChildren - add argument and description to function comment | |
| @todo ChildHandleBuffer - add argument and description to function comment | |
| @todo EFI_SUCCESS - add return value to function comment | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| AtapiScsiPassThruDriverBindingStop ( | |
| IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
| IN EFI_HANDLE Controller, | |
| IN UINTN NumberOfChildren, | |
| IN EFI_HANDLE *ChildHandleBuffer | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_SCSI_PASS_THRU_PROTOCOL *ScsiPassThru; | |
| ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; | |
| Status = gBS->OpenProtocol ( | |
| Controller, | |
| &gEfiScsiPassThruProtocolGuid, | |
| (VOID **) &ScsiPassThru, | |
| This->DriverBindingHandle, | |
| Controller, | |
| EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (ScsiPassThru); | |
| Status = gBS->UninstallProtocolInterface ( | |
| Controller, | |
| &gEfiScsiPassThruProtocolGuid, | |
| &AtapiScsiPrivate->ScsiPassThru | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // Release Pci Io protocol on the controller handle. | |
| // | |
| AtapiScsiPrivate->PciIo->Attributes ( | |
| AtapiScsiPrivate->PciIo, | |
| EfiPciIoAttributeOperationDisable, | |
| EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO | EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO | EFI_PCI_DEVICE_ENABLE, | |
| NULL | |
| ); | |
| gBS->CloseProtocol ( | |
| Controller, | |
| &gEfiPciIoProtocolGuid, | |
| This->DriverBindingHandle, | |
| Controller | |
| ); | |
| gBS->FreePool (AtapiScsiPrivate); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Attaches SCSI Pass Thru Protocol for specified IDE channel. | |
| @param Controller: Parent device handle to the IDE channel. | |
| @param PciIo: PCI I/O protocol attached on the "Controller". | |
| @return EFI_SUCCESS Always returned unless installing SCSI Pass Thru Protocol failed. | |
| @todo This - add argument and description to function comment | |
| @todo Controller - add argument and description to function comment | |
| @todo PciIo - add argument and description to function comment | |
| @todo EFI_OUT_OF_RESOURCES - add return value to function comment | |
| **/ | |
| EFI_STATUS | |
| RegisterAtapiScsiPassThru ( | |
| IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
| IN EFI_HANDLE Controller, | |
| IN EFI_PCI_IO_PROTOCOL *PciIo | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; | |
| UINT64 Attributes; | |
| AtapiScsiPrivate = AllocateZeroPool (sizeof (ATAPI_SCSI_PASS_THRU_DEV)); | |
| if (AtapiScsiPrivate == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| Attributes = EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO | EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO | EFI_PCI_DEVICE_ENABLE; | |
| CopyMem (AtapiScsiPrivate->ChannelName, gAtapiChannelString, sizeof (gAtapiChannelString)); | |
| // | |
| // Enable channel | |
| // | |
| PciIo->Attributes (PciIo, EfiPciIoAttributeOperationSet, Attributes, NULL); | |
| AtapiScsiPrivate->Signature = ATAPI_SCSI_PASS_THRU_DEV_SIGNATURE; | |
| AtapiScsiPrivate->Handle = Controller; | |
| // | |
| // will reset the IoPort inside each API function. | |
| // | |
| AtapiScsiPrivate->IoPort = gAtapiIoPortRegisters; | |
| AtapiScsiPrivate->PciIo = PciIo; | |
| // | |
| // initialize SCSI Pass Thru Protocol interface | |
| // | |
| AtapiScsiPrivate->ScsiPassThru.Mode = &AtapiScsiPrivate->ScsiPassThruMode; | |
| AtapiScsiPrivate->ScsiPassThru.PassThru = AtapiScsiPassThruFunction; | |
| AtapiScsiPrivate->ScsiPassThru.GetNextDevice = AtapiScsiPassThruGetNextDevice; | |
| AtapiScsiPrivate->ScsiPassThru.BuildDevicePath = AtapiScsiPassThruBuildDevicePath; | |
| AtapiScsiPrivate->ScsiPassThru.GetTargetLun = AtapiScsiPassThruGetTargetLun; | |
| AtapiScsiPrivate->ScsiPassThru.ResetChannel = AtapiScsiPassThruResetChannel; | |
| AtapiScsiPrivate->ScsiPassThru.ResetTarget = AtapiScsiPassThruResetTarget; | |
| // | |
| // Set Mode | |
| // | |
| CopyMem (AtapiScsiPrivate->ControllerName, gControllerNameString, sizeof (gControllerNameString)); | |
| AtapiScsiPrivate->ScsiPassThruMode.ControllerName = AtapiScsiPrivate->ControllerName; | |
| AtapiScsiPrivate->ScsiPassThruMode.ChannelName = AtapiScsiPrivate->ChannelName; | |
| AtapiScsiPrivate->ScsiPassThruMode.AdapterId = 4; | |
| // | |
| // non-RAID SCSI controllers should set both physical and logical attributes | |
| // | |
| AtapiScsiPrivate->ScsiPassThruMode.Attributes = EFI_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | | |
| EFI_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL; | |
| AtapiScsiPrivate->ScsiPassThruMode.IoAlign = 0; | |
| // | |
| // Initialize the LatestTargetId to 0xFFFFFFFF (for the GetNextDevice() call). | |
| // | |
| AtapiScsiPrivate->LatestTargetId = 0xFFFFFFFF; | |
| AtapiScsiPrivate->LatestLun = 0; | |
| Status = gBS->InstallProtocolInterface ( | |
| &Controller, | |
| &gEfiScsiPassThruProtocolGuid, | |
| EFI_NATIVE_INTERFACE, | |
| &AtapiScsiPrivate->ScsiPassThru | |
| ); | |
| return Status; | |
| } | |
| /** | |
| Implements EFI_SCSI_PASS_THRU_PROTOCOL.PassThru() function. | |
| @param This The EFI_SCSI_PASS_THRU_PROTOCOL instance. | |
| @param Target The Target ID of the ATAPI device to send the SCSI | |
| Request Packet. To ATAPI devices attached on an IDE | |
| Channel, Target ID 0 indicates Master device;Target | |
| ID 1 indicates Slave device. | |
| @param Lun The LUN of the ATAPI device to send the SCSI Request | |
| Packet. To the ATAPI device, Lun is always 0. | |
| @param Packet The SCSI Request Packet to send to the ATAPI device | |
| specified by Target and Lun. | |
| @param Event If non-blocking I/O is not supported then Event is ignored, | |
| and blocking I/O is performed.<br> | |
| If Event is NULL, then blocking I/O is performed.<br> | |
| If Event is not NULL and non blocking I/O is supported, | |
| then non-blocking I/O is performed, and Event will be signaled | |
| when the SCSI Request Packet completes. | |
| @todo This - add argument and description to function comment | |
| @todo EFI_INVALID_PARAMETER - add return value to function comment | |
| @todo EFI_SUCCESS - add return value to function comment | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| AtapiScsiPassThruFunction ( | |
| IN EFI_SCSI_PASS_THRU_PROTOCOL *This, | |
| IN UINT32 Target, | |
| IN UINT64 Lun, | |
| IN OUT EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, | |
| IN EFI_EVENT Event OPTIONAL | |
| ) | |
| { | |
| ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; | |
| EFI_STATUS Status; | |
| AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (This); | |
| // | |
| // Target is not allowed beyond MAX_TARGET_ID | |
| // | |
| if (Target > MAX_TARGET_ID) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // check the data fields in Packet parameter. | |
| // | |
| Status = CheckSCSIRequestPacket (Packet); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // If Request Packet targets at the IDE channel itself, | |
| // do nothing. | |
| // | |
| if (Target == This->Mode->AdapterId) { | |
| Packet->TransferLength = 0; | |
| return EFI_SUCCESS; | |
| } | |
| // | |
| // According to Target ID, reset the Atapi I/O Register mapping | |
| // (Target Id in [0,1] area, using gAtapiIoPortRegisters[0], | |
| // Target Id in [2,3] area, using gAtapiIoPortRegisters[1] | |
| // | |
| if ((Target / 2) == 0) { | |
| AtapiScsiPrivate->IoPort = &gAtapiIoPortRegisters[0]; | |
| } else { | |
| AtapiScsiPrivate->IoPort = &gAtapiIoPortRegisters[1]; | |
| } | |
| // | |
| // the ATAPI SCSI interface does not support non-blocking I/O | |
| // ignore the Event parameter | |
| // | |
| // Performs blocking I/O. | |
| // | |
| Status = SubmitBlockingIoCommand (AtapiScsiPrivate, Target, Packet); | |
| return Status; | |
| } | |
| /** | |
| Used to retrieve the list of legal Target IDs for SCSI devices | |
| on a SCSI channel. | |
| @param This Protocol instance pointer. | |
| @param Target On input, a pointer to the Target ID of a SCSI | |
| device present on the SCSI channel. On output, | |
| a pointer to the Target ID of the next SCSI device | |
| present on a SCSI channel. An input value of | |
| 0xFFFFFFFF retrieves the Target ID of the first | |
| SCSI device present on a SCSI channel. | |
| @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_NOT_FOUND There are no more SCSI devices on this SCSI channel. | |
| @retval EFI_INVALID_PARAMETER Target is not 0xFFFFFFFF,and Target and Lun were not | |
| returned on a previous call to GetNextDevice(). | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| AtapiScsiPassThruGetNextDevice ( | |
| IN EFI_SCSI_PASS_THRU_PROTOCOL *This, | |
| IN OUT UINT32 *Target, | |
| IN OUT UINT64 *Lun | |
| ) | |
| { | |
| ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; | |
| // | |
| // Retrieve Device Private Data Structure. | |
| // | |
| AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (This); | |
| // | |
| // Check whether Target is valid. | |
| // | |
| if (Target == NULL || Lun == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| if ((*Target != 0xFFFFFFFF) && | |
| ((*Target != AtapiScsiPrivate->LatestTargetId) || | |
| (*Lun != AtapiScsiPrivate->LatestLun))) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| if (*Target == MAX_TARGET_ID) { | |
| return EFI_NOT_FOUND; | |
| } | |
| if (*Target == 0xFFFFFFFF) { | |
| *Target = 0; | |
| } else { | |
| *Target = AtapiScsiPrivate->LatestTargetId + 1; | |
| } | |
| *Lun = 0; | |
| // | |
| // Update the LatestTargetId. | |
| // | |
| AtapiScsiPrivate->LatestTargetId = *Target; | |
| AtapiScsiPrivate->LatestLun = *Lun; | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Used to allocate and build a device path node for a SCSI device | |
| on a SCSI channel. Would not build device path for a SCSI Host Controller. | |
| @param This Protocol instance pointer. | |
| @param Target The Target ID of the SCSI device for which | |
| a device path node is to be allocated and built. | |
| @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_NOT_FOUND The SCSI devices specified by Target and Lun does | |
| not exist on the SCSI channel. | |
| @retval EFI_INVALID_PARAMETER DevicePath is NULL. | |
| @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate | |
| DevicePath. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| AtapiScsiPassThruBuildDevicePath ( | |
| IN EFI_SCSI_PASS_THRU_PROTOCOL *This, | |
| IN UINT32 Target, | |
| IN UINT64 Lun, | |
| IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath | |
| ) | |
| { | |
| EFI_DEV_PATH *Node; | |
| // | |
| // Validate parameters passed in. | |
| // | |
| if (DevicePath == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // can not build device path for the SCSI Host Controller. | |
| // | |
| if ((Target > (MAX_TARGET_ID - 1)) || (Lun != 0)) { | |
| return EFI_NOT_FOUND; | |
| } | |
| Node = AllocateZeroPool (sizeof (EFI_DEV_PATH)); | |
| if (Node == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| Node->DevPath.Type = MESSAGING_DEVICE_PATH; | |
| Node->DevPath.SubType = MSG_ATAPI_DP; | |
| SetDevicePathNodeLength (&Node->DevPath, sizeof (ATAPI_DEVICE_PATH)); | |
| Node->Atapi.PrimarySecondary = (UINT8) (Target / 2); | |
| Node->Atapi.SlaveMaster = (UINT8) (Target % 2); | |
| Node->Atapi.Lun = (UINT16) Lun; | |
| *DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) Node; | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Used to translate a device path node to a Target ID and LUN. | |
| @param This Protocol instance pointer. | |
| @param DevicePath A pointer to the device path node that | |
| describes a SCSI device on the SCSI channel. | |
| @param Target A pointer to the Target 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 is NULL. | |
| @retval EFI_INVALID_PARAMETER Target is NULL. | |
| @retval EFI_INVALID_PARAMETER Lun 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 | |
| Target ID and LUN does not exist. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| AtapiScsiPassThruGetTargetLun ( | |
| IN EFI_SCSI_PASS_THRU_PROTOCOL *This, | |
| IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, | |
| OUT UINT32 *Target, | |
| OUT UINT64 *Lun | |
| ) | |
| { | |
| EFI_DEV_PATH *Node; | |
| // | |
| // Validate parameters passed in. | |
| // | |
| if (DevicePath == NULL || Target == NULL || Lun == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // Check whether the DevicePath belongs to SCSI_DEVICE_PATH | |
| // | |
| if ((DevicePath->Type != MESSAGING_DEVICE_PATH) || | |
| (DevicePath->SubType != MSG_ATAPI_DP) || | |
| (DevicePathNodeLength(DevicePath) != sizeof(ATAPI_DEVICE_PATH))) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| Node = (EFI_DEV_PATH *) DevicePath; | |
| *Target = Node->Atapi.PrimarySecondary * 2 + Node->Atapi.SlaveMaster; | |
| *Lun = Node->Atapi.Lun; | |
| if (*Target > (MAX_TARGET_ID - 1) || *Lun != 0) { | |
| return EFI_NOT_FOUND; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Resets a SCSI channel.This operation resets all the | |
| SCSI devices connected to the SCSI channel. | |
| @param This Protocol instance pointer. | |
| @retval EFI_SUCCESS The SCSI channel was reset. | |
| @retval EFI_UNSUPPORTED The SCSI channel does not support | |
| a channel reset operation. | |
| @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. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| AtapiScsiPassThruResetChannel ( | |
| IN EFI_SCSI_PASS_THRU_PROTOCOL *This | |
| ) | |
| { | |
| UINT8 DeviceControlValue; | |
| ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; | |
| UINT8 Index; | |
| AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (This); | |
| // | |
| // Reset both Primary channel and Secondary channel. | |
| // so, the IoPort pointer must point to the right I/O Register group | |
| // | |
| for (Index = 0; Index < 2; Index++) { | |
| // | |
| // Reset | |
| // | |
| AtapiScsiPrivate->IoPort = &gAtapiIoPortRegisters[Index]; | |
| DeviceControlValue = 0; | |
| // | |
| // set SRST bit to initiate soft reset | |
| // | |
| DeviceControlValue |= SRST; | |
| // | |
| // disable Interrupt | |
| // | |
| DeviceControlValue |= bit (1); | |
| WritePortB ( | |
| AtapiScsiPrivate->PciIo, | |
| AtapiScsiPrivate->IoPort->Alt.DeviceControl, | |
| DeviceControlValue | |
| ); | |
| // | |
| // Wait 10us | |
| // | |
| gBS->Stall (10); | |
| // | |
| // Clear SRST bit | |
| // 0xfb:1111,1011 | |
| // | |
| DeviceControlValue &= 0xfb; | |
| WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Alt.DeviceControl, DeviceControlValue); | |
| // | |
| // slave device needs at most 31s to clear BSY | |
| // | |
| if (StatusWaitForBSYClear (AtapiScsiPrivate, 31000) == EFI_TIMEOUT) { | |
| return EFI_DEVICE_ERROR; | |
| } | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Resets a SCSI device that is connected to a SCSI channel. | |
| @param This Protocol instance pointer. | |
| @param Target The Target ID of the SCSI device to reset. | |
| @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_UNSUPPORTED The SCSI channel does not support a target | |
| reset operation. | |
| @retval EFI_INVALID_PARAMETER Target or Lun are invalid. | |
| @retval EFI_DEVICE_ERROR A device error occurred while attempting | |
| to reset the SCSI device specified by Target | |
| and Lun. | |
| @retval EFI_TIMEOUT A timeout occurred while attempting to reset | |
| the SCSI device specified by Target and Lun. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| AtapiScsiPassThruResetTarget ( | |
| IN EFI_SCSI_PASS_THRU_PROTOCOL *This, | |
| IN UINT32 Target, | |
| IN UINT64 Lun | |
| ) | |
| { | |
| ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate; | |
| UINT8 Command; | |
| UINT8 DeviceSelect; | |
| AtapiScsiPrivate = ATAPI_SCSI_PASS_THRU_DEV_FROM_THIS (This); | |
| if (Target > MAX_TARGET_ID) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // Directly return EFI_SUCCESS if want to reset the host controller | |
| // | |
| if (Target == This->Mode->AdapterId) { | |
| return EFI_SUCCESS; | |
| } | |
| // | |
| // According to Target ID, reset the Atapi I/O Register mapping | |
| // (Target Id in [0,1] area, using gAtapiIoPortRegisters[0], | |
| // Target Id in [2,3] area, using gAtapiIoPortRegisters[1] | |
| // | |
| if ((Target / 2) == 0) { | |
| AtapiScsiPrivate->IoPort = &gAtapiIoPortRegisters[0]; | |
| } else { | |
| AtapiScsiPrivate->IoPort = &gAtapiIoPortRegisters[1]; | |
| } | |
| // | |
| // for ATAPI device, no need to wait DRDY ready after device selecting. | |
| // | |
| // bit7 and bit5 are both set to 1 for backward compatibility | |
| // | |
| DeviceSelect = (UINT8) (((bit (7) | bit (5)) | (Target << 4))); | |
| WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Head, DeviceSelect); | |
| Command = ATAPI_SOFT_RESET_CMD; | |
| WritePortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Reg.Command, Command); | |
| // | |
| // BSY clear is the only status return to the host by the device | |
| // when reset is complete. | |
| // slave device needs at most 31s to clear BSY | |
| // | |
| if (EFI_ERROR (StatusWaitForBSYClear (AtapiScsiPrivate, 31000))) { | |
| return EFI_DEVICE_ERROR; | |
| } | |
| // | |
| // stall 5 seconds to make the device status stable | |
| // | |
| gBS->Stall (5000000); | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Checks the parameters in the SCSI Request Packet to make sure | |
| they are valid for a SCSI Pass Thru request. | |
| @todo function comment is missing 'Routine Description:' | |
| @todo function comment is missing 'Arguments:' | |
| @todo function comment is missing 'Returns:' | |
| @todo Packet - add argument and description to function comment | |
| @todo EFI_INVALID_PARAMETER - add return value to function comment | |
| @todo EFI_INVALID_PARAMETER - add return value to function comment | |
| @todo EFI_INVALID_PARAMETER - add return value to function comment | |
| @todo EFI_UNSUPPORTED - add return value to function comment | |
| @todo EFI_SUCCESS - add return value to function comment | |
| **/ | |
| EFI_STATUS | |
| CheckSCSIRequestPacket ( | |
| EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet | |
| ) | |
| { | |
| if (Packet == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| if (!ValidCdbLength (Packet->CdbLength)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| if (Packet->Cdb == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // Checks whether the request command is supported. | |
| // | |
| if (!IsCommandValid (Packet)) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Checks the requested SCSI command: | |
| Is it supported by this driver? | |
| Is the Data transfer direction reasonable? | |
| @todo function comment is missing 'Routine Description:' | |
| @todo function comment is missing 'Arguments:' | |
| @todo function comment is missing 'Returns:' | |
| @todo Packet - add argument and description to function comment | |
| **/ | |
| BOOLEAN | |
| IsCommandValid ( | |
| EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet | |
| ) | |
| { | |
| UINT8 Index; | |
| UINT8 *OpCode; | |
| OpCode = (UINT8 *) (Packet->Cdb); | |
| for (Index = 0; CompareMem (&gSupportedATAPICommands[Index], &gEndTable, sizeof (SCSI_COMMAND_SET)); Index++) { | |
| if (*OpCode == gSupportedATAPICommands[Index].OpCode) { | |
| // | |
| // Check whether the requested Command is supported by this driver | |
| // | |
| if (Packet->DataDirection == DataIn) { | |
| // | |
| // Check whether the requested data direction conforms to | |
| // what it should be. | |
| // | |
| if (gSupportedATAPICommands[Index].Direction == DataOut) { | |
| return FALSE; | |
| } | |
| } | |
| if (Packet->DataDirection == DataOut) { | |
| // | |
| // Check whether the requested data direction conforms to | |
| // what it should be. | |
| // | |
| if (gSupportedATAPICommands[Index].Direction == DataIn) { | |
| return FALSE; | |
| } | |
| } | |
| return TRUE; | |
| } | |
| } | |
| return FALSE; | |
| } | |
| /** | |
| Performs blocking I/O request. | |
| @param AtapiScsiPrivate Private data structure for the specified channel. | |
| @param Target The Target ID of the ATAPI device to send the SCSI | |
| Request Packet. To ATAPI devices attached on an IDE | |
| Channel, Target ID 0 indicates Master device;Target | |
| ID 1 indicates Slave device. | |
| @param Packet The SCSI Request Packet to send to the ATAPI device | |
| specified by Target. | |
| @todo AtapiScsiPrivate - add argument and description to function comment | |
| **/ | |
| EFI_STATUS | |
| SubmitBlockingIoCommand ( | |
| ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, | |
| UINT32 Target, | |
| EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet | |
| ) | |
| { | |
| UINT8 PacketCommand[12]; | |
| UINT64 TimeoutInMicroSeconds; | |
| EFI_STATUS PacketCommandStatus; | |
| // | |
| // Fill ATAPI Command Packet according to CDB | |
| // | |
| ZeroMem (&PacketCommand, 12); | |
| CopyMem (&PacketCommand, Packet->Cdb, Packet->CdbLength); | |
| // | |
| // Timeout is 100ns unit, convert it to 1000ns (1us) unit. | |
| // | |
| TimeoutInMicroSeconds = DivU64x32 (Packet->Timeout, (UINT32) 10); | |
| // | |
| // Submit ATAPI Command Packet | |
| // | |
| PacketCommandStatus = AtapiPacketCommand ( | |
| AtapiScsiPrivate, | |
| Target, | |
| PacketCommand, | |
| Packet->DataBuffer, | |
| &(Packet->TransferLength), | |
| (DATA_DIRECTION) Packet->DataDirection, | |
| TimeoutInMicroSeconds | |
| ); | |
| if (!EFI_ERROR (PacketCommandStatus) || (Packet->SenseData == NULL)) { | |
| Packet->SenseDataLength = 0; | |
| return PacketCommandStatus; | |
| } | |
| // | |
| // Return SenseData if PacketCommandStatus matches | |
| // the following return codes. | |
| // | |
| if ((PacketCommandStatus == EFI_WARN_BUFFER_TOO_SMALL) || | |
| (PacketCommandStatus == EFI_DEVICE_ERROR) || | |
| (PacketCommandStatus == EFI_TIMEOUT)) { | |
| // | |
| // avoid submit request sense command continuously. | |
| // | |
| if (PacketCommand[0] == OP_REQUEST_SENSE) { | |
| Packet->SenseDataLength = 0; | |
| return PacketCommandStatus; | |
| } | |
| RequestSenseCommand ( | |
| AtapiScsiPrivate, | |
| Target, | |
| Packet->Timeout, | |
| Packet->SenseData, | |
| &Packet->SenseDataLength | |
| ); | |
| } | |
| return PacketCommandStatus; | |
| } | |
| /** | |
| RequestSenseCommand | |
| @param AtapiScsiPrivate | |
| @param Target | |
| @param Timeout | |
| @param SenseData | |
| @param SenseDataLength | |
| @todo Add function description | |
| @todo AtapiScsiPrivate TODO: add argument description | |
| @todo Target TODO: add argument description | |
| @todo Timeout TODO: add argument description | |
| @todo SenseData TODO: add argument description | |
| @todo SenseDataLength TODO: add argument description | |
| @todo add return values | |
| **/ | |
| EFI_STATUS | |
| RequestSenseCommand ( | |
| ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, | |
| UINT32 Target, | |
| UINT64 Timeout, | |
| VOID *SenseData, | |
| UINT8 *SenseDataLength | |
| ) | |
| { | |
| EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET Packet; | |
| UINT8 Cdb[12]; | |
| EFI_STATUS Status; | |
| ZeroMem (&Packet, sizeof (EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET)); | |
| ZeroMem (Cdb, 12); | |
| Cdb[0] = OP_REQUEST_SENSE; | |
| Cdb[4] = (UINT8) (*SenseDataLength); | |
| Packet.Timeout = Timeout; | |
| Packet.DataBuffer = SenseData; | |
| Packet.SenseData = NULL; | |
| Packet.Cdb = Cdb; | |
| Packet.TransferLength = *SenseDataLength; | |
| Packet.CdbLength = 12; | |
| Packet.DataDirection = DataIn; | |
| Status = SubmitBlockingIoCommand (AtapiScsiPrivate, Target, &Packet); | |
| *SenseDataLength = (UINT8) (Packet.TransferLength); | |
| return Status; | |
| } | |
| /** | |
| Submits ATAPI command packet to the specified ATAPI device. | |
| @param AtapiScsiPrivate: Private data structure for the specified channel. | |
| @param Target: The Target ID of the ATAPI device to send the SCSI | |
| Request Packet. To ATAPI devices attached on an IDE | |
| Channel, Target ID 0 indicates Master device;Target | |
| ID 1 indicates Slave device. | |
| @param PacketCommand: Points to the ATAPI command packet. | |
| @param Buffer: Points to the transferred data. | |
| @param ByteCount: When input,indicates the buffer size; when output, | |
| indicates the actually transferred data size. | |
| @param Direction: Indicates the data transfer direction. | |
| @param TimeoutInMicroSeconds: The timeout, in micro second units, | |
| to use for the execution of this ATAPI command. | |
| A TimeoutInMicroSeconds value of 0 means that | |
| this function will wait indefinitely for the ATAPI | |
| command to execute. | |
| <P> | |
| If TimeoutInMicroSeconds is greater than zero, then | |
| this function will return EFI_TIMEOUT if the time | |
| required to execute the ATAPI command is greater | |
| than TimeoutInMicroSeconds. | |
| </P> | |
| @todo AtapiScsiPrivate - add argument and description to function comment | |
| @todo PacketCommand - add argument and description to function comment | |
| @todo Buffer - add argument and description to function comment | |
| @todo ByteCount - add argument and description to function comment | |
| @todo Direction - add argument and description to function comment | |
| **/ | |
| EFI_STATUS | |
| AtapiPacketCommand ( | |
| ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, | |
| UINT32 Target, | |
| UINT8 *PacketCommand, | |
| VOID *Buffer, | |
| UINT32 *ByteCount, | |
| DATA_DIRECTION Direction, | |
| UINT64 TimeoutInMicroSeconds | |
| ) | |
| { | |
| UINT16 *CommandIndex; | |
| UINT8 Count; | |
| EFI_STATUS Status; | |
| // | |
| // Set all the command parameters by fill related registers. | |
| // Before write to all the following registers, BSY and DRQ must be 0. | |
| // | |
| Status = StatusDRQClear (AtapiScsiPrivate, TimeoutInMicroSeconds); | |
| if (EFI_ERROR (Status)) { | |
| if (Status == EFI_ABORTED) { | |
| Status = EFI_DEVICE_ERROR; | |
| } | |
| *ByteCount = 0; | |
| return Status; | |
| } | |
| // | |
| // Select device via Device/Head Register. | |
| // "Target = 0" indicates device 0; "Target = 1" indicates device 1 | |
| // | |
| WritePortB ( | |
| AtapiScsiPrivate->PciIo, | |
| AtapiScsiPrivate->IoPort->Head, | |
| (UINT8) ((Target << 4) | DEFAULT_CMD) // DEFAULT_CMD: 0xa0 (1010,0000) | |
| ); | |
| // | |
| // No OVL; No DMA (by setting feature register) | |
| // | |
| WritePortB ( | |
| AtapiScsiPrivate->PciIo, | |
| AtapiScsiPrivate->IoPort->Reg1.Feature, | |
| 0x00 | |
| ); | |
| // | |
| // set the transfersize to MAX_ATAPI_BYTE_COUNT to let the device | |
| // determine how much data should be transfered. | |
| // | |
| WritePortB ( | |
| AtapiScsiPrivate->PciIo, | |
| AtapiScsiPrivate->IoPort->CylinderLsb, | |
| (UINT8) (MAX_ATAPI_BYTE_COUNT & 0x00ff) | |
| ); | |
| WritePortB ( | |
| AtapiScsiPrivate->PciIo, | |
| AtapiScsiPrivate->IoPort->CylinderMsb, | |
| (UINT8) (MAX_ATAPI_BYTE_COUNT >> 8) | |
| ); | |
| // | |
| // DEFAULT_CTL:0x0a (0000,1010) | |
| // Disable interrupt | |
| // | |
| WritePortB ( | |
| AtapiScsiPrivate->PciIo, | |
| AtapiScsiPrivate->IoPort->Alt.DeviceControl, | |
| DEFAULT_CTL | |
| ); | |
| // | |
| // Send Packet command to inform device | |
| // that the following data bytes are command packet. | |
| // | |
| WritePortB ( | |
| AtapiScsiPrivate->PciIo, | |
| AtapiScsiPrivate->IoPort->Reg.Command, | |
| PACKET_CMD | |
| ); | |
| // | |
| // Before data transfer, BSY should be 0 and DRQ should be 1. | |
| // if they are not in specified time frame, | |
| // retrieve Sense Key from Error Register before return. | |
| // | |
| Status = StatusDRQReady (AtapiScsiPrivate, TimeoutInMicroSeconds); | |
| if (EFI_ERROR (Status)) { | |
| if (Status == EFI_ABORTED) { | |
| Status = EFI_DEVICE_ERROR; | |
| } | |
| *ByteCount = 0; | |
| return Status; | |
| } | |
| // | |
| // Send out command packet | |
| // | |
| CommandIndex = (UINT16 *) PacketCommand; | |
| for (Count = 0; Count < 6; Count++, CommandIndex++) { | |
| WritePortW (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Data, *CommandIndex); | |
| } | |
| // | |
| // call AtapiPassThruPioReadWriteData() function to get | |
| // requested transfer data form device. | |
| // | |
| return AtapiPassThruPioReadWriteData ( | |
| AtapiScsiPrivate, | |
| Buffer, | |
| ByteCount, | |
| Direction, | |
| TimeoutInMicroSeconds | |
| ); | |
| } | |
| /** | |
| Performs data transfer between ATAPI device and host after the | |
| ATAPI command packet is sent. | |
| @param AtapiScsiPrivate: Private data structure for the specified channel. | |
| @param Buffer: Points to the transferred data. | |
| @param ByteCount: When input,indicates the buffer size; when output, | |
| indicates the actually transferred data size. | |
| @param Direction: Indicates the data transfer direction. | |
| @param TimeoutInMicroSeconds: The timeout, in micro second units, | |
| to use for the execution of this ATAPI command. | |
| A TimeoutInMicroSeconds value of 0 means that | |
| this function will wait indefinitely for the ATAPI | |
| command to execute. | |
| <P> | |
| If TimeoutInMicroSeconds is greater than zero, then | |
| this function will return EFI_TIMEOUT if the time | |
| required to execute the ATAPI command is greater | |
| than TimeoutInMicroSeconds. | |
| </P> | |
| @todo AtapiScsiPrivate - add argument and description to function comment | |
| @todo Buffer - add argument and description to function comment | |
| @todo ByteCount - add argument and description to function comment | |
| @todo Direction - add argument and description to function comment | |
| @todo EFI_DEVICE_ERROR - add return value to function comment | |
| @todo EFI_DEVICE_ERROR - add return value to function comment | |
| @todo EFI_WARN_BUFFER_TOO_SMALL - add return value to function comment | |
| **/ | |
| EFI_STATUS | |
| AtapiPassThruPioReadWriteData ( | |
| ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, | |
| UINT16 *Buffer, | |
| UINT32 *ByteCount, | |
| DATA_DIRECTION Direction, | |
| UINT64 TimeoutInMicroSeconds | |
| ) | |
| { | |
| UINT32 Index; | |
| UINT32 RequiredWordCount; | |
| UINT32 ActualWordCount; | |
| UINT32 WordCount; | |
| EFI_STATUS Status; | |
| UINT16 *ptrBuffer; | |
| Status = EFI_SUCCESS; | |
| // | |
| // Non Data transfer request is also supported. | |
| // | |
| if (*ByteCount == 0 || Buffer == NULL) { | |
| *ByteCount = 0; | |
| if (EFI_ERROR (StatusWaitForBSYClear (AtapiScsiPrivate, TimeoutInMicroSeconds))) { | |
| return EFI_DEVICE_ERROR; | |
| } | |
| } | |
| ptrBuffer = Buffer; | |
| RequiredWordCount = *ByteCount / 2; | |
| // | |
| // ActuralWordCount means the word count of data really transfered. | |
| // | |
| ActualWordCount = 0; | |
| while (ActualWordCount < RequiredWordCount) { | |
| // | |
| // before each data transfer stream, the host should poll DRQ bit ready, | |
| // which indicates device's ready for data transfer . | |
| // | |
| Status = StatusDRQReady (AtapiScsiPrivate, TimeoutInMicroSeconds); | |
| if (EFI_ERROR (Status)) { | |
| *ByteCount = ActualWordCount * 2; | |
| AtapiPassThruCheckErrorStatus (AtapiScsiPrivate); | |
| if (ActualWordCount == 0) { | |
| return EFI_DEVICE_ERROR; | |
| } | |
| // | |
| // ActualWordCount > 0 | |
| // | |
| if (ActualWordCount < RequiredWordCount) { | |
| return EFI_WARN_BUFFER_TOO_SMALL; | |
| } | |
| } | |
| // | |
| // get current data transfer size from Cylinder Registers. | |
| // | |
| WordCount = ReadPortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->CylinderMsb) << 8; | |
| WordCount = WordCount | ReadPortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->CylinderLsb); | |
| WordCount = WordCount & 0xffff; | |
| WordCount /= 2; | |
| // | |
| // perform a series data In/Out. | |
| // | |
| for (Index = 0; (Index < WordCount) && (ActualWordCount < RequiredWordCount); Index++, ActualWordCount++) { | |
| if (Direction == DataIn) { | |
| *ptrBuffer = ReadPortW (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Data); | |
| } else { | |
| WritePortW (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Data, *ptrBuffer); | |
| } | |
| ptrBuffer++; | |
| } | |
| } | |
| // | |
| // After data transfer is completed, normally, DRQ bit should clear. | |
| // | |
| StatusDRQClear (AtapiScsiPrivate, TimeoutInMicroSeconds); | |
| // | |
| // read status register to check whether error happens. | |
| // | |
| Status = AtapiPassThruCheckErrorStatus (AtapiScsiPrivate); | |
| *ByteCount = ActualWordCount * 2; | |
| return Status; | |
| } | |
| /** | |
| Read one byte from a specified I/O port. | |
| @todo function comment is missing 'Routine Description:' | |
| @todo function comment is missing 'Arguments:' | |
| @todo function comment is missing 'Returns:' | |
| @todo PciIo - add argument and description to function comment | |
| @todo Port - add argument and description to function comment | |
| **/ | |
| UINT8 | |
| ReadPortB ( | |
| IN EFI_PCI_IO_PROTOCOL *PciIo, | |
| IN UINT16 Port | |
| ) | |
| { | |
| UINT8 Data; | |
| Data = 0; | |
| PciIo->Io.Read ( | |
| PciIo, | |
| EfiPciIoWidthUint8, | |
| EFI_PCI_IO_PASS_THROUGH_BAR, | |
| (UINT64) Port, | |
| 1, | |
| &Data | |
| ); | |
| return Data; | |
| } | |
| /** | |
| Read one word from a specified I/O port. | |
| @todo function comment is missing 'Routine Description:' | |
| @todo function comment is missing 'Arguments:' | |
| @todo function comment is missing 'Returns:' | |
| @todo PciIo - add argument and description to function comment | |
| @todo Port - add argument and description to function comment | |
| **/ | |
| UINT16 | |
| ReadPortW ( | |
| IN EFI_PCI_IO_PROTOCOL *PciIo, | |
| IN UINT16 Port | |
| ) | |
| { | |
| UINT16 Data; | |
| Data = 0; | |
| PciIo->Io.Read ( | |
| PciIo, | |
| EfiPciIoWidthUint16, | |
| EFI_PCI_IO_PASS_THROUGH_BAR, | |
| (UINT64) Port, | |
| 1, | |
| &Data | |
| ); | |
| return Data; | |
| } | |
| /** | |
| Write one byte to a specified I/O port. | |
| @todo function comment is missing 'Routine Description:' | |
| @todo function comment is missing 'Arguments:' | |
| @todo function comment is missing 'Returns:' | |
| @todo PciIo - add argument and description to function comment | |
| @todo Port - add argument and description to function comment | |
| @todo Data - add argument and description to function comment | |
| **/ | |
| VOID | |
| WritePortB ( | |
| IN EFI_PCI_IO_PROTOCOL *PciIo, | |
| IN UINT16 Port, | |
| IN UINT8 Data | |
| ) | |
| { | |
| PciIo->Io.Write ( | |
| PciIo, | |
| EfiPciIoWidthUint8, | |
| EFI_PCI_IO_PASS_THROUGH_BAR, | |
| (UINT64) Port, | |
| 1, | |
| &Data | |
| ); | |
| } | |
| /** | |
| Write one word to a specified I/O port. | |
| @todo function comment is missing 'Routine Description:' | |
| @todo function comment is missing 'Arguments:' | |
| @todo function comment is missing 'Returns:' | |
| @todo PciIo - add argument and description to function comment | |
| @todo Port - add argument and description to function comment | |
| @todo Data - add argument and description to function comment | |
| **/ | |
| VOID | |
| WritePortW ( | |
| IN EFI_PCI_IO_PROTOCOL *PciIo, | |
| IN UINT16 Port, | |
| IN UINT16 Data | |
| ) | |
| { | |
| PciIo->Io.Write ( | |
| PciIo, | |
| EfiPciIoWidthUint16, | |
| EFI_PCI_IO_PASS_THROUGH_BAR, | |
| (UINT64) Port, | |
| 1, | |
| &Data | |
| ); | |
| } | |
| /** | |
| Check whether DRQ is clear in the Status Register. (BSY must also be cleared) | |
| If TimeoutInMicroSeconds is zero, this routine should wait infinitely for | |
| DRQ clear. Otherwise, it will return EFI_TIMEOUT when specified time is | |
| elapsed. | |
| @todo function comment is missing 'Routine Description:' | |
| @todo function comment is missing 'Arguments:' | |
| @todo function comment is missing 'Returns:' | |
| @todo AtapiScsiPrivate - add argument and description to function comment | |
| @todo TimeoutInMicroSeconds - add argument and description to function comment | |
| @todo EFI_ABORTED - add return value to function comment | |
| @todo EFI_TIMEOUT - add return value to function comment | |
| @todo EFI_SUCCESS - add return value to function comment | |
| **/ | |
| EFI_STATUS | |
| StatusDRQClear ( | |
| ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, | |
| UINT64 TimeoutInMicroSeconds | |
| ) | |
| { | |
| UINT64 Delay; | |
| UINT8 StatusRegister; | |
| UINT8 ErrRegister; | |
| if (TimeoutInMicroSeconds == 0) { | |
| Delay = 2; | |
| } else { | |
| Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1; | |
| } | |
| do { | |
| StatusRegister = ReadPortB ( | |
| AtapiScsiPrivate->PciIo, | |
| AtapiScsiPrivate->IoPort->Reg.Status | |
| ); | |
| // | |
| // wait for BSY == 0 and DRQ == 0 | |
| // | |
| if ((StatusRegister & (DRQ | BSY)) == 0) { | |
| break; | |
| } | |
| // | |
| // check whether the command is aborted by the device | |
| // | |
| if ((StatusRegister & (BSY | ERR)) == ERR) { | |
| ErrRegister = ReadPortB ( | |
| AtapiScsiPrivate->PciIo, | |
| AtapiScsiPrivate->IoPort->Reg1.Error | |
| ); | |
| if ((ErrRegister & ABRT_ERR) == ABRT_ERR) { | |
| return EFI_ABORTED; | |
| } | |
| } | |
| // | |
| // Stall for 30 us | |
| // | |
| gBS->Stall (30); | |
| // | |
| // Loop infinitely if not meeting expected condition | |
| // | |
| if (TimeoutInMicroSeconds == 0) { | |
| Delay = 2; | |
| } | |
| Delay--; | |
| } while (Delay); | |
| if (Delay == 0) { | |
| return EFI_TIMEOUT; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Check whether DRQ is clear in the Alternate Status Register. | |
| (BSY must also be cleared). | |
| If TimeoutInMicroSeconds is zero, this routine should wait infinitely for | |
| DRQ clear. Otherwise, it will return EFI_TIMEOUT when specified time is | |
| elapsed. | |
| @todo function comment is missing 'Routine Description:' | |
| @todo function comment is missing 'Arguments:' | |
| @todo function comment is missing 'Returns:' | |
| @todo AtapiScsiPrivate - add argument and description to function comment | |
| @todo TimeoutInMicroSeconds - add argument and description to function comment | |
| @todo EFI_ABORTED - add return value to function comment | |
| @todo EFI_TIMEOUT - add return value to function comment | |
| @todo EFI_SUCCESS - add return value to function comment | |
| **/ | |
| EFI_STATUS | |
| AltStatusDRQClear ( | |
| ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, | |
| UINT64 TimeoutInMicroSeconds | |
| ) | |
| { | |
| UINT64 Delay; | |
| UINT8 AltStatusRegister; | |
| UINT8 ErrRegister; | |
| if (TimeoutInMicroSeconds == 0) { | |
| Delay = 2; | |
| } else { | |
| Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1; | |
| } | |
| do { | |
| AltStatusRegister = ReadPortB ( | |
| AtapiScsiPrivate->PciIo, | |
| AtapiScsiPrivate->IoPort->Alt.AltStatus | |
| ); | |
| // | |
| // wait for BSY == 0 and DRQ == 0 | |
| // | |
| if ((AltStatusRegister & (DRQ | BSY)) == 0) { | |
| break; | |
| } | |
| if ((AltStatusRegister & (BSY | ERR)) == ERR) { | |
| ErrRegister = ReadPortB ( | |
| AtapiScsiPrivate->PciIo, | |
| AtapiScsiPrivate->IoPort->Reg1.Error | |
| ); | |
| if ((ErrRegister & ABRT_ERR) == ABRT_ERR) { | |
| return EFI_ABORTED; | |
| } | |
| } | |
| // | |
| // Stall for 30 us | |
| // | |
| gBS->Stall (30); | |
| // | |
| // Loop infinitely if not meeting expected condition | |
| // | |
| if (TimeoutInMicroSeconds == 0) { | |
| Delay = 2; | |
| } | |
| Delay--; | |
| } while (Delay); | |
| if (Delay == 0) { | |
| return EFI_TIMEOUT; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Check whether DRQ is ready in the Status Register. (BSY must also be cleared) | |
| If TimeoutInMicroSeconds is zero, this routine should wait infinitely for | |
| DRQ ready. Otherwise, it will return EFI_TIMEOUT when specified time is | |
| elapsed. | |
| @todo function comment is missing 'Routine Description:' | |
| @todo function comment is missing 'Arguments:' | |
| @todo function comment is missing 'Returns:' | |
| @todo AtapiScsiPrivate - add argument and description to function comment | |
| @todo TimeoutInMicroSeconds - add argument and description to function comment | |
| @todo EFI_ABORTED - add return value to function comment | |
| @todo EFI_TIMEOUT - add return value to function comment | |
| @todo EFI_SUCCESS - add return value to function comment | |
| **/ | |
| EFI_STATUS | |
| StatusDRQReady ( | |
| ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, | |
| UINT64 TimeoutInMicroSeconds | |
| ) | |
| { | |
| UINT64 Delay; | |
| UINT8 StatusRegister; | |
| UINT8 ErrRegister; | |
| if (TimeoutInMicroSeconds == 0) { | |
| Delay = 2; | |
| } else { | |
| Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1; | |
| } | |
| do { | |
| // | |
| // read Status Register will clear interrupt | |
| // | |
| StatusRegister = ReadPortB ( | |
| AtapiScsiPrivate->PciIo, | |
| AtapiScsiPrivate->IoPort->Reg.Status | |
| ); | |
| // | |
| // BSY==0,DRQ==1 | |
| // | |
| if ((StatusRegister & (BSY | DRQ)) == DRQ) { | |
| break; | |
| } | |
| if ((StatusRegister & (BSY | ERR)) == ERR) { | |
| ErrRegister = ReadPortB ( | |
| AtapiScsiPrivate->PciIo, | |
| AtapiScsiPrivate->IoPort->Reg1.Error | |
| ); | |
| if ((ErrRegister & ABRT_ERR) == ABRT_ERR) { | |
| return EFI_ABORTED; | |
| } | |
| } | |
| // | |
| // Stall for 30 us | |
| // | |
| gBS->Stall (30); | |
| // | |
| // Loop infinitely if not meeting expected condition | |
| // | |
| if (TimeoutInMicroSeconds == 0) { | |
| Delay = 2; | |
| } | |
| Delay--; | |
| } while (Delay); | |
| if (Delay == 0) { | |
| return EFI_TIMEOUT; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Check whether DRQ is ready in the Alternate Status Register. | |
| (BSY must also be cleared) | |
| If TimeoutInMicroSeconds is zero, this routine should wait infinitely for | |
| DRQ ready. Otherwise, it will return EFI_TIMEOUT when specified time is | |
| elapsed. | |
| @todo function comment is missing 'Routine Description:' | |
| @todo function comment is missing 'Arguments:' | |
| @todo function comment is missing 'Returns:' | |
| @todo AtapiScsiPrivate - add argument and description to function comment | |
| @todo TimeoutInMicroSeconds - add argument and description to function comment | |
| @todo EFI_ABORTED - add return value to function comment | |
| @todo EFI_TIMEOUT - add return value to function comment | |
| @todo EFI_SUCCESS - add return value to function comment | |
| **/ | |
| EFI_STATUS | |
| AltStatusDRQReady ( | |
| ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, | |
| UINT64 TimeoutInMicroSeconds | |
| ) | |
| { | |
| UINT64 Delay; | |
| UINT8 AltStatusRegister; | |
| UINT8 ErrRegister; | |
| if (TimeoutInMicroSeconds == 0) { | |
| Delay = 2; | |
| } else { | |
| Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1; | |
| } | |
| do { | |
| // | |
| // read Status Register will clear interrupt | |
| // | |
| AltStatusRegister = ReadPortB ( | |
| AtapiScsiPrivate->PciIo, | |
| AtapiScsiPrivate->IoPort->Alt.AltStatus | |
| ); | |
| // | |
| // BSY==0,DRQ==1 | |
| // | |
| if ((AltStatusRegister & (BSY | DRQ)) == DRQ) { | |
| break; | |
| } | |
| if ((AltStatusRegister & (BSY | ERR)) == ERR) { | |
| ErrRegister = ReadPortB ( | |
| AtapiScsiPrivate->PciIo, | |
| AtapiScsiPrivate->IoPort->Reg1.Error | |
| ); | |
| if ((ErrRegister & ABRT_ERR) == ABRT_ERR) { | |
| return EFI_ABORTED; | |
| } | |
| } | |
| // | |
| // Stall for 30 us | |
| // | |
| gBS->Stall (30); | |
| // | |
| // Loop infinitely if not meeting expected condition | |
| // | |
| if (TimeoutInMicroSeconds == 0) { | |
| Delay = 2; | |
| } | |
| Delay--; | |
| } while (Delay); | |
| if (Delay == 0) { | |
| return EFI_TIMEOUT; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Check whether BSY is clear in the Status Register. | |
| If TimeoutInMicroSeconds is zero, this routine should wait infinitely for | |
| BSY clear. Otherwise, it will return EFI_TIMEOUT when specified time is | |
| elapsed. | |
| @todo function comment is missing 'Routine Description:' | |
| @todo function comment is missing 'Arguments:' | |
| @todo function comment is missing 'Returns:' | |
| @todo AtapiScsiPrivate - add argument and description to function comment | |
| @todo TimeoutInMicroSeconds - add argument and description to function comment | |
| @todo EFI_TIMEOUT - add return value to function comment | |
| @todo EFI_SUCCESS - add return value to function comment | |
| **/ | |
| EFI_STATUS | |
| StatusWaitForBSYClear ( | |
| ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, | |
| UINT64 TimeoutInMicroSeconds | |
| ) | |
| { | |
| UINT64 Delay; | |
| UINT8 StatusRegister; | |
| if (TimeoutInMicroSeconds == 0) { | |
| Delay = 2; | |
| } else { | |
| Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1; | |
| } | |
| do { | |
| StatusRegister = ReadPortB ( | |
| AtapiScsiPrivate->PciIo, | |
| AtapiScsiPrivate->IoPort->Reg.Status | |
| ); | |
| if ((StatusRegister & BSY) == 0x00) { | |
| break; | |
| } | |
| // | |
| // Stall for 30 us | |
| // | |
| gBS->Stall (30); | |
| // | |
| // Loop infinitely if not meeting expected condition | |
| // | |
| if (TimeoutInMicroSeconds == 0) { | |
| Delay = 2; | |
| } | |
| Delay--; | |
| } while (Delay); | |
| if (Delay == 0) { | |
| return EFI_TIMEOUT; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Check whether BSY is clear in the Alternate Status Register. | |
| If TimeoutInMicroSeconds is zero, this routine should wait infinitely for | |
| BSY clear. Otherwise, it will return EFI_TIMEOUT when specified time is | |
| elapsed. | |
| @todo function comment is missing 'Routine Description:' | |
| @todo function comment is missing 'Arguments:' | |
| @todo function comment is missing 'Returns:' | |
| @todo AtapiScsiPrivate - add argument and description to function comment | |
| @todo TimeoutInMicroSeconds - add argument and description to function comment | |
| @todo EFI_TIMEOUT - add return value to function comment | |
| @todo EFI_SUCCESS - add return value to function comment | |
| **/ | |
| EFI_STATUS | |
| AltStatusWaitForBSYClear ( | |
| ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, | |
| UINT64 TimeoutInMicroSeconds | |
| ) | |
| { | |
| UINT64 Delay; | |
| UINT8 AltStatusRegister; | |
| if (TimeoutInMicroSeconds == 0) { | |
| Delay = 2; | |
| } else { | |
| Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1; | |
| } | |
| do { | |
| AltStatusRegister = ReadPortB ( | |
| AtapiScsiPrivate->PciIo, | |
| AtapiScsiPrivate->IoPort->Alt.AltStatus | |
| ); | |
| if ((AltStatusRegister & BSY) == 0x00) { | |
| break; | |
| } | |
| // | |
| // Stall for 30 us | |
| // | |
| gBS->Stall (30); | |
| // | |
| // Loop infinitely if not meeting expected condition | |
| // | |
| if (TimeoutInMicroSeconds == 0) { | |
| Delay = 2; | |
| } | |
| Delay--; | |
| } while (Delay); | |
| if (Delay == 0) { | |
| return EFI_TIMEOUT; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Check whether DRDY is ready in the Status Register. | |
| (BSY must also be cleared) | |
| If TimeoutInMicroSeconds is zero, this routine should wait infinitely for | |
| DRDY ready. Otherwise, it will return EFI_TIMEOUT when specified time is | |
| elapsed. | |
| @todo function comment is missing 'Routine Description:' | |
| @todo function comment is missing 'Arguments:' | |
| @todo function comment is missing 'Returns:' | |
| @todo AtapiScsiPrivate - add argument and description to function comment | |
| @todo TimeoutInMicroSeconds - add argument and description to function comment | |
| @todo EFI_ABORTED - add return value to function comment | |
| @todo EFI_TIMEOUT - add return value to function comment | |
| @todo EFI_SUCCESS - add return value to function comment | |
| **/ | |
| EFI_STATUS | |
| StatusDRDYReady ( | |
| ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, | |
| UINT64 TimeoutInMicroSeconds | |
| ) | |
| { | |
| UINT64 Delay; | |
| UINT8 StatusRegister; | |
| UINT8 ErrRegister; | |
| if (TimeoutInMicroSeconds == 0) { | |
| Delay = 2; | |
| } else { | |
| Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1; | |
| } | |
| do { | |
| StatusRegister = ReadPortB ( | |
| AtapiScsiPrivate->PciIo, | |
| AtapiScsiPrivate->IoPort->Reg.Status | |
| ); | |
| // | |
| // BSY == 0 , DRDY == 1 | |
| // | |
| if ((StatusRegister & (DRDY | BSY)) == DRDY) { | |
| break; | |
| } | |
| if ((StatusRegister & (BSY | ERR)) == ERR) { | |
| ErrRegister = ReadPortB ( | |
| AtapiScsiPrivate->PciIo, | |
| AtapiScsiPrivate->IoPort->Reg1.Error | |
| ); | |
| if ((ErrRegister & ABRT_ERR) == ABRT_ERR) { | |
| return EFI_ABORTED; | |
| } | |
| } | |
| // | |
| // Stall for 30 us | |
| // | |
| gBS->Stall (30); | |
| // | |
| // Loop infinitely if not meeting expected condition | |
| // | |
| if (TimeoutInMicroSeconds == 0) { | |
| Delay = 2; | |
| } | |
| Delay--; | |
| } while (Delay); | |
| if (Delay == 0) { | |
| return EFI_TIMEOUT; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Check whether DRDY is ready in the Alternate Status Register. | |
| (BSY must also be cleared) | |
| If TimeoutInMicroSeconds is zero, this routine should wait infinitely for | |
| DRDY ready. Otherwise, it will return EFI_TIMEOUT when specified time is | |
| elapsed. | |
| @todo function comment is missing 'Routine Description:' | |
| @todo function comment is missing 'Arguments:' | |
| @todo function comment is missing 'Returns:' | |
| @todo AtapiScsiPrivate - add argument and description to function comment | |
| @todo TimeoutInMicroSeconds - add argument and description to function comment | |
| @todo EFI_ABORTED - add return value to function comment | |
| @todo EFI_TIMEOUT - add return value to function comment | |
| @todo EFI_SUCCESS - add return value to function comment | |
| **/ | |
| EFI_STATUS | |
| AltStatusDRDYReady ( | |
| ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate, | |
| UINT64 TimeoutInMicroSeconds | |
| ) | |
| { | |
| UINT64 Delay; | |
| UINT8 AltStatusRegister; | |
| UINT8 ErrRegister; | |
| if (TimeoutInMicroSeconds == 0) { | |
| Delay = 2; | |
| } else { | |
| Delay = DivU64x32 (TimeoutInMicroSeconds, (UINT32) 30) + 1; | |
| } | |
| do { | |
| AltStatusRegister = ReadPortB ( | |
| AtapiScsiPrivate->PciIo, | |
| AtapiScsiPrivate->IoPort->Alt.AltStatus | |
| ); | |
| // | |
| // BSY == 0 , DRDY == 1 | |
| // | |
| if ((AltStatusRegister & (DRDY | BSY)) == DRDY) { | |
| break; | |
| } | |
| if ((AltStatusRegister & (BSY | ERR)) == ERR) { | |
| ErrRegister = ReadPortB ( | |
| AtapiScsiPrivate->PciIo, | |
| AtapiScsiPrivate->IoPort->Reg1.Error | |
| ); | |
| if ((ErrRegister & ABRT_ERR) == ABRT_ERR) { | |
| return EFI_ABORTED; | |
| } | |
| } | |
| // | |
| // Stall for 30 us | |
| // | |
| gBS->Stall (30); | |
| // | |
| // Loop infinitely if not meeting expected condition | |
| // | |
| if (TimeoutInMicroSeconds == 0) { | |
| Delay = 2; | |
| } | |
| Delay--; | |
| } while (Delay); | |
| if (Delay == 0) { | |
| return EFI_TIMEOUT; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Check Error Register for Error Information. | |
| @todo function comment is missing 'Routine Description:' | |
| @todo function comment is missing 'Arguments:' | |
| @todo function comment is missing 'Returns:' | |
| @todo AtapiScsiPrivate - add argument and description to function comment | |
| @todo EFI_SUCCESS - add return value to function comment | |
| @todo EFI_DEVICE_ERROR - add return value to function comment | |
| **/ | |
| EFI_STATUS | |
| AtapiPassThruCheckErrorStatus ( | |
| ATAPI_SCSI_PASS_THRU_DEV *AtapiScsiPrivate | |
| ) | |
| { | |
| UINT8 StatusRegister; | |
| UINT8 ErrorRegister; | |
| StatusRegister = ReadPortB ( | |
| AtapiScsiPrivate->PciIo, | |
| AtapiScsiPrivate->IoPort->Reg.Status | |
| ); | |
| DEBUG_CODE_BEGIN (); | |
| if (StatusRegister & DWF) { | |
| DEBUG ( | |
| (EFI_D_BLKIO, | |
| "AtapiPassThruCheckErrorStatus()-- %02x : Error : Write Fault\n", | |
| StatusRegister) | |
| ); | |
| } | |
| if (StatusRegister & CORR) { | |
| DEBUG ( | |
| (EFI_D_BLKIO, | |
| "AtapiPassThruCheckErrorStatus()-- %02x : Error : Corrected Data\n", | |
| StatusRegister) | |
| ); | |
| } | |
| if (StatusRegister & ERR) { | |
| ErrorRegister = ReadPortB (AtapiScsiPrivate->PciIo, AtapiScsiPrivate->IoPort->Reg1.Error); | |
| if (ErrorRegister & BBK_ERR) { | |
| DEBUG ( | |
| (EFI_D_BLKIO, | |
| "AtapiPassThruCheckErrorStatus()-- %02x : Error : Bad Block Detected\n", | |
| ErrorRegister) | |
| ); | |
| } | |
| if (ErrorRegister & UNC_ERR) { | |
| DEBUG ( | |
| (EFI_D_BLKIO, | |
| "AtapiPassThruCheckErrorStatus()-- %02x : Error : Uncorrectable Data\n", | |
| ErrorRegister) | |
| ); | |
| } | |
| if (ErrorRegister & MC_ERR) { | |
| DEBUG ( | |
| (EFI_D_BLKIO, | |
| "AtapiPassThruCheckErrorStatus()-- %02x : Error : Media Change\n", | |
| ErrorRegister) | |
| ); | |
| } | |
| if (ErrorRegister & ABRT_ERR) { | |
| DEBUG ( | |
| (EFI_D_BLKIO, | |
| "AtapiPassThruCheckErrorStatus()-- %02x : Error : Abort\n", | |
| ErrorRegister) | |
| ); | |
| } | |
| if (ErrorRegister & TK0NF_ERR) { | |
| DEBUG ( | |
| (EFI_D_BLKIO, | |
| "AtapiPassThruCheckErrorStatus()-- %02x : Error : Track 0 Not Found\n", | |
| ErrorRegister) | |
| ); | |
| } | |
| if (ErrorRegister & AMNF_ERR) { | |
| DEBUG ( | |
| (EFI_D_BLKIO, | |
| "AtapiPassThruCheckErrorStatus()-- %02x : Error : Address Mark Not Found\n", | |
| ErrorRegister) | |
| ); | |
| } | |
| } | |
| DEBUG_CODE_END (); | |
| if ((StatusRegister & (ERR | DWF | CORR)) == 0) { | |
| return EFI_SUCCESS; | |
| } | |
| return EFI_DEVICE_ERROR; | |
| } |