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; }
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; }
/////////////////////////////////////////////////////////////////////////////// // // 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); }
/////////////////////////////////////////////////////////////////////////////// // // 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); }
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 // }
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; }
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; }
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; }
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); }