]> Zhao Yanbai Git Server - minix.git/commitdiff
lance: perform full reinitialization for restarts 77/3477/2
authorDavid van Moolenbroek <david@minix3.org>
Sun, 26 Feb 2017 16:04:50 +0000 (16:04 +0000)
committerDavid van Moolenbroek <david@minix3.org>
Sun, 30 Apr 2017 13:15:50 +0000 (13:15 +0000)
When performing a restart (CSR0 STOP, STRT), the behavior regarding
the NIC's current RX/TX descriptor ring counters varies between cards:
older LANCE cards do not reset the counters; newer PCnet cards do
reset them; VirtualBox's emulation is once again broken in that it
claims to emulate newer cards but implements the older behavior.

Changing the card's receive mode requires such a restart, and now that
the system can actually change receive modes dynamically as part of
normal network operation, this results in the lance driver breaking
all the time on at least VirtualBox.

Instead of trying to figure out exactly what is going on with the
counters during a restart, we now simply perform a full-blown
reinitialization every time the NIC is restarted.  That leaves no
ambiguity regarding the counters, and appears to be what drivers on
other OSes do as well.  As a bonus, this approach actually saves code.

Change-Id: I60fad2df6de4616d5de2cec39c09b60c15d854fb

minix/drivers/net/lance/lance.c

index d360047b22c1d3c055c8812f970b52896f17aa4f..eed618c168030e822243f1ebef50d5dd5a28afd5 100644 (file)
@@ -27,7 +27,6 @@ static int do_init(unsigned int instance, netdriver_addr_t *addr,
        uint32_t *caps, unsigned int *ticks);
 static void ec_confaddr(netdriver_addr_t *addr, unsigned int instance);
 static void ec_reinit(ether_card_t *ec);
-static void ec_reset(ether_card_t *ec);
 static void do_intr(unsigned int mask);
 static void do_set_mode(unsigned int mode, const netdriver_addr_t *mcast_list,
        unsigned int mcast_count);
@@ -299,26 +298,24 @@ static int do_init(unsigned int instance, netdriver_addr_t *addr,
  *===========================================================================*/
 static void ec_reinit(ether_card_t *ec)
 {
+   spin_t spin;
    int i;
    unsigned short ioaddr = ec->ec_port;
 
    /* stop */
    write_csr(ioaddr, LANCE_CSR0, LANCE_CSR0_STOP);
-
-   /* purge Tx-ring */
-   tx_slot_nr = cur_tx_slot_nr = 0;
-   for (i=0; i<TX_RING_SIZE; i++)
-   {
-      lp->tx_ring[i].u.base = 0;
-      isstored[i]=0;
+   /* init */
+   write_csr(ioaddr, LANCE_CSR0, LANCE_CSR0_INIT);
+   /* poll for IDON */
+   SPIN_FOR(&spin, 1000) {
+      if (read_csr(ioaddr, LANCE_CSR0) & LANCE_CSR0_IDON)
+         break;
    }
 
-   /* re-init Rx-ring */
-   rx_slot_nr = 0;
-   for (i=0; i<RX_RING_SIZE; i++)
+   /* Set 'Multicast Table' */
+   for (i=0;i<4;++i)
    {
-      lp->rx_ring[i].buf_length = -ETH_FRAME_LEN;
-      lp->rx_ring[i].u.addr[3] |= 0x80;
+      write_csr(ioaddr, LANCE_CSR8 + i, 0xffff);
    }
 
    /* Set 'Receive Mode' */
@@ -339,6 +336,23 @@ static void ec_reinit(ether_card_t *ec)
       }
    }
 
+   /* purge Tx-ring */
+   tx_slot_nr = cur_tx_slot_nr = 0;
+   for (i=0; i<TX_RING_SIZE; i++)
+   {
+      lp->tx_ring[i].u.base = 0;
+      isstored[i]=0;
+   }
+   cur_tx_slot_nr = tx_slot_nr;
+
+   /* re-init Rx-ring */
+   rx_slot_nr = 0;
+   for (i=0; i<RX_RING_SIZE; i++)
+   {
+      lp->rx_ring[i].buf_length = -ETH_FRAME_LEN;
+      lp->rx_ring[i].u.addr[3] |= 0x80;
+   }
+
    /* start && enable interrupt */
    write_csr(ioaddr, LANCE_CSR0,
              LANCE_CSR0_IDON|LANCE_CSR0_IENA|LANCE_CSR0_STRT);
@@ -482,7 +496,10 @@ static void do_intr(unsigned int __unused mask)
          printf("ETH: restarting...\n");
 #endif
 
-         ec_reset(ec);
+         ec_reinit(ec);
+
+        /* store a buffered message on the slot if it exists */
+        netdriver_send();
       }
    }
 
@@ -491,45 +508,6 @@ static void do_intr(unsigned int __unused mask)
       panic("couldn't enable interrupt: %d", r);
 }
 
-/*===========================================================================*
- *                              ec_reset                                     *
- *===========================================================================*/
-static void ec_reset(ether_card_t *ec)
-{
-   /* Stop/start the chip, and clear all RX,TX-slots.  The spec says this type
-    * of reset should be done on MERR, UFLO, and TX BUFF errors.  For now it is
-    * called only for UFLO and TX BUFF (which causes UFLO).  MERR is not (yet?)
-    * handled.  Also note that the PCnet spec says that the LANCE chip does not
-    * require resetting Rx/Tx, but PCnet does.  We intend to support both here.
-    */
-   unsigned short ioaddr = ec->ec_port;
-   int i;
-
-   /* stop */
-   write_csr(ioaddr, LANCE_CSR0, LANCE_CSR0_STOP);
-   /* start */
-   write_csr(ioaddr, LANCE_CSR0, LANCE_CSR0_STRT);
-
-   /* purge Tx-ring */
-   tx_slot_nr = cur_tx_slot_nr = 0;
-   for (i=0; i<TX_RING_SIZE; i++)
-   {
-      lp->tx_ring[i].u.base = 0;
-      isstored[i]=0;
-   }
-
-   /* re-init Rx-ring */
-   rx_slot_nr = 0;
-   for (i=0; i<RX_RING_SIZE; i++)
-   {
-      lp->rx_ring[i].buf_length = -ETH_FRAME_LEN;
-      lp->rx_ring[i].u.addr[3] |= 0x80;
-   }
-
-   /* store a buffered message on the slot if it exists */
-   netdriver_send();
-}
-
 /*===========================================================================*
  *                              do_recv                                      *
  *===========================================================================*/
@@ -843,38 +821,7 @@ static void lance_init_hw(ether_card_t *ec, netdriver_addr_t *addr,
              |LANCE_CSR4_TXSTRTM|LANCE_CSR4_JABM);
 
    /* ----- start when init done. ----- */
-   /* stop */
-   write_csr(ioaddr, LANCE_CSR0, LANCE_CSR0_STOP);
-   /* init */
-   write_csr(ioaddr, LANCE_CSR0, LANCE_CSR0_INIT);
-   /* poll for IDON */
-   for (i = 10000; i > 0; --i)
-      if (read_csr(ioaddr, LANCE_CSR0) & LANCE_CSR0_IDON)
-         break;
-
-   /* Set 'Multicast Table' */
-   for (i=0;i<4;++i)
-   {
-      write_csr(ioaddr, LANCE_CSR8 + i, 0xffff);
-   }
-
-   /* Set 'Receive Mode' */
-   if (ec->ec_mode & NDEV_MODE_PROMISC)
-   {
-      write_csr(ioaddr, LANCE_CSR15, LANCE_CSR15_PROM);
-   }
-   else
-   {
-      if (ec->ec_mode &
-          (NDEV_MODE_BCAST | NDEV_MODE_MCAST_LIST | NDEV_MODE_MCAST_ALL))
-      {
-         write_csr(ioaddr, LANCE_CSR15, 0x0000);
-      }
-      else
-      {
-         write_csr(ioaddr, LANCE_CSR15, LANCE_CSR15_DRCVBC);
-      }
-   }
+   ec_reinit(ec);
 
    /* Set the interrupt handler */
    ec->ec_hook = ec->ec_irq;