mirror of
https://github.com/reactos/reactos.git
synced 2024-09-06 02:37:06 +00:00
1045 lines
30 KiB
C
1045 lines
30 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 "precomp.h"
|
|
|
|
#include <debug.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)
|
|
{
|
|
WARN_(FLOPPY, "HwRecalibrateResult: Seeked to track 0 successfully, but EC is set; returning failure\n");
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
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 controller 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));
|
|
}
|