Skip to content
Snippets Groups Projects
3c523.c 38.8 KiB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
				}
			} else {
				printk(KERN_WARNING "%s: received oversized frame.\n", dev->name);
				p->stats.rx_dropped++;
			}
		} else {	/* frame !(ok), only with 'save-bad-frames' */
			printk(KERN_WARNING "%s: oops! rfd-error-status: %04x\n", dev->name, status);
			p->stats.rx_errors++;
		}
		p->rfd_top->status = 0;
		p->rfd_top->last = RFD_SUSP;
		p->rfd_last->last = 0;	/* delete RU_SUSP  */
		p->rfd_last = p->rfd_top;
		p->rfd_top = (struct rfd_struct *) make32(p->rfd_top->next);	/* step to next RFD */
	}
}

/**********************************************************
 * handle 'Receiver went not ready'.
 */

static void elmc_rnr_int(struct net_device *dev)
{
	struct priv *p = (struct priv *) dev->priv;

	p->stats.rx_errors++;

	WAIT_4_SCB_CMD();	/* wait for the last cmd */
	p->scb->cmd = RUC_ABORT;	/* usually the RU is in the 'no resource'-state .. abort it now. */
	elmc_attn586();
	WAIT_4_SCB_CMD();	/* wait for accept cmd. */

	alloc_rfa(dev, (char *) p->rfd_first);
	startrecv586(dev);	/* restart RU */

	printk(KERN_WARNING "%s: Receive-Unit restarted. Status: %04x\n", dev->name, p->scb->status);

}

/**********************************************************
 * handle xmit - interrupt
 */

static void elmc_xmt_int(struct net_device *dev)
{
	int status;
	struct priv *p = (struct priv *) dev->priv;

	status = p->xmit_cmds[p->xmit_last]->cmd_status;
	if (!(status & STAT_COMPL)) {
		printk(KERN_WARNING "%s: strange .. xmit-int without a 'COMPLETE'\n", dev->name);
	}
	if (status & STAT_OK) {
		p->stats.tx_packets++;
		p->stats.collisions += (status & TCMD_MAXCOLLMASK);
	} else {
		p->stats.tx_errors++;
		if (status & TCMD_LATECOLL) {
			printk(KERN_WARNING "%s: late collision detected.\n", dev->name);
			p->stats.collisions++;
		} else if (status & TCMD_NOCARRIER) {
			p->stats.tx_carrier_errors++;
			printk(KERN_WARNING "%s: no carrier detected.\n", dev->name);
		} else if (status & TCMD_LOSTCTS) {
			printk(KERN_WARNING "%s: loss of CTS detected.\n", dev->name);
		} else if (status & TCMD_UNDERRUN) {
			p->stats.tx_fifo_errors++;
			printk(KERN_WARNING "%s: DMA underrun detected.\n", dev->name);
		} else if (status & TCMD_MAXCOLL) {
			printk(KERN_WARNING "%s: Max. collisions exceeded.\n", dev->name);
			p->stats.collisions += 16;
		}
	}

#if (NUM_XMIT_BUFFS != 1)
	if ((++p->xmit_last) == NUM_XMIT_BUFFS) {
		p->xmit_last = 0;
	}
#endif

	netif_wake_queue(dev);
}

/***********************************************************
 * (re)start the receiver
 */

static void startrecv586(struct net_device *dev)
{
	struct priv *p = (struct priv *) dev->priv;

	p->scb->rfa_offset = make16(p->rfd_first);
	p->scb->cmd = RUC_START;
	elmc_attn586();		/* start cmd. */
	WAIT_4_SCB_CMD();	/* wait for accept cmd. (no timeout!!) */
}

/******************************************************
 * timeout
 */
Linus Torvalds's avatar
Linus Torvalds committed
static void elmc_timeout(struct net_device *dev)
{
	struct priv *p = (struct priv *) dev->priv;
	/* COMMAND-UNIT active? */
	if (p->scb->status & CU_ACTIVE) {
#ifdef DEBUG
		printk("%s: strange ... timeout with CU active?!?\n", dev->name);
		printk("%s: X0: %04x N0: %04x N1: %04x %d\n", dev->name, (int) p->xmit_cmds[0]->cmd_status, (int) p->nop_cmds[0]->cmd_status, (int) p->nop_cmds[1]->cmd_status, (int) p->nop_point);
#endif
		p->scb->cmd = CUC_ABORT;
		elmc_attn586();
		WAIT_4_SCB_CMD();
		p->scb->cbl_offset = make16(p->nop_cmds[p->nop_point]);
		p->scb->cmd = CUC_START;
		elmc_attn586();
		WAIT_4_SCB_CMD();
		netif_wake_queue(dev);
	} else {
#ifdef DEBUG
		printk("%s: xmitter timed out, try to restart! stat: %04x\n", dev->name, p->scb->status);
		printk("%s: command-stats: %04x %04x\n", dev->name, p->xmit_cmds[0]->cmd_status, p->xmit_cmds[1]->cmd_status);
#endif
		elmc_close(dev);
		elmc_open(dev);
	}
}
Linus Torvalds's avatar
Linus Torvalds committed
/******************************************************
 * send frame
 */

static int elmc_send_packet(struct sk_buff *skb, struct net_device *dev)
{
	int len;
	int i;
#ifndef NO_NOPCOMMANDS
	int next_nop;
#endif
	struct priv *p = (struct priv *) dev->priv;

	netif_stop_queue(dev);

	len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
Linus Torvalds's avatar
Linus Torvalds committed
	if (len != skb->len)
		memset((char *) p->xmit_cbuffs[p->xmit_count], 0, ETH_ZLEN);
	skb_copy_from_linear_data(skb, (char *) p->xmit_cbuffs[p->xmit_count], skb->len);
Linus Torvalds's avatar
Linus Torvalds committed

#if (NUM_XMIT_BUFFS == 1)
#ifdef NO_NOPCOMMANDS
	p->xmit_buffs[0]->size = TBD_LAST | len;
	for (i = 0; i < 16; i++) {
		p->scb->cbl_offset = make16(p->xmit_cmds[0]);
		p->scb->cmd = CUC_START;
		p->xmit_cmds[0]->cmd_status = 0;
			elmc_attn586();
		dev->trans_start = jiffies;
		if (!i) {
			dev_kfree_skb(skb);
		}
		WAIT_4_SCB_CMD();
		if ((p->scb->status & CU_ACTIVE)) {	/* test it, because CU sometimes doesn't start immediately */
			break;
		}
		if (p->xmit_cmds[0]->cmd_status) {
			break;
		}
		if (i == 15) {
			printk(KERN_WARNING "%s: Can't start transmit-command.\n", dev->name);
		}
	}
#else
	next_nop = (p->nop_point + 1) & 0x1;
	p->xmit_buffs[0]->size = TBD_LAST | len;
Linus Torvalds's avatar
Linus Torvalds committed
	p->xmit_cmds[0]->cmd_link = p->nop_cmds[next_nop]->cmd_link
	    = make16((p->nop_cmds[next_nop]));
	p->xmit_cmds[0]->cmd_status = p->nop_cmds[next_nop]->cmd_status = 0;

	p->nop_cmds[p->nop_point]->cmd_link = make16((p->xmit_cmds[0]));
	dev->trans_start = jiffies;
	p->nop_point = next_nop;
	dev_kfree_skb(skb);
#endif
#else
	p->xmit_buffs[p->xmit_count]->size = TBD_LAST | len;
	if ((next_nop = p->xmit_count + 1) == NUM_XMIT_BUFFS) {
		next_nop = 0;
	}
	p->xmit_cmds[p->xmit_count]->cmd_status = 0;
	p->xmit_cmds[p->xmit_count]->cmd_link = p->nop_cmds[next_nop]->cmd_link
	    = make16((p->nop_cmds[next_nop]));
	p->nop_cmds[next_nop]->cmd_status = 0;
		p->nop_cmds[p->xmit_count]->cmd_link = make16((p->xmit_cmds[p->xmit_count]));
	dev->trans_start = jiffies;
	p->xmit_count = next_nop;
	if (p->xmit_count != p->xmit_last)
		netif_wake_queue(dev);
	dev_kfree_skb(skb);
#endif
	return 0;
}

