Commit | Line | Data |
---|---|---|
eaa60542 | 1 | /* if_il.c 4.12 82/10/10 */ |
f0d51478 BF |
2 | |
3 | #include "il.h" | |
f0d51478 BF |
4 | |
5 | /* | |
6 | * Interlan Ethernet Communications Controller interface | |
7 | */ | |
f0d51478 BF |
8 | #include "../h/param.h" |
9 | #include "../h/systm.h" | |
10 | #include "../h/mbuf.h" | |
11 | #include "../h/pte.h" | |
12 | #include "../h/buf.h" | |
13 | #include "../h/protosw.h" | |
14 | #include "../h/socket.h" | |
f0d51478 | 15 | #include "../h/vmmac.h" |
eaa60542 BJ |
16 | #include <errno.h> |
17 | ||
18 | #include "../net/if.h" | |
19 | #include "../net/netisr.h" | |
20 | #include "../net/route.h" | |
d2cc167c BJ |
21 | #include "../netinet/in.h" |
22 | #include "../netinet/in_systm.h" | |
d2cc167c BJ |
23 | #include "../netinet/ip.h" |
24 | #include "../netinet/ip_var.h" | |
25 | #include "../netpup/pup.h" | |
eaa60542 BJ |
26 | |
27 | #include "../vax/cpu.h" | |
28 | #include "../vax/mtpr.h" | |
29 | #include "../vaxif/if_ilreg.h" | |
30 | #include "../vaxif/if_il.h" | |
31 | #include "../vaxif/if_uba.h" | |
32 | #include "../vaxuba/ubareg.h" | |
33 | #include "../vaxuba/ubavar.h" | |
f0d51478 BF |
34 | |
35 | #define ILMTU 1500 | |
36 | ||
37 | int ilprobe(), ilattach(), ilrint(), ilcint(); | |
38 | struct uba_device *ilinfo[NIL]; | |
39 | u_short ilstd[] = { 0 }; | |
40 | struct uba_driver ildriver = | |
41 | { ilprobe, 0, ilattach, 0, ilstd, "il", ilinfo }; | |
f0d51478 | 42 | #define ILUNIT(x) minor(x) |
8d38dbc2 | 43 | int ilinit(),iloutput(),ilreset(),ilwatch(); |
f0d51478 | 44 | |
caf0e371 SL |
45 | u_char il_ectop[3] = { 0x02, 0x60, 0x8c }; |
46 | u_char ilbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; | |
47 | ||
f0d51478 BF |
48 | /* |
49 | * Ethernet software status per interface. | |
50 | * | |
51 | * Each interface is referenced by a network interface structure, | |
52 | * is_if, which the routing code uses to locate the interface. | |
53 | * This structure contains the output queue for the interface, its address, ... | |
54 | * We also have, for each interface, a UBA interface structure, which | |
55 | * contains information about the UNIBUS resources held by the interface: | |
56 | * map registers, buffered data paths, etc. Information is cached in this | |
57 | * structure for use by the if_uba.c routines in running the interface | |
58 | * efficiently. | |
59 | */ | |
60 | struct il_softc { | |
61 | struct ifnet is_if; /* network-visible interface */ | |
62 | struct ifuba is_ifuba; /* UNIBUS resources */ | |
8d38dbc2 SL |
63 | int is_flags; |
64 | #define ILF_OACTIVE 0x1 /* output is active */ | |
65 | #define ILF_RCVPENDING 0x2 /* start rcv in ilcint */ | |
66 | #define ILF_STATPENDING 0x4 /* stat cmd pending */ | |
67 | short is_lastcmd; /* can't read csr, so must save it */ | |
68 | short is_scaninterval; /* interval of stat collection */ | |
69 | #define ILWATCHINTERVAL 60 /* once every 60 seconds */ | |
70 | struct il_stats is_stats; /* holds on-board statistics */ | |
71 | struct il_stats is_sum; /* summation over time */ | |
72 | int is_ubaddr; /* mapping registers of is_stats */ | |
f0d51478 BF |
73 | } il_softc[NIL]; |
74 | ||
f0d51478 BF |
75 | ilprobe(reg) |
76 | caddr_t reg; | |
77 | { | |
78 | register int br, cvec; /* r11, r10 value-result */ | |
79 | register struct ildevice *addr = (struct ildevice *)reg; | |
80 | register i; | |
81 | ||
f0d51478 BF |
82 | #ifdef lint |
83 | br = 0; cvec = br; br = cvec; | |
8d38dbc2 | 84 | ilrint(0); ilcint(0); ilwatch(0); |
f0d51478 BF |
85 | #endif |
86 | ||
87 | addr->il_csr = ILC_OFFLINE|IL_CIE; | |
88 | DELAY(100000); | |
8d38dbc2 | 89 | i = addr->il_csr; /* clear CDONE */ |
f0d51478 BF |
90 | if (cvec > 0 && cvec != 0x200) |
91 | cvec -= 4; | |
92 | return (1); | |
93 | } | |
94 | ||
f0d51478 BF |
95 | /* |
96 | * Interface exists: make available by filling in network interface | |
97 | * record. System will initialize the interface when it is ready | |
98 | * to accept packets. A STATUS command is done to get the ethernet | |
99 | * address and other interesting data. | |
100 | */ | |
101 | ilattach(ui) | |
102 | struct uba_device *ui; | |
103 | { | |
104 | register struct il_softc *is = &il_softc[ui->ui_unit]; | |
caf0e371 | 105 | register struct ifnet *ifp = &is->is_if; |
f0d51478 | 106 | register struct ildevice *addr = (struct ildevice *)ui->ui_addr; |
caf0e371 | 107 | struct sockaddr_in *sin; |
f0d51478 | 108 | |
caf0e371 SL |
109 | ifp->if_unit = ui->ui_unit; |
110 | ifp->if_name = "il"; | |
111 | ifp->if_mtu = ILMTU; | |
112 | ifp->if_net = ui->ui_flags; | |
f0d51478 BF |
113 | |
114 | /* | |
8d38dbc2 SL |
115 | * Reset the board and map the statistics |
116 | * buffer onto the Unibus. | |
f0d51478 | 117 | */ |
8d38dbc2 SL |
118 | addr->il_csr = ILC_RESET; |
119 | while ((addr->il_csr&IL_CDONE) == 0) | |
120 | ; | |
121 | if (addr->il_csr&IL_STATUS) | |
122 | printf("il%d: reset failed, csr=%b\n", ui->ui_unit, | |
123 | addr->il_csr, IL_BITS); | |
f0d51478 | 124 | |
8d38dbc2 SL |
125 | is->is_ubaddr = uballoc(ui->ui_ubanum, &is->is_stats, |
126 | sizeof (struct il_stats), 0); | |
127 | addr->il_bar = is->is_ubaddr & 0xffff; | |
128 | addr->il_bcr = sizeof (struct il_stats); | |
129 | addr->il_csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_STAT; | |
130 | while ((addr->il_csr&IL_CDONE) == 0) | |
131 | ; | |
132 | if (addr->il_csr&IL_STATUS) | |
133 | printf("il%d: status failed, csr=%b\n", ui->ui_unit, | |
134 | addr->il_csr, IL_BITS); | |
135 | ubarelse(ui->ui_ubanum, &is->is_ubaddr); | |
f0d51478 BF |
136 | printf("il%d: addr=%x:%x:%x:%x:%x:%x module=%s firmware=%s\n", |
137 | ui->ui_unit, | |
8d38dbc2 SL |
138 | is->is_stats.ils_addr[0]&0xff, is->is_stats.ils_addr[1]&0xff, |
139 | is->is_stats.ils_addr[2]&0xff, is->is_stats.ils_addr[3]&0xff, | |
140 | is->is_stats.ils_addr[4]&0xff, is->is_stats.ils_addr[5]&0xff, | |
141 | is->is_stats.ils_module, is->is_stats.ils_firmware); | |
142 | ifp->if_host[0] = | |
143 | ((is->is_stats.ils_addr[3]&0xff)<<16) | 0x800000 | | |
144 | ((is->is_stats.ils_addr[4]&0xff)<<8) | | |
145 | (is->is_stats.ils_addr[5]&0xff); | |
caf0e371 | 146 | sin = (struct sockaddr_in *)&ifp->if_addr; |
f0d51478 | 147 | sin->sin_family = AF_INET; |
caf0e371 | 148 | sin->sin_addr = if_makeaddr(ifp->if_net, ifp->if_host[0]); |
f0d51478 | 149 | |
caf0e371 | 150 | sin = (struct sockaddr_in *)&ifp->if_broadaddr; |
f0d51478 | 151 | sin->sin_family = AF_INET; |
caf0e371 SL |
152 | sin->sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY); |
153 | ifp->if_flags = IFF_BROADCAST; | |
f0d51478 | 154 | |
caf0e371 SL |
155 | ifp->if_init = ilinit; |
156 | ifp->if_output = iloutput; | |
157 | ifp->if_ubareset = ilreset; | |
8d38dbc2 SL |
158 | ifp->if_watchdog = ilwatch; |
159 | is->is_scaninterval = ILWATCHINTERVAL; | |
160 | ifp->if_timer = is->is_scaninterval; | |
f0d51478 | 161 | is->is_ifuba.ifu_flags = UBA_CANTWAIT; |
caf0e371 SL |
162 | #ifdef notdef |
163 | is->is_ifuba.ifu_flags |= UBA_NEEDBDP; | |
164 | #endif | |
165 | if_attach(ifp); | |
f0d51478 BF |
166 | } |
167 | ||
168 | /* | |
169 | * Reset of interface after UNIBUS reset. | |
170 | * If interface is on specified uba, reset its state. | |
171 | */ | |
172 | ilreset(unit, uban) | |
173 | int unit, uban; | |
174 | { | |
175 | register struct uba_device *ui; | |
f0d51478 BF |
176 | |
177 | if (unit >= NIL || (ui = ilinfo[unit]) == 0 || ui->ui_alive == 0 || | |
178 | ui->ui_ubanum != uban) | |
179 | return; | |
180 | printf(" il%d", unit); | |
181 | ilinit(unit); | |
182 | } | |
183 | ||
184 | /* | |
185 | * Initialization of interface; clear recorded pending | |
186 | * operations, and reinitialize UNIBUS usage. | |
187 | */ | |
188 | ilinit(unit) | |
189 | int unit; | |
190 | { | |
191 | register struct il_softc *is = &il_softc[unit]; | |
192 | register struct uba_device *ui = ilinfo[unit]; | |
193 | register struct ildevice *addr; | |
8d38dbc2 | 194 | int s; |
f0d51478 BF |
195 | |
196 | if (if_ubainit(&is->is_ifuba, ui->ui_ubanum, | |
197 | sizeof (struct il_rheader), (int)btoc(ILMTU)) == 0) { | |
198 | printf("il%d: can't initialize\n", unit); | |
199 | is->is_if.if_flags &= ~IFF_UP; | |
200 | return; | |
201 | } | |
8d38dbc2 SL |
202 | is->is_ubaddr = uballoc(ui->ui_ubanum, &is->is_stats, |
203 | sizeof (struct il_stats), 0); | |
f0d51478 BF |
204 | addr = (struct ildevice *)ui->ui_addr; |
205 | ||
206 | /* | |
207 | * Set board online. | |
208 | * Hang receive buffer and start any pending | |
209 | * writes by faking a transmit complete. | |
210 | * Receive bcr is not a muliple of 4 so buffer | |
211 | * chaining can't happen. | |
212 | */ | |
213 | s = splimp(); | |
214 | addr->il_csr = ILC_ONLINE; | |
caf0e371 SL |
215 | while ((addr->il_csr & IL_CDONE) == 0) |
216 | ; | |
f0d51478 BF |
217 | addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff; |
218 | addr->il_bcr = sizeof(struct il_rheader) + ILMTU + 6; | |
8d38dbc2 SL |
219 | addr->il_csr = |
220 | ((is->is_ifuba.ifu_r.ifrw_info >> 2) & IL_EUA)|ILC_RCV|IL_RIE; | |
caf0e371 SL |
221 | while ((addr->il_csr & IL_CDONE) == 0) |
222 | ; | |
8d38dbc2 | 223 | is->is_flags = ILF_OACTIVE; |
f0d51478 | 224 | is->is_if.if_flags |= IFF_UP; |
8d38dbc2 | 225 | is->is_lastcmd = 0; |
f0d51478 BF |
226 | ilcint(unit); |
227 | splx(s); | |
a13c006d | 228 | if_rtinit(&is->is_if, RTF_UP); |
f0d51478 BF |
229 | } |
230 | ||
231 | /* | |
232 | * Start output on interface. | |
233 | * Get another datagram to send off of the interface queue, | |
234 | * and map it to the interface before starting the output. | |
235 | */ | |
236 | ilstart(dev) | |
237 | dev_t dev; | |
238 | { | |
caf0e371 | 239 | int unit = ILUNIT(dev), dest, len; |
f0d51478 BF |
240 | struct uba_device *ui = ilinfo[unit]; |
241 | register struct il_softc *is = &il_softc[unit]; | |
242 | register struct ildevice *addr; | |
f0d51478 | 243 | struct mbuf *m; |
caf0e371 | 244 | short csr; |
f0d51478 | 245 | |
f0d51478 | 246 | IF_DEQUEUE(&is->is_if.if_snd, m); |
8d38dbc2 SL |
247 | addr = (struct ildevice *)ui->ui_addr; |
248 | if (m == 0) { | |
249 | if ((is->is_flags & ILF_STATPENDING) == 0) | |
250 | return; | |
251 | addr->il_bar = is->is_ubaddr & 0xfff; | |
252 | addr->il_bcr = sizeof (struct il_stats); | |
253 | csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_STAT|IL_RIE|IL_CIE; | |
254 | is->is_flags &= ~ILF_STATPENDING; | |
255 | goto startcmd; | |
256 | } | |
f0d51478 | 257 | len = if_wubaput(&is->is_ifuba, m); |
f0d51478 BF |
258 | if (is->is_ifuba.ifu_flags & UBA_NEEDBDP) |
259 | UBAPURGE(is->is_ifuba.ifu_uba, is->is_ifuba.ifu_w.ifrw_bdp); | |
f0d51478 BF |
260 | addr->il_bar = is->is_ifuba.ifu_w.ifrw_info & 0xffff; |
261 | addr->il_bcr = len; | |
8d38dbc2 SL |
262 | csr = |
263 | ((is->is_ifuba.ifu_w.ifrw_info >> 2) & IL_EUA)|ILC_XMIT|IL_CIE|IL_RIE; | |
264 | ||
265 | startcmd: | |
266 | is->is_lastcmd = csr & IL_CMD; | |
caf0e371 | 267 | addr->il_csr = csr; |
8d38dbc2 | 268 | is->is_flags |= ILF_OACTIVE; |
f0d51478 BF |
269 | } |
270 | ||
271 | /* | |
272 | * Command done interrupt. | |
f0d51478 BF |
273 | */ |
274 | ilcint(unit) | |
275 | int unit; | |
276 | { | |
f0d51478 | 277 | register struct il_softc *is = &il_softc[unit]; |
caf0e371 | 278 | struct uba_device *ui = ilinfo[unit]; |
f0d51478 | 279 | register struct ildevice *addr = (struct ildevice *)ui->ui_addr; |
5565eee9 | 280 | short csr; |
f0d51478 | 281 | |
8d38dbc2 | 282 | if ((is->is_flags & ILF_OACTIVE) == 0) { |
caf0e371 | 283 | printf("il%d: stray xmit interrupt, csr=%b\n", unit, |
8d38dbc2 | 284 | addr->il_csr, IL_BITS); |
f0d51478 BF |
285 | return; |
286 | } | |
caf0e371 | 287 | |
5565eee9 | 288 | csr = addr->il_csr; |
f0d51478 | 289 | /* |
8d38dbc2 SL |
290 | * Hang receive buffer if it couldn't |
291 | * be done earlier (in ilrint). | |
f0d51478 | 292 | */ |
8d38dbc2 | 293 | if (is->is_flags & ILF_RCVPENDING) { |
f0d51478 BF |
294 | addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff; |
295 | addr->il_bcr = sizeof(struct il_rheader) + ILMTU + 6; | |
8d38dbc2 SL |
296 | addr->il_csr = |
297 | ((is->is_ifuba.ifu_r.ifrw_info >> 2) & IL_EUA)|ILC_RCV|IL_RIE; | |
caf0e371 SL |
298 | while ((addr->il_csr & IL_CDONE) == 0) |
299 | ; | |
8d38dbc2 SL |
300 | is->is_flags &= ~ILF_RCVPENDING; |
301 | } | |
302 | is->is_flags &= ~ILF_OACTIVE; | |
5565eee9 | 303 | csr &= IL_STATUS; |
8d38dbc2 SL |
304 | switch (is->is_lastcmd) { |
305 | ||
306 | case ILC_XMIT: | |
307 | is->is_if.if_opackets++; | |
5565eee9 | 308 | if (csr > ILERR_RETRIES) |
8d38dbc2 SL |
309 | is->is_if.if_oerrors++; |
310 | break; | |
311 | ||
312 | case ILC_STAT: | |
5565eee9 | 313 | if (csr == ILERR_SUCCESS) |
8d38dbc2 SL |
314 | iltotal(is); |
315 | break; | |
f0d51478 BF |
316 | } |
317 | if (is->is_ifuba.ifu_xtofree) { | |
318 | m_freem(is->is_ifuba.ifu_xtofree); | |
319 | is->is_ifuba.ifu_xtofree = 0; | |
320 | } | |
8d38dbc2 | 321 | ilstart(unit); |
f0d51478 BF |
322 | } |
323 | ||
324 | /* | |
325 | * Ethernet interface receiver interrupt. | |
326 | * If input error just drop packet. | |
327 | * Otherwise purge input buffered data path and examine | |
328 | * packet to determine type. If can't determine length | |
329 | * from type, then have to drop packet. Othewise decapsulate | |
330 | * packet based on type and pass to type specific higher-level | |
331 | * input routine. | |
332 | */ | |
333 | ilrint(unit) | |
334 | int unit; | |
335 | { | |
336 | register struct il_softc *is = &il_softc[unit]; | |
337 | struct ildevice *addr = (struct ildevice *)ilinfo[unit]->ui_addr; | |
338 | register struct il_rheader *il; | |
339 | struct mbuf *m; | |
340 | int len, off, resid; | |
341 | register struct ifqueue *inq; | |
342 | ||
f0d51478 BF |
343 | is->is_if.if_ipackets++; |
344 | if (is->is_ifuba.ifu_flags & UBA_NEEDBDP) | |
345 | UBAPURGE(is->is_ifuba.ifu_uba, is->is_ifuba.ifu_r.ifrw_bdp); | |
346 | il = (struct il_rheader *)(is->is_ifuba.ifu_r.ifrw_addr); | |
347 | len = il->ilr_length - sizeof(struct il_rheader); | |
8d38dbc2 | 348 | if ((il->ilr_status&(ILFSTAT_A|ILFSTAT_C)) || len < 46 || len > ILMTU) { |
f0d51478 BF |
349 | is->is_if.if_ierrors++; |
350 | #ifdef notdef | |
351 | if (is->is_if.if_ierrors % 100 == 0) | |
352 | printf("il%d: += 100 input errors\n", unit); | |
353 | #endif | |
f0d51478 BF |
354 | goto setup; |
355 | } | |
356 | ||
357 | /* | |
358 | * Deal with trailer protocol: if type is PUP trailer | |
359 | * get true type from first 16-bit word past data. | |
360 | * Remember that type was trailer by setting off. | |
361 | */ | |
362 | #define ildataaddr(il, off, type) ((type)(((caddr_t)((il)+1)+(off)))) | |
363 | if (il->ilr_type >= ILPUP_TRAIL && | |
364 | il->ilr_type < ILPUP_TRAIL+ILPUP_NTRAILER) { | |
365 | off = (il->ilr_type - ILPUP_TRAIL) * 512; | |
366 | if (off >= ILMTU) | |
367 | goto setup; /* sanity */ | |
368 | il->ilr_type = *ildataaddr(il, off, u_short *); | |
369 | resid = *(ildataaddr(il, off+2, u_short *)); | |
370 | if (off + resid > len) | |
371 | goto setup; /* sanity */ | |
372 | len = off + resid; | |
373 | } else | |
374 | off = 0; | |
375 | if (len == 0) | |
376 | goto setup; | |
377 | ||
378 | /* | |
379 | * Pull packet off interface. Off is nonzero if packet | |
380 | * has trailing header; ilget will then force this header | |
381 | * information to be at the front, but we still have to drop | |
382 | * the type and length which are at the front of any trailer data. | |
383 | */ | |
384 | m = if_rubaget(&is->is_ifuba, len, off); | |
385 | if (m == 0) | |
386 | goto setup; | |
387 | if (off) { | |
388 | m->m_off += 2 * sizeof (u_short); | |
389 | m->m_len -= 2 * sizeof (u_short); | |
390 | } | |
391 | switch (il->ilr_type) { | |
392 | ||
393 | #ifdef INET | |
394 | case ILPUP_IPTYPE: | |
395 | schednetisr(NETISR_IP); | |
396 | inq = &ipintrq; | |
397 | break; | |
398 | #endif | |
399 | default: | |
400 | m_freem(m); | |
401 | goto setup; | |
402 | } | |
403 | ||
404 | if (IF_QFULL(inq)) { | |
405 | IF_DROP(inq); | |
406 | m_freem(m); | |
caf0e371 SL |
407 | goto setup; |
408 | } | |
409 | IF_ENQUEUE(inq, m); | |
f0d51478 BF |
410 | |
411 | setup: | |
412 | /* | |
413 | * Reset for next packet if possible. | |
414 | * If waiting for transmit command completion, set flag | |
415 | * and wait until command completes. | |
416 | */ | |
8d38dbc2 SL |
417 | if (is->is_flags & ILF_OACTIVE) { |
418 | is->is_flags |= ILF_RCVPENDING; | |
caf0e371 SL |
419 | return; |
420 | } | |
421 | addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff; | |
422 | addr->il_bcr = sizeof(struct il_rheader) + ILMTU + 6; | |
8d38dbc2 SL |
423 | addr->il_csr = |
424 | ((is->is_ifuba.ifu_r.ifrw_info >> 2) & IL_EUA)|ILC_RCV|IL_RIE; | |
caf0e371 SL |
425 | while ((addr->il_csr & IL_CDONE) == 0) |
426 | ; | |
f0d51478 BF |
427 | } |
428 | ||
429 | /* | |
430 | * Ethernet output routine. | |
431 | * Encapsulate a packet of type family for the local net. | |
432 | * Use trailer local net encapsulation if enough data in first | |
433 | * packet leaves a multiple of 512 bytes of data in remainder. | |
434 | */ | |
435 | iloutput(ifp, m0, dst) | |
436 | struct ifnet *ifp; | |
437 | struct mbuf *m0; | |
438 | struct sockaddr *dst; | |
439 | { | |
440 | int type, dest, s, error; | |
441 | register struct il_softc *is = &il_softc[ifp->if_unit]; | |
442 | register struct mbuf *m = m0; | |
443 | register struct il_xheader *il; | |
caf0e371 | 444 | register int off, i; |
f0d51478 | 445 | |
f0d51478 BF |
446 | switch (dst->sa_family) { |
447 | ||
448 | #ifdef INET | |
449 | case AF_INET: | |
450 | dest = ((struct sockaddr_in *)dst)->sin_addr.s_addr; | |
451 | off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; | |
452 | if (off > 0 && (off & 0x1ff) == 0 && | |
453 | m->m_off >= MMINOFF + 2 * sizeof (u_short)) { | |
454 | type = ILPUP_TRAIL + (off>>9); | |
455 | m->m_off -= 2 * sizeof (u_short); | |
456 | m->m_len += 2 * sizeof (u_short); | |
457 | *mtod(m, u_short *) = ILPUP_IPTYPE; | |
458 | *(mtod(m, u_short *) + 1) = m->m_len; | |
459 | goto gottrailertype; | |
460 | } | |
461 | type = ILPUP_IPTYPE; | |
462 | off = 0; | |
463 | goto gottype; | |
464 | #endif | |
465 | ||
466 | default: | |
467 | printf("il%d: can't handle af%d\n", ifp->if_unit, | |
468 | dst->sa_family); | |
469 | error = EAFNOSUPPORT; | |
470 | goto bad; | |
471 | } | |
472 | ||
473 | gottrailertype: | |
474 | /* | |
475 | * Packet to be sent as trailer: move first packet | |
476 | * (control information) to end of chain. | |
477 | */ | |
478 | while (m->m_next) | |
479 | m = m->m_next; | |
480 | m->m_next = m0; | |
481 | m = m0->m_next; | |
482 | m0->m_next = 0; | |
483 | m0 = m; | |
484 | ||
485 | gottype: | |
486 | /* | |
487 | * Add local net header. If no space in first mbuf, | |
488 | * allocate another. | |
489 | */ | |
490 | if (m->m_off > MMAXOFF || | |
491 | MMINOFF + sizeof (struct il_xheader) > m->m_off) { | |
492 | m = m_get(M_DONTWAIT); | |
493 | if (m == 0) { | |
494 | error = ENOBUFS; | |
495 | goto bad; | |
496 | } | |
497 | m->m_next = m0; | |
498 | m->m_off = MMINOFF; | |
499 | m->m_len = sizeof (struct il_xheader); | |
500 | } else { | |
501 | m->m_off -= sizeof (struct il_xheader); | |
502 | m->m_len += sizeof (struct il_xheader); | |
503 | } | |
504 | il = mtod(m, struct il_xheader *); | |
505 | if ((dest &~ 0xff) == 0) | |
caf0e371 | 506 | bcopy(ilbroadcastaddr, il->ilx_dhost, 6); |
f0d51478 | 507 | else { |
8d38dbc2 | 508 | u_char *to = dest & 0x8000 ? is->is_stats.ils_addr : il_ectop; |
caf0e371 SL |
509 | |
510 | bcopy(to, il->ilx_dhost, 3); | |
f0d51478 BF |
511 | il->ilx_dhost[3] = (dest>>8) & 0x7f; |
512 | il->ilx_dhost[4] = (dest>>16) & 0xff; | |
513 | il->ilx_dhost[5] = (dest>>24) & 0xff; | |
514 | } | |
515 | il->ilx_type = type; | |
516 | ||
517 | /* | |
518 | * Queue message on interface, and start output if interface | |
519 | * not yet active. | |
520 | */ | |
521 | s = splimp(); | |
522 | if (IF_QFULL(&ifp->if_snd)) { | |
523 | IF_DROP(&ifp->if_snd); | |
caf0e371 SL |
524 | splx(s); |
525 | m_freem(m); | |
526 | return (ENOBUFS); | |
f0d51478 BF |
527 | } |
528 | IF_ENQUEUE(&ifp->if_snd, m); | |
8d38dbc2 | 529 | if ((is->is_flags & ILF_OACTIVE) == 0) |
f0d51478 BF |
530 | ilstart(ifp->if_unit); |
531 | splx(s); | |
532 | return (0); | |
caf0e371 | 533 | |
f0d51478 BF |
534 | bad: |
535 | m_freem(m0); | |
caf0e371 | 536 | return (error); |
f0d51478 | 537 | } |
8d38dbc2 SL |
538 | |
539 | /* | |
540 | * Watchdog routine, request statistics from board. | |
541 | */ | |
542 | ilwatch(unit) | |
543 | int unit; | |
544 | { | |
545 | register struct il_softc *is = &il_softc[unit]; | |
546 | register struct ifnet *ifp = &is->is_if; | |
547 | int s; | |
548 | ||
549 | if (is->is_flags & ILF_STATPENDING) { | |
550 | ifp->if_timer = is->is_scaninterval; | |
551 | return; | |
552 | } | |
553 | s = splimp(); | |
554 | is->is_flags |= ILF_STATPENDING; | |
555 | if ((is->is_flags & ILF_OACTIVE) == 0) | |
556 | ilstart(ifp->if_unit); | |
557 | splx(s); | |
558 | ifp->if_timer = is->is_scaninterval; | |
559 | } | |
560 | ||
561 | /* | |
562 | * Total up the on-board statistics. | |
563 | */ | |
564 | iltotal(is) | |
565 | register struct il_softc *is; | |
566 | { | |
567 | register u_short *interval, *sum, *end; | |
568 | ||
569 | interval = &is->is_stats.ils_frames; | |
570 | sum = &is->is_sum.ils_frames; | |
571 | end = is->is_sum.ils_fill2; | |
572 | while (sum < end) | |
573 | *sum++ += *interval++; | |
574 | is->is_if.if_collisions = is->is_sum.ils_collis; | |
575 | } |