xref: /csrg-svn/sys/vax/if/if_pcl.c (revision 37508)
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.4 (Berkeley) 04/25/89
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 	M_PREPEND(m, sizeof(struct pcl_header), M_DONTWAIT);
245 	if (m == 0) {
246 		error = ENOBUFS;
247 		goto bad;
248 	}
249 	pclp = mtod(m, struct pcl_header *);
250 	pclp->pcl_dest = dest;
251 
252 	/*
253 	 * Queue message on interface, and start output if interface
254 	 * not yet active.
255 	 */
256 	s = splimp();
257 	if (IF_QFULL(&ifp->if_snd)) {
258 		IF_DROP(&ifp->if_snd);
259 		error = ENOBUFS;
260 		goto qfull;
261 	}
262 	IF_ENQUEUE(&ifp->if_snd, m);
263 	if (pcl_softc[ifp->if_unit].sc_oactive == 0)
264 		pclstart(ifp->if_unit);
265 	splx(s);
266 	return (0);
267 qfull:
268 	splx(s);
269 bad:
270 	m_freem(m);
271 	return (error);
272 }
273 
274 /*
275  * Start or restart output on interface.
276  * If interface is already active, then this is a retransmit.
277  * If interface is not already active, get another datagram
278  * to send off of the interface queue, and map it to the interface
279  * before starting the output.
280  */
281 pclstart(dev)
282 	dev_t dev;
283 {
284         int unit = PCLUNIT(dev);
285 	struct uba_device *ui = pclinfo[unit];
286 	register struct pcl_softc *sc = &pcl_softc[unit];
287 	register struct pcldevice *addr;
288 	struct mbuf *m;
289 
290 	if (sc->sc_oactive)
291 		goto restart;
292 
293 	/*
294 	 * Not already active: dequeue another request
295 	 * and map it to the UNIBUS.  If no more requests,
296 	 * just return.
297 	 */
298 	IF_DEQUEUE(&sc->sc_if.if_snd, m);
299 	if (m == 0) {
300 		sc->sc_oactive = 0;
301 		return;
302 	}
303 
304 	/*
305 	 * Pull destination node out of pseudo-local net header.
306 	 * remove it from outbound data.
307 	 * Note that if_wubaput calls m_bcopy, which is prepared for
308 	 * m_len to be 0 in the first mbuf in the chain.
309 	 */
310 	sc->sc_bdest = mtod(m, struct pcl_header *)->pcl_dest;
311 	sc->sc_odest = sc->sc_bdest? sc->sc_bdest: 1;
312 	m->m_data += sizeof (struct pcl_header);
313 	m->m_len -= sizeof (struct pcl_header);
314 	if (m->m_flags & M_PKTHDR)
315 		m->m_pkthdr.len -= sizeof (struct pcl_header);
316 
317 	/* Map out to the DMA area */
318 	sc->sc_olen = if_wubaput(&sc->sc_ifuba, m);
319 
320 restart:
321 	/*
322 	 * Have request mapped to UNIBUS for transmission.
323 	 * Purge any stale data from this BDP, and start the output.
324 	 */
325 	if (sc->sc_ifuba.ifu_flags & UBA_NEEDBDP)
326 		UBAPURGE(sc->sc_ifuba.ifu_uba, sc->sc_ifuba.ifu_w.ifrw_bdp);
327 	addr = (struct pcldevice *)ui->ui_addr;
328 	addr->pcl_tcr = PCL_TXINIT;
329 	addr->pcl_tsba = (int)sc->sc_ifuba.ifu_w.ifrw_info;
330 	addr->pcl_tsbc = -sc->sc_olen;
331 
332 	/*
333 	 * RIB (retry if busy) is used on the second and subsequent packets
334 	 * to a single host, because TCP often wants to transmit multiple
335 	 * buffers in a row,
336 	 * and if they are all going to the same place, the second and
337 	 * subsequent ones may be lost due to receiver not ready again yet.
338 	 * This can cause serious problems, because the TCP will resend the
339 	 * whole window, which just repeats the problem.  The result is that
340 	 * a perfectly good link appears not to work unless we take steps here.
341 	 */
342 	addr->pcl_tcr = (((int)(sc->sc_ifuba.ifu_w.ifrw_info>>12))&0x0030) |
343 		((sc->sc_odest & 0xF)<<8) |
344 		PCL_TXNPR | PCL_SNDWD | PCL_STTXM | PCL_IE |
345 		(sc->sc_odest == sc->sc_lastdest ? PCL_RIB : 0);
346 	sc->sc_lastdest = sc->sc_odest;
347 	sc->sc_oactive = 1;
348 }
349 
350 /*
351  * PCL transmitter interrupt.
352  * Start another output if more data to send.
353  */
354 pclxint(unit)
355 	int unit;
356 {
357 	register struct uba_device *ui = pclinfo[unit];
358 	register struct pcl_softc *sc = &pcl_softc[unit];
359 	register struct pcldevice *addr = (struct pcldevice *)ui->ui_addr;
360 
361 	if (sc->sc_oactive == 0) {
362 		printf ("pcl%d: stray interrupt\n", unit);
363 		return;
364 	}
365 	if (addr->pcl_tsr & PCL_ERR) {
366 		sc->sc_lastdest = 0;		/* don't bother with RIB */
367 		if (addr->pcl_tsr & PCL_MSTDWN) {
368 			addr->pcl_tmmr = PCL_MASTER|PCL_AUTOADDR;
369 			pclstart(unit);	/* Retry */
370 			printf("pcl%d: master\n", unit );
371 			return;
372 		}
373 #ifndef PCL_TESTING
374 		if ((addr->pcl_tsr & (PCL_ERR|PCL_RESPB)) == (PCL_ERR|0))  {
375 			;	/* Receiver Offline -- not exactly an error */
376 		}  else  {
377 #else
378 		{
379 #endif
380 			/* Log as an error */
381 			printf("pcl%d: send error, tcr=%b tsr=%b\n",
382 				unit, addr->pcl_tcr, PCL_TCSRBITS,
383 				addr->pcl_tsr, PCL_TERRBITS);
384 			sc->sc_if.if_oerrors++;
385 		}
386 	} else
387 		sc->sc_if.if_opackets++;
388 	if (sc->sc_bdest == 0 && sc->sc_odest < PCLMAXTDM) {
389 		sc->sc_odest++;		/* do next host (broadcast) */
390 	} else {
391 		sc->sc_oactive = 0;
392 		if (sc->sc_ifuba.ifu_xtofree) {
393 			m_freem(sc->sc_ifuba.ifu_xtofree);
394 			sc->sc_ifuba.ifu_xtofree = 0;
395 		}
396 	}
397 	pclstart(unit);
398 }
399 
400 /*
401  * PCL interface receiver interrupt.
402  * If input error just drop packet.
403  */
404 pclrint(unit)
405 	int unit;
406 {
407 	register struct pcl_softc *sc = &pcl_softc[unit];
408 	struct pcldevice *addr = (struct pcldevice *)pclinfo[unit]->ui_addr;
409     	struct mbuf *m;
410 	int len;
411 	register struct ifqueue *inq;
412 
413 	sc->sc_if.if_ipackets++;
414 	/*
415 	 * Purge BDP; drop if input error indicated.
416 	 */
417 	if (sc->sc_ifuba.ifu_flags & UBA_NEEDBDP)
418 		UBAPURGE(sc->sc_ifuba.ifu_uba, sc->sc_ifuba.ifu_r.ifrw_bdp);
419 	if (addr->pcl_rsr & PCL_ERR) {
420 		printf("pcl%d: rcv error, rcr=%b rsr=%b\n",
421 			unit, addr->pcl_rcr, PCL_RCSRBITS,
422 			addr->pcl_rsr, PCL_RERRBITS);
423 		sc->sc_if.if_ierrors++;
424 		goto setup;
425 	}
426 	len = PCLMTU + addr->pcl_rdbc;
427 	if (len <= 0 || len > PCLMTU) {
428 		printf("pcl%d: bad len=%d.\n", unit, len);
429 		sc->sc_if.if_ierrors++;
430 		goto setup;
431 	}
432 
433 	/* Really short packets will be part of the startup sequence */
434 	if (len <= 4) {
435 		/* Later, do comming-up processing here */
436 		goto setup;	/* drop packet */
437 	}
438 
439 	/*
440 	 * Pull packet off interface.
441 	 */
442 	m = if_rubaget(&sc->sc_ifuba, len, 0, &sc->sc_if);
443 	if (m == 0)
444 		goto setup;
445 
446 	schednetisr(NETISR_IP);
447 	inq = &ipintrq;
448 
449 	if (IF_QFULL(inq)) {
450 		IF_DROP(inq);
451 		m_freem(m);
452 	} else
453 		IF_ENQUEUE(inq, m);
454 setup:
455 	/*
456 	 * Reset for next packet.
457 	 */
458 	addr->pcl_rcr = PCL_RCINIT;
459 	addr->pcl_rdba = (int)sc->sc_ifuba.ifu_r.ifrw_info;
460 	addr->pcl_rdbc = -PCLMTU;
461 	addr->pcl_rcr = (((int)(sc->sc_ifuba.ifu_w.ifrw_info>>12))&0x0030) |
462 		PCL_RCNPR | PCL_RCVWD | PCL_RCVDAT | PCL_IE;
463 }
464 
465 /*
466  * Process an ioctl request.
467  */
468 /* ARGSUSED */
469 pclioctl(ifp, cmd, data)
470 	register struct ifnet *ifp;
471 	int cmd;
472 	caddr_t data;
473 {
474 	int s = splimp(), error = 0;
475 
476 	switch (cmd) {
477 
478 	case SIOCSIFADDR:
479 		ifp->if_flags |= IFF_UP;
480 		if ((ifp->if_flags & IFF_RUNNING) == 0)
481 			pclinit(ifp->if_unit);
482 		break;
483 
484 	default:
485 		error = EINVAL;
486 	}
487 	splx(s);
488 	return (error);
489 }
490 #endif
491