📄 if_ne2kd.c
字号:
// ***** Update statistics
Statistics.OverrunErrors++;
// ***********************************************************************************
// ***** The following buffer ring overflow procedure is taken from the datasheet
// ***** DP8390D/NS32490D NIC Network Interface Controller (July 1995) from National
// ***** Semiconductor. This procedure is mandatory!
// ***** 1. Read and store the value of the TXP bit (from command register)
TxpBit = (INPORTB(NIC_CR) & CR_TXP);
PAUSE;
// ***** 2. Issue a stop command
OUTPORTB(NIC_CR, CR_STOP | CR_NO_DMA | CR_PAGE0);
PAUSE;
// ***** 3. Wait for at least 1.6 ms
LONGPAUSE;
// ***** 4. Clear NIC's Remote Byte Count registers (RBCR0 and RBCR1)
OUTPORTB(PG0W_RBCR0, 0x00);
PAUSE;
OUTPORTB(PG0W_RBCR1, 0x00);
PAUSE;
// ***** 5. Read the stored value of the TXP bit from step 1 (determine if we stopped the NIC
// ***** when it was transmitting).
// ***** IF TXP bit = 1 THEN
if (TxpBit)
// ***** IF PTX = 1 OR TXE = 1 THEN
if (INPORTB(PG0R_ISR) & (ISR_PTX | ISR_TXE))
{
PAUSE;
// ***** Resend = FALSE
Resend = FALSE;
}
else
{
PAUSE;
// ***** Resend = TRUE
Resend = TRUE;
}
else
// ***** Resend = FALSE
Resend = FALSE;
// ***** 6. Place the NIC in mode 1 (internal loopback)
OUTPORTB(PG0W_TCR, TCR_LB0);
PAUSE;
// ***** 7. Start the NIC
OUTPORTB(NIC_CR, CR_START | CR_NO_DMA | CR_PAGE0);
PAUSE;
}
else PAUSE;
// ***** 8. Remove one or more packets from the NIC
// ***** IF (packet = NULL) OR (length = 0) THEN - Just remove one packet from the receive buffer ring
if (!PacketLength)
{
// ***** IF ReadBuffer(header, NULL, 0) THEN
if (ReadBuffer(&Header, NULL, 0))
{
// ***** Remove packet!
// ***** NextPacket = Header.NextPage;
NextPacket = Header.NextPage;
// ***** Initialize Boundary (read) Pointer to the value of NextPacket - 1
// ***** IF Boundary Pointer < RSTART_PG THEN BNDRY = RSTOP_PG - 1
if ( (NextPacket - 1) < RSTART_PG )
OUTPORTB(PG0W_BNRY, RSTOP_PG - 1);
else
OUTPORTB(PG0W_BNRY, NextPacket - 1);
PAUSE;
// ***** Success = TRUE
Success = TRUE;
// ***** Update statistics
Statistics.BytesReceived += Header.Length - 4;
Statistics.PacketsReceived++;
}
}
else
{
// ***** IF ReadBuffer(header, packet, length) THEN
if (ReadBuffer(&Header, packet, PacketLength))
{
// ***** Remove packet!
// ***** NextPacket = Header.NextPage;
NextPacket = Header.NextPage;
// ***** Initialize Boundary (read) Pointer to the value of NextPacket - 1
// ***** IF Boundary Pointer < RSTART_PG THEN BNDRY = RSTOP_PG - 1
if ( (NextPacket - 1) < RSTART_PG )
OUTPORTB(PG0W_BNRY, RSTOP_PG - 1);
else
OUTPORTB(PG0W_BNRY, NextPacket - 1);
PAUSE;
// ***** Success = TRUE
Success = TRUE;
// ***** Update statistics
Statistics.BytesReceived += Header.Length - 4;
Statistics.PacketsReceived++;
}
}
// ***** IF NIC buffer overwrite warning THEN
if (INPORTB(PG0R_ISR) & ISR_OVW)
{
PAUSE;
// ***** 9. Reset the overwrite warning bit in the Interrupt Status Register.
OUTPORTB(PG0W_ISR, ISR_OVW);
PAUSE;
// ***** 10. Take the NIC out of loopback mode (that means normal operation)
OUTPORTB(PG0W_TCR, 0x00);
PAUSE;
// ***** 11. IF Resend = 1 THEN reissue a transmit
if (Resend)
{
// Reissue transmit
OUTPORTB(NIC_CR, CR_START | CR_NO_DMA | CR_TXP);
PAUSE;
}
}
else PAUSE;
// ***** Restore NIC IMR
OUTPORTB(PG0W_IMR, Imr);
PAUSE;
// ***** RETURN Success
return Success;
}
int Ne2kTransmitReady(void)
{
// ***** IF transmitting THEN RETURN FALSE
if (INPORTB(NIC_CR) & CR_TXP) return FALSE;
// ***** RETURN TRUE
return TRUE;
}
int Ne2kTransmit(const u_char *packet, u_short length)
{
int Timeout, Count;
u_char Imr;
u_char CrdaLow, CrdaHigh;
// ***** IF transmitting THEN RETURN FALSE
if (!Ne2kTransmitReady()) return FALSE;
// ***** IF (length < 14) OR (length > 1514) THEN RETURN FALSE
if ((length < 14) || (length > 1514)) return FALSE;
// ***** Remember NIC IMR and disable interrupt from NIC (ATOMIC OPERATION!)
DISABLE_INTERRUPTS;
// Select PAGE 2
OUTPORTB(NIC_CR, CR_PAGE2 | CR_NO_DMA | CR_START);
PAUSE;
// Read IMR register
Imr = INPORTB(PG2R_IMR);
PAUSE;
// Select PAGE 0 again
OUTPORTB(NIC_CR, CR_PAGE0 | CR_NO_DMA | CR_START);
PAUSE;
// Disable interrupts from NIC
OUTPORTB(PG0W_IMR, 0x00);
PAUSE;
ENABLE_INTERRUPTS;
// ***** Clear REMOTE DMA COMPLETE bit in ISR
OUTPORTB(PG0W_ISR, ISR_RDC);
PAUSE;
// ***********************************************************************************
// ***** Due to two non synchronized state machines in the NIC, you should always do a
// ***** Remote Read (called a dummy read) before a Remote Write.
// ***** This is stated in the datasheet DP8390D/NS32490D NIC Network Interface Controller
// ***** (July 1995) from National Semiconductor.
// ***** 1. Set Remote Byte Count to a value > 0 and Remote Start Address to unused RAM
// (like transmit start page - 1) (REMEMBER THESE VALUES!)
CrdaLow = 0;
CrdaHigh = TSTART_PG - 1;
OUTPORTB(PG0W_RBCR0, 1);
PAUSE;
OUTPORTB(PG0W_RBCR1, 0);
PAUSE;
OUTPORTB(PG0W_RSAR0, CrdaLow);
PAUSE;
OUTPORTB(PG0W_RSAR1, CrdaHigh);
PAUSE;
// ***** 2. Issue the "dummy" Remote Read command
OUTPORTB(NIC_CR, CR_START | CR_DMA_READ);
PAUSE;
// ***** 3. Read the current remote DMA address (CRDA) (both bytes)
// ***** 4. Compare to previous CRDA value, if different go to step 6
// ***** 5. Delay and go to step 3.
// timeout < 100 === leave us with appr. 200us timeout (which is a lot)
// (this timeout is not impl. in NS' datasheet, but it's better to be on the safe side)
for (Timeout = 0; Timeout < 50; Timeout++)
{
if (CrdaLow != INPORTB(PG0R_CRDA0)) break;
PAUSE;
if (CrdaHigh != INPORTB(PG0R_CRDA1)) break;
PAUSE;
}
// ***** 6. Setup for the Remote Write command
// ***** Stop REMOTE DMA
OUTPORTB(NIC_CR, CR_START | CR_NO_DMA);
PAUSE;
// ***** Initialize Transmit Byte Count Registers to packet length and MIN. MIN_PACKET_SIZE bytes
if (length < MIN_PACKET_SIZE)
{
// Padding required
OUTPORTB(PG0W_TBCR0, MIN_PACKET_SIZE);
PAUSE;
OUTPORTB(PG0W_TBCR1, 0);
PAUSE;
}
else
{
// No padding required
OUTPORTB(PG0W_TBCR0, (length & 0xFF));
PAUSE;
OUTPORTB(PG0W_TBCR1, ((length >> 8) & 0xFF) );
PAUSE;
}
// ***** Update statistics (before making length an even value!)
Statistics.BytesTransmitted += length;
Statistics.PacketsTransmitted++;
// ***** We use word transfer mode, so make length an even value
length &= 0xfffe;
length++;
// ***** Initialize Remote Byte Count Register (DMA) to packet length
OUTPORTB(PG0W_RBCR0, (length & 0xFF));
PAUSE;
OUTPORTB(PG0W_RBCR1, ((length >> 8) & 0xFF));
PAUSE;
// ***** Initialize Remote Start Address Register to address of NIC transmit buffer
OUTPORTB(PG0W_RSAR0, 0);
PAUSE;
OUTPORTB(PG0W_RSAR1, TSTART_PG);
PAUSE;
// ***** 7. Issue the Remote DMA WRITE
// ***** Start DMA Write
OUTPORTB(NIC_CR, CR_START | CR_DMA_WRITE);
PAUSE;
// ***** Write data to NIC
for (Count = 0; Count < length; Count += 2) OUTPORTW(NIC_DATAPORT, packet[Count] | (packet[Count+1] << 8));
// ***** Wait for Remote DMA to complete (should be instantly, but we have to wait anyway)
// timeout < 200 === leave us with appr. 200us timeout (which is a lot)
// (this timeout is not impl. in NS' datasheet, but it's better to be on the safe side)
for (Timeout = 0; Timeout < 200; Timeout++)
{
if (INPORTB(PG0R_ISR) & ISR_RDC) break;
PAUSE;
}
// ***** Clear REMOTE DMA COMPLETE bit in ISR
OUTPORTB(PG0W_ISR, ISR_RDC);
PAUSE;
// ***** Initialize Transmit Page Start Register
OUTPORTB(PG0W_TPSR, TSTART_PG);
PAUSE;
// ***** Start transmission
OUTPORTB(NIC_CR, CR_START | CR_NO_DMA | CR_TXP);
PAUSE;
// ***** Restore NIC IMR
OUTPORTB(PG0W_IMR, Imr);
PAUSE;
// ***** RETURN TRUE
return TRUE;
}
void Ne2kGetStatistics(Ne2kStatistics *statistics)
{
DISABLE_INTERRUPTS;
// Copy statistics to caller
memcpy(statistics, &Statistics, sizeof(Statistics));
ENABLE_INTERRUPTS;
}
int ReadBuffer(BufferHeader *header, u_char *packet, u_short length)
{
u_char Save_curr;
Word Word;
int OddLength;
int Count = 0;
// ***** IF packet pointer = NULL THEN length = 0
if (!packet) length = 0;
// ***** Clear REMOTE DMA COMPLETE bit in ISR
OUTPORTB(PG0W_ISR, ISR_RDC);
PAUSE;
// ***** Make length an even value
OddLength = length & 0x0001;
length++;
length &= 0xfffe;
// ***** Read CURR
// Read Current Page of rcv ring
// Go to page 1
OUTPORTB(NIC_CR, CR_START | CR_NO_DMA | CR_PAGE1);
PAUSE;
// Read CURR
Save_curr = INPORTB(PG1R_CURR);
PAUSE;
// Go to page 0
OUTPORTB(NIC_CR, CR_START | CR_NO_DMA | CR_PAGE0);
PAUSE;
// ***** LOOP WHILE CURR <> NextPacket - While we have a packet in buffer
while (Save_curr != NextPacket)
{
// Setup Remote Byte Counts and Remote Start Address to read length + 4 bytes for buffer header
OUTPORTB(PG0W_RBCR0, (length + 4) & 0xFF);
PAUSE;
OUTPORTB(PG0W_RBCR1, ((length + 4) >> 8) & 0xFF);
PAUSE;
OUTPORTB(PG0W_RSAR0, 0);
PAUSE;
OUTPORTB(PG0W_RSAR1, NextPacket);
PAUSE;
// ***** Issue the Remote Read command
OUTPORTB(NIC_CR, CR_START | CR_DMA_READ);
PAUSE;
// ***** Read buffer header (4 bytes)
Word.Word = INPORTW(NIC_DATAPORT);
header->Status = Word.Uchar[0];
header->NextPage = Word.Uchar[1];
header->Length = INPORTW(NIC_DATAPORT);
if (length)
{
for (Count = 0; Count < (length - 2); Count+=2)
{
Word.Word = INPORTW(NIC_DATAPORT);
packet[Count] = Word.Uchar[0];
packet[Count+1] = Word.Uchar[1];
// The line below might very well be a bit faster...
// *((u_short *)(packet+Count)) = INPORTW(NIC_DATAPORT);
}
Word.Word = INPORTW(NIC_DATAPORT);
}
// ***** Stop REMOTE DMA
OUTPORTB(NIC_CR, CR_START | CR_NO_DMA);
PAUSE;
if (length)
{
packet[Count] = Word.Uchar[0];
if (!OddLength) packet[Count+1] = Word.Uchar[1];
}
// Clear REMOTE DMA COMPLETE bit in ISR
OUTPORTB(PG0W_ISR, ISR_RDC);
PAUSE;
// ***** IF next page pointer is out of receive buffer range THEN
// We have a serious problem
if ( (header->NextPage < RSTART_PG) || (header->NextPage > RSTOP_PG) )
{
// ***** Initialize NextPacket pointer to last known good value of the Current (write) Pointer.
// This means that we drop a lot of packets, but beats winding up in the weeds!
NextPacket = Save_curr;
// ***** Initialize Boundary (read) Pointer to the value of NextPacket - 1
// ***** IF Boundary Pointer < RSTART_PG THEN BNDRY = PSTOP - 1
if ( (NextPacket - 1) < RSTART_PG )
OUTPORTB(PG0W_BNRY, RSTOP_PG - 1);
else
OUTPORTB(PG0W_BNRY, NextPacket - 1);
PAUSE;
// ***** This will drop a lot of packets! Count this error!
Statistics.NextPageErrors++;
}
else
{
// ***** Packets should always be OK, but you never know
// (the NIC is configured to only store valid packets)
// ***** IF status field is OK (valid packet) THEN RETURN length
if (header->Status & RSR_PRX)
{
return header->Length - 4;
}
else
{
// ***** Discard packet in receive buffer
// ***** Set NextPacket to next page pointer
NextPacket = header->NextPage;
// ***** Initialize Boundary (read) Pointer to the value of NextPacket - 1
// ***** IF Boundary Pointer < RSTART_PG THEN BNDRY = RSTOP - 1
// (drops invalid packet)
if ( (NextPacket - 1) < RSTART_PG )
OUTPORTB(PG0W_BNRY, RSTOP_PG - 1);
else
OUTPORTB(PG0W_BNRY, NextPacket - 1);
PAUSE;
}
}
}
// ***** No valid packets in buffer, RETURN FALSE
return FALSE;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -