freebsd_amp_hwpstate/sys/pc98/boot/netboot/ether.c

573 lines
18 KiB
C

/**************************************************************************
NETBOOT - BOOTP/TFTP Bootstrap Program
Author: Martin Renters.
Date: Mar 22 1995
This code is based heavily on David Greenman's if_ed.c driver and
Andres Vega Garcia's if_ep.c driver.
Copyright (C) 1993-1994, David Greenman, Martin Renters.
Copyright (C) 1993-1995, Andres Vega Garcia.
Copyright (C) 1995, Serge Babkin.
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.
3c503 support added by Bill Paul (wpaul@ctr.columbia.edu) on 11/15/94
SMC8416 support added by Bill Paul (wpaul@ctr.columbia.edu) on 12/25/94
3c509 support added by Serge Babkin (babkin@hq.icb.chel.su) on 03/22/95
***************************************************************************/
/* #define EDEBUG */
#include "netboot.h"
#include "ether.h"
extern short aui;
char bnc=0, utp=0; /* for 3C509 */
unsigned short eth_nic_base;
unsigned short eth_asic_base;
unsigned short eth_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_rmem;
unsigned char *eth_node_addr;
/**************************************************************************
The following two variables are used externally
***************************************************************************/
char packet[ETH_MAX_PACKET];
int packetlen;
/*************************************************************************
ETH_FILLNAME - Fill name of adapter in NFS structure
**************************************************************************/
eth_fillname(where)
char *where;
{
switch(eth_vendor) {
case VENDOR_3C509:
where[0]='e'; where[1]='p'; where[2]='0'; where[3]=0;
break;
case VENDOR_WD:
case VENDOR_NOVELL:
case VENDOR_3COM:
where[0]='e'; where[1]='d'; where[2]='0'; where[3]=0;
break;
default:
where[0]='?'; where[1]='?'; where[2]='?'; where[3]=0;
break;
}
}
/**************************************************************************
ETH_PROBE - Look for an adapter
***************************************************************************/
eth_probe()
{
/* common variables */
int i;
#if defined(INCLUDE_EGY) || defined(INCLUDE_LGY) || defined(INCLUDE_ICM) || defined(INCLUDE_SIC)
/* varaibles for 8390 */
struct wd_board *brd;
char *name;
unsigned short chksum;
unsigned char c;
#endif
eth_vendor = VENDOR_NONE;
#if defined(INCLUDE_EGY) || defined(INCLUDE_LGY) || defined(INCLUDE_ICM) || defined(INCLUDE_SIC)
#ifdef INCLUDE_SIC
/******************************************************************
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;
if (brd->id == TYPE_SMC8216T || brd->id == TYPE_SMC8216C) {
(unsigned int) *(eth_bmem + 8192) = (unsigned int)0;
if ((unsigned int) *(eth_bmem + 8192)) {
brd += 2;
eth_memsize = brd->memsize;
}
}
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++) {
printf("%b",(int)(arptable[ARP_CLIENT].node[i] =
inb(i+eth_asic_base+WD_LAR)));
if (i < 5) printf (":");
}
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) & 0xB0));
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 defined(INCLUDE_EGY) || defined(INCLUDE_LGY) || defined(INCLUDE_ICM)
/******************************************************************
Search for EGY/LGY/IF-2766 if no SIC cards
*******************************************************************/
if (eth_vendor == VENDOR_NONE) {
char romdata[16], testbuf[32];
char test[] = "NE1000/2000 memory";
eth_bmem = (char *)0; /* No shared memory */
eth_asic_base = ED_BASE + ED_ASIC_OFFSET;
eth_nic_base = ED_BASE;
eth_vendor = VENDOR_NOVELL;
eth_flags = FLAG_PIO;
eth_memsize = MEM_16384;
eth_tx_start = 32;
c = inb(eth_asic_base + ED_RESET);
outb(eth_asic_base + ED_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++) {
printf("%b",(int)(arptable[ARP_CLIENT].node[i] = romdata[i
+ ((eth_flags & FLAG_16BIT) ? i : 0)]));
if (i < 5) printf (":");
}
printf("\r\n");
}
if (eth_vendor == VENDOR_NONE)
goto no8390;
if (eth_vendor != VENDOR_3COM) eth_rmem = eth_bmem;
eth_node_addr = arptable[ARP_CLIENT].node;
eth_reset();
return(eth_vendor);
#endif /* NE */
no8390:
#endif /*8390 */
return VENDOR_NONE;
}
/**************************************************************************
ETH_RESET - Reset adapter
***************************************************************************/
eth_reset()
{
int s, i;
#if defined(INCLUDE_EGY) || defined(INCLUDE_LGY) || defined(INCLUDE_ICM) || defined(INCLUDE_SIC)
/**************************************************************
Reset cards based on 8390 chip
****************************************************************/
if(eth_vendor!=VENDOR_WD && eth_vendor!=VENDOR_NOVELL
&& eth_vendor!=VENDOR_3COM)
goto no8390;
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);
no8390:
#endif /* 8390 */
}
/**************************************************************************
ETH_TRANSMIT - Transmit a frame
***************************************************************************/
static const char padmap[] = {
0, 3, 2, 1};
eth_transmit(d,t,s,p)
char *d; /* Destination */
unsigned short t; /* Type */
unsigned short s; /* size */
char *p; /* Packet */
{
register u_int len;
int pad;
int status;
unsigned char c;
#if defined(INCLUDE_EGY) || defined(INCLUDE_LGY) || defined(INCLUDE_ICM) || defined(INCLUDE_SIC)
if(eth_vendor!=VENDOR_WD && eth_vendor!=VENDOR_NOVELL
&& eth_vendor!=VENDOR_3COM)
goto no8390;
#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
#if defined(INCLUDE_EGY) || defined(INCLUDE_LGY) || defined(INCLUDE_ICM)
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);
no8390:
#endif /* 8390 */
}
/**************************************************************************
ETH_POLL - Wait for a frame
***************************************************************************/
eth_poll()
{
/* common variables */
unsigned short type = 0;
unsigned short len;
/* variables for 3C509 */
/* variables for 8390 */
#if defined(INCLUDE_EGY) || defined(INCLUDE_LGY) || defined(INCLUDE_ICM) || defined(INCLUDE_SIC)
int ret = 0;
unsigned char bound,curr,rstat;
unsigned short pktoff;
unsigned char *p;
struct ringbuffer pkthdr;
#endif
#if defined(INCLUDE_EGY) || defined(INCLUDE_LGY) || defined(INCLUDE_ICM) || defined(INCLUDE_SIC)
if(eth_vendor!=VENDOR_WD && eth_vendor!=VENDOR_NOVELL
&& eth_vendor!=VENDOR_3COM)
goto no8390;
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_rmem + 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_rmem + 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_rmem + 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);
no8390:
#endif /* 8390 */
}
#ifdef INCLUDE_NE
/**************************************************************************
NE1000/NE2000 Support Routines
***************************************************************************/
/* inw and outw are not needed more - standard version of them is used */
/**************************************************************************
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