mirror of https://github.com/ipxe/ipxe.git
376 lines
12 KiB
C
376 lines
12 KiB
C
/**************************************************************************
|
|
ETHERBOOT - 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.
|
|
|
|
Multicast support added by Timothy Legge (timlegge@users.sourceforge.net) 09/28/2003
|
|
Relocation support added by Ken Yap (ken_yap@users.sourceforge.net) 28/12/02
|
|
Card Detect support adapted from the eCos driver (Christian Plessl <cplessl@ee.ethz.ch>)
|
|
Extracted from ns8390.c and adapted by Pantelis Koukousoulas <pktoss@gmail.com>
|
|
**************************************************************************/
|
|
|
|
FILE_LICENCE ( BSD2 );
|
|
|
|
#include "ns8390.h"
|
|
#include "etherboot.h"
|
|
#include "nic.h"
|
|
#include <ipxe/ethernet.h>
|
|
#include <ipxe/isa.h>
|
|
#include <errno.h>
|
|
|
|
#define ASIC_PIO NE_DATA
|
|
|
|
static unsigned char eth_vendor, eth_flags;
|
|
static unsigned short eth_nic_base, eth_asic_base;
|
|
static unsigned char eth_memsize, eth_rx_start, eth_tx_start;
|
|
static Address eth_bmem, eth_rmem;
|
|
static unsigned char eth_drain_receiver;
|
|
|
|
static struct nic_operations ne_operations;
|
|
static void ne_reset(struct nic *nic, struct isa_device *isa);
|
|
|
|
static isa_probe_addr_t ne_probe_addrs[] = { 0x300, 0x280, 0x320, 0x340, 0x380, 0x220, };
|
|
|
|
/**************************************************************************
|
|
ETH_PIO_READ - Read a frame via Programmed I/O
|
|
**************************************************************************/
|
|
static void eth_pio_read(unsigned int src, unsigned char *dst, unsigned int cnt) {
|
|
outb(D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
|
|
outb(cnt, eth_nic_base + D8390_P0_RBCR0);
|
|
outb(cnt >> 8, eth_nic_base + D8390_P0_RBCR1);
|
|
outb(src, eth_nic_base + D8390_P0_RSAR0);
|
|
outb(src >> 8, eth_nic_base + D8390_P0_RSAR1);
|
|
outb(D8390_COMMAND_RD0 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
|
|
if (eth_flags & FLAG_16BIT)
|
|
cnt = (cnt + 1) >> 1;
|
|
|
|
while (cnt--) {
|
|
if (eth_flags & FLAG_16BIT) {
|
|
*((unsigned short *) dst) = inw(eth_asic_base + ASIC_PIO);
|
|
dst += 2;
|
|
} else
|
|
*(dst++) = inb(eth_asic_base + ASIC_PIO);
|
|
}
|
|
}
|
|
|
|
/**************************************************************************
|
|
ETH_PIO_WRITE - Write a frame via Programmed I/O
|
|
**************************************************************************/
|
|
static void eth_pio_write(const unsigned char *src, unsigned int dst,
|
|
unsigned int cnt) {
|
|
outb(D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
|
|
outb(D8390_ISR_RDC, eth_nic_base + D8390_P0_ISR);
|
|
outb(cnt, eth_nic_base + D8390_P0_RBCR0);
|
|
outb(cnt >> 8, eth_nic_base + D8390_P0_RBCR1);
|
|
outb(dst, eth_nic_base + D8390_P0_RSAR0);
|
|
outb(dst >> 8, eth_nic_base + D8390_P0_RSAR1);
|
|
outb(D8390_COMMAND_RD1 | D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
|
|
if (eth_flags & FLAG_16BIT)
|
|
cnt = (cnt + 1) >> 1;
|
|
|
|
while (cnt--) {
|
|
|
|
if (eth_flags & FLAG_16BIT) {
|
|
outw(*((unsigned short *) src), eth_asic_base + ASIC_PIO);
|
|
src += 2;
|
|
} else
|
|
outb(*(src++), eth_asic_base + ASIC_PIO);
|
|
}
|
|
}
|
|
|
|
/**************************************************************************
|
|
enable_multicast - Enable Multicast
|
|
**************************************************************************/
|
|
static void enable_multicast(unsigned short eth_nic_base) {
|
|
unsigned char mcfilter[8];
|
|
int i;
|
|
|
|
memset(mcfilter, 0xFF, 8);
|
|
outb(4, eth_nic_base + D8390_P0_RCR);
|
|
outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS1, eth_nic_base + D8390_P0_COMMAND);
|
|
for (i = 0; i < 8; i++) {
|
|
outb(mcfilter[i], eth_nic_base + 8 + i);
|
|
if (inb(eth_nic_base + 8 + i) != mcfilter[i])
|
|
DBG("Error SMC 83C690 Multicast filter read/write mishap %d\n",
|
|
i);
|
|
}
|
|
outb(D8390_COMMAND_RD2 + D8390_COMMAND_PS0, eth_nic_base + D8390_P0_COMMAND);
|
|
outb(4 | 0x08, eth_nic_base + D8390_P0_RCR);
|
|
}
|
|
|
|
/**************************************************************************
|
|
NE_PROBE1 - Look for an adapter on the ISA bus
|
|
**************************************************************************/
|
|
static int ne_probe1(isa_probe_addr_t ioaddr) {
|
|
//From the eCos driver
|
|
unsigned int regd;
|
|
unsigned int state;
|
|
|
|
state = inb(ioaddr);
|
|
outb(ioaddr, D8390_COMMAND_RD2 | D8390_COMMAND_PS1 | D8390_COMMAND_STP);
|
|
regd = inb(ioaddr + D8390_P0_TCR);
|
|
|
|
if (inb(ioaddr + D8390_P0_TCR)) {
|
|
outb(ioaddr, state);
|
|
outb(ioaddr + 0x0d, regd);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**************************************************************************
|
|
NE_PROBE - Initialize an adapter ???
|
|
**************************************************************************/
|
|
static int ne_probe(struct nic *nic, struct isa_device *isa) {
|
|
int i;
|
|
unsigned char c;
|
|
unsigned char romdata[16];
|
|
unsigned char testbuf[32];
|
|
|
|
eth_vendor = VENDOR_NONE;
|
|
eth_drain_receiver = 0;
|
|
|
|
nic->irqno = 0;
|
|
nic->ioaddr = isa->ioaddr;
|
|
eth_nic_base = isa->ioaddr;
|
|
|
|
/******************************************************************
|
|
Search for NE1000/2000 if no WD/SMC or 3com cards
|
|
******************************************************************/
|
|
if (eth_vendor == VENDOR_NONE) {
|
|
|
|
static unsigned char test[] = "NE*000 memory";
|
|
|
|
eth_bmem = 0; /* No shared memory */
|
|
|
|
eth_flags = FLAG_PIO;
|
|
eth_asic_base = eth_nic_base + NE_ASIC_OFFSET;
|
|
eth_memsize = MEM_16384;
|
|
eth_tx_start = 32;
|
|
eth_rx_start = 32 + D8390_TXBUF_SIZE;
|
|
c = inb(eth_asic_base + NE_RESET);
|
|
outb(c, eth_asic_base + NE_RESET);
|
|
(void) inb(0x84);
|
|
outb(D8390_COMMAND_STP | D8390_COMMAND_RD2, eth_nic_base
|
|
+ D8390_P0_COMMAND);
|
|
outb(D8390_RCR_MON, eth_nic_base + D8390_P0_RCR);
|
|
outb(D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base + D8390_P0_DCR);
|
|
outb(MEM_8192, eth_nic_base + D8390_P0_PSTART);
|
|
outb(MEM_16384, eth_nic_base + D8390_P0_PSTOP);
|
|
eth_pio_write((unsigned char *) test, 8192, sizeof(test));
|
|
eth_pio_read(8192, testbuf, sizeof(test));
|
|
if (!memcmp(test, testbuf, sizeof(test)))
|
|
goto out;
|
|
eth_flags |= FLAG_16BIT;
|
|
eth_memsize = MEM_32768;
|
|
eth_tx_start = 64;
|
|
eth_rx_start = 64 + D8390_TXBUF_SIZE;
|
|
outb(D8390_DCR_WTS | D8390_DCR_FT1 | D8390_DCR_LS, eth_nic_base
|
|
+ D8390_P0_DCR);
|
|
outb(MEM_16384, eth_nic_base + D8390_P0_PSTART);
|
|
outb(MEM_32768, eth_nic_base + D8390_P0_PSTOP);
|
|
eth_pio_write((unsigned char *) test, 16384, sizeof(test));
|
|
eth_pio_read(16384, testbuf, sizeof(test));
|
|
if (!memcmp(testbuf, test, sizeof(test)))
|
|
goto out;
|
|
|
|
|
|
out:
|
|
if (eth_nic_base == 0)
|
|
return (0);
|
|
if (eth_nic_base > ISA_MAX_ADDR) /* PCI probably */
|
|
eth_flags |= FLAG_16BIT;
|
|
eth_vendor = VENDOR_NOVELL;
|
|
eth_pio_read(0, romdata, sizeof(romdata));
|
|
for (i = 0; i < ETH_ALEN; i++) {
|
|
nic->node_addr[i] = romdata[i + ((eth_flags & FLAG_16BIT) ? i : 0)];
|
|
}
|
|
nic->ioaddr = eth_nic_base;
|
|
DBG("\nNE%c000 base %4.4x, MAC Addr %s\n",
|
|
(eth_flags & FLAG_16BIT) ? '2' : '1', eth_nic_base, eth_ntoa(
|
|
nic->node_addr));
|
|
}
|
|
|
|
if (eth_vendor == VENDOR_NONE)
|
|
return (0);
|
|
|
|
if (eth_vendor != VENDOR_3COM)
|
|
eth_rmem = eth_bmem;
|
|
|
|
ne_reset(nic, isa);
|
|
nic->nic_op = &ne_operations;
|
|
return 1;
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
NE_DISABLE - Turn off adapter
|
|
**************************************************************************/
|
|
static void ne_disable(struct nic *nic, struct isa_device *isa) {
|
|
ne_reset(nic, isa);
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
NE_RESET - Reset adapter
|
|
**************************************************************************/
|
|
static void ne_reset(struct nic *nic, struct isa_device *isa __unused)
|
|
{
|
|
int i;
|
|
|
|
eth_drain_receiver = 0;
|
|
outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 |
|
|
D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
|
|
if (eth_flags & FLAG_16BIT)
|
|
outb(0x49, eth_nic_base+D8390_P0_DCR);
|
|
else
|
|
outb(0x48, eth_nic_base+D8390_P0_DCR);
|
|
outb(0, eth_nic_base+D8390_P0_RBCR0);
|
|
outb(0, eth_nic_base+D8390_P0_RBCR1);
|
|
outb(0x20, eth_nic_base+D8390_P0_RCR); /* monitor mode */
|
|
outb(2, eth_nic_base+D8390_P0_TCR);
|
|
outb(eth_tx_start, eth_nic_base+D8390_P0_TPSR);
|
|
outb(eth_rx_start, eth_nic_base+D8390_P0_PSTART);
|
|
|
|
outb(eth_memsize, eth_nic_base+D8390_P0_PSTOP);
|
|
outb(eth_memsize - 1, eth_nic_base+D8390_P0_BOUND);
|
|
outb(0xFF, eth_nic_base+D8390_P0_ISR);
|
|
outb(0, eth_nic_base+D8390_P0_IMR);
|
|
outb(D8390_COMMAND_PS1 |
|
|
D8390_COMMAND_RD2 | D8390_COMMAND_STP, eth_nic_base+D8390_P0_COMMAND);
|
|
|
|
for (i=0; i<ETH_ALEN; i++)
|
|
outb(nic->node_addr[i], eth_nic_base+D8390_P1_PAR0+i);
|
|
for (i=0; i<ETH_ALEN; i++)
|
|
outb(0xFF, eth_nic_base+D8390_P1_MAR0+i);
|
|
outb(eth_rx_start, eth_nic_base+D8390_P1_CURR);
|
|
outb(D8390_COMMAND_PS0 |
|
|
D8390_COMMAND_RD2 | D8390_COMMAND_STA, eth_nic_base+D8390_P0_COMMAND);
|
|
outb(0xFF, eth_nic_base+D8390_P0_ISR);
|
|
outb(0, eth_nic_base+D8390_P0_TCR); /* transmitter on */
|
|
outb(4, eth_nic_base+D8390_P0_RCR); /* allow rx broadcast frames */
|
|
|
|
enable_multicast(eth_nic_base);
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
NE_POLL - Wait for a frame
|
|
**************************************************************************/
|
|
static int ne_poll(struct nic *nic __unused, int retrieve __unused)
|
|
{
|
|
int ret = 0;
|
|
unsigned char rstat, curr, next;
|
|
unsigned short len, frag;
|
|
unsigned short pktoff;
|
|
unsigned char *p;
|
|
struct ringbuffer pkthdr;
|
|
|
|
rstat = inb(eth_nic_base+D8390_P0_RSR);
|
|
if (!(rstat & D8390_RSTAT_PRX)) return(0);
|
|
next = inb(eth_nic_base+D8390_P0_BOUND)+1;
|
|
if (next >= eth_memsize) next = eth_rx_start;
|
|
outb(D8390_COMMAND_PS1, eth_nic_base+D8390_P0_COMMAND);
|
|
curr = inb(eth_nic_base+D8390_P1_CURR);
|
|
outb(D8390_COMMAND_PS0, eth_nic_base+D8390_P0_COMMAND);
|
|
if (curr >= eth_memsize) curr=eth_rx_start;
|
|
if (curr == next) return(0);
|
|
|
|
if ( ! retrieve ) return 1;
|
|
|
|
pktoff = next << 8;
|
|
if (eth_flags & FLAG_PIO)
|
|
eth_pio_read(pktoff, (unsigned char *)&pkthdr, 4);
|
|
else
|
|
memcpy(&pkthdr, bus_to_virt(eth_rmem + pktoff), 4);
|
|
pktoff += sizeof(pkthdr);
|
|
/* incoming length includes FCS so must sub 4 */
|
|
len = pkthdr.len - 4;
|
|
if ((pkthdr.status & D8390_RSTAT_PRX) == 0 || len < ETH_ZLEN
|
|
|| len> ETH_FRAME_LEN) {
|
|
DBG("Bogus packet, ignoring\n");
|
|
return (0);
|
|
}
|
|
else {
|
|
p = nic->packet;
|
|
nic->packetlen = len; /* available to caller */
|
|
frag = (eth_memsize << 8) - pktoff;
|
|
if (len> frag) { /* We have a wrap-around */
|
|
/* read first part */
|
|
if (eth_flags & FLAG_PIO)
|
|
eth_pio_read(pktoff, p, frag);
|
|
else
|
|
memcpy(p, bus_to_virt(eth_rmem + pktoff), frag);
|
|
pktoff = eth_rx_start << 8;
|
|
p += frag;
|
|
len -= frag;
|
|
}
|
|
/* read second part */
|
|
if (eth_flags & FLAG_PIO)
|
|
eth_pio_read(pktoff, p, len);
|
|
else
|
|
memcpy(p, bus_to_virt(eth_rmem + pktoff), len);
|
|
ret = 1;
|
|
}
|
|
next = pkthdr.next; /* frame number of next packet */
|
|
if (next == eth_rx_start)
|
|
next = eth_memsize;
|
|
outb(next-1, eth_nic_base+D8390_P0_BOUND);
|
|
return(ret);
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
NE_TRANSMIT - Transmit a frame
|
|
**************************************************************************/
|
|
static void ne_transmit(struct nic *nic, const char *d, /* Destination */
|
|
unsigned int t, /* Type */
|
|
unsigned int s, /* size */
|
|
const char *p) { /* Packet */
|
|
|
|
/* Programmed I/O */
|
|
unsigned short type;
|
|
type = (t >> 8) | (t << 8);
|
|
eth_pio_write((unsigned char *) d, eth_tx_start << 8, ETH_ALEN);
|
|
eth_pio_write(nic->node_addr, (eth_tx_start << 8) + ETH_ALEN, ETH_ALEN);
|
|
/* bcc generates worse code without (const+const) below */
|
|
eth_pio_write((unsigned char *) &type, (eth_tx_start << 8) + (ETH_ALEN
|
|
+ ETH_ALEN), 2);
|
|
eth_pio_write((unsigned char *) p, (eth_tx_start << 8) + ETH_HLEN, s);
|
|
s += ETH_HLEN;
|
|
if (s < ETH_ZLEN)
|
|
s = ETH_ZLEN;
|
|
|
|
outb(D8390_COMMAND_PS0 | D8390_COMMAND_RD2 | D8390_COMMAND_STA,
|
|
eth_nic_base + D8390_P0_COMMAND);
|
|
outb(eth_tx_start, eth_nic_base + D8390_P0_TPSR);
|
|
outb(s, eth_nic_base + D8390_P0_TBCR0);
|
|
outb(s >> 8, eth_nic_base + D8390_P0_TBCR1);
|
|
|
|
outb(D8390_COMMAND_PS0 | D8390_COMMAND_TXP | D8390_COMMAND_RD2
|
|
| D8390_COMMAND_STA, eth_nic_base + D8390_P0_COMMAND);
|
|
}
|
|
|
|
static struct nic_operations ne_operations = { .connect = dummy_connect,
|
|
.poll = ne_poll, .transmit = ne_transmit, .irq = dummy_irq,
|
|
};
|
|
|
|
ISA_DRIVER ( ne_driver, ne_probe_addrs, ne_probe1,
|
|
GENERIC_ISAPNP_VENDOR, 0x0600 );
|
|
|
|
DRIVER ( "ne", nic_driver, isapnp_driver, ne_driver,
|
|
ne_probe, ne_disable );
|
|
|
|
ISA_ROM("ne","NE1000/2000 and clones");
|