sis900.c
来自「linux 内核源代码」· C语言 代码 · 共 2,188 行 · 第 1/5 页
C
2,188 行
/* Change mode issue */ if ((mii_phy->phy_id0 == 0x001D) && ((mii_phy->phy_id1 & 0xFFF0) == 0x8000)) sis900_reset_phy(net_dev, sis_priv->cur_phy); sis630_set_eq(net_dev, sis_priv->chipset_rev); goto LookForLink; } } sis_priv->timer.expires = jiffies + next_tick; add_timer(&sis_priv->timer);}/** * sis900_check_mode - check the media mode for sis900 * @net_dev: the net device to be checked * @mii_phy: the mii phy * * Older driver gets the media mode from mii status output * register. Now we set our media capability and auto-negotiate * to get the upper bound of speed and duplex between two ends. * If the types of mii phy is HOME, it doesn't need to auto-negotiate * and autong_complete should be set to 1. */static void sis900_check_mode(struct net_device *net_dev, struct mii_phy *mii_phy){ struct sis900_private *sis_priv = net_dev->priv; long ioaddr = net_dev->base_addr; int speed, duplex; if (mii_phy->phy_types == LAN) { outl(~EXD & inl(ioaddr + cfg), ioaddr + cfg); sis900_set_capability(net_dev , mii_phy); sis900_auto_negotiate(net_dev, sis_priv->cur_phy); } else { outl(EXD | inl(ioaddr + cfg), ioaddr + cfg); speed = HW_SPEED_HOME; duplex = FDX_CAPABLE_HALF_SELECTED; sis900_set_mode(ioaddr, speed, duplex); sis_priv->autong_complete = 1; }}/** * sis900_set_mode - Set the media mode of mac register. * @ioaddr: the address of the device * @speed : the transmit speed to be determined * @duplex: the duplex mode to be determined * * Set the media mode of mac register txcfg/rxcfg according to * speed and duplex of phy. Bit EDB_MASTER_EN indicates the EDB * bus is used instead of PCI bus. When this bit is set 1, the * Max DMA Burst Size for TX/RX DMA should be no larger than 16 * double words. */static void sis900_set_mode (long ioaddr, int speed, int duplex){ u32 tx_flags = 0, rx_flags = 0; if (inl(ioaddr + cfg) & EDB_MASTER_EN) { tx_flags = TxATP | (DMA_BURST_64 << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift); rx_flags = DMA_BURST_64 << RxMXDMA_shift; } else { tx_flags = TxATP | (DMA_BURST_512 << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift); rx_flags = DMA_BURST_512 << RxMXDMA_shift; } if (speed == HW_SPEED_HOME || speed == HW_SPEED_10_MBPS) { rx_flags |= (RxDRNT_10 << RxDRNT_shift); tx_flags |= (TxDRNT_10 << TxDRNT_shift); } else { rx_flags |= (RxDRNT_100 << RxDRNT_shift); tx_flags |= (TxDRNT_100 << TxDRNT_shift); } if (duplex == FDX_CAPABLE_FULL_SELECTED) { tx_flags |= (TxCSI | TxHBI); rx_flags |= RxATX; }#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) /* Can accept Jumbo packet */ rx_flags |= RxAJAB;#endif outl (tx_flags, ioaddr + txcfg); outl (rx_flags, ioaddr + rxcfg);}/** * sis900_auto_negotiate - Set the Auto-Negotiation Enable/Reset bit. * @net_dev: the net device to read mode for * @phy_addr: mii phy address * * If the adapter is link-on, set the auto-negotiate enable/reset bit. * autong_complete should be set to 0 when starting auto-negotiation. * autong_complete should be set to 1 if we didn't start auto-negotiation. * sis900_timer will wait for link on again if autong_complete = 0. */static void sis900_auto_negotiate(struct net_device *net_dev, int phy_addr){ struct sis900_private *sis_priv = net_dev->priv; int i = 0; u32 status; for (i = 0; i < 2; i++) status = mdio_read(net_dev, phy_addr, MII_STATUS); if (!(status & MII_STAT_LINK)){ if(netif_msg_link(sis_priv)) printk(KERN_INFO "%s: Media Link Off\n", net_dev->name); sis_priv->autong_complete = 1; netif_carrier_off(net_dev); return; } /* (Re)start AutoNegotiate */ mdio_write(net_dev, phy_addr, MII_CONTROL, MII_CNTL_AUTO | MII_CNTL_RST_AUTO); sis_priv->autong_complete = 0;}/** * sis900_read_mode - read media mode for sis900 internal phy * @net_dev: the net device to read mode for * @speed : the transmit speed to be determined * @duplex : the duplex mode to be determined * * The capability of remote end will be put in mii register autorec * after auto-negotiation. Use AND operation to get the upper bound * of speed and duplex between two ends. */static void sis900_read_mode(struct net_device *net_dev, int *speed, int *duplex){ struct sis900_private *sis_priv = net_dev->priv; struct mii_phy *phy = sis_priv->mii; int phy_addr = sis_priv->cur_phy; u32 status; u16 autoadv, autorec; int i; for (i = 0; i < 2; i++) status = mdio_read(net_dev, phy_addr, MII_STATUS); if (!(status & MII_STAT_LINK)) return; /* AutoNegotiate completed */ autoadv = mdio_read(net_dev, phy_addr, MII_ANADV); autorec = mdio_read(net_dev, phy_addr, MII_ANLPAR); status = autoadv & autorec; *speed = HW_SPEED_10_MBPS; *duplex = FDX_CAPABLE_HALF_SELECTED; if (status & (MII_NWAY_TX | MII_NWAY_TX_FDX)) *speed = HW_SPEED_100_MBPS; if (status & ( MII_NWAY_TX_FDX | MII_NWAY_T_FDX)) *duplex = FDX_CAPABLE_FULL_SELECTED; sis_priv->autong_complete = 1; /* Workaround for Realtek RTL8201 PHY issue */ if ((phy->phy_id0 == 0x0000) && ((phy->phy_id1 & 0xFFF0) == 0x8200)) { if (mdio_read(net_dev, phy_addr, MII_CONTROL) & MII_CNTL_FDX) *duplex = FDX_CAPABLE_FULL_SELECTED; if (mdio_read(net_dev, phy_addr, 0x0019) & 0x01) *speed = HW_SPEED_100_MBPS; } if(netif_msg_link(sis_priv)) printk(KERN_INFO "%s: Media Link On %s %s-duplex \n", net_dev->name, *speed == HW_SPEED_100_MBPS ? "100mbps" : "10mbps", *duplex == FDX_CAPABLE_FULL_SELECTED ? "full" : "half");}/** * sis900_tx_timeout - sis900 transmit timeout routine * @net_dev: the net device to transmit * * print transmit timeout status * disable interrupts and do some tasks */static void sis900_tx_timeout(struct net_device *net_dev){ struct sis900_private *sis_priv = net_dev->priv; long ioaddr = net_dev->base_addr; unsigned long flags; int i; if(netif_msg_tx_err(sis_priv)) printk(KERN_INFO "%s: Transmit timeout, status %8.8x %8.8x \n", net_dev->name, inl(ioaddr + cr), inl(ioaddr + isr)); /* Disable interrupts by clearing the interrupt mask. */ outl(0x0000, ioaddr + imr); /* use spinlock to prevent interrupt handler accessing buffer ring */ spin_lock_irqsave(&sis_priv->lock, flags); /* discard unsent packets */ sis_priv->dirty_tx = sis_priv->cur_tx = 0; for (i = 0; i < NUM_TX_DESC; i++) { struct sk_buff *skb = sis_priv->tx_skbuff[i]; if (skb) { pci_unmap_single(sis_priv->pci_dev, sis_priv->tx_ring[i].bufptr, skb->len, PCI_DMA_TODEVICE); dev_kfree_skb_irq(skb); sis_priv->tx_skbuff[i] = NULL; sis_priv->tx_ring[i].cmdsts = 0; sis_priv->tx_ring[i].bufptr = 0; net_dev->stats.tx_dropped++; } } sis_priv->tx_full = 0; netif_wake_queue(net_dev); spin_unlock_irqrestore(&sis_priv->lock, flags); net_dev->trans_start = jiffies; /* load Transmit Descriptor Register */ outl(sis_priv->tx_ring_dma, ioaddr + txdp); /* Enable all known interrupts by setting the interrupt mask. */ outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxIDLE), ioaddr + imr); return;}/** * sis900_start_xmit - sis900 start transmit routine * @skb: socket buffer pointer to put the data being transmitted * @net_dev: the net device to transmit with * * Set the transmit buffer descriptor, * and write TxENA to enable transmit state machine. * tell upper layer if the buffer is full */static intsis900_start_xmit(struct sk_buff *skb, struct net_device *net_dev){ struct sis900_private *sis_priv = net_dev->priv; long ioaddr = net_dev->base_addr; unsigned int entry; unsigned long flags; unsigned int index_cur_tx, index_dirty_tx; unsigned int count_dirty_tx; /* Don't transmit data before the complete of auto-negotiation */ if(!sis_priv->autong_complete){ netif_stop_queue(net_dev); return 1; } spin_lock_irqsave(&sis_priv->lock, flags); /* Calculate the next Tx descriptor entry. */ entry = sis_priv->cur_tx % NUM_TX_DESC; sis_priv->tx_skbuff[entry] = skb; /* set the transmit buffer descriptor and enable Transmit State Machine */ sis_priv->tx_ring[entry].bufptr = pci_map_single(sis_priv->pci_dev, skb->data, skb->len, PCI_DMA_TODEVICE); sis_priv->tx_ring[entry].cmdsts = (OWN | skb->len); outl(TxENA | inl(ioaddr + cr), ioaddr + cr); sis_priv->cur_tx ++; index_cur_tx = sis_priv->cur_tx; index_dirty_tx = sis_priv->dirty_tx; for (count_dirty_tx = 0; index_cur_tx != index_dirty_tx; index_dirty_tx++) count_dirty_tx ++; if (index_cur_tx == index_dirty_tx) { /* dirty_tx is met in the cycle of cur_tx, buffer full */ sis_priv->tx_full = 1; netif_stop_queue(net_dev); } else if (count_dirty_tx < NUM_TX_DESC) { /* Typical path, tell upper layer that more transmission is possible */ netif_start_queue(net_dev); } else { /* buffer full, tell upper layer no more transmission */ sis_priv->tx_full = 1; netif_stop_queue(net_dev); } spin_unlock_irqrestore(&sis_priv->lock, flags); net_dev->trans_start = jiffies; if (netif_msg_tx_queued(sis_priv)) printk(KERN_DEBUG "%s: Queued Tx packet at %p size %d " "to slot %d.\n", net_dev->name, skb->data, (int)skb->len, entry); return 0;}/** * sis900_interrupt - sis900 interrupt handler * @irq: the irq number * @dev_instance: the client data object * @regs: snapshot of processor context * * The interrupt handler does all of the Rx thread work, * and cleans up after the Tx thread */static irqreturn_t sis900_interrupt(int irq, void *dev_instance){ struct net_device *net_dev = dev_instance; struct sis900_private *sis_priv = net_dev->priv; int boguscnt = max_interrupt_work; long ioaddr = net_dev->base_addr; u32 status; unsigned int handled = 0; spin_lock (&sis_priv->lock); do { status = inl(ioaddr + isr); if ((status & (HIBERR|TxURN|TxERR|TxIDLE|RxORN|RxERR|RxOK)) == 0) /* nothing intresting happened */ break; handled = 1; /* why dow't we break after Tx/Rx case ?? keyword: full-duplex */ if (status & (RxORN | RxERR | RxOK)) /* Rx interrupt */ sis900_rx(net_dev); if (status & (TxURN | TxERR | TxIDLE)) /* Tx interrupt */ sis900_finish_xmit(net_dev); /* something strange happened !!! */ if (status & HIBERR) { if(netif_msg_intr(sis_priv)) printk(KERN_INFO "%s: Abnormal interrupt," "status %#8.8x.\n", net_dev->name, status); break; } if (--boguscnt < 0) { if(netif_msg_intr(sis_priv)) printk(KERN_INFO "%s: Too much work at interrupt, " "interrupt status = %#8.8x.\n", net_dev->name, status); break; } } while (1); if(netif_msg_intr(sis_priv)) printk(KERN_DEBUG "%s: exiting interrupt, " "interrupt status = 0x%#8.8x.\n", net_dev->name, inl(ioaddr + isr)); spin_unlock (&sis_priv->lock); return IRQ_RETVAL(handled);}/** * sis900_rx - sis900 receive routine * @net_dev: the net device which receives data * * Process receive interrupt events, * put buffer to higher layer and refill buffer pool * Note: This function is called by interrupt handler, * don't do "too much" work here */static int sis900_rx(struct net_device *net_dev){ struct sis900_private *sis_priv = net_dev->priv; long ioaddr = net_dev->base_addr; unsigned int entry = sis_priv->cur_rx % NUM_RX_DESC; u32 rx_status = sis_priv->rx_ring[entry].cmdsts; int rx_work_limit; if (netif_msg_rx_status(sis_priv)) printk(KERN_DEBUG "sis900_rx, cur_rx:%4.4d, dirty_rx:%4.4d " "status:0x%8.8x\n", sis_priv->cur_rx, sis_priv->dirty_rx, rx_status); rx_work_limit = sis_priv->dirty_rx + NUM_RX_DESC - sis_priv->cur_rx; while (rx_status & OWN) { unsigned int rx_size; unsigned int data_size; if (--rx_work_limit < 0) break; data_size = rx_status & DSIZE; rx_size = data_size - CRC_SIZE;#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) /* ``TOOLONG'' flag means jumbo packet recived. */ if ((rx_status & TOOLONG) && data_size <= MAX_FRAME_SIZE) rx_status &= (~ ((unsigned int)TOOLONG));#endif if (rx_status & (ABORT|OVERRUN|TOOLONG|RUNT|RXISERR|CRCERR|FAERR)) { /* corrupted packet received */ if (netif_msg_rx_err(sis_priv)) printk(KERN_DEBUG "%s: Corrupted packet " "received, buffer status = 0x%8.8x/%d.\n", net_dev->name, rx_status, data_size); net_dev->stats.rx_errors++; if (rx_status & OVERRUN) net_dev->stats.rx_over_errors++; if (rx_status & (TOOLONG|RUNT)) net_dev->stats.rx_length_errors++; if (rx_status & (RXISERR | FAERR)) net_dev->stats.rx_frame_errors++; if (rx_status & CRCERR) net_dev->stats.rx_crc_errors++; /* reset buffer descriptor state */ sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE; } else { struct sk_buff * skb; struct sk_buff * rx_skb;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?