📄 sis900.c
字号:
consuming ?? */ for (;sis_priv->cur_rx - sis_priv->dirty_rx > 0; 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 */ printk(KERN_INFO "%s: Memory squeeze," "deferring packet.\n", net_dev->name); 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); } } /* re-enable the potentially idle receive state matchine */ outl(RxENA , 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 = (struct sis900_private *)net_dev->priv; for (; sis_priv->dirty_tx < sis_priv->cur_tx; sis_priv->dirty_tx++) { 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 transmited 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 transmited */ 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 transmited */ 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. */ dev_kfree_skb_irq(sis_priv->tx_skbuff[entry]); 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 = (struct sis900_private *)net_dev->priv; 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, 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++) { if (sis_priv->rx_skbuff[i] != NULL) dev_kfree_skb(sis_priv->rx_skbuff[i]); sis_priv->rx_skbuff[i] = 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; } /* Green! Put the chip in low-power mode. */ MOD_DEC_USE_COUNT; return 0;}/** * 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 = (struct sis900_private *)net_dev->priv; u16 *data = (u16 *)&rq->ifr_data; switch(cmd) { case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ data[0] = sis_priv->mii->phy_addr; /* Fall Through */ case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ data[3] = mdio_read(net_dev, data[0] & 0x1f, data[1] & 0x1f); return 0; case SIOCDEVPRIVATE+2: /* Write the specified MII register */ if (!capable(CAP_NET_ADMIN)) return -EPERM; mdio_write(net_dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); 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 = (struct sis900_private *)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 = (struct sis900_private *)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 * * 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 */static u16 sis900_compute_hashtable_index(u8 *addr){/* 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 7 most siginifant bits */ 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. */static void set_rx_mode(struct net_device *net_dev){ long ioaddr = net_dev->base_addr; u16 mc_filter[8]; /* 128 bits multicast hash table */ int i; u32 rx_mode; if (net_dev->flags & IFF_PROMISC) { /* Accept any kinds of packets */ rx_mode = RFPromiscuous; for (i = 0; i < 8; 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 < 8; 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; i < 8; i++) mc_filter[i]=0; 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), mc_filter); } /* update Multicast Hash Table in Receive Filter */ for (i = 0; i < 8; 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 */static void sis900_reset(struct net_device *net_dev){ long ioaddr = net_dev->base_addr; int i = 0; u32 status = TxRCMP | RxRCMP; outl(0, ioaddr + ier); outl(0, ioaddr + imr); outl(0, ioaddr + rfcr); outl(RxRESET | TxRESET | RESET, ioaddr + cr); /* Check that the chip has finished the reset. */ while (status && (i++ < 1000)) { status ^= (inl(isr + ioaddr) & status); } 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_dev->driver_data; unregister_netdev(net_dev); release_region(net_dev->base_addr, SIS900_TOTAL_SIZE); kfree(net_dev);}#define SIS900_MODULE_NAME "sis900"static struct pci_driver sis900_pci_driver = { name: SIS900_MODULE_NAME, id_table: sis900_pci_tbl, probe: sis900_probe, remove: sis900_remove,};static int __init sis900_init_module(void){ printk(KERN_INFO "%s", version); 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 + -