mirror of https://github.com/acidanthera/audk.git
376 lines
11 KiB
C
376 lines
11 KiB
C
/** @file
|
|
Implementation of the EFI Block IO Protocol for ISA Floppy driver
|
|
|
|
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
|
|
This program and the accompanying materials
|
|
are licensed and made available under the terms and conditions of the BSD License
|
|
which accompanies this distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
**/
|
|
|
|
#include "IsaFloppy.h"
|
|
|
|
/**
|
|
Reset the Block Device.
|
|
|
|
@param This Indicates a pointer to the calling context.
|
|
@param ExtendedVerification Driver may perform diagnostics on reset.
|
|
|
|
@retval EFI_SUCCESS The device was reset.
|
|
@retval EFI_DEVICE_ERROR The device is not functioning properly and could
|
|
not be reset.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
FdcReset (
|
|
IN EFI_BLOCK_IO_PROTOCOL *This,
|
|
IN BOOLEAN ExtendedVerification
|
|
)
|
|
{
|
|
FDC_BLK_IO_DEV *FdcDev;
|
|
|
|
//
|
|
// Reset the Floppy Disk Controller
|
|
//
|
|
FdcDev = FDD_BLK_IO_FROM_THIS (This);
|
|
|
|
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
|
|
EFI_PROGRESS_CODE,
|
|
EFI_P_PC_RESET | EFI_PERIPHERAL_REMOVABLE_MEDIA,
|
|
FdcDev->DevicePath
|
|
);
|
|
|
|
return FddReset (FdcDev);
|
|
}
|
|
|
|
/**
|
|
Flush the Block Device.
|
|
|
|
@param This Indicates a pointer to the calling context.
|
|
|
|
@retval EFI_SUCCESS All outstanding data was written to the device
|
|
@retval EFI_DEVICE_ERROR The device reported an error while writting back the data
|
|
@retval EFI_NO_MEDIA There is no media in the device.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
FddFlushBlocks (
|
|
IN EFI_BLOCK_IO_PROTOCOL *This
|
|
)
|
|
{
|
|
//
|
|
// Not supported yet
|
|
//
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Common report status code interface.
|
|
|
|
@param This Pointer of FDC_BLK_IO_DEV instance
|
|
@param Read Read or write operation when error occurrs
|
|
**/
|
|
VOID
|
|
FddReportStatus (
|
|
IN EFI_BLOCK_IO_PROTOCOL *This,
|
|
IN BOOLEAN Read
|
|
)
|
|
{
|
|
FDC_BLK_IO_DEV *FdcDev;
|
|
|
|
FdcDev = FDD_BLK_IO_FROM_THIS (This);
|
|
|
|
REPORT_STATUS_CODE_WITH_DEVICE_PATH (
|
|
EFI_ERROR_CODE,
|
|
((Read) ? EFI_P_EC_INPUT_ERROR : EFI_P_EC_OUTPUT_ERROR) | EFI_PERIPHERAL_REMOVABLE_MEDIA,
|
|
FdcDev->DevicePath
|
|
);
|
|
}
|
|
|
|
/**
|
|
Read BufferSize bytes from Lba into Buffer.
|
|
|
|
@param This Indicates a pointer to the calling context.
|
|
@param MediaId Id of the media, changes every time the media is replaced.
|
|
@param Lba The starting Logical Block Address to read from
|
|
@param BufferSize Size of Buffer, must be a multiple of device block size.
|
|
@param Buffer A pointer to the destination buffer for the data. The caller is
|
|
responsible for either having implicit or explicit ownership of the buffer.
|
|
|
|
@retval EFI_SUCCESS The data was read correctly from the device.
|
|
@retval EFI_DEVICE_ERROR The device reported an error while performing the read.
|
|
@retval EFI_NO_MEDIA There is no media in the device.
|
|
@retval EFI_MEDIA_CHANGED The MediaId does not matched the current device.
|
|
@retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.
|
|
@retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,
|
|
or the buffer is not on proper alignment.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
FddReadBlocks (
|
|
IN EFI_BLOCK_IO_PROTOCOL *This,
|
|
IN UINT32 MediaId,
|
|
IN EFI_LBA Lba,
|
|
IN UINTN BufferSize,
|
|
OUT VOID *Buffer
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
Status = FddReadWriteBlocks (This, MediaId, Lba, BufferSize, READ, Buffer);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
FddReportStatus (This, TRUE);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Write BufferSize bytes from Lba into Buffer.
|
|
|
|
@param This Indicates a pointer to the calling context.
|
|
@param MediaId The media ID that the write request is for.
|
|
@param Lba The starting logical block address to be written. The caller is
|
|
responsible for writing to only legitimate locations.
|
|
@param BufferSize Size of Buffer, must be a multiple of device block size.
|
|
@param Buffer A pointer to the source buffer for the data.
|
|
|
|
@retval EFI_SUCCESS The data was written correctly to the device.
|
|
@retval EFI_WRITE_PROTECTED The device can not be written to.
|
|
@retval EFI_DEVICE_ERROR The device reported an error while performing the write.
|
|
@retval EFI_NO_MEDIA There is no media in the device.
|
|
@retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
|
|
@retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.
|
|
@retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid,
|
|
or the buffer is not on proper alignment.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
FddWriteBlocks (
|
|
IN EFI_BLOCK_IO_PROTOCOL *This,
|
|
IN UINT32 MediaId,
|
|
IN EFI_LBA Lba,
|
|
IN UINTN BufferSize,
|
|
IN VOID *Buffer
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
Status = FddReadWriteBlocks (This, MediaId, Lba, BufferSize, WRITE, Buffer);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
FddReportStatus (This, FALSE);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Read or Write a number of blocks to floppy disk
|
|
|
|
@param This Indicates a pointer to the calling context.
|
|
@param MediaId Id of the media, changes every time the media is replaced.
|
|
@param Lba The starting Logical Block Address to read from
|
|
@param BufferSize Size of Buffer, must be a multiple of device block size.
|
|
@param Operation Specifies the read or write operation.
|
|
@param Buffer A pointer to the destination buffer for the data. The caller is
|
|
responsible for either having implicit or explicit ownership of the buffer.
|
|
|
|
@retval EFI_SUCCESS The data was read correctly from the device.
|
|
@retval EFI_DEVICE_ERROR The device reported an error while performing the read.
|
|
@retval EFI_NO_MEDIA There is no media in the device.
|
|
@retval EFI_MEDIA_CHANGED The MediaId does not matched the current device.
|
|
@retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.
|
|
@retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,
|
|
or the buffer is not on proper alignment.
|
|
@retval EFI_WRITE_PROTECTED The device can not be written to.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
FddReadWriteBlocks (
|
|
IN EFI_BLOCK_IO_PROTOCOL *This,
|
|
IN UINT32 MediaId,
|
|
IN EFI_LBA Lba,
|
|
IN UINTN BufferSize,
|
|
IN BOOLEAN Operation,
|
|
OUT VOID *Buffer
|
|
)
|
|
{
|
|
EFI_BLOCK_IO_MEDIA *Media;
|
|
FDC_BLK_IO_DEV *FdcDev;
|
|
UINTN BlockSize;
|
|
UINTN NumberOfBlocks;
|
|
UINTN BlockCount;
|
|
EFI_STATUS Status;
|
|
EFI_LBA Lba0;
|
|
UINT8 *Pointer;
|
|
|
|
//
|
|
// Get the intrinsic block size
|
|
//
|
|
Media = This->Media;
|
|
BlockSize = Media->BlockSize;
|
|
FdcDev = FDD_BLK_IO_FROM_THIS (This);
|
|
|
|
if (Operation == WRITE) {
|
|
if (Lba == 0) {
|
|
FdcFreeCache (FdcDev);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the drive motor on
|
|
//
|
|
Status = MotorOn (FdcDev);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
//
|
|
// Check to see if media can be detected
|
|
//
|
|
Status = DetectMedia (FdcDev);
|
|
if (EFI_ERROR (Status)) {
|
|
MotorOff (FdcDev);
|
|
FdcFreeCache (FdcDev);
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
//
|
|
// Check to see if media is present
|
|
//
|
|
if (!(Media->MediaPresent)) {
|
|
MotorOff (FdcDev);
|
|
FdcFreeCache (FdcDev);
|
|
return EFI_NO_MEDIA;
|
|
}
|
|
//
|
|
// Check to see if media has been changed
|
|
//
|
|
if (MediaId != Media->MediaId) {
|
|
MotorOff (FdcDev);
|
|
FdcFreeCache (FdcDev);
|
|
return EFI_MEDIA_CHANGED;
|
|
}
|
|
|
|
if (BufferSize == 0) {
|
|
MotorOff (FdcDev);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
if (Operation == WRITE) {
|
|
if (Media->ReadOnly) {
|
|
MotorOff (FdcDev);
|
|
return EFI_WRITE_PROTECTED;
|
|
}
|
|
}
|
|
//
|
|
// Check the parameters for this read/write operation
|
|
//
|
|
if (Buffer == NULL) {
|
|
MotorOff (FdcDev);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (BufferSize % BlockSize != 0) {
|
|
MotorOff (FdcDev);
|
|
return EFI_BAD_BUFFER_SIZE;
|
|
}
|
|
|
|
if (Lba > Media->LastBlock) {
|
|
MotorOff (FdcDev);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (((BufferSize / BlockSize) + Lba - 1) > Media->LastBlock) {
|
|
MotorOff (FdcDev);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (Operation == READ) {
|
|
//
|
|
// See if the data that is being read is already in the cache
|
|
//
|
|
if (FdcDev->Cache != NULL) {
|
|
if (Lba == 0 && BufferSize == BlockSize) {
|
|
MotorOff (FdcDev);
|
|
CopyMem ((UINT8 *) Buffer, (UINT8 *) FdcDev->Cache, BlockSize);
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// Set up Floppy Disk Controller
|
|
//
|
|
Status = Setup (FdcDev);
|
|
if (EFI_ERROR (Status)) {
|
|
MotorOff (FdcDev);
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
NumberOfBlocks = BufferSize / BlockSize;
|
|
Lba0 = Lba;
|
|
Pointer = Buffer;
|
|
|
|
//
|
|
// read blocks in the same cylinder.
|
|
// in a cylinder , there are 18 * 2 = 36 blocks
|
|
//
|
|
BlockCount = GetTransferBlockCount (FdcDev, Lba, NumberOfBlocks);
|
|
while ((BlockCount != 0) && !EFI_ERROR (Status)) {
|
|
Status = ReadWriteDataSector (FdcDev, Buffer, Lba, BlockCount, Operation);
|
|
if (EFI_ERROR (Status)) {
|
|
MotorOff (FdcDev);
|
|
FddReset (FdcDev);
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
Lba += BlockCount;
|
|
NumberOfBlocks -= BlockCount;
|
|
Buffer = (VOID *) ((UINTN) Buffer + BlockCount * BlockSize);
|
|
BlockCount = GetTransferBlockCount (FdcDev, Lba, NumberOfBlocks);
|
|
}
|
|
|
|
Buffer = Pointer;
|
|
|
|
//
|
|
// Turn the motor off
|
|
//
|
|
MotorOff (FdcDev);
|
|
|
|
if (Operation == READ) {
|
|
//
|
|
// Cache the data read
|
|
//
|
|
if (Lba0 == 0 && FdcDev->Cache == NULL) {
|
|
FdcDev->Cache = AllocateCopyPool (BlockSize, Buffer);
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
/**
|
|
Free cache for a floppy disk.
|
|
|
|
@param FdcDev A Pointer to FDC_BLK_IO_DEV instance
|
|
|
|
**/
|
|
VOID
|
|
FdcFreeCache (
|
|
IN FDC_BLK_IO_DEV *FdcDev
|
|
)
|
|
{
|
|
if (FdcDev->Cache != NULL) {
|
|
FreePool (FdcDev->Cache);
|
|
FdcDev->Cache = NULL;
|
|
}
|
|
}
|