Esempio n. 1
0
File: dma.c Progetto: GYGit/reactos
static IO_ALLOCATION_ACTION NTAPI SoundProgramDMA(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID MapRegisterBase,
    IN PVOID Context)
{
    PDEVICE_EXTENSION Device = DeviceObject->DeviceExtension;
    ULONG zzz;
    PUCHAR VirtualAddress = (PUCHAR) MmGetMdlVirtualAddress(Device->Mdl);

    DPRINT("IoMapTransfer\n");
    IoMapTransfer(Device->Adapter,
                    Device->Mdl,
                    MapRegisterBase,
                    (PUCHAR) MmGetMdlVirtualAddress(Device->Mdl),
                    &Device->BufferSize,    // is this right?
                    TRUE);

    DPRINT("VBuffer == 0x%x (really 0x%x?) Bufsize == %u\n", Device->VirtualBuffer, MmGetPhysicalAddress(Device->VirtualBuffer), Device->BufferSize);

    DPRINT("Writing %u bytes of garbage...\n", Device->BufferSize);
    // Write some garbage:
    for (zzz = 0; zzz < Device->BufferSize; zzz ++)
        *(VirtualAddress + zzz) = (UCHAR) zzz % 200;

    DPRINT("done\n");

    KeSetEvent(Context, 0, FALSE);

    return KeepObject;
}
Esempio n. 2
0
///////////////////////////////////////////////////////////////////////////////
//
//  OsrAdapterControlWrite
//
//    This is routine is called by the I/O Manager when the Adapter resources
//    (such as map registers) requested by the OsrStartWriteIrp function are
//    available for our use.
//
//  INPUTS:
//
//      DeviceObject - Address of the DEVICE_OBJECT for our device.
//  
//      MapRegisterBase - Base address of the Map registers that have been
//                      reserved by the I/O Manager and HAL for our use.
//
//      Context - address of the Write Irp for the operation to be started on the 
//              device.
//
//  OUTPUTS:
//
//      None.
//
//  RETURNS:
//
//    DeallocateObjectKeepRegisters - indicates that the map registers that
//            were allocated to us should not be deallocated at this time.
//            We will deallocate them with the Read operation completes.
//
//  IRQL:
//
//    This routine is called at IRQL_DISPATCH_LEVEL.
//
//  NOTES:
//
///////////////////////////////////////////////////////////////////////////////
IO_ALLOCATION_ACTION 
OsrAdapterControlWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP NotUsed, 
                                  IN PVOID MapRegisterBase, IN PVOID Context)
{
    PIRP irp = (PIRP) Context;
    PIO_STACK_LOCATION ioStack;
    POSR_DEVICE_EXT devExt;
    PUCHAR baseVa;

#if DBG
    DbgPrint("AdapterControlWrite: Irp = 0x%0x\n", irp);
#endif

    devExt = DeviceObject->DeviceExtension;

    ioStack = IoGetCurrentIrpStackLocation(irp);

    devExt->WriteLength = ioStack->Parameters.Write.Length - devExt->WriteSoFar;

#if DBG
    DbgPrint("AdapterControlWrite: Length remaining = %d. \n", devExt->WriteLength);
#endif

    //
    // Get set-up for the transfer
    //
    devExt->WriteMapRegBase = MapRegisterBase;

    baseVa = MmGetMdlVirtualAddress(irp->MdlAddress);

    devExt->WriteStartingOffset =  devExt->WriteSoFar;

    //
    // Get the base address and length of the segment to write.
    //
    devExt->WritePaToDevice = IoMapTransfer(devExt->WriteAdapter,
                                   irp->MdlAddress,
                                   MapRegisterBase,
                                   baseVa+(devExt->WriteSoFar),
                                   &devExt->WriteLength,
                                   TRUE);      // WriteToDevice

    //
    // Update the length transfered so far
    //
    devExt->WriteSoFar += devExt->WriteLength;

    //
    // Put the request on the device
    //
    (VOID)KeSynchronizeExecution(devExt->InterruptObject,
                            OsrStartWriteOnDevice,
                            DeviceObject);

    return(DeallocateObjectKeepRegisters);
}        
Esempio n. 3
0
IO_ALLOCATION_ACTION
pVideoPortBuildScatterGather(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP           pIrp,
    IN PVOID          MapRegisterBase,
    IN PVOID          Context
    )

/*++

Routine Description:

    This function is called by the I/O system when an adapter object and map
    registers have been allocated.  This routine then builds a scatter/gather
    list for use by the miniport driver.  Next it sets the timeout and
    the current pIrp for the logical unit.  Finally it calls the miniport
    StartIo routine.  Once that routines complete, this routine will return
    requesting that the adapter be freed and but the registers remain allocated.
    The registers will be freed when the request completes.

Arguments:

    DeviceObject - Supplies a pointer to the port driver device object.

    pIrp - Supplies a pointer to the current Irp.

    MapRegisterBase - Supplies a context pointer to be used with calls the
        adapter object routines.

    Context - Supplies a pointer to the PDMA_PARAMETERS data structure.

Return Value:

    Returns DeallocateObjectKeepRegisters so that the adapter object can be
        used by other logical units.

--*/

{
    PDMA_PARAMETERS             pIoVrb          = Context;
    PDEVICE_EXTENSION           deviceExtension = DeviceObject->DeviceExtension;
    BOOLEAN                     writeToDevice;
    PIO_STACK_LOCATION          irpStack;
    PPUBLIC_VIDEO_REQUEST_BLOCK pPVRB;
    PVRB_SG                     pScatterList;
    PCCHAR                      dataVirtualAddress;
    ULONG                       totalLength;
    KIRQL                       currentIrql;

    irpStack = IoGetCurrentIrpStackLocation(pIrp);
    pPVRB = (PPUBLIC_VIDEO_REQUEST_BLOCK)irpStack->Parameters.Others.Argument1;

    //
    // Save the MapRegisterBase for later use to deallocate the map registers.
    //

    pIoVrb->pMapRegisterBase = MapRegisterBase;

    //
    // If scatter gather list not NULL, then miniport is reusing locked memory,
    // so no need to lock memory and build SG list.
    //

    if (pIoVrb->pScatterGather) {
        return(DeallocateObjectKeepRegisters);
    }

    //
    // Determine if scatter/gather list must come from pool.
    //

    if (pIoVrb->NumberOfMapRegisters > 17) {

        //
        // Allocate scatter/gather list from pool.
        //

        pIoVrb->pScatterGather =
            ExAllocatePool(NonPagedPool,
                           pIoVrb->NumberOfMapRegisters * sizeof(VRB_SG));

        if (pIoVrb->pScatterGather == NULL) {

            //
            // Beyond the point of return.
            //

            pIoVrb->pScatterGather =
                ExAllocatePool(NonPagedPoolMustSucceed,
                               pIoVrb->NumberOfMapRegisters * sizeof(VRB_SG));
        }

        //
        // Indicate scatter gather list came from pool.
        //

        pPVRB->VRBFlags |= VRB_FLAGS_SGLIST_FROM_POOL;

    } else {

        //
        // Use embedded scatter/gather list.
        //

        pIoVrb->pScatterGather = pIoVrb->SGList;
    }

    pScatterList = pIoVrb->pScatterGather;
    totalLength  = 0;

    //
    // Determine the virtual address of the buffer for the Io map transfers.
    //

    dataVirtualAddress = (PCCHAR)MmGetMdlVirtualAddress(pIrp->MdlAddress) +
                ((PCCHAR)pPVRB->vrp.InputBuffer - pIoVrb->DataOffset);

    //
    // Lock the users buffer down.
    //

    __try   {

        MmProbeAndLockPages(pIrp->MdlAddress,
                            KernelMode,
                            IoModifyAccess);

    } __except(EXCEPTION_EXECUTE_HANDLER) {

        IoFreeMdl(pIrp->MdlAddress);
        VideoPortDebugPrint(0,
                        "VideoPortIoStartRequest: MmProbeandLockPages exception\n");
    }

    //
    // Build the scatter/gather list by looping throught the transfer calling
    // I/O map transfer.
    //

    while (totalLength < pPVRB->vrp.InputBufferLength) {

        //
        // Request that the rest of the transfer be mapped.
        //

        pScatterList->Length = pPVRB->vrp.InputBufferLength - totalLength;

        //
        // Wacky deal: call IoMapTransfer with NULL PADAPTER_OBJECT to just
        // get the physical addresses.
        //

        pScatterList->PhysicalAddress = IoMapTransfer(NULL,
                                                     pIrp->MdlAddress,
                                                     MapRegisterBase,
                                                     (PCCHAR) dataVirtualAddress + totalLength,
                                                     &pScatterList->Length,
                                                     TRUE).LowPart;

        totalLength += pScatterList->Length;
        pScatterList++;
    }

    //
    // Update the active request count.
    //

    InterlockedIncrement( &deviceExtension->ActiveRequestCount );

    // BUGBUG: synchronize????
    //
    // Acquire the spinlock to protect the various structures.
    //

    KeAcquireSpinLock(&deviceExtension->SpinLock, &currentIrql);

    deviceExtension->SynchronizeExecution(
        deviceExtension->InterruptObject,
        pVideoPortStartIoSynchronized,
        DeviceObject
        );

    KeReleaseSpinLock(&deviceExtension->SpinLock, currentIrql);

    return(DeallocateObjectKeepRegisters);

}
Esempio n. 4
0
///////////////////////////////////////////////////////////////////////////////
//
//  OsrAdapterControlRead
//
//    This is routine is called by the I/O Manager when the Adapter resources
//    (such as map registers) requested by the OsrStartReadIrp function are
//    available for our use.
//
//  INPUTS:
//
//      DeviceObject - Address of the DEVICE_OBJECT for our device.
//  
//      MapRegisterBase - Base address of the Map registers that have been
//                        reserved for us use.
//
//       Context - address of the Read Irp for the operation to be started
//
//  OUTPUTS:
//
//      None.
//
//  RETURNS:
//
//    DeallocateObjectKeepRegisters - indicates that the Mapping Registers that
//              were allocated to us should not be deallocated at this time.  We
//              will deallocate them from the DpcForIsr when the Read completes.
//
//  IRQL:
//
//    This routine is called at IRQL_DISPATCH_LEVEL.
//
//  NOTES:
//
///////////////////////////////////////////////////////////////////////////////
IO_ALLOCATION_ACTION 
OsrAdapterControlRead(IN PDEVICE_OBJECT DeviceObject, IN PIRP NotUsed, 
                                  IN PVOID MapRegisterBase, IN PVOID Context)
{
    PIRP irp = (PIRP) Context;
    PIO_STACK_LOCATION ioStack;
    POSR_DEVICE_EXT devExt;
    PUCHAR baseVa;

#if DBG
    DbgPrint("AdapterControlRead: Irp = 0x%0x\n", irp);
    DbgPrint("AdapterControlRead: Map Register Base = 0x%0x\n", MapRegisterBase);
#endif

    devExt = DeviceObject->DeviceExtension;
    
    ioStack = IoGetCurrentIrpStackLocation(irp);

    devExt->ReadLength = ioStack->Parameters.Read.Length - devExt->ReadSoFar;

#if DBG
    DbgPrint("AdapterControlRead: Length remaining = %d. \n", devExt->ReadLength);
#endif

    //
    // Get set-up for the transfer
    //
    devExt->ReadMapRegBase = MapRegisterBase;

    devExt->ReadStartingOffset =  devExt->ReadSoFar;

    //
    // Get requestor's virtual address of the buffer.  This is used by
    // IoMapTransfer() as an index into the buffer to track the progress
    // of the map operation.
    //
    baseVa = MmGetMdlVirtualAddress(irp->MdlAddress);

    //
    // Get the logical base address and length of a fragment of the 
    // requestor's buffer.
    //
    // Even though we are a Busmaster device, our device does not support
    // scatter/gather.  Thus, we can only use a single base address and length
    // at a time.  If the requestor's buffer has more fragments, we will
    // do additional DMA operations (one for each fragment) until the entire
    // transfer has been completed.
    //
    devExt->ReadPaToDevice = IoMapTransfer(devExt->ReadAdapter,
                                   irp->MdlAddress,
                                   MapRegisterBase,
                                   baseVa+(devExt->ReadSoFar),
                                   &devExt->ReadLength,
                                   FALSE);  // FALSE = READ from device


    //
    // Track the length of the requestor's buffer we've read so far.
    //
    devExt->ReadSoFar += devExt->ReadLength;

    //
    // Start the request on the device -- Base Address and Length
    // of this fragment are stored in the device extension
    //
    (VOID)KeSynchronizeExecution(devExt->InterruptObject,
                            OsrStartReadOnDevice,
                            DeviceObject);

    return(DeallocateObjectKeepRegisters);
}        
Esempio n. 5
0
VOID
sndReStartDMA(
    IN PGLOBAL_DEVICE_INFO pGDI,
    IN int WhichBuffer
    )
/*++

Routine Description:

    Restart the DMA on a given channel

Arguments:

    pGDI -  Supplies pointer to global device info.
    WhichBuffer - which channel to use

Return Value:

    Returns FALSE

--*/
{
    ULONG length;


    length = pGDI->DmaHalfBufferSize;

    //
    // Increment count of pending interrupts.
    //

    pGDI->SoundHardware.TcInterruptsPending += 1;

    dprintf5("sndReStartDMA(): incremented pending interrupts %d",
             pGDI->SoundHardware.TcInterruptsPending);

    //
    // Program the DMA controller registers for the transfer
    // Set the direction of transfer by whether we're wave in or
    // wave out.
    //

    KeFlushIoBuffers( pGDI->pDMABufferMDL[WhichBuffer],
                     (pGDI->Usage == SoundInterruptUsageWaveIn),
                                         TRUE);

    dprintf4("sndReStartDMA(): calling IoMapTransfer BUFFER = %d", WhichBuffer);

    if (pGDI->Usage == SoundInterruptUsageWaveIn) {

        IoMapTransfer(pGDI->pAdapterObject[(WhichBuffer) ? 3 : 2],
                  pGDI->pDMABufferMDL[WhichBuffer],
                  pGDI->pMRB[WhichBuffer],
                  pGDI->DMABuffer[WhichBuffer].Buf,
                  &length,
                  (BOOLEAN)(pGDI->Usage != SoundInterruptUsageWaveIn));

    } else {

        IoMapTransfer(pGDI->pAdapterObject[WhichBuffer],
                  pGDI->pDMABufferMDL[WhichBuffer],
                  pGDI->pMRB[WhichBuffer],
                  pGDI->DMABuffer[WhichBuffer].Buf,
                  &length,
                  (BOOLEAN)(pGDI->Usage != SoundInterruptUsageWaveIn));

    }
}
Esempio n. 6
0
dVoid kdi_ProgramDMA
(
/* INPUT PARAMETERS:  */

	dVoidPtr		context,
	dBoolean		write_operation,
	dVoidPtr		phy_data_ptr,
	dUDWord		bytes_transferred_so_far,

/* UPDATE PARAMETERS: */

	dUDWordPtr	total_bytes_of_transfer_ptr

/* OUTPUT PARAMETERS: */

)
/* COMMENTS: *****************************************************************
 *
 * DEFINITIONS: *************************************************************/
{

/* DATA: ********************************************************************/

   PHYSICAL_ADDRESS val;
    KdiContextPtr   kdi_context = (KdiContextPtr)context;

/* CODE: ********************************************************************/


   kdi_LockUnlockDMA(kdi_context, dTRUE);

   //
   // Map the transfer through the DMA hardware.
   //

   KeFlushIoBuffers( phy_data_ptr, !write_operation, dTRUE );

/*
   DbgAddEntry(0x1234567a);
   DbgAddEntry((dUDWord)kdi_context->adapter_object);
   DbgAddEntry((dUDWord)phy_data_ptr);
   DbgAddEntry((dUDWord)kdi_context->map_register_base);
   DbgAddEntry((dUDWord) MmGetMdlVirtualAddress(phy_data_ptr)
            + bytes_transferred_so_far );
   DbgAddEntry(*total_bytes_of_transfer_ptr);
   DbgAddEntry(write_operation);
*/

   val = IoMapTransfer(
      kdi_context->adapter_object,
      phy_data_ptr,
      kdi_context->map_register_base,
      (dVoidPtr)( (dUDWord) MmGetMdlVirtualAddress((PMDL)phy_data_ptr)
            + bytes_transferred_so_far ),
      total_bytes_of_transfer_ptr,
      write_operation );
/*
   DbgAddEntry(val.HighPart);
   DbgAddEntry(val.LowPart);
   DbgAddEntry(*total_bytes_of_transfer_ptr);
*/
}
Esempio n. 7
0
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();

    TRACE_(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 && !(Stack->Flags & SL_OVERRIDE_VERIFY_VOLUME))
    {
        INFO_(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)
    {
        WARN_(FLOPPY, "ReadWritePassive(): unable to detect disk change; Completing with STATUS_UNSUCCESSFUL\n");
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        StopMotor(DriveInfo->ControllerInfo);
        return;
    }

    if(DiskChanged)
    {
        INFO_(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)
        {
            WARN_(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)
        {
            WARN_(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 Odyssey 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)
    {
        WARN_(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;

        INFO_(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)
        {
            WARN_(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)
            {
                WARN_(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
         */
        INFO_(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);
        }

        INFO_(FLOPPY, "0x%x\n", CurrentTransferSectors);

        CurrentTransferBytes = CurrentTransferSectors * DriveInfo->DiskGeometry.BytesPerSector;

        /*
         * Adjust to map registers
         * BUG: Does this take into account page crossings?
         */
        INFO_(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;

            INFO_(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,
                      (PVOID)((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)
        {
            WARN_(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 ;
        }

        INFO_(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)
        {
            WARN_(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! */
    INFO_(FLOPPY, "ReadWritePassive(): success; Completing with STATUS_SUCCESS\n");
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = Length;
    IoCompleteRequest(Irp, IO_DISK_INCREMENT);
    StopMotor(DriveInfo->ControllerInfo);
}