readwrite.c

来自「一个类似windows」· C语言 代码 · 共 766 行 · 第 1/2 页

C
766
字号
/*
 *  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: Break up ReadWritePassive and handle errors better
 * TODO: Figure out data rate issues
 * 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
 */

#define NDEBUG
#include <debug.h>
#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;
  UNREFERENCED_PARAMETER(DeviceObject);
  UNREFERENCED_PARAMETER(Irp);

  DPRINT("floppy: MapRegisterCallback Called\n");

  ControllerInfo->MapRegisterBase = MapRegisterBase;
  KeSetEvent(&ControllerInfo->SynchEvent, 0, FALSE);

  return KeepObject;
}


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
 */
{
  DPRINT("floppy: ReadWrite called\n");

  ASSERT(DeviceObject);
  ASSERT(Irp);

  if(!Irp->MdlAddress)
    {
      DPRINT("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();

  DPRINT("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
    {
      int i;

      /* Program data rate */
      if(HwSetDataRate(DriveInfo->ControllerInfo, DRSR_DSEL_500KBPS) != STATUS_SUCCESS)
	{
	  DPRINT("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)
	{
	  DPRINT("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 */
      for(i=0; i < 2; i++)
	{
	  NTSTATUS RecalStatus;

	  if(HwRecalibrate(DriveInfo) != STATUS_SUCCESS)
	    {
	      DPRINT("floppy: RWDetermineMediaType(): Recalibrate failed\n");
	      return STATUS_UNSUCCESSFUL;
	    }

	  /* Wait for the recalibrate to finish */
	  WaitForControllerInterrupt(DriveInfo->ControllerInfo);

	  RecalStatus = HwRecalibrateResult(DriveInfo->ControllerInfo);

	  if(RecalStatus == STATUS_SUCCESS)
	    break;

	  if(i == 1) /* failed for 2nd time */
	    {
	      DPRINT("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 */
	{
	  DPRINT("floppy: RWDetermineMediaType(): ReadId failed\n");
	  return STATUS_UNSUCCESSFUL; /* if we can't even write to the controller, it's hopeless */
	}

      /* Wait for the ReadID to finish */
      WaitForControllerInterrupt(DriveInfo->ControllerInfo);

      if(HwReadIdResult(DriveInfo->ControllerInfo, NULL, NULL) != STATUS_SUCCESS)
	{
	  DPRINT("floppy: RWDetermineMediaType(): ReadIdResult failed; continuing\n");
	  continue;
	}

      /* Found the media; populate the geometry now */
      DPRINT("FIXME: Hardcoded media type!\n");
      DPRINT("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);

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

  DPRINT("floppy: RWSeekToCylinder called drive 0x%x cylinder %d\n", DriveInfo, Cylinder);

  /* Clear any spurious interrupts */
  KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);

  /* queue seek command */
  if(HwSeek(DriveInfo, Cylinder) != STATUS_SUCCESS)
    {
      DPRINT("floppy: RWSeekToTrack(): unable to seek\n");
      return STATUS_UNSUCCESSFUL;
    }

  WaitForControllerInterrupt(DriveInfo->ControllerInfo);

  if(HwSenseInterruptStatus(DriveInfo->ControllerInfo) != STATUS_SUCCESS)
    {
      DPRINT("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)
    {
      DPRINT("floppy: RWSeekToTrack(): unable to queue ReadId\n");
      return STATUS_UNSUCCESSFUL;
    }

  WaitForControllerInterrupt(DriveInfo->ControllerInfo);

  if(HwReadIdResult(DriveInfo->ControllerInfo, &CurCylinder, NULL) != STATUS_SUCCESS)
    {
      DPRINT("floppy: RWSeekToTrack(): unable to get ReadId result\n");
      return STATUS_UNSUCCESSFUL;
    }

  if(CurCylinder != Cylinder)
    {
      DPRINT("floppy: RWSeekToTrack(): Seeek to track failed; current cylinder is 0x%x\n", CurCylinder);
      return STATUS_UNSUCCESSFUL;
    }

  DPRINT("floppy: RWSeekToCylinder: returning successfully, now on cyl %d\n", Cylinder);

  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!
 *     - This is really crummy code.  Please FIXME.
 */
{
  ULONG AbsoluteSector;
  UCHAR SectorsPerCylinder = (UCHAR)DriveInfo->DiskGeometry.SectorsPerTrack * (UCHAR)DriveInfo->DiskGeometry.TracksPerCylinder;

  DPRINT("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 is zero-based to make the math a little easier */
  AbsoluteSector = DiskByteOffset / DriveInfo->DiskGeometry.BytesPerSector;  /* Num full sectors */

  /* Cylinder number is floor(AbsoluteSector / SectorsPerCylinder) */
  *Cylinder =  (CHAR)(AbsoluteSector / SectorsPerCylinder);

  /* Head number is 0 if the sector within the cylinder < SectorsPerTrack; 1 otherwise */
  *Head =  AbsoluteSector % SectorsPerCylinder < DriveInfo->DiskGeometry.SectorsPerTrack ? 0 : 1;

  /*
   * 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!
   */

⌨️ 快捷键说明

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