📄 sis900.c
字号:
revision == SIS630A_900_REV) sis630_set_eq(net_dev, revision); /* change what cur_phy means */ if (mii_phy->phy_addr != sis_priv->cur_phy) { printk(KERN_INFO "%s: Changing transceiver to %s\n", net_dev->name, mii_phy->chip_info->name); /* disable previous PHY */ status = mdio_read(net_dev, sis_priv->cur_phy, MII_CONTROL); mdio_write(net_dev, sis_priv->cur_phy, MII_CONTROL, status | MII_CNTL_ISOLATE); /* enable next PHY */ status = mdio_read(net_dev, mii_phy->phy_addr, MII_CONTROL); mdio_write(net_dev, mii_phy->phy_addr, MII_CONTROL, status & ~MII_CNTL_ISOLATE); sis_priv->cur_phy = mii_phy->phy_addr; } sis900_check_mode(net_dev, mii_phy); } 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 * * call mii_phy->chip_info->read_mode function * to check the speed and duplex mode for sis900 */static void sis900_check_mode (struct net_device *net_dev, struct mii_phy *mii_phy){ struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; long ioaddr = net_dev->base_addr; int speed, duplex; u32 tx_flags = 0, rx_flags = 0; mii_phy->chip_info->read_mode(net_dev, sis_priv->cur_phy, &speed, &duplex); tx_flags = TxATP | (TX_DMA_BURST << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift); rx_flags = RX_DMA_BURST << 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; } outl (tx_flags, ioaddr + txcfg); outl (rx_flags, ioaddr + rxcfg);}/** * sis900_read_mode: - read media mode for sis900 internal phy * @net_dev: the net device to read mode for * @phy_addr: mii phy address * @speed: the transmit speed to be determined * @duplex: the duplex mode to be determined * * read MII_STSOUT register from sis900 internal phy * to determine the speed and duplex mode for sis900 */static void sis900_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex){ int i = 0; u32 status; /* STSOUT register is Latched on Transition, read operation updates it */ while (i++ < 2) status = mdio_read(net_dev, phy_addr, MII_STSOUT); if (status & MII_STSOUT_SPD) *speed = HW_SPEED_100_MBPS; else *speed = HW_SPEED_10_MBPS; if (status & MII_STSOUT_DPLX) *duplex = FDX_CAPABLE_FULL_SELECTED; else *duplex = FDX_CAPABLE_HALF_SELECTED; if (status & MII_STSOUT_LINK_FAIL) printk(KERN_INFO "%s: Media Link Off\n", net_dev->name); else 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");}/** * amd79c901_read_mode: - read media mode for amd79c901 phy * @net_dev: the net device to read mode for * @phy_addr: mii phy address * @speed: the transmit speed to be determined * @duplex: the duplex mode to be determined * * read MII_STATUS register from amd79c901 phy * to determine the speed and duplex mode for sis900 */static void amd79c901_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex){ int i; u16 status; for (i = 0; i < 2; i++) status = mdio_read(net_dev, phy_addr, MII_STATUS); if (status & MII_STAT_CAN_AUTO) { /* 10BASE-T PHY */ for (i = 0; i < 2; i++) status = mdio_read(net_dev, phy_addr, MII_STATUS_SUMMARY); if (status & MII_STSSUM_SPD) *speed = HW_SPEED_100_MBPS; else *speed = HW_SPEED_10_MBPS; if (status & MII_STSSUM_DPLX) *duplex = FDX_CAPABLE_FULL_SELECTED; else *duplex = FDX_CAPABLE_HALF_SELECTED; if (status & MII_STSSUM_LINK) 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"); else printk(KERN_INFO "%s: Media Link Off\n", net_dev->name); } else { /* HomePNA */ *speed = HW_SPEED_HOME; *duplex = FDX_CAPABLE_HALF_SELECTED; if (status & MII_STAT_LINK) printk(KERN_INFO "%s: Media Link On 1mbps half-duplex \n", net_dev->name); else printk(KERN_INFO "%s: Media Link Off\n", net_dev->name); }}/** * ics1893_read_mode: - read media mode for ICS1893 PHY * @net_dev: the net device to read mode for * @phy_addr: mii phy address * @speed: the transmit speed to be determined * @duplex: the duplex mode to be determined * * ICS1893 PHY use Quick Poll Detailed Status register * to determine the speed and duplex mode for sis900 */static void ics1893_read_mode(struct net_device *net_dev, int phy_addr, int *speed, int *duplex){ int i = 0; u32 status; /* MII_QPDSTS is Latched, read twice in succession will reflect the current state */ for (i = 0; i < 2; i++) status = mdio_read(net_dev, phy_addr, MII_QPDSTS); if (status & MII_STSICS_SPD) *speed = HW_SPEED_100_MBPS; else *speed = HW_SPEED_10_MBPS; if (status & MII_STSICS_DPLX) *duplex = FDX_CAPABLE_FULL_SELECTED; else *duplex = FDX_CAPABLE_HALF_SELECTED; if (status & MII_STSICS_LINKSTS) 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"); else printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);}/** * 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 = (struct sis900_private *)net_dev->priv; long ioaddr = net_dev->base_addr; unsigned long flags; int i; 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++) { if (sis_priv->tx_skbuff[i] != NULL) { dev_kfree_skb(sis_priv->tx_skbuff[i]); sis_priv->tx_skbuff[i] = 0; sis_priv->tx_ring[i].cmdsts = 0; sis_priv->tx_ring[i].bufptr = 0; sis_priv->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; /* FIXME: Should we restart the transmission thread here ?? */ outl(TxENA, ioaddr + cr); /* 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 transimt 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 = (struct sis900_private *)net_dev->priv; long ioaddr = net_dev->base_addr; unsigned int entry; unsigned long flags; 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 = virt_to_bus(skb->data); sis_priv->tx_ring[entry].cmdsts = (OWN | skb->len); outl(TxENA, ioaddr + cr); if (++sis_priv->cur_tx - sis_priv->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 (sis900_debug > 3) printk(KERN_INFO "%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 void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs){ struct net_device *net_dev = (struct net_device *)dev_instance; struct sis900_private *sis_priv = (struct sis900_private *)net_dev->priv; int boguscnt = max_interrupt_work; long ioaddr = net_dev->base_addr; u32 status; spin_lock (&sis_priv->lock); do { status = inl(ioaddr + isr); if ((status & (HIBERR|TxURN|TxERR|TxIDLE|RxORN|RxERR|RxOK)) == 0) /* nothing intresting happened */ break; /* 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) { printk(KERN_INFO "%s: Abnormal interrupt," "status %#8.8x.\n", net_dev->name, status); break; } if (--boguscnt < 0) { printk(KERN_INFO "%s: Too much work at interrupt, " "interrupt status = %#8.8x.\n", net_dev->name, status); break; } } while (1); if (sis900_debug > 3) printk(KERN_INFO "%s: exiting interrupt, " "interrupt status = 0x%#8.8x.\n", net_dev->name, inl(ioaddr + isr)); spin_unlock (&sis_priv->lock); return;}/** * 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 fucntion 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 = (struct sis900_private *)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; if (sis900_debug > 3) printk(KERN_INFO "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); while (rx_status & OWN) { unsigned int rx_size; rx_size = (rx_status & DSIZE) - CRC_SIZE; if (rx_status & (ABORT|OVERRUN|TOOLONG|RUNT|RXISERR|CRCERR|FAERR)) { /* corrupted packet received */ if (sis900_debug > 3) printk(KERN_INFO "%s: Corrupted packet " "received, buffer status = 0x%8.8x.\n", net_dev->name, rx_status); sis_priv->stats.rx_errors++; if (rx_status & OVERRUN) sis_priv->stats.rx_over_errors++; if (rx_status & (TOOLONG|RUNT)) sis_priv->stats.rx_length_errors++; if (rx_status & (RXISERR | FAERR)) sis_priv->stats.rx_frame_errors++; if (rx_status & CRCERR) sis_priv->stats.rx_crc_errors++; /* reset buffer descriptor state */ sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE; } else { struct sk_buff * skb; /* This situation should never happen, but due to some unknow bugs, it is possible that we are working on NULL sk_buff :-( */ if (sis_priv->rx_skbuff[entry] == NULL) { printk(KERN_INFO "%s: NULL pointer " "encountered in Rx ring, skipping\n", net_dev->name); break; } /* gvie the socket buffer to upper layers */ skb = sis_priv->rx_skbuff[entry]; skb_put(skb, rx_size); skb->protocol = eth_type_trans(skb, net_dev); netif_rx(skb); /* some network statistics */ if ((rx_status & BCAST) == MCAST) sis_priv->stats.multicast++; net_dev->last_rx = jiffies; sis_priv->stats.rx_bytes += rx_size; sis_priv->stats.rx_packets++; /* refill the Rx buffer, what if there is not enought memory for new socket buffer ?? */ if ((skb = dev_alloc_skb(RX_BUF_SIZE)) == NULL) { /* not enough memory for skbuff, this makes a "hole" on the buffer ring, it is not clear how the hardware will react to this kind of degenerated buffer */ printk(KERN_INFO "%s: Memory squeeze," "deferring packet.\n", net_dev->name); sis_priv->rx_skbuff[entry] = NULL; /* reset buffer descriptor state */ sis_priv->rx_ring[entry].cmdsts = 0; sis_priv->rx_ring[entry].bufptr = 0; sis_priv->stats.rx_dropped++; break; } skb->dev = net_dev; sis_priv->rx_skbuff[entry] = skb; sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE; sis_priv->rx_ring[entry].bufptr = virt_to_bus(skb->tail); sis_priv->dirty_rx++; } sis_priv->cur_rx++; entry = sis_priv->cur_rx % NUM_RX_DESC; rx_status = sis_priv->rx_ring[entry].cmdsts; } // while /* refill the Rx buffer, what if the rate of refilling is slower than
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -