Thank you everyone for taking the time to look at this.
First, it?s good news to hear that I will be interrupted again if the interrupt doesn?t go away. This is what I expected and designed for.
The DPC is supposed to handle it if the interrupt was called multiple times before the DPC got to run. I actually have 3 sets of shadow registers, one for each interrupt reason. And the DPC should also handle the cases when it?s run on multiple processors at the same time. Probably the easiest thing to do now is to attach a slightly paired down version of the DCP and ISR code for everyone to look at, because I can?t find anything wrong with it so far.
Thanks!
enum INTERRUPT_REASON
{
ISR_FOR_DMA,
ISR_FOR_CANBUS,
ISR_FOR_ERRORS,
NUM_INTERRUPT_REASONS
};
typedef struct _ISR_DATA{
//
// Is this interrupt data valid? Did we get this interrupt?
//
BOOLEAN Active;
//
// Shadow copies of the Status and Control registers from this ISR entry
//
ULONG ShadowIsrStatusRegister;
ULONG ShadowIsrControlRegister;
}ISR_DATA;
typedef struct _GEMINI_DEVICE_EXT {
//
// …
//
//
// Why were we interrupted?
//
ISR_DATA InterruptReason[NUM_INTERRUPT_REASONS];
//
// …
//
} GEMINI_DEVICE_EXT, *PGEMINI_DEVICE_EXT;
BOOLEAN HandleInterrupt ( PKINTERRUPT Interrupt,
PVOID ServiceContext)
{
//--------------------------------------------------------------------
PGEMINI_DEVICE_EXT devExt = (PGEMINI_DEVICE_EXT)ServiceContext;
ULONG StatusRegister;
ULONG ControlRegister;
BOOLEAN OurInterrupt = FALSE;
//--------------------------------------------------------------------
UNREFERENCED_PARAMETER(Interrupt);
//
// Always check the signature of the struct!
//
ASSERT(devExt->magic == GEMINI_DEVICE_EXT_MAGIC);
//
// Read out the Status and control once
//
StatusRegister = ReadStatusRegister(devExt);
ControlRegister = ReadControlRegister(devExt);
//
// Check if it’s an error interrupt first
//
if (StatusRegister & ( STATUS_BIT_MEMORY_ERROR
| STATUS_BIT_LINK_PROTO_ERR)){
devExt->InterruptReason[ISR_FOR_ERRORS].Active = TRUE;
devExt->InterruptReason[ISR_FOR_ERRORS].ShadowIsrStatusRegister = StatusRegister;
devExt->InterruptReason[ISR_FOR_ERRORS].ShadowIsrControlRegister = ControlRegister;
//
// For now, just clear the errors
//
DisableErrorInterrupts(devExt);
ClearErrors(devExt);
OurInterrupt = TRUE;
}
//
// Check if it’s our DMA interrupt
//
if (StatusRegister & STATUS_BIT_DMA_INT) {
devExt->InterruptReason[ISR_FOR_DMA].Active = TRUE;
devExt->InterruptReason[ISR_FOR_DMA].ShadowIsrStatusRegister = StatusRegister;
devExt->InterruptReason[ISR_FOR_DMA].ShadowIsrControlRegister = ControlRegister;
//
// Turn off the DMA_ENABLE bit to deassert the interrupt.
//
ClearControlRegisterBits(devExt, CTRL_BIT_ENABLE_DMA);
DisableDmaInterrupts(devExt);
OurInterrupt = TRUE;
}
//
// Check if it’s our CANbus interrupt
//
if (StatusRegister & STATUS_BIT_CAN_INT) {
devExt->InterruptReason[ISR_FOR_CANBUS].Active = TRUE;
devExt->InterruptReason[ISR_FOR_CANBUS].ShadowIsrStatusRegister = StatusRegister;
devExt->InterruptReason[ISR_FOR_CANBUS].ShadowIsrControlRegister = ControlRegister;
//
// We must disable these here too
//
DisableCanbusInterrupts(devExt);
if (devExt->FirmwareProductVersion >= 2){
//
// Debug timers
//
PDEBUG_LATENCY_TIMERS Timers = &devExt->Statistics.DebugLatencyTimers;
StopCanbusTimer(devExt);
Timers->CanbusRegister = ReadCanbusTimerReg(devExt);
WriteCanbusTimerReg(devExt, 0);
if (Timers->CanbusRegister > Timers->MaxCanbusIsrLatency){
Timers->MaxCanbusIsrLatency = Timers->CanbusRegister;
}
__int64 TimerTmp = (Timers->AverageCanbusIsrLatency * Timers->CanbusIsrCount)
- Timers->CanbusRegister;
Timers->CanbusIsrCount++;
Timers->AverageCanbusIsrLatency = (ULONG)(TimerTmp / Timers->CanbusIsrCount);
}
OurInterrupt = TRUE;
}
//
// If it wasn’t ours, get out!
//
if (!OurInterrupt){
devExt->Statistics.NotOurInterrupt++;
return (FALSE);
}
//
// Measure the ISR / DPC latency
//
WriteDebugTimerReg(devExt, 0);
StartDebugTimer(devExt);
//
// Ok - if we’re here it is out interrupt.
// Queue a DPCForISR
//
IoRequestDpc( devExt->FunctionalDeviceObject,
NULL,
NULL);
return (TRUE);
}
VOID DpcForIsr( PKDPC Dpc,
PDEVICE_OBJECT DeviceObject,
PIRP Unused,
PVOID Context)
{
//-------------------------------------------------------------------------------------
PGEMINI_DEVICE_EXT devExt = (PGEMINI_DEVICE_EXT) DeviceObject->DeviceExtension;
KIRQL oldIrql;
PIRP readIrp;
ULONG i;
ISR_DATA InterruptReason[NUM_INTERRUPT_REASONS];
BOOLEAN IrpIsCancelled;
NTSTATUS status = STATUS_SUCCESS;
NTSTATUS ErrorIsrStatus = STATUS_SUCCESS;
NTSTATUS DmaIsrStatus = STATUS_SUCCESS;
QUEUED_IO_COMPLETE_REQUEST QueuedIoCompleteRequests[MAX_QUEUED_IO_COMPLETE_REQUESTS];
ULONG CurrentIoCompleteQueue;
//-------------------------------------------------------------------------------------
UNREFERENCED_PARAMETER(Dpc);
UNREFERENCED_PARAMETER(Unused);
UNREFERENCED_PARAMETER(Context);
//
// By definition DPC’s are called at DISPATCH_LEVEL, so we shouldn’t
// ever not be called at DISPATCH_LEVEL
//
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
//
// Always check the signature of the struct!
//
ASSERT(devExt->magic == GEMINI_DEVICE_EXT_MAGIC);
//---------------------------------------------------------------------
//
// Grab our interrupt spinlock
//
oldIrql = KeAcquireInterruptSpinLock(devExt->InterruptObject);
//
// Make a local copy of the device struct
//
RtlCopyMemory( &InterruptReason,
&devExt->InterruptReason,
NUM_INTERRUPT_REASONS * sizeof (ISR_DATA));
//
// And zero out the DevExt copy
//
RtlZeroMemory ( &devExt->InterruptReason,
NUM_INTERRUPT_REASONS * sizeof (ISR_DATA)
);
//
// Release our interrupt spinlock
//
KeReleaseInterruptSpinLock(devExt->InterruptObject, oldIrql);
//---------------------------------------------------------------------
//
// Zero our Queue of Irp Completions right away
//
RtlZeroMemory( QueuedIoCompleteRequests,
sizeof(QUEUED_IO_COMPLETE_REQUEST) * MAX_QUEUED_IO_COMPLETE_REQUESTS
);
CurrentIoCompleteQueue = 0;
//
// CANbus data or event? ---------------------------------------------
//
if (InterruptReason[ISR_FOR_CANBUS].Active) {
HandleCanbusDpc(devExt);
//
// Enable the interrupt when we are done
//
EnableCanbusInterrupts(devExt);
}
//
// Did we have any errors? --------------------------------------------
//
if (InterruptReason[ISR_FOR_ERRORS].Active) {
//
// Check these for the statistics we should be keeping
//
ErrorIsrStatus = CheckStatusErrorsForImageRead( devExt,
InterruptReason[ISR_FOR_ERRORS].ShadowIsrStatusRegister);
//
// Enable the interrupt when we are done
//
EnableErrorInterrupts(devExt);
//
// More processing here…
//
}
//
// Is an Image Read complete? ----------------------------------------
//
if (InterruptReason[ISR_FOR_DMA].Active) {
//
// More processing here…
//
}// End of - InterruptReason[ISR_FOR_DMA]
//
// More processing here…
//
return;
}