📄 8390.c
字号:
struct ei_device *ei_local = (struct ei_device *) dev->priv; outb_p(ENISR_TX, e8390_base + EN0_ISR); /* Ack intr. */#ifdef EI_PINGPONG /* * There are two Tx buffers, see which one finished, and trigger * the send of another one if it exists. */ ei_local->txqueue--; if (ei_local->tx1 < 0) { if (ei_local->lasttx != 1 && ei_local->lasttx != -1) printk("%s: bogus last_tx_buffer %d, tx1=%d.\n", ei_local->name, ei_local->lasttx, ei_local->tx1); ei_local->tx1 = 0; dev->tbusy = 0; if (ei_local->tx2 > 0) { ei_local->txing = 1; NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6); dev->trans_start = jiffies; ei_local->tx2 = -1, ei_local->lasttx = 2; } else ei_local->lasttx = 20, ei_local->txing = 0; } else if (ei_local->tx2 < 0) { if (ei_local->lasttx != 2 && ei_local->lasttx != -2) printk("%s: bogus last_tx_buffer %d, tx2=%d.\n", ei_local->name, ei_local->lasttx, ei_local->tx2); ei_local->tx2 = 0; dev->tbusy = 0; if (ei_local->tx1 > 0) { ei_local->txing = 1; NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page); dev->trans_start = jiffies; ei_local->tx1 = -1; ei_local->lasttx = 1; } else ei_local->lasttx = 10, ei_local->txing = 0; } else printk("%s: unexpected TX-done interrupt, lasttx=%d.\n", dev->name, ei_local->lasttx);#else /* EI_PINGPONG */ /* * Single Tx buffer: mark it free so another packet can be loaded. */ ei_local->txing = 0; dev->tbusy = 0;#endif /* Minimize Tx latency: update the statistics after we restart TXing. */ if (status & ENTSR_COL) ei_local->stat.collisions++; if (status & ENTSR_PTX) ei_local->stat.tx_packets++; else { ei_local->stat.tx_errors++; if (status & ENTSR_ABT) ei_local->stat.tx_aborted_errors++; if (status & ENTSR_CRS) ei_local->stat.tx_carrier_errors++; if (status & ENTSR_FU) ei_local->stat.tx_fifo_errors++; if (status & ENTSR_CDH) ei_local->stat.tx_heartbeat_errors++; if (status & ENTSR_OWC) ei_local->stat.tx_window_errors++; } mark_bh (NET_BH);}/* We have a good packet(s), get it/them out of the buffers. */static void ei_receive(struct device *dev){ int e8390_base = dev->base_addr; struct ei_device *ei_local = (struct ei_device *) dev->priv; unsigned char rxing_page, this_frame, next_frame; unsigned short current_offset; int rx_pkt_count = 0; struct e8390_pkt_hdr rx_frame; int num_rx_pages = ei_local->stop_page-ei_local->rx_start_page; while (++rx_pkt_count < 10) { int pkt_len; /* Get the rx page (incoming packet pointer). */ outb_p(E8390_NODMA+E8390_PAGE1, e8390_base + E8390_CMD); rxing_page = inb_p(e8390_base + EN1_CURPAG); outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD); /* Remove one frame from the ring. Boundary is always a page behind. */ this_frame = inb_p(e8390_base + EN0_BOUNDARY) + 1; if (this_frame >= ei_local->stop_page) this_frame = ei_local->rx_start_page; /* Someday we'll omit the previous, iff we never get this message. (There is at least one clone claimed to have a problem.) */ if (ei_debug > 0 && this_frame != ei_local->current_page) printk("%s: mismatched read page pointers %2x vs %2x.\n", dev->name, this_frame, ei_local->current_page); if (this_frame == rxing_page) /* Read all the frames? */ break; /* Done for now */ current_offset = this_frame << 8; ei_get_8390_hdr(dev, &rx_frame, this_frame); pkt_len = rx_frame.count - sizeof(struct e8390_pkt_hdr); next_frame = this_frame + 1 + ((pkt_len+4)>>8); /* Check for bogosity warned by 3c503 book: the status byte is never written. This happened a lot during testing! This code should be cleaned up someday. */ if (rx_frame.next != next_frame && rx_frame.next != next_frame + 1 && rx_frame.next != next_frame - num_rx_pages && rx_frame.next != next_frame + 1 - num_rx_pages) { ei_local->current_page = rxing_page; outb(ei_local->current_page-1, e8390_base+EN0_BOUNDARY); ei_local->stat.rx_errors++; continue; } if (pkt_len < 60 || pkt_len > 1518) { if (ei_debug) printk("%s: bogus packet size: %d, status=%#2x nxpg=%#2x.\n", dev->name, rx_frame.count, rx_frame.status, rx_frame.next); ei_local->stat.rx_errors++; } else if ((rx_frame.status & 0x0F) == ENRSR_RXOK) { struct sk_buff *skb; skb = dev_alloc_skb(pkt_len+2); if (skb == NULL) { if (ei_debug > 1) printk("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, pkt_len); ei_local->stat.rx_dropped++; break; } else { skb_reserve(skb,2); /* IP headers on 16 byte boundaries */ skb->dev = dev; skb_put(skb, pkt_len); /* Make room */ ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame)); skb->protocol=eth_type_trans(skb,dev); netif_rx(skb); ei_local->stat.rx_packets++; } } else { int errs = rx_frame.status; if (ei_debug) printk("%s: bogus packet: status=%#2x nxpg=%#2x size=%d\n", dev->name, rx_frame.status, rx_frame.next, rx_frame.count); if (errs & ENRSR_FO) ei_local->stat.rx_fifo_errors++; } next_frame = rx_frame.next; /* This _should_ never happen: it's here for avoiding bad clones. */ if (next_frame >= ei_local->stop_page) { printk("%s: next frame inconsistency, %#2x\n", dev->name, next_frame); next_frame = ei_local->rx_start_page; } ei_local->current_page = next_frame; outb_p(next_frame-1, e8390_base+EN0_BOUNDARY); } /* We used to also ack ENISR_OVER here, but that would sometimes mask a real overrun, leaving the 8390 in a stopped state with rec'vr off. */ outb_p(ENISR_RX+ENISR_RX_ERR, e8390_base+EN0_ISR); return;}/* * We have a receiver overrun: we have to kick the 8390 to get it started * again. Problem is that you have to kick it exactly as NS prescribes in * the updated datasheets, or "the NIC may act in an unpredictable manner." * This includes causing "the NIC to defer indefinitely when it is stopped * on a busy network." Ugh. */static void ei_rx_overrun(struct device *dev){ int e8390_base = dev->base_addr; unsigned long wait_start_time; unsigned char was_txing, must_resend = 0; struct ei_device *ei_local = (struct ei_device *) dev->priv; /* * Record whether a Tx was in progress and then issue the * stop command. */ was_txing = inb_p(e8390_base+E8390_CMD) & E8390_TRANS; outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); if (ei_debug > 1) printk("%s: Receiver overrun.\n", dev->name); ei_local->stat.rx_over_errors++; /* * Wait a full Tx time (1.2ms) + some guard time, NS says 1.6ms total. * Early datasheets said to poll the reset bit, but now they say that * it "is not a reliable indicator and subsequently should be ignored." * We wait at least 10ms. */ wait_start_time = jiffies; while (jiffies - wait_start_time <= 1*HZ/100) barrier(); /* * Reset RBCR[01] back to zero as per magic incantation. */ outb_p(0x00, e8390_base+EN0_RCNTLO); outb_p(0x00, e8390_base+EN0_RCNTHI); /* * See if any Tx was interrupted or not. According to NS, this * step is vital, and skipping it will cause no end of havoc. */ if (was_txing) { unsigned char tx_completed = inb_p(e8390_base+EN0_ISR) & (ENISR_TX+ENISR_TX_ERR); if (!tx_completed) must_resend = 1; } /* * Have to enter loopback mode and then restart the NIC before * you are allowed to slurp packets up off the ring. */ outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, e8390_base + E8390_CMD); /* * Clear the Rx ring of all the debris, and ack the interrupt. */ ei_receive(dev); outb_p(ENISR_OVER, e8390_base+EN0_ISR); /* * Leave loopback mode, and resend any packet that got stopped. */ outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); if (must_resend) outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START + E8390_TRANS, e8390_base + E8390_CMD); }static struct enet_statistics *get_stats(struct device *dev){ short ioaddr = dev->base_addr; struct ei_device *ei_local = (struct ei_device *) dev->priv; /* If the card is stopped, just return the present stats. */ if (dev->start == 0) return &ei_local->stat; /* Read the counter registers, assuming we are in page 0. */ ei_local->stat.rx_frame_errors += inb_p(ioaddr + EN0_COUNTER0); ei_local->stat.rx_crc_errors += inb_p(ioaddr + EN0_COUNTER1); ei_local->stat.rx_missed_errors+= inb_p(ioaddr + EN0_COUNTER2); return &ei_local->stat;}/* * Set or clear the multicast filter for this adaptor. */ static void set_multicast_list(struct device *dev){ short ioaddr = dev->base_addr; if(dev->flags&IFF_PROMISC) { outb_p(E8390_RXCONFIG | 0x18, ioaddr + EN0_RXCR); } else if((dev->flags&IFF_ALLMULTI)||dev->mc_list) { /* The multicast-accept list is initialized to accept-all, and we rely on higher-level filtering for now. */ outb_p(E8390_RXCONFIG | 0x08, ioaddr + EN0_RXCR); } else outb_p(E8390_RXCONFIG, ioaddr + EN0_RXCR);}/* Initialize the rest of the 8390 device structure. */int ethdev_init(struct device *dev){ if (ei_debug > 1) printk(version); if (dev->priv == NULL) { struct ei_device *ei_local; dev->priv = kmalloc(sizeof(struct ei_device), GFP_KERNEL); if (dev->priv == NULL) return -ENOMEM; memset(dev->priv, 0, sizeof(struct ei_device)); ei_local = (struct ei_device *)dev->priv; } dev->hard_start_xmit = &ei_start_xmit; dev->get_stats = get_stats; dev->set_multicast_list = &set_multicast_list; ether_setup(dev); return 0;}/* This page of functions should be 8390 generic *//* Follow National Semi's recommendations for initializing the "NIC". */void NS8390_init(struct device *dev, int startp){ int e8390_base = dev->base_addr; struct ei_device *ei_local = (struct ei_device *) dev->priv; int i; int endcfg = ei_local->word16 ? (0x48 | ENDCFG_WTS) : 0x48; unsigned long flags; /* Follow National Semi's recommendations for initing the DP83902. */ outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base); /* 0x21 */ outb_p(endcfg, e8390_base + EN0_DCFG); /* 0x48 or 0x49 */ /* Clear the remote byte count registers. */ outb_p(0x00, e8390_base + EN0_RCNTLO); outb_p(0x00, e8390_base + EN0_RCNTHI); /* Set to monitor and loopback mode -- this is vital!. */ outb_p(E8390_RXOFF, e8390_base + EN0_RXCR); /* 0x20 */ outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); /* 0x02 */ /* Set the transmit page and receive ring. */ outb_p(ei_local->tx_start_page, e8390_base + EN0_TPSR); ei_local->tx1 = ei_local->tx2 = 0; outb_p(ei_local->rx_start_page, e8390_base + EN0_STARTPG); outb_p(ei_local->stop_page-1, e8390_base + EN0_BOUNDARY); /* 3c503 says 0x3f,NS0x26*/ ei_local->current_page = ei_local->rx_start_page; /* assert boundary+1 */ outb_p(ei_local->stop_page, e8390_base + EN0_STOPPG); /* Clear the pending interrupts and mask. */ outb_p(0xFF, e8390_base + EN0_ISR); outb_p(0x00, e8390_base + EN0_IMR); /* Copy the station address into the DS8390 registers, and set the multicast hash bitmap to receive all multicasts. */ save_flags(flags); cli(); outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base); /* 0x61 */ for(i = 0; i < 6; i++) { outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS + i); } /* Initialize the multicast list to accept-all. If we enable multicast the higher levels can do the filtering. */ for(i = 0; i < 8; i++) outb_p(0xff, e8390_base + EN1_MULT + i); outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG); outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base); restore_flags(flags); dev->tbusy = 0; dev->interrupt = 0; ei_local->tx1 = ei_local->tx2 = 0; ei_local->txing = 0; if (startp) { outb_p(0xff, e8390_base + EN0_ISR); outb_p(ENISR_ALL, e8390_base + EN0_IMR); outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base); outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */ /* 3c503 TechMan says rxconfig only after the NIC is started. */ outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR); /* rx on, */ dev->set_multicast_list(dev); /* Get the multicast status right if this was a reset. */ } return;}/* Trigger a transmit start, assuming the length is valid. */static void NS8390_trigger_send(struct device *dev, unsigned int length, int start_page){ int e8390_base = dev->base_addr; outb_p(E8390_NODMA+E8390_PAGE0, e8390_base); if (inb_p(e8390_base) & E8390_TRANS) { printk("%s: trigger_send() called with the transmitter busy.\n", dev->name); return; } outb_p(length & 0xff, e8390_base + EN0_TCNTLO); outb_p(length >> 8, e8390_base + EN0_TCNTHI); outb_p(start_page, e8390_base + EN0_TPSR); outb_p(E8390_NODMA+E8390_TRANS+E8390_START, e8390_base); return;}#ifdef MODULEint init_module(void){ return 0;}voidcleanup_module(void){}#endif /* MODULE *//* * Local variables: * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 8390.c" * version-control: t * kept-new-versions: 5 * c-indent-level: 4 * tab-width: 4 * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -