Visual C++ backend for rbuild (for now just a hacked mingw backend) and related compi...
[reactos.git] / drivers / storage / floppy / readwrite.c
1 /*
2 * ReactOS Floppy Driver
3 * Copyright (C) 2004, Vizzini (vizzini@plasmic.com)
4 *
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.
9 *
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.
14 *
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.
18 *
19 * PROJECT: ReactOS Floppy Driver
20 * FILE: readwrite.c
21 * PURPOSE: Read/Write handler routines
22 * PROGRAMMER: Vizzini (vizzini@plasmic.com)
23 * REVISIONS:
24 * 15-Feb-2004 vizzini - Created
25 * NOTES:
26 *
27 * READ/WRITE PROCESS
28 *
29 * This process is extracted from the Intel datasheet for the floppy controller.
30 *
31 * - Turn on the motor and set turnoff time
32 * - Program the drive's data rate
33 * - Seek
34 * - Read ID
35 * - Set up DMA
36 * - Send read/write command to FDC
37 * - Read result bytes
38 *
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
41 * more details.
42 *
43 * NOTES:
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.
47 *
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
53 */
54
55 #include <ntddk.h>
56 #include <debug.h>
57
58 #include "floppy.h"
59 #include "csqrtns.h"
60 #include "hardware.h"
61 #include "readwrite.h"
62
63 \f
64 static IO_ALLOCATION_ACTION NTAPI MapRegisterCallback(PDEVICE_OBJECT DeviceObject,
65 PIRP Irp,
66 PVOID MapRegisterBase,
67 PVOID Context)
68 /*
69 * FUNCTION: Acquire map registers in prep for DMA
70 * ARGUMENTS:
71 * DeviceObject: unused
72 * Irp: unused
73 * MapRegisterBase: returned to blocked thread via a member var
74 * Context: contains a pointer to the right ControllerInfo
75 * struct
76 * RETURNS:
77 * KeepObject, because that's what the DDK says to do
78 */
79 {
80 PCONTROLLER_INFO ControllerInfo = (PCONTROLLER_INFO)Context;
81 UNREFERENCED_PARAMETER(DeviceObject);
82 UNREFERENCED_PARAMETER(Irp);
83
84 TRACE_(FLOPPY, "MapRegisterCallback Called\n");
85
86 ControllerInfo->MapRegisterBase = MapRegisterBase;
87 KeSetEvent(&ControllerInfo->SynchEvent, 0, FALSE);
88
89 return KeepObject;
90 }
91
92 \f
93 NTSTATUS NTAPI ReadWrite(PDEVICE_OBJECT DeviceObject,
94 PIRP Irp)
95 /*
96 * FUNCTION: Dispatch routine called for read or write IRPs
97 * ARGUMENTS:
98 * RETURNS:
99 * STATUS_PENDING if the IRP is queued
100 * STATUS_INVALID_PARAMETER if IRP is set up wrong
101 * NOTES:
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
108 */
109 {
110 TRACE_(FLOPPY, "ReadWrite called\n");
111
112 ASSERT(DeviceObject);
113 ASSERT(Irp);
114
115 if(!Irp->MdlAddress)
116 {
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;
122 }
123
124 /*
125 * Queue the irp to the thread.
126 * The de-queue thread will look in DriverContext[0] for the Device Object.
127 */
128 Irp->Tail.Overlay.DriverContext[0] = DeviceObject;
129 IoCsqInsertIrp(&Csq, Irp, NULL);
130
131 return STATUS_PENDING;
132 }
133
134 \f
135 static VOID NTAPI RWFreeAdapterChannel(PADAPTER_OBJECT AdapterObject)
136 /*
137 * FUNCTION: Free the adapter DMA channel that we allocated
138 * ARGUMENTS:
139 * AdapterObject: the object with the map registers to free
140 * NOTES:
141 * - This function is primarily needed because IoFreeAdapterChannel wants to
142 * be called at DISPATCH_LEVEL
143 */
144 {
145 KIRQL Irql;
146
147 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
148
149 KeRaiseIrql(DISPATCH_LEVEL, &Irql);
150 IoFreeAdapterChannel(AdapterObject);
151 KeLowerIrql(Irql);
152 }
153
154 \f
155 static NTSTATUS NTAPI RWDetermineMediaType(PDRIVE_INFO DriveInfo)
156 /*
157 * FUNCTION: Determine the media type of the disk in the drive and fill in the geometry
158 * ARGUMENTS:
159 * DriveInfo: drive to look at
160 * RETURNS:
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
164 * NOTES:
165 * - Expects the motor to already be running
166 * - Currently only supports 1.44MB 3.5" disks
167 * - PAGED_CODE because it waits
168 * TODO:
169 * - Support more disk types
170 */
171 {
172 UCHAR HeadLoadTime;
173 UCHAR HeadUnloadTime;
174 UCHAR StepRateTime;
175
176 PAGED_CODE();
177
178 TRACE_(FLOPPY, "RWDetermineMediaType called\n");
179
180 /*
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
183 * at all.
184 */
185
186 do
187 {
188 int i;
189
190 /* Program data rate */
191 if(HwSetDataRate(DriveInfo->ControllerInfo, DRSR_DSEL_500KBPS) != STATUS_SUCCESS)
192 {
193 WARN_(FLOPPY, "RWDetermineMediaType(): unable to set data rate\n");
194 return STATUS_UNSUCCESSFUL;
195 }
196
197 /* Specify */
198 HeadLoadTime = SPECIFY_HLT_500K;
199 HeadUnloadTime = SPECIFY_HUT_500K;
200 StepRateTime = SPECIFY_SRT_500K;
201
202 /* Don't disable DMA --> enable dma (dumb & confusing) */
203 if(HwSpecify(DriveInfo->ControllerInfo, HeadLoadTime, HeadUnloadTime, StepRateTime, FALSE) != STATUS_SUCCESS)
204 {
205 WARN_(FLOPPY, "RWDetermineMediaType(): specify failed\n");
206 return STATUS_UNSUCCESSFUL;
207 }
208
209 /* clear any spurious interrupts in preparation for recalibrate */
210 KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);
211
212 /* Recalibrate --> head over first track */
213 for(i=0; i < 2; i++)
214 {
215 NTSTATUS RecalStatus;
216
217 if(HwRecalibrate(DriveInfo) != STATUS_SUCCESS)
218 {
219 WARN_(FLOPPY, "RWDetermineMediaType(): Recalibrate failed\n");
220 return STATUS_UNSUCCESSFUL;
221 }
222
223 /* Wait for the recalibrate to finish */
224 WaitForControllerInterrupt(DriveInfo->ControllerInfo);
225
226 RecalStatus = HwRecalibrateResult(DriveInfo->ControllerInfo);
227
228 if(RecalStatus == STATUS_SUCCESS)
229 break;
230
231 if(i == 1) /* failed for 2nd time */
232 {
233 WARN_(FLOPPY, "RWDetermineMediaType(): RecalibrateResult failed\n");
234 return STATUS_UNSUCCESSFUL;
235 }
236 }
237
238 /* clear any spurious interrupts */
239 KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);
240
241 /* Try to read an ID */
242 if(HwReadId(DriveInfo, 0) != STATUS_SUCCESS) /* read the first ID we find, from head 0 */
243 {
244 WARN_(FLOPPY, "RWDetermineMediaType(): ReadId failed\n");
245 return STATUS_UNSUCCESSFUL; /* if we can't even write to the controller, it's hopeless */
246 }
247
248 /* Wait for the ReadID to finish */
249 WaitForControllerInterrupt(DriveInfo->ControllerInfo);
250
251 if(HwReadIdResult(DriveInfo->ControllerInfo, NULL, NULL) != STATUS_SUCCESS)
252 {
253 WARN_(FLOPPY, "RWDetermineMediaType(): ReadIdResult failed; continuing\n");
254 continue;
255 }
256
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;
267 }
268 while(FALSE);
269
270 TRACE_(FLOPPY, "RWDetermineMediaType(): failed to find media\n");
271 return STATUS_UNRECOGNIZED_MEDIA;
272 }
273
274 \f
275 static NTSTATUS NTAPI RWSeekToCylinder(PDRIVE_INFO DriveInfo,
276 UCHAR Cylinder)
277 /*
278 * FUNCTION: Seek a particular drive to a particular track
279 * ARGUMENTS:
280 * DriveInfo: Drive to seek
281 * Cylinder: track to seek to
282 * RETURNS:
283 * STATUS_SUCCESS if the head was successfully seeked
284 * STATUS_UNSUCCESSFUL if not
285 * NOTES:
286 * - PAGED_CODE because it blocks
287 */
288 {
289 UCHAR CurCylinder;
290
291 PAGED_CODE();
292
293 TRACE_(FLOPPY, "RWSeekToCylinder called drive 0x%p cylinder %d\n", DriveInfo, Cylinder);
294
295 /* Clear any spurious interrupts */
296 KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);
297
298 /* queue seek command */
299 if(HwSeek(DriveInfo, Cylinder) != STATUS_SUCCESS)
300 {
301 WARN_(FLOPPY, "RWSeekToTrack(): unable to seek\n");
302 return STATUS_UNSUCCESSFUL;
303 }
304
305 WaitForControllerInterrupt(DriveInfo->ControllerInfo);
306
307 if(HwSenseInterruptStatus(DriveInfo->ControllerInfo) != STATUS_SUCCESS)
308 {
309 WARN_(FLOPPY, "RWSeekToTrack(): unable to get seek results\n");
310 return STATUS_UNSUCCESSFUL;
311 }
312
313 /* read ID mark from head 0 to verify */
314 if(HwReadId(DriveInfo, 0) != STATUS_SUCCESS)
315 {
316 WARN_(FLOPPY, "RWSeekToTrack(): unable to queue ReadId\n");
317 return STATUS_UNSUCCESSFUL;
318 }
319
320 WaitForControllerInterrupt(DriveInfo->ControllerInfo);
321
322 if(HwReadIdResult(DriveInfo->ControllerInfo, &CurCylinder, NULL) != STATUS_SUCCESS)
323 {
324 WARN_(FLOPPY, "RWSeekToTrack(): unable to get ReadId result\n");
325 return STATUS_UNSUCCESSFUL;
326 }
327
328 if(CurCylinder != Cylinder)
329 {
330 WARN_(FLOPPY, "RWSeekToTrack(): Seeek to track failed; current cylinder is 0x%x\n", CurCylinder);
331 return STATUS_UNSUCCESSFUL;
332 }
333
334 INFO_(FLOPPY, "RWSeekToCylinder: returning successfully, now on cyl %d\n", Cylinder);
335
336 return STATUS_SUCCESS;
337 }
338
339 \f
340 static NTSTATUS NTAPI RWComputeCHS(PDRIVE_INFO IN DriveInfo,
341 ULONG IN DiskByteOffset,
342 PUCHAR OUT Cylinder,
343 PUCHAR OUT Head,
344 PUCHAR OUT Sector)
345 /*
346 * FUNCTION: Compute the CHS from the absolute byte offset on disk
347 * ARGUMENTS:
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
353 * RETURNS:
354 * STATUS_SUCCESS if CHS are determined correctly
355 * STATUS_UNSUCCESSFUL otherwise
356 * NOTES:
357 * - Lots of ugly typecasts here
358 * - Sectors are 1-based!
359 * - This is really crummy code. Please FIXME.
360 */
361 {
362 ULONG AbsoluteSector;
363 UCHAR SectorsPerCylinder = (UCHAR)DriveInfo->DiskGeometry.SectorsPerTrack * (UCHAR)DriveInfo->DiskGeometry.TracksPerCylinder;
364
365 TRACE_(FLOPPY, "RWComputeCHS: Called with offset 0x%x\n", DiskByteOffset);
366
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 */
369
370 /* AbsoluteSector is zero-based to make the math a little easier */
371 AbsoluteSector = DiskByteOffset / DriveInfo->DiskGeometry.BytesPerSector; /* Num full sectors */
372
373 /* Cylinder number is floor(AbsoluteSector / SectorsPerCylinder) */
374 *Cylinder = (CHAR)(AbsoluteSector / SectorsPerCylinder);
375
376 /* Head number is 0 if the sector within the cylinder < SectorsPerTrack; 1 otherwise */
377 *Head = AbsoluteSector % SectorsPerCylinder < DriveInfo->DiskGeometry.SectorsPerTrack ? 0 : 1;
378
379 /*
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!
382 */
383 *Sector = ((UCHAR)(AbsoluteSector % SectorsPerCylinder) + 1) - ((*Head) * (UCHAR)DriveInfo->DiskGeometry.SectorsPerTrack);
384
385 INFO_(FLOPPY, "RWComputeCHS: offset 0x%x is c:0x%x h:0x%x s:0x%x\n", DiskByteOffset, *Cylinder, *Head, *Sector);
386
387 /* Sanity checking */
388 ASSERT(*Cylinder <= DriveInfo->DiskGeometry.Cylinders.QuadPart);
389 ASSERT(*Head <= DriveInfo->DiskGeometry.TracksPerCylinder);
390 ASSERT(*Sector <= DriveInfo->DiskGeometry.SectorsPerTrack);
391
392 return STATUS_SUCCESS;
393 }
394
395 \f
396 VOID NTAPI ReadWritePassive(PDRIVE_INFO DriveInfo,
397 PIRP Irp)
398 /*
399 * FUNCTION: Handle the first phase of a read or write IRP
400 * ARGUMENTS:
401 * DeviceObject: DeviceObject that is the target of the IRP
402 * Irp: IRP to process
403 * RETURNS:
404 * STATUS_VERIFY_REQUIRED if the media has changed and we need the filesystems to re-synch
405 * STATUS_SUCCESS otherwise
406 * NOTES:
407 * - Must be called at PASSIVE_LEVEL
408 * - This function is about 250 lines longer than I wanted it to be. Sorry.
409 *
410 * DETAILS:
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
424 */
425 {
426 PDEVICE_OBJECT DeviceObject = DriveInfo->DeviceObject;
427 PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
428 BOOLEAN WriteToDevice;
429 ULONG Length;
430 ULONG DiskByteOffset;
431 KIRQL OldIrql;
432 NTSTATUS Status;
433 BOOLEAN DiskChanged;
434 ULONG_PTR TransferByteOffset;
435 UCHAR Gap;
436
437 PAGED_CODE();
438
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));
444
445 /* Default return codes */
446 Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
447 Irp->IoStatus.Information = 0;
448
449 /*
450 * Check to see if the volume needs to be verified. If so,
451 * we can get out of here quickly.
452 */
453 if(DeviceObject->Flags & DO_VERIFY_VOLUME && !(DeviceObject->Flags & SL_OVERRIDE_VERIFY_VOLUME))
454 {
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);
458 return;
459 }
460
461 /*
462 * Check the change line, and if it's set, return
463 */
464 StartMotor(DriveInfo);
465 if(HwDiskChanged(DeviceObject->DeviceExtension, &DiskChanged) != STATUS_SUCCESS)
466 {
467 WARN_(FLOPPY, "ReadWritePassive(): unable to detect disk change; Completing with STATUS_UNSUCCESSFUL\n");
468 IoCompleteRequest(Irp, IO_NO_INCREMENT);
469 StopMotor(DriveInfo->ControllerInfo);
470 return;
471 }
472
473 if(DiskChanged)
474 {
475 INFO_(FLOPPY, "ReadWritePhase1(): signalling media changed; Completing with STATUS_MEDIA_CHANGED\n");
476
477 /* The following call sets IoStatus.Status and IoStatus.Information */
478 SignalMediaChanged(DeviceObject, Irp);
479
480 /*
481 * Guessing at something... see ioctl.c for more info
482 */
483 if(ResetChangeFlag(DriveInfo) == STATUS_NO_MEDIA_IN_DEVICE)
484 Irp->IoStatus.Status = STATUS_NO_MEDIA_IN_DEVICE;
485
486 IoCompleteRequest(Irp, IO_NO_INCREMENT);
487 StopMotor(DriveInfo->ControllerInfo);
488 return;
489 }
490
491 /*
492 * Figure out the media type, if we don't know it already
493 */
494 if(DriveInfo->DiskGeometry.MediaType == Unknown)
495 {
496 if(RWDetermineMediaType(DriveInfo) != STATUS_SUCCESS)
497 {
498 WARN_(FLOPPY, "ReadWritePassive(): unable to determine media type; completing with STATUS_UNSUCCESSFUL\n");
499 IoCompleteRequest(Irp, IO_NO_INCREMENT);
500 StopMotor(DriveInfo->ControllerInfo);
501 return;
502 }
503
504 if(DriveInfo->DiskGeometry.MediaType == Unknown)
505 {
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);
510 return;
511 }
512 }
513
514 /* Set up parameters for read or write */
515 if(Stack->MajorFunction == IRP_MJ_READ)
516 {
517 Length = Stack->Parameters.Read.Length;
518 DiskByteOffset = Stack->Parameters.Read.ByteOffset.u.LowPart;
519 WriteToDevice = FALSE;
520 }
521 else
522 {
523 Length = Stack->Parameters.Write.Length;
524 DiskByteOffset = Stack->Parameters.Write.ByteOffset.u.LowPart;
525 WriteToDevice = TRUE;
526 }
527
528 /*
529 * FIXME:
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).
534 */
535 Gap = DriveInfo->FloppyDeviceData.ReadWriteGapLength;
536
537 /*
538 * Set up DMA transfer
539 *
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).
543 *
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
547 * will return X.
548 *
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.
557 *
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.
562 *
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
569 * Don't Do That.
570 *
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.
578 *
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
588 * is the effect).
589 *
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.
596 */
597
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);
603
604 if(Status != STATUS_SUCCESS)
605 {
606 WARN_(FLOPPY, "ReadWritePassive(): unable allocate an adapter channel; completing with STATUS_UNSUCCESSFUL\n");
607 IoCompleteRequest(Irp, IO_NO_INCREMENT);
608 StopMotor(DriveInfo->ControllerInfo);
609 return ;
610 }
611
612
613 /*
614 * Read from (or write to) the device
615 *
616 * This has to be called in a loop, as you can only transfer data to/from a single track at
617 * a time.
618 */
619 TransferByteOffset = 0;
620 while(TransferByteOffset < Length)
621 {
622 UCHAR Cylinder;
623 UCHAR Head;
624 UCHAR StartSector;
625 ULONG CurrentTransferBytes;
626 UCHAR CurrentTransferSectors;
627
628 INFO_(FLOPPY, "ReadWritePassive(): iterating in while (TransferByteOffset = 0x%x of 0x%x total) - allocating %d registers\n",
629 TransferByteOffset, Length, DriveInfo->ControllerInfo->MapRegisters);
630
631 KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);
632
633 /*
634 * Compute starting CHS
635 */
636 if(RWComputeCHS(DriveInfo, DiskByteOffset+TransferByteOffset, &Cylinder, &Head, &StartSector) != STATUS_SUCCESS)
637 {
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);
642 return;
643 }
644
645 /*
646 * Seek to the right track
647 */
648 if(!DriveInfo->ControllerInfo->ImpliedSeeks)
649 {
650 if(RWSeekToCylinder(DriveInfo, Cylinder) != STATUS_SUCCESS)
651 {
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);
656 return ;
657 }
658 }
659
660 /*
661 * Compute last sector
662 *
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
665 */
666 INFO_(FLOPPY, "ReadWritePassive(): computing number of sectors to transfer (StartSector 0x%x): ", StartSector);
667
668 /* 1-based sector number */
669 if( (((DriveInfo->DiskGeometry.TracksPerCylinder - Head) * DriveInfo->DiskGeometry.SectorsPerTrack - StartSector) + 1 ) <
670 (Length - TransferByteOffset) / DriveInfo->DiskGeometry.BytesPerSector)
671 {
672 CurrentTransferSectors = (UCHAR)((DriveInfo->DiskGeometry.TracksPerCylinder - Head) * DriveInfo->DiskGeometry.SectorsPerTrack - StartSector) + 1;
673 }
674 else
675 {
676 CurrentTransferSectors = (UCHAR)((Length - TransferByteOffset) / DriveInfo->DiskGeometry.BytesPerSector);
677 }
678
679 INFO_(FLOPPY, "0x%x\n", CurrentTransferSectors);
680
681 CurrentTransferBytes = CurrentTransferSectors * DriveInfo->DiskGeometry.BytesPerSector;
682
683 /*
684 * Adjust to map registers
685 * BUG: Does this take into account page crossings?
686 */
687 INFO_(FLOPPY, "ReadWritePassive(): Trying to transfer 0x%x bytes\n", CurrentTransferBytes);
688
689 ASSERT(CurrentTransferBytes);
690
691 if(BYTES_TO_PAGES(CurrentTransferBytes) > DriveInfo->ControllerInfo->MapRegisters)
692 {
693 CurrentTransferSectors = (UCHAR)((DriveInfo->ControllerInfo->MapRegisters * PAGE_SIZE) /
694 DriveInfo->DiskGeometry.BytesPerSector);
695
696 CurrentTransferBytes = CurrentTransferSectors * DriveInfo->DiskGeometry.BytesPerSector;
697
698 INFO_(FLOPPY, "ReadWritePassive: limiting transfer to 0x%x bytes (0x%x sectors) due to map registers\n",
699 CurrentTransferBytes, CurrentTransferSectors);
700 }
701
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);
705
706 IoMapTransfer(DriveInfo->ControllerInfo->AdapterObject, Irp->MdlAddress,
707 DriveInfo->ControllerInfo->MapRegisterBase,
708 (PUCHAR)((ULONG_PTR)MmGetMdlVirtualAddress(Irp->MdlAddress) + TransferByteOffset),
709 &CurrentTransferBytes, WriteToDevice);
710
711 /*
712 * Read or Write
713 */
714 KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);
715
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)
719 {
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);
724 return ;
725 }
726
727 INFO_(FLOPPY, "ReadWritePassive(): HwReadWriteData returned -- waiting on event\n");
728
729 /*
730 * At this point, we block and wait for an interrupt
731 * FIXME: this seems to take too long
732 */
733 WaitForControllerInterrupt(DriveInfo->ControllerInfo);
734
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);
740
741 /* Read the results from the drive */
742 if(HwReadWriteResult(DriveInfo->ControllerInfo) != STATUS_SUCCESS)
743 {
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);
749 return ;
750 }
751
752 TransferByteOffset += CurrentTransferBytes;
753 }
754
755 RWFreeAdapterChannel(DriveInfo->ControllerInfo->AdapterObject);
756
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);
763 }
764