📄 via-rhine.c
字号:
/* The Rx and Tx buffer descriptors. */struct rx_desc { s32 rx_status; u32 desc_length; u32 addr; u32 next_desc;};struct tx_desc { s32 tx_status; u32 desc_length; u32 addr; u32 next_desc;};/* Bits in *_desc.status */enum rx_status_bits { RxOK=0x8000, RxWholePkt=0x0300, RxErr=0x008F};enum desc_status_bits { DescOwn=0x80000000, DescEndPacket=0x4000, DescIntr=0x1000,};/* Bits in rx.desc_length for extended status. */enum rx_info_bits { RxTypeTag=0x00010000, RxTypeUDP=0x00020000, RxTypeTCP=0x00040000, RxTypeIP=0x00080000, RxTypeUTChksumOK=0x00100000, RxTypeIPChksumOK=0x00200000, /* Summarized. */ RxTypeCsumMask=0x003E0000, RxTypeUDPSumOK=0x003A0000, RxTypeTCPSumOK=0x003C0000, };/* Bits in ChipCmd. */enum chip_cmd_bits { CmdInit=0x0001, CmdStart=0x0002, CmdStop=0x0004, CmdRxOn=0x0008, CmdTxOn=0x0010, CmdTxDemand=0x0020, CmdRxDemand=0x0040, CmdEarlyRx=0x0100, CmdEarlyTx=0x0200, CmdFDuplex=0x0400, CmdNoTxPoll=0x0800, CmdReset=0x8000,};#define PRIV_ALIGN 15 /* Required alignment mask *//* Use __attribute__((aligned (L1_CACHE_BYTES))) to maintain alignment within the structure. */struct netdev_private { /* Descriptor rings first for alignment. */ struct rx_desc rx_ring[RX_RING_SIZE]; struct tx_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 later free(). */ struct sk_buff* tx_skbuff[TX_RING_SIZE]; unsigned char *tx_buf[TX_RING_SIZE]; /* Tx bounce buffers */ unsigned char *tx_bufs; /* Tx bounce buffer region. */ struct net_device *next_module; /* Link for devices of this type. */ void *priv_addr; /* Unaligned address for kfree */ struct net_device_stats stats; struct timer_list timer; /* Media monitoring timer. */ int msg_level; int max_interrupt_work; int intr_enable; int chip_id, drv_flags; struct pci_dev *pci_dev; /* Frequently used values: keep some adjacent for cache effect. */ struct rx_desc *rx_head_desc; unsigned int cur_rx, dirty_rx; /* Producer/consumer ring indices */ unsigned int rx_buf_sz; /* Based on MTU+slack. */ int rx_copybreak; unsigned int cur_tx, dirty_tx; u16 chip_cmd; /* Current setting for ChipCmd */ int multicast_filter_limit; u32 mc_filter[2]; int rx_mode; unsigned int tx_full:1; /* The Tx queue is full. */ /* These values are keep track of the transceiver/media in use. */ 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; /* Last dev->if_port value. */ u8 tx_thresh, rx_thresh; /* MII transceiver section. */ int mii_cnt; /* MII device addresses. */ u16 advertising; /* NWay media advertisement */ unsigned char phys[2]; /* MII device addresses. */};static int mdio_read(struct net_device *dev, int phy_id, int location);static void mdio_write(struct net_device *dev, int phy_id, int location, int value);static int netdev_open(struct net_device *dev);static void check_duplex(struct net_device *dev);static void netdev_timer(unsigned long data);static void tx_timeout(struct net_device *dev);static void init_ring(struct net_device *dev);static int start_tx(struct sk_buff *skb, struct net_device *dev);static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs);static int netdev_rx(struct net_device *dev);static void netdev_error(struct net_device *dev, int intr_status);static void set_rx_mode(struct net_device *dev);static struct net_device_stats *get_stats(struct net_device *dev);static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);static int netdev_close(struct net_device *dev);/* A list of our installed devices, for removing the driver module. */static struct net_device *root_net_dev = NULL;#ifndef MODULEint via_rhine_probe(struct net_device *dev){ printk(KERN_INFO "%s" KERN_INFO "%s", version1, version2); return pci_drv_register(&via_rhine_drv_id, dev);}#endifstatic void *via_probe1(struct pci_dev *pdev, void *init_dev, long ioaddr, int irq, int chip_idx, int card_idx){ struct net_device *dev; struct netdev_private *np; void *priv_mem; int i, option = card_idx < MAX_UNITS ? options[card_idx] : 0; dev = init_etherdev(init_dev, 0); if (!dev) return NULL; printk(KERN_INFO "%s: %s at 0x%lx, ", dev->name, pci_tbl[chip_idx].name, ioaddr); /* We would prefer to directly read the EEPROM but access may be locked. */ for (i = 0; i < 6; i++) dev->dev_addr[i] = readb(ioaddr + StationAddr + i); if (memcmp(dev->dev_addr, "\0\0\0\0\0", 6) == 0) { /* Reload the station address from the EEPROM. */ writeb(0x20, ioaddr + MACRegEEcsr); /* Typically 2 cycles to reload. */ for (i = 0; i < 150; i++) if (! (readb(ioaddr + MACRegEEcsr) & 0x20)) break; for (i = 0; i < 6; i++) dev->dev_addr[i] = readb(ioaddr + StationAddr + i); if (memcmp(dev->dev_addr, "\0\0\0\0\0", 6) == 0) { printk(" (MISSING EEPROM ADDRESS)"); /* Fill a temp addr with the "locally administered" bit set. */ memcpy(dev->dev_addr, ">Linux", 6); } } for (i = 0; i < 5; i++) printk("%2.2x:", dev->dev_addr[i]); printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq); /* Make certain the descriptor lists are cache-aligned. */ priv_mem = kmalloc(sizeof(*np) + PRIV_ALIGN, GFP_KERNEL); /* Check for the very unlikely case of no memory. */ if (priv_mem == NULL) return NULL;#ifdef USE_IO_OPS request_region(ioaddr, pci_tbl[chip_idx].io_size, dev->name);#endif /* Reset the chip to erase previous misconfiguration. */ writew(CmdReset, ioaddr + ChipCmd); dev->base_addr = ioaddr; dev->irq = irq; dev->priv = np = (void *)(((long)priv_mem + PRIV_ALIGN) & ~PRIV_ALIGN); memset(np, 0, sizeof(*np)); np->priv_addr = priv_mem; np->next_module = root_net_dev; root_net_dev = dev; np->pci_dev = pdev; np->chip_id = chip_idx; np->drv_flags = pci_tbl[chip_idx].drv_flags; np->msg_level = (1 << debug) - 1; np->rx_copybreak = rx_copybreak; np->max_interrupt_work = max_interrupt_work; np->multicast_filter_limit = multicast_filter_limit; if (dev->mem_start) option = dev->mem_start; /* The lower four bits are the media type. */ if (option > 0) { if (option & 0x220) np->full_duplex = 1; np->default_port = option & 15; if (np->default_port) np->medialock = 1; } if (card_idx < MAX_UNITS && full_duplex[card_idx] > 0) np->full_duplex = 1; if (np->full_duplex) { printk(KERN_INFO "%s: Set to forced full duplex, autonegotiation" " disabled.\n", dev->name); np->duplex_lock = 1; } /* The chip-specific entries in the device structure. */ dev->open = &netdev_open; dev->hard_start_xmit = &start_tx; dev->stop = &netdev_close; dev->get_stats = &get_stats; dev->set_multicast_list = &set_rx_mode; dev->do_ioctl = &mii_ioctl; if (np->drv_flags & CanHaveMII) { int phy, phy_idx = 0; np->phys[0] = 1; /* Standard for this chip. */ for (phy = 1; phy < 32 && phy_idx < 4; phy++) { int mii_status = mdio_read(dev, phy, 1); if (mii_status != 0xffff && mii_status != 0x0000) { np->phys[phy_idx++] = phy; np->advertising = mdio_read(dev, phy, 4); printk(KERN_INFO "%s: MII PHY found at address %d, status " "0x%4.4x advertising %4.4x Link %4.4x.\n", dev->name, phy, mii_status, np->advertising, mdio_read(dev, phy, 5)); } } np->mii_cnt = phy_idx; } /* Allow forcing the media type. */ if (option > 0) { if (option & 0x220) np->full_duplex = 1; np->default_port = option & 0x3ff; if (np->default_port & 0x330) { np->medialock = 1; printk(KERN_INFO " Forcing %dMbs %s-duplex operation.\n", (option & 0x300 ? 100 : 10), (np->full_duplex ? "full" : "half")); if (np->mii_cnt) mdio_write(dev, np->phys[0], 0, ((option & 0x300) ? 0x2000 : 0) | /* 100mbps? */ (np->full_duplex ? 0x0100 : 0)); /* Full duplex? */ } } return dev;}/* Read and write over the MII Management Data I/O (MDIO) interface. */static int mdio_read(struct net_device *dev, int phy_id, int regnum){ long ioaddr = dev->base_addr; int boguscnt = 1024; /* Wait for a previous command to complete. */ while ((readb(ioaddr + MIICmd) & 0x60) && --boguscnt > 0) ; writeb(0x00, ioaddr + MIICmd); writeb(phy_id, ioaddr + MIIPhyAddr); writeb(regnum, ioaddr + MIIRegAddr); writeb(0x40, ioaddr + MIICmd); /* Trigger read */ boguscnt = 1024; while ((readb(ioaddr + MIICmd) & 0x40) && --boguscnt > 0) ; return readw(ioaddr + MIIData);}static void mdio_write(struct net_device *dev, int phy_id, int regnum, int value){ struct netdev_private *np = (struct netdev_private *)dev->priv; long ioaddr = dev->base_addr; int boguscnt = 1024; if (phy_id == np->phys[0]) { switch (regnum) { case 0: /* Is user forcing speed/duplex? */ if (value & 0x9000) /* Autonegotiation. */ np->duplex_lock = 0; else np->full_duplex = (value & 0x0100) ? 1 : 0; break; case 4: np->advertising = value; break; } } /* Wait for a previous command to complete. */ while ((readb(ioaddr + MIICmd) & 0x60) && --boguscnt > 0) ; writeb(0x00, ioaddr + MIICmd); writeb(phy_id, ioaddr + MIIPhyAddr); writeb(regnum, ioaddr + MIIRegAddr); writew(value, ioaddr + MIIData); writeb(0x20, ioaddr + MIICmd); /* Trigger write. */ return;}static int netdev_open(struct net_device *dev){ struct netdev_private *np = (struct netdev_private *)dev->priv; long ioaddr = dev->base_addr; int i; /* Reset the chip. */ writew(CmdReset, ioaddr + ChipCmd); MOD_INC_USE_COUNT; if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) { MOD_DEC_USE_COUNT; return -EAGAIN; } if (np->msg_level & NETIF_MSG_IFUP) printk(KERN_DEBUG "%s: netdev_open() irq %d.\n", dev->name, dev->irq); init_ring(dev); writel(virt_to_bus(np->rx_ring), ioaddr + RxRingPtr); writel(virt_to_bus(np->tx_ring), ioaddr + TxRingPtr); for (i = 0; i < 6; i++) writeb(dev->dev_addr[i], ioaddr + StationAddr + i); /* Initialize other registers. */ writew(0x0006, ioaddr + PCIBusConfig); /* Tune configuration??? */ /* Configure the FIFO thresholds. */ writeb(0x20, ioaddr + TxConfig); /* Initial threshold 32 bytes */ np->tx_thresh = 0x20; np->rx_thresh = 0x60; /* Written in set_rx_mode(). */ if (dev->if_port == 0) dev->if_port = np->default_port; set_rx_mode(dev); netif_start_tx_queue(dev); np->intr_enable = IntrRxDone | IntrRxErr | IntrRxEmpty | IntrRxOverflow| IntrRxDropped| IntrTxDone | IntrTxAbort | IntrTxUnderrun | IntrPCIErr | IntrStatsMax | IntrLinkChange | IntrMIIChange; /* Enable interrupts by setting the interrupt mask. */ writew(np->intr_enable, ioaddr + IntrEnable); np->chip_cmd = CmdStart|CmdTxOn|CmdRxOn|CmdNoTxPoll; if (np->duplex_lock) np->chip_cmd |= CmdFDuplex; writew(np->chip_cmd, ioaddr + ChipCmd); check_duplex(dev); /* The LED outputs of various MII xcvrs should be configured. */ /* For NS or Mison phys, turn on bit 1 in register 0x17 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -