OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [drivers/] [net/] [mc68en302.c] - Rev 1777

Go to most recent revision | Compare with Previous | Blame | View Log

/* mcen302.c: A Linux network driver for Mototrola 68EN302 MCU
 *
 *	Copyright (C) 1999 Aplio S.A.
 *  Written by Vadim Lebedev
 *
 *  based on the skeleton.c by Donald Becker
 *	This software may be used and distributed according to the terms
 *	of the GNU Public License, incorporated herein by reference.
 *
 */
 
static const char *version =
	"$Version$\n";
 
/*
 *  Sources:
 *	List your sources of programming information to document that
 *	the driver is your own creation, and give due credit to others
 *	that contributed to the work. Remember that GNU project code
 *	cannot use proprietary or trade secret information. Interface
 *	definitions are generally considered non-copyrightable to the
 *	extent that the same names and structures must be used to be
 *	compatible.
 *
 *	Finally, keep in mind that the Linux kernel is has an API, not
 *	ABI. Proprietary object-code-only distributions are not permitted
 *	under the GPL.
 */
 
#include <linux/module.h>
 
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/in.h>
#include <linux/malloc.h>
#include <linux/string.h>
#include <asm/system.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <linux/errno.h>
 
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <asm/m68en302.h>
#include <asm/irq.h>
 
/*
 * The name of the card. Is used for messages and in the requests for
 * io regions, irqs and dma channels
 */
static const char* cardname = "en302";
 
/* First, a few definitions that the brave might change. */
 
/* A zero-terminated list of I/O addresses to be probed. */
static unsigned int netcard_portlist[] =
   { ECNTRL0 };
 
#define NETCARD_IO_EXTENT sizeof(struct _68EN302_ETHR_BLOCK)
 
 
 
/* use 0 for production, 1 for verification, >2 for debug */
#ifndef NET_DEBUG
#define NET_DEBUG 2
#endif
static unsigned int net_debug = NET_DEBUG;
 
 
#define MAX_FRAME_SIZE 0x600
#define RX_QUEUE_SIZE 8
#define TX_QUEUE_SIZE 8
#define TX_QUEUE_MASK 7
#define RX_QUEUE_MASK 7
#define HW_MAX_ADDRS (_68EN302_MAX_CET-1)
 
#define TX_BD_START 0
#define RX_BD_START 64
 
/* Information that need to be kept for each board. */
struct net_local {
	struct enet_statistics stats;
	long open_time;			/* Useless example local info. */
	struct sk_buff* rx_skb[RX_QUEUE_SIZE];
	struct sk_buff* tx_skb[TX_QUEUE_SIZE];
	struct _68EN302_ETHR_BLOCK* eblk;
	int		tx_running;
	int		tx_next;
	int		tx_next_done;
	int		tx_count;
	int		rx_next;		/* check this RX descriptor on the next interrupt */
};
 
/* The station (ethernet) address prefix, used for IDing the board. */
#if 0
#define SA_ADDR0 0x00
#define SA_ADDR1 0x42
#define SA_ADDR2 0x65
#endif
 
/* Index to functions, as function prototypes. */
 
extern int netcard_probe(struct device *dev);
 
static int netcard_probe1(struct device *dev, int ioaddr);
static int net_open(struct device *dev);
static int	net_send_packet(struct sk_buff *skb, struct device *dev);
static void net_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static void net_rx(struct device *dev);
static void net_tx(struct device* dev);
static int net_close(struct device *dev);
static struct enet_statistics *net_get_stats(struct device *dev);
static void set_multicast_list(struct device *dev);
 
 
 
#define tx_done(dev) 1
 
 
 
static void en302_set_filter(struct net_local* lp, struct dev_mc_list* list)
{
	int i = 1;
	struct _68EN302_ETHR_BLOCK* eblk = lp->eblk;
 
	while(list)
	{
		memcpy(eblk->CET[i], list->dmi_addr, ETH_ALEN);
		list = list->next;
		i++;
	}
 
	for(;  i < _68EN302_MAX_CET; i++)
		memset(eblk->CET[i], 0, ETH_ALEN);
}
 
