diff options
author | Federico G. Schwindt <fgsch@cvs.openbsd.org> | 1999-12-27 21:51:36 +0000 |
---|---|---|
committer | Federico G. Schwindt <fgsch@cvs.openbsd.org> | 1999-12-27 21:51:36 +0000 |
commit | d22c519e835f6ff88c8df7f1ee3837808ed67a4b (patch) | |
tree | 32685fa72e7c6b42e4fceadac4bac4e7f18a3dc2 /sys/dev/ic/tropic.c | |
parent | 27c6ceea191523e7370c6ecd6a6837d88fe6a13b (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.c | 1748 |
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); +} |