/***************************************************************************** ** ** ** 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; iMiniportReserved); // // if (ReturnStatus != NDIS_STATUS_PENDING) { // ProcReturnedRpd(Adapter, rpd); // } // } // 4.0.1 BOC for (i=PacketArrayCount-PacketFreeCount; iMiniportReserved); 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; } } }