static void en302_stop(volatile struct net_local *lp)
{
 
	lp->eblk->info.ECNTRL.GTS = 1;
 
	while(lp->tx_running)
		;
 
	lp->eblk->info.ECNTRL.ETHER_EN = 0;
	lp->rx_next = lp->tx_next = lp->tx_next_done = 0;
 
}
 
 
static void en302_send_packet(struct _68EN302_ETHR_BLOCK* eblk, void* buf, unsigned short length, int index)
{
	struct _68EN302_ETHR_TXBD* bd;
 
	bd = &eblk->BD[TX_BD_START+index].tx;
	bd->length = length;
	bd->address = (unsigned long) buf;
	bd->flags.w |=  0x9C00; /* R=1, I=1, L=1 TC=1 */
#if 0
	bd->flags.s.L = 1;
	bd->flags.s.I = 1;
	bd->flags.s.TC = 1;
	bd->flags.s.R = 1;
#endif
}
 
 
static void en302_flush_tx_ring(struct net_local* lp)
{
	int i;
 
	for(i = 0; i < TX_QUEUE_SIZE; i++)
	{
		if (lp->tx_skb[i])
			dev_kfree_skb (lp->tx_skb[i], FREE_WRITE);
		lp->tx_skb[i] = 0;
	}
 
	lp->tx_count = 0;
}
 
static void en302_flush_rx_ring(struct net_local* lp)
{
	int i;
 
	for(i = 0; i < RX_QUEUE_SIZE; i++)
	{
		if (lp->rx_skb[i])
			dev_kfree_skb (lp->rx_skb[i], FREE_READ);
		lp->rx_skb[i] = 0;
	}
 
}
 
static void en302_load_rx_ring(struct net_local *lp)
{
	struct _68EN302_ETHR_BLOCK* eblk = lp->eblk;
	int i;
 
 
	for(i = 0; i < RX_QUEUE_SIZE; i++)
	{
		struct sk_buff *skb;		
 
		if (!lp->rx_skb[i])
		{
			struct  _68EN302_ETHR_RXBD* bd;
			skb = dev_alloc_skb(MAX_FRAME_SIZE);
 
			if (skb == NULL)
				break;
 
			lp->rx_skb[i] = skb;
			bd = &eblk->BD[RX_BD_START+i].rx;			
			bd->length = MAX_FRAME_SIZE;
			bd->address.l = (unsigned long) skb->tail;
			bd->flags.s.I = 1;
			bd->flags.s.E = 1;
		}
	}
}
 
 
 
 
static void en302_init(struct net_local* lp, int flag)
{
	struct _68EN302_ETHR_BLOCK* eblk = lp->eblk;
 
	eblk->BD[TX_BD_START+TX_QUEUE_SIZE-1].tx.flags.s.W = 1;
	eblk->BD[RX_BD_START+RX_QUEUE_SIZE-1].rx.flags.s.W = 1;
 
	lp->rx_next = 0;
	lp->tx_next = 0;
	lp->tx_next_done = 0;
	lp->tx_count = 0;
 
	en302_load_rx_ring(lp);
	eblk->info.ECNTRL.GTS = 0;
	eblk->info.ECNTRL.ETHER_EN = 1;
 
 
 
}
 
 
 
 
 
 
 
/*
 * Check for a network adaptor of this type, and return '0' iff one exists.
 * If dev->base_addr == 0, probe all likely locations.
 * If dev->base_addr == 1, always return failure.
 * If dev->base_addr == 2, allocate space for the device and return success
 * (detachable devices only).
 */
#ifdef HAVE_DEVLIST
/*
 * Support for a alternate probe manager,
 * which will eliminate the boilerplate below.
 */
struct netdev_entry netcard_drv =
{cardname, netcard_probe1, NETCARD_IO_EXTENT, netcard_portlist};
#else
int
en302_probe(struct device *dev)
{
	int i;
	int base_addr = dev ? dev->base_addr : 0;
 
	if (base_addr > 0x1ff)    /* Check a single specified location. */
		return netcard_probe1(dev, base_addr);
	else if (base_addr != 0)  /* Don't probe at all. */
		return -ENXIO;
 
	for (i = 0; netcard_portlist[i]; i++) {
		int ioaddr = netcard_portlist[i];
		if (check_region(ioaddr, NETCARD_IO_EXTENT))
			continue;
		if (netcard_probe1(dev, ioaddr) == 0)
			return 0;
	}
 
	return -ENODEV;
}
#endif
 
/*
 * This is the real probe routine. Linux has a history of friendly device
 * probes on the ISA bus. A good device probes avoids doing writes, and
 * verifies that the correct device exists and functions.
 */
static int netcard_probe1(struct device *dev, int ioaddr)
{
	static unsigned version_printed = 0;
	/* address of the INTERRUPT EXTENSION REGISTER */
	unsigned long ier = 2 + (((unsigned long) (REG16(MOBARREG) & ~0xf000)) << 12);
 
 
	/* Allocate a new 'dev' if needed. */
	if (dev == NULL) {
		/*
		 * Don't allocate the private data here, it is done later
		 * This makes it easier to free the memory when this driver
		 * is used as a module.
		 */
		dev = init_etherdev(0, 0);
		if (dev == NULL)
			return -ENOMEM;
	}
 
	if (net_debug  &&  version_printed++ == 0)
		printk(KERN_DEBUG "%s", version);
 
	printk(KERN_INFO "%s: %s found at %#3x, ", dev->name, cardname, ioaddr);
 
	/* Fill in the 'dev' fields. */
	dev->base_addr = ioaddr;
	if (REG16(ier) & 0x0800) /* MIL bit is set */
	{
		dev->irq = IRQ3;
	}
	else
	{
		dev->irq = IRQ5;
	}
 
 
	/* Initialize the device structure. */
	if (dev->priv == NULL) {
		dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
		if (dev->priv == NULL)
			return -ENOMEM;
	}
 
	memset(dev->priv, 0, sizeof(struct net_local));
 
	/* Grab the region so that no one else tries to probe our ioports. */
	request_region(ioaddr, NETCARD_IO_EXTENT, cardname);
 
	dev->open		= net_open;
	dev->stop		= net_close;
	dev->hard_start_xmit = net_send_packet;
	dev->get_stats	= net_get_stats;
	dev->set_multicast_list = &set_multicast_list;
 
	/* Fill in the fields of the device structure with ethernet values. */
	ether_setup(dev);
 
	return 0;
}
 
/*
 * Open/initialize the board. This is called (in the current kernel)
 * sometime after booting when the 'ifconfig' program is run.
 *
 * This routine should set everything up anew at each open, even
 * registers that "should" only need to be set once at boot, so that
 * there is non-reboot way to recover if something goes wrong.
 */
static int
net_open(struct device *dev)
{
	struct net_local *lp = (struct net_local *)dev->priv;
	struct _68EN302_ETHR_BLOCK *eblk = (struct _68EN302_ETHR_BLOCK*) dev->base_addr;
	unsigned short vecBase = REG16(GIMR) & 0xE0;   /* interrupt vector base  */
 
	/*
	 * This is used if the interrupt line can turned off (shared).
	 * See 3c503.c for an example of selecting the IRQ at config-time.
	 */
	if (request_irq(dev->irq, &net_interrupt, 0, cardname, NULL)) {
		return -EAGAIN;
	}
 
 
	irq2dev_map[dev->irq] = dev;
 
 
	lp->eblk = eblk;
 
	eblk->info.ECNTRL.RESET = 1;
	eblk->info.EDMA.MBZ = 0;
	eblk->info.EDMA.BLIM = 3;
	eblk->info.EDMA.WMRK = 1;
	eblk->info.EDMA.BDSIZE = 3;
 
 
	eblk->info.EMRBRL = MAX_FRAME_SIZE;
	eblk->info.INTR_VEC.VG = 0;
	eblk->info.INTR_VEC.INV = vecBase + dev->irq;
	eblk->info.INTR_VEC.MBZ = 0;
 
	eblk->info.INTR_MASK = 0x07BC;
	eblk->info.ECNFIG = ETHR_FDEN;
 
	eblk->info.AR_CNTRL.MBZ = 0;
	eblk->info.AR_CNTRL.MULT = 0;
	eblk->info.AR_CNTRL.PROM = 0;
	eblk->info.AR_CNTRL.PA_REJ = 0;
 
 
 
	memset(eblk->BD,  0, sizeof(eblk->BD));
	en302_set_filter(lp, NULL);
	memcpy(eblk->CET[0], dev->dev_addr, ETH_ALEN); 
 
	lp->open_time = jiffies;
 
	dev->tbusy = 0;
	dev->interrupt = 0;
	dev->start = 1;
	lp->tx_running = 1;
	en302_init(lp, 1);
	MOD_INC_USE_COUNT;
 
	return 0;
}
 
static int
net_send_packet(struct sk_buff *skb, struct device *dev)
{
	struct net_local *lp = (struct net_local *)dev->priv;
 
	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(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name,
			   tx_done(dev) ? "IRQ conflict" : "network cable problem");
		/* Try to restart the adaptor. */
		en302_stop(lp);
		en302_flush_rx_ring(lp);
		en302_flush_tx_ring(lp);
		en302_init(lp, 1);
 
		dev->tbusy=0;
		dev->trans_start = 0;
	}
	/*
	 * 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;
	}
 
 
	{
		short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
		unsigned char *buf = skb->data;
		unsigned long flags;
 
		save_flags(flags); cli();
		if (lp->tx_count < TX_QUEUE_SIZE)
		{
			lp->tx_skb[lp->tx_next] = skb;
			en302_send_packet(lp->eblk, buf, length, lp->tx_next);
			lp->tx_next = (lp->tx_next+1) & TX_QUEUE_MASK;
			if (++lp->tx_count == TX_QUEUE_SIZE)
			{
				set_bit(0, (void*)&dev->tbusy);
				dev->trans_start = jiffies;
			}
 
		}
 
		restore_flags(flags);
 
	}
 
	return 0;
}
 
/*
 * The typical workload of the driver:
 *   Handle the network interface interrupts.
 */
static void
net_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
	struct device *dev = (struct device *)(irq2dev_map[irq]);
	struct net_local *lp;
	int  status;
	struct _68EN302_ETHR_BLOCK* eblk;
 
	if (dev == NULL) {
		printk(KERN_WARNING "%s: irq %d for unknown device.\n", cardname, irq);
		return;
	}
	dev->interrupt = 1;
 
	lp = (struct net_local *)dev->priv;
	eblk = lp->eblk;
 
	status = eblk->info.INTR_EVENT;
 
	while(status)
	{
		eblk->info.INTR_EVENT |= status;
 
		if (status & ETHR_RFINT) {
			/* Got a packet(s). */
			net_rx(dev);
		}
		if (status & ETHR_TFINT) {
			net_tx(dev);
			dev->tbusy = 0;
			mark_bh(NET_BH);	/* Inform upper layers. */
		}
 
		if (status & ETHR_BSY)
		{
			if (!(status & ETHR_RFINT))
			{
				net_rx(dev);
			}
			else
			{
				en302_load_rx_ring(lp);
			}
		}
 
 
 
		if (status & ETHR_HBERR)
		{
		}
 
		if (status & ETHR_BABR)
		{
		}
 
		if (status & ETHR_BABT)
		{
		}
 
		if (status & ETHR_GRA)
		{
			eblk->info.ECNTRL.GTS = 0;
			lp->tx_running = 0;
		}
 
		if (status & ETHR_EBERR)
		{
 
			printk(KERN_WARNING "Ethernet buss error: BD %d\n", eblk->info.EDMA.BDERR);
		}
 
 
		status = eblk->info.INTR_EVENT;
 
 
	}
 
	dev->interrupt = 0;
	return;
}
 
static void dummy_call()
{
}
 
 
 
