windows-nt/Source/XPSP1/NT/drivers/net/irda/mknet/sys/interrup.c
2020-09-26 16:20:57 +08:00

674 lines
18 KiB
C

/*****************************************************************************
** **
** COPYRIGHT (C) 2000, 2001 MKNET CORPORATION **
** DEVELOPED FOR THE MK7100-BASED VFIR PCI CONTROLLER. **
** **
*****************************************************************************/
/**********************************************************************
Module Name:
INTERRUP.C
Procedures:
MKMiniportIsr
MKMiniportHandleInterrupt
ProcessRXComp
ProcessTXComp
ProcessRXCompIsr
ProcessTXCompIsr
Comments:
**********************************************************************/
#include "precomp.h"
#pragma hdrstop
#include "protot.h"
//-----------------------------------------------------------------------------
// Procedure: [MKMiniportIsr] (miniport)
//
// Description: This is the interrupt service routine running at interrupt level.
// It checks to see if there is an interrupt pending. If yes, it
// disables board interrupts and schedules HandleInterrupt callback.
//
// Arguments:
// MiniportAdapterContext - The context value returned by the Miniport
// when the adapter was initialized (see the call
// NdisMSetAttributes). In reality, it is a pointer to MK7_ADAPTER.
//
// Returns:
// InterruptRecognized - Returns True if the interrupt belonges to this
// adapter, and false otherwise.
// QueueMiniportHandleInterrupt - Returns True if we want a callback to
// HandleInterrupt.
//
//-----------------------------------------------------------------------------
VOID
MKMiniportIsr( OUT PBOOLEAN InterruptRecognized,
OUT PBOOLEAN QueueMiniportHandleInterrupt,
IN NDIS_HANDLE MiniportAdapterContext )
{
MK7REG mk7reg, ReadInt;
PMK7_ADAPTER Adapter = PMK7_ADAPTER_FROM_CONTEXT_HANDLE(MiniportAdapterContext);
DBGLOG("=> INT", 0);
//****************************************
// Read the Interrupt Event Reg and save to context area for
// DPC processing.
//
// IMPORTANT NOTE: The ISR runs at DIRQL level and is, thus, higher
// proiority than other miniport routines. We need to be careful
// about shared resources. Example: If our multi-pkt send is running
// when an int occurs, the send routine can be preempted. If the ISR
// and the send routine access shared resource then we have problems.
//
// We save the interrupt in recentInt because the interrupt event
// register may be cleared upon a Read. (This has been verified.)
//****************************************
//MK7Reg_Read(Adapter, R_INTS, &Adapter->recentInt);
MK7Reg_Read(Adapter, R_INTS, &ReadInt);
if (MK7OurInterrupt(ReadInt)) {
// Int enable should happen only after DPC is done.
// Also disabling interrupt clears Interrupt Status.
MK7DisableInterrupt(Adapter);
Adapter->recentInt = ReadInt;
MK7Reg_Read(Adapter, R_CFG3, &mk7reg);
if ((mk7reg & 0x1000) != 0){
mk7reg &= 0xEFFF;
MK7Reg_Write(Adapter, R_CFG3, mk7reg);
mk7reg |= 0x1000;
MK7Reg_Write(Adapter, R_CFG3, mk7reg);
// mk7reg = mk7reg; //For Debugging
}
#if DBG
GDbgStat.isrCnt++;
DBGLOG(" INT status", Adapter->recentInt);
#endif
// Don't do TX processing in ISR. I saw a condition where SetSpeed()
// was called while tcbused = 1. I set the change-speed flags correctly.
// But the TX processing in ISR cleared tcbused resulting in the code
// not chaning speed in DPC.
// ProcessTXCompIsr(Adapter);
ProcessRXCompIsr(Adapter);
*InterruptRecognized = TRUE;
*QueueMiniportHandleInterrupt = TRUE;
}
else {
*InterruptRecognized = FALSE;
*QueueMiniportHandleInterrupt = FALSE;
}
}
//-----------------------------------------------------------------------------
// Procedure: [MKMiniportHandleInterrupt]
//
// Description: This is the DPC for the ISR. It goes on to do RX & TX
// completion processing.
//
// Arguments:
// MiniportAdapterContext (miniport) - The context value returned by the
// Miniport when the adapter was initialized (see the call
// NdisMSetAttributes). In reality, it is a pointer to MK7_ADAPTER.
//
// Returns: (none)
//-----------------------------------------------------------------------------
VOID
MKMiniportHandleInterrupt(NDIS_HANDLE MiniportAdapterContext)
{
MK7REG mk7reg;
PMK7_ADAPTER Adapter = PMK7_ADAPTER_FROM_CONTEXT_HANDLE(MiniportAdapterContext);
NdisAcquireSpinLock(&Adapter->Lock);
DBGLOG("=> MKMiniportHandleInterrupt", Adapter->recentInt);
//****************************************
// DPC runs at Dispatch Level IRQL (just below ISR's DIRQL in proiority).
// Note that recentInt can be modified in the ISR routine, which is
// higher IRQL. But since this is DPC w/ int disabled, recentInt can
// be safely queried.
//****************************************
ProcessTXComp(Adapter);
ProcessRXComp(Adapter);
Adapter->recentInt = 0; // clear the saved int
NdisReleaseSpinLock(&Adapter->Lock);
MK7EnableInterrupt(Adapter);
}
//-----------------------------------------------------------------------------
// Procedure: [ProcessRXComp]
//
// Description: This is the DPC for RX completions.
//
// Arguments:
// Adapter - ptr to Adapter object instance
//
// Returns: (none)
//-----------------------------------------------------------------------------
VOID ProcessRXComp(PMK7_ADAPTER Adapter)
{
UINT PacketArrayCount, i;
// 4.0.1 BOC
UINT PacketFreeCount;
// 4.0.1 EOC
PNDIS_PACKET PacketArray[MAX_ARRAY_RECEIVE_PACKETS];
PRCB rcb;
PRRD rrd;
PRPD rpd;
UINT rrdstatus;
BOOLEAN done=FALSE;
BOOLEAN gotdata=FALSE;
MK7REG intereg;
UINT rcvcnt;
// 4.0.1 BOC
BOOLEAN LowResource;
// 4.0.1 EOC
// Process only if we get the corresponding int.
if (!(Adapter->recentInt & B_RX_INTS)) {
return;
}
DBGLOG("=> RX comp", 0);
#if DBG
GDbgStat.rxIsrCnt++;
#endif
// 1.0.0
// If we have just started receiving a packet, indicate media-busy
// to the protocol.
// if (Adapter->mediaBusy && !Adapter->haveIndicatedMediaBusy) {
// if (Adapter->CurrentSpeed > MAX_SIR_SPEED) {
//#if DBG
// DBGLOG("Error: MKMiniportHandleInterrupt is in wrong state",
// Adapter->CurrentSpeed);
//#endif
// ASSERT(0);
// }
// NdisMIndicateStatus(Adapter->MK7AdapterHandle,
// NDIS_STATUS_MEDIA_BUSY, NULL, 0);
// NdisMIndicateStatusComplete(Adapter->MK7AdapterHandle);
// RYM-5+
// May need to protect this because ISR also writes to this?
// Adapter->haveIndicatedMediaBusy = TRUE;
// }
rcb = Adapter->pRcbArray[Adapter->nextRxRcbIdx];
rrd = rcb->rrd;
rrdstatus = rrd->status; // for debug
do {
PacketArrayCount = 0;
// 4.0.1 BOC
LowResource = FALSE;
// 4.0.1 EOC.
// 4.0.1 BOC
PacketFreeCount = 0;
// 4.0.1 EOC.
// inner loop
while ( !HwOwnsRrd(rrd) &&
(PacketArrayCount < MAX_ARRAY_RECEIVE_PACKETS) ) {
// 4.0.1 BOC
if (QueueEmpty(&Adapter->FreeRpdList))
{
break;
}
// 4.0.1 EOC.
#if DBG
// DBG_STAT
if (RrdAnyError(rrd)) {
GDbgStat.rxErrCnt++;
GDbgStat.rxErr |= rrd->status;
DBGSTATUS1(" RX err: %x \n\r", rrd->status);
}
if (Adapter->recentInt & B_RX_INTS)
GDbgStat.rxComp++;
else
GDbgStat.rxCompNoInt++;
#endif
if (RrdError(rrd)) {
// If error just give RRD back to hw and continue.
// (NOTE: This may indicate errors for MIR & FIR only.
// The sw does the FCS for SIR.)
// (Note that hw may not detect all SIR errors.)
rrd->count = 0;
GrantRrdToHw(rrd);
// Next receive to read from
Adapter->nextRxRcbIdx++;
Adapter->nextRxRcbIdx %= Adapter->NumRcb;
rcb = Adapter->pRcbArray[Adapter->nextRxRcbIdx];
rrd = rcb->rrd;
rrdstatus = rrd->status; // for debug
// break; // this to do 1 rx per int
DBGLOG(" RX err", 0);
continue; // this to do > 1 rx per int
}
// Additional software processing for SIR frames
if (Adapter->CurrentSpeed <= MAX_SIR_SPEED) {
if (!ProcRXSir(rcb->rpd->databuff, (UINT)rrd->count)) {
// If error just give RRD back to hw and continue.
rrd->count = 0;
GrantRrdToHw(rrd);
// Next receive to read from
Adapter->nextRxRcbIdx++;
Adapter->nextRxRcbIdx %= Adapter->NumRcb;
rcb = Adapter->pRcbArray[Adapter->nextRxRcbIdx];
rrd = rcb->rrd;
rrdstatus = rrd->status; // for debug
#if DBG
GDbgStat.rxErrCnt++;
GDbgStat.rxErrSirCrc++;
#endif
// break; // this to do 1 rx per int
DBGLOG(" RX err", 0);
continue; // this to do > 1 rx per int
}
}
// Remove count of FCS bytes:
// SIR/MIR = 2 (16 bits)
// FIR/VFIR = 4 (32 bits)
if (Adapter->CurrentSpeed < MIN_FIR_SPEED) {
rcvcnt = (UINT) rrd->count - SIR_FCS_SIZE;
DBGLOG(" RX comp (slow)", 0);
}
else {
rcvcnt = (UINT) rrd->count - FASTIR_FCS_SIZE;
DBGLOG(" RX comp (fast)", 0);
}
NdisAdjustBufferLength(rcb->rpd->ReceiveBuffer, rcvcnt);
#if DBG
if (rcvcnt > GDbgStat.rxLargestPkt) {
GDbgStat.rxLargestPkt = rcvcnt;
}
// NdisGetCurrentSystemTime((PLARGE_INTEGER)&GDbgTARspTime[GDbgTATimeIdx]);
// GDbgTATime[GDbgTATimeIdx] = GDbgTARspTime[GDbgTATimeIdx] -
// GDbgTACmdTime[GDbgTATimeIdx];
// GDbgTATimeIdx++;
// GDbgTATimeIdx %= 1000; // wrap around
#endif
PacketArray[PacketArrayCount] = rcb->rpd->ReceivePacket;
// 4.0.1 BOC
if (((Adapter->NumRpd - Adapter->UsedRpdCount-Adapter->NumRcb) <= 4)|| LowResource==TRUE) {
NDIS_SET_PACKET_STATUS(PacketArray[PacketArrayCount], NDIS_STATUS_RESOURCES);
LowResource = TRUE;
PacketFreeCount++;
}
else {
// NDIS_SET_PACKET_STATUS(PacketArray[PacketArrayCount], NDIS_STATUS_SUCCESS);
NDIS_SET_PACKET_STATUS(PacketArray[PacketArrayCount], NDIS_STATUS_SUCCESS);
}
// 4.0.1 EOC
PacketArrayCount++;
// 4.0.1 BOC
Adapter->UsedRpdCount++;
// 4.0.1 EOC
// unbind the one we just indicated to upper layer
rcb->rpd = (PRPD)NULL;
rcb->rrd->addr = 0;
// get a new one for the next rx
rpd = (PRPD) QueuePopHead(&Adapter->FreeRpdList);
// 4.0.1 BOC
ASSERT(!(rpd == (PRPD)NULL));
// if (rpd == (PRPD)NULL) {
//****************************************
// If there's no existing RCB that's waiting for a
// RPD, set the start of waiting RCBs to this one.
//****************************************
// if (Adapter->rcbPendRpdCnt == 0) {
// Adapter->rcbPendRpdIdx = Adapter->nextRxRcbIdx;
// }
// Adapter->rcbPendRpdCnt++;
//
//#if DBG
// GDbgStat.rxNoRpd++;
//#endif
// }
// else {
// 4.0.1 EOC
// bind new RDP-Packet to RCB-RRD
rcb->rpd = rpd;
rcb->rrd->addr = rpd->databuffphys;
rcb->rrd->count = 0;
GrantRrdToHw(rcb->rrd);
// 4.0.1 BOC
// }
// 4.0.1 EOC.
// Next receive to read from
Adapter->nextRxRcbIdx++;
Adapter->nextRxRcbIdx %= Adapter->NumRcb;
rcb = Adapter->pRcbArray[Adapter->nextRxRcbIdx];
rrd = rcb->rrd;
rrdstatus = rrd->status; // for debug
} // while
if (PacketArrayCount >= MAX_ARRAY_RECEIVE_PACKETS) {
DBGLOG(" RX max indicate", 0);
}
//****************************************
// RYM-5+
// NOTE: This controls whether we poll the next ring buffers
// for data after serviceing the current ring buffer that
// caused the original RX int. The current int scheme
// is to get 1 rx buffer per int. So the following lines
// are replaced with a 1 rx per int logic.
// **We're done when we run into the 1st Ring entry that
// **we have no ownership.
//****************************************
if (HwOwnsRrd(rrd))
done = TRUE;
// done = TRUE;
// Indicate away
if(PacketArrayCount) {
NdisReleaseSpinLock(&Adapter->Lock);
NdisMIndicateReceivePacket(Adapter->MK7AdapterHandle,
PacketArray,
PacketArrayCount);
#if DBG
GDbgStat.rxPktsInd += PacketArrayCount;
#endif
gotdata = TRUE;
NdisAcquireSpinLock(&Adapter->Lock);
//DBGLOG(" ProcessRXInterrupt: indicated Packet(s)", PacketArrayCount);
}
//****************************************
// Check Packet status on return from Indicate. Pending means
// NDIS-upper layer still holds it, else it's ours.
//****************************************
// Don't do this for deserialized driver.
// for (i=0; i<PacketArrayCount; i++ ) {
// NDIS_STATUS ReturnStatus;
//
// ReturnStatus = NDIS_GET_PACKET_STATUS(PacketArray[i]);
//
// recover the RPD
// rpd = *(PRPD *)(PacketArray[i]->MiniportReserved);
//
// if (ReturnStatus != NDIS_STATUS_PENDING) {
// ProcReturnedRpd(Adapter, rpd);
// }
// }
// 4.0.1 BOC
for (i=PacketArrayCount-PacketFreeCount; i<PacketArrayCount; i++){
rpd = *(PRPD *)(PacketArray[i]->MiniportReserved);
ProcReturnedRpd(Adapter, rpd);
Adapter->UsedRpdCount--;
}
// 4.0.1 EOC.
} while (!done);
Adapter->nowReceiving = FALSE;
}
//-----------------------------------------------------------------------------
// Procedure: [ProcessTXComp]
//
// Description: TX complete processing in DPC. This is very similar to
// ProcessTXCompIsr(), the main difference being we also process the TX
// queue here and perform TXs as necessary.
//
// Arguements: Adapter.
//
// Result: (none)
//-----------------------------------------------------------------------------
VOID ProcessTXComp(PMK7_ADAPTER Adapter)
{
PTCB tcb;
MK7REG mk7reg;
NDIS_STATUS SendStatus;
PNDIS_PACKET QueuePacket;
// Process only if we get the corresponding int.
if (!(Adapter->recentInt & B_TX_INTS)) {
return;
}
DBGLOG("=> TX comp", 0);
#if DBG
GDbgStat.txIsrCnt++;
#endif
// Debug
if (Adapter->CurrentSpeed > MAX_SIR_SPEED) {
DBGLOG(" TX comp (fast)", 0);
}
else {
DBGLOG(" TX comp (slow)", 0);
}
// Simplified change speed
if (Adapter->changeSpeedPending == CHANGESPEED_ON_DONE) {
// Note: We're changing speed in TX mode.
MK7ChangeSpeedNow(Adapter);
Adapter->changeSpeedPending = 0;
}
while (Adapter->tcbUsed > 0) {
tcb = Adapter->pTcbArray[Adapter->nextReturnTcbIdx];
if ( !HwOwnsTrd(tcb->trd) ) {
#if DBG
if (TrdAnyError(tcb->trd)) {
GDbgStat.txErrCnt++;
GDbgStat.txErr |= tcb->trd->status;
DBGSTATUS1(" TX err: %x \n\r", tcb->trd->status);
}
if (Adapter->recentInt & B_TX_INTS)
GDbgStat.txComp++;
else
GDbgStat.txCompNoInt++;
#endif
tcb->trd->count = 0;
// For each completing TX there's a corresponding q'd pkt.
// We release it here.
QueuePacket = Adapter->FirstTxQueue;
DequeuePacket(Adapter->FirstTxQueue, Adapter->LastTxQueue);
Adapter->NumPacketsQueued--;
NDIS_SET_PACKET_STATUS(QueuePacket, NDIS_STATUS_SUCCESS);
NdisMSendComplete( Adapter->MK7AdapterHandle,
QueuePacket,
NDIS_STATUS_SUCCESS);
Adapter->HangCheck = 0; // 1.0.0
Adapter->nextReturnTcbIdx++;
Adapter->nextReturnTcbIdx %= Adapter->NumTcb;
Adapter->tcbUsed--;
}
else {
DBGLOG(" Not our TCB; but tcbUsed>0", 0);
break;
}
}
// No resource even if we have more to send. Return now & let subsequent
// TX completes keep the ball rolling.
if (Adapter->tcbUsed >= Adapter->NumTcb) {
// NdisReleaseSpinLock(&Adapter->Lock);
return;
}
// If no TXs queued and all TXs are done, then switch to RX mode.
if ( (!Adapter->FirstTxQueue) && (Adapter->tcbUsed == 0) ) {
MK7SwitchToRXMode(Adapter);
return;
}
// Send the q'd pkts until all done or until all TX ring buffers are used up.
//while(Adapter->FirstTxQueue) {
if (Adapter->FirstTxQueue) {
#if DBG
GDbgStat.txProcQ++;
#endif
DBGLOG(" Proc Q", 0);
QueuePacket = Adapter->FirstTxQueue;
SendStatus = SendPkt(Adapter, QueuePacket);
}
}
//-----------------------------------------------------------------------------
// Procedure: [ProcessRXCompIsr]
//
// Description: Some RX complete processing in ISR.
//
// Arguements: Adapter.
//
// Result: (none)
//-----------------------------------------------------------------------------
VOID ProcessRXCompIsr(PMK7_ADAPTER Adapter)
{
// 4.1.0 Back for HW_VER_1 support
if (Adapter->recentInt & B_RX_INTS) {
Adapter->nowReceiving=TRUE;
// if (!Adapter->mediaBusy) {
// mediaBusy: IrLAP clears mediaBusy (via OID) to indicate
// it wants to be notified when media becomes busy. Here
// we detect it is cleared. We then set it and clear
// haveIndicatedMediaBusy so we do notify later in DPC.
// Adapter->mediaBusy = TRUE;
// Adapter->haveIndicatedMediaBusy = FALSE;
// Adapter->nowReceiving = TRUE;
// }
}
}
//-----------------------------------------------------------------------------
// Procedure: [ProcessTXCompIsr]
//
// Description: TX complete processing in ISR. This is very similar to
// ProcessTXComp() except we don't start any TX's here.
//
// Arguements: Adapter.
//
// Result: (none)
//-----------------------------------------------------------------------------
VOID ProcessTXCompIsr(PMK7_ADAPTER Adapter)
{
PTCB tcb;
MK7REG mk7reg;
//******************************
// Whether or not there was a TX-completion interrupt, we do some
// processing here in case ever the driver or hw missed an
// interrupt previously.
//
// We loop until all tcb's are returned (tcbUsed == 0) or we run into
// a TX ring buff that the hw still owns (HwOwnsTrd()). When we leave
// here, we should have processed all current TX completions based on
// the TX ownership bit. We switch to RX mode ONLY after all TX are
// completed (either here in the ISR or in DPC).
//******************************
while (Adapter->tcbUsed > 0) {
tcb = Adapter->pTcbArray[Adapter->nextReturnTcbIdx];
if ( !HwOwnsTrd(tcb->trd) ) {
#if DBG
if (TrdAnyError(tcb->trd)) {
GDbgStat.txErrCnt++;
GDbgStat.txErr |= tcb->trd->status;
DBGSTATUS1(" TX err: %x \n\r", tcb->trd->status);
}
if (Adapter->recentInt & B_TX_INTS)
GDbgStat.txComp++;
else
GDbgStat.txCompNoInt++;
#endif
tcb->trd->count = 0;
Adapter->nextReturnTcbIdx++;
Adapter->nextReturnTcbIdx %= Adapter->NumTcb;
Adapter->tcbUsed--;
}
else {
return;
}
}
}