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

📄 atp.c

📁 主要内容为linux内核代码
💻 C
📖 第 1 页 / 共 2 页
字号:
    write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
    write_reg_high(ioaddr, IMR, ISRh_RxErr);

	lp->tx_unit_busy = 0;
    lp->pac_cnt_in_tx_buf = 0;
	lp->saved_tx_size = 0;

	dev->tbusy = 0;
	dev->interrupt = 0;
}

static void trigger_send(short ioaddr, int length)
{
	write_reg_byte(ioaddr, TxCNT0, length & 0xff);
	write_reg(ioaddr, TxCNT1, length >> 8);
	write_reg(ioaddr, CMR1, CMR1_Xmit);
}

static void write_packet(short ioaddr, int length, unsigned char *packet, int data_mode)
{
    length = (length + 1) & ~1;		/* Round up to word length. */
    outb(EOC+MAR, ioaddr + PAR_DATA);
    if ((data_mode & 1) == 0) {
		/* Write the packet out, starting with the write addr. */
		outb(WrAddr+MAR, ioaddr + PAR_DATA);
		do {
			write_byte_mode0(ioaddr, *packet++);
		} while (--length > 0) ;
    } else {
		/* Write the packet out in slow mode. */
		unsigned char outbyte = *packet++;

		outb(Ctrl_LNibWrite + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
		outb(WrAddr+MAR, ioaddr + PAR_DATA);

		outb((outbyte & 0x0f)|0x40, ioaddr + PAR_DATA);
		outb(outbyte & 0x0f, ioaddr + PAR_DATA);
		outbyte >>= 4;
		outb(outbyte & 0x0f, ioaddr + PAR_DATA);
		outb(Ctrl_HNibWrite + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
		while (--length > 0)
			write_byte_mode1(ioaddr, *packet++);
    }
    /* Terminate the Tx frame.  End of write: ECB. */
    outb(0xff, ioaddr + PAR_DATA);
    outb(Ctrl_HNibWrite | Ctrl_SelData | Ctrl_IRQEN, ioaddr + PAR_CONTROL);
}

static int
net_send_packet(struct sk_buff *skb, struct device *dev)
{
	struct net_local *lp = (struct net_local *)dev->priv;
	int ioaddr = dev->base_addr;

	if (dev->tbusy) {
		/* If we get here, some higher level has decided we are broken.
		   There should really be a "kick me" function call instead. */
		int tickssofar = jiffies - dev->trans_start;
		if (tickssofar < 5)
			return 1;
		printk("%s: transmit timed out, %s?\n", dev->name,
			   inb(ioaddr + PAR_CONTROL) & 0x10 ? "network cable problem"
			   :  "IRQ conflict");
		lp->stats.tx_errors++;
		/* Try to restart the adaptor. */
		hardware_init(dev);
		dev->tbusy=0;
		dev->trans_start = jiffies;
	}

	/* If some higher layer thinks we've missed an tx-done interrupt
	   we are passed NULL. Caution: dev_tint() handles the cli()/sti()
	   itself. */
	if (skb == NULL) {
		dev_tint(dev);
		return 0;
	}

	/* For ethernet, fill in the header.  This should really be done by a
	   higher level, rather than duplicated for each ethernet adaptor. */
	if (!skb->arp  &&  dev->rebuild_header(skb->data, dev)) {
		skb->dev = dev;
		arp_queue (skb);
		return 0;
	}
	skb->arp=1;

	/* Block a timer-based transmit from overlapping.  This could better be
	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
	if (set_bit(0, (void*)&dev->tbusy) != 0)
		printk("%s: Transmitter access conflict.\n", dev->name);
	else {
		short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
		unsigned char *buf = skb->data;
		int flags;

		/* Disable interrupts by writing 0x00 to the Interrupt Mask Register.
		   This sequence must not be interrupted by an incoming packet. */
		save_flags(flags);
		cli();
		write_reg(ioaddr, IMR, 0);
		write_reg_high(ioaddr, IMR, 0);
		restore_flags(flags);

		write_packet(ioaddr, length, buf, dev->if_port);

		lp->pac_cnt_in_tx_buf++;
		if (lp->tx_unit_busy == 0) {
			trigger_send(ioaddr, length);
			lp->saved_tx_size = 0; 				/* Redundent */
			lp->re_tx = 0;
			lp->tx_unit_busy = 1;
		} else
			lp->saved_tx_size = length;

		dev->trans_start = jiffies;
		/* Re-enable the LPT interrupts. */
		write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
		write_reg_high(ioaddr, IMR, ISRh_RxErr);
	}

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

	return 0;
}

/* The typical workload of the driver:
   Handle the network interface interrupts. */
static void
net_interrupt(int reg_ptr)
{
	int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
	struct device *dev = (struct device *)(irq2dev_map[irq]);
	struct net_local *lp;
	int ioaddr, status, boguscount = 20;
	static int num_tx_since_rx = 0;

	if (dev == NULL) {
		printk ("ATP_interrupt(): irq %d for unknown device.\n", irq);
		return;
	}
	dev->interrupt = 1;

	ioaddr = dev->base_addr;
	lp = (struct net_local *)dev->priv;

	/* Disable additional spurious interrupts. */
	outb(Ctrl_SelData, ioaddr + PAR_CONTROL);

	/* The adaptor's output is currently the IRQ line, switch it to data. */
	write_reg(ioaddr, CMR2, CMR2_NULL);
	write_reg(ioaddr, IMR, 0);

	if (net_debug > 5) printk("%s: In interrupt ", dev->name);
    while (--boguscount > 0) {
		status = read_nibble(ioaddr, ISR);
		if (net_debug > 5) printk("loop status %02x..", status);

		if (status & (ISR_RxOK<<3)) {
			write_reg(ioaddr, ISR, ISR_RxOK); /* Clear the Rx interrupt. */
			do {
				int read_status = read_nibble(ioaddr, CMR1);
				if (net_debug > 6)
					printk("handling Rx packet %02x..", read_status);
				/* We acknowledged the normal Rx interrupt, so if the interrupt
				   is still outstanding we must have a Rx error. */
				if (read_status & (CMR1_IRQ << 3)) { /* Overrun. */
					lp->stats.rx_over_errors++;
					/* Set to no-accept mode long enough to remove a packet. */
					write_reg_high(ioaddr, CMR2, CMR2h_OFF);
					net_rx(dev);
					/* Clear the interrupt and return to normal Rx mode. */
					write_reg_high(ioaddr, ISR, ISRh_RxErr);
					write_reg_high(ioaddr, CMR2, lp->addr_mode);
				} else if ((read_status & (CMR1_BufEnb << 3)) == 0) {
					net_rx(dev);
					dev->last_rx = jiffies;
					num_tx_since_rx = 0;
				} else
					break;
			} while (--boguscount > 0);
		} else if (status & ((ISR_TxErr + ISR_TxOK)<<3)) {
			if (net_debug > 6)  printk("handling Tx done..");
			/* Clear the Tx interrupt.  We should check for too many failures
			   and reinitialize the adaptor. */
			write_reg(ioaddr, ISR, ISR_TxErr + ISR_TxOK);
			if (status & (ISR_TxErr<<3)) {
				lp->stats.collisions++;
				if (++lp->re_tx > 15) {
					lp->stats.tx_aborted_errors++;
					hardware_init(dev);
					break;
				}
				/* Attempt to retransmit. */
				if (net_debug > 6)  printk("attempting to ReTx");
				write_reg(ioaddr, CMR1, CMR1_ReXmit + CMR1_Xmit);
			} else {
				/* Finish up the transmit. */
				lp->stats.tx_packets++;
				lp->pac_cnt_in_tx_buf--;
				if ( lp->saved_tx_size) {
					trigger_send(ioaddr, lp->saved_tx_size);
					lp->saved_tx_size = 0;
					lp->re_tx = 0;
				} else
					lp->tx_unit_busy = 0;
				dev->tbusy = 0;
				mark_bh(INET_BH);	/* Inform upper layers. */
			}
			num_tx_since_rx++;
		} else if (num_tx_since_rx > 8
				   && jiffies > dev->last_rx + 100) {
			if (net_debug > 2)
				printk("%s: Missed packet? No Rx after %d Tx and %ld jiffies"
					   " status %02x  CMR1 %02x.\n", dev->name,
					   num_tx_since_rx, jiffies - dev->last_rx, status,
					   (read_nibble(ioaddr, CMR1) >> 3) & 15);
			lp->stats.rx_missed_errors++;
			hardware_init(dev);
			num_tx_since_rx = 0;
			break;
		} else
			break;
    }

	/* This following code fixes a rare (and very difficult to track down)
	   problem where the adaptor forgets its ethernet address. */
	{
		int i;
		for (i = 0; i < 6; i++)
			write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]);
	}

	/* Tell the adaptor that it can go back to using the output line as IRQ. */
    write_reg(ioaddr, CMR2, CMR2_IRQOUT);
	/* Enable the physical interrupt line, which is sure to be low until.. */
	outb(Ctrl_SelData + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
	/* .. we enable the interrupt sources. */
	write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
	write_reg_high(ioaddr, IMR, ISRh_RxErr); 			/* Hmmm, really needed? */

	if (net_debug > 5) printk("exiting interrupt.\n");

	dev->interrupt = 0;

	return;
}

