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
61 #include "readwrite.h"
64 static IO_ALLOCATION_ACTION NTAPI
MapRegisterCallback(PDEVICE_OBJECT DeviceObject
,
66 PVOID MapRegisterBase
,
69 * FUNCTION: Acquire map registers in prep for DMA
71 * DeviceObject: unused
73 * MapRegisterBase: returned to blocked thread via a member var
74 * Context: contains a pointer to the right ControllerInfo
77 * KeepObject, because that's what the DDK says to do
80 PCONTROLLER_INFO ControllerInfo
= (PCONTROLLER_INFO
)Context
;
81 UNREFERENCED_PARAMETER(DeviceObject
);
82 UNREFERENCED_PARAMETER(Irp
);
84 TRACE_(FLOPPY
, "MapRegisterCallback Called\n");
86 ControllerInfo
->MapRegisterBase
= MapRegisterBase
;
87 KeSetEvent(&ControllerInfo
->SynchEvent
, 0, FALSE
);
93 NTSTATUS NTAPI
ReadWrite(PDEVICE_OBJECT DeviceObject
,
96 * FUNCTION: Dispatch routine called for read or write IRPs
99 * STATUS_PENDING if the IRP is queued
100 * STATUS_INVALID_PARAMETER if IRP is set up wrong
102 * - This function validates arguments to the IRP and then queues it
103 * - Note that this function is implicitly serialized by the queue logic. Only
104 * one of these at a time is active in the system, no matter how many processors
105 * and threads we have.
106 * - This function stores the DeviceObject in the IRP's context area before dropping
107 * it onto the irp queue
110 TRACE_(FLOPPY
, "ReadWrite called\n");
112 ASSERT(DeviceObject
);
117 WARN_(FLOPPY
, "ReadWrite(): MDL not found in IRP - Completing with STATUS_INVALID_PARAMETER\n");
118 Irp
->IoStatus
.Status
= STATUS_INVALID_PARAMETER
;
119 Irp
->IoStatus
.Information
= 0;
120 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
121 return STATUS_INVALID_PARAMETER
;
125 * Queue the irp to the thread.
126 * The de-queue thread will look in DriverContext[0] for the Device Object.
128 Irp
->Tail
.Overlay
.DriverContext
[0] = DeviceObject
;
129 IoCsqInsertIrp(&Csq
, Irp
, NULL
);
131 return STATUS_PENDING
;
135 static VOID NTAPI
RWFreeAdapterChannel(PADAPTER_OBJECT AdapterObject
)
137 * FUNCTION: Free the adapter DMA channel that we allocated
139 * AdapterObject: the object with the map registers to free
141 * - This function is primarily needed because IoFreeAdapterChannel wants to
142 * be called at DISPATCH_LEVEL
147 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL
);
149 KeRaiseIrql(DISPATCH_LEVEL
, &Irql
);
150 IoFreeAdapterChannel(AdapterObject
);
155 static NTSTATUS NTAPI
RWDetermineMediaType(PDRIVE_INFO DriveInfo
)
157 * FUNCTION: Determine the media type of the disk in the drive and fill in the geometry
159 * DriveInfo: drive to look at
161 * STATUS_SUCCESS if the media was recognized and the geometry struct was filled in
162 * STATUS_UNRECOGNIZED_MEDIA if not
163 * STATUS_UNSUCCESSFUL if the controller can't be talked to
165 * - Expects the motor to already be running
166 * - Currently only supports 1.44MB 3.5" disks
167 * - PAGED_CODE because it waits
169 * - Support more disk types
173 UCHAR HeadUnloadTime
;
178 TRACE_(FLOPPY
, "RWDetermineMediaType called\n");
181 * This algorithm assumes that a 1.44MB floppy is in the drive. If it's not,
182 * it works backwards until the read works. Note that only 1.44 has been tested
190 /* Program data rate */
191 if(HwSetDataRate(DriveInfo
->ControllerInfo
, DRSR_DSEL_500KBPS
) != STATUS_SUCCESS
)
193 WARN_(FLOPPY
, "RWDetermineMediaType(): unable to set data rate\n");
194 return STATUS_UNSUCCESSFUL
;
198 HeadLoadTime
= SPECIFY_HLT_500K
;
199 HeadUnloadTime
= SPECIFY_HUT_500K
;
200 StepRateTime
= SPECIFY_SRT_500K
;
202 /* Don't disable DMA --> enable dma (dumb & confusing) */
203 if(HwSpecify(DriveInfo
->ControllerInfo
, HeadLoadTime
, HeadUnloadTime
, StepRateTime
, FALSE
) != STATUS_SUCCESS
)
205 WARN_(FLOPPY
, "RWDetermineMediaType(): specify failed\n");
206 return STATUS_UNSUCCESSFUL
;
209 /* clear any spurious interrupts in preparation for recalibrate */
210 KeClearEvent(&DriveInfo
->ControllerInfo
->SynchEvent
);
212 /* Recalibrate --> head over first track */
215 NTSTATUS RecalStatus
;
217 if(HwRecalibrate(DriveInfo
) != STATUS_SUCCESS
)
219 WARN_(FLOPPY
, "RWDetermineMediaType(): Recalibrate failed\n");
220 return STATUS_UNSUCCESSFUL
;
223 /* Wait for the recalibrate to finish */
224 WaitForControllerInterrupt(DriveInfo
->ControllerInfo
);
226 RecalStatus
= HwRecalibrateResult(DriveInfo
->ControllerInfo
);
228 if(RecalStatus
== STATUS_SUCCESS
)
231 if(i
== 1) /* failed for 2nd time */
233 WARN_(FLOPPY
, "RWDetermineMediaType(): RecalibrateResult failed\n");
234 return STATUS_UNSUCCESSFUL
;
238 /* clear any spurious interrupts */
239 KeClearEvent(&DriveInfo
->ControllerInfo
->SynchEvent
);
241 /* Try to read an ID */
242 if(HwReadId(DriveInfo
, 0) != STATUS_SUCCESS
) /* read the first ID we find, from head 0 */
244 WARN_(FLOPPY
, "RWDetermineMediaType(): ReadId failed\n");
245 return STATUS_UNSUCCESSFUL
; /* if we can't even write to the controller, it's hopeless */
248 /* Wait for the ReadID to finish */
249 WaitForControllerInterrupt(DriveInfo
->ControllerInfo
);
251 if(HwReadIdResult(DriveInfo
->ControllerInfo
, NULL
, NULL
) != STATUS_SUCCESS
)
253 WARN_(FLOPPY
, "RWDetermineMediaType(): ReadIdResult failed; continuing\n");
257 /* Found the media; populate the geometry now */
258 WARN_(FLOPPY
, "Hardcoded media type!\n");
259 INFO_(FLOPPY
, "RWDetermineMediaType(): Found 1.44 media; returning success\n");
260 DriveInfo
->DiskGeometry
.MediaType
= GEOMETRY_144_MEDIATYPE
;
261 DriveInfo
->DiskGeometry
.Cylinders
.QuadPart
= GEOMETRY_144_CYLINDERS
;
262 DriveInfo
->DiskGeometry
.TracksPerCylinder
= GEOMETRY_144_TRACKSPERCYLINDER
;
263 DriveInfo
->DiskGeometry
.SectorsPerTrack
= GEOMETRY_144_SECTORSPERTRACK
;
264 DriveInfo
->DiskGeometry
.BytesPerSector
= GEOMETRY_144_BYTESPERSECTOR
;
265 DriveInfo
->BytesPerSectorCode
= HW_512_BYTES_PER_SECTOR
;
266 return STATUS_SUCCESS
;
270 TRACE_(FLOPPY
, "RWDetermineMediaType(): failed to find media\n");
271 return STATUS_UNRECOGNIZED_MEDIA
;
275 static NTSTATUS NTAPI
RWSeekToCylinder(PDRIVE_INFO DriveInfo
,
278 * FUNCTION: Seek a particular drive to a particular track
280 * DriveInfo: Drive to seek
281 * Cylinder: track to seek to
283 * STATUS_SUCCESS if the head was successfully seeked
284 * STATUS_UNSUCCESSFUL if not
286 * - PAGED_CODE because it blocks
293 TRACE_(FLOPPY
, "RWSeekToCylinder called drive 0x%p cylinder %d\n", DriveInfo
, Cylinder
);
295 /* Clear any spurious interrupts */
296 KeClearEvent(&DriveInfo
->ControllerInfo
->SynchEvent
);
298 /* queue seek command */
299 if(HwSeek(DriveInfo
, Cylinder
) != STATUS_SUCCESS
)
301 WARN_(FLOPPY
, "RWSeekToTrack(): unable to seek\n");
302 return STATUS_UNSUCCESSFUL
;
305 WaitForControllerInterrupt(DriveInfo
->ControllerInfo
);
307 if(HwSenseInterruptStatus(DriveInfo
->ControllerInfo
) != STATUS_SUCCESS
)
309 WARN_(FLOPPY
, "RWSeekToTrack(): unable to get seek results\n");
310 return STATUS_UNSUCCESSFUL
;
313 /* read ID mark from head 0 to verify */
314 if(HwReadId(DriveInfo
, 0) != STATUS_SUCCESS
)
316 WARN_(FLOPPY
, "RWSeekToTrack(): unable to queue ReadId\n");
317 return STATUS_UNSUCCESSFUL
;
320 WaitForControllerInterrupt(DriveInfo
->ControllerInfo
);
322 if(HwReadIdResult(DriveInfo
->ControllerInfo
, &CurCylinder
, NULL
) != STATUS_SUCCESS
)
324 WARN_(FLOPPY
, "RWSeekToTrack(): unable to get ReadId result\n");
325 return STATUS_UNSUCCESSFUL
;
328 if(CurCylinder
!= Cylinder
)
330 WARN_(FLOPPY
, "RWSeekToTrack(): Seeek to track failed; current cylinder is 0x%x\n", CurCylinder
);
331 return STATUS_UNSUCCESSFUL
;
334 INFO_(FLOPPY
, "RWSeekToCylinder: returning successfully, now on cyl %d\n", Cylinder
);
336 return STATUS_SUCCESS
;
340 static NTSTATUS NTAPI
RWComputeCHS(PDRIVE_INFO IN DriveInfo
,
341 ULONG IN DiskByteOffset
,
346 * FUNCTION: Compute the CHS from the absolute byte offset on disk
348 * DriveInfo: Drive to compute on
349 * DiskByteOffset: Absolute offset on disk of the starting byte
350 * Cylinder: Cylinder that the byte is on
351 * Head: Head that the byte is on
352 * Sector: Sector that the byte is on
354 * STATUS_SUCCESS if CHS are determined correctly
355 * STATUS_UNSUCCESSFUL otherwise
357 * - Lots of ugly typecasts here
358 * - Sectors are 1-based!
359 * - This is really crummy code. Please FIXME.
362 ULONG AbsoluteSector
;
363 UCHAR SectorsPerCylinder
= (UCHAR
)DriveInfo
->DiskGeometry
.SectorsPerTrack
* (UCHAR
)DriveInfo
->DiskGeometry
.TracksPerCylinder
;
365 TRACE_(FLOPPY
, "RWComputeCHS: Called with offset 0x%x\n", DiskByteOffset
);
367 /* First calculate the 1-based "absolute sector" based on the byte offset */
368 ASSERT(!(DiskByteOffset
% DriveInfo
->DiskGeometry
.BytesPerSector
)); /* FIXME: Only handle full sector transfers atm */
370 /* AbsoluteSector is zero-based to make the math a little easier */
371 AbsoluteSector
= DiskByteOffset
/ DriveInfo
->DiskGeometry
.BytesPerSector
; /* Num full sectors */
373 /* Cylinder number is floor(AbsoluteSector / SectorsPerCylinder) */
374 *Cylinder
= (CHAR
)(AbsoluteSector
/ SectorsPerCylinder
);
376 /* Head number is 0 if the sector within the cylinder < SectorsPerTrack; 1 otherwise */
377 *Head
= AbsoluteSector
% SectorsPerCylinder
< DriveInfo
->DiskGeometry
.SectorsPerTrack
? 0 : 1;
380 * Sector number is the sector within the cylinder if on head 0; that minus SectorsPerTrack if it's on head 1
381 * (lots of casts to placate msvc). 1-based!
383 *Sector
= ((UCHAR
)(AbsoluteSector
% SectorsPerCylinder
) + 1) - ((*Head
) * (UCHAR
)DriveInfo
->DiskGeometry
.SectorsPerTrack
);
385 INFO_(FLOPPY
, "RWComputeCHS: offset 0x%x is c:0x%x h:0x%x s:0x%x\n", DiskByteOffset
, *Cylinder
, *Head
, *Sector
);
387 /* Sanity checking */
388 ASSERT(*Cylinder
<= DriveInfo
->DiskGeometry
.Cylinders
.QuadPart
);
389 ASSERT(*Head
<= DriveInfo
->DiskGeometry
.TracksPerCylinder
);
390 ASSERT(*Sector
<= DriveInfo
->DiskGeometry
.SectorsPerTrack
);
392 return STATUS_SUCCESS
;
396 VOID NTAPI
ReadWritePassive(PDRIVE_INFO DriveInfo
,
399 * FUNCTION: Handle the first phase of a read or write IRP
401 * DeviceObject: DeviceObject that is the target of the IRP
402 * Irp: IRP to process
404 * STATUS_VERIFY_REQUIRED if the media has changed and we need the filesystems to re-synch
405 * STATUS_SUCCESS otherwise
407 * - Must be called at PASSIVE_LEVEL
408 * - This function is about 250 lines longer than I wanted it to be. Sorry.
411 * This routine manages the whole process of servicing a read or write request. It goes like this:
412 * 1) Check the DO_VERIFY_VOLUME flag and return if it's set
413 * 2) Check the disk change line and notify the OS if it's set and return
414 * 3) Detect the media if we haven't already
415 * 4) Set up DiskByteOffset, Length, and WriteToDevice parameters
416 * 5) Get DMA map registers
417 * 6) Then, in a loop for each track, until all bytes are transferred:
418 * a) Compute the current CHS to set the read/write head to
419 * b) Seek to that spot
420 * c) Compute the last sector to transfer on that track
421 * d) Map the transfer through DMA
422 * e) Send the read or write command to the controller
423 * f) Read the results of the command
426 PDEVICE_OBJECT DeviceObject
= DriveInfo
->DeviceObject
;
427 PIO_STACK_LOCATION Stack
= IoGetCurrentIrpStackLocation(Irp
);
428 BOOLEAN WriteToDevice
;
430 ULONG DiskByteOffset
;
434 ULONG_PTR TransferByteOffset
;
439 TRACE_(FLOPPY
, "ReadWritePassive called to %s 0x%x bytes from offset 0x%x\n",
440 (Stack
->MajorFunction
== IRP_MJ_READ
? "read" : "write"),
441 (Stack
->MajorFunction
== IRP_MJ_READ
? Stack
->Parameters
.Read
.Length
: Stack
->Parameters
.Write
.Length
),
442 (Stack
->MajorFunction
== IRP_MJ_READ
? Stack
->Parameters
.Read
.ByteOffset
.u
.LowPart
:
443 Stack
->Parameters
.Write
.ByteOffset
.u
.LowPart
));
445 /* Default return codes */
446 Irp
->IoStatus
.Status
= STATUS_UNSUCCESSFUL
;
447 Irp
->IoStatus
.Information
= 0;
450 * Check to see if the volume needs to be verified. If so,
451 * we can get out of here quickly.
453 if(DeviceObject
->Flags
& DO_VERIFY_VOLUME
&& !(DeviceObject
->Flags
& SL_OVERRIDE_VERIFY_VOLUME
))
455 INFO_(FLOPPY
, "ReadWritePassive(): DO_VERIFY_VOLUME set; Completing with STATUS_VERIFY_REQUIRED\n");
456 Irp
->IoStatus
.Status
= STATUS_VERIFY_REQUIRED
;
457 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
462 * Check the change line, and if it's set, return
464 StartMotor(DriveInfo
);
465 if(HwDiskChanged(DeviceObject
->DeviceExtension
, &DiskChanged
) != STATUS_SUCCESS
)
467 WARN_(FLOPPY
, "ReadWritePassive(): unable to detect disk change; Completing with STATUS_UNSUCCESSFUL\n");
468 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
469 StopMotor(DriveInfo
->ControllerInfo
);
475 INFO_(FLOPPY
, "ReadWritePhase1(): signalling media changed; Completing with STATUS_MEDIA_CHANGED\n");
477 /* The following call sets IoStatus.Status and IoStatus.Information */
478 SignalMediaChanged(DeviceObject
, Irp
);
481 * Guessing at something... see ioctl.c for more info
483 if(ResetChangeFlag(DriveInfo
) == STATUS_NO_MEDIA_IN_DEVICE
)
484 Irp
->IoStatus
.Status
= STATUS_NO_MEDIA_IN_DEVICE
;
486 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
487 StopMotor(DriveInfo
->ControllerInfo
);
492 * Figure out the media type, if we don't know it already
494 if(DriveInfo
->DiskGeometry
.MediaType
== Unknown
)
496 if(RWDetermineMediaType(DriveInfo
) != STATUS_SUCCESS
)
498 WARN_(FLOPPY
, "ReadWritePassive(): unable to determine media type; completing with STATUS_UNSUCCESSFUL\n");
499 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
500 StopMotor(DriveInfo
->ControllerInfo
);
504 if(DriveInfo
->DiskGeometry
.MediaType
== Unknown
)
506 WARN_(FLOPPY
, "ReadWritePassive(): Unknown media in drive; completing with STATUS_UNRECOGNIZED_MEDIA\n");
507 Irp
->IoStatus
.Status
= STATUS_UNRECOGNIZED_MEDIA
;
508 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
509 StopMotor(DriveInfo
->ControllerInfo
);
514 /* Set up parameters for read or write */
515 if(Stack
->MajorFunction
== IRP_MJ_READ
)
517 Length
= Stack
->Parameters
.Read
.Length
;
518 DiskByteOffset
= Stack
->Parameters
.Read
.ByteOffset
.u
.LowPart
;
519 WriteToDevice
= FALSE
;
523 Length
= Stack
->Parameters
.Write
.Length
;
524 DiskByteOffset
= Stack
->Parameters
.Write
.ByteOffset
.u
.LowPart
;
525 WriteToDevice
= TRUE
;
530 * FloppyDeviceData.ReadWriteGapLength specify the value for the physical drive.
531 * We should set this value depend on the format of the inserted disk and possible
532 * depend on the request (read or write). A value of 0 results in one rotation
533 * between the sectors (7.2sec for reading a track).
535 Gap
= DriveInfo
->FloppyDeviceData
.ReadWriteGapLength
;
538 * Set up DMA transfer
540 * This is as good of a place as any to document something that used to confuse me
541 * greatly (and I even wrote some of the kernel's DMA code, so if it confuses me, it
542 * probably confuses at least a couple of other people too).
544 * MmGetMdlVirtualAddress() returns the virtal address, as mapped in the buffer's original
545 * process context, of the MDL. In other words: say you start with a buffer at address X, then
546 * you build an MDL out of that buffer called Mdl. If you call MmGetMdlVirtualAddress(Mdl), it
549 * There are two parameters that the function looks at to produce X again, given the MDL: the
550 * first is the StartVa, which is the base virtual address of the page that the buffer starts
551 * in. If your buffer's virtual address is 0x12345678, StartVa will be 0x12345000, assuming 4K pages
552 * (which is (almost) always the case on x86). Note well: this address is only valid in the
553 * process context that you initially built the MDL from. The physical pages that make up
554 * the MDL might perhaps be mapped in other process contexts too (or even in the system space,
555 * above 0x80000000 (default; 0xc0000000 on current ReactOS or /3GB Windows)), but it will
556 * (possibly) be mapped at a different address.
558 * The second parameter is the ByteOffset. Given an original buffer address of 0x12345678,
559 * the ByteOffset would be 0x678. Because MDLs can only describe full pages (and therefore
560 * StartVa always points to the start address of a page), the ByteOffset must be used to
561 * find the real start of the buffer.
563 * In general, if you add the StartVa and ByteOffset together, you get back your original
564 * buffer pointer, which you are free to use if you're sure you're in the right process
565 * context. You could tell by accessing the (hidden and not-to-be-used) Process member of
566 * the MDL, but in general, if you have to ask whether or not you are in the right context,
567 * then you shouldn't be using this address for anything anyway. There are also security implications
568 * (big ones, really, I wouldn't kid about this) to directly accessing a user's buffer by VA, so
571 * There is a somewhat weird but very common use of the virtual address associated with a MDL
572 * that pops up often in the context of DMA. DMA APIs (particularly MapTransfer()) need to
573 * know where the memory is that they should DMA into and out of. This memory is described
574 * by a MDL. The controller eventually needs to know a physical address on the host side,
575 * which is generally a 32-bit linear address (on x86), and not just a page address. Therefore,
576 * the DMA APIs look at the ByteOffset field of the MDL to reconstruct the real address that
577 * should be programmed into the DMA controller.
579 * It is often the case that a transfer needs to be broken down over more than one DMA operation,
580 * particularly when it is a big transfer and the HAL doesn't give you enough map registers
581 * to map the whole thing at once. Therefore, the APIs need a way to tell how far into the MDL
582 * they should look to transfer the next chunk of bytes. Now, Microsoft could have designed
583 * MapTransfer to take a "MDL offset" argument, starting with 0, for how far into the buffer to
584 * start, but it didn't. Instead, MapTransfer asks for the virtual address of the MDL as an "index" into
585 * the MDL. The way it computes how far into the page to start the transfer is by masking off all but
586 * the bottom 12 bits (on x86) of the number you supply as the CurrentVa and using *that* as the
587 * ByteOffset instead of the one in the MDL. (OK, this varies a bit by OS and version, but this
590 * In other words, you get a number back from MmGetMdlVirtualAddress that represents the start of your
591 * buffer, and you pass it to the first MapTransfer call. Then, for each successive operation
592 * on the same buffer, you increment that address to point to the next spot in the MDL that
593 * you want to DMA to/from. The fact that the virtual address you're manipulating is probably not
594 * mapped into the process context that you're running in is irrelevant, since it's only being
595 * used to index into the MDL.
598 /* Get map registers for DMA */
599 KeRaiseIrql(DISPATCH_LEVEL
, &OldIrql
);
600 Status
= IoAllocateAdapterChannel(DriveInfo
->ControllerInfo
->AdapterObject
, DeviceObject
,
601 DriveInfo
->ControllerInfo
->MapRegisters
, MapRegisterCallback
, DriveInfo
->ControllerInfo
);
602 KeLowerIrql(OldIrql
);
604 if(Status
!= STATUS_SUCCESS
)
606 WARN_(FLOPPY
, "ReadWritePassive(): unable allocate an adapter channel; completing with STATUS_UNSUCCESSFUL\n");
607 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
608 StopMotor(DriveInfo
->ControllerInfo
);
614 * Read from (or write to) the device
616 * This has to be called in a loop, as you can only transfer data to/from a single track at
619 TransferByteOffset
= 0;
620 while(TransferByteOffset
< Length
)
625 ULONG CurrentTransferBytes
;
626 UCHAR CurrentTransferSectors
;
628 INFO_(FLOPPY
, "ReadWritePassive(): iterating in while (TransferByteOffset = 0x%x of 0x%x total) - allocating %d registers\n",
629 TransferByteOffset
, Length
, DriveInfo
->ControllerInfo
->MapRegisters
);
631 KeClearEvent(&DriveInfo
->ControllerInfo
->SynchEvent
);
634 * Compute starting CHS
636 if(RWComputeCHS(DriveInfo
, DiskByteOffset
+TransferByteOffset
, &Cylinder
, &Head
, &StartSector
) != STATUS_SUCCESS
)
638 WARN_(FLOPPY
, "ReadWritePassive(): unable to compute CHS; completing with STATUS_UNSUCCESSFUL\n");
639 RWFreeAdapterChannel(DriveInfo
->ControllerInfo
->AdapterObject
);
640 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
641 StopMotor(DriveInfo
->ControllerInfo
);
646 * Seek to the right track
648 if(!DriveInfo
->ControllerInfo
->ImpliedSeeks
)
650 if(RWSeekToCylinder(DriveInfo
, Cylinder
) != STATUS_SUCCESS
)
652 WARN_(FLOPPY
, "ReadWritePassive(): unable to seek; completing with STATUS_UNSUCCESSFUL\n");
653 RWFreeAdapterChannel(DriveInfo
->ControllerInfo
->AdapterObject
);
654 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
655 StopMotor(DriveInfo
->ControllerInfo
);
661 * Compute last sector
663 * We can only ask for a transfer up to the end of the track. Then we have to re-seek and do more.
664 * TODO: Support the MT bit
666 INFO_(FLOPPY
, "ReadWritePassive(): computing number of sectors to transfer (StartSector 0x%x): ", StartSector
);
668 /* 1-based sector number */
669 if( (((DriveInfo
->DiskGeometry
.TracksPerCylinder
- Head
) * DriveInfo
->DiskGeometry
.SectorsPerTrack
- StartSector
) + 1 ) <
670 (Length
- TransferByteOffset
) / DriveInfo
->DiskGeometry
.BytesPerSector
)
672 CurrentTransferSectors
= (UCHAR
)((DriveInfo
->DiskGeometry
.TracksPerCylinder
- Head
) * DriveInfo
->DiskGeometry
.SectorsPerTrack
- StartSector
) + 1;
676 CurrentTransferSectors
= (UCHAR
)((Length
- TransferByteOffset
) / DriveInfo
->DiskGeometry
.BytesPerSector
);
679 INFO_(FLOPPY
, "0x%x\n", CurrentTransferSectors
);
681 CurrentTransferBytes
= CurrentTransferSectors
* DriveInfo
->DiskGeometry
.BytesPerSector
;
684 * Adjust to map registers
685 * BUG: Does this take into account page crossings?
687 INFO_(FLOPPY
, "ReadWritePassive(): Trying to transfer 0x%x bytes\n", CurrentTransferBytes
);
689 ASSERT(CurrentTransferBytes
);
691 if(BYTES_TO_PAGES(CurrentTransferBytes
) > DriveInfo
->ControllerInfo
->MapRegisters
)
693 CurrentTransferSectors
= (UCHAR
)((DriveInfo
->ControllerInfo
->MapRegisters
* PAGE_SIZE
) /
694 DriveInfo
->DiskGeometry
.BytesPerSector
);
696 CurrentTransferBytes
= CurrentTransferSectors
* DriveInfo
->DiskGeometry
.BytesPerSector
;
698 INFO_(FLOPPY
, "ReadWritePassive: limiting transfer to 0x%x bytes (0x%x sectors) due to map registers\n",
699 CurrentTransferBytes
, CurrentTransferSectors
);
702 /* set up this round's dma operation */
703 /* param 2 is ReadOperation --> opposite of WriteToDevice that IoMapTransfer takes. BAD MS. */
704 KeFlushIoBuffers(Irp
->MdlAddress
, !WriteToDevice
, TRUE
);
706 IoMapTransfer(DriveInfo
->ControllerInfo
->AdapterObject
, Irp
->MdlAddress
,
707 DriveInfo
->ControllerInfo
->MapRegisterBase
,
708 (PUCHAR
)((ULONG_PTR
)MmGetMdlVirtualAddress(Irp
->MdlAddress
) + TransferByteOffset
),
709 &CurrentTransferBytes
, WriteToDevice
);
714 KeClearEvent(&DriveInfo
->ControllerInfo
->SynchEvent
);
716 /* Issue the read/write command to the controller. Note that it expects the opposite of WriteToDevice. */
717 if(HwReadWriteData(DriveInfo
->ControllerInfo
, !WriteToDevice
, DriveInfo
->UnitNumber
, Cylinder
, Head
, StartSector
,
718 DriveInfo
->BytesPerSectorCode
, DriveInfo
->DiskGeometry
.SectorsPerTrack
, Gap
, 0xff) != STATUS_SUCCESS
)
720 WARN_(FLOPPY
, "ReadWritePassive(): HwReadWriteData returned failure; unable to read; completing with STATUS_UNSUCCESSFUL\n");
721 RWFreeAdapterChannel(DriveInfo
->ControllerInfo
->AdapterObject
);
722 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
723 StopMotor(DriveInfo
->ControllerInfo
);
727 INFO_(FLOPPY
, "ReadWritePassive(): HwReadWriteData returned -- waiting on event\n");
730 * At this point, we block and wait for an interrupt
731 * FIXME: this seems to take too long
733 WaitForControllerInterrupt(DriveInfo
->ControllerInfo
);
735 /* Read is complete; flush & free adapter channel */
736 IoFlushAdapterBuffers(DriveInfo
->ControllerInfo
->AdapterObject
, Irp
->MdlAddress
,
737 DriveInfo
->ControllerInfo
->MapRegisterBase
,
738 (PVOID
)((ULONG_PTR
)MmGetMdlVirtualAddress(Irp
->MdlAddress
) + TransferByteOffset
),
739 CurrentTransferBytes
, WriteToDevice
);
741 /* Read the results from the drive */
742 if(HwReadWriteResult(DriveInfo
->ControllerInfo
) != STATUS_SUCCESS
)
744 WARN_(FLOPPY
, "ReadWritePassive(): HwReadWriteResult returned failure; unable to read; completing with STATUS_UNSUCCESSFUL\n");
745 HwDumpRegisters(DriveInfo
->ControllerInfo
);
746 RWFreeAdapterChannel(DriveInfo
->ControllerInfo
->AdapterObject
);
747 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
748 StopMotor(DriveInfo
->ControllerInfo
);
752 TransferByteOffset
+= CurrentTransferBytes
;
755 RWFreeAdapterChannel(DriveInfo
->ControllerInfo
->AdapterObject
);
757 /* That's all folks! */
758 INFO_(FLOPPY
, "ReadWritePassive(): success; Completing with STATUS_SUCCESS\n");
759 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
760 Irp
->IoStatus
.Information
= Length
;
761 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
762 StopMotor(DriveInfo
->ControllerInfo
);