sis900.c
来自「linux 内核源代码」· C语言 代码 · 共 2,188 行 · 第 1/5 页
C
2,188 行
pci_unmap_single(sis_priv->pci_dev, sis_priv->rx_ring[entry].bufptr, RX_BUF_SIZE, PCI_DMA_FROMDEVICE); /* 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 to refill the buffer * so we need to recycle the old one so * as to avoid creating a memory hole * in the rx ring */ skb = sis_priv->rx_skbuff[entry]; net_dev->stats.rx_dropped++; goto refill_rx_ring; } /* 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) { if (netif_msg_rx_err(sis_priv)) printk(KERN_WARNING "%s: NULL pointer " "encountered in Rx ring\n" "cur_rx:%4.4d, dirty_rx:%4.4d\n", net_dev->name, sis_priv->cur_rx, sis_priv->dirty_rx); break; } /* give the socket buffer to upper layers */ rx_skb = sis_priv->rx_skbuff[entry]; skb_put(rx_skb, rx_size); rx_skb->protocol = eth_type_trans(rx_skb, net_dev); netif_rx(rx_skb); /* some network statistics */ if ((rx_status & BCAST) == MCAST) net_dev->stats.multicast++; net_dev->last_rx = jiffies; net_dev->stats.rx_bytes += rx_size; net_dev->stats.rx_packets++; sis_priv->dirty_rx++;refill_rx_ring: sis_priv->rx_skbuff[entry] = skb; sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE; sis_priv->rx_ring[entry].bufptr = pci_map_single(sis_priv->pci_dev, skb->data, RX_BUF_SIZE, PCI_DMA_FROMDEVICE); } 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 consuming ?? */ for (; sis_priv->cur_rx != sis_priv->dirty_rx; sis_priv->dirty_rx++) { struct sk_buff *skb; entry = sis_priv->dirty_rx % NUM_RX_DESC; if (sis_priv->rx_skbuff[entry] == NULL) { 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 */ if (netif_msg_rx_err(sis_priv)) printk(KERN_INFO "%s: Memory squeeze," "deferring packet.\n", net_dev->name); net_dev->stats.rx_dropped++; break; } sis_priv->rx_skbuff[entry] = skb; sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE; sis_priv->rx_ring[entry].bufptr = pci_map_single(sis_priv->pci_dev, skb->data, RX_BUF_SIZE, PCI_DMA_FROMDEVICE); } } /* re-enable the potentially idle receive state matchine */ outl(RxENA | inl(ioaddr + cr), ioaddr + cr ); return 0;}/** * sis900_finish_xmit - finish up transmission of packets * @net_dev: the net device to be transmitted on * * Check for error condition and free socket buffer etc * schedule for more transmission as needed * Note: This function is called by interrupt handler, * don't do "too much" work here */static void sis900_finish_xmit (struct net_device *net_dev){ struct sis900_private *sis_priv = net_dev->priv; for (; sis_priv->dirty_tx != sis_priv->cur_tx; sis_priv->dirty_tx++) { struct sk_buff *skb; unsigned int entry; u32 tx_status; entry = sis_priv->dirty_tx % NUM_TX_DESC; tx_status = sis_priv->tx_ring[entry].cmdsts; if (tx_status & OWN) { /* The packet is not transmitted yet (owned by hardware) ! * Note: the interrupt is generated only when Tx Machine * is idle, so this is an almost impossible case */ break; } if (tx_status & (ABORT | UNDERRUN | OWCOLL)) { /* packet unsuccessfully transmitted */ if (netif_msg_tx_err(sis_priv)) printk(KERN_DEBUG "%s: Transmit " "error, Tx status %8.8x.\n", net_dev->name, tx_status); net_dev->stats.tx_errors++; if (tx_status & UNDERRUN) net_dev->stats.tx_fifo_errors++; if (tx_status & ABORT) net_dev->stats.tx_aborted_errors++; if (tx_status & NOCARRIER) net_dev->stats.tx_carrier_errors++; if (tx_status & OWCOLL) net_dev->stats.tx_window_errors++; } else { /* packet successfully transmitted */ net_dev->stats.collisions += (tx_status & COLCNT) >> 16; net_dev->stats.tx_bytes += tx_status & DSIZE; net_dev->stats.tx_packets++; } /* Free the original skb. */ skb = sis_priv->tx_skbuff[entry]; pci_unmap_single(sis_priv->pci_dev, sis_priv->tx_ring[entry].bufptr, skb->len, PCI_DMA_TODEVICE); dev_kfree_skb_irq(skb); sis_priv->tx_skbuff[entry] = NULL; sis_priv->tx_ring[entry].bufptr = 0; sis_priv->tx_ring[entry].cmdsts = 0; } if (sis_priv->tx_full && netif_queue_stopped(net_dev) && sis_priv->cur_tx - sis_priv->dirty_tx < NUM_TX_DESC - 4) { /* The ring is no longer full, clear tx_full and schedule * more transmission by netif_wake_queue(net_dev) */ sis_priv->tx_full = 0; netif_wake_queue (net_dev); }}/** * sis900_close - close sis900 device * @net_dev: the net device to be closed * * Disable interrupts, stop the Tx and Rx Status Machine * free Tx and RX socket buffer */static int sis900_close(struct net_device *net_dev){ long ioaddr = net_dev->base_addr; struct sis900_private *sis_priv = net_dev->priv; struct sk_buff *skb; int i; netif_stop_queue(net_dev); /* Disable interrupts by clearing the interrupt mask. */ outl(0x0000, ioaddr + imr); outl(0x0000, ioaddr + ier); /* Stop the chip's Tx and Rx Status Machine */ outl(RxDIS | TxDIS | inl(ioaddr + cr), ioaddr + cr); del_timer(&sis_priv->timer); free_irq(net_dev->irq, net_dev); /* Free Tx and RX skbuff */ for (i = 0; i < NUM_RX_DESC; i++) { skb = sis_priv->rx_skbuff[i]; if (skb) { pci_unmap_single(sis_priv->pci_dev, sis_priv->rx_ring[i].bufptr, RX_BUF_SIZE, PCI_DMA_FROMDEVICE); dev_kfree_skb(skb); sis_priv->rx_skbuff[i] = NULL; } } for (i = 0; i < NUM_TX_DESC; i++) { 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(skb); sis_priv->tx_skbuff[i] = NULL; } } /* Green! Put the chip in low-power mode. */ return 0;}/** * sis900_get_drvinfo - Return information about driver * @net_dev: the net device to probe * @info: container for info returned * * Process ethtool command such as "ehtool -i" to show information */static void sis900_get_drvinfo(struct net_device *net_dev, struct ethtool_drvinfo *info){ struct sis900_private *sis_priv = net_dev->priv; strcpy (info->driver, SIS900_MODULE_NAME); strcpy (info->version, SIS900_DRV_VERSION); strcpy (info->bus_info, pci_name(sis_priv->pci_dev));}static u32 sis900_get_msglevel(struct net_device *net_dev){ struct sis900_private *sis_priv = net_dev->priv; return sis_priv->msg_enable;}static void sis900_set_msglevel(struct net_device *net_dev, u32 value){ struct sis900_private *sis_priv = net_dev->priv; sis_priv->msg_enable = value;}static u32 sis900_get_link(struct net_device *net_dev){ struct sis900_private *sis_priv = net_dev->priv; return mii_link_ok(&sis_priv->mii_info);}static int sis900_get_settings(struct net_device *net_dev, struct ethtool_cmd *cmd){ struct sis900_private *sis_priv = net_dev->priv; spin_lock_irq(&sis_priv->lock); mii_ethtool_gset(&sis_priv->mii_info, cmd); spin_unlock_irq(&sis_priv->lock); return 0;}static int sis900_set_settings(struct net_device *net_dev, struct ethtool_cmd *cmd){ struct sis900_private *sis_priv = net_dev->priv; int rt; spin_lock_irq(&sis_priv->lock); rt = mii_ethtool_sset(&sis_priv->mii_info, cmd); spin_unlock_irq(&sis_priv->lock); return rt;}static int sis900_nway_reset(struct net_device *net_dev){ struct sis900_private *sis_priv = net_dev->priv; return mii_nway_restart(&sis_priv->mii_info);}/** * sis900_set_wol - Set up Wake on Lan registers * @net_dev: the net device to probe * @wol: container for info passed to the driver * * Process ethtool command "wol" to setup wake on lan features. * SiS900 supports sending WoL events if a correct packet is received, * but there is no simple way to filter them to only a subset (broadcast, * multicast, unicast or arp). */static int sis900_set_wol(struct net_device *net_dev, struct ethtool_wolinfo *wol){ struct sis900_private *sis_priv = net_dev->priv; long pmctrl_addr = net_dev->base_addr + pmctrl; u32 cfgpmcsr = 0, pmctrl_bits = 0; if (wol->wolopts == 0) { pci_read_config_dword(sis_priv->pci_dev, CFGPMCSR, &cfgpmcsr); cfgpmcsr &= ~PME_EN; pci_write_config_dword(sis_priv->pci_dev, CFGPMCSR, cfgpmcsr); outl(pmctrl_bits, pmctrl_addr); if (netif_msg_wol(sis_priv)) printk(KERN_DEBUG "%s: Wake on LAN disabled\n", net_dev->name); return 0; } if (wol->wolopts & (WAKE_MAGICSECURE | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_ARP)) return -EINVAL; if (wol->wolopts & WAKE_MAGIC) pmctrl_bits |= MAGICPKT; if (wol->wolopts & WAKE_PHY) pmctrl_bits |= LINKON; outl(pmctrl_bits, pmctrl_addr); pci_read_config_dword(sis_priv->pci_dev, CFGPMCSR, &cfgpmcsr); cfgpmcsr |= PME_EN; pci_write_config_dword(sis_priv->pci_dev, CFGPMCSR, cfgpmcsr); if (netif_msg_wol(sis_priv)) printk(KERN_DEBUG "%s: Wake on LAN enabled\n", net_dev->name); return 0;}static void sis900_get_wol(struct net_device *net_dev, struct ethtool_wolinfo *wol){ long pmctrl_addr = net_dev->base_addr + pmctrl; u32 pmctrl_bits; pmctrl_bits = inl(pmctrl_addr); if (pmctrl_bits & MAGICPKT) wol->wolopts |= WAKE_MAGIC; if (pmctrl_bits & LINKON) wol->wolopts |= WAKE_PHY; wol->supported = (WAKE_PHY | WAKE_MAGIC);}static const struct ethtool_ops sis900_ethtool_ops = { .get_drvinfo = sis900_get_drvinfo, .get_msglevel = sis900_get_msglevel, .set_msglevel = sis900_set_msglevel, .get_link = sis900_get_link, .get_settings = sis900_get_settings, .set_settings = sis900_set_settings, .nway_reset = sis900_nway_reset, .get_wol = sis900_get_wol, .set_wol = sis900_set_wol};/** * mii_ioctl - process MII i/o control command * @net_dev: the net device to command for * @rq: parameter for command * @cmd: the i/o command * * Process MII command like read/write MII register */static int mii_ioctl(struct net_device *net_dev, struct ifreq *rq, int cmd){ struct sis900_private *sis_priv = net_dev->priv; struct mii_ioctl_data *data = if_mii(rq); switch(cmd) { case SIOCGMIIPHY: /* Get address of MII PHY in use. */ data->phy_id = sis_priv->mii->phy_addr; /* Fall Through */ case SIOCGMIIREG: /* Read MII PHY register. */ data->val_out = mdio_read(net_dev, data->phy_id & 0x1f, data->reg_num & 0x1f); return 0; case SIOCSMIIREG: /* Write MII PHY register. */ if (!capable(CAP_NET_ADMIN)) return -EPERM; mdio_write(net_dev, data->phy_id & 0x1f, data->reg_num & 0x1f, data->val_in); return 0; default: return -EOPNOTSUPP; }}/** * sis900_set_config - Set media type by net_device.set_config * @dev: the net device for media type change * @map: ifmap passed by ifconfig * * Set media type to 10baseT, 100baseT or 0(for auto) by ifconfig * we support only port changes. All other runtime configuration * changes will be ignored */static int sis900_set_config(struct net_device *dev, struct ifmap *map){ struct sis900_private *sis_priv = dev->priv; struct mii_phy *mii_phy = sis_priv->mii; u16 status; if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) { /* we switch on the ifmap->port field. I couldn't find anything * like a definition or standard for the values of that field. * I think the meaning of those values is device specific. But * since I would like to change the media type via the ifconfig * command I use the definition from linux/netdevice.h * (which seems to be different from the ifport(pcmcia) definition) */ switch(map->port){ case IF_PORT_UNKNOWN: /* use auto here */ dev->if_port = map->port; /* we are going to change the media type, so the Link * will be temporary down and we need to reflect that * here. When the Link comes up again, it will be * sensed by the sis_timer procedure, which also does * all the rest for us */ netif_carrier_off(dev); /* read current state */ status = mdio_read(dev, mii_phy->phy_addr, MII_CONTROL); /* enable auto negotiation and reset the negotioation * (I don't really know what the auto negatiotiation * reset really means, but it sounds for me right to * do one here) */ mdio_write(dev, mii_phy->phy_addr, MII_CONTROL, status | MII_CNTL_AUTO | MII_CNTL_RST_AUTO); break; case IF_PORT_10BASET: /* 10BaseT */ dev->if_port = map->port; /* we are going to change the media type, so the Link * will be temporary down and we need to reflect that * here. When the Link comes up again, it will be * sensed
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?