summaryrefslogtreecommitdiffstats
path: root/private/ntos/fw/alpha/jxfboot.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/fw/alpha/jxfboot.c')
-rw-r--r--private/ntos/fw/alpha/jxfboot.c2273
1 files changed, 2273 insertions, 0 deletions
diff --git a/private/ntos/fw/alpha/jxfboot.c b/private/ntos/fw/alpha/jxfboot.c
new file mode 100644
index 000000000..039b56ee7
--- /dev/null
+++ b/private/ntos/fw/alpha/jxfboot.c
@@ -0,0 +1,2273 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+Copyright (c) 1993 Digital Equipment 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:
+
+ 31-August-1992 John DeRosa [DEC]
+
+ Made Alpha/Jensen modifications. This file is now Alpha-specific.
+
+--*/
+
+#include "fwp.h"
+#include "jnsnprom.h"
+#include "ntdddisk.h"
+#include "flo_data.h"
+#include "xxstring.h"
+
+//
+// Define local static data.
+//
+
+UCHAR DebugByte[8];
+ULONG MotorStatus;
+PDRIVE_MEDIA_CONSTANTS CurrentDriveMediaConstants;
+
+//
+// Define timeout constants.
+//
+
+#define MICROSECONDS_10 10
+#define MICROSECONDS_20 20
+#define MICROSECONDS_250 250
+#define MICROSECONDS_500 500
+#define MILLISECONDS_15 (15 * 1000)
+#define MILLISECONDS_30 (30 * 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)
+
+
+
+//
+// Define bits in some of the floppy registers. The existing Microsoft
+// code uses constants (e.g. 0xC0) for register masking. These defines
+// will be used in Digital Equipment Corporation code.
+//
+
+#define FLOPPY_SRA_INTPENDING 0x80
+#define FLOPPY_MSR_DRV0BUSY 0x1
+#define FLOPPY_MSR_DRV1BUSY 0x2
+#define FLOPPY_MSR_CMDBUSY 0x10
+#define FLOPPY_MSR_NONDMA 0x20
+#define FLOPPY_MSR_DIO 0x40
+#define FLOPPY_MSR_RQM 0x80
+
+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
+WaitForFloppyInterrupt(
+ ULONG Timeout
+ );
+
+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,
+ OUT PMEDIA_TYPE mediaType
+ );
+
+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 = Unknown;
+ DRIVE_MEDIA_TYPE driveMediaType;
+ ULONG DriveType;
+ ULONG ConfigDriveType;
+
+ //
+ // Get the drive number from the pathname.
+ //
+
+ if (JzGetPathMnemonicKey(OpenPath, "fdisk", &DriveNumber)) {
+ return ENODEV;
+ }
+
+ //
+ // If the ARC CDS data is invalid, default to 2.88MB floppy
+ //
+
+ BlFileTable[*FileId].u.FloppyContext.DriveType = DRIVE_TYPE_2880;
+
+ //
+ // 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_PORT_UCHAR((PUCHAR)&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,
+ &mediaType);
+ 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 next lowest floppy type.
+ //
+
+ DriveType = BlFileTable[*FileId].u.FloppyContext.DriveType;
+
+ if (DriveType == DRIVE_TYPE_2880) {
+ BlFileTable[*FileId].u.FloppyContext.DriveType = DRIVE_TYPE_1440;
+ } else if (DriveType == DRIVE_TYPE_1440) {
+ BlFileTable[*FileId].u.FloppyContext.DriveType = DRIVE_TYPE_1200;
+ }
+
+ ArcStatus = FloppyDetermineMediaType(&BlFileTable[*FileId].u.FloppyContext,
+ &mediaType);
+
+ if (ArcStatus != ESUCCESS) {
+// 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 );
+
+ 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:
+ default:
+
+ //
+ // FloppyDetermineMediaType did the right thing for this case,
+ // so no additional verification is needed.
+ //
+
+ 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.
+ //
+
+ if (DriveType != ConfigDriveType) {
+
+ switch (DriveType) {
+
+ case DRIVE_TYPE_1200:
+
+ strcpy(FloppyData->Size,"5.25");
+ FloppyData->MaxDensity = 1200;
+ break;
+
+ case DRIVE_TYPE_1440:
+
+ strcpy(FloppyData->Size,"3.5");
+ FloppyData->MaxDensity = 1440;
+ break;
+
+ default:
+
+ strcpy(FloppyData->Size,"3.5");
+ FloppyData->MaxDensity = 2880;
+ break;
+ }
+
+ //
+ // 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);
+ }
+
+ //
+ // 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.
+ //
+ (PCHAR)Buffer += Length & (~(SECTOR_SIZE - 1));
+ Limit = Length & (SECTOR_SIZE - 1);
+ for (Index = 0; Index < Limit; Index += 1) {
+ *((PCHAR)Buffer)++ = *(TempPointer + Index);
+ }
+ 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)++;
+ }
+
+ //
+ // 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.
+ //
+ (PCHAR)Buffer += Length & (~(SECTOR_SIZE - 1));
+ Limit = Length & (SECTOR_SIZE - 1);
+ for (Index = 0; Index < Limit; Index += 1) {
+ *(TempPointer + Index) = *((PCHAR)Buffer)++;
+ }
+ //
+ // 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_PORT_UCHAR((PUCHAR)&FLOPPY_CONTROL->DigitalOutput, 0x0c);
+ FwStallExecution(MICROSECONDS_500);
+
+ //
+ // Reset the floppy controller by setting the s/w reset bit in the DSR.
+ //
+
+ WRITE_PORT_UCHAR((PUCHAR)&FLOPPY_CONTROL->MsrDsr.DataRateSelect, 0x80);
+ FwStallExecution(MILLISECONDS_30);
+
+ //
+ // Set the data rate in the CCR.
+ //
+
+// WRITE_PORT_UCHAR(&FLOPPY_CONTROL->DirCcr.ConfigurationControl, 0);
+
+// if (FwWaitForDeviceInterrupt(1 << (FLOPPY_VECTOR - DEVICE_VECTORS - 1),
+// FW_FLOPPY_TIMEOUT)) {
+ if (WaitForFloppyInterrupt(FW_FLOPPY_TIMEOUT)) {
+// FwPrint("Floppy setup timeout\r\n");
+ return;
+ }
+
+ FwStallExecution(MICROSECONDS_20);
+
+
+ //
+ // 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 = FW_FLOPPY_0_DEVICE;
+ LookupTable->DispatchTable = &FloppyEntryTable;
+
+ //
+ // Get the floppy configuration information.
+ //
+ FloppyComponent = FwGetComponent(FW_FLOPPY_0_DEVICE);
+ if ((FloppyComponent != NULL) &&
+ (FloppyComponent->Type == FloppyDiskPeripheral)) {
+
+ //
+ // Initialize the lookup table.
+ //
+
+ LookupTable->DevicePath = FW_FLOPPY_0_DEVICE;
+ LookupTable->DispatchTable = &FloppyEntryTable;
+ LookupTable++;
+
+ //
+ // Return if no more room in the lookup table.
+ //
+
+ if (Entries == 1) {
+ return;
+ }
+ }
+ FloppyComponent = FwGetComponent(FW_FLOPPY_1_DEVICE);
+ if ((FloppyComponent != NULL) &&
+ (FloppyComponent->Type == FloppyDiskPeripheral)) {
+
+ //
+ // Initialize the lookup table.
+ //
+ LookupTable->DevicePath = FW_FLOPPY_1_DEVICE;
+ 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_PORT_UCHAR((PUCHAR)&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_PORT_UCHAR((PUCHAR)&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)) {
+ if (WaitForFloppyInterrupt(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)) {
+ if (WaitForFloppyInterrupt(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_PORT_UCHAR((PUCHAR)&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_PORT_UCHAR((PUCHAR)&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)) {
+ if (WaitForFloppyInterrupt(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_PORT_UCHAR((PUCHAR)&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_PORT_UCHAR((PUCHAR)&FLOPPY_CONTROL->MsrDsr.MainStatus) &
+ STATUS_IO_READY_MASK) == STATUS_READ_READY) {
+ return READ_PORT_UCHAR((PUCHAR)&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_PORT_UCHAR((PUCHAR)&FLOPPY_CONTROL->MsrDsr.MainStatus) &
+ STATUS_IO_READY_MASK) == STATUS_WRITE_READY) {
+ WRITE_PORT_UCHAR((PUCHAR)&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_PORT_UCHAR((PUCHAR)&FLOPPY_CONTROL->MsrDsr.MainStatus) &
+ STATUS_IO_READY_MASK) == STATUS_READ_READY) {
+ READ_PORT_UCHAR((PUCHAR)&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_PORT_UCHAR((PUCHAR)&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,
+ OUT PMEDIA_TYPE mediaType
+ )
+
+/*++
+
+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.
+
+ mediaType - supplies a pointer to a variable that receives the
+ type of the media in the drive.
+
+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)) {
+ if (WaitForFloppyInterrupt(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;
+
+ *mediaType = CurrentDriveMediaConstants->MediaType;
+ }
+ return Status;
+}
+
+ULONG
+ReadFloppyFifo (
+ IN OUT PUCHAR Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ This reads data from the floppy fifo.
+
+Arguments:
+
+ Buffer - Pointer to the buffer that receives the data read.
+
+Return Value:
+
+ The number of bytes read.
+
+--*/
+{
+ ULONG BytesRead = 0;
+ UCHAR MSR;
+
+ while (TRUE) {
+
+ // Loop until DIO and RQM are both set.
+ while (
+ ((MSR = READ_PORT_UCHAR((PUCHAR)&FLOPPY_CONTROL->MsrDsr.MainStatus))
+ & (FLOPPY_MSR_DIO | FLOPPY_MSR_RQM))
+ != (FLOPPY_MSR_DIO | FLOPPY_MSR_RQM)
+ ) {
+ }
+
+ // If the non-DMA bit is clear, end of transfer.
+ if ((MSR & FLOPPY_MSR_NONDMA) == 0) {
+ return (BytesRead);
+ }
+
+ // Read the byte, increment pointer and total, until done.
+ *Buffer++ = READ_PORT_UCHAR((PUCHAR)&FLOPPY_CONTROL->Fifo);
+ BytesRead++;
+ }
+}
+
+VOID
+WriteFloppyFifo(
+ IN PUCHAR Buffer,
+ IN ULONG Size
+ )
+
+/*++
+
+Routine Description:
+
+ This writes data to the floppy fifo.
+
+Arguments:
+
+ Buffer - Pointer to the buffer that contains the data to be written.
+
+ Size - The number of bytes to be written.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ while (Size) {
+
+ // Loop until DIO is clear and RQM is set.
+ while (
+ (READ_PORT_UCHAR((PUCHAR)&FLOPPY_CONTROL->MsrDsr.MainStatus)
+ & (FLOPPY_MSR_DIO | FLOPPY_MSR_RQM))
+ != FLOPPY_MSR_RQM
+ ) {
+ }
+
+ //
+ // Write the byte, increment pointer, decrement count, until done.
+ // Remember, C does call by value.
+ //
+
+ WRITE_PORT_UCHAR((PUCHAR)&FLOPPY_CONTROL->Fifo, *Buffer++);
+ --Size;
+ }
+
+ //
+ // All bytes written to fifo. Wait until the floppy controller
+ // empties it.
+ //
+
+ while (
+ (READ_PORT_UCHAR((PUCHAR)&FLOPPY_CONTROL->MsrDsr.MainStatus)
+ & FLOPPY_MSR_NONDMA)
+ != 0 ) {
+ }
+
+
+ return;
+
+}
+
+ARC_STATUS
+WaitForFloppyInterrupt(
+ ULONG Timeout
+ )
+
+/*++
+
+Routine Description:
+
+ This waits until the floppy controller has an interrupt pending,
+ or the requested Timeout period has been reached.
+
+ The Jazz code enables interrupts for the keyboard and individual
+ devices. In actuality, the only non-keyboard device it enables interrupts
+ for is the floppy controller. For Alpha we avoid dealing
+ with interrupts in the firmware code to keep the PALcode simple. So
+ every Jazz call to "FwWaitForDeviceInterrupt" has been changed to
+ this function.
+
+Arguments:
+
+ Timeout - A timeout value in seconds. Note that a timeout of 0 gives
+ an actual timeout of between 0 and 1, a timeout of 1 gives
+ an actual timeout of between 1 and 2, and so on.
+
+Return Value:
+
+ ESUCCESS if an interrupt is floppy interrupt is detected.
+ EIO if a timeout occurs.
+
+--*/
+
+{
+ ULONG Time1;
+
+
+ Time1 = FwGetRelativeTime();
+
+ // Ask for the interrupt wires
+ WRITE_PORT_UCHAR((PUCHAR)EISA_INT_OCW3, EISA_INT_OCW3_IRR);
+
+ //
+ // 0x40 is irq6, which is the floppy controller. I should make this
+ // into a #define.
+ //
+ while (
+ (READ_PORT_UCHAR((PUCHAR)EISA_INT_OCW3) & 0x40)
+ == 0) {
+ if ((FwGetRelativeTime() - Time1) > (Timeout + 1)) {
+ return(EIO);
+ }
+ }
+
+ return(ESUCCESS);
+}