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