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