diff options
Diffstat (limited to 'general/PLX9x5x/sys/IsrDpc.c')
| -rw-r--r-- | general/PLX9x5x/sys/IsrDpc.c | 441 |
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; +} |
