|  | @@ -0,0 +1,373 @@
 | 
		
	
		
			
			|  | 1 | +/**************************************************************************
 | 
		
	
		
			
			|  | 2 | + ETHERBOOT -  BOOTP/TFTP Bootstrap Program
 | 
		
	
		
			
			|  | 3 | +
 | 
		
	
		
			
			|  | 4 | + Author: Martin Renters
 | 
		
	
		
			
			|  | 5 | + Date: May/94
 | 
		
	
		
			
			|  | 6 | +
 | 
		
	
		
			
			|  | 7 | + This code is based heavily on David Greenman's if_ed.c driver
 | 
		
	
		
			
			|  | 8 | +
 | 
		
	
		
			
			|  | 9 | + Copyright (C) 1993-1994, David Greenman, Martin Renters.
 | 
		
	
		
			
			|  | 10 | + This software may be used, modified, copied, distributed, and sold, in
 | 
		
	
		
			
			|  | 11 | + both source and binary form provided that the above copyright and these
 | 
		
	
		
			
			|  | 12 | + terms are retained. Under no circumstances are the authors responsible for
 | 
		
	
		
			
			|  | 13 | + the proper functioning of this software, nor do the authors assume any
 | 
		
	
		
			
			|  | 14 | + responsibility for damages incurred with its use.
 | 
		
	
		
			
			|  | 15 | +
 | 
		
	
		
			
			|  | 16 | + Multicast support added by Timothy Legge (timlegge@users.sourceforge.net) 09/28/2003
 | 
		
	
		
			
			|  | 17 | + Relocation support added by Ken Yap (ken_yap@users.sourceforge.net) 28/12/02
 | 
		
	
		
			
			|  | 18 | + Card Detect support adapted from the eCos driver (Christian Plessl <cplessl@ee.ethz.ch>)
 | 
		
	
		
			
			|  | 19 | + Extracted from ns8390.c and adapted by Pantelis Koukousoulas <pktoss@gmail.com>
 | 
		
	
		
			
			|  | 20 | + **************************************************************************/
 | 
		
	
		
			
			|  | 21 | +
 | 
		
	
		
			
			|  | 22 | +#include "ns8390.h"
 | 
		
	
		
			
			|  | 23 | +#include "etherboot.h"
 | 
		
	
		
			
			|  | 24 | +#include "nic.h"
 | 
		
	
		
			
			|  | 25 | +#include <gpxe/ethernet.h>
 | 
		
	
		
			
			|  | 26 | +#include <gpxe/isa.h>
 | 
		
	
		
			
			|  | 27 | +#include <errno.h>
 | 
		
	
		
			
			|  | 28 | +
 | 
		
	
		
			
			|  | 29 | +#define ASIC_PIO NE_DATA
 | 
		
	
		
			
			|  | 30 | +
 | 
		
	
		
			
			|  | 31 | +static unsigned char eth_vendor, eth_flags;
 | 
		
	
		
			
			|  | 32 | +static unsigned short eth_nic_base, eth_asic_base;
 | 
		
	
		
			
			|  | 33 | +static unsigned char eth_memsize, eth_rx_start, eth_tx_start;
 | 
		
	
		
			
			|  | 34 | +static Address eth_bmem, eth_rmem;
 | 
		
	
		
			
			|  | 35 | +static unsigned char eth_drain_receiver;
 | 
		
	
		
			
			|  | 36 | +
 | 
		
	
		
			
			|  | 37 | +static struct nic_operations ne_operations;
 | 
		
	
		
			
			|  | 38 | +static void ne_reset(struct nic *nic, struct isa_device *isa);
 | 
		
	
		
			
			|  | 39 | +
 | 
		
	
		
			
			|  | 40 | +static isa_probe_addr_t ne_probe_addrs[] = { 0x300, 0x280, 0x320, 0x340, 0x380, 0x220, };
 | 
		
	
		
			
			|  | 41 | +
 | 
		
	
		
			
			|  | 42 | +/**************************************************************************
 | 
		
	
		
			
			|  | 43 | + ETH_PIO_READ - Read a frame via Programmed I/O
 | 
		
	
		
			
			|  | 44 | + **************************************************************************/
 | 
		
	
		
			
			|  | 45 | +static void eth_pio_read(unsigned int src, unsigned char *dst, unsigned int cnt) {
 | 
		
	
		
			
			|  | 46 | +	outb(D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
 | 
		
	
		
			
			|  | 47 | +	outb(cnt, eth_nic_base + D8390_P0_RBCR0);
 | 
		
	
		
			
			|  | 48 | +	outb(cnt >> 8, eth_nic_base + D8390_P0_RBCR1);
 | 
		
	
		
			
			|  | 49 | +	outb(src, eth_nic_base + D8390_P0_RSAR0);
 | 
		
	
		
			
			|  | 50 | +	outb(src >> 8, eth_nic_base + D8390_P0_RSAR1);
 | 
		
	
		
			
			|  | 51 | +	outb(D8390_COMMAND_RD0 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
 | 
		
	
		
			
			|  | 52 | +	if (eth_flags & FLAG_16BIT)
 | 
		
	
		
			
			|  | 53 | +		cnt = (cnt + 1) >> 1;
 | 
		
	
		
			
			|  | 54 | +
 | 
		
	
		
			
			|  | 55 | +	while (cnt--) {
 | 
		
	
		
			
			|  | 56 | +		if (eth_flags & FLAG_16BIT) {
 | 
		
	
		
			
			|  | 57 | +			*((unsigned short *) dst) = inw(eth_asic_base + ASIC_PIO);
 | 
		
	
		
			
			|  | 58 | +			dst += 2;
 | 
		
	
		
			
			|  | 59 | +		} else
 | 
		
	
		
			
			|  | 60 | +			*(dst++) = inb(eth_asic_base + ASIC_PIO);
 | 
		
	
		
			
			|  | 61 | +	}
 | 
		
	
		
			
			|  | 62 | +}
 | 
		
	
		
			
			|  | 63 | +
 | 
		
	
		
			
			|  | 64 | +/**************************************************************************
 | 
		
	
		
			
			|  | 65 | + ETH_PIO_WRITE - Write a frame via Programmed I/O
 | 
		
	
		
			
			|  | 66 | + **************************************************************************/
 | 
		
	
		
			
			|  | 67 | +static void eth_pio_write(const unsigned char *src, unsigned int dst,
 | 
		
	
		
			
			|  | 68 | +		unsigned int cnt) {
 | 
		
	
		
			
			|  | 69 | +	outb(D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
 | 
		
	
		
			
			|  | 70 | +	outb(D8390_ISR_RDC, eth_nic_base + D8390_P0_ISR);
 | 
		
	
		
			
			|  | 71 | +	outb(cnt, eth_nic_base + D8390_P0_RBCR0);
 | 
		
	
		
			
			|  | 72 | +	outb(cnt >> 8, eth_nic_base + D8390_P0_RBCR1);
 | 
		
	
		
			
			|  | 73 | +	outb(dst, eth_nic_base + D8390_P0_RSAR0);
 | 
		
	
		
			
			|  | 74 | +	outb(dst >> 8, eth_nic_base + D8390_P0_RSAR1);
 | 
		
	
		
			
			|  | 75 | +	outb(D8390_COMMAND_RD1 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
 | 
		
	
		
			
			|  | 76 | +	if (eth_flags & FLAG_16BIT)
 | 
		
	
		
			
			|  | 77 | +		cnt = (cnt + 1) >> 1;
 | 
		
	
		
			
			|  | 78 | +
 | 
		
	
		
			
			|  | 79 | +	while (cnt--) {
 | 
		
	
		
			
			|  | 80 | +
 | 
		
	
		
			
			|  | 81 | +		if (eth_flags & FLAG_16BIT) {
 | 
		
	
		
			
			|  | 82 | +			outw(*((unsigned short *) src), eth_asic_base + ASIC_PIO);
 | 
		
	
		
			
			|  | 83 | +			src += 2;
 | 
		
	
		
			
			|  | 84 | +		} else
 | 
		
	
		
			
			|  | 85 | +			outb(*(src++), eth_asic_base + ASIC_PIO);
 | 
		
	
		
			
			|  | 86 | +	}
 | 
		
	
		
			
			|  | 87 | +}
 | 
		
	
		
			
			|  | 88 | +
 | 
		
	
		
			
			|  | 89 | +/**************************************************************************
 | 
		
	
		
			
			|  | 90 | + enable_multicast - Enable Multicast
 | 
		
	
		
			
			|  | 91 | + **************************************************************************/
 | 
		
	
		
			
			|  | 92 | +static void enable_multicast(unsigned short eth_nic_base) {
 | 
		
	
		
			
			|  | 93 | +	unsigned char mcfilter[8];
 | 
		
	
		
			
			|  | 94 | +	int i;
 | 
		
	
		
			
			|  | 95 | +
 | 
		
	
		
			
			|  | 96 | +	memset(mcfilter, 0xFF, 8);
 | 
		
	
		
			
			|  | 97 | +	outb(4, eth_nic_base + D8390_P0_RCR);
 | 
		
	
		
			
			|  | 98 | +	outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS1, eth_nic_base + D8390_P0_COMMAND);
 | 
		
	
		
			
			|  | 99 | +	for (i = 0; i < 8; i++) {
 | 
		
	
		
			
			|  | 100 | +		outb(mcfilter[i], eth_nic_base + 8 + i);
 | 
		
	
		
			
			|  | 101 | +		if (inb(eth_nic_base + 8 + i) != mcfilter[i])
 | 
		
	
		
			
			|  | 102 | +			DBG("Error SMC 83C690 Multicast filter read/write mishap %d\n",
 | 
		
	
		
			
			|  | 103 | +					i);
 | 
		
	
		
			
			|  | 104 | +	}
 | 
		
	
		
			
			|  | 105 | +	outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS0, eth_nic_base + D8390_P0_COMMAND);
 | 
		
	
		
			
			|  | 106 | +	outb(4 | 0x08, eth_nic_base + D8390_P0_RCR);
 | 
		
	
		
			
			|  | 107 | +}
 | 
		
	
		
			
			|  | 108 | +
 | 
		
	
		
			
			|  | 109 | +/**************************************************************************
 | 
		
	
		
			
			|  | 110 | + NE_PROBE1 - Look for an adapter on the ISA bus
 | 
		
	
		
			
			|  | 111 | + **************************************************************************/
 | 
		
	
		
			
			|  | 112 | +static int ne_probe1(isa_probe_addr_t ioaddr) {
 | 
		
	
		
			
			|  | 113 | +	//From the eCos driver
 | 
		
	
		
			
			|  | 114 | +	unsigned int regd;
 | 
		
	
		
			
			|  | 115 | +	unsigned int state;
 | 
		
	
		
			
			|  | 116 | +
 | 
		
	
		
			
			|  | 117 | +	state = inb(ioaddr);
 | 
		
	
		
			
			|  | 118 | +	outb(ioaddr, D8390_COMMAND_RD2 | D8390_COMMAND_PS1 | D8390_COMMAND_STP);
 | 
		
	
		
			
			|  | 119 | +	regd = inb(ioaddr + D8390_P0_TCR);
 | 
		
	
		
			
			|  | 120 | +
 | 
		
	
		
			
			|  | 121 | +	if (inb(ioaddr + D8390_P0_TCR)) {
 | 
		
	
		
			
			|  | 122 | +		outb(ioaddr, state);
 | 
		
	
		
			
			|  | 123 | +		outb(ioaddr + 0x0d, regd);
 | 
		
	
		
			
			|  | 124 | +		return 0;
 | 
		
	
		
			
			|  | 125 | +	}
 | 
		
	
		
			
			|  | 126 | +
 | 
		
	
		
			
			|  | 127 | +	return 1;
 | 
		
	
		
			
			|  | 128 | +}
 | 
		
	
		
			
			|  | 129 | +
 | 
		
	
		
			
			|  | 130 | +/**************************************************************************
 | 
		
	
		
			
			|  | 131 | + NE_PROBE - Initialize an adapter ???
 | 
		
	
		
			
			|  | 132 | + **************************************************************************/
 | 
		
	
		
			
			|  | 133 | +static int ne_probe(struct nic *nic, struct isa_device *isa) {
 | 
		
	
		
			
			|  | 134 | +	int i;
 | 
		
	
		
			
			|  | 135 | +	unsigned char c;
 | 
		
	
		
			
			|  | 136 | +	unsigned char romdata[16];
 | 
		
	
		
			
			|  | 137 | +	unsigned char testbuf[32];
 | 
		
	
		
			
			|  | 138 | +
 | 
		
	
		
			
			|  | 139 | +	eth_vendor = VENDOR_NONE;
 | 
		
	
		
			
			|  | 140 | +	eth_drain_receiver = 0;
 | 
		
	
		
			
			|  | 141 | +
 | 
		
	
		
			
			|  | 142 | +	nic->irqno = 0;
 | 
		
	
		
			
			|  | 143 | +	nic->ioaddr = isa->ioaddr;
 | 
		
	
		
			
			|  | 144 | +	eth_nic_base = isa->ioaddr;
 | 
		
	
		
			
			|  | 145 | +
 | 
		
	
		
			
			|  | 146 | +	/******************************************************************
 | 
		
	
		
			
			|  | 147 | +	 Search for NE1000/2000 if no WD/SMC or 3com cards
 | 
		
	
		
			
			|  | 148 | +	 ******************************************************************/
 | 
		
	
		
			
			|  | 149 | +	if (eth_vendor == VENDOR_NONE) {
 | 
		
	
		
			
			|  | 150 | +
 | 
		
	
		
			
			|  | 151 | +		static unsigned char test[] = "NE*000 memory";
 | 
		
	
		
			
			|  | 152 | +
 | 
		
	
		
			
			|  | 153 | +		eth_bmem = 0; /* No shared memory */
 | 
		
	
		
			
			|  | 154 | +
 | 
		
	
		
			
			|  | 155 | +		eth_flags = FLAG_PIO;
 | 
		
	
		
			
			|  | 156 | +		eth_asic_base = eth_nic_base + NE_ASIC_OFFSET;
 | 
		
	
		
			
			|  | 157 | +		eth_memsize = MEM_16384;
 | 
		
	
		
			
			|  | 158 | +		eth_tx_start = 32;
 | 
		
	
		
			
			|  | 159 | +		eth_rx_start = 32 + D8390_TXBUF_SIZE;
 | 
		
	
		
			
			|  | 160 | +		c = inb(eth_asic_base + NE_RESET);
 | 
		
	
		
			
			|  | 161 | +		outb(c, eth_asic_base + NE_RESET);
 | 
		
	
		
			
			|  | 162 | +		(void) inb(0x84);
 | 
		
	
		
			
			|  | 163 | +		outb(D8390_COMMAND_STP | D8390_COMMAND_RD2, eth_nic_base
 | 
		
	
		
			
			|  | 164 | +				+ D8390_P0_COMMAND);
 | 
		
	
		
			
			|  | 165 | +		outb(D8390_RCR_MON, eth_nic_base + D8390_P0_RCR);
 | 
		
	
		
			
			|  | 166 | +		outb(D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base + D8390_P0_DCR);
 | 
		
	
		
			
			|  | 167 | +		outb(MEM_8192, eth_nic_base + D8390_P0_PSTART);
 | 
		
	
		
			
			|  | 168 | +		outb(MEM_16384, eth_nic_base + D8390_P0_PSTOP);
 | 
		
	
		
			
			|  | 169 | +		eth_pio_write((unsigned char *) test, 8192, sizeof(test));
 | 
		
	
		
			
			|  | 170 | +		eth_pio_read(8192, testbuf, sizeof(test));
 | 
		
	
		
			
			|  | 171 | +		if (!memcmp(test, testbuf, sizeof(test)))
 | 
		
	
		
			
			|  | 172 | +			goto out;
 | 
		
	
		
			
			|  | 173 | +		eth_flags |= FLAG_16BIT;
 | 
		
	
		
			
			|  | 174 | +		eth_memsize = MEM_32768;
 | 
		
	
		
			
			|  | 175 | +		eth_tx_start = 64;
 | 
		
	
		
			
			|  | 176 | +		eth_rx_start = 64 + D8390_TXBUF_SIZE;
 | 
		
	
		
			
			|  | 177 | +		outb(D8390_DCR_WTS | D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base
 | 
		
	
		
			
			|  | 178 | +				+ D8390_P0_DCR);
 | 
		
	
		
			
			|  | 179 | +		outb(MEM_16384, eth_nic_base + D8390_P0_PSTART);
 | 
		
	
		
			
			|  | 180 | +		outb(MEM_32768, eth_nic_base + D8390_P0_PSTOP);
 | 
		
	
		
			
			|  | 181 | +		eth_pio_write((unsigned char *) test, 16384, sizeof(test));
 | 
		
	
		
			
			|  | 182 | +		eth_pio_read(16384, testbuf, sizeof(test));
 | 
		
	
		
			
			|  | 183 | +		if (!memcmp(testbuf, test, sizeof(test)))
 | 
		
	
		
			
			|  | 184 | +			goto out;
 | 
		
	
		
			
			|  | 185 | +
 | 
		
	
		
			
			|  | 186 | +
 | 
		
	
		
			
			|  | 187 | +out:
 | 
		
	
		
			
			|  | 188 | +		if (eth_nic_base == 0)
 | 
		
	
		
			
			|  | 189 | +			return (0);
 | 
		
	
		
			
			|  | 190 | +		if (eth_nic_base > ISA_MAX_ADDR) /* PCI probably */
 | 
		
	
		
			
			|  | 191 | +			eth_flags |= FLAG_16BIT;
 | 
		
	
		
			
			|  | 192 | +		eth_vendor = VENDOR_NOVELL;
 | 
		
	
		
			
			|  | 193 | +		eth_pio_read(0, romdata, sizeof(romdata));
 | 
		
	
		
			
			|  | 194 | +		for (i = 0; i < ETH_ALEN; i++) {
 | 
		
	
		
			
			|  | 195 | +			nic->node_addr[i] = romdata[i + ((eth_flags & FLAG_16BIT) ? i : 0)];
 | 
		
	
		
			
			|  | 196 | +		}
 | 
		
	
		
			
			|  | 197 | +		nic->ioaddr = eth_nic_base;
 | 
		
	
		
			
			|  | 198 | +		DBG("\nNE%c000 base %4.4x, MAC Addr %s\n",
 | 
		
	
		
			
			|  | 199 | +				(eth_flags & FLAG_16BIT) ? '2' : '1', eth_nic_base, eth_ntoa(
 | 
		
	
		
			
			|  | 200 | +						nic->node_addr));
 | 
		
	
		
			
			|  | 201 | +	}
 | 
		
	
		
			
			|  | 202 | +
 | 
		
	
		
			
			|  | 203 | +	if (eth_vendor == VENDOR_NONE)
 | 
		
	
		
			
			|  | 204 | +		return (0);
 | 
		
	
		
			
			|  | 205 | +
 | 
		
	
		
			
			|  | 206 | +	if (eth_vendor != VENDOR_3COM)
 | 
		
	
		
			
			|  | 207 | +		eth_rmem = eth_bmem;
 | 
		
	
		
			
			|  | 208 | +
 | 
		
	
		
			
			|  | 209 | +	ne_reset(nic, isa);
 | 
		
	
		
			
			|  | 210 | +	nic->nic_op = &ne_operations;
 | 
		
	
		
			
			|  | 211 | +	return 1;
 | 
		
	
		
			
			|  | 212 | +}
 | 
		
	
		
			
			|  | 213 | +
 | 
		
	
		
			
			|  | 214 | +
 | 
		
	
		
			
			|  | 215 | +/**************************************************************************
 | 
		
	
		
			
			|  | 216 | + NE_DISABLE - Turn off adapter
 | 
		
	
		
			
			|  | 217 | + **************************************************************************/
 | 
		
	
		
			
			|  | 218 | +static void ne_disable(struct nic *nic, struct isa_device *isa) {
 | 
		
	
		
			
			|  | 219 | +	ne_reset(nic, isa);
 | 
		
	
		
			
			|  | 220 | +}
 | 
		
	
		
			
			|  | 221 | +
 | 
		
	
		
			
			|  | 222 | +
 | 
		
	
		
			
			|  | 223 | +/**************************************************************************
 | 
		
	
		
			
			|  | 224 | + NE_RESET - Reset adapter
 | 
		
	
		
			
			|  | 225 | + **************************************************************************/
 | 
		
	
		
			
			|  | 226 | +static void ne_reset(struct nic *nic, struct isa_device *isa __unused)
 | 
		
	
		
			
			|  | 227 | +{
 | 
		
	
		
			
			|  | 228 | +	int i;
 | 
		
	
		
			
			|  | 229 | +
 | 
		
	
		
			
			|  | 230 | +	eth_drain_receiver = 0;
 | 
		
	
		
			
			|  | 231 | +	outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 |
 | 
		
	
		
			
			|  | 232 | +			D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
 | 
		
	
		
			
			|  | 233 | +	if (eth_flags & FLAG_16BIT)
 | 
		
	
		
			
			|  | 234 | +	outb(0x49, eth_nic_base+D8390_P0_DCR);
 | 
		
	
		
			
			|  | 235 | +	else
 | 
		
	
		
			
			|  | 236 | +	outb(0x48, eth_nic_base+D8390_P0_DCR);
 | 
		
	
		
			
			|  | 237 | +	outb(0, eth_nic_base+D8390_P0_RBCR0);
 | 
		
	
		
			
			|  | 238 | +	outb(0, eth_nic_base+D8390_P0_RBCR1);
 | 
		
	
		
			
			|  | 239 | +	outb(0x20, eth_nic_base+D8390_P0_RCR); /* monitor mode */
 | 
		
	
		
			
			|  | 240 | +	outb(2, eth_nic_base+D8390_P0_TCR);
 | 
		
	
		
			
			|  | 241 | +	outb(eth_tx_start, eth_nic_base+D8390_P0_TPSR);
 | 
		
	
		
			
			|  | 242 | +	outb(eth_rx_start, eth_nic_base+D8390_P0_PSTART);
 | 
		
	
		
			
			|  | 243 | +
 | 
		
	
		
			
			|  | 244 | +	outb(eth_memsize, eth_nic_base+D8390_P0_PSTOP);
 | 
		
	
		
			
			|  | 245 | +	outb(eth_memsize - 1, eth_nic_base+D8390_P0_BOUND);
 | 
		
	
		
			
			|  | 246 | +	outb(0xFF, eth_nic_base+D8390_P0_ISR);
 | 
		
	
		
			
			|  | 247 | +	outb(0, eth_nic_base+D8390_P0_IMR);
 | 
		
	
		
			
			|  | 248 | +	outb(D8390_COMMAND_PS1 |
 | 
		
	
		
			
			|  | 249 | +			D8390_COMMAND_RD2 | D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
 | 
		
	
		
			
			|  | 250 | +
 | 
		
	
		
			
			|  | 251 | +	for (i=0; i<ETH_ALEN; i++)
 | 
		
	
		
			
			|  | 252 | +	outb(nic->node_addr[i], eth_nic_base+D8390_P1_PAR0+i);
 | 
		
	
		
			
			|  | 253 | +	for (i=0; i<ETH_ALEN; i++)
 | 
		
	
		
			
			|  | 254 | +	outb(0xFF, eth_nic_base+D8390_P1_MAR0+i);
 | 
		
	
		
			
			|  | 255 | +	outb(eth_rx_start, eth_nic_base+D8390_P1_CURR);
 | 
		
	
		
			
			|  | 256 | +	outb(D8390_COMMAND_PS0 |
 | 
		
	
		
			
			|  | 257 | +			D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
 | 
		
	
		
			
			|  | 258 | +	outb(0xFF, eth_nic_base+D8390_P0_ISR);
 | 
		
	
		
			
			|  | 259 | +	outb(0, eth_nic_base+D8390_P0_TCR); /* transmitter on */
 | 
		
	
		
			
			|  | 260 | +	outb(4, eth_nic_base+D8390_P0_RCR); /* allow rx broadcast frames */
 | 
		
	
		
			
			|  | 261 | +
 | 
		
	
		
			
			|  | 262 | +	enable_multicast(eth_nic_base);
 | 
		
	
		
			
			|  | 263 | +}
 | 
		
	
		
			
			|  | 264 | +
 | 
		
	
		
			
			|  | 265 | +
 | 
		
	
		
			
			|  | 266 | +/**************************************************************************
 | 
		
	
		
			
			|  | 267 | + NE_POLL - Wait for a frame
 | 
		
	
		
			
			|  | 268 | + **************************************************************************/
 | 
		
	
		
			
			|  | 269 | +static int ne_poll(struct nic *nic __unused, int retrieve __unused)
 | 
		
	
		
			
			|  | 270 | +{
 | 
		
	
		
			
			|  | 271 | +	int ret = 0;
 | 
		
	
		
			
			|  | 272 | +	unsigned char rstat, curr, next;
 | 
		
	
		
			
			|  | 273 | +	unsigned short len, frag;
 | 
		
	
		
			
			|  | 274 | +	unsigned short pktoff;
 | 
		
	
		
			
			|  | 275 | +	unsigned char *p;
 | 
		
	
		
			
			|  | 276 | +	struct ringbuffer pkthdr;
 | 
		
	
		
			
			|  | 277 | +
 | 
		
	
		
			
			|  | 278 | +	rstat = inb(eth_nic_base+D8390_P0_RSR);
 | 
		
	
		
			
			|  | 279 | +	if (!(rstat & D8390_RSTAT_PRX)) return(0);
 | 
		
	
		
			
			|  | 280 | +	next = inb(eth_nic_base+D8390_P0_BOUND)+1;
 | 
		
	
		
			
			|  | 281 | +	if (next >= eth_memsize) next = eth_rx_start;
 | 
		
	
		
			
			|  | 282 | +	outb(D8390_COMMAND_PS1, eth_nic_base+D8390_P0_COMMAND);
 | 
		
	
		
			
			|  | 283 | +	curr = inb(eth_nic_base+D8390_P1_CURR);
 | 
		
	
		
			
			|  | 284 | +	outb(D8390_COMMAND_PS0, eth_nic_base+D8390_P0_COMMAND);
 | 
		
	
		
			
			|  | 285 | +	if (curr >= eth_memsize) curr=eth_rx_start;
 | 
		
	
		
			
			|  | 286 | +	if (curr == next) return(0);
 | 
		
	
		
			
			|  | 287 | +
 | 
		
	
		
			
			|  | 288 | +	if ( ! retrieve ) return 1;
 | 
		
	
		
			
			|  | 289 | +
 | 
		
	
		
			
			|  | 290 | +	pktoff = next << 8;
 | 
		
	
		
			
			|  | 291 | +	if (eth_flags & FLAG_PIO)
 | 
		
	
		
			
			|  | 292 | +	eth_pio_read(pktoff, (unsigned char *)&pkthdr, 4);
 | 
		
	
		
			
			|  | 293 | +	else
 | 
		
	
		
			
			|  | 294 | +	memcpy(&pkthdr, bus_to_virt(eth_rmem + pktoff), 4);
 | 
		
	
		
			
			|  | 295 | +	pktoff += sizeof(pkthdr);
 | 
		
	
		
			
			|  | 296 | +	/* incoming length includes FCS so must sub 4 */
 | 
		
	
		
			
			|  | 297 | +	len = pkthdr.len - 4;
 | 
		
	
		
			
			|  | 298 | +	if ((pkthdr.status & D8390_RSTAT_PRX) == 0 || len < ETH_ZLEN
 | 
		
	
		
			
			|  | 299 | +			|| len> ETH_FRAME_LEN) {
 | 
		
	
		
			
			|  | 300 | +		DBG("Bogus packet, ignoring\n");
 | 
		
	
		
			
			|  | 301 | +		return (0);
 | 
		
	
		
			
			|  | 302 | +	}
 | 
		
	
		
			
			|  | 303 | +	else {
 | 
		
	
		
			
			|  | 304 | +		p = nic->packet;
 | 
		
	
		
			
			|  | 305 | +		nic->packetlen = len; /* available to caller */
 | 
		
	
		
			
			|  | 306 | +		frag = (eth_memsize << 8) - pktoff;
 | 
		
	
		
			
			|  | 307 | +		if (len> frag) { /* We have a wrap-around */
 | 
		
	
		
			
			|  | 308 | +			/* read first part */
 | 
		
	
		
			
			|  | 309 | +			if (eth_flags & FLAG_PIO)
 | 
		
	
		
			
			|  | 310 | +			eth_pio_read(pktoff, p, frag);
 | 
		
	
		
			
			|  | 311 | +			else
 | 
		
	
		
			
			|  | 312 | +			memcpy(p, bus_to_virt(eth_rmem + pktoff), frag);
 | 
		
	
		
			
			|  | 313 | +			pktoff = eth_rx_start << 8;
 | 
		
	
		
			
			|  | 314 | +			p += frag;
 | 
		
	
		
			
			|  | 315 | +			len -= frag;
 | 
		
	
		
			
			|  | 316 | +		}
 | 
		
	
		
			
			|  | 317 | +		/* read second part */
 | 
		
	
		
			
			|  | 318 | +		if (eth_flags & FLAG_PIO)
 | 
		
	
		
			
			|  | 319 | +		eth_pio_read(pktoff, p, len);
 | 
		
	
		
			
			|  | 320 | +		else
 | 
		
	
		
			
			|  | 321 | +		memcpy(p, bus_to_virt(eth_rmem + pktoff), len);
 | 
		
	
		
			
			|  | 322 | +		ret = 1;
 | 
		
	
		
			
			|  | 323 | +	}
 | 
		
	
		
			
			|  | 324 | +	next = pkthdr.next; /* frame number of next packet */
 | 
		
	
		
			
			|  | 325 | +	if (next == eth_rx_start)
 | 
		
	
		
			
			|  | 326 | +	next = eth_memsize;
 | 
		
	
		
			
			|  | 327 | +	outb(next-1, eth_nic_base+D8390_P0_BOUND);
 | 
		
	
		
			
			|  | 328 | +	return(ret);
 | 
		
	
		
			
			|  | 329 | +}
 | 
		
	
		
			
			|  | 330 | +
 | 
		
	
		
			
			|  | 331 | +
 | 
		
	
		
			
			|  | 332 | +/**************************************************************************
 | 
		
	
		
			
			|  | 333 | + NE_TRANSMIT - Transmit a frame
 | 
		
	
		
			
			|  | 334 | + **************************************************************************/
 | 
		
	
		
			
			|  | 335 | +static void ne_transmit(struct nic *nic, const char *d, /* Destination */
 | 
		
	
		
			
			|  | 336 | +unsigned int t, /* Type */
 | 
		
	
		
			
			|  | 337 | +unsigned int s, /* size */
 | 
		
	
		
			
			|  | 338 | +const char *p) { /* Packet */
 | 
		
	
		
			
			|  | 339 | +
 | 
		
	
		
			
			|  | 340 | +	/* Programmed I/O */
 | 
		
	
		
			
			|  | 341 | +	unsigned short type;
 | 
		
	
		
			
			|  | 342 | +	type = (t >> 8) | (t << 8);
 | 
		
	
		
			
			|  | 343 | +	eth_pio_write((unsigned char *) d, eth_tx_start << 8, ETH_ALEN);
 | 
		
	
		
			
			|  | 344 | +	eth_pio_write(nic->node_addr, (eth_tx_start << 8) + ETH_ALEN, ETH_ALEN);
 | 
		
	
		
			
			|  | 345 | +	/* bcc generates worse code without (const+const) below */
 | 
		
	
		
			
			|  | 346 | +	eth_pio_write((unsigned char *) &type, (eth_tx_start << 8) + (ETH_ALEN
 | 
		
	
		
			
			|  | 347 | +			+ ETH_ALEN), 2);
 | 
		
	
		
			
			|  | 348 | +	eth_pio_write((unsigned char *) p, (eth_tx_start << 8) + ETH_HLEN, s);
 | 
		
	
		
			
			|  | 349 | +	s += ETH_HLEN;
 | 
		
	
		
			
			|  | 350 | +	if (s < ETH_ZLEN)
 | 
		
	
		
			
			|  | 351 | +		s = ETH_ZLEN;
 | 
		
	
		
			
			|  | 352 | +
 | 
		
	
		
			
			|  | 353 | +	outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 | D8390_COMMAND_STA,
 | 
		
	
		
			
			|  | 354 | +			eth_nic_base + D8390_P0_COMMAND);
 | 
		
	
		
			
			|  | 355 | +	outb(eth_tx_start, eth_nic_base + D8390_P0_TPSR);
 | 
		
	
		
			
			|  | 356 | +	outb(s, eth_nic_base + D8390_P0_TBCR0);
 | 
		
	
		
			
			|  | 357 | +	outb(s >> 8, eth_nic_base + D8390_P0_TBCR1);
 | 
		
	
		
			
			|  | 358 | +
 | 
		
	
		
			
			|  | 359 | +	outb(D8390_COMMAND_PS0 | D8390_COMMAND_TXP | D8390_COMMAND_RD2
 | 
		
	
		
			
			|  | 360 | +			| D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
 | 
		
	
		
			
			|  | 361 | +}
 | 
		
	
		
			
			|  | 362 | +
 | 
		
	
		
			
			|  | 363 | +static struct nic_operations ne_operations = { .connect = dummy_connect,
 | 
		
	
		
			
			|  | 364 | +		.poll = ne_poll, .transmit = ne_transmit, .irq = dummy_irq,
 | 
		
	
		
			
			|  | 365 | +};
 | 
		
	
		
			
			|  | 366 | +
 | 
		
	
		
			
			|  | 367 | +ISA_DRIVER ( ne_driver, ne_probe_addrs, ne_probe1,
 | 
		
	
		
			
			|  | 368 | +		GENERIC_ISAPNP_VENDOR, 0x0600 );
 | 
		
	
		
			
			|  | 369 | +
 | 
		
	
		
			
			|  | 370 | +DRIVER ( "ne", nic_driver, isapnp_driver, ne_driver,
 | 
		
	
		
			
			|  | 371 | +		ne_probe, ne_disable );
 | 
		
	
		
			
			|  | 372 | +
 | 
		
	
		
			
			|  | 373 | +ISA_ROM("ne","NE1000/2000 and clones");
 |