sis900.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,170 行 · 第 1/5 页

C
2,170
字号
	if( inl(ioaddr + cfg) & EDB_MASTER_EN ){		tx_flags = TxATP | (DMA_BURST_64 << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift);		rx_flags = DMA_BURST_64 << RxMXDMA_shift;	}	else{		tx_flags = TxATP | (DMA_BURST_512 << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift);		rx_flags = DMA_BURST_512 << RxMXDMA_shift;	}	if (speed == HW_SPEED_HOME || speed == HW_SPEED_10_MBPS ) {		rx_flags |= (RxDRNT_10 << RxDRNT_shift);		tx_flags |= (TxDRNT_10 << TxDRNT_shift);	}	else {		rx_flags |= (RxDRNT_100 << RxDRNT_shift);		tx_flags |= (TxDRNT_100 << TxDRNT_shift);	}	if (duplex == FDX_CAPABLE_FULL_SELECTED) {		tx_flags |= (TxCSI | TxHBI);		rx_flags |= RxATX;	}	outl (tx_flags, ioaddr + txcfg);	outl (rx_flags, ioaddr + rxcfg);}/** *	sis900_auto_negotiate - Set the Auto-Negotiation Enable/Reset bit. *	@net_dev: the net device to read mode for *	@phy_addr: mii phy address * *	If the adapter is link-on, set the auto-negotiate enable/reset bit. *	autong_complete should be set to 0 when starting auto-negotiation. *	autong_complete should be set to 1 if we didn't start auto-negotiation. *	sis900_timer will wait for link on again if autong_complete = 0. */static void sis900_auto_negotiate(struct net_device *net_dev, int phy_addr){	struct sis900_private *sis_priv = net_dev->priv;	int i = 0;	u32 status;		while (i++ < 2)		status = mdio_read(net_dev, phy_addr, MII_STATUS);	if (!(status & MII_STAT_LINK)){		printk(KERN_INFO "%s: Media Link Off\n", net_dev->name);		sis_priv->autong_complete = 1;		netif_carrier_off(net_dev);		return;	}	/* (Re)start AutoNegotiate */	mdio_write(net_dev, phy_addr, MII_CONTROL,		   MII_CNTL_AUTO | MII_CNTL_RST_AUTO);	sis_priv->autong_complete = 0;}/** *	sis900_read_mode - read media mode for sis900 internal phy *	@net_dev: the net device to read mode for *	@speed  : the transmit speed to be determined *	@duplex : the duplex mode to be determined * *	The capability of remote end will be put in mii register autorec *	after auto-negotiation. Use AND operation to get the upper bound *	of speed and duplex between two ends. */static void sis900_read_mode(struct net_device *net_dev, int *speed, int *duplex){	struct sis900_private *sis_priv = net_dev->priv;	struct mii_phy *phy = sis_priv->mii;	int phy_addr = sis_priv->cur_phy;	u32 status;	u16 autoadv, autorec;	int i = 0;	while (i++ < 2)		status = mdio_read(net_dev, phy_addr, MII_STATUS);	if (!(status & MII_STAT_LINK))		return;	/* AutoNegotiate completed */	autoadv = mdio_read(net_dev, phy_addr, MII_ANADV);	autorec = mdio_read(net_dev, phy_addr, MII_ANLPAR);	status = autoadv & autorec;		*speed = HW_SPEED_10_MBPS;	*duplex = FDX_CAPABLE_HALF_SELECTED;	if (status & (MII_NWAY_TX | MII_NWAY_TX_FDX))		*speed = HW_SPEED_100_MBPS;	if (status & ( MII_NWAY_TX_FDX | MII_NWAY_T_FDX))		*duplex = FDX_CAPABLE_FULL_SELECTED;		sis_priv->autong_complete = 1;	/* Workaround for Realtek RTL8201 PHY issue */	if((phy->phy_id0 == 0x0000) && ((phy->phy_id1 & 0xFFF0) == 0x8200)){		if(mdio_read(net_dev, phy_addr, MII_CONTROL) & MII_CNTL_FDX)			*duplex = FDX_CAPABLE_FULL_SELECTED;		if(mdio_read(net_dev, phy_addr, 0x0019) & 0x01)			*speed = HW_SPEED_100_MBPS;	}	printk(KERN_INFO "%s: Media Link On %s %s-duplex \n",	       net_dev->name,	       *speed == HW_SPEED_100_MBPS ?	       "100mbps" : "10mbps",	       *duplex == FDX_CAPABLE_FULL_SELECTED ?	       "full" : "half");}/** *	sis900_tx_timeout - sis900 transmit timeout routine *	@net_dev: the net device to transmit * *	print transmit timeout status *	disable interrupts and do some tasks */static void sis900_tx_timeout(struct net_device *net_dev){	struct sis900_private *sis_priv = net_dev->priv;	long ioaddr = net_dev->base_addr;	unsigned long flags;	int i;	printk(KERN_INFO "%s: Transmit timeout, status %8.8x %8.8x \n",	       net_dev->name, inl(ioaddr + cr), inl(ioaddr + isr));	/* Disable interrupts by clearing the interrupt mask. */	outl(0x0000, ioaddr + imr);	/* use spinlock to prevent interrupt handler accessing buffer ring */	spin_lock_irqsave(&sis_priv->lock, flags);	/* discard unsent packets */	sis_priv->dirty_tx = sis_priv->cur_tx = 0;	for (i = 0; i < NUM_TX_DESC; i++) {		struct sk_buff *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_irq(skb);			sis_priv->tx_skbuff[i] = NULL;			sis_priv->tx_ring[i].cmdsts = 0;			sis_priv->tx_ring[i].bufptr = 0;			sis_priv->stats.tx_dropped++;		}	}	sis_priv->tx_full = 0;	netif_wake_queue(net_dev);	spin_unlock_irqrestore(&sis_priv->lock, flags);	net_dev->trans_start = jiffies;	/* load Transmit Descriptor Register */	outl(sis_priv->tx_ring_dma, ioaddr + txdp);	/* Enable all known interrupts by setting the interrupt mask. */	outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxIDLE), ioaddr + imr);	return;}/** *	sis900_start_xmit - sis900 start transmit routine *	@skb: socket buffer pointer to put the data being transmitted *	@net_dev: the net device to transmit with * *	Set the transmit buffer descriptor,  *	and write TxENA to enable transmit state machine. *	tell upper layer if the buffer is full */static intsis900_start_xmit(struct sk_buff *skb, struct net_device *net_dev){	struct sis900_private *sis_priv = net_dev->priv;	long ioaddr = net_dev->base_addr;	unsigned int  entry;	unsigned long flags;	unsigned int  index_cur_tx, index_dirty_tx;	unsigned int  count_dirty_tx;	/* Don't transmit data before the complete of auto-negotiation */	if(!sis_priv->autong_complete){		netif_stop_queue(net_dev);		return 1;	}	spin_lock_irqsave(&sis_priv->lock, flags);	/* Calculate the next Tx descriptor entry. */	entry = sis_priv->cur_tx % NUM_TX_DESC;	sis_priv->tx_skbuff[entry] = skb;	/* set the transmit buffer descriptor and enable Transmit State Machine */	sis_priv->tx_ring[entry].bufptr = pci_map_single(sis_priv->pci_dev,		skb->data, skb->len, PCI_DMA_TODEVICE);	sis_priv->tx_ring[entry].cmdsts = (OWN | skb->len);	outl(TxENA | inl(ioaddr + cr), ioaddr + cr);	sis_priv->cur_tx ++;	index_cur_tx = sis_priv->cur_tx;	index_dirty_tx = sis_priv->dirty_tx;	for (count_dirty_tx = 0; index_cur_tx != index_dirty_tx; index_dirty_tx++)		count_dirty_tx ++;	if (index_cur_tx == index_dirty_tx) {		/* dirty_tx is met in the cycle of cur_tx, buffer full */		sis_priv->tx_full = 1;		netif_stop_queue(net_dev);	} else if (count_dirty_tx < NUM_TX_DESC) { 		/* Typical path, tell upper layer that more transmission is possible */		netif_start_queue(net_dev);	} else {		/* buffer full, tell upper layer no more transmission */		sis_priv->tx_full = 1;		netif_stop_queue(net_dev);	}	spin_unlock_irqrestore(&sis_priv->lock, flags);	net_dev->trans_start = jiffies;	if (sis900_debug > 3)		printk(KERN_INFO "%s: Queued Tx packet at %p size %d "		       "to slot %d.\n",		       net_dev->name, skb->data, (int)skb->len, entry);	return 0;}/** *	sis900_interrupt - sis900 interrupt handler *	@irq: the irq number *	@dev_instance: the client data object *	@regs: snapshot of processor context * *	The interrupt handler does all of the Rx thread work,  *	and cleans up after the Tx thread */static irqreturn_t sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs){	struct net_device *net_dev = dev_instance;	struct sis900_private *sis_priv = net_dev->priv;	int boguscnt = max_interrupt_work;	long ioaddr = net_dev->base_addr;	u32 status;	unsigned int handled = 0;	spin_lock (&sis_priv->lock);	do {		status = inl(ioaddr + isr);		if ((status & (HIBERR|TxURN|TxERR|TxIDLE|RxORN|RxERR|RxOK)) == 0)			/* nothing intresting happened */			break;		handled = 1;		/* why dow't we break after Tx/Rx case ?? keyword: full-duplex */		if (status & (RxORN | RxERR | RxOK))			/* Rx interrupt */			sis900_rx(net_dev);		if (status & (TxURN | TxERR | TxIDLE))			/* Tx interrupt */			sis900_finish_xmit(net_dev);		/* something strange happened !!! */		if (status & HIBERR) {			printk(KERN_INFO "%s: Abnormal interrupt,"			       "status %#8.8x.\n", net_dev->name, status);			break;		}		if (--boguscnt < 0) {			printk(KERN_INFO "%s: Too much work at interrupt, "			       "interrupt status = %#8.8x.\n",			       net_dev->name, status);			break;		}	} while (1);	if (sis900_debug > 3)		printk(KERN_INFO "%s: exiting interrupt, "		       "interrupt status = 0x%#8.8x.\n",		       net_dev->name, inl(ioaddr + isr));		spin_unlock (&sis_priv->lock);	return IRQ_RETVAL(handled);}/** *	sis900_rx - sis900 receive routine *	@net_dev: the net device which receives data * *	Process receive interrupt events,  *	put buffer to higher layer and refill buffer pool *	Note: This fucntion is called by interrupt handler,  *	don't do "too much" work here */static int sis900_rx(struct net_device *net_dev){	struct sis900_private *sis_priv = net_dev->priv;	long ioaddr = net_dev->base_addr;	unsigned int entry = sis_priv->cur_rx % NUM_RX_DESC;	u32 rx_status = sis_priv->rx_ring[entry].cmdsts;	if (sis900_debug > 3)		printk(KERN_INFO "sis900_rx, cur_rx:%4.4d, dirty_rx:%4.4d "		       "status:0x%8.8x\n",		       sis_priv->cur_rx, sis_priv->dirty_rx, rx_status);	while (rx_status & OWN) {		unsigned int rx_size;		rx_size = (rx_status & DSIZE) - CRC_SIZE;		if (rx_status & (ABORT|OVERRUN|TOOLONG|RUNT|RXISERR|CRCERR|FAERR)) {			/* corrupted packet received */			if (sis900_debug > 3)				printk(KERN_INFO "%s: Corrupted packet "				       "received, buffer status = 0x%8.8x.\n",				       net_dev->name, rx_status);			sis_priv->stats.rx_errors++;			if (rx_status & OVERRUN)				sis_priv->stats.rx_over_errors++;			if (rx_status & (TOOLONG|RUNT))				sis_priv->stats.rx_length_errors++;			if (rx_status & (RXISERR | FAERR))				sis_priv->stats.rx_frame_errors++;			if (rx_status & CRCERR) 				sis_priv->stats.rx_crc_errors++;			/* reset buffer descriptor state */			sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE;		} else {			struct sk_buff * skb;			/* 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) {				printk(KERN_INFO "%s: NULL pointer " 				       "encountered in Rx ring, skipping\n",				       net_dev->name);				break;			}			pci_unmap_single(sis_priv->pci_dev, 				sis_priv->rx_ring[entry].bufptr, RX_BUF_SIZE, 				PCI_DMA_FROMDEVICE);			/* give the socket buffer to upper layers */			skb = sis_priv->rx_skbuff[entry];			skb_put(skb, rx_size);			skb->protocol = eth_type_trans(skb, net_dev);			netif_rx(skb);			/* some network statistics */			if ((rx_status & BCAST) == MCAST)				sis_priv->stats.multicast++;			net_dev->last_rx = jiffies;			sis_priv->stats.rx_bytes += rx_size;			sis_priv->stats.rx_packets++;			/* 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 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->rx_skbuff[entry] = NULL;				/* reset buffer descriptor state */				sis_priv->rx_ring[entry].cmdsts = 0;				sis_priv->rx_ring[entry].bufptr = 0;				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 = 				pci_map_single(sis_priv->pci_dev, skb->tail, 					RX_BUF_SIZE, PCI_DMA_FROMDEVICE);			sis_priv->dirty_rx++;		}		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 > 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 =				pci_map_single(sis_priv->pci_dev, skb->tail,					RX_BUF_SIZE, PCI_DMA_FROMDEVICE);		}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?