patch-2.3.50 linux/drivers/net/cs89x0.c
Next file: linux/drivers/net/cs89x0.h
Previous file: linux/drivers/net/cops_ltdrv.h
Back to the patch index
Back to the overall index
- Lines: 1386
- Date:
Fri Mar 3 12:56:13 2000
- Orig file:
v2.3.49/linux/drivers/net/cs89x0.c
- Orig date:
Sun Feb 20 21:12:39 2000
diff -u --recursive --new-file v2.3.49/linux/drivers/net/cs89x0.c linux/drivers/net/cs89x0.c
@@ -1,4 +1,7 @@
-/* cs89x0.c: A Crystal Semiconductor CS89[02]0 driver for linux. */
+/* cs89x0.c: A Crystal Semiconductor (Now Cirrus Logic) CS89[02]0
+ * driver for linux.
+ */
+
/*
Written 1996 by Russell Nelson, with reference to skeleton.c
written 1993-1994 by Donald Becker.
@@ -6,8 +9,8 @@
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 at nelson@crynwr.com, Crynwr
- Software, 11 Grant St., Potsdam, NY 13676
+ The author may be reached at nelson@crynwr.com, Crynwr
+ Software, 521 Pleasant Valley Rd., Potsdam, NY 13676
Changelog:
@@ -25,19 +28,30 @@
: as an example. Disabled autoprobing in init_module(),
: not a good thing to do to other devices while Linux
: is running from all accounts.
-
+
+ Russ Nelson : Jul 13 1998. Added RxOnly DMA support.
+
+ Melody Lee : Aug 10 1999. Changes for Linux 2.2.5 compatibility.
+ : email: ethernet@crystal.cirrus.com
+
Alan Cox : Removed 1.2 support, added 2.1 extra counters.
+
+ Andrew Morton : andrewm@uow.edu.au
+ : Kernel 2.3.48
+ : Handle kmalloc() failures
+ : Other resource allocation fixes
+ : Add SMP locks
+ : Integrate Russ Nelson's ALLOW_DMA functionality back in.
+ : If ALLOW_DMA is true, make DMA runtime selectable
+ : Folded in changes from Cirrus (Melody Lee
+ : <klee@crystal.cirrus.com>)
+ : Don't call netif_wake_queue() in net_send_packet()
+ : Fixed an out-of-mem bug in dma_rx()
+ : Updated Documentation/cs89x0.txt
*/
static char *version =
-"cs89x0.c:v1.03 11/26/96 Russell Nelson <nelson@crynwr.com>\n";
-
-/* ======================= configure the driver here ======================= */
-
-/* use 0 for production, 1 for verification, >2 for debug */
-#ifndef NET_DEBUG
-#define NET_DEBUG 2
-#endif
+"cs89x0.c: (kernel 2.3.48) Russell Nelson <nelson@crynwr.com>, Andrew Morton <andrewm@uow.edu.au>\n";
/* ======================= end of configuration ======================= */
@@ -52,7 +66,19 @@
#define MOD_DEC_USE_COUNT
#endif
-#define PRINTK(x) printk x
+/*
+ * Set this to zero to disable DMA code
+ *
+ * Note that even if DMA is turned off we still support the 'dma' and 'use_dma'
+ * module options so we don't break any startup scripts.
+ */
+#define ALLOW_DMA 1
+
+/*
+ * Set this to zero to remove all the debug statements via
+ * dead code elimination
+ */
+#define DEBUGGING 0
/*
Sources:
@@ -76,39 +102,66 @@
#include <asm/system.h>
#include <asm/bitops.h>
#include <asm/io.h>
+#if ALLOW_DMA
+#include <asm/dma.h>
+#endif
#include <linux/errno.h>
#include <linux/init.h>
+#include <linux/spinlock.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
+
#include "cs89x0.h"
/* 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[] __initdata =
- { 0x300, 0x320, 0x340, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0};
+ { 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0};
-static unsigned int net_debug = NET_DEBUG;
+#if DEBUGGING
+static unsigned int net_debug = 5;
+#else
+#define net_debug 0 /* gcc will remove all the debug code for us */
+#endif
/* The number of low I/O ports used by the ethercard. */
#define NETCARD_IO_EXTENT 16
+/* we allow the user to override various values normally set in the EEPROM */
+#define FORCE_RJ45 0x0001 /* pick one of these three */
+#define FORCE_AUI 0x0002
+#define FORCE_BNC 0x0004
+
+#define FORCE_AUTO 0x0010 /* pick one of these three */
+#define FORCE_HALF 0x0020
+#define FORCE_FULL 0x0030
+
/* Information that need to be kept for each board. */
struct net_local {
struct net_device_stats stats;
int chip_type; /* one of: CS8900, CS8920, CS8920M */
char chip_revision; /* revision letter of the chip ('A'...) */
- int send_cmd; /* the propercommand used to send a packet. */
- int auto_neg_cnf;
- int adapter_cnf;
- int isa_config;
- int irq_map;
- int rx_mode;
- int curr_rx_cfg;
- int linectl;
- int send_underrun; /* keep track of how many underruns in a row we get */
- struct sk_buff *skb;
+ int send_cmd; /* the proper send command: TX_NOW, TX_AFTER_381, or TX_AFTER_ALL */
+ int auto_neg_cnf; /* auto-negotiation word from EEPROM */
+ int adapter_cnf; /* adapter configuration from EEPROM */
+ int isa_config; /* ISA configuration from EEPROM */
+ int irq_map; /* IRQ map from EEPROM */
+ int rx_mode; /* what mode are we in? 0, RX_MULTCAST_ACCEPT, or RX_ALL_ACCEPT */
+ int curr_rx_cfg; /* a copy of PP_RxCFG */
+ int linectl; /* either 0 or LOW_RX_SQUELCH, depending on configuration. */
+ int send_underrun; /* keep track of how many underruns in a row we get */
+ int force; /* force various values; see FORCE* above. */
+ spinlock_t lock;
+#if ALLOW_DMA
+ int use_dma; /* Flag: we're using dma */
+ int dma; /* DMA channel */
+ int dmasize; /* 16 or 64 */
+ unsigned char *dma_buff; /* points to the beginning of the buffer */
+ unsigned char *end_dma_buff; /* points to the end of the buffer */
+ unsigned char *rx_dma_ptr; /* points to the next packet */
+#endif
};
/* Index to functions, as function prototypes. */
@@ -117,7 +170,7 @@
static int cs89x0_probe1(struct net_device *dev, int ioaddr);
static int net_open(struct net_device *dev);
-static int net_send_packet(struct sk_buff *skb, struct net_device *dev);
+static int net_send_packet(struct sk_buff *skb, struct net_device *dev);
static void net_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static void set_multicast_list(struct net_device *dev);
static void net_timeout(struct net_device *dev);
@@ -128,7 +181,11 @@
static int get_eeprom_data(struct net_device *dev, int off, int len, int *buffer);
static int get_eeprom_cksum(int off, int len, int *buffer);
static int set_mac_address(struct net_device *dev, void *addr);
-
+static void count_rx_errors(int status, struct net_local *lp);
+#if ALLOW_DMA
+static void get_dma_channel(struct net_device *dev);
+static void release_dma_buff(struct net_local *lp);
+#endif
/* Example routines you must write ;->. */
#define tx_done(dev) 1
@@ -139,6 +196,7 @@
If dev->base_addr == 1, always return failure.
If dev->base_addr == 2, allocate space for the device and return success
(detachable devices only).
+ Return 0 on success.
*/
int __init cs89x0_probe(struct net_device *dev)
@@ -146,6 +204,9 @@
int i;
int base_addr = dev ? dev->base_addr : 0;
+ if (net_debug)
+ printk("cs89x0:cs89x0_probe()\n");
+
if (base_addr > 0x1ff) /* Check a single specified location. */
return cs89x0_probe1(dev, base_addr);
else if (base_addr != 0) /* Don't probe at all. */
@@ -158,34 +219,38 @@
if (cs89x0_probe1(dev, ioaddr) == 0)
return 0;
}
- printk("cs89x0: no cs8900 or cs8920 detected. Be sure to disable PnP with SETUP\n");
+ printk(KERN_WARNING "cs89x0: no cs8900 or cs8920 detected. Be sure to disable PnP with SETUP\n");
return ENODEV;
}
-extern int inline readreg(struct net_device *dev, int portno)
+extern int inline
+readreg(struct net_device *dev, int portno)
{
outw(portno, dev->base_addr + ADD_PORT);
return inw(dev->base_addr + DATA_PORT);
}
-extern void inline writereg(struct net_device *dev, int portno, int value)
+extern void inline
+writereg(struct net_device *dev, int portno, int value)
{
outw(portno, dev->base_addr + ADD_PORT);
outw(value, dev->base_addr + DATA_PORT);
}
-
-extern int inline readword(struct net_device *dev, int portno)
+extern int inline
+readword(struct net_device *dev, int portno)
{
return inw(dev->base_addr + portno);
}
-extern void inline writeword(struct net_device *dev, int portno, int value)
+extern void inline
+writeword(struct net_device *dev, int portno, int value)
{
outw(value, dev->base_addr + portno);
}
-static int __init wait_eeprom_ready(struct net_device *dev)
+static int __init
+wait_eeprom_ready(struct net_device *dev)
{
int timeout = jiffies;
/* check to see if the EEPROM is ready, a timeout is used -
@@ -197,7 +262,8 @@
return 0;
}
-static int __init get_eeprom_data(struct net_device *dev, int off, int len, int *buffer)
+static int __init
+get_eeprom_data(struct net_device *dev, int off, int len, int *buffer)
{
int i;
@@ -214,7 +280,8 @@
return 0;
}
-static int __init get_eeprom_cksum(int off, int len, int *buffer)
+static int __init
+get_eeprom_cksum(int off, int len, int *buffer)
{
int i, cksum;
@@ -229,19 +296,28 @@
/* 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. */
+ verifies that the correct device exists and functions.
+ Return 0 on success.
+ */
-static int __init cs89x0_probe1(struct net_device *dev, int ioaddr)
+static int __init
+cs89x0_probe1(struct net_device *dev, int ioaddr)
{
struct net_local *lp;
static unsigned version_printed = 0;
int i;
unsigned rev_type = 0;
int eeprom_buff[CHKSUM_LEN];
+ int retval;
/* Initialize the device structure. */
if (dev->priv == NULL) {
dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+ if (dev->priv == 0)
+ {
+ retval = ENOMEM;
+ goto out;
+ }
memset(dev->priv, 0, sizeof(struct net_local));
}
lp = (struct net_local *)dev->priv;
@@ -257,7 +333,10 @@
}
if (inw(ioaddr + DATA_PORT) != CHIP_EISA_ID_SIG)
- return ENODEV;
+ {
+ retval = ENODEV;
+ goto out1;
+ }
/* Fill in the 'dev' fields. */
dev->base_addr = ioaddr;
@@ -278,7 +357,7 @@
if (net_debug && version_printed++ == 0)
printk(version);
- printk("%s: cs89%c0%s rev %c found at %#3lx",
+ printk(KERN_INFO "%s: cs89%c0%s rev %c found at %#3lx ",
dev->name,
lp->chip_type==CS8900?'0':'2',
lp->chip_type==CS8920M?"M":"",
@@ -289,11 +368,11 @@
/* First check to see if an EEPROM is attached*/
if ((readreg(dev, PP_SelfST) & EEPROM_PRESENT) == 0)
- printk("\ncs89x0: No EEPROM, relying on command line....\n");
+ printk(KERN_WARNING "\ncs89x0: No EEPROM, relying on command line....\n");
else if (get_eeprom_data(dev, START_EEPROM_DATA,CHKSUM_LEN,eeprom_buff) < 0) {
- printk("\ncs89x0: EEPROM read failed, relying on command line.\n");
+ printk(KERN_WARNING "\ncs89x0: EEPROM read failed, relying on command line.\n");
} else if (get_eeprom_cksum(START_EEPROM_DATA,CHKSUM_LEN,eeprom_buff) < 0) {
- printk("\ncs89x0: EEPROM checksum bad, relying on command line\n");
+ printk(KERN_WARNING "\ncs89x0: EEPROM checksum bad, relying on command line\n");
} else {
/* get transmission control word but keep the autonegotiation bits */
if (!lp->auto_neg_cnf) lp->auto_neg_cnf = eeprom_buff[AUTO_NEG_CNF_OFFSET/2];
@@ -301,16 +380,36 @@
if (!lp->adapter_cnf) lp->adapter_cnf = eeprom_buff[ADAPTER_CNF_OFFSET/2];
/* Store ISA configuration */
lp->isa_config = eeprom_buff[ISA_CNF_OFFSET/2];
- /* store the initial memory base address */
dev->mem_start = eeprom_buff[PACKET_PAGE_OFFSET/2] << 8;
+
+ /* eeprom_buff has 32-bit ints, so we can't just memcpy it */
+ /* store the initial memory base address */
for (i = 0; i < ETH_ALEN/2; i++) {
dev->dev_addr[i*2] = eeprom_buff[i];
dev->dev_addr[i*2+1] = eeprom_buff[i] >> 8;
}
}
+ /* allow them to force multiple transceivers. If they force multiple, autosense */
+ {
+ int count = 0;
+ if (lp->force & FORCE_RJ45) {lp->adapter_cnf |= A_CNF_10B_T; count++; }
+ if (lp->force & FORCE_AUI) {lp->adapter_cnf |= A_CNF_AUI; count++; }
+ if (lp->force & FORCE_BNC) {lp->adapter_cnf |= A_CNF_10B_2; count++; }
+ if (count > 1) {lp->adapter_cnf |= A_CNF_MEDIA_AUTO; }
+ else if (lp->force & FORCE_RJ45){lp->adapter_cnf |= A_CNF_MEDIA_10B_T; }
+ else if (lp->force & FORCE_AUI) {lp->adapter_cnf |= A_CNF_MEDIA_AUI; }
+ else if (lp->force & FORCE_BNC) {lp->adapter_cnf |= A_CNF_MEDIA_10B_2; }
+ }
+
+ /* FIXME: We don't let you set dc-dc polarity or low RX squelch from the command line: add it here */
+
+ /* FIXME: We don't let you set the IMM bit from the command line: add it to lp->auto_neg_cnf here */
- printk(" media %s%s%s",
+ /* FIXME: we don't set the Ethernet address on the command line. Use
+ ifconfig IFACE hw ether AABBCCDDEEFF */
+
+ printk(KERN_INFO "cs89x0 media %s%s%s",
(lp->adapter_cnf & A_CNF_10B_T)?"RJ-45,":"",
(lp->adapter_cnf & A_CNF_AUI)?"AUI,":"",
(lp->adapter_cnf & A_CNF_10B_2)?"BNC,":"");
@@ -353,14 +452,33 @@
dev->irq = i;
}
- printk(" IRQ %d", dev->irq);
+ printk(", IRQ %d", dev->irq);
+#if ALLOW_DMA
+ if (lp->use_dma)
+ {
+ get_dma_channel(dev);
+ printk(", DMA %d", dev->dma);
+ }
+ else
+#endif
+ {
+ printk(", programmed I/O");
+ }
/* print the ethernet address. */
+ printk(", MAC ");
for (i = 0; i < ETH_ALEN; i++)
- printk(" %2.2x", dev->dev_addr[i]);
+ {
+ printk("%s%02x", i ? ":" : "", dev->dev_addr[i]);
+ }
/* Grab the region so we can find another board if autoIRQ fails. */
+
+ /*
+ * FIXME: we should check this, but really the isapnp stuff should have given
+ * us a free region. Sort this out when the isapnp is sorted out
+ */
request_region(ioaddr, NETCARD_IO_EXTENT,"cs89x0");
dev->open = net_open;
@@ -376,11 +494,188 @@
ether_setup(dev);
printk("\n");
+ if (net_debug)
+ printk("cs89x0_probe1() successful\n");
return 0;
+out1:
+ kfree(dev->priv);
+ dev->priv = 0;
+out:
+ return retval;
+}
+
+
+/*********************************
+ * This page contains DMA routines
+**********************************/
+
+#if ALLOW_DMA
+
+#define dma_page_eq(ptr1, ptr2) ((long)(ptr1)>>17 == (long)(ptr2)>>17)
+
+static void
+get_dma_channel(struct net_device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+
+ if (lp->dma) {
+ dev->dma = lp->dma;
+ lp->isa_config |= ISA_RxDMA;
+ } else {
+ if ((lp->isa_config & ANY_ISA_DMA) == 0)
+ return;
+ dev->dma = lp->isa_config & DMA_NO_MASK;
+ if (lp->chip_type == CS8900)
+ dev->dma += 5;
+ if (dev->dma < 5 || dev->dma > 7) {
+ lp->isa_config &= ~ANY_ISA_DMA;
+ return;
+ }
+ }
+ return;
+}
+
+static void
+write_dma(struct net_device *dev, int chip_type, int dma)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ if ((lp->isa_config & ANY_ISA_DMA) == 0)
+ return;
+ if (chip_type == CS8900) {
+ writereg(dev, PP_CS8900_ISADMA, dma-5);
+ } else {
+ writereg(dev, PP_CS8920_ISADMA, dma);
+ }
+}
+
+static void
+set_dma_cfg(struct net_device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+
+ if (lp->use_dma)
+ {
+ if ((lp->isa_config & ANY_ISA_DMA) == 0)
+ {
+ if (net_debug > 3)
+ printk("set_dma_cfg(): no DMA\n");
+ return;
+ }
+ if (lp->isa_config & ISA_RxDMA)
+ {
+ lp->curr_rx_cfg |= RX_DMA_ONLY;
+ if (net_debug > 3)
+ printk("set_dma_cfg(): RX_DMA_ONLY\n");
+ }
+ else
+ {
+ lp->curr_rx_cfg |= AUTO_RX_DMA; /* not that we support it... */
+ if (net_debug > 3)
+ printk("set_dma_cfg(): AUTO_RX_DMA\n");
+ }
+ }
+}
+
+static int
+dma_bufcfg(struct net_device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ if (lp->use_dma)
+ return (lp->isa_config & ANY_ISA_DMA)? RX_DMA_ENBL : 0;
+ else
+ return 0;
+}
+
+static int
+dma_busctl(struct net_device *dev)
+{
+ int retval = 0;
+ struct net_local *lp = (struct net_local *)dev->priv;
+ if (lp->use_dma)
+ {
+ if (lp->isa_config & ANY_ISA_DMA)
+ retval |= RESET_RX_DMA; /* Reset the DMA pointer */
+ if (lp->isa_config & DMA_BURST)
+ retval |= DMA_BURST_MODE; /* Does ISA config specify DMA burst ? */
+ if (lp->dmasize == 64)
+ retval |= RX_DMA_SIZE_64K; /* did they ask for 64K? */
+ retval |= MEMORY_ON; /* we need memory enabled to use DMA. */
+ }
+ return retval;
+}
+
+static void
+dma_rx(struct net_device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ struct sk_buff *skb;
+ int status, length;
+ unsigned char *bp = lp->rx_dma_ptr;
+
+ {
+ int i;
+ for (i = 0; i < 1000; i++)
+ ;
+ }
+
+ status = bp[0] + (bp[1]<<8);
+ length = bp[2] + (bp[3]<<8);
+ bp += 4;
+ if (net_debug > 5)
+ {
+ printk( "%s: receiving DMA packet at %lx, status %x, length %x\n",
+ dev->name, (unsigned long)bp, status, length);
+ }
+ if ((status & RX_OK) == 0) {
+ count_rx_errors(status, lp);
+ goto skip_this_frame;
+ }
+
+ /* Malloc up new buffer. */
+ skb = alloc_skb(length, GFP_ATOMIC);
+ if (skb == NULL) {
+ if (net_debug) /* I don't think we want to do this to a stressed system */
+ printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+
+ /* AKPM: advance bp to the next frame */
+skip_this_frame:
+ bp += (length + 3) & ~3;
+ if (bp >= lp->end_dma_buff) bp -= lp->dmasize*1024;
+ lp->rx_dma_ptr = bp;
+ return;
+ }
+
+ skb->len = length;
+ skb->dev = dev;
+
+ if (bp + length > lp->end_dma_buff) {
+ int semi_cnt = lp->end_dma_buff - bp;
+ memcpy(skb_put(skb,semi_cnt), bp, semi_cnt);
+ memcpy(skb_put(skb,length - semi_cnt), lp->dma_buff,
+ length - semi_cnt);
+ } else {
+ memcpy(skb_put(skb,length), bp, length);
+ }
+ bp += (length + 3) & ~3;
+ if (bp >= lp->end_dma_buff) bp -= lp->dmasize*1024;
+ lp->rx_dma_ptr = bp;
+
+ if (net_debug > 3)
+ {
+ printk( "%s: received %d byte DMA packet of type %x\n",
+ dev->name, length,
+ (skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]);
+ }
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ return;
}
-void __init
-reset_chip(struct net_device *dev)
+#endif /* ALLOW_DMA */
+
+void __init reset_chip(struct net_device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
int ioaddr = dev->base_addr;
@@ -399,8 +694,8 @@
outb(0, ioaddr + DATA_PORT + 1);
outw(PP_CS8920_ISAMemB, ioaddr + ADD_PORT);
- outb((dev->mem_start >> 8) & 0xff, ioaddr + DATA_PORT);
- outb((dev->mem_start >> 24) & 0xff, ioaddr + DATA_PORT + 1);
+ outb((dev->mem_start >> 16) & 0xff, ioaddr + DATA_PORT);
+ outb((dev->mem_start >> 8) & 0xff, ioaddr + DATA_PORT + 1);
}
/* Wait until the chip is reset */
reset_start_time = jiffies;
@@ -430,11 +725,18 @@
}
+#define DETECTED_NONE 0
+#define DETECTED_RJ45H 1
+#define DETECTED_RJ45F 2
+#define DETECTED_AUI 3
+#define DETECTED_BNC 4
+
static int
detect_tp(struct net_device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
int timenow = jiffies;
+ int fdx;
if (net_debug > 1) printk("%s: Attempting TP\n", dev->name);
@@ -450,35 +752,63 @@
for (timenow = jiffies; jiffies - timenow < 15; )
;
if ((readreg(dev, PP_LineST) & LINK_OK) == 0)
- return 0;
+ return DETECTED_NONE;
- if (lp->chip_type != CS8900) {
+ if (lp->chip_type == CS8900) {
+ switch (lp->force & 0xf0) {
+#if 0
+ case FORCE_AUTO:
+ printk("%s: cs8900 doesn't autonegotiate\n",dev->name);
+ return DETECTED_NONE;
+#endif
+ /* CS8900 doesn't support AUTO, change to HALF*/
+ case FORCE_AUTO:
+ lp->force &= ~FORCE_AUTO;
+ lp->force |= FORCE_HALF;
+ break;
+ case FORCE_HALF:
+ break;
+ case FORCE_FULL:
+ writereg(dev, PP_TestCTL, readreg(dev, PP_TestCTL) | FDX_8900);
+ break;
+ }
+ fdx = readreg(dev, PP_TestCTL) & FDX_8900;
+ } else {
+ switch (lp->force & 0xf0) {
+ case FORCE_AUTO:
+ lp->auto_neg_cnf = AUTO_NEG_ENABLE;
+ break;
+ case FORCE_HALF:
+ lp->auto_neg_cnf = 0;
+ break;
+ case FORCE_FULL:
+ lp->auto_neg_cnf = RE_NEG_NOW | ALLOW_FDX;
+ break;
+ }
writereg(dev, PP_AutoNegCTL, lp->auto_neg_cnf & AUTO_NEG_MASK);
if ((lp->auto_neg_cnf & AUTO_NEG_BITS) == AUTO_NEG_ENABLE) {
- printk("%s: negotiating duplex...\n",dev->name);
+ printk(KERN_INFO "%s: negotiating duplex...\n",dev->name);
while (readreg(dev, PP_AutoNegST) & AUTO_NEG_BUSY) {
if (jiffies - timenow > 4000) {
- printk("**** Full / half duplex auto-negotiation timed out ****\n");
+ printk(KERN_ERR "**** Full / half duplex auto-negotiation timed out ****\n");
break;
}
}
}
- if (readreg(dev, PP_AutoNegST) & FDX_ACTIVE)
- printk("%s: using full duplex\n", dev->name);
- else
- printk("%s: using half duplex\n", dev->name);
+ fdx = readreg(dev, PP_AutoNegST) & FDX_ACTIVE;
}
-
- return A_CNF_MEDIA_10B_T;
+ if (fdx)
+ return DETECTED_RJ45F;
+ else
+ return DETECTED_RJ45H;
}
/* send a test packet - return true if carrier bits are ok */
static int
send_test_pkt(struct net_device *dev)
{
- int ioaddr = dev->base_addr;
char test_packet[] = { 0,0,0,0,0,0, 0,0,0,0,0,0,
0, 46, /* A 46 in network order */
0, 0, /* DSAP=0 & SSAP=0 fields */
@@ -490,8 +820,8 @@
memcpy(test_packet, dev->dev_addr, ETH_ALEN);
memcpy(test_packet+ETH_ALEN, dev->dev_addr, ETH_ALEN);
- outw(TX_AFTER_ALL, ioaddr + TX_CMD_PORT);
- outw(ETH_ZLEN, ioaddr + TX_LEN_PORT);
+ writeword(dev, TX_CMD_PORT, TX_AFTER_ALL);
+ writeword(dev, TX_LEN_PORT, ETH_ZLEN);
/* Test to see if the chip has allocated memory for the packet */
while (jiffies - timenow < 5)
@@ -501,11 +831,7 @@
return 0; /* this shouldn't happen */
/* Write the contents of the packet */
- if (dev->mem_start) {
- memcpy((void *)dev->mem_start + PP_TxFrame, test_packet, ETH_ZLEN);
- } else {
- outsw(ioaddr + TX_FRAME_PORT,test_packet,(ETH_ZLEN+1) >>1);
- }
+ outsw(dev->base_addr + TX_FRAME_PORT,test_packet,(ETH_ZLEN+1) >>1);
if (net_debug > 1) printk("Sending test packet ");
/* wait a couple of jiffies for packet to be received */
@@ -531,9 +857,9 @@
writereg(dev, PP_LineCTL, (lp->linectl &~ AUTO_AUI_10BASET) | AUI_ONLY);
if (send_test_pkt(dev))
- return A_CNF_MEDIA_AUI;
+ return DETECTED_AUI;
else
- return 0;
+ return DETECTED_NONE;
}
static int
@@ -547,9 +873,9 @@
writereg(dev, PP_LineCTL, (lp->linectl &~ AUTO_AUI_10BASET) | AUI_ONLY);
if (send_test_pkt(dev))
- return A_CNF_MEDIA_10B_2;
+ return DETECTED_BNC;
else
- return 0;
+ return DETECTED_NONE;
}
@@ -579,6 +905,9 @@
registers that "should" only need to be set once at boot, so that
there is non-reboot way to recover if something goes wrong.
*/
+
+/* AKPM: do we need to do any locking here? */
+
static int
net_open(struct net_device *dev)
{
@@ -588,9 +917,15 @@
if (dev->irq < 2) {
/* Allow interrupts to be generated by the chip */
+/* Cirrus' release had this: */
+#if 0
+ writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL)|ENABLE_IRQ );
+#endif
+/* And 2.3.47 had this: */
writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON);
+
for (i = 2; i < CS8920_NO_INTS; i++) if ((1 << dev->irq) & lp->irq_map) {
- if (request_irq (i, NULL, 0, "cs8920", dev) != -EBUSY) {
+ if (request_irq (i, NULL, 0, "cs89x0", dev) != -EBUSY) {
write_irq(dev, lp->chip_type, i);
writereg(dev, PP_BufCFG, GENERATE_SW_INTERRUPT);
if (request_irq (dev->irq = i, &net_interrupt, 0, "cs89x0", dev) == 0)
@@ -601,21 +936,74 @@
if (i >= CS8920_NO_INTS) {
writereg(dev, PP_BusCTL, 0); /* disable interrupts. */
+ if (net_debug)
+ printk("cs89x0: can't get an interrupt\n");
return -EAGAIN;
}
} else {
if (((1 << dev->irq) & lp->irq_map) == 0) {
- printk("%s: IRQ %d is not in our map of allowable IRQs, which is %x\n",
+ printk(KERN_ERR "%s: IRQ %d is not in our map of allowable IRQs, which is %x\n",
dev->name, dev->irq, lp->irq_map);
return -EAGAIN;
}
+/* FIXME: Cirrus' release had this: */
+ writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL)|ENABLE_IRQ );
+/* And 2.3.47 had this: */
+#if 0
writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON);
+#endif
write_irq(dev, lp->chip_type, dev->irq);
if (request_irq(dev->irq, &net_interrupt, 0, "cs89x0", dev)) {
+ if (net_debug)
+ printk("cs89x0: request_irq(%d) failed\n", dev->irq);
return -EAGAIN;
}
}
+#if ALLOW_DMA
+ if (lp->use_dma)
+ {
+ if (lp->isa_config & ANY_ISA_DMA) {
+ unsigned long flags;
+ lp->dma_buff = (unsigned char *)__get_dma_pages(GFP_KERNEL,
+ (lp->dmasize * 1024) / PAGE_SIZE);
+
+ if (!lp->dma_buff) {
+ printk(KERN_ERR "%s: cannot get %dK memory for DMA\n", dev->name, lp->dmasize);
+ goto release_irq;
+ }
+ if (net_debug > 1)
+ {
+ printk( "%s: dma %lx %lx\n",
+ dev->name,
+ (unsigned long)lp->dma_buff,
+ (unsigned long)virt_to_bus(lp->dma_buff));
+ }
+ if ((unsigned long)virt_to_bus(lp->dma_buff) >= MAX_DMA_ADDRESS ||
+ !dma_page_eq(lp->dma_buff, lp->dma_buff+lp->dmasize*1024-1)) {
+ printk(KERN_ERR "%s: not usable as DMA buffer\n", dev->name);
+ goto release_irq;
+ }
+ memset(lp->dma_buff, 0, lp->dmasize * 1024); /* Why? */
+ if (request_dma(dev->dma, "cs89x0")) {
+ printk(KERN_ERR "%s: cannot get dma channel %d\n", dev->name, dev->dma);
+ goto release_irq;
+ }
+ write_dma(dev, lp->chip_type, dev->dma);
+ lp->rx_dma_ptr = lp->dma_buff;
+ lp->end_dma_buff = lp->dma_buff + lp->dmasize*1024;
+ spin_lock_irqsave(&lp->lock, flags);
+ disable_dma(dev->dma);
+ clear_dma_ff(dev->dma);
+ set_dma_mode(dev->dma, 0x14); /* auto_init as well */
+ set_dma_addr(dev->dma, virt_to_bus(lp->dma_buff));
+ set_dma_count(dev->dma, lp->dmasize*1024);
+ enable_dma(dev->dma);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ }
+ }
+#endif /* ALLOW_DMA */
+
/* set the Ethernet address */
for (i=0; i < ETH_ALEN/2; i++)
writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));
@@ -637,8 +1025,11 @@
default: result = lp->adapter_cnf & (A_CNF_10B_T | A_CNF_AUI | A_CNF_10B_2);
}
if (!result) {
- printk("%s: EEPROM is configured for unavailable media\n", dev->name);
+ printk(KERN_ERR "%s: EEPROM is configured for unavailable media\n", dev->name);
release_irq:
+#if ALLOW_DMA
+ release_dma_buff(lp);
+#endif
writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) & ~(SERIAL_TX_ON | SERIAL_RX_ON));
free_irq(dev->irq, dev);
return -EAGAIN;
@@ -648,43 +1039,58 @@
switch(lp->adapter_cnf & A_CNF_MEDIA_TYPE) {
case A_CNF_MEDIA_10B_T:
result = detect_tp(dev);
- if (!result) printk("%s: 10Base-T (RJ-45) has no cable\n", dev->name);
- if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
- result = A_CNF_MEDIA_10B_T; /* Yes! I don't care if I see a link pulse */
+ if (result==DETECTED_NONE) {
+ printk(KERN_WARNING "%s: 10Base-T (RJ-45) has no cable\n", dev->name);
+ if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
+ result = DETECTED_RJ45H; /* Yes! I don't care if I see a link pulse */
+ }
break;
case A_CNF_MEDIA_AUI:
result = detect_aui(dev);
- if (!result) printk("%s: 10Base-5 (AUI) has no cable\n", dev->name);
- if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
- result = A_CNF_MEDIA_AUI; /* Yes! I don't care if I see a carrrier */
+ if (result==DETECTED_NONE) {
+ printk(KERN_WARNING "%s: 10Base-5 (AUI) has no cable\n", dev->name);
+ if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
+ result = DETECTED_AUI; /* Yes! I don't care if I see a carrrier */
+ }
break;
case A_CNF_MEDIA_10B_2:
result = detect_bnc(dev);
- if (!result) printk("%s: 10Base-2 (BNC) has no cable\n", dev->name);
- if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
- result = A_CNF_MEDIA_10B_2; /* Yes! I don't care if I can xmit a packet */
+ if (result==DETECTED_NONE) {
+ printk(KERN_WARNING "%s: 10Base-2 (BNC) has no cable\n", dev->name);
+ if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
+ result = DETECTED_BNC; /* Yes! I don't care if I can xmit a packet */
+ }
break;
case A_CNF_MEDIA_AUTO:
writereg(dev, PP_LineCTL, lp->linectl | AUTO_AUI_10BASET);
if (lp->adapter_cnf & A_CNF_10B_T)
- if ((result = detect_tp(dev)) != 0)
+ if ((result = detect_tp(dev)) != DETECTED_NONE)
break;
if (lp->adapter_cnf & A_CNF_AUI)
- if ((result = detect_aui(dev)) != 0)
+ if ((result = detect_aui(dev)) != DETECTED_NONE)
break;
if (lp->adapter_cnf & A_CNF_10B_2)
- if ((result = detect_bnc(dev)) != 0)
+ if ((result = detect_bnc(dev)) != DETECTED_NONE)
break;
- printk("%s: no media detected\n", dev->name);
+ printk(KERN_ERR "%s: no media detected\n", dev->name);
goto release_irq;
}
switch(result) {
- case 0: printk("%s: no network cable attached to configured media\n", dev->name);
+ case DETECTED_NONE:
+ printk(KERN_ERR "%s: no network cable attached to configured media\n", dev->name);
goto release_irq;
- case A_CNF_MEDIA_10B_T: printk("%s: using 10Base-T (RJ-45)\n", dev->name);break;
- case A_CNF_MEDIA_AUI: printk("%s: using 10Base-5 (AUI)\n", dev->name);break;
- case A_CNF_MEDIA_10B_2: printk("%s: using 10Base-2 (BNC)\n", dev->name);break;
- default: printk("%s: unexpected result was %x\n", dev->name, result); goto release_irq;
+ case DETECTED_RJ45H:
+ printk(KERN_INFO "%s: using half-duplex 10Base-T (RJ-45)\n", dev->name);
+ break;
+ case DETECTED_RJ45F:
+ printk(KERN_INFO "%s: using full-duplex 10Base-T (RJ-45)\n", dev->name);
+ break;
+ case DETECTED_AUI:
+ printk(KERN_INFO "%s: using 10Base-5 (AUI)\n", dev->name);
+ break;
+ case DETECTED_BNC:
+ printk(KERN_INFO "%s: using 10Base-2 (BNC)\n", dev->name);
+ break;
}
/* Turn on both receive and transmit operations */
@@ -695,22 +1101,34 @@
writereg(dev, PP_RxCTL, DEF_RX_ACCEPT);
lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL;
+
if (lp->isa_config & STREAM_TRANSFER)
lp->curr_rx_cfg |= RX_STREAM_ENBL;
-
+#if ALLOW_DMA
+ set_dma_cfg(dev);
+#endif
writereg(dev, PP_RxCFG, lp->curr_rx_cfg);
writereg(dev, PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL |
- TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL);
+ TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL);
writereg(dev, PP_BufCFG, READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL |
- TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL);
+#if ALLOW_DMA
+ dma_bufcfg(dev) |
+#endif
+ TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL);
/* now that we've got our act together, enable everything */
writereg(dev, PP_BusCTL, ENABLE_IRQ
+ | (dev->mem_start?MEMORY_ON : 0) /* turn memory on */
+#if ALLOW_DMA
+ | dma_busctl(dev)
+#endif
);
MOD_INC_USE_COUNT;
netif_start_queue(dev);
+ if (net_debug)
+ printk("cs89x0: net_open() succeeded\n");
return 0;
}
@@ -727,39 +1145,54 @@
static int net_send_packet(struct sk_buff *skb, struct net_device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
- short ioaddr = dev->base_addr;
- unsigned long flags;
if (net_debug > 3)
- printk("%s: sent %d byte packet of type %x\n", dev->name, skb->len, (skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]);
+ {
+ printk("%s: sent %d byte packet of type %x\n",
+ dev->name, skb->len,
+ (skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]);
+ }
/* keep the upload from being interrupted, since we
ask the chip to start transmitting before the
whole packet has been completely uploaded. */
- save_flags(flags);
- cli();
+ spin_lock_irq(&lp->lock);
netif_stop_queue(dev);
-
+
/* initiate a transmit sequence */
- outw(lp->send_cmd, ioaddr + TX_CMD_PORT);
- outw(skb->len, ioaddr + TX_LEN_PORT);
+ writeword(dev, TX_CMD_PORT, lp->send_cmd);
+ writeword(dev, TX_LEN_PORT, skb->len);
/* Test to see if the chip has allocated memory for the packet */
- if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) {
- /* Gasp! It hasn't. But that shouldn't happen since
- we're waiting for TxOk, so return 1 and requeue this packet. */
- restore_flags(flags);
+ if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0)
+ {
+ /*
+ * Gasp! It hasn't. But that shouldn't happen since
+ * we're waiting for TxOk, so return 1 and requeue this packet.
+ */
+
+ spin_unlock_irq(&lp->lock);
+ if (net_debug) printk("cs89x0: Tx buffer not free!\n");
return 1;
}
/* Write the contents of the packet */
- outsw(ioaddr + TX_FRAME_PORT,skb->data,(skb->len+1) >>1);
-
- restore_flags(flags);
+ outsw(dev->base_addr + TX_FRAME_PORT,skb->data,(skb->len+1) >>1);
+ spin_unlock_irq(&lp->lock);
dev->trans_start = jiffies;
dev_kfree_skb (skb);
- netif_wake_queue(dev);
-
+
+ /*
+ * We DO NOT call netif_wake_queue() here.
+ * We also DO NOT call netif_start_queue().
+ *
+ * Either of these would cause another bottom half run through
+ * net_send_packet() before this packet has fully gone out. That causes
+ * us to hit the "Gasp!" above and the send is rescheduled. it runs like
+ * a dog. We just return and wait for the Tx completion interrupt handler
+ * to restart the netdevice layer
+ */
+
return 0;
}
@@ -771,7 +1204,7 @@
struct net_device *dev = dev_id;
struct net_local *lp;
int ioaddr, status;
-
+
ioaddr = dev->base_addr;
lp = (struct net_local *)dev->priv;
@@ -792,11 +1225,18 @@
case ISQ_TRANSMITTER_EVENT:
lp->stats.tx_packets++;
netif_wake_queue(dev); /* Inform upper layers. */
- if ((status & TX_OK) == 0) lp->stats.tx_errors++;
- if (status & TX_LOST_CRS) lp->stats.tx_carrier_errors++;
- if (status & TX_SQE_ERROR) lp->stats.tx_heartbeat_errors++;
- if (status & TX_LATE_COL) lp->stats.tx_window_errors++;
- if (status & TX_16_COL) lp->stats.tx_aborted_errors++;
+ if ((status & ( TX_OK |
+ TX_LOST_CRS |
+ TX_SQE_ERROR |
+ TX_LATE_COL |
+ TX_16_COL)) != TX_OK)
+ {
+ if ((status & TX_OK) == 0) lp->stats.tx_errors++;
+ if (status & TX_LOST_CRS) lp->stats.tx_carrier_errors++;
+ if (status & TX_SQE_ERROR) lp->stats.tx_heartbeat_errors++;
+ if (status & TX_LATE_COL) lp->stats.tx_window_errors++;
+ if (status & TX_16_COL) lp->stats.tx_aborted_errors++;
+ }
break;
case ISQ_BUFFER_EVENT:
if (status & READY_FOR_TX) {
@@ -819,6 +1259,22 @@
event of a tx underrun */
netif_wake_queue(dev); /* Inform upper layers. */
}
+#if ALLOW_DMA
+ if (lp->use_dma && (status & RX_DMA)) {
+ int count = readreg(dev, PP_DmaFrameCnt);
+ while(count) {
+ if (net_debug > 5)
+ printk("%s: receiving %d DMA frames\n", dev->name, count);
+ if (net_debug > 2 && count >1)
+ printk("%s: receiving %d DMA frames\n", dev->name, count);
+ dma_rx(dev);
+ if (--count == 0)
+ count = readreg(dev, PP_DmaFrameCnt);
+ if (net_debug > 2 && count > 0)
+ printk("%s: continuing with %d DMA frames\n", dev->name, count);
+ }
+ }
+#endif
break;
case ISQ_RX_MISS_EVENT:
lp->stats.rx_missed_errors += (status >>6);
@@ -830,45 +1286,58 @@
}
}
+static void
+count_rx_errors(int status, struct net_local *lp)
+{
+ lp->stats.rx_errors++;
+ if (status & RX_RUNT) lp->stats.rx_length_errors++;
+ if (status & RX_EXTRA_DATA) lp->stats.rx_length_errors++;
+ if (status & RX_CRC_ERROR) if (!(status & (RX_EXTRA_DATA|RX_RUNT)))
+ /* per str 172 */
+ lp->stats.rx_crc_errors++;
+ if (status & RX_DRIBBLE) lp->stats.rx_frame_errors++;
+ return;
+}
+
/* We have a good packet(s), get it/them out of the buffers. */
static void
net_rx(struct net_device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
- int ioaddr = dev->base_addr;
struct sk_buff *skb;
int status, length;
+ int ioaddr = dev->base_addr;
status = inw(ioaddr + RX_FRAME_PORT);
length = inw(ioaddr + RX_FRAME_PORT);
+
if ((status & RX_OK) == 0) {
- lp->stats.rx_errors++;
- if (status & RX_RUNT) lp->stats.rx_length_errors++;
- if (status & RX_EXTRA_DATA) lp->stats.rx_length_errors++;
- if (status & RX_CRC_ERROR) if (!(status & (RX_EXTRA_DATA|RX_RUNT)))
- /* per str 172 */
- lp->stats.rx_crc_errors++;
- if (status & RX_DRIBBLE) lp->stats.rx_frame_errors++;
+ count_rx_errors(status, lp);
return;
}
/* Malloc up new buffer. */
skb = alloc_skb(length, GFP_ATOMIC);
if (skb == NULL) {
- printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+#if 0 /* Again, this seems a cruel thing to do */
+ printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
+#endif
lp->stats.rx_dropped++;
return;
}
skb->len = length;
skb->dev = dev;
- insw(ioaddr + RX_FRAME_PORT, skb->data, length >> 1);
+ insw(ioaddr + RX_FRAME_PORT, skb->data, length >> 1);
if (length & 1)
skb->data[length-1] = inw(ioaddr + RX_FRAME_PORT);
- if (net_debug > 3)printk("%s: received %d byte packet of type %x\n",
- dev->name, length,
- (skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]);
+ if (net_debug > 3)
+ {
+ printk( "%s: received %d byte packet of type %x\n",
+ dev->name, length,
+ (skb->data[ETH_ALEN+ETH_ALEN] << 8) | skb->data[ETH_ALEN+ETH_ALEN+1]);
+ }
skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
@@ -877,10 +1346,23 @@
return;
}
+#if ALLOW_DMA
+static void release_dma_buff(struct net_local *lp)
+{
+ if (lp->dma_buff)
+ {
+ free_pages((unsigned long)(lp->dma_buff), (lp->dmasize * 1024) / PAGE_SIZE);
+ lp->dma_buff = 0;
+ }
+}
+#endif
+
/* The inverse routine to net_open(). */
static int
net_close(struct net_device *dev)
{
+ struct net_local *lp = (struct net_local *)dev->priv;
+
netif_stop_queue(dev);
writereg(dev, PP_RxCFG, 0);
@@ -890,11 +1372,16 @@
free_irq(dev->irq, dev);
- /* Update the statistics here. */
+#if ALLOW_DMA
+ if (lp->use_dma && lp->dma) {
+ free_dma(dev->dma);
+ release_dma_buff(lp);
+ }
+#endif
+ /* Update the statistics here. */
MOD_DEC_USE_COUNT;
return 0;
-
}
/* Get the current statistics. This may be called with the card open or
@@ -903,12 +1390,13 @@
net_get_stats(struct net_device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
+ unsigned long flags;
- cli();
+ spin_lock_irqsave(&lp->lock, flags);
/* Update the statistics from the device registers. */
lp->stats.rx_missed_errors += (readreg(dev, PP_RxMiss) >> 6);
lp->stats.collisions += (readreg(dev, PP_TxCol) >> 6);
- sti();
+ spin_unlock_irqrestore(&lp->lock, flags);
return &lp->stats;
}
@@ -916,7 +1404,9 @@
static void set_multicast_list(struct net_device *dev)
{
struct net_local *lp = (struct net_local *)dev->priv;
+ unsigned long flags;
+ spin_lock_irqsave(&lp->lock, flags);
if(dev->flags&IFF_PROMISC)
{
lp->rx_mode = RX_ALL_ACCEPT;
@@ -935,18 +1425,23 @@
/* in promiscuous mode, we accept errored packets, so we have to enable interrupts on them also */
writereg(dev, PP_RxCFG, lp->curr_rx_cfg |
(lp->rx_mode == RX_ALL_ACCEPT? (RX_CRC_ERROR_ENBL|RX_RUNT_ENBL|RX_EXTRA_DATA_ENBL) : 0));
+ spin_unlock_irqrestore(&lp->lock, flags);
}
static int set_mac_address(struct net_device *dev, void *addr)
{
int i;
+
if (netif_running(dev))
return -EBUSY;
- printk("%s: Setting MAC address to ", dev->name);
- for (i = 0; i < 6; i++)
- printk(" %2.2x", dev->dev_addr[i] = ((unsigned char *)addr)[i]);
- printk(".\n");
+ if (net_debug)
+ {
+ printk("%s: Setting MAC address to ", dev->name);
+ for (i = 0; i < 6; i++)
+ printk(" %2.2x", dev->dev_addr[i] = ((unsigned char *)addr)[i]);
+ printk(".\n");
+ }
/* set the Ethernet address */
for (i=0; i < ETH_ALEN/2; i++)
writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8));
@@ -963,17 +1458,31 @@
0, 0,
0, 0, 0, NULL, NULL };
+/*
+ * Support the 'debug' module parm even if we're compiled for non-debug to
+ * avoid breaking someone's startup scripts
+ */
+
static int io=0;
static int irq=0;
static int debug=0;
static char media[8];
static int duplex=-1;
+static int use_dma = 0; /* These generate unused var warnings if ALLOW_DMA = 0 */
+static int dma=0;
+static int dmasize=16; /* or 64 */
+
MODULE_PARM(io, "i");
MODULE_PARM(irq, "i");
MODULE_PARM(debug, "i");
MODULE_PARM(media, "s");
MODULE_PARM(duplex, "i");
+MODULE_PARM(dma , "i");
+MODULE_PARM(dmasize , "i");
+MODULE_PARM(use_dma , "i");
+
+MODULE_AUTHOR("Mike Cruse, Russwll Nelson <nelson@crynwr.com>, Andrew Morton <andrewm@uow.edu.au>");
EXPORT_NO_SYMBOLS;
@@ -1008,15 +1517,34 @@
{
struct net_local *lp;
+#if DEBUGGING
net_debug = debug;
+#endif
dev_cs89x0.name = namespace;
dev_cs89x0.irq = irq;
dev_cs89x0.base_addr = io;
+
dev_cs89x0.init = cs89x0_probe;
dev_cs89x0.priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+ if (dev_cs89x0.priv == 0)
+ {
+ printk(KERN_ERR "cs89x0.c: Out of memory.\n");
+ return -ENOMEM;
+ }
memset(dev_cs89x0.priv, 0, sizeof(struct net_local));
lp = (struct net_local *)dev_cs89x0.priv;
+#if ALLOW_DMA
+ if (use_dma)
+ {
+ lp->use_dma = use_dma;
+ lp->dma = dma;
+ lp->dmasize = dmasize;
+ }
+#endif
+
+ spin_lock_init(&lp->lock);
+
/* boy, they'd better get these right */
if (!strcmp(media, "rj45"))
lp->adapter_cnf = A_CNF_MEDIA_10B_T | A_CNF_10B_T;
@@ -1031,12 +1559,20 @@
lp->auto_neg_cnf = AUTO_NEG_ENABLE;
if (io == 0) {
- printk(KERN_NOTICE "cs89x0.c: Module autoprobing not allowed.\n");
- printk(KERN_NOTICE "cs89x0.c: Append io=0xNNN\n");
+ printk(KERN_ERR "cs89x0.c: Module autoprobing not allowed.\n");
+ printk(KERN_ERR "cs89x0.c: Append io=0xNNN\n");
return -EPERM;
}
+
+#if ALLOW_DMA
+ if (use_dma && dmasize != 16 && dmasize != 64) {
+ printk(KERN_ERR "cs89x0.c: dma size must be either 16K or 64K, not %dK\n", dmasize);
+ return -EPERM;
+ }
+#endif
+
if (register_netdev(&dev_cs89x0) != 0) {
- printk(KERN_WARNING "cs89x0.c: No card found at 0x%x\n", io);
+ printk(KERN_ERR "cs89x0.c: No card found at 0x%x\n", io);
return -ENXIO;
}
return 0;
@@ -1045,13 +1581,7 @@
void
cleanup_module(void)
{
-
-#endif
-#ifdef MODULE
- outw(0, dev_cs89x0.base_addr + ADD_PORT);
-#endif
-#ifdef MODULE
-
+ outw(PP_ChipID, dev_cs89x0.base_addr + ADD_PORT);
if (dev_cs89x0.priv != NULL) {
/* Free up the private structure, or leak memory :-) */
unregister_netdev(&dev_cs89x0);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)