mirror of
https://github.com/reactos/reactos.git
synced 2024-09-22 10:37:10 +00:00
1421 lines
41 KiB
C
1421 lines
41 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS Novell Eagle 2000 driver
|
|
* FILE: ne2000/8390.c
|
|
* PURPOSE: DP8390 NIC specific routines
|
|
* PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
|
|
* REVISIONS:
|
|
* CSH 27/08-2000 Created
|
|
*/
|
|
|
|
#include <ne2000.h>
|
|
|
|
/* Null-terminated array of ports to probe. This is "semi-risky" (Don Becker). */
|
|
ULONG_PTR ProbeAddressList[] = { 0x280, 0x300, 0x320, 0x340, 0x360, 0x380, 0 };
|
|
|
|
static BOOLEAN ProbeAddressForNIC(
|
|
ULONG_PTR address)
|
|
/*
|
|
* FUNCTION: Probes an address for a NIC
|
|
* ARGUMENTS:
|
|
* address = Base address to probe
|
|
* RETURNS:
|
|
* TRUE if an NIC is found at the address
|
|
* FALSE otherwise
|
|
* NOTES:
|
|
* If the adapter responds correctly to a
|
|
* stop command we assume it is present
|
|
*/
|
|
{
|
|
UCHAR Tmp;
|
|
|
|
NDIS_DbgPrint(MID_TRACE, ("Probing address 0x%x\n", address));
|
|
|
|
/* Disable interrupts */
|
|
NdisRawWritePortUchar(address + PG0_IMR, 0);
|
|
|
|
/* Stop the NIC */
|
|
NdisRawWritePortUchar(address + PG0_CR, CR_STP | CR_RD2);
|
|
|
|
/* Pause for 1.6ms */
|
|
NdisStallExecution(1600);
|
|
|
|
/* Read NIC response */
|
|
NdisRawReadPortUchar(address + PG0_CR, &Tmp);
|
|
|
|
if ((Tmp == (CR_RD2 | CR_STP)) || (Tmp == (CR_RD2 | CR_STP | CR_STA)))
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOLEAN NICCheck(
|
|
PNIC_ADAPTER Adapter)
|
|
/*
|
|
* FUNCTION: Tests for a NIC
|
|
* ARGUMENTS:
|
|
* Adapter = Pointer to adapter information
|
|
* RETURNS:
|
|
* TRUE if NIC is believed to be present, FALSE if not
|
|
*/
|
|
{
|
|
int i;
|
|
|
|
NDIS_DbgPrint(MAX_TRACE, ("Called\n"));
|
|
|
|
/* first try the supplied value */
|
|
if(ProbeAddressForNIC(Adapter->IoBaseAddress))
|
|
{
|
|
NDIS_DbgPrint(MID_TRACE, ("Found adapter at 0x%x\n", Adapter->IoBaseAddress));
|
|
return TRUE;
|
|
}
|
|
|
|
/* ok, no dice, time to probe */
|
|
for(i = 0; ProbeAddressList[i]; i++)
|
|
{
|
|
if(ProbeAddressForNIC(ProbeAddressList[i]))
|
|
{
|
|
NDIS_DbgPrint(MID_TRACE, ("Found adapter at address 0x%x\n", ProbeAddressList[i]));
|
|
Adapter->IoBaseAddress = ProbeAddressList[i];
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
NDIS_DbgPrint(MIN_TRACE,("Adapter NOT found!\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static BOOLEAN NICTestAddress(
|
|
PNIC_ADAPTER Adapter,
|
|
ULONG Address)
|
|
/*
|
|
* FUNCTION: Tests if an address is writable
|
|
* ARGUMENTS:
|
|
* Adapter = Pointer to adapter information
|
|
* RETURNS:
|
|
* TRUE if the address is writable, FALSE if not
|
|
*/
|
|
{
|
|
USHORT Data;
|
|
USHORT Tmp;
|
|
|
|
/* Read one word */
|
|
NICReadDataAlign(Adapter, &Data, Address, 0x02);
|
|
|
|
/* Alter it */
|
|
Data ^= 0xFFFF;
|
|
|
|
/* Write it back */
|
|
NICWriteDataAlign(Adapter, Address, &Data, 0x02);
|
|
|
|
/* Check if it has changed on the NIC */
|
|
NICReadDataAlign(Adapter, &Tmp, Address, 0x02);
|
|
|
|
return (Data == Tmp);
|
|
}
|
|
|
|
|
|
static BOOLEAN NICTestRAM(
|
|
PNIC_ADAPTER Adapter)
|
|
/*
|
|
* FUNCTION: Finds out how much RAM a NIC has
|
|
* ARGUMENTS:
|
|
* Adapter = Pointer to adapter information
|
|
* RETURNS:
|
|
* TRUE if the RAM size was found, FALSE if not
|
|
* NOTES:
|
|
* Start at 1KB and test for every 1KB up to 64KB
|
|
*/
|
|
{
|
|
ULONG_PTR Base;
|
|
|
|
NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
|
|
|
|
/* Locate RAM base address */
|
|
for (Base = 0x0400; Base < 0x10000; Base += 0x0400) {
|
|
if (NICTestAddress(Adapter, Base))
|
|
break;
|
|
}
|
|
|
|
if (Base == 0x10000) {
|
|
/* No RAM on this board */
|
|
NDIS_DbgPrint(MIN_TRACE, ("No RAM found on board.\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
Adapter->RamBase = (PUCHAR)Base;
|
|
|
|
/* Find RAM size */
|
|
for (; Base < 0x10000; Base += 0x0400) {
|
|
if (!NICTestAddress(Adapter, Base))
|
|
break;
|
|
}
|
|
|
|
Adapter->RamSize = (UINT)(Base - (ULONG_PTR)Adapter->RamBase);
|
|
|
|
NDIS_DbgPrint(MID_TRACE, ("RAM is at (0x%X). Size is (0x%X).\n",
|
|
Adapter->RamBase, Adapter->RamSize));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static VOID NICSetPhysicalAddress(
|
|
PNIC_ADAPTER Adapter)
|
|
/*
|
|
* FUNCTION: Initializes the physical address on the NIC
|
|
* ARGUMENTS:
|
|
* Adapter = Pointer to adapter information
|
|
* NOTES:
|
|
* The physical address is taken from Adapter.
|
|
* The NIC is stopped by this operation
|
|
*/
|
|
{
|
|
UINT i;
|
|
|
|
/* Select page 1 */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_CR, CR_STP | CR_RD2 | CR_PAGE1);
|
|
|
|
/* Initialize PAR - Physical Address Registers */
|
|
for (i = 0; i < 0x06; i++)
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG1_PAR + i, Adapter->StationAddress[i]);
|
|
|
|
/* Go back to page 0 */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_CR, CR_STP | CR_RD2 | CR_PAGE0);
|
|
}
|
|
|
|
|
|
static VOID NICSetMulticastAddressMask(
|
|
PNIC_ADAPTER Adapter)
|
|
/*
|
|
* FUNCTION: Initializes the multicast address mask on the NIC
|
|
* ARGUMENTS:
|
|
* Adapter = Pointer to adapter information
|
|
* NOTES:
|
|
* The multicast address mask is taken from Adapter.
|
|
* The NIC is stopped by this operation
|
|
*/
|
|
{
|
|
UINT i;
|
|
|
|
/* Select page 1 */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_CR, CR_STP | CR_RD2 | CR_PAGE1);
|
|
|
|
/* Initialize MAR - Multicast Address Registers */
|
|
for (i = 0; i < 0x08; i++)
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG1_MAR + i, Adapter->MulticastAddressMask[i]);
|
|
|
|
/* Go back to page 0 */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_CR, CR_STP | CR_RD2 | CR_PAGE0);
|
|
}
|
|
|
|
|
|
static BOOLEAN NICReadSAPROM(
|
|
PNIC_ADAPTER Adapter)
|
|
/*
|
|
* FUNCTION: Reads the Station Address PROM data from the NIC
|
|
* ARGUMENTS:
|
|
* Adapter = Pointer to adapter information
|
|
* RETURNS:
|
|
* TRUE if a the NIC is an NE2000
|
|
* NOTES:
|
|
* This routine also determines if the NIC can support word mode transfers
|
|
* and if it does initializes the NIC for word mode.
|
|
* The station address in the adapter structure is initialized with
|
|
* the address from the SAPROM
|
|
*/
|
|
{
|
|
UINT i;
|
|
UCHAR Buffer[32];
|
|
UCHAR WordLength;
|
|
|
|
NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
|
|
|
|
/* Read Station Address PROM (SAPROM) which is 16 bytes at remote DMA address 0.
|
|
Some cards double the data read which we must compensate for */
|
|
|
|
/* Initialize RBCR0 and RBCR1 - Remote Byte Count Registers */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_RBCR0, 0x20);
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_RBCR1, 0x00);
|
|
|
|
/* Initialize RSAR0 and RSAR1 - Remote Start Address Registers */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_RSAR0, 0x00);
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_RSAR1, 0x00);
|
|
|
|
/* Select page 0, read and start the NIC */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_CR, CR_STA | CR_RD0 | CR_PAGE0);
|
|
|
|
/* Read one byte at a time */
|
|
WordLength = 2; /* Assume a word is two bytes */
|
|
for (i = 0; i < 32; i += 2) {
|
|
NdisRawReadPortUchar(Adapter->IOBase + NIC_DATA, &Buffer[i]);
|
|
NdisRawReadPortUchar(Adapter->IOBase + NIC_DATA, &Buffer[i + 1]);
|
|
if (Buffer[i] != Buffer[i + 1])
|
|
WordLength = 1; /* A word is one byte long */
|
|
}
|
|
|
|
/* If WordLength is 2 the data read before was doubled. We must compensate for this */
|
|
if (WordLength == 2) {
|
|
NDIS_DbgPrint(MAX_TRACE,("NE2000 or compatible network adapter found.\n"));
|
|
|
|
Adapter->WordMode = TRUE;
|
|
|
|
/* Move the SAPROM data to the adapter object */
|
|
for (i = 0; i < 16; i++)
|
|
Adapter->SAPROM[i] = Buffer[i * 2];
|
|
|
|
/* Copy the permanent address */
|
|
NdisMoveMemory(
|
|
(PVOID)&Adapter->PermanentAddress,
|
|
(PVOID)&Adapter->SAPROM,
|
|
DRIVER_LENGTH_OF_ADDRESS);
|
|
|
|
/* Initialize DCR - Data Configuration Register (word mode/4 words FIFO) */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_DCR, DCR_WTS | DCR_LS | DCR_FT10);
|
|
|
|
return TRUE;
|
|
} else {
|
|
NDIS_DbgPrint(MAX_TRACE, ("NE1000 or compatible network adapter found.\n"));
|
|
|
|
Adapter->WordMode = FALSE;
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
NDIS_STATUS NICInitialize(
|
|
PNIC_ADAPTER Adapter)
|
|
/*
|
|
* FUNCTION: Initializes a NIC
|
|
* ARGUMENTS:
|
|
* Adapter = Pointer to adapter information
|
|
* RETURNS:
|
|
* Status of NIC initialization
|
|
* NOTES:
|
|
* The NIC is put into loopback mode
|
|
*/
|
|
{
|
|
UCHAR Tmp;
|
|
|
|
NDIS_DbgPrint(MID_TRACE, ("Called.\n"));
|
|
|
|
/* Reset the NIC */
|
|
NdisRawReadPortUchar(Adapter->IOBase + NIC_RESET, &Tmp);
|
|
|
|
/* Wait for 1.6ms */
|
|
NdisStallExecution(1600);
|
|
|
|
/* Write the value back */
|
|
NdisRawWritePortUchar(Adapter->IOBase + NIC_RESET, Tmp);
|
|
|
|
/* Select page 0 and stop NIC */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_CR, CR_STP | CR_RD2 | CR_PAGE0);
|
|
|
|
/* Initialize DCR - Data Configuration Register (byte mode/8 bytes FIFO) */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_DCR, DCR_LS | DCR_FT10);
|
|
|
|
/* Clear RBCR0 and RBCR1 - Remote Byte Count Registers */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_RBCR0, 0x00);
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_RBCR1, 0x00);
|
|
|
|
/* Initialize RCR - Receive Configuration Register (monitor mode) */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_RCR, RCR_MON);
|
|
|
|
/* Enter loopback mode (internal NIC module loopback) */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_TCR, TCR_LOOP);
|
|
|
|
/* Read the Station Address PROM */
|
|
if (!NICReadSAPROM(Adapter))
|
|
return NDIS_STATUS_ADAPTER_NOT_FOUND;
|
|
|
|
NDIS_DbgPrint(MID_TRACE, ("Station address is (%02X %02X %02X %02X %02X %02X).\n",
|
|
Adapter->StationAddress[0], Adapter->StationAddress[1],
|
|
Adapter->StationAddress[2], Adapter->StationAddress[3],
|
|
Adapter->StationAddress[4], Adapter->StationAddress[5]));
|
|
|
|
/* Select page 0 and start NIC */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_CR, CR_STA | CR_RD2 | CR_PAGE0);
|
|
|
|
/* Clear ISR - Interrupt Status Register */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_ISR, 0xFF);
|
|
|
|
/* Find NIC RAM size */
|
|
NICTestRAM(Adapter);
|
|
|
|
return NDIS_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NDIS_STATUS NICSetup(
|
|
PNIC_ADAPTER Adapter)
|
|
/*
|
|
* FUNCTION: Sets up a NIC
|
|
* ARGUMENTS:
|
|
* Adapter = Pointer to adapter information
|
|
* RETURNS:
|
|
* Status of operation
|
|
* NOTES:
|
|
* The NIC is put into loopback mode
|
|
*/
|
|
{
|
|
NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
|
|
|
|
if (Adapter->WordMode ) {
|
|
/* Initialize DCR - Data Configuration Register (word mode/4 words FIFO) */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_DCR, DCR_WTS | DCR_LS | DCR_FT10);
|
|
} else {
|
|
/* Initialize DCR - Data Configuration Register (byte mode/8 bytes FIFO) */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_DCR, DCR_LS | DCR_FT10);
|
|
}
|
|
|
|
/* Clear RBCR0 and RBCR1 - Remote Byte Count Registers */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_RBCR0, 0x00);
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_RBCR1, 0x00);
|
|
|
|
/* Initialize RCR - Receive Configuration Register (monitor mode) */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_RCR, RCR_MON);
|
|
|
|
/* Enter loopback mode (internal NIC module loopback) */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_TCR, TCR_LOOP);
|
|
|
|
/* Set boundary page */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_BNRY, Adapter->NextPacket);
|
|
|
|
/* Set start page */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_PSTART, Adapter->PageStart);
|
|
|
|
/* Set stop page */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_PSTOP, Adapter->PageStop);
|
|
|
|
/* Program our address on the NIC */
|
|
NICSetPhysicalAddress(Adapter);
|
|
|
|
/* Program the multicast address mask on the NIC */
|
|
NICSetMulticastAddressMask(Adapter);
|
|
|
|
/* Select page 1 and stop NIC */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_CR, CR_STP | CR_RD2 | CR_PAGE1);
|
|
|
|
/* Initialize current page register */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG1_CURR, Adapter->PageStart + 1);
|
|
|
|
/* Select page 0 and stop NIC */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_CR, CR_STP | CR_RD2 | CR_PAGE0);
|
|
|
|
/* Clear ISR - Interrupt Status Register */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_ISR, 0xFF);
|
|
|
|
/* Initialize IMR - Interrupt Mask Register */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_IMR, Adapter->InterruptMask);
|
|
|
|
/* Select page 0 and start NIC */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_CR, CR_STA | CR_RD2 | CR_PAGE0);
|
|
|
|
Adapter->CurrentPage = Adapter->PageStart + 1;
|
|
Adapter->NextPacket = Adapter->PageStart + 1;
|
|
Adapter->BufferOverflow = FALSE;
|
|
Adapter->ReceiveError = FALSE;
|
|
Adapter->TransmitError = FALSE;
|
|
|
|
NDIS_DbgPrint(MAX_TRACE, ("Leaving.\n"));
|
|
|
|
return NDIS_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NDIS_STATUS NICStart(
|
|
PNIC_ADAPTER Adapter)
|
|
/*
|
|
* FUNCTION: Starts a NIC
|
|
* ARGUMENTS:
|
|
* Adapter = Pointer to adapter information
|
|
* RETURNS:
|
|
* Status of operation
|
|
*/
|
|
{
|
|
NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
|
|
|
|
/* Take NIC out of loopback mode */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_TCR, 0x00);
|
|
|
|
/* Initialize RCR - Receive Configuration Register (accept all) */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_RCR, RCR_AB | RCR_AM | RCR_PRO);
|
|
|
|
return NDIS_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NDIS_STATUS NICStop(
|
|
PNIC_ADAPTER Adapter)
|
|
/*
|
|
* FUNCTION: Stops a NIC
|
|
* ARGUMENTS:
|
|
* Adapter = Pointer to adapter information
|
|
* RETURNS:
|
|
* Status of operation
|
|
*/
|
|
{
|
|
UCHAR Tmp;
|
|
UINT i;
|
|
|
|
NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
|
|
|
|
/* Select page 0 and stop NIC */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_CR, CR_STP | CR_RD2 | CR_PAGE0);
|
|
|
|
/* Clear Remote Byte Count Register so ISR_RST will be set */
|
|
NdisRawWritePortUchar( Adapter->IOBase + PG0_RBCR0, 0x00);
|
|
NdisRawWritePortUchar( Adapter->IOBase + PG0_RBCR0, 0x00);
|
|
|
|
/* Wait for ISR_RST to be set, but timeout after 2ms */
|
|
for (i = 0; i < 4; i++) {
|
|
NdisRawReadPortUchar(Adapter->IOBase + PG0_ISR, &Tmp);
|
|
if (Tmp & ISR_RST)
|
|
break;
|
|
|
|
NdisStallExecution(500);
|
|
}
|
|
|
|
#if DBG
|
|
if (i == 4)
|
|
NDIS_DbgPrint(MIN_TRACE, ("NIC was not reset after 2ms.\n"));
|
|
#endif
|
|
|
|
/* Initialize RCR - Receive Configuration Register (monitor mode) */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_RCR, RCR_MON);
|
|
|
|
/* Initialize TCR - Transmit Configuration Register (loopback mode) */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_TCR, TCR_LOOP);
|
|
|
|
/* Start NIC */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_CR, CR_STA | CR_RD2);
|
|
|
|
return NDIS_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NDIS_STATUS NICReset(
|
|
PNIC_ADAPTER Adapter)
|
|
/*
|
|
* FUNCTION: Resets a NIC
|
|
* ARGUMENTS:
|
|
* Adapter = Pointer to adapter information
|
|
* RETURNS:
|
|
* Status of operation
|
|
*/
|
|
{
|
|
UCHAR Tmp;
|
|
|
|
NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
|
|
|
|
/* Stop the NIC */
|
|
NICStop(Adapter);
|
|
|
|
/* Reset the NIC */
|
|
NdisRawReadPortUchar(Adapter->IOBase + NIC_RESET, &Tmp);
|
|
|
|
/* Wait for 1.6ms */
|
|
NdisStallExecution(1600);
|
|
|
|
/* Write the value back */
|
|
NdisRawWritePortUchar(Adapter->IOBase + NIC_RESET, Tmp);
|
|
|
|
/* Restart the NIC */
|
|
NICStart(Adapter);
|
|
|
|
return NDIS_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
static VOID NICStartTransmit(
|
|
PNIC_ADAPTER Adapter)
|
|
/*
|
|
* FUNCTION: Starts transmitting a packet
|
|
* ARGUMENTS:
|
|
* Adapter = Pointer to adapter information
|
|
*/
|
|
{
|
|
UINT Length;
|
|
UCHAR FrameStart;
|
|
UCHAR Tmp;
|
|
|
|
NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
|
|
|
|
if (Adapter->TXCurrent < 0) return;
|
|
|
|
//FrameStart = Adapter->TXStart + Adapter->TXCurrent * DRIVER_BLOCK_SIZE;
|
|
//FrameStart = Adapter->TXStart;
|
|
FrameStart = (UCHAR)(Adapter->TXStart + (UCHAR)(Adapter->TXCurrent * BUFFERS_PER_TX_BUF));
|
|
|
|
/* Set start of frame */
|
|
NdisRawReadPortUchar(Adapter->IOBase + PG0_TPSR, &Tmp);
|
|
// NdisRawWritePortUchar(Adapter->IOBase + PG0_TPSR,
|
|
// Adapter->TXStart + Adapter->TXCurrent * DRIVER_BLOCK_SIZE);
|
|
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_TPSR, FrameStart);
|
|
//NDIS_DbgPrint(MID_TRACE, ("Setting start of frame to (%d).\n", FrameStart));
|
|
|
|
/* Set length of frame */
|
|
Length = Adapter->TXSize[Adapter->TXCurrent];
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_TBCR0, Length & 0xFF);
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_TBCR1, Length >> 8);
|
|
|
|
/* Start transmitting */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_CR, CR_STA | CR_TXP | CR_RD2);
|
|
|
|
NDIS_DbgPrint(MID_TRACE, ("Transmitting. FrameStart (%d) TXCurrent (%d) TXStart (%d) Length (%d).\n\n",
|
|
FrameStart,
|
|
Adapter->TXCurrent,
|
|
Adapter->TXStart,
|
|
Length));
|
|
|
|
}
|
|
|
|
|
|
static VOID NICSetBoundaryPage(
|
|
PNIC_ADAPTER Adapter)
|
|
/*
|
|
* FUNCTION: Sets the boundary page on the adapter to be one less than NextPacket
|
|
* ARGUMENTS:
|
|
* Adapter = Pointer to adapter information
|
|
*/
|
|
{
|
|
if (Adapter->NextPacket == Adapter->PageStart) {
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_BNRY,
|
|
(UCHAR)(Adapter->PageStop - 1));
|
|
} else {
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_BNRY,
|
|
(UCHAR)(Adapter->NextPacket - 1));
|
|
}
|
|
}
|
|
|
|
|
|
static VOID NICGetCurrentPage(
|
|
PNIC_ADAPTER Adapter)
|
|
/*
|
|
* FUNCTION: Retrieves the current page from the adapter
|
|
* ARGUMENTS:
|
|
* Adapter = Pointer to adapter information
|
|
*/
|
|
{
|
|
UCHAR Current;
|
|
|
|
/* Select page 1 */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_CR, CR_STA | CR_RD2 | CR_PAGE1);
|
|
|
|
/* Read current page */
|
|
NdisRawReadPortUchar(Adapter->IOBase + PG1_CURR, &Current);
|
|
|
|
/* Select page 0 */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_CR, CR_STA | CR_RD2 | CR_PAGE0);
|
|
|
|
Adapter->CurrentPage = Current;
|
|
}
|
|
|
|
|
|
VOID NICUpdateCounters(
|
|
PNIC_ADAPTER Adapter)
|
|
/*
|
|
* FUNCTION: Updates counters
|
|
* ARGUMENTS:
|
|
* Adapter = Pointer to adapter information
|
|
*/
|
|
{
|
|
UCHAR Tmp;
|
|
|
|
NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
|
|
|
|
NdisRawReadPortUchar(Adapter->IOBase + PG0_CNTR0, &Tmp);
|
|
Adapter->FrameAlignmentErrors += Tmp;
|
|
|
|
NdisRawReadPortUchar(Adapter->IOBase + PG0_CNTR1, &Tmp);
|
|
Adapter->CrcErrors += Tmp;
|
|
|
|
NdisRawReadPortUchar(Adapter->IOBase + PG0_CNTR2, &Tmp);
|
|
Adapter->MissedPackets += Tmp;
|
|
}
|
|
|
|
|
|
VOID NICReadDataAlign(
|
|
PNIC_ADAPTER Adapter,
|
|
PUSHORT Target,
|
|
ULONG_PTR Source,
|
|
USHORT Length)
|
|
/*
|
|
* FUNCTION: Copies data from a NIC's RAM into a buffer
|
|
* ARGUMENTS:
|
|
* Adapter = Pointer to adapter information
|
|
* Target = Pointer to buffer to copy data into (in host memory)
|
|
* Source = Offset into NIC's RAM (must be an even number)
|
|
* Length = Number of bytes to copy from NIC's RAM (must be an even number)
|
|
*/
|
|
{
|
|
UCHAR Tmp;
|
|
USHORT Count;
|
|
|
|
Count = Length;
|
|
|
|
/* Select page 0 and start the NIC */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_CR, CR_STA | CR_RD2 | CR_PAGE0);
|
|
|
|
/* Initialize RSAR0 and RSAR1 - Remote Start Address Registers */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_RSAR0, (UCHAR)(Source & 0xFF));
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_RSAR1, (UCHAR)(Source >> 8));
|
|
|
|
/* Initialize RBCR0 and RBCR1 - Remote Byte Count Registers */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_RBCR0, (UCHAR)(Count & 0xFF));
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_RBCR1, (UCHAR)(Count >> 8));
|
|
|
|
/* Select page 0, read and start the NIC */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_CR, CR_STA | CR_RD0 | CR_PAGE0);
|
|
|
|
if (Adapter->WordMode)
|
|
NdisRawReadPortBufferUshort(Adapter->IOBase + NIC_DATA, Target, Count >> 1);
|
|
else
|
|
NdisRawReadPortBufferUchar(Adapter->IOBase + NIC_DATA, Target, Count);
|
|
|
|
/* Wait for remote DMA to complete, but timeout after some time */
|
|
for (Count = 0; Count < 0xFFFF; Count++) {
|
|
NdisRawReadPortUchar(Adapter->IOBase + PG0_ISR, &Tmp);
|
|
if (Tmp & ISR_RDC)
|
|
break;
|
|
|
|
NdisStallExecution(4);
|
|
}
|
|
|
|
#if DBG
|
|
if (Count == 0xFFFF)
|
|
NDIS_DbgPrint(MIN_TRACE, ("Remote DMA did not complete.\n"));
|
|
#endif
|
|
|
|
/* Clear remote DMA bit in ISR - Interrupt Status Register */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_ISR, ISR_RDC);
|
|
}
|
|
|
|
|
|
VOID NICWriteDataAlign(
|
|
PNIC_ADAPTER Adapter,
|
|
ULONG_PTR Target,
|
|
PUSHORT Source,
|
|
USHORT Length)
|
|
/*
|
|
* FUNCTION: Copies data from a buffer into the NIC's RAM
|
|
* ARGUMENTS:
|
|
* Adapter = Pointer to adapter information
|
|
* Target = Offset into NIC's RAM (must be an even number)
|
|
* Source = Pointer to buffer to copy data from (in host memory)
|
|
* Length = Number of bytes to copy from the buffer (must be an even number)
|
|
*/
|
|
{
|
|
UCHAR Tmp;
|
|
USHORT Count;
|
|
|
|
/* Select page 0 and start the NIC */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_CR, CR_STA | CR_RD2 | CR_PAGE0);
|
|
|
|
/* Handle read-before-write bug */
|
|
|
|
/* Initialize RSAR0 and RSAR1 - Remote Start Address Registers */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_RSAR0, (UCHAR)(Target & 0xFF));
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_RSAR1, (UCHAR)(Target >> 8));
|
|
|
|
/* Initialize RBCR0 and RBCR1 - Remote Byte Count Registers */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_RBCR0, 0x02);
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_RBCR1, 0x00);
|
|
|
|
/* Read and start the NIC */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_CR, CR_STA | CR_RD0 | CR_PAGE0);
|
|
|
|
/* Read data */
|
|
NdisRawReadPortUshort(Adapter->IOBase + NIC_DATA, &Count);
|
|
|
|
/* Wait for remote DMA to complete, but timeout after some time */
|
|
for (Count = 0; Count < 0xFFFF; Count++) {
|
|
NdisRawReadPortUchar(Adapter->IOBase + PG0_ISR, &Tmp);
|
|
if (Tmp & ISR_RDC)
|
|
break;
|
|
|
|
NdisStallExecution(4);
|
|
}
|
|
|
|
#if DBG
|
|
if (Count == 0xFFFF)
|
|
NDIS_DbgPrint(MIN_TRACE, ("Remote DMA did not complete.\n"));
|
|
#endif
|
|
|
|
/* Clear remote DMA bit in ISR - Interrupt Status Register */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_ISR, ISR_RDC);
|
|
|
|
|
|
/* Now output some data */
|
|
Count = Length;
|
|
|
|
/* Initialize RSAR0 and RSAR1 - Remote Start Address Registers */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_RSAR0, (UCHAR)(Target & 0xFF));
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_RSAR1, (UCHAR)(Target >> 8));
|
|
|
|
/* Initialize RBCR0 and RBCR1 - Remote Byte Count Registers */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_RBCR0, (UCHAR)(Count & 0xFF));
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_RBCR1, (UCHAR)(Count >> 8));
|
|
|
|
/* Write and start the NIC */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_CR, CR_STA | CR_RD1 | CR_PAGE0);
|
|
|
|
if (Adapter->WordMode)
|
|
NdisRawWritePortBufferUshort(Adapter->IOBase + NIC_DATA, Source, Count >> 1);
|
|
else
|
|
NdisRawWritePortBufferUchar(Adapter->IOBase + NIC_DATA, Source, Count);
|
|
|
|
/* Wait for remote DMA to complete, but timeout after some time */
|
|
for (Count = 0; Count < 0xFFFF; Count++) {
|
|
NdisRawReadPortUchar(Adapter->IOBase + PG0_ISR, &Tmp);
|
|
if (Tmp & ISR_RDC)
|
|
break;
|
|
|
|
NdisStallExecution(4);
|
|
}
|
|
|
|
#if DBG
|
|
if (Count == 0xFFFF)
|
|
NDIS_DbgPrint(MIN_TRACE, ("Remote DMA did not complete.\n"));
|
|
#endif
|
|
|
|
/* Clear remote DMA bit in ISR - Interrupt Status Register */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_ISR, ISR_RDC);
|
|
}
|
|
|
|
|
|
VOID NICReadData(
|
|
PNIC_ADAPTER Adapter,
|
|
PUCHAR Target,
|
|
ULONG_PTR Source,
|
|
USHORT Length)
|
|
/*
|
|
* FUNCTION: Copies data from a NIC's RAM into a buffer
|
|
* ARGUMENTS:
|
|
* Adapter = Pointer to adapter information
|
|
* Target = Pointer to buffer to copy data into (in host memory)
|
|
* Source = Offset into NIC's RAM
|
|
* Length = Number of bytes to copy from NIC's RAM
|
|
*/
|
|
{
|
|
USHORT Tmp;
|
|
|
|
/* Avoid transfers to odd addresses */
|
|
if (Source & 0x01) {
|
|
/* Transfer one word and use the MSB */
|
|
NICReadDataAlign(Adapter, &Tmp, Source - 1, 0x02);
|
|
*Target = (UCHAR)(Tmp >> 8);
|
|
Source++;
|
|
Target++;
|
|
Length--;
|
|
}
|
|
|
|
if (Length & 0x01) {
|
|
/* Transfer as many words as we can without exceeding the buffer length */
|
|
Tmp = Length & 0xFFFE;
|
|
NICReadDataAlign(Adapter, (PUSHORT)Target, Source, Tmp);
|
|
Source += Tmp;
|
|
Target = (PUCHAR)((ULONG_PTR) Target + Tmp);
|
|
|
|
/* Read one word and keep the LSB */
|
|
NICReadDataAlign(Adapter, &Tmp, Source, 0x02);
|
|
*Target = (UCHAR)(Tmp & 0x00FF);
|
|
} else
|
|
/* Transfer the rest of the data */
|
|
NICReadDataAlign(Adapter, (PUSHORT)Target, Source, Length);
|
|
}
|
|
|
|
|
|
VOID NICWriteData(
|
|
PNIC_ADAPTER Adapter,
|
|
ULONG_PTR Target,
|
|
PUCHAR Source,
|
|
USHORT Length)
|
|
/*
|
|
* FUNCTION: Copies data from a buffer into NIC's RAM
|
|
* ARGUMENTS:
|
|
* Adapter = Pointer to adapter information
|
|
* Target = Offset into NIC's RAM to store data
|
|
* Source = Pointer to buffer to copy data from (in host memory)
|
|
* Length = Number of bytes to copy from buffer
|
|
*/
|
|
{
|
|
USHORT Tmp;
|
|
|
|
/* Avoid transfers to odd addresses */
|
|
if (Target & 0x01) {
|
|
/* Read one word */
|
|
NICReadDataAlign(Adapter, &Tmp, Target - 1, 0x02);
|
|
|
|
/* Merge LSB with the new byte which become the new MSB */
|
|
Tmp = (Tmp & 0x00FF) | (*Source << 8);
|
|
|
|
/* Finally write the value back */
|
|
NICWriteDataAlign(Adapter, Target - 1, &Tmp, 0x02);
|
|
|
|
/* Update pointers */
|
|
Source = (PUCHAR) ((ULONG_PTR) Source + 1);
|
|
Target += 1;
|
|
Length--;
|
|
}
|
|
|
|
if (Length & 0x01) {
|
|
/* Transfer as many words as we can without exceeding the transfer length */
|
|
Tmp = Length & 0xFFFE;
|
|
NICWriteDataAlign(Adapter, Target, (PUSHORT)Source, Tmp);
|
|
Source += Tmp;
|
|
Target += Tmp;
|
|
|
|
/* Read one word */
|
|
NICReadDataAlign(Adapter, &Tmp, Target, 0x02);
|
|
|
|
/* Merge MSB with the new byte which become the new LSB */
|
|
Tmp = (Tmp & 0xFF00) | (*Source);
|
|
|
|
/* Finally write the value back */
|
|
NICWriteDataAlign(Adapter, Target, &Tmp, 0x02);
|
|
} else
|
|
/* Transfer the rest of the data */
|
|
NICWriteDataAlign(Adapter, Target, (PUSHORT)Source, Length);
|
|
}
|
|
|
|
|
|
static VOID NICIndicatePacket(
|
|
PNIC_ADAPTER Adapter)
|
|
/*
|
|
* FUNCTION: Indicates a packet to the wrapper
|
|
* ARGUMENTS:
|
|
* Adapter = Pointer to adapter information
|
|
*/
|
|
{
|
|
UINT IndicateLength;
|
|
|
|
IndicateLength = (Adapter->PacketHeader.PacketLength <
|
|
(Adapter->LookaheadSize + DRIVER_HEADER_SIZE))?
|
|
(Adapter->PacketHeader.PacketLength) :
|
|
(Adapter->LookaheadSize + DRIVER_HEADER_SIZE);
|
|
|
|
/* Fill the lookahead buffer */
|
|
NICReadData(Adapter,
|
|
(PUCHAR)&Adapter->Lookahead,
|
|
Adapter->PacketOffset + sizeof(PACKET_HEADER),
|
|
IndicateLength + DRIVER_HEADER_SIZE);
|
|
|
|
NDIS_DbgPrint(MID_TRACE, ("Indicating (%d) bytes.\n", IndicateLength));
|
|
NDIS_DbgPrint(MID_TRACE, ("ne2000!NICIndicatePacket: Indicating (%d) bytes.\n", IndicateLength));
|
|
|
|
#if 0
|
|
NDIS_DbgPrint(MAX_TRACE, ("FRAME:\n"));
|
|
for (i = 0; i < (IndicateLength + 7) / 8; i++) {
|
|
NDIS_DbgPrint(MAX_TRACE, ("%02X %02X %02X %02X %02X %02X %02X %02X\n",
|
|
Adapter->Lookahead[i*8+0],
|
|
Adapter->Lookahead[i*8+1],
|
|
Adapter->Lookahead[i*8+2],
|
|
Adapter->Lookahead[i*8+3],
|
|
Adapter->Lookahead[i*8+4],
|
|
Adapter->Lookahead[i*8+5],
|
|
Adapter->Lookahead[i*8+6],
|
|
Adapter->Lookahead[i*8+7]));
|
|
}
|
|
#endif
|
|
|
|
if (IndicateLength >= DRIVER_HEADER_SIZE) {
|
|
NDIS_DbgPrint(MAX_TRACE,("Adapter->MiniportAdapterHandle: %x\n",
|
|
Adapter->MiniportAdapterHandle));
|
|
NdisMEthIndicateReceive(Adapter->MiniportAdapterHandle,
|
|
NULL,
|
|
(PVOID)&Adapter->Lookahead,
|
|
DRIVER_HEADER_SIZE,
|
|
(PVOID)&Adapter->Lookahead[DRIVER_HEADER_SIZE],
|
|
IndicateLength - DRIVER_HEADER_SIZE,
|
|
Adapter->PacketHeader.PacketLength - DRIVER_HEADER_SIZE);
|
|
} else {
|
|
NdisMEthIndicateReceive(Adapter->MiniportAdapterHandle,
|
|
NULL,
|
|
(PVOID)&Adapter->Lookahead,
|
|
IndicateLength,
|
|
NULL,
|
|
0,
|
|
0);
|
|
}
|
|
}
|
|
|
|
|
|
static VOID NICReadPacket(
|
|
PNIC_ADAPTER Adapter)
|
|
/*
|
|
* FUNCTION: Reads a full packet from the receive buffer ring
|
|
* ARGUMENTS:
|
|
* Adapter = Pointer to adapter information
|
|
*/
|
|
{
|
|
BOOLEAN SkipPacket = FALSE;
|
|
|
|
NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
|
|
|
|
/* Get the header of the next packet in the receive ring */
|
|
Adapter->PacketOffset = Adapter->NextPacket << 8;
|
|
NICReadData(Adapter,
|
|
(PUCHAR)&Adapter->PacketHeader,
|
|
Adapter->PacketOffset,
|
|
sizeof(PACKET_HEADER));
|
|
|
|
NDIS_DbgPrint(MAX_TRACE, ("HEADER: (Status) (0x%X)\n", Adapter->PacketHeader.Status));
|
|
NDIS_DbgPrint(MAX_TRACE, ("HEADER: (NextPacket) (0x%X)\n", Adapter->PacketHeader.NextPacket));
|
|
NDIS_DbgPrint(MAX_TRACE, ("HEADER: (PacketLength) (0x%X)\n", Adapter->PacketHeader.PacketLength));
|
|
|
|
if (Adapter->PacketHeader.PacketLength < 64 ||
|
|
Adapter->PacketHeader.PacketLength > 1518) { /* XXX I don't think the CRC will show up... should be 1514 */
|
|
NDIS_DbgPrint(MAX_TRACE, ("Bogus packet size (%d).\n",
|
|
Adapter->PacketHeader.PacketLength));
|
|
SkipPacket = TRUE;
|
|
}
|
|
|
|
if (SkipPacket) {
|
|
/* Skip packet */
|
|
Adapter->NextPacket = Adapter->CurrentPage;
|
|
} else {
|
|
NDIS_DbgPrint(MAX_TRACE,("Adapter->MiniportAdapterHandle: %x\n",
|
|
Adapter->MiniportAdapterHandle));
|
|
NICIndicatePacket(Adapter);
|
|
|
|
/* Go to the next free buffer in receive ring */
|
|
Adapter->NextPacket = Adapter->PacketHeader.NextPacket;
|
|
}
|
|
|
|
/* Update boundary page */
|
|
NICSetBoundaryPage(Adapter);
|
|
}
|
|
|
|
|
|
static VOID NICWritePacket(
|
|
PNIC_ADAPTER Adapter)
|
|
/*
|
|
* FUNCTION: Writes a full packet to the transmit buffer ring
|
|
* ARGUMENTS:
|
|
* Adapter = Pointer to adapter information
|
|
* NOTES:
|
|
* There must be enough free buffers available in the transmit buffer ring.
|
|
* The packet is taken from the head of the transmit queue and the position
|
|
* into the transmit buffer ring is taken from TXNext
|
|
*/
|
|
{
|
|
PNDIS_BUFFER SrcBuffer;
|
|
UINT BytesToCopy, SrcSize, DstSize;
|
|
PUCHAR SrcData;
|
|
ULONG DstData;
|
|
UINT TXStart;
|
|
UINT TXStop;
|
|
|
|
NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
|
|
|
|
TXStart = Adapter->TXStart * DRIVER_BLOCK_SIZE;
|
|
TXStop = (Adapter->TXStart + Adapter->TXCount) * DRIVER_BLOCK_SIZE;
|
|
|
|
NdisQueryPacket(Adapter->TXQueueHead,
|
|
NULL,
|
|
NULL,
|
|
&SrcBuffer,
|
|
&Adapter->TXSize[Adapter->TXNext]);
|
|
|
|
NDIS_DbgPrint(MID_TRACE, ("Packet (%d) is now size (%d).\n",
|
|
Adapter->TXNext,
|
|
Adapter->TXSize[Adapter->TXNext]));
|
|
|
|
NdisQueryBuffer(SrcBuffer, (PVOID)&SrcData, &SrcSize);
|
|
|
|
DstData = TXStart + Adapter->TXNext * DRIVER_BLOCK_SIZE;
|
|
DstSize = TXStop - DstData;
|
|
|
|
/* Start copying the data */
|
|
for (;;) {
|
|
BytesToCopy = (SrcSize < DstSize)? SrcSize : DstSize;
|
|
|
|
NICWriteData(Adapter, DstData, SrcData, BytesToCopy);
|
|
|
|
SrcData = (PUCHAR)((ULONG_PTR) SrcData + BytesToCopy);
|
|
SrcSize -= BytesToCopy;
|
|
DstData += BytesToCopy;
|
|
DstSize -= BytesToCopy;
|
|
|
|
if (SrcSize == 0) {
|
|
/* No more bytes in source buffer. Proceed to
|
|
the next buffer in the source buffer chain */
|
|
NdisGetNextBuffer(SrcBuffer, &SrcBuffer);
|
|
if (!SrcBuffer)
|
|
break;
|
|
|
|
NdisQueryBuffer(SrcBuffer, (PVOID)&SrcData, &SrcSize);
|
|
}
|
|
|
|
if (DstSize == 0) {
|
|
/* Wrap around the end of the transmit buffer ring */
|
|
DstData = TXStart;
|
|
DstSize = Adapter->TXCount * DRIVER_BLOCK_SIZE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static BOOLEAN NICPrepareForTransmit(
|
|
PNIC_ADAPTER Adapter)
|
|
/*
|
|
* FUNCTION: Prepares a packet for transmission
|
|
* ARGUMENTS:
|
|
* Adapter = Pointer to adapter information
|
|
* NOTES:
|
|
* There must be at least one packet in the transmit queue
|
|
* RETURNS:
|
|
* TRUE if a packet was prepared, FALSE if not
|
|
*/
|
|
{
|
|
UINT Length;
|
|
UINT BufferCount;
|
|
PNDIS_PACKET Packet;
|
|
|
|
NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
|
|
|
|
/* Calculate number of buffers needed to transmit packet */
|
|
NdisQueryPacket(Adapter->TXQueueHead,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&Length);
|
|
|
|
BufferCount = (Length + DRIVER_BLOCK_SIZE - 1) / DRIVER_BLOCK_SIZE;
|
|
|
|
if (BufferCount > Adapter->TXFree) {
|
|
NDIS_DbgPrint(MID_TRACE, ("No transmit resources. Have (%d) buffers, need (%d).\n",
|
|
Adapter->TXFree, BufferCount));
|
|
/* We don't have the resources to transmit this packet right now */
|
|
return FALSE;
|
|
}
|
|
|
|
/* Write the packet to the card */
|
|
NICWritePacket(Adapter);
|
|
|
|
/* If the NIC is not transmitting, reset the current transmit pointer */
|
|
if (Adapter->TXCurrent == -1)
|
|
Adapter->TXCurrent = Adapter->TXNext;
|
|
|
|
Adapter->TXNext = (Adapter->TXNext + BufferCount) % Adapter->TXCount;
|
|
Adapter->TXFree -= BufferCount;
|
|
|
|
/* Remove the packet from the queue */
|
|
Packet = Adapter->TXQueueHead;
|
|
Adapter->TXQueueHead = RESERVED(Packet)->Next;
|
|
|
|
if (Packet == Adapter->TXQueueTail)
|
|
Adapter->TXQueueTail = NULL;
|
|
|
|
/* Assume the transmit went well */
|
|
NdisMSendComplete(Adapter->MiniportAdapterHandle,
|
|
Packet,
|
|
NDIS_STATUS_SUCCESS);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID NICTransmit(
|
|
PNIC_ADAPTER Adapter)
|
|
/*
|
|
* FUNCTION: Starts transmitting packets in the transmit queue
|
|
* ARGUMENTS:
|
|
* Adapter = Pointer to adapter information
|
|
* NOTES:
|
|
* There must be at least one packet in the transmit queue
|
|
*/
|
|
{
|
|
NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
|
|
|
|
if (Adapter->TXCurrent == -1) {
|
|
/* NIC is not transmitting, so start transmitting now */
|
|
|
|
/* Load next packet onto the card, and start transmitting */
|
|
if (NICPrepareForTransmit(Adapter))
|
|
NICStartTransmit(Adapter);
|
|
}
|
|
}
|
|
|
|
|
|
static VOID HandleReceive(
|
|
PNIC_ADAPTER Adapter)
|
|
/*
|
|
* FUNCTION: Handles reception of a packet
|
|
* ARGUMENTS:
|
|
* Adapter = Pointer to adapter information
|
|
* NOTES:
|
|
* Buffer overflows are also handled here
|
|
*/
|
|
{
|
|
UINT i;
|
|
UCHAR Tmp;
|
|
UINT PacketCount;
|
|
|
|
NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
|
|
|
|
Adapter->DoneIndicating = FALSE;
|
|
PacketCount = 0;
|
|
|
|
NICGetCurrentPage(Adapter);
|
|
|
|
if (Adapter->BufferOverflow) {
|
|
|
|
NDIS_DbgPrint(MID_TRACE, ("Receive ring overflow.\n"));
|
|
|
|
/* Select page 0 and stop the NIC */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_CR, CR_STP | CR_RD2 | CR_PAGE0);
|
|
|
|
/* Clear RBCR0,RBCR1 - Remote Byte Count Registers */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_RBCR0, 0x00);
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_RBCR1, 0x00);
|
|
|
|
/* Wait for ISR_RST to be set, but timeout after 2ms */
|
|
for (i = 0; i < 4; i++) {
|
|
NdisRawReadPortUchar(Adapter->IOBase + PG0_ISR, &Tmp);
|
|
if (Tmp & ISR_RST)
|
|
break;
|
|
|
|
NdisStallExecution(500);
|
|
}
|
|
|
|
#if DBG
|
|
if (i == 4)
|
|
NDIS_DbgPrint(MIN_TRACE, ("NIC was not reset after 2ms.\n"));
|
|
#endif
|
|
|
|
if ((Adapter->InterruptStatus & (ISR_PTX | ISR_TXE)) == 0) {
|
|
/* We may need to restart the transmitter */
|
|
Adapter->TransmitPending = TRUE;
|
|
}
|
|
|
|
/* Initialize TCR - Transmit Configuration Register to loopback mode 1 */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_TCR, TCR_LOOP);
|
|
|
|
/* Start NIC */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_CR, CR_STA | CR_RD2);
|
|
|
|
NICStart(Adapter);
|
|
|
|
Adapter->BufferOverflow = FALSE;
|
|
}
|
|
|
|
if (Adapter->ReceiveError) {
|
|
NDIS_DbgPrint(MID_TRACE, ("Receive error.\n"));
|
|
|
|
/* Skip this packet */
|
|
Adapter->NextPacket = Adapter->CurrentPage;
|
|
NICSetBoundaryPage(Adapter);
|
|
|
|
Adapter->ReceiveError = FALSE;
|
|
}
|
|
|
|
for (;;) {
|
|
NICGetCurrentPage(Adapter);
|
|
|
|
NDIS_DbgPrint(MID_TRACE, ("Current page (0x%X) NextPacket (0x%X).\n",
|
|
Adapter->CurrentPage,
|
|
Adapter->NextPacket));
|
|
|
|
if (Adapter->CurrentPage == Adapter->NextPacket) {
|
|
NDIS_DbgPrint(MID_TRACE, ("No more packets.\n"));
|
|
break;
|
|
} else {
|
|
NDIS_DbgPrint(MID_TRACE, ("Got a packet in the receive ring.\n"));
|
|
|
|
NDIS_DbgPrint(MAX_TRACE,("Adapter->MiniportAdapterHandle: %x\n",
|
|
Adapter->MiniportAdapterHandle));
|
|
/* Read packet from receive buffer ring */
|
|
NICReadPacket(Adapter);
|
|
|
|
Adapter->DoneIndicating = TRUE;
|
|
|
|
PacketCount++;
|
|
if (PacketCount == 10) {
|
|
/* Don't starve transmit interrupts */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((Adapter->TransmitPending) && (Adapter->TXCurrent != -1)) {
|
|
NDIS_DbgPrint(MID_TRACE, ("Retransmitting current packet at (%d).\n", Adapter->TXCurrent));
|
|
/* Retransmit packet */
|
|
NICStartTransmit(Adapter);
|
|
Adapter->TransmitPending = FALSE;
|
|
}
|
|
|
|
if (Adapter->DoneIndicating)
|
|
NdisMEthIndicateReceiveComplete(Adapter->MiniportAdapterHandle);
|
|
}
|
|
|
|
|
|
static VOID HandleTransmit(
|
|
PNIC_ADAPTER Adapter)
|
|
/*
|
|
* FUNCTION: Handles transmission of a packet
|
|
* ARGUMENTS:
|
|
* Adapter = Pointer to adapter information
|
|
*/
|
|
{
|
|
UINT Length;
|
|
UINT BufferCount;
|
|
|
|
// PIP_PACKET pIPPacket;
|
|
// pIPPacket = (PIP_PACKET)
|
|
// DisplayIPPacket(pIPPacket);
|
|
|
|
if (Adapter->TransmitError) {
|
|
/* FIXME: Retransmit now or let upper layer protocols handle retransmit? */
|
|
Adapter->TransmitError = FALSE;
|
|
}
|
|
|
|
/* Free transmit buffers */
|
|
Length = Adapter->TXSize[Adapter->TXCurrent];
|
|
BufferCount = (Length + DRIVER_BLOCK_SIZE - 1) / DRIVER_BLOCK_SIZE;
|
|
|
|
NDIS_DbgPrint(MID_TRACE, ("Freeing (%d) buffers at (%d).\n",
|
|
BufferCount,
|
|
Adapter->TXCurrent));
|
|
|
|
Adapter->TXFree += BufferCount;
|
|
Adapter->TXSize[Adapter->TXCurrent] = 0;
|
|
Adapter->TXCurrent = (Adapter->TXCurrent + BufferCount) % Adapter->TXCount;
|
|
|
|
if (Adapter->TXSize[Adapter->TXCurrent] == 0) {
|
|
NDIS_DbgPrint(MID_TRACE, ("No more packets in transmit buffer.\n"));
|
|
|
|
Adapter->TXCurrent = -1;
|
|
}
|
|
|
|
if (Adapter->TXQueueTail) {
|
|
if (NICPrepareForTransmit(Adapter))
|
|
NICStartTransmit(Adapter);
|
|
}
|
|
}
|
|
|
|
|
|
VOID NTAPI MiniportHandleInterrupt(
|
|
IN NDIS_HANDLE MiniportAdapterContext)
|
|
/*
|
|
* FUNCTION: Handler for deferred processing of interrupts
|
|
* ARGUMENTS:
|
|
* MiniportAdapterContext = Pointer to adapter context area
|
|
* NOTES:
|
|
* Interrupt Service Register is read to determine which interrupts
|
|
* are pending. All pending interrupts are handled
|
|
*/
|
|
{
|
|
UCHAR ISRValue;
|
|
UCHAR ISRMask;
|
|
UCHAR Mask;
|
|
PNIC_ADAPTER Adapter = (PNIC_ADAPTER)MiniportAdapterContext;
|
|
UINT i = 0;
|
|
|
|
ISRMask = Adapter->InterruptMask;
|
|
NdisRawReadPortUchar(Adapter->IOBase + PG0_ISR, &ISRValue);
|
|
|
|
NDIS_DbgPrint(MID_TRACE, ("ISRValue (0x%X).\n", ISRValue));
|
|
|
|
Adapter->InterruptStatus |= (ISRValue & ISRMask);
|
|
|
|
Mask = 0x01;
|
|
while (Adapter->InterruptStatus != 0x00 && i++ < INTERRUPT_LIMIT) {
|
|
|
|
if (ISRValue != 0x00) {
|
|
/* Acknowledge interrupts */
|
|
NdisRawWritePortUchar(Adapter->IOBase + PG0_ISR, ISRValue);
|
|
Mask = 0x01;
|
|
}
|
|
|
|
NDIS_DbgPrint(MID_TRACE, ("Adapter->InterruptStatus (0x%X) Mask (0x%X).\n",
|
|
Adapter->InterruptStatus, Mask));
|
|
|
|
/* Find next interrupt type */
|
|
while (((Adapter->InterruptStatus & Mask) == 0) && (Mask < ISRMask))
|
|
Mask = (Mask << 1);
|
|
|
|
switch (Adapter->InterruptStatus & Mask) {
|
|
case ISR_OVW:
|
|
NDIS_DbgPrint(MID_TRACE, ("Overflow interrupt.\n"));
|
|
/* Overflow. Handled almost the same way as a receive interrupt */
|
|
Adapter->BufferOverflow = TRUE;
|
|
|
|
NDIS_DbgPrint(MAX_TRACE,("Adapter->MiniportAdapterHandle: %x\n",
|
|
Adapter->MiniportAdapterHandle));
|
|
if(Adapter->MiniportAdapterHandle)
|
|
HandleReceive(Adapter);
|
|
else
|
|
NDIS_DbgPrint(MAX_TRACE,("No miniport adapter yet\n"));
|
|
|
|
Adapter->InterruptStatus &= ~ISR_OVW;
|
|
break;
|
|
|
|
case ISR_RXE:
|
|
NDIS_DbgPrint(MID_TRACE, ("Receive error interrupt.\n"));
|
|
NICUpdateCounters(Adapter);
|
|
|
|
Adapter->ReceiveError = TRUE;
|
|
break;
|
|
case ISR_PRX:
|
|
NDIS_DbgPrint(MID_TRACE, ("Receive interrupt.\n"));
|
|
|
|
NDIS_DbgPrint(MAX_TRACE,("Adapter->MiniportAdapterHandle: %x\n",
|
|
Adapter->MiniportAdapterHandle));
|
|
if(Adapter->MiniportAdapterHandle)
|
|
HandleReceive(Adapter);
|
|
else
|
|
NDIS_DbgPrint(MAX_TRACE,("No miniport adapter yet\n"));
|
|
|
|
Adapter->InterruptStatus &= ~(ISR_PRX | ISR_RXE);
|
|
break;
|
|
|
|
case ISR_TXE:
|
|
NDIS_DbgPrint(MID_TRACE, ("Transmit error interrupt.\n"));
|
|
NICUpdateCounters(Adapter);
|
|
|
|
Adapter->TransmitError = TRUE;
|
|
break;
|
|
case ISR_PTX:
|
|
NDIS_DbgPrint(MID_TRACE, ("Transmit interrupt.\n"));
|
|
|
|
HandleTransmit(Adapter);
|
|
|
|
Adapter->InterruptStatus &= ~(ISR_PTX | ISR_TXE);
|
|
break;
|
|
|
|
case ISR_CNT:
|
|
NDIS_DbgPrint(MID_TRACE, ("Counter interrupt.\n"));
|
|
/* Counter overflow. Read counters from the NIC */
|
|
NICUpdateCounters(Adapter);
|
|
|
|
Adapter->InterruptStatus &= ~ISR_CNT;
|
|
break;
|
|
|
|
default:
|
|
NDIS_DbgPrint(MID_TRACE, ("Unknown interrupt. Adapter->InterruptStatus (0x%X).\n", Adapter->InterruptStatus));
|
|
Adapter->InterruptStatus &= ~Mask;
|
|
break;
|
|
}
|
|
|
|
Mask = (Mask << 1);
|
|
|
|
/* Check if new interrupts are generated */
|
|
|
|
NdisRawReadPortUchar(Adapter->IOBase + PG0_ISR, &ISRValue);
|
|
|
|
NDIS_DbgPrint(MID_TRACE, ("ISRValue (0x%X).\n", ISRValue));
|
|
|
|
Adapter->InterruptStatus |= (ISRValue & ISRMask);
|
|
}
|
|
|
|
NICEnableInterrupts((PNIC_ADAPTER)MiniportAdapterContext);
|
|
}
|
|
|
|
/* EOF */
|