Example #1
0
File: dma.c Project: GYGit/reactos
BOOLEAN CreateDMA(PDEVICE_OBJECT DeviceObject)
{
    DEVICE_DESCRIPTION Desc;
    ULONG MappedRegs = 0;
    PDEVICE_EXTENSION Device = DeviceObject->DeviceExtension;
    KEVENT DMAEvent;
    KIRQL OldIrql;

    // Buffersize should already be set but it isn't yet !
    Device->BufferSize = SB_BUFSIZE;
    DPRINT("Bufsize == %u\n", Device->BufferSize);

    RtlZeroMemory(&Desc, sizeof(DEVICE_DESCRIPTION));

    // Init memory!
    Desc.Version = DEVICE_DESCRIPTION_VERSION;
    Desc.Master = FALSE;    // Slave
    Desc.ScatterGather = FALSE; // Don't think so anyway
    Desc.DemandMode = FALSE;    // == !SingleModeDMA
    Desc.AutoInitialize = TRUE; // ?
    Desc.Dma32BitAddresses = FALSE; // I don't think we can
    Desc.IgnoreCount = FALSE; // Should be OK
    Desc.Reserved1 = 0;
//    Desc.Reserved2 = 0;
    Desc.BusNumber = 0;
    Desc.DmaChannel = Device->DMA;    // Our channel :)
    Desc.InterfaceType = Isa;   // (BusType == MicroChannel) ? MicroChannel : Isa;
    Desc.DmaWidth = 0;    // hmm... 8 bits?
    Desc.DmaSpeed = 0;     // double hmm (Compatible it should be)
    Desc.MaximumLength = Device->BufferSize;
//    Desc.MinimumLength = 0;
    Desc.DmaPort = 0;

    DPRINT("Calling HalGetAdapter(), asking for %d mapped regs\n", MappedRegs);

    Device->Adapter = HalGetAdapter(&Desc, &MappedRegs);

    DPRINT("Called\n");

    if (! Device->Adapter)
    {
        DPRINT("HalGetAdapter() FAILED\n");
        return FALSE;
    }

    DPRINT("Bufsize == %u\n", Device->BufferSize);

    if (MappedRegs < BYTES_TO_PAGES(Device->BufferSize))
    {
        DPRINT("Could only allocate %u mapping registers\n", MappedRegs);

        if (MappedRegs == 0)
            return FALSE;

        Device->BufferSize = MappedRegs * PAGE_SIZE;
        DPRINT("Bufsize == %u\n", Device->BufferSize);
    }

    DPRINT("Allocated %u mapping registers\n", MappedRegs);

    // Check if we already have memory here...

    // Check to make sure we're >= minimum

    DPRINT("Allocating buffer\n");

    DPRINT("Bufsize == %u\n", Device->BufferSize);

    Device->VirtualBuffer = HalAllocateCommonBuffer(Device->Adapter, Device->BufferSize,
                                                &Device->Buffer, FALSE);

    // For some reason BufferSize == 0 here?!
//    DPRINT("Buffer == 0x%x Bufsize == %u\n", Device->Buffer, Device->BufferSize);
    DPRINT("Bufsize == %u,", Device->BufferSize);
    DPRINT("Buffer == 0x%x\n", Device->Buffer);

    if (! Device->VirtualBuffer)
    {
        DPRINT("Could not allocate buffer :(\n");
        // should try again with smaller buffer...
        return FALSE;
    }

//    DPRINT("Buffer == 0x%x Bufsize == %u\n", Device->Buffer, Device->BufferSize);
    DPRINT("Bufsize == %u,", Device->BufferSize);
    DPRINT("Buffer == 0x%x\n", Device->Buffer);

    DPRINT("Calling IoAllocateMdl()\n");
    Device->Mdl = IoAllocateMdl(Device->VirtualBuffer, Device->BufferSize, FALSE, FALSE, NULL);
    DPRINT("Bufsize == %u\n", Device->BufferSize);

    // IS THIS RIGHT:
    if (! Device->Mdl)
    {
        DPRINT("IoAllocateMdl() FAILED\n");
        // Free the HAL buffer
        return FALSE;
    }

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

    DPRINT("Calling MmBuildMdlForNonPagedPool\n");
    MmBuildMdlForNonPagedPool(Device->Mdl);

    DPRINT("Bufsize == %u\n", Device->BufferSize);

    // part II:
    KeInitializeEvent(&DMAEvent, SynchronizationEvent, FALSE);
    // Raise IRQL
    KeRaiseIrql(DISPATCH_LEVEL,&OldIrql);
    IoAllocateAdapterChannel(Device->Adapter, DeviceObject,
                            BYTES_TO_PAGES(Device->BufferSize),
                            SoundProgramDMA, &DMAEvent);
    // Lower IRQL
    KeLowerIrql(OldIrql);
    DPRINT("VBuffer == 0x%x Bufsize == %u\n", Device->VirtualBuffer, Device->BufferSize);
    KeWaitForSingleObject(&DMAEvent, Executive, KernelMode, FALSE, NULL);


//    if (MappedRegs == 0)
//        MappedRegs = 2;
//    else
//        MappedRegs ++;


//    Status = IoAllocateAdapterChannel(
//                    Adapter,
//                    DeviceObject,
//                    MappedRegs,
//                    CALLBACK,
//                    DeviceObject); // Context
    return TRUE;
}
Example #2
0
BOOLEAN
VideoPortDoDma(
    IN PVOID                        HwDeviceExtension,
    IN PVIDEO_REQUEST_PACKET        pVrp
    )
{
    PDEVICE_EXTENSION            deviceExtension =
                                ((PDEVICE_EXTENSION) HwDeviceExtension) - 1;
    PPUBLIC_VIDEO_REQUEST_BLOCK pPVRB;
    PDMA_PARAMETERS             pIoVrb;
    PIRP                        pIrp;

    GET_PVRB_FROM_PVRP(pPVRB, pVrp);

    pIoVrb = pVideoPortGetDmaParameters(deviceExtension, pPVRB);

    if (!pIoVrb) {

        //
        // Can't get DmaParameter storage. set flag and return
        //

        deviceExtension->VRBFlags |= INSUFFICIENT_DMA_RESOURCES;
        return FALSE;
    }

    pIrp                              = pPVRB->pIrp;
    deviceExtension->MapDmaParameters = pIoVrb;

    //
    // Get Mdl for user buffer.
    //

    if (!pPVRB || !IoAllocateMdl(pPVRB->vrp.InputBuffer,
                       pPVRB->vrp.InputBufferLength,
                       FALSE,
                       FALSE,
                       pIrp)) {

            VideoPortDebugPrint(0,
                        "VideoPortIoStartRequest: Can't allocate Mdl\n");

            pPVRB->vrp.StatusBlock->Status = VRB_STATUS_INVALID_REQUEST;

            VideoPortNotification(RequestComplete,
                                 deviceExtension,
                                 pIoVrb);

            VideoPortNotification(NextRequest,
                                 deviceExtension);

            //
            // Queue a DPC to process the work that was just indicated.
            //

            IoRequestDpc(deviceExtension->DeviceObject, NULL, NULL);
            return FALSE;
    }

    //
    // Save the Mdl virtual address
    //

    pIoVrb->DataOffset = MmGetMdlVirtualAddress(pIrp->MdlAddress);

    //
    // Determine if the device needs mapped memory.
    //

    if (deviceExtension->bMapBuffers) {

        if (pIrp->MdlAddress) {
            pIoVrb->DataOffset = MmGetSystemAddressForMdl(pIrp->MdlAddress);

            pPVRB->vrp.InputBuffer  = ((PUCHAR)pIoVrb->DataOffset) +
                                 (ULONG)(((PUCHAR)pPVRB->vrp.InputBuffer) - ((PUCHAR)MmGetMdlVirtualAddress(pIrp->MdlAddress)));
        }
    }

    if (deviceExtension->DmaAdapterObject) {

        //
        // If the buffer is not mapped then the I/O buffer must be flushed
        // to aid in cache coherency.
        //

        KeFlushIoBuffers(pIrp->MdlAddress, TRUE, TRUE);
    }

    //
    // Determine if this adapter needs map registers
    //

    if (deviceExtension->bMasterWithAdapter) {

        //
        // Calculate the number of map registers needed for this transfer.
        // Note that this may be recalculated if the miniport really wants
        // to do DMA
        //

        pIoVrb->NumberOfMapRegisters = ADDRESS_AND_SIZE_TO_SPAN_PAGES(
                pPVRB->vrp.InputBuffer,
                pPVRB->vrp.InputBufferLength
                );
    }

    //
    // The miniport may have requested too big of a buffer, so iteratively
    // chop it in half until we find one we can do. This changes the
    // vrp.InputBufferLength, which the miniport must check to see how much
    // is actually sent and queue up the remainder.
    //

    while (pIoVrb->NumberOfMapRegisters >
        deviceExtension->Capabilities.MaximumPhysicalPages) {

        pPVRB->vrp.InputBufferLength /= 2;

        pIoVrb->NumberOfMapRegisters = ADDRESS_AND_SIZE_TO_SPAN_PAGES(
            pPVRB->vrp.InputBuffer,
            pPVRB->vrp.InputBufferLength
            );

    }

    //
    // Allocate the adapter channel with sufficient map registers
    // for the transfer.
    //

    IoAllocateAdapterChannel(
        deviceExtension->DmaAdapterObject,  // AdapterObject
        deviceExtension->DeviceObject,      // DeviceObject
        pIoVrb->NumberOfMapRegisters,       // NumberOfMapRegisters
        pVideoPortBuildScatterGather,       // ExecutionRoutine (Must return DeallocateObjectKeepRegisters)
        pIoVrb);                            // Context

    //
    // The execution routine called via IoAllocateChannel will do the
    // rest of the work so just return.
    //

    return TRUE;

}
Example #3
0
///////////////////////////////////////////////////////////////////////////////
//
//  OsrStartWriteIrp
//
//    This is routine is called by the OsrWrite and DpcForIsr routine to
//    start a new Write operation.  The request started is the IRP located
//    at the head of the write queue.
//
//  INPUTS:
//
//      DeviceObject - Address of the DEVICE_OBJECT for our device.
//  
//      Irp - Address of the IRP representing the IRP_MJ_WRITE call.
//
//  OUTPUTS:
//
//      None.
//
//  RETURNS:
//
//      None.
//
//  IRQL:
//
//      This routine is called at IRQL_DISPATCH_LEVEL.
//
//  NOTES:
//      *** Called (and returns) with the WriteQueueLock held.
//
///////////////////////////////////////////////////////////////////////////////
VOID
OsrStartWriteIrp(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    POSR_DEVICE_EXT devExt = DeviceObject->DeviceExtension;
    PIO_STACK_LOCATION ioStack;
    ULONG mapRegsNeeded;
    
    ioStack = IoGetCurrentIrpStackLocation(Irp);

    //
    // In progress IRPs cannot be cancelled
    //
    IoSetCancelRoutine(Irp, NULL);

#if DBG
    DbgPrint("OsrWrite: Transfer length %d.\n",
                                ioStack->Parameters.Write.Length);
#endif

    //
    // We're starting a request... therefore, we clear the StopEvent
    // flag.
    //
    KeClearEvent(&devExt->StopEvent);

    //
    // There is no in-progress request.  Start this request on the
    // device.
    //
    devExt->CurrentWriteIrp = Irp;

    devExt->WriteTotalLength = ioStack->Parameters.Write.Length;

    devExt->WriteSoFar = 0;

    devExt->WriteStartingOffset = 0;

    //
    // Start the watchdog timer on this IRP
    //
    (ULONG)Irp->Tail.Overlay.DriverContext[0] = OSR_WATCHDOG_INTERVAL;

    //
    // Since we're about to initiate a DMA operation, ensure the user's data
    // buffer is flushed from the cache back into memory, on processors that
    // are non-DMA cache coherent.
    //
    KeFlushIoBuffers(Irp->MdlAddress, FALSE, TRUE);

    //
    // Determine the number of map registers we'll need for this transfer
    //
    mapRegsNeeded = 
        ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(Irp->MdlAddress),
                                        ioStack->Parameters.Write.Length);
        
#if DBG
    DbgPrint("StartWrite: %d. map regs needed\n", mapRegsNeeded);
#endif

    //
    // If the number of map registers required for this transfer exceeds the
    // maximum we're allowed to use (as reported to us from HalGetAdapter() ),
    // we'll need to limit ourselves to the maximum we're allowed.
    //
    devExt->MapRegsThisWrite = ((mapRegsNeeded > devExt->WriteMapRegsGot) ? 
                              devExt->WriteMapRegsGot : mapRegsNeeded);

#if DBG
    DbgPrint("StartWrite: %d. map regs this xfer\n", devExt->MapRegsThisWrite);
#endif

    //
    // Ready to GO! Allocate the appropriate Adapter Object and map registers.
    //
    IoAllocateAdapterChannel(devExt->WriteAdapter,
                             DeviceObject, 
                             devExt->MapRegsThisWrite,
                             OsrAdapterControlWrite,
                             Irp);
}
Example #4
0
///////////////////////////////////////////////////////////////////////////////
//
//  OsrStartReadIrp
//
//    This is routine is called by the OsrRead and Dpc routine in order to
//    begin a new Read operation.
//
//  INPUTS:
//
//      DeviceObject - Address of the DEVICE_OBJECT for our device.
//  
//      Irp - Address of the IRP representing the IRP_MJ_READ call.
//
//  OUTPUTS:
//
//      None.
//
//  RETURNS:
//
//      None.
//
//  IRQL:
//
//      This routine is called at IRQL_DISPATCH_LEVEL.
//
//  NOTES:
//      *** Called (and returns) with the WriteQueueLock held.
//
///////////////////////////////////////////////////////////////////////////////
VOID
OsrStartReadIrp(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    POSR_DEVICE_EXT devExt = DeviceObject->DeviceExtension;
    PIO_STACK_LOCATION ioStack;
    ULONG mapRegsNeeded;

    ioStack = IoGetCurrentIrpStackLocation(Irp);

    //
    // In progress IRPs cannot be cancelled
    //
    IoSetCancelRoutine(Irp, NULL);

#if DBG
    DbgPrint("OsrRead: Transfer length %d.\n",
                                ioStack->Parameters.Read.Length);
#endif

    //
    // We're starting a request... therefore, we clear the StopEvent
    // flag.
    //
    KeClearEvent(&devExt->StopEvent);

    //
    // There is no in-progress request.  Start this request on the
    // device.
    //
    devExt->CurrentReadIrp = Irp;

    devExt->ReadTotalLength = ioStack->Parameters.Read.Length;

    devExt->ReadSoFar = 0;

    devExt->ReadStartingOffset = 0;

    //
    // Start the watchdog timer on this IRP
    //
    (ULONG)Irp->Tail.Overlay.DriverContext[0] = OSR_WATCHDOG_INTERVAL;

    //
    // Flush the requestor's buffer back from cache on non-dma coherent
    // machines.
    //
    KeFlushIoBuffers(Irp->MdlAddress, TRUE, TRUE);

    //
    // Determine number of map registers required by this read
    //
    mapRegsNeeded = 
        ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(Irp->MdlAddress),
                                        ioStack->Parameters.Read.Length);
        
