📄 ax88796.c
字号:
#include <windows.h>
#include <halether.h>
#include "ceddk.h"
#include "ax88796.h"
//#define DEBUG_INIT
//#define DEBUG_SENDFRAME
//#define DEBUG_GETFRAME
#define NE_DATAPORT EI_SHIFT(0x10) /* NatSemi-defined port window offset. */
#define NE_RESET EI_SHIFT(0x1f) /* Issue a read to reset, a write to clear. */
static unsigned E8390_BASE = 0x400;
static unsigned char inb( unsigned addr)
{
return (unsigned char) *((volatile unsigned short *) addr);
}
static void insw( unsigned addr, unsigned short* buffer, int count)
{
while( count-- > 0)
*buffer++ = *((volatile unsigned short *)addr);
}
static void outb( unsigned char c, unsigned addr)
{
*((volatile unsigned short *)addr) = c;
}
static void outsw( unsigned addr, unsigned short* buffer, int count)
{
while( count-- > 0)
*((volatile unsigned short *)addr) = *buffer++;
}
void NE2000DisableInts(void)
{
#ifdef DEBUG_INIT
EdbgOutputDebugString( "ax88796: eth_halt\r\n");
#endif
outb(E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_BASE + E8390_CMD); // switch to page 0 + stop
}
void NE2000EnableInts(void)
{
#ifdef DEBUG_INIT
EdbgOutputDebugString( "ax88796: eth_start\r\n");
#endif
// clear the pending interrupts and mask
outb( 0xff, E8390_BASE + EN0_ISR);
outb( ENISR_ALL, E8390_BASE + EN0_IMR);
outb(E8390_NODMA+E8390_PAGE0+E8390_START, E8390_BASE + E8390_CMD); // switch to page 0 + start
}
DWORD NE2000GetPendingInts(void)
{
unsigned char curr_page, this_page;
#ifdef DEBUG_INIT
EdbgOutputDebugString( "ax88796: get_interrupts\r\n");
#endif
// get the rx page (incoming packet pointer)
outb(E8390_NODMA+E8390_PAGE1, E8390_BASE + E8390_CMD); // switch to page 1, get curr page register
curr_page = inb( E8390_BASE + EN1_CURPAG);
outb(E8390_NODMA+E8390_PAGE0, E8390_BASE + E8390_CMD); // switch to page 0
#ifdef DEBUG_INIT
EdbgOutputDebugString( "curr_page=0x%x\r\n", curr_page);
#endif
// remove one frame from the ring, boundary is always a page behind
this_page = inb( E8390_BASE + EN0_BOUNDARY) + 1;
if (this_page >= 0x80)
this_page = 0x46; // wrap around page register
#ifdef DEBUG_INIT
EdbgOutputDebugString( "this_page=0x%x\r\n", this_page);
#endif
if (this_page == curr_page) // read all the frames
return 0;
return INTR_TYPE_RX;
}
extern void msWait(unsigned msVal);
static int ax88796_reset(unsigned ioaddr)
{
unsigned rto = 500; // reset timeout 5*5ms
// reset card, who knows what brain-damaged state it was left in
outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET);
while( (inb(ioaddr + EN0_ISR) & ENISR_RESET) == 0)
{
if( rto-- == 0) // reset timeout ?
return -1;
msWait(5); // wait 5ms
}
outb(0xff, ioaddr + EN0_ISR); // ack all intr
return 0;
}
static int ax88796_probe( int ioaddr)
{
// verify chip present
if( inb(E8390_BASE) == 0xff)
return -1;
// Do a preliminary verification that we have a 8390 chipset
outb(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD); // set page 1 registers
outb(0xff, ioaddr + EN0_COUNTER0); // set 0xff at offset of EN0_COUNTER0(page0)
outb(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD); // switch to page 0
inb(ioaddr + EN0_COUNTER0); // clear the counter by reading
if( inb(ioaddr + EN0_COUNTER0) != 0)
return -1;
return 0;
}
BOOL NE2000Init( BYTE *pbBaseAddress, DWORD dwMultiplier, USHORT macAddr[3])
{
E8390_BASE = (unsigned) pbBaseAddress;
#ifdef DEBUG_INIT
EdbgOutputDebugString( "ax88796: eth_init chip at 0x%X\r\n", E8390_BASE);
#endif
if( ax88796_probe(E8390_BASE) < 0)
{
EdbgOutputDebugString("ax88796: ethernet chip not found\r\n");
return FALSE;
}
#ifdef DEBUG_INIT
EdbgOutputDebugString("ax88796: found at 0x%X\r\n", E8390_BASE);
#endif
if( ax88796_reset(E8390_BASE) < 0)
{
EdbgOutputDebugString("ax88796: reset failed (no reset ack)\r\n");
return FALSE;
}
// follow ax88796 init procedure
outb(E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_BASE + E8390_CMD); // switch to page 0
outb( 0x01, E8390_BASE + EN0_DCFG); // select word wide transfer
outb( E8390_RXCONFIG, E8390_BASE + EN0_RXCR); // irq high active, accept broadcasts
// clear the remote byte count registers
outb( 0x00, E8390_BASE + EN0_RCNTLO);
outb( 0x00, E8390_BASE + EN0_RCNTHI);
// set the transmit page and receive ring
outb( 0x40, E8390_BASE + EN0_TPSR); // tx start page register
outb( 0x46, E8390_BASE + EN0_STARTPG); // start page register
outb( 0x80, E8390_BASE + EN0_STOPPG); // stop page register
outb( 0x46, E8390_BASE + EN0_BOUNDARY);
// clear the pending interrupts and mask
outb( 0xff, E8390_BASE + EN0_ISR);
outb( ENISR_ALL, E8390_BASE + EN0_IMR);
{
int i;
unsigned char* pmac = (unsigned char*) macAddr;
// set mac address
outb(E8390_NODMA + E8390_PAGE1 + E8390_STOP, E8390_BASE + E8390_CMD); // switch to page 1
for(i = 0; i < ETHER_ADDR_LEN; i++)
{
outb( pmac[i], E8390_BASE + EN1_PHYS_SFT(i));
if( inb( E8390_BASE + EN1_PHYS_SFT(i)) != pmac[i])
EdbgOutputDebugString( "ax88796: mac address read/write mismap %d\r\n", i);
}
}
outb( 0x47, E8390_BASE + EN1_CURPAG); // set current page register
outb(E8390_NODMA+E8390_PAGE0+E8390_START, E8390_BASE + E8390_CMD); // switch to page 0 + start
if( inb( E8390_BASE + EN0_GPI) & 0x02) // set to full duplex mode
outb( 0x80 | E8390_TXCONFIG, E8390_BASE + EN0_TXCR);
else
outb( E8390_TXCONFIG, E8390_BASE + EN0_TXCR);
return TRUE;
}
// Send a data block via Ethernet
UINT16 NE2000SendFrame( BYTE *packet, DWORD length )
{
unsigned txto;
#ifdef DEBUG_SENDFRAME
EdbgOutputDebugString( "ax88796: +eth_send\r\n");
#endif
outb(E8390_PAGE0+E8390_START+E8390_NODMA, E8390_BASE + E8390_CMD); // switch to page 0
outb(ENISR_RDC, E8390_BASE + EN0_ISR); // set remote dma complete
if( length & 0x01) // round the length up for word writes
length++;
// setup remote dma transfer
outb( (unsigned char) (length & 0xff), E8390_BASE + EN0_RCNTLO);
outb( (unsigned char) (length >> 8), E8390_BASE + EN0_RCNTHI);
outb( 0x00, E8390_BASE + EN0_RSARLO);
outb( 0x40, E8390_BASE + EN0_RSARHI);
//start data transfer
outb( E8390_RWRITE+E8390_START, E8390_BASE + E8390_CMD);
outsw( E8390_BASE + NE_DATAPORT, (unsigned short*)packet, length>>1);
// write packet tx length
outb( (unsigned char) (length & 0xff), E8390_BASE + EN0_TCNTLO);
outb( (unsigned char) (length >> 8), E8390_BASE + EN0_TCNTHI);
// trigger tx
outb( E8390_NODMA+E8390_TRANS+E8390_START, E8390_BASE + E8390_CMD);
txto = 20; // tx timeout 20*10ms
while( (inb( E8390_BASE + EN0_ISR) & (ENISR_RDC|ENISR_TX)) == 0)
{
if( txto-- == 0)
{
EdbgOutputDebugString("ax88796: tramsmit timeout\r\n");
// ax88796_reset(E8390_BASE);
break;
}
msWait(10);
}
// outb( (ENISR_RDC|ENISR_ALL), E8390_BASE + EN0_ISR); // ack intr
#ifdef DEBUG_SENDFRAME
EdbgOutputDebugString( "ax88796: -eth_send\r\n");
#endif
return 0;
}
static void ax88796_read( unsigned char* buffer, int count, unsigned ring_addr)
{
#ifdef DEBUG_GETFRAME
EdbgOutputDebugString( "ax88796: reading %d bytes from ring at 0x%x\r\n", count, ring_addr);
#endif
outb( E8390_NODMA+E8390_PAGE0+E8390_START, E8390_BASE + E8390_CMD); // switch to page 0
outb( count & 0xff, E8390_BASE + EN0_RCNTLO); // write count of bytes
outb( count >> 8, E8390_BASE + EN0_RCNTHI);
outb( ring_addr & 0xff, E8390_BASE + EN0_RSARLO); // set read start address
outb( ring_addr >> 8, E8390_BASE + EN0_RSARHI);
// read data from rx buffer
outb( E8390_RREAD+E8390_START, E8390_BASE + E8390_CMD);
insw( E8390_BASE + NE_DATAPORT, (unsigned short*)buffer, count>>1);
if (count & 0x01)
buffer[count-1] = inb( E8390_BASE + NE_DATAPORT);
}
#ifdef DEBUG_GETFRAME
static void DumpEtherFrame( BYTE *pFrame, WORD cwFrameLength )
{
int i,j;
EdbgOutputDebugString( "Frame Buffer Address: 0x%X\r\n", pFrame );
EdbgOutputDebugString( "To: %B:%B:%B:%B:%B:%B From: %B:%B:%B:%B:%B:%B Type: 0x%H Length: %u\r\n",
pFrame[0], pFrame[1], pFrame[2], pFrame[3], pFrame[4], pFrame[5],
pFrame[6], pFrame[7], pFrame[8], pFrame[9], pFrame[10], pFrame[11],
ntohs(*((UINT16 *)(pFrame + 12))), cwFrameLength );
for( i = 0; i < cwFrameLength / 16; i++ ) {
for( j = 0; j < 16; j++ )
EdbgOutputDebugString( " %B", pFrame[i*16 + j] );
EdbgOutputDebugString( "\r\n" );
}
for( j = 0; j < cwFrameLength % 16; j++ )
EdbgOutputDebugString( " %B", pFrame[i*16 + j] );
EdbgOutputDebugString( "\r\n" );
}
#endif
// Get a data block via Ethernet
UINT16 NE2000GetFrame(BYTE *pbData, UINT16 *pwLength )
{
int rxlen = *pwLength;
unsigned char curr_page, this_page;
struct e8390_pkt_hdr rx_frame;
#ifdef DEBUG_GETFRAME
EdbgOutputDebugString( "ax88796: +eth_rx\r\n");
#endif
outb( 0, E8390_BASE + EN0_IMR); // mask interrupts
outb( ENISR_ALL, E8390_BASE + EN0_ISR); // ack interrupts
*pwLength = 0;
// get the rx page (incoming packet pointer)
outb(E8390_NODMA+E8390_PAGE1, E8390_BASE + E8390_CMD); // switch to page 1, get curr page register
curr_page = inb( E8390_BASE + EN1_CURPAG);
outb(E8390_NODMA+E8390_PAGE0, E8390_BASE + E8390_CMD); // switch to page 0
#ifdef DEBUG_GETFRAME
EdbgOutputDebugString( "curr_page=0x%x\r\n", curr_page);
#endif
// remove one frame from the ring, boundary is always a page behind
this_page = inb( E8390_BASE + EN0_BOUNDARY) + 1;
if (this_page >= 0x80)
this_page = 0x46; // wrap around page register
#ifdef DEBUG_GETFRAME
EdbgOutputDebugString( "this_page=0x%x\r\n", this_page);
#endif
if (this_page != curr_page) // read all the frames
{
// read page aligned packet header
ax88796_read( (unsigned char*) &rx_frame, sizeof(struct e8390_pkt_hdr), this_page<<8);
#ifdef DEBUG_GETFRAME
EdbgOutputDebugString( "8390hdr: status=0x%x, next=0x%x, count=0x%x\r\n", rx_frame.status, rx_frame.next, rx_frame.count);
#endif
*pwLength = rx_frame.count - sizeof(struct e8390_pkt_hdr);
// read packet data
if(*pwLength > rxlen)
EdbgOutputDebugString( "ax88796: bogus packet size %d\r\n", *pwLength);
if( (rx_frame.status & 0x0F) == ENRSR_RXOK)
ax88796_read( pbData, min(*pwLength,rxlen), (this_page<<8) + sizeof(rx_frame));
if( rx_frame.next > 0x80 && rx_frame.next <= 0x46) // set boundary register
rx_frame.next = 0x80;
outb( rx_frame.next-1, E8390_BASE + EN0_BOUNDARY);
#ifdef DEBUG_GETFRAME
EdbgOutputDebugString( "setting boundary to 0x%x\r\n", inb( E8390_BASE + EN0_BOUNDARY));
DumpEtherFrame( pbData, *pwLength);
EdbgOutputDebugString( "ax88796: -eth_rx\r\n");
#endif
}
outb( ENISR_ALL, E8390_BASE + EN0_IMR); // enable interrupts
return *pwLength;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -