#if defined(JAZZ) && !defined(DUO)

/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    jxfboot.c

Abstract:

    This module implements the floppy disk boot driver for the Jazz system.

Author:

    Darryl E. Havens (darrylh) 28-Aug-1989

Environment:

    Kernel mode only, raised IRQL, generally self-contained.


Revision History:

--*/

#include "fwp.h"
#include "jazzprom.h"
#include "jazzint.h"
#include "ntdddisk.h"
#include "flo_data.h"
#include "fwstring.h"


//
// Define local static data.
//

UCHAR DebugByte[8];
ULONG MotorStatus;
PDRIVE_MEDIA_CONSTANTS CurrentDriveMediaConstants;

//
// Define timeout constants.
//

#define MICROSECONDS_10                 10
#define MICROSECONDS_250                250
#define MILLISECONDS_15                 (15 * 1000)
#define MILLISECONDS_500                (500 * 1000)
#define SECONDS_2                       (2 * 1000 * 1000)
#define FW_FLOPPY_TIMEOUT               2

//
// Define the number of times an operation is retried before it is considered
// to be a hard error.
//

#define RETRY_COUNT  8

//
// Define the MINIMUM macro.
//

#define MINIMUM( x, y ) ( x <= y ? x : y )

//
// Define floppy device register structure.
//

typedef struct _FLOPPY_REGISTERS {
    UCHAR StatusRegisterA;
    UCHAR StatusRegisterB;
    UCHAR DigitalOutput;
    UCHAR Reserved1;
    union {
        UCHAR MainStatus;
        UCHAR DataRateSelect;
    } MsrDsr;
    UCHAR Fifo;
    UCHAR Reserved2;
    union {
        UCHAR DigitalInput;
        UCHAR ConfigurationControl;
    } DirCcr;
} FLOPPY_REGISTERS, *PFLOPPY_REGISTERS;

//
// Define pointer to the floppy registers.
//

#define FLOPPY_CONTROL ((volatile PFLOPPY_REGISTERS)FLOPPY_VIRTUAL_BASE)

PUCHAR Floppy0Path = "multi(0)disk(0)fdisk(0)";
PUCHAR Floppy1Path = "multi(0)disk(0)fdisk(1)";


ARC_STATUS
FloppyClose (
    IN ULONG FileId
    );

ARC_STATUS
FloppyMount (
    IN PCHAR MountPath,
    IN MOUNT_OPERATION Operation
    );

ARC_STATUS
FloppyOpen (
    IN PCHAR OpenPath,
    IN OPEN_MODE OpenMode,
    OUT PULONG FileId
    );

ARC_STATUS
FloppyRead (
    IN ULONG FileId,
    IN PVOID Buffer,
    IN ULONG Length,
    OUT PULONG Count
    );

ARC_STATUS
FloppyGetReadStatus (
    IN ULONG FileId
    );

ARC_STATUS
FloppySeek (
    IN ULONG FileId,
    IN PLARGE_INTEGER Offset,
    IN SEEK_MODE SeekMode
    );

ARC_STATUS
FloppyWrite (
    IN ULONG FileId,
    IN PVOID Buffer,
    IN ULONG Length,
    OUT PULONG Count
    );

ARC_STATUS
FloppyGetFileInformation (
    IN ULONG FileId,
    OUT PFILE_INFORMATION Finfo
    );

ARC_STATUS
FloppyBootIO(
    IN PMDL MdlAddress,
    IN ULONG StartingBlock,
    IN ULONG FileId,
    IN BOOLEAN ReadWrite
    );

VOID
ClearFloppyFifo (
    IN VOID
    );

ULONG
ReadFloppyFifo (
    IN PUCHAR Buffer
    );

VOID
WriteFloppyFifo(
    IN PUCHAR Buffer,
    IN ULONG Size
    );

//
// Declare and Initialize the floppy disk device entry table.
//

BL_DEVICE_ENTRY_TABLE FloppyEntryTable = {
    FloppyClose,
    FloppyMount,
    FloppyOpen,
    FloppyRead,
    FloppyGetReadStatus,
    FloppySeek,
    FloppyWrite,
    FloppyGetFileInformation,
    (PARC_SET_FILE_INFO_ROUTINE)NULL
    };

//
// Define prototypes for all routines used by this module.
//


ARC_STATUS
FloppyBootClose(
    );

BOOLEAN
Recalibrate (
    UCHAR   DriveNumber
    );

VOID
FloppyBootSetup(
    VOID
    );

UCHAR
ReceiveByte (
    );

BOOLEAN
SendByte(
    IN UCHAR SourceByte
    );

ARC_STATUS
FloppyDetermineMediaType(
    IN OUT PFLOPPY_CONTEXT FloppyContext
    );

ARC_STATUS
FloppyDatarateSpecifyConfigure(
    IN DRIVE_MEDIA_TYPE DriveMediaType,
    IN UCHAR DriveNumber
    );


ARC_STATUS
FloppyClose (
    IN ULONG FileId
    )

/*++

Routine Description:

    This function closes the file table entry specified by the file id.

Arguments:

    FileId - Supplies the file table index.

Return Value:

    ESUCCESS is returned

--*/

{
    FloppyBootClose();
    BlFileTable[FileId].Flags.Open = 0;
    return ESUCCESS;
}

ARC_STATUS
FloppyMount (
    IN PCHAR MountPath,
    IN MOUNT_OPERATION Operation
    )

/*++

Routine Description:


Arguments:


Return Value:


--*/

{
    return ESUCCESS;
}

ARC_STATUS
FloppyOpen (
    IN PCHAR OpenPath,
    IN OPEN_MODE OpenMode,
    IN OUT PULONG FileId
    )

/*++

Routine Description:


Arguments:


Return Value:


--*/

{
    PCONFIGURATION_COMPONENT FloppyComponent, FloppyController;
    UCHAR Data[sizeof(CM_PARTIAL_RESOURCE_LIST) +
               sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) * 8 +
               sizeof(CM_FLOPPY_DEVICE_DATA)];
    PCM_PARTIAL_RESOURCE_LIST List = (PCM_PARTIAL_RESOURCE_LIST)Data;
    PCM_FLOPPY_DEVICE_DATA FloppyData;
    ULONG DriveNumber;
    ULONG Index;
    ARC_STATUS ArcStatus;
    CHAR TempBuffer[SECTOR_SIZE + 32];
    PCHAR TempPointer;
    ULONG Count;
    UCHAR mediaDescriptor;
    MEDIA_TYPE mediaType;
    DRIVE_MEDIA_TYPE driveMediaType;
    ULONG DriveType;
    ULONG ConfigDriveType;

    //
    // Get the drive number from the pathname.
    //

    if (FwGetPathMnemonicKey(OpenPath, "fdisk", &DriveNumber)) {
        return ENODEV;
    }

    //
    // Default to 1.44MB floppy
    //

    BlFileTable[*FileId].u.FloppyContext.DriveType = DRIVE_TYPE_1440;

    //
    // Look in the configuration database for the floppy device data to
    // determine the size of the floppy drive.
    //

    FloppyComponent = FwGetComponent(OpenPath);
    if ((FloppyComponent != NULL) &&
        (FloppyComponent->Type == FloppyDiskPeripheral)) {
        if (FwGetConfigurationData(List, FloppyComponent) == ESUCCESS) {
            FloppyData = (PCM_FLOPPY_DEVICE_DATA)&List->PartialDescriptors[List->Count];
            if (strcmp(FloppyData->Size,"5.25")==0) {
                BlFileTable[*FileId].u.FloppyContext.DriveType = DRIVE_TYPE_1200;
            } else {
                if (strcmp(FloppyData->Size,"3.5")==0) {
                    if (FloppyData->MaxDensity == 2880) {
                        BlFileTable[*FileId].u.FloppyContext.DriveType = DRIVE_TYPE_2880;
                    } else {
                        BlFileTable[*FileId].u.FloppyContext.DriveType = DRIVE_TYPE_1440;
                    }
                }
            }
        }
    }

    ConfigDriveType = BlFileTable[*FileId].u.FloppyContext.DriveType;
    BlFileTable[*FileId].u.FloppyContext.DiskId = DriveNumber;
    BlFileTable[*FileId].Position.LowPart=0;
    BlFileTable[*FileId].Position.HighPart=0;

    //
    // Enable the drive and start the motor via the DOR.
    //

    if (MotorStatus != DriveNumber) {
        WRITE_REGISTER_UCHAR(&FLOPPY_CONTROL->DigitalOutput,
                             ((0xc + DriveNumber) + (1 << (DriveNumber + 4))));
        MotorStatus = DriveNumber;

        //
        // Wait for at least 500ms to ensure that the motor really is running.
        //

        FwStallExecution(MILLISECONDS_500);
    }

    //
    // Determine the disk density.
    //

    ClearFloppyFifo();
    ArcStatus = FloppyDetermineMediaType(&BlFileTable[*FileId].u.FloppyContext);
    if (ArcStatus == EIO) {
        FloppyClose(*FileId);

        //
        // Reset the floppy, it seems to get in a bad state.
        //

        FloppyBootSetup();
        return(ArcStatus);
    } else if (ArcStatus != ESUCCESS) {

        //
        // The floppy was not readable, so try the other floppy type.
        //

        DriveType = BlFileTable[*FileId].u.FloppyContext.DriveType;
        if ((DriveType == DRIVE_TYPE_1440) || (DriveType == DRIVE_TYPE_2880)) {
            BlFileTable[*FileId].u.FloppyContext.DriveType = DRIVE_TYPE_1200;
        } else {
            BlFileTable[*FileId].u.FloppyContext.DriveType = DRIVE_TYPE_1440;
        }

        ArcStatus = FloppyDetermineMediaType(&BlFileTable[*FileId].u.FloppyContext);
        if (ArcStatus == EIO) {
            FloppyClose(*FileId);
            return(ArcStatus);
        } else if (ArcStatus != ESUCCESS) {
            BlFileTable[*FileId].u.FloppyContext.DriveType = DriveType;
//            FwPrint("Unrecognized floppy format\r\n");
            FloppyClose(*FileId);
            return(ArcStatus);
        }
    }

    //
    // Read the first sector to get the media descriptor byte.
    //

    TempPointer =  (PVOID) ((ULONG) (TempBuffer + KeGetDcacheFillSize() - 1)
        & ~(KeGetDcacheFillSize() - 1));
    ArcStatus = FloppyRead(*FileId, TempPointer, SECTOR_SIZE, &Count);

    if (ArcStatus != ESUCCESS) {
//        FwPrint("Error opening floppy\r\n");
        FloppyClose(*FileId);
        return(ArcStatus);
    }

    //
    // Check the media descriptor byte to verify that we have the right
    // drive and media type.
    //

    DriveType = BlFileTable[*FileId].u.FloppyContext.DriveType;
    mediaDescriptor = *( TempPointer + MEDIA_DESCRIPTOR_OFFSET );
    mediaType = 0;

    switch ( mediaDescriptor ) {

    case MEDIA_DESCRIPTOR_160K:
        mediaType = F5_160_512;
        DriveType = DRIVE_TYPE_1200;
        break;

    case MEDIA_DESCRIPTOR_180K:
        mediaType = F5_180_512;
        DriveType = DRIVE_TYPE_1200;
        break;

    case MEDIA_DESCRIPTOR_320K:
        mediaType = F5_320_512;
        DriveType = DRIVE_TYPE_1200;
        break;

    case MEDIA_DESCRIPTOR_360K:
        mediaType = F5_360_512;
        DriveType = DRIVE_TYPE_1200;
        break;

    case MEDIA_DESCRIPTOR_720K_OR_1220K:

        //
        // The following code tries to take care of the case when the floppy
        // is really a 5 1/4" drive but the firmware thinks its 3 1/2".  A
        // 1.2 MByte floppy can be read with the 1.44 MByte parameters, but
        // the descriptor byte will be MEDIA_DESCRIPTOR_720K_OR_1220K.  Check
        // if the parameters are really for 720 K, otherwise default to
        // 1.2 MByte.
        //

        if ((DriveType == DRIVE_TYPE_1440) &&
            (BlFileTable[*FileId].u.FloppyContext.SectorsPerTrack == 9)) {
            mediaType = F3_720_512;
        } else {
            mediaType = F5_1Pt2_512;
            DriveType = DRIVE_TYPE_1200;
        }
        break;

    case MEDIA_DESCRIPTOR_1440K_OR_2880K:
        mediaType = F3_1Pt44_512;
        DriveType = DRIVE_TYPE_1440;
        break;

    default:
        break;

    }

    if ( mediaType != 0 ) {

        //
        // Find the constants for this media type.
        //

        driveMediaType = DriveMediaLimits[DriveType].HighestDriveMediaType;
        while ( ( DriveMediaConstants[driveMediaType].MediaType != mediaType ) &&
                ( driveMediaType > DriveMediaLimits[DriveType].LowestDriveMediaType ) ) {

            driveMediaType--;
        }

        //
        // Set the sectors per track and the drive type in the floppy
        // context record.
        //

        BlFileTable[*FileId].u.FloppyContext.SectorsPerTrack =
                DriveMediaConstants[driveMediaType].SectorsPerTrack;
        BlFileTable[*FileId].u.FloppyContext.DriveType = DriveType;
    }

    //
    // If the floppy drive type has changed, update the configuration database
    // with the correct drive type.  BUGBUG Doesn't do 2.88 MByte floppies.
    //

    if (DriveType != ConfigDriveType) {

        if (DriveType == DRIVE_TYPE_1200) {
            strcpy(FloppyData->Size,"5.25");
            FloppyData->MaxDensity = 1200;
        } else {
            strcpy(FloppyData->Size,"3.5");
            FloppyData->MaxDensity = 1440;
        }

        //
        // Get a pointer to the floppy controller component.
        //

        if ((FloppyController = FwGetParent(FloppyComponent)) != NULL) {

            //
            // Delete the old entry, note that this does not actually delete the
            // data in the database, it only changes the pointers, so that the
            // AddChild call can still use the old component data structure.
            //

            if (FwDeleteComponent(FloppyComponent) == ESUCCESS) {

                //
                // Add back the modified floppy structure.
                //

                FwAddChild(FloppyController, FloppyComponent, List);
            }
        }
    }

    return ESUCCESS;
}

ARC_STATUS
FloppyRead (
    IN ULONG FileId,
    IN PVOID Buffer,
    IN ULONG Length,
    OUT PULONG Count
    )

/*++

Routine Description:

    This function reads data from the floppy starting at the position
    specified in the file table.


Arguments:

    FileId - Supplies the file table index.

    Buffer - Supplies a poiner to the buffer that receives the data
        read.

    Length - Supplies the number of bytes to be read.

    Count - Supplies a pointer to a variable that receives the number of
        bytes actually read.

Return Value:


    The read completion status is returned.

--*/

{

    ARC_STATUS ArcStatus;
    ULONG FrameNumber;
    ULONG Index;
    ULONG Limit;
    PMDL MdlAddress;
    UCHAR MdlBuffer[sizeof(MDL) + ((64 / 4) + 1) * sizeof(ULONG)];
    ULONG NumberOfPages;
    ULONG Offset;
    PULONG PageFrame;
    ULONG Position;
    CHAR TempBuffer[SECTOR_SIZE + 32];
    PCHAR TempPointer;

    //
    // If the requested size of the transfer is zero return ESUCCESS
    //
    if (Length==0) {
        return ESUCCESS;
    }
    //
    // If the current position is not at a sector boundary , then
    // read the first and/or last sector separately and copy the data.
    //

    Offset = BlFileTable[FileId].Position.LowPart & (SECTOR_SIZE - 1);
    if (Offset != 0) {
        //
        // Adjust position to the sector boundary, align the transfer address
        // and read that first sector.
        //
        BlFileTable[FileId].Position.LowPart -= Offset;
        TempPointer =  (PVOID) ((ULONG) (TempBuffer + KeGetDcacheFillSize() - 1)
            & ~(KeGetDcacheFillSize() - 1));

        ArcStatus = FloppyRead(FileId, TempPointer, SECTOR_SIZE, Count);

        //
        // If the transfer was not successful, then reset the position
        // and return the completion status.
        //
        if (ArcStatus != ESUCCESS) {
            BlFileTable[FileId].Position.LowPart += Offset;
            return ArcStatus;
        }

        //
        // If the length of read is less than the number of bytes from
        // the offset to the end of the sector, then copy only the number
        // of bytes required to fulfil the request. Otherwise copy to the end
        // of the sector and, read the remaining data.
        //

        if ((SECTOR_SIZE - Offset) > Length) {
            Limit = Offset + Length;
        } else {
            Limit = SECTOR_SIZE;
        }

        //
        // Copy the data to the specified buffer.
        //
        for (Index = Offset; Index < Limit; Index += 1) {
            *((PCHAR)Buffer) = *(TempPointer + Index);
            Buffer = (PCHAR)Buffer + 1;
        }

        //
        // Adjust the current position and
        // Read the remaining part of the specified transfer.
        //

        BlFileTable[FileId].Position.LowPart -= SECTOR_SIZE-Limit;
        Position = BlFileTable[FileId].Position.LowPart;
        ArcStatus = FloppyRead(FileId,
                               Buffer,
                               Length - (Limit - Offset),
                               Count);

        //
        // If the transfer was not successful, then reset the device
        // position and return the completion status.
        //

        if (ArcStatus != ESUCCESS) {
            BlFileTable[FileId].Position.LowPart = Position;
            return ArcStatus;
        } else {
            *Count = Length;
            return ESUCCESS;
        }
    } else {
        //
        // if the size of requested data is not a multiple of the sector
        // size then read the last sector separately.
        //
        if (Length & (SECTOR_SIZE - 1)) {
            Position = BlFileTable[FileId].Position.LowPart;
            ArcStatus = FloppyRead(FileId,
                                   Buffer,
                                   Length & (~(SECTOR_SIZE - 1)),
                                   Count);

            //
            // If the transfer was not successful, then reset the device
            // position and return the completion status.
            //
            if (ArcStatus != ESUCCESS) {
                BlFileTable[FileId].Position.LowPart = Position;
                return ArcStatus;
            }

            //
            // Read the last sector and copy the requested data.
            //
            TempPointer =  (PVOID) ((ULONG) (TempBuffer + KeGetDcacheFillSize() - 1)
                & ~(KeGetDcacheFillSize() - 1));

            ArcStatus = FloppyRead(FileId, TempPointer, SECTOR_SIZE, Count);

            //
            // If the transfer was not successful return the completion status.
            //
            if (ArcStatus != ESUCCESS) {
                return ArcStatus;
            }

            //
            // Copy the data to the specified buffer.
            //
            Buffer = (PCHAR)Buffer + (Length & (~(SECTOR_SIZE - 1)));
            Limit =  Length & (SECTOR_SIZE - 1);
            for (Index = 0; Index < Limit; Index += 1) {
                *((PCHAR)Buffer) = *(TempPointer + Index);
		Buffer = (PCHAR)Buffer + 1;
            }
            BlFileTable[FileId].Position.LowPart -= SECTOR_SIZE - Limit;
            *Count = Length;
            return ESUCCESS;



        } else {

            //
            // Build the memory descriptor list.
            //

            MdlAddress = (PMDL)&MdlBuffer[0];
            MdlAddress->Next = NULL;
            MdlAddress->Size = sizeof(MDL) +
                  ADDRESS_AND_SIZE_TO_SPAN_PAGES(Buffer, Length) * sizeof(ULONG);
            MdlAddress->StartVa = (PVOID)PAGE_ALIGN(Buffer);
            MdlAddress->ByteCount = Length;
            MdlAddress->ByteOffset = BYTE_OFFSET(Buffer);
            PageFrame = (PULONG)(MdlAddress + 1);
            FrameNumber = (((ULONG)MdlAddress->StartVa) & 0x1fffffff) >> PAGE_SHIFT;
            NumberOfPages = (MdlAddress->ByteCount +
                  MdlAddress->ByteOffset + PAGE_SIZE - 1) >> PAGE_SHIFT;
            for (Index = 0; Index < NumberOfPages; Index += 1) {
                *PageFrame++ = FrameNumber++;
            }

            //
            // Flush I/O buffers and read from the boot device.
            //

            HalFlushIoBuffers(MdlAddress, TRUE, TRUE);
            ArcStatus = FloppyBootIO(MdlAddress,
                                     BlFileTable[FileId].Position.LowPart >> SECTOR_SHIFT,
                                     FileId,
                                     FALSE);

            if (ArcStatus == ESUCCESS) {
                BlFileTable[FileId].Position.LowPart += Length;
                *Count = Length;
                return ESUCCESS;
            } else {
                *Count = 0;
                return EIO;
            }
        }
    }
}

ARC_STATUS
FloppyWrite (
    IN ULONG FileId,
    IN PVOID Buffer,
    IN ULONG Length,
    OUT PULONG Count
    )

/*++

Routine Description:

    This function writes data to the floppy starting at the position
    specified in the file table.


Arguments:

    FileId - Supplies the file table index.

    Buffer - Supplies a poiner to the buffer that contains the data
        to be written.

    Length - Supplies the number of bytes to be writtes.

    Count - Supplies a pointer to a variable that receives the number of
        bytes actually written.

Return Value:


    The write completion status is returned.

--*/

{

    ARC_STATUS ArcStatus;
    ULONG FrameNumber;
    ULONG Index;
    ULONG Limit;
    PMDL MdlAddress;
    UCHAR MdlBuffer[sizeof(MDL) + ((64 / 4) + 1) * sizeof(ULONG)];
    ULONG NumberOfPages;
    ULONG Offset;
    PULONG PageFrame;
    ULONG Position;
    CHAR TempBuffer[SECTOR_SIZE + 32];
    PCHAR TempPointer;

    //
    // If the requested size of the transfer is zero return ESUCCESS
    //
    if (Length==0) {
        return ESUCCESS;
    }
    //
    // If the current position is not at a sector boundary , then
    // read the first and/or last sector separately and copy the data.
    //

    Offset = BlFileTable[FileId].Position.LowPart & (SECTOR_SIZE - 1);
    if (Offset != 0) {
        //
        // Adjust position to the sector boundary, align the transfer address
        // and read that first sector.
        //
        BlFileTable[FileId].Position.LowPart -= Offset;
        TempPointer =  (PVOID) ((ULONG) (TempBuffer + KeGetDcacheFillSize() - 1)
            & ~(KeGetDcacheFillSize() - 1));

        ArcStatus = FloppyRead(FileId, TempPointer, SECTOR_SIZE, Count);

        //
        // If the transfer was not successful, then reset the position
        // and return the completion status.
        //
        if (ArcStatus != ESUCCESS) {
            BlFileTable[FileId].Position.LowPart += Offset;
            return ArcStatus;
        } else {
            //
            // Reset the position as it was before the read.
            //
            BlFileTable[FileId].Position.LowPart -= SECTOR_SIZE;
        }

        //
        // If the length of write is less than the number of bytes from
        // the offset to the end of the sector, then copy only the number
        // of bytes required to fulfil the request. Otherwise copy to the end
        // of the sector and, read the remaining data.
        //

        if ((SECTOR_SIZE - Offset) > Length) {
            Limit = Offset + Length;
        } else {
            Limit = SECTOR_SIZE;
        }

        //
        // Merge the data from the specified buffer.
        //
        for (Index = Offset; Index < Limit; Index += 1) {
            *(TempPointer + Index) = *((PCHAR)Buffer);
            Buffer = (PCHAR)Buffer + 1;
        }

        //
        // Write the modified sector.
        //
        ArcStatus = FloppyWrite(FileId, TempPointer, SECTOR_SIZE, Count);

        if (ArcStatus != ESUCCESS) {
            return ArcStatus;
        }

        //
        // Adjust the current position and
        // Write the remaining part of the specified transfer.
        //

        BlFileTable[FileId].Position.LowPart -= SECTOR_SIZE-Limit;
        Position = BlFileTable[FileId].Position.LowPart;
        ArcStatus = FloppyWrite(FileId,
                               Buffer,
                               Length - (Limit - Offset),
                               Count);

        //
        // If the transfer was not successful, then reset the device
        // position and return the completion status.
        //

        if (ArcStatus != ESUCCESS) {
            BlFileTable[FileId].Position.LowPart = Position;
            return ArcStatus;
        } else {
            *Count = Length;
            return ESUCCESS;
        }
    } else {
        //
        // if the size of requested data is not a multiple of the sector
        // size then write the last sector separately.
        //
        if (Length & (SECTOR_SIZE - 1)) {

            //
            // Do the transfer of the complete sectors in the middle
            //
            Position = BlFileTable[FileId].Position.LowPart;
            ArcStatus = FloppyWrite(FileId,
                                   Buffer,
                                   Length & (~(SECTOR_SIZE - 1)),
                                   Count);

            //
            // If the transfer was not successful, then reset the device
            // position and return the completion status.
            //
            if (ArcStatus != ESUCCESS) {
                BlFileTable[FileId].Position.LowPart = Position;
                return ArcStatus;
            }

            //
            // Read the last sector and copy the requested data.
            //
            TempPointer =  (PVOID) ((ULONG) (TempBuffer + KeGetDcacheFillSize() - 1)
                & ~(KeGetDcacheFillSize() - 1));

            ArcStatus = FloppyRead(FileId, TempPointer, SECTOR_SIZE, Count);

            //
            // If the transfer was not successful return the completion status.
            //
            if (ArcStatus != ESUCCESS) {
                return ArcStatus;
            }

            //
            // Copy the data to the specified buffer.
            //
            Buffer = (PCHAR)Buffer + (Length & (~(SECTOR_SIZE - 1)));
            Limit =  Length & (SECTOR_SIZE - 1);
            for (Index = 0; Index < Limit; Index += 1) {
                *(TempPointer + Index) = *((PCHAR)Buffer);
                Buffer = (PCHAR)Buffer + 1;
            }
            //
            // Adjust the position and write the data.
            //
            BlFileTable[FileId].Position.LowPart -= SECTOR_SIZE;
            ArcStatus = FloppyWrite(FileId, TempPointer, SECTOR_SIZE, Count);

            //
            // Set the position for the requested transfer
            //
            BlFileTable[FileId].Position.LowPart -= SECTOR_SIZE - Limit;

            *Count = Length;
            return ArcStatus;

        } else {

            //
            // Build the memory descriptor list.
            //

            MdlAddress = (PMDL)&MdlBuffer[0];
            MdlAddress->Next = NULL;
            MdlAddress->Size = sizeof(MDL) +
                  ADDRESS_AND_SIZE_TO_SPAN_PAGES(Buffer, Length) * sizeof(ULONG);
            MdlAddress->StartVa = (PVOID)PAGE_ALIGN(Buffer);
            MdlAddress->ByteCount = Length;
            MdlAddress->ByteOffset = BYTE_OFFSET(Buffer);
            PageFrame = (PULONG)(MdlAddress + 1);
            FrameNumber = (((ULONG)MdlAddress->StartVa) & 0x1fffffff) >> PAGE_SHIFT;
            NumberOfPages = (MdlAddress->ByteCount +
                  MdlAddress->ByteOffset + PAGE_SIZE - 1) >> PAGE_SHIFT;
            for (Index = 0; Index < NumberOfPages; Index += 1) {
                *PageFrame++ = FrameNumber++;
            }

            //
            // Flush I/O buffers and write to the boot device.
            //

            HalFlushIoBuffers(MdlAddress, FALSE, TRUE);
            ArcStatus = FloppyBootIO(MdlAddress,
                                     BlFileTable[FileId].Position.LowPart >> SECTOR_SHIFT,
                                     FileId,
                                     TRUE);

            if (ArcStatus == ESUCCESS) {
                BlFileTable[FileId].Position.LowPart += Length;
                *Count = Length;
                return ESUCCESS;
            } else {
                *Count = 0;
                return EIO;
            }
        }
    }
}

ARC_STATUS
FloppyGetReadStatus (
    IN ULONG FileId
    )

/*++

Routine Description:


Arguments:


Return Value:


--*/

{
    return ESUCCESS;
}

ARC_STATUS
FloppySeek (
    IN ULONG FileId,
    IN PLARGE_INTEGER Offset,
    IN SEEK_MODE SeekMode
    )

/*++

Routine Description:

    This function sets the device position to the specified offset for
    the specified file id.

Arguments:

    FileId - Supplies the file table index.

    Offset - Supplies to new device position.

    SeekMode - Supplies the mode for the position.

Return Value:

    ESUCCESS is returned.

--*/

{

    //
    // Set the current device position as specifed by the seek mode.
    //

    if (SeekMode == SeekAbsolute) {
        BlFileTable[FileId].Position.LowPart = Offset->LowPart;

    } else if (SeekMode == SeekRelative) {
        BlFileTable[FileId].Position.LowPart += Offset->LowPart;
    }

    return ESUCCESS;
}

ARC_STATUS
FloppyGetFileInformation (
    IN ULONG FileId,
    OUT PFILE_INFORMATION Finfo
    )

/*++

Routine Description:

    This routine returns the floppy size.

Arguments:

    FileId - Supplies the file table index.

    Finfo - Supplies a pointer to where the File Information is stored.

Return Value:

    ESUCCESS is returned.

--*/

{

    RtlZeroMemory(Finfo, sizeof(FILE_INFORMATION));

    switch (BlFileTable[FileId].u.FloppyContext.DriveType) {

        case DRIVE_TYPE_1200: Finfo->EndingAddress.LowPart = 1200 * 1024;
                              break;
        case DRIVE_TYPE_1440: Finfo->EndingAddress.LowPart = 1440 * 1024;
                              break;
        case DRIVE_TYPE_2880: Finfo->EndingAddress.LowPart = 2880 * 1024;
                              break;

        default             : return EINVAL;
    }

    Finfo->CurrentPosition = BlFileTable[FileId].Position;
    Finfo->Type = FloppyDiskPeripheral;
    return ESUCCESS;
}


VOID
FloppyBootSetup(
    VOID
    )

/*++

Routine Description:

    This routine is invoked to initialize the floppy boot device before any
    other floppy operations are attempted.  This routine performs the following
    operations to initialize the device:

        o  Clear the reset and DMA gate flags in the DOR
        o  Reset the floppy by writing the s/w reset in the DSR
        o  Set the program data rate in the CCR
        o  Issue a sense interrupt command and read the four statuses back
        o  Issue a configure command
        o  Issue a specify command

Arguments:

    None.

Return Value:

    None.

--*/

{

    ULONG i,j;
    //
    // Begin by clearing the reset and DMA gate flags in the DOR.
    //

    WRITE_REGISTER_UCHAR(&FLOPPY_CONTROL->DigitalOutput, 0x0c);

    //
    // Reset the floppy controller by setting the s/w reset bit in the DSR.
    //

    WRITE_REGISTER_UCHAR(&FLOPPY_CONTROL->MsrDsr.DataRateSelect, 0x80);

    //
    // Set the data rate in the CCR.
    //

//    WRITE_REGISTER_UCHAR(&FLOPPY_CONTROL->DirCcr.ConfigurationControl, 0);

    if (FwWaitForDeviceInterrupt(1 << (FLOPPY_VECTOR - DEVICE_VECTORS - 1),
                                 FW_FLOPPY_TIMEOUT)) {
//        FwPrint("Floppy setup timeout\r\n");
        return;
    }


    //
    // Issue the sense interrupt command and read back the status and
    // relative cylinder number from the controller.  This is done for
    // each of the four possible devices.  Note that the output is always
    // ignored.
    //

    for (i = j = 0; i <= 3; i++) {
        SendByte(COMMND_SENSE_INTERRUPT);
        DebugByte[j++] = ReceiveByte();
        DebugByte[j++] = ReceiveByte();
    }

    //
    // Issue the configuration command.
    //

    SendByte( COMMND_CONFIGURE );  // command
    SendByte( 0x00 );           // required 0
    SendByte( 0x58 );           // implied seeks, disable polling & threshold = 8
    SendByte( 0x00 );           // precompensation track = 0

    //
    // Issue the specify command.
    //

    SendByte( COMMND_SPECIFY );    // command
    SendByte( 0xdf );           // step rate time=d, head unload=f
    SendByte( 0x03 );           // head load=1, DMA disabled

    return;
}

VOID
FloppyInitialize(
    IN OUT PDRIVER_LOOKUP_ENTRY LookupTable,
    IN ULONG Entries
    )

/*++

Routine Description:

    This routine initializes the FloppyDriver.

Arguments:

    LookupTable - Pointer to the driver lookup table where the pathnames
                  recognized by this driver will be stored.

    Entries     - Number of entries in the table.

Return Value:

    None.

--*/

{
    PCONFIGURATION_COMPONENT FloppyComponent;
    CHAR FloppyPath[32];
    ULONG Drive;
    //
    // Set motor status to all motors stopped.
    //

    MotorStatus = MAXLONG;

    FloppyBootSetup();

    //
    // Default to floppy 0 in case no configuration is present in the NVRAM
    //
    LookupTable->DevicePath = Floppy0Path;
    LookupTable->DispatchTable = &FloppyEntryTable;

    //
    // Get the floppy configuration information.
    //
    FloppyComponent = FwGetComponent(Floppy0Path);
    if ((FloppyComponent != NULL) &&
        (FloppyComponent->Type == FloppyDiskPeripheral)) {

        //
        // Initialize the lookup table.
        //

        LookupTable->DevicePath = Floppy0Path;
        LookupTable->DispatchTable = &FloppyEntryTable;
        LookupTable++;

        //
        // Return if no more room in the lookup table.
        //

        if (Entries == 1) {
            return;
        }
    }
    FloppyComponent = FwGetComponent(Floppy1Path);
    if ((FloppyComponent != NULL) &&
        (FloppyComponent->Type == FloppyDiskPeripheral)) {

        //
        // Initialize the lookup table.
        //
        LookupTable->DevicePath = Floppy1Path;
        LookupTable->DispatchTable = &FloppyEntryTable;
    }
}

ARC_STATUS
FloppyBootClose(
    )

/*++

Routine Description:

    This routine shuts down the floppy after the boot has taken place.

Arguments:

    None.

Return Value:

    Normal, successful completion status.

--*/

{

    //
    // Turn the floppy drive's motor off and indicate that the
    // motor has been shut off.
    //

    WRITE_REGISTER_UCHAR(&FLOPPY_CONTROL->DigitalOutput, 0xc);
    MotorStatus = MAXLONG;


    return ESUCCESS;
}

ARC_STATUS
FloppyBootIO (
    IN PMDL Mdl,
    IN ULONG StartingBlock,
    IN ULONG FileId,
    IN BOOLEAN ReadWrite
    )

/*++

Routine Description:

    This routine reads or writes blocks from the floppy into the buffer described by
    the MDL.  The size of the read is the number of bytes mapped by the MDL
    and the blocks read start at StartingBlock.

Arguments:

    Mdl - Memory Descriptor List for buffer.

    StartingBlock - Block to begin the read operation.

    FileId - The file identifier of the floppy drive to access.

    ReadWrite - Specifies the kind of transfer to be done
              TRUE = WRITE
              FALSE = READ

Return Value:

    The function value is the status of the operation.


--*/

{

    UCHAR Cylinder;
    UCHAR Head = 0;
    UCHAR Sector;
    UCHAR EndSector;
    ULONG BlockCount;
    ULONG TransferSize;
    ULONG TransferedBytes;
    ULONG i,j,k;
    PUCHAR Buffer;
    BOOLEAN Success;
    ARC_STATUS Status = ESUCCESS;
    UCHAR DriveNumber;
    ULONG SectorsPerTrack;

    DriveNumber = BlFileTable[FileId].u.FloppyContext.DiskId;
    SectorsPerTrack = BlFileTable[FileId].u.FloppyContext.SectorsPerTrack;

    //
    // Enable the drive and start the motor via the DOR.
    //

    if (MotorStatus != DriveNumber) {
        WRITE_REGISTER_UCHAR(&FLOPPY_CONTROL->DigitalOutput,
                             ((0xc + DriveNumber) + (1 << (DriveNumber + 4))));
        MotorStatus = DriveNumber;

        //
        // Wait for at least 500ms to ensure that the motor really is running.
        //

        FwStallExecution(MILLISECONDS_500);
    }

    //
    // Get the number of blocks that need to be read in order to fulfill
    // this request.  Also set the base address of the caller's buffer.
    //

    Buffer = ((PUCHAR) Mdl->StartVa) + Mdl->ByteOffset;

    //
    // Get the parameters for the read command.
    //

    Cylinder = StartingBlock / (SectorsPerTrack * 2);
    Sector = (StartingBlock % SectorsPerTrack) + 1;
    Head = (StartingBlock / SectorsPerTrack) % 2;

    ClearFloppyFifo();

    //
    // Loop reading blocks from the device until the request has been
    // satisfied.
    //

    for (BlockCount = Mdl->ByteCount >> 9; BlockCount > 0; ) {

        //
        // Determine the size of this read based on the number of blocks
        // required and where the current sector is on the current track.
        //

        EndSector = MINIMUM( SectorsPerTrack, (Sector + (BlockCount - 1)) );
        TransferSize = (EndSector - Sector) + 1;
        BlockCount -= TransferSize;
        TransferSize <<= 9;

        //
        // Attempt to read the block(s) up to RETRY_COUNT times.
        //

        for (k = 0; k < RETRY_COUNT; k++) {

            //
            // Assume that the operation will be successful.
            //

            Success = TRUE;

            //
            // Do an explicit seek if this is a 360 K disk in a 1.2 MB drive.
            //

            if (CurrentDriveMediaConstants->CylinderShift != 0) {
                if (!SendByte( COMMND_SEEK ) ||
                    !SendByte( (Head << 2) + DriveNumber ) ||  // head select & drive
                    !SendByte( Cylinder << CurrentDriveMediaConstants->CylinderShift )) {
                    return(EIO);
                }

                if (FwWaitForDeviceInterrupt(1 << (FLOPPY_VECTOR - DEVICE_VECTORS - 1),
                                             FW_FLOPPY_TIMEOUT)) {
                    return(EIO);
                }

                //
                // Send the sense interrupt command.
                //

                if (!SendByte( COMMND_SENSE_INTERRUPT )) {    // command
                    return(EIO);
                }

                //
                // Read back the information from the drive and check the status of the
                // recalibrate command.
                //

                DebugByte[0] = ReceiveByte();
                DebugByte[1] = ReceiveByte();

                if (DebugByte[1] != (Cylinder << CurrentDriveMediaConstants->CylinderShift)) {
                    return(EIO);
                }

                //
                // Now try to read the ID from wherever we're at.
                //

                if (!SendByte( COMMND_READ_ID + COMMND_MFM ) ||  // command
                    !SendByte( DriveNumber | ((CurrentDriveMediaConstants->NumberOfHeads - 1) << 2) )) {
                    return(EIO);
                }

                if (FwWaitForDeviceInterrupt(1 << (FLOPPY_VECTOR - DEVICE_VECTORS - 1),
                                             FW_FLOPPY_TIMEOUT)) {
                    return(EIO);
                }

                for (i = 0; i < 7; i++) {
                    DebugByte[i] = ReceiveByte();
                }

                if ( ( DebugByte[0] !=
                        (UCHAR)(DriveNumber |
                                ((CurrentDriveMediaConstants->NumberOfHeads - 1) << 2))) ||
                    ( DebugByte[1] != 0 ) ||
                    ( DebugByte[2] != 0 ) ) {

                    return(EIO);
                }

            }

            //
            // Send the command and parameters for the operation.
            //

            if (ReadWrite == TRUE) {
                if (!SendByte( COMMND_WRITE_DATA + COMMND_MFM )) {
                    return(EIO);
                }
            } else {
                if (!SendByte( COMMND_READ_DATA + COMMND_MFM )) {
                    return(EIO);
                }
            }
            if (!SendByte( (Head << 2) + DriveNumber ) ||  // head select & drive
                !SendByte( Cylinder ) ||                   // cylinder
                !SendByte( Head ) ||                       // head
                !SendByte( Sector ) ||                     // sector
                !SendByte( 2 ) ||                          // sector size; 2 => 512B/sec
                !SendByte( EndSector ) ||                  // end of track sector
                !SendByte( CurrentDriveMediaConstants->ReadWriteGapLength ) ||
                !SendByte( 0xff )) {                       // special sector size
                return(EIO);
            }

            //
            // Ensure that the floppy drive does not time-out.
            //

            for (j = 0; j < SECONDS_2; j += MICROSECONDS_10) {
                if (READ_REGISTER_UCHAR(&FLOPPY_CONTROL->MsrDsr.MainStatus) > 127) {
                    break;
                }

                FwStallExecution(MICROSECONDS_10);
            }

            //
            // Check for time-out;  if one occurred, then return unsuccessful
            // status.
            //

            if (j == SECONDS_2) {
//                FwPrint("Floppy timeout\r\n");
                return EIO;
            }

            //
            // Read the data from the appropriate block(s) and check the number
            // of bytes actually read.
            //

            if (ReadWrite == TRUE) {
                WriteFloppyFifo(Buffer,TransferSize);
            } else {
                TransferedBytes = ReadFloppyFifo(Buffer);
                if (TransferedBytes != TransferSize) {
                    Success = FALSE;
                }
            }

            //
            // Read the status information from the device.
            //

            while (READ_REGISTER_UCHAR(&FLOPPY_CONTROL->MsrDsr.MainStatus) <= 127) {
            }

            for (i = 0; i < 7; i++) {
                DebugByte[i] = ReceiveByte();
            }

            if (((DebugByte[0] >> 4) == 2) &&
                (DebugByte[1] == 0) &&
                (DebugByte[2] == 0) &&
                Success) {
                ;

            } else {

                if ((DebugByte[0] >> 4) != 4) {
                    Success = FALSE;
                }

                if (DebugByte[1] != 0x80) {
                    Success = FALSE;
                }

                if (DebugByte[2] != 0) {
                    Success = FALSE;
                }
            }

            //
            // If the operation was successful, exit the loop.
            //

            if (Success) {
                Buffer += TransferSize;
                break;
            }

            //
            // The operation did not work.  Attempt to recalibrate the
            // device and wait for everything to settle out, and then
            // try the operation again.
            //

            if (!Recalibrate(DriveNumber)) {
//                FwPrint("Floppy recalibration error\r\n");
                return(EIO);
            }
            FwStallExecution(MILLISECONDS_15);
        }

        //
        // If the operation was not successful after RETRY_COUNT tries, get
        // out now.
        //

        if (!Success) {
            Status = EIO;
            break;
        }

        //
        // If more data is needed, get the next place to read from.  Note
        // that if there is more data to be read, then the last sector
        // just read was the last sector on this head.
        //

        if (BlockCount > 0) {
            if (Head == 1) {
                Cylinder += 1;
                Head = 0;
            } else {
                Head = 1;
            }
            Sector = 1;
        }

        if (Success) {
            Status = ESUCCESS;
        }
    }
    return Status;
}

BOOLEAN
Recalibrate(
    UCHAR DriveNumber
    )

/*++

Routine Description:

    This routine issues a recalibrate command to the device, waits for it to
    interrupt, sends it a sense interrupt command, and checks the result to
    ensure that the recalibrate command worked properly.

Arguments:

    DriveNumber - Supplies the Floppy drive to recalibrate.

Return Value:

    Returns TRUE if the recalibrate was successful, FALSE if not.

--*/

{

    //
    // Send the recalibrate command to the device.
    //

    if (!SendByte( COMMND_RECALIBRATE ) ||    // command
        !SendByte( DriveNumber )) {           // drive select
        return(FALSE);
    }

    if (FwWaitForDeviceInterrupt(1 << (FLOPPY_VECTOR - DEVICE_VECTORS - 1),
                                 FW_FLOPPY_TIMEOUT)) {
//        FwPrint("Floppy recalibrate timeout\r\n");
        return(FALSE);
    }

    //
    // Send the sense interrupt command.
    //

    if (!SendByte( COMMND_SENSE_INTERRUPT )) {    // command
        return(FALSE);
    }

    //
    // Read back the information from the drive and check the status of the
    // recalibrate command.
    //

    DebugByte[0] = ReceiveByte();
    if ((DebugByte[0] >> 4) != 2) {
        return FALSE;
    }

    DebugByte[1] = ReceiveByte();

    if ((READ_REGISTER_UCHAR(&FLOPPY_CONTROL->MsrDsr.MainStatus) &
         STATUS_IO_READY_MASK) == STATUS_READ_READY) {
        return FALSE;
    }

    return TRUE;
}

UCHAR
ReceiveByte(
    )

/*++

Routine Description:

    This routine reads the next byte from the floppy FIFO register.

Arguments:

    None.

Return Value:

    The function value is the value of the byte read from the floppy FIFO.

--*/

{

    ULONG i;

    //
    // Check status register for readiness to receive data.
    //

    for (i = 0; i < MICROSECONDS_250; i += MICROSECONDS_10) {
        if ((READ_REGISTER_UCHAR(&FLOPPY_CONTROL->MsrDsr.MainStatus) &
             STATUS_IO_READY_MASK) == STATUS_READ_READY) {
            return READ_REGISTER_UCHAR(&FLOPPY_CONTROL->Fifo);
        }

        FwStallExecution(MICROSECONDS_10);
    }

    //
    // A timeout occurred while attempting to read data from the floppy fifo.
    // Output an error message and return.
    //

//    FwPrint("Error reading from floppy fifo\r\n");
    return(0xFF);
}

BOOLEAN
SendByte(
    IN UCHAR SourceByte
    )

/*++

Routine Description:

    This routine sends a specified byte to the floppy FIFO register.

Arguments:

    SourceByte - Byte to be sent to the controller.

Return Value:

    If the byte was successfully written to the floppy FIFO, TRUE is returned,
    otherwise FALSE is returned.

--*/

{

    ULONG i;

    //
    // Check status register for readiness to receive data.
    //

    for (i = 0; i < MICROSECONDS_250; i += MICROSECONDS_10) {
        if ((READ_REGISTER_UCHAR(&FLOPPY_CONTROL->MsrDsr.MainStatus) &
             STATUS_IO_READY_MASK) == STATUS_WRITE_READY) {
            WRITE_REGISTER_UCHAR(&FLOPPY_CONTROL->Fifo, SourceByte);
            return(TRUE);
        }

        FwStallExecution(MICROSECONDS_10);
    }

    //
    // A timeout occurred while attempting to write data to the floppy fifo.
    // Output an error message and return.
    //

//    FwPrint("Error writing to floppy fifo\r\n");
    return(FALSE);
}

VOID
ClearFloppyFifo(
    IN VOID
    )

/*++

Routine Description:

    This routine empties the fifo.

Arguments:

    None.

Return Value:

    None.

--*/

{

    ULONG i;

    //
    // Check status register for readiness to receive data.
    //
    while ((READ_REGISTER_UCHAR(&FLOPPY_CONTROL->MsrDsr.MainStatus) &
             STATUS_IO_READY_MASK) == STATUS_READ_READY) {
        READ_REGISTER_UCHAR(&FLOPPY_CONTROL->Fifo);
        FwStallExecution(MICROSECONDS_10);
    }
}


ARC_STATUS
FloppyDatarateSpecifyConfigure(
    IN DRIVE_MEDIA_TYPE DriveMediaType,
    IN UCHAR DriveNumber
    )

/*++

Routine Description:

    This routine is called to set up the controller every time a new type
    of diskette is to be accessed.  It issues the CONFIGURE command,
    does a SPECIFY, sets the data rate, and RECALIBRATEs the drive.

Arguments:

    DriveMediaType - supplies the drive type/media density combination.

    DriveNumber - supplies the drive number.

Return Value:

    ESUCCESS if the controller is properly prepared; appropriate
    error propogated otherwise.

--*/

{
    UCHAR Configure;

    //
    // Don't enable implied seeks when there is a 360K disk in a 1.2M drive.
    //

    if (DriveMediaConstants[DriveMediaType].CylinderShift) {
        Configure = 0x18;
    } else {
        Configure = 0x58;
    }

    //
    // Issue the configuration command.
    //

    if (!SendByte( COMMND_CONFIGURE ) ||  // command
        !SendByte( 0x00 ) ||              // required 0
        !SendByte( Configure ) ||         // implied seeks, disable polling & threshold = 8
        !SendByte( 0x00 ) ||              // precompensation track = 0

    //
    // Issue SPECIFY command to program the head load and unload
    // rates, the drive step rate, and the DMA data transfer mode.
    //

        !SendByte( COMMND_SPECIFY ) ||    // command
        !SendByte( DriveMediaConstants[DriveMediaType].StepRateHeadUnloadTime) ||
        !SendByte( DriveMediaConstants[DriveMediaType].HeadLoadTime + 1)) {
        return(EIO);
    }

    //
    // Program the data rate
    //

    WRITE_REGISTER_UCHAR(&FLOPPY_CONTROL->MsrDsr.DataRateSelect,
                         DriveMediaConstants[DriveMediaType].DataTransferRate );

    //
    // Recalibrate the drive, now that we've changed all its
    // parameters.
    //

    if (Recalibrate(DriveNumber)) {
        return(ESUCCESS);
    } else {
//        FwPrint("Floppy recalibration error\r\n");
        return(EIO);
    }
}

ARC_STATUS
FloppyDetermineMediaType(
    IN OUT PFLOPPY_CONTEXT FloppyContext
    )

/*++

Routine Description:

    This routine is called by FloppyBootIO() when the media type is
    unknown.  It assumes the largest media supported by the drive is
    available, and keeps trying lower values until it finds one that
    works.

Arguments:

    FloppyContext - supplies a pointer to the floppy context structure.

Return Value:

    ESUCCESS if the type of the media is determined; appropriate
    error propogated otherwise.

--*/

{
    ARC_STATUS Status;
    BOOLEAN mediaTypesExhausted = FALSE;
    DRIVE_MEDIA_TYPE DriveMediaType;
    UCHAR DriveNumber;
    ULONG i;

    //
    // Assume that the largest supported media is in the drive.  If that
    // turns out to be untrue, we'll try successively smaller media types
    // until we find what's really in there (or we run out and decide
    // that the media isn't formatted).
    //

    DriveMediaType =
        DriveMediaLimits[FloppyContext->DriveType].HighestDriveMediaType;

    DriveNumber = FloppyContext->DiskId;

    do {

        Status = FloppyDatarateSpecifyConfigure( DriveMediaType, DriveNumber );

        if ( Status != ESUCCESS ) {

            //
            // The SPECIFY or CONFIGURE commands resulted in an error.
            // Force ourselves out of this loop and return error.
            //

            mediaTypesExhausted = TRUE;

        } else {

            CurrentDriveMediaConstants = &DriveMediaConstants[DriveMediaType];

            //
            // Now try to read the ID from wherever we're at.
            //

            if (!SendByte( COMMND_READ_ID + COMMND_MFM ) ||  // command
                !SendByte( DriveNumber | ((CurrentDriveMediaConstants->NumberOfHeads - 1) << 2) )) {
                return(EIO);
            }

            if (FwWaitForDeviceInterrupt(1 << (FLOPPY_VECTOR - DEVICE_VECTORS - 1),
                                         FW_FLOPPY_TIMEOUT)) {
//                FwPrint("Floppy determine media timeout\r\n");
                return(EIO);
            }

            for (i = 0; i < 7; i++) {
                DebugByte[i] = ReceiveByte();
            }

            if ( ( DebugByte[0] !=
                    (UCHAR)(DriveNumber |
                            ((CurrentDriveMediaConstants->NumberOfHeads - 1) << 2))) ||
                ( DebugByte[1] != 0 ) ||
                ( DebugByte[2] != 0 ) ) {

                DriveMediaType--;

                Status = ENXIO;

                //
                // Next comparison must be signed, for when
                // LowestDriveMediaType = 0.
                //

                if ( (CHAR)( DriveMediaType ) <
                     (CHAR)( DriveMediaLimits[FloppyContext->DriveType].LowestDriveMediaType )) {

                    mediaTypesExhausted = TRUE;

                }
            }
        }

    } while ( ( ( Status != ESUCCESS ) ) && !( mediaTypesExhausted ) );

    if ( Status == ESUCCESS ) {

        FloppyContext->SectorsPerTrack =
            CurrentDriveMediaConstants->SectorsPerTrack;
    }
    return Status;
}
#endif
