mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-30 12:43:41 +01:00
7c0aa811ec
Signed-off-by: Sergey Isakov <isakov-sl@bk.ru>
1970 lines
54 KiB
C
1970 lines
54 KiB
C
/* $Id: Atapi.c 33540 2010-10-28 09:27:05Z vboxsync $ */
|
|
/** @file
|
|
* Atapi.c
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2009-2010 Oracle Corporation
|
|
*
|
|
* This file is part of VirtualBox Open Source Edition (OSE), as
|
|
* available from http://www.virtualbox.org. This file is free software;
|
|
* you can redistribute it and/or modify it under the terms of the GNU
|
|
* General Public License (GPL) as published by the Free Software
|
|
* Foundation, in version 2 as it comes in the "COPYING" file of the
|
|
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
|
|
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
|
|
*/
|
|
|
|
/** @file
|
|
This file contains all helper functions on the ATAPI command
|
|
|
|
Copyright (c) 2006 - 2008, 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 "IdeBus.h"
|
|
|
|
/**
|
|
This function is used to get the current status of the media residing
|
|
in the LS-120 drive or ZIP drive. The media status is returned in the
|
|
Error Status.
|
|
|
|
@param IdeDev pointer pointing to IDE_BLK_IO_DEV data structure, used
|
|
to record all the information of the IDE device.
|
|
|
|
@retval EFI_SUCCESS The media status is achieved successfully and the media
|
|
can be read/written.
|
|
@retval EFI_DEVICE_ERROR Get Media Status Command is failed.
|
|
@retval EFI_NO_MEDIA There is no media in the drive.
|
|
@retval EFI_WRITE_PROTECTED The media is writing protected.
|
|
|
|
@note This function must be called after the LS120EnableMediaStatus()
|
|
with second parameter set to TRUE
|
|
(means enable media status notification) is called.
|
|
**/
|
|
EFI_STATUS
|
|
LS120GetMediaStatus (
|
|
IN IDE_BLK_IO_DEV *IdeDev
|
|
)
|
|
{
|
|
UINT8 DeviceSelect;
|
|
UINT8 StatusValue;
|
|
EFI_STATUS EfiStatus;
|
|
//
|
|
// Poll Alternate Register for BSY clear within timeout.
|
|
//
|
|
EfiStatus = WaitForBSYClear2 (IdeDev, ATATIMEOUT);
|
|
if (EFI_ERROR (EfiStatus)) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
//
|
|
// Select device via Device/Head Register.
|
|
//
|
|
DeviceSelect = (UINT8) ((IdeDev->Device) << 4 | 0xe0);
|
|
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Head, DeviceSelect);
|
|
|
|
//
|
|
// Poll Alternate Register for DRDY set within timeout.
|
|
// After device is selected, DRDY set indicates the device is ready to
|
|
// accept command.
|
|
//
|
|
EfiStatus = DRDYReady2 (IdeDev, ATATIMEOUT);
|
|
if (EFI_ERROR (EfiStatus)) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
//
|
|
// Get Media Status Command is sent
|
|
//
|
|
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, 0xDA);
|
|
|
|
//
|
|
// BSY bit will clear after command is complete.
|
|
//
|
|
EfiStatus = WaitForBSYClear2 (IdeDev, ATATIMEOUT);
|
|
if (EFI_ERROR (EfiStatus)) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
//
|
|
// the media status is returned by the command in the ERROR register
|
|
//
|
|
StatusValue = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Error);
|
|
|
|
if ((StatusValue & BIT1) != 0) {
|
|
return EFI_NO_MEDIA;
|
|
}
|
|
|
|
if ((StatusValue & BIT6) != 0) {
|
|
return EFI_WRITE_PROTECTED;
|
|
} else {
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
/**
|
|
This function is used to send Enable Media Status Notification Command
|
|
or Disable Media Status Notification Command.
|
|
|
|
@param IdeDev pointer pointing to IDE_BLK_IO_DEV data structure, used
|
|
to record all the information of the IDE device.
|
|
|
|
@param Enable a flag that indicates whether enable or disable media
|
|
status notification.
|
|
@retval EFI_SUCCESS If command completes successfully.
|
|
@retval EFI_DEVICE_ERROR If command failed.
|
|
**/
|
|
EFI_STATUS
|
|
LS120EnableMediaStatus (
|
|
IN IDE_BLK_IO_DEV *IdeDev,
|
|
IN BOOLEAN Enable
|
|
)
|
|
{
|
|
UINT8 DeviceSelect;
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// Poll Alternate Register for BSY clear within timeout.
|
|
//
|
|
Status = WaitForBSYClear2 (IdeDev, ATATIMEOUT);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
//
|
|
// Select device via Device/Head Register.
|
|
//
|
|
DeviceSelect = (UINT8) ((IdeDev->Device) << 4 | 0xe0);
|
|
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Head, DeviceSelect);
|
|
|
|
//
|
|
// Poll Alternate Register for DRDY set within timeout.
|
|
// After device is selected, DRDY set indicates the device is ready to
|
|
// accept command.
|
|
//
|
|
Status = DRDYReady2 (IdeDev, ATATIMEOUT);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
if (Enable) {
|
|
//
|
|
// 0x95: Enable media status notification
|
|
//
|
|
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Feature, 0x95);
|
|
} else {
|
|
//
|
|
// 0x31: Disable media status notification
|
|
//
|
|
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Feature, 0x31);
|
|
}
|
|
//
|
|
// Set Feature Command is sent
|
|
//
|
|
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, 0xEF);
|
|
|
|
//
|
|
// BSY bit will clear after command is complete.
|
|
//
|
|
Status = WaitForBSYClear (IdeDev, ATATIMEOUT);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
/**
|
|
This function reads the pending data in the device.
|
|
|
|
@param IdeDev Indicates the calling context.
|
|
|
|
@retval EFI_SUCCESS Successfully read.
|
|
@retval EFI_NOT_READY The BSY is set avoiding reading.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
AtapiReadPendingData (
|
|
IN IDE_BLK_IO_DEV *IdeDev
|
|
)
|
|
{
|
|
UINT8 AltRegister;
|
|
UINT16 TempWordBuffer;
|
|
|
|
AltRegister = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Alt.AltStatus);
|
|
if ((AltRegister & ATA_STSREG_BSY) == ATA_STSREG_BSY) {
|
|
return EFI_NOT_READY;
|
|
}
|
|
if ((AltRegister & (ATA_STSREG_BSY | ATA_STSREG_DRQ)) == ATA_STSREG_DRQ) {
|
|
TempWordBuffer = IDEReadPortB (IdeDev->PciIo,IdeDev->IoPort->Alt.AltStatus);
|
|
while ((TempWordBuffer & (ATA_STSREG_BSY | ATA_STSREG_DRQ)) == ATA_STSREG_DRQ) {
|
|
IDEReadPortWMultiple (
|
|
IdeDev->PciIo,
|
|
IdeDev->IoPort->Data,
|
|
1,
|
|
&TempWordBuffer
|
|
);
|
|
TempWordBuffer = IDEReadPortB (IdeDev->PciIo,IdeDev->IoPort->Alt.AltStatus);
|
|
}
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
This function is called by either AtapiPacketCommandIn() or AtapiPacketCommandOut().
|
|
It is used to transfer data between host and device. The data direction is specified
|
|
by the fourth parameter.
|
|
|
|
@param IdeDev pointer pointing to IDE_BLK_IO_DEV data structure, used to record
|
|
all the information of the IDE device.
|
|
@param Buffer buffer contained data transferred between host and device.
|
|
@param ByteCount data size in byte unit of the buffer.
|
|
@param Read flag used to determine the data transfer direction.
|
|
Read equals 1, means data transferred from device to host;
|
|
Read equals 0, means data transferred from host to device.
|
|
@param TimeOut timeout value for wait DRQ ready before each data stream's transfer.
|
|
|
|
@retval EFI_SUCCESS data is transferred successfully.
|
|
@retval EFI_DEVICE_ERROR the device failed to transfer data.
|
|
**/
|
|
EFI_STATUS
|
|
PioReadWriteData (
|
|
IN IDE_BLK_IO_DEV *IdeDev,
|
|
IN UINT16 *Buffer,
|
|
IN UINT32 ByteCount,
|
|
IN BOOLEAN Read,
|
|
IN UINTN TimeOut
|
|
)
|
|
{
|
|
//
|
|
// required transfer data in word unit.
|
|
//
|
|
UINT32 RequiredWordCount;
|
|
|
|
//
|
|
// actual transfer data in word unit.
|
|
//
|
|
UINT32 ActualWordCount;
|
|
UINT32 WordCount;
|
|
EFI_STATUS Status;
|
|
UINT16 *PtrBuffer;
|
|
|
|
//
|
|
// No data transfer is permitted.
|
|
//
|
|
if (ByteCount == 0) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
//
|
|
// for performance, we assert the ByteCount is an even number
|
|
// which is actually a reasonable assumption
|
|
ASSERT((ByteCount%2) == 0);
|
|
|
|
PtrBuffer = Buffer;
|
|
RequiredWordCount = ByteCount / 2;
|
|
//
|
|
// ActuralWordCount means the word count of data really transferred.
|
|
//
|
|
ActualWordCount = 0;
|
|
|
|
while (ActualWordCount < RequiredWordCount) {
|
|
|
|
//
|
|
// before each data transfer stream, the host should poll DRQ bit ready,
|
|
// to see whether indicates device is ready to transfer data.
|
|
//
|
|
Status = DRQReady2 (IdeDev, TimeOut);
|
|
if (EFI_ERROR (Status)) {
|
|
return CheckErrorStatus (IdeDev);
|
|
}
|
|
|
|
//
|
|
// read Status Register will clear interrupt
|
|
//
|
|
IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Status);
|
|
|
|
//
|
|
// get current data transfer size from Cylinder Registers.
|
|
//
|
|
WordCount = IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->CylinderMsb) << 8;
|
|
WordCount = WordCount | IDEReadPortB (IdeDev->PciIo, IdeDev->IoPort->CylinderLsb);
|
|
WordCount = WordCount & 0xffff;
|
|
WordCount /= 2;
|
|
|
|
WordCount = MIN (WordCount, (RequiredWordCount - ActualWordCount));
|
|
|
|
if (Read) {
|
|
IDEReadPortWMultiple (
|
|
IdeDev->PciIo,
|
|
IdeDev->IoPort->Data,
|
|
WordCount,
|
|
PtrBuffer
|
|
);
|
|
} else {
|
|
IDEWritePortWMultiple (
|
|
IdeDev->PciIo,
|
|
IdeDev->IoPort->Data,
|
|
WordCount,
|
|
PtrBuffer
|
|
);
|
|
}
|
|
|
|
PtrBuffer += WordCount;
|
|
ActualWordCount += WordCount;
|
|
}
|
|
|
|
if (Read) {
|
|
//
|
|
// In the case where the drive wants to send more data than we need to read,
|
|
// the DRQ bit will be set and cause delays from DRQClear2().
|
|
// We need to read data from the drive until it clears DRQ so we can move on.
|
|
//
|
|
AtapiReadPendingData (IdeDev);
|
|
}
|
|
|
|
//
|
|
// After data transfer is completed, normally, DRQ bit should clear.
|
|
//
|
|
Status = DRQClear2 (IdeDev, ATAPITIMEOUT);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
//
|
|
// read status register to check whether error happens.
|
|
//
|
|
return CheckErrorStatus (IdeDev);
|
|
}
|
|
|
|
/**
|
|
This function is used to send out ATAPI commands conforms to the Packet Command
|
|
with PIO Data In Protocol.
|
|
|
|
@param IdeDev pointer pointing to IDE_BLK_IO_DEV data structure, used
|
|
to record all the information of the IDE device.
|
|
@param Packet pointer pointing to ATAPI_PACKET_COMMAND data structure
|
|
which contains the contents of the command.
|
|
@param Buffer buffer contained data transferred from device to host.
|
|
@param ByteCount data size in byte unit of the buffer.
|
|
@param TimeOut this parameter is used to specify the timeout value for the
|
|
PioReadWriteData() function.
|
|
|
|
@retval EFI_SUCCESS send out the ATAPI packet command successfully
|
|
and device sends data successfully.
|
|
@retval EFI_DEVICE_ERROR the device failed to send data.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
AtapiPacketCommandIn (
|
|
IN IDE_BLK_IO_DEV *IdeDev,
|
|
IN ATAPI_PACKET_COMMAND *Packet,
|
|
IN UINT16 *Buffer,
|
|
IN UINT32 ByteCount,
|
|
IN UINTN TimeOut
|
|
)
|
|
{
|
|
UINT16 *CommandIndex;
|
|
EFI_STATUS Status;
|
|
UINT32 Count;
|
|
|
|
//
|
|
// Set all the command parameters by fill related registers.
|
|
// Before write to all the following registers, BSY and DRQ must be 0.
|
|
//
|
|
Status = DRQClear2 (IdeDev, ATAPITIMEOUT);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Select device via Device/Head Register.
|
|
//
|
|
IDEWritePortB (
|
|
IdeDev->PciIo,
|
|
IdeDev->IoPort->Head,
|
|
(UINT8) ((IdeDev->Device << 4) | ATA_DEFAULT_CMD) // DEFAULT_CMD: 0xa0 (1010,0000)
|
|
);
|
|
|
|
//
|
|
// No OVL; No DMA
|
|
//
|
|
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Feature, 0x00);
|
|
|
|
//
|
|
// set the transfersize to ATAPI_MAX_BYTE_COUNT to let the device
|
|
// determine how many data should be transferred.
|
|
//
|
|
IDEWritePortB (
|
|
IdeDev->PciIo,
|
|
IdeDev->IoPort->CylinderLsb,
|
|
(UINT8) (ATAPI_MAX_BYTE_COUNT & 0x00ff)
|
|
);
|
|
IDEWritePortB (
|
|
IdeDev->PciIo,
|
|
IdeDev->IoPort->CylinderMsb,
|
|
(UINT8) (ATAPI_MAX_BYTE_COUNT >> 8)
|
|
);
|
|
|
|
//
|
|
// ATA_DEFAULT_CTL:0x0a (0000,1010)
|
|
// Disable interrupt
|
|
//
|
|
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, ATA_DEFAULT_CTL);
|
|
|
|
//
|
|
// Send Packet command to inform device
|
|
// that the following data bytes are command packet.
|
|
//
|
|
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, ATA_CMD_PACKET);
|
|
|
|
Status = DRQReady (IdeDev, ATAPITIMEOUT);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Send out command packet
|
|
//
|
|
CommandIndex = Packet->Data16;
|
|
for (Count = 0; Count < 6; Count++, CommandIndex++) {
|
|
|
|
IDEWritePortW (IdeDev->PciIo, IdeDev->IoPort->Data, *CommandIndex);
|
|
gBS->Stall (10);
|
|
}
|
|
|
|
//
|
|
// call PioReadWriteData() function to get
|
|
// requested transfer data form device.
|
|
//
|
|
return PioReadWriteData (IdeDev, Buffer, ByteCount, 1, TimeOut);
|
|
}
|
|
/**
|
|
This function is used to send out ATAPI commands conforms to the Packet Command
|
|
with PIO Data Out Protocol.
|
|
|
|
@param IdeDev pointer pointing to IDE_BLK_IO_DEV data structure, used
|
|
to record all the information of the IDE device.
|
|
@param Packet pointer pointing to ATAPI_PACKET_COMMAND data structure
|
|
which contains the contents of the command.
|
|
@param Buffer buffer contained data transferred from host to device.
|
|
@param ByteCount data size in byte unit of the buffer.
|
|
@param TimeOut this parameter is used to specify the timeout value
|
|
for the PioReadWriteData() function.
|
|
@retval EFI_SUCCESS send out the ATAPI packet command successfully
|
|
and device received data successfully.
|
|
@retval EFI_DEVICE_ERROR the device failed to send data.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
AtapiPacketCommandOut (
|
|
IN IDE_BLK_IO_DEV *IdeDev,
|
|
IN ATAPI_PACKET_COMMAND *Packet,
|
|
IN UINT16 *Buffer,
|
|
IN UINT32 ByteCount,
|
|
IN UINTN TimeOut
|
|
)
|
|
{
|
|
UINT16 *CommandIndex;
|
|
EFI_STATUS Status;
|
|
UINT32 Count;
|
|
|
|
//
|
|
// set all the command parameters
|
|
// Before write to all the following registers, BSY and DRQ must be 0.
|
|
//
|
|
Status = DRQClear2 (IdeDev, ATAPITIMEOUT);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Select device via Device/Head Register.
|
|
//
|
|
IDEWritePortB (
|
|
IdeDev->PciIo,
|
|
IdeDev->IoPort->Head,
|
|
(UINT8) ((IdeDev->Device << 4) | ATA_DEFAULT_CMD) // ATA_DEFAULT_CMD: 0xa0 (1010,0000)
|
|
);
|
|
|
|
//
|
|
// No OVL; No DMA
|
|
//
|
|
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg1.Feature, 0x00);
|
|
|
|
//
|
|
// set the transfersize to ATAPI_MAX_BYTE_COUNT to
|
|
// let the device determine how many data should be transferred.
|
|
//
|
|
IDEWritePortB (
|
|
IdeDev->PciIo,
|
|
IdeDev->IoPort->CylinderLsb,
|
|
(UINT8) (ATAPI_MAX_BYTE_COUNT & 0x00ff)
|
|
);
|
|
IDEWritePortB (
|
|
IdeDev->PciIo,
|
|
IdeDev->IoPort->CylinderMsb,
|
|
(UINT8) (ATAPI_MAX_BYTE_COUNT >> 8)
|
|
);
|
|
|
|
//
|
|
// DEFAULT_CTL:0x0a (0000,1010)
|
|
// Disable interrupt
|
|
//
|
|
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Alt.DeviceControl, ATA_DEFAULT_CTL);
|
|
|
|
//
|
|
// Send Packet command to inform device
|
|
// that the following data bytes are command packet.
|
|
//
|
|
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, ATA_CMD_PACKET);
|
|
|
|
Status = DRQReady2 (IdeDev, ATAPITIMEOUT);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Send out command packet
|
|
//
|
|
CommandIndex = Packet->Data16;
|
|
for (Count = 0; Count < 6; Count++, CommandIndex++) {
|
|
IDEWritePortW (IdeDev->PciIo, IdeDev->IoPort->Data, *CommandIndex);
|
|
gBS->Stall (10);
|
|
}
|
|
|
|
//
|
|
// call PioReadWriteData() function to send requested transfer data to device.
|
|
//
|
|
return PioReadWriteData (IdeDev, Buffer, ByteCount, 0, TimeOut);
|
|
}
|
|
/**
|
|
Sends out ATAPI Inquiry Packet Command to the specified device. This command will
|
|
return INQUIRY data of the device.
|
|
|
|
@param IdeDev pointer pointing to IDE_BLK_IO_DEV data structure, used
|
|
to record all the information of the IDE device.
|
|
|
|
@retval EFI_SUCCESS Inquiry command completes successfully.
|
|
@retval EFI_DEVICE_ERROR Inquiry command failed.
|
|
|
|
@note Parameter "IdeDev" will be updated in this function.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
AtapiInquiry (
|
|
IN IDE_BLK_IO_DEV *IdeDev
|
|
)
|
|
{
|
|
ATAPI_PACKET_COMMAND Packet;
|
|
EFI_STATUS Status;
|
|
ATAPI_INQUIRY_DATA *InquiryData;
|
|
|
|
//
|
|
// prepare command packet for the ATAPI Inquiry Packet Command.
|
|
//
|
|
ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
|
|
Packet.Inquiry.opcode = ATA_CMD_INQUIRY;
|
|
Packet.Inquiry.page_code = 0;
|
|
Packet.Inquiry.allocation_length = sizeof (ATAPI_INQUIRY_DATA);
|
|
|
|
InquiryData = AllocatePool (sizeof (ATAPI_INQUIRY_DATA));
|
|
if (InquiryData == NULL) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
//
|
|
// Send command packet and get requested Inquiry data.
|
|
//
|
|
Status = AtapiPacketCommandIn (
|
|
IdeDev,
|
|
&Packet,
|
|
(UINT16 *) InquiryData,
|
|
sizeof (ATAPI_INQUIRY_DATA),
|
|
ATAPITIMEOUT
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
gBS->FreePool (InquiryData);
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
IdeDev->InquiryData = InquiryData;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
/**
|
|
This function is called by DiscoverIdeDevice() during its device
|
|
identification.
|
|
Its main purpose is to get enough information for the device media
|
|
to fill in the Media data structure of the Block I/O Protocol interface.
|
|
|
|
There are 5 steps to reach such objective:
|
|
1. Sends out the ATAPI Identify Command to the specified device.
|
|
Only ATAPI device responses to this command. If the command succeeds,
|
|
it returns the Identify data structure which filled with information
|
|
about the device. Since the ATAPI device contains removable media,
|
|
the only meaningful information is the device module name.
|
|
2. Sends out ATAPI Inquiry Packet Command to the specified device.
|
|
This command will return inquiry data of the device, which contains
|
|
the device type information.
|
|
3. Allocate sense data space for future use. We don't detect the media
|
|
presence here to improvement boot performance, especially when CD
|
|
media is present. The media detection will be performed just before
|
|
each BLK_IO read/write
|
|
|
|
@param IdeDev pointer pointing to IDE_BLK_IO_DEV data structure, used
|
|
to record all the information of the IDE device.
|
|
|
|
@retval EFI_SUCCESS Identify ATAPI device successfully.
|
|
@retval EFI_DEVICE_ERROR ATAPI Identify Device Command failed or device type
|
|
is not supported by this IDE driver.
|
|
@retval EFI_OUT_OF_RESOURCES Allocate memory for sense data failed
|
|
|
|
@note Parameter "IdeDev" will be updated in this function.
|
|
**/
|
|
EFI_STATUS
|
|
ATAPIIdentify (
|
|
IN IDE_BLK_IO_DEV *IdeDev
|
|
)
|
|
{
|
|
EFI_IDENTIFY_DATA *AtapiIdentifyPointer;
|
|
UINT8 DeviceSelect;
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// device select bit
|
|
//
|
|
DeviceSelect = (UINT8) ((IdeDev->Device) << 4);
|
|
|
|
AtapiIdentifyPointer = AllocatePool (sizeof (EFI_IDENTIFY_DATA));
|
|
if (AtapiIdentifyPointer == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
//
|
|
// Send ATAPI Identify Command to get IDENTIFY data.
|
|
//
|
|
Status = AtaPioDataIn (
|
|
IdeDev,
|
|
(VOID *) AtapiIdentifyPointer,
|
|
sizeof (EFI_IDENTIFY_DATA),
|
|
ATA_CMD_IDENTIFY_DEVICE,
|
|
DeviceSelect,
|
|
0,
|
|
0,
|
|
0,
|
|
0
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
gBS->FreePool (AtapiIdentifyPointer);
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
IdeDev->IdData = AtapiIdentifyPointer;
|
|
PrintAtaModuleName (IdeDev);
|
|
|
|
//
|
|
// Send ATAPI Inquiry Packet Command to get INQUIRY data.
|
|
//
|
|
Status = AtapiInquiry (IdeDev);
|
|
if (EFI_ERROR (Status)) {
|
|
gBS->FreePool (IdeDev->IdData);
|
|
//
|
|
// Make sure the pIdData will not be freed again.
|
|
//
|
|
IdeDev->IdData = NULL;
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
//
|
|
// Get media removable info from INQUIRY data.
|
|
//
|
|
IdeDev->BlkIo.Media->RemovableMedia = (UINT8) ((IdeDev->InquiryData->RMB & 0x80) == 0x80);
|
|
|
|
//
|
|
// Identify device type via INQUIRY data.
|
|
//
|
|
switch (IdeDev->InquiryData->peripheral_type & 0x1f) {
|
|
|
|
//
|
|
// Magnetic Disk
|
|
//
|
|
case 0x00:
|
|
|
|
//
|
|
// device is LS120 or ZIP drive.
|
|
//
|
|
IdeDev->Type = IdeMagnetic;
|
|
|
|
IdeDev->BlkIo.Media->MediaId = 0;
|
|
//
|
|
// Give initial value
|
|
//
|
|
IdeDev->BlkIo.Media->MediaPresent = FALSE;
|
|
|
|
IdeDev->BlkIo.Media->LastBlock = 0;
|
|
IdeDev->BlkIo.Media->BlockSize = 0x200;
|
|
break;
|
|
|
|
//
|
|
// CD-ROM
|
|
//
|
|
case 0x05:
|
|
|
|
IdeDev->Type = IdeCdRom;
|
|
IdeDev->BlkIo.Media->MediaId = 0;
|
|
//
|
|
// Give initial value
|
|
//
|
|
IdeDev->BlkIo.Media->MediaPresent = FALSE;
|
|
|
|
IdeDev->BlkIo.Media->LastBlock = 0;
|
|
IdeDev->BlkIo.Media->BlockSize = 0x800;
|
|
IdeDev->BlkIo.Media->ReadOnly = TRUE;
|
|
break;
|
|
|
|
//
|
|
// Tape
|
|
//
|
|
case 0x01:
|
|
|
|
//
|
|
// WORM
|
|
//
|
|
case 0x04:
|
|
|
|
//
|
|
// Optical
|
|
//
|
|
case 0x07:
|
|
|
|
default:
|
|
IdeDev->Type = IdeUnknown;
|
|
gBS->FreePool (IdeDev->IdData);
|
|
gBS->FreePool (IdeDev->InquiryData);
|
|
//
|
|
// Make sure the pIdData and pInquiryData will not be freed again.
|
|
//
|
|
IdeDev->IdData = NULL;
|
|
IdeDev->InquiryData = NULL;
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
//
|
|
// original sense data numbers
|
|
//
|
|
IdeDev->SenseDataNumber = 20;
|
|
|
|
IdeDev->SenseData = AllocatePool (IdeDev->SenseDataNumber * sizeof (ATAPI_REQUEST_SENSE_DATA));
|
|
if (IdeDev->SenseData == NULL) {
|
|
gBS->FreePool (IdeDev->IdData);
|
|
gBS->FreePool (IdeDev->InquiryData);
|
|
//
|
|
// Make sure the pIdData and pInquiryData will not be freed again.
|
|
//
|
|
IdeDev->IdData = NULL;
|
|
IdeDev->InquiryData = NULL;
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
/**
|
|
Sends out ATAPI Request Sense Packet Command to the specified device. This command
|
|
will return all the current Sense data in the device. This function will pack
|
|
all the Sense data in one single buffer.
|
|
|
|
@param IdeDev pointer pointing to IDE_BLK_IO_DEV data structure, used
|
|
to record all the information of the IDE device.
|
|
@param SenseCounts allocated in this function, and freed by the calling function.
|
|
This buffer is used to accommodate all the sense data returned
|
|
by the device.
|
|
|
|
@retval EFI_SUCCESS Request Sense command completes successfully.
|
|
@retval EFI_DEVICE_ERROR Request Sense command failed.
|
|
**/
|
|
EFI_STATUS
|
|
AtapiRequestSense (
|
|
IN IDE_BLK_IO_DEV *IdeDev,
|
|
OUT UINTN *SenseCounts
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
ATAPI_REQUEST_SENSE_DATA *Sense;
|
|
UINT16 *Ptr;
|
|
BOOLEAN FetchSenseData;
|
|
ATAPI_PACKET_COMMAND Packet;
|
|
|
|
*SenseCounts = 0;
|
|
|
|
ZeroMem (IdeDev->SenseData, sizeof (ATAPI_REQUEST_SENSE_DATA) * (IdeDev->SenseDataNumber));
|
|
//
|
|
// fill command packet for Request Sense Packet Command
|
|
//
|
|
ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
|
|
Packet.RequestSence.opcode = ATA_CMD_REQUEST_SENSE;
|
|
Packet.RequestSence.allocation_length = sizeof (ATAPI_REQUEST_SENSE_DATA);
|
|
|
|
//
|
|
// initialize pointer
|
|
//
|
|
Ptr = (UINT16 *) IdeDev->SenseData;
|
|
//
|
|
// request sense data from device continuously until no sense data
|
|
// exists in the device.
|
|
//
|
|
for (FetchSenseData = TRUE; FetchSenseData;) {
|
|
|
|
Sense = (ATAPI_REQUEST_SENSE_DATA *) Ptr;
|
|
|
|
//
|
|
// send out Request Sense Packet Command and get one Sense data form device
|
|
//
|
|
Status = AtapiPacketCommandIn (
|
|
IdeDev,
|
|
&Packet,
|
|
Ptr,
|
|
sizeof (ATAPI_REQUEST_SENSE_DATA),
|
|
ATAPITIMEOUT
|
|
);
|
|
//
|
|
// failed to get Sense data
|
|
//
|
|
if (EFI_ERROR (Status)) {
|
|
if (*SenseCounts == 0) {
|
|
return EFI_DEVICE_ERROR;
|
|
} else {
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
|
|
(*SenseCounts)++;
|
|
//
|
|
// We limit MAX sense data count to 20 in order to avoid dead loop. Some
|
|
// incompatible ATAPI devices don't retrieve NO_SENSE when there is no media.
|
|
// In this case, dead loop occurs if we don't have a gatekeeper. 20 is
|
|
// supposed to be large enough for any ATAPI device.
|
|
//
|
|
if ((Sense->sense_key != ATA_SK_NO_SENSE) && ((*SenseCounts) < 20)) {
|
|
//
|
|
// Ptr is word-based pointer
|
|
//
|
|
Ptr += (sizeof (ATAPI_REQUEST_SENSE_DATA) + 1) >> 1;
|
|
|
|
} else {
|
|
//
|
|
// when no sense key, skip out the loop
|
|
//
|
|
FetchSenseData = FALSE;
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
/**
|
|
This function is used to parse sense data. Only the first sense data is honoured
|
|
|
|
@param IdeDev Indicates the calling context.
|
|
@param SenseCount Count of sense data.
|
|
@param Result The parsed result.
|
|
|
|
@retval EFI_SUCCESS Successfully parsed.
|
|
@retval EFI_INVALID_PARAMETER Count of sense data is zero.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
ParseSenseData (
|
|
IN IDE_BLK_IO_DEV *IdeDev,
|
|
IN UINTN SenseCount,
|
|
OUT SENSE_RESULT *Result
|
|
)
|
|
{
|
|
ATAPI_REQUEST_SENSE_DATA *SenseData;
|
|
|
|
if (SenseCount == 0) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Only use the first sense data
|
|
//
|
|
SenseData = IdeDev->SenseData;
|
|
*Result = SenseOtherSense;
|
|
|
|
switch (SenseData->sense_key) {
|
|
case ATA_SK_NO_SENSE:
|
|
*Result = SenseNoSenseKey;
|
|
break;
|
|
case ATA_SK_NOT_READY:
|
|
switch (SenseData->addnl_sense_code) {
|
|
case ATA_ASC_NO_MEDIA:
|
|
*Result = SenseNoMedia;
|
|
break;
|
|
case ATA_ASC_MEDIA_UPSIDE_DOWN:
|
|
*Result = SenseMediaError;
|
|
break;
|
|
case ATA_ASC_NOT_READY:
|
|
if (SenseData->addnl_sense_code_qualifier == ATA_ASCQ_IN_PROGRESS) {
|
|
*Result = SenseDeviceNotReadyNeedRetry;
|
|
} else {
|
|
*Result = SenseDeviceNotReadyNoRetry;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case ATA_SK_UNIT_ATTENTION:
|
|
if (SenseData->addnl_sense_code == ATA_ASC_MEDIA_CHANGE) {
|
|
*Result = SenseMediaChange;
|
|
}
|
|
break;
|
|
case ATA_SK_MEDIUM_ERROR:
|
|
switch (SenseData->addnl_sense_code) {
|
|
case ATA_ASC_MEDIA_ERR1:
|
|
case ATA_ASC_MEDIA_ERR2:
|
|
case ATA_ASC_MEDIA_ERR3:
|
|
case ATA_ASC_MEDIA_ERR4:
|
|
*Result = SenseMediaError;
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Sends out ATAPI Test Unit Ready Packet Command to the specified device
|
|
to find out whether device is accessible.
|
|
|
|
@param IdeDev Pointer pointing to IDE_BLK_IO_DEV data structure, used
|
|
to record all the information of the IDE device.
|
|
@param SResult Sense result for this packet command.
|
|
|
|
@retval EFI_SUCCESS Device is accessible.
|
|
@retval EFI_DEVICE_ERROR Device is not accessible.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
AtapiTestUnitReady (
|
|
IN IDE_BLK_IO_DEV *IdeDev,
|
|
OUT SENSE_RESULT *SResult
|
|
)
|
|
{
|
|
ATAPI_PACKET_COMMAND Packet;
|
|
EFI_STATUS Status;
|
|
UINTN SenseCount;
|
|
|
|
//
|
|
// fill command packet
|
|
//
|
|
ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
|
|
Packet.TestUnitReady.opcode = ATA_CMD_TEST_UNIT_READY;
|
|
|
|
//
|
|
// send command packet
|
|
//
|
|
Status = AtapiPacketCommandIn (IdeDev, &Packet, NULL, 0, ATAPITIMEOUT);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = AtapiRequestSense (IdeDev, &SenseCount);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
ParseSenseData (IdeDev, SenseCount, SResult);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Sends out ATAPI Read Capacity Packet Command to the specified device.
|
|
This command will return the information regarding the capacity of the
|
|
media in the device.
|
|
|
|
Current device status will impact device's response to the Read Capacity
|
|
Command. For example, if the device once reset, the Read Capacity
|
|
Command will fail. The Sense data record the current device status, so
|
|
if the Read Capacity Command failed, the Sense data must be requested
|
|
and be analyzed to determine if the Read Capacity Command should retry.
|
|
|
|
@param IdeDev Pointer pointing to IDE_BLK_IO_DEV data structure, used
|
|
to record all the information of the IDE device.
|
|
@param SResult Sense result for this packet command
|
|
|
|
@retval EFI_SUCCESS Read Capacity Command finally completes successfully.
|
|
@retval EFI_DEVICE_ERROR Read Capacity Command failed because of device error.
|
|
@retval EFI_NOT_READY Operation succeeds but returned capacity is 0
|
|
|
|
@note Parameter "IdeDev" will be updated in this function.
|
|
|
|
|
|
**/
|
|
EFI_STATUS
|
|
AtapiReadCapacity (
|
|
IN IDE_BLK_IO_DEV *IdeDev,
|
|
OUT SENSE_RESULT *SResult
|
|
)
|
|
{
|
|
//
|
|
// status returned by Read Capacity Packet Command
|
|
//
|
|
EFI_STATUS Status;
|
|
EFI_STATUS SenseStatus;
|
|
ATAPI_PACKET_COMMAND Packet;
|
|
UINTN SenseCount;
|
|
|
|
//
|
|
// used for capacity data returned from ATAPI device
|
|
//
|
|
ATAPI_READ_CAPACITY_DATA Data;
|
|
ATAPI_READ_FORMAT_CAPACITY_DATA FormatData;
|
|
|
|
ZeroMem (&Data, sizeof (Data));
|
|
ZeroMem (&FormatData, sizeof (FormatData));
|
|
|
|
if (IdeDev->Type == IdeCdRom) {
|
|
|
|
ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
|
|
Packet.Inquiry.opcode = ATA_CMD_READ_CAPACITY;
|
|
Status = AtapiPacketCommandIn (
|
|
IdeDev,
|
|
&Packet,
|
|
(UINT16 *) &Data,
|
|
sizeof (ATAPI_READ_CAPACITY_DATA),
|
|
ATAPITIMEOUT
|
|
);
|
|
|
|
} else {
|
|
//
|
|
// Type == IdeMagnetic
|
|
//
|
|
ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
|
|
Packet.ReadFormatCapacity.opcode = ATA_CMD_READ_FORMAT_CAPACITY;
|
|
Packet.ReadFormatCapacity.allocation_length_lo = 12;
|
|
Status = AtapiPacketCommandIn (
|
|
IdeDev,
|
|
&Packet,
|
|
(UINT16 *) &FormatData,
|
|
sizeof (ATAPI_READ_FORMAT_CAPACITY_DATA),
|
|
ATAPITIMEOUT
|
|
);
|
|
}
|
|
|
|
if (Status == EFI_TIMEOUT) {
|
|
return Status;
|
|
}
|
|
|
|
SenseStatus = AtapiRequestSense (IdeDev, &SenseCount);
|
|
|
|
if (!EFI_ERROR (SenseStatus)) {
|
|
ParseSenseData (IdeDev, SenseCount, SResult);
|
|
|
|
if (!EFI_ERROR (Status) && *SResult == SenseNoSenseKey) {
|
|
if (IdeDev->Type == IdeCdRom) {
|
|
|
|
IdeDev->BlkIo.Media->LastBlock = (Data.LastLba3 << 24) |
|
|
(Data.LastLba2 << 16) |
|
|
(Data.LastLba1 << 8) |
|
|
Data.LastLba0;
|
|
|
|
IdeDev->BlkIo.Media->MediaPresent = TRUE;
|
|
|
|
IdeDev->BlkIo.Media->ReadOnly = TRUE;
|
|
|
|
//
|
|
// Because the user data portion in the sector of the Data CD supported
|
|
// is always 0x800
|
|
//
|
|
IdeDev->BlkIo.Media->BlockSize = 0x800;
|
|
}
|
|
|
|
if (IdeDev->Type == IdeMagnetic) {
|
|
|
|
if (FormatData.DesCode == 3) {
|
|
IdeDev->BlkIo.Media->MediaPresent = FALSE;
|
|
IdeDev->BlkIo.Media->LastBlock = 0;
|
|
} else {
|
|
|
|
IdeDev->BlkIo.Media->LastBlock = (FormatData.LastLba3 << 24) |
|
|
(FormatData.LastLba2 << 16) |
|
|
(FormatData.LastLba1 << 8) |
|
|
FormatData.LastLba0;
|
|
if (IdeDev->BlkIo.Media->LastBlock != 0) {
|
|
IdeDev->BlkIo.Media->LastBlock--;
|
|
|
|
IdeDev->BlkIo.Media->BlockSize = (FormatData.BlockSize2 << 16) |
|
|
(FormatData.BlockSize1 << 8) |
|
|
FormatData.BlockSize0;
|
|
|
|
IdeDev->BlkIo.Media->MediaPresent = TRUE;
|
|
} else {
|
|
IdeDev->BlkIo.Media->MediaPresent = FALSE;
|
|
//
|
|
// Return EFI_NOT_READY operation succeeds but returned capacity is 0
|
|
//
|
|
return EFI_NOT_READY;
|
|
}
|
|
|
|
IdeDev->BlkIo.Media->BlockSize = 0x200;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
} else {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
}
|
|
/**
|
|
This function is used to test the current media write-protected or not residing
|
|
in the LS-120 drive or ZIP drive.
|
|
@param IdeDev pointer pointing to IDE_BLK_IO_DEV data structure, used
|
|
to record all the information of the IDE device.
|
|
@param WriteProtected if True, current media is write protected.
|
|
if FALSE, current media is writable
|
|
|
|
@retval EFI_SUCCESS The media write-protected status is achieved successfully
|
|
@retval EFI_DEVICE_ERROR Get Media Status Command is failed.
|
|
**/
|
|
EFI_STATUS
|
|
IsLS120orZipWriteProtected (
|
|
IN IDE_BLK_IO_DEV *IdeDev,
|
|
OUT BOOLEAN *WriteProtected
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
*WriteProtected = FALSE;
|
|
|
|
Status = LS120EnableMediaStatus (IdeDev, TRUE);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
//
|
|
// the Get Media Status Command is only valid
|
|
// if a Set Features/Enable Media Status Command has been previously issued.
|
|
//
|
|
if (LS120GetMediaStatus (IdeDev) == EFI_WRITE_PROTECTED) {
|
|
|
|
*WriteProtected = TRUE;
|
|
} else {
|
|
|
|
*WriteProtected = FALSE;
|
|
}
|
|
|
|
//
|
|
// After Get Media Status Command completes,
|
|
// Set Features/Disable Media Command should be sent.
|
|
//
|
|
Status = LS120EnableMediaStatus (IdeDev, FALSE);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Used before read/write blocks from/to ATAPI device media. Since ATAPI device
|
|
media is removable, it is necessary to detect whether media is present and
|
|
get current present media's information, and if media has been changed, Block
|
|
I/O Protocol need to be reinstalled.
|
|
|
|
@param IdeDev pointer pointing to IDE_BLK_IO_DEV data structure, used
|
|
to record all the information of the IDE device.
|
|
@param MediaChange return value that indicates if the media of the device has been
|
|
changed.
|
|
|
|
@retval EFI_SUCCESS media found successfully.
|
|
@retval EFI_DEVICE_ERROR any error encounters during media detection.
|
|
@retval EFI_NO_MEDIA media not found.
|
|
|
|
@note
|
|
parameter IdeDev may be updated in this function.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
AtapiDetectMedia (
|
|
IN IDE_BLK_IO_DEV *IdeDev,
|
|
OUT BOOLEAN *MediaChange
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_STATUS CleanStateStatus;
|
|
EFI_BLOCK_IO_MEDIA OldMediaInfo;
|
|
UINTN RetryTimes;
|
|
UINTN RetryNotReady;
|
|
SENSE_RESULT SResult;
|
|
BOOLEAN WriteProtected;
|
|
|
|
CopyMem (&OldMediaInfo, IdeDev->BlkIo.Media, sizeof (EFI_BLOCK_IO_MEDIA));
|
|
*MediaChange = FALSE;
|
|
//
|
|
// Retry for SenseDeviceNotReadyNeedRetry.
|
|
// Each retry takes 1s and we limit the upper boundary to
|
|
// 120 times about 2 min.
|
|
//
|
|
RetryNotReady = 120;
|
|
|
|
//
|
|
// Do Test Unit Ready
|
|
//
|
|
DoTUR:
|
|
//
|
|
// Retry 5 times
|
|
//
|
|
RetryTimes = 5;
|
|
while (RetryTimes != 0) {
|
|
|
|
Status = AtapiTestUnitReady (IdeDev, &SResult);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// Test Unit Ready error without sense data.
|
|
// For some devices, this means there's extra data
|
|
// that has not been read, so we read these extra
|
|
// data out before going on.
|
|
//
|
|
CleanStateStatus = AtapiReadPendingData (IdeDev);
|
|
if (EFI_ERROR (CleanStateStatus)) {
|
|
//
|
|
// Busy wait failed, try again
|
|
//
|
|
RetryTimes--;
|
|
}
|
|
//
|
|
// Try again without counting down RetryTimes
|
|
//
|
|
continue;
|
|
} else {
|
|
switch (SResult) {
|
|
case SenseNoSenseKey:
|
|
if (IdeDev->BlkIo.Media->MediaPresent) {
|
|
goto Done;
|
|
} else {
|
|
//
|
|
// Media present but the internal structure need refreshed.
|
|
// Try Read Capacity
|
|
//
|
|
goto DoRC;
|
|
}
|
|
break;
|
|
|
|
case SenseDeviceNotReadyNeedRetry:
|
|
if (--RetryNotReady == 0) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
gBS->Stall (1000 * STALL_1_MILLI_SECOND);
|
|
continue;
|
|
break;
|
|
|
|
case SenseNoMedia:
|
|
IdeDev->BlkIo.Media->MediaPresent = FALSE;
|
|
IdeDev->BlkIo.Media->LastBlock = 0;
|
|
goto Done;
|
|
break;
|
|
|
|
case SenseDeviceNotReadyNoRetry:
|
|
case SenseMediaError:
|
|
return EFI_DEVICE_ERROR;
|
|
|
|
case SenseMediaChange:
|
|
IdeDev->BlkIo.Media->MediaId++;
|
|
goto DoRC;
|
|
break;
|
|
|
|
default:
|
|
RetryTimes--;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return EFI_DEVICE_ERROR;
|
|
|
|
//
|
|
// Do Read Capacity
|
|
//
|
|
DoRC:
|
|
RetryTimes = 5;
|
|
|
|
while (RetryTimes != 0) {
|
|
|
|
Status = AtapiReadCapacity (IdeDev, &SResult);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
RetryTimes--;
|
|
continue;
|
|
} else {
|
|
switch (SResult) {
|
|
case SenseNoSenseKey:
|
|
goto Done;
|
|
break;
|
|
|
|
case SenseDeviceNotReadyNeedRetry:
|
|
//
|
|
// We use Test Unit Ready to retry which
|
|
// is faster.
|
|
//
|
|
goto DoTUR;
|
|
break;
|
|
|
|
case SenseNoMedia:
|
|
IdeDev->BlkIo.Media->MediaPresent = FALSE;
|
|
IdeDev->BlkIo.Media->LastBlock = 0;
|
|
goto Done;
|
|
break;
|
|
|
|
case SenseDeviceNotReadyNoRetry:
|
|
case SenseMediaError:
|
|
return EFI_DEVICE_ERROR;
|
|
|
|
case SenseMediaChange:
|
|
IdeDev->BlkIo.Media->MediaId++;
|
|
continue;
|
|
break;
|
|
|
|
default:
|
|
RetryTimes--;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return EFI_DEVICE_ERROR;
|
|
|
|
Done:
|
|
//
|
|
// the following code is to check the write-protected for LS120 media
|
|
//
|
|
if ((IdeDev->BlkIo.Media->MediaPresent) && (IdeDev->Type == IdeMagnetic)) {
|
|
|
|
Status = IsLS120orZipWriteProtected (IdeDev, &WriteProtected);
|
|
if (!EFI_ERROR (Status)) {
|
|
|
|
if (WriteProtected) {
|
|
|
|
IdeDev->BlkIo.Media->ReadOnly = TRUE;
|
|
} else {
|
|
|
|
IdeDev->BlkIo.Media->ReadOnly = FALSE;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if (IdeDev->BlkIo.Media->MediaId != OldMediaInfo.MediaId) {
|
|
//
|
|
// Media change information got from the device
|
|
//
|
|
*MediaChange = TRUE;
|
|
}
|
|
|
|
if (IdeDev->BlkIo.Media->ReadOnly != OldMediaInfo.ReadOnly) {
|
|
*MediaChange = TRUE;
|
|
IdeDev->BlkIo.Media->MediaId += 1;
|
|
}
|
|
|
|
if (IdeDev->BlkIo.Media->BlockSize != OldMediaInfo.BlockSize) {
|
|
*MediaChange = TRUE;
|
|
IdeDev->BlkIo.Media->MediaId += 1;
|
|
}
|
|
|
|
if (IdeDev->BlkIo.Media->LastBlock != OldMediaInfo.LastBlock) {
|
|
*MediaChange = TRUE;
|
|
IdeDev->BlkIo.Media->MediaId += 1;
|
|
}
|
|
|
|
if (IdeDev->BlkIo.Media->MediaPresent != OldMediaInfo.MediaPresent) {
|
|
if (IdeDev->BlkIo.Media->MediaPresent) {
|
|
//
|
|
// when change from no media to media present, reset the MediaId to 1.
|
|
//
|
|
IdeDev->BlkIo.Media->MediaId = 1;
|
|
} else {
|
|
//
|
|
// when no media, reset the MediaId to zero.
|
|
//
|
|
IdeDev->BlkIo.Media->MediaId = 0;
|
|
}
|
|
|
|
*MediaChange = TRUE;
|
|
}
|
|
|
|
//
|
|
// if any change on current existing media,
|
|
// the Block I/O protocol need to be reinstalled.
|
|
//
|
|
if (*MediaChange) {
|
|
gBS->ReinstallProtocolInterface (
|
|
IdeDev->Handle,
|
|
&gEfiBlockIoProtocolGuid,
|
|
&IdeDev->BlkIo,
|
|
&IdeDev->BlkIo
|
|
);
|
|
}
|
|
|
|
if (IdeDev->BlkIo.Media->MediaPresent) {
|
|
return EFI_SUCCESS;
|
|
} else {
|
|
return EFI_NO_MEDIA;
|
|
}
|
|
}
|
|
|
|
/**
|
|
This function is called by the AtapiBlkIoReadBlocks() to perform
|
|
read from media in block unit.
|
|
|
|
The main command used to access media here is READ(10) Command.
|
|
READ(10) Command requests that the ATAPI device media transfer
|
|
specified data to the host. Data is transferred in block(sector)
|
|
unit. The maximum number of blocks that can be transferred once is
|
|
65536. This is the main difference between READ(10) and READ(12)
|
|
Command. The maximum number of blocks in READ(12) is 2 power 32.
|
|
|
|
@param IdeDev pointer pointing to IDE_BLK_IO_DEV data structure, used
|
|
to record all the information of the IDE device.
|
|
@param Buffer A pointer to the destination buffer for the data.
|
|
@param Lba The starting logical block address to read from on the
|
|
device media.
|
|
@param NumberOfBlocks The number of transfer data blocks.
|
|
|
|
@return status is fully dependent on the return status of AtapiPacketCommandIn() function.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
AtapiReadSectors (
|
|
IN IDE_BLK_IO_DEV *IdeDev,
|
|
IN VOID *Buffer,
|
|
IN EFI_LBA Lba,
|
|
IN UINTN NumberOfBlocks
|
|
)
|
|
{
|
|
|
|
ATAPI_PACKET_COMMAND Packet;
|
|
ATAPI_READ10_CMD *Read10Packet;
|
|
EFI_STATUS Status;
|
|
UINTN BlocksRemaining;
|
|
UINT32 Lba32;
|
|
UINT32 BlockSize;
|
|
UINT32 ByteCount;
|
|
UINT16 SectorCount;
|
|
VOID *PtrBuffer;
|
|
UINT16 MaxBlock;
|
|
UINTN TimeOut;
|
|
|
|
//
|
|
// fill command packet for Read(10) command
|
|
//
|
|
ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
|
|
Read10Packet = &Packet.Read10;
|
|
Lba32 = (UINT32) Lba;
|
|
PtrBuffer = Buffer;
|
|
|
|
BlockSize = IdeDev->BlkIo.Media->BlockSize;
|
|
|
|
//
|
|
// limit the data bytes that can be transferred by one Read(10) Command
|
|
//
|
|
MaxBlock = 65535;
|
|
|
|
BlocksRemaining = NumberOfBlocks;
|
|
|
|
Status = EFI_SUCCESS;
|
|
while (BlocksRemaining > 0) {
|
|
|
|
if (BlocksRemaining <= MaxBlock) {
|
|
|
|
SectorCount = (UINT16) BlocksRemaining;
|
|
} else {
|
|
|
|
SectorCount = MaxBlock;
|
|
}
|
|
|
|
//
|
|
// fill the Packet data structure
|
|
//
|
|
|
|
Read10Packet->opcode = ATA_CMD_READ_10;
|
|
|
|
//
|
|
// Lba0 ~ Lba3 specify the start logical block address of the data transfer.
|
|
// Lba0 is MSB, Lba3 is LSB
|
|
//
|
|
Read10Packet->Lba3 = (UINT8) (Lba32 & 0xff);
|
|
Read10Packet->Lba2 = (UINT8) (Lba32 >> 8);
|
|
Read10Packet->Lba1 = (UINT8) (Lba32 >> 16);
|
|
Read10Packet->Lba0 = (UINT8) (Lba32 >> 24);
|
|
|
|
//
|
|
// TranLen0 ~ TranLen1 specify the transfer length in block unit.
|
|
// TranLen0 is MSB, TranLen is LSB
|
|
//
|
|
Read10Packet->TranLen1 = (UINT8) (SectorCount & 0xff);
|
|
Read10Packet->TranLen0 = (UINT8) (SectorCount >> 8);
|
|
|
|
ByteCount = SectorCount * BlockSize;
|
|
|
|
if (IdeDev->Type == IdeCdRom) {
|
|
TimeOut = CDROMLONGTIMEOUT;
|
|
} else {
|
|
TimeOut = ATAPILONGTIMEOUT;
|
|
}
|
|
|
|
Status = AtapiPacketCommandIn (
|
|
IdeDev,
|
|
&Packet,
|
|
(UINT16 *) PtrBuffer,
|
|
ByteCount,
|
|
TimeOut
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Lba32 += SectorCount;
|
|
PtrBuffer = (UINT8 *) PtrBuffer + SectorCount * BlockSize;
|
|
BlocksRemaining -= SectorCount;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
This function is called by the AtapiBlkIoWriteBlocks() to perform
|
|
write onto media in block unit.
|
|
The main command used to access media here is Write(10) Command.
|
|
Write(10) Command requests that the ATAPI device media transfer
|
|
specified data to the host. Data is transferred in block (sector)
|
|
unit. The maximum number of blocks that can be transferred once is
|
|
65536.
|
|
|
|
@param IdeDev pointer pointing to IDE_BLK_IO_DEV data structure, used
|
|
to record all the information of the IDE device.
|
|
@param Buffer A pointer to the source buffer for the data.
|
|
@param Lba The starting logical block address to write onto
|
|
the device media.
|
|
@param NumberOfBlocks The number of transfer data blocks.
|
|
|
|
@return status is fully dependent on the return status of AtapiPacketCommandOut() function.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
AtapiWriteSectors (
|
|
IN IDE_BLK_IO_DEV *IdeDev,
|
|
IN VOID *Buffer,
|
|
IN EFI_LBA Lba,
|
|
IN UINTN NumberOfBlocks
|
|
)
|
|
{
|
|
|
|
ATAPI_PACKET_COMMAND Packet;
|
|
ATAPI_READ10_CMD *Read10Packet;
|
|
|
|
EFI_STATUS Status;
|
|
UINTN BlocksRemaining;
|
|
UINT32 Lba32;
|
|
UINT32 BlockSize;
|
|
UINT32 ByteCount;
|
|
UINT16 SectorCount;
|
|
VOID *PtrBuffer;
|
|
UINT16 MaxBlock;
|
|
|
|
//
|
|
// fill command packet for Write(10) command
|
|
// Write(10) command packet has the same data structure as
|
|
// Read(10) command packet,
|
|
// so here use the Read10Packet data structure
|
|
// for the Write(10) command packet.
|
|
//
|
|
ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
|
|
Read10Packet = &Packet.Read10;
|
|
|
|
Lba32 = (UINT32) Lba;
|
|
PtrBuffer = Buffer;
|
|
|
|
BlockSize = IdeDev->BlkIo.Media->BlockSize;
|
|
|
|
//
|
|
// limit the data bytes that can be transferred by one Read(10) Command
|
|
//
|
|
MaxBlock = (UINT16) (65536 / BlockSize);
|
|
|
|
BlocksRemaining = NumberOfBlocks;
|
|
|
|
Status = EFI_SUCCESS;
|
|
while (BlocksRemaining > 0) {
|
|
|
|
if (BlocksRemaining >= MaxBlock) {
|
|
SectorCount = MaxBlock;
|
|
} else {
|
|
SectorCount = (UINT16) BlocksRemaining;
|
|
}
|
|
|
|
//
|
|
// Command code is WRITE_10.
|
|
//
|
|
Read10Packet->opcode = ATA_CMD_WRITE_10;
|
|
|
|
//
|
|
// Lba0 ~ Lba3 specify the start logical block address of the data transfer.
|
|
// Lba0 is MSB, Lba3 is LSB
|
|
//
|
|
Read10Packet->Lba3 = (UINT8) (Lba32 & 0xff);
|
|
Read10Packet->Lba2 = (UINT8) (Lba32 >> 8);
|
|
Read10Packet->Lba1 = (UINT8) (Lba32 >> 16);
|
|
Read10Packet->Lba0 = (UINT8) (Lba32 >> 24);
|
|
|
|
//
|
|
// TranLen0 ~ TranLen1 specify the transfer length in block unit.
|
|
// TranLen0 is MSB, TranLen is LSB
|
|
//
|
|
Read10Packet->TranLen1 = (UINT8) (SectorCount & 0xff);
|
|
Read10Packet->TranLen0 = (UINT8) (SectorCount >> 8);
|
|
|
|
ByteCount = SectorCount * BlockSize;
|
|
|
|
Status = AtapiPacketCommandOut (
|
|
IdeDev,
|
|
&Packet,
|
|
(UINT16 *) PtrBuffer,
|
|
ByteCount,
|
|
ATAPILONGTIMEOUT
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Lba32 += SectorCount;
|
|
PtrBuffer = ((UINT8 *) PtrBuffer + SectorCount * BlockSize);
|
|
BlocksRemaining -= SectorCount;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
/**
|
|
This function is used to implement the Soft Reset on the specified
|
|
ATAPI device. Different from the AtaSoftReset(), here reset is a ATA
|
|
Soft Reset Command special for ATAPI device, and it only take effects
|
|
on the specified ATAPI device, not on the whole IDE bus.
|
|
Since the ATAPI soft reset is needed when device is in exceptional
|
|
condition (such as BSY bit is always set ), I think the Soft Reset
|
|
command should be sent without waiting for the BSY clear and DRDY
|
|
set.
|
|
This function is called by IdeBlkIoReset(),
|
|
a interface function of Block I/O protocol.
|
|
|
|
@param IdeDev pointer pointing to IDE_BLK_IO_DEV data structure, used
|
|
to record all the information of the IDE device.
|
|
|
|
@retval EFI_SUCCESS Soft reset completes successfully.
|
|
@retval EFI_DEVICE_ERROR Any step during the reset process is failed.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
AtapiSoftReset (
|
|
IN IDE_BLK_IO_DEV *IdeDev
|
|
)
|
|
{
|
|
UINT8 Command;
|
|
UINT8 DeviceSelect;
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// for ATAPI device, no need to wait DRDY ready after device selecting.
|
|
// (bit7 and bit5 are both set to 1 for backward compatibility)
|
|
//
|
|
DeviceSelect = (UINT8) (((BIT7 | BIT5) | (IdeDev->Device << 4)));
|
|
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Head, DeviceSelect);
|
|
|
|
Command = ATA_CMD_SOFT_RESET;
|
|
IDEWritePortB (IdeDev->PciIo, IdeDev->IoPort->Reg.Command, Command);
|
|
|
|
//
|
|
// BSY cleared is the only status return to the host by the device
|
|
// when reset is completed.
|
|
// slave device needs at most 31s to clear BSY
|
|
//
|
|
Status = WaitForBSYClear (IdeDev, 31000);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
//
|
|
// stall 5 seconds to make the device status stable
|
|
//
|
|
gBS->Stall (5000000);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
This function is the ATAPI implementation for ReadBlocks in the
|
|
Block I/O Protocol interface.
|
|
|
|
@param IdeBlkIoDevice Indicates the calling context.
|
|
@param MediaId The media id that the read request is for.
|
|
@param Lba The starting logical block address to read from on the device.
|
|
@param BufferSize The size of the Buffer in bytes. This must be a multiple
|
|
of the intrinsic block size of the device.
|
|
@param Buffer A pointer to the destination buffer for the data. The caller
|
|
is responsible for either having implicit or explicit
|
|
ownership of the memory that data is read into.
|
|
|
|
@retval EFI_SUCCESS Read Blocks successfully.
|
|
@retval EFI_DEVICE_ERROR Read Blocks failed.
|
|
@retval EFI_NO_MEDIA There is no media in the device.
|
|
@retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
|
|
@retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of the
|
|
intrinsic block size of the device.
|
|
@retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,
|
|
or the data buffer is not valid.
|
|
**/
|
|
EFI_STATUS
|
|
AtapiBlkIoReadBlocks (
|
|
IN IDE_BLK_IO_DEV *IdeBlkIoDevice,
|
|
IN UINT32 MediaId,
|
|
IN EFI_LBA Lba,
|
|
IN UINTN BufferSize,
|
|
OUT VOID *Buffer
|
|
)
|
|
{
|
|
EFI_BLOCK_IO_MEDIA *Media;
|
|
UINTN BlockSize;
|
|
UINTN NumberOfBlocks;
|
|
EFI_STATUS Status;
|
|
|
|
BOOLEAN MediaChange;
|
|
|
|
if (Buffer == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (BufferSize == 0) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// ATAPI device media is removable, so it is a must
|
|
// to detect media first before read operation
|
|
//
|
|
MediaChange = FALSE;
|
|
Status = AtapiDetectMedia (IdeBlkIoDevice, &MediaChange);
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
if (IdeBlkIoDevice->Cache != NULL) {
|
|
gBS->FreePool (IdeBlkIoDevice->Cache);
|
|
IdeBlkIoDevice->Cache = NULL;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
//
|
|
// Get the intrinsic block size
|
|
//
|
|
Media = IdeBlkIoDevice->BlkIo.Media;
|
|
BlockSize = Media->BlockSize;
|
|
|
|
NumberOfBlocks = BufferSize / BlockSize;
|
|
|
|
if (!(Media->MediaPresent)) {
|
|
|
|
if (IdeBlkIoDevice->Cache != NULL) {
|
|
gBS->FreePool (IdeBlkIoDevice->Cache);
|
|
IdeBlkIoDevice->Cache = NULL;
|
|
}
|
|
return EFI_NO_MEDIA;
|
|
|
|
}
|
|
|
|
if ((MediaId != Media->MediaId) || MediaChange) {
|
|
|
|
if (IdeBlkIoDevice->Cache != NULL) {
|
|
gBS->FreePool (IdeBlkIoDevice->Cache);
|
|
IdeBlkIoDevice->Cache = NULL;
|
|
}
|
|
return EFI_MEDIA_CHANGED;
|
|
}
|
|
|
|
if (BufferSize % BlockSize != 0) {
|
|
return EFI_BAD_BUFFER_SIZE;
|
|
}
|
|
|
|
if (Lba > Media->LastBlock) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// if all the parameters are valid, then perform read sectors command
|
|
// to transfer data from device to host.
|
|
//
|
|
Status = AtapiReadSectors (IdeBlkIoDevice, Buffer, Lba, NumberOfBlocks);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
//
|
|
// Read blocks succeeded
|
|
//
|
|
|
|
//
|
|
// save the first block to the cache for performance
|
|
//
|
|
if (Lba == 0 && (IdeBlkIoDevice->Cache == NULL)) {
|
|
IdeBlkIoDevice->Cache = AllocatePool (BlockSize);
|
|
if (IdeBlkIoDevice->Cache!= NULL) {
|
|
CopyMem ((UINT8 *) IdeBlkIoDevice->Cache, (UINT8 *) Buffer, BlockSize);
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
/**
|
|
This function is the ATAPI implementation for WriteBlocks in the
|
|
Block I/O Protocol interface.
|
|
|
|
@param IdeBlkIoDevice Indicates the calling context.
|
|
@param MediaId The media id that the write request is for.
|
|
@param Lba The starting logical block address to write onto the device.
|
|
@param BufferSize The size of the Buffer in bytes. This must be a multiple
|
|
of the intrinsic block size of the device.
|
|
@param Buffer A pointer to the source buffer for the data. The caller
|
|
is responsible for either having implicit or explicit ownership
|
|
of the memory that data is written from.
|
|
|
|
@retval EFI_SUCCESS Write Blocks successfully.
|
|
@retval EFI_DEVICE_ERROR Write Blocks failed.
|
|
@retval EFI_NO_MEDIA There is no media in the device.
|
|
@retval EFI_MEDIA_CHANGE The MediaId is not for the current media.
|
|
@retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of the
|
|
intrinsic block size of the device.
|
|
@retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid,
|
|
or the data buffer is not valid.
|
|
|
|
@retval EFI_WRITE_PROTECTED The write protected is enabled or the media does not support write
|
|
**/
|
|
EFI_STATUS
|
|
AtapiBlkIoWriteBlocks (
|
|
IN IDE_BLK_IO_DEV *IdeBlkIoDevice,
|
|
IN UINT32 MediaId,
|
|
IN EFI_LBA Lba,
|
|
IN UINTN BufferSize,
|
|
OUT VOID *Buffer
|
|
)
|
|
{
|
|
|
|
EFI_BLOCK_IO_MEDIA *Media;
|
|
UINTN BlockSize;
|
|
UINTN NumberOfBlocks;
|
|
EFI_STATUS Status;
|
|
BOOLEAN MediaChange;
|
|
|
|
if (Lba == 0 && IdeBlkIoDevice->Cache != NULL) {
|
|
gBS->FreePool (IdeBlkIoDevice->Cache);
|
|
IdeBlkIoDevice->Cache = NULL;
|
|
}
|
|
|
|
if (Buffer == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (BufferSize == 0) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// ATAPI device media is removable,
|
|
// so it is a must to detect media first before write operation
|
|
//
|
|
MediaChange = FALSE;
|
|
Status = AtapiDetectMedia (IdeBlkIoDevice, &MediaChange);
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
if (Lba == 0 && IdeBlkIoDevice->Cache != NULL) {
|
|
gBS->FreePool (IdeBlkIoDevice->Cache);
|
|
IdeBlkIoDevice->Cache = NULL;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Get the intrinsic block size
|
|
//
|
|
Media = IdeBlkIoDevice->BlkIo.Media;
|
|
BlockSize = Media->BlockSize;
|
|
NumberOfBlocks = BufferSize / BlockSize;
|
|
|
|
if (!(Media->MediaPresent)) {
|
|
|
|
if (Lba == 0 && IdeBlkIoDevice->Cache != NULL) {
|
|
gBS->FreePool (IdeBlkIoDevice->Cache);
|
|
IdeBlkIoDevice->Cache = NULL;
|
|
}
|
|
return EFI_NO_MEDIA;
|
|
}
|
|
|
|
if ((MediaId != Media->MediaId) || MediaChange) {
|
|
|
|
if (Lba == 0 && IdeBlkIoDevice->Cache != NULL) {
|
|
gBS->FreePool (IdeBlkIoDevice->Cache);
|
|
IdeBlkIoDevice->Cache = NULL;
|
|
}
|
|
return EFI_MEDIA_CHANGED;
|
|
}
|
|
|
|
if (Media->ReadOnly) {
|
|
return EFI_WRITE_PROTECTED;
|
|
}
|
|
|
|
if (BufferSize % BlockSize != 0) {
|
|
return EFI_BAD_BUFFER_SIZE;
|
|
}
|
|
|
|
if (Lba > Media->LastBlock) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// if all the parameters are valid,
|
|
// then perform write sectors command to transfer data from host to device.
|
|
//
|
|
Status = AtapiWriteSectors (IdeBlkIoDevice, Buffer, Lba, NumberOfBlocks);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|