Merge from vendor branch GDB:
[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.11 2004/07/23 07:16:25 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, struct ucred *);
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_start = el_start;
191         ifp->if_ioctl = el_ioctl;
192         ifp->if_watchdog = el_watchdog;
193         ifp->if_init = el_init;
194         ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX);
195
196         /* Now we can attach the interface */
197         dprintf(("Attaching interface...\n"));
198         ether_ifattach(ifp, sc->arpcom.ac_enaddr);
199
200         dprintf(("el_attach() finished.\n"));
201         return(1);
202 }
203
204 /* This routine resets the interface. */
205 static void 
206 el_reset(xsc)
207         void *xsc;
208 {
209         struct el_softc *sc = xsc;
210         int s;
211
212         dprintf(("elreset()\n"));
213         s = splimp();
214         el_stop(sc);
215         el_init(sc);
216         splx(s);
217 }
218
219 static void el_stop(xsc)
220         void *xsc;
221 {
222         struct el_softc *sc = xsc;
223
224         outb(sc->el_base+EL_AC,0);
225 }
226
227 /* Initialize interface.  */
228 static void 
229 el_init(xsc)
230         void *xsc;
231 {
232         struct el_softc *sc = xsc;
233         struct ifnet *ifp;
234         int s;
235         u_short base;
236
237         /* Set up pointers */
238         ifp = &sc->arpcom.ac_if;
239         base = sc->el_base;
240
241         /* If address not known, do nothing. */
242         if(TAILQ_EMPTY(&ifp->if_addrhead)) /* XXX unlikely */
243                 return;
244
245         s = splimp();
246
247         /* First, reset the board. */
248         dprintf(("Resetting board...\n"));
249         el_hardreset(sc);
250
251         /* Configure rx */
252         dprintf(("Configuring rx...\n"));
253         if(ifp->if_flags & IFF_PROMISC)
254                 outb(base+EL_RXC,(EL_RXC_PROMISC|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
255         else
256                 outb(base+EL_RXC,(EL_RXC_ABROAD|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
257         outb(base+EL_RBC,0);
258
259         /* Configure TX */
260         dprintf(("Configuring tx...\n"));
261         outb(base+EL_TXC,0);
262
263         /* Start reception */
264         dprintf(("Starting reception...\n"));
265         outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
266
267         /* Set flags appropriately */
268         ifp->if_flags |= IFF_RUNNING;
269         ifp->if_flags &= ~IFF_OACTIVE;
270
271         /* And start output. */
272         el_start(ifp);
273
274         splx(s);
275 }
276
277 /* Start output on interface.  Get datagrams from the queue and output
278  * them, giving the receiver a chance between datagrams.  Call only
279  * from splimp or interrupt level!
280  */
281 static void
282 el_start(struct ifnet *ifp)
283 {
284         struct el_softc *sc;
285         u_short base;
286         struct mbuf *m, *m0;
287         int s, i, len, retries, done;
288
289         /* Get things pointing in the right directions */
290         sc = ifp->if_softc;
291         base = sc->el_base;
292
293         dprintf(("el_start()...\n"));
294         s = splimp();
295
296         /* Don't do anything if output is active */
297         if(sc->arpcom.ac_if.if_flags & IFF_OACTIVE)
298                 return;
299         sc->arpcom.ac_if.if_flags |= IFF_OACTIVE;
300
301         /* The main loop.  They warned me against endless loops, but
302          * would I listen?  NOOO....
303          */
304         while(1) {
305                 /* Dequeue the next datagram */
306                 IF_DEQUEUE(&sc->arpcom.ac_if.if_snd,m0);
307
308                 /* If there's nothing to send, return. */
309                 if(m0 == NULL) {
310                         sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
311                         splx(s);
312                         return;
313                 }
314
315                 /* Disable the receiver */
316                 outb(base+EL_AC,EL_AC_HOST);
317                 outb(base+EL_RBC,0);
318
319                 /* Copy the datagram to the buffer. */
320                 len = 0;
321                 for(m = m0; m != NULL; m = m->m_next) {
322                         if(m->m_len == 0)
323                                 continue;
324                         bcopy(mtod(m,caddr_t),sc->el_pktbuf+len,m->m_len);
325                         len += m->m_len;
326                 }
327                 m_freem(m0);
328
329                 len = max(len,ETHER_MIN_LEN);
330
331                 /* Give the packet to the bpf, if any */
332                 if(sc->arpcom.ac_if.if_bpf)
333                         bpf_tap(&sc->arpcom.ac_if, sc->el_pktbuf, len);
334
335                 /* Transfer datagram to board */
336                 dprintf(("el: xfr pkt length=%d...\n",len));
337                 i = EL_BUFSIZ - len;
338                 outb(base+EL_GPBL,(i & 0xff));
339                 outb(base+EL_GPBH,((i>>8)&0xff));
340                 outsb(base+EL_BUF,sc->el_pktbuf,len);
341
342                 /* Now transmit the datagram */
343                 retries=0;
344                 done=0;
345                 while(!done) {
346                         if(el_xmit(sc,len)) { /* Something went wrong */
347                                 done = -1;
348                                 break;
349                         }
350                         /* Check out status */
351                         i = inb(base+EL_TXS);
352                         dprintf(("tx status=0x%x\n",i));
353                         if(!(i & EL_TXS_READY)) {
354                                 dprintf(("el: err txs=%x\n",i));
355                                 sc->arpcom.ac_if.if_oerrors++;
356                                 if(i & (EL_TXS_COLL|EL_TXS_COLL16)) {
357                                         if((!(i & EL_TXC_DCOLL16)) && retries < 15) {
358                                                 retries++;
359                                                 outb(base+EL_AC,EL_AC_HOST);
360                                         }
361                                 }
362                                 else
363                                         done = 1;
364                         }
365                         else {
366                                 sc->arpcom.ac_if.if_opackets++;
367                                 done = 1;
368                         }
369                 }
370                 if(done == -1)  /* Packet not transmitted */
371                         continue;
372
373                 /* Now give the card a chance to receive.
374                  * Gotta love 3c501s...
375                  */
376                 (void)inb(base+EL_AS);
377                 outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
378                 splx(s);
379                 /* Interrupt here */
380                 s = splimp();
381         }
382 }
383
384 /* This function actually attempts to transmit a datagram downloaded
385  * to the board.  Call at splimp or interrupt, after downloading data!
386  * Returns 0 on success, non-0 on failure
387  */
388 static int
389 el_xmit(struct el_softc *sc,int len)
390 {
391         int gpl;
392         int i;
393
394         gpl = EL_BUFSIZ - len;
395         dprintf(("el: xmit..."));
396         outb((sc->el_base)+EL_GPBL,(gpl & 0xff));
397         outb((sc->el_base)+EL_GPBH,((gpl>>8)&0xff));
398         outb((sc->el_base)+EL_AC,EL_AC_TXFRX);
399         i = 20000;
400         while((inb((sc->el_base)+EL_AS) & EL_AS_TXBUSY) && (i>0))
401                 i--;
402         if(i == 0) {
403                 dprintf(("tx not ready\n"));
404                 sc->arpcom.ac_if.if_oerrors++;
405                 return(-1);
406         }
407         dprintf(("%d cycles.\n",(20000-i)));
408         return(0);
409 }
410
411 /* Pass a packet up to the higher levels. */
412 static __inline void
413 elread(struct el_softc *sc,caddr_t buf,int len)
414 {
415         struct ether_header *eh;
416         struct mbuf *m;
417
418         eh = (struct ether_header *)buf;
419
420         /*
421          * Put packet into an mbuf chain
422          */
423         m = elget(buf,len,&sc->arpcom.ac_if);
424         if(m == 0)
425                 return;
426
427         ether_input(&sc->arpcom.ac_if,eh,m);
428 }
429
430 /* controller interrupt */
431 static void
432 elintr(int unit)
433 {
434         struct el_softc *sc;
435         int base;
436         int stat, rxstat, len, done;
437
438         /* Get things pointing properly */
439         sc = &el_softc[unit];
440         base = sc->el_base;
441
442         dprintf(("elintr: "));
443
444         /* Check board status */
445         stat = inb(base+EL_AS);
446         if(stat & EL_AS_RXBUSY) {
447                 (void)inb(base+EL_RXC);
448                 outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
449                 return;
450         }
451
452         done = 0;
453         while(!done) {
454                 rxstat = inb(base+EL_RXS);
455                 if(rxstat & EL_RXS_STALE) {
456                         (void)inb(base+EL_RXC);
457                         outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
458                         return;
459                 }
460
461                 /* If there's an overflow, reinit the board. */
462                 if(!(rxstat & EL_RXS_NOFLOW)) {
463                         dprintf(("overflow.\n"));
464                         el_hardreset(sc);
465                         /* Put board back into receive mode */
466                         if(sc->arpcom.ac_if.if_flags & IFF_PROMISC)
467                                 outb(base+EL_RXC,(EL_RXC_PROMISC|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
468                         else
469                                 outb(base+EL_RXC,(EL_RXC_ABROAD|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
470                         (void)inb(base+EL_AS);
471                         outb(base+EL_RBC,0);
472                         (void)inb(base+EL_RXC);
473                         outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
474                         return;
475                 }
476
477                 /* Incoming packet */
478                 len = inb(base+EL_RBL);
479                 len |= inb(base+EL_RBH) << 8;
480                 dprintf(("receive len=%d rxstat=%x ",len,rxstat));
481                 outb(base+EL_AC,EL_AC_HOST);
482
483                 /* If packet too short or too long, restore rx mode and return
484                  */
485                 if((len <= sizeof(struct ether_header)) || (len > ETHER_MAX_LEN)) {
486                         if(sc->arpcom.ac_if.if_flags & IFF_PROMISC)
487                                 outb(base+EL_RXC,(EL_RXC_PROMISC|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
488                         else
489                                 outb(base+EL_RXC,(EL_RXC_ABROAD|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW));
490                         (void)inb(base+EL_AS);
491                         outb(base+EL_RBC,0);
492                         (void)inb(base+EL_RXC);
493                         outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
494                         return;
495                 }
496
497                 sc->arpcom.ac_if.if_ipackets++;
498
499                 /* Copy the data into our buffer */
500                 outb(base+EL_GPBL,0);
501                 outb(base+EL_GPBH,0);
502                 insb(base+EL_BUF,sc->el_pktbuf,len);
503                 outb(base+EL_RBC,0);
504                 outb(base+EL_AC,EL_AC_RX);
505                 dprintf(("%6D-->",sc->el_pktbuf+6,":"));
506                 dprintf(("%6D\n",sc->el_pktbuf,":"));
507
508                 /* Pass data up to upper levels */
509                 len -= sizeof(struct ether_header);
510                 elread(sc,(caddr_t)(sc->el_pktbuf),len);
511
512                 /* Is there another packet? */
513                 stat = inb(base+EL_AS);
514
515                 /* If so, do it all again (i.e. don't set done to 1) */
516                 if(!(stat & EL_AS_RXBUSY))
517                         dprintf(("<rescan> "));
518                 else
519                         done = 1;
520         }
521
522         (void)inb(base+EL_RXC);
523         outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX));
524         return;
525 }
526
527 /*
528  * Pull read data off a interface.
529  * Len is length of data, with local net header stripped.
530  */
531 static struct mbuf *
532 elget(buf, totlen, ifp)
533         caddr_t buf;
534         int totlen;
535         struct ifnet *ifp;
536 {
537         struct mbuf *top, **mp, *m;
538         int len;
539         caddr_t cp;
540         char *epkt;
541
542         buf += sizeof(struct ether_header);
543         cp = buf;
544         epkt = cp + totlen;
545
546         MGETHDR(m, MB_DONTWAIT, MT_DATA);
547         if (m == 0)
548                 return (0);
549         m->m_pkthdr.rcvif = ifp;
550         m->m_pkthdr.len = totlen;
551         m->m_len = MHLEN;
552         top = 0;
553         mp = &top;
554         while (totlen > 0) {
555                 if (top) {
556                         MGET(m, MB_DONTWAIT, MT_DATA);
557                         if (m == 0) {
558                                 m_freem(top);
559                                 return (0);
560                         }
561                         m->m_len = MLEN;
562                 }
563                 len = min(totlen, epkt - cp);
564                 if (len >= MINCLSIZE) {
565                         MCLGET(m, MB_DONTWAIT);
566                         if (m->m_flags & M_EXT)
567                                 m->m_len = len = min(len, MCLBYTES);
568                         else
569                                 len = m->m_len;
570                 } else {
571                         /*
572                          * Place initial small packet/header at end of mbuf.
573                          */
574                         if (len < m->m_len) {
575                                 if (top == 0 && len + max_linkhdr <= m->m_len)
576                                         m->m_data += max_linkhdr;
577                                 m->m_len = len;
578                         } else
579                                 len = m->m_len;
580                 }
581                 bcopy(cp, mtod(m, caddr_t), (unsigned)len);
582                 cp += len;
583                 *mp = m;
584                 mp = &m->m_next;
585                 totlen -= len;
586                 if (cp == epkt)
587                         cp = buf;
588         }
589         return (top);
590 }
591
592 /*
593  * Process an ioctl request. This code needs some work - it looks
594  *      pretty ugly.
595  */
596 static int
597 el_ioctl(struct ifnet *ifp, u_long command, caddr_t data, struct ucred *cr)
598 {
599         int s, error = 0;
600
601         s = splimp();
602
603         switch (command) {
604         case SIOCSIFADDR:
605         case SIOCGIFADDR:
606         case SIOCSIFMTU:
607                 error = ether_ioctl(ifp, command, data);
608                 break;
609
610         case SIOCSIFFLAGS:
611                 /*
612                  * If interface is marked down and it is running, then stop it
613                  */
614                 if (((ifp->if_flags & IFF_UP) == 0) &&
615                     (ifp->if_flags & IFF_RUNNING)) {
616                         el_stop(ifp->if_softc);
617                         ifp->if_flags &= ~IFF_RUNNING;
618                 } else {
619                 /*
620                  * If interface is marked up and it is stopped, then start it
621                  */
622                         if ((ifp->if_flags & IFF_UP) &&
623                             ((ifp->if_flags & IFF_RUNNING) == 0))
624                                 el_init(ifp->if_softc);
625                 }
626                 break;
627         default:
628                 error = EINVAL;
629         }
630         (void) splx(s);
631         return (error);
632 }
633
634 /* Device timeout routine */
635 static void
636 el_watchdog(struct ifnet *ifp)
637 {
638         log(LOG_ERR,"%s: device timeout\n", ifp->if_xname);
639         ifp->if_oerrors++;
640         el_reset(ifp->if_softc);
641 }