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