Netdev List
 help / color / mirror / Atom feed
* [PATCH net v2 07/15] drivers: net: smsc: smc9194: Remove this driver
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
  Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
	Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
In-Reply-To: <20260422-v7-0-0-net-next-driver-removal-v1-v2-0-08a5b59784d5@lunn.ch>

The smc9194 was written by Erik Stahlman in 1996. It is an ISA device,
so unlikely to be used with modern kernels.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
 drivers/net/ethernet/smsc/Kconfig   |   15 -
 drivers/net/ethernet/smsc/Makefile  |    1 -
 drivers/net/ethernet/smsc/smc9194.c | 1535 -----------------------------------
 3 files changed, 1551 deletions(-)

diff --git a/drivers/net/ethernet/smsc/Kconfig b/drivers/net/ethernet/smsc/Kconfig
index 13ce9086a9ca..d25bbcc98854 100644
--- a/drivers/net/ethernet/smsc/Kconfig
+++ b/drivers/net/ethernet/smsc/Kconfig
@@ -19,21 +19,6 @@ config NET_VENDOR_SMSC
 
 if NET_VENDOR_SMSC
 
-config SMC9194
-	tristate "SMC 9194 support"
-	depends on ISA
-	select CRC32
-	select NETDEV_LEGACY_INIT
-	help
-	  This is support for the SMC9xxx based Ethernet cards. Choose this
-	  option if you have a DELL laptop with the docking station, or
-	  another SMC9192/9194 based chipset.  Say Y if you want it compiled
-	  into the kernel, and read the file
-	  <file:Documentation/networking/device_drivers/ethernet/smsc/smc9.rst>.
-
-	  To compile this driver as a module, choose M here. The module
-	  will be called smc9194.
-
 config SMC91X
 	tristate "SMC 91C9x/91C1xxx support"
 	select CRC32
diff --git a/drivers/net/ethernet/smsc/Makefile b/drivers/net/ethernet/smsc/Makefile
index 1501fa364c13..afea0b94c2a4 100644
--- a/drivers/net/ethernet/smsc/Makefile
+++ b/drivers/net/ethernet/smsc/Makefile
@@ -3,7 +3,6 @@
 # Makefile for the SMSC network device drivers.
 #
 
-obj-$(CONFIG_SMC9194) += smc9194.o
 obj-$(CONFIG_SMC91X) += smc91x.o
 obj-$(CONFIG_PCMCIA_SMC91C92) += smc91c92_cs.o
 obj-$(CONFIG_EPIC100) += epic100.o
diff --git a/drivers/net/ethernet/smsc/smc9194.c b/drivers/net/ethernet/smsc/smc9194.c
deleted file mode 100644
index e2e7b1c68563..000000000000
--- a/drivers/net/ethernet/smsc/smc9194.c
+++ /dev/null
@@ -1,1535 +0,0 @@
-/*------------------------------------------------------------------------
- . smc9194.c
- . This is a driver for SMC's 9000 series of Ethernet cards.
- .
- . Copyright (C) 1996 by Erik Stahlman
- . This software may be used and distributed according to the terms
- . of the GNU General Public License, incorporated herein by reference.
- .
- . "Features" of the SMC chip:
- .   4608 byte packet memory. ( for the 91C92.  Others have more )
- .   EEPROM for configuration
- .   AUI/TP selection  ( mine has 10Base2/10BaseT select )
- .
- . Arguments:
- . 	io		 = for the base address
- .	irq	 = for the IRQ
- .	ifport = 0 for autodetect, 1 for TP, 2 for AUI ( or 10base2 )
- .
- . author:
- . 	Erik Stahlman				( erik@vt.edu )
- . contributors:
- .      Arnaldo Carvalho de Melo <acme@conectiva.com.br>
- .
- . Hardware multicast code from Peter Cammaert ( pc@denkart.be )
- .
- . Sources:
- .    o   SMC databook
- .    o   skeleton.c by Donald Becker ( becker@scyld.com )
- .    o   ( a LOT of advice from Becker as well )
- .
- . History:
- .	12/07/95  Erik Stahlman  written, got receive/xmit handled
- . 	01/03/96  Erik Stahlman  worked out some bugs, actually usable!!! :-)
- .	01/06/96  Erik Stahlman	 cleaned up some, better testing, etc
- .	01/29/96  Erik Stahlman	 fixed autoirq, added multicast
- . 	02/01/96  Erik Stahlman	 1. disabled all interrupts in smc_reset
- .		   		 2. got rid of post-decrementing bug -- UGH.
- .	02/13/96  Erik Stahlman  Tried to fix autoirq failure.  Added more
- .				 descriptive error messages.
- .	02/15/96  Erik Stahlman  Fixed typo that caused detection failure
- . 	02/23/96  Erik Stahlman	 Modified it to fit into kernel tree
- .				 Added support to change hardware address
- .				 Cleared stats on opens
- .	02/26/96  Erik Stahlman	 Trial support for Kernel 1.2.13
- .				 Kludge for automatic IRQ detection
- .	03/04/96  Erik Stahlman	 Fixed kernel 1.3.70 +
- .				 Fixed bug reported by Gardner Buchanan in
- .				   smc_enable, with outw instead of outb
- .	03/06/96  Erik Stahlman  Added hardware multicast from Peter Cammaert
- .	04/14/00  Heiko Pruessing (SMA Regelsysteme)  Fixed bug in chip memory
- .				 allocation
- .      08/20/00  Arnaldo Melo   fix kfree(skb) in smc_hardware_send_packet
- .      12/15/00  Christian Jullien fix "Warning: kfree_skb on hard IRQ"
- .      11/08/01 Matt Domsch     Use common crc32 function
- ----------------------------------------------------------------------------*/
-
-static const char version[] =
-	"smc9194.c:v0.14 12/15/00 by Erik Stahlman (erik@vt.edu)";
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/in.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <linux/crc32.h>
-#include <linux/errno.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/bitops.h>
-
-#include <asm/io.h>
-
-#include "smc9194.h"
-
-#define DRV_NAME "smc9194"
-
-/*------------------------------------------------------------------------
- .
- . Configuration options, for the experienced user to change.
- .
- -------------------------------------------------------------------------*/
-
-/*
- . Do you want to use 32 bit xfers?  This should work on all chips, as
- . the chipset is designed to accommodate them.
-*/
-#ifdef __sh__
-#undef USE_32_BIT
-#else
-#define USE_32_BIT 1
-#endif
-
-/*
- .the SMC9194 can be at any of the following port addresses.  To change,
- .for a slightly different card, you can add it to the array.  Keep in
- .mind that the array must end in zero.
-*/
-
-struct devlist {
-	unsigned int port;
-	unsigned int irq;
-};
-
-static struct devlist smc_devlist[] __initdata = {
-	{.port = 0x200, .irq = 0},
-	{.port = 0x220, .irq = 0},
-	{.port = 0x240, .irq = 0},
-	{.port = 0x260, .irq = 0},
-	{.port = 0x280, .irq = 0},
-	{.port = 0x2A0, .irq = 0},
-	{.port = 0x2C0, .irq = 0},
-	{.port = 0x2E0, .irq = 0},
-	{.port = 0x300, .irq = 0},
-	{.port = 0x320, .irq = 0},
-	{.port = 0x340, .irq = 0},
-	{.port = 0x360, .irq = 0},
-	{.port = 0x380, .irq = 0},
-	{.port = 0x3A0, .irq = 0},
-	{.port = 0x3C0, .irq = 0},
-	{.port = 0x3E0, .irq = 0},
-	{.port = 0,     .irq = 0},
-};
-/*
- . Wait time for memory to be free.  This probably shouldn't be
- . tuned that much, as waiting for this means nothing else happens
- . in the system
-*/
-#define MEMORY_WAIT_TIME 16
-
-/*
- . DEBUGGING LEVELS
- .
- . 0 for normal operation
- . 1 for slightly more details
- . >2 for various levels of increasingly useless information
- .    2 for interrupt tracking, status flags
- .    3 for packet dumps, etc.
-*/
-#define SMC_DEBUG 0
-
-#if (SMC_DEBUG > 2 )
-#define PRINTK3(x) printk x
-#else
-#define PRINTK3(x)
-#endif
-
-#if SMC_DEBUG > 1
-#define PRINTK2(x) printk x
-#else
-#define PRINTK2(x)
-#endif
-
-#ifdef SMC_DEBUG
-#define PRINTK(x) printk x
-#else
-#define PRINTK(x)
-#endif
-
-
-/*------------------------------------------------------------------------
- .
- . The internal workings of the driver.  If you are changing anything
- . here with the SMC stuff, you should have the datasheet and known
- . what you are doing.
- .
- -------------------------------------------------------------------------*/
-#define CARDNAME "SMC9194"
-
-
-/* store this information for the driver.. */
-struct smc_local {
-	/*
-	   If I have to wait until memory is available to send
-	   a packet, I will store the skbuff here, until I get the
-	   desired memory.  Then, I'll send it out and free it.
-	*/
-	struct sk_buff * saved_skb;
-
-	/*
-	 . This keeps track of how many packets that I have
-	 . sent out.  When an TX_EMPTY interrupt comes, I know
-	 . that all of these have been sent.
-	*/
-	int	packets_waiting;
-};
-
-
-/*-----------------------------------------------------------------
- .
- .  The driver can be entered at any of the following entry points.
- .
- .------------------------------------------------------------------  */
-
-/*
- . This is called by  register_netdev().  It is responsible for
- . checking the portlist for the SMC9000 series chipset.  If it finds
- . one, then it will initialize the device, find the hardware information,
- . and sets up the appropriate device parameters.
- . NOTE: Interrupts are *OFF* when this procedure is called.
- .
- . NB:This shouldn't be static since it is referred to externally.
-*/
-struct net_device *smc_init(int unit);
-
-/*
- . The kernel calls this function when someone wants to use the device,
- . typically 'ifconfig ethX up'.
-*/
-static int smc_open(struct net_device *dev);
-
-/*
- . Our watchdog timed out. Called by the networking layer
-*/
-static void smc_timeout(struct net_device *dev, unsigned int txqueue);
-
-/*
- . This is called by the kernel in response to 'ifconfig ethX down'.  It
- . is responsible for cleaning up everything that the open routine
- . does, and maybe putting the card into a powerdown state.
-*/
-static int smc_close(struct net_device *dev);
-
-/*
- . Finally, a call to set promiscuous mode ( for TCPDUMP and related
- . programs ) and multicast modes.
-*/
-static void smc_set_multicast_list(struct net_device *dev);
-
-
-/*---------------------------------------------------------------
- .
- . Interrupt level calls..
- .
- ----------------------------------------------------------------*/
-
-/*
- . Handles the actual interrupt
-*/
-static irqreturn_t smc_interrupt(int irq, void *);
-/*
- . This is a separate procedure to handle the receipt of a packet, to
- . leave the interrupt code looking slightly cleaner
-*/
-static inline void smc_rcv( struct net_device *dev );
-/*
- . This handles a TX interrupt, which is only called when an error
- . relating to a packet is sent.
-*/
-static inline void smc_tx( struct net_device * dev );
-
-/*
- ------------------------------------------------------------
- .
- . Internal routines
- .
- ------------------------------------------------------------
-*/
-
-/*
- . Test if a given location contains a chip, trying to cause as
- . little damage as possible if it's not a SMC chip.
-*/
-static int smc_probe(struct net_device *dev, int ioaddr);
-
-/*
- . A rather simple routine to print out a packet for debugging purposes.
-*/
-#if SMC_DEBUG > 2
-static void print_packet( byte *, int );
-#endif
-
-#define tx_done(dev) 1
-
-/* this is called to actually send the packet to the chip */
-static void smc_hardware_send_packet( struct net_device * dev );
-
-/* Since I am not sure if I will have enough room in the chip's ram
- . to store the packet, I call this routine, which either sends it
- . now, or generates an interrupt when the card is ready for the
- . packet */
-static netdev_tx_t  smc_wait_to_send_packet( struct sk_buff * skb,
-					     struct net_device *dev );
-
-/* this does a soft reset on the device */
-static void smc_reset( int ioaddr );
-
-/* Enable Interrupts, Receive, and Transmit */
-static void smc_enable( int ioaddr );
-
-/* this puts the device in an inactive state */
-static void smc_shutdown( int ioaddr );
-
-/* This routine will find the IRQ of the driver if one is not
- . specified in the input to the device.  */
-static int smc_findirq( int ioaddr );
-
-/*
- . Function: smc_reset( int ioaddr )
- . Purpose:
- .  	This sets the SMC91xx chip to its normal state, hopefully from whatever
- . 	mess that any other DOS driver has put it in.
- .
- . Maybe I should reset more registers to defaults in here?  SOFTRESET  should
- . do that for me.
- .
- . Method:
- .	1.  send a SOFT RESET
- .	2.  wait for it to finish
- .	3.  enable autorelease mode
- .	4.  reset the memory management unit
- .	5.  clear all interrupts
- .
-*/
-static void smc_reset( int ioaddr )
-{
-	/* This resets the registers mostly to defaults, but doesn't
-	   affect EEPROM.  That seems unnecessary */
-	SMC_SELECT_BANK( 0 );
-	outw( RCR_SOFTRESET, ioaddr + RCR );
-
-	/* this should pause enough for the chip to be happy */
-	SMC_DELAY( );
-
-	/* Set the transmit and receive configuration registers to
-	   default values */
-	outw( RCR_CLEAR, ioaddr + RCR );
-	outw( TCR_CLEAR, ioaddr + TCR );
-
-	/* set the control register to automatically
-	   release successfully transmitted packets, to make the best
-	   use out of our limited memory */
-	SMC_SELECT_BANK( 1 );
-	outw( inw( ioaddr + CONTROL ) | CTL_AUTO_RELEASE , ioaddr + CONTROL );
-
-	/* Reset the MMU */
-	SMC_SELECT_BANK( 2 );
-	outw( MC_RESET, ioaddr + MMU_CMD );
-
-	/* Note:  It doesn't seem that waiting for the MMU busy is needed here,
-	   but this is a place where future chipsets _COULD_ break.  Be wary
-	   of issuing another MMU command right after this */
-
-	outb( 0, ioaddr + INT_MASK );
-}
-
-/*
- . Function: smc_enable
- . Purpose: let the chip talk to the outside work
- . Method:
- .	1.  Enable the transmitter
- .	2.  Enable the receiver
- .	3.  Enable interrupts
-*/
-static void smc_enable( int ioaddr )
-{
-	SMC_SELECT_BANK( 0 );
-	/* see the header file for options in TCR/RCR NORMAL*/
-	outw( TCR_NORMAL, ioaddr + TCR );
-	outw( RCR_NORMAL, ioaddr + RCR );
-
-	/* now, enable interrupts */
-	SMC_SELECT_BANK( 2 );
-	outb( SMC_INTERRUPT_MASK, ioaddr + INT_MASK );
-}
-
-/*
- . Function: smc_shutdown
- . Purpose:  closes down the SMC91xxx chip.
- . Method:
- .	1. zero the interrupt mask
- .	2. clear the enable receive flag
- .	3. clear the enable xmit flags
- .
- . TODO:
- .   (1) maybe utilize power down mode.
- .	Why not yet?  Because while the chip will go into power down mode,
- .	the manual says that it will wake up in response to any I/O requests
- .	in the register space.   Empirical results do not show this working.
-*/
-static void smc_shutdown( int ioaddr )
-{
-	/* no more interrupts for me */
-	SMC_SELECT_BANK( 2 );
-	outb( 0, ioaddr + INT_MASK );
-
-	/* and tell the card to stay away from that nasty outside world */
-	SMC_SELECT_BANK( 0 );
-	outb( RCR_CLEAR, ioaddr + RCR );
-	outb( TCR_CLEAR, ioaddr + TCR );
-#if 0
-	/* finally, shut the chip down */
-	SMC_SELECT_BANK( 1 );
-	outw( inw( ioaddr + CONTROL ), CTL_POWERDOWN, ioaddr + CONTROL  );
-#endif
-}
-
-
-/*
- . Function: smc_setmulticast( int ioaddr, struct net_device *dev )
- . Purpose:
- .    This sets the internal hardware table to filter out unwanted multicast
- .    packets before they take up memory.
- .
- .    The SMC chip uses a hash table where the high 6 bits of the CRC of
- .    address are the offset into the table.  If that bit is 1, then the
- .    multicast packet is accepted.  Otherwise, it's dropped silently.
- .
- .    To use the 6 bits as an offset into the table, the high 3 bits are the
- .    number of the 8 bit register, while the low 3 bits are the bit within
- .    that register.
- .
- . This routine is based very heavily on the one provided by Peter Cammaert.
-*/
-
-
-static void smc_setmulticast(int ioaddr, struct net_device *dev)
-{
-	int			i;
-	unsigned char		multicast_table[ 8 ];
-	struct netdev_hw_addr *ha;
-	/* table for flipping the order of 3 bits */
-	unsigned char invert3[] = { 0, 4, 2, 6, 1, 5, 3, 7 };
-
-	/* start with a table of all zeros: reject all */
-	memset( multicast_table, 0, sizeof( multicast_table ) );
-
-	netdev_for_each_mc_addr(ha, dev) {
-		int position;
-
-		/* only use the low order bits */
-		position = ether_crc_le(6, ha->addr) & 0x3f;
-
-		/* do some messy swapping to put the bit in the right spot */
-		multicast_table[invert3[position&7]] |=
-					(1<<invert3[(position>>3)&7]);
-
-	}
-	/* now, the table can be loaded into the chipset */
-	SMC_SELECT_BANK( 3 );
-
-	for ( i = 0; i < 8 ; i++ ) {
-		outb( multicast_table[i], ioaddr + MULTICAST1 + i );
-	}
-}
-
-/*
- . Function: smc_wait_to_send_packet( struct sk_buff * skb, struct net_device * )
- . Purpose:
- .    Attempt to allocate memory for a packet, if chip-memory is not
- .    available, then tell the card to generate an interrupt when it
- .    is available.
- .
- . Algorithm:
- .
- . o	if the saved_skb is not currently null, then drop this packet
- .	on the floor.  This should never happen, because of TBUSY.
- . o	if the saved_skb is null, then replace it with the current packet,
- . o	See if I can sending it now.
- . o 	(NO): Enable interrupts and let the interrupt handler deal with it.
- . o	(YES):Send it now.
-*/
-static netdev_tx_t smc_wait_to_send_packet(struct sk_buff *skb,
-					   struct net_device *dev)
-{
-	struct smc_local *lp = netdev_priv(dev);
-	unsigned int ioaddr 	= dev->base_addr;
-	word 			length;
-	unsigned short 		numPages;
-	word			time_out;
-
-	netif_stop_queue(dev);
-	/* Well, I want to send the packet.. but I don't know
-	   if I can send it right now...  */
-
-	if ( lp->saved_skb) {
-		/* THIS SHOULD NEVER HAPPEN. */
-		dev->stats.tx_aborted_errors++;
-		printk(CARDNAME": Bad Craziness - sent packet while busy.\n" );
-		return NETDEV_TX_BUSY;
-	}
-	lp->saved_skb = skb;
-
-	length = skb->len;
-
-	if (length < ETH_ZLEN) {
-		if (skb_padto(skb, ETH_ZLEN)) {
-			netif_wake_queue(dev);
-			return NETDEV_TX_OK;
-		}
-		length = ETH_ZLEN;
-	}
-
-	/*
-	** The MMU wants the number of pages to be the number of 256 bytes
-	** 'pages', minus 1 ( since a packet can't ever have 0 pages :) )
-	**
-	** Pkt size for allocating is data length +6 (for additional status words,
-	** length and ctl!) If odd size last byte is included in this header.
-	*/
-	numPages =  ((length & 0xfffe) + 6) / 256;
-
-	if (numPages > 7 ) {
-		printk(CARDNAME": Far too big packet error.\n");
-		/* freeing the packet is a good thing here... but should
-		 . any packets of this size get down here?   */
-		dev_kfree_skb (skb);
-		lp->saved_skb = NULL;
-		/* this IS an error, but, i don't want the skb saved */
-		netif_wake_queue(dev);
-		return NETDEV_TX_OK;
-	}
-	/* either way, a packet is waiting now */
-	lp->packets_waiting++;
-
-	/* now, try to allocate the memory */
-	SMC_SELECT_BANK( 2 );
-	outw( MC_ALLOC | numPages, ioaddr + MMU_CMD );
-	/*
-	. Performance Hack
-	.
-	. wait a short amount of time.. if I can send a packet now, I send
-	. it now.  Otherwise, I enable an interrupt and wait for one to be
-	. available.
-	.
-	. I could have handled this a slightly different way, by checking to
-	. see if any memory was available in the FREE MEMORY register.  However,
-	. either way, I need to generate an allocation, and the allocation works
-	. no matter what, so I saw no point in checking free memory.
-	*/
-	time_out = MEMORY_WAIT_TIME;
-	do {
-		word	status;
-
-		status = inb( ioaddr + INTERRUPT );
-		if ( status & IM_ALLOC_INT ) {
-			/* acknowledge the interrupt */
-			outb( IM_ALLOC_INT, ioaddr + INTERRUPT );
-			break;
-		}
-	} while ( -- time_out );
-
-	if ( !time_out ) {
-		/* oh well, wait until the chip finds memory later */
-		SMC_ENABLE_INT( IM_ALLOC_INT );
-		PRINTK2((CARDNAME": memory allocation deferred.\n"));
-		/* it's deferred, but I'll handle it later */
-		return NETDEV_TX_OK;
-	}
-	/* or YES! I can send the packet now.. */
-	smc_hardware_send_packet(dev);
-	netif_wake_queue(dev);
-	return NETDEV_TX_OK;
-}
-
-/*
- . Function:  smc_hardware_send_packet(struct net_device * )
- . Purpose:
- .	This sends the actual packet to the SMC9xxx chip.
- .
- . Algorithm:
- . 	First, see if a saved_skb is available.
- .		( this should NOT be called if there is no 'saved_skb'
- .	Now, find the packet number that the chip allocated
- .	Point the data pointers at it in memory
- .	Set the length word in the chip's memory
- .	Dump the packet to chip memory
- .	Check if a last byte is needed ( odd length packet )
- .		if so, set the control flag right
- . 	Tell the card to send it
- .	Enable the transmit interrupt, so I know if it failed
- . 	Free the kernel data if I actually sent it.
-*/
-static void smc_hardware_send_packet( struct net_device * dev )
-{
-	struct smc_local *lp = netdev_priv(dev);
-	byte	 		packet_no;
-	struct sk_buff * 	skb = lp->saved_skb;
-	word			length;
-	unsigned int		ioaddr;
-	byte			* buf;
-
-	ioaddr = dev->base_addr;
-
-	if ( !skb ) {
-		PRINTK((CARDNAME": In XMIT with no packet to send\n"));
-		return;
-	}
-	length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
-	buf = skb->data;
-
-	/* If I get here, I _know_ there is a packet slot waiting for me */
-	packet_no = inb( ioaddr + PNR_ARR + 1 );
-	if ( packet_no & 0x80 ) {
-		/* or isn't there?  BAD CHIP! */
-		netdev_dbg(dev, CARDNAME": Memory allocation failed.\n");
-		dev_kfree_skb_any(skb);
-		lp->saved_skb = NULL;
-		netif_wake_queue(dev);
-		return;
-	}
-
-	/* we have a packet address, so tell the card to use it */
-	outb( packet_no, ioaddr + PNR_ARR );
-
-	/* point to the beginning of the packet */
-	outw( PTR_AUTOINC , ioaddr + POINTER );
-
-	PRINTK3((CARDNAME": Trying to xmit packet of length %x\n", length));
-#if SMC_DEBUG > 2
-	print_packet( buf, length );
-#endif
-
-	/* send the packet length ( +6 for status, length and ctl byte )
-	   and the status word ( set to zeros ) */
-#ifdef USE_32_BIT
-	outl(  (length +6 ) << 16 , ioaddr + DATA_1 );
-#else
-	outw( 0, ioaddr + DATA_1 );
-	/* send the packet length ( +6 for status words, length, and ctl*/
-	outb( (length+6) & 0xFF,ioaddr + DATA_1 );
-	outb( (length+6) >> 8 , ioaddr + DATA_1 );
-#endif
-
-	/* send the actual data
-	 . I _think_ it's faster to send the longs first, and then
-	 . mop up by sending the last word.  It depends heavily
-	 . on alignment, at least on the 486.  Maybe it would be
-	 . a good idea to check which is optimal?  But that could take
-	 . almost as much time as is saved?
-	*/
-#ifdef USE_32_BIT
-	if ( length & 0x2  ) {
-		outsl(ioaddr + DATA_1, buf,  length >> 2 );
-		outw( *((word *)(buf + (length & 0xFFFFFFFC))),ioaddr +DATA_1);
-	}
-	else
-		outsl(ioaddr + DATA_1, buf,  length >> 2 );
-#else
-	outsw(ioaddr + DATA_1 , buf, (length ) >> 1);
-#endif
-	/* Send the last byte, if there is one.   */
-
-	if ( (length & 1) == 0 ) {
-		outw( 0, ioaddr + DATA_1 );
-	} else {
-		outb( buf[length -1 ], ioaddr + DATA_1 );
-		outb( 0x20, ioaddr + DATA_1);
-	}
-
-	/* enable the interrupts */
-	SMC_ENABLE_INT( (IM_TX_INT | IM_TX_EMPTY_INT) );
-
-	/* and let the chipset deal with it */
-	outw( MC_ENQUEUE , ioaddr + MMU_CMD );
-
-	PRINTK2((CARDNAME": Sent packet of length %d\n", length));
-
-	lp->saved_skb = NULL;
-	dev_kfree_skb_any (skb);
-
-	netif_trans_update(dev);
-
-	/* we can send another packet */
-	netif_wake_queue(dev);
-}
-
-/*-------------------------------------------------------------------------
- |
- | smc_init(int unit)
- |   Input parameters:
- |	dev->base_addr == 0, try to find all possible locations
- |	dev->base_addr == 1, return failure code
- |	dev->base_addr == 2, always allocate space,  and return success
- |	dev->base_addr == <anything else>   this is the address to check
- |
- |   Output:
- |	pointer to net_device or ERR_PTR(error)
- |
- ---------------------------------------------------------------------------
-*/
-static int io;
-static int irq;
-static int ifport;
-
-struct net_device * __init smc_init(int unit)
-{
-	struct net_device *dev = alloc_etherdev(sizeof(struct smc_local));
-	struct devlist *smcdev = smc_devlist;
-	int err = 0;
-
-	if (!dev)
-		return ERR_PTR(-ENODEV);
-
-	if (unit >= 0) {
-		sprintf(dev->name, "eth%d", unit);
-		netdev_boot_setup_check(dev);
-		io = dev->base_addr;
-		irq = dev->irq;
-	}
-
-	if (io > 0x1ff) {	/* Check a single specified location. */
-		err = smc_probe(dev, io);
-	} else if (io != 0) {	/* Don't probe at all. */
-		err = -ENXIO;
-	} else {
-		for (;smcdev->port; smcdev++) {
-			if (smc_probe(dev, smcdev->port) == 0)
-				break;
-		}
-		if (!smcdev->port)
-			err = -ENODEV;
-	}
-	if (err)
-		goto out;
-	err = register_netdev(dev);
-	if (err)
-		goto out1;
-	return dev;
-out1:
-	free_irq(dev->irq, dev);
-	release_region(dev->base_addr, SMC_IO_EXTENT);
-out:
-	free_netdev(dev);
-	return ERR_PTR(err);
-}
-
-/*----------------------------------------------------------------------
- . smc_findirq
- .
- . This routine has a simple purpose -- make the SMC chip generate an
- . interrupt, so an auto-detect routine can detect it, and find the IRQ,
- ------------------------------------------------------------------------
-*/
-static int __init smc_findirq(int ioaddr)
-{
-#ifndef NO_AUTOPROBE
-	int	timeout = 20;
-	unsigned long cookie;
-
-
-	cookie = probe_irq_on();
-
-	/*
-	 * What I try to do here is trigger an ALLOC_INT. This is done
-	 * by allocating a small chunk of memory, which will give an interrupt
-	 * when done.
-	 */
-
-
-	SMC_SELECT_BANK(2);
-	/* enable ALLOCation interrupts ONLY */
-	outb( IM_ALLOC_INT, ioaddr + INT_MASK );
-
-	/*
-	 . Allocate 512 bytes of memory.  Note that the chip was just
-	 . reset so all the memory is available
-	*/
-	outw( MC_ALLOC | 1, ioaddr + MMU_CMD );
-
-	/*
-	 . Wait until positive that the interrupt has been generated
-	*/
-	while ( timeout ) {
-		byte	int_status;
-
-		int_status = inb( ioaddr + INTERRUPT );
-
-		if ( int_status & IM_ALLOC_INT )
-			break;		/* got the interrupt */
-		timeout--;
-	}
-	/* there is really nothing that I can do here if timeout fails,
-	   as probe_irq_off will return a 0 anyway, which is what I
-	   want in this case.   Plus, the clean up is needed in both
-	   cases.  */
-
-	/* DELAY HERE!
-	   On a fast machine, the status might change before the interrupt
-	   is given to the processor.  This means that the interrupt was
-	   never detected, and probe_irq_off fails to report anything.
-	   This should fix probe_irq_* problems.
-	*/
-	SMC_DELAY();
-	SMC_DELAY();
-
-	/* and disable all interrupts again */
-	outb( 0, ioaddr + INT_MASK );
-
-	/* and return what I found */
-	return probe_irq_off(cookie);
-#else /* NO_AUTOPROBE */
-	struct devlist *smcdev;
-	for (smcdev = smc_devlist; smcdev->port; smcdev++) {
-		if (smcdev->port == ioaddr)
-			return smcdev->irq;
-	}
-	return 0;
-#endif
-}
-
-static const struct net_device_ops smc_netdev_ops = {
-	.ndo_open		 = smc_open,
-	.ndo_stop		= smc_close,
-	.ndo_start_xmit    	= smc_wait_to_send_packet,
-	.ndo_tx_timeout	    	= smc_timeout,
-	.ndo_set_rx_mode	= smc_set_multicast_list,
-	.ndo_set_mac_address 	= eth_mac_addr,
-	.ndo_validate_addr	= eth_validate_addr,
-};
-
-/*----------------------------------------------------------------------
- . Function: smc_probe( int ioaddr )
- .
- . Purpose:
- .	Tests to see if a given ioaddr points to an SMC9xxx chip.
- .	Returns a 0 on success
- .
- . Algorithm:
- .	(1) see if the high byte of BANK_SELECT is 0x33
- . 	(2) compare the ioaddr with the base register's address
- .	(3) see if I recognize the chip ID in the appropriate register
- .
- .---------------------------------------------------------------------
- */
-
-/*---------------------------------------------------------------
- . Here I do typical initialization tasks.
- .
- . o  Initialize the structure if needed
- . o  print out my vanity message if not done so already
- . o  print out what type of hardware is detected
- . o  print out the ethernet address
- . o  find the IRQ
- . o  set up my private data
- . o  configure the dev structure with my subroutines
- . o  actually GRAB the irq.
- . o  GRAB the region
- .-----------------------------------------------------------------
-*/
-static int __init smc_probe(struct net_device *dev, int ioaddr)
-{
-	int i, memory, retval;
-	unsigned int bank;
-
-	const char *version_string;
-	const char *if_string;
-
-	/* registers */
-	word revision_register;
-	word base_address_register;
-	word configuration_register;
-	word memory_info_register;
-	word memory_cfg_register;
-	u8 addr[ETH_ALEN];
-
-	/* Grab the region so that no one else tries to probe our ioports. */
-	if (!request_region(ioaddr, SMC_IO_EXTENT, DRV_NAME))
-		return -EBUSY;
-
-	dev->irq = irq;
-	dev->if_port = ifport;
-
-	/* First, see if the high byte is 0x33 */
-	bank = inw( ioaddr + BANK_SELECT );
-	if ( (bank & 0xFF00) != 0x3300 ) {
-		retval = -ENODEV;
-		goto err_out;
-	}
-	/* The above MIGHT indicate a device, but I need to write to further
-		test this.  */
-	outw( 0x0, ioaddr + BANK_SELECT );
-	bank = inw( ioaddr + BANK_SELECT );
-	if ( (bank & 0xFF00 ) != 0x3300 ) {
-		retval = -ENODEV;
-		goto err_out;
-	}
-	/* well, we've already written once, so hopefully another time won't
-	   hurt.  This time, I need to switch the bank register to bank 1,
-	   so I can access the base address register */
-	SMC_SELECT_BANK(1);
-	base_address_register = inw( ioaddr + BASE );
-	if ( ioaddr != ( base_address_register >> 3 & 0x3E0 ) )  {
-		printk(CARDNAME ": IOADDR %x doesn't match configuration (%x). "
-			"Probably not a SMC chip\n",
-			ioaddr, base_address_register >> 3 & 0x3E0 );
-		/* well, the base address register didn't match.  Must not have
-		   been a SMC chip after all. */
-		retval = -ENODEV;
-		goto err_out;
-	}
-
-	/*  check if the revision register is something that I recognize.
-	    These might need to be added to later, as future revisions
-	    could be added.  */
-	SMC_SELECT_BANK(3);
-	revision_register  = inw( ioaddr + REVISION );
-	if ( !chip_ids[ ( revision_register  >> 4 ) & 0xF  ] ) {
-		/* I don't recognize this chip, so... */
-		printk(CARDNAME ": IO %x: Unrecognized revision register:"
-			" %x, Contact author.\n", ioaddr, revision_register);
-
-		retval = -ENODEV;
-		goto err_out;
-	}
-
-	/* at this point I'll assume that the chip is an SMC9xxx.
-	   It might be prudent to check a listing of MAC addresses
-	   against the hardware address, or do some other tests. */
-
-	pr_info_once("%s\n", version);
-
-	/* fill in some of the fields */
-	dev->base_addr = ioaddr;
-
-	/*
-	 . Get the MAC address ( bank 1, regs 4 - 9 )
-	*/
-	SMC_SELECT_BANK( 1 );
-	for ( i = 0; i < 6; i += 2 ) {
-		word	address;
-
-		address = inw( ioaddr + ADDR0 + i  );
-		addr[i + 1] = address >> 8;
-		addr[i] = address & 0xFF;
-	}
-	eth_hw_addr_set(dev, addr);
-
-	/* get the memory information */
-
-	SMC_SELECT_BANK( 0 );
-	memory_info_register = inw( ioaddr + MIR );
-	memory_cfg_register  = inw( ioaddr + MCR );
-	memory = ( memory_cfg_register >> 9 )  & 0x7;  /* multiplier */
-	memory *= 256 * ( memory_info_register & 0xFF );
-
-	/*
-	 Now, I want to find out more about the chip.  This is sort of
-	 redundant, but it's cleaner to have it in both, rather than having
-	 one VERY long probe procedure.
-	*/
-	SMC_SELECT_BANK(3);
-	revision_register  = inw( ioaddr + REVISION );
-	version_string = chip_ids[ ( revision_register  >> 4 ) & 0xF  ];
-	if ( !version_string ) {
-		/* I shouldn't get here because this call was done before.... */
-		retval = -ENODEV;
-		goto err_out;
-	}
-
-	/* is it using AUI or 10BaseT ? */
-	if ( dev->if_port == 0 ) {
-		SMC_SELECT_BANK(1);
-		configuration_register = inw( ioaddr + CONFIG );
-		if ( configuration_register & CFG_AUI_SELECT )
-			dev->if_port = 2;
-		else
-			dev->if_port = 1;
-	}
-	if_string = interfaces[ dev->if_port - 1 ];
-
-	/* now, reset the chip, and put it into a known state */
-	smc_reset( ioaddr );
-
-	/*
-	 . If dev->irq is 0, then the device has to be banged on to see
-	 . what the IRQ is.
-	 .
-	 . This banging doesn't always detect the IRQ, for unknown reasons.
-	 . a workaround is to reset the chip and try again.
-	 .
-	 . Interestingly, the DOS packet driver *SETS* the IRQ on the card to
-	 . be what is requested on the command line.   I don't do that, mostly
-	 . because the card that I have uses a non-standard method of accessing
-	 . the IRQs, and because this _should_ work in most configurations.
-	 .
-	 . Specifying an IRQ is done with the assumption that the user knows
-	 . what (s)he is doing.  No checking is done!!!!
-	 .
-	*/
-	if ( dev->irq < 2 ) {
-		int	trials;
-
-		trials = 3;
-		while ( trials-- ) {
-			dev->irq = smc_findirq( ioaddr );
-			if ( dev->irq )
-				break;
-			/* kick the card and try again */
-			smc_reset( ioaddr );
-		}
-	}
-	if (dev->irq == 0 ) {
-		printk(CARDNAME": Couldn't autodetect your IRQ. Use irq=xx.\n");
-		retval = -ENODEV;
-		goto err_out;
-	}
-
-	/* now, print out the card info, in a short format.. */
-
-	netdev_info(dev, "%s(r:%d) at %#3x IRQ:%d INTF:%s MEM:%db ",
-		    version_string, revision_register & 0xF, ioaddr, dev->irq,
-		    if_string, memory);
-	/*
-	 . Print the Ethernet address
-	*/
-	netdev_info(dev, "ADDR: %pM\n", dev->dev_addr);
-
-	/* Grab the IRQ */
-	retval = request_irq(dev->irq, smc_interrupt, 0, DRV_NAME, dev);
-	if (retval) {
-		netdev_warn(dev, "%s: unable to get IRQ %d (irqval=%d).\n",
-			    DRV_NAME, dev->irq, retval);
-		goto err_out;
-	}
-
-	dev->netdev_ops			= &smc_netdev_ops;
-	dev->watchdog_timeo		= HZ/20;
-
-	return 0;
-
-err_out:
-	release_region(ioaddr, SMC_IO_EXTENT);
-	return retval;
-}
-
-#if SMC_DEBUG > 2
-static void print_packet( byte * buf, int length )
-{
-#if 0
-	print_hex_dump_debug(DRV_NAME, DUMP_PREFIX_OFFSET, 16, 1,
-			     buf, length, true);
-#endif
-}
-#endif
-
-
-/*
- * Open and Initialize the board
- *
- * Set up everything, reset the card, etc ..
- *
- */
-static int smc_open(struct net_device *dev)
-{
-	int	ioaddr = dev->base_addr;
-
-	int	i;	/* used to set hw ethernet address */
-
-	/* clear out all the junk that was put here before... */
-	memset(netdev_priv(dev), 0, sizeof(struct smc_local));
-
-	/* reset the hardware */
-
-	smc_reset( ioaddr );
-	smc_enable( ioaddr );
-
-	/* Select which interface to use */
-
-	SMC_SELECT_BANK( 1 );
-	if ( dev->if_port == 1 ) {
-		outw( inw( ioaddr + CONFIG ) & ~CFG_AUI_SELECT,
-			ioaddr + CONFIG );
-	}
-	else if ( dev->if_port == 2 ) {
-		outw( inw( ioaddr + CONFIG ) | CFG_AUI_SELECT,
-			ioaddr + CONFIG );
-	}
-
-	/*
-		According to Becker, I have to set the hardware address
-		at this point, because the (l)user can set it with an
-		ioctl.  Easily done...
-	*/
-	SMC_SELECT_BANK( 1 );
-	for ( i = 0; i < 6; i += 2 ) {
-		word	address;
-
-		address = dev->dev_addr[ i + 1 ] << 8 ;
-		address  |= dev->dev_addr[ i ];
-		outw( address, ioaddr + ADDR0 + i );
-	}
-
-	netif_start_queue(dev);
-	return 0;
-}
-
-/*--------------------------------------------------------
- . Called by the kernel to send a packet out into the void
- . of the net.  This routine is largely based on
- . skeleton.c, from Becker.
- .--------------------------------------------------------
-*/
-
-static void smc_timeout(struct net_device *dev, unsigned int txqueue)
-{
-	/* If we get here, some higher level has decided we are broken.
-	   There should really be a "kick me" function call instead. */
-	netdev_warn(dev, CARDNAME": transmit timed out, %s?\n",
-		    tx_done(dev) ? "IRQ conflict" : "network cable problem");
-	/* "kick" the adaptor */
-	smc_reset( dev->base_addr );
-	smc_enable( dev->base_addr );
-	netif_trans_update(dev); /* prevent tx timeout */
-	/* clear anything saved */
-	((struct smc_local *)netdev_priv(dev))->saved_skb = NULL;
-	netif_wake_queue(dev);
-}
-
-/*-------------------------------------------------------------
- .
- . smc_rcv -  receive a packet from the card
- .
- . There is ( at least ) a packet waiting to be read from
- . chip-memory.
- .
- . o Read the status
- . o If an error, record it
- . o otherwise, read in the packet
- --------------------------------------------------------------
-*/
-static void smc_rcv(struct net_device *dev)
-{
-	int 	ioaddr = dev->base_addr;
-	int 	packet_number;
-	word	status;
-	word	packet_length;
-
-	/* assume bank 2 */
-
-	packet_number = inw( ioaddr + FIFO_PORTS );
-
-	if ( packet_number & FP_RXEMPTY ) {
-		/* we got called , but nothing was on the FIFO */
-		PRINTK((CARDNAME ": WARNING: smc_rcv with nothing on FIFO.\n"));
-		/* don't need to restore anything */
-		return;
-	}
-
-	/*  start reading from the start of the packet */
-	outw( PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER );
-
-	/* First two words are status and packet_length */
-	status 		= inw( ioaddr + DATA_1 );
-	packet_length 	= inw( ioaddr + DATA_1 );
-
-	packet_length &= 0x07ff;  /* mask off top bits */
-
-	PRINTK2(("RCV: STATUS %4x LENGTH %4x\n", status, packet_length ));
-	/*
-	 . the packet length contains 3 extra words :
-	 . status, length, and an extra word with an odd byte .
-	*/
-	packet_length -= 6;
-
-	if ( !(status & RS_ERRORS ) ){
-		/* do stuff to make a new packet */
-		struct sk_buff  * skb;
-		byte		* data;
-
-		/* read one extra byte */
-		if ( status & RS_ODDFRAME )
-			packet_length++;
-
-		/* set multicast stats */
-		if ( status & RS_MULTICAST )
-			dev->stats.multicast++;
-
-		skb = netdev_alloc_skb(dev, packet_length + 5);
-		if ( skb == NULL ) {
-			dev->stats.rx_dropped++;
-			goto done;
-		}
-
-		/*
-		 ! This should work without alignment, but it could be
-		 ! in the worse case
-		*/
-
-		skb_reserve( skb, 2 );   /* 16 bit alignment */
-
-		data = skb_put( skb, packet_length);
-
-#ifdef USE_32_BIT
-		/* QUESTION:  Like in the TX routine, do I want
-		   to send the DWORDs or the bytes first, or some
-		   mixture.  A mixture might improve already slow PIO
-		   performance  */
-		PRINTK3((" Reading %d dwords (and %d bytes)\n",
-			packet_length >> 2, packet_length & 3 ));
-		insl(ioaddr + DATA_1 , data, packet_length >> 2 );
-		/* read the left over bytes */
-		insb( ioaddr + DATA_1, data + (packet_length & 0xFFFFFC),
-			packet_length & 0x3  );
-#else
-		PRINTK3((" Reading %d words and %d byte(s)\n",
-			(packet_length >> 1 ), packet_length & 1 ));
-		insw(ioaddr + DATA_1 , data, packet_length >> 1);
-		if ( packet_length & 1 ) {
-			data += packet_length & ~1;
-			*(data++) = inb( ioaddr + DATA_1 );
-		}
-#endif
-#if	SMC_DEBUG > 2
-			print_packet( data, packet_length );
-#endif
-
-		skb->protocol = eth_type_trans(skb, dev );
-		netif_rx(skb);
-		dev->stats.rx_packets++;
-		dev->stats.rx_bytes += packet_length;
-	} else {
-		/* error ... */
-		dev->stats.rx_errors++;
-
-		if ( status & RS_ALGNERR )  dev->stats.rx_frame_errors++;
-		if ( status & (RS_TOOSHORT | RS_TOOLONG ) )
-			dev->stats.rx_length_errors++;
-		if ( status & RS_BADCRC)	dev->stats.rx_crc_errors++;
-	}
-
-done:
-	/*  error or good, tell the card to get rid of this packet */
-	outw( MC_RELEASE, ioaddr + MMU_CMD );
-}
-
-
-/*************************************************************************
- . smc_tx
- .
- . Purpose:  Handle a transmit error message.   This will only be called
- .   when an error, because of the AUTO_RELEASE mode.
- .
- . Algorithm:
- .	Save pointer and packet no
- .	Get the packet no from the top of the queue
- .	check if it's valid ( if not, is this an error??? )
- .	read the status word
- .	record the error
- .	( resend?  Not really, since we don't want old packets around )
- .	Restore saved values
- ************************************************************************/
-static void smc_tx( struct net_device * dev )
-{
-	int	ioaddr = dev->base_addr;
-	struct smc_local *lp = netdev_priv(dev);
-	byte saved_packet;
-	byte packet_no;
-	word tx_status;
-
-
-	/* assume bank 2  */
-
-	saved_packet = inb( ioaddr + PNR_ARR );
-	packet_no = inw( ioaddr + FIFO_PORTS );
-	packet_no &= 0x7F;
-
-	/* select this as the packet to read from */
-	outb( packet_no, ioaddr + PNR_ARR );
-
-	/* read the first word from this packet */
-	outw( PTR_AUTOINC | PTR_READ, ioaddr + POINTER );
-
-	tx_status = inw( ioaddr + DATA_1 );
-	PRINTK3((CARDNAME": TX DONE STATUS: %4x\n", tx_status));
-
-	dev->stats.tx_errors++;
-	if ( tx_status & TS_LOSTCAR ) dev->stats.tx_carrier_errors++;
-	if ( tx_status & TS_LATCOL  ) {
-		netdev_dbg(dev, CARDNAME": Late collision occurred on last xmit.\n");
-		dev->stats.tx_window_errors++;
-	}
-#if 0
-		if ( tx_status & TS_16COL ) { ... }
-#endif
-
-	if ( tx_status & TS_SUCCESS ) {
-		netdev_info(dev, CARDNAME": Successful packet caused interrupt\n");
-	}
-	/* re-enable transmit */
-	SMC_SELECT_BANK( 0 );
-	outw( inw( ioaddr + TCR ) | TCR_ENABLE, ioaddr + TCR );
-
-	/* kill the packet */
-	SMC_SELECT_BANK( 2 );
-	outw( MC_FREEPKT, ioaddr + MMU_CMD );
-
-	/* one less packet waiting for me */
-	lp->packets_waiting--;
-
-	outb( saved_packet, ioaddr + PNR_ARR );
-}
-
-/*--------------------------------------------------------------------
- .
- . This is the main routine of the driver, to handle the device when
- . it needs some attention.
- .
- . So:
- .   first, save state of the chipset
- .   branch off into routines to handle each case, and acknowledge
- .	    each to the interrupt register
- .   and finally restore state.
- .
- ---------------------------------------------------------------------*/
-
-static irqreturn_t smc_interrupt(int irq, void * dev_id)
-{
-	struct net_device *dev 	= dev_id;
-	int ioaddr 		= dev->base_addr;
-	struct smc_local *lp = netdev_priv(dev);
-
-	byte	status;
-	word	card_stats;
-	byte	mask;
-	int	timeout;
-	/* state registers */
-	word	saved_bank;
-	word	saved_pointer;
-	int handled = 0;
-
-
-	PRINTK3((CARDNAME": SMC interrupt started\n"));
-
-	saved_bank = inw( ioaddr + BANK_SELECT );
-
-	SMC_SELECT_BANK(2);
-	saved_pointer = inw( ioaddr + POINTER );
-
-	mask = inb( ioaddr + INT_MASK );
-	/* clear all interrupts */
-	outb( 0, ioaddr + INT_MASK );
-
-
-	/* set a timeout value, so I don't stay here forever */
-	timeout = 4;
-
-	PRINTK2((KERN_WARNING CARDNAME ": MASK IS %x\n", mask));
-	do {
-		/* read the status flag, and mask it */
-		status = inb( ioaddr + INTERRUPT ) & mask;
-		if (!status )
-			break;
-
-		handled = 1;
-
-		PRINTK3((KERN_WARNING CARDNAME
-			": Handling interrupt status %x\n", status));
-
-		if (status & IM_RCV_INT) {
-			/* Got a packet(s). */
-			PRINTK2((KERN_WARNING CARDNAME
-				": Receive Interrupt\n"));
-			smc_rcv(dev);
-		} else if (status & IM_TX_INT ) {
-			PRINTK2((KERN_WARNING CARDNAME
-				": TX ERROR handled\n"));
-			smc_tx(dev);
-			outb(IM_TX_INT, ioaddr + INTERRUPT );
-		} else if (status & IM_TX_EMPTY_INT ) {
-			/* update stats */
-			SMC_SELECT_BANK( 0 );
-			card_stats = inw( ioaddr + COUNTER );
-			/* single collisions */
-			dev->stats.collisions += card_stats & 0xF;
-			card_stats >>= 4;
-			/* multiple collisions */
-			dev->stats.collisions += card_stats & 0xF;
-
-			/* these are for when linux supports these statistics */
-
-			SMC_SELECT_BANK( 2 );
-			PRINTK2((KERN_WARNING CARDNAME
-				": TX_BUFFER_EMPTY handled\n"));
-			outb( IM_TX_EMPTY_INT, ioaddr + INTERRUPT );
-			mask &= ~IM_TX_EMPTY_INT;
-			dev->stats.tx_packets += lp->packets_waiting;
-			lp->packets_waiting = 0;
-
-		} else if (status & IM_ALLOC_INT ) {
-			PRINTK2((KERN_DEBUG CARDNAME
-				": Allocation interrupt\n"));
-			/* clear this interrupt so it doesn't happen again */
-			mask &= ~IM_ALLOC_INT;
-
-			smc_hardware_send_packet( dev );
-
-			/* enable xmit interrupts based on this */
-			mask |= ( IM_TX_EMPTY_INT | IM_TX_INT );
-
-			/* and let the card send more packets to me */
-			netif_wake_queue(dev);
-
-			PRINTK2((CARDNAME": Handoff done successfully.\n"));
-		} else if (status & IM_RX_OVRN_INT ) {
-			dev->stats.rx_errors++;
-			dev->stats.rx_fifo_errors++;
-			outb( IM_RX_OVRN_INT, ioaddr + INTERRUPT );
-		} else if (status & IM_EPH_INT ) {
-			PRINTK((CARDNAME ": UNSUPPORTED: EPH INTERRUPT\n"));
-		} else if (status & IM_ERCV_INT ) {
-			PRINTK((CARDNAME ": UNSUPPORTED: ERCV INTERRUPT\n"));
-			outb( IM_ERCV_INT, ioaddr + INTERRUPT );
-		}
-	} while ( timeout -- );
-
-
-	/* restore state register */
-	SMC_SELECT_BANK( 2 );
-	outb( mask, ioaddr + INT_MASK );
-
-	PRINTK3((KERN_WARNING CARDNAME ": MASK is now %x\n", mask));
-	outw( saved_pointer, ioaddr + POINTER );
-
-	SMC_SELECT_BANK( saved_bank );
-
-	PRINTK3((CARDNAME ": Interrupt done\n"));
-	return IRQ_RETVAL(handled);
-}
-
-
-/*----------------------------------------------------
- . smc_close
- .
- . this makes the board clean up everything that it can
- . and not talk to the outside world.   Caused by
- . an 'ifconfig ethX down'
- .
- -----------------------------------------------------*/
-static int smc_close(struct net_device *dev)
-{
-	netif_stop_queue(dev);
-	/* clear everything */
-	smc_shutdown( dev->base_addr );
-
-	/* Update the statistics here. */
-	return 0;
-}
-
-/*-----------------------------------------------------------
- . smc_set_multicast_list
- .
- . This routine will, depending on the values passed to it,
- . either make it accept multicast packets, go into
- . promiscuous mode ( for TCPDUMP and cousins ) or accept
- . a select set of multicast packets
-*/
-static void smc_set_multicast_list(struct net_device *dev)
-{
-	short ioaddr = dev->base_addr;
-
-	SMC_SELECT_BANK(0);
-	if ( dev->flags & IFF_PROMISC )
-		outw( inw(ioaddr + RCR ) | RCR_PROMISC, ioaddr + RCR );
-
-/* BUG?  I never disable promiscuous mode if multicasting was turned on.
-   Now, I turn off promiscuous mode, but I don't do anything to multicasting
-   when promiscuous mode is turned on.
-*/
-
-	/* Here, I am setting this to accept all multicast packets.
-	   I don't need to zero the multicast table, because the flag is
-	   checked before the table is
-	*/
-	else if (dev->flags & IFF_ALLMULTI)
-		outw( inw(ioaddr + RCR ) | RCR_ALMUL, ioaddr + RCR );
-
-	/* We just get all multicast packets even if we only want them
-	 . from one source.  This will be changed at some future
-	 . point. */
-	else if (!netdev_mc_empty(dev)) {
-		/* support hardware multicasting */
-
-		/* be sure I get rid of flags I might have set */
-		outw( inw( ioaddr + RCR ) & ~(RCR_PROMISC | RCR_ALMUL),
-			ioaddr + RCR );
-		/* NOTE: this has to set the bank, so make sure it is the
-		   last thing called.  The bank is set to zero at the top */
-		smc_setmulticast(ioaddr, dev);
-	}
-	else  {
-		outw( inw( ioaddr + RCR ) & ~(RCR_PROMISC | RCR_ALMUL),
-			ioaddr + RCR );
-
-		/*
-		  since I'm disabling all multicast entirely, I need to
-		  clear the multicast list
-		*/
-		SMC_SELECT_BANK( 3 );
-		outw( 0, ioaddr + MULTICAST1 );
-		outw( 0, ioaddr + MULTICAST2 );
-		outw( 0, ioaddr + MULTICAST3 );
-		outw( 0, ioaddr + MULTICAST4 );
-	}
-}
-
-#ifdef MODULE
-
-static struct net_device *devSMC9194;
-MODULE_DESCRIPTION("SMC 9194 Ethernet driver");
-MODULE_LICENSE("GPL");
-
-module_param_hw(io, int, ioport, 0);
-module_param_hw(irq, int, irq, 0);
-module_param(ifport, int, 0);
-MODULE_PARM_DESC(io, "SMC 99194 I/O base address");
-MODULE_PARM_DESC(irq, "SMC 99194 IRQ number");
-MODULE_PARM_DESC(ifport, "SMC 99194 interface port (0-default, 1-TP, 2-AUI)");
-
-static int __init smc_init_module(void)
-{
-	if (io == 0)
-		printk(KERN_WARNING
-		CARDNAME": You shouldn't use auto-probing with insmod!\n" );
-
-	/* copy the parameters from insmod into the device structure */
-	devSMC9194 = smc_init(-1);
-	return PTR_ERR_OR_ZERO(devSMC9194);
-}
-module_init(smc_init_module);
-
-static void __exit smc_cleanup_module(void)
-{
-	unregister_netdev(devSMC9194);
-	free_irq(devSMC9194->irq, devSMC9194);
-	release_region(devSMC9194->base_addr, SMC_IO_EXTENT);
-	free_netdev(devSMC9194);
-}
-module_exit(smc_cleanup_module);
-
-#endif /* MODULE */

-- 
2.53.0


^ permalink raw reply related

* [PATCH net v2 06/15] drivers: net: amd: nmclan: Remove this driver
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
  Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
	Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
In-Reply-To: <20260422-v7-0-0-net-next-driver-removal-v1-v2-0-08a5b59784d5@lunn.ch>

The nmclan was written by Roger C Pao in 1995. It is an PCMCIA device,
so unlikely to be used with modern kernels.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
 drivers/net/ethernet/amd/Kconfig     |   10 -
 drivers/net/ethernet/amd/Makefile    |    1 -
 drivers/net/ethernet/amd/nmclan_cs.c | 1508 ----------------------------------
 3 files changed, 1519 deletions(-)

diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig
index c5abb81977dd..e35991141a1a 100644
--- a/drivers/net/ethernet/amd/Kconfig
+++ b/drivers/net/ethernet/amd/Kconfig
@@ -109,16 +109,6 @@ config MVME147_NET
 	  driver for this chip in your kernel.
 	  To compile this driver as a module, choose M here.
 
-config PCMCIA_NMCLAN
-	tristate "New Media PCMCIA support"
-	depends on PCMCIA && HAS_IOPORT
-	help
-	  Say Y here if you intend to attach a New Media Ethernet or LiveWire
-	  PCMCIA (PC-card) Ethernet card to your computer.
-
-	  To compile this driver as a module, choose M here: the module will be
-	  called nmclan_cs.  If unsure, say N.
-
 config SUN3LANCE
 	tristate "Sun3/Sun3x on-board LANCE support"
 	depends on (SUN3 || SUN3X)
diff --git a/drivers/net/ethernet/amd/Makefile b/drivers/net/ethernet/amd/Makefile
index f261501f7324..e485fae235a7 100644
--- a/drivers/net/ethernet/amd/Makefile
+++ b/drivers/net/ethernet/amd/Makefile
@@ -11,7 +11,6 @@ obj-$(CONFIG_DECLANCE) += declance.o
 obj-$(CONFIG_HPLANCE) += hplance.o 7990.o
 obj-$(CONFIG_MIPS_AU1X00_ENET) += au1000_eth.o
 obj-$(CONFIG_MVME147_NET) += mvme147.o 7990.o
-obj-$(CONFIG_PCMCIA_NMCLAN) += nmclan_cs.o
 obj-$(CONFIG_PCNET32) += pcnet32.o
 obj-$(CONFIG_SUN3LANCE) += sun3lance.o
 obj-$(CONFIG_SUNLANCE) += sunlance.o
diff --git a/drivers/net/ethernet/amd/nmclan_cs.c b/drivers/net/ethernet/amd/nmclan_cs.c
deleted file mode 100644
index 37054a670407..000000000000
--- a/drivers/net/ethernet/amd/nmclan_cs.c
+++ /dev/null
@@ -1,1508 +0,0 @@
-/* ----------------------------------------------------------------------------
-Linux PCMCIA ethernet adapter driver for the New Media Ethernet LAN.
-  nmclan_cs.c,v 0.16 1995/07/01 06:42:17 rpao Exp rpao
-
-  The Ethernet LAN uses the Advanced Micro Devices (AMD) Am79C940 Media
-  Access Controller for Ethernet (MACE).  It is essentially the Am2150
-  PCMCIA Ethernet card contained in the Am2150 Demo Kit.
-
-Written by Roger C. Pao <rpao@paonet.org>
-  Copyright 1995 Roger C. Pao
-  Linux 2.5 cleanups Copyright Red Hat 2003
-
-  This software may be used and distributed according to the terms of
-  the GNU General Public License.
-
-Ported to Linux 1.3.* network driver environment by
-  Matti Aarnio <mea@utu.fi>
-
-References
-
-  Am2150 Technical Reference Manual, Revision 1.0, August 17, 1993
-  Am79C940 (MACE) Data Sheet, 1994
-  Am79C90 (C-LANCE) Data Sheet, 1994
-  Linux PCMCIA Programmer's Guide v1.17
-  /usr/src/linux/net/inet/dev.c, Linux kernel 1.2.8
-
-  Eric Mears, New Media Corporation
-  Tom Pollard, New Media Corporation
-  Dean Siasoyco, New Media Corporation
-  Ken Lesniak, Silicon Graphics, Inc. <lesniak@boston.sgi.com>
-  Donald Becker <becker@scyld.com>
-  David Hinds <dahinds@users.sourceforge.net>
-
-  The Linux client driver is based on the 3c589_cs.c client driver by
-  David Hinds.
-
-  The Linux network driver outline is based on the 3c589_cs.c driver,
-  the 8390.c driver, and the example skeleton.c kernel code, which are
-  by Donald Becker.
-
-  The Am2150 network driver hardware interface code is based on the
-  OS/9000 driver for the New Media Ethernet LAN by Eric Mears.
-
-  Special thanks for testing and help in debugging this driver goes
-  to Ken Lesniak.
-
--------------------------------------------------------------------------------
-Driver Notes and Issues
--------------------------------------------------------------------------------
-
-1. Developed on a Dell 320SLi
-   PCMCIA Card Services 2.6.2
-   Linux dell 1.2.10 #1 Thu Jun 29 20:23:41 PDT 1995 i386
-
-2. rc.pcmcia may require loading pcmcia_core with io_speed=300:
-   'insmod pcmcia_core.o io_speed=300'.
-   This will avoid problems with fast systems which causes rx_framecnt
-   to return random values.
-
-3. If hot extraction does not work for you, use 'ifconfig eth0 down'
-   before extraction.
-
-4. There is a bad slow-down problem in this driver.
-
-5. Future: Multicast processing.  In the meantime, do _not_ compile your
-   kernel with multicast ip enabled.
-
--------------------------------------------------------------------------------
-History
--------------------------------------------------------------------------------
-Log: nmclan_cs.c,v
- * 2.5.75-ac1 2003/07/11 Alan Cox <alan@lxorguk.ukuu.org.uk>
- * Fixed hang on card eject as we probe it
- * Cleaned up to use new style locking.
- *
- * Revision 0.16  1995/07/01  06:42:17  rpao
- * Bug fix: nmclan_reset() called CardServices incorrectly.
- *
- * Revision 0.15  1995/05/24  08:09:47  rpao
- * Re-implement MULTI_TX dev->tbusy handling.
- *
- * Revision 0.14  1995/05/23  03:19:30  rpao
- * Added, in nmclan_config(), "tuple.Attributes = 0;".
- * Modified MACE ID check to ignore chip revision level.
- * Avoid tx_free_frames race condition between _start_xmit and _interrupt.
- *
- * Revision 0.13  1995/05/18  05:56:34  rpao
- * Statistics changes.
- * Bug fix: nmclan_reset did not enable TX and RX: call restore_multicast_list.
- * Bug fix: mace_interrupt checks ~MACE_IMR_DEFAULT.  Fixes driver lockup.
- *
- * Revision 0.12  1995/05/14  00:12:23  rpao
- * Statistics overhaul.
- *
-
-95/05/13 rpao	V0.10a
-		Bug fix: MACE statistics counters used wrong I/O ports.
-		Bug fix: mace_interrupt() needed to allow statistics to be
-		processed without RX or TX interrupts pending.
-95/05/11 rpao	V0.10
-		Multiple transmit request processing.
-		Modified statistics to use MACE counters where possible.
-95/05/10 rpao	V0.09 Bug fix: Must use IO_DATA_PATH_WIDTH_AUTO.
-		*Released
-95/05/10 rpao	V0.08
-		Bug fix: Make all non-exported functions private by using
-		static keyword.
-		Bug fix: Test IntrCnt _before_ reading MACE_IR.
-95/05/10 rpao	V0.07 Statistics.
-95/05/09 rpao	V0.06 Fix rx_framecnt problem by addition of PCIC wait states.
-
----------------------------------------------------------------------------- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#define DRV_NAME	"nmclan_cs"
-
-/* ----------------------------------------------------------------------------
-Conditional Compilation Options
----------------------------------------------------------------------------- */
-
-#define MULTI_TX			0
-#define RESET_ON_TIMEOUT		1
-#define TX_INTERRUPTABLE		1
-#define RESET_XILINX			0
-
-/* ----------------------------------------------------------------------------
-Include Files
----------------------------------------------------------------------------- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/ptrace.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/in.h>
-#include <linux/delay.h>
-#include <linux/ethtool.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-#include <linux/ioport.h>
-#include <linux/bitops.h>
-
-#include <pcmcia/cisreg.h>
-#include <pcmcia/cistpl.h>
-#include <pcmcia/ds.h>
-
-#include <linux/uaccess.h>
-#include <asm/io.h>
-
-/* ----------------------------------------------------------------------------
-Defines
----------------------------------------------------------------------------- */
-
-#define MACE_LADRF_LEN			8
-					/* 8 bytes in Logical Address Filter */
-
-/* Loop Control Defines */
-#define MACE_MAX_IR_ITERATIONS		10
-#define MACE_MAX_RX_ITERATIONS		12
-	/*
-	TBD: Dean brought this up, and I assumed the hardware would
-	handle it:
-
-	If MACE_MAX_RX_ITERATIONS is > 1, rx_framecnt may still be
-	non-zero when the isr exits.  We may not get another interrupt
-	to process the remaining packets for some time.
-	*/
-
-/*
-The Am2150 has a Xilinx XC3042 field programmable gate array (FPGA)
-which manages the interface between the MACE and the PCMCIA bus.  It
-also includes buffer management for the 32K x 8 SRAM to control up to
-four transmit and 12 receive frames at a time.
-*/
-#define AM2150_MAX_TX_FRAMES		4
-#define AM2150_MAX_RX_FRAMES		12
-
-/* Am2150 Ethernet Card I/O Mapping */
-#define AM2150_RCV			0x00
-#define AM2150_XMT			0x04
-#define AM2150_XMT_SKIP			0x09
-#define AM2150_RCV_NEXT			0x0A
-#define AM2150_RCV_FRAME_COUNT		0x0B
-#define AM2150_MACE_BANK		0x0C
-#define AM2150_MACE_BASE		0x10
-
-/* MACE Registers */
-#define MACE_RCVFIFO			0
-#define MACE_XMTFIFO			1
-#define MACE_XMTFC			2
-#define MACE_XMTFS			3
-#define MACE_XMTRC			4
-#define MACE_RCVFC			5
-#define MACE_RCVFS			6
-#define MACE_FIFOFC			7
-#define MACE_IR				8
-#define MACE_IMR			9
-#define MACE_PR				10
-#define MACE_BIUCC			11
-#define MACE_FIFOCC			12
-#define MACE_MACCC			13
-#define MACE_PLSCC			14
-#define MACE_PHYCC			15
-#define MACE_CHIPIDL			16
-#define MACE_CHIPIDH			17
-#define MACE_IAC			18
-/* Reserved */
-#define MACE_LADRF			20
-#define MACE_PADR			21
-/* Reserved */
-/* Reserved */
-#define MACE_MPC			24
-/* Reserved */
-#define MACE_RNTPC			26
-#define MACE_RCVCC			27
-/* Reserved */
-#define MACE_UTR			29
-#define MACE_RTR1			30
-#define MACE_RTR2			31
-
-/* MACE Bit Masks */
-#define MACE_XMTRC_EXDEF		0x80
-#define MACE_XMTRC_XMTRC		0x0F
-
-#define MACE_XMTFS_XMTSV		0x80
-#define MACE_XMTFS_UFLO			0x40
-#define MACE_XMTFS_LCOL			0x20
-#define MACE_XMTFS_MORE			0x10
-#define MACE_XMTFS_ONE			0x08
-#define MACE_XMTFS_DEFER		0x04
-#define MACE_XMTFS_LCAR			0x02
-#define MACE_XMTFS_RTRY			0x01
-
-#define MACE_RCVFS_RCVSTS		0xF000
-#define MACE_RCVFS_OFLO			0x8000
-#define MACE_RCVFS_CLSN			0x4000
-#define MACE_RCVFS_FRAM			0x2000
-#define MACE_RCVFS_FCS			0x1000
-
-#define MACE_FIFOFC_RCVFC		0xF0
-#define MACE_FIFOFC_XMTFC		0x0F
-
-#define MACE_IR_JAB			0x80
-#define MACE_IR_BABL			0x40
-#define MACE_IR_CERR			0x20
-#define MACE_IR_RCVCCO			0x10
-#define MACE_IR_RNTPCO			0x08
-#define MACE_IR_MPCO			0x04
-#define MACE_IR_RCVINT			0x02
-#define MACE_IR_XMTINT			0x01
-
-#define MACE_MACCC_PROM			0x80
-#define MACE_MACCC_DXMT2PD		0x40
-#define MACE_MACCC_EMBA			0x20
-#define MACE_MACCC_RESERVED		0x10
-#define MACE_MACCC_DRCVPA		0x08
-#define MACE_MACCC_DRCVBC		0x04
-#define MACE_MACCC_ENXMT		0x02
-#define MACE_MACCC_ENRCV		0x01
-
-#define MACE_PHYCC_LNKFL		0x80
-#define MACE_PHYCC_DLNKTST		0x40
-#define MACE_PHYCC_REVPOL		0x20
-#define MACE_PHYCC_DAPC			0x10
-#define MACE_PHYCC_LRT			0x08
-#define MACE_PHYCC_ASEL			0x04
-#define MACE_PHYCC_RWAKE		0x02
-#define MACE_PHYCC_AWAKE		0x01
-
-#define MACE_IAC_ADDRCHG		0x80
-#define MACE_IAC_PHYADDR		0x04
-#define MACE_IAC_LOGADDR		0x02
-
-#define MACE_UTR_RTRE			0x80
-#define MACE_UTR_RTRD			0x40
-#define MACE_UTR_RPA			0x20
-#define MACE_UTR_FCOLL			0x10
-#define MACE_UTR_RCVFCSE		0x08
-#define MACE_UTR_LOOP_INCL_MENDEC	0x06
-#define MACE_UTR_LOOP_NO_MENDEC		0x04
-#define MACE_UTR_LOOP_EXTERNAL		0x02
-#define MACE_UTR_LOOP_NONE		0x00
-#define MACE_UTR_RESERVED		0x01
-
-/* Switch MACE register bank (only 0 and 1 are valid) */
-#define MACEBANK(win_num) outb((win_num), ioaddr + AM2150_MACE_BANK)
-
-#define MACE_IMR_DEFAULT \
-  (0xFF - \
-    ( \
-      MACE_IR_CERR | \
-      MACE_IR_RCVCCO | \
-      MACE_IR_RNTPCO | \
-      MACE_IR_MPCO | \
-      MACE_IR_RCVINT | \
-      MACE_IR_XMTINT \
-    ) \
-  )
-#undef MACE_IMR_DEFAULT
-#define MACE_IMR_DEFAULT 0x00 /* New statistics handling: grab everything */
-
-#define TX_TIMEOUT		((400*HZ)/1000)
-
-/* ----------------------------------------------------------------------------
-Type Definitions
----------------------------------------------------------------------------- */
-
-typedef struct _mace_statistics {
-    /* MACE_XMTFS */
-    int xmtsv;
-    int uflo;
-    int lcol;
-    int more;
-    int one;
-    int defer;
-    int lcar;
-    int rtry;
-
-    /* MACE_XMTRC */
-    int exdef;
-    int xmtrc;
-
-    /* RFS1--Receive Status (RCVSTS) */
-    int oflo;
-    int clsn;
-    int fram;
-    int fcs;
-
-    /* RFS2--Runt Packet Count (RNTPC) */
-    int rfs_rntpc;
-
-    /* RFS3--Receive Collision Count (RCVCC) */
-    int rfs_rcvcc;
-
-    /* MACE_IR */
-    int jab;
-    int babl;
-    int cerr;
-    int rcvcco;
-    int rntpco;
-    int mpco;
-
-    /* MACE_MPC */
-    int mpc;
-
-    /* MACE_RNTPC */
-    int rntpc;
-
-    /* MACE_RCVCC */
-    int rcvcc;
-} mace_statistics;
-
-typedef struct _mace_private {
-	struct pcmcia_device	*p_dev;
-    mace_statistics mace_stats; /* MACE chip statistics counters */
-
-    /* restore_multicast_list() state variables */
-    int multicast_ladrf[MACE_LADRF_LEN]; /* Logical address filter */
-    int multicast_num_addrs;
-
-    char tx_free_frames; /* Number of free transmit frame buffers */
-    char tx_irq_disabled; /* MACE TX interrupt disabled */
-
-    spinlock_t bank_lock; /* Must be held if you step off bank 0 */
-} mace_private;
-
-/* ----------------------------------------------------------------------------
-Private Global Variables
----------------------------------------------------------------------------- */
-
-static const char *if_names[]={
-    "Auto", "10baseT", "BNC",
-};
-
-/* ----------------------------------------------------------------------------
-Parameters
-	These are the parameters that can be set during loading with
-	'insmod'.
----------------------------------------------------------------------------- */
-
-MODULE_DESCRIPTION("New Media PCMCIA ethernet driver");
-MODULE_LICENSE("GPL");
-
-#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
-
-/* 0=auto, 1=10baseT, 2 = 10base2, default=auto */
-INT_MODULE_PARM(if_port, 0);
-
-
-/* ----------------------------------------------------------------------------
-Function Prototypes
----------------------------------------------------------------------------- */
-
-static int nmclan_config(struct pcmcia_device *link);
-static void nmclan_release(struct pcmcia_device *link);
-
-static void nmclan_reset(struct net_device *dev);
-static int mace_config(struct net_device *dev, struct ifmap *map);
-static int mace_open(struct net_device *dev);
-static int mace_close(struct net_device *dev);
-static netdev_tx_t mace_start_xmit(struct sk_buff *skb,
-					 struct net_device *dev);
-static void mace_tx_timeout(struct net_device *dev, unsigned int txqueue);
-static irqreturn_t mace_interrupt(int irq, void *dev_id);
-static struct net_device_stats *mace_get_stats(struct net_device *dev);
-static int mace_rx(struct net_device *dev, unsigned char RxCnt);
-static void restore_multicast_list(struct net_device *dev);
-static void set_multicast_list(struct net_device *dev);
-static const struct ethtool_ops netdev_ethtool_ops;
-
-
-static void nmclan_detach(struct pcmcia_device *p_dev);
-
-static const struct net_device_ops mace_netdev_ops = {
-	.ndo_open		= mace_open,
-	.ndo_stop		= mace_close,
-	.ndo_start_xmit		= mace_start_xmit,
-	.ndo_tx_timeout		= mace_tx_timeout,
-	.ndo_set_config		= mace_config,
-	.ndo_get_stats		= mace_get_stats,
-	.ndo_set_rx_mode	= set_multicast_list,
-	.ndo_set_mac_address 	= eth_mac_addr,
-	.ndo_validate_addr	= eth_validate_addr,
-};
-
-static int nmclan_probe(struct pcmcia_device *link)
-{
-    mace_private *lp;
-    struct net_device *dev;
-
-    dev_dbg(&link->dev, "nmclan_attach()\n");
-
-    /* Create new ethernet device */
-    dev = alloc_etherdev(sizeof(mace_private));
-    if (!dev)
-	    return -ENOMEM;
-    lp = netdev_priv(dev);
-    lp->p_dev = link;
-    link->priv = dev;
-
-    spin_lock_init(&lp->bank_lock);
-    link->resource[0]->end = 32;
-    link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
-    link->config_flags |= CONF_ENABLE_IRQ;
-    link->config_index = 1;
-    link->config_regs = PRESENT_OPTION;
-
-    lp->tx_free_frames=AM2150_MAX_TX_FRAMES;
-
-    dev->netdev_ops = &mace_netdev_ops;
-    dev->ethtool_ops = &netdev_ethtool_ops;
-    dev->watchdog_timeo = TX_TIMEOUT;
-
-    return nmclan_config(link);
-} /* nmclan_attach */
-
-static void nmclan_detach(struct pcmcia_device *link)
-{
-    struct net_device *dev = link->priv;
-
-    dev_dbg(&link->dev, "nmclan_detach\n");
-
-    unregister_netdev(dev);
-
-    nmclan_release(link);
-
-    free_netdev(dev);
-} /* nmclan_detach */
-
-/* ----------------------------------------------------------------------------
-mace_read
-	Reads a MACE register.  This is bank independent; however, the
-	caller must ensure that this call is not interruptable.  We are
-	assuming that during normal operation, the MACE is always in
-	bank 0.
----------------------------------------------------------------------------- */
-static int mace_read(mace_private *lp, unsigned int ioaddr, int reg)
-{
-  int data = 0xFF;
-  unsigned long flags;
-
-  switch (reg >> 4) {
-  case 0: /* register 0-15 */
-      data = inb(ioaddr + AM2150_MACE_BASE + reg);
-      break;
-  case 1: /* register 16-31 */
-      spin_lock_irqsave(&lp->bank_lock, flags);
-      MACEBANK(1);
-      data = inb(ioaddr + AM2150_MACE_BASE + (reg & 0x0F));
-      MACEBANK(0);
-      spin_unlock_irqrestore(&lp->bank_lock, flags);
-      break;
-  }
-  return data & 0xFF;
-} /* mace_read */
-
-/* ----------------------------------------------------------------------------
-mace_write
-	Writes to a MACE register.  This is bank independent; however,
-	the caller must ensure that this call is not interruptable.  We
-	are assuming that during normal operation, the MACE is always in
-	bank 0.
----------------------------------------------------------------------------- */
-static void mace_write(mace_private *lp, unsigned int ioaddr, int reg,
-		       int data)
-{
-  unsigned long flags;
-
-  switch (reg >> 4) {
-  case 0: /* register 0-15 */
-      outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + reg);
-      break;
-  case 1: /* register 16-31 */
-      spin_lock_irqsave(&lp->bank_lock, flags);
-      MACEBANK(1);
-      outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + (reg & 0x0F));
-      MACEBANK(0);
-      spin_unlock_irqrestore(&lp->bank_lock, flags);
-      break;
-  }
-} /* mace_write */
-
-/* ----------------------------------------------------------------------------
-mace_init
-	Resets the MACE chip.
----------------------------------------------------------------------------- */
-static int mace_init(mace_private *lp, unsigned int ioaddr,
-		     const char *enet_addr)
-{
-  int i;
-  int ct = 0;
-
-  /* MACE Software reset */
-  mace_write(lp, ioaddr, MACE_BIUCC, 1);
-  while (mace_read(lp, ioaddr, MACE_BIUCC) & 0x01) {
-    /* Wait for reset bit to be cleared automatically after <= 200ns */;
-    if(++ct > 500)
-    {
-	pr_err("reset failed, card removed?\n");
-	return -1;
-    }
-    udelay(1);
-  }
-  mace_write(lp, ioaddr, MACE_BIUCC, 0);
-
-  /* The Am2150 requires that the MACE FIFOs operate in burst mode. */
-  mace_write(lp, ioaddr, MACE_FIFOCC, 0x0F);
-
-  mace_write(lp,ioaddr, MACE_RCVFC, 0); /* Disable Auto Strip Receive */
-  mace_write(lp, ioaddr, MACE_IMR, 0xFF); /* Disable all interrupts until _open */
-
-  /*
-   * Bit 2-1 PORTSEL[1-0] Port Select.
-   * 00 AUI/10Base-2
-   * 01 10Base-T
-   * 10 DAI Port (reserved in Am2150)
-   * 11 GPSI
-   * For this card, only the first two are valid.
-   * So, PLSCC should be set to
-   * 0x00 for 10Base-2
-   * 0x02 for 10Base-T
-   * Or just set ASEL in PHYCC below!
-   */
-  switch (if_port) {
-  case 1:
-      mace_write(lp, ioaddr, MACE_PLSCC, 0x02);
-      break;
-  case 2:
-      mace_write(lp, ioaddr, MACE_PLSCC, 0x00);
-      break;
-  default:
-      mace_write(lp, ioaddr, MACE_PHYCC, /* ASEL */ 4);
-      /* ASEL Auto Select.  When set, the PORTSEL[1-0] bits are overridden,
-	 and the MACE device will automatically select the operating media
-	 interface port. */
-      break;
-  }
-
-  mace_write(lp, ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_PHYADDR);
-  /* Poll ADDRCHG bit */
-  ct = 0;
-  while (mace_read(lp, ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG)
-  {
-	if(++ ct > 500)
-	{
-		pr_err("ADDRCHG timeout, card removed?\n");
-		return -1;
-	}
-  }
-  /* Set PADR register */
-  for (i = 0; i < ETH_ALEN; i++)
-    mace_write(lp, ioaddr, MACE_PADR, enet_addr[i]);
-
-  /* MAC Configuration Control Register should be written last */
-  /* Let set_multicast_list set this. */
-  /* mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); */
-  mace_write(lp, ioaddr, MACE_MACCC, 0x00);
-  return 0;
-} /* mace_init */
-
-static int nmclan_config(struct pcmcia_device *link)
-{
-  struct net_device *dev = link->priv;
-  mace_private *lp = netdev_priv(dev);
-  u8 *buf;
-  size_t len;
-  int i, ret;
-  unsigned int ioaddr;
-
-  dev_dbg(&link->dev, "nmclan_config\n");
-
-  link->io_lines = 5;
-  ret = pcmcia_request_io(link);
-  if (ret)
-	  goto failed;
-  ret = pcmcia_request_irq(link, mace_interrupt);
-  if (ret)
-	  goto failed;
-  ret = pcmcia_enable_device(link);
-  if (ret)
-	  goto failed;
-
-  dev->irq = link->irq;
-  dev->base_addr = link->resource[0]->start;
-
-  ioaddr = dev->base_addr;
-
-  /* Read the ethernet address from the CIS. */
-  len = pcmcia_get_tuple(link, 0x80, &buf);
-  if (!buf || len < ETH_ALEN) {
-	  kfree(buf);
-	  goto failed;
-  }
-  eth_hw_addr_set(dev, buf);
-  kfree(buf);
-
-  /* Verify configuration by reading the MACE ID. */
-  {
-    char sig[2];
-
-    sig[0] = mace_read(lp, ioaddr, MACE_CHIPIDL);
-    sig[1] = mace_read(lp, ioaddr, MACE_CHIPIDH);
-    if ((sig[0] == 0x40) && ((sig[1] & 0x0F) == 0x09)) {
-      dev_dbg(&link->dev, "nmclan_cs configured: mace id=%x %x\n",
-	    sig[0], sig[1]);
-    } else {
-      pr_notice("mace id not found: %x %x should be 0x40 0x?9\n",
-		sig[0], sig[1]);
-      goto failed;
-    }
-  }
-
-  if(mace_init(lp, ioaddr, dev->dev_addr) == -1)
-	goto failed;
-
-  /* The if_port symbol can be set when the module is loaded */
-  if (if_port <= 2)
-    dev->if_port = if_port;
-  else
-    pr_notice("invalid if_port requested\n");
-
-  SET_NETDEV_DEV(dev, &link->dev);
-
-  i = register_netdev(dev);
-  if (i != 0) {
-    pr_notice("register_netdev() failed\n");
-    goto failed;
-  }
-
-  netdev_info(dev, "nmclan: port %#3lx, irq %d, %s port, hw_addr %pM\n",
-	      dev->base_addr, dev->irq, if_names[dev->if_port], dev->dev_addr);
-  return 0;
-
-failed:
-	nmclan_release(link);
-	return -ENODEV;
-} /* nmclan_config */
-
-static void nmclan_release(struct pcmcia_device *link)
-{
-	dev_dbg(&link->dev, "nmclan_release\n");
-	pcmcia_disable_device(link);
-}
-
-static int nmclan_suspend(struct pcmcia_device *link)
-{
-	struct net_device *dev = link->priv;
-
-	if (link->open)
-		netif_device_detach(dev);
-
-	return 0;
-}
-
-static int nmclan_resume(struct pcmcia_device *link)
-{
-	struct net_device *dev = link->priv;
-
-	if (link->open) {
-		nmclan_reset(dev);
-		netif_device_attach(dev);
-	}
-
-	return 0;
-}
-
-
-/* ----------------------------------------------------------------------------
-nmclan_reset
-	Reset and restore all of the Xilinx and MACE registers.
----------------------------------------------------------------------------- */
-static void nmclan_reset(struct net_device *dev)
-{
-  mace_private *lp = netdev_priv(dev);
-
-#if RESET_XILINX
-  struct pcmcia_device *link = &lp->link;
-  u8 OrigCorValue;
-
-  /* Save original COR value */
-  pcmcia_read_config_byte(link, CISREG_COR, &OrigCorValue);
-
-  /* Reset Xilinx */
-  dev_dbg(&link->dev, "nmclan_reset: OrigCorValue=0x%x, resetting...\n",
-	OrigCorValue);
-  pcmcia_write_config_byte(link, CISREG_COR, COR_SOFT_RESET);
-  /* Need to wait for 20 ms for PCMCIA to finish reset. */
-
-  /* Restore original COR configuration index */
-  pcmcia_write_config_byte(link, CISREG_COR,
-			  (COR_LEVEL_REQ | (OrigCorValue & COR_CONFIG_MASK)));
-  /* Xilinx is now completely reset along with the MACE chip. */
-  lp->tx_free_frames=AM2150_MAX_TX_FRAMES;
-
-#endif /* #if RESET_XILINX */
-
-  /* Xilinx is now completely reset along with the MACE chip. */
-  lp->tx_free_frames=AM2150_MAX_TX_FRAMES;
-
-  /* Reinitialize the MACE chip for operation. */
-  mace_init(lp, dev->base_addr, dev->dev_addr);
-  mace_write(lp, dev->base_addr, MACE_IMR, MACE_IMR_DEFAULT);
-
-  /* Restore the multicast list and enable TX and RX. */
-  restore_multicast_list(dev);
-} /* nmclan_reset */
-
-/* ----------------------------------------------------------------------------
-mace_config
-	[Someone tell me what this is supposed to do?  Is if_port a defined
-	standard?  If so, there should be defines to indicate 1=10Base-T,
-	2=10Base-2, etc. including limited automatic detection.]
----------------------------------------------------------------------------- */
-static int mace_config(struct net_device *dev, struct ifmap *map)
-{
-  if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
-    if (map->port <= 2) {
-      WRITE_ONCE(dev->if_port, map->port);
-      netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]);
-    } else
-      return -EINVAL;
-  }
-  return 0;
-} /* mace_config */
-
-/* ----------------------------------------------------------------------------
-mace_open
-	Open device driver.
----------------------------------------------------------------------------- */
-static int mace_open(struct net_device *dev)
-{
-  unsigned int ioaddr = dev->base_addr;
-  mace_private *lp = netdev_priv(dev);
-  struct pcmcia_device *link = lp->p_dev;
-
-  if (!pcmcia_dev_present(link))
-    return -ENODEV;
-
-  link->open++;
-
-  MACEBANK(0);
-
-  netif_start_queue(dev);
-  nmclan_reset(dev);
-
-  return 0; /* Always succeed */
-} /* mace_open */
-
-/* ----------------------------------------------------------------------------
-mace_close
-	Closes device driver.
----------------------------------------------------------------------------- */
-static int mace_close(struct net_device *dev)
-{
-  unsigned int ioaddr = dev->base_addr;
-  mace_private *lp = netdev_priv(dev);
-  struct pcmcia_device *link = lp->p_dev;
-
-  dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name);
-
-  /* Mask off all interrupts from the MACE chip. */
-  outb(0xFF, ioaddr + AM2150_MACE_BASE + MACE_IMR);
-
-  link->open--;
-  netif_stop_queue(dev);
-
-  return 0;
-} /* mace_close */
-
-static void netdev_get_drvinfo(struct net_device *dev,
-			       struct ethtool_drvinfo *info)
-{
-	strscpy(info->driver, DRV_NAME, sizeof(info->driver));
-	snprintf(info->bus_info, sizeof(info->bus_info),
-		"PCMCIA 0x%lx", dev->base_addr);
-}
-
-static const struct ethtool_ops netdev_ethtool_ops = {
-	.get_drvinfo		= netdev_get_drvinfo,
-};
-
-/* ----------------------------------------------------------------------------
-mace_start_xmit
-	This routine begins the packet transmit function.  When completed,
-	it will generate a transmit interrupt.
-
-	According to /usr/src/linux/net/inet/dev.c, if _start_xmit
-	returns 0, the "packet is now solely the responsibility of the
-	driver."  If _start_xmit returns non-zero, the "transmission
-	failed, put skb back into a list."
----------------------------------------------------------------------------- */
-
-static void mace_tx_timeout(struct net_device *dev, unsigned int txqueue)
-{
-  mace_private *lp = netdev_priv(dev);
-  struct pcmcia_device *link = lp->p_dev;
-
-  netdev_notice(dev, "transmit timed out -- ");
-#if RESET_ON_TIMEOUT
-  pr_cont("resetting card\n");
-  pcmcia_reset_card(link->socket);
-#else /* #if RESET_ON_TIMEOUT */
-  pr_cont("NOT resetting card\n");
-#endif /* #if RESET_ON_TIMEOUT */
-  netif_trans_update(dev); /* prevent tx timeout */
-  netif_wake_queue(dev);
-}
-
-static netdev_tx_t mace_start_xmit(struct sk_buff *skb,
-					 struct net_device *dev)
-{
-  mace_private *lp = netdev_priv(dev);
-  unsigned int ioaddr = dev->base_addr;
-
-  netif_stop_queue(dev);
-
-  pr_debug("%s: mace_start_xmit(length = %ld) called.\n",
-	dev->name, (long)skb->len);
-
-#if (!TX_INTERRUPTABLE)
-  /* Disable MACE TX interrupts. */
-  outb(MACE_IMR_DEFAULT | MACE_IR_XMTINT,
-    ioaddr + AM2150_MACE_BASE + MACE_IMR);
-  lp->tx_irq_disabled=1;
-#endif /* #if (!TX_INTERRUPTABLE) */
-
-  {
-    /* This block must not be interrupted by another transmit request!
-       mace_tx_timeout will take care of timer-based retransmissions from
-       the upper layers.  The interrupt handler is guaranteed never to
-       service a transmit interrupt while we are in here.
-    */
-
-    dev->stats.tx_bytes += skb->len;
-    lp->tx_free_frames--;
-
-    /* WARNING: Write the _exact_ number of bytes written in the header! */
-    /* Put out the word header [must be an outw()] . . . */
-    outw(skb->len, ioaddr + AM2150_XMT);
-    /* . . . and the packet [may be any combination of outw() and outb()] */
-    outsw(ioaddr + AM2150_XMT, skb->data, skb->len >> 1);
-    if (skb->len & 1) {
-      /* Odd byte transfer */
-      outb(skb->data[skb->len-1], ioaddr + AM2150_XMT);
-    }
-
-#if MULTI_TX
-    if (lp->tx_free_frames > 0)
-      netif_start_queue(dev);
-#endif /* #if MULTI_TX */
-  }
-
-#if (!TX_INTERRUPTABLE)
-  /* Re-enable MACE TX interrupts. */
-  lp->tx_irq_disabled=0;
-  outb(MACE_IMR_DEFAULT, ioaddr + AM2150_MACE_BASE + MACE_IMR);
-#endif /* #if (!TX_INTERRUPTABLE) */
-
-  dev_kfree_skb(skb);
-
-  return NETDEV_TX_OK;
-} /* mace_start_xmit */
-
-/* ----------------------------------------------------------------------------
-mace_interrupt
-	The interrupt handler.
----------------------------------------------------------------------------- */
-static irqreturn_t mace_interrupt(int irq, void *dev_id)
-{
-  struct net_device *dev = (struct net_device *) dev_id;
-  mace_private *lp = netdev_priv(dev);
-  unsigned int ioaddr;
-  int status;
-  int IntrCnt = MACE_MAX_IR_ITERATIONS;
-
-  if (!dev) {
-    pr_debug("mace_interrupt(): irq 0x%X for unknown device.\n",
-	  irq);
-    return IRQ_NONE;
-  }
-
-  ioaddr = dev->base_addr;
-
-  if (lp->tx_irq_disabled) {
-    const char *msg;
-    if (lp->tx_irq_disabled)
-      msg = "Interrupt with tx_irq_disabled";
-    else
-      msg = "Re-entering the interrupt handler";
-    netdev_notice(dev, "%s [isr=%02X, imr=%02X]\n",
-		  msg,
-		  inb(ioaddr + AM2150_MACE_BASE + MACE_IR),
-		  inb(ioaddr + AM2150_MACE_BASE + MACE_IMR));
-    /* WARNING: MACE_IR has been read! */
-    return IRQ_NONE;
-  }
-
-  if (!netif_device_present(dev)) {
-    netdev_dbg(dev, "interrupt from dead card\n");
-    return IRQ_NONE;
-  }
-
-  do {
-    /* WARNING: MACE_IR is a READ/CLEAR port! */
-    status = inb(ioaddr + AM2150_MACE_BASE + MACE_IR);
-    if (!(status & ~MACE_IMR_DEFAULT) && IntrCnt == MACE_MAX_IR_ITERATIONS)
-      return IRQ_NONE;
-
-    pr_debug("mace_interrupt: irq 0x%X status 0x%X.\n", irq, status);
-
-    if (status & MACE_IR_RCVINT) {
-      mace_rx(dev, MACE_MAX_RX_ITERATIONS);
-    }
-
-    if (status & MACE_IR_XMTINT) {
-      unsigned char fifofc;
-      unsigned char xmtrc;
-      unsigned char xmtfs;
-
-      fifofc = inb(ioaddr + AM2150_MACE_BASE + MACE_FIFOFC);
-      if ((fifofc & MACE_FIFOFC_XMTFC)==0) {
-	dev->stats.tx_errors++;
-	outb(0xFF, ioaddr + AM2150_XMT_SKIP);
-      }
-
-      /* Transmit Retry Count (XMTRC, reg 4) */
-      xmtrc = inb(ioaddr + AM2150_MACE_BASE + MACE_XMTRC);
-      if (xmtrc & MACE_XMTRC_EXDEF) lp->mace_stats.exdef++;
-      lp->mace_stats.xmtrc += (xmtrc & MACE_XMTRC_XMTRC);
-
-      if (
-        (xmtfs = inb(ioaddr + AM2150_MACE_BASE + MACE_XMTFS)) &
-        MACE_XMTFS_XMTSV /* Transmit Status Valid */
-      ) {
-	lp->mace_stats.xmtsv++;
-
-	if (xmtfs & ~MACE_XMTFS_XMTSV) {
-	  if (xmtfs & MACE_XMTFS_UFLO) {
-	    /* Underflow.  Indicates that the Transmit FIFO emptied before
-	       the end of frame was reached. */
-	    lp->mace_stats.uflo++;
-	  }
-	  if (xmtfs & MACE_XMTFS_LCOL) {
-	    /* Late Collision */
-	    lp->mace_stats.lcol++;
-	  }
-	  if (xmtfs & MACE_XMTFS_MORE) {
-	    /* MORE than one retry was needed */
-	    lp->mace_stats.more++;
-	  }
-	  if (xmtfs & MACE_XMTFS_ONE) {
-	    /* Exactly ONE retry occurred */
-	    lp->mace_stats.one++;
-	  }
-	  if (xmtfs & MACE_XMTFS_DEFER) {
-	    /* Transmission was defered */
-	    lp->mace_stats.defer++;
-	  }
-	  if (xmtfs & MACE_XMTFS_LCAR) {
-	    /* Loss of carrier */
-	    lp->mace_stats.lcar++;
-	  }
-	  if (xmtfs & MACE_XMTFS_RTRY) {
-	    /* Retry error: transmit aborted after 16 attempts */
-	    lp->mace_stats.rtry++;
-	  }
-        } /* if (xmtfs & ~MACE_XMTFS_XMTSV) */
-
-      } /* if (xmtfs & MACE_XMTFS_XMTSV) */
-
-      dev->stats.tx_packets++;
-      lp->tx_free_frames++;
-      netif_wake_queue(dev);
-    } /* if (status & MACE_IR_XMTINT) */
-
-    if (status & ~MACE_IMR_DEFAULT & ~MACE_IR_RCVINT & ~MACE_IR_XMTINT) {
-      if (status & MACE_IR_JAB) {
-        /* Jabber Error.  Excessive transmit duration (20-150ms). */
-        lp->mace_stats.jab++;
-      }
-      if (status & MACE_IR_BABL) {
-        /* Babble Error.  >1518 bytes transmitted. */
-        lp->mace_stats.babl++;
-      }
-      if (status & MACE_IR_CERR) {
-	/* Collision Error.  CERR indicates the absence of the
-	   Signal Quality Error Test message after a packet
-	   transmission. */
-        lp->mace_stats.cerr++;
-      }
-      if (status & MACE_IR_RCVCCO) {
-        /* Receive Collision Count Overflow; */
-        lp->mace_stats.rcvcco++;
-      }
-      if (status & MACE_IR_RNTPCO) {
-        /* Runt Packet Count Overflow */
-        lp->mace_stats.rntpco++;
-      }
-      if (status & MACE_IR_MPCO) {
-        /* Missed Packet Count Overflow */
-        lp->mace_stats.mpco++;
-      }
-    } /* if (status & ~MACE_IMR_DEFAULT & ~MACE_IR_RCVINT & ~MACE_IR_XMTINT) */
-
-  } while ((status & ~MACE_IMR_DEFAULT) && (--IntrCnt));
-
-  return IRQ_HANDLED;
-} /* mace_interrupt */
-
-/* ----------------------------------------------------------------------------
-mace_rx
-	Receives packets.
----------------------------------------------------------------------------- */
-static int mace_rx(struct net_device *dev, unsigned char RxCnt)
-{
-  mace_private *lp = netdev_priv(dev);
-  unsigned int ioaddr = dev->base_addr;
-  unsigned char rx_framecnt;
-  unsigned short rx_status;
-
-  while (
-    ((rx_framecnt = inb(ioaddr + AM2150_RCV_FRAME_COUNT)) > 0) &&
-    (rx_framecnt <= 12) && /* rx_framecnt==0xFF if card is extracted. */
-    (RxCnt--)
-  ) {
-    rx_status = inw(ioaddr + AM2150_RCV);
-
-    pr_debug("%s: in mace_rx(), framecnt 0x%X, rx_status"
-	  " 0x%X.\n", dev->name, rx_framecnt, rx_status);
-
-    if (rx_status & MACE_RCVFS_RCVSTS) { /* Error, update stats. */
-      dev->stats.rx_errors++;
-      if (rx_status & MACE_RCVFS_OFLO) {
-        lp->mace_stats.oflo++;
-      }
-      if (rx_status & MACE_RCVFS_CLSN) {
-        lp->mace_stats.clsn++;
-      }
-      if (rx_status & MACE_RCVFS_FRAM) {
-	lp->mace_stats.fram++;
-      }
-      if (rx_status & MACE_RCVFS_FCS) {
-        lp->mace_stats.fcs++;
-      }
-    } else {
-      short pkt_len = (rx_status & ~MACE_RCVFS_RCVSTS) - 4;
-        /* Auto Strip is off, always subtract 4 */
-      struct sk_buff *skb;
-
-      lp->mace_stats.rfs_rntpc += inb(ioaddr + AM2150_RCV);
-        /* runt packet count */
-      lp->mace_stats.rfs_rcvcc += inb(ioaddr + AM2150_RCV);
-        /* rcv collision count */
-
-      pr_debug("    receiving packet size 0x%X rx_status"
-	    " 0x%X.\n", pkt_len, rx_status);
-
-      skb = netdev_alloc_skb(dev, pkt_len + 2);
-
-      if (skb) {
-	skb_reserve(skb, 2);
-	insw(ioaddr + AM2150_RCV, skb_put(skb, pkt_len), pkt_len>>1);
-	if (pkt_len & 1)
-	    *(skb_tail_pointer(skb) - 1) = inb(ioaddr + AM2150_RCV);
-	skb->protocol = eth_type_trans(skb, dev);
-
-	netif_rx(skb); /* Send the packet to the upper (protocol) layers. */
-
-	dev->stats.rx_packets++;
-	dev->stats.rx_bytes += pkt_len;
-	outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */
-	continue;
-      } else {
-	pr_debug("%s: couldn't allocate a sk_buff of size"
-	      " %d.\n", dev->name, pkt_len);
-	dev->stats.rx_dropped++;
-      }
-    }
-    outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */
-  } /* while */
-
-  return 0;
-} /* mace_rx */
-
-/* ----------------------------------------------------------------------------
-pr_linux_stats
----------------------------------------------------------------------------- */
-static void pr_linux_stats(struct net_device_stats *pstats)
-{
-  pr_debug("pr_linux_stats\n");
-  pr_debug(" rx_packets=%-7ld        tx_packets=%ld\n",
-	(long)pstats->rx_packets, (long)pstats->tx_packets);
-  pr_debug(" rx_errors=%-7ld         tx_errors=%ld\n",
-	(long)pstats->rx_errors, (long)pstats->tx_errors);
-  pr_debug(" rx_dropped=%-7ld        tx_dropped=%ld\n",
-	(long)pstats->rx_dropped, (long)pstats->tx_dropped);
-  pr_debug(" multicast=%-7ld         collisions=%ld\n",
-	(long)pstats->multicast, (long)pstats->collisions);
-
-  pr_debug(" rx_length_errors=%-7ld  rx_over_errors=%ld\n",
-	(long)pstats->rx_length_errors, (long)pstats->rx_over_errors);
-  pr_debug(" rx_crc_errors=%-7ld     rx_frame_errors=%ld\n",
-	(long)pstats->rx_crc_errors, (long)pstats->rx_frame_errors);
-  pr_debug(" rx_fifo_errors=%-7ld    rx_missed_errors=%ld\n",
-	(long)pstats->rx_fifo_errors, (long)pstats->rx_missed_errors);
-
-  pr_debug(" tx_aborted_errors=%-7ld tx_carrier_errors=%ld\n",
-	(long)pstats->tx_aborted_errors, (long)pstats->tx_carrier_errors);
-  pr_debug(" tx_fifo_errors=%-7ld    tx_heartbeat_errors=%ld\n",
-	(long)pstats->tx_fifo_errors, (long)pstats->tx_heartbeat_errors);
-  pr_debug(" tx_window_errors=%ld\n",
-	(long)pstats->tx_window_errors);
-} /* pr_linux_stats */
-
-/* ----------------------------------------------------------------------------
-pr_mace_stats
----------------------------------------------------------------------------- */
-static void pr_mace_stats(mace_statistics *pstats)
-{
-  pr_debug("pr_mace_stats\n");
-
-  pr_debug(" xmtsv=%-7d             uflo=%d\n",
-	pstats->xmtsv, pstats->uflo);
-  pr_debug(" lcol=%-7d              more=%d\n",
-	pstats->lcol, pstats->more);
-  pr_debug(" one=%-7d               defer=%d\n",
-	pstats->one, pstats->defer);
-  pr_debug(" lcar=%-7d              rtry=%d\n",
-	pstats->lcar, pstats->rtry);
-
-  /* MACE_XMTRC */
-  pr_debug(" exdef=%-7d             xmtrc=%d\n",
-	pstats->exdef, pstats->xmtrc);
-
-  /* RFS1--Receive Status (RCVSTS) */
-  pr_debug(" oflo=%-7d              clsn=%d\n",
-	pstats->oflo, pstats->clsn);
-  pr_debug(" fram=%-7d              fcs=%d\n",
-	pstats->fram, pstats->fcs);
-
-  /* RFS2--Runt Packet Count (RNTPC) */
-  /* RFS3--Receive Collision Count (RCVCC) */
-  pr_debug(" rfs_rntpc=%-7d         rfs_rcvcc=%d\n",
-	pstats->rfs_rntpc, pstats->rfs_rcvcc);
-
-  /* MACE_IR */
-  pr_debug(" jab=%-7d               babl=%d\n",
-	pstats->jab, pstats->babl);
-  pr_debug(" cerr=%-7d              rcvcco=%d\n",
-	pstats->cerr, pstats->rcvcco);
-  pr_debug(" rntpco=%-7d            mpco=%d\n",
-	pstats->rntpco, pstats->mpco);
-
-  /* MACE_MPC */
-  pr_debug(" mpc=%d\n", pstats->mpc);
-
-  /* MACE_RNTPC */
-  pr_debug(" rntpc=%d\n", pstats->rntpc);
-
-  /* MACE_RCVCC */
-  pr_debug(" rcvcc=%d\n", pstats->rcvcc);
-
-} /* pr_mace_stats */
-
-/* ----------------------------------------------------------------------------
-update_stats
-	Update statistics.  We change to register window 1, so this
-	should be run single-threaded if the device is active. This is
-	expected to be a rare operation, and it's simpler for the rest
-	of the driver to assume that window 0 is always valid rather
-	than use a special window-state variable.
-
-	oflo & uflo should _never_ occur since it would mean the Xilinx
-	was not able to transfer data between the MACE FIFO and the
-	card's SRAM fast enough.  If this happens, something is
-	seriously wrong with the hardware.
----------------------------------------------------------------------------- */
-static void update_stats(unsigned int ioaddr, struct net_device *dev)
-{
-  mace_private *lp = netdev_priv(dev);
-
-  lp->mace_stats.rcvcc += mace_read(lp, ioaddr, MACE_RCVCC);
-  lp->mace_stats.rntpc += mace_read(lp, ioaddr, MACE_RNTPC);
-  lp->mace_stats.mpc += mace_read(lp, ioaddr, MACE_MPC);
-  /* At this point, mace_stats is fully updated for this call.
-     We may now update the netdev stats. */
-
-  /* The MACE has no equivalent for netdev stats field which are commented
-     out. */
-
-  /* dev->stats.multicast; */
-  dev->stats.collisions =
-    lp->mace_stats.rcvcco * 256 + lp->mace_stats.rcvcc;
-    /* Collision: The MACE may retry sending a packet 15 times
-       before giving up.  The retry count is in XMTRC.
-       Does each retry constitute a collision?
-       If so, why doesn't the RCVCC record these collisions? */
-
-  /* detailed rx_errors: */
-  dev->stats.rx_length_errors =
-    lp->mace_stats.rntpco * 256 + lp->mace_stats.rntpc;
-  /* dev->stats.rx_over_errors */
-  dev->stats.rx_crc_errors = lp->mace_stats.fcs;
-  dev->stats.rx_frame_errors = lp->mace_stats.fram;
-  dev->stats.rx_fifo_errors = lp->mace_stats.oflo;
-  dev->stats.rx_missed_errors =
-    lp->mace_stats.mpco * 256 + lp->mace_stats.mpc;
-
-  /* detailed tx_errors */
-  dev->stats.tx_aborted_errors = lp->mace_stats.rtry;
-  dev->stats.tx_carrier_errors = lp->mace_stats.lcar;
-    /* LCAR usually results from bad cabling. */
-  dev->stats.tx_fifo_errors = lp->mace_stats.uflo;
-  dev->stats.tx_heartbeat_errors = lp->mace_stats.cerr;
-  /* dev->stats.tx_window_errors; */
-} /* update_stats */
-
-/* ----------------------------------------------------------------------------
-mace_get_stats
-	Gathers ethernet statistics from the MACE chip.
----------------------------------------------------------------------------- */
-static struct net_device_stats *mace_get_stats(struct net_device *dev)
-{
-  mace_private *lp = netdev_priv(dev);
-
-  update_stats(dev->base_addr, dev);
-
-  pr_debug("%s: updating the statistics.\n", dev->name);
-  pr_linux_stats(&dev->stats);
-  pr_mace_stats(&lp->mace_stats);
-
-  return &dev->stats;
-} /* net_device_stats */
-
-/* ----------------------------------------------------------------------------
-updateCRC
-	Modified from Am79C90 data sheet.
----------------------------------------------------------------------------- */
-
-#ifdef BROKEN_MULTICAST
-
-static void updateCRC(int *CRC, int bit)
-{
-  static const int poly[]={
-    1,1,1,0, 1,1,0,1,
-    1,0,1,1, 1,0,0,0,
-    1,0,0,0, 0,0,1,1,
-    0,0,1,0, 0,0,0,0
-  }; /* CRC polynomial.  poly[n] = coefficient of the x**n term of the
-	CRC generator polynomial. */
-
-  int j;
-
-  /* shift CRC and control bit (CRC[32]) */
-  for (j = 32; j > 0; j--)
-    CRC[j] = CRC[j-1];
-  CRC[0] = 0;
-
-  /* If bit XOR(control bit) = 1, set CRC = CRC XOR polynomial. */
-  if (bit ^ CRC[32])
-    for (j = 0; j < 32; j++)
-      CRC[j] ^= poly[j];
-} /* updateCRC */
-
-/* ----------------------------------------------------------------------------
-BuildLAF
-	Build logical address filter.
-	Modified from Am79C90 data sheet.
-
-Input
-	ladrf: logical address filter (contents initialized to 0)
-	adr: ethernet address
----------------------------------------------------------------------------- */
-static void BuildLAF(int *ladrf, int *adr)
-{
-  int CRC[33]={1}; /* CRC register, 1 word/bit + extra control bit */
-
-  int i, byte; /* temporary array indices */
-  int hashcode; /* the output object */
-
-  CRC[32]=0;
-
-  for (byte = 0; byte < 6; byte++)
-    for (i = 0; i < 8; i++)
-      updateCRC(CRC, (adr[byte] >> i) & 1);
-
-  hashcode = 0;
-  for (i = 0; i < 6; i++)
-    hashcode = (hashcode << 1) + CRC[i];
-
-  byte = hashcode >> 3;
-  ladrf[byte] |= (1 << (hashcode & 7));
-
-#ifdef PCMCIA_DEBUG
-  if (0)
-    printk(KERN_DEBUG "    adr =%pM\n", adr);
-  printk(KERN_DEBUG "    hashcode = %d(decimal), ladrf[0:63] =", hashcode);
-  for (i = 0; i < 8; i++)
-    pr_cont(" %02X", ladrf[i]);
-  pr_cont("\n");
-#endif
-} /* BuildLAF */
-
-/* ----------------------------------------------------------------------------
-restore_multicast_list
-	Restores the multicast filter for MACE chip to the last
-	set_multicast_list() call.
-
-Input
-	multicast_num_addrs
-	multicast_ladrf[]
----------------------------------------------------------------------------- */
-static void restore_multicast_list(struct net_device *dev)
-{
-  mace_private *lp = netdev_priv(dev);
-  int num_addrs = lp->multicast_num_addrs;
-  int *ladrf = lp->multicast_ladrf;
-  unsigned int ioaddr = dev->base_addr;
-  int i;
-
-  pr_debug("%s: restoring Rx mode to %d addresses.\n",
-	dev->name, num_addrs);
-
-  if (num_addrs > 0) {
-
-    pr_debug("Attempt to restore multicast list detected.\n");
-
-    mace_write(lp, ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_LOGADDR);
-    /* Poll ADDRCHG bit */
-    while (mace_read(lp, ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG)
-      ;
-    /* Set LADRF register */
-    for (i = 0; i < MACE_LADRF_LEN; i++)
-      mace_write(lp, ioaddr, MACE_LADRF, ladrf[i]);
-
-    mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_RCVFCSE | MACE_UTR_LOOP_EXTERNAL);
-    mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
-
-  } else if (num_addrs < 0) {
-
-    /* Promiscuous mode: receive all packets */
-    mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
-    mace_write(lp, ioaddr, MACE_MACCC,
-      MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV
-    );
-
-  } else {
-
-    /* Normal mode */
-    mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
-    mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
-
-  }
-} /* restore_multicast_list */
-
-/* ----------------------------------------------------------------------------
-set_multicast_list
-	Set or clear the multicast filter for this adaptor.
-
-Input
-	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.
-Output
-	multicast_num_addrs
-	multicast_ladrf[]
----------------------------------------------------------------------------- */
-
-static void set_multicast_list(struct net_device *dev)
-{
-  mace_private *lp = netdev_priv(dev);
-  int adr[ETH_ALEN] = {0}; /* Ethernet address */
-  struct netdev_hw_addr *ha;
-
-#ifdef PCMCIA_DEBUG
-  {
-    static int old;
-    if (netdev_mc_count(dev) != old) {
-      old = netdev_mc_count(dev);
-      pr_debug("%s: setting Rx mode to %d addresses.\n",
-	    dev->name, old);
-    }
-  }
-#endif
-
-  /* Set multicast_num_addrs. */
-  lp->multicast_num_addrs = netdev_mc_count(dev);
-
-  /* Set multicast_ladrf. */
-  if (num_addrs > 0) {
-    /* Calculate multicast logical address filter */
-    memset(lp->multicast_ladrf, 0, MACE_LADRF_LEN);
-    netdev_for_each_mc_addr(ha, dev) {
-      memcpy(adr, ha->addr, ETH_ALEN);
-      BuildLAF(lp->multicast_ladrf, adr);
-    }
-  }
-
-  restore_multicast_list(dev);
-
-} /* set_multicast_list */
-
-#endif /* BROKEN_MULTICAST */
-
-static void restore_multicast_list(struct net_device *dev)
-{
-  unsigned int ioaddr = dev->base_addr;
-  mace_private *lp = netdev_priv(dev);
-
-  pr_debug("%s: restoring Rx mode to %d addresses.\n", dev->name,
-	lp->multicast_num_addrs);
-
-  if (dev->flags & IFF_PROMISC) {
-    /* Promiscuous mode: receive all packets */
-    mace_write(lp,ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
-    mace_write(lp, ioaddr, MACE_MACCC,
-      MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV
-    );
-  } else {
-    /* Normal mode */
-    mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
-    mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
-  }
-} /* restore_multicast_list */
-
-static void set_multicast_list(struct net_device *dev)
-{
-  mace_private *lp = netdev_priv(dev);
-
-#ifdef PCMCIA_DEBUG
-  {
-    static int old;
-    if (netdev_mc_count(dev) != old) {
-      old = netdev_mc_count(dev);
-      pr_debug("%s: setting Rx mode to %d addresses.\n",
-	    dev->name, old);
-    }
-  }
-#endif
-
-  lp->multicast_num_addrs = netdev_mc_count(dev);
-  restore_multicast_list(dev);
-
-} /* set_multicast_list */
-
-static const struct pcmcia_device_id nmclan_ids[] = {
-	PCMCIA_DEVICE_PROD_ID12("New Media Corporation", "Ethernet", 0x085a850b, 0x00b2e941),
-	PCMCIA_DEVICE_PROD_ID12("Portable Add-ons", "Ethernet+", 0xebf1d60, 0xad673aaf),
-	PCMCIA_DEVICE_NULL,
-};
-MODULE_DEVICE_TABLE(pcmcia, nmclan_ids);
-
-static struct pcmcia_driver nmclan_cs_driver = {
-	.owner		= THIS_MODULE,
-	.name		= "nmclan_cs",
-	.probe		= nmclan_probe,
-	.remove		= nmclan_detach,
-	.id_table       = nmclan_ids,
-	.suspend	= nmclan_suspend,
-	.resume		= nmclan_resume,
-};
-module_pcmcia_driver(nmclan_cs_driver);

-- 
2.53.0


^ permalink raw reply related

* [PATCH net v2 05/15] drivers: net: amd: lance: Remove this driver
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
  Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
	Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
In-Reply-To: <20260422-v7-0-0-net-next-driver-removal-v1-v2-0-08a5b59784d5@lunn.ch>

The lance was written by Donald Becker between 1993-1998. It is an ISA
device, so unlikely to be used with modern kernels.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
 drivers/net/ethernet/amd/Kconfig  |   11 -
 drivers/net/ethernet/amd/Makefile |    1 -
 drivers/net/ethernet/amd/lance.c  | 1317 -------------------------------------
 3 files changed, 1329 deletions(-)

diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig
index 45e8d698781c..c5abb81977dd 100644
--- a/drivers/net/ethernet/amd/Kconfig
+++ b/drivers/net/ethernet/amd/Kconfig
@@ -43,17 +43,6 @@ config AMD8111_ETH
 	  To compile this driver as a module, choose M here. The module
 	  will be called amd8111e.
 
-config LANCE
-	tristate "AMD LANCE and PCnet (AT1500 and NE2100) support"
-	depends on ISA && ISA_DMA_API && !ARM && !PPC32
-	select NETDEV_LEGACY_INIT
-	help
-	  If you have a network (Ethernet) card of this type, say Y here.
-	  Some LinkSys cards are of this type.
-
-	  To compile this driver as a module, choose M here: the module
-	  will be called lance.  This is recommended.
-
 config PCNET32
 	tristate "AMD PCnet32 PCI support"
 	depends on PCI && HAS_IOPORT
diff --git a/drivers/net/ethernet/amd/Makefile b/drivers/net/ethernet/amd/Makefile
index 2dcfb84731e1..f261501f7324 100644
--- a/drivers/net/ethernet/amd/Makefile
+++ b/drivers/net/ethernet/amd/Makefile
@@ -9,7 +9,6 @@ obj-$(CONFIG_ARIADNE) += ariadne.o
 obj-$(CONFIG_ATARILANCE) += atarilance.o
 obj-$(CONFIG_DECLANCE) += declance.o
 obj-$(CONFIG_HPLANCE) += hplance.o 7990.o
-obj-$(CONFIG_LANCE) += lance.o
 obj-$(CONFIG_MIPS_AU1X00_ENET) += au1000_eth.o
 obj-$(CONFIG_MVME147_NET) += mvme147.o 7990.o
 obj-$(CONFIG_PCMCIA_NMCLAN) += nmclan_cs.o
diff --git a/drivers/net/ethernet/amd/lance.c b/drivers/net/ethernet/amd/lance.c
deleted file mode 100644
index 98afd8cb0efb..000000000000
--- a/drivers/net/ethernet/amd/lance.c
+++ /dev/null
@@ -1,1317 +0,0 @@
-/* lance.c: An AMD LANCE/PCnet ethernet driver for Linux. */
-/*
-	Written/copyright 1993-1998 by Donald Becker.
-
-	Copyright 1993 United States Government as represented by the
-	Director, National Security Agency.
-	This software may be used and distributed according to the terms
-	of the GNU General Public License, incorporated herein by reference.
-
-	This driver is for the Allied Telesis AT1500 and HP J2405A, and should work
-	with most other LANCE-based bus-master (NE2100/NE2500) ethercards.
-
-	The author may be reached as becker@scyld.com, or C/O
-	Scyld Computing Corporation
-	410 Severn Ave., Suite 210
-	Annapolis MD 21403
-
-	Andrey V. Savochkin:
-	- alignment problem with 1.3.* kernel and some minor changes.
-	Thomas Bogendoerfer (tsbogend@bigbug.franken.de):
-	- added support for Linux/Alpha, but removed most of it, because
-        it worked only for the PCI chip.
-      - added hook for the 32bit lance driver
-      - added PCnetPCI II (79C970A) to chip table
-	Paul Gortmaker (gpg109@rsphy1.anu.edu.au):
-	- hopefully fix above so Linux/Alpha can use ISA cards too.
-    8/20/96 Fixed 7990 autoIRQ failure and reversed unneeded alignment -djb
-    v1.12 10/27/97 Module support -djb
-    v1.14  2/3/98 Module support modified, made PCI support optional -djb
-    v1.15 5/27/99 Fixed bug in the cleanup_module(). dev->priv was freed
-                  before unregister_netdev() which caused NULL pointer
-                  reference later in the chain (in rtnetlink_fill_ifinfo())
-                  -- Mika Kuoppala <miku@iki.fi>
-
-    Forward ported v1.14 to 2.1.129, merged the PCI and misc changes from
-    the 2.1 version of the old driver - Alan Cox
-
-    Get rid of check_region, check kmalloc return in lance_probe1
-    Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 11/01/2001
-
-	Reworked detection, added support for Racal InterLan EtherBlaster cards
-	Vesselin Kostadinov <vesok at yahoo dot com > - 22/4/2004
-*/
-
-static const char version[] = "lance.c:v1.16 2006/11/09 dplatt@3do.com, becker@cesdis.gsfc.nasa.gov\n";
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/mm.h>
-#include <linux/bitops.h>
-#include <net/Space.h>
-
-#include <asm/io.h>
-#include <asm/dma.h>
-
-static unsigned int lance_portlist[] __initdata = { 0x300, 0x320, 0x340, 0x360, 0};
-static int lance_probe1(struct net_device *dev, int ioaddr, int irq, int options);
-static int __init do_lance_probe(struct net_device *dev);
-
-
-static struct card {
-	char id_offset14;
-	char id_offset15;
-} cards[] = {
-	{	//"normal"
-		.id_offset14 = 0x57,
-		.id_offset15 = 0x57,
-	},
-	{	//NI6510EB
-		.id_offset14 = 0x52,
-		.id_offset15 = 0x44,
-	},
-	{	//Racal InterLan EtherBlaster
-		.id_offset14 = 0x52,
-		.id_offset15 = 0x49,
-	},
-};
-#define NUM_CARDS 3
-
-#ifdef LANCE_DEBUG
-static int lance_debug = LANCE_DEBUG;
-#else
-static int lance_debug = 1;
-#endif
-
-/*
-				Theory of Operation
-
-I. Board Compatibility
-
-This device driver is designed for the AMD 79C960, the "PCnet-ISA
-single-chip ethernet controller for ISA".  This chip is used in a wide
-variety of boards from vendors such as Allied Telesis, HP, Kingston,
-and Boca.  This driver is also intended to work with older AMD 7990
-designs, such as the NE1500 and NE2100, and newer 79C961.  For convenience,
-I use the name LANCE to refer to all of the AMD chips, even though it properly
-refers only to the original 7990.
-
-II. Board-specific settings
-
-The driver is designed to work the boards that use the faster
-bus-master mode, rather than in shared memory mode.	 (Only older designs
-have on-board buffer memory needed to support the slower shared memory mode.)
-
-Most ISA boards have jumpered settings for the I/O base, IRQ line, and DMA
-channel.  This driver probes the likely base addresses:
-{0x300, 0x320, 0x340, 0x360}.
-After the board is found it generates a DMA-timeout interrupt and uses
-autoIRQ to find the IRQ line.  The DMA channel can be set with the low bits
-of the otherwise-unused dev->mem_start value (aka PARAM1).  If unset it is
-probed for by enabling each free DMA channel in turn and checking if
-initialization succeeds.
-
-The HP-J2405A board is an exception: with this board it is easy to read the
-EEPROM-set values for the base, IRQ, and DMA.  (Of course you must already
-_know_ the base address -- that field is for writing the EEPROM.)
-
-III. Driver operation
-
-IIIa. Ring buffers
-The LANCE uses ring buffers of Tx and Rx descriptors.  Each entry describes
-the base and length of the data buffer, along with status bits.	 The length
-of these buffers is set by LANCE_LOG_{RX,TX}_BUFFERS, which is log_2() of
-the buffer length (rather than being directly the buffer length) for
-implementation ease.  The current values are 2 (Tx) and 4 (Rx), which leads to
-ring sizes of 4 (Tx) and 16 (Rx).  Increasing the number of ring entries
-needlessly uses extra space and reduces the chance that an upper layer will
-be able to reorder queued Tx packets based on priority.	 Decreasing the number
-of entries makes it more difficult to achieve back-to-back packet transmission
-and increases the chance that Rx ring will overflow.  (Consider the worst case
-of receiving back-to-back minimum-sized packets.)
-
-The LANCE has the capability to "chain" both Rx and Tx buffers, but this driver
-statically allocates full-sized (slightly oversized -- PKT_BUF_SZ) buffers to
-avoid the administrative overhead. For the Rx side this avoids dynamically
-allocating full-sized buffers "just in case", at the expense of a
-memory-to-memory data copy for each packet received.  For most systems this
-is a good tradeoff: the Rx buffer will always be in low memory, the copy
-is inexpensive, and it primes the cache for later packet processing.  For Tx
-the buffers are only used when needed as low-memory bounce buffers.
-
-IIIB. 16M memory limitations.
-For the ISA bus master mode all structures used directly by the LANCE,
-the initialization block, Rx and Tx rings, and data buffers, must be
-accessible from the ISA bus, i.e. in the lower 16M of real memory.
-This is a problem for current Linux kernels on >16M machines. The network
-devices are initialized after memory initialization, and the kernel doles out
-memory from the top of memory downward.	 The current solution is to have a
-special network initialization routine that's called before memory
-initialization; this will eventually be generalized for all network devices.
-As mentioned before, low-memory "bounce-buffers" are used when needed.
-
-IIIC. Synchronization
-The driver runs as two independent, single-threaded flows of control.  One
-is the send-packet routine, which enforces single-threaded use by the
-dev->tbusy flag.  The other thread is the interrupt handler, which is single
-threaded by the hardware and other software.
-
-The send packet thread has partial control over the Tx ring and 'dev->tbusy'
-flag.  It sets the tbusy flag whenever it's queuing a Tx packet. If the next
-queue slot is empty, it clears the tbusy flag when finished otherwise it sets
-the 'lp->tx_full' flag.
-
-The interrupt handler has exclusive control over the Rx ring and records stats
-from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so
-we can't avoid the interrupt overhead by having the Tx routine reap the Tx
-stats.)	 After reaping the stats, it marks the queue entry as empty by setting
-the 'base' to zero. Iff the 'lp->tx_full' flag is set, it clears both the
-tx_full and tbusy flags.
-
-*/
-
-/* Set the number of Tx and Rx buffers, using Log_2(# buffers).
-   Reasonable default values are 16 Tx buffers, and 16 Rx buffers.
-   That translates to 4 and 4 (16 == 2^^4).
-   This is a compile-time option for efficiency.
-   */
-#ifndef LANCE_LOG_TX_BUFFERS
-#define LANCE_LOG_TX_BUFFERS 4
-#define LANCE_LOG_RX_BUFFERS 4
-#endif
-
-#define TX_RING_SIZE			(1 << (LANCE_LOG_TX_BUFFERS))
-#define TX_RING_MOD_MASK		(TX_RING_SIZE - 1)
-#define TX_RING_LEN_BITS		((LANCE_LOG_TX_BUFFERS) << 29)
-
-#define RX_RING_SIZE			(1 << (LANCE_LOG_RX_BUFFERS))
-#define RX_RING_MOD_MASK		(RX_RING_SIZE - 1)
-#define RX_RING_LEN_BITS		((LANCE_LOG_RX_BUFFERS) << 29)
-
-#define PKT_BUF_SZ		1544
-
-/* Offsets from base I/O address. */
-#define LANCE_DATA 0x10
-#define LANCE_ADDR 0x12
-#define LANCE_RESET 0x14
-#define LANCE_BUS_IF 0x16
-#define LANCE_TOTAL_SIZE 0x18
-
-#define TX_TIMEOUT	(HZ/5)
-
-/* The LANCE Rx and Tx ring descriptors. */
-struct lance_rx_head {
-	s32 base;
-	s16 buf_length;			/* This length is 2s complement (negative)! */
-	s16 msg_length;			/* This length is "normal". */
-};
-
-struct lance_tx_head {
-	s32 base;
-	s16 length;				/* Length is 2s complement (negative)! */
-	s16 misc;
-};
-
-/* The LANCE initialization block, described in databook. */
-struct lance_init_block {
-	u16 mode;		/* Pre-set mode (reg. 15) */
-	u8  phys_addr[6]; /* Physical ethernet address */
-	u32 filter[2];			/* Multicast filter (unused). */
-	/* Receive and transmit ring base, along with extra bits. */
-	u32  rx_ring;			/* Tx and Rx ring base pointers */
-	u32  tx_ring;
-};
-
-struct lance_private {
-	/* The Tx and Rx ring entries must be aligned on 8-byte boundaries. */
-	struct lance_rx_head rx_ring[RX_RING_SIZE];
-	struct lance_tx_head tx_ring[TX_RING_SIZE];
-	struct lance_init_block	init_block;
-	const char *name;
-	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
-	struct sk_buff* tx_skbuff[TX_RING_SIZE];
-	/* The addresses of receive-in-place skbuffs. */
-	struct sk_buff* rx_skbuff[RX_RING_SIZE];
-	unsigned long rx_buffs;		/* Address of Rx and Tx buffers. */
-	/* Tx low-memory "bounce buffer" address. */
-	char (*tx_bounce_buffs)[PKT_BUF_SZ];
-	int cur_rx, cur_tx;			/* The next free ring entry */
-	int dirty_rx, dirty_tx;		/* The ring entries to be free()ed. */
-	int dma;
-	unsigned char chip_version;	/* See lance_chip_type. */
-	spinlock_t devlock;
-};
-
-#define LANCE_MUST_PAD          0x00000001
-#define LANCE_ENABLE_AUTOSELECT 0x00000002
-#define LANCE_MUST_REINIT_RING  0x00000004
-#define LANCE_MUST_UNRESET      0x00000008
-#define LANCE_HAS_MISSED_FRAME  0x00000010
-
-/* A mapping from the chip ID number to the part number and features.
-   These are from the datasheets -- in real life the '970 version
-   reportedly has the same ID as the '965. */
-static struct lance_chip_type {
-	int id_number;
-	const char *name;
-	int flags;
-} chip_table[] = {
-	{0x0000, "LANCE 7990",				/* Ancient lance chip.  */
-		LANCE_MUST_PAD + LANCE_MUST_UNRESET},
-	{0x0003, "PCnet/ISA 79C960",		/* 79C960 PCnet/ISA.  */
-		LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
-			LANCE_HAS_MISSED_FRAME},
-	{0x2260, "PCnet/ISA+ 79C961",		/* 79C961 PCnet/ISA+, Plug-n-Play.  */
-		LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
-			LANCE_HAS_MISSED_FRAME},
-	{0x2420, "PCnet/PCI 79C970",		/* 79C970 or 79C974 PCnet-SCSI, PCI. */
-		LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
-			LANCE_HAS_MISSED_FRAME},
-	/* Bug: the PCnet/PCI actually uses the PCnet/VLB ID number, so just call
-		it the PCnet32. */
-	{0x2430, "PCnet32",					/* 79C965 PCnet for VL bus. */
-		LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
-			LANCE_HAS_MISSED_FRAME},
-        {0x2621, "PCnet/PCI-II 79C970A",        /* 79C970A PCInetPCI II. */
-                LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
-                        LANCE_HAS_MISSED_FRAME},
-	{0x0, 	 "PCnet (unknown)",
-		LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
-			LANCE_HAS_MISSED_FRAME},
-};
-
-enum {OLD_LANCE = 0, PCNET_ISA=1, PCNET_ISAP=2, PCNET_PCI=3, PCNET_VLB=4, PCNET_PCI_II=5, LANCE_UNKNOWN=6};
-
-
-/* Non-zero if lance_probe1() needs to allocate low-memory bounce buffers.
-   Assume yes until we know the memory size. */
-static unsigned char lance_need_isa_bounce_buffers = 1;
-
-static int lance_open(struct net_device *dev);
-static void lance_init_ring(struct net_device *dev, gfp_t mode);
-static netdev_tx_t lance_start_xmit(struct sk_buff *skb,
-				    struct net_device *dev);
-static int lance_rx(struct net_device *dev);
-static irqreturn_t lance_interrupt(int irq, void *dev_id);
-static int lance_close(struct net_device *dev);
-static struct net_device_stats *lance_get_stats(struct net_device *dev);
-static void set_multicast_list(struct net_device *dev);
-static void lance_tx_timeout (struct net_device *dev, unsigned int txqueue);
-
-
-
-#ifdef MODULE
-#define MAX_CARDS		8	/* Max number of interfaces (cards) per module */
-
-static struct net_device *dev_lance[MAX_CARDS];
-static int io[MAX_CARDS];
-static int dma[MAX_CARDS];
-static int irq[MAX_CARDS];
-
-module_param_hw_array(io, int, ioport, NULL, 0);
-module_param_hw_array(dma, int, dma, NULL, 0);
-module_param_hw_array(irq, int, irq, NULL, 0);
-module_param(lance_debug, int, 0);
-MODULE_PARM_DESC(io, "LANCE/PCnet I/O base address(es),required");
-MODULE_PARM_DESC(dma, "LANCE/PCnet ISA DMA channel (ignored for some devices)");
-MODULE_PARM_DESC(irq, "LANCE/PCnet IRQ number (ignored for some devices)");
-MODULE_PARM_DESC(lance_debug, "LANCE/PCnet debug level (0-7)");
-
-static int __init lance_init_module(void)
-{
-	struct net_device *dev;
-	int this_dev, found = 0;
-
-	for (this_dev = 0; this_dev < MAX_CARDS; this_dev++) {
-		if (io[this_dev] == 0)  {
-			if (this_dev != 0) /* only complain once */
-				break;
-			printk(KERN_NOTICE "lance.c: Module autoprobing not allowed. Append \"io=0xNNN\" value(s).\n");
-			return -EPERM;
-		}
-		dev = alloc_etherdev(0);
-		if (!dev)
-			break;
-		dev->irq = irq[this_dev];
-		dev->base_addr = io[this_dev];
-		dev->dma = dma[this_dev];
-		if (do_lance_probe(dev) == 0) {
-			dev_lance[found++] = dev;
-			continue;
-		}
-		free_netdev(dev);
-		break;
-	}
-	if (found != 0)
-		return 0;
-	return -ENXIO;
-}
-module_init(lance_init_module);
-
-static void cleanup_card(struct net_device *dev)
-{
-	struct lance_private *lp = dev->ml_priv;
-	if (dev->dma != 4)
-		free_dma(dev->dma);
-	release_region(dev->base_addr, LANCE_TOTAL_SIZE);
-	kfree(lp->tx_bounce_buffs);
-	kfree((void*)lp->rx_buffs);
-	kfree(lp);
-}
-
-static void __exit lance_cleanup_module(void)
-{
-	int this_dev;
-
-	for (this_dev = 0; this_dev < MAX_CARDS; this_dev++) {
-		struct net_device *dev = dev_lance[this_dev];
-		if (dev) {
-			unregister_netdev(dev);
-			cleanup_card(dev);
-			free_netdev(dev);
-		}
-	}
-}
-module_exit(lance_cleanup_module);
-#endif /* MODULE */
-MODULE_DESCRIPTION("AMD LANCE/PCnet Ethernet driver");
-MODULE_LICENSE("GPL");
-
-
-/* Starting in v2.1.*, the LANCE/PCnet probe is now similar to the other
-   board probes now that kmalloc() can allocate ISA DMA-able regions.
-   This also allows the LANCE driver to be used as a module.
-   */
-static int __init do_lance_probe(struct net_device *dev)
-{
-	unsigned int *port;
-	int result;
-
-	if (high_memory <= phys_to_virt(16*1024*1024))
-		lance_need_isa_bounce_buffers = 0;
-
-	for (port = lance_portlist; *port; port++) {
-		int ioaddr = *port;
-		struct resource *r = request_region(ioaddr, LANCE_TOTAL_SIZE,
-							"lance-probe");
-
-		if (r) {
-			/* Detect the card with minimal I/O reads */
-			char offset14 = inb(ioaddr + 14);
-			int card;
-			for (card = 0; card < NUM_CARDS; ++card)
-				if (cards[card].id_offset14 == offset14)
-					break;
-			if (card < NUM_CARDS) {/*yes, the first byte matches*/
-				char offset15 = inb(ioaddr + 15);
-				for (card = 0; card < NUM_CARDS; ++card)
-					if ((cards[card].id_offset14 == offset14) &&
-						(cards[card].id_offset15 == offset15))
-						break;
-			}
-			if (card < NUM_CARDS) { /*Signature OK*/
-				result = lance_probe1(dev, ioaddr, 0, 0);
-				if (!result) {
-					struct lance_private *lp = dev->ml_priv;
-					int ver = lp->chip_version;
-
-					r->name = chip_table[ver].name;
-					return 0;
-				}
-			}
-			release_region(ioaddr, LANCE_TOTAL_SIZE);
-		}
-	}
-	return -ENODEV;
-}
-
-#ifndef MODULE
-struct net_device * __init lance_probe(int unit)
-{
-	struct net_device *dev = alloc_etherdev(0);
-	int err;
-
-	if (!dev)
-		return ERR_PTR(-ENODEV);
-
-	sprintf(dev->name, "eth%d", unit);
-	netdev_boot_setup_check(dev);
-
-	err = do_lance_probe(dev);
-	if (err)
-		goto out;
-	return dev;
-out:
-	free_netdev(dev);
-	return ERR_PTR(err);
-}
-#endif
-
-static const struct net_device_ops lance_netdev_ops = {
-	.ndo_open 		= lance_open,
-	.ndo_start_xmit		= lance_start_xmit,
-	.ndo_stop		= lance_close,
-	.ndo_get_stats		= lance_get_stats,
-	.ndo_set_rx_mode	= set_multicast_list,
-	.ndo_tx_timeout		= lance_tx_timeout,
-	.ndo_set_mac_address 	= eth_mac_addr,
-	.ndo_validate_addr	= eth_validate_addr,
-};
-
-static int __init lance_probe1(struct net_device *dev, int ioaddr, int irq, int options)
-{
-	struct lance_private *lp;
-	unsigned long dma_channels;	/* Mark spuriously-busy DMA channels */
-	int i, reset_val, lance_version;
-	const char *chipname;
-	/* Flags for specific chips or boards. */
-	unsigned char hpJ2405A = 0;	/* HP ISA adaptor */
-	int hp_builtin = 0;		/* HP on-board ethernet. */
-	static int did_version;		/* Already printed version info. */
-	unsigned long flags;
-	int err = -ENOMEM;
-	void __iomem *bios;
-	u8 addr[ETH_ALEN];
-
-	/* First we look for special cases.
-	   Check for HP's on-board ethernet by looking for 'HP' in the BIOS.
-	   There are two HP versions, check the BIOS for the configuration port.
-	   This method provided by L. Julliard, Laurent_Julliard@grenoble.hp.com.
-	   */
-	bios = ioremap(0xf00f0, 0x14);
-	if (!bios)
-		return -ENOMEM;
-	if (readw(bios + 0x12) == 0x5048)  {
-		static const short ioaddr_table[] = { 0x300, 0x320, 0x340, 0x360};
-		int hp_port = (readl(bios + 1) & 1)  ? 0x499 : 0x99;
-		/* We can have boards other than the built-in!  Verify this is on-board. */
-		if ((inb(hp_port) & 0xc0) == 0x80 &&
-		    ioaddr_table[inb(hp_port) & 3] == ioaddr)
-			hp_builtin = hp_port;
-	}
-	iounmap(bios);
-	/* We also recognize the HP Vectra on-board here, but check below. */
-	hpJ2405A = (inb(ioaddr) == 0x08 && inb(ioaddr+1) == 0x00 &&
-		    inb(ioaddr+2) == 0x09);
-
-	/* Reset the LANCE.	 */
-	reset_val = inw(ioaddr+LANCE_RESET); /* Reset the LANCE */
-
-	/* The Un-Reset needed is only needed for the real NE2100, and will
-	   confuse the HP board. */
-	if (!hpJ2405A)
-		outw(reset_val, ioaddr+LANCE_RESET);
-
-	outw(0x0000, ioaddr+LANCE_ADDR); /* Switch to window 0 */
-	if (inw(ioaddr+LANCE_DATA) != 0x0004)
-		return -ENODEV;
-
-	/* Get the version of the chip. */
-	outw(88, ioaddr+LANCE_ADDR);
-	if (inw(ioaddr+LANCE_ADDR) != 88) {
-		lance_version = 0;
-	} else {			/* Good, it's a newer chip. */
-		int chip_version = inw(ioaddr+LANCE_DATA);
-		outw(89, ioaddr+LANCE_ADDR);
-		chip_version |= inw(ioaddr+LANCE_DATA) << 16;
-		if (lance_debug > 2)
-			printk("  LANCE chip version is %#x.\n", chip_version);
-		if ((chip_version & 0xfff) != 0x003)
-			return -ENODEV;
-		chip_version = (chip_version >> 12) & 0xffff;
-		for (lance_version = 1; chip_table[lance_version].id_number; lance_version++) {
-			if (chip_table[lance_version].id_number == chip_version)
-				break;
-		}
-	}
-
-	/* We can't allocate private data from alloc_etherdev() because it must
-	   a ISA DMA-able region. */
-	chipname = chip_table[lance_version].name;
-	printk("%s: %s at %#3x, ", dev->name, chipname, ioaddr);
-
-	/* There is a 16 byte station address PROM at the base address.
-	   The first six bytes are the station address. */
-	for (i = 0; i < 6; i++)
-		addr[i] = inb(ioaddr + i);
-	eth_hw_addr_set(dev, addr);
-	printk("%pM", dev->dev_addr);
-
-	dev->base_addr = ioaddr;
-	/* Make certain the data structures used by the LANCE are aligned and DMAble. */
-
-	lp = kzalloc_obj(*lp, GFP_DMA | GFP_KERNEL);
-	if (!lp)
-		return -ENOMEM;
-	if (lance_debug > 6) printk(" (#0x%05lx)", (unsigned long)lp);
-	dev->ml_priv = lp;
-	lp->name = chipname;
-	lp->rx_buffs = (unsigned long)kmalloc_array(RX_RING_SIZE, PKT_BUF_SZ,
-						    GFP_DMA | GFP_KERNEL);
-	if (!lp->rx_buffs)
-		goto out_lp;
-	if (lance_need_isa_bounce_buffers) {
-		lp->tx_bounce_buffs = kmalloc_array(TX_RING_SIZE, PKT_BUF_SZ,
-						    GFP_DMA | GFP_KERNEL);
-		if (!lp->tx_bounce_buffs)
-			goto out_rx;
-	} else
-		lp->tx_bounce_buffs = NULL;
-
-	lp->chip_version = lance_version;
-	spin_lock_init(&lp->devlock);
-
-	lp->init_block.mode = 0x0003;		/* Disable Rx and Tx. */
-	for (i = 0; i < 6; i++)
-		lp->init_block.phys_addr[i] = dev->dev_addr[i];
-	lp->init_block.filter[0] = 0x00000000;
-	lp->init_block.filter[1] = 0x00000000;
-	lp->init_block.rx_ring = ((u32)isa_virt_to_bus(lp->rx_ring) & 0xffffff) | RX_RING_LEN_BITS;
-	lp->init_block.tx_ring = ((u32)isa_virt_to_bus(lp->tx_ring) & 0xffffff) | TX_RING_LEN_BITS;
-
-	outw(0x0001, ioaddr+LANCE_ADDR);
-	inw(ioaddr+LANCE_ADDR);
-	outw((short) (u32) isa_virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA);
-	outw(0x0002, ioaddr+LANCE_ADDR);
-	inw(ioaddr+LANCE_ADDR);
-	outw(((u32)isa_virt_to_bus(&lp->init_block)) >> 16, ioaddr+LANCE_DATA);
-	outw(0x0000, ioaddr+LANCE_ADDR);
-	inw(ioaddr+LANCE_ADDR);
-
-	if (irq) {					/* Set iff PCI card. */
-		dev->dma = 4;			/* Native bus-master, no DMA channel needed. */
-		dev->irq = irq;
-	} else if (hp_builtin) {
-		static const char dma_tbl[4] = {3, 5, 6, 0};
-		static const char irq_tbl[4] = {3, 4, 5, 9};
-		unsigned char port_val = inb(hp_builtin);
-		dev->dma = dma_tbl[(port_val >> 4) & 3];
-		dev->irq = irq_tbl[(port_val >> 2) & 3];
-		printk(" HP Vectra IRQ %d DMA %d.\n", dev->irq, dev->dma);
-	} else if (hpJ2405A) {
-		static const char dma_tbl[4] = {3, 5, 6, 7};
-		static const char irq_tbl[8] = {3, 4, 5, 9, 10, 11, 12, 15};
-		short reset_val = inw(ioaddr+LANCE_RESET);
-		dev->dma = dma_tbl[(reset_val >> 2) & 3];
-		dev->irq = irq_tbl[(reset_val >> 4) & 7];
-		printk(" HP J2405A IRQ %d DMA %d.\n", dev->irq, dev->dma);
-	} else if (lance_version == PCNET_ISAP) {		/* The plug-n-play version. */
-		short bus_info;
-		outw(8, ioaddr+LANCE_ADDR);
-		bus_info = inw(ioaddr+LANCE_BUS_IF);
-		dev->dma = bus_info & 0x07;
-		dev->irq = (bus_info >> 4) & 0x0F;
-	} else {
-		/* The DMA channel may be passed in PARAM1. */
-		if (dev->mem_start & 0x07)
-			dev->dma = dev->mem_start & 0x07;
-	}
-
-	if (dev->dma == 0) {
-		/* Read the DMA channel status register, so that we can avoid
-		   stuck DMA channels in the DMA detection below. */
-		dma_channels = ((inb(DMA1_STAT_REG) >> 4) & 0x0f) |
-			(inb(DMA2_STAT_REG) & 0xf0);
-	}
-	err = -ENODEV;
-	if (dev->irq >= 2)
-		printk(" assigned IRQ %d", dev->irq);
-	else if (lance_version != 0)  {	/* 7990 boards need DMA detection first. */
-		unsigned long irq_mask;
-
-		/* To auto-IRQ we enable the initialization-done and DMA error
-		   interrupts. For ISA boards we get a DMA error, but VLB and PCI
-		   boards will work. */
-		irq_mask = probe_irq_on();
-
-		/* Trigger an initialization just for the interrupt. */
-		outw(0x0041, ioaddr+LANCE_DATA);
-
-		mdelay(20);
-		dev->irq = probe_irq_off(irq_mask);
-		if (dev->irq)
-			printk(", probed IRQ %d", dev->irq);
-		else {
-			printk(", failed to detect IRQ line.\n");
-			goto out_tx;
-		}
-
-		/* Check for the initialization done bit, 0x0100, which means
-		   that we don't need a DMA channel. */
-		if (inw(ioaddr+LANCE_DATA) & 0x0100)
-			dev->dma = 4;
-	}
-
-	if (dev->dma == 4) {
-		printk(", no DMA needed.\n");
-	} else if (dev->dma) {
-		if (request_dma(dev->dma, chipname)) {
-			printk("DMA %d allocation failed.\n", dev->dma);
-			goto out_tx;
-		} else
-			printk(", assigned DMA %d.\n", dev->dma);
-	} else {			/* OK, we have to auto-DMA. */
-		for (i = 0; i < 4; i++) {
-			static const char dmas[] = { 5, 6, 7, 3 };
-			int dma = dmas[i];
-			int boguscnt;
-
-			/* Don't enable a permanently busy DMA channel, or the machine
-			   will hang. */
-			if (test_bit(dma, &dma_channels))
-				continue;
-			outw(0x7f04, ioaddr+LANCE_DATA); /* Clear the memory error bits. */
-			if (request_dma(dma, chipname))
-				continue;
-
-			flags=claim_dma_lock();
-			set_dma_mode(dma, DMA_MODE_CASCADE);
-			enable_dma(dma);
-			release_dma_lock(flags);
-
-			/* Trigger an initialization. */
-			outw(0x0001, ioaddr+LANCE_DATA);
-			for (boguscnt = 100; boguscnt > 0; --boguscnt)
-				if (inw(ioaddr+LANCE_DATA) & 0x0900)
-					break;
-			if (inw(ioaddr+LANCE_DATA) & 0x0100) {
-				dev->dma = dma;
-				printk(", DMA %d.\n", dev->dma);
-				break;
-			} else {
-				flags=claim_dma_lock();
-				disable_dma(dma);
-				release_dma_lock(flags);
-				free_dma(dma);
-			}
-		}
-		if (i == 4) {			/* Failure: bail. */
-			printk("DMA detection failed.\n");
-			goto out_tx;
-		}
-	}
-
-	if (lance_version == 0 && dev->irq == 0) {
-		/* We may auto-IRQ now that we have a DMA channel. */
-		/* Trigger an initialization just for the interrupt. */
-		unsigned long irq_mask;
-
-		irq_mask = probe_irq_on();
-		outw(0x0041, ioaddr+LANCE_DATA);
-
-		mdelay(40);
-		dev->irq = probe_irq_off(irq_mask);
-		if (dev->irq == 0) {
-			printk("  Failed to detect the 7990 IRQ line.\n");
-			goto out_dma;
-		}
-		printk("  Auto-IRQ detected IRQ%d.\n", dev->irq);
-	}
-
-	if (chip_table[lp->chip_version].flags & LANCE_ENABLE_AUTOSELECT) {
-		/* Turn on auto-select of media (10baseT or BNC) so that the user
-		   can watch the LEDs even if the board isn't opened. */
-		outw(0x0002, ioaddr+LANCE_ADDR);
-		/* Don't touch 10base2 power bit. */
-		outw(inw(ioaddr+LANCE_BUS_IF) | 0x0002, ioaddr+LANCE_BUS_IF);
-	}
-
-	if (lance_debug > 0  &&  did_version++ == 0)
-		printk(version);
-
-	/* The LANCE-specific entries in the device structure. */
-	dev->netdev_ops = &lance_netdev_ops;
-	dev->watchdog_timeo = TX_TIMEOUT;
-
-	err = register_netdev(dev);
-	if (err)
-		goto out_dma;
-	return 0;
-out_dma:
-	if (dev->dma != 4)
-		free_dma(dev->dma);
-out_tx:
-	kfree(lp->tx_bounce_buffs);
-out_rx:
-	kfree((void*)lp->rx_buffs);
-out_lp:
-	kfree(lp);
-	return err;
-}
-
-
-static int
-lance_open(struct net_device *dev)
-{
-	struct lance_private *lp = dev->ml_priv;
-	int ioaddr = dev->base_addr;
-	int i;
-
-	if (dev->irq == 0 ||
-		request_irq(dev->irq, lance_interrupt, 0, dev->name, dev)) {
-		return -EAGAIN;
-	}
-
-	/* We used to allocate DMA here, but that was silly.
-	   DMA lines can't be shared!  We now permanently allocate them. */
-
-	/* Reset the LANCE */
-	inw(ioaddr+LANCE_RESET);
-
-	/* The DMA controller is used as a no-operation slave, "cascade mode". */
-	if (dev->dma != 4) {
-		unsigned long flags=claim_dma_lock();
-		enable_dma(dev->dma);
-		set_dma_mode(dev->dma, DMA_MODE_CASCADE);
-		release_dma_lock(flags);
-	}
-
-	/* Un-Reset the LANCE, needed only for the NE2100. */
-	if (chip_table[lp->chip_version].flags & LANCE_MUST_UNRESET)
-		outw(0, ioaddr+LANCE_RESET);
-
-	if (chip_table[lp->chip_version].flags & LANCE_ENABLE_AUTOSELECT) {
-		/* This is 79C960-specific: Turn on auto-select of media (AUI, BNC). */
-		outw(0x0002, ioaddr+LANCE_ADDR);
-		/* Only touch autoselect bit. */
-		outw(inw(ioaddr+LANCE_BUS_IF) | 0x0002, ioaddr+LANCE_BUS_IF);
-	}
-
-	if (lance_debug > 1)
-		printk("%s: lance_open() irq %d dma %d tx/rx rings %#x/%#x init %#x.\n",
-			   dev->name, dev->irq, dev->dma,
-		           (u32) isa_virt_to_bus(lp->tx_ring),
-		           (u32) isa_virt_to_bus(lp->rx_ring),
-			   (u32) isa_virt_to_bus(&lp->init_block));
-
-	lance_init_ring(dev, GFP_KERNEL);
-	/* Re-initialize the LANCE, and start it when done. */
-	outw(0x0001, ioaddr+LANCE_ADDR);
-	outw((short) (u32) isa_virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA);
-	outw(0x0002, ioaddr+LANCE_ADDR);
-	outw(((u32)isa_virt_to_bus(&lp->init_block)) >> 16, ioaddr+LANCE_DATA);
-
-	outw(0x0004, ioaddr+LANCE_ADDR);
-	outw(0x0915, ioaddr+LANCE_DATA);
-
-	outw(0x0000, ioaddr+LANCE_ADDR);
-	outw(0x0001, ioaddr+LANCE_DATA);
-
-	netif_start_queue (dev);
-
-	i = 0;
-	while (i++ < 100)
-		if (inw(ioaddr+LANCE_DATA) & 0x0100)
-			break;
-	/*
-	 * We used to clear the InitDone bit, 0x0100, here but Mark Stockton
-	 * reports that doing so triggers a bug in the '974.
-	 */
-	outw(0x0042, ioaddr+LANCE_DATA);
-
-	if (lance_debug > 2)
-		printk("%s: LANCE open after %d ticks, init block %#x csr0 %4.4x.\n",
-			   dev->name, i, (u32) isa_virt_to_bus(&lp->init_block), inw(ioaddr+LANCE_DATA));
-
-	return 0;					/* Always succeed */
-}
-
-/* The LANCE has been halted for one reason or another (busmaster memory
-   arbitration error, Tx FIFO underflow, driver stopped it to reconfigure,
-   etc.).  Modern LANCE variants always reload their ring-buffer
-   configuration when restarted, so we must reinitialize our ring
-   context before restarting.  As part of this reinitialization,
-   find all packets still on the Tx ring and pretend that they had been
-   sent (in effect, drop the packets on the floor) - the higher-level
-   protocols will time out and retransmit.  It'd be better to shuffle
-   these skbs to a temp list and then actually re-Tx them after
-   restarting the chip, but I'm too lazy to do so right now.  dplatt@3do.com
-*/
-
-static void
-lance_purge_ring(struct net_device *dev)
-{
-	struct lance_private *lp = dev->ml_priv;
-	int i;
-
-	/* Free all the skbuffs in the Rx and Tx queues. */
-	for (i = 0; i < RX_RING_SIZE; i++) {
-		struct sk_buff *skb = lp->rx_skbuff[i];
-		lp->rx_skbuff[i] = NULL;
-		lp->rx_ring[i].base = 0;		/* Not owned by LANCE chip. */
-		if (skb)
-			dev_kfree_skb_any(skb);
-	}
-	for (i = 0; i < TX_RING_SIZE; i++) {
-		if (lp->tx_skbuff[i]) {
-			dev_kfree_skb_any(lp->tx_skbuff[i]);
-			lp->tx_skbuff[i] = NULL;
-		}
-	}
-}
-
-
-/* Initialize the LANCE Rx and Tx rings. */
-static void
-lance_init_ring(struct net_device *dev, gfp_t gfp)
-{
-	struct lance_private *lp = dev->ml_priv;
-	int i;
-
-	lp->cur_rx = lp->cur_tx = 0;
-	lp->dirty_rx = lp->dirty_tx = 0;
-
-	for (i = 0; i < RX_RING_SIZE; i++) {
-		struct sk_buff *skb;
-		void *rx_buff;
-
-		skb = alloc_skb(PKT_BUF_SZ, GFP_DMA | gfp);
-		lp->rx_skbuff[i] = skb;
-		if (skb)
-			rx_buff = skb->data;
-		else
-			rx_buff = kmalloc(PKT_BUF_SZ, GFP_DMA | gfp);
-		if (!rx_buff)
-			lp->rx_ring[i].base = 0;
-		else
-			lp->rx_ring[i].base = (u32)isa_virt_to_bus(rx_buff) | 0x80000000;
-		lp->rx_ring[i].buf_length = -PKT_BUF_SZ;
-	}
-	/* The Tx buffer address is filled in as needed, but we do need to clear
-	   the upper ownership bit. */
-	for (i = 0; i < TX_RING_SIZE; i++) {
-		lp->tx_skbuff[i] = NULL;
-		lp->tx_ring[i].base = 0;
-	}
-
-	lp->init_block.mode = 0x0000;
-	for (i = 0; i < 6; i++)
-		lp->init_block.phys_addr[i] = dev->dev_addr[i];
-	lp->init_block.filter[0] = 0x00000000;
-	lp->init_block.filter[1] = 0x00000000;
-	lp->init_block.rx_ring = ((u32)isa_virt_to_bus(lp->rx_ring) & 0xffffff) | RX_RING_LEN_BITS;
-	lp->init_block.tx_ring = ((u32)isa_virt_to_bus(lp->tx_ring) & 0xffffff) | TX_RING_LEN_BITS;
-}
-
-static void
-lance_restart(struct net_device *dev, unsigned int csr0_bits, int must_reinit)
-{
-	struct lance_private *lp = dev->ml_priv;
-
-	if (must_reinit ||
-		(chip_table[lp->chip_version].flags & LANCE_MUST_REINIT_RING)) {
-		lance_purge_ring(dev);
-		lance_init_ring(dev, GFP_ATOMIC);
-	}
-	outw(0x0000,    dev->base_addr + LANCE_ADDR);
-	outw(csr0_bits, dev->base_addr + LANCE_DATA);
-}
-
-
-static void lance_tx_timeout (struct net_device *dev, unsigned int txqueue)
-{
-	struct lance_private *lp = (struct lance_private *) dev->ml_priv;
-	int ioaddr = dev->base_addr;
-
-	outw (0, ioaddr + LANCE_ADDR);
-	printk ("%s: transmit timed out, status %4.4x, resetting.\n",
-		dev->name, inw (ioaddr + LANCE_DATA));
-	outw (0x0004, ioaddr + LANCE_DATA);
-	dev->stats.tx_errors++;
-#ifndef final_version
-	if (lance_debug > 3) {
-		int i;
-		printk (" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.",
-		  lp->dirty_tx, lp->cur_tx, netif_queue_stopped(dev) ? " (full)" : "",
-			lp->cur_rx);
-		for (i = 0; i < RX_RING_SIZE; i++)
-			printk ("%s %08x %04x %04x", i & 0x3 ? "" : "\n ",
-			 lp->rx_ring[i].base, -lp->rx_ring[i].buf_length,
-				lp->rx_ring[i].msg_length);
-		for (i = 0; i < TX_RING_SIZE; i++)
-			printk ("%s %08x %04x %04x", i & 0x3 ? "" : "\n ",
-			     lp->tx_ring[i].base, -lp->tx_ring[i].length,
-				lp->tx_ring[i].misc);
-		printk ("\n");
-	}
-#endif
-	lance_restart (dev, 0x0043, 1);
-
-	netif_trans_update(dev); /* prevent tx timeout */
-	netif_wake_queue (dev);
-}
-
-
-static netdev_tx_t lance_start_xmit(struct sk_buff *skb,
-				    struct net_device *dev)
-{
-	struct lance_private *lp = dev->ml_priv;
-	int ioaddr = dev->base_addr;
-	int entry;
-	unsigned long flags;
-
-	spin_lock_irqsave(&lp->devlock, flags);
-
-	if (lance_debug > 3) {
-		outw(0x0000, ioaddr+LANCE_ADDR);
-		printk("%s: lance_start_xmit() called, csr0 %4.4x.\n", dev->name,
-			   inw(ioaddr+LANCE_DATA));
-		outw(0x0000, ioaddr+LANCE_DATA);
-	}
-
-	/* Fill in a Tx ring entry */
-
-	/* Mask to ring buffer boundary. */
-	entry = lp->cur_tx & TX_RING_MOD_MASK;
-
-	/* Caution: the write order is important here, set the base address
-	   with the "ownership" bits last. */
-
-	/* The old LANCE chips doesn't automatically pad buffers to min. size. */
-	if (chip_table[lp->chip_version].flags & LANCE_MUST_PAD) {
-		if (skb->len < ETH_ZLEN) {
-			if (skb_padto(skb, ETH_ZLEN))
-				goto out;
-			lp->tx_ring[entry].length = -ETH_ZLEN;
-		}
-		else
-			lp->tx_ring[entry].length = -skb->len;
-	} else
-		lp->tx_ring[entry].length = -skb->len;
-
-	lp->tx_ring[entry].misc = 0x0000;
-
-	dev->stats.tx_bytes += skb->len;
-
-	/* If any part of this buffer is >16M we must copy it to a low-memory
-	   buffer. */
-	if ((u32)isa_virt_to_bus(skb->data) + skb->len > 0x01000000) {
-		if (lance_debug > 5)
-			printk("%s: bouncing a high-memory packet (%#x).\n",
-				   dev->name, (u32)isa_virt_to_bus(skb->data));
-		skb_copy_from_linear_data(skb, &lp->tx_bounce_buffs[entry], skb->len);
-		lp->tx_ring[entry].base =
-			((u32)isa_virt_to_bus((lp->tx_bounce_buffs + entry)) & 0xffffff) | 0x83000000;
-		dev_consume_skb_irq(skb);
-	} else {
-		lp->tx_skbuff[entry] = skb;
-		lp->tx_ring[entry].base = ((u32)isa_virt_to_bus(skb->data) & 0xffffff) | 0x83000000;
-	}
-	lp->cur_tx++;
-
-	/* Trigger an immediate send poll. */
-	outw(0x0000, ioaddr+LANCE_ADDR);
-	outw(0x0048, ioaddr+LANCE_DATA);
-
-	if ((lp->cur_tx - lp->dirty_tx) >= TX_RING_SIZE)
-		netif_stop_queue(dev);
-
-out:
-	spin_unlock_irqrestore(&lp->devlock, flags);
-	return NETDEV_TX_OK;
-}
-
-/* The LANCE interrupt handler. */
-static irqreturn_t lance_interrupt(int irq, void *dev_id)
-{
-	struct net_device *dev = dev_id;
-	struct lance_private *lp;
-	int csr0, ioaddr, boguscnt=10;
-	int must_restart;
-
-	ioaddr = dev->base_addr;
-	lp = dev->ml_priv;
-
-	spin_lock (&lp->devlock);
-
-	outw(0x00, dev->base_addr + LANCE_ADDR);
-	while ((csr0 = inw(dev->base_addr + LANCE_DATA)) & 0x8600 &&
-	       --boguscnt >= 0) {
-		/* Acknowledge all of the current interrupt sources ASAP. */
-		outw(csr0 & ~0x004f, dev->base_addr + LANCE_DATA);
-
-		must_restart = 0;
-
-		if (lance_debug > 5)
-			printk("%s: interrupt  csr0=%#2.2x new csr=%#2.2x.\n",
-				   dev->name, csr0, inw(dev->base_addr + LANCE_DATA));
-
-		if (csr0 & 0x0400)			/* Rx interrupt */
-			lance_rx(dev);
-
-		if (csr0 & 0x0200) {		/* Tx-done interrupt */
-			int dirty_tx = lp->dirty_tx;
-
-			while (dirty_tx < lp->cur_tx) {
-				int entry = dirty_tx & TX_RING_MOD_MASK;
-				int status = lp->tx_ring[entry].base;
-
-				if (status < 0)
-					break;			/* It still hasn't been Txed */
-
-				lp->tx_ring[entry].base = 0;
-
-				if (status & 0x40000000) {
-					/* There was an major error, log it. */
-					int err_status = lp->tx_ring[entry].misc;
-					dev->stats.tx_errors++;
-					if (err_status & 0x0400)
-						dev->stats.tx_aborted_errors++;
-					if (err_status & 0x0800)
-						dev->stats.tx_carrier_errors++;
-					if (err_status & 0x1000)
-						dev->stats.tx_window_errors++;
-					if (err_status & 0x4000) {
-						/* Ackk!  On FIFO errors the Tx unit is turned off! */
-						dev->stats.tx_fifo_errors++;
-						/* Remove this verbosity later! */
-						printk("%s: Tx FIFO error! Status %4.4x.\n",
-							   dev->name, csr0);
-						/* Restart the chip. */
-						must_restart = 1;
-					}
-				} else {
-					if (status & 0x18000000)
-						dev->stats.collisions++;
-					dev->stats.tx_packets++;
-				}
-
-				/* We must free the original skb if it's not a data-only copy
-				   in the bounce buffer. */
-				if (lp->tx_skbuff[entry]) {
-					dev_consume_skb_irq(lp->tx_skbuff[entry]);
-					lp->tx_skbuff[entry] = NULL;
-				}
-				dirty_tx++;
-			}
-
-#ifndef final_version
-			if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) {
-				printk("out-of-sync dirty pointer, %d vs. %d, full=%s.\n",
-					   dirty_tx, lp->cur_tx,
-					   netif_queue_stopped(dev) ? "yes" : "no");
-				dirty_tx += TX_RING_SIZE;
-			}
-#endif
-
-			/* if the ring is no longer full, accept more packets */
-			if (netif_queue_stopped(dev) &&
-			    dirty_tx > lp->cur_tx - TX_RING_SIZE + 2)
-				netif_wake_queue (dev);
-
-			lp->dirty_tx = dirty_tx;
-		}
-
-		/* Log misc errors. */
-		if (csr0 & 0x4000)
-			dev->stats.tx_errors++; /* Tx babble. */
-		if (csr0 & 0x1000)
-			dev->stats.rx_errors++; /* Missed a Rx frame. */
-		if (csr0 & 0x0800) {
-			printk("%s: Bus master arbitration failure, status %4.4x.\n",
-				   dev->name, csr0);
-			/* Restart the chip. */
-			must_restart = 1;
-		}
-
-		if (must_restart) {
-			/* stop the chip to clear the error condition, then restart */
-			outw(0x0000, dev->base_addr + LANCE_ADDR);
-			outw(0x0004, dev->base_addr + LANCE_DATA);
-			lance_restart(dev, 0x0002, 0);
-		}
-	}
-
-	/* Clear any other interrupt, and set interrupt enable. */
-	outw(0x0000, dev->base_addr + LANCE_ADDR);
-	outw(0x7940, dev->base_addr + LANCE_DATA);
-
-	if (lance_debug > 4)
-		printk("%s: exiting interrupt, csr%d=%#4.4x.\n",
-			   dev->name, inw(ioaddr + LANCE_ADDR),
-			   inw(dev->base_addr + LANCE_DATA));
-
-	spin_unlock (&lp->devlock);
-	return IRQ_HANDLED;
-}
-
-static int
-lance_rx(struct net_device *dev)
-{
-	struct lance_private *lp = dev->ml_priv;
-	int entry = lp->cur_rx & RX_RING_MOD_MASK;
-	int i;
-
-	/* If we own the next entry, it's a new packet. Send it up. */
-	while (lp->rx_ring[entry].base >= 0) {
-		int status = lp->rx_ring[entry].base >> 24;
-
-		if (status != 0x03) {			/* There was an error. */
-			/* There is a tricky error noted by John Murphy,
-			   <murf@perftech.com> to Russ Nelson: Even with full-sized
-			   buffers it's possible for a jabber packet to use two
-			   buffers, with only the last correctly noting the error. */
-			if (status & 0x01)	/* Only count a general error at the */
-				dev->stats.rx_errors++; /* end of a packet.*/
-			if (status & 0x20)
-				dev->stats.rx_frame_errors++;
-			if (status & 0x10)
-				dev->stats.rx_over_errors++;
-			if (status & 0x08)
-				dev->stats.rx_crc_errors++;
-			if (status & 0x04)
-				dev->stats.rx_fifo_errors++;
-			lp->rx_ring[entry].base &= 0x03ffffff;
-		}
-		else
-		{
-			/* Malloc up new buffer, compatible with net3. */
-			short pkt_len = (lp->rx_ring[entry].msg_length & 0xfff)-4;
-			struct sk_buff *skb;
-
-			if(pkt_len<60)
-			{
-				printk("%s: Runt packet!\n",dev->name);
-				dev->stats.rx_errors++;
-			}
-			else
-			{
-				skb = dev_alloc_skb(pkt_len+2);
-				if (!skb)
-				{
-					printk("%s: Memory squeeze, deferring packet.\n", dev->name);
-					for (i=0; i < RX_RING_SIZE; i++)
-						if (lp->rx_ring[(entry+i) & RX_RING_MOD_MASK].base < 0)
-							break;
-
-					if (i > RX_RING_SIZE -2)
-					{
-						dev->stats.rx_dropped++;
-						lp->rx_ring[entry].base |= 0x80000000;
-						lp->cur_rx++;
-					}
-					break;
-				}
-				skb_reserve(skb,2);	/* 16 byte align */
-				skb_put(skb,pkt_len);	/* Make room */
-				skb_copy_to_linear_data(skb,
-					(unsigned char *)isa_bus_to_virt((lp->rx_ring[entry].base & 0x00ffffff)),
-					pkt_len);
-				skb->protocol=eth_type_trans(skb,dev);
-				netif_rx(skb);
-				dev->stats.rx_packets++;
-				dev->stats.rx_bytes += pkt_len;
-			}
-		}
-		/* The docs say that the buffer length isn't touched, but Andrew Boyd
-		   of QNX reports that some revs of the 79C965 clear it. */
-		lp->rx_ring[entry].buf_length = -PKT_BUF_SZ;
-		lp->rx_ring[entry].base |= 0x80000000;
-		entry = (++lp->cur_rx) & RX_RING_MOD_MASK;
-	}
-
-	/* We should check that at least two ring entries are free.	 If not,
-	   we should free one and mark stats->rx_dropped++. */
-
-	return 0;
-}
-
-static int
-lance_close(struct net_device *dev)
-{
-	int ioaddr = dev->base_addr;
-	struct lance_private *lp = dev->ml_priv;
-
-	netif_stop_queue (dev);
-
-	if (chip_table[lp->chip_version].flags & LANCE_HAS_MISSED_FRAME) {
-		outw(112, ioaddr+LANCE_ADDR);
-		dev->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA);
-	}
-	outw(0, ioaddr+LANCE_ADDR);
-
-	if (lance_debug > 1)
-		printk("%s: Shutting down ethercard, status was %2.2x.\n",
-			   dev->name, inw(ioaddr+LANCE_DATA));
-
-	/* We stop the LANCE here -- it occasionally polls
-	   memory if we don't. */
-	outw(0x0004, ioaddr+LANCE_DATA);
-
-	if (dev->dma != 4)
-	{
-		unsigned long flags=claim_dma_lock();
-		disable_dma(dev->dma);
-		release_dma_lock(flags);
-	}
-	free_irq(dev->irq, dev);
-
-	lance_purge_ring(dev);
-
-	return 0;
-}
-
-static struct net_device_stats *lance_get_stats(struct net_device *dev)
-{
-	struct lance_private *lp = dev->ml_priv;
-
-	if (chip_table[lp->chip_version].flags & LANCE_HAS_MISSED_FRAME) {
-		short ioaddr = dev->base_addr;
-		short saved_addr;
-		unsigned long flags;
-
-		spin_lock_irqsave(&lp->devlock, flags);
-		saved_addr = inw(ioaddr+LANCE_ADDR);
-		outw(112, ioaddr+LANCE_ADDR);
-		dev->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA);
-		outw(saved_addr, ioaddr+LANCE_ADDR);
-		spin_unlock_irqrestore(&lp->devlock, flags);
-	}
-
-	return &dev->stats;
-}
-
-/* Set or clear the multicast filter for this adaptor.
- */
-
-static void set_multicast_list(struct net_device *dev)
-{
-	short ioaddr = dev->base_addr;
-
-	outw(0, ioaddr+LANCE_ADDR);
-	outw(0x0004, ioaddr+LANCE_DATA); /* Temporarily stop the lance.	 */
-
-	if (dev->flags&IFF_PROMISC) {
-		outw(15, ioaddr+LANCE_ADDR);
-		outw(0x8000, ioaddr+LANCE_DATA); /* Set promiscuous mode */
-	} else {
-		short multicast_table[4];
-		int i;
-		int num_addrs=netdev_mc_count(dev);
-		if(dev->flags&IFF_ALLMULTI)
-			num_addrs=1;
-		/* FIXIT: We don't use the multicast table, but rely on upper-layer filtering. */
-		memset(multicast_table, (num_addrs == 0) ? 0 : -1, sizeof(multicast_table));
-		for (i = 0; i < 4; i++) {
-			outw(8 + i, ioaddr+LANCE_ADDR);
-			outw(multicast_table[i], ioaddr+LANCE_DATA);
-		}
-		outw(15, ioaddr+LANCE_ADDR);
-		outw(0x0000, ioaddr+LANCE_DATA); /* Unset promiscuous mode */
-	}
-
-	lance_restart(dev, 0x0142, 0); /*  Resume normal operation */
-
-}
-

-- 
2.53.0


^ permalink raw reply related

* [PATCH net v2 03/15] drivers: net: 3com: 3c574: Remove this driver
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
  Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
	Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
In-Reply-To: <20260422-v7-0-0-net-next-driver-removal-v1-v2-0-08a5b59784d5@lunn.ch>

The 3c574 was written by Donald Becker between 19973-1998. It is an
PCMCIA device, so unlikely to be used with modern kernels.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
 drivers/net/ethernet/3com/3c574_cs.c | 1164 ----------------------------------
 drivers/net/ethernet/3com/Kconfig    |   10 -
 drivers/net/ethernet/3com/Makefile   |    1 -
 3 files changed, 1175 deletions(-)

diff --git a/drivers/net/ethernet/3com/3c574_cs.c b/drivers/net/ethernet/3com/3c574_cs.c
deleted file mode 100644
index 1f2070497a75..000000000000
--- a/drivers/net/ethernet/3com/3c574_cs.c
+++ /dev/null
@@ -1,1164 +0,0 @@
-/* 3c574.c: A PCMCIA ethernet driver for the 3com 3c574 "RoadRunner".
-
-	Written 1993-1998 by
-	Donald Becker, becker@scyld.com, (driver core) and
-	David Hinds, dahinds@users.sourceforge.net (from his PC card code).
-	Locking fixes (C) Copyright 2003 Red Hat Inc
-
-	This software may be used and distributed according to the terms of
-	the GNU General Public License, incorporated herein by reference.
-
-	This driver derives from Donald Becker's 3c509 core, which has the
-	following copyright:
-	Copyright 1993 United States Government as represented by the
-	Director, National Security Agency.
-	
-
-*/
-
-/*
-				Theory of Operation
-
-I. Board Compatibility
-
-This device driver is designed for the 3Com 3c574 PC card Fast Ethernet
-Adapter.
-
-II. Board-specific settings
-
-None -- PC cards are autoconfigured.
-
-III. Driver operation
-
-The 3c574 uses a Boomerang-style interface, without the bus-master capability.
-See the Boomerang driver and documentation for most details.
-
-IV. Notes and chip documentation.
-
-Two added registers are used to enhance PIO performance, RunnerRdCtrl and
-RunnerWrCtrl.  These are 11 bit down-counters that are preloaded with the
-count of word (16 bits) reads or writes the driver is about to do to the Rx
-or Tx FIFO.  The chip is then able to hide the internal-PCI-bus to PC-card
-translation latency by buffering the I/O operations with an 8 word FIFO.
-Note: No other chip accesses are permitted when this buffer is used.
-
-A second enhancement is that both attribute and common memory space
-0x0800-0x0fff can translated to the PIO FIFO.  Thus memory operations (faster
-with *some* PCcard bridges) may be used instead of I/O operations.
-This is enabled by setting the 0x10 bit in the PCMCIA LAN COR.
-
-Some slow PC card bridges work better if they never see a WAIT signal.
-This is configured by setting the 0x20 bit in the PCMCIA LAN COR.
-Only do this after testing that it is reliable and improves performance.
-
-The upper five bits of RunnerRdCtrl are used to window into PCcard
-configuration space registers.  Window 0 is the regular Boomerang/Odie
-register set, 1-5 are various PC card control registers, and 16-31 are
-the (reversed!) CIS table.
-
-A final note: writing the InternalConfig register in window 3 with an
-invalid ramWidth is Very Bad.
-
-V. References
-
-http://www.scyld.com/expert/NWay.html
-http://www.national.com/opf/DP/DP83840A.html
-
-Thanks to Terry Murphy of 3Com for providing development information for
-earlier 3Com products.
-
-*/
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/in.h>
-#include <linux/delay.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-#include <linux/ioport.h>
-#include <linux/bitops.h>
-#include <linux/mii.h>
-
-#include <pcmcia/cistpl.h>
-#include <pcmcia/cisreg.h>
-#include <pcmcia/ciscode.h>
-#include <pcmcia/ds.h>
-
-#include <linux/uaccess.h>
-#include <asm/io.h>
-
-/*====================================================================*/
-
-/* Module parameters */
-
-MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
-MODULE_DESCRIPTION("3Com 3c574 series PCMCIA ethernet driver");
-MODULE_LICENSE("GPL");
-
-#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
-
-/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-INT_MODULE_PARM(max_interrupt_work, 32);
-
-/* Force full duplex modes? */
-INT_MODULE_PARM(full_duplex, 0);
-
-/* Autodetect link polarity reversal? */
-INT_MODULE_PARM(auto_polarity, 1);
-
-
-/*====================================================================*/
-
-/* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT  ((800*HZ)/1000)
-
-/* To minimize the size of the driver source and make the driver more
-   readable not all constants are symbolically defined.
-   You'll need the manual if you want to understand driver details anyway. */
-/* Offsets from base I/O address. */
-#define EL3_DATA	0x00
-#define EL3_CMD		0x0e
-#define EL3_STATUS	0x0e
-
-#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
-
-/* The top five bits written to EL3_CMD are a command, the lower
-   11 bits are the parameter, if applicable. */
-enum el3_cmds {
-	TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11,
-	RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11,
-	TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
-	FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11,
-	SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
-	SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11,
-	StatsDisable = 22<<11, StopCoax = 23<<11,
-};
-
-enum elxl_status {
-	IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
-	TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
-	IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000 };
-
-/* The SetRxFilter command accepts the following classes: */
-enum RxFilter {
-	RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8
-};
-
-enum Window0 {
-	Wn0EepromCmd = 10, Wn0EepromData = 12, /* EEPROM command/address, data. */
-	IntrStatus=0x0E,		/* Valid in all windows. */
-};
-/* These assumes the larger EEPROM. */
-enum Win0_EEPROM_cmds {
-	EEPROM_Read = 0x200, EEPROM_WRITE = 0x100, EEPROM_ERASE = 0x300,
-	EEPROM_EWENB = 0x30,		/* Enable erasing/writing for 10 msec. */
-	EEPROM_EWDIS = 0x00,		/* Disable EWENB before 10 msec timeout. */
-};
-
-/* Register window 1 offsets, the window used in normal operation.
-   On the "Odie" this window is always mapped at offsets 0x10-0x1f.
-   Except for TxFree, which is overlapped by RunnerWrCtrl. */
-enum Window1 {
-	TX_FIFO = 0x10,  RX_FIFO = 0x10,  RxErrors = 0x14,
-	RxStatus = 0x18,  Timer=0x1A, TxStatus = 0x1B,
-	TxFree = 0x0C, /* Remaining free bytes in Tx buffer. */
-	RunnerRdCtrl = 0x16, RunnerWrCtrl = 0x1c,
-};
-
-enum Window3 {			/* Window 3: MAC/config bits. */
-	Wn3_Config=0, Wn3_MAC_Ctrl=6, Wn3_Options=8,
-};
-enum wn3_config {
-	Ram_size = 7,
-	Ram_width = 8,
-	Ram_speed = 0x30,
-	Rom_size = 0xc0,
-	Ram_split_shift = 16,
-	Ram_split = 3 << Ram_split_shift,
-	Xcvr_shift = 20,
-	Xcvr = 7 << Xcvr_shift,
-	Autoselect = 0x1000000,
-};
-
-enum Window4 {		/* Window 4: Xcvr/media bits. */
-	Wn4_FIFODiag = 4, Wn4_NetDiag = 6, Wn4_PhysicalMgmt=8, Wn4_Media = 10,
-};
-
-#define MEDIA_TP	0x00C0	/* Enable link beat and jabber for 10baseT. */
-
-struct el3_private {
-	struct pcmcia_device	*p_dev;
-	u16 advertising, partner;		/* NWay media advertisement */
-	unsigned char phys;			/* MII device address */
-	unsigned int autoselect:1, default_media:3;	/* Read from the EEPROM/Wn3_Config. */
-	/* for transceiver monitoring */
-	struct timer_list media;
-	unsigned short media_status;
-	unsigned short fast_poll;
-	unsigned long last_irq;
-	spinlock_t window_lock;			/* Guards the Window selection */
-};
-
-/* Set iff a MII transceiver on any interface requires mdio preamble.
-   This only set with the original DP83840 on older 3c905 boards, so the extra
-   code size of a per-interface flag is not worthwhile. */
-static char mii_preamble_required = 0;
-
-/* Index of functions. */
-
-static int tc574_config(struct pcmcia_device *link);
-static void tc574_release(struct pcmcia_device *link);
-
-static void mdio_sync(unsigned int ioaddr, int bits);
-static int mdio_read(unsigned int ioaddr, int phy_id, int location);
-static void mdio_write(unsigned int ioaddr, int phy_id, int location,
-		       int value);
-static unsigned short read_eeprom(unsigned int ioaddr, int index);
-static void tc574_wait_for_completion(struct net_device *dev, int cmd);
-
-static void tc574_reset(struct net_device *dev);
-static void media_check(struct timer_list *t);
-static int el3_open(struct net_device *dev);
-static netdev_tx_t el3_start_xmit(struct sk_buff *skb,
-					struct net_device *dev);
-static irqreturn_t el3_interrupt(int irq, void *dev_id);
-static void update_stats(struct net_device *dev);
-static struct net_device_stats *el3_get_stats(struct net_device *dev);
-static int el3_rx(struct net_device *dev, int worklimit);
-static int el3_close(struct net_device *dev);
-static void el3_tx_timeout(struct net_device *dev, unsigned int txqueue);
-static int el3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-static void set_rx_mode(struct net_device *dev);
-static void set_multicast_list(struct net_device *dev);
-
-static void tc574_detach(struct pcmcia_device *p_dev);
-
-/*
-	tc574_attach() creates an "instance" of the driver, allocating
-	local data structures for one device.  The device is registered
-	with Card Services.
-*/
-static const struct net_device_ops el3_netdev_ops = {
-	.ndo_open 		= el3_open,
-	.ndo_stop 		= el3_close,
-	.ndo_start_xmit		= el3_start_xmit,
-	.ndo_tx_timeout 	= el3_tx_timeout,
-	.ndo_get_stats		= el3_get_stats,
-	.ndo_eth_ioctl		= el3_ioctl,
-	.ndo_set_rx_mode	= set_multicast_list,
-	.ndo_set_mac_address 	= eth_mac_addr,
-	.ndo_validate_addr	= eth_validate_addr,
-};
-
-static int tc574_probe(struct pcmcia_device *link)
-{
-	struct el3_private *lp;
-	struct net_device *dev;
-
-	dev_dbg(&link->dev, "3c574_attach()\n");
-
-	/* Create the PC card device object. */
-	dev = alloc_etherdev(sizeof(struct el3_private));
-	if (!dev)
-		return -ENOMEM;
-	lp = netdev_priv(dev);
-	link->priv = dev;
-	lp->p_dev = link;
-
-	spin_lock_init(&lp->window_lock);
-	link->resource[0]->end = 32;
-	link->resource[0]->flags |= IO_DATA_PATH_WIDTH_16;
-	link->config_flags |= CONF_ENABLE_IRQ;
-	link->config_index = 1;
-
-	dev->netdev_ops = &el3_netdev_ops;
-	dev->watchdog_timeo = TX_TIMEOUT;
-
-	return tc574_config(link);
-}
-
-static void tc574_detach(struct pcmcia_device *link)
-{
-	struct net_device *dev = link->priv;
-
-	dev_dbg(&link->dev, "3c574_detach()\n");
-
-	unregister_netdev(dev);
-
-	tc574_release(link);
-
-	free_netdev(dev);
-} /* tc574_detach */
-
-static const char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
-
-static int tc574_config(struct pcmcia_device *link)
-{
-	struct net_device *dev = link->priv;
-	struct el3_private *lp = netdev_priv(dev);
-	int ret, i, j;
-	__be16 addr[ETH_ALEN / 2];
-	unsigned int ioaddr;
-	char *cardname;
-	__u32 config;
-	u8 *buf;
-	size_t len;
-
-	dev_dbg(&link->dev, "3c574_config()\n");
-
-	link->io_lines = 16;
-
-	for (i = j = 0; j < 0x400; j += 0x20) {
-		link->resource[0]->start = j ^ 0x300;
-		i = pcmcia_request_io(link);
-		if (i == 0)
-			break;
-	}
-	if (i != 0)
-		goto failed;
-
-	ret = pcmcia_request_irq(link, el3_interrupt);
-	if (ret)
-		goto failed;
-
-	ret = pcmcia_enable_device(link);
-	if (ret)
-		goto failed;
-
-	dev->irq = link->irq;
-	dev->base_addr = link->resource[0]->start;
-
-	ioaddr = dev->base_addr;
-
-	/* The 3c574 normally uses an EEPROM for configuration info, including
-	   the hardware address.  The future products may include a modem chip
-	   and put the address in the CIS. */
-
-	len = pcmcia_get_tuple(link, 0x88, &buf);
-	if (buf && len >= 6) {
-		for (i = 0; i < 3; i++)
-			addr[i] = htons(le16_to_cpu(buf[i * 2]));
-		kfree(buf);
-	} else {
-		kfree(buf); /* 0 < len < 6 */
-		EL3WINDOW(0);
-		for (i = 0; i < 3; i++)
-			addr[i] = htons(read_eeprom(ioaddr, i + 10));
-		if (addr[0] == htons(0x6060)) {
-			pr_notice("IO port conflict at 0x%03lx-0x%03lx\n",
-				  dev->base_addr, dev->base_addr+15);
-			goto failed;
-		}
-	}
-	eth_hw_addr_set(dev, (u8 *)addr);
-	if (link->prod_id[1])
-		cardname = link->prod_id[1];
-	else
-		cardname = "3Com 3c574";
-
-	{
-		u_char mcr;
-		outw(2<<11, ioaddr + RunnerRdCtrl);
-		mcr = inb(ioaddr + 2);
-		outw(0<<11, ioaddr + RunnerRdCtrl);
-		pr_info("  ASIC rev %d,", mcr>>3);
-		EL3WINDOW(3);
-		config = inl(ioaddr + Wn3_Config);
-		lp->default_media = (config & Xcvr) >> Xcvr_shift;
-		lp->autoselect = config & Autoselect ? 1 : 0;
-	}
-
-	timer_setup(&lp->media, media_check, 0);
-
-	{
-		int phy;
-		
-		/* Roadrunner only: Turn on the MII transceiver */
-		outw(0x8040, ioaddr + Wn3_Options);
-		mdelay(1);
-		outw(0xc040, ioaddr + Wn3_Options);
-		tc574_wait_for_completion(dev, TxReset);
-		tc574_wait_for_completion(dev, RxReset);
-		mdelay(1);
-		outw(0x8040, ioaddr + Wn3_Options);
-		
-		EL3WINDOW(4);
-		for (phy = 1; phy <= 32; phy++) {
-			int mii_status;
-			mdio_sync(ioaddr, 32);
-			mii_status = mdio_read(ioaddr, phy & 0x1f, 1);
-			if (mii_status != 0xffff) {
-				lp->phys = phy & 0x1f;
-				dev_dbg(&link->dev, "  MII transceiver at "
-					"index %d, status %x.\n",
-					  phy, mii_status);
-				if ((mii_status & 0x0040) == 0)
-					mii_preamble_required = 1;
-				break;
-			}
-		}
-		if (phy > 32) {
-			pr_notice("  No MII transceivers found!\n");
-			goto failed;
-		}
-		i = mdio_read(ioaddr, lp->phys, 16) | 0x40;
-		mdio_write(ioaddr, lp->phys, 16, i);
-		lp->advertising = mdio_read(ioaddr, lp->phys, 4);
-		if (full_duplex) {
-			/* Only advertise the FD media types. */
-			lp->advertising &= ~0x02a0;
-			mdio_write(ioaddr, lp->phys, 4, lp->advertising);
-		}
-	}
-
-	SET_NETDEV_DEV(dev, &link->dev);
-
-	if (register_netdev(dev) != 0) {
-		pr_notice("register_netdev() failed\n");
-		goto failed;
-	}
-
-	netdev_info(dev, "%s at io %#3lx, irq %d, hw_addr %pM\n",
-		    cardname, dev->base_addr, dev->irq, dev->dev_addr);
-	netdev_info(dev, " %dK FIFO split %s Rx:Tx, %sMII interface.\n",
-		    8 << (config & Ram_size),
-		    ram_split[(config & Ram_split) >> Ram_split_shift],
-		    config & Autoselect ? "autoselect " : "");
-
-	return 0;
-
-failed:
-	tc574_release(link);
-	return -ENODEV;
-
-} /* tc574_config */
-
-static void tc574_release(struct pcmcia_device *link)
-{
-	pcmcia_disable_device(link);
-}
-
-static int tc574_suspend(struct pcmcia_device *link)
-{
-	struct net_device *dev = link->priv;
-
-	if (link->open)
-		netif_device_detach(dev);
-
-	return 0;
-}
-
-static int tc574_resume(struct pcmcia_device *link)
-{
-	struct net_device *dev = link->priv;
-
-	if (link->open) {
-		tc574_reset(dev);
-		netif_device_attach(dev);
-	}
-
-	return 0;
-}
-
-static void dump_status(struct net_device *dev)
-{
-	unsigned int ioaddr = dev->base_addr;
-	EL3WINDOW(1);
-	netdev_info(dev, "  irq status %04x, rx status %04x, tx status %02x, tx free %04x\n",
-		    inw(ioaddr+EL3_STATUS),
-		    inw(ioaddr+RxStatus), inb(ioaddr+TxStatus),
-		    inw(ioaddr+TxFree));
-	EL3WINDOW(4);
-	netdev_info(dev, "  diagnostics: fifo %04x net %04x ethernet %04x media %04x\n",
-		    inw(ioaddr+0x04), inw(ioaddr+0x06),
-		    inw(ioaddr+0x08), inw(ioaddr+0x0a));
-	EL3WINDOW(1);
-}
-
-/*
-  Use this for commands that may take time to finish
-*/
-static void tc574_wait_for_completion(struct net_device *dev, int cmd)
-{
-	int i = 1500;
-	outw(cmd, dev->base_addr + EL3_CMD);
-	while (--i > 0)
-		if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000)) break;
-	if (i == 0)
-		netdev_notice(dev, "command 0x%04x did not complete!\n", cmd);
-}
-
-/* Read a word from the EEPROM using the regular EEPROM access register.
-   Assume that we are in register window zero.
- */
-static unsigned short read_eeprom(unsigned int ioaddr, int index)
-{
-	int timer;
-	outw(EEPROM_Read + index, ioaddr + Wn0EepromCmd);
-	/* Pause for at least 162 usec for the read to take place. */
-	for (timer = 1620; timer >= 0; timer--) {
-		if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0)
-			break;
-	}
-	return inw(ioaddr + Wn0EepromData);
-}
-
-/* MII transceiver control section.
-   Read and write the MII registers using software-generated serial
-   MDIO protocol.  See the MII specifications or DP83840A data sheet
-   for details.
-   The maxium data clock rate is 2.5 Mhz.  The timing is easily met by the
-   slow PC card interface. */
-
-#define MDIO_SHIFT_CLK	0x01
-#define MDIO_DIR_WRITE	0x04
-#define MDIO_DATA_WRITE0 (0x00 | MDIO_DIR_WRITE)
-#define MDIO_DATA_WRITE1 (0x02 | MDIO_DIR_WRITE)
-#define MDIO_DATA_READ	0x02
-#define MDIO_ENB_IN		0x00
-
-/* Generate the preamble required for initial synchronization and
-   a few older transceivers. */
-static void mdio_sync(unsigned int ioaddr, int bits)
-{
-	unsigned int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
-
-	/* Establish sync by sending at least 32 logic ones. */
-	while (-- bits >= 0) {
-		outw(MDIO_DATA_WRITE1, mdio_addr);
-		outw(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
-	}
-}
-
-static int mdio_read(unsigned int ioaddr, int phy_id, int location)
-{
-	int i;
-	int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
-	unsigned int retval = 0;
-	unsigned int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
-
-	if (mii_preamble_required)
-		mdio_sync(ioaddr, 32);
-
-	/* Shift the read command bits out. */
-	for (i = 14; i >= 0; i--) {
-		int dataval = (read_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
-		outw(dataval, mdio_addr);
-		outw(dataval | MDIO_SHIFT_CLK, mdio_addr);
-	}
-	/* Read the two transition, 16 data, and wire-idle bits. */
-	for (i = 19; i > 0; i--) {
-		outw(MDIO_ENB_IN, mdio_addr);
-		retval = (retval << 1) | ((inw(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
-		outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
-	}
-	return (retval>>1) & 0xffff;
-}
-
-static void mdio_write(unsigned int ioaddr, int phy_id, int location, int value)
-{
-	int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value;
-	unsigned int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
-	int i;
-
-	if (mii_preamble_required)
-		mdio_sync(ioaddr, 32);
-
-	/* Shift the command bits out. */
-	for (i = 31; i >= 0; i--) {
-		int dataval = (write_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
-		outw(dataval, mdio_addr);
-		outw(dataval | MDIO_SHIFT_CLK, mdio_addr);
-	}
-	/* Leave the interface idle. */
-	for (i = 1; i >= 0; i--) {
-		outw(MDIO_ENB_IN, mdio_addr);
-		outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
-	}
-}
-
-/* Reset and restore all of the 3c574 registers. */
-static void tc574_reset(struct net_device *dev)
-{
-	struct el3_private *lp = netdev_priv(dev);
-	int i;
-	unsigned int ioaddr = dev->base_addr;
-	unsigned long flags;
-
-	tc574_wait_for_completion(dev, TotalReset|0x10);
-
-	spin_lock_irqsave(&lp->window_lock, flags);
-	/* Clear any transactions in progress. */
-	outw(0, ioaddr + RunnerWrCtrl);
-	outw(0, ioaddr + RunnerRdCtrl);
-
-	/* Set the station address and mask. */
-	EL3WINDOW(2);
-	for (i = 0; i < 6; i++)
-		outb(dev->dev_addr[i], ioaddr + i);
-	for (; i < 12; i+=2)
-		outw(0, ioaddr + i);
-
-	/* Reset config options */
-	EL3WINDOW(3);
-	outb((dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl);
-	outl((lp->autoselect ? 0x01000000 : 0) | 0x0062001b,
-		 ioaddr + Wn3_Config);
-	/* Roadrunner only: Turn on the MII transceiver. */
-	outw(0x8040, ioaddr + Wn3_Options);
-	mdelay(1);
-	outw(0xc040, ioaddr + Wn3_Options);
-	EL3WINDOW(1);
-	spin_unlock_irqrestore(&lp->window_lock, flags);
-	
-	tc574_wait_for_completion(dev, TxReset);
-	tc574_wait_for_completion(dev, RxReset);
-	mdelay(1);
-	spin_lock_irqsave(&lp->window_lock, flags);
-	EL3WINDOW(3);
-	outw(0x8040, ioaddr + Wn3_Options);
-
-	/* Switch to the stats window, and clear all stats by reading. */
-	outw(StatsDisable, ioaddr + EL3_CMD);
-	EL3WINDOW(6);
-	for (i = 0; i < 10; i++)
-		inb(ioaddr + i);
-	inw(ioaddr + 10);
-	inw(ioaddr + 12);
-	EL3WINDOW(4);
-	inb(ioaddr + 12);
-	inb(ioaddr + 13);
-
-	/* .. enable any extra statistics bits.. */
-	outw(0x0040, ioaddr + Wn4_NetDiag);
-	
-	EL3WINDOW(1);
-	spin_unlock_irqrestore(&lp->window_lock, flags);
-	
-	/* .. re-sync MII and re-fill what NWay is advertising. */
-	mdio_sync(ioaddr, 32);
-	mdio_write(ioaddr, lp->phys, 4, lp->advertising);
-	if (!auto_polarity) {
-		/* works for TDK 78Q2120 series MII's */
-		i = mdio_read(ioaddr, lp->phys, 16) | 0x20;
-		mdio_write(ioaddr, lp->phys, 16, i);
-	}
-
-	spin_lock_irqsave(&lp->window_lock, flags);
-	/* Switch to register set 1 for normal use, just for TxFree. */
-	set_rx_mode(dev);
-	spin_unlock_irqrestore(&lp->window_lock, flags);
-	outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
-	outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
-	outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
-	/* Allow status bits to be seen. */
-	outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
-	/* Ack all pending events, and set active indicator mask. */
-	outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
-		 ioaddr + EL3_CMD);
-	outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
-		 | AdapterFailure | RxEarly, ioaddr + EL3_CMD);
-}
-
-static int el3_open(struct net_device *dev)
-{
-	struct el3_private *lp = netdev_priv(dev);
-	struct pcmcia_device *link = lp->p_dev;
-
-	if (!pcmcia_dev_present(link))
-		return -ENODEV;
-	
-	link->open++;
-	netif_start_queue(dev);
-	
-	tc574_reset(dev);
-	lp->media.expires = jiffies + HZ;
-	add_timer(&lp->media);
-	
-	dev_dbg(&link->dev, "%s: opened, status %4.4x.\n",
-		  dev->name, inw(dev->base_addr + EL3_STATUS));
-	
-	return 0;
-}
-
-static void el3_tx_timeout(struct net_device *dev, unsigned int txqueue)
-{
-	unsigned int ioaddr = dev->base_addr;
-	
-	netdev_notice(dev, "Transmit timed out!\n");
-	dump_status(dev);
-	dev->stats.tx_errors++;
-	netif_trans_update(dev); /* prevent tx timeout */
-	/* Issue TX_RESET and TX_START commands. */
-	tc574_wait_for_completion(dev, TxReset);
-	outw(TxEnable, ioaddr + EL3_CMD);
-	netif_wake_queue(dev);
-}
-
-static void pop_tx_status(struct net_device *dev)
-{
-	unsigned int ioaddr = dev->base_addr;
-	int i;
-    
-	/* Clear the Tx status stack. */
-	for (i = 32; i > 0; i--) {
-		u_char tx_status = inb(ioaddr + TxStatus);
-		if (!(tx_status & 0x84))
-			break;
-		/* reset transmitter on jabber error or underrun */
-		if (tx_status & 0x30)
-			tc574_wait_for_completion(dev, TxReset);
-		if (tx_status & 0x38) {
-			pr_debug("%s: transmit error: status 0x%02x\n",
-				  dev->name, tx_status);
-			outw(TxEnable, ioaddr + EL3_CMD);
-			dev->stats.tx_aborted_errors++;
-		}
-		outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */
-	}
-}
-
-static netdev_tx_t el3_start_xmit(struct sk_buff *skb,
-					struct net_device *dev)
-{
-	unsigned int ioaddr = dev->base_addr;
-	struct el3_private *lp = netdev_priv(dev);
-	unsigned long flags;
-
-	pr_debug("%s: el3_start_xmit(length = %ld) called, "
-		  "status %4.4x.\n", dev->name, (long)skb->len,
-		  inw(ioaddr + EL3_STATUS));
-
-	spin_lock_irqsave(&lp->window_lock, flags);
-
-	dev->stats.tx_bytes += skb->len;
-
-	/* Put out the doubleword header... */
-	outw(skb->len, ioaddr + TX_FIFO);
-	outw(0, ioaddr + TX_FIFO);
-	/* ... and the packet rounded to a doubleword. */
-	outsl(ioaddr + TX_FIFO, skb->data, (skb->len+3)>>2);
-
-	/* TxFree appears only in Window 1, not offset 0x1c. */
-	if (inw(ioaddr + TxFree) <= 1536) {
-		netif_stop_queue(dev);
-		/* Interrupt us when the FIFO has room for max-sized packet. 
-		   The threshold is in units of dwords. */
-		outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD);
-	}
-
-	pop_tx_status(dev);
-	spin_unlock_irqrestore(&lp->window_lock, flags);
-	dev_kfree_skb(skb);
-	return NETDEV_TX_OK;
-}
-
-/* The EL3 interrupt handler. */
-static irqreturn_t el3_interrupt(int irq, void *dev_id)
-{
-	struct net_device *dev = (struct net_device *) dev_id;
-	struct el3_private *lp = netdev_priv(dev);
-	unsigned int ioaddr;
-	unsigned status;
-	int work_budget = max_interrupt_work;
-	int handled = 0;
-
-	if (!netif_device_present(dev))
-		return IRQ_NONE;
-	ioaddr = dev->base_addr;
-
-	pr_debug("%s: interrupt, status %4.4x.\n",
-		  dev->name, inw(ioaddr + EL3_STATUS));
-
-	spin_lock(&lp->window_lock);
-	
-	while ((status = inw(ioaddr + EL3_STATUS)) &
-		   (IntLatch | RxComplete | RxEarly | StatsFull)) {
-		if (!netif_device_present(dev) ||
-			((status & 0xe000) != 0x2000)) {
-			pr_debug("%s: Interrupt from dead card\n", dev->name);
-			break;
-		}
-
-		handled = 1;
-
-		if (status & RxComplete)
-			work_budget = el3_rx(dev, work_budget);
-
-		if (status & TxAvailable) {
-			pr_debug("  TX room bit was handled.\n");
-			/* There's room in the FIFO for a full-sized packet. */
-			outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
-			netif_wake_queue(dev);
-		}
-
-		if (status & TxComplete)
-			pop_tx_status(dev);
-
-		if (status & (AdapterFailure | RxEarly | StatsFull)) {
-			/* Handle all uncommon interrupts. */
-			if (status & StatsFull)
-				update_stats(dev);
-			if (status & RxEarly) {
-				work_budget = el3_rx(dev, work_budget);
-				outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
-			}
-			if (status & AdapterFailure) {
-				u16 fifo_diag;
-				EL3WINDOW(4);
-				fifo_diag = inw(ioaddr + Wn4_FIFODiag);
-				EL3WINDOW(1);
-				netdev_notice(dev, "adapter failure, FIFO diagnostic register %04x\n",
-					      fifo_diag);
-				if (fifo_diag & 0x0400) {
-					/* Tx overrun */
-					tc574_wait_for_completion(dev, TxReset);
-					outw(TxEnable, ioaddr + EL3_CMD);
-				}
-				if (fifo_diag & 0x2000) {
-					/* Rx underrun */
-					tc574_wait_for_completion(dev, RxReset);
-					set_rx_mode(dev);
-					outw(RxEnable, ioaddr + EL3_CMD);
-				}
-				outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
-			}
-		}
-
-		if (--work_budget < 0) {
-			pr_debug("%s: Too much work in interrupt, "
-				  "status %4.4x.\n", dev->name, status);
-			/* Clear all interrupts */
-			outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
-			break;
-		}
-		/* Acknowledge the IRQ. */
-		outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
-	}
-
-	pr_debug("%s: exiting interrupt, status %4.4x.\n",
-		  dev->name, inw(ioaddr + EL3_STATUS));
-		  
-	spin_unlock(&lp->window_lock);
-	return IRQ_RETVAL(handled);
-}
-
-/*
-    This timer serves two purposes: to check for missed interrupts
-	(and as a last resort, poll the NIC for events), and to monitor
-	the MII, reporting changes in cable status.
-*/
-static void media_check(struct timer_list *t)
-{
-	struct el3_private *lp = timer_container_of(lp, t, media);
-	struct net_device *dev = lp->p_dev->priv;
-	unsigned int ioaddr = dev->base_addr;
-	unsigned long flags;
-	unsigned short /* cable, */ media, partner;
-
-	if (!netif_device_present(dev))
-		goto reschedule;
-	
-	/* Check for pending interrupt with expired latency timer: with
-	   this, we can limp along even if the interrupt is blocked */
-	if ((inw(ioaddr + EL3_STATUS) & IntLatch) && (inb(ioaddr + Timer) == 0xff)) {
-		if (!lp->fast_poll)
-			netdev_info(dev, "interrupt(s) dropped!\n");
-
-		local_irq_save(flags);
-		el3_interrupt(dev->irq, dev);
-		local_irq_restore(flags);
-
-		lp->fast_poll = HZ;
-	}
-	if (lp->fast_poll) {
-		lp->fast_poll--;
-		lp->media.expires = jiffies + 2*HZ/100;
-		add_timer(&lp->media);
-		return;
-	}
-
-	spin_lock_irqsave(&lp->window_lock, flags);
-	EL3WINDOW(4);
-	media = mdio_read(ioaddr, lp->phys, 1);
-	partner = mdio_read(ioaddr, lp->phys, 5);
-	EL3WINDOW(1);
-	
-	if (media != lp->media_status) {
-		if ((media ^ lp->media_status) & 0x0004)
-			netdev_info(dev, "%s link beat\n",
-				    (lp->media_status & 0x0004) ? "lost" : "found");
-		if ((media ^ lp->media_status) & 0x0020) {
-			lp->partner = 0;
-			if (lp->media_status & 0x0020) {
-				netdev_info(dev, "autonegotiation restarted\n");
-			} else if (partner) {
-				partner &= lp->advertising;
-				lp->partner = partner;
-				netdev_info(dev, "autonegotiation complete: "
-					    "%dbaseT-%cD selected\n",
-					    (partner & 0x0180) ? 100 : 10,
-					    (partner & 0x0140) ? 'F' : 'H');
-			} else {
-				netdev_info(dev, "link partner did not autonegotiate\n");
-			}
-
-			EL3WINDOW(3);
-			outb((partner & 0x0140 ? 0x20 : 0) |
-				 (dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl);
-			EL3WINDOW(1);
-
-		}
-		if (media & 0x0010)
-			netdev_info(dev, "remote fault detected\n");
-		if (media & 0x0002)
-			netdev_info(dev, "jabber detected\n");
-		lp->media_status = media;
-	}
-	spin_unlock_irqrestore(&lp->window_lock, flags);
-
-reschedule:
-	lp->media.expires = jiffies + HZ;
-	add_timer(&lp->media);
-}
-
-static struct net_device_stats *el3_get_stats(struct net_device *dev)
-{
-	struct el3_private *lp = netdev_priv(dev);
-
-	if (netif_device_present(dev)) {
-		unsigned long flags;
-		spin_lock_irqsave(&lp->window_lock, flags);
-		update_stats(dev);
-		spin_unlock_irqrestore(&lp->window_lock, flags);
-	}
-	return &dev->stats;
-}
-
-/*  Update statistics.
-	Surprisingly this need not be run single-threaded, but it effectively is.
-	The counters clear when read, so the adds must merely be atomic.
- */
-static void update_stats(struct net_device *dev)
-{
-	unsigned int ioaddr = dev->base_addr;
-	u8 up;
-
-	pr_debug("%s: updating the statistics.\n", dev->name);
-
-	if (inw(ioaddr+EL3_STATUS) == 0xffff) /* No card. */
-		return;
-		
-	/* Unlike the 3c509 we need not turn off stats updates while reading. */
-	/* Switch to the stats window, and read everything. */
-	EL3WINDOW(6);
-	dev->stats.tx_carrier_errors 		+= inb(ioaddr + 0);
-	dev->stats.tx_heartbeat_errors		+= inb(ioaddr + 1);
-	/* Multiple collisions. */	   	inb(ioaddr + 2);
-	dev->stats.collisions			+= inb(ioaddr + 3);
-	dev->stats.tx_window_errors		+= inb(ioaddr + 4);
-	dev->stats.rx_fifo_errors		+= inb(ioaddr + 5);
-	dev->stats.tx_packets			+= inb(ioaddr + 6);
-	up		 			 = inb(ioaddr + 9);
-	dev->stats.tx_packets			+= (up&0x30) << 4;
-	/* Rx packets   */			   inb(ioaddr + 7);
-	/* Tx deferrals */			   inb(ioaddr + 8);
-	/* rx */				   inw(ioaddr + 10);
-	/* tx */				   inw(ioaddr + 12);
-
-	EL3WINDOW(4);
-	/* BadSSD */				   inb(ioaddr + 12);
-	up					 = inb(ioaddr + 13);
-
-	EL3WINDOW(1);
-}
-
-static int el3_rx(struct net_device *dev, int worklimit)
-{
-	unsigned int ioaddr = dev->base_addr;
-	short rx_status;
-	
-	pr_debug("%s: in rx_packet(), status %4.4x, rx_status %4.4x.\n",
-		  dev->name, inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus));
-	while (!((rx_status = inw(ioaddr + RxStatus)) & 0x8000) &&
-			worklimit > 0) {
-		worklimit--;
-		if (rx_status & 0x4000) { /* Error, update stats. */
-			short error = rx_status & 0x3800;
-			dev->stats.rx_errors++;
-			switch (error) {
-			case 0x0000:	dev->stats.rx_over_errors++; break;
-			case 0x0800:	dev->stats.rx_length_errors++; break;
-			case 0x1000:	dev->stats.rx_frame_errors++; break;
-			case 0x1800:	dev->stats.rx_length_errors++; break;
-			case 0x2000:	dev->stats.rx_frame_errors++; break;
-			case 0x2800:	dev->stats.rx_crc_errors++; break;
-			}
-		} else {
-			short pkt_len = rx_status & 0x7ff;
-			struct sk_buff *skb;
-
-			skb = netdev_alloc_skb(dev, pkt_len + 5);
-
-			pr_debug("  Receiving packet size %d status %4.4x.\n",
-				  pkt_len, rx_status);
-			if (skb != NULL) {
-				skb_reserve(skb, 2);
-				insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len),
-						((pkt_len+3)>>2));
-				skb->protocol = eth_type_trans(skb, dev);
-				netif_rx(skb);
-				dev->stats.rx_packets++;
-				dev->stats.rx_bytes += pkt_len;
-			} else {
-				pr_debug("%s: couldn't allocate a sk_buff of"
-					  " size %d.\n", dev->name, pkt_len);
-				dev->stats.rx_dropped++;
-			}
-		}
-		tc574_wait_for_completion(dev, RxDiscard);
-	}
-
-	return worklimit;
-}
-
-/* Provide ioctl() calls to examine the MII xcvr state. */
-static int el3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
-{
-	struct el3_private *lp = netdev_priv(dev);
-	unsigned int ioaddr = dev->base_addr;
-	struct mii_ioctl_data *data = if_mii(rq);
-	int phy = lp->phys & 0x1f;
-
-	pr_debug("%s: In ioct(%-.6s, %#4.4x) %4.4x %4.4x %4.4x %4.4x.\n",
-		  dev->name, rq->ifr_ifrn.ifrn_name, cmd,
-		  data->phy_id, data->reg_num, data->val_in, data->val_out);
-
-	switch(cmd) {
-	case SIOCGMIIPHY:		/* Get the address of the PHY in use. */
-		data->phy_id = phy;
-		fallthrough;
-	case SIOCGMIIREG:		/* Read the specified MII register. */
-		{
-			int saved_window;
-			unsigned long flags;
-
-			spin_lock_irqsave(&lp->window_lock, flags);
-			saved_window = inw(ioaddr + EL3_CMD) >> 13;
-			EL3WINDOW(4);
-			data->val_out = mdio_read(ioaddr, data->phy_id & 0x1f,
-						  data->reg_num & 0x1f);
-			EL3WINDOW(saved_window);
-			spin_unlock_irqrestore(&lp->window_lock, flags);
-			return 0;
-		}
-	case SIOCSMIIREG:		/* Write the specified MII register */
-		{
-			int saved_window;
-                       unsigned long flags;
-
-			spin_lock_irqsave(&lp->window_lock, flags);
-			saved_window = inw(ioaddr + EL3_CMD) >> 13;
-			EL3WINDOW(4);
-			mdio_write(ioaddr, data->phy_id & 0x1f,
-				   data->reg_num & 0x1f, data->val_in);
-			EL3WINDOW(saved_window);
-			spin_unlock_irqrestore(&lp->window_lock, flags);
-			return 0;
-		}
-	default:
-		return -EOPNOTSUPP;
-	}
-}
-
-/* The Odie chip has a 64 bin multicast filter, but the bit layout is not
-   documented.  Until it is we revert to receiving all multicast frames when
-   any multicast reception is desired.
-   Note: My other drivers emit a log message whenever promiscuous mode is
-   entered to help detect password sniffers.  This is less desirable on
-   typical PC card machines, so we omit the message.
-   */
-
-static void set_rx_mode(struct net_device *dev)
-{
-	unsigned int ioaddr = dev->base_addr;
-
-	if (dev->flags & IFF_PROMISC)
-		outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
-			 ioaddr + EL3_CMD);
-	else if (!netdev_mc_empty(dev) || (dev->flags & IFF_ALLMULTI))
-		outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD);
-	else
-		outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
-}
-
-static void set_multicast_list(struct net_device *dev)
-{
-	struct el3_private *lp = netdev_priv(dev);
-	unsigned long flags;
-
-	spin_lock_irqsave(&lp->window_lock, flags);
-	set_rx_mode(dev);
-	spin_unlock_irqrestore(&lp->window_lock, flags);
-}
-
-static int el3_close(struct net_device *dev)
-{
-	unsigned int ioaddr = dev->base_addr;
-	struct el3_private *lp = netdev_priv(dev);
-	struct pcmcia_device *link = lp->p_dev;
-
-	dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name);
-	
-	if (pcmcia_dev_present(link)) {
-		unsigned long flags;
-
-		/* Turn off statistics ASAP.  We update lp->stats below. */
-		outw(StatsDisable, ioaddr + EL3_CMD);
-		
-		/* Disable the receiver and transmitter. */
-		outw(RxDisable, ioaddr + EL3_CMD);
-		outw(TxDisable, ioaddr + EL3_CMD);
-		
-		/* Note: Switching to window 0 may disable the IRQ. */
-		EL3WINDOW(0);
-		spin_lock_irqsave(&lp->window_lock, flags);
-		update_stats(dev);
-		spin_unlock_irqrestore(&lp->window_lock, flags);
-
-		/* force interrupts off */
-		outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD);
-	}
-
-	link->open--;
-	netif_stop_queue(dev);
-	timer_delete_sync(&lp->media);
-
-	return 0;
-}
-
-static const struct pcmcia_device_id tc574_ids[] = {
-	PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0574),
-	PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x0556, "cis/3CCFEM556.cis"),
-	PCMCIA_DEVICE_NULL,
-};
-MODULE_DEVICE_TABLE(pcmcia, tc574_ids);
-
-static struct pcmcia_driver tc574_driver = {
-	.owner		= THIS_MODULE,
-	.name		= "3c574_cs",
-	.probe		= tc574_probe,
-	.remove		= tc574_detach,
-	.id_table       = tc574_ids,
-	.suspend	= tc574_suspend,
-	.resume		= tc574_resume,
-};
-module_pcmcia_driver(tc574_driver);
diff --git a/drivers/net/ethernet/3com/Kconfig b/drivers/net/ethernet/3com/Kconfig
index 3fd3202d9776..294403ad7141 100644
--- a/drivers/net/ethernet/3com/Kconfig
+++ b/drivers/net/ethernet/3com/Kconfig
@@ -17,16 +17,6 @@ config NET_VENDOR_3COM
 
 if NET_VENDOR_3COM
 
-config PCMCIA_3C574
-	tristate "3Com 3c574 PCMCIA support"
-	depends on PCMCIA && HAS_IOPORT
-	help
-	  Say Y here if you intend to attach a 3Com 3c574 or compatible PCMCIA
-	  (PC-card) Fast Ethernet card to your computer.
-
-	  To compile this driver as a module, choose M here: the module will be
-	  called 3c574_cs.  If unsure, say N.
-
 config PCMCIA_3C589
 	tristate "3Com 3c589 PCMCIA support"
 	depends on PCMCIA && HAS_IOPORT
diff --git a/drivers/net/ethernet/3com/Makefile b/drivers/net/ethernet/3com/Makefile
index babfd93d5d53..45fb9af9b5c7 100644
--- a/drivers/net/ethernet/3com/Makefile
+++ b/drivers/net/ethernet/3com/Makefile
@@ -4,6 +4,5 @@
 #
 
 obj-$(CONFIG_PCMCIA_3C589) += 3c589_cs.o
-obj-$(CONFIG_PCMCIA_3C574) += 3c574_cs.o
 obj-$(CONFIG_VORTEX) += 3c59x.o
 obj-$(CONFIG_TYPHOON) += typhoon.o

-- 
2.53.0


^ permalink raw reply related

* [PATCH net v2 04/15] drivers: net: 3com: 3c589: Remove this driver
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
  Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
	Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
In-Reply-To: <20260422-v7-0-0-net-next-driver-removal-v1-v2-0-08a5b59784d5@lunn.ch>

The 3c589 was written by David A. Hinds 2001. It is an PCMCIA device,
so unlikely to be used with modern kernels.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
 drivers/net/ethernet/3com/3c589_cs.c | 974 -----------------------------------
 drivers/net/ethernet/3com/Kconfig    |  10 -
 drivers/net/ethernet/3com/Makefile   |   1 -
 3 files changed, 985 deletions(-)

diff --git a/drivers/net/ethernet/3com/3c589_cs.c b/drivers/net/ethernet/3com/3c589_cs.c
deleted file mode 100644
index ea49be43b8c3..000000000000
--- a/drivers/net/ethernet/3com/3c589_cs.c
+++ /dev/null
@@ -1,974 +0,0 @@
-/* ======================================================================
- *
- * A PCMCIA ethernet driver for the 3com 3c589 card.
- *
- * Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
- *
- * 3c589_cs.c 1.162 2001/10/13 00:08:50
- *
- * The network driver code is based on Donald Becker's 3c589 code:
- *
- * Written 1994 by Donald Becker.
- * Copyright 1993 United States Government as represented by the
- * Director, National Security Agency.  This software may be used and
- * distributed according to the terms of the GNU General Public License,
- * incorporated herein by reference.
- * Donald Becker may be reached at becker@scyld.com
- *
- * Updated for 2.5.x by Alan Cox <alan@lxorguk.ukuu.org.uk>
- *
- * ======================================================================
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#define DRV_NAME	"3c589_cs"
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/ptrace.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/in.h>
-#include <linux/delay.h>
-#include <linux/ethtool.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-#include <linux/ioport.h>
-#include <linux/bitops.h>
-#include <linux/jiffies.h>
-#include <linux/uaccess.h>
-#include <linux/io.h>
-
-#include <pcmcia/cistpl.h>
-#include <pcmcia/cisreg.h>
-#include <pcmcia/ciscode.h>
-#include <pcmcia/ds.h>
-
-
-/* To minimize the size of the driver source I only define operating
- * constants if they are used several times. You'll need the manual
- * if you want to understand driver details.
- */
-
-/* Offsets from base I/O address. */
-#define EL3_DATA	0x00
-#define EL3_TIMER	0x0a
-#define EL3_CMD		0x0e
-#define EL3_STATUS	0x0e
-
-#define EEPROM_READ	0x0080
-#define EEPROM_BUSY	0x8000
-
-#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
-
-/* The top five bits written to EL3_CMD are a command, the lower
- * 11 bits are the parameter, if applicable.
- */
-
-enum c509cmd {
-	TotalReset	= 0<<11,
-	SelectWindow	= 1<<11,
-	StartCoax	= 2<<11,
-	RxDisable	= 3<<11,
-	RxEnable	= 4<<11,
-	RxReset		= 5<<11,
-	RxDiscard	= 8<<11,
-	TxEnable	= 9<<11,
-	TxDisable	= 10<<11,
-	TxReset		= 11<<11,
-	FakeIntr	= 12<<11,
-	AckIntr		= 13<<11,
-	SetIntrEnb	= 14<<11,
-	SetStatusEnb	= 15<<11,
-	SetRxFilter	= 16<<11,
-	SetRxThreshold	= 17<<11,
-	SetTxThreshold	= 18<<11,
-	SetTxStart	= 19<<11,
-	StatsEnable	= 21<<11,
-	StatsDisable	= 22<<11,
-	StopCoax	= 23<<11
-};
-
-enum c509status {
-	IntLatch	= 0x0001,
-	AdapterFailure	= 0x0002,
-	TxComplete	= 0x0004,
-	TxAvailable	= 0x0008,
-	RxComplete	= 0x0010,
-	RxEarly		= 0x0020,
-	IntReq		= 0x0040,
-	StatsFull	= 0x0080,
-	CmdBusy		= 0x1000
-};
-
-/* The SetRxFilter command accepts the following classes: */
-enum RxFilter {
-	RxStation	= 1,
-	RxMulticast	= 2,
-	RxBroadcast	= 4,
-	RxProm		= 8
-};
-
-/* Register window 1 offsets, the window used in normal operation. */
-#define TX_FIFO		0x00
-#define RX_FIFO		0x00
-#define RX_STATUS	0x08
-#define TX_STATUS	0x0B
-#define TX_FREE		0x0C	/* Remaining free bytes in Tx buffer. */
-
-#define WN0_IRQ		0x08	/* Window 0: Set IRQ line in bits 12-15. */
-#define WN4_MEDIA	0x0A	/* Window 4: Various transcvr/media bits. */
-#define MEDIA_TP	0x00C0	/* Enable link beat and jabber for 10baseT. */
-#define MEDIA_LED	0x0001	/* Enable link light on 3C589E cards. */
-
-/* Time in jiffies before concluding Tx hung */
-#define TX_TIMEOUT	((400*HZ)/1000)
-
-struct el3_private {
-	struct pcmcia_device	*p_dev;
-	/* For transceiver monitoring */
-	struct timer_list	media;
-	u16			media_status;
-	u16			fast_poll;
-	unsigned long		last_irq;
-	spinlock_t		lock;
-};
-
-static const char *if_names[] = { "auto", "10baseT", "10base2", "AUI" };
-
-/*====================================================================*/
-
-/* Module parameters */
-
-MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
-MODULE_DESCRIPTION("3Com 3c589 series PCMCIA ethernet driver");
-MODULE_LICENSE("GPL");
-
-#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
-
-/* Special hook for setting if_port when module is loaded */
-INT_MODULE_PARM(if_port, 0);
-
-
-/*====================================================================*/
-
-static int tc589_config(struct pcmcia_device *link);
-static void tc589_release(struct pcmcia_device *link);
-
-static u16 read_eeprom(unsigned int ioaddr, int index);
-static void tc589_reset(struct net_device *dev);
-static void media_check(struct timer_list *t);
-static int el3_config(struct net_device *dev, struct ifmap *map);
-static int el3_open(struct net_device *dev);
-static netdev_tx_t el3_start_xmit(struct sk_buff *skb,
-					struct net_device *dev);
-static irqreturn_t el3_interrupt(int irq, void *dev_id);
-static void update_stats(struct net_device *dev);
-static struct net_device_stats *el3_get_stats(struct net_device *dev);
-static int el3_rx(struct net_device *dev);
-static int el3_close(struct net_device *dev);
-static void el3_tx_timeout(struct net_device *dev, unsigned int txqueue);
-static void set_rx_mode(struct net_device *dev);
-static void set_multicast_list(struct net_device *dev);
-static const struct ethtool_ops netdev_ethtool_ops;
-
-static void tc589_detach(struct pcmcia_device *p_dev);
-
-static const struct net_device_ops el3_netdev_ops = {
-	.ndo_open		= el3_open,
-	.ndo_stop		= el3_close,
-	.ndo_start_xmit		= el3_start_xmit,
-	.ndo_tx_timeout		= el3_tx_timeout,
-	.ndo_set_config		= el3_config,
-	.ndo_get_stats		= el3_get_stats,
-	.ndo_set_rx_mode	= set_multicast_list,
-	.ndo_set_mac_address	= eth_mac_addr,
-	.ndo_validate_addr	= eth_validate_addr,
-};
-
-static int tc589_probe(struct pcmcia_device *link)
-{
-	struct el3_private *lp;
-	struct net_device *dev;
-	int ret;
-
-	dev_dbg(&link->dev, "3c589_attach()\n");
-
-	/* Create new ethernet device */
-	dev = alloc_etherdev(sizeof(struct el3_private));
-	if (!dev)
-		return -ENOMEM;
-	lp = netdev_priv(dev);
-	link->priv = dev;
-	lp->p_dev = link;
-
-	spin_lock_init(&lp->lock);
-	link->resource[0]->end = 16;
-	link->resource[0]->flags |= IO_DATA_PATH_WIDTH_16;
-
-	link->config_flags |= CONF_ENABLE_IRQ;
-	link->config_index = 1;
-
-	dev->netdev_ops = &el3_netdev_ops;
-	dev->watchdog_timeo = TX_TIMEOUT;
-
-	dev->ethtool_ops = &netdev_ethtool_ops;
-
-	ret = tc589_config(link);
-	if (ret)
-		goto err_free_netdev;
-
-	return 0;
-
-err_free_netdev:
-	free_netdev(dev);
-	return ret;
-}
-
-static void tc589_detach(struct pcmcia_device *link)
-{
-	struct net_device *dev = link->priv;
-
-	dev_dbg(&link->dev, "3c589_detach\n");
-
-	unregister_netdev(dev);
-
-	tc589_release(link);
-
-	free_netdev(dev);
-} /* tc589_detach */
-
-static int tc589_config(struct pcmcia_device *link)
-{
-	struct net_device *dev = link->priv;
-	int ret, i, j, multi = 0, fifo;
-	__be16 addr[ETH_ALEN / 2];
-	unsigned int ioaddr;
-	static const char * const ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
-	u8 *buf;
-	size_t len;
-
-	dev_dbg(&link->dev, "3c589_config\n");
-
-	/* Is this a 3c562? */
-	if (link->manf_id != MANFID_3COM)
-		dev_info(&link->dev, "hmmm, is this really a 3Com card??\n");
-	multi = (link->card_id == PRODID_3COM_3C562);
-
-	link->io_lines = 16;
-
-	/* For the 3c562, the base address must be xx00-xx7f */
-	for (i = j = 0; j < 0x400; j += 0x10) {
-		if (multi && (j & 0x80))
-			continue;
-		link->resource[0]->start = j ^ 0x300;
-		i = pcmcia_request_io(link);
-		if (i == 0)
-			break;
-	}
-	if (i != 0)
-		goto failed;
-
-	ret = pcmcia_request_irq(link, el3_interrupt);
-	if (ret)
-		goto failed;
-
-	ret = pcmcia_enable_device(link);
-	if (ret)
-		goto failed;
-
-	dev->irq = link->irq;
-	dev->base_addr = link->resource[0]->start;
-	ioaddr = dev->base_addr;
-	EL3WINDOW(0);
-
-	/* The 3c589 has an extra EEPROM for configuration info, including
-	 * the hardware address.  The 3c562 puts the address in the CIS.
-	 */
-	len = pcmcia_get_tuple(link, 0x88, &buf);
-	if (buf && len >= 6) {
-		for (i = 0; i < 3; i++)
-			addr[i] = htons(le16_to_cpu(buf[i*2]));
-		kfree(buf);
-	} else {
-		kfree(buf); /* 0 < len < 6 */
-		for (i = 0; i < 3; i++)
-			addr[i] = htons(read_eeprom(ioaddr, i));
-		if (addr[0] == htons(0x6060)) {
-			dev_err(&link->dev, "IO port conflict at 0x%03lx-0x%03lx\n",
-					dev->base_addr, dev->base_addr+15);
-			goto failed;
-		}
-	}
-	eth_hw_addr_set(dev, (u8 *)addr);
-
-	/* The address and resource configuration register aren't loaded from
-	 * the EEPROM and *must* be set to 0 and IRQ3 for the PCMCIA version.
-	 */
-
-	outw(0x3f00, ioaddr + 8);
-	fifo = inl(ioaddr);
-
-	/* The if_port symbol can be set when the module is loaded */
-	if ((if_port >= 0) && (if_port <= 3))
-		dev->if_port = if_port;
-	else
-		dev_err(&link->dev, "invalid if_port requested\n");
-
-	SET_NETDEV_DEV(dev, &link->dev);
-
-	if (register_netdev(dev) != 0) {
-		dev_err(&link->dev, "register_netdev() failed\n");
-		goto failed;
-	}
-
-	netdev_info(dev, "3Com 3c%s, io %#3lx, irq %d, hw_addr %pM\n",
-			(multi ? "562" : "589"), dev->base_addr, dev->irq,
-			dev->dev_addr);
-	netdev_info(dev, "  %dK FIFO split %s Rx:Tx, %s xcvr\n",
-			(fifo & 7) ? 32 : 8, ram_split[(fifo >> 16) & 3],
-			if_names[dev->if_port]);
-	return 0;
-
-failed:
-	tc589_release(link);
-	return -ENODEV;
-} /* tc589_config */
-
-static void tc589_release(struct pcmcia_device *link)
-{
-	pcmcia_disable_device(link);
-}
-
-static int tc589_suspend(struct pcmcia_device *link)
-{
-	struct net_device *dev = link->priv;
-
-	if (link->open)
-		netif_device_detach(dev);
-
-	return 0;
-}
-
-static int tc589_resume(struct pcmcia_device *link)
-{
-	struct net_device *dev = link->priv;
-
-	if (link->open) {
-		tc589_reset(dev);
-		netif_device_attach(dev);
-	}
-
-	return 0;
-}
-
-/*====================================================================*/
-
-/* Use this for commands that may take time to finish */
-
-static void tc589_wait_for_completion(struct net_device *dev, int cmd)
-{
-	int i = 100;
-	outw(cmd, dev->base_addr + EL3_CMD);
-	while (--i > 0)
-		if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000))
-			break;
-	if (i == 0)
-		netdev_warn(dev, "command 0x%04x did not complete!\n", cmd);
-}
-
-/* Read a word from the EEPROM using the regular EEPROM access register.
- * Assume that we are in register window zero.
- */
-
-static u16 read_eeprom(unsigned int ioaddr, int index)
-{
-	int i;
-	outw(EEPROM_READ + index, ioaddr + 10);
-	/* Reading the eeprom takes 162 us */
-	for (i = 1620; i >= 0; i--)
-		if ((inw(ioaddr + 10) & EEPROM_BUSY) == 0)
-			break;
-	return inw(ioaddr + 12);
-}
-
-/* Set transceiver type, perhaps to something other than what the user
- * specified in dev->if_port.
- */
-
-static void tc589_set_xcvr(struct net_device *dev, int if_port)
-{
-	struct el3_private *lp = netdev_priv(dev);
-	unsigned int ioaddr = dev->base_addr;
-
-	EL3WINDOW(0);
-	switch (if_port) {
-	case 0:
-	case 1:
-		outw(0, ioaddr + 6);
-		break;
-	case 2:
-		outw(3<<14, ioaddr + 6);
-		break;
-	case 3:
-		outw(1<<14, ioaddr + 6);
-		break;
-	}
-	/* On PCMCIA, this just turns on the LED */
-	outw((if_port == 2) ? StartCoax : StopCoax, ioaddr + EL3_CMD);
-	/* 10baseT interface, enable link beat and jabber check. */
-	EL3WINDOW(4);
-	outw(MEDIA_LED | ((if_port < 2) ? MEDIA_TP : 0), ioaddr + WN4_MEDIA);
-	EL3WINDOW(1);
-	if (if_port == 2)
-		lp->media_status = ((dev->if_port == 0) ? 0x8000 : 0x4000);
-	else
-		lp->media_status = ((dev->if_port == 0) ? 0x4010 : 0x8800);
-}
-
-static void dump_status(struct net_device *dev)
-{
-	unsigned int ioaddr = dev->base_addr;
-	EL3WINDOW(1);
-	netdev_info(dev, "  irq status %04x, rx status %04x, tx status %02x  tx free %04x\n",
-			inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS),
-			inb(ioaddr+TX_STATUS), inw(ioaddr+TX_FREE));
-	EL3WINDOW(4);
-	netdev_info(dev, "  diagnostics: fifo %04x net %04x ethernet %04x media %04x\n",
-			inw(ioaddr+0x04), inw(ioaddr+0x06), inw(ioaddr+0x08),
-			inw(ioaddr+0x0a));
-	EL3WINDOW(1);
-}
-
-/* Reset and restore all of the 3c589 registers. */
-static void tc589_reset(struct net_device *dev)
-{
-	unsigned int ioaddr = dev->base_addr;
-	int i;
-
-	EL3WINDOW(0);
-	outw(0x0001, ioaddr + 4);			/* Activate board. */
-	outw(0x3f00, ioaddr + 8);			/* Set the IRQ line. */
-
-	/* Set the station address in window 2. */
-	EL3WINDOW(2);
-	for (i = 0; i < 6; i++)
-		outb(dev->dev_addr[i], ioaddr + i);
-
-	tc589_set_xcvr(dev, dev->if_port);
-
-	/* Switch to the stats window, and clear all stats by reading. */
-	outw(StatsDisable, ioaddr + EL3_CMD);
-	EL3WINDOW(6);
-	for (i = 0; i < 9; i++)
-		inb(ioaddr+i);
-	inw(ioaddr + 10);
-	inw(ioaddr + 12);
-
-	/* Switch to register set 1 for normal use. */
-	EL3WINDOW(1);
-
-	set_rx_mode(dev);
-	outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
-	outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
-	outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
-	/* Allow status bits to be seen. */
-	outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
-	/* Ack all pending events, and set active indicator mask. */
-	outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
-	 ioaddr + EL3_CMD);
-	outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
-	 | AdapterFailure, ioaddr + EL3_CMD);
-}
-
-static void netdev_get_drvinfo(struct net_device *dev,
-			       struct ethtool_drvinfo *info)
-{
-	strscpy(info->driver, DRV_NAME, sizeof(info->driver));
-	snprintf(info->bus_info, sizeof(info->bus_info),
-		"PCMCIA 0x%lx", dev->base_addr);
-}
-
-static const struct ethtool_ops netdev_ethtool_ops = {
-	.get_drvinfo		= netdev_get_drvinfo,
-};
-
-static int el3_config(struct net_device *dev, struct ifmap *map)
-{
-	if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
-		if (map->port <= 3) {
-			WRITE_ONCE(dev->if_port, map->port);
-			netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]);
-			tc589_set_xcvr(dev, dev->if_port);
-		} else {
-			return -EINVAL;
-		}
-	}
-	return 0;
-}
-
-static int el3_open(struct net_device *dev)
-{
-	struct el3_private *lp = netdev_priv(dev);
-	struct pcmcia_device *link = lp->p_dev;
-
-	if (!pcmcia_dev_present(link))
-		return -ENODEV;
-
-	link->open++;
-	netif_start_queue(dev);
-
-	tc589_reset(dev);
-	timer_setup(&lp->media, media_check, 0);
-	mod_timer(&lp->media, jiffies + HZ);
-
-	dev_dbg(&link->dev, "%s: opened, status %4.4x.\n",
-	  dev->name, inw(dev->base_addr + EL3_STATUS));
-
-	return 0;
-}
-
-static void el3_tx_timeout(struct net_device *dev, unsigned int txqueue)
-{
-	unsigned int ioaddr = dev->base_addr;
-
-	netdev_warn(dev, "Transmit timed out!\n");
-	dump_status(dev);
-	dev->stats.tx_errors++;
-	netif_trans_update(dev); /* prevent tx timeout */
-	/* Issue TX_RESET and TX_START commands. */
-	tc589_wait_for_completion(dev, TxReset);
-	outw(TxEnable, ioaddr + EL3_CMD);
-	netif_wake_queue(dev);
-}
-
-static void pop_tx_status(struct net_device *dev)
-{
-	unsigned int ioaddr = dev->base_addr;
-	int i;
-
-	/* Clear the Tx status stack. */
-	for (i = 32; i > 0; i--) {
-		u_char tx_status = inb(ioaddr + TX_STATUS);
-		if (!(tx_status & 0x84))
-			break;
-		/* reset transmitter on jabber error or underrun */
-		if (tx_status & 0x30)
-			tc589_wait_for_completion(dev, TxReset);
-		if (tx_status & 0x38) {
-			netdev_dbg(dev, "transmit error: status 0x%02x\n", tx_status);
-			outw(TxEnable, ioaddr + EL3_CMD);
-			dev->stats.tx_aborted_errors++;
-		}
-		outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
-	}
-}
-
-static netdev_tx_t el3_start_xmit(struct sk_buff *skb,
-					struct net_device *dev)
-{
-	unsigned int ioaddr = dev->base_addr;
-	struct el3_private *priv = netdev_priv(dev);
-	unsigned long flags;
-
-	netdev_dbg(dev, "el3_start_xmit(length = %ld) called, status %4.4x.\n",
-	       (long)skb->len, inw(ioaddr + EL3_STATUS));
-
-	spin_lock_irqsave(&priv->lock, flags);
-
-	dev->stats.tx_bytes += skb->len;
-
-	/* Put out the doubleword header... */
-	outw(skb->len, ioaddr + TX_FIFO);
-	outw(0x00, ioaddr + TX_FIFO);
-	/* ... and the packet rounded to a doubleword. */
-	outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
-
-	if (inw(ioaddr + TX_FREE) <= 1536) {
-		netif_stop_queue(dev);
-		/* Interrupt us when the FIFO has room for max-sized packet. */
-		outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
-	}
-
-	pop_tx_status(dev);
-	spin_unlock_irqrestore(&priv->lock, flags);
-	dev_kfree_skb(skb);
-
-	return NETDEV_TX_OK;
-}
-
-/* The EL3 interrupt handler. */
-static irqreturn_t el3_interrupt(int irq, void *dev_id)
-{
-	struct net_device *dev = (struct net_device *) dev_id;
-	struct el3_private *lp = netdev_priv(dev);
-	unsigned int ioaddr;
-	__u16 status;
-	int i = 0, handled = 1;
-
-	if (!netif_device_present(dev))
-		return IRQ_NONE;
-
-	ioaddr = dev->base_addr;
-
-	netdev_dbg(dev, "interrupt, status %4.4x.\n", inw(ioaddr + EL3_STATUS));
-
-	spin_lock(&lp->lock);
-	while ((status = inw(ioaddr + EL3_STATUS)) &
-	(IntLatch | RxComplete | StatsFull)) {
-		if ((status & 0xe000) != 0x2000) {
-			netdev_dbg(dev, "interrupt from dead card\n");
-			handled = 0;
-			break;
-		}
-		if (status & RxComplete)
-			el3_rx(dev);
-		if (status & TxAvailable) {
-			netdev_dbg(dev, "    TX room bit was handled.\n");
-			/* There's room in the FIFO for a full-sized packet. */
-			outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
-			netif_wake_queue(dev);
-		}
-		if (status & TxComplete)
-			pop_tx_status(dev);
-		if (status & (AdapterFailure | RxEarly | StatsFull)) {
-			/* Handle all uncommon interrupts. */
-			if (status & StatsFull)		/* Empty statistics. */
-				update_stats(dev);
-			if (status & RxEarly) {
-				/* Rx early is unused. */
-				el3_rx(dev);
-				outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
-			}
-			if (status & AdapterFailure) {
-				u16 fifo_diag;
-				EL3WINDOW(4);
-				fifo_diag = inw(ioaddr + 4);
-				EL3WINDOW(1);
-				netdev_warn(dev, "adapter failure, FIFO diagnostic register %04x.\n",
-			    fifo_diag);
-				if (fifo_diag & 0x0400) {
-					/* Tx overrun */
-					tc589_wait_for_completion(dev, TxReset);
-					outw(TxEnable, ioaddr + EL3_CMD);
-				}
-				if (fifo_diag & 0x2000) {
-					/* Rx underrun */
-					tc589_wait_for_completion(dev, RxReset);
-					set_rx_mode(dev);
-					outw(RxEnable, ioaddr + EL3_CMD);
-				}
-				outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
-			}
-		}
-		if (++i > 10) {
-			netdev_err(dev, "infinite loop in interrupt, status %4.4x.\n",
-					status);
-			/* Clear all interrupts */
-			outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
-			break;
-		}
-		/* Acknowledge the IRQ. */
-		outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
-	}
-	lp->last_irq = jiffies;
-	spin_unlock(&lp->lock);
-	netdev_dbg(dev, "exiting interrupt, status %4.4x.\n",
-			inw(ioaddr + EL3_STATUS));
-	return IRQ_RETVAL(handled);
-}
-
-static void media_check(struct timer_list *t)
-{
-	struct el3_private *lp = timer_container_of(lp, t, media);
-	struct net_device *dev = lp->p_dev->priv;
-	unsigned int ioaddr = dev->base_addr;
-	u16 media, errs;
-	unsigned long flags;
-
-	if (!netif_device_present(dev))
-		goto reschedule;
-
-	/* Check for pending interrupt with expired latency timer: with
-	 * this, we can limp along even if the interrupt is blocked
-	 */
-	if ((inw(ioaddr + EL3_STATUS) & IntLatch) &&
-	(inb(ioaddr + EL3_TIMER) == 0xff)) {
-		if (!lp->fast_poll)
-			netdev_warn(dev, "interrupt(s) dropped!\n");
-
-		local_irq_save(flags);
-		el3_interrupt(dev->irq, dev);
-		local_irq_restore(flags);
-
-		lp->fast_poll = HZ;
-	}
-	if (lp->fast_poll) {
-		lp->fast_poll--;
-		lp->media.expires = jiffies + HZ/100;
-		add_timer(&lp->media);
-		return;
-	}
-
-	/* lp->lock guards the EL3 window. Window should always be 1 except
-	 * when the lock is held
-	 */
-
-	spin_lock_irqsave(&lp->lock, flags);
-	EL3WINDOW(4);
-	media = inw(ioaddr+WN4_MEDIA) & 0xc810;
-
-	/* Ignore collisions unless we've had no irq's recently */
-	if (time_before(jiffies, lp->last_irq + HZ)) {
-		media &= ~0x0010;
-	} else {
-		/* Try harder to detect carrier errors */
-		EL3WINDOW(6);
-		outw(StatsDisable, ioaddr + EL3_CMD);
-		errs = inb(ioaddr + 0);
-		outw(StatsEnable, ioaddr + EL3_CMD);
-		dev->stats.tx_carrier_errors += errs;
-		if (errs || (lp->media_status & 0x0010))
-			media |= 0x0010;
-	}
-
-	if (media != lp->media_status) {
-		if ((media & lp->media_status & 0x8000) &&
-				((lp->media_status ^ media) & 0x0800))
-		netdev_info(dev, "%s link beat\n",
-				(lp->media_status & 0x0800 ? "lost" : "found"));
-		else if ((media & lp->media_status & 0x4000) &&
-		 ((lp->media_status ^ media) & 0x0010))
-		netdev_info(dev, "coax cable %s\n",
-				(lp->media_status & 0x0010 ? "ok" : "problem"));
-		if (dev->if_port == 0) {
-			if (media & 0x8000) {
-				if (media & 0x0800)
-					netdev_info(dev, "flipped to 10baseT\n");
-				else
-			tc589_set_xcvr(dev, 2);
-			} else if (media & 0x4000) {
-				if (media & 0x0010)
-					tc589_set_xcvr(dev, 1);
-				else
-					netdev_info(dev, "flipped to 10base2\n");
-			}
-		}
-		lp->media_status = media;
-	}
-
-	EL3WINDOW(1);
-	spin_unlock_irqrestore(&lp->lock, flags);
-
-reschedule:
-	lp->media.expires = jiffies + HZ;
-	add_timer(&lp->media);
-}
-
-static struct net_device_stats *el3_get_stats(struct net_device *dev)
-{
-	struct el3_private *lp = netdev_priv(dev);
-	unsigned long flags;
-	struct pcmcia_device *link = lp->p_dev;
-
-	if (pcmcia_dev_present(link)) {
-		spin_lock_irqsave(&lp->lock, flags);
-		update_stats(dev);
-		spin_unlock_irqrestore(&lp->lock, flags);
-	}
-	return &dev->stats;
-}
-
-/* Update statistics.  We change to register window 6, so this should be run
-* single-threaded if the device is active. This is expected to be a rare
-* operation, and it's simpler for the rest of the driver to assume that
-* window 1 is always valid rather than use a special window-state variable.
-*
-* Caller must hold the lock for this
-*/
-
-static void update_stats(struct net_device *dev)
-{
-	unsigned int ioaddr = dev->base_addr;
-
-	netdev_dbg(dev, "updating the statistics.\n");
-	/* Turn off statistics updates while reading. */
-	outw(StatsDisable, ioaddr + EL3_CMD);
-	/* Switch to the stats window, and read everything. */
-	EL3WINDOW(6);
-	dev->stats.tx_carrier_errors	+= inb(ioaddr + 0);
-	dev->stats.tx_heartbeat_errors	+= inb(ioaddr + 1);
-	/* Multiple collisions. */
-	inb(ioaddr + 2);
-	dev->stats.collisions		+= inb(ioaddr + 3);
-	dev->stats.tx_window_errors		+= inb(ioaddr + 4);
-	dev->stats.rx_fifo_errors		+= inb(ioaddr + 5);
-	dev->stats.tx_packets		+= inb(ioaddr + 6);
-	/* Rx packets   */
-	inb(ioaddr + 7);
-	/* Tx deferrals */
-	inb(ioaddr + 8);
-	/* Rx octets */
-	inw(ioaddr + 10);
-	/* Tx octets */
-	inw(ioaddr + 12);
-
-	/* Back to window 1, and turn statistics back on. */
-	EL3WINDOW(1);
-	outw(StatsEnable, ioaddr + EL3_CMD);
-}
-
-static int el3_rx(struct net_device *dev)
-{
-	unsigned int ioaddr = dev->base_addr;
-	int worklimit = 32;
-	short rx_status;
-
-	netdev_dbg(dev, "in rx_packet(), status %4.4x, rx_status %4.4x.\n",
-	       inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS));
-	while (!((rx_status = inw(ioaddr + RX_STATUS)) & 0x8000) &&
-		    worklimit > 0) {
-		worklimit--;
-		if (rx_status & 0x4000) { /* Error, update stats. */
-			short error = rx_status & 0x3800;
-			dev->stats.rx_errors++;
-			switch (error) {
-			case 0x0000:
-				dev->stats.rx_over_errors++;
-				break;
-			case 0x0800:
-				dev->stats.rx_length_errors++;
-				break;
-			case 0x1000:
-				dev->stats.rx_frame_errors++;
-				break;
-			case 0x1800:
-				dev->stats.rx_length_errors++;
-				break;
-			case 0x2000:
-				dev->stats.rx_frame_errors++;
-				break;
-			case 0x2800:
-				dev->stats.rx_crc_errors++;
-				break;
-			}
-		} else {
-			short pkt_len = rx_status & 0x7ff;
-			struct sk_buff *skb;
-
-			skb = netdev_alloc_skb(dev, pkt_len + 5);
-
-			netdev_dbg(dev, "    Receiving packet size %d status %4.4x.\n",
-		       pkt_len, rx_status);
-			if (skb != NULL) {
-				skb_reserve(skb, 2);
-				insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len),
-			(pkt_len+3)>>2);
-				skb->protocol = eth_type_trans(skb, dev);
-				netif_rx(skb);
-				dev->stats.rx_packets++;
-				dev->stats.rx_bytes += pkt_len;
-			} else {
-				netdev_dbg(dev, "couldn't allocate a sk_buff of size %d.\n",
-			   pkt_len);
-				dev->stats.rx_dropped++;
-			}
-		}
-		/* Pop the top of the Rx FIFO */
-		tc589_wait_for_completion(dev, RxDiscard);
-	}
-	if (worklimit == 0)
-		netdev_warn(dev, "too much work in el3_rx!\n");
-	return 0;
-}
-
-static void set_rx_mode(struct net_device *dev)
-{
-	unsigned int ioaddr = dev->base_addr;
-	u16 opts = SetRxFilter | RxStation | RxBroadcast;
-
-	if (dev->flags & IFF_PROMISC)
-		opts |= RxMulticast | RxProm;
-	else if (!netdev_mc_empty(dev) || (dev->flags & IFF_ALLMULTI))
-		opts |= RxMulticast;
-	outw(opts, ioaddr + EL3_CMD);
-}
-
-static void set_multicast_list(struct net_device *dev)
-{
-	struct el3_private *priv = netdev_priv(dev);
-	unsigned long flags;
-
-	spin_lock_irqsave(&priv->lock, flags);
-	set_rx_mode(dev);
-	spin_unlock_irqrestore(&priv->lock, flags);
-}
-
-static int el3_close(struct net_device *dev)
-{
-	struct el3_private *lp = netdev_priv(dev);
-	struct pcmcia_device *link = lp->p_dev;
-	unsigned int ioaddr = dev->base_addr;
-
-	dev_dbg(&link->dev, "%s: shutting down ethercard.\n", dev->name);
-
-	if (pcmcia_dev_present(link)) {
-		/* Turn off statistics ASAP.  We update dev->stats below. */
-		outw(StatsDisable, ioaddr + EL3_CMD);
-
-		/* Disable the receiver and transmitter. */
-		outw(RxDisable, ioaddr + EL3_CMD);
-		outw(TxDisable, ioaddr + EL3_CMD);
-
-		if (dev->if_port == 2)
-			/* Turn off thinnet power.  Green! */
-			outw(StopCoax, ioaddr + EL3_CMD);
-		else if (dev->if_port == 1) {
-			/* Disable link beat and jabber */
-			EL3WINDOW(4);
-			outw(0, ioaddr + WN4_MEDIA);
-		}
-
-		/* Switching back to window 0 disables the IRQ. */
-		EL3WINDOW(0);
-		/* But we explicitly zero the IRQ line select anyway. */
-		outw(0x0f00, ioaddr + WN0_IRQ);
-
-		/* Check if the card still exists */
-		if ((inw(ioaddr+EL3_STATUS) & 0xe000) == 0x2000)
-			update_stats(dev);
-	}
-
-	link->open--;
-	netif_stop_queue(dev);
-	timer_delete_sync(&lp->media);
-
-	return 0;
-}
-
-static const struct pcmcia_device_id tc589_ids[] = {
-	PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0101, 0x0562),
-	PCMCIA_MFC_DEVICE_PROD_ID1(0, "Motorola MARQUIS", 0xf03e4e77),
-	PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0589),
-	PCMCIA_DEVICE_PROD_ID12("Farallon", "ENet", 0x58d93fc4, 0x992c2202),
-	PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x0035, "cis/3CXEM556.cis"),
-	PCMCIA_MFC_DEVICE_CIS_MANF_CARD(0, 0x0101, 0x003d, "cis/3CXEM556.cis"),
-	PCMCIA_DEVICE_NULL,
-};
-MODULE_DEVICE_TABLE(pcmcia, tc589_ids);
-
-static struct pcmcia_driver tc589_driver = {
-	.owner		= THIS_MODULE,
-	.name		= "3c589_cs",
-	.probe		= tc589_probe,
-	.remove		= tc589_detach,
-	.id_table	= tc589_ids,
-	.suspend	= tc589_suspend,
-	.resume		= tc589_resume,
-};
-module_pcmcia_driver(tc589_driver);
diff --git a/drivers/net/ethernet/3com/Kconfig b/drivers/net/ethernet/3com/Kconfig
index 294403ad7141..399cb6c56198 100644
--- a/drivers/net/ethernet/3com/Kconfig
+++ b/drivers/net/ethernet/3com/Kconfig
@@ -17,16 +17,6 @@ config NET_VENDOR_3COM
 
 if NET_VENDOR_3COM
 
-config PCMCIA_3C589
-	tristate "3Com 3c589 PCMCIA support"
-	depends on PCMCIA && HAS_IOPORT
-	help
-	  Say Y here if you intend to attach a 3Com 3c589 or compatible PCMCIA
-	  (PC-card) Ethernet card to your computer.
-
-	  To compile this driver as a module, choose M here: the module will be
-	  called 3c589_cs.  If unsure, say N.
-
 config VORTEX
 	tristate "3c590/3c900 series (592/595/597) \"Vortex/Boomerang\" support"
 	depends on (PCI || EISA) && HAS_IOPORT_MAP
diff --git a/drivers/net/ethernet/3com/Makefile b/drivers/net/ethernet/3com/Makefile
index 45fb9af9b5c7..5c4d07f1d456 100644
--- a/drivers/net/ethernet/3com/Makefile
+++ b/drivers/net/ethernet/3com/Makefile
@@ -3,6 +3,5 @@
 # Makefile for the 3Com Ethernet device drivers
 #
 
-obj-$(CONFIG_PCMCIA_3C589) += 3c589_cs.o
 obj-$(CONFIG_VORTEX) += 3c59x.o
 obj-$(CONFIG_TYPHOON) += typhoon.o

-- 
2.53.0


^ permalink raw reply related

* [PATCH net v2 00/15] Remove a number of ISA and PCMCIA Ethernet drivers
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
  Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
	Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn

These old drivers have not been much of a Maintenance burden until
recently. Now there are more newbies using AI and fuzzers finding
issues, resulting in more work for Maintainers. Fixing these old
drivers make little sense, if it is not clear they have users.

These mostly ISA and PCMCIA Ethernet devices, mostly from the last
century, a couple from 2001 or 2002. It seems unlikely they are still
used. However, remove them one patch at a time so they can be brought
back if somebody still has the hardware, runs modern kernels and wants
to take up the roll of driver Maintainer.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
Changes in v2:
- Drop removal of 3c59x and xirc2ps, MVME147
- Remove some documents which belong to the drivers being removed.
- Link to v1: https://lore.kernel.org/r/20260421-v7-0-0-net-next-driver-removal-v1-v1-0-69517c689d1f@lunn.ch

---
Andrew Lunn (15):
      drivers: net: 3com: 3c509: Remove this driver
      drivers: net: 3com: 3c515: Remove this driver
      drivers: net: 3com: 3c574: Remove this driver
      drivers: net: 3com: 3c589: Remove this driver
      drivers: net: amd: lance: Remove this driver
      drivers: net: amd: nmclan: Remove this driver
      drivers: net: smsc: smc9194: Remove this driver
      drivers: net: smsc: smc91c92: Remove this driver
      drivers: net: cirrus: cs89x0: Remove this driver
      drivers: net: cirrus: mac89x0: Remove this driver
      drivers: net: fujitsu: fmvj18x: Remove this driver
      drivers: net: 8390: AX88190: Remove this driver
      drivers: net: 8390: pcnet: Remove this driver
      drivers: net: 8390: ultra: Remove this driver
      drivers: net: 8390: wd80x3: Remove this driver

 .../device_drivers/ethernet/3com/3c509.rst         |  249 ---
 .../device_drivers/ethernet/cirrus/cs89x0.rst      |  647 ------
 .../device_drivers/ethernet/smsc/smc9.rst          |   48 -
 drivers/net/ethernet/3com/3c509.c                  | 1448 --------------
 drivers/net/ethernet/3com/3c515.c                  | 1566 ---------------
 drivers/net/ethernet/3com/3c574_cs.c               | 1164 -----------
 drivers/net/ethernet/3com/3c589_cs.c               |  974 ---------
 drivers/net/ethernet/3com/Kconfig                  |   45 -
 drivers/net/ethernet/3com/Makefile                 |    4 -
 drivers/net/ethernet/8390/Kconfig                  |   52 -
 drivers/net/ethernet/8390/Makefile                 |    4 -
 drivers/net/ethernet/8390/axnet_cs.c               | 1707 ----------------
 drivers/net/ethernet/8390/pcnet_cs.c               | 1717 ----------------
 drivers/net/ethernet/8390/smc-ultra.c              |  630 ------
 drivers/net/ethernet/8390/wd.c                     |  575 ------
 drivers/net/ethernet/Kconfig                       |    1 -
 drivers/net/ethernet/amd/Kconfig                   |   21 -
 drivers/net/ethernet/amd/Makefile                  |    2 -
 drivers/net/ethernet/amd/lance.c                   | 1317 -------------
 drivers/net/ethernet/amd/nmclan_cs.c               | 1508 --------------
 drivers/net/ethernet/cirrus/Kconfig                |   40 -
 drivers/net/ethernet/cirrus/Makefile               |    2 -
 drivers/net/ethernet/cirrus/cs89x0.c               | 1915 ------------------
 drivers/net/ethernet/cirrus/cs89x0.h               |  461 -----
 drivers/net/ethernet/cirrus/mac89x0.c              |  577 ------
 drivers/net/ethernet/fujitsu/Kconfig               |   30 -
 drivers/net/ethernet/fujitsu/Makefile              |    6 -
 drivers/net/ethernet/fujitsu/fmvj18x_cs.c          | 1176 -----------
 drivers/net/ethernet/smsc/Kconfig                  |   27 -
 drivers/net/ethernet/smsc/Makefile                 |    2 -
 drivers/net/ethernet/smsc/smc9194.c                | 1535 ---------------
 drivers/net/ethernet/smsc/smc91c92_cs.c            | 2059 --------------------
 32 files changed, 21509 deletions(-)
---
base-commit: 1f5ffc672165ff851063a5fd044b727ab2517ae3
change-id: 20260421-v7-0-0-net-next-driver-removal-v1-47c88c987b34

Best regards,
-- 
Andrew Lunn <andrew@lunn.ch>


^ permalink raw reply

* [PATCH net v2 01/15] drivers: net: 3com: 3c509: Remove this driver
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
  Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
	Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
In-Reply-To: <20260422-v7-0-0-net-next-driver-removal-v1-v2-0-08a5b59784d5@lunn.ch>

The 3c509 was written by Donald Becker between 1993-2000. It is an ISA
device, so unlikely to be used with modern kernels.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
v2
Delete Documentation as well
---
 .../device_drivers/ethernet/3com/3c509.rst         |  249 ----
 drivers/net/ethernet/3com/3c509.c                  | 1448 --------------------
 drivers/net/ethernet/3com/Kconfig                  |   14 -
 drivers/net/ethernet/3com/Makefile                 |    1 -
 4 files changed, 1712 deletions(-)

diff --git a/Documentation/networking/device_drivers/ethernet/3com/3c509.rst b/Documentation/networking/device_drivers/ethernet/3com/3c509.rst
deleted file mode 100644
index 47f706bacdd9..000000000000
--- a/Documentation/networking/device_drivers/ethernet/3com/3c509.rst
+++ /dev/null
@@ -1,249 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-=============================================================================
-Linux and the 3Com EtherLink III Series Ethercards (driver v1.18c and higher)
-=============================================================================
-
-This file contains the instructions and caveats for v1.18c and higher versions
-of the 3c509 driver. You should not use the driver without reading this file.
-
-release 1.0
-
-28 February 2002
-
-Current maintainer (corrections to):
-  David Ruggiero <jdr@farfalle.com>
-
-Introduction
-============
-
-The following are notes and information on using the 3Com EtherLink III series
-ethercards in Linux. These cards are commonly known by the most widely-used
-card's 3Com model number, 3c509. They are all 10mb/s ISA-bus cards and shouldn't
-be (but sometimes are) confused with the similarly-numbered PCI-bus "3c905"
-(aka "Vortex" or "Boomerang") series.  Kernel support for the 3c509 family is
-provided by the module 3c509.c, which has code to support all of the following
-models:
-
- - 3c509 (original ISA card)
- - 3c509B (later revision of the ISA card; supports full-duplex)
- - 3c589 (PCMCIA)
- - 3c589B (later revision of the 3c589; supports full-duplex)
- - 3c579 (EISA)
-
-Large portions of this documentation were heavily borrowed from the guide
-written the original author of the 3c509 driver, Donald Becker. The master
-copy of that document, which contains notes on older versions of the driver,
-currently resides on Scyld web server: http://www.scyld.com/.
-
-
-Special Driver Features
-=======================
-
-Overriding card settings
-
-The driver allows boot- or load-time overriding of the card's detected IOADDR,
-IRQ, and transceiver settings, although this capability shouldn't generally be
-needed except to enable full-duplex mode (see below). An example of the syntax
-for LILO parameters for doing this::
-
-    ether=10,0x310,3,0x3c509,eth0
-
-This configures the first found 3c509 card for IRQ 10, base I/O 0x310, and
-transceiver type 3 (10base2). The flag "0x3c509" must be set to avoid conflicts
-with other card types when overriding the I/O address. When the driver is
-loaded as a module, only the IRQ may be overridden. For example,
-setting two cards to IRQ10 and IRQ11 is done by using the irq module
-option::
-
-   options 3c509 irq=10,11
-
-
-Full-duplex mode
-================
-
-The v1.18c driver added support for the 3c509B's full-duplex capabilities.
-In order to enable and successfully use full-duplex mode, three conditions
-must be met:
-
-(a) You must have a Etherlink III card model whose hardware supports full-
-duplex operations. Currently, the only members of the 3c509 family that are
-positively known to support full-duplex are the 3c509B (ISA bus) and 3c589B
-(PCMCIA) cards. Cards without the "B" model designation do *not* support
-full-duplex mode; these include the original 3c509 (no "B"), the original
-3c589, the 3c529 (MCA bus), and the 3c579 (EISA bus).
-
-(b) You must be using your card's 10baseT transceiver (i.e., the RJ-45
-connector), not its AUI (thick-net) or 10base2 (thin-net/coax) interfaces.
-AUI and 10base2 network cabling is physically incapable of full-duplex
-operation.
-
-(c) Most importantly, your 3c509B must be connected to a link partner that is
-itself full-duplex capable. This is almost certainly one of two things: a full-
-duplex-capable  Ethernet switch (*not* a hub), or a full-duplex-capable NIC on
-another system that's connected directly to the 3c509B via a crossover cable.
-
-Full-duplex mode can be enabled using 'ethtool'.
-
-.. warning::
-
-  Extremely important caution concerning full-duplex mode
-
-  Understand that the 3c509B's hardware's full-duplex support is much more
-  limited than that provide by more modern network interface cards. Although
-  at the physical layer of the network it fully supports full-duplex operation,
-  the card was designed before the current Ethernet auto-negotiation (N-way)
-  spec was written. This means that the 3c509B family ***cannot and will not
-  auto-negotiate a full-duplex connection with its link partner under any
-  circumstances, no matter how it is initialized***. If the full-duplex mode
-  of the 3c509B is enabled, its link partner will very likely need to be
-  independently _forced_ into full-duplex mode as well; otherwise various nasty
-  failures will occur - at the very least, you'll see massive numbers of packet
-  collisions. This is one of very rare circumstances where disabling auto-
-  negotiation and forcing the duplex mode of a network interface card or switch
-  would ever be necessary or desirable.
-
-
-Available Transceiver Types
-===========================
-
-For versions of the driver v1.18c and above, the available transceiver types are:
-
-== =========================================================================
-0  transceiver type from EEPROM config (normally 10baseT); force half-duplex
-1  AUI (thick-net / DB15 connector)
-2  (undefined)
-3  10base2 (thin-net == coax / BNC connector)
-4  10baseT (RJ-45 connector); force half-duplex mode
-8  transceiver type and duplex mode taken from card's EEPROM config settings
-12 10baseT (RJ-45 connector); force full-duplex mode
-== =========================================================================
-
-Prior to driver version 1.18c, only transceiver codes 0-4 were supported. Note
-that the new transceiver codes 8 and 12 are the *only* ones that will enable
-full-duplex mode, no matter what the card's detected EEPROM settings might be.
-This insured that merely upgrading the driver from an earlier version would
-never automatically enable full-duplex mode in an existing installation;
-it must always be explicitly enabled via one of these code in order to be
-activated.
-
-The transceiver type can be changed using 'ethtool'.
-
-
-Interpretation of error messages and common problems
-----------------------------------------------------
-
-Error Messages
-^^^^^^^^^^^^^^
-
-eth0: Infinite loop in interrupt, status 2011.
-These are "mostly harmless" message indicating that the driver had too much
-work during that interrupt cycle. With a status of 0x2011 you are receiving
-packets faster than they can be removed from the card. This should be rare
-or impossible in normal operation. Possible causes of this error report are:
-
-   - a "green" mode enabled that slows the processor down when there is no
-     keyboard activity.
-
-   - some other device or device driver hogging the bus or disabling interrupts.
-     Check /proc/interrupts for excessive interrupt counts. The timer tick
-     interrupt should always be incrementing faster than the others.
-
-No received packets
-^^^^^^^^^^^^^^^^^^^
-
-If a 3c509, 3c562 or 3c589 can successfully transmit packets, but never
-receives packets (as reported by /proc/net/dev or 'ifconfig') you likely
-have an interrupt line problem. Check /proc/interrupts to verify that the
-card is actually generating interrupts. If the interrupt count is not
-increasing you likely have a physical conflict with two devices trying to
-use the same ISA IRQ line. The common conflict is with a sound card on IRQ10
-or IRQ5, and the easiest solution is to move the 3c509 to a different
-interrupt line. If the device is receiving packets but 'ping' doesn't work,
-you have a routing problem.
-
-Tx Carrier Errors Reported in /proc/net/dev
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-
-If an EtherLink III appears to transmit packets, but the "Tx carrier errors"
-field in /proc/net/dev increments as quickly as the Tx packet count, you
-likely have an unterminated network or the incorrect media transceiver selected.
-
-3c509B card is not detected on machines with an ISA PnP BIOS.
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-While the updated driver works with most PnP BIOS programs, it does not work
-with all. This can be fixed by disabling PnP support using the 3Com-supplied
-setup program.
-
-3c509 card is not detected on overclocked machines
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Increase the delay time in id_read_eeprom() from the current value, 500,
-to an absurdly high value, such as 5000.
-
-
-Decoding Status and Error Messages
-----------------------------------
-
-
-The bits in the main status register are:
-
-=====	======================================
-value 	description
-=====	======================================
-0x01 	Interrupt latch
-0x02 	Tx overrun, or Rx underrun
-0x04 	Tx complete
-0x08 	Tx FIFO room available
-0x10 	A complete Rx packet has arrived
-0x20 	A Rx packet has started to arrive
-0x40 	The driver has requested an interrupt
-0x80 	Statistics counter nearly full
-=====	======================================
-
-The bits in the transmit (Tx) status word are:
-
-=====	============================================
-value	description
-=====	============================================
-0x02	Out-of-window collision.
-0x04	Status stack overflow (normally impossible).
-0x08	16 collisions.
-0x10	Tx underrun (not enough PCI bus bandwidth).
-0x20	Tx jabber.
-0x40	Tx interrupt requested.
-0x80	Status is valid (this should always be set).
-=====	============================================
-
-
-When a transmit error occurs the driver produces a status message such as::
-
-   eth0: Transmit error, Tx status register 82
-
-The two values typically seen here are:
-
-0x82
-^^^^
-
-Out of window collision. This typically occurs when some other Ethernet
-host is incorrectly set to full duplex on a half duplex network.
-
-0x88
-^^^^
-
-16 collisions. This typically occurs when the network is exceptionally busy
-or when another host doesn't correctly back off after a collision. If this
-error is mixed with 0x82 errors it is the result of a host incorrectly set
-to full duplex (see above).
-
-Both of these errors are the result of network problems that should be
-corrected. They do not represent driver malfunction.
-
-
-Revision history (this file)
-============================
-
-28Feb02 v1.0  DR   New; major portions based on Becker original 3c509 docs
-
diff --git a/drivers/net/ethernet/3com/3c509.c b/drivers/net/ethernet/3com/3c509.c
deleted file mode 100644
index fb68339e1511..000000000000
--- a/drivers/net/ethernet/3com/3c509.c
+++ /dev/null
@@ -1,1448 +0,0 @@
-/* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */
-/*
-	Written 1993-2000 by Donald Becker.
-
-	Copyright 1994-2000 by Donald Becker.
-	Copyright 1993 United States Government as represented by the
-	Director, National Security Agency.	 This software may be used and
-	distributed according to the terms of the GNU General Public License,
-	incorporated herein by reference.
-
-	This driver is for the 3Com EtherLinkIII series.
-
-	The author may be reached as becker@scyld.com, or C/O
-	Scyld Computing Corporation
-	410 Severn Ave., Suite 210
-	Annapolis MD 21403
-
-	Known limitations:
-	Because of the way 3c509 ISA detection works it's difficult to predict
-	a priori which of several ISA-mode cards will be detected first.
-
-	This driver does not use predictive interrupt mode, resulting in higher
-	packet latency but lower overhead.  If interrupts are disabled for an
-	unusually long time it could also result in missed packets, but in
-	practice this rarely happens.
-
-
-	FIXES:
-		Alan Cox:       Removed the 'Unexpected interrupt' bug.
-		Michael Meskes:	Upgraded to Donald Becker's version 1.07.
-		Alan Cox:	Increased the eeprom delay. Regardless of
-				what the docs say some people definitely
-				get problems with lower (but in card spec)
-				delays
-		v1.10 4/21/97 Fixed module code so that multiple cards may be detected,
-				other cleanups.  -djb
-		Andrea Arcangeli:	Upgraded to Donald Becker's version 1.12.
-		Rick Payne:	Fixed SMP race condition
-		v1.13 9/8/97 Made 'max_interrupt_work' an insmod-settable variable -djb
-		v1.14 10/15/97 Avoided waiting..discard message for fast machines -djb
-		v1.15 1/31/98 Faster recovery for Tx errors. -djb
-		v1.16 2/3/98 Different ID port handling to avoid sound cards. -djb
-		v1.18 12Mar2001 Andrew Morton
-			- Avoid bogus detect of 3c590's (Andrzej Krzysztofowicz)
-			- Reviewed against 1.18 from scyld.com
-		v1.18a 17Nov2001 Jeff Garzik <jgarzik@pobox.com>
-			- ethtool support
-		v1.18b 1Mar2002 Zwane Mwaikambo <zwane@commfireservices.com>
-			- Power Management support
-		v1.18c 1Mar2002 David Ruggiero <jdr@farfalle.com>
-			- Full duplex support
-		v1.19  16Oct2002 Zwane Mwaikambo <zwane@linuxpower.ca>
-			- Additional ethtool features
-		v1.19a 28Oct2002 Davud Ruggiero <jdr@farfalle.com>
-			- Increase *read_eeprom udelay to workaround oops with 2 cards.
-		v1.19b 08Nov2002 Marc Zyngier <maz@wild-wind.fr.eu.org>
-			- Introduce driver model for EISA cards.
-		v1.20  04Feb2008 Ondrej Zary <linux@rainbow-software.org>
-			- convert to isa_driver and pnp_driver and some cleanups
-*/
-
-#define DRV_NAME	"3c509"
-
-/* A few values that may be tweaked. */
-
-/* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT  (400*HZ/1000)
-
-#include <linux/module.h>
-#include <linux/isa.h>
-#include <linux/pnp.h>
-#include <linux/string.h>
-#include <linux/interrupt.h>
-#include <linux/errno.h>
-#include <linux/in.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/pm.h>
-#include <linux/skbuff.h>
-#include <linux/delay.h>	/* for udelay() */
-#include <linux/spinlock.h>
-#include <linux/ethtool.h>
-#include <linux/device.h>
-#include <linux/eisa.h>
-#include <linux/bitops.h>
-
-#include <linux/uaccess.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-
-#ifdef EL3_DEBUG
-static int el3_debug = EL3_DEBUG;
-#else
-static int el3_debug = 2;
-#endif
-
-/* Used to do a global count of all the cards in the system.  Must be
- * a global variable so that the eisa probe routines can increment
- * it */
-static int el3_cards = 0;
-#define EL3_MAX_CARDS 8
-
-/* To minimize the size of the driver source I only define operating
-   constants if they are used several times.  You'll need the manual
-   anyway if you want to understand driver details. */
-/* Offsets from base I/O address. */
-#define EL3_DATA 0x00
-#define EL3_CMD 0x0e
-#define EL3_STATUS 0x0e
-#define	EEPROM_READ 0x80
-
-#define EL3_IO_EXTENT	16
-
-#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
-
-
-/* The top five bits written to EL3_CMD are a command, the lower
-   11 bits are the parameter, if applicable. */
-enum c509cmd {
-	TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11,
-	RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11,
-	TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
-	FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11,
-	SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
-	SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11,
-	StatsDisable = 22<<11, StopCoax = 23<<11, PowerUp = 27<<11,
-	PowerDown = 28<<11, PowerAuto = 29<<11};
-
-enum c509status {
-	IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
-	TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
-	IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000, };
-
-/* The SetRxFilter command accepts the following classes: */
-enum RxFilter {
-	RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 };
-
-/* Register window 1 offsets, the window used in normal operation. */
-#define TX_FIFO		0x00
-#define RX_FIFO		0x00
-#define RX_STATUS 	0x08
-#define TX_STATUS 	0x0B
-#define TX_FREE		0x0C		/* Remaining free bytes in Tx buffer. */
-
-#define WN0_CONF_CTRL	0x04		/* Window 0: Configuration control register */
-#define WN0_ADDR_CONF	0x06		/* Window 0: Address configuration register */
-#define WN0_IRQ		0x08		/* Window 0: Set IRQ line in bits 12-15. */
-#define WN4_MEDIA	0x0A		/* Window 4: Various transcvr/media bits. */
-#define	MEDIA_TP	0x00C0		/* Enable link beat and jabber for 10baseT. */
-#define WN4_NETDIAG	0x06		/* Window 4: Net diagnostic */
-#define FD_ENABLE	0x8000		/* Enable full-duplex ("external loopback") */
-
-/*
- * Must be a power of two (we use a binary and in the
- * circular queue)
- */
-#define SKB_QUEUE_SIZE	64
-
-enum el3_cardtype { EL3_ISA, EL3_PNP, EL3_EISA };
-
-struct el3_private {
-	spinlock_t lock;
-	/* skb send-queue */
-	int head, size;
-	struct sk_buff *queue[SKB_QUEUE_SIZE];
-	enum el3_cardtype type;
-};
-static int id_port;
-static int current_tag;
-static struct net_device *el3_devs[EL3_MAX_CARDS];
-
-/* Parameters that may be passed into the module. */
-static int debug = -1;
-static int irq[] = {-1, -1, -1, -1, -1, -1, -1, -1};
-/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-static int max_interrupt_work = 10;
-#ifdef CONFIG_PNP
-static int nopnp;
-#endif
-
-static int el3_common_init(struct net_device *dev);
-static void el3_common_remove(struct net_device *dev);
-static ushort id_read_eeprom(int index);
-static ushort read_eeprom(int ioaddr, int index);
-static int el3_open(struct net_device *dev);
-static netdev_tx_t el3_start_xmit(struct sk_buff *skb, struct net_device *dev);
-static irqreturn_t el3_interrupt(int irq, void *dev_id);
-static void update_stats(struct net_device *dev);
-static struct net_device_stats *el3_get_stats(struct net_device *dev);
-static int el3_rx(struct net_device *dev);
-static int el3_close(struct net_device *dev);
-static void set_multicast_list(struct net_device *dev);
-static void el3_tx_timeout (struct net_device *dev, unsigned int txqueue);
-static void el3_down(struct net_device *dev);
-static void el3_up(struct net_device *dev);
-static const struct ethtool_ops ethtool_ops;
-#ifdef CONFIG_PM
-static int el3_suspend(struct device *, pm_message_t);
-static int el3_resume(struct device *);
-#else
-#define el3_suspend NULL
-#define el3_resume NULL
-#endif
-
-
-/* generic device remove for all device types */
-static int el3_device_remove (struct device *device);
-#ifdef CONFIG_NET_POLL_CONTROLLER
-static void el3_poll_controller(struct net_device *dev);
-#endif
-
-/* Return 0 on success, 1 on error, 2 when found already detected PnP card */
-static int el3_isa_id_sequence(__be16 *phys_addr)
-{
-	short lrs_state = 0xff;
-	int i;
-
-	/* ISA boards are detected by sending the ID sequence to the
-	   ID_PORT.  We find cards past the first by setting the 'current_tag'
-	   on cards as they are found.  Cards with their tag set will not
-	   respond to subsequent ID sequences. */
-
-	outb(0x00, id_port);
-	outb(0x00, id_port);
-	for (i = 0; i < 255; i++) {
-		outb(lrs_state, id_port);
-		lrs_state <<= 1;
-		lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state;
-	}
-	/* For the first probe, clear all board's tag registers. */
-	if (current_tag == 0)
-		outb(0xd0, id_port);
-	else			/* Otherwise kill off already-found boards. */
-		outb(0xd8, id_port);
-	if (id_read_eeprom(7) != 0x6d50)
-		return 1;
-	/* Read in EEPROM data, which does contention-select.
-	   Only the lowest address board will stay "on-line".
-	   3Com got the byte order backwards. */
-	for (i = 0; i < 3; i++)
-		phys_addr[i] = htons(id_read_eeprom(i));
-#ifdef CONFIG_PNP
-	if (!nopnp) {
-		/* The ISA PnP 3c509 cards respond to the ID sequence too.
-		   This check is needed in order not to register them twice. */
-		for (i = 0; i < el3_cards; i++) {
-			struct el3_private *lp = netdev_priv(el3_devs[i]);
-			if (lp->type == EL3_PNP &&
-			    ether_addr_equal((u8 *)phys_addr, el3_devs[i]->dev_addr)) {
-				if (el3_debug > 3)
-					pr_debug("3c509 with address %02x %02x %02x %02x %02x %02x was found by ISAPnP\n",
-						phys_addr[0] & 0xff, phys_addr[0] >> 8,
-						phys_addr[1] & 0xff, phys_addr[1] >> 8,
-						phys_addr[2] & 0xff, phys_addr[2] >> 8);
-				/* Set the adaptor tag so that the next card can be found. */
-				outb(0xd0 + ++current_tag, id_port);
-				return 2;
-			}
-		}
-	}
-#endif /* CONFIG_PNP */
-	return 0;
-
-}
-
-static void el3_dev_fill(struct net_device *dev, __be16 *phys_addr, int ioaddr,
-			 int irq, int if_port, enum el3_cardtype type)
-{
-	struct el3_private *lp = netdev_priv(dev);
-
-	eth_hw_addr_set(dev, (u8 *)phys_addr);
-	dev->base_addr = ioaddr;
-	dev->irq = irq;
-	dev->if_port = if_port;
-	lp->type = type;
-}
-
-static int el3_isa_match(struct device *pdev, unsigned int ndev)
-{
-	struct net_device *dev;
-	int ioaddr, isa_irq, if_port, err;
-	unsigned int iobase;
-	__be16 phys_addr[3];
-
-	while ((err = el3_isa_id_sequence(phys_addr)) == 2)
-		;	/* Skip to next card when PnP card found */
-	if (err == 1)
-		return 0;
-
-	iobase = id_read_eeprom(8);
-	if_port = iobase >> 14;
-	ioaddr = 0x200 + ((iobase & 0x1f) << 4);
-	if (irq[el3_cards] > 1 && irq[el3_cards] < 16)
-		isa_irq = irq[el3_cards];
-	else
-		isa_irq = id_read_eeprom(9) >> 12;
-
-	dev = alloc_etherdev(sizeof(struct el3_private));
-	if (!dev)
-		return -ENOMEM;
-
-	SET_NETDEV_DEV(dev, pdev);
-
-	if (!request_region(ioaddr, EL3_IO_EXTENT, "3c509-isa")) {
-		free_netdev(dev);
-		return 0;
-	}
-
-	/* Set the adaptor tag so that the next card can be found. */
-	outb(0xd0 + ++current_tag, id_port);
-
-	/* Activate the adaptor at the EEPROM location. */
-	outb((ioaddr >> 4) | 0xe0, id_port);
-
-	EL3WINDOW(0);
-	if (inw(ioaddr) != 0x6d50) {
-		free_netdev(dev);
-		return 0;
-	}
-
-	/* Free the interrupt so that some other card can use it. */
-	outw(0x0f00, ioaddr + WN0_IRQ);
-
-	el3_dev_fill(dev, phys_addr, ioaddr, isa_irq, if_port, EL3_ISA);
-	dev_set_drvdata(pdev, dev);
-	if (el3_common_init(dev)) {
-		free_netdev(dev);
-		return 0;
-	}
-
-	el3_devs[el3_cards++] = dev;
-	return 1;
-}
-
-static void el3_isa_remove(struct device *pdev,
-				    unsigned int ndev)
-{
-	el3_device_remove(pdev);
-	dev_set_drvdata(pdev, NULL);
-}
-
-#ifdef CONFIG_PM
-static int el3_isa_suspend(struct device *dev, unsigned int n,
-			   pm_message_t state)
-{
-	current_tag = 0;
-	return el3_suspend(dev, state);
-}
-
-static int el3_isa_resume(struct device *dev, unsigned int n)
-{
-	struct net_device *ndev = dev_get_drvdata(dev);
-	int ioaddr = ndev->base_addr, err;
-	__be16 phys_addr[3];
-
-	while ((err = el3_isa_id_sequence(phys_addr)) == 2)
-		;	/* Skip to next card when PnP card found */
-	if (err == 1)
-		return 0;
-	/* Set the adaptor tag so that the next card can be found. */
-	outb(0xd0 + ++current_tag, id_port);
-	/* Enable the card */
-	outb((ioaddr >> 4) | 0xe0, id_port);
-	EL3WINDOW(0);
-	if (inw(ioaddr) != 0x6d50)
-		return 1;
-	/* Free the interrupt so that some other card can use it. */
-	outw(0x0f00, ioaddr + WN0_IRQ);
-	return el3_resume(dev);
-}
-#endif
-
-static struct isa_driver el3_isa_driver = {
-	.match		= el3_isa_match,
-	.remove		= el3_isa_remove,
-#ifdef CONFIG_PM
-	.suspend	= el3_isa_suspend,
-	.resume		= el3_isa_resume,
-#endif
-	.driver		= {
-		.name	= "3c509"
-	},
-};
-static int isa_registered;
-
-#ifdef CONFIG_PNP
-static const struct pnp_device_id el3_pnp_ids[] = {
-	{ .id = "TCM5090" }, /* 3Com Etherlink III (TP) */
-	{ .id = "TCM5091" }, /* 3Com Etherlink III */
-	{ .id = "TCM5094" }, /* 3Com Etherlink III (combo) */
-	{ .id = "TCM5095" }, /* 3Com Etherlink III (TPO) */
-	{ .id = "TCM5098" }, /* 3Com Etherlink III (TPC) */
-	{ .id = "PNP80f7" }, /* 3Com Etherlink III compatible */
-	{ .id = "PNP80f8" }, /* 3Com Etherlink III compatible */
-	{ .id = "" }
-};
-MODULE_DEVICE_TABLE(pnp, el3_pnp_ids);
-
-static int el3_pnp_probe(struct pnp_dev *pdev, const struct pnp_device_id *id)
-{
-	short i;
-	int ioaddr, irq, if_port;
-	__be16 phys_addr[3];
-	struct net_device *dev = NULL;
-	int err;
-
-	ioaddr = pnp_port_start(pdev, 0);
-	if (!request_region(ioaddr, EL3_IO_EXTENT, "3c509-pnp"))
-		return -EBUSY;
-	irq = pnp_irq(pdev, 0);
-	EL3WINDOW(0);
-	for (i = 0; i < 3; i++)
-		phys_addr[i] = htons(read_eeprom(ioaddr, i));
-	if_port = read_eeprom(ioaddr, 8) >> 14;
-	dev = alloc_etherdev(sizeof(struct el3_private));
-	if (!dev) {
-		release_region(ioaddr, EL3_IO_EXTENT);
-		return -ENOMEM;
-	}
-	SET_NETDEV_DEV(dev, &pdev->dev);
-
-	el3_dev_fill(dev, phys_addr, ioaddr, irq, if_port, EL3_PNP);
-	pnp_set_drvdata(pdev, dev);
-	err = el3_common_init(dev);
-
-	if (err) {
-		pnp_set_drvdata(pdev, NULL);
-		free_netdev(dev);
-		return err;
-	}
-
-	el3_devs[el3_cards++] = dev;
-	return 0;
-}
-
-static void el3_pnp_remove(struct pnp_dev *pdev)
-{
-	el3_common_remove(pnp_get_drvdata(pdev));
-	pnp_set_drvdata(pdev, NULL);
-}
-
-#ifdef CONFIG_PM
-static int el3_pnp_suspend(struct pnp_dev *pdev, pm_message_t state)
-{
-	return el3_suspend(&pdev->dev, state);
-}
-
-static int el3_pnp_resume(struct pnp_dev *pdev)
-{
-	return el3_resume(&pdev->dev);
-}
-#endif
-
-static struct pnp_driver el3_pnp_driver = {
-	.name		= "3c509",
-	.id_table	= el3_pnp_ids,
-	.probe		= el3_pnp_probe,
-	.remove		= el3_pnp_remove,
-#ifdef CONFIG_PM
-	.suspend	= el3_pnp_suspend,
-	.resume		= el3_pnp_resume,
-#endif
-};
-static int pnp_registered;
-#endif /* CONFIG_PNP */
-
-#ifdef CONFIG_EISA
-static const struct eisa_device_id el3_eisa_ids[] = {
-		{ "TCM5090" },
-		{ "TCM5091" },
-		{ "TCM5092" },
-		{ "TCM5093" },
-		{ "TCM5094" },
-		{ "TCM5095" },
-		{ "TCM5098" },
-		{ "" }
-};
-MODULE_DEVICE_TABLE(eisa, el3_eisa_ids);
-
-static int el3_eisa_probe (struct device *device);
-
-static struct eisa_driver el3_eisa_driver = {
-		.id_table = el3_eisa_ids,
-		.driver   = {
-				.name    = "3c579",
-				.probe   = el3_eisa_probe,
-				.remove  = el3_device_remove,
-				.suspend = el3_suspend,
-				.resume  = el3_resume,
-		}
-};
-static int eisa_registered;
-#endif
-
-static const struct net_device_ops netdev_ops = {
-	.ndo_open 		= el3_open,
-	.ndo_stop	 	= el3_close,
-	.ndo_start_xmit 	= el3_start_xmit,
-	.ndo_get_stats 		= el3_get_stats,
-	.ndo_set_rx_mode	= set_multicast_list,
-	.ndo_tx_timeout 	= el3_tx_timeout,
-	.ndo_set_mac_address 	= eth_mac_addr,
-	.ndo_validate_addr	= eth_validate_addr,
-#ifdef CONFIG_NET_POLL_CONTROLLER
-	.ndo_poll_controller	= el3_poll_controller,
-#endif
-};
-
-static int el3_common_init(struct net_device *dev)
-{
-	struct el3_private *lp = netdev_priv(dev);
-	int err;
-	static const char * const if_names[] = {
-		"10baseT", "AUI", "undefined", "BNC"
-	};
-
-	spin_lock_init(&lp->lock);
-
-	if (dev->mem_start & 0x05) { /* xcvr codes 1/3/4/12 */
-		dev->if_port = (dev->mem_start & 0x0f);
-	} else { /* xcvr codes 0/8 */
-		/* use eeprom value, but save user's full-duplex selection */
-		dev->if_port |= (dev->mem_start & 0x08);
-	}
-
-	/* The EL3-specific entries in the device structure. */
-	dev->netdev_ops = &netdev_ops;
-	dev->watchdog_timeo = TX_TIMEOUT;
-	dev->ethtool_ops = &ethtool_ops;
-
-	err = register_netdev(dev);
-	if (err) {
-		pr_err("Failed to register 3c5x9 at %#3.3lx, IRQ %d.\n",
-			dev->base_addr, dev->irq);
-		release_region(dev->base_addr, EL3_IO_EXTENT);
-		return err;
-	}
-
-	pr_info("%s: 3c5x9 found at %#3.3lx, %s port, address %pM, IRQ %d.\n",
-	       dev->name, dev->base_addr, if_names[(dev->if_port & 0x03)],
-	       dev->dev_addr, dev->irq);
-
-	return 0;
-
-}
-
-static void el3_common_remove (struct net_device *dev)
-{
-	unregister_netdev (dev);
-	release_region(dev->base_addr, EL3_IO_EXTENT);
-	free_netdev (dev);
-}
-
-#ifdef CONFIG_EISA
-static int el3_eisa_probe(struct device *device)
-{
-	short i;
-	int ioaddr, irq, if_port;
-	__be16 phys_addr[3];
-	struct net_device *dev = NULL;
-	struct eisa_device *edev;
-	int err;
-
-	/* Yeepee, The driver framework is calling us ! */
-	edev = to_eisa_device (device);
-	ioaddr = edev->base_addr;
-
-	if (!request_region(ioaddr, EL3_IO_EXTENT, "3c579-eisa"))
-		return -EBUSY;
-
-	/* Change the register set to the configuration window 0. */
-	outw(SelectWindow | 0, ioaddr + 0xC80 + EL3_CMD);
-
-	irq = inw(ioaddr + WN0_IRQ) >> 12;
-	if_port = inw(ioaddr + 6)>>14;
-	for (i = 0; i < 3; i++)
-		phys_addr[i] = htons(read_eeprom(ioaddr, i));
-
-	/* Restore the "Product ID" to the EEPROM read register. */
-	read_eeprom(ioaddr, 3);
-
-	dev = alloc_etherdev(sizeof (struct el3_private));
-	if (dev == NULL) {
-		release_region(ioaddr, EL3_IO_EXTENT);
-		return -ENOMEM;
-	}
-
-	SET_NETDEV_DEV(dev, device);
-
-	el3_dev_fill(dev, phys_addr, ioaddr, irq, if_port, EL3_EISA);
-	eisa_set_drvdata (edev, dev);
-	err = el3_common_init(dev);
-
-	if (err) {
-		eisa_set_drvdata (edev, NULL);
-		free_netdev(dev);
-		return err;
-	}
-
-	el3_devs[el3_cards++] = dev;
-	return 0;
-}
-#endif
-
-/* This remove works for all device types.
- *
- * The net dev must be stored in the driver data field */
-static int el3_device_remove(struct device *device)
-{
-	struct net_device *dev;
-
-	dev = dev_get_drvdata(device);
-
-	el3_common_remove (dev);
-	return 0;
-}
-
-/* Read a word from the EEPROM using the regular EEPROM access register.
-   Assume that we are in register window zero.
- */
-static ushort read_eeprom(int ioaddr, int index)
-{
-	outw(EEPROM_READ + index, ioaddr + 10);
-	/* Pause for at least 162 us. for the read to take place.
-	   Some chips seem to require much longer */
-	mdelay(2);
-	return inw(ioaddr + 12);
-}
-
-/* Read a word from the EEPROM when in the ISA ID probe state. */
-static ushort id_read_eeprom(int index)
-{
-	int bit, word = 0;
-
-	/* Issue read command, and pause for at least 162 us. for it to complete.
-	   Assume extra-fast 16Mhz bus. */
-	outb(EEPROM_READ + index, id_port);
-
-	/* Pause for at least 162 us. for the read to take place. */
-	/* Some chips seem to require much longer */
-	mdelay(4);
-
-	for (bit = 15; bit >= 0; bit--)
-		word = (word << 1) + (inb(id_port) & 0x01);
-
-	if (el3_debug > 3)
-		pr_debug("  3c509 EEPROM word %d %#4.4x.\n", index, word);
-
-	return word;
-}
-
-
-static int
-el3_open(struct net_device *dev)
-{
-	int ioaddr = dev->base_addr;
-	int i;
-
-	outw(TxReset, ioaddr + EL3_CMD);
-	outw(RxReset, ioaddr + EL3_CMD);
-	outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
-
-	i = request_irq(dev->irq, el3_interrupt, 0, dev->name, dev);
-	if (i)
-		return i;
-
-	EL3WINDOW(0);
-	if (el3_debug > 3)
-		pr_debug("%s: Opening, IRQ %d	 status@%x %4.4x.\n", dev->name,
-			   dev->irq, ioaddr + EL3_STATUS, inw(ioaddr + EL3_STATUS));
-
-	el3_up(dev);
-
-	if (el3_debug > 3)
-		pr_debug("%s: Opened 3c509  IRQ %d  status %4.4x.\n",
-			   dev->name, dev->irq, inw(ioaddr + EL3_STATUS));
-
-	return 0;
-}
-
-static void
-el3_tx_timeout (struct net_device *dev, unsigned int txqueue)
-{
-	int ioaddr = dev->base_addr;
-
-	/* Transmitter timeout, serious problems. */
-	pr_warn("%s: transmit timed out, Tx_status %2.2x status %4.4x Tx FIFO room %d\n",
-		dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS),
-		inw(ioaddr + TX_FREE));
-	dev->stats.tx_errors++;
-	netif_trans_update(dev); /* prevent tx timeout */
-	/* Issue TX_RESET and TX_START commands. */
-	outw(TxReset, ioaddr + EL3_CMD);
-	outw(TxEnable, ioaddr + EL3_CMD);
-	netif_wake_queue(dev);
-}
-
-
-static netdev_tx_t
-el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
-{
-	struct el3_private *lp = netdev_priv(dev);
-	int ioaddr = dev->base_addr;
-	unsigned long flags;
-
-	netif_stop_queue (dev);
-
-	dev->stats.tx_bytes += skb->len;
-
-	if (el3_debug > 4) {
-		pr_debug("%s: el3_start_xmit(length = %u) called, status %4.4x.\n",
-			   dev->name, skb->len, inw(ioaddr + EL3_STATUS));
-	}
-	/*
-	 *	We lock the driver against other processors. Note
-	 *	we don't need to lock versus the IRQ as we suspended
-	 *	that. This means that we lose the ability to take
-	 *	an RX during a TX upload. That sucks a bit with SMP
-	 *	on an original 3c509 (2K buffer)
-	 *
-	 *	Using disable_irq stops us crapping on other
-	 *	time sensitive devices.
-	 */
-
-	spin_lock_irqsave(&lp->lock, flags);
-
-	/* Put out the doubleword header... */
-	outw(skb->len, ioaddr + TX_FIFO);
-	outw(0x00, ioaddr + TX_FIFO);
-	/* ... and the packet rounded to a doubleword. */
-	outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
-
-	if (inw(ioaddr + TX_FREE) > 1536)
-		netif_start_queue(dev);
-	else
-		/* Interrupt us when the FIFO has room for max-sized packet. */
-		outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
-
-	spin_unlock_irqrestore(&lp->lock, flags);
-
-	dev_consume_skb_any (skb);
-
-	/* Clear the Tx status stack. */
-	{
-		short tx_status;
-		int i = 4;
-
-		while (--i > 0	&&	(tx_status = inb(ioaddr + TX_STATUS)) > 0) {
-			if (tx_status & 0x38) dev->stats.tx_aborted_errors++;
-			if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD);
-			if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD);
-			outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
-		}
-	}
-	return NETDEV_TX_OK;
-}
-
-/* The EL3 interrupt handler. */
-static irqreturn_t
-el3_interrupt(int irq, void *dev_id)
-{
-	struct net_device *dev = dev_id;
-	struct el3_private *lp;
-	int ioaddr, status;
-	int i = max_interrupt_work;
-
-	lp = netdev_priv(dev);
-	spin_lock(&lp->lock);
-
-	ioaddr = dev->base_addr;
-
-	if (el3_debug > 4) {
-		status = inw(ioaddr + EL3_STATUS);
-		pr_debug("%s: interrupt, status %4.4x.\n", dev->name, status);
-	}
-
-	while ((status = inw(ioaddr + EL3_STATUS)) &
-		   (IntLatch | RxComplete | StatsFull)) {
-
-		if (status & RxComplete)
-			el3_rx(dev);
-
-		if (status & TxAvailable) {
-			if (el3_debug > 5)
-				pr_debug("	TX room bit was handled.\n");
-			/* There's room in the FIFO for a full-sized packet. */
-			outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
-			netif_wake_queue (dev);
-		}
-		if (status & (AdapterFailure | RxEarly | StatsFull | TxComplete)) {
-			/* Handle all uncommon interrupts. */
-			if (status & StatsFull)				/* Empty statistics. */
-				update_stats(dev);
-			if (status & RxEarly) {				/* Rx early is unused. */
-				el3_rx(dev);
-				outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
-			}
-			if (status & TxComplete) {			/* Really Tx error. */
-				short tx_status;
-				int i = 4;
-
-				while (--i>0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) {
-					if (tx_status & 0x38) dev->stats.tx_aborted_errors++;
-					if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD);
-					if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD);
-					outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
-				}
-			}
-			if (status & AdapterFailure) {
-				/* Adapter failure requires Rx reset and reinit. */
-				outw(RxReset, ioaddr + EL3_CMD);
-				/* Set the Rx filter to the current state. */
-				outw(SetRxFilter | RxStation | RxBroadcast
-					 | (dev->flags & IFF_ALLMULTI ? RxMulticast : 0)
-					 | (dev->flags & IFF_PROMISC ? RxProm : 0),
-					 ioaddr + EL3_CMD);
-				outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */
-				outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
-			}
-		}
-
-		if (--i < 0) {
-			pr_err("%s: Infinite loop in interrupt, status %4.4x.\n",
-				   dev->name, status);
-			/* Clear all interrupts. */
-			outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
-			break;
-		}
-		/* Acknowledge the IRQ. */
-		outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); /* Ack IRQ */
-	}
-
-	if (el3_debug > 4) {
-		pr_debug("%s: exiting interrupt, status %4.4x.\n", dev->name,
-			   inw(ioaddr + EL3_STATUS));
-	}
-	spin_unlock(&lp->lock);
-	return IRQ_HANDLED;
-}
-
-
-#ifdef CONFIG_NET_POLL_CONTROLLER
-/*
- * Polling receive - used by netconsole and other diagnostic tools
- * to allow network i/o with interrupts disabled.
- */
-static void el3_poll_controller(struct net_device *dev)
-{
-	disable_irq(dev->irq);
-	el3_interrupt(dev->irq, dev);
-	enable_irq(dev->irq);
-}
-#endif
-
-static struct net_device_stats *
-el3_get_stats(struct net_device *dev)
-{
-	struct el3_private *lp = netdev_priv(dev);
-	unsigned long flags;
-
-	/*
-	 *	This is fast enough not to bother with disable IRQ
-	 *	stuff.
-	 */
-
-	spin_lock_irqsave(&lp->lock, flags);
-	update_stats(dev);
-	spin_unlock_irqrestore(&lp->lock, flags);
-	return &dev->stats;
-}
-
-/*  Update statistics.  We change to register window 6, so this should be run
-	single-threaded if the device is active. This is expected to be a rare
-	operation, and it's simpler for the rest of the driver to assume that
-	window 1 is always valid rather than use a special window-state variable.
-	*/
-static void update_stats(struct net_device *dev)
-{
-	int ioaddr = dev->base_addr;
-
-	if (el3_debug > 5)
-		pr_debug("   Updating the statistics.\n");
-	/* Turn off statistics updates while reading. */
-	outw(StatsDisable, ioaddr + EL3_CMD);
-	/* Switch to the stats window, and read everything. */
-	EL3WINDOW(6);
-	dev->stats.tx_carrier_errors 	+= inb(ioaddr + 0);
-	dev->stats.tx_heartbeat_errors	+= inb(ioaddr + 1);
-	/* Multiple collisions. */	   inb(ioaddr + 2);
-	dev->stats.collisions		+= inb(ioaddr + 3);
-	dev->stats.tx_window_errors	+= inb(ioaddr + 4);
-	dev->stats.rx_fifo_errors	+= inb(ioaddr + 5);
-	dev->stats.tx_packets		+= inb(ioaddr + 6);
-	/* Rx packets	*/		   inb(ioaddr + 7);
-	/* Tx deferrals */		   inb(ioaddr + 8);
-	inw(ioaddr + 10);	/* Total Rx and Tx octets. */
-	inw(ioaddr + 12);
-
-	/* Back to window 1, and turn statistics back on. */
-	EL3WINDOW(1);
-	outw(StatsEnable, ioaddr + EL3_CMD);
-}
-
-static int
-el3_rx(struct net_device *dev)
-{
-	int ioaddr = dev->base_addr;
-	short rx_status;
-
-	if (el3_debug > 5)
-		pr_debug("   In rx_packet(), status %4.4x, rx_status %4.4x.\n",
-			   inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS));
-	while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) {
-		if (rx_status & 0x4000) { /* Error, update stats. */
-			short error = rx_status & 0x3800;
-
-			outw(RxDiscard, ioaddr + EL3_CMD);
-			dev->stats.rx_errors++;
-			switch (error) {
-			case 0x0000:		dev->stats.rx_over_errors++; break;
-			case 0x0800:		dev->stats.rx_length_errors++; break;
-			case 0x1000:		dev->stats.rx_frame_errors++; break;
-			case 0x1800:		dev->stats.rx_length_errors++; break;
-			case 0x2000:		dev->stats.rx_frame_errors++; break;
-			case 0x2800:		dev->stats.rx_crc_errors++; break;
-			}
-		} else {
-			short pkt_len = rx_status & 0x7ff;
-			struct sk_buff *skb;
-
-			skb = netdev_alloc_skb(dev, pkt_len + 5);
-			if (el3_debug > 4)
-				pr_debug("Receiving packet size %d status %4.4x.\n",
-					   pkt_len, rx_status);
-			if (skb != NULL) {
-				skb_reserve(skb, 2);     /* Align IP on 16 byte */
-
-				/* 'skb->data' points to the start of sk_buff data area. */
-				insl(ioaddr + RX_FIFO, skb_put(skb,pkt_len),
-					 (pkt_len + 3) >> 2);
-
-				outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
-				skb->protocol = eth_type_trans(skb,dev);
-				netif_rx(skb);
-				dev->stats.rx_bytes += pkt_len;
-				dev->stats.rx_packets++;
-				continue;
-			}
-			outw(RxDiscard, ioaddr + EL3_CMD);
-			dev->stats.rx_dropped++;
-			if (el3_debug)
-				pr_debug("%s: Couldn't allocate a sk_buff of size %d.\n",
-					   dev->name, pkt_len);
-		}
-		inw(ioaddr + EL3_STATUS); 				/* Delay. */
-		while (inw(ioaddr + EL3_STATUS) & 0x1000)
-			pr_debug("	Waiting for 3c509 to discard packet, status %x.\n",
-				   inw(ioaddr + EL3_STATUS) );
-	}
-
-	return 0;
-}
-
-/*
- *     Set or clear the multicast filter for this adaptor.
- */
-static void
-set_multicast_list(struct net_device *dev)
-{
-	unsigned long flags;
-	struct el3_private *lp = netdev_priv(dev);
-	int ioaddr = dev->base_addr;
-	int mc_count = netdev_mc_count(dev);
-
-	if (el3_debug > 1) {
-		static int old;
-		if (old != mc_count) {
-			old = mc_count;
-			pr_debug("%s: Setting Rx mode to %d addresses.\n",
-				 dev->name, mc_count);
-		}
-	}
-	spin_lock_irqsave(&lp->lock, flags);
-	if (dev->flags&IFF_PROMISC) {
-		outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
-			 ioaddr + EL3_CMD);
-	}
-	else if (mc_count || (dev->flags&IFF_ALLMULTI)) {
-		outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast, ioaddr + EL3_CMD);
-	}
-	else
-		outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
-	spin_unlock_irqrestore(&lp->lock, flags);
-}
-
-static int
-el3_close(struct net_device *dev)
-{
-	int ioaddr = dev->base_addr;
-	struct el3_private *lp = netdev_priv(dev);
-
-	if (el3_debug > 2)
-		pr_debug("%s: Shutting down ethercard.\n", dev->name);
-
-	el3_down(dev);
-
-	free_irq(dev->irq, dev);
-	/* Switching back to window 0 disables the IRQ. */
-	EL3WINDOW(0);
-	if (lp->type != EL3_EISA) {
-		/* But we explicitly zero the IRQ line select anyway. Don't do
-		 * it on EISA cards, it prevents the module from getting an
-		 * IRQ after unload+reload... */
-		outw(0x0f00, ioaddr + WN0_IRQ);
-	}
-
-	return 0;
-}
-
-static int
-el3_link_ok(struct net_device *dev)
-{
-	int ioaddr = dev->base_addr;
-	u16 tmp;
-
-	EL3WINDOW(4);
-	tmp = inw(ioaddr + WN4_MEDIA);
-	EL3WINDOW(1);
-	return tmp & (1<<11);
-}
-
-static void
-el3_netdev_get_ecmd(struct net_device *dev, struct ethtool_link_ksettings *cmd)
-{
-	u16 tmp;
-	int ioaddr = dev->base_addr;
-	u32 supported;
-
-	EL3WINDOW(0);
-	/* obtain current transceiver via WN4_MEDIA? */
-	tmp = inw(ioaddr + WN0_ADDR_CONF);
-	switch (tmp >> 14) {
-	case 0:
-		cmd->base.port = PORT_TP;
-		break;
-	case 1:
-		cmd->base.port = PORT_AUI;
-		break;
-	case 3:
-		cmd->base.port = PORT_BNC;
-		break;
-	default:
-		break;
-	}
-
-	cmd->base.duplex = DUPLEX_HALF;
-	supported = 0;
-	tmp = inw(ioaddr + WN0_CONF_CTRL);
-	if (tmp & (1<<13))
-		supported |= SUPPORTED_AUI;
-	if (tmp & (1<<12))
-		supported |= SUPPORTED_BNC;
-	if (tmp & (1<<9)) {
-		supported |= SUPPORTED_TP | SUPPORTED_10baseT_Half |
-				SUPPORTED_10baseT_Full;	/* hmm... */
-		EL3WINDOW(4);
-		tmp = inw(ioaddr + WN4_NETDIAG);
-		if (tmp & FD_ENABLE)
-			cmd->base.duplex = DUPLEX_FULL;
-	}
-
-	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
-						supported);
-	cmd->base.speed = SPEED_10;
-	EL3WINDOW(1);
-}
-
-static int
-el3_netdev_set_ecmd(struct net_device *dev,
-		    const struct ethtool_link_ksettings *cmd)
-{
-	u16 tmp;
-	int ioaddr = dev->base_addr;
-
-	if (cmd->base.speed != SPEED_10)
-		return -EINVAL;
-	if ((cmd->base.duplex != DUPLEX_HALF) &&
-	    (cmd->base.duplex != DUPLEX_FULL))
-		return -EINVAL;
-
-	/* change XCVR type */
-	EL3WINDOW(0);
-	tmp = inw(ioaddr + WN0_ADDR_CONF);
-	switch (cmd->base.port) {
-	case PORT_TP:
-		tmp &= ~(3<<14);
-		dev->if_port = 0;
-		break;
-	case PORT_AUI:
-		tmp |= (1<<14);
-		dev->if_port = 1;
-		break;
-	case PORT_BNC:
-		tmp |= (3<<14);
-		dev->if_port = 3;
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	outw(tmp, ioaddr + WN0_ADDR_CONF);
-	if (dev->if_port == 3) {
-		/* fire up the DC-DC convertor if BNC gets enabled */
-		tmp = inw(ioaddr + WN0_ADDR_CONF);
-		if (tmp & (3 << 14)) {
-			outw(StartCoax, ioaddr + EL3_CMD);
-			udelay(800);
-		} else
-			return -EIO;
-	}
-
-	EL3WINDOW(4);
-	tmp = inw(ioaddr + WN4_NETDIAG);
-	if (cmd->base.duplex == DUPLEX_FULL)
-		tmp |= FD_ENABLE;
-	else
-		tmp &= ~FD_ENABLE;
-	outw(tmp, ioaddr + WN4_NETDIAG);
-	EL3WINDOW(1);
-
-	return 0;
-}
-
-static void el3_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
-{
-	strscpy(info->driver, DRV_NAME, sizeof(info->driver));
-}
-
-static int el3_get_link_ksettings(struct net_device *dev,
-				  struct ethtool_link_ksettings *cmd)
-{
-	struct el3_private *lp = netdev_priv(dev);
-
-	spin_lock_irq(&lp->lock);
-	el3_netdev_get_ecmd(dev, cmd);
-	spin_unlock_irq(&lp->lock);
-	return 0;
-}
-
-static int el3_set_link_ksettings(struct net_device *dev,
-				  const struct ethtool_link_ksettings *cmd)
-{
-	struct el3_private *lp = netdev_priv(dev);
-	int ret;
-
-	spin_lock_irq(&lp->lock);
-	ret = el3_netdev_set_ecmd(dev, cmd);
-	spin_unlock_irq(&lp->lock);
-	return ret;
-}
-
-static u32 el3_get_link(struct net_device *dev)
-{
-	struct el3_private *lp = netdev_priv(dev);
-	u32 ret;
-
-	spin_lock_irq(&lp->lock);
-	ret = el3_link_ok(dev);
-	spin_unlock_irq(&lp->lock);
-	return ret;
-}
-
-static u32 el3_get_msglevel(struct net_device *dev)
-{
-	return el3_debug;
-}
-
-static void el3_set_msglevel(struct net_device *dev, u32 v)
-{
-	el3_debug = v;
-}
-
-static const struct ethtool_ops ethtool_ops = {
-	.get_drvinfo = el3_get_drvinfo,
-	.get_link = el3_get_link,
-	.get_msglevel = el3_get_msglevel,
-	.set_msglevel = el3_set_msglevel,
-	.get_link_ksettings = el3_get_link_ksettings,
-	.set_link_ksettings = el3_set_link_ksettings,
-};
-
-static void
-el3_down(struct net_device *dev)
-{
-	int ioaddr = dev->base_addr;
-
-	netif_stop_queue(dev);
-
-	/* Turn off statistics ASAP.  We update lp->stats below. */
-	outw(StatsDisable, ioaddr + EL3_CMD);
-
-	/* Disable the receiver and transmitter. */
-	outw(RxDisable, ioaddr + EL3_CMD);
-	outw(TxDisable, ioaddr + EL3_CMD);
-
-	if (dev->if_port == 3)
-		/* Turn off thinnet power.  Green! */
-		outw(StopCoax, ioaddr + EL3_CMD);
-	else if (dev->if_port == 0) {
-		/* Disable link beat and jabber, if_port may change here next open(). */
-		EL3WINDOW(4);
-		outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA);
-	}
-
-	outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD);
-
-	update_stats(dev);
-}
-
-static void
-el3_up(struct net_device *dev)
-{
-	int i, sw_info, net_diag;
-	int ioaddr = dev->base_addr;
-
-	/* Activating the board required and does no harm otherwise */
-	outw(0x0001, ioaddr + 4);
-
-	/* Set the IRQ line. */
-	outw((dev->irq << 12) | 0x0f00, ioaddr + WN0_IRQ);
-
-	/* Set the station address in window 2 each time opened. */
-	EL3WINDOW(2);
-
-	for (i = 0; i < 6; i++)
-		outb(dev->dev_addr[i], ioaddr + i);
-
-	if ((dev->if_port & 0x03) == 3) /* BNC interface */
-		/* Start the thinnet transceiver. We should really wait 50ms...*/
-		outw(StartCoax, ioaddr + EL3_CMD);
-	else if ((dev->if_port & 0x03) == 0) { /* 10baseT interface */
-		/* Combine secondary sw_info word (the adapter level) and primary
-			sw_info word (duplex setting plus other useless bits) */
-		EL3WINDOW(0);
-		sw_info = (read_eeprom(ioaddr, 0x14) & 0x400f) |
-			(read_eeprom(ioaddr, 0x0d) & 0xBff0);
-
-		EL3WINDOW(4);
-		net_diag = inw(ioaddr + WN4_NETDIAG);
-		net_diag = (net_diag | FD_ENABLE); /* temporarily assume full-duplex will be set */
-		pr_info("%s: ", dev->name);
-		switch (dev->if_port & 0x0c) {
-			case 12:
-				/* force full-duplex mode if 3c5x9b */
-				if (sw_info & 0x000f) {
-					pr_cont("Forcing 3c5x9b full-duplex mode");
-					break;
-				}
-				fallthrough;
-			case 8:
-				/* set full-duplex mode based on eeprom config setting */
-				if ((sw_info & 0x000f) && (sw_info & 0x8000)) {
-					pr_cont("Setting 3c5x9b full-duplex mode (from EEPROM configuration bit)");
-					break;
-				}
-				fallthrough;
-			default:
-				/* xcvr=(0 || 4) OR user has an old 3c5x9 non "B" model */
-				pr_cont("Setting 3c5x9/3c5x9B half-duplex mode");
-				net_diag = (net_diag & ~FD_ENABLE); /* disable full duplex */
-		}
-
-		outw(net_diag, ioaddr + WN4_NETDIAG);
-		pr_cont(" if_port: %d, sw_info: %4.4x\n", dev->if_port, sw_info);
-		if (el3_debug > 3)
-			pr_debug("%s: 3c5x9 net diag word is now: %4.4x.\n", dev->name, net_diag);
-		/* Enable link beat and jabber check. */
-		outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA);
-	}
-
-	/* Switch to the stats window, and clear all stats by reading. */
-	outw(StatsDisable, ioaddr + EL3_CMD);
-	EL3WINDOW(6);
-	for (i = 0; i < 9; i++)
-		inb(ioaddr + i);
-	inw(ioaddr + 10);
-	inw(ioaddr + 12);
-
-	/* Switch to register set 1 for normal use. */
-	EL3WINDOW(1);
-
-	/* Accept b-case and phys addr only. */
-	outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
-	outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
-
-	outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
-	outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
-	/* Allow status bits to be seen. */
-	outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
-	/* Ack all pending events, and set active indicator mask. */
-	outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
-		 ioaddr + EL3_CMD);
-	outw(SetIntrEnb | IntLatch|TxAvailable|TxComplete|RxComplete|StatsFull,
-		 ioaddr + EL3_CMD);
-
-	netif_start_queue(dev);
-}
-
-/* Power Management support functions */
-#ifdef CONFIG_PM
-
-static int
-el3_suspend(struct device *pdev, pm_message_t state)
-{
-	unsigned long flags;
-	struct net_device *dev;
-	struct el3_private *lp;
-	int ioaddr;
-
-	dev = dev_get_drvdata(pdev);
-	lp = netdev_priv(dev);
-	ioaddr = dev->base_addr;
-
-	spin_lock_irqsave(&lp->lock, flags);
-
-	if (netif_running(dev))
-		netif_device_detach(dev);
-
-	el3_down(dev);
-	outw(PowerDown, ioaddr + EL3_CMD);
-
-	spin_unlock_irqrestore(&lp->lock, flags);
-	return 0;
-}
-
-static int
-el3_resume(struct device *pdev)
-{
-	unsigned long flags;
-	struct net_device *dev;
-	struct el3_private *lp;
-	int ioaddr;
-
-	dev = dev_get_drvdata(pdev);
-	lp = netdev_priv(dev);
-	ioaddr = dev->base_addr;
-
-	spin_lock_irqsave(&lp->lock, flags);
-
-	outw(PowerUp, ioaddr + EL3_CMD);
-	EL3WINDOW(0);
-	el3_up(dev);
-
-	if (netif_running(dev))
-		netif_device_attach(dev);
-
-	spin_unlock_irqrestore(&lp->lock, flags);
-	return 0;
-}
-
-#endif /* CONFIG_PM */
-
-module_param(debug,int, 0);
-module_param_hw_array(irq, int, irq, NULL, 0);
-module_param(max_interrupt_work, int, 0);
-MODULE_PARM_DESC(debug, "debug level (0-6)");
-MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
-MODULE_PARM_DESC(max_interrupt_work, "maximum events handled per interrupt");
-#ifdef CONFIG_PNP
-module_param(nopnp, int, 0);
-MODULE_PARM_DESC(nopnp, "disable ISA PnP support (0-1)");
-#endif	/* CONFIG_PNP */
-MODULE_DESCRIPTION("3Com Etherlink III (3c509, 3c509B, 3c529, 3c579) ethernet driver");
-MODULE_LICENSE("GPL");
-
-static int __init el3_init_module(void)
-{
-	int ret = 0;
-
-	if (debug >= 0)
-		el3_debug = debug;
-
-#ifdef CONFIG_PNP
-	if (!nopnp) {
-		ret = pnp_register_driver(&el3_pnp_driver);
-		if (!ret)
-			pnp_registered = 1;
-	}
-#endif
-	/* Select an open I/O location at 0x1*0 to do ISA contention select. */
-	/* Start with 0x110 to avoid some sound cards.*/
-	for (id_port = 0x110 ; id_port < 0x200; id_port += 0x10) {
-		if (!request_region(id_port, 1, "3c509-control"))
-			continue;
-		outb(0x00, id_port);
-		outb(0xff, id_port);
-		if (inb(id_port) & 0x01)
-			break;
-		else
-			release_region(id_port, 1);
-	}
-	if (id_port >= 0x200) {
-		id_port = 0;
-		pr_err("No I/O port available for 3c509 activation.\n");
-	} else {
-		ret = isa_register_driver(&el3_isa_driver, EL3_MAX_CARDS);
-		if (!ret)
-			isa_registered = 1;
-	}
-#ifdef CONFIG_EISA
-	ret = eisa_driver_register(&el3_eisa_driver);
-	if (!ret)
-		eisa_registered = 1;
-#endif
-
-#ifdef CONFIG_PNP
-	if (pnp_registered)
-		ret = 0;
-#endif
-	if (isa_registered)
-		ret = 0;
-#ifdef CONFIG_EISA
-	if (eisa_registered)
-		ret = 0;
-#endif
-	return ret;
-}
-
-static void __exit el3_cleanup_module(void)
-{
-#ifdef CONFIG_PNP
-	if (pnp_registered)
-		pnp_unregister_driver(&el3_pnp_driver);
-#endif
-	if (isa_registered)
-		isa_unregister_driver(&el3_isa_driver);
-	if (id_port)
-		release_region(id_port, 1);
-#ifdef CONFIG_EISA
-	if (eisa_registered)
-		eisa_driver_unregister(&el3_eisa_driver);
-#endif
-}
-
-module_init (el3_init_module);
-module_exit (el3_cleanup_module);
diff --git a/drivers/net/ethernet/3com/Kconfig b/drivers/net/ethernet/3com/Kconfig
index 1fbab79e2be4..c05a1b63c1c9 100644
--- a/drivers/net/ethernet/3com/Kconfig
+++ b/drivers/net/ethernet/3com/Kconfig
@@ -17,20 +17,6 @@ config NET_VENDOR_3COM
 
 if NET_VENDOR_3COM
 
-config EL3
-	tristate "3c509/3c579 \"EtherLink III\" support"
-	depends on (ISA || EISA)
-	help
-	  If you have a network (Ethernet) card belonging to the 3Com
-	  EtherLinkIII series, say Y here.
-
-	  If your card is not working you may need to use the DOS
-	  setup disk to disable Plug & Play mode, and to select the default
-	  media type.
-
-	  To compile this driver as a module, choose M here. The module
-	  will be called 3c509.
-
 config 3C515
 	tristate "3c515 ISA \"Fast EtherLink\""
 	depends on ISA && ISA_DMA_API && !PPC32
diff --git a/drivers/net/ethernet/3com/Makefile b/drivers/net/ethernet/3com/Makefile
index f8b73babc510..f7623fa2d441 100644
--- a/drivers/net/ethernet/3com/Makefile
+++ b/drivers/net/ethernet/3com/Makefile
@@ -3,7 +3,6 @@
 # Makefile for the 3Com Ethernet device drivers
 #
 
-obj-$(CONFIG_EL3) += 3c509.o
 obj-$(CONFIG_3C515) += 3c515.o
 obj-$(CONFIG_PCMCIA_3C589) += 3c589_cs.o
 obj-$(CONFIG_PCMCIA_3C574) += 3c574_cs.o

-- 
2.53.0


^ permalink raw reply related

* [PATCH net v2 02/15] drivers: net: 3com: 3c515: Remove this driver
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
  Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
	Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
In-Reply-To: <20260422-v7-0-0-net-next-driver-removal-v1-v2-0-08a5b59784d5@lunn.ch>

The 3c515 was written by Donald Becker between 1997-1998. It is an ISA
device, so unlikely to be used with modern kernels.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
 drivers/net/ethernet/3com/3c515.c  | 1566 ------------------------------------
 drivers/net/ethernet/3com/Kconfig  |   11 -
 drivers/net/ethernet/3com/Makefile |    1 -
 3 files changed, 1578 deletions(-)

diff --git a/drivers/net/ethernet/3com/3c515.c b/drivers/net/ethernet/3com/3c515.c
deleted file mode 100644
index 2227c83a4862..000000000000
--- a/drivers/net/ethernet/3com/3c515.c
+++ /dev/null
@@ -1,1566 +0,0 @@
-/*
-	Written 1997-1998 by Donald Becker.
-
-	This software may be used and distributed according to the terms
-	of the GNU General Public License, incorporated herein by reference.
-
-	This driver is for the 3Com ISA EtherLink XL "Corkscrew" 3c515 ethercard.
-
-	The author may be reached as becker@scyld.com, or C/O
-	Scyld Computing Corporation
-	410 Severn Ave., Suite 210
-	Annapolis MD 21403
-
-
-	2000/2/2- Added support for kernel-level ISAPnP
-		by Stephen Frost <sfrost@snowman.net> and Alessandro Zummo
-	Cleaned up for 2.3.x/softnet by Jeff Garzik and Alan Cox.
-
-	2001/11/17 - Added ethtool support (jgarzik)
-
-	2002/10/28 - Locking updates for 2.5 (alan@lxorguk.ukuu.org.uk)
-
-*/
-
-#define DRV_NAME		"3c515"
-
-#define CORKSCREW 1
-
-/* "Knobs" that adjust features and parameters. */
-/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
-   Setting to > 1512 effectively disables this feature. */
-static int rx_copybreak = 200;
-
-/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-static int max_interrupt_work = 20;
-
-/* Enable the automatic media selection code -- usually set. */
-#define AUTOMEDIA 1
-
-/* Allow the use of fragment bus master transfers instead of only
-   programmed-I/O for Vortex cards.  Full-bus-master transfers are always
-   enabled by default on Boomerang cards.  If VORTEX_BUS_MASTER is defined,
-   the feature may be turned on using 'options'. */
-#define VORTEX_BUS_MASTER
-
-/* A few values that may be tweaked. */
-/* Keep the ring sizes a power of two for efficiency. */
-#define TX_RING_SIZE	16
-#define RX_RING_SIZE	16
-#define PKT_BUF_SZ		1536	/* Size of each temporary Rx buffer. */
-
-#include <linux/module.h>
-#include <linux/isapnp.h>
-#include <linux/kernel.h>
-#include <linux/netdevice.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/in.h>
-#include <linux/ioport.h>
-#include <linux/skbuff.h>
-#include <linux/etherdevice.h>
-#include <linux/interrupt.h>
-#include <linux/timer.h>
-#include <linux/ethtool.h>
-#include <linux/bitops.h>
-#include <linux/uaccess.h>
-
-#include <net/Space.h>
-
-#include <asm/io.h>
-#include <asm/dma.h>
-
-#define NEW_MULTICAST
-#include <linux/delay.h>
-
-#define MAX_UNITS 8
-
-MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
-MODULE_DESCRIPTION("3Com 3c515 Corkscrew driver");
-MODULE_LICENSE("GPL");
-
-/* "Knobs" for adjusting internal parameters. */
-/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */
-#define DRIVER_DEBUG 1
-/* Some values here only for performance evaluation and path-coverage
-   debugging. */
-static int rx_nocopy, rx_copy, queued_packet;
-
-/* Number of times to check to see if the Tx FIFO has space, used in some
-   limited cases. */
-#define WAIT_TX_AVAIL 200
-
-/* Operational parameter that usually are not changed. */
-#define TX_TIMEOUT  ((4*HZ)/10)	/* Time in jiffies before concluding Tx hung */
-
-/* The size here is somewhat misleading: the Corkscrew also uses the ISA
-   aliased registers at <base>+0x400.
-   */
-#define CORKSCREW_TOTAL_SIZE 0x20
-
-#ifdef DRIVER_DEBUG
-static int corkscrew_debug = DRIVER_DEBUG;
-#else
-static int corkscrew_debug = 1;
-#endif
-
-#define CORKSCREW_ID 10
-
-/*
-				Theory of Operation
-
-I. Board Compatibility
-
-This device driver is designed for the 3Com 3c515 ISA Fast EtherLink XL,
-3Com's ISA bus adapter for Fast Ethernet.  Due to the unique I/O port layout,
-it's not practical to integrate this driver with the other EtherLink drivers.
-
-II. Board-specific settings
-
-The Corkscrew has an EEPROM for configuration, but no special settings are
-needed for Linux.
-
-III. Driver operation
-
-The 3c515 series use an interface that's very similar to the 3c900 "Boomerang"
-PCI cards, with the bus master interface extensively modified to work with
-the ISA bus.
-
-The card is capable of full-bus-master transfers with separate
-lists of transmit and receive descriptors, similar to the AMD LANCE/PCnet,
-DEC Tulip and Intel Speedo3.
-
-This driver uses a "RX_COPYBREAK" scheme rather than a fixed intermediate
-receive buffer.  This scheme allocates full-sized skbuffs as receive
-buffers.  The value RX_COPYBREAK is used as the copying breakpoint: it is
-chosen to trade-off the memory wasted by passing the full-sized skbuff to
-the queue layer for all frames vs. the copying cost of copying a frame to a
-correctly-sized skbuff.
-
-
-IIIC. Synchronization
-The driver runs as two independent, single-threaded flows of control.  One
-is the send-packet routine, which enforces single-threaded use by the netif
-layer.  The other thread is the interrupt handler, which is single
-threaded by the hardware and other software.
-
-IV. Notes
-
-Thanks to Terry Murphy of 3Com for providing documentation and a development
-board.
-
-The names "Vortex", "Boomerang" and "Corkscrew" are the internal 3Com
-project names.  I use these names to eliminate confusion -- 3Com product
-numbers and names are very similar and often confused.
-
-The new chips support both ethernet (1.5K) and FDDI (4.5K) frame sizes!
-This driver only supports ethernet frames because of the recent MTU limit
-of 1.5K, but the changes to support 4.5K are minimal.
-*/
-
-/* Operational definitions.
-   These are not used by other compilation units and thus are not
-   exported in a ".h" file.
-
-   First the windows.  There are eight register windows, with the command
-   and status registers available in each.
-   */
-#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
-#define EL3_CMD 0x0e
-#define EL3_STATUS 0x0e
-
-/* The top five bits written to EL3_CMD are a command, the lower
-   11 bits are the parameter, if applicable.
-   Note that 11 parameters bits was fine for ethernet, but the new chips
-   can handle FDDI length frames (~4500 octets) and now parameters count
-   32-bit 'Dwords' rather than octets. */
-
-enum corkscrew_cmd {
-	TotalReset = 0 << 11, SelectWindow = 1 << 11, StartCoax = 2 << 11,
-	RxDisable = 3 << 11, RxEnable = 4 << 11, RxReset = 5 << 11,
-	UpStall = 6 << 11, UpUnstall = (6 << 11) + 1, DownStall = (6 << 11) + 2,
-	DownUnstall = (6 << 11) + 3, RxDiscard = 8 << 11, TxEnable = 9 << 11,
-	TxDisable = 10 << 11, TxReset = 11 << 11, FakeIntr = 12 << 11,
-	AckIntr = 13 << 11, SetIntrEnb = 14 << 11, SetStatusEnb = 15 << 11,
-	SetRxFilter = 16 << 11, SetRxThreshold = 17 << 11,
-	SetTxThreshold = 18 << 11, SetTxStart = 19 << 11, StartDMAUp = 20 << 11,
-	StartDMADown = (20 << 11) + 1, StatsEnable = 21 << 11,
-	StatsDisable = 22 << 11, StopCoax = 23 << 11,
-};
-
-/* The SetRxFilter command accepts the following classes: */
-enum RxFilter {
-	RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8
-};
-
-/* Bits in the general status register. */
-enum corkscrew_status {
-	IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
-	TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
-	IntReq = 0x0040, StatsFull = 0x0080,
-	DMADone = 1 << 8, DownComplete = 1 << 9, UpComplete = 1 << 10,
-	DMAInProgress = 1 << 11,	/* DMA controller is still busy. */
-	CmdInProgress = 1 << 12,	/* EL3_CMD is still busy. */
-};
-
-/* Register window 1 offsets, the window used in normal operation.
-   On the Corkscrew this window is always mapped at offsets 0x10-0x1f. */
-enum Window1 {
-	TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14,
-	RxStatus = 0x18, Timer = 0x1A, TxStatus = 0x1B,
-	TxFree = 0x1C,		/* Remaining free bytes in Tx buffer. */
-};
-enum Window0 {
-	Wn0IRQ = 0x08,
-#if defined(CORKSCREW)
-	Wn0EepromCmd = 0x200A,	/* Corkscrew EEPROM command register. */
-	Wn0EepromData = 0x200C,	/* Corkscrew EEPROM results register. */
-#else
-	Wn0EepromCmd = 10,	/* Window 0: EEPROM command register. */
-	Wn0EepromData = 12,	/* Window 0: EEPROM results register. */
-#endif
-};
-enum Win0_EEPROM_bits {
-	EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0,
-	EEPROM_EWENB = 0x30,	/* Enable erasing/writing for 10 msec. */
-	EEPROM_EWDIS = 0x00,	/* Disable EWENB before 10 msec timeout. */
-};
-
-/* EEPROM locations. */
-enum eeprom_offset {
-	PhysAddr01 = 0, PhysAddr23 = 1, PhysAddr45 = 2, ModelID = 3,
-	EtherLink3ID = 7,
-};
-
-enum Window3 {			/* Window 3: MAC/config bits. */
-	Wn3_Config = 0, Wn3_MAC_Ctrl = 6, Wn3_Options = 8,
-};
-enum wn3_config {
-	Ram_size = 7,
-	Ram_width = 8,
-	Ram_speed = 0x30,
-	Rom_size = 0xc0,
-	Ram_split_shift = 16,
-	Ram_split = 3 << Ram_split_shift,
-	Xcvr_shift = 20,
-	Xcvr = 7 << Xcvr_shift,
-	Autoselect = 0x1000000,
-};
-
-enum Window4 {
-	Wn4_NetDiag = 6, Wn4_Media = 10,	/* Window 4: Xcvr/media bits. */
-};
-enum Win4_Media_bits {
-	Media_SQE = 0x0008,	/* Enable SQE error counting for AUI. */
-	Media_10TP = 0x00C0,	/* Enable link beat and jabber for 10baseT. */
-	Media_Lnk = 0x0080,	/* Enable just link beat for 100TX/100FX. */
-	Media_LnkBeat = 0x0800,
-};
-enum Window7 {			/* Window 7: Bus Master control. */
-	Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12,
-};
-
-/* Boomerang-style bus master control registers.  Note ISA aliases! */
-enum MasterCtrl {
-	PktStatus = 0x400, DownListPtr = 0x404, FragAddr = 0x408, FragLen =
-	    0x40c,
-	TxFreeThreshold = 0x40f, UpPktStatus = 0x410, UpListPtr = 0x418,
-};
-
-/* The Rx and Tx descriptor lists.
-   Caution Alpha hackers: these types are 32 bits!  Note also the 8 byte
-   alignment contraint on tx_ring[] and rx_ring[]. */
-struct boom_rx_desc {
-	u32 next;
-	s32 status;
-	u32 addr;
-	s32 length;
-};
-
-/* Values for the Rx status entry. */
-enum rx_desc_status {
-	RxDComplete = 0x00008000, RxDError = 0x4000,
-	/* See boomerang_rx() for actual error bits */
-};
-
-struct boom_tx_desc {
-	u32 next;
-	s32 status;
-	u32 addr;
-	s32 length;
-};
-
-struct corkscrew_private {
-	const char *product_name;
-	struct list_head list;
-	struct net_device *our_dev;
-	/* The Rx and Tx rings are here to keep them quad-word-aligned. */
-	struct boom_rx_desc rx_ring[RX_RING_SIZE];
-	struct boom_tx_desc tx_ring[TX_RING_SIZE];
-	/* The addresses of transmit- and receive-in-place skbuffs. */
-	struct sk_buff *rx_skbuff[RX_RING_SIZE];
-	struct sk_buff *tx_skbuff[TX_RING_SIZE];
-	unsigned int cur_rx, cur_tx;	/* The next free ring entry */
-	unsigned int dirty_rx, dirty_tx;/* The ring entries to be free()ed. */
-	struct sk_buff *tx_skb;	/* Packet being eaten by bus master ctrl.  */
-	struct timer_list timer;	/* Media selection timer. */
-	int capabilities	;	/* Adapter capabilities word. */
-	int options;			/* User-settable misc. driver options. */
-	int last_rx_packets;		/* For media autoselection. */
-	unsigned int available_media:8,	/* From Wn3_Options */
-		media_override:3,	/* Passed-in media type. */
-		default_media:3,	/* Read from the EEPROM. */
-		full_duplex:1, autoselect:1, bus_master:1,	/* Vortex can only do a fragment bus-m. */
-		full_bus_master_tx:1, full_bus_master_rx:1,	/* Boomerang  */
-		tx_full:1;
-	spinlock_t lock;
-	struct device *dev;
-};
-
-/* The action to take with a media selection timer tick.
-   Note that we deviate from the 3Com order by checking 10base2 before AUI.
- */
-enum xcvr_types {
-	XCVR_10baseT = 0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx,
-	XCVR_100baseFx, XCVR_MII = 6, XCVR_Default = 8,
-};
-
-static struct media_table {
-	char *name;
-	unsigned int media_bits:16,	/* Bits to set in Wn4_Media register. */
-		mask:8,			/* The transceiver-present bit in Wn3_Config. */
-		next:8;			/* The media type to try next. */
-	short wait;			/* Time before we check media status. */
-} media_tbl[] = {
-	{ "10baseT", Media_10TP, 0x08, XCVR_10base2, (14 * HZ) / 10 },
-	{ "10Mbs AUI", Media_SQE, 0x20, XCVR_Default, (1 * HZ) / 10},
-	{ "undefined", 0, 0x80, XCVR_10baseT, 10000},
-	{ "10base2", 0, 0x10, XCVR_AUI, (1 * HZ) / 10},
-	{ "100baseTX", Media_Lnk, 0x02, XCVR_100baseFx, (14 * HZ) / 10},
-	{ "100baseFX", Media_Lnk, 0x04, XCVR_MII, (14 * HZ) / 10},
-	{ "MII", 0, 0x40, XCVR_10baseT, 3 * HZ},
-	{ "undefined", 0, 0x01, XCVR_10baseT, 10000},
-	{ "Default", 0, 0xFF, XCVR_10baseT, 10000},
-};
-
-#ifdef __ISAPNP__
-static struct isapnp_device_id corkscrew_isapnp_adapters[] = {
-	{	ISAPNP_ANY_ID, ISAPNP_ANY_ID,
-		ISAPNP_VENDOR('T', 'C', 'M'), ISAPNP_FUNCTION(0x5051),
-		(long) "3Com Fast EtherLink ISA" },
-	{ }	/* terminate list */
-};
-
-MODULE_DEVICE_TABLE(isapnp, corkscrew_isapnp_adapters);
-
-static int nopnp;
-#endif /* __ISAPNP__ */
-
-static struct net_device *corkscrew_scan(int unit);
-static int corkscrew_setup(struct net_device *dev, int ioaddr,
-			    struct pnp_dev *idev, int card_number);
-static int corkscrew_open(struct net_device *dev);
-static void corkscrew_timer(struct timer_list *t);
-static netdev_tx_t corkscrew_start_xmit(struct sk_buff *skb,
-					struct net_device *dev);
-static int corkscrew_rx(struct net_device *dev);
-static void corkscrew_timeout(struct net_device *dev, unsigned int txqueue);
-static int boomerang_rx(struct net_device *dev);
-static irqreturn_t corkscrew_interrupt(int irq, void *dev_id);
-static int corkscrew_close(struct net_device *dev);
-static void update_stats(int addr, struct net_device *dev);
-static struct net_device_stats *corkscrew_get_stats(struct net_device *dev);
-static void set_rx_mode(struct net_device *dev);
-static const struct ethtool_ops netdev_ethtool_ops;
-
-
-/*
-   Unfortunately maximizing the shared code between the integrated and
-   module version of the driver results in a complicated set of initialization
-   procedures.
-   init_module() -- modules /  tc59x_init()  -- built-in
-		The wrappers for corkscrew_scan()
-   corkscrew_scan()  		 The common routine that scans for PCI and EISA cards
-   corkscrew_found_device() Allocate a device structure when we find a card.
-					Different versions exist for modules and built-in.
-   corkscrew_probe1()		Fill in the device structure -- this is separated
-					so that the modules code can put it in dev->init.
-*/
-/* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
-/* Note: this is the only limit on the number of cards supported!! */
-static int options[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1, };
-
-#ifdef MODULE
-static int debug = -1;
-
-module_param(debug, int, 0);
-module_param_array(options, int, NULL, 0);
-module_param(rx_copybreak, int, 0);
-module_param(max_interrupt_work, int, 0);
-MODULE_PARM_DESC(debug, "3c515 debug level (0-6)");
-MODULE_PARM_DESC(options, "3c515: Bits 0-2: media type, bit 3: full duplex, bit 4: bus mastering");
-MODULE_PARM_DESC(rx_copybreak, "3c515 copy breakpoint for copy-only-tiny-frames");
-MODULE_PARM_DESC(max_interrupt_work, "3c515 maximum events handled per interrupt");
-
-/* A list of all installed Vortex devices, for removing the driver module. */
-/* we will need locking (and refcounting) if we ever use it for more */
-static LIST_HEAD(root_corkscrew_dev);
-
-static int corkscrew_init_module(void)
-{
-	int found = 0;
-	if (debug >= 0)
-		corkscrew_debug = debug;
-	while (corkscrew_scan(-1))
-		found++;
-	return found ? 0 : -ENODEV;
-}
-module_init(corkscrew_init_module);
-
-#else
-struct net_device *tc515_probe(int unit)
-{
-	struct net_device *dev = corkscrew_scan(unit);
-
-	if (!dev)
-		return ERR_PTR(-ENODEV);
-
-	return dev;
-}
-#endif				/* not MODULE */
-
-static int check_device(unsigned ioaddr)
-{
-	int timer;
-
-	if (!request_region(ioaddr, CORKSCREW_TOTAL_SIZE, "3c515"))
-		return 0;
-	/* Check the resource configuration for a matching ioaddr. */
-	if ((inw(ioaddr + 0x2002) & 0x1f0) != (ioaddr & 0x1f0)) {
-		release_region(ioaddr, CORKSCREW_TOTAL_SIZE);
-		return 0;
-	}
-	/* Verify by reading the device ID from the EEPROM. */
-	outw(EEPROM_Read + 7, ioaddr + Wn0EepromCmd);
-	/* Pause for at least 162 us. for the read to take place. */
-	for (timer = 4; timer >= 0; timer--) {
-		udelay(162);
-		if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0)
-			break;
-	}
-	if (inw(ioaddr + Wn0EepromData) != 0x6d50) {
-		release_region(ioaddr, CORKSCREW_TOTAL_SIZE);
-		return 0;
-	}
-	return 1;
-}
-
-static void cleanup_card(struct net_device *dev)
-{
-	struct corkscrew_private *vp = netdev_priv(dev);
-	list_del_init(&vp->list);
-	if (dev->dma)
-		free_dma(dev->dma);
-	outw(TotalReset, dev->base_addr + EL3_CMD);
-	release_region(dev->base_addr, CORKSCREW_TOTAL_SIZE);
-	if (vp->dev)
-		pnp_device_detach(to_pnp_dev(vp->dev));
-}
-
-static struct net_device *corkscrew_scan(int unit)
-{
-	struct net_device *dev;
-	static int cards_found = 0;
-	static int ioaddr;
-	int err;
-#ifdef __ISAPNP__
-	short i;
-	static int pnp_cards;
-#endif
-
-	dev = alloc_etherdev(sizeof(struct corkscrew_private));
-	if (!dev)
-		return ERR_PTR(-ENOMEM);
-
-	if (unit >= 0) {
-		sprintf(dev->name, "eth%d", unit);
-		netdev_boot_setup_check(dev);
-	}
-
-#ifdef __ISAPNP__
-	if(nopnp == 1)
-		goto no_pnp;
-	for(i=0; corkscrew_isapnp_adapters[i].vendor != 0; i++) {
-		struct pnp_dev *idev = NULL;
-		int irq;
-		while((idev = pnp_find_dev(NULL,
-					   corkscrew_isapnp_adapters[i].vendor,
-					   corkscrew_isapnp_adapters[i].function,
-					   idev))) {
-
-			if (pnp_device_attach(idev) < 0)
-				continue;
-			if (pnp_activate_dev(idev) < 0) {
-				pr_warn("pnp activate failed (out of resources?)\n");
-				pnp_device_detach(idev);
-				continue;
-			}
-			if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0)) {
-				pnp_device_detach(idev);
-				continue;
-			}
-			ioaddr = pnp_port_start(idev, 0);
-			irq = pnp_irq(idev, 0);
-			if (!check_device(ioaddr)) {
-				pnp_device_detach(idev);
-				continue;
-			}
-			if(corkscrew_debug)
-				pr_debug("ISAPNP reports %s at i/o 0x%x, irq %d\n",
-					(char*) corkscrew_isapnp_adapters[i].driver_data, ioaddr, irq);
-			pr_info("3c515 Resource configuration register %#4.4x, DCR %4.4x.\n",
-		     		inl(ioaddr + 0x2002), inw(ioaddr + 0x2000));
-			/* irq = inw(ioaddr + 0x2002) & 15; */ /* Use the irq from isapnp */
-			SET_NETDEV_DEV(dev, &idev->dev);
-			pnp_cards++;
-			err = corkscrew_setup(dev, ioaddr, idev, cards_found++);
-			if (!err)
-				return dev;
-			cleanup_card(dev);
-		}
-	}
-no_pnp:
-#endif /* __ISAPNP__ */
-
-	/* Check all locations on the ISA bus -- evil! */
-	for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20) {
-		if (!check_device(ioaddr))
-			continue;
-
-		pr_info("3c515 Resource configuration register %#4.4x, DCR %4.4x.\n",
-		     inl(ioaddr + 0x2002), inw(ioaddr + 0x2000));
-		err = corkscrew_setup(dev, ioaddr, NULL, cards_found++);
-		if (!err)
-			return dev;
-		cleanup_card(dev);
-	}
-	free_netdev(dev);
-	return NULL;
-}
-
-
-static const struct net_device_ops netdev_ops = {
-	.ndo_open		= corkscrew_open,
-	.ndo_stop		= corkscrew_close,
-	.ndo_start_xmit		= corkscrew_start_xmit,
-	.ndo_tx_timeout		= corkscrew_timeout,
-	.ndo_get_stats		= corkscrew_get_stats,
-	.ndo_set_rx_mode	= set_rx_mode,
-	.ndo_set_mac_address 	= eth_mac_addr,
-	.ndo_validate_addr	= eth_validate_addr,
-};
-
-
-static int corkscrew_setup(struct net_device *dev, int ioaddr,
-			    struct pnp_dev *idev, int card_number)
-{
-	struct corkscrew_private *vp = netdev_priv(dev);
-	unsigned int eeprom[0x40], checksum = 0;	/* EEPROM contents */
-	__be16 addr[ETH_ALEN / 2];
-	int i;
-	int irq;
-
-#ifdef __ISAPNP__
-	if (idev) {
-		irq = pnp_irq(idev, 0);
-		vp->dev = &idev->dev;
-	} else {
-		irq = inw(ioaddr + 0x2002) & 15;
-	}
-#else
-	irq = inw(ioaddr + 0x2002) & 15;
-#endif
-
-	dev->base_addr = ioaddr;
-	dev->irq = irq;
-	dev->dma = inw(ioaddr + 0x2000) & 7;
-	vp->product_name = "3c515";
-	vp->options = dev->mem_start;
-	vp->our_dev = dev;
-
-	if (!vp->options) {
-		 if (card_number >= MAX_UNITS)
-			vp->options = -1;
-		else
-			vp->options = options[card_number];
-	}
-
-	if (vp->options >= 0) {
-		vp->media_override = vp->options & 7;
-		if (vp->media_override == 2)
-			vp->media_override = 0;
-		vp->full_duplex = (vp->options & 8) ? 1 : 0;
-		vp->bus_master = (vp->options & 16) ? 1 : 0;
-	} else {
-		vp->media_override = 7;
-		vp->full_duplex = 0;
-		vp->bus_master = 0;
-	}
-#ifdef MODULE
-	list_add(&vp->list, &root_corkscrew_dev);
-#endif
-
-	pr_info("%s: 3Com %s at %#3x,", dev->name, vp->product_name, ioaddr);
-
-	spin_lock_init(&vp->lock);
-
-	timer_setup(&vp->timer, corkscrew_timer, 0);
-
-	/* Read the station address from the EEPROM. */
-	EL3WINDOW(0);
-	for (i = 0; i < 0x18; i++) {
-		int timer;
-		outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd);
-		/* Pause for at least 162 us. for the read to take place. */
-		for (timer = 4; timer >= 0; timer--) {
-			udelay(162);
-			if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0)
-				break;
-		}
-		eeprom[i] = inw(ioaddr + Wn0EepromData);
-		checksum ^= eeprom[i];
-		if (i < 3)
-			addr[i] = htons(eeprom[i]);
-	}
-	eth_hw_addr_set(dev, (u8 *)addr);
-	checksum = (checksum ^ (checksum >> 8)) & 0xff;
-	if (checksum != 0x00)
-		pr_cont(" ***INVALID CHECKSUM %4.4x*** ", checksum);
-	pr_cont(" %pM", dev->dev_addr);
-	if (eeprom[16] == 0x11c7) {	/* Corkscrew */
-		if (request_dma(dev->dma, "3c515")) {
-			pr_cont(", DMA %d allocation failed", dev->dma);
-			dev->dma = 0;
-		} else
-			pr_cont(", DMA %d", dev->dma);
-	}
-	pr_cont(", IRQ %d\n", dev->irq);
-	/* Tell them about an invalid IRQ. */
-	if (corkscrew_debug && (dev->irq <= 0 || dev->irq > 15))
-		pr_warn(" *** Warning: this IRQ is unlikely to work! ***\n");
-
-	{
-		static const char * const ram_split[] = {
-			"5:3", "3:1", "1:1", "3:5"
-		};
-		__u32 config;
-		EL3WINDOW(3);
-		vp->available_media = inw(ioaddr + Wn3_Options);
-		config = inl(ioaddr + Wn3_Config);
-		if (corkscrew_debug > 1)
-			pr_info("  Internal config register is %4.4x, transceivers %#x.\n",
-				config, inw(ioaddr + Wn3_Options));
-		pr_info("  %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n",
-			8 << config & Ram_size,
-			config & Ram_width ? "word" : "byte",
-			ram_split[(config & Ram_split) >> Ram_split_shift],
-			config & Autoselect ? "autoselect/" : "",
-			media_tbl[(config & Xcvr) >> Xcvr_shift].name);
-		vp->default_media = (config & Xcvr) >> Xcvr_shift;
-		vp->autoselect = config & Autoselect ? 1 : 0;
-		dev->if_port = vp->default_media;
-	}
-	if (vp->media_override != 7) {
-		pr_info("  Media override to transceiver type %d (%s).\n",
-		       vp->media_override,
-		       media_tbl[vp->media_override].name);
-		dev->if_port = vp->media_override;
-	}
-
-	vp->capabilities = eeprom[16];
-	vp->full_bus_master_tx = (vp->capabilities & 0x20) ? 1 : 0;
-	/* Rx is broken at 10mbps, so we always disable it. */
-	/* vp->full_bus_master_rx = 0; */
-	vp->full_bus_master_rx = (vp->capabilities & 0x20) ? 1 : 0;
-
-	/* The 3c51x-specific entries in the device structure. */
-	dev->netdev_ops = &netdev_ops;
-	dev->watchdog_timeo = (400 * HZ) / 1000;
-	dev->ethtool_ops = &netdev_ethtool_ops;
-
-	return register_netdev(dev);
-}
-
-
-static int corkscrew_open(struct net_device *dev)
-{
-	int ioaddr = dev->base_addr;
-	struct corkscrew_private *vp = netdev_priv(dev);
-	bool armtimer = false;
-	__u32 config;
-	int i;
-
-	/* Before initializing select the active media port. */
-	EL3WINDOW(3);
-	if (vp->full_duplex)
-		outb(0x20, ioaddr + Wn3_MAC_Ctrl);	/* Set the full-duplex bit. */
-	config = inl(ioaddr + Wn3_Config);
-
-	if (vp->media_override != 7) {
-		if (corkscrew_debug > 1)
-			pr_info("%s: Media override to transceiver %d (%s).\n",
-				dev->name, vp->media_override,
-				media_tbl[vp->media_override].name);
-		dev->if_port = vp->media_override;
-	} else if (vp->autoselect) {
-		/* Find first available media type, starting with 100baseTx. */
-		dev->if_port = 4;
-		while (!(vp->available_media & media_tbl[dev->if_port].mask))
-			dev->if_port = media_tbl[dev->if_port].next;
-
-		if (corkscrew_debug > 1)
-			pr_debug("%s: Initial media type %s.\n",
-			       dev->name, media_tbl[dev->if_port].name);
-		armtimer = true;
-	} else
-		dev->if_port = vp->default_media;
-
-	config = (config & ~Xcvr) | (dev->if_port << Xcvr_shift);
-	outl(config, ioaddr + Wn3_Config);
-
-	if (corkscrew_debug > 1) {
-		pr_debug("%s: corkscrew_open() InternalConfig %8.8x.\n",
-		       dev->name, config);
-	}
-
-	outw(TxReset, ioaddr + EL3_CMD);
-	for (i = 20; i >= 0; i--)
-		if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
-			break;
-
-	outw(RxReset, ioaddr + EL3_CMD);
-	/* Wait a few ticks for the RxReset command to complete. */
-	for (i = 20; i >= 0; i--)
-		if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
-			break;
-
-	outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
-
-	/* Use the now-standard shared IRQ implementation. */
-	if (vp->capabilities == 0x11c7) {
-		/* Corkscrew: Cannot share ISA resources. */
-		if (dev->irq == 0 ||
-		    dev->dma == 0 ||
-		    request_irq(dev->irq, corkscrew_interrupt, 0,
-				vp->product_name, dev))
-			return -EAGAIN;
-		enable_dma(dev->dma);
-		set_dma_mode(dev->dma, DMA_MODE_CASCADE);
-	} else if (request_irq(dev->irq, corkscrew_interrupt, IRQF_SHARED,
-			       vp->product_name, dev)) {
-		return -EAGAIN;
-	}
-
-	if (armtimer)
-		mod_timer(&vp->timer, jiffies + media_tbl[dev->if_port].wait);
-
-	if (corkscrew_debug > 1) {
-		EL3WINDOW(4);
-		pr_debug("%s: corkscrew_open() irq %d media status %4.4x.\n",
-		       dev->name, dev->irq, inw(ioaddr + Wn4_Media));
-	}
-
-	/* Set the station address and mask in window 2 each time opened. */
-	EL3WINDOW(2);
-	for (i = 0; i < 6; i++)
-		outb(dev->dev_addr[i], ioaddr + i);
-	for (; i < 12; i += 2)
-		outw(0, ioaddr + i);
-
-	if (dev->if_port == 3)
-		/* Start the thinnet transceiver. We should really wait 50ms... */
-		outw(StartCoax, ioaddr + EL3_CMD);
-	EL3WINDOW(4);
-	outw((inw(ioaddr + Wn4_Media) & ~(Media_10TP | Media_SQE)) |
-	     media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media);
-
-	/* Switch to the stats window, and clear all stats by reading. */
-	outw(StatsDisable, ioaddr + EL3_CMD);
-	EL3WINDOW(6);
-	for (i = 0; i < 10; i++)
-		inb(ioaddr + i);
-	inw(ioaddr + 10);
-	inw(ioaddr + 12);
-	/* New: On the Vortex we must also clear the BadSSD counter. */
-	EL3WINDOW(4);
-	inb(ioaddr + 12);
-	/* ..and on the Boomerang we enable the extra statistics bits. */
-	outw(0x0040, ioaddr + Wn4_NetDiag);
-
-	/* Switch to register set 7 for normal use. */
-	EL3WINDOW(7);
-
-	if (vp->full_bus_master_rx) {	/* Boomerang bus master. */
-		vp->cur_rx = vp->dirty_rx = 0;
-		if (corkscrew_debug > 2)
-			pr_debug("%s:  Filling in the Rx ring.\n", dev->name);
-		for (i = 0; i < RX_RING_SIZE; i++) {
-			struct sk_buff *skb;
-			if (i < (RX_RING_SIZE - 1))
-				vp->rx_ring[i].next =
-				    isa_virt_to_bus(&vp->rx_ring[i + 1]);
-			else
-				vp->rx_ring[i].next = 0;
-			vp->rx_ring[i].status = 0;	/* Clear complete bit. */
-			vp->rx_ring[i].length = PKT_BUF_SZ | 0x80000000;
-			skb = netdev_alloc_skb(dev, PKT_BUF_SZ);
-			vp->rx_skbuff[i] = skb;
-			if (skb == NULL)
-				break;	/* Bad news!  */
-			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
-			vp->rx_ring[i].addr = isa_virt_to_bus(skb->data);
-		}
-		if (i != 0)
-			vp->rx_ring[i - 1].next =
-				isa_virt_to_bus(&vp->rx_ring[0]);	/* Wrap the ring. */
-		outl(isa_virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr);
-	}
-	if (vp->full_bus_master_tx) {	/* Boomerang bus master Tx. */
-		vp->cur_tx = vp->dirty_tx = 0;
-		outb(PKT_BUF_SZ >> 8, ioaddr + TxFreeThreshold);	/* Room for a packet. */
-		/* Clear the Tx ring. */
-		for (i = 0; i < TX_RING_SIZE; i++)
-			vp->tx_skbuff[i] = NULL;
-		outl(0, ioaddr + DownListPtr);
-	}
-	/* Set receiver mode: presumably accept b-case and phys addr only. */
-	set_rx_mode(dev);
-	outw(StatsEnable, ioaddr + EL3_CMD);	/* Turn on statistics. */
-
-	netif_start_queue(dev);
-
-	outw(RxEnable, ioaddr + EL3_CMD);	/* Enable the receiver. */
-	outw(TxEnable, ioaddr + EL3_CMD);	/* Enable transmitter. */
-	/* Allow status bits to be seen. */
-	outw(SetStatusEnb | AdapterFailure | IntReq | StatsFull |
-	     (vp->full_bus_master_tx ? DownComplete : TxAvailable) |
-	     (vp->full_bus_master_rx ? UpComplete : RxComplete) |
-	     (vp->bus_master ? DMADone : 0), ioaddr + EL3_CMD);
-	/* Ack all pending events, and set active indicator mask. */
-	outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
-	     ioaddr + EL3_CMD);
-	outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
-	     | (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete,
-	     ioaddr + EL3_CMD);
-
-	return 0;
-}
-
-static void corkscrew_timer(struct timer_list *t)
-{
-#ifdef AUTOMEDIA
-	struct corkscrew_private *vp = timer_container_of(vp, t, timer);
-	struct net_device *dev = vp->our_dev;
-	int ioaddr = dev->base_addr;
-	unsigned long flags;
-	int ok = 0;
-
-	if (corkscrew_debug > 1)
-		pr_debug("%s: Media selection timer tick happened, %s.\n",
-		       dev->name, media_tbl[dev->if_port].name);
-
-	spin_lock_irqsave(&vp->lock, flags);
-
-	{
-		int old_window = inw(ioaddr + EL3_CMD) >> 13;
-		int media_status;
-		EL3WINDOW(4);
-		media_status = inw(ioaddr + Wn4_Media);
-		switch (dev->if_port) {
-		case 0:
-		case 4:
-		case 5:	/* 10baseT, 100baseTX, 100baseFX  */
-			if (media_status & Media_LnkBeat) {
-				ok = 1;
-				if (corkscrew_debug > 1)
-					pr_debug("%s: Media %s has link beat, %x.\n",
-						dev->name,
-						media_tbl[dev->if_port].name,
-						media_status);
-			} else if (corkscrew_debug > 1)
-				pr_debug("%s: Media %s is has no link beat, %x.\n",
-					dev->name,
-					media_tbl[dev->if_port].name,
-					media_status);
-
-			break;
-		default:	/* Other media types handled by Tx timeouts. */
-			if (corkscrew_debug > 1)
-				pr_debug("%s: Media %s is has no indication, %x.\n",
-					dev->name,
-					media_tbl[dev->if_port].name,
-					media_status);
-			ok = 1;
-		}
-		if (!ok) {
-			__u32 config;
-
-			do {
-				dev->if_port =
-				    media_tbl[dev->if_port].next;
-			}
-			while (!(vp->available_media & media_tbl[dev->if_port].mask));
-
-			if (dev->if_port == 8) {	/* Go back to default. */
-				dev->if_port = vp->default_media;
-				if (corkscrew_debug > 1)
-					pr_debug("%s: Media selection failing, using default %s port.\n",
-						dev->name,
-						media_tbl[dev->if_port].name);
-			} else {
-				if (corkscrew_debug > 1)
-					pr_debug("%s: Media selection failed, now trying %s port.\n",
-						dev->name,
-						media_tbl[dev->if_port].name);
-				vp->timer.expires = jiffies + media_tbl[dev->if_port].wait;
-				add_timer(&vp->timer);
-			}
-			outw((media_status & ~(Media_10TP | Media_SQE)) |
-			     media_tbl[dev->if_port].media_bits,
-			     ioaddr + Wn4_Media);
-
-			EL3WINDOW(3);
-			config = inl(ioaddr + Wn3_Config);
-			config = (config & ~Xcvr) | (dev->if_port << Xcvr_shift);
-			outl(config, ioaddr + Wn3_Config);
-
-			outw(dev->if_port == 3 ? StartCoax : StopCoax,
-			     ioaddr + EL3_CMD);
-		}
-		EL3WINDOW(old_window);
-	}
-
-	spin_unlock_irqrestore(&vp->lock, flags);
-	if (corkscrew_debug > 1)
-		pr_debug("%s: Media selection timer finished, %s.\n",
-		       dev->name, media_tbl[dev->if_port].name);
-
-#endif				/* AUTOMEDIA */
-}
-
-static void corkscrew_timeout(struct net_device *dev, unsigned int txqueue)
-{
-	int i;
-	struct corkscrew_private *vp = netdev_priv(dev);
-	int ioaddr = dev->base_addr;
-
-	pr_warn("%s: transmit timed out, tx_status %2.2x status %4.4x\n",
-		dev->name, inb(ioaddr + TxStatus),
-		inw(ioaddr + EL3_STATUS));
-	/* Slight code bloat to be user friendly. */
-	if ((inb(ioaddr + TxStatus) & 0x88) == 0x88)
-		pr_warn("%s: Transmitter encountered 16 collisions -- network cable problem?\n",
-			dev->name);
-#ifndef final_version
-	pr_debug("  Flags; bus-master %d, full %d; dirty %d current %d.\n",
-	       vp->full_bus_master_tx, vp->tx_full, vp->dirty_tx,
-	       vp->cur_tx);
-	pr_debug("  Down list %8.8x vs. %p.\n", inl(ioaddr + DownListPtr),
-	       &vp->tx_ring[0]);
-	for (i = 0; i < TX_RING_SIZE; i++) {
-		pr_debug("  %d: %p  length %8.8x status %8.8x\n", i,
-		       &vp->tx_ring[i],
-		       vp->tx_ring[i].length, vp->tx_ring[i].status);
-	}
-#endif
-	/* Issue TX_RESET and TX_START commands. */
-	outw(TxReset, ioaddr + EL3_CMD);
-	for (i = 20; i >= 0; i--)
-		if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
-			break;
-	outw(TxEnable, ioaddr + EL3_CMD);
-	netif_trans_update(dev); /* prevent tx timeout */
-	dev->stats.tx_errors++;
-	dev->stats.tx_dropped++;
-	netif_wake_queue(dev);
-}
-
-static netdev_tx_t corkscrew_start_xmit(struct sk_buff *skb,
-					struct net_device *dev)
-{
-	struct corkscrew_private *vp = netdev_priv(dev);
-	int ioaddr = dev->base_addr;
-
-	/* Block a timer-based transmit from overlapping. */
-
-	netif_stop_queue(dev);
-
-	if (vp->full_bus_master_tx) {	/* BOOMERANG bus-master */
-		/* Calculate the next Tx descriptor entry. */
-		int entry = vp->cur_tx % TX_RING_SIZE;
-		struct boom_tx_desc *prev_entry;
-		unsigned long flags;
-		int i;
-
-		if (vp->tx_full)	/* No room to transmit with */
-			return NETDEV_TX_BUSY;
-		if (vp->cur_tx != 0)
-			prev_entry = &vp->tx_ring[(vp->cur_tx - 1) % TX_RING_SIZE];
-		else
-			prev_entry = NULL;
-		if (corkscrew_debug > 3)
-			pr_debug("%s: Trying to send a packet, Tx index %d.\n",
-				dev->name, vp->cur_tx);
-		/* vp->tx_full = 1; */
-		vp->tx_skbuff[entry] = skb;
-		vp->tx_ring[entry].next = 0;
-		vp->tx_ring[entry].addr = isa_virt_to_bus(skb->data);
-		vp->tx_ring[entry].length = skb->len | 0x80000000;
-		vp->tx_ring[entry].status = skb->len | 0x80000000;
-
-		spin_lock_irqsave(&vp->lock, flags);
-		outw(DownStall, ioaddr + EL3_CMD);
-		/* Wait for the stall to complete. */
-		for (i = 20; i >= 0; i--)
-			if ((inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0)
-				break;
-		if (prev_entry)
-			prev_entry->next = isa_virt_to_bus(&vp->tx_ring[entry]);
-		if (inl(ioaddr + DownListPtr) == 0) {
-			outl(isa_virt_to_bus(&vp->tx_ring[entry]),
-			     ioaddr + DownListPtr);
-			queued_packet++;
-		}
-		outw(DownUnstall, ioaddr + EL3_CMD);
-		spin_unlock_irqrestore(&vp->lock, flags);
-
-		vp->cur_tx++;
-		if (vp->cur_tx - vp->dirty_tx > TX_RING_SIZE - 1)
-			vp->tx_full = 1;
-		else {		/* Clear previous interrupt enable. */
-			if (prev_entry)
-				prev_entry->status &= ~0x80000000;
-			netif_wake_queue(dev);
-		}
-		return NETDEV_TX_OK;
-	}
-	/* Put out the doubleword header... */
-	outl(skb->len, ioaddr + TX_FIFO);
-	dev->stats.tx_bytes += skb->len;
-#ifdef VORTEX_BUS_MASTER
-	if (vp->bus_master) {
-		/* Set the bus-master controller to transfer the packet. */
-		outl(isa_virt_to_bus(skb->data), ioaddr + Wn7_MasterAddr);
-		outw((skb->len + 3) & ~3, ioaddr + Wn7_MasterLen);
-		vp->tx_skb = skb;
-		outw(StartDMADown, ioaddr + EL3_CMD);
-		/* queue will be woken at the DMADone interrupt. */
-	} else {
-		/* ... and the packet rounded to a doubleword. */
-		outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
-		dev_kfree_skb(skb);
-		if (inw(ioaddr + TxFree) > 1536) {
-			netif_wake_queue(dev);
-		} else
-			/* Interrupt us when the FIFO has room for max-sized packet. */
-			outw(SetTxThreshold + (1536 >> 2),
-			     ioaddr + EL3_CMD);
-	}
-#else
-	/* ... and the packet rounded to a doubleword. */
-	outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
-	dev_kfree_skb(skb);
-	if (inw(ioaddr + TxFree) > 1536) {
-		netif_wake_queue(dev);
-	} else
-		/* Interrupt us when the FIFO has room for max-sized packet. */
-		outw(SetTxThreshold + (1536 >> 2), ioaddr + EL3_CMD);
-#endif				/* bus master */
-
-
-	/* Clear the Tx status stack. */
-	{
-		short tx_status;
-		int i = 4;
-
-		while (--i > 0 && (tx_status = inb(ioaddr + TxStatus)) > 0) {
-			if (tx_status & 0x3C) {	/* A Tx-disabling error occurred.  */
-				if (corkscrew_debug > 2)
-					pr_debug("%s: Tx error, status %2.2x.\n",
-						dev->name, tx_status);
-				if (tx_status & 0x04)
-					dev->stats.tx_fifo_errors++;
-				if (tx_status & 0x38)
-					dev->stats.tx_aborted_errors++;
-				if (tx_status & 0x30) {
-					int j;
-					outw(TxReset, ioaddr + EL3_CMD);
-					for (j = 20; j >= 0; j--)
-						if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
-							break;
-				}
-				outw(TxEnable, ioaddr + EL3_CMD);
-			}
-			outb(0x00, ioaddr + TxStatus);	/* Pop the status stack. */
-		}
-	}
-	return NETDEV_TX_OK;
-}
-
-/* The interrupt handler does all of the Rx thread work and cleans up
-   after the Tx thread. */
-
-static irqreturn_t corkscrew_interrupt(int irq, void *dev_id)
-{
-	/* Use the now-standard shared IRQ implementation. */
-	struct net_device *dev = dev_id;
-	struct corkscrew_private *lp = netdev_priv(dev);
-	int ioaddr, status;
-	int latency;
-	int i = max_interrupt_work;
-
-	ioaddr = dev->base_addr;
-	latency = inb(ioaddr + Timer);
-
-	spin_lock(&lp->lock);
-
-	status = inw(ioaddr + EL3_STATUS);
-
-	if (corkscrew_debug > 4)
-		pr_debug("%s: interrupt, status %4.4x, timer %d.\n",
-			dev->name, status, latency);
-	if ((status & 0xE000) != 0xE000) {
-		static int donedidthis;
-		/* Some interrupt controllers store a bogus interrupt from boot-time.
-		   Ignore a single early interrupt, but don't hang the machine for
-		   other interrupt problems. */
-		if (donedidthis++ > 100) {
-			pr_err("%s: Bogus interrupt, bailing. Status %4.4x, start=%d.\n",
-				   dev->name, status, netif_running(dev));
-			free_irq(dev->irq, dev);
-			dev->irq = -1;
-		}
-	}
-
-	do {
-		if (corkscrew_debug > 5)
-			pr_debug("%s: In interrupt loop, status %4.4x.\n",
-			       dev->name, status);
-		if (status & RxComplete)
-			corkscrew_rx(dev);
-
-		if (status & TxAvailable) {
-			if (corkscrew_debug > 5)
-				pr_debug("	TX room bit was handled.\n");
-			/* There's room in the FIFO for a full-sized packet. */
-			outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
-			netif_wake_queue(dev);
-		}
-		if (status & DownComplete) {
-			unsigned int dirty_tx = lp->dirty_tx;
-
-			while (lp->cur_tx - dirty_tx > 0) {
-				int entry = dirty_tx % TX_RING_SIZE;
-				if (inl(ioaddr + DownListPtr) == isa_virt_to_bus(&lp->tx_ring[entry]))
-					break;	/* It still hasn't been processed. */
-				if (lp->tx_skbuff[entry]) {
-					dev_consume_skb_irq(lp->tx_skbuff[entry]);
-					lp->tx_skbuff[entry] = NULL;
-				}
-				dirty_tx++;
-			}
-			lp->dirty_tx = dirty_tx;
-			outw(AckIntr | DownComplete, ioaddr + EL3_CMD);
-			if (lp->tx_full && (lp->cur_tx - dirty_tx <= TX_RING_SIZE - 1)) {
-				lp->tx_full = 0;
-				netif_wake_queue(dev);
-			}
-		}
-#ifdef VORTEX_BUS_MASTER
-		if (status & DMADone) {
-			outw(0x1000, ioaddr + Wn7_MasterStatus);	/* Ack the event. */
-			dev_consume_skb_irq(lp->tx_skb);	/* Release the transferred buffer */
-			netif_wake_queue(dev);
-		}
-#endif
-		if (status & UpComplete) {
-			boomerang_rx(dev);
-			outw(AckIntr | UpComplete, ioaddr + EL3_CMD);
-		}
-		if (status & (AdapterFailure | RxEarly | StatsFull)) {
-			/* Handle all uncommon interrupts at once. */
-			if (status & RxEarly) {	/* Rx early is unused. */
-				corkscrew_rx(dev);
-				outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
-			}
-			if (status & StatsFull) {	/* Empty statistics. */
-				static int DoneDidThat;
-				if (corkscrew_debug > 4)
-					pr_debug("%s: Updating stats.\n", dev->name);
-				update_stats(ioaddr, dev);
-				/* DEBUG HACK: Disable statistics as an interrupt source. */
-				/* This occurs when we have the wrong media type! */
-				if (DoneDidThat == 0 && inw(ioaddr + EL3_STATUS) & StatsFull) {
-					int win, reg;
-					pr_notice("%s: Updating stats failed, disabling stats as an interrupt source.\n",
-						dev->name);
-					for (win = 0; win < 8; win++) {
-						EL3WINDOW(win);
-						pr_notice("Vortex window %d:", win);
-						for (reg = 0; reg < 16; reg++)
-							pr_cont(" %2.2x", inb(ioaddr + reg));
-						pr_cont("\n");
-					}
-					EL3WINDOW(7);
-					outw(SetIntrEnb | TxAvailable |
-					     RxComplete | AdapterFailure |
-					     UpComplete | DownComplete |
-					     TxComplete, ioaddr + EL3_CMD);
-					DoneDidThat++;
-				}
-			}
-			if (status & AdapterFailure) {
-				/* Adapter failure requires Rx reset and reinit. */
-				outw(RxReset, ioaddr + EL3_CMD);
-				/* Set the Rx filter to the current state. */
-				set_rx_mode(dev);
-				outw(RxEnable, ioaddr + EL3_CMD);	/* Re-enable the receiver. */
-				outw(AckIntr | AdapterFailure,
-				     ioaddr + EL3_CMD);
-			}
-		}
-
-		if (--i < 0) {
-			pr_err("%s: Too much work in interrupt, status %4.4x. Disabling functions (%4.4x).\n",
-				dev->name, status, SetStatusEnb | ((~status) & 0x7FE));
-			/* Disable all pending interrupts. */
-			outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD);
-			outw(AckIntr | 0x7FF, ioaddr + EL3_CMD);
-			break;
-		}
-		/* Acknowledge the IRQ. */
-		outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
-
-	} while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete));
-
-	spin_unlock(&lp->lock);
-
-	if (corkscrew_debug > 4)
-		pr_debug("%s: exiting interrupt, status %4.4x.\n", dev->name, status);
-	return IRQ_HANDLED;
-}
-
-static int corkscrew_rx(struct net_device *dev)
-{
-	int ioaddr = dev->base_addr;
-	int i;
-	short rx_status;
-
-	if (corkscrew_debug > 5)
-		pr_debug("   In rx_packet(), status %4.4x, rx_status %4.4x.\n",
-		     inw(ioaddr + EL3_STATUS), inw(ioaddr + RxStatus));
-	while ((rx_status = inw(ioaddr + RxStatus)) > 0) {
-		if (rx_status & 0x4000) {	/* Error, update stats. */
-			unsigned char rx_error = inb(ioaddr + RxErrors);
-			if (corkscrew_debug > 2)
-				pr_debug(" Rx error: status %2.2x.\n",
-				       rx_error);
-			dev->stats.rx_errors++;
-			if (rx_error & 0x01)
-				dev->stats.rx_over_errors++;
-			if (rx_error & 0x02)
-				dev->stats.rx_length_errors++;
-			if (rx_error & 0x04)
-				dev->stats.rx_frame_errors++;
-			if (rx_error & 0x08)
-				dev->stats.rx_crc_errors++;
-			if (rx_error & 0x10)
-				dev->stats.rx_length_errors++;
-		} else {
-			/* The packet length: up to 4.5K!. */
-			short pkt_len = rx_status & 0x1fff;
-			struct sk_buff *skb;
-
-			skb = netdev_alloc_skb(dev, pkt_len + 5 + 2);
-			if (corkscrew_debug > 4)
-				pr_debug("Receiving packet size %d status %4.4x.\n",
-				     pkt_len, rx_status);
-			if (skb != NULL) {
-				skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
-				/* 'skb_put()' points to the start of sk_buff data area. */
-				insl(ioaddr + RX_FIFO,
-				     skb_put(skb, pkt_len),
-				     (pkt_len + 3) >> 2);
-				outw(RxDiscard, ioaddr + EL3_CMD);	/* Pop top Rx packet. */
-				skb->protocol = eth_type_trans(skb, dev);
-				netif_rx(skb);
-				dev->stats.rx_packets++;
-				dev->stats.rx_bytes += pkt_len;
-				/* Wait a limited time to go to next packet. */
-				for (i = 200; i >= 0; i--)
-					if (! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
-						break;
-				continue;
-			} else if (corkscrew_debug)
-				pr_debug("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, pkt_len);
-		}
-		outw(RxDiscard, ioaddr + EL3_CMD);
-		dev->stats.rx_dropped++;
-		/* Wait a limited time to skip this packet. */
-		for (i = 200; i >= 0; i--)
-			if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
-				break;
-	}
-	return 0;
-}
-
-static int boomerang_rx(struct net_device *dev)
-{
-	struct corkscrew_private *vp = netdev_priv(dev);
-	int entry = vp->cur_rx % RX_RING_SIZE;
-	int ioaddr = dev->base_addr;
-	int rx_status;
-
-	if (corkscrew_debug > 5)
-		pr_debug("   In boomerang_rx(), status %4.4x, rx_status %4.4x.\n",
-			inw(ioaddr + EL3_STATUS), inw(ioaddr + RxStatus));
-	while ((rx_status = vp->rx_ring[entry].status) & RxDComplete) {
-		if (rx_status & RxDError) {	/* Error, update stats. */
-			unsigned char rx_error = rx_status >> 16;
-			if (corkscrew_debug > 2)
-				pr_debug(" Rx error: status %2.2x.\n",
-				       rx_error);
-			dev->stats.rx_errors++;
-			if (rx_error & 0x01)
-				dev->stats.rx_over_errors++;
-			if (rx_error & 0x02)
-				dev->stats.rx_length_errors++;
-			if (rx_error & 0x04)
-				dev->stats.rx_frame_errors++;
-			if (rx_error & 0x08)
-				dev->stats.rx_crc_errors++;
-			if (rx_error & 0x10)
-				dev->stats.rx_length_errors++;
-		} else {
-			/* The packet length: up to 4.5K!. */
-			short pkt_len = rx_status & 0x1fff;
-			struct sk_buff *skb;
-
-			dev->stats.rx_bytes += pkt_len;
-			if (corkscrew_debug > 4)
-				pr_debug("Receiving packet size %d status %4.4x.\n",
-				     pkt_len, rx_status);
-
-			/* Check if the packet is long enough to just accept without
-			   copying to a properly sized skbuff. */
-			if (pkt_len < rx_copybreak &&
-			    (skb = netdev_alloc_skb(dev, pkt_len + 4)) != NULL) {
-				skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
-				/* 'skb_put()' points to the start of sk_buff data area. */
-				skb_put_data(skb,
-					     isa_bus_to_virt(vp->rx_ring[entry].addr),
-					     pkt_len);
-				rx_copy++;
-			} else {
-				void *temp;
-				/* Pass up the skbuff already on the Rx ring. */
-				skb = vp->rx_skbuff[entry];
-				vp->rx_skbuff[entry] = NULL;
-				temp = skb_put(skb, pkt_len);
-				/* Remove this checking code for final release. */
-				if (isa_bus_to_virt(vp->rx_ring[entry].addr) != temp)
-					pr_warn("%s: Warning -- the skbuff addresses do not match in boomerang_rx: %p vs. %p / %p\n",
-						dev->name,
-						isa_bus_to_virt(vp->rx_ring[entry].addr),
-						skb->head, temp);
-				rx_nocopy++;
-			}
-			skb->protocol = eth_type_trans(skb, dev);
-			netif_rx(skb);
-			dev->stats.rx_packets++;
-		}
-		entry = (++vp->cur_rx) % RX_RING_SIZE;
-	}
-	/* Refill the Rx ring buffers. */
-	for (; vp->cur_rx - vp->dirty_rx > 0; vp->dirty_rx++) {
-		struct sk_buff *skb;
-		entry = vp->dirty_rx % RX_RING_SIZE;
-		if (vp->rx_skbuff[entry] == NULL) {
-			skb = netdev_alloc_skb(dev, PKT_BUF_SZ);
-			if (skb == NULL)
-				break;	/* Bad news!  */
-			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
-			vp->rx_ring[entry].addr = isa_virt_to_bus(skb->data);
-			vp->rx_skbuff[entry] = skb;
-		}
-		vp->rx_ring[entry].status = 0;	/* Clear complete bit. */
-	}
-	return 0;
-}
-
-static int corkscrew_close(struct net_device *dev)
-{
-	struct corkscrew_private *vp = netdev_priv(dev);
-	int ioaddr = dev->base_addr;
-	int i;
-
-	netif_stop_queue(dev);
-
-	if (corkscrew_debug > 1) {
-		pr_debug("%s: corkscrew_close() status %4.4x, Tx status %2.2x.\n",
-		     dev->name, inw(ioaddr + EL3_STATUS),
-		     inb(ioaddr + TxStatus));
-		pr_debug("%s: corkscrew close stats: rx_nocopy %d rx_copy %d tx_queued %d.\n",
-			dev->name, rx_nocopy, rx_copy, queued_packet);
-	}
-
-	timer_delete_sync(&vp->timer);
-
-	/* Turn off statistics ASAP.  We update lp->stats below. */
-	outw(StatsDisable, ioaddr + EL3_CMD);
-
-	/* Disable the receiver and transmitter. */
-	outw(RxDisable, ioaddr + EL3_CMD);
-	outw(TxDisable, ioaddr + EL3_CMD);
-
-	if (dev->if_port == XCVR_10base2)
-		/* Turn off thinnet power.  Green! */
-		outw(StopCoax, ioaddr + EL3_CMD);
-
-	free_irq(dev->irq, dev);
-
-	outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD);
-
-	update_stats(ioaddr, dev);
-	if (vp->full_bus_master_rx) {	/* Free Boomerang bus master Rx buffers. */
-		outl(0, ioaddr + UpListPtr);
-		for (i = 0; i < RX_RING_SIZE; i++)
-			if (vp->rx_skbuff[i]) {
-				dev_kfree_skb(vp->rx_skbuff[i]);
-				vp->rx_skbuff[i] = NULL;
-			}
-	}
-	if (vp->full_bus_master_tx) {	/* Free Boomerang bus master Tx buffers. */
-		outl(0, ioaddr + DownListPtr);
-		for (i = 0; i < TX_RING_SIZE; i++)
-			if (vp->tx_skbuff[i]) {
-				dev_kfree_skb(vp->tx_skbuff[i]);
-				vp->tx_skbuff[i] = NULL;
-			}
-	}
-
-	return 0;
-}
-
-static struct net_device_stats *corkscrew_get_stats(struct net_device *dev)
-{
-	struct corkscrew_private *vp = netdev_priv(dev);
-	unsigned long flags;
-
-	if (netif_running(dev)) {
-		spin_lock_irqsave(&vp->lock, flags);
-		update_stats(dev->base_addr, dev);
-		spin_unlock_irqrestore(&vp->lock, flags);
-	}
-	return &dev->stats;
-}
-
-/*  Update statistics.
-	Unlike with the EL3 we need not worry about interrupts changing
-	the window setting from underneath us, but we must still guard
-	against a race condition with a StatsUpdate interrupt updating the
-	table.  This is done by checking that the ASM (!) code generated uses
-	atomic updates with '+='.
-	*/
-static void update_stats(int ioaddr, struct net_device *dev)
-{
-	/* Unlike the 3c5x9 we need not turn off stats updates while reading. */
-	/* Switch to the stats window, and read everything. */
-	EL3WINDOW(6);
-	dev->stats.tx_carrier_errors += inb(ioaddr + 0);
-	dev->stats.tx_heartbeat_errors += inb(ioaddr + 1);
-	/* Multiple collisions. */ inb(ioaddr + 2);
-	dev->stats.collisions += inb(ioaddr + 3);
-	dev->stats.tx_window_errors += inb(ioaddr + 4);
-	dev->stats.rx_fifo_errors += inb(ioaddr + 5);
-	dev->stats.tx_packets += inb(ioaddr + 6);
-	dev->stats.tx_packets += (inb(ioaddr + 9) & 0x30) << 4;
-						/* Rx packets   */ inb(ioaddr + 7);
-						/* Must read to clear */
-	/* Tx deferrals */ inb(ioaddr + 8);
-	/* Don't bother with register 9, an extension of registers 6&7.
-	   If we do use the 6&7 values the atomic update assumption above
-	   is invalid. */
-	inw(ioaddr + 10);	/* Total Rx and Tx octets. */
-	inw(ioaddr + 12);
-	/* New: On the Vortex we must also clear the BadSSD counter. */
-	EL3WINDOW(4);
-	inb(ioaddr + 12);
-
-	/* We change back to window 7 (not 1) with the Vortex. */
-	EL3WINDOW(7);
-}
-
-/* This new version of set_rx_mode() supports v1.4 kernels.
-   The Vortex chip has no documented multicast filter, so the only
-   multicast setting is to receive all multicast frames.  At least
-   the chip has a very clean way to set the mode, unlike many others. */
-static void set_rx_mode(struct net_device *dev)
-{
-	int ioaddr = dev->base_addr;
-	unsigned short new_mode;
-
-	if (dev->flags & IFF_PROMISC) {
-		if (corkscrew_debug > 3)
-			pr_debug("%s: Setting promiscuous mode.\n",
-			       dev->name);
-		new_mode = SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm;
-	} else if (!netdev_mc_empty(dev) || dev->flags & IFF_ALLMULTI) {
-		new_mode = SetRxFilter | RxStation | RxMulticast | RxBroadcast;
-	} else
-		new_mode = SetRxFilter | RxStation | RxBroadcast;
-
-	outw(new_mode, ioaddr + EL3_CMD);
-}
-
-static void netdev_get_drvinfo(struct net_device *dev,
-			       struct ethtool_drvinfo *info)
-{
-	strscpy(info->driver, DRV_NAME, sizeof(info->driver));
-	snprintf(info->bus_info, sizeof(info->bus_info), "ISA 0x%lx",
-		 dev->base_addr);
-}
-
-static u32 netdev_get_msglevel(struct net_device *dev)
-{
-	return corkscrew_debug;
-}
-
-static void netdev_set_msglevel(struct net_device *dev, u32 level)
-{
-	corkscrew_debug = level;
-}
-
-static const struct ethtool_ops netdev_ethtool_ops = {
-	.get_drvinfo		= netdev_get_drvinfo,
-	.get_msglevel		= netdev_get_msglevel,
-	.set_msglevel		= netdev_set_msglevel,
-};
-
-#ifdef MODULE
-static void __exit corkscrew_exit_module(void)
-{
-	while (!list_empty(&root_corkscrew_dev)) {
-		struct net_device *dev;
-		struct corkscrew_private *vp;
-
-		vp = list_entry(root_corkscrew_dev.next,
-				struct corkscrew_private, list);
-		dev = vp->our_dev;
-		unregister_netdev(dev);
-		cleanup_card(dev);
-		free_netdev(dev);
-	}
-}
-module_exit(corkscrew_exit_module);
-#endif				/* MODULE */
diff --git a/drivers/net/ethernet/3com/Kconfig b/drivers/net/ethernet/3com/Kconfig
index c05a1b63c1c9..3fd3202d9776 100644
--- a/drivers/net/ethernet/3com/Kconfig
+++ b/drivers/net/ethernet/3com/Kconfig
@@ -17,17 +17,6 @@ config NET_VENDOR_3COM
 
 if NET_VENDOR_3COM
 
-config 3C515
-	tristate "3c515 ISA \"Fast EtherLink\""
-	depends on ISA && ISA_DMA_API && !PPC32
-	select NETDEV_LEGACY_INIT
-	help
-	  If you have a 3Com ISA EtherLink XL "Corkscrew" 3c515 Fast Ethernet
-	  network card, say Y here.
-
-	  To compile this driver as a module, choose M here. The module
-	  will be called 3c515.
-
 config PCMCIA_3C574
 	tristate "3Com 3c574 PCMCIA support"
 	depends on PCMCIA && HAS_IOPORT
diff --git a/drivers/net/ethernet/3com/Makefile b/drivers/net/ethernet/3com/Makefile
index f7623fa2d441..babfd93d5d53 100644
--- a/drivers/net/ethernet/3com/Makefile
+++ b/drivers/net/ethernet/3com/Makefile
@@ -3,7 +3,6 @@
 # Makefile for the 3Com Ethernet device drivers
 #
 
-obj-$(CONFIG_3C515) += 3c515.o
 obj-$(CONFIG_PCMCIA_3C589) += 3c589_cs.o
 obj-$(CONFIG_PCMCIA_3C574) += 3c574_cs.o
 obj-$(CONFIG_VORTEX) += 3c59x.o

-- 
2.53.0


^ permalink raw reply related

* Re: [PATCH iwl-net v1] ice: fix UAF/NULL deref when VSI rebuild and XDP attach race
From: Simon Horman @ 2026-04-22 18:00 UTC (permalink / raw)
  To: Kohei Enju
  Cc: intel-wired-lan, netdev, Tony Nguyen, Przemek Kitszel,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Wojciech Drewek, Jacob Keller, Larysa Zaremba,
	Maciej Fijalkowski
In-Reply-To: <20260418090137.411506-1-kohei@enjuk.jp>

On Sat, Apr 18, 2026 at 09:01:15AM +0000, Kohei Enju wrote:
> ice_xdp_setup_prog() unconditionally hot-swaps xdp_prog when
> ICE_VSI_REBUILD_PENDING is set. In the attach path, this can publish a
> new rx_ring->xdp_prog before rx_ring->xdp_ring becomes valid while the
> rebuild is pending. As a result, ice_clean_rx_irq() may dereference
> rx_ring->xdp_ring too early.
> 
> With high-volume RX packets, running these commands in parallel
> triggered a KASAN splat [1].
>  # ethtool --reset $DEV irq dma filter offload
>  # ip link set dev $DEV xdp {obj $OBJ sec xdp,off}
> 
> Fix this by rejecting XDP attach while rebuild is pending.
> Keep XDP detach allowed in this window. Detach clears rx_ring->xdp_prog,
> so the RX path will not attempt to access rx_ring->xdp_ring.
> 
> [1]
> BUG: KASAN: slab-use-after-free in ice_napi_poll+0x3921/0x41a0
> Read of size 2 at addr ffff88812475b880 by task ksoftirqd/1/23
> [...]
> Call Trace:
>  <TASK>
>  ice_napi_poll+0x3921/0x41a0
>  __napi_poll+0x98/0x520
>  net_rx_action+0x8f2/0xfa0
>  handle_softirqs+0x1cb/0x7f0
> [...]
>  </TASK>
> 
> Allocated by task 7246:
>  ice_prepare_xdp_rings+0x3de/0x12d0
>  ice_xdp+0x61c/0xef0
>  dev_xdp_install+0x3c4/0x840
>  dev_xdp_attach+0x50a/0x10a0
>  dev_change_xdp_fd+0x175/0x210
> [...]
> 
> Freed by task 7251:
>  __rcu_free_sheaf_prepare+0x5f/0x230
>  rcu_free_sheaf+0x1a/0xf0
>  rcu_core+0x567/0x1d80
>  handle_softirqs+0x1cb/0x7f0
> 
> Fixes: 2504b8405768 ("ice: protect XDP configuration with a mutex")
> Signed-off-by: Kohei Enju <kohei@enjuk.jp>

Reviewed-by: Simon Horman <horms@kernel.org>

Sashiko has provided some feedback on this patch.
However, I believe the issues it raises are not introduced
by this patch and should not block progress of it.
I'd like to ask if you could take a look over that feedback
and see if any follow-up is appropriate.

Thanks!

^ permalink raw reply

* Re: [net-next v38] mctp pcc: Implement MCTP over PCC Transport
From: Adam Young @ 2026-04-22 17:17 UTC (permalink / raw)
  To: Jakub Kicinski, admiyo
  Cc: jk, matt, andrew+netdev, davem, edumazet, pabeni, netdev,
	linux-kernel, sudeep.holla, Jonathan.Cameron, lihuisong
In-Reply-To: <20260410032441.1844450-1-kuba@kernel.org>


On 4/9/26 23:24, Jakub Kicinski wrote:
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> ---
> mctp pcc: Implement MCTP over PCC Transport
>
> This commit implements a network driver for the Management Component
> Transport Protocol (MCTP) over the Platform Communication Channel (PCC).
>
> Link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0292_1.0.0WIP50.pdf
> Link: https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/14_Platform_Communications_Channel/Platform_Comm_Channel.html
>
>> diff --git a/drivers/net/mctp/mctp-pcc.c b/drivers/net/mctp/mctp-pcc.c
>> new file mode 100644
>> --- /dev/null
>> +++ b/drivers/net/mctp/mctp-pcc.c
> [ ... ]
>
>> +static void mctp_pcc_client_rx_callback(struct mbox_client *cl, void *mssg)
>> +{
> [ ... ]
>
>> +	// The message must at least have the PCC command indicating it is an MCTP
>> +	// message followed by the MCTP header, or we have a malformed message.
>> +	if (pcc_header.length < sizeof(pcc_header.command) + sizeof(struct mctp_hdr)) {
>> +		dev_dstats_rx_dropped(mctp_pcc_ndev->ndev);
>> +		return;
>> +	}
>> +
>> +	size = pcc_header.length - sizeof(pcc_header.command) + sizeof(pcc_header);
> Can this size calculation wrap around? Because pcc_header.length is read from
> shared memory, a maliciously large value (e.g. 0xFFFFFFF8) could wrap around
> the unsigned 32-bit addition, resulting in a small positive size.
>
> This small positive size would then bypass the check below, and eventually
> fail in skb_pull(), leaving the malformed skb to be passed to the network
> stack.

This seems legit.  The logic to check the size should ensure that the 
value pulled out of the shared buffer is the size of the buffer, minus 
the header.


>
>> +	// If the reported size is larger than the shared memory, something is wrong
>> +	// and the best we can do is treat it as corrupted data.
>> +	if (size > inbox->chan->shmem_size) {
>> +		dev_dstats_rx_dropped(mctp_pcc_ndev->ndev);
>> +		return;
>> +	}
> [ ... ]
>
>> +static netdev_tx_t mctp_pcc_tx(struct sk_buff *skb, struct net_device *ndev)
>> +{
> [ ... ]
>
>> +	pcc_header = skb_push(skb, sizeof(*pcc_header));
>> +	pcc_header->signature = PCC_SIGNATURE | mpnd->outbox.index;
>> +	pcc_header->flags = PCC_CMD_COMPLETION_NOTIFY;
>> +	memcpy(&pcc_header->command, MCTP_SIGNATURE, MCTP_SIGNATURE_LENGTH);
>> +	pcc_header->length = len + MCTP_SIGNATURE_LENGTH;
> Are these assignments writing native endian values into shared memory?
>
> The ACPI Platform Communications Channel specification defines the shared
> memory region as strictly little-endian. Writing without cpu_to_le32() will
> cause big endian architectures to write byte-swapped metadata, resulting in
> dropped packets.

This looks legit, and it reflects some of the early version of my patch 
where I was doing similar transforms.

The target for this code is only ARM little endian systems, but I think 
it is more correct to handle the wider cases in case that decision changes.


>
>> +
>> +	rc = mbox_send_message(mpnd->outbox.chan->mchan, skb);
>> +	if (rc < 0) {
> [ ... ]
>
>> +static void mctp_pcc_tx_prepare(struct mbox_client *cl, void *mssg)
>> +{
> [ ... ]
>
>> +	if (skb->len > outbox->chan->shmem_size) {
>> +		dev_dstats_tx_dropped(mctp_pcc_ndev->ndev);
>> +		return;
>> +	}
> If the skb is oversized, this skips the copy and returns, but does it abort
> the transmission?
>
> Since mctp_pcc_tx() already submitted the message and tx_prepare() returns
> void, the hardware doorbell will still be rung. The remote endpoint would
> process the unmodified shared memory, replaying the previous packet's data.
This looks legit.  Once we are in tx_prepare the message is going to be 
sent. All checks like this need to be done in tx
>
>> +	memcpy_toio(outbox->chan->shmem,  skb->data, skb->len);
> Is it possible to read out of bounds here if the skb is fragmented?
>
> The skb->data pointer only points to the linear portion of the packet, while
> skb->len represents the total packet length including page fragments.
> skb_cow_head() does not linearize the packet, so a call to skb_linearize()
> might be needed before copying.

Legit.

While we don't have fragmentation enabled right now, it could very well 
be enabled in the future, and this should be addressed.


>
>> +}
>> +
>> +static void mctp_pcc_tx_done(struct mbox_client *c, void *mssg, int r)
>> +{
>> +	struct mctp_pcc_ndev *mctp_pcc_ndev;
>> +	struct sk_buff *skb = mssg;
>> +
>> +	mctp_pcc_ndev = container_of(c, struct mctp_pcc_ndev, outbox.client);
>> +	dev_dstats_tx_add(mctp_pcc_ndev->ndev, skb->len);
> Could this lead to sequence counter corruption and deadlock?
>
> mctp_pcc_tx() runs in process or softirq context and calls
> dev_dstats_tx_dropped(). mctp_pcc_tx_done() runs in the controller's hardirq
> context and calls dev_dstats_tx_add().
>
> The generic dev_dstats_* macros use u64_stats_update_begin(), which does not
> disable interrupts. If a hardware interrupt fires while the softirq path is
> inside u64_stats_update_begin(), the hardirq path will re-enter the lock.

I think this is legit. mctp_pcc_tx_done is triggered by the interupt 
handler (it is the registered call back). While I do prefer to register 
the packet as being sent at this point, I think that latest it can be 
done is after the packet is written to the shared region in 
mctp_pcc_tx_prepare.


>
>> +	dev_consume_skb_any(skb);
>> +	netif_wake_queue(mctp_pcc_ndev->ndev);
>> +}
> [ ... ]
>
>> +static int mctp_pcc_ndo_stop(struct net_device *ndev)
>> +{
>> +	struct mctp_pcc_ndev *mctp_pcc_ndev;
>> +	unsigned int count, idx;
>> +	struct mbox_chan *chan;
>> +	struct sk_buff *skb;
>> +
>> +	mctp_pcc_ndev = netdev_priv(ndev);
>> +	chan = mctp_pcc_ndev->outbox.chan->mchan;
>> +
>> +	scoped_guard(spinlock_irqsave, &chan->lock) {
>> +		chan->active_req = NULL;
> Does this bypass the mailbox API and cause a memory leak?
>
> By explicitly setting active_req to NULL without freeing the in-flight skb,
> the skb is leaked.
>
> Additionally, when the hardware completion interrupt fires, mbox_chan_txdone()
> will pass NULL as the mssg parameter to mctp_pcc_tx_done(). Since
> mctp_pcc_tx_done() dereferences skb->len, this will result in a NULL pointer
> dereference.

Legit.  I was thinking that the packet pointed to by chan->active_req 
was still in the ring buffer, but it has already been removed.


>
>> +		while (chan->msg_count > 0) {
>> +			count = chan->msg_count;
>> +			idx = chan->msg_free;
>> +			if (idx >= count)
>> +				idx -= count;
>> +			else
>> +				idx += MBOX_TX_QUEUE_LEN - count;
> Is this index calculation accurate?
>
> Since msg_free is incremented and msg_count is decremented in each iteration
> below, the calculated idx jumps by 2. This skips alternate skbs, leaking
> them, and reads uninitialized memory, which is then passed to
> dev_consume_skb_any(), causing a use-after-free or double free.

I don't think this is legit.  I've written code to test this algorithm, 
and it works as written.  This logic comes from the 
drivers/mailbox/mailbox.c function msg_submit.

  idx does not jump by two each iteration.

>
>> +			skb = chan->msg_data[idx];
>> +			dev_dstats_tx_dropped(ndev);
>> +			dev_consume_skb_any(skb);
>> +			chan->msg_count--;
>> +			chan->msg_free++;
>> +		}
>> +	}
> [ ... ]
>
>> +static int mctp_pcc_driver_add(struct acpi_device *acpi_dev)
>> +{
>> +	struct mctp_pcc_lookup_context context = {0};
>> +	struct mctp_pcc_ndev *mctp_pcc_ndev;
> [ ... ]
>
>> +	status = acpi_walk_resources(dev_handle, "_CRS", lookup_pcct_indices,
>> +				     &context);
>> +	if (!ACPI_SUCCESS(status)) {
>> +		dev_err(dev, "FAILED to lookup PCC indexes from CRS\n");
>> +		return -EINVAL;
>> +	}
> What happens if the _CRS table contains fewer than two PCC_DWORD_TYPE
> resources?
>
> If context.index is not verified to be at least 2 here, context.inbox_index
> and context.outbox_index might remain uninitialized or at their default zero
> values. This could cause the driver to blindly request PCC channel 0, which
> may belong to another hardware subsystem.

If that is the case then things are truely corrupted on the system.  So, 
yeah, this is legit, but a sign of things much worse happening.

It is easy enough to check that we get 2 values in the context from walk 
resources.


>
>> +
>> +	snprintf(name, sizeof(name), "mctppcc%d", context.inbox_index);
>> +	ndev = alloc_netdev(sizeof(*mctp_pcc_ndev), name, NET_NAME_PREDICTABLE,
>> +			    mctp_pcc_setup);

^ permalink raw reply

* Re: [PATCH] net: dsa: realtek: rtl8365mb: add support for RTL8367SB
From: Breno Leitao @ 2026-04-22 17:12 UTC (permalink / raw)
  To: Mieczyslaw Nalewaj; +Cc: netdev@vger.kernel.org
In-Reply-To: <f5ac420f-0087-4af0-bb43-b9a5b6228fbd@yahoo.com>

On Wed, Apr 22, 2026 at 05:58:30PM +0200, Mieczyslaw Nalewaj wrote:
> Add chip info entry for the Realtek RTL8367SB switch. This device has
> chip ID 0x6367 and version 0x0010. It exposes two external interfaces:
> port 6 supports SGMII and HSGMII, while port 7 supports MII, TMII,
> RMII and RGMII. Use the existing 8365MB-VC jam table for initialization.

This should target net-next, which is currently closed. Additionally, it
appears the maintainers were not properly copied on this patch?!

https://lore.kernel.org/all/20260412142250.131bf997@kernel.org/

pw-bot: cr

^ permalink raw reply

* Re: [PATCH net 0/6] net/ncsi: harden packet parsing against malformed BMC replies
From: Michael Bommarito @ 2026-04-22 17:06 UTC (permalink / raw)
  To: Paul Fertser
  Cc: Samuel Mendoza-Jonas, netdev, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, linux-kernel
In-Reply-To: <aej66UTWzQ74dven@home.paul.comp>

On Wed, Apr 22, 2026 at 12:44 PM Paul Fertser <fercerpav@gmail.com> wrote:
> The code your patches modify always runs only on the BMC itself, the
> packets parsed are generated by a NIC directly.
>
> So if anything, the threat model here is compromised NIC
> firmware. MITMing sounds unlikely as that would require tricky
> hardware modifications and if you can do that it's easier to put a
> modified NIC instead.

Thanks for the correction, I definitely got that backwards!

Should I submit a v2 with the corrected notes or wait for any other
feedback / review on patch substance (assuming you think it's actually
worth merging)?

Thanks,
Mike Bommarito

^ permalink raw reply

* Re: [PATCH net v3 1/2] bnge: fix initial HWRM sequence
From: Simon Horman @ 2026-04-22 17:06 UTC (permalink / raw)
  To: vikas.gupta
  Cc: 'Simon Horman', davem, edumazet, kuba, pabeni,
	andrew+netdev, netdev, linux-kernel, vsrama-krishna.nemani,
	bhargava.marreddy, rajashekar.hudumula, ajit.khaparde,
	dharmender.garg, rahul-rg.gupta
In-Reply-To: <20260418023438.1597876-2-vikas.gupta@broadcom.com>

From: 'Simon Horman' <horms@kernel.org>

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://sashiko.dev
---
bnge: fix initial HWRM sequence

Firmware may not advertize correct resources if backing store is not
enabled before resource information is queried.
Fix the initial sequence of HWRMs so that driver gets capabilities
and resource information correctly.

> diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_core.c b/drivers/net/ethernet/broadcom/bnge/bnge_core.c
> index 1c14c5fe8d613..68b74eb2c3a29 100644
> --- a/drivers/net/ethernet/broadcom/bnge/bnge_core.c
> +++ b/drivers/net/ethernet/broadcom/bnge/bnge_core.c
[ ... ]
> @@ -133,23 +140,28 @@ static int bnge_fw_register_dev(struct bnge_dev *bd)
>  
>  	bnge_hwrm_fw_set_time(bd);
>  
> -	rc =  bnge_hwrm_func_drv_rgtr(bd);
> +	/* Get the resources and configuration from firmware */
> +	rc = bnge_func_qcaps(bd);
>  	if (rc) {
> -		dev_err(bd->dev, "Failed to rgtr with firmware rc: %d\n", rc);
> +		dev_err(bd->dev, "Failed querying caps rc: %d\n", rc);
>  		return rc;
>  	}
>  
>  	rc = bnge_alloc_ctx_mem(bd);
>  	if (rc) {
>  		dev_err(bd->dev, "Failed to allocate ctx mem rc: %d\n", rc);
> -		goto err_func_unrgtr;
> +		goto err_free_ctx_mem;
>  	}
>  
> -	/* Get the resources and configuration from firmware */
> -	rc = bnge_func_qcaps(bd);
> +	rc = bnge_hwrm_func_drv_rgtr(bd);
>  	if (rc) {
> -		dev_err(bd->dev, "Failed initial configuration rc: %d\n", rc);
> -		rc = -ENODEV;
> +		dev_err(bd->dev, "Failed to rgtr with firmware rc: %d\n", rc);
> +		goto err_free_ctx_mem;

Does this regression leave the hardware with active DMA pointers to freed
memory?

If bnge_alloc_ctx_mem() allocates host DMA memory and passes the addresses
to the firmware via bnge_hwrm_func_backing_store(), and then
bnge_hwrm_func_drv_rgtr() fails, the code jumps directly to err_free_ctx_mem.

> +	}
> +
> +	rc = bnge_func_qrcaps_qcfg(bd);
> +	if (rc) {
> +		dev_err(bd->dev, "Failed querying resources rc: %d\n", rc);
>  		goto err_func_unrgtr;
>  	}
>  
> @@ -158,7 +170,9 @@ static int bnge_fw_register_dev(struct bnge_dev *bd)
>  	return 0;
>  
>  err_func_unrgtr:
> -	bnge_fw_unregister_dev(bd);
> +	bnge_hwrm_func_drv_unrgtr(bd);
> +err_free_ctx_mem:
> +	bnge_free_ctx_mem(bd);

Since the err_func_unrgtr label is bypassed, is the firmware ever told to
drop the backing store configuration before the host frees the memory in
bnge_free_ctx_mem()?

In the pre-patch sequence, the driver was registered first, so any failure
subsequently triggered a driver unregister, which implicitly told the
firmware to drop the backing store configuration. With the new order, does
the firmware maintain active DMA pointers to the freed host memory?

^ permalink raw reply

* Re: [PATCH v2] mptcp: do not drop partial packets
From: Matthieu Baerts @ 2026-04-22 17:03 UTC (permalink / raw)
  To: Shardul Bankar, pabeni, martineau
  Cc: geliang, davem, edumazet, kuba, horms, netdev, mptcp,
	linux-kernel, janak, kalpan.jani, Shardul Bankar
In-Reply-To: <20260422143931.43281-1-shardul.b@mpiricsoftware.com>

Hi Shardul,

On 22/04/2026 16:39, Shardul Bankar wrote:
> When a packet arrives with map_seq < ack_seq < end_seq, the beginning
> of the packet has already been acknowledged but the end contains new
> data.  Currently the entire packet is dropped as "old data," forcing
> the sender to retransmit.
> 
> Instead, skip the already-acked bytes by adjusting the skb offset and
> enqueue only the new portion.  Update bytes_received and ack_seq to
> reflect the new data consumed.

Thank you for the v2.

I didn't review it (yet), but just to let you know that there are some
rules on Netdev [1] (that usually also applied on MPTCP side), and an
important one is:

 - don't repost your patches within one 24h period

Each version generates a lot of emails that are sent and need to be
triaged. With the high volume, it is then harder for us to follow, plus
a lot of shared resources are used, etc.

One last thing, because this patch is not an urgent fix, do you mind
sending new versions only the to MPTCP ML: to a restricted number of
people for the first versions, there is enough traffic on Netdev.

[1] https://docs.kernel.org/process/maintainer-netdev.html

> A previous attempt at this fix (commit 1d2ce718811a ("mptcp: do not
> drop partial packets"), reverted in commit bf39160c4218 ("Revert

Note: these two commits should not be mentioned here, they have only
been applied to the MPTCP tree, but not upstreamed. Instead, please use
lore links, e.g.

https://lore.kernel.org/c9b426a4e163aa3c4fe8b80c79f1a610f47ae7d8.1763075056.git.pabeni@redhat.com

> "mptcp: do not drop partial packets"")) also added a zero-window
> check and changed rcv_wnd_sent initialization, which caused test
> regressions.  This version addresses only the partial packet handling
> without modifying receive window accounting.
> 
> Fixes: ab174ad8ef76 ("mptcp: move ooo skbs into msk out of order queue.")
> Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/600
> Signed-off-by: Shardul Bankar <shardul.b@mpiricsoftware.com>

Checkpatch is complaining that the author of the patch is not the same
as the one who sent the patch; You probably need to run this command to
fix it:

  git commit --amend --reset-author

(when the patch is sent, `git format-patch` will add a second From: tag)

Cheers,
Matt
-- 
Sponsored by the NGI0 Core fund.


^ permalink raw reply

* Re: [PATCH net v3 8/8] xsk: don't support AF_XDP on 32-bit architectures
From: Alexander Lobakin @ 2026-04-22 17:00 UTC (permalink / raw)
  To: Jason Xing
  Cc: davem, edumazet, kuba, pabeni, bjorn, magnus.karlsson,
	maciej.fijalkowski, jonathan.lemon, sdf, ast, daniel, hawk,
	john.fastabend, bpf, netdev, Jason Xing
In-Reply-To: <CAL+tcoDTXEsehprDHeO7P1pcqwu_US3Li-dm_YyGDAwu2aYZvw@mail.gmail.com>

From: Jason Xing <kerneljasonxing@gmail.com>
Date: Thu, 23 Apr 2026 00:37:07 +0800

> On Thu, Apr 23, 2026 at 12:10 AM Alexander Lobakin
> <aleksander.lobakin@intel.com> wrote:
>>
>> From: Jason Xing <kerneljasonxing@gmail.com>
>> Date: Wed, 22 Apr 2026 11:36:50 +0800
>>
>>> From: Jason Xing <kernelxing@tencent.com>
>>>
>>> In copy mode TX, xsk_skb_destructor_set_addr() stores the 64-bit
>>> descriptor address into skb_shinfo(skb)->destructor_arg (void *) via a
>>> uintptr_t cast:
>>>
>>>     skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);
>>>
>>> On 32-bit architectures uintptr_t is 32 bits, so the upper 32 bits of
>>> the descriptor address are silently dropped. In XDP_ZEROCOPY unaligned
>>> mode the chunk offset is encoded in bits 48-63 of the descriptor
>>> address (XSK_UNALIGNED_BUF_OFFSET_SHIFT = 48), meaning the offset is
>>> lost entirely. The completion queue then returns a truncated address to
>>> userspace, making buffer recycling impossible.
>>
>> What if we relax the restriction a bit? For example, refuse to configure
> 
> As to the bug itself, yes, It only affects the unaligned mode.
> 
> I wonder if we can support this after someone requires us to support
> 32-bit arch and use it in the real world, then we can use the previous
> patch to complete the full support (which doesn't harm the path on
> 64-bit arch).
> 
> The code looks like this based on your suggestion. Just for the record.
> diff --git a/net/xdp/xdp_umem.c b/net/xdp/xdp_umem.c
> index 58da2f4f4397..03417b04592f 100644
> --- a/net/xdp/xdp_umem.c
> +++ b/net/xdp/xdp_umem.c
> @@ -177,6 +177,9 @@ static int xdp_umem_reg(struct xdp_umem *umem,
> struct xdp_umem_reg *mr)
> if (mr->flags & ~XDP_UMEM_FLAGS_VALID)
> return -EINVAL;
> + if (!IS_ENABLED(CONFIG_64BIT) && unaligned_chunks)
> + return -EOPNOTSUPP;

I'm fine with this one, as well as with the check I proposed. This one
would happen during the configuration so it won't affect the hotpath,
my check is just inspired by what Page Pool does when
`sizeof(dma_addr_t) > sizeof(unsigned long)` (rare case but perfectly
valid on some architectures).

> +
> if (!unaligned_chunks && !is_power_of_2(chunk_size))
> return -EINVAL;
> 
> Actually I'm fine with either of them. Right now I'm not so sure which
> direction this patch should take :)

Up to you and other reviewers, but I really do think cutting off 32-bit
arches entirely due to a bug which can happen only under certain
conditions (skb XSk mode + unaligned mode) is too aggressive.

> 
> Thanks,
> Jason
> 
>> an XSk socket in unaligned mode if on a 32-bit arch? Or add a check
>> under CONFIG_32_BIT like it was done in Page Pool:
>>
>> skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);
>>
>> #ifdef CONFIG_32BIT
>> if (((uintptr_t)skb_shinfo(skb)->destructor_arg) & ~0x1UL) != addr)
>>         // WARN_ONCE or whatever + error path
>> #endif
>>
>> I never used XSk on a 32-bit arch, but back when I was working on 32-bit
>> MIPS 1G routers, I wanted to add native XSk support to the Eth driver.
>> Sure, just for fun, now that we have cheap AArch64 and other 64-bit
>> embedded chips, 32-bit embedded networking SoCs are almost dead, but
>> OTOH, as you can see, other subsystems like PP still try to support 32 bit.
>> Especially given that this issue applies to only to the skb XSk path,
>> not native in-driver implementations.
>>
>>>
>>> Since we hear no one is using AF_XDP on 32-bit arch, we decided to
>>> strictly stop supporting it at compile time.
Thanks,
Olek

^ permalink raw reply

* Re: [PATCH net v3 8/8] xsk: don't support AF_XDP on 32-bit architectures
From: Maciej Fijalkowski @ 2026-04-22 16:58 UTC (permalink / raw)
  To: Jason Xing
  Cc: Alexander Lobakin, davem, edumazet, kuba, pabeni, bjorn,
	magnus.karlsson, jonathan.lemon, sdf, ast, daniel, hawk,
	john.fastabend, bpf, netdev, Jason Xing
In-Reply-To: <CAL+tcoDTXEsehprDHeO7P1pcqwu_US3Li-dm_YyGDAwu2aYZvw@mail.gmail.com>

On Thu, Apr 23, 2026 at 12:37:07AM +0800, Jason Xing wrote:
> On Thu, Apr 23, 2026 at 12:10 AM Alexander Lobakin
> <aleksander.lobakin@intel.com> wrote:
> >
> > From: Jason Xing <kerneljasonxing@gmail.com>
> > Date: Wed, 22 Apr 2026 11:36:50 +0800
> >
> > > From: Jason Xing <kernelxing@tencent.com>
> > >
> > > In copy mode TX, xsk_skb_destructor_set_addr() stores the 64-bit
> > > descriptor address into skb_shinfo(skb)->destructor_arg (void *) via a
> > > uintptr_t cast:
> > >
> > >     skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);
> > >
> > > On 32-bit architectures uintptr_t is 32 bits, so the upper 32 bits of
> > > the descriptor address are silently dropped. In XDP_ZEROCOPY unaligned
> > > mode the chunk offset is encoded in bits 48-63 of the descriptor
> > > address (XSK_UNALIGNED_BUF_OFFSET_SHIFT = 48), meaning the offset is
> > > lost entirely. The completion queue then returns a truncated address to
> > > userspace, making buffer recycling impossible.
> >
> > What if we relax the restriction a bit? For example, refuse to configure
> 
> As to the bug itself, yes, It only affects the unaligned mode.
> 
> I wonder if we can support this after someone requires us to support
> 32-bit arch and use it in the real world, then we can use the previous
> patch to complete the full support (which doesn't harm the path on
> 64-bit arch).
> 
> The code looks like this based on your suggestion. Just for the record.
> diff --git a/net/xdp/xdp_umem.c b/net/xdp/xdp_umem.c
> index 58da2f4f4397..03417b04592f 100644
> --- a/net/xdp/xdp_umem.c
> +++ b/net/xdp/xdp_umem.c
> @@ -177,6 +177,9 @@ static int xdp_umem_reg(struct xdp_umem *umem,
> struct xdp_umem_reg *mr)
> if (mr->flags & ~XDP_UMEM_FLAGS_VALID)
> return -EINVAL;
> + if (!IS_ENABLED(CONFIG_64BIT) && unaligned_chunks)
> + return -EOPNOTSUPP;
> +
> if (!unaligned_chunks && !is_power_of_2(chunk_size))
> return -EINVAL;
> 
> Actually I'm fine with either of them. Right now I'm not so sure which
> direction this patch should take :)

Abstracting from discussion, that is not a patch we would want against
-net. We should not forbid 32bit on stable kernels, maybe there is someone
in the 'basement' using 32bit xsk on stable kernel. Plus the fixes tag vs
patch's content looks weird ;)

> 
> Thanks,
> Jason
> 
> > an XSk socket in unaligned mode if on a 32-bit arch? Or add a check
> > under CONFIG_32_BIT like it was done in Page Pool:
> >
> > skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);
> >
> > #ifdef CONFIG_32BIT
> > if (((uintptr_t)skb_shinfo(skb)->destructor_arg) & ~0x1UL) != addr)
> >         // WARN_ONCE or whatever + error path
> > #endif
> >
> > I never used XSk on a 32-bit arch, but back when I was working on 32-bit
> > MIPS 1G routers, I wanted to add native XSk support to the Eth driver.
> > Sure, just for fun, now that we have cheap AArch64 and other 64-bit
> > embedded chips, 32-bit embedded networking SoCs are almost dead, but
> > OTOH, as you can see, other subsystems like PP still try to support 32 bit.
> > Especially given that this issue applies to only to the skb XSk path,
> > not native in-driver implementations.
> >
> > >
> > > Since we hear no one is using AF_XDP on 32-bit arch, we decided to
> > > strictly stop supporting it at compile time.
> > >
> > > Closes: https://lore.kernel.org/all/20260419045824.D9E5EC2BCAF@smtp.kernel.org/
> > > Fixes: 0ebc27a4c67d ("xsk: avoid data corruption on cq descriptor number")
> > > Suggested-by: Stanislav Fomichev <sdf@fomichev.me>
> > > Signed-off-by: Jason Xing <kernelxing@tencent.com>
> > > ---
> > >  net/xdp/Kconfig | 2 +-
> > >  1 file changed, 1 insertion(+), 1 deletion(-)
> > >
> > > diff --git a/net/xdp/Kconfig b/net/xdp/Kconfig
> > > index 71af2febe72a..819aa5795f50 100644
> > > --- a/net/xdp/Kconfig
> > > +++ b/net/xdp/Kconfig
> > > @@ -1,7 +1,7 @@
> > >  # SPDX-License-Identifier: GPL-2.0-only
> > >  config XDP_SOCKETS
> > >       bool "XDP sockets"
> > > -     depends on BPF_SYSCALL
> > > +     depends on BPF_SYSCALL && 64BIT
> > >       default n
> > >       help
> > >         XDP sockets allows a channel between XDP programs and
> >
> > Thanks,
> > Olek

^ permalink raw reply

* Re: [PATCH iwl-net v1] igc: set tx buffer type for SMD frames
From: Simon Horman @ 2026-04-22 16:46 UTC (permalink / raw)
  To: Kohei Enju
  Cc: intel-wired-lan, netdev, Tony Nguyen, Przemek Kitszel,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Choong Yong Liang, Vladimir Oltean,
	Vinicius Costa Gomes, Chwee-Lin Choong
In-Reply-To: <20260417193223.291093-1-kohei@enjuk.jp>

On Sat, Apr 18, 2026 at 04:31:47AM +0900, Kohei Enju wrote:
> Sashiko pointed out that igc_fpe_init_smd_frame() initializes
> igc_tx_buffer fields for an SMD skb, but does not set the buffer type:
> https://sashiko.dev/#/patchset/20260415025226.114115-1-kohei%40enjuk.jp
> 
> Since igc_tx_buffer entries are reused, a stale XDP or XSK type can
> remain and make TX completion use the wrong cleanup path.
> 
> Set the buffer type to IGC_TX_BUFFER_TYPE_SKB.
> 
> Fixes: 5422570c0010 ("igc: add support for frame preemption verification")
> Signed-off-by: Kohei Enju <kohei@enjuk.jp>

Reviewed-by: Simon Horman <horms@kernel.org>


^ permalink raw reply

* Re: [PATCH net 0/6] net/ncsi: harden packet parsing against malformed BMC replies
From: Paul Fertser @ 2026-04-22 16:44 UTC (permalink / raw)
  To: Michael Bommarito
  Cc: Samuel Mendoza-Jonas, netdev, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, linux-kernel
In-Reply-To: <20260422160342.1975093-1-michael.bommarito@gmail.com>

Hello Michael,

On Wed, Apr 22, 2026 at 12:03:36PM -0400, Michael Bommarito wrote:
> NC-SI treats the management controller as privileged, but the Linux
...
> The threat model here is a compromised BMC or management-channel MITM
> on the NC-SI link.

The subject of the cover letter and the quoted fragment suggest that
you have a wrong impression of where NC-SI links exist and what they
carry, let me try to clarify.

On motherboards with BMC (the management controller) there often is a
way for the BMC (dedicated SoC these days) to talk to the
host-controlled NIC via NC-SI which is basically RMII (normally used
to talk to Ethernet PHY but here it's used to talk to a whole big NIC)
on hardware level plus special kind of frames sent in-band for
(partial) control and monitoring of the NIC. And regular frames are
transmitted over the same set of signals, there's no dedicated channel
for any kind of management inside NC-SI.

The code your patches modify always runs only on the BMC itself, the
packets parsed are generated by a NIC directly.

So if anything, the threat model here is compromised NIC
firmware. MITMing sounds unlikely as that would require tricky
hardware modifications and if you can do that it's easier to put a
modified NIC instead.

The idea to not trust anything coming from a NIC too much is good in
general but please take the correct context into account when
reasoning about the patches.

^ permalink raw reply

* Re: [PATCH net-next v7 0/5] TLS read_sock performance scalability
From: Chuck Lever @ 2026-04-22 16:41 UTC (permalink / raw)
  To: john.fastabend, Jakub Kicinski, Sabrina Dubroca
  Cc: netdev, kernel-tls-handshake, Chuck Lever, Hannes Reinecke,
	Alistair Francis
In-Reply-To: <20260328-tls-read-sock-v7-0-15678415dfc1@oracle.com>


On Sat, Mar 28, 2026, at 11:17 AM, Chuck Lever wrote:
> I'd like to encourage in-kernel kTLS consumers (i.e., NFS and
> NVMe/TCP) to coalesce on the use of read_sock. When I suggested
> this to Hannes, he reported a number of nagging performance
> scalability issues with read_sock. This series is an attempt to
> run these issues down and get them fixed before we convert the
> above sock_recvmsg consumers over to read_sock.
>
> Batch async decryption and its submit/deliver scaffolding were
> dropped from this series because async_capable is always false
> for TLS 1.3, which NFS and NVMe/TCP both require. Async crypto
> support for TLS 1.3 is a prerequisite for revisiting that work.
>
> ---
> Changes since v6:
> - Rebased on net-next, v5's 1/6 was merged upstream
>
> Changes since v5:
> - Patch 6: Set released = true when sk_flush_backlog() returns
>   true, so tls_strp_msg_load() knows the socket lock was
>   released (Sabrina)
> - Patch 6: Drop Fixes tag; submit bug fix separately via net
>   if warranted (Sabrina)
> - Patch 6: Note redundant flush on cold path in commit message
>   (Sabrina)
>
> Changes since v4:
> - Drop batch async decryption and submit/deliver restructure:
>   async_capable is always false for TLS 1.3, so the new code
>   was unreachable for NFS and NVMe/TCP
> - Purge async_hold directly in tls_decrypt_async_wait() and drop
>   the tls_decrypt_async_drain() wrapper
> - Merge tls_strp_check_rcv_quiet() into tls_strp_check_rcv() with
>   a bool wake parameter; fix lost wakeup on the recvmsg exit path
>
> Changes since v3:
> - Clarify why tls_decrypt_async_drain() is separate from _wait()
> - Fold tls_err_abort() into tls_rx_one_record(), drop tls_rx_decrypt_record()
> - Move backlog flush into tls_rx_rec_wait() so all RX paths benefit
>
> Changes since v2:
> - Fix short read self tests
>
> Changes since v1:
> - Add C11 reference
> - Extend data_ready reduction to recvmsg and splice
> - Restructure read_sock and recvmsg using shared helpers
>
> ---
> Chuck Lever (5):
>       tls: Abort the connection on decrypt failure
>       tls: Fix dangling skb pointer in tls_sw_read_sock()
>       tls: Factor tls_strp_msg_release() from tls_strp_msg_done()
>       tls: Suppress spurious saved_data_ready on all receive paths
>       tls: Flush backlog before waiting for a new record
>
>  net/tls/tls.h      |  4 ++--
>  net/tls/tls_main.c |  2 +-
>  net/tls/tls_strp.c | 42 +++++++++++++++++++++++++++++++-----------
>  net/tls/tls_sw.c   | 50 ++++++++++++++++++++++++++++++--------------------
>  4 files changed, 64 insertions(+), 34 deletions(-)
> ---
> base-commit: ced629dc8e5c51ff2b5d847adeeb1035cd655d58
> change-id: 20260317-tls-read-sock-a0022c9df265

I see that this series is currently not in v7.1. What is left to do?


-- 
Chuck Lever

^ permalink raw reply

* Re: [PATCH net v3 8/8] xsk: don't support AF_XDP on 32-bit architectures
From: Jason Xing @ 2026-04-22 16:37 UTC (permalink / raw)
  To: Alexander Lobakin
  Cc: davem, edumazet, kuba, pabeni, bjorn, magnus.karlsson,
	maciej.fijalkowski, jonathan.lemon, sdf, ast, daniel, hawk,
	john.fastabend, bpf, netdev, Jason Xing
In-Reply-To: <2e0e0e18-cb75-4638-9a12-5906de6a8308@intel.com>

On Thu, Apr 23, 2026 at 12:10 AM Alexander Lobakin
<aleksander.lobakin@intel.com> wrote:
>
> From: Jason Xing <kerneljasonxing@gmail.com>
> Date: Wed, 22 Apr 2026 11:36:50 +0800
>
> > From: Jason Xing <kernelxing@tencent.com>
> >
> > In copy mode TX, xsk_skb_destructor_set_addr() stores the 64-bit
> > descriptor address into skb_shinfo(skb)->destructor_arg (void *) via a
> > uintptr_t cast:
> >
> >     skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);
> >
> > On 32-bit architectures uintptr_t is 32 bits, so the upper 32 bits of
> > the descriptor address are silently dropped. In XDP_ZEROCOPY unaligned
> > mode the chunk offset is encoded in bits 48-63 of the descriptor
> > address (XSK_UNALIGNED_BUF_OFFSET_SHIFT = 48), meaning the offset is
> > lost entirely. The completion queue then returns a truncated address to
> > userspace, making buffer recycling impossible.
>
> What if we relax the restriction a bit? For example, refuse to configure

As to the bug itself, yes, It only affects the unaligned mode.

I wonder if we can support this after someone requires us to support
32-bit arch and use it in the real world, then we can use the previous
patch to complete the full support (which doesn't harm the path on
64-bit arch).

The code looks like this based on your suggestion. Just for the record.
diff --git a/net/xdp/xdp_umem.c b/net/xdp/xdp_umem.c
index 58da2f4f4397..03417b04592f 100644
--- a/net/xdp/xdp_umem.c
+++ b/net/xdp/xdp_umem.c
@@ -177,6 +177,9 @@ static int xdp_umem_reg(struct xdp_umem *umem,
struct xdp_umem_reg *mr)
if (mr->flags & ~XDP_UMEM_FLAGS_VALID)
return -EINVAL;
+ if (!IS_ENABLED(CONFIG_64BIT) && unaligned_chunks)
+ return -EOPNOTSUPP;
+
if (!unaligned_chunks && !is_power_of_2(chunk_size))
return -EINVAL;

Actually I'm fine with either of them. Right now I'm not so sure which
direction this patch should take :)

Thanks,
Jason

> an XSk socket in unaligned mode if on a 32-bit arch? Or add a check
> under CONFIG_32_BIT like it was done in Page Pool:
>
> skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);
>
> #ifdef CONFIG_32BIT
> if (((uintptr_t)skb_shinfo(skb)->destructor_arg) & ~0x1UL) != addr)
>         // WARN_ONCE or whatever + error path
> #endif
>
> I never used XSk on a 32-bit arch, but back when I was working on 32-bit
> MIPS 1G routers, I wanted to add native XSk support to the Eth driver.
> Sure, just for fun, now that we have cheap AArch64 and other 64-bit
> embedded chips, 32-bit embedded networking SoCs are almost dead, but
> OTOH, as you can see, other subsystems like PP still try to support 32 bit.
> Especially given that this issue applies to only to the skb XSk path,
> not native in-driver implementations.
>
> >
> > Since we hear no one is using AF_XDP on 32-bit arch, we decided to
> > strictly stop supporting it at compile time.
> >
> > Closes: https://lore.kernel.org/all/20260419045824.D9E5EC2BCAF@smtp.kernel.org/
> > Fixes: 0ebc27a4c67d ("xsk: avoid data corruption on cq descriptor number")
> > Suggested-by: Stanislav Fomichev <sdf@fomichev.me>
> > Signed-off-by: Jason Xing <kernelxing@tencent.com>
> > ---
> >  net/xdp/Kconfig | 2 +-
> >  1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/net/xdp/Kconfig b/net/xdp/Kconfig
> > index 71af2febe72a..819aa5795f50 100644
> > --- a/net/xdp/Kconfig
> > +++ b/net/xdp/Kconfig
> > @@ -1,7 +1,7 @@
> >  # SPDX-License-Identifier: GPL-2.0-only
> >  config XDP_SOCKETS
> >       bool "XDP sockets"
> > -     depends on BPF_SYSCALL
> > +     depends on BPF_SYSCALL && 64BIT
> >       default n
> >       help
> >         XDP sockets allows a channel between XDP programs and
>
> Thanks,
> Olek

^ permalink raw reply related

* Re: [BUG] rxrpc: Client connection leak and BUG() call during kernel IO thread exit
From: Anderson Nascimento @ 2026-04-22 16:37 UTC (permalink / raw)
  To: David Howells
  Cc: netdev, Marc Dionne, Jakub Kicinski, David S. Miller,
	Eric Dumazet, Paolo Abeni, linux-kernel, Jeffrey Altman,
	Simon Horman
In-Reply-To: <CAPhRvkyCQQM1cv+zQgZjbQDhQ2YVxJ26PG9UrkA6B7uTCguhPQ@mail.gmail.com>

On Wed, Apr 22, 2026 at 1:25 PM Anderson Nascimento
<anderson@allelesecurity.com> wrote:
>
> Hi David,
>
> On Wed, Apr 22, 2026 at 1:08 PM David Howells <dhowells@redhat.com> wrote:
> >
> > Do you by any chance have a reproducer program for this?
> >
>
> Sorry, I mixed things up and ended up sending a different reproducer.
> I do have it, I will test it now and send here in a few minutes.

You can find the reproducers below. I run them simultaneously in a
bash while loop on two different SSH shells. I can trigger running
only one server and one client, but when I discovered the bug I was
running 2 servers and 2 clients simultaneously. You can try both, and
here it doesn't take long to trigger it. My virtual machine is
configured to have 4 cores.

while true; do ./server; done
while true; do ./client; done

server.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/rxrpc.h>
#include <pthread.h>

#define RXRPC_ADD_CHARGE_ACCEPT(control, ctrllen) \
do { \
struct cmsghdr *__cmsg; \
__cmsg = (void *)(control) + (ctrllen); \
__cmsg->cmsg_len = CMSG_LEN(0); \
__cmsg->cmsg_level = SOL_RXRPC; \
__cmsg->cmsg_type = RXRPC_CHARGE_ACCEPT; \
(ctrllen) += __cmsg->cmsg_len; \
\
} while (0)

int sk;

void *__close(void *a){

close(sk);

return NULL;
}

int main(void){
struct sockaddr_rxrpc sockaddr_rxrpc_server;
struct msghdr msg;
struct iovec iov;
char buffer_msg_control[4096];
size_t control_len = 0;
pthread_t th[2];

memset(&sockaddr_rxrpc_server,'\0',sizeof(sockaddr_rxrpc_server));
memset(&buffer_msg_control,'\0',sizeof(buffer_msg_control));
memset(&msg,'\0',sizeof(msg));
memset(&iov,'\0',sizeof(iov));

sk = socket(AF_RXRPC, SOCK_DGRAM, PF_INET);

sockaddr_rxrpc_server.srx_family = AF_RXRPC;
sockaddr_rxrpc_server.srx_service = 1234;
sockaddr_rxrpc_server.transport_type = SOCK_DGRAM;
sockaddr_rxrpc_server.transport_len =
sizeof(sockaddr_rxrpc_server.transport.sin);

sockaddr_rxrpc_server.transport.family = AF_INET;
        sockaddr_rxrpc_server.transport.sin.sin_port = htons(7000);

bind(sk, (struct sockaddr *)&sockaddr_rxrpc_server,
sizeof(sockaddr_rxrpc_server));

listen(sk,10);

RXRPC_ADD_CHARGE_ACCEPT(buffer_msg_control, control_len);

msg.msg_control = buffer_msg_control;
msg.msg_controllen = control_len;

sendmsg(sk, &msg, 0);

pthread_create(&th[0],NULL,&__close,NULL);

pthread_join(th[0],NULL);

return 0;
}

client.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/rxrpc.h>

static const unsigned char local_addr[4] = { 127, 0, 0, 1 };

#define RXRPC_ADD_CALLID(control, ctrllen, id) \
do { \
struct cmsghdr *__cmsg; \
__cmsg = (void *)(control) + (ctrllen); \
__cmsg->cmsg_len = CMSG_LEN(sizeof(unsigned long)); \
__cmsg->cmsg_level = SOL_RXRPC; \
__cmsg->cmsg_type = RXRPC_USER_CALL_ID; \
*(unsigned long *)CMSG_DATA(__cmsg) = (id); \
(ctrllen) += __cmsg->cmsg_len; \
\
} while (0)

int main(void){
struct msghdr msg;
struct sockaddr_rxrpc sockaddr_rxrpc_local;
struct sockaddr_rxrpc sockaddr_rxrpc_client;
char buffer_msg_control[4096];
size_t control_len;
int sk;

memset(&sockaddr_rxrpc_local,'\0',sizeof(sockaddr_rxrpc_local));
memset(&sockaddr_rxrpc_client,'\0',sizeof(sockaddr_rxrpc_client));
memset(&buffer_msg_control,'\0',sizeof(buffer_msg_control));
memset(&msg,'\0',sizeof(msg));

sk = socket(AF_RXRPC, SOCK_DGRAM, PF_INET);

sockaddr_rxrpc_local.srx_family = AF_RXRPC;
sockaddr_rxrpc_local.srx_service = 0;
sockaddr_rxrpc_local.transport_type = SOCK_DGRAM;
sockaddr_rxrpc_local.transport_len = sizeof(sockaddr_rxrpc_local.transport.sin);

sockaddr_rxrpc_local.transport.family = AF_INET;
sockaddr_rxrpc_local.transport.sin.sin_port = htons(7001);

memcpy(&sockaddr_rxrpc_local.transport.sin.sin_addr, &local_addr, 4);

bind(sk, (struct sockaddr *)&sockaddr_rxrpc_local,
sizeof(sockaddr_rxrpc_local));

sockaddr_rxrpc_client.srx_family = AF_RXRPC;
sockaddr_rxrpc_client.srx_service = 1234;
sockaddr_rxrpc_client.transport_type = SOCK_DGRAM;
sockaddr_rxrpc_client.transport_len =
sizeof(sockaddr_rxrpc_client.transport.sin);
sockaddr_rxrpc_client.transport.family = AF_INET;
sockaddr_rxrpc_client.transport.sin.sin_port = htons(7000);

memcpy(&sockaddr_rxrpc_client.transport.sin.sin_addr, &local_addr, 4);

connect(sk, (struct sockaddr *)&sockaddr_rxrpc_client,
sizeof(sockaddr_rxrpc_client));

control_len = 0;
RXRPC_ADD_CALLID(buffer_msg_control, control_len, 0x1234);

msg.msg_control = buffer_msg_control;
msg.msg_controllen = control_len;

sendmsg(sk, &msg, 0);

return 0;
}

The report I have just triggered.

[  473.601077] rxrpc: AF_RXRPC: Leaked client conn 00000000bf02a6a7 {1}
[  473.601115] ------------[ cut here ]------------
[  473.601117] kernel BUG at net/rxrpc/conn_client.c:64!
[  473.601169] Oops: invalid opcode: 0000 [#1] SMP NOPTI
[  473.601180] CPU: 0 UID: 0 PID: 1107239 Comm: krxrpcio/7001 Not
tainted 6.18.13-200.fc43.x86_64 #1 PREEMPT(lazy)
[  473.601193] Hardware name: VMware, Inc. VMware Virtual
Platform/440BX Desktop Reference Platform, BIOS 6.00 11/12/2020
[  473.601205] RIP: 0010:rxrpc_purge_client_connections+0x58/0xa0 [rxrpc]
[  473.601261] Code: 28 01 00 00 00 74 25 31 c0 48 8d 74 24 0c 48 89
cf 89 44 24 0c 48 89 0c 24 e8 d4 ec c2 c1 48 89 c6 48 85 c0 0f 85 49
dd 01 00 <0f> 0b 31 f6 48 89 cf 48 89 0c 24 e8 c8 aa c4 c1 48 8b 0c 24
85 c0
[  473.601280] RSP: 0018:ffffc900159cfdd8 EFLAGS: 00010246
[  473.601288] RAX: 0000000000000000 RBX: ffff88810a6b4800 RCX: 0000000000000000
[  473.601297] RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff88810a6b4920
[  473.601305] RBP: ffff888123398000 R08: ffffc900159cfdb8 R09: ffff88810a6b4928
[  473.601313] R10: 0000000000000018 R11: 0000000040000000 R12: ffff88810a9cda00
[  473.601322] R13: ffff88810a6b4800 R14: ffffc900159cfe70 R15: ffff88812d0c2800
[  473.601330] FS:  0000000000000000(0000) GS:ffff8882af626000(0000)
knlGS:0000000000000000
[  473.601339] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  473.601347] CR2: 00007faf20630030 CR3: 000000000382e002 CR4: 00000000003706f0
[  473.601380] Call Trace:
[  473.601387]  <TASK>
[  473.601393]  rxrpc_destroy_local+0xc9/0xe0 [rxrpc]
[  473.601443]  rxrpc_io_thread+0x65d/0x750 [rxrpc]
[  473.601487]  ? __pfx_rxrpc_io_thread+0x10/0x10 [rxrpc]
[  473.601527]  kthread+0xfc/0x240
[  473.601536]  ? __pfx_kthread+0x10/0x10
[  473.601542]  ret_from_fork+0xf4/0x110
[  473.601550]  ? __pfx_kthread+0x10/0x10
[  473.601558]  ret_from_fork_asm+0x1a/0x30
[  473.601574]  </TASK>
[  473.601578] Modules linked in: vsock_diag fcrypt pcbc rxrpc
ip6_udp_tunnel krb5 udp_tunnel rfkill nft_fib_inet nft_fib_ipv4
nft_fib_ipv6 nft_fib nft_reject_inet nf_reject_ipv4 nf_reject_ipv6
nft_reject nft_ct nft_chain_nat nf_nat nf_conntrack nf_defrag_ipv6
nf_defrag_ipv4 nf_tables intel_rapl_msr intel_rapl_common
intel_uncore_frequency_common intel_pmc_core pmt_telemetry
pmt_discovery pmt_class intel_pmc_ssram_telemetry intel_vsec rapl
vmw_balloon sunrpc qrtr vmxnet3 i2c_piix4 i2c_smbus binfmt_misc joydev
loop dm_multipath nfnetlink zram lz4hc_compress lz4_compress
vmw_vsock_vmci_transport vsock vmw_vmci xfs nvme nvme_core
nvme_keyring polyval_clmulni ghash_clmulni_intel nvme_auth vmwgfx hkdf
drm_ttm_helper ata_generic ttm pata_acpi serio_raw scsi_dh_rdac
scsi_dh_emc scsi_dh_alua i2c_dev fuse
[  473.601690] ---[ end trace 0000000000000000 ]---
[  473.601698] RIP: 0010:rxrpc_purge_client_connections+0x58/0xa0 [rxrpc]
[  473.601794] Code: 28 01 00 00 00 74 25 31 c0 48 8d 74 24 0c 48 89
cf 89 44 24 0c 48 89 0c 24 e8 d4 ec c2 c1 48 89 c6 48 85 c0 0f 85 49
dd 01 00 <0f> 0b 31 f6 48 89 cf 48 89 0c 24 e8 c8 aa c4 c1 48 8b 0c 24
85 c0
[  473.601813] RSP: 0018:ffffc900159cfdd8 EFLAGS: 00010246
[  473.601820] RAX: 0000000000000000 RBX: ffff88810a6b4800 RCX: 0000000000000000
[  473.601829] RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff88810a6b4920
[  473.601837] RBP: ffff888123398000 R08: ffffc900159cfdb8 R09: ffff88810a6b4928
[  473.601845] R10: 0000000000000018 R11: 0000000040000000 R12: ffff88810a9cda00
[  473.602211] R13: ffff88810a6b4800 R14: ffffc900159cfe70 R15: ffff88812d0c2800
[  473.602599] FS:  0000000000000000(0000) GS:ffff8882af626000(0000)
knlGS:0000000000000000
[  473.603301] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  473.604869] CR2: 00007faf20630030 CR3: 000000000382e002 CR4: 00000000003706f0

Best regards,
-- 
Anderson Nascimento
Allele Security Intelligence
https://www.allelesecurity.com

^ permalink raw reply

* Re: [PATCH net v3 6/8] xsk: free the skb when hitting the upper bound MAX_SKB_FRAGS
From: Stanislav Fomichev @ 2026-04-22 16:31 UTC (permalink / raw)
  To: Jason Xing; +Cc: bpf, netdev, Jason Xing
In-Reply-To: <20260422033650.68457-7-kerneljasonxing@gmail.com>

> From: Jason Xing <kernelxing@tencent.com>
> 
> Fix it by explicitly adding kfree_skb() before returning back to its
> caller.
> 
> How to reproduce it in virtio_net:
> 1. the current skb is the first one (which means xs->skb is NULL) and
>    hit the limit MAX_SKB_FRAGS.
> 2. xsk_build_skb_zerocopy() returns -EOVERFLOW.
> 3. the caller xsk_build_skb() clears skb by using 'skb = NULL;'. This
>    is why bug can be triggered.
> 4. there is no chance to free this skb anymore.
> 
> Note that if in this case the xs->skb is not NULL, xsk_build_skb() will
> call xsk_drop_skb(xs->skb) to do the right thing.
> 
> Fixes: cf24f5a5feea ("xsk: add support for AF_XDP multi-buffer on Tx path")
> Signed-off-by: Jason Xing <kernelxing@tencent.com>
> ---
>  net/xdp/xsk.c | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
> 
> diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
> index c49b58199d2f..5e6326e076ab 100644
> --- a/net/xdp/xsk.c
> +++ b/net/xdp/xsk.c
> @@ -776,8 +776,11 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs,
>  	addr = buffer - pool->addrs;
>  
>  	for (copied = 0, i = skb_shinfo(skb)->nr_frags; copied < len; i++) {
> -		if (unlikely(i >= MAX_SKB_FRAGS))
> +		if (unlikely(i >= MAX_SKB_FRAGS)) {
> +			if (!xs->skb)
> +				kfree_skb(skb);
>  			return ERR_PTR(-EOVERFLOW);
> +		}
>  
>  		page = pool->umem->pgs[addr >> PAGE_SHIFT];
>  		get_page(page);
> --
> 2.41.3
>

Acked-by: Stanislav Fomichev <sdf@fomichev.me>

^ permalink raw reply

* Re: [PATCH net v3 8/8] xsk: don't support AF_XDP on 32-bit architectures
From: Stanislav Fomichev @ 2026-04-22 16:31 UTC (permalink / raw)
  To: Jason Xing; +Cc: bpf, netdev, Jason Xing
In-Reply-To: <20260422033650.68457-9-kerneljasonxing@gmail.com>

> From: Jason Xing <kernelxing@tencent.com>
> 
> In copy mode TX, xsk_skb_destructor_set_addr() stores the 64-bit
> descriptor address into skb_shinfo(skb)->destructor_arg (void *) via a
> uintptr_t cast:
> 
>     skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);
> 
> On 32-bit architectures uintptr_t is 32 bits, so the upper 32 bits of
> the descriptor address are silently dropped. In XDP_ZEROCOPY unaligned
> mode the chunk offset is encoded in bits 48-63 of the descriptor
> address (XSK_UNALIGNED_BUF_OFFSET_SHIFT = 48), meaning the offset is
> lost entirely. The completion queue then returns a truncated address to
> userspace, making buffer recycling impossible.
> 
> Since we hear no one is using AF_XDP on 32-bit arch, we decided to
> strictly stop supporting it at compile time.
> 
> Closes: https://lore.kernel.org/all/20260419045824.D9E5EC2BCAF@smtp.kernel.org/
> Fixes: 0ebc27a4c67d ("xsk: avoid data corruption on cq descriptor number")
> Suggested-by: Stanislav Fomichev <sdf@fomichev.me>
> Signed-off-by: Jason Xing <kernelxing@tencent.com>
> ---
>  net/xdp/Kconfig | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/net/xdp/Kconfig b/net/xdp/Kconfig
> index 71af2febe72a..819aa5795f50 100644
> --- a/net/xdp/Kconfig
> +++ b/net/xdp/Kconfig
> @@ -1,7 +1,7 @@
>  # SPDX-License-Identifier: GPL-2.0-only
>  config XDP_SOCKETS
>  	bool "XDP sockets"
> -	depends on BPF_SYSCALL
> +	depends on BPF_SYSCALL && 64BIT
>  	default n
>  	help
>  	  XDP sockets allows a channel between XDP programs and
> --
> 2.41.3
>

Acked-by: Stanislav Fomichev <sdf@fomichev.me>

^ permalink raw reply

* Re: [PATCH net v3 5/8] xsk: avoid skb leak in XDP_TX_METADATA case
From: Stanislav Fomichev @ 2026-04-22 16:31 UTC (permalink / raw)
  To: Jason Xing; +Cc: bpf, netdev, Jason Xing
In-Reply-To: <20260422033650.68457-6-kerneljasonxing@gmail.com>

> From: Jason Xing <kernelxing@tencent.com>
> 
> Fix it by explicitly adding kfree_skb() before returning back to its
> caller.
> 
> How to reproduce it in virtio_net:
> 1. the current skb is the first one (which means no frag and xs->skb is
>    NULL) and users enable metadata feature.
> 2. xsk_skb_metadata() returns a error code.
> 3. the caller xsk_build_skb() clears skb by using 'skb = NULL;'.
> 4. there is no chance to free this skb anymore.
> 
> Closes: https://lore.kernel.org/all/20260415085204.3F87AC19424@smtp.kernel.org/
> Fixes: 30c3055f9c0d ("xsk: wrap generic metadata handling onto separate function")
> Signed-off-by: Jason Xing <kernelxing@tencent.com>
> ---
>  net/xdp/xsk.c | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
> index 88ec6d2cbbcf..c49b58199d2f 100644
> --- a/net/xdp/xsk.c
> +++ b/net/xdp/xsk.c
> @@ -741,8 +741,10 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs,
>  		skb_reserve(skb, hr);
>  		if (desc->options & XDP_TX_METADATA) {
>  			err = xsk_skb_metadata(skb, buffer, desc, pool, hr);
> -			if (unlikely(err))
> +			if (unlikely(err)) {
> +				kfree_skb(skb);
>  				return ERR_PTR(err);
> +			}
>  		}
>  	} else {
>  		struct xsk_addrs *xsk_addr;
> --
> 2.41.3
>

Acked-by: Stanislav Fomichev <sdf@fomichev.me>

^ permalink raw reply

* Re: [PATCH net v3 4/8] xsk: prevent CQ desync when freeing half-built skbs in xsk_build_skb()
From: Stanislav Fomichev @ 2026-04-22 16:31 UTC (permalink / raw)
  To: Jason Xing; +Cc: bpf, netdev, Jason Xing
In-Reply-To: <20260422033650.68457-5-kerneljasonxing@gmail.com>

> From: Jason Xing <kernelxing@tencent.com>
> 
> Once xsk_skb_init_misc() has been called on an skb, its destructor is
> set to xsk_destruct_skb(), which submits the descriptor address(es) to
> the completion queue and advances the CQ producer. If such an skb is
> subsequently freed via kfree_skb() along an error path - before the
> skb has ever been handed to the driver - the destructor still runs and
> submits a bogus, half-initialized address to the CQ.
> 
> Postpone the init phase when we believe the allocation of first frag is
> successfully completed. Before this init, skb can be safely freed by
> kfree_skb().
> 
> Closes: https://lore.kernel.org/all/20260419045822.843BFC2BCAF@smtp.kernel.org/
> Fixes: c30d084960cf ("xsk: avoid overwriting skb fields for multi-buffer traffic")
> Signed-off-by: Jason Xing <kernelxing@tencent.com>
> ---
>  net/xdp/xsk.c | 5 ++---
>  1 file changed, 2 insertions(+), 3 deletions(-)
> 
> diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
> index d23d1b14b8b4..88ec6d2cbbcf 100644
> --- a/net/xdp/xsk.c
> +++ b/net/xdp/xsk.c
> @@ -739,8 +739,6 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs,
>  			return ERR_PTR(err);
>  
>  		skb_reserve(skb, hr);
> -
> -		xsk_skb_init_misc(skb, xs, desc->addr);
>  		if (desc->options & XDP_TX_METADATA) {
>  			err = xsk_skb_metadata(skb, buffer, desc, pool, hr);
>  			if (unlikely(err))
> @@ -834,7 +832,6 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
>  			if (unlikely(err))
>  				goto free_err;
>  
> -			xsk_skb_init_misc(skb, xs, desc->addr);
>  			if (desc->options & XDP_TX_METADATA) {
>  				err = xsk_skb_metadata(skb, buffer, desc,
>  						       xs->pool, hr);
> @@ -884,6 +881,8 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
>  		}
>  	}
>  
> +	if (!xs->skb)
> +		xsk_skb_init_misc(skb, xs, desc->addr);
>  	xsk_inc_num_desc(skb);
>  
>  	return skb;
> --
> 2.41.3
>

Acked-by: Stanislav Fomichev <sdf@fomichev.me>

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox