xref: /original-bsd/sys/vax/if/if_pcl.c (revision e0c244b1)
1 /*	if_pcl.c	4.4	83/07/06	*/
2 
3 #include "pcl.h"
4 #if NPCL > 0
5 /*
6  * DEC CSS PCL-11B Parallel Communications Interface
7  *
8  * Written by Mike Muuss and Jeff Schwab.
9  */
10 #include "../machine/pte.h"
11 
12 #include "../h/param.h"
13 #include "../h/systm.h"
14 #include "../h/mbuf.h"
15 #include "../h/buf.h"
16 #include "../h/protosw.h"
17 #include "../h/socket.h"
18 #include "../h/vmmac.h"
19 #include "../h/ioctl.h"
20 #include "../h/errno.h"
21 
22 #include "../net/if.h"
23 #include "../net/netisr.h"
24 #include "../net/route.h"
25 #include "../netinet/in.h"
26 #include "../netinet/in_systm.h"
27 #include "../netinet/ip.h"
28 #include "../netinet/ip_var.h"
29 
30 #include "../vax/cpu.h"
31 #include "../vax/mtpr.h"
32 #include "../vaxif/if_pclreg.h"
33 #include "../vaxif/if_uba.h"
34 #include "../vaxuba/ubareg.h"
35 #include "../vaxuba/ubavar.h"
36 
37 /* The MTU has been carefully selected to prevent fragmentation <-> ArpaNet */
38 #define	PCLMTU		(1006)	/* Max transmission unit (bytes) */
39 #define	PCLMAXTDM	7	/* Max unit number on TDM bus */
40 
41 int	pclprobe(), pclattach(), pclrint(), pclxint();
42 int	pclinit(), pclioctl(), pcloutput(), pclreset();
43 
44 struct	uba_device	*pclinfo[NPCL];
45 u_short pclstd[] = { 0 };
46 #define	PCLUNIT(x)	minor(x)
47 struct	uba_driver pcldriver =
48 	{ pclprobe, 0, pclattach, 0, pclstd, "pcl", pclinfo };
49 
50 /*
51  * PCL software status per interface.
52  *
53  * Each interface is referenced by a network interface structure,
54  * sc_if, which the routing code uses to locate the interface.
55  * This structure contains the output queue for the interface, its address, ...
56  * We also have, for each interface, a UBA interface structure, which
57  * contains information about the UNIBUS resources held by the interface:
58  * map registers, buffered data paths, etc.  Information is cached in this
59  * structure for use by the if_uba.c routines in running the interface
60  * efficiently.
61  */
62 struct	pcl_softc {
63 	struct	ifnet sc_if;		/* network-visible interface */
64 	struct	ifuba sc_ifuba;		/* UNIBUS resources */
65 	short	sc_oactive;		/* is output active? */
66 	short	sc_olen;		/* length of last output */
67 	short	sc_lastdest;		/* previous destination */
68 	short	sc_odest;		/* current xmit destination */
69 	short	sc_bdest;		/* buffer's stated destination */
70 	short	sc_pattern;		/* identification pattern */
71 } pcl_softc[NPCL];
72 
73 /*
74  * Structure of "local header", which only goes between
75  * pcloutput and pclstart.
76  */
77 struct pcl_header {
78 	short	pcl_dest;		/* Destination PCL station */
79 };
80 
81 /*
82  * Do non-DMA output of 1 word to determine presence of interface,
83  * and to find the interupt vector.  1 word messages are a special
84  * case in the receiver routine, and will be discarded.
85  */
86 pclprobe(reg)
87 	caddr_t reg;
88 {
89 	register int br, cvec;		/* r11, r10 value-result */
90 	register struct pcldevice *addr = (struct pcldevice *)reg;
91 
92 #ifdef lint
93 	br = 0; cvec = br; br = cvec;
94 	pclrint(0); pclxint(0);
95 #endif
96 	addr->pcl_rcr = PCL_RCINIT;
97 	addr->pcl_tcr = PCL_TXINIT;
98 	addr->pcl_tsba = 0xFFFE;
99 	/* going for 01777776 */
100 	addr->pcl_tsbc = -4;		/* really short */
101 	addr->pcl_tcr =
102 	 ((1 & 0xF) << 8) | PCL_TXNPR | PCL_SNDWD | PCL_STTXM | PCL_IE | 0x0030;
103 	DELAY(100000);
104 	addr->pcl_tcr = PCL_TXINIT;
105 	return (sizeof (struct pcldevice));
106 }
107 
108 /*
109  * Interface exists: make available by filling in network interface
110  * record.  System will initialize the interface when it is ready
111  * to accept packets.
112  */
113 pclattach(ui)
114 	struct uba_device *ui;
115 {
116 	register struct pcl_softc *sc = &pcl_softc[ui->ui_unit];
117 
118 	sc->sc_if.if_unit = ui->ui_unit;
119 	sc->sc_if.if_name = "pcl";
120 	sc->sc_if.if_mtu = PCLMTU;
121 	sc->sc_if.if_init = pclinit;
122 	sc->sc_if.if_output = pcloutput;
123 	sc->sc_if.if_ioctl = pclioctl;
124 	sc->sc_if.if_reset = pclreset;
125 	sc->sc_ifuba.ifu_flags = UBA_NEEDBDP;
126 	if_attach(&sc->sc_if);
127 }
128 
129 /*
130  * Reset of interface after UNIBUS reset.
131  * If interface is on specified uba, reset its state.
132  */
133 pclreset(unit, uban)
134 	int unit, uban;
135 {
136 	register struct uba_device *ui;
137 
138 	if (unit >= NPCL || (ui = pclinfo[unit]) == 0 || ui->ui_alive == 0 ||
139 	    ui->ui_ubanum != uban)
140 		return;
141 	printf(" pcl%d", unit);
142 	pclinit(unit);
143 }
144 
145 /*
146  * Initialization of interface; clear recorded pending
147  * operations, and reinitialize UNIBUS usage.
148  */
149 pclinit(unit)
150 	int unit;
151 {
152 	register struct pcl_softc *sc = &pcl_softc[unit];
153 	register struct uba_device *ui = pclinfo[unit];
154 	register struct pcldevice *addr;
155 	struct sockaddr_in *sin;
156 	int s;
157 
158 	sin = (struct sockaddr_in *)&sc->sc_if.if_addr;
159 	if (sin->sin_addr.s_addr == 0)
160 		return;
161 	if (if_ubainit(&sc->sc_ifuba, ui->ui_ubanum, 0,
162 	    (int)btoc(PCLMTU)) == 0) {
163 		printf("pcl%d: can't init\n", unit);
164 		sc->sc_if.if_flags &= ~IFF_UP;
165 		return;
166 	}
167 	addr = (struct pcldevice *)ui->ui_addr;
168 	addr->pcl_rcr = PCL_RCINIT;
169 	addr->pcl_tcr = PCL_TXINIT;
170 
171 	/*
172 	 * Hang a receive and start any
173 	 * pending writes by faking a transmit complete.
174 	 */
175 	s = splimp();
176 	addr->pcl_rdba = (short) sc->sc_ifuba.ifu_r.ifrw_info;
177 	addr->pcl_rdbc = -PCLMTU;
178 	addr->pcl_rcr = (((int)(sc->sc_ifuba.ifu_r.ifrw_info>>12))&0x0030) |
179 		PCL_RCNPR | PCL_RCVWD | PCL_RCVDAT | PCL_IE;
180 	sc->sc_oactive = 0;
181 	sc->sc_if.if_flags |= IFF_UP|IFF_RUNNING;
182 	pclstart(unit);
183 	splx(s);
184 	/* Set up routing table entry */
185 	if_rtinit(&sc->sc_if, RTF_UP);
186 }
187 
188 /*
189  * PCL output routine.
190  */
191 pcloutput(ifp, m, dst)
192 	struct ifnet *ifp;
193 	struct mbuf *m;
194 	struct sockaddr *dst;
195 {
196 	int dest, s, error;
197 	struct pcl_header *pclp;
198 	struct mbuf *m2;
199 
200 	switch (dst->sa_family) {
201 
202 #ifdef INET
203 	case AF_INET:
204 		dest = in_lnaof(((struct sockaddr_in *)dst)->sin_addr);
205 		if (dest > PCLMAXTDM) {
206 			error = EHOSTUNREACH;
207 			goto bad;
208 		}
209 		break;
210 #endif
211 	default:
212 		printf("pcl%d: can't handle af%d\n", ifp->if_unit,
213 			dst->sa_family);
214 		error = EAFNOSUPPORT;
215 		goto bad;
216 	}
217 
218 	/*
219 	 * Add pseudo local net header.
220 	 * Actually, it does not get transmitted, but merely stripped
221 	 * off and used by the START routine to route the packet.
222 	 * If no space in first mbuf, allocate another.
223 	 */
224 	if (m->m_off > MMAXOFF ||
225 	    MMINOFF + sizeof (struct pcl_header) > m->m_off) {
226 		m2 = m_get(M_DONTWAIT, MT_HEADER);
227 		if (m2 == 0) {
228 			error = ENOBUFS;
229 			goto bad;
230 		}
231 		m2->m_next = m;
232 		m2->m_off = MMINOFF;
233 		m2->m_len = sizeof (struct pcl_header);
234 		m = m2;
235 	} else {
236 		m->m_off -= sizeof (struct pcl_header);
237 		m->m_len += sizeof (struct pcl_header);
238 	}
239 	pclp = mtod(m, struct pcl_header *);
240 	pclp->pcl_dest = dest;
241 
242 	/*
243 	 * Queue message on interface, and start output if interface
244 	 * not yet active.
245 	 */
246 	s = splimp();
247 	if (IF_QFULL(&ifp->if_snd)) {
248 		IF_DROP(&ifp->if_snd);
249 		error = ENOBUFS;
250 		goto qfull;
251 	}
252 	IF_ENQUEUE(&ifp->if_snd, m);
253 	if (pcl_softc[ifp->if_unit].sc_oactive == 0)
254 		pclstart(ifp->if_unit);
255 	splx(s);
256 	return (0);
257 qfull:
258 	splx(s);
259 bad:
260 	m_freem(m);
261 	return (error);
262 }
263 
264 /*
265  * Start or restart output on interface.
266  * If interface is already active, then this is a retransmit.
267  * If interface is not already active, get another datagram
268  * to send off of the interface queue, and map it to the interface
269  * before starting the output.
270  */
271 pclstart(dev)
272 	dev_t dev;
273 {
274         int unit = PCLUNIT(dev);
275 	struct uba_device *ui = pclinfo[unit];
276 	register struct pcl_softc *sc = &pcl_softc[unit];
277 	register struct pcldevice *addr;
278 	struct mbuf *m;
279 
280 	if (sc->sc_oactive)
281 		goto restart;
282 
283 	/*
284 	 * Not already active: dequeue another request
285 	 * and map it to the UNIBUS.  If no more requests,
286 	 * just return.
287 	 */
288 	IF_DEQUEUE(&sc->sc_if.if_snd, m);
289 	if (m == 0) {
290 		sc->sc_oactive = 0;
291 		return;
292 	}
293 
294 	/*
295 	 * Pull destination node out of pseudo-local net header.
296 	 * remove it from outbound data.
297 	 * Note that if_wubaput calls m_bcopy, which is prepared for
298 	 * m_len to be 0 in the first mbuf in the chain.
299 	 */
300 	sc->sc_bdest = mtod(m, struct pcl_header *)->pcl_dest;
301 	sc->sc_odest = sc->sc_bdest? sc->sc_bdest: 1;
302 	m->m_off += sizeof (struct pcl_header);
303 	m->m_len -= sizeof (struct pcl_header);
304 
305 	/* Map out to the DMA area */
306 	sc->sc_olen = if_wubaput(&sc->sc_ifuba, m);
307 
308 restart:
309 	/*
310 	 * Have request mapped to UNIBUS for transmission.
311 	 * Purge any stale data from this BDP, and start the output.
312 	 */
313 	if (sc->sc_ifuba.ifu_flags & UBA_NEEDBDP)
314 		UBAPURGE(sc->sc_ifuba.ifu_uba, sc->sc_ifuba.ifu_w.ifrw_bdp);
315 	addr = (struct pcldevice *)ui->ui_addr;
316 	addr->pcl_tcr = PCL_TXINIT;
317 	addr->pcl_tsba = (int)sc->sc_ifuba.ifu_w.ifrw_info;
318 	addr->pcl_tsbc = -sc->sc_olen;
319 
320 	/*
321 	 * RIB (retry if busy) is used on the second and subsequent packets
322 	 * to a single host, because TCP often wants to transmit multiple
323 	 * buffers in a row,
324 	 * and if they are all going to the same place, the second and
325 	 * subsequent ones may be lost due to receiver not ready again yet.
326 	 * This can cause serious problems, because the TCP will resend the
327 	 * whole window, which just repeats the problem.  The result is that
328 	 * a perfectly good link appears not to work unless we take steps here.
329 	 */
330 	addr->pcl_tcr = (((int)(sc->sc_ifuba.ifu_w.ifrw_info>>12))&0x0030) |
331 		((sc->sc_odest & 0xF)<<8) |
332 		PCL_TXNPR | PCL_SNDWD | PCL_STTXM | PCL_IE |
333 		(sc->sc_odest == sc->sc_lastdest ? PCL_RIB : 0);
334 	sc->sc_lastdest = sc->sc_odest;
335 	sc->sc_oactive = 1;
336 }
337 
338 /*
339  * PCL transmitter interrupt.
340  * Start another output if more data to send.
341  */
342 pclxint(unit)
343 	int unit;
344 {
345 	register struct uba_device *ui = pclinfo[unit];
346 	register struct pcl_softc *sc = &pcl_softc[unit];
347 	register struct pcldevice *addr = (struct pcldevice *)ui->ui_addr;
348 
349 	if (sc->sc_oactive == 0) {
350 		printf ("pcl%d: stray interrupt\n", unit);
351 		return;
352 	}
353 	if (addr->pcl_tsr & PCL_ERR) {
354 		sc->sc_lastdest = 0;		/* don't bother with RIB */
355 		if (addr->pcl_tsr & PCL_MSTDWN) {
356 			addr->pcl_tmmr = PCL_MASTER|PCL_AUTOADDR;
357 			pclstart(unit);	/* Retry */
358 			printf("pcl%d: master\n", unit );
359 			return;
360 		}
361 #ifndef PCL_TESTING
362 		if ((addr->pcl_tsr & (PCL_ERR|PCL_RESPB)) == (PCL_ERR|0))  {
363 			;	/* Receiver Offline -- not exactly an error */
364 		}  else  {
365 #else
366 		{
367 #endif
368 			/* Log as an error */
369 			printf("pcl%d: send error, tcr=%b tsr=%b\n",
370 				unit, addr->pcl_tcr, PCL_TCSRBITS,
371 				addr->pcl_tsr, PCL_TERRBITS);
372 			sc->sc_if.if_oerrors++;
373 		}
374 	} else
375 		sc->sc_if.if_opackets++;
376 	if (sc->sc_bdest == 0 && sc->sc_odest < PCLMAXTDM) {
377 		sc->sc_odest++;		/* do next host (broadcast) */
378 	} else {
379 		sc->sc_oactive = 0;
380 		if (sc->sc_ifuba.ifu_xtofree) {
381 			m_freem(sc->sc_ifuba.ifu_xtofree);
382 			sc->sc_ifuba.ifu_xtofree = 0;
383 		}
384 	}
385 	pclstart(unit);
386 }
387 
388 /*
389  * PCL interface receiver interrupt.
390  * If input error just drop packet.
391  */
392 pclrint(unit)
393 	int unit;
394 {
395 	register struct pcl_softc *sc = &pcl_softc[unit];
396 	struct pcldevice *addr = (struct pcldevice *)pclinfo[unit]->ui_addr;
397     	struct mbuf *m;
398 	int len;
399 	register struct ifqueue *inq;
400 
401 	sc->sc_if.if_ipackets++;
402 	/*
403 	 * Purge BDP; drop if input error indicated.
404 	 */
405 	if (sc->sc_ifuba.ifu_flags & UBA_NEEDBDP)
406 		UBAPURGE(sc->sc_ifuba.ifu_uba, sc->sc_ifuba.ifu_r.ifrw_bdp);
407 	if (addr->pcl_rsr & PCL_ERR) {
408 		printf("pcl%d: rcv error, rcr=%b rsr=%b\n",
409 			unit, addr->pcl_rcr, PCL_RCSRBITS,
410 			addr->pcl_rsr, PCL_RERRBITS);
411 		sc->sc_if.if_ierrors++;
412 		goto setup;
413 	}
414 	len = PCLMTU + addr->pcl_rdbc;
415 	if (len <= 0 || len > PCLMTU) {
416 		printf("pcl%d: bad len=%d.\n", unit, len);
417 		sc->sc_if.if_ierrors++;
418 		goto setup;
419 	}
420 
421 	/* Really short packets will be part of the startup sequence */
422 	if (len <= 4) {
423 		/* Later, do comming-up processing here */
424 		goto setup;	/* drop packet */
425 	}
426 
427 	/*
428 	 * Pull packet off interface.
429 	 */
430 	m = if_rubaget(&sc->sc_ifuba, len, 0);
431 	if (m == 0)
432 		goto setup;
433 
434 	schednetisr(NETISR_IP);
435 	inq = &ipintrq;
436 
437 	if (IF_QFULL(inq)) {
438 		IF_DROP(inq);
439 		m_freem(m);
440 	} else
441 		IF_ENQUEUE(inq, m);
442 setup:
443 	/*
444 	 * Reset for next packet.
445 	 */
446 	addr->pcl_rcr = PCL_RCINIT;
447 	addr->pcl_rdba = (int)sc->sc_ifuba.ifu_r.ifrw_info;
448 	addr->pcl_rdbc = -PCLMTU;
449 	addr->pcl_rcr = (((int)(sc->sc_ifuba.ifu_w.ifrw_info>>12))&0x0030) |
450 		PCL_RCNPR | PCL_RCVWD | PCL_RCVDAT | PCL_IE;
451 }
452 
453 /*
454  * Process an ioctl request.
455  */
456 pclioctl(ifp, cmd, data)
457 	register struct ifnet *ifp;
458 	int cmd;
459 	caddr_t data;
460 {
461 	struct ifreq *ifr = (struct ifreq *)data;
462 	struct sockaddr_in *sin;
463 	int s = splimp(), error = 0;
464 
465 	switch (cmd) {
466 
467 	case SIOCSIFADDR:
468 		if (ifp->if_flags & IFF_RUNNING)
469 			if_rtinit(ifp, -1);	/* delete previous route */
470 		sin = (struct sockaddr_in *)&ifr->ifr_addr;
471 		ifp->if_addr = *(struct sockaddr *)sin;
472 		ifp->if_net = in_netof(sin->sin_addr);
473 		ifp->if_host[0] = in_lnaof(sin->sin_addr);
474 		ifp->if_broadaddr = *(struct sockaddr *)sin;
475 		sin = (struct sockaddr_in *)&ifp->if_broadaddr;
476 		sin->sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY);
477 		ifp->if_flags |= IFF_BROADCAST;
478 		if (ifp->if_flags & IFF_RUNNING)
479 			if_rtinit(ifp, RTF_UP);
480 		else
481 			pclinit(ifp->if_unit);
482 		break;
483 
484 	default:
485 		error = EINVAL;
486 	}
487 	splx(s);
488 	return (error);
489 }
490 #endif
491