Commit | Line | Data |
---|---|---|
ecfa166d | 1 | /* |
d9bcdfcc | 2 | * @(#)if_dmv.c 7.2 (Berkeley) %G% |
ecfa166d MK |
3 | * DMV-11 Driver |
4 | * | |
5 | * Qbus Sync DDCMP interface - DMV operated in full duplex, point to point mode | |
6 | * | |
7 | * Derived from 4.3 release if_dmv.c rev. 6.12 dated 4/23/86 | |
8 | * (which wasn't the 4.3 release!) | |
9 | * | |
10 | * Bob Kridle | |
11 | * mt Xinu | |
12 | */ | |
13 | ||
14 | #include "dmv.h" | |
15 | #if NDMV > 0 | |
16 | ||
17 | ||
18 | #include "../machine/pte.h" | |
19 | ||
20 | #include "param.h" | |
21 | #include "systm.h" | |
22 | #include "mbuf.h" | |
23 | #include "buf.h" | |
24 | #include "ioctl.h" /* must precede tty.h */ | |
25 | #include "tty.h" | |
26 | #include "protosw.h" | |
27 | #include "socket.h" | |
28 | #include "syslog.h" | |
29 | #include "vmmac.h" | |
30 | #include "errno.h" | |
31 | ||
32 | #include "../net/if.h" | |
33 | #include "../net/netisr.h" | |
34 | #include "../net/route.h" | |
35 | ||
36 | #ifdef INET | |
37 | #include "../netinet/in.h" | |
38 | #include "../netinet/in_systm.h" | |
39 | #include "../netinet/in_var.h" | |
40 | #include "../netinet/ip.h" | |
41 | #endif | |
42 | ||
43 | #include "../vax/cpu.h" | |
44 | #include "../vax/mtpr.h" | |
45 | #include "if_uba.h" | |
46 | #include "if_dmv.h" | |
47 | #include "../vaxuba/ubareg.h" | |
48 | #include "../vaxuba/ubavar.h" | |
49 | ||
50 | #include "../h/time.h" | |
51 | #include "../h/kernel.h" | |
52 | ||
53 | int dmvtimer; /* timer started? */ | |
54 | int dmv_timeout = 8; /* timeout value */ | |
55 | int dmvwatch(); | |
56 | ||
57 | /* | |
58 | * Driver information for auto-configuration stuff. | |
59 | */ | |
60 | int dmvprobe(), dmvattach(), dmvinit(), dmvioctl(); | |
61 | int dmvoutput(), dmvreset(); | |
62 | struct uba_device *dmvinfo[NDMV]; | |
63 | u_short dmvstd[] = { 0 }; | |
64 | struct uba_driver dmvdriver = | |
65 | { dmvprobe, 0, dmvattach, 0, dmvstd, "dmv", dmvinfo }; | |
66 | ||
67 | /* | |
68 | * Don't really know how many buffers/commands can be queued to a DMV-11. | |
69 | * Manual doesn't say... Perhaps we can look at a DEC driver some day. | |
70 | * These numbers ame from DMV/DMR driver. | |
71 | */ | |
72 | #define NRCV 5 | |
73 | #define NXMT 3 | |
74 | #define NCMDS (NRCV+NXMT+4) /* size of command queue */ | |
75 | ||
76 | #define printd if (sc->sc_if.if_flags & IFF_DEBUG) \ | |
77 | printf("DMVDEBUG: dmv%d: ", unit), printf | |
78 | ||
79 | /* error reporting intervals */ | |
80 | ||
81 | #define DMV_RPRTE 1 | |
82 | #define DMV_RPTTE 1 | |
83 | #define DMV_RPSTE 1 | |
84 | #define DMV_RPNXM 1 | |
85 | #define DMV_RPMODD 1 | |
86 | #define DMV_RPQOVF 1 | |
87 | #define DMV_RPCXRL 1 | |
d9bcdfcc MK |
88 | |
89 | /* number of errors to accept before trying a reset */ | |
90 | #define DMV_RPUNKNOWN 10 | |
ecfa166d MK |
91 | |
92 | struct dmv_command { | |
93 | u_char qp_mask; /* Which registers to set up */ | |
94 | #define QP_TRIB 0x01 | |
95 | #define QP_SEL4 0x02 | |
96 | #define QP_SEL6 0x04 | |
97 | #define QP_SEL10 0x08 | |
98 | u_char qp_cmd; | |
99 | u_char qp_tributary; | |
100 | u_short qp_sel4; | |
101 | u_short qp_sel6; | |
102 | u_short qp_sel10; | |
103 | struct dmv_command *qp_next; /* next command on queue */ | |
104 | }; | |
105 | ||
106 | #define qp_lowbufaddr qp_ | |
107 | ||
108 | struct dmvbufs { | |
109 | int ubinfo; /* from uballoc */ | |
110 | short cc; /* buffer size */ | |
111 | short flags; /* access control */ | |
112 | }; | |
113 | ||
114 | #define DBUF_OURS 0 /* buffer is available */ | |
115 | #define DBUF_DMVS 1 /* buffer claimed by somebody */ | |
116 | #define DBUF_XMIT 4 /* transmit buffer */ | |
117 | #define DBUF_RCV 8 /* receive buffer */ | |
118 | ||
119 | ||
120 | /* | |
121 | * DMV software status per interface. | |
122 | * | |
123 | * Each interface is referenced by a network interface structure, | |
124 | * sc_if, which the routing code uses to locate the interface. | |
125 | * This structure contains the output queue for the interface, its address, ... | |
126 | * We also have, for each interface, a set of 7 UBA interface structures | |
127 | * for each, which | |
128 | * contain information about the UNIBUS resources held by the interface: | |
129 | * map registers, buffered data paths, etc. Information is cached in this | |
130 | * structure for use by the if_uba.c routines in running the interface | |
131 | * efficiently. | |
132 | */ | |
133 | struct dmv_softc { | |
134 | struct ifnet sc_if; /* network-visible interface */ | |
135 | struct dmvbufs sc_rbufs[NRCV]; /* receive buffer info */ | |
136 | struct dmvbufs sc_xbufs[NXMT]; /* transmit buffer info */ | |
137 | struct ifubinfo sc_ifuba; /* UNIBUS resources */ | |
138 | struct ifrw sc_ifr[NRCV]; /* UNIBUS receive buffer maps */ | |
139 | struct ifxmt sc_ifw[NXMT]; /* UNIBUS receive buffer maps */ | |
140 | short sc_oused; /* output buffers currently in use */ | |
141 | short sc_iused; /* input buffers given to DMV */ | |
142 | short sc_flag; /* flags */ | |
143 | int sc_nticks; /* seconds since last interrupt */ | |
144 | int sc_ubinfo; /* UBA mapping info for base table */ | |
145 | int sc_errors[8]; /* error counters */ | |
146 | #define sc_rte sc_errors[0] /* receive threshhold error */ | |
147 | #define sc_xte sc_errors[1] /* xmit threshhold error */ | |
148 | #define sc_ste sc_errors[2] /* select threshhold error */ | |
149 | #define sc_nxm sc_errors[3] /* non-existant memory */ | |
150 | #define sc_modd sc_errors[4] /* modem disconnect */ | |
151 | #define sc_qovf sc_errors[5] /* command/response queue overflow */ | |
152 | #define sc_cxrl sc_errors[6] /* carrier loss */ | |
153 | #define sc_unknown sc_errors[7] /* other errors - look in DMV manual */ | |
154 | /* command queue stuff */ | |
155 | struct dmv_command sc_cmdbuf[NCMDS]; | |
156 | struct dmv_command *sc_qhead; /* head of command queue */ | |
157 | struct dmv_command *sc_qtail; /* tail of command queue */ | |
158 | struct dmv_command *sc_qactive; /* command in progress */ | |
159 | struct dmv_command *sc_qfreeh; /* head of list of free cmd buffers */ | |
160 | struct dmv_command *sc_qfreet; /* tail of list of free cmd buffers */ | |
161 | /* end command queue stuff */ | |
162 | } dmv_softc[NDMV]; | |
163 | ||
164 | /* flags */ | |
165 | #define DMV_ALLOC 0x01 /* unibus resources allocated */ | |
166 | #define DMV_RESTART 0x04 /* software restart in progress */ | |
167 | #define DMV_ACTIVE 0x08 /* device active */ | |
168 | #define DMV_RUNNING 0x20 /* device initialized */ | |
169 | ||
170 | ||
171 | /* queue manipulation macros */ | |
172 | #define QUEUE_AT_HEAD(qp, head, tail) \ | |
173 | (qp)->qp_next = (head); \ | |
174 | (head) = (qp); \ | |
175 | if ((tail) == (struct dmv_command *) 0) \ | |
176 | (tail) = (head) | |
177 | ||
178 | #define QUEUE_AT_TAIL(qp, head, tail) \ | |
179 | if ((tail)) \ | |
180 | (tail)->qp_next = (qp); \ | |
181 | else \ | |
182 | (head) = (qp); \ | |
183 | (qp)->qp_next = (struct dmv_command *) 0; \ | |
184 | (tail) = (qp) | |
185 | ||
186 | #define DEQUEUE(head, tail) \ | |
187 | (head) = (head)->qp_next;\ | |
188 | if ((head) == (struct dmv_command *) 0)\ | |
189 | (tail) = (head) | |
190 | ||
191 | dmvprobe(reg) | |
192 | caddr_t reg; | |
193 | { | |
194 | register int br, cvec; | |
195 | register struct dmvdevice *addr = (struct dmvdevice *)reg; | |
196 | register int i; | |
197 | ||
198 | #ifdef lint | |
199 | br = 0; cvec = br; br = cvec; | |
200 | dmvrint(0); dmvxint(0); | |
201 | #endif | |
202 | addr->bsel1 = DMV_MCLR; | |
203 | for (i = 100000; i && (addr->bsel1 & DMV_RUN) == 0; i--) | |
204 | ; | |
205 | if ((addr->bsel1 & DMV_RUN) == 0) { | |
206 | printf("dmvprobe: can't start device\n" ); | |
207 | return (0); | |
208 | } | |
209 | if ((addr->bsel4 != 033) || (addr->bsel6 != 0305)) | |
210 | { | |
211 | printf("dmvprobe: device init failed, bsel4=%o, bsel6=%o\n", | |
212 | addr->bsel4, addr->bsel6); | |
213 | return (0); | |
214 | } | |
215 | addr->bsel0 = DMV_RQI|DMV_IEI|DMV_IEO; | |
216 | DELAY(1000000); | |
217 | addr->bsel1 = DMV_MCLR; | |
218 | for (i = 100000; i && (addr->bsel1 & DMV_RUN) == 0; i--) | |
219 | ; | |
220 | return (1); | |
221 | } | |
222 | ||
223 | /* | |
224 | * Interface exists: make available by filling in network interface | |
225 | * record. System will initialize the interface when it is ready | |
226 | * to accept packets. | |
227 | */ | |
228 | dmvattach(ui) | |
229 | register struct uba_device *ui; | |
230 | { | |
231 | register struct dmv_softc *sc = &dmv_softc[ui->ui_unit]; | |
232 | ||
233 | sc->sc_if.if_unit = ui->ui_unit; | |
234 | sc->sc_if.if_name = "dmv"; | |
235 | sc->sc_if.if_mtu = DMVMTU; | |
236 | sc->sc_if.if_init = dmvinit; | |
237 | sc->sc_if.if_output = dmvoutput; | |
238 | sc->sc_if.if_ioctl = dmvioctl; | |
239 | sc->sc_if.if_reset = dmvreset; | |
240 | sc->sc_if.if_flags = IFF_POINTOPOINT; | |
241 | sc->sc_ifuba.iff_flags = UBA_CANTWAIT; | |
242 | ||
243 | if (dmvtimer == 0) { | |
244 | dmvtimer = 1; | |
245 | timeout(dmvwatch, (caddr_t) 0, hz); | |
246 | } | |
247 | if_attach(&sc->sc_if); | |
248 | } | |
249 | ||
250 | /* | |
251 | * Reset of interface after UNIBUS reset. | |
252 | * If interface is on specified UBA, reset its state. | |
253 | */ | |
254 | dmvreset(unit, uban) | |
255 | int unit, uban; | |
256 | { | |
257 | register struct uba_device *ui; | |
258 | register struct dmv_softc *sc = &dmv_softc[unit]; | |
259 | ||
260 | if (unit >= NDMV || (ui = dmvinfo[unit]) == 0 || ui->ui_alive == 0 || | |
261 | ui->ui_ubanum != uban) | |
262 | return; | |
263 | printf(" dmv%d", unit); | |
264 | sc->sc_flag = 0; | |
265 | sc->sc_if.if_flags &= ~IFF_RUNNING; | |
266 | dmvinit(unit); | |
267 | } | |
268 | ||
269 | /* | |
270 | * Initialization of interface; reinitialize UNIBUS usage. | |
271 | */ | |
272 | dmvinit(unit) | |
273 | int unit; | |
274 | { | |
275 | register struct dmv_softc *sc = &dmv_softc[unit]; | |
276 | register struct uba_device *ui = dmvinfo[unit]; | |
277 | register struct dmvdevice *addr; | |
278 | register struct ifnet *ifp = &sc->sc_if; | |
279 | register struct ifrw *ifrw; | |
280 | register struct ifxmt *ifxp; | |
281 | register struct dmvbufs *rp; | |
282 | register struct dmv_command *qp; | |
283 | struct ifaddr *ifa; | |
284 | int base; | |
285 | int s; | |
286 | ||
287 | addr = (struct dmvdevice *)ui->ui_addr; | |
288 | ||
289 | /* | |
290 | * Check to see that an address has been set | |
291 | * (both local and destination for an address family). | |
292 | */ | |
293 | for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) | |
294 | if (ifa->ifa_addr.sa_family && ifa->ifa_dstaddr.sa_family) | |
295 | break; | |
296 | if (ifa == (struct ifaddr *) 0) | |
297 | return; | |
298 | ||
299 | if ((addr->bsel1&DMV_RUN) == 0) { | |
300 | log(LOG_CRIT, "dmvinit: dmv%d not running\n", unit); | |
301 | ifp->if_flags &= ~IFF_UP; | |
302 | return; | |
303 | } | |
304 | printd("dmvinit\n"); | |
305 | /* initialize UNIBUS resources */ | |
306 | sc->sc_iused = sc->sc_oused = 0; | |
307 | if ((ifp->if_flags & IFF_RUNNING) == 0) { | |
308 | if (if_ubaminit( | |
309 | &sc->sc_ifuba, | |
310 | ui->ui_ubanum, | |
311 | sizeof(struct dmv_header), | |
312 | (int)btoc(DMVMTU), | |
313 | sc->sc_ifr, | |
314 | NRCV, | |
315 | sc->sc_ifw, | |
316 | NXMT | |
317 | ) == 0) { | |
318 | log(LOG_CRIT, "dmvinit: dmv%d can't allocate uba resources\n", unit); | |
319 | ifp->if_flags &= ~IFF_UP; | |
320 | return; | |
321 | } | |
322 | ifp->if_flags |= IFF_RUNNING; | |
323 | } | |
324 | ||
325 | /* initialize buffer pool */ | |
326 | /* receives */ | |
327 | ifrw = &sc->sc_ifr[0]; | |
328 | for (rp = &sc->sc_rbufs[0]; rp < &sc->sc_rbufs[NRCV]; rp++) { | |
329 | rp->ubinfo = ifrw->ifrw_info & 0x3ffff; | |
330 | rp->cc = DMVMTU + sizeof (struct dmv_header); | |
331 | rp->flags = DBUF_OURS|DBUF_RCV; | |
332 | ifrw++; | |
333 | } | |
334 | /* transmits */ | |
335 | ifxp = &sc->sc_ifw[0]; | |
336 | for (rp = &sc->sc_xbufs[0]; rp < &sc->sc_xbufs[NXMT]; rp++) { | |
337 | rp->ubinfo = ifxp->ifw_info & 0x3ffff; | |
338 | rp->cc = 0; | |
339 | rp->flags = DBUF_OURS|DBUF_XMIT; | |
340 | ifxp++; | |
341 | } | |
342 | ||
343 | /* set up command queues */ | |
344 | sc->sc_qfreeh = sc->sc_qfreet | |
345 | = sc->sc_qhead = sc->sc_qtail = sc->sc_qactive = | |
346 | (struct dmv_command *)0; | |
347 | /* set up free command buffer list */ | |
348 | for (qp = &sc->sc_cmdbuf[0]; qp < &sc->sc_cmdbuf[NCMDS]; qp++) { | |
349 | QUEUE_AT_HEAD(qp, sc->sc_qfreeh, sc->sc_qfreet); | |
350 | } | |
351 | if(sc->sc_flag & DMV_RUNNING) | |
352 | dmvload( sc, DMV_CNTRLI, (QP_TRIB|QP_SEL6), 1, 0, DMV_REQHS,0); | |
353 | else | |
354 | dmvload( sc, DMV_CNTRLI, (QP_TRIB|QP_SEL6), 1, 0, DMV_ESTTRIB,0); | |
355 | dmvload( sc, DMV_CNTRLI, (QP_TRIB|QP_SEL6), 1, 0, DMV_REQSUS,0); | |
356 | sc->sc_flag |= (DMV_RESTART|DMV_RUNNING); | |
357 | sc->sc_flag &= ~DMV_ACTIVE; | |
358 | addr->bsel0 |= DMV_IEO; | |
359 | } | |
360 | ||
361 | /* | |
362 | * Start output on interface. Get another datagram | |
363 | * to send from the interface queue and map it to | |
364 | * the interface before starting output. | |
365 | * | |
366 | * Must be called at spl 5 | |
367 | */ | |
368 | dmvstart(dev) | |
369 | dev_t dev; | |
370 | { | |
371 | int unit = minor(dev); | |
372 | register struct dmv_softc *sc = &dmv_softc[unit]; | |
373 | struct mbuf *m; | |
374 | register struct dmvbufs *rp; | |
375 | register int n; | |
376 | ||
377 | /* | |
378 | * Dequeue up to NXMT requests and map them to the UNIBUS. | |
379 | * If no more requests, or no dmv buffers available, just return. | |
380 | */ | |
381 | printd("dmvstart\n"); | |
382 | n = 0; | |
383 | for (rp = &sc->sc_xbufs[0]; rp < &sc->sc_xbufs[NXMT]; rp++ ) { | |
384 | /* find an available buffer */ | |
385 | if ((rp->flags & DBUF_DMVS) == 0) { | |
386 | IF_DEQUEUE(&sc->sc_if.if_snd, m); | |
387 | if (m == 0) | |
388 | return; | |
389 | /* mark it dmvs */ | |
390 | rp->flags |= (DBUF_DMVS); | |
391 | /* | |
392 | * Have request mapped to UNIBUS for transmission | |
393 | * and start the output. | |
394 | */ | |
395 | rp->cc = if_ubaput(&sc->sc_ifuba, &sc->sc_ifw[n], m); | |
396 | sc->sc_oused++; | |
397 | dmvload( | |
398 | sc, | |
399 | DMV_BACCX, | |
400 | QP_TRIB|QP_SEL4|QP_SEL6|QP_SEL10, | |
401 | 1, | |
402 | rp->ubinfo, | |
403 | (rp->ubinfo>>16)&0x3f, | |
404 | rp->cc | |
405 | ); | |
406 | } | |
407 | n++; | |
408 | } | |
409 | } | |
410 | ||
411 | /* | |
412 | * Utility routine to load the DMV device registers. | |
413 | */ | |
414 | dmvload(sc, cmd, mask, tributary, sel4, sel6, sel10) | |
415 | register struct dmv_softc *sc; | |
416 | u_char cmd, tributary, mask; | |
417 | u_short sel4, sel6, sel10; | |
418 | { | |
419 | register struct dmvdevice *addr; | |
420 | register int unit, sps; | |
421 | register struct dmv_command *qp; | |
422 | ||
423 | unit = sc - dmv_softc; | |
424 | printd("dmvload: cmd=%x mask=%x trib=%x sel4=%x sel6=%x sel10=%x\n", | |
425 | (unsigned) cmd, | |
426 | (unsigned) mask, | |
427 | (unsigned) tributary, | |
428 | (unsigned) sel4, | |
429 | (unsigned) sel6, | |
430 | (unsigned) sel10 | |
431 | ); | |
432 | addr = (struct dmvdevice *)dmvinfo[unit]->ui_addr; | |
433 | sps = spl5(); | |
434 | ||
435 | /* grab a command buffer from the free list */ | |
436 | if ((qp = sc->sc_qfreeh) == (struct dmv_command *)0) | |
437 | panic("dmv command queue overflow"); | |
438 | DEQUEUE(sc->sc_qfreeh, sc->sc_qfreet); | |
439 | ||
440 | /* fill in requested info */ | |
441 | qp->qp_cmd = cmd; | |
442 | qp->qp_mask = mask; | |
443 | qp->qp_tributary = tributary; | |
444 | qp->qp_sel4 = sel4; | |
445 | qp->qp_sel6 = sel6; | |
446 | qp->qp_sel10 = sel10; | |
447 | ||
448 | if (sc->sc_qactive) { /* command in progress */ | |
449 | if (cmd == DMV_BACCR) { /* supply read buffers first */ | |
450 | QUEUE_AT_HEAD(qp, sc->sc_qhead, sc->sc_qtail); | |
451 | } else { | |
452 | QUEUE_AT_TAIL(qp, sc->sc_qhead, sc->sc_qtail); | |
453 | } | |
454 | } else { /* command port free */ | |
455 | sc->sc_qactive = qp; | |
456 | addr->bsel0 = (DMV_RQI|DMV_IEI|DMV_IEO); | |
457 | } | |
458 | splx(sps); | |
459 | } | |
460 | /* | |
461 | * DMV interface input interrupt. | |
462 | * Ready to accept another command, | |
463 | * pull one off the command queue. | |
464 | */ | |
465 | dmvrint(unit) | |
466 | int unit; | |
467 | { | |
468 | register struct dmv_softc *sc; | |
469 | register struct dmvdevice *addr; | |
470 | register struct dmv_command *qp; | |
471 | register int n; | |
472 | ||
473 | addr = (struct dmvdevice *)dmvinfo[unit]->ui_addr; | |
474 | sc = &dmv_softc[unit]; | |
475 | printd("dmvrint\n"); | |
476 | if ((qp = sc->sc_qactive) == (struct dmv_command *) 0) { | |
477 | log(LOG_WARNING, "dmvrint: dmv%d no command\n", unit); | |
478 | return; | |
479 | } | |
480 | while (addr->bsel2&DMV_RDI) { | |
481 | if(qp->qp_mask&QP_SEL4) | |
482 | addr->wsel4 = qp->qp_sel4; | |
483 | if(qp->qp_mask&QP_SEL6) | |
484 | addr->wsel6 = qp->qp_sel6; | |
485 | if(qp->qp_mask&QP_SEL10) { | |
486 | addr->wsel10 = qp->qp_sel10; | |
487 | qp->qp_cmd |= DMV_22BIT; | |
488 | } | |
489 | if(qp->qp_mask&QP_TRIB) | |
490 | addr->wsel2 = qp->qp_cmd|(qp->qp_tributary << 8); | |
491 | else | |
492 | addr->bsel2 = qp->qp_cmd; | |
493 | QUEUE_AT_HEAD(qp, sc->sc_qfreeh, sc->sc_qfreet); | |
494 | if ((sc->sc_qactive = sc->sc_qhead) == (struct dmv_command *)0) | |
495 | break; | |
496 | qp = sc->sc_qactive; | |
497 | DEQUEUE(sc->sc_qhead, sc->sc_qtail); | |
498 | if (addr->bsel2&DMV_RDO) | |
499 | break; | |
500 | } | |
501 | if (!sc->sc_qactive) { | |
502 | if(addr->bsel2&DMV_RDI) { | |
503 | /* clear RQI prior to last command per DMV manual */ | |
504 | addr->bsel0 &= ~DMV_RQI; | |
505 | addr->wsel6 = DMV_NOP; | |
506 | addr->bsel2 = DMV_CNTRLI; | |
507 | } | |
508 | addr->bsel0 = DMV_IEO; | |
509 | } | |
510 | else /* RDO set or DMV still holding CSR */ | |
511 | addr->bsel0 = (DMV_RQI|DMV_IEI|DMV_IEO); | |
512 | ||
513 | } | |
514 | ||
515 | /* | |
516 | * DMV interface output interrupt. | |
517 | * A transfer may have completed, check for errors. | |
518 | * If it was a read, notify appropriate protocol. | |
519 | * If it was a write, pull the next one off the queue. | |
520 | */ | |
521 | dmvxint(unit) | |
522 | int unit; | |
523 | { | |
524 | register struct dmv_softc *sc; | |
525 | register struct ifnet *ifp; | |
526 | struct uba_device *ui = dmvinfo[unit]; | |
527 | struct dmvdevice *addr; | |
528 | struct mbuf *m; | |
529 | struct ifqueue *inq; | |
530 | int sel2, sel3, sel4, sel6, sel10, pkaddr, len, s; | |
531 | register struct ifrw *ifrw; | |
532 | register struct dmvbufs *rp; | |
533 | register struct ifxmt *ifxp; | |
534 | struct dmv_header *dh; | |
535 | int off, resid, fatal; | |
536 | ||
537 | addr = (struct dmvdevice *)ui->ui_addr; | |
538 | sc = &dmv_softc[unit]; | |
539 | ifp = &sc->sc_if; | |
540 | ||
541 | while (addr->bsel2 & DMV_RDO) { | |
542 | ||
543 | sel2 = addr->bsel2; | |
544 | sel3 = addr->bsel3; | |
545 | sel4 = addr->wsel4; /* release port */ | |
546 | sel6 = addr->wsel6; | |
547 | if(sel2 & DMV_22BIT) | |
548 | sel10 = addr->wsel10; | |
549 | addr->bsel2 &= ~DMV_RDO; | |
550 | pkaddr = sel4 | ((sel6 & 0x3f) << 16); | |
551 | printd("dmvxint: sel2=%x sel4=%x sel6=%x sel10=%x pkaddr=%x\n", | |
552 | (unsigned) sel2, | |
553 | (unsigned) sel4, | |
554 | (unsigned) sel6, | |
555 | (unsigned) sel10, | |
556 | (unsigned) pkaddr | |
557 | ); | |
558 | if((sc->sc_flag & DMV_RUNNING)==0) { | |
559 | log(LOG_WARNING, "dmvxint: dmv%d xint while down\n", unit); | |
560 | return; | |
561 | } | |
562 | switch (sel2 & 07) { | |
563 | case DMV_BDRUS: | |
564 | /* | |
565 | * A read has completed. | |
566 | * Pass packet to type specific | |
567 | * higher-level input routine. | |
568 | */ | |
569 | ifp->if_ipackets++; | |
570 | /* find location in dmvuba struct */ | |
571 | ifrw= &sc->sc_ifr[0]; | |
572 | for (rp = &sc->sc_rbufs[0]; rp < &sc->sc_rbufs[NRCV]; rp++) { | |
573 | if(rp->ubinfo == pkaddr) | |
574 | break; | |
575 | ifrw++; | |
576 | } | |
577 | if (rp >= &sc->sc_rbufs[NRCV]) | |
578 | panic("dmv rcv"); | |
579 | if ((rp->flags & DBUF_DMVS) == 0) | |
580 | log(LOG_WARNING, "dmvxint: dmv%d done unalloc rbuf\n", unit); | |
581 | ||
582 | len = (sel10&0x3fff) - sizeof (struct dmv_header); | |
583 | if (len < 0 || len > DMVMTU) { | |
584 | ifp->if_ierrors++; | |
585 | log(LOG_ERR, "dmvxint: dmv%d bad rcv pkt addr 0x%x len 0x%x\n", | |
586 | unit, pkaddr, len); | |
587 | goto setup; | |
588 | } | |
589 | /* | |
590 | * Deal with trailer protocol: if type is trailer | |
591 | * get true type from first 16-bit word past data. | |
592 | * Remember that type was trailer by setting off. | |
593 | */ | |
594 | dh = (struct dmv_header *)ifrw->ifrw_addr; | |
595 | dh->dmv_type = ntohs((u_short)dh->dmv_type); | |
596 | #define dmvdataaddr(dh, off, type) ((type)(((caddr_t)((dh)+1)+(off)))) | |
597 | if (dh->dmv_type >= DMV_TRAILER && | |
598 | dh->dmv_type < DMV_TRAILER+DMV_NTRAILER) { | |
599 | off = (dh->dmv_type - DMV_TRAILER) * 512; | |
600 | if (off >= DMVMTU) | |
601 | goto setup; /* sanity */ | |
602 | dh->dmv_type = ntohs(*dmvdataaddr(dh, off, u_short *)); | |
603 | resid = ntohs(*(dmvdataaddr(dh, off+2, u_short *))); | |
604 | if (off + resid > len) | |
605 | goto setup; /* sanity */ | |
606 | len = off + resid; | |
607 | } else | |
608 | off = 0; | |
609 | if (len == 0) | |
610 | goto setup; | |
611 | ||
612 | /* | |
613 | * Pull packet off interface. Off is nonzero if | |
614 | * packet has trailing header; dmv_get will then | |
615 | * force this header information to be at the front, | |
616 | * but we still have to drop the type and length | |
617 | * which are at the front of any trailer data. | |
618 | */ | |
619 | m = if_ubaget(&sc->sc_ifuba, ifrw, len, off, ifp); | |
620 | if (m == 0) | |
621 | goto setup; | |
622 | if (off) { | |
623 | ifp = *(mtod(m, struct ifnet **)); | |
624 | m->m_off += 2 * sizeof (u_short); | |
625 | m->m_len -= 2 * sizeof (u_short); | |
626 | *(mtod(m, struct ifnet **)) = ifp; | |
627 | } | |
628 | switch (dh->dmv_type) { | |
629 | #ifdef INET | |
630 | case DMV_IPTYPE: | |
631 | schednetisr(NETISR_IP); | |
632 | inq = &ipintrq; | |
633 | break; | |
634 | #endif | |
635 | default: | |
636 | m_freem(m); | |
637 | goto setup; | |
638 | } | |
639 | ||
640 | s = splimp(); | |
641 | if (IF_QFULL(inq)) { | |
642 | IF_DROP(inq); | |
643 | m_freem(m); | |
644 | } else | |
645 | IF_ENQUEUE(inq, m); | |
646 | splx(s); | |
647 | setup: | |
648 | /* is this needed? */ | |
649 | rp->ubinfo = ifrw->ifrw_info & 0x3ffff; | |
650 | dmvload( | |
651 | sc, | |
652 | DMV_BACCR, | |
653 | QP_SEL4|QP_SEL6|QP_SEL10, | |
654 | 0, | |
655 | rp->ubinfo, | |
656 | (rp->ubinfo>>16)&0x3f, | |
657 | rp->cc | |
658 | ); | |
659 | break; | |
660 | case DMV_BDXSA: | |
661 | /* | |
662 | * A write has completed, start another | |
663 | * transfer if there is more data to send. | |
664 | */ | |
665 | ifp->if_opackets++; | |
666 | /* find associated dmvbuf structure */ | |
667 | ifxp = &sc->sc_ifw[0]; | |
668 | for (rp = &sc->sc_xbufs[0]; rp < &sc->sc_xbufs[NXMT]; rp++) { | |
669 | if(rp->ubinfo == pkaddr) | |
670 | break; | |
671 | ifxp++; | |
672 | } | |
673 | if (rp >= &sc->sc_xbufs[NXMT]) { | |
674 | log(LOG_ERR, "dmv%d: bad packet address 0x%x\n", | |
675 | unit, pkaddr); | |
676 | break; | |
677 | } | |
678 | if ((rp->flags & DBUF_DMVS) == 0) | |
679 | log(LOG_ERR, "dmvxint: dmv%d unallocated packet 0x%x\n", | |
680 | unit, pkaddr); | |
681 | /* mark buffer free */ | |
682 | if (ifxp->ifw_xtofree) { | |
683 | (void)m_freem(ifxp->ifw_xtofree); | |
684 | ifxp->ifw_xtofree = 0; | |
685 | } | |
686 | rp->flags &= ~DBUF_DMVS; | |
687 | sc->sc_oused--; | |
688 | sc->sc_nticks = 0; | |
689 | sc->sc_flag |= DMV_ACTIVE; | |
690 | break; | |
691 | ||
692 | case DMV_CNTRLO: | |
693 | /* ACCUMULATE STATISTICS */ | |
694 | fatal=0; | |
695 | switch(sel6&DMV_EEC) { | |
696 | case DMV_ORUN: | |
697 | if(sc->sc_flag & DMV_RESTART) { | |
698 | load_rec_bufs(sc); | |
699 | sc->sc_flag &= ~DMV_RESTART; | |
700 | log(LOG_INFO, | |
701 | "dmvxint: dmv%d far end on-line\n", | |
702 | unit | |
703 | ); | |
704 | } else { | |
705 | log(LOG_WARNING, | |
706 | "dmvxint: dmv%d far end restart\n", | |
707 | unit | |
708 | ); | |
709 | goto fatal; | |
710 | } | |
711 | break; | |
712 | case DMV_RTE: | |
713 | ifp->if_ierrors++; | |
ecfa166d | 714 | if ((sc->sc_rte++ % DMV_RPRTE) == 0) |
d9bcdfcc MK |
715 | log(LOG_WARNING, |
716 | "dmvxint: dmv%d receive threshold error\n", | |
717 | unit); | |
ecfa166d MK |
718 | break; |
719 | case DMV_TTE: | |
720 | ifp->if_oerrors++; | |
ecfa166d | 721 | if ((sc->sc_xte++ % DMV_RPTTE) == 0) |
d9bcdfcc MK |
722 | log(LOG_WARNING, |
723 | "dmvxint: dmv%d transmit threshold error\n", | |
724 | unit); | |
ecfa166d MK |
725 | break; |
726 | case DMV_STE: | |
ecfa166d | 727 | if ((sc->sc_ste++ % DMV_RPSTE) == 0) |
d9bcdfcc MK |
728 | log(LOG_WARNING, |
729 | "dmvxint: dmv%d select threshold error\n", | |
730 | unit); | |
ecfa166d MK |
731 | break; |
732 | case DMV_NXM: | |
d9bcdfcc MK |
733 | if ((sc->sc_nxm++ % DMV_RPNXM) == 0) |
734 | log(LOG_WARNING, | |
ecfa166d | 735 | "dmvxint: dmv%d nonexistent memory error\n", |
d9bcdfcc | 736 | unit); |
ecfa166d MK |
737 | break; |
738 | case DMV_MODD: | |
ecfa166d | 739 | if ((sc->sc_modd++ % DMV_RPMODD) == 0) |
d9bcdfcc MK |
740 | log(LOG_WARNING, |
741 | "dmvxint: dmv%d modem disconnected error\n", | |
742 | unit); | |
ecfa166d MK |
743 | break; |
744 | case DMV_CXRL: | |
ecfa166d | 745 | if ((sc->sc_cxrl++ % DMV_RPCXRL) == 0) |
d9bcdfcc MK |
746 | log(LOG_WARNING, |
747 | "dmvxint: dmv%d carrier loss error\n", | |
748 | unit); | |
ecfa166d MK |
749 | break; |
750 | case DMV_QOVF: | |
751 | log(LOG_WARNING, | |
752 | "dmvxint: dmv%d response queue overflow\n", | |
753 | unit | |
754 | ); | |
755 | sc->sc_qovf++; | |
756 | goto fatal; | |
757 | ||
758 | default: | |
759 | log(LOG_WARNING, | |
760 | "dmvxint: dmv%d unknown error %o\n", | |
761 | unit, | |
762 | sel6&DMV_EEC | |
763 | ); | |
764 | if ((sc->sc_unknown++ % DMV_RPUNKNOWN) == 0) | |
765 | goto fatal; | |
766 | break; | |
767 | } | |
768 | break; | |
769 | ||
770 | case DMV_BDRUNUS: | |
771 | case DMV_BDXSN: | |
772 | case DMV_BDXNS: | |
773 | log(LOG_INFO, | |
774 | "dmvxint: dmv%d buffer disp for halted trib %o\n", | |
775 | unit, sel2&0x7 | |
776 | ); | |
777 | break; | |
778 | ||
779 | case DMV_MDEFO: | |
780 | if((sel6&0x1f) == 020) { | |
781 | log(LOG_INFO, | |
782 | "dmvxint: dmv%d buffer return complete sel3=%x\n", | |
783 | unit, sel3); | |
784 | } else { | |
785 | log(LOG_INFO, | |
786 | "dmvxint: dmv%d info resp sel3=%x sel4=%x sel6=%x\n", | |
787 | unit, sel3, sel4, sel6 | |
788 | ); | |
789 | } | |
790 | break; | |
791 | ||
792 | default: | |
793 | log(LOG_WARNING, | |
794 | "dmvxint: dmv%d bad control %o\n", | |
795 | unit, sel2&0x7 | |
796 | ); | |
797 | break; | |
798 | } | |
799 | } | |
800 | dmvstart(unit); | |
801 | return; | |
802 | fatal: | |
803 | log( | |
804 | LOG_ERR, | |
805 | "dmv%d: fatal error, output code ==%o\n", | |
806 | unit, | |
807 | sel6&DMV_EEC | |
808 | ); | |
809 | dmvrestart(unit); | |
810 | } | |
811 | load_rec_bufs(sc) | |
812 | register struct dmv_softc *sc; | |
813 | { | |
814 | register struct dmvbufs *rp; | |
815 | ||
816 | /* queue first NRCV buffers for DMV to fill */ | |
817 | for (rp = &sc->sc_rbufs[0]; rp < &sc->sc_rbufs[NRCV]; rp++) { | |
818 | rp->flags |= DBUF_DMVS; | |
819 | dmvload( | |
820 | sc, | |
821 | DMV_BACCR, | |
822 | QP_TRIB|QP_SEL4|QP_SEL6|QP_SEL10, | |
823 | 1, | |
824 | rp->ubinfo, | |
825 | (rp->ubinfo>>16)&0x3f, | |
826 | rp->cc | |
827 | ); | |
828 | sc->sc_iused++; | |
829 | } | |
830 | } | |
831 | ||
832 | /* | |
833 | * DMV output routine. | |
834 | * Encapsulate a packet of type family for the dmv. | |
835 | * Use trailer local net encapsulation if enough data in first | |
836 | * packet leaves a multiple of 512 bytes of data in remainder. | |
837 | */ | |
838 | dmvoutput(ifp, m0, dst) | |
839 | register struct ifnet *ifp; | |
840 | register struct mbuf *m0; | |
841 | struct sockaddr *dst; | |
842 | { | |
843 | int type, error, s; | |
844 | register struct mbuf *m = m0; | |
845 | register struct dmv_header *dh; | |
846 | register int off; | |
847 | ||
848 | switch (dst->sa_family) { | |
849 | #ifdef INET | |
850 | case AF_INET: | |
851 | off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; | |
852 | if ((ifp->if_flags & IFF_NOTRAILERS) == 0) | |
853 | if (off > 0 && (off & 0x1ff) == 0 && | |
854 | m->m_off >= MMINOFF + 2 * sizeof (u_short)) { | |
855 | type = DMV_TRAILER + (off>>9); | |
856 | m->m_off -= 2 * sizeof (u_short); | |
857 | m->m_len += 2 * sizeof (u_short); | |
858 | *mtod(m, u_short *) = htons((u_short)DMV_IPTYPE); | |
859 | *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len); | |
860 | goto gottrailertype; | |
861 | } | |
862 | type = DMV_IPTYPE; | |
863 | off = 0; | |
864 | goto gottype; | |
865 | #endif | |
866 | ||
867 | case AF_UNSPEC: | |
868 | dh = (struct dmv_header *)dst->sa_data; | |
869 | type = dh->dmv_type; | |
870 | goto gottype; | |
871 | ||
872 | default: | |
873 | log(LOG_ERR, "dmvoutput, dmv%d can't handle af%d\n", ifp->if_unit, | |
874 | dst->sa_family); | |
875 | error = EAFNOSUPPORT; | |
876 | goto bad; | |
877 | } | |
878 | ||
879 | gottrailertype: | |
880 | /* | |
881 | * Packet to be sent as a trailer; move first packet | |
882 | * (control information) to end of chain. | |
883 | */ | |
884 | while (m->m_next) | |
885 | m = m->m_next; | |
886 | m->m_next = m0; | |
887 | m = m0->m_next; | |
888 | m0->m_next = 0; | |
889 | m0 = m; | |
890 | ||
891 | gottype: | |
892 | /* | |
893 | * Add local network header | |
894 | * (there is space for a uba on a vax to step on) | |
895 | */ | |
896 | if (m->m_off > MMAXOFF || | |
897 | MMINOFF + sizeof(struct dmv_header) > m->m_off) { | |
898 | m = m_get(M_DONTWAIT, MT_HEADER); | |
899 | if (m == 0) { | |
900 | error = ENOBUFS; | |
901 | goto bad; | |
902 | } | |
903 | m->m_next = m0; | |
904 | m->m_off = MMINOFF; | |
905 | m->m_len = sizeof (struct dmv_header); | |
906 | } else { | |
907 | m->m_off -= sizeof (struct dmv_header); | |
908 | m->m_len += sizeof (struct dmv_header); | |
909 | } | |
910 | dh = mtod(m, struct dmv_header *); | |
911 | dh->dmv_type = htons((u_short)type); | |
912 | ||
913 | /* | |
914 | * Queue message on interface, and start output if interface | |
915 | * not yet active. | |
916 | */ | |
917 | s = splimp(); | |
918 | if (IF_QFULL(&ifp->if_snd)) { | |
919 | IF_DROP(&ifp->if_snd); | |
920 | m_freem(m); | |
921 | splx(s); | |
922 | return (ENOBUFS); | |
923 | } | |
924 | IF_ENQUEUE(&ifp->if_snd, m); | |
925 | dmvstart(ifp->if_unit); | |
926 | splx(s); | |
927 | return (0); | |
928 | ||
929 | bad: | |
930 | m_freem(m0); | |
931 | return (error); | |
932 | } | |
933 | ||
934 | ||
935 | /* | |
936 | * Process an ioctl request. | |
937 | */ | |
938 | /* ARGSUSED */ | |
939 | dmvioctl(ifp, cmd, data) | |
940 | register struct ifnet *ifp; | |
941 | int cmd; | |
942 | caddr_t data; | |
943 | { | |
944 | int s = splimp(), error = 0; | |
945 | struct mbuf *m; | |
946 | register struct dmv_softc *sc = &dmv_softc[ifp->if_unit]; | |
947 | ||
948 | switch (cmd) { | |
949 | ||
950 | case SIOCSIFADDR: | |
951 | ifp->if_flags |= IFF_UP; | |
952 | if ((ifp->if_flags & IFF_RUNNING) == 0) | |
953 | dmvinit(ifp->if_unit); | |
954 | break; | |
955 | ||
956 | case SIOCSIFDSTADDR: | |
957 | if ((ifp->if_flags & IFF_RUNNING) == 0) | |
958 | dmvinit(ifp->if_unit); | |
959 | break; | |
960 | ||
961 | case SIOCSIFFLAGS: | |
962 | if ((ifp->if_flags & IFF_UP) == 0 && | |
963 | sc->sc_flag & DMV_RUNNING) { | |
964 | ((struct dmvdevice *) | |
965 | (dmvinfo[ifp->if_unit]->ui_addr))->bsel1 = DMV_MCLR; | |
966 | for(;;) { | |
967 | IF_DEQUEUE(&sc->sc_if.if_snd, m); | |
968 | if (m != NULL) | |
969 | m_freem(m); | |
970 | else | |
971 | break; | |
972 | } | |
973 | sc->sc_flag &= ~DMV_RUNNING; | |
974 | } else if (ifp->if_flags & IFF_UP && | |
975 | (sc->sc_flag & DMV_RUNNING) == 0) | |
976 | dmvrestart(ifp->if_unit); | |
977 | break; | |
978 | ||
979 | default: | |
980 | error = EINVAL; | |
981 | } | |
982 | splx(s); | |
983 | return (error); | |
984 | } | |
985 | ||
986 | /* | |
987 | * Restart after a fatal error. | |
988 | * Clear device and reinitialize. | |
989 | */ | |
990 | dmvrestart(unit) | |
991 | int unit; | |
992 | { | |
993 | register struct dmv_softc *sc = &dmv_softc[unit]; | |
994 | register struct uba_device *ui = dmvinfo[unit]; | |
995 | register struct dmvdevice *addr; | |
996 | register struct ifxmt *ifxp; | |
997 | register int i; | |
998 | ||
999 | #ifdef notdef | |
1000 | addr = (struct dmvdevice *)ui->ui_addr; | |
1001 | /* | |
1002 | * Let the DMR finish the MCLR. At 1 Mbit, it should do so | |
1003 | * in about a max of 6.4 milliseconds with diagnostics enabled. | |
1004 | */ | |
1005 | addr->bsel1 = DMV_MCLR; | |
1006 | for (i = 100000; i && (addr->bsel1 & DMV_RUN) == 0; i--) | |
1007 | ; | |
1008 | if ((addr->bsel1 & DMV_RUN) == 0) { | |
1009 | log(LOG_ERR, "dmvrestart: can't start device\n" ); | |
1010 | return (0); | |
1011 | } | |
1012 | if ((addr->bsel4 != 033) || (addr->bsel6 != 0305)) | |
1013 | { | |
1014 | log(LOG_ERR, "dmvrestart: device init failed, bsel4=%o, bsel6=%o\n", | |
1015 | addr->bsel4, addr->bsel6); | |
1016 | return (0); | |
1017 | } | |
1018 | #endif | |
1019 | for (ifxp = sc->sc_ifw; ifxp < &sc->sc_ifw[NXMT]; ifxp++) { | |
1020 | if (ifxp->ifw_xtofree) { | |
1021 | (void) m_freem(ifxp->ifw_xtofree); | |
1022 | ifxp->ifw_xtofree = 0; | |
1023 | } | |
1024 | } | |
1025 | /* restart DMV */ | |
1026 | dmvinit(unit); | |
1027 | sc->sc_if.if_collisions++; /* why not? */ | |
1028 | } | |
1029 | ||
1030 | /* | |
1031 | * Check to see that transmitted packets don't | |
1032 | * lose interrupts. The device has to be active. | |
1033 | */ | |
1034 | dmvwatch() | |
1035 | { | |
1036 | register struct uba_device *ui; | |
1037 | register struct dmv_softc *sc; | |
1038 | struct dmvdevice *addr; | |
1039 | register int i; | |
1040 | ||
1041 | for (i = 0; i < NDMV; i++) { | |
1042 | sc = &dmv_softc[i]; | |
1043 | if ((sc->sc_flag & DMV_ACTIVE) == 0) | |
1044 | continue; | |
1045 | if ((ui = dmvinfo[i]) == 0 || ui->ui_alive == 0) | |
1046 | continue; | |
1047 | if (sc->sc_oused) { | |
1048 | sc->sc_nticks++; | |
1049 | if (sc->sc_nticks > dmv_timeout) { | |
1050 | sc->sc_nticks = 0; | |
1051 | addr = (struct dmvdevice *)ui->ui_addr; | |
1052 | log(LOG_ERR, "dmv%d hung: bsel0=%b bsel2=%b\n", | |
1053 | i, addr->bsel0 & 0xff, DMV0BITS, | |
1054 | addr->bsel2 & 0xff, DMV2BITS); | |
1055 | dmvrestart(i); | |
1056 | } | |
1057 | } | |
1058 | } | |
1059 | timeout(dmvwatch, (caddr_t) 0, hz); | |
1060 | } | |
1061 | #endif |