14 added files
reactos/drivers/storage/floppy
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
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
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
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
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
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
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
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
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
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
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
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
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
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