FreeBSD/Linux Kernel Cross Reference
sys/dev/vx/if_vx.c
1 /*
2 * Copyright (c) 1994 Herb Peyerl <hpeyerl@novatel.ca>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by Herb Peyerl.
16 * 4. The name of Herb Peyerl may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * $FreeBSD: src/sys/dev/vx/if_vx.c,v 1.2.2.7 1999/09/05 08:09:31 peter Exp $
31 *
32 */
33
34 /*
35 * Created from if_ep.c driver by Fred Gray (fgray@rice.edu) to support
36 * the 3c590 family.
37 */
38
39 /*
40 * Modified from the FreeBSD 1.1.5.1 version by:
41 * Andres Vega Garcia
42 * INRIA - Sophia Antipolis, France
43 * avega@sophia.inria.fr
44 */
45
46 /*
47 * Promiscuous mode added and interrupt logic slightly changed
48 * to reduce the number of adapter failures. Transceiver select
49 * logic changed to use value from EEPROM. Autoconfiguration
50 * features added.
51 * Done by:
52 * Serge Babkin
53 * Chelindbank (Chelyabinsk, Russia)
54 * babkin@hq.icb.chel.su
55 */
56
57 #include "vx.h"
58 #if NVX > 0
59
60 #if NVX < 4 /* These cost 4 bytes apiece, so give us 4 */
61 #undef NVX
62 #define NVX 4
63 #endif
64
65 #include "bpfilter.h"
66
67 #include <sys/param.h>
68 #include <sys/systm.h>
69 #include <sys/sockio.h>
70 #include <sys/malloc.h>
71 #include <sys/mbuf.h>
72 #include <sys/socket.h>
73
74 #include <net/if.h>
75
76 #ifdef INET
77 #include <netinet/in.h>
78 #include <netinet/in_systm.h>
79 #include <netinet/in_var.h>
80 #include <netinet/ip.h>
81 #include <netinet/if_ether.h>
82 #endif
83
84 #include <net/ethernet.h>
85 #include <net/if_arp.h>
86
87 #if NBPFILTER > 0
88 #include <net/bpf.h>
89 #endif
90
91 #include <machine/clock.h>
92
93 #include <dev/vx/if_vxreg.h>
94
95 #define ETHER_MAX_LEN 1518
96 #define ETHER_ADDR_LEN 6
97
98 struct vx_softc *vx_softc[NVX];
99
100 u_long vx_count; /* both PCI and EISA */
101
102 static struct connector_entry {
103 int bit;
104 char *name;
105 } conn_tab[VX_CONNECTORS] = {
106 #define CONNECTOR_UTP 0
107 { 0x08, "utp"},
108 #define CONNECTOR_AUI 1
109 { 0x20, "aui"},
110 /* dummy */
111 { 0, "???"},
112 #define CONNECTOR_BNC 3
113 { 0x10, "bnc"},
114 #define CONNECTOR_TX 4
115 { 0x02, "tx"},
116 #define CONNECTOR_FX 5
117 { 0x04, "fx"},
118 #define CONNECTOR_MII 6
119 { 0x40, "mii"},
120 { 0, "???"}
121 };
122
123 /* struct vx_softc *vxalloc __P((int)); */
124 /* void *vxfree __P((struct vx_softc *)); */
125 /* int vxattach __P((struct vx_softc *)); */
126 static void vxtxstat __P((struct vx_softc *));
127 static int vxstatus __P((struct vx_softc *));
128 static void vxinit __P((void *));
129 static int vxioctl __P((struct ifnet *, int, caddr_t));
130 static void vxstart __P((struct ifnet *ifp));
131 static void vxwatchdog __P((struct ifnet *));
132 static void vxreset __P((struct vx_softc *));
133 /* void vxstop __P((struct vx_softc *)); */
134 static void vxread __P((struct vx_softc *));
135 static struct mbuf *vxget __P((struct vx_softc *, u_int));
136 static void vxmbuffill __P((void *));
137 static void vxmbufempty __P((struct vx_softc *));
138 static void vxsetfilter __P((struct vx_softc *));
139 static void vxgetlink __P((struct vx_softc *));
140 static void vxsetlink __P((struct vx_softc *));
141 /* int vxbusyeeprom __P((struct vx_softc *)); */
142 /* void vxintr __P((void *)); */
143
144 struct vx_softc *
145 vxalloc(unit)
146 int unit;
147 {
148 struct vx_softc *sc;
149
150 if (unit >= NVX) {
151 printf("vx%d: unit number too high.\n", unit);
152 return NULL;
153 }
154
155 if (vx_softc[unit]) {
156 printf("vx%d: already allocated.\n", unit);
157 return NULL;
158 }
159
160 sc = malloc(sizeof(struct vx_softc), M_DEVBUF, M_NOWAIT);
161 if (sc == NULL) {
162 printf("vx%d: cannot malloc.\n", unit);
163 return NULL;
164 }
165 bzero(sc, sizeof(struct vx_softc));
166
167 vx_softc[unit] = sc;
168 sc->unit = unit;
169 return (sc);
170 }
171
172 void
173 vxfree(sc)
174 struct vx_softc *sc;
175 {
176 vx_softc[sc->unit] = NULL;
177 free(sc, M_DEVBUF);
178 return;
179 }
180
181 int
182 vxattach(sc)
183 struct vx_softc *sc;
184 {
185 struct ifnet *ifp = &sc->arpcom.ac_if;
186 int i;
187
188 GO_WINDOW(0);
189 outw(VX_COMMAND, GLOBAL_RESET);
190 VX_BUSY_WAIT;
191
192 vxgetlink(sc);
193
194 /*
195 * Read the station address from the eeprom
196 */
197 GO_WINDOW(0);
198 for (i = 0; i < 3; i++) {
199 int x;
200 if (vxbusyeeprom(sc))
201 return 0;
202 outw(BASE + VX_W0_EEPROM_COMMAND, EEPROM_CMD_RD
203 | (EEPROM_OEM_ADDR_0 + i));
204 if (vxbusyeeprom(sc))
205 return 0;
206 x = inw(BASE + VX_W0_EEPROM_DATA);
207 sc->arpcom.ac_enaddr[(i << 1)] = x >> 8;
208 sc->arpcom.ac_enaddr[(i << 1) + 1] = x;
209 }
210
211 printf(" address %6D\n", sc->arpcom.ac_enaddr, ":");
212
213 ifp->if_unit = sc->unit;
214 ifp->if_name = "vx";
215 ifp->if_mtu = ETHERMTU;
216 ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
217 ifp->if_output = ether_output;
218 ifp->if_start = vxstart;
219 ifp->if_ioctl = vxioctl;
220 ifp->if_init = vxinit;
221 ifp->if_watchdog = vxwatchdog;
222 ifp->if_softc = sc;
223
224 if_attach(ifp);
225 ether_ifattach(ifp);
226
227 #if NBPFILTER > 0
228 bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
229 #endif
230
231 sc->tx_start_thresh = 20; /* probably a good starting point. */
232
233 vxstop(sc);
234
235 return 1;
236 }
237
238
239
240 /*
241 * The order in here seems important. Otherwise we may not receive
242 * interrupts. ?!
243 */
244 static void
245 vxinit(xsc)
246 void *xsc;
247 {
248 struct vx_softc *sc = (struct vx_softc *) xsc;
249 struct ifnet *ifp = &sc->arpcom.ac_if;
250 int i;
251
252 VX_BUSY_WAIT;
253
254 GO_WINDOW(2);
255
256 for (i = 0; i < 6; i++) /* Reload the ether_addr. */
257 outb(BASE + VX_W2_ADDR_0 + i, sc->arpcom.ac_enaddr[i]);
258
259 outw(BASE + VX_COMMAND, RX_RESET);
260 VX_BUSY_WAIT;
261 outw(BASE + VX_COMMAND, TX_RESET);
262 VX_BUSY_WAIT;
263
264 GO_WINDOW(1); /* Window 1 is operating window */
265 for (i = 0; i < 31; i++)
266 inb(BASE + VX_W1_TX_STATUS);
267
268 outw(BASE + VX_COMMAND,SET_RD_0_MASK | S_CARD_FAILURE |
269 S_RX_COMPLETE | S_TX_COMPLETE | S_TX_AVAIL);
270 outw(BASE + VX_COMMAND,SET_INTR_MASK | S_CARD_FAILURE |
271 S_RX_COMPLETE | S_TX_COMPLETE | S_TX_AVAIL);
272
273 /*
274 * Attempt to get rid of any stray interrupts that occured during
275 * configuration. On the i386 this isn't possible because one may
276 * already be queued. However, a single stray interrupt is
277 * unimportant.
278 */
279 outw(BASE + VX_COMMAND, ACK_INTR | 0xff);
280
281 vxsetfilter(sc);
282 vxsetlink(sc);
283
284 outw(BASE + VX_COMMAND, RX_ENABLE);
285 outw(BASE + VX_COMMAND, TX_ENABLE);
286
287 vxmbuffill((caddr_t) sc);
288
289 /* Interface is now `running', with no output active. */
290 ifp->if_flags |= IFF_RUNNING;
291 ifp->if_flags &= ~IFF_OACTIVE;
292
293 /* Attempt to start output, if any. */
294 vxstart(ifp);
295 }
296
297 static void
298 vxsetfilter(sc)
299 struct vx_softc *sc;
300 {
301 register struct ifnet *ifp = &sc->arpcom.ac_if;
302
303 GO_WINDOW(1); /* Window 1 is operating window */
304 outw(BASE + VX_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | FIL_BRDCST |
305 FIL_MULTICAST |
306 ((ifp->if_flags & IFF_PROMISC) ? FIL_PROMISC : 0 ));
307 }
308
309 static void
310 vxgetlink(sc)
311 struct vx_softc *sc;
312 {
313 int n, k;
314
315 GO_WINDOW(3);
316 sc->vx_connectors = inw(BASE + VX_W3_RESET_OPT) & 0x7f;
317 for (n = 0, k = 0; k < VX_CONNECTORS; k++) {
318 if (sc->vx_connectors & conn_tab[k].bit) {
319 if (n > 0) {
320 printf("/");
321 }
322 printf(conn_tab[k].name);
323 n++;
324 }
325 }
326 if (sc->vx_connectors == 0) {
327 printf("no connectors!");
328 return;
329 }
330 GO_WINDOW(3);
331 sc->vx_connector = (inl(BASE + VX_W3_INTERNAL_CFG)
332 & INTERNAL_CONNECTOR_MASK)
333 >> INTERNAL_CONNECTOR_BITS;
334 if (sc->vx_connector & 0x10) {
335 sc->vx_connector &= 0x0f;
336 printf("[*%s*]", conn_tab[sc->vx_connector].name);
337 printf(": disable 'auto select' with DOS util!", sc->unit);
338 } else {
339 printf("[*%s*]", conn_tab[sc->vx_connector].name);
340 }
341 }
342
343 static void
344 vxsetlink(sc)
345 struct vx_softc *sc;
346 {
347 register struct ifnet *ifp = &sc->arpcom.ac_if;
348 int i, j, k;
349 char *reason, *warning;
350 static short prev_flags;
351 static char prev_conn = -1;
352
353 if (prev_conn == -1) {
354 prev_conn = sc->vx_connector;
355 }
356
357 /*
358 * S.B.
359 *
360 * Now behavior was slightly changed:
361 *
362 * if any of flags link[0-2] is used and its connector is
363 * physically present the following connectors are used:
364 *
365 * link0 - AUI * highest precedence
366 * link1 - BNC
367 * link2 - UTP * lowest precedence
368 *
369 * If none of them is specified then
370 * connector specified in the EEPROM is used
371 * (if present on card or UTP if not).
372 */
373
374 i = sc->vx_connector; /* default in EEPROM */
375 reason = "default";
376 warning = 0;
377
378 if (ifp->if_flags & IFF_LINK0) {
379 if (sc->vx_connectors & conn_tab[CONNECTOR_AUI].bit) {
380 i = CONNECTOR_AUI;
381 reason = "link0";
382 } else {
383 warning = "aui not present! (link0)";
384 }
385 } else if (ifp->if_flags & IFF_LINK1) {
386 if (sc->vx_connectors & conn_tab[CONNECTOR_BNC].bit) {
387 i = CONNECTOR_BNC;
388 reason = "link1";
389 } else {
390 warning = "bnc not present! (link1)";
391 }
392 } else if (ifp->if_flags & IFF_LINK2) {
393 if (sc->vx_connectors & conn_tab[CONNECTOR_UTP].bit) {
394 i = CONNECTOR_UTP;
395 reason = "link2";
396 } else {
397 warning = "utp not present! (link2)";
398 }
399 } else if ((sc->vx_connectors & conn_tab[sc->vx_connector].bit) == 0) {
400 warning = "strange connector type in EEPROM.";
401 reason = "forced";
402 i = CONNECTOR_UTP;
403 }
404
405 /* Avoid unnecessary message. */
406 k = (prev_flags ^ ifp->if_flags) & (IFF_LINK0 | IFF_LINK1 | IFF_LINK2);
407 if ((k != 0) || (prev_conn != i)) {
408 if (warning != 0) {
409 printf("vx%d: warning: %s\n", sc->unit, warning);
410 }
411 printf("vx%d: selected %s. (%s)\n",
412 sc->unit, conn_tab[i].name, reason);
413 }
414
415 /* Set the selected connector. */
416 GO_WINDOW(3);
417 j = inl(BASE + VX_W3_INTERNAL_CFG) & ~INTERNAL_CONNECTOR_MASK;
418 outl(BASE + VX_W3_INTERNAL_CFG, j | (i <<INTERNAL_CONNECTOR_BITS));
419
420 /* First, disable all. */
421 outw(BASE + VX_COMMAND, STOP_TRANSCEIVER);
422 DELAY(800);
423 GO_WINDOW(4);
424 outw(BASE + VX_W4_MEDIA_TYPE, 0);
425
426 /* Second, enable the selected one. */
427 switch(i) {
428 case CONNECTOR_UTP:
429 GO_WINDOW(4);
430 outw(BASE + VX_W4_MEDIA_TYPE, ENABLE_UTP);
431 break;
432 case CONNECTOR_BNC:
433 outw(BASE + VX_COMMAND, START_TRANSCEIVER);
434 DELAY(800);
435 break;
436 case CONNECTOR_TX:
437 case CONNECTOR_FX:
438 GO_WINDOW(4);
439 outw(BASE + VX_W4_MEDIA_TYPE, LINKBEAT_ENABLE);
440 break;
441 default: /* AUI and MII fall here */
442 break;
443 }
444 GO_WINDOW(1);
445
446 prev_flags = ifp->if_flags;
447 prev_conn = i;
448 }
449
450 static void
451 vxstart(ifp)
452 struct ifnet *ifp;
453 {
454 register struct vx_softc *sc = vx_softc[ifp->if_unit];
455 register struct mbuf *m, *m0;
456 int sh, len, pad;
457
458 /* Don't transmit if interface is busy or not running */
459 if ((sc->arpcom.ac_if.if_flags & (IFF_RUNNING|IFF_OACTIVE)) != IFF_RUNNING)
460 return;
461
462 startagain:
463 /* Sneak a peek at the next packet */
464 m0 = ifp->if_snd.ifq_head;
465 if (m0 == 0) {
466 return;
467 }
468 /* We need to use m->m_pkthdr.len, so require the header */
469 if ((m0->m_flags & M_PKTHDR) == 0)
470 panic("vxstart: no header mbuf");
471 len = m0->m_pkthdr.len;
472
473 pad = (4 - len) & 3;
474
475 /*
476 * The 3c509 automatically pads short packets to minimum ethernet length,
477 * but we drop packets that are too large. Perhaps we should truncate
478 * them instead?
479 */
480 if (len + pad > ETHER_MAX_LEN) {
481 /* packet is obviously too large: toss it */
482 ++ifp->if_oerrors;
483 IF_DEQUEUE(&ifp->if_snd, m0);
484 m_freem(m0);
485 goto readcheck;
486 }
487 VX_BUSY_WAIT;
488 if (inw(BASE + VX_W1_FREE_TX) < len + pad + 4) {
489 outw(BASE + VX_COMMAND, SET_TX_AVAIL_THRESH | ((len + pad + 4) >> 2));
490 /* not enough room in FIFO */
491 if (inw(BASE + VX_W1_FREE_TX) < len + pad + 4) { /* make sure */
492 ifp->if_flags |= IFF_OACTIVE;
493 ifp->if_timer = 1;
494 return;
495 }
496 }
497 outw(BASE + VX_COMMAND, SET_TX_AVAIL_THRESH | (8188 >> 2));
498 IF_DEQUEUE(&ifp->if_snd, m0);
499 if (m0 == 0) { /* not really needed */
500 return;
501 }
502
503 VX_BUSY_WAIT;
504 outw(BASE + VX_COMMAND, SET_TX_START_THRESH |
505 ((len / 4 + sc->tx_start_thresh) >> 2));
506
507 #if NBPFILTER > 0
508 if (sc->arpcom.ac_if.if_bpf) {
509 bpf_mtap(&sc->arpcom.ac_if, m0);
510 }
511 #endif
512
513 /*
514 * Do the output at splhigh() so that an interrupt from another device
515 * won't cause a FIFO underrun.
516 */
517 sh = splhigh();
518
519 outl(BASE + VX_W1_TX_PIO_WR_1, len | TX_INDICATE);
520
521 for (m = m0; m != 0;) {
522 if (m->m_len > 3)
523 outsl(BASE + VX_W1_TX_PIO_WR_1, mtod(m, caddr_t), m->m_len / 4);
524 if (m->m_len & 3)
525 outsb(BASE + VX_W1_TX_PIO_WR_1,
526 mtod(m, caddr_t) + (m->m_len & ~3) , m->m_len & 3);
527 MFREE(m, m0);
528 m = m0;
529 }
530 while (pad--)
531 outb(BASE + VX_W1_TX_PIO_WR_1, 0); /* Padding */
532
533 splx(sh);
534
535 ++ifp->if_opackets;
536 ifp->if_timer = 1;
537
538 readcheck:
539 if ((inw(BASE + VX_W1_RX_STATUS) & ERR_INCOMPLETE) == 0) {
540 /* We received a complete packet. */
541
542 if ((inw(BASE + VX_STATUS) & S_INTR_LATCH) == 0) {
543 /*
544 * No interrupt, read the packet and continue
545 * Is this supposed to happen? Is my motherboard
546 * completely busted?
547 */
548 vxread(sc);
549 } else
550 /* Got an interrupt, return so that it gets serviced. */
551 return;
552 } else {
553 /* Check if we are stuck and reset [see XXX comment] */
554 if (vxstatus(sc)) {
555 if (ifp->if_flags & IFF_DEBUG)
556 printf("vx%d: adapter reset\n", ifp->if_unit);
557 vxreset(sc);
558 }
559 }
560
561 goto startagain;
562 }
563
564 /*
565 * XXX: The 3c509 card can get in a mode where both the fifo status bit
566 * FIFOS_RX_OVERRUN and the status bit ERR_INCOMPLETE are set
567 * We detect this situation and we reset the adapter.
568 * It happens at times when there is a lot of broadcast traffic
569 * on the cable (once in a blue moon).
570 */
571 static int
572 vxstatus(sc)
573 struct vx_softc *sc;
574 {
575 int fifost;
576
577 /*
578 * Check the FIFO status and act accordingly
579 */
580 GO_WINDOW(4);
581 fifost = inw(BASE + VX_W4_FIFO_DIAG);
582 GO_WINDOW(1);
583
584 if (fifost & FIFOS_RX_UNDERRUN) {
585 if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
586 printf("vx%d: RX underrun\n", sc->unit);
587 vxreset(sc);
588 return 0;
589 }
590
591 if (fifost & FIFOS_RX_STATUS_OVERRUN) {
592 if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
593 printf("vx%d: RX Status overrun\n", sc->unit);
594 return 1;
595 }
596
597 if (fifost & FIFOS_RX_OVERRUN) {
598 if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
599 printf("vx%d: RX overrun\n", sc->unit);
600 return 1;
601 }
602
603 if (fifost & FIFOS_TX_OVERRUN) {
604 if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
605 printf("vx%d: TX overrun\n", sc->unit);
606 vxreset(sc);
607 return 0;
608 }
609
610 return 0;
611 }
612
613 static void
614 vxtxstat(sc)
615 struct vx_softc *sc;
616 {
617 int i;
618
619 /*
620 * We need to read+write TX_STATUS until we get a 0 status
621 * in order to turn off the interrupt flag.
622 */
623 while ((i = inb(BASE + VX_W1_TX_STATUS)) & TXS_COMPLETE) {
624 outb(BASE + VX_W1_TX_STATUS, 0x0);
625
626 if (i & TXS_JABBER) {
627 ++sc->arpcom.ac_if.if_oerrors;
628 if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
629 printf("vx%d: jabber (%x)\n", sc->unit, i);
630 vxreset(sc);
631 } else if (i & TXS_UNDERRUN) {
632 ++sc->arpcom.ac_if.if_oerrors;
633 if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
634 printf("vx%d: fifo underrun (%x) @%d\n",
635 sc->unit, i, sc->tx_start_thresh);
636 if (sc->tx_succ_ok < 100)
637 sc->tx_start_thresh = min(ETHER_MAX_LEN, sc->tx_start_thresh + 20);
638 sc->tx_succ_ok = 0;
639 vxreset(sc);
640 } else if (i & TXS_MAX_COLLISION) {
641 ++sc->arpcom.ac_if.if_collisions;
642 outw(BASE + VX_COMMAND, TX_ENABLE);
643 sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
644 } else
645 sc->tx_succ_ok = (sc->tx_succ_ok+1) & 127;
646 }
647 }
648
649 void
650 vxintr(sc)
651 struct vx_softc *sc;
652 {
653 register short status;
654 struct ifnet *ifp = &sc->arpcom.ac_if;
655
656 for (;;) {
657 outw(BASE + VX_COMMAND, C_INTR_LATCH);
658
659 status = inw(BASE + VX_STATUS);
660
661 if ((status & (S_TX_COMPLETE | S_TX_AVAIL |
662 S_RX_COMPLETE | S_CARD_FAILURE)) == 0)
663 break;
664
665 /*
666 * Acknowledge any interrupts. It's important that we do this
667 * first, since there would otherwise be a race condition.
668 * Due to the i386 interrupt queueing, we may get spurious
669 * interrupts occasionally.
670 */
671 outw(BASE + VX_COMMAND, ACK_INTR | status);
672
673 if (status & S_RX_COMPLETE)
674 vxread(sc);
675 if (status & S_TX_AVAIL) {
676 ifp->if_timer = 0;
677 sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
678 vxstart(&sc->arpcom.ac_if);
679 }
680 if (status & S_CARD_FAILURE) {
681 printf("vx%d: adapter failure (%x)\n", sc->unit, status);
682 ifp->if_timer = 0;
683 vxreset(sc);
684 return;
685 }
686 if (status & S_TX_COMPLETE) {
687 ifp->if_timer = 0;
688 vxtxstat(sc);
689 vxstart(ifp);
690 }
691 }
692
693 /* no more interrupts */
694 return;
695 }
696
697 static void
698 vxread(sc)
699 struct vx_softc *sc;
700 {
701 struct ifnet *ifp = &sc->arpcom.ac_if;
702 struct mbuf *m;
703 struct ether_header *eh;
704 u_int len;
705
706 len = inw(BASE + VX_W1_RX_STATUS);
707
708 again:
709
710 if (ifp->if_flags & IFF_DEBUG) {
711 int err = len & ERR_MASK;
712 char *s = NULL;
713
714 if (len & ERR_INCOMPLETE)
715 s = "incomplete packet";
716 else if (err == ERR_OVERRUN)
717 s = "packet overrun";
718 else if (err == ERR_RUNT)
719 s = "runt packet";
720 else if (err == ERR_ALIGNMENT)
721 s = "bad alignment";
722 else if (err == ERR_CRC)
723 s = "bad crc";
724 else if (err == ERR_OVERSIZE)
725 s = "oversized packet";
726 else if (err == ERR_DRIBBLE)
727 s = "dribble bits";
728
729 if (s)
730 printf("vx%d: %s\n", sc->unit, s);
731 }
732
733 if (len & ERR_INCOMPLETE)
734 return;
735
736 if (len & ERR_RX) {
737 ++ifp->if_ierrors;
738 goto abort;
739 }
740
741 len &= RX_BYTES_MASK; /* Lower 11 bits = RX bytes. */
742
743 /* Pull packet off interface. */
744 m = vxget(sc, len);
745 if (m == 0) {
746 ifp->if_ierrors++;
747 goto abort;
748 }
749
750 ++ifp->if_ipackets;
751
752 /* We assume the header fit entirely in one mbuf. */
753 eh = mtod(m, struct ether_header *);
754
755 #if NBPFILTER > 0
756 /*
757 * Check if there's a BPF listener on this interface.
758 * If so, hand off the raw packet to BPF.
759 */
760 if (sc->arpcom.ac_if.if_bpf) {
761 bpf_mtap(&sc->arpcom.ac_if, m);
762 }
763 #endif
764
765 /*
766 * XXX: Some cards seem to be in promiscous mode all the time.
767 * we need to make sure we only get our own stuff always.
768 * bleah!
769 */
770
771 if ((eh->ether_dhost[0] & 1) == 0 && /* !mcast and !bcast */
772 bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr,
773 sizeof(eh->ether_dhost)) != 0) {
774 m_freem(m);
775 return;
776 }
777 /* We assume the header fit entirely in one mbuf. */
778 m_adj(m, sizeof(struct ether_header));
779 ether_input(ifp, eh, m);
780
781 /*
782 * In periods of high traffic we can actually receive enough
783 * packets so that the fifo overrun bit will be set at this point,
784 * even though we just read a packet. In this case we
785 * are not going to receive any more interrupts. We check for
786 * this condition and read again until the fifo is not full.
787 * We could simplify this test by not using vxstatus(), but
788 * rechecking the RX_STATUS register directly. This test could
789 * result in unnecessary looping in cases where there is a new
790 * packet but the fifo is not full, but it will not fix the
791 * stuck behavior.
792 *
793 * Even with this improvement, we still get packet overrun errors
794 * which are hurting performance. Maybe when I get some more time
795 * I'll modify vxread() so that it can handle RX_EARLY interrupts.
796 */
797 if (vxstatus(sc)) {
798 len = inw(BASE + VX_W1_RX_STATUS);
799 /* Check if we are stuck and reset [see XXX comment] */
800 if (len & ERR_INCOMPLETE) {
801 if (ifp->if_flags & IFF_DEBUG)
802 printf("vx%d: adapter reset\n", sc->unit);
803 vxreset(sc);
804 return;
805 }
806 goto again;
807 }
808
809 return;
810
811 abort:
812 outw(BASE + VX_COMMAND, RX_DISCARD_TOP_PACK);
813 }
814
815 static struct mbuf *
816 vxget(sc, totlen)
817 struct vx_softc *sc;
818 u_int totlen;
819 {
820 struct ifnet *ifp = &sc->arpcom.ac_if;
821 struct mbuf *top, **mp, *m;
822 int len;
823 int sh;
824
825 m = sc->mb[sc->next_mb];
826 sc->mb[sc->next_mb] = 0;
827 if (m == 0) {
828 MGETHDR(m, M_DONTWAIT, MT_DATA);
829 if (m == 0)
830 return 0;
831 } else {
832 /* If the queue is no longer full, refill. */
833 if (sc->last_mb == sc->next_mb)
834 timeout(vxmbuffill, sc, 1);
835 /* Convert one of our saved mbuf's. */
836 sc->next_mb = (sc->next_mb + 1) % MAX_MBS;
837 m->m_data = m->m_pktdat;
838 m->m_flags = M_PKTHDR;
839 }
840 m->m_pkthdr.rcvif = ifp;
841 m->m_pkthdr.len = totlen;
842 len = MHLEN;
843 top = 0;
844 mp = ⊤
845
846 /*
847 * We read the packet at splhigh() so that an interrupt from another
848 * device doesn't cause the card's buffer to overflow while we're
849 * reading it. We may still lose packets at other times.
850 */
851 sh = splhigh();
852
853 /*
854 * Since we don't set allowLargePackets bit in MacControl register,
855 * we can assume that totlen <= 1500bytes.
856 * The while loop will be performed iff we have a packet with
857 * MLEN < m_len < MINCLSIZE.
858 */
859 while (totlen > 0) {
860 if (top) {
861 m = sc->mb[sc->next_mb];
862 sc->mb[sc->next_mb] = 0;
863 if (m == 0) {
864 MGET(m, M_DONTWAIT, MT_DATA);
865 if (m == 0) {
866 splx(sh);
867 m_freem(top);
868 return 0;
869 }
870 } else {
871 sc->next_mb = (sc->next_mb + 1) % MAX_MBS;
872 }
873 len = MLEN;
874 }
875 if (totlen >= MINCLSIZE) {
876 MCLGET(m, M_DONTWAIT);
877 if (m->m_flags & M_EXT)
878 len = MCLBYTES;
879 }
880 len = min(totlen, len);
881 if (len > 3)
882 insl(BASE + VX_W1_RX_PIO_RD_1, mtod(m, u_int32_t *), len / 4);
883 if (len & 3) {
884 insb(BASE + VX_W1_RX_PIO_RD_1, mtod(m, u_int8_t *) + (len & ~3),
885 len & 3);
886 }
887 m->m_len = len;
888 totlen -= len;
889 *mp = m;
890 mp = &m->m_next;
891 }
892
893 outw(BASE +VX_COMMAND, RX_DISCARD_TOP_PACK);
894
895 splx(sh);
896
897 return top;
898 }
899
900
901 static int
902 vxioctl(ifp, cmd, data)
903 register struct ifnet *ifp;
904 int cmd;
905 caddr_t data;
906 {
907 struct vx_softc *sc = vx_softc[ifp->if_unit];
908 struct ifreq *ifr = (struct ifreq *) data;
909 int s, error = 0;
910
911 s = splimp();
912
913 switch (cmd) {
914 case SIOCSIFADDR:
915 case SIOCGIFADDR:
916 ether_ioctl(ifp, cmd, data);
917 break;
918
919 case SIOCSIFFLAGS:
920 if ((ifp->if_flags & IFF_UP) == 0 &&
921 (ifp->if_flags & IFF_RUNNING) != 0) {
922 /*
923 * If interface is marked up and it is stopped, then
924 * start it.
925 */
926 vxstop(sc);
927 ifp->if_flags &= ~IFF_RUNNING;
928 } else if ((ifp->if_flags & IFF_UP) != 0 &&
929 (ifp->if_flags & IFF_RUNNING) == 0) {
930 /*
931 * If interface is marked up and it is stopped, then
932 * start it.
933 */
934 vxinit(sc);
935 } else {
936 /*
937 * deal with flags changes:
938 * IFF_MULTICAST, IFF_PROMISC,
939 * IFF_LINK0, IFF_LINK1,
940 */
941 vxsetfilter(sc);
942 vxsetlink(sc);
943 }
944 break;
945
946 case SIOCSIFMTU:
947 /*
948 * Set the interface MTU.
949 */
950 if (ifr->ifr_mtu > ETHERMTU) {
951 error = EINVAL;
952 } else {
953 ifp->if_mtu = ifr->ifr_mtu;
954 }
955 break;
956
957 case SIOCADDMULTI:
958 case SIOCDELMULTI:
959 /*
960 * Multicast list has changed; set the hardware filter
961 * accordingly.
962 */
963 vxreset(sc);
964 error = 0;
965 break;
966
967
968 default:
969 error = EINVAL;
970 }
971
972 splx(s);
973
974 return (error);
975 }
976
977 static void
978 vxreset(sc)
979 struct vx_softc *sc;
980 {
981 int s;
982 s = splimp();
983
984 vxstop(sc);
985 vxinit(sc);
986 splx(s);
987 }
988
989 static void
990 vxwatchdog(ifp)
991 struct ifnet *ifp;
992 {
993 struct vx_softc *sc = vx_softc[ifp->if_unit];
994
995 if (ifp->if_flags & IFF_DEBUG)
996 printf("vx%d: device timeout\n", ifp->if_unit);
997 ifp->if_flags &= ~IFF_OACTIVE;
998 vxstart(ifp);
999 vxintr(sc);
1000 }
1001
1002 void
1003 vxstop(sc)
1004 struct vx_softc *sc;
1005 {
1006 struct ifnet *ifp = &sc->arpcom.ac_if;
1007
1008 ifp->if_timer = 0;
1009
1010 outw(BASE + VX_COMMAND, RX_DISABLE);
1011 outw(BASE + VX_COMMAND, RX_DISCARD_TOP_PACK);
1012 VX_BUSY_WAIT;
1013 outw(BASE + VX_COMMAND, TX_DISABLE);
1014 outw(BASE + VX_COMMAND, STOP_TRANSCEIVER);
1015 DELAY(800);
1016 outw(BASE + VX_COMMAND, RX_RESET);
1017 VX_BUSY_WAIT;
1018 outw(BASE + VX_COMMAND, TX_RESET);
1019 VX_BUSY_WAIT;
1020 outw(BASE + VX_COMMAND, C_INTR_LATCH);
1021 outw(BASE + VX_COMMAND, SET_RD_0_MASK);
1022 outw(BASE + VX_COMMAND, SET_INTR_MASK);
1023 outw(BASE + VX_COMMAND, SET_RX_FILTER);
1024
1025 vxmbufempty(sc);
1026 }
1027
1028 int
1029 vxbusyeeprom(sc)
1030 struct vx_softc *sc;
1031 {
1032 int j, i = 100;
1033
1034 while (i--) {
1035 j = inw(BASE + VX_W0_EEPROM_COMMAND);
1036 if (j & EEPROM_BUSY)
1037 DELAY(100);
1038 else
1039 break;
1040 }
1041 if (!i) {
1042 printf("vx%d: eeprom failed to come ready\n", sc->unit);
1043 return (1);
1044 }
1045 return (0);
1046 }
1047
1048 static void
1049 vxmbuffill(sp)
1050 void *sp;
1051 {
1052 struct vx_softc *sc = (struct vx_softc *) sp;
1053 int s, i;
1054
1055 s = splimp();
1056 i = sc->last_mb;
1057 do {
1058 if (sc->mb[i] == NULL)
1059 MGET(sc->mb[i], M_DONTWAIT, MT_DATA);
1060 if (sc->mb[i] == NULL)
1061 break;
1062 i = (i + 1) % MAX_MBS;
1063 } while (i != sc->next_mb);
1064 sc->last_mb = i;
1065 /* If the queue was not filled, try again. */
1066 if (sc->last_mb != sc->next_mb)
1067 timeout(vxmbuffill, sc, 1);
1068 splx(s);
1069 }
1070
1071 static void
1072 vxmbufempty(sc)
1073 struct vx_softc *sc;
1074 {
1075 int s, i;
1076
1077 s = splimp();
1078 for (i = 0; i < MAX_MBS; i++) {
1079 if (sc->mb[i]) {
1080 m_freem(sc->mb[i]);
1081 sc->mb[i] = NULL;
1082 }
1083 }
1084 sc->last_mb = sc->next_mb = 0;
1085 untimeout(vxmbuffill, sc);
1086 splx(s);
1087 }
1088
1089 #endif /* NVX > 0 */
Cache object: 353abeefc20baad906f3df06b5246232
|