various hacks and fixes: don't sleep when init'ing if from uba reset;
[unix-history] / usr / src / sys / vax / if / if_pcl.c
CommitLineData
da7c5cc6 1/*
2eb4e665 2 * Copyright (c) 1982, 1986 Regents of the University of California.
5f9369d6 3 * All rights reserved.
da7c5cc6 4 *
5f9369d6
KB
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 *
c18e27ed 17 * @(#)if_pcl.c 7.4 (Berkeley) %G%
da7c5cc6 18 */
f5a868d6
SL
19
20#include "pcl.h"
21#if NPCL > 0
22/*
23 * DEC CSS PCL-11B Parallel Communications Interface
24 *
e99dc359 25 * Written by Mike Muuss and Jeff Schwab.
f5a868d6 26 */
c18e27ed 27#include "machine/pte.h"
f5a868d6 28
a6e960e7
JB
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"
f5a868d6
SL
38
39#include "../net/if.h"
40#include "../net/netisr.h"
41#include "../net/route.h"
67eb6277 42
58bca61d 43#ifdef INET
f5a868d6
SL
44#include "../netinet/in.h"
45#include "../netinet/in_systm.h"
58bca61d 46#include "../netinet/in_var.h"
f5a868d6 47#include "../netinet/ip.h"
58bca61d 48#endif
f5a868d6
SL
49
50#include "../vax/cpu.h"
51#include "../vax/mtpr.h"
a6e960e7
JB
52#include "if_pclreg.h"
53#include "if_uba.h"
f5a868d6
SL
54#include "../vaxuba/ubareg.h"
55#include "../vaxuba/ubavar.h"
56
57/* The MTU has been carefully selected to prevent fragmentation <-> ArpaNet */
e99dc359
SL
58#define PCLMTU (1006) /* Max transmission unit (bytes) */
59#define PCLMAXTDM 7 /* Max unit number on TDM bus */
f5a868d6
SL
60
61int pclprobe(), pclattach(), pclrint(), pclxint();
17718098 62int pclinit(), pclioctl(), pcloutput(), pclreset();
f5a868d6
SL
63
64struct uba_device *pclinfo[NPCL];
65u_short pclstd[] = { 0 };
66#define PCLUNIT(x) minor(x)
67struct 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 */
82struct 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 */
e99dc359 89 short sc_bdest; /* buffer's stated destination */
f5a868d6
SL
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 */
97struct 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 */
106pclprobe(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 */
133pclattach(ui)
134 struct uba_device *ui;
135{
136 register struct pcl_softc *sc = &pcl_softc[ui->ui_unit];
f5a868d6
SL
137
138 sc->sc_if.if_unit = ui->ui_unit;
139 sc->sc_if.if_name = "pcl";
140 sc->sc_if.if_mtu = PCLMTU;
f5a868d6
SL
141 sc->sc_if.if_init = pclinit;
142 sc->sc_if.if_output = pcloutput;
17718098 143 sc->sc_if.if_ioctl = pclioctl;
f5a868d6 144 sc->sc_if.if_reset = pclreset;
b05c393d 145 sc->sc_if.if_flags = IFF_BROADCAST;
f5a868d6
SL
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 */
154pclreset(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 */
170pclinit(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
b05c393d 178 if (sc->sc_if.if_addrlist == (struct ifaddr *)0)
17718098 179 return;
f5a868d6
SL
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);
b05c393d 183 sc->sc_if.if_flags &= ~(IFF_UP | IFF_RUNNING);
f5a868d6
SL
184 return;
185 }
b05c393d 186 sc->sc_if.if_flags |= IFF_RUNNING;
f5a868d6
SL
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;
f5a868d6
SL
201 pclstart(unit);
202 splx(s);
f5a868d6
SL
203}
204
205/*
206 * PCL output routine.
207 */
208pcloutput(ifp, m, dst)
209 struct ifnet *ifp;
210 struct mbuf *m;
211 struct sockaddr *dst;
212{
44eb2da3 213 int dest, s, error;
f5a868d6
SL
214 struct pcl_header *pclp;
215 struct mbuf *m2;
216
217 switch (dst->sa_family) {
218
219#ifdef INET
220 case AF_INET:
b05c393d
MK
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);
e99dc359
SL
225 if (dest > PCLMAXTDM) {
226 error = EHOSTUNREACH;
227 goto bad;
228 }
f5a868d6
SL
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 */
4f02060d
KS
244 M_PREPEND(m, sizeof(struct pcl_header), M_DONTWAIT);
245 if (m == 0) {
246 error = ENOBUFS;
247 goto bad;
f5a868d6
SL
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);
267qfull:
268 splx(s);
269bad:
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 */
281pclstart(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 */
e99dc359
SL
310 sc->sc_bdest = mtod(m, struct pcl_header *)->pcl_dest;
311 sc->sc_odest = sc->sc_bdest? sc->sc_bdest: 1;
4f02060d 312 m->m_data += sizeof (struct pcl_header);
f5a868d6 313 m->m_len -= sizeof (struct pcl_header);
4f02060d
KS
314 if (m->m_flags & M_PKTHDR)
315 m->m_pkthdr.len -= sizeof (struct pcl_header);
f5a868d6
SL
316
317 /* Map out to the DMA area */
318 sc->sc_olen = if_wubaput(&sc->sc_ifuba, m);
319
320restart:
321 /*
322 * Have request mapped to UNIBUS for transmission.
e99dc359 323 * Purge any stale data from this BDP, and start the output.
f5a868d6
SL
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 */
354pclxint(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 }
e99dc359
SL
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
f5a868d6
SL
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++;
e99dc359
SL
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 }
f5a868d6
SL
396 }
397 pclstart(unit);
398}
399
400/*
401 * PCL interface receiver interrupt.
402 * If input error just drop packet.
403 */
404pclrint(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;
44eb2da3 410 int len;
f5a868d6 411 register struct ifqueue *inq;
f5a868d6
SL
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 */
67eb6277 442 m = if_rubaget(&sc->sc_ifuba, len, 0, &sc->sc_if);
f5a868d6
SL
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);
454setup:
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}
17718098
SL
464
465/*
466 * Process an ioctl request.
467 */
9205905c 468/* ARGSUSED */
17718098
SL
469pclioctl(ifp, cmd, data)
470 register struct ifnet *ifp;
471 int cmd;
472 caddr_t data;
473{
17718098
SL
474 int s = splimp(), error = 0;
475
476 switch (cmd) {
477
478 case SIOCSIFADDR:
67eb6277 479 ifp->if_flags |= IFF_UP;
b05c393d 480 if ((ifp->if_flags & IFF_RUNNING) == 0)
17718098
SL
481 pclinit(ifp->if_unit);
482 break;
483
484 default:
485 error = EINVAL;
486 }
487 splx(s);
488 return (error);
489}
f5a868d6 490#endif