summaryrefslogtreecommitdiff
path: root/sys/dev/ic/tropic.c
diff options
context:
space:
mode:
authorFederico G. Schwindt <fgsch@cvs.openbsd.org>1999-12-27 21:51:36 +0000
committerFederico G. Schwindt <fgsch@cvs.openbsd.org>1999-12-27 21:51:36 +0000
commitd22c519e835f6ff88c8df7f1ee3837808ed67a4b (patch)
tree32685fa72e7c6b42e4fceadac4bac4e7f18a3dc2 /sys/dev/ic/tropic.c
parent27c6ceea191523e7370c6ecd6a6837d88fe6a13b (diff)
Token ring driver for Tropic based boards; from NetBSD.
This is as is in NetBSD. I've some mods in my tree ready to be merged. For the records: * This driver needs massive work to be ready for production (people is invited to help, don't be shy). Rewrite part/whole is my list. * IP isn't working at the moment. ARP handling needs several changes. (remember this guys? maybe this time...) * This only handle SNAP frames. IPX using IPX frames doesn't work (you could receive but not transmit to be exact). In order to do that, dsap needs to be changed. I don't know how to setup multiple dsap's at the moment (don't worry about it now, our IPX code is going to be replaced soon since the current implementation is buggy). * Source routing is missing. * tcpdump needs some hacking too (in my tree too). * More things I'm probably forgetting now (haven't worked on this for a while). Our networking code needs some cleanup and rearrangement (specially the headers). net/if_ether.h is an example; arp is not ether specific.
Diffstat (limited to 'sys/dev/ic/tropic.c')
-rw-r--r--sys/dev/ic/tropic.c1748
1 files changed, 1748 insertions, 0 deletions
diff --git a/sys/dev/ic/tropic.c b/sys/dev/ic/tropic.c
new file mode 100644
index 00000000000..32c7e72e0ab
--- /dev/null
+++ b/sys/dev/ic/tropic.c
@@ -0,0 +1,1748 @@
+/* $OpenBSD: tropic.c,v 1.1 1999/12/27 21:51:35 fgsch Exp $ */
+/* $NetBSD: tropic.c,v 1.6 1999/12/17 08:26:31 fvdl Exp $ */
+
+/*
+ * Ported to NetBSD by Onno van der Linden
+ * Many thanks to Larry Lile for sending me the IBM TROPIC documentation.
+ *
+ * Mach Operating System
+ * Copyright (c) 1991 Carnegie Mellon University
+ * Copyright (c) 1991 IBM Corporation
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation,
+ * and that the name IBM not be used in advertising or publicity
+ * pertaining to distribution of the software without specific, written
+ * prior permission.
+ *
+ * CARNEGIE MELLON AND IBM ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON AND IBM DISCLAIM ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/mbuf.h>
+#include <sys/buf.h>
+#include <sys/socket.h>
+#include <sys/syslog.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/device.h>
+
+#include <net/if.h>
+#include <net/if_llc.h>
+#include <net/if_media.h>
+#include <net/netisr.h>
+#include <net/route.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+#include <netinet/in_var.h>
+#include <net/if_token.h>
+#endif
+
+#ifdef NS
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#include <net/bpfdesc.h>
+#endif
+
+#ifndef ifr_mtu
+#define ifr_mtu ifr_metric
+#endif
+
+#include <machine/cpu.h>
+#include <machine/bus.h>
+
+#include <dev/ic/tropicreg.h>
+#include <dev/ic/tropicvar.h>
+
+static void tr_shutdown __P((void *));
+
+void tr_rint __P((struct tr_softc *));
+void tr_xint __P((struct tr_softc *));
+void tr_oldxint __P((struct tr_softc *));
+struct mbuf *tr_get __P((struct tr_softc *, int, struct ifnet *));
+void tr_opensap __P((struct tr_softc *, u_char));
+void tr_timeout __P((void *));
+int tr_mbcopy __P((struct tr_softc *, bus_size_t, struct mbuf *));
+void tr_bcopy __P((struct tr_softc *, u_char *, int));
+void tr_start __P((struct ifnet *));
+void tr_oldstart __P((struct ifnet *));
+void tr_watchdog __P((struct ifnet *));
+int tr_mediachange __P((struct ifnet *));
+void tr_mediastatus __P((struct ifnet *, struct ifmediareq *));
+int tropic_mediachange __P((struct tr_softc *));
+void tropic_mediastatus __P((struct tr_softc *, struct ifmediareq *));
+void tr_reinit __P((void *));
+
+struct cfdriver tr_cd = {
+ NULL, "tr", DV_IFNET
+};
+
+/*
+ * TODO:
+ * clean up tr_intr: more subroutines
+ * IFF_LINK0 == IFM_TOK_SRCRT change to link flag implies media flag change
+ * IFF_LINK1 == IFM_TOK_ALLR change to link flag implies media flag change
+ * XXX Create receive_done queue to kill "ASB not free", but does this ever
+ * XXX happen ?
+ */
+
+static int media[] = {
+ IFM_TOKEN | IFM_TOK_UTP4,
+ IFM_TOKEN | IFM_TOK_STP4,
+ IFM_TOKEN | IFM_TOK_UTP16,
+ IFM_TOKEN | IFM_TOK_STP16,
+ IFM_TOKEN | IFM_TOK_UTP4,
+ IFM_TOKEN | IFM_TOK_UTP16,
+ IFM_TOKEN | IFM_TOK_STP4,
+ IFM_TOKEN | IFM_TOK_STP16
+};
+
+int
+tropic_mediachange(sc)
+ struct tr_softc *sc;
+{
+ if (IFM_TYPE(sc->sc_media.ifm_media) != IFM_TOKEN)
+ return EINVAL;
+
+ switch (IFM_SUBTYPE(sc->sc_media.ifm_media)) {
+ case IFM_TOK_STP16:
+ case IFM_TOK_UTP16:
+ if ((sc->sc_init_status & RSP_16) == 0) {
+ tr_stop(sc);
+ if (tr_setspeed(sc, 16))
+ return EINVAL;
+ if (tr_reset(sc))
+ return EINVAL;
+ if (tr_config(sc))
+ return EINVAL;
+ }
+ break;
+ case IFM_TOK_STP4:
+ case IFM_TOK_UTP4:
+ if ((sc->sc_init_status & RSP_16) != 0) {
+ tr_stop(sc);
+ if (tr_setspeed(sc, 4))
+ return EINVAL;
+ if (tr_reset(sc))
+ return EINVAL;
+ if (tr_config(sc))
+ return EINVAL;
+ }
+ break;
+ }
+/*
+ * XXX Handle Early Token Release !!!!
+ */
+ return 0;
+}
+
+void
+tropic_mediastatus(sc, ifmr)
+ struct tr_softc *sc;
+ struct ifmediareq *ifmr;
+{
+ struct ifmedia *ifm = &sc->sc_media;
+
+ ifmr->ifm_active = ifm->ifm_cur->ifm_media;
+}
+
+int
+tr_config(sc)
+ struct tr_softc *sc;
+{
+ if (sc->sc_init_status & FAST_PATH_TRANSMIT) {
+ int i;
+
+ for (i=0; i < SRB_CFP_CMDSIZE; i++)
+ SRB_OUTB(sc, sc->sc_srb, i, 0);
+
+ SRB_OUTB(sc, sc->sc_srb, SRB_CMD, DIR_CONFIG_FAST_PATH_RAM);
+
+ SRB_OUTW(sc, sc->sc_srb, SRB_CFP_RAMSIZE,
+ (16 + (sc->sc_nbuf * FP_BUF_LEN) / 8));
+ SRB_OUTW(sc, sc->sc_srb, SRB_CFP_BUFSIZE, FP_BUF_LEN);
+
+ /* tell adapter: command in SRB */
+ ACA_SETB(sc, ACA_ISRA_o, CMD_IN_SRB);
+
+ for (i = 0; i < 30000; i++) {
+ if (ACA_RDB(sc, ACA_ISRP_o) & SRB_RESP_INT)
+ break;
+ delay(100);
+ }
+
+ if (i == 30000 && sc->sc_srb == ACA_RDW(sc, ACA_WRBR)) {
+ printf("No response for fast path cfg\n");
+ return 1;
+ }
+
+ ACA_RSTB(sc, ACA_ISRP_o, ~(SRB_RESP_INT));
+
+
+ if ((SRB_INB(sc, sc->sc_srb, SRB_RETCODE) != 0)) {
+ printf("cfg fast path returned: %02x\n",
+ SRB_INB(sc, sc->sc_srb, SRB_RETCODE));
+ return 1;
+ }
+
+ sc->sc_txca = SRB_INW(sc, sc->sc_srb, SRB_CFPRESP_FPXMIT);
+ sc->sc_srb = SRB_INW(sc, sc->sc_srb, SRB_CFPRESP_SRBADDR);
+ }
+ else {
+ if (sc->sc_init_status & RSP_16)
+ sc->sc_maxmtu = sc->sc_dhb16maxsz;
+ else
+ sc->sc_maxmtu = sc->sc_dhb4maxsz;
+/*
+ * XXX Not completely true because Fast Path Transmit has 514 byte buffers
+ * XXX and TR_MAX_LINK_HDR is only correct when source-routing is used.
+ * XXX depending on wether source routing is used change the calculation
+ * XXX use IFM_TOK_SRCRT (IFF_LINK0)
+ * XXX recompute sc_minbuf !!
+ */
+ sc->sc_maxmtu -= TR_MAX_LINK_HDR;
+ }
+ return 0;
+}
+
+int
+tr_attach(sc)
+ struct tr_softc *sc;
+{
+ int nmedia, *mediaptr, *defmediaptr;
+ int i, temp;
+ struct ifnet *ifp = &sc->sc_arpcom.ac_if;
+
+ if (sc->sc_init_status & FAST_PATH_TRANSMIT) {
+ bus_size_t srb;
+ int nbuf = 0;
+
+ srb = sc->sc_srb;
+
+ switch (sc->sc_memsize) {
+ case 65536:
+ nbuf = 58;
+ sc->sc_maxmtu = IPMTU_4MBIT_MAX;
+ break;
+ case 32768:
+ nbuf = 29;
+ sc->sc_maxmtu = IPMTU_4MBIT_MAX;
+ break;
+ case 16384:
+ nbuf = 13;
+ sc->sc_maxmtu = IPMTU_4MBIT_MAX;
+ break;
+ case 8192:
+ nbuf = 5;
+ sc->sc_maxmtu = ISO88025_MTU;
+ }
+
+ sc->sc_minbuf = ((sc->sc_maxmtu + 511) / 512) + 1;
+ sc->sc_nbuf = nbuf;
+
+/*
+ * Create circular queues caching the buffer pointers ?
+ */
+ }
+ else {
+/*
+ * MAX_MACFRAME_SIZE = DHB_SIZE - 6
+ * IPMTU = MAX_MACFRAME_SIZE - (14 + 18 + 8)
+ * (14 = header, 18 = sroute, 8 = llcsnap)
+ */
+
+ switch (sc->sc_memsize) {
+ case 8192:
+ sc->sc_dhb4maxsz = 2048;
+ sc->sc_dhb16maxsz = 2048;
+ break;
+ case 16384:
+ sc->sc_dhb4maxsz = 4096;
+ sc->sc_dhb16maxsz = 4096;
+ break;
+ case 32768:
+ sc->sc_dhb4maxsz = 4464;
+ sc->sc_dhb16maxsz = 8192;
+ break;
+ case 65536:
+ sc->sc_dhb4maxsz = 4464;
+ sc->sc_dhb16maxsz = 8192;
+ break;
+ }
+ switch (MM_INB(sc, TR_DHB4_OFFSET)) {
+ case 0xF:
+ if (sc->sc_dhb4maxsz > 2048)
+ sc->sc_dhb4maxsz = 2048;
+ break;
+ case 0xE:
+ if (sc->sc_dhb4maxsz > 4096)
+ sc->sc_dhb4maxsz = 4096;
+ break;
+ case 0xD:
+ if (sc->sc_dhb4maxsz > 4464)
+ sc->sc_dhb4maxsz = 4464;
+ break;
+ }
+
+ switch (MM_INB(sc, TR_DHB16_OFFSET)) {
+ case 0xF:
+ if (sc->sc_dhb16maxsz > 2048)
+ sc->sc_dhb16maxsz = 2048;
+ break;
+ case 0xE:
+ if (sc->sc_dhb16maxsz > 4096)
+ sc->sc_dhb16maxsz = 4096;
+ break;
+ case 0xD:
+ if (sc->sc_dhb16maxsz > 8192)
+ sc->sc_dhb16maxsz = 8192;
+ break;
+ case 0xC:
+ if (sc->sc_dhb16maxsz > 8192)
+ sc->sc_dhb16maxsz = 8192;
+ break;
+ case 0xB:
+ if (sc->sc_dhb16maxsz > 8192)
+ sc->sc_dhb16maxsz = 8192;
+ break;
+ }
+ }
+
+ if (tr_config(sc))
+ return 1;
+
+ /*
+ * init network-visible interface
+ */
+ bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
+ ifp->if_softc = sc;
+ ifp->if_ioctl = tr_ioctl;
+ if (sc->sc_init_status & FAST_PATH_TRANSMIT)
+ ifp->if_start = tr_start;
+ else
+ ifp->if_start = tr_oldstart;
+ ifp->if_flags = IFF_BROADCAST | IFF_NOTRAILERS;
+ ifp->if_watchdog = tr_watchdog;
+
+ switch (MM_INB(sc, TR_MEDIAS_OFFSET)) {
+ case 0xF:
+ nmedia = 1;
+ mediaptr = &media[6];
+ break;
+ case 0xE:
+ nmedia = 2;
+ mediaptr = &media[0];
+ break;
+ case 0xD:
+ nmedia = 1;
+ mediaptr = &media[4];
+ break;
+ default:
+ nmedia = 0;
+ mediaptr = NULL;
+ }
+
+ switch (MM_INB(sc, TR_RATES_OFFSET)) {
+ case 0xF:
+ /* 4 Mbps */
+ break;
+ case 0xE:
+ /* 16 Mbps */
+ if (mediaptr)
+ mediaptr += nmedia;
+ break;
+ case 0xD:
+ /* 4/16 Mbps */
+ nmedia *= 2;
+ break;
+ }
+
+ switch (MM_INB(sc, TR_MEDIA_OFFSET)) {
+ case 0xF:
+ /* STP */
+ defmediaptr = &media[6];
+ break;
+ case 0xE:
+ /* UTP */
+ defmediaptr = &media[4];
+ break;
+ case 0xD:
+ /* STP and UTP == a single shielded RJ45 which supports both */
+ /* XXX additional types in net/if_media.h ?? */
+ defmediaptr = &media[4];
+ break;
+ default:
+ defmediaptr = NULL;
+ }
+
+ if (defmediaptr && (sc->sc_init_status & RSP_16))
+ ++defmediaptr;
+
+ if (sc->sc_mediachange == NULL && sc->sc_mediastatus == NULL) {
+ switch (MM_INB(sc, TR_TYP_OFFSET)) {
+ case 0x0D:
+ case 0x0C:
+ sc->sc_mediachange = tropic_mediachange;
+ sc->sc_mediastatus = tropic_mediastatus;
+ }
+ }
+
+ ifmedia_init(&sc->sc_media, 0, tr_mediachange, tr_mediastatus);
+ if (mediaptr != NULL) {
+ for (i = 0; i < nmedia; i++)
+ ifmedia_add(&sc->sc_media, mediaptr[i], 0, NULL);
+ if (defmediaptr)
+ ifmedia_set(&sc->sc_media, *defmediaptr);
+ else
+ ifmedia_set(&sc->sc_media, 0);
+ }
+ else {
+ ifmedia_add(&sc->sc_media, IFM_TOKEN | IFM_MANUAL, 0, NULL);
+ ifmedia_set(&sc->sc_media, IFM_TOKEN | IFM_MANUAL);
+ }
+
+ if_attach(ifp);
+
+ for (i = 0, temp = 0; i < ISO88025_ADDR_LEN; i++, temp += 4) {
+ sc->sc_arpcom.ac_enaddr[i] =
+ (MM_INB(sc, (TR_MAC_OFFSET + temp)) & 0xf) << 4;
+ sc->sc_arpcom.ac_enaddr[i] |=
+ MM_INB(sc, (TR_MAC_OFFSET + temp + 2)) & 0xf;
+ }
+
+ token_ifattach(ifp);
+
+ printf("\n%s: address %s ring speed %d Mbps\n",
+ sc->sc_dev.dv_xname, token_sprintf(sc->sc_arpcom.ac_enaddr),
+ (sc->sc_init_status & RSP_16) ? 16 : 4);
+
+#if NBPFILTER > 0
+ bpfattach(&ifp->if_bpf, ifp, DLT_IEEE802, sizeof(struct token_header));
+#endif
+
+/*
+ * XXX rnd stuff
+ */
+ shutdownhook_establish(tr_shutdown, sc);
+ return 0;
+}
+
+int
+tr_setspeed(sc, speed)
+struct tr_softc *sc;
+u_int8_t speed;
+{
+ SRB_OUTB(sc, sc->sc_srb, SRB_CMD, DIR_SET_DEFAULT_RING_SPEED);
+ SRB_OUTB(sc, sc->sc_srb, CMD_RETCODE, 0xfe);
+ SRB_OUTB(sc, sc->sc_srb, SRB_SET_DEFRSP, speed);
+ /* Tell adapter: command in SRB. */
+ ACA_SETB(sc, ACA_ISRA_o, CMD_IN_SRB);
+
+ /* Wait for it to complete. */
+ tr_sleep(sc);
+
+ if ((SRB_INB(sc, sc->sc_srb, SRB_RETCODE) != 0)) {
+ printf("set default ringspeed returned: %02x\n",
+ SRB_INB(sc, sc->sc_srb, SRB_RETCODE));
+ return 1;
+ }
+ return 0;
+}
+
+int
+tr_mediachange(ifp)
+ struct ifnet *ifp;
+{
+ struct tr_softc *sc = ifp->if_softc;
+
+ if (sc->sc_mediachange)
+ return ((*sc->sc_mediachange)(sc));
+ return EINVAL;
+}
+
+void
+tr_mediastatus(ifp, ifmr)
+ struct ifnet *ifp;
+ struct ifmediareq *ifmr;
+{
+ struct tr_softc *sc = ifp->if_softc;
+
+/* set LINK0 and/or LINK1 */
+ if (sc->sc_mediastatus)
+ (*sc->sc_mediastatus)(sc, ifmr);
+}
+
+int
+tr_reset(sc)
+struct tr_softc *sc;
+{
+ int i;
+
+ sc->sc_srb = 0;
+
+ /*
+ * Reset the card.
+ */
+ /* latch on an unconditional adapter reset */
+ bus_space_write_1(sc->sc_piot, sc->sc_pioh, TR_RESET, 0);
+ delay(50000); /* delay 50ms */
+ /*
+ * XXX set paging if we have the right type of card
+ */
+ /* turn off adapter reset */
+ bus_space_write_1(sc->sc_piot, sc->sc_pioh, TR_RELEASE, 0);
+
+ /* Enable interrupts. */
+
+ ACA_SETB(sc, ACA_ISRP_e, INT_ENABLE);
+
+ /* Wait for an answer from the adapter. */
+
+ for (i = 0; i < 35000; i++) {
+ if (ACA_RDB(sc, ACA_ISRP_o) & SRB_RESP_INT)
+ break;
+ delay(100);
+ }
+
+ if (i == 35000 && sc->sc_srb == 0) {
+ printf("No response from adapter after reset\n");
+ return 1;
+ }
+
+ ACA_RSTB(sc, ACA_ISRP_o, ~(SRB_RESP_INT));
+
+ ACA_OUTB(sc, ACA_RRR_e, (sc->sc_maddr >> 12));
+ sc->sc_srb = ACA_RDW(sc, ACA_WRBR);
+ if (SRB_INB(sc, sc->sc_srb, SRB_CMD) != 0x80) {
+ printf("Initialization incomplete, status: %02x\n",
+ SRB_INB(sc, sc->sc_srb, SRB_CMD));
+ return 1;
+ }
+ if (SRB_INB(sc, sc->sc_srb, SRB_INIT_BUC) != 0) {
+ printf("Bring Up Code %02x\n",
+ SRB_INB(sc, sc->sc_srb, SRB_INIT_BUC));
+ return 1;
+ }
+
+ sc->sc_init_status = SRB_INB(sc, sc->sc_srb, SRB_INIT_STATUS);
+
+ sc->sc_xmit_head = sc->sc_xmit_tail = 0;
+
+ /* XXX should depend on sc_resvdmem. */
+ if (MM_INB(sc, TR_RAM_OFFSET) == 0xB && sc->sc_memsize == 65536)
+ for (i = 0; i < 512; i++)
+ SR_OUTB(sc, 0xfe00 + i, 0);
+ return 0;
+}
+
+/*
+ * tr_stop - stop interface (issue a DIR CLOSE ADAPTER command)
+ */
+void
+tr_stop(sc)
+struct tr_softc *sc;
+{
+ struct ifnet *ifp = &sc->sc_arpcom.ac_if;
+
+ if ((ifp->if_flags & IFF_RUNNING) != 0) {
+/*
+ * transmitter cannot be used from now on
+ */
+ ifp->if_flags |= IFF_OACTIVE;
+
+ /* Close command. */
+ SRB_OUTB(sc, sc->sc_srb, SRB_CMD, DIR_CLOSE);
+ /* Tell adapter: command in SRB. */
+ ACA_SETB(sc, ACA_ISRA_o, CMD_IN_SRB);
+
+ /* Wait for it to complete. */
+ tr_sleep(sc);
+ sc->sc_srb = ACA_RDW(sc, ACA_WRBR);
+ }
+}
+
+static void
+tr_shutdown(arg)
+ void *arg;
+{
+ struct tr_softc *sc = arg;
+
+ tr_stop(sc);
+}
+
+void
+tr_reinit(arg)
+ void *arg;
+{
+ if (tr_reset((struct tr_softc *) arg))
+ return;
+ if (tr_config((struct tr_softc *) arg))
+ return;
+ tr_init(arg);
+}
+
+/*
+ * tr_init - initialize network interface, open adapter for packet
+ * reception and start any pending output
+ */
+void
+tr_init(arg)
+ void *arg;
+{
+ struct tr_softc *sc = arg;
+ struct ifnet *ifp = &sc->sc_arpcom.ac_if;
+ bus_size_t open_srb;
+ int s, num_dhb;
+ int resvdmem, availmem, dhbsize;
+
+ if ((ifp->if_flags & IFF_RUNNING) != 0)
+ return;
+
+ s = splimp();
+
+ ifp->if_flags &= ~IFF_OACTIVE;
+ sc->sc_xmit_head = sc->sc_xmit_tail = 0; /* XXX tr_reset() */
+
+ open_srb = sc->sc_srb;
+
+ /* Zero SRB. */
+ bus_space_set_region_1(sc->sc_memt, sc->sc_sramh,
+ open_srb, 0, SRB_OPEN_CMDSIZE);
+
+ /* Open command. */
+ SRB_OUTB(sc, open_srb, SRB_CMD, DIR_OPEN_ADAPTER);
+/*
+ * XXX handle IFM_TOK_ETR !!!!
+ */
+ /* Set open parameters in SRB. */
+ SRB_OUTW(sc, open_srb, SRB_OPEN_OPTIONS, OPEN_PASS_BCON_MAC);
+
+ num_dhb = 1;
+
+ if ((sc->sc_init_status & FAST_PATH_TRANSMIT) == 0) {
+ availmem = sc->sc_memsize;
+ resvdmem = RESVDMEM_SIZE + sc->sc_memreserved;
+
+ /* allow MAX of two SAPS */
+ SRB_OUTB(sc, open_srb, SRB_OPEN_DLCMAXSAP, 2);
+ resvdmem += 2 * SAPCB_SIZE;
+
+ /* allow MAX of 4 stations */
+ SRB_OUTB(sc, open_srb, SRB_OPEN_DLCMAXSTA, 4);
+ resvdmem += 4 * LSCB_SIZE;
+
+ if (sc->sc_init_status & RSP_16) {
+ dhbsize = sc->sc_dhb16maxsz;
+ }
+ else {
+ dhbsize = sc->sc_dhb4maxsz;
+ }
+#if 0 /* XXXchb unneeded? */
+ if (dhbsize > 2048)
+ num_dhb = 2;
+#endif
+ SRB_OUTW(sc, open_srb, SRB_OPEN_DHBLEN, dhbsize);
+ sc->sc_nbuf = (dhbsize + 511) / 512;
+ /*
+ * Try to leave room for two fullsized packets when
+ * requesting DHBs.
+ */
+ availmem -= resvdmem;
+ num_dhb = (availmem / dhbsize) - 2;
+ if (num_dhb > 2)
+ num_dhb = 2; /* firmware can't cope with more DHBs */
+ if (num_dhb < 1)
+ num_dhb = 1; /* we need at least one */
+ }
+ else
+ SRB_OUTW(sc, open_srb, SRB_OPEN_DHBLEN, DHB_LENGTH);
+
+ SRB_OUTB(sc, open_srb, SRB_OPEN_NUMDHB, num_dhb);
+ SRB_OUTW(sc, open_srb, SRB_OPEN_RCVBUFLEN, RCV_BUF_LEN);
+ SRB_OUTW(sc, open_srb, SRB_OPEN_NUMRCVBUF, sc->sc_nbuf);
+
+ /* Tell adapter: command in SRB. */
+ ACA_SETB(sc, ACA_ISRA_o, CMD_IN_SRB);
+
+ splx(s);
+}
+
+/*
+ * tr_oldstart - Present transmit request to adapter
+ */
+void
+tr_oldstart(ifp)
+struct ifnet *ifp;
+{
+ struct tr_softc *sc = ifp->if_softc;
+ bus_size_t srb = sc->sc_srb;
+
+ if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
+ return;
+
+ ifp->if_flags |= IFF_OACTIVE;
+
+ /* Load SRB to request transmit. */
+ SRB_OUTB(sc, srb, SRB_CMD, XMIT_UI_FRM);
+ SRB_OUTW(sc, srb, XMIT_STATIONID, sc->exsap_station);
+ ACA_SETB(sc, ACA_ISRA_o, CMD_IN_SRB);
+}
+
+void
+tr_start(ifp)
+struct ifnet *ifp;
+{
+ struct tr_softc *sc = ifp->if_softc;
+ bus_size_t first_txbuf, txbuf;
+ struct mbuf *m0, *m;
+ int size, bufspace;
+ bus_size_t framedata;
+
+ if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
+ return;
+
+
+next:
+ if (sc->sc_xmit_buffers < sc->sc_minbuf)
+ return;
+
+ /* if data in queue, copy mbuf chain to fast path buffers */
+ IF_DEQUEUE(&ifp->if_snd, m0);
+
+ if (m0 == 0)
+ return;
+#if NBPFILTER > 0
+ if (ifp->if_bpf)
+ bpf_mtap(ifp->if_bpf, m0);
+#endif
+ first_txbuf = txbuf = TXCA_INW(sc, TXCA_FREE_QUEUE_HEAD) - XMIT_NEXTBUF;
+ framedata = txbuf + XMIT_FP_DATA;
+ size = 0;
+ bufspace = FP_BUF_LEN - XMIT_FP_DATA;
+ --sc->sc_xmit_buffers;
+ for (m = m0; m; m = m->m_next) {
+ int len = m->m_len;
+ char *ptr = mtod(m, char *);
+
+ while (len >= bufspace) {
+ --sc->sc_xmit_buffers;
+ bus_space_write_region_1(sc->sc_memt, sc->sc_sramh,
+ framedata, ptr, bufspace);
+ size += bufspace;
+ ptr += bufspace;
+ len -= bufspace;
+ TXB_OUTW(sc, txbuf, XMIT_BUFLEN,
+ (FP_BUF_LEN - XMIT_FP_DATA));
+ txbuf = TXB_INW(sc, txbuf, XMIT_NEXTBUF) - XMIT_NEXTBUF;
+ framedata = txbuf + XMIT_FP_DATA;
+ bufspace = FP_BUF_LEN - XMIT_FP_DATA;
+ }
+ if (len > 0) {
+ bus_space_write_region_1(sc->sc_memt, sc->sc_sramh,
+ framedata, ptr, len);
+ size += len;
+ bufspace -= len;
+ framedata += len;
+ }
+ }
+ TXB_OUTW(sc, txbuf, XMIT_BUFLEN, (FP_BUF_LEN - XMIT_FP_DATA - bufspace));
+ m_freem(m0); /* free mbuf chain */
+
+ TXB_OUTB(sc, first_txbuf, XMIT_RETCODE, 0xfe);
+ TXB_OUTW(sc, first_txbuf, XMIT_FRAMELEN, size);
+ TXB_OUTW(sc, first_txbuf, XMIT_LASTBUF, (txbuf + XMIT_NEXTBUF));
+ TXB_OUTB(sc, first_txbuf, XMIT_CMD, XMIT_DIR_FRAME);
+ TXB_OUTW(sc, first_txbuf, XMIT_STATIONID, 0);
+ TXB_OUTB(sc, first_txbuf, XMIT_CMDCORR, sc->sc_xmit_correlator);
+ sc->sc_xmit_correlator = (sc->sc_xmit_correlator + 1) & 0x7f;
+
+ /*
+ * To prevent race conditions on 8-bit cards when reading or writing
+ * 16-bit values. See page 4-12 of the IBM manual.
+ */
+ TXCA_OUTW(sc, TXCA_FREE_QUEUE_HEAD, 1);
+ TXCA_OUTW(sc, TXCA_FREE_QUEUE_HEAD, TXB_INW(sc, txbuf, XMIT_NEXTBUF));
+
+ ACA_SETB(sc, ACA_ISRA_o, XMIT_REQ);
+
+ ifp->if_flags |= IFF_OACTIVE;
+ ifp->if_opackets++;
+#if 1
+/* XXX do while construction */
+ goto next;
+#endif
+}
+
+
+#define IF_EMPTYQUEUE(queue) ((queue).ifq_head == 0)
+
+/*
+ * tr_intr - interrupt handler. Find the cause of the interrupt and
+ * service it.
+ */
+int
+tr_intr(arg)
+ void *arg;
+{
+ struct tr_softc *sc = arg;
+ struct ifnet *ifp = &sc->sc_arpcom.ac_if;
+ u_char status; /* holds status from adapter status register */
+ u_char command; /* holds command from status or request block */
+ u_char retcode; /* holds return value from status or request block */
+ int rc = 0; /* 0 = unclaimed interrupt, 1 = interrupt claimed */
+
+ status = ACA_RDB(sc, ACA_ISRP_o);
+ while (status != 0) {
+
+ /* Is this interrupt caused by an adapter check? */
+ if (status & ADAP_CHK_INT) {
+ printf("%s: adapter check 0x%04x\n",
+ sc->sc_dev.dv_xname,
+ (unsigned int)ntohs(ACA_RDW(sc, ACA_WWCR)));
+
+ /* Clear this interrupt bit */
+ ACA_RSTB(sc, ACA_ISRP_o, ~(ADAP_CHK_INT));
+
+ rc = 1; /* Claim interrupt. */
+ break; /* Terminate loop. */
+ }
+ else if (status & XMIT_COMPLETE) {
+ ACA_RSTB(sc, ACA_ISRP_o, ~(XMIT_COMPLETE));
+ tr_xint(sc);
+ rc = 1;
+ }
+
+ /*
+ * Process SRB_RESP_INT, ASB_FREE_INT, ARB_CMD_INT
+ * & SSB_RESP_INT in that order, ISRP-L Hi to Lo
+ */
+ else if (status & SRB_RESP_INT) { /* Adapter response in SRB? */
+ bus_size_t sap_srb;
+ bus_size_t srb;
+#ifdef TROPICDEBUG
+ bus_size_t log_srb;
+#endif
+ if (sc->sc_srb == 0)
+ sc->sc_srb = ACA_RDW(sc, ACA_WRBR);
+ srb = sc->sc_srb; /* pointer to SRB */
+ retcode = SRB_INB(sc, srb, SRB_RETCODE);
+ command = SRB_INB(sc, srb, SRB_CMD);
+ switch (command) {
+ case 0x80: /* 0x80 == initialization complete */
+ case DIR_CONFIG_FAST_PATH_RAM:
+ break;
+ case XMIT_DIR_FRAME: /* Response to xmit request */
+ case XMIT_UI_FRM: /* Response to xmit request */
+ /* Response not valid? */
+ if (retcode != 0xff)
+ printf("%s: error on xmit request =%x\n",
+ sc->sc_dev.dv_xname, retcode);
+ break;
+
+ case DIR_OPEN_ADAPTER: /* open-adapter-cmd response */
+ /* Open successful? */
+ if (retcode == 0) {
+ ifp->if_flags |= IFF_UP | IFF_RUNNING;
+ /* Save new ACA ctrl block addresses */
+ sc->sc_ssb = SRB_INW(sc, srb,
+ SRB_OPENRESP_SSBADDR);
+ sc->sc_arb = SRB_INW(sc, srb,
+ SRB_OPENRESP_ARBADDR);
+ sc->sc_srb = SRB_INW(sc, srb,
+ SRB_OPENRESP_SRBADDR);
+ sc->sc_asb = SRB_INW(sc, srb,
+ SRB_OPENRESP_ASBADDR);
+
+ /*
+ * XXX, what about LLC_{X25,ISO}_LSAP ?
+ * open two more saps .....
+ */
+ if (sc->sc_init_status &
+ FAST_PATH_TRANSMIT) {
+ sc->sc_xmit_buffers =
+ TXCA_INW(sc, TXCA_BUFFER_COUNT);
+ sc->sc_nbuf =
+ sc->sc_xmit_buffers;
+#ifdef TROPICDEBUG
+ printf("buffers = %d\n",
+ sc->sc_xmit_buffers);
+#endif
+ sc->sc_xmit_correlator = 0;
+ untimeout(tr_timeout, sc);
+ wakeup(&sc->tr_sleepevent);
+ }
+ else
+ tr_opensap(sc, LLC_SNAP_LSAP);
+ }
+ else {
+ printf("%s: Open error = %x\n",
+ sc->sc_dev.dv_xname,
+ SRB_INB(sc, srb, SRB_RETCODE));
+ ifp->if_flags &= ~IFF_RUNNING;
+ ifp->if_flags &= ~IFF_UP;
+/*
+ * XXX untimeout depending on the error, timeout in other cases
+ * XXX error 0x24 && autospeed mode: open again !!!!
+ */
+ timeout(tr_init, sc, hz*30);
+ }
+ break;
+
+ case DIR_CLOSE: /* Response to close adapter command */
+ /* Close not successful? */
+ if (retcode != 0)
+ printf("%s: close error = %x\n",
+ sc->sc_dev.dv_xname, retcode);
+ else {
+ ifp->if_flags &= ~IFF_RUNNING;
+ ifp->if_flags &= ~IFF_UP;
+ ifp->if_flags &= ~IFF_OACTIVE;
+ untimeout(tr_timeout, sc);
+ wakeup(&sc->tr_sleepevent);
+ }
+ break;
+ case DIR_SET_DEFAULT_RING_SPEED:
+ untimeout(tr_timeout, sc);
+ wakeup(&sc->tr_sleepevent);
+ break;
+
+ case DLC_OPEN_SAP: /* Response to open sap cmd */
+ sap_srb = sc->sc_srb;
+ if (SRB_INB(sc, sap_srb, SRB_OPNSAP_SAPVALUE)
+ == LLC_SNAP_LSAP)
+ sc->exsap_station =
+ SRB_INW(sc, sap_srb,
+ SRB_OPNSAP_STATIONID);
+ printf("%s: Token Ring opened\n",
+ sc->sc_dev.dv_xname);
+ untimeout(tr_timeout, sc);
+ wakeup(&sc->tr_sleepevent);
+ break;
+/* XXX DLC_CLOSE_SAP not needed ? */
+ case DLC_CLOSE_SAP: /* Response to close sap cmd */
+ break;
+ case DIR_READ_LOG: /* Response to read log */
+ /* Cmd not successful? */
+ if (retcode != 0)
+ printf("%s: read error log cmd err =%x\n",
+ sc->sc_dev.dv_xname, retcode);
+#ifdef TROPICDEBUG
+ log_srb = sc->sc_srb;
+ printf("%s: ERROR LOG:\n",sc->sc_dev.dv_xname);
+ printf("%s: Line=%d, Internal=%d, Burst=%d\n",
+ sc->sc_dev.dv_xname,
+ (SRB_INB(sc, log_srb, SRB_LOG_LINEERRS)),
+ (SRB_INB(sc, log_srb, SRB_LOG_INTERRS)),
+ (SRB_INB(sc, log_srb, SRB_LOG_BRSTERRS)));
+ printf("%s: A/C=%d, Abort=%d, Lost frames=%d\n",
+ sc->sc_dev.dv_xname,
+ (SRB_INB(sc, log_srb, SRB_LOG_ACERRS)),
+ (SRB_INB(sc, log_srb, SRB_LOG_ABRTERRS)),
+ (SRB_INB(sc, log_srb, SRB_LOG_LOSTFRMS)));
+ printf("%s: Receive congestion=%d, Frame copied=%d, Frequency=%d\n",
+ sc->sc_dev.dv_xname,
+ (SRB_INB(sc, log_srb, SRB_LOG_RCVCONG)),
+ (SRB_INB(sc, log_srb, SRB_LOG_FCPYERRS)),
+ (SRB_INB(sc, log_srb, SRB_LOG_FREQERRS)));
+ printf("%s: Token=%d\n",sc->sc_dev.dv_xname,
+ (SRB_INB(sc, log_srb, SRB_LOG_TOKENERRS)));
+#endif /* TROPICDEBUG */
+ ifp->if_flags &= ~IFF_OACTIVE;
+ break;
+ default:
+ printf("%s: bad SRB command encountered %x\n",
+ sc->sc_dev.dv_xname, command);
+ break;
+ }
+ /* clear the SRB-response interrupt bit */
+ ACA_RSTB(sc, ACA_ISRP_o, ~(SRB_RESP_INT));
+
+ }
+
+ else if (status & ASB_FREE_INT) { /* Is ASB Free? */
+ bus_size_t asb = sc->sc_asb;
+
+ /*
+ * Remove message from asb queue, first element in
+ * structure is the command. command == REC_DATA?
+ * size = 8 : size = 10
+ * reply in isra_l with (RESP_IN_ASB | ASB_FREE)
+ */
+ retcode = ASB_INB(sc, asb, CMD_RETCODE);
+ command = ASB_INB(sc, asb, CMD_CMD);
+ switch (command) {
+ case REC_DATA: /* Receive */
+ /* Response not valid? */
+ if (retcode != 0xff)
+ printf("%s: ASB bad receive response =%x\n",
+ sc->sc_dev.dv_xname, retcode);
+ break;
+ case XMIT_DIR_FRAME: /* Transmit */
+ case XMIT_UI_FRM: /* Transmit */
+ /* Response not valid? */
+ if (retcode != 0xff)
+ printf("%s: ASB response err on xmit =%x\n",
+ sc->sc_dev.dv_xname, retcode);
+ break;
+ default:
+ printf("%s: Invalid command in ASB =%x\n",
+ sc->sc_dev.dv_xname, command);
+ break;
+ }
+ /* Clear this interrupt bit */
+ ACA_RSTB(sc, ACA_ISRP_o, ~(ASB_FREE_INT));
+ }
+ else if (status & ARB_CMD_INT) { /* Command for PC to handle? */
+ bus_size_t arb = sc->sc_arb;
+
+ command = ARB_INB(sc, arb, ARB_CMD);
+ switch (command) {
+ case DLC_STATUS: /* DLC status change */
+ printf("%s: ARB new DLC status = 0x%x\n",
+ sc->sc_dev.dv_xname,
+ ARB_INW(sc, arb, ARB_DLCSTAT_STATUS));
+ break;
+ case REC_DATA: /* Adapter has data for PC */
+ /* Call receive interrupt handler */
+ tr_rint(sc);
+ break;
+
+ case RING_STAT_CHANGE: /* Ring status change */
+ if (ARB_INW(sc, arb, ARB_RINGSTATUS) &
+ (SIGNAL_LOSS + LOBE_FAULT)){
+ printf("%s: SIGNAL LOSS/LOBE FAULT\n",
+ sc->sc_dev.dv_xname);
+ ifp->if_flags &= ~IFF_RUNNING;
+ ifp->if_flags &= ~IFF_UP;
+ if_qflush(&ifp->if_snd);
+ timeout(tr_reinit, sc ,hz*30);
+ }
+ else {
+#ifdef TROPICDEBUG
+ if (ARB_INW(sc, arb, ARB_RINGSTATUS) &
+ ~(SOFT_ERR))
+ printf(
+ "%s: ARB new ring status = 0x%x\n",
+ sc->sc_dev.dv_xname,
+ ARB_INW(sc, arb,
+ ARB_RINGSTATUS));
+#endif /* TROPICDEBUG */
+ }
+ if (ARB_INW(sc, arb, ARB_RINGSTATUS) &
+ LOG_OFLOW){
+/*
+ * XXX CMD_IN_SRB, handle with SRB_FREE_INT ?
+ */
+ ifp->if_flags |= IFF_OACTIVE;
+ SRB_OUTB(sc, sc->sc_srb, SRB_CMD,
+ DIR_READ_LOG);
+ /* Read & reset err log cmnd in SRB. */
+ ACA_SETB(sc, ACA_ISRA_o, CMD_IN_SRB);
+ }
+ break;
+
+ case XMIT_DATA_REQ: /* Adapter wants data to transmit */
+ /* Call transmit interrupt handler */
+ tr_oldxint(sc);
+ break;
+
+ default:
+ printf("%s: Invalid command in ARB =%x\n",
+ sc->sc_dev.dv_xname, command);
+ break;
+ }
+
+ /* Clear this interrupt bit */
+ ACA_RSTB(sc, ACA_ISRP_o, ~(ARB_CMD_INT));
+
+ /* Tell adapter that ARB is now free */
+ ACA_SETB(sc, ACA_ISRA_o, ARB_FREE);
+ }
+
+
+ else if (status & SSB_RESP_INT) { /* SSB resp. to SRB cmd? */
+ bus_size_t ssb = sc->sc_ssb;
+
+ retcode = SSB_INB(sc, ssb, SSB_RETCODE);
+ command = SSB_INB(sc, ssb, SSB_CMD);
+ switch (command) {
+ case XMIT_UI_FRM:
+ case XMIT_DIR_FRAME: /* SSB response to SRB xmit cmd */
+ /* collect status on last packet */
+ if (retcode != 0) {
+ printf("xmit return code = 0x%x\n",
+ retcode);
+ /* XXXchb */
+ if (retcode == 0x22) {
+ printf("FS = 0x%2x\n",
+ SSB_INB(sc, ssb,
+ SSB_XMITERR));
+ }
+ ifp->if_oerrors++;
+ }
+ else
+ ifp->if_opackets++;
+
+ ifp->if_flags &= ~IFF_OACTIVE;
+/*
+ * XXX should this be done here ?
+ */
+ /* if data on send queue */
+ if (!IF_EMPTYQUEUE(ifp->if_snd))
+ tr_oldstart(ifp);
+ break;
+
+ case XMIT_XID_CMD:
+ printf("tr_int: xmit XID return code = 0x%x\n",
+ retcode);
+ break;
+ default:
+ printf("%s: SSB error, invalid command =%x\n",
+ sc->sc_dev.dv_xname, command);
+ }
+ /* clear this interrupt bit */
+ ACA_RSTB(sc, ACA_ISRP_o, ~(SSB_RESP_INT));
+
+ /* tell adapter that SSB is available */
+ ACA_SETB(sc, ACA_ISRA_o, SSB_FREE);
+ }
+ rc = 1; /* Claim responsibility for interrupt */
+ status = ACA_RDB(sc, ACA_ISRP_o);
+ }
+ /* Is this interrupt caused by an adapter error or access violation? */
+ if (ACA_RDB(sc, ACA_ISRP_e) & (TCR_INT | ERR_INT | ACCESS_INT)) {
+ printf("%s: adapter error, ISRP_e = %x\n",
+ sc->sc_dev.dv_xname, ACA_RDB(sc, ACA_ISRP_e));
+
+ /* Clear these interrupt bits */
+ ACA_RSTB(sc, ACA_ISRP_e, ~(TCR_INT | ERR_INT | ACCESS_INT));
+ rc = 1; /* Claim responsibility for interrupt */
+
+ }
+
+ /* Clear IRQ latch in order to reenable interrupts. */
+ bus_space_write_1(sc->sc_piot, sc->sc_pioh, TR_CLEARINT, 0);
+ return (rc);
+}
+
+#ifdef notyet
+int asb_reply_rcv()
+{
+}
+
+int asb_reply_xmit()
+{
+}
+
+int asb_response(bus_size_t asb, size_t len)
+{
+ if (empty_queue) {
+ answer with RESP_IN_ASB | ASB_FREE
+ }
+ else {
+ put asb in queue
+ }
+}
+#endif
+
+
+/*
+ * U-B receive interrupt.
+ *
+ * in the original version, this routine had three tasks:
+ *
+ * 1. move the data into the receive buffer and set up various pointers
+ * in the tr_softc struct
+ * 2. switch on the type field for ip and arp, dropping all else
+ * 3. resetting the adaptor status block info (asb) and updating the
+ * tr_softc struct
+ * determine lan message type, pull packet off interface and
+ * pass to an appropriate higher-level routine
+ *
+ */
+void
+tr_rint(sc)
+struct tr_softc *sc;
+{
+ bus_size_t arb = sc->sc_arb;
+ bus_size_t asb = sc->sc_asb;
+ struct rbcb *rbc = &sc->rbc;
+ struct mbuf *m;
+ struct ifnet *ifp = &sc->sc_arpcom.ac_if;
+
+#ifdef TROPICDEBUG
+ printf("tr_rint: arb.command = %x, arb.station_id= %x\n",
+ ARB_INB(sc, arb, ARB_CMD), ARB_INW(sc, arb, ARB_STATIONID));
+ printf("arb.buf_addr = %x, arb.lan_hdr_len = %x\n",
+ ARB_INW(sc, arb, ARB_RXD_BUFADDR),
+ ARB_INB(sc, arb, ARB_RXD_LANHDRLEN));
+ printf("arb.dlc_hdr_len = %d, arb.frame_len = %d\n",
+ ARB_INB(sc, arb, ARB_RXD_DLCHDRLEN),
+ ARB_INW(sc, arb, ARB_RXD_FRAMELEN));
+ printf("arb.msg_type = %x\n", ARB_INB(sc, arb, ARB_RXD_MSGTYPE));
+#endif /* TROPICDEBUG */
+ /*
+ * copy the offset in RAM of the first receive buffer from the
+ * receive-data block of the adapter request block associated
+ * with the unit's softc struct into the receive control block.
+ */
+ rbc->rbufp = ARB_INW(sc, arb, ARB_RXD_BUFADDR);
+
+ /*
+ * copy the pointer to data in first receive buffer
+ */
+ rbc->rbuf_datap = rbc->rbufp + RB_DATA;
+ /*
+ * the token-ring header is viewed as two header structs: the physical
+ * header (aka TR header) with access, frame, dest, src, and routing
+ * information, and the logical link control header (aka LLC header)
+ * with dsap, ssap, llc, proto and type fields.
+ *
+ * rfc1042 requires support for unnumbered information (UI) commands,
+ * but does not specify a required semantic, so we'll discard them.
+ *
+ */
+
+ /*
+ * if there is a second receive buffer, set up the next pointer
+ */
+ if (RB_INW(sc, rbc->rbufp, RB_NEXTBUF))
+ rbc->rbufp_next = RB_INW(sc, rbc->rbufp, RB_NEXTBUF) -
+ RB_NEXTBUF;
+ else
+ rbc->rbufp_next = 0; /* we're finished */
+
+ rbc->data_len = RB_INW(sc, rbc->rbufp, RB_BUFLEN);
+ /*
+ * At this point we move the packet from the adapter to a chain
+ * of mbufs
+ */
+ m = tr_get(sc, ARB_INW(sc, arb, ARB_RXD_FRAMELEN), ifp);
+/*
+ * XXX Clear ARB interrupt here?
+ */
+/*
+ * XXX create a queue where the responses are buffered
+ * XXX but is it really needed ?
+ */
+
+ if (ASB_INB(sc, asb, RECV_RETCODE) != 0xff)
+ printf("tr_rint: ASB IS NOT FREE!!!\n");
+ /*
+ * Load receive response into ASB.
+ */
+ ASB_OUTB(sc, asb, RECV_CMD, REC_DATA);
+ ASB_OUTW(sc, asb, RECV_STATIONID, ARB_INW(sc, arb, ARB_STATIONID));
+ ASB_OUTW(sc, asb, RECV_RESP_RECBUFADDR,
+ ARB_INW(sc, arb, ARB_RXD_BUFADDR));
+
+ if (m == 0) {
+ /*
+ * Tell adapter data lost, no mbufs.
+ */
+ ASB_OUTB(sc, asb, RECV_RETCODE, 0x20);
+ ACA_SETB(sc, ACA_ISRA_o, RESP_IN_ASB);
+ ++ifp->if_ierrors;
+#ifdef TROPICDEBUG
+ printf("tr_rint: packet dropped\n");
+#endif /* TROPICDEBUG */
+ }
+ else {
+ /*
+ * Indicate successful receive.
+ */
+ ASB_OUTB(sc, asb, RECV_RETCODE, 0);
+ ACA_SETB(sc, ACA_ISRA_o, RESP_IN_ASB);
+ ++ifp->if_ipackets;
+
+#if NBPFILTER > 0
+ if (ifp->if_bpf)
+ bpf_mtap(ifp->if_bpf, m);
+#endif
+ token_input(ifp, m);
+ }
+}
+
+/*
+ * Interrupt handler for old style "adapter requires data to transmit".
+ */
+void
+tr_oldxint(sc)
+struct tr_softc *sc;
+{
+ bus_size_t arb = sc->sc_arb; /* pointer to ARB */
+ bus_size_t asb = sc->sc_asb; /* pointer to ASB */
+ bus_size_t dhb; /* pointer to DHB */
+ struct mbuf *m0; /* pointer to top of mbuf chain */
+ u_short size = 0;
+ char command;
+ struct ifnet *ifp = &sc->sc_arpcom.ac_if;
+ struct token_header *trh;
+ int i;
+ u_int8_t hlen;
+
+/*
+ * XXX xmit_asb_response()
+ */
+ if (ASB_INB(sc, asb, XMIT_RETCODE) != 0xff)
+ printf("tr_oldxint: ASB IS NOT FREE!!!\n");
+
+ /* load parameters into ASB */
+ ASB_OUTB(sc, asb, XMIT_CMDCORR, ARB_INB(sc, arb, ARB_XMT_CMDCORR));
+ ASB_OUTW(sc, asb, XMIT_STATIONID, ARB_INW(sc, arb, ARB_STATIONID));
+ ASB_OUTB(sc, asb, XMIT_RETCODE, 0);
+/*
+ * XXX LLC_{X25,ISO}_LSAP
+ */
+ ASB_OUTB(sc, asb, XMIT_REMSAP, LLC_SNAP_LSAP);
+
+ /* XXX if num_dhb == 2 this should alternate between the two buffers */
+ dhb = ARB_INW(sc, arb, ARB_XMT_DHBADDR);
+
+ command = SRB_INB(sc, sc->sc_srb, SRB_CMD);
+
+ if (command == XMIT_XID_CMD || command == XMIT_TEST_CMD) {
+ ASB_OUTB(sc, asb, XMIT_CMD, command);
+ ASB_OUTW(sc, asb, XMIT_FRAMELEN, 0x11);
+/*
+ * XXX 0xe == sizeof(struct token_header)
+ */
+ ASB_OUTB(sc, asb, XMIT_HDRLEN, 0x0e);
+
+ SR_OUTB(sc, (dhb + 0), TOKEN_AC);
+ SR_OUTB(sc, (dhb + 1), TOKEN_FC);
+ /* Load destination and source addresses. */
+ for (i=0; i < ISO88025_ADDR_LEN; i++) {
+ SR_OUTB(sc, (dhb + 2 + i), 0xff);
+ SR_OUTB(sc, (dhb + 8 + i), 0x00);
+ }
+ }
+ else {
+/*
+ * XXX what's command here ? command = 0x0d (always ?)
+ */
+ /* if data in queue, copy mbuf chain to DHB */
+ IF_DEQUEUE(&ifp->if_snd, m0);
+ if (m0 != 0) {
+#if NBPFILTER > 0
+ if (ifp->if_bpf)
+ bpf_mtap(ifp->if_bpf, m0);
+#endif
+ /* Pull packet off interface send queue, fill DHB. */
+ trh = mtod(m0, struct token_header *);
+ hlen = sizeof(struct token_header);
+ if (trh->token_shost[0] & TOKEN_RI_PRESENT) {
+/*
+ * XXX assumes route info is in the same mbuf as the token-ring header
+ */
+ struct token_rif *rif;
+
+ rif = TOKEN_RIF(trh);
+ hlen += ((ntohs(rif->tr_rcf) & TOKEN_RCF_LEN_MASK) >> 8);
+ }
+ size = tr_mbcopy(sc, dhb, m0);
+ m_freem(m0);
+
+ ASB_OUTB(sc, asb, XMIT_CMD, XMIT_UI_FRM);
+ ASB_OUTB(sc, asb, XMIT_HDRLEN, hlen);
+
+ /* Set size of transmission frame in ASB. */
+ ASB_OUTW(sc, asb, XMIT_FRAMELEN, size);
+ }
+ else {
+ printf("%s: unexpected empty mbuf send queue\n",
+ sc->sc_dev.dv_xname);
+
+ /* Set size of transmission frame in ASB to zero. */
+ ASB_OUTW(sc, asb, XMIT_FRAMELEN, 0);
+ }
+ }
+/*
+ * XXX asb_response(void *asb, len)
+ */
+ /* tell adapter that there is a response in the ASB */
+ ACA_SETB(sc, ACA_ISRA_o, RESP_IN_ASB);
+}
+
+/*
+ * Interrupt handler for fast path transmit complete
+ */
+void
+tr_xint(sc)
+struct tr_softc *sc;
+{
+ u_short tail;
+ struct ifnet *ifp = &sc->sc_arpcom.ac_if;
+ bus_size_t txbuf;
+
+ /*
+ * To prevent race conditions on 8-bit cards when reading or writing
+ * 16-bit values. See page 4-12 of the IBM manual.
+ * XXX use volatile ?
+ */
+ do {
+ tail = TXCA_INW(sc, TXCA_COMPLETION_QUEUE_TAIL);
+ } while (tail != TXCA_INW(sc, TXCA_COMPLETION_QUEUE_TAIL));
+ while (tail != TXCA_INW(sc, TXCA_FREE_QUEUE_TAIL)) {
+ txbuf = TXCA_INW(sc, TXCA_FREE_QUEUE_TAIL) - XMIT_NEXTBUF;
+ txbuf = TXB_INW(sc, txbuf, XMIT_NEXTBUF) - XMIT_NEXTBUF;
+ if (TXB_INB(sc, txbuf, XMIT_RETCODE) != 0) {
+ ifp->if_oerrors++;
+ printf("tx: retcode = %x\n",
+ TXB_INB(sc, txbuf, XMIT_RETCODE));
+ }
+ sc->sc_xmit_buffers +=
+ (TXB_INW(sc, txbuf, XMIT_FRAMELEN) + 514 - 1) / 514;
+ tail = TXB_INW(sc, txbuf, XMIT_LASTBUF);
+ TXCA_OUTW(sc, TXCA_FREE_QUEUE_TAIL, tail);
+ tail = TXCA_INW(sc, TXCA_COMPLETION_QUEUE_TAIL);
+ do {
+ tail = TXCA_INW(sc, TXCA_COMPLETION_QUEUE_TAIL);
+ } while (tail != TXCA_INW(sc, TXCA_COMPLETION_QUEUE_TAIL));
+ }
+ if (sc->sc_xmit_buffers == sc->sc_nbuf)
+ ifp->if_flags &= ~IFF_OACTIVE;
+ tr_start(ifp);
+}
+
+
+/*
+ * copy out the packet byte-by-byte in resonably optimal fashion
+ */
+int
+tr_mbcopy(sc, dhb, m0)
+struct tr_softc *sc;
+bus_size_t dhb;
+struct mbuf *m0;
+{
+ bus_size_t addr = dhb;
+ int len, size = 0;
+ char *ptr;
+ struct mbuf *m;
+
+ for (m = m0; m; m = m->m_next) {
+ len = m->m_len;
+ ptr = mtod(m, char *);
+
+ bus_space_write_region_1(sc->sc_memt, sc->sc_sramh,
+ addr, ptr, len);
+ size += len;
+ addr += len;
+ }
+ return (size);
+}
+
+/*
+ * Pull read data off an interface.
+ * Len is length of data, with local net header stripped.
+ * Off is non-zero if a trailer protocol was used, and
+ * gives the offset of the trailer information.
+ * XXX trailer information, really ????
+ * We copy the trailer information and then all the normal
+ * data into mbufs.
+ *
+ * called from tr_rint - receive interupt routine
+ */
+struct mbuf *
+tr_get(sc, totlen, ifp)
+struct tr_softc *sc;
+int totlen;
+struct ifnet *ifp;
+{
+ int len;
+ struct mbuf *m, *m0, *newm;
+
+ MGETHDR(m0, M_DONTWAIT, MT_DATA);
+ if (m0 == 0)
+ return (0);
+
+ m0->m_pkthdr.rcvif = ifp;
+ m0->m_pkthdr.len = totlen;
+ len = MHLEN;
+
+ m = m0;
+ while (totlen > 0) {
+ if (totlen >= MINCLSIZE) {
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ m_free(m0);
+ return 0;
+ }
+ len = MCLBYTES;
+ }
+
+ /*
+ * Make sure data after the MAC header is aligned.
+ */
+ if (m == m0) {
+ caddr_t newdata = (caddr_t)
+ ALIGN(m->m_data + sizeof(struct token_header)) -
+ sizeof(struct token_header);
+ len -= newdata - m->m_data;
+ m->m_data = newdata;
+ }
+ m->m_len = len = min(totlen, len);
+ tr_bcopy(sc, mtod(m, char *), len);
+ totlen -= len;
+ if (totlen > 0) {
+ MGET(newm, M_DONTWAIT, MT_DATA);
+ if (newm == 0){
+ m_freem(m0);
+ return (0);
+ }
+ m->m_next = newm;
+ m = newm;
+ len = MLEN;
+ }
+ /*
+ * ignore trailers case again
+ */
+ }
+ return (m0);
+}
+
+/*
+ * tr_ioctl - process an ioctl request
+ */
+int
+tr_ioctl(ifp, cmd, data)
+struct ifnet *ifp;
+u_long cmd;
+caddr_t data;
+{
+ struct tr_softc *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *) data;
+ struct ifaddr *ifa = (struct ifaddr *) data;
+ int s;
+ int error = 0;
+
+ s = splnet();
+
+ switch (cmd) {
+ case SIOCSIFADDR:
+
+ switch (ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ /* XXX if not running */
+ if ((ifp->if_flags & IFF_RUNNING) == 0) {
+ tr_init(sc); /* before arp_ifinit */
+ tr_sleep(sc);
+ }
+ arp_ifinit(&sc->sc_arpcom, ifa);
+ break;
+#endif INET
+ default:
+ /* XXX if not running */
+ if ((ifp->if_flags & IFF_RUNNING) == 0) {
+ tr_init(sc); /* before arpwhohas */
+ tr_sleep(sc);
+ }
+ break;
+ }
+ break;
+ case SIOCSIFFLAGS:
+ /*
+ * 1- If the adapter is DOWN , turn the device off
+ * ie. adapter down but still running
+ * 2- If the adapter is UP, turn the device on
+ * ie. adapter up but not running yet
+ */
+ if ((ifp->if_flags & (IFF_RUNNING | IFF_UP)) == IFF_RUNNING) {
+ tr_stop(sc);
+ ifp->if_flags &= ~IFF_RUNNING;
+ }
+ else if ((ifp->if_flags & (IFF_RUNNING | IFF_UP)) == IFF_UP) {
+ tr_init(sc);
+ tr_sleep(sc);
+ }
+ else {
+/*
+ * XXX handle other flag changes
+ */
+ }
+ break;
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
+ break;
+#ifdef SIOCSIFMTU
+ case SIOCSIFMTU:
+ if (ifr->ifr_mtu > sc->sc_maxmtu)
+ error = EINVAL;
+ else
+ ifp->if_mtu = ifr->ifr_mtu;
+ break;
+#endif
+ default:
+ error = EINVAL;
+ }
+ splx(s);
+ return (error);
+}
+
+
+/*
+ * tr_bcopy - like bcopy except that it knows about the structure of
+ * adapter receive buffers.
+ */
+void
+tr_bcopy(sc, dest, len)
+struct tr_softc *sc; /* pointer to softc struct for this adapter */
+u_char *dest; /* destination address */
+int len; /* number of bytes to copy */
+{
+ struct rbcb *rbc = &sc->rbc; /* pointer to rec buf ctl blk */
+
+ /* While amount of data needed >= amount in current receive buffer. */
+ while (len >= rbc->data_len) {
+ /* Copy all data from receive buffer to destination. */
+
+ bus_space_read_region_1(sc->sc_memt, sc->sc_sramh,
+ rbc->rbuf_datap, dest, (bus_size_t)rbc->data_len);
+ len -= rbc->data_len; /* update length left to transfer */
+ dest += rbc->data_len; /* update destination address */
+
+ /* Make next receive buffer current receive buffer. */
+ rbc->rbufp = rbc->rbufp_next;
+ if (rbc->rbufp != 0) { /* More receive buffers? */
+
+ /* Calculate pointer to next receive buffer. */
+ rbc->rbufp_next = RB_INW(sc, rbc->rbufp, RB_NEXTBUF);
+ if (rbc->rbufp_next != 0)
+ rbc->rbufp_next -= RB_NEXTBUF;
+
+ /* Get pointer to data in current receive buffer. */
+ rbc->rbuf_datap = rbc->rbufp + RB_DATA;
+
+ /* Get length of data in current receive buffer. */
+ rbc->data_len = RB_INW(sc, rbc->rbufp, RB_BUFLEN);
+ }
+ else {
+ if (len != 0) /* len should equal zero. */
+ printf("tr_bcopy: residual data not copied\n");
+ return;
+ }
+ }
+
+ /* Amount of data needed is < amount in current receive buffer. */
+
+ bus_space_read_region_1(sc->sc_memt, sc->sc_sramh,
+ rbc->rbuf_datap, dest, (bus_size_t)len);
+ rbc->data_len -= len; /* Update count of data in receive buffer. */
+ rbc->rbuf_datap += len; /* Update pointer to receive buffer data. */
+}
+
+/*
+ * tr_opensap - open the token ring SAP interface
+ */
+void
+tr_opensap(sc, type)
+struct tr_softc *sc;
+u_char type;
+{
+ bus_size_t srb = sc->sc_srb;
+
+/************************************************************************
+ ** To use the SAP level interface, we will have to execute a **
+ ** DLC.OPEN.SAP (pg.6-61 of the Token Ring Tech. Ref.) after we have **
+ ** received a good return code from the DIR.OPEN.ADAPTER command. **
+ ** We will open the IP SAP x'aa'. **
+ ** **
+ ** STEPS: **
+ ** 1) Reset SRB response interrupt bit **
+ ** 2) Use the open_sap srb. **
+ ** 3) Fill the following fields: **
+ ** command - x'15' **
+ ** sap_value - x'aa' **
+ ** sap_options- x'24' **
+ ** **
+ ***********************************************************************/
+
+ ACA_RSTB(sc, ACA_ISRP_o, ~(SRB_RESP_INT));
+
+ SRB_OUTB(sc, srb, SRB_CMD, DLC_OPEN_SAP);
+ SRB_OUTB(sc, srb, SRB_RETCODE, 0x00);
+ SRB_OUTW(sc, srb, SRB_OPNSAP_STATIONID, 0x0000);
+ SRB_OUTB(sc, srb, SRB_OPNSAP_TIMERT1, 0x00);
+ SRB_OUTB(sc, srb, SRB_OPNSAP_TIMERT2, 0x00);
+ SRB_OUTB(sc, srb, SRB_OPNSAP_TIMERTI, 0x00);
+ SRB_OUTB(sc, srb, SRB_OPNSAP_MAXOUT, 0x00);
+ SRB_OUTB(sc, srb, SRB_OPNSAP_MAXIN, 0x00);
+ SRB_OUTB(sc, srb, SRB_OPNSAP_MAXOUTINCR, 0x00);
+ SRB_OUTB(sc, srb, SRB_OPNSAP_MAXRETRY, 0x00);
+ SRB_OUTB(sc, srb, SRB_OPNSAP_GSAPMAXMEMB, 0x00);
+ SRB_OUTW(sc, srb, SRB_OPNSAP_MAXIFIELD, 0x0088);
+ SRB_OUTB(sc, srb, SRB_OPNSAP_SAPVALUE, type);
+ SRB_OUTB(sc, srb, SRB_OPNSAP_SAPOPTIONS, 0x24);
+ SRB_OUTB(sc, srb, SRB_OPNSAP_STATIONCNT, 0x01);
+ SRB_OUTB(sc, srb, SRB_OPNSAP_SAPGSAPMEMB, 0x00);
+
+ ACA_SETB(sc, ACA_ISRP_e, INT_ENABLE);
+ ACA_SETB(sc, ACA_ISRA_o, CMD_IN_SRB);
+}
+
+/*
+ * tr_sleep - sleep to wait for adapter to open
+ */
+void
+tr_sleep(sc)
+struct tr_softc *sc;
+{
+ timeout(tr_timeout, sc, hz*30);
+ sleep(&sc->tr_sleepevent, 1);
+}
+
+void
+tr_watchdog(ifp)
+struct ifnet *ifp;
+{
+ struct tr_softc *sc = ifp->if_softc;
+
+ log(LOG_ERR,"%s: device timeout\n", sc->sc_dev.dv_xname);
+ ++ifp->if_oerrors;
+
+ tr_reset(sc);
+}
+
+/*
+ * tr_timeout - timeout routine if adapter does not open in 30 seconds
+ */
+void
+tr_timeout(arg)
+void *arg;
+{
+ struct tr_softc *sc = arg;
+
+ printf("Token Ring timeout\n");
+ wakeup(&sc->tr_sleepevent);
+}