sis900.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,170 行 · 第 1/5 页
C
2,170 行
} /* 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 fucntion 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 (sis900_debug > 3) printk(KERN_INFO "%s: Transmit " "error, Tx status %8.8x.\n", net_dev->name, tx_status); sis_priv->stats.tx_errors++; if (tx_status & UNDERRUN) sis_priv->stats.tx_fifo_errors++; if (tx_status & ABORT) sis_priv->stats.tx_aborted_errors++; if (tx_status & NOCARRIER) sis_priv->stats.tx_carrier_errors++; if (tx_status & OWCOLL) sis_priv->stats.tx_window_errors++; } else { /* packet successfully transmitted */ sis_priv->stats.collisions += (tx_status & COLCNT) >> 16; sis_priv->stats.tx_bytes += tx_status & DSIZE; sis_priv->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 intsis900_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 struct ethtool_ops sis900_ethtool_ops = { .get_drvinfo = sis900_get_drvinfo,};/** * 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_get_stats - Get sis900 read/write statistics * @net_dev: the net device to get statistics for * * get tx/rx statistics for sis900 */static struct net_device_stats *sis900_get_stats(struct net_device *net_dev){ struct sis900_private *sis_priv = net_dev->priv; return &sis_priv->stats;}/** * 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 by the sis_timer procedure, which also does all the rest for us */ netif_carrier_off(dev); /* set Speed to 10Mbps */ /* read current state */ status = mdio_read(dev, mii_phy->phy_addr, MII_CONTROL); /* disable auto negotiation and force 10MBit mode*/ mdio_write(dev, mii_phy->phy_addr, MII_CONTROL, status & ~(MII_CNTL_SPEED | MII_CNTL_AUTO)); break; case IF_PORT_100BASET: /* 100BaseT */ case IF_PORT_100BASETX: /* 100BaseTx */ 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); /* set Speed to 100Mbps */ /* disable auto negotiation and enable 100MBit Mode */ status = mdio_read(dev, mii_phy->phy_addr, MII_CONTROL); mdio_write(dev, mii_phy->phy_addr, MII_CONTROL, (status & ~MII_CNTL_SPEED) | MII_CNTL_SPEED); break; case IF_PORT_10BASE2: /* 10Base2 */ case IF_PORT_AUI: /* AUI */ case IF_PORT_100BASEFX: /* 100BaseFx */ /* These Modes are not supported (are they?)*/ printk(KERN_INFO "Not supported"); return -EOPNOTSUPP; break; default: printk(KERN_INFO "Invalid"); return -EINVAL; } } return 0;}/** * sis900_mcast_bitnr - compute hashtable index * @addr: multicast address * @revision: revision id of chip * * SiS 900 uses the most sigificant 7 bits to index a 128 bits multicast * hash table, which makes this function a little bit different from other drivers * SiS 900 B0 & 635 M/B uses the most significat 8 bits to index 256 bits * multicast hash table. */static inline u16 sis900_mcast_bitnr(u8 *addr, u8 revision){ u32 crc = ether_crc(6, addr); /* leave 8 or 7 most siginifant bits */ if ((revision >= SIS635A_900_REV) || (revision == SIS900B_900_REV)) return ((int)(crc >> 24)); else return ((int)(crc >> 25));}/** * set_rx_mode - Set SiS900 receive mode * @net_dev: the net device to be set * * Set SiS900 receive mode for promiscuous, multicast, or broadcast mode. * And set the appropriate multicast filter. * Multicast hash table changes from 128 to 256 bits for 635M/B & 900B0. */static void set_rx_mode(struct net_device *net_dev){ long ioaddr = net_dev->base_addr; struct sis900_private * sis_priv = net_dev->priv; u16 mc_filter[16] = {0}; /* 256/128 bits multicast hash table */ int i, table_entries; u32 rx_mode; u8 revision; /* 635 Hash Table entires = 256(2^16) */ pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision); if((revision >= SIS635A_900_REV) || (revision == SIS900B_900_REV)) table_entries = 16; else table_entries = 8; if (net_dev->flags & IFF_PROMISC) { /* Accept any kinds of packets */ rx_mode = RFPromiscuous; for (i = 0; i < table_entries; i++) mc_filter[i] = 0xffff; } else if ((net_dev->mc_count > multicast_filter_limit) || (net_dev->flags & IFF_ALLMULTI)) { /* too many multicast addresses or accept all multicast packet */ rx_mode = RFAAB | RFAAM; for (i = 0; i < table_entries; i++) mc_filter[i] = 0xffff; } else { /* Accept Broadcast packet, destination address matchs our MAC address, use Receive Filter to reject unwanted MCAST packet */ struct dev_mc_list *mclist; rx_mode = RFAAB; for (i = 0, mclist = net_dev->mc_list; mclist && i < net_dev->mc_count; i++, mclist = mclist->next) { unsigned int bit_nr = sis900_mcast_bitnr(mclist->dmi_addr, revision); mc_filter[bit_nr >> 4] |= (1 << (bit_nr & 0xf)); } } /* update Multicast Hash Table in Receive Filter */ for (i = 0; i < table_entries; i++) { /* why plus 0x04 ??, That makes the correct value for hash table. */ outl((u32)(0x00000004+i) << RFADDR_shift, ioaddr + rfcr); outl(mc_filter[i], ioaddr + rfdr); } outl(RFEN | rx_mode, ioaddr + rfcr); /* sis900 is capatable of looping back packet at MAC level for debugging purpose */ if (net_dev->flags & IFF_LOOPBACK) { u32 cr_saved; /* We must disable Tx/Rx before setting loopback mode */ cr_saved = inl(ioaddr + cr); outl(cr_saved | TxDIS | RxDIS, ioaddr + cr); /* enable loopback */ outl(inl(ioaddr + txcfg) | TxMLB, ioaddr + txcfg); outl(inl(ioaddr + rxcfg) | RxATX, ioaddr + rxcfg); /* restore cr */ outl(cr_saved, ioaddr + cr); } return;}/** * sis900_reset - Reset sis900 MAC * @net_dev: the net device to reset * * reset sis900 MAC and wait until finished * reset through command register * change backoff algorithm for 900B0 & 635 M/B */static void sis900_reset(struct net_device *net_dev){ struct sis900_private * sis_priv = net_dev->priv; long ioaddr = net_dev->base_addr; int i = 0; u32 status = TxRCMP | RxRCMP; u8 revision; outl(0, ioaddr + ier); outl(0, ioaddr + imr); outl(0, ioaddr + rfcr); outl(RxRESET | TxRESET | RESET | inl(ioaddr + cr), ioaddr + cr); /* Check that the chip has finished the reset. */ while (status && (i++ < 1000)) { status ^= (inl(isr + ioaddr) & status); } pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision); if( (revision >= SIS635A_900_REV) || (revision == SIS900B_900_REV) ) outl(PESEL | RND_CNT, ioaddr + cfg); else outl(PESEL, ioaddr + cfg);}/** *
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?