/** @file Implementation of the EFI Block IO Protocol for ISA Floppy driver Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #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; } }