reactos/drivers/storage/floppy/hardware.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));
}