1 /* 2 * ReactOS Realtek 8139 Driver 3 * 4 * Copyright (C) 2013 Cameron Gutman 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License along 17 * with this program; if not, write to the Free Software Foundation, Inc., 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 */ 21 22 #include "nic.h" 23 24 #define NDEBUG 25 #include <debug.h> 26 27 VOID 28 NTAPI 29 MiniportISR ( 30 OUT PBOOLEAN InterruptRecognized, 31 OUT PBOOLEAN QueueMiniportHandleInterrupt, 32 IN NDIS_HANDLE MiniportAdapterContext 33 ) 34 { 35 PRTL_ADAPTER adapter = (PRTL_ADAPTER)MiniportAdapterContext; 36 ULONG csConfig; 37 38 // 39 // FIXME: We need to synchronize with this ISR for changes to InterruptPending, 40 // LinkChange, MediaState, and LinkSpeedMbps. We can get away with IRQL 41 // synchronization on non-SMP machines because we run a DIRQL here. 42 // 43 44 adapter->InterruptPending |= NICInterruptRecognized(adapter, InterruptRecognized); 45 if (!(*InterruptRecognized)) 46 { 47 // 48 // This is not ours. 49 // 50 *QueueMiniportHandleInterrupt = FALSE; 51 return; 52 } 53 54 // 55 // We have to check for a special link change interrupt before acknowledging 56 // 57 if (adapter->InterruptPending & R_I_RXUNDRUN) 58 { 59 NdisRawReadPortUlong(adapter->IoBase + R_CSCFG, &csConfig); 60 if (csConfig & R_CSCR_LINKCHNG) 61 { 62 adapter->LinkChange = TRUE; 63 NICUpdateLinkStatus(adapter); 64 } 65 } 66 67 // 68 // Acknowledge the interrupt and mark the events pending service 69 // 70 NICAcknowledgeInterrupts(adapter); 71 *QueueMiniportHandleInterrupt = TRUE; 72 } 73 74 VOID 75 NTAPI 76 MiniportHandleInterrupt ( 77 IN NDIS_HANDLE MiniportAdapterContext 78 ) 79 { 80 PRTL_ADAPTER adapter = (PRTL_ADAPTER)MiniportAdapterContext; 81 ULONG txStatus; 82 UCHAR command; 83 PPACKET_HEADER nicHeader; 84 PETH_HEADER ethHeader; 85 86 NdisDprAcquireSpinLock(&adapter->Lock); 87 88 NDIS_DbgPrint(MAX_TRACE, ("Interrupts pending: 0x%x\n", adapter->InterruptPending)); 89 90 // 91 // Handle a link change 92 // 93 if (adapter->LinkChange) 94 { 95 NdisDprReleaseSpinLock(&adapter->Lock); 96 NdisMIndicateStatus(adapter->MiniportAdapterHandle, 97 adapter->MediaState == NdisMediaStateConnected ? 98 NDIS_STATUS_MEDIA_CONNECT : NDIS_STATUS_MEDIA_DISCONNECT, 99 NULL, 100 0); 101 NdisMIndicateStatusComplete(adapter->MiniportAdapterHandle); 102 NdisDprAcquireSpinLock(&adapter->Lock); 103 adapter->LinkChange = FALSE; 104 } 105 106 // 107 // Handle a TX interrupt 108 // 109 if (adapter->InterruptPending & (R_I_TXOK | R_I_TXERR)) 110 { 111 while (adapter->TxFull || adapter->DirtyTxDesc != adapter->CurrentTxDesc) 112 { 113 NdisRawReadPortUlong(adapter->IoBase + R_TXSTS0 + 114 (adapter->DirtyTxDesc * sizeof(ULONG)), &txStatus); 115 116 if (!(txStatus & (R_TXS_STATOK | R_TXS_UNDERRUN | R_TXS_ABORTED))) 117 { 118 // 119 // Not sent yet 120 // 121 break; 122 } 123 124 NDIS_DbgPrint(MAX_TRACE, ("Transmission for desc %d complete: 0x%x\n", 125 adapter->DirtyTxDesc, txStatus)); 126 127 if (txStatus & R_TXS_STATOK) 128 { 129 adapter->TransmitOk++; 130 } 131 else 132 { 133 adapter->TransmitError++; 134 } 135 136 adapter->DirtyTxDesc++; 137 adapter->DirtyTxDesc %= TX_DESC_COUNT; 138 adapter->InterruptPending &= ~(R_I_TXOK | R_I_TXERR); 139 adapter->TxFull = FALSE; 140 } 141 } 142 143 // 144 // Handle a good RX interrupt 145 // 146 if (adapter->InterruptPending & (R_I_RXOK | R_I_RXERR)) 147 { 148 for (;;) 149 { 150 NdisRawReadPortUchar(adapter->IoBase + R_CMD, &command); 151 if (command & R_CMD_RXEMPTY) 152 { 153 // 154 // The buffer is empty 155 // 156 adapter->InterruptPending &= ~(R_I_RXOK | R_I_RXERR); 157 break; 158 } 159 160 adapter->ReceiveOffset %= RECEIVE_BUFFER_SIZE; 161 162 NDIS_DbgPrint(MAX_TRACE, ("Looking for a packet at offset 0x%x\n", 163 adapter->ReceiveOffset)); 164 nicHeader = (PPACKET_HEADER)(adapter->ReceiveBuffer + adapter->ReceiveOffset); 165 if (!(nicHeader->Status & RSR_ROK)) 166 { 167 // 168 // Receive failed 169 // 170 NDIS_DbgPrint(MIN_TRACE, ("Receive failed: 0x%x\n", nicHeader->Status)); 171 172 if (nicHeader->Status & RSR_FAE) 173 { 174 adapter->ReceiveAlignmentError++; 175 } 176 else if (nicHeader->Status & RSR_CRC) 177 { 178 adapter->ReceiveCrcError++; 179 } 180 adapter->ReceiveError++; 181 182 goto NextPacket; 183 } 184 185 NDIS_DbgPrint(MAX_TRACE, ("Indicating %d byte packet to NDIS\n", 186 nicHeader->PacketLength - RECV_CRC_LENGTH)); 187 188 ethHeader = (PETH_HEADER)(nicHeader + 1); 189 NdisMEthIndicateReceive(adapter->MiniportAdapterHandle, 190 NULL, 191 (PVOID)(ethHeader), 192 sizeof(ETH_HEADER), 193 (PVOID)(ethHeader + 1), 194 nicHeader->PacketLength - sizeof(ETH_HEADER) - RECV_CRC_LENGTH, 195 nicHeader->PacketLength - sizeof(ETH_HEADER) - RECV_CRC_LENGTH); 196 adapter->ReceiveOk++; 197 198 NextPacket: 199 adapter->ReceiveOffset += nicHeader->PacketLength + sizeof(PACKET_HEADER); 200 adapter->ReceiveOffset = (adapter->ReceiveOffset + 3) & ~3; 201 NdisRawWritePortUshort(adapter->IoBase + R_CAPR, adapter->ReceiveOffset - 0x10); 202 203 if (adapter->InterruptPending & (R_I_RXOVRFLW | R_I_FIFOOVR)) 204 { 205 // 206 // We can only clear these interrupts once CAPR has been reset 207 // 208 NdisRawWritePortUshort(adapter->IoBase + R_IS, R_I_RXOVRFLW | R_I_FIFOOVR); 209 adapter->InterruptPending &= ~(R_I_RXOVRFLW | R_I_FIFOOVR); 210 } 211 } 212 213 NdisMEthIndicateReceiveComplete(adapter->MiniportAdapterHandle); 214 } 215 216 NdisDprReleaseSpinLock(&adapter->Lock); 217 } 218