Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/drivers/usb/net/Kconfig b/drivers/usb/net/Kconfig
new file mode 100644
index 0000000..db64c90
--- /dev/null
+++ b/drivers/usb/net/Kconfig
@@ -0,0 +1,307 @@
+#
+# USB Network devices configuration
+#
+comment "Networking support is needed for USB Network Adapter support"
+	depends on USB && !NET
+
+menu "USB Network Adapters"
+	depends on USB && NET
+
+config USB_CATC
+	tristate "USB CATC NetMate-based Ethernet device support (EXPERIMENTAL)"
+	depends on EXPERIMENTAL
+	select CRC32
+	---help---
+	  Say Y if you want to use one of the following 10Mbps USB Ethernet
+	  device based on the EL1210A chip. Supported devices are:
+	  Belkin F5U011
+	  Belkin F5U111
+	  CATC NetMate
+	  CATC NetMate II
+	  smartBridges smartNIC
+
+	  This driver makes the adapter appear as a normal Ethernet interface,
+	  typically on eth0, if it is the only ethernet device, or perhaps on
+	  eth1, if you have a PCI or ISA ethernet card installed.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called catc.
+
+config USB_KAWETH
+	tristate "USB KLSI KL5USB101-based ethernet device support"
+	---help---
+	  Say Y here if you want to use one of the following 10Mbps only
+	  USB Ethernet adapters based on the KLSI KL5KUSB101B chipset:
+	  3Com 3C19250
+	  ADS USB-10BT
+	  ATEN USB Ethernet
+	  ASANTE USB To Ethernet Adapter
+	  AOX Endpoints USB Ethernet
+	  Correga K.K.
+	  D-Link DSB-650C and DU-E10
+	  Entrega / Portgear E45
+	  I-O DATA USB-ET/T
+	  Jaton USB Ethernet Device Adapter
+	  Kingston Technology USB Ethernet Adapter
+	  Linksys USB10T
+	  Mobility USB-Ethernet Adapter
+	  NetGear EA-101
+	  Peracom Enet and Enet2
+	  Portsmith Express Ethernet Adapter
+	  Shark Pocket Adapter
+	  SMC 2202USB
+	  Sony Vaio port extender
+
+	  This driver is likely to work with most 10Mbps only USB Ethernet
+	  adapters, including some "no brand" devices. It does NOT work on
+	  SmartBridges smartNIC or on Belkin F5U111 devices - you should use
+	  the CATC NetMate driver for those. If you are not sure which one
+	  you need, select both, and the correct one should be selected for
+	  you.
+
+	  This driver makes the adapter appear as a normal Ethernet interface,
+	  typically on eth0, if it is the only ethernet device, or perhaps on
+	  eth1, if you have a PCI or ISA ethernet card installed.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called kaweth.
+
+config USB_PEGASUS
+	tristate "USB Pegasus/Pegasus-II based ethernet device support"
+	select MII
+	---help---
+	  Say Y here if you know you have Pegasus or Pegasus-II based adapter.
+	  If in doubt then look at <file:drivers/usb/net/pegasus.h> for the
+	  complete list of supported devices.
+
+	  If your particular adapter is not in the list and you are _sure_ it
+	  is Pegasus or Pegasus II based then send me
+	  <petkan@users.sourceforge.net> vendor and device IDs.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called pegasus.
+
+config USB_RTL8150
+	tristate "USB RTL8150 based ethernet device support (EXPERIMENTAL)"
+	depends on EXPERIMENTAL
+	help
+	  Say Y here if you have RTL8150 based usb-ethernet adapter.
+	  Send me <petkan@users.sourceforge.net> any comments you may have.
+	  You can also check for updates at <http://pegasus2.sourceforge.net/>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rtl8150.
+
+config USB_USBNET
+	tristate "Multi-purpose USB Networking Framework"
+	---help---
+	  This driver supports several kinds of network links over USB,
+	  with "minidrivers" built around a common network driver core
+	  that supports deep queues for efficient transfers.  (This gives
+	  better performance with small packets and at high speeds).
+	  
+	  The USB host runs "usbnet", and the other end of the link might be:
+
+	  - Another USB host, when using USB "network" or "data transfer"
+	    cables.  These are often used to network laptops to PCs, like
+	    "Laplink" parallel cables or some motherboards.  These rely
+	    on specialized chips from many suppliers.
+
+	  - An intelligent USB gadget, perhaps embedding a Linux system.
+	    These include PDAs running Linux (iPaq, Yopy, Zaurus, and
+	    others), and devices that interoperate using the standard
+	    CDC-Ethernet specification (including many cable modems).
+
+	  - Network adapter hardware (like those for 10/100 Ethernet) which
+	    uses this driver framework.
+
+	  The link will appear with a name like "usb0", when the link is
+	  a two-node link, or "eth0" for most CDC-Ethernet devices.  Those
+	  two-node links are most easily managed with Ethernet Bridging
+	  (CONFIG_BRIDGE) instead of routing.
+
+	  For more information see <http://www.linux-usb.org/usbnet/>.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called usbnet.
+
+comment "USB Host-to-Host Cables"
+	depends on USB_USBNET
+
+config USB_ALI_M5632
+	boolean "ALi M5632 based 'USB 2.0 Data Link' cables"
+	depends on USB_USBNET
+	default y
+	help
+	  Choose this option if you're using a host-to-host cable
+	  based on this design, which supports USB 2.0 high speed.
+
+config USB_AN2720
+	boolean "AnchorChips 2720 based cables (Xircom PGUNET, ...)"
+	depends on USB_USBNET
+	default y
+	help
+	  Choose this option if you're using a host-to-host cable
+	  based on this design.  Note that AnchorChips is now a
+	  Cypress brand.
+
+config USB_BELKIN
+	boolean "eTEK based host-to-host cables (Advance, Belkin, ...)"
+	depends on USB_USBNET
+	default y
+	help
+	  Choose this option if you're using a host-to-host cable
+	  based on this design:  two NetChip 2890 chips and an Atmel
+	  microcontroller, with LEDs that indicate traffic.
+
+config USB_GENESYS
+	boolean "GeneSys GL620USB-A based cables"
+	default y
+	depends on USB_USBNET
+	help
+	  Choose this option if you're using a host-to-host cable,
+	  or PC2PC motherboard, with this chip.
+
+	  Note that the half-duplex "GL620USB" is not supported.
+
+config USB_NET1080
+	boolean "NetChip 1080 based cables (Laplink, ...)"
+	default y
+	depends on USB_USBNET
+	help
+	  Choose this option if you're using a host-to-host cable based
+	  on this design:  one NetChip 1080 chips and supporting logic,
+	  supporting LEDs that indicate traffic
+
+config USB_PL2301
+	boolean "Prolific PL-2301/2302 based cables"
+	default y
+	# handshake/init/reset problems, from original 'plusb' driver
+	depends on USB_USBNET && EXPERIMENTAL
+	help
+	  Choose this option if you're using a host-to-host cable
+	  with one of these chips.
+
+config USB_KC2190
+	boolean "KT Technology KC2190 based cables (InstaNet)"
+	default y
+	depends on USB_USBNET && EXPERIMENTAL
+	help
+	  Choose this option if you're using a host-to-host cable
+	  with one of these chips.
+
+comment "Intelligent USB Devices/Gadgets"
+	depends on USB_USBNET
+
+config USB_ARMLINUX
+	boolean "Embedded ARM Linux links (iPaq, ...)"
+	depends on USB_USBNET
+	default y
+	help
+	  Choose this option to support the "usb-eth" networking driver
+	  used by most of the ARM Linux community with device controllers
+	  such as the SA-11x0 and PXA-25x UDCs, or the tftp capabilities
+	  in some PXA versions of the "blob" boot loader.
+
+	  Linux-based "Gumstix" PXA-25x based systems use this protocol
+	  to talk with other Linux systems.
+
+	  Although the ROMs shipped with Sharp Zaurus products use a
+	  different link level framing protocol, you can have them use
+	  this simpler protocol by installing a different kernel.
+
+config USB_EPSON2888
+	boolean "Epson 2888 based firmware (DEVELOPMENT)"
+	depends on USB_USBNET
+	default y
+	help
+	  Choose this option to support the usb networking links used
+	  by some sample firmware from Epson.
+
+config USB_ZAURUS
+	boolean "Sharp Zaurus (stock ROMs)"
+	depends on USB_USBNET
+	select CRC32
+	default y
+	help
+	  Choose this option to support the usb networking links used by
+	  Zaurus models like the SL-5000D, SL-5500, SL-5600, A-300, B-500.
+
+	  If you install an alternate ROM image, you may no longer need
+	  to support this protocol.  Only the "eth-fd" driver really needs
+	  this non-conformant variant of CDC Ethernet protocol.
+
+config USB_CDCETHER
+	boolean "CDC Ethernet support (smart devices such as cable modems)"
+	depends on USB_USBNET
+	default y
+	help
+	  This option supports devices conforming to the Communication Device
+	  Class (CDC) Ethernet Control Model, a specification that's easy to
+	  implement in device firmware.  The CDC specifications are available
+	  from <http://www.usb.org/>.
+	  
+	  CDC Ethernet is an implementation option for DOCSIS cable modems
+	  that support USB connectivity, used for non-Microsoft USB hosts.
+ 	  This driver should work with at least the following devices:
+
+ 	    * Ericsson PipeRider (all variants)
+ 	    * Motorola (DM100 and SB4100)
+ 	    * Broadcom Cable Modem (reference design)
+ 	    * Toshiba PCX1100U
+	    * ...
+
+	  This driver creates an interface named "ethX", where X depends on
+	  what other networking devices you have in use.  However, if the
+	  IEEE 802 "local assignment" bit is set in the address, a "usbX"
+	  name is used instead.
+
+comment "USB Network Adapters"
+	depends on USB_USBNET
+
+config USB_AX8817X
+	boolean "ASIX AX88xxx Based USB 2.0 Ethernet Devices"
+	depends on USB_USBNET && NET_ETHERNET
+	select CRC32
+	select MII
+	default y
+	help
+	  This option adds support for ASIX AX88xxx based USB 2.0
+	  10/100 Ethernet devices.
+
+ 	  This driver should work with at least the following devices:
+	    * Aten UC210T
+	    * ASIX AX88172
+	    * Billionton Systems, USB2AR 
+	    * Buffalo LUA-U2-KTX
+	    * Corega FEther USB2-TX
+	    * D-Link DUB-E100
+	    * Hawking UF200
+	    * Linksys USB200M
+	    * Netgear FA120
+	    * Sitecom LN-029
+	    * Intellinet USB 2.0 Ethernet
+	    * ST Lab USB 2.0 Ethernet
+	    * TrendNet TU2-ET100
+
+	  This driver creates an interface named "ethX", where X depends on
+	  what other networking devices you have in use.  
+
+config USB_ZD1201
+	tristate "USB ZD1201 based Wireless device support"
+	depends on NET_RADIO
+	select FW_LOADER
+	---help---
+	  Say Y if you want to use wireless LAN adapters based on the ZyDAS
+	  ZD1201 chip.
+
+	  This driver makes the adapter appear as a normal Ethernet interface,
+	  typically on wlan0.
+	  
+	  The zd1201 device requires external firmware to be loaded.
+	  This can be found at http://linux-lc100020.sourceforge.net/
+	  
+	  To compile this driver as a module, choose M here: the
+	  module will be called zd1201.
+
+endmenu
diff --git a/drivers/usb/net/Makefile b/drivers/usb/net/Makefile
new file mode 100644
index 0000000..16f3521
--- /dev/null
+++ b/drivers/usb/net/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for USB Network drivers
+#
+
+obj-$(CONFIG_USB_CATC)		+= catc.o
+obj-$(CONFIG_USB_KAWETH)	+= kaweth.o
+obj-$(CONFIG_USB_PEGASUS)	+= pegasus.o
+obj-$(CONFIG_USB_RTL8150)	+= rtl8150.o
+obj-$(CONFIG_USB_USBNET)	+= usbnet.o
+obj-$(CONFIG_USB_ZD1201)	+= zd1201.o
+
+CFLAGS_zd1201.o = -Idrivers/net/wireless/
diff --git a/drivers/usb/net/catc.c b/drivers/usb/net/catc.c
new file mode 100644
index 0000000..c8be912
--- /dev/null
+++ b/drivers/usb/net/catc.c
@@ -0,0 +1,960 @@
+/*
+ *  Copyright (c) 2001 Vojtech Pavlik
+ *
+ *  CATC EL1210A NetMate USB Ethernet driver
+ *
+ *  Sponsored by SuSE
+ *
+ *  Based on the work of
+ *		Donald Becker
+ * 
+ *  Old chipset support added by Simon Evans <spse@secret.org.uk> 2002
+ *    - adds support for Belkin F5U011
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or 
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * 
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@suse.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/ethtool.h>
+#include <linux/crc32.h>
+#include <linux/bitops.h>
+#include <asm/uaccess.h>
+
+#undef DEBUG
+
+#include <linux/usb.h>
+
+/*
+ * Version information.
+ */
+
+#define DRIVER_VERSION "v2.8"
+#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@suse.cz>"
+#define DRIVER_DESC "CATC EL1210A NetMate USB Ethernet driver"
+#define SHORT_DRIVER_DESC "EL1210A NetMate USB Ethernet"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+static const char driver_name[] = "catc";
+
+/*
+ * Some defines.
+ */ 
+
+#define STATS_UPDATE		(HZ)	/* Time between stats updates */
+#define TX_TIMEOUT		(5*HZ)	/* Max time the queue can be stopped */
+#define PKT_SZ			1536	/* Max Ethernet packet size */
+#define RX_MAX_BURST		15	/* Max packets per rx buffer (> 0, < 16) */
+#define TX_MAX_BURST		15	/* Max full sized packets per tx buffer (> 0) */
+#define CTRL_QUEUE		16	/* Max control requests in flight (power of two) */
+#define RX_PKT_SZ		1600	/* Max size of receive packet for F5U011 */
+
+/*
+ * Control requests.
+ */
+
+enum control_requests {
+	ReadMem =	0xf1,
+	GetMac =	0xf2,
+	Reset =		0xf4,
+	SetMac =	0xf5,
+	SetRxMode =     0xf5,  /* F5U011 only */
+	WriteROM =	0xf8,
+	SetReg =	0xfa,
+	GetReg =	0xfb,
+	WriteMem =	0xfc,
+	ReadROM =	0xfd,
+};
+
+/*
+ * Registers.
+ */
+
+enum register_offsets {
+	TxBufCount =	0x20,
+	RxBufCount =	0x21,
+	OpModes =	0x22,
+	TxQed =		0x23,
+	RxQed =		0x24,
+	MaxBurst =	0x25,
+	RxUnit =	0x60,
+	EthStatus =	0x61,
+	StationAddr0 =	0x67,
+	EthStats =	0x69,
+	LEDCtrl =	0x81,
+};
+
+enum eth_stats {
+	TxSingleColl =	0x00,
+        TxMultiColl =	0x02,
+        TxExcessColl =	0x04,
+        RxFramErr =	0x06,
+};
+
+enum op_mode_bits {
+	Op3MemWaits =	0x03,
+	OpLenInclude =	0x08,
+	OpRxMerge =	0x10,
+	OpTxMerge =	0x20,
+	OpWin95bugfix =	0x40,
+	OpLoopback =	0x80,
+};
+
+enum rx_filter_bits {
+	RxEnable =	0x01,
+	RxPolarity =	0x02,
+	RxForceOK =	0x04,
+	RxMultiCast =	0x08,
+	RxPromisc =	0x10,
+	AltRxPromisc =  0x20, /* F5U011 uses different bit */
+};
+
+enum led_values {
+	LEDFast = 	0x01,
+	LEDSlow =	0x02,
+	LEDFlash =	0x03,
+	LEDPulse =	0x04,
+	LEDLink =	0x08,
+};
+
+enum link_status {
+	LinkNoChange = 0,
+	LinkGood     = 1,
+	LinkBad      = 2
+};
+
+/*
+ * The catc struct.
+ */
+
+#define CTRL_RUNNING	0
+#define RX_RUNNING	1
+#define TX_RUNNING	2
+
+struct catc {
+	struct net_device *netdev;
+	struct usb_device *usbdev;
+
+	struct net_device_stats stats;
+	unsigned long flags;
+
+	unsigned int tx_ptr, tx_idx;
+	unsigned int ctrl_head, ctrl_tail;
+	spinlock_t tx_lock, ctrl_lock;
+
+	u8 tx_buf[2][TX_MAX_BURST * (PKT_SZ + 2)];
+	u8 rx_buf[RX_MAX_BURST * (PKT_SZ + 2)];
+	u8 irq_buf[2];
+	u8 ctrl_buf[64];
+	struct usb_ctrlrequest ctrl_dr;
+
+	struct timer_list timer;
+	u8 stats_buf[8];
+	u16 stats_vals[4];
+	unsigned long last_stats;
+
+	u8 multicast[64];
+
+	struct ctrl_queue {
+		u8 dir;
+		u8 request;
+		u16 value;
+		u16 index;
+		void *buf;
+		int len;
+		void (*callback)(struct catc *catc, struct ctrl_queue *q);
+	} ctrl_queue[CTRL_QUEUE];
+
+	struct urb *tx_urb, *rx_urb, *irq_urb, *ctrl_urb;
+
+	u8 is_f5u011;	/* Set if device is an F5U011 */
+	u8 rxmode[2];	/* Used for F5U011 */
+	atomic_t recq_sz; /* Used for F5U011 - counter of waiting rx packets */
+};
+
+/*
+ * Useful macros.
+ */
+
+#define catc_get_mac(catc, mac)				catc_ctrl_msg(catc, USB_DIR_IN,  GetMac, 0, 0, mac,  6)
+#define catc_reset(catc)				catc_ctrl_msg(catc, USB_DIR_OUT, Reset, 0, 0, NULL, 0)
+#define catc_set_reg(catc, reg, val)			catc_ctrl_msg(catc, USB_DIR_OUT, SetReg, val, reg, NULL, 0)
+#define catc_get_reg(catc, reg, buf)			catc_ctrl_msg(catc, USB_DIR_IN,  GetReg, 0, reg, buf, 1)
+#define catc_write_mem(catc, addr, buf, size)		catc_ctrl_msg(catc, USB_DIR_OUT, WriteMem, 0, addr, buf, size)
+#define catc_read_mem(catc, addr, buf, size)		catc_ctrl_msg(catc, USB_DIR_IN,  ReadMem, 0, addr, buf, size)
+
+#define f5u011_rxmode(catc, rxmode)			catc_ctrl_msg(catc, USB_DIR_OUT, SetRxMode, 0, 1, rxmode, 2)
+#define f5u011_rxmode_async(catc, rxmode)		catc_ctrl_async(catc, USB_DIR_OUT, SetRxMode, 0, 1, &rxmode, 2, NULL)
+#define f5u011_mchash_async(catc, hash)			catc_ctrl_async(catc, USB_DIR_OUT, SetRxMode, 0, 2, &hash, 8, NULL)
+
+#define catc_set_reg_async(catc, reg, val)		catc_ctrl_async(catc, USB_DIR_OUT, SetReg, val, reg, NULL, 0, NULL)
+#define catc_get_reg_async(catc, reg, cb)		catc_ctrl_async(catc, USB_DIR_IN, GetReg, 0, reg, NULL, 1, cb)
+#define catc_write_mem_async(catc, addr, buf, size)	catc_ctrl_async(catc, USB_DIR_OUT, WriteMem, 0, addr, buf, size, NULL)
+
+/*
+ * Receive routines.
+ */
+
+static void catc_rx_done(struct urb *urb, struct pt_regs *regs)
+{
+	struct catc *catc = urb->context;
+	u8 *pkt_start = urb->transfer_buffer;
+	struct sk_buff *skb;
+	int pkt_len, pkt_offset = 0;
+
+	if (!catc->is_f5u011) {
+		clear_bit(RX_RUNNING, &catc->flags);
+		pkt_offset = 2;
+	}
+
+	if (urb->status) {
+		dbg("rx_done, status %d, length %d", urb->status, urb->actual_length);
+		return;
+	}
+
+	do {
+		if(!catc->is_f5u011) {
+			pkt_len = le16_to_cpup((__le16*)pkt_start);
+			if (pkt_len > urb->actual_length) {
+				catc->stats.rx_length_errors++;
+				catc->stats.rx_errors++;
+				break;
+			}
+		} else {
+			pkt_len = urb->actual_length;
+		}
+
+		if (!(skb = dev_alloc_skb(pkt_len)))
+			return;
+
+		skb->dev = catc->netdev;
+		eth_copy_and_sum(skb, pkt_start + pkt_offset, pkt_len, 0);
+		skb_put(skb, pkt_len);
+
+		skb->protocol = eth_type_trans(skb, catc->netdev);
+		netif_rx(skb);
+
+		catc->stats.rx_packets++;
+		catc->stats.rx_bytes += pkt_len;
+
+		/* F5U011 only does one packet per RX */
+		if (catc->is_f5u011)
+			break;
+		pkt_start += (((pkt_len + 1) >> 6) + 1) << 6;
+
+	} while (pkt_start - (u8 *) urb->transfer_buffer < urb->actual_length);
+
+	catc->netdev->last_rx = jiffies;
+
+	if (catc->is_f5u011) {
+		if (atomic_read(&catc->recq_sz)) {
+			int status;
+			atomic_dec(&catc->recq_sz);
+			dbg("getting extra packet");
+			urb->dev = catc->usbdev;
+			if ((status = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+				dbg("submit(rx_urb) status %d", status);
+			}
+		} else {
+			clear_bit(RX_RUNNING, &catc->flags);
+		}
+	}
+}
+
+static void catc_irq_done(struct urb *urb, struct pt_regs *regs)
+{
+	struct catc *catc = urb->context;
+	u8 *data = urb->transfer_buffer;
+	int status;
+	unsigned int hasdata = 0, linksts = LinkNoChange;
+
+	if (!catc->is_f5u011) {
+		hasdata = data[1] & 0x80;
+		if (data[1] & 0x40)
+			linksts = LinkGood;
+		else if (data[1] & 0x20)
+			linksts = LinkBad;
+	} else {
+		hasdata = (unsigned int)(be16_to_cpup((__be16*)data) & 0x0fff);
+		if (data[0] == 0x90)
+			linksts = LinkGood;
+		else if (data[0] == 0xA0)
+			linksts = LinkBad;
+	}
+
+	switch (urb->status) {
+	case 0:			/* success */
+		break;
+	case -ECONNRESET:	/* unlink */
+	case -ENOENT:
+	case -ESHUTDOWN:
+		return;
+	/* -EPIPE:  should clear the halt */
+	default:		/* error */
+		dbg("irq_done, status %d, data %02x %02x.", urb->status, data[0], data[1]);
+		goto resubmit;
+	}
+
+	if (linksts == LinkGood) {
+		netif_carrier_on(catc->netdev);
+		dbg("link ok");
+	}
+
+	if (linksts == LinkBad) {
+		netif_carrier_off(catc->netdev);
+		dbg("link bad");
+	}
+
+	if (hasdata) {
+		if (test_and_set_bit(RX_RUNNING, &catc->flags)) {
+			if (catc->is_f5u011)
+				atomic_inc(&catc->recq_sz);
+		} else {
+			catc->rx_urb->dev = catc->usbdev;
+			if ((status = usb_submit_urb(catc->rx_urb, GFP_ATOMIC)) < 0) {
+				err("submit(rx_urb) status %d", status);
+			}
+		} 
+	}
+resubmit:
+	status = usb_submit_urb (urb, SLAB_ATOMIC);
+	if (status)
+		err ("can't resubmit intr, %s-%s, status %d",
+				catc->usbdev->bus->bus_name,
+				catc->usbdev->devpath, status);
+}
+
+/*
+ * Transmit routines.
+ */
+
+static void catc_tx_run(struct catc *catc)
+{
+	int status;
+
+	if (catc->is_f5u011)
+		catc->tx_ptr = (catc->tx_ptr + 63) & ~63;
+
+	catc->tx_urb->transfer_buffer_length = catc->tx_ptr;
+	catc->tx_urb->transfer_buffer = catc->tx_buf[catc->tx_idx];
+	catc->tx_urb->dev = catc->usbdev;
+
+	if ((status = usb_submit_urb(catc->tx_urb, GFP_ATOMIC)) < 0)
+		err("submit(tx_urb), status %d", status);
+
+	catc->tx_idx = !catc->tx_idx;
+	catc->tx_ptr = 0;
+
+	catc->netdev->trans_start = jiffies;
+}
+
+static void catc_tx_done(struct urb *urb, struct pt_regs *regs)
+{
+	struct catc *catc = urb->context;
+	unsigned long flags;
+
+	if (urb->status == -ECONNRESET) {
+		dbg("Tx Reset.");
+		urb->transfer_flags &= ~URB_ASYNC_UNLINK;
+		urb->status = 0;
+		catc->netdev->trans_start = jiffies;
+		catc->stats.tx_errors++;
+		clear_bit(TX_RUNNING, &catc->flags);
+		netif_wake_queue(catc->netdev);
+		return;
+	}
+
+	if (urb->status) {
+		dbg("tx_done, status %d, length %d", urb->status, urb->actual_length);
+		return;
+	}
+
+	spin_lock_irqsave(&catc->tx_lock, flags);
+
+	if (catc->tx_ptr)
+		catc_tx_run(catc);
+	else
+		clear_bit(TX_RUNNING, &catc->flags);
+
+	netif_wake_queue(catc->netdev);
+
+	spin_unlock_irqrestore(&catc->tx_lock, flags);
+}
+
+static int catc_hard_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+	struct catc *catc = netdev_priv(netdev);
+	unsigned long flags;
+	char *tx_buf;
+
+	spin_lock_irqsave(&catc->tx_lock, flags);
+
+	catc->tx_ptr = (((catc->tx_ptr - 1) >> 6) + 1) << 6;
+	tx_buf = catc->tx_buf[catc->tx_idx] + catc->tx_ptr;
+	*((u16*)tx_buf) = (catc->is_f5u011) ? cpu_to_be16((u16)skb->len) : cpu_to_le16((u16)skb->len);
+	memcpy(tx_buf + 2, skb->data, skb->len);
+	catc->tx_ptr += skb->len + 2;
+
+	if (!test_and_set_bit(TX_RUNNING, &catc->flags))
+		catc_tx_run(catc);
+
+	if ((catc->is_f5u011 && catc->tx_ptr)
+	     || (catc->tx_ptr >= ((TX_MAX_BURST - 1) * (PKT_SZ + 2))))
+		netif_stop_queue(netdev);
+
+	spin_unlock_irqrestore(&catc->tx_lock, flags);
+
+	catc->stats.tx_bytes += skb->len;
+	catc->stats.tx_packets++;
+
+	dev_kfree_skb(skb);
+
+	return 0;
+}
+
+static void catc_tx_timeout(struct net_device *netdev)
+{
+	struct catc *catc = netdev_priv(netdev);
+
+	warn("Transmit timed out.");
+	catc->tx_urb->transfer_flags |= URB_ASYNC_UNLINK;
+	usb_unlink_urb(catc->tx_urb);
+}
+
+/*
+ * Control messages.
+ */
+
+static int catc_ctrl_msg(struct catc *catc, u8 dir, u8 request, u16 value, u16 index, void *buf, int len)
+{
+        int retval = usb_control_msg(catc->usbdev,
+		dir ? usb_rcvctrlpipe(catc->usbdev, 0) : usb_sndctrlpipe(catc->usbdev, 0),
+		 request, 0x40 | dir, value, index, buf, len, 1000);
+        return retval < 0 ? retval : 0;
+}
+
+static void catc_ctrl_run(struct catc *catc)
+{
+	struct ctrl_queue *q = catc->ctrl_queue + catc->ctrl_tail;
+	struct usb_device *usbdev = catc->usbdev;
+	struct urb *urb = catc->ctrl_urb;
+	struct usb_ctrlrequest *dr = &catc->ctrl_dr;
+	int status;
+
+	dr->bRequest = q->request;
+	dr->bRequestType = 0x40 | q->dir;
+	dr->wValue = cpu_to_le16(q->value);
+	dr->wIndex = cpu_to_le16(q->index);
+	dr->wLength = cpu_to_le16(q->len);
+
+        urb->pipe = q->dir ? usb_rcvctrlpipe(usbdev, 0) : usb_sndctrlpipe(usbdev, 0);
+	urb->transfer_buffer_length = q->len;
+	urb->transfer_buffer = catc->ctrl_buf;
+	urb->setup_packet = (void *) dr;
+	urb->dev = usbdev;
+
+	if (!q->dir && q->buf && q->len)
+		memcpy(catc->ctrl_buf, q->buf, q->len);
+
+	if ((status = usb_submit_urb(catc->ctrl_urb, GFP_KERNEL)))
+		err("submit(ctrl_urb) status %d", status);
+}
+
+static void catc_ctrl_done(struct urb *urb, struct pt_regs *regs)
+{
+	struct catc *catc = urb->context;
+	struct ctrl_queue *q;
+	unsigned long flags;
+
+	if (urb->status)
+		dbg("ctrl_done, status %d, len %d.", urb->status, urb->actual_length);
+
+	spin_lock_irqsave(&catc->ctrl_lock, flags);
+
+	q = catc->ctrl_queue + catc->ctrl_tail;
+
+	if (q->dir) {
+		if (q->buf && q->len)
+			memcpy(q->buf, catc->ctrl_buf, q->len);
+		else
+			q->buf = catc->ctrl_buf;
+	}
+
+	if (q->callback)
+		q->callback(catc, q);
+
+	catc->ctrl_tail = (catc->ctrl_tail + 1) & (CTRL_QUEUE - 1);
+
+	if (catc->ctrl_head != catc->ctrl_tail)
+		catc_ctrl_run(catc);
+	else
+		clear_bit(CTRL_RUNNING, &catc->flags);
+
+	spin_unlock_irqrestore(&catc->ctrl_lock, flags);
+}
+
+static int catc_ctrl_async(struct catc *catc, u8 dir, u8 request, u16 value,
+	u16 index, void *buf, int len, void (*callback)(struct catc *catc, struct ctrl_queue *q))
+{
+	struct ctrl_queue *q;
+	int retval = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&catc->ctrl_lock, flags);
+	
+	q = catc->ctrl_queue + catc->ctrl_head;
+
+	q->dir = dir;
+	q->request = request;
+	q->value = value;
+	q->index = index;
+	q->buf = buf;
+	q->len = len;
+	q->callback = callback;
+
+	catc->ctrl_head = (catc->ctrl_head + 1) & (CTRL_QUEUE - 1);
+
+	if (catc->ctrl_head == catc->ctrl_tail) {
+		err("ctrl queue full");
+		catc->ctrl_tail = (catc->ctrl_tail + 1) & (CTRL_QUEUE - 1);
+		retval = -1;
+	}
+
+	if (!test_and_set_bit(CTRL_RUNNING, &catc->flags))
+		catc_ctrl_run(catc);
+
+	spin_unlock_irqrestore(&catc->ctrl_lock, flags);
+
+	return retval;
+}
+
+/*
+ * Statistics.
+ */
+
+static void catc_stats_done(struct catc *catc, struct ctrl_queue *q)
+{
+	int index = q->index - EthStats;
+	u16 data, last;
+
+	catc->stats_buf[index] = *((char *)q->buf);
+
+	if (index & 1)
+		return;
+
+	data = ((u16)catc->stats_buf[index] << 8) | catc->stats_buf[index + 1];
+	last = catc->stats_vals[index >> 1];
+
+	switch (index) {
+		case TxSingleColl:
+		case TxMultiColl:
+			catc->stats.collisions += data - last;
+			break;
+		case TxExcessColl:
+			catc->stats.tx_aborted_errors += data - last;
+			catc->stats.tx_errors += data - last;
+			break;
+		case RxFramErr:
+			catc->stats.rx_frame_errors += data - last;
+			catc->stats.rx_errors += data - last;
+			break;
+	}
+
+	catc->stats_vals[index >> 1] = data;
+}
+
+static void catc_stats_timer(unsigned long data)
+{
+	struct catc *catc = (void *) data;
+	int i;
+
+	for (i = 0; i < 8; i++)
+		catc_get_reg_async(catc, EthStats + 7 - i, catc_stats_done);
+
+	mod_timer(&catc->timer, jiffies + STATS_UPDATE);
+}
+
+static struct net_device_stats *catc_get_stats(struct net_device *netdev)
+{
+	struct catc *catc = netdev_priv(netdev);
+	return &catc->stats;
+}
+
+/*
+ * Receive modes. Broadcast, Multicast, Promisc.
+ */
+
+static void catc_multicast(unsigned char *addr, u8 *multicast)
+{
+	u32 crc;
+
+	crc = ether_crc_le(6, addr);
+	multicast[(crc >> 3) & 0x3f] |= 1 << (crc & 7);
+}
+
+static void catc_set_multicast_list(struct net_device *netdev)
+{
+	struct catc *catc = netdev_priv(netdev);
+	struct dev_mc_list *mc;
+	u8 broadcast[6];
+	u8 rx = RxEnable | RxPolarity | RxMultiCast;
+	int i;
+
+	memset(broadcast, 0xff, 6);
+	memset(catc->multicast, 0, 64);
+
+	catc_multicast(broadcast, catc->multicast);
+	catc_multicast(netdev->dev_addr, catc->multicast);
+
+	if (netdev->flags & IFF_PROMISC) {
+		memset(catc->multicast, 0xff, 64);
+		rx |= (!catc->is_f5u011) ? RxPromisc : AltRxPromisc;
+	} 
+
+	if (netdev->flags & IFF_ALLMULTI) {
+		memset(catc->multicast, 0xff, 64);
+	} else {
+		for (i = 0, mc = netdev->mc_list; mc && i < netdev->mc_count; i++, mc = mc->next) {
+			u32 crc = ether_crc_le(6, mc->dmi_addr);
+			if (!catc->is_f5u011) {
+				catc->multicast[(crc >> 3) & 0x3f] |= 1 << (crc & 7);
+			} else {
+				catc->multicast[7-(crc >> 29)] |= 1 << ((crc >> 26) & 7);
+			}
+		}
+	}
+	if (!catc->is_f5u011) {
+		catc_set_reg_async(catc, RxUnit, rx);
+		catc_write_mem_async(catc, 0xfa80, catc->multicast, 64);
+	} else {
+		f5u011_mchash_async(catc, catc->multicast);
+		if (catc->rxmode[0] != rx) {
+			catc->rxmode[0] = rx;
+			dbg("Setting RX mode to %2.2X %2.2X", catc->rxmode[0], catc->rxmode[1]);
+			f5u011_rxmode_async(catc, catc->rxmode);
+		}
+	}
+}
+
+static void catc_get_drvinfo(struct net_device *dev,
+			     struct ethtool_drvinfo *info)
+{
+	struct catc *catc = netdev_priv(dev);
+	strncpy(info->driver, driver_name, ETHTOOL_BUSINFO_LEN);
+	strncpy(info->version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN);
+	usb_make_path (catc->usbdev, info->bus_info, sizeof info->bus_info);
+}
+
+static int catc_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct catc *catc = netdev_priv(dev);
+	if (!catc->is_f5u011)
+		return -EOPNOTSUPP;
+
+	cmd->supported = SUPPORTED_10baseT_Half | SUPPORTED_TP;
+	cmd->advertising = ADVERTISED_10baseT_Half | ADVERTISED_TP;
+	cmd->speed = SPEED_10;
+	cmd->duplex = DUPLEX_HALF;
+	cmd->port = PORT_TP; 
+	cmd->phy_address = 0;
+	cmd->transceiver = XCVR_INTERNAL;
+	cmd->autoneg = AUTONEG_DISABLE;
+	cmd->maxtxpkt = 1;
+	cmd->maxrxpkt = 1;
+	return 0;
+}
+
+static struct ethtool_ops ops = {
+	.get_drvinfo = catc_get_drvinfo,
+	.get_settings = catc_get_settings,
+	.get_link = ethtool_op_get_link
+};
+
+/*
+ * Open, close.
+ */
+
+static int catc_open(struct net_device *netdev)
+{
+	struct catc *catc = netdev_priv(netdev);
+	int status;
+
+	catc->irq_urb->dev = catc->usbdev;
+	if ((status = usb_submit_urb(catc->irq_urb, GFP_KERNEL)) < 0) {
+		err("submit(irq_urb) status %d", status);
+		return -1;
+	}
+
+	netif_start_queue(netdev);
+
+	if (!catc->is_f5u011)
+		mod_timer(&catc->timer, jiffies + STATS_UPDATE);
+
+	return 0;
+}
+
+static int catc_stop(struct net_device *netdev)
+{
+	struct catc *catc = netdev_priv(netdev);
+
+	netif_stop_queue(netdev);
+
+	if (!catc->is_f5u011)
+		del_timer_sync(&catc->timer);
+
+	usb_kill_urb(catc->rx_urb);
+	usb_kill_urb(catc->tx_urb);
+	usb_kill_urb(catc->irq_urb);
+	usb_kill_urb(catc->ctrl_urb);
+
+	return 0;
+}
+
+/*
+ * USB probe, disconnect.
+ */
+
+static int catc_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_device *usbdev = interface_to_usbdev(intf);
+	struct net_device *netdev;
+	struct catc *catc;
+	u8 broadcast[6];
+	int i, pktsz;
+
+	if (usb_set_interface(usbdev,
+			intf->altsetting->desc.bInterfaceNumber, 1)) {
+                err("Can't set altsetting 1.");
+		return -EIO;
+	}
+
+	netdev = alloc_etherdev(sizeof(struct catc));
+	if (!netdev)
+		return -ENOMEM;
+
+	catc = netdev_priv(netdev);
+
+	netdev->open = catc_open;
+	netdev->hard_start_xmit = catc_hard_start_xmit;
+	netdev->stop = catc_stop;
+	netdev->get_stats = catc_get_stats;
+	netdev->tx_timeout = catc_tx_timeout;
+	netdev->watchdog_timeo = TX_TIMEOUT;
+	netdev->set_multicast_list = catc_set_multicast_list;
+	SET_ETHTOOL_OPS(netdev, &ops);
+
+	catc->usbdev = usbdev;
+	catc->netdev = netdev;
+
+	spin_lock_init(&catc->tx_lock);
+	spin_lock_init(&catc->ctrl_lock);
+
+	init_timer(&catc->timer);
+	catc->timer.data = (long) catc;
+	catc->timer.function = catc_stats_timer;
+
+	catc->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL);
+	catc->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+	catc->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+	catc->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if ((!catc->ctrl_urb) || (!catc->tx_urb) || 
+	    (!catc->rx_urb) || (!catc->irq_urb)) {
+		err("No free urbs available.");
+		if (catc->ctrl_urb)
+			usb_free_urb(catc->ctrl_urb);
+		if (catc->tx_urb)
+			usb_free_urb(catc->tx_urb);
+		if (catc->rx_urb)
+			usb_free_urb(catc->rx_urb);
+		if (catc->irq_urb)
+			usb_free_urb(catc->irq_urb);
+		free_netdev(netdev);
+		return -ENOMEM;
+	}
+
+	/* The F5U011 has the same vendor/product as the netmate but a device version of 0x130 */
+	if (le16_to_cpu(usbdev->descriptor.idVendor) == 0x0423 && 
+	    le16_to_cpu(usbdev->descriptor.idProduct) == 0xa &&
+	    le16_to_cpu(catc->usbdev->descriptor.bcdDevice) == 0x0130) {
+		dbg("Testing for f5u011");
+		catc->is_f5u011 = 1;		
+		atomic_set(&catc->recq_sz, 0);
+		pktsz = RX_PKT_SZ;
+	} else {
+		pktsz = RX_MAX_BURST * (PKT_SZ + 2);
+	}
+	
+	usb_fill_control_urb(catc->ctrl_urb, usbdev, usb_sndctrlpipe(usbdev, 0),
+		NULL, NULL, 0, catc_ctrl_done, catc);
+
+	usb_fill_bulk_urb(catc->tx_urb, usbdev, usb_sndbulkpipe(usbdev, 1),
+		NULL, 0, catc_tx_done, catc);
+
+	usb_fill_bulk_urb(catc->rx_urb, usbdev, usb_rcvbulkpipe(usbdev, 1),
+		catc->rx_buf, pktsz, catc_rx_done, catc);
+
+	usb_fill_int_urb(catc->irq_urb, usbdev, usb_rcvintpipe(usbdev, 2),
+                catc->irq_buf, 2, catc_irq_done, catc, 1);
+
+	if (!catc->is_f5u011) {
+		dbg("Checking memory size\n");
+
+		i = 0x12345678;
+		catc_write_mem(catc, 0x7a80, &i, 4);
+		i = 0x87654321;	
+		catc_write_mem(catc, 0xfa80, &i, 4);
+		catc_read_mem(catc, 0x7a80, &i, 4);
+	  
+		switch (i) {
+		case 0x12345678:
+			catc_set_reg(catc, TxBufCount, 8);
+			catc_set_reg(catc, RxBufCount, 32);
+			dbg("64k Memory\n");
+			break;
+		default:
+			warn("Couldn't detect memory size, assuming 32k");
+		case 0x87654321:
+			catc_set_reg(catc, TxBufCount, 4);
+			catc_set_reg(catc, RxBufCount, 16);
+			dbg("32k Memory\n");
+			break;
+		}
+	  
+		dbg("Getting MAC from SEEROM.");
+	  
+		catc_get_mac(catc, netdev->dev_addr);
+		
+		dbg("Setting MAC into registers.");
+	  
+		for (i = 0; i < 6; i++)
+			catc_set_reg(catc, StationAddr0 - i, netdev->dev_addr[i]);
+		
+		dbg("Filling the multicast list.");
+	  
+		memset(broadcast, 0xff, 6);
+		catc_multicast(broadcast, catc->multicast);
+		catc_multicast(netdev->dev_addr, catc->multicast);
+		catc_write_mem(catc, 0xfa80, catc->multicast, 64);
+		
+		dbg("Clearing error counters.");
+		
+		for (i = 0; i < 8; i++)
+			catc_set_reg(catc, EthStats + i, 0);
+		catc->last_stats = jiffies;
+		
+		dbg("Enabling.");
+		
+		catc_set_reg(catc, MaxBurst, RX_MAX_BURST);
+		catc_set_reg(catc, OpModes, OpTxMerge | OpRxMerge | OpLenInclude | Op3MemWaits);
+		catc_set_reg(catc, LEDCtrl, LEDLink);
+		catc_set_reg(catc, RxUnit, RxEnable | RxPolarity | RxMultiCast);
+	} else {
+		dbg("Performing reset\n");
+		catc_reset(catc);
+		catc_get_mac(catc, netdev->dev_addr);
+		
+		dbg("Setting RX Mode");
+		catc->rxmode[0] = RxEnable | RxPolarity | RxMultiCast;
+		catc->rxmode[1] = 0;
+		f5u011_rxmode(catc, catc->rxmode);
+	}
+	dbg("Init done.");
+	printk(KERN_INFO "%s: %s USB Ethernet at usb-%s-%s, ",
+	       netdev->name, (catc->is_f5u011) ? "Belkin F5U011" : "CATC EL1210A NetMate",
+	       usbdev->bus->bus_name, usbdev->devpath);
+	for (i = 0; i < 5; i++) printk("%2.2x:", netdev->dev_addr[i]);
+	printk("%2.2x.\n", netdev->dev_addr[i]);
+	usb_set_intfdata(intf, catc);
+
+	SET_NETDEV_DEV(netdev, &intf->dev);
+	if (register_netdev(netdev) != 0) {
+		usb_set_intfdata(intf, NULL);
+		usb_free_urb(catc->ctrl_urb);
+		usb_free_urb(catc->tx_urb);
+		usb_free_urb(catc->rx_urb);
+		usb_free_urb(catc->irq_urb);
+		free_netdev(netdev);
+		return -EIO;
+	}
+	return 0;
+}
+
+static void catc_disconnect(struct usb_interface *intf)
+{
+	struct catc *catc = usb_get_intfdata(intf);
+
+	usb_set_intfdata(intf, NULL);
+	if (catc) {
+		unregister_netdev(catc->netdev);
+		usb_free_urb(catc->ctrl_urb);
+		usb_free_urb(catc->tx_urb);
+		usb_free_urb(catc->rx_urb);
+		usb_free_urb(catc->irq_urb);
+		free_netdev(catc->netdev);
+	}
+}
+
+/*
+ * Module functions and tables.
+ */
+
+static struct usb_device_id catc_id_table [] = {
+	{ USB_DEVICE(0x0423, 0xa) },	/* CATC Netmate, Belkin F5U011 */
+	{ USB_DEVICE(0x0423, 0xc) },	/* CATC Netmate II, Belkin F5U111 */
+	{ USB_DEVICE(0x08d1, 0x1) },	/* smartBridges smartNIC */
+	{ }
+};
+
+MODULE_DEVICE_TABLE(usb, catc_id_table);
+
+static struct usb_driver catc_driver = {
+	.owner =	THIS_MODULE,
+	.name =		driver_name,
+	.probe =	catc_probe,
+	.disconnect =	catc_disconnect,
+	.id_table =	catc_id_table,
+};
+
+static int __init catc_init(void)
+{
+	int result = usb_register(&catc_driver);
+	if (result == 0)
+		info(DRIVER_VERSION " " DRIVER_DESC);
+	return result;
+}
+
+static void __exit catc_exit(void)
+{
+	usb_deregister(&catc_driver);
+}
+
+module_init(catc_init);
+module_exit(catc_exit);
diff --git a/drivers/usb/net/kaweth.c b/drivers/usb/net/kaweth.c
new file mode 100644
index 0000000..a9a7cf4
--- /dev/null
+++ b/drivers/usb/net/kaweth.c
@@ -0,0 +1,1271 @@
+/****************************************************************
+ *
+ *     kaweth.c - driver for KL5KUSB101 based USB->Ethernet
+ *
+ *     (c) 2000 Interlan Communications
+ *     (c) 2000 Stephane Alnet
+ *     (C) 2001 Brad Hards
+ *     (C) 2002 Oliver Neukum
+ *
+ *     Original author: The Zapman <zapman@interlan.net>
+ *     Inspired by, and much credit goes to Michael Rothwell
+ *     <rothwell@interlan.net> for the test equipment, help, and patience
+ *     Based off of (and with thanks to) Petko Manolov's pegaus.c driver.
+ *     Also many thanks to Joel Silverman and Ed Surprenant at Kawasaki
+ *     for providing the firmware and driver resources.
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2, or
+ *     (at your option) any later version.
+ *
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ *
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software Foundation,
+ *     Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ ****************************************************************/
+
+/* TODO:
+ * Fix in_interrupt() problem
+ * Develop test procedures for USB net interfaces
+ * Run test procedures
+ * Fix bugs from previous two steps
+ * Snoop other OSs for any tricks we're not doing
+ * SMP locking
+ * Reduce arbitrary timeouts
+ * Smart multicast support
+ * Temporary MAC change support
+ * Tunable SOFs parameter - ioctl()?
+ * Ethernet stats collection
+ * Code formatting improvements
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/usb.h>
+#include <linux/types.h>
+#include <linux/ethtool.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/wait.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+#include <asm/byteorder.h>
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define kaweth_dbg(format, arg...) printk(KERN_DEBUG __FILE__ ": " format "\n" ,##arg)
+#else
+#define kaweth_dbg(format, arg...) do {} while (0)
+#endif
+#define kaweth_err(format, arg...) printk(KERN_ERR __FILE__ ": " format "\n" ,##arg)
+#define kaweth_info(format, arg...) printk(KERN_INFO __FILE__ ": " format "\n" , ##arg)
+#define kaweth_warn(format, arg...) printk(KERN_WARNING __FILE__ ": " format "\n" , ##arg)
+
+
+#include "kawethfw.h"
+
+#define KAWETH_MTU			1514
+#define KAWETH_BUF_SIZE			1664
+#define KAWETH_TX_TIMEOUT		(5 * HZ)
+#define KAWETH_SCRATCH_SIZE		32
+#define KAWETH_FIRMWARE_BUF_SIZE	4096
+#define KAWETH_CONTROL_TIMEOUT		(30 * HZ)
+
+#define KAWETH_STATUS_BROKEN		0x0000001
+#define KAWETH_STATUS_CLOSING		0x0000002
+
+#define KAWETH_PACKET_FILTER_PROMISCUOUS	0x01
+#define KAWETH_PACKET_FILTER_ALL_MULTICAST	0x02
+#define KAWETH_PACKET_FILTER_DIRECTED		0x04
+#define KAWETH_PACKET_FILTER_BROADCAST		0x08
+#define KAWETH_PACKET_FILTER_MULTICAST		0x10
+
+/* Table 7 */
+#define KAWETH_COMMAND_GET_ETHERNET_DESC	0x00
+#define KAWETH_COMMAND_MULTICAST_FILTERS        0x01
+#define KAWETH_COMMAND_SET_PACKET_FILTER	0x02
+#define KAWETH_COMMAND_STATISTICS               0x03
+#define KAWETH_COMMAND_SET_TEMP_MAC     	0x06
+#define KAWETH_COMMAND_GET_TEMP_MAC             0x07
+#define KAWETH_COMMAND_SET_URB_SIZE		0x08
+#define KAWETH_COMMAND_SET_SOFS_WAIT		0x09
+#define KAWETH_COMMAND_SCAN			0xFF
+
+#define KAWETH_SOFS_TO_WAIT			0x05
+
+#define INTBUFFERSIZE				4
+
+#define STATE_OFFSET				0
+#define STATE_MASK				0x40
+#define	STATE_SHIFT				5
+
+
+MODULE_AUTHOR("Michael Zappe <zapman@interlan.net>, Stephane Alnet <stephane@u-picardie.fr>, Brad Hards <bhards@bigpond.net.au> and Oliver Neukum <oliver@neukum.org>");
+MODULE_DESCRIPTION("KL5USB101 USB Ethernet driver");
+MODULE_LICENSE("GPL");
+
+static const char driver_name[] = "kaweth";
+
+static int kaweth_probe(
+		struct usb_interface *intf,
+		const struct usb_device_id *id	/* from id_table */
+	);
+static void kaweth_disconnect(struct usb_interface *intf);
+static int kaweth_internal_control_msg(struct usb_device *usb_dev,
+				       unsigned int pipe,
+				       struct usb_ctrlrequest *cmd, void *data,
+				       int len, int timeout);
+
+/****************************************************************
+ *     usb_device_id
+ ****************************************************************/
+static struct usb_device_id usb_klsi_table[] = {
+	{ USB_DEVICE(0x03e8, 0x0008) }, /* AOX Endpoints USB Ethernet */
+	{ USB_DEVICE(0x04bb, 0x0901) }, /* I-O DATA USB-ET/T */
+	{ USB_DEVICE(0x0506, 0x03e8) }, /* 3Com 3C19250 */
+	{ USB_DEVICE(0x0506, 0x11f8) }, /* 3Com 3C460 */
+	{ USB_DEVICE(0x0557, 0x2002) }, /* ATEN USB Ethernet */
+	{ USB_DEVICE(0x0557, 0x4000) }, /* D-Link DSB-650C */
+	{ USB_DEVICE(0x0565, 0x0002) }, /* Peracom Enet */
+	{ USB_DEVICE(0x0565, 0x0003) }, /* Optus@Home UEP1045A */
+	{ USB_DEVICE(0x0565, 0x0005) }, /* Peracom Enet2 */
+	{ USB_DEVICE(0x05e9, 0x0008) }, /* KLSI KL5KUSB101B */
+	{ USB_DEVICE(0x05e9, 0x0009) }, /* KLSI KL5KUSB101B (Board change) */
+	{ USB_DEVICE(0x066b, 0x2202) }, /* Linksys USB10T */
+	{ USB_DEVICE(0x06e1, 0x0008) }, /* ADS USB-10BT */
+	{ USB_DEVICE(0x06e1, 0x0009) }, /* ADS USB-10BT */
+	{ USB_DEVICE(0x0707, 0x0100) }, /* SMC 2202USB */
+	{ USB_DEVICE(0x07aa, 0x0001) }, /* Correga K.K. */
+	{ USB_DEVICE(0x07b8, 0x4000) }, /* D-Link DU-E10 */
+	{ USB_DEVICE(0x0846, 0x1001) }, /* NetGear EA-101 */
+	{ USB_DEVICE(0x0846, 0x1002) }, /* NetGear EA-101 */
+	{ USB_DEVICE(0x085a, 0x0008) }, /* PortGear Ethernet Adapter */
+	{ USB_DEVICE(0x085a, 0x0009) }, /* PortGear Ethernet Adapter */
+	{ USB_DEVICE(0x087d, 0x5704) }, /* Jaton USB Ethernet Device Adapter */
+	{ USB_DEVICE(0x0951, 0x0008) }, /* Kingston Technology USB Ethernet Adapter */
+	{ USB_DEVICE(0x095a, 0x3003) }, /* Portsmith Express Ethernet Adapter */
+	{ USB_DEVICE(0x10bd, 0x1427) }, /* ASANTE USB To Ethernet Adapter */
+	{ USB_DEVICE(0x1342, 0x0204) }, /* Mobility USB-Ethernet Adapter */
+	{ USB_DEVICE(0x13d2, 0x0400) }, /* Shark Pocket Adapter */
+	{ USB_DEVICE(0x1485, 0x0001) },	/* Silicom U2E */
+	{ USB_DEVICE(0x1485, 0x0002) }, /* Psion Dacom Gold Port Ethernet */
+	{ USB_DEVICE(0x1645, 0x0005) }, /* Entrega E45 */
+	{ USB_DEVICE(0x1645, 0x0008) }, /* Entrega USB Ethernet Adapter */
+	{ USB_DEVICE(0x1645, 0x8005) }, /* PortGear Ethernet Adapter */
+	{ USB_DEVICE(0x2001, 0x4000) }, /* D-link DSB-650C */
+	{} /* Null terminator */
+};
+
+MODULE_DEVICE_TABLE (usb, usb_klsi_table);
+
+/****************************************************************
+ *     kaweth_driver
+ ****************************************************************/
+static struct usb_driver kaweth_driver = {
+	.owner =	THIS_MODULE,
+	.name =		driver_name,
+	.probe =	kaweth_probe,
+	.disconnect =	kaweth_disconnect,
+	.id_table =     usb_klsi_table,
+};
+
+typedef __u8 eth_addr_t[6];
+
+/****************************************************************
+ *     usb_eth_dev
+ ****************************************************************/
+struct usb_eth_dev {
+	char *name;
+	__u16 vendor;
+	__u16 device;
+	void *pdata;
+};
+
+/****************************************************************
+ *     kaweth_ethernet_configuration
+ *     Refer Table 8
+ ****************************************************************/
+struct kaweth_ethernet_configuration
+{
+	__u8 size;
+	__u8 reserved1;
+	__u8 reserved2;
+	eth_addr_t hw_addr;
+	__u32 statistics_mask;
+	__le16 segment_size;
+	__u16 max_multicast_filters;
+	__u8 reserved3;
+} __attribute__ ((packed));
+
+/****************************************************************
+ *     kaweth_device
+ ****************************************************************/
+struct kaweth_device
+{
+	spinlock_t device_lock;
+
+	__u32 status;
+	int end;
+	int removed;
+	int suspend_lowmem_rx;
+	int suspend_lowmem_ctrl;
+	int linkstate;
+	struct work_struct lowmem_work;
+
+	struct usb_device *dev;
+	struct net_device *net;
+	wait_queue_head_t term_wait;
+
+	struct urb *rx_urb;
+	struct urb *tx_urb;
+	struct urb *irq_urb;
+
+	dma_addr_t intbufferhandle;
+	__u8 *intbuffer;
+	dma_addr_t rxbufferhandle;
+	__u8 *rx_buf;
+
+	
+	struct sk_buff *tx_skb;
+
+	__u8 *firmware_buf;
+	__u8 scratch[KAWETH_SCRATCH_SIZE];
+	__u16 packet_filter_bitmap;
+
+	struct kaweth_ethernet_configuration configuration;
+
+	struct net_device_stats stats;
+};
+
+
+/****************************************************************
+ *     kaweth_control
+ ****************************************************************/
+static int kaweth_control(struct kaweth_device *kaweth,
+			  unsigned int pipe,
+			  __u8 request,
+			  __u8 requesttype,
+			  __u16 value,
+			  __u16 index,
+			  void *data,
+			  __u16 size,
+			  int timeout)
+{
+	struct usb_ctrlrequest *dr;
+
+	kaweth_dbg("kaweth_control()");
+
+	if(in_interrupt()) {
+		kaweth_dbg("in_interrupt()");
+		return -EBUSY;
+	}
+
+	dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
+
+	if (!dr) {
+		kaweth_dbg("kmalloc() failed");
+		return -ENOMEM;
+	}
+
+	dr->bRequestType= requesttype;
+	dr->bRequest = request;
+	dr->wValue = cpu_to_le16p(&value);
+	dr->wIndex = cpu_to_le16p(&index);
+	dr->wLength = cpu_to_le16p(&size);
+
+	return kaweth_internal_control_msg(kaweth->dev,
+					pipe,
+					dr,
+					data,
+					size,
+					timeout);
+}
+
+/****************************************************************
+ *     kaweth_read_configuration
+ ****************************************************************/
+static int kaweth_read_configuration(struct kaweth_device *kaweth)
+{
+	int retval;
+
+	kaweth_dbg("Reading kaweth configuration");
+
+	retval = kaweth_control(kaweth,
+				usb_rcvctrlpipe(kaweth->dev, 0),
+				KAWETH_COMMAND_GET_ETHERNET_DESC,
+				USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE,
+				0,
+				0,
+				(void *)&kaweth->configuration,
+				sizeof(kaweth->configuration),
+				KAWETH_CONTROL_TIMEOUT);
+
+	return retval;
+}
+
+/****************************************************************
+ *     kaweth_set_urb_size
+ ****************************************************************/
+static int kaweth_set_urb_size(struct kaweth_device *kaweth, __u16 urb_size)
+{
+	int retval;
+
+	kaweth_dbg("Setting URB size to %d", (unsigned)urb_size);
+
+	retval = kaweth_control(kaweth,
+				usb_sndctrlpipe(kaweth->dev, 0),
+				KAWETH_COMMAND_SET_URB_SIZE,
+				USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+				urb_size,
+				0,
+				(void *)&kaweth->scratch,
+				0,
+				KAWETH_CONTROL_TIMEOUT);
+
+	return retval;
+}
+
+/****************************************************************
+ *     kaweth_set_sofs_wait
+ ****************************************************************/
+static int kaweth_set_sofs_wait(struct kaweth_device *kaweth, __u16 sofs_wait)
+{
+	int retval;
+
+	kaweth_dbg("Set SOFS wait to %d", (unsigned)sofs_wait);
+
+	retval = kaweth_control(kaweth,
+				usb_sndctrlpipe(kaweth->dev, 0),
+				KAWETH_COMMAND_SET_SOFS_WAIT,
+				USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+				sofs_wait,
+				0,
+				(void *)&kaweth->scratch,
+				0,
+				KAWETH_CONTROL_TIMEOUT);
+
+	return retval;
+}
+
+/****************************************************************
+ *     kaweth_set_receive_filter
+ ****************************************************************/
+static int kaweth_set_receive_filter(struct kaweth_device *kaweth,
+				     __u16 receive_filter)
+{
+	int retval;
+
+	kaweth_dbg("Set receive filter to %d", (unsigned)receive_filter);
+
+	retval = kaweth_control(kaweth,
+				usb_sndctrlpipe(kaweth->dev, 0),
+				KAWETH_COMMAND_SET_PACKET_FILTER,
+				USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+				receive_filter,
+				0,
+				(void *)&kaweth->scratch,
+				0,
+				KAWETH_CONTROL_TIMEOUT);
+
+	return retval;
+}
+
+/****************************************************************
+ *     kaweth_download_firmware
+ ****************************************************************/
+static int kaweth_download_firmware(struct kaweth_device *kaweth,
+				    __u8 *data,
+				    __u16 data_len,
+				    __u8 interrupt,
+				    __u8 type)
+{
+	if(data_len > KAWETH_FIRMWARE_BUF_SIZE)	{
+		kaweth_err("Firmware too big: %d", data_len);
+		return -ENOSPC;
+	}
+
+	memcpy(kaweth->firmware_buf, data, data_len);
+
+	kaweth->firmware_buf[2] = (data_len & 0xFF) - 7;
+	kaweth->firmware_buf[3] = data_len >> 8;
+	kaweth->firmware_buf[4] = type;
+	kaweth->firmware_buf[5] = interrupt;
+
+	kaweth_dbg("High: %i, Low:%i", kaweth->firmware_buf[3],
+		   kaweth->firmware_buf[2]);
+
+	kaweth_dbg("Downloading firmware at %p to kaweth device at %p",
+	    data,
+	    kaweth);
+	kaweth_dbg("Firmware length: %d", data_len);
+
+	return kaweth_control(kaweth,
+		              usb_sndctrlpipe(kaweth->dev, 0),
+			      KAWETH_COMMAND_SCAN,
+			      USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+			      0,
+			      0,
+			      (void *)kaweth->firmware_buf,
+			      data_len,
+			      KAWETH_CONTROL_TIMEOUT);
+}
+
+/****************************************************************
+ *     kaweth_trigger_firmware
+ ****************************************************************/
+static int kaweth_trigger_firmware(struct kaweth_device *kaweth,
+				   __u8 interrupt)
+{
+	kaweth->firmware_buf[0] = 0xB6;
+	kaweth->firmware_buf[1] = 0xC3;
+	kaweth->firmware_buf[2] = 0x01;
+	kaweth->firmware_buf[3] = 0x00;
+	kaweth->firmware_buf[4] = 0x06;
+	kaweth->firmware_buf[5] = interrupt;
+	kaweth->firmware_buf[6] = 0x00;
+	kaweth->firmware_buf[7] = 0x00;
+
+	kaweth_dbg("Triggering firmware");
+
+	return kaweth_control(kaweth,
+			      usb_sndctrlpipe(kaweth->dev, 0),
+			      KAWETH_COMMAND_SCAN,
+			      USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+			      0,
+			      0,
+			      (void *)kaweth->firmware_buf,
+			      8,
+			      KAWETH_CONTROL_TIMEOUT);
+}
+
+/****************************************************************
+ *     kaweth_reset
+ ****************************************************************/
+static int kaweth_reset(struct kaweth_device *kaweth)
+{
+	int result;
+
+	kaweth_dbg("kaweth_reset(%p)", kaweth);
+	result = kaweth_control(kaweth,
+				usb_sndctrlpipe(kaweth->dev, 0),
+				USB_REQ_SET_CONFIGURATION,
+				0,
+				kaweth->dev->config[0].desc.bConfigurationValue,
+				0,
+				NULL,
+				0,
+				KAWETH_CONTROL_TIMEOUT);
+
+	udelay(10000);
+
+	kaweth_dbg("kaweth_reset() returns %d.",result);
+
+	return result;
+}
+
+static void kaweth_usb_receive(struct urb *, struct pt_regs *regs);
+static int kaweth_resubmit_rx_urb(struct kaweth_device *, int);
+
+/****************************************************************
+	int_callback
+*****************************************************************/
+
+static void kaweth_resubmit_int_urb(struct kaweth_device *kaweth, int mf)
+{
+	int status;
+
+	status = usb_submit_urb (kaweth->irq_urb, mf);
+	if (unlikely(status == -ENOMEM)) {
+		kaweth->suspend_lowmem_ctrl = 1;
+		schedule_delayed_work(&kaweth->lowmem_work, HZ/4);
+	} else {
+		kaweth->suspend_lowmem_ctrl = 0;
+	}
+
+	if (status)
+		err ("can't resubmit intr, %s-%s, status %d",
+				kaweth->dev->bus->bus_name,
+				kaweth->dev->devpath, status);
+}
+
+static void int_callback(struct urb *u, struct pt_regs *regs)
+{
+	struct kaweth_device *kaweth = u->context;
+	int act_state;
+
+	switch (u->status) {
+	case 0:			/* success */
+		break;
+	case -ECONNRESET:	/* unlink */
+	case -ENOENT:
+	case -ESHUTDOWN:
+		return;
+	/* -EPIPE:  should clear the halt */
+	default:		/* error */
+		goto resubmit;
+	}
+
+	/* we check the link state to report changes */
+	if (kaweth->linkstate != (act_state = ( kaweth->intbuffer[STATE_OFFSET] | STATE_MASK) >> STATE_SHIFT)) {
+		if (!act_state)
+			netif_carrier_on(kaweth->net);
+		else
+			netif_carrier_off(kaweth->net);
+
+		kaweth->linkstate = act_state;
+	}
+resubmit:
+	kaweth_resubmit_int_urb(kaweth, GFP_ATOMIC);
+}
+
+static void kaweth_resubmit_tl(void *d)
+{
+	struct kaweth_device *kaweth = (struct kaweth_device *)d;
+
+	if (kaweth->status | KAWETH_STATUS_CLOSING)
+		return;
+
+	if (kaweth->suspend_lowmem_rx)
+		kaweth_resubmit_rx_urb(kaweth, GFP_NOIO);
+
+	if (kaweth->suspend_lowmem_ctrl)
+		kaweth_resubmit_int_urb(kaweth, GFP_NOIO);
+}
+
+
+/****************************************************************
+ *     kaweth_resubmit_rx_urb
+ ****************************************************************/
+static int kaweth_resubmit_rx_urb(struct kaweth_device *kaweth,
+						int mem_flags)
+{
+	int result;
+
+	usb_fill_bulk_urb(kaweth->rx_urb,
+		      kaweth->dev,
+		      usb_rcvbulkpipe(kaweth->dev, 1),
+		      kaweth->rx_buf,
+		      KAWETH_BUF_SIZE,
+		      kaweth_usb_receive,
+		      kaweth);
+	kaweth->rx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	kaweth->rx_urb->transfer_dma = kaweth->rxbufferhandle;
+
+	if((result = usb_submit_urb(kaweth->rx_urb, mem_flags))) {
+		if (result == -ENOMEM) {
+			kaweth->suspend_lowmem_rx = 1;
+			schedule_delayed_work(&kaweth->lowmem_work, HZ/4);
+		}
+		kaweth_err("resubmitting rx_urb %d failed", result);
+	} else {
+		kaweth->suspend_lowmem_rx = 0;
+	}
+
+	return result;
+}
+
+static void kaweth_async_set_rx_mode(struct kaweth_device *kaweth);
+
+/****************************************************************
+ *     kaweth_usb_receive
+ ****************************************************************/
+static void kaweth_usb_receive(struct urb *urb, struct pt_regs *regs)
+{
+	struct kaweth_device *kaweth = urb->context;
+	struct net_device *net = kaweth->net;
+
+	int count = urb->actual_length;
+	int count2 = urb->transfer_buffer_length;
+
+	__u16 pkt_len = le16_to_cpup((__le16 *)kaweth->rx_buf);
+
+	struct sk_buff *skb;
+
+	if(unlikely(urb->status == -ECONNRESET || urb->status == -ESHUTDOWN))
+	/* we are killed - set a flag and wake the disconnect handler */
+	{
+		kaweth->end = 1;
+		wake_up(&kaweth->term_wait);
+		return;
+	}
+
+	if (kaweth->status & KAWETH_STATUS_CLOSING)
+		return;
+
+	if(urb->status && urb->status != -EREMOTEIO && count != 1) {
+		kaweth_err("%s RX status: %d count: %d packet_len: %d",
+                           net->name,
+			   urb->status,
+			   count,
+			   (int)pkt_len);
+		kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC);
+                return;
+	}
+
+	if(kaweth->net && (count > 2)) {
+		if(pkt_len > (count - 2)) {
+			kaweth_err("Packet length too long for USB frame (pkt_len: %x, count: %x)",pkt_len, count);
+			kaweth_err("Packet len & 2047: %x", pkt_len & 2047);
+			kaweth_err("Count 2: %x", count2);
+		        kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC);
+                        return;
+                }
+
+		if(!(skb = dev_alloc_skb(pkt_len+2))) {
+		        kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC);
+                        return;
+		}
+
+		skb_reserve(skb, 2);    /* Align IP on 16 byte boundaries */
+
+		skb->dev = net;
+
+		eth_copy_and_sum(skb, kaweth->rx_buf + 2, pkt_len, 0);
+
+		skb_put(skb, pkt_len);
+
+		skb->protocol = eth_type_trans(skb, net);
+
+		netif_rx(skb);
+
+		kaweth->stats.rx_packets++;
+		kaweth->stats.rx_bytes += pkt_len;
+	}
+
+	kaweth_resubmit_rx_urb(kaweth, GFP_ATOMIC);
+}
+
+/****************************************************************
+ *     kaweth_open
+ ****************************************************************/
+static int kaweth_open(struct net_device *net)
+{
+	struct kaweth_device *kaweth = netdev_priv(net);
+	int res;
+
+	kaweth_dbg("Opening network device.");
+
+	res = kaweth_resubmit_rx_urb(kaweth, GFP_KERNEL);
+	if (res)
+		return -EIO;
+
+	usb_fill_int_urb(
+		kaweth->irq_urb,
+		kaweth->dev,
+		usb_rcvintpipe(kaweth->dev, 3),
+		kaweth->intbuffer,
+		INTBUFFERSIZE,
+		int_callback,
+		kaweth,
+		250); /* overriding the descriptor */
+	kaweth->irq_urb->transfer_dma = kaweth->intbufferhandle;
+	kaweth->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	res = usb_submit_urb(kaweth->irq_urb, GFP_KERNEL);
+	if (res) {
+		usb_kill_urb(kaweth->rx_urb);
+		return -EIO;
+	}
+
+	netif_start_queue(net);
+
+	kaweth_async_set_rx_mode(kaweth);
+	return 0;
+}
+
+/****************************************************************
+ *     kaweth_close
+ ****************************************************************/
+static int kaweth_close(struct net_device *net)
+{
+	struct kaweth_device *kaweth = netdev_priv(net);
+
+	netif_stop_queue(net);
+
+	kaweth->status |= KAWETH_STATUS_CLOSING;
+
+	usb_kill_urb(kaweth->irq_urb);
+	usb_kill_urb(kaweth->rx_urb);
+
+	flush_scheduled_work();
+
+	/* a scheduled work may have resubmitted,
+	   we hit them again */
+	usb_kill_urb(kaweth->irq_urb);
+	usb_kill_urb(kaweth->rx_urb);
+
+	kaweth->status &= ~KAWETH_STATUS_CLOSING;
+
+	return 0;
+}
+
+static void kaweth_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+
+	strlcpy(info->driver, driver_name, sizeof(info->driver));
+}
+
+static struct ethtool_ops ops = {
+	.get_drvinfo = kaweth_get_drvinfo
+};
+
+/****************************************************************
+ *     kaweth_usb_transmit_complete
+ ****************************************************************/
+static void kaweth_usb_transmit_complete(struct urb *urb, struct pt_regs *regs)
+{
+	struct kaweth_device *kaweth = urb->context;
+	struct sk_buff *skb = kaweth->tx_skb;
+
+	if (unlikely(urb->status != 0))
+		if (urb->status != -ENOENT)
+			kaweth_dbg("%s: TX status %d.", kaweth->net->name, urb->status);
+
+	netif_wake_queue(kaweth->net);
+	dev_kfree_skb_irq(skb);
+}
+
+/****************************************************************
+ *     kaweth_start_xmit
+ ****************************************************************/
+static int kaweth_start_xmit(struct sk_buff *skb, struct net_device *net)
+{
+	struct kaweth_device *kaweth = netdev_priv(net);
+	__le16 *private_header;
+
+	int res;
+
+	spin_lock(&kaweth->device_lock);
+
+	if (kaweth->removed) {
+	/* our device is undergoing disconnection - we bail out */
+		spin_unlock(&kaweth->device_lock);
+		dev_kfree_skb_irq(skb);
+		return 0;
+	}
+
+	kaweth_async_set_rx_mode(kaweth);
+	netif_stop_queue(net);
+
+	/* We now decide whether we can put our special header into the sk_buff */
+	if (skb_cloned(skb) || skb_headroom(skb) < 2) {
+		/* no such luck - we make our own */
+		struct sk_buff *copied_skb;
+		copied_skb = skb_copy_expand(skb, 2, 0, GFP_ATOMIC);
+		dev_kfree_skb_irq(skb);
+		skb = copied_skb;
+		if (!copied_skb) {
+			kaweth->stats.tx_errors++;
+			netif_start_queue(net);
+			spin_unlock(&kaweth->device_lock);
+			return 0;
+		}
+	}
+
+	private_header = (__le16 *)__skb_push(skb, 2);
+	*private_header = cpu_to_le16(skb->len-2);
+	kaweth->tx_skb = skb;
+
+	usb_fill_bulk_urb(kaweth->tx_urb,
+		      kaweth->dev,
+		      usb_sndbulkpipe(kaweth->dev, 2),
+		      private_header,
+		      skb->len,
+		      kaweth_usb_transmit_complete,
+		      kaweth);
+	kaweth->end = 0;
+	kaweth->tx_urb->transfer_flags |= URB_ASYNC_UNLINK;
+
+	if((res = usb_submit_urb(kaweth->tx_urb, GFP_ATOMIC)))
+	{
+		kaweth_warn("kaweth failed tx_urb %d", res);
+		kaweth->stats.tx_errors++;
+
+		netif_start_queue(net);
+		dev_kfree_skb_irq(skb);
+	}
+	else
+	{
+		kaweth->stats.tx_packets++;
+		kaweth->stats.tx_bytes += skb->len;
+		net->trans_start = jiffies;
+	}
+
+	spin_unlock(&kaweth->device_lock);
+
+	return 0;
+}
+
+/****************************************************************
+ *     kaweth_set_rx_mode
+ ****************************************************************/
+static void kaweth_set_rx_mode(struct net_device *net)
+{
+	struct kaweth_device *kaweth = netdev_priv(net);
+
+	__u16 packet_filter_bitmap = KAWETH_PACKET_FILTER_DIRECTED |
+                                     KAWETH_PACKET_FILTER_BROADCAST |
+		                     KAWETH_PACKET_FILTER_MULTICAST;
+
+	kaweth_dbg("Setting Rx mode to %d", packet_filter_bitmap);
+
+	netif_stop_queue(net);
+
+	if (net->flags & IFF_PROMISC) {
+		packet_filter_bitmap |= KAWETH_PACKET_FILTER_PROMISCUOUS;
+	}
+	else if ((net->mc_count) || (net->flags & IFF_ALLMULTI)) {
+		packet_filter_bitmap |= KAWETH_PACKET_FILTER_ALL_MULTICAST;
+	}
+
+	kaweth->packet_filter_bitmap = packet_filter_bitmap;
+	netif_wake_queue(net);
+}
+
+/****************************************************************
+ *     kaweth_async_set_rx_mode
+ ****************************************************************/
+static void kaweth_async_set_rx_mode(struct kaweth_device *kaweth)
+{
+	__u16 packet_filter_bitmap = kaweth->packet_filter_bitmap;
+	kaweth->packet_filter_bitmap = 0;
+	if (packet_filter_bitmap == 0)
+		return;
+
+	{
+	int result;
+	result = kaweth_control(kaweth,
+				usb_sndctrlpipe(kaweth->dev, 0),
+				KAWETH_COMMAND_SET_PACKET_FILTER,
+				USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+				packet_filter_bitmap,
+				0,
+				(void *)&kaweth->scratch,
+				0,
+				KAWETH_CONTROL_TIMEOUT);
+
+	if(result < 0) {
+		kaweth_err("Failed to set Rx mode: %d", result);
+	}
+	else {
+		kaweth_dbg("Set Rx mode to %d", packet_filter_bitmap);
+	}
+	}
+}
+
+/****************************************************************
+ *     kaweth_netdev_stats
+ ****************************************************************/
+static struct net_device_stats *kaweth_netdev_stats(struct net_device *dev)
+{
+	struct kaweth_device *kaweth = netdev_priv(dev);
+	return &kaweth->stats;
+}
+
+/****************************************************************
+ *     kaweth_tx_timeout
+ ****************************************************************/
+static void kaweth_tx_timeout(struct net_device *net)
+{
+	struct kaweth_device *kaweth = netdev_priv(net);
+
+	kaweth_warn("%s: Tx timed out. Resetting.", net->name);
+	kaweth->stats.tx_errors++;
+	net->trans_start = jiffies;
+
+	usb_unlink_urb(kaweth->tx_urb);
+}
+
+/****************************************************************
+ *     kaweth_probe
+ ****************************************************************/
+static int kaweth_probe(
+		struct usb_interface *intf,
+		const struct usb_device_id *id      /* from id_table */
+	)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct kaweth_device *kaweth;
+	struct net_device *netdev;
+	const eth_addr_t bcast_addr = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+	int result = 0;
+
+	kaweth_dbg("Kawasaki Device Probe (Device number:%d): 0x%4.4x:0x%4.4x:0x%4.4x",
+		 dev->devnum,
+		 le16_to_cpu(dev->descriptor.idVendor),
+		 le16_to_cpu(dev->descriptor.idProduct),
+		 le16_to_cpu(dev->descriptor.bcdDevice));
+
+	kaweth_dbg("Device at %p", dev);
+
+	kaweth_dbg("Descriptor length: %x type: %x",
+		 (int)dev->descriptor.bLength,
+		 (int)dev->descriptor.bDescriptorType);
+
+	netdev = alloc_etherdev(sizeof(*kaweth));
+	if (!netdev)
+		return -ENOMEM;
+
+	kaweth = netdev_priv(netdev);
+	kaweth->dev = dev;
+	kaweth->net = netdev;
+
+	spin_lock_init(&kaweth->device_lock);
+	init_waitqueue_head(&kaweth->term_wait);
+
+	kaweth_dbg("Resetting.");
+
+	kaweth_reset(kaweth);
+
+	/*
+	 * If high byte of bcdDevice is nonzero, firmware is already
+	 * downloaded. Don't try to do it again, or we'll hang the device.
+	 */
+
+	if (le16_to_cpu(dev->descriptor.bcdDevice) >> 8) {
+		kaweth_info("Firmware present in device.");
+	} else {
+		/* Download the firmware */
+		kaweth_info("Downloading firmware...");
+		kaweth->firmware_buf = (__u8 *)__get_free_page(GFP_KERNEL);
+		if ((result = kaweth_download_firmware(kaweth,
+						      kaweth_new_code,
+						      len_kaweth_new_code,
+						      100,
+						      2)) < 0) {
+			kaweth_err("Error downloading firmware (%d)", result);
+			goto err_fw;
+		}
+
+		if ((result = kaweth_download_firmware(kaweth,
+						      kaweth_new_code_fix,
+						      len_kaweth_new_code_fix,
+						      100,
+						      3)) < 0) {
+			kaweth_err("Error downloading firmware fix (%d)", result);
+			goto err_fw;
+		}
+
+		if ((result = kaweth_download_firmware(kaweth,
+						      kaweth_trigger_code,
+						      len_kaweth_trigger_code,
+						      126,
+						      2)) < 0) {
+			kaweth_err("Error downloading trigger code (%d)", result);
+			goto err_fw;
+
+		}
+
+		if ((result = kaweth_download_firmware(kaweth,
+						      kaweth_trigger_code_fix,
+						      len_kaweth_trigger_code_fix,
+						      126,
+						      3)) < 0) {
+			kaweth_err("Error downloading trigger code fix (%d)", result);
+			goto err_fw;
+		}
+
+
+		if ((result = kaweth_trigger_firmware(kaweth, 126)) < 0) {
+			kaweth_err("Error triggering firmware (%d)", result);
+			goto err_fw;
+		}
+
+		/* Device will now disappear for a moment...  */
+		kaweth_info("Firmware loaded.  I'll be back...");
+err_fw:
+		free_page((unsigned long)kaweth->firmware_buf);
+		free_netdev(netdev);
+		return -EIO;
+	}
+
+	result = kaweth_read_configuration(kaweth);
+
+	if(result < 0) {
+		kaweth_err("Error reading configuration (%d), no net device created", result);
+		goto err_free_netdev;
+	}
+
+	kaweth_info("Statistics collection: %x", kaweth->configuration.statistics_mask);
+	kaweth_info("Multicast filter limit: %x", kaweth->configuration.max_multicast_filters & ((1 << 15) - 1));
+	kaweth_info("MTU: %d", le16_to_cpu(kaweth->configuration.segment_size));
+	kaweth_info("Read MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
+		 (int)kaweth->configuration.hw_addr[0],
+		 (int)kaweth->configuration.hw_addr[1],
+		 (int)kaweth->configuration.hw_addr[2],
+		 (int)kaweth->configuration.hw_addr[3],
+		 (int)kaweth->configuration.hw_addr[4],
+		 (int)kaweth->configuration.hw_addr[5]);
+
+	if(!memcmp(&kaweth->configuration.hw_addr,
+                   &bcast_addr,
+		   sizeof(bcast_addr))) {
+		kaweth_err("Firmware not functioning properly, no net device created");
+		goto err_free_netdev;
+	}
+
+	if(kaweth_set_urb_size(kaweth, KAWETH_BUF_SIZE) < 0) {
+		kaweth_dbg("Error setting URB size");
+		goto err_free_netdev;
+	}
+
+	if(kaweth_set_sofs_wait(kaweth, KAWETH_SOFS_TO_WAIT) < 0) {
+		kaweth_err("Error setting SOFS wait");
+		goto err_free_netdev;
+	}
+
+	result = kaweth_set_receive_filter(kaweth,
+                                           KAWETH_PACKET_FILTER_DIRECTED |
+                                           KAWETH_PACKET_FILTER_BROADCAST |
+                                           KAWETH_PACKET_FILTER_MULTICAST);
+
+	if(result < 0) {
+		kaweth_err("Error setting receive filter");
+		goto err_free_netdev;
+	}
+
+	kaweth_dbg("Initializing net device.");
+
+	kaweth->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!kaweth->tx_urb)
+		goto err_free_netdev;
+	kaweth->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!kaweth->rx_urb)
+		goto err_only_tx;
+	kaweth->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!kaweth->irq_urb)
+		goto err_tx_and_rx;
+
+	kaweth->intbuffer = usb_buffer_alloc(	kaweth->dev,
+						INTBUFFERSIZE,
+						GFP_KERNEL,
+						&kaweth->intbufferhandle);
+	if (!kaweth->intbuffer)
+		goto err_tx_and_rx_and_irq;
+	kaweth->rx_buf = usb_buffer_alloc(	kaweth->dev,
+						KAWETH_BUF_SIZE,
+						GFP_KERNEL,
+						&kaweth->rxbufferhandle);
+	if (!kaweth->rx_buf)
+		goto err_all_but_rxbuf;
+
+	memcpy(netdev->broadcast, &bcast_addr, sizeof(bcast_addr));
+	memcpy(netdev->dev_addr, &kaweth->configuration.hw_addr,
+               sizeof(kaweth->configuration.hw_addr));
+
+	netdev->open = kaweth_open;
+	netdev->stop = kaweth_close;
+
+	netdev->watchdog_timeo = KAWETH_TX_TIMEOUT;
+	netdev->tx_timeout = kaweth_tx_timeout;
+
+	netdev->hard_start_xmit = kaweth_start_xmit;
+	netdev->set_multicast_list = kaweth_set_rx_mode;
+	netdev->get_stats = kaweth_netdev_stats;
+	netdev->mtu = le16_to_cpu(kaweth->configuration.segment_size);
+	SET_ETHTOOL_OPS(netdev, &ops);
+
+	/* kaweth is zeroed as part of alloc_netdev */
+
+	INIT_WORK(&kaweth->lowmem_work, kaweth_resubmit_tl, (void *)kaweth);
+
+	SET_MODULE_OWNER(netdev);
+
+	usb_set_intfdata(intf, kaweth);
+
+#if 0
+// dma_supported() is deeply broken on almost all architectures
+	if (dma_supported (&intf->dev, 0xffffffffffffffffULL))
+		kaweth->net->features |= NETIF_F_HIGHDMA;
+#endif
+
+	SET_NETDEV_DEV(netdev, &intf->dev);
+	if (register_netdev(netdev) != 0) {
+		kaweth_err("Error registering netdev.");
+		goto err_intfdata;
+	}
+
+	kaweth_info("kaweth interface created at %s", kaweth->net->name);
+
+	kaweth_dbg("Kaweth probe returning.");
+
+	return 0;
+
+err_intfdata:
+	usb_set_intfdata(intf, NULL);
+	usb_buffer_free(kaweth->dev, KAWETH_BUF_SIZE, (void *)kaweth->rx_buf, kaweth->rxbufferhandle);
+err_all_but_rxbuf:
+	usb_buffer_free(kaweth->dev, INTBUFFERSIZE, (void *)kaweth->intbuffer, kaweth->intbufferhandle);
+err_tx_and_rx_and_irq:
+	usb_free_urb(kaweth->irq_urb);
+err_tx_and_rx:
+	usb_free_urb(kaweth->rx_urb);
+err_only_tx:
+	usb_free_urb(kaweth->tx_urb);
+err_free_netdev:
+	free_netdev(netdev);
+
+	return -EIO;
+}
+
+/****************************************************************
+ *     kaweth_disconnect
+ ****************************************************************/
+static void kaweth_disconnect(struct usb_interface *intf)
+{
+	struct kaweth_device *kaweth = usb_get_intfdata(intf);
+	struct net_device *netdev;
+
+	kaweth_info("Unregistering");
+
+	usb_set_intfdata(intf, NULL);
+	if (!kaweth) {
+		kaweth_warn("unregistering non-existant device");
+		return;
+	}
+	netdev = kaweth->net;
+	kaweth->removed = 1;
+	usb_kill_urb(kaweth->irq_urb);
+	usb_kill_urb(kaweth->rx_urb);
+	usb_kill_urb(kaweth->tx_urb);
+
+	kaweth_dbg("Unregistering net device");
+	unregister_netdev(netdev);
+
+	usb_free_urb(kaweth->rx_urb);
+	usb_free_urb(kaweth->tx_urb);
+	usb_free_urb(kaweth->irq_urb);
+
+	usb_buffer_free(kaweth->dev, KAWETH_BUF_SIZE, (void *)kaweth->rx_buf, kaweth->rxbufferhandle);
+	usb_buffer_free(kaweth->dev, INTBUFFERSIZE, (void *)kaweth->intbuffer, kaweth->intbufferhandle);
+
+	free_netdev(netdev);
+}
+
+
+// FIXME this completion stuff is a modified clone of
+// an OLD version of some stuff in usb.c ...
+struct usb_api_data {
+	wait_queue_head_t wqh;
+	int done;
+};
+
+/*-------------------------------------------------------------------*
+ * completion handler for compatibility wrappers (sync control/bulk) *
+ *-------------------------------------------------------------------*/
+static void usb_api_blocking_completion(struct urb *urb, struct pt_regs *regs)
+{
+        struct usb_api_data *awd = (struct usb_api_data *)urb->context;
+
+	awd->done=1;
+	wake_up(&awd->wqh);
+}
+
+/*-------------------------------------------------------------------*
+ *                         COMPATIBILITY STUFF                       *
+ *-------------------------------------------------------------------*/
+
+// Starts urb and waits for completion or timeout
+static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length)
+{
+	struct usb_api_data awd;
+        int status;
+
+        init_waitqueue_head(&awd.wqh);
+        awd.done = 0;
+
+        urb->context = &awd;
+        status = usb_submit_urb(urb, GFP_NOIO);
+        if (status) {
+                // something went wrong
+                usb_free_urb(urb);
+                return status;
+        }
+
+	if (!wait_event_timeout(awd.wqh, awd.done, timeout)) {
+                // timeout
+                kaweth_warn("usb_control/bulk_msg: timeout");
+                usb_kill_urb(urb);  // remove urb safely
+                status = -ETIMEDOUT;
+        }
+	else {
+                status = urb->status;
+	}
+
+        if (actual_length) {
+                *actual_length = urb->actual_length;
+	}
+
+        usb_free_urb(urb);
+        return status;
+}
+
+/*-------------------------------------------------------------------*/
+// returns status (negative) or length (positive)
+static int kaweth_internal_control_msg(struct usb_device *usb_dev,
+				       unsigned int pipe,
+				       struct usb_ctrlrequest *cmd, void *data,
+				       int len, int timeout)
+{
+        struct urb *urb;
+        int retv;
+        int length;
+
+        urb = usb_alloc_urb(0, GFP_NOIO);
+        if (!urb)
+                return -ENOMEM;
+
+        usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char*)cmd, data,
+			 len, usb_api_blocking_completion, NULL);
+
+        retv = usb_start_wait_urb(urb, timeout, &length);
+        if (retv < 0) {
+                return retv;
+	}
+        else {
+                return length;
+	}
+}
+
+
+/****************************************************************
+ *     kaweth_init
+ ****************************************************************/
+static int __init kaweth_init(void)
+{
+	kaweth_dbg("Driver loading");
+	return usb_register(&kaweth_driver);
+}
+
+/****************************************************************
+ *     kaweth_exit
+ ****************************************************************/
+static void __exit kaweth_exit(void)
+{
+	usb_deregister(&kaweth_driver);
+}
+
+module_init(kaweth_init);
+module_exit(kaweth_exit);
+
+
+
+
+
+
+
+
+
diff --git a/drivers/usb/net/kawethfw.h b/drivers/usb/net/kawethfw.h
new file mode 100644
index 0000000..cf85fcb
--- /dev/null
+++ b/drivers/usb/net/kawethfw.h
@@ -0,0 +1,557 @@
+/******************************************/
+/* NOTE: B6/C3 is data header signature   */
+/*       0xAA/0xBB is data length = total */
+/*       bytes - 7, 0xCC is type, 0xDD is */
+/*       interrupt to use.                */
+/******************************************/
+
+/****************************************************************
+ *     kaweth_trigger_code
+ ****************************************************************/
+static __u8 kaweth_trigger_code[] = 
+{
+    0xB6, 0xC3, 0xAA, 0xBB, 0xCC, 0xDD,
+    0xc8, 0x07, 0xa0, 0x00, 0xf0, 0x07, 0x5e, 0x00,
+    0x06, 0x00, 0xf0, 0x07, 0x0a, 0x00, 0x08, 0x00,
+    0xf0, 0x09, 0x00, 0x00, 0x02, 0x00, 0xe7, 0x07,
+    0x36, 0x00, 0x00, 0x00, 0xf0, 0x07, 0x00, 0x00,
+    0x04, 0x00, 0xe7, 0x07, 0x50, 0xc3, 0x10, 0xc0,
+    0xf0, 0x09, 0x0e, 0xc0, 0x00, 0x00, 0xe7, 0x87,
+    0x01, 0x00, 0x0e, 0xc0, 0x97, 0xcf, 0xd7, 0x09,
+    0x00, 0xc0, 0x17, 0x02, 0xc8, 0x07, 0xa0, 0x00,
+    0xe7, 0x17, 0x50, 0xc3, 0x10, 0xc0, 0x30, 0xd8,
+    0x04, 0x00, 0x30, 0x5c, 0x08, 0x00, 0x04, 0x00,
+    0xb0, 0xc0, 0x06, 0x00, 0xc8, 0x05, 0xe7, 0x05,
+    0x00, 0xc0, 0xc0, 0xdf, 0x97, 0xcf, 0x49, 0xaf,
+    0xc0, 0x07, 0x00, 0x00, 0x60, 0xaf, 0x4a, 0xaf,
+    0x00, 0x0c, 0x0c, 0x00, 0x40, 0xd2, 0x00, 0x1c,
+    0x0c, 0x00, 0x40, 0xd2, 0x30, 0x00, 0x08, 0x00,
+    0xf0, 0x07, 0x00, 0x00, 0x04, 0x00, 0xf0, 0x07,
+    0x86, 0x00, 0x06, 0x00, 0x67, 0xcf, 0x27, 0x0c,
+    0x02, 0x00, 0x00, 0x00, 0x27, 0x0c, 0x00, 0x00,
+    0x0e, 0xc0, 0x49, 0xaf, 0x64, 0xaf, 0xc0, 0x07,
+    0x00, 0x00, 0x4b, 0xaf, 0x4a, 0xaf, 0x5a, 0xcf,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x94, 0x00, 0x05, 0x00,
+    0x00, 0x00
+};
+/****************************************************************
+ *     kaweth_trigger_code_fix
+ ****************************************************************/
+static __u8 kaweth_trigger_code_fix[] = 
+{
+    0xB6, 0xC3, 0xAA, 0xBB, 0xCC, 0xDD,
+    0x02, 0x00, 0x06, 0x00, 0x18, 0x00, 0x3e, 0x00,
+    0x80, 0x00, 0x98, 0x00, 0xaa, 0x00,
+    0x00, 0x00
+};
+
+/****************************************************************
+ *     kaweth_new_code
+ ****************************************************************/
+static __u8 kaweth_new_code[] = 
+{
+    0xB6, 0xC3, 0xAA, 0xBB, 0xCC, 0xDD,
+    0x9f, 0xcf, 0xde, 0x06, 0xe7, 0x57, 0x00, 0x00,
+    0xc4, 0x06, 0x97, 0xc1, 0xe7, 0x67, 0xff, 0x1f,
+    0x28, 0xc0, 0xe7, 0x87, 0x00, 0x04, 0x24, 0xc0,
+    0xe7, 0x67, 0xff, 0xf9, 0x22, 0xc0, 0x97, 0xcf,
+    0xd7, 0x09, 0x00, 0xc0, 0xe7, 0x09, 0xa2, 0xc0,
+    0xbe, 0x06, 0x9f, 0xaf, 0x36, 0x00, 0xe7, 0x05,
+    0x00, 0xc0, 0xa7, 0xcf, 0xbc, 0x06, 0x97, 0xcf,
+    0xe7, 0x57, 0x00, 0x00, 0xb8, 0x06, 0xa7, 0xa1,
+    0xb8, 0x06, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00,
+    0x14, 0x08, 0x0a, 0xc0, 0xe7, 0x57, 0x00, 0x00,
+    0xa4, 0xc0, 0xa7, 0xc0, 0x7a, 0x06, 0x9f, 0xaf,
+    0x92, 0x07, 0xe7, 0x07, 0x00, 0x00, 0x14, 0x08,
+    0xe7, 0x57, 0xff, 0xff, 0xba, 0x06, 0x9f, 0xa0,
+    0x38, 0x00, 0xe7, 0x59, 0xba, 0x06, 0xbe, 0x06,
+    0x9f, 0xa0, 0x38, 0x00, 0xc8, 0x09, 0xca, 0x06,
+    0x08, 0x62, 0x9f, 0xa1, 0x36, 0x08, 0xc0, 0x09,
+    0x76, 0x06, 0x00, 0x60, 0xa7, 0xc0, 0x7a, 0x06,
+    0x9f, 0xaf, 0xcc, 0x02, 0xe7, 0x57, 0x00, 0x00,
+    0xb8, 0x06, 0xa7, 0xc1, 0x7a, 0x06, 0x9f, 0xaf,
+    0x04, 0x00, 0xe7, 0x57, 0x00, 0x00, 0x8e, 0x06,
+    0x0a, 0xc1, 0xe7, 0x09, 0x20, 0xc0, 0x10, 0x08,
+    0xe7, 0xd0, 0x10, 0x08, 0xe7, 0x67, 0x40, 0x00,
+    0x10, 0x08, 0x9f, 0xaf, 0x92, 0x0c, 0xc0, 0x09,
+    0xd0, 0x06, 0x00, 0x60, 0x05, 0xc4, 0xc0, 0x59,
+    0xbe, 0x06, 0x02, 0xc0, 0x9f, 0xaf, 0xec, 0x00,
+    0x9f, 0xaf, 0x34, 0x02, 0xe7, 0x57, 0x00, 0x00,
+    0xa6, 0x06, 0x9f, 0xa0, 0x7a, 0x02, 0xa7, 0xcf,
+    0x7a, 0x06, 0x48, 0x02, 0xe7, 0x09, 0xbe, 0x06,
+    0xd0, 0x06, 0xc8, 0x37, 0x04, 0x00, 0x9f, 0xaf,
+    0x08, 0x03, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00,
+    0xce, 0x06, 0x97, 0xc0, 0xd7, 0x09, 0x00, 0xc0,
+    0xc1, 0xdf, 0xc8, 0x09, 0xc6, 0x06, 0x08, 0x62,
+    0x14, 0xc0, 0x27, 0x04, 0xc6, 0x06, 0x10, 0x94,
+    0xf0, 0x07, 0x10, 0x08, 0x02, 0x00, 0xc1, 0x07,
+    0x01, 0x00, 0x70, 0x00, 0x04, 0x00, 0xf0, 0x07,
+    0x30, 0x01, 0x06, 0x00, 0x50, 0xaf, 0xe7, 0x07,
+    0xff, 0xff, 0xd0, 0x06, 0xe7, 0x07, 0x00, 0x00,
+    0xce, 0x06, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf,
+    0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x48, 0x02,
+    0xd0, 0x09, 0xc6, 0x06, 0x27, 0x02, 0xc6, 0x06,
+    0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0x48, 0x02,
+    0xc8, 0x37, 0x04, 0x00, 0x00, 0x0c, 0x0c, 0x00,
+    0x00, 0x60, 0x21, 0xc0, 0xc0, 0x37, 0x3e, 0x00,
+    0x23, 0xc9, 0xc0, 0x57, 0xb4, 0x05, 0x1b, 0xc8,
+    0xc0, 0x17, 0x3f, 0x00, 0xc0, 0x67, 0xc0, 0xff,
+    0x30, 0x00, 0x08, 0x00, 0xf0, 0x07, 0x00, 0x00,
+    0x04, 0x00, 0x00, 0x02, 0xc0, 0x17, 0x4c, 0x00,
+    0x30, 0x00, 0x06, 0x00, 0xf0, 0x07, 0xa0, 0x01,
+    0x0a, 0x00, 0x48, 0x02, 0xc1, 0x07, 0x02, 0x00,
+    0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x51, 0xaf,
+    0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0x9f, 0xaf,
+    0x08, 0x03, 0x9f, 0xaf, 0x7a, 0x02, 0x97, 0xcf,
+    0x9f, 0xaf, 0x7a, 0x02, 0xc9, 0x37, 0x04, 0x00,
+    0xc1, 0xdf, 0xc8, 0x09, 0xa2, 0x06, 0x50, 0x02,
+    0x67, 0x02, 0xa2, 0x06, 0xd1, 0x07, 0x00, 0x00,
+    0x27, 0xd8, 0xaa, 0x06, 0xc0, 0xdf, 0x9f, 0xaf,
+    0xc4, 0x01, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00,
+    0xd2, 0x06, 0x97, 0xc1, 0xe7, 0x57, 0x01, 0x00,
+    0xa8, 0x06, 0x97, 0xc0, 0xc8, 0x09, 0xa0, 0x06,
+    0x08, 0x62, 0x97, 0xc0, 0x00, 0x02, 0xc0, 0x17,
+    0x0e, 0x00, 0x27, 0x00, 0x34, 0x01, 0x27, 0x0c,
+    0x0c, 0x00, 0x36, 0x01, 0xe7, 0x07, 0x50, 0xc3,
+    0x12, 0xc0, 0xe7, 0x07, 0xcc, 0x0b, 0x02, 0x00,
+    0xe7, 0x07, 0x01, 0x00, 0xa8, 0x06, 0xe7, 0x07,
+    0x05, 0x00, 0x90, 0xc0, 0x97, 0xcf, 0xc8, 0x09,
+    0xa4, 0x06, 0x08, 0x62, 0x02, 0xc0, 0x10, 0x64,
+    0x07, 0xc1, 0xe7, 0x07, 0x00, 0x00, 0x9e, 0x06,
+    0xe7, 0x07, 0x72, 0x04, 0x24, 0x00, 0x97, 0xcf,
+    0x27, 0x04, 0xa4, 0x06, 0xc8, 0x17, 0x0e, 0x00,
+    0x27, 0x02, 0x9e, 0x06, 0xe7, 0x07, 0x80, 0x04,
+    0x24, 0x00, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0,
+    0xc1, 0xdf, 0xe7, 0x57, 0x00, 0x00, 0x90, 0x06,
+    0x13, 0xc1, 0x9f, 0xaf, 0x06, 0x02, 0xe7, 0x57,
+    0x00, 0x00, 0x9e, 0x06, 0x13, 0xc0, 0xe7, 0x09,
+    0x9e, 0x06, 0x30, 0x01, 0xe7, 0x07, 0xf2, 0x05,
+    0x32, 0x01, 0xe7, 0x07, 0x10, 0x00, 0x96, 0xc0,
+    0xe7, 0x09, 0x9e, 0x06, 0x90, 0x06, 0x04, 0xcf,
+    0xe7, 0x57, 0x00, 0x00, 0x9e, 0x06, 0x02, 0xc1,
+    0x9f, 0xaf, 0x06, 0x02, 0xe7, 0x05, 0x00, 0xc0,
+    0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf,
+    0x08, 0x92, 0xe7, 0x57, 0x02, 0x00, 0xaa, 0x06,
+    0x02, 0xc3, 0xc8, 0x09, 0xa4, 0x06, 0x27, 0x02,
+    0xa6, 0x06, 0x08, 0x62, 0x03, 0xc1, 0xe7, 0x05,
+    0x00, 0xc0, 0x97, 0xcf, 0x27, 0x04, 0xa4, 0x06,
+    0xe7, 0x05, 0x00, 0xc0, 0xf0, 0x07, 0x40, 0x00,
+    0x08, 0x00, 0xf0, 0x07, 0x00, 0x00, 0x04, 0x00,
+    0x00, 0x02, 0xc0, 0x17, 0x0c, 0x00, 0x30, 0x00,
+    0x06, 0x00, 0xf0, 0x07, 0x46, 0x01, 0x0a, 0x00,
+    0xc8, 0x17, 0x04, 0x00, 0xc1, 0x07, 0x02, 0x00,
+    0x51, 0xaf, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00,
+    0x96, 0x06, 0x97, 0xc0, 0xc1, 0xdf, 0xc8, 0x09,
+    0x96, 0x06, 0x27, 0x04, 0x96, 0x06, 0x27, 0x52,
+    0x98, 0x06, 0x03, 0xc1, 0xe7, 0x07, 0x96, 0x06,
+    0x98, 0x06, 0xc0, 0xdf, 0x17, 0x02, 0xc8, 0x17,
+    0x0e, 0x00, 0x9f, 0xaf, 0xba, 0x03, 0xc8, 0x05,
+    0x00, 0x60, 0x03, 0xc0, 0x9f, 0xaf, 0x24, 0x03,
+    0x97, 0xcf, 0x9f, 0xaf, 0x08, 0x03, 0x97, 0xcf,
+    0x57, 0x02, 0xc9, 0x07, 0xa4, 0x06, 0xd7, 0x09,
+    0x00, 0xc0, 0xc1, 0xdf, 0x08, 0x62, 0x1b, 0xc0,
+    0x50, 0x04, 0x11, 0x02, 0xe7, 0x05, 0x00, 0xc0,
+    0xc9, 0x05, 0x97, 0xcf, 0x97, 0x02, 0xca, 0x09,
+    0xd6, 0x06, 0xf2, 0x17, 0x01, 0x00, 0x04, 0x00,
+    0xf2, 0x27, 0x00, 0x00, 0x06, 0x00, 0xca, 0x17,
+    0x2c, 0x00, 0xf8, 0x77, 0x01, 0x00, 0x0e, 0x00,
+    0x06, 0xc0, 0xca, 0xd9, 0xf8, 0x57, 0xff, 0x00,
+    0x0e, 0x00, 0x01, 0xc1, 0xca, 0xd9, 0x22, 0x1c,
+    0x0c, 0x00, 0xe2, 0x27, 0x00, 0x00, 0xe2, 0x17,
+    0x01, 0x00, 0xe2, 0x27, 0x00, 0x00, 0xca, 0x05,
+    0x00, 0x0c, 0x0c, 0x00, 0xc0, 0x17, 0x41, 0x00,
+    0xc0, 0x67, 0xc0, 0xff, 0x30, 0x00, 0x08, 0x00,
+    0x00, 0x02, 0xc0, 0x17, 0x0c, 0x00, 0x30, 0x00,
+    0x06, 0x00, 0xf0, 0x07, 0xda, 0x00, 0x0a, 0x00,
+    0xf0, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x0c,
+    0x08, 0x00, 0x40, 0xd1, 0x01, 0x00, 0xc0, 0x19,
+    0xce, 0x06, 0xc0, 0x59, 0xc2, 0x06, 0x04, 0xc9,
+    0x49, 0xaf, 0x9f, 0xaf, 0xec, 0x00, 0x4a, 0xaf,
+    0x67, 0x10, 0xce, 0x06, 0xc8, 0x17, 0x04, 0x00,
+    0xc1, 0x07, 0x01, 0x00, 0xd7, 0x09, 0x00, 0xc0,
+    0xc1, 0xdf, 0x50, 0xaf, 0xe7, 0x05, 0x00, 0xc0,
+    0x97, 0xcf, 0xc0, 0x07, 0x01, 0x00, 0xc1, 0x09,
+    0xac, 0x06, 0xc1, 0x77, 0x01, 0x00, 0x97, 0xc1,
+    0xd8, 0x77, 0x01, 0x00, 0x12, 0xc0, 0xc9, 0x07,
+    0x6a, 0x06, 0x9f, 0xaf, 0x08, 0x04, 0x04, 0xc1,
+    0xc1, 0x77, 0x08, 0x00, 0x13, 0xc0, 0x97, 0xcf,
+    0xc1, 0x77, 0x02, 0x00, 0x97, 0xc1, 0xc1, 0x77,
+    0x10, 0x00, 0x0c, 0xc0, 0x9f, 0xaf, 0x2c, 0x04,
+    0x97, 0xcf, 0xc1, 0x77, 0x04, 0x00, 0x06, 0xc0,
+    0xc9, 0x07, 0x70, 0x06, 0x9f, 0xaf, 0x08, 0x04,
+    0x97, 0xc0, 0x00, 0xcf, 0x00, 0x90, 0x97, 0xcf,
+    0x50, 0x54, 0x97, 0xc1, 0x70, 0x5c, 0x02, 0x00,
+    0x02, 0x00, 0x97, 0xc1, 0x70, 0x5c, 0x04, 0x00,
+    0x04, 0x00, 0x97, 0xcf, 0x80, 0x01, 0xc0, 0x00,
+    0x60, 0x00, 0x30, 0x00, 0x18, 0x00, 0x0c, 0x00,
+    0x06, 0x00, 0x00, 0x00, 0xcb, 0x09, 0xb2, 0x06,
+    0xcc, 0x09, 0xb4, 0x06, 0x0b, 0x53, 0x11, 0xc0,
+    0xc9, 0x02, 0xca, 0x07, 0x1c, 0x04, 0x9f, 0xaf,
+    0x08, 0x04, 0x97, 0xc0, 0x0a, 0xc8, 0x82, 0x08,
+    0x0a, 0xcf, 0x82, 0x08, 0x9f, 0xaf, 0x08, 0x04,
+    0x97, 0xc0, 0x05, 0xc2, 0x89, 0x30, 0x82, 0x60,
+    0x78, 0xc1, 0x00, 0x90, 0x97, 0xcf, 0x89, 0x10,
+    0x09, 0x53, 0x79, 0xc2, 0x89, 0x30, 0x82, 0x08,
+    0x7a, 0xcf, 0xc0, 0xdf, 0x97, 0xcf, 0xc0, 0xdf,
+    0x97, 0xcf, 0xe7, 0x09, 0x96, 0xc0, 0x92, 0x06,
+    0xe7, 0x09, 0x98, 0xc0, 0x94, 0x06, 0x0f, 0xcf,
+    0xe7, 0x09, 0x96, 0xc0, 0x92, 0x06, 0xe7, 0x09,
+    0x98, 0xc0, 0x94, 0x06, 0xe7, 0x09, 0x9e, 0x06,
+    0x30, 0x01, 0xe7, 0x07, 0xf2, 0x05, 0x32, 0x01,
+    0xe7, 0x07, 0x10, 0x00, 0x96, 0xc0, 0xd7, 0x09,
+    0x00, 0xc0, 0x17, 0x02, 0xc8, 0x09, 0x90, 0x06,
+    0xc8, 0x37, 0x0e, 0x00, 0xe7, 0x77, 0x2a, 0x00,
+    0x92, 0x06, 0x30, 0xc0, 0x97, 0x02, 0xca, 0x09,
+    0xd6, 0x06, 0xe7, 0x77, 0x20, 0x00, 0x92, 0x06,
+    0x0e, 0xc0, 0xf2, 0x17, 0x01, 0x00, 0x10, 0x00,
+    0xf2, 0x27, 0x00, 0x00, 0x12, 0x00, 0xe7, 0x77,
+    0x0a, 0x00, 0x92, 0x06, 0xca, 0x05, 0x1e, 0xc0,
+    0x97, 0x02, 0xca, 0x09, 0xd6, 0x06, 0xf2, 0x17,
+    0x01, 0x00, 0x0c, 0x00, 0xf2, 0x27, 0x00, 0x00,
+    0x0e, 0x00, 0xe7, 0x77, 0x02, 0x00, 0x92, 0x06,
+    0x07, 0xc0, 0xf2, 0x17, 0x01, 0x00, 0x44, 0x00,
+    0xf2, 0x27, 0x00, 0x00, 0x46, 0x00, 0x06, 0xcf,
+    0xf2, 0x17, 0x01, 0x00, 0x60, 0x00, 0xf2, 0x27,
+    0x00, 0x00, 0x62, 0x00, 0xca, 0x05, 0x9f, 0xaf,
+    0x08, 0x03, 0x0f, 0xcf, 0x57, 0x02, 0x09, 0x02,
+    0xf1, 0x09, 0x94, 0x06, 0x0c, 0x00, 0xf1, 0xda,
+    0x0c, 0x00, 0xc8, 0x09, 0x98, 0x06, 0x50, 0x02,
+    0x67, 0x02, 0x98, 0x06, 0xd1, 0x07, 0x00, 0x00,
+    0xc9, 0x05, 0xe7, 0x09, 0x9e, 0x06, 0x90, 0x06,
+    0xe7, 0x57, 0x00, 0x00, 0x90, 0x06, 0x02, 0xc0,
+    0x9f, 0xaf, 0x06, 0x02, 0xc8, 0x05, 0xe7, 0x05,
+    0x00, 0xc0, 0xc0, 0xdf, 0x97, 0xcf, 0xd7, 0x09,
+    0x00, 0xc0, 0x17, 0x00, 0x17, 0x02, 0x97, 0x02,
+    0xc0, 0x09, 0x92, 0xc0, 0xe7, 0x07, 0x04, 0x00,
+    0x90, 0xc0, 0xca, 0x09, 0xd6, 0x06, 0xe7, 0x07,
+    0x00, 0x00, 0xa8, 0x06, 0xe7, 0x07, 0x6a, 0x04,
+    0x02, 0x00, 0xc0, 0x77, 0x02, 0x00, 0x08, 0xc0,
+    0xf2, 0x17, 0x01, 0x00, 0x50, 0x00, 0xf2, 0x27,
+    0x00, 0x00, 0x52, 0x00, 0x9f, 0xcf, 0x24, 0x06,
+    0xc0, 0x77, 0x10, 0x00, 0x06, 0xc0, 0xf2, 0x17,
+    0x01, 0x00, 0x58, 0x00, 0xf2, 0x27, 0x00, 0x00,
+    0x5a, 0x00, 0xc0, 0x77, 0x80, 0x00, 0x06, 0xc0,
+    0xf2, 0x17, 0x01, 0x00, 0x70, 0x00, 0xf2, 0x27,
+    0x00, 0x00, 0x72, 0x00, 0xc0, 0x77, 0x08, 0x00,
+    0x1d, 0xc1, 0xf2, 0x17, 0x01, 0x00, 0x08, 0x00,
+    0xf2, 0x27, 0x00, 0x00, 0x0a, 0x00, 0xc0, 0x77,
+    0x00, 0x02, 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00,
+    0x64, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x66, 0x00,
+    0xc0, 0x77, 0x40, 0x00, 0x06, 0xc0, 0xf2, 0x17,
+    0x01, 0x00, 0x5c, 0x00, 0xf2, 0x27, 0x00, 0x00,
+    0x5e, 0x00, 0xc0, 0x77, 0x01, 0x00, 0x01, 0xc0,
+    0x1b, 0xcf, 0x1a, 0xcf, 0xf2, 0x17, 0x01, 0x00,
+    0x00, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x02, 0x00,
+    0xc8, 0x09, 0x34, 0x01, 0xca, 0x17, 0x14, 0x00,
+    0xd8, 0x77, 0x01, 0x00, 0x05, 0xc0, 0xca, 0xd9,
+    0xd8, 0x57, 0xff, 0x00, 0x01, 0xc0, 0xca, 0xd9,
+    0xe2, 0x19, 0x94, 0xc0, 0xe2, 0x27, 0x00, 0x00,
+    0xe2, 0x17, 0x01, 0x00, 0xe2, 0x27, 0x00, 0x00,
+    0x9f, 0xaf, 0x40, 0x06, 0x9f, 0xaf, 0xc4, 0x01,
+    0xe7, 0x57, 0x00, 0x00, 0xd2, 0x06, 0x9f, 0xa1,
+    0x0e, 0x0a, 0xca, 0x05, 0xc8, 0x05, 0xc0, 0x05,
+    0xe7, 0x05, 0x00, 0xc0, 0xc0, 0xdf, 0x97, 0xcf,
+    0xc8, 0x09, 0xa0, 0x06, 0x08, 0x62, 0x97, 0xc0,
+    0x27, 0x04, 0xa0, 0x06, 0x27, 0x52, 0xa2, 0x06,
+    0x03, 0xc1, 0xe7, 0x07, 0xa0, 0x06, 0xa2, 0x06,
+    0x9f, 0xaf, 0x08, 0x03, 0xe7, 0x57, 0x00, 0x00,
+    0xaa, 0x06, 0x02, 0xc0, 0x27, 0xda, 0xaa, 0x06,
+    0x97, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0xff, 0xff, 0xfb, 0x13, 0xe7, 0x57,
+    0x00, 0x80, 0xb2, 0x00, 0x06, 0xc2, 0xe7, 0x07,
+    0xee, 0x0b, 0x12, 0x00, 0xe7, 0x07, 0x34, 0x0c,
+    0xb2, 0x00, 0xe7, 0x07, 0xc6, 0x07, 0xf2, 0x02,
+    0xc8, 0x09, 0xb4, 0x00, 0xf8, 0x07, 0x02, 0x00,
+    0x0d, 0x00, 0xd7, 0x09, 0x0e, 0xc0, 0xe7, 0x07,
+    0x00, 0x00, 0x0e, 0xc0, 0xc8, 0x09, 0xde, 0x00,
+    0xc8, 0x17, 0x09, 0x00, 0xc9, 0x07, 0xda, 0x06,
+    0xc0, 0x07, 0x04, 0x00, 0x68, 0x0a, 0x00, 0xda,
+    0x7d, 0xc1, 0xe7, 0x09, 0xc0, 0x00, 0x7c, 0x06,
+    0xe7, 0x09, 0xbe, 0x00, 0x78, 0x06, 0xe7, 0x09,
+    0x10, 0x00, 0xbc, 0x06, 0xc8, 0x07, 0xd6, 0x07,
+    0x9f, 0xaf, 0xae, 0x07, 0x9f, 0xaf, 0x00, 0x0a,
+    0xc8, 0x09, 0xde, 0x00, 0x00, 0x0e, 0x0f, 0x00,
+    0x41, 0x90, 0x9f, 0xde, 0x06, 0x00, 0x44, 0xaf,
+    0x27, 0x00, 0xb2, 0x06, 0x27, 0x00, 0xb4, 0x06,
+    0x27, 0x00, 0xb6, 0x06, 0xc0, 0x07, 0x74, 0x00,
+    0x44, 0xaf, 0x27, 0x00, 0xd6, 0x06, 0x08, 0x00,
+    0x00, 0x90, 0xc1, 0x07, 0x3a, 0x00, 0x20, 0x00,
+    0x01, 0xda, 0x7d, 0xc1, 0x9f, 0xaf, 0xba, 0x09,
+    0xc0, 0x07, 0x44, 0x00, 0x48, 0xaf, 0x27, 0x00,
+    0x7a, 0x06, 0x9f, 0xaf, 0x96, 0x0a, 0xe7, 0x07,
+    0x01, 0x00, 0xc0, 0x06, 0xe7, 0x05, 0x0e, 0xc0,
+    0x97, 0xcf, 0x49, 0xaf, 0xe7, 0x87, 0x43, 0x00,
+    0x0e, 0xc0, 0xe7, 0x07, 0xff, 0xff, 0xbe, 0x06,
+    0x9f, 0xaf, 0xae, 0x0a, 0xc0, 0x07, 0x01, 0x00,
+    0x60, 0xaf, 0x4a, 0xaf, 0x97, 0xcf, 0x00, 0x08,
+    0x09, 0x08, 0x11, 0x08, 0x00, 0xda, 0x7c, 0xc1,
+    0x97, 0xcf, 0x67, 0x04, 0xcc, 0x02, 0xc0, 0xdf,
+    0x51, 0x94, 0xb1, 0xaf, 0x06, 0x00, 0xc1, 0xdf,
+    0xc9, 0x09, 0xcc, 0x02, 0x49, 0x62, 0x75, 0xc1,
+    0xc0, 0xdf, 0xa7, 0xcf, 0xd6, 0x02, 0x0e, 0x00,
+    0x24, 0x00, 0x80, 0x04, 0x22, 0x00, 0x4e, 0x05,
+    0xd0, 0x00, 0x0e, 0x0a, 0xaa, 0x00, 0x30, 0x08,
+    0xbe, 0x00, 0x4a, 0x0a, 0x10, 0x00, 0x20, 0x00,
+    0x04, 0x00, 0x6e, 0x04, 0x02, 0x00, 0x6a, 0x04,
+    0x06, 0x00, 0x00, 0x00, 0x24, 0xc0, 0x04, 0x04,
+    0x28, 0xc0, 0xfe, 0xfb, 0x1e, 0xc0, 0x00, 0x04,
+    0x22, 0xc0, 0xff, 0xf4, 0xc0, 0x00, 0x90, 0x09,
+    0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x56, 0x08,
+    0x60, 0x08, 0xd0, 0x08, 0xda, 0x08, 0x00, 0x09,
+    0x04, 0x09, 0x08, 0x09, 0x32, 0x09, 0x42, 0x09,
+    0x50, 0x09, 0x52, 0x09, 0x5a, 0x09, 0x5a, 0x09,
+    0x27, 0x02, 0xca, 0x06, 0x97, 0xcf, 0xe7, 0x07,
+    0x00, 0x00, 0xca, 0x06, 0x0a, 0x0e, 0x01, 0x00,
+    0xca, 0x57, 0x0e, 0x00, 0x9f, 0xc3, 0x5a, 0x09,
+    0xca, 0x37, 0x00, 0x00, 0x9f, 0xc2, 0x5a, 0x09,
+    0x0a, 0xd2, 0xb2, 0xcf, 0x16, 0x08, 0xc8, 0x09,
+    0xde, 0x00, 0x07, 0x06, 0x9f, 0xcf, 0x6c, 0x09,
+    0x17, 0x02, 0xc8, 0x09, 0xde, 0x00, 0x00, 0x0e,
+    0x0f, 0x00, 0x41, 0x90, 0x9f, 0xde, 0x06, 0x00,
+    0xc8, 0x05, 0x30, 0x50, 0x06, 0x00, 0x9f, 0xc8,
+    0x5a, 0x09, 0x27, 0x0c, 0x02, 0x00, 0xb0, 0x06,
+    0xc0, 0x09, 0xb2, 0x06, 0x27, 0x00, 0xb4, 0x06,
+    0xe7, 0x07, 0x00, 0x00, 0xae, 0x06, 0x27, 0x00,
+    0x80, 0x06, 0x00, 0x1c, 0x06, 0x00, 0x27, 0x00,
+    0xb6, 0x06, 0x41, 0x90, 0x67, 0x50, 0xb0, 0x06,
+    0x0d, 0xc0, 0x67, 0x00, 0x7e, 0x06, 0x27, 0x0c,
+    0x06, 0x00, 0x82, 0x06, 0xe7, 0x07, 0xbc, 0x08,
+    0x84, 0x06, 0xc8, 0x07, 0x7e, 0x06, 0x41, 0x90,
+    0x51, 0xaf, 0x97, 0xcf, 0x9f, 0xaf, 0x48, 0x0c,
+    0xe7, 0x09, 0xb6, 0x06, 0xb4, 0x06, 0xe7, 0x09,
+    0xb0, 0x06, 0xae, 0x06, 0x59, 0xaf, 0x97, 0xcf,
+    0x27, 0x0c, 0x02, 0x00, 0xac, 0x06, 0x59, 0xaf,
+    0x97, 0xcf, 0x09, 0x0c, 0x02, 0x00, 0x09, 0xda,
+    0x49, 0xd2, 0xc9, 0x19, 0xd6, 0x06, 0xc8, 0x07,
+    0x7e, 0x06, 0xe0, 0x07, 0x00, 0x00, 0x60, 0x02,
+    0xe0, 0x07, 0x04, 0x00, 0xd0, 0x07, 0xcc, 0x08,
+    0x48, 0xdb, 0x41, 0x90, 0x50, 0xaf, 0x97, 0xcf,
+    0x59, 0xaf, 0x97, 0xcf, 0x59, 0xaf, 0x97, 0xcf,
+    0xf0, 0x57, 0x06, 0x00, 0x06, 0x00, 0x25, 0xc1,
+    0xe7, 0x07, 0x70, 0x06, 0x80, 0x06, 0x41, 0x90,
+    0x67, 0x00, 0x7e, 0x06, 0x27, 0x0c, 0x06, 0x00,
+    0x82, 0x06, 0xe7, 0x07, 0x8c, 0x09, 0x84, 0x06,
+    0xc8, 0x07, 0x7e, 0x06, 0x41, 0x90, 0x51, 0xaf,
+    0x97, 0xcf, 0x07, 0x0c, 0x06, 0x00, 0xc7, 0x57,
+    0x06, 0x00, 0x0f, 0xc1, 0xc8, 0x07, 0x70, 0x06,
+    0x15, 0xcf, 0x00, 0x0c, 0x02, 0x00, 0x00, 0xda,
+    0x40, 0xd1, 0x27, 0x00, 0xc2, 0x06, 0x1e, 0xcf,
+    0x1d, 0xcf, 0x27, 0x0c, 0x02, 0x00, 0xcc, 0x06,
+    0x19, 0xcf, 0x27, 0x02, 0x20, 0x01, 0xe7, 0x07,
+    0x08, 0x00, 0x22, 0x01, 0xe7, 0x07, 0x13, 0x00,
+    0xb0, 0xc0, 0x97, 0xcf, 0x41, 0x90, 0x67, 0x00,
+    0x7e, 0x06, 0xe7, 0x01, 0x82, 0x06, 0x27, 0x02,
+    0x80, 0x06, 0xe7, 0x07, 0x8c, 0x09, 0x84, 0x06,
+    0xc8, 0x07, 0x7e, 0x06, 0xc1, 0x07, 0x00, 0x80,
+    0x50, 0xaf, 0x97, 0xcf, 0x59, 0xaf, 0x97, 0xcf,
+    0x00, 0x60, 0x05, 0xc0, 0xe7, 0x07, 0x00, 0x00,
+    0xc4, 0x06, 0xa7, 0xcf, 0x7c, 0x06, 0x9f, 0xaf,
+    0x00, 0x0a, 0xe7, 0x07, 0x01, 0x00, 0xc4, 0x06,
+    0x49, 0xaf, 0xd7, 0x09, 0x00, 0xc0, 0x07, 0xaf,
+    0xe7, 0x05, 0x00, 0xc0, 0x4a, 0xaf, 0xa7, 0xcf,
+    0x7c, 0x06, 0xc0, 0x07, 0xfe, 0x7f, 0x44, 0xaf,
+    0x40, 0x00, 0xc0, 0x37, 0x00, 0x01, 0x41, 0x90,
+    0xc0, 0x37, 0x08, 0x00, 0xdf, 0xde, 0x50, 0x06,
+    0xc0, 0x57, 0x10, 0x00, 0x02, 0xc2, 0xc0, 0x07,
+    0x10, 0x00, 0x27, 0x00, 0x9a, 0x06, 0x41, 0x90,
+    0x9f, 0xde, 0x40, 0x06, 0x44, 0xaf, 0x27, 0x00,
+    0x9c, 0x06, 0xc0, 0x09, 0x9a, 0x06, 0x41, 0x90,
+    0x00, 0xd2, 0x00, 0xd8, 0x9f, 0xde, 0x08, 0x00,
+    0x44, 0xaf, 0x27, 0x00, 0xc8, 0x06, 0x97, 0xcf,
+    0xe7, 0x87, 0x00, 0x84, 0x28, 0xc0, 0xe7, 0x67,
+    0xff, 0xfb, 0x24, 0xc0, 0x97, 0xcf, 0xe7, 0x87,
+    0x01, 0x00, 0xd2, 0x06, 0xe7, 0x57, 0x00, 0x00,
+    0xa8, 0x06, 0x97, 0xc1, 0x9f, 0xaf, 0x00, 0x0a,
+    0xe7, 0x87, 0x00, 0x06, 0x22, 0xc0, 0xe7, 0x07,
+    0x00, 0x00, 0x90, 0xc0, 0xe7, 0x67, 0xfe, 0xff,
+    0x3e, 0xc0, 0xe7, 0x07, 0x26, 0x00, 0x0a, 0xc0,
+    0xe7, 0x87, 0x01, 0x00, 0x3e, 0xc0, 0xe7, 0x07,
+    0xff, 0xff, 0xbe, 0x06, 0x9f, 0xaf, 0x10, 0x0b,
+    0x97, 0xcf, 0x17, 0x00, 0xa7, 0xaf, 0x78, 0x06,
+    0xc0, 0x05, 0x27, 0x00, 0x76, 0x06, 0xe7, 0x87,
+    0x01, 0x00, 0xd2, 0x06, 0x9f, 0xaf, 0x00, 0x0a,
+    0xe7, 0x07, 0x0c, 0x00, 0x40, 0xc0, 0x9f, 0xaf,
+    0x10, 0x0b, 0x00, 0x90, 0x27, 0x00, 0xa6, 0x06,
+    0x27, 0x00, 0xaa, 0x06, 0xe7, 0x09, 0xb2, 0x06,
+    0xb4, 0x06, 0x27, 0x00, 0xae, 0x06, 0x27, 0x00,
+    0xac, 0x06, 0x9f, 0xaf, 0xae, 0x0a, 0xc0, 0x07,
+    0x00, 0x00, 0x27, 0x00, 0xb2, 0x02, 0x27, 0x00,
+    0xb4, 0x02, 0x27, 0x00, 0x8e, 0x06, 0xc0, 0x07,
+    0x06, 0x00, 0xc8, 0x09, 0xde, 0x00, 0xc8, 0x17,
+    0x03, 0x00, 0xc9, 0x07, 0x70, 0x06, 0x29, 0x0a,
+    0x00, 0xda, 0x7d, 0xc1, 0x97, 0xcf, 0xd7, 0x09,
+    0x00, 0xc0, 0xc1, 0xdf, 0x00, 0x90, 0x27, 0x00,
+    0x96, 0x06, 0xe7, 0x07, 0x96, 0x06, 0x98, 0x06,
+    0x27, 0x00, 0xa0, 0x06, 0xe7, 0x07, 0xa0, 0x06,
+    0xa2, 0x06, 0x27, 0x00, 0xa6, 0x06, 0x27, 0x00,
+    0x90, 0x06, 0x27, 0x00, 0x9e, 0x06, 0xc8, 0x09,
+    0x9c, 0x06, 0xc1, 0x09, 0x9a, 0x06, 0xc9, 0x07,
+    0xa4, 0x06, 0x11, 0x02, 0x09, 0x02, 0xc8, 0x17,
+    0x40, 0x06, 0x01, 0xda, 0x7a, 0xc1, 0x51, 0x94,
+    0xc8, 0x09, 0xc8, 0x06, 0xc9, 0x07, 0xc6, 0x06,
+    0xc1, 0x09, 0x9a, 0x06, 0x11, 0x02, 0x09, 0x02,
+    0xc8, 0x17, 0x08, 0x00, 0x01, 0xda, 0x7a, 0xc1,
+    0x51, 0x94, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf,
+    0xe7, 0x57, 0x00, 0x00, 0x76, 0x06, 0x97, 0xc0,
+    0x9f, 0xaf, 0x04, 0x00, 0xe7, 0x09, 0xbe, 0x06,
+    0xba, 0x06, 0xe7, 0x57, 0xff, 0xff, 0xba, 0x06,
+    0x04, 0xc1, 0xe7, 0x07, 0x10, 0x0b, 0xb8, 0x06,
+    0x97, 0xcf, 0xe7, 0x17, 0x32, 0x00, 0xba, 0x06,
+    0xe7, 0x67, 0xff, 0x07, 0xba, 0x06, 0xe7, 0x07,
+    0x46, 0x0b, 0xb8, 0x06, 0x97, 0xcf, 0xe7, 0x57,
+    0x00, 0x00, 0xc0, 0x06, 0x23, 0xc0, 0xe7, 0x07,
+    0x04, 0x00, 0x90, 0xc0, 0xe7, 0x07, 0x00, 0x80,
+    0x80, 0xc0, 0xe7, 0x07, 0x00, 0x00, 0x80, 0xc0,
+    0xe7, 0x07, 0x00, 0x80, 0x80, 0xc0, 0xc0, 0x07,
+    0x00, 0x00, 0xc0, 0x07, 0x00, 0x00, 0xc0, 0x07,
+    0x00, 0x00, 0xe7, 0x07, 0x00, 0x00, 0x80, 0xc0,
+    0xe7, 0x07, 0x00, 0x80, 0x80, 0xc0, 0xe7, 0x07,
+    0x00, 0x80, 0x40, 0xc0, 0xc0, 0x07, 0x00, 0x00,
+    0xe7, 0x07, 0x00, 0x00, 0x40, 0xc0, 0xe7, 0x07,
+    0x00, 0x00, 0x80, 0xc0, 0xe7, 0x07, 0x04, 0x00,
+    0x90, 0xc0, 0xe7, 0x07, 0x00, 0x02, 0x40, 0xc0,
+    0xe7, 0x07, 0x0c, 0x02, 0x40, 0xc0, 0xe7, 0x07,
+    0x00, 0x00, 0xc0, 0x06, 0xe7, 0x07, 0x00, 0x00,
+    0xb8, 0x06, 0xe7, 0x07, 0x00, 0x00, 0xd2, 0x06,
+    0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x9f, 0xaf,
+    0x34, 0x02, 0xe7, 0x05, 0x00, 0xc0, 0x9f, 0xaf,
+    0xc4, 0x01, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0,
+    0x17, 0x00, 0x17, 0x02, 0x97, 0x02, 0xe7, 0x57,
+    0x00, 0x00, 0xa8, 0x06, 0x06, 0xc0, 0xc0, 0x09,
+    0x92, 0xc0, 0xc0, 0x77, 0x09, 0x02, 0x9f, 0xc1,
+    0x5c, 0x05, 0x9f, 0xcf, 0x32, 0x06, 0xd7, 0x09,
+    0x0e, 0xc0, 0xe7, 0x07, 0x00, 0x00, 0x0e, 0xc0,
+    0x9f, 0xaf, 0x02, 0x0c, 0xe7, 0x05, 0x0e, 0xc0,
+    0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, 0x17, 0x02,
+    0xc8, 0x09, 0xb0, 0xc0, 0xe7, 0x67, 0xfe, 0x7f,
+    0xb0, 0xc0, 0xc8, 0x77, 0x00, 0x20, 0x9f, 0xc1,
+    0x64, 0xeb, 0xe7, 0x57, 0x00, 0x00, 0xc8, 0x02,
+    0x9f, 0xc1, 0x80, 0xeb, 0xc8, 0x99, 0xca, 0x02,
+    0xc8, 0x67, 0x04, 0x00, 0x9f, 0xc1, 0x96, 0xeb,
+    0x9f, 0xcf, 0x4c, 0xeb, 0xe7, 0x07, 0x00, 0x00,
+    0xa6, 0xc0, 0xe7, 0x09, 0xb0, 0xc0, 0xc8, 0x02,
+    0xe7, 0x07, 0x03, 0x00, 0xb0, 0xc0, 0x97, 0xcf,
+    0xc0, 0x09, 0xb0, 0x06, 0xc0, 0x37, 0x01, 0x00,
+    0x97, 0xc9, 0xc9, 0x09, 0xb2, 0x06, 0x02, 0x00,
+    0x41, 0x90, 0x48, 0x02, 0xc9, 0x17, 0x06, 0x00,
+    0x9f, 0xaf, 0x08, 0x04, 0x9f, 0xa2, 0x72, 0x0c,
+    0x02, 0xda, 0x77, 0xc1, 0x41, 0x60, 0x71, 0xc1,
+    0x97, 0xcf, 0x17, 0x02, 0x57, 0x02, 0x43, 0x04,
+    0x21, 0x04, 0xe0, 0x00, 0x43, 0x04, 0x21, 0x04,
+    0xe0, 0x00, 0x43, 0x04, 0x21, 0x04, 0xe0, 0x00,
+    0xc1, 0x07, 0x01, 0x00, 0xc9, 0x05, 0xc8, 0x05,
+    0x97, 0xcf, 0xe7, 0x07, 0x01, 0x00, 0x8e, 0x06,
+    0xc8, 0x07, 0x86, 0x06, 0xe7, 0x07, 0x00, 0x00,
+    0x86, 0x06, 0xe7, 0x07, 0x10, 0x08, 0x88, 0x06,
+    0xe7, 0x07, 0x04, 0x00, 0x8a, 0x06, 0xe7, 0x07,
+    0xbc, 0x0c, 0x8c, 0x06, 0xc1, 0x07, 0x03, 0x80,
+    0x50, 0xaf, 0x97, 0xcf, 0xe7, 0x07, 0x00, 0x00,
+    0x8e, 0x06, 0x97, 0xcf,
+    0x00, 0x00
+};
+
+/****************************************************************
+ *     kaweth_new_code_fix
+ ****************************************************************/
+static __u8 kaweth_new_code_fix[] = 
+{
+    0xB6, 0xC3, 0xAA, 0xBB, 0xCC, 0xDD,
+    0x02, 0x00, 0x08, 0x00, 0x28, 0x00, 0x2c, 0x00,
+    0x34, 0x00, 0x3c, 0x00, 0x40, 0x00, 0x48, 0x00,
+    0x54, 0x00, 0x58, 0x00, 0x5e, 0x00, 0x64, 0x00,
+    0x68, 0x00, 0x6e, 0x00, 0x6c, 0x00, 0x72, 0x00,
+    0x76, 0x00, 0x7c, 0x00, 0x80, 0x00, 0x86, 0x00,
+    0x8a, 0x00, 0x90, 0x00, 0x94, 0x00, 0x98, 0x00,
+    0x9e, 0x00, 0xa6, 0x00, 0xaa, 0x00, 0xb0, 0x00,
+    0xb4, 0x00, 0xb8, 0x00, 0xc0, 0x00, 0xc6, 0x00,
+    0xca, 0x00, 0xd0, 0x00, 0xd4, 0x00, 0xd8, 0x00,
+    0xe0, 0x00, 0xde, 0x00, 0xe8, 0x00, 0xf0, 0x00,
+    0xfc, 0x00, 0x04, 0x01, 0x0a, 0x01, 0x18, 0x01,
+    0x22, 0x01, 0x28, 0x01, 0x3a, 0x01, 0x3e, 0x01,
+    0x7e, 0x01, 0x98, 0x01, 0x9c, 0x01, 0xa2, 0x01,
+    0xac, 0x01, 0xb2, 0x01, 0xba, 0x01, 0xc0, 0x01,
+    0xc8, 0x01, 0xd0, 0x01, 0xd6, 0x01, 0xf4, 0x01,
+    0xfc, 0x01, 0x08, 0x02, 0x16, 0x02, 0x1a, 0x02,
+    0x22, 0x02, 0x2a, 0x02, 0x2e, 0x02, 0x3e, 0x02,
+    0x44, 0x02, 0x4a, 0x02, 0x50, 0x02, 0x64, 0x02,
+    0x62, 0x02, 0x6c, 0x02, 0x72, 0x02, 0x86, 0x02,
+    0x8c, 0x02, 0x90, 0x02, 0x9e, 0x02, 0xbc, 0x02,
+    0xd0, 0x02, 0xd8, 0x02, 0xdc, 0x02, 0xe0, 0x02,
+    0xe8, 0x02, 0xe6, 0x02, 0xf4, 0x02, 0xfe, 0x02,
+    0x04, 0x03, 0x0c, 0x03, 0x28, 0x03, 0x7c, 0x03,
+    0x90, 0x03, 0x94, 0x03, 0x9c, 0x03, 0xa2, 0x03,
+    0xc0, 0x03, 0xd0, 0x03, 0xd4, 0x03, 0xee, 0x03,
+    0xfa, 0x03, 0xfe, 0x03, 0x2e, 0x04, 0x32, 0x04,
+    0x3c, 0x04, 0x40, 0x04, 0x4e, 0x04, 0x76, 0x04,
+    0x7c, 0x04, 0x84, 0x04, 0x8a, 0x04, 0x8e, 0x04,
+    0xa6, 0x04, 0xb0, 0x04, 0xb8, 0x04, 0xbe, 0x04,
+    0xd2, 0x04, 0xdc, 0x04, 0xee, 0x04, 0x10, 0x05,
+    0x1a, 0x05, 0x24, 0x05, 0x2a, 0x05, 0x36, 0x05,
+    0x34, 0x05, 0x3c, 0x05, 0x42, 0x05, 0x64, 0x05,
+    0x6a, 0x05, 0x6e, 0x05, 0x86, 0x05, 0x22, 0x06,
+    0x26, 0x06, 0x2c, 0x06, 0x30, 0x06, 0x42, 0x06,
+    0x4a, 0x06, 0x4e, 0x06, 0x56, 0x06, 0x54, 0x06,
+    0x5a, 0x06, 0x60, 0x06, 0x66, 0x06, 0xe8, 0x06,
+    0xee, 0x06, 0xf4, 0x06, 0x16, 0x07, 0x26, 0x07,
+    0x2c, 0x07, 0x32, 0x07, 0x36, 0x07, 0x3a, 0x07,
+    0x3e, 0x07, 0x52, 0x07, 0x56, 0x07, 0x5a, 0x07,
+    0x64, 0x07, 0x76, 0x07, 0x7a, 0x07, 0x80, 0x07,
+    0x84, 0x07, 0x8a, 0x07, 0x9e, 0x07, 0xa2, 0x07,
+    0xda, 0x07, 0xde, 0x07, 0xe2, 0x07, 0xe6, 0x07,
+    0xea, 0x07, 0xee, 0x07, 0xf2, 0x07, 0xf6, 0x07,
+    0x0e, 0x08, 0x16, 0x08, 0x18, 0x08, 0x1a, 0x08,
+    0x1c, 0x08, 0x1e, 0x08, 0x20, 0x08, 0x22, 0x08,
+    0x24, 0x08, 0x26, 0x08, 0x28, 0x08, 0x2a, 0x08,
+    0x2c, 0x08, 0x2e, 0x08, 0x32, 0x08, 0x3a, 0x08,
+    0x46, 0x08, 0x4e, 0x08, 0x54, 0x08, 0x5e, 0x08,
+    0x78, 0x08, 0x7e, 0x08, 0x82, 0x08, 0x86, 0x08,
+    0x8c, 0x08, 0x90, 0x08, 0x98, 0x08, 0x9e, 0x08,
+    0xa4, 0x08, 0xaa, 0x08, 0xb0, 0x08, 0xae, 0x08,
+    0xb4, 0x08, 0xbe, 0x08, 0xc4, 0x08, 0xc2, 0x08,
+    0xca, 0x08, 0xc8, 0x08, 0xd4, 0x08, 0xe4, 0x08,
+    0xe8, 0x08, 0xf6, 0x08, 0x14, 0x09, 0x12, 0x09,
+    0x1a, 0x09, 0x20, 0x09, 0x26, 0x09, 0x24, 0x09,
+    0x2a, 0x09, 0x3e, 0x09, 0x4c, 0x09, 0x56, 0x09,
+    0x70, 0x09, 0x74, 0x09, 0x78, 0x09, 0x7e, 0x09,
+    0x7c, 0x09, 0x82, 0x09, 0x98, 0x09, 0x9c, 0x09,
+    0xa0, 0x09, 0xa6, 0x09, 0xb8, 0x09, 0xdc, 0x09,
+    0xe8, 0x09, 0xec, 0x09, 0xfc, 0x09, 0x12, 0x0a,
+    0x18, 0x0a, 0x1e, 0x0a, 0x42, 0x0a, 0x46, 0x0a,
+    0x4e, 0x0a, 0x54, 0x0a, 0x5a, 0x0a, 0x5e, 0x0a,
+    0x68, 0x0a, 0x6e, 0x0a, 0x72, 0x0a, 0x78, 0x0a,
+    0x76, 0x0a, 0x7c, 0x0a, 0x80, 0x0a, 0x84, 0x0a,
+    0x94, 0x0a, 0xa4, 0x0a, 0xb8, 0x0a, 0xbe, 0x0a,
+    0xbc, 0x0a, 0xc2, 0x0a, 0xc8, 0x0a, 0xc6, 0x0a,
+    0xcc, 0x0a, 0xd0, 0x0a, 0xd4, 0x0a, 0xd8, 0x0a,
+    0xdc, 0x0a, 0xe0, 0x0a, 0xf2, 0x0a, 0xf6, 0x0a,
+    0xfa, 0x0a, 0x14, 0x0b, 0x1a, 0x0b, 0x20, 0x0b,
+    0x1e, 0x0b, 0x26, 0x0b, 0x2e, 0x0b, 0x2c, 0x0b,
+    0x36, 0x0b, 0x3c, 0x0b, 0x42, 0x0b, 0x40, 0x0b,
+    0x4a, 0x0b, 0xaa, 0x0b, 0xb0, 0x0b, 0xb6, 0x0b,
+    0xc0, 0x0b, 0xc8, 0x0b, 0xda, 0x0b, 0xe8, 0x0b,
+    0xec, 0x0b, 0xfa, 0x0b, 0x4a, 0x0c, 0x54, 0x0c,
+    0x62, 0x0c, 0x66, 0x0c, 0x96, 0x0c, 0x9a, 0x0c,
+    0xa0, 0x0c, 0xa6, 0x0c, 0xa4, 0x0c, 0xac, 0x0c,
+    0xb2, 0x0c, 0xb0, 0x0c, 0xc0, 0x0c,
+    0x00, 0x00
+};
+
+
+static const int len_kaweth_trigger_code = sizeof(kaweth_trigger_code);
+static const int len_kaweth_trigger_code_fix = sizeof(kaweth_trigger_code_fix);
+static const int len_kaweth_new_code = sizeof(kaweth_new_code);
+static const int len_kaweth_new_code_fix = sizeof(kaweth_new_code_fix);
diff --git a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c
new file mode 100644
index 0000000..f6c19d7
--- /dev/null
+++ b/drivers/usb/net/pegasus.c
@@ -0,0 +1,1408 @@
+/*
+ *  Copyright (c) 1999-2005 Petko Manolov (petkan@users.sourceforge.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *	ChangeLog:
+ *		....	Most of the time spent on reading sources & docs.
+ *		v0.2.x	First official release for the Linux kernel.
+ *		v0.3.0	Beutified and structured, some bugs fixed.
+ *		v0.3.x	URBifying bulk requests and bugfixing. First relatively
+ *			stable release. Still can touch device's registers only
+ *			from top-halves.
+ *		v0.4.0	Control messages remained unurbified are now URBs.
+ *			Now we can touch the HW at any time.
+ *		v0.4.9	Control urbs again use process context to wait. Argh...
+ *			Some long standing bugs (enable_net_traffic) fixed.
+ *			Also nasty trick about resubmiting control urb from
+ *			interrupt context used. Please let me know how it
+ *			behaves. Pegasus II support added since this version.
+ *			TODO: suppressing HCD warnings spewage on disconnect.
+ *		v0.4.13	Ethernet address is now set at probe(), not at open()
+ *			time as this seems to break dhcpd. 
+ *		v0.5.0	branch to 2.5.x kernels
+ *		v0.5.1	ethtool support added
+ *		v0.5.5	rx socket buffers are in a pool and the their allocation
+ * 			is out of the interrupt routine.
+ */
+
+#undef	DEBUG
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/module.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+#include "pegasus.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.6.12 (2005/01/13)"
+#define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>"
+#define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver"
+
+static const char driver_name[] = "pegasus";
+
+#undef	PEGASUS_WRITE_EEPROM
+#define	BMSR_MEDIA	(BMSR_10HALF | BMSR_10FULL | BMSR_100HALF | \
+			BMSR_100FULL | BMSR_ANEGCAPABLE)
+
+static int loopback = 0;
+static int mii_mode = 0;
+static int multicast_filter_limit = 32;
+
+static struct usb_eth_dev usb_dev_id[] = {
+#define	PEGASUS_DEV(pn, vid, pid, flags)	\
+	{.name = pn, .vendor = vid, .device = pid, .private = flags},
+#include "pegasus.h"
+#undef	PEGASUS_DEV
+	{NULL, 0, 0, 0}
+};
+
+static struct usb_device_id pegasus_ids[] = {
+#define	PEGASUS_DEV(pn, vid, pid, flags) \
+	{.match_flags = USB_DEVICE_ID_MATCH_DEVICE, .idVendor = vid, .idProduct = pid},
+#include "pegasus.h"
+#undef	PEGASUS_DEV
+	{}
+};
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+module_param(loopback, bool, 0);
+module_param(mii_mode, bool, 0);
+MODULE_PARM_DESC(loopback, "Enable MAC loopback mode (bit 0)");
+MODULE_PARM_DESC(mii_mode, "Enable HomePNA mode (bit 0),default=MII mode = 0");
+
+/* use ethtool to change the level for any given device */
+static int msg_level = -1;
+module_param (msg_level, int, 0);
+MODULE_PARM_DESC (msg_level, "Override default message level");
+
+MODULE_DEVICE_TABLE(usb, pegasus_ids);
+
+static int update_eth_regs_async(pegasus_t *);
+/* Aargh!!! I _really_ hate such tweaks */
+static void ctrl_callback(struct urb *urb, struct pt_regs *regs)
+{
+	pegasus_t *pegasus = urb->context;
+
+	if (!pegasus)
+		return;
+
+	switch (urb->status) {
+	case 0:
+		if (pegasus->flags & ETH_REGS_CHANGE) {
+			pegasus->flags &= ~ETH_REGS_CHANGE;
+			pegasus->flags |= ETH_REGS_CHANGED;
+			update_eth_regs_async(pegasus);
+			return;
+		}
+		break;
+	case -EINPROGRESS:
+		return;
+	case -ENOENT:
+		break;
+	default:
+		if (netif_msg_drv(pegasus))
+			dev_err(&pegasus->intf->dev, "%s, status %d\n",
+				__FUNCTION__, urb->status);
+	}
+	pegasus->flags &= ~ETH_REGS_CHANGED;
+	wake_up(&pegasus->ctrl_wait);
+}
+
+static int get_registers(pegasus_t * pegasus, __u16 indx, __u16 size,
+			 void *data)
+{
+	int ret;
+	char *buffer;
+	DECLARE_WAITQUEUE(wait, current);
+
+	buffer = kmalloc(size, GFP_KERNEL);
+	if (!buffer) {
+		if (netif_msg_drv(pegasus))
+			dev_warn(&pegasus->intf->dev, "out of memory in %s\n",
+					__FUNCTION__);
+		return -ENOMEM;
+	}
+	add_wait_queue(&pegasus->ctrl_wait, &wait);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	while (pegasus->flags & ETH_REGS_CHANGED)
+		schedule();
+	remove_wait_queue(&pegasus->ctrl_wait, &wait);
+	set_current_state(TASK_RUNNING);
+
+	pegasus->dr.bRequestType = PEGASUS_REQT_READ;
+	pegasus->dr.bRequest = PEGASUS_REQ_GET_REGS;
+	pegasus->dr.wValue = cpu_to_le16(0);
+	pegasus->dr.wIndex = cpu_to_le16p(&indx);
+	pegasus->dr.wLength = cpu_to_le16p(&size);
+	pegasus->ctrl_urb->transfer_buffer_length = size;
+
+	usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb,
+			     usb_rcvctrlpipe(pegasus->usb, 0),
+			     (char *) &pegasus->dr,
+			     buffer, size, ctrl_callback, pegasus);
+
+	add_wait_queue(&pegasus->ctrl_wait, &wait);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+
+	/* using ATOMIC, we'd never wake up if we slept */
+	if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) {
+		if (netif_msg_drv(pegasus))
+			dev_err(&pegasus->intf->dev, "%s, status %d\n",
+					__FUNCTION__, ret);
+		goto out;
+	}
+
+	schedule();
+out:
+	remove_wait_queue(&pegasus->ctrl_wait, &wait);
+	memcpy(data, buffer, size);
+	kfree(buffer);
+
+	return ret;
+}
+
+static int set_registers(pegasus_t * pegasus, __u16 indx, __u16 size,
+			 void *data)
+{
+	int ret;
+	char *buffer;
+	DECLARE_WAITQUEUE(wait, current);
+
+	buffer = kmalloc(size, GFP_KERNEL);
+	if (!buffer) {
+		if (netif_msg_drv(pegasus))
+			dev_warn(&pegasus->intf->dev, "out of memory in %s\n",
+					__FUNCTION__);
+		return -ENOMEM;
+	}
+	memcpy(buffer, data, size);
+
+	add_wait_queue(&pegasus->ctrl_wait, &wait);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	while (pegasus->flags & ETH_REGS_CHANGED)
+		schedule();
+	remove_wait_queue(&pegasus->ctrl_wait, &wait);
+	set_current_state(TASK_RUNNING);
+
+	pegasus->dr.bRequestType = PEGASUS_REQT_WRITE;
+	pegasus->dr.bRequest = PEGASUS_REQ_SET_REGS;
+	pegasus->dr.wValue = cpu_to_le16(0);
+	pegasus->dr.wIndex = cpu_to_le16p(&indx);
+	pegasus->dr.wLength = cpu_to_le16p(&size);
+	pegasus->ctrl_urb->transfer_buffer_length = size;
+
+	usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb,
+			     usb_sndctrlpipe(pegasus->usb, 0),
+			     (char *) &pegasus->dr,
+			     buffer, size, ctrl_callback, pegasus);
+
+	add_wait_queue(&pegasus->ctrl_wait, &wait);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+
+	if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) {
+		if (netif_msg_drv(pegasus))
+			dev_err(&pegasus->intf->dev, "%s, status %d\n",
+					__FUNCTION__, ret);
+		goto out;
+	}
+
+	schedule();
+out:
+	remove_wait_queue(&pegasus->ctrl_wait, &wait);
+	kfree(buffer);
+
+	return ret;
+}
+
+static int set_register(pegasus_t * pegasus, __u16 indx, __u8 data)
+{
+	int ret;
+	char *tmp;
+	DECLARE_WAITQUEUE(wait, current);
+
+	tmp = kmalloc(1, GFP_KERNEL);
+	if (!tmp) {
+		if (netif_msg_drv(pegasus))
+			dev_warn(&pegasus->intf->dev, "out of memory in %s\n",
+					__FUNCTION__);
+		return -ENOMEM;
+	}
+	memcpy(tmp, &data, 1);
+	add_wait_queue(&pegasus->ctrl_wait, &wait);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	while (pegasus->flags & ETH_REGS_CHANGED)
+		schedule();
+	remove_wait_queue(&pegasus->ctrl_wait, &wait);
+	set_current_state(TASK_RUNNING);
+
+	pegasus->dr.bRequestType = PEGASUS_REQT_WRITE;
+	pegasus->dr.bRequest = PEGASUS_REQ_SET_REG;
+	pegasus->dr.wValue = cpu_to_le16(data);
+	pegasus->dr.wIndex = cpu_to_le16p(&indx);
+	pegasus->dr.wLength = cpu_to_le16(1);
+	pegasus->ctrl_urb->transfer_buffer_length = 1;
+
+	usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb,
+			     usb_sndctrlpipe(pegasus->usb, 0),
+			     (char *) &pegasus->dr,
+			     &tmp, 1, ctrl_callback, pegasus);
+
+	add_wait_queue(&pegasus->ctrl_wait, &wait);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+
+	if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC))) {
+		if (netif_msg_drv(pegasus))
+			dev_err(&pegasus->intf->dev, "%s, status %d\n",
+					__FUNCTION__, ret);
+		goto out;
+	}
+
+	schedule();
+out:
+	remove_wait_queue(&pegasus->ctrl_wait, &wait);
+	kfree(tmp);
+
+	return ret;
+}
+
+static int update_eth_regs_async(pegasus_t * pegasus)
+{
+	int ret;
+
+	pegasus->dr.bRequestType = PEGASUS_REQT_WRITE;
+	pegasus->dr.bRequest = PEGASUS_REQ_SET_REGS;
+	pegasus->dr.wValue = 0;
+	pegasus->dr.wIndex = cpu_to_le16(EthCtrl0);
+	pegasus->dr.wLength = cpu_to_le16(3);
+	pegasus->ctrl_urb->transfer_buffer_length = 3;
+
+	usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb,
+			     usb_sndctrlpipe(pegasus->usb, 0),
+			     (char *) &pegasus->dr,
+			     pegasus->eth_regs, 3, ctrl_callback, pegasus);
+
+	if ((ret = usb_submit_urb(pegasus->ctrl_urb, GFP_ATOMIC)))
+		if (netif_msg_drv(pegasus))
+			dev_err(&pegasus->intf->dev, "%s, status %d\n",
+					__FUNCTION__, ret);
+
+	return ret;
+}
+
+static int read_mii_word(pegasus_t * pegasus, __u8 phy, __u8 indx, __u16 * regd)
+{
+	int i;
+	__u8 data[4] = { phy, 0, 0, indx };
+	__le16 regdi;
+	int ret;
+
+	ret = set_register(pegasus, PhyCtrl, 0);
+	ret = set_registers(pegasus, PhyAddr, sizeof (data), data);
+	ret = set_register(pegasus, PhyCtrl, (indx | PHY_READ));
+	for (i = 0; i < REG_TIMEOUT; i++) {
+		ret = get_registers(pegasus, PhyCtrl, 1, data);
+		if (data[0] & PHY_DONE)
+			break;
+	}
+	if (i < REG_TIMEOUT) {
+		ret = get_registers(pegasus, PhyData, 2, &regdi);
+		*regd = le16_to_cpu(regdi);
+		return 1;
+	}
+	if (netif_msg_drv(pegasus))
+		dev_warn(&pegasus->intf->dev, "fail %s\n", __FUNCTION__);
+
+	return 0;
+}
+
+static int mdio_read(struct net_device *dev, int phy_id, int loc)
+{
+	pegasus_t *pegasus = (pegasus_t *) netdev_priv(dev);
+	u16 res;
+
+	read_mii_word(pegasus, phy_id, loc, &res);
+	return (int)res;
+}
+
+static int write_mii_word(pegasus_t * pegasus, __u8 phy, __u8 indx, __u16 regd)
+{
+	int i;
+	__u8 data[4] = { phy, 0, 0, indx };
+	int ret;
+
+	data[1] = (u8) regd;
+	data[2] = (u8) (regd >> 8);
+	ret = set_register(pegasus, PhyCtrl, 0);
+	ret = set_registers(pegasus, PhyAddr, sizeof(data), data);
+	ret = set_register(pegasus, PhyCtrl, (indx | PHY_WRITE));
+	for (i = 0; i < REG_TIMEOUT; i++) {
+		ret = get_registers(pegasus, PhyCtrl, 1, data);
+		if (data[0] & PHY_DONE)
+			break;
+	}
+	if (i < REG_TIMEOUT)
+		return 0;
+
+	if (netif_msg_drv(pegasus))
+		dev_warn(&pegasus->intf->dev, "fail %s\n", __FUNCTION__);
+	return 1;
+}
+
+static void mdio_write(struct net_device *dev, int phy_id, int loc, int val)
+{
+	pegasus_t *pegasus = (pegasus_t *) netdev_priv(dev);
+
+	write_mii_word(pegasus, phy_id, loc, val);
+}
+
+static int read_eprom_word(pegasus_t * pegasus, __u8 index, __u16 * retdata)
+{
+	int i;
+	__u8 tmp;
+	__le16 retdatai;
+	int ret;
+
+	ret = set_register(pegasus, EpromCtrl, 0);
+	ret = set_register(pegasus, EpromOffset, index);
+	ret = set_register(pegasus, EpromCtrl, EPROM_READ);
+
+	for (i = 0; i < REG_TIMEOUT; i++) {
+		ret = get_registers(pegasus, EpromCtrl, 1, &tmp);
+		if (tmp & EPROM_DONE)
+			break;
+	}
+	if (i < REG_TIMEOUT) {
+		ret = get_registers(pegasus, EpromData, 2, &retdatai);
+		*retdata = le16_to_cpu(retdatai);
+		return 0;
+	}
+
+	if (netif_msg_drv(pegasus))
+		dev_warn(&pegasus->intf->dev, "fail %s\n", __FUNCTION__);
+	return -1;
+}
+
+#ifdef	PEGASUS_WRITE_EEPROM
+static inline void enable_eprom_write(pegasus_t * pegasus)
+{
+	__u8 tmp;
+	int ret;
+
+	ret = get_registers(pegasus, EthCtrl2, 1, &tmp);
+	ret = set_register(pegasus, EthCtrl2, tmp | EPROM_WR_ENABLE);
+}
+
+static inline void disable_eprom_write(pegasus_t * pegasus)
+{
+	__u8 tmp;
+	int ret;
+
+	ret = get_registers(pegasus, EthCtrl2, 1, &tmp);
+	ret = set_register(pegasus, EpromCtrl, 0);
+	ret = set_register(pegasus, EthCtrl2, tmp & ~EPROM_WR_ENABLE);
+}
+
+static int write_eprom_word(pegasus_t * pegasus, __u8 index, __u16 data)
+{
+	int i;
+	__u8 tmp, d[4] = { 0x3f, 0, 0, EPROM_WRITE };
+	int ret;
+
+	ret = set_registers(pegasus, EpromOffset, 4, d);
+	enable_eprom_write(pegasus);
+	ret = set_register(pegasus, EpromOffset, index);
+	ret = set_registers(pegasus, EpromData, 2, &data);
+	ret = set_register(pegasus, EpromCtrl, EPROM_WRITE);
+
+	for (i = 0; i < REG_TIMEOUT; i++) {
+		ret = get_registers(pegasus, EpromCtrl, 1, &tmp);
+		if (tmp & EPROM_DONE)
+			break;
+	}
+	disable_eprom_write(pegasus);
+	if (i < REG_TIMEOUT)
+		return 0;
+	if (netif_msg_drv(pegasus))
+		dev_warn(&pegasus->intf->dev, "fail %s\n", __FUNCTION__);
+	return -1;
+}
+#endif				/* PEGASUS_WRITE_EEPROM */
+
+static inline void get_node_id(pegasus_t * pegasus, __u8 * id)
+{
+	int i;
+	__u16 w16;
+
+	for (i = 0; i < 3; i++) {
+		read_eprom_word(pegasus, i, &w16);
+		((__le16 *) id)[i] = cpu_to_le16p(&w16);
+	}
+}
+
+static void set_ethernet_addr(pegasus_t * pegasus)
+{
+	__u8 node_id[6];
+	int ret;
+
+	get_node_id(pegasus, node_id);
+	ret = set_registers(pegasus, EthID, sizeof (node_id), node_id);
+	memcpy(pegasus->net->dev_addr, node_id, sizeof (node_id));
+}
+
+static inline int reset_mac(pegasus_t * pegasus)
+{
+	__u8 data = 0x8;
+	int i;
+	int ret;
+
+	ret = set_register(pegasus, EthCtrl1, data);
+	for (i = 0; i < REG_TIMEOUT; i++) {
+		ret = get_registers(pegasus, EthCtrl1, 1, &data);
+		if (~data & 0x08) {
+			if (loopback & 1)
+				break;
+			if (mii_mode && (pegasus->features & HAS_HOME_PNA))
+				ret = set_register(pegasus, Gpio1, 0x34);
+			else
+				ret = set_register(pegasus, Gpio1, 0x26);
+			ret = set_register(pegasus, Gpio0, pegasus->features);
+			ret = set_register(pegasus, Gpio0, DEFAULT_GPIO_SET);
+			break;
+		}
+	}
+	if (i == REG_TIMEOUT)
+		return 1;
+
+	if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS ||
+	    usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) {
+		ret = set_register(pegasus, Gpio0, 0x24);
+		ret = set_register(pegasus, Gpio0, 0x26);
+	}
+	if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_ELCON) {
+		__u16 auxmode;
+		read_mii_word(pegasus, 3, 0x1b, &auxmode);
+		write_mii_word(pegasus, 3, 0x1b, auxmode | 4);
+	}
+
+	return 0;
+}
+
+static int enable_net_traffic(struct net_device *dev, struct usb_device *usb)
+{
+	__u16 linkpart;
+	__u8 data[4];
+	pegasus_t *pegasus = netdev_priv(dev);
+	int ret;
+
+	read_mii_word(pegasus, pegasus->phy, MII_LPA, &linkpart);
+	data[0] = 0xc9;
+	data[1] = 0;
+	if (linkpart & (ADVERTISE_100FULL | ADVERTISE_10FULL))
+		data[1] |= 0x20;	/* set full duplex */
+	if (linkpart & (ADVERTISE_100FULL | ADVERTISE_100HALF))
+		data[1] |= 0x10;	/* set 100 Mbps */
+	if (mii_mode)
+		data[1] = 0;
+	data[2] = (loopback & 1) ? 0x09 : 0x01;
+
+	memcpy(pegasus->eth_regs, data, sizeof (data));
+	ret = set_registers(pegasus, EthCtrl0, 3, data);
+
+	if (usb_dev_id[pegasus->dev_index].vendor == VENDOR_LINKSYS ||
+	    usb_dev_id[pegasus->dev_index].vendor == VENDOR_DLINK) {
+		u16 auxmode;
+		read_mii_word(pegasus, 0, 0x1b, &auxmode);
+		write_mii_word(pegasus, 0, 0x1b, auxmode | 4);
+	}
+
+	return 0;
+}
+
+static void fill_skb_pool(pegasus_t * pegasus)
+{
+	int i;
+
+	for (i = 0; i < RX_SKBS; i++) {
+		if (pegasus->rx_pool[i])
+			continue;
+		pegasus->rx_pool[i] = dev_alloc_skb(PEGASUS_MTU + 2);
+		/*
+		 ** we give up if the allocation fail. the tasklet will be
+		 ** rescheduled again anyway...
+		 */
+		if (pegasus->rx_pool[i] == NULL)
+			return;
+		pegasus->rx_pool[i]->dev = pegasus->net;
+		skb_reserve(pegasus->rx_pool[i], 2);
+	}
+}
+
+static void free_skb_pool(pegasus_t * pegasus)
+{
+	int i;
+
+	for (i = 0; i < RX_SKBS; i++) {
+		if (pegasus->rx_pool[i]) {
+			dev_kfree_skb(pegasus->rx_pool[i]);
+			pegasus->rx_pool[i] = NULL;
+		}
+	}
+}
+
+static inline struct sk_buff *pull_skb(pegasus_t * pegasus)
+{
+	int i;
+	struct sk_buff *skb;
+
+	for (i = 0; i < RX_SKBS; i++) {
+		if (likely(pegasus->rx_pool[i] != NULL)) {
+			skb = pegasus->rx_pool[i];
+			pegasus->rx_pool[i] = NULL;
+			return skb;
+		}
+	}
+	return NULL;
+}
+
+static void read_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+	pegasus_t *pegasus = urb->context;
+	struct net_device *net;
+	int rx_status, count = urb->actual_length;
+	u8 *buf = urb->transfer_buffer;
+	__u16 pkt_len;
+
+	if (!pegasus)
+		return;
+
+	net = pegasus->net;
+	if (!netif_device_present(net) || !netif_running(net))
+		return;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ETIMEDOUT:
+		if (netif_msg_rx_err(pegasus))
+			pr_debug("%s: reset MAC\n", net->name);
+		pegasus->flags &= ~PEGASUS_RX_BUSY;
+		break;
+	case -EPIPE:		/* stall, or disconnect from TT */
+		/* FIXME schedule work to clear the halt */
+		if (netif_msg_rx_err(pegasus))
+			printk(KERN_WARNING "%s: no rx stall recovery\n",
+					net->name);
+		return;
+	case -ENOENT:
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		if (netif_msg_ifdown(pegasus))
+			pr_debug("%s: rx unlink, %d\n", net->name, urb->status);
+		return;
+	default:
+		if (netif_msg_rx_err(pegasus))
+			pr_debug("%s: RX status %d\n", net->name, urb->status);
+		goto goon;
+	}
+
+	if (!count || count < 4)
+		goto goon;
+
+	rx_status = buf[count - 2];
+	if (rx_status & 0x1e) {
+		if (netif_msg_rx_err(pegasus))
+			pr_debug("%s: RX packet error %x\n",
+					net->name, rx_status);
+		pegasus->stats.rx_errors++;
+		if (rx_status & 0x06)	// long or runt
+			pegasus->stats.rx_length_errors++;
+		if (rx_status & 0x08)
+			pegasus->stats.rx_crc_errors++;
+		if (rx_status & 0x10)	// extra bits
+			pegasus->stats.rx_frame_errors++;
+		goto goon;
+	}
+	if (pegasus->chip == 0x8513) {
+		pkt_len = le32_to_cpu(*(__le32 *)urb->transfer_buffer);
+		pkt_len &= 0x0fff;
+		pegasus->rx_skb->data += 2;
+	} else {
+		pkt_len = buf[count - 3] << 8;
+		pkt_len += buf[count - 4];
+		pkt_len &= 0xfff;
+		pkt_len -= 8;
+	}
+
+	/*
+	 * at this point we are sure pegasus->rx_skb != NULL
+	 * so we go ahead and pass up the packet.
+	 */
+	skb_put(pegasus->rx_skb, pkt_len);
+	pegasus->rx_skb->protocol = eth_type_trans(pegasus->rx_skb, net);
+	netif_rx(pegasus->rx_skb);
+	pegasus->stats.rx_packets++;
+	pegasus->stats.rx_bytes += pkt_len;
+
+	if (pegasus->flags & PEGASUS_UNPLUG)
+		return;
+
+	spin_lock(&pegasus->rx_pool_lock);
+	pegasus->rx_skb = pull_skb(pegasus);
+	spin_unlock(&pegasus->rx_pool_lock);
+
+	if (pegasus->rx_skb == NULL)
+		goto tl_sched;
+goon:
+	usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb,
+			  usb_rcvbulkpipe(pegasus->usb, 1),
+			  pegasus->rx_skb->data, PEGASUS_MTU + 8,
+			  read_bulk_callback, pegasus);
+	if (usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC)) {
+		pegasus->flags |= PEGASUS_RX_URB_FAIL;
+		goto tl_sched;
+	} else {
+		pegasus->flags &= ~PEGASUS_RX_URB_FAIL;
+	}
+
+	return;
+
+tl_sched:
+	tasklet_schedule(&pegasus->rx_tl);
+}
+
+static void rx_fixup(unsigned long data)
+{
+	pegasus_t *pegasus;
+	unsigned long flags;
+
+	pegasus = (pegasus_t *) data;
+	if (pegasus->flags & PEGASUS_UNPLUG)
+		return;
+
+	spin_lock_irqsave(&pegasus->rx_pool_lock, flags);
+	fill_skb_pool(pegasus);
+	if (pegasus->flags & PEGASUS_RX_URB_FAIL)
+		if (pegasus->rx_skb)
+			goto try_again;
+	if (pegasus->rx_skb == NULL) {
+		pegasus->rx_skb = pull_skb(pegasus);
+	}
+	if (pegasus->rx_skb == NULL) {
+		if (netif_msg_rx_err(pegasus))
+			printk(KERN_WARNING "%s: low on memory\n",
+					pegasus->net->name);
+		tasklet_schedule(&pegasus->rx_tl);
+		goto done;
+	}
+	usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb,
+			  usb_rcvbulkpipe(pegasus->usb, 1),
+			  pegasus->rx_skb->data, PEGASUS_MTU + 8,
+			  read_bulk_callback, pegasus);
+try_again:
+	if (usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC)) {
+		pegasus->flags |= PEGASUS_RX_URB_FAIL;
+		tasklet_schedule(&pegasus->rx_tl);
+	} else {
+		pegasus->flags &= ~PEGASUS_RX_URB_FAIL;
+	}
+done:
+	spin_unlock_irqrestore(&pegasus->rx_pool_lock, flags);
+}
+
+static void write_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+	pegasus_t *pegasus = urb->context;
+	struct net_device *net = pegasus->net;
+
+	if (!pegasus)
+		return;
+
+	if (!netif_device_present(net) || !netif_running(net))
+		return;
+
+	switch (urb->status) {
+	case -EPIPE:
+		/* FIXME schedule_work() to clear the tx halt */
+		netif_stop_queue(net);
+		if (netif_msg_tx_err(pegasus))
+			printk(KERN_WARNING "%s: no tx stall recovery\n",
+					net->name);
+		return;
+	case -ENOENT:
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		if (netif_msg_ifdown(pegasus))
+			pr_debug("%s: tx unlink, %d\n", net->name, urb->status);
+		return;
+	default:
+		if (netif_msg_tx_err(pegasus))
+			pr_info("%s: TX status %d\n", net->name, urb->status);
+		/* FALL THROUGH */
+	case 0:
+		break;
+	}
+
+	net->trans_start = jiffies;
+	netif_wake_queue(net);
+}
+
+static void intr_callback(struct urb *urb, struct pt_regs *regs)
+{
+	pegasus_t *pegasus = urb->context;
+	struct net_device *net;
+	int status;
+
+	if (!pegasus)
+		return;
+	net = pegasus->net;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ECONNRESET:	/* unlink */
+	case -ENOENT:
+	case -ESHUTDOWN:
+		return;
+	default:
+		/* some Pegasus-I products report LOTS of data
+		 * toggle errors... avoid log spamming
+		 */
+		if (netif_msg_timer(pegasus))
+			pr_debug("%s: intr status %d\n", net->name,
+					urb->status);
+	}
+
+	if (urb->actual_length >= 6) {
+		u8	* d = urb->transfer_buffer;
+
+		/* byte 0 == tx_status1, reg 2B */
+		if (d[0] & (TX_UNDERRUN|EXCESSIVE_COL
+					|LATE_COL|JABBER_TIMEOUT)) {
+			pegasus->stats.tx_errors++;
+			if (d[0] & TX_UNDERRUN)
+				pegasus->stats.tx_fifo_errors++;
+			if (d[0] & (EXCESSIVE_COL | JABBER_TIMEOUT))
+				pegasus->stats.tx_aborted_errors++;
+			if (d[0] & LATE_COL)
+				pegasus->stats.tx_window_errors++;
+		}
+
+		/* d[5].LINK_STATUS lies on some adapters.
+		 * d[0].NO_CARRIER kicks in only with failed TX.
+		 * ... so monitoring with MII may be safest.
+		 */
+		if (d[0] & NO_CARRIER)
+			netif_carrier_off(net);	
+		else
+			netif_carrier_on(net);
+
+		/* bytes 3-4 == rx_lostpkt, reg 2E/2F */
+		pegasus->stats.rx_missed_errors += ((d[3] & 0x7f) << 8) | d[4];
+	}
+
+	status = usb_submit_urb(urb, SLAB_ATOMIC);
+	if (status && netif_msg_timer(pegasus))
+		printk(KERN_ERR "%s: can't resubmit interrupt urb, %d\n",
+				net->name, status);
+}
+
+static void pegasus_tx_timeout(struct net_device *net)
+{
+	pegasus_t *pegasus = netdev_priv(net);
+	if (netif_msg_timer(pegasus))
+		printk(KERN_WARNING "%s: tx timeout\n", net->name);
+	pegasus->tx_urb->transfer_flags |= URB_ASYNC_UNLINK;
+	usb_unlink_urb(pegasus->tx_urb);
+	pegasus->stats.tx_errors++;
+}
+
+static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net)
+{
+	pegasus_t *pegasus = netdev_priv(net);
+	int count = ((skb->len + 2) & 0x3f) ? skb->len + 2 : skb->len + 3;
+	int res;
+	__u16 l16 = skb->len;
+
+	netif_stop_queue(net);
+
+	((__le16 *) pegasus->tx_buff)[0] = cpu_to_le16(l16);
+	memcpy(pegasus->tx_buff + 2, skb->data, skb->len);
+	usb_fill_bulk_urb(pegasus->tx_urb, pegasus->usb,
+			  usb_sndbulkpipe(pegasus->usb, 2),
+			  pegasus->tx_buff, count,
+			  write_bulk_callback, pegasus);
+	if ((res = usb_submit_urb(pegasus->tx_urb, GFP_ATOMIC))) {
+		if (netif_msg_tx_err(pegasus))
+			printk(KERN_WARNING "%s: fail tx, %d\n",
+					net->name, res);
+		switch (res) {
+		case -EPIPE:		/* stall, or disconnect from TT */
+			/* cleanup should already have been scheduled */
+			break;
+		case -ENODEV:		/* disconnect() upcoming */
+			break;
+		default:
+			pegasus->stats.tx_errors++;
+			netif_start_queue(net);
+		}
+	} else {
+		pegasus->stats.tx_packets++;
+		pegasus->stats.tx_bytes += skb->len;
+		net->trans_start = jiffies;
+	}
+	dev_kfree_skb(skb);
+
+	return 0;
+}
+
+static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev)
+{
+	return &((pegasus_t *) netdev_priv(dev))->stats;
+}
+
+static inline void disable_net_traffic(pegasus_t * pegasus)
+{
+	int tmp = 0;
+	int ret;
+
+	ret = set_registers(pegasus, EthCtrl0, 2, &tmp);
+}
+
+static inline void get_interrupt_interval(pegasus_t * pegasus)
+{
+	__u8 data[2];
+
+	read_eprom_word(pegasus, 4, (__u16 *) data);
+	if (data[1] < 0x80) {
+		if (netif_msg_timer(pegasus))
+			dev_info(&pegasus->intf->dev,
+				"intr interval changed from %ums to %ums\n",
+				data[1], 0x80);
+		data[1] = 0x80;
+#ifdef	PEGASUS_WRITE_EEPROM
+		write_eprom_word(pegasus, 4, *(__u16 *) data);
+#endif
+	}
+	pegasus->intr_interval = data[1];
+}
+
+static void set_carrier(struct net_device *net)
+{
+	pegasus_t *pegasus = netdev_priv(net);
+	u16 tmp;
+
+	if (read_mii_word(pegasus, pegasus->phy, MII_BMSR, &tmp))
+		return;
+	if (tmp & BMSR_LSTATUS)
+		netif_carrier_on(net);
+	else
+		netif_carrier_off(net);
+}
+
+static void free_all_urbs(pegasus_t * pegasus)
+{
+	usb_free_urb(pegasus->intr_urb);
+	usb_free_urb(pegasus->tx_urb);
+	usb_free_urb(pegasus->rx_urb);
+	usb_free_urb(pegasus->ctrl_urb);
+}
+
+static void unlink_all_urbs(pegasus_t * pegasus)
+{
+	usb_kill_urb(pegasus->intr_urb);
+	usb_kill_urb(pegasus->tx_urb);
+	usb_kill_urb(pegasus->rx_urb);
+	usb_kill_urb(pegasus->ctrl_urb);
+}
+
+static int alloc_urbs(pegasus_t * pegasus)
+{
+	pegasus->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!pegasus->ctrl_urb) {
+		return 0;
+	}
+	pegasus->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!pegasus->rx_urb) {
+		usb_free_urb(pegasus->ctrl_urb);
+		return 0;
+	}
+	pegasus->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!pegasus->tx_urb) {
+		usb_free_urb(pegasus->rx_urb);
+		usb_free_urb(pegasus->ctrl_urb);
+		return 0;
+	}
+	pegasus->intr_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!pegasus->intr_urb) {
+		usb_free_urb(pegasus->tx_urb);
+		usb_free_urb(pegasus->rx_urb);
+		usb_free_urb(pegasus->ctrl_urb);
+		return 0;
+	}
+
+	return 1;
+}
+
+static int pegasus_open(struct net_device *net)
+{
+	pegasus_t *pegasus = netdev_priv(net);
+	int res;
+
+	if (pegasus->rx_skb == NULL)
+		pegasus->rx_skb = pull_skb(pegasus);
+	/*
+	 ** Note: no point to free the pool.  it is empty :-)
+	 */
+	if (!pegasus->rx_skb)
+		return -ENOMEM;
+
+	res = set_registers(pegasus, EthID, 6, net->dev_addr);
+	
+	usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb,
+			  usb_rcvbulkpipe(pegasus->usb, 1),
+			  pegasus->rx_skb->data, PEGASUS_MTU + 8,
+			  read_bulk_callback, pegasus);
+	if ((res = usb_submit_urb(pegasus->rx_urb, GFP_KERNEL))) {
+		if (netif_msg_ifup(pegasus))
+			pr_debug("%s: failed rx_urb, %d", net->name, res);
+		goto exit;
+	}
+
+	usb_fill_int_urb(pegasus->intr_urb, pegasus->usb,
+			 usb_rcvintpipe(pegasus->usb, 3),
+			 pegasus->intr_buff, sizeof (pegasus->intr_buff),
+			 intr_callback, pegasus, pegasus->intr_interval);
+	if ((res = usb_submit_urb(pegasus->intr_urb, GFP_KERNEL))) {
+		if (netif_msg_ifup(pegasus))
+			pr_debug("%s: failed intr_urb, %d\n", net->name, res);
+		usb_kill_urb(pegasus->rx_urb);
+		goto exit;
+	}
+	if ((res = enable_net_traffic(net, pegasus->usb))) {
+		if (netif_msg_ifup(pegasus))
+			pr_debug("%s: can't enable_net_traffic() - %d\n",
+					net->name, res);
+		res = -EIO;
+		usb_kill_urb(pegasus->rx_urb);
+		usb_kill_urb(pegasus->intr_urb);
+		free_skb_pool(pegasus);
+		goto exit;
+	}
+	set_carrier(net);
+	netif_start_queue(net);
+	if (netif_msg_ifup(pegasus))
+		pr_debug("%s: open\n", net->name);
+	res = 0;
+exit:
+	return res;
+}
+
+static int pegasus_close(struct net_device *net)
+{
+	pegasus_t *pegasus = netdev_priv(net);
+
+	netif_stop_queue(net);
+	if (!(pegasus->flags & PEGASUS_UNPLUG))
+		disable_net_traffic(pegasus);
+	tasklet_kill(&pegasus->rx_tl);
+	unlink_all_urbs(pegasus);
+
+	return 0;
+}
+
+static void pegasus_get_drvinfo(struct net_device *dev,
+				struct ethtool_drvinfo *info)
+{
+	pegasus_t *pegasus = netdev_priv(dev);
+	strncpy(info->driver, driver_name, sizeof (info->driver) - 1);
+	strncpy(info->version, DRIVER_VERSION, sizeof (info->version) - 1);
+	usb_make_path(pegasus->usb, info->bus_info, sizeof (info->bus_info));
+}
+
+/* also handles three patterns of some kind in hardware */
+#define	WOL_SUPPORTED	(WAKE_MAGIC|WAKE_PHY)
+
+static void
+pegasus_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+	pegasus_t	*pegasus = netdev_priv(dev);
+
+	wol->supported = WAKE_MAGIC | WAKE_PHY;
+	wol->wolopts = pegasus->wolopts;
+}
+
+static int
+pegasus_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+	pegasus_t	*pegasus = netdev_priv(dev);
+	u8		reg78 = 0x04;
+	
+	if (wol->wolopts & ~WOL_SUPPORTED)
+		return -EINVAL;
+
+	if (wol->wolopts & WAKE_MAGIC)
+		reg78 |= 0x80;
+	if (wol->wolopts & WAKE_PHY)
+		reg78 |= 0x40;
+	/* FIXME this 0x10 bit still needs to get set in the chip... */
+	if (wol->wolopts)
+		pegasus->eth_regs[0] |= 0x10;
+	else
+		pegasus->eth_regs[0] &= ~0x10;
+	pegasus->wolopts = wol->wolopts;
+	return set_register(pegasus, WakeupControl, reg78);
+}
+
+static inline void pegasus_reset_wol(struct net_device *dev)
+{
+	struct ethtool_wolinfo wol;
+	
+	memset(&wol, 0, sizeof wol);
+	(void) pegasus_set_wol(dev, &wol);
+}
+
+static int
+pegasus_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+	pegasus_t *pegasus;
+
+	if (in_atomic())
+		return 0;
+
+	pegasus = netdev_priv(dev);
+	mii_ethtool_gset(&pegasus->mii, ecmd);
+
+	return 0;
+}
+
+static int
+pegasus_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+	pegasus_t *pegasus = netdev_priv(dev);
+	return mii_ethtool_sset(&pegasus->mii, ecmd);
+}
+
+static int pegasus_nway_reset(struct net_device *dev)
+{
+	pegasus_t *pegasus = netdev_priv(dev);
+	return mii_nway_restart(&pegasus->mii);
+}
+
+static u32 pegasus_get_link(struct net_device *dev)
+{
+	pegasus_t *pegasus = netdev_priv(dev);
+	return mii_link_ok(&pegasus->mii);
+}
+
+static u32 pegasus_get_msglevel(struct net_device *dev)
+{
+	pegasus_t *pegasus = netdev_priv(dev);
+	return pegasus->msg_enable;
+}
+
+static void pegasus_set_msglevel(struct net_device *dev, u32 v)
+{
+	pegasus_t *pegasus = netdev_priv(dev);
+	pegasus->msg_enable = v;
+}
+
+static struct ethtool_ops ops = {
+	.get_drvinfo = pegasus_get_drvinfo,
+	.get_settings = pegasus_get_settings,
+	.set_settings = pegasus_set_settings,
+	.nway_reset = pegasus_nway_reset,
+	.get_link = pegasus_get_link,
+	.get_msglevel = pegasus_get_msglevel,
+	.set_msglevel = pegasus_set_msglevel,
+	.get_wol = pegasus_get_wol,
+	.set_wol = pegasus_set_wol,
+};
+
+static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
+{
+	__u16 *data = (__u16 *) & rq->ifr_ifru;
+	pegasus_t *pegasus = netdev_priv(net);
+	int res;
+
+	switch (cmd) {
+	case SIOCDEVPRIVATE:
+		data[0] = pegasus->phy;
+	case SIOCDEVPRIVATE + 1:
+		read_mii_word(pegasus, data[0], data[1] & 0x1f, &data[3]);
+		res = 0;
+		break;
+	case SIOCDEVPRIVATE + 2:
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+		write_mii_word(pegasus, pegasus->phy, data[1] & 0x1f, data[2]);
+		res = 0;
+		break;
+	default:
+		res = -EOPNOTSUPP;
+	}
+	return res;
+}
+
+static void pegasus_set_multicast(struct net_device *net)
+{
+	pegasus_t *pegasus = netdev_priv(net);
+
+	if (net->flags & IFF_PROMISC) {
+		pegasus->eth_regs[EthCtrl2] |= RX_PROMISCUOUS;
+		if (netif_msg_link(pegasus))
+			pr_info("%s: Promiscuous mode enabled.\n", net->name);
+	} else if ((net->mc_count > multicast_filter_limit) ||
+		   (net->flags & IFF_ALLMULTI)) {
+		pegasus->eth_regs[EthCtrl0] |= RX_MULTICAST;
+		pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS;
+		if (netif_msg_link(pegasus))
+			pr_info("%s: set allmulti\n", net->name);
+	} else {
+		pegasus->eth_regs[EthCtrl0] &= ~RX_MULTICAST;
+		pegasus->eth_regs[EthCtrl2] &= ~RX_PROMISCUOUS;
+	}
+
+	pegasus->flags |= ETH_REGS_CHANGE;
+	ctrl_callback(pegasus->ctrl_urb, NULL);
+}
+
+static __u8 mii_phy_probe(pegasus_t * pegasus)
+{
+	int i;
+	__u16 tmp;
+
+	for (i = 0; i < 32; i++) {
+		read_mii_word(pegasus, i, MII_BMSR, &tmp);
+		if (tmp == 0 || tmp == 0xffff || (tmp & BMSR_MEDIA) == 0)
+			continue;
+		else
+			return i;
+	}
+
+	return 0xff;
+}
+
+static inline void setup_pegasus_II(pegasus_t * pegasus)
+{
+	__u8 data = 0xa5;
+	int ret;
+	
+	ret = set_register(pegasus, Reg1d, 0);
+	ret = set_register(pegasus, Reg7b, 1);
+	mdelay(100);
+	if ((pegasus->features & HAS_HOME_PNA) && mii_mode)
+		ret = set_register(pegasus, Reg7b, 0);
+	else
+		ret = set_register(pegasus, Reg7b, 2);
+
+	ret = set_register(pegasus, 0x83, data);
+	ret = get_registers(pegasus, 0x83, 1, &data);
+
+	if (data == 0xa5) {
+		pegasus->chip = 0x8513;
+	} else {
+		pegasus->chip = 0;
+	}
+
+	ret = set_register(pegasus, 0x80, 0xc0);
+	ret = set_register(pegasus, 0x83, 0xff);
+	ret = set_register(pegasus, 0x84, 0x01);
+	
+	if (pegasus->features & HAS_HOME_PNA && mii_mode)
+		ret = set_register(pegasus, Reg81, 6);
+	else
+		ret = set_register(pegasus, Reg81, 2);
+}
+
+
+static struct workqueue_struct *pegasus_workqueue = NULL;
+#define CARRIER_CHECK_DELAY (2 * HZ)
+
+static void check_carrier(void *data)
+{
+	pegasus_t *pegasus = data;
+	set_carrier(pegasus->net);
+	if (!(pegasus->flags & PEGASUS_UNPLUG)) {
+		queue_delayed_work(pegasus_workqueue, &pegasus->carrier_check,
+			CARRIER_CHECK_DELAY);
+	}
+}
+
+static int pegasus_probe(struct usb_interface *intf,
+			 const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev(intf);
+	struct net_device *net;
+	pegasus_t *pegasus;
+	int dev_index = id - pegasus_ids;
+	int res = -ENOMEM;
+
+	usb_get_dev(dev);
+	net = alloc_etherdev(sizeof(struct pegasus));
+	if (!net) {
+		dev_err(&intf->dev, "can't allocate %s\n", "device");
+		goto out;
+	}
+
+	pegasus = netdev_priv(net);
+	memset(pegasus, 0, sizeof (struct pegasus));
+	pegasus->dev_index = dev_index;
+	init_waitqueue_head(&pegasus->ctrl_wait);
+
+	if (!alloc_urbs(pegasus)) {
+		dev_err(&intf->dev, "can't allocate %s\n", "urbs");
+		goto out1;
+	}
+
+	tasklet_init(&pegasus->rx_tl, rx_fixup, (unsigned long) pegasus);
+
+	INIT_WORK(&pegasus->carrier_check, check_carrier, pegasus);
+
+	pegasus->intf = intf;
+	pegasus->usb = dev;
+	pegasus->net = net;
+	SET_MODULE_OWNER(net);
+	net->open = pegasus_open;
+	net->stop = pegasus_close;
+	net->watchdog_timeo = PEGASUS_TX_TIMEOUT;
+	net->tx_timeout = pegasus_tx_timeout;
+	net->do_ioctl = pegasus_ioctl;
+	net->hard_start_xmit = pegasus_start_xmit;
+	net->set_multicast_list = pegasus_set_multicast;
+	net->get_stats = pegasus_netdev_stats;
+	SET_ETHTOOL_OPS(net, &ops);
+	pegasus->mii.dev = net;
+	pegasus->mii.mdio_read = mdio_read;
+	pegasus->mii.mdio_write = mdio_write;
+	pegasus->mii.phy_id_mask = 0x1f;
+	pegasus->mii.reg_num_mask = 0x1f;
+	spin_lock_init(&pegasus->rx_pool_lock);
+	pegasus->msg_enable = netif_msg_init (msg_level, NETIF_MSG_DRV
+				| NETIF_MSG_PROBE | NETIF_MSG_LINK);
+
+	pegasus->features = usb_dev_id[dev_index].private;
+	get_interrupt_interval(pegasus);
+	if (reset_mac(pegasus)) {
+		dev_err(&intf->dev, "can't reset MAC\n");
+		res = -EIO;
+		goto out2;
+	}
+	set_ethernet_addr(pegasus);
+	fill_skb_pool(pegasus);
+	if (pegasus->features & PEGASUS_II) {
+		dev_info(&intf->dev, "setup Pegasus II specific registers\n");
+		setup_pegasus_II(pegasus);
+	}
+	pegasus->phy = mii_phy_probe(pegasus);
+	if (pegasus->phy == 0xff) {
+		dev_warn(&intf->dev, "can't locate MII phy, using default\n");
+		pegasus->phy = 1;
+	}
+	pegasus->mii.phy_id = pegasus->phy;
+	usb_set_intfdata(intf, pegasus);
+	SET_NETDEV_DEV(net, &intf->dev);
+	pegasus_reset_wol(net);
+	res = register_netdev(net);
+	if (res)
+		goto out3;
+	queue_delayed_work(pegasus_workqueue, &pegasus->carrier_check,
+				CARRIER_CHECK_DELAY);
+
+	dev_info(&intf->dev, "%s, %s, %02x:%02x:%02x:%02x:%02x:%02x\n",
+		net->name,
+		usb_dev_id[dev_index].name,
+		net->dev_addr [0], net->dev_addr [1],
+		net->dev_addr [2], net->dev_addr [3],
+		net->dev_addr [4], net->dev_addr [5]);
+	return 0;
+
+out3:
+	usb_set_intfdata(intf, NULL);
+	free_skb_pool(pegasus);
+out2:
+	free_all_urbs(pegasus);
+out1:
+	free_netdev(net);
+out:
+	usb_put_dev(dev);
+	return res;
+}
+
+static void pegasus_disconnect(struct usb_interface *intf)
+{
+	struct pegasus *pegasus = usb_get_intfdata(intf);
+
+	usb_set_intfdata(intf, NULL);
+	if (!pegasus) {
+		dev_dbg(&intf->dev, "unregistering non-bound device?\n");
+		return;
+	}
+
+	pegasus->flags |= PEGASUS_UNPLUG;
+	cancel_delayed_work(&pegasus->carrier_check);
+	unregister_netdev(pegasus->net);
+	usb_put_dev(interface_to_usbdev(intf));
+	free_all_urbs(pegasus);
+	free_skb_pool(pegasus);
+	if (pegasus->rx_skb)
+		dev_kfree_skb(pegasus->rx_skb);
+	free_netdev(pegasus->net);
+}
+
+static int pegasus_suspend (struct usb_interface *intf, pm_message_t state)
+{
+	struct pegasus *pegasus = usb_get_intfdata(intf);
+	
+	netif_device_detach (pegasus->net);
+	return 0;
+}
+
+static int pegasus_resume (struct usb_interface *intf)
+{
+	struct pegasus *pegasus = usb_get_intfdata(intf);
+
+	netif_device_attach (pegasus->net);
+	return 0;
+}
+
+static struct usb_driver pegasus_driver = {
+	.name = driver_name,
+	.probe = pegasus_probe,
+	.disconnect = pegasus_disconnect,
+	.id_table = pegasus_ids,
+	.suspend = pegasus_suspend,
+	.resume = pegasus_resume,
+};
+
+static int __init pegasus_init(void)
+{
+	pr_info("%s: %s, " DRIVER_DESC "\n", driver_name, DRIVER_VERSION);
+	pegasus_workqueue = create_singlethread_workqueue("pegasus");
+	if (!pegasus_workqueue)
+		return -ENOMEM;
+	return usb_register(&pegasus_driver);
+}
+
+static void __exit pegasus_exit(void)
+{
+	destroy_workqueue(pegasus_workqueue);
+	usb_deregister(&pegasus_driver);
+}
+
+module_init(pegasus_init);
+module_exit(pegasus_exit);
diff --git a/drivers/usb/net/pegasus.h b/drivers/usb/net/pegasus.h
new file mode 100644
index 0000000..13ccede
--- /dev/null
+++ b/drivers/usb/net/pegasus.h
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 1999-2003 Petko Manolov - Petkan (petkan@users.sourceforge.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+
+#ifndef	PEGASUS_DEV
+
+#define	PEGASUS_II		0x80000000
+#define	HAS_HOME_PNA		0x40000000
+
+#define	PEGASUS_MTU		1536
+#define	RX_SKBS			4
+
+#define	EPROM_WRITE		0x01
+#define	EPROM_READ		0x02
+#define	EPROM_DONE		0x04
+#define	EPROM_WR_ENABLE		0x10
+#define	EPROM_LOAD		0x20
+
+#define	PHY_DONE		0x80
+#define	PHY_READ		0x40
+#define	PHY_WRITE		0x20
+#define	DEFAULT_GPIO_RESET	0x24
+#define	LINKSYS_GPIO_RESET	0x24
+#define	DEFAULT_GPIO_SET	0x26
+
+#define	PEGASUS_PRESENT		0x00000001
+#define	PEGASUS_TX_BUSY		0x00000004
+#define	PEGASUS_RX_BUSY		0x00000008
+#define	CTRL_URB_RUNNING	0x00000010
+#define	CTRL_URB_SLEEP		0x00000020
+#define	PEGASUS_UNPLUG		0x00000040
+#define	PEGASUS_RX_URB_FAIL	0x00000080
+#define	ETH_REGS_CHANGE		0x40000000
+#define	ETH_REGS_CHANGED	0x80000000
+
+#define	RX_MULTICAST		2
+#define	RX_PROMISCUOUS		4
+
+#define	REG_TIMEOUT		(HZ)
+#define	PEGASUS_TX_TIMEOUT	(HZ*10)
+
+#define	TX_UNDERRUN		0x80
+#define	EXCESSIVE_COL		0x40
+#define	LATE_COL		0x20
+#define	NO_CARRIER		0x10
+#define	LOSS_CARRIER		0x08
+#define	JABBER_TIMEOUT		0x04
+
+#define	LINK_STATUS		0x01
+
+#define	PEGASUS_REQT_READ	0xc0
+#define	PEGASUS_REQT_WRITE	0x40
+#define	PEGASUS_REQ_GET_REGS	0xf0
+#define	PEGASUS_REQ_SET_REGS	0xf1
+#define	PEGASUS_REQ_SET_REG	PEGASUS_REQ_SET_REGS
+
+enum pegasus_registers {
+	EthCtrl0 = 0,
+	EthCtrl1 = 1,
+	EthCtrl2 = 2,
+	EthID = 0x10,
+	Reg1d = 0x1d,
+	EpromOffset = 0x20,
+	EpromData = 0x21,	/* 0x21 low, 0x22 high byte */
+	EpromCtrl = 0x23,
+	PhyAddr = 0x25,
+	PhyData = 0x26, 	/* 0x26 low, 0x27 high byte */
+	PhyCtrl = 0x28,
+	UsbStst = 0x2a,
+	EthTxStat0 = 0x2b,
+	EthTxStat1 = 0x2c,
+	EthRxStat = 0x2d,
+	WakeupControl = 0x78,
+	Reg7b = 0x7b,
+	Gpio0 = 0x7e,
+	Gpio1 = 0x7f,
+	Reg81 = 0x81,
+};
+
+
+typedef struct pegasus {
+	struct usb_device	*usb;
+	struct usb_interface	*intf;
+	struct net_device	*net;
+	struct net_device_stats	stats;
+	struct mii_if_info	mii;
+	unsigned		flags;
+	unsigned		features;
+	u32			msg_enable;
+	u32			wolopts;
+	int			dev_index;
+	int			intr_interval;
+	struct tasklet_struct	rx_tl;
+	struct work_struct	carrier_check;
+	struct urb		*ctrl_urb, *rx_urb, *tx_urb, *intr_urb;
+	struct sk_buff		*rx_pool[RX_SKBS];
+	struct sk_buff		*rx_skb;
+	struct usb_ctrlrequest	dr;
+	wait_queue_head_t	ctrl_wait;
+	spinlock_t		rx_pool_lock;
+	int			chip;
+	unsigned char		intr_buff[8];
+	__u8			tx_buff[PEGASUS_MTU];
+	__u8			eth_regs[4];
+	__u8			phy;
+	__u8			gpio_res;
+} pegasus_t;
+
+
+struct usb_eth_dev {
+	char	*name;
+	__u16	vendor;
+	__u16	device;
+	__u32	private; /* LSB is gpio reset value */
+};
+
+#define	VENDOR_3COM		0x0506
+#define	VENDOR_ABOCOM		0x07b8
+#define	VENDOR_ACCTON		0x083a
+#define	VENDOR_ADMTEK		0x07a6
+#define	VENDOR_AEILAB		0x3334
+#define	VENDOR_ALLIEDTEL	0x07c9
+#define	VENDOR_ATEN		0x0557
+#define	VENDOR_BELKIN		0x050d
+#define	VENDOR_BILLIONTON	0x08dd
+#define	VENDOR_COMPAQ		0x049f
+#define	VENDOR_COREGA		0x07aa
+#define	VENDOR_DLINK		0x2001
+#define	VENDOR_ELCON		0x0db7
+#define	VENDOR_ELSA		0x05cc
+#define	VENDOR_GIGABYTE		0x1044
+#define	VENDOR_HAWKING		0x0e66
+#define	VENDOR_HP		0x03f0
+#define	VENDOR_IODATA		0x04bb
+#define	VENDOR_KINGSTON		0x0951
+#define	VENDOR_LANEED		0x056e
+#define	VENDOR_LINKSYS		0x066b
+#define	VENDOR_MELCO		0x0411
+#define	VENDOR_MICROSOFT	0x045e
+#define	VENDOR_MOBILITY		0x1342
+#define	VENDOR_NETGEAR		0x0846
+#define	VENDOR_OCT		0x0b39
+#define	VENDOR_SMARTBRIDGES	0x08d1
+#define	VENDOR_SMC		0x0707
+#define	VENDOR_SOHOWARE		0x15e8
+#define	VENDOR_SIEMENS		0x067c
+
+
+#else	/* PEGASUS_DEV */
+
+PEGASUS_DEV( "3Com USB Ethernet 3C460B", VENDOR_3COM, 0x4601,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "ATEN USB Ethernet UC-110T", VENDOR_ATEN, 0x2007,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "USB HPNA/Ethernet", VENDOR_ABOCOM, 0x110c,
+		DEFAULT_GPIO_RESET | PEGASUS_II | HAS_HOME_PNA )
+PEGASUS_DEV( "USB HPNA/Ethernet", VENDOR_ABOCOM, 0x4104,
+		DEFAULT_GPIO_RESET | HAS_HOME_PNA )
+PEGASUS_DEV( "USB HPNA/Ethernet", VENDOR_ABOCOM, 0x4004,
+		DEFAULT_GPIO_RESET | HAS_HOME_PNA )
+PEGASUS_DEV( "USB HPNA/Ethernet", VENDOR_ABOCOM, 0x4007,
+		DEFAULT_GPIO_RESET | HAS_HOME_PNA )
+PEGASUS_DEV( "USB 10/100 Fast Ethernet", VENDOR_ABOCOM, 0x4102,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "USB 10/100 Fast Ethernet", VENDOR_ABOCOM, 0x4002,
+		DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "USB 10/100 Fast Ethernet", VENDOR_ABOCOM, 0x400b,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "USB 10/100 Fast Ethernet", VENDOR_ABOCOM, 0x400c,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "USB 10/100 Fast Ethernet", VENDOR_ABOCOM, 0xabc1,
+		DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "USB 10/100 Fast Ethernet", VENDOR_ABOCOM, 0x200c,
+		DEFAULT_GPIO_RESET | PEGASUS_II ) 	
+PEGASUS_DEV( "Accton USB 10/100 Ethernet Adapter", VENDOR_ACCTON, 0x1046,
+		DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "SpeedStream USB 10/100 Ethernet", VENDOR_ACCTON, 0x5046,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "ADMtek ADM8511 \"Pegasus II\" USB Ethernet",
+		VENDOR_ADMTEK, 0x8511,
+		DEFAULT_GPIO_RESET | PEGASUS_II | HAS_HOME_PNA )
+PEGASUS_DEV( "ADMtek ADM8513 \"Pegasus II\" USB Ethernet",
+		VENDOR_ADMTEK, 0x8513,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "ADMtek ADM8515 \"Pegasus II\" USB-2.0 Ethernet",
+		VENDOR_ADMTEK, 0x8515,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "ADMtek AN986 \"Pegasus\" USB Ethernet (evaluation board)",
+		VENDOR_ADMTEK, 0x0986,
+		DEFAULT_GPIO_RESET | HAS_HOME_PNA )
+PEGASUS_DEV( "AN986A USB MAC", VENDOR_ADMTEK, 1986,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "AEI USB Fast Ethernet Adapter", VENDOR_AEILAB, 0x1701,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "Allied Telesyn Int. AT-USB100", VENDOR_ALLIEDTEL, 0xb100,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "Belkin F5D5050 USB Ethernet", VENDOR_BELKIN, 0x0121,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "Billionton USB-100", VENDOR_BILLIONTON, 0x0986,
+		DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "Billionton USBLP-100", VENDOR_BILLIONTON, 0x0987,
+		DEFAULT_GPIO_RESET | HAS_HOME_PNA )
+PEGASUS_DEV( "iPAQ Networking 10/100 USB", VENDOR_COMPAQ, 0x8511,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "Billionton USBEL-100", VENDOR_BILLIONTON, 0x0988,
+		DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "Billionton USBE-100", VENDOR_BILLIONTON, 0x8511,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "Corega FEter USB-TX", VENDOR_COREGA, 0x0004,
+		DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "Corega FEter USB-TXS", VENDOR_COREGA, 0x000d,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK, 0x4001,
+		LINKSYS_GPIO_RESET )
+PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK, 0x4002,
+		LINKSYS_GPIO_RESET )
+PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK, 0x4102,
+		LINKSYS_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK, 0x400b,
+		LINKSYS_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK, 0x200c,
+		LINKSYS_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "D-Link DSB-650TX(PNA)", VENDOR_DLINK, 0x4003,
+		DEFAULT_GPIO_RESET | HAS_HOME_PNA )
+PEGASUS_DEV( "D-Link DSB-650", VENDOR_DLINK, 0xabc1,
+		DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "GOLDPFEIL USB Adapter", VENDOR_ELCON, 0x0002,
+		DEFAULT_GPIO_RESET | PEGASUS_II | HAS_HOME_PNA )
+PEGASUS_DEV( "EasiDock Ethernet", VENDOR_MOBILITY, 0x0304,
+		DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "Elsa Micolink USB2Ethernet", VENDOR_ELSA, 0x3000,
+		DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "GIGABYTE GN-BR402W Wireless Router", VENDOR_GIGABYTE, 0x8002,
+		DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "Hawking UF100 10/100 Ethernet", VENDOR_HAWKING, 0x400c,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "HP hn210c Ethernet USB", VENDOR_HP, 0x811c,
+		DEFAULT_GPIO_RESET | PEGASUS_II )	
+PEGASUS_DEV( "IO DATA USB ET/TX", VENDOR_IODATA, 0x0904,
+		DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "IO DATA USB ET/TX-S", VENDOR_IODATA, 0x0913,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "Kingston KNU101TX Ethernet", VENDOR_KINGSTON, 0x000a,
+		DEFAULT_GPIO_RESET)
+PEGASUS_DEV( "LANEED USB Ethernet LD-USB/TX", VENDOR_LANEED, 0x4002,
+		DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "LANEED USB Ethernet LD-USB/TX", VENDOR_LANEED, 0x400b,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "LANEED USB Ethernet LD-USB/T", VENDOR_LANEED, 0xabc1,
+		DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "LANEED USB Ethernet LD-USB/TX", VENDOR_LANEED, 0x200c,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "Linksys USB10TX", VENDOR_LINKSYS, 0x2202,
+		LINKSYS_GPIO_RESET )
+PEGASUS_DEV( "Linksys USB100TX", VENDOR_LINKSYS, 0x2203,
+		LINKSYS_GPIO_RESET )
+PEGASUS_DEV( "Linksys USB100TX", VENDOR_LINKSYS, 0x2204,
+		LINKSYS_GPIO_RESET | HAS_HOME_PNA )
+PEGASUS_DEV( "Linksys USB10T Ethernet Adapter", VENDOR_LINKSYS, 0x2206,
+		LINKSYS_GPIO_RESET | PEGASUS_II)
+PEGASUS_DEV( "Linksys USB USB100TX", VENDOR_LINKSYS, 0x400b,
+		LINKSYS_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "Linksys USB10TX", VENDOR_LINKSYS, 0x200c,
+		LINKSYS_GPIO_RESET | PEGASUS_II )	
+PEGASUS_DEV( "MELCO/BUFFALO LUA-TX", VENDOR_MELCO, 0x0001,
+		DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "MELCO/BUFFALO LUA-TX", VENDOR_MELCO, 0x0005,
+		DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "MELCO/BUFFALO LUA2-TX", VENDOR_MELCO, 0x0009,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "Microsoft MN-110", VENDOR_MICROSOFT, 0x007a,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "NETGEAR FA101", VENDOR_NETGEAR, 0x1020,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "OCT Inc.", VENDOR_OCT, 0x0109,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "OCT USB TO Ethernet", VENDOR_OCT, 0x0901,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "smartNIC 2 PnP Adapter", VENDOR_SMARTBRIDGES, 0x0003,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "SMC 202 USB Ethernet", VENDOR_SMC, 0x0200,
+		DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "SMC 2206 USB Ethernet", VENDOR_SMC, 0x0201,
+		DEFAULT_GPIO_RESET | PEGASUS_II)
+PEGASUS_DEV( "SOHOware NUB100 Ethernet", VENDOR_SOHOWARE, 0x9100,
+		DEFAULT_GPIO_RESET )
+PEGASUS_DEV( "SOHOware NUB110 Ethernet", VENDOR_SOHOWARE, 0x9110,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
+PEGASUS_DEV( "SpeedStream USB 10/100 Ethernet", VENDOR_SIEMENS, 0x1001,
+		DEFAULT_GPIO_RESET | PEGASUS_II )
+
+
+#endif	/* PEGASUS_DEV */
diff --git a/drivers/usb/net/rtl8150.c b/drivers/usb/net/rtl8150.c
new file mode 100644
index 0000000..8fb2233
--- /dev/null
+++ b/drivers/usb/net/rtl8150.c
@@ -0,0 +1,942 @@
+/*
+ *  Copyright (c) 2002 Petko Manolov (petkan@users.sourceforge.net)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/signal.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/usb.h>
+#include <asm/uaccess.h>
+
+/* Version Information */
+#define DRIVER_VERSION "v0.6.2 (2004/08/27)"
+#define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>"
+#define DRIVER_DESC "rtl8150 based usb-ethernet driver"
+
+#define	IDR			0x0120
+#define	MAR			0x0126
+#define	CR			0x012e
+#define	TCR			0x012f
+#define	RCR			0x0130
+#define	TSR			0x0132
+#define	RSR			0x0133
+#define	CON0			0x0135
+#define	CON1			0x0136
+#define	MSR			0x0137
+#define	PHYADD			0x0138
+#define	PHYDAT			0x0139
+#define	PHYCNT			0x013b
+#define	GPPC			0x013d
+#define	BMCR			0x0140
+#define	BMSR			0x0142
+#define	ANAR			0x0144
+#define	ANLP			0x0146
+#define	AER			0x0148
+#define CSCR			0x014C  /* This one has the link status */
+#define CSCR_LINK_STATUS	(1 << 3)
+
+#define	IDR_EEPROM		0x1202
+
+#define	PHY_READ		0
+#define	PHY_WRITE		0x20
+#define	PHY_GO			0x40
+
+#define	MII_TIMEOUT		10
+#define	INTBUFSIZE		8
+
+#define	RTL8150_REQT_READ	0xc0
+#define	RTL8150_REQT_WRITE	0x40
+#define	RTL8150_REQ_GET_REGS	0x05
+#define	RTL8150_REQ_SET_REGS	0x05
+
+
+/* Transmit status register errors */
+#define TSR_ECOL		(1<<5)
+#define TSR_LCOL		(1<<4)
+#define TSR_LOSS_CRS		(1<<3)
+#define TSR_JBR			(1<<2)
+#define TSR_ERRORS		(TSR_ECOL | TSR_LCOL | TSR_LOSS_CRS | TSR_JBR)
+/* Receive status register errors */
+#define RSR_CRC			(1<<2)
+#define RSR_FAE			(1<<1)
+#define RSR_ERRORS		(RSR_CRC | RSR_FAE)
+
+/* Media status register definitions */
+#define MSR_DUPLEX		(1<<4)
+#define MSR_SPEED		(1<<3)
+#define MSR_LINK		(1<<2)
+
+/* Interrupt pipe data */
+#define INT_TSR			0x00
+#define INT_RSR			0x01
+#define INT_MSR			0x02
+#define INT_WAKSR		0x03
+#define INT_TXOK_CNT		0x04
+#define INT_RXLOST_CNT		0x05
+#define INT_CRERR_CNT		0x06
+#define INT_COL_CNT		0x07
+
+/* Transmit status register errors */
+#define TSR_ECOL		(1<<5)
+#define TSR_LCOL		(1<<4)
+#define TSR_LOSS_CRS		(1<<3)
+#define TSR_JBR			(1<<2)
+#define TSR_ERRORS		(TSR_ECOL | TSR_LCOL | TSR_LOSS_CRS | TSR_JBR)
+/* Receive status register errors */
+#define RSR_CRC			(1<<2)
+#define RSR_FAE			(1<<1)
+#define RSR_ERRORS		(RSR_CRC | RSR_FAE)
+
+/* Media status register definitions */
+#define MSR_DUPLEX		(1<<4)
+#define MSR_SPEED		(1<<3)
+#define MSR_LINK		(1<<2)
+
+/* Interrupt pipe data */
+#define INT_TSR			0x00
+#define INT_RSR			0x01
+#define INT_MSR			0x02
+#define INT_WAKSR		0x03
+#define INT_TXOK_CNT		0x04
+#define INT_RXLOST_CNT		0x05
+#define INT_CRERR_CNT		0x06
+#define INT_COL_CNT		0x07
+
+
+#define	RTL8150_MTU		1540
+#define	RTL8150_TX_TIMEOUT	(HZ)
+#define	RX_SKB_POOL_SIZE	4
+
+/* rtl8150 flags */
+#define	RTL8150_HW_CRC		0
+#define	RX_REG_SET		1
+#define	RTL8150_UNPLUG		2
+#define	RX_URB_FAIL		3
+
+/* Define these values to match your device */
+#define VENDOR_ID_REALTEK		0x0bda
+#define	VENDOR_ID_MELCO			0x0411
+#define VENDOR_ID_MICRONET		0x3980
+#define	VENDOR_ID_LONGSHINE		0x07b8
+
+#define PRODUCT_ID_RTL8150		0x8150
+#define	PRODUCT_ID_LUAKTX		0x0012
+#define	PRODUCT_ID_LCS8138TX		0x401a
+#define PRODUCT_ID_SP128AR		0x0003
+
+#undef	EEPROM_WRITE
+
+/* table of devices that work with this driver */
+static struct usb_device_id rtl8150_table[] = {
+	{USB_DEVICE(VENDOR_ID_REALTEK, PRODUCT_ID_RTL8150)},
+	{USB_DEVICE(VENDOR_ID_MELCO, PRODUCT_ID_LUAKTX)},
+	{USB_DEVICE(VENDOR_ID_MICRONET, PRODUCT_ID_SP128AR)},
+	{USB_DEVICE(VENDOR_ID_LONGSHINE, PRODUCT_ID_LCS8138TX)},
+	{}
+};
+
+MODULE_DEVICE_TABLE(usb, rtl8150_table);
+
+struct rtl8150 {
+	unsigned long flags;
+	struct usb_device *udev;
+	struct tasklet_struct tl;
+	struct net_device_stats stats;
+	struct net_device *netdev;
+	struct urb *rx_urb, *tx_urb, *intr_urb, *ctrl_urb;
+	struct sk_buff *tx_skb, *rx_skb;
+	struct sk_buff *rx_skb_pool[RX_SKB_POOL_SIZE];
+	spinlock_t rx_pool_lock;
+	struct usb_ctrlrequest dr;
+	int intr_interval;
+	__le16 rx_creg;
+	u8 *intr_buff;
+	u8 phy;
+};
+
+typedef struct rtl8150 rtl8150_t;
+
+static unsigned long multicast_filter_limit = 32;
+
+static void fill_skb_pool(rtl8150_t *);
+static void free_skb_pool(rtl8150_t *);
+static inline struct sk_buff *pull_skb(rtl8150_t *);
+static void rtl8150_disconnect(struct usb_interface *intf);
+static int rtl8150_probe(struct usb_interface *intf,
+			   const struct usb_device_id *id);
+
+static const char driver_name [] = "rtl8150";
+
+static struct usb_driver rtl8150_driver = {
+	.owner =	THIS_MODULE,
+	.name =		driver_name,
+	.probe =	rtl8150_probe,
+	.disconnect =	rtl8150_disconnect,
+	.id_table =	rtl8150_table,
+};
+
+/*
+**
+**	device related part of the code
+**
+*/
+static int get_registers(rtl8150_t * dev, u16 indx, u16 size, void *data)
+{
+	return usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
+			       RTL8150_REQ_GET_REGS, RTL8150_REQT_READ,
+			       indx, 0, data, size, 500);
+}
+
+static int set_registers(rtl8150_t * dev, u16 indx, u16 size, void *data)
+{
+	return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
+			       RTL8150_REQ_SET_REGS, RTL8150_REQT_WRITE,
+			       indx, 0, data, size, 500);
+}
+
+static void ctrl_callback(struct urb *urb, struct pt_regs *regs)
+{
+	rtl8150_t *dev;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -EINPROGRESS:
+		break;
+	case -ENOENT:
+		break;
+	default:
+		warn("ctrl urb status %d", urb->status);
+	}
+	dev = urb->context;
+	clear_bit(RX_REG_SET, &dev->flags);
+}
+
+static int async_set_registers(rtl8150_t * dev, u16 indx, u16 size)
+{
+	int ret;
+
+	if (test_bit(RX_REG_SET, &dev->flags))
+		return -EAGAIN;
+
+	dev->dr.bRequestType = RTL8150_REQT_WRITE;
+	dev->dr.bRequest = RTL8150_REQ_SET_REGS;
+	dev->dr.wValue = cpu_to_le16(indx);
+	dev->dr.wIndex = 0;
+	dev->dr.wLength = cpu_to_le16(size);
+	dev->ctrl_urb->transfer_buffer_length = size;
+	usb_fill_control_urb(dev->ctrl_urb, dev->udev,
+			 usb_sndctrlpipe(dev->udev, 0), (char *) &dev->dr,
+			 &dev->rx_creg, size, ctrl_callback, dev);
+	if ((ret = usb_submit_urb(dev->ctrl_urb, GFP_ATOMIC)))
+		err("control request submission failed: %d", ret);
+	else
+		set_bit(RX_REG_SET, &dev->flags);
+
+	return ret;
+}
+
+static int read_mii_word(rtl8150_t * dev, u8 phy, __u8 indx, u16 * reg)
+{
+	int i;
+	u8 data[3], tmp;
+
+	data[0] = phy;
+	data[1] = data[2] = 0;
+	tmp = indx | PHY_READ | PHY_GO;
+	i = 0;
+
+	set_registers(dev, PHYADD, sizeof(data), data);
+	set_registers(dev, PHYCNT, 1, &tmp);
+	do {
+		get_registers(dev, PHYCNT, 1, data);
+	} while ((data[0] & PHY_GO) && (i++ < MII_TIMEOUT));
+
+	if (i < MII_TIMEOUT) {
+		get_registers(dev, PHYDAT, 2, data);
+		*reg = data[0] | (data[1] << 8);
+		return 0;
+	} else
+		return 1;
+}
+
+static int write_mii_word(rtl8150_t * dev, u8 phy, __u8 indx, u16 reg)
+{
+	int i;
+	u8 data[3], tmp;
+
+	data[0] = phy;
+	*(data + 1) = cpu_to_le16p(&reg);
+	tmp = indx | PHY_WRITE | PHY_GO;
+	i = 0;
+
+	set_registers(dev, PHYADD, sizeof(data), data);
+	set_registers(dev, PHYCNT, 1, &tmp);
+	do {
+		get_registers(dev, PHYCNT, 1, data);
+	} while ((data[0] & PHY_GO) && (i++ < MII_TIMEOUT));
+
+	if (i < MII_TIMEOUT)
+		return 0;
+	else
+		return 1;
+}
+
+static inline void set_ethernet_addr(rtl8150_t * dev)
+{
+	u8 node_id[6];
+
+	get_registers(dev, IDR, sizeof(node_id), node_id);
+	memcpy(dev->netdev->dev_addr, node_id, sizeof(node_id));
+}
+
+static int rtl8150_set_mac_address(struct net_device *netdev, void *p)
+{
+	struct sockaddr *addr = p;
+	rtl8150_t *dev = netdev_priv(netdev);
+	int i;
+
+	if (netif_running(netdev))
+		return -EBUSY;
+
+	memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
+	dbg("%s: Setting MAC address to ", netdev->name);
+	for (i = 0; i < 5; i++)
+		dbg("%02X:", netdev->dev_addr[i]);
+	dbg("%02X\n", netdev->dev_addr[i]);
+	/* Set the IDR registers. */
+	set_registers(dev, IDR, sizeof(netdev->dev_addr), netdev->dev_addr);
+#ifdef EEPROM_WRITE
+	{
+	u8 cr;
+	/* Get the CR contents. */
+	get_registers(dev, CR, 1, &cr);
+	/* Set the WEPROM bit (eeprom write enable). */
+	cr |= 0x20;
+	set_registers(dev, CR, 1, &cr);
+	/* Write the MAC address into eeprom. Eeprom writes must be word-sized,
+	   so we need to split them up. */
+	for (i = 0; i * 2 < netdev->addr_len; i++) {
+		set_registers(dev, IDR_EEPROM + (i * 2), 2, 
+		netdev->dev_addr + (i * 2));
+	}
+	/* Clear the WEPROM bit (preventing accidental eeprom writes). */
+	cr &= 0xdf;
+	set_registers(dev, CR, 1, &cr);
+	}
+#endif
+	return 0;
+}
+
+static int rtl8150_reset(rtl8150_t * dev)
+{
+	u8 data = 0x10;
+	int i = HZ;
+
+	set_registers(dev, CR, 1, &data);
+	do {
+		get_registers(dev, CR, 1, &data);
+	} while ((data & 0x10) && --i);
+
+	return (i > 0) ? 1 : 0;
+}
+
+static int alloc_all_urbs(rtl8150_t * dev)
+{
+	dev->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->rx_urb)
+		return 0;
+	dev->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->tx_urb) {
+		usb_free_urb(dev->rx_urb);
+		return 0;
+	}
+	dev->intr_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->intr_urb) {
+		usb_free_urb(dev->rx_urb);
+		usb_free_urb(dev->tx_urb);
+		return 0;
+	}
+	dev->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!dev->intr_urb) {
+		usb_free_urb(dev->rx_urb);
+		usb_free_urb(dev->tx_urb);
+		usb_free_urb(dev->intr_urb);
+		return 0;
+	}
+
+	return 1;
+}
+
+static void free_all_urbs(rtl8150_t * dev)
+{
+	usb_free_urb(dev->rx_urb);
+	usb_free_urb(dev->tx_urb);
+	usb_free_urb(dev->intr_urb);
+	usb_free_urb(dev->ctrl_urb);
+}
+
+static void unlink_all_urbs(rtl8150_t * dev)
+{
+	usb_kill_urb(dev->rx_urb);
+	usb_kill_urb(dev->tx_urb);
+	usb_kill_urb(dev->intr_urb);
+	usb_kill_urb(dev->ctrl_urb);
+}
+
+static inline struct sk_buff *pull_skb(rtl8150_t *dev)
+{
+	struct sk_buff *skb;
+	int i;
+
+	for (i = 0; i < RX_SKB_POOL_SIZE; i++) {
+		if (dev->rx_skb_pool[i]) {
+			skb = dev->rx_skb_pool[i];
+			dev->rx_skb_pool[i] = NULL;
+			return skb;
+		}
+	}
+	return NULL;
+}
+
+static void read_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+	rtl8150_t *dev;
+	unsigned pkt_len, res;
+	struct sk_buff *skb;
+	struct net_device *netdev;
+	u16 rx_stat;
+
+	dev = urb->context;
+	if (!dev)
+		return;
+	if (test_bit(RTL8150_UNPLUG, &dev->flags))
+		return;
+	netdev = dev->netdev;
+	if (!netif_device_present(netdev))
+		return;
+
+	switch (urb->status) {
+	case 0:
+		break;
+	case -ENOENT:
+		return;	/* the urb is in unlink state */
+	case -ETIMEDOUT:
+		warn("may be reset is needed?..");
+		goto goon;
+	default:
+		warn("Rx status %d", urb->status);
+		goto goon;
+	}
+
+	if (!dev->rx_skb)
+		goto resched;
+	/* protect against short packets (tell me why we got some?!?) */
+	if (urb->actual_length < 4)
+		goto goon;
+
+	res = urb->actual_length;
+	rx_stat = le16_to_cpu(*(__le16 *)(urb->transfer_buffer + res - 4));
+	pkt_len = res - 4;
+
+	skb_put(dev->rx_skb, pkt_len);
+	dev->rx_skb->protocol = eth_type_trans(dev->rx_skb, netdev);
+	netif_rx(dev->rx_skb);
+	dev->stats.rx_packets++;
+	dev->stats.rx_bytes += pkt_len;
+
+	spin_lock(&dev->rx_pool_lock);
+	skb = pull_skb(dev);
+	spin_unlock(&dev->rx_pool_lock);
+	if (!skb)
+		goto resched;
+
+	dev->rx_skb = skb;
+goon:
+	usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1),
+		      dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev);
+	if (usb_submit_urb(dev->rx_urb, GFP_ATOMIC)) {
+		set_bit(RX_URB_FAIL, &dev->flags);
+		goto resched;
+	} else {
+		clear_bit(RX_URB_FAIL, &dev->flags);
+	}
+
+	return;
+resched:
+	tasklet_schedule(&dev->tl);
+}
+
+static void rx_fixup(unsigned long data)
+{
+	rtl8150_t *dev;
+	struct sk_buff *skb;
+
+	dev = (rtl8150_t *)data;
+
+	spin_lock_irq(&dev->rx_pool_lock);
+	fill_skb_pool(dev);
+	spin_unlock_irq(&dev->rx_pool_lock);
+	if (test_bit(RX_URB_FAIL, &dev->flags))
+		if (dev->rx_skb)
+			goto try_again;
+	spin_lock_irq(&dev->rx_pool_lock);
+	skb = pull_skb(dev);
+	spin_unlock_irq(&dev->rx_pool_lock);
+	if (skb == NULL)
+		goto tlsched;
+	dev->rx_skb = skb;
+	usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1),
+		      dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev);
+try_again:
+	if (usb_submit_urb(dev->rx_urb, GFP_ATOMIC)) {
+		set_bit(RX_URB_FAIL, &dev->flags);
+		goto tlsched;
+	 } else {
+		clear_bit(RX_URB_FAIL, &dev->flags);
+	}
+
+	return;
+tlsched:
+	tasklet_schedule(&dev->tl);
+}
+
+static void write_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+	rtl8150_t *dev;
+
+	dev = urb->context;
+	if (!dev)
+		return;
+	dev_kfree_skb_irq(dev->tx_skb);
+	if (!netif_device_present(dev->netdev))
+		return;
+	if (urb->status)
+		info("%s: Tx status %d", dev->netdev->name, urb->status);
+	dev->netdev->trans_start = jiffies;
+	netif_wake_queue(dev->netdev);
+}
+
+static void intr_callback(struct urb *urb, struct pt_regs *regs)
+{
+	rtl8150_t *dev;
+	__u8 *d;
+	int status;
+
+	dev = urb->context;
+	if (!dev)
+		return;
+	switch (urb->status) {
+	case 0:			/* success */
+		break;
+	case -ECONNRESET:	/* unlink */
+	case -ENOENT:
+	case -ESHUTDOWN:
+		return;
+	/* -EPIPE:  should clear the halt */
+	default:
+		info("%s: intr status %d", dev->netdev->name, urb->status);
+		goto resubmit;
+	}
+
+	d = urb->transfer_buffer;
+	if (d[0] & TSR_ERRORS) {
+		dev->stats.tx_errors++;
+		if (d[INT_TSR] & (TSR_ECOL | TSR_JBR))
+			dev->stats.tx_aborted_errors++;
+		if (d[INT_TSR] & TSR_LCOL)
+			dev->stats.tx_window_errors++;
+		if (d[INT_TSR] & TSR_LOSS_CRS)
+			dev->stats.tx_carrier_errors++;
+	}
+	/* Report link status changes to the network stack */
+	if ((d[INT_MSR] & MSR_LINK) == 0) {
+		if (netif_carrier_ok(dev->netdev)) {
+			netif_carrier_off(dev->netdev);
+			dbg("%s: LINK LOST\n", __func__);
+		}
+	} else {
+		if (!netif_carrier_ok(dev->netdev)) {
+			netif_carrier_on(dev->netdev);
+			dbg("%s: LINK CAME BACK\n", __func__);
+		}
+	}
+
+resubmit:
+	status = usb_submit_urb (urb, SLAB_ATOMIC);
+	if (status)
+		err ("can't resubmit intr, %s-%s/input0, status %d",
+				dev->udev->bus->bus_name,
+				dev->udev->devpath, status);
+}
+
+
+/*
+**
+**	network related part of the code
+**
+*/
+
+static void fill_skb_pool(rtl8150_t *dev)
+{
+	struct sk_buff *skb;
+	int i;
+
+	for (i = 0; i < RX_SKB_POOL_SIZE; i++) {
+		if (dev->rx_skb_pool[i])
+			continue;
+		skb = dev_alloc_skb(RTL8150_MTU + 2);
+		if (!skb) {
+			return;
+		}
+		skb->dev = dev->netdev;
+		skb_reserve(skb, 2);
+		dev->rx_skb_pool[i] = skb;
+	}
+}
+
+static void free_skb_pool(rtl8150_t *dev)
+{
+	int i;
+
+	for (i = 0; i < RX_SKB_POOL_SIZE; i++)
+		if (dev->rx_skb_pool[i])
+			dev_kfree_skb(dev->rx_skb_pool[i]);
+}
+
+static int enable_net_traffic(rtl8150_t * dev)
+{
+	u8 cr, tcr, rcr, msr;
+
+	if (!rtl8150_reset(dev)) {
+		warn("%s - device reset failed", __FUNCTION__);
+	}
+	/* RCR bit7=1 attach Rx info at the end;  =0 HW CRC (which is broken) */
+	rcr = 0x9e;
+	dev->rx_creg = cpu_to_le16(rcr);
+	tcr = 0xd8;
+	cr = 0x0c;
+	if (!(rcr & 0x80))
+		set_bit(RTL8150_HW_CRC, &dev->flags);
+	set_registers(dev, RCR, 1, &rcr);
+	set_registers(dev, TCR, 1, &tcr);
+	set_registers(dev, CR, 1, &cr);
+	get_registers(dev, MSR, 1, &msr);
+
+	return 0;
+}
+
+static void disable_net_traffic(rtl8150_t * dev)
+{
+	u8 cr;
+
+	get_registers(dev, CR, 1, &cr);
+	cr &= 0xf3;
+	set_registers(dev, CR, 1, &cr);
+}
+
+static struct net_device_stats *rtl8150_netdev_stats(struct net_device *dev)
+{
+	return &((rtl8150_t *)netdev_priv(dev))->stats;
+}
+
+static void rtl8150_tx_timeout(struct net_device *netdev)
+{
+	rtl8150_t *dev = netdev_priv(netdev);
+	warn("%s: Tx timeout.", netdev->name);
+	dev->tx_urb->transfer_flags |= URB_ASYNC_UNLINK;
+	usb_unlink_urb(dev->tx_urb);
+	dev->stats.tx_errors++;
+}
+
+static void rtl8150_set_multicast(struct net_device *netdev)
+{
+	rtl8150_t *dev = netdev_priv(netdev);
+	netif_stop_queue(netdev);
+	if (netdev->flags & IFF_PROMISC) {
+		dev->rx_creg |= cpu_to_le16(0x0001);
+		info("%s: promiscuous mode", netdev->name);
+	} else if ((netdev->mc_count > multicast_filter_limit) ||
+		   (netdev->flags & IFF_ALLMULTI)) {
+		dev->rx_creg &= cpu_to_le16(0xfffe);
+		dev->rx_creg |= cpu_to_le16(0x0002);
+		info("%s: allmulti set", netdev->name);
+	} else {
+		/* ~RX_MULTICAST, ~RX_PROMISCUOUS */
+		dev->rx_creg &= cpu_to_le16(0x00fc);
+	}
+	async_set_registers(dev, RCR, 2);
+	netif_wake_queue(netdev);
+}
+
+static int rtl8150_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+	rtl8150_t *dev = netdev_priv(netdev);
+	int count, res;
+
+	netif_stop_queue(netdev);
+	count = (skb->len < 60) ? 60 : skb->len;
+	count = (count & 0x3f) ? count : count + 1;
+	dev->tx_skb = skb;
+	usb_fill_bulk_urb(dev->tx_urb, dev->udev, usb_sndbulkpipe(dev->udev, 2),
+		      skb->data, count, write_bulk_callback, dev);
+	if ((res = usb_submit_urb(dev->tx_urb, GFP_ATOMIC))) {
+		warn("failed tx_urb %d\n", res);
+		dev->stats.tx_errors++;
+		netif_start_queue(netdev);
+	} else {
+		dev->stats.tx_packets++;
+		dev->stats.tx_bytes += skb->len;
+		netdev->trans_start = jiffies;
+	}
+
+	return 0;
+}
+
+
+static void set_carrier(struct net_device *netdev)
+{
+	rtl8150_t *dev = netdev_priv(netdev);
+	short tmp;
+
+	get_registers(dev, CSCR, 2, &tmp);
+	if (tmp & CSCR_LINK_STATUS)
+		netif_carrier_on(netdev);
+	else
+		netif_carrier_off(netdev);
+}
+
+static int rtl8150_open(struct net_device *netdev)
+{
+	rtl8150_t *dev = netdev_priv(netdev);
+	int res;
+
+	if (dev->rx_skb == NULL)
+		dev->rx_skb = pull_skb(dev);
+	if (!dev->rx_skb)
+		return -ENOMEM;
+
+	set_registers(dev, IDR, 6, netdev->dev_addr);
+	
+	usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1),
+		      dev->rx_skb->data, RTL8150_MTU, read_bulk_callback, dev);
+	if ((res = usb_submit_urb(dev->rx_urb, GFP_KERNEL)))
+		warn("%s: rx_urb submit failed: %d", __FUNCTION__, res);
+	usb_fill_int_urb(dev->intr_urb, dev->udev, usb_rcvintpipe(dev->udev, 3),
+		     dev->intr_buff, INTBUFSIZE, intr_callback,
+		     dev, dev->intr_interval);
+	if ((res = usb_submit_urb(dev->intr_urb, GFP_KERNEL)))
+		warn("%s: intr_urb submit failed: %d", __FUNCTION__, res);
+	netif_start_queue(netdev);
+	enable_net_traffic(dev);
+	set_carrier(netdev);
+
+	return res;
+}
+
+static int rtl8150_close(struct net_device *netdev)
+{
+	rtl8150_t *dev = netdev_priv(netdev);
+	int res = 0;
+
+	netif_stop_queue(netdev);
+	if (!test_bit(RTL8150_UNPLUG, &dev->flags))
+		disable_net_traffic(dev);
+	unlink_all_urbs(dev);
+
+	return res;
+}
+
+static void rtl8150_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info)
+{
+	rtl8150_t *dev = netdev_priv(netdev);
+
+	strncpy(info->driver, driver_name, ETHTOOL_BUSINFO_LEN);
+	strncpy(info->version, DRIVER_VERSION, ETHTOOL_BUSINFO_LEN);
+	usb_make_path(dev->udev, info->bus_info, sizeof info->bus_info);
+}
+
+static int rtl8150_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
+{
+	rtl8150_t *dev = netdev_priv(netdev);
+	short lpa, bmcr;
+
+	ecmd->supported = (SUPPORTED_10baseT_Half |
+			  SUPPORTED_10baseT_Full |
+			  SUPPORTED_100baseT_Half |
+			  SUPPORTED_100baseT_Full |
+			  SUPPORTED_Autoneg |
+			  SUPPORTED_TP | SUPPORTED_MII);
+	ecmd->port = PORT_TP;
+	ecmd->transceiver = XCVR_INTERNAL;
+	ecmd->phy_address = dev->phy;
+	get_registers(dev, BMCR, 2, &bmcr);
+	get_registers(dev, ANLP, 2, &lpa);
+	if (bmcr & BMCR_ANENABLE) {
+		ecmd->autoneg = AUTONEG_ENABLE;
+		ecmd->speed = (lpa & (LPA_100HALF | LPA_100FULL)) ?
+			     SPEED_100 : SPEED_10;
+		if (ecmd->speed == SPEED_100)
+			ecmd->duplex = (lpa & LPA_100FULL) ?
+			    DUPLEX_FULL : DUPLEX_HALF;
+		else
+			ecmd->duplex = (lpa & LPA_10FULL) ?
+			    DUPLEX_FULL : DUPLEX_HALF;
+	} else {
+		ecmd->autoneg = AUTONEG_DISABLE;
+		ecmd->speed = (bmcr & BMCR_SPEED100) ?
+		    SPEED_100 : SPEED_10;
+		ecmd->duplex = (bmcr & BMCR_FULLDPLX) ?
+		    DUPLEX_FULL : DUPLEX_HALF;
+	}
+	return 0;
+}
+
+static struct ethtool_ops ops = {
+	.get_drvinfo = rtl8150_get_drvinfo,
+	.get_settings = rtl8150_get_settings,
+	.get_link = ethtool_op_get_link
+};
+
+static int rtl8150_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
+{
+	rtl8150_t *dev = netdev_priv(netdev);
+	u16 *data = (u16 *) & rq->ifr_ifru;
+	int res = 0;
+
+	switch (cmd) {
+	case SIOCDEVPRIVATE:
+		data[0] = dev->phy;
+	case SIOCDEVPRIVATE + 1:
+		read_mii_word(dev, dev->phy, (data[1] & 0x1f), &data[3]);
+		break;
+	case SIOCDEVPRIVATE + 2:
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+		write_mii_word(dev, dev->phy, (data[1] & 0x1f), data[2]);
+		break;
+	default:
+		res = -EOPNOTSUPP;
+	}
+
+	return res;
+}
+
+static int rtl8150_probe(struct usb_interface *intf,
+			 const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	rtl8150_t *dev;
+	struct net_device *netdev;
+
+	netdev = alloc_etherdev(sizeof(rtl8150_t));
+	if (!netdev) {
+		err("Out of memory");
+		return -ENOMEM;
+	}
+
+	dev = netdev_priv(netdev);
+	memset(dev, 0, sizeof(rtl8150_t));
+
+	dev->intr_buff = kmalloc(INTBUFSIZE, GFP_KERNEL);
+	if (!dev->intr_buff) {
+		free_netdev(netdev);
+		return -ENOMEM;
+	}
+
+	tasklet_init(&dev->tl, rx_fixup, (unsigned long)dev);
+	spin_lock_init(&dev->rx_pool_lock);
+	
+	dev->udev = udev;
+	dev->netdev = netdev;
+	SET_MODULE_OWNER(netdev);
+	netdev->open = rtl8150_open;
+	netdev->stop = rtl8150_close;
+	netdev->do_ioctl = rtl8150_ioctl;
+	netdev->watchdog_timeo = RTL8150_TX_TIMEOUT;
+	netdev->tx_timeout = rtl8150_tx_timeout;
+	netdev->hard_start_xmit = rtl8150_start_xmit;
+	netdev->set_multicast_list = rtl8150_set_multicast;
+	netdev->set_mac_address = rtl8150_set_mac_address;
+	netdev->get_stats = rtl8150_netdev_stats;
+	netdev->mtu = RTL8150_MTU;
+	SET_ETHTOOL_OPS(netdev, &ops);
+	dev->intr_interval = 100;	/* 100ms */
+
+	if (!alloc_all_urbs(dev)) {
+		err("out of memory");
+		goto out;
+	}
+	if (!rtl8150_reset(dev)) {
+		err("couldn't reset the device");
+		goto out1;
+	}
+	fill_skb_pool(dev);
+	set_ethernet_addr(dev);
+	info("%s: rtl8150 is detected", netdev->name);
+	
+	usb_set_intfdata(intf, dev);
+	SET_NETDEV_DEV(netdev, &intf->dev);
+	if (register_netdev(netdev) != 0) {
+		err("couldn't register the device");
+		goto out2;
+	}
+	return 0;
+
+out2:
+	usb_set_intfdata(intf, NULL);
+	free_skb_pool(dev);
+out1:
+	free_all_urbs(dev);
+out:
+	kfree(dev->intr_buff);
+	free_netdev(netdev);
+	return -EIO;
+}
+
+static void rtl8150_disconnect(struct usb_interface *intf)
+{
+	rtl8150_t *dev = usb_get_intfdata(intf);
+
+	usb_set_intfdata(intf, NULL);
+	if (dev) {
+		set_bit(RTL8150_UNPLUG, &dev->flags);
+		unregister_netdev(dev->netdev);
+		unlink_all_urbs(dev);
+		free_all_urbs(dev);
+		free_skb_pool(dev);
+		if (dev->rx_skb)
+			dev_kfree_skb(dev->rx_skb);
+		kfree(dev->intr_buff);
+		free_netdev(dev->netdev);
+	}
+}
+
+static int __init usb_rtl8150_init(void)
+{
+	info(DRIVER_DESC " " DRIVER_VERSION);
+	return usb_register(&rtl8150_driver);
+}
+
+static void __exit usb_rtl8150_exit(void)
+{
+	usb_deregister(&rtl8150_driver);
+}
+
+module_init(usb_rtl8150_init);
+module_exit(usb_rtl8150_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c
new file mode 100644
index 0000000..dd8b445
--- /dev/null
+++ b/drivers/usb/net/usbnet.c
@@ -0,0 +1,4106 @@
+/*
+ * USB Networking Links
+ * Copyright (C) 2000-2003 by David Brownell <dbrownell@users.sourceforge.net>
+ * Copyright (C) 2002 Pavel Machek <pavel@ucw.cz>
+ * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
+ * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net>
+ * Copyright (c) 2002-2003 TiVo Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*
+ * This is a generic "USB networking" framework that works with several
+ * kinds of full and high speed networking devices:
+ *
+ *   + USB host-to-host "network cables", used for IP-over-USB links.
+ *     These are often used for Laplink style connectivity products.
+ *	- AnchorChip 2720
+ *	- Belkin, eTEK (interops with Win32 drivers)
+ *	- GeneSys GL620USB-A
+ *	- NetChip 1080 (interoperates with NetChip Win32 drivers)
+ *	- Prolific PL-2301/2302 (replaces "plusb" driver)
+ *	- KC Technology KC2190
+ *
+ *   + Smart USB devices can support such links directly, using Internet
+ *     standard protocols instead of proprietary host-to-device links.
+ *	- Linux PDAs like iPaq, Yopy, and Zaurus
+ *	- The BLOB boot loader (for diskless booting)
+ *	- Linux "gadgets", perhaps using PXA-2xx or Net2280 controllers
+ *	- Devices using EPSON's sample USB firmware
+ *	- CDC-Ethernet class devices, such as many cable modems
+ *
+ *   + Adapters to networks such as Ethernet.
+ *	- AX8817X based USB 2.0 products
+ *
+ * Links to these devices can be bridged using Linux Ethernet bridging.
+ * With minor exceptions, these all use similar USB framing for network
+ * traffic, but need different protocols for control traffic.
+ *
+ * USB devices can implement their side of this protocol at the cost
+ * of two bulk endpoints; it's not restricted to "cable" applications.
+ * See the SA1110, Zaurus, or EPSON device/client support in this driver;
+ * slave/target drivers such as "usb-eth" (on most SA-1100 PDAs) or
+ * "g_ether" (in the Linux "gadget" framework) implement that behavior
+ * within devices.
+ *
+ *
+ * CHANGELOG:
+ *
+ * 13-sep-2000	experimental, new
+ * 10-oct-2000	usb_device_id table created. 
+ * 28-oct-2000	misc fixes; mostly, discard more TTL-mangled rx packets.
+ * 01-nov-2000	usb_device_id table and probing api update by
+ *		Adam J. Richter <adam@yggdrasil.com>.
+ * 18-dec-2000	(db) tx watchdog, "net1080" renaming to "usbnet", device_info
+ *		and prolific support, isolate net1080-specific bits, cleanup.
+ *		fix unlink_urbs oops in D3 PM resume code path.
+ *
+ * 02-feb-2001	(db) fix tx skb sharing, packet length, match_flags, ...
+ * 08-feb-2001	stubbed in "linuxdev", maybe the SA-1100 folk can use it;
+ *		AnchorChips 2720 support (from spec) for testing;
+ *		fix bit-ordering problem with ethernet multicast addr
+ * 19-feb-2001  Support for clearing halt conditions. SA1100 UDC support
+ *		updates. Oleg Drokin (green@iXcelerator.com)
+ * 25-mar-2001	More SA-1100 updates, including workaround for ip problem
+ *		expecting cleared skb->cb and framing change to match latest
+ *		handhelds.org version (Oleg).  Enable device IDs from the
+ *		Win32 Belkin driver; other cleanups (db).
+ * 16-jul-2001	Bugfixes for uhci oops-on-unplug, Belkin support, various
+ *		cleanups for problems not yet seen in the field. (db)
+ * 17-oct-2001	Handle "Advance USBNET" product, like Belkin/eTEK devices,
+ *		from Ioannis Mavroukakis <i.mavroukakis@btinternet.com>;
+ *		rx unlinks somehow weren't async; minor cleanup.
+ * 03-nov-2001	Merged GeneSys driver; original code from Jiun-Jie Huang
+ *		<huangjj@genesyslogic.com.tw>, updated by Stanislav Brabec
+ *		<utx@penguin.cz>.  Made framing options (NetChip/GeneSys)
+ *		tie mostly to (sub)driver info.  Workaround some PL-2302
+ *		chips that seem to reject SET_INTERFACE requests.
+ *
+ * 06-apr-2002	Added ethtool support, based on a patch from Brad Hards.
+ *		Level of diagnostics is more configurable; they use device
+ *		location (usb_device->devpath) instead of address (2.5).
+ *		For tx_fixup, memflags can't be NOIO.
+ * 07-may-2002	Generalize/cleanup keventd support, handling rx stalls (mostly
+ *		for USB 2.0 TTs) and memory shortages (potential) too. (db)
+ *		Use "locally assigned" IEEE802 address space. (Brad Hards)
+ * 18-oct-2002	Support for Zaurus (Pavel Machek), related cleanup (db).
+ * 14-dec-2002	Remove Zaurus-private crc32 code (Pavel); 2.5 oops fix,
+ * 		cleanups and stubbed PXA-250 support (db), fix for framing
+ * 		issues on Z, net1080, and gl620a (Toby Milne)
+ *
+ * 31-mar-2003	Use endpoint descriptors:  high speed support, simpler sa1100
+ * 		vs pxa25x, and CDC Ethernet.  Throttle down log floods on
+ * 		disconnect; other cleanups. (db)  Flush net1080 fifos
+ * 		after several sequential framing errors. (Johannes Erdfelt)
+ * 22-aug-2003	AX8817X support (Dave Hollis).
+ * 14-jun-2004  Trivial patch for AX8817X based Buffalo LUA-U2-KTX in Japan
+ *		(Neil Bortnak)
+ * 03-nov-2004	Trivial patch for KC2190 (KC-190) chip. (Jonathan McDowell)
+ *
+ * 01-feb-2005	AX88772 support (Phil Chang & Dave Hollis)
+ *-------------------------------------------------------------------------*/
+
+// #define	DEBUG			// error path messages, extra info
+// #define	VERBOSE			// more; success messages
+
+#include <linux/config.h>
+#ifdef	CONFIG_USB_DEBUG
+#   define DEBUG
+#endif
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/random.h>
+#include <linux/ethtool.h>
+#include <linux/workqueue.h>
+#include <linux/mii.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+#include <linux/usb.h>
+#include <asm/io.h>
+#include <asm/scatterlist.h>
+#include <linux/mm.h>
+#include <linux/dma-mapping.h>
+
+#define DRIVER_VERSION		"03-Nov-2004"
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Nineteen USB 1.1 max size bulk transactions per frame (ms), max.
+ * Several dozen bytes of IPv4 data can fit in two such transactions.
+ * One maximum size Ethernet packet takes twenty four of them.
+ * For high speed, each frame comfortably fits almost 36 max size
+ * Ethernet packets (so queues should be bigger).
+ */
+#define	RX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? 60 : 4)
+#define	TX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? 60 : 4)
+
+// packets are always ethernet inside
+// ... except they can be bigger (limit of 64K with NetChip framing)
+#define MIN_PACKET	sizeof(struct ethhdr)
+#define MAX_PACKET	32768
+
+// reawaken network queue this soon after stopping; else watchdog barks
+#define TX_TIMEOUT_JIFFIES	(5*HZ)
+
+// throttle rx/tx briefly after some faults, so khubd might disconnect()
+// us (it polls at HZ/4 usually) before we report too many false errors.
+#define THROTTLE_JIFFIES	(HZ/8)
+
+// for vendor-specific control operations
+#define	CONTROL_TIMEOUT_MS	500
+
+// between wakeups
+#define UNLINK_TIMEOUT_MS	3
+
+/*-------------------------------------------------------------------------*/
+
+// randomly generated ethernet address
+static u8	node_id [ETH_ALEN];
+
+// state we keep for each device we handle
+struct usbnet {
+	// housekeeping
+	struct usb_device	*udev;
+	struct driver_info	*driver_info;
+	wait_queue_head_t	*wait;
+
+	// i/o info: pipes etc
+	unsigned		in, out;
+	struct usb_host_endpoint *status;
+	unsigned		maxpacket;
+	struct timer_list	delay;
+
+	// protocol/interface state
+	struct net_device	*net;
+	struct net_device_stats	stats;
+	int			msg_enable;
+	unsigned long		data [5];
+
+	struct mii_if_info	mii;
+
+	// various kinds of pending driver work
+	struct sk_buff_head	rxq;
+	struct sk_buff_head	txq;
+	struct sk_buff_head	done;
+	struct urb		*interrupt;
+	struct tasklet_struct	bh;
+
+	struct work_struct	kevent;
+	unsigned long		flags;
+#		define EVENT_TX_HALT	0
+#		define EVENT_RX_HALT	1
+#		define EVENT_RX_MEMORY	2
+#		define EVENT_STS_SPLIT	3
+};
+
+// device-specific info used by the driver
+struct driver_info {
+	char		*description;
+
+	int		flags;
+/* framing is CDC Ethernet, not writing ZLPs (hw issues), or optionally: */
+#define FLAG_FRAMING_NC	0x0001		/* guard against device dropouts */ 
+#define FLAG_FRAMING_GL	0x0002		/* genelink batches packets */
+#define FLAG_FRAMING_Z	0x0004		/* zaurus adds a trailer */
+#define FLAG_FRAMING_RN	0x0008		/* RNDIS batches, plus huge header */
+
+#define FLAG_NO_SETINT	0x0010		/* device can't set_interface() */
+#define FLAG_ETHER	0x0020		/* maybe use "eth%d" names */
+
+#define FLAG_FRAMING_AX 0x0040          /* AX88772/178 packets */
+
+	/* init device ... can sleep, or cause probe() failure */
+	int	(*bind)(struct usbnet *, struct usb_interface *);
+
+	/* cleanup device ... can sleep, but can't fail */
+	void	(*unbind)(struct usbnet *, struct usb_interface *);
+
+	/* reset device ... can sleep */
+	int	(*reset)(struct usbnet *);
+
+	/* see if peer is connected ... can sleep */
+	int	(*check_connect)(struct usbnet *);
+
+	/* for status polling */
+	void	(*status)(struct usbnet *, struct urb *);
+
+	/* fixup rx packet (strip framing) */
+	int	(*rx_fixup)(struct usbnet *dev, struct sk_buff *skb);
+
+	/* fixup tx packet (add framing) */
+	struct sk_buff	*(*tx_fixup)(struct usbnet *dev,
+				struct sk_buff *skb, int flags);
+
+	// FIXME -- also an interrupt mechanism
+	// useful for at least PL2301/2302 and GL620USB-A
+	// and CDC use them to report 'is it connected' changes
+
+	/* for new devices, use the descriptor-reading code instead */
+	int		in;		/* rx endpoint */
+	int		out;		/* tx endpoint */
+
+	unsigned long	data;		/* Misc driver specific data */
+};
+
+// we record the state for each of our queued skbs
+enum skb_state {
+	illegal = 0,
+	tx_start, tx_done,
+	rx_start, rx_done, rx_cleanup
+};
+
+struct skb_data {	// skb->cb is one of these
+	struct urb		*urb;
+	struct usbnet		*dev;
+	enum skb_state		state;
+	size_t			length;
+};
+
+static const char driver_name [] = "usbnet";
+
+/* use ethtool to change the level for any given device */
+static int msg_level = -1;
+module_param (msg_level, int, 0);
+MODULE_PARM_DESC (msg_level, "Override default message level");
+
+
+#ifdef DEBUG
+#define devdbg(usbnet, fmt, arg...) \
+	printk(KERN_DEBUG "%s: " fmt "\n" , (usbnet)->net->name , ## arg)
+#else
+#define devdbg(usbnet, fmt, arg...) do {} while(0)
+#endif
+
+#define deverr(usbnet, fmt, arg...) \
+	printk(KERN_ERR "%s: " fmt "\n" , (usbnet)->net->name , ## arg)
+#define devwarn(usbnet, fmt, arg...) \
+	printk(KERN_WARNING "%s: " fmt "\n" , (usbnet)->net->name , ## arg)
+
+#define devinfo(usbnet, fmt, arg...) \
+	printk(KERN_INFO "%s: " fmt "\n" , (usbnet)->net->name , ## arg); \
+
+/*-------------------------------------------------------------------------*/
+
+static void usbnet_get_drvinfo (struct net_device *, struct ethtool_drvinfo *);
+static u32 usbnet_get_link (struct net_device *);
+static u32 usbnet_get_msglevel (struct net_device *);
+static void usbnet_set_msglevel (struct net_device *, u32);
+
+/* mostly for PDA style devices, which are always connected if present */
+static int always_connected (struct usbnet *dev)
+{
+	return 0;
+}
+
+/* handles CDC Ethernet and many other network "bulk data" interfaces */
+static int
+get_endpoints (struct usbnet *dev, struct usb_interface *intf)
+{
+	int				tmp;
+	struct usb_host_interface	*alt = NULL;
+	struct usb_host_endpoint	*in = NULL, *out = NULL;
+	struct usb_host_endpoint	*status = NULL;
+
+	for (tmp = 0; tmp < intf->num_altsetting; tmp++) {
+		unsigned	ep;
+
+		in = out = status = NULL;
+		alt = intf->altsetting + tmp;
+
+		/* take the first altsetting with in-bulk + out-bulk;
+		 * remember any status endpoint, just in case;
+		 * ignore other endpoints and altsetttings.
+		 */
+		for (ep = 0; ep < alt->desc.bNumEndpoints; ep++) {
+			struct usb_host_endpoint	*e;
+			int				intr = 0;
+
+			e = alt->endpoint + ep;
+			switch (e->desc.bmAttributes) {
+			case USB_ENDPOINT_XFER_INT:
+				if (!(e->desc.bEndpointAddress & USB_DIR_IN))
+					continue;
+				intr = 1;
+				/* FALLTHROUGH */
+			case USB_ENDPOINT_XFER_BULK:
+				break;
+			default:
+				continue;
+			}
+			if (e->desc.bEndpointAddress & USB_DIR_IN) {
+				if (!intr && !in)
+					in = e;
+				else if (intr && !status)
+					status = e;
+			} else {
+				if (!out)
+					out = e;
+			}
+		}
+		if (in && out)
+			break;
+	}
+	if (!alt || !in || !out)
+		return -EINVAL;
+
+	if (alt->desc.bAlternateSetting != 0
+			|| !(dev->driver_info->flags & FLAG_NO_SETINT)) {
+		tmp = usb_set_interface (dev->udev, alt->desc.bInterfaceNumber,
+				alt->desc.bAlternateSetting);
+		if (tmp < 0)
+			return tmp;
+	}
+	
+	dev->in = usb_rcvbulkpipe (dev->udev,
+			in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+	dev->out = usb_sndbulkpipe (dev->udev,
+			out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+	dev->status = status;
+	return 0;
+}
+
+static void intr_complete (struct urb *urb, struct pt_regs *regs);
+
+static int init_status (struct usbnet *dev, struct usb_interface *intf)
+{
+	char		*buf = NULL;
+	unsigned	pipe = 0;
+	unsigned	maxp;
+	unsigned	period;
+
+	if (!dev->driver_info->status)
+		return 0;
+
+	pipe = usb_rcvintpipe (dev->udev,
+			dev->status->desc.bEndpointAddress
+				& USB_ENDPOINT_NUMBER_MASK);
+	maxp = usb_maxpacket (dev->udev, pipe, 0);
+
+	/* avoid 1 msec chatter:  min 8 msec poll rate */
+	period = max ((int) dev->status->desc.bInterval,
+		(dev->udev->speed == USB_SPEED_HIGH) ? 7 : 3);
+
+	buf = kmalloc (maxp, SLAB_KERNEL);
+	if (buf) {
+		dev->interrupt = usb_alloc_urb (0, SLAB_KERNEL);
+		if (!dev->interrupt) {
+			kfree (buf);
+			return -ENOMEM;
+		} else {
+			usb_fill_int_urb(dev->interrupt, dev->udev, pipe,
+				buf, maxp, intr_complete, dev, period);
+			dev_dbg(&intf->dev,
+				"status ep%din, %d bytes period %d\n",
+				usb_pipeendpoint(pipe), maxp, period);
+		}
+	}
+	return  0;
+}
+
+static void skb_return (struct usbnet *dev, struct sk_buff *skb)
+{
+	int	status;
+
+	skb->dev = dev->net;
+	skb->protocol = eth_type_trans (skb, dev->net);
+	dev->stats.rx_packets++;
+	dev->stats.rx_bytes += skb->len;
+
+	if (netif_msg_rx_status (dev))
+		devdbg (dev, "< rx, len %d, type 0x%x",
+			skb->len + sizeof (struct ethhdr), skb->protocol);
+	memset (skb->cb, 0, sizeof (struct skb_data));
+	status = netif_rx (skb);
+	if (status != NET_RX_SUCCESS && netif_msg_rx_err (dev))
+		devdbg (dev, "netif_rx status %d", status);
+}
+
+
+#ifdef	CONFIG_USB_ALI_M5632
+#define	HAVE_HARDWARE
+
+/*-------------------------------------------------------------------------
+ *
+ * ALi M5632 driver ... does high speed
+ *
+ *-------------------------------------------------------------------------*/
+
+static const struct driver_info	ali_m5632_info = {
+	.description =	"ALi M5632",
+};
+
+
+#endif
+
+
+#ifdef	CONFIG_USB_AN2720
+#define	HAVE_HARDWARE
+
+/*-------------------------------------------------------------------------
+ *
+ * AnchorChips 2720 driver ... http://www.cypress.com
+ *
+ * This doesn't seem to have a way to detect whether the peer is
+ * connected, or need any reset handshaking.  It's got pretty big
+ * internal buffers (handles most of a frame's worth of data).
+ * Chip data sheets don't describe any vendor control messages.
+ *
+ *-------------------------------------------------------------------------*/
+
+static const struct driver_info	an2720_info = {
+	.description =	"AnchorChips/Cypress 2720",
+	// no reset available!
+	// no check_connect available!
+
+	.in = 2, .out = 2,		// direction distinguishes these
+};
+
+#endif	/* CONFIG_USB_AN2720 */
+
+
+#ifdef CONFIG_USB_AX8817X
+/* ASIX AX8817X based USB 2.0 Ethernet Devices */
+
+#define HAVE_HARDWARE
+#define NEED_MII
+
+#include <linux/crc32.h>
+
+#define AX_CMD_SET_SW_MII		0x06
+#define AX_CMD_READ_MII_REG		0x07
+#define AX_CMD_WRITE_MII_REG		0x08
+#define AX_CMD_SET_HW_MII		0x0a
+#define AX_CMD_READ_EEPROM		0x0b
+#define AX_CMD_WRITE_EEPROM		0x0c
+#define AX_CMD_WRITE_ENABLE		0x0d
+#define AX_CMD_WRITE_DISABLE		0x0e
+#define AX_CMD_WRITE_RX_CTL		0x10
+#define AX_CMD_READ_IPG012		0x11
+#define AX_CMD_WRITE_IPG0		0x12
+#define AX_CMD_WRITE_IPG1		0x13
+#define AX_CMD_WRITE_IPG2		0x14
+#define AX_CMD_WRITE_MULTI_FILTER	0x16
+#define AX_CMD_READ_NODE_ID		0x17
+#define AX_CMD_READ_PHY_ID		0x19
+#define AX_CMD_WRITE_MEDIUM_MODE	0x1b
+#define AX_CMD_READ_MONITOR_MODE	0x1c
+#define AX_CMD_WRITE_MONITOR_MODE	0x1d
+#define AX_CMD_WRITE_GPIOS		0x1f
+#define AX_CMD_SW_RESET			0x20
+#define AX_CMD_SW_PHY_STATUS		0x21
+#define AX_CMD_SW_PHY_SELECT		0x22
+#define AX88772_CMD_READ_NODE_ID	0x13
+
+#define AX_MONITOR_MODE			0x01
+#define AX_MONITOR_LINK			0x02
+#define AX_MONITOR_MAGIC		0x04
+#define AX_MONITOR_HSFS			0x10
+
+#define AX_MCAST_FILTER_SIZE		8
+#define AX_MAX_MCAST			64
+
+#define AX_INTERRUPT_BUFSIZE		8
+
+#define AX_EEPROM_LEN			0x40
+
+#define AX_SWRESET_CLEAR		0x00
+#define AX_SWRESET_RR			0x01
+#define AX_SWRESET_RT			0x02
+#define AX_SWRESET_PRTE			0x04
+#define AX_SWRESET_PRL			0x08
+#define AX_SWRESET_BZ			0x10
+#define AX_SWRESET_IPRL			0x20
+#define AX_SWRESET_IPPD			0x40
+
+#define AX88772_IPG0_DEFAULT		0x15
+#define AX88772_IPG1_DEFAULT		0x0c
+#define AX88772_IPG2_DEFAULT		0x12
+
+#define AX_EEPROM_MAGIC			0xdeadbeef
+
+/* This structure cannot exceed sizeof(unsigned long [5]) AKA 20 bytes */
+struct ax8817x_data {
+	u8 multi_filter[AX_MCAST_FILTER_SIZE];
+	struct urb *int_urb;
+	u8 *int_buf;
+};
+
+static int ax8817x_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			    u16 size, void *data)
+{
+	return usb_control_msg(
+		dev->udev,
+		usb_rcvctrlpipe(dev->udev, 0),
+		cmd,
+		USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+		value,
+		index,
+		data,
+		size,
+		CONTROL_TIMEOUT_MS);
+}
+
+static int ax8817x_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			     u16 size, void *data)
+{
+	return usb_control_msg(
+		dev->udev,
+		usb_sndctrlpipe(dev->udev, 0),
+		cmd,
+		USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+		value,
+		index,
+		data,
+		size,
+		CONTROL_TIMEOUT_MS);
+}
+
+static void ax8817x_async_cmd_callback(struct urb *urb, struct pt_regs *regs)
+{
+	struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context;
+
+	if (urb->status < 0)
+		printk(KERN_DEBUG "ax8817x_async_cmd_callback() failed with %d",
+			urb->status);
+
+	kfree(req);
+	usb_free_urb(urb);
+}
+
+static void ax8817x_interrupt_complete(struct urb *urb, struct pt_regs *regs)
+{
+	struct usbnet *dev = (struct usbnet *)urb->context;
+	struct ax8817x_data *data = (struct ax8817x_data *)&dev->data;
+	int link;
+
+	if (urb->status < 0) {
+		devdbg(dev,"ax8817x_interrupt_complete() failed with %d",
+			urb->status);
+	} else {
+		link = data->int_buf[2] & 0x01;
+		if (netif_carrier_ok(dev->net) != link) {
+			if (link)
+				netif_carrier_on(dev->net);
+			else
+				netif_carrier_off(dev->net);
+			devdbg(dev, "ax8817x - Link Status is: %d", link);
+		}
+		usb_submit_urb(data->int_urb, GFP_ATOMIC);
+	}
+}
+
+static void ax8817x_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+				    u16 size, void *data)
+{
+	struct usb_ctrlrequest *req;
+	int status;
+	struct urb *urb;
+
+	if ((urb = usb_alloc_urb(0, GFP_ATOMIC)) == NULL) {
+		devdbg(dev, "Error allocating URB in write_cmd_async!");
+		return;
+	}
+
+	if ((req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC)) == NULL) {
+		deverr(dev, "Failed to allocate memory for control request");
+		usb_free_urb(urb);
+		return;
+	}
+
+	req->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+	req->bRequest = cmd;
+	req->wValue = cpu_to_le16(value);
+	req->wIndex = cpu_to_le16(index); 
+	req->wLength = cpu_to_le16(size);
+
+	usb_fill_control_urb(urb, dev->udev,
+			     usb_sndctrlpipe(dev->udev, 0),
+			     (void *)req, data, size,
+			     ax8817x_async_cmd_callback, req);
+
+	if((status = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+		deverr(dev, "Error submitting the control message: status=%d", status);
+		kfree(req);
+		usb_free_urb(urb);
+	}
+}
+
+static void ax8817x_set_multicast(struct net_device *net)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct ax8817x_data *data = (struct ax8817x_data *)&dev->data;
+	u8 rx_ctl = 0x8c;
+
+	if (net->flags & IFF_PROMISC) {
+		rx_ctl |= 0x01;
+	} else if (net->flags & IFF_ALLMULTI
+		   || net->mc_count > AX_MAX_MCAST) {
+		rx_ctl |= 0x02;
+	} else if (net->mc_count == 0) {
+		/* just broadcast and directed */
+	} else {
+		/* We use the 20 byte dev->data
+		 * for our 8 byte filter buffer
+		 * to avoid allocating memory that
+		 * is tricky to free later */
+		struct dev_mc_list *mc_list = net->mc_list;
+		u32 crc_bits;
+		int i;
+
+		memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE);
+
+		/* Build the multicast hash filter. */
+		for (i = 0; i < net->mc_count; i++) {
+			crc_bits =
+			    ether_crc(ETH_ALEN,
+				      mc_list->dmi_addr) >> 26;
+			data->multi_filter[crc_bits >> 3] |=
+			    1 << (crc_bits & 7);
+			mc_list = mc_list->next;
+		}
+
+		ax8817x_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0,
+				   AX_MCAST_FILTER_SIZE, data->multi_filter);
+
+		rx_ctl |= 0x10;
+	}
+
+	ax8817x_write_cmd_async(dev, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL);
+}
+
+static int ax8817x_mdio_read(struct net_device *netdev, int phy_id, int loc)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	u16 res;
+	u8 buf[1];
+
+	ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, &buf);
+	ax8817x_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id, (__u16)loc, 2, (u16 *)&res);
+	ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, &buf);
+
+	return res & 0xffff;
+}
+
+static void ax8817x_mdio_write(struct net_device *netdev, int phy_id, int loc, int val)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	u16 res = val;
+	u8 buf[1];
+
+	ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, &buf);
+	ax8817x_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, (__u16)loc, 2, (u16 *)&res);
+	ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, &buf);
+}
+
+static void ax8817x_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u8 opt;
+
+	if (ax8817x_read_cmd(dev, AX_CMD_READ_MONITOR_MODE, 0, 0, 1, &opt) < 0) {
+		wolinfo->supported = 0;
+		wolinfo->wolopts = 0;
+		return;
+	}
+	wolinfo->supported = WAKE_PHY | WAKE_MAGIC;
+	wolinfo->wolopts = 0;
+	if (opt & AX_MONITOR_MODE) {
+		if (opt & AX_MONITOR_LINK)
+			wolinfo->wolopts |= WAKE_PHY;
+		if (opt & AX_MONITOR_MAGIC)
+			wolinfo->wolopts |= WAKE_MAGIC;
+	}
+}
+
+static int ax8817x_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u8 opt = 0;
+	u8 buf[1];
+
+	if (wolinfo->wolopts & WAKE_PHY)
+		opt |= AX_MONITOR_LINK;
+	if (wolinfo->wolopts & WAKE_MAGIC)
+		opt |= AX_MONITOR_MAGIC;
+	if (opt != 0)
+		opt |= AX_MONITOR_MODE;
+
+	if (ax8817x_write_cmd(dev, AX_CMD_WRITE_MONITOR_MODE,
+			      opt, 0, 0, &buf) < 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int ax8817x_get_eeprom_len(struct net_device *net)
+{
+	return AX_EEPROM_LEN;
+}
+
+static int ax8817x_get_eeprom(struct net_device *net,
+			      struct ethtool_eeprom *eeprom, u8 *data)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u16 *ebuf = (u16 *)data;
+	int i;
+
+	/* Crude hack to ensure that we don't overwrite memory
+	 * if an odd length is supplied
+	 */
+	if (eeprom->len % 2)
+		return -EINVAL;
+
+	eeprom->magic = AX_EEPROM_MAGIC;
+
+	/* ax8817x returns 2 bytes from eeprom on read */
+	for (i=0; i < eeprom->len / 2; i++) {
+		if (ax8817x_read_cmd(dev, AX_CMD_READ_EEPROM, 
+			eeprom->offset + i, 0, 2, &ebuf[i]) < 0)
+			return -EINVAL;
+	}
+	return 0;
+}
+
+static void ax8817x_get_drvinfo (struct net_device *net,
+				 struct ethtool_drvinfo *info)
+{
+	/* Inherit standard device info */
+	usbnet_get_drvinfo(net, info);
+	info->eedump_len = 0x3e;
+}
+
+static int ax8817x_get_settings(struct net_device *net, struct ethtool_cmd *cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+
+	return mii_ethtool_gset(&dev->mii,cmd);
+}
+
+static int ax8817x_set_settings(struct net_device *net, struct ethtool_cmd *cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+
+	return mii_ethtool_sset(&dev->mii,cmd);
+}
+
+/* We need to override some ethtool_ops so we require our
+   own structure so we don't interfere with other usbnet
+   devices that may be connected at the same time. */
+static struct ethtool_ops ax8817x_ethtool_ops = {
+	.get_drvinfo		= ax8817x_get_drvinfo,
+	.get_link		= ethtool_op_get_link,
+	.get_msglevel		= usbnet_get_msglevel,
+	.set_msglevel		= usbnet_set_msglevel,
+	.get_wol		= ax8817x_get_wol,
+	.set_wol		= ax8817x_set_wol,
+	.get_eeprom_len		= ax8817x_get_eeprom_len,
+	.get_eeprom		= ax8817x_get_eeprom,
+	.get_settings		= ax8817x_get_settings,
+	.set_settings		= ax8817x_set_settings,
+};
+
+static int ax8817x_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	int ret = 0;
+	void *buf;
+	int i;
+	unsigned long gpio_bits = dev->driver_info->data;
+	struct ax8817x_data *data = (struct ax8817x_data *)dev->data;
+
+	get_endpoints(dev,intf);
+
+	if ((data->int_urb = usb_alloc_urb (0, GFP_KERNEL)) == NULL) {
+		dbg ("%s: cannot allocate interrupt URB",
+			dev->net->name);
+		ret = -ENOMEM;
+		goto out1;
+	}
+	
+	if ((data->int_buf = kmalloc(AX_INTERRUPT_BUFSIZE, GFP_KERNEL)) == NULL) {
+		dbg ("%s: cannot allocate memory for interrupt buffer",
+			dev->net->name);
+		ret = -ENOMEM;
+		goto out1;
+	}
+	memset(data->int_buf, 0, AX_INTERRUPT_BUFSIZE);
+
+	usb_fill_int_urb (data->int_urb, dev->udev,
+		usb_rcvintpipe (dev->udev, 1),
+		data->int_buf, AX_INTERRUPT_BUFSIZE,
+		ax8817x_interrupt_complete, dev,
+		dev->udev->speed == USB_SPEED_HIGH ? 8 : 100);
+
+	buf = kmalloc(ETH_ALEN, GFP_KERNEL);
+	if(!buf) {
+		ret = -ENOMEM;
+		goto out2;
+	}
+
+	/* Toggle the GPIOs in a manufacturer/model specific way */
+	for (i = 2; i >= 0; i--) {
+		if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+					(gpio_bits >> (i * 8)) & 0xff, 0, 0,
+					buf)) < 0)
+			goto out3;
+		msleep(5);
+	}
+
+	if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, 0x80, 0, 0, buf)) < 0) {
+		dbg("send AX_CMD_WRITE_RX_CTL failed: %d", ret);
+		goto out3;
+	}
+
+	/* Get the MAC address */
+	memset(buf, 0, ETH_ALEN);
+	if ((ret = ax8817x_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, 6, buf)) < 0) {
+		dbg("read AX_CMD_READ_NODE_ID failed: %d", ret);
+		goto out3;
+	}
+	memcpy(dev->net->dev_addr, buf, ETH_ALEN);
+
+	/* Get the PHY id */
+	if ((ret = ax8817x_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf)) < 0) {
+		dbg("error on read AX_CMD_READ_PHY_ID: %02x", ret);
+		goto out3;
+	} else if (ret < 2) {
+		/* this should always return 2 bytes */
+		dbg("AX_CMD_READ_PHY_ID returned less than 2 bytes: ret=%02x", ret);
+		ret = -EIO;
+		goto out3;
+	}
+
+	/* Initialize MII structure */
+	dev->mii.dev = dev->net;
+	dev->mii.mdio_read = ax8817x_mdio_read;
+	dev->mii.mdio_write = ax8817x_mdio_write;
+	dev->mii.phy_id_mask = 0x3f;
+	dev->mii.reg_num_mask = 0x1f;
+	dev->mii.phy_id = *((u8 *)buf + 1);
+
+	dev->net->set_multicast_list = ax8817x_set_multicast;
+	dev->net->ethtool_ops = &ax8817x_ethtool_ops;
+
+	ax8817x_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR,
+			cpu_to_le16(BMCR_RESET));
+	ax8817x_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+			cpu_to_le16(ADVERTISE_ALL | ADVERTISE_CSMA | 0x0400));
+	mii_nway_restart(&dev->mii);
+
+	if((ret = usb_submit_urb(data->int_urb, GFP_KERNEL)) < 0) {
+		dbg("Failed to submit interrupt URB: %02x", ret);
+		goto out2;
+	}
+
+	return 0;
+out3:
+	kfree(buf);
+out2:
+	kfree(data->int_buf);
+out1:
+	usb_free_urb(data->int_urb);
+	return ret;
+}
+
+static void ax8817x_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+	struct ax8817x_data *data = (struct ax8817x_data *)dev->data;
+
+	usb_kill_urb(data->int_urb);
+	usb_free_urb(data->int_urb);
+	kfree(data->int_buf);
+}
+
+static struct ethtool_ops ax88772_ethtool_ops = {
+	.get_drvinfo		= ax8817x_get_drvinfo,
+	.get_link		= ethtool_op_get_link,
+	.get_msglevel		= usbnet_get_msglevel,
+	.set_msglevel		= usbnet_set_msglevel,
+	.get_wol		= ax8817x_get_wol,
+	.set_wol		= ax8817x_set_wol,
+	.get_eeprom_len		= ax8817x_get_eeprom_len,
+	.get_eeprom		= ax8817x_get_eeprom,
+	.get_settings		= ax8817x_get_settings,
+	.set_settings		= ax8817x_set_settings,
+};
+
+static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	int ret;
+	void *buf;
+	struct ax8817x_data *data = (struct ax8817x_data *)dev->data;
+
+	get_endpoints(dev,intf);
+
+	if ((data->int_urb = usb_alloc_urb (0, GFP_KERNEL)) == 0) {
+		dbg ("Cannot allocate interrupt URB");
+		ret = -ENOMEM;
+		goto out1;
+	}
+	
+	if ((data->int_buf = kmalloc(AX_INTERRUPT_BUFSIZE, GFP_KERNEL)) == NULL) {
+		dbg ("Cannot allocate memory for interrupt buffer");
+		ret = -ENOMEM;
+		goto out1;
+	}
+	memset(data->int_buf, 0, AX_INTERRUPT_BUFSIZE);
+
+	usb_fill_int_urb (data->int_urb, dev->udev,
+		usb_rcvintpipe (dev->udev, 1),
+		data->int_buf, AX_INTERRUPT_BUFSIZE,
+		ax8817x_interrupt_complete, dev,
+		dev->udev->speed == USB_SPEED_HIGH ? 8 : 100);
+
+	buf = kmalloc(6, GFP_KERNEL);
+	if(!buf) {
+		dbg ("Cannot allocate memory for buffer");
+		ret = -ENOMEM;
+		goto out2;
+	}
+
+	if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+				     0x00B0, 0, 0, buf)) < 0)
+		goto out3;
+
+	msleep(5);
+	if ((ret = ax8817x_write_cmd(dev, AX_CMD_SW_PHY_SELECT, 0x0001, 0, 0, buf)) < 0) {
+		dbg("Select PHY #1 failed: %d", ret);
+		goto out3;
+	}
+
+	if ((ret =
+	     ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPPD, 0, 0, buf)) < 0) {
+		dbg("Failed to power down internal PHY: %d", ret);
+		goto out3;
+	}
+
+	msleep(150);
+	if ((ret =
+	     ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_CLEAR, 0, 0, buf)) < 0) {
+		dbg("Failed to perform software reset: %d", ret);
+		goto out3;
+	}
+
+	msleep(150);
+	if ((ret =
+	     ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPRL | AX_SWRESET_PRL, 0, 0, buf)) < 0) {
+		dbg("Failed to set Internal/External PHY reset control: %d", ret);
+		goto out3;
+	}
+
+	msleep(150);
+	if ((ret =
+	     ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, 0x0000, 0, 0,
+			       buf)) < 0) {
+		dbg("Failed to reset RX_CTL: %d", ret);
+		goto out3;
+	}
+
+	/* Get the MAC address */
+	memset(buf, 0, ETH_ALEN);
+	if ((ret = ax8817x_read_cmd(dev, AX88772_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf)) < 0) {
+		dbg("Failed to read MAC address: %d", ret);
+		goto out3;
+	}
+	memcpy(dev->net->dev_addr, buf, ETH_ALEN);
+
+	if ((ret = ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, buf)) < 0) {
+		dbg("Enabling software MII failed: %d", ret);
+		goto out3;
+	}
+
+	if (((ret =
+	      ax8817x_read_cmd(dev, AX_CMD_READ_MII_REG, 0x0010, 2, 2, buf)) < 0)
+	    || (*((u16 *)buf) != 0x003b)) {
+		dbg("Read PHY register 2 must be 0x3b00: %d", ret);
+		goto out3;
+	}
+
+	/* Initialize MII structure */
+	dev->mii.dev = dev->net;
+	dev->mii.mdio_read = ax8817x_mdio_read;
+	dev->mii.mdio_write = ax8817x_mdio_write;
+	dev->mii.phy_id_mask = 0xff;
+	dev->mii.reg_num_mask = 0xff;
+
+	/* Get the PHY id */
+	if ((ret = ax8817x_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf)) < 0) {
+		dbg("Error reading PHY ID: %02x", ret);
+		goto out3;
+	} else if (ret < 2) {
+		/* this should always return 2 bytes */
+		dbg("AX_CMD_READ_PHY_ID returned less than 2 bytes: ret=%02x",
+		    ret);
+		ret = -EIO;
+		goto out3;
+	}
+	dev->mii.phy_id = *((u8 *)buf + 1);
+
+	if ((ret =
+	     ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_PRL, 0, 0, buf)) < 0) {
+		dbg("Set external PHY reset pin level: %d", ret);
+		goto out3;
+	}
+	msleep(150);
+	if ((ret =
+	     ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPRL | AX_SWRESET_PRL, 0, 0, buf)) < 0) {
+		dbg("Set Internal/External PHY reset control: %d", ret);
+		goto out3;
+	}
+	msleep(150);
+
+
+	dev->net->set_multicast_list = ax8817x_set_multicast;
+	dev->net->ethtool_ops = &ax88772_ethtool_ops;
+
+	ax8817x_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR,
+			cpu_to_le16(BMCR_RESET));
+	ax8817x_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+			cpu_to_le16(ADVERTISE_ALL | ADVERTISE_CSMA));
+	mii_nway_restart(&dev->mii);
+
+	if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, 0x0336, 0, 0, buf)) < 0) {
+		dbg("Write medium mode register: %d", ret);
+		goto out3;
+	}
+
+	if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_IPG0, AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,AX88772_IPG2_DEFAULT, 0, buf)) < 0) {
+		dbg("Write IPG,IPG1,IPG2 failed: %d", ret);
+		goto out3;
+	}
+	if ((ret =
+	     ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, &buf)) < 0) {
+		dbg("Failed to set hardware MII: %02x", ret);
+		goto out3;
+	}
+
+	/* Set RX_CTL to default values with 2k buffer, and enable cactus */
+	if ((ret =
+	     ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, 0x0088, 0, 0,
+			       buf)) < 0) {
+		dbg("Reset RX_CTL failed: %d", ret);
+		goto out3;
+	}
+
+	if((ret = usb_submit_urb(data->int_urb, GFP_KERNEL)) < 0) {
+		dbg("Failed to submit interrupt URB: %02x", ret);
+		goto out3;
+	}
+
+	kfree(buf);
+
+	return 0;
+
+out3:
+	kfree(buf);
+out2:
+	kfree(data->int_buf);
+out1:
+	usb_free_urb(data->int_urb);
+
+	return ret;
+}
+
+static int ax88772_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+	u32 *header;
+	char *packet;
+	struct sk_buff *ax_skb;
+	u16 size;
+
+	header = (u32 *) skb->data;
+	le32_to_cpus(header);
+	packet = (char *)(header + 1);
+
+	skb_pull(skb, 4);
+
+	while (skb->len > 0) {
+		if ((short)(*header & 0x0000ffff) !=
+		    ~((short)((*header & 0xffff0000) >> 16))) {
+			devdbg(dev,"header length data is error");
+		}
+		/* get the packet length */
+		size = (u16) (*header & 0x0000ffff);
+
+		if ((skb->len) - ((size + 1) & 0xfffe) == 0)
+			return 2;
+		if (size > ETH_FRAME_LEN) {
+			devdbg(dev,"invalid rx length %d", size);
+			return 0;
+		}
+		ax_skb = skb_clone(skb, GFP_ATOMIC);
+		if (ax_skb) {
+			ax_skb->len = size;
+			ax_skb->data = packet;
+			ax_skb->tail = packet + size;
+			skb_return(dev, ax_skb);
+		} else {
+			return 0;
+		}
+
+		skb_pull(skb, (size + 1) & 0xfffe);
+
+		if (skb->len == 0)
+			break;
+
+		header = (u32 *) skb->data;
+		le32_to_cpus(header);
+		packet = (char *)(header + 1);
+		skb_pull(skb, 4);
+	}
+
+	if (skb->len < 0) {
+		devdbg(dev,"invalid rx length %d", skb->len);
+		return 0;
+	}
+	return 1;
+}
+
+static struct sk_buff *ax88772_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
+					int flags)
+{
+	int padlen;
+	int headroom = skb_headroom(skb);
+	int tailroom = skb_tailroom(skb);
+	u32 *packet_len;
+	u32 *padbytes_ptr;
+
+	padlen = ((skb->len + 4) % 512) ? 0 : 4;
+
+	if ((!skb_cloned(skb))
+	    && ((headroom + tailroom) >= (4 + padlen))) {
+		if ((headroom < 4) || (tailroom < padlen)) {
+			skb->data = memmove(skb->head + 4, skb->data, skb->len);
+			skb->tail = skb->data + skb->len;
+		}
+	} else {
+		struct sk_buff *skb2;
+		skb2 = skb_copy_expand(skb, 4, padlen, flags);
+		dev_kfree_skb_any(skb);
+		skb = skb2;
+		if (!skb)
+			return NULL;
+	}
+
+	packet_len = (u32 *) skb_push(skb, 4);
+
+	packet_len = (u32 *) skb->data;
+	*packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4);
+
+	if ((skb->len % 512) == 0) {
+		padbytes_ptr = (u32 *) skb->tail;
+		*padbytes_ptr = 0xffff0000;
+		skb_put(skb, padlen);
+	}
+	return skb;
+}
+
+static const struct driver_info ax8817x_info = {
+	.description = "ASIX AX8817x USB 2.0 Ethernet",
+	.bind = ax8817x_bind,
+	.unbind = ax8817x_unbind,
+	.flags =  FLAG_ETHER,
+	.data = 0x00130103,
+};
+
+static const struct driver_info dlink_dub_e100_info = {
+	.description = "DLink DUB-E100 USB Ethernet",
+	.bind = ax8817x_bind,
+	.unbind = ax8817x_unbind,
+	.flags =  FLAG_ETHER,
+	.data = 0x009f9d9f,
+};
+
+static const struct driver_info netgear_fa120_info = {
+	.description = "Netgear FA-120 USB Ethernet",
+	.bind = ax8817x_bind,
+	.unbind = ax8817x_unbind,
+	.flags =  FLAG_ETHER,
+	.data = 0x00130103,
+};
+
+static const struct driver_info hawking_uf200_info = {
+	.description = "Hawking UF200 USB Ethernet",
+	.bind = ax8817x_bind,
+	.unbind = ax8817x_unbind,
+	.flags =  FLAG_ETHER,
+	.data = 0x001f1d1f,
+};
+
+static const struct driver_info ax88772_info = {
+	.description = "ASIX AX88772 USB 2.0 Ethernet",
+	.bind = ax88772_bind,
+	.unbind = ax8817x_unbind,
+	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
+	.rx_fixup = ax88772_rx_fixup,
+	.tx_fixup = ax88772_tx_fixup,
+	.data = 0x00130103,
+};
+
+#endif /* CONFIG_USB_AX8817X */
+
+
+
+#ifdef	CONFIG_USB_BELKIN
+#define	HAVE_HARDWARE
+
+/*-------------------------------------------------------------------------
+ *
+ * Belkin F5U104 ... two NetChip 2280 devices + Atmel microcontroller
+ *
+ * ... also two eTEK designs, including one sold as "Advance USBNET"
+ *
+ *-------------------------------------------------------------------------*/
+
+static const struct driver_info	belkin_info = {
+	.description =	"Belkin, eTEK, or compatible",
+};
+
+#endif	/* CONFIG_USB_BELKIN */
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Communications Device Class declarations.
+ * Used by CDC Ethernet, and some CDC variants
+ *
+ *-------------------------------------------------------------------------*/
+
+#ifdef	CONFIG_USB_CDCETHER
+#define NEED_GENERIC_CDC
+#endif
+
+#ifdef	CONFIG_USB_ZAURUS
+/* Ethernet variant uses funky framing, broken ethernet addressing */
+#define NEED_GENERIC_CDC
+#endif
+
+#ifdef	CONFIG_USB_RNDIS
+/* ACM variant uses even funkier framing, complex control RPC scheme */
+#define NEED_GENERIC_CDC
+#endif
+
+
+#ifdef	NEED_GENERIC_CDC
+
+#include <linux/usb_cdc.h>
+
+struct cdc_state {
+	struct usb_cdc_header_desc	*header;
+	struct usb_cdc_union_desc	*u;
+	struct usb_cdc_ether_desc	*ether;
+	struct usb_interface		*control;
+	struct usb_interface		*data;
+};
+
+static struct usb_driver usbnet_driver;
+
+/*
+ * probes control interface, claims data interface, collects the bulk
+ * endpoints, activates data interface (if needed), maybe sets MTU.
+ * all pure cdc, except for certain firmware workarounds.
+ */
+static int generic_cdc_bind (struct usbnet *dev, struct usb_interface *intf)
+{
+	u8				*buf = intf->cur_altsetting->extra;
+	int				len = intf->cur_altsetting->extralen;
+	struct usb_interface_descriptor	*d;
+	struct cdc_state		*info = (void *) &dev->data;
+	int				status;
+	int				rndis;
+
+	if (sizeof dev->data < sizeof *info)
+		return -EDOM;
+
+	/* expect strict spec conformance for the descriptors, but
+	 * cope with firmware which stores them in the wrong place
+	 */
+	if (len == 0 && dev->udev->actconfig->extralen) {
+		/* Motorola SB4100 (and others: Brad Hards says it's
+		 * from a Broadcom design) put CDC descriptors here
+		 */
+		buf = dev->udev->actconfig->extra;
+		len = dev->udev->actconfig->extralen;
+		if (len)
+			dev_dbg (&intf->dev,
+				"CDC descriptors on config\n");
+	}
+
+	/* this assumes that if there's a non-RNDIS vendor variant
+	 * of cdc-acm, it'll fail RNDIS requests cleanly.
+	 */
+	rndis = (intf->cur_altsetting->desc.bInterfaceProtocol == 0xff);
+
+	memset (info, 0, sizeof *info);
+	info->control = intf;
+	while (len > 3) {
+		if (buf [1] != USB_DT_CS_INTERFACE)
+			goto next_desc;
+
+		/* use bDescriptorSubType to identify the CDC descriptors.
+		 * We expect devices with CDC header and union descriptors.
+		 * For CDC Ethernet we need the ethernet descriptor.
+		 * For RNDIS, ignore two (pointless) CDC modem descriptors
+		 * in favor of a complicated OID-based RPC scheme doing what
+		 * CDC Ethernet achieves with a simple descriptor.
+		 */
+		switch (buf [2]) {
+		case USB_CDC_HEADER_TYPE:
+			if (info->header) {
+				dev_dbg (&intf->dev, "extra CDC header\n");
+				goto bad_desc;
+			}
+			info->header = (void *) buf;
+			if (info->header->bLength != sizeof *info->header) {
+				dev_dbg (&intf->dev, "CDC header len %u\n",
+					info->header->bLength);
+				goto bad_desc;
+			}
+			break;
+		case USB_CDC_UNION_TYPE:
+			if (info->u) {
+				dev_dbg (&intf->dev, "extra CDC union\n");
+				goto bad_desc;
+			}
+			info->u = (void *) buf;
+			if (info->u->bLength != sizeof *info->u) {
+				dev_dbg (&intf->dev, "CDC union len %u\n",
+					info->u->bLength);
+				goto bad_desc;
+			}
+
+			/* we need a master/control interface (what we're
+			 * probed with) and a slave/data interface; union
+			 * descriptors sort this all out.
+			 */
+			info->control = usb_ifnum_to_if(dev->udev,
+						info->u->bMasterInterface0);
+			info->data = usb_ifnum_to_if(dev->udev,
+						info->u->bSlaveInterface0);
+			if (!info->control || !info->data) {
+				dev_dbg (&intf->dev,
+					"master #%u/%p slave #%u/%p\n",
+					info->u->bMasterInterface0,
+					info->control,
+					info->u->bSlaveInterface0,
+					info->data);
+				goto bad_desc;
+			}
+			if (info->control != intf) {
+				dev_dbg (&intf->dev, "bogus CDC Union\n");
+				/* Ambit USB Cable Modem (and maybe others)
+				 * interchanges master and slave interface.
+				 */
+				if (info->data == intf) {
+					info->data = info->control;
+					info->control = intf;
+				} else
+					goto bad_desc;
+			}
+
+			/* a data interface altsetting does the real i/o */
+			d = &info->data->cur_altsetting->desc;
+			if (d->bInterfaceClass != USB_CLASS_CDC_DATA) {
+				dev_dbg (&intf->dev, "slave class %u\n",
+					d->bInterfaceClass);
+				goto bad_desc;
+			}
+			break;
+		case USB_CDC_ETHERNET_TYPE:
+			if (info->ether) {
+				dev_dbg (&intf->dev, "extra CDC ether\n");
+				goto bad_desc;
+			}
+			info->ether = (void *) buf;
+			if (info->ether->bLength != sizeof *info->ether) {
+				dev_dbg (&intf->dev, "CDC ether len %u\n",
+					info->u->bLength);
+				goto bad_desc;
+			}
+			dev->net->mtu = le16_to_cpup (
+						&info->ether->wMaxSegmentSize)
+					- ETH_HLEN;
+			/* because of Zaurus, we may be ignoring the host
+			 * side link address we were given.
+			 */
+			break;
+		}
+next_desc:
+		len -= buf [0];	/* bLength */
+		buf += buf [0];
+	}
+
+	if (!info->header || !info->u || (!rndis && !info->ether)) {
+		dev_dbg (&intf->dev, "missing cdc %s%s%sdescriptor\n",
+			info->header ? "" : "header ",
+			info->u ? "" : "union ",
+			info->ether ? "" : "ether ");
+		goto bad_desc;
+	}
+
+	/* claim data interface and set it up ... with side effects.
+	 * network traffic can't flow until an altsetting is enabled.
+	 */
+	status = usb_driver_claim_interface (&usbnet_driver, info->data, dev);
+	if (status < 0)
+		return status;
+	status = get_endpoints (dev, info->data);
+	if (status < 0) {
+		/* ensure immediate exit from usbnet_disconnect */
+		usb_set_intfdata(info->data, NULL);
+		usb_driver_release_interface (&usbnet_driver, info->data);
+		return status;
+	}
+
+	/* status endpoint: optional for CDC Ethernet, not RNDIS (or ACM) */
+	dev->status = NULL;
+	if (info->control->cur_altsetting->desc.bNumEndpoints == 1) {
+		struct usb_endpoint_descriptor	*desc;
+
+		dev->status = &info->control->cur_altsetting->endpoint [0];
+		desc = &dev->status->desc;
+		if (desc->bmAttributes != USB_ENDPOINT_XFER_INT
+				|| !(desc->bEndpointAddress & USB_DIR_IN)
+				|| (le16_to_cpu(desc->wMaxPacketSize)
+					< sizeof (struct usb_cdc_notification))
+				|| !desc->bInterval) {
+			dev_dbg (&intf->dev, "bad notification endpoint\n");
+			dev->status = NULL;
+		}
+	}
+	if (rndis && !dev->status) {
+		dev_dbg (&intf->dev, "missing RNDIS status endpoint\n");
+		usb_set_intfdata(info->data, NULL);
+		usb_driver_release_interface (&usbnet_driver, info->data);
+		return -ENODEV;
+	}
+	return 0;
+
+bad_desc:
+	dev_info (&dev->udev->dev, "bad CDC descriptors\n");
+	return -ENODEV;
+}
+
+static void cdc_unbind (struct usbnet *dev, struct usb_interface *intf)
+{
+	struct cdc_state		*info = (void *) &dev->data;
+
+	/* disconnect master --> disconnect slave */
+	if (intf == info->control && info->data) {
+		/* ensure immediate exit from usbnet_disconnect */
+		usb_set_intfdata(info->data, NULL);
+		usb_driver_release_interface (&usbnet_driver, info->data);
+		info->data = NULL;
+	}
+
+	/* and vice versa (just in case) */
+	else if (intf == info->data && info->control) {
+		/* ensure immediate exit from usbnet_disconnect */
+		usb_set_intfdata(info->control, NULL);
+		usb_driver_release_interface (&usbnet_driver, info->control);
+		info->control = NULL;
+	}
+}
+
+
+static void dumpspeed (struct usbnet *dev, __le32 *speeds)
+{
+	if (netif_msg_timer (dev))
+		devinfo (dev, "link speeds: %u kbps up, %u kbps down",
+			__le32_to_cpu(speeds[0]) / 1000,
+		__le32_to_cpu(speeds[1]) / 1000);
+}
+
+static void cdc_status (struct usbnet *dev, struct urb *urb)
+{
+	struct usb_cdc_notification	*event;
+
+	if (urb->actual_length < sizeof *event)
+		return;
+	
+	/* SPEED_CHANGE can get split into two 8-byte packets */
+	if (test_and_clear_bit (EVENT_STS_SPLIT, &dev->flags)) {
+		dumpspeed (dev, (__le32 *) urb->transfer_buffer);
+		return;
+	}
+
+	event = urb->transfer_buffer;
+	switch (event->bNotificationType) {
+	case USB_CDC_NOTIFY_NETWORK_CONNECTION:
+		if (netif_msg_timer (dev))
+			devdbg (dev, "CDC: carrier %s",
+					event->wValue ? "on" : "off");
+		if (event->wValue)
+			netif_carrier_on(dev->net);
+		else
+			netif_carrier_off(dev->net);
+		break;
+	case USB_CDC_NOTIFY_SPEED_CHANGE:	/* tx/rx rates */
+		if (netif_msg_timer (dev))
+			devdbg (dev, "CDC: speed change (len %d)",
+					urb->actual_length);
+		if (urb->actual_length != (sizeof *event + 8))
+			set_bit (EVENT_STS_SPLIT, &dev->flags);
+		else
+			dumpspeed (dev, (__le32 *) &event[1]);
+		break;
+	// case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:	/* RNDIS; or unsolicited */
+	default:
+		deverr (dev, "CDC: unexpected notification %02x!",
+				 event->bNotificationType);
+		break;
+	}
+}
+
+#endif	/* NEED_GENERIC_CDC */
+
+
+#ifdef	CONFIG_USB_CDCETHER
+#define	HAVE_HARDWARE
+
+/*-------------------------------------------------------------------------
+ *
+ * Communications Device Class, Ethernet Control model
+ * 
+ * Takes two interfaces.  The DATA interface is inactive till an altsetting
+ * is selected.  Configuration data includes class descriptors.
+ *
+ * This should interop with whatever the 2.4 "CDCEther.c" driver
+ * (by Brad Hards) talked with.
+ *
+ *-------------------------------------------------------------------------*/
+
+#include <linux/ctype.h>
+
+static u8 nibble (unsigned char c)
+{
+	if (likely (isdigit (c)))
+		return c - '0';
+	c = toupper (c);
+	if (likely (isxdigit (c)))
+		return 10 + c - 'A';
+	return 0;
+}
+
+static inline int
+get_ethernet_addr (struct usbnet *dev, struct usb_cdc_ether_desc *e)
+{
+	int 		tmp, i;
+	unsigned char	buf [13];
+
+	tmp = usb_string (dev->udev, e->iMACAddress, buf, sizeof buf);
+	if (tmp != 12) {
+		dev_dbg (&dev->udev->dev,
+			"bad MAC string %d fetch, %d\n", e->iMACAddress, tmp);
+		if (tmp >= 0)
+			tmp = -EINVAL;
+		return tmp;
+	}
+	for (i = tmp = 0; i < 6; i++, tmp += 2)
+		dev->net->dev_addr [i] =
+			 (nibble (buf [tmp]) << 4) + nibble (buf [tmp + 1]);
+	return 0;
+}
+
+static int cdc_bind (struct usbnet *dev, struct usb_interface *intf)
+{
+	int				status;
+	struct cdc_state		*info = (void *) &dev->data;
+
+	status = generic_cdc_bind (dev, intf);
+	if (status < 0)
+		return status;
+
+	status = get_ethernet_addr (dev, info->ether);
+	if (status < 0) {
+		usb_set_intfdata(info->data, NULL);
+		usb_driver_release_interface (&usbnet_driver, info->data);
+		return status;
+	}
+
+	/* FIXME cdc-ether has some multicast code too, though it complains
+	 * in routine cases.  info->ether describes the multicast support.
+	 */
+	return 0;
+}
+
+static const struct driver_info	cdc_info = {
+	.description =	"CDC Ethernet Device",
+	.flags =	FLAG_ETHER,
+	// .check_connect = cdc_check_connect,
+	.bind =		cdc_bind,
+	.unbind =	cdc_unbind,
+	.status =	cdc_status,
+};
+
+#endif	/* CONFIG_USB_CDCETHER */
+
+
+
+#ifdef	CONFIG_USB_EPSON2888
+#define	HAVE_HARDWARE
+
+/*-------------------------------------------------------------------------
+ *
+ * EPSON USB clients
+ *
+ * This is the same idea as Linux PDAs (below) except the firmware in the
+ * device might not be Tux-powered.  Epson provides reference firmware that
+ * implements this interface.  Product developers can reuse or modify that
+ * code, such as by using their own product and vendor codes.
+ *
+ * Support was from Juro Bystricky <bystricky.juro@erd.epson.com>
+ *
+ *-------------------------------------------------------------------------*/
+
+static const struct driver_info	epson2888_info = {
+	.description =	"Epson USB Device",
+	.check_connect = always_connected,
+
+	.in = 4, .out = 3,
+};
+
+#endif	/* CONFIG_USB_EPSON2888 */
+
+
+#ifdef CONFIG_USB_GENESYS
+#define	HAVE_HARDWARE
+
+/*-------------------------------------------------------------------------
+ *
+ * GeneSys GL620USB-A (www.genesyslogic.com.tw)
+ *
+ * ... should partially interop with the Win32 driver for this hardware
+ * The GeneSys docs imply there's some NDIS issue motivating this framing.
+ *
+ * Some info from GeneSys:
+ *  - GL620USB-A is full duplex; GL620USB is only half duplex for bulk.
+ *    (Some cables, like the BAFO-100c, use the half duplex version.)
+ *  - For the full duplex model, the low bit of the version code says
+ *    which side is which ("left/right").
+ *  - For the half duplex type, a control/interrupt handshake settles
+ *    the transfer direction.  (That's disabled here, partially coded.)
+ *    A control URB would block until other side writes an interrupt.
+ *
+ * Original code from Jiun-Jie Huang <huangjj@genesyslogic.com.tw>
+ * and merged into "usbnet" by Stanislav Brabec <utx@penguin.cz>.
+ *
+ *-------------------------------------------------------------------------*/
+
+// control msg write command
+#define GENELINK_CONNECT_WRITE			0xF0
+// interrupt pipe index
+#define GENELINK_INTERRUPT_PIPE			0x03
+// interrupt read buffer size
+#define INTERRUPT_BUFSIZE			0x08
+// interrupt pipe interval value
+#define GENELINK_INTERRUPT_INTERVAL		0x10
+// max transmit packet number per transmit
+#define GL_MAX_TRANSMIT_PACKETS			32
+// max packet length
+#define GL_MAX_PACKET_LEN			1514
+// max receive buffer size 
+#define GL_RCV_BUF_SIZE		\
+	(((GL_MAX_PACKET_LEN + 4) * GL_MAX_TRANSMIT_PACKETS) + 4)
+
+struct gl_packet {
+	u32		packet_length;
+	char		packet_data [1];
+};
+
+struct gl_header {
+	u32			packet_count;
+	struct gl_packet	packets;
+};
+
+#ifdef	GENELINK_ACK
+
+// FIXME:  this code is incomplete, not debugged; it doesn't
+// handle interrupts correctly.  interrupts should be generic
+// code like all other device I/O, anyway.
+
+struct gl_priv { 
+	struct urb	*irq_urb;
+	char		irq_buf [INTERRUPT_BUFSIZE];
+};
+
+static inline int gl_control_write (struct usbnet *dev, u8 request, u16 value)
+{
+	int retval;
+
+	retval = usb_control_msg (dev->udev,
+		      usb_sndctrlpipe (dev->udev, 0),
+		      request,
+		      USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+		      value, 
+		      0,			// index
+		      0,			// data buffer
+		      0,			// size
+		      CONTROL_TIMEOUT_MS);
+	return retval;
+}
+
+static void gl_interrupt_complete (struct urb *urb, struct pt_regs *regs)
+{
+	int status = urb->status;
+	
+	switch (status) {
+	case 0:
+		/* success */
+		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		dbg("%s - urb shutting down with status: %d",
+				__FUNCTION__, status);
+		return;
+	default:
+		dbg("%s - nonzero urb status received: %d",
+				__FUNCTION__, urb->status);
+	}
+
+	status = usb_submit_urb (urb, GFP_ATOMIC);
+	if (status)
+		err ("%s - usb_submit_urb failed with result %d",
+		     __FUNCTION__, status);
+}
+
+static int gl_interrupt_read (struct usbnet *dev)
+{
+	struct gl_priv	*priv = dev->priv_data;
+	int		retval;
+
+	// issue usb interrupt read
+	if (priv && priv->irq_urb) {
+		// submit urb
+		if ((retval = usb_submit_urb (priv->irq_urb, GFP_KERNEL)) != 0)
+			dbg ("gl_interrupt_read: submit fail - %X...", retval);
+		else
+			dbg ("gl_interrupt_read: submit success...");
+	}
+
+	return 0;
+}
+
+// check whether another side is connected
+static int genelink_check_connect (struct usbnet *dev)
+{
+	int			retval;
+
+	dbg ("genelink_check_connect...");
+
+	// detect whether another side is connected
+	if ((retval = gl_control_write (dev, GENELINK_CONNECT_WRITE, 0)) != 0) {
+		dbg ("%s: genelink_check_connect write fail - %X",
+			dev->net->name, retval);
+		return retval;
+	}
+
+	// usb interrupt read to ack another side 
+	if ((retval = gl_interrupt_read (dev)) != 0) {
+		dbg ("%s: genelink_check_connect read fail - %X",
+			dev->net->name, retval);
+		return retval;
+	}
+
+	dbg ("%s: genelink_check_connect read success", dev->net->name);
+	return 0;
+}
+
+// allocate and initialize the private data for genelink
+static int genelink_init (struct usbnet *dev)
+{
+	struct gl_priv *priv;
+
+	// allocate the private data structure
+	if ((priv = kmalloc (sizeof *priv, GFP_KERNEL)) == 0) {
+		dbg ("%s: cannot allocate private data per device",
+			dev->net->name);
+		return -ENOMEM;
+	}
+
+	// allocate irq urb
+	if ((priv->irq_urb = usb_alloc_urb (0, GFP_KERNEL)) == 0) {
+		dbg ("%s: cannot allocate private irq urb per device",
+			dev->net->name);
+		kfree (priv);
+		return -ENOMEM;
+	}
+
+	// fill irq urb
+	usb_fill_int_urb (priv->irq_urb, dev->udev,
+		usb_rcvintpipe (dev->udev, GENELINK_INTERRUPT_PIPE),
+		priv->irq_buf, INTERRUPT_BUFSIZE,
+		gl_interrupt_complete, 0,
+		GENELINK_INTERRUPT_INTERVAL);
+
+	// set private data pointer
+	dev->priv_data = priv;
+
+	return 0;
+}
+
+// release the private data
+static int genelink_free (struct usbnet *dev)
+{
+	struct gl_priv	*priv = dev->priv_data;
+
+	if (!priv) 
+		return 0;
+
+// FIXME:  can't cancel here; it's synchronous, and
+// should have happened earlier in any case (interrupt
+// handling needs to be generic)
+
+	// cancel irq urb first
+	usb_kill_urb (priv->irq_urb);
+
+	// free irq urb
+	usb_free_urb (priv->irq_urb);
+
+	// free the private data structure
+	kfree (priv);
+
+	return 0;
+}
+
+#endif
+
+static int genelink_rx_fixup (struct usbnet *dev, struct sk_buff *skb)
+{
+	struct gl_header	*header;
+	struct gl_packet	*packet;
+	struct sk_buff		*gl_skb;
+	u32			size;
+
+	header = (struct gl_header *) skb->data;
+
+	// get the packet count of the received skb
+	le32_to_cpus (&header->packet_count);
+	if ((header->packet_count > GL_MAX_TRANSMIT_PACKETS)
+			|| (header->packet_count < 0)) {
+		dbg ("genelink: invalid received packet count %d",
+			header->packet_count);
+		return 0;
+	}
+
+	// set the current packet pointer to the first packet
+	packet = &header->packets;
+
+	// decrement the length for the packet count size 4 bytes
+	skb_pull (skb, 4);
+
+	while (header->packet_count > 1) {
+		// get the packet length
+		size = packet->packet_length;
+
+		// this may be a broken packet
+		if (size > GL_MAX_PACKET_LEN) {
+			dbg ("genelink: invalid rx length %d", size);
+			return 0;
+		}
+
+		// allocate the skb for the individual packet
+		gl_skb = alloc_skb (size, GFP_ATOMIC);
+		if (gl_skb) {
+
+			// copy the packet data to the new skb
+			memcpy(skb_put(gl_skb, size), packet->packet_data, size);
+			skb_return (dev, skb);
+		}
+
+		// advance to the next packet
+		packet = (struct gl_packet *)
+			&packet->packet_data [size];
+		header->packet_count--;
+
+		// shift the data pointer to the next gl_packet
+		skb_pull (skb, size + 4);
+	}
+
+	// skip the packet length field 4 bytes
+	skb_pull (skb, 4);
+
+	if (skb->len > GL_MAX_PACKET_LEN) {
+		dbg ("genelink: invalid rx length %d", skb->len);
+		return 0;
+	}
+	return 1;
+}
+
+static struct sk_buff *
+genelink_tx_fixup (struct usbnet *dev, struct sk_buff *skb, int flags)
+{
+	int 	padlen;
+	int	length = skb->len;
+	int	headroom = skb_headroom (skb);
+	int	tailroom = skb_tailroom (skb);
+	u32	*packet_count;
+	u32	*packet_len;
+
+	// FIXME:  magic numbers, bleech
+	padlen = ((skb->len + (4 + 4*1)) % 64) ? 0 : 1;
+
+	if ((!skb_cloned (skb))
+			&& ((headroom + tailroom) >= (padlen + (4 + 4*1)))) {
+		if ((headroom < (4 + 4*1)) || (tailroom < padlen)) {
+			skb->data = memmove (skb->head + (4 + 4*1),
+					     skb->data, skb->len);
+			skb->tail = skb->data + skb->len;
+		}
+	} else {
+		struct sk_buff	*skb2;
+		skb2 = skb_copy_expand (skb, (4 + 4*1) , padlen, flags);
+		dev_kfree_skb_any (skb);
+		skb = skb2;
+		if (!skb)
+			return NULL;
+	}
+
+	// attach the packet count to the header
+	packet_count = (u32 *) skb_push (skb, (4 + 4*1));
+	packet_len = packet_count + 1;
+
+	// FIXME little endian?
+	*packet_count = 1;
+	*packet_len = length;
+
+	// add padding byte
+	if ((skb->len % dev->maxpacket) == 0)
+		skb_put (skb, 1);
+
+	return skb;
+}
+
+static const struct driver_info	genelink_info = {
+	.description =	"Genesys GeneLink",
+	.flags =	FLAG_FRAMING_GL | FLAG_NO_SETINT,
+	.rx_fixup =	genelink_rx_fixup,
+	.tx_fixup =	genelink_tx_fixup,
+
+	.in = 1, .out = 2,
+
+#ifdef	GENELINK_ACK
+	.check_connect =genelink_check_connect,
+#endif
+};
+
+#endif /* CONFIG_USB_GENESYS */
+
+
+
+#ifdef	CONFIG_USB_NET1080
+#define	HAVE_HARDWARE
+
+/*-------------------------------------------------------------------------
+ *
+ * Netchip 1080 driver ... http://www.netchip.com
+ * Used in LapLink cables
+ *
+ *-------------------------------------------------------------------------*/
+
+#define dev_packet_id	data[0]
+#define frame_errors	data[1]
+
+/*
+ * NetChip framing of ethernet packets, supporting additional error
+ * checks for links that may drop bulk packets from inside messages.
+ * Odd USB length == always short read for last usb packet.
+ *	- nc_header
+ *	- Ethernet header (14 bytes)
+ *	- payload
+ *	- (optional padding byte, if needed so length becomes odd)
+ *	- nc_trailer
+ *
+ * This framing is to be avoided for non-NetChip devices.
+ */
+
+struct nc_header {		// packed:
+	__le16	hdr_len;		// sizeof nc_header (LE, all)
+	__le16	packet_len;		// payload size (including ethhdr)
+	__le16	packet_id;		// detects dropped packets
+#define MIN_HEADER	6
+
+	// all else is optional, and must start with:
+	// u16	vendorId;		// from usb-if
+	// u16	productId;
+} __attribute__((__packed__));
+
+#define	PAD_BYTE	((unsigned char)0xAC)
+
+struct nc_trailer {
+	__le16	packet_id;
+} __attribute__((__packed__));
+
+// packets may use FLAG_FRAMING_NC and optional pad
+#define FRAMED_SIZE(mtu) (sizeof (struct nc_header) \
+				+ sizeof (struct ethhdr) \
+				+ (mtu) \
+				+ 1 \
+				+ sizeof (struct nc_trailer))
+
+#define MIN_FRAMED	FRAMED_SIZE(0)
+
+
+/*
+ * Zero means no timeout; else, how long a 64 byte bulk packet may be queued
+ * before the hardware drops it.  If that's done, the driver will need to
+ * frame network packets to guard against the dropped USB packets.  The win32
+ * driver sets this for both sides of the link.
+ */
+#define	NC_READ_TTL_MS	((u8)255)	// ms
+
+/*
+ * We ignore most registers and EEPROM contents.
+ */
+#define	REG_USBCTL	((u8)0x04)
+#define REG_TTL		((u8)0x10)
+#define REG_STATUS	((u8)0x11)
+
+/*
+ * Vendor specific requests to read/write data
+ */
+#define	REQUEST_REGISTER	((u8)0x10)
+#define	REQUEST_EEPROM		((u8)0x11)
+
+static int
+nc_vendor_read (struct usbnet *dev, u8 req, u8 regnum, u16 *retval_ptr)
+{
+	int status = usb_control_msg (dev->udev,
+		usb_rcvctrlpipe (dev->udev, 0),
+		req,
+		USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+		0, regnum,
+		retval_ptr, sizeof *retval_ptr,
+		CONTROL_TIMEOUT_MS);
+	if (status > 0)
+		status = 0;
+	if (!status)
+		le16_to_cpus (retval_ptr);
+	return status;
+}
+
+static inline int
+nc_register_read (struct usbnet *dev, u8 regnum, u16 *retval_ptr)
+{
+	return nc_vendor_read (dev, REQUEST_REGISTER, regnum, retval_ptr);
+}
+
+// no retval ... can become async, usable in_interrupt()
+static void
+nc_vendor_write (struct usbnet *dev, u8 req, u8 regnum, u16 value)
+{
+	usb_control_msg (dev->udev,
+		usb_sndctrlpipe (dev->udev, 0),
+		req,
+		USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+		value, regnum,
+		NULL, 0,			// data is in setup packet
+		CONTROL_TIMEOUT_MS);
+}
+
+static inline void
+nc_register_write (struct usbnet *dev, u8 regnum, u16 value)
+{
+	nc_vendor_write (dev, REQUEST_REGISTER, regnum, value);
+}
+
+
+#if 0
+static void nc_dump_registers (struct usbnet *dev)
+{
+	u8	reg;
+	u16	*vp = kmalloc (sizeof (u16));
+
+	if (!vp) {
+		dbg ("no memory?");
+		return;
+	}
+
+	dbg ("%s registers:", dev->net->name);
+	for (reg = 0; reg < 0x20; reg++) {
+		int retval;
+
+		// reading some registers is trouble
+		if (reg >= 0x08 && reg <= 0xf)
+			continue;
+		if (reg >= 0x12 && reg <= 0x1e)
+			continue;
+
+		retval = nc_register_read (dev, reg, vp);
+		if (retval < 0)
+			dbg ("%s reg [0x%x] ==> error %d",
+				dev->net->name, reg, retval);
+		else
+			dbg ("%s reg [0x%x] = 0x%x",
+				dev->net->name, reg, *vp);
+	}
+	kfree (vp);
+}
+#endif
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Control register
+ */
+
+#define	USBCTL_WRITABLE_MASK	0x1f0f
+// bits 15-13 reserved, r/o
+#define	USBCTL_ENABLE_LANG	(1 << 12)
+#define	USBCTL_ENABLE_MFGR	(1 << 11)
+#define	USBCTL_ENABLE_PROD	(1 << 10)
+#define	USBCTL_ENABLE_SERIAL	(1 << 9)
+#define	USBCTL_ENABLE_DEFAULTS	(1 << 8)
+// bits 7-4 reserved, r/o
+#define	USBCTL_FLUSH_OTHER	(1 << 3)
+#define	USBCTL_FLUSH_THIS	(1 << 2)
+#define	USBCTL_DISCONN_OTHER	(1 << 1)
+#define	USBCTL_DISCONN_THIS	(1 << 0)
+
+static inline void nc_dump_usbctl (struct usbnet *dev, u16 usbctl)
+{
+	if (!netif_msg_link (dev))
+		return;
+	devdbg (dev, "net1080 %s-%s usbctl 0x%x:%s%s%s%s%s;"
+			" this%s%s;"
+			" other%s%s; r/o 0x%x",
+		dev->udev->bus->bus_name, dev->udev->devpath,
+		usbctl,
+		(usbctl & USBCTL_ENABLE_LANG) ? " lang" : "",
+		(usbctl & USBCTL_ENABLE_MFGR) ? " mfgr" : "",
+		(usbctl & USBCTL_ENABLE_PROD) ? " prod" : "",
+		(usbctl & USBCTL_ENABLE_SERIAL) ? " serial" : "",
+		(usbctl & USBCTL_ENABLE_DEFAULTS) ? " defaults" : "",
+
+		(usbctl & USBCTL_FLUSH_OTHER) ? " FLUSH" : "",
+		(usbctl & USBCTL_DISCONN_OTHER) ? " DIS" : "",
+		(usbctl & USBCTL_FLUSH_THIS) ? " FLUSH" : "",
+		(usbctl & USBCTL_DISCONN_THIS) ? " DIS" : "",
+		usbctl & ~USBCTL_WRITABLE_MASK
+		);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Status register
+ */
+
+#define	STATUS_PORT_A		(1 << 15)
+
+#define	STATUS_CONN_OTHER	(1 << 14)
+#define	STATUS_SUSPEND_OTHER	(1 << 13)
+#define	STATUS_MAILBOX_OTHER	(1 << 12)
+#define	STATUS_PACKETS_OTHER(n)	(((n) >> 8) && 0x03)
+
+#define	STATUS_CONN_THIS	(1 << 6)
+#define	STATUS_SUSPEND_THIS	(1 << 5)
+#define	STATUS_MAILBOX_THIS	(1 << 4)
+#define	STATUS_PACKETS_THIS(n)	(((n) >> 0) && 0x03)
+
+#define	STATUS_UNSPEC_MASK	0x0c8c
+#define	STATUS_NOISE_MASK 	((u16)~(0x0303|STATUS_UNSPEC_MASK))
+
+
+static inline void nc_dump_status (struct usbnet *dev, u16 status)
+{
+	if (!netif_msg_link (dev))
+		return;
+	devdbg (dev, "net1080 %s-%s status 0x%x:"
+			" this (%c) PKT=%d%s%s%s;"
+			" other PKT=%d%s%s%s; unspec 0x%x",
+		dev->udev->bus->bus_name, dev->udev->devpath,
+		status,
+
+		// XXX the packet counts don't seem right
+		// (1 at reset, not 0); maybe UNSPEC too
+
+		(status & STATUS_PORT_A) ? 'A' : 'B',
+		STATUS_PACKETS_THIS (status),
+		(status & STATUS_CONN_THIS) ? " CON" : "",
+		(status & STATUS_SUSPEND_THIS) ? " SUS" : "",
+		(status & STATUS_MAILBOX_THIS) ? " MBOX" : "",
+
+		STATUS_PACKETS_OTHER (status),
+		(status & STATUS_CONN_OTHER) ? " CON" : "",
+		(status & STATUS_SUSPEND_OTHER) ? " SUS" : "",
+		(status & STATUS_MAILBOX_OTHER) ? " MBOX" : "",
+
+		status & STATUS_UNSPEC_MASK
+		);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * TTL register
+ */
+
+#define	TTL_THIS(ttl)	(0x00ff & ttl)
+#define	TTL_OTHER(ttl)	(0x00ff & (ttl >> 8))
+#define MK_TTL(this,other)	((u16)(((other)<<8)|(0x00ff&(this))))
+
+static inline void nc_dump_ttl (struct usbnet *dev, u16 ttl)
+{
+	if (netif_msg_link (dev))
+		devdbg (dev, "net1080 %s-%s ttl 0x%x this = %d, other = %d",
+			dev->udev->bus->bus_name, dev->udev->devpath,
+			ttl, TTL_THIS (ttl), TTL_OTHER (ttl));
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int net1080_reset (struct usbnet *dev)
+{
+	u16		usbctl, status, ttl;
+	u16		*vp = kmalloc (sizeof (u16), GFP_KERNEL);
+	int		retval;
+
+	if (!vp)
+		return -ENOMEM;
+
+	// nc_dump_registers (dev);
+
+	if ((retval = nc_register_read (dev, REG_STATUS, vp)) < 0) {
+		dbg ("can't read %s-%s status: %d",
+			dev->udev->bus->bus_name, dev->udev->devpath, retval);
+		goto done;
+	}
+	status = *vp;
+	nc_dump_status (dev, status);
+
+	if ((retval = nc_register_read (dev, REG_USBCTL, vp)) < 0) {
+		dbg ("can't read USBCTL, %d", retval);
+		goto done;
+	}
+	usbctl = *vp;
+	nc_dump_usbctl (dev, usbctl);
+
+	nc_register_write (dev, REG_USBCTL,
+			USBCTL_FLUSH_THIS | USBCTL_FLUSH_OTHER);
+
+	if ((retval = nc_register_read (dev, REG_TTL, vp)) < 0) {
+		dbg ("can't read TTL, %d", retval);
+		goto done;
+	}
+	ttl = *vp;
+	// nc_dump_ttl (dev, ttl);
+
+	nc_register_write (dev, REG_TTL,
+			MK_TTL (NC_READ_TTL_MS, TTL_OTHER (ttl)) );
+	dbg ("%s: assigned TTL, %d ms", dev->net->name, NC_READ_TTL_MS);
+
+	if (netif_msg_link (dev))
+		devinfo (dev, "port %c, peer %sconnected",
+			(status & STATUS_PORT_A) ? 'A' : 'B',
+			(status & STATUS_CONN_OTHER) ? "" : "dis"
+			);
+	retval = 0;
+
+done:
+	kfree (vp);
+	return retval;
+}
+
+static int net1080_check_connect (struct usbnet *dev)
+{
+	int			retval;
+	u16			status;
+	u16			*vp = kmalloc (sizeof (u16), GFP_KERNEL);
+
+	if (!vp)
+		return -ENOMEM;
+	retval = nc_register_read (dev, REG_STATUS, vp);
+	status = *vp;
+	kfree (vp);
+	if (retval != 0) {
+		dbg ("%s net1080_check_conn read - %d", dev->net->name, retval);
+		return retval;
+	}
+	if ((status & STATUS_CONN_OTHER) != STATUS_CONN_OTHER)
+		return -ENOLINK;
+	return 0;
+}
+
+static void nc_flush_complete (struct urb *urb, struct pt_regs *regs)
+{
+	kfree (urb->context);
+	usb_free_urb(urb);
+}
+
+static void nc_ensure_sync (struct usbnet *dev)
+{
+	dev->frame_errors++;
+	if (dev->frame_errors > 5) {
+		struct urb		*urb;
+		struct usb_ctrlrequest	*req;
+		int			status;
+
+		/* Send a flush */
+		urb = usb_alloc_urb (0, SLAB_ATOMIC);
+		if (!urb)
+			return;
+
+		req = kmalloc (sizeof *req, GFP_ATOMIC);
+		if (!req) {
+			usb_free_urb (urb);
+			return;
+		}
+
+		req->bRequestType = USB_DIR_OUT
+			| USB_TYPE_VENDOR
+			| USB_RECIP_DEVICE;
+		req->bRequest = REQUEST_REGISTER;
+		req->wValue = cpu_to_le16 (USBCTL_FLUSH_THIS
+				| USBCTL_FLUSH_OTHER);
+		req->wIndex = cpu_to_le16 (REG_USBCTL);
+		req->wLength = cpu_to_le16 (0);
+
+		/* queue an async control request, we don't need
+		 * to do anything when it finishes except clean up.
+		 */
+		usb_fill_control_urb (urb, dev->udev,
+			usb_sndctrlpipe (dev->udev, 0),
+			(unsigned char *) req,
+			NULL, 0,
+			nc_flush_complete, req);
+		status = usb_submit_urb (urb, GFP_ATOMIC);
+		if (status) {
+			kfree (req);
+			usb_free_urb (urb);
+			return;
+		}
+
+		if (netif_msg_rx_err (dev))
+			devdbg (dev, "flush net1080; too many framing errors");
+		dev->frame_errors = 0;
+	}
+}
+
+static int net1080_rx_fixup (struct usbnet *dev, struct sk_buff *skb)
+{
+	struct nc_header	*header;
+	struct nc_trailer	*trailer;
+	u16			hdr_len, packet_len;
+
+	if (!(skb->len & 0x01)
+			|| MIN_FRAMED > skb->len
+			|| skb->len > FRAMED_SIZE (dev->net->mtu)) {
+		dev->stats.rx_frame_errors++;
+		dbg ("rx framesize %d range %d..%d mtu %d", skb->len,
+			(int)MIN_FRAMED, (int)FRAMED_SIZE (dev->net->mtu),
+			dev->net->mtu);
+		nc_ensure_sync (dev);
+		return 0;
+	}
+
+	header = (struct nc_header *) skb->data;
+	hdr_len = le16_to_cpup (&header->hdr_len);
+	packet_len = le16_to_cpup (&header->packet_len);
+	if (FRAMED_SIZE (packet_len) > MAX_PACKET) {
+		dev->stats.rx_frame_errors++;
+		dbg ("packet too big, %d", packet_len);
+		nc_ensure_sync (dev);
+		return 0;
+	} else if (hdr_len < MIN_HEADER) {
+		dev->stats.rx_frame_errors++;
+		dbg ("header too short, %d", hdr_len);
+		nc_ensure_sync (dev);
+		return 0;
+	} else if (hdr_len > MIN_HEADER) {
+		// out of band data for us?
+		dbg ("header OOB, %d bytes", hdr_len - MIN_HEADER);
+		nc_ensure_sync (dev);
+		// switch (vendor/product ids) { ... }
+	}
+	skb_pull (skb, hdr_len);
+
+	trailer = (struct nc_trailer *)
+		(skb->data + skb->len - sizeof *trailer);
+	skb_trim (skb, skb->len - sizeof *trailer);
+
+	if ((packet_len & 0x01) == 0) {
+		if (skb->data [packet_len] != PAD_BYTE) {
+			dev->stats.rx_frame_errors++;
+			dbg ("bad pad");
+			return 0;
+		}
+		skb_trim (skb, skb->len - 1);
+	}
+	if (skb->len != packet_len) {
+		dev->stats.rx_frame_errors++;
+		dbg ("bad packet len %d (expected %d)",
+			skb->len, packet_len);
+		nc_ensure_sync (dev);
+		return 0;
+	}
+	if (header->packet_id != get_unaligned (&trailer->packet_id)) {
+		dev->stats.rx_fifo_errors++;
+		dbg ("(2+ dropped) rx packet_id mismatch 0x%x 0x%x",
+			le16_to_cpu (header->packet_id),
+			le16_to_cpu (trailer->packet_id));
+		return 0;
+	}
+#if 0
+	devdbg (dev, "frame <rx h %d p %d id %d", header->hdr_len,
+		header->packet_len, header->packet_id);
+#endif
+	dev->frame_errors = 0;
+	return 1;
+}
+
+static struct sk_buff *
+net1080_tx_fixup (struct usbnet *dev, struct sk_buff *skb, int flags)
+{
+	int			padlen;
+	struct sk_buff		*skb2;
+
+	padlen = ((skb->len + sizeof (struct nc_header)
+			+ sizeof (struct nc_trailer)) & 0x01) ? 0 : 1;
+	if (!skb_cloned (skb)) {
+		int	headroom = skb_headroom (skb);
+		int	tailroom = skb_tailroom (skb);
+
+		if ((padlen + sizeof (struct nc_trailer)) <= tailroom
+			    && sizeof (struct nc_header) <= headroom)
+			/* There's enough head and tail room */
+			return skb;
+
+		if ((sizeof (struct nc_header) + padlen
+					+ sizeof (struct nc_trailer)) <
+				(headroom + tailroom)) {
+			/* There's enough total room, so just readjust */
+			skb->data = memmove (skb->head
+						+ sizeof (struct nc_header),
+					    skb->data, skb->len);
+			skb->tail = skb->data + skb->len;
+			return skb;
+		}
+	}
+
+	/* Create a new skb to use with the correct size */
+	skb2 = skb_copy_expand (skb,
+				sizeof (struct nc_header),
+				sizeof (struct nc_trailer) + padlen,
+				flags);
+	dev_kfree_skb_any (skb);
+	return skb2;
+}
+
+static const struct driver_info	net1080_info = {
+	.description =	"NetChip TurboCONNECT",
+	.flags =	FLAG_FRAMING_NC,
+	.reset =	net1080_reset,
+	.check_connect =net1080_check_connect,
+	.rx_fixup =	net1080_rx_fixup,
+	.tx_fixup =	net1080_tx_fixup,
+};
+
+#endif /* CONFIG_USB_NET1080 */
+
+
+
+#ifdef CONFIG_USB_PL2301
+#define	HAVE_HARDWARE
+
+/*-------------------------------------------------------------------------
+ *
+ * Prolific PL-2301/PL-2302 driver ... http://www.prolifictech.com
+ *
+ * The protocol and handshaking used here should be bug-compatible
+ * with the Linux 2.2 "plusb" driver, by Deti Fliegl.
+ *
+ *-------------------------------------------------------------------------*/
+
+/*
+ * Bits 0-4 can be used for software handshaking; they're set from
+ * one end, cleared from the other, "read" with the interrupt byte.
+ */
+#define	PL_S_EN		(1<<7)		/* (feature only) suspend enable */
+/* reserved bit -- rx ready (6) ? */
+#define	PL_TX_READY	(1<<5)		/* (interrupt only) transmit ready */
+#define	PL_RESET_OUT	(1<<4)		/* reset output pipe */
+#define	PL_RESET_IN	(1<<3)		/* reset input pipe */
+#define	PL_TX_C		(1<<2)		/* transmission complete */
+#define	PL_TX_REQ	(1<<1)		/* transmission received */
+#define	PL_PEER_E	(1<<0)		/* peer exists */
+
+static inline int
+pl_vendor_req (struct usbnet *dev, u8 req, u8 val, u8 index)
+{
+	return usb_control_msg (dev->udev,
+		usb_rcvctrlpipe (dev->udev, 0),
+		req,
+		USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+		val, index,
+		NULL, 0,
+		CONTROL_TIMEOUT_MS);
+}
+
+static inline int
+pl_clear_QuickLink_features (struct usbnet *dev, int val)
+{
+	return pl_vendor_req (dev, 1, (u8) val, 0);
+}
+
+static inline int
+pl_set_QuickLink_features (struct usbnet *dev, int val)
+{
+	return pl_vendor_req (dev, 3, (u8) val, 0);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int pl_reset (struct usbnet *dev)
+{
+	/* some units seem to need this reset, others reject it utterly.
+	 * FIXME be more like "naplink" or windows drivers.
+	 */
+	(void) pl_set_QuickLink_features (dev,
+		PL_S_EN|PL_RESET_OUT|PL_RESET_IN|PL_PEER_E);
+	return 0;
+}
+
+static const struct driver_info	prolific_info = {
+	.description =	"Prolific PL-2301/PL-2302",
+	.flags =	FLAG_NO_SETINT,
+		/* some PL-2302 versions seem to fail usb_set_interface() */
+	.reset =	pl_reset,
+};
+
+#endif /* CONFIG_USB_PL2301 */
+
+
+#ifdef CONFIG_USB_KC2190
+#define HAVE_HARDWARE
+static const struct driver_info kc2190_info = {
+	.description =  "KC Technology KC-190",
+};
+#endif /* CONFIG_USB_KC2190 */
+
+
+#ifdef	CONFIG_USB_ARMLINUX
+#define	HAVE_HARDWARE
+
+/*-------------------------------------------------------------------------
+ *
+ * Intel's SA-1100 chip integrates basic USB support, and is used
+ * in PDAs like some iPaqs, the Yopy, some Zaurus models, and more.
+ * When they run Linux, arch/arm/mach-sa1100/usb-eth.c may be used to
+ * network using minimal USB framing data.
+ *
+ * This describes the driver currently in standard ARM Linux kernels.
+ * The Zaurus uses a different driver (see later).
+ *
+ * PXA25x and PXA210 use XScale cores (ARM v5TE) with better USB support
+ * and different USB endpoint numbering than the SA1100 devices.  The
+ * mach-pxa/usb-eth.c driver re-uses the device ids from mach-sa1100
+ * so we rely on the endpoint descriptors.
+ *
+ *-------------------------------------------------------------------------*/
+
+static const struct driver_info	linuxdev_info = {
+	.description =	"Linux Device",
+	.check_connect = always_connected,
+};
+
+static const struct driver_info	yopy_info = {
+	.description =	"Yopy",
+	.check_connect = always_connected,
+};
+
+static const struct driver_info	blob_info = {
+	.description =	"Boot Loader OBject",
+	.check_connect = always_connected,
+};
+
+#endif	/* CONFIG_USB_ARMLINUX */
+
+
+#ifdef CONFIG_USB_ZAURUS
+#define	HAVE_HARDWARE
+
+#include <linux/crc32.h>
+
+/*-------------------------------------------------------------------------
+ *
+ * Zaurus is also a SA-1110 based PDA, but one using a different driver
+ * (and framing) for its USB slave/gadget controller than the case above.
+ *
+ * For the current version of that driver, the main way that framing is
+ * nonstandard (also from perspective of the CDC ethernet model!) is a
+ * crc32, added to help detect when some sa1100 usb-to-memory DMA errata
+ * haven't been fully worked around.  Also, all Zaurii use the same
+ * default Ethernet address.
+ *
+ * PXA based models use the same framing, and also can't implement
+ * set_interface properly.
+ *
+ * All known Zaurii lie about their standards conformance.  Most lie by
+ * saying they support CDC Ethernet.  Some lie and say they support CDC
+ * MDLM (as if for access to cell phone modems).  Someone, please beat 
+ * on Sharp for a while with a cluestick.
+ *
+ *-------------------------------------------------------------------------*/
+
+static struct sk_buff *
+zaurus_tx_fixup (struct usbnet *dev, struct sk_buff *skb, int flags)
+{
+	int			padlen;
+	struct sk_buff		*skb2;
+
+	padlen = 2;
+	if (!skb_cloned (skb)) {
+		int	tailroom = skb_tailroom (skb);
+		if ((padlen + 4) <= tailroom)
+			goto done;
+	}
+	skb2 = skb_copy_expand (skb, 0, 4 + padlen, flags);
+	dev_kfree_skb_any (skb);
+	skb = skb2;
+	if (skb) {
+		u32		fcs;
+done:
+		fcs = crc32_le (~0, skb->data, skb->len);
+		fcs = ~fcs;
+
+		*skb_put (skb, 1) = fcs       & 0xff;
+		*skb_put (skb, 1) = (fcs>> 8) & 0xff;
+		*skb_put (skb, 1) = (fcs>>16) & 0xff;
+		*skb_put (skb, 1) = (fcs>>24) & 0xff;
+	}
+	return skb;
+}
+
+static const struct driver_info	zaurus_sl5x00_info = {
+	.description =	"Sharp Zaurus SL-5x00",
+	.flags =	FLAG_FRAMING_Z,
+	.check_connect = always_connected,
+	.bind =		generic_cdc_bind,
+	.unbind =	cdc_unbind,
+	.tx_fixup = 	zaurus_tx_fixup,
+};
+#define	ZAURUS_STRONGARM_INFO	((unsigned long)&zaurus_sl5x00_info)
+
+static const struct driver_info	zaurus_pxa_info = {
+	.description =	"Sharp Zaurus, PXA-2xx based",
+	.flags =	FLAG_FRAMING_Z,
+	.check_connect = always_connected,
+	.bind =		generic_cdc_bind,
+	.unbind =	cdc_unbind,
+	.tx_fixup = 	zaurus_tx_fixup,
+};
+#define	ZAURUS_PXA_INFO		((unsigned long)&zaurus_pxa_info)
+
+static const struct driver_info	zaurus_pxa_mdlm_info = {
+	.description =	"Sharp Zaurus, PXA-255 based",
+	.flags =	FLAG_FRAMING_Z,
+	.check_connect = always_connected,
+	.tx_fixup = 	zaurus_tx_fixup,
+};
+
+static const struct driver_info	olympus_mxl_info = {
+	.description =	"Olympus R1000",
+	.flags =	FLAG_FRAMING_Z,
+	.check_connect = always_connected,
+	.bind =		generic_cdc_bind,
+	.unbind =	cdc_unbind,
+	.tx_fixup = 	zaurus_tx_fixup,
+};
+#define	OLYMPUS_MXL_INFO	((unsigned long)&olympus_mxl_info)
+
+#else
+
+/* blacklist all those devices */
+#define	ZAURUS_STRONGARM_INFO	0
+#define	ZAURUS_PXA_INFO		0
+#define	OLYMPUS_MXL_INFO	0
+
+#endif
+
+
+/*-------------------------------------------------------------------------
+ *
+ * Network Device Driver (peer link to "Host Device", from USB host)
+ *
+ *-------------------------------------------------------------------------*/
+
+static int usbnet_change_mtu (struct net_device *net, int new_mtu)
+{
+	struct usbnet	*dev = netdev_priv(net);
+
+	if (new_mtu <= MIN_PACKET || new_mtu > MAX_PACKET)
+		return -EINVAL;
+#ifdef	CONFIG_USB_NET1080
+	if (((dev->driver_info->flags) & FLAG_FRAMING_NC)) {
+		if (FRAMED_SIZE (new_mtu) > MAX_PACKET)
+			return -EINVAL;
+	}
+#endif
+#ifdef	CONFIG_USB_GENESYS
+	if (((dev->driver_info->flags) & FLAG_FRAMING_GL)
+			&& new_mtu > GL_MAX_PACKET_LEN)
+		return -EINVAL;
+#endif
+	// no second zero-length packet read wanted after mtu-sized packets
+	if (((new_mtu + sizeof (struct ethhdr)) % dev->maxpacket) == 0)
+		return -EDOM;
+	net->mtu = new_mtu;
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct net_device_stats *usbnet_get_stats (struct net_device *net)
+{
+	struct usbnet	*dev = netdev_priv(net);
+	return &dev->stats;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* some LK 2.4 HCDs oopsed if we freed or resubmitted urbs from
+ * completion callbacks.  2.5 should have fixed those bugs...
+ */
+
+static void defer_bh (struct usbnet *dev, struct sk_buff *skb)
+{
+	struct sk_buff_head	*list = skb->list;
+	unsigned long		flags;
+
+	spin_lock_irqsave (&list->lock, flags);
+	__skb_unlink (skb, list);
+	spin_unlock (&list->lock);
+	spin_lock (&dev->done.lock);
+	__skb_queue_tail (&dev->done, skb);
+	if (dev->done.qlen == 1)
+		tasklet_schedule (&dev->bh);
+	spin_unlock_irqrestore (&dev->done.lock, flags);
+}
+
+/* some work can't be done in tasklets, so we use keventd
+ *
+ * NOTE:  annoying asymmetry:  if it's active, schedule_work() fails,
+ * but tasklet_schedule() doesn't.  hope the failure is rare.
+ */
+static void defer_kevent (struct usbnet *dev, int work)
+{
+	set_bit (work, &dev->flags);
+	if (!schedule_work (&dev->kevent))
+		deverr (dev, "kevent %d may have been dropped", work);
+	else
+		devdbg (dev, "kevent %d scheduled", work);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void rx_complete (struct urb *urb, struct pt_regs *regs);
+
+static void rx_submit (struct usbnet *dev, struct urb *urb, int flags)
+{
+	struct sk_buff		*skb;
+	struct skb_data		*entry;
+	int			retval = 0;
+	unsigned long		lockflags;
+	size_t			size;
+
+#ifdef CONFIG_USB_NET1080
+	if (dev->driver_info->flags & FLAG_FRAMING_NC)
+		size = FRAMED_SIZE (dev->net->mtu);
+	else
+#endif
+#ifdef CONFIG_USB_GENESYS
+	if (dev->driver_info->flags & FLAG_FRAMING_GL)
+		size = GL_RCV_BUF_SIZE;
+	else
+#endif
+#ifdef CONFIG_USB_ZAURUS
+	if (dev->driver_info->flags & FLAG_FRAMING_Z)
+		size = 6 + (sizeof (struct ethhdr) + dev->net->mtu);
+	else
+#endif
+#ifdef CONFIG_USB_RNDIS
+	if (dev->driver_info->flags & FLAG_FRAMING_RN)
+		size = RNDIS_MAX_TRANSFER;
+	else
+#endif
+#ifdef CONFIG_USB_AX8817X
+	if (dev->driver_info->flags & FLAG_FRAMING_AX)
+		size = 2048;
+	else
+#endif
+		size = (sizeof (struct ethhdr) + dev->net->mtu);
+
+	if ((skb = alloc_skb (size + NET_IP_ALIGN, flags)) == NULL) {
+		if (netif_msg_rx_err (dev))
+			devdbg (dev, "no rx skb");
+		defer_kevent (dev, EVENT_RX_MEMORY);
+		usb_free_urb (urb);
+		return;
+	}
+	skb_reserve (skb, NET_IP_ALIGN);
+
+	entry = (struct skb_data *) skb->cb;
+	entry->urb = urb;
+	entry->dev = dev;
+	entry->state = rx_start;
+	entry->length = 0;
+
+	usb_fill_bulk_urb (urb, dev->udev, dev->in,
+		skb->data, size, rx_complete, skb);
+	urb->transfer_flags |= URB_ASYNC_UNLINK;
+
+	spin_lock_irqsave (&dev->rxq.lock, lockflags);
+
+	if (netif_running (dev->net)
+			&& netif_device_present (dev->net)
+			&& !test_bit (EVENT_RX_HALT, &dev->flags)) {
+		switch (retval = usb_submit_urb (urb, GFP_ATOMIC)){ 
+		case -EPIPE:
+			defer_kevent (dev, EVENT_RX_HALT);
+			break;
+		case -ENOMEM:
+			defer_kevent (dev, EVENT_RX_MEMORY);
+			break;
+		case -ENODEV:
+			if (netif_msg_ifdown (dev))
+				devdbg (dev, "device gone");
+			netif_device_detach (dev->net);
+			break;
+		default:
+			if (netif_msg_rx_err (dev))
+				devdbg (dev, "rx submit, %d", retval);
+			tasklet_schedule (&dev->bh);
+			break;
+		case 0:
+			__skb_queue_tail (&dev->rxq, skb);
+		}
+	} else {
+		if (netif_msg_ifdown (dev))
+			devdbg (dev, "rx: stopped");
+		retval = -ENOLINK;
+	}
+	spin_unlock_irqrestore (&dev->rxq.lock, lockflags);
+	if (retval) {
+		dev_kfree_skb_any (skb);
+		usb_free_urb (urb);
+	}
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static inline void rx_process (struct usbnet *dev, struct sk_buff *skb)
+{
+	if (dev->driver_info->rx_fixup
+			&& !dev->driver_info->rx_fixup (dev, skb))
+		goto error;
+	// else network stack removes extra byte if we forced a short packet
+
+	if (skb->len)
+		skb_return (dev, skb);
+	else {
+		if (netif_msg_rx_err (dev))
+			devdbg (dev, "drop");
+error:
+		dev->stats.rx_errors++;
+		skb_queue_tail (&dev->done, skb);
+	}
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void rx_complete (struct urb *urb, struct pt_regs *regs)
+{
+	struct sk_buff		*skb = (struct sk_buff *) urb->context;
+	struct skb_data		*entry = (struct skb_data *) skb->cb;
+	struct usbnet		*dev = entry->dev;
+	int			urb_status = urb->status;
+
+	skb_put (skb, urb->actual_length);
+	entry->state = rx_done;
+	entry->urb = NULL;
+
+	switch (urb_status) {
+	    // success
+	    case 0:
+		if (MIN_PACKET > skb->len || skb->len > MAX_PACKET) {
+			entry->state = rx_cleanup;
+			dev->stats.rx_errors++;
+			dev->stats.rx_length_errors++;
+			if (netif_msg_rx_err (dev))
+				devdbg (dev, "rx length %d", skb->len);
+		}
+		break;
+
+	    // stalls need manual reset. this is rare ... except that
+	    // when going through USB 2.0 TTs, unplug appears this way.
+	    // we avoid the highspeed version of the ETIMEOUT/EILSEQ
+	    // storm, recovering as needed.
+	    case -EPIPE:
+		dev->stats.rx_errors++;
+		defer_kevent (dev, EVENT_RX_HALT);
+		// FALLTHROUGH
+
+	    // software-driven interface shutdown
+	    case -ECONNRESET:		// async unlink
+	    case -ESHUTDOWN:		// hardware gone
+		if (netif_msg_ifdown (dev))
+			devdbg (dev, "rx shutdown, code %d", urb_status);
+		goto block;
+
+	    // we get controller i/o faults during khubd disconnect() delays.
+	    // throttle down resubmits, to avoid log floods; just temporarily,
+	    // so we still recover when the fault isn't a khubd delay.
+	    case -EPROTO:		// ehci
+	    case -ETIMEDOUT:		// ohci
+	    case -EILSEQ:		// uhci
+		dev->stats.rx_errors++;
+		if (!timer_pending (&dev->delay)) {
+			mod_timer (&dev->delay, jiffies + THROTTLE_JIFFIES);
+			if (netif_msg_link (dev))
+				devdbg (dev, "rx throttle %d", urb_status);
+		}
+block:
+		entry->state = rx_cleanup;
+		entry->urb = urb;
+		urb = NULL;
+		break;
+
+	    // data overrun ... flush fifo?
+	    case -EOVERFLOW:
+		dev->stats.rx_over_errors++;
+		// FALLTHROUGH
+	    
+	    default:
+		entry->state = rx_cleanup;
+		dev->stats.rx_errors++;
+		if (netif_msg_rx_err (dev))
+			devdbg (dev, "rx status %d", urb_status);
+		break;
+	}
+
+	defer_bh (dev, skb);
+
+	if (urb) {
+		if (netif_running (dev->net)
+				&& !test_bit (EVENT_RX_HALT, &dev->flags)) {
+			rx_submit (dev, urb, GFP_ATOMIC);
+			return;
+		}
+		usb_free_urb (urb);
+	}
+	if (netif_msg_rx_err (dev))
+		devdbg (dev, "no read resubmitted");
+}
+
+static void intr_complete (struct urb *urb, struct pt_regs *regs)
+{
+	struct usbnet	*dev = urb->context;
+	int		status = urb->status;
+
+	switch (status) {
+	    /* success */
+	    case 0:
+		dev->driver_info->status(dev, urb);
+		break;
+
+	    /* software-driven interface shutdown */
+	    case -ENOENT:		// urb killed
+	    case -ESHUTDOWN:		// hardware gone
+		if (netif_msg_ifdown (dev))
+			devdbg (dev, "intr shutdown, code %d", status);
+		return;
+
+	    /* NOTE:  not throttling like RX/TX, since this endpoint
+	     * already polls infrequently
+	     */
+	    default:
+		devdbg (dev, "intr status %d", status);
+		break;
+	}
+
+	if (!netif_running (dev->net))
+		return;
+
+	memset(urb->transfer_buffer, 0, urb->transfer_buffer_length);
+	status = usb_submit_urb (urb, GFP_ATOMIC);
+	if (status != 0 && netif_msg_timer (dev))
+		deverr(dev, "intr resubmit --> %d", status);
+}
+
+/*-------------------------------------------------------------------------*/
+
+// unlink pending rx/tx; completion handlers do all other cleanup
+
+static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
+{
+	unsigned long		flags;
+	struct sk_buff		*skb, *skbnext;
+	int			count = 0;
+
+	spin_lock_irqsave (&q->lock, flags);
+	for (skb = q->next; skb != (struct sk_buff *) q; skb = skbnext) {
+		struct skb_data		*entry;
+		struct urb		*urb;
+		int			retval;
+
+		entry = (struct skb_data *) skb->cb;
+		urb = entry->urb;
+		skbnext = skb->next;
+
+		// during some PM-driven resume scenarios,
+		// these (async) unlinks complete immediately
+		retval = usb_unlink_urb (urb);
+		if (retval != -EINPROGRESS && retval != 0)
+			devdbg (dev, "unlink urb err, %d", retval);
+		else
+			count++;
+	}
+	spin_unlock_irqrestore (&q->lock, flags);
+	return count;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+// precondition: never called in_interrupt
+
+static int usbnet_stop (struct net_device *net)
+{
+	struct usbnet		*dev = netdev_priv(net);
+	int			temp;
+	DECLARE_WAIT_QUEUE_HEAD (unlink_wakeup); 
+	DECLARE_WAITQUEUE (wait, current);
+
+	netif_stop_queue (net);
+
+	if (netif_msg_ifdown (dev))
+		devinfo (dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld",
+			dev->stats.rx_packets, dev->stats.tx_packets, 
+			dev->stats.rx_errors, dev->stats.tx_errors
+			);
+
+	// ensure there are no more active urbs
+	add_wait_queue (&unlink_wakeup, &wait);
+	dev->wait = &unlink_wakeup;
+	temp = unlink_urbs (dev, &dev->txq) + unlink_urbs (dev, &dev->rxq);
+
+	// maybe wait for deletions to finish.
+	while (skb_queue_len (&dev->rxq)
+			&& skb_queue_len (&dev->txq)
+			&& skb_queue_len (&dev->done)) {
+		msleep(UNLINK_TIMEOUT_MS);
+		if (netif_msg_ifdown (dev))
+			devdbg (dev, "waited for %d urb completions", temp);
+	}
+	dev->wait = NULL;
+	remove_wait_queue (&unlink_wakeup, &wait); 
+
+	usb_kill_urb(dev->interrupt);
+
+	/* deferred work (task, timer, softirq) must also stop.
+	 * can't flush_scheduled_work() until we drop rtnl (later),
+	 * else workers could deadlock; so make workers a NOP.
+	 */
+	dev->flags = 0;
+	del_timer_sync (&dev->delay);
+	tasklet_kill (&dev->bh);
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+// posts reads, and enables write queuing
+
+// precondition: never called in_interrupt
+
+static int usbnet_open (struct net_device *net)
+{
+	struct usbnet		*dev = netdev_priv(net);
+	int			retval = 0;
+	struct driver_info	*info = dev->driver_info;
+
+	// put into "known safe" state
+	if (info->reset && (retval = info->reset (dev)) < 0) {
+		if (netif_msg_ifup (dev))
+			devinfo (dev,
+				"open reset fail (%d) usbnet usb-%s-%s, %s",
+				retval,
+				dev->udev->bus->bus_name, dev->udev->devpath,
+			info->description);
+		goto done;
+	}
+
+	// insist peer be connected
+	if (info->check_connect && (retval = info->check_connect (dev)) < 0) {
+		if (netif_msg_ifup (dev))
+			devdbg (dev, "can't open; %d", retval);
+		goto done;
+	}
+
+	/* start any status interrupt transfer */
+	if (dev->interrupt) {
+		retval = usb_submit_urb (dev->interrupt, GFP_KERNEL);
+		if (retval < 0) {
+			if (netif_msg_ifup (dev))
+				deverr (dev, "intr submit %d", retval);
+			goto done;
+		}
+	}
+
+	netif_start_queue (net);
+	if (netif_msg_ifup (dev)) {
+		char	*framing;
+
+		if (dev->driver_info->flags & FLAG_FRAMING_NC)
+			framing = "NetChip";
+		else if (dev->driver_info->flags & FLAG_FRAMING_GL)
+			framing = "GeneSys";
+		else if (dev->driver_info->flags & FLAG_FRAMING_Z)
+			framing = "Zaurus";
+		else if (dev->driver_info->flags & FLAG_FRAMING_RN)
+			framing = "RNDIS";
+		else if (dev->driver_info->flags & FLAG_FRAMING_AX)
+			framing = "ASIX";
+		else
+			framing = "simple";
+
+		devinfo (dev, "open: enable queueing "
+				"(rx %d, tx %d) mtu %d %s framing",
+			RX_QLEN (dev), TX_QLEN (dev), dev->net->mtu,
+			framing);
+	}
+
+	// delay posting reads until we're fully open
+	tasklet_schedule (&dev->bh);
+done:
+	return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void usbnet_get_drvinfo (struct net_device *net, struct ethtool_drvinfo *info)
+{
+	struct usbnet *dev = netdev_priv(net);
+
+	strncpy (info->driver, driver_name, sizeof info->driver);
+	strncpy (info->version, DRIVER_VERSION, sizeof info->version);
+	strncpy (info->fw_version, dev->driver_info->description,
+		sizeof info->fw_version);
+	usb_make_path (dev->udev, info->bus_info, sizeof info->bus_info);
+}
+
+static u32 usbnet_get_link (struct net_device *net)
+{
+	struct usbnet *dev = netdev_priv(net);
+
+	/* If a check_connect is defined, return it's results */
+	if (dev->driver_info->check_connect)
+		return dev->driver_info->check_connect (dev) == 0;
+
+	/* Otherwise, we're up to avoid breaking scripts */
+	return 1;
+}
+
+static u32 usbnet_get_msglevel (struct net_device *net)
+{
+	struct usbnet *dev = netdev_priv(net);
+
+	return dev->msg_enable;
+}
+
+static void usbnet_set_msglevel (struct net_device *net, u32 level)
+{
+	struct usbnet *dev = netdev_priv(net);
+
+	dev->msg_enable = level;
+}
+
+static int usbnet_ioctl (struct net_device *net, struct ifreq *rq, int cmd)
+{
+#ifdef NEED_MII
+	{
+	struct usbnet *dev = netdev_priv(net);
+
+	if (dev->mii.mdio_read != NULL && dev->mii.mdio_write != NULL)
+		return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
+	}
+#endif
+	return -EOPNOTSUPP;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* work that cannot be done in interrupt context uses keventd.
+ *
+ * NOTE:  with 2.5 we could do more of this using completion callbacks,
+ * especially now that control transfers can be queued.
+ */
+static void
+kevent (void *data)
+{
+	struct usbnet		*dev = data;
+	int			status;
+
+	/* usb_clear_halt() needs a thread context */
+	if (test_bit (EVENT_TX_HALT, &dev->flags)) {
+		unlink_urbs (dev, &dev->txq);
+		status = usb_clear_halt (dev->udev, dev->out);
+		if (status < 0 && status != -EPIPE) {
+			if (netif_msg_tx_err (dev))
+				deverr (dev, "can't clear tx halt, status %d",
+					status);
+		} else {
+			clear_bit (EVENT_TX_HALT, &dev->flags);
+			netif_wake_queue (dev->net);
+		}
+	}
+	if (test_bit (EVENT_RX_HALT, &dev->flags)) {
+		unlink_urbs (dev, &dev->rxq);
+		status = usb_clear_halt (dev->udev, dev->in);
+		if (status < 0 && status != -EPIPE) {
+			if (netif_msg_rx_err (dev))
+				deverr (dev, "can't clear rx halt, status %d",
+					status);
+		} else {
+			clear_bit (EVENT_RX_HALT, &dev->flags);
+			tasklet_schedule (&dev->bh);
+		}
+	}
+
+	/* tasklet could resubmit itself forever if memory is tight */
+	if (test_bit (EVENT_RX_MEMORY, &dev->flags)) {
+		struct urb	*urb = NULL;
+
+		if (netif_running (dev->net))
+			urb = usb_alloc_urb (0, GFP_KERNEL);
+		else
+			clear_bit (EVENT_RX_MEMORY, &dev->flags);
+		if (urb != NULL) {
+			clear_bit (EVENT_RX_MEMORY, &dev->flags);
+			rx_submit (dev, urb, GFP_KERNEL);
+			tasklet_schedule (&dev->bh);
+		}
+	}
+
+	if (dev->flags)
+		devdbg (dev, "kevent done, flags = 0x%lx",
+			dev->flags);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void tx_complete (struct urb *urb, struct pt_regs *regs)
+{
+	struct sk_buff		*skb = (struct sk_buff *) urb->context;
+	struct skb_data		*entry = (struct skb_data *) skb->cb;
+	struct usbnet		*dev = entry->dev;
+
+	if (urb->status == 0) {
+		dev->stats.tx_packets++;
+		dev->stats.tx_bytes += entry->length;
+	} else {
+		dev->stats.tx_errors++;
+
+		switch (urb->status) {
+		case -EPIPE:
+			defer_kevent (dev, EVENT_TX_HALT);
+			break;
+
+		/* software-driven interface shutdown */
+		case -ECONNRESET:		// async unlink
+		case -ESHUTDOWN:		// hardware gone
+			break;
+
+		// like rx, tx gets controller i/o faults during khubd delays
+		// and so it uses the same throttling mechanism.
+		case -EPROTO:		// ehci
+		case -ETIMEDOUT:	// ohci
+		case -EILSEQ:		// uhci
+			if (!timer_pending (&dev->delay)) {
+				mod_timer (&dev->delay,
+					jiffies + THROTTLE_JIFFIES);
+				if (netif_msg_link (dev))
+					devdbg (dev, "tx throttle %d",
+							urb->status);
+			}
+			netif_stop_queue (dev->net);
+			break;
+		default:
+			if (netif_msg_tx_err (dev))
+				devdbg (dev, "tx err %d", entry->urb->status);
+			break;
+		}
+	}
+
+	urb->dev = NULL;
+	entry->state = tx_done;
+	defer_bh (dev, skb);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void usbnet_tx_timeout (struct net_device *net)
+{
+	struct usbnet		*dev = netdev_priv(net);
+
+	unlink_urbs (dev, &dev->txq);
+	tasklet_schedule (&dev->bh);
+
+	// FIXME: device recovery -- reset?
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net)
+{
+	struct usbnet		*dev = netdev_priv(net);
+	int			length;
+	int			retval = NET_XMIT_SUCCESS;
+	struct urb		*urb = NULL;
+	struct skb_data		*entry;
+	struct driver_info	*info = dev->driver_info;
+	unsigned long		flags;
+#ifdef	CONFIG_USB_NET1080
+	struct nc_header	*header = NULL;
+	struct nc_trailer	*trailer = NULL;
+#endif	/* CONFIG_USB_NET1080 */
+
+	// some devices want funky USB-level framing, for
+	// win32 driver (usually) and/or hardware quirks
+	if (info->tx_fixup) {
+		skb = info->tx_fixup (dev, skb, GFP_ATOMIC);
+		if (!skb) {
+			if (netif_msg_tx_err (dev))
+				devdbg (dev, "can't tx_fixup skb");
+			goto drop;
+		}
+	}
+	length = skb->len;
+
+	if (!(urb = usb_alloc_urb (0, GFP_ATOMIC))) {
+		if (netif_msg_tx_err (dev))
+			devdbg (dev, "no urb");
+		goto drop;
+	}
+
+	entry = (struct skb_data *) skb->cb;
+	entry->urb = urb;
+	entry->dev = dev;
+	entry->state = tx_start;
+	entry->length = length;
+
+	// FIXME: reorganize a bit, so that fixup() fills out NetChip
+	// framing too. (Packet ID update needs the spinlock...)
+	// [ BETTER:  we already own net->xmit_lock, that's enough ]
+
+#ifdef	CONFIG_USB_NET1080
+	if (info->flags & FLAG_FRAMING_NC) {
+		header = (struct nc_header *) skb_push (skb, sizeof *header);
+		header->hdr_len = cpu_to_le16 (sizeof (*header));
+		header->packet_len = cpu_to_le16 (length);
+		if (!((skb->len + sizeof *trailer) & 0x01))
+			*skb_put (skb, 1) = PAD_BYTE;
+		trailer = (struct nc_trailer *) skb_put (skb, sizeof *trailer);
+	}
+#endif	/* CONFIG_USB_NET1080 */
+
+	usb_fill_bulk_urb (urb, dev->udev, dev->out,
+			skb->data, skb->len, tx_complete, skb);
+	urb->transfer_flags |= URB_ASYNC_UNLINK;
+
+	/* don't assume the hardware handles USB_ZERO_PACKET
+	 * NOTE:  strictly conforming cdc-ether devices should expect
+	 * the ZLP here, but ignore the one-byte packet.
+	 *
+	 * FIXME zero that byte, if it doesn't require a new skb.
+	 */
+	if ((length % dev->maxpacket) == 0)
+		urb->transfer_buffer_length++;
+
+	spin_lock_irqsave (&dev->txq.lock, flags);
+
+#ifdef	CONFIG_USB_NET1080
+	if (info->flags & FLAG_FRAMING_NC) {
+		header->packet_id = cpu_to_le16 ((u16)dev->dev_packet_id++);
+		put_unaligned (header->packet_id, &trailer->packet_id);
+#if 0
+		devdbg (dev, "frame >tx h %d p %d id %d",
+			header->hdr_len, header->packet_len,
+			header->packet_id);
+#endif
+	}
+#endif	/* CONFIG_USB_NET1080 */
+
+	switch ((retval = usb_submit_urb (urb, GFP_ATOMIC))) {
+	case -EPIPE:
+		netif_stop_queue (net);
+		defer_kevent (dev, EVENT_TX_HALT);
+		break;
+	default:
+		if (netif_msg_tx_err (dev))
+			devdbg (dev, "tx: submit urb err %d", retval);
+		break;
+	case 0:
+		net->trans_start = jiffies;
+		__skb_queue_tail (&dev->txq, skb);
+		if (dev->txq.qlen >= TX_QLEN (dev))
+			netif_stop_queue (net);
+	}
+	spin_unlock_irqrestore (&dev->txq.lock, flags);
+
+	if (retval) {
+		if (netif_msg_tx_err (dev))
+			devdbg (dev, "drop, code %d", retval);
+drop:
+		retval = NET_XMIT_SUCCESS;
+		dev->stats.tx_dropped++;
+		if (skb)
+			dev_kfree_skb_any (skb);
+		usb_free_urb (urb);
+	} else if (netif_msg_tx_queued (dev)) {
+		devdbg (dev, "> tx, len %d, type 0x%x",
+			length, skb->protocol);
+	}
+	return retval;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+// tasklet (work deferred from completions, in_irq) or timer
+
+static void usbnet_bh (unsigned long param)
+{
+	struct usbnet		*dev = (struct usbnet *) param;
+	struct sk_buff		*skb;
+	struct skb_data		*entry;
+
+	while ((skb = skb_dequeue (&dev->done))) {
+		entry = (struct skb_data *) skb->cb;
+		switch (entry->state) {
+		    case rx_done:
+			entry->state = rx_cleanup;
+			rx_process (dev, skb);
+			continue;
+		    case tx_done:
+		    case rx_cleanup:
+			usb_free_urb (entry->urb);
+			dev_kfree_skb (skb);
+			continue;
+		    default:
+			devdbg (dev, "bogus skb state %d", entry->state);
+		}
+	}
+
+	// waiting for all pending urbs to complete?
+	if (dev->wait) {
+		if ((dev->txq.qlen + dev->rxq.qlen + dev->done.qlen) == 0) {
+			wake_up (dev->wait);
+		}
+
+	// or are we maybe short a few urbs?
+	} else if (netif_running (dev->net)
+			&& netif_device_present (dev->net)
+			&& !timer_pending (&dev->delay)
+			&& !test_bit (EVENT_RX_HALT, &dev->flags)) {
+		int	temp = dev->rxq.qlen;
+		int	qlen = RX_QLEN (dev);
+
+		if (temp < qlen) {
+			struct urb	*urb;
+			int		i;
+
+			// don't refill the queue all at once
+			for (i = 0; i < 10 && dev->rxq.qlen < qlen; i++) {
+				urb = usb_alloc_urb (0, GFP_ATOMIC);
+				if (urb != NULL)
+					rx_submit (dev, urb, GFP_ATOMIC);
+			}
+			if (temp != dev->rxq.qlen && netif_msg_link (dev))
+				devdbg (dev, "rxqlen %d --> %d",
+						temp, dev->rxq.qlen);
+			if (dev->rxq.qlen < qlen)
+				tasklet_schedule (&dev->bh);
+		}
+		if (dev->txq.qlen < TX_QLEN (dev))
+			netif_wake_queue (dev->net);
+	}
+}
+
+
+
+/*-------------------------------------------------------------------------
+ *
+ * USB Device Driver support
+ *
+ *-------------------------------------------------------------------------*/
+ 
+// precondition: never called in_interrupt
+
+static void usbnet_disconnect (struct usb_interface *intf)
+{
+	struct usbnet		*dev;
+	struct usb_device	*xdev;
+	struct net_device	*net;
+
+	dev = usb_get_intfdata(intf);
+	usb_set_intfdata(intf, NULL);
+	if (!dev)
+		return;
+
+	xdev = interface_to_usbdev (intf);
+
+	if (netif_msg_probe (dev))
+		devinfo (dev, "unregister usbnet usb-%s-%s, %s",
+			xdev->bus->bus_name, xdev->devpath,
+			dev->driver_info->description);
+	
+	net = dev->net;
+	unregister_netdev (net);
+
+	/* we don't hold rtnl here ... */
+	flush_scheduled_work ();
+
+	if (dev->driver_info->unbind)
+		dev->driver_info->unbind (dev, intf);
+
+	free_netdev(net);
+	usb_put_dev (xdev);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static struct ethtool_ops usbnet_ethtool_ops;
+
+// precondition: never called in_interrupt
+
+static int
+usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
+{
+	struct usbnet			*dev;
+	struct net_device 		*net;
+	struct usb_host_interface	*interface;
+	struct driver_info		*info;
+	struct usb_device		*xdev;
+	int				status;
+
+	info = (struct driver_info *) prod->driver_info;
+	if (!info) {
+		dev_dbg (&udev->dev, "blacklisted by %s\n", driver_name);
+		return -ENODEV;
+	}
+	xdev = interface_to_usbdev (udev);
+	interface = udev->cur_altsetting;
+
+	usb_get_dev (xdev);
+
+	status = -ENOMEM;
+
+	// set up our own records
+	net = alloc_etherdev(sizeof(*dev));
+	if (!net) {
+		dbg ("can't kmalloc dev");
+		goto out;
+	}
+
+	dev = netdev_priv(net);
+	dev->udev = xdev;
+	dev->driver_info = info;
+	dev->msg_enable = netif_msg_init (msg_level, NETIF_MSG_DRV
+				| NETIF_MSG_PROBE | NETIF_MSG_LINK);
+	skb_queue_head_init (&dev->rxq);
+	skb_queue_head_init (&dev->txq);
+	skb_queue_head_init (&dev->done);
+	dev->bh.func = usbnet_bh;
+	dev->bh.data = (unsigned long) dev;
+	INIT_WORK (&dev->kevent, kevent, dev);
+	dev->delay.function = usbnet_bh;
+	dev->delay.data = (unsigned long) dev;
+	init_timer (&dev->delay);
+
+	SET_MODULE_OWNER (net);
+	dev->net = net;
+	strcpy (net->name, "usb%d");
+	memcpy (net->dev_addr, node_id, sizeof node_id);
+
+#if 0
+// dma_supported() is deeply broken on almost all architectures
+	// possible with some EHCI controllers
+	if (dma_supported (&udev->dev, DMA_64BIT_MASK))
+		net->features |= NETIF_F_HIGHDMA;
+#endif
+
+	net->change_mtu = usbnet_change_mtu;
+	net->get_stats = usbnet_get_stats;
+	net->hard_start_xmit = usbnet_start_xmit;
+	net->open = usbnet_open;
+	net->stop = usbnet_stop;
+	net->watchdog_timeo = TX_TIMEOUT_JIFFIES;
+	net->tx_timeout = usbnet_tx_timeout;
+	net->do_ioctl = usbnet_ioctl;
+	net->ethtool_ops = &usbnet_ethtool_ops;
+
+	// allow device-specific bind/init procedures
+	// NOTE net->name still not usable ...
+	if (info->bind) {
+		status = info->bind (dev, udev);
+		// heuristic:  "usb%d" for links we know are two-host,
+		// else "eth%d" when there's reasonable doubt.  userspace
+		// can rename the link if it knows better.
+		if ((dev->driver_info->flags & FLAG_ETHER) != 0
+				&& (net->dev_addr [0] & 0x02) == 0)
+			strcpy (net->name, "eth%d");
+	} else if (!info->in || info->out)
+		status = get_endpoints (dev, udev);
+	else {
+		dev->in = usb_rcvbulkpipe (xdev, info->in);
+		dev->out = usb_sndbulkpipe (xdev, info->out);
+		if (!(info->flags & FLAG_NO_SETINT))
+			status = usb_set_interface (xdev,
+				interface->desc.bInterfaceNumber,
+				interface->desc.bAlternateSetting);
+		else
+			status = 0;
+
+	}
+
+	if (status == 0 && dev->status)
+		status = init_status (dev, udev);
+	if (status < 0)
+		goto out1;
+
+	dev->maxpacket = usb_maxpacket (dev->udev, dev->out, 1);
+	
+	SET_NETDEV_DEV(net, &udev->dev);
+	status = register_netdev (net);
+	if (status)
+		goto out3;
+	if (netif_msg_probe (dev))
+		devinfo (dev, "register usbnet at usb-%s-%s, %s, "
+				"%02x:%02x:%02x:%02x:%02x:%02x",
+			xdev->bus->bus_name, xdev->devpath,
+			dev->driver_info->description,
+			net->dev_addr [0], net->dev_addr [1],
+			net->dev_addr [2], net->dev_addr [3],
+			net->dev_addr [4], net->dev_addr [5]);
+
+	// ok, it's ready to go.
+	usb_set_intfdata (udev, dev);
+
+	// start as if the link is up
+	netif_device_attach (net);
+
+	return 0;
+
+out3:
+	if (info->unbind)
+		info->unbind (dev, udev);
+out1:
+	free_netdev(net);
+out:
+	usb_put_dev(xdev);
+	return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef	CONFIG_PM
+
+static int usbnet_suspend (struct usb_interface *intf, u32 state)
+{
+	struct usbnet		*dev = usb_get_intfdata(intf);
+	
+	netif_device_detach (dev->net);
+	return 0;
+}
+
+static int usbnet_resume (struct usb_interface *intf)
+{
+	struct usbnet		*dev = usb_get_intfdata(intf);
+
+	netif_device_attach (dev->net);
+	return 0;
+}
+
+#else	/* !CONFIG_PM */
+
+#define	usbnet_suspend	NULL
+#define	usbnet_resume	NULL
+
+#endif	/* CONFIG_PM */
+
+/*-------------------------------------------------------------------------*/
+
+#ifndef	HAVE_HARDWARE
+#error You need to configure some hardware for this driver
+#endif
+
+/*
+ * chip vendor names won't normally be on the cables, and
+ * may not be on the device.
+ */
+
+static const struct usb_device_id	products [] = {
+
+#ifdef	CONFIG_USB_ALI_M5632
+{
+	USB_DEVICE (0x0402, 0x5632),	// ALi defaults
+	.driver_info =	(unsigned long) &ali_m5632_info,
+},
+#endif
+
+#ifdef	CONFIG_USB_AN2720
+{
+	USB_DEVICE (0x0547, 0x2720),	// AnchorChips defaults
+	.driver_info =	(unsigned long) &an2720_info,
+}, {
+	USB_DEVICE (0x0547, 0x2727),	// Xircom PGUNET
+	.driver_info =	(unsigned long) &an2720_info,
+},
+#endif
+
+#ifdef	CONFIG_USB_BELKIN
+{
+	USB_DEVICE (0x050d, 0x0004),	// Belkin
+	.driver_info =	(unsigned long) &belkin_info,
+}, {
+	USB_DEVICE (0x056c, 0x8100),	// eTEK
+	.driver_info =	(unsigned long) &belkin_info,
+}, {
+	USB_DEVICE (0x0525, 0x9901),	// Advance USBNET (eTEK)
+	.driver_info =	(unsigned long) &belkin_info,
+},
+#endif
+
+#ifdef CONFIG_USB_AX8817X
+{
+	// Linksys USB200M
+	USB_DEVICE (0x077b, 0x2226),
+	.driver_info =	(unsigned long) &ax8817x_info,
+}, {
+	// Netgear FA120
+	USB_DEVICE (0x0846, 0x1040),
+	.driver_info =  (unsigned long) &netgear_fa120_info,
+}, {
+	// DLink DUB-E100
+	USB_DEVICE (0x2001, 0x1a00),
+	.driver_info =  (unsigned long) &dlink_dub_e100_info,
+}, {
+	// Intellinet, ST Lab USB Ethernet
+	USB_DEVICE (0x0b95, 0x1720),
+	.driver_info =  (unsigned long) &ax8817x_info,
+}, {
+	// Hawking UF200, TrendNet TU2-ET100
+	USB_DEVICE (0x07b8, 0x420a),
+	.driver_info =  (unsigned long) &hawking_uf200_info,
+}, {
+        // Billionton Systems, USB2AR 
+        USB_DEVICE (0x08dd, 0x90ff),
+        .driver_info =  (unsigned long) &ax8817x_info,
+}, {
+	// ATEN UC210T
+	USB_DEVICE (0x0557, 0x2009),
+	.driver_info =  (unsigned long) &ax8817x_info,
+}, {
+	// Buffalo LUA-U2-KTX
+	USB_DEVICE (0x0411, 0x003d),
+	.driver_info =  (unsigned long) &ax8817x_info,
+}, {
+	// Sitecom LN-029 "USB 2.0 10/100 Ethernet adapter"
+	USB_DEVICE (0x6189, 0x182d),
+	.driver_info =  (unsigned long) &ax8817x_info,
+}, {
+	// corega FEther USB2-TX
+	USB_DEVICE (0x07aa, 0x0017),
+	.driver_info =  (unsigned long) &ax8817x_info,
+}, {
+	// Surecom EP-1427X-2
+	USB_DEVICE (0x1189, 0x0893),
+	.driver_info = (unsigned long) &ax8817x_info,
+}, {
+	// goodway corp usb gwusb2e
+	USB_DEVICE (0x1631, 0x6200),
+	.driver_info = (unsigned long) &ax8817x_info,
+}, {
+	// ASIX AX88772 10/100
+        USB_DEVICE (0x0b95, 0x7720),
+        .driver_info = (unsigned long) &ax88772_info,
+},
+#endif
+
+#ifdef	CONFIG_USB_EPSON2888
+{
+	USB_DEVICE (0x0525, 0x2888),	// EPSON USB client
+	.driver_info	= (unsigned long) &epson2888_info,
+},
+#endif
+
+#ifdef	CONFIG_USB_GENESYS
+{
+	USB_DEVICE (0x05e3, 0x0502),	// GL620USB-A
+	.driver_info =	(unsigned long) &genelink_info,
+},
+	/* NOT: USB_DEVICE (0x05e3, 0x0501),	// GL620USB
+	 * that's half duplex, not currently supported
+	 */
+#endif
+
+#ifdef	CONFIG_USB_NET1080
+{
+	USB_DEVICE (0x0525, 0x1080),	// NetChip ref design
+	.driver_info =	(unsigned long) &net1080_info,
+}, {
+	USB_DEVICE (0x06D0, 0x0622),	// Laplink Gold
+	.driver_info =	(unsigned long) &net1080_info,
+},
+#endif
+
+#ifdef CONFIG_USB_PL2301
+{
+	USB_DEVICE (0x067b, 0x0000),	// PL-2301
+	.driver_info =	(unsigned long) &prolific_info,
+}, {
+	USB_DEVICE (0x067b, 0x0001),	// PL-2302
+	.driver_info =	(unsigned long) &prolific_info,
+},
+#endif
+
+#ifdef CONFIG_USB_KC2190
+{
+	USB_DEVICE (0x050f, 0x0190),	// KC-190
+	.driver_info =	(unsigned long) &kc2190_info,
+},
+#endif
+
+#ifdef	CONFIG_USB_RNDIS
+{
+	/* RNDIS is MSFT's un-official variant of CDC ACM */
+	USB_INTERFACE_INFO (USB_CLASS_COMM, 2 /* ACM */, 0x0ff),
+	.driver_info = (unsigned long) &rndis_info,
+},
+#endif
+
+#ifdef	CONFIG_USB_ARMLINUX
+/*
+ * SA-1100 using standard ARM Linux kernels, or compatible.
+ * Often used when talking to Linux PDAs (iPaq, Yopy, etc).
+ * The sa-1100 "usb-eth" driver handles the basic framing.
+ *
+ * PXA25x or PXA210 ...  these use a "usb-eth" driver much like
+ * the sa1100 one, but hardware uses different endpoint numbers.
+ *
+ * Or the Linux "Ethernet" gadget on hardware that can't talk
+ * CDC Ethernet (e.g., no altsettings), in either of two modes:
+ *  - acting just like the old "usb-eth" firmware, though
+ *    the implementation is different 
+ *  - supporting RNDIS as the first/default configuration for
+ *    MS-Windows interop; Linux needs to use the other config
+ */
+{
+	// 1183 = 0x049F, both used as hex values?
+	// Compaq "Itsy" vendor/product id
+	USB_DEVICE (0x049F, 0x505A),	// usb-eth, or compatible
+	.driver_info =	(unsigned long) &linuxdev_info,
+}, {
+	USB_DEVICE (0x0E7E, 0x1001),	// G.Mate "Yopy"
+	.driver_info =	(unsigned long) &yopy_info,
+}, {
+	USB_DEVICE (0x8086, 0x07d3),	// "blob" bootloader
+	.driver_info =	(unsigned long) &blob_info,
+}, {
+	// Linux Ethernet/RNDIS gadget on pxa210/25x/26x
+	// e.g. Gumstix, current OpenZaurus, ...
+	USB_DEVICE_VER (0x0525, 0xa4a2, 0x0203, 0x0203),
+	.driver_info =	(unsigned long) &linuxdev_info,
+}, 
+#endif
+
+#if	defined(CONFIG_USB_ZAURUS) || defined(CONFIG_USB_CDCETHER)
+/*
+ * SA-1100 based Sharp Zaurus ("collie"), or compatible.
+ * Same idea as above, but different framing.
+ *
+ * PXA-2xx based models are also lying-about-cdc.
+ * Some models don't even tell the same lies ...
+ *
+ * NOTE:  OpenZaurus versions with 2.6 kernels won't use these entries,
+ * unlike the older ones with 2.4 "embedix" kernels.
+ *
+ * NOTE:  These entries do double-duty, serving as blacklist entries
+ * whenever Zaurus support isn't enabled, but CDC Ethernet is.
+ */
+#define	ZAURUS_MASTER_INTERFACE \
+	.bInterfaceClass	= USB_CLASS_COMM, \
+	.bInterfaceSubClass	= USB_CDC_SUBCLASS_ETHERNET, \
+	.bInterfaceProtocol	= USB_CDC_PROTO_NONE
+{
+	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
+			  | USB_DEVICE_ID_MATCH_DEVICE, 
+	.idVendor		= 0x04DD,
+	.idProduct		= 0x8004,
+	ZAURUS_MASTER_INTERFACE,
+	.driver_info = ZAURUS_STRONGARM_INFO,
+}, {
+	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
+			  | USB_DEVICE_ID_MATCH_DEVICE, 
+	.idVendor		= 0x04DD,
+	.idProduct		= 0x8005,	/* A-300 */
+	ZAURUS_MASTER_INTERFACE,
+	.driver_info = ZAURUS_PXA_INFO,
+}, {
+	.match_flags	=   USB_DEVICE_ID_MATCH_INT_INFO
+			  | USB_DEVICE_ID_MATCH_DEVICE, 
+	.idVendor		= 0x04DD,
+	.idProduct		= 0x8006,	/* B-500/SL-5600 */
+	ZAURUS_MASTER_INTERFACE,
+	.driver_info = ZAURUS_PXA_INFO,
+}, {
+	.match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO
+	          | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor		= 0x04DD,
+	.idProduct		= 0x8007,	/* C-700 */
+	ZAURUS_MASTER_INTERFACE,
+	.driver_info = ZAURUS_PXA_INFO,
+}, {
+	.match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO
+		 | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor               = 0x04DD,
+	.idProduct              = 0x9031,	/* C-750 C-760 */
+	ZAURUS_MASTER_INTERFACE,
+	.driver_info = ZAURUS_PXA_INFO,
+}, {
+	.match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO
+		 | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor               = 0x04DD,
+	.idProduct              = 0x9032,	/* SL-6000 */
+	ZAURUS_MASTER_INTERFACE,
+	.driver_info = ZAURUS_PXA_INFO,
+}, {
+	.match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO
+		 | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor               = 0x04DD,
+	/* reported with some C860 units */
+	.idProduct              = 0x9050,	/* C-860 */
+	ZAURUS_MASTER_INTERFACE,
+	.driver_info = ZAURUS_PXA_INFO,
+#ifdef	CONFIG_USB_ZAURUS
+	/* at least some (reports vary) C-860 units have very different
+	 * lies about their standards support.
+	 */
+}, {
+	.match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO
+		 | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor               = 0x04DD,
+	/* reported with some C860 units */
+	.idProduct              = 0x9031,	/* C-860 */
+	.bInterfaceClass	= USB_CLASS_COMM,
+	.bInterfaceSubClass	= USB_CDC_SUBCLASS_MDLM,
+	.bInterfaceProtocol	= USB_CDC_PROTO_NONE,
+	.driver_info 		= (unsigned long) &zaurus_pxa_mdlm_info,
+#endif
+},
+
+/* Olympus has some models with a Zaurus-compatible option.
+ * R-1000 uses a FreeScale i.MXL cpu (ARMv4T)
+ */
+{
+	.match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO
+		 | USB_DEVICE_ID_MATCH_DEVICE,
+	.idVendor               = 0x07B4,
+	.idProduct              = 0x0F02,	/* R-1000 */
+	ZAURUS_MASTER_INTERFACE,
+	.driver_info = OLYMPUS_MXL_INFO,
+},
+#endif
+
+#ifdef	CONFIG_USB_CDCETHER
+{
+	/* CDC Ether uses two interfaces, not necessarily consecutive.
+	 * We match the main interface, ignoring the optional device
+	 * class so we could handle devices that aren't exclusively
+	 * CDC ether.
+	 *
+	 * NOTE:  this match must come AFTER entries working around
+	 * bugs/quirks in a given product (like Zaurus, above).
+	 */
+	USB_INTERFACE_INFO (USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET,
+			USB_CDC_PROTO_NONE),
+	.driver_info = (unsigned long) &cdc_info,
+},
+#endif
+
+	{ },		// END
+};
+MODULE_DEVICE_TABLE (usb, products);
+
+static struct usb_driver usbnet_driver = {
+	.owner =	THIS_MODULE,
+	.name =		driver_name,
+	.id_table =	products,
+	.probe =	usbnet_probe,
+	.disconnect =	usbnet_disconnect,
+	.suspend =	usbnet_suspend,
+	.resume =	usbnet_resume,
+};
+
+/* Default ethtool_ops assigned.  Devices can override in their bind() routine */
+static struct ethtool_ops usbnet_ethtool_ops = {
+	.get_drvinfo		= usbnet_get_drvinfo,
+	.get_link		= usbnet_get_link,
+	.get_msglevel		= usbnet_get_msglevel,
+	.set_msglevel		= usbnet_set_msglevel,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int __init usbnet_init (void)
+{
+	// compiler should optimize these out
+	BUG_ON (sizeof (((struct sk_buff *)0)->cb)
+			< sizeof (struct skb_data));
+#ifdef	CONFIG_USB_CDCETHER
+	BUG_ON ((sizeof (((struct usbnet *)0)->data)
+			< sizeof (struct cdc_state)));
+#endif
+
+	random_ether_addr(node_id);
+
+ 	return usb_register(&usbnet_driver);
+}
+module_init (usbnet_init);
+
+static void __exit usbnet_exit (void)
+{
+ 	usb_deregister (&usbnet_driver);
+}
+module_exit (usbnet_exit);
+
+MODULE_AUTHOR ("David Brownell <dbrownell@users.sourceforge.net>");
+MODULE_DESCRIPTION ("USB Host-to-Host Link Drivers (numerous vendors)");
+MODULE_LICENSE ("GPL");
diff --git a/drivers/usb/net/zd1201.c b/drivers/usb/net/zd1201.c
new file mode 100644
index 0000000..938025e
--- /dev/null
+++ b/drivers/usb/net/zd1201.c
@@ -0,0 +1,1905 @@
+/*
+ *	Driver for ZyDAS zd1201 based wireless USB devices.
+ *
+ *	Copyright (c) 2004, 2005 Jeroen Vreeken (pe1rxq@amsat.org)
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	version 2 as published by the Free Software Foundation.
+ *
+ *	Parts of this driver have been derived from a wlan-ng version
+ *	modified by ZyDAS. They also made documentation available, thanks!
+ *	Copyright (C) 1999 AbsoluteValue Systems, Inc.  All Rights Reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
+#include <linux/string.h>
+#include <linux/if_arp.h>
+#include <linux/firmware.h>
+#include <ieee802_11.h>
+#include "zd1201.h"
+
+static struct usb_device_id zd1201_table[] = {
+	{USB_DEVICE(0x0586, 0x3400)}, /* Peabird Wireless USB Adapter */
+	{USB_DEVICE(0x0ace, 0x1201)}, /* ZyDAS ZD1201 Wireless USB Adapter */
+	{USB_DEVICE(0x050d, 0x6051)}, /* Belkin F5D6051 usb  adapter */
+	{USB_DEVICE(0x0db0, 0x6823)}, /* MSI UB11B usb  adapter */
+	{}
+};
+
+static int ap = 0;	/* Are we an AP or a normal station? */
+
+#define ZD1201_VERSION	"0.15"
+
+MODULE_AUTHOR("Jeroen Vreeken <pe1rxq@amsat.org>");
+MODULE_DESCRIPTION("Driver for ZyDAS ZD1201 based USB Wireless adapters");
+MODULE_VERSION(ZD1201_VERSION);
+MODULE_LICENSE("GPL");
+module_param(ap, int, 0);
+MODULE_PARM_DESC(ap, "If non-zero Access Point firmware will be loaded");
+MODULE_DEVICE_TABLE(usb, zd1201_table);
+
+
+int zd1201_fw_upload(struct usb_device *dev, int apfw)
+{
+	const struct firmware *fw_entry;
+	char* data;
+	unsigned long len;
+	int err;
+	unsigned char ret;
+	char *buf;
+	char *fwfile;
+
+	if (apfw)
+		fwfile = "zd1201-ap.fw";
+	else
+		fwfile = "zd1201.fw";
+
+	err = request_firmware(&fw_entry, fwfile, &dev->dev);
+	if (err) {
+		dev_err(&dev->dev, "Failed to load %s firmware file!\n", fwfile);
+		dev_err(&dev->dev, "Make sure the hotplug firmware loader is installed.\n");
+		dev_err(&dev->dev, "Goto http://linux-lc100020.sourceforge.net for more info\n");
+		return err;
+	}
+
+	data = fw_entry->data;
+        len = fw_entry->size;
+
+	buf = kmalloc(1024, GFP_ATOMIC);
+	if (!buf)
+		goto exit;
+	
+	while (len > 0) {
+		int translen = (len > 1024) ? 1024 : len;
+		memcpy(buf, data, translen);
+
+		err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0,
+		    USB_DIR_OUT | 0x40, 0, 0, buf, translen,
+		    ZD1201_FW_TIMEOUT);
+		if (err < 0)
+			goto exit;
+
+		len -= translen;
+		data += translen;
+	}
+                                        
+	err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x2,
+	    USB_DIR_OUT | 0x40, 0, 0, NULL, 0, ZD1201_FW_TIMEOUT);
+	if (err < 0)
+		goto exit;
+                                                                                                                                                                
+	err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x4,
+	    USB_DIR_IN | 0x40, 0,0, &ret, sizeof(ret), ZD1201_FW_TIMEOUT);
+	if (err < 0)
+		goto exit;
+                                                                                                                                                                                                                                                                                        
+	if (ret & 0x80) {
+		err = -EIO;
+		goto exit;
+	}
+
+	err = 0;
+exit:
+ 	if (buf)
+		kfree(buf);
+	release_firmware(fw_entry);
+	return err;
+}
+
+void zd1201_usbfree(struct urb *urb, struct pt_regs *regs)
+{
+	struct zd1201 *zd = urb->context;
+
+	switch(urb->status) {
+		case -EILSEQ:
+		case -ENODEV:
+		case -ETIMEDOUT:
+		case -ENOENT:
+		case -EPIPE:
+		case -EOVERFLOW:
+		case -ESHUTDOWN:
+			dev_warn(&zd->usb->dev, "%s: urb failed: %d\n", 
+			    zd->dev->name, urb->status);
+	}
+
+	kfree(urb->transfer_buffer);
+	usb_free_urb(urb);
+	return;
+}
+
+/* cmdreq message: 
+	u32 type
+	u16 cmd
+	u16 parm0
+	u16 parm1
+	u16 parm2
+	u8  pad[4]
+
+	total: 4 + 2 + 2 + 2 + 2 + 4 = 16
+*/
+int zd1201_docmd(struct zd1201 *zd, int cmd, int parm0, int parm1, int parm2)
+{
+	unsigned char *command;
+	int ret;
+	struct urb *urb;
+
+	command = kmalloc(16, GFP_ATOMIC);
+	if (!command)
+		return -ENOMEM;
+
+	*((__le32*)command) = cpu_to_le32(ZD1201_USB_CMDREQ);
+	*((__le16*)&command[4]) = cpu_to_le16(cmd);
+	*((__le16*)&command[6]) = cpu_to_le16(parm0);
+	*((__le16*)&command[8]) = cpu_to_le16(parm1);
+	*((__le16*)&command[10])= cpu_to_le16(parm2);
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		kfree(command);
+		return -ENOMEM;
+	}
+	usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb, zd->endp_out2),
+	     command, 16, zd1201_usbfree, zd);
+	ret = usb_submit_urb(urb, GFP_ATOMIC);
+	if (ret) {
+		kfree(command);
+		usb_free_urb(urb);
+	}
+	
+	return ret;
+}
+
+/* Callback after sending out a packet */
+void zd1201_usbtx(struct urb *urb, struct pt_regs *regs)
+{
+	struct zd1201 *zd = urb->context;
+	netif_wake_queue(zd->dev);
+	return;
+}
+
+/* Incomming data */
+void zd1201_usbrx(struct urb *urb, struct pt_regs *regs)
+{
+	struct zd1201 *zd = urb->context;
+	int free = 0;
+	unsigned char *data = urb->transfer_buffer;
+	struct sk_buff *skb;
+	unsigned char type;
+
+	if (!zd) {
+		free = 1;
+		goto exit;
+	}
+
+	switch(urb->status) {
+		case -EILSEQ:
+		case -ENODEV:
+		case -ETIMEDOUT:
+		case -ENOENT:
+		case -EPIPE:
+		case -EOVERFLOW:
+		case -ESHUTDOWN:
+			dev_warn(&zd->usb->dev, "%s: rx urb failed: %d\n",
+			    zd->dev->name, urb->status);
+			free = 1;
+			goto exit;
+	}
+	
+	if (urb->status != 0 || urb->actual_length == 0)
+		goto resubmit;
+
+	type = data[0];
+	if (type == ZD1201_PACKET_EVENTSTAT || type == ZD1201_PACKET_RESOURCE) {
+		memcpy(zd->rxdata, data, urb->actual_length);
+		zd->rxlen = urb->actual_length;
+		zd->rxdatas = 1;
+		wake_up(&zd->rxdataq);
+	}
+	/* Info frame */
+	if (type == ZD1201_PACKET_INQUIRE) {
+		int i = 0;
+		unsigned short infotype, framelen, copylen;
+		framelen = le16_to_cpu(*(__le16*)&data[4]);
+		infotype = le16_to_cpu(*(__le16*)&data[6]);
+
+		if (infotype == ZD1201_INF_LINKSTATUS) {
+			short linkstatus;
+
+			linkstatus = le16_to_cpu(*(__le16*)&data[8]);
+			switch(linkstatus) {
+				case 1:
+					netif_carrier_on(zd->dev);
+					break;
+				case 2:
+					netif_carrier_off(zd->dev);
+					break;
+				case 3:
+					netif_carrier_off(zd->dev);
+					break;
+				case 4:
+					netif_carrier_on(zd->dev);
+					break;
+				default:
+					netif_carrier_off(zd->dev);
+			}
+			goto resubmit;
+		}
+		if (infotype == ZD1201_INF_ASSOCSTATUS) {
+			short status = le16_to_cpu(*(__le16*)(data+8));
+			int event;
+			union iwreq_data wrqu;
+
+			switch (status) {
+				case ZD1201_ASSOCSTATUS_STAASSOC:
+				case ZD1201_ASSOCSTATUS_REASSOC:
+					event = IWEVREGISTERED;
+					break;
+				case ZD1201_ASSOCSTATUS_DISASSOC:
+				case ZD1201_ASSOCSTATUS_ASSOCFAIL:
+				case ZD1201_ASSOCSTATUS_AUTHFAIL:
+				default:
+					event = IWEVEXPIRED;
+			}
+			memcpy(wrqu.addr.sa_data, data+10, ETH_ALEN);
+			wrqu.addr.sa_family = ARPHRD_ETHER;
+
+			/* Send event to user space */
+			wireless_send_event(zd->dev, event, &wrqu, NULL);
+
+			goto resubmit;
+		}
+		if (infotype == ZD1201_INF_AUTHREQ) {
+			union iwreq_data wrqu;
+
+			memcpy(wrqu.addr.sa_data, data+8, ETH_ALEN);
+			wrqu.addr.sa_family = ARPHRD_ETHER;
+			/* There isn't a event that trully fits this request.
+			   We assume that userspace will be smart enough to
+			   see a new station being expired and sends back a
+			   authstation ioctl to authorize it. */
+			wireless_send_event(zd->dev, IWEVEXPIRED, &wrqu, NULL);
+			goto resubmit;
+		}
+		/* Other infotypes are handled outside this handler */
+		zd->rxlen = 0;
+		while (i < urb->actual_length) {
+			copylen = le16_to_cpu(*(__le16*)&data[i+2]);
+			/* Sanity check, sometimes we get junk */
+			if (copylen+zd->rxlen > sizeof(zd->rxdata))
+				break;
+			memcpy(zd->rxdata+zd->rxlen, data+i+4, copylen);
+			zd->rxlen += copylen;
+			i += 64;
+		}
+		if (i >= urb->actual_length) {
+			zd->rxdatas = 1;
+			wake_up(&zd->rxdataq);
+		}
+		goto  resubmit;
+	}
+	/* Actual data */
+	if (data[urb->actual_length-1] == ZD1201_PACKET_RXDATA) {
+		int datalen = urb->actual_length-1;
+		unsigned short len, fc, seq;
+		struct hlist_node *node;
+
+		len = ntohs(*(__be16 *)&data[datalen-2]);
+		if (len>datalen)
+			len=datalen;
+		fc = le16_to_cpu(*(__le16 *)&data[datalen-16]);
+		seq = le16_to_cpu(*(__le16 *)&data[datalen-24]);
+
+		if(zd->monitor) {
+			if (datalen < 24)
+				goto resubmit;
+			if (!(skb = dev_alloc_skb(datalen+24)))
+				goto resubmit;
+			
+			memcpy(skb_put(skb, 2), &data[datalen-16], 2);
+			memcpy(skb_put(skb, 2), &data[datalen-2], 2);
+			memcpy(skb_put(skb, 6), &data[datalen-14], 6);
+			memcpy(skb_put(skb, 6), &data[datalen-22], 6);
+			memcpy(skb_put(skb, 6), &data[datalen-8], 6);
+			memcpy(skb_put(skb, 2), &data[datalen-24], 2);
+			memcpy(skb_put(skb, len), data, len);
+			skb->dev = zd->dev;
+			skb->dev->last_rx = jiffies;
+			skb->protocol = eth_type_trans(skb, zd->dev);
+			zd->stats.rx_packets++;
+			zd->stats.rx_bytes += skb->len;
+			netif_rx(skb);
+			goto resubmit;
+		}
+			
+		if ((seq & IEEE802_11_SCTL_FRAG) ||
+		    (fc & IEEE802_11_FCTL_MOREFRAGS)) {
+			struct zd1201_frag *frag = NULL;
+			char *ptr;
+
+			if (datalen<14)
+				goto resubmit;
+			if ((seq & IEEE802_11_SCTL_FRAG) == 0) {
+				frag = kmalloc(sizeof(struct zd1201_frag*),
+				    GFP_ATOMIC);
+				if (!frag)
+					goto resubmit;
+				skb = dev_alloc_skb(IEEE802_11_DATA_LEN +14+2);
+				if (!skb) {
+					kfree(frag);
+					goto resubmit;
+				}
+				frag->skb = skb;
+				frag->seq = seq & IEEE802_11_SCTL_SEQ;
+				skb_reserve(skb, 2);
+				memcpy(skb_put(skb, 12), &data[datalen-14], 12);
+				memcpy(skb_put(skb, 2), &data[6], 2);
+				memcpy(skb_put(skb, len), data+8, len);
+				hlist_add_head(&frag->fnode, &zd->fraglist);
+				goto resubmit;
+			}
+			hlist_for_each_entry(frag, node, &zd->fraglist, fnode)
+				if(frag->seq == (seq&IEEE802_11_SCTL_SEQ))
+					break;
+			if (!frag)
+				goto resubmit;
+			skb = frag->skb;
+			ptr = skb_put(skb, len);
+			if (ptr)
+				memcpy(ptr, data+8, len);
+			if (fc & IEEE802_11_FCTL_MOREFRAGS)
+				goto resubmit;
+			hlist_del_init(&frag->fnode);
+			kfree(frag);
+			/* Fallthrough */
+		} else {
+			if (datalen<14)
+				goto resubmit;
+			skb = dev_alloc_skb(len + 14 + 2);
+			if (!skb)
+				goto resubmit;
+			skb_reserve(skb, 2);
+			memcpy(skb_put(skb, 12), &data[datalen-14], 12);
+			memcpy(skb_put(skb, 2), &data[6], 2);
+			memcpy(skb_put(skb, len), data+8, len);
+		}
+		skb->dev = zd->dev;
+		skb->dev->last_rx = jiffies;
+		skb->protocol = eth_type_trans(skb, zd->dev);
+		zd->stats.rx_packets++;
+		zd->stats.rx_bytes += skb->len;
+		netif_rx(skb);
+	}
+resubmit:
+	memset(data, 0, ZD1201_RXSIZE);
+
+	urb->status = 0;
+	urb->dev = zd->usb;
+	if(usb_submit_urb(urb, GFP_ATOMIC))
+		free = 1;
+
+exit:
+	if (free) {
+		zd->rxlen = 0;
+		zd->rxdatas = 1;
+		wake_up(&zd->rxdataq);
+		kfree(urb->transfer_buffer);
+	}
+	return;
+}
+
+static int zd1201_getconfig(struct zd1201 *zd, int rid, void *riddata,
+	unsigned int riddatalen)
+{
+	int err;
+	int i = 0;
+	int code;
+	int rid_fid;
+	int length;
+	unsigned char *pdata;
+	
+	zd->rxdatas = 0;
+	err = zd1201_docmd(zd, ZD1201_CMDCODE_ACCESS, rid, 0, 0);
+	if (err)
+		return err;
+
+	wait_event_interruptible(zd->rxdataq, zd->rxdatas);
+	if (!zd->rxlen)
+		return -EIO;
+
+	code = le16_to_cpu(*(__le16*)(&zd->rxdata[4]));
+	rid_fid = le16_to_cpu(*(__le16*)(&zd->rxdata[6]));
+	length = le16_to_cpu(*(__le16*)(&zd->rxdata[8]));
+	if (length > zd->rxlen)
+		length = zd->rxlen-6;
+
+	/* If access bit is not on, then error */
+	if ((code & ZD1201_ACCESSBIT) != ZD1201_ACCESSBIT || rid_fid != rid )
+		return -EINVAL;
+
+	/* Not enough buffer for allocating data */
+	if (riddatalen != (length - 4)) {
+		dev_dbg(&zd->usb->dev, "riddatalen mismatches, expected=%u, (packet=%u) length=%u, rid=0x%04X, rid_fid=0x%04X\n",
+		    riddatalen, zd->rxlen, length, rid, rid_fid);
+		return -ENODATA;
+	}
+
+	zd->rxdatas = 0;
+	/* Issue SetRxRid commnd */			
+	err = zd1201_docmd(zd, ZD1201_CMDCODE_SETRXRID, rid, 0, length);
+	if (err)
+		return err;
+
+	/* Receive RID record from resource packets */
+	wait_event_interruptible(zd->rxdataq, zd->rxdatas);
+	if (!zd->rxlen)
+		return -EIO;
+
+	if (zd->rxdata[zd->rxlen - 1] != ZD1201_PACKET_RESOURCE) {
+		dev_dbg(&zd->usb->dev, "Packet type mismatch: 0x%x not 0x3\n",
+		    zd->rxdata[zd->rxlen-1]);
+		return -EINVAL;
+	}
+
+	/* Set the data pointer and received data length */
+	pdata = zd->rxdata;
+	length = zd->rxlen;
+
+	do {
+		int  actual_length;
+
+		actual_length = (length > 64) ? 64 : length;
+
+		if(pdata[0] != 0x3) {
+			dev_dbg(&zd->usb->dev, "Rx Resource packet type error: %02X\n",
+			    pdata[0]);
+			return -EINVAL;
+		}
+
+		if (actual_length != 64) {
+			/* Trim the last packet type byte */
+			actual_length--;
+		}
+
+		/* Skip the 4 bytes header (RID length and RID) */
+		if(i == 0) {
+			pdata += 8;
+			actual_length -= 8;
+		}
+		else {
+			pdata += 4;
+			actual_length -= 4;
+		}
+		
+		memcpy(riddata, pdata, actual_length);
+		riddata += actual_length;
+		pdata += actual_length;
+		length -= 64;
+		i++;
+	} while (length > 0);
+
+	return 0;
+}
+
+/*
+ *	resreq:
+ *		byte	type
+ *		byte	sequence
+ *		u16	reserved
+ *		byte	data[12]
+ *	total: 16
+ */
+static int zd1201_setconfig(struct zd1201 *zd, int rid, void *buf, int len, int wait)
+{
+	int err;
+	unsigned char *request;
+	int reqlen;
+	char seq=0;
+	struct urb *urb;
+	unsigned int gfp_mask = wait ? GFP_NOIO : GFP_ATOMIC;
+
+	len += 4;			/* first 4 are for header */
+
+	zd->rxdatas = 0;
+	zd->rxlen = 0;
+	for (seq=0; len > 0; seq++) {
+		request = kmalloc(16, gfp_mask);
+		if (!request)
+			return -ENOMEM;
+		urb = usb_alloc_urb(0, gfp_mask);
+		if (!urb) {
+			kfree(request);
+			return -ENOMEM;
+		}
+		memset(request, 0, 16);
+		reqlen = len>12 ? 12 : len;
+		request[0] = ZD1201_USB_RESREQ;
+		request[1] = seq;
+		request[2] = 0;
+		request[3] = 0;
+		if (request[1] == 0) {
+			/* add header */
+			*(__le16*)&request[4] = cpu_to_le16((len-2+1)/2);
+			*(__le16*)&request[6] = cpu_to_le16(rid);
+			memcpy(request+8, buf, reqlen-4);
+			buf += reqlen-4;
+		} else {
+			memcpy(request+4, buf, reqlen);
+			buf += reqlen;
+		}
+
+		len -= reqlen;
+
+		usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb,
+		    zd->endp_out2), request, 16, zd1201_usbfree, zd);
+		err = usb_submit_urb(urb, gfp_mask);
+		if (err)
+			goto err;
+	}
+
+	request = kmalloc(16, gfp_mask);
+	if (!request)
+		return -ENOMEM;
+	urb = usb_alloc_urb(0, gfp_mask);
+	if (!urb) {
+		kfree(request);
+		return -ENOMEM;
+	}
+	*((__le32*)request) = cpu_to_le32(ZD1201_USB_CMDREQ);
+	*((__le16*)&request[4]) = 
+	    cpu_to_le16(ZD1201_CMDCODE_ACCESS|ZD1201_ACCESSBIT);
+	*((__le16*)&request[6]) = cpu_to_le16(rid);
+	*((__le16*)&request[8]) = cpu_to_le16(0);
+	*((__le16*)&request[10]) = cpu_to_le16(0);
+	usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb, zd->endp_out2),
+	     request, 16, zd1201_usbfree, zd);
+	err = usb_submit_urb(urb, gfp_mask);
+	if (err)
+		goto err;
+	
+	if (wait) {
+		wait_event_interruptible(zd->rxdataq, zd->rxdatas);
+		if (!zd->rxlen || le16_to_cpu(*(__le16*)&zd->rxdata[6]) != rid) {
+			dev_dbg(&zd->usb->dev, "wrong or no RID received\n");
+		}
+	}
+
+	return 0;
+err:
+	kfree(request);
+	usb_free_urb(urb);
+	return err;
+}
+
+static inline int zd1201_getconfig16(struct zd1201 *zd, int rid, short *val)
+{
+	int err;
+	__le16 zdval;
+
+	err = zd1201_getconfig(zd, rid, &zdval, sizeof(__le16));
+	if (err)
+		return err;
+	*val = le16_to_cpu(zdval);
+	return 0;
+}
+
+static inline int zd1201_setconfig16(struct zd1201 *zd, int rid, short val)
+{
+	__le16 zdval = cpu_to_le16(val);
+	return (zd1201_setconfig(zd, rid, &zdval, sizeof(__le16), 1));
+}
+
+int zd1201_drvr_start(struct zd1201 *zd)
+{
+	int err, i;
+	short max;
+	__le16 zdmax;
+	unsigned char *buffer;
+	
+	buffer = kmalloc(ZD1201_RXSIZE, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+	memset(buffer, 0, ZD1201_RXSIZE);
+
+	usb_fill_bulk_urb(zd->rx_urb, zd->usb, 
+	    usb_rcvbulkpipe(zd->usb, zd->endp_in), buffer, ZD1201_RXSIZE,
+	    zd1201_usbrx, zd);
+
+	err = usb_submit_urb(zd->rx_urb, GFP_KERNEL);
+	if (err)
+		goto err_buffer;
+	
+	err = zd1201_docmd(zd, ZD1201_CMDCODE_INIT, 0, 0, 0);
+	if (err)
+		goto err_urb;
+
+	err = zd1201_getconfig(zd, ZD1201_RID_CNFMAXTXBUFFERNUMBER, &zdmax,
+	    sizeof(__le16));
+	if (err)
+		goto err_urb;
+
+	max = le16_to_cpu(zdmax);
+	for (i=0; i<max; i++) {
+		err = zd1201_docmd(zd, ZD1201_CMDCODE_ALLOC, 1514, 0, 0);
+		if (err)
+			goto err_urb;
+	}
+
+	return 0;
+
+err_urb:
+	usb_kill_urb(zd->rx_urb);
+	return err;
+err_buffer:
+	kfree(buffer);
+	return err;
+}
+
+/*	Magic alert: The firmware doesn't seem to like the MAC state being
+ *	toggled in promisc (aka monitor) mode.
+ *	(It works a number of times, but will halt eventually)
+ *	So we turn it of before disabling and on after enabling if needed.
+ */
+static int zd1201_enable(struct zd1201 *zd)
+{
+	int err;
+
+	if (zd->mac_enabled)
+		return 0;
+
+	err = zd1201_docmd(zd, ZD1201_CMDCODE_ENABLE, 0, 0, 0);
+	if (!err)
+		zd->mac_enabled = 1;
+
+	if (zd->monitor)
+		err = zd1201_setconfig16(zd, ZD1201_RID_PROMISCUOUSMODE, 1);
+
+	return err;
+}
+
+static int zd1201_disable(struct zd1201 *zd)
+{
+	int err;
+	
+	if (!zd->mac_enabled)
+		return 0;
+	if (zd->monitor) {
+		err = zd1201_setconfig16(zd, ZD1201_RID_PROMISCUOUSMODE, 0);
+		if (err)
+			return err;
+	}
+
+	err = zd1201_docmd(zd, ZD1201_CMDCODE_DISABLE, 0, 0, 0);
+	if (!err)
+		zd->mac_enabled = 0;
+	return err;
+}
+
+static int zd1201_mac_reset(struct zd1201 *zd)
+{
+	if (!zd->mac_enabled)
+		return 0;
+	zd1201_disable(zd);
+	return zd1201_enable(zd);
+}
+
+static int zd1201_join(struct zd1201 *zd, char *essid, int essidlen)
+{
+	int err, val;
+	char buf[IW_ESSID_MAX_SIZE+2];
+
+	err = zd1201_disable(zd);
+	if (err)
+		return err;
+
+	val = ZD1201_CNFAUTHENTICATION_OPENSYSTEM;
+	val |= ZD1201_CNFAUTHENTICATION_SHAREDKEY;
+	err = zd1201_setconfig16(zd, ZD1201_RID_CNFAUTHENTICATION, val);
+	if (err)
+		return err;
+
+	*(__le16 *)buf = cpu_to_le16(essidlen);
+	memcpy(buf+2, essid, essidlen);
+	if (!zd->ap) {	/* Normal station */
+		err = zd1201_setconfig(zd, ZD1201_RID_CNFDESIREDSSID, buf,
+		    IW_ESSID_MAX_SIZE+2, 1);
+		if (err)
+			return err;
+	} else {	/* AP */
+		err = zd1201_setconfig(zd, ZD1201_RID_CNFOWNSSID, buf,
+		    IW_ESSID_MAX_SIZE+2, 1);
+		if (err)
+			return err;
+	}
+
+	err = zd1201_setconfig(zd, ZD1201_RID_CNFOWNMACADDR, 
+	    zd->dev->dev_addr, zd->dev->addr_len, 1);
+	if (err)
+		return err;
+
+	err = zd1201_enable(zd);
+	if (err)
+		return err;
+
+	msleep(100);
+	return 0;
+}
+
+static int zd1201_net_open(struct net_device *dev)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+
+	/* Start MAC with wildcard if no essid set */
+	if (!zd->mac_enabled)
+		zd1201_join(zd, zd->essid, zd->essidlen);
+	netif_start_queue(dev);
+
+	return 0;
+}
+
+static int zd1201_net_stop(struct net_device *dev)
+{
+	netif_stop_queue(dev);
+	
+	return 0;
+}
+
+/*
+	RFC 1042 encapsulates Ethernet frames in 802.11 frames
+	by prefixing them with 0xaa, 0xaa, 0x03) followed by a SNAP OID of 0
+	(0x00, 0x00, 0x00). Zd requires an additionnal padding, copy
+	of ethernet addresses, length of the standard RFC 1042 packet
+	and a command byte (which is nul for tx).
+	
+	tx frame (from Wlan NG):
+	RFC 1042:
+		llc		0xAA 0xAA 0x03 (802.2 LLC)
+		snap		0x00 0x00 0x00 (Ethernet encapsulated)
+		type		2 bytes, Ethernet type field
+		payload		(minus eth header)
+	Zydas specific:
+		padding		1B if (skb->len+8+1)%64==0
+		Eth MAC addr	12 bytes, Ethernet MAC addresses
+		length		2 bytes, RFC 1042 packet length 
+				(llc+snap+type+payload)
+		zd		1 null byte, zd1201 packet type
+ */
+static int zd1201_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+	unsigned char *txbuf = zd->txdata;
+	int txbuflen, pad = 0, err;
+	struct urb *urb = zd->tx_urb;
+
+	if (!zd->mac_enabled || zd->monitor) {
+		zd->stats.tx_dropped++;
+		kfree_skb(skb);
+		return 0;
+	}
+	netif_stop_queue(dev);
+
+	txbuflen = skb->len + 8 + 1;
+	if (txbuflen%64 == 0) {
+		pad = 1;
+		txbuflen++;
+	}
+	txbuf[0] = 0xAA;
+	txbuf[1] = 0xAA;
+	txbuf[2] = 0x03;
+	txbuf[3] = 0x00;	/* rfc1042 */
+	txbuf[4] = 0x00;
+	txbuf[5] = 0x00;
+
+	memcpy(txbuf+6, skb->data+12, skb->len-12);
+	if (pad)
+		txbuf[skb->len-12+6]=0;
+	memcpy(txbuf+skb->len-12+6+pad, skb->data, 12);
+	*(__be16*)&txbuf[skb->len+6+pad] = htons(skb->len-12+6);
+	txbuf[txbuflen-1] = 0;
+
+	usb_fill_bulk_urb(urb, zd->usb, usb_sndbulkpipe(zd->usb, zd->endp_out),
+	    txbuf, txbuflen, zd1201_usbtx, zd);
+
+	err = usb_submit_urb(zd->tx_urb, GFP_ATOMIC);
+	if (err) {
+		zd->stats.tx_errors++;
+		netif_start_queue(dev);
+		return err;
+	}
+	zd->stats.tx_packets++;
+	zd->stats.tx_bytes += skb->len;
+	dev->trans_start = jiffies;
+	kfree_skb(skb);
+
+	return 0;
+}
+
+static void zd1201_tx_timeout(struct net_device *dev)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+
+	if (!zd)
+		return;
+	dev_warn(&zd->usb->dev, "%s: TX timeout, shooting down urb\n",
+	    dev->name);
+	zd->tx_urb->transfer_flags |= URB_ASYNC_UNLINK;
+	usb_unlink_urb(zd->tx_urb);
+	zd->stats.tx_errors++;
+	/* Restart the timeout to quiet the watchdog: */
+	dev->trans_start = jiffies;
+}
+
+static int zd1201_set_mac_address(struct net_device *dev, void *p)
+{
+	struct sockaddr *addr = p;
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+	int err;
+
+	if (!zd)
+		return -ENODEV;
+
+	err = zd1201_setconfig(zd, ZD1201_RID_CNFOWNMACADDR, 
+	    addr->sa_data, dev->addr_len, 1);
+	if (err)
+		return err;
+	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+
+	return zd1201_mac_reset(zd);
+}
+
+static struct net_device_stats *zd1201_get_stats(struct net_device *dev)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+
+	return &zd->stats;
+}
+
+static struct iw_statistics *zd1201_get_wireless_stats(struct net_device *dev)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+
+	return &zd->iwstats;
+}
+
+static void zd1201_set_multicast(struct net_device *dev)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+	struct dev_mc_list *mc = dev->mc_list;
+	unsigned char reqbuf[ETH_ALEN*ZD1201_MAXMULTI];
+	int i;
+
+	if (dev->mc_count > ZD1201_MAXMULTI)
+		return;
+
+	for (i=0; i<dev->mc_count; i++) {
+		memcpy(reqbuf+i*ETH_ALEN, mc->dmi_addr, ETH_ALEN);
+		mc = mc->next;
+	}
+	zd1201_setconfig(zd, ZD1201_RID_CNFGROUPADDRESS, reqbuf,
+	    dev->mc_count*ETH_ALEN, 0);
+	
+}
+
+static int zd1201_config_commit(struct net_device *dev, 
+    struct iw_request_info *info, struct iw_point *data, char *essid)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+
+	return zd1201_mac_reset(zd);
+}
+
+static int zd1201_get_name(struct net_device *dev,
+    struct iw_request_info *info, char *name, char *extra)
+{
+	strcpy(name, "IEEE 802.11b");
+
+	return 0;
+}
+
+static int zd1201_set_freq(struct net_device *dev,
+    struct iw_request_info *info, struct iw_freq *freq, char *extra)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+	short channel = 0;
+	int err;
+
+	if (freq->e == 0)
+		channel = freq->m;
+	else {
+		if (freq->m >= 2482)
+			channel = 14;
+		if (freq->m >= 2407)
+			channel = (freq->m-2407)/5;
+	}
+
+	err = zd1201_setconfig16(zd, ZD1201_RID_CNFOWNCHANNEL, channel);
+	if (err)
+		return err;
+
+	zd1201_mac_reset(zd);
+
+	return 0;
+}
+
+static int zd1201_get_freq(struct net_device *dev,
+    struct iw_request_info *info, struct iw_freq *freq, char *extra)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+	short channel;
+	int err;
+
+	err = zd1201_getconfig16(zd, ZD1201_RID_CNFOWNCHANNEL, &channel);
+	if (err)
+		return err;
+	freq->e = 0;
+	freq->m = channel;
+
+	return 0;
+}
+
+static int zd1201_set_mode(struct net_device *dev,
+    struct iw_request_info *info, __u32 *mode, char *extra)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+	short porttype, monitor = 0;
+	unsigned char buffer[IW_ESSID_MAX_SIZE+2];
+	int err;
+
+	if (zd->ap) {
+		if (*mode != IW_MODE_MASTER)
+			return -EINVAL;
+		return 0;
+	}
+
+	err = zd1201_setconfig16(zd, ZD1201_RID_PROMISCUOUSMODE, 0);
+	if (err)
+		return err;
+	zd->dev->type = ARPHRD_ETHER;
+	switch(*mode) {
+		case IW_MODE_MONITOR:
+			monitor = 1;
+			zd->dev->type = ARPHRD_IEEE80211;
+			/* Make sure we are no longer associated with by
+			   setting an 'impossible' essid.
+			   (otherwise we mess up firmware)
+			 */
+			zd1201_join(zd, "\0-*#\0", 5);
+			/* Put port in pIBSS */
+		case 8: /* No pseudo-IBSS in wireless extensions (yet) */
+			porttype = ZD1201_PORTTYPE_PSEUDOIBSS;
+			break;
+		case IW_MODE_ADHOC:
+			porttype = ZD1201_PORTTYPE_IBSS;
+			break;
+		case IW_MODE_INFRA:
+			porttype = ZD1201_PORTTYPE_BSS;
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	err = zd1201_setconfig16(zd, ZD1201_RID_CNFPORTTYPE, porttype);
+	if (err)
+		return err;
+	if (zd->monitor && !monitor) {
+			zd1201_disable(zd);
+			*(__le16 *)buffer = cpu_to_le16(zd->essidlen);
+			memcpy(buffer+2, zd->essid, zd->essidlen);
+			err = zd1201_setconfig(zd, ZD1201_RID_CNFDESIREDSSID,
+			    buffer, IW_ESSID_MAX_SIZE+2, 1);
+			if (err)
+				return err;
+	}
+	zd->monitor=monitor;
+	/* If monitor mode is set we don't actually turn it on here since it
+	 * is done during mac reset anyway (see zd1201_mac_enable).
+	 */
+
+	zd1201_mac_reset(zd);
+
+	return 0;
+}
+
+static int zd1201_get_mode(struct net_device *dev,
+    struct iw_request_info *info, __u32 *mode, char *extra)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+	short porttype;
+	int err;
+
+	err = zd1201_getconfig16(zd, ZD1201_RID_CNFPORTTYPE, &porttype);
+	if (err)
+		return err;
+	switch(porttype) {
+		case ZD1201_PORTTYPE_IBSS:
+			*mode = IW_MODE_ADHOC;
+			break;
+		case ZD1201_PORTTYPE_BSS:
+			*mode = IW_MODE_INFRA;
+			break;
+		case ZD1201_PORTTYPE_WDS:
+			*mode = IW_MODE_REPEAT;
+			break;
+		case ZD1201_PORTTYPE_PSEUDOIBSS:
+			*mode = 8;/* No Pseudo-IBSS... */
+			break;
+		case ZD1201_PORTTYPE_AP:
+			*mode = IW_MODE_MASTER;
+			break;
+		default:
+			dev_dbg(&zd->usb->dev, "Unknown porttype: %d\n",
+			    porttype);
+			*mode = IW_MODE_AUTO;
+	}
+	if (zd->monitor)
+		*mode = IW_MODE_MONITOR;
+
+	return 0;
+}
+
+static int zd1201_get_range(struct net_device *dev,
+    struct iw_request_info *info, struct iw_point *wrq, char *extra)
+{
+	struct iw_range *range = (struct iw_range *)extra;
+
+	wrq->length = sizeof(struct iw_range);
+	memset(range, 0, sizeof(struct iw_range));
+	range->we_version_compiled = WIRELESS_EXT;
+	range->we_version_source = WIRELESS_EXT;
+
+	range->max_qual.qual = 128;
+	range->max_qual.level = 128;
+	range->max_qual.noise = 128;
+	range->max_qual.updated = 7;
+
+	range->encoding_size[0] = 5;
+	range->encoding_size[1] = 13;
+	range->num_encoding_sizes = 2;
+	range->max_encoding_tokens = ZD1201_NUMKEYS;
+
+	range->num_bitrates = 4;
+	range->bitrate[0] = 1000000;
+	range->bitrate[1] = 2000000;
+	range->bitrate[2] = 5500000;
+	range->bitrate[3] = 11000000;
+
+	range->min_rts = 0;
+	range->min_frag = ZD1201_FRAGMIN;
+	range->max_rts = ZD1201_RTSMAX;
+	range->min_frag = ZD1201_FRAGMAX;
+
+	return 0;
+}
+
+/*	Little bit of magic here: we only get the quality if we poll
+ *	for it, and we never get an actual request to trigger such
+ *	a poll. Therefore we 'asume' that the user will soon ask for
+ *	the stats after asking the bssid.
+ */
+static int zd1201_get_wap(struct net_device *dev,
+    struct iw_request_info *info, struct sockaddr *ap_addr, char *extra)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+	unsigned char buffer[6];
+
+	if (!zd1201_getconfig(zd, ZD1201_RID_COMMSQUALITY, buffer, 6)) {
+		/* Unfortunatly the quality and noise reported is useless.
+		   they seem to be accumulators that increase until you
+		   read them, unless we poll on a fixed interval we can't
+		   use them
+		 */
+		/*zd->iwstats.qual.qual = le16_to_cpu(((__le16 *)buffer)[0]);*/
+		zd->iwstats.qual.level = le16_to_cpu(((__le16 *)buffer)[1]);
+		/*zd->iwstats.qual.noise = le16_to_cpu(((__le16 *)buffer)[2]);*/
+		zd->iwstats.qual.updated = 2;
+	}
+
+	return zd1201_getconfig(zd,ZD1201_RID_CURRENTBSSID,ap_addr->sa_data,6);
+}
+
+static int zd1201_set_scan(struct net_device *dev,
+    struct iw_request_info *info, struct iw_point *srq, char *extra)
+{
+	/* We do everything in get_scan */
+	return 0;
+}
+
+static int zd1201_get_scan(struct net_device *dev,
+    struct iw_request_info *info, struct iw_point *srq, char *extra)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+	int err, i, j, enabled_save;
+	struct iw_event iwe;
+	char *cev = extra;
+	char *end_buf = extra + IW_SCAN_MAX_DATA;
+
+	/* No scanning in AP mode */
+	if (zd->ap)
+		return -EOPNOTSUPP;
+
+	/* Scan doesn't seem to work if disabled */
+	enabled_save = zd->mac_enabled;
+	zd1201_enable(zd);
+
+	zd->rxdatas = 0;
+	err = zd1201_docmd(zd, ZD1201_CMDCODE_INQUIRE, 
+	     ZD1201_INQ_SCANRESULTS, 0, 0);
+	if (err)
+		return err;
+
+	wait_event_interruptible(zd->rxdataq, zd->rxdatas);
+	if (!zd->rxlen)
+		return -EIO;
+
+	if (le16_to_cpu(*(__le16*)&zd->rxdata[2]) != ZD1201_INQ_SCANRESULTS)
+		return -EIO;
+
+	for(i=8; i<zd->rxlen; i+=62) {
+		iwe.cmd = SIOCGIWAP;
+		iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+		memcpy(iwe.u.ap_addr.sa_data, zd->rxdata+i+6, 6);
+		cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_ADDR_LEN);
+
+		iwe.cmd = SIOCGIWESSID;
+		iwe.u.data.length = zd->rxdata[i+16];
+		iwe.u.data.flags = 1;
+		cev = iwe_stream_add_point(cev, end_buf, &iwe, zd->rxdata+i+18);
+
+		iwe.cmd = SIOCGIWMODE;
+		if (zd->rxdata[i+14]&0x01)
+			iwe.u.mode = IW_MODE_MASTER;
+		else
+			iwe.u.mode = IW_MODE_ADHOC;
+		cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_UINT_LEN);
+		
+		iwe.cmd = SIOCGIWFREQ;
+		iwe.u.freq.m = zd->rxdata[i+0];
+		iwe.u.freq.e = 0;
+		cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_FREQ_LEN);
+		
+		iwe.cmd = SIOCGIWRATE;
+		iwe.u.bitrate.fixed = 0;
+		iwe.u.bitrate.disabled = 0;
+		for (j=0; j<10; j++) if (zd->rxdata[i+50+j]) {
+			iwe.u.bitrate.value = (zd->rxdata[i+50+j]&0x7f)*500000;
+			cev=iwe_stream_add_event(cev, end_buf, &iwe,
+			    IW_EV_PARAM_LEN);
+		}
+		
+		iwe.cmd = SIOCGIWENCODE;
+		iwe.u.data.length = 0;
+		if (zd->rxdata[i+14]&0x10)
+			iwe.u.data.flags = IW_ENCODE_ENABLED;
+		else
+			iwe.u.data.flags = IW_ENCODE_DISABLED;
+		cev = iwe_stream_add_point(cev, end_buf, &iwe, NULL);
+		
+		iwe.cmd = IWEVQUAL;
+		iwe.u.qual.qual = zd->rxdata[i+4];
+		iwe.u.qual.noise= zd->rxdata[i+2]/10-100;
+		iwe.u.qual.level = (256+zd->rxdata[i+4]*100)/255-100;
+		iwe.u.qual.updated = 7;
+		cev = iwe_stream_add_event(cev, end_buf, &iwe, IW_EV_QUAL_LEN);
+	}
+
+	if (!enabled_save)
+		zd1201_disable(zd);
+
+	srq->length = cev - extra;
+	srq->flags = 0;
+
+	return 0;
+}
+
+static int zd1201_set_essid(struct net_device *dev,
+    struct iw_request_info *info, struct iw_point *data, char *essid)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+
+	if (data->length > IW_ESSID_MAX_SIZE)
+		return -EINVAL;
+	if (data->length < 1)
+		data->length = 1;
+	zd->essidlen = data->length-1;
+	memset(zd->essid, 0, IW_ESSID_MAX_SIZE+1);
+	memcpy(zd->essid, essid, data->length);
+	return zd1201_join(zd, zd->essid, zd->essidlen);
+}
+
+static int zd1201_get_essid(struct net_device *dev,
+    struct iw_request_info *info, struct iw_point *data, char *essid)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+
+	memcpy(essid, zd->essid, zd->essidlen);
+	data->flags = 1;
+	data->length = zd->essidlen;
+
+	return 0;
+}
+
+static int zd1201_get_nick(struct net_device *dev, struct iw_request_info *info,
+    struct iw_point *data, char *nick)
+{
+	strcpy(nick, "zd1201");
+	data->flags = 1;
+	data->length = strlen(nick);
+	return 0;
+}
+
+static int zd1201_set_rate(struct net_device *dev,
+    struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+	short rate;
+	int err;
+
+	switch (rrq->value) {
+		case 1000000:
+			rate = ZD1201_RATEB1;
+			break;
+		case 2000000:
+			rate = ZD1201_RATEB2;
+			break;
+		case 5500000:
+			rate = ZD1201_RATEB5;
+			break;
+		case 11000000:
+		default:
+			rate = ZD1201_RATEB11;
+			break;
+	}
+	if (!rrq->fixed) { /* Also enable all lower bitrates */
+		rate |= rate-1;
+	}
+	
+	err = zd1201_setconfig16(zd, ZD1201_RID_TXRATECNTL, rate);
+	if (err)
+		return err;
+
+	return zd1201_mac_reset(zd);
+}
+
+static int zd1201_get_rate(struct net_device *dev,
+    struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+	short rate;
+	int err;
+
+	err = zd1201_getconfig16(zd, ZD1201_RID_CURRENTTXRATE, &rate);
+	if (err)
+		return err;
+
+	switch(rate) {
+		case 1:
+			rrq->value = 1000000;
+			break;
+		case 2:
+			rrq->value = 2000000;
+			break;
+		case 5:
+			rrq->value = 5500000;
+			break;
+		case 11:
+			rrq->value = 11000000;
+			break;
+		default:
+			rrq->value = 0;
+	}
+	rrq->fixed = 0;
+	rrq->disabled = 0;
+
+	return 0;
+}
+
+static int zd1201_set_rts(struct net_device *dev, struct iw_request_info *info,
+    struct iw_param *rts, char *extra)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+	int err;
+	short val = rts->value;
+
+	if (rts->disabled || !rts->fixed)
+		val = ZD1201_RTSMAX;
+	if (val > ZD1201_RTSMAX)
+		return -EINVAL;
+	if (val < 0)
+		return -EINVAL;
+
+	err = zd1201_setconfig16(zd, ZD1201_RID_CNFRTSTHRESHOLD, val);
+	if (err)
+		return err;
+	return zd1201_mac_reset(zd);
+}
+
+static int zd1201_get_rts(struct net_device *dev, struct iw_request_info *info,
+    struct iw_param *rts, char *extra)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+	short rtst;
+	int err;
+
+	err = zd1201_getconfig16(zd, ZD1201_RID_CNFRTSTHRESHOLD, &rtst);
+	if (err)
+		return err;
+	rts->value = rtst;
+	rts->disabled = (rts->value == ZD1201_RTSMAX);
+	rts->fixed = 1;
+
+	return 0;
+}
+
+static int zd1201_set_frag(struct net_device *dev, struct iw_request_info *info,
+    struct iw_param *frag, char *extra)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+	int err;
+	short val = frag->value;
+
+	if (frag->disabled || !frag->fixed)
+		val = ZD1201_FRAGMAX;
+	if (val > ZD1201_FRAGMAX)
+		return -EINVAL;
+	if (val < ZD1201_FRAGMIN)
+		return -EINVAL;
+	if (val & 1)
+		return -EINVAL;
+	err = zd1201_setconfig16(zd, ZD1201_RID_CNFFRAGTHRESHOLD, val);
+	if (err)
+		return err;
+	return zd1201_mac_reset(zd);
+}
+
+static int zd1201_get_frag(struct net_device *dev, struct iw_request_info *info,
+    struct iw_param *frag, char *extra)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+	short fragt;
+	int err;
+
+	err = zd1201_getconfig16(zd, ZD1201_RID_CNFFRAGTHRESHOLD, &fragt);
+	if (err)
+		return err;
+	frag->value = fragt;
+	frag->disabled = (frag->value == ZD1201_FRAGMAX);
+	frag->fixed = 1;
+
+	return 0;
+}
+
+static int zd1201_set_retry(struct net_device *dev,
+    struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+	return 0;
+}
+
+static int zd1201_get_retry(struct net_device *dev,
+    struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+	return 0;
+}
+
+static int zd1201_set_encode(struct net_device *dev,
+    struct iw_request_info *info, struct iw_point *erq, char *key)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+	short i;
+	int err, rid;
+
+	if (erq->length > ZD1201_MAXKEYLEN)
+		return -EINVAL;
+
+	i = (erq->flags & IW_ENCODE_INDEX)-1;
+	if (i == -1) {
+		err = zd1201_getconfig16(zd,ZD1201_RID_CNFDEFAULTKEYID,&i);
+		if (err)
+			return err;
+	} else {
+		err = zd1201_setconfig16(zd, ZD1201_RID_CNFDEFAULTKEYID, i);
+		if (err)
+			return err;
+	}
+
+	if (i < 0 || i >= ZD1201_NUMKEYS)
+		return -EINVAL;
+
+	rid = ZD1201_RID_CNFDEFAULTKEY0 + i;
+	err = zd1201_setconfig(zd, rid, key, erq->length, 1);
+	if (err)
+		return err;
+	zd->encode_keylen[i] = erq->length;
+	memcpy(zd->encode_keys[i], key, erq->length);
+
+	i=0;
+	if (!(erq->flags & IW_ENCODE_DISABLED & IW_ENCODE_MODE)) {
+		i |= 0x01;
+		zd->encode_enabled = 1;
+	} else
+		zd->encode_enabled = 0;
+	if (erq->flags & IW_ENCODE_RESTRICTED & IW_ENCODE_MODE) {
+		i |= 0x02;
+		zd->encode_restricted = 1;
+	} else
+		zd->encode_restricted = 0;
+	err = zd1201_setconfig16(zd, ZD1201_RID_CNFWEBFLAGS, i);
+	if (err)
+		return err;
+
+	if (zd->encode_enabled)
+		i = ZD1201_CNFAUTHENTICATION_SHAREDKEY;
+	else
+		i = ZD1201_CNFAUTHENTICATION_OPENSYSTEM;
+	err = zd1201_setconfig16(zd, ZD1201_RID_CNFAUTHENTICATION, i);
+	if (err)
+		return err;
+
+	return zd1201_mac_reset(zd);
+}
+
+static int zd1201_get_encode(struct net_device *dev,
+    struct iw_request_info *info, struct iw_point *erq, char *key)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+	short i;
+	int err;
+
+	if (zd->encode_enabled)
+		erq->flags = IW_ENCODE_ENABLED;
+	else
+		erq->flags = IW_ENCODE_DISABLED;
+	if (zd->encode_restricted)
+		erq->flags |= IW_ENCODE_RESTRICTED;
+	else
+		erq->flags |= IW_ENCODE_OPEN;
+
+	i = (erq->flags & IW_ENCODE_INDEX) -1;
+	if (i == -1) {
+		err = zd1201_getconfig16(zd, ZD1201_RID_CNFDEFAULTKEYID, &i);
+		if (err)
+			return err;
+	}
+	if (i<0 || i>= ZD1201_NUMKEYS)
+		return -EINVAL;
+
+	erq->flags |= i+1;
+	
+	erq->length = zd->encode_keylen[i];
+	memcpy(key, zd->encode_keys[i], erq->length);
+
+	return 0;
+}
+
+static int zd1201_set_power(struct net_device *dev, 
+    struct iw_request_info *info, struct iw_param *vwrq, char *extra)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+	short enabled, duration, level;
+	int err;
+
+	enabled = vwrq->disabled ? 0 : 1;
+	if (enabled) {
+		if (vwrq->flags & IW_POWER_PERIOD) {
+			duration = vwrq->value;
+			err = zd1201_setconfig16(zd, 
+			    ZD1201_RID_CNFMAXSLEEPDURATION, duration);
+			if (err)
+				return err;
+			goto out;
+		}
+		if (vwrq->flags & IW_POWER_TIMEOUT) {
+			err = zd1201_getconfig16(zd, 
+			    ZD1201_RID_CNFMAXSLEEPDURATION, &duration);
+			if (err)
+				return err;
+			level = vwrq->value * 4 / duration;
+			if (level > 4)
+				level = 4;
+			if (level < 0)
+				level = 0;
+			err = zd1201_setconfig16(zd, ZD1201_RID_CNFPMEPS,
+			    level);
+			if (err)
+				return err;
+			goto out;
+		}
+		return -EINVAL;
+	}
+out:
+	err = zd1201_setconfig16(zd, ZD1201_RID_CNFPMENABLED, enabled);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static int zd1201_get_power(struct net_device *dev,
+    struct iw_request_info *info, struct iw_param *vwrq, char *extra)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+	short enabled, level, duration;
+	int err;
+
+	err = zd1201_getconfig16(zd, ZD1201_RID_CNFPMENABLED, &enabled);
+	if (err)
+		return err;
+	err = zd1201_getconfig16(zd, ZD1201_RID_CNFPMEPS, &level);
+	if (err)
+		return err;
+	err = zd1201_getconfig16(zd, ZD1201_RID_CNFMAXSLEEPDURATION, &duration);
+	if (err)
+		return err;
+	vwrq->disabled = enabled ? 0 : 1;
+	if (vwrq->flags & IW_POWER_TYPE) {
+		if (vwrq->flags & IW_POWER_PERIOD) {
+			vwrq->value = duration;
+			vwrq->flags = IW_POWER_PERIOD;
+		} else {
+			vwrq->value = duration * level / 4;
+			vwrq->flags = IW_POWER_TIMEOUT;
+		}
+	}
+	if (vwrq->flags & IW_POWER_MODE) {
+		if (enabled && level)
+			vwrq->flags = IW_POWER_UNICAST_R;
+		else
+			vwrq->flags = IW_POWER_ALL_R;
+	}
+
+	return 0;
+}
+
+
+static const iw_handler zd1201_iw_handler[] =
+{
+	(iw_handler) zd1201_config_commit,	/* SIOCSIWCOMMIT */
+	(iw_handler) zd1201_get_name,    	/* SIOCGIWNAME */
+	(iw_handler) NULL,			/* SIOCSIWNWID */
+	(iw_handler) NULL,			/* SIOCGIWNWID */
+	(iw_handler) zd1201_set_freq,		/* SIOCSIWFREQ */
+	(iw_handler) zd1201_get_freq,		/* SIOCGIWFREQ */
+	(iw_handler) zd1201_set_mode,		/* SIOCSIWMODE */
+	(iw_handler) zd1201_get_mode,		/* SIOCGIWMODE */
+	(iw_handler) NULL,                  	/* SIOCSIWSENS */
+	(iw_handler) NULL,           		/* SIOCGIWSENS */
+	(iw_handler) NULL,			/* SIOCSIWRANGE */
+	(iw_handler) zd1201_get_range,           /* SIOCGIWRANGE */
+	(iw_handler) NULL,			/* SIOCSIWPRIV */
+	(iw_handler) NULL,			/* SIOCGIWPRIV */
+	(iw_handler) NULL,			/* SIOCSIWSTATS */
+	(iw_handler) NULL,			/* SIOCGIWSTATS */
+	(iw_handler) NULL,			/* SIOCSIWSPY */
+	(iw_handler) NULL,			/* SIOCGIWSPY */
+	(iw_handler) NULL,			/* -- hole -- */
+	(iw_handler) NULL,			/* -- hole -- */
+	(iw_handler) NULL/*zd1201_set_wap*/,		/* SIOCSIWAP */
+	(iw_handler) zd1201_get_wap,		/* SIOCGIWAP */
+	(iw_handler) NULL,			/* -- hole -- */
+	(iw_handler) NULL,       		/* SIOCGIWAPLIST */
+	(iw_handler) zd1201_set_scan,		/* SIOCSIWSCAN */
+	(iw_handler) zd1201_get_scan,		/* SIOCGIWSCAN */
+	(iw_handler) zd1201_set_essid,		/* SIOCSIWESSID */
+	(iw_handler) zd1201_get_essid,		/* SIOCGIWESSID */
+	(iw_handler) NULL,         		/* SIOCSIWNICKN */
+	(iw_handler) zd1201_get_nick, 		/* SIOCGIWNICKN */
+	(iw_handler) NULL,			/* -- hole -- */
+	(iw_handler) NULL,			/* -- hole -- */
+	(iw_handler) zd1201_set_rate,		/* SIOCSIWRATE */
+	(iw_handler) zd1201_get_rate,		/* SIOCGIWRATE */
+	(iw_handler) zd1201_set_rts,		/* SIOCSIWRTS */
+	(iw_handler) zd1201_get_rts,		/* SIOCGIWRTS */
+	(iw_handler) zd1201_set_frag,		/* SIOCSIWFRAG */
+	(iw_handler) zd1201_get_frag,		/* SIOCGIWFRAG */
+	(iw_handler) NULL,         		/* SIOCSIWTXPOW */
+	(iw_handler) NULL,          		/* SIOCGIWTXPOW */
+	(iw_handler) zd1201_set_retry,		/* SIOCSIWRETRY */
+	(iw_handler) zd1201_get_retry,		/* SIOCGIWRETRY */
+	(iw_handler) zd1201_set_encode,		/* SIOCSIWENCODE */
+	(iw_handler) zd1201_get_encode,		/* SIOCGIWENCODE */
+	(iw_handler) zd1201_set_power,		/* SIOCSIWPOWER */
+	(iw_handler) zd1201_get_power,		/* SIOCGIWPOWER */
+};
+
+static int zd1201_set_hostauth(struct net_device *dev,
+    struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+	int err;
+
+	if (!zd->ap)
+		return -EOPNOTSUPP;
+
+	err = zd1201_setconfig16(zd, ZD1201_RID_CNFHOSTAUTH, rrq->value);
+	if (err)
+		return err;
+	return 0;
+}
+
+static int zd1201_get_hostauth(struct net_device *dev,
+    struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+	short hostauth;
+	int err;
+
+	if (!zd->ap)
+		return -EOPNOTSUPP;
+
+	err = zd1201_getconfig16(zd, ZD1201_RID_CNFHOSTAUTH, &hostauth);
+	if (err)
+		return err;
+	rrq->value = hostauth;
+	rrq->fixed = 1;
+
+	return 0;
+}
+
+static int zd1201_auth_sta(struct net_device *dev,
+    struct iw_request_info *info, struct sockaddr *sta, char *extra)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+	unsigned char buffer[10];
+
+	if (!zd->ap)
+		return -EOPNOTSUPP;
+
+	memcpy(buffer, sta->sa_data, ETH_ALEN);
+	*(short*)(buffer+6) = 0;	/* 0==success, 1==failure */
+	*(short*)(buffer+8) = 0;
+
+	return zd1201_setconfig(zd, ZD1201_RID_AUTHENTICATESTA, buffer, 10, 1);
+}
+
+static int zd1201_set_maxassoc(struct net_device *dev,
+    struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+	int err;
+
+	if (!zd->ap)
+		return -EOPNOTSUPP;
+
+	err = zd1201_setconfig16(zd, ZD1201_RID_CNFMAXASSOCSTATIONS, rrq->value);
+	if (err)
+		return err;
+	return 0;
+}
+
+static int zd1201_get_maxassoc(struct net_device *dev,
+    struct iw_request_info *info, struct iw_param *rrq, char *extra)
+{
+	struct zd1201 *zd = (struct zd1201 *)dev->priv;
+	short maxassoc;
+	int err;
+
+	if (!zd->ap)
+		return -EOPNOTSUPP;
+
+	err = zd1201_getconfig16(zd, ZD1201_RID_CNFMAXASSOCSTATIONS, &maxassoc);
+	if (err)
+		return err;
+	rrq->value = maxassoc;
+	rrq->fixed = 1;
+
+	return 0;
+}
+
+static const iw_handler zd1201_private_handler[] = {
+	(iw_handler) zd1201_set_hostauth,	/* ZD1201SIWHOSTAUTH */
+	(iw_handler) zd1201_get_hostauth,	/* ZD1201GIWHOSTAUTH */
+	(iw_handler) zd1201_auth_sta,		/* ZD1201SIWAUTHSTA */
+	(iw_handler) NULL,			/* nothing to get */
+	(iw_handler) zd1201_set_maxassoc,	/* ZD1201SIMAXASSOC */
+	(iw_handler) zd1201_get_maxassoc,	/* ZD1201GIMAXASSOC */
+};
+
+static const struct iw_priv_args zd1201_private_args[] = {
+	{ ZD1201SIWHOSTAUTH, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	    IW_PRIV_TYPE_NONE, "sethostauth" },
+	{ ZD1201GIWHOSTAUTH, IW_PRIV_TYPE_NONE,
+	    IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostauth" },
+	{ ZD1201SIWAUTHSTA, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 
+	    IW_PRIV_TYPE_NONE, "authstation" },
+	{ ZD1201SIWMAXASSOC, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+	    IW_PRIV_TYPE_NONE, "setmaxassoc" },
+	{ ZD1201GIWMAXASSOC, IW_PRIV_TYPE_NONE,
+	    IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmaxassoc" },
+};
+
+static const struct iw_handler_def zd1201_iw_handlers = {
+	.num_standard 		= sizeof(zd1201_iw_handler)/sizeof(iw_handler),
+	.num_private 		= sizeof(zd1201_private_handler)/sizeof(iw_handler),
+	.num_private_args 	= sizeof(zd1201_private_args)/sizeof(struct iw_priv_args),
+	.standard 		= (iw_handler *)zd1201_iw_handler,
+	.private 		= (iw_handler *)zd1201_private_handler,
+	.private_args 		= (struct iw_priv_args *) zd1201_private_args,
+};
+
+int zd1201_probe(struct usb_interface *interface, const struct usb_device_id *id)
+{
+	struct zd1201 *zd;
+	struct usb_device *usb;
+	int i, err;
+	short porttype;
+	char buf[IW_ESSID_MAX_SIZE+2];
+
+	usb = interface_to_usbdev(interface);
+
+	zd = kmalloc(sizeof(struct zd1201), GFP_KERNEL);
+	if (!zd) {
+		return -ENOMEM;
+	}
+	memset(zd, 0, sizeof(struct zd1201));
+	zd->ap = ap;
+	zd->usb = usb;
+	zd->removed = 0;
+	init_waitqueue_head(&zd->rxdataq);
+	INIT_HLIST_HEAD(&zd->fraglist);
+	
+	err = zd1201_fw_upload(usb, zd->ap);
+	if (err) {
+		dev_err(&usb->dev, "zd1201 firmware upload failed: %d\n", err);
+		goto err_zd;
+	}
+	
+	zd->endp_in = 1;
+	zd->endp_out = 1;
+	zd->endp_out2 = 2;
+	zd->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+	zd->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!zd->rx_urb || !zd->tx_urb)
+		goto err_zd;
+
+	for(i = 0; i<100; i++)
+		udelay(1000);
+
+	err = zd1201_drvr_start(zd);
+	if (err)
+		goto err_zd;
+
+	err = zd1201_setconfig16(zd, ZD1201_RID_CNFMAXDATALEN, 2312);
+	if (err)
+		goto err_start;
+
+	err = zd1201_setconfig16(zd, ZD1201_RID_TXRATECNTL,
+	    ZD1201_RATEB1 | ZD1201_RATEB2 | ZD1201_RATEB5 | ZD1201_RATEB11);
+	if (err)
+		goto err_start;
+
+	zd->dev = alloc_etherdev(0);
+	if (!zd->dev)
+		goto err_start;
+
+	zd->dev->priv = zd;
+	zd->dev->open = zd1201_net_open;
+	zd->dev->stop = zd1201_net_stop;
+	zd->dev->get_stats = zd1201_get_stats;
+	zd->dev->get_wireless_stats = zd1201_get_wireless_stats;
+	zd->dev->wireless_handlers =
+	    (struct iw_handler_def *)&zd1201_iw_handlers;
+	zd->dev->hard_start_xmit = zd1201_hard_start_xmit;
+	zd->dev->watchdog_timeo = ZD1201_TX_TIMEOUT;
+	zd->dev->tx_timeout = zd1201_tx_timeout;
+	zd->dev->set_multicast_list = zd1201_set_multicast;
+	zd->dev->set_mac_address = zd1201_set_mac_address;
+	strcpy(zd->dev->name, "wlan%d");
+
+	err = zd1201_getconfig(zd, ZD1201_RID_CNFOWNMACADDR, 
+	    zd->dev->dev_addr, zd->dev->addr_len);
+	if (err)
+		goto err_net;
+
+	/* Set wildcard essid to match zd->essid */
+	*(__le16 *)buf = cpu_to_le16(0);
+	err = zd1201_setconfig(zd, ZD1201_RID_CNFDESIREDSSID, buf,
+	    IW_ESSID_MAX_SIZE+2, 1);
+	if (err)
+		goto err_net;
+
+	if (zd->ap)
+		porttype = ZD1201_PORTTYPE_AP;
+	else
+		porttype = ZD1201_PORTTYPE_BSS;
+	err = zd1201_setconfig16(zd, ZD1201_RID_CNFPORTTYPE, porttype);
+	if (err)
+		goto err_net;
+
+	err = register_netdev(zd->dev);
+	if (err)
+		goto err_net;
+	dev_info(&usb->dev, "%s: ZD1201 USB Wireless interface\n",
+	    zd->dev->name);
+	
+	usb_set_intfdata(interface, zd);
+	return 0;
+
+err_net:
+	free_netdev(zd->dev);
+err_start:
+	/* Leave the device in reset state */
+	zd1201_docmd(zd, ZD1201_CMDCODE_INIT, 0, 0, 0);
+err_zd:
+	if (zd->tx_urb)
+		usb_free_urb(zd->tx_urb);
+	if (zd->rx_urb)
+		usb_free_urb(zd->rx_urb);
+	kfree(zd);
+	return err;
+}
+
+void zd1201_disconnect(struct usb_interface *interface)
+{
+	struct zd1201 *zd=(struct zd1201 *)usb_get_intfdata(interface);
+	struct hlist_node *node, *node2;
+	struct zd1201_frag *frag;
+
+	if (!zd)
+		return;
+	usb_set_intfdata(interface, NULL);
+	if (zd->dev) {
+		unregister_netdev(zd->dev);
+		free_netdev(zd->dev);
+	}
+
+	hlist_for_each_entry_safe(frag, node, node2, &zd->fraglist, fnode) {
+		hlist_del_init(&frag->fnode);
+		kfree_skb(frag->skb);
+		kfree(frag);
+	}
+
+	if (zd->tx_urb) {
+		usb_kill_urb(zd->tx_urb);
+		usb_free_urb(zd->tx_urb);
+	}
+	if (zd->rx_urb) {
+		usb_kill_urb(zd->rx_urb);
+		usb_free_urb(zd->rx_urb);
+	}
+	kfree(zd);
+}
+
+struct usb_driver zd1201_usb = {
+	.owner = THIS_MODULE,
+	.name = "zd1201",
+	.probe = zd1201_probe,
+	.disconnect = zd1201_disconnect,
+	.id_table = zd1201_table,
+};
+
+static int __init zd1201_init(void)
+{
+	return usb_register(&zd1201_usb);
+}
+
+static void __exit zd1201_cleanup(void)
+{
+	usb_deregister(&zd1201_usb);
+}
+
+module_init(zd1201_init);
+module_exit(zd1201_cleanup);
diff --git a/drivers/usb/net/zd1201.h b/drivers/usb/net/zd1201.h
new file mode 100644
index 0000000..1627c71
--- /dev/null
+++ b/drivers/usb/net/zd1201.h
@@ -0,0 +1,147 @@
+/*
+ *	Copyright (c) 2004, 2005 Jeroen Vreeken (pe1rxq@amsat.org)
+ *
+ *	This program is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU General Public License
+ *	version 2 as published by the Free Software Foundation.
+ *
+ *	Parts of this driver have been derived from a wlan-ng version
+ *	modified by ZyDAS.
+ *	Copyright (C) 1999 AbsoluteValue Systems, Inc.  All Rights Reserved.
+ */
+
+#ifndef _INCLUDE_ZD1201_H_
+#define _INCLUDE_ZD1201_H_
+
+#define ZD1201_NUMKEYS		4
+#define ZD1201_MAXKEYLEN	13
+#define ZD1201_MAXMULTI		16
+#define ZD1201_FRAGMAX		2500
+#define ZD1201_FRAGMIN		256
+#define ZD1201_RTSMAX		2500
+
+#define ZD1201_RXSIZE		3000
+
+struct zd1201 {
+	struct usb_device	*usb;
+	int			removed;
+	struct net_device	*dev;
+	struct net_device_stats stats;
+	struct iw_statistics	iwstats;
+
+	int			endp_in;
+	int			endp_out;
+	int			endp_out2;
+	struct urb		*rx_urb;
+	struct urb		*tx_urb;
+
+	unsigned char 		rxdata[ZD1201_RXSIZE];
+	int			rxlen;
+	wait_queue_head_t	rxdataq;
+	int			rxdatas;
+	struct hlist_head	fraglist;
+	unsigned char		txdata[ZD1201_RXSIZE];
+
+	int			ap;
+	char			essid[IW_ESSID_MAX_SIZE+1];
+	int			essidlen;
+	int			mac_enabled;
+	int			monitor;
+	int			encode_enabled;
+	int			encode_restricted;
+	unsigned char		encode_keys[ZD1201_NUMKEYS][ZD1201_MAXKEYLEN];
+	int			encode_keylen[ZD1201_NUMKEYS];
+};
+
+struct zd1201_frag {
+	struct hlist_node	fnode;
+	int			seq;
+	struct sk_buff		*skb;
+};
+
+#define ZD1201SIWHOSTAUTH SIOCIWFIRSTPRIV
+#define ZD1201GIWHOSTAUTH ZD1201SIWHOSTAUTH+1
+#define ZD1201SIWAUTHSTA SIOCIWFIRSTPRIV+2
+#define ZD1201SIWMAXASSOC SIOCIWFIRSTPRIV+4
+#define ZD1201GIWMAXASSOC ZD1201SIWMAXASSOC+1
+
+#define ZD1201_FW_TIMEOUT	(1000)
+
+#define ZD1201_TX_TIMEOUT	(2000)
+
+#define ZD1201_USB_CMDREQ	0
+#define ZD1201_USB_RESREQ	1
+
+#define	ZD1201_CMDCODE_INIT	0x00
+#define ZD1201_CMDCODE_ENABLE	0x01
+#define ZD1201_CMDCODE_DISABLE	0x02
+#define ZD1201_CMDCODE_ALLOC	0x0a
+#define ZD1201_CMDCODE_INQUIRE	0x11
+#define ZD1201_CMDCODE_SETRXRID	0x17
+#define ZD1201_CMDCODE_ACCESS	0x21
+
+#define ZD1201_PACKET_EVENTSTAT	0x0
+#define ZD1201_PACKET_RXDATA	0x1
+#define ZD1201_PACKET_INQUIRE	0x2
+#define ZD1201_PACKET_RESOURCE	0x3
+
+#define ZD1201_ACCESSBIT	0x0100
+
+#define ZD1201_RID_CNFPORTTYPE		0xfc00
+#define ZD1201_RID_CNFOWNMACADDR	0xfc01
+#define ZD1201_RID_CNFDESIREDSSID	0xfc02
+#define ZD1201_RID_CNFOWNCHANNEL	0xfc03
+#define ZD1201_RID_CNFOWNSSID		0xfc04
+#define ZD1201_RID_CNFMAXDATALEN	0xfc07
+#define ZD1201_RID_CNFPMENABLED		0xfc09
+#define ZD1201_RID_CNFPMEPS		0xfc0a
+#define ZD1201_RID_CNFMAXSLEEPDURATION	0xfc0c
+#define ZD1201_RID_CNFDEFAULTKEYID	0xfc23
+#define ZD1201_RID_CNFDEFAULTKEY0	0xfc24
+#define ZD1201_RID_CNFDEFAULTKEY1	0xfc25
+#define ZD1201_RID_CNFDEFAULTKEY2	0xfc26
+#define ZD1201_RID_CNFDEFAULTKEY3	0xfc27
+#define ZD1201_RID_CNFWEBFLAGS		0xfc28
+#define ZD1201_RID_CNFAUTHENTICATION	0xfc2a
+#define ZD1201_RID_CNFMAXASSOCSTATIONS	0xfc2b
+#define ZD1201_RID_CNFHOSTAUTH		0xfc2e
+#define ZD1201_RID_CNFGROUPADDRESS	0xfc80
+#define ZD1201_RID_CNFFRAGTHRESHOLD	0xfc82
+#define ZD1201_RID_CNFRTSTHRESHOLD	0xfc83
+#define ZD1201_RID_TXRATECNTL		0xfc84
+#define ZD1201_RID_PROMISCUOUSMODE	0xfc85
+#define ZD1201_RID_CNFBASICRATES	0xfcb3
+#define ZD1201_RID_AUTHENTICATESTA	0xfce3
+#define ZD1201_RID_CURRENTBSSID		0xfd42
+#define ZD1201_RID_COMMSQUALITY		0xfd43
+#define ZD1201_RID_CURRENTTXRATE	0xfd44
+#define ZD1201_RID_CNFMAXTXBUFFERNUMBER	0xfda0
+#define ZD1201_RID_CURRENTCHANNEL	0xfdc1
+
+#define ZD1201_INQ_SCANRESULTS		0xf101
+
+#define ZD1201_INF_LINKSTATUS		0xf200
+#define ZD1201_INF_ASSOCSTATUS		0xf201
+#define ZD1201_INF_AUTHREQ		0xf202
+
+#define ZD1201_ASSOCSTATUS_STAASSOC	0x1
+#define ZD1201_ASSOCSTATUS_REASSOC	0x2
+#define ZD1201_ASSOCSTATUS_DISASSOC	0x3
+#define ZD1201_ASSOCSTATUS_ASSOCFAIL	0x4
+#define ZD1201_ASSOCSTATUS_AUTHFAIL	0x5
+
+#define ZD1201_PORTTYPE_IBSS		0
+#define ZD1201_PORTTYPE_BSS		1
+#define ZD1201_PORTTYPE_WDS		2
+#define ZD1201_PORTTYPE_PSEUDOIBSS	3
+#define ZD1201_PORTTYPE_AP		6
+
+#define ZD1201_RATEB1	1
+#define ZD1201_RATEB2	2
+#define ZD1201_RATEB5	4	/* 5.5 really, but 5 is shorter :) */
+#define ZD1201_RATEB11	8
+
+#define ZD1201_CNFAUTHENTICATION_OPENSYSTEM	0x0001
+#define ZD1201_CNFAUTHENTICATION_SHAREDKEY	0x0002
+
+#endif /* _INCLUDE_ZD1201_H_ */