windows-nt/Source/XPSP1/NT/net/ndis/sys/timerm.c
2020-09-26 16:20:57 +08:00

1480 lines
42 KiB
C

/*++
Copyright (c) 1990-1995 Microsoft Corporation
Module Name:
timerm.c
Abstract:
NDIS wrapper functions for miniport isr/timer
Author:
Sean Selitrennikoff (SeanSe) 05-Oct-93
Environment:
Kernel mode, FSD
Revision History:
Jameel Hyder (JameelH) Re-organization 01-Jun-95
--*/
#include <precomp.h>
#pragma hdrstop
//
// Define the module number for debug code.
//
#define MODULE_NUMBER MODULE_TIMERM
//
// Timers
//
VOID
NdisMInitializeTimer(
IN OUT PNDIS_MINIPORT_TIMER MiniportTimer,
IN NDIS_HANDLE MiniportAdapterHandle,
IN PNDIS_TIMER_FUNCTION TimerFunction,
IN PVOID FunctionContext
)
/*++
Routine Description:
Sets up an Miniport Timer object, initializing the DPC in the timer to
the function and context.
Arguments:
MiniportTimer - the timer object.
MiniportAdapterHandle - pointer to the mini-port block;
TimerFunction - Routine to start.
FunctionContext - Context of TimerFunction.
Return Value:
None.
--*/
{
INITIALIZE_TIMER(&MiniportTimer->Timer);
MiniportTimer->Miniport = (PNDIS_MINIPORT_BLOCK)MiniportAdapterHandle;
MiniportTimer->MiniportTimerFunction = TimerFunction;
MiniportTimer->MiniportTimerContext = FunctionContext;
//
// Initialize our dpc. If Dpc was previously initialized, this will
// reinitialize it.
//
INITIALIZE_DPC(&MiniportTimer->Dpc,
MINIPORT_TEST_FLAG(MiniportTimer->Miniport, fMINIPORT_DESERIALIZE) ?
(PKDEFERRED_ROUTINE)ndisMTimerDpcX : (PKDEFERRED_ROUTINE)ndisMTimerDpc,
(PVOID)MiniportTimer);
SET_PROCESSOR_DPC(&MiniportTimer->Dpc,
MiniportTimer->Miniport->AssignedProcessor);
}
VOID
NdisMSetTimer(
IN PNDIS_MINIPORT_TIMER MiniportTimer,
IN UINT MillisecondsToDelay
)
/*++
Routine Description:
Sets up TimerFunction to fire after MillisecondsToDelay.
Arguments:
MiniportTimer - the timer object.
MillisecondsToDelay - Amount of time before TimerFunction is started.
Return Value:
None.
--*/
{
LARGE_INTEGER FireUpTime;
FireUpTime.QuadPart = Int32x32To64((LONG)MillisecondsToDelay, -10000);
#if CHECK_TIMER
if (MiniportTimer->Miniport->DriverHandle->Flags & fMINIBLOCK_VERIFYING)
{
KIRQL OldIrql;
PNDIS_MINIPORT_TIMER pTimer;
ACQUIRE_SPIN_LOCK(&MiniportTimer->Miniport->TimerQueueLock, &OldIrql);
//
// check to see if the timer is already set
//
for (pTimer = MiniportTimer->Miniport->TimerQueue;
pTimer != NULL;
pTimer = pTimer->NextTimer)
{
if (pTimer == MiniportTimer)
break;
}
if (pTimer == NULL)
{
MiniportTimer->NextTimer = MiniportTimer->Miniport->TimerQueue;
MiniportTimer->Miniport->TimerQueue = MiniportTimer;
}
RELEASE_SPIN_LOCK(&MiniportTimer->Miniport->TimerQueueLock, OldIrql);
}
#endif
//
// Set the timer
//
SET_TIMER(&MiniportTimer->Timer, FireUpTime, &MiniportTimer->Dpc);
}
VOID
NdisMCancelTimer(
IN PNDIS_MINIPORT_TIMER Timer,
OUT PBOOLEAN TimerCancelled
)
/*++
Routine Description:
Cancels a timer.
Arguments:
Timer - The timer to cancel.
TimerCancelled - TRUE if the timer was canceled, else FALSE.
Return Value:
None
--*/
{
if (MINIPORT_VERIFY_TEST_FLAG(Timer->Miniport, fMINIPORT_VERIFY_FAIL_CANCEL_TIMER))
{
*TimerCancelled = FALSE;
#if DBG
DbgPrint("NdisMCancelTimer for Timer %p failed to verify miniport %p\n",
Timer, Timer->Miniport);
#endif
return;
}
*TimerCancelled = CANCEL_TIMER(&((PNDIS_TIMER)Timer)->Timer);
#if CHECK_TIMER
if (Timer->Miniport->DriverHandle->Flags & fMINIBLOCK_VERIFYING)
{
if (*TimerCancelled)
{
PNDIS_MINIPORT_TIMER *pTimer;
KIRQL OldIrql;
BOOLEAN Dequeued = FALSE;
ACQUIRE_SPIN_LOCK(&Timer->Miniport->TimerQueueLock, &OldIrql);
for (pTimer = &Timer->Miniport->TimerQueue;
*pTimer != NULL;
pTimer = &(*pTimer)->NextTimer)
{
if (*pTimer == Timer)
{
*pTimer = Timer->NextTimer;
Dequeued = TRUE;
break;
}
}
RELEASE_SPIN_LOCK(&Timer->Miniport->TimerQueueLock, OldIrql);
}
}
#endif
}
VOID
ndisMTimerDpc(
IN PKDPC Dpc,
IN PVOID Context,
IN PVOID SystemContext1,
IN PVOID SystemContext2
)
/*++
Routine Description:
This function services all mini-port timer interrupts. It then calls the
appropriate function that mini-port consumers have registered in the
call to NdisMInitializeTimer.
Arguments:
Dpc - Not used.
Context - A pointer to the NDIS_MINIPORT_TIMER which is bound to this DPC.
SystemContext1,2 - not used.
Return Value:
None.
Note:
by virtue of having either the local lock or miniport spinlock, the driver's
timer function is protected against getting unloaded .
--*/
{
PNDIS_MINIPORT_TIMER MiniportTimer = (PNDIS_MINIPORT_TIMER)(Context);
PNDIS_MINIPORT_BLOCK Miniport = MiniportTimer->Miniport;
PNDIS_TIMER_FUNCTION TimerFunction;
UNREFERENCED_PARAMETER(Dpc);
UNREFERENCED_PARAMETER(SystemContext1);
UNREFERENCED_PARAMETER(SystemContext2);
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport);
do
{
BLOCK_LOCK_MINIPORT_DPC_L(Miniport);
#if CHECK_TIMER
if (Miniport->DriverHandle->Flags & fMINIBLOCK_VERIFYING)
{
PNDIS_MINIPORT_TIMER *pTimer;
BOOLEAN Dequeued = FALSE;
ACQUIRE_SPIN_LOCK_DPC(&Miniport->TimerQueueLock);
for (pTimer = &Miniport->TimerQueue;
*pTimer != NULL;
pTimer = &(*pTimer)->NextTimer)
{
if (*pTimer == MiniportTimer)
{
//
// don't dequeue periodic timers when they fire
//
if (MiniportTimer->Timer.Period == 0)
{
*pTimer = MiniportTimer->NextTimer;
}
Dequeued = TRUE;
break;
}
}
RELEASE_SPIN_LOCK_DPC(&Miniport->TimerQueueLock);
}
#endif
if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_IN_INITIALIZE))
{
//
// Queue the timer as we cannot call the miniport
//
NdisMSetTimer(MiniportTimer, 10);
//
// Unlock the miniport
//
UNLOCK_MINIPORT_L(Miniport);
break;
}
//
// if the miniport is shut down (no, I don't mean halted)
// then don't send the timer down.
//
if (MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_SHUTTING_DOWN))
{
UNLOCK_MINIPORT_L(Miniport);
break;
}
//
// Call Miniport timer function
//
TimerFunction = MiniportTimer->MiniportTimerFunction;
NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport);
(*TimerFunction)(NULL, MiniportTimer->MiniportTimerContext, NULL, NULL);
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport);
NDISM_PROCESS_DEFERRED(Miniport);
UNLOCK_MINIPORT_L(Miniport);
} while (FALSE);
NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport);
}
VOID
ndisMTimerDpcX(
IN PKDPC Dpc,
IN PVOID Context,
IN PVOID SystemContext1,
IN PVOID SystemContext2
)
/*++
Routine Description:
This function services all mini-port timer DPCs. It then calls the
appropriate function that mini-port consumers have registered in the
call to NdisMInitializeTimer.
Arguments:
Dpc - Not used.
Context - A pointer to the NDIS_MINIPORT_TIMER which is bound to this DPC.
SystemContext1,2 - not used.
Return Value:
None.
Note:
we have to make sure the driver does not go away while the driver's timer function
is running. this can happen for example if the timer function was to signal an event
to let the Halthandler and Halt proceed.
No need to protect the miniport here becasue we do not touch the miniport after the
timer function returns.
--*/
{
PNDIS_MINIPORT_TIMER MiniportTimer = (PNDIS_MINIPORT_TIMER)(Context);
PNDIS_MINIPORT_BLOCK Miniport = MiniportTimer->Miniport;
PNDIS_M_DRIVER_BLOCK MiniDriver = Miniport->DriverHandle;
UNREFERENCED_PARAMETER(Dpc);
UNREFERENCED_PARAMETER(SystemContext1);
UNREFERENCED_PARAMETER(SystemContext2);
//
// make sure the driver does not go away while the timer function
// is running
ndisReferenceDriver(MiniDriver);
#if CHECK_TIMER
if (MiniportTimer->Miniport->DriverHandle->Flags & fMINIBLOCK_VERIFYING)
{
PNDIS_MINIPORT_TIMER *pTimer;
KIRQL OldIrql;
BOOLEAN Dequeued = FALSE;
ACQUIRE_SPIN_LOCK_DPC(&MiniportTimer->Miniport->TimerQueueLock);
for (pTimer = &Miniport->TimerQueue;
*pTimer != NULL;
pTimer = &(*pTimer)->NextTimer)
{
if (*pTimer == MiniportTimer)
{
//
// don't dequeue periodic timers when they fire
//
if (MiniportTimer->Timer.Period == 0)
{
*pTimer = MiniportTimer->NextTimer;
}
Dequeued = TRUE;
break;
}
}
RELEASE_SPIN_LOCK_DPC(&MiniportTimer->Miniport->TimerQueueLock);
}
#endif
//
// if the miniport is shut down (no, I don't mean halted)
// then don't send the timer down.
//
if (!MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_SHUTTING_DOWN))
{
(*MiniportTimer->MiniportTimerFunction)(NULL, MiniportTimer->MiniportTimerContext, NULL, NULL);
}
//
// this can be called at DPC
//
ndisDereferenceDriver(MiniDriver, FALSE);
}
NDIS_STATUS
NdisMRegisterInterrupt(
OUT PNDIS_MINIPORT_INTERRUPT Interrupt,
IN NDIS_HANDLE MiniportAdapterHandle,
IN UINT InterruptVector,
IN UINT InterruptLevel,
IN BOOLEAN RequestIsr,
IN BOOLEAN SharedInterrupt,
IN NDIS_INTERRUPT_MODE InterruptMode
)
{
PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)MiniportAdapterHandle;
NDIS_STATUS Status;
Interrupt->Reserved = (PVOID)Miniport->MiniportAdapterContext;
Miniport->Interrupt = (PNDIS_MINIPORT_INTERRUPT)Interrupt;
Status = ndisMRegisterInterruptCommon(
Interrupt,
MiniportAdapterHandle,
InterruptVector,
InterruptLevel,
RequestIsr,
SharedInterrupt,
InterruptMode);
if (Status != NDIS_STATUS_SUCCESS)
{
Miniport->Interrupt = NULL;
}
return Status;
}
VOID
NdisMDeregisterInterrupt(
IN PNDIS_MINIPORT_INTERRUPT MiniportInterrupt
)
{
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
("==>NdisMDeregisterInterrupt: Miniport %p\n", MiniportInterrupt->Miniport));
do
{
if (MiniportInterrupt->InterruptObject == NULL)
break;
ndisMDeregisterInterruptCommon(MiniportInterrupt);
MiniportInterrupt->Miniport->Interrupt = NULL;
} while (FALSE);
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
("<==NdisMDeregisterInterrupt: Miniport %p\n", MiniportInterrupt->Miniport));
}
BOOLEAN
NdisMSynchronizeWithInterrupt(
IN PNDIS_MINIPORT_INTERRUPT Interrupt,
IN PVOID SynchronizeFunction,
IN PVOID SynchronizeContext
)
{
return (SYNC_WITH_ISR((Interrupt)->InterruptObject,
SynchronizeFunction,
SynchronizeContext));
}
VOID
ndisMWakeUpDpcX(
IN PKDPC Dpc,
IN PVOID Context,
IN PVOID SystemContext1,
IN PVOID SystemContext2
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)(Context);
BOOLEAN Hung = FALSE;
PNDIS_MINIPORT_WORK_ITEM WorkItem;
NDIS_STATUS Status;
BOOLEAN AddressingReset = FALSE;
BOOLEAN fDontReset = FALSE;
UNREFERENCED_PARAMETER(Dpc);
UNREFERENCED_PARAMETER(SystemContext1);
UNREFERENCED_PARAMETER(SystemContext2);
do
{
//
// If the miniport is halting then do nothing.
//
if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_PM_HALTING))
{
break;
}
Miniport->CFHangCurrentTick--;
if (Miniport->CFHangCurrentTick == 0)
{
Miniport->CFHangCurrentTick = Miniport->CFHangTicks;
//
// Call Miniport stall checker.
//
if (Miniport->DriverHandle->MiniportCharacteristics.CheckForHangHandler != NULL)
{
Hung = (Miniport->DriverHandle->MiniportCharacteristics.CheckForHangHandler)(Miniport->MiniportAdapterContext);
}
//
// Was there a request to reset the device?
//
if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_RESTORING_FILTERS))
{
Hung = FALSE;
break;
}
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport);
//
// Check the internal wrapper states for the miniport and
// see if we think the miniport should be reset.
//
if (!Hung)
{
//
// Should we check the request queue?
// Did a request pend too long?
//
if (!MINIPORT_TEST_FLAG(Miniport, fMINIPORT_IGNORE_REQUEST_QUEUE) &&
MINIPORT_TEST_FLAG(Miniport, fMINIPORT_PROCESSING_REQUEST))
{
if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_REQUEST_TIMEOUT))
{
Miniport->InternalResetCount ++;
Hung = TRUE;
}
else
{
if (Miniport->CFHangXTicks == 0)
{
MINIPORT_SET_FLAG(Miniport, fMINIPORT_REQUEST_TIMEOUT);
}
else
{
Miniport->CFHangXTicks--;
}
}
}
}
else
{
Miniport->MiniportResetCount ++;
}
if (Hung)
{
if (NULL != Miniport->DriverHandle->MiniportCharacteristics.ResetHandler)
{
if ((MINIPORT_TEST_FLAG(Miniport, fMINIPORT_RESET_IN_PROGRESS)) ||
(MINIPORT_PNP_TEST_FLAG(Miniport, fMINIPORT_HALTING)))
{
fDontReset = TRUE;
}
else
{
MINIPORT_SET_FLAG(Miniport, fMINIPORT_RESET_IN_PROGRESS);
Miniport->ResetOpen = NULL;
}
ndisMSwapOpenHandlers(Miniport,
NDIS_STATUS_RESET_IN_PROGRESS,
fMINIPORT_STATE_RESETTING);
}
else Hung = FALSE;
}
if (Hung && !fDontReset)
{
MINIPORT_SET_FLAG(Miniport, fMINIPORT_CALLING_RESET);
//
// wait for all the requests to come back.
// note: this is not the same as waiting for all requests to complete
// we just make sure the original request call has come back
//
do
{
if (Miniport->RequestCount == 0)
{
break;
}
else
{
NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport);
NDIS_INTERNAL_STALL(50);
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport);
}
} while (TRUE);
NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport);
NdisMIndicateStatus(Miniport, NDIS_STATUS_RESET_START, NULL, 0);
NdisMIndicateStatusComplete(Miniport);
DBGPRINT(DBG_COMP_WORK_ITEM, DBG_LEVEL_INFO,
("Calling miniport reset\n"));
//
// Call the miniport's reset handler.
//
Status = (Miniport->DriverHandle->MiniportCharacteristics.ResetHandler)(
&AddressingReset,
Miniport->MiniportAdapterContext);
if (NDIS_STATUS_PENDING != Status)
{
NdisMResetComplete(Miniport, Status, AddressingReset);
}
}
else
{
NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport);
}
}
if (!Hung)
{
if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_REQUIRES_MEDIA_POLLING) == TRUE)
{
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport);
ndisMPollMediaState(Miniport);
NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport);
}
}
} while (FALSE);
}
VOID
ndisMWakeUpDpc(
IN PKDPC Dpc,
IN PVOID Context,
IN PVOID SystemContext1,
IN PVOID SystemContext2
)
/*++
Routine Description:
This function services all mini-port. It checks to see if a mini-port is
ever stalled.
Arguments:
Dpc - Not used.
Context - A pointer to the NDIS_TIMER which is bound to this DPC.
SystemContext1,2 - not used.
Return Value:
None.
--*/
{
PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)(Context);
BOOLEAN Hung = FALSE;
BOOLEAN LocalLock;
PNDIS_MINIPORT_WORK_ITEM WorkItem;
UNREFERENCED_PARAMETER(Dpc);
UNREFERENCED_PARAMETER(SystemContext1);
UNREFERENCED_PARAMETER(SystemContext2);
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport);
do
{
//
// If the miniport is halting then do nothing.
//
if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_PM_HALTING))
{
break;
}
//
// Can we get the miniport lock. If not then quit. This is not time-critical
// and we can try again next tick
//
LOCK_MINIPORT(Miniport, LocalLock);
if (!LocalLock ||
MINIPORT_TEST_FLAG(Miniport, (fMINIPORT_RESET_IN_PROGRESS | fMINIPORT_RESET_REQUESTED)))
{
UNLOCK_MINIPORT(Miniport, LocalLock);
break;
}
Miniport->CFHangCurrentTick--;
if (Miniport->CFHangCurrentTick == 0)
{
Miniport->CFHangCurrentTick = Miniport->CFHangTicks;
//
// Call Miniport stall checker.
//
if (Miniport->DriverHandle->MiniportCharacteristics.CheckForHangHandler != NULL)
{
NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport);
Hung = (Miniport->DriverHandle->MiniportCharacteristics.CheckForHangHandler)(Miniport->MiniportAdapterContext);
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport);
}
if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_RESTORING_FILTERS))
{
//
// We are restoring filters post reset. Don't pre-empt again
//
Hung = FALSE;
UNLOCK_MINIPORT(Miniport, LocalLock);
break;
}
//
// Check the internal wrapper states for the miniport and
// see if we think the miniport should be reset.
//
if (Hung)
{
Miniport->MiniportResetCount ++;
}
else do
{
//
// Should we check the request queue ? Did a request pend too long ?
//
if ((Miniport->Flags & (fMINIPORT_IGNORE_REQUEST_QUEUE|fMINIPORT_PROCESSING_REQUEST)) == fMINIPORT_PROCESSING_REQUEST)
{
if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_REQUEST_TIMEOUT))
{
Miniport->InternalResetCount ++;
Hung = TRUE;
break;
}
else
{
if (Miniport->CFHangXTicks == 0)
{
MINIPORT_SET_FLAG(Miniport, fMINIPORT_REQUEST_TIMEOUT);
}
else
{
Miniport->CFHangXTicks--;
}
}
}
//
// Should we check the packet queue ? Did a packet pend too long ?
//
if (!MINIPORT_TEST_FLAG(Miniport, fMINIPORT_IGNORE_PACKET_QUEUE))
{
PNDIS_PACKET Packet;
GET_FIRST_MINIPORT_PACKET(Miniport, &Packet);
//
// Does the miniport have possession of any packets?
//
if ((Packet != NULL) &&
MINIPORT_TEST_PACKET_FLAG(Packet, fPACKET_PENDING))
{
//
// Has the packet timed out?
//
if (MINIPORT_TEST_PACKET_FLAG(Packet, fPACKET_HAS_TIMED_OUT))
{
//
// Reset the miniport.
//
Miniport->InternalResetCount ++;
Hung = TRUE;
}
else
{
//
// Set the packet flag and wait to see if it is still
// there next time in.
//
MINIPORT_SET_PACKET_FLAG(Packet, fPACKET_HAS_TIMED_OUT);
}
}
else
{
break;
}
//
// If we are hung then we don't need to check for token ring errors.
//
if (Hung)
{
break;
}
}
//
// Are we ignoring token ring errors?
//
if (!MINIPORT_TEST_FLAG(Miniport, fMINIPORT_IGNORE_TOKEN_RING_ERRORS))
{
//
// Token Ring reset...
//
if (Miniport->TrResetRing == 1)
{
Miniport->InternalResetCount ++;
Hung = TRUE;
break;
}
else if (Miniport->TrResetRing > 1)
{
Miniport->TrResetRing--;
}
}
} while (FALSE);
//
// If the miniport is hung then queue a workitem to reset it.
//
if (Hung)
{
if (!MINIPORT_TEST_FLAG(Miniport, fMINIPORT_RESTORING_FILTERS))
{
if (NULL != Miniport->DriverHandle->MiniportCharacteristics.ResetHandler)
{
NDISM_QUEUE_WORK_ITEM(Miniport, NdisWorkItemResetRequested, NULL);
}
}
}
}
if (!Hung)
{
if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_REQUIRES_MEDIA_POLLING) == TRUE)
{
ndisMPollMediaState(Miniport);
}
}
//
// Process any changes that have occurred.
//
NDISM_PROCESS_DEFERRED(Miniport);
UNLOCK_MINIPORT_L(Miniport);
} while (FALSE);
NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport);
}
BOOLEAN
ndisMIsr(
IN PKINTERRUPT KInterrupt,
IN PVOID Context
)
/*++
Routine Description:
Handles ALL Miniport interrupts, calling the appropriate Miniport ISR and DPC
depending on the context.
Arguments:
Interrupt - Interrupt object for the Mac.
Context - Really a pointer to the interrupt.
Return Value:
None.
--*/
{
PNDIS_MINIPORT_INTERRUPT Interrupt = (PNDIS_MINIPORT_INTERRUPT)Context;
PNDIS_MINIPORT_BLOCK Miniport = Interrupt->Miniport;
BOOLEAN InterruptRecognized;
BOOLEAN IsrCalled = FALSE, QueueDpc = FALSE;
do
{
if (MINIPORT_TEST_FLAG(Miniport, fMINIPORT_NORMAL_INTERRUPTS))
{
MINIPORT_DISABLE_INTERRUPT_EX(Miniport, Interrupt);
InterruptRecognized = QueueDpc = TRUE;
}
else
{
IsrCalled = TRUE;
// Interrupt->MiniportIsr(&InterruptRecognized,
// &QueueDpc,
// Miniport->MiniportAdapterContext);
Interrupt->MiniportIsr(&InterruptRecognized,
&QueueDpc,
Interrupt->Reserved);
}
if (QueueDpc)
{
InterlockedIncrement((PLONG)&Interrupt->DpcCount);
if (QUEUE_DPC(&Interrupt->InterruptDpc))
{
break;
}
//
// The DPC was already queued, so we have an extra reference (we
// do it this way to ensure that the reference is added *before*
// the DPC is queued).
InterlockedDecrement((PLONG)&Interrupt->DpcCount);
break;
}
if (!IsrCalled)
{
if (!Interrupt->SharedInterrupt &&
!Interrupt->IsrRequested &&
!MINIPORT_TEST_FLAG(Miniport, fMINIPORT_IN_INITIALIZE))
{
ASSERT(Miniport->DisableInterruptHandler != NULL);
MINIPORT_DISABLE_INTERRUPT_EX(Miniport, Interrupt);
InterruptRecognized = TRUE;
break;
}
//
// Call MiniportIsr, but don't queue a DPC.
//
// Interrupt->MiniportIsr(&InterruptRecognized,
// &QueueDpc,
// Miniport->MiniportAdapterContext);
Interrupt->MiniportIsr(&InterruptRecognized,
&QueueDpc,
Interrupt->Reserved);
}
} while (FALSE);
return(InterruptRecognized);
}
VOID
ndisMDpc(
IN PVOID SystemSpecific1,
IN PVOID InterruptContext,
IN PVOID SystemSpecific2,
IN PVOID SystemSpecific3
)
/*++
Routine Description:
Handles ALL Miniport interrupt DPCs, calling the appropriate Miniport DPC
depending on the context.
Arguments:
Interrupt - Interrupt object for the Mac.
Context - Really a pointer to the Interrupt.
Return Value:
None.
--*/
{
PNDIS_MINIPORT_INTERRUPT Interrupt = (PNDIS_MINIPORT_INTERRUPT)(InterruptContext);
PNDIS_MINIPORT_BLOCK Miniport = Interrupt->Miniport;
W_HANDLE_INTERRUPT_HANDLER MiniportDpc = Interrupt->MiniportDpc;
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport);
do
{
if (MINIPORT_TEST_FLAG(Miniport, (fMINIPORT_PM_HALTING)) ||
Interrupt->DpcCountLock)
{
InterlockedDecrement((PLONG)&Interrupt->DpcCount);
if (Interrupt->DpcCount==0)
{
SET_EVENT(&Interrupt->DpcsCompletedEvent);
}
break;
}
BLOCK_LOCK_MINIPORT_DPC_L(Miniport);
NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport);
// (*MiniportDpc)(Miniport->MiniportAdapterContext);
(*MiniportDpc)(Interrupt->Reserved);
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport);
InterlockedDecrement((PLONG)&Interrupt->DpcCount);
MINIPORT_SYNC_ENABLE_INTERRUPT_EX(Miniport, Interrupt);
NDISM_PROCESS_DEFERRED(Miniport);
UNLOCK_MINIPORT(Miniport, TRUE);
} while (FALSE);
NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport);
}
VOID
ndisMDpcX(
IN PVOID SystemSpecific1,
IN PVOID InterruptContext,
IN PVOID SystemSpecific2,
IN PVOID SystemSpecific3
)
/*++
Routine Description:
Handles ALL Miniport interrupt DPCs, calling the appropriate Miniport DPC
depending on the context.
Arguments:
Interrupt - Interrupt object for the Mac.
Context - Really a pointer to the Interrupt.
Return Value:
None.
--*/
{
PNDIS_MINIPORT_INTERRUPT Interrupt = (PNDIS_MINIPORT_INTERRUPT)(InterruptContext);
PNDIS_MINIPORT_BLOCK Miniport = Interrupt->Miniport;
W_HANDLE_INTERRUPT_HANDLER MiniportDpc = Interrupt->MiniportDpc;
if (MINIPORT_TEST_FLAG(Miniport, (fMINIPORT_PM_HALTING)) ||
Interrupt->DpcCountLock)
{
InterlockedDecrement((PLONG)&Interrupt->DpcCount);
if (Interrupt->DpcCount==0)
{
SET_EVENT(&Interrupt->DpcsCompletedEvent);
}
}
else
{
//(*MiniportDpc)(Miniport->MiniportAdapterContext);
(*MiniportDpc)(Interrupt->Reserved);
InterlockedDecrement((PLONG)&Interrupt->DpcCount);
MINIPORT_SYNC_ENABLE_INTERRUPT_EX(Miniport, Interrupt);
}
}
VOID
ndisMDeferredDpc(
IN PKDPC Dpc,
IN PVOID Context,
IN PVOID SystemContext1,
IN PVOID SystemContext2
)
/*++
Routine Description:
This is a DPC routine that is queue'd by some of the [full-duplex] routines
in order to get ndisMProcessDeferred to run outside of their
context.
Arguments:
Return Value:
None.
--*/
{
PNDIS_MINIPORT_BLOCK Miniport = Context;
UNREFERENCED_PARAMETER(Dpc);
UNREFERENCED_PARAMETER(SystemContext1);
UNREFERENCED_PARAMETER(SystemContext2);
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK_DPC(Miniport);
BLOCK_LOCK_MINIPORT_DPC_L(Miniport);
NDISM_PROCESS_DEFERRED(Miniport);
UNLOCK_MINIPORT(Miniport, TRUE);
NDIS_RELEASE_MINIPORT_SPIN_LOCK_DPC(Miniport);
}
NDIS_STATUS
NdisMRegisterInterruptEx(
OUT PNDIS_MINIPORT_INTERRUPT_EX Interrupt,
IN NDIS_HANDLE MiniportAdapterHandle,
IN UINT InterruptVector,
IN UINT InterruptLevel,
IN BOOLEAN RequestIsr,
IN BOOLEAN SharedInterrupt,
IN NDIS_INTERRUPT_MODE InterruptMode
)
{
PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)MiniportAdapterHandle;
NDIS_STATUS Status;
KIRQL OldIrql;
Interrupt->InterruptContext = (PVOID)Interrupt;
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
Interrupt->NextInterrupt = (PNDIS_MINIPORT_INTERRUPT_EX)Miniport->Interrupt;
Miniport->Interrupt = (PNDIS_MINIPORT_INTERRUPT)Interrupt;
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
Status = ndisMRegisterInterruptCommon(
(PNDIS_MINIPORT_INTERRUPT)Interrupt,
MiniportAdapterHandle,
InterruptVector,
InterruptLevel,
RequestIsr,
SharedInterrupt,
InterruptMode);
if (Status != NDIS_STATUS_SUCCESS)
{
PNDIS_MINIPORT_INTERRUPT_EX *ppQ;
PnPReferencePackage();
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
for (ppQ = (PNDIS_MINIPORT_INTERRUPT_EX *)&Miniport->Interrupt;
*ppQ != NULL;
ppQ = &(*ppQ)->NextInterrupt)
{
if (*ppQ == Interrupt)
{
*ppQ = Interrupt->NextInterrupt;
break;
}
}
PnPDereferencePackage();
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
}
return Status;
}
VOID
NdisMDeregisterInterruptEx(
IN PNDIS_MINIPORT_INTERRUPT_EX MiniportInterrupt
)
{
PNDIS_MINIPORT_BLOCK Miniport = MiniportInterrupt->Miniport;
PNDIS_MINIPORT_INTERRUPT_EX *ppQ;
KIRQL OldIrql;
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
("==>NdisMDeregisterInterruptEx: Miniport %p\n", MiniportInterrupt->Miniport));
ndisMDeregisterInterruptCommon((PNDIS_MINIPORT_INTERRUPT)MiniportInterrupt);
PnPReferencePackage();
NDIS_ACQUIRE_MINIPORT_SPIN_LOCK(Miniport, &OldIrql);
for (ppQ = (PNDIS_MINIPORT_INTERRUPT_EX *)&Miniport->Interrupt;
*ppQ != NULL;
ppQ = &(*ppQ)->NextInterrupt)
{
if (*ppQ == MiniportInterrupt)
{
*ppQ = MiniportInterrupt->NextInterrupt;
break;
}
}
NDIS_RELEASE_MINIPORT_SPIN_LOCK(Miniport, OldIrql);
PnPDereferencePackage();
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
("<==NdisMDeregisterInterruptEx: Miniport %p\n", MiniportInterrupt->Miniport));
}
NDIS_STATUS
ndisMRegisterInterruptCommon(
OUT PNDIS_MINIPORT_INTERRUPT Interrupt,
IN NDIS_HANDLE MiniportAdapterHandle,
IN UINT InterruptVector,
IN UINT InterruptLevel,
IN BOOLEAN RequestIsr,
IN BOOLEAN SharedInterrupt,
IN NDIS_INTERRUPT_MODE InterruptMode
)
{
PNDIS_MINIPORT_BLOCK Miniport = (PNDIS_MINIPORT_BLOCK)MiniportAdapterHandle;
ULONG Vector;
NDIS_STATUS Status;
NTSTATUS NtStatus;
KIRQL Irql;
KAFFINITY InterruptAffinity;
PCM_PARTIAL_RESOURCE_DESCRIPTOR pResourceDescriptor;
PHYSICAL_ADDRESS NonTranslatedInterrupt, TranslatedIrql;
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
("==>ndisMRegisterInterruptCommon: Miniport %p\n", MiniportAdapterHandle));
if (MINIPORT_VERIFY_TEST_FLAG((PNDIS_MINIPORT_BLOCK)MiniportAdapterHandle, fMINIPORT_VERIFY_FAIL_INTERRUPT_REGISTER))
{
#if DBG
DbgPrint("NdisMRegisterInterrupt failed to verify miniport %p\n", MiniportAdapterHandle);
#endif
return NDIS_STATUS_RESOURCES;
}
do
{
Status = NDIS_STATUS_SUCCESS;
InterlockedIncrement(&Miniport->RegisteredInterrupts);
//
// We must do this stuff first because if we connect the
// interrupt first then an interrupt could occur before
// the ISR is recorded in the Ndis interrupt structure.
//
Interrupt->DpcCount = 0;
Interrupt->DpcCountLock = 0;
Interrupt->Miniport = Miniport;
Interrupt->MiniportIsr = Miniport->DriverHandle->MiniportCharacteristics.ISRHandler;
Interrupt->MiniportDpc = Miniport->HandleInterruptHandler;
Interrupt->SharedInterrupt = SharedInterrupt;
Interrupt->IsrRequested = RequestIsr;
//
// This is used to tell when all Dpcs are completed after the
// interrupt has been removed.
//
INITIALIZE_EVENT(&Interrupt->DpcsCompletedEvent);
INITIALIZE_DPC(&Interrupt->InterruptDpc,
MINIPORT_TEST_FLAG(Miniport, fMINIPORT_DESERIALIZE) ?
ndisMDpcX : ndisMDpc,
Interrupt);
SET_DPC_IMPORTANCE(&Interrupt->InterruptDpc);
SET_PROCESSOR_DPC(&Interrupt->InterruptDpc,
Miniport->AssignedProcessor);
NonTranslatedInterrupt.QuadPart = InterruptLevel;
Status = ndisTranslateResources(Miniport,
CmResourceTypeInterrupt,
NonTranslatedInterrupt,
&TranslatedIrql,
&pResourceDescriptor);
if (NDIS_STATUS_SUCCESS != Status)
{
DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO,
((" NdisMRegisterInterrupt: trying to register interrupt %p which is not allocated to device\n"),
InterruptLevel));
Status = NDIS_STATUS_FAILURE;
break;
}
Irql = (KIRQL)pResourceDescriptor->u.Interrupt.Level;
Vector = pResourceDescriptor->u.Interrupt.Vector;
InterruptAffinity = pResourceDescriptor->u.Interrupt.Affinity;
if (pResourceDescriptor->Flags == CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE)
{
InterruptMode = LevelSensitive;
}
else
{
InterruptMode = Latched;
}
//
// just in case this is not the first time we try to get an interrupt
// for this miniport (suspend/resume or if the miniport has decided to
// let go of interrupt and hook it again
//
MINIPORT_CLEAR_FLAG(Miniport, fMINIPORT_DEREGISTERED_INTERRUPT);
NtStatus = IoConnectInterrupt(&Interrupt->InterruptObject,
(PKSERVICE_ROUTINE)ndisMIsr,
Interrupt,
NULL,
Vector,
Irql,
Irql,
(KINTERRUPT_MODE)InterruptMode,
SharedInterrupt,
InterruptAffinity,
FALSE);
if (!NT_SUCCESS(NtStatus))
{
Status = NDIS_STATUS_FAILURE;
DBGPRINT_RAW(DBG_COMP_INIT, DBG_LEVEL_INFO,
((" NdisMRegisterInterrupt: IoConnectInterrupt failed on Interrupt Level:%lx, Vector: %lx\n"),
Irql, Vector));
//
// zero out the interrupt object just in case driver tries to remove the interrupt
// they are aligned in both structures
//
Interrupt->InterruptObject = NULL;
}
} while (FALSE);
if (Status != NDIS_STATUS_SUCCESS)
{
InterlockedDecrement(&Miniport->RegisteredInterrupts);
}
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
("<==ndisMRegisterInterruptCommon: Miniport %p, Status %lx\n", MiniportAdapterHandle, Status));
return Status;
}
VOID
ndisMDeregisterInterruptCommon(
IN PNDIS_MINIPORT_INTERRUPT MiniportInterrupt
)
{
LARGE_INTEGER TimeoutValue;
PNDIS_MINIPORT_BLOCK Miniport = MiniportInterrupt->Miniport;
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
("==>ndisMDeregisterInterruptCommon: Miniport %p\n", MiniportInterrupt->Miniport));
do
{
//
// drivers can register interrupts only during initialization
// and deregister them only during halt
// so here we can safely set the flag after the ref count
// goes to zero.
//
if (InterlockedDecrement(&Miniport->RegisteredInterrupts) == 0)
{
MINIPORT_SET_FLAG(MiniportInterrupt->Miniport, fMINIPORT_DEREGISTERED_INTERRUPT);
}
//
// overloading the DpcCountLock to say that interrupt is
// deregistered
//
(ULONG)MiniportInterrupt->DpcCountLock = 1;
//
// Now we disconnect the interrupt.
// NOTE: they are aligned in both structures
//
IoDisconnectInterrupt(MiniportInterrupt->InterruptObject);
//
// Right now we know that any Dpcs that may fire are counted.
// We don't have to guard this with a spin lock because the
// Dpc will set the event it completes first, or we may
// wait for a little while for it to complete.
//
TimeoutValue.QuadPart = Int32x32To64(1000, -10000); // Make it 1 second
if (MiniportInterrupt->DpcCount > 0)
{
//
// Now we wait for all dpcs to complete.
//
WAIT_FOR_OBJECT(&MiniportInterrupt->DpcsCompletedEvent, &TimeoutValue);
RESET_EVENT(&MiniportInterrupt->DpcsCompletedEvent);
}
} while (FALSE);
DBGPRINT(DBG_COMP_INIT, DBG_LEVEL_INFO,
("<==ndisMDeregisterInterruptCommon: Miniport %p\n", MiniportInterrupt->Miniport));
}