mirror of
https://github.com/reactos/reactos.git
synced 2025-02-23 17:05:46 +00:00
1026 lines
29 KiB
C
1026 lines
29 KiB
C
/*
|
||
* 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.,
|
||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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.
|
||
*/
|
||
|
||
#include <ntddk.h>
|
||
#include <debug.h>
|
||
|
||
#include "floppy.h"
|
||
#include "hardware.h"
|
||
|
||
/*
|
||
* 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
|
||
* - 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
|
||
*/
|
||
{
|
||
int i;
|
||
|
||
PAGED_CODE();
|
||
|
||
for(i = 0; i < 5; i++)
|
||
{
|
||
if(ReadyForWrite(ControllerInfo))
|
||
break;
|
||
|
||
KeStallExecutionProcessor(50);
|
||
}
|
||
|
||
if (i < 5)
|
||
{
|
||
WRITE_PORT_UCHAR(ControllerInfo->BaseAddress + FIFO, Byte);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
else
|
||
{
|
||
INFO_(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
|
||
*/
|
||
{
|
||
int i;
|
||
|
||
PAGED_CODE();
|
||
|
||
for(i = 0; i < 5; i++)
|
||
{
|
||
if(ReadyForRead(ControllerInfo))
|
||
break;
|
||
|
||
KeStallExecutionProcessor(50);
|
||
}
|
||
|
||
if (i < 5)
|
||
{
|
||
*Byte = READ_PORT_UCHAR(ControllerInfo->BaseAddress + FIFO);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
else
|
||
{
|
||
INFO_(FLOPPY, "Get_Byte: timed out trying to write\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
|
||
*/
|
||
{
|
||
TRACE_(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
|
||
*/
|
||
{
|
||
TRACE_(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;
|
||
|
||
TRACE_(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();
|
||
|
||
TRACE_(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)
|
||
{
|
||
WARN_(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();
|
||
|
||
/* 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;
|
||
|
||
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++)
|
||
{
|
||
INFO_(FLOPPY, "HwReadWriteData: Sending a command byte to the FIFO: 0x%x\n", Buffer[i]);
|
||
|
||
if(Send_Byte(ControllerInfo, Buffer[i]) != STATUS_SUCCESS)
|
||
{
|
||
WARN_(FLOPPY, "HwReadWriteData: Unable to write to the FIFO\n");
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
}
|
||
|
||
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
|
||
* - perhaps handle more status
|
||
*/
|
||
{
|
||
UCHAR Buffer[2];
|
||
int i;
|
||
|
||
PAGED_CODE();
|
||
|
||
if(Send_Byte(ControllerInfo, COMMAND_SENSE_INTERRUPT_STATUS) != STATUS_SUCCESS)
|
||
{
|
||
WARN_(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)
|
||
{
|
||
WARN_(FLOPPY, "HwRecalibrateResult: unable to read FIFO\n");
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
/* Validate that it did what we told it to */
|
||
INFO_(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)
|
||
{
|
||
WARN_(FLOPPY, "HwRecalibrateResult: PCN not 0\n");
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
/* test seek complete */
|
||
if((Buffer[0] & SR0_SEEK_COMPLETE) != SR0_SEEK_COMPLETE)
|
||
{
|
||
WARN_(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)
|
||
INFO_(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:
|
||
* - perhaps handle more status
|
||
*/
|
||
{
|
||
UCHAR Buffer[7];
|
||
int i;
|
||
|
||
PAGED_CODE();
|
||
|
||
for(i = 0; i < 7; i++)
|
||
if(Get_Byte(ControllerInfo, &Buffer[i]) != STATUS_SUCCESS)
|
||
{
|
||
WARN_(FLOPPY, "HwReadWriteResult: unable to read fifo\n");
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
/* Validate that it did what we told it to */
|
||
INFO_(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;
|
||
|
||
TRACE_(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)
|
||
{
|
||
WARN_(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();
|
||
|
||
if(Send_Byte(ControllerInfo, COMMAND_SENSE_INTERRUPT_STATUS) != STATUS_SUCCESS)
|
||
{
|
||
WARN_(FLOPPY, "HwSenseInterruptStatus: failed to write controller\n");
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
for(i = 0; i < 2; i++)
|
||
{
|
||
if(Get_Byte(ControllerInfo, &Buffer[i]) != STATUS_SUCCESS)
|
||
{
|
||
WARN_(FLOPPY, "HwSenseInterruptStatus: failed to read controller\n");
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
}
|
||
|
||
INFO_(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;
|
||
|
||
TRACE_(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)
|
||
{
|
||
WARN_(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;
|
||
|
||
TRACE_(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)
|
||
{
|
||
WARN_(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;
|
||
|
||
TRACE_(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)
|
||
{
|
||
WARN_(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;
|
||
|
||
TRACE_(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)
|
||
{
|
||
WARN_(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)
|
||
{
|
||
WARN_(FLOPPY, "HwGetVersion: unable to write fifo\n");
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
if(Get_Byte(ControllerInfo, &Buffer) != STATUS_SUCCESS)
|
||
{
|
||
WARN_(FLOPPY, "HwGetVersion: unable to write fifo\n");
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
INFO_(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);
|
||
|
||
TRACE_(FLOPPY, "HwDiskChanged: read 0x%x from DIR\n", Buffer);
|
||
|
||
if(ControllerInfo->Model30)
|
||
{
|
||
if(!(Buffer & DIR_DISKETTE_CHANGE))
|
||
{
|
||
INFO_(FLOPPY, "HdDiskChanged - Model30 - returning TRUE\n");
|
||
*DiskChanged = TRUE;
|
||
}
|
||
else
|
||
{
|
||
INFO_(FLOPPY, "HdDiskChanged - Model30 - returning FALSE\n");
|
||
*DiskChanged = FALSE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if(Buffer & DIR_DISKETTE_CHANGE)
|
||
{
|
||
INFO_(FLOPPY, "HdDiskChanged - PS2 - returning TRUE\n");
|
||
*DiskChanged = TRUE;
|
||
}
|
||
else
|
||
{
|
||
INFO_(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)
|
||
{
|
||
WARN_(FLOPPY, "HwSenseDriveStatus: unable to read fifo\n");
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
TRACE_(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
|
||
* - perhaps handle 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)
|
||
{
|
||
WARN_(FLOPPY, "ReadIdResult(): can't read from the controller\n");
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
/* Validate that it did what we told it to */
|
||
INFO_(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)
|
||
{
|
||
WARN_(FLOPPY, "ReadId didn't return last command success\n");
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
/* ID mark found? */
|
||
if(Buffer[1] & SR1_CANNOT_FIND_ID_ADDRESS)
|
||
{
|
||
WARN_(FLOPPY, "ReadId didn't find an address mark\n");
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
if(CurCylinder)
|
||
*CurCylinder = Buffer[3];
|
||
|
||
if(CurHead)
|
||
*CurHead = Buffer[4];
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
NTSTATUS NTAPI HwSpecify(PCONTROLLER_INFO ControllerInfo,
|
||
UCHAR HeadLoadTime,
|
||
UCHAR HeadUnloadTime,
|
||
UCHAR StepRateTime,
|
||
BOOLEAN NonDma)
|
||
/*
|
||
* FUNCTION: Set up timing and DMA mode for the controller
|
||
* ARGUMENTS:
|
||
* ControllerInfo: Controller to set up
|
||
* HeadLoadTime: Head load time (see data sheet for details)
|
||
* HeadUnloadTime: Head unload time
|
||
* StepRateTime: Step rate time
|
||
* NonDma: TRUE to disable DMA mode
|
||
* RETURNS:
|
||
* STATUS_SUCCESS if the contrller is successfully programmed
|
||
* STATUS_UNSUCCESSFUL if not
|
||
* NOTES:
|
||
* - Does not interrupt
|
||
*
|
||
* TODO: Figure out timings
|
||
*/
|
||
{
|
||
UCHAR Buffer[3];
|
||
int i;
|
||
|
||
Buffer[0] = COMMAND_SPECIFY;
|
||
/*
|
||
Buffer[1] = (StepRateTime << 4) + HeadUnloadTime;
|
||
Buffer[2] = (HeadLoadTime << 1) + (NonDma ? 1 : 0);
|
||
*/
|
||
Buffer[1] = 0xdf;
|
||
Buffer[2] = 0x2;
|
||
|
||
//INFO_(FLOPPY, "HwSpecify: sending 0x%x 0x%x 0x%x to FIFO\n", Buffer[0], Buffer[1], Buffer[2]);
|
||
WARN_(FLOPPY, "HWSPECIFY: FIXME - sending 0x3 0xd1 0x2 to FIFO\n");
|
||
|
||
for(i = 0; i < 3; i++)
|
||
if(Send_Byte(ControllerInfo, Buffer[i]) != STATUS_SUCCESS)
|
||
{
|
||
WARN_(FLOPPY, "HwSpecify: unable to write to controller\n");
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
NTSTATUS NTAPI HwReset(PCONTROLLER_INFO ControllerInfo)
|
||
/*
|
||
* FUNCTION: Reset the controller
|
||
* ARGUMENTS:
|
||
* ControllerInfo: controller to reset
|
||
* RETURNS:
|
||
* STATUS_SUCCESS in all cases
|
||
* NOTES:
|
||
* - Generates an interrupt that must be serviced four times (one per drive)
|
||
*/
|
||
{
|
||
TRACE_(FLOPPY, "HwReset called\n");
|
||
|
||
/* Write the reset bit in the DRSR */
|
||
WRITE_PORT_UCHAR(ControllerInfo->BaseAddress + DATA_RATE_SELECT_REGISTER, DRSR_SW_RESET);
|
||
|
||
/* Check for the reset bit in the DOR and set it if necessary (see Intel doc) */
|
||
if(!(READ_PORT_UCHAR(ControllerInfo->BaseAddress + DIGITAL_OUTPUT_REGISTER) & DOR_RESET))
|
||
{
|
||
HwDumpRegisters(ControllerInfo);
|
||
INFO_(FLOPPY, "HwReset: Setting Enable bit\n");
|
||
WRITE_PORT_UCHAR(ControllerInfo->BaseAddress + DIGITAL_OUTPUT_REGISTER, DOR_DMA_IO_INTERFACE_ENABLE|DOR_RESET);
|
||
HwDumpRegisters(ControllerInfo);
|
||
|
||
if(!(READ_PORT_UCHAR(ControllerInfo->BaseAddress + DIGITAL_OUTPUT_REGISTER) & DOR_RESET))
|
||
{
|
||
WARN_(FLOPPY, "HwReset: failed to set the DOR enable bit!\n");
|
||
HwDumpRegisters(ControllerInfo);
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
NTSTATUS NTAPI HwPowerOff(PCONTROLLER_INFO ControllerInfo)
|
||
/*
|
||
* FUNCTION: Power down a controller
|
||
* ARGUMENTS:
|
||
* ControllerInfo: Controller to power down
|
||
* RETURNS:
|
||
* STATUS_SUCCESS
|
||
* NOTES:
|
||
* - Wake up with a hardware reset
|
||
*/
|
||
{
|
||
TRACE_(FLOPPY, "HwPowerOff called on controller 0x%p\n", ControllerInfo);
|
||
|
||
WRITE_PORT_UCHAR(ControllerInfo->BaseAddress + DATA_RATE_SELECT_REGISTER, DRSR_POWER_DOWN);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
VOID NTAPI HwDumpRegisters(PCONTROLLER_INFO ControllerInfo)
|
||
/*
|
||
* FUNCTION: Dump all readable registers from the floppy controller
|
||
* ARGUMENTS:
|
||
* ControllerInfo: Controller to dump registers from
|
||
*/
|
||
{
|
||
UNREFERENCED_PARAMETER(ControllerInfo);
|
||
|
||
INFO_(FLOPPY, "STATUS:\n");
|
||
INFO_(FLOPPY, "STATUS_REGISTER_A = 0x%x\n", READ_PORT_UCHAR(ControllerInfo->BaseAddress + STATUS_REGISTER_A));
|
||
INFO_(FLOPPY, "STATUS_REGISTER_B = 0x%x\n", READ_PORT_UCHAR(ControllerInfo->BaseAddress + STATUS_REGISTER_B));
|
||
INFO_(FLOPPY, "DIGITAL_OUTPUT_REGISTER = 0x%x\n", READ_PORT_UCHAR(ControllerInfo->BaseAddress + DIGITAL_OUTPUT_REGISTER));
|
||
INFO_(FLOPPY, "MAIN_STATUS_REGISTER =0x%x\n", READ_PORT_UCHAR(ControllerInfo->BaseAddress + MAIN_STATUS_REGISTER));
|
||
INFO_(FLOPPY, "DIGITAL_INPUT_REGISTER = 0x%x\n", READ_PORT_UCHAR(ControllerInfo->BaseAddress + DIGITAL_INPUT_REGISTER));
|
||
}
|
||
|