📄 sis900.c
字号:
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] = 0; } } 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] = 0; } } /* Green! Put the chip in low-power mode. */ return 0;}/** * netdev_ethtool_ioctl: - For the basic support of ethtool * @net_dev: the net device to command for * @useraddr: start address of interface request * * Process ethtool command such as "ehtool -i" to show information */static int netdev_ethtool_ioctl (struct net_device *net_dev, void *useraddr){ struct sis900_private *sis_priv = net_dev->priv; u32 ethcmd; if (copy_from_user (ðcmd, useraddr, sizeof (ethcmd))) return -EFAULT; switch (ethcmd) { case ETHTOOL_GDRVINFO: { struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; strcpy (info.driver, SIS900_MODULE_NAME); strcpy (info.version, SIS900_DRV_VERSION); strcpy (info.bus_info, sis_priv->pci_dev->slot_name); if (copy_to_user (useraddr, &info, sizeof (info))) return -EFAULT; return 0; } default: break; } return -EOPNOTSUPP;}/** * 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 = (struct mii_ioctl_data *)&rq->ifr_data; switch(cmd) { case SIOCETHTOOL: return netdev_ethtool_ioctl(net_dev, (void *) rq->ifr_data); case SIOCGMIIPHY: /* Get address of MII PHY in use. */ case SIOCDEVPRIVATE: /* for binary compat, remove in 2.5 */ data->phy_id = sis_priv->mii->phy_addr; /* Fall Through */ case SIOCGMIIREG: /* Read MII PHY register. */ case SIOCDEVPRIVATE+1: /* for binary compat, remove in 2.5 */ data->val_out = mdio_read(net_dev, data->phy_id & 0x1f, data->reg_num & 0x1f); return 0; case SIOCSMIIREG: /* Write MII PHY register. */ case SIOCDEVPRIVATE+2: /* for binary compat, remove in 2.5 */ 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 dont 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_compute_hashtable_index: - 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 u16 sis900_compute_hashtable_index(u8 *addr, u8 revision){/* what is the correct value of the POLYNOMIAL ?? Donald Becker use 0x04C11DB7U Joseph Zbiciak im14u2c@primenet.com gives me the correct answer, thank you Joe !! */#define POLYNOMIAL 0x04C11DB7L u32 crc = 0xffffffff, msb; int i, j; u32 byte; for (i = 0; i < 6; i++) { byte = *addr++; for (j = 0; j < 8; j++) { msb = crc >> 31; crc <<= 1; if (msb ^ (byte & 1)) { crc ^= POLYNOMIAL; } byte >>= 1; } } /* 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) set_bit(sis900_compute_hashtable_index(mclist->dmi_addr, revision), mc_filter); } /* 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);}/** * sis900_remove: - Remove sis900 device * @pci_dev: the pci device to be removed * * remove and release SiS900 net device */static void __devexit sis900_remove(struct pci_dev *pci_dev){ struct net_device *net_dev = pci_get_drvdata(pci_dev); struct sis900_private * sis_priv = net_dev->priv; struct mii_phy *phy = NULL; while (sis_priv->first_mii) { phy = sis_priv->first_mii; sis_priv->first_mii = phy->next; kfree(phy); } pci_free_consistent(pci_dev, RX_TOTAL_SIZE, sis_priv->rx_ring, sis_priv->rx_ring_dma); pci_free_consistent(pci_dev, TX_TOTAL_SIZE, sis_priv->tx_ring, sis_priv->tx_ring_dma); unregister_netdev(net_dev); kfree(net_dev); pci_release_regions(pci_dev); pci_set_drvdata(pci_dev, NULL);}static struct pci_driver sis900_pci_driver = { name: SIS900_MODULE_NAME, id_table: sis900_pci_tbl, probe: sis900_probe, remove: __devexit_p(sis900_remove),};static int __init sis900_init_module(void){/* when a module, this is printed whether or not devices are found in probe */#ifdef MODULE printk(version);#endif return pci_module_init(&sis900_pci_driver);}static void __exit sis900_cleanup_module(void){ pci_unregister_driver(&sis900_pci_driver);}module_init(sis900_init_module);module_exit(sis900_cleanup_module);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -