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 + -
显示快捷键?