📄 yellowfin.c
字号:
}static struct device *yellowfin_probe1(struct device *dev, int ioaddr, int irq, int chip_id, int options){ static int did_version = 0; /* Already printed version info. */ struct yellowfin_private *yp; int i; if (yellowfin_debug > 0 && did_version++ == 0) printk(version); dev = init_etherdev(dev, sizeof(struct yellowfin_private)); printk("%s: P-E Yellowfin type %8x at %#3x, ", dev->name, inl(ioaddr + ChipRev), ioaddr); for (i = 0; i < 5; i++) printk("%2.2x:", inb(ioaddr + StnAddr + i)); printk("%2.2x, IRQ %d.\n", inb(ioaddr + StnAddr + i), irq); for (i = 0; i < 6; i++) dev->dev_addr[i] = inb(ioaddr + StnAddr + i); /* Reset the chip. */ outl(0x80000000, ioaddr + DMACtrl); /* We do a request_region() only to register /proc/ioports info. */ request_region(ioaddr, YELLOWFIN_TOTAL_SIZE, card_name); dev->base_addr = ioaddr; dev->irq = irq; /* Make certain the descriptor lists are aligned. */ yp = (void *)(((long)kmalloc(sizeof(*yp), GFP_KERNEL) + 31) & ~31); memset(yp, 0, sizeof(*yp)); dev->priv = yp;#ifdef MODULE yp->next_module = root_yellowfin_dev; root_yellowfin_dev = dev;#endif yp->chip_id = chip_id; yp->full_duplex = 1;#ifdef YELLOWFIN_DEFAULT_MEDIA yp->default_port = YELLOWFIN_DEFAULT_MEDIA;#endif#ifdef YELLOWFIN_NO_MEDIA_SWITCH yp->medialock = 1;#endif /* The lower four bits are the media type. */ if (options > 0) { yp->full_duplex = (options & 16) ? 1 : 0; yp->default_port = options & 15; if (yp->default_port) yp->medialock = 1; } /* The Yellowfin-specific entries in the device structure. */ dev->open = &yellowfin_open; dev->hard_start_xmit = &yellowfin_start_xmit; dev->stop = &yellowfin_close; dev->get_stats = &yellowfin_get_stats; dev->set_multicast_list = &set_rx_mode; if (mtu) dev->mtu = mtu; /* todo: Reset the xcvr interface and turn on heartbeat. */ return dev;}static intyellowfin_open(struct device *dev){ struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv; int ioaddr = dev->base_addr; /* Reset the chip. */ outl(0x80000000, ioaddr + DMACtrl);#ifdef SA_SHIRQ if (request_irq(dev->irq, &yellowfin_interrupt, SA_SHIRQ, card_name, dev)) { return -EAGAIN; }#else if (irq2dev_map[dev->irq] != NULL || (irq2dev_map[dev->irq] = dev) == NULL || dev->irq == 0 || request_irq(dev->irq, &yellowfin_interrupt, 0, card_name)) { return -EAGAIN; }#endif if (yellowfin_debug > 1) printk("%s: yellowfin_open() irq %d.\n", dev->name, dev->irq); MOD_INC_USE_COUNT; yellowfin_init_ring(dev); outl(virt_to_bus(yp->rx_ring), ioaddr + RxPtr); outl(virt_to_bus(yp->tx_ring), ioaddr + TxPtr); /* Set up various condition 'select' registers. There are no options here. */ outl(0x00800080, ioaddr + TxIntrSel); /* Interrupt on Tx abort */ outl(0x00800080, ioaddr + TxBranchSel); /* Branch on Tx abort */ outl(0x00400040, ioaddr + TxWaitSel); /* Wait on Tx status */ outl(0x00400040, ioaddr + RxIntrSel); /* Interrupt on Rx done */ outl(0x00400040, ioaddr + RxBranchSel); /* Branch on Rx error */ outl(0x00400040, ioaddr + RxWaitSel); /* Wait on Rx done */ /* Initialize other registers: with so many this eventually this will converted to an offset/value list. */ outl(dma_ctrl, ioaddr + DMACtrl); outw(fifo_cfg, ioaddr + FIFOcfg); /* Enable automatic generation of flow control frames, period 0xffff. */ outl(0x0030FFFF, ioaddr + FlowCtrl); if (dev->if_port == 0) dev->if_port = yp->default_port; dev->tbusy = 0; dev->interrupt = 0; yp->in_interrupt = 0; /* We are always in full-duplex mode with the current chip! */ yp->full_duplex = 1; /* Setting the Rx mode will start the Rx process. */ outw(0x01CD | (yp->full_duplex ? 2 : 0), ioaddr + Cnfg);#ifdef NEW_MULTICAST set_rx_mode(dev);#else set_rx_mode(dev, 0, 0);#endif dev->start = 1; /* Enable interrupts by setting the interrupt mask. */ outw(0x81ff, ioaddr + IntrEnb); /* See enum intr_status_bits */ outw(0x0000, ioaddr + EventStatus); /* Clear non-interrupting events */ outl(0x80008000, ioaddr + RxCtrl); /* Start Rx and Tx channels. */ outl(0x80008000, ioaddr + TxCtrl); if (yellowfin_debug > 2) { printk("%s: Done yellowfin_open().\n", dev->name); } /* Set the timer to check for link beat. */ init_timer(&yp->timer); yp->timer.expires = RUN_AT((24*HZ)/10); /* 2.4 sec. */ yp->timer.data = (unsigned long)dev; yp->timer.function = &yellowfin_timer; /* timer handler */ add_timer(&yp->timer); return 0;}static void yellowfin_timer(unsigned long data){ struct device *dev = (struct device *)data; struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv; int ioaddr = dev->base_addr; int next_tick = 0; if (yellowfin_debug > 3) { printk("%s: Yellowfin timer tick, status %8.8x.\n", dev->name, inl(ioaddr + IntrStatus)); } if (next_tick) { yp->timer.expires = RUN_AT(next_tick); add_timer(&yp->timer); }}static void yellowfin_tx_timeout(struct device *dev){ struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv; int ioaddr = dev->base_addr; printk("%s: Yellowfin transmit timed out, status %8.8x, resetting...\n", dev->name, inl(ioaddr));#ifndef __alpha__ { int i; printk(" Rx ring %8.8x: ", (int)yp->rx_ring); for (i = 0; i < RX_RING_SIZE; i++) printk(" %8.8x", (unsigned int)yp->rx_ring[i].status); printk("\n Tx ring %8.8x: ", (int)yp->tx_ring); for (i = 0; i < TX_RING_SIZE; i++) printk(" %4.4x /%4.4x", yp->tx_status[i].tx_errs, yp->tx_ring[i].status); printk("\n"); }#endif /* Perhaps we should reinitialize the hardware here. */ dev->if_port = 0; /* Stop and restart the chip's Tx processes . */ /* Trigger an immediate transmit demand. */ dev->trans_start = jiffies; yp->stats.tx_errors++; return;}/* Initialize the Rx and Tx rings, along with various 'dev' bits. */static voidyellowfin_init_ring(struct device *dev){ struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv; int i; yp->tx_full = 0; yp->cur_rx = yp->cur_tx = 0; yp->dirty_rx = yp->dirty_tx = 0; for (i = 0; i < RX_RING_SIZE; i++) { struct sk_buff *skb; int pkt_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); yp->rx_ring[i].request_cnt = pkt_buf_sz; yp->rx_ring[i].cmd = CMD_RX_BUF | INTR_ALWAYS; skb = DEV_ALLOC_SKB(pkt_buf_sz); skb_reserve(skb, 2); /* 16 byte align the IP header. */ yp->rx_skbuff[i] = skb; if (skb == NULL) break; /* Bad news! */ skb->dev = dev; /* Mark as being used by this device. */#if LINUX_VERSION_CODE > 0x10300 yp->rx_ring[i].addr = virt_to_bus(skb->tail);#else yp->rx_ring[i].addr = virt_to_bus(skb->data);#endif yp->rx_ring[i].branch_addr = virt_to_bus(&yp->rx_ring[i+1]); } /* Mark the last entry as wrapping the ring. */ yp->rx_ring[i-1].cmd = CMD_RX_BUF | INTR_ALWAYS | BRANCH_ALWAYS; yp->rx_ring[i-1].branch_addr = virt_to_bus(&yp->rx_ring[0]);/*#define NO_TXSTATS*/#ifdef NO_TXSTATS /* In this mode the Tx ring needs only a single descriptor. */ for (i = 0; i < TX_RING_SIZE; i++) { yp->tx_skbuff[i] = 0; yp->tx_ring[i].cmd = CMD_STOP; yp->tx_ring[i].branch_addr = virt_to_bus(&yp->tx_ring[i+1]); } yp->tx_ring[--i].cmd = CMD_STOP | BRANCH_ALWAYS; /* Wrap ring */ yp->tx_ring[i].branch_addr = virt_to_bus(&yp->tx_ring[0]);#else /* Tx ring needs a pair of descriptors, the second for the status. */ for (i = 0; i < TX_RING_SIZE*2; i++) { yp->tx_skbuff[i/2] = 0; yp->tx_ring[i].cmd = CMD_STOP; /* Branch on Tx error. */ yp->tx_ring[i].branch_addr = virt_to_bus(&yp->tx_ring[i+1]); i++; yp->tx_ring[i].cmd = CMD_TXSTATUS; /* Interrupt, no wait. */ yp->tx_ring[i].request_cnt = sizeof(yp->tx_status[i]); yp->tx_ring[i].addr = virt_to_bus(&yp->tx_status[i/2]); yp->tx_ring[i].branch_addr = virt_to_bus(&yp->tx_ring[i+1]); } /* Wrap ring */ yp->tx_ring[--i].cmd = CMD_TXSTATUS | BRANCH_ALWAYS | INTR_ALWAYS; yp->tx_ring[i].branch_addr = virt_to_bus(&yp->tx_ring[0]);#endif}static intyellowfin_start_xmit(struct sk_buff *skb, struct device *dev){ struct yellowfin_private *yp = (struct yellowfin_private *)dev->priv; unsigned entry; /* Block a timer-based transmit from overlapping. This could better be done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { if (jiffies - dev->trans_start < TX_TIMEOUT) return 1; yellowfin_tx_timeout(dev); return 1; } /* Caution: the write order is important here, set the base address with the "ownership" bits last. */ /* Calculate the next Tx descriptor entry. */ entry = yp->cur_tx % TX_RING_SIZE; yp->tx_skbuff[entry] = skb;#ifdef NO_TXSTATS yp->tx_ring[entry].request_cnt = skb->len; yp->tx_ring[entry].addr = virt_to_bus(skb->data); yp->tx_ring[entry].status = 0; if (entry >= TX_RING_SIZE-1) { yp->tx_ring[0].cmd = CMD_STOP; /* New stop command. */ yp->tx_ring[TX_RING_SIZE-1].cmd = CMD_TX_PKT | BRANCH_ALWAYS; } else { yp->tx_ring[entry+1].cmd = CMD_STOP; /* New stop command. */ yp->tx_ring[entry].cmd = CMD_TX_PKT | BRANCH_IFTRUE; } yp->cur_tx++;#else yp->tx_ring[entry<<1].request_cnt = skb->len; yp->tx_ring[entry<<1].addr = virt_to_bus(skb->data); /* The input_last (status-write) command is constant, but we must rewrite the subsequent 'stop' command. */ yp->cur_tx++; { unsigned next_entry = yp->cur_tx % TX_RING_SIZE; yp->tx_ring[next_entry<<1].cmd = CMD_STOP; } /* Final step -- overwrite the old 'stop' command. */ yp->tx_ring[entry<<1].cmd = (entry % 6) == 0 ? CMD_TX_PKT | INTR_ALWAYS | BRANCH_IFTRUE : CMD_TX_PKT | BRANCH_IFTRUE;#endif /* Todo: explicitly flush cache lines here. */ /* Wake the potentially-idle transmit channel. */ outl(0x10001000, dev->base_addr + TxCtrl); if (yp->cur_tx - yp->dirty_tx < TX_RING_SIZE - 1) clear_bit(0, (void*)&dev->tbusy); /* Typical path */ else yp->tx_full = 1; dev->trans_start = jiffies; if (yellowfin_debug > 4) { printk("%s: Yellowfin transmit frame #%d queued in slot %d.\n", dev->name, yp->cur_tx, entry); } return 0;}/* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */static void yellowfin_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs){#ifdef SA_SHIRQ /* Use the now-standard shared IRQ implementation. */ struct device *dev = (struct device *)dev_instance;#else struct device *dev = (struct device *)(irq2dev_map[irq]);#endif struct yellowfin_private *lp; int ioaddr, boguscnt = max_interrupt_work; if (dev == NULL) { printk ("yellowfin_interrupt(): irq %d for unknown device.\n", irq); return; } ioaddr = dev->base_addr; lp = (struct yellowfin_private *)dev->priv; if (test_and_set_bit(0, (void*)&lp->in_interrupt)) { dev->interrupt = 1; printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name); return; } do { u16 intr_status = inw(ioaddr + IntrClear); unsigned dirty_tx = lp->dirty_tx; if (yellowfin_debug > 4) printk("%s: Yellowfin interrupt, status %4.4x.\n", dev->name, intr_status); if (intr_status == 0) break; if (intr_status & (IntrRxDone | IntrEarlyRx)) yellowfin_rx(dev);#ifdef NO_TXSTATS for (; lp->cur_tx - dirty_tx > 0; dirty_tx++) { int entry = dirty_tx % TX_RING_SIZE; if (lp->tx_ring[entry].status == 0) break; /* Free the original skb. */ dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE); lp->tx_skbuff[entry] = 0; lp->stats.tx_packets++; } if (lp->tx_full && dev->tbusy && lp->cur_tx - dirty_tx < TX_RING_SIZE - 4) { /* The ring is no longer full, clear tbusy. */ lp->tx_full = 0; clear_bit(0, (void*)&dev->tbusy); mark_bh(NET_BH); } lp->dirty_tx = dirty_tx;#else if (intr_status & IntrTxDone || lp->tx_status[dirty_tx % TX_RING_SIZE].tx_errs) { for (dirty_tx = lp->dirty_tx; lp->cur_tx - dirty_tx > 0; dirty_tx++) { /* Todo: optimize this. */ int entry = dirty_tx % TX_RING_SIZE; u16 tx_errs = lp->tx_status[entry].tx_errs; if (tx_errs == 0) break; /* It still hasn't been Txed */ if (tx_errs & 0xF8100000) { /* There was an major error, log it. */#ifndef final_version if (yellowfin_debug > 1) printk("%s: Transmit error, Tx status %4.4x.\n", dev->name, tx_errs);#endif lp->stats.tx_errors++; if (tx_errs & 0xF800) lp->stats.tx_aborted_errors++; if (tx_errs & 0x0800) lp->stats.tx_carrier_errors++; if (tx_errs & 0x2000) lp->stats.tx_window_errors++; if (tx_errs & 0x8000) lp->stats.tx_fifo_errors++;#ifdef ETHER_STATS if (tx_errs & 0x1000) lp->stats.collisions16++;#endif } else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -