Commit | Line | Data |
---|---|---|
1cd789fb | 1 | /* if_vv.c 4.21 83/06/12 */ |
547e5718 | 2 | |
961945a8 | 3 | #include "vv.h" |
4cdd59b5 | 4 | |
547e5718 SL |
5 | /* |
6 | * Proteon 10 Meg Ring Driver. | |
7 | * This device is called "vv" because its "real name", | |
8 | * V2LNI won't work if shortened to the obvious "v2". | |
9 | * Hence the subterfuge. | |
4cdd59b5 | 10 | * |
547e5718 | 11 | */ |
38061563 | 12 | #include "../machine/pte.h" |
961945a8 | 13 | |
547e5718 SL |
14 | #include "../h/param.h" |
15 | #include "../h/systm.h" | |
16 | #include "../h/mbuf.h" | |
547e5718 SL |
17 | #include "../h/buf.h" |
18 | #include "../h/protosw.h" | |
19 | #include "../h/socket.h" | |
547e5718 | 20 | #include "../h/vmmac.h" |
4cdd59b5 | 21 | #include "../h/errno.h" |
38061563 SL |
22 | #include "../h/time.h" |
23 | #include "../h/kernel.h" | |
1cd789fb | 24 | #include "../h/ioctl.h" |
eaa60542 BJ |
25 | |
26 | #include "../net/if.h" | |
38061563 | 27 | #include "../net/netisr.h" |
eaa60542 | 28 | #include "../net/route.h" |
4cdd59b5 | 29 | |
d2cc167c BJ |
30 | #include "../netinet/in.h" |
31 | #include "../netinet/in_systm.h" | |
32 | #include "../netinet/ip.h" | |
33 | #include "../netinet/ip_var.h" | |
eaa60542 | 34 | |
38061563 SL |
35 | #include "../vax/mtpr.h" |
36 | #include "../vax/cpu.h" | |
4cdd59b5 | 37 | |
eaa60542 BJ |
38 | #include "../vaxuba/ubareg.h" |
39 | #include "../vaxuba/ubavar.h" | |
547e5718 | 40 | |
38061563 SL |
41 | #include "../vaxif/if_vv.h" |
42 | #include "../vaxif/if_uba.h" | |
43 | ||
547e5718 SL |
44 | /* |
45 | * N.B. - if WIRECENTER is defined wrong, it can well break | |
46 | * the hardware!! | |
47 | */ | |
547e5718 SL |
48 | #define WIRECENTER |
49 | ||
50 | #ifdef WIRECENTER | |
51 | #define VV_CONF VV_HEN /* drive wire center relay */ | |
52 | #else | |
53 | #define VV_CONF VV_STE /* allow operation without wire center */ | |
54 | #endif | |
55 | ||
56 | #define VVMTU (1024+512) | |
5f3ad58b SL |
57 | #define VVMRU (1024+512+16) /* space for trailer */ |
58 | ||
1cd789fb | 59 | int vv_tracehdr = 0, /* 1 => trace headers (slowly!!) */ |
4cdd59b5 SL |
60 | vv_tracetimeout = 1; /* 1 => trace input error-rate limiting */ |
61 | vv_logreaderrors = 0; /* 1 => log all read errors */ | |
62 | ||
63 | #define vvtracehdr if (vv_tracehdr) vvprt_hdr | |
64 | #define vvtrprintf if (vv_tracetimeout) printf | |
65 | ||
66 | int vv_ticking = 0; /* error flywheel is running */ | |
67 | ||
38061563 SL |
68 | /* |
69 | * Interval in HZ - 50 msec. | |
70 | * N.B. all times below are in units of flywheel ticks | |
71 | */ | |
72 | #define VV_FLYWHEEL 3 | |
4cdd59b5 SL |
73 | #define VV_ERRORTHRESHOLD 100 /* errors/flywheel-interval */ |
74 | #define VV_MODE1ATTEMPTS 10 /* number mode 1 retries */ | |
75 | #define VV_MODE1DELAY 2 /* period interface is PAUSEd - 100ms */ | |
76 | #define VV_MODE2DELAY 4 /* base interval host relay is off - 200ms */ | |
77 | #define VV_MAXDELAY 6400 /* max interval host relay is off - 2 minutes */ | |
547e5718 SL |
78 | |
79 | int vvprobe(), vvattach(), vvrint(), vvxint(); | |
80 | struct uba_device *vvinfo[NVV]; | |
81 | u_short vvstd[] = { 0 }; | |
82 | struct uba_driver vvdriver = | |
83 | { vvprobe, 0, vvattach, 0, vvstd, "vv", vvinfo }; | |
84 | #define VVUNIT(x) minor(x) | |
1cd789fb | 85 | int vvinit(),vvioctl(),vvoutput(),vvreset(); |
547e5718 SL |
86 | |
87 | /* | |
88 | * Software status of each interface. | |
89 | * | |
90 | * Each interface is referenced by a network interface structure, | |
91 | * vs_if, which the routing code uses to locate the interface. | |
92 | * This structure contains the output queue for the interface, its address, ... | |
93 | * We also have, for each interface, a UBA interface structure, which | |
94 | * contains information about the UNIBUS resources held by the interface: | |
95 | * map registers, buffered data paths, etc. Information is cached in this | |
96 | * structure for use by the if_uba.c routines in running the interface | |
97 | * efficiently. | |
98 | */ | |
99 | struct vv_softc { | |
100 | struct ifnet vs_if; /* network-visible interface */ | |
101 | struct ifuba vs_ifuba; /* UNIBUS resources */ | |
4cdd59b5 SL |
102 | short vs_oactive; /* is output active */ |
103 | short vs_iactive; /* is input active */ | |
547e5718 SL |
104 | short vs_olen; /* length of last output */ |
105 | u_short vs_lastx; /* last destination address */ | |
4cdd59b5 | 106 | short vs_tries; /* transmit current retry count */ |
547e5718 | 107 | short vs_init; /* number of ring inits */ |
547e5718 | 108 | short vs_nottaken; /* number of packets refused */ |
4cdd59b5 SL |
109 | /* input error rate limiting state */ |
110 | short vs_major; /* recovery major state */ | |
111 | short vs_minor; /* recovery minor state */ | |
112 | short vs_retry; /* recovery retry count */ | |
113 | short vs_delayclock; /* recovery delay clock */ | |
114 | short vs_delayrange; /* increasing delay interval */ | |
115 | short vs_dropped; /* number of packes tossed in last dt */ | |
547e5718 SL |
116 | } vv_softc[NVV]; |
117 | ||
4cdd59b5 | 118 | /* |
38061563 | 119 | * States of vs_iactive. |
4cdd59b5 | 120 | */ |
4cdd59b5 SL |
121 | #define ACTIVE 1 /* interface should post new receives */ |
122 | #define PAUSE 0 /* interface should NOT post new receives */ | |
123 | #define OPEN -1 /* PAUSE and open host relay */ | |
124 | ||
125 | /* | |
38061563 | 126 | * Recovery major states. |
4cdd59b5 | 127 | */ |
4cdd59b5 SL |
128 | #define MODE0 0 /* everything is wonderful */ |
129 | #define MODE1 1 /* hopefully whatever will go away */ | |
38061563 | 130 | #define MODE2 2 /* drastic measures - open host relay for increasing intervals */ |
4cdd59b5 | 131 | |
547e5718 SL |
132 | vvprobe(reg) |
133 | caddr_t reg; | |
134 | { | |
135 | register int br, cvec; | |
136 | register struct vvreg *addr = (struct vvreg *)reg; | |
137 | ||
138 | #ifdef lint | |
66923854 | 139 | br = 0; cvec = br; br = cvec; vvrint(0); |
547e5718 SL |
140 | #endif |
141 | /* reset interface, enable, and wait till dust settles */ | |
142 | addr->vvicsr = VV_RST; | |
143 | addr->vvocsr = VV_RST; | |
5b319f7c | 144 | DELAY(10000); |
547e5718 SL |
145 | /* generate interrupt by doing 1 word DMA from 0 in uba space!! */ |
146 | addr->vvocsr = VV_IEN; /* enable interrupt */ | |
147 | addr->vvoba = 0; /* low 16 bits */ | |
148 | addr->vvoea = 0; /* extended bits */ | |
149 | addr->vvowc = -1; /* for 1 word */ | |
150 | addr->vvocsr |= VV_DEN; /* start the DMA */ | |
151 | DELAY(100000); | |
152 | addr->vvocsr = 0; | |
153 | if (cvec && cvec != 0x200) | |
154 | cvec -= 4; /* backup so vector => recieve */ | |
155 | return(1); | |
156 | } | |
157 | ||
158 | /* | |
159 | * Interface exists: make available by filling in network interface | |
160 | * record. System will initialize the interface when it is ready | |
161 | * to accept packets. | |
162 | */ | |
163 | vvattach(ui) | |
164 | struct uba_device *ui; | |
165 | { | |
166 | register struct vv_softc *vs = &vv_softc[ui->ui_unit]; | |
547e5718 SL |
167 | |
168 | vs->vs_if.if_unit = ui->ui_unit; | |
169 | vs->vs_if.if_name = "vv"; | |
170 | vs->vs_if.if_mtu = VVMTU; | |
547e5718 | 171 | vs->vs_if.if_init = vvinit; |
1cd789fb | 172 | vs->vs_if.if_ioctl = vvioctl; |
547e5718 | 173 | vs->vs_if.if_output = vvoutput; |
38061563 | 174 | vs->vs_if.if_reset = vvreset; |
5f3ad58b | 175 | vs->vs_ifuba.ifu_flags = UBA_CANTWAIT | UBA_NEEDBDP | UBA_NEED16; |
41fb002a MD |
176 | #if defined(VAX750) |
177 | /* don't chew up 750 bdp's */ | |
178 | if (cpu == VAX_750 && ui->ui_unit > 0) | |
179 | vs->vs_ifuba.ifu_flags &= ~UBA_NEEDBDP; | |
180 | #endif | |
547e5718 | 181 | if_attach(&vs->vs_if); |
547e5718 SL |
182 | } |
183 | ||
184 | /* | |
185 | * Reset of interface after UNIBUS reset. | |
186 | * If interface is on specified uba, reset its state. | |
187 | */ | |
188 | vvreset(unit, uban) | |
189 | int unit, uban; | |
190 | { | |
191 | register struct uba_device *ui; | |
547e5718 SL |
192 | |
193 | if (unit >= NVV || (ui = vvinfo[unit]) == 0 || ui->ui_alive == 0 || | |
194 | ui->ui_ubanum != uban) | |
195 | return; | |
196 | printf(" vv%d", unit); | |
197 | vvinit(unit); | |
198 | } | |
199 | ||
200 | /* | |
201 | * Initialization of interface; clear recorded pending | |
202 | * operations, and reinitialize UNIBUS usage. | |
203 | */ | |
204 | vvinit(unit) | |
205 | int unit; | |
206 | { | |
207 | register struct vv_softc *vs = &vv_softc[unit]; | |
208 | register struct uba_device *ui = vvinfo[unit]; | |
209 | register struct vvreg *addr; | |
210 | struct sockaddr_in *sin; | |
5f3ad58b | 211 | int ubainfo, s; |
4cdd59b5 | 212 | int vvtimeout(); |
547e5718 | 213 | |
1cd789fb SL |
214 | if (vs->vs_if.if_net == 0) |
215 | return; | |
5f3ad58b | 216 | addr = (struct vvreg *)ui->ui_addr; |
547e5718 SL |
217 | if (if_ubainit(&vs->vs_ifuba, ui->ui_ubanum, |
218 | sizeof (struct vv_header), (int)btoc(VVMTU)) == 0) { | |
219 | printf("vv%d: can't initialize\n", unit); | |
5f3ad58b | 220 | vs->vs_if.if_flags &= ~IFF_UP; |
547e5718 SL |
221 | return; |
222 | } | |
38061563 SL |
223 | if (vv_ticking++ == 0) |
224 | timeout(vvtimeout, (caddr_t) 0, VV_FLYWHEEL); | |
5f3ad58b | 225 | /* |
38061563 | 226 | * Discover our host address and post it |
5f3ad58b | 227 | */ |
5f3ad58b | 228 | vs->vs_if.if_host[0] = vvidentify(unit); |
5f3ad58b SL |
229 | printf("vv%d: host %d\n", unit, vs->vs_if.if_host[0]); |
230 | sin = (struct sockaddr_in *)&vs->vs_if.if_addr; | |
231 | sin->sin_family = AF_INET; | |
38061563 | 232 | sin->sin_addr = if_makeaddr(vs->vs_if.if_net, vs->vs_if.if_host[0]); |
1cd789fb SL |
233 | sin = (struct sockaddr_in *)&vs->vs_if.if_broadaddr; |
234 | sin->sin_family = AF_INET; | |
235 | sin->sin_addr = if_makeaddr(vs->vs_if.if_net, VV_BROADCAST); | |
5f3ad58b SL |
236 | |
237 | /* | |
238 | * Reset the interface, and join the ring | |
239 | */ | |
240 | addr->vvocsr = VV_RST | VV_CPB; /* clear packet buffer */ | |
241 | addr->vvicsr = VV_RST | VV_CONF; /* close logical relay */ | |
5b319f7c | 242 | DELAY(500000); /* let contacts settle */ |
5f3ad58b | 243 | vs->vs_init = 0; |
4cdd59b5 | 244 | vs->vs_dropped = 0; |
5f3ad58b SL |
245 | vs->vs_nottaken = 0; |
246 | ||
247 | /* | |
248 | * Hang a receive and start any | |
249 | * pending writes by faking a transmit complete. | |
250 | */ | |
251 | s = splimp(); | |
252 | ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; | |
1cd789fb SL |
253 | addr->vviba = (u_short)ubainfo; |
254 | addr->vviea = (u_short)(ubainfo >> 16); | |
5f3ad58b SL |
255 | addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1; |
256 | addr->vvicsr = VV_IEN | VV_CONF | VV_DEN | VV_ENB; | |
4cdd59b5 | 257 | vs->vs_iactive = ACTIVE; |
5f3ad58b | 258 | vs->vs_oactive = 1; |
1cd789fb | 259 | vs->vs_if.if_flags |= IFF_UP | IFF_RUNNING; |
5f3ad58b SL |
260 | vvxint(unit); |
261 | splx(s); | |
262 | if_rtinit(&vs->vs_if, RTF_UP); | |
263 | } | |
264 | ||
265 | /* | |
266 | * vvidentify() - return our host address | |
267 | */ | |
38061563 | 268 | vvidentify(unit) |
1cd789fb | 269 | int unit; |
38061563 | 270 | { |
5f3ad58b SL |
271 | register struct vv_softc *vs = &vv_softc[unit]; |
272 | register struct uba_device *ui = vvinfo[unit]; | |
273 | register struct vvreg *addr; | |
274 | struct mbuf *m; | |
275 | struct vv_header *v; | |
66923854 | 276 | int ubainfo, attempts, waitcount; |
5f3ad58b | 277 | |
547e5718 SL |
278 | /* |
279 | * Build a multicast message to identify our address | |
280 | */ | |
5f3ad58b | 281 | addr = (struct vvreg *)ui->ui_addr; |
547e5718 | 282 | attempts = 0; /* total attempts, including bad msg type */ |
38061563 | 283 | m = m_get(M_DONTWAIT, MT_HEADER); |
66923854 | 284 | if (m == NULL) |
1cd789fb | 285 | return (0); |
4cdd59b5 | 286 | m->m_next = 0; |
547e5718 SL |
287 | m->m_off = MMINOFF; |
288 | m->m_len = sizeof(struct vv_header); | |
547e5718 | 289 | v = mtod(m, struct vv_header *); |
4cdd59b5 | 290 | v->vh_dhost = VV_BROADCAST; /* multicast destination address */ |
547e5718 SL |
291 | v->vh_shost = 0; /* will be overwritten with ours */ |
292 | v->vh_version = RING_VERSION; | |
293 | v->vh_type = RING_WHOAMI; | |
294 | v->vh_info = 0; | |
4cdd59b5 | 295 | /* map xmit message into uba */ |
5f3ad58b SL |
296 | vs->vs_olen = if_wubaput(&vs->vs_ifuba, m); |
297 | if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) | |
298 | UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp); | |
547e5718 SL |
299 | /* |
300 | * Reset interface, establish Digital Loopback Mode, and | |
301 | * send the multicast (to myself) with Input Copy enabled. | |
302 | */ | |
303 | retry: | |
304 | ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; | |
305 | addr->vvicsr = VV_RST; | |
306 | addr->vviba = (u_short) ubainfo; | |
307 | addr->vviea = (u_short) (ubainfo >> 16); | |
308 | addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1; | |
309 | addr->vvicsr = VV_STE | VV_DEN | VV_ENB | VV_LPB; | |
5f3ad58b SL |
310 | |
311 | /* let flag timers fire so ring will initialize */ | |
d3758410 | 312 | DELAY(2000000); |
5f3ad58b | 313 | |
547e5718 SL |
314 | addr->vvocsr = VV_RST | VV_CPB; /* clear packet buffer */ |
315 | ubainfo = vs->vs_ifuba.ifu_w.ifrw_info; | |
316 | addr->vvoba = (u_short) ubainfo; | |
317 | addr->vvoea = (u_short) (ubainfo >> 16); | |
318 | addr->vvowc = -((vs->vs_olen + 1) >> 1); | |
319 | addr->vvocsr = VV_CPB | VV_DEN | VV_INR | VV_ENB; | |
547e5718 SL |
320 | /* |
321 | * Wait for receive side to finish. | |
4cdd59b5 | 322 | * Extract source address (which will our own), |
547e5718 SL |
323 | * and post to interface structure. |
324 | */ | |
325 | DELAY(1000); | |
4cdd59b5 | 326 | for (waitcount = 0; (addr->vvicsr & VV_RDY) == 0; waitcount++) { |
0d6eea7c | 327 | if (waitcount < 10) { |
547e5718 | 328 | DELAY(1000); |
38061563 | 329 | continue; |
4cdd59b5 | 330 | } |
38061563 SL |
331 | if (attempts++ >= 10) { |
332 | printf("vv%d: can't initialize\n", unit); | |
333 | printf("vvinit loopwait: icsr = %b\n", | |
334 | 0xffff&(addr->vvicsr), VV_IBITS); | |
335 | vs->vs_if.if_flags &= ~IFF_UP; | |
66923854 | 336 | return (0); |
547e5718 | 337 | } |
38061563 | 338 | goto retry; |
4cdd59b5 | 339 | } |
547e5718 SL |
340 | if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) |
341 | UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp); | |
342 | if (vs->vs_ifuba.ifu_xtofree) | |
343 | m_freem(vs->vs_ifuba.ifu_xtofree); | |
344 | if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) | |
345 | UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp); | |
346 | m = if_rubaget(&vs->vs_ifuba, sizeof(struct vv_header), 0); | |
38061563 | 347 | if (m != NULL) |
547e5718 SL |
348 | m_freem(m); |
349 | /* | |
38061563 | 350 | * Check message type before we believe the source host address |
547e5718 SL |
351 | */ |
352 | v = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr); | |
38061563 | 353 | if (v->vh_type != RING_WHOAMI) |
5f3ad58b | 354 | goto retry; |
38061563 | 355 | return(v->vh_shost); |
4cdd59b5 SL |
356 | } |
357 | ||
358 | /* | |
359 | * vvtimeout() - called by timer flywheel to monitor input packet | |
360 | * discard rate. Interfaces getting too many errors are shut | |
361 | * down for a while. If the condition persists, the interface | |
362 | * is marked down. | |
363 | */ | |
66923854 | 364 | /*ARGSUSED*/ |
4cdd59b5 | 365 | vvtimeout(junk) |
38061563 | 366 | int junk; |
4cdd59b5 SL |
367 | { |
368 | register struct vv_softc *vs; | |
369 | register int i; | |
370 | register struct vvreg *addr; | |
371 | int ubainfo; | |
372 | ||
373 | timeout(vvtimeout, (caddr_t) 0, VV_FLYWHEEL); | |
38061563 | 374 | for (i = 0; i < NVV; i++) { |
4cdd59b5 SL |
375 | vs = &vv_softc[i]; |
376 | addr = (struct vvreg *)vvinfo[i]->ui_addr; | |
66923854 SL |
377 | if ((vs->vs_if.if_flags & IFF_UP) == 0) |
378 | continue; | |
4cdd59b5 SL |
379 | switch (vs->vs_major) { |
380 | ||
381 | /* | |
382 | * MODE0: generally OK, just check error rate | |
383 | */ | |
4cdd59b5 SL |
384 | case MODE0: |
385 | if (vs->vs_dropped < VV_ERRORTHRESHOLD) { | |
386 | vs->vs_dropped = 0; | |
387 | continue; | |
388 | } | |
38061563 SL |
389 | /* suspend reads for a while */ |
390 | vvtrprintf("vv%d going MODE1 in vvtimeout\n",i); | |
391 | vs->vs_major = MODE1; | |
392 | vs->vs_iactive = PAUSE; /* no new reads */ | |
393 | vs->vs_retry = VV_MODE1ATTEMPTS; | |
394 | vs->vs_delayclock = VV_MODE1DELAY; | |
395 | vs->vs_minor = 0; | |
396 | continue; | |
4cdd59b5 SL |
397 | |
398 | /* | |
399 | * MODE1: excessive error rate observed | |
400 | * Scheme: try simply suspending reads for a | |
401 | * short while a small number of times | |
402 | */ | |
4cdd59b5 SL |
403 | case MODE1: |
404 | if (vs->vs_delayclock > 0) { | |
405 | vs->vs_delayclock--; | |
406 | continue; | |
407 | } | |
408 | switch (vs->vs_minor) { | |
38061563 | 409 | |
4cdd59b5 SL |
410 | case 0: /* reenable reads */ |
411 | vvtrprintf("vv%d M1m0\n",i); | |
412 | vs->vs_dropped = 0; | |
413 | vs->vs_iactive = ACTIVE; | |
414 | vs->vs_minor = 1; /* next state */ | |
415 | ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; | |
416 | addr->vviba = (u_short) ubainfo; | |
417 | addr->vviea = (u_short) (ubainfo >> 16); | |
38061563 SL |
418 | addr->vviwc = |
419 | -(sizeof (struct vv_header) + VVMTU) >> 1; | |
4cdd59b5 SL |
420 | addr->vvicsr = VV_RST | VV_CONF; |
421 | addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB; | |
422 | continue; | |
38061563 | 423 | |
4cdd59b5 SL |
424 | case 1: /* see if it worked */ |
425 | vvtrprintf("vv%d M1m1\n",i); | |
426 | if (vs->vs_dropped < VV_ERRORTHRESHOLD) { | |
427 | vs->vs_dropped = 0; | |
428 | vs->vs_major = MODE0; /* yeah!! */ | |
429 | continue; | |
430 | } | |
38061563 SL |
431 | if (vs->vs_retry -- > 0) { |
432 | vs->vs_dropped = 0; | |
433 | vs->vs_iactive = PAUSE; | |
434 | vs->vs_delayclock = VV_MODE1DELAY; | |
435 | vs->vs_minor = 0; /* recheck */ | |
436 | continue; | |
4cdd59b5 | 437 | } |
38061563 SL |
438 | vs->vs_major = MODE2; |
439 | vs->vs_minor = 0; | |
440 | vs->vs_dropped = 0; | |
441 | vs->vs_iactive = OPEN; | |
442 | vs->vs_delayrange = VV_MODE2DELAY; | |
443 | vs->vs_delayclock = VV_MODE2DELAY; | |
444 | /* fall thru ... */ | |
4cdd59b5 SL |
445 | } |
446 | ||
447 | /* | |
448 | * MODE2: simply ignoring traffic didn't relieve condition | |
449 | * Scheme: open host relay for intervals linearly | |
450 | * increasing up to some maximum of a several minutes. | |
451 | * This allows broken networks to return to operation | |
452 | * without rebooting. | |
453 | */ | |
4cdd59b5 SL |
454 | case MODE2: |
455 | if (vs->vs_delayclock > 0) { | |
456 | vs->vs_delayclock--; | |
457 | continue; | |
458 | } | |
459 | switch (vs->vs_minor) { | |
38061563 | 460 | |
4cdd59b5 SL |
461 | case 0: /* close relay and reenable reads */ |
462 | vvtrprintf("vv%d M2m0\n",i); | |
463 | vs->vs_dropped = 0; | |
464 | vs->vs_iactive = ACTIVE; | |
465 | vs->vs_minor = 1; /* next state */ | |
466 | ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; | |
467 | addr->vviba = (u_short) ubainfo; | |
468 | addr->vviea = (u_short) (ubainfo >> 16); | |
38061563 SL |
469 | addr->vviwc = |
470 | -(sizeof (struct vv_header) + VVMTU) >> 1; | |
4cdd59b5 SL |
471 | addr->vvicsr = VV_RST | VV_CONF; |
472 | addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB; | |
473 | continue; | |
38061563 | 474 | |
4cdd59b5 SL |
475 | case 1: /* see if it worked */ |
476 | vvtrprintf("vv%d M2m1\n",i); | |
477 | if (vs->vs_dropped < VV_ERRORTHRESHOLD) { | |
478 | vs->vs_dropped = 0; | |
479 | vs->vs_major = MODE0; /* yeah!! */ | |
480 | continue; | |
481 | } | |
38061563 SL |
482 | vvtrprintf("vv%d M2m1 ++ delay\n",i); |
483 | vs->vs_dropped = 0; | |
484 | vs->vs_iactive = OPEN; | |
485 | vs->vs_minor = 0; | |
486 | if (vs->vs_delayrange < VV_MAXDELAY) | |
487 | vs->vs_delayrange += | |
488 | (vs->vs_delayrange/2); | |
489 | vs->vs_delayclock = vs->vs_delayrange; | |
490 | continue; | |
4cdd59b5 SL |
491 | } |
492 | ||
4cdd59b5 SL |
493 | default: |
494 | printf("vv%d: major state screwed\n", i); | |
495 | vs->vs_if.if_flags &= ~IFF_UP; | |
496 | } | |
497 | } | |
547e5718 SL |
498 | } |
499 | ||
500 | /* | |
501 | * Start or restart output on interface. | |
4cdd59b5 SL |
502 | * If interface is active, this is a retransmit, so just |
503 | * restuff registers and go. | |
547e5718 SL |
504 | * If interface is not already active, get another datagram |
505 | * to send off of the interface queue, and map it to the interface | |
506 | * before starting the output. | |
507 | */ | |
508 | vvstart(dev) | |
509 | dev_t dev; | |
510 | { | |
511 | int unit = VVUNIT(dev); | |
512 | struct uba_device *ui = vvinfo[unit]; | |
513 | register struct vv_softc *vs = &vv_softc[unit]; | |
514 | register struct vvreg *addr; | |
515 | struct mbuf *m; | |
4cdd59b5 SL |
516 | int ubainfo; |
517 | int dest; | |
547e5718 SL |
518 | |
519 | if (vs->vs_oactive) | |
520 | goto restart; | |
547e5718 SL |
521 | /* |
522 | * Not already active: dequeue another request | |
523 | * and map it to the UNIBUS. If no more requests, | |
524 | * just return. | |
525 | */ | |
526 | IF_DEQUEUE(&vs->vs_if.if_snd, m); | |
38061563 | 527 | if (m == NULL) { |
547e5718 SL |
528 | vs->vs_oactive = 0; |
529 | return; | |
530 | } | |
531 | dest = mtod(m, struct vv_header *)->vh_dhost; | |
532 | vs->vs_olen = if_wubaput(&vs->vs_ifuba, m); | |
533 | vs->vs_lastx = dest; | |
547e5718 SL |
534 | restart: |
535 | /* | |
536 | * Have request mapped to UNIBUS for transmission. | |
537 | * Purge any stale data from this BDP, and start the otput. | |
538 | */ | |
c8c82d1f | 539 | if (vs->vs_olen > VVMTU + sizeof (struct vv_header)) { |
4cdd59b5 SL |
540 | printf("vv%d vs_olen: %d > VVMTU\n", unit, vs->vs_olen); |
541 | panic("vvdriver vs_olen botch"); | |
542 | } | |
547e5718 SL |
543 | if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) |
544 | UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp); | |
545 | addr = (struct vvreg *)ui->ui_addr; | |
546 | ubainfo = vs->vs_ifuba.ifu_w.ifrw_info; | |
547 | addr->vvoba = (u_short) ubainfo; | |
548 | addr->vvoea = (u_short) (ubainfo >> 16); | |
549 | addr->vvowc = -((vs->vs_olen + 1) >> 1); | |
550 | addr->vvocsr = VV_IEN | VV_CPB | VV_DEN | VV_INR | VV_ENB; | |
551 | vs->vs_oactive = 1; | |
552 | } | |
553 | ||
554 | /* | |
555 | * VVLNI transmit interrupt | |
556 | * Start another output if more data to send. | |
557 | */ | |
558 | vvxint(unit) | |
559 | int unit; | |
560 | { | |
561 | register struct uba_device *ui = vvinfo[unit]; | |
562 | register struct vv_softc *vs = &vv_softc[unit]; | |
563 | register struct vvreg *addr; | |
564 | register int oc; | |
547e5718 SL |
565 | |
566 | addr = (struct vvreg *)ui->ui_addr; | |
567 | oc = 0xffff & (addr->vvocsr); | |
568 | if (vs->vs_oactive == 0) { | |
4cdd59b5 | 569 | printf("vv%d: stray interrupt vvocsr = %b\n", unit, |
547e5718 SL |
570 | oc, VV_OBITS); |
571 | return; | |
572 | } | |
573 | if (oc & (VV_OPT | VV_RFS)) { | |
5f3ad58b | 574 | vs->vs_if.if_collisions++; |
4cdd59b5 | 575 | if (vs->vs_tries++ < VVRETRY) { |
547e5718 SL |
576 | if (oc & VV_OPT) |
577 | vs->vs_init++; | |
578 | if (oc & VV_RFS) | |
579 | vs->vs_nottaken++; | |
4cdd59b5 | 580 | vvstart(unit); /* restart this message */ |
547e5718 SL |
581 | return; |
582 | } | |
583 | if (oc & VV_OPT) | |
584 | printf("vv%d: output timeout\n"); | |
585 | } | |
586 | vs->vs_if.if_opackets++; | |
587 | vs->vs_oactive = 0; | |
588 | vs->vs_tries = 0; | |
589 | if (oc & VVXERR) { | |
590 | vs->vs_if.if_oerrors++; | |
4cdd59b5 | 591 | printf("vv%d: error vvocsr = %b\n", unit, 0xffff & oc, |
547e5718 SL |
592 | VV_OBITS); |
593 | } | |
594 | if (vs->vs_ifuba.ifu_xtofree) { | |
595 | m_freem(vs->vs_ifuba.ifu_xtofree); | |
596 | vs->vs_ifuba.ifu_xtofree = 0; | |
597 | } | |
598 | if (vs->vs_if.if_snd.ifq_head == 0) { | |
4cdd59b5 | 599 | vs->vs_lastx = 256; /* an invalid address */ |
547e5718 SL |
600 | return; |
601 | } | |
602 | vvstart(unit); | |
603 | } | |
604 | ||
605 | /* | |
606 | * V2lni interface receiver interrupt. | |
607 | * If input error just drop packet. | |
608 | * Otherwise purge input buffered data path and examine | |
609 | * packet to determine type. If can't determine length | |
610 | * from type, then have to drop packet. Othewise decapsulate | |
611 | * packet based on type and pass to type specific higher-level | |
612 | * input routine. | |
613 | */ | |
614 | vvrint(unit) | |
615 | int unit; | |
616 | { | |
617 | register struct vv_softc *vs = &vv_softc[unit]; | |
618 | struct vvreg *addr = (struct vvreg *)vvinfo[unit]->ui_addr; | |
619 | register struct vv_header *vv; | |
620 | register struct ifqueue *inq; | |
621 | struct mbuf *m; | |
622 | int ubainfo, len, off; | |
5f3ad58b | 623 | short resid; |
547e5718 SL |
624 | |
625 | vs->vs_if.if_ipackets++; | |
626 | /* | |
627 | * Purge BDP; drop if input error indicated. | |
628 | */ | |
629 | if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP) | |
630 | UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp); | |
631 | if (addr->vvicsr & VVRERR) { | |
4cdd59b5 SL |
632 | if (vv_logreaderrors) |
633 | printf("vv%d: error vvicsr = %b\n", unit, | |
634 | 0xffff&(addr->vvicsr), VV_IBITS); | |
5f3ad58b | 635 | goto dropit; |
547e5718 | 636 | } |
5f3ad58b | 637 | |
547e5718 | 638 | /* |
5f3ad58b SL |
639 | * Get packet length from word count residue |
640 | * | |
641 | * Compute header offset if trailer protocol | |
642 | * | |
643 | * Pull packet off interface. Off is nonzero if packet | |
644 | * has trailing header; if_rubaget will then force this header | |
645 | * information to be at the front. The vh_info field | |
646 | * carries the offset to the trailer data in trailer | |
647 | * format packets. | |
547e5718 | 648 | */ |
5f3ad58b | 649 | vv = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr); |
4cdd59b5 | 650 | vvtracehdr("vi", vv); |
5f3ad58b SL |
651 | resid = addr->vviwc; |
652 | if (resid) | |
653 | resid |= 0176000; /* ugly!!!! */ | |
654 | len = (((sizeof (struct vv_header) + VVMRU) >> 1) + resid) << 1; | |
655 | len -= sizeof(struct vv_header); | |
4cdd59b5 | 656 | if (len > VVMRU || len <= 0) |
5f3ad58b SL |
657 | goto dropit; |
658 | #define vvdataaddr(vv, off, type) ((type)(((caddr_t)((vv)+1)+(off)))) | |
1cd789fb SL |
659 | if ((ifp->if_flags & IFF_NOTRAILERS) == 0) |
660 | if (vv->vh_type >= RING_IPTrailer && | |
661 | vv->vh_type < RING_IPTrailer+RING_IPNTrailer) { | |
5f3ad58b SL |
662 | off = (vv->vh_type - RING_IPTrailer) * 512; |
663 | if (off > VVMTU) | |
664 | goto dropit; | |
665 | vv->vh_type = *vvdataaddr(vv, off, u_short *); | |
666 | resid = *(vvdataaddr(vv, off+2, u_short *)); | |
667 | if (off + resid > len) | |
668 | goto dropit; | |
669 | len = off + resid; | |
38061563 | 670 | } else |
5f3ad58b SL |
671 | off = 0; |
672 | if (len == 0) | |
673 | goto dropit; | |
674 | m = if_rubaget(&vs->vs_ifuba, len, off); | |
38061563 | 675 | if (m == NULL) |
5f3ad58b SL |
676 | goto dropit; |
677 | if (off) { | |
678 | m->m_off += 2 * sizeof(u_short); | |
679 | m->m_len -= 2 * sizeof(u_short); | |
680 | } | |
4cdd59b5 SL |
681 | |
682 | /* | |
683 | * Demultiplex on packet type | |
684 | */ | |
547e5718 | 685 | switch (vv->vh_type) { |
4cdd59b5 | 686 | |
547e5718 SL |
687 | #ifdef INET |
688 | case RING_IP: | |
547e5718 SL |
689 | schednetisr(NETISR_IP); |
690 | inq = &ipintrq; | |
691 | break; | |
692 | #endif | |
693 | default: | |
694 | printf("vv%d: unknown pkt type 0x%x\n", unit, vv->vh_type); | |
5f3ad58b | 695 | m_freem(m); |
547e5718 SL |
696 | goto setup; |
697 | } | |
5f3ad58b SL |
698 | if (IF_QFULL(inq)) { |
699 | IF_DROP(inq); | |
700 | m_freem(m); | |
38061563 | 701 | } else |
5f3ad58b | 702 | IF_ENQUEUE(inq, m); |
547e5718 SL |
703 | setup: |
704 | /* | |
4cdd59b5 SL |
705 | * Check the error rate and start recovery if needed |
706 | * this has to go here since the timer flywheel runs at | |
707 | * a lower ipl and never gets a chance to change the mode | |
708 | */ | |
709 | if (vs->vs_major == MODE0 && vs->vs_dropped > VV_ERRORTHRESHOLD) { | |
4cdd59b5 SL |
710 | vvtrprintf("vv%d going MODE1 in vvrint\n",unit); |
711 | vs->vs_major = MODE1; | |
712 | vs->vs_iactive = PAUSE; /* no new reads */ | |
713 | vs->vs_retry = VV_MODE1ATTEMPTS; | |
714 | vs->vs_delayclock = VV_MODE1DELAY; | |
715 | vs->vs_minor = 0; | |
716 | vs->vs_dropped = 0; | |
717 | } | |
4cdd59b5 SL |
718 | switch (vs->vs_iactive) { |
719 | ||
38061563 | 720 | case ACTIVE: /* Restart the read for next packet */ |
4cdd59b5 SL |
721 | ubainfo = vs->vs_ifuba.ifu_r.ifrw_info; |
722 | addr->vviba = (u_short) ubainfo; | |
723 | addr->vviea = (u_short) (ubainfo >> 16); | |
724 | addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1; | |
725 | addr->vvicsr = VV_RST | VV_CONF; | |
726 | addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB; | |
727 | return; | |
728 | ||
38061563 | 729 | case PAUSE: /* requested to not start any new reads */ |
4cdd59b5 SL |
730 | vs->vs_dropped = 0; |
731 | return; | |
732 | ||
38061563 | 733 | case OPEN: /* request to open host relay */ |
4cdd59b5 SL |
734 | vs->vs_dropped = 0; |
735 | addr->vvicsr = 0; | |
736 | return; | |
737 | ||
738 | default: | |
739 | printf("vv%d: vs_iactive = %d\n", unit, vs->vs_iactive); | |
740 | return; | |
741 | } | |
4cdd59b5 | 742 | /* |
38061563 | 743 | * Drop packet on floor -- count them!! |
547e5718 | 744 | */ |
5f3ad58b SL |
745 | dropit: |
746 | vs->vs_if.if_ierrors++; | |
4cdd59b5 | 747 | vs->vs_dropped++; |
5f3ad58b SL |
748 | /* |
749 | printf("vv%d: error vvicsr = %b\n", unit, | |
750 | 0xffff&(addr->vvicsr), VV_IBITS); | |
751 | */ | |
752 | goto setup; | |
547e5718 SL |
753 | } |
754 | ||
755 | /* | |
756 | * V2lni output routine. | |
757 | * Encapsulate a packet of type family for the local net. | |
758 | * Use trailer local net encapsulation if enough data in first | |
759 | * packet leaves a multiple of 512 bytes of data in remainder. | |
760 | */ | |
761 | vvoutput(ifp, m0, dst) | |
762 | struct ifnet *ifp; | |
763 | struct mbuf *m0; | |
764 | struct sockaddr *dst; | |
765 | { | |
766 | register struct mbuf *m = m0; | |
767 | register struct vv_header *vv; | |
5f3ad58b SL |
768 | register int off; |
769 | int type, dest, s, error; | |
547e5718 SL |
770 | |
771 | switch (dst->sa_family) { | |
4cdd59b5 | 772 | |
547e5718 SL |
773 | #ifdef INET |
774 | case AF_INET: { | |
5f3ad58b | 775 | dest = ((struct sockaddr_in *)dst)->sin_addr.s_addr; |
4cdd59b5 | 776 | if ((dest = in_lnaof(*((struct in_addr *)&dest))) >= 0x100) { |
5f3ad58b SL |
777 | error = EPERM; |
778 | goto bad; | |
779 | } | |
5f3ad58b SL |
780 | off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; |
781 | if (vv_dotrailer && off > 0 && (off & 0x1ff) == 0 && | |
782 | m->m_off >= MMINOFF + 2 * sizeof (u_short)) { | |
783 | type = RING_IPTrailer + (off>>9); | |
784 | m->m_off -= 2 * sizeof (u_short); | |
785 | m->m_len += 2 * sizeof (u_short); | |
786 | *mtod(m, u_short *) = RING_IP; | |
787 | *(mtod(m, u_short *) + 1) = m->m_len; | |
788 | goto gottrailertype; | |
789 | } | |
547e5718 SL |
790 | type = RING_IP; |
791 | off = 0; | |
792 | goto gottype; | |
793 | } | |
794 | #endif | |
795 | default: | |
796 | printf("vv%d: can't handle af%d\n", ifp->if_unit, | |
797 | dst->sa_family); | |
5f3ad58b SL |
798 | error = EAFNOSUPPORT; |
799 | goto bad; | |
547e5718 SL |
800 | } |
801 | ||
802 | gottrailertype: | |
803 | /* | |
804 | * Packet to be sent as trailer: move first packet | |
805 | * (control information) to end of chain. | |
806 | */ | |
807 | while (m->m_next) | |
808 | m = m->m_next; | |
809 | m->m_next = m0; | |
810 | m = m0->m_next; | |
811 | m0->m_next = 0; | |
812 | m0 = m; | |
547e5718 SL |
813 | gottype: |
814 | /* | |
815 | * Add local net header. If no space in first mbuf, | |
816 | * allocate another. | |
817 | */ | |
818 | if (m->m_off > MMAXOFF || | |
819 | MMINOFF + sizeof (struct vv_header) > m->m_off) { | |
38061563 SL |
820 | m = m_get(M_DONTWAIT, MT_HEADER); |
821 | if (m == NULL) { | |
5f3ad58b SL |
822 | error = ENOBUFS; |
823 | goto bad; | |
547e5718 SL |
824 | } |
825 | m->m_next = m0; | |
826 | m->m_off = MMINOFF; | |
827 | m->m_len = sizeof (struct vv_header); | |
828 | } else { | |
829 | m->m_off -= sizeof (struct vv_header); | |
830 | m->m_len += sizeof (struct vv_header); | |
831 | } | |
832 | vv = mtod(m, struct vv_header *); | |
833 | vv->vh_shost = ifp->if_host[0]; | |
834 | vv->vh_dhost = dest; | |
835 | vv->vh_version = RING_VERSION; | |
836 | vv->vh_type = type; | |
5f3ad58b | 837 | vv->vh_info = off; |
4cdd59b5 | 838 | vvtracehdr("vo", vv); |
547e5718 SL |
839 | |
840 | /* | |
841 | * Queue message on interface, and start output if interface | |
842 | * not yet active. | |
843 | */ | |
844 | s = splimp(); | |
5f3ad58b SL |
845 | if (IF_QFULL(&ifp->if_snd)) { |
846 | IF_DROP(&ifp->if_snd); | |
847 | error = ENOBUFS; | |
848 | goto qfull; | |
849 | } | |
547e5718 SL |
850 | IF_ENQUEUE(&ifp->if_snd, m); |
851 | if (vv_softc[ifp->if_unit].vs_oactive == 0) | |
852 | vvstart(ifp->if_unit); | |
853 | splx(s); | |
5f3ad58b | 854 | return (0); |
5f3ad58b SL |
855 | qfull: |
856 | m0 = m; | |
857 | splx(s); | |
858 | bad: | |
859 | m_freem(m0); | |
860 | return(error); | |
547e5718 SL |
861 | } |
862 | ||
1cd789fb SL |
863 | /* |
864 | * Process an ioctl request. | |
865 | */ | |
866 | vvioctl(ifp, cmd, data) | |
867 | register struct ifnet *ifp; | |
868 | int cmd; | |
869 | caddr_t data; | |
870 | { | |
871 | struct ifreq *ifr = (struct ifreq *)data; | |
872 | int s = splimp(), error = 0; | |
873 | ||
874 | switch (cmd) { | |
875 | ||
876 | case SIOCSIFADDR: | |
877 | /* too difficult to change addr while running */ | |
878 | if ((ifp->if_flags & IFF_RUNNING) == 0) { | |
879 | ifp->if_net = in_netof(ifr->ifr_addr.sin_addr); | |
880 | vvinit(ifp->if_unit); | |
881 | } else | |
882 | error = EINVAL; | |
883 | break; | |
884 | ||
885 | default: | |
886 | error = EINVAL; | |
887 | } | |
888 | splx(s); | |
889 | return (error); | |
890 | } | |
891 | ||
547e5718 SL |
892 | /* |
893 | * vvprt_hdr(s, v) print the local net header in "v" | |
894 | * with title is "s" | |
895 | */ | |
896 | vvprt_hdr(s, v) | |
897 | char *s; | |
898 | register struct vv_header *v; | |
899 | { | |
900 | printf("%s: dsvti: 0x%x 0x%x 0x%x 0x%x 0x%x\n", | |
901 | s, | |
902 | 0xff & (int)(v->vh_dhost), 0xff & (int)(v->vh_shost), | |
903 | 0xff & (int)(v->vh_version), 0xff & (int)(v->vh_type), | |
904 | 0xffff & (int)(v->vh_info)); | |
905 | } | |
906 | ||
66923854 | 907 | #ifdef notdef |
547e5718 SL |
908 | /* |
909 | * print "l" hex bytes starting at "s" | |
910 | */ | |
911 | vvprt_hex(s, l) | |
912 | char *s; | |
913 | int l; | |
914 | { | |
915 | register int i; | |
916 | register int z; | |
917 | ||
918 | for (i=0 ; i < l; i++) { | |
919 | z = 0xff & (int)(*(s + i)); | |
920 | printf("%c%c ", | |
921 | "0123456789abcdef"[(z >> 4) & 0x0f], | |
922 | "0123456789abcdef"[z & 0x0f] | |
923 | ); | |
924 | } | |
925 | } | |
66923854 | 926 | #endif |