readwrite.c
来自「一个类似windows」· C语言 代码 · 共 766 行 · 第 1/2 页
C
766 行
*Sector = ((UCHAR)(AbsoluteSector % SectorsPerCylinder) + 1) - ((*Head) * (UCHAR)DriveInfo->DiskGeometry.SectorsPerTrack);
DPRINT("floppy: RWComputeCHS: offset 0x%x is c:0x%x h:0x%x s:0x%x\n", DiskByteOffset, *Cylinder, *Head, *Sector);
/* Sanity checking */
ASSERT(*Cylinder <= DriveInfo->DiskGeometry.Cylinders.QuadPart);
ASSERT(*Head <= DriveInfo->DiskGeometry.TracksPerCylinder);
ASSERT(*Sector <= DriveInfo->DiskGeometry.SectorsPerTrack);
return STATUS_SUCCESS;
}
VOID NTAPI ReadWritePassive(PDRIVE_INFO DriveInfo,
PIRP Irp)
/*
* FUNCTION: Handle the first phase of a read or write IRP
* ARGUMENTS:
* DeviceObject: DeviceObject that is the target of the IRP
* Irp: IRP to process
* RETURNS:
* STATUS_VERIFY_REQUIRED if the media has changed and we need the filesystems to re-synch
* STATUS_SUCCESS otherwise
* NOTES:
* - Must be called at PASSIVE_LEVEL
* - This function is about 250 lines longer than I wanted it to be. Sorry.
*
* DETAILS:
* This routine manages the whole process of servicing a read or write request. It goes like this:
* 1) Check the DO_VERIFY_VOLUME flag and return if it's set
* 2) Check the disk change line and notify the OS if it's set and return
* 3) Detect the media if we haven't already
* 4) Set up DiskByteOffset, Length, and WriteToDevice parameters
* 5) Get DMA map registers
* 6) Then, in a loop for each track, until all bytes are transferred:
* a) Compute the current CHS to set the read/write head to
* b) Seek to that spot
* c) Compute the last sector to transfer on that track
* d) Map the transfer through DMA
* e) Send the read or write command to the controller
* f) Read the results of the command
*/
{
PDEVICE_OBJECT DeviceObject = DriveInfo->DeviceObject;
PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
BOOLEAN WriteToDevice;
ULONG Length;
ULONG DiskByteOffset;
KIRQL OldIrql;
NTSTATUS Status;
BOOLEAN DiskChanged;
ULONG_PTR TransferByteOffset;
UCHAR Gap;
PAGED_CODE();
DPRINT("floppy: ReadWritePassive called to %s 0x%x bytes from offset 0x%x\n",
(Stack->MajorFunction == IRP_MJ_READ ? "read" : "write"),
(Stack->MajorFunction == IRP_MJ_READ ? Stack->Parameters.Read.Length : Stack->Parameters.Write.Length),
(Stack->MajorFunction == IRP_MJ_READ ? Stack->Parameters.Read.ByteOffset.u.LowPart :
Stack->Parameters.Write.ByteOffset.u.LowPart));
/* Default return codes */
Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
Irp->IoStatus.Information = 0;
/*
* Check to see if the volume needs to be verified. If so,
* we can get out of here quickly.
*/
if(DeviceObject->Flags & DO_VERIFY_VOLUME && !(DeviceObject->Flags & SL_OVERRIDE_VERIFY_VOLUME))
{
DPRINT("floppy: ReadWritePassive(): DO_VERIFY_VOLUME set; Completing with STATUS_VERIFY_REQUIRED\n");
Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return;
}
/*
* Check the change line, and if it's set, return
*/
StartMotor(DriveInfo);
if(HwDiskChanged(DeviceObject->DeviceExtension, &DiskChanged) != STATUS_SUCCESS)
{
DPRINT("floppy: ReadWritePassive(): unable to detect disk change; Completing with STATUS_UNSUCCESSFUL\n");
IoCompleteRequest(Irp, IO_NO_INCREMENT);
StopMotor(DriveInfo->ControllerInfo);
return;
}
if(DiskChanged)
{
DPRINT("floppy: ReadWritePhase1(): signalling media changed; Completing with STATUS_MEDIA_CHANGED\n");
/* The following call sets IoStatus.Status and IoStatus.Information */
SignalMediaChanged(DeviceObject, Irp);
/*
* Guessing at something... see ioctl.c for more info
*/
if(ResetChangeFlag(DriveInfo) == STATUS_NO_MEDIA_IN_DEVICE)
Irp->IoStatus.Status = STATUS_NO_MEDIA_IN_DEVICE;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
StopMotor(DriveInfo->ControllerInfo);
return;
}
/*
* Figure out the media type, if we don't know it already
*/
if(DriveInfo->DiskGeometry.MediaType == Unknown)
{
if(RWDetermineMediaType(DriveInfo) != STATUS_SUCCESS)
{
DPRINT("floppy: ReadWritePassive(): unable to determine media type; completing with STATUS_UNSUCCESSFUL\n");
IoCompleteRequest(Irp, IO_NO_INCREMENT);
StopMotor(DriveInfo->ControllerInfo);
return;
}
if(DriveInfo->DiskGeometry.MediaType == Unknown)
{
DPRINT("floppy: ReadWritePassive(): Unknown media in drive; completing with STATUS_UNRECOGNIZED_MEDIA\n");
Irp->IoStatus.Status = STATUS_UNRECOGNIZED_MEDIA;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
StopMotor(DriveInfo->ControllerInfo);
return;
}
}
/* Set up parameters for read or write */
if(Stack->MajorFunction == IRP_MJ_READ)
{
Length = Stack->Parameters.Read.Length;
DiskByteOffset = Stack->Parameters.Read.ByteOffset.u.LowPart;
WriteToDevice = FALSE;
}
else
{
Length = Stack->Parameters.Write.Length;
DiskByteOffset = Stack->Parameters.Write.ByteOffset.u.LowPart;
WriteToDevice = TRUE;
}
/*
* FIXME:
* FloppyDeviceData.ReadWriteGapLength specify the value for the physical drive.
* We should set this value depend on the format of the inserted disk and possible
* depend on the request (read or write). A value of 0 results in one rotation
* between the sectors (7.2sec for reading a track).
*/
Gap = DriveInfo->FloppyDeviceData.ReadWriteGapLength;
/*
* Set up DMA transfer
*
* This is as good of a place as any to document something that used to confuse me
* greatly (and I even wrote some of the kernel's DMA code, so if it confuses me, it
* probably confuses at least a couple of other people too).
*
* MmGetMdlVirtualAddress() returns the virtal address, as mapped in the buffer's original
* process context, of the MDL. In other words: say you start with a buffer at address X, then
* you build an MDL out of that buffer called Mdl. If you call MmGetMdlVirtualAddress(Mdl), it
* will return X.
*
* There are two parameters that the function looks at to produce X again, given the MDL: the
* first is the StartVa, which is the base virtual address of the page that the buffer starts
* in. If your buffer's virtual address is 0x12345678, StartVa will be 0x12345000, assuming 4K pages
* (which is (almost) always the case on x86). Note well: this address is only valid in the
* process context that you initially built the MDL from. The physical pages that make up
* the MDL might perhaps be mapped in other process contexts too (or even in the system space,
* above 0x80000000 (default; 0xc0000000 on current ReactOS or /3GB Windows)), but it will
* (possibly) be mapped at a different address.
*
* The second parameter is the ByteOffset. Given an original buffer address of 0x12345678,
* the ByteOffset would be 0x678. Because MDLs can only describe full pages (and therefore
* StartVa always points to the start address of a page), the ByteOffset must be used to
* find the real start of the buffer.
*
* In general, if you add the StartVa and ByteOffset together, you get back your original
* buffer pointer, which you are free to use if you're sure you're in the right process
* context. You could tell by accessing the (hidden and not-to-be-used) Process member of
* the MDL, but in general, if you have to ask whether or not you are in the right context,
* then you shouldn't be using this address for anything anyway. There are also security implications
* (big ones, really, I wouldn't kid about this) to directly accessing a user's buffer by VA, so
* Don't Do That.
*
* There is a somewhat weird but very common use of the virtual address associated with a MDL
* that pops up often in the context of DMA. DMA APIs (particularly MapTransfer()) need to
* know where the memory is that they should DMA into and out of. This memory is described
* by a MDL. The controller eventually needs to know a physical address on the host side,
* which is generally a 32-bit linear address (on x86), and not just a page address. Therefore,
* the DMA APIs look at the ByteOffset field of the MDL to reconstruct the real address that
* should be programmed into the DMA controller.
*
* It is often the case that a transfer needs to be broken down over more than one DMA operation,
* particularly when it is a big transfer and the HAL doesn't give you enough map registers
* to map the whole thing at once. Therefore, the APIs need a way to tell how far into the MDL
* they should look to transfer the next chunk of bytes. Now, Microsoft could have designed
* MapTransfer to take a "MDL offset" argument, starting with 0, for how far into the buffer to
* start, but it didn't. Instead, MapTransfer asks for the virtual address of the MDL as an "index" into
* the MDL. The way it computes how far into the page to start the transfer is by masking off all but
* the bottom 12 bits (on x86) of the number you supply as the CurrentVa and using *that* as the
* ByteOffset instead of the one in the MDL. (OK, this varies a bit by OS and version, but this
* is the effect).
*
* In other words, you get a number back from MmGetMdlVirtualAddress that represents the start of your
* buffer, and you pass it to the first MapTransfer call. Then, for each successive operation
* on the same buffer, you increment that address to point to the next spot in the MDL that
* you want to DMA to/from. The fact that the virtual address you're manipulating is probably not
* mapped into the process context that you're running in is irrelevant, since it's only being
* used to index into the MDL.
*/
/* Get map registers for DMA */
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
Status = IoAllocateAdapterChannel(DriveInfo->ControllerInfo->AdapterObject, DeviceObject,
DriveInfo->ControllerInfo->MapRegisters, MapRegisterCallback, DriveInfo->ControllerInfo);
KeLowerIrql(OldIrql);
if(Status != STATUS_SUCCESS)
{
DPRINT("floppy: ReadWritePassive(): unable allocate an adapter channel; completing with STATUS_UNSUCCESSFUL\n");
IoCompleteRequest(Irp, IO_NO_INCREMENT);
StopMotor(DriveInfo->ControllerInfo);
return ;
}
/*
* Read from (or write to) the device
*
* This has to be called in a loop, as you can only transfer data to/from a single track at
* a time.
*/
TransferByteOffset = 0;
while(TransferByteOffset < Length)
{
UCHAR Cylinder;
UCHAR Head;
UCHAR StartSector;
ULONG CurrentTransferBytes;
UCHAR CurrentTransferSectors;
DPRINT("floppy: ReadWritePassive(): iterating in while (TransferByteOffset = 0x%x of 0x%x total) - allocating %d registers\n",
TransferByteOffset, Length, DriveInfo->ControllerInfo->MapRegisters);
KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);
/*
* Compute starting CHS
*/
if(RWComputeCHS(DriveInfo, DiskByteOffset+TransferByteOffset, &Cylinder, &Head, &StartSector) != STATUS_SUCCESS)
{
DPRINT("floppy: ReadWritePassive(): unable to compute CHS; completing with STATUS_UNSUCCESSFUL\n");
RWFreeAdapterChannel(DriveInfo->ControllerInfo->AdapterObject);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
StopMotor(DriveInfo->ControllerInfo);
return;
}
/*
* Seek to the right track
*/
if(!DriveInfo->ControllerInfo->ImpliedSeeks)
{
if(RWSeekToCylinder(DriveInfo, Cylinder) != STATUS_SUCCESS)
{
DPRINT("floppy: ReadWritePassive(): unable to seek; completing with STATUS_UNSUCCESSFUL\n");
RWFreeAdapterChannel(DriveInfo->ControllerInfo->AdapterObject);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
StopMotor(DriveInfo->ControllerInfo);
return ;
}
}
/*
* Compute last sector
*
* We can only ask for a transfer up to the end of the track. Then we have to re-seek and do more.
* TODO: Support the MT bit
*/
DPRINT("floppy: ReadWritePassive(): computing number of sectors to transfer (StartSector 0x%x): ", StartSector);
/* 1-based sector number */
if( (((DriveInfo->DiskGeometry.TracksPerCylinder - Head) * DriveInfo->DiskGeometry.SectorsPerTrack - StartSector) + 1 ) <
(Length - TransferByteOffset) / DriveInfo->DiskGeometry.BytesPerSector)
{
CurrentTransferSectors = (UCHAR)((DriveInfo->DiskGeometry.TracksPerCylinder - Head) * DriveInfo->DiskGeometry.SectorsPerTrack - StartSector) + 1;
}
else
{
CurrentTransferSectors = (UCHAR)((Length - TransferByteOffset) / DriveInfo->DiskGeometry.BytesPerSector);
}
DPRINT("0x%x\n", CurrentTransferSectors);
CurrentTransferBytes = CurrentTransferSectors * DriveInfo->DiskGeometry.BytesPerSector;
/*
* Adjust to map registers
* BUG: Does this take into account page crossings?
*/
DPRINT("floppy: ReadWritePassive(): Trying to transfer 0x%x bytes\n", CurrentTransferBytes);
ASSERT(CurrentTransferBytes);
if(BYTES_TO_PAGES(CurrentTransferBytes) > DriveInfo->ControllerInfo->MapRegisters)
{
CurrentTransferSectors = (UCHAR)((DriveInfo->ControllerInfo->MapRegisters * PAGE_SIZE) /
DriveInfo->DiskGeometry.BytesPerSector);
CurrentTransferBytes = CurrentTransferSectors * DriveInfo->DiskGeometry.BytesPerSector;
DPRINT("floppy: ReadWritePassive: limiting transfer to 0x%x bytes (0x%x sectors) due to map registers\n",
CurrentTransferBytes, CurrentTransferSectors);
}
/* set up this round's dma operation */
/* param 2 is ReadOperation --> opposite of WriteToDevice that IoMapTransfer takes. BAD MS. */
KeFlushIoBuffers(Irp->MdlAddress, !WriteToDevice, TRUE);
IoMapTransfer(DriveInfo->ControllerInfo->AdapterObject, Irp->MdlAddress,
DriveInfo->ControllerInfo->MapRegisterBase,
(PUCHAR)((ULONG_PTR)MmGetMdlVirtualAddress(Irp->MdlAddress) + TransferByteOffset),
&CurrentTransferBytes, WriteToDevice);
/*
* Read or Write
*/
KeClearEvent(&DriveInfo->ControllerInfo->SynchEvent);
/* Issue the read/write command to the controller. Note that it expects the opposite of WriteToDevice. */
if(HwReadWriteData(DriveInfo->ControllerInfo, !WriteToDevice, DriveInfo->UnitNumber, Cylinder, Head, StartSector,
DriveInfo->BytesPerSectorCode, DriveInfo->DiskGeometry.SectorsPerTrack, Gap, 0xff) != STATUS_SUCCESS)
{
DPRINT("floppy: ReadWritePassive(): HwReadWriteData returned failure; unable to read; completing with STATUS_UNSUCCESSFUL\n");
RWFreeAdapterChannel(DriveInfo->ControllerInfo->AdapterObject);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
StopMotor(DriveInfo->ControllerInfo);
return ;
}
DPRINT("floppy: ReadWritePassive(): HwReadWriteData returned -- waiting on event\n");
/*
* At this point, we block and wait for an interrupt
* FIXME: this seems to take too long
*/
WaitForControllerInterrupt(DriveInfo->ControllerInfo);
/* Read is complete; flush & free adapter channel */
IoFlushAdapterBuffers(DriveInfo->ControllerInfo->AdapterObject, Irp->MdlAddress,
DriveInfo->ControllerInfo->MapRegisterBase,
(PVOID)((ULONG_PTR)MmGetMdlVirtualAddress(Irp->MdlAddress) + TransferByteOffset),
CurrentTransferBytes, WriteToDevice);
/* Read the results from the drive */
if(HwReadWriteResult(DriveInfo->ControllerInfo) != STATUS_SUCCESS)
{
DPRINT("floppy: ReadWritePassive(): HwReadWriteResult returned failure; unable to read; completing with STATUS_UNSUCCESSFUL\n");
HwDumpRegisters(DriveInfo->ControllerInfo);
RWFreeAdapterChannel(DriveInfo->ControllerInfo->AdapterObject);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
StopMotor(DriveInfo->ControllerInfo);
return ;
}
TransferByteOffset += CurrentTransferBytes;
}
RWFreeAdapterChannel(DriveInfo->ControllerInfo->AdapterObject);
/* That's all folks! */
DPRINT("floppy: ReadWritePassive(): success; Completing with STATUS_SUCCESS\n");
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = Length;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
StopMotor(DriveInfo->ControllerInfo);
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?