2 * ReactOS Floppy Driver
3 * Copyright (C) 2004, Vizzini (vizzini@plasmic.com)
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 * PROJECT: ReactOS Floppy Driver
21 * PURPOSE: Read/Write handler routines
22 * PROGRAMMER: Vizzini (vizzini@plasmic.com)
24 * 15-Feb-2004 vizzini - Created
29 * This process is extracted from the Intel datasheet for the floppy controller.
31 * - Turn on the motor and set turnoff time
32 * - Program the drive's data rate
36 * - Send read/write command to FDC
39 * This is mostly implemented in one big function, which watis on the SynchEvent
40 * as many times as necessary to get through the process. See ReadWritePassive() for
44 * - Currently doesn't support partial-sector transfers, which is really just a failing
45 * of RWComputeCHS. I've never seen Windows send a partial-sector request, though, so
46 * this may not be a bad thing. Should be looked into, regardless.
48 * TODO: Break up ReadWritePassive and handle errors better
49 * TODO: Figure out data rate issues
50 * TODO: Media type detection
51 * TODO: Figure out perf issue - waiting after call to read/write for about a second each time
52 * TODO: Figure out specify timings
62 #include "readwrite.h"
65 static IO_ALLOCATION_ACTION NTAPI
MapRegisterCallback(PDEVICE_OBJECT DeviceObject
,
67 PVOID MapRegisterBase
,
70 * FUNCTION: Acquire map registers in prep for DMA
72 * DeviceObject: unused
74 * MapRegisterBase: returned to blocked thread via a member var
75 * Context: contains a pointer to the right ControllerInfo
78 * KeepObject, because that's what the DDK says to do
81 PCONTROLLER_INFO ControllerInfo
= (PCONTROLLER_INFO
)Context
;
82 UNREFERENCED_PARAMETER(DeviceObject
);
83 UNREFERENCED_PARAMETER(Irp
);
85 DPRINT("floppy: MapRegisterCallback Called\n");
87 ControllerInfo
->MapRegisterBase
= MapRegisterBase
;
88 KeSetEvent(&ControllerInfo
->SynchEvent
, 0, FALSE
);
94 NTSTATUS NTAPI
ReadWrite(PDEVICE_OBJECT DeviceObject
,
97 * FUNCTION: Dispatch routine called for read or write IRPs
100 * STATUS_PENDING if the IRP is queued
101 * STATUS_INVALID_PARAMETER if IRP is set up wrong
103 * - This function validates arguments to the IRP and then queues it
104 * - Note that this function is implicitly serialized by the queue logic. Only
105 * one of these at a time is active in the system, no matter how many processors
106 * and threads we have.
107 * - This function stores the DeviceObject in the IRP's context area before dropping
108 * it onto the irp queue
111 DPRINT("floppy: ReadWrite called\n");
113 ASSERT(DeviceObject
);
118 DPRINT("floppy: ReadWrite(): MDL not found in IRP - Completing with STATUS_INVALID_PARAMETER\n");
119 Irp
->IoStatus
.Status
= STATUS_INVALID_PARAMETER
;
120 Irp
->IoStatus
.Information
= 0;
121 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
122 return STATUS_INVALID_PARAMETER
;
126 * Queue the irp to the thread.
127 * The de-queue thread will look in DriverContext[0] for the Device Object.
129 Irp
->Tail
.Overlay
.DriverContext
[0] = DeviceObject
;
130 IoCsqInsertIrp(&Csq
, Irp
, NULL
);
132 return STATUS_PENDING
;
136 static VOID NTAPI
RWFreeAdapterChannel(PADAPTER_OBJECT AdapterObject
)
138 * FUNCTION: Free the adapter DMA channel that we allocated
140 * AdapterObject: the object with the map registers to free
142 * - This function is primarily needed because IoFreeAdapterChannel wants to
143 * be called at DISPATCH_LEVEL
148 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL
);
150 KeRaiseIrql(DISPATCH_LEVEL
, &Irql
);
151 IoFreeAdapterChannel(AdapterObject
);
156 static NTSTATUS NTAPI
RWDetermineMediaType(PDRIVE_INFO DriveInfo
)
158 * FUNCTION: Determine the media type of the disk in the drive and fill in the geometry
160 * DriveInfo: drive to look at
162 * STATUS_SUCCESS if the media was recognized and the geometry struct was filled in
163 * STATUS_UNRECOGNIZED_MEDIA if not
164 * STATUS_UNSUCCESSFUL if the controller can't be talked to
166 * - Expects the motor to already be running
167 * - Currently only supports 1.44MB 3.5" disks
168 * - PAGED_CODE because it waits
170 * - Support more disk types
174 UCHAR HeadUnloadTime
;
179 DPRINT("floppy: RWDetermineMediaType called\n");
182 * This algorithm assumes that a 1.44MB floppy is in the drive. If it's not,
183 * it works backwards until the read works. Note that only 1.44 has been tested
191 /* Program data rate */
192 if(HwSetDataRate(DriveInfo
->ControllerInfo
, DRSR_DSEL_500KBPS
) != STATUS_SUCCESS
)
194 DPRINT("floppy: RWDetermineMediaType(): unable to set data rate\n");
195 return STATUS_UNSUCCESSFUL
;
199 HeadLoadTime
= SPECIFY_HLT_500K
;
200 HeadUnloadTime
= SPECIFY_HUT_500K
;
201 StepRateTime
= SPECIFY_SRT_500K
;
203 /* Don't disable DMA --> enable dma (dumb & confusing) */
204 if(HwSpecify(DriveInfo
->ControllerInfo
, HeadLoadTime
, HeadUnloadTime
, StepRateTime
, FALSE
) != STATUS_SUCCESS
)
206 DPRINT("floppy: RWDetermineMediaType(): specify failed\n");
207 return STATUS_UNSUCCESSFUL
;
210 /* clear any spurious interrupts in preparation for recalibrate */
211 KeClearEvent(&DriveInfo
->ControllerInfo
->SynchEvent
);
213 /* Recalibrate --> head over first track */
216 NTSTATUS RecalStatus
;
218 if(HwRecalibrate(DriveInfo
) != STATUS_SUCCESS
)
220 DPRINT("floppy: RWDetermineMediaType(): Recalibrate failed\n");
221 return STATUS_UNSUCCESSFUL
;
224 /* Wait for the recalibrate to finish */
225 WaitForControllerInterrupt(DriveInfo
->ControllerInfo
);
227 RecalStatus
= HwRecalibrateResult(DriveInfo
->ControllerInfo
);
229 if(RecalStatus
== STATUS_SUCCESS
)
232 if(i
== 1) /* failed for 2nd time */
234 DPRINT("floppy: RWDetermineMediaType(): RecalibrateResult failed\n");
235 return STATUS_UNSUCCESSFUL
;
239 /* clear any spurious interrupts */
240 KeClearEvent(&DriveInfo
->ControllerInfo
->SynchEvent
);
242 /* Try to read an ID */
243 if(HwReadId(DriveInfo
, 0) != STATUS_SUCCESS
) /* read the first ID we find, from head 0 */
245 DPRINT("floppy: RWDetermineMediaType(): ReadId failed\n");
246 return STATUS_UNSUCCESSFUL
; /* if we can't even write to the controller, it's hopeless */
249 /* Wait for the ReadID to finish */
250 WaitForControllerInterrupt(DriveInfo
->ControllerInfo
);
252 if(HwReadIdResult(DriveInfo
->ControllerInfo
, NULL
, NULL
) != STATUS_SUCCESS
)
254 DPRINT("floppy: RWDetermineMediaType(): ReadIdResult failed; continuing\n");
258 /* Found the media; populate the geometry now */
259 DPRINT("FIXME: Hardcoded media type!\n");
260 DPRINT("floppy: RWDetermineMediaType(): Found 1.44 media; returning success\n");
261 DriveInfo
->DiskGeometry
.MediaType
= GEOMETRY_144_MEDIATYPE
;
262 DriveInfo
->DiskGeometry
.Cylinders
.QuadPart
= GEOMETRY_144_CYLINDERS
;
263 DriveInfo
->DiskGeometry
.TracksPerCylinder
= GEOMETRY_144_TRACKSPERCYLINDER
;
264 DriveInfo
->DiskGeometry
.SectorsPerTrack
= GEOMETRY_144_SECTORSPERTRACK
;
265 DriveInfo
->DiskGeometry
.BytesPerSector
= GEOMETRY_144_BYTESPERSECTOR
;
266 DriveInfo
->BytesPerSectorCode
= HW_512_BYTES_PER_SECTOR
;
267 return STATUS_SUCCESS
;
271 DPRINT("floppy: RWDetermineMediaType(): failed to find media\n");
272 return STATUS_UNRECOGNIZED_MEDIA
;
276 static NTSTATUS NTAPI
RWSeekToCylinder(PDRIVE_INFO DriveInfo
,
279 * FUNCTION: Seek a particular drive to a particular track
281 * DriveInfo: Drive to seek
282 * Cylinder: track to seek to
284 * STATUS_SUCCESS if the head was successfully seeked
285 * STATUS_UNSUCCESSFUL if not
287 * - PAGED_CODE because it blocks
294 DPRINT("floppy: RWSeekToCylinder called drive 0x%x cylinder %d\n", DriveInfo
, Cylinder
);
296 /* Clear any spurious interrupts */
297 KeClearEvent(&DriveInfo
->ControllerInfo
->SynchEvent
);
299 /* queue seek command */
300 if(HwSeek(DriveInfo
, Cylinder
) != STATUS_SUCCESS
)
302 DPRINT("floppy: RWSeekToTrack(): unable to seek\n");
303 return STATUS_UNSUCCESSFUL
;
306 WaitForControllerInterrupt(DriveInfo
->ControllerInfo
);
308 if(HwSenseInterruptStatus(DriveInfo
->ControllerInfo
) != STATUS_SUCCESS
)
310 DPRINT("floppy: RWSeekToTrack(): unable to get seek results\n");
311 return STATUS_UNSUCCESSFUL
;
314 /* read ID mark from head 0 to verify */
315 if(HwReadId(DriveInfo
, 0) != STATUS_SUCCESS
)
317 DPRINT("floppy: RWSeekToTrack(): unable to queue ReadId\n");
318 return STATUS_UNSUCCESSFUL
;
321 WaitForControllerInterrupt(DriveInfo
->ControllerInfo
);
323 if(HwReadIdResult(DriveInfo
->ControllerInfo
, &CurCylinder
, NULL
) != STATUS_SUCCESS
)
325 DPRINT("floppy: RWSeekToTrack(): unable to get ReadId result\n");
326 return STATUS_UNSUCCESSFUL
;
329 if(CurCylinder
!= Cylinder
)
331 DPRINT("floppy: RWSeekToTrack(): Seeek to track failed; current cylinder is 0x%x\n", CurCylinder
);
332 return STATUS_UNSUCCESSFUL
;
335 DPRINT("floppy: RWSeekToCylinder: returning successfully, now on cyl %d\n", Cylinder
);
337 return STATUS_SUCCESS
;
341 static NTSTATUS NTAPI
RWComputeCHS(PDRIVE_INFO IN DriveInfo
,
342 ULONG IN DiskByteOffset
,
347 * FUNCTION: Compute the CHS from the absolute byte offset on disk
349 * DriveInfo: Drive to compute on
350 * DiskByteOffset: Absolute offset on disk of the starting byte
351 * Cylinder: Cylinder that the byte is on
352 * Head: Head that the byte is on
353 * Sector: Sector that the byte is on
355 * STATUS_SUCCESS if CHS are determined correctly
356 * STATUS_UNSUCCESSFUL otherwise
358 * - Lots of ugly typecasts here
359 * - Sectors are 1-based!
360 * - This is really crummy code. Please FIXME.
363 ULONG AbsoluteSector
;
364 UCHAR SectorsPerCylinder
= (UCHAR
)DriveInfo
->DiskGeometry
.SectorsPerTrack
* (UCHAR
)DriveInfo
->DiskGeometry
.TracksPerCylinder
;
366 DPRINT("floppy: RWComputeCHS: Called with offset 0x%x\n", DiskByteOffset
);
368 /* First calculate the 1-based "absolute sector" based on the byte offset */
369 ASSERT(!(DiskByteOffset
% DriveInfo
->DiskGeometry
.BytesPerSector
)); /* FIXME: Only handle full sector transfers atm */
371 /* AbsoluteSector is zero-based to make the math a little easier */
372 AbsoluteSector
= DiskByteOffset
/ DriveInfo
->DiskGeometry
.BytesPerSector
; /* Num full sectors */
374 /* Cylinder number is floor(AbsoluteSector / SectorsPerCylinder) */
375 *Cylinder
= (CHAR
)(AbsoluteSector
/ SectorsPerCylinder
);
377 /* Head number is 0 if the sector within the cylinder < SectorsPerTrack; 1 otherwise */
378 *Head
= AbsoluteSector
% SectorsPerCylinder
< DriveInfo
->DiskGeometry
.SectorsPerTrack
? 0 : 1;
381 * Sector number is the sector within the cylinder if on head 0; that minus SectorsPerTrack if it's on head 1
382 * (lots of casts to placate msvc). 1-based!
384 *Sector
= ((UCHAR
)(AbsoluteSector
% SectorsPerCylinder
) + 1) - ((*Head
) * (UCHAR
)DriveInfo
->DiskGeometry
.SectorsPerTrack
);
386 DPRINT("floppy: RWComputeCHS: offset 0x%x is c:0x%x h:0x%x s:0x%x\n", DiskByteOffset
, *Cylinder
, *Head
, *Sector
);
388 /* Sanity checking */
389 ASSERT(*Cylinder
<= DriveInfo
->DiskGeometry
.Cylinders
.QuadPart
);
390 ASSERT(*Head
<= DriveInfo
->DiskGeometry
.TracksPerCylinder
);
391 ASSERT(*Sector
<= DriveInfo
->DiskGeometry
.SectorsPerTrack
);
393 return STATUS_SUCCESS
;
397 VOID NTAPI
ReadWritePassive(PDRIVE_INFO DriveInfo
,
400 * FUNCTION: Handle the first phase of a read or write IRP
402 * DeviceObject: DeviceObject that is the target of the IRP
403 * Irp: IRP to process
405 * STATUS_VERIFY_REQUIRED if the media has changed and we need the filesystems to re-synch
406 * STATUS_SUCCESS otherwise
408 * - Must be called at PASSIVE_LEVEL
409 * - This function is about 250 lines longer than I wanted it to be. Sorry.
412 * This routine manages the whole process of servicing a read or write request. It goes like this:
413 * 1) Check the DO_VERIFY_VOLUME flag and return if it's set
414 * 2) Check the disk change line and notify the OS if it's set and return
415 * 3) Detect the media if we haven't already
416 * 4) Set up DiskByteOffset, Length, and WriteToDevice parameters
417 * 5) Get DMA map registers
418 * 6) Then, in a loop for each track, until all bytes are transferred:
419 * a) Compute the current CHS to set the read/write head to
420 * b) Seek to that spot
421 * c) Compute the last sector to transfer on that track
422 * d) Map the transfer through DMA
423 * e) Send the read or write command to the controller
424 * f) Read the results of the command
427 PDEVICE_OBJECT DeviceObject
= DriveInfo
->DeviceObject
;
428 PIO_STACK_LOCATION Stack
= IoGetCurrentIrpStackLocation(Irp
);
429 BOOLEAN WriteToDevice
;
431 ULONG DiskByteOffset
;
435 ULONG_PTR TransferByteOffset
;
440 DPRINT("floppy: ReadWritePassive called to %s 0x%x bytes from offset 0x%x\n",
441 (Stack
->MajorFunction
== IRP_MJ_READ
? "read" : "write"),
442 (Stack
->MajorFunction
== IRP_MJ_READ
? Stack
->Parameters
.Read
.Length
: Stack
->Parameters
.Write
.Length
),
443 (Stack
->MajorFunction
== IRP_MJ_READ
? Stack
->Parameters
.Read
.ByteOffset
.u
.LowPart
:
444 Stack
->Parameters
.Write
.ByteOffset
.u
.LowPart
));
446 /* Default return codes */
447 Irp
->IoStatus
.Status
= STATUS_UNSUCCESSFUL
;
448 Irp
->IoStatus
.Information
= 0;
451 * Check to see if the volume needs to be verified. If so,
452 * we can get out of here quickly.
454 if(DeviceObject
->Flags
& DO_VERIFY_VOLUME
&& !(DeviceObject
->Flags
& SL_OVERRIDE_VERIFY_VOLUME
))
456 DPRINT("floppy: ReadWritePassive(): DO_VERIFY_VOLUME set; Completing with STATUS_VERIFY_REQUIRED\n");
457 Irp
->IoStatus
.Status
= STATUS_VERIFY_REQUIRED
;
458 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
463 * Check the change line, and if it's set, return
465 StartMotor(DriveInfo
);
466 if(HwDiskChanged(DeviceObject
->DeviceExtension
, &DiskChanged
) != STATUS_SUCCESS
)
468 DPRINT("floppy: ReadWritePassive(): unable to detect disk change; Completing with STATUS_UNSUCCESSFUL\n");
469 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
470 StopMotor(DriveInfo
->ControllerInfo
);
476 DPRINT("floppy: ReadWritePhase1(): signalling media changed; Completing with STATUS_MEDIA_CHANGED\n");
478 /* The following call sets IoStatus.Status and IoStatus.Information */
479 SignalMediaChanged(DeviceObject
, Irp
);
482 * Guessing at something... see ioctl.c for more info
484 if(ResetChangeFlag(DriveInfo
) == STATUS_NO_MEDIA_IN_DEVICE
)
485 Irp
->IoStatus
.Status
= STATUS_NO_MEDIA_IN_DEVICE
;
487 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
488 StopMotor(DriveInfo
->ControllerInfo
);
493 * Figure out the media type, if we don't know it already
495 if(DriveInfo
->DiskGeometry
.MediaType
== Unknown
)
497 if(RWDetermineMediaType(DriveInfo
) != STATUS_SUCCESS
)
499 DPRINT("floppy: ReadWritePassive(): unable to determine media type; completing with STATUS_UNSUCCESSFUL\n");
500 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
501 StopMotor(DriveInfo
->ControllerInfo
);
505 if(DriveInfo
->DiskGeometry
.MediaType
== Unknown
)
507 DPRINT("floppy: ReadWritePassive(): Unknown media in drive; completing with STATUS_UNRECOGNIZED_MEDIA\n");
508 Irp
->IoStatus
.Status
= STATUS_UNRECOGNIZED_MEDIA
;
509 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
510 StopMotor(DriveInfo
->ControllerInfo
);
515 /* Set up parameters for read or write */
516 if(Stack
->MajorFunction
== IRP_MJ_READ
)
518 Length
= Stack
->Parameters
.Read
.Length
;
519 DiskByteOffset
= Stack
->Parameters
.Read
.ByteOffset
.u
.LowPart
;
520 WriteToDevice
= FALSE
;
524 Length
= Stack
->Parameters
.Write
.Length
;
525 DiskByteOffset
= Stack
->Parameters
.Write
.ByteOffset
.u
.LowPart
;
526 WriteToDevice
= TRUE
;
531 * FloppyDeviceData.ReadWriteGapLength specify the value for the physical drive.
532 * We should set this value depend on the format of the inserted disk and possible
533 * depend on the request (read or write). A value of 0 results in one rotation
534 * between the sectors (7.2sec for reading a track).
536 Gap
= DriveInfo
->FloppyDeviceData
.ReadWriteGapLength
;
539 * Set up DMA transfer
541 * This is as good of a place as any to document something that used to confuse me
542 * greatly (and I even wrote some of the kernel's DMA code, so if it confuses me, it
543 * probably confuses at least a couple of other people too).
545 * MmGetMdlVirtualAddress() returns the virtal address, as mapped in the buffer's original
546 * process context, of the MDL. In other words: say you start with a buffer at address X, then
547 * you build an MDL out of that buffer called Mdl. If you call MmGetMdlVirtualAddress(Mdl), it
550 * There are two parameters that the function looks at to produce X again, given the MDL: the
551 * first is the StartVa, which is the base virtual address of the page that the buffer starts
552 * in. If your buffer's virtual address is 0x12345678, StartVa will be 0x12345000, assuming 4K pages
553 * (which is (almost) always the case on x86). Note well: this address is only valid in the
554 * process context that you initially built the MDL from. The physical pages that make up
555 * the MDL might perhaps be mapped in other process contexts too (or even in the system space,
556 * above 0x80000000 (default; 0xc0000000 on current ReactOS or /3GB Windows)), but it will
557 * (possibly) be mapped at a different address.
559 * The second parameter is the ByteOffset. Given an original buffer address of 0x12345678,
560 * the ByteOffset would be 0x678. Because MDLs can only describe full pages (and therefore
561 * StartVa always points to the start address of a page), the ByteOffset must be used to
562 * find the real start of the buffer.
564 * In general, if you add the StartVa and ByteOffset together, you get back your original
565 * buffer pointer, which you are free to use if you're sure you're in the right process
566 * context. You could tell by accessing the (hidden and not-to-be-used) Process member of
567 * the MDL, but in general, if you have to ask whether or not you are in the right context,
568 * then you shouldn't be using this address for anything anyway. There are also security implications
569 * (big ones, really, I wouldn't kid about this) to directly accessing a user's buffer by VA, so
572 * There is a somewhat weird but very common use of the virtual address associated with a MDL
573 * that pops up often in the context of DMA. DMA APIs (particularly MapTransfer()) need to
574 * know where the memory is that they should DMA into and out of. This memory is described
575 * by a MDL. The controller eventually needs to know a physical address on the host side,
576 * which is generally a 32-bit linear address (on x86), and not just a page address. Therefore,
577 * the DMA APIs look at the ByteOffset field of the MDL to reconstruct the real address that
578 * should be programmed into the DMA controller.
580 * It is often the case that a transfer needs to be broken down over more than one DMA operation,
581 * particularly when it is a big transfer and the HAL doesn't give you enough map registers
582 * to map the whole thing at once. Therefore, the APIs need a way to tell how far into the MDL
583 * they should look to transfer the next chunk of bytes. Now, Microsoft could have designed
584 * MapTransfer to take a "MDL offset" argument, starting with 0, for how far into the buffer to
585 * start, but it didn't. Instead, MapTransfer asks for the virtual address of the MDL as an "index" into
586 * the MDL. The way it computes how far into the page to start the transfer is by masking off all but
587 * the bottom 12 bits (on x86) of the number you supply as the CurrentVa and using *that* as the
588 * ByteOffset instead of the one in the MDL. (OK, this varies a bit by OS and version, but this
591 * In other words, you get a number back from MmGetMdlVirtualAddress that represents the start of your
592 * buffer, and you pass it to the first MapTransfer call. Then, for each successive operation
593 * on the same buffer, you increment that address to point to the next spot in the MDL that
594 * you want to DMA to/from. The fact that the virtual address you're manipulating is probably not
595 * mapped into the process context that you're running in is irrelevant, since it's only being
596 * used to index into the MDL.
599 /* Get map registers for DMA */
600 KeRaiseIrql(DISPATCH_LEVEL
, &OldIrql
);
601 Status
= IoAllocateAdapterChannel(DriveInfo
->ControllerInfo
->AdapterObject
, DeviceObject
,
602 DriveInfo
->ControllerInfo
->MapRegisters
, MapRegisterCallback
, DriveInfo
->ControllerInfo
);
603 KeLowerIrql(OldIrql
);
605 if(Status
!= STATUS_SUCCESS
)
607 DPRINT("floppy: ReadWritePassive(): unable allocate an adapter channel; completing with STATUS_UNSUCCESSFUL\n");
608 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
609 StopMotor(DriveInfo
->ControllerInfo
);
615 * Read from (or write to) the device
617 * This has to be called in a loop, as you can only transfer data to/from a single track at
620 TransferByteOffset
= 0;
621 while(TransferByteOffset
< Length
)
626 ULONG CurrentTransferBytes
;
627 UCHAR CurrentTransferSectors
;
629 DPRINT("floppy: ReadWritePassive(): iterating in while (TransferByteOffset = 0x%x of 0x%x total) - allocating %d registers\n",
630 TransferByteOffset
, Length
, DriveInfo
->ControllerInfo
->MapRegisters
);
632 KeClearEvent(&DriveInfo
->ControllerInfo
->SynchEvent
);
635 * Compute starting CHS
637 if(RWComputeCHS(DriveInfo
, DiskByteOffset
+TransferByteOffset
, &Cylinder
, &Head
, &StartSector
) != STATUS_SUCCESS
)
639 DPRINT("floppy: ReadWritePassive(): unable to compute CHS; completing with STATUS_UNSUCCESSFUL\n");
640 RWFreeAdapterChannel(DriveInfo
->ControllerInfo
->AdapterObject
);
641 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
642 StopMotor(DriveInfo
->ControllerInfo
);
647 * Seek to the right track
649 if(!DriveInfo
->ControllerInfo
->ImpliedSeeks
)
651 if(RWSeekToCylinder(DriveInfo
, Cylinder
) != STATUS_SUCCESS
)
653 DPRINT("floppy: ReadWritePassive(): unable to seek; completing with STATUS_UNSUCCESSFUL\n");
654 RWFreeAdapterChannel(DriveInfo
->ControllerInfo
->AdapterObject
);
655 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
656 StopMotor(DriveInfo
->ControllerInfo
);
662 * Compute last sector
664 * We can only ask for a transfer up to the end of the track. Then we have to re-seek and do more.
665 * TODO: Support the MT bit
667 DPRINT("floppy: ReadWritePassive(): computing number of sectors to transfer (StartSector 0x%x): ", StartSector
);
669 /* 1-based sector number */
670 if( (((DriveInfo
->DiskGeometry
.TracksPerCylinder
- Head
) * DriveInfo
->DiskGeometry
.SectorsPerTrack
- StartSector
) + 1 ) <
671 (Length
- TransferByteOffset
) / DriveInfo
->DiskGeometry
.BytesPerSector
)
673 CurrentTransferSectors
= (UCHAR
)((DriveInfo
->DiskGeometry
.TracksPerCylinder
- Head
) * DriveInfo
->DiskGeometry
.SectorsPerTrack
- StartSector
) + 1;
677 CurrentTransferSectors
= (UCHAR
)((Length
- TransferByteOffset
) / DriveInfo
->DiskGeometry
.BytesPerSector
);
680 DPRINT("0x%x\n", CurrentTransferSectors
);
682 CurrentTransferBytes
= CurrentTransferSectors
* DriveInfo
->DiskGeometry
.BytesPerSector
;
685 * Adjust to map registers
686 * BUG: Does this take into account page crossings?
688 DPRINT("floppy: ReadWritePassive(): Trying to transfer 0x%x bytes\n", CurrentTransferBytes
);
690 ASSERT(CurrentTransferBytes
);
692 if(BYTES_TO_PAGES(CurrentTransferBytes
) > DriveInfo
->ControllerInfo
->MapRegisters
)
694 CurrentTransferSectors
= (UCHAR
)((DriveInfo
->ControllerInfo
->MapRegisters
* PAGE_SIZE
) /
695 DriveInfo
->DiskGeometry
.BytesPerSector
);
697 CurrentTransferBytes
= CurrentTransferSectors
* DriveInfo
->DiskGeometry
.BytesPerSector
;
699 DPRINT("floppy: ReadWritePassive: limiting transfer to 0x%x bytes (0x%x sectors) due to map registers\n",
700 CurrentTransferBytes
, CurrentTransferSectors
);
703 /* set up this round's dma operation */
704 /* param 2 is ReadOperation --> opposite of WriteToDevice that IoMapTransfer takes. BAD MS. */
705 KeFlushIoBuffers(Irp
->MdlAddress
, !WriteToDevice
, TRUE
);
707 IoMapTransfer(DriveInfo
->ControllerInfo
->AdapterObject
, Irp
->MdlAddress
,
708 DriveInfo
->ControllerInfo
->MapRegisterBase
,
709 (PUCHAR
)((ULONG_PTR
)MmGetMdlVirtualAddress(Irp
->MdlAddress
) + TransferByteOffset
),
710 &CurrentTransferBytes
, WriteToDevice
);
715 KeClearEvent(&DriveInfo
->ControllerInfo
->SynchEvent
);
717 /* Issue the read/write command to the controller. Note that it expects the opposite of WriteToDevice. */
718 if(HwReadWriteData(DriveInfo
->ControllerInfo
, !WriteToDevice
, DriveInfo
->UnitNumber
, Cylinder
, Head
, StartSector
,
719 DriveInfo
->BytesPerSectorCode
, DriveInfo
->DiskGeometry
.SectorsPerTrack
, Gap
, 0xff) != STATUS_SUCCESS
)
721 DPRINT("floppy: ReadWritePassive(): HwReadWriteData returned failure; unable to read; completing with STATUS_UNSUCCESSFUL\n");
722 RWFreeAdapterChannel(DriveInfo
->ControllerInfo
->AdapterObject
);
723 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
724 StopMotor(DriveInfo
->ControllerInfo
);
728 DPRINT("floppy: ReadWritePassive(): HwReadWriteData returned -- waiting on event\n");
731 * At this point, we block and wait for an interrupt
732 * FIXME: this seems to take too long
734 WaitForControllerInterrupt(DriveInfo
->ControllerInfo
);
736 /* Read is complete; flush & free adapter channel */
737 IoFlushAdapterBuffers(DriveInfo
->ControllerInfo
->AdapterObject
, Irp
->MdlAddress
,
738 DriveInfo
->ControllerInfo
->MapRegisterBase
,
739 (PVOID
)((ULONG_PTR
)MmGetMdlVirtualAddress(Irp
->MdlAddress
) + TransferByteOffset
),
740 CurrentTransferBytes
, WriteToDevice
);
742 /* Read the results from the drive */
743 if(HwReadWriteResult(DriveInfo
->ControllerInfo
) != STATUS_SUCCESS
)
745 DPRINT("floppy: ReadWritePassive(): HwReadWriteResult returned failure; unable to read; completing with STATUS_UNSUCCESSFUL\n");
746 HwDumpRegisters(DriveInfo
->ControllerInfo
);
747 RWFreeAdapterChannel(DriveInfo
->ControllerInfo
->AdapterObject
);
748 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
749 StopMotor(DriveInfo
->ControllerInfo
);
753 TransferByteOffset
+= CurrentTransferBytes
;
756 RWFreeAdapterChannel(DriveInfo
->ControllerInfo
->AdapterObject
);
758 /* That's all folks! */
759 DPRINT("floppy: ReadWritePassive(): success; Completing with STATUS_SUCCESS\n");
760 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
761 Irp
->IoStatus
.Information
= Length
;
762 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
763 StopMotor(DriveInfo
->ControllerInfo
);