xref: /reactos/drivers/network/dd/rtl8139/interrupt.c (revision 9393fc32)
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