/* We have a good packet(s), get it/them out of the buffers. */
static void net_rx(struct device *dev)
{
	struct net_local *lp = (struct net_local *)dev->priv;
	int ioaddr = dev->base_addr;
#ifdef notdef
	ushort header[4];
#else
	struct rx_header rx_head;
#endif

	/* Process the received packet. */
	outb(EOC+MAR, ioaddr + PAR_DATA);
	read_block(ioaddr, 8, (unsigned char*)&rx_head, dev->if_port);
	if (net_debug > 5)
		printk(" rx_count %04x %04x %04x %04x..", rx_head.pad,
			   rx_head.rx_count, rx_head.rx_status, rx_head.cur_addr);
	if ((rx_head.rx_status & 0x77) != 0x01) {
		lp->stats.rx_errors++;
		/* Ackkk!  I don't have any documentation on what the error bits mean!
		   The best I can do is slap the device around a bit. */
		if (net_debug > 3) printk("%s: Unknown ATP Rx error %04x.\n",
								  dev->name, rx_head.rx_status);
		hardware_init(dev);
		return;
	} else {
		/* Malloc up new buffer. */
		int pkt_len = (rx_head.rx_count & 0x7ff) - 4; 		/* The "-4" is omits the FCS (CRC). */
		int sksize = sizeof(struct sk_buff) + pkt_len;
		struct sk_buff *skb;
		
		skb = alloc_skb(sksize, GFP_ATOMIC);
		if (skb == NULL) {
			printk("%s: Memory squeeze, dropping packet.\n", dev->name);
			lp->stats.rx_dropped++;
			goto done;
		}
		skb->mem_len = sksize;
		skb->mem_addr = skb;
		skb->len = pkt_len;
		skb->dev = dev;
		
		read_block(ioaddr, pkt_len, skb->data, dev->if_port);

		if (net_debug > 6) {
			unsigned char *data = skb->data;
			printk(" data %02x%02x%02x %02x%02x%02x %02x%02x%02x"
				   "%02x%02x%02x %02x%02x..",
				   data[0], data[1], data[2], data[3], data[4], data[5],
				   data[6], data[7], data[8], data[9], data[10], data[11],
				   data[12], data[13]);
		}
		
#ifdef HAVE_NETIF_RX
		netif_rx(skb);
#else
		skb->lock = 0;
		if (dev_rint((unsigned char*)skb, pkt_len, IN_SKBUFF, dev) != 0) {
			kfree_s(skb, sksize);
			lp->stats.rx_dropped++;
			break;
		}
#endif
		lp->stats.rx_packets++;
	}
 done:
	write_reg(ioaddr, CMR1, CMR1_NextPkt);
	return;
}

static void read_block(short ioaddr, int length, unsigned char *p, int data_mode)
{

	if (data_mode <= 3) { /* Mode 0 or 1 */
		outb(Ctrl_LNibRead, ioaddr + PAR_CONTROL);
		outb(length == 8  ?  RdAddr | HNib | MAR  :  RdAddr | MAR,
			 ioaddr + PAR_DATA);
		if (data_mode <= 1) { /* Mode 0 or 1 */
			do  *p++ = read_byte_mode0(ioaddr);  while (--length > 0);
		} else	/* Mode 2 or 3 */
			do  *p++ = read_byte_mode2(ioaddr);  while (--length > 0);
	} else if (data_mode <= 5)
		do      *p++ = read_byte_mode4(ioaddr);  while (--length > 0);
	else
		do      *p++ = read_byte_mode6(ioaddr);  while (--length > 0);

    outb(EOC+HNib+MAR, ioaddr + PAR_DATA);
	outb(Ctrl_SelData, ioaddr + PAR_CONTROL);
}

/* The inverse routine to net_open(). */
static int
net_close(struct device *dev)
{
	struct net_local *lp = (struct net_local *)dev->priv;
	int ioaddr = dev->base_addr;

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

	/* Flush the Tx and disable Rx here. */
	lp->addr_mode = CMR2h_OFF;
	write_reg_high(ioaddr, CMR2, CMR2h_OFF);

	/* Free the IRQ line. */
	outb(0x00, ioaddr + PAR_CONTROL);
	free_irq(dev->irq);
	irq2dev_map[dev->irq] = 0;

	/* Leave the hardware in a reset state. */
    write_reg_high(ioaddr, CMR1, CMR1h_RESET);

	return 0;
}

/* Get the current statistics.	This may be called with the card open or
   closed. */
static struct enet_statistics *
net_get_stats(struct device *dev)
{
	struct net_local *lp = (struct net_local *)dev->priv;
	return &lp->stats;
}

#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)
{
	struct net_local *lp = (struct net_local *)dev->priv;
	short ioaddr = dev->base_addr;
	lp->addr_mode = num_addrs ? CMR2h_PROMISC : CMR2h_Normal;
	write_reg_high(ioaddr, CMR2, lp->addr_mode);
}
#endif

/*
 * Local variables:
 *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c atp.c"
 *  version-control: t
 *  kept-new-versions: 5
 *  tab-width: 4
 * End:
 */

⌨️ 快捷键说明

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