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 + -
显示快捷键?