/************************************************************************** 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