Commit | Line | Data |
---|---|---|
cd4baa58 | 1 | /* |
2eb4e665 | 2 | * Copyright (c) 1982, 1986 Regents of the University of California. |
cd4baa58 KS |
3 | * All rights reserved. The Berkeley software License Agreement |
4 | * specifies the terms and conditions for redistribution. | |
5 | * | |
ca67e7b4 | 6 | * @(#)if_il.c 7.2 (Berkeley) 8/7/86 |
cd4baa58 | 7 | */ |
f0d51478 BF |
8 | |
9 | #include "il.h" | |
9a0b0c74 | 10 | #if NIL > 0 |
f0d51478 BF |
11 | |
12 | /* | |
13 | * Interlan Ethernet Communications Controller interface | |
14 | */ | |
961945a8 SL |
15 | #include "../machine/pte.h" |
16 | ||
a6e960e7 JB |
17 | #include "param.h" |
18 | #include "systm.h" | |
19 | #include "mbuf.h" | |
20 | #include "buf.h" | |
21 | #include "protosw.h" | |
22 | #include "socket.h" | |
23 | #include "vmmac.h" | |
24 | #include "ioctl.h" | |
25 | #include "errno.h" | |
f883ae2e | 26 | #include "syslog.h" |
eaa60542 BJ |
27 | |
28 | #include "../net/if.h" | |
29 | #include "../net/netisr.h" | |
30 | #include "../net/route.h" | |
cd4baa58 KS |
31 | |
32 | #ifdef INET | |
d2cc167c BJ |
33 | #include "../netinet/in.h" |
34 | #include "../netinet/in_systm.h" | |
7f0e1e06 | 35 | #include "../netinet/in_var.h" |
d2cc167c | 36 | #include "../netinet/ip.h" |
8ae4cebd | 37 | #include "../netinet/if_ether.h" |
cd4baa58 KS |
38 | #endif |
39 | ||
cd4baa58 KS |
40 | #ifdef NS |
41 | #include "../netns/ns.h" | |
42 | #include "../netns/ns_if.h" | |
43 | #endif | |
44 | ||
eaa60542 BJ |
45 | #include "../vax/cpu.h" |
46 | #include "../vax/mtpr.h" | |
a6e960e7 JB |
47 | #include "if_il.h" |
48 | #include "if_ilreg.h" | |
49 | #include "if_uba.h" | |
eaa60542 BJ |
50 | #include "../vaxuba/ubareg.h" |
51 | #include "../vaxuba/ubavar.h" | |
f0d51478 | 52 | |
f0d51478 BF |
53 | int ilprobe(), ilattach(), ilrint(), ilcint(); |
54 | struct uba_device *ilinfo[NIL]; | |
55 | u_short ilstd[] = { 0 }; | |
56 | struct uba_driver ildriver = | |
57 | { ilprobe, 0, ilattach, 0, ilstd, "il", ilinfo }; | |
f0d51478 | 58 | #define ILUNIT(x) minor(x) |
1cd789fb | 59 | int ilinit(),iloutput(),ilioctl(),ilreset(),ilwatch(); |
955bf7a0 | 60 | int ildebug = 0; |
f0d51478 BF |
61 | |
62 | /* | |
63 | * Ethernet software status per interface. | |
64 | * | |
65 | * Each interface is referenced by a network interface structure, | |
66 | * is_if, which the routing code uses to locate the interface. | |
67 | * This structure contains the output queue for the interface, its address, ... | |
68 | * We also have, for each interface, a UBA interface structure, which | |
69 | * contains information about the UNIBUS resources held by the interface: | |
70 | * map registers, buffered data paths, etc. Information is cached in this | |
71 | * structure for use by the if_uba.c routines in running the interface | |
72 | * efficiently. | |
73 | */ | |
74 | struct il_softc { | |
8ae4cebd SL |
75 | struct arpcom is_ac; /* Ethernet common part */ |
76 | #define is_if is_ac.ac_if /* network-visible interface */ | |
77 | #define is_addr is_ac.ac_enaddr /* hardware Ethernet address */ | |
f0d51478 | 78 | struct ifuba is_ifuba; /* UNIBUS resources */ |
8d38dbc2 SL |
79 | int is_flags; |
80 | #define ILF_OACTIVE 0x1 /* output is active */ | |
81 | #define ILF_RCVPENDING 0x2 /* start rcv in ilcint */ | |
82 | #define ILF_STATPENDING 0x4 /* stat cmd pending */ | |
0f487295 | 83 | #define ILF_RUNNING 0x8 /* board is running */ |
d4ce9212 | 84 | #define ILF_SETADDR 0x10 /* physical address is changed */ |
8d38dbc2 SL |
85 | short is_lastcmd; /* can't read csr, so must save it */ |
86 | short is_scaninterval; /* interval of stat collection */ | |
87 | #define ILWATCHINTERVAL 60 /* once every 60 seconds */ | |
88 | struct il_stats is_stats; /* holds on-board statistics */ | |
89 | struct il_stats is_sum; /* summation over time */ | |
90 | int is_ubaddr; /* mapping registers of is_stats */ | |
f0d51478 BF |
91 | } il_softc[NIL]; |
92 | ||
f0d51478 BF |
93 | ilprobe(reg) |
94 | caddr_t reg; | |
95 | { | |
96 | register int br, cvec; /* r11, r10 value-result */ | |
97 | register struct ildevice *addr = (struct ildevice *)reg; | |
98 | register i; | |
99 | ||
f0d51478 BF |
100 | #ifdef lint |
101 | br = 0; cvec = br; br = cvec; | |
4fce3bf9 | 102 | i = 0; ilrint(i); ilcint(i); ilwatch(i); |
f0d51478 BF |
103 | #endif |
104 | ||
105 | addr->il_csr = ILC_OFFLINE|IL_CIE; | |
106 | DELAY(100000); | |
8d38dbc2 | 107 | i = addr->il_csr; /* clear CDONE */ |
f0d51478 BF |
108 | if (cvec > 0 && cvec != 0x200) |
109 | cvec -= 4; | |
110 | return (1); | |
111 | } | |
112 | ||
f0d51478 BF |
113 | /* |
114 | * Interface exists: make available by filling in network interface | |
115 | * record. System will initialize the interface when it is ready | |
116 | * to accept packets. A STATUS command is done to get the ethernet | |
117 | * address and other interesting data. | |
118 | */ | |
119 | ilattach(ui) | |
120 | struct uba_device *ui; | |
121 | { | |
122 | register struct il_softc *is = &il_softc[ui->ui_unit]; | |
caf0e371 | 123 | register struct ifnet *ifp = &is->is_if; |
f0d51478 | 124 | register struct ildevice *addr = (struct ildevice *)ui->ui_addr; |
f0d51478 | 125 | |
caf0e371 SL |
126 | ifp->if_unit = ui->ui_unit; |
127 | ifp->if_name = "il"; | |
3fa8d9bb | 128 | ifp->if_mtu = ETHERMTU; |
7f0e1e06 | 129 | ifp->if_flags = IFF_BROADCAST; |
f0d51478 BF |
130 | |
131 | /* | |
8d38dbc2 SL |
132 | * Reset the board and map the statistics |
133 | * buffer onto the Unibus. | |
f0d51478 | 134 | */ |
8d38dbc2 | 135 | addr->il_csr = ILC_RESET; |
d4ce9212 | 136 | (void)ilwait(ui, "reset"); |
f0d51478 | 137 | |
4fce3bf9 | 138 | is->is_ubaddr = uballoc(ui->ui_ubanum, (caddr_t)&is->is_stats, |
1cd789fb | 139 | sizeof (struct il_stats), 0); |
8d38dbc2 SL |
140 | addr->il_bar = is->is_ubaddr & 0xffff; |
141 | addr->il_bcr = sizeof (struct il_stats); | |
142 | addr->il_csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_STAT; | |
d4ce9212 | 143 | (void)ilwait(ui, "status"); |
8d38dbc2 | 144 | ubarelse(ui->ui_ubanum, &is->is_ubaddr); |
955bf7a0 MK |
145 | if (ildebug) |
146 | printf("il%d: module=%s firmware=%s\n", ui->ui_unit, | |
147 | is->is_stats.ils_module, is->is_stats.ils_firmware); | |
7f0e1e06 MK |
148 | bcopy((caddr_t)is->is_stats.ils_addr, (caddr_t)is->is_addr, |
149 | sizeof (is->is_addr)); | |
d4ce9212 MK |
150 | printf("il%d: hardware address %s\n", ui->ui_unit, |
151 | ether_sprintf(is->is_addr)); | |
caf0e371 SL |
152 | ifp->if_init = ilinit; |
153 | ifp->if_output = iloutput; | |
1cd789fb | 154 | ifp->if_ioctl = ilioctl; |
51595ca2 | 155 | ifp->if_reset = ilreset; |
f0d51478 | 156 | is->is_ifuba.ifu_flags = UBA_CANTWAIT; |
caf0e371 | 157 | if_attach(ifp); |
f0d51478 BF |
158 | } |
159 | ||
d4ce9212 MK |
160 | ilwait(ui, op) |
161 | struct uba_device *ui; | |
162 | char *op; | |
163 | { | |
164 | register struct ildevice *addr = (struct ildevice *)ui->ui_addr; | |
165 | ||
166 | while ((addr->il_csr&IL_CDONE) == 0) | |
167 | ; | |
168 | if (addr->il_csr&IL_STATUS) { | |
169 | printf("il%d: %s failed, csr=%b\n", ui->ui_unit, op, | |
170 | addr->il_csr, IL_BITS); | |
171 | return (-1); | |
172 | } | |
173 | return (0); | |
174 | } | |
175 | ||
f0d51478 BF |
176 | /* |
177 | * Reset of interface after UNIBUS reset. | |
178 | * If interface is on specified uba, reset its state. | |
179 | */ | |
180 | ilreset(unit, uban) | |
181 | int unit, uban; | |
182 | { | |
183 | register struct uba_device *ui; | |
f0d51478 BF |
184 | |
185 | if (unit >= NIL || (ui = ilinfo[unit]) == 0 || ui->ui_alive == 0 || | |
186 | ui->ui_ubanum != uban) | |
187 | return; | |
188 | printf(" il%d", unit); | |
0f487295 MK |
189 | il_softc[unit].is_if.if_flags &= ~IFF_RUNNING; |
190 | il_softc[unit].is_flags &= ~ILF_RUNNING; | |
f0d51478 BF |
191 | ilinit(unit); |
192 | } | |
193 | ||
194 | /* | |
195 | * Initialization of interface; clear recorded pending | |
196 | * operations, and reinitialize UNIBUS usage. | |
197 | */ | |
198 | ilinit(unit) | |
199 | int unit; | |
200 | { | |
201 | register struct il_softc *is = &il_softc[unit]; | |
202 | register struct uba_device *ui = ilinfo[unit]; | |
203 | register struct ildevice *addr; | |
8ae4cebd | 204 | register struct ifnet *ifp = &is->is_if; |
1cd789fb | 205 | int s; |
8ae4cebd | 206 | |
7f0e1e06 MK |
207 | /* not yet, if address still unknown */ |
208 | if (ifp->if_addrlist == (struct ifaddr *)0) | |
8ae4cebd | 209 | return; |
0f487295 | 210 | if (is->is_flags & ILF_RUNNING) |
f0d51478 | 211 | return; |
0f487295 MK |
212 | |
213 | if ((ifp->if_flags & IFF_RUNNING) == 0) { | |
214 | if (if_ubainit(&is->is_ifuba, ui->ui_ubanum, | |
215 | sizeof (struct il_rheader), (int)btoc(ETHERMTU)) == 0) { | |
216 | printf("il%d: can't initialize\n", unit); | |
217 | is->is_if.if_flags &= ~IFF_UP; | |
218 | return; | |
219 | } | |
220 | is->is_ubaddr = uballoc(ui->ui_ubanum, (caddr_t)&is->is_stats, | |
221 | sizeof (struct il_stats), 0); | |
f0d51478 | 222 | } |
84afd52f MK |
223 | ifp->if_watchdog = ilwatch; |
224 | is->is_scaninterval = ILWATCHINTERVAL; | |
225 | ifp->if_timer = is->is_scaninterval; | |
f0d51478 BF |
226 | addr = (struct ildevice *)ui->ui_addr; |
227 | ||
4fce3bf9 SL |
228 | /* |
229 | * Turn off source address insertion (it's faster this way), | |
e99cc7c6 SL |
230 | * and set board online. Former doesn't work if board is |
231 | * already online (happens on ubareset), so we put it offline | |
232 | * first. | |
4fce3bf9 SL |
233 | */ |
234 | s = splimp(); | |
0f487295 | 235 | addr->il_csr = ILC_RESET; |
d4ce9212 | 236 | if (ilwait(ui, "hardware diag")) { |
0f487295 MK |
237 | is->is_if.if_flags &= ~IFF_UP; |
238 | splx(s); | |
239 | return; | |
240 | } | |
e99cc7c6 | 241 | addr->il_csr = ILC_CISA; |
4fce3bf9 SL |
242 | while ((addr->il_csr & IL_CDONE) == 0) |
243 | ; | |
35871340 KS |
244 | /* |
245 | * If we must reprogram this board's physical ethernet | |
246 | * address (as for secondary XNS interfaces), we do so | |
247 | * before putting it on line, and starting receive requests. | |
248 | * If you try this on an older 1010 board, it will total | |
249 | * wedge the board. | |
250 | */ | |
251 | if (is->is_flags & ILF_SETADDR) { | |
252 | bcopy((caddr_t)is->is_addr, (caddr_t)&is->is_stats, | |
253 | sizeof is->is_addr); | |
254 | addr->il_bar = is->is_ubaddr & 0xffff; | |
255 | addr->il_bcr = sizeof is->is_addr; | |
256 | addr->il_csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_LDPA; | |
257 | if (ilwait(ui, "setaddr")) | |
258 | return; | |
259 | addr->il_bar = is->is_ubaddr & 0xffff; | |
260 | addr->il_bcr = sizeof (struct il_stats); | |
261 | addr->il_csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_STAT; | |
262 | if (ilwait(ui, "verifying setaddr")) | |
263 | return; | |
264 | if (bcmp((caddr_t)is->is_stats.ils_addr, (caddr_t)is->is_addr, | |
265 | sizeof (is->is_addr)) != 0) { | |
266 | printf("il%d: setaddr didn't work\n", ui->ui_unit); | |
267 | return; | |
268 | } | |
269 | } | |
f0d51478 BF |
270 | /* |
271 | * Set board online. | |
272 | * Hang receive buffer and start any pending | |
273 | * writes by faking a transmit complete. | |
0f487295 | 274 | * Receive bcr is not a multiple of 8 so buffer |
f0d51478 BF |
275 | * chaining can't happen. |
276 | */ | |
f0d51478 | 277 | addr->il_csr = ILC_ONLINE; |
caf0e371 SL |
278 | while ((addr->il_csr & IL_CDONE) == 0) |
279 | ; | |
f0d51478 | 280 | addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff; |
3fa8d9bb | 281 | addr->il_bcr = sizeof(struct il_rheader) + ETHERMTU + 6; |
8d38dbc2 | 282 | addr->il_csr = |
1cd789fb | 283 | ((is->is_ifuba.ifu_r.ifrw_info >> 2) & IL_EUA)|ILC_RCV|IL_RIE; |
caf0e371 SL |
284 | while ((addr->il_csr & IL_CDONE) == 0) |
285 | ; | |
8d38dbc2 | 286 | is->is_flags = ILF_OACTIVE; |
7f0e1e06 | 287 | is->is_if.if_flags |= IFF_RUNNING; |
0f487295 | 288 | is->is_flags |= ILF_RUNNING; |
8d38dbc2 | 289 | is->is_lastcmd = 0; |
f0d51478 BF |
290 | ilcint(unit); |
291 | splx(s); | |
f0d51478 BF |
292 | } |
293 | ||
294 | /* | |
295 | * Start output on interface. | |
296 | * Get another datagram to send off of the interface queue, | |
297 | * and map it to the interface before starting the output. | |
298 | */ | |
299 | ilstart(dev) | |
300 | dev_t dev; | |
301 | { | |
4fce3bf9 | 302 | int unit = ILUNIT(dev), len; |
f0d51478 BF |
303 | struct uba_device *ui = ilinfo[unit]; |
304 | register struct il_softc *is = &il_softc[unit]; | |
305 | register struct ildevice *addr; | |
f0d51478 | 306 | struct mbuf *m; |
caf0e371 | 307 | short csr; |
f0d51478 | 308 | |
f0d51478 | 309 | IF_DEQUEUE(&is->is_if.if_snd, m); |
8d38dbc2 SL |
310 | addr = (struct ildevice *)ui->ui_addr; |
311 | if (m == 0) { | |
312 | if ((is->is_flags & ILF_STATPENDING) == 0) | |
313 | return; | |
4fce3bf9 | 314 | addr->il_bar = is->is_ubaddr & 0xffff; |
8d38dbc2 SL |
315 | addr->il_bcr = sizeof (struct il_stats); |
316 | csr = ((is->is_ubaddr >> 2) & IL_EUA)|ILC_STAT|IL_RIE|IL_CIE; | |
317 | is->is_flags &= ~ILF_STATPENDING; | |
318 | goto startcmd; | |
319 | } | |
f0d51478 | 320 | len = if_wubaput(&is->is_ifuba, m); |
4fce3bf9 SL |
321 | /* |
322 | * Ensure minimum packet length. | |
323 | * This makes the safe assumtion that there are no virtual holes | |
324 | * after the data. | |
325 | * For security, it might be wise to zero out the added bytes, | |
326 | * but we're mainly interested in speed at the moment. | |
327 | */ | |
3fa8d9bb SL |
328 | if (len - sizeof(struct ether_header) < ETHERMIN) |
329 | len = ETHERMIN + sizeof(struct ether_header); | |
f0d51478 BF |
330 | if (is->is_ifuba.ifu_flags & UBA_NEEDBDP) |
331 | UBAPURGE(is->is_ifuba.ifu_uba, is->is_ifuba.ifu_w.ifrw_bdp); | |
f0d51478 BF |
332 | addr->il_bar = is->is_ifuba.ifu_w.ifrw_info & 0xffff; |
333 | addr->il_bcr = len; | |
8d38dbc2 SL |
334 | csr = |
335 | ((is->is_ifuba.ifu_w.ifrw_info >> 2) & IL_EUA)|ILC_XMIT|IL_CIE|IL_RIE; | |
336 | ||
337 | startcmd: | |
338 | is->is_lastcmd = csr & IL_CMD; | |
caf0e371 | 339 | addr->il_csr = csr; |
8d38dbc2 | 340 | is->is_flags |= ILF_OACTIVE; |
f0d51478 BF |
341 | } |
342 | ||
343 | /* | |
344 | * Command done interrupt. | |
f0d51478 BF |
345 | */ |
346 | ilcint(unit) | |
347 | int unit; | |
348 | { | |
f0d51478 | 349 | register struct il_softc *is = &il_softc[unit]; |
caf0e371 | 350 | struct uba_device *ui = ilinfo[unit]; |
f0d51478 | 351 | register struct ildevice *addr = (struct ildevice *)ui->ui_addr; |
5565eee9 | 352 | short csr; |
f0d51478 | 353 | |
8d38dbc2 | 354 | if ((is->is_flags & ILF_OACTIVE) == 0) { |
caf0e371 | 355 | printf("il%d: stray xmit interrupt, csr=%b\n", unit, |
8d38dbc2 | 356 | addr->il_csr, IL_BITS); |
f0d51478 BF |
357 | return; |
358 | } | |
caf0e371 | 359 | |
5565eee9 | 360 | csr = addr->il_csr; |
f0d51478 | 361 | /* |
8d38dbc2 SL |
362 | * Hang receive buffer if it couldn't |
363 | * be done earlier (in ilrint). | |
f0d51478 | 364 | */ |
8d38dbc2 | 365 | if (is->is_flags & ILF_RCVPENDING) { |
0f487295 MK |
366 | int s; |
367 | ||
f0d51478 | 368 | addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff; |
3fa8d9bb | 369 | addr->il_bcr = sizeof(struct il_rheader) + ETHERMTU + 6; |
8d38dbc2 SL |
370 | addr->il_csr = |
371 | ((is->is_ifuba.ifu_r.ifrw_info >> 2) & IL_EUA)|ILC_RCV|IL_RIE; | |
0f487295 | 372 | s = splhigh(); |
caf0e371 SL |
373 | while ((addr->il_csr & IL_CDONE) == 0) |
374 | ; | |
0f487295 | 375 | splx(s); |
8d38dbc2 SL |
376 | is->is_flags &= ~ILF_RCVPENDING; |
377 | } | |
378 | is->is_flags &= ~ILF_OACTIVE; | |
5565eee9 | 379 | csr &= IL_STATUS; |
8d38dbc2 SL |
380 | switch (is->is_lastcmd) { |
381 | ||
382 | case ILC_XMIT: | |
383 | is->is_if.if_opackets++; | |
5565eee9 | 384 | if (csr > ILERR_RETRIES) |
8d38dbc2 SL |
385 | is->is_if.if_oerrors++; |
386 | break; | |
387 | ||
388 | case ILC_STAT: | |
5565eee9 | 389 | if (csr == ILERR_SUCCESS) |
8d38dbc2 SL |
390 | iltotal(is); |
391 | break; | |
f0d51478 BF |
392 | } |
393 | if (is->is_ifuba.ifu_xtofree) { | |
394 | m_freem(is->is_ifuba.ifu_xtofree); | |
395 | is->is_ifuba.ifu_xtofree = 0; | |
396 | } | |
8d38dbc2 | 397 | ilstart(unit); |
f0d51478 BF |
398 | } |
399 | ||
400 | /* | |
401 | * Ethernet interface receiver interrupt. | |
402 | * If input error just drop packet. | |
403 | * Otherwise purge input buffered data path and examine | |
404 | * packet to determine type. If can't determine length | |
405 | * from type, then have to drop packet. Othewise decapsulate | |
406 | * packet based on type and pass to type specific higher-level | |
407 | * input routine. | |
408 | */ | |
409 | ilrint(unit) | |
410 | int unit; | |
411 | { | |
412 | register struct il_softc *is = &il_softc[unit]; | |
413 | struct ildevice *addr = (struct ildevice *)ilinfo[unit]->ui_addr; | |
414 | register struct il_rheader *il; | |
415 | struct mbuf *m; | |
56739ded | 416 | int len, off, resid, s; |
f0d51478 BF |
417 | register struct ifqueue *inq; |
418 | ||
f0d51478 BF |
419 | is->is_if.if_ipackets++; |
420 | if (is->is_ifuba.ifu_flags & UBA_NEEDBDP) | |
421 | UBAPURGE(is->is_ifuba.ifu_uba, is->is_ifuba.ifu_r.ifrw_bdp); | |
422 | il = (struct il_rheader *)(is->is_ifuba.ifu_r.ifrw_addr); | |
423 | len = il->ilr_length - sizeof(struct il_rheader); | |
3fa8d9bb SL |
424 | if ((il->ilr_status&(ILFSTAT_A|ILFSTAT_C)) || len < 46 || |
425 | len > ETHERMTU) { | |
f0d51478 BF |
426 | is->is_if.if_ierrors++; |
427 | #ifdef notdef | |
428 | if (is->is_if.if_ierrors % 100 == 0) | |
429 | printf("il%d: += 100 input errors\n", unit); | |
430 | #endif | |
f0d51478 BF |
431 | goto setup; |
432 | } | |
433 | ||
434 | /* | |
7f0e1e06 | 435 | * Deal with trailer protocol: if type is trailer type |
f0d51478 BF |
436 | * get true type from first 16-bit word past data. |
437 | * Remember that type was trailer by setting off. | |
438 | */ | |
3fa8d9bb | 439 | il->ilr_type = ntohs((u_short)il->ilr_type); |
f0d51478 | 440 | #define ildataaddr(il, off, type) ((type)(((caddr_t)((il)+1)+(off)))) |
7f0e1e06 MK |
441 | if (il->ilr_type >= ETHERTYPE_TRAIL && |
442 | il->ilr_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { | |
443 | off = (il->ilr_type - ETHERTYPE_TRAIL) * 512; | |
3fa8d9bb | 444 | if (off >= ETHERMTU) |
f0d51478 | 445 | goto setup; /* sanity */ |
3fa8d9bb SL |
446 | il->ilr_type = ntohs(*ildataaddr(il, off, u_short *)); |
447 | resid = ntohs(*(ildataaddr(il, off+2, u_short *))); | |
f0d51478 BF |
448 | if (off + resid > len) |
449 | goto setup; /* sanity */ | |
450 | len = off + resid; | |
451 | } else | |
452 | off = 0; | |
453 | if (len == 0) | |
454 | goto setup; | |
455 | ||
456 | /* | |
457 | * Pull packet off interface. Off is nonzero if packet | |
458 | * has trailing header; ilget will then force this header | |
459 | * information to be at the front, but we still have to drop | |
460 | * the type and length which are at the front of any trailer data. | |
461 | */ | |
67eb6277 | 462 | m = if_rubaget(&is->is_ifuba, len, off, &is->is_if); |
f0d51478 BF |
463 | if (m == 0) |
464 | goto setup; | |
465 | if (off) { | |
67eb6277 MK |
466 | struct ifnet *ifp; |
467 | ||
468 | ifp = *(mtod(m, struct ifnet **)); | |
f0d51478 BF |
469 | m->m_off += 2 * sizeof (u_short); |
470 | m->m_len -= 2 * sizeof (u_short); | |
67eb6277 | 471 | *(mtod(m, struct ifnet **)) = ifp; |
f0d51478 BF |
472 | } |
473 | switch (il->ilr_type) { | |
474 | ||
475 | #ifdef INET | |
7f0e1e06 | 476 | case ETHERTYPE_IP: |
f0d51478 BF |
477 | schednetisr(NETISR_IP); |
478 | inq = &ipintrq; | |
479 | break; | |
8ae4cebd | 480 | |
7f0e1e06 | 481 | case ETHERTYPE_ARP: |
8ae4cebd | 482 | arpinput(&is->is_ac, m); |
27bcf34c | 483 | goto setup; |
cd4baa58 KS |
484 | #endif |
485 | #ifdef NS | |
486 | case ETHERTYPE_NS: | |
487 | schednetisr(NETISR_NS); | |
488 | inq = &nsintrq; | |
489 | break; | |
490 | ||
f0d51478 BF |
491 | #endif |
492 | default: | |
493 | m_freem(m); | |
494 | goto setup; | |
495 | } | |
496 | ||
56739ded | 497 | s = splimp(); |
f0d51478 BF |
498 | if (IF_QFULL(inq)) { |
499 | IF_DROP(inq); | |
500 | m_freem(m); | |
56739ded CL |
501 | } else |
502 | IF_ENQUEUE(inq, m); | |
503 | splx(s); | |
f0d51478 BF |
504 | |
505 | setup: | |
506 | /* | |
507 | * Reset for next packet if possible. | |
508 | * If waiting for transmit command completion, set flag | |
509 | * and wait until command completes. | |
510 | */ | |
8d38dbc2 SL |
511 | if (is->is_flags & ILF_OACTIVE) { |
512 | is->is_flags |= ILF_RCVPENDING; | |
caf0e371 SL |
513 | return; |
514 | } | |
515 | addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff; | |
3fa8d9bb | 516 | addr->il_bcr = sizeof(struct il_rheader) + ETHERMTU + 6; |
8d38dbc2 SL |
517 | addr->il_csr = |
518 | ((is->is_ifuba.ifu_r.ifrw_info >> 2) & IL_EUA)|ILC_RCV|IL_RIE; | |
0f487295 | 519 | s = splhigh(); |
caf0e371 SL |
520 | while ((addr->il_csr & IL_CDONE) == 0) |
521 | ; | |
0f487295 | 522 | splx(s); |
f0d51478 BF |
523 | } |
524 | ||
525 | /* | |
526 | * Ethernet output routine. | |
527 | * Encapsulate a packet of type family for the local net. | |
528 | * Use trailer local net encapsulation if enough data in first | |
529 | * packet leaves a multiple of 512 bytes of data in remainder. | |
530 | */ | |
531 | iloutput(ifp, m0, dst) | |
532 | struct ifnet *ifp; | |
533 | struct mbuf *m0; | |
534 | struct sockaddr *dst; | |
535 | { | |
8ae4cebd | 536 | int type, s, error; |
7f0e1e06 | 537 | u_char edst[6]; |
8ae4cebd | 538 | struct in_addr idst; |
f0d51478 BF |
539 | register struct il_softc *is = &il_softc[ifp->if_unit]; |
540 | register struct mbuf *m = m0; | |
3fa8d9bb | 541 | register struct ether_header *il; |
4fce3bf9 | 542 | register int off; |
d4ce9212 | 543 | int usetrailers; |
f0d51478 | 544 | |
0f487295 MK |
545 | if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { |
546 | error = ENETDOWN; | |
547 | goto bad; | |
548 | } | |
f0d51478 BF |
549 | switch (dst->sa_family) { |
550 | ||
551 | #ifdef INET | |
552 | case AF_INET: | |
8ae4cebd | 553 | idst = ((struct sockaddr_in *)dst)->sin_addr; |
d4ce9212 | 554 | if (!arpresolve(&is->is_ac, m, &idst, edst, &usetrailers)) |
8ae4cebd | 555 | return (0); /* if not yet resolved */ |
f0d51478 | 556 | off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; |
d4ce9212 | 557 | if (usetrailers && off > 0 && (off & 0x1ff) == 0 && |
f0d51478 | 558 | m->m_off >= MMINOFF + 2 * sizeof (u_short)) { |
7f0e1e06 | 559 | type = ETHERTYPE_TRAIL + (off>>9); |
f0d51478 BF |
560 | m->m_off -= 2 * sizeof (u_short); |
561 | m->m_len += 2 * sizeof (u_short); | |
7f0e1e06 | 562 | *mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP); |
3fa8d9bb | 563 | *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len); |
f0d51478 BF |
564 | goto gottrailertype; |
565 | } | |
7f0e1e06 | 566 | type = ETHERTYPE_IP; |
f0d51478 BF |
567 | off = 0; |
568 | goto gottype; | |
569 | #endif | |
cd4baa58 KS |
570 | #ifdef NS |
571 | case AF_NS: | |
572 | type = ETHERTYPE_NS; | |
573 | bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host), | |
574 | (caddr_t)edst, sizeof (edst)); | |
575 | off = 0; | |
576 | goto gottype; | |
577 | #endif | |
f0d51478 | 578 | |
8ae4cebd SL |
579 | case AF_UNSPEC: |
580 | il = (struct ether_header *)dst->sa_data; | |
7f0e1e06 | 581 | bcopy((caddr_t)il->ether_dhost, (caddr_t)edst, sizeof (edst)); |
8ae4cebd SL |
582 | type = il->ether_type; |
583 | goto gottype; | |
584 | ||
f0d51478 BF |
585 | default: |
586 | printf("il%d: can't handle af%d\n", ifp->if_unit, | |
587 | dst->sa_family); | |
588 | error = EAFNOSUPPORT; | |
589 | goto bad; | |
590 | } | |
591 | ||
592 | gottrailertype: | |
593 | /* | |
594 | * Packet to be sent as trailer: move first packet | |
595 | * (control information) to end of chain. | |
596 | */ | |
597 | while (m->m_next) | |
598 | m = m->m_next; | |
599 | m->m_next = m0; | |
600 | m = m0->m_next; | |
601 | m0->m_next = 0; | |
602 | m0 = m; | |
603 | ||
604 | gottype: | |
605 | /* | |
606 | * Add local net header. If no space in first mbuf, | |
607 | * allocate another. | |
608 | */ | |
609 | if (m->m_off > MMAXOFF || | |
3fa8d9bb | 610 | MMINOFF + sizeof (struct ether_header) > m->m_off) { |
cce93e4b | 611 | m = m_get(M_DONTWAIT, MT_HEADER); |
f0d51478 BF |
612 | if (m == 0) { |
613 | error = ENOBUFS; | |
614 | goto bad; | |
615 | } | |
616 | m->m_next = m0; | |
617 | m->m_off = MMINOFF; | |
3fa8d9bb | 618 | m->m_len = sizeof (struct ether_header); |
f0d51478 | 619 | } else { |
3fa8d9bb SL |
620 | m->m_off -= sizeof (struct ether_header); |
621 | m->m_len += sizeof (struct ether_header); | |
f0d51478 | 622 | } |
3fa8d9bb | 623 | il = mtod(m, struct ether_header *); |
3fa8d9bb | 624 | il->ether_type = htons((u_short)type); |
7f0e1e06 MK |
625 | bcopy((caddr_t)edst, (caddr_t)il->ether_dhost, sizeof (edst)); |
626 | bcopy((caddr_t)is->is_addr, (caddr_t)il->ether_shost, | |
627 | sizeof(il->ether_shost)); | |
f0d51478 BF |
628 | |
629 | /* | |
630 | * Queue message on interface, and start output if interface | |
631 | * not yet active. | |
632 | */ | |
633 | s = splimp(); | |
634 | if (IF_QFULL(&ifp->if_snd)) { | |
635 | IF_DROP(&ifp->if_snd); | |
caf0e371 SL |
636 | splx(s); |
637 | m_freem(m); | |
638 | return (ENOBUFS); | |
f0d51478 BF |
639 | } |
640 | IF_ENQUEUE(&ifp->if_snd, m); | |
8d38dbc2 | 641 | if ((is->is_flags & ILF_OACTIVE) == 0) |
f0d51478 BF |
642 | ilstart(ifp->if_unit); |
643 | splx(s); | |
644 | return (0); | |
caf0e371 | 645 | |
f0d51478 BF |
646 | bad: |
647 | m_freem(m0); | |
caf0e371 | 648 | return (error); |
f0d51478 | 649 | } |
8d38dbc2 SL |
650 | |
651 | /* | |
652 | * Watchdog routine, request statistics from board. | |
653 | */ | |
654 | ilwatch(unit) | |
655 | int unit; | |
656 | { | |
657 | register struct il_softc *is = &il_softc[unit]; | |
658 | register struct ifnet *ifp = &is->is_if; | |
659 | int s; | |
660 | ||
661 | if (is->is_flags & ILF_STATPENDING) { | |
662 | ifp->if_timer = is->is_scaninterval; | |
663 | return; | |
664 | } | |
665 | s = splimp(); | |
666 | is->is_flags |= ILF_STATPENDING; | |
667 | if ((is->is_flags & ILF_OACTIVE) == 0) | |
668 | ilstart(ifp->if_unit); | |
669 | splx(s); | |
670 | ifp->if_timer = is->is_scaninterval; | |
671 | } | |
672 | ||
673 | /* | |
674 | * Total up the on-board statistics. | |
675 | */ | |
676 | iltotal(is) | |
677 | register struct il_softc *is; | |
678 | { | |
679 | register u_short *interval, *sum, *end; | |
680 | ||
681 | interval = &is->is_stats.ils_frames; | |
682 | sum = &is->is_sum.ils_frames; | |
683 | end = is->is_sum.ils_fill2; | |
684 | while (sum < end) | |
685 | *sum++ += *interval++; | |
686 | is->is_if.if_collisions = is->is_sum.ils_collis; | |
f883ae2e KS |
687 | if ((is->is_flags & ILF_SETADDR) && |
688 | (bcmp((caddr_t)is->is_stats.ils_addr, (caddr_t)is->is_addr, | |
689 | sizeof (is->is_addr)) != 0)) { | |
690 | log(LOG_ERR, "il%d: physaddr reverted\n", is->is_if.if_unit); | |
691 | is->is_flags &= ~ILF_RUNNING; | |
692 | ilinit(is->is_if.if_unit); | |
693 | } | |
8d38dbc2 | 694 | } |
1cd789fb SL |
695 | |
696 | /* | |
697 | * Process an ioctl request. | |
698 | */ | |
699 | ilioctl(ifp, cmd, data) | |
700 | register struct ifnet *ifp; | |
701 | int cmd; | |
702 | caddr_t data; | |
703 | { | |
7f0e1e06 | 704 | register struct ifaddr *ifa = (struct ifaddr *)data; |
0f487295 | 705 | register struct il_softc *is = &il_softc[ifp->if_unit]; |
1cd789fb SL |
706 | int s = splimp(), error = 0; |
707 | ||
708 | switch (cmd) { | |
709 | ||
710 | case SIOCSIFADDR: | |
7f0e1e06 | 711 | ifp->if_flags |= IFF_UP; |
1cd789fb | 712 | ilinit(ifp->if_unit); |
7f0e1e06 MK |
713 | |
714 | switch (ifa->ifa_addr.sa_family) { | |
cd4baa58 | 715 | #ifdef INET |
7f0e1e06 MK |
716 | case AF_INET: |
717 | ((struct arpcom *)ifp)->ac_ipaddr = | |
718 | IA_SIN(ifa)->sin_addr; | |
719 | arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); | |
720 | break; | |
cd4baa58 KS |
721 | #endif |
722 | #ifdef NS | |
723 | case AF_NS: | |
d4ce9212 MK |
724 | { |
725 | register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); | |
726 | ||
727 | if (ns_nullhost(*ina)) { | |
728 | ina->x_host = * (union ns_host *) | |
729 | (il_softc[ifp->if_unit].is_addr); | |
730 | } else { | |
d4ce9212 | 731 | il_setaddr(ina->x_host.c_host, ifp->if_unit); |
35871340 | 732 | return (0); |
d4ce9212 | 733 | } |
cd4baa58 | 734 | break; |
d4ce9212 | 735 | } |
cd4baa58 | 736 | #endif |
7f0e1e06 | 737 | } |
1cd789fb SL |
738 | break; |
739 | ||
0f487295 MK |
740 | case SIOCSIFFLAGS: |
741 | if ((ifp->if_flags & IFF_UP) == 0 && | |
742 | is->is_flags & ILF_RUNNING) { | |
743 | ((struct ildevice *) | |
744 | (ilinfo[ifp->if_unit]->ui_addr))->il_csr = ILC_RESET; | |
745 | is->is_flags &= ~ILF_RUNNING; | |
746 | } else if (ifp->if_flags & IFF_UP && | |
747 | (is->is_flags & ILF_RUNNING) == 0) | |
748 | ilinit(ifp->if_unit); | |
749 | break; | |
750 | ||
1cd789fb SL |
751 | default: |
752 | error = EINVAL; | |
753 | } | |
754 | splx(s); | |
755 | return (error); | |
756 | } | |
d4ce9212 MK |
757 | |
758 | /* | |
759 | * set ethernet address for unit | |
760 | */ | |
761 | il_setaddr(physaddr, unit) | |
762 | u_char *physaddr; | |
763 | int unit; | |
764 | { | |
765 | register struct il_softc *is = &il_softc[unit]; | |
d4ce9212 MK |
766 | |
767 | if (! (is->is_flags & ILF_RUNNING)) | |
768 | return; | |
769 | ||
35871340 KS |
770 | bcopy((caddr_t)physaddr, (caddr_t)is->is_addr, sizeof is->is_addr); |
771 | is->is_flags &= ~ILF_RUNNING; | |
772 | is->is_flags |= ILF_SETADDR; | |
773 | ilinit(unit); | |
d4ce9212 | 774 | } |
9a0b0c74 | 775 | #endif |