⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 3c574_cs.c

📁 h内核
💻 C
📖 第 1 页 / 共 3 页
字号:
	kio_addr_t ioaddr = dev->base_addr;	struct el3_private *lp = netdev_priv(dev);	unsigned long flags;	DEBUG(3, "%s: el3_start_xmit(length = %ld) called, "		  "status %4.4x.\n", dev->name, (long)skb->len,		  inw(ioaddr + EL3_STATUS));	spin_lock_irqsave(&lp->window_lock, flags);	outw(skb->len, ioaddr + TX_FIFO);	outw(0, ioaddr + TX_FIFO);	outsl(ioaddr + TX_FIFO, skb->data, (skb->len+3)>>2);	dev->trans_start = jiffies;	/* TxFree appears only in Window 1, not offset 0x1c. */	if (inw(ioaddr + TxFree) <= 1536) {		netif_stop_queue(dev);		/* Interrupt us when the FIFO has room for max-sized packet. 		   The threshold is in units of dwords. */		outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD);	}	pop_tx_status(dev);	spin_unlock_irqrestore(&lp->window_lock, flags);	dev_kfree_skb(skb);	return 0;}/* The EL3 interrupt handler. */static irqreturn_t el3_interrupt(int irq, void *dev_id, struct pt_regs *regs){	struct net_device *dev = (struct net_device *) dev_id;	struct el3_private *lp = netdev_priv(dev);	kio_addr_t ioaddr;	unsigned status;	int work_budget = max_interrupt_work;	int handled = 0;	if (!netif_device_present(dev))		return IRQ_NONE;	ioaddr = dev->base_addr;	DEBUG(3, "%s: interrupt, status %4.4x.\n",		  dev->name, inw(ioaddr + EL3_STATUS));	spin_lock(&lp->window_lock);		while ((status = inw(ioaddr + EL3_STATUS)) &		   (IntLatch | RxComplete | RxEarly | StatsFull)) {		if (!netif_device_present(dev) ||			((status & 0xe000) != 0x2000)) {			DEBUG(1, "%s: Interrupt from dead card\n", dev->name);			break;		}		handled = 1;		if (status & RxComplete)			work_budget = el3_rx(dev, work_budget);		if (status & TxAvailable) {			DEBUG(3, "  TX room bit was handled.\n");			/* There's room in the FIFO for a full-sized packet. */			outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);			netif_wake_queue(dev);		}		if (status & TxComplete)			pop_tx_status(dev);		if (status & (AdapterFailure | RxEarly | StatsFull)) {			/* Handle all uncommon interrupts. */			if (status & StatsFull)				update_stats(dev);			if (status & RxEarly) {				work_budget = el3_rx(dev, work_budget);				outw(AckIntr | RxEarly, ioaddr + EL3_CMD);			}			if (status & AdapterFailure) {				u16 fifo_diag;				EL3WINDOW(4);				fifo_diag = inw(ioaddr + Wn4_FIFODiag);				EL3WINDOW(1);				printk(KERN_NOTICE "%s: adapter failure, FIFO diagnostic"					   " register %04x.\n", dev->name, fifo_diag);				if (fifo_diag & 0x0400) {					/* Tx overrun */					tc574_wait_for_completion(dev, TxReset);					outw(TxEnable, ioaddr + EL3_CMD);				}				if (fifo_diag & 0x2000) {					/* Rx underrun */					tc574_wait_for_completion(dev, RxReset);					set_rx_mode(dev);					outw(RxEnable, ioaddr + EL3_CMD);				}				outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);			}		}		if (--work_budget < 0) {			DEBUG(0, "%s: Too much work in interrupt, "				  "status %4.4x.\n", dev->name, status);			/* Clear all interrupts */			outw(AckIntr | 0xFF, ioaddr + EL3_CMD);			break;		}		/* Acknowledge the IRQ. */		outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);	}	DEBUG(3, "%s: exiting interrupt, status %4.4x.\n",		  dev->name, inw(ioaddr + EL3_STATUS));		  	spin_unlock(&lp->window_lock);	return IRQ_RETVAL(handled);}/*    This timer serves two purposes: to check for missed interrupts	(and as a last resort, poll the NIC for events), and to monitor	the MII, reporting changes in cable status.*/static void media_check(unsigned long arg){	struct net_device *dev = (struct net_device *) arg;	struct el3_private *lp = netdev_priv(dev);	kio_addr_t ioaddr = dev->base_addr;	unsigned long flags;	unsigned short /* cable, */ media, partner;	if (!netif_device_present(dev))		goto reschedule;		/* Check for pending interrupt with expired latency timer: with	   this, we can limp along even if the interrupt is blocked */	if ((inw(ioaddr + EL3_STATUS) & IntLatch) && (inb(ioaddr + Timer) == 0xff)) {		if (!lp->fast_poll)			printk(KERN_INFO "%s: interrupt(s) dropped!\n", dev->name);		el3_interrupt(dev->irq, lp, NULL);		lp->fast_poll = HZ;	}	if (lp->fast_poll) {		lp->fast_poll--;		lp->media.expires = jiffies + 2*HZ/100;		add_timer(&lp->media);		return;	}	spin_lock_irqsave(&lp->window_lock, flags);	EL3WINDOW(4);	media = mdio_read(ioaddr, lp->phys, 1);	partner = mdio_read(ioaddr, lp->phys, 5);	EL3WINDOW(1);		if (media != lp->media_status) {		if ((media ^ lp->media_status) & 0x0004)			printk(KERN_INFO "%s: %s link beat\n", dev->name,				   (lp->media_status & 0x0004) ? "lost" : "found");		if ((media ^ lp->media_status) & 0x0020) {			lp->partner = 0;			if (lp->media_status & 0x0020) {				printk(KERN_INFO "%s: autonegotiation restarted\n",					   dev->name);			} else if (partner) {				partner &= lp->advertising;				lp->partner = partner;				printk(KERN_INFO "%s: autonegotiation complete: "					   "%sbaseT-%cD selected\n", dev->name,					   ((partner & 0x0180) ? "100" : "10"),					   ((partner & 0x0140) ? 'F' : 'H'));			} else {				printk(KERN_INFO "%s: link partner did not autonegotiate\n",					   dev->name);			}			EL3WINDOW(3);			outb((partner & 0x0140 ? 0x20 : 0) |				 (dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl);			EL3WINDOW(1);		}		if (media & 0x0010)			printk(KERN_INFO "%s: remote fault detected\n",				   dev->name);		if (media & 0x0002)			printk(KERN_INFO "%s: jabber detected\n", dev->name);		lp->media_status = media;	}	spin_unlock_irqrestore(&lp->window_lock, flags);reschedule:	lp->media.expires = jiffies + HZ;	add_timer(&lp->media);}static struct net_device_stats *el3_get_stats(struct net_device *dev){	struct el3_private *lp = netdev_priv(dev);	if (netif_device_present(dev)) {		unsigned long flags;		spin_lock_irqsave(&lp->window_lock, flags);		update_stats(dev);		spin_unlock_irqrestore(&lp->window_lock, flags);	}	return &lp->stats;}/*  Update statistics.	Suprisingly this need not be run single-threaded, but it effectively is.	The counters clear when read, so the adds must merely be atomic. */static void update_stats(struct net_device *dev){	struct el3_private *lp = netdev_priv(dev);	kio_addr_t ioaddr = dev->base_addr;	u8 rx, tx, up;	DEBUG(2, "%s: updating the statistics.\n", dev->name);	if (inw(ioaddr+EL3_STATUS) == 0xffff) /* No card. */		return;			/* Unlike the 3c509 we need not turn off stats updates while reading. */	/* Switch to the stats window, and read everything. */	EL3WINDOW(6);	lp->stats.tx_carrier_errors 		+= inb(ioaddr + 0);	lp->stats.tx_heartbeat_errors		+= inb(ioaddr + 1);	/* Multiple collisions. */	   	inb(ioaddr + 2);	lp->stats.collisions			+= inb(ioaddr + 3);	lp->stats.tx_window_errors		+= inb(ioaddr + 4);	lp->stats.rx_fifo_errors		+= inb(ioaddr + 5);	lp->stats.tx_packets			+= inb(ioaddr + 6);	up		 			 = inb(ioaddr + 9);	lp->stats.tx_packets			+= (up&0x30) << 4;	/* Rx packets   */			   inb(ioaddr + 7);	/* Tx deferrals */			   inb(ioaddr + 8);	rx		 			 = inw(ioaddr + 10);	tx					 = inw(ioaddr + 12);	EL3WINDOW(4);	/* BadSSD */				   inb(ioaddr + 12);	up					 = inb(ioaddr + 13);	lp->stats.tx_bytes 			+= tx + ((up & 0xf0) << 12);	EL3WINDOW(1);}static int el3_rx(struct net_device *dev, int worklimit){	struct el3_private *lp = netdev_priv(dev);	kio_addr_t ioaddr = dev->base_addr;	short rx_status;		DEBUG(3, "%s: in rx_packet(), status %4.4x, rx_status %4.4x.\n",		  dev->name, inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus));	while (!((rx_status = inw(ioaddr + RxStatus)) & 0x8000) &&		   (--worklimit >= 0)) {		if (rx_status & 0x4000) { /* Error, update stats. */			short error = rx_status & 0x3800;			lp->stats.rx_errors++;			switch (error) {			case 0x0000:	lp->stats.rx_over_errors++; break;			case 0x0800:	lp->stats.rx_length_errors++; break;			case 0x1000:	lp->stats.rx_frame_errors++; break;			case 0x1800:	lp->stats.rx_length_errors++; break;			case 0x2000:	lp->stats.rx_frame_errors++; break;			case 0x2800:	lp->stats.rx_crc_errors++; break;			}		} else {			short pkt_len = rx_status & 0x7ff;			struct sk_buff *skb;			skb = dev_alloc_skb(pkt_len+5);			DEBUG(3, "  Receiving packet size %d status %4.4x.\n",				  pkt_len, rx_status);			if (skb != NULL) {				skb->dev = dev;				skb_reserve(skb, 2);				insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len),						((pkt_len+3)>>2));				skb->protocol = eth_type_trans(skb, dev);				netif_rx(skb);				dev->last_rx = jiffies;				lp->stats.rx_packets++;				lp->stats.rx_bytes += pkt_len;			} else {				DEBUG(1, "%s: couldn't allocate a sk_buff of"					  " size %d.\n", dev->name, pkt_len);				lp->stats.rx_dropped++;			}		}		tc574_wait_for_completion(dev, RxDiscard);	}	return worklimit;}static void netdev_get_drvinfo(struct net_device *dev,			       struct ethtool_drvinfo *info){	strcpy(info->driver, "3c574_cs");}static struct ethtool_ops netdev_ethtool_ops = {	.get_drvinfo		= netdev_get_drvinfo,};/* Provide ioctl() calls to examine the MII xcvr state. */static int el3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd){	struct el3_private *lp = netdev_priv(dev);	kio_addr_t ioaddr = dev->base_addr;	u16 *data = (u16 *)&rq->ifr_ifru;	int phy = lp->phys & 0x1f;	DEBUG(2, "%s: In ioct(%-.6s, %#4.4x) %4.4x %4.4x %4.4x %4.4x.\n",		  dev->name, rq->ifr_ifrn.ifrn_name, cmd,		  data[0], data[1], data[2], data[3]);	switch(cmd) {	case SIOCGMIIPHY:		/* Get the address of the PHY in use. */		data[0] = phy;	case SIOCGMIIREG:		/* Read the specified MII register. */		{			int saved_window;			unsigned long flags;			spin_lock_irqsave(&lp->window_lock, flags);			saved_window = inw(ioaddr + EL3_CMD) >> 13;			EL3WINDOW(4);			data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f);			EL3WINDOW(saved_window);			spin_unlock_irqrestore(&lp->window_lock, flags);			return 0;		}	case SIOCSMIIREG:		/* Write the specified MII register */		{			int saved_window;                       unsigned long flags;			if (!capable(CAP_NET_ADMIN))				return -EPERM;			spin_lock_irqsave(&lp->window_lock, flags);			saved_window = inw(ioaddr + EL3_CMD) >> 13;			EL3WINDOW(4);			mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]);			EL3WINDOW(saved_window);			spin_unlock_irqrestore(&lp->window_lock, flags);			return 0;		}	default:		return -EOPNOTSUPP;	}}/* The Odie chip has a 64 bin multicast filter, but the bit layout is not   documented.  Until it is we revert to receiving all multicast frames when   any multicast reception is desired.   Note: My other drivers emit a log message whenever promiscuous mode is   entered to help detect password sniffers.  This is less desirable on   typical PC card machines, so we omit the message.   */static void set_rx_mode(struct net_device *dev){	kio_addr_t ioaddr = dev->base_addr;	if (dev->flags & IFF_PROMISC)		outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,			 ioaddr + EL3_CMD);	else if (dev->mc_count || (dev->flags & IFF_ALLMULTI))		outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD);	else		outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);}static int el3_close(struct net_device *dev){	kio_addr_t ioaddr = dev->base_addr;	struct el3_private *lp = netdev_priv(dev);	dev_link_t *link = &lp->link;	DEBUG(2, "%s: shutting down ethercard.\n", dev->name);		if (DEV_OK(link)) {		unsigned long flags;		/* Turn off statistics ASAP.  We update lp->stats below. */		outw(StatsDisable, ioaddr + EL3_CMD);				/* Disable the receiver and transmitter. */		outw(RxDisable, ioaddr + EL3_CMD);		outw(TxDisable, ioaddr + EL3_CMD);				/* Note: Switching to window 0 may disable the IRQ. */		EL3WINDOW(0);		spin_lock_irqsave(&lp->window_lock, flags);		update_stats(dev);		spin_unlock_irqrestore(&lp->window_lock, flags);	}	link->open--;	netif_stop_queue(dev);	del_timer_sync(&lp->media);	return 0;}static struct pcmcia_driver tc574_driver = {	.owner		= THIS_MODULE,	.drv		= {		.name	= "3c574_cs",	},	.attach		= tc574_attach,	.detach		= tc574_detach,};static int __init init_tc574(void){	return pcmcia_register_driver(&tc574_driver);}static void __exit exit_tc574(void){	pcmcia_unregister_driver(&tc574_driver);	BUG_ON(dev_list != NULL);}module_init(init_tc574);module_exit(exit_tc574);

⌨️ 快捷键说明

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