Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

629 lines
23 KiB

/*
ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
(C) Copyright 1998
All rights reserved.
ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
Portions of this software are:
(C) Copyright 1995, 1999 TriplePoint, Inc. -- http://www.TriplePoint.com
License to use this software is granted under the terms outlined in
the TriplePoint Software Services Agreement.
(C) Copyright 1992 Microsoft Corp. -- http://www.Microsoft.com
License to use this software is granted under the terms outlined in
the Microsoft Windows Device Driver Development Kit.
ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
@doc INTERNAL Interupt Interupt_c
@module Interupt.c |
This module implements the Miniport interrupt processing routines and
asynchronous processing routines. This module is very dependent on the
hardware/firmware interface and should be looked at whenever changes
to these interfaces occur.
@comm
This driver does not support the physical hardware, so there is no need
for the typical interrupt handler routines. However, the driver does
have an asynchronous event handler which is contained in this module.
@head3 Contents |
@index class,mfunc,func,msg,mdata,struct,enum | Interupt_c
@end
ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
*/
#define __FILEID__ INTERRUPT_OBJECT_TYPE
// Unique file ID for error logging
#include "Miniport.h" // Defines all the miniport objects
#if defined(NDIS_LCODE)
# pragma NDIS_LCODE // Windows 95 wants this code locked down!
# pragma NDIS_LDATA
#endif
/* @doc INTERNAL Interupt Interupt_c MiniportCheckForHang
@func
<f MiniportCheckForHang> reports the state of the network interface card.
@comm
In NIC drivers, <f MiniportCheckForHang> does nothing more than check
the internal state of the NIC and return TRUE if it detects that
the NIC is not operating correctly.
In intermediate drivers, <f MiniportCheckForHang> can periodically check the
state of the driver's virtual NIC to determine whether the underlying
device driver appears to be hung.
By default, the NDIS library calls <f MiniportCheckForHang> approximately
every two seconds.
If <f MiniportCheckForHang> returns TRUE, NDIS then calls the driver's
MiniportReset function.
If a NIC driver has no <f MiniportCheckForHang> function and NDIS
judges the driver unresponsive as, for example, when NDIS holds
many pending sends and requests queued to the miniport for a time-out
interval, NDIS calls the driver's <f MiniportReset> function. The NDIS
library's default time-out interval for queued sends and requests is
around four seconds. However, a NIC driver's <f MiniportInitialize>
function can extend NDIS's time-out interval by calling NdisMSetAttributesEx
from <f MiniportInitialize> to avoid unnecessary resets.
The <f MiniportInitialize> function of an intermediate driver
should disable NDIS's time-out interval with NdisMSetAttributesEx
because such a driver can neither control nor estimate a reasonable
completion interval for the underlying device driver.
<f MiniportCheckForHang> can be pre-empted by an interrupt.
By default, <f MiniportCheckForHang> runs at IRQL DISPATCH_LEVEL.
<f Note>:
If your hardware/firmware is flakey you can request that the NDIS
wrapper call your MiniportReset routine by returning TRUE from this
routine. For well behaved hardware/firmware you should always return
FALSE from this routine.
@rdesc
<f MiniportCheckForHang> returns FALSE if the NIC is working properly.<nl>
Otherwise, a TRUE return value indicates that the NIC needs to be reset.
*/
BOOLEAN MiniportCheckForHang(
IN PMINIPORT_ADAPTER_OBJECT pAdapter // @parm
// A pointer to the <t MINIPORT_ADAPTER_OBJECT> instance.
)
{
DBG_FUNC("MiniportCheckForHang")
// If your hardware can lockup, then you can return TRUE here.
// If you return TRUE, your MiniportReset routine will be called.
return (FALSE);
}
#if defined(CARD_REQUEST_ISR)
#if (CARD_REQUEST_ISR == FALSE)
/* @doc INTERNAL Interupt Interupt_c MiniportDisableInterrupt
@func
<f MiniportDisableInterrupt> disables the interrupt capability of
the NIC to keep it from generating interrupts.
@comm
<f MiniportDisableInterrupt> typically disables interrupts by writing
a mask to the NIC. If a driver does not have this function, typically
its <f MiniportISR> disables interrupts on the NIC.
If the NIC does not support dynamic enabling and disabling of
interrupts or if it shares an IRQ, the miniport driver must register
a <f MiniportISR> function and set RequestIsr to TRUE when it calls
NdisMRegisterMiniport. Such a driver's MiniportISR function must
acknowledge each interrupt generated by the NIC and save any
necessary interrupt information for the driver's
MiniportHandleInterrupt function.
By default, MiniportDisableInterrupt runs at DIRQL, in particular
at the DIRQL assigned when the NIC driver's MiniportInitialize
function called NdisMRegisterInterrupt. Therefore,
MiniportDisableInterrupt can call only a subset of the NDIS library
functions, such as the NdisRawXxx functions that are safe to call
at any IRQL.
If <f MiniportDisableInterrupt> shares resources, such as NIC registers,
with another MiniportXxx that runs at a lower IRQL, that MiniportXxx
must call NdisMSychronizeWithInterrupt so the driver's
<f MiniportSynchronizeISR> function will access those shared
resources in a synchronized and multiprocessor-safe manner.
Otherwise, while it is accessing the shared resources, that
MiniportXxx function can be pre-empted by <f MiniportDisableInterrupt>,
possibly undoing the work just done by MiniportXxx.
@xref
<f MiniportEnableInterrupt>
<f MiniportHandleInterrupt>
<f MiniportInitialize>
<f MiniportISR>
*/
void MiniportDisableInterrupt(
IN PMINIPORT_ADAPTER_OBJECT pAdapter // @parm
// A pointer to the <t MINIPORT_ADAPTER_OBJECT> instance.
)
{
DBG_FUNC("MiniportDisableInterrupt")
DBG_ERROR(pAdapter,("This should not be called!\n"));
}
/* @doc INTERNAL Interupt Interupt_c MiniportEnableInterrupt
@func
<f MiniportEnableInterrupt> enables the NIC to generate interrupts.
@comm
<f MiniportEnableInterrupt> typically enables interrupts by writing
a mask to the NIC.
A NIC driver that exports a <f MiniportDisableInterrupt> function
need not have a reciprocal <f MiniportEnableInterrupt> function.
Such a driver's <f MiniportHandleInterrupt> function is responsible
for re-enabling interrupts on the NIC.
If its NIC does not support dynamic enabling and disabling of
interrupts or if it shares an IRQ, the NIC driver must register
a <f MiniportISR> function and set RequestIsr to TRUE when it calls
NdisMRegisterMiniport. Such a driver's <f MiniportISR> function must
acknowledge each interrupt generated by the NIC and save any
necessary interrupt information for the driver's
<f MiniportHandleInterrupt> function.
<f MiniportEnableInterrupt> can be pre-empted by an interrupt.
By default, <f MiniportEnableInterrupt> runs at IRQL DISPATCH_LEVEL.
@xref
<f MiniportDisableInterrupt>
<f MiniportHandleInterrupt>
<f MiniportInitialize>
<f MiniportISR>
*/
void MiniportEnableInterrupt(
IN PMINIPORT_ADAPTER_OBJECT pAdapter // @parm
// A pointer to the <t MINIPORT_ADAPTER_OBJECT> instance.
)
{
DBG_FUNC("MiniportEnableInterrupt")
DBG_ERROR(pAdapter,("This should not be called!\n"));
}
#else // !(CARD_REQUEST_ISR == FALSE)
/* @doc INTERNAL Interupt Interupt_c MiniportISR
@func
<f MiniportISR> is the miniport driver's interrupt service routine
and it runs at a high priority in response to an interrupt.
@comm
Any NIC driver should do as little work as possible in its
<f MiniportISR> function, deferring I/O operations for each
interrupt the NIC generates to the <f MiniportHandleInterrupt>
function. A NIC driver's ISR is not re-entrant, although two
instantiations of a <f MiniportISR> function can execute concurrently
in SMP machines, particularly if the miniport supports
full-duplex sends and receives.
Miniport ISR is called under the following conditions:
An interrupt occurs on the NIC while the driver's <f MiniportInitialize>
or <f MiniportHalt> function is running. An interrupt occurs on the I/O bus
and the NIC shares an IRQ with other devices on that bus.
If the NIC shares an IRQ with other devices, that miniport's ISR
must be called on every interrupt to determine whether its NIC
actually generated the interrupt. If not, <f MiniportISR> should return
FALSE immediately so the driver of the device that actually generated
the interrupt is called quickly. This strategy maximizes I/O throughput
for every device on the same bus.
An interrupt occurs and the NIC driver specified that its ISR should be
called to handle every interrupt when its <f MiniportInitialize> function
called NdisMRegisterInterrupt.
Miniports that do not provide <f MiniportDisableInterrupt>/<f MiniportEnableInterrupt>
functionality must have their ISRs called on every interrupt.
<f MiniportISR> dismisses the interrupt on the NIC, saves whatever state
it must about the interrupt, and defers as much of the I/O processing
for each interrupt as possible to the <f MiniportHandleInterrupt> function.
After <f MiniportISR> returns control with the variables at InterruptRecognized
and QueueMiniportHandleInterrupt set to TRUE, the corresponding
<f MiniportHandleInterrupt> function runs at a lower hardware priority
(IRQL DISPATCH_LEVEL) than that of the ISR (DIRQL). As a general
rule, <f MiniportHandleInterrupt> should do all the work for interrupt-driven
I/O operations except for determining whether the NIC actually generated
the interrupt, and, if necessary, preserving the type (receive, send,
reset...) of interrupt.
However, a driver writer should not rely on a one-to-one correspondence
between the execution of <f MiniportISR> and <f MiniportHandleInterrupt>. A
<f MiniportHandleInterrupt> function should be written to handle the I/O
processing for more than one NIC interrupt. Its MiniportISR and
<f MiniportHandleInterrupt> functions can run concurrently in SMP machines.
Moreover, as soon as <f MiniportISR> acknowledges a NIC interrupt, the NIC
can generate another interrupt, while the <f MiniportHandleInterrupt> DPC
can be queued for execution once for such a sequence of interrupts.
The <f MiniportHandleInterrupt> function is not queued if the driver's
<f MiniportHalt> or <f MiniportInitialize> function is currently executing.
If <f MiniportISR> shares resources, such as NIC registers or state
variables, with another MiniportXxx that runs at lower IRQL,
that MiniportXxx must call NdisMSychronizeWithInterrupt so the
driver's MiniportSynchronizeISR function will access those shared
resources in a synchronized and multiprocessor-safe manner. Otherwise,
while it is accessing the shared resources, that MiniportXxx function
can be pre-empted by <f MiniportISR>, possibly undoing the work just done
by MiniportXxx.
By default, <f MiniportISR> runs at DIRQL, in particular at the DIRQL
assigned when the driver initialized the interrupt object with
NdisMRegisterInterrupt. Therefore, <f MiniportIsr> can call only a
subset of the NDIS library functions, such as the NdisRawXxx or
NdisRead/WriteRegisterXxx functions that are safe to call at
any IRQL.
@devnote
<f MiniportISR> must not call any support functions in the NDIS
interface library or the transport driver.
@xref
<f MiniportDisableInterrupt>
<f MiniportEnableInterrupt>
<f MiniportHalt>
<f MiniportHandleInterrupt>
<f MiniportInitialize>
<f MiniportSynchronizeISR>
*/
void MiniportISR(
OUT PBOOLEAN InterruptRecognized, // @parm
// If the miniport driver is sharing an interrupt line and it detects
// that the interrupt came from its NIC, <f MiniportISR> should set
// this parameter to TRUE.
OUT PBOOLEAN QueueMiniportHandleInterrupt, // @parm
// If the miniport driver is sharing an interrupt line and if
// <f MiniportHandleInterrupt> must be called to complete handling of
// the interrupt, <f MiniportISR> should set this parameter to TRUE.
IN PMINIPORT_ADAPTER_OBJECT pAdapter // @parm
// A pointer to the <t MINIPORT_ADAPTER_OBJECT> instance.
)
{
DBG_FUNC("MiniportISR")
ULONG InterruptStatus;
if ((InterruptStatus = pAdapter->TODO) == 0)
{
*InterruptRecognized =
*QueueMiniportHandleInterrupt = FALSE;
}
else
{
pAdapter->pCard->InterruptStatus = InterruptStatus;
*InterruptRecognized =
*QueueMiniportHandleInterrupt = TRUE;
}
}
#endif // (CARD_REQUEST_ISR == FALSE)
#endif // defined(CARD_REQUEST_ISR)
/* @doc INTERNAL Interupt Interupt_c MiniportHandleInterrupt
@func
<f MiniportHandleInterrupt> is called by the deferred processing routine
in the NDIS library to process an interrupt.
@comm
<f MiniportHandleInterrupt> does the deferred processing of all
outstanding interrupt operations and starts any new operations.
That is, the driver's <f MiniportISR> or <f MiniportDisableInterrupt>
function dismisses the interrupt on the NIC, saves any necessary
state about the operation, and returns control as quickly as possible,
thereby deferring most interrupt-driven I/O operations to
<f MiniportHandleInterrupt>.
<f MiniportHandleInterrupt> carries out most operations to indicate
receives on NICs that generate interrupts, including but not
limited to the following:
Adjusting the size of the buffer descriptor(s) to match the size of
the received data and chaining the buffer descriptor(s) to the packet
descriptor for the indication.
Setting up an array of packet descriptors and setting up any
out-of-band information for each packet in the array for the
indication or, if the miniport does not support multipacket
receive indications, setting up a lookahead buffer
If the driver supports multipacket receives, it must indicate
packet arrays in which the packet descriptors were allocated
from packet pool and the buffer descriptors chained to those
packets were allocated from buffer pool.
Calling the appropriate Ndis..IndicateReceive function for the
received data.
<f MiniportHandleInterrupt> also can call NdisSendComplete on packets
for which the MiniportSendPackets or <f MiniportWanSend> function
returned NDIS_STATUS_PENDING.
If the NIC shares an IRQ, <f MiniportHandleInterrupt> is called only i
f the <f MiniportISR> function returned InterruptRecognized set to
TRUE, thereby indicating that the NIC generated a particular interrupt.
When <f MiniportHandleInterrupt> is called, interrupts are disabled
on the NIC, either by the <f MiniportISR> or <f MiniportDisableInterrupt>
function. Before it returns control, <f MiniportHandleInterrupt> can
re-enable interrupts on the NIC. Otherwise, NDIS calls a driver-supplied
MiniportEnableInterrupt function to do so when <f MiniportHandleInterrupt>
returns control.
By default, <f MiniportHandleInterrupt> runs at IRQL DISPATCH_LEVEL.
@xref
<f MiniportDisableInterrupt>
<f MiniportEnableInterrupt>
<f MiniportInitialize>
<f MiniportISR>
<f MiniportWanSend>
*/
void MiniportHandleInterrupt(
IN PMINIPORT_ADAPTER_OBJECT pAdapter // @parm
// A pointer to the <t MINIPORT_ADAPTER_OBJECT> instance.
)
{
DBG_FUNC("MiniportHandleInterrupt")
PBCHANNEL_OBJECT pBChannel;
// A Pointer to one of our <t BCHANNEL_OBJECT>'s.
ULONG BChannelIndex;
// Index into the pBChannelArray.
/*
// Process NIC interrupt.
*/
CardInterruptHandler(pAdapter->pCard);
/*
// Walk through all the links to see if there is any post-proccessing
// that needs to be done.
*/
for (BChannelIndex = 0; BChannelIndex < pAdapter->NumBChannels; ++BChannelIndex)
{
pBChannel = GET_BCHANNEL_FROM_INDEX(pAdapter, BChannelIndex);
if (pBChannel->IsOpen)
{
/*
// If this is the last transmit queued on this link, and it has
// been closed, close the link and notify the protocol that the
// link has been closed.
*/
if (IsListEmpty(&pBChannel->TransmitBusyList)
&& pBChannel->CallClosing)
{
DBG_FILTER(pAdapter, DBG_TAPICALL_ON,
("#%d Call=0x%X CallState=0x%X CLOSE PENDED\n",
pBChannel->BChannelIndex,
pBChannel->htCall, pBChannel->CallState));
/*
// This must not be called until all transmits have been dequeued
// and ack'd. Otherwise the wrapper will hang waiting for transmit
// request to complete.
*/
DChannelCloseCall(pAdapter->pDChannel, pBChannel);
/*
// Indicate close complete to the wrapper.
*/
NdisMSetInformationComplete(
pAdapter->MiniportAdapterHandle,
NDIS_STATUS_SUCCESS
);
}
/*
// Indicate a receive complete if it's needed.
*/
if (pBChannel->NeedReceiveCompleteIndication)
{
pBChannel->NeedReceiveCompleteIndication = FALSE;
/*
// Indicate receive complete to the NDIS wrapper.
*/
DBG_RXC(pAdapter, pBChannel->BChannelIndex);
NdisMWanIndicateReceiveComplete(
pAdapter->MiniportAdapterHandle,
pBChannel->NdisLinkContext
);
}
}
}
/*
// Indicate a status complete if it's needed.
*/
if (pAdapter->NeedStatusCompleteIndication)
{
pAdapter->NeedStatusCompleteIndication = FALSE;
NdisMIndicateStatusComplete(pAdapter->MiniportAdapterHandle);
}
}
/* @doc INTERNAL Interupt Interupt_c MiniportTimer
ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
@func
<f MiniportTimer> is a required function if a Miniport's NIC does not
generate interrupts. Otherwise, one or more <f MiniportTimer> functions
are optional.
@comm
For a NIC that does not generate interrupts, the <f MiniportTimer>
function is used to poll the state of the NIC.
After such a driver's <f MiniportInitialize> function sets up the
driver-allocated timer object with NdisMInitializeTimer, a
call to NdisMSetPeriodicTimer causes the <f MiniportTimer> function
associated with the timer object to be run repeatedly and
automatically at the interval specified by MillisecondsPeriod.
Such a polling <f MiniportTimer> function monitors the state of the
NIC to determine when to make indications, when to complete
pending sends, and so forth. In effect, such a polling <f MiniportTimer>
function has the same functionality as the <f MiniportHandleInterrupt>
function in the driver of a NIC that does generate interrupts.
By contrast, calling NdisMSetTimer causes the <f MiniportTimer>
function associated with the timer object to be run once when
the given MillisecondsToDelay expires. Such a <f MiniportTimer>
function usually performs some driver-determined action if a
particular operation times out.
If either type of <f MiniportTimer> function shares resources with
other driver functions, the driver should synchronize access to
those resources with a spin lock.
Any NIC driver or intermediate driver can have more than one
<f MiniportTimer> function at the discretion of the driver writer.
Each such <f MiniportTimer> function must be associated with a
driver-allocated and initialized timer object.
A call to NdisMCancelTimer cancels execution of a nonpolling
<f MiniportTimer> function, provided that the interval passed in
the immediately preceding call to NdisMSetTimer has not yet
expired. After a call to NdisMSetPeriodicTimer, a call to
NdisMSetTimer or NdisMCancelTimer with the same timer object
disables a polling <f MiniportTimer> function: either the
MiniportTimer function runs once, or it is canceled.
The <f MiniportHalt> function of any driver with a <f MiniportTimer>
function should call NdisMCancelTimer to ensure that the
<f MiniportTimer> function does not attempt to access resources
that <f MiniportHalt> has already released.
By default, <f MiniportTimer> runs at IRQL DISPATCH_LEVEL.
@xref
<f MiniportHalt>
<f MiniportInitialize>
<f NdisAcquireSpinLock>
<f NdisAllocateSpinLock>
*/
void MiniportTimer(
IN PVOID SystemSpecific1, // @parm
// Points to a system-specific variable, which is opaque
// to <f MiniportTimer> and reserved for system use.
// UNREFERENCED_PARAMETER
IN PMINIPORT_ADAPTER_OBJECT pAdapter, // @parm
// A pointer to the <t MINIPORT_ADAPTER_OBJECT> instance.
IN PVOID SystemSpecific2, // @parm
// UNREFERENCED_PARAMETER
IN PVOID SystemSpecific3 // @parm
// UNREFERENCED_PARAMETER
)
{
DBG_FUNC("MiniportTimer")
DBG_ENTER(pAdapter);
/*
// If this is a nested callback, just return, and we'll loop back to
// the DoItAgain before leaving the outermost callback.
*/
if (++(pAdapter->NestedEventHandler) > 1)
{
DBG_WARNING(pAdapter,("NestedEventHandler=%d > 1\n",
pAdapter->NestedEventHandler));
return;
}
DoItAgain:
#if defined(SAMPLE_DRIVER)
/*
// This sample driver uses timer to simulate interrupts.
*/
MiniportHandleInterrupt(pAdapter);
#else // SAMPLE_DRIVER
// TODO - Add code here to handle timer interrupt events.
#endif // SAMPLE_DRIVER
/*
// If we got a nested callback, we have to loop back around.
*/
if (--(pAdapter->NestedEventHandler) > 0)
{
goto DoItAgain;
}
else if (pAdapter->NestedEventHandler < 0)
{
DBG_ERROR(pAdapter,("NestedEventHandler=%d < 0\n",
pAdapter->NestedEventHandler));
}
DBG_LEAVE(pAdapter);
UNREFERENCED_PARAMETER(SystemSpecific1);
UNREFERENCED_PARAMETER(SystemSpecific2);
UNREFERENCED_PARAMETER(SystemSpecific3);
}