/* $NetBSD: tropic.c,v 1.20 2003/01/06 13:05:13 wiz 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 __KERNEL_RCSID(0, "$NetBSD: tropic.c,v 1.20 2003/01/06 13:05:13 wiz Exp $"); #include "opt_inet.h" #include "opt_ns.h" #include "bpfilter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #include #include #include #endif #ifdef NS #include #include #endif #if NBPFILTER > 0 #include #include #endif #include #include #include #include static void tr_shutdown __P((void *)); static void tr_reopen __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)); 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 *)); /* * 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("%s: no response for fast path cfg\n", sc->sc_dev.dv_xname); return 1; } ACA_RSTB(sc, ACA_ISRP_o, ~(SRB_RESP_INT)); if ((SRB_INB(sc, sc->sc_srb, SRB_RETCODE) != 0)) { printf("%s: cfg fast path returned: 0x%02x\n", sc->sc_dev.dv_xname, 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; u_int8_t myaddr[ISO88025_ADDR_LEN]; struct ifnet *ifp = &sc->sc_ethercom.ec_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 */ strcpy(ifp->if_xname, sc->sc_dev.dv_xname); 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; IFQ_SET_READY(&ifp->if_snd); 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) { myaddr[i] = (MM_INB(sc, (TR_MAC_OFFSET + temp)) & 0xf) << 4; myaddr[i] |= MM_INB(sc, (TR_MAC_OFFSET + temp + 2)) & 0xf; } token_ifattach(ifp, myaddr); printf("%s: address %s ring speed %d Mbps\n", sc->sc_dev.dv_xname, token_sprintf(myaddr), (sc->sc_init_status & RSP_16) ? 16 : 4); callout_init(&sc->sc_init_callout); callout_init(&sc->sc_reinit_callout); sc->sc_sdhook = 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("%s: set default ringspeed returned: 0x%02x\n", sc->sc_dev.dv_xname, 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("%s: no response from adapter after reset\n", sc->sc_dev.dv_xname); 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("%s: initialization incomplete, status: 0x%02x\n", sc->sc_dev.dv_xname, SRB_INB(sc, sc->sc_srb, SRB_CMD)); return 1; } if (SRB_INB(sc, sc->sc_srb, SRB_INIT_BUC) != 0) { printf("%s: Bring Up Code %02x\n", sc->sc_dev.dv_xname, 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_ethercom.ec_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; { struct tr_softc *sc = arg; int s; s = splnet(); if (tr_reset(sc) == 0) { if (tr_config(sc) == 0) tr_init(arg); } splx(s); } static void tr_reopen(arg) void *arg; { int s; s = splnet(); tr_init(arg); splx(s); } /* * tr_init - initialize network interface, open adapter for packet * - reception and start any pending output * - must be called at splnet */ void tr_init(arg) void *arg; { struct tr_softc *sc = arg; struct ifnet *ifp = &sc->sc_ethercom.ec_if; bus_size_t open_srb; int num_dhb, resvdmem, availmem, dhbsize; if ((ifp->if_flags & IFF_RUNNING) != 0) return; 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); } /* * 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 */ IFQ_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; 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; } } if (size % (FP_BUF_LEN - XMIT_FP_DATA)) { --sc->sc_xmit_buffers; 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 } /* * 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_ethercom.ec_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 = " "0x%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("%s: %d buffers\n", sc->sc_dev.dv_xname, sc->sc_xmit_buffers); #endif sc->sc_xmit_correlator = 0; wakeup(&sc->tr_sleepevent); } else tr_opensap(sc, LLC_SNAP_LSAP); } else { printf("%s: open error = 0x%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 !!!! */ callout_reset(&sc->sc_init_callout, hz * 30, tr_reopen, sc); } break; case DIR_CLOSE: /* Response to close adapter command */ /* Close not successful? */ if (retcode != 0) printf("%s: close error = 0x%x\n", sc->sc_dev.dv_xname, retcode); else { ifp->if_flags &= ~IFF_RUNNING; ifp->if_flags &= ~IFF_UP; ifp->if_flags &= ~IFF_OACTIVE; wakeup(&sc->tr_sleepevent); } break; case DIR_SET_DEFAULT_RING_SPEED: 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); 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 = " "0x%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 0x%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 = 0x%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 = 0x%x\n", sc->sc_dev.dv_xname, retcode); break; default: printf("%s: invalid command in ASB = 0x%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; IFQ_PURGE(&ifp->if_snd); callout_reset(&sc->sc_reinit_callout, hz * 30, tr_reinit, sc); } 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 = 0x%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("%s: xmit return code = 0x%x\n", sc->sc_dev.dv_xname, retcode); /* XXXchb */ if (retcode == 0x22) { printf("%s: FS = 0x%2x\n", sc->sc_dev.dv_xname, 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 (IFQ_IS_EMPTY(&ifp->if_snd) == 0) 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 = 0x%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_ethercom.ec_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 (*ifp->if_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_ethercom.ec_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 */ IFQ_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_ethercom.ec_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("%s: xmit error = 0x%x\n", sc->sc_dev.dv_xname, 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 interrupt 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: if ((error = tr_enable(sc)) != 0) break; 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(ifp, 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; tr_disable(sc); } else if ((ifp->if_flags & (IFF_RUNNING | IFF_UP)) == IFF_UP) { if ((error = tr_enable(sc)) != 0) break; 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; { int error; error = tsleep(&sc->tr_sleepevent, 1, "trsleep", hz * 30); if (error == EWOULDBLOCK) printf("%s: sleep event timeout\n", sc->sc_dev.dv_xname); } 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); } int tr_enable(sc) struct tr_softc *sc; { if (sc->sc_enabled == 0 && sc->sc_enable != NULL) { if ((*sc->sc_enable)(sc) != 0) { printf("%s: device enable failed\n", sc->sc_dev.dv_xname); return (EIO); } } sc->sc_enabled = 1; return (0); } void tr_disable(sc) struct tr_softc *sc; { if (sc->sc_enabled != 0 && sc->sc_disable != NULL) { (*sc->sc_disable)(sc); sc->sc_enabled = 0; } } int tr_activate(self, act) struct device *self; enum devact act; { struct tr_softc *sc = (struct tr_softc *)self; struct ifnet *ifp = &sc->sc_ethercom.ec_if; int rv = 0, s; s = splnet(); switch (act) { case DVACT_ACTIVATE: rv = EOPNOTSUPP; break; case DVACT_DEACTIVATE: if_deactivate(ifp); break; } splx(s); return (rv); } int tr_detach(self, flags) struct device *self; int flags; { struct tr_softc *sc = (struct tr_softc *)self; struct ifnet *ifp = &sc->sc_ethercom.ec_if; tr_disable(sc); callout_stop(&sc->sc_init_callout); callout_stop(&sc->sc_reinit_callout); /* Delete all remaining media. */ ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY); token_ifdetach(ifp); if_detach(ifp); shutdownhook_disestablish(sc->sc_sdhook); return (0); }