/* 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;
	struct _68EN302_ETHR_BLOCK* eblk = lp->eblk;
	int rxbd = 0;
	struct sk_buff *skb;
	int count = 0;
 
	for(rxbd = lp->rx_next;  count < RX_QUEUE_SIZE; rxbd = (rxbd + 1) & RX_QUEUE_MASK, count++)
	{
		struct  _68EN302_ETHR_RXBD* bd = &eblk->BD[RX_BD_START+rxbd].rx;
		union  _68EN302_ETHR_RXBD_FLAGS  flags;
		int bad = 0;
 
 
		skb = lp->rx_skb[rxbd];
 
		if (!skb)
		{
			skb = dev_alloc_skb(MAX_FRAME_SIZE);
 
			if (skb != NULL)
			{
				bd->length = MAX_FRAME_SIZE;
				bd->address.l = (unsigned long) skb->tail;
				bd->flags.s.I = 1;
				bd->flags.s.E = 1;
			}
 
			continue;
 
		}
 
 
		flags.w = bd->flags.w;
 
		if (flags.s.E)
		{
			break;
		}
 
		if (flags.s.SH)
		{
			bad = 1;
			lp->stats.rx_length_errors++;
		}
 
		if (flags.s.L)
		{
			if (flags.s.LG)
			{
				bad = 1;
				lp->stats.rx_length_errors++;
			}
 
			if (flags.s.NO)
			{
				bad = 1;
				lp->stats.rx_frame_errors++;
			}
 
			if (flags.s.CR)
			{
				bad = 1;
				lp->stats.rx_crc_errors++;
			}
 
			if (flags.s.OV)
			{
				bad = 1;
				lp->stats.rx_fifo_errors++;
			}
 
		}
 
		if (flags.s.CL)
		{
			bad = 1;
		}
 
 
 
		if (bad)
		{
			lp->stats.rx_errors++;
			bd->length = MAX_FRAME_SIZE;
			bd->address.l = (unsigned long) lp->rx_skb[rxbd]->tail;
			bd->flags.s.I = 1;
			bd->flags.s.E = 1;
 
			continue;
		}
 
		if (bd->length < 128) // small packet
		{
			struct sk_buff *nskb = dev_alloc_skb(bd->length);
			if (nskb)
			{
				memcpy(skb_put(nskb, bd->length), bd->address.l, bd->length);
				dummy_call();
				nskb->dev = dev;
				nskb->protocol = eth_type_trans(nskb,dev);
				netif_rx(nskb);
				lp->stats.rx_packets++;
			}
			else
			{
				lp->stats.rx_dropped++;
			}
		}
		else
		{
			skb->dev = dev;
			skb_put(skb, bd->length);
			dummy_call();
			skb->protocol = eth_type_trans(skb,dev);
			netif_rx(skb);
			lp->stats.rx_packets++;
 
			skb = dev_alloc_skb(MAX_FRAME_SIZE);
		}
 
		if (skb != NULL)
		{
			bd->length = MAX_FRAME_SIZE;
			bd->address.l = (unsigned long) skb->tail;
			bd->flags.s.I = 1;
			bd->flags.s.E = 1;
		}
		else
		{
			printk(KERN_WARNING "en302: can't get input packet buffer\n");
		}
 
		lp->rx_skb[rxbd] = skb;
 
 
	}
 
	lp->rx_next = rxbd;
 
 
 
 
}
 
 
static void 
net_tx(struct device *dev)
{
	struct net_local *lp = (struct net_local *)dev->priv;
	struct _68EN302_ETHR_BLOCK* eblk = lp->eblk;
	int txbd;
	int bad = 0;
	int count = 0;
 
	for(txbd = lp->tx_next_done;  count < TX_QUEUE_SIZE; txbd = (txbd + 1) & TX_QUEUE_MASK, count++)
	{
		struct  _68EN302_ETHR_TXBD* bd = &eblk->BD[TX_BD_START+txbd].tx;
		register union  _68EN302_ETHR_TXBD_FLAGS  flags;
 
		flags.w = bd->flags.w;
 
		if (!lp->tx_skb[txbd])
			break;
 
		if (flags.s.R)
			break;
 
		dev_kfree_skb (lp->tx_skb[txbd], FREE_WRITE);
		lp->tx_skb[txbd] = 0;
		lp->tx_count--;
 
		if (flags.s.CSL)
		{
			bad = 1;
			lp->stats.tx_carrier_errors++;
		}
 
		if (flags.s.UN)
		{
			bad = 1;
			lp->stats.tx_fifo_errors++;
		}
 
		if (flags.s.RL)
		{
			bad = 1;
			lp->stats.tx_aborted_errors++;
		}
 
		if (flags.s.HB)
		{
			bad = 1;
			lp->stats.tx_heartbeat_errors++;
		}
 
		if (flags.s.LC)
		{
			bad = 1;
			lp->stats.tx_window_errors++;
		}
 
		if (flags.w & 0x1C0)
		{
			bad = 1;
			lp->stats.tx_dropped++;
		}
 
		if (!bad)
			lp->stats.tx_packets++;
	}
 
	lp->tx_next_done = txbd;
 
}
 
/* The inverse routine to net_open(). */
static int
net_close(struct device *dev)
{
	struct net_local *lp = (struct net_local *)dev->priv;
 
 
	lp->open_time = 0;
 
	dev->tbusy = 1;
	dev->start = 0;
 
	en302_stop(lp);
	en302_flush_rx_ring(lp);
	en302_flush_tx_ring(lp);
 
 
	/* Update the statistics here. */
 
	MOD_DEC_USE_COUNT;
 
	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;
}
 
 
 
 
/*
 * 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)
{
	struct net_local* lp = (struct net_local*) dev->priv;
	struct _68EN302_ETHR_BLOCK* eblk = lp->eblk;
 
	en302_stop(lp);
	en302_flush_tx_ring(lp);
	en302_flush_rx_ring(lp);
 
	memcpy(eblk->CET[0], dev->dev_addr, ETH_ALEN); 
 
	if (dev->flags&IFF_PROMISC)
	{
		/* Enable promiscuous mode */
		eblk->info.AR_CNTRL.PROM = 1;
	}
	else if((dev->flags&IFF_ALLMULTI) || dev->mc_count > HW_MAX_ADDRS)
	{
 
		/* Disable promiscuous mode, use normal mode. */
		eblk->info.AR_CNTRL.PROM = 0;
		eblk->info.AR_CNTRL.MULT = 2;
		en302_set_filter(lp, NULL);
 
	}
	else if(dev->mc_count)
	{
		/* Walk the address list, and load the filter */
		en302_set_filter(lp, dev->mc_list);
	}
	else
	{
		eblk->info.AR_CNTRL.MULT = 0;
		en302_set_filter(lp, NULL);		
	}
 
	en302_init(lp, 1);
}
 
#ifdef MODULE
 
static char devicename[9] = { 0, };
static struct device this_device = {
	devicename, /* will be inserted by linux/drivers/net/net_init.c */
	0, 0, 0, 0,
	0, 0,  /* I/O address, IRQ */
	0, 0, 0, NULL, netcard_probe };
 
static int io = ECTRL0;
static int irq = 0;
static int dma = 0;
static int mem = 0;
 
int init_module(void)
{
	int result;
 
	if (io == 0)
		printk(KERN_WARNING "%s: You shouldn't use auto-probing with insmod!\n",
			   cardname);
 
	/* Copy the parameters from insmod into the device structure. */
	this_device.base_addr = io;
	this_device.irq       = irq;
	this_device.dma       = dma;
	this_device.mem_start = mem;
 
	if ((result = register_netdev(&this_device)) != 0)
		return result;
 
	return 0;
}
 
void
cleanup_module(void)
{
	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
	unregister_netdev(&this_device);
	/*
	 * If we don't do this, we can't re-insmod it later.
	 * Release irq/dma here, when you have jumpered versions and
	 * allocate them in net_probe1().
	 */
	/*
	   free_irq(this_device.irq, NULL);
	   free_dma(this_device.dma);
	*/
	release_region(this_device.base_addr, NETCARD_IO_EXTENT);
 
	if (this_device.priv)
		kfree_s(this_device.priv, sizeof(struct net_local));
}
 
#endif /* MODULE */
 
/*
 * Local variables:
 *  compile-command:
 *	gcc -D__KERNEL__ -Wall -Wstrict-prototypes -Wwrite-strings
 *	-Wredundant-decls -O2 -m486 -c skeleton.c
 *  version-control: t
 *  kept-new-versions: 5
 *  tab-width: 4
 *  c-indent-level: 4
 * End:
 */
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.