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