summaryrefslogtreecommitdiff
path: root/general/PLX9x5x/sys/IsrDpc.c
diff options
context:
space:
mode:
Diffstat (limited to 'general/PLX9x5x/sys/IsrDpc.c')
-rw-r--r--general/PLX9x5x/sys/IsrDpc.c441
1 files changed, 441 insertions, 0 deletions
diff --git a/general/PLX9x5x/sys/IsrDpc.c b/general/PLX9x5x/sys/IsrDpc.c
new file mode 100644
index 00000000..a7d319d3
--- /dev/null
+++ b/general/PLX9x5x/sys/IsrDpc.c
@@ -0,0 +1,441 @@
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+ THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+ KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+ PURPOSE.
+
+Module Name:
+
+ IsrDpc.c
+
+Abstract:
+
+ Contains routines related to interrupt and dpc handling.
+
+Environment:
+
+ Kernel mode
+
+--*/
+
+#include "precomp.h"
+
+#include "IsrDpc.tmh"
+
+NTSTATUS
+PLxInterruptCreate(
+ IN PDEVICE_EXTENSION DevExt
+ )
+/*++
+Routine Description:
+
+ Configure and create the WDFINTERRUPT object.
+ This routine is called by EvtDeviceAdd callback.
+
+Arguments:
+
+ DevExt Pointer to our DEVICE_EXTENSION
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+{
+ NTSTATUS status;
+ WDF_INTERRUPT_CONFIG InterruptConfig;
+
+ WDF_INTERRUPT_CONFIG_INIT( &InterruptConfig,
+ PLxEvtInterruptIsr,
+ PLxEvtInterruptDpc );
+
+ InterruptConfig.EvtInterruptEnable = PLxEvtInterruptEnable;
+ InterruptConfig.EvtInterruptDisable = PLxEvtInterruptDisable;
+
+ // JOHNR: Enable testing of the DpcForIsr Synchronization
+ InterruptConfig.AutomaticSerialization = TRUE;
+
+ //
+ // Unlike WDM, framework driver should create interrupt object in EvtDeviceAdd and
+ // let the framework do the resource parsing and registration of ISR with the kernel.
+ // Framework connects the interrupt after invoking the EvtDeviceD0Entry callback
+ // and disconnect before invoking EvtDeviceD0Exit. EvtInterruptEnable is called after
+ // the interrupt interrupt is connected and EvtInterruptDisable before the interrupt is
+ // disconnected.
+ //
+ status = WdfInterruptCreate( DevExt->Device,
+ &InterruptConfig,
+ WDF_NO_OBJECT_ATTRIBUTES,
+ &DevExt->Interrupt );
+
+ if( !NT_SUCCESS(status) ) {
+ TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
+ "WdfInterruptCreate failed: %!STATUS!", status);
+ }
+
+ return status;
+}
+
+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
+PLxEvtInterruptEnable(
+ IN WDFINTERRUPT Interrupt,
+ IN WDFDEVICE Device
+ )
+/*++
+
+Routine Description:
+
+ Called by the framework at DIRQL immediately after registering the ISR with the kernel
+ by calling IoConnectInterrupt.
+
+Return Value:
+
+ NTSTATUS
+--*/
+{
+ PDEVICE_EXTENSION devExt;
+
+ union {
+ INT_CSR bits;
+ ULONG ulong;
+ } intCSR;
+
+ TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INTERRUPT,
+ "PLxEvtInterruptEnable: 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 = TRUE;
+
+ WRITE_REGISTER_ULONG( (PULONG) &devExt->Regs->Int_Csr,
+ intCSR.ulong );
+
+ return STATUS_SUCCESS;
+}
+
+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;
+}