Revert to previous version of this driver (1.19) until we figure
[unix-history] / sys / i386 / netboot / ether.c
/**************************************************************************
NETBOOT - BOOTP/TFTP Bootstrap Program
Author: Martin Renters
Date: May/94
This code is based heavily on David Greenman's if_ed.c driver
Copyright (C) 1993-1994, David Greenman, Martin Renters.
This software may be used, modified, copied, distributed, and sold, in
both source and binary form provided that the above copyright and these
terms are retained. Under no circumstances are the authors responsible for
the proper functioning of this software, nor do the authors assume any
responsibility for damages incurred with its use.
**************************************************************************/
#include "netboot.h"
#include "ether.h"
unsigned short eth_nic_base;
unsigned short eth_asic_base;
unsigned char eth_tx_start;
unsigned char eth_laar;
unsigned char eth_flags;
unsigned char eth_vendor;
unsigned char eth_memsize;
unsigned char *eth_bmem;
unsigned char *eth_node_addr;
/**************************************************************************
The following two variables are used externally
**************************************************************************/
char packet[ETH_MAX_PACKET];
int packetlen;
/**************************************************************************
ETH_PROBE - Look for an adapter
**************************************************************************/
eth_probe()
{
int i;
struct wd_board *brd;
char *name;
unsigned short chksum;
unsigned char c;
eth_vendor = VENDOR_NONE;
#ifdef INCLUDE_WD
/******************************************************************
Search for WD/SMC cards
******************************************************************/
for (eth_asic_base = WD_LOW_BASE; eth_asic_base <= WD_HIGH_BASE;
eth_asic_base += 0x20) {
chksum = 0;
for (i=8; i<16; i++)
chksum += inb(i+eth_asic_base);
if ((chksum & 0x00FF) == 0x00FF)
break;
}
if (eth_asic_base <= WD_HIGH_BASE) { /* We've found a board */
eth_vendor = VENDOR_WD;
eth_nic_base = eth_asic_base + WD_NIC_ADDR;
c = inb(eth_asic_base+WD_BID); /* Get board id */
for (brd = wd_boards; brd->name; brd++)
if (brd->id == c) break;
if (!brd->name) {
printf("\r\nUnknown Ethernet type %x\r\n", c);
return(0); /* Unknown type */
}
eth_flags = brd->flags;
eth_memsize = brd->memsize;
eth_tx_start = 0;
if ((c == TYPE_WD8013EP) &&
(inb(eth_asic_base + WD_ICR) & WD_ICR_16BIT)) {
eth_flags = FLAG_16BIT;
eth_memsize = MEM_16384;
}
if ((c & WD_SOFTCONFIG) && (!(eth_flags & FLAG_790))) {
eth_bmem = (char *)(0x80000 |
((inb(eth_asic_base + WD_MSR) & 0x3F) << 13));
} else
eth_bmem = (char *)WD_DEFAULT_MEM;
outb(eth_asic_base + WD_MSR, 0x80); /* Reset */
printf("\r\n%s base 0x%x, memory 0x%X, addr ",
brd->name, eth_asic_base, eth_bmem);
for (i=0; i<6; i++)
printhb((int)(arptable[ARP_CLIENT].node[i] =
inb(i+eth_asic_base+WD_LAR)));
if (eth_flags & FLAG_790) {
outb(eth_asic_base+WD_MSR, WD_MSR_MENB);
outb(eth_asic_base+0x04, (inb(eth_asic_base+0x04) |
0x80));
outb(eth_asic_base+0x0B,
(((unsigned)eth_bmem >> 13) & 0x0F) |
(((unsigned)eth_bmem >> 11) & 0x40) |
(inb(eth_asic_base+0x0B) & 0x0B));
outb(eth_asic_base+0x04, (inb(eth_asic_base+0x04) &
~0x80));
} else {
outb(eth_asic_base+WD_MSR,
(((unsigned)eth_bmem >> 13) & 0x3F) | 0x40);
}
if (eth_flags & FLAG_16BIT) {
if (eth_flags & FLAG_790) {
eth_laar = inb(eth_asic_base + WD_LAAR);
outb(eth_asic_base + WD_LAAR, WD_LAAR_M16EN);
inb(0x84);
} else {
outb(eth_asic_base + WD_LAAR, (eth_laar =
WD_LAAR_M16EN | WD_LAAR_L16EN | 1));
}
}
printf("\r\n");
}
#endif
#ifdef INCLUDE_NE
/******************************************************************
Search for NE1000/2000 if no WD/SMC cards
******************************************************************/
if (eth_vendor != VENDOR_WD) {
char romdata[16], testbuf[32];
char test[] = "NE1000/2000 memory";
eth_bmem = (char *)0; /* No shared memory */
eth_asic_base = NE_BASE + NE_ASIC_OFFSET;
eth_nic_base = NE_BASE;
eth_vendor = VENDOR_NOVELL;
eth_flags = FLAG_PIO;
eth_memsize = MEM_16384;
eth_tx_start = 32;
c = inb(eth_asic_base + NE_RESET);
outb(eth_asic_base + NE_RESET, c);
inb(0x84);
outb(eth_nic_base + D8390_P0_COMMAND, D8390_COMMAND_STP |
D8390_COMMAND_RD2);
outb(eth_nic_base + D8390_P0_RCR, D8390_RCR_MON);
outb(eth_nic_base + D8390_P0_DCR, D8390_DCR_FT1 | D8390_DCR_LS);
outb(eth_nic_base + D8390_P0_PSTART, MEM_8192);
outb(eth_nic_base + D8390_P0_PSTOP, MEM_16384);
eth_pio_write(test, 8192, sizeof(test));
eth_pio_read(8192, testbuf, sizeof(test));
if (!bcompare(test, testbuf, sizeof(test))) {
eth_flags |= FLAG_16BIT;
eth_memsize = MEM_32768;
eth_tx_start = 64;
outb(eth_nic_base + D8390_P0_DCR, D8390_DCR_WTS |
D8390_DCR_FT1 | D8390_DCR_LS);
outb(eth_nic_base + D8390_P0_PSTART, MEM_16384);
outb(eth_nic_base + D8390_P0_PSTOP, MEM_32768);
eth_pio_write(test, 16384, sizeof(test));
eth_pio_read(16384, testbuf, sizeof(test));
if (!bcompare(testbuf, test, sizeof(test))) return(0);
}
eth_pio_read(0, romdata, 16);
printf("\r\nNE1000/NE2000 base 0x%x, addr ", eth_nic_base);
for (i=0; i<6; i++)
printhb((int)(arptable[ARP_CLIENT].node[i] = romdata[i
+ ((eth_flags & FLAG_16BIT) ? i : 0)]));
printf("\r\n");
}
#endif
if (eth_vendor == VENDOR_NONE) return(0);
eth_node_addr = arptable[ARP_CLIENT].node;
eth_reset();
return(eth_vendor);
}
/**************************************************************************
ETH_RESET - Reset adapter
**************************************************************************/
eth_reset()
{
int i;
if (eth_flags & FLAG_790)
outb(eth_nic_base+D8390_P0_COMMAND,
D8390_COMMAND_PS0 | D8390_COMMAND_STP);
else
outb(eth_nic_base+D8390_P0_COMMAND,
D8390_COMMAND_PS0 | D8390_COMMAND_RD2 |
D8390_COMMAND_STP);
if (eth_flags & FLAG_16BIT)
outb(eth_nic_base+D8390_P0_DCR, 0x49);
else
outb(eth_nic_base+D8390_P0_DCR, 0x48);
outb(eth_nic_base+D8390_P0_RBCR0, 0);
outb(eth_nic_base+D8390_P0_RBCR1, 0);
outb(eth_nic_base+D8390_P0_RCR, 4); /* allow broadcast frames */
outb(eth_nic_base+D8390_P0_TCR, 2);
outb(eth_nic_base+D8390_P0_TPSR, eth_tx_start);
outb(eth_nic_base+D8390_P0_PSTART, eth_tx_start + D8390_TXBUF_SIZE);
if (eth_flags & FLAG_790) outb(eth_nic_base + 0x09, 0);
outb(eth_nic_base+D8390_P0_PSTOP, eth_memsize);
outb(eth_nic_base+D8390_P0_BOUND, eth_tx_start + D8390_TXBUF_SIZE);
outb(eth_nic_base+D8390_P0_ISR, 0xFF);
outb(eth_nic_base+D8390_P0_IMR, 0);
if (eth_flags & FLAG_790)
outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS1 |
D8390_COMMAND_STP);
else
outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS1 |
D8390_COMMAND_RD2 | D8390_COMMAND_STP);
for (i=0; i<6; i++)
outb(eth_nic_base+D8390_P1_PAR0+i, eth_node_addr[i]);
for (i=0; i<6; i++)
outb(eth_nic_base+D8390_P1_MAR0+i, 0xFF);
outb(eth_nic_base+D8390_P1_CURR, eth_tx_start + D8390_TXBUF_SIZE+1);
if (eth_flags & FLAG_790)
outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0 |
D8390_COMMAND_STA);
else
outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0 |
D8390_COMMAND_RD2 | D8390_COMMAND_STA);
outb(eth_nic_base+D8390_P0_ISR, 0xFF);
outb(eth_nic_base+D8390_P0_TCR, 0);
return(1);
}
/**************************************************************************
ETH_TRANSMIT - Transmit a frame
**************************************************************************/
eth_transmit(d,t,s,p)
char *d; /* Destination */
unsigned short t; /* Type */
unsigned short s; /* size */
char *p; /* Packet */
{
unsigned char c;
#ifdef INCLUDE_WD
if (eth_vendor == VENDOR_WD) { /* Memory interface */
if (eth_flags & FLAG_16BIT) {
outb(eth_asic_base + WD_LAAR, eth_laar | WD_LAAR_M16EN);
inb(0x84);
}
if (eth_flags & FLAG_790) {
outb(eth_asic_base + WD_MSR, WD_MSR_MENB);
inb(0x84);
}
inb(0x84);
bcopy(d, eth_bmem, 6); /* dst */
bcopy(eth_node_addr, eth_bmem+6, ETHER_ADDR_SIZE); /* src */
*(eth_bmem+12) = t>>8; /* type */
*(eth_bmem+13) = t;
bcopy(p, eth_bmem+14, s);
s += 14;
while (s < ETH_MIN_PACKET) *(eth_bmem+(s++)) = 0;
if (eth_flags & FLAG_790) {
outb(eth_asic_base + WD_MSR, 0);
inb(0x84);
}
if (eth_flags & FLAG_16BIT) {
outb(eth_asic_base + WD_LAAR, eth_laar & ~WD_LAAR_M16EN);
inb(0x84);
}
}
#endif
#ifdef INCLUDE_NE
if (eth_vendor == VENDOR_NOVELL) { /* Programmed I/O */
unsigned short type;
type = (t >> 8) | (t << 8);
eth_pio_write(d, eth_tx_start<<8, 6);
eth_pio_write(eth_node_addr, (eth_tx_start<<8)+6, 6);
eth_pio_write(&type, (eth_tx_start<<8)+12, 2);
eth_pio_write(p, (eth_tx_start<<8)+14, s);
s += 14;
if (s < ETH_MIN_PACKET) s = ETH_MIN_PACKET;
}
#endif
twiddle();
if (eth_flags & FLAG_790)
outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0 |
D8390_COMMAND_STA);
else
outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0 |
D8390_COMMAND_RD2 | D8390_COMMAND_STA);
outb(eth_nic_base+D8390_P0_TPSR, eth_tx_start);
outb(eth_nic_base+D8390_P0_TBCR0, s);
outb(eth_nic_base+D8390_P0_TBCR1, s>>8);
if (eth_flags & FLAG_790)
outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0 |
D8390_COMMAND_TXP | D8390_COMMAND_STA);
else
outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0 |
D8390_COMMAND_TXP | D8390_COMMAND_RD2 |
D8390_COMMAND_STA);
return(0);
}
/**************************************************************************
ETH_POLL - Wait for a frame
**************************************************************************/
eth_poll()
{
int ret = 0;
unsigned short type = 0;
unsigned char bound,curr,rstat;
unsigned short len;
unsigned short pktoff;
unsigned char *p;
struct ringbuffer pkthdr;
rstat = inb(eth_nic_base+D8390_P0_RSR);
if (rstat & D8390_RSTAT_OVER) {
eth_reset();
return(0);
}
if (!(rstat & D8390_RSTAT_PRX)) return(0);
bound = inb(eth_nic_base+D8390_P0_BOUND)+1;
if (bound == eth_memsize) bound = eth_tx_start + D8390_TXBUF_SIZE;
outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS1);
curr = inb(eth_nic_base+D8390_P1_CURR);
outb(eth_nic_base+D8390_P0_COMMAND, D8390_COMMAND_PS0);
if (curr == eth_memsize) curr=eth_tx_start + D8390_TXBUF_SIZE;
if (curr == bound) return(0);
if (eth_vendor == VENDOR_WD) {
if (eth_flags & FLAG_16BIT) {
outb(eth_asic_base + WD_LAAR, eth_laar | WD_LAAR_M16EN);
inb(0x84);
}
if (eth_flags & FLAG_790) {
outb(eth_asic_base + WD_MSR, WD_MSR_MENB);
inb(0x84);
}
inb(0x84);
}
pktoff = (bound << 8);
if (eth_flags & FLAG_PIO)
eth_pio_read(pktoff, &pkthdr, 4);
else
bcopy(eth_bmem + pktoff, &pkthdr, 4);
len = pkthdr.len - 4; /* sub CRC */
pktoff += 4;
if (len > 1514) len = 1514;
bound = pkthdr.bound; /* New bound ptr */
if ( (pkthdr.status & D8390_RSTAT_PRX) && (len > 14) && (len < 1518)) {
p = packet;
packetlen = len;
len = (eth_memsize << 8) - pktoff;
if (packetlen > len) { /* We have a wrap-around */
if (eth_flags & FLAG_PIO)
eth_pio_read(pktoff, p, len);
else
bcopy(eth_bmem + pktoff, p, len);
pktoff = (eth_tx_start + D8390_TXBUF_SIZE) << 8;
p += len;
packetlen -= len;
}
if (eth_flags & FLAG_PIO)
eth_pio_read(pktoff, p, packetlen);
else
bcopy(eth_bmem + pktoff, p, packetlen);
type = (packet[12]<<8) | packet[13];
ret = 1;
}
if (eth_vendor == VENDOR_WD) {
if (eth_flags & FLAG_790) {
outb(eth_asic_base + WD_MSR, 0);
inb(0x84);
}
if (eth_flags & FLAG_16BIT) {
outb(eth_asic_base + WD_LAAR, eth_laar &
~WD_LAAR_M16EN);
inb(0x84);
}
inb(0x84);
}
if (bound == (eth_tx_start + D8390_TXBUF_SIZE))
bound = eth_memsize;
outb(eth_nic_base+D8390_P0_BOUND, bound-1);
if (ret && (type == ARP)) {
struct arprequest *arpreq;
unsigned long reqip;
arpreq = (struct arprequest *)&packet[ETHER_HDR_SIZE];
convert_ipaddr(&reqip, arpreq->tipaddr);
if ((ntohs(arpreq->opcode) == ARP_REQUEST) &&
(reqip == arptable[ARP_CLIENT].ipaddr)) {
arpreq->opcode = htons(ARP_REPLY);
bcopy(arpreq->sipaddr, arpreq->tipaddr, 4);
bcopy(arpreq->shwaddr, arpreq->thwaddr, 6);
bcopy(arptable[ARP_CLIENT].node, arpreq->shwaddr, 6);
convert_ipaddr(arpreq->sipaddr, &reqip);
eth_transmit(arpreq->thwaddr, ARP, sizeof(struct arprequest),
arpreq);
return(0);
}
}
return(ret);
}
#ifdef INCLUDE_NE
/**************************************************************************
NE1000/NE2000 Support Routines
**************************************************************************/
static inline unsigned short inw(unsigned short a)
{
unsigned short d;
asm volatile( "inw %1, %0" : "=a" (d) : "d" (a));
return d;
}
static inline void outw(unsigned short a, unsigned short d)
{
asm volatile( "outw %0, %1" : : "a" (d), "d" (a));
}
/**************************************************************************
ETH_PIO_READ - Read a frame via Programmed I/O
**************************************************************************/
eth_pio_read(src, dst, cnt, init)
unsigned short src;
unsigned char *dst;
unsigned short cnt;
int init;
{
if (cnt & 1) cnt++;
outb(eth_nic_base + D8390_P0_COMMAND, D8390_COMMAND_RD2 |
D8390_COMMAND_STA);
outb(eth_nic_base + D8390_P0_RBCR0, cnt);
outb(eth_nic_base + D8390_P0_RBCR1, cnt>>8);
outb(eth_nic_base + D8390_P0_RSAR0, src);
outb(eth_nic_base + D8390_P0_RSAR1, src>>8);
outb(eth_nic_base + D8390_P0_COMMAND, D8390_COMMAND_RD0 |
D8390_COMMAND_STA);
if (eth_flags & FLAG_16BIT) {
while (cnt) {
*((unsigned short *)dst) = inw(eth_asic_base + NE_DATA);
dst += 2;
cnt -= 2;
}
}
else {
while (cnt--)
*(dst++) = inb(eth_asic_base + NE_DATA);
}
}
/**************************************************************************
ETH_PIO_WRITE - Write a frame via Programmed I/O
**************************************************************************/
eth_pio_write(src, dst, cnt, init)
unsigned char *src;
unsigned short dst;
unsigned short cnt;
int init;
{
outb(eth_nic_base + D8390_P0_COMMAND, D8390_COMMAND_RD2 |
D8390_COMMAND_STA);
outb(eth_nic_base + D8390_P0_ISR, D8390_ISR_RDC);
outb(eth_nic_base + D8390_P0_RBCR0, cnt);
outb(eth_nic_base + D8390_P0_RBCR1, cnt>>8);
outb(eth_nic_base + D8390_P0_RSAR0, dst);
outb(eth_nic_base + D8390_P0_RSAR1, dst>>8);
outb(eth_nic_base + D8390_P0_COMMAND, D8390_COMMAND_RD1 |
D8390_COMMAND_STA);
if (eth_flags & FLAG_16BIT) {
if (cnt & 1) cnt++; /* Round up */
while (cnt) {
outw(eth_asic_base + NE_DATA, *((unsigned short *)src));
src += 2;
cnt -= 2;
}
}
else {
while (cnt--)
outb(eth_asic_base + NE_DATA, *(src++));
}
while((inb(eth_nic_base + D8390_P0_ISR) & D8390_ISR_RDC)
!= D8390_ISR_RDC);
}
#else
/**************************************************************************
ETH_PIO_READ - Dummy routine when NE2000 not compiled in
**************************************************************************/
eth_pio_read() {}
#endif