Commit in reactos/drivers/storage/floppy on MAIN
GNUmakefile+34added 1.1
Makefile+32added 1.1
SOURCES+30added 1.1
csqrtns.c+171added 1.1
csqrtns.h+59added 1.1
floppy.c+1121added 1.1
floppy.h+145added 1.1
floppy.rc+62added 1.1
hardware.c+1117added 1.1
hardware.h+336added 1.1
ioctl.c+250added 1.1
ioctl.h+31added 1.1
readwrite.c+710added 1.1
readwrite.h+32added 1.1
+4130
14 added files
First import of in-progress floppy driver replacement.  Still needs some 
debugging and feature improvements, and the kernel needs a bit of work to
get this driver loading on ReactOS.

reactos/drivers/storage/floppy
GNUmakefile added at 1.1
diff -N GNUmakefile
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ GNUmakefile	1 Mar 2004 06:37:26 -0000	1.1
@@ -0,0 +1,34 @@
+#  ReactOS Floppy Driver
+#  Copyright (C) 2004, Vizzini (vizzini@plasmic.com)
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# PROJECT:         ReactOS Floppy Driver
+# FILE:            GNUMakefile
+# PURPOSE:         Makefile for ReactOS build system
+# PROGRAMMER:      Vizzini (vizzini@plasmic.com)
+# REVISIONS:
+#                  15-Feb-2004 vizzini - Created/
+
+PATH_TO_TOP = ../../..
+TARGET_TYPE = driver
+TARGET_NAME = floppy
+TARGET_CFLAGS = -Wall -Werror 
+TARGET_OBJECTS = floppy.o csqrtns.o hardware.o readwrite.o ioctl.o
+TARGET_DDKLIBS = csq.a
+
+include $(PATH_TO_TOP)/rules.mak
+include $(TOOLS_PATH)/helper.mk
+

reactos/drivers/storage/floppy
Makefile added at 1.1
diff -N Makefile
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ Makefile	1 Mar 2004 06:37:26 -0000	1.1
@@ -0,0 +1,32 @@
+#  ReactOS Floppy Driver
+#  Copyright (C) 2004, Vizzini (vizzini@plasmic.com)
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# PROJECT:         ReactOS Floppy Driver
+# FILE:            Makefile
+# PURPOSE:         Makefile for Microsoft DDK build system
+# PROGRAMMER:      Vizzini (vizzini@plasmic.com)
+# REVISIONS:
+#                  15-Feb-2004 vizzini - Created/
+#
+# MAKEFILE for ARTICLE
+#
+# DO NOT EDIT THIS FILE!!!  Edit .\sources. if you want to add a new source
+# file to this component.  This file merely indirects to the real make file
+# that is shared by all the driver components of the Windows NT DDK
+#
+
+!INCLUDE $(NTMAKEENV)\makefile.def

reactos/drivers/storage/floppy
SOURCES added at 1.1
diff -N SOURCES
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ SOURCES	1 Mar 2004 06:37:26 -0000	1.1
@@ -0,0 +1,30 @@
+#  ReactOS Floppy Driver
+#  Copyright (C) 2004, Vizzini (vizzini@plasmic.com)
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# PROJECT:         ReactOS Floppy Driver
+# FILE:            SOURCES
+# PURPOSE:         SOURCES file for Microsoft DDK build system
+# PROGRAMMER:      Vizzini (vizzini@plasmic.com)
+# REVISIONS:
+#                  15-Feb-2004 vizzini - Created/
+
+TARGETNAME=floppy
+TARGETTYPE=DRIVER
+TARGETPATH=obj
+TARGETLIBS= $(DDK_LIB_PATH)\csq.lib
+SOURCES= floppy.c csqrtns.c hardware.c readwrite.c ioctl.c
+MSC_WARNING_LEVEL=/W3 /WX

reactos/drivers/storage/floppy
csqrtns.c added at 1.1
diff -N csqrtns.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ csqrtns.c	1 Mar 2004 06:37:26 -0000	1.1
@@ -0,0 +1,171 @@
+/*
+ *  ReactOS Floppy Driver
+ *  Copyright (C) 2004, Vizzini (vizzini@plasmic.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * PROJECT:         ReactOS Floppy Driver
+ * FILE:            csqrtns.c
+ * PURPOSE:         Cancel-safe queue routines
+ * PROGRAMMER:      Vizzini (vizzini@plasmic.com)
+ * REVISIONS:
+ *                  15-Feb-2004 vizzini - Created
+ * NOTES:
+ *     - These functions provide the callbacks for the CSQ routines.
+ *       They will be called automatically by the IoCsqXxx() routines.
+ *       This driver uses the CSQ in the standard way.  In addition to
+ *       queuing and de-queuing IRPs, the InsertIrp routine releases
+ *       a semaphore every time an IRP is queued, allowing a queue management
+ *       thread to properly drain the queue.
+ *     - Note that the semaphore can get ahead of the number of IRPs in the
+ *       queue if any are canceled; the queue management thread that de-queues
+ *       IRPs is coded with that in mind.
+ *     - For more information, see the csqtest driver in the ReactOS tree,
+ *       or the cancel sample in recent (3790+) Microsoft DDKs.
+ *     - Many of these routines are called at DISPATCH_LEVEL, due to the fact
+ *       that my lock choice is a spin lock.
+ */
+
+#include <ntddk.h>
+
+#include "floppy.h"
+#include "csqrtns.h"
+
+/* Global CSQ struct that the CSQ functions initialize and use */
+IO_CSQ Csq;
+
+/* List and lock for the actual IRP queue */
+LIST_ENTRY IrpQueue;
+KSPIN_LOCK IrpQueueLock;
+KSEMAPHORE QueueSemaphore;
+
+/* 
+ * CSQ Callbacks 
+ */
+
+
+VOID NTAPI CsqRemoveIrp(PIO_CSQ Csq, 
+                        PIRP Irp)
+/*
+ * FUNCTION: Remove an IRP from the queue
+ * ARGUMENTS:
+ *     Csq: Pointer to CSQ context structure
+ *     Irp: Pointer to the IRP to remove from the queue
+ * NOTES:
+ *     - Called under the protection of the queue lock
+ */
+{
+  KdPrint(("CSQ: Removing IRP 0x%x\n", Irp));
+  RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
+}
+
+
+PIRP NTAPI CsqPeekNextIrp(PIO_CSQ Csq, 
+                          PIRP Irp, 
+                          PVOID PeekContext)
+/*
+ * FUNCTION: Remove the next IRP from the queue
+ * ARGUMENTS:
+ *     Csq: Pointer to CSQ context structure
+ *     Irp: Pointer to a starting IRP in the queue (i.e. start search here)
+ *     PeekContext: Unused
+ * RETURNS:
+ *     Pointer to an IRP that is next in line to be removed, if one can be found
+ * NOTES:
+ *     - This does *not* remove the IRP from the queue; it merely returns a pointer.
+ *     - Called under the protection of the queue lock
+ */
+{
+  KdPrint(("CSQ: Peeking for next IRP\n"));
+
+  if(Irp)
+    return CONTAINING_RECORD(&Irp->Tail.Overlay.ListEntry.Flink, IRP, Tail.Overlay.ListEntry);
+
+  if(IsListEmpty(&IrpQueue))
+    return NULL;
+
+  return CONTAINING_RECORD(IrpQueue.Flink, IRP, Tail.Overlay.ListEntry);
+}
+
+
+VOID NTAPI CsqAcquireLock(PIO_CSQ Csq, 
+                          PKIRQL Irql)
+/*
+ * FUNCTION: Acquire the queue lock
+ * ARGUMENTS:
+ *     Csq: Pointer to CSQ context structure
+ *     Irql: Pointer to a variable to store the old irql into
+ */
+{
+  KdPrint(("CSQ: Acquiring spin lock\n"));
+  KeAcquireSpinLock(&IrpQueueLock, Irql);
+}
+
+
+VOID NTAPI CsqReleaseLock(PIO_CSQ Csq, 
+                          KIRQL Irql)
+/*
+ * FUNCTION: Release the queue lock
+ * ARGUMENTS:
+ *     Csq: Pointer to CSQ context structure
+ *     Irql: IRQL to lower to on release
+ */
+{
+  KdPrint(("CSQ: Releasing spin lock\n"));
+  KeReleaseSpinLock(&IrpQueueLock, Irql);
+}
+
+
+VOID NTAPI CsqCompleteCanceledIrp(PIO_CSQ Csq, 
+                                  PIRP Irp)
+/*
+ * FUNCTION: Complete a canceled IRP
+ * ARGUMENTS:
+ *    Csq: Pointer to CSQ context structure
+ *    Irp: IRP to complete
+ * NOTES:
+ *    - Perhaps we should complete with something besides NO_INCREMENT
+ *    - MS misspelled CANCELLED... sigh...
+ */
+{
+  KdPrint(("CSQ: Canceling irp 0x%x\n", Irp));
+  Irp->IoStatus.Status = STATUS_CANCELLED;
+  Irp->IoStatus.Information = 0;
+  IoCompleteRequest(Irp, IO_NO_INCREMENT);
+}
+
+
+VOID NTAPI CsqInsertIrp(PIO_CSQ Csq, 
+                        PIRP Irp)
+/*
+ * FUNCTION: Queue an IRP
+ * ARGUMENTS:
+ *     Csq: Unused
+ *     Irp: IRP to add to the queue
+ * NOTES:
+ *     - Called under the protection of the queue lock
+ *     - Releases the semaphore for each queued packet, which is how 
+ *       the queue management thread knows that there might be
+ *       an IRP in the queue
+ *     - Note that the semaphore will get released more times than
+ *       the queue management thread will have IRPs to process, given
+ *       that at least one IRP is canceled at some point
+ */
+{
+  KdPrint(("CSQ: Inserting IRP 0x%x\n", Irp));
+  InsertTailList(&IrpQueue, &Irp->Tail.Overlay.ListEntry);
+  KeReleaseSemaphore(&QueueSemaphore, 0, 1, FALSE);
+}
+

reactos/drivers/storage/floppy
csqrtns.h added at 1.1
diff -N csqrtns.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ csqrtns.h	1 Mar 2004 06:37:26 -0000	1.1
@@ -0,0 +1,59 @@
+/*
+ *  ReactOS Floppy Driver
+ *  Copyright (C) 2004, Vizzini (vizzini@plasmic.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * PROJECT:         ReactOS Floppy Driver
+ * FILE:            csqrtns.h
+ * PURPOSE:         Header for Cancel-safe queue routines
+ * PROGRAMMER:      Vizzini (vizzini@plasmic.com)
+ * REVISIONS:
+ *                  15-Feb-2004 vizzini - Created
+ */
+
+#ifdef _MSC_VER
+#include <csq.h>
+#else
+#include <ddk/csq.h>
+#endif
+
+/*
+ * CSQ Stuff
+ */
+extern IO_CSQ Csq;
+extern LIST_ENTRY IrpQueue;
+extern KSPIN_LOCK IrpQueueLock;
+extern KSEMAPHORE QueueSemaphore;
+
+VOID NTAPI CsqInsertIrp(PIO_CSQ Csq, 
+                        PIRP Irp); 
+
+VOID NTAPI CsqRemoveIrp(PIO_CSQ Csq, 
+                        PIRP Irp);
+
+PIRP NTAPI CsqPeekNextIrp(PIO_CSQ Csq, 
+                          PIRP Irp, 
+                          PVOID PeekContext);
+
+VOID NTAPI CsqAcquireLock(PIO_CSQ Csq, 
+                          PKIRQL Irql);
+
+VOID NTAPI CsqReleaseLock(PIO_CSQ Csq, 
+                          KIRQL Irql);
+
+VOID NTAPI CsqCompleteCanceledIrp(PIO_CSQ Csq, 
+                                  PIRP Irp);
+

reactos/drivers/storage/floppy
floppy.c added at 1.1
diff -N floppy.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ floppy.c	1 Mar 2004 06:37:26 -0000	1.1
@@ -0,0 +1,1121 @@
+/*
+ *  ReactOS Floppy Driver
+ *  Copyright (C) 2004, Vizzini (vizzini@plasmic.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * PROJECT:         ReactOS Floppy Driver
+ * FILE:            floppy.c
+ * PURPOSE:         Main floppy driver routines
+ * PROGRAMMER:      Vizzini (vizzini@plasmic.com)
+ * REVISIONS:
+ *                  15-Feb-2004 vizzini - Created
+ * NOTES:
+ *
+ * ---- General to-do items ----
+ * TODO: Clean up properly on failed init
+ * TODO: Add arc-path support so we can boot from the floppy
+ * TODO: Fix all these stupid STATUS_UNSUCCESSFUL return values
+ * TODO: Think about IO_NO_INCREMENT
+ * TODO: Figure out why CreateClose isn't called any more on XP.  Seems to correspond 
+ *       with the driver not being unloadable.  Does it have to do with cleanup?
+ * TODO: Consider using the built-in device object pointer in the stack location 
+ *       rather than the context area
+ * TODO: Think about StopDpcQueued -- could be a race; too tired atm to tell
+ *
+ * ---- Support for proper media detection ----
+ * TODO: Handle MFM flag
+ * TODO: Un-hardcode the data rate from various places
+ * TODO: Proper media detection (right now we're hardcoded to 1.44)
+ * TODO: Media detection based on sector 1
+ *
+ * ---- Support for normal floppy hardware ----
+ * TODO: Support the three primary types of controller
+ * TODO: Figure out thinkpad compatibility (I've heard rumors of weirdness with them)
+ *
+ * ---- Support for non-ISA and/or non-slave-dma controllers, if they exist ----
+ * TODO: Find controllers on non-ISA buses
+ * TODO: Think about making the interrupt shareable
+ * TODO: Support bus-master controllers.  PCI will break ATM.
+ */
+
+#include <ntddk.h>
+
+#include "floppy.h"
+#include "hardware.h"
+#include "csqrtns.h"
+#include "ioctl.h"
+#include "readwrite.h"
+
+/*
+ * Global controller info structures.  Each controller gets one.  Since the system 
+ * will probably have only one, with four being a very unlikely maximum, a static
+ * global array is easiest to deal with.
+ */
+CONTROLLER_INFO gControllerInfo[MAX_CONTROLLERS];
+ULONG gNumberOfControllers = 0;
+
+/* Queue thread management */
+KEVENT QueueThreadTerminate;
+PVOID ThreadObject;
+
+/* ISR DPC */
+KDPC Dpc;
+
+
+static VOID NTAPI MotorStopDpcFunc(PKDPC Dpc,
+			    PVOID DeferredContext,
+			    PVOID SystemArgument1,
+			    PVOID SystemArgument2)
+/*
+ * FUNCTION: Stop the floppy motor
+ * ARGUMENTS:
+ *     Dpc: DPC object that's going off
+ *     DeferredContext: called with DRIVE_INFO for drive to turn off
+ *     SystemArgument1: unused
+ *     SystemArgument2: unused
+ * NOTES:
+ *     - Must set an event to let other threads know we're done turning off the motor
+ *     - Called back at DISPATCH_LEVEL
+ */
+{
+  PCONTROLLER_INFO ControllerInfo = (PCONTROLLER_INFO)DeferredContext;
+
+  ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
+  ASSERT(ControllerInfo);
+
+  KdPrint(("floppy: MotorStopDpcFunc called\n"));
+
+  HwTurnOffMotor(ControllerInfo);
+  ControllerInfo->StopDpcQueued = FALSE;
+  KeSetEvent(&ControllerInfo->MotorStoppedEvent, IO_NO_INCREMENT, FALSE);
+}
+
+
+VOID NTAPI StartMotor(PDRIVE_INFO DriveInfo)
+/*
+ * FUNCTION: Start the motor, taking into account proper handling of the timer race
+ * ARGUMENTS:
+ *     DriveInfo: drive to start
+ * NOTES:
+ *     - Never call HwTurnOnMotor() directly
+ *     - This protocol manages a race between the cancel timer and the requesting thread.
+ *       You wouldn't want to turn on the motor and then cancel the timer, because the
+ *       cancel dpc might fire in the meantime, and that'd un-do what you just did.  If you
+ *       cancel the timer first, but KeCancelTimer returns false, the dpc is already running,
+ *       so you have to wait until the dpc is completly done running, or else you'll race
+ *       with the turner-offer
+ *     - PAGED_CODE because we wait
+ */
+{
+  PAGED_CODE();
+  ASSERT(DriveInfo);
+
+  KdPrint(("floppy: StartMotor called\n"));
+
+  if(DriveInfo->ControllerInfo->StopDpcQueued && 
+     !KeCancelTimer(&DriveInfo->ControllerInfo->MotorTimer))
+    {
+      /* Motor turner-offer is already running; wait for it to finish */
+      KeWaitForSingleObject(&DriveInfo->ControllerInfo->MotorStoppedEvent, Executive, KernelMode, FALSE, NULL);
+      DriveInfo->ControllerInfo->StopDpcQueued = FALSE;
+    }
+
+  HwTurnOnMotor(DriveInfo);
+}
+
+
+VOID NTAPI StopMotor(PCONTROLLER_INFO ControllerInfo)
+/*
+ * FUNCTION: Stop all motors on the controller
+ * ARGUMENTS:
+ *     DriveInfo: Drive to stop
+ * NOTES:
+ *     - Never call HwTurnOffMotor() directly
+ *     - This manages the timer cancelation race (see StartMotor for details).
+ *       All we have to do is set up a timer.
+ */
+{
+  LARGE_INTEGER StopTime;
+
+  ASSERT(ControllerInfo);
+
+  KdPrint(("floppy: StopMotor called\n"));
+
+  /* one relative second, in 100-ns units */
+  StopTime.QuadPart = 10000000;
+  StopTime.QuadPart *= -1;
+
+  KeClearEvent(&ControllerInfo->MotorStoppedEvent);
+  KeSetTimer(&ControllerInfo->MotorTimer, StopTime, &ControllerInfo->MotorStopDpc);
+  ControllerInfo->StopDpcQueued = TRUE;
+}
+
+
+VOID NTAPI WaitForControllerInterrupt(PCONTROLLER_INFO ControllerInfo)
+/*
+ * FUNCTION: Wait for the controller to interrupt, and then clear the event
+ * ARGUMENTS:
+ *     ControllerInfo: Controller to wait for
+ * NOTES:
+ *     - There is a small chance that an unexpected or spurious interrupt could
+ *       be lost with this clear/wait/clear scheme used in this driver.  This is
+ *       deemed to be an acceptable risk due to the unlikeliness of the scenario,
+ *       and the fact that it'll probably work fine next time.
+ *     - PAGED_CODE because it waits
+ */
+{
+  PAGED_CODE();
+  ASSERT(ControllerInfo);
+
+  KeWaitForSingleObject(&ControllerInfo->SynchEvent, Executive, KernelMode, FALSE, NULL);
+  KeClearEvent(&ControllerInfo->SynchEvent);
+}
+
+
+static NTSTATUS NTAPI CreateClose(PDEVICE_OBJECT DeviceObject, 
+                                  PIRP Irp)
+/*
+ * FUNCTION: Dispatch function called for Create and Close IRPs
+ * ARGUMENTS:
+ *     DeviceObject: DeviceObject that is the target of the IRP
+ *     Irp: IRP to process
+ * RETURNS:
+ *     STATUS_SUCCESS in all cases
+ * NOTES:
+ *     - The Microsoft sample drivers tend to return FILE_OPENED in Information, so I do too.
+ *     - No reason to fail the device open
+ *     - No state to track, so this routine is easy
+ *     - Can be called <= DISPATCH_LEVEL
+ *
+ * TODO: Figure out why this isn't getting called any more, and remove the ASSERT once that happens
+ */
+{
+  KdPrint(("floppy: CreateClose called\n"));
+
+  ASSERT(0);
+
+  Irp->IoStatus.Status = STATUS_SUCCESS;
+  Irp->IoStatus.Information = FILE_OPENED;
+
+  IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+  return STATUS_SUCCESS;
+}
+
+
+static NTSTATUS NTAPI Recalibrate(PDRIVE_INFO DriveInfo)
+/*
+ * FUNCTION: Start the recalibration process
+ * ARGUMENTS:
+ *     DriveInfo: Pointer to the driveinfo struct associated with the targeted drive
+ * RETURNS:
+ *     STATUS_SUCCESS on successful starting of the process
+ *     STATUS_UNSUCCESSFUL if it fails
+ * NOTES:
+ *     - Sometimes you have to do two recalibrations, particularly if the disk has <80 tracks.
+ *     - PAGED_CODE because we wait
+ */
+{
+  NTSTATUS Status;
+  ULONG i;
+
+  PAGED_CODE();
+  ASSERT(DriveInfo);
+
+  /* first turn on the motor */
+  /* Must stop after every start, prior to return */
+  StartMotor(DriveInfo);
+
+  /* set the data rate */
+  KdPrint(("floppy: FIXME: UN-HARDCODE DATA RATE\n"));
+  HwSetDataRate(DriveInfo->ControllerInfo, 0);
+
+  /* clear the event just in case the last call forgot */
+  KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);
+
+  /* sometimes you have to do this twice */
+  for(i = 0; i < 2; i++)
+    {
+      /* Send the command */
+      Status = HwRecalibrate(DriveInfo);
+      if(Status != STATUS_SUCCESS)
+	{
+	  KdPrint(("floppy: Recalibrate: HwRecalibrate returned error\n"));
+          continue;
+	}
+
+      WaitForControllerInterrupt(DriveInfo->ControllerInfo);
+
+      /* Get the results */
+      Status = HwRecalibrateResult(DriveInfo->ControllerInfo);
+      if(Status != STATUS_SUCCESS)
+	{
+	  KdPrint(("floppy: Recalibrate: HwRecalibrateResult returned error\n"));
+          break;
+        }
+    }
+
+  KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);
+
+  /* Must stop after every start, prior to return */
+  StopMotor(DriveInfo->ControllerInfo);
+
+  return Status;
+}
+
+
+NTSTATUS NTAPI ResetChangeFlag(PDRIVE_INFO DriveInfo)
+/*
+ * FUNCTION: Reset the drive's change flag (as reflected in the DIR)
+ * ARGUMENTS:
+ *     DriveInfo: the drive to reset
+ * RETURNS:
+ *     STATUS_SUCCESS if the changeline is cleared
+ *     STATUS_NO_MEDIA_IN_DEVICE if the changeline cannot be cleared
+ *     STATUS_IO_DEVICE_ERROR if the controller cannot be communicated with
+ * NOTES:
+ *     - Change reset procedure: recalibrate, seek 1, seek 0
+ *     - If the line is still set after that, there's clearly no disk in the
+ *       drive, so we return STATUS_NO_MEDIA_IN_DEVICE
+ *     - PAGED_CODE because we wait
+ */
+{
+  BOOLEAN DiskChanged;
+
+  PAGED_CODE();
+  ASSERT(DriveInfo);
+
+  KdPrint(("floppy: ResetChangeFlag called\n"));
+
+  /* Try to recalibrate.  We don't care if it works. */
+  Recalibrate(DriveInfo);
+
+  /* clear spurious interrupts in prep for seeks */
+  KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);
+
+  /* Seek to 1 */
+  if(HwSeek(DriveInfo, 1) != STATUS_SUCCESS)
+    {
+      KdPrint(("floppy: ResetChangeFlag(): HwSeek failed; returning STATUS_IO_DEVICE_ERROR\n"));
+      return STATUS_IO_DEVICE_ERROR;
+    }
+
+  WaitForControllerInterrupt(DriveInfo->ControllerInfo);
+
+  HwSenseInterruptStatus(DriveInfo->ControllerInfo);
+
+  /* Seek back to 0 */
+  if(HwSeek(DriveInfo, 1) != STATUS_SUCCESS)
+    {
+      KdPrint(("floppy: ResetChangeFlag(): HwSeek failed; returning STATUS_IO_DEVICE_ERROR\n"));
+      return STATUS_IO_DEVICE_ERROR;
+    }
+
+  WaitForControllerInterrupt(DriveInfo->ControllerInfo);
+
+  HwSenseInterruptStatus(DriveInfo->ControllerInfo);
+
+  /* Check the change bit */
+  if(HwDiskChanged(DriveInfo, &DiskChanged) != STATUS_SUCCESS)
+    {
+      KdPrint(("floppy: ResetChangeFlag(): HwDiskChagned failed; returning STATUS_IO_DEVICE_ERROR\n"));
+      return STATUS_IO_DEVICE_ERROR;
+    }
+
+  /* if the change flag is still set, there's probably no media in the drive. */
+  if(DiskChanged)
+    return STATUS_NO_MEDIA_IN_DEVICE;
+
+  /* else we're done! */
+  return STATUS_SUCCESS;
+}
+
+
+static VOID NTAPI Unload(PDRIVER_OBJECT DriverObject)
+/*
+ * FUNCTION: Unload the driver from memory
+ * ARGUMENTS:
+ *     DriverObject - The driver that is being unloaded
+ *
+ * TODO: Delete ARC links
+ */
+{
+  ULONG i,j;
+
+  PAGED_CODE();
+
+  KdPrint(("floppy: unloading\n"));
+
+  KeSetEvent(&QueueThreadTerminate, 0, FALSE);
+  KeWaitForSingleObject(ThreadObject, Executive, KernelMode, FALSE, 0);
+  ObDereferenceObject(ThreadObject);
+
+  for(i = 0; i < gNumberOfControllers; i++)
+    {
+      if(!gControllerInfo[i].Populated)
+	continue;
+
+      for(j = 0; j < gControllerInfo[i].NumberOfDrives; j++)
+	{
+          if(gControllerInfo[i].DriveInfo[j].DeviceObject)
+            {
+	      UNICODE_STRING Link;
+	      RtlInitUnicodeString(&Link, gControllerInfo[i].DriveInfo[j].SymLinkBuffer);
+	      IoDeleteSymbolicLink(&Link);
+              IoDeleteDevice(gControllerInfo[i].DriveInfo[j].DeviceObject);
+            }
+	}
+
+      IoDisconnectInterrupt(gControllerInfo[i].InterruptObject);
+
+      /* Power down the controller */
+      HwPowerOff(&gControllerInfo[i]);
+    }
+}
+
+
+static NTSTATUS NTAPI ConfigCallback(PVOID Context,
+                                     PUNICODE_STRING PathName,
+                                     INTERFACE_TYPE BusType,
+                                     ULONG BusNumber,
+                                     PKEY_VALUE_FULL_INFORMATION *BusInformation,
+                                     CONFIGURATION_TYPE ControllerType,
+                                     ULONG ControllerNumber,
+                                     PKEY_VALUE_FULL_INFORMATION *ControllerInformation,
+                                     CONFIGURATION_TYPE PeripheralType,
+                                     ULONG PeripheralNumber,
+                                     PKEY_VALUE_FULL_INFORMATION *PeripheralInformation)
+/*
+ * FUNCTION: Callback to IoQueryDeviceDescription, which tells us about our controllers
+ * ARGUMENTS:
+ *     Context: Unused
+ *     PathName: Unused
+ *     BusType: Type of the bus that our controller is on
+ *     BusNumber: Number of the bus that our controller is on
+ *     BusInformation: Unused
+ *     ControllerType: Unused
+ *     ControllerNumber: Number of the controller that we're adding
+ *     ControllerInformation: Full configuration information for our controller
+ *     PeripheralType: Unused
+ *     PeripheralNumber: Unused
+ *     PeripheralInformation: Full configuration information for each drive on our controller
+ * RETURNS:
+ *     STATUS_SUCCESS in all cases
+ * NOTES:
+ *     - The only documentation I've found about the contents of these structures is
+ *       from the various Microsoft floppy samples and from the DDK headers.  They're
+ *       very vague, though, so I'm only mostly sure that this stuff is correct, as
+ *       the MS samples do things completely differently than I have done them.  Seems
+ *       to work in my VMWare, though.
+ *     - Basically, the function gets all of the information (port, dma, irq) about the
+ *       controller, and then loops through all of the drives presented in PeripheralInformation.
+ *     - Each controller has a CONTROLLER_INFO created for it, and each drive has a DRIVE_INFO.
+ *     - Device objects are created for each drive (not controller), as that's the targeted
+ *       device in the eyes of the rest of the OS.  Each DRIVE_INFO points to a single CONTROLLER_INFO.
+ *     - We only support up to four controllers in the whole system, each of which supports up to four
+ *       drives.
+ */
+{
+  PKEY_VALUE_FULL_INFORMATION ControllerFullDescriptor = ControllerInformation[IoQueryDeviceConfigurationData];
+  PCM_FULL_RESOURCE_DESCRIPTOR ControllerResourceDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)((PCHAR)ControllerFullDescriptor + 
+                                                               ControllerFullDescriptor->DataOffset);
+
+  PKEY_VALUE_FULL_INFORMATION PeripheralFullDescriptor = PeripheralInformation[IoQueryDeviceConfigurationData];
+  PCM_FULL_RESOURCE_DESCRIPTOR PeripheralResourceDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)((PCHAR)PeripheralFullDescriptor + 
+                                                               PeripheralFullDescriptor->DataOffset);
+
+  PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor;
+  PCM_FLOPPY_DEVICE_DATA FloppyDeviceData;
+  UCHAR i;
+
+  PAGED_CODE();
+
+  KdPrint(("floppy: ConfigCallback called with ControllerNumber %d\n", ControllerNumber));
+
+  gControllerInfo[gNumberOfControllers].ControllerNumber = ControllerNumber;
+  gControllerInfo[gNumberOfControllers].InterfaceType = BusType;
+  gControllerInfo[gNumberOfControllers].BusNumber = BusNumber;
+
+  /* Get controller interrupt level/vector, dma channel, and port base */
+  for(i = 0; i < ControllerResourceDescriptor->PartialResourceList.Count; i++)
+    {
+      KeInitializeEvent(&gControllerInfo[gNumberOfControllers].SynchEvent, NotificationEvent, FALSE);
+
+      PartialDescriptor = &ControllerResourceDescriptor->PartialResourceList.PartialDescriptors[i];
+
+      if(PartialDescriptor->Type == CmResourceTypeInterrupt)
+        {
+          gControllerInfo[gNumberOfControllers].Level = PartialDescriptor->u.Interrupt.Level;
+          gControllerInfo[gNumberOfControllers].Vector = PartialDescriptor->u.Interrupt.Vector;
+
+          if(PartialDescriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
+            gControllerInfo[gNumberOfControllers].InterruptMode = Latched;
+          else
+            gControllerInfo[gNumberOfControllers].InterruptMode = LevelSensitive;
+        }
+
+      else if(PartialDescriptor->Type == CmResourceTypePort)
+        {
+          PHYSICAL_ADDRESS TranslatedAddress;
+          ULONG AddressSpace = 0x1; /* I/O Port Range */
+
+          if(!HalTranslateBusAddress(BusType, BusNumber, PartialDescriptor->u.Port.Start, &AddressSpace, &TranslatedAddress))
+            ASSERT(0);
+
+          if(AddressSpace == 0)
+            gControllerInfo[gNumberOfControllers].BaseAddress = MmMapIoSpace(TranslatedAddress, 8, FALSE); // symbolic constant?
+          else
+            gControllerInfo[gNumberOfControllers].BaseAddress = (PUCHAR)TranslatedAddress.u.LowPart;
+        }
+
+      else if(PartialDescriptor->Type == CmResourceTypeDma)
+        gControllerInfo[gNumberOfControllers].Dma = PartialDescriptor->u.Dma.Channel;
+    }
+
+  /* Start with 0 drives, then go looking */
+  gControllerInfo[gNumberOfControllers].NumberOfDrives = 0;
+
+  /* learn about drives attached to controller */
+  for(i = 0; i < PeripheralResourceDescriptor->PartialResourceList.Count; i++)
+    {
+      PDRIVE_INFO DriveInfo = &gControllerInfo[gNumberOfControllers].DriveInfo[i];
+
+      PartialDescriptor = &PeripheralResourceDescriptor->PartialResourceList.PartialDescriptors[i];
+
+      if(PartialDescriptor->Type != CmResourceTypeDeviceSpecific)
+        continue;
+
+      FloppyDeviceData = (PCM_FLOPPY_DEVICE_DATA)(PartialDescriptor + 1);
+
+      DriveInfo->ControllerInfo = &gControllerInfo[gNumberOfControllers];
+      DriveInfo->UnitNumber = i;
+
+      DriveInfo->FloppyDeviceData.MaxDensity = FloppyDeviceData->MaxDensity;
+      DriveInfo->FloppyDeviceData.MountDensity = FloppyDeviceData->MountDensity;
+      DriveInfo->FloppyDeviceData.StepRateHeadUnloadTime = FloppyDeviceData->StepRateHeadUnloadTime;
+      DriveInfo->FloppyDeviceData.HeadLoadTime = FloppyDeviceData->HeadLoadTime;
+      DriveInfo->FloppyDeviceData.MotorOffTime = FloppyDeviceData->MotorOffTime;
+      DriveInfo->FloppyDeviceData.SectorLengthCode = FloppyDeviceData->SectorLengthCode;
+      DriveInfo->FloppyDeviceData.SectorPerTrack = FloppyDeviceData->SectorPerTrack;
+      DriveInfo->FloppyDeviceData.ReadWriteGapLength = FloppyDeviceData->ReadWriteGapLength;
+      DriveInfo->FloppyDeviceData.FormatGapLength = FloppyDeviceData->FormatGapLength;
+      DriveInfo->FloppyDeviceData.FormatFillCharacter = FloppyDeviceData->FormatFillCharacter;
+      DriveInfo->FloppyDeviceData.HeadSettleTime = FloppyDeviceData->HeadSettleTime;
+      DriveInfo->FloppyDeviceData.MotorSettleTime = FloppyDeviceData->MotorSettleTime;
+      DriveInfo->FloppyDeviceData.MaximumTrackValue = FloppyDeviceData->MaximumTrackValue;
+      DriveInfo->FloppyDeviceData.DataTransferLength = FloppyDeviceData->DataTransferLength;
+
+      /* Once it's all set up, acknowledge its existance in the controller info object */
+      gControllerInfo[gNumberOfControllers].NumberOfDrives++;
+    }
+
+  gControllerInfo[gNumberOfControllers].Populated = TRUE;
+  gNumberOfControllers++;
+
+  return STATUS_SUCCESS;
+}
+
+
+static BOOLEAN NTAPI Isr(PKINTERRUPT Interrupt,
+                         PVOID ServiceContext)
+/*
+ * FUNCTION: Interrupt service routine for the controllers
+ * ARGUMENTS:
+ *     Interrupt: Interrupt object representing the interrupt that occured
+ *     ServiceContext: Pointer to the ControllerInfo object that caused the interrupt
+ * RETURNS:
+ *     TRUE in all cases (see notes)
+ * NOTES:
+ *     - We should always be the target of the interrupt, being an edge-triggered ISA interrupt, but
+ *       this won't be the case with a level-sensitive system like PCI
+ *     - Note that it probably doesn't matter if the interrupt isn't dismissed, as it's edge-triggered.
+ *       It probably won't keep re-interrupting.
+ *     - There are two different ways to dismiss a floppy interrupt.  If the command has a result phase
+ *       (see intel datasheet), you dismiss the interrupt by reading the first data byte.  If it does
+ *       not, you dismiss the interrupt by doing a Sense Interrupt command.  Again, because it's edge-
+ *       triggered, this is safe to not do here, as we can just wait for the DPC.
+ *     - Either way, we don't want to do this here.  The controller shouldn't interrupt again, so we'll
+ *       schedule a DPC to take care of it.
+ * TODO:
+ *     - This driver really cannot shrare interrupts, as I don't know how to conclusively say 
+ *       whether it was our controller that interrupted or not.  I just have to assume that any time
+ *       my ISR gets called, it was my board that called it.  Dumb design, yes, but it goes back to 
+ *       the semantics of ISA buses.  That, and I don't know much about ISA drivers. :-)
+ *       UPDATE: The high bit of Status Register A seems to work on non-AT controllers.
+ *     - Called at DIRQL
+ */
+{
+  PCONTROLLER_INFO ControllerInfo = (PCONTROLLER_INFO)ServiceContext;
+
+  ASSERT(ControllerInfo);
+
+  KdPrint(("floppy: ISR called\n"));
+
+  /*
+   * Due to the stupidity of the drive/controller relationship on the floppy drive, only one device object
+   * can have an active interrupt pending.  Due to the nature of these IRPs, though, there will only ever
+   * be one thread expecting an interrupt at a time, and furthermore, Interrupts (outside of spurious ones)
+   * won't ever happen unless a thread is expecting them.  Therefore, all we have to do is signal an event
+   * and we're done.  Queue a DPC and leave.
+   */
+  KeInsertQueueDpc(&ControllerInfo->Dpc, NULL, NULL);
+
+  return TRUE;
+}
+
+
+VOID NTAPI DpcForIsr(PKDPC Dpc,
+                     PVOID Context,
+                     PVOID SystemArgument1,
+                     PVOID SystemArgument2)
+/*
+ * FUNCTION: This DPC gets queued by every ISR.  Does the real per-interrupt work.
+ * ARGUMENTS:
+ *     Dpc: Pointer to the DPC object that represents our function
+ *     DeviceObject: Device that this DPC is running for
+ *     Irp: Unused
+ *     Context: Pointer to our ControllerInfo struct 
+ * NOTES:
+ *     - This function just kicks off whatever the SynchEvent is and returns.  We depend on
+ *       the thing that caused the drive to interrupt to handle the work of clearing the interrupt.
+ *       This enables us to get back to PASSIVE_LEVEL and not hog system time on a really stupid,
+ *       slow, screwed-up piece of hardare.
+ *     - If nothing is waiting for us to set the event, the interrupt is effectively lost and will
+ *       never be dismissed.  I wonder if this will become a problem.
+ *     - Called at DISPATCH_LEVEL
+ */
+{
+  PCONTROLLER_INFO ControllerInfo = (PCONTROLLER_INFO)Context;
+
+  ASSERT(ControllerInfo);
+
+  KdPrint(("floppy: DpcForIsr called\n"));
+
+  KeSetEvent(&ControllerInfo->SynchEvent, IO_NO_INCREMENT, FALSE);
+}
+
+
+static NTSTATUS NTAPI InitController(PCONTROLLER_INFO ControllerInfo)
+/*
+ * FUNCTION:  Initialize a newly-found controller
+ * ARGUMENTS:
+ *     ControllerInfo: pointer to the controller to be initialized
+ * RETURNS:
+ *     STATUS_SUCCESS if the controller is successfully initialized
+ *     STATUS_UNSUCCESSFUL otherwise
+ */
+{
+  int i;
+  UCHAR HeadLoadTime;
+  UCHAR HeadUnloadTime;
+  UCHAR StepRateTime;
+
+  PAGED_CODE();
+  ASSERT(ControllerInfo);
+
+  KdPrint(("floppy: InitController called with Controller 0x%x\n", ControllerInfo));
+
+  KeClearEvent(&ControllerInfo->SynchEvent);
+
+  //HwDumpRegisters(ControllerInfo);
+
+  KdPrint(("floppy: InitController: resetting the controller\n"));
+
+  /* Reset the controller */
+  if(HwReset(ControllerInfo) != STATUS_SUCCESS)
+    {
+      KdPrint(("floppy: InitController: unable to reset controller\n"));
+      return STATUS_UNSUCCESSFUL;
+    }
+
+  //HwDumpRegisters(ControllerInfo);
+
+  KdPrint(("floppy: InitController: setting data rate\n"));
+
+  /* Set data rate */
+  if(HwSetDataRate(ControllerInfo, DRSR_DSEL_500KBPS) != STATUS_SUCCESS)
+    {
+      KdPrint(("floppy: InitController: unable to set data rate\n"));
+      return STATUS_UNSUCCESSFUL;
+    }
+
+  KdPrint(("floppy: InitController: waiting for initial interrupt\n"));
+
+  /* Wait for an interrupt */
+  WaitForControllerInterrupt(ControllerInfo);
+
+  /* Reset means you have to clear each of the four interrupts (one per drive) */
+  for(i = 0; i < MAX_DRIVES_PER_CONTROLLER; i++)
+    {
+      KdPrint(("floppy: InitController: Sensing interrupt %d\n", i));
+      //HwDumpRegisters(ControllerInfo);
+
+      if(HwSenseInterruptStatus(ControllerInfo) != STATUS_SUCCESS)
+	{
+	  KdPrint(("floppy: InitController: Unable to clear interrupt 0x%x\n", i));
+	  return STATUS_UNSUCCESSFUL;
+	}
+    }
+
+  KdPrint(("floppy: InitController: done sensing interrupts\n"));
+
+  //HwDumpRegisters(ControllerInfo);
+
+  /* Next, see if we have the right version to do implied seek */
+  if(HwGetVersion(ControllerInfo) != VERSION_ENHANCED)
+    {
+      KdPrint(("floppy: InitController: enhanced version not supported; disabling implied seeks\n"));
+      ControllerInfo->ImpliedSeeks = FALSE;
+      ControllerInfo->Model30 = FALSE;
+    }
+  else
+    {
+      /* If so, set that up -- all defaults below except first TRUE for EIS */
+      if(HwConfigure(ControllerInfo, TRUE, TRUE, FALSE, 0, 0) != STATUS_SUCCESS)
+	{
+	  KdPrint(("floppy: InitController: unable to set up implied seek\n"));
+          ControllerInfo->ImpliedSeeks = FALSE;
+	}
+      else 
+	{
+	  KdPrint(("floppy: InitController: implied seeks set!\n"));
+          ControllerInfo->ImpliedSeeks = TRUE;
+	}
+
+      ControllerInfo->Model30 = TRUE;
+    }
+  
+  /* Specify */
+  KdPrint(("FLOPPY: FIXME: Figure out speed\n"));
+  HeadLoadTime = SPECIFY_HLT_500K;
+  HeadUnloadTime = SPECIFY_HUT_500K;
+  StepRateTime = SPECIFY_SRT_500K;
+
+  KdPrint(("floppy: InitController: issuing specify command to controller\n"));
+      
+  /* Don't disable DMA --> enable dma (dumb & confusing) */
+  if(HwSpecify(ControllerInfo, HeadLoadTime, HeadUnloadTime, StepRateTime, FALSE) != STATUS_SUCCESS)
+    {
+      KdPrint(("floppy: InitController: unable to specify options\n"));
+      return STATUS_UNSUCCESSFUL;
+    }
+
+  //HwDumpRegisters(ControllerInfo);
+
+  /* Init the stop stuff */
+  KeInitializeDpc(&ControllerInfo->MotorStopDpc, MotorStopDpcFunc, ControllerInfo);
+  KeInitializeTimer(&ControllerInfo->MotorTimer);
+  KeInitializeEvent(&ControllerInfo->MotorStoppedEvent, SynchronizationEvent, FALSE);
+  ControllerInfo->StopDpcQueued = FALSE;
+
+  /* Recalibrate each drive on the controller (depends on StartMotor, which depends on the timer stuff above) */
+  /* TODO: Handle failure of one or more drives */
+  for(i = 0; i < ControllerInfo->NumberOfDrives; i++)
+    {
+      KdPrint(("floppy: InitController: recalibrating drive 0x%x on controller 0x%x\n", i, ControllerInfo));
+
+      if(Recalibrate(&ControllerInfo->DriveInfo[i]) != STATUS_SUCCESS)
+	{
+	  KdPrint(("floppy: InitController: unable to recalibrate drive\n"));
+	  return STATUS_UNSUCCESSFUL;
+	}
+    }
+
+  //HwDumpRegisters(ControllerInfo);
+
+  KdPrint(("floppy: InitController: done initializing; returning STATUS_SUCCESS\n"));
+
+  return STATUS_SUCCESS;
+}
+
+
+static BOOLEAN NTAPI AddControllers(PDRIVER_OBJECT DriverObject)
+/*
+ * FUNCTION: Called on initialization to find our controllers and build device and controller objects for them
+ * ARGUMENTS:
+ *     DriverObject: Our driver's DriverObject (so we can create devices against it)
+ * RETURNS:
+ *     FALSE if we can't allocate a device, adapter, or interrupt object, or if we fail to find any controllers
+ *     TRUE otherwise (i.e. we have at least one fully-configured controller)
+ * NOTES:
+ *     - Currently we only support ISA buses.  
+ *     - BUG: Windows 2000 seems to clobber the response from the IoQueryDeviceDescription callback, so now we
+ *       just test a boolean value in the first object to see if it was completely populated.  The same value
+ *       is tested for each controller before we build device objects for it.
+ * TODO:
+ *     - Figure out a workable interrupt-sharing scheme and un-hardcode FALSE in IoConnectInterrupt
+ *     - Add support for non-ISA buses, by looping through all of the bus types looking for floppy controllers
+ *     - Report resource usage to the HAL
+ *     - Add ARC path support
+ *     - Think more about error handling; atm most errors abort the start of the driver
+ */
+{
+  INTERFACE_TYPE InterfaceType = Isa;
+  CONFIGURATION_TYPE ControllerType = DiskController;
+  CONFIGURATION_TYPE PeripheralType = FloppyDiskPeripheral;
+  KAFFINITY Affinity;
+  DEVICE_DESCRIPTION DeviceDescription;
+  UCHAR i;
+  UCHAR j;
+
+  PAGED_CODE();
+
+  /* Find our controllers on all ISA buses */
+  IoQueryDeviceDescription(&InterfaceType, 0, &ControllerType, 0, &PeripheralType, 0, ConfigCallback, 0);
+
+  /* 
+   * w2k breaks the return val from ConfigCallback, so we have to hack around it, rather than just
+   * looking for a return value from ConfigCallback
+   */ 
+  if(!gControllerInfo[0].Populated)
+    {
+      KdPrint(("floppy: AddControllers: failed to get controller info from registry\n"));
+      return FALSE;
+    }
+
+  /* Now that we have a controller, set it up with the system */
+  for(i = 0; i < gNumberOfControllers; i++)
+    {
+      /* 0: Report resource usage to the kernel, to make sure they aren't assigned to anyone else */
+      /* XXX do me baby */
+
+      /* 1: Set up interrupt */
+      gControllerInfo[i].MappedVector = HalGetInterruptVector(gControllerInfo[i].InterfaceType, gControllerInfo[i].BusNumber,
+                                                              gControllerInfo[i].Level, gControllerInfo[i].Vector,
+                                                              &gControllerInfo[i].MappedLevel, &Affinity);
+
+      /* Must set up the DPC before we connect the interrupt */
+      KeInitializeDpc(&gControllerInfo[i].Dpc, DpcForIsr, &gControllerInfo[i]);
+
+      KdPrint(("floppy: Connecting interrupt %d to controller%d (object 0x%x)\n", gControllerInfo[i].MappedVector,
+	       i, &gControllerInfo[i]));
+
+      /* NOTE: We cannot share our interrupt, even on level-triggered buses.  See Isr() for details. */
+      if(IoConnectInterrupt(&gControllerInfo[i].InterruptObject, Isr, &gControllerInfo[i], 0, gControllerInfo[i].MappedVector,
+         gControllerInfo[i].MappedLevel, gControllerInfo[i].MappedLevel, gControllerInfo[i].InterruptMode,
+         FALSE, Affinity, 0) != STATUS_SUCCESS)
+        {
+          KdPrint(("floppy: AddControllers: unable to connect interrupt\n"));
+          return FALSE;
+        }
+
+      /* 2: Set up DMA */
+
+      memset(&DeviceDescription, 0, sizeof(DeviceDescription));
+      DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
+      DeviceDescription.Master = (gControllerInfo[i].InterfaceType == PCIBus ? TRUE : FALSE); /* guessing if not pci not master */
+      DeviceDescription.DmaChannel = gControllerInfo[i].Dma;
+      DeviceDescription.InterfaceType = gControllerInfo[i].InterfaceType;
+      DeviceDescription.BusNumber = gControllerInfo[i].BusNumber;
+
+      if(gControllerInfo[i].InterfaceType == PCIBus)
+        {
+          DeviceDescription.Dma32BitAddresses = TRUE;
+          DeviceDescription.DmaWidth = Width32Bits;
+        }
+      else
+	/* DMA 0,1,2,3 are 8-bit; 4,5,6,7 are 16-bit (4 is chain i think) */
+        DeviceDescription.DmaWidth = gControllerInfo[i].Dma > 3 ? Width16Bits: Width8Bits;
+
+      gControllerInfo[i].AdapterObject = HalGetAdapter(&DeviceDescription, &gControllerInfo[i].MapRegisters);
+
+      if(!gControllerInfo[i].AdapterObject)
+        {
+          KdPrint(("floppy: AddControllers: unable to allocate an adapter object\n"));
+          IoDisconnectInterrupt(gControllerInfo[i].InterruptObject);
+          return FALSE;
+        }
+
+      /* 2b: Initialize the new controller */
+      if(InitController(&gControllerInfo[i]) != STATUS_SUCCESS)
+	{
+	  KdPrint(("floppy: AddControllers():Unable to set up controller %d - initialization failed\n", i));
+	  ASSERT(0); /* FIXME: clean up properly */
+	  continue;
+	}
+
+      /* 3: per-drive setup */
+      for(j = 0; j < gControllerInfo[i].NumberOfDrives; j++)
+        {
+          WCHAR DeviceNameBuf[MAX_DEVICE_NAME];
+          UNICODE_STRING DeviceName;
+          UNICODE_STRING LinkName;
+	  UCHAR DriveNumber;
+
+	  KdPrint(("floppy: AddControllers(): Configuring drive %d on controller %d\n", i, j));
+
+	  /* 
+	   * 3a: create a device object for the drive
+	   * Controllers and drives are 0-based, so the combos are:
+	   * 0: 0,0
+	   * 1: 0,1
+	   * 2: 0,2
+	   * 3: 0,3
+	   * 4: 1,0
+	   * 5: 1,1
+	   * ...
+	   * 14: 3,2
+	   * 15: 3,3 
+	   */ 
+	  DriveNumber = i*4 + j;
+
+          swprintf(DeviceNameBuf, L"\\Device\\Floppy%d", DriveNumber);
+          RtlInitUnicodeString(&DeviceName, DeviceNameBuf);
+
+          if(IoCreateDevice(DriverObject, sizeof(PVOID), &DeviceName, 
+			    FILE_DEVICE_DISK, FILE_REMOVABLE_MEDIA | FILE_FLOPPY_DISKETTE, FALSE,
+                            &gControllerInfo[i].DriveInfo[j].DeviceObject) != STATUS_SUCCESS)
+            {
+              KdPrint(("floppy: AddControllers: unable to register a Device object\n"));
+              IoDisconnectInterrupt(gControllerInfo[i].InterruptObject);
+              return FALSE;
+            }
+
+	  KdPrint(("floppy: AddControllers: New device: %S (0x%x)\n", DeviceNameBuf, gControllerInfo[i].DriveInfo[j].DeviceObject));
+
+	  /* 3c: Set flags up */
+	  gControllerInfo[i].DriveInfo[j].DeviceObject->Flags |= DO_DIRECT_IO;
+
+	  /* 3d: Create a symlink */
+	  swprintf(gControllerInfo[i].DriveInfo[j].SymLinkBuffer, L"\\DosDevices\\%c:", DriveNumber + 'A');
+	  RtlInitUnicodeString(&LinkName, gControllerInfo[i].DriveInfo[j].SymLinkBuffer);
+	  if(IoCreateSymbolicLink(&LinkName, &DeviceName) != STATUS_SUCCESS)
+	    {
+	      KdPrint(("floppy: AddControllers: Unable to create a symlink for drive %d\n", DriveNumber));
+	      IoDisconnectInterrupt(gControllerInfo[i].InterruptObject);
+	      // delete devices too?
+	      return FALSE;
+	    }
+
+	  /* 3e: Set up the DPC */
+	  IoInitializeDpcRequest(gControllerInfo[i].DriveInfo[j].DeviceObject, DpcForIsr);
+
+	  /* 3f: Point the device extension at our DriveInfo struct */
+	  gControllerInfo[i].DriveInfo[j].DeviceObject->DeviceExtension = &gControllerInfo[i].DriveInfo[j];
+
+	  /* 3g: neat comic strip */
+
+	  /* 3h: set the initial media type to unknown */
+	  memset(&gControllerInfo[i].DriveInfo[j].DiskGeometry, 0, sizeof(DISK_GEOMETRY));
+	  gControllerInfo[i].DriveInfo[j].DiskGeometry.MediaType = Unknown;
+        }
+    }
+
+  KdPrint(("floppy: AddControllers: --------------------------------------------> finished adding controllers\n"));
+
+  return TRUE;
+}
+
+
+VOID NTAPI SignalMediaChanged(PDEVICE_OBJECT DeviceObject,
+                              PIRP Irp)
+/*
+ * FUNCTION: Process an IRP when the media has changed, and possibly notify the user
+ * ARGUMENTS:
+ *     DeviceObject: DeviceObject associated with the IRP
+ *     Irp: IRP that we're failing due to change
+ * NOTES:
+ *     - This procedure is documented in the DDK by "Notifying the File System of Possible Media Changes",
+ *       "IoSetHardErrorOrVerifyDevice", and by "Responding to Check-Verify Requests from the File System".
+ *     - Callable at <= DISPATCH_LEVEL
+ */
+{
+  PDRIVE_INFO DriveInfo = DeviceObject->DeviceExtension;
+
+  KdPrint(("floppy: SignalMediaChanged called\n"));
+
+  DriveInfo->DiskChangeCount++;
+
+  /* If volume is not mounted, do NOT set verify and return STATUS_IO_DEVICE_ERROR */
+  if(!(DeviceObject->Vpb->Flags & VPB_MOUNTED))
+    {
+      Irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR;
+      Irp->IoStatus.Information = 0;
+      return;
+    }
+
+  /* Notify the filesystem that it will need to verify the volume */
+  DeviceObject->Flags |= DO_VERIFY_VOLUME;
+  Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
+  Irp->IoStatus.Information = 0;
+
+  /* 
+   * If this is a user-based, threaded request, let the IO manager know to pop up a box asking
+   * the user to supply the correct media, but only if the error (which we just picked out above)
+   * is deemed by the IO manager to be "user induced".  The reason we don't just unconditionally
+   * call IoSetHardError... is because MS might change the definition of "user induced" some day,
+   * and we don't want to have to remember to re-code this.
+   */
+  if(Irp->Tail.Overlay.Thread && IoIsErrorUserInduced(Irp->IoStatus.Status))
+    IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
+}
+
+
+VOID NTAPI QueueThread(PVOID Context)
+/*
+ * FUNCTION: Thread that manages the queue and dispatches any queued requests
+ * ARGUMENTS:
+ *     Context: unused
+ */
+{
+  PIRP Irp;
+  PIO_STACK_LOCATION Stack;
+  PDEVICE_OBJECT DeviceObject;
+  PVOID Objects[2];
+
+  PAGED_CODE();
+
+  Objects[0] = &QueueSemaphore;
+  Objects[1] = &QueueThreadTerminate;
+
+  for(;;)
+    {
+      KeWaitForMultipleObjects(2, Objects, WaitAny, Executive, KernelMode, FALSE, NULL, NULL);
+
+      if(KeReadStateEvent(&QueueThreadTerminate))
+	{
+	  KdPrint(("floppy: QueueThread terminating\n"));
+          return;
+	}
+
+      KdPrint(("floppy: QueueThread: servicing an IRP\n"));
+
+      Irp = IoCsqRemoveNextIrp(&Csq, 0);
[truncated at 1000 lines; 125 more skipped]

reactos/drivers/storage/floppy
floppy.h added at 1.1
diff -N floppy.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ floppy.h	1 Mar 2004 06:37:26 -0000	1.1
@@ -0,0 +1,145 @@
+/*
+ *  ReactOS Floppy Driver
+ *  Copyright (C) 2004, Vizzini (vizzini@plasmic.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * PROJECT:         ReactOS Floppy Driver
+ * FILE:            floppy.h
+ * PURPOSE:         Header for Main floppy driver routines
+ * PROGRAMMER:      Vizzini (vizzini@plasmic.com)
+ * REVISIONS:
+ *                  15-Feb-2004 vizzini - Created
+ */
+
+#define MAX_DEVICE_NAME 255
+#define MAX_ARC_PATH_LEN 255
+#define MAX_DRIVES_PER_CONTROLLER 4
+#define MAX_CONTROLLERS 4
+
+#ifdef _MSC_VER
+/* MS doesn't prototype this but the w2k kernel exports it */
+int _cdecl swprintf(const WCHAR *, ...);
+
+/* need ioctls in ddk build mode */
+#include <ntdddisk.h>
+#endif
+
+/* missing from ros headers */
+/* TODO: fix this right */
+#ifndef KdPrint
+#define KdPrint(x) DbgPrint x
+#endif
+
+#ifndef ASSERT
+#define ASSERT(x) { if(!(x)) __asm__("int $3\n"); }
+#endif
+
+#ifndef assert
+#define assert(x) ASSERT(x)
+#endif
+
+#ifndef PAGED_CODE
+#define PAGED_CODE() {ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);}
+#endif
+
+struct _CONTROLLER_INFO;
+
+typedef struct _DRIVE_INFO
+{
+  struct _CONTROLLER_INFO  *ControllerInfo;
+  UCHAR                    UnitNumber; /* 0,1,2,3 */
+  LARGE_INTEGER            MotorStartTime;
+  PDEVICE_OBJECT           DeviceObject;
+  CM_FLOPPY_DEVICE_DATA    FloppyDeviceData;
+  DISK_GEOMETRY            DiskGeometry;
+  UCHAR                    BytesPerSectorCode;
+  WCHAR                    SymLinkBuffer[MAX_DEVICE_NAME];
+  ULONG                    DiskChangeCount;
+} DRIVE_INFO, *PDRIVE_INFO;
+
+typedef struct _CONTROLLER_INFO
+{
+  BOOLEAN          Populated;
+  ULONG            ControllerNumber;
+  INTERFACE_TYPE   InterfaceType;
+  ULONG            BusNumber;
+  ULONG            Level;
+  KIRQL            MappedLevel;
+  ULONG            Vector;
+  ULONG            MappedVector;
+  KINTERRUPT_MODE  InterruptMode;
+  PUCHAR           BaseAddress;
+  ULONG            Dma;
+  ULONG            MapRegisters;
+  PVOID            MapRegisterBase;
+  BOOLEAN          Master;
+  KEVENT           SynchEvent;
+  KDPC             Dpc;
+  PKINTERRUPT      InterruptObject;
+  PADAPTER_OBJECT  AdapterObject;
+  UCHAR            NumberOfDrives;
+  BOOLEAN          ImpliedSeeks;
+  DRIVE_INFO       DriveInfo[MAX_DRIVES_PER_CONTROLLER];
+  PDRIVE_INFO      CurrentDrive;
+  BOOLEAN          Model30;
+  KEVENT           MotorStoppedEvent;
+  KTIMER           MotorTimer;
+  KDPC             MotorStopDpc;
+  BOOLEAN          StopDpcQueued;
+} CONTROLLER_INFO, *PCONTROLLER_INFO;
+
+NTSTATUS NTAPI ReadWrite(PDEVICE_OBJECT DeviceObject,
+                                PIRP Irp);
+
+VOID NTAPI QueueThread(PVOID Context);
+
+extern KEVENT QueueThreadTerminate;
+
+NTSTATUS NTAPI DriverEntry(PDRIVER_OBJECT DriverObject,
+                           PUNICODE_STRING RegistryPath);
+
+VOID NTAPI SignalMediaChanged(PDEVICE_OBJECT DeviceObject,
+                              PIRP Irp);
+
+/*
+ * MEDIA TYPES
+ *
+ * This table was found at http://www.nondot.org/sabre/os/files/Disk/FloppyMediaIDs.txt.  
+ * Thanks to raster@indirect.com for this information.
+ *
+ * Format   Size   Cyls   Heads  Sec/Trk   FATs   Sec/FAT   Sec/Root   Media
+ * 160K     5 1/4   40      1       8       2        ?         ?        FE
+ * 180K     5 1/4   40      1       9       2        ?         4        FC
+ * 320K     5 1/4   40      2       8       2        ?         ?        FF
+ * 360K     5 1/4   40      2       9       2        4         7        FD
+ * 1.2M     5 1/4   80      2      15       2       14        14        F9
+ * 720K     3 1/2   80      2       9       2        6         7        F9
+ * 1.44M    3 1/2   80      2      18       2       18        14        F0
+ */
+
+#define GEOMETRY_144_MEDIATYPE F3_1Pt44_512
+#define GEOMETRY_144_CYLINDERS 80
+#define GEOMETRY_144_TRACKSPERCYLINDER 2
+#define GEOMETRY_144_SECTORSPERTRACK 18
+#define GEOMETRY_144_BYTESPERSECTOR 512
+
+VOID NTAPI WaitForControllerInterrupt(PCONTROLLER_INFO ControllerInfo);
+
+NTSTATUS NTAPI ResetChangeFlag(PDRIVE_INFO DriveInfo);
+
+VOID NTAPI StartMotor(PDRIVE_INFO DriveInfo);
+VOID NTAPI StopMotor(PCONTROLLER_INFO ControllerInfo);
+

reactos/drivers/storage/floppy
floppy.rc added at 1.1
diff -N floppy.rc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ floppy.rc	1 Mar 2004 06:37:26 -0000	1.1
@@ -0,0 +1,62 @@
+/*
+ *  ReactOS Floppy Driver
+ *  Copyright (C) 2004, Vizzini (vizzini@plasmic.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * PROJECT:         ReactOS Floppy Driver
+ * FILE:            floppy.rc
+ * PURPOSE:         Resrouce definition file
+ * PROGRAMMER:      Vizzini (vizzini@plasmic.com)
+ * REVISIONS:
+ *                  15-Feb-2004 vizzini - Created
+ */
+#include <defines.h>
+#include <reactos/resource.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+	FILEVERSION	RES_UINT_FV_MAJOR,RES_UINT_FV_MINOR,RES_UINT_FV_REVISION,RES_UINT_FV_BUILD
+	PRODUCTVERSION	RES_UINT_PV_MAJOR,RES_UINT_PV_MINOR,RES_UINT_PV_REVISION,RES_UINT_PV_BUILD
+	FILEFLAGSMASK	0x3fL
+#ifdef _DEBUG
+	FILEFLAGS	0x1L
+#else
+	FILEFLAGS	0x0L
+#endif
+	FILEOS		0x40004L
+	FILETYPE	0x2L
+	FILESUBTYPE	0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "CompanyName",	RES_STR_COMPANY_NAME
+            VALUE "FileDescription",	"Floppy Disk Controller Driver\0"
+            VALUE "FileVersion",	"0.0.0\0"
+            VALUE "InternalName",	"floppy\0"
+            VALUE "LegalCopyright",	RES_STR_LEGAL_COPYRIGHT
+            VALUE "OriginalFilename",	"floppy.sys\0"
+            VALUE "ProductName",	RES_STR_PRODUCT_NAME
+            VALUE "ProductVersion",	RES_STR_PRODUCT_VERSION
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END

reactos/drivers/storage/floppy
hardware.c added at 1.1
diff -N hardware.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ hardware.c	1 Mar 2004 06:37:26 -0000	1.1
@@ -0,0 +1,1117 @@
+/*
+ *  ReactOS Floppy Driver
+ *  Copyright (C) 2004, Vizzini (vizzini@plasmic.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * PROJECT:         ReactOS Floppy Driver
+ * FILE:            hardware.c
+ * PURPOSE:         FDC Hardware control routines
+ * PROGRAMMER:      Vizzini (vizzini@plasmic.com)
+ * REVISIONS:
+ *                  15-Feb-2004 vizzini - Created
+ * NOTES:
+ *     - Many of these functions are based directly on information from the 
+ *       Intel datasheet for their enhanced floppy controller.  Send_Byte and
+ *       Get_Byte are direct C implementations of their flowcharts, and the 
+ *       read/write routine and others are loose adaptations of their charts.
+ *     - These routines are generally designed to be small, atomic operations.  They
+ *       do not wait for interrupts, deal with DMA, or do any other Windows-
+ *       specific things, unless they have to.
+ *     - If you compare this to Microsoft samples or to the old ReactOS driver,
+ *       or even to the linux driver, you will notice a big difference:  we use
+ *       a system thread to drain the queue.  This is because it's illegal to block
+ *       in a dispatch routine, unless you're a top-level driver (which we absolutely
+ *       are not).  One big reason is that we may be called at raised IRQL, at which
+ *       it's illegal to block.  The floppy controller is a *dumb* piece of hardware,
+ *       too - it is slow and difficult to deal with.  The solution is to do all
+ *       of the blocking and servicing of the controller in a dedicated worker
+ *       thread.  
+ *     - Some information taken from Intel 82077AA data sheet (order #290166-007)
+ *
+ * TODO: Fix all of the stupid STATUS_UNSUCCESSFUL return codes
+ * TODO: ATM the constants defined in hardware.h *might* be shifted to line up
+ *       with the bit position in the register, or they *might not*.  This should
+ *       all be converted to standardize on absolute values or shifts.
+ *       I prefer bit fields, but they break endianness.
+ * TODO: Figure out the right delays in Send_Byte and Get_Byte
+ */
+
+#include <ntddk.h>
+
+#include "floppy.h"
+#include "hardware.h"
+
+/*
+ * Global variable that tracks the amount of time we've
+ * been waiting on the controller
+ */
+static ULONG TimeIncrement = 0;
+
+
+/*
+ * Hardware Support Routines
+ */
+
+
+static BOOLEAN NTAPI ReadyForWrite(PCONTROLLER_INFO ControllerInfo)
+/*
+ * FUNCTION: Determine of the controller is ready to accept a byte on the FIFO
+ * ARGUMENTS:
+ *     ControllerInfo: Info structure for the FDC we're testing
+ * RETURNS:
+ *     TRUE if the controller can accept a byte right now
+ *     FALSE otherwise
+ * NOTES:
+ *     - it is necessary to check both that the FIFO is set to "outbound" 
+ *       and that the "ready for i/o" bit is set.
+ */
+{
+  UCHAR Status = READ_PORT_UCHAR(ControllerInfo->BaseAddress + MAIN_STATUS_REGISTER);
+	
+  PAGED_CODE();
+
+  if((Status & MSR_IO_DIRECTION)) /* 0 for out */
+    {
+      //KdPrint(("floppy: ReadyForWrite returning false due to incorrect FIFO direction; Status = 0x%x\n", Status));
+      //HwDumpRegisters(ControllerInfo);
+      return FALSE;
+    }
+
+  if(!(Status & MSR_DATA_REG_READY_FOR_IO))
+    {
+      //KdPrint(("floppy: ReadyForWrite returning false due to MSR Not ready for I/O; Status = 0x%x\n", Status));
+      //HwDumpRegisters(ControllerInfo);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+
+static BOOLEAN NTAPI ReadyForRead(PCONTROLLER_INFO ControllerInfo)
+/*
+ * FUNCTION: Determine of the controller is ready to read a byte on the FIFO
+ * ARGUMENTS:
+ *     ControllerInfo: Info structure for the FDC we're testing
+ * RETURNS:
+ *     TRUE if the controller can read a byte right now
+ *     FALSE otherwise
+ * NOTES:
+ *     - it is necessary to check both that the FIFO is set to "inbound" 
+ *       and that the "ready for i/o" bit is set.
+ */
+{
+  UCHAR Status = READ_PORT_UCHAR(ControllerInfo->BaseAddress + MAIN_STATUS_REGISTER);
+	
+  PAGED_CODE();
+
+  if(!(Status & MSR_IO_DIRECTION)) /* Read = 1 */
+    {
+//      KdPrint(("floppy: ReadyForRead returning FALSE due to incorrect FIFO direction; Status 0x%x\n", Status));
+//      HwDumpRegisters(ControllerInfo);
+      return FALSE;
+    }
+
+  if(!(Status & MSR_DATA_REG_READY_FOR_IO))
+    {
+//      KdPrint(("floppy: ReadyForRead returning FALSE due to MSR Not ready for I/O\n; Status 0x%x", Status));
+//      HwDumpRegisters(ControllerInfo);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+
+static NTSTATUS NTAPI Send_Byte(PCONTROLLER_INFO ControllerInfo, 
+                                UCHAR Byte)
+/*
+ * FUNCTION: Send a byte from the host to the controller's FIFO
+ * ARGUMENTS:
+ *     ControllerInfo: Info structure for the controller we're writing to
+ *     Offset: Offset over the controller's base address that we're writing to 
+ *     Byte: Byte to write to the bus
+ * RETURNS:
+ *     STATUS_SUCCESS if the byte was written successfully
+ *     STATUS_UNSUCCESSFUL if not
+ * NOTES:
+ *     - Function designed after flowchart in intel datasheet
+ *     - 250us max delay.  Note that this is exactly 5 times longer
+ *       than Microsoft recommends stalling the processor
+ *     - Remember that we can be interrupted here, so this might
+ *       take much more wall clock time than 250us
+ *     - PAGED_CODE, because we spin for more than the Microsoft-recommended
+ *       maximum.
+ *     - This function is necessary because sometimes the FIFO reacts slowly
+ *       and isn't yet ready to read or write the next byte
+ */
+{
+  LARGE_INTEGER StartingTickCount;
+  LARGE_INTEGER CurrentTickCount;
+  PUCHAR Address;
+
+  //KdPrint(("floppy: Send_Byte called to write 0x%x at offset 0x%x\n", Byte, Offset));
+
+  PAGED_CODE();
+
+  Address = ControllerInfo->BaseAddress + FIFO;
+
+  if(!TimeIncrement)
+    TimeIncrement = KeQueryTimeIncrement();
+
+  StartingTickCount.QuadPart = 0;
+
+  for(;;)
+    {
+      if(!ReadyForWrite(ControllerInfo))
+	{
+	  ULONG64 ElapsedTicks;
+	  ULONG64 TimeUnits;
+
+	  /* If this is the first time through... */
+	  if(!StartingTickCount.QuadPart)
+	    {
+              KeQueryTickCount(&StartingTickCount);
+	      continue;
+	    }
+
+	  /* Otherwise, only do this for 250 us == 2500 100ns units */
+	  KeQueryTickCount(&CurrentTickCount);
+	  ElapsedTicks = CurrentTickCount.QuadPart - StartingTickCount.QuadPart;
+	  TimeUnits = ElapsedTicks * TimeIncrement;
+
+	  //KdPrint(("floppy: Send_Byte: failed to write; elapsed time %d\n", TimeUnits));
+
+	  if(TimeUnits > 25000000)
+	    break;
+
+          continue;
+	}
+			
+      WRITE_PORT_UCHAR(Address, Byte);
+      return STATUS_SUCCESS;
+    }
+
+  KdPrint(("floppy: Send_Byte: timed out trying to write\n"));
+  HwDumpRegisters(ControllerInfo);
+  return STATUS_UNSUCCESSFUL;
+}
+
+
+static NTSTATUS NTAPI Get_Byte(PCONTROLLER_INFO ControllerInfo, 
+                               PUCHAR Byte)
+/*
+ * FUNCTION: Read a byte from the controller to the host
+ * ARGUMENTS:
+ *     ControllerInfo: Info structure for the controller we're reading from
+ *     Offset: Offset over the controller's base address  that we're reading from
+ *     Byte: Byte to read from the bus
+ * RETURNS:
+ *     STATUS_SUCCESS if the byte was read successfully
+ *     STATUS_UNSUCCESSFUL if not
+ * NOTES:
+ *     - Function designed after flowchart in intel datasheet
+ *     - 250us max delay.  Note that this is exactly 5 times longer
+ *       than Microsoft recommends stalling the processor
+ *     - Remember that we can be interrupted here, so this might
+ *       take much more wall clock time than 250us
+ *     - PAGED_CODE because we spin for longer than Microsoft recommends
+ */
+{
+  LARGE_INTEGER StartingTickCount;
+  LARGE_INTEGER CurrentTickCount;
+  PUCHAR Address;
+
+  PAGED_CODE();
+
+  Address = ControllerInfo->BaseAddress + FIFO;
+
+  if(!TimeIncrement)
+    TimeIncrement = KeQueryTimeIncrement();
+
+  StartingTickCount.QuadPart = 0;
+
+  for(;;)
+    {
+      if(!ReadyForRead(ControllerInfo))
+	{
+	  ULONG64 ElapsedTicks;
+	  ULONG64 TimeUnits;
+
+	  /* if this is the first time through, start the timer */
+          if(!StartingTickCount.QuadPart)
+	    {
+              KeQueryTickCount(&StartingTickCount);
+	      continue;
+	    }
+
+	  /* Otherwise, only do this for 250 us == 2500 100ns units */
+	  KeQueryTickCount(&CurrentTickCount);
+	  ElapsedTicks = CurrentTickCount.QuadPart - StartingTickCount.QuadPart;
+	  TimeUnits = ElapsedTicks * TimeIncrement;
+
+	  //KdPrint(("floppy: Get_Byte: failed to read; elapsed time %d\n", TimeUnits));
+
+	  if(TimeUnits > 25000000)
+	    break;
+
+          continue;
+	}
+			
+      *Byte = READ_PORT_UCHAR(Address);
+
+      //KdPrint(("floppy: Get_Byte read 0x%x from offset 0x%x\n", *Byte, Offset));
+
+      return STATUS_SUCCESS;
+    }
+
+  KdPrint(("floppy: Get_Byte: timed out trying to read\n"));
+  HwDumpRegisters(ControllerInfo);
+  return STATUS_UNSUCCESSFUL;
+}
+
+
+NTSTATUS NTAPI HwSetDataRate(PCONTROLLER_INFO ControllerInfo, 
+                             UCHAR DataRate)
+/*
+ * FUNCTION: Set the data rte on a controller
+ * ARGUMENTS:
+ *     ControllerInfo: Controller whose rate is being set
+ *     DataRate: Data rate code to set the controller to
+ * RETURNS:
+ *     STATUS_SUCCESS
+ */
+{
+  KdPrint(("floppy: HwSetDataRate called; writing rate code 0x%x to offset 0x%x\n", DataRate, DATA_RATE_SELECT_REGISTER));
+
+  WRITE_PORT_UCHAR(ControllerInfo->BaseAddress + DATA_RATE_SELECT_REGISTER, DataRate);
+
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS NTAPI HwTurnOffMotor(PCONTROLLER_INFO ControllerInfo)
+/*
+ * FUNCTION: Turn off all motors
+ * ARGUMENTS:
+ *     DriveInfo: drive to turn off
+ * RETURNS:
+ *     STATUS_SUCCESS if the motor is successfully turned off
+ * NOTES:
+ *     - Don't call this routine directly unless you've thought about it
+ *       and read the source to StartMotor() and StopMotor().
+ *     - Called at DISPATCH_LEVEL
+ */
+{
+  KdPrint(("floppy: HwTurnOffMotor: writing byte 0x%x to offset 0x%x\n", DOR_FDC_ENABLE|DOR_DMA_IO_INTERFACE_ENABLE));
+
+  WRITE_PORT_UCHAR(ControllerInfo->BaseAddress + DIGITAL_OUTPUT_REGISTER, DOR_FDC_ENABLE|DOR_DMA_IO_INTERFACE_ENABLE);
+
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS NTAPI HwTurnOnMotor(PDRIVE_INFO DriveInfo) 
+/*
+ * FUNCTION: Turn on the motor on the selected drive
+ * ARGUMENTS:
+ *     DriveInfo: drive to turn on
+ * RETURNS:
+ *     STATUS_SUCCESS if the motor is successfully turned on
+ *     STATUS_UNSUCCESSFUL otherwise
+ * NOTES:
+ *     - Doesn't interrupt
+ */
+{
+  PCONTROLLER_INFO ControllerInfo = DriveInfo->ControllerInfo;
+  UCHAR Unit = DriveInfo->UnitNumber;
+  UCHAR Buffer;
+
+  //KdPrint(("floppy: HwTurnOnMotor called; setting unit 0x%x to data rate 0x%x\n", Unit, DataRate));
+
+  PAGED_CODE();
+
+  /* turn on motor */
+  Buffer = Unit;
+
+  Buffer |= DOR_FDC_ENABLE;
+  Buffer |= DOR_DMA_IO_INTERFACE_ENABLE;
+	
+  if(Unit == 0)
+    Buffer |= DOR_FLOPPY_MOTOR_ON_A;
+  else if (Unit == 1)
+    Buffer |= DOR_FLOPPY_MOTOR_ON_B;
+  else if (Unit == 2)
+    Buffer |= DOR_FLOPPY_MOTOR_ON_C;
+  else if (Unit == 3)
+    Buffer |= DOR_FLOPPY_MOTOR_ON_D;
+
+  KdPrint(("floppy: HwTurnOnMotor: writing byte 0x%x to offset 0x%x\n", Buffer, DIGITAL_OUTPUT_REGISTER));
+  WRITE_PORT_UCHAR(ControllerInfo->BaseAddress + DIGITAL_OUTPUT_REGISTER, Buffer);
+
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS NTAPI HwSenseDriveStatus(PDRIVE_INFO DriveInfo)
+/*
+ * FUNCTION: Start a sense status command
+ * ARGUMENTS:
+ *     DriveInfo: Drive to inquire about
+ * RETURNS:
+ *     STATUS_SUCCESS if the command is successfully queued to the controller
+ *     STATUS_UNSUCCESSFUL if not
+ * NOTES:
+ *     - Generates an interrupt
+ *     - hard-wired to head 0
+ */
+{
+  UCHAR Buffer[2];
+  int i;
+
+  PAGED_CODE();
+
+  KdPrint(("floppy: HwSenseDriveStatus called\n"));
+
+  Buffer[0] = COMMAND_SENSE_DRIVE_STATUS;
+  Buffer[1] = DriveInfo->UnitNumber; /* hard-wired to head 0 for now */
+
+  for(i = 0; i < 2; i++)
+    if(Send_Byte(DriveInfo->ControllerInfo, Buffer[i]) != STATUS_SUCCESS)
+      {
+	KdPrint(("floppy: HwSenseDriveStatus: failed to write FIFO\n"));
+        return STATUS_UNSUCCESSFUL;
+      }
+
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS NTAPI HwReadWriteData(PCONTROLLER_INFO ControllerInfo, 
+                               BOOLEAN Read,
+                               UCHAR Unit, 
+                               UCHAR Cylinder, 
+                               UCHAR Head, 
+                               UCHAR Sector,
+                               UCHAR BytesPerSector,
+                               UCHAR EndOfTrack,
+                               UCHAR Gap3Length,
+                               UCHAR DataLength)
+/*
+ * FUNCTION: Read or write data to the drive
+ * ARGUMENTS:
+ *     ControllerInfo: controller to target the read/write request to
+ *     Read: TRUE if the device should be read; FALSE if written
+ *     Unit: Drive number to target
+ *     Cylinder: cylinder to start the read on
+ *     Head: head to start the read on
+ *     Sector: sector to start the read on (1-based!)
+ *     BytesPerSector: sector size constant (hardware.h)
+ *     EndOfTrack: Marks the last sector number to read/write on the track
+ *     Gap3Length: Gap length for the operation
+ *     DataLength: Bytes to read, *unless* BytesPerSector is specified
+ * RETURNS:
+ *     STATUS_SUCCESS if the operation was successfully queued to the controller
+ *     STATUS_UNSUCCESSFUL otherwise
+ * NOTES:
+ *     - Generates an interrupt
+ */
+{
+  UCHAR Buffer[9];
+  int i;
+
+  PAGED_CODE();
+
+  //KdPrint(("floppy: HwReadWriteData called\n"));
+
+  /* Shouldn't be using DataLength in this driver */
+  ASSERT(DataLength == 0xff);
+
+  /* Build the command to send */
+  if(Read)
+    Buffer[0] = COMMAND_READ_DATA; 
+  else
+    Buffer[0] = COMMAND_WRITE_DATA; 
+
+  Buffer[0] |= READ_DATA_MFM | READ_DATA_MT | READ_DATA_SK;
+
+  Buffer[1] = (Head << COMMAND_HEAD_NUMBER_SHIFT) | Unit;
+  Buffer[2] = Cylinder;
+  Buffer[3] = Head;
+  Buffer[4] = Sector;
+  Buffer[5] = BytesPerSector;
+  Buffer[6] = EndOfTrack;
+  Buffer[7] = Gap3Length;
+  Buffer[8] = DataLength;
+
+  /* Send the command */
+  for(i = 0; i < 9; i++)
+    {
+        KdPrint(("floppy: HwReadWriteData: Sending a command byte to the FIFO: 0x%x\n", Buffer[i]));
+
+	if(Send_Byte(ControllerInfo, Buffer[i]) != STATUS_SUCCESS)
+	  {
+	    KdPrint(("HwReadWriteData: Unable to write to the FIFO\n"));
+	    return STATUS_UNSUCCESSFUL;
+	  }
+    }
+
+  //KdPrint(("floppy: HwReadWriteData: returning STATUS_SUCCESS\n"));
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS NTAPI HwRecalibrateResult(PCONTROLLER_INFO ControllerInfo)
+/*
+ * FUNCTION: Get the result of a recalibrate command
+ * ARGUMENTS:
+ *     ControllerInfo: controller to query
+ * RETURNS:
+ *     STATUS_SUCCESS if the recalibratewas a success
+ *     STATUS_UNSUCCESSFUL otherwise
+ * NOTES:
+ *     - This function tests the error conditions itself, and boils the
+ *       whole thing down to a single SUCCESS or FAILURE result
+ *     - Called post-interrupt; does not interrupt
+ * TODO 
+ *     - handle much more status
+ */
+{
+  UCHAR Buffer[2];
+  int i;
+
+  PAGED_CODE();
+
+  //KdPrint(("floppy: HwRecalibrateResult called\n"));
+
+  if(Send_Byte(ControllerInfo, COMMAND_SENSE_INTERRUPT_STATUS) != STATUS_SUCCESS)
+    {
+      KdPrint(("floppy: HwRecalibrateResult: Unable to write the controller\n"));
+      return STATUS_UNSUCCESSFUL;
+    }
+
+  for(i = 0; i < 2; i++)
+    if(Get_Byte(ControllerInfo, &Buffer[i]) != STATUS_SUCCESS)
+      {
+	KdPrint(("floppy: HwRecalibrateResult: unable to read FIFO\n"));
+        return STATUS_UNSUCCESSFUL;
+      }
+
+  /* Validate  that it did what we told it to */ 
+  KdPrint(("floppy: HwRecalibrateResult results: ST0: 0x%x PCN: 0x%x\n", Buffer[0], Buffer[1]));
+
+  /*
+   * Buffer[0] = ST0
+   * Buffer[1] = PCN
+   */
+
+  /* Is the PCN 0? */
+  if(Buffer[1] != 0)
+    {
+      KdPrint(("floppy: HwRecalibrateResult: PCN not 0\n"));
+      return STATUS_UNSUCCESSFUL;
+    }
+
+  /* test seek complete */
+  if((Buffer[0] & SR0_SEEK_COMPLETE) != SR0_SEEK_COMPLETE)
+    {
+      KdPrint(("floppy: HwRecalibrateResult: Failed to complete the seek\n"));
+      return STATUS_UNSUCCESSFUL;
+    }
+
+  /* Is the equipment check flag set?  Could be no disk in drive... */
+  if((Buffer[0] & SR0_EQUIPMENT_CHECK) == SR0_EQUIPMENT_CHECK)
+      KdPrint(("floppy: HwRecalibrateResult: Seeked to track 0 successfully, but EC is set; returning STATUS_SUCCESS anyway\n"));
+
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS NTAPI HwReadWriteResult(PCONTROLLER_INFO ControllerInfo)
+/*
+ * FUNCTION: Get the result of a read or write from the controller
+ * ARGUMENTS:
+ *     ControllerInfo: controller to query
+ * RETURNS:
+ *     STATUS_SUCCESS if the read/write was a success
+ *     STATUS_UNSUCCESSFUL otherwise
+ * NOTES:
+ *     - This function tests the error conditions itself, and boils the
+ *       whole thing down to a single SUCCESS or FAILURE result
+ *     - Called post-interrupt; does not interrupt
+ * TODO 
+ *     - handle much more status
+ */
+{
+  UCHAR Buffer[7];
+  int i;
+
+  //KdPrint(("floppy: HwReadWriteResult called\n"));
+
+  PAGED_CODE();
+
+  for(i = 0; i < 7; i++)
+    if(Get_Byte(ControllerInfo, &Buffer[i]) != STATUS_SUCCESS)
+      {
+	KdPrint(("floppy: HwReadWriteResult: unable to read fifo\n"));
+        return STATUS_UNSUCCESSFUL;
+      }
+
+  /* Validate  that it did what we told it to */ 
+  KdPrint(("floppy: HwReadWriteResult results: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", Buffer[0], Buffer[1], Buffer[2], Buffer[3],
+	   Buffer[4], Buffer[5], Buffer[6]));
+
+  /* Last command successful? */
+  if((Buffer[0] & SR0_LAST_COMMAND_STATUS) != SR0_LCS_SUCCESS)
+    return STATUS_UNSUCCESSFUL;
+
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS NTAPI HwRecalibrate(PDRIVE_INFO DriveInfo)
+/*
+ * FUNCTION: Start a recalibration of a drive
+ * ARGUMENTS:
+ *     DriveInfo: Drive to recalibrate
+ * RETURNS:
+ *     STATUS_SUCCESS if the command was successfully queued to the controller
+ *     STATUS_UNSUCCESSFUL otherwise
+ * NOTES:
+ *     - Generates an interrupt
+ */
+{
+  PCONTROLLER_INFO ControllerInfo = DriveInfo->ControllerInfo;
+  UCHAR Unit = DriveInfo->UnitNumber;
+  UCHAR Buffer[2];
+  int i;
+
+  KdPrint(("floppy: HwRecalibrate called\n"));
+
+  PAGED_CODE();
+
+  Buffer[0] = COMMAND_RECALIBRATE;
+  Buffer[1] = Unit;
+
+  for(i = 0; i < 2; i++)
+    if(Send_Byte(ControllerInfo, Buffer[i]) != STATUS_SUCCESS)
+      {
+	KdPrint(("floppy: HwRecalibrate: unable to write FIFO\n"));
+        return STATUS_UNSUCCESSFUL;
+      }
+
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS NTAPI HwSenseInterruptStatus(PCONTROLLER_INFO ControllerInfo)
+/*
+ * FUNCTION: Send a sense interrupt status command to a controller
+ * ARGUMENTS:
+ *     ControllerInfo: controller to queue the command to
+ * RETURNS:
+ *     STATUS_SUCCESS if the command is queued successfully
+ *     STATUS_UNSUCCESSFUL if not
+ */
+{
+  UCHAR Buffer[2];
+  int i;
+
+  PAGED_CODE();
+
+  //KdPrint(("floppy: HwSenseInterruptStatus called\n"));
+
+  if(Send_Byte(ControllerInfo, COMMAND_SENSE_INTERRUPT_STATUS) != STATUS_SUCCESS)
+    {
+      KdPrint(("floppy: HwSenseInterruptStatus: failed to write controller\n"));
+      return STATUS_UNSUCCESSFUL;
+    }
+
+  for(i = 0; i  < 2; i++)
+    {
+      if(Get_Byte(ControllerInfo, &Buffer[i]) != STATUS_SUCCESS)
+	{
+	  KdPrint(("floppy: HwSenseInterruptStatus: failed to read controller\n"));
+	  return STATUS_UNSUCCESSFUL;
+	}
+    }
+
+  KdPrint(("floppy: HwSenseInterruptStatus returned 0x%x 0x%x\n", Buffer[0], Buffer[1]));
+
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS NTAPI HwReadId(PDRIVE_INFO DriveInfo, UCHAR Head)
+/*
+ * FUNCTION: Issue a read id command to the drive
+ * ARGUMENTS:
+ *     DriveInfo: Drive to read id from
+ *     Head: Head to read the ID from
+ * RETURNS:
+ *     STATUS_SUCCESS if the command is queued
+ *     STATUS_UNSUCCESSFUL otherwise
+ * NOTES:
+ *     - Generates an interrupt
+ */
+{
+  UCHAR Buffer[2];
+  int i;
+
+  KdPrint(("floppy: HwReadId called\n"));
+
+  PAGED_CODE();
+
+  Buffer[0] = COMMAND_READ_ID | READ_ID_MFM;
+  Buffer[1] = (Head << COMMAND_HEAD_NUMBER_SHIFT) | DriveInfo->UnitNumber;
+
+  for(i = 0; i < 2; i++)
+    if(Send_Byte(DriveInfo->ControllerInfo, Buffer[i]) != STATUS_SUCCESS)
+      {
+	KdPrint(("floppy: HwReadId: unable to send bytes to fifo\n"));
+        return STATUS_UNSUCCESSFUL;
+      }
+
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS NTAPI HwFormatTrack(PCONTROLLER_INFO ControllerInfo, 
+                             UCHAR Unit,
+                             UCHAR Head, 
+                             UCHAR BytesPerSector,
+                             UCHAR SectorsPerTrack,
+                             UCHAR Gap3Length,
+                             UCHAR FillerPattern)
+/*
+ * FUNCTION: Format a track
+ * ARGUMENTS:
+ *     ControllerInfo: controller to target with the request
+ *     Unit: drive to format on
+ *     Head: head to format on
+ *     BytesPerSector: constant from hardware.h to select density
+ *     SectorsPerTrack: sectors per track
+ *     Gap3Length: gap length to use during format
+ *     FillerPattern: pattern to write into the data portion of sectors
+ * RETURNS:
+ *     STATUS_SUCCESS if the command is successfully queued
+ *     STATUS_UNSUCCESSFUL otherwise
+ */
+{
+  UCHAR Buffer[6];
+  int i;
+
+  KdPrint(("floppy: HwFormatTrack called\n"));
+
+  PAGED_CODE();
+
+  Buffer[0] = COMMAND_FORMAT_TRACK;
+  Buffer[1] = (Head << COMMAND_HEAD_NUMBER_SHIFT) | Unit;
+  Buffer[2] = BytesPerSector;
+  Buffer[3] = SectorsPerTrack;
+  Buffer[4] = Gap3Length;
+  Buffer[5] = FillerPattern;
+
+  for(i = 0; i < 6; i++)
+    if(Send_Byte(ControllerInfo, Buffer[i]) != STATUS_SUCCESS)
+      {
+	KdPrint(("floppy: HwFormatTrack: unable to send bytes to floppy\n"));
+        return STATUS_UNSUCCESSFUL;
+      }
+
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS NTAPI HwSeek(PDRIVE_INFO DriveInfo,
+                      UCHAR Cylinder) 
+/*
+ * FUNCTION: Seek the heads to a particular cylinder
+ * ARGUMENTS:
+ *     DriveInfo: Drive to seek
+ *     Cylinder: cylinder to move to
+ * RETURNS:
+ *     STATUS_SUCCESS if the command is successfully sent
+ *     STATUS_UNSUCCESSFUL otherwise
+ * NOTES:
+ *     - Generates an interrupt
+ */
+{
+  LARGE_INTEGER Delay;
+  UCHAR Buffer[3];
+  int i;
+
+  KdPrint(("floppy: HwSeek called for cyl 0x%x\n", Cylinder));
+
+  PAGED_CODE();
+
+  Buffer[0] = COMMAND_SEEK;
+  Buffer[1] = DriveInfo->UnitNumber;
+  Buffer[2] = Cylinder;
+
+  for(i = 0; i < 3; i++)
+    if(Send_Byte(DriveInfo->ControllerInfo, Buffer[i]) != STATUS_SUCCESS)
+      {
+	KdPrint(("floppy: HwSeek: failed to write fifo\n"));
+        return STATUS_UNSUCCESSFUL;
+      }
+
+  /* Wait for the head to settle */
+  Delay.QuadPart = 10 * 1000;
+  Delay.QuadPart *= -1;
+  Delay.QuadPart *= DriveInfo->FloppyDeviceData.HeadSettleTime;
+
+  KeDelayExecutionThread(KernelMode, FALSE, &Delay);
+
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS NTAPI HwConfigure(PCONTROLLER_INFO ControllerInfo, 
+                           BOOLEAN EIS,
+			   BOOLEAN EFIFO,
+			   BOOLEAN POLL,
+			   UCHAR FIFOTHR,
+			   UCHAR PRETRK) 
+/*
+ * FUNCTION: Sends configuration to the drive
+ * ARGUMENTS:
+ *     ControllerInfo: controller to target with the request
+ *     EIS: Enable implied seek
+ *     EFIFO: Enable advanced fifo
+ *     POLL: Enable polling
+ *     FIFOTHR: fifo threshold
+ *     PRETRK: precomp (see intel datasheet)
+ * RETURNS:
+ *     STATUS_SUCCESS if the command is successfully sent
+ *     STATUS_UNSUCCESSFUL otherwise
+ * NOTES:
+ *     - No interrupt
+ */
+{
+  UCHAR Buffer[4];
+  int i;
+
+  KdPrint(("floppy: HwConfigure called\n"));
+
+  PAGED_CODE();
+
+  Buffer[0] = COMMAND_CONFIGURE;
+  Buffer[1] = 0;
+  Buffer[2] = (EIS * CONFIGURE_EIS) + (EFIFO * CONFIGURE_EFIFO) + (POLL * CONFIGURE_POLL) + (FIFOTHR);
+  Buffer[3] = PRETRK;
+
+  for(i = 0; i < 4; i++)
+    if(Send_Byte(ControllerInfo, Buffer[i]) != STATUS_SUCCESS)
+      {
+	KdPrint(("floppy: HwConfigure: failed to write the fifo\n"));
+        return STATUS_UNSUCCESSFUL;
+      }
+
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS NTAPI HwGetVersion(PCONTROLLER_INFO ControllerInfo)
+/*
+ * FUNCTION: Gets the version of the controller
+ * ARGUMENTS:
+ *     ControllerInfo: controller to target with the request
+ *     ConfigValue: Configuration value to send to the drive (see header)
+ * RETURNS:
+ *     Version number returned by the command, or
+ *     0 on failure
+ * NOTE:
+ *     - This command doesn't interrupt, so we go right to reading after
+ *       we issue the command
+ */
+{
+  UCHAR Buffer;
+
+  PAGED_CODE();
+
+  if(Send_Byte(ControllerInfo, COMMAND_VERSION) != STATUS_SUCCESS)
+    {
+      KdPrint(("floppy: HwGetVersion: unable to write fifo\n"));
+      return STATUS_UNSUCCESSFUL;;
+    }
+
+  if(Get_Byte(ControllerInfo, &Buffer) != STATUS_SUCCESS)
+    {
+      KdPrint(("floppy: HwGetVersion: unable to write fifo\n"));
+      return STATUS_UNSUCCESSFUL;
+    }
+
+  KdPrint(("floppy: HwGetVersion returning version 0x%x\n", Buffer));
+
+  return Buffer;
+}
+
+NTSTATUS NTAPI HwDiskChanged(PDRIVE_INFO DriveInfo, 
+                             PBOOLEAN DiskChanged)
+/*
+ * FUNCTION: Detect whether the hardware has sensed a disk change
+ * ARGUMENTS:
+ *     DriveInfo: pointer to the drive that we are to check
+ *     DiskChanged: boolean that is set with whether or not the controller thinks there has been a disk change
+ * RETURNS:
+ *     STATUS_SUCCESS if the drive is successfully queried
+ * NOTES:
+ *     - Does not interrupt.
+ *     - Guessing a bit at the Model30 stuff
+ */
+{
+  UCHAR Buffer;
+  PCONTROLLER_INFO ControllerInfo = (PCONTROLLER_INFO) DriveInfo->ControllerInfo;
+
+  Buffer = READ_PORT_UCHAR(ControllerInfo->BaseAddress + DIGITAL_INPUT_REGISTER);
+
+  KdPrint(("floppy: HwDiskChanged: read 0x%x from DIR\n", Buffer));
+
+  if(ControllerInfo->Model30)
+    {
+      if(!(Buffer & DIR_DISKETTE_CHANGE))
+	{
+	  KdPrint(("floppy: HdDiskChanged - Model30 - returning TRUE\n"));
+	  *DiskChanged = TRUE;
+	}
+      else
+	{
+	  KdPrint(("floppy: HdDiskChanged - Model30 - returning FALSE\n"));
+	  *DiskChanged = FALSE;
+	}
+    }
+  else
+    {
+      if(Buffer & DIR_DISKETTE_CHANGE)
+	{
+	  KdPrint(("floppy: HdDiskChanged - PS2 - returning TRUE\n"));
+	  *DiskChanged = TRUE;
+	}
+      else
+	{
+	  KdPrint(("floppy: HdDiskChanged - PS2 - returning FALSE\n"));
+	  *DiskChanged = FALSE;
+	}
+    }
+
+  return STATUS_SUCCESS;
+}
+
+NTSTATUS NTAPI HwSenseDriveStatusResult(PCONTROLLER_INFO ControllerInfo, 
+                                        PUCHAR Status)
+/*
+ * FUNCTION: Get the result of a sense drive status command
+ * ARGUMENTS:
+ *     ControllerInfo: controller to query
+ *     Status: Status from the drive sense command
+ * RETURNS:
+ *     STATUS_SUCCESS if we can successfully read the status
+ *     STATUS_UNSUCCESSFUL otherwise
+ * NOTES:
+ *     - Called post-interrupt; does not interrupt
+ */
+{
+  PAGED_CODE();
+
+  if(Get_Byte(ControllerInfo, Status) != STATUS_SUCCESS)
+    {
+      KdPrint(("floppy: HwSenseDriveStatus: unable to read fifo\n"));
+      return STATUS_UNSUCCESSFUL;
+    }
+
+  KdPrint(("floppy: HwSenseDriveStatusResult: ST3: 0x%x\n", *Status));
+
+  return STATUS_SUCCESS;
+}
+
+
+NTSTATUS NTAPI HwReadIdResult(PCONTROLLER_INFO ControllerInfo,
+                              PUCHAR CurCylinder,
+                              PUCHAR CurHead)
+/*
+ * FUNCTION: Get the result of a read id command
+ * ARGUMENTS:
+ *     ControllerInfo: controller to query
+ *     CurCylinder: Returns the cylinder that we're at
+ *     CurHead: Returns the head that we're at
+ * RETURNS:
+ *     STATUS_SUCCESS if the read id was a success
+ *     STATUS_UNSUCCESSFUL otherwise
+ * NOTES:
+ *     - This function tests the error conditions itself, and boils the
+ *       whole thing down to a single SUCCESS or FAILURE result
+ *     - Called post-interrupt; does not interrupt
+ * TODO 
+ *     - handle much more status
+ */
+{
+  UCHAR Buffer[7] = {0,0,0,0,0,0,0};
+  int i;
+
+  PAGED_CODE();
+
+  for(i = 0; i < 7; i++)
+    if(Get_Byte(ControllerInfo, &Buffer[i]) != STATUS_SUCCESS)
+      {
+	KdPrint(("floppy: ReadIdResult(): can't read from the controller\n"));
+        return STATUS_UNSUCCESSFUL;
+      }
+
+  /* Validate  that it did what we told it to */ 
+  KdPrint(("floppy: ReadId results: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", Buffer[0], Buffer[1], Buffer[2], Buffer[3],
+	   Buffer[4], Buffer[5], Buffer[6]));
+
+  /* Last command successful? */
+  if((Buffer[0] & SR0_LAST_COMMAND_STATUS) != SR0_LCS_SUCCESS)
+    {
+      KdPrint(("floppy: ReadId didn't return last command success\n"));
+      return STATUS_UNSUCCESSFUL;
+    }
+
+  /* ID mark found? */
+  if(Buffer[1] & SR1_CANNOT_FIND_ID_ADDRESS)
+    {
+      KdPrint(("floppy: ReadId didn't find an address mark\n"));
+      return STATUS_UNSUCCESSFUL;
+    }
+
+  if(CurCylinder)
+    *CurCylinder = Buffer[3];
+
+  if(CurHead)
+    *CurHead = Buffer[4];
[truncated at 1000 lines; 121 more skipped]

reactos/drivers/storage/floppy
hardware.h added at 1.1
diff -N hardware.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ hardware.h	1 Mar 2004 06:37:26 -0000	1.1
@@ -0,0 +1,336 @@
+/*
+ *  ReactOS Floppy Driver
+ *  Copyright (C) 2004, Vizzini (vizzini@plasmic.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * PROJECT:         ReactOS Floppy Driver
+ * FILE:            hardware.h
+ * PURPOSE:         Header for FDC control routines
+ * PROGRAMMER:      Vizzini (vizzini@plasmic.com)
+ * REVISIONS:
+ *                  15-Feb-2004 vizzini - Created
+ *
+ * NOTES:
+ *     - Baesd on http://www.nondot.org/sabre/os/files/Disk/FLOPPY.TXT
+ *     - Some information taken from Intel 82077AA data sheet (order #290166-007)
+ *     - Some definitions are PS/2-Specific; others include the original NEC PD765
+ *     - Other information gathered from the comments in the NT 3.5 floppy driver
+ *
+ * TODO:
+ *     - Convert these numbers to 100% absolute values; eliminate bit positions
+ *       in favor of shifts or bitfields
+ */
+
+#define FLOPPY_DEFAULT_IRQ              0x6
+
+/* Register offsets from base address (usually 0x3f8)  */
+#define STATUS_REGISTER_A               0x0 /* Read; PS/2 Only */
+#define STATUS_REGISTER_B               0x1 /* Read; PS/2 Only */
+#define DIGITAL_OUTPUT_REGISTER         0x2 /* Read/Write */
+#define TAPE_DRIVE_REGISTER             0x3 /* Read/Write */
+#define MAIN_STATUS_REGISTER            0x4 /* Read */
+#define DATA_RATE_SELECT_REGISTER       0x4 /* Write */
+#define FIFO                            0x5 /* Read/Write */
+#define RESERVED_REGISTER               0x6 /* Reserved */
+#define DIGITAL_INPUT_REGISTER          0x7 /* Read; PS/2 Only */
+#define CONFIGURATION_CONTROL_REGISTER  0x7 /* Write; PS/2 Only */
+
+/* STATUS_REGISTER_A */
+#define DSRA_DIRECTION                  0x1
+#define DSRA_WRITE_PROTECT              0x2
+#define DSRA_INDEX                      0x4
+#define DSRA_HEAD_1_SELECT              0x8
+#define DSRA_TRACK_0                    0x10
+#define DSRA_STEP                       0x20
+#define DSRA_SECOND_DRIVE_INSTALLED     0x40
+#define DSRA_INTERRUPT_PENDING          0x80
+
+/* STATUS_REGISTER_B */
+#define DSRB_MOTOR_ENABLE_0             0x1
+#define DSRB_MOTOR_ENABLE_1             0x2
+#define DSRB_WRITE_ENABLE               0x4
+#define DSRB_READ_DATA                  0x8 
+#define DSRB_WRTITE_DATA                0x10
+#define DSRB_DRIVE_SELECT               0x20
+
+/* DIGITAL_OUTPUT_REGISTER */
+#define DOR_FLOPPY_DRIVE_SELECT         0x3 /* Covers 2 bits, defined below */
+#define DOR_FDC_ENABLE                  0x4 /* from the website */
+#define DOR_RESET                       0x4 /* from the Intel guide; 0 = resetting, 1 = enabled */
+#define DOR_DMA_IO_INTERFACE_ENABLE     0x8 /* Reserved on PS/2 */
+#define DOR_FLOPPY_MOTOR_ON_A           0x10
+#define DOR_FLOPPY_MOTOR_ON_B           0x20
+#define DOR_FLOPPY_MOTOR_ON_C           0x40 /* Reserved on PS/2 */
+#define DOR_FLOPPY_MOTOR_ON_D           0x80 /* Reserved on PS/2 */
+
+/* DOR_FLOPPY_DRIVE_SELECT */
+#define DOR_FLOPPY_DRIVE_SELECT_A       0x0
+#define DOR_FLOPPY_DRIVE_SELECT_B       0x1
+#define DOR_FLOPPY_DRIVE_SELECT_C       0x2 /* Reserved on PS/2 */
+#define DOR_FLOPPY_DRIVE_SELECT_D       0x3 /* Reserved on PS/2 */
+
+/* MAIN_STATUS_REGISTER */
+#define MSR_FLOPPY_BUSY_0               0x1
+#define MSR_FLOPPY_BUSY_1               0x2
+#define MSR_FLOPPY_BUSY_2               0x4 /* Reserved on PS/2 */
+#define MSR_FLOPPY_BUSY_3               0x8 /* Reserved on PS/2 */
+#define MSR_READ_WRITE_IN_PROGRESS      0x10
+#define MSR_NON_DMA_MODE                0x20
+#define MSR_IO_DIRECTION                0x40 /* Determines meaning of Command Status Registers */
+#define MSR_DATA_REG_READY_FOR_IO       0x80
+
+/* DATA_RATE_SELECT_REGISTER */
+#define DRSR_DSEL                       0x3 /* covers two bits as defined below */
+#define DRSR_PRECOMP                    0x1c /* covers three bits as defined below */
+#define DRSR_MBZ                        0x20
+#define DRSR_POWER_DOWN                 0x40
+#define DRSR_SW_RESET                   0x80
+
+/* DRSR_DSEL */
+#define DRSR_DSEL_500KBPS               0x0
+#define DRSR_DSEL_300KBPS               0x1
+#define DRSR_DSEL_250KBPS               0x2
+#define DRSR_DSEL_1MBPS                 0x3
+
+/* STATUS_REGISTER_0 */
+#define SR0_UNIT_SELECTED_AT_INTERRUPT  0x3 /* Covers two bits as defined below */
+#define SR0_HEAD_NUMBER_AT_INTERRUPT    0x4 /* Values defined below */
+#define SR0_NOT_READY_ON_READ_WRITE     0x8 /* Unused in PS/2 */
+#define SR0_SS_ACCESS_TO_HEAD_1         0x8 /* Unused in PS/2 */
+#define SR0_EQUIPMENT_CHECK             0x10
+#define SR0_SEEK_COMPLETE               0x20
+#define SR0_LAST_COMMAND_STATUS         0xC0 /* Covers two bits as defined below */
+
+/* SR0_UNIT_SELECTED_AT_INTERRUPT */
+#define SR0_UNIT_SELECTED_A             0x0
+#define SR0_UNIT_SELECTED_B             0x1
+#define SR0_UNIT_SELECTED_C             0x2
+#define SR0_UNIT_SELECTED_D             0x3
+#define SR0_PS2_UNIT_SELECTED_A         0x1 /* PS/2 uses only two drives: A = 01b  B = 10b */
+#define SR0_PST_UNIT_SELECTED_B         0x2
+
+/* SR0_HEAD_NUMBER_AT_INTERRUPT */
+#define SR0_HEAD_0                      0x0
+#define SR0_HEAD_1                      0x1
+
+/* SR0_LAST_COMMAND_STATUS */
+#define SR0_LCS_SUCCESS                 0x0
+#define SR0_LCS_TERMINATED_ABNORMALLY   0x40
+#define SR0_LCS_INVALID_COMMAND_ISSUED  0x80
+#define SR0_LCS_READY_SIGNAL_CHANGED    0xc0 /* Reserved on PS/2; a/k/a abnormal termination due to polling */
+
+/* STATUS_REGISTER_1 */
+#define SR1_CANNOT_FIND_ID_ADDRESS      0x1 /* Mimics SR2_WRONG_CYLINDER_DETECTED */
+#define SR1_WRITE_PROTECT_DETECTED      0x2
+#define SR1_CANNOT_FIND_SECTOR_ID       0x4
+#define SR1_OVERRUN                     0x10
+#define SR1_CRC_ERROR                   0x20
+#define SR1_END_OF_CYLINDER             0x80
+
+/* STATUS_REGISTER_2 */
+#define SR2_MISSING_ADDRESS_MARK        0x1
+#define SR2_BAD_CYLINDER                0x2
+#define SR2_SCAN_COMMAND_FAILED         0x4
+#define SR2_SCAN_COMMAND_EQUAL          0x8
+#define SR2_WRONG_CYLINDER_DETECTED     0x10 /* Mimics SR1_CANNOT_FIND_ID_ADDRESS */
+#define SR2_CRC_ERROR_IN_SECTOR_DATA    0x20
+#define SR2_SECTOR_WITH_DELETED_DATA    0x40
+
+/* STATUS_REGISTER_3 */
+#define SR3_UNIT_SELECTED               0x3 /* Covers two bits; defined below */
+#define SR3_SIDE_HEAD_SELECT_STATUS     0x4 /* Values defined below */
+#define SR3_TWO_SIDED_STATUS_SIGNAL     0x8
+#define SR3_TRACK_ZERO_STATUS_SIGNAL    0x10
+#define SR3_READY_STATUS_SIGNAL         0x20
+#define SR3_WRITE_PROTECT_STATUS_SIGNAL 0x40
+#define SR3_FAULT_STATUS_SIGNAL         0x80
+
+/* SR3_UNIT_SELECTED */
+#define SR3_UNIT_SELECTED_A             0x0
+#define SR3_UNIT_SELECTED_B             0x1
+#define SR3_UNIT_SELECTED_C             0x2
+#define SR3_UNIT_SELECTED_D             0x3
+
+/* SR3_SIDE_HEAD_SELECT_STATUS */
+#define SR3_SHSS_HEAD_0                 0x0
+#define SR3_SHSS_HEAD_1                 0x1
+
+/* DIGITAL_INPUT_REGISTER */
+#define DIR_HIGH_DENSITY_SELECT         0x1
+#define DIR_DISKETTE_CHANGE             0x80
+
+/* CONFIGURATION_CONTROL_REGISTER */
+#define CCR_DRC                         0x3 /* Covers two bits, defined below */
+#define CCR_DRC_0                       0x1
+#define CCR_DRC_1                       0x2
+
+/* CCR_DRC */
+#define CCR_DRC_500000                  0x0
+#define CCR_DRC_250000                  0x2
+
+/* Commands */
+#define COMMAND_READ_TRACK              0x2 
+#define COMMAND_SPECIFY                 0x3
+#define COMMAND_SENSE_DRIVE_STATUS      0x4
+#define COMMAND_WRITE_DATA              0x5
+#define COMMAND_READ_DATA               0x6
+#define COMMAND_RECALIBRATE             0x7
+#define COMMAND_SENSE_INTERRUPT_STATUS  0x8
+#define COMMAND_WRITE_DELETED_DATA      0x9
+#define COMMAND_READ_ID                 0xA
+#define COMMAND_READ_DELETED_DATA       0xC
+#define COMMAND_FORMAT_TRACK            0xD
+#define COMMAND_SEEK                    0xF
+#define COMMAND_VERSION                 0x10
+#define COMMAND_SCAN_EQUAL              0x11
+#define COMMAND_CONFIGURE               0x13
+#define COMMAND_SCAN_LOW_OR_EQUAL       0x19
+#define COMMAND_SCAN_HIGH_OR_EQUAL      0x1D
+
+/* COMMAND_READ_DATA constants */
+#define READ_DATA_DS0                   0x1
+#define READ_DATA_DS1                   0x2
+#define READ_DATA_HDS                   0x4
+#define READ_DATA_SK                    0x20
+#define READ_DATA_MFM                   0x40
+#define READ_DATA_MT                    0x80
+
+/* COMMAND_READ_ID constants */
+#define READ_ID_MFM                     0x40
+
+/* COMMAND_SPECIFY constants */
+#define SPECIFY_HLT_1M                  0x10  /* 16ms; based on intel data sheet */
+#define SPECIFY_HLT_500K                0x8   /* 16ms; based on intel data sheet */
+#define SPECIFY_HLT_300K                0x6   /* 16ms; based on intel data sheet */
+#define SPECIFY_HLT_250K                0x4   /* 16ms; based on intel data sheet */
+#define SPECIFY_HUT_1M                  0x0   /* Need to figure out these eight values; 0 is max */
+#define SPECIFY_HUT_500K                0x0
+#define SPECIFY_HUT_300K                0x0
+#define SPECIFY_HUT_250K                0x0
+#define SPECIFY_SRT_1M                  0x0
+#define SPECIFY_SRT_500K                0x0
+#define SPECIFY_SRT_300K                0x0
+#define SPECIFY_SRT_250K                0x0
+
+/* Command byte 1 constants */
+#define COMMAND_UNIT_SELECT             0x3 /* Covers two bits; defined below */
+#define COMMAND_UNIT_SELECT_0           0x1
+#define COMMAND_UNIT_SELECT_1           0x2
+#define COMMAND_HEAD_NUMBER             0x4
+#define COMMAND_HEAD_NUMBER_SHIFT       0x2
+
+/* COMMAND_VERSION */
+#define VERSION_ENHANCED                0x90
+
+/* COMMAND_UNIT_SELECT */
+#define CUS_UNIT_0                      0x0
+#define CUS_UNIT_1                      0x1
+
+/* COMMAND_CONFIGURE constants */
+#define CONFIGURE_FIFOTHR               0xf
+#define CONFIGURE_POLL                  0x10
+#define CONFIGURE_EFIFO                 0x20
+#define CONFIGURE_EIS                   0x40
+#define CONFIGURE_PRETRK                0xff
+
+/* Command Head Number Constants */
+#define COMMAND_HEAD_0                  0x0
+#define COMMAND_HEAD_1                  0x1
+
+/* Bytes per sector constants */
+#define HW_128_BYTES_PER_SECTOR         0x0
+#define HW_256_BYTES_PER_SECTOR         0x1
+#define HW_512_BYTES_PER_SECTOR         0x2
+#define HW_1024_BYTES_PER_SECTOR        0x3
+
+/*
+ * FUNCTIONS
+ */
+NTSTATUS NTAPI HwTurnOnMotor(PDRIVE_INFO DriveInfo);
+
+NTSTATUS NTAPI HwSenseDriveStatus(PDRIVE_INFO DriveInfo);
+
+NTSTATUS NTAPI HwReadWriteData(PCONTROLLER_INFO ControllerInfo, 
+                               BOOLEAN Read,
+                               UCHAR Unit, 
+                               UCHAR Cylinder, 
+                               UCHAR Head, 
+                               UCHAR Sector,
+                               UCHAR BytesPerSector,
+                               UCHAR EndOfTrack,
+                               UCHAR Gap3Length,
+                               UCHAR DataLength);
+
+NTSTATUS NTAPI HwRecalibrate(PDRIVE_INFO DriveInfo);
+
+NTSTATUS NTAPI HwSenseInterruptStatus(PCONTROLLER_INFO ControllerInfo);
+
+NTSTATUS NTAPI HwReadId(PDRIVE_INFO DriveInfo, 
+                        UCHAR Head);
+
+NTSTATUS NTAPI HwFormatTrack(PCONTROLLER_INFO ControllerInfo, 
+                             UCHAR Unit,
+                             UCHAR Head, 
+                             UCHAR BytesPerSector,
+                             UCHAR SectorsPerTrack,
+                             UCHAR Gap3Length,
+                             UCHAR FillerPattern);
+
+NTSTATUS NTAPI HwSeek(PDRIVE_INFO DriveInfo,
+                      UCHAR Cylinder);
+
+NTSTATUS NTAPI HwReadWriteResult(PCONTROLLER_INFO ControllerInfo);
+
+NTSTATUS NTAPI HwGetVersion(PCONTROLLER_INFO ControllerInfo);
+
+NTSTATUS NTAPI HwConfigure(PCONTROLLER_INFO ControllerInfo, 
+                           BOOLEAN EIS,
+			   BOOLEAN EFIFO,
+			   BOOLEAN POLL,
+			   UCHAR FIFOTHR,
+			   UCHAR PRETRK) ;
+
+NTSTATUS NTAPI HwRecalibrateResult(PCONTROLLER_INFO ControllerInfo);
+
+NTSTATUS NTAPI HwDiskChanged(PDRIVE_INFO DriveInfo, 
+                             PBOOLEAN DiskChanged);
+
+NTSTATUS NTAPI HwSenseDriveStatusResult(PCONTROLLER_INFO ControllerInfo, 
+                                        PUCHAR Status);
+
+NTSTATUS NTAPI HwSpecify(PCONTROLLER_INFO ControllerInfo,
+                         UCHAR HeadLoadTime,
+                         UCHAR HeadUnloadTime,
+                         UCHAR StepRateTime,
+                         BOOLEAN NonDma);
+
+NTSTATUS NTAPI HwReadIdResult(PCONTROLLER_INFO ControllerInfo,
+                              PUCHAR CurCylinder,
+                              PUCHAR CurHead);
+
+NTSTATUS NTAPI HwSetDataRate(PCONTROLLER_INFO ControllerInfo, 
+                             UCHAR DataRate);
+
+NTSTATUS NTAPI HwRecalibrateResult(PCONTROLLER_INFO ControllerInfo);
+
+NTSTATUS NTAPI HwReset(PCONTROLLER_INFO Controller);
+
+NTSTATUS NTAPI HwPowerOff(PCONTROLLER_INFO ControllerInfo);
+
+VOID NTAPI HwDumpRegisters(PCONTROLLER_INFO ControllerInfo);
+
+NTSTATUS NTAPI HwTurnOffMotor(PCONTROLLER_INFO ControllerInfo);
+

reactos/drivers/storage/floppy
ioctl.c added at 1.1
diff -N ioctl.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ioctl.c	1 Mar 2004 06:37:26 -0000	1.1
@@ -0,0 +1,250 @@
+/*
+ *  ReactOS Floppy Driver
+ *  Copyright (C) 2004, Vizzini (vizzini@plasmic.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * PROJECT:         ReactOS Floppy Driver
+ * FILE:            ioctl.c
+ * PURPOSE:         IOCTL Routines
+ * PROGRAMMER:      Vizzini (vizzini@plasmic.com)
+ * REVISIONS:
+ *                  15-Feb-2004 vizzini - Created
+ * NOTES:
+ *     - All IOCTL documentation taken from the DDK
+ *     - This driver tries to support all of the IOCTLs that the DDK floppy
+ *       sample does
+ *
+ * TODO: Add support to GET_MEDIA_TYPES for non-1.44 disks
+ * TODO: Implement format
+ */
+
+#include <ntddk.h>
+
+#include "floppy.h"
+#include "hardware.h"
+#include "csqrtns.h"
+#include "ioctl.h"
+
+
+NTSTATUS NTAPI DeviceIoctl(PDEVICE_OBJECT DeviceObject, 
+                           PIRP Irp)
+/*
+ * FUNCTION: Queue IOCTL IRPs
+ * ARGUMENTS:
+ *     DeviceObject: DeviceObject that is the target of the IRP
+ *     Irp: IRP to process
+ * RETURNS:
+ *     STATUS_SUCCESS in all cases, so far
+ * NOTES:
+ *     - We can't just service these immediately because, even though some
+ *       are able to run at DISPATCH, they'll get out of sync with other 
+ *       read/write or ioctl irps.
+ */
+{
+  ASSERT(DeviceObject);
+  ASSERT(Irp);
+
+  Irp->Tail.Overlay.DriverContext[0] = DeviceObject;
+  IoCsqInsertIrp(&Csq, Irp, NULL);
+
+  return STATUS_PENDING;
+}
+
+
+VOID NTAPI DeviceIoctlPassive(PDRIVE_INFO DriveInfo, 
+			      PIRP Irp)
+/*
+ * FUNCTION: Handlees IOCTL requests at PASSIVE_LEVEL
+ * ARGUMENTS:
+ *     DriveInfo: Drive that the IOCTL is directed at
+ *     Irp: IRP with the request in it
+ */
+{
+  PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
+  ULONG OutputLength = Stack->Parameters.DeviceIoControl.OutputBufferLength;
+  PVOID OutputBuffer = Irp->AssociatedIrp.SystemBuffer;
+  ULONG Code = Stack->Parameters.DeviceIoControl.IoControlCode;
+  BOOLEAN DiskChanged;
+
+  KdPrint(("floppy: DeviceIoctl called\n"));
+  Irp->IoStatus.Status = STATUS_SUCCESS;
+  Irp->IoStatus.Information = 0;
+
+  /* 
+   * First the non-change-sensitive ioctls 
+   */ 
+  if(Code == IOCTL_DISK_GET_MEDIA_TYPES)
+    {
+      PDISK_GEOMETRY Geometry = OutputBuffer;
+      KdPrint(("floppy: IOCTL_DISK_GET_MEDIA_TYPES Called\n"));
+
+      if(OutputLength < sizeof(DISK_GEOMETRY))
+        {
+	  KdPrint(("floppy: IOCTL_DISK_GET_MEDIA_TYPES: insufficient buffer; returning STATUS_INVALID_PARAMETER\n"));
+          Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+	  IoCompleteRequest(Irp, IO_NO_INCREMENT);
+	  return;
+        }
+
+      /* 
+       * for now, this driver only supports 3.5" HD media 
+       */
+      Geometry->MediaType = F3_1Pt44_512;
+      Geometry->Cylinders.QuadPart = 80;
+      Geometry->TracksPerCylinder = 2 * 18;
+      Geometry->SectorsPerTrack = 18;
+      Geometry->BytesPerSector = 512;
+
+      Irp->IoStatus.Status = STATUS_SUCCESS;
+      Irp->IoStatus.Information = sizeof(DISK_GEOMETRY);
+      KdPrint(("floppy: Ioctl: completing with STATUS_SUCCESS\n"));
+      IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+      return;
+    }
+
+  /* 
+   * Now, check to see if the volume needs to be verified.  If so,
+   * return STATUS_VERIFY_REQUIRED.  
+   *
+   * NOTE:  This code, which is outside of the switch and if/else blocks, 
+   * will implicity catch and correctly service IOCTL_DISK_CHECK_VERIFY.  
+   * Therefore if we see one below in the switch, we can return STATUS_SUCCESS
+   * immediately.
+   */
+  if(DriveInfo->DeviceObject->Flags & DO_VERIFY_VOLUME && !(DriveInfo->DeviceObject->Flags & SL_OVERRIDE_VERIFY_VOLUME)) 
+    {
+      KdPrint(("floppy: DeviceIoctl(): completing with STATUS_VERIFY_REQUIRED\n"));
+      Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
+      Irp->IoStatus.Information = 0;
+      IoCompleteRequest(Irp, IO_NO_INCREMENT);
+      return;
+    }
+
+  /*
+   * Check the change line, and if it's set, return
+   */
+  if(HwDiskChanged(DriveInfo, &DiskChanged) != STATUS_SUCCESS)
+    {
+      KdPrint(("floppy: DeviceIoctl(): unable to sense disk change; completing with STATUS_UNSUCCESSFUL\n"));
+      Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
+      Irp->IoStatus.Information = 0;
+      IoCompleteRequest(Irp, IO_NO_INCREMENT);
+      return;
+    }
+
+  if(DiskChanged)
+    {
+      KdPrint(("floppy: DeviceIoctl(): detected disk changed; signalling media change and completing\n"));
+      SignalMediaChanged(DriveInfo->DeviceObject, Irp);
+
+      /* 
+       * Just guessing here - I have a choice of returning NO_MEDIA or VERIFY_REQUIRED.  If there's
+       * really no disk in the drive, I'm thinking I can save time by just reporting that fact, rather
+       * than forcing windows to ask me twice.  If this doesn't work, we'll need to split this up and
+       * handle the CHECK_VERIFY IOCTL separately.
+       */
+      if(ResetChangeFlag(DriveInfo) == STATUS_NO_MEDIA_IN_DEVICE)
+	Irp->IoStatus.Status = STATUS_NO_MEDIA_IN_DEVICE;
+
+      IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+      return;
+    }
+
+  switch(Code)
+    {
+    case IOCTL_DISK_IS_WRITABLE:
+      {
+        UCHAR Status;
+
+        KdPrint(("floppy: IOCTL_DISK_IS_WRITABLE Called\n"));
+
+        /* This IRP always has 0 information */
+        Irp->IoStatus.Information = 0;
+
+        if(HwSenseDriveStatus(DriveInfo) != STATUS_SUCCESS)
+	  {
+	    KdPrint(("floppy: IoctlDiskIsWritable(): unable to sense drive status\n"));
+	    Irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR;
+	    break;
+	  }
+
+	/* Now, read the drive's status back */
+	if(HwSenseDriveStatusResult(DriveInfo->ControllerInfo, &Status) != STATUS_SUCCESS)
+	  {
+	    KdPrint(("floppy: IoctlDiskIsWritable(): unable to read drive status result\n"));
+	    Irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR;
+	    break;
+	  }
+
+        /* Check to see if the write flag is set. */
+        if(Status & SR3_WRITE_PROTECT_STATUS_SIGNAL)
+          {
+            KdPrint(("floppy: IOCTL_DISK_IS_WRITABLE: disk is write protected\n"));
+            Irp->IoStatus.Status = STATUS_MEDIA_WRITE_PROTECTED;
+          }
+        else
+	    Irp->IoStatus.Status = STATUS_SUCCESS;
+      }
+      break;
+
+    case IOCTL_DISK_CHECK_VERIFY:
+      KdPrint(("floppy: IOCTL_DISK_CHECK_VERIFY called\n"));
+      *((PULONG)OutputBuffer) = DriveInfo->DiskChangeCount;
+      Irp->IoStatus.Status = STATUS_SUCCESS;
+      Irp->IoStatus.Information = sizeof(ULONG);
+      break;
+
+    case IOCTL_DISK_GET_DRIVE_GEOMETRY:
+      {
+        KdPrint(("floppy: IOCTL_DISK_GET_DRIVE_GEOMETRY Called\n"));
+        if(OutputLength < sizeof(DISK_GEOMETRY))
+          {
+            Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+            break;
+          }
+
+	/* This still works right even if DriveInfo->DiskGeometry->MediaType = Unknown */
+	memcpy(OutputBuffer, &DriveInfo->DiskGeometry, sizeof(DISK_GEOMETRY));
+        Irp->IoStatus.Information = sizeof(DISK_GEOMETRY);
+        break;
+      }
+
+    case IOCTL_DISK_FORMAT_TRACKS:
+    case IOCTL_DISK_FORMAT_TRACKS_EX:
+      KdPrint(("floppy: Format called; not supported yet\n"));
+      break;
+
+    case IOCTL_DISK_GET_PARTITION_INFO:
+      KdPrint(("floppy: IOCTL_DISK_GET_PARTITION_INFO Called; not supported\n"));
+      Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+      Irp->IoStatus.Information = 0;
+      break;
+
+    default:
+      KdPrint(("floppy: UNKNOWN IOCTL CODE: 0x%x\n", Code));
+      Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+      Irp->IoStatus.Information = 0;
+      break;
+    }
+
+  KdPrint(("floppy: ioctl: completing with status 0x%x\n", Irp->IoStatus.Status));
+  IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+  return;
+}
+

reactos/drivers/storage/floppy
ioctl.h added at 1.1
diff -N ioctl.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ioctl.h	1 Mar 2004 06:37:26 -0000	1.1
@@ -0,0 +1,31 @@
+/*
+ *  ReactOS Floppy Driver
+ *  Copyright (C) 2004, Vizzini (vizzini@plasmic.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * PROJECT:         ReactOS Floppy Driver
+ * FILE:            ioctl.h
+ * PURPOSE:         Header for IOCTL routines
+ * PROGRAMMER:      Vizzini (vizzini@plasmic.com)
+ * REVISIONS:
+ *                  15-Feb-2004 vizzini - Created
+ */
+
+NTSTATUS NTAPI DeviceIoctl(PDEVICE_OBJECT DeviceObject, 
+                           PIRP Irp);
+
+VOID NTAPI DeviceIoctlPassive(PDRIVE_INFO DriveInfo,
+                              PIRP Irp);

reactos/drivers/storage/floppy
readwrite.c added at 1.1
diff -N readwrite.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ readwrite.c	1 Mar 2004 06:37:26 -0000	1.1
@@ -0,0 +1,710 @@
+/*
+ *  ReactOS Floppy Driver
+ *  Copyright (C) 2004, Vizzini (vizzini@plasmic.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * PROJECT:         ReactOS Floppy Driver
+ * FILE:            readwrite.c
+ * PURPOSE:         Read/Write handler routines
+ * PROGRAMMER:      Vizzini (vizzini@plasmic.com)
+ * REVISIONS:
+ *                  15-Feb-2004 vizzini - Created
+ * NOTES:
+ *
+ * READ/WRITE PROCESS
+ *
+ * This process is extracted from the Intel datasheet for the floppy controller.
+ *
+ * - Turn on the motor and set turnoff time
+ * - Program the drive's data rate
+ * - Seek
+ * - Read ID
+ * - Set up DMA
+ * - Send read/write command to FDC
+ * - Read result bytes
+ *
+ * This is mostly implemented in one big function, which watis on the SynchEvent
+ * as many times as necessary to get through the process.  See ReadWritePassive() for
+ * more details.
+ *
+ * NOTES:
+ *     - Currently doesn't support partial-sector transfers, which is really just a failing
+ *       of RWComputeCHS.  I've never seen Windows send a partial-sector request, though, so
+ *       this may not be a bad thing.  Should be looked into, regardless.
+ *
+ * TODO: Handle split reads and writes across multiple map registers
+ * TODO: Break up ReadWritePassive and handle errors better
+ * TODO: Figure out data rate issues
+ * TODO: I think RWComputeCHS is bugified
+ * TODO: Media type detection
+ * TODO: Figure out perf issue - waiting after call to read/write for about a second each time
+ * TODO: Figure out specify timings
+ */
+#include <ntddk.h>
+
+#include "floppy.h"
+#include "csqrtns.h"
+#include "hardware.h"
+#include "readwrite.h"
+
+
+static IO_ALLOCATION_ACTION NTAPI MapRegisterCallback(PDEVICE_OBJECT DeviceObject,
+                                                      PIRP Irp,
+                                                      PVOID MapRegisterBase,
+                                                      PVOID Context)
+/*
+ * FUNCTION: Acquire map registers in prep for DMA
+ * ARGUMENTS:
+ *     DeviceObject: unused
+ *     Irp: unused
+ *     MapRegisterBase: returned to blocked thread via a member var
+ *     Context: contains a pointer to the right ControllerInfo
+ *     struct
+ * RETURNS:
+ *     KeepObject, because that's what the DDK says to do
+ */
+{
+  PCONTROLLER_INFO ControllerInfo = (PCONTROLLER_INFO)Context;
+
+  KdPrint(("floppy: MapRegisterCallback Called\n"));
+
+  ControllerInfo->MapRegisterBase = MapRegisterBase;
+  KeSetEvent(&ControllerInfo->SynchEvent, 0, FALSE);
+
+  return KeepObject; /* FIXME: Should be something else if we find a bus master */
+}
+
+
+NTSTATUS NTAPI ReadWrite(PDEVICE_OBJECT DeviceObject,
+                         PIRP Irp)
+/*
+ * FUNCTION: Dispatch routine called for read or write IRPs
+ * ARGUMENTS:
+ * RETURNS:
+ *     STATUS_PENDING if the IRP is queued
+ *     STATUS_INVALID_PARAMETER if IRP is set up wrong
+ * NOTES:
+ *     - This function validates arguments to the IRP and then queues it
+ *     - Note that this function is implicitly serialized by the queue logic.  Only
+ *       one of these at a time is active in the system, no matter how many processors
+ *       and threads we have.
+ *     - This function stores the DeviceObject in the IRP's context area before dropping
+ *       it onto the irp queue
+ */
+{
+  PIO_STACK_LOCATION Stack;
+  KdPrint(("floppy: ReadWrite called\n"));
+
+  ASSERT(DeviceObject);
+  ASSERT(Irp);
+
+  Stack = IoGetCurrentIrpStackLocation(Irp);
+
+  if(!Irp->MdlAddress)
+    {
+      KdPrint(("floppy: ReadWrite(): MDL not found in IRP - Completing with STATUS_INVALID_PARAMETER\n"));
+      Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+      Irp->IoStatus.Information = 0;
+      IoCompleteRequest(Irp, IO_NO_INCREMENT);
+      return STATUS_INVALID_PARAMETER;
+    }
+
+  /* 
+   * Queue the irp to the thread.  
+   * The de-queue thread will look in DriverContext[0] for the Device Object. 
+   */
+  Irp->Tail.Overlay.DriverContext[0] = DeviceObject;
+  IoCsqInsertIrp(&Csq, Irp, NULL);
+
+  return STATUS_PENDING;
+}
+
+
+static VOID NTAPI RWFreeAdapterChannel(PADAPTER_OBJECT AdapterObject)
+/*
+ * FUNCTION: Free the adapter DMA channel that we allocated
+ * ARGUMENTS:
+ *     AdapterObject: the object with the map registers to free
+ * NOTES:
+ *     - This function is primarily needed because IoFreeAdapterChannel wants to
+ *       be called at DISPATCH_LEVEL
+ */
+{
+  KIRQL Irql;
+
+  ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
+
+  KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+  IoFreeAdapterChannel(AdapterObject);
+  KeLowerIrql(Irql);
+}
+
+
+static NTSTATUS NTAPI RWDetermineMediaType(PDRIVE_INFO DriveInfo)
+/*
+ * FUNCTION: Determine the media type of the disk in the drive and fill in the geometry
+ * ARGUMENTS:
+ *     DriveInfo: drive to look at
+ * RETURNS:
+ *     STATUS_SUCCESS if the media was recognized and the geometry struct was filled in
+ *     STATUS_UNRECOGNIZED_MEDIA if not
+ *     STATUS_UNSUCCESSFUL if the controller can't be talked to
+ * NOTES:
+ *     - Expects the motor to already be running
+ *     - Currently only supports 1.44MB 3.5" disks
+ *     - PAGED_CODE because it waits
+ * TODO:
+ *     - Support more disk types
+ */
+{
+  UCHAR HeadLoadTime;
+  UCHAR HeadUnloadTime;
+  UCHAR StepRateTime;
+
+  PAGED_CODE();
+
+  KdPrint(("floppy: RWDetermineMediaType called\n"));
+
+  /*
+   * This algorithm assumes that a 1.44MB floppy is in the drive.  If it's not,
+   * it works backwards until the read works.  Note that only 1.44 has been tested
+   * at all.
+   */
+
+  do
+    {
+      /* Program data rate */
+      if(HwSetDataRate(DriveInfo->ControllerInfo, DRSR_DSEL_500KBPS) != STATUS_SUCCESS)
+	{
+	  KdPrint(("floppy: RWDetermineMediaType(): unable to set data rate\n"));
+	  return STATUS_UNSUCCESSFUL;
+	}
+      
+      /* Specify */
+      HeadLoadTime = SPECIFY_HLT_500K;
+      HeadUnloadTime = SPECIFY_HUT_500K;
+      StepRateTime = SPECIFY_SRT_500K;
+      
+      /* Don't disable DMA --> enable dma (dumb & confusing) */
+      if(HwSpecify(DriveInfo->ControllerInfo, HeadLoadTime, HeadUnloadTime, StepRateTime, FALSE) != STATUS_SUCCESS)
+	{
+	  KdPrint(("floppy: RWDetermineMediaType(): specify failed\n"));
+	  return STATUS_UNSUCCESSFUL;
+	}
+
+      /* clear any spurious interrupts in preparation for recalibrate */
+      KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);
+
+      /* Recalibrate --> head over first track */
+      /* FIXME: should be done in a loop? */
+      if(HwRecalibrate(DriveInfo) != STATUS_SUCCESS)
+	{
+	  KdPrint(("floppy: RWDetermineMediaType(): Recalibrate failed\n"));
+	  return STATUS_UNSUCCESSFUL;
+	}
+
+      /* Wait for the recalibrate to finish */
+      WaitForControllerInterrupt(DriveInfo->ControllerInfo);
+
+      if(HwRecalibrateResult(DriveInfo->ControllerInfo) != STATUS_SUCCESS)
+	{
+	  KdPrint(("floppy: RWDetermineMediaType(): RecalibrateResult failed\n"));
+	  return STATUS_UNSUCCESSFUL;
+	}
+
+      /* clear any spurious interrupts */
+      KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);
+
+      /* Try to read an ID */
+      if(HwReadId(DriveInfo, 0) != STATUS_SUCCESS) /* read the first ID we find, from head 0 */
+	{
+	  KdPrint(("floppy: RWDetermineMediaType(): ReadId failed\n"));
+	  return STATUS_UNSUCCESSFUL; /* if we can't even write to the controller, it's hopeless */
+	}
+   
+      /* Wait for the recalibrate to finish */
+      WaitForControllerInterrupt(DriveInfo->ControllerInfo);
+
+      if(HwReadIdResult(DriveInfo->ControllerInfo, NULL, NULL) != STATUS_SUCCESS)
+	{
+	  KdPrint(("floppy: RWDetermineMediaType(): ReadIdResult failed; continuing\n"));
+	  continue;
+	}
+
+      /* Found the media; populate the geometry now */
+      KdPrint(("FIXME: Hardcoded media type!\n"));
+      KdPrint(("floppy: RWDetermineMediaType(): Found 1.44 media; returning success\n"));
+      DriveInfo->DiskGeometry.MediaType = GEOMETRY_144_MEDIATYPE;
+      DriveInfo->DiskGeometry.Cylinders.QuadPart = GEOMETRY_144_CYLINDERS;
+      DriveInfo->DiskGeometry.TracksPerCylinder = GEOMETRY_144_TRACKSPERCYLINDER;
+      DriveInfo->DiskGeometry.SectorsPerTrack = GEOMETRY_144_SECTORSPERTRACK;
+      DriveInfo->DiskGeometry.BytesPerSector = GEOMETRY_144_BYTESPERSECTOR;
+      DriveInfo->BytesPerSectorCode = HW_512_BYTES_PER_SECTOR;
+      return STATUS_SUCCESS;
+    }
+  while(FALSE);
+
+  KdPrint(("floppy: RWDetermineMediaType(): failed to find media\n"));
+  return STATUS_UNRECOGNIZED_MEDIA;
+}
+
+
+static NTSTATUS NTAPI RWSeekToCylinder(PDRIVE_INFO DriveInfo,
+                                       UCHAR Cylinder)
+/*
+ * FUNCTION: Seek a particular drive to a particular track
+ * ARGUMENTS:
+ *     DriveInfo: Drive to seek
+ *     Cylinder: track to seek to
+ * RETURNS:
+ *     STATUS_SUCCESS if the head was successfully seeked
+ *     STATUS_UNSUCCESSFUL if not
+ * NOTES:
+ *     - PAGED_CODE because it blocks
+ */
+{
+  UCHAR CurCylinder;
+
+  PAGED_CODE();
+
+  /* Clear any spurious interrupts */
+  KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);
+
+  /* queue seek command */
+  if(HwSeek(DriveInfo, Cylinder) != STATUS_SUCCESS)
+    {
+      KdPrint(("floppy: RWSeekToTrack(): unable to seek\n"));
+      return STATUS_UNSUCCESSFUL;
+    }
+
+  WaitForControllerInterrupt(DriveInfo->ControllerInfo);
+
+  if(HwSenseInterruptStatus(DriveInfo->ControllerInfo) != STATUS_SUCCESS)
+    {
+      KdPrint(("floppy: RWSeekToTrack(): unable to get seek results\n"));
+      return STATUS_UNSUCCESSFUL;
+    }
+
+  /* read ID mark from head 0 to verify */
+  if(HwReadId(DriveInfo, 0) != STATUS_SUCCESS)
+    {
+      KdPrint(("floppy: RWSeekToTrack(): unable to queue ReadId\n"));
+      return STATUS_UNSUCCESSFUL;
+    }
+
+  WaitForControllerInterrupt(DriveInfo->ControllerInfo);
+
+  if(HwReadIdResult(DriveInfo->ControllerInfo, &CurCylinder, NULL) != STATUS_SUCCESS)
+    {
+      KdPrint(("floppy: RWSeekToTrack(): unable to get ReadId result\n"));
+      return STATUS_UNSUCCESSFUL;
+    }
+
+  if(CurCylinder != Cylinder)
+    {
+      KdPrint(("floppy: RWSeekToTrack(): Seeek to track failed; current cylinder is 0x%x\n", CurCylinder));
+      return STATUS_UNSUCCESSFUL;
+    }
+
+  return STATUS_SUCCESS;
+}
+
+
+static NTSTATUS NTAPI RWComputeCHS(PDRIVE_INFO IN  DriveInfo,
+				   ULONG       IN  DiskByteOffset,
+				   PUCHAR      OUT Cylinder,
+				   PUCHAR      OUT Head,
+				   PUCHAR      OUT Sector)
+/*
+ * FUNCTION: Compute the CHS from the absolute byte offset on disk
+ * ARGUMENTS:
+ *     DriveInfo: Drive to compute on
+ *     DiskByteOffset: Absolute offset on disk of the starting byte
+ *     Cylinder: Cylinder that the byte is on
+ *     Head: Head that the byte is on
+ *     Sector: Sector that the byte is on
+ * RETURNS:
+ *     STATUS_SUCCESS if CHS are determined correctly
+ *     STATUS_UNSUCCESSFUL otherwise
+ * NOTES:
+ *     - Lots of ugly typecasts here
+ *     - Sectors are 1-based!
+ *     - FIXME: I think this routine is still bugified a bit
+ */
+{
+  ULONG AbsoluteSector;
+  UCHAR SectorsPerCylinder = (UCHAR)DriveInfo->DiskGeometry.SectorsPerTrack * (UCHAR)DriveInfo->DiskGeometry.TracksPerCylinder;
+
+  KdPrint(("floppy: RWComputeCHS: Called with offset 0x%x\n", DiskByteOffset));
+
+  /* First calculate the 1-based "absolute sector" based on the byte offset */
+  ASSERT(!(DiskByteOffset % DriveInfo->DiskGeometry.BytesPerSector));         /* FIXME: Only handle full sector transfers atm */
+  AbsoluteSector = DiskByteOffset / DriveInfo->DiskGeometry.BytesPerSector;  /* Num full sectors */
+
+  /* Cylinder number is floor(AbsoluteSector / SectorsPerCylinder) */
+  *Cylinder =  (CHAR)(AbsoluteSector / SectorsPerCylinder); 
+
+  /* Head number is 1 if the sector within the cylinder > SectorsPerTrack; 0 otherwise */
+  *Head =  AbsoluteSector % SectorsPerCylinder > DriveInfo->DiskGeometry.SectorsPerTrack ? 1 : 0;
+
+  /* 
+   * Sector number is the sector within the cylinder if on head 0; that minus SectorsPerTrack if it's on head 1 
+   * (lots of casts to placate msvc).  1-based!
+   */
+  *Sector =  ((UCHAR)(AbsoluteSector % SectorsPerCylinder) + 1) - ((*Head) * (UCHAR)DriveInfo->DiskGeometry.SectorsPerTrack);
+
+  KdPrint(("floppy: RWComputeCHS: offset 0x%x is c:0x%x h:0x%x s:0x%x\n", DiskByteOffset, *Cylinder, *Head, *Sector));
+
+  return STATUS_SUCCESS;
+}
+
+
+VOID NTAPI ReadWritePassive(PDRIVE_INFO DriveInfo,
+                            PIRP Irp)
+/*
+ * FUNCTION: Handle the first phase of a read or write IRP
+ * ARGUMENTS:
+ *     DeviceObject: DeviceObject that is the target of the IRP
+ *     Irp: IRP to process
+ * RETURNS:
+ *     STATUS_VERIFY_REQUIRED if the media has changed and we need the filesystems to re-synch
+ *     STATUS_SUCCESS otherwise
+ * NOTES:
+ *     - Must be called at PASSIVE_LEVEL
+ *     - This function is about 250 lines longer than I wanted it to be.  Sorry.
+ *
+ * DETAILS:
+ *  This routine manages the whole process of servicing a read or write request.  It goes like this:
+ *    1) Check the DO_VERIFY_VOLUME flag and return if it's set
+ *    2) Check the disk change line and notify the OS if it's set and return
+ *    3) Detect the media if we haven't already
+ *    4) Set up DiskByteOffset, Length, and WriteToDevice parameters
+ *    5) Get DMA map registers
+ *    6) Then, in a loop for each track, until all bytes are transferred:
+ *      a) Compute the current CHS to set the read/write head to
+ *      b) Seek to that spot
+ *      c) Compute the last sector to transfer on that track
+ *      d) Map the transfer through DMA
+ *      e) Send the read or write command to the controller
+ *      f) Read the results of the command
+ */
+{
+  PDEVICE_OBJECT DeviceObject = DriveInfo->DeviceObject;
+  PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
+  BOOLEAN WriteToDevice;
+  ULONG Length;
+  ULONG DiskByteOffset;
+  KIRQL OldIrql;
+  NTSTATUS Status;
+  BOOLEAN DiskChanged;
+  ULONG_PTR TransferByteOffset;
+
+  PAGED_CODE();
+
+  KdPrint(("floppy: ReadWritePassive called to %s 0x%x bytes from offset 0x%x\n",
+	   (Stack->MajorFunction == IRP_MJ_READ ? "read" : "write"),
+	   (Stack->MajorFunction == IRP_MJ_READ ? Stack->Parameters.Read.Length : Stack->Parameters.Write.Length),
+	   (Stack->MajorFunction == IRP_MJ_READ ? Stack->Parameters.Read.ByteOffset.u.LowPart : 
+	    Stack->Parameters.Write.ByteOffset.u.LowPart)));
+
+  /* Default return codes */
+  Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
+  Irp->IoStatus.Information = 0;
+
+  /* 
+   * Check to see if the volume needs to be verified.  If so,
+   * we can get out of here quickly.
+   */
+  if(DeviceObject->Flags & DO_VERIFY_VOLUME && !(DeviceObject->Flags & SL_OVERRIDE_VERIFY_VOLUME)) 
+    {
+      KdPrint(("floppy: ReadWritePassive(): DO_VERIFY_VOLUME set; Completing with  STATUS_VERIFY_REQUIRED\n"));
+      Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
+      IoCompleteRequest(Irp, IO_NO_INCREMENT);
+      return;
+    }
+
+  /*
+   * Check the change line, and if it's set, return
+   */
+  if(HwDiskChanged(DeviceObject->DeviceExtension, &DiskChanged) != STATUS_SUCCESS)
+    {
+      KdPrint(("floppy: ReadWritePassive(): unable to detect disk change; Completing with STATUS_UNSUCCESSFUL\n"));
+      IoCompleteRequest(Irp, IO_NO_INCREMENT);
+      return;
+    }
+      
+  if(DiskChanged)
+    {
+      KdPrint(("floppy: ReadWritePhase1(): signalling media changed; Completing with STATUS_MEDIA_CHANGED\n"));
+
+      /* The following call sets IoStatus.Status and IoStatus.Information */
+      SignalMediaChanged(DeviceObject, Irp);
+
+      /*
+       * Guessing at something... see ioctl.c for more info
+       */
+      if(ResetChangeFlag(DriveInfo) == STATUS_NO_MEDIA_IN_DEVICE)
+	Irp->IoStatus.Status = STATUS_NO_MEDIA_IN_DEVICE;
+
+      IoCompleteRequest(Irp, IO_NO_INCREMENT);
+      return;
+    }
+
+  /* Start the motor and set the initial data rate */
+  StartMotor(DriveInfo);
+
+  /* 
+   * Figure out the media type, if we don't know it already 
+   */ 
+  if(DriveInfo->DiskGeometry.MediaType == Unknown)
+    {
+      if(RWDetermineMediaType(DriveInfo) != STATUS_SUCCESS)
+	{
+	  KdPrint(("floppy: ReadWritePassive(): unable to determine media type; completing with STATUS_UNSUCCESSFUL\n"));
+	  IoCompleteRequest(Irp, IO_NO_INCREMENT);
+	  StopMotor(DriveInfo->ControllerInfo);
+	  return;
+	}
+
+      if(DriveInfo->DiskGeometry.MediaType == Unknown)
+	{
+	  KdPrint(("floppy: ReadWritePassive(): Unknown media in drive; completing with STATUS_UNRECOGNIZED_MEDIA\n"));
+	  Irp->IoStatus.Status = STATUS_UNRECOGNIZED_MEDIA;
+	  IoCompleteRequest(Irp, IO_NO_INCREMENT);
+	  StopMotor(DriveInfo->ControllerInfo);
+	  return;
+	}
+    }
+
+  /* Set up parameters for read or write */
+  if(Stack->MajorFunction == IRP_MJ_READ)
+    {
+      if(Stack->Parameters.Read.Length > PAGE_SIZE * DriveInfo->ControllerInfo->MapRegisters)
+	{
+	  KdPrint(("floppy: ReadWritePassive(): unable to transfer; would have to split\n"));
+	  ASSERT(0);
+	}
+      Length = Stack->Parameters.Read.Length;
+      DiskByteOffset = Stack->Parameters.Read.ByteOffset.u.LowPart;
+      WriteToDevice = FALSE;
+    }
+  else
+    {
+      if(Stack->Parameters.Write.Length > PAGE_SIZE * DriveInfo->ControllerInfo->MapRegisters)
+	{
+	  KdPrint(("floppy: ReadWritePassive(): unable to transfer; would have to split\n"));
+	  ASSERT(0);
+	}
+      Length = Stack->Parameters.Write.Length;
+      DiskByteOffset = Stack->Parameters.Write.ByteOffset.u.LowPart;
+      WriteToDevice = TRUE;
+    }
+
+  /* 
+   * Set up DMA transfer 
+   *
+   * This is as good of a place as any to document something that used to confuse me
+   * greatly (and I even wrote some of the kernel's DMA code, so if it confuses me, it
+   * probably confuses at least a couple of other people too).  
+   *
+   * MmGetMdlVirtualAddress() returns the virtal address, as mapped in the buffer's original
+   * process context, of the MDL.  In other words:  say you start with a buffer at address X, then
+   * you build an MDL out of that buffer called Mdl. If you call MmGetMdlVirtualAddress(Mdl), it
+   * will return X.  
+   *
+   * There are two parameters that the function looks at to produce X again, given the MDL:  the
+   * first is the StartVa, which is the base virtual address of the page that the buffer starts
+   * in.  If your buffer's virtual address is 0x12345678, StartVa will be 0x12345000, assuming 4K pages
+   * (which is (almost) always the case on x86).  Note well: this address is only valid in the
+   * process context that you initially built the MDL from.  The physical pages that make up
+   * the MDL might perhaps be mapped in other process contexts too (or even in the system space,
+   * above 0x80000000 (default; 0xc0000000 on current ReactOS or /3GB Windows)), but it will
+   * (possibly) be mapped at a different address.
+   *
+   * The second parameter is the ByteOffset.  Given an original buffer address of 0x12345678, 
+   * the ByteOffset would be 0x678.  Because MDLs can only describe full pages (and therefore
+   * StartVa always points to the start address of a page), the ByteOffset must be used to
+   * find the real start of the buffer.
+   *
+   * In general, if you add the StartVa and ByteOffset together, you get back your original
+   * buffer pointer, which you are free to use if you're sure you're in the right process
+   * context.  You could tell by accessing the (hidden and not-to-be-used) Process member of
+   * the MDL, but in general, if you have to ask whether or not you are in the right context,
+   * then you shouldn't be using this address for anything anyway.  There are also security implications
+   * (big ones, really, I wouldn't kid about this) to directly accessing a user's buffer by VA, so
+   * Don't Do That.
+   *
+   * There is a somewhat weird but very common use of the virtual address associated with a MDL
+   * that pops up often in the context of DMA.  DMA APIs (particularly MapTransfer()) need to
+   * know where the memory is that they should DMA into and out of.  This memory is described
+   * by a MDL.  The controller eventually needs to know a physical address on the host side, 
+   * which is generally a 32-bit linear address (on x86), and not just a page address.  Therefore,
+   * the DMA APIs look at the ByteOffset field of the MDL to reconstruct the real address that
+   * should be programmed into the DMA controller.
+   *
+   * It is often the case that a transfer needs to be broken down over more than one DMA operation,
+   * particularly when it is a big transfer and the HAL doesn't give you enough map registers
+   * to map the whole thing at once.  Therefore, the APIs need a way to tell how far into the MDL
+   * they should look to transfer the next chunk of bytes.  Now, Microsoft could have designed
+   * MapTransfer to take a  "MDL offset" argument, starting with 0, for how far into the buffer to
+   * start, but it didn't.  Instead, MapTransfer asks for the virtual address of the MDL as an "index" into
+   * the MDL.  The way it computes how far into the page to start the transfer is by masking off all but
+   * the bottom 12 bits (on x86) of the number you supply as the CurrentVa and using *that* as the
+   * ByteOffset instead of the one in the MDL.  (OK, this varies a bit by OS and version, but this
+   * is the effect).  
+   *
+   * In other words, you get a number back from MmGetMdlVirtualAddress that represents the start of your 
+   * buffer, and you pass it to the first MapTransfer call.  Then, for each successive operation
+   * on the same buffer, you increment that address to point to the next spot in the MDL that
+   * you want to DMA to/from.  The fact that the virtual address you're manipulating is probably not
+   * mapped into the process context that you're running in is irrelevant, since it's only being
+   * used to index into the MDL.
+   */ 
+
+  /* Get map registers for DMA */
+  KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
+  Status = IoAllocateAdapterChannel(DriveInfo->ControllerInfo->AdapterObject, DeviceObject,
+                                    DriveInfo->ControllerInfo->MapRegisters, MapRegisterCallback, DriveInfo->ControllerInfo);
+  KeLowerIrql(OldIrql);
+
+  if(Status != STATUS_SUCCESS)
+    {
+      KdPrint(("floppy: ReadWritePassive(): unable allocate an adapter channel; completing with STATUS_UNSUCCESSFUL\n"));
+      IoCompleteRequest(Irp, IO_NO_INCREMENT);
+      StopMotor(DriveInfo->ControllerInfo);
+      return ;
+    }
+
+  /*
+   * Read from (or write to) the device
+   *
+   * This has to be called in a loop, as you can only transfer data to/from a single track at
+   * a time.
+   */
+
+  TransferByteOffset = 0;
+  while(TransferByteOffset < Length)
+    {
+      UCHAR Cylinder;
+      UCHAR Head;
+      UCHAR StartSector;
+      ULONG CurrentTransferBytes;
+      UCHAR CurrentTransferSectors;
+      
+      KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent); 
+
+      /*
+       * Compute starting CHS
+       */
+      if(RWComputeCHS(DriveInfo, DiskByteOffset+TransferByteOffset, &Cylinder, &Head, &StartSector) != STATUS_SUCCESS)
+	{
+	  KdPrint(("floppy: ReadWritePassive(): unable to compute CHS; completing with STATUS_UNSUCCESSFUL\n"));
+	  RWFreeAdapterChannel(DriveInfo->ControllerInfo->AdapterObject);
+	  IoCompleteRequest(Irp, IO_NO_INCREMENT);
+          StopMotor(DriveInfo->ControllerInfo);
+	  return;
+	}
+
+      /*
+       * Seek to the right track
+       */
+      if(!DriveInfo->ControllerInfo->ImpliedSeeks)
+        {
+	  if(RWSeekToCylinder(DriveInfo, Cylinder) != STATUS_SUCCESS)
+            {
+	      KdPrint(("floppy: ReadWritePassive(): unable to seek; completing with STATUS_UNSUCCESSFUL\n"));
+	      RWFreeAdapterChannel(DriveInfo->ControllerInfo->AdapterObject);
+	      IoCompleteRequest(Irp, IO_NO_INCREMENT);
+              StopMotor(DriveInfo->ControllerInfo);
+	      return ;
+	    }
+        }
+
+      /*
+       * Compute last sector
+       *
+       * We can only ask for a transfer up to the end of the track.  Then we have to re-seek and do more.
+       * TODO: Support the MT bit
+       */
+      if( (DriveInfo->DiskGeometry.SectorsPerTrack - StartSector) < Length / DriveInfo->DiskGeometry.BytesPerSector)
+	  CurrentTransferSectors = (UCHAR)DriveInfo->DiskGeometry.SectorsPerTrack - StartSector;
+      else
+	  CurrentTransferSectors = (UCHAR)(Length / DriveInfo->DiskGeometry.BytesPerSector);
+
+      CurrentTransferBytes = CurrentTransferSectors * DriveInfo->DiskGeometry.BytesPerSector;
+
+      /* set up this round's dma operation */
+      /* param 2 is ReadOperation --> opposite of WriteToDevice that IoMapTransfer takes.  BAD MS. */
+      KeFlushIoBuffers(Irp->MdlAddress, !WriteToDevice, TRUE);
+
+      IoMapTransfer(DriveInfo->ControllerInfo->AdapterObject, Irp->MdlAddress, 
+		    DriveInfo->ControllerInfo->MapRegisterBase, 
+		    (PUCHAR)((ULONG_PTR)MmGetMdlVirtualAddress(Irp->MdlAddress) + TransferByteOffset), 
+		    &CurrentTransferBytes, WriteToDevice);
+
+      /*
+       * Read or Write
+       */
+      KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent); 
+
+      /* Issue the read/write command to the controller.  Note that it expects the opposite of WriteToDevice. */ 
+      if(HwReadWriteData(DriveInfo->ControllerInfo, !WriteToDevice, DriveInfo->UnitNumber, Cylinder, Head, StartSector, 
+			 DriveInfo->BytesPerSectorCode, StartSector + CurrentTransferSectors, 0, 0xff) != STATUS_SUCCESS)
+	{
+	  KdPrint(("floppy: ReadWritePassive(): HwReadWriteData returned failure; unable to read; completing with STATUS_UNSUCCESSFUL\n"));
+	  RWFreeAdapterChannel(DriveInfo->ControllerInfo->AdapterObject);
+	  IoCompleteRequest(Irp, IO_NO_INCREMENT);
+          StopMotor(DriveInfo->ControllerInfo);
+	  return ;
+	}
+
+      KdPrint(("floppy: ReadWritePassive(): HwReadWriteData returned -- waiting on event\n"));
+
+      /* 
+       * At this point, we block and wait for an interrupt
+       * FIXME: this seems to take too long
+       */
+      WaitForControllerInterrupt(DriveInfo->ControllerInfo);
+
+      /* Read is complete; flush & free adapter channel */
+      IoFlushAdapterBuffers(DriveInfo->ControllerInfo->AdapterObject, Irp->MdlAddress, 
+			    DriveInfo->ControllerInfo->MapRegisterBase, 
+			    MmGetMdlVirtualAddress(Irp->MdlAddress),
+			    CurrentTransferBytes, WriteToDevice);
+
+      /* Read the results from the drive */
+      if(HwReadWriteResult(DriveInfo->ControllerInfo) != STATUS_SUCCESS)
+	{
+	  KdPrint(("floppy: ReadWritePassive(): HwReadWriteResult returned failure; unable to read; completing with STATUS_UNSUCCESSFUL\n"));
+	  HwDumpRegisters(DriveInfo->ControllerInfo);
+	  RWFreeAdapterChannel(DriveInfo->ControllerInfo->AdapterObject);
+	  IoCompleteRequest(Irp, IO_NO_INCREMENT);
+          StopMotor(DriveInfo->ControllerInfo);
+	  return ;
+	}
+
+      TransferByteOffset += CurrentTransferBytes;
+    }
+
+  RWFreeAdapterChannel(DriveInfo->ControllerInfo->AdapterObject);
+
+  /* That's all folks! */
+  KdPrint(("floppy: ReadWritePassive(): success; Completing with STATUS_SUCCESS\n"));
+  Irp->IoStatus.Status = STATUS_SUCCESS;
+  IoCompleteRequest(Irp, IO_NO_INCREMENT);
+  StopMotor(DriveInfo->ControllerInfo);
+}
+

reactos/drivers/storage/floppy
readwrite.h added at 1.1
diff -N readwrite.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ readwrite.h	1 Mar 2004 06:37:26 -0000	1.1
@@ -0,0 +1,32 @@
+/*
+ *  ReactOS Floppy Driver
+ *  Copyright (C) 2004, Vizzini (vizzini@plasmic.com)
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * PROJECT:         ReactOS Floppy Driver
+ * FILE:            readwrite.h
+ * PURPOSE:         Header for read/write routines
+ * PROGRAMMER:      Vizzini (vizzini@plasmic.com)
+ * REVISIONS:
+ *                  15-Feb-2004 vizzini - Created
+ */
+
+
+NTSTATUS NTAPI ReadWrite(PDEVICE_OBJECT DeviceObject,
+                         PIRP Irp);
+
+VOID NTAPI ReadWritePassive(PDRIVE_INFO DriveInfo, PIRP Irp);
+
CVSspam 0.2.8