mirror of
https://github.com/reactos/reactos.git
synced 2024-08-26 05:08:39 +00:00
218 lines
7.1 KiB
C
218 lines
7.1 KiB
C
/*
|
|
* ReactOS Realtek 8139 Driver
|
|
*
|
|
* Copyright (C) 2013 Cameron Gutman
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
*/
|
|
|
|
#include "nic.h"
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
VOID
|
|
NTAPI
|
|
MiniportISR (
|
|
OUT PBOOLEAN InterruptRecognized,
|
|
OUT PBOOLEAN QueueMiniportHandleInterrupt,
|
|
IN NDIS_HANDLE MiniportAdapterContext
|
|
)
|
|
{
|
|
PRTL_ADAPTER adapter = (PRTL_ADAPTER)MiniportAdapterContext;
|
|
ULONG csConfig;
|
|
|
|
//
|
|
// FIXME: We need to synchronize with this ISR for changes to InterruptPending,
|
|
// LinkChange, MediaState, and LinkSpeedMbps. We can get away with IRQL
|
|
// synchronization on non-SMP machines because we run a DIRQL here.
|
|
//
|
|
|
|
adapter->InterruptPending |= NICInterruptRecognized(adapter, InterruptRecognized);
|
|
if (!(*InterruptRecognized))
|
|
{
|
|
//
|
|
// This is not ours.
|
|
//
|
|
*QueueMiniportHandleInterrupt = FALSE;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// We have to check for a special link change interrupt before acknowledging
|
|
//
|
|
if (adapter->InterruptPending & R_I_RXUNDRUN)
|
|
{
|
|
NdisRawReadPortUlong(adapter->IoBase + R_CSCFG, &csConfig);
|
|
if (csConfig & R_CSCR_LINKCHNG)
|
|
{
|
|
adapter->LinkChange = TRUE;
|
|
NICUpdateLinkStatus(adapter);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Acknowledge the interrupt and mark the events pending service
|
|
//
|
|
NICAcknowledgeInterrupts(adapter);
|
|
*QueueMiniportHandleInterrupt = TRUE;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
MiniportHandleInterrupt (
|
|
IN NDIS_HANDLE MiniportAdapterContext
|
|
)
|
|
{
|
|
PRTL_ADAPTER adapter = (PRTL_ADAPTER)MiniportAdapterContext;
|
|
ULONG txStatus;
|
|
UCHAR command;
|
|
PPACKET_HEADER nicHeader;
|
|
PETH_HEADER ethHeader;
|
|
|
|
NdisDprAcquireSpinLock(&adapter->Lock);
|
|
|
|
NDIS_DbgPrint(MAX_TRACE, ("Interrupts pending: 0x%x\n", adapter->InterruptPending));
|
|
|
|
//
|
|
// Handle a link change
|
|
//
|
|
if (adapter->LinkChange)
|
|
{
|
|
NdisDprReleaseSpinLock(&adapter->Lock);
|
|
NdisMIndicateStatus(adapter->MiniportAdapterHandle,
|
|
adapter->MediaState == NdisMediaStateConnected ?
|
|
NDIS_STATUS_MEDIA_CONNECT : NDIS_STATUS_MEDIA_DISCONNECT,
|
|
NULL,
|
|
0);
|
|
NdisMIndicateStatusComplete(adapter->MiniportAdapterHandle);
|
|
NdisDprAcquireSpinLock(&adapter->Lock);
|
|
adapter->LinkChange = FALSE;
|
|
}
|
|
|
|
//
|
|
// Handle a TX interrupt
|
|
//
|
|
if (adapter->InterruptPending & (R_I_TXOK | R_I_TXERR))
|
|
{
|
|
while (adapter->TxFull || adapter->DirtyTxDesc != adapter->CurrentTxDesc)
|
|
{
|
|
NdisRawReadPortUlong(adapter->IoBase + R_TXSTS0 +
|
|
(adapter->DirtyTxDesc * sizeof(ULONG)), &txStatus);
|
|
|
|
if (!(txStatus & (R_TXS_STATOK | R_TXS_UNDERRUN | R_TXS_ABORTED)))
|
|
{
|
|
//
|
|
// Not sent yet
|
|
//
|
|
break;
|
|
}
|
|
|
|
NDIS_DbgPrint(MAX_TRACE, ("Transmission for desc %d complete: 0x%x\n",
|
|
adapter->DirtyTxDesc, txStatus));
|
|
|
|
if (txStatus & R_TXS_STATOK)
|
|
{
|
|
adapter->TransmitOk++;
|
|
}
|
|
else
|
|
{
|
|
adapter->TransmitError++;
|
|
}
|
|
|
|
adapter->DirtyTxDesc++;
|
|
adapter->DirtyTxDesc %= TX_DESC_COUNT;
|
|
adapter->InterruptPending &= ~(R_I_TXOK | R_I_TXERR);
|
|
adapter->TxFull = FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Handle a good RX interrupt
|
|
//
|
|
if (adapter->InterruptPending & (R_I_RXOK | R_I_RXERR))
|
|
{
|
|
for (;;)
|
|
{
|
|
NdisRawReadPortUchar(adapter->IoBase + R_CMD, &command);
|
|
if (command & R_CMD_RXEMPTY)
|
|
{
|
|
//
|
|
// The buffer is empty
|
|
//
|
|
adapter->InterruptPending &= ~(R_I_RXOK | R_I_RXERR);
|
|
break;
|
|
}
|
|
|
|
adapter->ReceiveOffset %= RECEIVE_BUFFER_SIZE;
|
|
|
|
NDIS_DbgPrint(MAX_TRACE, ("Looking for a packet at offset 0x%x\n",
|
|
adapter->ReceiveOffset));
|
|
nicHeader = (PPACKET_HEADER)(adapter->ReceiveBuffer + adapter->ReceiveOffset);
|
|
if (!(nicHeader->Status & RSR_ROK))
|
|
{
|
|
//
|
|
// Receive failed
|
|
//
|
|
NDIS_DbgPrint(MIN_TRACE, ("Receive failed: 0x%x\n", nicHeader->Status));
|
|
|
|
if (nicHeader->Status & RSR_FAE)
|
|
{
|
|
adapter->ReceiveAlignmentError++;
|
|
}
|
|
else if (nicHeader->Status & RSR_CRC)
|
|
{
|
|
adapter->ReceiveCrcError++;
|
|
}
|
|
adapter->ReceiveError++;
|
|
|
|
goto NextPacket;
|
|
}
|
|
|
|
NDIS_DbgPrint(MAX_TRACE, ("Indicating %d byte packet to NDIS\n",
|
|
nicHeader->PacketLength - RECV_CRC_LENGTH));
|
|
|
|
ethHeader = (PETH_HEADER)(nicHeader + 1);
|
|
NdisMEthIndicateReceive(adapter->MiniportAdapterHandle,
|
|
NULL,
|
|
(PVOID)(ethHeader),
|
|
sizeof(ETH_HEADER),
|
|
(PVOID)(ethHeader + 1),
|
|
nicHeader->PacketLength - sizeof(ETH_HEADER) - RECV_CRC_LENGTH,
|
|
nicHeader->PacketLength - sizeof(ETH_HEADER) - RECV_CRC_LENGTH);
|
|
adapter->ReceiveOk++;
|
|
|
|
NextPacket:
|
|
adapter->ReceiveOffset += nicHeader->PacketLength + sizeof(PACKET_HEADER);
|
|
adapter->ReceiveOffset = (adapter->ReceiveOffset + 3) & ~3;
|
|
NdisRawWritePortUshort(adapter->IoBase + R_CAPR, adapter->ReceiveOffset - 0x10);
|
|
|
|
if (adapter->InterruptPending & (R_I_RXOVRFLW | R_I_FIFOOVR))
|
|
{
|
|
//
|
|
// We can only clear these interrupts once CAPR has been reset
|
|
//
|
|
NdisRawWritePortUshort(adapter->IoBase + R_IS, R_I_RXOVRFLW | R_I_FIFOOVR);
|
|
adapter->InterruptPending &= ~(R_I_RXOVRFLW | R_I_FIFOOVR);
|
|
}
|
|
}
|
|
|
|
NdisMEthIndicateReceiveComplete(adapter->MiniportAdapterHandle);
|
|
}
|
|
|
|
NdisDprReleaseSpinLock(&adapter->Lock);
|
|
}
|