📄 sunhme.c
字号:
* the hardware, so we cannot re-read it and get a correct value. * * hp->happy_lock must be held */static int happy_meal_is_not_so_happy(struct happy_meal *hp, u32 status){ int reset = 0; /* Only print messages for non-counter related interrupts. */ if (status & (GREG_STAT_STSTERR | GREG_STAT_TFIFO_UND | GREG_STAT_MAXPKTERR | GREG_STAT_RXERR | GREG_STAT_RXPERR | GREG_STAT_RXTERR | GREG_STAT_EOPERR | GREG_STAT_MIFIRQ | GREG_STAT_TXEACK | GREG_STAT_TXLERR | GREG_STAT_TXPERR | GREG_STAT_TXTERR | GREG_STAT_SLVERR | GREG_STAT_SLVPERR)) printk(KERN_ERR "%s: Error interrupt for happy meal, status = %08x\n", hp->dev->name, status); if (status & GREG_STAT_RFIFOVF) { /* Receive FIFO overflow is harmless and the hardware will take care of it, just some packets are lost. Who cares. */ printk(KERN_DEBUG "%s: Happy Meal receive FIFO overflow.\n", hp->dev->name); } if (status & GREG_STAT_STSTERR) { /* BigMAC SQE link test failed. */ printk(KERN_ERR "%s: Happy Meal BigMAC SQE test failed.\n", hp->dev->name); reset = 1; } if (status & GREG_STAT_TFIFO_UND) { /* Transmit FIFO underrun, again DMA error likely. */ printk(KERN_ERR "%s: Happy Meal transmitter FIFO underrun, DMA error.\n", hp->dev->name); reset = 1; } if (status & GREG_STAT_MAXPKTERR) { /* Driver error, tried to transmit something larger * than ethernet max mtu. */ printk(KERN_ERR "%s: Happy Meal MAX Packet size error.\n", hp->dev->name); reset = 1; } if (status & GREG_STAT_NORXD) { /* This is harmless, it just means the system is * quite loaded and the incoming packet rate was * faster than the interrupt handler could keep up * with. */ printk(KERN_INFO "%s: Happy Meal out of receive " "descriptors, packet dropped.\n", hp->dev->name); } if (status & (GREG_STAT_RXERR|GREG_STAT_RXPERR|GREG_STAT_RXTERR)) { /* All sorts of DMA receive errors. */ printk(KERN_ERR "%s: Happy Meal rx DMA errors [ ", hp->dev->name); if (status & GREG_STAT_RXERR) printk("GenericError "); if (status & GREG_STAT_RXPERR) printk("ParityError "); if (status & GREG_STAT_RXTERR) printk("RxTagBotch "); printk("]\n"); reset = 1; } if (status & GREG_STAT_EOPERR) { /* Driver bug, didn't set EOP bit in tx descriptor given * to the happy meal. */ printk(KERN_ERR "%s: EOP not set in happy meal transmit descriptor!\n", hp->dev->name); reset = 1; } if (status & GREG_STAT_MIFIRQ) { /* MIF signalled an interrupt, were we polling it? */ printk(KERN_ERR "%s: Happy Meal MIF interrupt.\n", hp->dev->name); } if (status & (GREG_STAT_TXEACK|GREG_STAT_TXLERR|GREG_STAT_TXPERR|GREG_STAT_TXTERR)) { /* All sorts of transmit DMA errors. */ printk(KERN_ERR "%s: Happy Meal tx DMA errors [ ", hp->dev->name); if (status & GREG_STAT_TXEACK) printk("GenericError "); if (status & GREG_STAT_TXLERR) printk("LateError "); if (status & GREG_STAT_TXPERR) printk("ParityErro "); if (status & GREG_STAT_TXTERR) printk("TagBotch "); printk("]\n"); reset = 1; } if (status & (GREG_STAT_SLVERR|GREG_STAT_SLVPERR)) { /* Bus or parity error when cpu accessed happy meal registers * or it's internal FIFO's. Should never see this. */ printk(KERN_ERR "%s: Happy Meal register access SBUS slave (%s) error.\n", hp->dev->name, (status & GREG_STAT_SLVPERR) ? "parity" : "generic"); reset = 1; } if (reset) { printk(KERN_NOTICE "%s: Resetting...\n", hp->dev->name); happy_meal_init(hp); return 1; } return 0;}/* hp->happy_lock must be held */static void happy_meal_mif_interrupt(struct happy_meal *hp){ void __iomem *tregs = hp->tcvregs; printk(KERN_INFO "%s: Link status change.\n", hp->dev->name); hp->sw_bmcr = happy_meal_tcvr_read(hp, tregs, MII_BMCR); hp->sw_lpa = happy_meal_tcvr_read(hp, tregs, MII_LPA); /* Use the fastest transmission protocol possible. */ if (hp->sw_lpa & LPA_100FULL) { printk(KERN_INFO "%s: Switching to 100Mbps at full duplex.", hp->dev->name); hp->sw_bmcr |= (BMCR_FULLDPLX | BMCR_SPEED100); } else if (hp->sw_lpa & LPA_100HALF) { printk(KERN_INFO "%s: Switching to 100MBps at half duplex.", hp->dev->name); hp->sw_bmcr |= BMCR_SPEED100; } else if (hp->sw_lpa & LPA_10FULL) { printk(KERN_INFO "%s: Switching to 10MBps at full duplex.", hp->dev->name); hp->sw_bmcr |= BMCR_FULLDPLX; } else { printk(KERN_INFO "%s: Using 10Mbps at half duplex.", hp->dev->name); } happy_meal_tcvr_write(hp, tregs, MII_BMCR, hp->sw_bmcr); /* Finally stop polling and shut up the MIF. */ happy_meal_poll_stop(hp, tregs);}#ifdef TXDEBUG#define TXD(x) printk x#else#define TXD(x)#endif/* hp->happy_lock must be held */static void happy_meal_tx(struct happy_meal *hp){ struct happy_meal_txd *txbase = &hp->happy_block->happy_meal_txd[0]; struct happy_meal_txd *this; struct net_device *dev = hp->dev; int elem; elem = hp->tx_old; TXD(("TX<")); while (elem != hp->tx_new) { struct sk_buff *skb; u32 flags, dma_addr, dma_len; int frag; TXD(("[%d]", elem)); this = &txbase[elem]; flags = hme_read_desc32(hp, &this->tx_flags); if (flags & TXFLAG_OWN) break; skb = hp->tx_skbs[elem]; if (skb_shinfo(skb)->nr_frags) { int last; last = elem + skb_shinfo(skb)->nr_frags; last &= (TX_RING_SIZE - 1); flags = hme_read_desc32(hp, &txbase[last].tx_flags); if (flags & TXFLAG_OWN) break; } hp->tx_skbs[elem] = NULL; hp->net_stats.tx_bytes += skb->len; for (frag = 0; frag <= skb_shinfo(skb)->nr_frags; frag++) { dma_addr = hme_read_desc32(hp, &this->tx_addr); dma_len = hme_read_desc32(hp, &this->tx_flags); dma_len &= TXFLAG_SIZE; hme_dma_unmap(hp, dma_addr, dma_len, DMA_TODEVICE); elem = NEXT_TX(elem); this = &txbase[elem]; } dev_kfree_skb_irq(skb); hp->net_stats.tx_packets++; } hp->tx_old = elem; TXD((">")); if (netif_queue_stopped(dev) && TX_BUFFS_AVAIL(hp) > (MAX_SKB_FRAGS + 1)) netif_wake_queue(dev);}#ifdef RXDEBUG#define RXD(x) printk x#else#define RXD(x)#endif/* Originally I used to handle the allocation failure by just giving back just * that one ring buffer to the happy meal. Problem is that usually when that * condition is triggered, the happy meal expects you to do something reasonable * with all of the packets it has DMA'd in. So now I just drop the entire * ring when we cannot get a new skb and give them all back to the happy meal, * maybe things will be "happier" now. * * hp->happy_lock must be held */static void happy_meal_rx(struct happy_meal *hp, struct net_device *dev){ struct happy_meal_rxd *rxbase = &hp->happy_block->happy_meal_rxd[0]; struct happy_meal_rxd *this; int elem = hp->rx_new, drops = 0; u32 flags; RXD(("RX<")); this = &rxbase[elem]; while (!((flags = hme_read_desc32(hp, &this->rx_flags)) & RXFLAG_OWN)) { struct sk_buff *skb; int len = flags >> 16; u16 csum = flags & RXFLAG_CSUM; u32 dma_addr = hme_read_desc32(hp, &this->rx_addr); RXD(("[%d ", elem)); /* Check for errors. */ if ((len < ETH_ZLEN) || (flags & RXFLAG_OVERFLOW)) { RXD(("ERR(%08x)]", flags)); hp->net_stats.rx_errors++; if (len < ETH_ZLEN) hp->net_stats.rx_length_errors++; if (len & (RXFLAG_OVERFLOW >> 16)) { hp->net_stats.rx_over_errors++; hp->net_stats.rx_fifo_errors++; } /* Return it to the Happy meal. */ drop_it: hp->net_stats.rx_dropped++; hme_write_rxd(hp, this, (RXFLAG_OWN|((RX_BUF_ALLOC_SIZE-RX_OFFSET)<<16)), dma_addr); goto next; } skb = hp->rx_skbs[elem]; if (len > RX_COPY_THRESHOLD) { struct sk_buff *new_skb; /* Now refill the entry, if we can. */ new_skb = happy_meal_alloc_skb(RX_BUF_ALLOC_SIZE, GFP_ATOMIC); if (new_skb == NULL) { drops++; goto drop_it; } hme_dma_unmap(hp, dma_addr, RX_BUF_ALLOC_SIZE, DMA_FROMDEVICE); hp->rx_skbs[elem] = new_skb; new_skb->dev = dev; skb_put(new_skb, (ETH_FRAME_LEN + RX_OFFSET)); hme_write_rxd(hp, this, (RXFLAG_OWN|((RX_BUF_ALLOC_SIZE-RX_OFFSET)<<16)), hme_dma_map(hp, new_skb->data, RX_BUF_ALLOC_SIZE, DMA_FROMDEVICE)); skb_reserve(new_skb, RX_OFFSET); /* Trim the original skb for the netif. */ skb_trim(skb, len); } else { struct sk_buff *copy_skb = dev_alloc_skb(len + 2); if (copy_skb == NULL) { drops++; goto drop_it; } copy_skb->dev = dev; skb_reserve(copy_skb, 2); skb_put(copy_skb, len); hme_dma_sync_for_cpu(hp, dma_addr, len, DMA_FROMDEVICE); memcpy(copy_skb->data, skb->data, len); hme_dma_sync_for_device(hp, dma_addr, len, DMA_FROMDEVICE); /* Reuse original ring buffer. */ hme_write_rxd(hp, this, (RXFLAG_OWN|((RX_BUF_ALLOC_SIZE-RX_OFFSET)<<16)), dma_addr); skb = copy_skb; } /* This card is _fucking_ hot... */ skb->csum = ntohs(csum ^ 0xffff); skb->ip_summed = CHECKSUM_HW; RXD(("len=%d csum=%4x]", len, csum)); skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); dev->last_rx = jiffies; hp->net_stats.rx_packets++; hp->net_stats.rx_bytes += len; next: elem = NEXT_RX(elem); this = &rxbase[elem]; } hp->rx_new = elem; if (drops) printk(KERN_INFO "%s: Memory squeeze, deferring packet.\n", hp->dev->name); RXD((">"));}static irqreturn_t happy_meal_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct net_device *dev = (struct net_device *) dev_id; struct happy_meal *hp = dev->priv; u32 happy_status = hme_read32(hp, hp->gregs + GREG_STAT); HMD(("happy_meal_interrupt: status=%08x ", happy_status)); spin_lock(&hp->happy_lock); if (happy_status & GREG_STAT_ERRORS) { HMD(("ERRORS ")); if (happy_meal_is_not_so_happy(hp, /* un- */ happy_status)) goto out; } if (happy_status & GREG_STAT_MIFIRQ) { HMD(("MIFIRQ ")); happy_meal_mif_interrupt(hp); } if (happy_status & GREG_STAT_TXALL) { HMD(("TXALL ")); happy_meal_tx(hp); } if (happy_status & GREG_STAT_RXTOHOST) { HMD(("RXTOHOST ")); happy_meal_rx(hp, dev); } HMD(("done\n"));out: spin_unlock(&hp->happy_lock); return IRQ_HANDLED;}#ifdef CONFIG_SBUSstatic irqreturn_t quattro_sbus_interrupt(int irq, void *cookie, struct pt_regs *ptregs){ struct quattro *qp = (struct quattro *) cookie; int i; for (i = 0; i < 4; i++) { struct net_device *dev = qp->happy_meals[i]; struct happy_meal *hp = dev->priv; u32 happy_status = hme_read32(hp, hp->gregs + GREG_STAT); HMD(("quattro_interrupt: status=%08x ", happy_status)); if (!(happy_status & (GREG_STAT_ERRORS | GREG_STAT_MIFIRQ | GREG_STAT_TXALL | GREG_STAT_RXTOHOST))) continue; spin_lock(&hp->happy_lock); if (happy_status & GREG_STAT_ERRORS) { HMD(("ERRORS ")); if (happy_meal_is_not_so_happy(hp, happy_status)) goto next; } if (happy_status & GREG_STAT_MIFIRQ) { HMD(("MIFIRQ ")); happy_meal_mif_interrupt(hp); } if (happy_status & GREG_STAT_TXALL) { HMD(("TXALL ")); happy_meal_tx(hp); } if (happy_status & GREG_STAT_RXTOHOST) { HMD(("RXTOHOST ")); happy_meal_rx(hp, dev); } next: spin_unlock(&hp->happy_lock); } HMD(("done\n")); return IRQ_HANDLED;}#endifstatic int happy_meal_open(struct net_device *dev){ struct happy_meal *hp = dev->priv; int res; HMD(("happy_meal_open: ")); /* On SBUS Quattro QFE cards, all hme interrupts are concentrated * into a single source which we register handling at probe time. */ if ((hp->happy_flags & (HFLAG_QUATTRO|HFLAG_PCI)) != HFLAG_QUATTRO) { if (request_irq(dev->irq, &happy_meal_interrupt, SA_SHIRQ, dev->name, (void *)dev)) { HMD(("EAGAIN\n"));#ifdef __sparc__ printk(KERN_ERR "happy_meal(SBUS): Can't order irq %s to go.\n", __irq_itoa(dev->irq));#else printk(KERN_ERR "happy_meal(SBUS): Can't order irq %d to go.\n", dev->irq);#endif return -EAGAIN; } } HMD(("to happy_meal_init\n")); spin_lock_irq(&hp->happy_lock); res = happy_meal_init(hp); spin_unlock_irq(&hp->happy_lock); if (res && ((hp->happy_flags & (HFLAG_QUATTRO|HFLAG_PCI)) != HFLAG_QUATTRO)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -