From: David van Moolenbroek Date: Sun, 26 Feb 2017 16:04:50 +0000 (+0000) Subject: lance: perform full reinitialization for restarts X-Git-Url: http://zhaoyanbai.com/repos/%22http:/www.isc.org/icons/zpipe.c?a=commitdiff_plain;h=refs%2Fchanges%2F77%2F3477%2F2;p=minix.git lance: perform full reinitialization for restarts 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 --- diff --git a/minix/drivers/net/lance/lance.c b/minix/drivers/net/lance/lance.c index d360047b2..eed618c16 100644 --- a/minix/drivers/net/lance/lance.c +++ b/minix/drivers/net/lance/lance.c @@ -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; itx_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; irx_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; itx_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; irx_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; itx_ring[i].u.base = 0; - isstored[i]=0; - } - - /* re-init Rx-ring */ - rx_slot_nr = 0; - for (i=0; irx_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;