xref: /reactos/drivers/network/dd/dc21x4/interrupt.c (revision bdae8cf9)
1 /*
2  * PROJECT:     ReactOS DC21x4 Driver
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Interrupt handling
5  * COPYRIGHT:   Copyright 2023 Dmitry Borisov <di.sean@protonmail.com>
6  */
7 
8 /* INCLUDES *******************************************************************/
9 
10 #include "dc21x4.h"
11 
12 #include <debug.h>
13 
14 /* FUNCTIONS ******************************************************************/
15 
16 static
17 VOID
18 DcAdjustTxFifoThreshold(
19     _In_ PDC21X4_ADAPTER Adapter)
20 {
21     ULONG OpMode;
22 
23     TRACE("TX underrun\n");
24 
25     /* Maximum threshold reached */
26     if ((Adapter->OpMode & DC_OPMODE_STORE_AND_FORWARD) ||
27         (++Adapter->TransmitUnderruns < DC_TX_UNDERRUN_LIMIT))
28     {
29         NdisDprAcquireSpinLock(&Adapter->SendLock);
30 
31         /* Start the transmit process if it was suspended */
32         DC_WRITE(Adapter, DcCsr1_TxPoll, DC_TX_POLL_DOORBELL);
33 
34         NdisDprReleaseSpinLock(&Adapter->SendLock);
35         return;
36     }
37     Adapter->TransmitUnderruns = 0;
38 
39     NdisDprAcquireSpinLock(&Adapter->ModeLock);
40 
41     OpMode = Adapter->OpMode;
42 
43     /* Update the FIFO threshold level to minimize Tx FIFO underrun */
44     if ((OpMode & DC_OPMODE_TX_THRESHOLD_CTRL_MASK) != DC_OPMODE_TX_THRESHOLD_MAX)
45     {
46         OpMode += DC_OPMODE_TX_THRESHOLD_LEVEL;
47 
48         INFO("New OP Mode %08lx\n", OpMode);
49     }
50     else
51     {
52         OpMode |= DC_OPMODE_STORE_AND_FORWARD;
53 
54         INFO("Store & Forward\n");
55     }
56 
57     DcStopTxRxProcess(Adapter);
58 
59     Adapter->OpMode = OpMode;
60 
61     /* Restart the transmit process */
62     DC_WRITE(Adapter, DcCsr6_OpMode, OpMode);
63 
64     NdisDprReleaseSpinLock(&Adapter->ModeLock);
65 }
66 
67 static
68 VOID
69 DcHandleTxJabberTimeout(
70     _In_ PDC21X4_ADAPTER Adapter)
71 {
72     WARN("Transmit jabber timer timed out\n");
73 
74     NdisWriteErrorLogEntry(Adapter->AdapterHandle, NDIS_ERROR_CODE_HARDWARE_FAILURE, 1, __LINE__);
75 
76     NdisDprAcquireSpinLock(&Adapter->ModeLock);
77 
78     /* Start the transmit process if it was stopped */
79     DC_WRITE(Adapter, DcCsr6_OpMode, Adapter->OpMode);
80 
81     NdisDprReleaseSpinLock(&Adapter->ModeLock);
82 }
83 
84 static
85 VOID
86 DcHandleTxCompletedFrames(
87     _In_ PDC21X4_ADAPTER Adapter,
88     _Inout_ PLIST_ENTRY SendReadyList,
89     _Out_ PULONG DpcEvents)
90 {
91     PDC_TCB Tcb;
92     PDC_TBD Tbd;
93     ULONG TbdStatus, Collisions;
94 
95     for (Tcb = Adapter->LastTcb;
96          Tcb != Adapter->CurrentTcb;
97          Tcb = DC_NEXT_TCB(Adapter, Tcb))
98     {
99         Tbd = Tcb->Tbd;
100         TbdStatus = Tbd->Status;
101 
102         if (TbdStatus & DC_TBD_STATUS_OWNED)
103             break;
104 
105         ++Adapter->TcbCompleted;
106 
107         /* Complete the packet filter change request asynchronously */
108         if (Tbd->Control & DC_TBD_CONTROL_SETUP_FRAME)
109         {
110             Tbd->Control &= ~DC_TBD_CONTROL_SETUP_FRAME;
111 
112             if (Tbd->Control & DC_TBD_CONTROL_REQUEST_INTERRUPT)
113             {
114                 *DpcEvents |= DC_EVENT_SETUP_FRAME_COMPLETED;
115             }
116 
117             continue;
118         }
119 
120         /* This is our media test packet, so no need to update the TX statistics */
121         if (!Tcb->Packet)
122         {
123             _InterlockedExchange(&Adapter->MediaTestStatus,
124                                  !(TbdStatus & DC_TBD_STATUS_ERROR_SUMMARY));
125 
126             ASSERT(Adapter->LoopbackFrameSlots < DC_LOOPBACK_FRAMES);
127 
128             ++Adapter->LoopbackFrameSlots;
129 
130             continue;
131         }
132 
133         if (TbdStatus & DC_TBD_STATUS_ERROR_SUMMARY)
134         {
135             ++Adapter->Statistics.TransmitErrors;
136 
137             if (TbdStatus & DC_TBD_STATUS_UNDERFLOW)
138                 ++Adapter->Statistics.TransmitUnderrunErrors;
139             else if (TbdStatus & DC_TBD_STATUS_LATE_COLLISION)
140                 ++Adapter->Statistics.TransmitLateCollisions;
141 
142             if (TbdStatus & DC_TBD_STATUS_RETRY_ERROR)
143                 ++Adapter->Statistics.TransmitExcessiveCollisions;
144             if (TbdStatus & DC_TBD_STATUS_CARRIER_LOST)
145                 ++Adapter->Statistics.TransmitLostCarrierSense;
146         }
147         else
148         {
149             ++Adapter->Statistics.TransmitOk;
150 
151             if (TbdStatus & DC_TBD_STATUS_DEFFERED)
152                 ++Adapter->Statistics.TransmitDeferred;
153             if (TbdStatus & DC_TBD_STATUS_HEARTBEAT_FAIL)
154                 ++Adapter->Statistics.TransmitHeartbeatErrors;
155 
156             Collisions = (TbdStatus & DC_TBD_STATUS_COLLISIONS_MASK) >>
157                          DC_TBD_STATUS_COLLISIONS_SHIFT;
158             if (Collisions == 1)
159                 ++Adapter->Statistics.TransmitOneRetry;
160             else if (Collisions > 1)
161                 ++Adapter->Statistics.TransmitMoreCollisions;
162         }
163 
164         InsertTailList(SendReadyList, DC_LIST_ENTRY_FROM_PACKET(Tcb->Packet));
165 
166         DC_RELEASE_TCB(Adapter, Tcb);
167     }
168 
169     Adapter->LastTcb = Tcb;
170 }
171 
172 static
173 VOID
174 DcHandleTx(
175     _In_ PDC21X4_ADAPTER Adapter)
176 {
177     LIST_ENTRY SendReadyList;
178     ULONG DpcEvents;
179 
180     TRACE("Handle TX\n");
181 
182     InitializeListHead(&SendReadyList);
183     DpcEvents = 0;
184 
185     NdisDprAcquireSpinLock(&Adapter->SendLock);
186 
187     DcHandleTxCompletedFrames(Adapter, &SendReadyList, &DpcEvents);
188 
189     if (!IsListEmpty(&Adapter->SendQueueList))
190     {
191         DcProcessPendingPackets(Adapter);
192     }
193 
194     NdisDprReleaseSpinLock(&Adapter->SendLock);
195 
196     while (!IsListEmpty(&SendReadyList))
197     {
198         PLIST_ENTRY Entry = RemoveHeadList(&SendReadyList);
199 
200         TRACE("Complete TX packet %p\n", DC_PACKET_FROM_LIST_ENTRY(Entry));
201 
202         NdisMSendComplete(Adapter->AdapterHandle,
203                           DC_PACKET_FROM_LIST_ENTRY(Entry),
204                           NDIS_STATUS_SUCCESS);
205     }
206 
207     /* We have to complete the OID request outside of the spinlock */
208     if (DpcEvents & DC_EVENT_SETUP_FRAME_COMPLETED)
209     {
210         TRACE("SP completed\n");
211 
212         Adapter->OidPending = FALSE;
213 
214         NdisMSetInformationComplete(Adapter->AdapterHandle, NDIS_STATUS_SUCCESS);
215     }
216 }
217 
218 static
219 VOID
220 DcStopRxProcess(
221     _In_ PDC21X4_ADAPTER Adapter)
222 {
223     ULONG i, OpMode, Status;
224 
225     OpMode = Adapter->OpMode;
226     OpMode &= ~DC_OPMODE_RX_ENABLE;
227     DC_WRITE(Adapter, DcCsr6_OpMode, OpMode);
228 
229     for (i = 0; i < 5000; ++i)
230     {
231         Status = DC_READ(Adapter, DcCsr5_Status);
232 
233         if ((Status & DC_STATUS_RX_STATE_MASK) == DC_STATUS_RX_STATE_STOPPED)
234             return;
235 
236         NdisStallExecution(10);
237     }
238 
239     WARN("Failed to stop the RX process 0x%08lx\n", Status);
240 }
241 
242 VOID
243 NTAPI
244 DcReturnPacket(
245     _In_ NDIS_HANDLE MiniportAdapterContext,
246     _In_ PNDIS_PACKET Packet)
247 {
248     PDC21X4_ADAPTER Adapter = (PDC21X4_ADAPTER)MiniportAdapterContext;
249     PDC_RCB Rcb;
250 
251     Rcb = *DC_RCB_FROM_PACKET(Packet);
252 
253     NdisAcquireSpinLock(&Adapter->ReceiveLock);
254 
255     PushEntryList(&Adapter->FreeRcbList, &Rcb->ListEntry);
256 
257     ++Adapter->RcbFree;
258 
259     NdisReleaseSpinLock(&Adapter->ReceiveLock);
260 }
261 
262 static
263 VOID
264 DcIndicateReceivePackets(
265     _In_ PDC21X4_ADAPTER Adapter,
266     _In_ PNDIS_PACKET* ReceiveArray,
267     _In_ ULONG PacketsToIndicate)
268 {
269     PNDIS_PACKET Packet;
270     PDC_RBD Rbd;
271     PDC_RCB Rcb;
272     ULONG i;
273 
274     NdisDprReleaseSpinLock(&Adapter->ReceiveLock);
275 
276     NdisMIndicateReceivePacket(Adapter->AdapterHandle,
277                                ReceiveArray,
278                                PacketsToIndicate);
279 
280     NdisDprAcquireSpinLock(&Adapter->ReceiveLock);
281 
282     for (i = 0; i < PacketsToIndicate; ++i)
283     {
284         Packet = ReceiveArray[i];
285         Rcb = *DC_RCB_FROM_PACKET(Packet);
286 
287         /* Reuse the RCB immediately */
288         if (Rcb->Flags & DC_RCB_FLAG_RECLAIM)
289         {
290             Rbd = *DC_RBD_FROM_PACKET(Packet);
291 
292             Rbd->Status = DC_RBD_STATUS_OWNED;
293         }
294     }
295 }
296 
297 static
298 VOID
299 DcHandleRxReceivedFrames(
300     _In_ PDC21X4_ADAPTER Adapter)
301 {
302     PDC_RBD Rbd, StartRbd, LastRbd;
303     ULONG PacketsToIndicate;
304     PNDIS_PACKET ReceiveArray[DC_RECEIVE_ARRAY_SIZE];
305 
306     Rbd = StartRbd = LastRbd = Adapter->CurrentRbd;
307     PacketsToIndicate = 0;
308 
309     while (PacketsToIndicate < RTL_NUMBER_OF(ReceiveArray))
310     {
311         PDC_RCB Rcb;
312         PDC_RCB* RcbSlot;
313         PNDIS_PACKET Packet;
314         ULONG RbdStatus, PacketLength, RxCounters;
315 
316         if (Rbd->Status & DC_RBD_STATUS_OWNED)
317             break;
318 
319         /* Work around the RX overflow bug */
320         if ((Adapter->Features & DC_NEED_RX_OVERFLOW_WORKAROUND) && (Rbd == LastRbd))
321         {
322             /* Find the last received packet, to correctly catch invalid packets */
323             do
324             {
325                 LastRbd = DC_NEXT_RBD(Adapter, LastRbd);
326 
327                 if (LastRbd->Status & DC_RBD_STATUS_OWNED)
328                     break;
329             }
330             while (LastRbd != Rbd);
331 
332             RxCounters = DC_READ(Adapter, DcCsr8_RxCounters);
333 
334             Adapter->Statistics.ReceiveNoBuffers += RxCounters & DC_COUNTER_RX_NO_BUFFER_MASK;
335 
336             /* A receive overflow might indicate a data corruption */
337             if (RxCounters & DC_COUNTER_RX_OVERFLOW_MASK)
338             {
339                 ERR("RX overflow, dropping the packets\n");
340 
341                 Adapter->Statistics.ReceiveOverrunErrors +=
342                     (RxCounters & DC_COUNTER_RX_OVERFLOW_MASK) >> DC_COUNTER_RX_OVERFLOW_SHIFT;
343 
344                 NdisDprAcquireSpinLock(&Adapter->ModeLock);
345 
346                 /* Stop the receive process */
347                 DcStopRxProcess(Adapter);
348 
349                 /* Drop all received packets regardless of what the status indicates */
350                 while (TRUE)
351                 {
352                     if (Rbd->Status & DC_RBD_STATUS_OWNED)
353                         break;
354 
355                     ++Adapter->Statistics.ReceiveOverrunErrors;
356 
357                     Rbd->Status = DC_RBD_STATUS_OWNED;
358 
359                     Rbd = DC_NEXT_RBD(Adapter, Rbd);
360                 }
361                 LastRbd = Rbd;
362 
363                 /* Restart the receive process */
364                 DC_WRITE(Adapter, DcCsr6_OpMode, Adapter->OpMode);
365 
366                 NdisDprReleaseSpinLock(&Adapter->ModeLock);
367 
368                 continue;
369             }
370         }
371 
372         RbdStatus = Rbd->Status;
373 
374         /* Ignore oversized packets */
375         if (!(RbdStatus & DC_RBD_STATUS_LAST_DESCRIPTOR))
376         {
377             Rbd->Status = DC_RBD_STATUS_OWNED;
378             goto NextRbd;
379         }
380 
381         /* Check for an invalid packet */
382         if (RbdStatus & DC_RBD_STATUS_INVALID)
383         {
384             ++Adapter->Statistics.ReceiveErrors;
385 
386             if (RbdStatus & DC_RBD_STATUS_OVERRUN)
387                 ++Adapter->Statistics.ReceiveOverrunErrors;
388 
389             if (RbdStatus & DC_RBD_STATUS_CRC_ERROR)
390             {
391                 if (RbdStatus & DC_RBD_STATUS_DRIBBLE)
392                     ++Adapter->Statistics.ReceiveAlignmentErrors;
393                 else
394                     ++Adapter->Statistics.ReceiveCrcErrors;
395             }
396 
397             Rbd->Status = DC_RBD_STATUS_OWNED;
398             goto NextRbd;
399         }
400 
401         ++Adapter->Statistics.ReceiveOk;
402 
403         PacketLength = (RbdStatus & DC_RBD_STATUS_FRAME_LENGTH_MASK) >>
404                        DC_RBD_STATUS_FRAME_LENGTH_SHIFT;
405 
406         /* Omit the CRC */
407         PacketLength -= 4;
408 
409         RcbSlot = DC_GET_RCB_SLOT(Adapter, Rbd);
410         Rcb = *RcbSlot;
411 
412         TRACE("RX packet (len %u), RCB %p\n", PacketLength, Rcb);
413 
414         NdisAdjustBufferLength(Rcb->NdisBuffer, PacketLength);
415 
416         /* Receive buffers are in cached memory */
417         NdisFlushBuffer(Rcb->NdisBuffer, FALSE);
418 
419         if (RbdStatus & DC_RBD_STATUS_MULTICAST)
420         {
421             if (ETH_IS_BROADCAST(Rcb->VirtualAddress))
422                 ++Adapter->Statistics.ReceiveBroadcast;
423             else
424                 ++Adapter->Statistics.ReceiveMulticast;
425         }
426         else
427         {
428             ++Adapter->Statistics.ReceiveUnicast;
429         }
430 
431         Packet = Rcb->Packet;
432 
433         ReceiveArray[PacketsToIndicate++] = Packet;
434 
435         if (Adapter->FreeRcbList.Next)
436         {
437             Rcb->Flags = 0;
438             NDIS_SET_PACKET_STATUS(Packet, NDIS_STATUS_SUCCESS);
439 
440             Rcb = (PDC_RCB)DcPopEntryList(&Adapter->FreeRcbList);
441             *RcbSlot = Rcb;
442 
443             ASSERT(Adapter->RcbFree > 0);
444             --Adapter->RcbFree;
445 
446             Rbd->Address1 = Rcb->PhysicalAddress;
447             DC_WRITE_BARRIER();
448             Rbd->Status = DC_RBD_STATUS_OWNED;
449         }
450         else
451         {
452             Rcb->Flags = DC_RCB_FLAG_RECLAIM;
453             NDIS_SET_PACKET_STATUS(Packet, NDIS_STATUS_RESOURCES);
454 
455             *DC_RBD_FROM_PACKET(Packet) = Rbd;
456         }
457 
458 NextRbd:
459         Rbd = DC_NEXT_RBD(Adapter, Rbd);
460 
461         /*
462          * Check the next descriptor to prevent wrap-around.
463          * Since we don't use a fixed-sized ring,
464          * the receive ring may be smaller in length than the ReceiveArray[].
465          */
466         if (Rbd == StartRbd)
467             break;
468     }
469 
470     Adapter->CurrentRbd = Rbd;
471 
472     /* Pass the packets up */
473     if (PacketsToIndicate)
474     {
475         DcIndicateReceivePackets(Adapter, ReceiveArray, PacketsToIndicate);
476     }
477 }
478 
479 static
480 VOID
481 DcHandleRx(
482     _In_ PDC21X4_ADAPTER Adapter)
483 {
484     NdisDprAcquireSpinLock(&Adapter->ReceiveLock);
485 
486     do
487     {
488         DcHandleRxReceivedFrames(Adapter);
489     }
490     while (!(Adapter->CurrentRbd->Status & DC_RBD_STATUS_OWNED));
491 
492     NdisDprReleaseSpinLock(&Adapter->ReceiveLock);
493 }
494 
495 static
496 VOID
497 DcHandleSystemError(
498     _In_ PDC21X4_ADAPTER Adapter,
499     _In_ ULONG InterruptStatus)
500 {
501     ERR("%s error occured, CSR5 %08lx\n", DcDbgBusError(InterruptStatus), InterruptStatus);
502 
503     NdisWriteErrorLogEntry(Adapter->AdapterHandle, NDIS_ERROR_CODE_HARDWARE_FAILURE, 1, __LINE__);
504 
505     /* Issue a software reset, which also enables the interrupts */
506     if (_InterlockedCompareExchange(&Adapter->ResetLock, 2, 0) == 0)
507     {
508         NdisScheduleWorkItem(&Adapter->ResetWorkItem);
509     }
510 }
511 
512 VOID
513 NTAPI
514 DcHandleInterrupt(
515     _In_ NDIS_HANDLE MiniportAdapterContext)
516 {
517     ULONG InterruptStatus, IoLimit;
518     PDC21X4_ADAPTER Adapter = (PDC21X4_ADAPTER)MiniportAdapterContext;
519 
520     TRACE("Events %08lx\n", Adapter->InterruptStatus);
521 
522     if (!(Adapter->Flags & DC_ACTIVE))
523         return;
524 
525     IoLimit = DC_INTERRUPT_PROCESSING_LIMIT;
526     InterruptStatus = Adapter->InterruptStatus;
527 
528     /* Loop until the condition to stop is encountered */
529     while (TRUE)
530     {
531         /* Uncommon interrupts */
532         if (InterruptStatus & DC_IRQ_ABNORMAL_SUMMARY)
533         {
534             /* PCI bus error detected */
535             if (InterruptStatus & DC_IRQ_SYSTEM_ERROR)
536             {
537                 DcHandleSystemError(Adapter, InterruptStatus);
538                 return;
539             }
540 
541             /* Transmit jabber timeout */
542             if (InterruptStatus & DC_IRQ_TX_JABBER_TIMEOUT)
543             {
544                 DcHandleTxJabberTimeout(Adapter);
545             }
546 
547             /* Link state changed */
548             if (InterruptStatus & Adapter->LinkStateChangeMask)
549             {
550                 Adapter->HandleLinkStateChange(Adapter, InterruptStatus);
551             }
552         }
553 
554         /* Handling receive interrupts */
555         if (InterruptStatus & (DC_IRQ_RX_OK | DC_IRQ_RX_STOPPED))
556         {
557             DcHandleRx(Adapter);
558         }
559 
560         /* Handling transmit interrupts */
561         if (InterruptStatus & (DC_IRQ_TX_OK | DC_IRQ_TX_STOPPED))
562         {
563             DcHandleTx(Adapter);
564         }
565 
566         /* Transmit underflow error detected */
567         if (InterruptStatus & DC_IRQ_TX_UNDERFLOW)
568         {
569             DcAdjustTxFifoThreshold(Adapter);
570         }
571 
572         /* Limit in order to avoid doing too much work at DPC level */
573         if (!--IoLimit)
574             break;
575 
576         /* Check if new events have occurred */
577         InterruptStatus = DC_READ(Adapter, DcCsr5_Status);
578         if (InterruptStatus == 0xFFFFFFFF || !(InterruptStatus & Adapter->InterruptMask))
579             break;
580 
581         /* Acknowledge the events */
582         DC_WRITE(Adapter, DcCsr5_Status, InterruptStatus);
583     }
584 
585     /* TODO: Add interrupt mitigation (CSR11) */
586 
587     /* Reenable interrupts */
588     _InterlockedExchange((PLONG)&Adapter->CurrentInterruptMask, Adapter->InterruptMask);
589     DC_WRITE(Adapter, DcCsr7_IrqMask, Adapter->InterruptMask);
590 }
591 
592 VOID
593 NTAPI
594 DcIsr(
595     _Out_ PBOOLEAN InterruptRecognized,
596     _Out_ PBOOLEAN QueueMiniportHandleInterrupt,
597     _In_ NDIS_HANDLE MiniportAdapterContext)
598 {
599     PDC21X4_ADAPTER Adapter = (PDC21X4_ADAPTER)MiniportAdapterContext;
600     ULONG InterruptStatus;
601 
602     if (Adapter->CurrentInterruptMask == 0)
603         goto NotOurs;
604 
605     InterruptStatus = DC_READ(Adapter, DcCsr5_Status);
606     if (InterruptStatus == 0xFFFFFFFF || !(InterruptStatus & Adapter->CurrentInterruptMask))
607         goto NotOurs;
608 
609     /* Disable further interrupts */
610     DC_WRITE(Adapter, DcCsr7_IrqMask, 0);
611 
612     /* Clear all pending events */
613     DC_WRITE(Adapter, DcCsr5_Status, InterruptStatus);
614 
615     Adapter->InterruptStatus = InterruptStatus;
616     Adapter->CurrentInterruptMask = 0;
617 
618     *InterruptRecognized = TRUE;
619     *QueueMiniportHandleInterrupt = TRUE;
620     return;
621 
622 NotOurs:
623     *InterruptRecognized = FALSE;
624     *QueueMiniportHandleInterrupt = FALSE;
625 }
626