📄 82596.c
字号:
length = ETH_ZLEN; } netif_stop_queue(dev); tx_cmd = lp->tx_cmds + lp->next_tx_cmd; tbd = lp->tbds + lp->next_tx_cmd; if (tx_cmd->cmd.command) { printk(KERN_NOTICE "%s: xmit ring full, dropping packet.\n", dev->name); dev->stats.tx_dropped++; dev_kfree_skb(skb); } else { if (++lp->next_tx_cmd == TX_RING_SIZE) lp->next_tx_cmd = 0; tx_cmd->tbd = WSWAPtbd(virt_to_bus(tbd)); tbd->next = I596_NULL; tx_cmd->cmd.command = CMD_FLEX | CmdTx; tx_cmd->skb = skb; tx_cmd->pad = 0; tx_cmd->size = 0; tbd->pad = 0; tbd->size = EOF | length; tbd->data = WSWAPchar(virt_to_bus(skb->data));#ifdef __mc68000__ cache_push(virt_to_phys(skb->data), length);#endif DEB(DEB_TXADDR,print_eth(skb->data, "tx-queued")); i596_add_cmd(dev, &tx_cmd->cmd); dev->stats.tx_packets++; dev->stats.tx_bytes += length; } netif_start_queue(dev); return 0;}static void print_eth(unsigned char *add, char *str){ DECLARE_MAC_BUF(mac); DECLARE_MAC_BUF(mac2); printk(KERN_DEBUG "i596 0x%p, %s --> %s %02X%02X, %s\n", add, print_mac(mac, add + 6), print_mac(mac2, add), add[12], add[13], str);}static int io = 0x300;static int irq = 10;struct net_device * __init i82596_probe(int unit){ struct net_device *dev; int i; struct i596_private *lp; char eth_addr[8]; static int probed; int err; if (probed) return ERR_PTR(-ENODEV); probed++; dev = alloc_etherdev(0); if (!dev) return ERR_PTR(-ENOMEM); if (unit >= 0) { sprintf(dev->name, "eth%d", unit); netdev_boot_setup_check(dev); } else { dev->base_addr = io; dev->irq = irq; }#ifdef ENABLE_MVME16x_NET if (MACH_IS_MVME16x) { if (mvme16x_config & MVME16x_CONFIG_NO_ETHERNET) { printk(KERN_NOTICE "Ethernet probe disabled - chip not present\n"); err = -ENODEV; goto out; } memcpy(eth_addr, (void *) 0xfffc1f2c, 6); /* YUCK! Get addr from NOVRAM */ dev->base_addr = MVME_I596_BASE; dev->irq = (unsigned) MVME16x_IRQ_I596; }#endif#ifdef ENABLE_BVME6000_NET if (MACH_IS_BVME6000) { volatile unsigned char *rtc = (unsigned char *) BVME_RTC_BASE; unsigned char msr = rtc[3]; int i; rtc[3] |= 0x80; for (i = 0; i < 6; i++) eth_addr[i] = rtc[i * 4 + 7]; /* Stored in RTC RAM at offset 1 */ rtc[3] = msr; dev->base_addr = BVME_I596_BASE; dev->irq = (unsigned) BVME_IRQ_I596; }#endif#ifdef ENABLE_APRICOT { int checksum = 0; int ioaddr = 0x300; /* this is easy the ethernet interface can only be at 0x300 */ /* first check nothing is already registered here */ if (!request_region(ioaddr, I596_TOTAL_SIZE, DRV_NAME)) { printk(KERN_ERR "82596: IO address 0x%04x in use\n", ioaddr); err = -EBUSY; goto out; } dev->base_addr = ioaddr; for (i = 0; i < 8; i++) { eth_addr[i] = inb(ioaddr + 8 + i); checksum += eth_addr[i]; } /* checksum is a multiple of 0x100, got this wrong first time some machines have 0x100, some 0x200. The DOS driver doesn't even bother with the checksum. Some other boards trip the checksum.. but then appear as ether address 0. Trap these - AC */ if ((checksum % 0x100) || (memcmp(eth_addr, "\x00\x00\x49", 3) != 0)) { err = -ENODEV; goto out1; } dev->irq = 10; }#endif dev->mem_start = (int)__get_free_pages(GFP_ATOMIC, 0); if (!dev->mem_start) { err = -ENOMEM; goto out1; } DEB(DEB_PROBE,printk(KERN_INFO "%s: 82596 at %#3lx,", dev->name, dev->base_addr)); for (i = 0; i < 6; i++) DEB(DEB_PROBE,printk(" %2.2X", dev->dev_addr[i] = eth_addr[i])); DEB(DEB_PROBE,printk(" IRQ %d.\n", dev->irq)); DEB(DEB_PROBE,printk(KERN_INFO "%s", version)); /* The 82596-specific entries in the device structure. */ dev->open = i596_open; dev->stop = i596_close; dev->hard_start_xmit = i596_start_xmit; dev->set_multicast_list = set_multicast_list; dev->tx_timeout = i596_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; dev->priv = (void *)(dev->mem_start); lp = dev->priv; DEB(DEB_INIT,printk(KERN_DEBUG "%s: lp at 0x%08lx (%zd bytes), " "lp->scb at 0x%08lx\n", dev->name, (unsigned long)lp, sizeof(struct i596_private), (unsigned long)&lp->scb)); memset((void *) lp, 0, sizeof(struct i596_private));#ifdef __mc68000__ cache_push(virt_to_phys((void *)(dev->mem_start)), 4096); cache_clear(virt_to_phys((void *)(dev->mem_start)), 4096); kernel_set_cachemode((void *)(dev->mem_start), 4096, IOMAP_NOCACHE_SER);#endif lp->scb.command = 0; lp->scb.cmd = I596_NULL; lp->scb.rfd = I596_NULL; spin_lock_init(&lp->lock); err = register_netdev(dev); if (err) goto out2; return dev;out2:#ifdef __mc68000__ /* XXX This assumes default cache mode to be IOMAP_FULL_CACHING, * XXX which may be invalid (CONFIG_060_WRITETHROUGH) */ kernel_set_cachemode((void *)(dev->mem_start), 4096, IOMAP_FULL_CACHING);#endif free_page ((u32)(dev->mem_start));out1:#ifdef ENABLE_APRICOT release_region(dev->base_addr, I596_TOTAL_SIZE);#endifout: free_netdev(dev); return ERR_PTR(err);}static irqreturn_t i596_interrupt(int irq, void *dev_id){ struct net_device *dev = dev_id; struct i596_private *lp; short ioaddr; unsigned short status, ack_cmd = 0; int handled = 0;#ifdef ENABLE_BVME6000_NET if (MACH_IS_BVME6000) { if (*(char *) BVME_LOCAL_IRQ_STAT & BVME_ETHERR) { i596_error(irq, dev_id); return IRQ_HANDLED; } }#endif if (dev == NULL) { printk(KERN_ERR "i596_interrupt(): irq %d for unknown device.\n", irq); return IRQ_NONE; } ioaddr = dev->base_addr; lp = dev->priv; spin_lock (&lp->lock); wait_cmd(dev,lp,100,"i596 interrupt, timeout"); status = lp->scb.status; DEB(DEB_INTS,printk(KERN_DEBUG "%s: i596 interrupt, IRQ %d, status %4.4x.\n", dev->name, irq, status)); ack_cmd = status & 0xf000; if ((status & 0x8000) || (status & 0x2000)) { struct i596_cmd *ptr; handled = 1; if ((status & 0x8000)) DEB(DEB_INTS,printk(KERN_DEBUG "%s: i596 interrupt completed command.\n", dev->name)); if ((status & 0x2000)) DEB(DEB_INTS,printk(KERN_DEBUG "%s: i596 interrupt command unit inactive %x.\n", dev->name, status & 0x0700)); while ((lp->cmd_head != I596_NULL) && (lp->cmd_head->status & STAT_C)) { ptr = lp->cmd_head; DEB(DEB_STATUS,printk(KERN_DEBUG "cmd_head->status = %04x, ->command = %04x\n", lp->cmd_head->status, lp->cmd_head->command)); lp->cmd_head = ptr->v_next; lp->cmd_backlog--; switch ((ptr->command) & 0x7) { case CmdTx: { struct tx_cmd *tx_cmd = (struct tx_cmd *) ptr; struct sk_buff *skb = tx_cmd->skb; if ((ptr->status) & STAT_OK) { DEB(DEB_TXADDR,print_eth(skb->data, "tx-done")); } else { dev->stats.tx_errors++; if ((ptr->status) & 0x0020) dev->stats.collisions++; if (!((ptr->status) & 0x0040)) dev->stats.tx_heartbeat_errors++; if ((ptr->status) & 0x0400) dev->stats.tx_carrier_errors++; if ((ptr->status) & 0x0800) dev->stats.collisions++; if ((ptr->status) & 0x1000) dev->stats.tx_aborted_errors++; } dev_kfree_skb_irq(skb); tx_cmd->cmd.command = 0; /* Mark free */ break; } case CmdTDR: { unsigned short status = ((struct tdr_cmd *)ptr)->status; if (status & 0x8000) { DEB(DEB_TDR,printk(KERN_INFO "%s: link ok.\n", dev->name)); } else { if (status & 0x4000) printk(KERN_ERR "%s: Transceiver problem.\n", dev->name); if (status & 0x2000) printk(KERN_ERR "%s: Termination problem.\n", dev->name); if (status & 0x1000) printk(KERN_ERR "%s: Short circuit.\n", dev->name); DEB(DEB_TDR,printk(KERN_INFO "%s: Time %d.\n", dev->name, status & 0x07ff)); } break; } case CmdConfigure: case CmdMulticastList: /* Zap command so set_multicast_list() knows it is free */ ptr->command = 0; break; } ptr->v_next = ptr->b_next = I596_NULL; lp->last_cmd = jiffies; } ptr = lp->cmd_head; while ((ptr != I596_NULL) && (ptr != lp->cmd_tail)) { ptr->command &= 0x1fff; ptr = ptr->v_next; } if ((lp->cmd_head != I596_NULL)) ack_cmd |= CUC_START; lp->scb.cmd = WSWAPcmd(virt_to_bus(&lp->cmd_head->status)); } if ((status & 0x1000) || (status & 0x4000)) { if ((status & 0x4000)) DEB(DEB_INTS,printk(KERN_DEBUG "%s: i596 interrupt received a frame.\n", dev->name)); i596_rx(dev); /* Only RX_START if stopped - RGH 07-07-96 */ if (status & 0x1000) { if (netif_running(dev)) { DEB(DEB_ERRORS,printk(KERN_ERR "%s: i596 interrupt receive unit inactive, status 0x%x\n", dev->name, status)); ack_cmd |= RX_START; dev->stats.rx_errors++; dev->stats.rx_fifo_errors++; rebuild_rx_bufs(dev); } } } wait_cmd(dev,lp,100,"i596 interrupt, timeout"); lp->scb.command = ack_cmd;#ifdef ENABLE_MVME16x_NET if (MACH_IS_MVME16x) { /* Ack the interrupt */ volatile unsigned char *pcc2 = (unsigned char *) 0xfff42000; pcc2[0x2a] |= 0x08; }#endif#ifdef ENABLE_BVME6000_NET if (MACH_IS_BVME6000) { volatile unsigned char *ethirq = (unsigned char *) BVME_ETHIRQ_REG; *ethirq = 1; *ethirq = 3; }#endif#ifdef ENABLE_APRICOT (void) inb(ioaddr + 0x10); outb(4, ioaddr + 0xf);#endif CA(dev); DEB(DEB_INTS,printk(KERN_DEBUG "%s: exiting interrupt.\n", dev->name)); spin_unlock (&lp->lock); return IRQ_RETVAL(handled);}static int i596_close(struct net_device *dev){ struct i596_private *lp = dev->priv; unsigned long flags; netif_stop_queue(dev); DEB(DEB_INIT,printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n", dev->name, lp->scb.status)); spin_lock_irqsave(&lp->lock, flags); wait_cmd(dev,lp,100,"close1 timed out"); lp->scb.command = CUC_ABORT | RX_ABORT; CA(dev); wait_cmd(dev,lp,100,"close2 timed out"); spin_unlock_irqrestore(&lp->lock, flags); DEB(DEB_STRUCT,i596_display_data(dev)); i596_cleanup_cmd(dev,lp);#ifdef ENABLE_MVME16x_NET if (MACH_IS_MVME16x) { volatile unsigned char *pcc2 = (unsigned char *) 0xfff42000; /* Disable all ints */ pcc2[0x28] = 1; pcc2[0x2a] = 0x40; pcc2[0x2b] = 0x40; /* Set snooping bits now! */ }#endif#ifdef ENABLE_BVME6000_NET if (MACH_IS_BVME6000) { volatile unsigned char *ethirq = (unsigned char *) BVME_ETHIRQ_REG; *ethirq = 1; }#endif free_irq(dev->irq, dev); remove_rx_bufs(dev); return 0;}/* * Set or clear the multicast filter for this adaptor. */static void set_multicast_list(struct net_device *dev){ struct i596_private *lp = dev->priv; int config = 0, cnt; DEB(DEB_MULTI,printk(KERN_DEBUG "%s: set multicast list, %d entries, promisc %s, allmulti %s\n", dev->name, dev->mc_count, dev->flags & IFF_PROMISC ? "ON" : "OFF", dev->flags & IFF_ALLMULTI ? "ON" : "OFF")); if (wait_cfg(dev, &lp->cf_cmd.cmd, 1000, "config change request timed out")) return; if ((dev->flags & IFF_PROMISC) && !(lp->cf_cmd.i596_config[8] & 0x01)) { lp->cf_cmd.i596_config[8] |= 0x01; config = 1; } if (!(dev->flags & IFF_PROMISC) && (lp->cf_cmd.i596_config[8] & 0x01)) { lp->cf_cmd.i596_config[8] &= ~0x01; config = 1; } if ((dev->flags & IFF_ALLMULTI) && (lp->cf_cmd.i596_config[11] & 0x20)) { lp->cf_cmd.i596_config[11] &= ~0x20; config = 1; } if (!(dev->flags & IFF_ALLMULTI) && !(lp->cf_cmd.i596_config[11] & 0x20)) { lp->cf_cmd.i596_config[11] |= 0x20; config = 1; } if (config) { lp->cf_cmd.cmd.command = CmdConfigure; i596_add_cmd(dev, &lp->cf_cmd.cmd); } cnt = dev->mc_count; if (cnt > MAX_MC_CNT) { cnt = MAX_MC_CNT; printk(KERN_ERR "%s: Only %d multicast addresses supported", dev->name, cnt); } if (dev->mc_count > 0) { struct dev_mc_list *dmi; unsigned char *cp; struct mc_cmd *cmd; DECLARE_MAC_BUF(mac); if (wait_cfg(dev, &lp->mc_cmd.cmd, 1000, "multicast list change request timed out")) return; cmd = &lp->mc_cmd; cmd->cmd.command = CmdMulticastList; cmd->mc_cnt = dev->mc_count * 6; cp = cmd->mc_addrs; for (dmi = dev->mc_list; cnt && dmi != NULL; dmi = dmi->next, cnt--, cp += 6) { memcpy(cp, dmi->dmi_addr, 6); if (i596_debug > 1) DEB(DEB_MULTI,printk(KERN_INFO "%s: Adding address %s\n", dev->name, print_mac(mac, cp))); } i596_add_cmd(dev, &cmd->cmd); }}#ifdef MODULEstatic struct net_device *dev_82596;#ifdef ENABLE_APRICOTmodule_param(irq, int, 0);MODULE_PARM_DESC(irq, "Apricot IRQ number");#endifstatic int debug = -1;module_param(debug, int, 0);MODULE_PARM_DESC(debug, "i82596 debug mask");int __init init_module(void){ if (debug >= 0) i596_debug = debug; dev_82596 = i82596_probe(-1); if (IS_ERR(dev_82596)) return PTR_ERR(dev_82596); return 0;}void __exit cleanup_module(void){ unregister_netdev(dev_82596);#ifdef __mc68000__ /* XXX This assumes default cache mode to be IOMAP_FULL_CACHING, * XXX which may be invalid (CONFIG_060_WRITETHROUGH) */ kernel_set_cachemode((void *)(dev_82596->mem_start), 4096, IOMAP_FULL_CACHING);#endif free_page ((u32)(dev_82596->mem_start));#ifdef ENABLE_APRICOT /* If we don't do this, we can't re-insmod it later. */ release_region(dev_82596->base_addr, I596_TOTAL_SIZE);#endif free_netdev(dev_82596);}#endif /* MODULE *//* * Local variables: * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 82596.c" * End: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -