* Sync up to trunk HEAD (r62285). Branch guys deserve the significant speedups too ;)
[reactos.git] / drivers / network / dd / rtl8139 / interrupt.c
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 }