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