123300Smckusick /* 223300Smckusick * Copyright (c) 1982 Regents of the University of California. 323300Smckusick * All rights reserved. The Berkeley software License Agreement 423300Smckusick * specifies the terms and conditions for redistribution. 523300Smckusick * 6*26301Skarels * @(#)if_pcl.c 6.7 (Berkeley) 02/20/86 723300Smckusick */ 811816Ssam 911816Ssam #include "pcl.h" 1011816Ssam #if NPCL > 0 1111816Ssam /* 1211816Ssam * DEC CSS PCL-11B Parallel Communications Interface 1311816Ssam * 1413800Ssam * Written by Mike Muuss and Jeff Schwab. 1511816Ssam */ 1611816Ssam #include "../machine/pte.h" 1711816Ssam 1817116Sbloom #include "param.h" 1917116Sbloom #include "systm.h" 2017116Sbloom #include "mbuf.h" 2117116Sbloom #include "buf.h" 2217116Sbloom #include "protosw.h" 2317116Sbloom #include "socket.h" 2417116Sbloom #include "vmmac.h" 2517116Sbloom #include "ioctl.h" 2617116Sbloom #include "errno.h" 2711816Ssam 2811816Ssam #include "../net/if.h" 2911816Ssam #include "../net/netisr.h" 3011816Ssam #include "../net/route.h" 3124792Skarels 3211816Ssam #include "../netinet/in.h" 3311816Ssam #include "../netinet/in_systm.h" 3411816Ssam #include "../netinet/ip.h" 3511816Ssam 3611816Ssam #include "../vax/cpu.h" 3711816Ssam #include "../vax/mtpr.h" 3817116Sbloom #include "if_pclreg.h" 3917116Sbloom #include "if_uba.h" 4011816Ssam #include "../vaxuba/ubareg.h" 4111816Ssam #include "../vaxuba/ubavar.h" 4211816Ssam 4311816Ssam /* The MTU has been carefully selected to prevent fragmentation <-> ArpaNet */ 4413800Ssam #define PCLMTU (1006) /* Max transmission unit (bytes) */ 4513800Ssam #define PCLMAXTDM 7 /* Max unit number on TDM bus */ 4611816Ssam 4711816Ssam int pclprobe(), pclattach(), pclrint(), pclxint(); 4813064Ssam int pclinit(), pclioctl(), pcloutput(), pclreset(); 4911816Ssam 5011816Ssam struct uba_device *pclinfo[NPCL]; 5111816Ssam u_short pclstd[] = { 0 }; 5211816Ssam #define PCLUNIT(x) minor(x) 5311816Ssam struct uba_driver pcldriver = 5411816Ssam { pclprobe, 0, pclattach, 0, pclstd, "pcl", pclinfo }; 5511816Ssam 5611816Ssam /* 5711816Ssam * PCL software status per interface. 5811816Ssam * 5911816Ssam * Each interface is referenced by a network interface structure, 6011816Ssam * sc_if, which the routing code uses to locate the interface. 6111816Ssam * This structure contains the output queue for the interface, its address, ... 6211816Ssam * We also have, for each interface, a UBA interface structure, which 6311816Ssam * contains information about the UNIBUS resources held by the interface: 6411816Ssam * map registers, buffered data paths, etc. Information is cached in this 6511816Ssam * structure for use by the if_uba.c routines in running the interface 6611816Ssam * efficiently. 6711816Ssam */ 6811816Ssam struct pcl_softc { 6911816Ssam struct ifnet sc_if; /* network-visible interface */ 7011816Ssam struct ifuba sc_ifuba; /* UNIBUS resources */ 7111816Ssam short sc_oactive; /* is output active? */ 7211816Ssam short sc_olen; /* length of last output */ 7311816Ssam short sc_lastdest; /* previous destination */ 7411816Ssam short sc_odest; /* current xmit destination */ 7513800Ssam short sc_bdest; /* buffer's stated destination */ 7611816Ssam short sc_pattern; /* identification pattern */ 7711816Ssam } pcl_softc[NPCL]; 7811816Ssam 7911816Ssam /* 8011816Ssam * Structure of "local header", which only goes between 8111816Ssam * pcloutput and pclstart. 8211816Ssam */ 8311816Ssam struct pcl_header { 8411816Ssam short pcl_dest; /* Destination PCL station */ 8511816Ssam }; 8611816Ssam 8711816Ssam /* 8811816Ssam * Do non-DMA output of 1 word to determine presence of interface, 8911816Ssam * and to find the interupt vector. 1 word messages are a special 9011816Ssam * case in the receiver routine, and will be discarded. 9111816Ssam */ 9211816Ssam pclprobe(reg) 9311816Ssam caddr_t reg; 9411816Ssam { 9511816Ssam register int br, cvec; /* r11, r10 value-result */ 9611816Ssam register struct pcldevice *addr = (struct pcldevice *)reg; 9711816Ssam 9811816Ssam #ifdef lint 9911816Ssam br = 0; cvec = br; br = cvec; 10011816Ssam pclrint(0); pclxint(0); 10111816Ssam #endif 10211816Ssam addr->pcl_rcr = PCL_RCINIT; 10311816Ssam addr->pcl_tcr = PCL_TXINIT; 10411816Ssam addr->pcl_tsba = 0xFFFE; 10511816Ssam /* going for 01777776 */ 10611816Ssam addr->pcl_tsbc = -4; /* really short */ 10711816Ssam addr->pcl_tcr = 10811816Ssam ((1 & 0xF) << 8) | PCL_TXNPR | PCL_SNDWD | PCL_STTXM | PCL_IE | 0x0030; 10911816Ssam DELAY(100000); 11011816Ssam addr->pcl_tcr = PCL_TXINIT; 11111816Ssam return (sizeof (struct pcldevice)); 11211816Ssam } 11311816Ssam 11411816Ssam /* 11511816Ssam * Interface exists: make available by filling in network interface 11611816Ssam * record. System will initialize the interface when it is ready 11711816Ssam * to accept packets. 11811816Ssam */ 11911816Ssam pclattach(ui) 12011816Ssam struct uba_device *ui; 12111816Ssam { 12211816Ssam register struct pcl_softc *sc = &pcl_softc[ui->ui_unit]; 12311816Ssam 12411816Ssam sc->sc_if.if_unit = ui->ui_unit; 12511816Ssam sc->sc_if.if_name = "pcl"; 12611816Ssam sc->sc_if.if_mtu = PCLMTU; 12711816Ssam sc->sc_if.if_init = pclinit; 12811816Ssam sc->sc_if.if_output = pcloutput; 12913064Ssam sc->sc_if.if_ioctl = pclioctl; 13011816Ssam sc->sc_if.if_reset = pclreset; 13121774Skarels sc->sc_if.if_flags = IFF_BROADCAST; 13211816Ssam sc->sc_ifuba.ifu_flags = UBA_NEEDBDP; 13311816Ssam if_attach(&sc->sc_if); 13411816Ssam } 13511816Ssam 13611816Ssam /* 13711816Ssam * Reset of interface after UNIBUS reset. 13811816Ssam * If interface is on specified uba, reset its state. 13911816Ssam */ 14011816Ssam pclreset(unit, uban) 14111816Ssam int unit, uban; 14211816Ssam { 14311816Ssam register struct uba_device *ui; 14411816Ssam 14511816Ssam if (unit >= NPCL || (ui = pclinfo[unit]) == 0 || ui->ui_alive == 0 || 14611816Ssam ui->ui_ubanum != uban) 14711816Ssam return; 14811816Ssam printf(" pcl%d", unit); 14911816Ssam pclinit(unit); 15011816Ssam } 15111816Ssam 15211816Ssam /* 15311816Ssam * Initialization of interface; clear recorded pending 15411816Ssam * operations, and reinitialize UNIBUS usage. 15511816Ssam */ 15611816Ssam pclinit(unit) 15711816Ssam int unit; 15811816Ssam { 15911816Ssam register struct pcl_softc *sc = &pcl_softc[unit]; 16011816Ssam register struct uba_device *ui = pclinfo[unit]; 16111816Ssam register struct pcldevice *addr; 16211816Ssam int s; 16311816Ssam 16421774Skarels if (sc->sc_if.if_addrlist == (struct ifaddr *)0) 16513064Ssam return; 16611816Ssam if (if_ubainit(&sc->sc_ifuba, ui->ui_ubanum, 0, 16711816Ssam (int)btoc(PCLMTU)) == 0) { 16811816Ssam printf("pcl%d: can't init\n", unit); 16921774Skarels sc->sc_if.if_flags &= ~(IFF_UP | IFF_RUNNING); 17011816Ssam return; 17111816Ssam } 17221774Skarels sc->sc_if.if_flags |= IFF_RUNNING; 17311816Ssam addr = (struct pcldevice *)ui->ui_addr; 17411816Ssam addr->pcl_rcr = PCL_RCINIT; 17511816Ssam addr->pcl_tcr = PCL_TXINIT; 17611816Ssam 17711816Ssam /* 17811816Ssam * Hang a receive and start any 17911816Ssam * pending writes by faking a transmit complete. 18011816Ssam */ 18111816Ssam s = splimp(); 18211816Ssam addr->pcl_rdba = (short) sc->sc_ifuba.ifu_r.ifrw_info; 18311816Ssam addr->pcl_rdbc = -PCLMTU; 18411816Ssam addr->pcl_rcr = (((int)(sc->sc_ifuba.ifu_r.ifrw_info>>12))&0x0030) | 18511816Ssam PCL_RCNPR | PCL_RCVWD | PCL_RCVDAT | PCL_IE; 18611816Ssam sc->sc_oactive = 0; 18711816Ssam pclstart(unit); 18811816Ssam splx(s); 18911816Ssam } 19011816Ssam 19111816Ssam /* 19211816Ssam * PCL output routine. 19311816Ssam */ 19411816Ssam pcloutput(ifp, m, dst) 19511816Ssam struct ifnet *ifp; 19611816Ssam struct mbuf *m; 19711816Ssam struct sockaddr *dst; 19811816Ssam { 19913089Ssam int dest, s, error; 20011816Ssam struct pcl_header *pclp; 20111816Ssam struct mbuf *m2; 20211816Ssam 20311816Ssam switch (dst->sa_family) { 20411816Ssam 20511816Ssam #ifdef INET 20611816Ssam case AF_INET: 20721774Skarels if (in_broadcast(((struct sockaddr_in *)dst)->sin_addr)) 20821774Skarels dest = 0; 20921774Skarels else 21021774Skarels dest = in_lnaof(((struct sockaddr_in *)dst)->sin_addr); 21113800Ssam if (dest > PCLMAXTDM) { 21213800Ssam error = EHOSTUNREACH; 21313800Ssam goto bad; 21413800Ssam } 21511816Ssam break; 21611816Ssam #endif 21711816Ssam default: 21811816Ssam printf("pcl%d: can't handle af%d\n", ifp->if_unit, 21911816Ssam dst->sa_family); 22011816Ssam error = EAFNOSUPPORT; 22111816Ssam goto bad; 22211816Ssam } 22311816Ssam 22411816Ssam /* 22511816Ssam * Add pseudo local net header. 22611816Ssam * Actually, it does not get transmitted, but merely stripped 22711816Ssam * off and used by the START routine to route the packet. 22811816Ssam * If no space in first mbuf, allocate another. 22911816Ssam */ 23011816Ssam if (m->m_off > MMAXOFF || 23111816Ssam MMINOFF + sizeof (struct pcl_header) > m->m_off) { 23211816Ssam m2 = m_get(M_DONTWAIT, MT_HEADER); 23311816Ssam if (m2 == 0) { 23411816Ssam error = ENOBUFS; 23511816Ssam goto bad; 23611816Ssam } 23711816Ssam m2->m_next = m; 23811816Ssam m2->m_off = MMINOFF; 23911816Ssam m2->m_len = sizeof (struct pcl_header); 24011816Ssam m = m2; 24111816Ssam } else { 24211816Ssam m->m_off -= sizeof (struct pcl_header); 24311816Ssam m->m_len += sizeof (struct pcl_header); 24411816Ssam } 24511816Ssam pclp = mtod(m, struct pcl_header *); 24611816Ssam pclp->pcl_dest = dest; 24711816Ssam 24811816Ssam /* 24911816Ssam * Queue message on interface, and start output if interface 25011816Ssam * not yet active. 25111816Ssam */ 25211816Ssam s = splimp(); 25311816Ssam if (IF_QFULL(&ifp->if_snd)) { 25411816Ssam IF_DROP(&ifp->if_snd); 25511816Ssam error = ENOBUFS; 25611816Ssam goto qfull; 25711816Ssam } 25811816Ssam IF_ENQUEUE(&ifp->if_snd, m); 25911816Ssam if (pcl_softc[ifp->if_unit].sc_oactive == 0) 26011816Ssam pclstart(ifp->if_unit); 26111816Ssam splx(s); 26211816Ssam return (0); 26311816Ssam qfull: 26411816Ssam splx(s); 26511816Ssam bad: 26611816Ssam m_freem(m); 26711816Ssam return (error); 26811816Ssam } 26911816Ssam 27011816Ssam /* 27111816Ssam * Start or restart output on interface. 27211816Ssam * If interface is already active, then this is a retransmit. 27311816Ssam * If interface is not already active, get another datagram 27411816Ssam * to send off of the interface queue, and map it to the interface 27511816Ssam * before starting the output. 27611816Ssam */ 27711816Ssam pclstart(dev) 27811816Ssam dev_t dev; 27911816Ssam { 28011816Ssam int unit = PCLUNIT(dev); 28111816Ssam struct uba_device *ui = pclinfo[unit]; 28211816Ssam register struct pcl_softc *sc = &pcl_softc[unit]; 28311816Ssam register struct pcldevice *addr; 28411816Ssam struct mbuf *m; 28511816Ssam 28611816Ssam if (sc->sc_oactive) 28711816Ssam goto restart; 28811816Ssam 28911816Ssam /* 29011816Ssam * Not already active: dequeue another request 29111816Ssam * and map it to the UNIBUS. If no more requests, 29211816Ssam * just return. 29311816Ssam */ 29411816Ssam IF_DEQUEUE(&sc->sc_if.if_snd, m); 29511816Ssam if (m == 0) { 29611816Ssam sc->sc_oactive = 0; 29711816Ssam return; 29811816Ssam } 29911816Ssam 30011816Ssam /* 30111816Ssam * Pull destination node out of pseudo-local net header. 30211816Ssam * remove it from outbound data. 30311816Ssam * Note that if_wubaput calls m_bcopy, which is prepared for 30411816Ssam * m_len to be 0 in the first mbuf in the chain. 30511816Ssam */ 30613800Ssam sc->sc_bdest = mtod(m, struct pcl_header *)->pcl_dest; 30713800Ssam sc->sc_odest = sc->sc_bdest? sc->sc_bdest: 1; 30811816Ssam m->m_off += sizeof (struct pcl_header); 30911816Ssam m->m_len -= sizeof (struct pcl_header); 31011816Ssam 31111816Ssam /* Map out to the DMA area */ 31211816Ssam sc->sc_olen = if_wubaput(&sc->sc_ifuba, m); 31311816Ssam 31411816Ssam restart: 31511816Ssam /* 31611816Ssam * Have request mapped to UNIBUS for transmission. 31713800Ssam * Purge any stale data from this BDP, and start the output. 31811816Ssam */ 31911816Ssam if (sc->sc_ifuba.ifu_flags & UBA_NEEDBDP) 32011816Ssam UBAPURGE(sc->sc_ifuba.ifu_uba, sc->sc_ifuba.ifu_w.ifrw_bdp); 32111816Ssam addr = (struct pcldevice *)ui->ui_addr; 32211816Ssam addr->pcl_tcr = PCL_TXINIT; 32311816Ssam addr->pcl_tsba = (int)sc->sc_ifuba.ifu_w.ifrw_info; 32411816Ssam addr->pcl_tsbc = -sc->sc_olen; 32511816Ssam 32611816Ssam /* 32711816Ssam * RIB (retry if busy) is used on the second and subsequent packets 32811816Ssam * to a single host, because TCP often wants to transmit multiple 32911816Ssam * buffers in a row, 33011816Ssam * and if they are all going to the same place, the second and 33111816Ssam * subsequent ones may be lost due to receiver not ready again yet. 33211816Ssam * This can cause serious problems, because the TCP will resend the 33311816Ssam * whole window, which just repeats the problem. The result is that 33411816Ssam * a perfectly good link appears not to work unless we take steps here. 33511816Ssam */ 33611816Ssam addr->pcl_tcr = (((int)(sc->sc_ifuba.ifu_w.ifrw_info>>12))&0x0030) | 33711816Ssam ((sc->sc_odest & 0xF)<<8) | 33811816Ssam PCL_TXNPR | PCL_SNDWD | PCL_STTXM | PCL_IE | 33911816Ssam (sc->sc_odest == sc->sc_lastdest ? PCL_RIB : 0); 34011816Ssam sc->sc_lastdest = sc->sc_odest; 34111816Ssam sc->sc_oactive = 1; 34211816Ssam } 34311816Ssam 34411816Ssam /* 34511816Ssam * PCL transmitter interrupt. 34611816Ssam * Start another output if more data to send. 34711816Ssam */ 34811816Ssam pclxint(unit) 34911816Ssam int unit; 35011816Ssam { 35111816Ssam register struct uba_device *ui = pclinfo[unit]; 35211816Ssam register struct pcl_softc *sc = &pcl_softc[unit]; 35311816Ssam register struct pcldevice *addr = (struct pcldevice *)ui->ui_addr; 35411816Ssam 35511816Ssam if (sc->sc_oactive == 0) { 35611816Ssam printf ("pcl%d: stray interrupt\n", unit); 35711816Ssam return; 35811816Ssam } 35911816Ssam if (addr->pcl_tsr & PCL_ERR) { 36011816Ssam sc->sc_lastdest = 0; /* don't bother with RIB */ 36111816Ssam if (addr->pcl_tsr & PCL_MSTDWN) { 36211816Ssam addr->pcl_tmmr = PCL_MASTER|PCL_AUTOADDR; 36311816Ssam pclstart(unit); /* Retry */ 36411816Ssam printf("pcl%d: master\n", unit ); 36511816Ssam return; 36611816Ssam } 36713800Ssam #ifndef PCL_TESTING 36813800Ssam if ((addr->pcl_tsr & (PCL_ERR|PCL_RESPB)) == (PCL_ERR|0)) { 36913800Ssam ; /* Receiver Offline -- not exactly an error */ 37013800Ssam } else { 37113800Ssam #else 37213800Ssam { 37313800Ssam #endif 37411816Ssam /* Log as an error */ 37511816Ssam printf("pcl%d: send error, tcr=%b tsr=%b\n", 37611816Ssam unit, addr->pcl_tcr, PCL_TCSRBITS, 37711816Ssam addr->pcl_tsr, PCL_TERRBITS); 37811816Ssam sc->sc_if.if_oerrors++; 37911816Ssam } 38011816Ssam } else 38111816Ssam sc->sc_if.if_opackets++; 38213800Ssam if (sc->sc_bdest == 0 && sc->sc_odest < PCLMAXTDM) { 38313800Ssam sc->sc_odest++; /* do next host (broadcast) */ 38413800Ssam } else { 38513800Ssam sc->sc_oactive = 0; 38613800Ssam if (sc->sc_ifuba.ifu_xtofree) { 38713800Ssam m_freem(sc->sc_ifuba.ifu_xtofree); 38813800Ssam sc->sc_ifuba.ifu_xtofree = 0; 38913800Ssam } 39011816Ssam } 39111816Ssam pclstart(unit); 39211816Ssam } 39311816Ssam 39411816Ssam /* 39511816Ssam * PCL interface receiver interrupt. 39611816Ssam * If input error just drop packet. 39711816Ssam */ 39811816Ssam pclrint(unit) 39911816Ssam int unit; 40011816Ssam { 40111816Ssam register struct pcl_softc *sc = &pcl_softc[unit]; 40211816Ssam struct pcldevice *addr = (struct pcldevice *)pclinfo[unit]->ui_addr; 40311816Ssam struct mbuf *m; 40413089Ssam int len; 40511816Ssam register struct ifqueue *inq; 40611816Ssam 40711816Ssam sc->sc_if.if_ipackets++; 40811816Ssam /* 40911816Ssam * Purge BDP; drop if input error indicated. 41011816Ssam */ 41111816Ssam if (sc->sc_ifuba.ifu_flags & UBA_NEEDBDP) 41211816Ssam UBAPURGE(sc->sc_ifuba.ifu_uba, sc->sc_ifuba.ifu_r.ifrw_bdp); 41311816Ssam if (addr->pcl_rsr & PCL_ERR) { 41411816Ssam printf("pcl%d: rcv error, rcr=%b rsr=%b\n", 41511816Ssam unit, addr->pcl_rcr, PCL_RCSRBITS, 41611816Ssam addr->pcl_rsr, PCL_RERRBITS); 41711816Ssam sc->sc_if.if_ierrors++; 41811816Ssam goto setup; 41911816Ssam } 42011816Ssam len = PCLMTU + addr->pcl_rdbc; 42111816Ssam if (len <= 0 || len > PCLMTU) { 42211816Ssam printf("pcl%d: bad len=%d.\n", unit, len); 42311816Ssam sc->sc_if.if_ierrors++; 42411816Ssam goto setup; 42511816Ssam } 42611816Ssam 42711816Ssam /* Really short packets will be part of the startup sequence */ 42811816Ssam if (len <= 4) { 42911816Ssam /* Later, do comming-up processing here */ 43011816Ssam goto setup; /* drop packet */ 43111816Ssam } 43211816Ssam 43311816Ssam /* 43411816Ssam * Pull packet off interface. 43511816Ssam */ 43624792Skarels m = if_rubaget(&sc->sc_ifuba, len, 0, &sc->sc_if); 43711816Ssam if (m == 0) 43811816Ssam goto setup; 43911816Ssam 44011816Ssam schednetisr(NETISR_IP); 44111816Ssam inq = &ipintrq; 44211816Ssam 44311816Ssam if (IF_QFULL(inq)) { 44411816Ssam IF_DROP(inq); 44511816Ssam m_freem(m); 44611816Ssam } else 44711816Ssam IF_ENQUEUE(inq, m); 44811816Ssam setup: 44911816Ssam /* 45011816Ssam * Reset for next packet. 45111816Ssam */ 45211816Ssam addr->pcl_rcr = PCL_RCINIT; 45311816Ssam addr->pcl_rdba = (int)sc->sc_ifuba.ifu_r.ifrw_info; 45411816Ssam addr->pcl_rdbc = -PCLMTU; 45511816Ssam addr->pcl_rcr = (((int)(sc->sc_ifuba.ifu_w.ifrw_info>>12))&0x0030) | 45611816Ssam PCL_RCNPR | PCL_RCVWD | PCL_RCVDAT | PCL_IE; 45711816Ssam } 45813064Ssam 45913064Ssam /* 46013064Ssam * Process an ioctl request. 46113064Ssam */ 462*26301Skarels /* ARGSUSED */ 46313064Ssam pclioctl(ifp, cmd, data) 46413064Ssam register struct ifnet *ifp; 46513064Ssam int cmd; 46613064Ssam caddr_t data; 46713064Ssam { 46813064Ssam int s = splimp(), error = 0; 46913064Ssam 47013064Ssam switch (cmd) { 47113064Ssam 47213064Ssam case SIOCSIFADDR: 47324792Skarels ifp->if_flags |= IFF_UP; 47421774Skarels if ((ifp->if_flags & IFF_RUNNING) == 0) 47513064Ssam pclinit(ifp->if_unit); 47613064Ssam break; 47713064Ssam 47813064Ssam default: 47913064Ssam error = EINVAL; 48013064Ssam } 48113064Ssam splx(s); 48213064Ssam return (error); 48313064Ssam } 48411816Ssam #endif 485