123300Smckusick /*
229369Smckusick * Copyright (c) 1982, 1986 Regents of the University of California.
335325Sbostic * All rights reserved.
423300Smckusick *
544562Sbostic * %sccs.include.redist.c%
635325Sbostic *
7*45801Sbostic * @(#)if_pcl.c 7.6 (Berkeley) 12/16/90
823300Smckusick */
911816Ssam
1011816Ssam #include "pcl.h"
1111816Ssam #if NPCL > 0
1211816Ssam /*
1311816Ssam * DEC CSS PCL-11B Parallel Communications Interface
1411816Ssam *
1513800Ssam * Written by Mike Muuss and Jeff Schwab.
1611816Ssam */
17*45801Sbostic #include "../include/pte.h"
1811816Ssam
19*45801Sbostic #include "sys/param.h"
20*45801Sbostic #include "sys/systm.h"
21*45801Sbostic #include "sys/mbuf.h"
22*45801Sbostic #include "sys/buf.h"
23*45801Sbostic #include "sys/protosw.h"
24*45801Sbostic #include "sys/socket.h"
25*45801Sbostic #include "sys/vmmac.h"
26*45801Sbostic #include "sys/ioctl.h"
27*45801Sbostic #include "sys/errno.h"
2811816Ssam
29*45801Sbostic #include "net/if.h"
30*45801Sbostic #include "net/netisr.h"
31*45801Sbostic #include "net/route.h"
3224792Skarels
3328952Skarels #ifdef INET
34*45801Sbostic #include "netinet/in.h"
35*45801Sbostic #include "netinet/in_systm.h"
36*45801Sbostic #include "netinet/in_var.h"
37*45801Sbostic #include "netinet/ip.h"
3828952Skarels #endif
3911816Ssam
40*45801Sbostic #include "../include/cpu.h"
41*45801Sbostic #include "../include/mtpr.h"
4217116Sbloom #include "if_pclreg.h"
4317116Sbloom #include "if_uba.h"
44*45801Sbostic #include "../uba/ubareg.h"
45*45801Sbostic #include "../uba/ubavar.h"
4611816Ssam
4711816Ssam /* The MTU has been carefully selected to prevent fragmentation <-> ArpaNet */
4813800Ssam #define PCLMTU (1006) /* Max transmission unit (bytes) */
4913800Ssam #define PCLMAXTDM 7 /* Max unit number on TDM bus */
5011816Ssam
5111816Ssam int pclprobe(), pclattach(), pclrint(), pclxint();
5213064Ssam int pclinit(), pclioctl(), pcloutput(), pclreset();
5311816Ssam
5411816Ssam struct uba_device *pclinfo[NPCL];
5511816Ssam u_short pclstd[] = { 0 };
5611816Ssam #define PCLUNIT(x) minor(x)
5711816Ssam struct uba_driver pcldriver =
5811816Ssam { pclprobe, 0, pclattach, 0, pclstd, "pcl", pclinfo };
5911816Ssam
6011816Ssam /*
6111816Ssam * PCL software status per interface.
6211816Ssam *
6311816Ssam * Each interface is referenced by a network interface structure,
6411816Ssam * sc_if, which the routing code uses to locate the interface.
6511816Ssam * This structure contains the output queue for the interface, its address, ...
6611816Ssam * We also have, for each interface, a UBA interface structure, which
6711816Ssam * contains information about the UNIBUS resources held by the interface:
6811816Ssam * map registers, buffered data paths, etc. Information is cached in this
6911816Ssam * structure for use by the if_uba.c routines in running the interface
7011816Ssam * efficiently.
7111816Ssam */
7211816Ssam struct pcl_softc {
7311816Ssam struct ifnet sc_if; /* network-visible interface */
7411816Ssam struct ifuba sc_ifuba; /* UNIBUS resources */
7511816Ssam short sc_oactive; /* is output active? */
7611816Ssam short sc_olen; /* length of last output */
7711816Ssam short sc_lastdest; /* previous destination */
7811816Ssam short sc_odest; /* current xmit destination */
7913800Ssam short sc_bdest; /* buffer's stated destination */
8011816Ssam short sc_pattern; /* identification pattern */
8111816Ssam } pcl_softc[NPCL];
8211816Ssam
8311816Ssam /*
8411816Ssam * Structure of "local header", which only goes between
8511816Ssam * pcloutput and pclstart.
8611816Ssam */
8711816Ssam struct pcl_header {
8811816Ssam short pcl_dest; /* Destination PCL station */
8911816Ssam };
9011816Ssam
9111816Ssam /*
9211816Ssam * Do non-DMA output of 1 word to determine presence of interface,
9311816Ssam * and to find the interupt vector. 1 word messages are a special
9411816Ssam * case in the receiver routine, and will be discarded.
9511816Ssam */
pclprobe(reg)9611816Ssam pclprobe(reg)
9711816Ssam caddr_t reg;
9811816Ssam {
9911816Ssam register int br, cvec; /* r11, r10 value-result */
10011816Ssam register struct pcldevice *addr = (struct pcldevice *)reg;
10111816Ssam
10211816Ssam #ifdef lint
10311816Ssam br = 0; cvec = br; br = cvec;
10411816Ssam pclrint(0); pclxint(0);
10511816Ssam #endif
10611816Ssam addr->pcl_rcr = PCL_RCINIT;
10711816Ssam addr->pcl_tcr = PCL_TXINIT;
10811816Ssam addr->pcl_tsba = 0xFFFE;
10911816Ssam /* going for 01777776 */
11011816Ssam addr->pcl_tsbc = -4; /* really short */
11111816Ssam addr->pcl_tcr =
11211816Ssam ((1 & 0xF) << 8) | PCL_TXNPR | PCL_SNDWD | PCL_STTXM | PCL_IE | 0x0030;
11311816Ssam DELAY(100000);
11411816Ssam addr->pcl_tcr = PCL_TXINIT;
11511816Ssam return (sizeof (struct pcldevice));
11611816Ssam }
11711816Ssam
11811816Ssam /*
11911816Ssam * Interface exists: make available by filling in network interface
12011816Ssam * record. System will initialize the interface when it is ready
12111816Ssam * to accept packets.
12211816Ssam */
12311816Ssam pclattach(ui)
12411816Ssam struct uba_device *ui;
12511816Ssam {
12611816Ssam register struct pcl_softc *sc = &pcl_softc[ui->ui_unit];
12711816Ssam
12811816Ssam sc->sc_if.if_unit = ui->ui_unit;
12911816Ssam sc->sc_if.if_name = "pcl";
13011816Ssam sc->sc_if.if_mtu = PCLMTU;
13111816Ssam sc->sc_if.if_init = pclinit;
13211816Ssam sc->sc_if.if_output = pcloutput;
13313064Ssam sc->sc_if.if_ioctl = pclioctl;
13411816Ssam sc->sc_if.if_reset = pclreset;
13521774Skarels sc->sc_if.if_flags = IFF_BROADCAST;
13611816Ssam sc->sc_ifuba.ifu_flags = UBA_NEEDBDP;
13711816Ssam if_attach(&sc->sc_if);
13811816Ssam }
13911816Ssam
14011816Ssam /*
14111816Ssam * Reset of interface after UNIBUS reset.
14211816Ssam * If interface is on specified uba, reset its state.
14311816Ssam */
pclreset(unit,uban)14411816Ssam pclreset(unit, uban)
14511816Ssam int unit, uban;
14611816Ssam {
14711816Ssam register struct uba_device *ui;
14811816Ssam
14911816Ssam if (unit >= NPCL || (ui = pclinfo[unit]) == 0 || ui->ui_alive == 0 ||
15011816Ssam ui->ui_ubanum != uban)
15111816Ssam return;
15211816Ssam printf(" pcl%d", unit);
15311816Ssam pclinit(unit);
15411816Ssam }
15511816Ssam
15611816Ssam /*
15711816Ssam * Initialization of interface; clear recorded pending
15811816Ssam * operations, and reinitialize UNIBUS usage.
15911816Ssam */
pclinit(unit)16011816Ssam pclinit(unit)
16111816Ssam int unit;
16211816Ssam {
16311816Ssam register struct pcl_softc *sc = &pcl_softc[unit];
16411816Ssam register struct uba_device *ui = pclinfo[unit];
16511816Ssam register struct pcldevice *addr;
16611816Ssam int s;
16711816Ssam
16821774Skarels if (sc->sc_if.if_addrlist == (struct ifaddr *)0)
16913064Ssam return;
17011816Ssam if (if_ubainit(&sc->sc_ifuba, ui->ui_ubanum, 0,
17111816Ssam (int)btoc(PCLMTU)) == 0) {
17211816Ssam printf("pcl%d: can't init\n", unit);
17321774Skarels sc->sc_if.if_flags &= ~(IFF_UP | IFF_RUNNING);
17411816Ssam return;
17511816Ssam }
17621774Skarels sc->sc_if.if_flags |= IFF_RUNNING;
17711816Ssam addr = (struct pcldevice *)ui->ui_addr;
17811816Ssam addr->pcl_rcr = PCL_RCINIT;
17911816Ssam addr->pcl_tcr = PCL_TXINIT;
18011816Ssam
18111816Ssam /*
18211816Ssam * Hang a receive and start any
18311816Ssam * pending writes by faking a transmit complete.
18411816Ssam */
18511816Ssam s = splimp();
18611816Ssam addr->pcl_rdba = (short) sc->sc_ifuba.ifu_r.ifrw_info;
18711816Ssam addr->pcl_rdbc = -PCLMTU;
18811816Ssam addr->pcl_rcr = (((int)(sc->sc_ifuba.ifu_r.ifrw_info>>12))&0x0030) |
18911816Ssam PCL_RCNPR | PCL_RCVWD | PCL_RCVDAT | PCL_IE;
19011816Ssam sc->sc_oactive = 0;
19111816Ssam pclstart(unit);
19211816Ssam splx(s);
19311816Ssam }
19411816Ssam
19511816Ssam /*
19611816Ssam * PCL output routine.
19711816Ssam */
19811816Ssam pcloutput(ifp, m, dst)
19911816Ssam struct ifnet *ifp;
20011816Ssam struct mbuf *m;
20111816Ssam struct sockaddr *dst;
20211816Ssam {
20313089Ssam int dest, s, error;
20411816Ssam struct pcl_header *pclp;
20511816Ssam struct mbuf *m2;
20611816Ssam
20711816Ssam switch (dst->sa_family) {
20811816Ssam
20911816Ssam #ifdef INET
21011816Ssam case AF_INET:
21121774Skarels if (in_broadcast(((struct sockaddr_in *)dst)->sin_addr))
21221774Skarels dest = 0;
21321774Skarels else
21421774Skarels dest = in_lnaof(((struct sockaddr_in *)dst)->sin_addr);
21513800Ssam if (dest > PCLMAXTDM) {
21613800Ssam error = EHOSTUNREACH;
21713800Ssam goto bad;
21813800Ssam }
21911816Ssam break;
22011816Ssam #endif
22111816Ssam default:
22211816Ssam printf("pcl%d: can't handle af%d\n", ifp->if_unit,
22311816Ssam dst->sa_family);
22411816Ssam error = EAFNOSUPPORT;
22511816Ssam goto bad;
22611816Ssam }
22711816Ssam
22811816Ssam /*
22911816Ssam * Add pseudo local net header.
23011816Ssam * Actually, it does not get transmitted, but merely stripped
23111816Ssam * off and used by the START routine to route the packet.
23211816Ssam * If no space in first mbuf, allocate another.
23311816Ssam */
23437476Ssklower M_PREPEND(m, sizeof(struct pcl_header), M_DONTWAIT);
23537476Ssklower if (m == 0) {
23637476Ssklower error = ENOBUFS;
23737476Ssklower goto bad;
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 */
pclstart(dev)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;
30237476Ssklower m->m_data += sizeof (struct pcl_header);
30311816Ssam m->m_len -= sizeof (struct pcl_header);
30437476Ssklower if (m->m_flags & M_PKTHDR)
30537476Ssklower m->m_pkthdr.len -= sizeof (struct pcl_header);
30611816Ssam
30711816Ssam /* Map out to the DMA area */
30811816Ssam sc->sc_olen = if_wubaput(&sc->sc_ifuba, m);
30911816Ssam
31011816Ssam restart:
31111816Ssam /*
31211816Ssam * Have request mapped to UNIBUS for transmission.
31313800Ssam * Purge any stale data from this BDP, and start the output.
31411816Ssam */
31511816Ssam if (sc->sc_ifuba.ifu_flags & UBA_NEEDBDP)
31611816Ssam UBAPURGE(sc->sc_ifuba.ifu_uba, sc->sc_ifuba.ifu_w.ifrw_bdp);
31711816Ssam addr = (struct pcldevice *)ui->ui_addr;
31811816Ssam addr->pcl_tcr = PCL_TXINIT;
31911816Ssam addr->pcl_tsba = (int)sc->sc_ifuba.ifu_w.ifrw_info;
32011816Ssam addr->pcl_tsbc = -sc->sc_olen;
32111816Ssam
32211816Ssam /*
32311816Ssam * RIB (retry if busy) is used on the second and subsequent packets
32411816Ssam * to a single host, because TCP often wants to transmit multiple
32511816Ssam * buffers in a row,
32611816Ssam * and if they are all going to the same place, the second and
32711816Ssam * subsequent ones may be lost due to receiver not ready again yet.
32811816Ssam * This can cause serious problems, because the TCP will resend the
32911816Ssam * whole window, which just repeats the problem. The result is that
33011816Ssam * a perfectly good link appears not to work unless we take steps here.
33111816Ssam */
33211816Ssam addr->pcl_tcr = (((int)(sc->sc_ifuba.ifu_w.ifrw_info>>12))&0x0030) |
33311816Ssam ((sc->sc_odest & 0xF)<<8) |
33411816Ssam PCL_TXNPR | PCL_SNDWD | PCL_STTXM | PCL_IE |
33511816Ssam (sc->sc_odest == sc->sc_lastdest ? PCL_RIB : 0);
33611816Ssam sc->sc_lastdest = sc->sc_odest;
33711816Ssam sc->sc_oactive = 1;
33811816Ssam }
33911816Ssam
34011816Ssam /*
34111816Ssam * PCL transmitter interrupt.
34211816Ssam * Start another output if more data to send.
34311816Ssam */
pclxint(unit)34411816Ssam pclxint(unit)
34511816Ssam int unit;
34611816Ssam {
34711816Ssam register struct uba_device *ui = pclinfo[unit];
34811816Ssam register struct pcl_softc *sc = &pcl_softc[unit];
34911816Ssam register struct pcldevice *addr = (struct pcldevice *)ui->ui_addr;
35011816Ssam
35111816Ssam if (sc->sc_oactive == 0) {
35211816Ssam printf ("pcl%d: stray interrupt\n", unit);
35311816Ssam return;
35411816Ssam }
35511816Ssam if (addr->pcl_tsr & PCL_ERR) {
35611816Ssam sc->sc_lastdest = 0; /* don't bother with RIB */
35711816Ssam if (addr->pcl_tsr & PCL_MSTDWN) {
35811816Ssam addr->pcl_tmmr = PCL_MASTER|PCL_AUTOADDR;
35911816Ssam pclstart(unit); /* Retry */
36011816Ssam printf("pcl%d: master\n", unit );
36111816Ssam return;
36211816Ssam }
36313800Ssam #ifndef PCL_TESTING
36413800Ssam if ((addr->pcl_tsr & (PCL_ERR|PCL_RESPB)) == (PCL_ERR|0)) {
36513800Ssam ; /* Receiver Offline -- not exactly an error */
36613800Ssam } else {
36713800Ssam #else
36813800Ssam {
36913800Ssam #endif
37011816Ssam /* Log as an error */
37111816Ssam printf("pcl%d: send error, tcr=%b tsr=%b\n",
37211816Ssam unit, addr->pcl_tcr, PCL_TCSRBITS,
37311816Ssam addr->pcl_tsr, PCL_TERRBITS);
37411816Ssam sc->sc_if.if_oerrors++;
37511816Ssam }
37611816Ssam } else
37711816Ssam sc->sc_if.if_opackets++;
37813800Ssam if (sc->sc_bdest == 0 && sc->sc_odest < PCLMAXTDM) {
37913800Ssam sc->sc_odest++; /* do next host (broadcast) */
38013800Ssam } else {
38113800Ssam sc->sc_oactive = 0;
38213800Ssam if (sc->sc_ifuba.ifu_xtofree) {
38313800Ssam m_freem(sc->sc_ifuba.ifu_xtofree);
38413800Ssam sc->sc_ifuba.ifu_xtofree = 0;
38513800Ssam }
38611816Ssam }
38711816Ssam pclstart(unit);
38811816Ssam }
38911816Ssam
39011816Ssam /*
39111816Ssam * PCL interface receiver interrupt.
39211816Ssam * If input error just drop packet.
39311816Ssam */
pclrint(unit)39411816Ssam pclrint(unit)
39511816Ssam int unit;
39611816Ssam {
39711816Ssam register struct pcl_softc *sc = &pcl_softc[unit];
39811816Ssam struct pcldevice *addr = (struct pcldevice *)pclinfo[unit]->ui_addr;
39911816Ssam struct mbuf *m;
40013089Ssam int len;
40111816Ssam register struct ifqueue *inq;
40211816Ssam
40311816Ssam sc->sc_if.if_ipackets++;
40411816Ssam /*
40511816Ssam * Purge BDP; drop if input error indicated.
40611816Ssam */
40711816Ssam if (sc->sc_ifuba.ifu_flags & UBA_NEEDBDP)
40811816Ssam UBAPURGE(sc->sc_ifuba.ifu_uba, sc->sc_ifuba.ifu_r.ifrw_bdp);
40911816Ssam if (addr->pcl_rsr & PCL_ERR) {
41011816Ssam printf("pcl%d: rcv error, rcr=%b rsr=%b\n",
41111816Ssam unit, addr->pcl_rcr, PCL_RCSRBITS,
41211816Ssam addr->pcl_rsr, PCL_RERRBITS);
41311816Ssam sc->sc_if.if_ierrors++;
41411816Ssam goto setup;
41511816Ssam }
41611816Ssam len = PCLMTU + addr->pcl_rdbc;
41711816Ssam if (len <= 0 || len > PCLMTU) {
41811816Ssam printf("pcl%d: bad len=%d.\n", unit, len);
41911816Ssam sc->sc_if.if_ierrors++;
42011816Ssam goto setup;
42111816Ssam }
42211816Ssam
42311816Ssam /* Really short packets will be part of the startup sequence */
42411816Ssam if (len <= 4) {
42511816Ssam /* Later, do comming-up processing here */
42611816Ssam goto setup; /* drop packet */
42711816Ssam }
42811816Ssam
42911816Ssam /*
43011816Ssam * Pull packet off interface.
43111816Ssam */
43224792Skarels m = if_rubaget(&sc->sc_ifuba, len, 0, &sc->sc_if);
43311816Ssam if (m == 0)
43411816Ssam goto setup;
43511816Ssam
43611816Ssam schednetisr(NETISR_IP);
43711816Ssam inq = &ipintrq;
43811816Ssam
43911816Ssam if (IF_QFULL(inq)) {
44011816Ssam IF_DROP(inq);
44111816Ssam m_freem(m);
44211816Ssam } else
44311816Ssam IF_ENQUEUE(inq, m);
44411816Ssam setup:
44511816Ssam /*
44611816Ssam * Reset for next packet.
44711816Ssam */
44811816Ssam addr->pcl_rcr = PCL_RCINIT;
44911816Ssam addr->pcl_rdba = (int)sc->sc_ifuba.ifu_r.ifrw_info;
45011816Ssam addr->pcl_rdbc = -PCLMTU;
45111816Ssam addr->pcl_rcr = (((int)(sc->sc_ifuba.ifu_w.ifrw_info>>12))&0x0030) |
45211816Ssam PCL_RCNPR | PCL_RCVWD | PCL_RCVDAT | PCL_IE;
45311816Ssam }
45413064Ssam
45513064Ssam /*
45613064Ssam * Process an ioctl request.
45713064Ssam */
45826301Skarels /* ARGSUSED */
pclioctl(ifp,cmd,data)45913064Ssam pclioctl(ifp, cmd, data)
46013064Ssam register struct ifnet *ifp;
46113064Ssam int cmd;
46213064Ssam caddr_t data;
46313064Ssam {
46413064Ssam int s = splimp(), error = 0;
46513064Ssam
46613064Ssam switch (cmd) {
46713064Ssam
46813064Ssam case SIOCSIFADDR:
46924792Skarels ifp->if_flags |= IFF_UP;
47021774Skarels if ((ifp->if_flags & IFF_RUNNING) == 0)
47113064Ssam pclinit(ifp->if_unit);
47213064Ssam break;
47313064Ssam
47413064Ssam default:
47513064Ssam error = EINVAL;
47613064Ssam }
47713064Ssam splx(s);
47813064Ssam return (error);
47913064Ssam }
48011816Ssam #endif
481