hardware.c

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

C
1,084
字号
/*
 *  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: 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
 */

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

  if(Status & MSR_IO_DIRECTION) /* 0 for out */
      return FALSE;

  if(!(Status & MSR_DATA_REG_READY_FOR_IO))
      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);

  if(!(Status & MSR_IO_DIRECTION)) /* Read = 1 */
      return FALSE;

  if(!(Status & MSR_DATA_REG_READY_FOR_IO))
      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
 * FIXME: time interval here and in Get_Byte
 */
{
  LARGE_INTEGER StartingTickCount;
  LARGE_INTEGER CurrentTickCount;
  PUCHAR Address;

  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;

	  if(TimeUnits > 25000000)
	    break;

          continue;
	}

      WRITE_PORT_UCHAR(Address, Byte);
      return STATUS_SUCCESS;
    }

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

	  if(TimeUnits > 25000000)
	    break;

          continue;
	}

      *Byte = READ_PORT_UCHAR(Address);

      return STATUS_SUCCESS;
    }

  DPRINT("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
 */
{
  DPRINT("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
 */
{
  DPRINT("floppy: HwTurnOffMotor: writing byte 0x%x to offset 0x%x\n", DOR_FDC_ENABLE|DOR_DMA_IO_INTERFACE_ENABLE, DIGITAL_OUTPUT_REGISTER);

  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
 *     - Currently cannot fail
 */
{
  PCONTROLLER_INFO ControllerInfo = DriveInfo->ControllerInfo;
  UCHAR Unit = DriveInfo->UnitNumber;
  UCHAR Buffer;

  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;

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

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

  Buffer[0] = COMMAND_SENSE_DRIVE_STATUS;

⌨️ 快捷键说明

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