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 + -
显示快捷键?