ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

readwrite.c
Go to the documentation of this file.
00001 /*
00002  *  ReactOS Floppy Driver
00003  *  Copyright (C) 2004, Vizzini (vizzini@plasmic.com)
00004  *
00005  *  This program is free software; you can redistribute it and/or modify
00006  *  it under the terms of the GNU General Public License as published by
00007  *  the Free Software Foundation; either version 2 of the License, or
00008  *  (at your option) any later version.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License along
00016  *  with this program; if not, write to the Free Software Foundation, Inc.,
00017  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
00018  *
00019  * PROJECT:         ReactOS Floppy Driver
00020  * FILE:            readwrite.c
00021  * PURPOSE:         Read/Write handler routines
00022  * PROGRAMMER:      Vizzini (vizzini@plasmic.com)
00023  * REVISIONS:
00024  *                  15-Feb-2004 vizzini - Created
00025  * NOTES:
00026  *
00027  * READ/WRITE PROCESS
00028  *
00029  * This process is extracted from the Intel datasheet for the floppy controller.
00030  *
00031  * - Turn on the motor and set turnoff time
00032  * - Program the drive's data rate
00033  * - Seek
00034  * - Read ID
00035  * - Set up DMA
00036  * - Send read/write command to FDC
00037  * - Read result bytes
00038  *
00039  * This is mostly implemented in one big function, which watis on the SynchEvent
00040  * as many times as necessary to get through the process.  See ReadWritePassive() for
00041  * more details.
00042  *
00043  * NOTES:
00044  *     - Currently doesn't support partial-sector transfers, which is really just a failing
00045  *       of RWComputeCHS.  I've never seen Windows send a partial-sector request, though, so
00046  *       this may not be a bad thing.  Should be looked into, regardless.
00047  *
00048  * TODO: Break up ReadWritePassive and handle errors better
00049  * TODO: Figure out data rate issues
00050  * TODO: Media type detection
00051  * TODO: Figure out perf issue - waiting after call to read/write for about a second each time
00052  * TODO: Figure out specify timings
00053  */
00054 
00055 #include "precomp.h"
00056 
00057 
00058 static IO_ALLOCATION_ACTION NTAPI
00059 MapRegisterCallback(PDEVICE_OBJECT DeviceObject,
00060                     PIRP Irp,
00061                     PVOID MapRegisterBase,
00062                     PVOID Context)
00063 /*
00064  * FUNCTION: Acquire map registers in prep for DMA
00065  * ARGUMENTS:
00066  *     DeviceObject: unused
00067  *     Irp: unused
00068  *     MapRegisterBase: returned to blocked thread via a member var
00069  *     Context: contains a pointer to the right ControllerInfo
00070  *     struct
00071  * RETURNS:
00072  *     KeepObject, because that's what the DDK says to do
00073  */
00074 {
00075     PCONTROLLER_INFO ControllerInfo = (PCONTROLLER_INFO)Context;
00076     UNREFERENCED_PARAMETER(DeviceObject);
00077     UNREFERENCED_PARAMETER(Irp);
00078 
00079     TRACE_(FLOPPY, "MapRegisterCallback Called\n");
00080 
00081     ControllerInfo->MapRegisterBase = MapRegisterBase;
00082     KeSetEvent(&ControllerInfo->SynchEvent, 0, FALSE);
00083 
00084     return KeepObject;
00085 }
00086 
00087 
00088 NTSTATUS NTAPI
00089 ReadWrite(PDEVICE_OBJECT DeviceObject, PIRP Irp)
00090 /*
00091  * FUNCTION: Dispatch routine called for read or write IRPs
00092  * ARGUMENTS:
00093  * RETURNS:
00094  *     STATUS_PENDING if the IRP is queued
00095  *     STATUS_INVALID_PARAMETER if IRP is set up wrong
00096  * NOTES:
00097  *     - This function validates arguments to the IRP and then queues it
00098  *     - Note that this function is implicitly serialized by the queue logic.  Only
00099  *       one of these at a time is active in the system, no matter how many processors
00100  *       and threads we have.
00101  *     - This function stores the DeviceObject in the IRP's context area before dropping
00102  *       it onto the irp queue
00103  */
00104 {
00105     TRACE_(FLOPPY, "ReadWrite called\n");
00106 
00107     ASSERT(DeviceObject);
00108     ASSERT(Irp);
00109 
00110     if(!Irp->MdlAddress)
00111     {
00112         WARN_(FLOPPY, "ReadWrite(): MDL not found in IRP - Completing with STATUS_INVALID_PARAMETER\n");
00113         Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
00114         Irp->IoStatus.Information = 0;
00115         IoCompleteRequest(Irp, IO_NO_INCREMENT);
00116         return STATUS_INVALID_PARAMETER;
00117     }
00118 
00119     /*
00120      * Queue the irp to the thread.
00121      * The de-queue thread will look in DriverContext[0] for the Device Object.
00122      */
00123     Irp->Tail.Overlay.DriverContext[0] = DeviceObject;
00124     IoCsqInsertIrp(&Csq, Irp, NULL);
00125 
00126     return STATUS_PENDING;
00127 }
00128 
00129 
00130 static VOID NTAPI
00131 RWFreeAdapterChannel(PADAPTER_OBJECT AdapterObject)
00132 /*
00133  * FUNCTION: Free the adapter DMA channel that we allocated
00134  * ARGUMENTS:
00135  *     AdapterObject: the object with the map registers to free
00136  * NOTES:
00137  *     - This function is primarily needed because IoFreeAdapterChannel wants to
00138  *       be called at DISPATCH_LEVEL
00139  */
00140 {
00141     KIRQL Irql;
00142 
00143     ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
00144 
00145     KeRaiseIrql(DISPATCH_LEVEL, &Irql);
00146     IoFreeAdapterChannel(AdapterObject);
00147     KeLowerIrql(Irql);
00148 }
00149 
00150 
00151 static NTSTATUS NTAPI
00152 RWDetermineMediaType(PDRIVE_INFO DriveInfo)
00153 /*
00154  * FUNCTION: Determine the media type of the disk in the drive and fill in the geometry
00155  * ARGUMENTS:
00156  *     DriveInfo: drive to look at
00157  * RETURNS:
00158  *     STATUS_SUCCESS if the media was recognized and the geometry struct was filled in
00159  *     STATUS_UNRECOGNIZED_MEDIA if not
00160  *     STATUS_UNSUCCESSFUL if the controller can't be talked to
00161  * NOTES:
00162  *     - Expects the motor to already be running
00163  *     - Currently only supports 1.44MB 3.5" disks
00164  *     - PAGED_CODE because it waits
00165  * TODO:
00166  *     - Support more disk types
00167  */
00168 {
00169     UCHAR HeadLoadTime;
00170     UCHAR HeadUnloadTime;
00171     UCHAR StepRateTime;
00172 
00173     PAGED_CODE();
00174 
00175     TRACE_(FLOPPY, "RWDetermineMediaType called\n");
00176 
00177     /*
00178      * This algorithm assumes that a 1.44MB floppy is in the drive.  If it's not,
00179      * it works backwards until the read works.  Note that only 1.44 has been tested
00180      * at all.
00181      */
00182 
00183     do
00184     {
00185         int i;
00186 
00187         /* Program data rate */
00188         if(HwSetDataRate(DriveInfo->ControllerInfo, DRSR_DSEL_500KBPS) != STATUS_SUCCESS)
00189         {
00190             WARN_(FLOPPY, "RWDetermineMediaType(): unable to set data rate\n");
00191             return STATUS_UNSUCCESSFUL;
00192         }
00193 
00194         /* Specify */
00195         HeadLoadTime = SPECIFY_HLT_500K;
00196         HeadUnloadTime = SPECIFY_HUT_500K;
00197         StepRateTime = SPECIFY_SRT_500K;
00198 
00199         /* Don't disable DMA --> enable dma (dumb & confusing) */
00200         if(HwSpecify(DriveInfo->ControllerInfo, HeadLoadTime, HeadUnloadTime, StepRateTime, FALSE) != STATUS_SUCCESS)
00201         {
00202             WARN_(FLOPPY, "RWDetermineMediaType(): specify failed\n");
00203             return STATUS_UNSUCCESSFUL;
00204         }
00205 
00206         /* clear any spurious interrupts in preparation for recalibrate */
00207         KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);
00208 
00209         /* Recalibrate --> head over first track */
00210         for(i=0; i < 2; i++)
00211         {
00212             NTSTATUS RecalStatus;
00213 
00214             if(HwRecalibrate(DriveInfo) != STATUS_SUCCESS)
00215             {
00216                 WARN_(FLOPPY, "RWDetermineMediaType(): Recalibrate failed\n");
00217                 return STATUS_UNSUCCESSFUL;
00218             }
00219 
00220             /* Wait for the recalibrate to finish */
00221             WaitForControllerInterrupt(DriveInfo->ControllerInfo);
00222 
00223             RecalStatus = HwRecalibrateResult(DriveInfo->ControllerInfo);
00224 
00225             if(RecalStatus == STATUS_SUCCESS)
00226                 break;
00227 
00228             if(i == 1) /* failed for 2nd time */
00229             {
00230                 WARN_(FLOPPY, "RWDetermineMediaType(): RecalibrateResult failed\n");
00231                 return STATUS_UNSUCCESSFUL;
00232             }
00233         }
00234 
00235         /* clear any spurious interrupts */
00236         KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);
00237 
00238         /* Try to read an ID */
00239         if(HwReadId(DriveInfo, 0) != STATUS_SUCCESS) /* read the first ID we find, from head 0 */
00240         {
00241             WARN_(FLOPPY, "RWDetermineMediaType(): ReadId failed\n");
00242             return STATUS_UNSUCCESSFUL; /* if we can't even write to the controller, it's hopeless */
00243         }
00244 
00245         /* Wait for the ReadID to finish */
00246         WaitForControllerInterrupt(DriveInfo->ControllerInfo);
00247 
00248         if(HwReadIdResult(DriveInfo->ControllerInfo, NULL, NULL) != STATUS_SUCCESS)
00249         {
00250             WARN_(FLOPPY, "RWDetermineMediaType(): ReadIdResult failed; continuing\n");
00251             continue;
00252         }
00253 
00254         /* Found the media; populate the geometry now */
00255         WARN_(FLOPPY, "Hardcoded media type!\n");
00256         INFO_(FLOPPY, "RWDetermineMediaType(): Found 1.44 media; returning success\n");
00257         DriveInfo->DiskGeometry.MediaType = GEOMETRY_144_MEDIATYPE;
00258         DriveInfo->DiskGeometry.Cylinders.QuadPart = GEOMETRY_144_CYLINDERS;
00259         DriveInfo->DiskGeometry.TracksPerCylinder = GEOMETRY_144_TRACKSPERCYLINDER;
00260         DriveInfo->DiskGeometry.SectorsPerTrack = GEOMETRY_144_SECTORSPERTRACK;
00261         DriveInfo->DiskGeometry.BytesPerSector = GEOMETRY_144_BYTESPERSECTOR;
00262         DriveInfo->BytesPerSectorCode = HW_512_BYTES_PER_SECTOR;
00263         return STATUS_SUCCESS;
00264     }
00265     while(FALSE);
00266 
00267     TRACE_(FLOPPY, "RWDetermineMediaType(): failed to find media\n");
00268     return STATUS_UNRECOGNIZED_MEDIA;
00269 }
00270 
00271 
00272 static NTSTATUS NTAPI
00273 RWSeekToCylinder(PDRIVE_INFO DriveInfo, UCHAR Cylinder)
00274 /*
00275  * FUNCTION: Seek a particular drive to a particular track
00276  * ARGUMENTS:
00277  *     DriveInfo: Drive to seek
00278  *     Cylinder: track to seek to
00279  * RETURNS:
00280  *     STATUS_SUCCESS if the head was successfully seeked
00281  *     STATUS_UNSUCCESSFUL if not
00282  * NOTES:
00283  *     - PAGED_CODE because it blocks
00284  */
00285 {
00286     UCHAR CurCylinder;
00287 
00288     PAGED_CODE();
00289 
00290     TRACE_(FLOPPY, "RWSeekToCylinder called drive 0x%p cylinder %d\n", DriveInfo, Cylinder);
00291 
00292     /* Clear any spurious interrupts */
00293     KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);
00294 
00295     /* queue seek command */
00296     if(HwSeek(DriveInfo, Cylinder) != STATUS_SUCCESS)
00297     {
00298         WARN_(FLOPPY, "RWSeekToTrack(): unable to seek\n");
00299         return STATUS_UNSUCCESSFUL;
00300     }
00301 
00302     WaitForControllerInterrupt(DriveInfo->ControllerInfo);
00303 
00304     if(HwSenseInterruptStatus(DriveInfo->ControllerInfo) != STATUS_SUCCESS)
00305     {
00306         WARN_(FLOPPY, "RWSeekToTrack(): unable to get seek results\n");
00307         return STATUS_UNSUCCESSFUL;
00308     }
00309 
00310     /* read ID mark from head 0 to verify */
00311     if(HwReadId(DriveInfo, 0) != STATUS_SUCCESS)
00312     {
00313         WARN_(FLOPPY, "RWSeekToTrack(): unable to queue ReadId\n");
00314         return STATUS_UNSUCCESSFUL;
00315     }
00316 
00317     WaitForControllerInterrupt(DriveInfo->ControllerInfo);
00318 
00319     if(HwReadIdResult(DriveInfo->ControllerInfo, &CurCylinder, NULL) != STATUS_SUCCESS)
00320     {
00321         WARN_(FLOPPY, "RWSeekToTrack(): unable to get ReadId result\n");
00322         return STATUS_UNSUCCESSFUL;
00323     }
00324 
00325     if(CurCylinder != Cylinder)
00326     {
00327         WARN_(FLOPPY, "RWSeekToTrack(): Seeek to track failed; current cylinder is 0x%x\n", CurCylinder);
00328         return STATUS_UNSUCCESSFUL;
00329     }
00330 
00331     INFO_(FLOPPY, "RWSeekToCylinder: returning successfully, now on cyl %d\n", Cylinder);
00332 
00333     return STATUS_SUCCESS;
00334 }
00335 
00336 
00337 static NTSTATUS NTAPI
00338 RWComputeCHS(PDRIVE_INFO IN  DriveInfo,
00339              ULONG       IN  DiskByteOffset,
00340              PUCHAR      OUT Cylinder,
00341              PUCHAR      OUT Head,
00342              PUCHAR      OUT Sector)
00343 /*
00344  * FUNCTION: Compute the CHS from the absolute byte offset on disk
00345  * ARGUMENTS:
00346  *     DriveInfo: Drive to compute on
00347  *     DiskByteOffset: Absolute offset on disk of the starting byte
00348  *     Cylinder: Cylinder that the byte is on
00349  *     Head: Head that the byte is on
00350  *     Sector: Sector that the byte is on
00351  * RETURNS:
00352  *     STATUS_SUCCESS if CHS are determined correctly
00353  *     STATUS_UNSUCCESSFUL otherwise
00354  * NOTES:
00355  *     - Lots of ugly typecasts here
00356  *     - Sectors are 1-based!
00357  *     - This is really crummy code.  Please FIXME.
00358  */
00359 {
00360     ULONG AbsoluteSector;
00361     UCHAR SectorsPerCylinder = (UCHAR)DriveInfo->DiskGeometry.SectorsPerTrack * (UCHAR)DriveInfo->DiskGeometry.TracksPerCylinder;
00362 
00363     TRACE_(FLOPPY, "RWComputeCHS: Called with offset 0x%x\n", DiskByteOffset);
00364 
00365     /* First calculate the 1-based "absolute sector" based on the byte offset */
00366     ASSERT(!(DiskByteOffset % DriveInfo->DiskGeometry.BytesPerSector));         /* FIXME: Only handle full sector transfers atm */
00367 
00368     /* AbsoluteSector is zero-based to make the math a little easier */
00369     AbsoluteSector = DiskByteOffset / DriveInfo->DiskGeometry.BytesPerSector;  /* Num full sectors */
00370 
00371     /* Cylinder number is floor(AbsoluteSector / SectorsPerCylinder) */
00372     *Cylinder =  (CHAR)(AbsoluteSector / SectorsPerCylinder);
00373 
00374     /* Head number is 0 if the sector within the cylinder < SectorsPerTrack; 1 otherwise */
00375     *Head =  AbsoluteSector % SectorsPerCylinder < DriveInfo->DiskGeometry.SectorsPerTrack ? 0 : 1;
00376 
00377     /*
00378      * Sector number is the sector within the cylinder if on head 0; that minus SectorsPerTrack if it's on head 1
00379      * (lots of casts to placate msvc).  1-based!
00380      */
00381     *Sector =  ((UCHAR)(AbsoluteSector % SectorsPerCylinder) + 1) - ((*Head) * (UCHAR)DriveInfo->DiskGeometry.SectorsPerTrack);
00382 
00383     INFO_(FLOPPY, "RWComputeCHS: offset 0x%x is c:0x%x h:0x%x s:0x%x\n", DiskByteOffset, *Cylinder, *Head, *Sector);
00384 
00385     /* Sanity checking */
00386     ASSERT(*Cylinder <= DriveInfo->DiskGeometry.Cylinders.QuadPart);
00387     ASSERT(*Head <= DriveInfo->DiskGeometry.TracksPerCylinder);
00388     ASSERT(*Sector <= DriveInfo->DiskGeometry.SectorsPerTrack);
00389 
00390     return STATUS_SUCCESS;
00391 }
00392 
00393 
00394 VOID NTAPI
00395 ReadWritePassive(PDRIVE_INFO DriveInfo, PIRP Irp)
00396 /*
00397  * FUNCTION: Handle the first phase of a read or write IRP
00398  * ARGUMENTS:
00399  *     DeviceObject: DeviceObject that is the target of the IRP
00400  *     Irp: IRP to process
00401  * RETURNS:
00402  *     STATUS_VERIFY_REQUIRED if the media has changed and we need the filesystems to re-synch
00403  *     STATUS_SUCCESS otherwise
00404  * NOTES:
00405  *     - Must be called at PASSIVE_LEVEL
00406  *     - This function is about 250 lines longer than I wanted it to be.  Sorry.
00407  *
00408  * DETAILS:
00409  *  This routine manages the whole process of servicing a read or write request.  It goes like this:
00410  *    1) Check the DO_VERIFY_VOLUME flag and return if it's set
00411  *    2) Check the disk change line and notify the OS if it's set and return
00412  *    3) Detect the media if we haven't already
00413  *    4) Set up DiskByteOffset, Length, and WriteToDevice parameters
00414  *    5) Get DMA map registers
00415  *    6) Then, in a loop for each track, until all bytes are transferred:
00416  *      a) Compute the current CHS to set the read/write head to
00417  *      b) Seek to that spot
00418  *      c) Compute the last sector to transfer on that track
00419  *      d) Map the transfer through DMA
00420  *      e) Send the read or write command to the controller
00421  *      f) Read the results of the command
00422  */
00423 {
00424     PDEVICE_OBJECT DeviceObject = DriveInfo->DeviceObject;
00425     PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
00426     BOOLEAN WriteToDevice;
00427     ULONG Length;
00428     ULONG DiskByteOffset;
00429     KIRQL OldIrql;
00430     NTSTATUS Status;
00431     BOOLEAN DiskChanged;
00432     ULONG_PTR TransferByteOffset;
00433     UCHAR Gap;
00434 
00435     PAGED_CODE();
00436 
00437     TRACE_(FLOPPY, "ReadWritePassive called to %s 0x%x bytes from offset 0x%x\n",
00438            (Stack->MajorFunction == IRP_MJ_READ ? "read" : "write"),
00439            (Stack->MajorFunction == IRP_MJ_READ ? Stack->Parameters.Read.Length : Stack->Parameters.Write.Length),
00440            (Stack->MajorFunction == IRP_MJ_READ ? Stack->Parameters.Read.ByteOffset.u.LowPart :
00441             Stack->Parameters.Write.ByteOffset.u.LowPart));
00442 
00443     /* Default return codes */
00444     Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
00445     Irp->IoStatus.Information = 0;
00446 
00447     /*
00448      * Check to see if the volume needs to be verified.  If so,
00449      * we can get out of here quickly.
00450      */
00451     if(DeviceObject->Flags & DO_VERIFY_VOLUME && !(Stack->Flags & SL_OVERRIDE_VERIFY_VOLUME))
00452     {
00453         INFO_(FLOPPY, "ReadWritePassive(): DO_VERIFY_VOLUME set; Completing with  STATUS_VERIFY_REQUIRED\n");
00454         Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
00455         IoCompleteRequest(Irp, IO_NO_INCREMENT);
00456         return;
00457     }
00458 
00459     /*
00460      * Check the change line, and if it's set, return
00461      */
00462     StartMotor(DriveInfo);
00463     if(HwDiskChanged(DeviceObject->DeviceExtension, &DiskChanged) != STATUS_SUCCESS)
00464     {
00465         WARN_(FLOPPY, "ReadWritePassive(): unable to detect disk change; Completing with STATUS_UNSUCCESSFUL\n");
00466         IoCompleteRequest(Irp, IO_NO_INCREMENT);
00467         StopMotor(DriveInfo->ControllerInfo);
00468         return;
00469     }
00470 
00471     if(DiskChanged)
00472     {
00473         INFO_(FLOPPY, "ReadWritePhase1(): signalling media changed; Completing with STATUS_MEDIA_CHANGED\n");
00474 
00475         /* The following call sets IoStatus.Status and IoStatus.Information */
00476         SignalMediaChanged(DeviceObject, Irp);
00477 
00478         /*
00479          * Guessing at something... see ioctl.c for more info
00480          */
00481         if(ResetChangeFlag(DriveInfo) == STATUS_NO_MEDIA_IN_DEVICE)
00482             Irp->IoStatus.Status = STATUS_NO_MEDIA_IN_DEVICE;
00483 
00484         IoCompleteRequest(Irp, IO_NO_INCREMENT);
00485         StopMotor(DriveInfo->ControllerInfo);
00486         return;
00487     }
00488 
00489     /*
00490      * Figure out the media type, if we don't know it already
00491      */
00492     if(DriveInfo->DiskGeometry.MediaType == Unknown)
00493     {
00494         if(RWDetermineMediaType(DriveInfo) != STATUS_SUCCESS)
00495         {
00496             WARN_(FLOPPY, "ReadWritePassive(): unable to determine media type; completing with STATUS_UNSUCCESSFUL\n");
00497             IoCompleteRequest(Irp, IO_NO_INCREMENT);
00498             StopMotor(DriveInfo->ControllerInfo);
00499             return;
00500         }
00501 
00502         if(DriveInfo->DiskGeometry.MediaType == Unknown)
00503         {
00504             WARN_(FLOPPY, "ReadWritePassive(): Unknown media in drive; completing with STATUS_UNRECOGNIZED_MEDIA\n");
00505             Irp->IoStatus.Status = STATUS_UNRECOGNIZED_MEDIA;
00506             IoCompleteRequest(Irp, IO_NO_INCREMENT);
00507             StopMotor(DriveInfo->ControllerInfo);
00508             return;
00509         }
00510     }
00511 
00512     /* Set up parameters for read or write */
00513     if(Stack->MajorFunction == IRP_MJ_READ)
00514     {
00515         Length = Stack->Parameters.Read.Length;
00516         DiskByteOffset = Stack->Parameters.Read.ByteOffset.u.LowPart;
00517         WriteToDevice = FALSE;
00518     }
00519     else
00520     {
00521         Length = Stack->Parameters.Write.Length;
00522         DiskByteOffset = Stack->Parameters.Write.ByteOffset.u.LowPart;
00523         WriteToDevice = TRUE;
00524     }
00525 
00526     /*
00527      * FIXME:
00528      *   FloppyDeviceData.ReadWriteGapLength specify the value for the physical drive.
00529      *   We should set this value depend on the format of the inserted disk and possible
00530      *   depend on the request (read or write). A value of 0 results in one rotation
00531      *   between the sectors (7.2sec for reading a track).
00532      */
00533     Gap = DriveInfo->FloppyDeviceData.ReadWriteGapLength;
00534 
00535     /*
00536      * Set up DMA transfer
00537      *
00538      * This is as good of a place as any to document something that used to confuse me
00539      * greatly (and I even wrote some of the kernel's DMA code, so if it confuses me, it
00540      * probably confuses at least a couple of other people too).
00541      *
00542      * MmGetMdlVirtualAddress() returns the virtal address, as mapped in the buffer's original
00543      * process context, of the MDL.  In other words:  say you start with a buffer at address X, then
00544      * you build an MDL out of that buffer called Mdl. If you call MmGetMdlVirtualAddress(Mdl), it
00545      * will return X.
00546      *
00547      * There are two parameters that the function looks at to produce X again, given the MDL:  the
00548      * first is the StartVa, which is the base virtual address of the page that the buffer starts
00549      * in.  If your buffer's virtual address is 0x12345678, StartVa will be 0x12345000, assuming 4K pages
00550      * (which is (almost) always the case on x86).  Note well: this address is only valid in the
00551      * process context that you initially built the MDL from.  The physical pages that make up
00552      * the MDL might perhaps be mapped in other process contexts too (or even in the system space,
00553      * above 0x80000000 (default; 0xc0000000 on current ReactOS or /3GB Windows)), but it will
00554      * (possibly) be mapped at a different address.
00555      *
00556      * The second parameter is the ByteOffset.  Given an original buffer address of 0x12345678,
00557      * the ByteOffset would be 0x678.  Because MDLs can only describe full pages (and therefore
00558      * StartVa always points to the start address of a page), the ByteOffset must be used to
00559      * find the real start of the buffer.
00560      *
00561      * In general, if you add the StartVa and ByteOffset together, you get back your original
00562      * buffer pointer, which you are free to use if you're sure you're in the right process
00563      * context.  You could tell by accessing the (hidden and not-to-be-used) Process member of
00564      * the MDL, but in general, if you have to ask whether or not you are in the right context,
00565      * then you shouldn't be using this address for anything anyway.  There are also security implications
00566      * (big ones, really, I wouldn't kid about this) to directly accessing a user's buffer by VA, so
00567      * Don't Do That.
00568      *
00569      * There is a somewhat weird but very common use of the virtual address associated with a MDL
00570      * that pops up often in the context of DMA.  DMA APIs (particularly MapTransfer()) need to
00571      * know where the memory is that they should DMA into and out of.  This memory is described
00572      * by a MDL.  The controller eventually needs to know a physical address on the host side,
00573      * which is generally a 32-bit linear address (on x86), and not just a page address.  Therefore,
00574      * the DMA APIs look at the ByteOffset field of the MDL to reconstruct the real address that
00575      * should be programmed into the DMA controller.
00576      *
00577      * It is often the case that a transfer needs to be broken down over more than one DMA operation,
00578      * particularly when it is a big transfer and the HAL doesn't give you enough map registers
00579      * to map the whole thing at once.  Therefore, the APIs need a way to tell how far into the MDL
00580      * they should look to transfer the next chunk of bytes.  Now, Microsoft could have designed
00581      * MapTransfer to take a  "MDL offset" argument, starting with 0, for how far into the buffer to
00582      * start, but it didn't.  Instead, MapTransfer asks for the virtual address of the MDL as an "index" into
00583      * the MDL.  The way it computes how far into the page to start the transfer is by masking off all but
00584      * the bottom 12 bits (on x86) of the number you supply as the CurrentVa and using *that* as the
00585      * ByteOffset instead of the one in the MDL.  (OK, this varies a bit by OS and version, but this
00586      * is the effect).
00587      *
00588      * In other words, you get a number back from MmGetMdlVirtualAddress that represents the start of your
00589      * buffer, and you pass it to the first MapTransfer call.  Then, for each successive operation
00590      * on the same buffer, you increment that address to point to the next spot in the MDL that
00591      * you want to DMA to/from.  The fact that the virtual address you're manipulating is probably not
00592      * mapped into the process context that you're running in is irrelevant, since it's only being
00593      * used to index into the MDL.
00594      */
00595 
00596     /* Get map registers for DMA */
00597     KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
00598     Status = IoAllocateAdapterChannel(DriveInfo->ControllerInfo->AdapterObject, DeviceObject,
00599                                       DriveInfo->ControllerInfo->MapRegisters, MapRegisterCallback, DriveInfo->ControllerInfo);
00600     KeLowerIrql(OldIrql);
00601 
00602     if(Status != STATUS_SUCCESS)
00603     {
00604         WARN_(FLOPPY, "ReadWritePassive(): unable allocate an adapter channel; completing with STATUS_UNSUCCESSFUL\n");
00605         IoCompleteRequest(Irp, IO_NO_INCREMENT);
00606         StopMotor(DriveInfo->ControllerInfo);
00607         return ;
00608     }
00609 
00610 
00611     /*
00612      * Read from (or write to) the device
00613      *
00614      * This has to be called in a loop, as you can only transfer data to/from a single track at
00615      * a time.
00616      */
00617     TransferByteOffset = 0;
00618     while(TransferByteOffset < Length)
00619     {
00620         UCHAR Cylinder;
00621         UCHAR Head;
00622         UCHAR StartSector;
00623         ULONG CurrentTransferBytes;
00624         UCHAR CurrentTransferSectors;
00625 
00626         INFO_(FLOPPY, "ReadWritePassive(): iterating in while (TransferByteOffset = 0x%x of 0x%x total) - allocating %d registers\n",
00627               TransferByteOffset, Length, DriveInfo->ControllerInfo->MapRegisters);
00628 
00629         KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);
00630 
00631         /*
00632          * Compute starting CHS
00633          */
00634         if(RWComputeCHS(DriveInfo, DiskByteOffset+TransferByteOffset, &Cylinder, &Head, &StartSector) != STATUS_SUCCESS)
00635         {
00636             WARN_(FLOPPY, "ReadWritePassive(): unable to compute CHS; completing with STATUS_UNSUCCESSFUL\n");
00637             RWFreeAdapterChannel(DriveInfo->ControllerInfo->AdapterObject);
00638             IoCompleteRequest(Irp, IO_NO_INCREMENT);
00639             StopMotor(DriveInfo->ControllerInfo);
00640             return;
00641         }
00642 
00643         /*
00644          * Seek to the right track
00645          */
00646         if(!DriveInfo->ControllerInfo->ImpliedSeeks)
00647         {
00648             if(RWSeekToCylinder(DriveInfo, Cylinder) != STATUS_SUCCESS)
00649             {
00650                 WARN_(FLOPPY, "ReadWritePassive(): unable to seek; completing with STATUS_UNSUCCESSFUL\n");
00651                 RWFreeAdapterChannel(DriveInfo->ControllerInfo->AdapterObject);
00652                 IoCompleteRequest(Irp, IO_NO_INCREMENT);
00653                 StopMotor(DriveInfo->ControllerInfo);
00654                 return ;
00655             }
00656         }
00657 
00658         /*
00659          * Compute last sector
00660          *
00661          * We can only ask for a transfer up to the end of the track.  Then we have to re-seek and do more.
00662          * TODO: Support the MT bit
00663          */
00664         INFO_(FLOPPY, "ReadWritePassive(): computing number of sectors to transfer (StartSector 0x%x): ", StartSector);
00665 
00666         /* 1-based sector number */
00667         if( (((DriveInfo->DiskGeometry.TracksPerCylinder - Head) * DriveInfo->DiskGeometry.SectorsPerTrack - StartSector) + 1 ) <
00668                 (Length - TransferByteOffset) / DriveInfo->DiskGeometry.BytesPerSector)
00669         {
00670             CurrentTransferSectors = (UCHAR)((DriveInfo->DiskGeometry.TracksPerCylinder - Head) * DriveInfo->DiskGeometry.SectorsPerTrack - StartSector) + 1;
00671         }
00672         else
00673         {
00674             CurrentTransferSectors = (UCHAR)((Length - TransferByteOffset) / DriveInfo->DiskGeometry.BytesPerSector);
00675         }
00676 
00677         INFO_(FLOPPY, "0x%x\n", CurrentTransferSectors);
00678 
00679         CurrentTransferBytes = CurrentTransferSectors * DriveInfo->DiskGeometry.BytesPerSector;
00680 
00681         /*
00682          * Adjust to map registers
00683          * BUG: Does this take into account page crossings?
00684          */
00685         INFO_(FLOPPY, "ReadWritePassive(): Trying to transfer 0x%x bytes\n", CurrentTransferBytes);
00686 
00687         ASSERT(CurrentTransferBytes);
00688 
00689         if(BYTES_TO_PAGES(CurrentTransferBytes) > DriveInfo->ControllerInfo->MapRegisters)
00690         {
00691             CurrentTransferSectors = (UCHAR)((DriveInfo->ControllerInfo->MapRegisters * PAGE_SIZE) /
00692                                              DriveInfo->DiskGeometry.BytesPerSector);
00693 
00694             CurrentTransferBytes = CurrentTransferSectors * DriveInfo->DiskGeometry.BytesPerSector;
00695 
00696             INFO_(FLOPPY, "ReadWritePassive: limiting transfer to 0x%x bytes (0x%x sectors) due to map registers\n",
00697                   CurrentTransferBytes, CurrentTransferSectors);
00698         }
00699 
00700         /* set up this round's dma operation */
00701         /* param 2 is ReadOperation --> opposite of WriteToDevice that IoMapTransfer takes.  BAD MS. */
00702         KeFlushIoBuffers(Irp->MdlAddress, !WriteToDevice, TRUE);
00703 
00704         IoMapTransfer(DriveInfo->ControllerInfo->AdapterObject, Irp->MdlAddress,
00705                       DriveInfo->ControllerInfo->MapRegisterBase,
00706                       (PVOID)((ULONG_PTR)MmGetMdlVirtualAddress(Irp->MdlAddress) + TransferByteOffset),
00707                       &CurrentTransferBytes, WriteToDevice);
00708 
00709         /*
00710          * Read or Write
00711          */
00712         KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);
00713 
00714         /* Issue the read/write command to the controller.  Note that it expects the opposite of WriteToDevice. */
00715         if(HwReadWriteData(DriveInfo->ControllerInfo, !WriteToDevice, DriveInfo->UnitNumber, Cylinder, Head, StartSector,
00716                            DriveInfo->BytesPerSectorCode, DriveInfo->DiskGeometry.SectorsPerTrack, Gap, 0xff) != STATUS_SUCCESS)
00717         {
00718             WARN_(FLOPPY, "ReadWritePassive(): HwReadWriteData returned failure; unable to read; completing with STATUS_UNSUCCESSFUL\n");
00719             RWFreeAdapterChannel(DriveInfo->ControllerInfo->AdapterObject);
00720             IoCompleteRequest(Irp, IO_NO_INCREMENT);
00721             StopMotor(DriveInfo->ControllerInfo);
00722             return ;
00723         }
00724 
00725         INFO_(FLOPPY, "ReadWritePassive(): HwReadWriteData returned -- waiting on event\n");
00726 
00727         /*
00728          * At this point, we block and wait for an interrupt
00729          * FIXME: this seems to take too long
00730          */
00731         WaitForControllerInterrupt(DriveInfo->ControllerInfo);
00732 
00733         /* Read is complete; flush & free adapter channel */
00734         IoFlushAdapterBuffers(DriveInfo->ControllerInfo->AdapterObject, Irp->MdlAddress,
00735                               DriveInfo->ControllerInfo->MapRegisterBase,
00736                               (PVOID)((ULONG_PTR)MmGetMdlVirtualAddress(Irp->MdlAddress) + TransferByteOffset),
00737                               CurrentTransferBytes, WriteToDevice);
00738 
00739         /* Read the results from the drive */
00740         if(HwReadWriteResult(DriveInfo->ControllerInfo) != STATUS_SUCCESS)
00741         {
00742             WARN_(FLOPPY, "ReadWritePassive(): HwReadWriteResult returned failure; unable to read; completing with STATUS_UNSUCCESSFUL\n");
00743             HwDumpRegisters(DriveInfo->ControllerInfo);
00744             RWFreeAdapterChannel(DriveInfo->ControllerInfo->AdapterObject);
00745             IoCompleteRequest(Irp, IO_NO_INCREMENT);
00746             StopMotor(DriveInfo->ControllerInfo);
00747             return ;
00748         }
00749 
00750         TransferByteOffset += CurrentTransferBytes;
00751     }
00752 
00753     RWFreeAdapterChannel(DriveInfo->ControllerInfo->AdapterObject);
00754 
00755     /* That's all folks! */
00756     INFO_(FLOPPY, "ReadWritePassive(): success; Completing with STATUS_SUCCESS\n");
00757     Irp->IoStatus.Status = STATUS_SUCCESS;
00758     Irp->IoStatus.Information = Length;
00759     IoCompleteRequest(Irp, IO_DISK_INCREMENT);
00760     StopMotor(DriveInfo->ControllerInfo);
00761 }
00762 

Generated on Sat May 26 2012 04:26:31 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.