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