readwrite.c
来自「一个类似windows」· C语言 代码 · 共 766 行 · 第 1/2 页
C
766 行
/*
* 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: readwrite.c
* PURPOSE: Read/Write handler routines
* PROGRAMMER: Vizzini (vizzini@plasmic.com)
* REVISIONS:
* 15-Feb-2004 vizzini - Created
* NOTES:
*
* READ/WRITE PROCESS
*
* This process is extracted from the Intel datasheet for the floppy controller.
*
* - Turn on the motor and set turnoff time
* - Program the drive's data rate
* - Seek
* - Read ID
* - Set up DMA
* - Send read/write command to FDC
* - Read result bytes
*
* This is mostly implemented in one big function, which watis on the SynchEvent
* as many times as necessary to get through the process. See ReadWritePassive() for
* more details.
*
* NOTES:
* - Currently doesn't support partial-sector transfers, which is really just a failing
* of RWComputeCHS. I've never seen Windows send a partial-sector request, though, so
* this may not be a bad thing. Should be looked into, regardless.
*
* TODO: Break up ReadWritePassive and handle errors better
* TODO: Figure out data rate issues
* TODO: Media type detection
* TODO: Figure out perf issue - waiting after call to read/write for about a second each time
* TODO: Figure out specify timings
*/
#define NDEBUG
#include <debug.h>
#include <ntddk.h>
#include "floppy.h"
#include "csqrtns.h"
#include "hardware.h"
#include "readwrite.h"
static IO_ALLOCATION_ACTION NTAPI MapRegisterCallback(PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID MapRegisterBase,
PVOID Context)
/*
* FUNCTION: Acquire map registers in prep for DMA
* ARGUMENTS:
* DeviceObject: unused
* Irp: unused
* MapRegisterBase: returned to blocked thread via a member var
* Context: contains a pointer to the right ControllerInfo
* struct
* RETURNS:
* KeepObject, because that's what the DDK says to do
*/
{
PCONTROLLER_INFO ControllerInfo = (PCONTROLLER_INFO)Context;
UNREFERENCED_PARAMETER(DeviceObject);
UNREFERENCED_PARAMETER(Irp);
DPRINT("floppy: MapRegisterCallback Called\n");
ControllerInfo->MapRegisterBase = MapRegisterBase;
KeSetEvent(&ControllerInfo->SynchEvent, 0, FALSE);
return KeepObject;
}
NTSTATUS NTAPI ReadWrite(PDEVICE_OBJECT DeviceObject,
PIRP Irp)
/*
* FUNCTION: Dispatch routine called for read or write IRPs
* ARGUMENTS:
* RETURNS:
* STATUS_PENDING if the IRP is queued
* STATUS_INVALID_PARAMETER if IRP is set up wrong
* NOTES:
* - This function validates arguments to the IRP and then queues it
* - Note that this function is implicitly serialized by the queue logic. Only
* one of these at a time is active in the system, no matter how many processors
* and threads we have.
* - This function stores the DeviceObject in the IRP's context area before dropping
* it onto the irp queue
*/
{
DPRINT("floppy: ReadWrite called\n");
ASSERT(DeviceObject);
ASSERT(Irp);
if(!Irp->MdlAddress)
{
DPRINT("floppy: ReadWrite(): MDL not found in IRP - Completing with STATUS_INVALID_PARAMETER\n");
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INVALID_PARAMETER;
}
/*
* Queue the irp to the thread.
* The de-queue thread will look in DriverContext[0] for the Device Object.
*/
Irp->Tail.Overlay.DriverContext[0] = DeviceObject;
IoCsqInsertIrp(&Csq, Irp, NULL);
return STATUS_PENDING;
}
static VOID NTAPI RWFreeAdapterChannel(PADAPTER_OBJECT AdapterObject)
/*
* FUNCTION: Free the adapter DMA channel that we allocated
* ARGUMENTS:
* AdapterObject: the object with the map registers to free
* NOTES:
* - This function is primarily needed because IoFreeAdapterChannel wants to
* be called at DISPATCH_LEVEL
*/
{
KIRQL Irql;
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
KeRaiseIrql(DISPATCH_LEVEL, &Irql);
IoFreeAdapterChannel(AdapterObject);
KeLowerIrql(Irql);
}
static NTSTATUS NTAPI RWDetermineMediaType(PDRIVE_INFO DriveInfo)
/*
* FUNCTION: Determine the media type of the disk in the drive and fill in the geometry
* ARGUMENTS:
* DriveInfo: drive to look at
* RETURNS:
* STATUS_SUCCESS if the media was recognized and the geometry struct was filled in
* STATUS_UNRECOGNIZED_MEDIA if not
* STATUS_UNSUCCESSFUL if the controller can't be talked to
* NOTES:
* - Expects the motor to already be running
* - Currently only supports 1.44MB 3.5" disks
* - PAGED_CODE because it waits
* TODO:
* - Support more disk types
*/
{
UCHAR HeadLoadTime;
UCHAR HeadUnloadTime;
UCHAR StepRateTime;
PAGED_CODE();
DPRINT("floppy: RWDetermineMediaType called\n");
/*
* This algorithm assumes that a 1.44MB floppy is in the drive. If it's not,
* it works backwards until the read works. Note that only 1.44 has been tested
* at all.
*/
do
{
int i;
/* Program data rate */
if(HwSetDataRate(DriveInfo->ControllerInfo, DRSR_DSEL_500KBPS) != STATUS_SUCCESS)
{
DPRINT("floppy: RWDetermineMediaType(): unable to set data rate\n");
return STATUS_UNSUCCESSFUL;
}
/* Specify */
HeadLoadTime = SPECIFY_HLT_500K;
HeadUnloadTime = SPECIFY_HUT_500K;
StepRateTime = SPECIFY_SRT_500K;
/* Don't disable DMA --> enable dma (dumb & confusing) */
if(HwSpecify(DriveInfo->ControllerInfo, HeadLoadTime, HeadUnloadTime, StepRateTime, FALSE) != STATUS_SUCCESS)
{
DPRINT("floppy: RWDetermineMediaType(): specify failed\n");
return STATUS_UNSUCCESSFUL;
}
/* clear any spurious interrupts in preparation for recalibrate */
KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);
/* Recalibrate --> head over first track */
for(i=0; i < 2; i++)
{
NTSTATUS RecalStatus;
if(HwRecalibrate(DriveInfo) != STATUS_SUCCESS)
{
DPRINT("floppy: RWDetermineMediaType(): Recalibrate failed\n");
return STATUS_UNSUCCESSFUL;
}
/* Wait for the recalibrate to finish */
WaitForControllerInterrupt(DriveInfo->ControllerInfo);
RecalStatus = HwRecalibrateResult(DriveInfo->ControllerInfo);
if(RecalStatus == STATUS_SUCCESS)
break;
if(i == 1) /* failed for 2nd time */
{
DPRINT("floppy: RWDetermineMediaType(): RecalibrateResult failed\n");
return STATUS_UNSUCCESSFUL;
}
}
/* clear any spurious interrupts */
KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);
/* Try to read an ID */
if(HwReadId(DriveInfo, 0) != STATUS_SUCCESS) /* read the first ID we find, from head 0 */
{
DPRINT("floppy: RWDetermineMediaType(): ReadId failed\n");
return STATUS_UNSUCCESSFUL; /* if we can't even write to the controller, it's hopeless */
}
/* Wait for the ReadID to finish */
WaitForControllerInterrupt(DriveInfo->ControllerInfo);
if(HwReadIdResult(DriveInfo->ControllerInfo, NULL, NULL) != STATUS_SUCCESS)
{
DPRINT("floppy: RWDetermineMediaType(): ReadIdResult failed; continuing\n");
continue;
}
/* Found the media; populate the geometry now */
DPRINT("FIXME: Hardcoded media type!\n");
DPRINT("floppy: RWDetermineMediaType(): Found 1.44 media; returning success\n");
DriveInfo->DiskGeometry.MediaType = GEOMETRY_144_MEDIATYPE;
DriveInfo->DiskGeometry.Cylinders.QuadPart = GEOMETRY_144_CYLINDERS;
DriveInfo->DiskGeometry.TracksPerCylinder = GEOMETRY_144_TRACKSPERCYLINDER;
DriveInfo->DiskGeometry.SectorsPerTrack = GEOMETRY_144_SECTORSPERTRACK;
DriveInfo->DiskGeometry.BytesPerSector = GEOMETRY_144_BYTESPERSECTOR;
DriveInfo->BytesPerSectorCode = HW_512_BYTES_PER_SECTOR;
return STATUS_SUCCESS;
}
while(FALSE);
DPRINT("floppy: RWDetermineMediaType(): failed to find media\n");
return STATUS_UNRECOGNIZED_MEDIA;
}
static NTSTATUS NTAPI RWSeekToCylinder(PDRIVE_INFO DriveInfo,
UCHAR Cylinder)
/*
* FUNCTION: Seek a particular drive to a particular track
* ARGUMENTS:
* DriveInfo: Drive to seek
* Cylinder: track to seek to
* RETURNS:
* STATUS_SUCCESS if the head was successfully seeked
* STATUS_UNSUCCESSFUL if not
* NOTES:
* - PAGED_CODE because it blocks
*/
{
UCHAR CurCylinder;
PAGED_CODE();
DPRINT("floppy: RWSeekToCylinder called drive 0x%x cylinder %d\n", DriveInfo, Cylinder);
/* Clear any spurious interrupts */
KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);
/* queue seek command */
if(HwSeek(DriveInfo, Cylinder) != STATUS_SUCCESS)
{
DPRINT("floppy: RWSeekToTrack(): unable to seek\n");
return STATUS_UNSUCCESSFUL;
}
WaitForControllerInterrupt(DriveInfo->ControllerInfo);
if(HwSenseInterruptStatus(DriveInfo->ControllerInfo) != STATUS_SUCCESS)
{
DPRINT("floppy: RWSeekToTrack(): unable to get seek results\n");
return STATUS_UNSUCCESSFUL;
}
/* read ID mark from head 0 to verify */
if(HwReadId(DriveInfo, 0) != STATUS_SUCCESS)
{
DPRINT("floppy: RWSeekToTrack(): unable to queue ReadId\n");
return STATUS_UNSUCCESSFUL;
}
WaitForControllerInterrupt(DriveInfo->ControllerInfo);
if(HwReadIdResult(DriveInfo->ControllerInfo, &CurCylinder, NULL) != STATUS_SUCCESS)
{
DPRINT("floppy: RWSeekToTrack(): unable to get ReadId result\n");
return STATUS_UNSUCCESSFUL;
}
if(CurCylinder != Cylinder)
{
DPRINT("floppy: RWSeekToTrack(): Seeek to track failed; current cylinder is 0x%x\n", CurCylinder);
return STATUS_UNSUCCESSFUL;
}
DPRINT("floppy: RWSeekToCylinder: returning successfully, now on cyl %d\n", Cylinder);
return STATUS_SUCCESS;
}
static NTSTATUS NTAPI RWComputeCHS(PDRIVE_INFO IN DriveInfo,
ULONG IN DiskByteOffset,
PUCHAR OUT Cylinder,
PUCHAR OUT Head,
PUCHAR OUT Sector)
/*
* FUNCTION: Compute the CHS from the absolute byte offset on disk
* ARGUMENTS:
* DriveInfo: Drive to compute on
* DiskByteOffset: Absolute offset on disk of the starting byte
* Cylinder: Cylinder that the byte is on
* Head: Head that the byte is on
* Sector: Sector that the byte is on
* RETURNS:
* STATUS_SUCCESS if CHS are determined correctly
* STATUS_UNSUCCESSFUL otherwise
* NOTES:
* - Lots of ugly typecasts here
* - Sectors are 1-based!
* - This is really crummy code. Please FIXME.
*/
{
ULONG AbsoluteSector;
UCHAR SectorsPerCylinder = (UCHAR)DriveInfo->DiskGeometry.SectorsPerTrack * (UCHAR)DriveInfo->DiskGeometry.TracksPerCylinder;
DPRINT("floppy: RWComputeCHS: Called with offset 0x%x\n", DiskByteOffset);
/* First calculate the 1-based "absolute sector" based on the byte offset */
ASSERT(!(DiskByteOffset % DriveInfo->DiskGeometry.BytesPerSector)); /* FIXME: Only handle full sector transfers atm */
/* AbsoluteSector is zero-based to make the math a little easier */
AbsoluteSector = DiskByteOffset / DriveInfo->DiskGeometry.BytesPerSector; /* Num full sectors */
/* Cylinder number is floor(AbsoluteSector / SectorsPerCylinder) */
*Cylinder = (CHAR)(AbsoluteSector / SectorsPerCylinder);
/* Head number is 0 if the sector within the cylinder < SectorsPerTrack; 1 otherwise */
*Head = AbsoluteSector % SectorsPerCylinder < DriveInfo->DiskGeometry.SectorsPerTrack ? 0 : 1;
/*
* Sector number is the sector within the cylinder if on head 0; that minus SectorsPerTrack if it's on head 1
* (lots of casts to placate msvc). 1-based!
*/
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?