#if DBG
    DbgPrint("StartReadIrp: %d. map regs needed\n", mapRegsNeeded);
#endif

    //
    // Limit the number of map registers used to the maximum allowed by the
    // HAL.  We determined this max when we called HalGetAdapter() during
    // our DriverEntry processing.
    //
    devExt->MapRegsThisRead = ((mapRegsNeeded > devExt->ReadMapRegsGot) ? 
                              devExt->ReadMapRegsGot : mapRegsNeeded);

#if DBG
    DbgPrint("StartReadIrp: %d. map regs this xfer\n", devExt->MapRegsThisRead);
#endif

    IoAllocateAdapterChannel(devExt->ReadAdapter,
                             DeviceObject, 
                             devExt->MapRegsThisRead,
                             OsrAdapterControlRead,
                             Irp);


}
Example #5
0
VOID
sndStartDMA(
    IN    PGLOBAL_DEVICE_INFO pGDI,
    IN    int PlayBack
)
/*++

Routine Description:

    Allocate the adapter channel (this had better not wait !)

Arguments:

    pGDI - Pointer to the global device data

Return Value:

    None

--*/
{
    ULONG DataLong;

    //
    // Test if DMA is already running
    //

    ASSERT(pGDI->DMABusy == FALSE);

    pGDI->DMABusy = TRUE;


    dprintf5("sndStartDMA()");

    //
    // Program the DMA hardware (isn't this a bit illegal ?)
    //

    DataLong = 0;

    ((PDMA_CHANNEL_MODE)(&DataLong))->AccessTime = ACCESS_200NS;

    if (pGDI->BytesPerSample == 1) {
        ((PDMA_CHANNEL_MODE)(&DataLong))->TransferWidth = WIDTH_8BITS;
    } else {
        ((PDMA_CHANNEL_MODE)(&DataLong))->TransferWidth = WIDTH_16BITS;
    }


    if (PlayBack){

        ((PDMA_CHANNEL_MODE)(&DataLong))->BurstMode = 0x01;

        WRITE_REGISTER_ULONG(&DMA_CONTROL->Channel[SOUND_CHANNEL_A].Mode.Long,
                         DataLong);
        WRITE_REGISTER_ULONG(&DMA_CONTROL->Channel[SOUND_CHANNEL_B].Mode.Long,
                         DataLong);

    } else {

        WRITE_REGISTER_ULONG(&DMA_CONTROL->Channel[SOUND_CHANNEL_A+2].Mode.Long,
                         DataLong);
        WRITE_REGISTER_ULONG(&DMA_CONTROL->Channel[SOUND_CHANNEL_B+2].Mode.Long,
                         DataLong);

    }

    //
    // Allocate an adapter channel.  When the system allocates
    // the channel, processing will continue in the sndProgramDMA
    // routine below.
    //


    if (PlayBack) {
        dprintf4("Allocating adapter channel (buffer = 0)");
        IoAllocateAdapterChannel(pGDI->pAdapterObject[0],
                             pGDI->pWaveOutDevObj,
                             BYTES_TO_PAGES(pGDI->DmaHalfBufferSize),
                             sndProgramDMA,
                             (PVOID)0);         // Context
    } else {
        dprintf4("Allocating adapter channel (buffer = 2)");
        IoAllocateAdapterChannel(pGDI->pAdapterObject[2],
                             pGDI->pWaveInDevObj,
                             BYTES_TO_PAGES(pGDI->DmaHalfBufferSize),
                             sndProgramDMA,
                             (PVOID)0);         // Context
    }

    //
    // Execution will continue in sndProgramDMA when the
    // adapter has been allocated
    //

}
Example #6
0
IO_ALLOCATION_ACTION
sndProgramDMA(
    IN    PDEVICE_OBJECT pDO,
    IN    PIRP pIrp,
    IN    PVOID pMRB,
    IN    PVOID Context
)
/*++

Routine Description:

    This routine is executed when an adapter channel is allocated
    for our DMA needs.

Arguments:

    pDO     - Device object
    pIrp    - IO request packet
    pMRB    -
    Context - Which buffer are we using


Return Value:

    Tell the system what to do with the adapter object

--*/
{
    PGLOBAL_DEVICE_INFO pGDI;
    int WhichBuffer;

    UNREFERENCED_PARAMETER(pIrp);

    WhichBuffer = (int) Context;

    pGDI = ((PLOCAL_DEVICE_INFO)pDO->DeviceExtension)->pGlobalInfo;

    pGDI->pMRB[WhichBuffer] = pMRB;

    sndReStartDMA(pGDI, WhichBuffer);

    //
    // return a value that says we want to keep the channel
    // and map registers.
    //

    if (WhichBuffer == 0) {

        //
        // Do the other one.
        //


        if (pGDI->Usage == SoundInterruptUsageWaveIn) {

            dprintf4("Allocating adapter channel (buffer = 3)");
            IoAllocateAdapterChannel(pGDI->pAdapterObject[3],
                pGDI->pWaveInDevObj,
                BYTES_TO_PAGES(pGDI->DmaHalfBufferSize),
                sndProgramDMA,
                (PVOID)1);              // next buffer


        } else {

            dprintf4("Allocating adapter channel (buffer = 1)");
            IoAllocateAdapterChannel(pGDI->pAdapterObject[1],
                pGDI->pWaveOutDevObj,
                BYTES_TO_PAGES(pGDI->DmaHalfBufferSize),
                sndProgramDMA,
                (PVOID)1);              // next buffer
        }

        //
        // Execution will continue in sndProgramDMA when the
        // adapter has been allocated (AGAIN)
        //

    } else {

        //
        // Now program the hardware on the card to begin the transfer.
        // Note that this must be synchronized with the isr
        //

        dprintf4("Calling (sync) sndInitiate");
        KeSynchronizeExecution(pGDI->pInterrupt,
                               pGDI->StartDMA,
                               pGDI);

        //
        // Execution continues in the SoundInitiate routine
        //
    }

    return KeepObject;
}
Example #7
0
dVoid kdi_LockUnlockDMA
(
/* INPUT PARAMETERS:  */

	KdiContextPtr kdi_context,
	dBoolean lock

/* UPDATE PARAMETERS: */

/* OUTPUT PARAMETERS: */

)

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

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

   KIRQL old_irql;

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

	if (kdi_context->adapter_object) {

   	if (lock) {

         if (!kdi_context->adapter_locked) {

            /* Allocate an adapter channel for the I/O. */

            (dVoid) KeResetEvent(
               &kdi_context->allocate_adapter_channel_event );

            KeRaiseIrql( DISPATCH_LEVEL, &old_irql );


            IoAllocateAdapterChannel(
               kdi_context->adapter_object,
               kdi_context->device_object,
               kdi_context->number_of_map_registers,
               kdi_AllocateAdapterChannel,
               kdi_context );

            KeLowerIrql( old_irql );

            /* Wait for the adapter to be allocated.  No */
            /* timeout; we trust the system to do it */
            /* properly - so KeWaitForSingleObject can't */
            /* return an error. */

            (dVoid) KeWaitForSingleObject(
               &kdi_context->allocate_adapter_channel_event,
               Executive,
               KernelMode,
               dFALSE,
               (dSDDWordPtr) dNULL_PTR );

            kdi_context->adapter_locked = dTRUE;
         }

   	} else {

         if (kdi_context->adapter_locked) {

            /* Free the adapter channel that we just used. */

            KeRaiseIrql( DISPATCH_LEVEL, &old_irql );

            IoFreeAdapterChannel( kdi_context->adapter_object );

            KeLowerIrql( old_irql );

            kdi_context->adapter_locked = dFALSE;


         }
   	}
	}


	return;
}
Example #8
0
NDIS_STATUS
NdisMAllocateMapRegisters(
	IN	NDIS_HANDLE				MiniportAdapterHandle,
	IN	UINT					DmaChannel,
	IN	BOOLEAN					Dma32BitAddresses,
	IN	ULONG					PhysicalMapRegistersNeeded,
	IN	ULONG					MaximumPhysicalMapping
	)

/*++

Routine Description:

	Allocates map registers for bus mastering devices.

Arguments:

	MiniportAdapterHandle - Handle passed to MiniportInitialize.

	PhysicalMapRegistersNeeded - The maximum number of map registers needed
		by the Miniport at any one time.

	MaximumPhysicalMapping - Maximum length of a buffer that will have to be mapped.

Return Value:

	None.

--*/

{
	//
	// Convert the handle to our internal structure.
	//
	PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK) MiniportAdapterHandle;

	//
	// This is needed by HalGetAdapter.
	//
	DEVICE_DESCRIPTION DeviceDescription;

	//
	// Returned by HalGetAdapter.
	//
	ULONG MapRegistersAllowed;

	//
	// Returned by HalGetAdapter.
	//
	PADAPTER_OBJECT AdapterObject;

	//
	// Map registers needed per channel.
	//
	ULONG MapRegistersPerChannel;

	NTSTATUS NtStatus;

	KIRQL OldIrql;

	UINT i;

	LARGE_INTEGER TimeoutValue;

	//
	// If the device is a busmaster, we get an adapter
	// object for it.
	// If map registers are needed, we loop, allocating an
	// adapter channel for each map register needed.
	//

	if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_BUS_MASTER) &&
		(Miniport->BusType != (NDIS_INTERFACE_TYPE)-1) &&
		(Miniport->BusNumber != (ULONG)-1))
	{
		TimeoutValue.QuadPart = Int32x32To64(2 * 1000, -10000);

		Miniport->PhysicalMapRegistersNeeded = PhysicalMapRegistersNeeded;
		Miniport->MaximumPhysicalMapping = MaximumPhysicalMapping;

		//
		// Allocate storage for holding the appropriate
		// information for each map register.
		//

		Miniport->MapRegisters = (PMAP_REGISTER_ENTRY)
		ALLOC_FROM_POOL(sizeof(MAP_REGISTER_ENTRY) * PhysicalMapRegistersNeeded,
						NDIS_TAG_DEFAULT);

		if (Miniport->MapRegisters == (PMAP_REGISTER_ENTRY)NULL)
		{
			//
			// Error out
			//

			NdisWriteErrorLogEntry((NDIS_HANDLE)Miniport,
								   NDIS_ERROR_CODE_OUT_OF_RESOURCES,
								   1,
								   0xFFFFFFFF);

			return NDIS_STATUS_RESOURCES;
		}

		//
		// Use this event to tell us when ndisAllocationExecutionRoutine
		// has been called.
		//

		INITIALIZE_EVENT(&Miniport->AllocationEvent);

		//
		// Set up the device description; zero it out in case its
		// size changes.
		//

		ZeroMemory(&DeviceDescription, sizeof(DEVICE_DESCRIPTION));

		DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION;
		DeviceDescription.Master = TRUE;
		DeviceDescription.ScatterGather = TRUE;

		DeviceDescription.BusNumber = Miniport->BusNumber;
		DeviceDescription.DmaChannel = DmaChannel;
		DeviceDescription.InterfaceType = Miniport->AdapterType;

		if (DeviceDescription.InterfaceType == NdisInterfaceIsa)
		{
			//
			// For ISA devices, the width is based on the DMA channel:
			// 0-3 == 8 bits, 5-7 == 16 bits. Timing is compatibility
			// mode.
			//

			if (DmaChannel > 4)
			{
				DeviceDescription.DmaWidth = Width16Bits;
			}
			else
			{
				DeviceDescription.DmaWidth = Width8Bits;
			}
			DeviceDescription.DmaSpeed = Compatible;

		}
		else if ((DeviceDescription.InterfaceType == NdisInterfaceEisa)	||
				 (DeviceDescription.InterfaceType == NdisInterfacePci)	||
				 (DeviceDescription.InterfaceType == NdisInterfaceMca))
		{
			DeviceDescription.Dma32BitAddresses = Dma32BitAddresses;
		}

		DeviceDescription.MaximumLength = MaximumPhysicalMapping;

		//
		// Get the adapter object.
		//

		AdapterObject = HalGetAdapter (&DeviceDescription, &MapRegistersAllowed);

		if (AdapterObject == NULL)
		{
			NdisWriteErrorLogEntry((NDIS_HANDLE)Miniport,
								   NDIS_ERROR_CODE_OUT_OF_RESOURCES,
								   1,
								   0xFFFFFFFF);

			FREE_POOL(Miniport->MapRegisters);
			Miniport->MapRegisters = NULL;
			DBGPRINT(DBG_COMP_ALL, DBG_LEVEL_INFO,
					("<==NdisRegisterAdapter\n"));
			return NDIS_STATUS_RESOURCES;
		}

		//
		// We save this to call IoFreeMapRegisters later.
		//

		Miniport->SystemAdapterObject = AdapterObject;

		//
		// Determine how many map registers we need per channel.
		//

		MapRegistersPerChannel = ((MaximumPhysicalMapping - 2) / PAGE_SIZE) + 2;

		ASSERT (MapRegistersAllowed >= MapRegistersPerChannel);

		//
		// Now loop, allocating an adapter channel each time, then
		// freeing everything but the map registers.
		//

		for (i=0; i<Miniport->PhysicalMapRegistersNeeded; i++)
		{
			Miniport->CurrentMapRegister = i;

			RAISE_IRQL_TO_DISPATCH(&OldIrql);

			NtStatus = IoAllocateAdapterChannel(AdapterObject,
												Miniport->DeviceObject,
												MapRegistersPerChannel,
												ndisAllocationExecutionRoutine,
												Miniport);

			if (!NT_SUCCESS(NtStatus))
			{
				DBGPRINT(DBG_COMP_ALL, DBG_LEVEL_ERR,
						("AllocateAdapterChannel: %lx\n", NtStatus));

				for (; i != 0; i--)
				{
					IoFreeMapRegisters(Miniport->SystemAdapterObject,
									   Miniport->MapRegisters[i-1].MapRegister,
									   MapRegistersPerChannel);
				}

				LOWER_IRQL(OldIrql);

				NdisWriteErrorLogEntry((NDIS_HANDLE)Miniport,
									   NDIS_ERROR_CODE_OUT_OF_RESOURCES,
									   1,
									   0xFFFFFFFF);

				FREE_POOL(Miniport->MapRegisters);
				Miniport->MapRegisters = NULL;
				return NDIS_STATUS_RESOURCES;
			}

			LOWER_IRQL(OldIrql);

			TimeoutValue.QuadPart = Int32x32To64(2 * 1000, -10000);

			//
			// ndisAllocationExecutionRoutine will set this event
			// when it has gotten FirstTranslationEntry.
			//

			NtStatus = WAIT_FOR_OBJECT(&Miniport->AllocationEvent, &TimeoutValue);

			if (NtStatus != STATUS_SUCCESS)
			{
				DBGPRINT(DBG_COMP_ALL, DBG_LEVEL_ERR,
						("NDIS DMA AllocateAdapterChannel: %lx\n", NtStatus));

				RAISE_IRQL_TO_DISPATCH(&OldIrql);

				for (; i != 0; i--)
				{
					IoFreeMapRegisters(Miniport->SystemAdapterObject,
									   Miniport->MapRegisters[i-1].MapRegister,
									   MapRegistersPerChannel);
				}

				LOWER_IRQL(OldIrql);

				NdisWriteErrorLogEntry((NDIS_HANDLE)Miniport,
										NDIS_ERROR_CODE_OUT_OF_RESOURCES,
										1,
										0xFFFFFFFF);

				FREE_POOL(Miniport->MapRegisters);
				Miniport->MapRegisters = NULL;
				return  NDIS_STATUS_RESOURCES;
			}

			RESET_EVENT(&Miniport->AllocationEvent);
		}
	}

	return NDIS_STATUS_SUCCESS;
}
Example #9
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);
}