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