Commit | Line | Data |
---|---|---|
9f4e6350 RG |
1 | /*- |
2 | * Copyright (c) 1992, 1993, University of Vermont and State | |
3 | * Agricultural College. | |
4 | * Copyright (c) 1992, 1993, Garrett A. Wollman. | |
5 | * | |
6 | * Portions: | |
7 | * Copyright (c) 1990, 1991, William F. Jolitz | |
8 | * Copyright (c) 1990, The Regents of the University of California | |
9 | * | |
10 | * All rights reserved. | |
11 | * | |
12 | * Redistribution and use in source and binary forms, with or without | |
13 | * modification, are permitted provided that the following conditions | |
14 | * are met: | |
15 | * 1. Redistributions of source code must retain the above copyright | |
16 | * notice, this list of conditions and the following disclaimer. | |
17 | * 2. Redistributions in binary form must reproduce the above copyright | |
18 | * notice, this list of conditions and the following disclaimer in the | |
19 | * documentation and/or other materials provided with the distribution. | |
20 | * 3. All advertising materials mentioning features or use of this software | |
21 | * must display the following acknowledgement: | |
22 | * This product includes software developed by the University of | |
23 | * Vermont and State Agricultural College and Garrett A. Wollman, | |
24 | * by William F. Jolitz, by the University of California, | |
25 | * Berkeley, by Larwence Berkeley Laboratory, and its contributors. | |
26 | * 4. Neither the names of the Universities nor the names of the authors | |
27 | * may be used to endorse or promote products derived from this software | |
28 | * without specific prior written permission. | |
29 | * | |
30 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
31 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
32 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
33 | * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR AUTHORS BE LIABLE | |
34 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
35 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
36 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
37 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
38 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
39 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
40 | * SUCH DAMAGE. | |
41 | * | |
a942b037 | 42 | * $Id: if_ie.c,v 1.2 1993/11/25 01:31:36 wollman Exp $ |
9f4e6350 RG |
43 | */ |
44 | ||
45 | /* | |
46 | * Intel 82586 Ethernet chip | |
47 | * Register, bit, and structure definitions. | |
48 | * | |
49 | * Written by GAW with reference to the Clarkson Packet Driver code for this | |
50 | * chip written by Russ Nelson and others. | |
51 | * | |
52 | * BPF support code stolen directly from hpdev/if_le.c, supplied with | |
53 | * tcpdump. | |
54 | */ | |
55 | ||
56 | /* | |
57 | * The i82586 is a very versatile chip, found in many implementations. | |
58 | * Programming this chip is mostly the same, but certain details differ | |
59 | * from card to card. This driver is written so that different cards | |
60 | * can be automatically detected at run-time. Currently, only the | |
61 | * AT&T EN100/StarLAN 10 series are supported. | |
62 | */ | |
63 | ||
64 | /* | |
65 | Mode of operation: | |
66 | ||
67 | We run the 82586 in a standard Ethernet mode. We keep NFRAMES received | |
68 | frame descriptors around for the receiver to use, and NBUFFS associated | |
69 | receive buffer descriptors, both in a circular list. Whenever a frame is | |
70 | received, we rotate both lists as necessary. (The 586 treats both lists | |
71 | as a simple queue.) We also keep a transmit command around so that packets | |
72 | can be sent off quickly. | |
73 | ||
74 | We configure the adapter in AL-LOC = 1 mode, which means that the | |
75 | Ethernet/802.3 MAC header is placed at the beginning of the receive buffer | |
76 | rather than being split off into various fields in the RFD. This also | |
77 | means that we must include this header in the transmit buffer as well. | |
78 | ||
79 | By convention, all transmit commands, and only transmit commands, shall | |
80 | have the I (IE_CMD_INTR) bit set in the command. This way, when an | |
81 | interrupt arrives at ieintr(), it is immediately possible to tell | |
82 | what precisely caused it. ANY OTHER command-sending routines should | |
83 | run at splimp(), and should post an acknowledgement to every interrupt | |
84 | they generate. | |
85 | ||
86 | The 82586 has a 24-bit address space internally, and the adaptor's | |
87 | memory is located at the top of this region. However, the value we are | |
88 | given in configuration is normally the *bottom* of the adaptor RAM. So, | |
89 | we must go through a few gyrations to come up with a kernel virtual address | |
90 | which represents the actual beginning of the 586 address space. First, | |
91 | we autosize the RAM by running through several possible sizes and trying | |
92 | to initialize the adapter under the assumption that the selected size | |
93 | is correct. Then, knowing the correct RAM size, we set up our pointers | |
94 | in ie_softc[unit]. `iomem' represents the computed base of the 586 | |
95 | address space. `iomembot' represents the actual configured base | |
96 | of adapter RAM. Finally, `iosize' represents the calculated size | |
97 | of 586 RAM. Then, when laying out commands, we use the interval | |
98 | [iomembot, iomembot + iosize); to make 24-pointers, we subtract | |
99 | iomem, and to make 16-pointers, we subtract iomem and and with 0xffff. | |
100 | ||
101 | */ | |
102 | ||
103 | #include "ie.h" | |
104 | #if NIE > 0 | |
105 | ||
106 | #include "param.h" | |
107 | #include "systm.h" | |
108 | #include "mbuf.h" | |
9f4e6350 RG |
109 | #include "protosw.h" |
110 | #include "socket.h" | |
111 | #include "ioctl.h" | |
112 | #include "errno.h" | |
113 | #include "syslog.h" | |
114 | ||
115 | #include "net/if.h" | |
116 | #include "net/if_types.h" | |
117 | #include "net/if_dl.h" | |
9f4e6350 RG |
118 | #include "net/route.h" |
119 | ||
120 | #include "bpfilter.h" | |
121 | ||
122 | #ifdef INET | |
123 | #include "netinet/in.h" | |
124 | #include "netinet/in_systm.h" | |
125 | #include "netinet/in_var.h" | |
126 | #include "netinet/ip.h" | |
127 | #include "netinet/if_ether.h" | |
128 | #endif | |
129 | ||
130 | #ifdef NS | |
131 | #include "netns/ns.h" | |
132 | #include "netns/ns_if.h" | |
133 | #endif | |
134 | ||
135 | #include "i386/isa/isa.h" | |
9f4e6350 RG |
136 | #include "i386/isa/isa_device.h" |
137 | #include "i386/isa/ic/i82586.h" | |
138 | #include "i386/isa/if_iereg.h" | |
139 | #include "i386/isa/icu.h" | |
140 | ||
141 | #include "vm/vm.h" | |
142 | ||
143 | #if NBPFILTER > 0 | |
144 | #include "net/bpf.h" | |
145 | #include "net/bpfdesc.h" | |
146 | #endif | |
147 | ||
148 | #if (NBPFILTER > 0) || defined(MULTICAST) | |
149 | #define FILTER | |
150 | static struct mbuf *last_not_for_us; | |
151 | #endif | |
152 | ||
153 | #ifdef DEBUG | |
154 | #define IED_RINT 1 | |
155 | #define IED_TINT 2 | |
156 | #define IED_RNR 4 | |
157 | #define IED_CNA 8 | |
158 | #define IED_READFRAME 16 | |
159 | int ie_debug = IED_RNR; | |
160 | #endif | |
161 | ||
162 | #ifndef ETHERMINLEN | |
163 | #define ETHERMINLEN 60 | |
164 | #endif | |
165 | ||
166 | #define IE_BUF_LEN 1512 /* length of transmit buffer */ | |
167 | ||
168 | /* Forward declaration */ | |
169 | struct ie_softc; | |
170 | ||
4c45483e GW |
171 | static int ieprobe(struct isa_device *dvp); |
172 | static int ieattach(struct isa_device *dvp); | |
173 | static void ieinit(int unit); | |
174 | static int ieioctl(struct ifnet *ifp, int command, caddr_t data); | |
175 | static void iestart(struct ifnet *ifp); | |
9f4e6350 RG |
176 | static void sl_reset_586(int unit); |
177 | static void sl_chan_attn(int unit); | |
4c45483e | 178 | static void iereset(int unit, int dummy); |
9f4e6350 RG |
179 | static void ie_readframe(int unit, struct ie_softc *ie, int bufno); |
180 | static void ie_drop_packet_buffer(int unit, struct ie_softc *ie); | |
181 | static void sl_read_ether(int unit, unsigned char addr[6]); | |
182 | static void find_ie_mem_size(int unit); | |
183 | static int command_and_wait(int unit, int command, void volatile *pcmd, int); | |
184 | static int ierint(int unit, struct ie_softc *ie); | |
185 | static int ietint(int unit, struct ie_softc *ie); | |
186 | static int iernr(int unit, struct ie_softc *ie); | |
187 | static void start_receiver(int unit); | |
188 | static int ieget(int, struct ie_softc *, struct mbuf **, | |
189 | struct ether_header *, int *); | |
190 | static caddr_t setup_rfa(caddr_t ptr, struct ie_softc *ie); | |
191 | static int mc_setup(int, caddr_t, volatile struct ie_sys_ctl_block *); | |
192 | #ifdef MULTICAST | |
193 | static void ie_mc_reset(int unit); | |
194 | #endif | |
195 | ||
196 | #ifdef DEBUG | |
197 | void print_rbd(volatile struct ie_recv_buf_desc *rbd); | |
198 | ||
199 | int in_ierint = 0; | |
200 | int in_ietint = 0; | |
201 | #endif | |
202 | ||
203 | /* | |
204 | * This tells the autoconf code how to set us up. | |
205 | */ | |
206 | struct isa_driver iedriver = { | |
207 | ieprobe, ieattach, "ie", | |
208 | }; | |
209 | ||
210 | enum ie_hardware { | |
211 | IE_STARLAN10, | |
212 | IE_EN100, | |
213 | IE_SLFIBER, | |
214 | IE_UNKNOWN | |
215 | }; | |
216 | ||
217 | const char *ie_hardware_names[] = { | |
218 | "StarLAN 10", | |
219 | "EN100", | |
220 | "StarLAN Fiber", | |
221 | "Unknown" | |
222 | }; | |
223 | ||
224 | /* | |
225 | sizeof(iscp) == 1+1+2+4 == 8 | |
226 | sizeof(scb) == 2+2+2+2+2+2+2+2 == 16 | |
227 | NFRAMES * sizeof(rfd) == NFRAMES*(2+2+2+2+6+6+2+2) == NFRAMES*24 == 384 | |
228 | sizeof(xmit_cmd) == 2+2+2+2+6+2 == 18 | |
229 | sizeof(transmit buffer) == 1512 | |
230 | sizeof(transmit buffer desc) == 8 | |
231 | ----- | |
232 | 1946 | |
233 | ||
234 | NBUFFS * sizeof(rbd) == NBUFFS*(2+2+4+2+2) == NBUFFS*12 | |
235 | NBUFFS * IE_RBUF_SIZE == NBUFFS*256 | |
236 | ||
237 | NBUFFS should be (16384 - 1946) / (256 + 12) == 14438 / 268 == 53 | |
238 | ||
239 | With NBUFFS == 48, this leaves us 1574 bytes for another command or | |
240 | more buffers. Another transmit command would be 18+8+1512 == 1538 | |
241 | ---just barely fits! | |
242 | ||
243 | Obviously all these would have to be reduced for smaller memory sizes. | |
244 | With a larger memory, it would be possible to roughly double the number of | |
245 | both transmit and receive buffers. | |
246 | */ | |
247 | ||
248 | #define NFRAMES 16 /* number of frames to allow for receive */ | |
249 | #define NBUFFS 48 /* number of buffers to allocate */ | |
250 | #define IE_RBUF_SIZE 256 /* size of each buffer, MUST BE POWER OF TWO */ | |
251 | ||
252 | /* | |
253 | * Ethernet status, per interface. | |
254 | */ | |
255 | struct ie_softc { | |
256 | struct arpcom arpcom; | |
257 | void (*ie_reset_586)(int); | |
258 | void (*ie_chan_attn)(int); | |
259 | enum ie_hardware hard_type; | |
260 | int hard_vers; | |
261 | ||
262 | u_short port; | |
263 | caddr_t iomem; | |
264 | caddr_t iomembot; | |
265 | unsigned iosize; | |
266 | ||
267 | int want_mcsetup; | |
268 | int promisc; | |
269 | volatile struct ie_int_sys_conf_ptr *iscp; | |
270 | volatile struct ie_sys_ctl_block *scb; | |
271 | volatile struct ie_recv_frame_desc *rframes[NFRAMES]; | |
272 | volatile struct ie_recv_buf_desc *rbuffs[NBUFFS]; | |
273 | volatile char *cbuffs[NBUFFS]; | |
274 | int rfhead, rftail, rbhead, rbtail; | |
275 | ||
276 | volatile struct ie_xmit_cmd *xmit_cmds[2]; | |
277 | volatile struct ie_xmit_buf *xmit_buffs[2]; | |
278 | int xmit_count; | |
279 | u_char *xmit_cbuffs[2]; | |
280 | ||
281 | struct ie_en_addr mcast_addrs[MAXMCAST + 1]; | |
282 | int mcast_count; | |
283 | ||
284 | #if NBPFILTER > 0 | |
285 | caddr_t ie_bpf; | |
286 | #endif | |
287 | ||
288 | } ie_softc[NIE]; | |
289 | ||
290 | #define MK_24(base, ptr) ((caddr_t)((u_long)ptr - (u_long)base)) | |
291 | #define MK_16(base, ptr) ((u_short)(u_long)MK_24(base, ptr)) | |
292 | ||
293 | #define PORT ie_softc[unit].port | |
294 | #define MEM ie_softc[unit].iomem | |
295 | ||
296 | ||
297 | int ieprobe(dvp) | |
298 | struct isa_device *dvp; | |
299 | { | |
300 | int unit = dvp->id_unit; | |
301 | u_char c; | |
302 | ||
303 | ie_softc[unit].port = dvp->id_iobase; | |
304 | ie_softc[unit].iomembot = dvp->id_maddr; | |
305 | ie_softc[unit].iomem = 0; | |
306 | ||
307 | c = inb(PORT + IEATT_REVISION); | |
308 | switch(SL_BOARD(c)) { | |
309 | case SL10_BOARD: | |
310 | ie_softc[unit].hard_type = IE_STARLAN10; | |
311 | ie_softc[unit].ie_reset_586 = sl_reset_586; | |
312 | ie_softc[unit].ie_chan_attn = sl_chan_attn; | |
313 | break; | |
314 | case EN100_BOARD: | |
315 | ie_softc[unit].hard_type = IE_EN100; | |
316 | ie_softc[unit].ie_reset_586 = sl_reset_586; | |
317 | ie_softc[unit].ie_chan_attn = sl_chan_attn; | |
318 | break; | |
319 | case SLFIBER_BOARD: | |
320 | ie_softc[unit].hard_type = IE_SLFIBER; | |
321 | ie_softc[unit].ie_reset_586 = sl_reset_586; | |
322 | ie_softc[unit].ie_chan_attn = sl_chan_attn; | |
323 | break; | |
324 | ||
325 | /* | |
326 | * Anything else is not recognized or cannot be used. | |
327 | */ | |
328 | default: | |
329 | return 0; | |
330 | } | |
331 | ||
332 | ie_softc[unit].hard_vers = SL_REV(c); | |
333 | ||
334 | /* | |
335 | * Divine memory size on-board the card. Ususally 16k. | |
336 | */ | |
337 | find_ie_mem_size(unit); | |
338 | ||
339 | if(!ie_softc[unit].iosize) { | |
340 | return 0; | |
341 | } | |
342 | ||
343 | dvp->id_msize = ie_softc[unit].iosize; | |
344 | ||
345 | switch(ie_softc[unit].hard_type) { | |
346 | case IE_EN100: | |
347 | case IE_STARLAN10: | |
348 | case IE_SLFIBER: | |
349 | sl_read_ether(unit, ie_softc[unit].arpcom.ac_enaddr); | |
350 | break; | |
351 | ||
352 | default: | |
353 | printf("ie%d: unknown AT&T board type code %d\n", unit, | |
354 | ie_softc[unit].hard_type); | |
355 | return 0; | |
356 | } | |
357 | ||
358 | return 1; | |
359 | } | |
360 | ||
361 | /* | |
362 | * Taken almost exactly from Bill's if_is.c, then modified beyond recognition. | |
363 | */ | |
364 | int | |
365 | ieattach(dvp) | |
366 | struct isa_device *dvp; | |
367 | { | |
368 | int unit = dvp->id_unit; | |
369 | struct ie_softc *ie = &ie_softc[unit]; | |
370 | struct ifnet *ifp = &ie->arpcom.ac_if; | |
371 | ||
372 | ifp->if_unit = unit; | |
373 | ifp->if_name = iedriver.name; | |
374 | ifp->if_mtu = ETHERMTU; | |
375 | printf("<%s R%d> ethernet address %s", | |
376 | ie_hardware_names[ie_softc[unit].hard_type], | |
377 | ie_softc[unit].hard_vers + 1, | |
378 | ether_sprintf(ie->arpcom.ac_enaddr)); | |
379 | ||
380 | ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS; | |
381 | #ifdef MULTICAST | |
382 | ifp->if_flags |= IFF_MULTICAST; | |
383 | #endif /* MULTICAST */ | |
384 | ||
385 | ifp->if_init = ieinit; | |
386 | ifp->if_output = ether_output; | |
387 | ifp->if_start = iestart; | |
388 | ifp->if_ioctl = ieioctl; | |
389 | ifp->if_reset = iereset; | |
390 | ifp->if_type = IFT_ETHER; | |
391 | ifp->if_addrlen = 6; | |
392 | ifp->if_hdrlen = 14; | |
393 | ||
394 | #if NBPFILTER > 0 | |
395 | printf("\n"); | |
396 | bpfattach(&ie_softc[unit].ie_bpf, ifp, DLT_EN10MB, | |
397 | sizeof(struct ether_header)); | |
398 | #endif | |
399 | ||
400 | if_attach(ifp); | |
401 | { | |
402 | struct ifaddr *ifa = ifp->if_addrlist; | |
403 | struct sockaddr_dl *sdl; | |
404 | while(ifa && ifa->ifa_addr && ifa->ifa_addr->sa_family != AF_LINK) | |
405 | ifa = ifa->ifa_next; | |
406 | ||
4c45483e | 407 | if(!ifa || !ifa->ifa_addr) return 1; |
9f4e6350 RG |
408 | |
409 | /* Provide our ether address to the higher layers */ | |
410 | sdl = (struct sockaddr_dl *)ifa->ifa_addr; | |
411 | sdl->sdl_type = IFT_ETHER; | |
412 | sdl->sdl_alen = 6; | |
413 | sdl->sdl_slen = 0; | |
414 | bcopy(ie->arpcom.ac_enaddr, LLADDR(sdl), 6); | |
4c45483e | 415 | return 1; |
9f4e6350 RG |
416 | } |
417 | } | |
418 | ||
419 | /* | |
420 | * What to do upon receipt of an interrupt. | |
421 | */ | |
422 | int ieintr(unit) | |
423 | int unit; | |
424 | { | |
425 | register struct ie_softc *ie = &ie_softc[unit]; | |
426 | register u_short status; | |
427 | ||
428 | status = ie->scb->ie_status; | |
429 | ||
430 | loop: | |
431 | if(status & (IE_ST_RECV | IE_ST_RNR)) { | |
432 | #ifdef DEBUG | |
433 | in_ierint++; | |
434 | if(ie_debug & IED_RINT) | |
435 | printf("ie%d: rint\n", unit); | |
436 | #endif | |
437 | ierint(unit, ie); | |
438 | #ifdef DEBUG | |
439 | in_ierint--; | |
440 | #endif | |
441 | } | |
442 | ||
443 | if(status & IE_ST_DONE) { | |
444 | #ifdef DEBUG | |
445 | in_ietint++; | |
446 | if(ie_debug & IED_TINT) | |
447 | printf("ie%d: tint\n", unit); | |
448 | #endif | |
449 | ietint(unit, ie); | |
450 | #ifdef DEBUG | |
451 | in_ietint--; | |
452 | #endif | |
453 | } | |
454 | ||
455 | if(status & IE_ST_RNR) { | |
456 | #ifdef DEBUG | |
457 | if(ie_debug & IED_RNR) | |
458 | printf("ie%d: rnr\n", unit); | |
459 | #endif | |
460 | iernr(unit, ie); | |
461 | } | |
462 | ||
463 | #ifdef DEBUG | |
464 | if((status & IE_ST_ALLDONE) | |
465 | && (ie_debug & IED_CNA)) | |
466 | printf("ie%d: cna\n", unit); | |
467 | #endif | |
468 | ||
469 | /* Don't ack interrupts which we didn't receive */ | |
470 | ie_ack(ie->scb, IE_ST_WHENCE & status, unit, ie->ie_chan_attn); | |
471 | ||
472 | if((status = ie->scb->ie_status) & IE_ST_WHENCE) | |
473 | goto loop; | |
474 | ||
475 | return unit; | |
476 | } | |
477 | ||
478 | /* | |
479 | * Process a received-frame interrupt. | |
480 | */ | |
481 | static int ierint(unit, ie) | |
482 | int unit; | |
483 | struct ie_softc *ie; | |
484 | { | |
485 | int i, status; | |
486 | static int timesthru = 1024; | |
487 | ||
488 | i = ie->rfhead; | |
489 | while(1) { | |
490 | status = ie->rframes[i]->ie_fd_status; | |
491 | ||
492 | if((status & IE_FD_COMPLETE) && (status & IE_FD_OK)) { | |
493 | ie->arpcom.ac_if.if_ipackets++; | |
494 | if(!--timesthru) { | |
495 | ie->arpcom.ac_if.if_ierrors += ie->scb->ie_err_crc + ie->scb->ie_err_align + | |
496 | ie->scb->ie_err_resource + ie->scb->ie_err_overrun; | |
497 | ie->scb->ie_err_crc = 0; | |
498 | ie->scb->ie_err_align = 0; | |
499 | ie->scb->ie_err_resource = 0; | |
500 | ie->scb->ie_err_overrun = 0; | |
501 | timesthru = 1024; | |
502 | } | |
503 | ie_readframe(unit, ie, i); | |
504 | } else { | |
505 | if(status & IE_FD_RNR) { | |
506 | if(!(ie->scb->ie_status & IE_RU_READY)) { | |
507 | ie->rframes[0]->ie_fd_next = MK_16(MEM, ie->rbuffs[0]); | |
508 | ie->scb->ie_recv_list = MK_16(MEM, ie->rframes[0]); | |
509 | command_and_wait(unit, IE_RU_START, 0, 0); | |
510 | } | |
511 | } | |
512 | break; | |
513 | } | |
514 | i = (i + 1) % NFRAMES; | |
515 | } | |
516 | return 0; | |
517 | } | |
518 | ||
519 | /* | |
520 | * Process a command-complete interrupt. These are only generated by | |
521 | * the transmission of frames. This routine is deceptively simple, since | |
522 | * most of the real work is done by iestart(). | |
523 | */ | |
524 | static int ietint(unit, ie) | |
525 | int unit; | |
526 | struct ie_softc *ie; | |
527 | { | |
528 | int status; | |
529 | int i; | |
530 | ||
531 | ie->arpcom.ac_if.if_timer = 0; | |
532 | ie->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; | |
533 | ||
534 | for(i = 0; i < ie->xmit_count; i++) { | |
535 | status = ie->xmit_cmds[i]->ie_xmit_status; | |
536 | ||
537 | if(status & IE_XS_LATECOLL) { | |
538 | printf("ie%d: late collision\n", unit); | |
539 | ie->arpcom.ac_if.if_collisions++; | |
540 | ie->arpcom.ac_if.if_oerrors++; | |
541 | } else if(status & IE_XS_NOCARRIER) { | |
542 | printf("ie%d: no carrier\n", unit); | |
543 | ie->arpcom.ac_if.if_oerrors++; | |
544 | } else if(status & IE_XS_LOSTCTS) { | |
545 | printf("ie%d: lost CTS\n", unit); | |
546 | ie->arpcom.ac_if.if_oerrors++; | |
547 | } else if(status & IE_XS_UNDERRUN) { | |
548 | printf("ie%d: DMA underrun\n", unit); | |
549 | ie->arpcom.ac_if.if_oerrors++; | |
550 | } else if(status & IE_XS_EXCMAX) { | |
551 | printf("ie%d: too many collisions\n", unit); | |
552 | ie->arpcom.ac_if.if_collisions += 16; | |
553 | ie->arpcom.ac_if.if_oerrors++; | |
554 | } else { | |
555 | ie->arpcom.ac_if.if_opackets++; | |
556 | ie->arpcom.ac_if.if_collisions += status & IE_XS_MAXCOLL; | |
557 | } | |
558 | } | |
559 | ie->xmit_count = 0; | |
560 | ||
561 | /* | |
562 | * If multicast addresses were added or deleted while we were transmitting, | |
563 | * ie_mc_reset() set the want_mcsetup flag indicating that we should do it. | |
564 | */ | |
565 | if(ie->want_mcsetup) { | |
566 | mc_setup(unit, (caddr_t)ie->xmit_cbuffs[0], ie->scb); | |
567 | ie->want_mcsetup = 0; | |
568 | } | |
569 | ||
570 | /* Wish I knew why this seems to be necessary... */ | |
571 | ie->xmit_cmds[0]->ie_xmit_status |= IE_STAT_COMPL; | |
572 | ||
573 | iestart(&ie->arpcom.ac_if); | |
574 | return 0; /* shouldn't be necessary */ | |
575 | } | |
576 | ||
577 | /* | |
578 | * Process a receiver-not-ready interrupt. I believe that we get these | |
579 | * when there aren't enough buffers to go around. For now (FIXME), we | |
580 | * just restart the receiver, and hope everything's ok. | |
581 | */ | |
582 | static int iernr(unit, ie) | |
583 | int unit; | |
584 | struct ie_softc *ie; | |
585 | { | |
586 | #ifdef doesnt_work | |
587 | setup_rfa((caddr_t)ie->rframes[0], ie); | |
588 | ||
589 | ie->scb->ie_recv_list = MK_16(MEM, ie_softc[unit].rframes[0]); | |
590 | command_and_wait(unit, IE_RU_START, 0, 0); | |
591 | #else | |
592 | /* This doesn't work either, but it doesn't hang either. */ | |
593 | command_and_wait(unit, IE_RU_DISABLE, 0, 0); /* just in case */ | |
594 | setup_rfa((caddr_t)ie->rframes[0], ie); /* ignore cast-qual */ | |
595 | ||
596 | ie->scb->ie_recv_list = MK_16(MEM, ie_softc[unit].rframes[0]); | |
597 | command_and_wait(unit, IE_RU_START, 0, 0); /* was ENABLE */ | |
598 | ||
599 | #endif | |
600 | ie_ack(ie->scb, IE_ST_WHENCE, unit, ie->ie_chan_attn); | |
601 | ||
602 | ie->arpcom.ac_if.if_ierrors++; | |
603 | return 0; | |
604 | } | |
605 | ||
606 | #ifdef FILTER | |
607 | /* | |
608 | * Compare two Ether/802 addresses for equality, inlined and | |
609 | * unrolled for speed. I'd love to have an inline assembler | |
610 | * version of this... | |
611 | */ | |
612 | static inline int ether_equal(u_char *one, u_char *two) { | |
613 | if(one[0] != two[0]) return 0; | |
614 | if(one[1] != two[1]) return 0; | |
615 | if(one[2] != two[2]) return 0; | |
616 | if(one[3] != two[3]) return 0; | |
617 | if(one[4] != two[4]) return 0; | |
618 | if(one[5] != two[5]) return 0; | |
619 | return 1; | |
620 | } | |
621 | ||
622 | /* | |
623 | * Check for a valid address. to_bpf is filled in with one of the following: | |
624 | * 0 -> BPF doesn't get this packet | |
625 | * 1 -> BPF does get this packet | |
626 | * 2 -> BPF does get this packet, but we don't | |
627 | * Return value is true if the packet is for us, and false otherwise. | |
628 | * | |
629 | * This routine is a mess, but it's also critical that it be as fast | |
630 | * as possible. It could be made cleaner if we can assume that the | |
631 | * only client which will fiddle with IFF_PROMISC is BPF. This is | |
632 | * probably a good assumption, but we do not make it here. (Yet.) | |
633 | */ | |
634 | static inline int check_eh(struct ie_softc *ie, | |
635 | struct ether_header *eh, | |
636 | int *to_bpf) { | |
637 | int i; | |
638 | ||
639 | switch(ie->promisc) { | |
640 | case IFF_ALLMULTI: | |
641 | /* | |
642 | * Receiving all multicasts, but no unicasts except those destined for us. | |
643 | */ | |
644 | #if NBPFILTER > 0 | |
645 | *to_bpf = (ie->ie_bpf != 0); /* BPF gets this packet if anybody cares */ | |
646 | #endif | |
647 | if(eh->ether_dhost[0] & 1) { | |
648 | return 1; | |
649 | } | |
650 | if(ether_equal(eh->ether_dhost, ie->arpcom.ac_enaddr)) return 1; | |
651 | return 0; | |
652 | ||
653 | case IFF_PROMISC: | |
654 | /* | |
655 | * Receiving all packets. These need to be passed on to BPF. | |
656 | */ | |
657 | #if NBPFILTER > 0 | |
658 | *to_bpf = (ie->ie_bpf != 0); | |
659 | #endif | |
660 | /* If for us, accept and hand up to BPF */ | |
661 | if(ether_equal(eh->ether_dhost, ie->arpcom.ac_enaddr)) return 1; | |
662 | ||
663 | #if NBPFILTER > 0 | |
664 | if(*to_bpf) *to_bpf = 2; /* we don't need to see it */ | |
665 | #endif | |
666 | ||
667 | #ifdef MULTICAST | |
668 | /* | |
669 | * Not a multicast, so BPF wants to see it but we don't. | |
670 | */ | |
671 | if(!(eh->ether_dhost[0] & 1)) return 1; | |
672 | ||
673 | /* | |
674 | * If it's one of our multicast groups, accept it and pass it | |
675 | * up. | |
676 | */ | |
677 | for(i = 0; i < ie->mcast_count; i++) { | |
678 | if(ether_equal(eh->ether_dhost, (u_char *)&ie->mcast_addrs[i])) { | |
679 | #if NBPFILTER > 0 | |
680 | if(*to_bpf) *to_bpf = 1; | |
681 | #endif | |
682 | return 1; | |
683 | } | |
684 | } | |
685 | #endif /* MULTICAST */ | |
686 | return 1; | |
687 | ||
688 | case IFF_ALLMULTI | IFF_PROMISC: | |
689 | /* | |
690 | * Acting as a multicast router, and BPF running at the same time. | |
691 | * Whew! (Hope this is a fast machine...) | |
692 | */ | |
693 | #if NBPFILTER > 0 | |
694 | *to_bpf = (ie->ie_bpf != 0); | |
695 | #endif | |
696 | /* We want to see multicasts. */ | |
697 | if(eh->ether_dhost[0] & 1) return 1; | |
698 | ||
699 | /* We want to see our own packets */ | |
700 | if(ether_equal(eh->ether_dhost, ie->arpcom.ac_enaddr)) return 1; | |
701 | ||
702 | /* Anything else goes to BPF but nothing else. */ | |
703 | #if NBPFILTER > 0 | |
704 | if(*to_bpf) *to_bpf = 2; | |
705 | #endif | |
706 | return 1; | |
707 | ||
708 | default: | |
709 | /* | |
710 | * Only accept unicast packets destined for us, or multicasts | |
711 | * for groups that we belong to. For now, we assume that the | |
712 | * '586 will only return packets that we asked it for. This | |
713 | * isn't strictly true (it uses hashing for the multicast filter), | |
714 | * but it will do in this case, and we want to get out of here | |
715 | * as quickly as possible. | |
716 | */ | |
717 | #if NBPFILTER > 0 | |
718 | *to_bpf = (ie->ie_bpf != 0); | |
719 | #endif | |
720 | return 1; | |
721 | } | |
722 | return 0; | |
723 | } | |
724 | #endif /* FILTER */ | |
725 | ||
726 | /* | |
727 | * We want to isolate the bits that have meaning... This assumes that | |
728 | * IE_RBUF_SIZE is an even power of two. If somehow the act_len exceeds | |
729 | * the size of the buffer, then we are screwed anyway. | |
730 | */ | |
731 | static inline int ie_buflen(struct ie_softc *ie, int head) { | |
732 | return (ie->rbuffs[head]->ie_rbd_actual | |
733 | & (IE_RBUF_SIZE | (IE_RBUF_SIZE - 1))); | |
734 | } | |
735 | ||
736 | static inline int ie_packet_len(int unit, struct ie_softc *ie) { | |
737 | int i; | |
738 | int head = ie->rbhead; | |
739 | int acc = 0; | |
740 | ||
741 | do { | |
742 | if(!(ie->rbuffs[ie->rbhead]->ie_rbd_actual & IE_RBD_USED)) { | |
743 | #ifdef DEBUG | |
744 | print_rbd(ie->rbuffs[ie->rbhead]); | |
745 | #endif | |
746 | log(LOG_ERR, "ie%d: receive descriptors out of sync at %d\n", | |
747 | unit, ie->rbhead); | |
748 | iereset(unit, 0); | |
749 | return -1; | |
750 | } | |
751 | ||
752 | i = ie->rbuffs[head]->ie_rbd_actual & IE_RBD_LAST; | |
753 | ||
754 | acc += ie_buflen(ie, head); | |
755 | head = (head + 1) % NBUFFS; | |
756 | } while(!i); | |
757 | ||
758 | return acc; | |
759 | } | |
760 | ||
761 | /* | |
762 | * Read data off the interface, and turn it into an mbuf chain. | |
763 | * | |
764 | * This code is DRAMATICALLY different from the previous version; this | |
765 | * version tries to allocate the entire mbuf chain up front, given the | |
766 | * length of the data available. This enables us to allocate mbuf | |
767 | * clusters in many situations where before we would have had a long | |
768 | * chain of partially-full mbufs. This should help to speed up the | |
769 | * operation considerably. (Provided that it works, of course.) | |
770 | */ | |
771 | static inline int ieget(unit, ie, mp, ehp, to_bpf) | |
772 | int unit; | |
773 | struct ie_softc *ie; | |
774 | struct mbuf **mp; | |
775 | struct ether_header *ehp; | |
776 | int *to_bpf; | |
777 | { | |
778 | struct mbuf *m, *top, **mymp; | |
779 | int i; | |
780 | int offset; | |
781 | int totlen, resid; | |
782 | int thismboff; | |
783 | int head; | |
784 | ||
785 | totlen = ie_packet_len(unit, ie); | |
786 | if(totlen <= 0) return -1; | |
787 | ||
788 | i = ie->rbhead; | |
789 | ||
790 | /* | |
791 | * Snarf the Ethernet header. | |
792 | */ | |
793 | bcopy((caddr_t)ie->cbuffs[i], (caddr_t)ehp, sizeof *ehp); | |
794 | /* ignore cast-qual warning here */ | |
795 | ||
796 | /* | |
797 | * As quickly as possible, check if this packet is for us. | |
798 | * If not, don't waste a single cycle copying the rest of the | |
799 | * packet in. | |
800 | * This is only a consideration when FILTER is defined; i.e., when | |
801 | * we are either running BPF or doing multicasting. | |
802 | */ | |
803 | #ifdef FILTER | |
804 | if(!check_eh(ie, ehp, to_bpf)) { | |
805 | ie_drop_packet_buffer(unit, ie); | |
806 | ie->arpcom.ac_if.if_ierrors--; /* just this case, it's not an error */ | |
807 | return -1; | |
808 | } | |
809 | #endif | |
810 | totlen -= (offset = sizeof *ehp); | |
811 | ||
812 | MGETHDR(*mp, M_DONTWAIT, MT_DATA); | |
813 | if(!*mp) { | |
814 | ie_drop_packet_buffer(unit, ie); | |
815 | return -1; | |
816 | } | |
817 | ||
818 | m = *mp; | |
819 | m->m_pkthdr.rcvif = &ie->arpcom.ac_if; | |
820 | m->m_len = MHLEN; | |
821 | resid = m->m_pkthdr.len = totlen; | |
822 | top = 0; | |
823 | mymp = ⊤ | |
824 | ||
825 | /* | |
826 | * This loop goes through and allocates mbufs for all the data we will | |
827 | * be copying in. It does not actually do the copying yet. | |
828 | */ | |
829 | do { /* while(resid > 0) */ | |
830 | /* | |
831 | * Try to allocate an mbuf to hold the data that we have. If we | |
832 | * already allocated one, just get another one and stick it on the | |
833 | * end (eventually). If we don't already have one, try to allocate | |
834 | * an mbuf cluster big enough to hold the whole packet, if we think it's | |
835 | * reasonable, or a single mbuf which may or may not be big enough. | |
836 | * Got that? | |
837 | */ | |
838 | if(top) { | |
839 | MGET(m, M_DONTWAIT, MT_DATA); | |
840 | if(!m) { | |
841 | m_freem(top); | |
842 | ie_drop_packet_buffer(unit, ie); | |
843 | return -1; | |
844 | } | |
845 | m->m_len = MLEN; | |
846 | } | |
847 | ||
848 | if(resid >= MINCLSIZE) { | |
849 | MCLGET(m, M_DONTWAIT); | |
850 | if(m->m_flags & M_EXT) | |
851 | m->m_len = min(resid, MCLBYTES); | |
852 | } else { | |
853 | if(resid < m->m_len) { | |
854 | if(!top && resid + max_linkhdr <= m->m_len) | |
855 | m->m_data += max_linkhdr; | |
856 | m->m_len = resid; | |
857 | } | |
858 | } | |
859 | resid -= m->m_len; | |
860 | *mymp = m; | |
861 | mymp = &m->m_next; | |
862 | } while(resid > 0); | |
863 | ||
864 | resid = totlen; | |
865 | m = top; | |
866 | thismboff = 0; | |
867 | head = ie->rbhead; | |
868 | ||
869 | /* | |
870 | * Now we take the mbuf chain (hopefully only one mbuf most of the | |
871 | * time) and stuff the data into it. There are no possible failures | |
872 | * at or after this point. | |
873 | */ | |
874 | while(resid > 0) { /* while there's stuff left */ | |
875 | int thislen = ie_buflen(ie, head) - offset; | |
876 | ||
877 | /* | |
878 | * If too much data for the current mbuf, then fill the current one | |
879 | * up, go to the next one, and try again. | |
880 | */ | |
881 | if(thislen > m->m_len - thismboff) { | |
882 | int newlen = m->m_len - thismboff; | |
883 | bcopy((caddr_t)(ie->cbuffs[head] + offset), | |
884 | mtod(m, caddr_t) + thismboff, (unsigned)newlen); | |
885 | /* ignore cast-qual warning */ | |
886 | m = m->m_next; | |
887 | thismboff = 0; /* new mbuf, so no offset */ | |
888 | offset += newlen; /* we are now this far into the packet */ | |
889 | resid -= newlen; /* so there is this much left to get */ | |
890 | continue; | |
891 | } | |
892 | ||
893 | /* | |
894 | * If there is more than enough space in the mbuf to hold the | |
895 | * contents of this buffer, copy everything in, advance pointers, | |
896 | * and so on. | |
897 | */ | |
898 | if(thislen < m->m_len - thismboff) { | |
899 | bcopy((caddr_t)(ie->cbuffs[head] + offset), /* ignore warning */ | |
900 | mtod(m, caddr_t) + thismboff, (unsigned)thislen); | |
901 | thismboff += thislen; /* we are this far into the mbuf */ | |
902 | resid -= thislen; /* and this much is left */ | |
903 | goto nextbuf; | |
904 | } | |
905 | ||
906 | /* | |
907 | * Otherwise, there is exactly enough space to put this buffer's | |
908 | * contents into the current mbuf. Do the combination of the above | |
909 | * actions. | |
910 | */ | |
911 | bcopy((caddr_t)(ie->cbuffs[head] + offset), /* ignore warning */ | |
912 | mtod(m, caddr_t) + thismboff, (unsigned)thislen); | |
913 | m = m->m_next; | |
914 | thismboff = 0; /* new mbuf, start at the beginning */ | |
915 | resid -= thislen; /* and we are this far through */ | |
916 | ||
917 | /* | |
918 | * Advance all the pointers. We can get here from either of the | |
919 | * last two cases, but never the first. | |
920 | */ | |
921 | nextbuf: | |
922 | offset = 0; | |
923 | ie->rbuffs[head]->ie_rbd_actual = 0; | |
924 | ie->rbuffs[head]->ie_rbd_length |= IE_RBD_LAST; | |
925 | ie->rbhead = head = (head + 1) % NBUFFS; | |
926 | ie->rbuffs[ie->rbtail]->ie_rbd_length &= ~IE_RBD_LAST; | |
927 | ie->rbtail = (ie->rbtail + 1) % NBUFFS; | |
928 | } | |
929 | ||
930 | /* | |
931 | * Unless something changed strangely while we were doing the copy, | |
932 | * we have now copied everything in from the shared memory. | |
933 | * This means that we are done. | |
934 | */ | |
935 | return 0; | |
936 | } | |
937 | ||
938 | /* | |
939 | * Read frame NUM from unit UNIT (pre-cached as IE). | |
940 | * | |
941 | * This routine reads the RFD at NUM, and copies in the buffers from | |
942 | * the list of RBD, then rotates the RBD and RFD lists so that the receiver | |
943 | * doesn't start complaining. Trailers are DROPPED---there's no point | |
944 | * in wasting time on confusing code to deal with them. Hopefully, | |
945 | * this machine will never ARP for trailers anyway. | |
946 | */ | |
947 | static void ie_readframe(unit, ie, num) | |
948 | int unit; | |
949 | struct ie_softc *ie; | |
950 | int num; /* frame number to read */ | |
951 | { | |
952 | struct ie_recv_frame_desc rfd; | |
953 | struct mbuf *m = 0; | |
954 | struct ether_header eh; | |
955 | #if NBPFILTER > 0 | |
956 | int bpf_gets_it = 0; | |
957 | #endif | |
958 | ||
959 | bcopy((caddr_t)(ie->rframes[num]), &rfd, sizeof(struct ie_recv_frame_desc)); | |
960 | ||
961 | /* Immediately advance the RFD list, since we we have copied ours now. */ | |
962 | ie->rframes[num]->ie_fd_status = 0; | |
963 | ie->rframes[num]->ie_fd_last |= IE_FD_LAST; | |
964 | ie->rframes[ie->rftail]->ie_fd_last &= ~IE_FD_LAST; | |
965 | ie->rftail = (ie->rftail + 1) % NFRAMES; | |
966 | ie->rfhead = (ie->rfhead + 1) % NFRAMES; | |
967 | ||
968 | if(rfd.ie_fd_status & IE_FD_OK) { | |
969 | if( | |
970 | #if NBPFILTER > 0 | |
971 | ieget(unit, ie, &m, &eh, &bpf_gets_it) | |
972 | #else | |
973 | ieget(unit, ie, &m, &eh, (int *)0) | |
974 | #endif | |
975 | ) { | |
976 | ie->arpcom.ac_if.if_ierrors++; /* this counts as an error */ | |
977 | return; | |
978 | } | |
979 | } | |
980 | ||
981 | #ifdef DEBUG | |
982 | if(ie_debug & IED_READFRAME) { | |
983 | printf("ie%d: frame from ether %s type %x\n", unit, | |
984 | ether_sprintf(eh.ether_shost), (unsigned)eh.ether_type); | |
985 | } | |
986 | if(ntohs(eh.ether_type) > ETHERTYPE_TRAIL | |
987 | && ntohs(eh.ether_type) < (ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER)) | |
988 | printf("received trailer!\n"); | |
989 | #endif | |
990 | ||
991 | if(!m) return; | |
992 | ||
993 | #ifdef FILTER | |
994 | if(last_not_for_us) { | |
995 | m_freem(last_not_for_us); | |
996 | last_not_for_us = 0; | |
997 | } | |
998 | ||
999 | #if NBPFILTER > 0 | |
1000 | /* | |
1001 | * Check for a BPF filter; if so, hand it up. | |
1002 | * Note that we have to stick an extra mbuf up front, because | |
1003 | * bpf_mtap expects to have the ether header at the front. | |
1004 | * It doesn't matter that this results in an ill-formatted mbuf chain, | |
1005 | * since BPF just looks at the data. (It doesn't try to free the mbuf, | |
1006 | * tho' it will make a copy for tcpdump.) | |
1007 | */ | |
1008 | if(bpf_gets_it) { | |
1009 | struct mbuf m0; | |
1010 | m0.m_len = sizeof eh; | |
1011 | m0.m_data = (caddr_t)&eh; | |
1012 | m0.m_next = m; | |
1013 | ||
1014 | /* Pass it up */ | |
1015 | bpf_mtap(ie->ie_bpf, &m0); | |
1016 | } | |
1017 | /* | |
1018 | * A signal passed up from the filtering code indicating that the | |
1019 | * packet is intended for BPF but not for the protocol machinery. | |
1020 | * We can save a few cycles by not handing it off to them. | |
1021 | */ | |
1022 | if(bpf_gets_it == 2) { | |
1023 | last_not_for_us = m; | |
1024 | return; | |
1025 | } | |
1026 | #endif /* NBPFILTER > 0 */ | |
1027 | /* | |
1028 | * In here there used to be code to check destination addresses upon | |
1029 | * receipt of a packet. We have deleted that code, and replaced it | |
1030 | * with code to check the address much earlier in the cycle, before | |
1031 | * copying the data in; this saves us valuable cycles when operating | |
1032 | * as a multicast router or when using BPF. | |
1033 | */ | |
1034 | #endif /* FILTER */ | |
1035 | ||
1036 | eh.ether_type = ntohs(eh.ether_type); | |
1037 | ||
1038 | /* | |
1039 | * Finally pass this packet up to higher layers. | |
1040 | */ | |
1041 | ether_input(&ie->arpcom.ac_if, &eh, m); | |
1042 | } | |
1043 | ||
1044 | static void ie_drop_packet_buffer(int unit, struct ie_softc *ie) { | |
1045 | int i; | |
1046 | ||
1047 | do { | |
1048 | /* | |
1049 | * This means we are somehow out of sync. So, we reset the | |
1050 | * adapter. | |
1051 | */ | |
1052 | if(!(ie->rbuffs[ie->rbhead]->ie_rbd_actual & IE_RBD_USED)) { | |
1053 | #ifdef DEBUG | |
1054 | print_rbd(ie->rbuffs[ie->rbhead]); | |
1055 | #endif | |
1056 | log(LOG_ERR, "ie%d: receive descriptors out of sync at %d\n", | |
1057 | unit, ie->rbhead); | |
1058 | iereset(unit, 0); | |
1059 | return; | |
1060 | } | |
1061 | ||
1062 | i = ie->rbuffs[ie->rbhead]->ie_rbd_actual & IE_RBD_LAST; | |
1063 | ||
1064 | ie->rbuffs[ie->rbhead]->ie_rbd_length |= IE_RBD_LAST; | |
1065 | ie->rbuffs[ie->rbhead]->ie_rbd_actual = 0; | |
1066 | ie->rbhead = (ie->rbhead + 1) % NBUFFS; | |
1067 | ie->rbuffs[ie->rbtail]->ie_rbd_length &= ~IE_RBD_LAST; | |
1068 | ie->rbtail = (ie->rbtail + 1) % NBUFFS; | |
1069 | } while(!i); | |
1070 | } | |
1071 | ||
1072 | ||
1073 | /* | |
1074 | * Start transmission on an interface. | |
1075 | */ | |
4c45483e GW |
1076 | static void |
1077 | iestart(ifp) | |
1078 | struct ifnet *ifp; | |
9f4e6350 RG |
1079 | { |
1080 | struct ie_softc *ie = &ie_softc[ifp->if_unit]; | |
1081 | struct mbuf *m0, *m; | |
1082 | unsigned char *buffer; | |
1083 | u_short len; | |
1084 | /* This is not really volatile, in this routine, but it makes gcc happy. */ | |
1085 | volatile u_short *bptr = &ie->scb->ie_command_list; | |
1086 | ||
1087 | if(!(ifp->if_flags & IFF_RUNNING)) | |
4c45483e | 1088 | return; |
9f4e6350 | 1089 | if(ifp->if_flags & IFF_OACTIVE) |
4c45483e | 1090 | return; |
9f4e6350 RG |
1091 | |
1092 | do { | |
1093 | IF_DEQUEUE(&ie->arpcom.ac_if.if_snd, m); | |
1094 | if(!m) | |
1095 | break; | |
1096 | ||
1097 | buffer = ie->xmit_cbuffs[ie->xmit_count]; | |
1098 | len = 0; | |
1099 | ||
1100 | for(m0 = m; m && len < IE_BUF_LEN; m = m->m_next) { | |
1101 | bcopy(mtod(m, caddr_t), buffer, m->m_len); | |
1102 | buffer += m->m_len; | |
1103 | len += m->m_len; | |
1104 | } | |
1105 | ||
1106 | m_freem(m0); | |
1107 | len = MAX(len, ETHERMINLEN); | |
1108 | ||
1109 | #if NBPFILTER > 0 | |
1110 | /* | |
1111 | * See if bpf is listening on this interface, let it see the packet | |
1112 | * before we commit it to the wire. | |
1113 | */ | |
1114 | if(ie->ie_bpf) | |
1115 | bpf_tap(ie->ie_bpf, ie->xmit_cbuffs[ie->xmit_count], len); | |
1116 | #endif | |
1117 | ||
1118 | ie->xmit_buffs[ie->xmit_count]->ie_xmit_flags = IE_XMIT_LAST | len; | |
1119 | ie->xmit_buffs[ie->xmit_count]->ie_xmit_next = 0xffff; | |
1120 | ie->xmit_buffs[ie->xmit_count]->ie_xmit_buf = | |
1121 | MK_24(ie->iomem, ie->xmit_cbuffs[ie->xmit_count]); | |
1122 | ||
1123 | ie->xmit_cmds[ie->xmit_count]->com.ie_cmd_cmd = IE_CMD_XMIT; | |
1124 | ie->xmit_cmds[ie->xmit_count]->ie_xmit_status = 0; | |
1125 | ie->xmit_cmds[ie->xmit_count]->ie_xmit_desc = | |
1126 | MK_16(ie->iomem, ie->xmit_buffs[ie->xmit_count]); | |
1127 | ||
1128 | *bptr = MK_16(ie->iomem, ie->xmit_cmds[ie->xmit_count]); | |
1129 | bptr = &ie->xmit_cmds[ie->xmit_count]->com.ie_cmd_link; | |
1130 | ie->xmit_count++; | |
1131 | } while(ie->xmit_count < 2); | |
1132 | ||
1133 | /* | |
1134 | * If we queued up anything for transmission, send it. | |
1135 | */ | |
1136 | if(ie->xmit_count) { | |
1137 | ie->xmit_cmds[ie->xmit_count - 1]->com.ie_cmd_cmd |= | |
1138 | IE_CMD_LAST | IE_CMD_INTR; | |
1139 | ||
1140 | /* | |
1141 | * By passing the command pointer as a null, we tell | |
1142 | * command_and_wait() to pretend that this isn't an action | |
1143 | * command. I wish I understood what was happening here. | |
1144 | */ | |
1145 | command_and_wait(ifp->if_unit, IE_CU_START, 0, 0); | |
1146 | ifp->if_flags |= IFF_OACTIVE; | |
1147 | } | |
1148 | ||
4c45483e | 1149 | return; |
9f4e6350 RG |
1150 | } |
1151 | ||
1152 | /* | |
1153 | * Check to see if there's an 82586 out there. | |
1154 | */ | |
1155 | int check_ie_present(unit, where, size) | |
1156 | int unit; | |
1157 | caddr_t where; | |
1158 | unsigned size; | |
1159 | { | |
1160 | volatile struct ie_sys_conf_ptr *scp; | |
1161 | volatile struct ie_int_sys_conf_ptr *iscp; | |
1162 | volatile struct ie_sys_ctl_block *scb; | |
1163 | u_long realbase; | |
1164 | int s; | |
1165 | ||
1166 | s = splimp(); | |
1167 | ||
1168 | realbase = (u_long)where + size - (1 << 24); | |
1169 | ||
1170 | scp = (volatile struct ie_sys_conf_ptr *)(realbase + IE_SCP_ADDR); | |
1171 | bzero((char *)scp, sizeof *scp); /* ignore cast-qual */ | |
1172 | ||
1173 | /* | |
1174 | * First we put the ISCP at the bottom of memory; this tests to make | |
1175 | * sure that our idea of the size of memory is the same as the controller's. | |
1176 | * This is NOT where the ISCP will be in normal operation. | |
1177 | */ | |
1178 | iscp = (volatile struct ie_int_sys_conf_ptr *)where; | |
1179 | bzero((char *)iscp, sizeof *iscp); /* ignore cast-qual */ | |
1180 | ||
1181 | scb = (volatile struct ie_sys_ctl_block *)where; | |
1182 | bzero((char *)scb, sizeof *scb); /* ignore cast-qual */ | |
1183 | ||
1184 | scp->ie_bus_use = 0; /* 16-bit */ | |
1185 | scp->ie_iscp_ptr = (caddr_t)((volatile caddr_t)iscp - /* ignore cast-qual */ | |
1186 | (volatile caddr_t)realbase); | |
1187 | ||
1188 | iscp->ie_busy = 1; | |
1189 | iscp->ie_scb_offset = MK_16(realbase, scb) + 256; | |
1190 | ||
1191 | (*ie_softc[unit].ie_reset_586)(unit); | |
1192 | (*ie_softc[unit].ie_chan_attn)(unit); | |
1193 | ||
1194 | DELAY(100); /* wait a while... */ | |
1195 | ||
1196 | if(iscp->ie_busy) { | |
1197 | splx(s); | |
1198 | return 0; | |
1199 | } | |
1200 | ||
1201 | /* | |
1202 | * Now relocate the ISCP to its real home, and reset the controller | |
1203 | * again. | |
1204 | */ | |
1205 | iscp = (void *)Align((caddr_t)(realbase + IE_SCP_ADDR - | |
1206 | sizeof(struct ie_int_sys_conf_ptr))); | |
1207 | bzero((char *)iscp, sizeof *iscp); /* ignore cast-qual */ | |
1208 | ||
1209 | scp->ie_iscp_ptr = (caddr_t)((caddr_t)iscp - (caddr_t)realbase); | |
1210 | /* ignore cast-qual */ | |
1211 | ||
1212 | iscp->ie_busy = 1; | |
1213 | iscp->ie_scb_offset = MK_16(realbase, scb); | |
1214 | ||
1215 | (*ie_softc[unit].ie_reset_586)(unit); | |
1216 | (*ie_softc[unit].ie_chan_attn)(unit); | |
1217 | ||
1218 | DELAY(100); | |
1219 | ||
1220 | if(iscp->ie_busy) { | |
1221 | splx(s); | |
1222 | return 0; | |
1223 | } | |
1224 | ||
1225 | ie_softc[unit].iosize = size; | |
1226 | ie_softc[unit].iomem = (caddr_t)realbase; | |
1227 | ||
1228 | ie_softc[unit].iscp = iscp; | |
1229 | ie_softc[unit].scb = scb; | |
1230 | ||
1231 | /* | |
1232 | * Acknowledge any interrupts we may have caused... | |
1233 | */ | |
1234 | ie_ack(scb, IE_ST_WHENCE, unit, ie_softc[unit].ie_chan_attn); | |
1235 | splx(s); | |
1236 | ||
1237 | return 1; | |
1238 | } | |
1239 | ||
1240 | /* | |
1241 | * Divine the memory size of ie board UNIT. | |
1242 | * Better hope there's nothing important hiding just below the ie card... | |
1243 | */ | |
1244 | static void find_ie_mem_size(unit) | |
1245 | int unit; | |
1246 | { | |
1247 | unsigned size; | |
1248 | ||
1249 | ie_softc[unit].iosize = 0; | |
1250 | ||
1251 | for(size = 65536; size >= 16384; size -= 16384) { | |
1252 | if(check_ie_present(unit, ie_softc[unit].iomembot, size)) { | |
1253 | return; | |
1254 | } | |
1255 | } | |
1256 | ||
1257 | return; | |
1258 | } | |
1259 | ||
1260 | void sl_reset_586(unit) | |
1261 | int unit; | |
1262 | { | |
1263 | outb(PORT + IEATT_RESET, 0); | |
1264 | } | |
1265 | ||
1266 | void sl_chan_attn(unit) | |
1267 | int unit; | |
1268 | { | |
1269 | outb(PORT + IEATT_ATTN, 0); | |
1270 | } | |
1271 | ||
1272 | void sl_read_ether(unit, addr) | |
1273 | int unit; | |
1274 | unsigned char addr[6]; | |
1275 | { | |
1276 | int i; | |
1277 | ||
1278 | for(i = 0; i < 6; i++) | |
1279 | addr[i] = inb(PORT + i); | |
1280 | } | |
1281 | ||
1282 | ||
4c45483e GW |
1283 | static void |
1284 | iereset(unit, dummy) | |
1285 | int unit, dummy; | |
9f4e6350 RG |
1286 | { |
1287 | int s = splimp(); | |
1288 | ||
1289 | if(unit >= NIE) { | |
1290 | splx(s); | |
4c45483e | 1291 | return; |
9f4e6350 RG |
1292 | } |
1293 | ||
1294 | printf("ie%d: reset\n", unit); | |
1295 | ie_softc[unit].arpcom.ac_if.if_flags &= ~IFF_UP; | |
1296 | ieioctl(&ie_softc[unit].arpcom.ac_if, SIOCSIFFLAGS, 0); | |
1297 | ||
1298 | /* | |
1299 | * Stop i82586 dead in its tracks. | |
1300 | */ | |
1301 | if(command_and_wait(unit, IE_RU_ABORT | IE_CU_ABORT, 0, 0)) | |
1302 | printf("ie%d: abort commands timed out\n", unit); | |
1303 | ||
1304 | if(command_and_wait(unit, IE_RU_DISABLE | IE_CU_STOP, 0, 0)) | |
1305 | printf("ie%d: disable commands timed out\n", unit); | |
1306 | ||
1307 | #ifdef notdef | |
1308 | if(!check_ie_present(unit, ie_softc[unit].iomembot, ie_softc[unit].iosize)) | |
1309 | panic("ie disappeared!\n"); | |
1310 | #endif | |
1311 | ||
1312 | ie_softc[unit].arpcom.ac_if.if_flags |= IFF_UP; | |
1313 | ieioctl(&ie_softc[unit].arpcom.ac_if, SIOCSIFFLAGS, 0); | |
1314 | ||
1315 | splx(s); | |
4c45483e | 1316 | return; |
9f4e6350 RG |
1317 | } |
1318 | ||
1319 | /* | |
1320 | * This is called if we time out. | |
1321 | */ | |
4c45483e GW |
1322 | static void |
1323 | chan_attn_timeout(rock, arg2) | |
1324 | caddr_t rock; | |
1325 | int arg2; | |
9f4e6350 RG |
1326 | { |
1327 | *(int *)rock = 1; | |
9f4e6350 RG |
1328 | } |
1329 | ||
1330 | /* | |
1331 | * Send a command to the controller and wait for it to either | |
1332 | * complete or be accepted, depending on the command. If the | |
1333 | * command pointer is null, then pretend that the command is | |
1334 | * not an action command. If the command pointer is not null, | |
1335 | * and the command is an action command, wait for | |
1336 | * ((volatile struct ie_cmd_common *)pcmd)->ie_cmd_status & MASK | |
1337 | * to become true. | |
1338 | */ | |
1339 | static int command_and_wait(unit, cmd, pcmd, mask) | |
1340 | int unit; | |
1341 | int cmd; | |
1342 | volatile void *pcmd; | |
1343 | int mask; | |
1344 | { | |
1345 | volatile struct ie_cmd_common *cc = pcmd; | |
1346 | volatile int timedout = 0; | |
1347 | extern int hz; | |
1348 | ||
1349 | ie_softc[unit].scb->ie_command = (u_short)cmd; | |
1350 | ||
1351 | if(IE_ACTION_COMMAND(cmd) && pcmd) { | |
1352 | (*ie_softc[unit].ie_chan_attn)(unit); | |
1353 | ||
1354 | /* | |
1355 | * According to the packet driver, the minimum timeout should be | |
1356 | * .369 seconds, which we round up to .37. | |
1357 | */ | |
1358 | timeout(chan_attn_timeout, (caddr_t)&timedout, 37 * hz / 100); | |
1359 | /* ignore cast-qual */ | |
1360 | ||
1361 | /* | |
1362 | * Now spin-lock waiting for status. This is not a very nice | |
1363 | * thing to do, but I haven't figured out how, or indeed if, we | |
1364 | * can put the process waiting for action to sleep. (We may | |
1365 | * be getting called through some other timeout running in the | |
1366 | * kernel.) | |
1367 | */ | |
1368 | while(1) { | |
1369 | if((cc->ie_cmd_status & mask) || timedout) | |
1370 | break; | |
1371 | } | |
1372 | ||
1373 | untimeout(chan_attn_timeout, (caddr_t)&timedout); | |
1374 | /* ignore cast-qual */ | |
1375 | ||
1376 | return timedout; | |
1377 | } else { | |
1378 | ||
1379 | /* | |
1380 | * Otherwise, just wait for the command to be accepted. | |
1381 | */ | |
1382 | (*ie_softc[unit].ie_chan_attn)(unit); | |
1383 | ||
1384 | while(ie_softc[unit].scb->ie_command) | |
1385 | ; /* spin lock */ | |
1386 | ||
1387 | return 0; | |
1388 | } | |
1389 | } | |
1390 | ||
1391 | /* | |
1392 | * Run the time-domain reflectometer... | |
1393 | */ | |
1394 | static void run_tdr(unit, cmd) | |
1395 | int unit; | |
1396 | struct ie_tdr_cmd *cmd; | |
1397 | { | |
1398 | int result; | |
1399 | ||
1400 | cmd->com.ie_cmd_status = 0; | |
1401 | cmd->com.ie_cmd_cmd = IE_CMD_TDR | IE_CMD_LAST; | |
1402 | cmd->com.ie_cmd_link = 0xffff; | |
1403 | cmd->ie_tdr_time = 0; | |
1404 | ||
1405 | ie_softc[unit].scb->ie_command_list = MK_16(MEM, cmd); | |
1406 | cmd->ie_tdr_time = 0; | |
1407 | ||
1408 | if(command_and_wait(unit, IE_CU_START, cmd, IE_STAT_COMPL)) | |
1409 | result = 0x2000; | |
1410 | else | |
1411 | result = cmd->ie_tdr_time; | |
1412 | ||
1413 | ie_ack(ie_softc[unit].scb, IE_ST_WHENCE, unit, | |
1414 | ie_softc[unit].ie_chan_attn); | |
1415 | ||
1416 | if(result & IE_TDR_SUCCESS) | |
1417 | return; | |
1418 | ||
1419 | if(result & IE_TDR_XCVR) { | |
1420 | printf("ie%d: transceiver problem\n", unit); | |
1421 | } else if(result & IE_TDR_OPEN) { | |
1422 | printf("ie%d: TDR detected an open %d clocks away\n", unit, | |
1423 | result & IE_TDR_TIME); | |
1424 | } else if(result & IE_TDR_SHORT) { | |
1425 | printf("ie%d: TDR detected a short %d clocks away\n", unit, | |
1426 | result & IE_TDR_TIME); | |
1427 | } else { | |
1428 | printf("ie%d: TDR returned unknown status %x\n", result); | |
1429 | } | |
1430 | } | |
1431 | ||
1432 | static void start_receiver(unit) | |
1433 | int unit; | |
1434 | { | |
1435 | int s = splimp(); | |
1436 | ||
1437 | ie_softc[unit].scb->ie_recv_list = MK_16(MEM, ie_softc[unit].rframes[0]); | |
1438 | command_and_wait(unit, IE_RU_START, 0, 0); | |
1439 | ||
1440 | ie_ack(ie_softc[unit].scb, IE_ST_WHENCE, unit, ie_softc[unit].ie_chan_attn); | |
1441 | ||
1442 | splx(s); | |
1443 | } | |
1444 | ||
1445 | /* | |
1446 | * Here is a helper routine for iernr() and ieinit(). This sets up | |
1447 | * the RFA. | |
1448 | */ | |
1449 | static caddr_t setup_rfa(caddr_t ptr, struct ie_softc *ie) { | |
1450 | volatile struct ie_recv_frame_desc *rfd = (void *)ptr; | |
1451 | volatile struct ie_recv_buf_desc *rbd; | |
1452 | int i; | |
1453 | int unit = ie - &ie_softc[0]; | |
1454 | ||
1455 | /* First lay them out */ | |
1456 | for(i = 0; i < NFRAMES; i++) { | |
1457 | ie->rframes[i] = rfd; | |
1458 | bzero((char *)rfd, sizeof *rfd); /* ignore cast-qual */ | |
1459 | rfd++; | |
1460 | } | |
1461 | ||
1462 | ptr = (caddr_t)Align((caddr_t)rfd); /* ignore cast-qual */ | |
1463 | ||
1464 | /* Now link them together */ | |
1465 | for(i = 0; i < NFRAMES; i++) { | |
1466 | ie->rframes[i]->ie_fd_next = | |
1467 | MK_16(MEM, ie->rframes[(i + 1) % NFRAMES]); | |
1468 | } | |
1469 | ||
1470 | /* Finally, set the EOL bit on the last one. */ | |
1471 | ie->rframes[NFRAMES - 1]->ie_fd_last |= IE_FD_LAST; | |
1472 | ||
1473 | /* | |
1474 | * Now lay out some buffers for the incoming frames. Note that | |
1475 | * we set aside a bit of slop in each buffer, to make sure that | |
1476 | * we have enough space to hold a single frame in every buffer. | |
1477 | */ | |
1478 | rbd = (void *)ptr; | |
1479 | ||
1480 | for(i = 0; i < NBUFFS; i++) { | |
1481 | ie->rbuffs[i] = rbd; | |
1482 | bzero((char *)rbd, sizeof *rbd); /* ignore cast-qual */ | |
1483 | ptr = (caddr_t)Align(ptr + sizeof *rbd); | |
1484 | rbd->ie_rbd_length = IE_RBUF_SIZE; | |
1485 | rbd->ie_rbd_buffer = MK_24(MEM, ptr); | |
1486 | ie->cbuffs[i] = (void *)ptr; | |
1487 | ptr += IE_RBUF_SIZE; | |
1488 | rbd = (void *)ptr; | |
1489 | } | |
1490 | ||
1491 | /* Now link them together */ | |
1492 | for(i = 0; i < NBUFFS; i++) { | |
1493 | ie->rbuffs[i]->ie_rbd_next = MK_16(MEM, ie->rbuffs[(i + 1) % NBUFFS]); | |
1494 | } | |
1495 | ||
1496 | /* Tag EOF on the last one */ | |
1497 | ie->rbuffs[NBUFFS - 1]->ie_rbd_length |= IE_RBD_LAST; | |
1498 | ||
1499 | /* We use the head and tail pointers on receive to keep track of | |
1500 | * the order in which RFDs and RBDs are used. */ | |
1501 | ie->rfhead = 0; | |
1502 | ie->rftail = NFRAMES - 1; | |
1503 | ie->rbhead = 0; | |
1504 | ie->rbtail = NBUFFS - 1; | |
1505 | ||
1506 | ie->scb->ie_recv_list = MK_16(MEM, ie->rframes[0]); | |
1507 | ie->rframes[0]->ie_fd_buf_desc = MK_16(MEM, ie->rbuffs[0]); | |
1508 | ||
1509 | ptr = Align(ptr); | |
1510 | return ptr; | |
1511 | } | |
1512 | ||
1513 | /* | |
1514 | * Run the multicast setup command. | |
1515 | * Call at splimp(). | |
1516 | */ | |
1517 | static int mc_setup(int unit, caddr_t ptr, | |
1518 | volatile struct ie_sys_ctl_block *scb) { | |
1519 | struct ie_softc *ie = &ie_softc[unit]; | |
1520 | volatile struct ie_mcast_cmd *cmd = (void *)ptr; | |
1521 | ||
1522 | cmd->com.ie_cmd_status = 0; | |
1523 | cmd->com.ie_cmd_cmd = IE_CMD_MCAST | IE_CMD_LAST; | |
1524 | cmd->com.ie_cmd_link = 0xffff; | |
1525 | ||
1526 | /* ignore cast-qual */ | |
1527 | bcopy((caddr_t)ie->mcast_addrs, (caddr_t)cmd->ie_mcast_addrs, | |
1528 | ie->mcast_count * sizeof *ie->mcast_addrs); | |
1529 | ||
1530 | cmd->ie_mcast_bytes = ie->mcast_count * 6; /* grrr... */ | |
1531 | ||
1532 | scb->ie_command_list = MK_16(MEM, cmd); | |
1533 | if(command_and_wait(unit, IE_CU_START, cmd, IE_STAT_COMPL) | |
1534 | || !(cmd->com.ie_cmd_status & IE_STAT_OK)) { | |
1535 | printf("ie%d: multicast address setup command failed\n", unit); | |
1536 | return 0; | |
1537 | } | |
1538 | return 1; | |
1539 | } | |
1540 | ||
1541 | /* | |
1542 | * This routine takes the environment generated by check_ie_present() | |
1543 | * and adds to it all the other structures we need to operate the adapter. | |
1544 | * This includes executing the CONFIGURE, IA-SETUP, and MC-SETUP commands, | |
1545 | * starting the receiver unit, and clearing interrupts. | |
1546 | * | |
1547 | * THIS ROUTINE MUST BE CALLED AT splimp() OR HIGHER. | |
1548 | */ | |
4c45483e GW |
1549 | static void |
1550 | ieinit(unit) | |
9f4e6350 RG |
1551 | int unit; |
1552 | { | |
1553 | struct ie_softc *ie = &ie_softc[unit]; | |
1554 | volatile struct ie_sys_ctl_block *scb = ie->scb; | |
1555 | caddr_t ptr; | |
1556 | ||
1557 | ptr = (caddr_t)Align((caddr_t)scb + sizeof *scb); /* ignore cast-qual */ | |
1558 | ||
1559 | /* | |
1560 | * Send the configure command first. | |
1561 | */ | |
1562 | { | |
1563 | volatile struct ie_config_cmd *cmd = (void *)ptr; | |
1564 | ||
1565 | ie_setup_config(cmd, ie->promisc, ie->hard_type == IE_STARLAN10); | |
1566 | cmd->com.ie_cmd_status = 0; | |
1567 | cmd->com.ie_cmd_cmd = IE_CMD_CONFIG | IE_CMD_LAST; | |
1568 | cmd->com.ie_cmd_link = 0xffff; | |
1569 | ||
1570 | scb->ie_command_list = MK_16(MEM, cmd); | |
1571 | ||
1572 | if(command_and_wait(unit, IE_CU_START, cmd, IE_STAT_COMPL) | |
1573 | || !(cmd->com.ie_cmd_status & IE_STAT_OK)) { | |
1574 | printf("ie%d: configure command failed\n", unit); | |
4c45483e | 1575 | return; |
9f4e6350 RG |
1576 | } |
1577 | } | |
1578 | /* | |
1579 | * Now send the Individual Address Setup command. | |
1580 | */ | |
1581 | { | |
1582 | volatile struct ie_iasetup_cmd *cmd = (void *)ptr; | |
1583 | ||
1584 | cmd->com.ie_cmd_status = 0; | |
1585 | cmd->com.ie_cmd_cmd = IE_CMD_IASETUP | IE_CMD_LAST; | |
1586 | cmd->com.ie_cmd_link = 0xffff; | |
1587 | ||
1588 | bcopy((char *)ie_softc[unit].arpcom.ac_enaddr, (char *)&cmd->ie_address, | |
1589 | sizeof cmd->ie_address); /* ignore cast-qual */ | |
1590 | ||
1591 | scb->ie_command_list = MK_16(MEM, cmd); | |
1592 | if(command_and_wait(unit, IE_CU_START, cmd, IE_STAT_COMPL) | |
1593 | || !(cmd->com.ie_cmd_status & IE_STAT_OK)) { | |
1594 | printf("ie%d: individual address setup command failed\n", unit); | |
4c45483e | 1595 | return; |
9f4e6350 RG |
1596 | } |
1597 | } | |
1598 | ||
1599 | /* | |
1600 | * Now run the time-domain reflectometer. | |
1601 | */ | |
1602 | run_tdr(unit, (void *)ptr); | |
1603 | ||
1604 | /* | |
1605 | * Acknowledge any interrupts we have generated thus far. | |
1606 | */ | |
1607 | ie_ack(ie->scb, IE_ST_WHENCE, unit, ie->ie_chan_attn); | |
1608 | ||
1609 | /* | |
1610 | * Set up the RFA. | |
1611 | */ | |
1612 | ptr = setup_rfa(ptr, ie); | |
1613 | ||
1614 | /* | |
1615 | * Finally, the transmit command and buffer are the last little bit of work. | |
1616 | */ | |
1617 | ie->xmit_cmds[0] = (void *)ptr; | |
1618 | ptr += sizeof *ie->xmit_cmds[0]; | |
1619 | ptr = Align(ptr); | |
1620 | ie->xmit_buffs[0] = (void *)ptr; | |
1621 | ptr += sizeof *ie->xmit_buffs[0]; | |
1622 | ptr = Align(ptr); | |
1623 | ||
1624 | /* Second transmit command */ | |
1625 | ie->xmit_cmds[1] = (void *)ptr; | |
1626 | ptr += sizeof *ie->xmit_cmds[1]; | |
1627 | ptr = Align(ptr); | |
1628 | ie->xmit_buffs[1] = (void *)ptr; | |
1629 | ptr += sizeof *ie->xmit_buffs[1]; | |
1630 | ptr = Align(ptr); | |
1631 | ||
1632 | /* Both transmit buffers */ | |
1633 | ie->xmit_cbuffs[0] = (void *)ptr; | |
1634 | ptr += IE_BUF_LEN; | |
1635 | ptr = Align(ptr); | |
1636 | ie->xmit_cbuffs[1] = (void *)ptr; | |
1637 | ||
1638 | bzero((caddr_t)ie->xmit_cmds[0], sizeof *ie->xmit_cmds[0]); /* ignore */ | |
1639 | bzero((caddr_t)ie->xmit_buffs[0], sizeof *ie->xmit_buffs[0]); /* cast-qual */ | |
1640 | bzero((caddr_t)ie->xmit_cmds[1], sizeof *ie->xmit_cmds[0]); /* warnings */ | |
1641 | bzero((caddr_t)ie->xmit_buffs[1], sizeof *ie->xmit_buffs[0]); /* here */ | |
1642 | ||
1643 | /* | |
1644 | * This must be coordinated with iestart() and ietint(). | |
1645 | */ | |
1646 | ie->xmit_cmds[0]->ie_xmit_status = IE_STAT_COMPL; | |
1647 | ||
1648 | ie->arpcom.ac_if.if_flags |= IFF_RUNNING; /* tell higher levels that we are here */ | |
1649 | start_receiver(unit); | |
4c45483e | 1650 | return; |
9f4e6350 RG |
1651 | } |
1652 | ||
1653 | static void ie_stop(unit) | |
1654 | int unit; | |
1655 | { | |
1656 | command_and_wait(unit, IE_RU_DISABLE, 0, 0); | |
1657 | } | |
1658 | ||
4c45483e GW |
1659 | static int |
1660 | ieioctl(ifp, command, data) | |
1661 | struct ifnet *ifp; | |
1662 | int command; | |
1663 | caddr_t data; | |
9f4e6350 RG |
1664 | { |
1665 | struct ifaddr *ifa = (struct ifaddr *)data; | |
1666 | struct ie_softc *ie = &ie_softc[ifp->if_unit]; | |
1667 | int s, error = 0; | |
1668 | ||
1669 | s = splimp(); | |
1670 | ||
1671 | switch(command) { | |
1672 | case SIOCSIFADDR: | |
1673 | ifp->if_flags |= IFF_UP; | |
1674 | ||
1675 | switch(ifa->ifa_addr->sa_family) { | |
1676 | #ifdef INET | |
1677 | case AF_INET: | |
1678 | ieinit(ifp->if_unit); | |
1679 | ((struct arpcom *)ifp)->ac_ipaddr = | |
1680 | IA_SIN(ifa)->sin_addr; | |
1681 | arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); | |
1682 | break; | |
1683 | #endif /* INET */ | |
1684 | ||
1685 | #ifdef NS | |
1686 | /* This magic copied from if_is.c; I don't use XNS, so I have no | |
1687 | * way of telling if this actually works or not. | |
1688 | */ | |
1689 | case AF_NS: | |
1690 | { | |
1691 | struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); | |
1692 | ||
1693 | if(ns_nullhost(*ina)) { | |
1694 | ina->x_host = *(union ns_host *)(ie->arpcom.ac_enaddr); | |
1695 | } else { | |
1696 | ifp->if_flags &= ~IFF_RUNNING; | |
1697 | bcopy((caddr_t)ina->x_host.c_host, | |
1698 | (caddr_t)ie->arpcom.ac_enaddr, | |
1699 | sizeof ie->arpcom.ac_enaddr); | |
1700 | } | |
1701 | ||
1702 | ieinit(ifp->if_unit); | |
1703 | } | |
1704 | break; | |
1705 | #endif /* NS */ | |
1706 | ||
1707 | default: | |
1708 | ieinit(ifp->if_unit); | |
1709 | break; | |
1710 | } | |
1711 | break; | |
1712 | ||
1713 | case SIOCSIFFLAGS: | |
1714 | /* | |
1715 | * Note that this device doesn't have an "all multicast" mode, so we | |
1716 | * must turn on promiscuous mode and do the filtering manually. | |
1717 | */ | |
1718 | if((ifp->if_flags & IFF_UP) == 0 && | |
1719 | (ifp->if_flags & IFF_RUNNING)) { | |
1720 | ifp->if_flags &= ~IFF_RUNNING; | |
1721 | ie_stop(ifp->if_unit); | |
1722 | } else if((ifp->if_flags & IFF_UP) && | |
1723 | (ifp->if_flags & IFF_RUNNING) == 0) { | |
1724 | ie_softc[ifp->if_unit].promisc = | |
1725 | ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI); | |
1726 | ieinit(ifp->if_unit); | |
1727 | } else if(ie_softc[ifp->if_unit].promisc ^ | |
1728 | (ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI))) { | |
1729 | ie_softc[ifp->if_unit].promisc = | |
1730 | ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI); | |
1731 | ieinit(ifp->if_unit); | |
1732 | } | |
1733 | break; | |
1734 | ||
1735 | #ifdef MULTICAST | |
1736 | case SIOCADDMULTI: | |
1737 | case SIOCDELMULTI: | |
1738 | /* | |
1739 | * Update multicast listeners | |
1740 | */ | |
1741 | error = ((command == SIOCADDMULTI) | |
1742 | ? ether_addmulti((struct ifreq *)data, &ie->arpcom) | |
1743 | : ether_delmulti((struct ifreq *)data, &ie->arpcom)); | |
1744 | ||
1745 | if(error == ENETRESET) { | |
1746 | /* reset multicast filtering */ | |
1747 | ie_mc_reset(ifp->if_unit); | |
1748 | error = 0; | |
1749 | } | |
1750 | break; | |
1751 | #endif /* MULTICAST */ | |
1752 | ||
1753 | default: | |
1754 | error = EINVAL; | |
1755 | } | |
1756 | ||
1757 | splx(s); | |
1758 | return error; | |
1759 | } | |
1760 | ||
1761 | #ifdef MULTICAST | |
1762 | static void ie_mc_reset(int unit) { | |
1763 | struct ie_softc *ie = &ie_softc[unit]; | |
1764 | struct ether_multi *enm; | |
1765 | struct ether_multistep step; | |
1766 | ||
1767 | /* | |
1768 | * Step through the list of addresses. | |
1769 | */ | |
1770 | ie->mcast_count = 0; | |
1771 | ETHER_FIRST_MULTI(step, &ie->arpcom, enm); | |
1772 | while(enm) { | |
1773 | if(ie->mcast_count >= MAXMCAST | |
1774 | || bcmp(enm->enm_addrlo, enm->enm_addrhi, 6) != 0) { | |
1775 | ie->arpcom.ac_if.if_flags |= IFF_ALLMULTI; | |
1776 | ieioctl(&ie->arpcom.ac_if, SIOCSIFFLAGS, (void *)0); | |
1777 | goto setflag; | |
1778 | } | |
1779 | ||
1780 | bcopy(enm->enm_addrlo, &(ie->mcast_addrs[ie->mcast_count]), 6); | |
1781 | ie->mcast_count++; | |
1782 | ETHER_NEXT_MULTI(step, enm); | |
1783 | } | |
1784 | ||
1785 | setflag: | |
1786 | ie->want_mcsetup = 1; | |
1787 | } | |
1788 | ||
1789 | #endif | |
1790 | ||
1791 | #ifdef DEBUG | |
1792 | void print_rbd(volatile struct ie_recv_buf_desc *rbd) { | |
1793 | printf("RBD at %08lx:\n" | |
1794 | "actual %04x, next %04x, buffer %08x\n" | |
1795 | "length %04x, mbz %04x\n", | |
1796 | (unsigned long)rbd, | |
1797 | rbd->ie_rbd_actual, rbd->ie_rbd_next, rbd->ie_rbd_buffer, | |
1798 | rbd->ie_rbd_length, rbd->mbz); | |
1799 | } | |
1800 | #endif /* DEBUG */ | |
1801 | #endif /* NIE > 0 */ | |
1802 |