1*21774Skarels /* if_pcl.c 6.3 85/06/03 */ 211816Ssam 311816Ssam #include "pcl.h" 411816Ssam #if NPCL > 0 511816Ssam /* 611816Ssam * DEC CSS PCL-11B Parallel Communications Interface 711816Ssam * 813800Ssam * Written by Mike Muuss and Jeff Schwab. 911816Ssam */ 1011816Ssam #include "../machine/pte.h" 1111816Ssam 1217116Sbloom #include "param.h" 1317116Sbloom #include "systm.h" 1417116Sbloom #include "mbuf.h" 1517116Sbloom #include "buf.h" 1617116Sbloom #include "protosw.h" 1717116Sbloom #include "socket.h" 1817116Sbloom #include "vmmac.h" 1917116Sbloom #include "ioctl.h" 2017116Sbloom #include "errno.h" 2111816Ssam 2211816Ssam #include "../net/if.h" 2311816Ssam #include "../net/netisr.h" 2411816Ssam #include "../net/route.h" 2511816Ssam #include "../netinet/in.h" 2611816Ssam #include "../netinet/in_systm.h" 2711816Ssam #include "../netinet/ip.h" 2811816Ssam #include "../netinet/ip_var.h" 2911816Ssam 3011816Ssam #include "../vax/cpu.h" 3111816Ssam #include "../vax/mtpr.h" 3217116Sbloom #include "if_pclreg.h" 3317116Sbloom #include "if_uba.h" 3411816Ssam #include "../vaxuba/ubareg.h" 3511816Ssam #include "../vaxuba/ubavar.h" 3611816Ssam 3711816Ssam /* The MTU has been carefully selected to prevent fragmentation <-> ArpaNet */ 3813800Ssam #define PCLMTU (1006) /* Max transmission unit (bytes) */ 3913800Ssam #define PCLMAXTDM 7 /* Max unit number on TDM bus */ 4011816Ssam 4111816Ssam int pclprobe(), pclattach(), pclrint(), pclxint(); 4213064Ssam int pclinit(), pclioctl(), pcloutput(), pclreset(); 4311816Ssam 4411816Ssam struct uba_device *pclinfo[NPCL]; 4511816Ssam u_short pclstd[] = { 0 }; 4611816Ssam #define PCLUNIT(x) minor(x) 4711816Ssam struct uba_driver pcldriver = 4811816Ssam { pclprobe, 0, pclattach, 0, pclstd, "pcl", pclinfo }; 4911816Ssam 5011816Ssam /* 5111816Ssam * PCL software status per interface. 5211816Ssam * 5311816Ssam * Each interface is referenced by a network interface structure, 5411816Ssam * sc_if, which the routing code uses to locate the interface. 5511816Ssam * This structure contains the output queue for the interface, its address, ... 5611816Ssam * We also have, for each interface, a UBA interface structure, which 5711816Ssam * contains information about the UNIBUS resources held by the interface: 5811816Ssam * map registers, buffered data paths, etc. Information is cached in this 5911816Ssam * structure for use by the if_uba.c routines in running the interface 6011816Ssam * efficiently. 6111816Ssam */ 6211816Ssam struct pcl_softc { 6311816Ssam struct ifnet sc_if; /* network-visible interface */ 6411816Ssam struct ifuba sc_ifuba; /* UNIBUS resources */ 6511816Ssam short sc_oactive; /* is output active? */ 6611816Ssam short sc_olen; /* length of last output */ 6711816Ssam short sc_lastdest; /* previous destination */ 6811816Ssam short sc_odest; /* current xmit destination */ 6913800Ssam short sc_bdest; /* buffer's stated destination */ 7011816Ssam short sc_pattern; /* identification pattern */ 7111816Ssam } pcl_softc[NPCL]; 7211816Ssam 7311816Ssam /* 7411816Ssam * Structure of "local header", which only goes between 7511816Ssam * pcloutput and pclstart. 7611816Ssam */ 7711816Ssam struct pcl_header { 7811816Ssam short pcl_dest; /* Destination PCL station */ 7911816Ssam }; 8011816Ssam 8111816Ssam /* 8211816Ssam * Do non-DMA output of 1 word to determine presence of interface, 8311816Ssam * and to find the interupt vector. 1 word messages are a special 8411816Ssam * case in the receiver routine, and will be discarded. 8511816Ssam */ 8611816Ssam pclprobe(reg) 8711816Ssam caddr_t reg; 8811816Ssam { 8911816Ssam register int br, cvec; /* r11, r10 value-result */ 9011816Ssam register struct pcldevice *addr = (struct pcldevice *)reg; 9111816Ssam 9211816Ssam #ifdef lint 9311816Ssam br = 0; cvec = br; br = cvec; 9411816Ssam pclrint(0); pclxint(0); 9511816Ssam #endif 9611816Ssam addr->pcl_rcr = PCL_RCINIT; 9711816Ssam addr->pcl_tcr = PCL_TXINIT; 9811816Ssam addr->pcl_tsba = 0xFFFE; 9911816Ssam /* going for 01777776 */ 10011816Ssam addr->pcl_tsbc = -4; /* really short */ 10111816Ssam addr->pcl_tcr = 10211816Ssam ((1 & 0xF) << 8) | PCL_TXNPR | PCL_SNDWD | PCL_STTXM | PCL_IE | 0x0030; 10311816Ssam DELAY(100000); 10411816Ssam addr->pcl_tcr = PCL_TXINIT; 10511816Ssam return (sizeof (struct pcldevice)); 10611816Ssam } 10711816Ssam 10811816Ssam /* 10911816Ssam * Interface exists: make available by filling in network interface 11011816Ssam * record. System will initialize the interface when it is ready 11111816Ssam * to accept packets. 11211816Ssam */ 11311816Ssam pclattach(ui) 11411816Ssam struct uba_device *ui; 11511816Ssam { 11611816Ssam register struct pcl_softc *sc = &pcl_softc[ui->ui_unit]; 11711816Ssam 11811816Ssam sc->sc_if.if_unit = ui->ui_unit; 11911816Ssam sc->sc_if.if_name = "pcl"; 12011816Ssam sc->sc_if.if_mtu = PCLMTU; 12111816Ssam sc->sc_if.if_init = pclinit; 12211816Ssam sc->sc_if.if_output = pcloutput; 12313064Ssam sc->sc_if.if_ioctl = pclioctl; 12411816Ssam sc->sc_if.if_reset = pclreset; 125*21774Skarels sc->sc_if.if_flags = IFF_BROADCAST; 12611816Ssam sc->sc_ifuba.ifu_flags = UBA_NEEDBDP; 12711816Ssam if_attach(&sc->sc_if); 12811816Ssam } 12911816Ssam 13011816Ssam /* 13111816Ssam * Reset of interface after UNIBUS reset. 13211816Ssam * If interface is on specified uba, reset its state. 13311816Ssam */ 13411816Ssam pclreset(unit, uban) 13511816Ssam int unit, uban; 13611816Ssam { 13711816Ssam register struct uba_device *ui; 13811816Ssam 13911816Ssam if (unit >= NPCL || (ui = pclinfo[unit]) == 0 || ui->ui_alive == 0 || 14011816Ssam ui->ui_ubanum != uban) 14111816Ssam return; 14211816Ssam printf(" pcl%d", unit); 14311816Ssam pclinit(unit); 14411816Ssam } 14511816Ssam 14611816Ssam /* 14711816Ssam * Initialization of interface; clear recorded pending 14811816Ssam * operations, and reinitialize UNIBUS usage. 14911816Ssam */ 15011816Ssam pclinit(unit) 15111816Ssam int unit; 15211816Ssam { 15311816Ssam register struct pcl_softc *sc = &pcl_softc[unit]; 15411816Ssam register struct uba_device *ui = pclinfo[unit]; 15511816Ssam register struct pcldevice *addr; 15611816Ssam int s; 15711816Ssam 158*21774Skarels if (sc->sc_if.if_addrlist == (struct ifaddr *)0) 15913064Ssam return; 16011816Ssam if (if_ubainit(&sc->sc_ifuba, ui->ui_ubanum, 0, 16111816Ssam (int)btoc(PCLMTU)) == 0) { 16211816Ssam printf("pcl%d: can't init\n", unit); 163*21774Skarels sc->sc_if.if_flags &= ~(IFF_UP | IFF_RUNNING); 16411816Ssam return; 16511816Ssam } 166*21774Skarels sc->sc_if.if_flags |= IFF_RUNNING; 16711816Ssam addr = (struct pcldevice *)ui->ui_addr; 16811816Ssam addr->pcl_rcr = PCL_RCINIT; 16911816Ssam addr->pcl_tcr = PCL_TXINIT; 17011816Ssam 17111816Ssam /* 17211816Ssam * Hang a receive and start any 17311816Ssam * pending writes by faking a transmit complete. 17411816Ssam */ 17511816Ssam s = splimp(); 17611816Ssam addr->pcl_rdba = (short) sc->sc_ifuba.ifu_r.ifrw_info; 17711816Ssam addr->pcl_rdbc = -PCLMTU; 17811816Ssam addr->pcl_rcr = (((int)(sc->sc_ifuba.ifu_r.ifrw_info>>12))&0x0030) | 17911816Ssam PCL_RCNPR | PCL_RCVWD | PCL_RCVDAT | PCL_IE; 18011816Ssam sc->sc_oactive = 0; 18111816Ssam pclstart(unit); 18211816Ssam splx(s); 18311816Ssam } 18411816Ssam 18511816Ssam /* 18611816Ssam * PCL output routine. 18711816Ssam */ 18811816Ssam pcloutput(ifp, m, dst) 18911816Ssam struct ifnet *ifp; 19011816Ssam struct mbuf *m; 19111816Ssam struct sockaddr *dst; 19211816Ssam { 19313089Ssam int dest, s, error; 19411816Ssam struct pcl_header *pclp; 19511816Ssam struct mbuf *m2; 19611816Ssam 19711816Ssam switch (dst->sa_family) { 19811816Ssam 19911816Ssam #ifdef INET 20011816Ssam case AF_INET: 201*21774Skarels if (in_broadcast(((struct sockaddr_in *)dst)->sin_addr)) 202*21774Skarels dest = 0; 203*21774Skarels else 204*21774Skarels dest = in_lnaof(((struct sockaddr_in *)dst)->sin_addr); 20513800Ssam if (dest > PCLMAXTDM) { 20613800Ssam error = EHOSTUNREACH; 20713800Ssam goto bad; 20813800Ssam } 20911816Ssam break; 21011816Ssam #endif 21111816Ssam default: 21211816Ssam printf("pcl%d: can't handle af%d\n", ifp->if_unit, 21311816Ssam dst->sa_family); 21411816Ssam error = EAFNOSUPPORT; 21511816Ssam goto bad; 21611816Ssam } 21711816Ssam 21811816Ssam /* 21911816Ssam * Add pseudo local net header. 22011816Ssam * Actually, it does not get transmitted, but merely stripped 22111816Ssam * off and used by the START routine to route the packet. 22211816Ssam * If no space in first mbuf, allocate another. 22311816Ssam */ 22411816Ssam if (m->m_off > MMAXOFF || 22511816Ssam MMINOFF + sizeof (struct pcl_header) > m->m_off) { 22611816Ssam m2 = m_get(M_DONTWAIT, MT_HEADER); 22711816Ssam if (m2 == 0) { 22811816Ssam error = ENOBUFS; 22911816Ssam goto bad; 23011816Ssam } 23111816Ssam m2->m_next = m; 23211816Ssam m2->m_off = MMINOFF; 23311816Ssam m2->m_len = sizeof (struct pcl_header); 23411816Ssam m = m2; 23511816Ssam } else { 23611816Ssam m->m_off -= sizeof (struct pcl_header); 23711816Ssam m->m_len += sizeof (struct pcl_header); 23811816Ssam } 23911816Ssam pclp = mtod(m, struct pcl_header *); 24011816Ssam pclp->pcl_dest = dest; 24111816Ssam 24211816Ssam /* 24311816Ssam * Queue message on interface, and start output if interface 24411816Ssam * not yet active. 24511816Ssam */ 24611816Ssam s = splimp(); 24711816Ssam if (IF_QFULL(&ifp->if_snd)) { 24811816Ssam IF_DROP(&ifp->if_snd); 24911816Ssam error = ENOBUFS; 25011816Ssam goto qfull; 25111816Ssam } 25211816Ssam IF_ENQUEUE(&ifp->if_snd, m); 25311816Ssam if (pcl_softc[ifp->if_unit].sc_oactive == 0) 25411816Ssam pclstart(ifp->if_unit); 25511816Ssam splx(s); 25611816Ssam return (0); 25711816Ssam qfull: 25811816Ssam splx(s); 25911816Ssam bad: 26011816Ssam m_freem(m); 26111816Ssam return (error); 26211816Ssam } 26311816Ssam 26411816Ssam /* 26511816Ssam * Start or restart output on interface. 26611816Ssam * If interface is already active, then this is a retransmit. 26711816Ssam * If interface is not already active, get another datagram 26811816Ssam * to send off of the interface queue, and map it to the interface 26911816Ssam * before starting the output. 27011816Ssam */ 27111816Ssam pclstart(dev) 27211816Ssam dev_t dev; 27311816Ssam { 27411816Ssam int unit = PCLUNIT(dev); 27511816Ssam struct uba_device *ui = pclinfo[unit]; 27611816Ssam register struct pcl_softc *sc = &pcl_softc[unit]; 27711816Ssam register struct pcldevice *addr; 27811816Ssam struct mbuf *m; 27911816Ssam 28011816Ssam if (sc->sc_oactive) 28111816Ssam goto restart; 28211816Ssam 28311816Ssam /* 28411816Ssam * Not already active: dequeue another request 28511816Ssam * and map it to the UNIBUS. If no more requests, 28611816Ssam * just return. 28711816Ssam */ 28811816Ssam IF_DEQUEUE(&sc->sc_if.if_snd, m); 28911816Ssam if (m == 0) { 29011816Ssam sc->sc_oactive = 0; 29111816Ssam return; 29211816Ssam } 29311816Ssam 29411816Ssam /* 29511816Ssam * Pull destination node out of pseudo-local net header. 29611816Ssam * remove it from outbound data. 29711816Ssam * Note that if_wubaput calls m_bcopy, which is prepared for 29811816Ssam * m_len to be 0 in the first mbuf in the chain. 29911816Ssam */ 30013800Ssam sc->sc_bdest = mtod(m, struct pcl_header *)->pcl_dest; 30113800Ssam sc->sc_odest = sc->sc_bdest? sc->sc_bdest: 1; 30211816Ssam m->m_off += sizeof (struct pcl_header); 30311816Ssam m->m_len -= sizeof (struct pcl_header); 30411816Ssam 30511816Ssam /* Map out to the DMA area */ 30611816Ssam sc->sc_olen = if_wubaput(&sc->sc_ifuba, m); 30711816Ssam 30811816Ssam restart: 30911816Ssam /* 31011816Ssam * Have request mapped to UNIBUS for transmission. 31113800Ssam * Purge any stale data from this BDP, and start the output. 31211816Ssam */ 31311816Ssam if (sc->sc_ifuba.ifu_flags & UBA_NEEDBDP) 31411816Ssam UBAPURGE(sc->sc_ifuba.ifu_uba, sc->sc_ifuba.ifu_w.ifrw_bdp); 31511816Ssam addr = (struct pcldevice *)ui->ui_addr; 31611816Ssam addr->pcl_tcr = PCL_TXINIT; 31711816Ssam addr->pcl_tsba = (int)sc->sc_ifuba.ifu_w.ifrw_info; 31811816Ssam addr->pcl_tsbc = -sc->sc_olen; 31911816Ssam 32011816Ssam /* 32111816Ssam * RIB (retry if busy) is used on the second and subsequent packets 32211816Ssam * to a single host, because TCP often wants to transmit multiple 32311816Ssam * buffers in a row, 32411816Ssam * and if they are all going to the same place, the second and 32511816Ssam * subsequent ones may be lost due to receiver not ready again yet. 32611816Ssam * This can cause serious problems, because the TCP will resend the 32711816Ssam * whole window, which just repeats the problem. The result is that 32811816Ssam * a perfectly good link appears not to work unless we take steps here. 32911816Ssam */ 33011816Ssam addr->pcl_tcr = (((int)(sc->sc_ifuba.ifu_w.ifrw_info>>12))&0x0030) | 33111816Ssam ((sc->sc_odest & 0xF)<<8) | 33211816Ssam PCL_TXNPR | PCL_SNDWD | PCL_STTXM | PCL_IE | 33311816Ssam (sc->sc_odest == sc->sc_lastdest ? PCL_RIB : 0); 33411816Ssam sc->sc_lastdest = sc->sc_odest; 33511816Ssam sc->sc_oactive = 1; 33611816Ssam } 33711816Ssam 33811816Ssam /* 33911816Ssam * PCL transmitter interrupt. 34011816Ssam * Start another output if more data to send. 34111816Ssam */ 34211816Ssam pclxint(unit) 34311816Ssam int unit; 34411816Ssam { 34511816Ssam register struct uba_device *ui = pclinfo[unit]; 34611816Ssam register struct pcl_softc *sc = &pcl_softc[unit]; 34711816Ssam register struct pcldevice *addr = (struct pcldevice *)ui->ui_addr; 34811816Ssam 34911816Ssam if (sc->sc_oactive == 0) { 35011816Ssam printf ("pcl%d: stray interrupt\n", unit); 35111816Ssam return; 35211816Ssam } 35311816Ssam if (addr->pcl_tsr & PCL_ERR) { 35411816Ssam sc->sc_lastdest = 0; /* don't bother with RIB */ 35511816Ssam if (addr->pcl_tsr & PCL_MSTDWN) { 35611816Ssam addr->pcl_tmmr = PCL_MASTER|PCL_AUTOADDR; 35711816Ssam pclstart(unit); /* Retry */ 35811816Ssam printf("pcl%d: master\n", unit ); 35911816Ssam return; 36011816Ssam } 36113800Ssam #ifndef PCL_TESTING 36213800Ssam if ((addr->pcl_tsr & (PCL_ERR|PCL_RESPB)) == (PCL_ERR|0)) { 36313800Ssam ; /* Receiver Offline -- not exactly an error */ 36413800Ssam } else { 36513800Ssam #else 36613800Ssam { 36713800Ssam #endif 36811816Ssam /* Log as an error */ 36911816Ssam printf("pcl%d: send error, tcr=%b tsr=%b\n", 37011816Ssam unit, addr->pcl_tcr, PCL_TCSRBITS, 37111816Ssam addr->pcl_tsr, PCL_TERRBITS); 37211816Ssam sc->sc_if.if_oerrors++; 37311816Ssam } 37411816Ssam } else 37511816Ssam sc->sc_if.if_opackets++; 37613800Ssam if (sc->sc_bdest == 0 && sc->sc_odest < PCLMAXTDM) { 37713800Ssam sc->sc_odest++; /* do next host (broadcast) */ 37813800Ssam } else { 37913800Ssam sc->sc_oactive = 0; 38013800Ssam if (sc->sc_ifuba.ifu_xtofree) { 38113800Ssam m_freem(sc->sc_ifuba.ifu_xtofree); 38213800Ssam sc->sc_ifuba.ifu_xtofree = 0; 38313800Ssam } 38411816Ssam } 38511816Ssam pclstart(unit); 38611816Ssam } 38711816Ssam 38811816Ssam /* 38911816Ssam * PCL interface receiver interrupt. 39011816Ssam * If input error just drop packet. 39111816Ssam */ 39211816Ssam pclrint(unit) 39311816Ssam int unit; 39411816Ssam { 39511816Ssam register struct pcl_softc *sc = &pcl_softc[unit]; 39611816Ssam struct pcldevice *addr = (struct pcldevice *)pclinfo[unit]->ui_addr; 39711816Ssam struct mbuf *m; 39813089Ssam int len; 39911816Ssam register struct ifqueue *inq; 40011816Ssam 40111816Ssam sc->sc_if.if_ipackets++; 40211816Ssam /* 40311816Ssam * Purge BDP; drop if input error indicated. 40411816Ssam */ 40511816Ssam if (sc->sc_ifuba.ifu_flags & UBA_NEEDBDP) 40611816Ssam UBAPURGE(sc->sc_ifuba.ifu_uba, sc->sc_ifuba.ifu_r.ifrw_bdp); 40711816Ssam if (addr->pcl_rsr & PCL_ERR) { 40811816Ssam printf("pcl%d: rcv error, rcr=%b rsr=%b\n", 40911816Ssam unit, addr->pcl_rcr, PCL_RCSRBITS, 41011816Ssam addr->pcl_rsr, PCL_RERRBITS); 41111816Ssam sc->sc_if.if_ierrors++; 41211816Ssam goto setup; 41311816Ssam } 41411816Ssam len = PCLMTU + addr->pcl_rdbc; 41511816Ssam if (len <= 0 || len > PCLMTU) { 41611816Ssam printf("pcl%d: bad len=%d.\n", unit, len); 41711816Ssam sc->sc_if.if_ierrors++; 41811816Ssam goto setup; 41911816Ssam } 42011816Ssam 42111816Ssam /* Really short packets will be part of the startup sequence */ 42211816Ssam if (len <= 4) { 42311816Ssam /* Later, do comming-up processing here */ 42411816Ssam goto setup; /* drop packet */ 42511816Ssam } 42611816Ssam 42711816Ssam /* 42811816Ssam * Pull packet off interface. 42911816Ssam */ 43011816Ssam m = if_rubaget(&sc->sc_ifuba, len, 0); 43111816Ssam if (m == 0) 43211816Ssam goto setup; 43311816Ssam 43411816Ssam schednetisr(NETISR_IP); 43511816Ssam inq = &ipintrq; 43611816Ssam 43711816Ssam if (IF_QFULL(inq)) { 43811816Ssam IF_DROP(inq); 43911816Ssam m_freem(m); 44011816Ssam } else 44111816Ssam IF_ENQUEUE(inq, m); 44211816Ssam setup: 44311816Ssam /* 44411816Ssam * Reset for next packet. 44511816Ssam */ 44611816Ssam addr->pcl_rcr = PCL_RCINIT; 44711816Ssam addr->pcl_rdba = (int)sc->sc_ifuba.ifu_r.ifrw_info; 44811816Ssam addr->pcl_rdbc = -PCLMTU; 44911816Ssam addr->pcl_rcr = (((int)(sc->sc_ifuba.ifu_w.ifrw_info>>12))&0x0030) | 45011816Ssam PCL_RCNPR | PCL_RCVWD | PCL_RCVDAT | PCL_IE; 45111816Ssam } 45213064Ssam 45313064Ssam /* 45413064Ssam * Process an ioctl request. 45513064Ssam */ 45613064Ssam pclioctl(ifp, cmd, data) 45713064Ssam register struct ifnet *ifp; 45813064Ssam int cmd; 45913064Ssam caddr_t data; 46013064Ssam { 46113064Ssam int s = splimp(), error = 0; 46213064Ssam 46313064Ssam switch (cmd) { 46413064Ssam 46513064Ssam case SIOCSIFADDR: 466*21774Skarels if ((ifp->if_flags & IFF_RUNNING) == 0) 46713064Ssam pclinit(ifp->if_unit); 468*21774Skarels ifp->if_flags |= IFF_UP; 46913064Ssam break; 47013064Ssam 47113064Ssam default: 47213064Ssam error = EINVAL; 47313064Ssam } 47413064Ssam splx(s); 47513064Ssam return (error); 47613064Ssam } 47711816Ssam #endif 478