patch-2.0.31 linux/drivers/net/atp.c
Next file: linux/drivers/net/atp.h
Previous file: linux/drivers/net/Space.c
Back to the patch index
Back to the overall index
- Lines: 668
- Date:
Tue Aug 12 13:21:12 1997
- Orig file:
v2.0.30/linux/drivers/net/atp.c
- Orig date:
Thu Feb 29 21:50:43 1996
diff -u --recursive --new-file v2.0.30/linux/drivers/net/atp.c linux/drivers/net/atp.c
@@ -1,9 +1,9 @@
/* atp.c: Attached (pocket) ethernet adapter driver for linux. */
/*
- This is a driver for a commonly OEMed pocket (parallel port)
- ethernet adapter.
+ This is a driver for commonly OEMed pocket (parallel port)
+ ethernet adapters based on the Realtek RTL8002 and RTL8012 chips.
- Written 1993,1994,1995 by Donald Becker.
+ Written 1993-95,1997 by Donald Becker.
Copyright 1993 United States Government as represented by the
Director, National Security Agency.
@@ -19,7 +19,11 @@
*/
static const char *version =
- "atp.c:v1.01 1/18/95 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+ "atp.c:v1.08 4/1/97 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+/* Operational parameters that may be safely changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT ((400*HZ)/1000)
/*
This file is a device driver for the RealTek (aka AT-Lan-Tec) pocket
@@ -33,6 +37,10 @@
description is written based on guesses and writing lots of special-purpose
code to test my theorized operation.
+ In 1997 Realtek made available the documentation for the second generation
+ RTL8012 chip, which has lead to several driver improvements.
+ http://www.realtek.com.tw/cn/cn.html
+
Theory of Operation
The RTL8002 adapter seems to be built around a custom spin of the SEEQ
@@ -58,6 +66,10 @@
timing and control bits. The data is then read from status port or written
to the data port.
+ Correction: the controller has two banks of 16 registers. The second
+ bank contains only the multicast filter table (now used) and the EEPROM
+ access registers.
+
Since the bulk data transfer of the actual packets through the slow
parallel port dominates the driver's running time, four distinct data
(non-register) transfer modes are provided by the adapter, two in each
@@ -80,6 +92,14 @@
generate reasonable object code. This header file also documents my
interpretations of the device registers.
*/
+#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
#include <linux/kernel.h>
#include <linux/sched.h>
@@ -103,6 +123,49 @@
#include "atp.h"
+/* Kernel compatibility defines, common to David Hind's PCMCIA package.
+ This is only in the support-all-kernels source code. */
+#include <linux/version.h> /* Evil, but neccessary */
+
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10300
+#define RUN_AT(x) (x) /* What to put in timer->expires. */
+#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC)
+#define virt_to_bus(addr) ((unsigned long)addr)
+#define bus_to_virt(addr) ((void*)addr)
+
+#else /* 1.3.0 and later */
+#define RUN_AT(x) (jiffies + (x))
+#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
+#endif
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10338
+#ifdef MODULE
+#if !defined(CONFIG_MODVERSIONS) && !defined(__NO_VERSION__)
+char kernel_version[] = UTS_RELEASE;
+#endif
+#else
+#undef MOD_INC_USE_COUNT
+#define MOD_INC_USE_COUNT
+#undef MOD_DEC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+#endif /* 1.3.38 */
+
+#if (LINUX_VERSION_CODE >= 0x10344)
+#define NEW_MULTICAST
+#include <linux/delay.h>
+#endif
+
+#ifdef SA_SHIRQ
+#define FREE_IRQ(irqnum, dev) free_irq(irqnum, dev)
+#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n, instance)
+#define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs)
+#else
+#define FREE_IRQ(irqnum, dev) free_irq(irqnum)
+#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n)
+#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs)
+#endif
+/* End of kernel compatibility defines. */
+
/* use 0 for production, 1 for verification, >2 for debug */
#ifndef NET_DEBUG
#define NET_DEBUG 1
@@ -112,6 +175,9 @@
/* The number of low I/O ports used by the ethercard. */
#define ETHERCARD_TOTAL_SIZE 3
+/* Sequence to switch an 8012 from printer mux to ethernet mode. */
+static char mux_8012[] = { 0xff, 0xf7, 0xff, 0xfb, 0xf3, 0xfb, 0xff, 0xf7,};
+
/* This code, written by wwc@super.org, resets the adapter every
TIMED_CHECKER ticks. This recovers from an unknown error which
hangs the device. */
@@ -119,13 +185,11 @@
#ifdef TIMED_CHECKER
#include <linux/timer.h>
static void atp_timed_checker(unsigned long ignored);
-static struct device *atp_timed_dev;
-static struct timer_list atp_timer = {NULL, NULL, 0, 0, atp_timed_checker};
#endif
/* Index to functions, as function prototypes. */
-extern int atp_probe(struct device *dev);
+extern int atp_init(struct device *dev);
static int atp_probe1(struct device *dev, short ioaddr);
static void get_node_ID(struct device *dev);
@@ -135,14 +199,23 @@
static void write_packet(short ioaddr, int length, unsigned char *packet, int mode);
static void trigger_send(short ioaddr, int length);
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_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs);
static void net_rx(struct device *dev);
static void read_block(short ioaddr, int length, unsigned char *buffer, int data_mode);
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);
+#ifdef NEW_MULTICAST
+static void set_rx_mode_8002(struct device *dev);
+static void set_rx_mode_8012(struct device *dev);
+#else
+static void set_rx_mode_8002(struct device *dev, int num_addrs, void *addrs);
+static void set_rx_mode_8012(struct device *dev, int num_addrs, void *addrs);
+#endif
+/* A list of all installed ATP devices, for removing the driver module. */
+static struct device *root_atp_dev = NULL;
+
/* Check for a network adapter 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.
@@ -153,7 +226,7 @@
atp_init(struct device *dev)
{
int *port, ports[] = {0x378, 0x278, 0x3bc, 0};
- int base_addr = dev->base_addr;
+ int base_addr = dev ? dev->base_addr : 0;
if (base_addr > 0x1ff) /* Check a single specified location. */
return atp_probe1(dev, base_addr);
@@ -174,7 +247,8 @@
static int atp_probe1(struct device *dev, short ioaddr)
{
- int saved_ctrl_reg, status;
+ struct net_local *lp;
+ int saved_ctrl_reg, status, i;
outb(0xff, ioaddr + PAR_DATA);
/* Save the original value of the Control register, in case we guessed
@@ -182,6 +256,9 @@
saved_ctrl_reg = inb(ioaddr + PAR_CONTROL);
/* IRQEN=0, SLCTB=high INITB=high, AUTOFDB=high, STBB=high. */
outb(0x04, ioaddr + PAR_CONTROL);
+ /* Turn off the printer multiplexer on the 8012. */
+ for (i = 0; i < 8; i++)
+ outb(mux_8012[i], ioaddr + PAR_DATA);
write_reg_high(ioaddr, CMR1, CMR1h_RESET);
eeprom_delay(2048);
status = read_nibble(ioaddr, CMR1);
@@ -196,6 +273,9 @@
outb(saved_ctrl_reg, ioaddr + PAR_CONTROL);
return 1;
}
+
+ dev = init_etherdev(dev, sizeof(struct net_local));
+
/* Find the IRQ used by triggering an interrupt. */
write_reg_byte(ioaddr, CMR2, 0x01); /* No accept mode, IRQ out. */
write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE); /* Enable Tx and Rx. */
@@ -218,27 +298,29 @@
dev->irq, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
- /* Leave the hardware in a reset state. */
- write_reg_high(ioaddr, CMR1, CMR1h_RESET);
+ /* Reset the ethernet hardware and activate the printer pass-through. */
+ write_reg_high(ioaddr, CMR1, CMR1h_RESET | CMR1h_MUX);
if (net_debug)
printk(version);
/* Initialize the device structure. */
ether_setup(dev);
- dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+ 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));
+ lp = (struct net_local *)dev->priv;
+ lp->chip_type = RTL8002;
+ lp->addr_mode = CMR2h_Normal;
- {
- struct net_local *lp = (struct net_local *)dev->priv;
- lp->addr_mode = CMR2h_Normal;
- }
+ lp->next_module = root_atp_dev;
+ root_atp_dev = dev;
/* For the ATP adapter the "if_port" is really the data transfer mode. */
- dev->if_port = (dev->mem_start & 0xf) ? dev->mem_start & 0x7 : 4;
+ dev->if_port = (dev->mem_start & 0xf) ? (dev->mem_start & 0x7) : 4;
if (dev->mem_end & 0xf)
net_debug = dev->mem_end & 7;
@@ -246,14 +328,9 @@
dev->stop = net_close;
dev->hard_start_xmit = net_send_packet;
dev->get_stats = net_get_stats;
- dev->set_multicast_list = &set_multicast_list;
+ dev->set_multicast_list =
+ lp->chip_type == RTL8002 ? &set_rx_mode_8002 : &set_rx_mode_8012;
-#ifdef TIMED_CHECKER
- del_timer(&atp_timer);
- atp_timer.expires = jiffies + TIMED_CHECKER;
- atp_timed_dev = dev;
- add_timer(&atp_timer);
-#endif
return 0;
}
@@ -322,18 +399,32 @@
*/
static int net_open(struct device *dev)
{
+ struct net_local *lp = (struct net_local *)dev->priv;
/* The interrupt line is turned off (tri-stated) when the device isn't in
use. That's especially important for "attached" interfaces where the
port or interrupt may be shared. */
+#ifndef SA_SHIRQ
if (irq2dev_map[dev->irq] != 0
|| (irq2dev_map[dev->irq] = dev) == 0
- || request_irq(dev->irq, &net_interrupt, 0, "ATP", NULL)) {
+ || REQUEST_IRQ(dev->irq, &net_interrupt, 0, "ATP", dev)) {
return -EAGAIN;
}
+#else
+ if (request_irq(dev->irq, &net_interrupt, 0, "ATP Ethernet", dev))
+ return -EAGAIN;
+#endif
+ MOD_INC_USE_COUNT;
hardware_init(dev);
dev->start = 1;
+
+ init_timer(&lp->timer);
+ lp->timer.expires = RUN_AT(TIMED_CHECKER);
+ lp->timer.data = (unsigned long)dev;
+ lp->timer.function = &atp_timed_checker; /* timer handler */
+ add_timer(&lp->timer);
+
return 0;
}
@@ -345,6 +436,9 @@
int ioaddr = dev->base_addr;
int i;
+ /* Turn off the printer multiplexer on the 8012. */
+ for (i = 0; i < 8; i++)
+ outb(mux_8012[i], ioaddr + PAR_DATA);
write_reg_high(ioaddr, CMR1, CMR1h_RESET);
for (i = 0; i < 6; i++)
@@ -418,11 +512,18 @@
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)
+#ifndef final_version
+ if (skb == NULL || skb->len <= 0) {
+ printk("%s: Obsolete driver layer request made: skbuff==NULL.\n",
+ dev->name);
+ dev_tint(dev);
+ return 0;
+ }
+#endif
+
+ /* Use transmit-while-tbusy as a crude error timer. */
+ if (set_bit(0, (void*)&dev->tbusy) != 0) {
+ if (jiffies - dev->trans_start < TX_TIMEOUT)
return 1;
printk("%s: transmit timed out, %s?\n", dev->name,
inb(ioaddr + PAR_CONTROL) & 0x10 ? "network cable problem"
@@ -432,21 +533,8 @@
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;
- }
-
- /* 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 {
+ return 1;
+ } else {
short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
unsigned char *buf = skb->data;
int flags;
@@ -484,9 +572,13 @@
/* The typical workload of the driver:
Handle the network interface interrupts. */
static void
-net_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+net_interrupt IRQ(int irq, void *dev_instance, struct pt_regs * regs)
{
+#ifdef SA_SHIRQ
+ struct device *dev = (struct device *)dev_instance;
+#else
struct device *dev = (struct device *)(irq2dev_map[irq]);
+#endif
struct net_local *lp;
int ioaddr, status, boguscount = 20;
static int num_tx_since_rx = 0;
@@ -585,11 +677,6 @@
int i;
for (i = 0; i < 6; i++)
write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]);
-#ifdef TIMED_CHECKER
- del_timer(&atp_timer);
- atp_timer.expires = jiffies + TIMED_CHECKER;
- add_timer(&atp_timer);
-#endif
}
/* Tell the adapter that it can go back to using the output line as IRQ. */
@@ -610,17 +697,23 @@
#ifdef TIMED_CHECKER
/* This following code fixes a rare (and very difficult to track down)
problem where the adapter forgets its ethernet address. */
-static void atp_timed_checker(unsigned long ignored)
+static void atp_timed_checker(unsigned long data)
{
- int i;
- int ioaddr = atp_timed_dev->base_addr;
+ struct device *dev = (struct device *)data;
+ int ioaddr = dev->base_addr;
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int tickssofar = jiffies - lp->last_rx_time;
+ int i;
- if (!atp_timed_dev->interrupt)
- {
- for (i = 0; i < 6; i++)
-#if 0
- if (read_cmd_byte(ioaddr, PAR0 + i) != atp_timed_dev->dev_addr[i])
- {
+ if (tickssofar > 2*HZ && dev->interrupt == 0) {
+#if 1
+ for (i = 0; i < 6; i++)
+ write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]);
+ lp->last_rx_time = jiffies;
+#else
+ for (i = 0; i < 6; i++)
+ if (read_cmd_byte(ioaddr, PAR0 + i) != atp_timed_dev->dev_addr[i])
+ {
struct net_local *lp = (struct net_local *)atp_timed_dev->priv;
write_reg_byte(ioaddr, PAR0 + i, atp_timed_dev->dev_addr[i]);
if (i == 2)
@@ -632,13 +725,10 @@
else
lp->stats.rx_errors++;
}
-#else
- write_reg_byte(ioaddr, PAR0 + i, atp_timed_dev->dev_addr[i]);
#endif
}
- del_timer(&atp_timer);
- atp_timer.expires = jiffies + TIMED_CHECKER;
- add_timer(&atp_timer);
+ lp->timer.expires = RUN_AT(TIMED_CHECKER);
+ add_timer(&lp->timer);
}
#endif
@@ -647,11 +737,7 @@
{
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);
@@ -661,18 +747,23 @@
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 (rx_head.rx_status & 0x0004) lp->stats.rx_frame_errors++;
+ else if (rx_head.rx_status & 0x0002) lp->stats.rx_crc_errors++;
if (net_debug > 3) printk("%s: Unknown ATP Rx error %04x.\n",
dev->name, rx_head.rx_status);
- hardware_init(dev);
+ if (rx_head.rx_status & 0x0020) {
+ lp->stats.rx_fifo_errors++;
+ write_reg_high(ioaddr, CMR1, CMR1h_TxENABLE);
+ write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE);
+ } else if (rx_head.rx_status & 0x0050)
+ 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). */
+ /* Malloc up new buffer. The "-4" is omits the FCS (CRC). */
+ int pkt_len = (rx_head.rx_count & 0x7ff) - 4;
struct sk_buff *skb;
- skb = dev_alloc_skb(pkt_len);
+ skb = DEV_ALLOC_SKB(pkt_len + 2);
if (skb == NULL) {
printk("%s: Memory squeeze, dropping packet.\n", dev->name);
lp->stats.rx_dropped++;
@@ -680,23 +771,20 @@
}
skb->dev = dev;
+#if LINUX_VERSION_CODE >= 0x10300
+ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
read_block(ioaddr, pkt_len, skb_put(skb,pkt_len), 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]);
- }
-
- skb->protocol=eth_type_trans(skb,dev);
+ skb->protocol = eth_type_trans(skb, dev);
+#else
+ read_block(ioaddr, pkt_len, skb->data, dev->if_port);
+ skb->len = pkt_len;
+#endif
netif_rx(skb);
lp->stats.rx_packets++;
}
done:
write_reg(ioaddr, CMR1, CMR1_NextPkt);
+ lp->last_rx_time = jiffies;
return;
}
@@ -730,17 +818,23 @@
dev->tbusy = 1;
dev->start = 0;
+ del_timer(&lp->timer);
+
/* 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, NULL);
+ FREE_IRQ(dev->irq, dev);
+#ifndef SA_SHIRQ
irq2dev_map[dev->irq] = 0;
+#endif
+
+ /* Reset the ethernet hardware and activate the printer pass-through. */
+ write_reg_high(ioaddr, CMR1, CMR1h_RESET | CMR1h_MUX);
- /* Leave the hardware in a reset state. */
- write_reg_high(ioaddr, CMR1, CMR1h_RESET);
+ MOD_DEC_USE_COUNT;
return 0;
}
@@ -757,29 +851,125 @@
/*
* Set or clear the multicast filter for this adapter.
*/
-
-static void set_multicast_list(struct device *dev)
+
+/* The little-endian AUTODIN32 ethernet CRC calculation.
+ This is common code and should be moved to net/core/crc.c */
+static unsigned const ethernet_polynomial_le = 0xedb88320U;
+static inline unsigned ether_crc_le(int length, unsigned char *data)
+{
+ unsigned int crc = 0xffffffff; /* Initial value. */
+ while(--length >= 0) {
+ unsigned char current_octet = *data++;
+ int bit;
+ for (bit = 8; --bit >= 0; current_octet >>= 1) {
+ if ((crc ^ current_octet) & 1) {
+ crc >>= 1;
+ crc ^= ethernet_polynomial_le;
+ } else
+ crc >>= 1;
+ }
+ }
+ return crc;
+}
+
+static void
+#ifdef NEW_MULTICAST
+set_rx_mode_8002(struct device *dev)
+#else
+static void set_rx_mode_8002(struct device *dev, int num_addrs, void *addrs);
+#endif
{
struct net_local *lp = (struct net_local *)dev->priv;
short ioaddr = dev->base_addr;
- int num_addrs=dev->mc_list;
-
- if(dev->flags&(IFF_ALLMULTI|IFF_PROMISC))
- num_addrs=1;
- /*
- * We must make the kernel realise we had to move
- * into promisc mode or we start all out war on
- * the cable. - AC
- */
- if(num_addrs)
- dev->flags|=IFF_PROMISC;
- lp->addr_mode = num_addrs ? CMR2h_PROMISC : CMR2h_Normal;
+
+ if ( dev->mc_count > 0 || (dev->flags & (IFF_ALLMULTI|IFF_PROMISC))) {
+ /* We must make the kernel realise we had to move
+ * into promisc mode or we start all out war on
+ * the cable. - AC
+ */
+ dev->flags|=IFF_PROMISC;
+ lp->addr_mode = CMR2h_PROMISC;
+ } else
+ lp->addr_mode = CMR2h_Normal;
write_reg_high(ioaddr, CMR2, lp->addr_mode);
}
+
+static void
+#ifdef NEW_MULTICAST
+set_rx_mode_8012(struct device *dev)
+#else
+static void set_rx_mode_8012(struct device *dev, int num_addrs, void *addrs);
+#endif
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+ unsigned char new_mode, mc_filter[8]; /* Multicast hash filter */
+ int i;
+
+ if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
+ new_mode = CMR2h_PROMISC;
+ } else if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) {
+ /* Too many to filter perfectly -- accept all multicasts. */
+ memset(mc_filter, 0xff, sizeof(mc_filter));
+ new_mode = CMR2h_Normal;
+ } else {
+ struct dev_mc_list *mclist;
+
+ memset(mc_filter, 0, sizeof(mc_filter));
+ for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+ i++, mclist = mclist->next)
+ set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f,
+ mc_filter);
+ new_mode = CMR2h_Normal;
+ }
+ lp->addr_mode = new_mode;
+ write_reg(ioaddr, CMR2, CMR2_IRQOUT | 0x04); /* Switch to page 1. */
+ for (i = 0; i < 8; i++)
+ write_reg_byte(ioaddr, i, mc_filter[i]);
+ if (net_debug > 2 || 1) {
+ lp->addr_mode = 1;
+ printk("%s: Mode %d, setting multicast filter to",
+ dev->name, lp->addr_mode);
+ for (i = 0; i < 8; i++)
+ printk(" %2.2x", mc_filter[i]);
+ printk(".\n");
+ }
+
+ write_reg_high(ioaddr, CMR2, lp->addr_mode);
+ write_reg(ioaddr, CMR2, CMR2_IRQOUT); /* Switch back to page 0 */
+}
+
+#ifdef MODULE
+static int debug = 1;
+int
+init_module(void)
+{
+ net_debug = debug;
+ root_atp_dev = NULL;
+ atp_init(0);
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ struct device *next_dev;
+
+ /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+ /* No need to release_region(), since we never snarf it. */
+ while (root_atp_dev) {
+ next_dev = ((struct net_local *)root_atp_dev->priv)->next_module;
+ unregister_netdev(root_atp_dev);
+ kfree(root_atp_dev);
+ root_atp_dev = next_dev;
+ }
+}
+#endif /* MODULE */
+
/*
* Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c atp.c"
+ * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c atp.c"
* version-control: t
* kept-new-versions: 5
* tab-width: 4
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov