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

📄 3c509.c

📁 主要内容为linux内核代码
💻 C
📖 第 1 页 / 共 2 页
字号:
	/* Transmitter timeout, serious problems. */
	if (dev->tbusy) {
		int tickssofar = jiffies - dev->trans_start;
		if (tickssofar < 10)
			return 1;
		printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
			   dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS));
		dev->trans_start = jiffies;
		/* Issue TX_RESET and TX_START commands. */
		outw(0x5800, ioaddr + EL3_CMD); /* TX_RESET */
		outw(0x4800, ioaddr + EL3_CMD); /* TX_START */
		dev->tbusy = 0;
	}

	if (skb == NULL) {
		dev_tint(dev);
		return 0;
	}

	/* Fill in the ethernet header. */
	if (!skb->arp  &&  dev->rebuild_header(skb->data, dev)) {
		skb->dev = dev;
		arp_queue (skb);
		return 0;
	}
	skb->arp=1;

	if (skb->len <= 0)
		return 0;

	if (el3_debug > 4) {
		printk("%s: el3_start_xmit(lenght = %ld) called, status %4.4x.\n",
			   dev->name, skb->len, inw(ioaddr + EL3_STATUS));
	}
#ifndef final_version
	{	/* Error-checking code, delete for 1.00. */
		ushort status = inw(ioaddr + EL3_STATUS);
		if (status & 0x0001 		/* IRQ line active, missed one. */
			&& inw(ioaddr + EL3_STATUS) & 1) { 			/* Make sure. */
			printk("%s: Missed interrupt, status then %04x now %04x"
				   "  Tx %2.2x Rx %4.4x.\n", dev->name, status,
				   inw(ioaddr + EL3_STATUS), inb(ioaddr + TX_STATUS),
				   inw(ioaddr + RX_STATUS));
			outw(0x7800, ioaddr + EL3_CMD); /* Fake interrupt trigger. */
			outw(0x6899, ioaddr + EL3_CMD); /* Ack IRQ */
			outw(0x78ff, ioaddr + EL3_CMD); /* Set all status bits visible. */
		}
	}
#endif

	/* Avoid timer-based retransmission conflicts. */
	if (set_bit(0, (void*)&dev->tbusy) != 0)
		printk("%s: Transmitter access conflict.\n", dev->name);
	else {
		/* Put out the doubleword header... */
		outw(skb->len, ioaddr + TX_FIFO);
		outw(0x00, ioaddr + TX_FIFO);
		/* ... and the packet rounded to a doubleword. */
		outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
	
		dev->trans_start = jiffies;
		if (inw(ioaddr + TX_FREE) > 1536) {
			dev->tbusy=0;
		} else
			/* Interrupt us when the FIFO has room for max-sized packet. */
			outw(0x9000 + 1536, ioaddr + EL3_CMD);
	}

	if (skb->free)
		kfree_skb (skb, FREE_WRITE);

	/* Clear the Tx status stack. */
	{
		short tx_status;
		int i = 4;

		while (--i > 0	&&	(tx_status = inb(ioaddr + TX_STATUS)) > 0) {
			if (el3_debug > 5)
				printk("		Tx status %4.4x.\n", tx_status);
			if (tx_status & 0x38) lp->stats.tx_aborted_errors++;
			if (tx_status & 0x30) outw(0x5800, ioaddr + EL3_CMD);
			if (tx_status & 0x3C) outw(0x4800, ioaddr + EL3_CMD);
			outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
		}
	}
	return 0;
}

/* The EL3 interrupt handler. */
static void
el3_interrupt(int reg_ptr)
{
	int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
	struct device *dev = (struct device *)(irq2dev_map[irq]);
	int ioaddr, status;
	int i = 0;

	if (dev == NULL) {
		printk ("el3_interrupt(): irq %d for unknown device.\n", irq);
		return;
	}

	if (dev->interrupt)
		printk("%s: Re-entering the interrupt handler.\n", dev->name);
	dev->interrupt = 1;

	ioaddr = dev->base_addr;
	status = inw(ioaddr + EL3_STATUS);

	if (el3_debug > 4)
		printk("%s: interrupt, status %4.4x.\n", dev->name, status);
	
	while ((status = inw(ioaddr + EL3_STATUS)) & 0x01) {

		if (status & 0x10)
			el3_rx(dev);

		if (status & 0x08) {
			if (el3_debug > 5)
				printk("	TX room bit was handled.\n");
			/* There's room in the FIFO for a full-sized packet. */
			outw(0x6808, ioaddr + EL3_CMD); /* Ack IRQ */
			dev->tbusy = 0;
			mark_bh(INET_BH);
		}
		if (status & 0x80)				/* Statistics full. */
			update_stats(ioaddr, dev);
		
		if (++i > 10) {
			printk("%s: Infinite loop in interrupt, status %4.4x.\n",
				   dev->name, status);
			/* Clear all interrupts we have handled. */
			outw(0x68FF, ioaddr + EL3_CMD);
			break;
		}
		/* Acknowledge the IRQ. */
		outw(0x6891, ioaddr + EL3_CMD); /* Ack IRQ */
	}

	if (el3_debug > 4) {
		printk("%s: exiting interrupt, status %4.4x.\n", dev->name,
			   inw(ioaddr + EL3_STATUS));
	}
	
	dev->interrupt = 0;
	return;
}


static struct enet_statistics *
el3_get_stats(struct device *dev)
{
	struct el3_private *lp = (struct el3_private *)dev->priv;

	sti();
	update_stats(dev->base_addr, dev);
	cli();
	return &lp->stats;
}

/*  Update statistics.  We change to register window 6, so this should be run
	single-threaded if the device is active. This is expected to be a rare
	operation, and it's simpler for the rest of the driver to assume that
	window 1 is always valid rather than use a special window-state variable.
	*/
static void update_stats(int ioaddr, struct device *dev)
{
	struct el3_private *lp = (struct el3_private *)dev->priv;

	if (el3_debug > 5)
		printk("   Updating the statistics.\n");
	/* Turn off statistics updates while reading. */
	outw(0xB000, ioaddr + EL3_CMD);
	/* 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);
	lp->stats.rx_packets			+= inb(ioaddr + 7);
	/* Tx deferrals */				inb(ioaddr + 8);
	inw(ioaddr + 10);	/* Total Rx and Tx octets. */
	inw(ioaddr + 12);

	/* Back to window 1, and turn statistics back on. */
	EL3WINDOW(1);
	outw(0xA800, ioaddr + EL3_CMD);
	return;
}

static int
el3_rx(struct device *dev)
{
	struct el3_private *lp = (struct el3_private *)dev->priv;
	int ioaddr = dev->base_addr;
	short rx_status;

	if (el3_debug > 5)
		printk("	   In rx_packet(), status %4.4x, rx_status %4.4x.\n",
			   inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS));
	while ((rx_status = inw(ioaddr + RX_STATUS)) > 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;
			}
		}
		if ( (! (rx_status & 0x4000))
			|| ! (rx_status & 0x1000)) { /* Dribble bits are OK. */
			short pkt_len = rx_status & 0x7ff;
			int sksize = sizeof(struct sk_buff) + pkt_len + 3;
			struct sk_buff *skb;

			skb = alloc_skb(sksize, GFP_ATOMIC);
			if (el3_debug > 4)
				printk("	   Receiving packet size %d status %4.4x.\n",
					   pkt_len, rx_status);
			if (skb != NULL) {
				skb->mem_len = sksize;
				skb->mem_addr = skb;
				skb->len = pkt_len;
				skb->dev = dev;

				/* 'skb->data' points to the start of sk_buff data area. */
				insl(ioaddr+RX_FIFO, skb->data,
							(pkt_len + 3) >> 2);

#ifdef HAVE_NETIF_RX
				netif_rx(skb);
				outw(0x4000, ioaddr + EL3_CMD); /* Rx discard */
				continue;
#else
				skb->lock = 0;
				if (dev_rint((unsigned char *)skb, pkt_len,
							 IN_SKBUFF,dev)== 0){
					if (el3_debug > 6)
						printk("	 dev_rint() happy, status %4.4x.\n",
						inb(ioaddr + EL3_STATUS));
					outw(0x4000, ioaddr + EL3_CMD); /* Rx discard */
					while (inw(ioaddr + EL3_STATUS) & 0x1000)
					  printk("	Waiting for 3c509 to discard packet, status %x.\n",
							 inw(ioaddr + EL3_STATUS) );
					if (el3_debug > 6)
						printk("	 discarded packet, status %4.4x.\n",
						inb(ioaddr + EL3_STATUS));
					continue;
				} else {
					printk("%s: receive buffers full.\n", dev->name);
					kfree_s(skb, sksize);
				}
#endif
			} else if (el3_debug)
				printk("%s: Couldn't allocate a sk_buff of size %d.\n",
					   dev->name, sksize);
		}
		lp->stats.rx_dropped++;
		outw(0x4000, ioaddr + EL3_CMD); /* Rx discard */
		while (inw(ioaddr + EL3_STATUS) & 0x1000)
		  printk("	Waiting for 3c509 to discard packet, status %x.\n",
				 inw(ioaddr + EL3_STATUS) );
	}

	if (el3_debug > 5)
		printk("	   Exiting rx_packet(), status %4.4x, rx_status %4.4x.\n",
			   inw(ioaddr+EL3_STATUS), inw(ioaddr+8));

	return 0;
}

#ifdef HAVE_MULTICAST
/* Set or clear the multicast filter for this adaptor.
   num_addrs == -1		Promiscuous mode, receive all packets
   num_addrs == 0		Normal mode, clear multicast list
   num_addrs > 0		Multicast mode, receive normal and MC packets, and do
						best-effort filtering.
 */
static void
set_multicast_list(struct device *dev, int num_addrs, void *addrs)
{
	short ioaddr = dev->base_addr;
	if (num_addrs > 0) {
		outw(0x8007, ioaddr + EL3_CMD);
	} else if (num_addrs < 0) {
		outw(0x8008, ioaddr + EL3_CMD);
	} else
		outw(0x8005, ioaddr + EL3_CMD);
}
#endif

static int
el3_close(struct device *dev)
{
	int ioaddr = dev->base_addr;

	if (el3_debug > 2)
		printk("%s: Shutting down ethercard.\n", dev->name);

	dev->tbusy = 1;
	dev->start = 0;

	/* Turn off statistics.	 We update lp->stats below. */
	outw(0xB000, ioaddr + EL3_CMD);

	/* Disable the receiver and transmitter. */
	outw(0x1800, ioaddr + EL3_CMD);
	outw(0x5000, ioaddr + EL3_CMD);

	if (dev->if_port == 3)
		/* Turn off thinnet power. */
		outw(0xb800, ioaddr + EL3_CMD);
	else if (dev->if_port == 0) {
		/* Disable link beat and jabber, if_port may change ere next open(). */
		EL3WINDOW(4);
		outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA);
	}

	free_irq(dev->irq);
	/* Switching back to window 0 disables the IRQ. */
	EL3WINDOW(0);
	/* But we explicitly zero the IRQ line select anyway. */
	outw(0x0f00, ioaddr + 8);


	irq2dev_map[dev->irq] = 0;

	update_stats(ioaddr, dev);
	return 0;
}

/*
 * Local variables:
 *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 3c509.c"
 *  version-control: t
 *  kept-new-versions: 5
 *  tab-width: 4
 * End:
 */

⌨️ 快捷键说明

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