Annotation of sys/dev/isa/if_el.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: if_el.c,v 1.20 2006/03/25 22:41:44 djm Exp $ */
2: /* $NetBSD: if_el.c,v 1.39 1996/05/12 23:52:32 mycroft Exp $ */
3:
4: /*
5: * Copyright (c) 1994, Matthew E. Kimmel. Permission is hereby granted
6: * to use, copy, modify and distribute this software provided that both
7: * the copyright notice and this permission notice appear in all copies
8: * of the software, derivative works or modified versions, and any
9: * portions thereof.
10: */
11:
12: /*
13: * 3COM Etherlink 3C501 device driver
14: */
15:
16: /*
17: * Bugs/possible improvements:
18: * - Does not currently support DMA
19: * - Does not currently support multicasts
20: */
21:
22: #include "bpfilter.h"
23:
24: #include <sys/param.h>
25: #include <sys/systm.h>
26: #include <sys/errno.h>
27: #include <sys/ioctl.h>
28: #include <sys/mbuf.h>
29: #include <sys/socket.h>
30: #include <sys/syslog.h>
31: #include <sys/device.h>
32:
33: #include <net/if.h>
34: #include <net/if_dl.h>
35: #include <net/if_types.h>
36:
37: #ifdef INET
38: #include <netinet/in.h>
39: #include <netinet/in_systm.h>
40: #include <netinet/in_var.h>
41: #include <netinet/ip.h>
42: #include <netinet/if_ether.h>
43: #endif
44:
45: #if NBPFILTER > 0
46: #include <net/bpf.h>
47: #endif
48:
49: #include <machine/cpu.h>
50: #include <machine/intr.h>
51: #include <machine/pio.h>
52:
53: #include <dev/isa/isavar.h>
54: #include <dev/isa/if_elreg.h>
55:
56: /* for debugging convenience */
57: #ifdef EL_DEBUG
58: #define dprintf(x) printf x
59: #else
60: #define dprintf(x)
61: #endif
62:
63: /*
64: * per-line info and status
65: */
66: struct el_softc {
67: struct device sc_dev;
68: void *sc_ih;
69:
70: struct arpcom sc_arpcom; /* ethernet common */
71: int sc_iobase; /* base I/O addr */
72: };
73:
74: /*
75: * prototypes
76: */
77: int elintr(void *);
78: void elinit(struct el_softc *);
79: int elioctl(struct ifnet *, u_long, caddr_t);
80: void elstart(struct ifnet *);
81: void elwatchdog(struct ifnet *);
82: void elreset(struct el_softc *);
83: void elstop(struct el_softc *);
84: static int el_xmit(struct el_softc *);
85: void elread(struct el_softc *, int);
86: struct mbuf *elget(struct el_softc *sc, int);
87: static inline void el_hardreset(struct el_softc *);
88:
89: int elprobe(struct device *, void *, void *);
90: void elattach(struct device *, struct device *, void *);
91:
92: struct cfattach el_ca = {
93: sizeof(struct el_softc), elprobe, elattach
94: };
95:
96: struct cfdriver el_cd = {
97: NULL, "el", DV_IFNET
98: };
99:
100: /*
101: * Probe routine.
102: *
103: * See if the card is there and at the right place.
104: * (XXX - cgd -- needs help)
105: */
106: int
107: elprobe(parent, match, aux)
108: struct device *parent;
109: void *match, *aux;
110: {
111: struct el_softc *sc = match;
112: struct isa_attach_args *ia = aux;
113: int iobase = ia->ia_iobase;
114: u_char station_addr[ETHER_ADDR_LEN];
115: int i;
116:
117: /* First check the base. */
118: if (iobase < 0x280 || iobase > 0x3f0)
119: return 0;
120:
121: /* Grab some info for our structure. */
122: sc->sc_iobase = iobase;
123:
124: /*
125: * Now attempt to grab the station address from the PROM and see if it
126: * contains the 3com vendor code.
127: */
128: dprintf(("Probing 3c501 at 0x%x...\n", iobase));
129:
130: /* Reset the board. */
131: dprintf(("Resetting board...\n"));
132: outb(iobase+EL_AC, EL_AC_RESET);
133: delay(5);
134: outb(iobase+EL_AC, 0);
135:
136: /* Now read the address. */
137: dprintf(("Reading station address...\n"));
138: for (i = 0; i < ETHER_ADDR_LEN; i++) {
139: outb(iobase+EL_GPBL, i);
140: station_addr[i] = inb(iobase+EL_EAW);
141: }
142: dprintf(("Address is %s\n", ether_sprintf(station_addr)));
143:
144: /*
145: * If the vendor code is ok, return a 1. We'll assume that whoever
146: * configured this system is right about the IRQ.
147: */
148: if (station_addr[0] != 0x02 || station_addr[1] != 0x60 ||
149: station_addr[2] != 0x8c) {
150: dprintf(("Bad vendor code.\n"));
151: return 0;
152: }
153:
154: dprintf(("Vendor code ok.\n"));
155: /* Copy the station address into the arpcom structure. */
156: bcopy(station_addr, sc->sc_arpcom.ac_enaddr, ETHER_ADDR_LEN);
157:
158: ia->ia_iosize = 4; /* XXX */
159: ia->ia_msize = 0;
160: return 1;
161: }
162:
163: /*
164: * Attach the interface to the kernel data structures. By the time this is
165: * called, we know that the card exists at the given I/O address. We still
166: * assume that the IRQ given is correct.
167: */
168: void
169: elattach(parent, self, aux)
170: struct device *parent, *self;
171: void *aux;
172: {
173: struct el_softc *sc = (void *)self;
174: struct isa_attach_args *ia = aux;
175: struct ifnet *ifp = &sc->sc_arpcom.ac_if;
176:
177: dprintf(("Attaching %s...\n", sc->sc_dev.dv_xname));
178:
179: /* Stop the board. */
180: elstop(sc);
181:
182: /* Initialize ifnet structure. */
183: bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
184: ifp->if_softc = sc;
185: ifp->if_start = elstart;
186: ifp->if_ioctl = elioctl;
187: ifp->if_watchdog = elwatchdog;
188: ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
189: IFQ_SET_READY(&ifp->if_snd);
190:
191: /* Now we can attach the interface. */
192: dprintf(("Attaching interface...\n"));
193: if_attach(ifp);
194: ether_ifattach(ifp);
195:
196: /* Print out some information for the user. */
197: printf(": address %s\n", ether_sprintf(sc->sc_arpcom.ac_enaddr));
198:
199: sc->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq, IST_EDGE,
200: IPL_NET, elintr, sc, sc->sc_dev.dv_xname);
201:
202: dprintf(("elattach() finished.\n"));
203: }
204:
205: /*
206: * Reset interface.
207: */
208: void
209: elreset(sc)
210: struct el_softc *sc;
211: {
212: int s;
213:
214: dprintf(("elreset()\n"));
215: s = splnet();
216: elstop(sc);
217: elinit(sc);
218: splx(s);
219: }
220:
221: /*
222: * Stop interface.
223: */
224: void
225: elstop(sc)
226: struct el_softc *sc;
227: {
228:
229: outb(sc->sc_iobase+EL_AC, 0);
230: }
231:
232: /*
233: * Do a hardware reset of the board, and upload the ethernet address again in
234: * case the board forgets.
235: */
236: static inline void
237: el_hardreset(sc)
238: struct el_softc *sc;
239: {
240: int iobase = sc->sc_iobase;
241: int i;
242:
243: outb(iobase+EL_AC, EL_AC_RESET);
244: delay(5);
245: outb(iobase+EL_AC, 0);
246:
247: for (i = 0; i < ETHER_ADDR_LEN; i++)
248: outb(iobase+i, sc->sc_arpcom.ac_enaddr[i]);
249: }
250:
251: /*
252: * Initialize interface.
253: */
254: void
255: elinit(sc)
256: struct el_softc *sc;
257: {
258: struct ifnet *ifp = &sc->sc_arpcom.ac_if;
259: int iobase = sc->sc_iobase;
260:
261: /* First, reset the board. */
262: el_hardreset(sc);
263:
264: /* Configure rx. */
265: dprintf(("Configuring rx...\n"));
266: if (ifp->if_flags & IFF_PROMISC)
267: outb(iobase+EL_RXC, EL_RXC_AGF | EL_RXC_DSHORT | EL_RXC_DDRIB | EL_RXC_DOFLOW | EL_RXC_PROMISC);
268: else
269: outb(iobase+EL_RXC, EL_RXC_AGF | EL_RXC_DSHORT | EL_RXC_DDRIB | EL_RXC_DOFLOW | EL_RXC_ABROAD);
270: outb(iobase+EL_RBC, 0);
271:
272: /* Configure TX. */
273: dprintf(("Configuring tx...\n"));
274: outb(iobase+EL_TXC, 0);
275:
276: /* Start reception. */
277: dprintf(("Starting reception...\n"));
278: outb(iobase+EL_AC, EL_AC_IRQE | EL_AC_RX);
279:
280: /* Set flags appropriately. */
281: ifp->if_flags |= IFF_RUNNING;
282: ifp->if_flags &= ~IFF_OACTIVE;
283:
284: /* And start output. */
285: elstart(ifp);
286: }
287:
288: /*
289: * Start output on interface. Get datagrams from the queue and output them,
290: * giving the receiver a chance between datagrams. Call only from splnet or
291: * interrupt level!
292: */
293: void
294: elstart(ifp)
295: struct ifnet *ifp;
296: {
297: struct el_softc *sc = ifp->if_softc;
298: int iobase = sc->sc_iobase;
299: struct mbuf *m, *m0;
300: int s, i, off, retries;
301:
302: dprintf(("elstart()...\n"));
303: s = splnet();
304:
305: /* Don't do anything if output is active. */
306: if ((ifp->if_flags & IFF_OACTIVE) != 0) {
307: splx(s);
308: return;
309: }
310:
311: ifp->if_flags |= IFF_OACTIVE;
312:
313: /*
314: * The main loop. They warned me against endless loops, but would I
315: * listen? NOOO....
316: */
317: for (;;) {
318: /* Dequeue the next datagram. */
319: IFQ_DEQUEUE(&ifp->if_snd, m0);
320:
321: /* If there's nothing to send, return. */
322: if (m0 == 0)
323: break;
324:
325: #if NBPFILTER > 0
326: /* Give the packet to the bpf, if any. */
327: if (ifp->if_bpf)
328: bpf_mtap(ifp->if_bpf, m0, BPF_DIRECTION_OUT);
329: #endif
330:
331: /* Disable the receiver. */
332: outb(iobase+EL_AC, EL_AC_HOST);
333: outb(iobase+EL_RBC, 0);
334:
335: /* Transfer datagram to board. */
336: dprintf(("el: xfr pkt length=%d...\n", m0->m_pkthdr.len));
337: off = EL_BUFSIZ - max(m0->m_pkthdr.len, ETHER_MIN_LEN);
338: outb(iobase+EL_GPBL, off);
339: outb(iobase+EL_GPBH, off >> 8);
340:
341: /* Copy the datagram to the buffer. */
342: for (m = m0; m != 0; m = m->m_next)
343: outsb(iobase+EL_BUF, mtod(m, caddr_t), m->m_len);
344: for (i = 0;
345: i < ETHER_MIN_LEN - ETHER_CRC_LEN - m0->m_pkthdr.len; i++)
346: outb(iobase+EL_BUF, 0);
347:
348: m_freem(m0);
349:
350: /* Now transmit the datagram. */
351: retries = 0;
352: for (;;) {
353: outb(iobase+EL_GPBL, off);
354: outb(iobase+EL_GPBH, off >> 8);
355: if (el_xmit(sc)) {
356: ifp->if_oerrors++;
357: break;
358: }
359: /* Check out status. */
360: i = inb(iobase+EL_TXS);
361: dprintf(("tx status=0x%x\n", i));
362: if ((i & EL_TXS_READY) == 0) {
363: dprintf(("el: err txs=%x\n", i));
364: if (i & (EL_TXS_COLL | EL_TXS_COLL16)) {
365: ifp->if_collisions++;
366: if ((i & EL_TXC_DCOLL16) == 0 &&
367: retries < 15) {
368: retries++;
369: outb(iobase+EL_AC, EL_AC_HOST);
370: }
371: } else {
372: ifp->if_oerrors++;
373: break;
374: }
375: } else {
376: ifp->if_opackets++;
377: break;
378: }
379: }
380:
381: /*
382: * Now give the card a chance to receive.
383: * Gotta love 3c501s...
384: */
385: (void)inb(iobase+EL_AS);
386: outb(iobase+EL_AC, EL_AC_IRQE | EL_AC_RX);
387: splx(s);
388: /* Interrupt here. */
389: s = splnet();
390: }
391:
392: (void)inb(iobase+EL_AS);
393: outb(iobase+EL_AC, EL_AC_IRQE | EL_AC_RX);
394: ifp->if_flags &= ~IFF_OACTIVE;
395: splx(s);
396: }
397:
398: /*
399: * This function actually attempts to transmit a datagram downloaded to the
400: * board. Call at splnet or interrupt, after downloading data! Returns 0 on
401: * success, non-0 on failure.
402: */
403: static int
404: el_xmit(sc)
405: struct el_softc *sc;
406: {
407: int iobase = sc->sc_iobase;
408: int i;
409:
410: /*
411: * XXX
412: * This busy-waits for the tx completion. Can we get an interrupt
413: * instead?
414: */
415:
416: dprintf(("el: xmit..."));
417: outb(iobase+EL_AC, EL_AC_TXFRX);
418: i = 20000;
419: while ((inb(iobase+EL_AS) & EL_AS_TXBUSY) && (i > 0))
420: i--;
421: if (i == 0) {
422: dprintf(("tx not ready\n"));
423: return -1;
424: }
425: dprintf(("%d cycles.\n", 20000 - i));
426: return 0;
427: }
428:
429: /*
430: * Controller interrupt.
431: */
432: int
433: elintr(arg)
434: void *arg;
435: {
436: register struct el_softc *sc = arg;
437: int iobase = sc->sc_iobase;
438: int rxstat, len;
439:
440: dprintf(("elintr: "));
441:
442: /* Check board status. */
443: if ((inb(iobase+EL_AS) & EL_AS_RXBUSY) != 0) {
444: (void)inb(iobase+EL_RXC);
445: outb(iobase+EL_AC, EL_AC_IRQE | EL_AC_RX);
446: return 0;
447: }
448:
449: for (;;) {
450: rxstat = inb(iobase+EL_RXS);
451: if (rxstat & EL_RXS_STALE)
452: break;
453:
454: /* If there's an overflow, reinit the board. */
455: if ((rxstat & EL_RXS_NOFLOW) == 0) {
456: dprintf(("overflow.\n"));
457: el_hardreset(sc);
458: /* Put board back into receive mode. */
459: if (sc->sc_arpcom.ac_if.if_flags & IFF_PROMISC)
460: outb(iobase+EL_RXC, EL_RXC_AGF | EL_RXC_DSHORT | EL_RXC_DDRIB | EL_RXC_DOFLOW | EL_RXC_PROMISC);
461: else
462: outb(iobase+EL_RXC, EL_RXC_AGF | EL_RXC_DSHORT | EL_RXC_DDRIB | EL_RXC_DOFLOW | EL_RXC_ABROAD);
463: (void)inb(iobase+EL_AS);
464: outb(iobase+EL_RBC, 0);
465: break;
466: }
467:
468: /* Incoming packet. */
469: len = inb(iobase+EL_RBL);
470: len |= inb(iobase+EL_RBH) << 8;
471: dprintf(("receive len=%d rxstat=%x ", len, rxstat));
472: outb(iobase+EL_AC, EL_AC_HOST);
473:
474: /* Pass data up to upper levels. */
475: elread(sc, len);
476:
477: /* Is there another packet? */
478: if ((inb(iobase+EL_AS) & EL_AS_RXBUSY) != 0)
479: break;
480:
481: dprintf(("<rescan> "));
482: }
483:
484: (void)inb(iobase+EL_RXC);
485: outb(iobase+EL_AC, EL_AC_IRQE | EL_AC_RX);
486: return 1;
487: }
488:
489: /*
490: * Pass a packet to the higher levels.
491: */
492: void
493: elread(sc, len)
494: register struct el_softc *sc;
495: int len;
496: {
497: struct ifnet *ifp = &sc->sc_arpcom.ac_if;
498: struct mbuf *m;
499:
500: if (len <= sizeof(struct ether_header) ||
501: len > ETHER_MAX_LEN) {
502: printf("%s: invalid packet size %d; dropping\n",
503: sc->sc_dev.dv_xname, len);
504: ifp->if_ierrors++;
505: return;
506: }
507:
508: /* Pull packet off interface. */
509: m = elget(sc, len);
510: if (m == 0) {
511: ifp->if_ierrors++;
512: return;
513: }
514:
515: ifp->if_ipackets++;
516:
517: #if NBPFILTER > 0
518: /*
519: * Check if there's a BPF listener on this interface.
520: * If so, hand off the raw packet to BPF.
521: */
522: if (ifp->if_bpf)
523: bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN);
524: #endif
525:
526: ether_input_mbuf(ifp, m);
527: }
528:
529: /*
530: * Pull read data off a interface. Len is length of data, with local net
531: * header stripped. We copy the data into mbufs. When full cluster sized
532: * units are present we copy into clusters.
533: */
534: struct mbuf *
535: elget(sc, totlen)
536: struct el_softc *sc;
537: int totlen;
538: {
539: struct ifnet *ifp = &sc->sc_arpcom.ac_if;
540: int iobase = sc->sc_iobase;
541: struct mbuf *top, **mp, *m;
542: int len;
543:
544: MGETHDR(m, M_DONTWAIT, MT_DATA);
545: if (m == 0)
546: return 0;
547: m->m_pkthdr.rcvif = ifp;
548: m->m_pkthdr.len = totlen;
549: len = MHLEN;
550: top = 0;
551: mp = ⊤
552:
553: outb(iobase+EL_GPBL, 0);
554: outb(iobase+EL_GPBH, 0);
555:
556: while (totlen > 0) {
557: if (top) {
558: MGET(m, M_DONTWAIT, MT_DATA);
559: if (m == 0) {
560: m_freem(top);
561: return 0;
562: }
563: len = MLEN;
564: }
565: if (totlen >= MINCLSIZE) {
566: MCLGET(m, M_DONTWAIT);
567: if (m->m_flags & M_EXT)
568: len = MCLBYTES;
569: }
570: m->m_len = len = min(totlen, len);
571: insb(iobase+EL_BUF, mtod(m, caddr_t), len);
572: totlen -= len;
573: *mp = m;
574: mp = &m->m_next;
575: }
576:
577: outb(iobase+EL_RBC, 0);
578: outb(iobase+EL_AC, EL_AC_RX);
579:
580: return top;
581: }
582:
583: /*
584: * Process an ioctl request. This code needs some work - it looks pretty ugly.
585: */
586: int
587: elioctl(ifp, cmd, data)
588: register struct ifnet *ifp;
589: u_long cmd;
590: caddr_t data;
591: {
592: struct el_softc *sc = ifp->if_softc;
593: struct ifaddr *ifa = (struct ifaddr *)data;
594: int s, error = 0;
595:
596: s = splnet();
597:
598: if ((error = ether_ioctl(ifp, &sc->sc_arpcom, cmd, data)) > 0) {
599: splx(s);
600: return error;
601: }
602:
603: switch (cmd) {
604:
605: case SIOCSIFADDR:
606: ifp->if_flags |= IFF_UP;
607:
608: switch (ifa->ifa_addr->sa_family) {
609: #ifdef INET
610: case AF_INET:
611: elinit(sc);
612: arp_ifinit(&sc->sc_arpcom, ifa);
613: break;
614: #endif
615: default:
616: elinit(sc);
617: break;
618: }
619: break;
620:
621: case SIOCSIFFLAGS:
622: if ((ifp->if_flags & IFF_UP) == 0 &&
623: (ifp->if_flags & IFF_RUNNING) != 0) {
624: /*
625: * If interface is marked down and it is running, then
626: * stop it.
627: */
628: elstop(sc);
629: ifp->if_flags &= ~IFF_RUNNING;
630: } else if ((ifp->if_flags & IFF_UP) != 0 &&
631: (ifp->if_flags & IFF_RUNNING) == 0) {
632: /*
633: * If interface is marked up and it is stopped, then
634: * start it.
635: */
636: elinit(sc);
637: } else {
638: /*
639: * Some other important flag might have changed, so
640: * reset.
641: */
642: elreset(sc);
643: }
644: break;
645:
646: default:
647: error = EINVAL;
648: break;
649: }
650:
651: splx(s);
652: return error;
653: }
654:
655: /*
656: * Device timeout routine.
657: */
658: void
659: elwatchdog(ifp)
660: struct ifnet *ifp;
661: {
662: struct el_softc *sc = ifp->if_softc;
663:
664: log(LOG_ERR, "%s: device timeout\n", sc->sc_dev.dv_xname);
665: sc->sc_arpcom.ac_if.if_oerrors++;
666:
667: elreset(sc);
668: }
CVSweb