/*******************************************
 * Someone wanna have the statistics
 */

static struct net_device_stats *elmc_get_stats(struct net_device *dev)
{
	struct priv *p = (struct priv *) dev->priv;
	unsigned short crc, aln, rsc, ovrn;

	crc = p->scb->crc_errs;	/* get error-statistic from the ni82586 */
	p->scb->crc_errs -= crc;
	aln = p->scb->aln_errs;
	p->scb->aln_errs -= aln;
	rsc = p->scb->rsc_errs;
	p->scb->rsc_errs -= rsc;
	ovrn = p->scb->ovrn_errs;
	p->scb->ovrn_errs -= ovrn;

	p->stats.rx_crc_errors += crc;
	p->stats.rx_fifo_errors += ovrn;
	p->stats.rx_frame_errors += aln;
	p->stats.rx_dropped += rsc;

	return &p->stats;
}

/********************************************************
 * Set MC list ..
 */

#ifdef ELMC_MULTICAST
static void set_multicast_list(struct net_device *dev)
{
	if (!dev->start) {
		/* without a running interface, promiscuous doesn't work */
		return;
	}
	dev->start = 0;
	alloc586(dev);
	init586(dev);
	startrecv586(dev);
	dev->start = 1;
}
#endif

static void netdev_get_drvinfo(struct net_device *dev,
			       struct ethtool_drvinfo *info)
{
	strcpy(info->driver, DRV_NAME);
	strcpy(info->version, DRV_VERSION);
	sprintf(info->bus_info, "MCA 0x%lx", dev->base_addr);
}

static const struct ethtool_ops netdev_ethtool_ops = {
Linus Torvalds's avatar
Linus Torvalds committed
	.get_drvinfo		= netdev_get_drvinfo,
};

#ifdef MODULE

/* Increase if needed ;) */
#define MAX_3C523_CARDS 4

static struct net_device *dev_elmc[MAX_3C523_CARDS];
static int irq[MAX_3C523_CARDS];
static int io[MAX_3C523_CARDS];
module_param_array(irq, int, NULL, 0);
module_param_array(io, int, NULL, 0);
MODULE_PARM_DESC(io, "EtherLink/MC I/O base address(es)");
MODULE_PARM_DESC(irq, "EtherLink/MC IRQ number(s)");
MODULE_LICENSE("GPL");
Linus Torvalds's avatar
Linus Torvalds committed

int __init init_module(void)
Linus Torvalds's avatar
Linus Torvalds committed
{
	int this_dev,found = 0;

	/* Loop until we either can't find any more cards, or we have MAX_3C523_CARDS */
Linus Torvalds's avatar
Linus Torvalds committed
	for(this_dev=0; this_dev<MAX_3C523_CARDS; this_dev++) {
		struct net_device *dev = alloc_etherdev(sizeof(struct priv));
		if (!dev)
			break;
		dev->irq=irq[this_dev];
		dev->base_addr=io[this_dev];
		if (do_elmc_probe(dev) == 0) {
			dev_elmc[this_dev] = dev;
			found++;
			continue;
Linus Torvalds's avatar
Linus Torvalds committed
		}
		free_netdev(dev);
		if (io[this_dev]==0)
			break;
		printk(KERN_WARNING "3c523.c: No 3c523 card found at io=%#x\n",io[this_dev]);
	}

	if(found==0) {
		if(io[0]==0) printk(KERN_NOTICE "3c523.c: No 3c523 cards found\n");
		return -ENXIO;
	} else return 0;
}

void __exit cleanup_module(void)
Linus Torvalds's avatar
Linus Torvalds committed
{
	int this_dev;
	for (this_dev=0; this_dev<MAX_3C523_CARDS; this_dev++) {
		struct net_device *dev = dev_elmc[this_dev];
		if (dev) {
			unregister_netdev(dev);
			cleanup_card(dev);
			free_netdev(dev);
		}
	}
}

#endif				/* MODULE */