📄 pcnet.c
字号:
/*
* ReactOS AMD PCNet Driver
*
* Copyright (C) 2003 Vizzini <vizzini@plasmic.com>
* Copyright (C) 2004 Filip Navara <navaraf@reactos.com>
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* REVISIONS:
* 09-Sep-2003 vizzini - Created
* 10-Oct-2004 navaraf - Fix receive to work on VMware adapters (
* need to set busmaster bit on PCI).
* - Indicate receive completition.
* - Implement packet transmitting.
* - Don't read slot number from registry and
* report itself as NDIS 5.0 miniport.
* 11-Oct-2004 navaraf - Fix nasty bugs in halt code path.
* 17-Oct-2004 navaraf - Add multicast support.
* - Add media state detection support.
* - Protect the adapter context with spinlock
* and move code talking to card to inside
* NdisMSynchronizeWithInterrupt calls where
* necessary.
*
* NOTES:
* - this assumes a 32-bit machine
*/
#include <ndis.h>
#include "pci.h"
#include "pcnethw.h"
#include "pcnet.h"
#define NDEBUG
#include <debug.h>
NTSTATUS
STDCALL
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath);
static VOID
STDCALL
MiniportHandleInterrupt(
IN NDIS_HANDLE MiniportAdapterContext)
/*
* FUNCTION: Handle an interrupt if told to by MiniportISR
* ARGUMENTS:
* MiniportAdapterContext: context specified to NdisMSetAttributes
* NOTES:
* - Called by NDIS at DISPATCH_LEVEL
*/
{
PADAPTER Adapter = (PADAPTER)MiniportAdapterContext;
USHORT Data;
DPRINT("Called\n");
NdisDprAcquireSpinLock(&Adapter->Lock);
NdisRawWritePortUshort(Adapter->PortOffset + RAP, CSR0);
NdisRawReadPortUshort(Adapter->PortOffset + RDP, &Data);
DPRINT("CSR0 is 0x%x\n", Data);
while(Data & CSR0_INTR)
{
/* Clear interrupt flags early to avoid race conditions. */
NdisRawWritePortUshort(Adapter->PortOffset + RDP, Data);
if(Data & CSR0_ERR)
{
DPRINT("error: %x\n", Data & (CSR0_MERR|CSR0_BABL|CSR0_CERR|CSR0_MISS));
if (Data & CSR0_CERR)
Adapter->Statistics.XmtCollisions++;
}
if(Data & CSR0_IDON)
{
DPRINT("IDON\n");
}
if(Data & CSR0_RINT)
{
DPRINT("receive interrupt\n");
while(1)
{
PRECEIVE_DESCRIPTOR Descriptor = Adapter->ReceiveDescriptorRingVirt + Adapter->CurrentReceiveDescriptorIndex;
PCHAR Buffer;
ULONG ByteCount;
if(Descriptor->FLAGS & RD_OWN)
{
DPRINT("no more receive descriptors to process\n");
break;
}
if(Descriptor->FLAGS & RD_ERR)
{
DPRINT("receive descriptor error: 0x%x\n", Descriptor->FLAGS);
if (Descriptor->FLAGS & RD_BUFF)
Adapter->Statistics.RcvBufferErrors++;
if (Descriptor->FLAGS & RD_CRC)
Adapter->Statistics.RcvCrcErrors++;
if (Descriptor->FLAGS & RD_OFLO)
Adapter->Statistics.RcvOverflowErrors++;
if (Descriptor->FLAGS & RD_FRAM)
Adapter->Statistics.RcvFramingErrors++;
break;
}
if(!((Descriptor->FLAGS & RD_STP) && (Descriptor->FLAGS & RD_ENP)))
{
DPRINT("receive descriptor not start&end: 0x%x\n", Descriptor->FLAGS);
break;
}
Buffer = Adapter->ReceiveBufferPtrVirt + Adapter->CurrentReceiveDescriptorIndex * BUFFER_SIZE;
ByteCount = Descriptor->MCNT & 0xfff;
DPRINT("Indicating a %d-byte packet (index %d)\n", ByteCount, Adapter->CurrentReceiveDescriptorIndex);
NdisMEthIndicateReceive(Adapter->MiniportAdapterHandle, 0, Buffer, 14, Buffer+14, ByteCount-14, ByteCount-14);
NdisMEthIndicateReceiveComplete(Adapter->MiniportAdapterHandle);
RtlZeroMemory(Descriptor, sizeof(RECEIVE_DESCRIPTOR));
Descriptor->RBADR =
(ULONG)(Adapter->ReceiveBufferPtrPhys + Adapter->CurrentReceiveDescriptorIndex * BUFFER_SIZE);
Descriptor->BCNT = (-BUFFER_SIZE) | 0xf000;
Descriptor->FLAGS |= RD_OWN;
Adapter->CurrentReceiveDescriptorIndex++;
Adapter->CurrentReceiveDescriptorIndex %= NUMBER_OF_BUFFERS;
Adapter->Statistics.RcvGoodFrames++;
}
}
if(Data & CSR0_TINT)
{
PTRANSMIT_DESCRIPTOR Descriptor;
DPRINT("transmit interrupt\n");
while (Adapter->CurrentTransmitStartIndex !=
Adapter->CurrentTransmitEndIndex)
{
Descriptor = Adapter->TransmitDescriptorRingVirt + Adapter->CurrentTransmitStartIndex;
DPRINT("buffer %d flags %x flags2 %x\n",
Adapter->CurrentTransmitStartIndex,
Descriptor->FLAGS, Descriptor->FLAGS2);
if (Descriptor->FLAGS & TD1_OWN)
{
DPRINT("non-TXed buffer\n");
break;
}
if (Descriptor->FLAGS & TD1_STP)
{
if (Descriptor->FLAGS & TD1_ONE)
Adapter->Statistics.XmtOneRetry++;
else if (Descriptor->FLAGS & TD1_MORE)
Adapter->Statistics.XmtMoreThanOneRetry++;
}
if (Descriptor->FLAGS & TD1_ERR)
{
DPRINT("major error: %x\n", Descriptor->FLAGS2);
if (Descriptor->FLAGS2 & TD2_RTRY)
Adapter->Statistics.XmtRetryErrors++;
if (Descriptor->FLAGS2 & TD2_LCAR)
Adapter->Statistics.XmtLossesOfCarrier++;
if (Descriptor->FLAGS2 & TD2_LCOL)
Adapter->Statistics.XmtLateCollisions++;
if (Descriptor->FLAGS2 & TD2_EXDEF)
Adapter->Statistics.XmtExcessiveDefferals++;
if (Descriptor->FLAGS2 & TD2_UFLO)
Adapter->Statistics.XmtBufferUnderflows++;
if (Descriptor->FLAGS2 & TD2_BUFF)
Adapter->Statistics.XmtBufferErrors++;
break;
}
Adapter->CurrentTransmitStartIndex++;
Adapter->CurrentTransmitStartIndex %= NUMBER_OF_BUFFERS;
Adapter->Statistics.XmtGoodFrames++;
}
NdisMSendResourcesAvailable(Adapter->MiniportAdapterHandle);
}
if(Data & ~(CSR0_ERR | CSR0_IDON | CSR0_RINT | CSR0_TINT))
{
DPRINT("UNHANDLED INTERRUPT CSR0 0x%x\n", Data);
}
NdisRawReadPortUshort(Adapter->PortOffset + RDP, &Data);
}
/* re-enable interrupts */
NdisRawWritePortUshort(Adapter->PortOffset + RDP, CSR0_IENA);
NdisRawReadPortUshort(Adapter->PortOffset + RDP, &Data);
DPRINT("CSR0 is now 0x%x\n", Data);
NdisDprReleaseSpinLock(&Adapter->Lock);
}
static NDIS_STATUS
MiQueryCard(
IN PADAPTER Adapter)
/*
* FUNCTION: Detect the PCNET NIC in the configured slot and query its I/O address and interrupt vector
* ARGUMENTS:
* MiniportAdapterContext: context supplied to NdisMSetAttributes
* RETURNS:
* NDIS_STATUS_FAILURE on a general error
* NDIS_STATUS_ADAPTER_NOT_FOUND on not finding the adapter
* NDIS_STATUS_SUCCESS on succes
*/
{
ULONG buf32 = 0;
UCHAR buf8 = 0;
NDIS_STATUS Status;
/* Detect the card in the configured slot */
Status = NdisReadPciSlotInformation(Adapter->MiniportAdapterHandle, 0, PCI_PCIID, &buf32, 4);
if(Status != 4)
{
Status = NDIS_STATUS_FAILURE;
DPRINT("NdisReadPciSlotInformation failed\n");
BREAKPOINT;
return Status;
}
if(buf32 != PCI_ID)
{
Status = NDIS_STATUS_ADAPTER_NOT_FOUND;
DPRINT("card in slot isn't our: 0x%x\n", 0, buf32);
BREAKPOINT;
return Status;
}
/* set busmaster and io space enable bits */
buf32 = PCI_BMEN | PCI_IOEN;
NdisWritePciSlotInformation(Adapter->MiniportAdapterHandle, 0, PCI_COMMAND, &buf32, 4);
/* get IO base physical address */
buf32 = 0;
Status = NdisReadPciSlotInformation(Adapter->MiniportAdapterHandle, 0, PCI_IOBAR, &buf32, 4);
if(Status != 4)
{
Status = NDIS_STATUS_FAILURE;
DPRINT("NdisReadPciSlotInformation failed\n");
BREAKPOINT;
return Status;
}
if(!buf32)
{
DPRINT("No base i/o address set\n");
return NDIS_STATUS_FAILURE;
}
buf32 &= ~1; /* even up address - comes out odd for some reason */
DPRINT("detected io address 0x%x\n", buf32);
Adapter->IoBaseAddress = buf32;
/* get interrupt vector */
Status = NdisReadPciSlotInformation(Adapter->MiniportAdapterHandle, 0, PCI_ILR, &buf8, 1);
if(Status != 1)
{
Status = NDIS_STATUS_FAILURE;
DPRINT1("NdisReadPciSlotInformation failed\n");
BREAKPOINT;
return Status;
}
DPRINT("interrupt: 0x%x\n", buf8);
Adapter->InterruptVector = buf8;
return NDIS_STATUS_SUCCESS;
}
static NDIS_STATUS
MiAllocateSharedMemory(
PADAPTER Adapter)
/*
* FUNCTION: Allocate all shared memory used by the miniport
* ARGUMENTS:
* Adapter: Pointer to the miniport's adapter object
* RETURNS:
* NDIS_STATUS_RESOURCES on insufficient memory
* NDIS_STATUS_SUCCESS on success
*/
{
PTRANSMIT_DESCRIPTOR TransmitDescriptor;
PRECEIVE_DESCRIPTOR ReceiveDescriptor;
NDIS_PHYSICAL_ADDRESS PhysicalAddress;
ULONG i;
/* allocate the initialization block */
Adapter->InitializationBlockLength = sizeof(INITIALIZATION_BLOCK);
NdisMAllocateSharedMemory(Adapter->MiniportAdapterHandle, Adapter->InitializationBlockLength,
FALSE, (PVOID *)&Adapter->InitializationBlockVirt, &PhysicalAddress);
if(!Adapter->InitializationBlockVirt)
{
DPRINT1("insufficient resources\n");
BREAKPOINT;
return NDIS_STATUS_RESOURCES;
}
if(((ULONG)Adapter->InitializationBlockVirt & 0x00000003) != 0)
{
DPRINT("address 0x%x not dword-aligned\n", Adapter->InitializationBlockVirt);
BREAKPOINT;
return NDIS_STATUS_RESOURCES;
}
Adapter->InitializationBlockPhys = (PINITIALIZATION_BLOCK)NdisGetPhysicalAddressLow(PhysicalAddress);
/* allocate the transport descriptor ring */
Adapter->TransmitDescriptorRingLength = sizeof(TRANSMIT_DESCRIPTOR) * NUMBER_OF_BUFFERS;
NdisMAllocateSharedMemory(Adapter->MiniportAdapterHandle, Adapter->TransmitDescriptorRingLength,
FALSE, (PVOID *)&Adapter->TransmitDescriptorRingVirt, &PhysicalAddress);
if(!Adapter->TransmitDescriptorRingVirt)
{
DPRINT1("insufficient resources\n");
BREAKPOINT;
return NDIS_STATUS_RESOURCES;
}
if(((ULONG)Adapter->TransmitDescriptorRingVirt & 0x00000003) != 0)
{
DPRINT("address 0x%x not dword-aligned\n", Adapter->TransmitDescriptorRingVirt);
BREAKPOINT;
return NDIS_STATUS_RESOURCES;
}
Adapter->TransmitDescriptorRingPhys = (PTRANSMIT_DESCRIPTOR)NdisGetPhysicalAddressLow(PhysicalAddress);
RtlZeroMemory(Adapter->TransmitDescriptorRingVirt, sizeof(TRANSMIT_DESCRIPTOR) * NUMBER_OF_BUFFERS);
/* allocate the receive descriptor ring */
Adapter->ReceiveDescriptorRingLength = sizeof(RECEIVE_DESCRIPTOR) * NUMBER_OF_BUFFERS;
NdisMAllocateSharedMemory(Adapter->MiniportAdapterHandle, Adapter->ReceiveDescriptorRingLength,
FALSE, (PVOID *)&Adapter->ReceiveDescriptorRingVirt, &PhysicalAddress);
if(!Adapter->ReceiveDescriptorRingVirt)
{
DPRINT1("insufficient resources\n");
BREAKPOINT;
return NDIS_STATUS_RESOURCES;
}
if(((ULONG)Adapter->ReceiveDescriptorRingVirt & 0x00000003) != 0)
{
DPRINT("address 0x%x not dword-aligned\n", Adapter->ReceiveDescriptorRingVirt);
BREAKPOINT;
return NDIS_STATUS_RESOURCES;
}
Adapter->ReceiveDescriptorRingPhys = (PRECEIVE_DESCRIPTOR)NdisGetPhysicalAddressLow(PhysicalAddress);
RtlZeroMemory(Adapter->ReceiveDescriptorRingVirt, sizeof(RECEIVE_DESCRIPTOR) * NUMBER_OF_BUFFERS);
/* allocate transmit buffers */
Adapter->TransmitBufferLength = BUFFER_SIZE * NUMBER_OF_BUFFERS;
NdisMAllocateSharedMemory(Adapter->MiniportAdapterHandle, Adapter->TransmitBufferLength,
FALSE, (PVOID *)&Adapter->TransmitBufferPtrVirt, &PhysicalAddress);
if(!Adapter->TransmitBufferPtrVirt)
{
DPRINT1("insufficient resources\n");
BREAKPOINT;
return NDIS_STATUS_RESOURCES;
}
if(((ULONG)Adapter->TransmitBufferPtrVirt & 0x00000003) != 0)
{
DPRINT("address 0x%x not dword-aligned\n", Adapter->TransmitBufferPtrVirt);
BREAKPOINT;
return NDIS_STATUS_RESOURCES;
}
Adapter->TransmitBufferPtrPhys = (PCHAR)NdisGetPhysicalAddressLow(PhysicalAddress);
RtlZeroMemory(Adapter->TransmitBufferPtrVirt, BUFFER_SIZE * NUMBER_OF_BUFFERS);
/* allocate receive buffers */
Adapter->ReceiveBufferLength = BUFFER_SIZE * NUMBER_OF_BUFFERS;
NdisMAllocateSharedMemory(Adapter->MiniportAdapterHandle, Adapter->ReceiveBufferLength,
FALSE, (PVOID *)&Adapter->ReceiveBufferPtrVirt, &PhysicalAddress);
if(!Adapter->ReceiveBufferPtrVirt)
{
DPRINT1("insufficient resources\n");
BREAKPOINT;
return NDIS_STATUS_RESOURCES;
}
if(((ULONG)Adapter->ReceiveBufferPtrVirt & 0x00000003) != 0)
{
DPRINT("address 0x%x not dword-aligned\n", Adapter->ReceiveBufferPtrVirt);
BREAKPOINT;
return NDIS_STATUS_RESOURCES;
}
Adapter->ReceiveBufferPtrPhys = (PCHAR)NdisGetPhysicalAddressLow(PhysicalAddress);
RtlZeroMemory(Adapter->ReceiveBufferPtrVirt, BUFFER_SIZE * NUMBER_OF_BUFFERS);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -