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