Commit | Line | Data |
---|---|---|
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 | * | |
8b924880 | 6 | * @(#)if_dmc.c 6.9 (Berkeley) %G% |
da7c5cc6 | 7 | */ |
63665984 BJ |
8 | |
9 | #include "dmc.h" | |
10 | #if NDMC > 0 | |
ba0b299a | 11 | |
63665984 BJ |
12 | /* |
13 | * DMC11 device driver, internet version | |
14 | * | |
ba0b299a | 15 | * Bill Nesheim |
032f82b3 | 16 | * Cornell University |
c6020d6c | 17 | * |
ba0b299a MK |
18 | * Lou Salkind |
19 | * New York University | |
63665984 | 20 | */ |
ba0b299a MK |
21 | |
22 | /* #define DEBUG /* for base table dump on fatal error */ | |
23 | ||
961945a8 | 24 | #include "../machine/pte.h" |
63665984 | 25 | |
a6e960e7 JB |
26 | #include "param.h" |
27 | #include "systm.h" | |
28 | #include "mbuf.h" | |
29 | #include "buf.h" | |
032f82b3 | 30 | #include "ioctl.h" /* must precede tty.h */ |
a6e960e7 JB |
31 | #include "tty.h" |
32 | #include "protosw.h" | |
33 | #include "socket.h" | |
34 | #include "vmmac.h" | |
35 | #include "errno.h" | |
a9687d27 BJ |
36 | |
37 | #include "../net/if.h" | |
4fce3bf9 | 38 | #include "../net/netisr.h" |
a9687d27 | 39 | #include "../net/route.h" |
822a4f2c | 40 | |
822a4f2c | 41 | #ifdef INET |
d2cc167c BJ |
42 | #include "../netinet/in.h" |
43 | #include "../netinet/in_systm.h" | |
ba0b299a | 44 | #include "../netinet/ip.h" |
822a4f2c | 45 | #endif |
a9687d27 BJ |
46 | |
47 | #include "../vax/cpu.h" | |
48 | #include "../vax/mtpr.h" | |
a6e960e7 JB |
49 | #include "if_uba.h" |
50 | #include "if_dmc.h" | |
a9687d27 BJ |
51 | #include "../vaxuba/ubareg.h" |
52 | #include "../vaxuba/ubavar.h" | |
63665984 | 53 | |
ba0b299a MK |
54 | #include "../h/time.h" |
55 | #include "../h/kernel.h" | |
56 | ||
57 | int dmctimer; /* timer started? */ | |
58 | int dmc_timeout = 8; /* timeout value */ | |
59 | int dmcwatch(); | |
60 | ||
63665984 BJ |
61 | /* |
62 | * Driver information for auto-configuration stuff. | |
63 | */ | |
17718098 SL |
64 | int dmcprobe(), dmcattach(), dmcinit(), dmcioctl(); |
65 | int dmcoutput(), dmcreset(); | |
63665984 BJ |
66 | struct uba_device *dmcinfo[NDMC]; |
67 | u_short dmcstd[] = { 0 }; | |
68 | struct uba_driver dmcdriver = | |
69 | { dmcprobe, 0, dmcattach, 0, dmcstd, "dmc", dmcinfo }; | |
70 | ||
032f82b3 | 71 | #define NRCV 7 |
ba0b299a | 72 | #define NXMT 3 |
822a4f2c | 73 | #define NCMDS (NRCV+NXMT+4) /* size of command queue */ |
ba0b299a MK |
74 | |
75 | #define printd if(dmcdebug)printf | |
76 | int dmcdebug = 0; | |
032f82b3 TF |
77 | |
78 | /* error reporting intervals */ | |
79 | #define DMC_RPNBFS 50 | |
80 | #define DMC_RPDSC 1 | |
ba0b299a MK |
81 | #define DMC_RPTMO 10 |
82 | #define DMC_RPDCK 10 | |
032f82b3 TF |
83 | |
84 | struct dmc_command { | |
85 | char qp_cmd; /* command */ | |
86 | short qp_ubaddr; /* buffer address */ | |
87 | short qp_cc; /* character count || XMEM */ | |
88 | struct dmc_command *qp_next; /* next command on queue */ | |
89 | }; | |
90 | ||
032f82b3 TF |
91 | struct dmcbufs { |
92 | int ubinfo; /* from uballoc */ | |
93 | short cc; /* buffer size */ | |
94 | short flags; /* access control */ | |
95 | }; | |
96 | #define DBUF_OURS 0 /* buffer is available */ | |
97 | #define DBUF_DMCS 1 /* buffer claimed by somebody */ | |
98 | #define DBUF_XMIT 4 /* transmit buffer */ | |
ba0b299a | 99 | #define DBUF_RCV 8 /* receive buffer */ |
032f82b3 | 100 | |
032f82b3 | 101 | |
63665984 BJ |
102 | /* |
103 | * DMC software status per interface. | |
104 | * | |
105 | * Each interface is referenced by a network interface structure, | |
106 | * sc_if, which the routing code uses to locate the interface. | |
107 | * This structure contains the output queue for the interface, its address, ... | |
032f82b3 TF |
108 | * We also have, for each interface, a set of 7 UBA interface structures |
109 | * for each, which | |
110 | * contain information about the UNIBUS resources held by the interface: | |
63665984 BJ |
111 | * map registers, buffered data paths, etc. Information is cached in this |
112 | * structure for use by the if_uba.c routines in running the interface | |
113 | * efficiently. | |
114 | */ | |
115 | struct dmc_softc { | |
032f82b3 TF |
116 | short sc_oused; /* output buffers currently in use */ |
117 | short sc_iused; /* input buffers given to DMC */ | |
63665984 | 118 | short sc_flag; /* flags */ |
ba0b299a | 119 | int sc_nticks; /* seconds since last interrupt */ |
032f82b3 | 120 | struct ifnet sc_if; /* network-visible interface */ |
ba0b299a | 121 | struct dmcbufs sc_rbufs[NRCV]; /* receive buffer info */ |
032f82b3 | 122 | struct dmcbufs sc_xbufs[NXMT]; /* transmit buffer info */ |
822a4f2c MK |
123 | struct ifubinfo sc_ifuba; /* UNIBUS resources */ |
124 | struct ifrw sc_ifr[NRCV]; /* UNIBUS receive buffer maps */ | |
125 | struct ifxmt sc_ifw[NXMT]; /* UNIBUS receive buffer maps */ | |
63665984 | 126 | int sc_ubinfo; /* UBA mapping info for base table */ |
032f82b3 TF |
127 | int sc_errors[4]; /* non-fatal error counters */ |
128 | #define sc_datck sc_errors[0] | |
129 | #define sc_timeo sc_errors[1] | |
130 | #define sc_nobuf sc_errors[2] | |
131 | #define sc_disc sc_errors[3] | |
132 | /* command queue stuff */ | |
ba0b299a | 133 | struct dmc_command sc_cmdbuf[NCMDS]; |
032f82b3 TF |
134 | struct dmc_command *sc_qhead; /* head of command queue */ |
135 | struct dmc_command *sc_qtail; /* tail of command queue */ | |
136 | struct dmc_command *sc_qactive; /* command in progress */ | |
137 | struct dmc_command *sc_qfreeh; /* head of list of free cmd buffers */ | |
138 | struct dmc_command *sc_qfreet; /* tail of list of free cmd buffers */ | |
139 | /* end command queue stuff */ | |
63665984 BJ |
140 | } dmc_softc[NDMC]; |
141 | ||
142 | /* flags */ | |
ba0b299a MK |
143 | #define DMC_ALLOC 01 /* unibus resources allocated */ |
144 | #define DMC_BMAPPED 02 /* base table mapped */ | |
145 | #define DMC_RESTART 04 /* software restart in progress */ | |
146 | #define DMC_ACTIVE 08 /* device active */ | |
63665984 | 147 | |
ba0b299a MK |
148 | struct dmc_base { |
149 | short d_base[128]; /* DMC base table */ | |
63665984 BJ |
150 | } dmc_base[NDMC]; |
151 | ||
032f82b3 TF |
152 | /* queue manipulation macros */ |
153 | #define QUEUE_AT_HEAD(qp, head, tail) \ | |
154 | (qp)->qp_next = (head); \ | |
155 | (head) = (qp); \ | |
156 | if ((tail) == (struct dmc_command *) 0) \ | |
157 | (tail) = (head) | |
158 | ||
159 | #define QUEUE_AT_TAIL(qp, head, tail) \ | |
160 | if ((tail)) \ | |
161 | (tail)->qp_next = (qp); \ | |
162 | else \ | |
163 | (head) = (qp); \ | |
164 | (qp)->qp_next = (struct dmc_command *) 0; \ | |
165 | (tail) = (qp) | |
166 | ||
167 | #define DEQUEUE(head, tail) \ | |
168 | (head) = (head)->qp_next;\ | |
169 | if ((head) == (struct dmc_command *) 0)\ | |
170 | (tail) = (head) | |
63665984 BJ |
171 | |
172 | dmcprobe(reg) | |
173 | caddr_t reg; | |
174 | { | |
175 | register int br, cvec; | |
176 | register struct dmcdevice *addr = (struct dmcdevice *)reg; | |
177 | register int i; | |
178 | ||
179 | #ifdef lint | |
180 | br = 0; cvec = br; br = cvec; | |
181 | dmcrint(0); dmcxint(0); | |
182 | #endif | |
183 | addr->bsel1 = DMC_MCLR; | |
184 | for (i = 100000; i && (addr->bsel1 & DMC_RUN) == 0; i--) | |
185 | ; | |
ba0b299a MK |
186 | if ((addr->bsel1 & DMC_RUN) == 0) { |
187 | printf("dmcprobe: can't start device\n" ); | |
ee787340 | 188 | return (0); |
ba0b299a | 189 | } |
63665984 | 190 | addr->bsel0 = DMC_RQI|DMC_IEI; |
ba0b299a MK |
191 | /* let's be paranoid */ |
192 | addr->bsel0 |= DMC_RQI|DMC_IEI; | |
193 | DELAY(1000000); | |
63665984 BJ |
194 | addr->bsel1 = DMC_MCLR; |
195 | for (i = 100000; i && (addr->bsel1 & DMC_RUN) == 0; i--) | |
196 | ; | |
ee787340 | 197 | return (1); |
63665984 BJ |
198 | } |
199 | ||
200 | /* | |
201 | * Interface exists: make available by filling in network interface | |
202 | * record. System will initialize the interface when it is ready | |
203 | * to accept packets. | |
204 | */ | |
205 | dmcattach(ui) | |
206 | register struct uba_device *ui; | |
207 | { | |
208 | register struct dmc_softc *sc = &dmc_softc[ui->ui_unit]; | |
209 | ||
210 | sc->sc_if.if_unit = ui->ui_unit; | |
211 | sc->sc_if.if_name = "dmc"; | |
212 | sc->sc_if.if_mtu = DMCMTU; | |
63665984 BJ |
213 | sc->sc_if.if_init = dmcinit; |
214 | sc->sc_if.if_output = dmcoutput; | |
17718098 | 215 | sc->sc_if.if_ioctl = dmcioctl; |
51595ca2 | 216 | sc->sc_if.if_reset = dmcreset; |
032f82b3 | 217 | sc->sc_if.if_flags = IFF_POINTOPOINT; |
822a4f2c | 218 | sc->sc_ifuba.iff_flags = UBA_CANTWAIT; |
032f82b3 | 219 | |
ba0b299a MK |
220 | if (dmctimer == 0) { |
221 | dmctimer = 1; | |
222 | timeout(dmcwatch, (caddr_t) 0, hz); | |
223 | } | |
822a4f2c | 224 | if_attach(&sc->sc_if); |
63665984 BJ |
225 | } |
226 | ||
227 | /* | |
228 | * Reset of interface after UNIBUS reset. | |
7f0e1e06 | 229 | * If interface is on specified UBA, reset its state. |
63665984 BJ |
230 | */ |
231 | dmcreset(unit, uban) | |
232 | int unit, uban; | |
233 | { | |
234 | register struct uba_device *ui; | |
032f82b3 | 235 | register struct dmc_softc *sc = &dmc_softc[unit]; |
63665984 BJ |
236 | |
237 | if (unit >= NDMC || (ui = dmcinfo[unit]) == 0 || ui->ui_alive == 0 || | |
238 | ui->ui_ubanum != uban) | |
239 | return; | |
240 | printf(" dmc%d", unit); | |
ba0b299a | 241 | sc->sc_flag = 0; |
7f0e1e06 | 242 | sc->sc_if.if_flags &= ~IFF_RUNNING; |
63665984 BJ |
243 | dmcinit(unit); |
244 | } | |
245 | ||
246 | /* | |
247 | * Initialization of interface; reinitialize UNIBUS usage. | |
248 | */ | |
249 | dmcinit(unit) | |
250 | int unit; | |
251 | { | |
252 | register struct dmc_softc *sc = &dmc_softc[unit]; | |
253 | register struct uba_device *ui = dmcinfo[unit]; | |
254 | register struct dmcdevice *addr; | |
17718098 | 255 | register struct ifnet *ifp = &sc->sc_if; |
032f82b3 TF |
256 | register struct ifrw *ifrw; |
257 | register struct ifxmt *ifxp; | |
258 | register struct dmcbufs *rp; | |
ba0b299a | 259 | register struct dmc_command *qp; |
7f0e1e06 | 260 | struct ifaddr *ifa; |
63665984 | 261 | int base; |
ba0b299a | 262 | int s; |
63665984 | 263 | |
032f82b3 TF |
264 | addr = (struct dmcdevice *)ui->ui_addr; |
265 | ||
7f0e1e06 MK |
266 | /* |
267 | * Check to see that an address has been set | |
268 | * (both local and destination for an address family). | |
269 | */ | |
270 | for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) | |
271 | if (ifa->ifa_addr.sa_family && ifa->ifa_dstaddr.sa_family) | |
272 | break; | |
273 | if (ifa == (struct ifaddr *) 0) | |
032f82b3 TF |
274 | return; |
275 | ||
276 | if ((addr->bsel1&DMC_RUN) == 0) { | |
277 | printf("dmcinit: DMC not running\n"); | |
7f0e1e06 | 278 | ifp->if_flags &= ~IFF_UP; |
032f82b3 TF |
279 | return; |
280 | } | |
281 | /* map base table */ | |
ba0b299a | 282 | if ((sc->sc_flag & DMC_BMAPPED) == 0) { |
032f82b3 TF |
283 | sc->sc_ubinfo = uballoc(ui->ui_ubanum, |
284 | (caddr_t)&dmc_base[unit], sizeof (struct dmc_base), 0); | |
285 | sc->sc_flag |= DMC_BMAPPED; | |
286 | } | |
287 | /* initialize UNIBUS resources */ | |
288 | sc->sc_iused = sc->sc_oused = 0; | |
7f0e1e06 | 289 | if ((ifp->if_flags & IFF_RUNNING) == 0) { |
822a4f2c MK |
290 | if (if_ubaminit(&sc->sc_ifuba, ui->ui_ubanum, |
291 | sizeof(struct dmc_header), (int)btoc(DMCMTU), | |
292 | sc->sc_ifr, NRCV, sc->sc_ifw, NXMT) == 0) { | |
7f0e1e06 | 293 | printf("dmc%d: can't allocate uba resources\n", unit); |
17718098 SL |
294 | ifp->if_flags &= ~IFF_UP; |
295 | return; | |
296 | } | |
7f0e1e06 | 297 | ifp->if_flags |= IFF_RUNNING; |
032f82b3 TF |
298 | } |
299 | ||
300 | /* initialize buffer pool */ | |
ba0b299a | 301 | /* receives */ |
822a4f2c | 302 | ifrw = &sc->sc_ifr[0]; |
032f82b3 TF |
303 | for (rp = &sc->sc_rbufs[0]; rp < &sc->sc_rbufs[NRCV]; rp++) { |
304 | rp->ubinfo = ifrw->ifrw_info & 0x3ffff; | |
ba0b299a | 305 | rp->cc = DMCMTU + sizeof (struct dmc_header); |
032f82b3 | 306 | rp->flags = DBUF_OURS|DBUF_RCV; |
032f82b3 | 307 | ifrw++; |
63665984 | 308 | } |
032f82b3 | 309 | /* transmits */ |
822a4f2c | 310 | ifxp = &sc->sc_ifw[0]; |
032f82b3 | 311 | for (rp = &sc->sc_xbufs[0]; rp < &sc->sc_xbufs[NXMT]; rp++) { |
822a4f2c | 312 | rp->ubinfo = ifxp->ifw_info & 0x3ffff; |
032f82b3 TF |
313 | rp->cc = 0; |
314 | rp->flags = DBUF_OURS|DBUF_XMIT; | |
032f82b3 | 315 | ifxp++; |
f6311fb6 | 316 | } |
ba0b299a MK |
317 | |
318 | /* set up command queues */ | |
319 | sc->sc_qfreeh = sc->sc_qfreet | |
320 | = sc->sc_qhead = sc->sc_qtail = sc->sc_qactive = | |
321 | (struct dmc_command *)0; | |
322 | /* set up free command buffer list */ | |
323 | for (qp = &sc->sc_cmdbuf[0]; qp < &sc->sc_cmdbuf[NCMDS]; qp++) { | |
324 | QUEUE_AT_HEAD(qp, sc->sc_qfreeh, sc->sc_qfreet); | |
325 | } | |
326 | ||
032f82b3 TF |
327 | /* base in */ |
328 | base = sc->sc_ubinfo & 0x3ffff; | |
ba0b299a | 329 | dmcload(sc, DMC_BASEI, base, (base>>2) & DMC_XMEM); |
032f82b3 TF |
330 | /* specify half duplex operation, flags tell if primary */ |
331 | /* or secondary station */ | |
332 | if (ui->ui_flags == 0) | |
ba0b299a MK |
333 | /* use DDMCP mode in full duplex */ |
334 | dmcload(sc, DMC_CNTLI, 0, 0); | |
032f82b3 | 335 | else if (ui->ui_flags == 1) |
ba0b299a MK |
336 | /* use MAINTENENCE mode */ |
337 | dmcload(sc, DMC_CNTLI, 0, DMC_MAINT ); | |
032f82b3 TF |
338 | else if (ui->ui_flags == 2) |
339 | /* use DDCMP half duplex as primary station */ | |
340 | dmcload(sc, DMC_CNTLI, 0, DMC_HDPLX); | |
341 | else if (ui->ui_flags == 3) | |
342 | /* use DDCMP half duplex as secondary station */ | |
343 | dmcload(sc, DMC_CNTLI, 0, DMC_HDPLX | DMC_SEC); | |
ba0b299a MK |
344 | |
345 | /* enable operation done interrupts */ | |
346 | sc->sc_flag &= ~DMC_ACTIVE; | |
347 | while ((addr->bsel2 & DMC_IEO) == 0) | |
348 | addr->bsel2 |= DMC_IEO; | |
349 | s = spl5(); | |
032f82b3 TF |
350 | /* queue first NRCV buffers for DMC to fill */ |
351 | for (rp = &sc->sc_rbufs[0]; rp < &sc->sc_rbufs[NRCV]; rp++) { | |
352 | rp->flags |= DBUF_DMCS; | |
353 | dmcload(sc, DMC_READ, rp->ubinfo, | |
ba0b299a | 354 | (((rp->ubinfo>>2)&DMC_XMEM) | rp->cc)); |
032f82b3 TF |
355 | sc->sc_iused++; |
356 | } | |
ba0b299a | 357 | splx(s); |
63665984 BJ |
358 | } |
359 | ||
360 | /* | |
361 | * Start output on interface. Get another datagram | |
362 | * to send from the interface queue and map it to | |
363 | * the interface before starting output. | |
032f82b3 TF |
364 | * |
365 | * Must be called at spl 5 | |
63665984 BJ |
366 | */ |
367 | dmcstart(dev) | |
368 | dev_t dev; | |
369 | { | |
370 | int unit = minor(dev); | |
63665984 | 371 | register struct dmc_softc *sc = &dmc_softc[unit]; |
63665984 | 372 | struct mbuf *m; |
032f82b3 TF |
373 | register struct dmcbufs *rp; |
374 | register int n; | |
63665984 | 375 | |
63665984 | 376 | /* |
032f82b3 TF |
377 | * Dequeue up to NXMT requests and map them to the UNIBUS. |
378 | * If no more requests, or no dmc buffers available, just return. | |
63665984 | 379 | */ |
032f82b3 TF |
380 | n = 0; |
381 | for (rp = &sc->sc_xbufs[0]; rp < &sc->sc_xbufs[NXMT]; rp++ ) { | |
382 | /* find an available buffer */ | |
ba0b299a | 383 | if ((rp->flags & DBUF_DMCS) == 0) { |
032f82b3 TF |
384 | IF_DEQUEUE(&sc->sc_if.if_snd, m); |
385 | if (m == 0) | |
386 | return; | |
032f82b3 TF |
387 | /* mark it dmcs */ |
388 | rp->flags |= (DBUF_DMCS); | |
389 | /* | |
390 | * Have request mapped to UNIBUS for transmission | |
391 | * and start the output. | |
392 | */ | |
822a4f2c | 393 | rp->cc = if_ubaput(&sc->sc_ifuba, &sc->sc_ifw[n], m); |
ba0b299a | 394 | rp->cc &= DMC_CCOUNT; |
032f82b3 TF |
395 | sc->sc_oused++; |
396 | dmcload(sc, DMC_WRITE, rp->ubinfo, | |
397 | rp->cc | ((rp->ubinfo>>2)&DMC_XMEM)); | |
398 | } | |
399 | n++; | |
400 | } | |
63665984 BJ |
401 | } |
402 | ||
403 | /* | |
404 | * Utility routine to load the DMC device registers. | |
405 | */ | |
406 | dmcload(sc, type, w0, w1) | |
407 | register struct dmc_softc *sc; | |
408 | int type, w0, w1; | |
409 | { | |
410 | register struct dmcdevice *addr; | |
032f82b3 TF |
411 | register int unit, sps; |
412 | register struct dmc_command *qp; | |
63665984 | 413 | |
ba0b299a | 414 | unit = sc - dmc_softc; |
63665984 BJ |
415 | addr = (struct dmcdevice *)dmcinfo[unit]->ui_addr; |
416 | sps = spl5(); | |
032f82b3 TF |
417 | |
418 | /* grab a command buffer from the free list */ | |
419 | if ((qp = sc->sc_qfreeh) == (struct dmc_command *)0) | |
420 | panic("dmc command queue overflow"); | |
421 | DEQUEUE(sc->sc_qfreeh, sc->sc_qfreet); | |
422 | ||
423 | /* fill in requested info */ | |
424 | qp->qp_cmd = (type | DMC_RQI); | |
425 | qp->qp_ubaddr = w0; | |
426 | qp->qp_cc = w1; | |
427 | ||
428 | if (sc->sc_qactive) { /* command in progress */ | |
429 | if (type == DMC_READ) { | |
430 | QUEUE_AT_HEAD(qp, sc->sc_qhead, sc->sc_qtail); | |
431 | } else { | |
432 | QUEUE_AT_TAIL(qp, sc->sc_qhead, sc->sc_qtail); | |
433 | } | |
434 | } else { /* command port free */ | |
435 | sc->sc_qactive = qp; | |
436 | addr->bsel0 = qp->qp_cmd; | |
63665984 | 437 | dmcrint(unit); |
032f82b3 | 438 | } |
63665984 BJ |
439 | splx(sps); |
440 | } | |
441 | ||
442 | /* | |
443 | * DMC interface receiver interrupt. | |
444 | * Ready to accept another command, | |
445 | * pull one off the command queue. | |
446 | */ | |
447 | dmcrint(unit) | |
448 | int unit; | |
449 | { | |
450 | register struct dmc_softc *sc; | |
451 | register struct dmcdevice *addr; | |
032f82b3 | 452 | register struct dmc_command *qp; |
63665984 | 453 | register int n; |
63665984 BJ |
454 | |
455 | addr = (struct dmcdevice *)dmcinfo[unit]->ui_addr; | |
456 | sc = &dmc_softc[unit]; | |
032f82b3 | 457 | if ((qp = sc->sc_qactive) == (struct dmc_command *) 0) { |
ba0b299a | 458 | printf("dmc%d: dmcrint no command\n", unit); |
032f82b3 TF |
459 | return; |
460 | } | |
63665984 | 461 | while (addr->bsel0&DMC_RDYI) { |
032f82b3 TF |
462 | addr->sel4 = qp->qp_ubaddr; |
463 | addr->sel6 = qp->qp_cc; | |
63665984 | 464 | addr->bsel0 &= ~(DMC_IEI|DMC_RQI); |
032f82b3 TF |
465 | /* free command buffer */ |
466 | QUEUE_AT_HEAD(qp, sc->sc_qfreeh, sc->sc_qfreet); | |
467 | while (addr->bsel0 & DMC_RDYI) { | |
468 | /* | |
469 | * Can't check for RDYO here 'cause | |
470 | * this routine isn't reentrant! | |
471 | */ | |
472 | DELAY(5); | |
473 | } | |
474 | /* move on to next command */ | |
ba0b299a MK |
475 | if ((sc->sc_qactive = sc->sc_qhead) == (struct dmc_command *)0) |
476 | break; /* all done */ | |
032f82b3 TF |
477 | /* more commands to do, start the next one */ |
478 | qp = sc->sc_qactive; | |
479 | DEQUEUE(sc->sc_qhead, sc->sc_qtail); | |
480 | addr->bsel0 = qp->qp_cmd; | |
63665984 | 481 | n = RDYSCAN; |
ba0b299a MK |
482 | while (n-- > 0) |
483 | if ((addr->bsel0&DMC_RDYI) || (addr->bsel2&DMC_RDYO)) | |
484 | break; | |
032f82b3 TF |
485 | } |
486 | if (sc->sc_qactive) { | |
487 | addr->bsel0 |= DMC_IEI|DMC_RQI; | |
488 | /* VMS does it twice !*$%@# */ | |
489 | addr->bsel0 |= DMC_IEI|DMC_RQI; | |
63665984 | 490 | } |
ba0b299a | 491 | |
63665984 BJ |
492 | } |
493 | ||
494 | /* | |
495 | * DMC interface transmitter interrupt. | |
032f82b3 | 496 | * A transfer may have completed, check for errors. |
63665984 BJ |
497 | * If it was a read, notify appropriate protocol. |
498 | * If it was a write, pull the next one off the queue. | |
499 | */ | |
500 | dmcxint(unit) | |
501 | int unit; | |
502 | { | |
503 | register struct dmc_softc *sc; | |
abcd48c6 | 504 | register struct ifnet *ifp; |
63665984 BJ |
505 | struct uba_device *ui = dmcinfo[unit]; |
506 | struct dmcdevice *addr; | |
507 | struct mbuf *m; | |
ba0b299a | 508 | struct ifqueue *inq; |
032f82b3 TF |
509 | int arg, pkaddr, cmd, len; |
510 | register struct ifrw *ifrw; | |
511 | register struct dmcbufs *rp; | |
ba0b299a MK |
512 | register struct ifxmt *ifxp; |
513 | struct dmc_header *dh; | |
514 | int off, resid; | |
63665984 BJ |
515 | |
516 | addr = (struct dmcdevice *)ui->ui_addr; | |
63665984 | 517 | sc = &dmc_softc[unit]; |
abcd48c6 | 518 | ifp = &sc->sc_if; |
032f82b3 | 519 | |
ba0b299a MK |
520 | while (addr->bsel2 & DMC_RDYO) { |
521 | ||
522 | cmd = addr->bsel2 & 0xff; | |
523 | arg = addr->sel6 & 0xffff; | |
524 | /* reconstruct UNIBUS address of buffer returned to us */ | |
525 | pkaddr = ((arg&DMC_XMEM)<<2) | (addr->sel4 & 0xffff); | |
526 | /* release port */ | |
527 | addr->bsel2 &= ~DMC_RDYO; | |
528 | switch (cmd & 07) { | |
529 | ||
530 | case DMC_OUR: | |
531 | /* | |
532 | * A read has completed. | |
533 | * Pass packet to type specific | |
534 | * higher-level input routine. | |
535 | */ | |
536 | ifp->if_ipackets++; | |
537 | /* find location in dmcuba struct */ | |
822a4f2c | 538 | ifrw= &sc->sc_ifr[0]; |
ba0b299a MK |
539 | for (rp = &sc->sc_rbufs[0]; rp < &sc->sc_rbufs[NRCV]; rp++) { |
540 | if(rp->ubinfo == pkaddr) | |
541 | break; | |
542 | ifrw++; | |
543 | } | |
544 | if (rp >= &sc->sc_rbufs[NRCV]) | |
545 | panic("dmc rcv"); | |
546 | if ((rp->flags & DBUF_DMCS) == 0) | |
547 | printf("dmc%d: done unalloc rbuf\n", unit); | |
548 | ||
549 | len = (arg & DMC_CCOUNT) - sizeof (struct dmc_header); | |
550 | if (len < 0 || len > DMCMTU) { | |
551 | ifp->if_ierrors++; | |
552 | printd("dmc%d: bad rcv pkt addr 0x%x len 0x%x\n", | |
553 | unit, pkaddr, len); | |
554 | goto setup; | |
555 | } | |
556 | /* | |
557 | * Deal with trailer protocol: if type is trailer | |
558 | * get true type from first 16-bit word past data. | |
559 | * Remember that type was trailer by setting off. | |
560 | */ | |
561 | dh = (struct dmc_header *)ifrw->ifrw_addr; | |
562 | dh->dmc_type = ntohs((u_short)dh->dmc_type); | |
563 | #define dmcdataaddr(dh, off, type) ((type)(((caddr_t)((dh)+1)+(off)))) | |
564 | if (dh->dmc_type >= DMC_TRAILER && | |
565 | dh->dmc_type < DMC_TRAILER+DMC_NTRAILER) { | |
566 | off = (dh->dmc_type - DMC_TRAILER) * 512; | |
567 | if (off >= DMCMTU) | |
568 | goto setup; /* sanity */ | |
569 | dh->dmc_type = ntohs(*dmcdataaddr(dh, off, u_short *)); | |
570 | resid = ntohs(*(dmcdataaddr(dh, off+2, u_short *))); | |
571 | if (off + resid > len) | |
572 | goto setup; /* sanity */ | |
573 | len = off + resid; | |
574 | } else | |
575 | off = 0; | |
576 | if (len == 0) | |
577 | goto setup; | |
578 | ||
579 | /* | |
580 | * Pull packet off interface. Off is nonzero if | |
581 | * packet has trailing header; dmc_get will then | |
582 | * force this header information to be at the front, | |
583 | * but we still have to drop the type and length | |
584 | * which are at the front of any trailer data. | |
585 | */ | |
822a4f2c | 586 | m = if_ubaget(&sc->sc_ifuba, ifrw, len, off, ifp); |
ba0b299a MK |
587 | if (m == 0) |
588 | goto setup; | |
589 | if (off) { | |
822a4f2c MK |
590 | struct ifnet *ifp; |
591 | ||
592 | ifp = *(mtod(m, struct ifnet **)); | |
ba0b299a MK |
593 | m->m_off += 2 * sizeof (u_short); |
594 | m->m_len -= 2 * sizeof (u_short); | |
822a4f2c | 595 | *(mtod(m, struct ifnet **)) = ifp; |
ba0b299a MK |
596 | } |
597 | switch (dh->dmc_type) { | |
63665984 | 598 | |
63665984 | 599 | #ifdef INET |
ba0b299a MK |
600 | case DMC_IPTYPE: |
601 | schednetisr(NETISR_IP); | |
602 | inq = &ipintrq; | |
603 | break; | |
63665984 | 604 | #endif |
ba0b299a MK |
605 | default: |
606 | m_freem(m); | |
607 | goto setup; | |
608 | } | |
63665984 | 609 | |
ba0b299a MK |
610 | if (IF_QFULL(inq)) { |
611 | IF_DROP(inq); | |
612 | m_freem(m); | |
613 | } else | |
614 | IF_ENQUEUE(inq, m); | |
032f82b3 | 615 | |
ba0b299a MK |
616 | setup: |
617 | /* is this needed? */ | |
618 | rp->ubinfo = ifrw->ifrw_info & 0x3ffff; | |
63665984 | 619 | |
ba0b299a MK |
620 | dmcload(sc, DMC_READ, rp->ubinfo, |
621 | ((rp->ubinfo >> 2) & DMC_XMEM) | rp->cc); | |
622 | break; | |
63665984 | 623 | |
ba0b299a MK |
624 | case DMC_OUX: |
625 | /* | |
626 | * A write has completed, start another | |
627 | * transfer if there is more data to send. | |
628 | */ | |
629 | ifp->if_opackets++; | |
630 | /* find associated dmcbuf structure */ | |
822a4f2c | 631 | ifxp = &sc->sc_ifw[0]; |
ba0b299a MK |
632 | for (rp = &sc->sc_xbufs[0]; rp < &sc->sc_xbufs[NXMT]; rp++) { |
633 | if(rp->ubinfo == pkaddr) | |
634 | break; | |
635 | ifxp++; | |
636 | } | |
637 | if (rp >= &sc->sc_xbufs[NXMT]) { | |
638 | printf("dmc%d: bad packet address 0x%x\n", | |
639 | unit, pkaddr); | |
640 | break; | |
641 | } | |
642 | if ((rp->flags & DBUF_DMCS) == 0) | |
643 | printf("dmc%d: unallocated packet 0x%x\n", | |
644 | unit, pkaddr); | |
645 | /* mark buffer free */ | |
822a4f2c MK |
646 | if (ifxp->ifw_xtofree) { |
647 | (void)m_freem(ifxp->ifw_xtofree); | |
648 | ifxp->ifw_xtofree = 0; | |
ba0b299a MK |
649 | } |
650 | rp->flags &= ~DBUF_DMCS; | |
651 | sc->sc_oused--; | |
652 | sc->sc_nticks = 0; | |
653 | sc->sc_flag |= DMC_ACTIVE; | |
032f82b3 | 654 | break; |
ba0b299a MK |
655 | |
656 | case DMC_CNTLO: | |
657 | arg &= DMC_CNTMASK; | |
658 | if (arg & DMC_FATAL) { | |
659 | printd("dmc%d: fatal error, flags=%b\n", | |
660 | unit, arg, CNTLO_BITS); | |
ba0b299a MK |
661 | dmcrestart(unit); |
662 | break; | |
663 | } | |
664 | /* ACCUMULATE STATISTICS */ | |
032f82b3 TF |
665 | switch(arg) { |
666 | case DMC_NOBUFS: | |
ba0b299a MK |
667 | ifp->if_ierrors++; |
668 | if ((sc->sc_nobuf++ % DMC_RPNBFS) == 0) | |
669 | goto report; | |
670 | break; | |
032f82b3 | 671 | case DMC_DISCONN: |
ba0b299a MK |
672 | if ((sc->sc_disc++ % DMC_RPDSC) == 0) |
673 | goto report; | |
674 | break; | |
032f82b3 | 675 | case DMC_TIMEOUT: |
ba0b299a MK |
676 | if ((sc->sc_timeo++ % DMC_RPTMO) == 0) |
677 | goto report; | |
678 | break; | |
032f82b3 | 679 | case DMC_DATACK: |
ba0b299a MK |
680 | ifp->if_oerrors++; |
681 | if ((sc->sc_datck++ % DMC_RPDCK) == 0) | |
682 | goto report; | |
683 | break; | |
032f82b3 TF |
684 | default: |
685 | goto report; | |
686 | } | |
687 | break; | |
688 | report: | |
ba0b299a MK |
689 | printd("dmc%d: soft error, flags=%b\n", unit, |
690 | arg, CNTLO_BITS); | |
691 | if ((sc->sc_flag & DMC_RESTART) == 0) { | |
692 | /* | |
693 | * kill off the dmc to get things | |
694 | * going again by generating a | |
695 | * procedure error | |
696 | */ | |
697 | sc->sc_flag |= DMC_RESTART; | |
698 | arg = sc->sc_ubinfo & 0x3ffff; | |
699 | dmcload(sc, DMC_BASEI, arg, (arg>>2)&DMC_XMEM); | |
700 | } | |
701 | break; | |
63665984 | 702 | |
ba0b299a MK |
703 | default: |
704 | printf("dmc%d: bad control %o\n", unit, cmd); | |
705 | break; | |
706 | } | |
63665984 | 707 | } |
ba0b299a | 708 | dmcstart(unit); |
032f82b3 | 709 | return; |
63665984 BJ |
710 | } |
711 | ||
712 | /* | |
713 | * DMC output routine. | |
ba0b299a MK |
714 | * Encapsulate a packet of type family for the dmc. |
715 | * Use trailer local net encapsulation if enough data in first | |
716 | * packet leaves a multiple of 512 bytes of data in remainder. | |
63665984 | 717 | */ |
ba0b299a | 718 | dmcoutput(ifp, m0, dst) |
63665984 | 719 | register struct ifnet *ifp; |
ba0b299a | 720 | register struct mbuf *m0; |
ee787340 | 721 | struct sockaddr *dst; |
63665984 | 722 | { |
ba0b299a MK |
723 | int type, error, s; |
724 | register struct mbuf *m = m0; | |
725 | register struct dmc_header *dh; | |
726 | register int off; | |
727 | ||
728 | switch (dst->sa_family) { | |
729 | #ifdef INET | |
730 | case AF_INET: | |
731 | off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; | |
732 | if ((ifp->if_flags & IFF_NOTRAILERS) == 0) | |
733 | if (off > 0 && (off & 0x1ff) == 0 && | |
734 | m->m_off >= MMINOFF + 2 * sizeof (u_short)) { | |
735 | type = DMC_TRAILER + (off>>9); | |
736 | m->m_off -= 2 * sizeof (u_short); | |
737 | m->m_len += 2 * sizeof (u_short); | |
738 | *mtod(m, u_short *) = htons((u_short)DMC_IPTYPE); | |
739 | *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len); | |
740 | goto gottrailertype; | |
741 | } | |
742 | type = DMC_IPTYPE; | |
743 | off = 0; | |
744 | goto gottype; | |
745 | #endif | |
63665984 | 746 | |
ba0b299a MK |
747 | case AF_UNSPEC: |
748 | dh = (struct dmc_header *)dst->sa_data; | |
749 | type = dh->dmc_type; | |
750 | goto gottype; | |
751 | ||
752 | default: | |
753 | printf("dmc%d: can't handle af%d\n", ifp->if_unit, | |
754 | dst->sa_family); | |
755 | error = EAFNOSUPPORT; | |
756 | goto bad; | |
63665984 | 757 | } |
ba0b299a MK |
758 | |
759 | gottrailertype: | |
760 | /* | |
761 | * Packet to be sent as a trailer; move first packet | |
762 | * (control information) to end of chain. | |
763 | */ | |
764 | while (m->m_next) | |
765 | m = m->m_next; | |
766 | m->m_next = m0; | |
767 | m = m0->m_next; | |
768 | m0->m_next = 0; | |
769 | m0 = m; | |
770 | ||
771 | gottype: | |
772 | /* | |
773 | * Add local network header | |
774 | * (there is space for a uba on a vax to step on) | |
775 | */ | |
776 | if (m->m_off > MMAXOFF || | |
777 | MMINOFF + sizeof(struct dmc_header) > m->m_off) { | |
778 | m = m_get(M_DONTWAIT, MT_HEADER); | |
779 | if (m == 0) { | |
780 | error = ENOBUFS; | |
781 | goto bad; | |
782 | } | |
783 | m->m_next = m0; | |
784 | m->m_off = MMINOFF; | |
785 | m->m_len = sizeof (struct dmc_header); | |
786 | } else { | |
787 | m->m_off -= sizeof (struct dmc_header); | |
788 | m->m_len += sizeof (struct dmc_header); | |
789 | } | |
790 | dh = mtod(m, struct dmc_header *); | |
791 | dh->dmc_type = htons((u_short)type); | |
792 | ||
793 | /* | |
794 | * Queue message on interface, and start output if interface | |
795 | * not yet active. | |
796 | */ | |
797 | s = splimp(); | |
1e977657 BJ |
798 | if (IF_QFULL(&ifp->if_snd)) { |
799 | IF_DROP(&ifp->if_snd); | |
ee787340 | 800 | m_freem(m); |
1e977657 | 801 | splx(s); |
8a2f82db | 802 | return (ENOBUFS); |
1e977657 | 803 | } |
63665984 | 804 | IF_ENQUEUE(&ifp->if_snd, m); |
032f82b3 | 805 | dmcstart(ifp->if_unit); |
63665984 | 806 | splx(s); |
8a2f82db | 807 | return (0); |
ba0b299a MK |
808 | |
809 | bad: | |
810 | m_freem(m0); | |
811 | return (error); | |
63665984 | 812 | } |
17718098 | 813 | |
ba0b299a | 814 | |
17718098 SL |
815 | /* |
816 | * Process an ioctl request. | |
817 | */ | |
822a4f2c | 818 | /* ARGSUSED */ |
17718098 SL |
819 | dmcioctl(ifp, cmd, data) |
820 | register struct ifnet *ifp; | |
821 | int cmd; | |
822 | caddr_t data; | |
823 | { | |
17718098 SL |
824 | int s = splimp(), error = 0; |
825 | ||
826 | switch (cmd) { | |
827 | ||
828 | case SIOCSIFADDR: | |
032f82b3 | 829 | ifp->if_flags |= IFF_UP; |
7f0e1e06 MK |
830 | if ((ifp->if_flags & IFF_RUNNING) == 0) |
831 | dmcinit(ifp->if_unit); | |
17718098 SL |
832 | break; |
833 | ||
834 | case SIOCSIFDSTADDR: | |
7f0e1e06 MK |
835 | if ((ifp->if_flags & IFF_RUNNING) == 0) |
836 | dmcinit(ifp->if_unit); | |
17718098 | 837 | break; |
032f82b3 | 838 | |
17718098 SL |
839 | default: |
840 | error = EINVAL; | |
841 | } | |
842 | splx(s); | |
843 | return (error); | |
844 | } | |
032f82b3 | 845 | |
ba0b299a MK |
846 | /* |
847 | * Restart after a fatal error. | |
848 | * Clear device and reinitialize. | |
849 | */ | |
850 | dmcrestart(unit) | |
851 | int unit; | |
852 | { | |
853 | register struct dmc_softc *sc = &dmc_softc[unit]; | |
854 | register struct uba_device *ui = dmcinfo[unit]; | |
855 | register struct dmcdevice *addr; | |
856 | register struct ifxmt *ifxp; | |
857 | register int i; | |
822a4f2c | 858 | struct ifubinfo *ifu; |
ba0b299a MK |
859 | |
860 | addr = (struct dmcdevice *)ui->ui_addr; | |
861 | ifu = &sc->sc_ifuba; | |
862 | #ifdef DEBUG | |
863 | /* dump base table */ | |
864 | printf("dmc%d base table:\n", unit); | |
865 | for (i = 0; i < sizeof (struct dmc_base); i++) | |
866 | printf("%o\n" ,dmc_base[unit].d_base[i]); | |
867 | #endif | |
868 | /* | |
869 | * Let the DMR finish the MCLR. At 1 Mbit, it should do so | |
870 | * in about a max of 6.4 milliseconds with diagnostics enabled. | |
871 | */ | |
872 | addr->bsel1 = DMC_MCLR; | |
873 | for (i = 100000; i && (addr->bsel1 & DMC_RUN) == 0; i--) | |
874 | ; | |
875 | /* Did the timer expire or did the DMR finish? */ | |
876 | if ((addr->bsel1 & DMC_RUN) == 0) { | |
877 | printf("dmc%d: M820 Test Failed\n", unit); | |
878 | return; | |
879 | } | |
880 | ||
822a4f2c MK |
881 | for (ifxp = sc->sc_ifw; ifxp < &sc->sc_ifw[NXMT]; ifxp++) { |
882 | if (ifxp->ifw_xtofree) { | |
883 | (void) m_freem(ifxp->ifw_xtofree); | |
884 | ifxp->ifw_xtofree = 0; | |
ba0b299a MK |
885 | } |
886 | } | |
887 | ||
888 | /* restart DMC */ | |
889 | dmcinit(unit); | |
890 | sc->sc_flag &= ~DMC_RESTART; | |
891 | sc->sc_if.if_collisions++; /* why not? */ | |
892 | } | |
893 | ||
894 | /* | |
895 | * Check to see that transmitted packets don't | |
896 | * lose interrupts. The device has to be active. | |
897 | */ | |
898 | dmcwatch() | |
899 | { | |
900 | register struct uba_device *ui; | |
901 | register struct dmc_softc *sc; | |
902 | struct dmcdevice *addr; | |
903 | register int i; | |
904 | ||
905 | for (i = 0; i < NDMC; i++) { | |
906 | sc = &dmc_softc[i]; | |
907 | if ((sc->sc_flag & DMC_ACTIVE) == 0) | |
908 | continue; | |
909 | if ((ui = dmcinfo[i]) == 0 || ui->ui_alive == 0) | |
910 | continue; | |
911 | if (sc->sc_oused) { | |
912 | sc->sc_nticks++; | |
913 | if (sc->sc_nticks > dmc_timeout) { | |
914 | sc->sc_nticks = 0; | |
915 | addr = (struct dmcdevice *)ui->ui_addr; | |
916 | printd("dmc%d hung: bsel0=%b bsel2=%b\n", i, | |
917 | addr->bsel0 & 0xff, DMC0BITS, | |
918 | addr->bsel2 & 0xff, DMC2BITS); | |
919 | dmcrestart(i); | |
920 | } | |
921 | } | |
922 | } | |
923 | timeout(dmcwatch, (caddr_t) 0, hz); | |
924 | } | |
44eb2da3 | 925 | #endif |