07b064e02e
Some of the stuff (e.g., rarpd, bootpd, dhcpd etc., libsa) still will only support Ethernet. Tcpdump itself should be ok, but libpcap needs lot of work. For the detailed change history, look at the commit log entries for the is-newarp branch.
1465 lines
34 KiB
C
1465 lines
34 KiB
C
/* $NetBSD: if_iy.c,v 1.10 1997/03/15 18:11:47 is Exp $ */
|
|
/* #define IYDEBUG */
|
|
/* #define IYMEMDEBUG */
|
|
/*-
|
|
* Copyright (c) 1996 Ignatios Souvatzis.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product contains software developed by Ignatios Souvatzis for
|
|
* the NetBSD project.
|
|
* 4. The names of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "bpfilter.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/protosw.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/syslog.h>
|
|
#include <sys/device.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/if_types.h>
|
|
#include <net/if_dl.h>
|
|
|
|
#include <net/if_ether.h>
|
|
|
|
#if NBPFILTER > 0
|
|
#include <net/bpf.h>
|
|
#include <net/bpfdesc.h>
|
|
#endif
|
|
|
|
#ifdef INET
|
|
#include <netinet/in.h>
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/in_var.h>
|
|
#include <netinet/ip.h>
|
|
#include <netinet/if_inarp.h>
|
|
#endif
|
|
|
|
#ifdef NS
|
|
#include <netns/ns.h>
|
|
#include <netns/ns_if.h>
|
|
#endif
|
|
|
|
#include <vm/vm.h>
|
|
|
|
#include <machine/cpu.h>
|
|
#include <machine/bus.h>
|
|
#include <machine/intr.h>
|
|
|
|
#include <dev/isa/isareg.h>
|
|
#include <dev/isa/isavar.h>
|
|
#include <dev/ic/i82595reg.h>
|
|
|
|
#define ETHER_MIN_LEN (ETHERMIN + sizeof(struct ether_header) + 4)
|
|
#define ETHER_MAX_LEN (ETHERMTU + sizeof(struct ether_header) + 4)
|
|
|
|
/*
|
|
* Ethernet status, per interface.
|
|
*/
|
|
struct iy_softc {
|
|
struct device sc_dev;
|
|
void *sc_ih;
|
|
|
|
bus_space_tag_t sc_iot;
|
|
bus_space_handle_t sc_ioh;
|
|
|
|
struct ethercom sc_ethercom;
|
|
|
|
#define MAX_MBS 8
|
|
struct mbuf *mb[MAX_MBS];
|
|
int next_mb, last_mb;
|
|
|
|
int mappedirq;
|
|
|
|
int hard_vers;
|
|
|
|
int promisc;
|
|
|
|
int sram, tx_size, rx_size;
|
|
|
|
int tx_start, tx_end, tx_last;
|
|
int rx_start;
|
|
|
|
#ifdef IYDEBUG
|
|
int sc_debug;
|
|
#endif
|
|
};
|
|
|
|
void iywatchdog __P((struct ifnet *));
|
|
int iyioctl __P((struct ifnet *, u_long, caddr_t));
|
|
int iyintr __P((void *));
|
|
void iyinit __P((struct iy_softc *));
|
|
void iystop __P((struct iy_softc *));
|
|
void iystart __P((struct ifnet *));
|
|
|
|
void iy_intr_rx __P((struct iy_softc *));
|
|
void iy_intr_tx __P((struct iy_softc *));
|
|
|
|
void iyreset __P((struct iy_softc *));
|
|
void iy_readframe __P((struct iy_softc *, int));
|
|
void iy_drop_packet_buffer __P((struct iy_softc *));
|
|
void iy_find_mem_size __P((struct iy_softc *));
|
|
void iyrint __P((struct iy_softc *));
|
|
void iytint __P((struct iy_softc *));
|
|
void iyxmit __P((struct iy_softc *));
|
|
void iyget __P((struct iy_softc *, bus_space_tag_t, bus_space_handle_t, int));
|
|
void iymbuffill __P((void *));
|
|
void iymbufempty __P((void *));
|
|
void iyprobemem __P((struct iy_softc *));
|
|
static __inline void eepromwritebit __P((bus_space_tag_t, bus_space_handle_t,
|
|
int));
|
|
static __inline int eepromreadbit __P((bus_space_tag_t, bus_space_handle_t));
|
|
/*
|
|
* void iymeminit __P((void *, struct iy_softc *));
|
|
* static int iy_mc_setup __P((struct iy_softc *, void *));
|
|
* static void iy_mc_reset __P((struct iy_softc *));
|
|
*/
|
|
#ifdef IYDEBUGX
|
|
void print_rbd __P((volatile struct iy_recv_buf_desc *));
|
|
|
|
int in_ifrint = 0;
|
|
int in_iftint = 0;
|
|
#endif
|
|
|
|
int iyprobe __P((struct device *, void *, void *));
|
|
void iyattach __P((struct device *, struct device *, void *));
|
|
|
|
static u_int16_t eepromread __P((bus_space_tag_t, bus_space_handle_t, int));
|
|
|
|
static int eepromreadall __P((bus_space_tag_t, bus_space_handle_t, u_int16_t *,
|
|
int));
|
|
|
|
struct cfattach iy_ca = {
|
|
sizeof(struct iy_softc), iyprobe, iyattach
|
|
};
|
|
|
|
struct cfdriver iy_cd = {
|
|
NULL, "iy", DV_IFNET
|
|
};
|
|
|
|
static u_int8_t eepro_irqmap[] = EEPP_INTMAP;
|
|
static u_int8_t eepro_revirqmap[] = EEPP_RINTMAP;
|
|
|
|
int
|
|
iyprobe(parent, match, aux)
|
|
struct device *parent;
|
|
void *match, *aux;
|
|
{
|
|
struct isa_attach_args *ia = aux;
|
|
u_int16_t eaddr[8];
|
|
|
|
bus_space_tag_t iot;
|
|
bus_space_handle_t ioh;
|
|
|
|
u_int8_t c, d;
|
|
|
|
iot = ia->ia_iot;
|
|
|
|
if (bus_space_map(iot, ia->ia_iobase, 16, 0, &ioh))
|
|
return 0;
|
|
|
|
/* try to find the round robin sig: */
|
|
|
|
c = bus_space_read_1(iot, ioh, ID_REG);
|
|
if ((c & ID_REG_MASK) != ID_REG_SIG)
|
|
goto out;
|
|
|
|
d = bus_space_read_1(iot, ioh, ID_REG);
|
|
if ((d & ID_REG_MASK) != ID_REG_SIG)
|
|
goto out;
|
|
|
|
if (((d-c) & R_ROBIN_BITS) != 0x40)
|
|
goto out;
|
|
|
|
d = bus_space_read_1(iot, ioh, ID_REG);
|
|
if ((d & ID_REG_MASK) != ID_REG_SIG)
|
|
goto out;
|
|
|
|
if (((d-c) & R_ROBIN_BITS) != 0x80)
|
|
goto out;
|
|
|
|
d = bus_space_read_1(iot, ioh, ID_REG);
|
|
if ((d & ID_REG_MASK) != ID_REG_SIG)
|
|
goto out;
|
|
|
|
if (((d-c) & R_ROBIN_BITS) != 0xC0)
|
|
goto out;
|
|
|
|
d = bus_space_read_1(iot, ioh, ID_REG);
|
|
if ((d & ID_REG_MASK) != ID_REG_SIG)
|
|
goto out;
|
|
|
|
if (((d-c) & R_ROBIN_BITS) != 0x00)
|
|
goto out;
|
|
|
|
#ifdef IYDEBUG
|
|
printf("iyprobe verified working ID reg.\n");
|
|
#endif
|
|
|
|
if (eepromreadall(iot, ioh, eaddr, 8))
|
|
goto out;
|
|
|
|
if (ia->ia_irq == IRQUNK)
|
|
ia->ia_irq = eepro_irqmap[eaddr[EEPPW1] & EEPP_Int];
|
|
|
|
if (ia->ia_irq >= sizeof(eepro_revirqmap))
|
|
goto out;
|
|
|
|
if (eepro_revirqmap[ia->ia_irq] == 0xff)
|
|
goto out;
|
|
|
|
/* now lets reset the chip */
|
|
|
|
bus_space_write_1(iot, ioh, COMMAND_REG, RESET_CMD);
|
|
delay(200);
|
|
|
|
ia->ia_iosize = 16;
|
|
|
|
bus_space_unmap(iot, ioh, 16);
|
|
return 1; /* found */
|
|
out:
|
|
bus_space_unmap(iot, ioh, 16);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
iyattach(parent, self, aux)
|
|
struct device *parent, *self;
|
|
void *aux;
|
|
{
|
|
struct iy_softc *sc = (void *)self;
|
|
struct isa_attach_args *ia = aux;
|
|
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
|
|
bus_space_tag_t iot;
|
|
bus_space_handle_t ioh;
|
|
unsigned temp;
|
|
u_int16_t eaddr[8];
|
|
u_int8_t myaddr[ETHER_ADDR_LEN];
|
|
int eirq;
|
|
|
|
iot = ia->ia_iot;
|
|
|
|
if (bus_space_map(iot, ia->ia_iobase, 16, 0, &ioh))
|
|
panic("Can't bus_space_map in iyattach");
|
|
|
|
sc->sc_iot = iot;
|
|
sc->sc_ioh = ioh;
|
|
|
|
sc->mappedirq = eepro_revirqmap[ia->ia_irq];
|
|
|
|
/* now let's reset the chip */
|
|
|
|
bus_space_write_1(iot, ioh, COMMAND_REG, RESET_CMD);
|
|
delay(200);
|
|
|
|
iyprobemem(sc);
|
|
|
|
bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
|
|
ifp->if_softc = sc;
|
|
ifp->if_start = iystart;
|
|
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
|
|
/* XXX todo: | IFF_MULTICAST */
|
|
|
|
ifp->if_ioctl = iyioctl;
|
|
ifp->if_watchdog = iywatchdog;
|
|
|
|
(void)eepromreadall(iot, ioh, eaddr, 8);
|
|
sc->hard_vers = eaddr[EEPW6] & EEPP_BoardRev;
|
|
|
|
#ifdef DIAGNOSTICS
|
|
if ((eaddr[EEPPEther0] !=
|
|
eepromread(iot, ioh, EEPPEther0a)) &&
|
|
(eaddr[EEPPEther1] !=
|
|
eepromread(iot, ioh, EEPPEther1a)) &&
|
|
(eaddr[EEPPEther2] !=
|
|
eepromread(iot, ioh, EEPPEther2a)))
|
|
|
|
printf("EEPROM Ethernet address differs from copy\n");
|
|
#endif
|
|
|
|
myaddr[1] = eaddr[EEPPEther0] & 0xFF;
|
|
myaddr[0] = eaddr[EEPPEther0] >> 8;
|
|
myaddr[3] = eaddr[EEPPEther1] & 0xFF;
|
|
myaddr[2] = eaddr[EEPPEther1] >> 8;
|
|
myaddr[5] = eaddr[EEPPEther2] & 0xFF;
|
|
myaddr[4] = eaddr[EEPPEther2] >> 8;
|
|
|
|
/* Attach the interface. */
|
|
if_attach(ifp);
|
|
ether_ifattach(ifp, myaddr);
|
|
printf(": address %s, rev. %d, %d kB\n",
|
|
ether_sprintf(myaddr),
|
|
sc->hard_vers, sc->sram/1024);
|
|
|
|
eirq = eepro_irqmap[eaddr[EEPPW1] & EEPP_Int];
|
|
if (eirq != ia->ia_irq)
|
|
printf("%s: EEPROM irq setting %d ignored\n",
|
|
sc->sc_dev.dv_xname, eirq);
|
|
|
|
#if NBPFILTER > 0
|
|
bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header));
|
|
#endif
|
|
|
|
sc->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq, IST_EDGE,
|
|
IPL_NET, iyintr, sc);
|
|
|
|
temp = bus_space_read_1(iot, ioh, INT_NO_REG);
|
|
bus_space_write_1(iot, ioh, INT_NO_REG, (temp & 0xf8) | sc->mappedirq);
|
|
}
|
|
|
|
void
|
|
iystop(sc)
|
|
struct iy_softc *sc;
|
|
{
|
|
bus_space_tag_t iot;
|
|
bus_space_handle_t ioh;
|
|
#ifdef IYDEBUG
|
|
u_int p, v;
|
|
#endif
|
|
|
|
iot = sc->sc_iot;
|
|
ioh = sc->sc_ioh;
|
|
|
|
bus_space_write_1(iot, ioh, COMMAND_REG, RCV_DISABLE_CMD);
|
|
|
|
bus_space_write_1(iot, ioh, INT_MASK_REG, ALL_INTS);
|
|
bus_space_write_1(iot, ioh, STATUS_REG, ALL_INTS);
|
|
|
|
bus_space_write_1(iot, ioh, COMMAND_REG, RESET_CMD);
|
|
delay(200);
|
|
#ifdef IYDEBUG
|
|
printf("%s: dumping tx chain (st 0x%x end 0x%x last 0x%x)\n",
|
|
sc->sc_dev.dv_xname, sc->tx_start, sc->tx_end, sc->tx_last);
|
|
p = sc->tx_last;
|
|
if (!p)
|
|
p = sc->tx_start;
|
|
do {
|
|
bus_space_write_2(iot, ioh, HOST_ADDR_REG, p);
|
|
v = bus_space_read_2(iot, ioh, MEM_PORT_REG);
|
|
printf("0x%04x: %b ", p, v, "\020\006Ab\010Dn");
|
|
v = bus_space_read_2(iot, ioh, MEM_PORT_REG);
|
|
printf("0x%b", v, "\020\6MAX_COL\7HRT_BEAT\010TX_DEF\011UND_RUN\012JERR\013LST_CRS\014LTCOL\016TX_OK\020COLL");
|
|
p = bus_space_read_2(iot, ioh, MEM_PORT_REG);
|
|
printf(" 0x%04x", p);
|
|
v = bus_space_read_2(iot, ioh, MEM_PORT_REG);
|
|
printf(" 0x%b\n", v, "\020\020Ch");
|
|
|
|
} while (v & 0x8000);
|
|
#endif
|
|
sc->tx_start = sc->tx_end = sc->rx_size;
|
|
sc->tx_last = 0;
|
|
sc->sc_ethercom.ec_if.if_flags &= ~(IFF_RUNNING|IFF_OACTIVE);
|
|
|
|
iymbufempty((void *)sc);
|
|
}
|
|
|
|
void
|
|
iyreset(sc)
|
|
struct iy_softc *sc;
|
|
{
|
|
int s;
|
|
s = splimp();
|
|
iystop(sc);
|
|
iyinit(sc);
|
|
splx(s);
|
|
}
|
|
|
|
void
|
|
iyinit(sc)
|
|
struct iy_softc *sc;
|
|
{
|
|
int i;
|
|
unsigned temp;
|
|
struct ifnet *ifp;
|
|
bus_space_tag_t iot;
|
|
bus_space_handle_t ioh;
|
|
|
|
iot = sc->sc_iot;
|
|
ioh = sc->sc_ioh;
|
|
|
|
ifp = &sc->sc_ethercom.ec_if;
|
|
#ifdef IYDEBUG
|
|
printf("ifp is %p\n", ifp);
|
|
#endif
|
|
|
|
bus_space_write_1(iot, ioh, 0, BANK_SEL(2));
|
|
|
|
temp = bus_space_read_1(iot, ioh, EEPROM_REG);
|
|
if (temp & 0x10)
|
|
bus_space_write_1(iot, ioh, EEPROM_REG, temp & ~0x10);
|
|
|
|
for (i=0; i<6; ++i) {
|
|
bus_space_write_1(iot, ioh, I_ADD(i), LLADDR(ifp->if_sadl)[i]);
|
|
}
|
|
|
|
temp = bus_space_read_1(iot, ioh, REG1);
|
|
bus_space_write_1(iot, ioh, REG1,
|
|
temp | XMT_CHAIN_INT | XMT_CHAIN_ERRSTOP | RCV_DISCARD_BAD);
|
|
|
|
temp = bus_space_read_1(iot, ioh, RECV_MODES_REG);
|
|
bus_space_write_1(iot, ioh, RECV_MODES_REG, temp | MATCH_BRDCST);
|
|
#ifdef IYDEBUG
|
|
printf("%s: RECV_MODES were %b set to %b\n",
|
|
sc->sc_dev.dv_xname,
|
|
temp, "\020\1PRMSC\2NOBRDST\3SEECRC\4LENGTH\5NOSaIns\6MultiIA",
|
|
temp|MATCH_BRDCST,
|
|
"\020\1PRMSC\2NOBRDST\3SEECRC\4LENGTH\5NOSaIns\6MultiIA");
|
|
#endif
|
|
|
|
|
|
delay(500000); /* for the hardware to test for the connector */
|
|
|
|
temp = bus_space_read_1(iot, ioh, MEDIA_SELECT);
|
|
#ifdef IYDEBUG
|
|
printf("%s: media select was 0x%b ", sc->sc_dev.dv_xname,
|
|
temp, "\020\1LnkInDis\2PolCor\3TPE\4JabberDis\5NoAport\6BNC");
|
|
#endif
|
|
temp = (temp & TEST_MODE_MASK);
|
|
|
|
switch(ifp->if_flags & (IFF_LINK0 | IFF_LINK1)) {
|
|
case IFF_LINK0:
|
|
temp &= ~ (BNC_BIT | TPE_BIT);
|
|
break;
|
|
|
|
case IFF_LINK1:
|
|
temp = (temp & ~TPE_BIT) | BNC_BIT;
|
|
break;
|
|
|
|
case IFF_LINK0|IFF_LINK1:
|
|
temp = (temp & ~BNC_BIT) | TPE_BIT;
|
|
break;
|
|
default:
|
|
/* nothing; leave as it is */
|
|
}
|
|
|
|
|
|
bus_space_write_1(iot, ioh, MEDIA_SELECT, temp);
|
|
#ifdef IYDEBUG
|
|
printf("changed to 0x%b\n",
|
|
temp, "\020\1LnkInDis\2PolCor\3TPE\4JabberDis\5NoAport\6BNC");
|
|
#endif
|
|
|
|
bus_space_write_1(iot, ioh, 0, BANK_SEL(0));
|
|
bus_space_write_1(iot, ioh, INT_MASK_REG, ALL_INTS);
|
|
bus_space_write_1(iot, ioh, 0, BANK_SEL(1));
|
|
|
|
temp = bus_space_read_1(iot, ioh, INT_NO_REG);
|
|
bus_space_write_1(iot, ioh, INT_NO_REG, (temp & 0xf8) | sc->mappedirq);
|
|
|
|
#ifdef IYDEBUG
|
|
printf("%s: int no was %b\n", sc->sc_dev.dv_xname,
|
|
temp, "\020\4bad_irq\010flash/boot present");
|
|
temp = bus_space_read_1(iot, ioh, INT_NO_REG);
|
|
printf("%s: int no now 0x%02x\n", sc->sc_dev.dv_xname,
|
|
temp, "\020\4BAD IRQ\010flash/boot present");
|
|
#endif
|
|
|
|
|
|
bus_space_write_1(iot, ioh, RCV_LOWER_LIMIT_REG, 0);
|
|
bus_space_write_1(iot, ioh, RCV_UPPER_LIMIT_REG, (sc->rx_size - 2) >> 8);
|
|
bus_space_write_1(iot, ioh, XMT_LOWER_LIMIT_REG, sc->rx_size >> 8);
|
|
bus_space_write_1(iot, ioh, XMT_UPPER_LIMIT_REG, sc->sram >> 8);
|
|
|
|
temp = bus_space_read_1(iot, ioh, REG1);
|
|
#ifdef IYDEBUG
|
|
printf("%s: HW access is %b\n", sc->sc_dev.dv_xname,
|
|
temp, "\020\2WORD_WIDTH\010INT_ENABLE");
|
|
#endif
|
|
bus_space_write_1(iot, ioh, REG1, temp | INT_ENABLE); /* XXX what about WORD_WIDTH? */
|
|
|
|
#ifdef IYDEBUG
|
|
temp = bus_space_read_1(iot, ioh, REG1);
|
|
printf("%s: HW access is %b\n", sc->sc_dev.dv_xname,
|
|
temp, "\020\2WORD_WIDTH\010INT_ENABLE");
|
|
#endif
|
|
|
|
bus_space_write_1(iot, ioh, 0, BANK_SEL(0));
|
|
|
|
bus_space_write_1(iot, ioh, INT_MASK_REG, ALL_INTS & ~(RX_BIT|TX_BIT));
|
|
bus_space_write_1(iot, ioh, STATUS_REG, ALL_INTS); /* clear ints */
|
|
|
|
bus_space_write_2(iot, ioh, RCV_START_LOW, 0);
|
|
bus_space_write_2(iot, ioh, RCV_STOP_LOW, sc->rx_size - 2);
|
|
sc->rx_start = 0;
|
|
|
|
bus_space_write_1(iot, ioh, 0, SEL_RESET_CMD);
|
|
delay(200);
|
|
|
|
bus_space_write_2(iot, ioh, XMT_ADDR_REG, sc->rx_size);
|
|
|
|
sc->tx_start = sc->tx_end = sc->rx_size;
|
|
sc->tx_last = 0;
|
|
|
|
bus_space_write_1(iot, ioh, 0, RCV_ENABLE_CMD);
|
|
|
|
ifp->if_flags |= IFF_RUNNING;
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
|
}
|
|
|
|
void
|
|
iystart(ifp)
|
|
struct ifnet *ifp;
|
|
{
|
|
struct iy_softc *sc;
|
|
|
|
|
|
struct mbuf *m0, *m;
|
|
u_int len, pad, last, end;
|
|
u_int llen, residual;
|
|
int avail;
|
|
caddr_t data;
|
|
u_int16_t resval, stat;
|
|
bus_space_tag_t iot;
|
|
bus_space_handle_t ioh;
|
|
|
|
#ifdef IYDEBUG
|
|
printf("iystart called\n");
|
|
#endif
|
|
if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
|
|
return;
|
|
|
|
sc = ifp->if_softc;
|
|
iot = sc->sc_iot;
|
|
ioh = sc->sc_ioh;
|
|
|
|
while ((m0 = ifp->if_snd.ifq_head) != NULL) {
|
|
#ifdef IYDEBUG
|
|
printf("%s: trying to write another packet to the hardware\n",
|
|
sc->sc_dev.dv_xname);
|
|
#endif
|
|
|
|
/* We need to use m->m_pkthdr.len, so require the header */
|
|
if ((m0->m_flags & M_PKTHDR) == 0)
|
|
panic("iystart: no header mbuf");
|
|
|
|
len = m0->m_pkthdr.len;
|
|
pad = len & 1;
|
|
|
|
#ifdef IYDEBUG
|
|
printf("%s: length is %d.\n", sc->sc_dev.dv_xname, len);
|
|
#endif
|
|
if (len < ETHER_MIN_LEN) {
|
|
pad = ETHER_MIN_LEN - len;
|
|
}
|
|
|
|
if (len + pad > ETHER_MAX_LEN) {
|
|
/* packet is obviously too large: toss it */
|
|
++ifp->if_oerrors;
|
|
IF_DEQUEUE(&ifp->if_snd, m0);
|
|
m_freem(m0);
|
|
continue;
|
|
}
|
|
|
|
#if NBPFILTER > 0
|
|
if (ifp->if_bpf)
|
|
bpf_mtap(ifp->if_bpf, m0);
|
|
#endif
|
|
|
|
avail = sc->tx_start - sc->tx_end;
|
|
if (avail <= 0)
|
|
avail += sc->tx_size;
|
|
|
|
#ifdef IYDEBUG
|
|
printf("%s: avail is %d.\n", sc->sc_dev.dv_xname, avail);
|
|
#endif
|
|
/*
|
|
* we MUST RUN at splnet here ---
|
|
* XXX todo: or even turn off the boards ints ??? hm...
|
|
*/
|
|
|
|
/* See if there is room to put another packet in the buffer. */
|
|
|
|
if ((len+pad+2*I595_XMT_HDRLEN) > avail) {
|
|
printf("%s: len = %d, avail = %d, setting OACTIVE\n",
|
|
sc->sc_dev.dv_xname, len, avail);
|
|
ifp->if_flags |= IFF_OACTIVE;
|
|
return;
|
|
}
|
|
|
|
/* we know it fits in the hardware now, so dequeue it */
|
|
IF_DEQUEUE(&ifp->if_snd, m0);
|
|
|
|
last = sc->tx_end;
|
|
end = last + pad + len + I595_XMT_HDRLEN;
|
|
|
|
if (end >= sc->sram) {
|
|
if ((sc->sram - last) <= I595_XMT_HDRLEN) {
|
|
/* keep header in one piece */
|
|
last = sc->rx_size;
|
|
end = last + pad + len + I595_XMT_HDRLEN;
|
|
} else
|
|
end -= sc->tx_size;
|
|
}
|
|
|
|
bus_space_write_2(iot, ioh, HOST_ADDR_REG, last);
|
|
bus_space_write_2(iot, ioh, MEM_PORT_REG, XMT_CMD);
|
|
bus_space_write_2(iot, ioh, MEM_PORT_REG, 0);
|
|
bus_space_write_2(iot, ioh, MEM_PORT_REG, 0);
|
|
bus_space_write_2(iot, ioh, MEM_PORT_REG, len + pad);
|
|
|
|
residual = resval = 0;
|
|
|
|
while ((m = m0)!=0) {
|
|
data = mtod(m, caddr_t);
|
|
llen = m->m_len;
|
|
if (residual) {
|
|
#ifdef IYDEBUG
|
|
printf("%s: merging residual with next mbuf.\n",
|
|
sc->sc_dev.dv_xname);
|
|
#endif
|
|
resval |= *data << 8;
|
|
bus_space_write_2(iot, ioh, MEM_PORT_REG, resval);
|
|
--llen;
|
|
++data;
|
|
}
|
|
if (llen > 1)
|
|
bus_space_write_multi_2(iot, ioh, MEM_PORT_REG,
|
|
data, llen>>1);
|
|
residual = llen & 1;
|
|
if (residual) {
|
|
resval = *(data + llen - 1);
|
|
#ifdef IYDEBUG
|
|
printf("%s: got odd mbuf to send.\n",
|
|
sc->sc_dev.dv_xname);
|
|
#endif
|
|
}
|
|
|
|
MFREE(m, m0);
|
|
}
|
|
|
|
if (residual)
|
|
bus_space_write_2(iot, ioh, MEM_PORT_REG, resval);
|
|
|
|
pad >>= 1;
|
|
while (pad-- > 0)
|
|
bus_space_write_2(iot, ioh, MEM_PORT_REG, 0);
|
|
|
|
#ifdef IYDEBUG
|
|
printf("%s: new last = 0x%x, end = 0x%x.\n",
|
|
sc->sc_dev.dv_xname, last, end);
|
|
printf("%s: old start = 0x%x, end = 0x%x, last = 0x%x\n",
|
|
sc->sc_dev.dv_xname, sc->tx_start, sc->tx_end, sc->tx_last);
|
|
#endif
|
|
|
|
if (sc->tx_start != sc->tx_end) {
|
|
bus_space_write_2(iot, ioh, HOST_ADDR_REG, sc->tx_last + XMT_COUNT);
|
|
stat = bus_space_read_2(iot, ioh, MEM_PORT_REG);
|
|
|
|
bus_space_write_2(iot, ioh, HOST_ADDR_REG, sc->tx_last + XMT_CHAIN);
|
|
bus_space_write_2(iot, ioh, MEM_PORT_REG, last);
|
|
bus_space_write_2(iot, ioh, MEM_PORT_REG, stat | CHAIN);
|
|
#ifdef IYDEBUG
|
|
printf("%s: setting 0x%x to 0x%x\n",
|
|
sc->sc_dev.dv_xname, sc->tx_last + XMT_COUNT,
|
|
stat | CHAIN);
|
|
#endif
|
|
}
|
|
stat = bus_space_read_2(iot, ioh, MEM_PORT_REG); /* dummy read */
|
|
|
|
/* XXX todo: enable ints here if disabled */
|
|
|
|
++ifp->if_opackets;
|
|
|
|
if (sc->tx_start == sc->tx_end) {
|
|
bus_space_write_2(iot, ioh, XMT_ADDR_REG, last);
|
|
bus_space_write_1(iot, ioh, 0, XMT_CMD);
|
|
sc->tx_start = last;
|
|
#ifdef IYDEBUG
|
|
printf("%s: writing 0x%x to XAR and giving XCMD\n",
|
|
sc->sc_dev.dv_xname, last);
|
|
#endif
|
|
} else {
|
|
bus_space_write_1(iot, ioh, 0, RESUME_XMT_CMD);
|
|
#ifdef IYDEBUG
|
|
printf("%s: giving RESUME_XCMD\n",
|
|
sc->sc_dev.dv_xname);
|
|
#endif
|
|
}
|
|
sc->tx_last = last;
|
|
sc->tx_end = end;
|
|
}
|
|
}
|
|
|
|
|
|
static __inline void
|
|
eepromwritebit(iot, ioh, what)
|
|
bus_space_tag_t iot;
|
|
bus_space_handle_t ioh;
|
|
int what;
|
|
{
|
|
bus_space_write_1(iot, ioh, EEPROM_REG, what);
|
|
delay(1);
|
|
bus_space_write_1(iot, ioh, EEPROM_REG, what|EESK);
|
|
delay(1);
|
|
bus_space_write_1(iot, ioh, EEPROM_REG, what);
|
|
delay(1);
|
|
}
|
|
|
|
static __inline int
|
|
eepromreadbit(iot, ioh)
|
|
bus_space_tag_t iot;
|
|
bus_space_handle_t ioh;
|
|
{
|
|
int b;
|
|
|
|
bus_space_write_1(iot, ioh, EEPROM_REG, EECS|EESK);
|
|
delay(1);
|
|
b = bus_space_read_1(iot, ioh, EEPROM_REG);
|
|
bus_space_write_1(iot, ioh, EEPROM_REG, EECS);
|
|
delay(1);
|
|
|
|
return ((b & EEDO) != 0);
|
|
}
|
|
|
|
static u_int16_t
|
|
eepromread(iot, ioh, offset)
|
|
bus_space_tag_t iot;
|
|
bus_space_handle_t ioh;
|
|
int offset;
|
|
{
|
|
volatile int i;
|
|
volatile int j;
|
|
volatile u_int16_t readval;
|
|
|
|
bus_space_write_1(iot, ioh, 0, BANK_SEL(2));
|
|
delay(1);
|
|
bus_space_write_1(iot, ioh, EEPROM_REG, EECS); /* XXXX??? */
|
|
delay(1);
|
|
|
|
eepromwritebit(iot, ioh, EECS|EEDI);
|
|
eepromwritebit(iot, ioh, EECS|EEDI);
|
|
eepromwritebit(iot, ioh, EECS);
|
|
|
|
for (j=5; j>=0; --j) {
|
|
if ((offset>>j) & 1)
|
|
eepromwritebit(iot, ioh, EECS|EEDI);
|
|
else
|
|
eepromwritebit(iot, ioh, EECS);
|
|
}
|
|
|
|
for (readval=0, i=0; i<16; ++i) {
|
|
readval<<=1;
|
|
readval |= eepromreadbit(iot, ioh);
|
|
}
|
|
|
|
bus_space_write_1(iot, ioh, EEPROM_REG, 0|EESK);
|
|
delay(1);
|
|
bus_space_write_1(iot, ioh, EEPROM_REG, 0);
|
|
|
|
bus_space_write_1(iot, ioh, COMMAND_REG, BANK_SEL(0));
|
|
|
|
return readval;
|
|
}
|
|
|
|
/*
|
|
* Device timeout/watchdog routine. Entered if the device neglects to generate
|
|
* an interrupt after a transmit has been started on it.
|
|
*/
|
|
void
|
|
iywatchdog(ifp)
|
|
struct ifnet *ifp;
|
|
{
|
|
struct iy_softc *sc = ifp->if_softc;
|
|
|
|
log(LOG_ERR, "%s: device timeout\n", sc->sc_dev.dv_xname);
|
|
++sc->sc_ethercom.ec_if.if_oerrors;
|
|
iyreset(sc);
|
|
}
|
|
|
|
/*
|
|
* What to do upon receipt of an interrupt.
|
|
*/
|
|
int
|
|
iyintr(arg)
|
|
void *arg;
|
|
{
|
|
struct iy_softc *sc = arg;
|
|
bus_space_tag_t iot;
|
|
bus_space_handle_t ioh;
|
|
|
|
register u_short status;
|
|
|
|
iot = sc->sc_iot;
|
|
ioh = sc->sc_ioh;
|
|
|
|
status = bus_space_read_1(iot, ioh, STATUS_REG);
|
|
#ifdef IYDEBUG
|
|
if (status & ALL_INTS) {
|
|
printf("%s: got interupt %b", sc->sc_dev.dv_xname, status,
|
|
"\020\1RX_STP\2RX\3TX\4EXEC");
|
|
if (status & EXEC_INT)
|
|
printf(" event %b\n", bus_space_read_1(iot, ioh, 0),
|
|
"\020\6ABORT");
|
|
else
|
|
printf("\n");
|
|
}
|
|
#endif
|
|
if (((status & (RX_INT | TX_INT)) == 0))
|
|
return 0;
|
|
|
|
if (status & RX_INT) {
|
|
iy_intr_rx(sc);
|
|
bus_space_write_1(iot, ioh, STATUS_REG, RX_INT);
|
|
} else if (status & TX_INT) {
|
|
iy_intr_tx(sc);
|
|
bus_space_write_1(iot, ioh, STATUS_REG, TX_INT);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
iyget(sc, iot, ioh, rxlen)
|
|
struct iy_softc *sc;
|
|
bus_space_tag_t iot;
|
|
bus_space_handle_t ioh;
|
|
int rxlen;
|
|
{
|
|
struct mbuf *m, *top, **mp;
|
|
struct ether_header *eh;
|
|
struct ifnet *ifp;
|
|
int len;
|
|
|
|
ifp = &sc->sc_ethercom.ec_if;
|
|
|
|
m = sc->mb[sc->next_mb];
|
|
sc->mb[sc->next_mb] = 0;
|
|
if (m == 0) {
|
|
MGETHDR(m, M_DONTWAIT, MT_DATA);
|
|
if (m == 0)
|
|
goto dropped;
|
|
} else {
|
|
if (sc->last_mb == sc->next_mb)
|
|
timeout(iymbuffill, sc, 1);
|
|
sc->next_mb = (sc->next_mb + 1) % MAX_MBS;
|
|
m->m_data = m->m_pktdat;
|
|
m->m_flags = M_PKTHDR;
|
|
}
|
|
m->m_pkthdr.rcvif = ifp;
|
|
m->m_pkthdr.len = rxlen;
|
|
len = MHLEN;
|
|
top = 0;
|
|
mp = ⊤
|
|
|
|
while (rxlen > 0) {
|
|
if (top) {
|
|
m = sc->mb[sc->next_mb];
|
|
sc->mb[sc->next_mb] = 0;
|
|
if (m == 0) {
|
|
MGET(m, M_DONTWAIT, MT_DATA);
|
|
if (m == 0) {
|
|
m_freem(top);
|
|
goto dropped;
|
|
}
|
|
} else {
|
|
sc->next_mb = (sc->next_mb + 1) % MAX_MBS;
|
|
}
|
|
len = MLEN;
|
|
}
|
|
if (rxlen >= MINCLSIZE) {
|
|
MCLGET(m, M_DONTWAIT);
|
|
if (m->m_flags & M_EXT)
|
|
len = MCLBYTES;
|
|
}
|
|
len = min(rxlen, len);
|
|
if (len > 1) {
|
|
len &= ~1;
|
|
|
|
bus_space_read_multi_2(iot, ioh, MEM_PORT_REG,
|
|
mtod(m, caddr_t), len/2);
|
|
} else {
|
|
#ifdef IYDEBUG
|
|
printf("%s: received odd mbuf\n", sc->sc_dev.dv_xname);
|
|
#endif
|
|
*(mtod(m, caddr_t)) = bus_space_read_2(iot, ioh,
|
|
MEM_PORT_REG);
|
|
}
|
|
m->m_len = len;
|
|
rxlen -= len;
|
|
*mp = m;
|
|
mp = &m->m_next;
|
|
}
|
|
/* XXX receive the top here */
|
|
++ifp->if_ipackets;
|
|
|
|
eh = mtod(top, struct ether_header *);
|
|
|
|
#if NBPFILTER > 0
|
|
if (ifp->if_bpf) {
|
|
bpf_mtap(ifp->if_bpf, top);
|
|
if ((ifp->if_flags & IFF_PROMISC) &&
|
|
(eh->ether_dhost[0] & 1) == 0 &&
|
|
bcmp(eh->ether_dhost,
|
|
LLADDR(sc->sc_ethercom.ec_if.if_sadl),
|
|
sizeof(eh->ether_dhost)) != 0) {
|
|
|
|
m_freem(top);
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
m_adj(top, sizeof(struct ether_header));
|
|
ether_input(ifp, eh, top);
|
|
return;
|
|
|
|
dropped:
|
|
++ifp->if_ierrors;
|
|
return;
|
|
}
|
|
void
|
|
iy_intr_rx(sc)
|
|
struct iy_softc *sc;
|
|
{
|
|
struct ifnet *ifp;
|
|
bus_space_tag_t iot;
|
|
bus_space_handle_t ioh;
|
|
|
|
u_int rxadrs, rxevnt, rxstatus, rxnext, rxlen;
|
|
|
|
iot = sc->sc_iot;
|
|
ioh = sc->sc_ioh;
|
|
ifp = &sc->sc_ethercom.ec_if;
|
|
|
|
rxadrs = sc->rx_start;
|
|
bus_space_write_2(iot, ioh, HOST_ADDR_REG, rxadrs);
|
|
rxevnt = bus_space_read_2(iot, ioh, MEM_PORT_REG);
|
|
rxnext = 0;
|
|
|
|
while (rxevnt == RCV_DONE) {
|
|
rxstatus = bus_space_read_2(iot, ioh, MEM_PORT_REG);
|
|
rxnext = bus_space_read_2(iot, ioh, MEM_PORT_REG);
|
|
rxlen = bus_space_read_2(iot, ioh, MEM_PORT_REG);
|
|
#ifdef IYDEBUG
|
|
printf("%s: pck at 0x%04x stat %b next 0x%x len 0x%x\n",
|
|
sc->sc_dev.dv_xname, rxadrs, rxstatus,
|
|
"\020\1RCLD\2IA_MCH\010SHORT\011OVRN\013ALGERR"
|
|
"\014CRCERR\015LENERR\016RCVOK\020TYP",
|
|
rxnext, rxlen);
|
|
#endif
|
|
iyget(sc, iot, ioh, rxlen);
|
|
|
|
/* move stop address */
|
|
bus_space_write_2(iot, ioh, RCV_STOP_LOW,
|
|
rxnext == 0 ? sc->rx_size - 2 : rxnext - 2);
|
|
|
|
bus_space_write_2(iot, ioh, HOST_ADDR_REG, rxnext);
|
|
rxadrs = rxnext;
|
|
rxevnt = bus_space_read_2(iot, ioh, MEM_PORT_REG);
|
|
}
|
|
sc->rx_start = rxnext;
|
|
}
|
|
|
|
void
|
|
iy_intr_tx(sc)
|
|
struct iy_softc *sc;
|
|
{
|
|
bus_space_tag_t iot;
|
|
bus_space_handle_t ioh;
|
|
struct ifnet *ifp;
|
|
u_int txstatus, txstat2, txlen, txnext;
|
|
|
|
ifp = &sc->sc_ethercom.ec_if;
|
|
iot = sc->sc_iot;
|
|
ioh = sc->sc_ioh;
|
|
|
|
while (sc->tx_start != sc->tx_end) {
|
|
bus_space_write_2(iot, ioh, HOST_ADDR_REG, sc->tx_start);
|
|
txstatus = bus_space_read_2(iot, ioh, MEM_PORT_REG);
|
|
if ((txstatus & (TX_DONE|CMD_MASK)) != (TX_DONE|XMT_CMD))
|
|
break;
|
|
|
|
txstat2 = bus_space_read_2(iot, ioh, MEM_PORT_REG);
|
|
txnext = bus_space_read_2(iot, ioh, MEM_PORT_REG);
|
|
txlen = bus_space_read_2(iot, ioh, MEM_PORT_REG);
|
|
#ifdef IYDEBUG
|
|
printf("txstat 0x%x stat2 0x%b next 0x%x len 0x%x\n",
|
|
txstatus, txstat2, "\020\6MAX_COL\7HRT_BEAT\010TX_DEF"
|
|
"\011UND_RUN\012JERR\013LST_CRS\014LTCOL\016TX_OK\020COLL",
|
|
txnext, txlen);
|
|
#endif
|
|
if (txlen & CHAIN)
|
|
sc->tx_start = txnext;
|
|
else
|
|
sc->tx_start = sc->tx_end;
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
|
|
|
if ((txstat2 & 0x2000) == 0)
|
|
++ifp->if_oerrors;
|
|
if (txstat2 & 0x000f)
|
|
ifp->if_oerrors += txstat2 & 0x000f;
|
|
}
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
|
}
|
|
|
|
#if 0
|
|
/*
|
|
* Compare two Ether/802 addresses for equality, inlined and unrolled for
|
|
* speed. I'd love to have an inline assembler version of this...
|
|
*/
|
|
static inline int
|
|
ether_equal(one, two)
|
|
u_char *one, *two;
|
|
{
|
|
|
|
if (one[0] != two[0] || one[1] != two[1] || one[2] != two[2] ||
|
|
one[3] != two[3] || one[4] != two[4] || one[5] != two[5])
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Check for a valid address. to_bpf is filled in with one of the following:
|
|
* 0 -> BPF doesn't get this packet
|
|
* 1 -> BPF does get this packet
|
|
* 2 -> BPF does get this packet, but we don't
|
|
* Return value is true if the packet is for us, and false otherwise.
|
|
*
|
|
* This routine is a mess, but it's also critical that it be as fast
|
|
* as possible. It could be made cleaner if we can assume that the
|
|
* only client which will fiddle with IFF_PROMISC is BPF. This is
|
|
* probably a good assumption, but we do not make it here. (Yet.)
|
|
*/
|
|
static inline int
|
|
check_eh(sc, eh, to_bpf)
|
|
struct iy_softc *sc;
|
|
struct ether_header *eh;
|
|
int *to_bpf;
|
|
{
|
|
int i;
|
|
|
|
switch (sc->promisc) {
|
|
case IFF_ALLMULTI:
|
|
/*
|
|
* Receiving all multicasts, but no unicasts except those
|
|
* destined for us.
|
|
*/
|
|
#if NBPFILTER > 0
|
|
*to_bpf = (sc->sc_ethercom.ec_if.iy_bpf != 0); /* BPF gets this packet if anybody cares */
|
|
#endif
|
|
if (eh->ether_dhost[0] & 1)
|
|
return 1;
|
|
if (ether_equal(eh->ether_dhost,
|
|
LLADDR(sc->sc_ethercom.ec_if.if_sadl)))
|
|
return 1;
|
|
return 0;
|
|
|
|
case IFF_PROMISC:
|
|
/*
|
|
* Receiving all packets. These need to be passed on to BPF.
|
|
*/
|
|
#if NBPFILTER > 0
|
|
*to_bpf = (sc->sc_ethercom.ec_if.iy_bpf != 0);
|
|
#endif
|
|
/* If for us, accept and hand up to BPF */
|
|
if (ether_equal(eh->ether_dhost, LLADDR(sc->sc_ethercom.ec_if.if_sadl)))
|
|
return 1;
|
|
|
|
#if NBPFILTER > 0
|
|
if (*to_bpf)
|
|
*to_bpf = 2; /* we don't need to see it */
|
|
#endif
|
|
|
|
/*
|
|
* Not a multicast, so BPF wants to see it but we don't.
|
|
*/
|
|
if (!(eh->ether_dhost[0] & 1))
|
|
return 1;
|
|
|
|
/*
|
|
* If it's one of our multicast groups, accept it and pass it
|
|
* up.
|
|
*/
|
|
for (i = 0; i < sc->mcast_count; i++) {
|
|
if (ether_equal(eh->ether_dhost, (u_char *)&sc->mcast_addrs[i])) {
|
|
#if NBPFILTER > 0
|
|
if (*to_bpf)
|
|
*to_bpf = 1;
|
|
#endif
|
|
return 1;
|
|
}
|
|
}
|
|
return 1;
|
|
|
|
case IFF_ALLMULTI | IFF_PROMISC:
|
|
/*
|
|
* Acting as a multicast router, and BPF running at the same
|
|
* time. Whew! (Hope this is a fast machine...)
|
|
*/
|
|
#if NBPFILTER > 0
|
|
*to_bpf = (sc->sc_ethercom.ec_if.iy_bpf != 0);
|
|
#endif
|
|
/* We want to see multicasts. */
|
|
if (eh->ether_dhost[0] & 1)
|
|
return 1;
|
|
|
|
/* We want to see our own packets */
|
|
if (ether_equal(eh->ether_dhost, LLADDR(sc->sc_ethercom.ec_if.if_sadl)))
|
|
return 1;
|
|
|
|
/* Anything else goes to BPF but nothing else. */
|
|
#if NBPFILTER > 0
|
|
if (*to_bpf)
|
|
*to_bpf = 2;
|
|
#endif
|
|
return 1;
|
|
|
|
case 0:
|
|
/*
|
|
* Only accept unicast packets destined for us, or multicasts
|
|
* for groups that we belong to. For now, we assume that the
|
|
* '586 will only return packets that we asked it for. This
|
|
* isn't strictly true (it uses hashing for the multicast
|
|
* filter), but it will do in this case, and we want to get out
|
|
* of here as quickly as possible.
|
|
*/
|
|
#if NBPFILTER > 0
|
|
*to_bpf = (sc->sc_ethercom.ec_if.iy_bpf != 0);
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
#ifdef DIAGNOSTIC
|
|
panic("check_eh: impossible");
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
int
|
|
iyioctl(ifp, cmd, data)
|
|
register struct ifnet *ifp;
|
|
u_long cmd;
|
|
caddr_t data;
|
|
{
|
|
struct iy_softc *sc;
|
|
struct ifaddr *ifa;
|
|
struct ifreq *ifr;
|
|
int s, error = 0;
|
|
|
|
sc = ifp->if_softc;
|
|
ifa = (struct ifaddr *)data;
|
|
ifr = (struct ifreq *)data;
|
|
|
|
#ifdef IYDEBUG
|
|
printf("iyioctl called with ifp 0x%p (%s) cmd 0x%x data 0x%p\n",
|
|
ifp, ifp->if_xname, cmd, data);
|
|
#endif
|
|
|
|
s = splimp();
|
|
|
|
switch (cmd) {
|
|
|
|
case SIOCSIFADDR:
|
|
ifp->if_flags |= IFF_UP;
|
|
|
|
switch (ifa->ifa_addr->sa_family) {
|
|
#ifdef INET
|
|
case AF_INET:
|
|
iyinit(sc);
|
|
arp_ifinit(ifp, ifa);
|
|
break;
|
|
#endif
|
|
#ifdef NS
|
|
/* XXX - This code is probably wrong. */
|
|
case AF_NS:
|
|
{
|
|
struct ns_addr *ina = &IA_SNS(ifa)->sns_addr;
|
|
|
|
if (ns_nullhost(*ina))
|
|
ina->x_host = *(union ns_host *)
|
|
LLADDR(sc->sc_ethercom.ec_if.if_sadl);
|
|
else
|
|
bcopy(ina->x_host.c_host,
|
|
LLADDR(sc->sc_ethercom.ec_if.if_sadl),
|
|
ETHER_ADDR_LEN);
|
|
/* Set new address. */
|
|
iyinit(sc);
|
|
break;
|
|
}
|
|
#endif /* NS */
|
|
default:
|
|
iyinit(sc);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case SIOCSIFFLAGS:
|
|
sc->promisc = ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI);
|
|
if ((ifp->if_flags & IFF_UP) == 0 &&
|
|
(ifp->if_flags & IFF_RUNNING) != 0) {
|
|
/*
|
|
* If interface is marked down and it is running, then
|
|
* stop it.
|
|
*/
|
|
iystop(sc);
|
|
ifp->if_flags &= ~IFF_RUNNING;
|
|
} else if ((ifp->if_flags & IFF_UP) != 0 &&
|
|
(ifp->if_flags & IFF_RUNNING) == 0) {
|
|
/*
|
|
* If interface is marked up and it is stopped, then
|
|
* start it.
|
|
*/
|
|
iyinit(sc);
|
|
} else {
|
|
/*
|
|
* Reset the interface to pick up changes in any other
|
|
* flags that affect hardware registers.
|
|
*/
|
|
iystop(sc);
|
|
iyinit(sc);
|
|
}
|
|
#ifdef IYDEBUGX
|
|
if (ifp->if_flags & IFF_DEBUG)
|
|
sc->sc_debug = IFY_ALL;
|
|
else
|
|
sc->sc_debug = 0;
|
|
#endif
|
|
break;
|
|
|
|
#if 0 /* XXX */
|
|
case SIOCADDMULTI:
|
|
case SIOCDELMULTI:
|
|
error = (cmd == SIOCADDMULTI) ?
|
|
ether_addmulti(ifr, &sc->sc_ethercom):
|
|
ether_delmulti(ifr, &sc->sc_ethercom);
|
|
|
|
if (error == ENETRESET) {
|
|
/*
|
|
* Multicast list has changed; set the hardware filter
|
|
* accordingly.
|
|
*/
|
|
iy_mc_reset(sc); /* XXX */
|
|
error = 0;
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
error = EINVAL;
|
|
}
|
|
splx(s);
|
|
return error;
|
|
}
|
|
|
|
#if 0
|
|
static void
|
|
iy_mc_reset(sc)
|
|
struct iy_softc *sc;
|
|
{
|
|
struct ether_multi *enm;
|
|
struct ether_multistep step;
|
|
|
|
/*
|
|
* Step through the list of addresses.
|
|
*/
|
|
sc->mcast_count = 0;
|
|
ETHER_FIRST_MULTI(step, &sc->sc_ethercom, enm);
|
|
while (enm) {
|
|
if (sc->mcast_count >= MAXMCAST ||
|
|
bcmp(enm->enm_addrlo, enm->enm_addrhi, 6) != 0) {
|
|
sc->sc_ethercom.ec_if.if_flags |= IFF_ALLMULTI;
|
|
iyioctl(&sc->sc_ethercom.ec_if, SIOCSIFFLAGS,
|
|
(void *)0);
|
|
goto setflag;
|
|
}
|
|
|
|
bcopy(enm->enm_addrlo, &sc->mcast_addrs[sc->mcast_count], 6);
|
|
sc->mcast_count++;
|
|
ETHER_NEXT_MULTI(step, enm);
|
|
}
|
|
setflag:
|
|
sc->want_mcsetup = 1;
|
|
}
|
|
|
|
#ifdef IYDEBUG
|
|
void
|
|
print_rbd(rbd)
|
|
volatile struct ie_recv_buf_desc *rbd;
|
|
{
|
|
|
|
printf("RBD at %08lx:\nactual %04x, next %04x, buffer %08x\n"
|
|
"length %04x, mbz %04x\n", (u_long)rbd, rbd->ie_rbd_actual,
|
|
rbd->ie_rbd_next, rbd->ie_rbd_buffer, rbd->ie_rbd_length,
|
|
rbd->mbz);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
void
|
|
iymbuffill(arg)
|
|
void *arg;
|
|
{
|
|
struct iy_softc *sc = (struct iy_softc *)arg;
|
|
int s, i;
|
|
|
|
s = splimp();
|
|
i = sc->last_mb;
|
|
do {
|
|
if (sc->mb[i] == NULL)
|
|
MGET(sc->mb[i], M_DONTWAIT, MT_DATA);
|
|
if (sc->mb[i] == NULL)
|
|
break;
|
|
i = (i + 1) % MAX_MBS;
|
|
} while (i != sc->next_mb);
|
|
sc->last_mb = i;
|
|
/* If the queue was not filled, try again. */
|
|
if (sc->last_mb != sc->next_mb)
|
|
timeout(iymbuffill, sc, 1);
|
|
splx(s);
|
|
}
|
|
|
|
|
|
void
|
|
iymbufempty(arg)
|
|
void *arg;
|
|
{
|
|
struct iy_softc *sc = (struct iy_softc *)arg;
|
|
int s, i;
|
|
|
|
s = splimp();
|
|
for (i = 0; i<MAX_MBS; i++) {
|
|
if (sc->mb[i]) {
|
|
m_freem(sc->mb[i]);
|
|
sc->mb[i] = NULL;
|
|
}
|
|
}
|
|
sc->last_mb = sc->next_mb = 0;
|
|
untimeout(iymbuffill, sc);
|
|
splx(s);
|
|
}
|
|
|
|
void
|
|
iyprobemem(sc)
|
|
struct iy_softc *sc;
|
|
{
|
|
bus_space_tag_t iot;
|
|
bus_space_handle_t ioh;
|
|
int testing;
|
|
|
|
iot = sc->sc_iot;
|
|
ioh = sc->sc_ioh;
|
|
|
|
bus_space_write_1(iot, ioh, COMMAND_REG, BANK_SEL(0));
|
|
delay(1);
|
|
bus_space_write_2(iot, ioh, HOST_ADDR_REG, 4096-2);
|
|
bus_space_write_2(iot, ioh, MEM_PORT_REG, 0);
|
|
|
|
for (testing=65536; testing >= 4096; testing >>= 1) {
|
|
bus_space_write_2(iot, ioh, HOST_ADDR_REG, testing-2);
|
|
bus_space_write_2(iot, ioh, MEM_PORT_REG, 0xdead);
|
|
bus_space_write_2(iot, ioh, HOST_ADDR_REG, testing-2);
|
|
if (bus_space_read_2(iot, ioh, MEM_PORT_REG) != 0xdead) {
|
|
#ifdef IYMEMDEBUG
|
|
printf("%s: Didn't keep 0xdead at 0x%x\n",
|
|
sc->sc_dev.dv_xname, testing-2);
|
|
#endif
|
|
continue;
|
|
}
|
|
|
|
bus_space_write_2(iot, ioh, HOST_ADDR_REG, testing-2);
|
|
bus_space_write_2(iot, ioh, MEM_PORT_REG, 0xbeef);
|
|
bus_space_write_2(iot, ioh, HOST_ADDR_REG, testing-2);
|
|
if (bus_space_read_2(iot, ioh, MEM_PORT_REG) != 0xbeef) {
|
|
#ifdef IYMEMDEBUG
|
|
printf("%s: Didn't keep 0xbeef at 0x%x\n",
|
|
sc->sc_dev.dv_xname, testing-2);
|
|
#endif
|
|
continue;
|
|
}
|
|
|
|
bus_space_write_2(iot, ioh, HOST_ADDR_REG, 0);
|
|
bus_space_write_2(iot, ioh, MEM_PORT_REG, 0);
|
|
bus_space_write_2(iot, ioh, HOST_ADDR_REG, testing >> 1);
|
|
bus_space_write_2(iot, ioh, MEM_PORT_REG, testing >> 1);
|
|
bus_space_write_2(iot, ioh, HOST_ADDR_REG, 0);
|
|
if (bus_space_read_2(iot, ioh, MEM_PORT_REG) == (testing >> 1)) {
|
|
#ifdef IYMEMDEBUG
|
|
printf("%s: 0x%x alias of 0x0\n",
|
|
sc->sc_dev.dv_xname, testing >> 1);
|
|
#endif
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
sc->sram = testing;
|
|
|
|
switch(testing) {
|
|
case 65536:
|
|
/* 4 NFS packets + overhead RX, 2 NFS + overhead TX */
|
|
sc->rx_size = 44*1024;
|
|
break;
|
|
|
|
case 32768:
|
|
/* 2 NFS packets + overhead RX, 1 NFS + overhead TX */
|
|
sc->rx_size = 22*1024;
|
|
break;
|
|
|
|
case 16384:
|
|
/* 1 NFS packet + overhead RX, 4 big packets TX */
|
|
sc->rx_size = 10*1024;
|
|
break;
|
|
default:
|
|
sc->rx_size = testing/2;
|
|
break;
|
|
}
|
|
sc->tx_size = testing - sc->rx_size;
|
|
}
|
|
|
|
static int
|
|
eepromreadall(iot, ioh, wordp, maxi)
|
|
bus_space_tag_t iot;
|
|
bus_space_handle_t ioh;
|
|
u_int16_t *wordp;
|
|
int maxi;
|
|
{
|
|
int i;
|
|
u_int16_t checksum, tmp;
|
|
|
|
checksum = 0;
|
|
|
|
for (i=0; i<EEPP_LENGTH; ++i) {
|
|
tmp = eepromread(iot, ioh, i);
|
|
checksum += tmp;
|
|
if (i<maxi)
|
|
wordp[i] = tmp;
|
|
}
|
|
|
|
if (checksum != EEPP_CHKSUM) {
|
|
#ifdef IYDEBUG
|
|
printf("wrong EEPROM checksum 0x%x should be 0x%x\n",
|
|
checksum, EEPP_CHKSUM);
|
|
#endif
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|