The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/isa/if_el.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 5bb7470a204e9c2f3ed384aab0f024dd


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.