ibmlana.c

来自「linux 内核源代码」· C语言 代码 · 共 1,069 行 · 第 1/2 页

C
1,069
字号
	priv->currtxdescr = descr;	outw(CMDREG_TXP, dev->base_addr + SONIC_CMDREG);}/* ------------------------------------------------------------------------ * interrupt handler(s) * ------------------------------------------------------------------------ *//* receive buffer area exhausted */static void irqrbe_handler(struct net_device *dev){	ibmlana_priv *priv = netdev_priv(dev);	/* point the SONIC back to the RRA start */	outw(priv->rrastart, dev->base_addr + SONIC_RRP);	outw(priv->rrastart, dev->base_addr + SONIC_RWP);}/* receive interrupt */static void irqrx_handler(struct net_device *dev){	ibmlana_priv *priv = netdev_priv(dev);	rda_t rda;	u32 rdaaddr, lrdaaddr;	/* loop until ... */	while (1) {		/* read descriptor that was next to be filled by SONIC */		rdaaddr = priv->rdastart + (priv->nextrxdescr * sizeof(rda_t));		lrdaaddr = priv->rdastart + (priv->lastrxdescr * sizeof(rda_t));		memcpy_fromio(&rda, priv->base + rdaaddr, sizeof(rda_t));		/* iron out upper word halves of fields we use - SONIC will duplicate		   bits 0..15 to 16..31 */		rda.status &= 0xffff;		rda.length &= 0xffff;		rda.startlo &= 0xffff;		/* stop if the SONIC still owns it, i.e. there is no data for us */		if (rda.inuse)			break;		/* good packet? */		else if (rda.status & RCREG_PRX) {			struct sk_buff *skb;			/* fetch buffer */			skb = dev_alloc_skb(rda.length + 2);			if (skb == NULL)				dev->stats.rx_dropped++;			else {				/* copy out data */				memcpy_fromio(skb_put(skb, rda.length),					       priv->base +					       rda.startlo, rda.length);				/* set up skb fields */				skb->protocol = eth_type_trans(skb, dev);				skb->ip_summed = CHECKSUM_NONE;				/* bookkeeping */				dev->last_rx = jiffies;				dev->stats.rx_packets++;				dev->stats.rx_bytes += rda.length;				/* pass to the upper layers */				netif_rx(skb);			}		}		/* otherwise check error status bits and increase statistics */		else {			dev->stats.rx_errors++;			if (rda.status & RCREG_FAER)				dev->stats.rx_frame_errors++;			if (rda.status & RCREG_CRCR)				dev->stats.rx_crc_errors++;		}		/* descriptor processed, will become new last descriptor in queue */		rda.link = 1;		rda.inuse = 1;		memcpy_toio(priv->base + rdaaddr, &rda,			     sizeof(rda_t));		/* set up link and EOL = 0 in currently last descriptor. Only write		   the link field since the SONIC may currently already access the		   other fields. */		memcpy_toio(priv->base + lrdaaddr + 20, &rdaaddr, 4);		/* advance indices */		priv->lastrxdescr = priv->nextrxdescr;		if ((++priv->nextrxdescr) >= priv->rxbufcnt)			priv->nextrxdescr = 0;	}}/* transmit interrupt */static void irqtx_handler(struct net_device *dev){	ibmlana_priv *priv = netdev_priv(dev);	tda_t tda;	/* fetch descriptor (we forgot the size ;-) */	memcpy_fromio(&tda, priv->base + priv->tdastart + (priv->currtxdescr * sizeof(tda_t)), sizeof(tda_t));	/* update statistics */	dev->stats.tx_packets++;	dev->stats.tx_bytes += tda.length;	/* update our pointers */	priv->txused[priv->currtxdescr] = 0;	priv->txusedcnt--;	/* if there are more descriptors present in RAM, start them */	if (priv->txusedcnt > 0)		StartTx(dev, (priv->currtxdescr + 1) % TXBUFCNT);	/* tell the upper layer we can go on transmitting */	netif_wake_queue(dev);}static void irqtxerr_handler(struct net_device *dev){	ibmlana_priv *priv = netdev_priv(dev);	tda_t tda;	/* fetch descriptor to check status */	memcpy_fromio(&tda, priv->base + priv->tdastart + (priv->currtxdescr * sizeof(tda_t)), sizeof(tda_t));	/* update statistics */	dev->stats.tx_errors++;	if (tda.status & (TCREG_NCRS | TCREG_CRSL))		dev->stats.tx_carrier_errors++;	if (tda.status & TCREG_EXC)		dev->stats.tx_aborted_errors++;	if (tda.status & TCREG_OWC)		dev->stats.tx_window_errors++;	if (tda.status & TCREG_FU)		dev->stats.tx_fifo_errors++;	/* update our pointers */	priv->txused[priv->currtxdescr] = 0;	priv->txusedcnt--;	/* if there are more descriptors present in RAM, start them */	if (priv->txusedcnt > 0)		StartTx(dev, (priv->currtxdescr + 1) % TXBUFCNT);	/* tell the upper layer we can go on transmitting */	netif_wake_queue(dev);}/* general interrupt entry */static irqreturn_t irq_handler(int irq, void *device){	struct net_device *dev = (struct net_device *) device;	u16 ival;	/* in case we're not meant... */	if (!(inb(dev->base_addr + BCMREG) & BCMREG_IPEND))		return IRQ_NONE;	/* loop through the interrupt bits until everything is clear */	while (1) {		ival = inw(dev->base_addr + SONIC_ISREG);		if (ival & ISREG_RBE) {			irqrbe_handler(dev);			outw(ISREG_RBE, dev->base_addr + SONIC_ISREG);		}		if (ival & ISREG_PKTRX) {			irqrx_handler(dev);			outw(ISREG_PKTRX, dev->base_addr + SONIC_ISREG);		}		if (ival & ISREG_TXDN) {			irqtx_handler(dev);			outw(ISREG_TXDN, dev->base_addr + SONIC_ISREG);		}		if (ival & ISREG_TXER) {			irqtxerr_handler(dev);			outw(ISREG_TXER, dev->base_addr + SONIC_ISREG);		}		break;	}	return IRQ_HANDLED;}/* ------------------------------------------------------------------------ * driver methods * ------------------------------------------------------------------------ *//* MCA info */static int ibmlana_getinfo(char *buf, int slot, void *d){	int len = 0, i;	struct net_device *dev = (struct net_device *) d;	ibmlana_priv *priv;	/* can't say anything about an uninitialized device... */	if (dev == NULL)		return len;	priv = netdev_priv(dev);	/* print info */	len += sprintf(buf + len, "IRQ: %d\n", priv->realirq);	len += sprintf(buf + len, "I/O: %#lx\n", dev->base_addr);	len += sprintf(buf + len, "Memory: %#lx-%#lx\n", dev->mem_start, dev->mem_end - 1);	len += sprintf(buf + len, "Transceiver: %s\n", MediaNames[priv->medium]);	len += sprintf(buf + len, "Device: %s\n", dev->name);	len += sprintf(buf + len, "MAC address:");	for (i = 0; i < 6; i++)		len += sprintf(buf + len, " %02x", dev->dev_addr[i]);	buf[len++] = '\n';	buf[len] = 0;	return len;}/* open driver.  Means also initialization and start of LANCE */static int ibmlana_open(struct net_device *dev){	int result;	ibmlana_priv *priv = netdev_priv(dev);	/* register resources - only necessary for IRQ */	result = request_irq(priv->realirq, irq_handler, IRQF_SHARED | IRQF_SAMPLE_RANDOM, dev->name, dev);	if (result != 0) {		printk(KERN_ERR "%s: failed to register irq %d\n", dev->name, dev->irq);		return result;	}	dev->irq = priv->realirq;	/* set up the card and SONIC */	InitBoard(dev);	/* initialize operational flags */	netif_start_queue(dev);	return 0;}/* close driver.  Shut down board and free allocated resources */static int ibmlana_close(struct net_device *dev){	/* turn off board */	/* release resources */	if (dev->irq != 0)		free_irq(dev->irq, dev);	dev->irq = 0;	return 0;}/* transmit a block. */static int ibmlana_tx(struct sk_buff *skb, struct net_device *dev){	ibmlana_priv *priv = netdev_priv(dev);	int retval = 0, tmplen, addr;	unsigned long flags;	tda_t tda;	int baddr;	/* find out if there are free slots for a frame to transmit. If not,	   the upper layer is in deep desperation and we simply ignore the frame. */	if (priv->txusedcnt >= TXBUFCNT) {		retval = -EIO;		dev->stats.tx_dropped++;		goto tx_done;	}	/* copy the frame data into the next free transmit buffer - fillup missing */	tmplen = skb->len;	if (tmplen < 60)		tmplen = 60;	baddr = priv->txbufstart + (priv->nexttxdescr * PKTSIZE);	memcpy_toio(priv->base + baddr, skb->data, skb->len);	/* copy filler into RAM - in case we're filling up...	   we're filling a bit more than necessary, but that doesn't harm	   since the buffer is far larger...	   Sorry Linus for the filler string but I couldn't resist ;-) */	if (tmplen > skb->len) {		char *fill = "NetBSD is a nice OS too! ";		unsigned int destoffs = skb->len, l = strlen(fill);		while (destoffs < tmplen) {			memcpy_toio(priv->base + baddr + destoffs, fill, l);			destoffs += l;		}	}	/* set up the new frame descriptor */	addr = priv->tdastart + (priv->nexttxdescr * sizeof(tda_t));	memcpy_fromio(&tda, priv->base + addr, sizeof(tda_t));	tda.length = tda.fraglength = tmplen;	memcpy_toio(priv->base + addr, &tda, sizeof(tda_t));	/* if there were no active descriptors, trigger the SONIC */	spin_lock_irqsave(&priv->lock, flags);	priv->txusedcnt++;	priv->txused[priv->nexttxdescr] = 1;	/* are all transmission slots used up ? */	if (priv->txusedcnt >= TXBUFCNT)		netif_stop_queue(dev);	if (priv->txusedcnt == 1)		StartTx(dev, priv->nexttxdescr);	priv->nexttxdescr = (priv->nexttxdescr + 1) % TXBUFCNT;	spin_unlock_irqrestore(&priv->lock, flags);tx_done:	dev_kfree_skb(skb);	return retval;}/* switch receiver mode. */static void ibmlana_set_multicast_list(struct net_device *dev){	/* first stop the SONIC... */	StopSONIC(dev);	/* ...then reinit it with the new flags */	InitBoard(dev);}/* ------------------------------------------------------------------------ * hardware check * ------------------------------------------------------------------------ */static int startslot;		/* counts through slots when probing multiple devices */static int ibmlana_probe(struct net_device *dev){	int slot, z;	int base = 0, irq = 0, iobase = 0, memlen = 0;	ibmlana_priv *priv;	ibmlana_medium medium;	DECLARE_MAC_BUF(mac);	/* can't work without an MCA bus ;-) */	if (MCA_bus == 0)		return -ENODEV;	base = dev->mem_start;	irq = dev->irq;	for (slot = startslot; (slot = mca_find_adapter(IBM_LANA_ID, slot)) != -1; slot++) {		/* deduce card addresses */		getaddrs(slot, &base, &memlen, &iobase, &irq, &medium);		/* slot already in use ? */		if (mca_is_adapter_used(slot))			continue;		/* were we looking for something different ? */		if (dev->irq && dev->irq != irq)			continue;		if (dev->mem_start && dev->mem_start != base)			continue;		/* found something that matches */		break;	}	/* nothing found ? */	if (slot == -1)		return (base != 0 || irq != 0) ? -ENXIO : -ENODEV;	/* announce success */	printk(KERN_INFO "%s: IBM LAN Adapter/A found in slot %d\n", dev->name, slot + 1);	/* try to obtain I/O range */	if (!request_region(iobase, IBM_LANA_IORANGE, DRV_NAME)) {		printk(KERN_ERR "%s: cannot allocate I/O range at %#x!\n", DRV_NAME, iobase);		startslot = slot + 1;		return -EBUSY;	}	priv = netdev_priv(dev);	priv->slot = slot;	priv->realirq = irq;	priv->medium = medium;	spin_lock_init(&priv->lock);	/* set base + irq for this device (irq not allocated so far) */	dev->irq = 0;	dev->mem_start = base;	dev->mem_end = base + memlen;	dev->base_addr = iobase;	priv->base = ioremap(base, memlen);	if (!priv->base) {		printk(KERN_ERR "%s: cannot remap memory!\n", DRV_NAME);		startslot = slot + 1;		release_region(iobase, IBM_LANA_IORANGE);		return -EBUSY;	}	/* make procfs entries */	mca_set_adapter_name(slot, "IBM LAN Adapter/A");	mca_set_adapter_procfn(slot, (MCA_ProcFn) ibmlana_getinfo, dev);	mca_mark_as_used(slot);	/* set methods */	dev->open = ibmlana_open;	dev->stop = ibmlana_close;	dev->hard_start_xmit = ibmlana_tx;	dev->do_ioctl = NULL;	dev->set_multicast_list = ibmlana_set_multicast_list;	dev->flags |= IFF_MULTICAST;	/* copy out MAC address */	for (z = 0; z < sizeof(dev->dev_addr); z++)		dev->dev_addr[z] = inb(dev->base_addr + MACADDRPROM + z);	/* print config */	printk(KERN_INFO "%s: IRQ %d, I/O %#lx, memory %#lx-%#lx, "	       "MAC address %s.\n",	       dev->name, priv->realirq, dev->base_addr,	       dev->mem_start, dev->mem_end - 1,	       print_mac(mac, dev->dev_addr));	printk(KERN_INFO "%s: %s medium\n", dev->name, MediaNames[priv->medium]);	/* reset board */	ResetBoard(dev);	/* next probe will start at next slot */	startslot = slot + 1;	return 0;}/* ------------------------------------------------------------------------ * modularization support * ------------------------------------------------------------------------ */#ifdef MODULE#define DEVMAX 5static struct net_device *moddevs[DEVMAX];static int irq;static int io;module_param(irq, int, 0);module_param(io, int, 0);MODULE_PARM_DESC(irq, "IBM LAN/A IRQ number");MODULE_PARM_DESC(io, "IBM LAN/A I/O base address");MODULE_LICENSE("GPL");int init_module(void){	int z;	startslot = 0;	for (z = 0; z < DEVMAX; z++) {		struct net_device *dev = alloc_etherdev(sizeof(ibmlana_priv));		if (!dev)			break;		dev->irq = irq;		dev->base_addr = io;		if (ibmlana_probe(dev)) {			free_netdev(dev);			break;		}		if (register_netdev(dev)) {			ibmlana_priv *priv = netdev_priv(dev);			release_region(dev->base_addr, IBM_LANA_IORANGE);			mca_mark_as_unused(priv->slot);			mca_set_adapter_name(priv->slot, "");			mca_set_adapter_procfn(priv->slot, NULL, NULL);			iounmap(priv->base);			free_netdev(dev);			break;		}		moddevs[z] = dev;	}	return (z > 0) ? 0 : -EIO;}void cleanup_module(void){	int z;	for (z = 0; z < DEVMAX; z++) {		struct net_device *dev = moddevs[z];		if (dev) {			ibmlana_priv *priv = netdev_priv(dev);			unregister_netdev(dev);			/*DeinitBoard(dev); */			release_region(dev->base_addr, IBM_LANA_IORANGE);			mca_mark_as_unused(priv->slot);			mca_set_adapter_name(priv->slot, "");			mca_set_adapter_procfn(priv->slot, NULL, NULL);			iounmap(priv->base);			free_netdev(dev);		}	}}#endif				/* MODULE */

⌨️ 快捷键说明

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