📄 3c515.c
字号:
outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD); } } if (--i < 0) { printk(KERN_ERR "%s: Too much work in interrupt, status %4.4x. " "Disabling functions (%4.4x).\n", dev->name, status, SetStatusEnb | ((~status) & 0x7FE)); /* Disable all pending interrupts. */ outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD); outw(AckIntr | 0x7FF, ioaddr + EL3_CMD); break; } /* Acknowledge the IRQ. */ outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); } while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete)); if (corkscrew_debug > 4) printk("%s: exiting interrupt, status %4.4x.\n", dev->name, status);}static int corkscrew_rx(struct net_device *dev){ struct corkscrew_private *vp = (struct corkscrew_private *) dev->priv; int ioaddr = dev->base_addr; int i; short rx_status; if (corkscrew_debug > 5) printk(" In rx_packet(), status %4.4x, rx_status %4.4x.\n", inw(ioaddr + EL3_STATUS), inw(ioaddr + RxStatus)); while ((rx_status = inw(ioaddr + RxStatus)) > 0) { if (rx_status & 0x4000) { /* Error, update stats. */ unsigned char rx_error = inb(ioaddr + RxErrors); if (corkscrew_debug > 2) printk(" Rx error: status %2.2x.\n", rx_error); vp->stats.rx_errors++; if (rx_error & 0x01) vp->stats.rx_over_errors++; if (rx_error & 0x02) vp->stats.rx_length_errors++; if (rx_error & 0x04) vp->stats.rx_frame_errors++; if (rx_error & 0x08) vp->stats.rx_crc_errors++; if (rx_error & 0x10) vp->stats.rx_length_errors++; } else { /* The packet length: up to 4.5K!. */ short pkt_len = rx_status & 0x1fff; struct sk_buff *skb; skb = dev_alloc_skb(pkt_len + 5 + 2); if (corkscrew_debug > 4) printk("Receiving packet size %d status %4.4x.\n", pkt_len, rx_status); if (skb != NULL) { skb->dev = dev; skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ /* 'skb_put()' points to the start of sk_buff data area. */ insl(ioaddr + RX_FIFO, skb_put(skb, pkt_len), (pkt_len + 3) >> 2); outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */ skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); dev->last_rx = jiffies; vp->stats.rx_packets++; vp->stats.rx_bytes += pkt_len; /* Wait a limited time to go to next packet. */ for (i = 200; i >= 0; i--) if (! (inw(ioaddr + EL3_STATUS) & CmdInProgress)) break; continue; } else if (corkscrew_debug) printk("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, pkt_len); } outw(RxDiscard, ioaddr + EL3_CMD); vp->stats.rx_dropped++; /* Wait a limited time to skip this packet. */ for (i = 200; i >= 0; i--) if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress)) break; } return 0;}static int boomerang_rx(struct net_device *dev){ struct corkscrew_private *vp = (struct corkscrew_private *) dev->priv; int entry = vp->cur_rx % RX_RING_SIZE; int ioaddr = dev->base_addr; int rx_status; if (corkscrew_debug > 5) printk(" In boomerang_rx(), status %4.4x, rx_status %4.4x.\n", inw(ioaddr + EL3_STATUS), inw(ioaddr + RxStatus)); while ((rx_status = vp->rx_ring[entry].status) & RxDComplete) { if (rx_status & RxDError) { /* Error, update stats. */ unsigned char rx_error = rx_status >> 16; if (corkscrew_debug > 2) printk(" Rx error: status %2.2x.\n", rx_error); vp->stats.rx_errors++; if (rx_error & 0x01) vp->stats.rx_over_errors++; if (rx_error & 0x02) vp->stats.rx_length_errors++; if (rx_error & 0x04) vp->stats.rx_frame_errors++; if (rx_error & 0x08) vp->stats.rx_crc_errors++; if (rx_error & 0x10) vp->stats.rx_length_errors++; } else { /* The packet length: up to 4.5K!. */ short pkt_len = rx_status & 0x1fff; struct sk_buff *skb; vp->stats.rx_bytes += pkt_len; if (corkscrew_debug > 4) printk("Receiving packet size %d status %4.4x.\n", pkt_len, rx_status); /* Check if the packet is long enough to just accept without copying to a properly sized skbuff. */ if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 4)) != 0) { skb->dev = dev; skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ /* 'skb_put()' points to the start of sk_buff data area. */ memcpy(skb_put(skb, pkt_len), bus_to_virt(vp->rx_ring[entry]. addr), pkt_len); rx_copy++; } else { void *temp; /* Pass up the skbuff already on the Rx ring. */ skb = vp->rx_skbuff[entry]; vp->rx_skbuff[entry] = NULL; temp = skb_put(skb, pkt_len); /* Remove this checking code for final release. */ if (bus_to_virt(vp->rx_ring[entry].addr) != temp) printk("%s: Warning -- the skbuff addresses do not match" " in boomerang_rx: %p vs. %p / %p.\n", dev->name, bus_to_virt(vp-> rx_ring[entry]. addr), skb->head, temp); rx_nocopy++; } skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); dev->last_rx = jiffies; vp->stats.rx_packets++; } entry = (++vp->cur_rx) % RX_RING_SIZE; } /* Refill the Rx ring buffers. */ for (; vp->cur_rx - vp->dirty_rx > 0; vp->dirty_rx++) { struct sk_buff *skb; entry = vp->dirty_rx % RX_RING_SIZE; if (vp->rx_skbuff[entry] == NULL) { skb = dev_alloc_skb(PKT_BUF_SZ); if (skb == NULL) break; /* Bad news! */ skb->dev = dev; /* Mark as being used by this device. */ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ vp->rx_ring[entry].addr = virt_to_bus(skb->tail); vp->rx_skbuff[entry] = skb; } vp->rx_ring[entry].status = 0; /* Clear complete bit. */ } return 0;}static int corkscrew_close(struct net_device *dev){ struct corkscrew_private *vp = (struct corkscrew_private *) dev->priv; int ioaddr = dev->base_addr; int i; netif_stop_queue(dev); if (corkscrew_debug > 1) { printk("%s: corkscrew_close() status %4.4x, Tx status %2.2x.\n", dev->name, inw(ioaddr + EL3_STATUS), inb(ioaddr + TxStatus)); printk("%s: corkscrew close stats: rx_nocopy %d rx_copy %d" " tx_queued %d.\n", dev->name, rx_nocopy, rx_copy, queued_packet); } del_timer(&vp->timer); /* Turn off statistics ASAP. We update lp->stats below. */ outw(StatsDisable, ioaddr + EL3_CMD); /* Disable the receiver and transmitter. */ outw(RxDisable, ioaddr + EL3_CMD); outw(TxDisable, ioaddr + EL3_CMD); if (dev->if_port == XCVR_10base2) /* Turn off thinnet power. Green! */ outw(StopCoax, ioaddr + EL3_CMD); free_irq(dev->irq, dev); outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD); update_stats(ioaddr, dev); if (vp->full_bus_master_rx) { /* Free Boomerang bus master Rx buffers. */ outl(0, ioaddr + UpListPtr); for (i = 0; i < RX_RING_SIZE; i++) if (vp->rx_skbuff[i]) { dev_kfree_skb(vp->rx_skbuff[i]); vp->rx_skbuff[i] = 0; } } if (vp->full_bus_master_tx) { /* Free Boomerang bus master Tx buffers. */ outl(0, ioaddr + DownListPtr); for (i = 0; i < TX_RING_SIZE; i++) if (vp->tx_skbuff[i]) { dev_kfree_skb(vp->tx_skbuff[i]); vp->tx_skbuff[i] = 0; } } return 0;}static struct net_device_stats *corkscrew_get_stats(struct net_device *dev){ struct corkscrew_private *vp = (struct corkscrew_private *) dev->priv; unsigned long flags; if (netif_running(dev)) { save_flags(flags); cli(); update_stats(dev->base_addr, dev); restore_flags(flags); } return &vp->stats;}/* Update statistics. Unlike with the EL3 we need not worry about interrupts changing the window setting from underneath us, but we must still guard against a race condition with a StatsUpdate interrupt updating the table. This is done by checking that the ASM (!) code generated uses atomic updates with '+='. */static void update_stats(int ioaddr, struct net_device *dev){ struct corkscrew_private *vp = (struct corkscrew_private *) dev->priv; /* Unlike the 3c5x9 we need not turn off stats updates while reading. */ /* Switch to the stats window, and read everything. */ EL3WINDOW(6); vp->stats.tx_carrier_errors += inb(ioaddr + 0); vp->stats.tx_heartbeat_errors += inb(ioaddr + 1); /* Multiple collisions. */ inb(ioaddr + 2); vp->stats.collisions += inb(ioaddr + 3); vp->stats.tx_window_errors += inb(ioaddr + 4); vp->stats.rx_fifo_errors += inb(ioaddr + 5); vp->stats.tx_packets += inb(ioaddr + 6); vp->stats.tx_packets += (inb(ioaddr + 9) & 0x30) << 4; /* Rx packets */ inb(ioaddr + 7); /* Must read to clear */ /* Tx deferrals */ inb(ioaddr + 8); /* Don't bother with register 9, an extension of registers 6&7. If we do use the 6&7 values the atomic update assumption above is invalid. */ inw(ioaddr + 10); /* Total Rx and Tx octets. */ inw(ioaddr + 12); /* New: On the Vortex we must also clear the BadSSD counter. */ EL3WINDOW(4); inb(ioaddr + 12); /* We change back to window 7 (not 1) with the Vortex. */ EL3WINDOW(7); return;}/* This new version of set_rx_mode() supports v1.4 kernels. The Vortex chip has no documented multicast filter, so the only multicast setting is to receive all multicast frames. At least the chip has a very clean way to set the mode, unlike many others. */static void set_rx_mode(struct net_device *dev){ int ioaddr = dev->base_addr; short new_mode; if (dev->flags & IFF_PROMISC) { if (corkscrew_debug > 3) printk("%s: Setting promiscuous mode.\n", dev->name); new_mode = SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm; } else if ((dev->mc_list) || (dev->flags & IFF_ALLMULTI)) { new_mode = SetRxFilter | RxStation | RxMulticast | RxBroadcast; } else new_mode = SetRxFilter | RxStation | RxBroadcast; outw(new_mode, ioaddr + EL3_CMD);}/** * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls * @dev: network interface on which out-of-band action is to be performed * @useraddr: userspace address to which data is to be read and returned * * Process the various commands of the SIOCETHTOOL interface. */static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr){ u32 ethcmd; /* dev_ioctl() in ../../net/core/dev.c has already checked capable(CAP_NET_ADMIN), so don't bother with that here. */ if (get_user(ethcmd, (u32 *)useraddr)) return -EFAULT; switch (ethcmd) { case ETHTOOL_GDRVINFO: { struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; strcpy (info.driver, DRV_NAME); strcpy (info.version, DRV_VERSION); sprintf(info.bus_info, "ISA 0x%lx", dev->base_addr); if (copy_to_user (useraddr, &info, sizeof (info))) return -EFAULT; return 0; } /* get message-level */ case ETHTOOL_GMSGLVL: { struct ethtool_value edata = {ETHTOOL_GMSGLVL}; edata.data = corkscrew_debug; if (copy_to_user(useraddr, &edata, sizeof(edata))) return -EFAULT; return 0; } /* set message-level */ case ETHTOOL_SMSGLVL: { struct ethtool_value edata; if (copy_from_user(&edata, useraddr, sizeof(edata))) return -EFAULT; corkscrew_debug = edata.data; return 0; } default: break; } return -EOPNOTSUPP;}/** * netdev_ioctl: Handle network interface ioctls * @dev: network interface on which out-of-band action is to be performed * @rq: user request data * @cmd: command issued by user * * Process the various out-of-band ioctls passed to this driver. */static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd){ int rc = 0; switch (cmd) { case SIOCETHTOOL: rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data); break; default: rc = -EOPNOTSUPP; break; } return rc;} #ifdef MODULEvoid cleanup_module(void){ struct net_device *next_dev; /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ while (root_corkscrew_dev) { next_dev = ((struct corkscrew_private *) root_corkscrew_dev-> priv)->next_module; if (root_corkscrew_dev->dma) free_dma(root_corkscrew_dev->dma); unregister_netdev(root_corkscrew_dev); outw(TotalReset, root_corkscrew_dev->base_addr + EL3_CMD); release_region(root_corkscrew_dev->base_addr, CORKSCREW_TOTAL_SIZE); kfree(root_corkscrew_dev); root_corkscrew_dev = next_dev; }}#endif /* MODULE *//* * Local variables: * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c515.c" * c-indent-level: 4 * tab-width: 4 * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -