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