Convert to use <machine/bus.h>, plus 2 bug fixes:

- In egstart(), if the Send Packet command fails, m_freem()
	  the mbuf chain before dequeueing another one (memory leak).

	- In egintr(), return 1 if a known interrupt was processed.
	  Would manifest itself as spurious interrupts.
This commit is contained in:
thorpej 1996-08-03 02:12:46 +00:00
parent af6e1b10a8
commit c937985d60
1 changed files with 185 additions and 97 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: if_eg.c,v 1.26 1996/05/12 23:52:27 mycroft Exp $ */
/* $NetBSD: if_eg.c,v 1.27 1996/08/03 02:12:46 thorpej Exp $ */
/*
* Copyright (c) 1993 Dean Huxley <dean@fsa.ca>
@ -36,6 +36,7 @@
/* To do:
* - multicast
* - promiscuous
* - get rid of isa indirect stuff
*/
#include "bpfilter.h"
@ -75,7 +76,7 @@
#include <machine/cpu.h>
#include <machine/intr.h>
#include <machine/pio.h>
#include <machine/bus.h>
#include <dev/isa/isavar.h>
#include <dev/isa/if_egreg.h>
@ -102,17 +103,15 @@ struct eg_softc {
struct device sc_dev;
void *sc_ih;
struct arpcom sc_arpcom; /* Ethernet common part */
int eg_cmd; /* Command register R/W */
int eg_ctl; /* Control register R/W (EG_CTL_*) */
int eg_stat; /* Status register R/O (EG_STAT_*) */
int eg_data; /* Data register R/W (16 bits) */
u_char eg_rom_major; /* Cards ROM version (major number) */
u_char eg_rom_minor; /* Cards ROM version (minor number) */
short eg_ram; /* Amount of RAM on the card */
u_char eg_pcb[64]; /* Primary Command Block buffer */
u_char eg_incount; /* Number of buffers currently used */
u_char *eg_inbuf; /* Incoming packet buffer */
u_char *eg_outbuf; /* Outgoing packet buffer */
bus_chipset_tag_t sc_bc; /* bus chipset identifier */
bus_io_handle_t sc_ioh; /* i/o handle */
u_int8_t eg_rom_major; /* Cards ROM version (major number) */
u_int8_t eg_rom_minor; /* Cards ROM version (minor number) */
short eg_ram; /* Amount of RAM on the card */
u_int8_t eg_pcb[64]; /* Primary Command Block buffer */
u_int8_t eg_incount; /* Number of buffers currently used */
caddr_t eg_inbuf; /* Incoming packet buffer */
caddr_t eg_outbuf; /* Outgoing packet buffer */
};
int egprobe __P((struct device *, void *, void *));
@ -169,13 +168,15 @@ egprintstat(b)
static int
egoutPCB(sc, b)
struct eg_softc *sc;
u_char b;
u_int8_t b;
{
bus_chipset_tag_t bc = sc->sc_bc;
bus_io_handle_t ioh = sc->sc_ioh;
int i;
for (i=0; i < 4000; i++) {
if (inb(sc->eg_stat) & EG_STAT_HCRE) {
outb(sc->eg_cmd, b);
if (bus_io_read_1(bc, ioh, EG_STATUS) & EG_STAT_HCRE) {
bus_io_write_1(bc, ioh, EG_COMMAND, b);
return 0;
}
delay(10);
@ -187,16 +188,19 @@ egoutPCB(sc, b)
static int
egreadPCBstat(sc, statb)
struct eg_softc *sc;
u_char statb;
u_int8_t statb;
{
bus_chipset_tag_t bc = sc->sc_bc;
bus_io_handle_t ioh = sc->sc_ioh;
int i;
for (i=0; i < 5000; i++) {
if ((inb(sc->eg_stat) & EG_PCB_STAT) != EG_PCB_NULL)
if ((bus_io_read_1(bc, ioh, EG_STATUS) &
EG_PCB_STAT) != EG_PCB_NULL)
break;
delay(10);
}
if ((inb(sc->eg_stat) & EG_PCB_STAT) == statb)
if ((bus_io_read_1(bc, ioh, EG_STATUS) & EG_PCB_STAT) == statb)
return 0;
return 1;
}
@ -205,10 +209,12 @@ static int
egreadPCBready(sc)
struct eg_softc *sc;
{
bus_chipset_tag_t bc = sc->sc_bc;
bus_io_handle_t ioh = sc->sc_ioh;
int i;
for (i=0; i < 10000; i++) {
if (inb(sc->eg_stat) & EG_STAT_ACRF)
if (bus_io_read_1(bc, ioh, EG_STATUS) & EG_STAT_ACRF)
return 0;
delay(5);
}
@ -220,22 +226,26 @@ static int
egwritePCB(sc)
struct eg_softc *sc;
{
bus_chipset_tag_t bc = sc->sc_bc;
bus_io_handle_t ioh = sc->sc_ioh;
int i;
u_char len;
u_int8_t len;
outb(sc->eg_ctl, (inb(sc->eg_ctl) & ~EG_PCB_STAT) | EG_PCB_NULL);
bus_io_write_1(bc, ioh, EG_CONTROL,
(bus_io_read_1(bc, ioh, EG_CONTROL) & ~EG_PCB_STAT) | EG_PCB_NULL);
len = sc->eg_pcb[1] + 2;
for (i = 0; i < len; i++)
egoutPCB(sc, sc->eg_pcb[i]);
for (i=0; i < 4000; i++) {
if (inb(sc->eg_stat) & EG_STAT_HCRE)
if (bus_io_read_1(bc, ioh, EG_STATUS) & EG_STAT_HCRE)
break;
delay(10);
}
outb(sc->eg_ctl, (inb(sc->eg_ctl) & ~EG_PCB_STAT) | EG_PCB_DONE);
bus_io_write_1(bc, ioh, EG_CONTROL,
(inb(EG_CONTROL) & ~EG_PCB_STAT) | EG_PCB_DONE);
egoutPCB(sc, len);
@ -248,43 +258,48 @@ static int
egreadPCB(sc)
struct eg_softc *sc;
{
bus_chipset_tag_t bc = sc->sc_bc;
bus_io_handle_t ioh = sc->sc_ioh;
int i;
u_char b;
outb(sc->eg_ctl, (inb(sc->eg_ctl) & ~EG_PCB_STAT) | EG_PCB_NULL);
u_int8_t b;
bus_io_write_1(bc, ioh, EG_CONTROL,
(bus_io_read_1(bc, ioh, EG_CONTROL) & ~EG_PCB_STAT) | EG_PCB_NULL);
bzero(sc->eg_pcb, sizeof(sc->eg_pcb));
if (egreadPCBready(sc))
return 1;
sc->eg_pcb[0] = inb(sc->eg_cmd);
sc->eg_pcb[0] = inb(EG_COMMAND);
if (egreadPCBready(sc))
return 1;
sc->eg_pcb[1] = inb(sc->eg_cmd);
sc->eg_pcb[1] = bus_io_read_1(bc, ioh, EG_COMMAND);
if (sc->eg_pcb[1] > 62) {
dprintf(("len %d too large\n", sc->eg_pcb[1]));
return 1;
}
for (i = 0; i < sc->eg_pcb[1]; i++) {
if (egreadPCBready(sc))
return 1;
sc->eg_pcb[2+i] = inb(sc->eg_cmd);
sc->eg_pcb[2+i] = bus_io_read_1(bc, ioh, EG_COMMAND);
}
if (egreadPCBready(sc))
return 1;
if (egreadPCBstat(sc, EG_PCB_DONE))
return 1;
if ((b = inb(sc->eg_cmd)) != sc->eg_pcb[1] + 2) {
if ((b = bus_io_read_1(bc, ioh, EG_COMMAND)) != sc->eg_pcb[1] + 2) {
dprintf(("%d != %d\n", b, sc->eg_pcb[1] + 2));
return 1;
}
outb(sc->eg_ctl, (inb(sc->eg_ctl) & ~EG_PCB_STAT) | EG_PCB_ACCEPT);
bus_io_write_1(bc, ioh, EG_CONTROL,
(bus_io_read_1(bc, ioh, EG_CONTROL) &
~EG_PCB_STAT) | EG_PCB_ACCEPT);
return 0;
}
@ -300,52 +315,68 @@ egprobe(parent, match, aux)
{
struct eg_softc *sc = match;
struct isa_attach_args *ia = aux;
int i;
bus_chipset_tag_t bc = ia->ia_bc;
bus_io_handle_t ioh;
int i, rval;
rval = 0;
if (ia->ia_iobase & ~0x07f0 != 0) {
dprintf(("Weird iobase %x\n", ia->ia_iobase));
return 0;
}
sc->eg_cmd = ia->ia_iobase + EG_COMMAND;
sc->eg_ctl = ia->ia_iobase + EG_CONTROL;
sc->eg_stat = ia->ia_iobase + EG_STATUS;
sc->eg_data = ia->ia_iobase + EG_DATA;
/* Map i/o space. */
if (bus_io_map(bc, ia->ia_iobase, 0x08, &ioh)) {
dprintf(("egprobe: can't map i/o space in probe\n"));
return 0;
}
/*
* XXX Indirect brokenness.
*/
sc->sc_bc = bc; /* XXX */
sc->sc_ioh = ioh; /* XXX */
/* hard reset card */
outb(sc->eg_ctl, EG_CTL_RESET);
outb(sc->eg_ctl, 0);
bus_io_write_1(bc, ioh, EG_CONTROL, EG_CTL_RESET);
bus_io_write_1(bc, ioh, EG_CONTROL, 0);
for (i = 0; i < 5000; i++) {
delay(1000);
if ((inb(sc->eg_stat) & EG_PCB_STAT) == EG_PCB_NULL)
if ((bus_io_read_1(bc, ioh, EG_STATUS) &
EG_PCB_STAT) == EG_PCB_NULL)
break;
}
if ((inb(sc->eg_stat) & EG_PCB_STAT) != EG_PCB_NULL) {
dprintf(("eg: Reset failed\n"));
return 0;
if ((bus_io_read_1(bc, ioh, EG_STATUS) & EG_PCB_STAT) != EG_PCB_NULL) {
dprintf(("egprobe: Reset failed\n"));
goto out;
}
sc->eg_pcb[0] = EG_CMD_GETINFO; /* Get Adapter Info */
sc->eg_pcb[1] = 0;
if (egwritePCB(sc) != 0)
return 0;
goto out;
if (egreadPCB(sc) != 0) {
egprintpcb(sc);
return 0;
goto out;
}
if (sc->eg_pcb[0] != EG_RSP_GETINFO || /* Get Adapter Info Response */
sc->eg_pcb[1] != 0x0a) {
egprintpcb(sc);
return 0;
goto out;
}
sc->eg_rom_major = sc->eg_pcb[3];
sc->eg_rom_minor = sc->eg_pcb[2];
sc->eg_ram = sc->eg_pcb[6] | (sc->eg_pcb[7] << 8);
ia->ia_iosize = 0x08;
ia->ia_msize = 0;
return 1;
rval = 1;
out:
bus_io_unmap(bc, ioh, 0x08);
return rval;
}
void
@ -355,48 +386,64 @@ egattach(parent, self, aux)
{
struct eg_softc *sc = (void *)self;
struct isa_attach_args *ia = aux;
bus_chipset_tag_t bc = ia->ia_bc;
bus_io_handle_t ioh;
struct ifnet *ifp = &sc->sc_arpcom.ac_if;
int i;
printf("\n");
/* Map i/o space. */
if (bus_io_map(bc, ia->ia_iobase, ia->ia_iosize, &ioh)) {
printf("%s: can't map i/o space\n", self->dv_xname);
return;
}
sc->sc_bc = bc;
sc->sc_ioh = ioh;
egstop(sc);
sc->eg_pcb[0] = EG_CMD_GETEADDR; /* Get Station address */
sc->eg_pcb[1] = 0;
if (egwritePCB(sc) != 0) {
dprintf(("write error\n"));
printf("%s: can't send Get Station Address\n", self->dv_xname);
return;
}
if (egreadPCB(sc) != 0) {
dprintf(("read error\n"));
printf("%s: can't read station address\n", self->dv_xname);
egprintpcb(sc);
return;
}
/* check Get station address response */
if (sc->eg_pcb[0] != EG_RSP_GETEADDR || sc->eg_pcb[1] != 0x06) {
dprintf(("parse error\n"));
printf("%s: card responded with garbage (1)\n",
self->dv_xname);
egprintpcb(sc);
return;
}
bcopy(&sc->eg_pcb[2], sc->sc_arpcom.ac_enaddr, ETHER_ADDR_LEN);
printf(": ROM v%d.%02d %dk address %s\n",
printf("%s: ROM v%d.%02d %dk address %s\n", self->dv_xname,
sc->eg_rom_major, sc->eg_rom_minor, sc->eg_ram,
ether_sprintf(sc->sc_arpcom.ac_enaddr));
sc->eg_pcb[0] = EG_CMD_SETEADDR; /* Set station address */
if (egwritePCB(sc) != 0) {
dprintf(("write error2\n"));
printf("%s: can't send Set Station Address\n", self->dv_xname);
return;
}
if (egreadPCB(sc) != 0) {
dprintf(("read error2\n"));
printf("%s: can't read Set Station Address status\n",
self->dv_xname);
egprintpcb(sc);
return;
}
if (sc->eg_pcb[0] != EG_RSP_SETEADDR || sc->eg_pcb[1] != 0x02 ||
sc->eg_pcb[2] != 0 || sc->eg_pcb[3] != 0) {
dprintf(("parse error2\n"));
printf("%s: card responded with garbage (2)\n",
self->dv_xname);
egprintpcb(sc);
return;
}
@ -426,13 +473,15 @@ eginit(sc)
register struct eg_softc *sc;
{
register struct ifnet *ifp = &sc->sc_arpcom.ac_if;
bus_chipset_tag_t bc = sc->sc_bc;
bus_io_handle_t ioh = sc->sc_ioh;
/* soft reset the board */
outb(sc->eg_ctl, EG_CTL_FLSH);
bus_io_write_1(bc, ioh, EG_CONTROL, EG_CTL_FLSH);
delay(100);
outb(sc->eg_ctl, EG_CTL_ATTN);
bus_io_write_1(bc, ioh, EG_CONTROL, EG_CTL_ATTN);
delay(100);
outb(sc->eg_ctl, 0);
bus_io_write_1(bc, ioh, EG_CONTROL, 0);
delay(200);
sc->eg_pcb[0] = EG_CMD_CONFIG82586; /* Configure 82586 */
@ -440,23 +489,37 @@ eginit(sc)
sc->eg_pcb[2] = 3; /* receive broadcast & multicast */
sc->eg_pcb[3] = 0;
if (egwritePCB(sc) != 0)
dprintf(("write error3\n"));
printf("%s: can't send Configure 82586\n",
sc->sc_dev.dv_xname);
if (egreadPCB(sc) != 0) {
dprintf(("read error\n"));
printf("%s: can't read Configure 82586 status\n",
sc->sc_dev.dv_xname);
egprintpcb(sc);
} else if (sc->eg_pcb[2] != 0 || sc->eg_pcb[3] != 0)
printf("%s: configure card command failed\n",
sc->sc_dev.dv_xname);
if (sc->eg_inbuf == 0)
if (sc->eg_inbuf == NULL) {
sc->eg_inbuf = malloc(EG_BUFLEN, M_TEMP, M_NOWAIT);
if (sc->eg_inbuf == NULL) {
printf("%s: can't allocate inbuf\n",
sc->sc_dev.dv_xname);
panic("eginit");
}
}
sc->eg_incount = 0;
if (sc->eg_outbuf == 0)
if (sc->eg_outbuf == NULL) {
sc->eg_outbuf = malloc(EG_BUFLEN, M_TEMP, M_NOWAIT);
if (sc->eg_outbuf == NULL) {
printf("%s: can't allocate outbuf\n",
sc->sc_dev.dv_xname);
panic("eginit");
}
}
outb(sc->eg_ctl, EG_CTL_CMDE);
bus_io_write_1(bc, ioh, EG_CONTROL, EG_CTL_CMDE);
sc->eg_incount = 0;
egrecv(sc);
@ -496,10 +559,12 @@ egstart(ifp)
struct ifnet *ifp;
{
register struct eg_softc *sc = ifp->if_softc;
bus_chipset_tag_t bc = sc->sc_bc;
bus_io_handle_t ioh = sc->sc_ioh;
struct mbuf *m0, *m;
caddr_t buffer;
int len;
u_short *ptr;
u_int16_t *ptr;
/* Don't transmit if interface is busy or not running */
if ((ifp->if_flags & (IFF_RUNNING|IFF_OACTIVE)) != IFF_RUNNING)
@ -514,8 +579,10 @@ loop:
ifp->if_flags |= IFF_OACTIVE;
/* We need to use m->m_pkthdr.len, so require the header */
if ((m0->m_flags & M_PKTHDR) == 0)
panic("egstart: no header mbuf");
if ((m0->m_flags & M_PKTHDR) == 0) {
printf("%s: no header mbuf\n", sc->sc_dev.dv_xname);
panic("egstart");
}
len = max(m0->m_pkthdr.len, ETHER_MIN_LEN);
#if NBPFILTER > 0
@ -532,9 +599,11 @@ loop:
sc->eg_pcb[6] = len; /* length of packet */
sc->eg_pcb[7] = len >> 8;
if (egwritePCB(sc) != 0) {
dprintf(("egwritePCB in egstart failed\n"));
printf("%s: can't send Send Packet command\n",
sc->sc_dev.dv_xname);
ifp->if_oerrors++;
ifp->if_flags &= ~IFF_OACTIVE;
m_freem(m0);
goto loop;
}
@ -545,11 +614,12 @@ loop:
}
/* set direction bit: host -> adapter */
outb(sc->eg_ctl, inb(sc->eg_ctl) & ~EG_CTL_DIR);
bus_io_write_1(bc, ioh, EG_CONTROL,
bus_io_read_1(bc, ioh, EG_CONTROL) & ~EG_CTL_DIR);
for (ptr = (u_short *) sc->eg_outbuf; len > 0; len -= 2) {
outw(sc->eg_data, *ptr++);
while (!(inb(sc->eg_stat) & EG_STAT_HRDY))
for (ptr = (u_int16_t *) sc->eg_outbuf; len > 0; len -= 2) {
bus_io_write_2(bc, ioh, EG_DATA, *ptr++);
while (!(bus_io_read_1(bc, ioh, EG_STATUS) & EG_STAT_HRDY))
; /* XXX need timeout here */
}
@ -561,22 +631,29 @@ egintr(arg)
void *arg;
{
register struct eg_softc *sc = arg;
int i, len;
u_short *ptr;
bus_chipset_tag_t bc = sc->sc_bc;
bus_io_handle_t ioh = sc->sc_ioh;
int i, len, serviced;
u_int16_t *ptr;
while (inb(sc->eg_stat) & EG_STAT_ACRF) {
serviced = 0;
while (bus_io_read_1(bc, ioh, EG_STATUS) & EG_STAT_ACRF) {
egreadPCB(sc);
switch (sc->eg_pcb[0]) {
case EG_RSP_RECVPACKET:
len = sc->eg_pcb[6] | (sc->eg_pcb[7] << 8);
/* Set direction bit : Adapter -> host */
outb(sc->eg_ctl, inb(sc->eg_ctl) | EG_CTL_DIR);
bus_io_write_1(bc, ioh, EG_CONTROL,
bus_io_read_1(bc, ioh, EG_CONTROL) | EG_CTL_DIR);
for (ptr = (u_short *) sc->eg_inbuf; len > 0; len -= 2) {
while (!(inb(sc->eg_stat) & EG_STAT_HRDY))
for (ptr = (u_int16_t *) sc->eg_inbuf;
len > 0; len -= 2) {
while (!(bus_io_read_1(bc, ioh, EG_STATUS) &
EG_STAT_HRDY))
;
*ptr++ = inw(sc->eg_data);
*ptr++ = bus_io_read_2(bc, ioh, EG_DATA);
}
len = sc->eg_pcb[8] | (sc->eg_pcb[9] << 8);
@ -584,40 +661,51 @@ egintr(arg)
sc->eg_incount--;
egrecv(sc);
serviced = 1;
break;
case EG_RSP_SENDPACKET:
if (sc->eg_pcb[6] || sc->eg_pcb[7]) {
dprintf(("packet dropped\n"));
dprintf(("%s: packet dropped\n",
sc->sc_dev.dv_xname));
sc->sc_arpcom.ac_if.if_oerrors++;
} else
sc->sc_arpcom.ac_if.if_opackets++;
sc->sc_arpcom.ac_if.if_collisions += sc->eg_pcb[8] & 0xf;
sc->sc_arpcom.ac_if.if_collisions +=
sc->eg_pcb[8] & 0xf;
sc->sc_arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
egstart(&sc->sc_arpcom.ac_if);
serviced = 1;
break;
/* XXX byte-order and type-size bugs here... */
case EG_RSP_GETSTATS:
dprintf(("Card Statistics\n"));
dprintf(("%s: Card Statistics\n",
sc->sc_dev.dv_xname));
bcopy(&sc->eg_pcb[2], &i, sizeof(i));
dprintf(("Receive Packets %d\n", i));
bcopy(&sc->eg_pcb[6], &i, sizeof(i));
dprintf(("Transmit Packets %d\n", i));
dprintf(("CRC errors %d\n", *(short*) &sc->eg_pcb[10]));
dprintf(("alignment errors %d\n", *(short*) &sc->eg_pcb[12]));
dprintf(("no resources errors %d\n", *(short*) &sc->eg_pcb[14]));
dprintf(("overrun errors %d\n", *(short*) &sc->eg_pcb[16]));
dprintf(("CRC errors %d\n",
*(short *) &sc->eg_pcb[10]));
dprintf(("alignment errors %d\n",
*(short *) &sc->eg_pcb[12]));
dprintf(("no resources errors %d\n",
*(short *) &sc->eg_pcb[14]));
dprintf(("overrun errors %d\n",
*(short *) &sc->eg_pcb[16]));
serviced = 1;
break;
default:
dprintf(("egintr: Unknown response %x??\n",
sc->eg_pcb[0]));
printf("%s: egintr: Unknown response %x??\n",
sc->sc_dev.dv_xname, sc->eg_pcb[0]);
egprintpcb(sc);
break;
}
}
return 0;
return serviced;
}
/*
@ -760,7 +848,7 @@ egioctl(ifp, cmd, data)
if (ns_nullhost(*ina))
ina->x_host =
*(union ns_host *)(sc->sc_arpcom.ac_enaddr);
*(union ns_host *)(sc->sc_arpcom.ac_enaddr);
else
bcopy(ina->x_host.c_host,
sc->sc_arpcom.ac_enaddr,
@ -820,7 +908,7 @@ egreset(sc)
{
int s;
dprintf(("egreset()\n"));
dprintf(("%s: egreset()\n", sc->sc_dev.dv_xname));
s = splnet();
egstop(sc);
eginit(sc);
@ -844,5 +932,5 @@ egstop(sc)
register struct eg_softc *sc;
{
outb(sc->eg_ctl, 0);
bus_io_write_1(sc->sc_bc, sc->sc_ioh, EG_CONTROL, 0);
}