⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 floppy.c

📁 ReactOS是一些高手根据Windows XP的内核编写出的类XP。内核实现机理和API函数调用几乎相同。甚至可以兼容XP的程序。喜欢研究系统内核的人可以看一看。
💻 C
📖 第 1 页 / 共 3 页
字号:
/*
 *  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:
 *  - This driver is only designed to work with ISA-bus floppy controllers.  This
 *    won't work on PCI-based controllers or on anything else with level-sensitive
 *    interrupts without modification.  I don't think these controllers exist.
 *
 * ---- General to-do items ----
 * TODO: Figure out why CreateClose isn't called any more.  Seems to correspond
 *       with the driver not being unloadable.
 * TODO: Think about StopDpcQueued -- could be a race; too tired atm to tell
 * TODO: Clean up drive start/stop responsibilities (currently a mess...)
 *
 * ---- 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
 */

#define NDEBUG
#include <debug.h>
#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.
 */
static CONTROLLER_INFO gControllerInfo[MAX_CONTROLLERS];
static ULONG gNumberOfControllers = 0;

/* Queue thread management */
static KEVENT QueueThreadTerminate;
static PVOID QueueThreadObject;


static VOID NTAPI MotorStopDpcFunc(PKDPC UnusedDpc,
			    PVOID DeferredContext,
			    PVOID SystemArgument1,
			    PVOID SystemArgument2)
/*
 * FUNCTION: Stop the floppy motor
 * ARGUMENTS:
 *     UnusedDpc: 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;

  UNREFERENCED_PARAMETER(SystemArgument1);
  UNREFERENCED_PARAMETER(SystemArgument2);
  UNREFERENCED_PARAMETER(UnusedDpc);

  ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  ASSERT(ControllerInfo);

  DPRINT("floppy: MotorStopDpcFunc called\n");

  HwTurnOffMotor(ControllerInfo);
  ControllerInfo->StopDpcQueued = FALSE;
  KeSetEvent(&ControllerInfo->MotorStoppedEvent, EVENT_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);

  DPRINT("floppy: StartMotor called\n");

  if(DriveInfo->ControllerInfo->StopDpcQueued && !KeCancelTimer(&DriveInfo->ControllerInfo->MotorTimer))
    {
      /* Motor turner-offer is already running; wait for it to finish */
      DPRINT("floppy: StartMotor: motor turner-offer is already running; waiting for it\n");
      KeWaitForSingleObject(&DriveInfo->ControllerInfo->MotorStoppedEvent, Executive, KernelMode, FALSE, NULL);
      DPRINT("floppy: StartMotor: wait satisfied\n");
    }

  DriveInfo->ControllerInfo->StopDpcQueued = FALSE;

  if(HwTurnOnMotor(DriveInfo) != STATUS_SUCCESS)
  {
    DPRINT("floppy: StartMotor(): warning: HwTurnOnMotor failed\n");
  }
}


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);

  DPRINT("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
 */
{
  UNREFERENCED_PARAMETER(DeviceObject);

  DPRINT("floppy: CreateClose called\n");

  Irp->IoStatus.Status = STATUS_SUCCESS;
  Irp->IoStatus.Information = FILE_OPENED;

  IoCompleteRequest(Irp, IO_DISK_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_IO_DEVICE_ERROR 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 */
  DPRINT("floppy: FIXME: UN-HARDCODE DATA RATE\n");
  if(HwSetDataRate(DriveInfo->ControllerInfo, 0) != STATUS_SUCCESS)
    {
      DPRINT("floppy: Recalibrate: HwSetDataRate failed\n");
      StopMotor(DriveInfo->ControllerInfo);
      return STATUS_IO_DEVICE_ERROR;
    }

  /* clear the event just in case the last call forgot */
  KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);

  /* sometimes you have to do this twice; we'll just do it twice all the time since
   * we don't know if the people calling this Recalibrate routine expect a disk to
   * even be in the drive, and if so, if that disk is formatted.
   */
  for(i = 0; i < 2; i++)
    {
      /* Send the command */
      Status = HwRecalibrate(DriveInfo);
      if(Status != STATUS_SUCCESS)
	{
	  DPRINT("floppy: Recalibrate: HwRecalibrate returned error\n");
          continue;
	}

      WaitForControllerInterrupt(DriveInfo->ControllerInfo);

      /* Get the results */
      Status = HwRecalibrateResult(DriveInfo->ControllerInfo);
      if(Status != STATUS_SUCCESS)
	{
	  DPRINT("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);

  DPRINT("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);

  /* must re-start the drive because Recalibrate() stops it */
  StartMotor(DriveInfo);

  /* Seek to 1 */
  if(HwSeek(DriveInfo, 1) != STATUS_SUCCESS)
    {
      DPRINT("floppy: ResetChangeFlag(): HwSeek failed; returning STATUS_IO_DEVICE_ERROR\n");
      StopMotor(DriveInfo->ControllerInfo);
      return STATUS_IO_DEVICE_ERROR;
    }

  WaitForControllerInterrupt(DriveInfo->ControllerInfo);

  if(HwSenseInterruptStatus(DriveInfo->ControllerInfo) != STATUS_SUCCESS)
    {
      DPRINT("floppy: ResetChangeFlag(): HwSenseInterruptStatus failed; bailing out\n");
      StopMotor(DriveInfo->ControllerInfo);
      return STATUS_IO_DEVICE_ERROR;
    }

  /* Seek back to 0 */
  if(HwSeek(DriveInfo, 0) != STATUS_SUCCESS)
    {
      DPRINT("floppy: ResetChangeFlag(): HwSeek failed; returning STATUS_IO_DEVICE_ERROR\n");
      StopMotor(DriveInfo->ControllerInfo);
      return STATUS_IO_DEVICE_ERROR;
    }

  WaitForControllerInterrupt(DriveInfo->ControllerInfo);

  if(HwSenseInterruptStatus(DriveInfo->ControllerInfo) != STATUS_SUCCESS)
    {
      DPRINT("floppy: ResetChangeFlag(): HwSenseInterruptStatus #2 failed; bailing\n");
      StopMotor(DriveInfo->ControllerInfo);
      return STATUS_IO_DEVICE_ERROR;
    }

  /* Check the change bit */
  if(HwDiskChanged(DriveInfo, &DiskChanged) != STATUS_SUCCESS)
    {
      DPRINT("floppy: ResetChangeFlag(): HwDiskChagned failed; returning STATUS_IO_DEVICE_ERROR\n");
      StopMotor(DriveInfo->ControllerInfo);
      return STATUS_IO_DEVICE_ERROR;
    }

  StopMotor(DriveInfo->ControllerInfo);

  /* 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
 */
{
  ULONG i,j;

  PAGED_CODE();
  UNREFERENCED_PARAMETER(DriverObject);

  DPRINT("floppy: unloading\n");

  KeSetEvent(&QueueThreadTerminate, 0, FALSE);
  KeWaitForSingleObject(QueueThreadObject, Executive, KernelMode, FALSE, 0);
  ObDereferenceObject(QueueThreadObject);

  for(i = 0; i < gNumberOfControllers; i++)
    {
      if(!gControllerInfo[i].Initialized)
	continue;

      for(j = 0; j < gControllerInfo[i].NumberOfDrives; j++)
	{
	  if(!gControllerInfo[i].DriveInfo[j].Initialized)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -