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

📄 8390.c

📁 GNU Mach 微内核源代码, 基于美国卡内基美隆大学的 Mach 研究项目
💻 C
📖 第 1 页 / 共 2 页
字号:
/* 8390.c: A general NS8390 ethernet driver core for linux. *//*	Written 1992-94 by Donald Becker.  	Copyright 1993 United States Government as represented by the	Director, National Security Agency.	This software may be used and distributed according to the terms	of the GNU Public License, incorporated herein by reference.	The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O	Center of Excellence in Space Data and Information Sciences	   Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771    This is the chip-specific code for many 8390-based ethernet adaptors.  This is not a complete driver, it must be combined with board-specific  code such as ne.c, wd.c, 3c503.c, etc.  Seeing how at least eight drivers use this code, (not counting the  PCMCIA ones either) it is easy to break some card by what seems like  a simple innocent change. Please contact me or Donald if you think  you have found something that needs changing. -- PG  Changelog:  Paul Gortmaker	: remove set_bit lock, other cleanups.  Paul Gortmaker	: add ei_get_8390_hdr() so we can pass skb's to 			  ei_block_input() for eth_io_copy_and_sum().  Paul Gortmaker	: exchange static int ei_pingpong for a #define,			  also add better Tx error handling.  Paul Gortmaker	: rewrite Rx overrun handling as per NS specs.  Sources:  The National Semiconductor LAN Databook, and the 3Com 3c503 databook.  */static const char *version =    "8390.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";#include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/fs.h>#include <linux/types.h>#include <linux/ptrace.h>#include <linux/string.h>#include <asm/system.h>#include <asm/segment.h>#include <asm/bitops.h>#include <asm/io.h>#include <linux/errno.h>#include <linux/fcntl.h>#include <linux/in.h>#include <linux/interrupt.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include "8390.h"/* These are the operational function interfaces to board-specific   routines.	void reset_8390(struct device *dev)		Resets the board associated with DEV, including a hardware reset of		the 8390.  This is only called when there is a transmit timeout, and		it is always followed by 8390_init().	void block_output(struct device *dev, int count, const unsigned char *buf,					  int start_page)		Write the COUNT bytes of BUF to the packet buffer at START_PAGE.  The		"page" value uses the 8390's 256-byte pages.	void get_8390_hdr(struct device *dev, struct e8390_hdr *hdr, int ring_page)		Read the 4 byte, page aligned 8390 header. *If* there is a		subsequent read, it will be of the rest of the packet.	void block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)		Read COUNT bytes from the packet buffer into the skb data area. Start 		reading from RING_OFFSET, the address as the 8390 sees it.  This will always		follow the read of the 8390 header. */#define ei_reset_8390 (ei_local->reset_8390)#define ei_block_output (ei_local->block_output)#define ei_block_input (ei_local->block_input)#define ei_get_8390_hdr (ei_local->get_8390_hdr)/* use 0 for production, 1 for verification, >2 for debug */#ifdef EI_DEBUGint ei_debug = EI_DEBUG;#elseint ei_debug = 1;#endif/* Index to functions. */static void ei_tx_intr(struct device *dev);static void ei_tx_err(struct device *dev);static void ei_receive(struct device *dev);static void ei_rx_overrun(struct device *dev);/* Routines generic to NS8390-based boards. */static void NS8390_trigger_send(struct device *dev, unsigned int length,								int start_page);static void set_multicast_list(struct device *dev);/* Open/initialize the board.  This routine goes all-out, setting everything   up anew at each open, even though many of these registers should only   need to be set once at boot.   */int ei_open(struct device *dev){    struct ei_device *ei_local = (struct ei_device *) dev->priv;    /* This can't happen unless somebody forgot to call ethdev_init(). */    if (ei_local == NULL) {	printk(KERN_EMERG "%s: ei_open passed a non-existent device!\n", dev->name);	return -ENXIO;    }        irq2dev_map[dev->irq] = dev;    NS8390_init(dev, 1);    dev->start = 1;    ei_local->irqlock = 0;    return 0;}/* Opposite of above. Only used when "ifconfig <devname> down" is done. */int ei_close(struct device *dev){    NS8390_init(dev, 0);    dev->start = 0;    return 0;}static int ei_start_xmit(struct sk_buff *skb, struct device *dev){    int e8390_base = dev->base_addr;    struct ei_device *ei_local = (struct ei_device *) dev->priv;    int length, send_length, output_page;/* *  We normally shouldn't be called if dev->tbusy is set, but the *  existing code does anyway. If it has been too long since the *  last Tx, we assume the board has died and kick it. */     if (dev->tbusy) {	/* Do timeouts, just like the 8003 driver. */		int txsr = inb(e8390_base+EN0_TSR), isr;		int tickssofar = jiffies - dev->trans_start;		if (tickssofar < TX_TIMEOUT ||	(tickssofar < (TX_TIMEOUT+5) && ! (txsr & ENTSR_PTX))) {			return 1;		}		isr = inb(e8390_base+EN0_ISR);		if (dev->start == 0) {			printk("%s: xmit on stopped card\n", dev->name);			return 1;		}		/*		 * Note that if the Tx posted a TX_ERR interrupt, then the		 * error will have been handled from the interrupt handler.		 * and not here.		 */		printk(KERN_DEBUG "%s: Tx timed out, %s TSR=%#2x, ISR=%#2x, t=%d.\n",		   dev->name, (txsr & ENTSR_ABT) ? "excess collisions." :		   (isr) ? "lost interrupt?" : "cable problem?", txsr, isr, tickssofar);		if (!isr && !ei_local->stat.tx_packets) {		   /* The 8390 probably hasn't gotten on the cable yet. */		   ei_local->interface_num ^= 1;   /* Try a different xcvr.  */		}		/* Try to restart the card.  Perhaps the user has fixed something. */		ei_reset_8390(dev);		NS8390_init(dev, 1);		dev->trans_start = jiffies;    }        /* Sending a NULL skb means some higher layer thinks we've missed an       tx-done interrupt. Caution: dev_tint() handles the cli()/sti()       itself. */    if (skb == NULL) {		dev_tint(dev);		return 0;    }        length = skb->len;    if (skb->len <= 0)		return 0;    /* Mask interrupts from the ethercard. */    outb_p(0x00, e8390_base + EN0_IMR);    if (dev->interrupt) {	printk("%s: Tx request while isr active.\n",dev->name);	outb_p(ENISR_ALL, e8390_base + EN0_IMR);	return 1;    }    ei_local->irqlock = 1;    send_length = ETH_ZLEN < length ? length : ETH_ZLEN;#ifdef EI_PINGPONG    /*     * We have two Tx slots available for use. Find the first free     * slot, and then perform some sanity checks. With two Tx bufs,     * you get very close to transmitting back-to-back packets. With     * only one Tx buf, the transmitter sits idle while you reload the     * card, leaving a substantial gap between each transmitted packet.     */    if (ei_local->tx1 == 0) {	output_page = ei_local->tx_start_page;	ei_local->tx1 = send_length;	if (ei_debug  &&  ei_local->tx2 > 0)		printk("%s: idle transmitter tx2=%d, lasttx=%d, txing=%d.\n",			dev->name, ei_local->tx2, ei_local->lasttx, ei_local->txing);    } else if (ei_local->tx2 == 0) {	output_page = ei_local->tx_start_page + TX_1X_PAGES;	ei_local->tx2 = send_length;	if (ei_debug  &&  ei_local->tx1 > 0)		printk("%s: idle transmitter, tx1=%d, lasttx=%d, txing=%d.\n",			dev->name, ei_local->tx1, ei_local->lasttx, ei_local->txing);    } else {	/* We should never get here. */	if (ei_debug)		printk("%s: No Tx buffers free! irq=%d tx1=%d tx2=%d last=%d\n",			dev->name, dev->interrupt, ei_local->tx1, ei_local->tx2, ei_local->lasttx);	ei_local->irqlock = 0;	dev->tbusy = 1;	outb_p(ENISR_ALL, e8390_base + EN0_IMR);	return 1;    }    /*     * Okay, now upload the packet and trigger a send if the transmitter     * isn't already sending. If it is busy, the interrupt handler will     * trigger the send later, upon receiving a Tx done interrupt.     */    ei_block_output(dev, length, skb->data, output_page);    if (! ei_local->txing) {	ei_local->txing = 1;	NS8390_trigger_send(dev, send_length, output_page);	dev->trans_start = jiffies;	if (output_page == ei_local->tx_start_page) {		ei_local->tx1 = -1;		ei_local->lasttx = -1;	} else {		ei_local->tx2 = -1;		ei_local->lasttx = -2;	}    } else	ei_local->txqueue++;    dev->tbusy = (ei_local->tx1  &&  ei_local->tx2);#else	/* EI_PINGPONG */    /*     * Only one Tx buffer in use. You need two Tx bufs to come close to     * back-to-back transmits. Expect a 20 -> 25% performance hit on     * reasonable hardware if you only use one Tx buffer.     */    ei_block_output(dev, length, skb->data, ei_local->tx_start_page);    ei_local->txing = 1;    NS8390_trigger_send(dev, send_length, ei_local->tx_start_page);    dev->trans_start = jiffies;    dev->tbusy = 1;#endif	/* EI_PINGPONG */    /* Turn 8390 interrupts back on. */    ei_local->irqlock = 0;    outb_p(ENISR_ALL, e8390_base + EN0_IMR);    dev_kfree_skb (skb, FREE_WRITE);        return 0;}/* The typical workload of the driver:   Handle the ether interface interrupts. */void ei_interrupt(int irq, void *dev_id, struct pt_regs * regs){    struct device *dev = (struct device *)(irq2dev_map[irq]);    int e8390_base;    int interrupts, nr_serviced = 0;    struct ei_device *ei_local;        if (dev == NULL) {		printk ("net_interrupt(): irq %d for unknown device.\n", irq);		return;    }    e8390_base = dev->base_addr;    ei_local = (struct ei_device *) dev->priv;    if (dev->interrupt || ei_local->irqlock) {		/* The "irqlock" check is only for testing. */		printk(ei_local->irqlock			   ? "%s: Interrupted while interrupts are masked! isr=%#2x imr=%#2x.\n"			   : "%s: Reentering the interrupt handler! isr=%#2x imr=%#2x.\n",			   dev->name, inb_p(e8390_base + EN0_ISR),			   inb_p(e8390_base + EN0_IMR));		return;    }        dev->interrupt = 1;        /* Change to page 0 and read the intr status reg. */    outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);    if (ei_debug > 3)		printk("%s: interrupt(isr=%#2.2x).\n", dev->name,			   inb_p(e8390_base + EN0_ISR));        /* !!Assumption!! -- we stay in page 0.	 Don't break this. */    while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0		   && ++nr_serviced < MAX_SERVICE) {		if (dev->start == 0) {			printk("%s: interrupt from stopped card\n", dev->name);			interrupts = 0;			break;		}		if (interrupts & ENISR_OVER) {			ei_rx_overrun(dev);		} else if (interrupts & (ENISR_RX+ENISR_RX_ERR)) {			/* Got a good (?) packet. */			ei_receive(dev);		}		/* Push the next to-transmit packet through. */		if (interrupts & ENISR_TX) {			ei_tx_intr(dev);		} else if (interrupts & ENISR_TX_ERR) {			ei_tx_err(dev);		}		if (interrupts & ENISR_COUNTERS) {			ei_local->stat.rx_frame_errors += inb_p(e8390_base + EN0_COUNTER0);			ei_local->stat.rx_crc_errors   += inb_p(e8390_base + EN0_COUNTER1);			ei_local->stat.rx_missed_errors+= inb_p(e8390_base + EN0_COUNTER2);			outb_p(ENISR_COUNTERS, e8390_base + EN0_ISR); /* Ack intr. */		}				/* Ignore any RDC interrupts that make it back to here. */		if (interrupts & ENISR_RDC) {			outb_p(ENISR_RDC, e8390_base + EN0_ISR);		}		outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);    }        if (interrupts && ei_debug) {		outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);		if (nr_serviced >= MAX_SERVICE) {			printk("%s: Too much work at interrupt, status %#2.2x\n",				   dev->name, interrupts);			outb_p(ENISR_ALL, e8390_base + EN0_ISR); /* Ack. most intrs. */		} else {			printk("%s: unknown interrupt %#2x\n", dev->name, interrupts);			outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */		}    }    dev->interrupt = 0;    return;}/* * A transmitter error has happened. Most likely excess collisions (which * is a fairly normal condition). If the error is one where the Tx will * have been aborted, we try and send another one right away, instead of * letting the failed packet sit and collect dust in the Tx buffer. This * is a much better solution as it avoids kernel based Tx timeouts, and * an unnecessary card reset. */static void ei_tx_err(struct device *dev){    int e8390_base = dev->base_addr;    unsigned char txsr = inb_p(e8390_base+EN0_TSR);    unsigned char tx_was_aborted = txsr & (ENTSR_ABT+ENTSR_FU);    struct ei_device *ei_local = (struct ei_device *) dev->priv;#ifdef VERBOSE_ERROR_DUMP    printk(KERN_DEBUG "%s: transmitter error (%#2x): ", dev->name, txsr);    if (txsr & ENTSR_ABT)		printk("excess-collisions ");    if (txsr & ENTSR_ND)		printk("non-deferral ");    if (txsr & ENTSR_CRS)		printk("lost-carrier ");    if (txsr & ENTSR_FU)		printk("FIFO-underrun ");    if (txsr & ENTSR_CDH)		printk("lost-heartbeat ");    printk("\n");#endif    outb_p(ENISR_TX_ERR, e8390_base + EN0_ISR); /* Ack intr. */    if (tx_was_aborted)		ei_tx_intr(dev);    /*     * Note: NCR reads zero on 16 collisions so we add them     * in by hand. Somebody might care...     */    if (txsr & ENTSR_ABT)	ei_local->stat.collisions += 16;	}/* We have finished a transmit: check for errors and then trigger the next   packet to be sent. */static void ei_tx_intr(struct device *dev){    int e8390_base = dev->base_addr;    int status = inb(e8390_base + EN0_TSR);

⌨️ 快捷键说明

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