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