NTSTATUS PLxEvtDeviceReleaseHardware( IN WDFDEVICE Device, IN WDFCMRESLIST ResourcesTranslated ) /*++ Routine Description: Unmap the resources that were mapped in PLxEvtDevicePrepareHardware. This will only be called when the device stopped for resource rebalance, surprise-removed or query-removed. Arguments: Device - A handle to the WDFDEVICE ResourcesTranslated - The translated PnP resources associated with the device. This is what is important to a PCI device. Return Value: NT status code - failure will result in the device stack being torn down --*/ { KdPrint(("Entry PLxEvtDeviceReleaseHardware Routine")); PDEVICE_EXTENSION devExt; NTSTATUS status = STATUS_SUCCESS; UNREFERENCED_PARAMETER(ResourcesTranslated); PAGED_CODE(); TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> PLxEvtDeviceReleaseHardware"); devExt = PLxGetDeviceContext(Device); if (devExt->RegsBase) { MmUnmapIoSpace(devExt->RegsBase, devExt->RegsLength); devExt->RegsBase = NULL; } if(devExt->SRAMBase){ MmUnmapIoSpace(devExt->SRAMBase, devExt->SRAMLength); devExt->SRAMBase = NULL; } TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- PLxEvtDeviceReleaseHardware"); KdPrint(("Leave PLxEvtDeviceReleaseHardware Routine")); return status; }
NTSTATUS PLxEvtDeviceD0Entry( IN WDFDEVICE Device, IN WDF_POWER_DEVICE_STATE PreviousState ) /*++ Routine Description: This routine prepares the device for use. It is called whenever the device enters the D0 state, which happens when the device is started, when it is restarted, and when it has been powered off. Note that interrupts will not be enabled at the time that this is called. They will be enabled after this callback completes. This function is not marked pageable because this function is in the device power up path. When a function is marked pagable and the code section is paged out, it will generate a page fault which could impact the fast resume behavior because the client driver will have to wait until the system drivers can service this page fault. Arguments: Device - The handle to the WDF device object PreviousState - The state the device was in before this callback was invoked. Return Value: NTSTATUS Success implies that the device can be used. Failure will result in the device stack being torn down. --*/ { PDEVICE_EXTENSION devExt; NTSTATUS status; UNREFERENCED_PARAMETER(PreviousState); devExt = PLxGetDeviceContext(Device); status = PLxInitWrite( devExt ); if (NT_SUCCESS(status)) { status = PLxInitRead( devExt ); } return status; }
NTSTATUS PLxEvtDevicePrepareHardware ( WDFDEVICE Device, WDFCMRESLIST Resources, WDFCMRESLIST ResourcesTranslated ) /*++ Routine Description: Performs whatever initialization is needed to setup the device, setting up a DMA channel or mapping any I/O port resources. This will only be called as a device starts or restarts, not every time the device moves into the D0 state. Consequently, most hardware initialization belongs elsewhere. Arguments: Device - A handle to the WDFDEVICE Resources - The raw PnP resources associated with the device. Most of the time, these aren't useful for a PCI device. ResourcesTranslated - The translated PnP resources associated with the device. This is what is important to a PCI device. Return Value: NT status code - failure will result in the device stack being torn down --*/ { NTSTATUS status = STATUS_SUCCESS; PDEVICE_EXTENSION devExt; UNREFERENCED_PARAMETER(Resources); PAGED_CODE(); TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> PLxEvtDevicePrepareHardware"); devExt = PLxGetDeviceContext(Device); status = PLxPrepareHardware(devExt, ResourcesTranslated); if (!NT_SUCCESS (status)){ return status; } TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- PLxEvtDevicePrepareHardware, status %!STATUS!", status); return status; }
NTSTATUS PLxEvtInterruptDisable( IN WDFINTERRUPT Interrupt, IN WDFDEVICE Device ) /*++ Routine Description: Called by the framework at DIRQL before Deregistering the ISR with the kernel by calling IoDisconnectInterrupt. Return Value: NTSTATUS --*/ { PDEVICE_EXTENSION devExt; union { INT_CSR bits; ULONG ulong; } intCSR; TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INTERRUPT, "PLxEvtInterruptDisable: Interrupt 0x%p, Device 0x%p\n", Interrupt, Device); devExt = PLxGetDeviceContext(WdfInterruptGetDevice(Interrupt)); intCSR.ulong = READ_REGISTER_ULONG( (PULONG) &devExt->Regs->Int_Csr ); intCSR.bits.PciIntEnable = FALSE; WRITE_REGISTER_ULONG( (PULONG) &devExt->Regs->Int_Csr, intCSR.ulong ); return STATUS_SUCCESS; }
BOOLEAN PLxEvtInterruptIsr( IN WDFINTERRUPT Interrupt, IN ULONG MessageID ) /*++ Routine Description: Interrupt handler for this driver. Called at DIRQL level when the device or another device sharing the same interrupt line asserts the interrupt. The driver first checks the device to make sure whether this interrupt is generated by its device and if so clear the interrupt register to disable further generation of interrupts and queue a DPC to do other I/O work related to interrupt - such as reading the device memory, starting a DMA transaction, coping it to the request buffer and completing the request, etc. Arguments: Interupt - Handle to WDFINTERRUPT Object for this device. MessageID - MSI message ID (always 0 in this configuration) Return Value: TRUE -- This device generated the interrupt. FALSE -- This device did not generated this interrupt. --*/ { PDEVICE_EXTENSION devExt; BOOLEAN isRecognized = FALSE; union { INT_CSR bits; ULONG ulong; } intCsr; UNREFERENCED_PARAMETER(MessageID); //TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INTERRUPT, // "--> PLxInterruptHandler"); devExt = PLxGetDeviceContext(WdfInterruptGetDevice(Interrupt)); // // Read the Interrupt CSR register (INTCSR) // intCsr.ulong = READ_REGISTER_ULONG( (PULONG) &devExt->Regs->Int_Csr ); // // Is DMA channel 0 (Write-side) Active? // if (intCsr.bits.DmaChan0IntActive) { TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INTERRUPT, " Interrupt for DMA Channel 0 (write)"); devExt->IntCsr.bits.DmaChan0IntActive = TRUE; // // Clear this interrupt. // devExt->Dma0Csr.uchar = READ_REGISTER_UCHAR( (PUCHAR) &devExt->Regs->Dma0_Csr ); devExt->Dma0Csr.bits.Clear = TRUE; WRITE_REGISTER_UCHAR( (PUCHAR) &devExt->Regs->Dma0_Csr, devExt->Dma0Csr.uchar ); isRecognized = TRUE; } // // Is DMA channel 1 (Read-side) Active? // if (intCsr.bits.DmaChan1IntActive) { TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INTERRUPT, " Interrupt for DMA Channel 1 (read)"); devExt->IntCsr.bits.DmaChan1IntActive = TRUE; // // Clear this interrupt. // devExt->Dma1Csr.uchar = READ_REGISTER_UCHAR( (PUCHAR) &devExt->Regs->Dma1_Csr ); devExt->Dma1Csr.bits.Clear = TRUE; WRITE_REGISTER_UCHAR( (PUCHAR) &devExt->Regs->Dma1_Csr, devExt->Dma1Csr.uchar ); isRecognized = TRUE; } if ((isRecognized) && ((devExt->Dma0Csr.bits.Done) || (devExt->Dma1Csr.bits.Done))) { // // A read or a write or both is done. Queue a DPC. // WdfInterruptQueueDpcForIsr( devExt->Interrupt ); } //TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INTERRUPT, // "<-- PLxInterruptHandler"); return isRecognized; }
_Use_decl_annotations_ VOID PLxEvtInterruptDpc( WDFINTERRUPT Interrupt, WDFOBJECT Device ) /*++ Routine Description: DPC callback for ISR. Please note that on a multiprocessor system, you could have more than one DPCs running simulataneously on multiple processors. So if you are accesing any global resources make sure to synchrnonize the accesses with a spinlock. Arguments: Interupt - Handle to WDFINTERRUPT Object for this device. Device - WDFDEVICE object passed to InterruptCreate Return Value: --*/ { NTSTATUS status; WDFDMATRANSACTION dmaTransaction; PDEVICE_EXTENSION devExt; BOOLEAN writeInterrupt = FALSE; BOOLEAN readInterrupt = FALSE; UNREFERENCED_PARAMETER(Device); TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC, "--> EvtInterruptDpc"); devExt = PLxGetDeviceContext(WdfInterruptGetDevice(Interrupt)); // // Acquire this device's InterruptSpinLock. // WdfInterruptAcquireLock( Interrupt ); if ((devExt->IntCsr.bits.DmaChan0IntActive) && (devExt->Dma0Csr.bits.Done)) { // // If Dma0 channel 0 (write) is interrupting and the // Done bit is set in the Dma0 CSR, // we're interrupting because a WRITE is complete. // Clear the done bit and channel interrupting bit from // our copies... // devExt->IntCsr.bits.DmaChan0IntActive = FALSE; devExt->Dma0Csr.uchar = 0; writeInterrupt = TRUE; } if ((devExt->IntCsr.bits.DmaChan1IntActive) && (devExt->Dma1Csr.bits.Done)) { // // If DMA channel 1 is interrupting and the // DONE bit is set in the DMA1 control/status // register, we're interrupting because a READ // is complete. // Clear the done bit and channel interrupting bit from // our copies... // devExt->IntCsr.bits.DmaChan1IntActive = FALSE; devExt->Dma0Csr.uchar = 0; readInterrupt = TRUE; } // // Release our interrupt spinlock // WdfInterruptReleaseLock( Interrupt ); // // Did a Write DMA complete? // if (writeInterrupt) { BOOLEAN transactionComplete; // // Get the current Write DmaTransaction. // dmaTransaction = devExt->WriteDmaTransaction; // // Indicate this DMA operation has completed: // This may drive the transfer on the next packet if // there is still data to be transfered in the request. // transactionComplete = WdfDmaTransactionDmaCompleted( dmaTransaction, &status ); if (transactionComplete) { // // Complete this DmaTransaction. // TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC, "Completing Write request in the DpcForIsr"); PLxWriteRequestComplete( dmaTransaction, status ); } } // // Did a Read DMA complete? // if (readInterrupt) { BOOLEAN transactionComplete; PDMA_TRANSFER_ELEMENT dteVA; size_t length; // // Get the current Read DmaTransaction. // dmaTransaction = devExt->ReadDmaTransaction; // // Only on Read-side -- // Use "DMA Clear-Count Mode" to get complemetary // transferred byte count. // length = WdfDmaTransactionGetCurrentDmaTransferLength( dmaTransaction ); dteVA = (PDMA_TRANSFER_ELEMENT) devExt->ReadCommonBufferBase; while(dteVA->DescPtr.LastElement == FALSE) { length -= dteVA->TransferSize; dteVA++; } length -= dteVA->TransferSize; // // Indicate this DMA operation has completed: // This may drive the transfer on the next packet if // there is still data to be transfered in the request. // transactionComplete = WdfDmaTransactionDmaCompletedWithLength( dmaTransaction, length, &status ); if (transactionComplete) { // // Complete this DmaTransaction. // TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC, "Completing Read request in the DpcForIsr"); PLxReadRequestComplete( dmaTransaction, status ); } } TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC, "<-- EvtInterruptDpc"); return; }
NTSTATUS PLxEvtDeviceD0Exit( IN WDFDEVICE Device, IN WDF_POWER_DEVICE_STATE TargetState ) /*++ Routine Description: This routine undoes anything done in PLxEvtDeviceD0Entry. It is called whenever the device leaves the D0 state, which happens when the device is stopped, when it is removed, and when it is powered off. The device is still in D0 when this callback is invoked, which means that the driver can still touch hardware in this routine. Note that interrupts have already been disabled by the time that this callback is invoked. Arguments: Device - The handle to the WDF device object TargetState - The state the device will go to when this callback completes. Return Value: Success implies that the device can be used. Failure will result in the device stack being torn down. --*/ { PDEVICE_EXTENSION devExt; PAGED_CODE(); devExt = PLxGetDeviceContext(Device); switch (TargetState) { case WdfPowerDeviceD1: case WdfPowerDeviceD2: case WdfPowerDeviceD3: // // Fill in any code to save hardware state here. // // // Fill in any code to put the device in a low-power state here. // break; case WdfPowerDevicePrepareForHibernation: // // Fill in any code to save hardware state here. Do not put in any // code to shut the device off. If this device cannot support being // in the paging path (or being a parent or grandparent of a paging // path device) then this whole case can be deleted. // break; case WdfPowerDeviceD3Final: default: // // Reset the hardware, as we're shutting down for the last time. // PLxShutdown(devExt); break; } return STATUS_SUCCESS; }
NTSTATUS PLxEvtDeviceAdd( IN WDFDRIVER Driver, IN PWDFDEVICE_INIT DeviceInit ) /*++ Routine Description: EvtDeviceAdd is called by the framework in response to AddDevice call from the PnP manager. Here the driver should register all the PNP, power and Io callbacks, register interfaces and allocate other software resources required by the device. The driver can query any interfaces or get the config space information from the bus driver but cannot access hardware registers or initialize the device. Arguments: Return Value: --*/ { KdPrint(("Entry PLxEvtDeviceAdd Routine")); NTSTATUS status = STATUS_SUCCESS; WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks; WDF_OBJECT_ATTRIBUTES attributes; WDFDEVICE device; PDEVICE_EXTENSION devExt = NULL; UNREFERENCED_PARAMETER( Driver ); TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> PLxEvtDeviceAdd"); PAGED_CODE(); WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect); // // Zero out the PnpPowerCallbacks structure. // WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks); // // Set Callbacks for any of the functions we are interested in. // If no callback is set, Framework will take the default action // by itself. // pnpPowerCallbacks.EvtDevicePrepareHardware = PLxEvtDevicePrepareHardware; pnpPowerCallbacks.EvtDeviceReleaseHardware = PLxEvtDeviceReleaseHardware; // // These two callbacks set up and tear down hardware state that must be // done every time the device moves in and out of the D0-working state. // pnpPowerCallbacks.EvtDeviceD0Entry = PLxEvtDeviceD0Entry; pnpPowerCallbacks.EvtDeviceD0Exit = PLxEvtDeviceD0Exit; // // Register the PnP Callbacks.. // WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks); // // Initialize Fdo Attributes. // WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, DEVICE_EXTENSION); // // By opting for SynchronizationScopeDevice, we tell the framework to // synchronize callbacks events of all the objects directly associated // with the device. In this driver, we will associate queues and // and DpcForIsr. By doing that we don't have to worrry about synchronizing // access to device-context by Io Events and DpcForIsr because they would // not concurrently ever. Framework will serialize them by using an // internal device-lock. // attributes.SynchronizationScope = WdfSynchronizationScopeDevice; // // Create the device // status = WdfDeviceCreate( &DeviceInit, &attributes, &device ); if (!NT_SUCCESS(status)) { // // Device Initialization failed. // TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "DeviceCreate failed %!STATUS!", status); return status; } // // Get the DeviceExtension and initialize it. PLxGetDeviceContext is an inline function // defined by WDF_DECLARE_CONTEXT_TYPE_WITH_NAME macro in the // private header file. This function will do the type checking and return // the device context. If you pass a wrong object a wrong object handle // it will return NULL and assert if run under framework verifier mode. // devExt = PLxGetDeviceContext(device); devExt->Device = device; TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, " AddDevice PDO (0x%p) FDO (0x%p), DevExt (0x%p)", WdfDeviceWdmGetPhysicalDevice(device), WdfDeviceWdmGetDeviceObject(device), devExt); // // Tell the Framework that this device will need an interface // // NOTE: See the note in Public.h concerning this GUID value. // status = WdfDeviceCreateDeviceInterface( device, (LPGUID) &GUID_PLX_INTERFACE, NULL ); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP, "<-- DeviceCreateDeviceInterface " "failed %!STATUS!", status); return status; } // // Set the idle and wait-wake policy for this device. // status = PLxSetIdleAndWakeSettings(devExt); if (!NT_SUCCESS (status)) { // // NOTE: The attempt to set the Idle and Wake options // is a best-effort try. Failure is probably due to // the non-driver environmentals, such as the system, // bus or OS indicating that Wake is not supported for // this case. // All that being said, it probably not desirable to // return the failure code as it would cause the // AddDevice to fail and Idle and Wake are probably not // "must-have" options. // // You must decide for your case whether Idle/Wake are // "must-have" options...but my guess is probably not. // #if 1 status = STATUS_SUCCESS; #else return status; #endif } // // Initalize the Device Extension. // status = PLxInitializeDeviceExtension(devExt); if (!NT_SUCCESS(status)) { return status; } TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- PLxEvtDeviceAdd %!STATUS!", status); KdPrint(("Leave PLxEvtDeviceAdd Routine")); return status; }