📄 hamachi.c
字号:
03/14/2000 KDU Further tuning: -adjusted boguscnt in hamachi_rx() to depend on interrupt mitigation parameters chosen. -Selected a set of interrupt parameters based on some extensive testing. These may change with more testing.TO DO:-Consider borrowing from the acenic driver code to check PCI_COMMAND forPCI_COMMAND_INVALIDATE. Set maximum burst size to cache line size inthat case.-fix the reset procedure. It doesn't quite work. *//* A few values that may be tweaked. *//* Size of each temporary Rx buffer, calculated as: * 1518 bytes (ethernet packet) + 2 bytes (to get 8 byte alignment for * the card) + 8 bytes of status info + 8 bytes for the Rx Checksum + * 2 more because we use skb_reserve. */#define PKT_BUF_SZ 1538/* For now, this is going to be set to the maximum size of an ethernet * packet. Eventually, we may want to make it a variable that is * related to the MTU */#define MAX_FRAME_SIZE 1518/* The rest of these values should never change. */static void hamachi_timer(unsigned long data);enum capability_flags {CanHaveMII=1, };static struct chip_info { u16 vendor_id, device_id, device_id_mask, pad; const char *name; void (*media_timer)(unsigned long data); int flags;} chip_tbl[] = { {0x1318, 0x0911, 0xffff, 0, "Hamachi GNIC-II", hamachi_timer, 0}, {0,},};/* Offsets to the Hamachi registers. Various sizes. */enum hamachi_offsets { TxDMACtrl=0x00, TxCmd=0x04, TxStatus=0x06, TxPtr=0x08, TxCurPtr=0x10, RxDMACtrl=0x20, RxCmd=0x24, RxStatus=0x26, RxPtr=0x28, RxCurPtr=0x30, PCIClkMeas=0x060, MiscStatus=0x066, ChipRev=0x68, ChipReset=0x06B, LEDCtrl=0x06C, VirtualJumpers=0x06D, GPIO=0x6E, TxChecksum=0x074, RxChecksum=0x076, TxIntrCtrl=0x078, RxIntrCtrl=0x07C, InterruptEnable=0x080, InterruptClear=0x084, IntrStatus=0x088, EventStatus=0x08C, MACCnfg=0x0A0, FrameGap0=0x0A2, FrameGap1=0x0A4, /* See enum MII_offsets below. */ MACCnfg2=0x0B0, RxDepth=0x0B8, FlowCtrl=0x0BC, MaxFrameSize=0x0CE, AddrMode=0x0D0, StationAddr=0x0D2, /* Gigabit AutoNegotiation. */ ANCtrl=0x0E0, ANStatus=0x0E2, ANXchngCtrl=0x0E4, ANAdvertise=0x0E8, ANLinkPartnerAbility=0x0EA, EECmdStatus=0x0F0, EEData=0x0F1, EEAddr=0x0F2, FIFOcfg=0x0F8,};/* Offsets to the MII-mode registers. */enum MII_offsets { MII_Cmd=0xA6, MII_Addr=0xA8, MII_Wr_Data=0xAA, MII_Rd_Data=0xAC, MII_Status=0xAE,};/* Bits in the interrupt status/mask registers. */enum intr_status_bits { IntrRxDone=0x01, IntrRxPCIFault=0x02, IntrRxPCIErr=0x04, IntrTxDone=0x100, IntrTxPCIFault=0x200, IntrTxPCIErr=0x400, LinkChange=0x10000, NegotiationChange=0x20000, StatsMax=0x40000, };/* The Hamachi Rx and Tx buffer descriptors. */struct hamachi_desc { u32 status_n_length; #if ADDRLEN == 64 u32 pad; u64 addr;#else u32 addr;#endif};/* Bits in hamachi_desc.status_n_length */enum desc_status_bits { DescOwn=0x80000000, DescEndPacket=0x40000000, DescEndRing=0x20000000, DescIntr=0x10000000,};#define PRIV_ALIGN 15 /* Required alignment mask */struct hamachi_private { /* Descriptor rings first for alignment. Tx requires a second descriptor for status. */ struct hamachi_desc rx_ring[RX_RING_SIZE]; struct hamachi_desc tx_ring[TX_RING_SIZE]; /* The addresses of receive-in-place skbuffs. */ struct sk_buff* rx_skbuff[RX_RING_SIZE]; /* The saved address of a sent-in-place packet/buffer, for skfree(). */ struct sk_buff* tx_skbuff[TX_RING_SIZE]; struct net_device_stats stats; struct timer_list timer; /* Media selection timer. */ /* Frequently used and paired value: keep adjacent for cache effect. */ spinlock_t lock; int chip_id; struct hamachi_desc *rx_head_desc; unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */ unsigned int cur_tx, dirty_tx; unsigned int rx_buf_sz; /* Based on MTU+slack. */ unsigned int tx_full:1; /* The Tx queue is full. */ unsigned int full_duplex:1; /* Full-duplex operation requested. */ unsigned int duplex_lock:1; unsigned int medialock:1; /* Do not sense media. */ unsigned int default_port:4; /* Last dev->if_port value. */ /* MII transceiver section. */ int mii_cnt; /* MII device addresses. */ u16 advertising; /* NWay media advertisement */ unsigned char phys[2]; /* MII device addresses. */ u_int32_t rx_int_var, tx_int_var; /* interrupt control variables */ u_int32_t option; /* Hold on to a copy of the options */ u_int8_t pad[16]; /* Used for 32-byte alignment */};MODULE_AUTHOR("Donald Becker <becker@scyld.com>, Eric Kasten <kasten@nscl.msu.edu>, Keith Underwood <keithu@parl.clemson.edu>");MODULE_DESCRIPTION("Packet Engines 'Hamachi' GNIC-II Gigabit Ethernet driver");MODULE_PARM(max_interrupt_work, "i");MODULE_PARM(mtu, "i");MODULE_PARM(debug, "i");MODULE_PARM(min_rx_pkt, "i");MODULE_PARM(max_rx_gap, "i");MODULE_PARM(max_rx_latency, "i");MODULE_PARM(min_tx_pkt, "i");MODULE_PARM(max_tx_gap, "i");MODULE_PARM(max_tx_latency, "i");MODULE_PARM(rx_copybreak, "i");MODULE_PARM(rx_params, "1-" __MODULE_STRING(MAX_UNITS) "i");MODULE_PARM(tx_params, "1-" __MODULE_STRING(MAX_UNITS) "i");MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");MODULE_PARM(force32, "i");static int read_eeprom(long ioaddr, int location);static int mdio_read(long ioaddr, int phy_id, int location);static void mdio_write(long ioaddr, int phy_id, int location, int value);static int hamachi_open(struct net_device *dev);#ifdef HAVE_PRIVATE_IOCTLstatic int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);#endifstatic void hamachi_timer(unsigned long data);static void hamachi_tx_timeout(struct net_device *dev);static void hamachi_init_ring(struct net_device *dev);static int hamachi_start_xmit(struct sk_buff *skb, struct net_device *dev);static void hamachi_interrupt(int irq, void *dev_instance, struct pt_regs *regs);static inline int hamachi_rx(struct net_device *dev);static inline int hamachi_tx(struct net_device *dev);static void hamachi_error(struct net_device *dev, int intr_status);static int hamachi_close(struct net_device *dev);static struct net_device_stats *hamachi_get_stats(struct net_device *dev);static void set_rx_mode(struct net_device *dev);static int __init hamachi_init_one (struct pci_dev *pdev, const struct pci_device_id *ent){ static int did_version = 0; /* Already printed version info. */ struct hamachi_private *hmp; int option, i, rx_int_var, tx_int_var, boguscnt; int chip_id = ent->driver_data; int irq = pdev->irq; long ioaddr; static int card_idx = 0; struct net_device *dev; if (hamachi_debug > 0 && did_version++ == 0) printk(version); ioaddr = pci_resource_start(pdev, 0);#ifdef __alpha__ /* Really "64 bit addrs" */ ioaddr |= (pci_resource_start(pdev, 1) << 32);#endif if (pci_enable_device(pdev)) return -EIO; pci_set_master(pdev); ioaddr = (long) ioremap(ioaddr, 0x400); if (!ioaddr) return -ENOMEM; dev = init_etherdev(NULL, sizeof(struct hamachi_private)); if (!dev) { iounmap((char *)ioaddr); return -ENOMEM; } SET_MODULE_OWNER(dev);#ifdef TX_CHECKSUM printk("check that skbcopy in ip_queue_xmit isn't happening\n"); dev->hard_header_len += 8; /* for cksum tag */#endif printk(KERN_INFO "%s: %s type %x at 0x%lx, ", dev->name, chip_tbl[chip_id].name, readl(ioaddr + ChipRev), ioaddr); for (i = 0; i < 6; i++) dev->dev_addr[i] = 1 ? read_eeprom(ioaddr, 4 + i) : readb(ioaddr + StationAddr + i); for (i = 0; i < 5; i++) printk("%2.2x:", dev->dev_addr[i]); printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq);#if ! defined(final_version) if (hamachi_debug > 4) for (i = 0; i < 0x10; i++) printk("%2.2x%s", read_eeprom(ioaddr, i), i % 16 != 15 ? " " : "\n");#endif#if 0 /* Moving this until after the force 32 check and reset. */ i = readb(ioaddr + PCIClkMeas); printk(KERN_INFO "%s: %d-bit %d Mhz PCI bus (%d), Virtual Jumpers " "%2.2x, LPA %4.4x.\n", dev->name, readw(ioaddr + MiscStatus) & 1 ? 64 : 32, i ? 2000/(i&0x7f) : 0, i&0x7f, (int)readb(ioaddr + VirtualJumpers), readw(ioaddr + ANLinkPartnerAbility));#endif hmp = dev->priv; spin_lock_init(&hmp->lock); /* Check for options being passed in */ option = card_idx < MAX_UNITS ? options[card_idx] : 0; if (dev->mem_start) option = dev->mem_start; /* If the bus size is misidentified, do the following. */ force32 = force32 ? force32 : ((option >= 0) ? ((option & 0x00000070) >> 4) : 0 ); if (force32) writeb(force32, ioaddr + VirtualJumpers); /* Hmmm, do we really need to reset the chip???. */ writeb(0x01, ioaddr + ChipReset); /* After a reset, the clock speed measurement of the PCI bus will not * be valid for a moment. Wait for a little while until it is. If * it takes more than 10ms, forget it. */ udelay(10); i = readb(ioaddr + PCIClkMeas); for (boguscnt = 0; (!(i & 0x080)) && boguscnt < 1000; boguscnt++){ udelay(10); i = readb(ioaddr + PCIClkMeas); } printk(KERN_INFO "%s: %d-bit %d Mhz PCI bus (%d), Virtual Jumpers " "%2.2x, LPA %4.4x.\n", dev->name, readw(ioaddr + MiscStatus) & 1 ? 64 : 32, i ? 2000/(i&0x7f) : 0, i&0x7f, (int)readb(ioaddr + VirtualJumpers), readw(ioaddr + ANLinkPartnerAbility)); dev->base_addr = ioaddr; dev->irq = irq; hmp->chip_id = chip_id; /* The lower four bits are the media type. */ if (option > 0) { hmp->option = option; if (option & 0x200) hmp->full_duplex = 1; else if (option & 0x080) hmp->full_duplex = 0; hmp->default_port = option & 15; if (hmp->default_port) hmp->medialock = 1; } if (card_idx < MAX_UNITS && full_duplex[card_idx] > 0) hmp->full_duplex = 1; /* lock the duplex mode if someone specified a value */ if (hmp->full_duplex || (option & 0x080)) hmp->duplex_lock = 1; /* Set interrupt tuning parameters */ max_rx_latency = max_rx_latency & 0x00ff; max_rx_gap = max_rx_gap & 0x00ff; min_rx_pkt = min_rx_pkt & 0x00ff; max_tx_latency = max_tx_latency & 0x00ff; max_tx_gap = max_tx_gap & 0x00ff; min_tx_pkt = min_tx_pkt & 0x00ff; rx_int_var = card_idx < MAX_UNITS ? rx_params[card_idx] : -1; tx_int_var = card_idx < MAX_UNITS ? tx_params[card_idx] : -1; hmp->rx_int_var = rx_int_var >= 0 ? rx_int_var : (min_rx_pkt << 16 | max_rx_gap << 8 | max_rx_latency); hmp->tx_int_var = tx_int_var >= 0 ? tx_int_var : (min_tx_pkt << 16 | max_tx_gap << 8 | max_tx_latency); /* The Hamachi-specific entries in the device structure. */ dev->open = &hamachi_open; dev->hard_start_xmit = &hamachi_start_xmit; dev->stop = &hamachi_close; dev->get_stats = &hamachi_get_stats; dev->set_multicast_list = &set_rx_mode;#ifdef HAVE_PRIVATE_IOCTL dev->do_ioctl = &mii_ioctl;#endif dev->tx_timeout = &hamachi_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; if (mtu) dev->mtu = mtu; if (chip_tbl[hmp->chip_id].flags & CanHaveMII) { int phy, phy_idx = 0; for (phy = 0; phy < 32 && phy_idx < 4; phy++) { int mii_status = mdio_read(ioaddr, phy, 1); if (mii_status != 0xffff && mii_status != 0x0000) { hmp->phys[phy_idx++] = phy; hmp->advertising = mdio_read(ioaddr, phy, 4); printk(KERN_INFO "%s: MII PHY found at address %d, status " "0x%4.4x advertising %4.4x.\n", dev->name, phy, mii_status, hmp->advertising); } } hmp->mii_cnt = phy_idx; } /* Configure gigabit autonegotiation. */ writew(0x0400, ioaddr + ANXchngCtrl); /* Enable legacy links. */ writew(0x08e0, ioaddr + ANAdvertise); /* Set our advertise word. */ writew(0x1000, ioaddr + ANCtrl); /* Enable negotiation */ card_idx++; return 0;}static int read_eeprom(long ioaddr, int location){ int bogus_cnt = 1000; /* We should check busy first - per docs -KDU */ while ((readb(ioaddr + EECmdStatus) & 0x40) && --bogus_cnt > 0); writew(location, ioaddr + EEAddr); writeb(0x02, ioaddr + EECmdStatus); bogus_cnt = 1000; while ((readb(ioaddr + EECmdStatus) & 0x40) && --bogus_cnt > 0); if (hamachi_debug > 5) printk(" EEPROM status is %2.2x after %d ticks.\n", (int)readb(ioaddr + EECmdStatus), 1000- bogus_cnt); return readb(ioaddr + EEData);}/* MII Managemen Data I/O accesses. These routines assume the MDIO controller is idle, and do not exit until the command is finished. */static int mdio_read(long ioaddr, int phy_id, int location){ int i; /* We should check busy first - per docs -KDU */ for (i = 10000; i >= 0; i--) if ((readw(ioaddr + MII_Status) & 1) == 0) break; writew((phy_id<<8) + location, ioaddr + MII_Addr); writew(0x0001, ioaddr + MII_Cmd); for (i = 10000; i >= 0; i--) if ((readw(ioaddr + MII_Status) & 1) == 0) break; return readw(ioaddr + MII_Rd_Data);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -