bbf42aba34a1e10e992a4d4460ded322c25266b4
[dragonfly.git] / sys / dev / netif / el / if_el.c
1 /* Copyright (c) 1994, Matthew E. Kimmel.  Permission is hereby granted
2  * to use, copy, modify and distribute this software provided that both
3  * the copyright notice and this permission notice appear in all copies
4  * of the software, derivative works or modified versions, and any
5  * portions thereof.
6  *
7  * Questions, comments, bug reports and fixes to kimmel@cs.umass.edu.
8  *
9  * $FreeBSD: src/sys/i386/isa/if_el.c,v 1.47.2.2 2000/07/17 21:24:30 archie Exp $
10  * $DragonFly: src/sys/dev/netif/el/if_el.c,v 1.7 2004/03/14 15:36:49 joerg Exp $
11  */
12 /* Except of course for the portions of code lifted from other FreeBSD
13  * drivers (mainly elread, elget and el_ioctl)
14  */
15 /* 3COM Etherlink 3C501 device driver for FreeBSD */
16 /* Yeah, I know these cards suck, but you can also get them for free
17  * really easily...
18  */
19 /* Bugs/possible improvements:
20  *      - Does not currently support DMA
21  *      - Does not currently support multicasts
22  */
23 #include "use_el.h"
24 #include "opt_inet.h"
25 #include "opt_ipx.h"
26
27 #include <sys/param.h>
28 #include <sys/systm.h>
29 #include <sys/sockio.h>
30 #include <sys/mbuf.h>
31 #include <sys/socket.h>
32 #include <sys/syslog.h>
33 #include <sys/linker_set.h>
34 #include <sys/module.h>
35
36 #include <net/ethernet.h>
37 #include <net/if.h>
38
39 #include <netinet/in.h>
40 #include <netinet/if_ether.h>
41
42 #include <net/bpf.h>
43
44 #include <machine/clock.h>
45
46 #include <bus/isa/i386/isa_device.h>
47 #include "if_elreg.h"
48
49 DECLARE_DUMMY_MODULE(if_el);
50
51 /* For debugging convenience */
52 #ifdef EL_DEBUG
53 #define dprintf(x) printf x
54 #else
55 #define dprintf(x)
56 #endif
57
58 /* el_softc: per line info and status */
59 static struct el_softc {
60         struct arpcom arpcom;   /* Ethernet common */
61         u_short el_base;        /* Base I/O addr */
62         char el_pktbuf[EL_BUFSIZ];      /* Frame buffer */
63 } el_softc[NEL];
64
65 /* Prototypes */
66 static int el_attach(struct isa_device *);
67 static void el_init(void *);
68 static int el_ioctl(struct ifnet *,u_long,caddr_t);
69 static int el_probe(struct isa_device *);
70 static void el_start(struct ifnet *);
71 static void el_reset(void *);
72 static void el_watchdog(struct ifnet *);
73
74 static void el_stop(void *);
75 static int el_xmit(struct el_softc *,int);
76 static ointhand2_t elintr;
77 static __inline void elread(struct el_softc *,caddr_t,int);
78 static struct mbuf *elget(caddr_t,int,struct ifnet *);
79 static __inline void el_hardreset(void *);
80
81 /* isa_driver structure for autoconf */
82 struct isa_driver eldriver = {
83         el_probe, el_attach, "el"
84 };
85
86 /* Probe routine.  See if the card is there and at the right place. */
87 static int
88 el_probe(struct isa_device *idev)
89 {
90         struct el_softc *sc;
91         u_short base; /* Just for convenience */
92         u_char station_addr[ETHER_ADDR_LEN];
93         int i;
94
95         /* Grab some info for our structure */
96         sc = &el_softc[idev->id_unit];
97         sc->el_base = idev->id_iobase;
98         base = sc->el_base;
99
100         /* First check the base */
101         if((base < 0x280) || (base > 0x3f0)) {
102                 printf("el%d: ioaddr must be between 0x280 and 0x3f0\n",
103                         idev->id_unit);
104                 return(0);
105         }
106
107         /* Now attempt to grab the station address from the PROM
108          * and see if it contains the 3com vendor code.
109          */
110         dprintf(("Probing 3c501 at 0x%x...\n",base));
111
112         /* Reset the board */
113         dprintf(("Resetting board...\n"));
114         outb(base+EL_AC,EL_AC_RESET);
115         DELAY(5);
116         outb(base+EL_AC,0);
117         dprintf(("Reading station address...\n"));
118         /* Now read the address */
119         for(i=0;i<ETHER_ADDR_LEN;i++) {
120                 outb(base+EL_GPBL,i);
121                 station_addr[i] = inb(base+EL_EAW);
122         }
123         dprintf(("Address is %6D\n",station_addr, ":"));
124
125         /* If the vendor code is ok, return a 1.  We'll assume that
126          * whoever configured this system is right about the IRQ.
127          */
128         if((station_addr[0] != 0x02) || (station_addr[1] != 0x60)
129            || (station_addr[2] != 0x8c)) {
130                 dprintf(("Bad vendor code.\n"));
131                 return(0);
132         } else {
133                 dprintf(("Vendor code ok.\n"));
134                 /* Copy the station address into the arpcom structure */
135                 bcopy(station_addr,sc->arpcom.ac_enaddr,ETHER_ADDR_LEN);
136                 return(1);
137         }
138 }
139
140 /* Do a hardware reset of the 3c501.  Do not call until after el_probe()! */
141 static __inline void
142 el_hardreset(xsc)
143         void *xsc;
144 {
145         struct el_softc *sc = xsc;
146         int base;
147         int j;
148
149         base = sc->el_base;
150
151         /* First reset the board */
152         outb(base+EL_AC,EL_AC_RESET);
153         DELAY(5);
154         outb(base+EL_AC,0);
155
156         /* Then give it back its ethernet address.  Thanks to the mach
157          * source code for this undocumented goodie...
158          */
159         for(j=0;j<ETHER_ADDR_LEN;j++)
160                 outb(base+j,sc->arpcom.ac_enaddr[j]);
161 }
162
163 /* Attach the interface to the kernel data structures.  By the time
164  * this is called, we know that the card exists at the given I/O address.
165  * We still assume that the IRQ given is correct.
166  */
167 static int
168 el_attach(struct isa_device *idev)
169 {
170         struct el_softc *sc;
171         struct ifnet *ifp;
172         u_short base;
173
174         dprintf(("Attaching el%d...\n",idev->id_unit));
175
176         /* Get things pointing to the right places. */
177         idev->id_ointr = elintr;
178         sc = &el_softc[idev->id_unit];
179         ifp = &sc->arpcom.ac_if;
180         base = sc->el_base;
181
182         /* Now reset the board */
183         dprintf(("Resetting board...\n"));
184         el_hardreset(sc);
185
186         /* Initialize ifnet structure */
187         ifp->if_softc = sc;
188         if_initname(ifp, "el", idev->id_unit);
189         ifp->if_mtu = ETHERMTU;
190         ifp->if_output = ether_output;
191         ifp->if_start = el_start;
192         ifp->if_ioctl = el_ioctl;
193         ifp->if_watchdog = el_watchdog;
194         ifp->if_init = el_init;
195         ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX);
196
197         /* Now we can attach the interface */
198         dprintf(("Attaching interface...\n"));
199         ether_ifattach(ifp, sc->arpcom.ac_enaddr);
200
201         /* Print out some information for the user */
202         printf("el%d: 3c501 address %6D\n",idev->id_unit,
203           sc->arpcom.ac_enaddr, ":");
204
205         dprintf(("el_attach() finished.\n"));
206         return(1);
207 }
208
209 /* This routine resets the interface. */
210 static void 
211 el_reset(xsc)
212         void *xsc;
213 {
214         struct el_softc *sc = xsc;
215         int s;
216
217         dprintf(("elreset()\n"));
218         s = splimp();
219         el_stop(sc);
220         el_init(sc);
221         splx(s);
222 }
223
224 static void el_stop(xsc)
225         void *xsc;
226 {
227         struct el_softc *sc = xsc;
228
229         outb(sc->el_base+EL_AC,0);
230 }
231
232 /* Initialize interface.  */
233 static void 
234 el_init(xsc)
235         void *xsc;
236 {
237         struct el_softc *sc = xsc;
238         struct ifnet *ifp;
239         int s;
240         u_short base;
241
242         /* Set up pointers */
243         ifp = &sc->arpcom.ac_if;
244         base = sc->el_base;
245
246         /* If address not known, do nothing. */
247         if(TAILQ_EMPTY(&ifp->if_addrhead)) /* XXX unlikely */
248                 return;
249
250         s = splimp();
251
252         /* First, reset the board. */
253         dprintf(("Resetting board...\n"));
254         el_hardreset(sc);
255
256         /* Configure rx */
257         dprintf(("Configuring rx...\n"));
258         if(ifp->if_flags & IFF_PROMISC)
259                 outb(base+EL_RXC,(EL_RXC_PROMISC|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
260         else
261                 outb(base+EL_RXC,(EL_RXC_ABROAD|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
262         outb(base+EL_RBC,0);
263
264         /* Configure TX */
265         dprintf(("Configuring tx...\n"));
266         outb(base+EL_TXC,0);
267
268         /* Start reception */
269         dprintf(("Starting reception...\n"));
270         outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
271
272         /* Set flags appropriately */
273         ifp->if_flags |= IFF_RUNNING;
274         ifp->if_flags &= ~IFF_OACTIVE;
275
276         /* And start output. */
277         el_start(ifp);
278
279         splx(s);
280 }
281
282 /* Start output on interface.  Get datagrams from the queue and output
283  * them, giving the receiver a chance between datagrams.  Call only
284  * from splimp or interrupt level!
285  */
286 static void
287 el_start(struct ifnet *ifp)
288 {
289         struct el_softc *sc;
290         u_short base;
291         struct mbuf *m, *m0;
292         int s, i, len, retries, done;
293
294         /* Get things pointing in the right directions */
295         sc = ifp->if_softc;
296         base = sc->el_base;
297
298         dprintf(("el_start()...\n"));
299         s = splimp();
300
301         /* Don't do anything if output is active */
302         if(sc->arpcom.ac_if.if_flags & IFF_OACTIVE)
303                 return;
304         sc->arpcom.ac_if.if_flags |= IFF_OACTIVE;
305
306         /* The main loop.  They warned me against endless loops, but
307          * would I listen?  NOOO....
308          */
309         while(1) {
310                 /* Dequeue the next datagram */
311                 IF_DEQUEUE(&sc->arpcom.ac_if.if_snd,m0);
312
313                 /* If there's nothing to send, return. */
314                 if(m0 == NULL) {
315                         sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
316                         splx(s);
317                         return;
318                 }
319
320                 /* Disable the receiver */
321                 outb(base+EL_AC,EL_AC_HOST);
322                 outb(base+EL_RBC,0);
323
324                 /* Copy the datagram to the buffer. */
325                 len = 0;
326                 for(m = m0; m != NULL; m = m->m_next) {
327                         if(m->m_len == 0)
328                                 continue;
329                         bcopy(mtod(m,caddr_t),sc->el_pktbuf+len,m->m_len);
330                         len += m->m_len;
331                 }
332                 m_freem(m0);
333
334                 len = max(len,ETHER_MIN_LEN);
335
336                 /* Give the packet to the bpf, if any */
337                 if(sc->arpcom.ac_if.if_bpf)
338                         bpf_tap(&sc->arpcom.ac_if, sc->el_pktbuf, len);
339
340                 /* Transfer datagram to board */
341                 dprintf(("el: xfr pkt length=%d...\n",len));
342                 i = EL_BUFSIZ - len;
343                 outb(base+EL_GPBL,(i & 0xff));
344                 outb(base+EL_GPBH,((i>>8)&0xff));
345                 outsb(base+EL_BUF,sc->el_pktbuf,len);
346
347                 /* Now transmit the datagram */
348                 retries=0;
349                 done=0;
350                 while(!done) {
351                         if(el_xmit(sc,len)) { /* Something went wrong */
352                                 done = -1;
353                                 break;
354                         }
355                         /* Check out status */
356                         i = inb(base+EL_TXS);
357                         dprintf(("tx status=0x%x\n",i));
358                         if(!(i & EL_TXS_READY)) {
359                                 dprintf(("el: err txs=%x\n",i));
360                                 sc->arpcom.ac_if.if_oerrors++;
361                                 if(i & (EL_TXS_COLL|EL_TXS_COLL16)) {
362                                         if((!(i & EL_TXC_DCOLL16)) && retries < 15) {
363                                                 retries++;
364                                                 outb(base+EL_AC,EL_AC_HOST);
365                                         }
366                                 }
367                                 else
368                                         done = 1;
369                         }
370                         else {
371                                 sc->arpcom.ac_if.if_opackets++;
372                                 done = 1;
373                         }
374                 }
375                 if(done == -1)  /* Packet not transmitted */
376                         continue;
377
378                 /* Now give the card a chance to receive.
379                  * Gotta love 3c501s...
380                  */
381                 (void)inb(base+EL_AS);
382                 outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
383                 splx(s);
384                 /* Interrupt here */
385                 s = splimp();
386         }
387 }
388
389 /* This function actually attempts to transmit a datagram downloaded
390  * to the board.  Call at splimp or interrupt, after downloading data!
391  * Returns 0 on success, non-0 on failure
392  */
393 static int
394 el_xmit(struct el_softc *sc,int len)
395 {
396         int gpl;
397         int i;
398
399         gpl = EL_BUFSIZ - len;
400         dprintf(("el: xmit..."));
401         outb((sc->el_base)+EL_GPBL,(gpl & 0xff));
402         outb((sc->el_base)+EL_GPBH,((gpl>>8)&0xff));
403         outb((sc->el_base)+EL_AC,EL_AC_TXFRX);
404         i = 20000;
405         while((inb((sc->el_base)+EL_AS) & EL_AS_TXBUSY) && (i>0))
406                 i--;
407         if(i == 0) {
408                 dprintf(("tx not ready\n"));
409                 sc->arpcom.ac_if.if_oerrors++;
410                 return(-1);
411         }
412         dprintf(("%d cycles.\n",(20000-i)));
413         return(0);
414 }
415
416 /* Pass a packet up to the higher levels. */
417 static __inline void
418 elread(struct el_softc *sc,caddr_t buf,int len)
419 {
420         struct ether_header *eh;
421         struct mbuf *m;
422
423         eh = (struct ether_header *)buf;
424
425         /*
426          * Put packet into an mbuf chain
427          */
428         m = elget(buf,len,&sc->arpcom.ac_if);
429         if(m == 0)
430                 return;
431
432         ether_input(&sc->arpcom.ac_if,eh,m);
433 }
434
435 /* controller interrupt */
436 static void
437 elintr(int unit)
438 {
439         struct el_softc *sc;
440         int base;
441         int stat, rxstat, len, done;
442
443         /* Get things pointing properly */
444         sc = &el_softc[unit];
445         base = sc->el_base;
446
447         dprintf(("elintr: "));
448
449         /* Check board status */
450         stat = inb(base+EL_AS);
451         if(stat & EL_AS_RXBUSY) {
452                 (void)inb(base+EL_RXC);
453                 outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
454                 return;
455         }
456
457         done = 0;
458         while(!done) {
459                 rxstat = inb(base+EL_RXS);
460                 if(rxstat & EL_RXS_STALE) {
461                         (void)inb(base+EL_RXC);
462                         outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
463                         return;
464                 }
465
466                 /* If there's an overflow, reinit the board. */
467                 if(!(rxstat & EL_RXS_NOFLOW)) {
468                         dprintf(("overflow.\n"));
469                         el_hardreset(sc);
470                         /* Put board back into receive mode */
471                         if(sc->arpcom.ac_if.if_flags & IFF_PROMISC)
472                                 outb(base+EL_RXC,(EL_RXC_PROMISC|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
473                         else
474                                 outb(base+EL_RXC,(EL_RXC_ABROAD|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
475                         (void)inb(base+EL_AS);
476                         outb(base+EL_RBC,0);
477                         (void)inb(base+EL_RXC);
478                         outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
479                         return;
480                 }
481
482                 /* Incoming packet */
483                 len = inb(base+EL_RBL);
484                 len |= inb(base+EL_RBH) << 8;
485                 dprintf(("receive len=%d rxstat=%x ",len,rxstat));
486                 outb(base+EL_AC,EL_AC_HOST);
487
488                 /* If packet too short or too long, restore rx mode and return
489                  */
490                 if((len <= sizeof(struct ether_header)) || (len > ETHER_MAX_LEN)) {
491                         if(sc->arpcom.ac_if.if_flags & IFF_PROMISC)
492                                 outb(base+EL_RXC,(EL_RXC_PROMISC|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
493                         else
494                                 outb(base+EL_RXC,(EL_RXC_ABROAD|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
495                         (void)inb(base+EL_AS);
496                         outb(base+EL_RBC,0);
497                         (void)inb(base+EL_RXC);
498                         outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
499                         return;
500                 }
501
502                 sc->arpcom.ac_if.if_ipackets++;
503
504                 /* Copy the data into our buffer */
505                 outb(base+EL_GPBL,0);
506                 outb(base+EL_GPBH,0);
507                 insb(base+EL_BUF,sc->el_pktbuf,len);
508                 outb(base+EL_RBC,0);
509                 outb(base+EL_AC,EL_AC_RX);
510                 dprintf(("%6D-->",sc->el_pktbuf+6,":"));
511                 dprintf(("%6D\n",sc->el_pktbuf,":"));
512
513                 /* Pass data up to upper levels */
514                 len -= sizeof(struct ether_header);
515                 elread(sc,(caddr_t)(sc->el_pktbuf),len);
516
517                 /* Is there another packet? */
518                 stat = inb(base+EL_AS);
519
520                 /* If so, do it all again (i.e. don't set done to 1) */
521                 if(!(stat & EL_AS_RXBUSY))
522                         dprintf(("<rescan> "));
523                 else
524                         done = 1;
525         }
526
527         (void)inb(base+EL_RXC);
528         outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
529         return;
530 }
531
532 /*
533  * Pull read data off a interface.
534  * Len is length of data, with local net header stripped.
535  */
536 static struct mbuf *
537 elget(buf, totlen, ifp)
538         caddr_t buf;
539         int totlen;
540         struct ifnet *ifp;
541 {
542         struct mbuf *top, **mp, *m;
543         int len;
544         caddr_t cp;
545         char *epkt;
546
547         buf += sizeof(struct ether_header);
548         cp = buf;
549         epkt = cp + totlen;
550
551         MGETHDR(m, M_DONTWAIT, MT_DATA);
552         if (m == 0)
553                 return (0);
554         m->m_pkthdr.rcvif = ifp;
555         m->m_pkthdr.len = totlen;
556         m->m_len = MHLEN;
557         top = 0;
558         mp = &top;
559         while (totlen > 0) {
560                 if (top) {
561                         MGET(m, M_DONTWAIT, MT_DATA);
562                         if (m == 0) {
563                                 m_freem(top);
564                                 return (0);
565                         }
566                         m->m_len = MLEN;
567                 }
568                 len = min(totlen, epkt - cp);
569                 if (len >= MINCLSIZE) {
570                         MCLGET(m, M_DONTWAIT);
571                         if (m->m_flags & M_EXT)
572                                 m->m_len = len = min(len, MCLBYTES);
573                         else
574                                 len = m->m_len;
575                 } else {
576                         /*
577                          * Place initial small packet/header at end of mbuf.
578                          */
579                         if (len < m->m_len) {
580                                 if (top == 0 && len + max_linkhdr <= m->m_len)
581                                         m->m_data += max_linkhdr;
582                                 m->m_len = len;
583                         } else
584                                 len = m->m_len;
585                 }
586                 bcopy(cp, mtod(m, caddr_t), (unsigned)len);
587                 cp += len;
588                 *mp = m;
589                 mp = &m->m_next;
590                 totlen -= len;
591                 if (cp == epkt)
592                         cp = buf;
593         }
594         return (top);
595 }
596
597 /*
598  * Process an ioctl request. This code needs some work - it looks
599  *      pretty ugly.
600  */
601 static int
602 el_ioctl(ifp, command, data)
603         struct ifnet *ifp;
604         u_long command;
605         caddr_t data;
606 {
607         int s, error = 0;
608
609         s = splimp();
610
611         switch (command) {
612         case SIOCSIFADDR:
613         case SIOCGIFADDR:
614         case SIOCSIFMTU:
615                 error = ether_ioctl(ifp, command, data);
616                 break;
617
618         case SIOCSIFFLAGS:
619                 /*
620                  * If interface is marked down and it is running, then stop it
621                  */
622                 if (((ifp->if_flags & IFF_UP) == 0) &&
623                     (ifp->if_flags & IFF_RUNNING)) {
624                         el_stop(ifp->if_softc);
625                         ifp->if_flags &= ~IFF_RUNNING;
626                 } else {
627                 /*
628                  * If interface is marked up and it is stopped, then start it
629                  */
630                         if ((ifp->if_flags & IFF_UP) &&
631                             ((ifp->if_flags & IFF_RUNNING) == 0))
632                                 el_init(ifp->if_softc);
633                 }
634                 break;
635         default:
636                 error = EINVAL;
637         }
638         (void) splx(s);
639         return (error);
640 }
641
642 /* Device timeout routine */
643 static void
644 el_watchdog(struct ifnet *ifp)
645 {
646         log(LOG_ERR,"%s: device timeout\n", ifp->if_xname);
647         ifp->if_oerrors++;
648         el_reset(ifp->if_softc);
649 }