From 61598093bbdd283a7edc367d900f223070ead8d2 Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Fri, 10 May 2024 07:43:03 +0000
Subject: [PATCH] add ax88772C AX88772C_eeprom_tools

---
 kernel/drivers/net/usb/AX88772C_eeprom/ioctl.h      |   75 
 kernel/drivers/net/usb/AX88772C_eeprom/asix.h       |  575 ++
 kernel/drivers/net/usb/AX88772C_eeprom/axusbnet.h   |  212 
 kernel/drivers/net/usb/ax88772C/axusbnet.h          |  212 
 kernel/drivers/net/usb/AX88772C_eeprom/axusbnet.c   | 1488 ++++++
 kernel/drivers/net/usb/ax88772C/axusbnet.c          | 1488 ++++++
 kernel/drivers/net/usb/AX88772C_eeprom/asix.c       | 4528 ++++++++++++++++++
 kernel/drivers/net/usb/ax88772C/readme              |   92 
 kernel/drivers/net/usb/ax88772C/Makefile            |    1 
 kernel/drivers/net/usb/AX88772C_eeprom/ioctl_readme |   61 
 kernel/arch/arm64/configs/rockchip_linux_defconfig  |    8 
 kernel/drivers/net/usb/AX88772C_eeprom/command.h    |   67 
 kernel/drivers/net/usb/Makefile                     |    2 
 kernel/drivers/net/usb/ax88772C/asix.c              | 4369 ++++++++++++++++++
 kernel/drivers/net/usb/AX88772C_eeprom/readme       |   92 
 kernel/drivers/net/usb/AX88772C_eeprom/ioctl.c      |  481 ++
 kernel/drivers/net/usb/ax88772C/asix.h              |  575 ++
 kernel/drivers/net/usb/AX88772C_eeprom/Makefile     |    1 
 18 files changed, 14,323 insertions(+), 4 deletions(-)

diff --git a/kernel/arch/arm64/configs/rockchip_linux_defconfig b/kernel/arch/arm64/configs/rockchip_linux_defconfig
index fb2201e..625cbae 100644
--- a/kernel/arch/arm64/configs/rockchip_linux_defconfig
+++ b/kernel/arch/arm64/configs/rockchip_linux_defconfig
@@ -2076,11 +2076,11 @@
 CONFIG_USB_RTL8152=y
 # CONFIG_USB_LAN78XX is not set
 CONFIG_USB_USBNET=y
-CONFIG_USB_NET_AX8817X=y
-CONFIG_USB_NET_AX88179_178A=y
+# CONFIG_USB_NET_AX8817X is not set
+# CONFIG_USB_NET_AX88179_178A is not set
 CONFIG_USB_NET_CDCETHER=y
 # CONFIG_USB_NET_CDC_EEM is not set
-CONFIG_USB_NET_CDC_NCM=y
+# CONFIG_USB_NET_CDC_NCM is not set
 # CONFIG_USB_NET_HUAWEI_CDC_NCM is not set
 # CONFIG_USB_NET_CDC_MBIM is not set
 # CONFIG_USB_NET_DM9601 is not set
@@ -2089,7 +2089,7 @@
 # CONFIG_USB_NET_SMSC75XX is not set
 # CONFIG_USB_NET_SMSC95XX is not set
 # CONFIG_USB_NET_GL620A is not set
-CONFIG_USB_NET_NET1080=y
+# CONFIG_USB_NET_NET1080 is not set
 # CONFIG_USB_NET_PLUSB is not set
 # CONFIG_USB_NET_MCS7830 is not set
 # CONFIG_USB_NET_RNDIS_HOST is not set
diff --git a/kernel/drivers/net/usb/AX88772C_eeprom/Makefile b/kernel/drivers/net/usb/AX88772C_eeprom/Makefile
new file mode 100644
index 0000000..7505b41
--- /dev/null
+++ b/kernel/drivers/net/usb/AX88772C_eeprom/Makefile
@@ -0,0 +1 @@
+obj-m += asix.o
diff --git a/kernel/drivers/net/usb/AX88772C_eeprom/asix.c b/kernel/drivers/net/usb/AX88772C_eeprom/asix.c
new file mode 100644
index 0000000..92baa93
--- /dev/null
+++ b/kernel/drivers/net/usb/AX88772C_eeprom/asix.c
@@ -0,0 +1,4528 @@
+/*
+ * ASIX AX8817X based USB 2.0 Ethernet Devices
+ * 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
+ */
+
+/* debug messages, extra info */
+/* #define	DEBUG */
+
+#include <linux/version.h>
+/* #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/ethtool.h>
+#include <linux/workqueue.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/crc32.h>
+
+#include "axusbnet.c"
+#include "asix.h"
+#include "command.h"
+
+#define DRV_VERSION	"4.24.100"
+
+static char version[] =
+KERN_INFO "ASIX USB Ethernet Adapter:v" DRV_VERSION
+	"    http://www.asix.com.tw\n";
+
+/* configuration of maximum bulk in size */
+static int bsize = AX88772B_MAX_BULKIN_16K;
+module_param(bsize, int, 0);
+MODULE_PARM_DESC(bsize, "Maximum transfer size per bulk");
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+static void ax88772b_link_reset(void *data);
+static void ax88772a_link_reset(void *data);
+static void ax88772_link_reset(void *data);
+#else
+static void ax88772b_link_reset(struct work_struct *work);
+static void ax88772a_link_reset(struct work_struct *work);
+static void ax88772_link_reset(struct work_struct *work);
+#endif
+static int ax88772a_phy_powerup(struct usbnet *dev);
+static void ax8817x_mdio_write_le(struct net_device *netdev, int phy_id,
+				  int loc, int val);
+static int ax8817x_mdio_read_le(struct net_device *netdev, int phy_id, int loc);
+static int ax88772b_set_csums(struct usbnet *dev);
+static int ax88772b_external_phyinit(struct usbnet *dev);
+
+/* ASIX AX8817X based USB 2.0 Ethernet Devices */
+
+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,
+		USB_CTRL_GET_TIMEOUT);
+}
+
+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,
+		USB_CTRL_SET_TIMEOUT);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+static void ax8817x_async_cmd_callback(struct urb *urb, struct pt_regs *regs)
+#else
+static void ax8817x_async_cmd_callback(struct urb *urb)
+#endif
+{
+	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 int ax8817x_set_mac_addr(struct net_device *net, void *p)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct sockaddr *addr = p;
+	int ret;
+
+	memcpy(net->dev_addr, addr->sa_data, ETH_ALEN);
+
+	/* Set the MAC address */
+	ret = ax8817x_write_cmd(dev, AX88772_CMD_WRITE_NODE_ID,
+				 0, 0, ETH_ALEN, net->dev_addr);
+	if (ret < 0)
+		return ret;
+	
+	return 0;
+}
+
+static void ax88178_status(struct usbnet *dev, struct urb *urb)
+{
+	struct ax88172_int_data *event;
+	struct ax88178_data *ax178dataptr = (struct ax88178_data *)dev->priv;
+	int link;
+
+	if (urb->actual_length < 8)
+		return;
+
+	if (ax178dataptr->EepromData == PHY_MODE_MAC_TO_MAC_GMII)
+		return;
+
+	event = urb->transfer_buffer;
+	link = event->link & 0x01;
+	if (netif_carrier_ok(dev->net) != link) {
+		if (link) {
+			netif_carrier_on(dev->net);
+			axusbnet_defer_kevent(dev, EVENT_LINK_RESET);
+		} else
+			netif_carrier_off(dev->net);
+		devwarn(dev, "ax88178 - Link status is: %d", link);
+	}
+}
+
+static void ax8817x_status(struct usbnet *dev, struct urb *urb)
+{
+	struct ax88172_int_data *event;
+	int link;
+
+	if (urb->actual_length < 8)
+		return;
+
+	event = urb->transfer_buffer;
+	link = event->link & 0x01;
+	if (netif_carrier_ok(dev->net) != link) {
+		if (link) {
+			netif_carrier_on(dev->net);
+			axusbnet_defer_kevent(dev, EVENT_LINK_RESET);
+		} else
+			netif_carrier_off(dev->net);
+		devwarn(dev, "ax8817x - Link status is: %d", link);
+	}
+}
+
+static void ax88772_status(struct usbnet *dev, struct urb *urb)
+{
+	struct ax88172_int_data *event;
+	struct ax88772_data *ax772_data = (struct ax88772_data *)dev->priv;
+	int link;
+
+	if (urb->actual_length < 8)
+		return;
+
+	event = urb->transfer_buffer;
+	link = event->link & 0x01;
+
+	if (netif_carrier_ok(dev->net) != link) {
+		if (link) {
+			netif_carrier_on(dev->net);
+			ax772_data->Event = AX_SET_RX_CFG;
+		} else {
+			netif_carrier_off(dev->net);
+			if (ax772_data->Event == AX_NOP) {
+				ax772_data->Event = PHY_POWER_DOWN;
+				ax772_data->TickToExpire = 25;
+			}
+		}
+
+		devwarn(dev, "ax88772 - Link status is: %d", link);
+	}
+
+	if (ax772_data->Event)
+		queue_work(ax772_data->ax_work, &ax772_data->check_link);
+}
+
+static void ax88772a_status(struct usbnet *dev, struct urb *urb)
+{
+	struct ax88172_int_data *event;
+	struct ax88772a_data *ax772a_data = (struct ax88772a_data *)dev->priv;
+	int link;
+	int powsave = (ax772a_data->EepromData >> 14);
+
+	if (urb->actual_length < 8)
+		return;
+
+	event = urb->transfer_buffer;
+	link = event->link & 0x01;
+
+	if (netif_carrier_ok(dev->net) != link) {
+
+		if (link) {
+			netif_carrier_on(dev->net);
+			ax772a_data->Event = AX_SET_RX_CFG;
+		} else if ((powsave == 0x3) || (powsave == 0x1)) {
+			netif_carrier_off(dev->net);
+			if (ax772a_data->Event == AX_NOP) {
+				ax772a_data->Event = CHK_CABLE_EXIST;
+				ax772a_data->TickToExpire = 14;
+			}
+		} else {
+			netif_carrier_off(dev->net);
+			ax772a_data->Event = AX_NOP;
+		}
+
+		devwarn(dev, "ax88772a - Link status is: %d", link);
+	}
+
+	if (ax772a_data->Event)
+		queue_work(ax772a_data->ax_work, &ax772a_data->check_link);
+}
+
+static int ax88772b_stop(struct usbnet *dev)
+{
+	u16 *medium;
+
+	medium = kmalloc(2, GFP_ATOMIC);
+	if (medium) {
+		ax8817x_read_cmd(dev, AX_CMD_READ_MEDIUM_MODE, 0, 0, 2, medium);
+		ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE,
+				  (*medium & ~AX88772_MEDIUM_RX_ENABLE),
+				  0, 0, NULL);
+
+		kfree(medium);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int ax88772b_reset(struct usbnet *dev)
+{
+	int ret;
+
+	/* Set the MAC address */
+	ret = ax8817x_write_cmd(dev, AX88772_CMD_WRITE_NODE_ID,
+				0, 0, ETH_ALEN, dev->net->dev_addr);
+	if (ret < 0)
+		deverr(dev, "set MAC address failed: %d", ret);
+
+	/* stop MAC operation */
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, AX_RX_CTL_STOP,
+				0, 0, NULL);
+	if (ret < 0)
+		deverr(dev, "Reset RX_CTL failed: %d", ret);
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE,
+				AX88772_MEDIUM_DEFAULT, 0, 0,
+				NULL);
+	if (ret < 0)
+		deverr(dev, "Write medium mode register: %d", ret);
+
+	return ret;
+}
+
+static void ax88772b_status(struct usbnet *dev, struct urb *urb)
+{
+	struct ax88772b_data *ax772b_data = (struct ax88772b_data *)dev->priv;
+	struct ax88172_int_data *event;
+	int link;
+
+	if (urb->actual_length < 8)
+		return;
+
+	if (ax772b_data->OperationMode == OPERATION_PHY_MODE)
+		return;
+
+	event = urb->transfer_buffer;
+	if (ax772b_data->PhySelect == 0 &&
+	    ax772b_data->OperationMode == OPERATION_MAC_MODE)
+		link = (event->link & AX_INT_SPLS_LINK) >> 1;
+	else
+		link = event->link & AX_INT_PPLS_LINK;
+
+	if (netif_carrier_ok(dev->net) != link) {
+		if (link) {
+			netif_carrier_on(dev->net);
+			ax772b_data->Event = AX_SET_RX_CFG;
+		} else {
+			netif_carrier_off(dev->net);
+			ax772b_data->time_to_chk = jiffies;
+		}
+		devwarn(dev, "ax88772b - Link status is: %d", link);
+	}
+
+	if (!link) {
+
+		int no_cable = (event->link & AX_INT_CABOFF_UNPLUG) ? 1 : 0;
+
+		if (no_cable) {
+			if ((ax772b_data->psc &
+			    (AX_SWRESET_IPPSL_0 | AX_SWRESET_IPPSL_1)) &&
+			     !ax772b_data->pw_enabled) {
+				/*
+				 * AX88772B already entered power saving state
+				 */
+				ax772b_data->pw_enabled = 1;
+			}
+			if (ax772b_data->psc & AX_SWRESET_AUTODETACH)
+				ax772b_data->Event = AX_CHK_AUTODETACH;
+
+		} else {
+			/* AX88772B resumed from power saving state */
+			if (ax772b_data->pw_enabled ||
+				(jiffies > (ax772b_data->time_to_chk +
+				 AX88772B_WATCHDOG))) {
+				if (ax772b_data->pw_enabled)
+					ax772b_data->pw_enabled = 0;
+				ax772b_data->Event = PHY_POWER_UP;
+				ax772b_data->time_to_chk = jiffies;
+			}
+		}
+	}
+
+	if (ax772b_data->Event)
+		queue_work(ax772b_data->ax_work, &ax772b_data->check_link);
+}
+
+static void ax88772c_status(struct usbnet *dev, struct urb *urb)
+{
+	struct ax88772b_data *ax772b_data = (struct ax88772b_data *)dev->priv;
+	struct ax88172_int_data *event;
+	int link;
+
+	if (urb->actual_length < 8)
+		return;
+
+	if (ax772b_data->OperationMode == OPERATION_PHY_MODE)
+		return;
+
+	event = urb->transfer_buffer;
+	if (ax772b_data->PhySelect == 0 &&
+	    ax772b_data->OperationMode == OPERATION_MAC_MODE)
+		link = (event->link & AX_INT_SPLS_LINK) >> 1;
+	else
+		link = event->link & AX_INT_PPLS_LINK;
+
+	if (netif_carrier_ok(dev->net) != link) {
+		if (link) {
+			netif_carrier_on(dev->net);
+			ax772b_data->Event = AX_SET_RX_CFG;
+		} else {
+			netif_carrier_off(dev->net);
+			ax772b_data->time_to_chk = jiffies;
+		}
+		devwarn(dev, "ax88772c - Link status is: %d", link);
+	}
+
+	if (!link) {
+
+		int no_cable = (event->link & AX_INT_CABOFF_UNPLUG) ? 1 : 0;
+
+		if (no_cable) {
+			if ((ax772b_data->psc &
+			    (AX_SWRESET_IPPSL_0 | AX_SWRESET_IPPSL_1)) &&
+			     !ax772b_data->pw_enabled) {
+				/*
+				 * AX88772B already entered power saving state
+				 */
+				ax772b_data->pw_enabled = 1;
+			}
+			if (ax772b_data->psc & AX_SWRESET_AUTODETACH)
+				ax772b_data->Event = AX_CHK_AUTODETACH;
+		} else {
+			/* AX88772B resumed from power saving state */
+			if (ax772b_data->pw_enabled ||
+				(jiffies > (ax772b_data->time_to_chk +
+				 AX88772B_WATCHDOG))) {
+				if (ax772b_data->pw_enabled)
+					ax772b_data->pw_enabled = 0;
+				ax772b_data->Event = PHY_POWER_UP;
+				ax772b_data->time_to_chk = jiffies;
+			}
+		}
+	}
+
+	if (ax772b_data->Event)
+		queue_work(ax772b_data->ax_work, &ax772b_data->check_link);
+}
+
+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;
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (urb == NULL) {
+		deverr(dev, "Error allocating URB in write_cmd_async!");
+		return;
+	}
+
+	req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
+	if (req == 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);
+
+	status = usb_submit_urb(urb, GFP_ATOMIC);
+	if (status < 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 = AX_RX_CTL_START | AX_RX_CTL_AB;
+	int mc_count;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
+	mc_count = net->mc_count;
+#else
+	mc_count = netdev_mc_count(net);
+#endif
+
+	if (net->flags & IFF_PROMISC) {
+		rx_ctl |= AX_RX_CTL_PRO;
+	} else if (net->flags & IFF_ALLMULTI
+		   || mc_count > AX_MAX_MCAST) {
+		rx_ctl |= AX_RX_CTL_AMALL;
+	} else if (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 */
+		u32 crc_bits;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
+		struct dev_mc_list *mc_list = net->mc_list;
+		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;
+		}
+#else
+		struct netdev_hw_addr *ha = NULL;
+		memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE);
+		netdev_for_each_mc_addr(ha, net) {
+			crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26;
+			data->multi_filter[crc_bits >> 3] |=
+				1 << (crc_bits & 7);
+		}
+#endif
+		ax8817x_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0,
+				   AX_MCAST_FILTER_SIZE, data->multi_filter);
+
+		rx_ctl |= AX_RX_CTL_AM;
+	}
+
+	ax8817x_write_cmd_async(dev, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL);
+}
+
+static void ax88178_set_multicast(struct net_device *net)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct ax8817x_data *data = (struct ax8817x_data *)&dev->data;
+	u16 rx_ctl = (AX_RX_CTL_START | AX_RX_CTL_AB |  AX_RX_CTL_MFB);
+	int mc_count;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
+	mc_count = net->mc_count;
+#else
+	mc_count = netdev_mc_count(net);
+#endif
+
+	if (net->flags & IFF_PROMISC) {
+		rx_ctl |= AX_RX_CTL_PRO;
+	} else if (net->flags & IFF_ALLMULTI
+		   || mc_count > AX_MAX_MCAST) {
+		rx_ctl |= AX_RX_CTL_AMALL;
+	} else if (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 */
+		u32 crc_bits;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
+		struct dev_mc_list *mc_list = net->mc_list;
+		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;
+		}
+#else
+		struct netdev_hw_addr *ha = NULL;
+		memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE);
+		netdev_for_each_mc_addr(ha, net) {
+			crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26;
+			data->multi_filter[crc_bits >> 3] |=
+				1 << (crc_bits & 7);
+		}
+#endif
+		ax8817x_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0,
+				   AX_MCAST_FILTER_SIZE, data->multi_filter);
+
+		rx_ctl |= AX_RX_CTL_AM;
+	}
+
+	ax8817x_write_cmd_async(dev, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL);
+}
+
+static void ax88772b_set_multicast(struct net_device *net)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct ax8817x_data *data = (struct ax8817x_data *)&dev->data;
+	u16 rx_ctl = (AX_RX_CTL_START | AX_RX_CTL_AB | AX_RX_HEADER_DEFAULT);
+	int mc_count;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
+	mc_count = net->mc_count;
+#else
+	mc_count = netdev_mc_count(net);
+#endif
+
+	if (net->flags & IFF_PROMISC) {
+		rx_ctl |= AX_RX_CTL_PRO;
+	} else if (net->flags & IFF_ALLMULTI
+		   || mc_count > AX_MAX_MCAST) {
+		rx_ctl |= AX_RX_CTL_AMALL;
+	} else if (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 */
+		u32 crc_bits;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
+		struct dev_mc_list *mc_list = net->mc_list;
+		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;
+		}
+#else
+		struct netdev_hw_addr *ha = NULL;
+		memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE);
+		netdev_for_each_mc_addr(ha, net) {
+			crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26;
+			data->multi_filter[crc_bits >> 3] |=
+				1 << (crc_bits & 7);
+		}
+#endif
+		ax8817x_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0,
+				   AX_MCAST_FILTER_SIZE, data->multi_filter);
+
+		rx_ctl |= AX_RX_CTL_AM;
+	}
+
+	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, ret;
+	u8* smsr;
+	int i = 0;
+
+	res = kmalloc(2, GFP_ATOMIC);
+	if (!res)
+		return 0;
+
+	do {
+		ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, NULL);
+		
+		msleep(1);
+
+		smsr = (u8*) res;
+		ax8817x_read_cmd(dev, AX_CMD_READ_STATMNGSTS_REG, 0, 0, 1, smsr);
+	} while (!(*smsr & AX_HOST_EN) && (i++ < 30));
+	
+	ax8817x_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id, (__u16)loc, 2, res);
+	ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, NULL);
+
+	ret = *res & 0xffff;
+	kfree(res);
+
+	return ret;
+}
+
+static int
+ax8817x_swmii_mdio_read(struct net_device *netdev, int phy_id, int loc)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	u16 *res;
+	u16 ret;
+
+	res = kmalloc(2, GFP_ATOMIC);
+	if (!res)
+		return 0;
+
+	ax8817x_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id,
+				(__u16)loc, 2, res);
+
+	ret = *res & 0xffff;
+	kfree(res);
+
+	return ret;
+}
+
+/* same as above, but converts resulting value to cpu byte order */
+static int ax8817x_mdio_read_le(struct net_device *netdev, int phy_id, int loc)
+{
+	return le16_to_cpu(ax8817x_mdio_read(netdev, phy_id, loc));
+}
+
+static int
+ax8817x_swmii_mdio_read_le(struct net_device *netdev, int phy_id, int loc)
+{
+	return le16_to_cpu(ax8817x_swmii_mdio_read(netdev, phy_id, loc));
+}
+
+static void
+ax8817x_mdio_write(struct net_device *netdev, int phy_id, int loc, int val)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	u16 *res;
+	u8* smsr;
+	int i = 0;
+
+	res = kmalloc(2, GFP_ATOMIC);
+	if (!res)
+		return;
+	smsr = (u8 *) res;
+
+	do {
+		ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, NULL);
+
+		msleep(1);
+
+		ax8817x_read_cmd(dev, AX_CMD_READ_STATMNGSTS_REG, 0, 0, 1, smsr);
+	} while (!(*smsr & AX_HOST_EN) && (i++ < 30));	
+	
+	*res = val;
+
+	ax8817x_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id,
+			  (__u16)loc, 2, res);
+	ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, NULL);
+
+	kfree(res);
+}
+
+static void ax8817x_swmii_mdio_write(struct net_device *netdev, int phy_id,
+				     int loc, int val)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	u16 *res;
+
+	res = kmalloc(2, GFP_ATOMIC);
+	if (!res)
+		return;
+	*res = val;
+
+	ax8817x_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id,
+				(__u16)loc, 2, res);
+
+	kfree(res);
+}
+
+static void
+ax88772b_mdio_write(struct net_device *netdev, int phy_id, int loc, int val)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	u16 *res;
+
+	res = kmalloc(2, GFP_ATOMIC);
+	if (!res)
+		return;
+	*res = val;
+
+	ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, NULL);
+	ax8817x_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id,
+				(__u16)loc, 2, res);
+
+	if (loc == MII_ADVERTISE) {
+		*res = cpu_to_le16(BMCR_ANENABLE | BMCR_ANRESTART);
+		ax8817x_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id,
+				(__u16)MII_BMCR, 2, res);
+	}
+
+	ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, NULL);
+
+	kfree(res);
+}
+
+/* same as above, but converts new value to le16 byte order before writing */
+static void
+ax8817x_mdio_write_le(struct net_device *netdev, int phy_id, int loc, int val)
+{
+	ax8817x_mdio_write(netdev, phy_id, loc, cpu_to_le16(val));
+}
+
+static void ax8817x_swmii_mdio_write_le(struct net_device *netdev,
+			int phy_id, int loc, int val)
+{
+	ax8817x_swmii_mdio_write(netdev, phy_id, loc, cpu_to_le16(val));
+}
+
+static void
+ax88772b_mdio_write_le(struct net_device *netdev, int phy_id, int loc, int val)
+{
+	ax88772b_mdio_write(netdev, phy_id, loc, cpu_to_le16(val));
+}
+
+static int ax88772_suspend(struct usb_interface *intf,
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 10)
+			   pm_message_t message)
+#else
+			   u32 message)
+#endif
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	u16 *medium;
+
+	medium = kmalloc(2, GFP_ATOMIC);
+	if (!medium)
+		return axusbnet_suspend(intf, message);
+
+	ax8817x_read_cmd(dev, AX_CMD_READ_MEDIUM_MODE, 0, 0, 2, medium);
+	ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE,
+			(*medium & ~AX88772_MEDIUM_RX_ENABLE), 0, 0, NULL);
+
+	kfree(medium);
+	return axusbnet_suspend(intf, message);
+}
+
+static int ax88772b_suspend(struct usb_interface *intf,
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 10)
+			    pm_message_t message)
+#else
+			    u32 message)
+#endif
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	struct ax88772b_data *ax772b_data = (struct ax88772b_data *)dev->priv;
+	u16 *tmp16;
+	u8 *opt;
+
+	tmp16 = kmalloc(2, GFP_ATOMIC);
+	if (!tmp16)
+		return axusbnet_suspend(intf, message);
+	opt = (u8 *)tmp16;
+#if 0
+	/* Read Wake-up Frame Array Register (Mask Wakeup Timer) */
+	ax8817x_read_cmd(dev, AX_CMD_READ_WKFARY, 0x11, 0, 4, &tmp32);
+	tmp32 &= 0xFFF0FFFF;
+	/* 8 second */
+	tmp32 |= 0x00020000;
+	ax8817x_write_cmd(dev, AX_CMD_WRITE_WKFARY, 0x11, 0, 4, &tmp32);
+#endif
+	/* Preserve BMCR for restoring */
+	ax772b_data->presvd_phy_bmcr = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_BMCR);
+
+	/* Preserve Advertisement control reg for restoring */
+	ax772b_data->presvd_phy_advertise = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_ADVERTISE);
+
+	ax8817x_read_cmd(dev, AX_CMD_READ_MEDIUM_MODE, 0, 0, 2, tmp16);
+	ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE,
+			  (*tmp16 & ~AX88772_MEDIUM_RX_ENABLE),
+			  0, 0, NULL);
+
+	ax8817x_read_cmd(dev, AX_CMD_READ_MONITOR_MODE, 0, 0, 1, opt);
+	if (!(*opt & AX_MONITOR_LINK) && !(*opt & AX_MONITOR_MAGIC)) {
+		ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+				  AX_SWRESET_IPRL | AX_SWRESET_IPPD,
+				  0, 0, NULL);
+				  
+	} else {
+
+		if (ax772b_data->psc & AX_SWRESET_WOLLP) {
+			*tmp16 = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id,
+						      MII_BMCR);
+			ax8817x_mdio_write_le(dev->net, dev->mii.phy_id,
+					      MII_BMCR, *tmp16 | BMCR_ANENABLE);
+
+			ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+					  AX_SWRESET_IPRL | ax772b_data->psc,
+					  0, 0, NULL);
+		}
+
+		if (ax772b_data->psc &
+		    (AX_SWRESET_IPPSL_0 | AX_SWRESET_IPPSL_1)) {
+			*opt |= AX_MONITOR_LINK;
+			ax8817x_write_cmd(dev, AX_CMD_WRITE_MONITOR_MODE, *opt,
+					  0, 0, NULL);
+		}
+	}
+
+	kfree(tmp16);
+	return axusbnet_suspend(intf, message);
+}
+
+static int ax88772_resume(struct usb_interface *intf)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+
+	netif_carrier_off(dev->net);
+
+	return axusbnet_resume(intf);
+}
+
+static int ax88772b_resume(struct usb_interface *intf)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	struct ax88772b_data *ax772b_data = (struct ax88772b_data *)dev->priv;
+
+	if (ax772b_data->psc & AX_SWRESET_WOLLP) {
+		ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+				  AX_SWRESET_IPRL | (ax772b_data->psc & 0x7FFF),
+				  0, 0, NULL);
+	}
+
+	if (ax772b_data->psc & (AX_SWRESET_IPPSL_0 | AX_SWRESET_IPPSL_1))
+		ax88772a_phy_powerup(dev);
+
+	netif_carrier_off(dev->net);
+
+	if (ax772b_data->OperationMode == OPERATION_PHY_MODE)
+		netif_carrier_on(dev->net);
+
+	return axusbnet_resume(intf);
+}
+
+static int ax88172_link_reset(struct usbnet *dev)
+{
+	u16 lpa;
+	u16 adv;
+	u16 res;
+	u8 mode;
+
+	mode = AX_MEDIUM_TX_ABORT_ALLOW | AX_MEDIUM_FLOW_CONTROL_EN;
+	lpa = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_LPA);
+	adv = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_ADVERTISE);
+	res = mii_nway_result(lpa|adv);
+	if (res & LPA_DUPLEX)
+		mode |= AX_MEDIUM_FULL_DUPLEX;
+	ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL);
+
+	return 0;
+}
+
+static void
+ax8817x_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u8 *opt;
+
+	wolinfo->supported = 0;
+	wolinfo->wolopts = 0;
+
+	opt = kmalloc(1, GFP_KERNEL);
+	if (!opt)
+		return;
+
+	if (ax8817x_read_cmd(dev, AX_CMD_READ_MONITOR_MODE, 0, 0, 1, opt) < 0) {
+		kfree(opt);
+		return;
+	}
+
+	wolinfo->supported = WAKE_PHY | WAKE_MAGIC;
+
+	if (*opt & AX_MONITOR_LINK)
+		wolinfo->wolopts |= WAKE_PHY;
+	if (*opt & AX_MONITOR_MAGIC)
+		wolinfo->wolopts |= WAKE_MAGIC;
+
+	kfree(opt);
+}
+
+static int
+ax8817x_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u8 *opt;
+
+	opt = kmalloc(1, GFP_KERNEL);
+	if (!opt)
+		return -ENOMEM;
+
+	*opt = 0;
+	if (wolinfo->wolopts & WAKE_PHY)
+		*opt |= AX_MONITOR_LINK;
+	if (wolinfo->wolopts & WAKE_MAGIC)
+		*opt |= AX_MONITOR_MAGIC;
+
+	ax8817x_write_cmd(dev, AX_CMD_WRITE_MONITOR_MODE, *opt, 0, 0, NULL);
+
+	kfree(opt);
+	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 */
+	axusbnet_get_drvinfo(net, info);
+	info->eedump_len = 0x3e;
+}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
+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);
+}
+#else
+static int ax8817x_get_link_ksettings(struct net_device *net,
+				      struct ethtool_link_ksettings *cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+
+	if (!dev->mii.mdio_read)
+		return -EOPNOTSUPP;
+	
+	mii_ethtool_get_link_ksettings(&dev->mii, cmd);	
+
+	return 0; 
+}
+
+static int ax8817x_set_link_ksettings(struct net_device *net,
+				      const struct ethtool_link_ksettings *cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+
+	if (!dev->mii.mdio_write)
+		return -EOPNOTSUPP;
+
+	return mii_ethtool_set_link_ksettings(&dev->mii, cmd);
+}
+#endif
+/* 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		= axusbnet_get_msglevel,
+	.set_msglevel		= axusbnet_set_msglevel,
+	.get_wol		= ax8817x_get_wol,
+	.set_wol		= ax8817x_set_wol,
+	.get_eeprom_len	= ax8817x_get_eeprom_len,
+	.get_eeprom		= ax8817x_get_eeprom,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
+	.get_settings		= ax8817x_get_settings,
+	.set_settings		= ax8817x_set_settings,
+#else
+	.get_link_ksettings	= ax8817x_get_link_ksettings,
+	.set_link_ksettings	= ax8817x_set_link_ksettings,
+#endif
+};
+
+int ioctl_signature(struct usbnet *dev, AX_IOCTL_COMMAND *info)
+{
+	strncpy(info->sig, AX88772B_SIGNATURE, strlen(AX88772B_SIGNATURE));
+
+	return 0;
+}
+
+static void debug_func(const char *func_name, unsigned short *buf, unsigned long wLen)
+{
+#if (DEBUG_PARAM & DEB_DRIVER)
+	int i;
+	char str_buf[50];
+	printk("%s :\n", func_name);
+	printk("---------------------------------------\n");
+	for (i = 0; i < wLen / 8; i++) {
+		int j = 8 * i;
+		snprintf(str_buf, 50,
+				 "%04x %04x %04x %04x %04x %04x "
+				 "%04x %04x\n", 
+				 *(buf + j + 0), *(buf + j + 1),
+				 *(buf + j + 2), *(buf + j + 3),
+				 *(buf + j + 4), *(buf + j + 5),
+				 *(buf + j + 6), *(buf + j + 7));
+		printk("%s", str_buf);
+	}
+	printk("------------------------------------%3ld\n", wLen);
+#endif
+}
+
+int ioctl_read_eeprom(struct usbnet *dev, AX_IOCTL_COMMAND *info)
+{
+	int i, retval = 0;
+	u16 * buf;	
+
+	if (down_interruptible(&dev->sem)) { 		
+		return -ERESTARTSYS;
+	}
+
+	buf = kmalloc((sizeof(u16) * info->size), GFP_KERNEL);
+	if (!buf) {		
+		return -ERESTARTSYS;
+	}
+
+	for (i = 0; i < info->size; i++) {
+		retval = ax8817x_read_cmd(dev, AX_CMD_READ_EEPROM, i, 
+							0, 2, &buf[i]);
+
+		if(retval < 0) {
+			kfree(buf);			
+			up(&dev->sem);
+			return retval;
+		}
+		buf[i] = be16_to_cpu(buf[i]);	
+	}
+
+	if (copy_to_user(info->buf, buf, (sizeof(unsigned short) * info->size))) {
+			retval = -EFAULT;
+	}
+	kfree(buf);	
+	up(&dev->sem);
+
+	return retval;
+	
+}
+
+int ioctl_write_eeprom(struct usbnet *dev, AX_IOCTL_COMMAND *info)
+{
+	int i, retval;
+	u16 * buf;
+	
+	if (down_interruptible(&dev->sem))
+		return -ERESTARTSYS;
+
+	buf = kmalloc((sizeof(u16) * info->size), GFP_KERNEL);
+	if (!buf) {		
+		return -ERESTARTSYS;
+	}
+
+	if (copy_from_user(buf, info->buf, (sizeof(u16) * info->size))) {
+		kfree(buf);
+		up(&dev->sem);
+		return -EFAULT;
+	}
+
+	retval = ax8817x_write_cmd(dev, AX_CMD_WRITE_EEPROM_EN, 0,
+							0, 0, NULL);
+	if (retval < 0) {
+		kfree(buf);
+		up(&dev->sem);
+		return retval;
+	}
+
+	mdelay(info->delay);
+
+	for (i = 0; i < info->size; i++){
+		retval = ax8817x_write_cmd(dev, AX_CMD_WRITE_EEPROM, i,
+				cpu_to_be16(buf[i]), 0, NULL);
+
+		if (retval < 0) {
+			DPRINT_D("Failed to command Write EEPROM(retval:%d, offset:%d)\n", retval, i);			
+			kfree(buf);
+			up(&dev->sem);	
+			return retval;
+		}
+		mdelay(info->delay);
+	}
+
+	debug_func(__FUNCTION__, buf, i);
+
+	retval = ax8817x_write_cmd(dev, AX_CMD_WRITE_EEPROM_DIS, 0,
+							0, 0, NULL);
+	if (retval < 0) {
+		kfree(buf);
+		up(&dev->sem);	
+		return retval;
+	}
+
+	mdelay(info->delay);
+
+	retval = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS, AXGPIOS_RSE, 0, 0, NULL);
+
+	mdelay(info->delay);
+
+	kfree(buf);
+	up(&dev->sem);
+
+	return retval;
+}
+
+typedef int (*IOCTRL_TABLE)(struct usbnet *dev, AX_IOCTL_COMMAND *info);
+
+IOCTRL_TABLE ioctl_tbl[] = {
+	ioctl_signature,        //AX_SIGNATURE
+	ioctl_read_eeprom,
+	ioctl_write_eeprom,
+};
+
+static int ax8817x_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+	
+	AX_IOCTL_COMMAND info;
+	AX_IOCTL_COMMAND *uptr = (AX_IOCTL_COMMAND*) rq->ifr_data;
+	int private_cmd;
+
+	switch (cmd) { 
+	case AX_PRIVATE :
+		if (copy_from_user(&info, uptr, sizeof(AX_IOCTL_COMMAND))) {
+			return -EFAULT;
+		}
+
+		private_cmd = info.ioctl_cmd;
+		if ((*ioctl_tbl[private_cmd])(dev,&info) < 0) {
+			return -EFAULT;
+		}
+		
+		if (copy_to_user(uptr, &info, sizeof(AX_IOCTL_COMMAND))) {
+			return -EFAULT;
+		}
+
+		break;
+
+	default : 
+		return  generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
+	}
+	return 0;
+}
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
+static const struct net_device_ops ax88x72_netdev_ops = {
+	.ndo_open			= axusbnet_open,
+	.ndo_stop			= axusbnet_stop,
+	.ndo_start_xmit	= axusbnet_start_xmit,
+	.ndo_tx_timeout	= axusbnet_tx_timeout,
+	.ndo_change_mtu	= axusbnet_change_mtu,
+	.ndo_get_stats		= axusbnet_get_stats,
+	.ndo_do_ioctl		= ax8817x_ioctl,
+	.ndo_set_mac_address		= ax8817x_set_mac_addr,
+	.ndo_validate_addr		= eth_validate_addr,
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 2, 0)
+	.ndo_set_multicast_list	= ax8817x_set_multicast,
+#else
+	.ndo_set_rx_mode	= ax8817x_set_multicast,
+#endif
+};
+#endif
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
+static const struct net_device_ops ax88178_netdev_ops = {
+	.ndo_open			= axusbnet_open,
+	.ndo_stop			= axusbnet_stop,
+	.ndo_start_xmit	= axusbnet_start_xmit,
+	.ndo_tx_timeout	= axusbnet_tx_timeout,
+	.ndo_change_mtu	= axusbnet_change_mtu,
+	.ndo_get_stats		= axusbnet_get_stats,
+	.ndo_do_ioctl		= ax8817x_ioctl,
+	.ndo_set_mac_address		= ax8817x_set_mac_addr,
+	.ndo_validate_addr		= eth_validate_addr,
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 2, 0)
+	.ndo_set_multicast_list	= ax88178_set_multicast,
+#else
+	.ndo_set_rx_mode	= ax88178_set_multicast,
+#endif
+};
+#endif
+
+static int access_eeprom_mac(struct usbnet *dev, u8 *buf, u8 offset, bool wflag)
+{
+	int ret = 0, i;
+	u16* tmp = (u16*)buf;
+
+	if (wflag) {
+		ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_EEPROM_EN,
+						0, 0, 0, NULL);
+		if (ret < 0)
+			 return ret;
+
+		mdelay(15);
+	}
+
+	for (i = 0; i < (ETH_ALEN >> 1); i++) {
+		if (wflag) {
+			u16 wd = cpu_to_le16(*(tmp + i));
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_EEPROM, offset + i,
+						wd, 0, NULL);
+			if (ret < 0)
+				break;
+
+			mdelay(15);
+		}
+		else {
+			ret = ax8817x_read_cmd(dev, AX_CMD_READ_EEPROM,
+					       offset + i, 0, 2, tmp + i);
+			if (ret < 0)
+				break;
+		}
+	}
+
+	if (!wflag) {
+		if (ret < 0) {
+			#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)
+				netdev_dbg(dev->net, "Failed to read MAC address from EEPROM: %d\n", ret);
+			#else
+				devdbg(dev, "Failed to read MAC address from EEPROM: %d\n", ret);
+			#endif
+			return ret;
+		}
+		memcpy(dev->net->dev_addr, buf, ETH_ALEN);
+	}
+	else {
+		ax8817x_write_cmd(dev, AX_CMD_WRITE_EEPROM_DIS,
+				  0, 0, 0, NULL);
+		if (ret < 0)
+			 return ret;
+
+		/* reload eeprom data */
+		ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+					AXGPIOS_RSE, 0, 0, NULL);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int ax8817x_check_ether_addr(struct usbnet *dev)
+{
+	unsigned char *tmp = (unsigned char*)dev->net->dev_addr;
+	u8 default_mac[6] = {0, 0x0e, 0xc6, 0x87, 0x72, 0x01};
+
+	if (((*((u8*)tmp) == 0) && (*((u8*)tmp + 1) == 0) && (*((u8*)tmp + 2) == 0)) ||
+	    !is_valid_ether_addr((u8*)tmp) ||
+	    !memcmp(dev->net->dev_addr, default_mac, ETH_ALEN)) {
+		printk("Found invalid EEPROM MAC address value ");		
+		printk("%02X-%02X-%02X-%02X-%02X-%02X\n", *((u8*)tmp + 0),
+							*((u8*)tmp + 1),
+							*((u8*)tmp + 2),
+							*((u8*)tmp + 3),
+							*((u8*)tmp + 4),
+							*((u8*)tmp + 5));
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
+		eth_hw_addr_random(dev->net);
+#else
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
+		dev->net->addr_assign_type |= NET_ADDR_RANDOM;
+#endif
+		random_ether_addr(dev->net->dev_addr); 
+#endif
+		*tmp = 0;
+		*(tmp + 1) = 0x0E;
+		*(tmp + 2) = 0xC6;
+		*(tmp + 3) = 0x8F;
+
+		return -EADDRNOTAVAIL;	
+	} 
+	return 0;
+}
+
+static int ax8817x_get_mac(struct usbnet *dev, u8* buf)
+{
+	int ret, i;
+	
+
+	ret = access_eeprom_mac(dev, buf, 0x04, 0);
+	if (ret < 0)
+		goto out;
+
+	if (ax8817x_check_ether_addr(dev)) {
+		ret = access_eeprom_mac(dev, dev->net->dev_addr, 0x04, 1);
+		if (ret < 0) {
+			deverr(dev, "Failed to write MAC to EEPROM: %d", ret);
+			goto out;
+		}
+
+		msleep(5);
+
+		ret = ax8817x_read_cmd(dev, AX88772_CMD_READ_NODE_ID,
+				       0, 0, ETH_ALEN, buf);
+		if (ret < 0) {
+			deverr(dev, "Failed to read MAC address: %d", ret);
+			goto out;
+		}
+
+		for (i = 0; i < ETH_ALEN; i++)
+			if (*(dev->net->dev_addr + i) != *((u8*)buf + i)) {
+				devwarn(dev, "Found invalid EEPROM part or non-EEPROM");
+				break;
+			}
+	}
+
+	memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN);
+
+	/* Set the MAC address */
+	ax8817x_write_cmd (dev, AX88772_CMD_WRITE_NODE_ID, 0, 0,
+			   ETH_ALEN, dev->net->dev_addr);
+	
+	if (ret < 0) {
+		deverr(dev, "Failed to write MAC address: %d", ret);
+		goto out;
+	}
+
+	return 0;
+out:
+	return ret;
+}
+
+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;
+
+	axusbnet_get_endpoints(dev, intf);
+
+	buf = kmalloc(ETH_ALEN, GFP_KERNEL);
+	if (!buf) {
+		ret = -ENOMEM;
+		goto out1;
+	}
+
+	/* Toggle the GPIOs in a manufacturer/model specific way */
+	for (i = 2; i >= 0; i--) {
+		ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+					(gpio_bits >> (i * 8)) & 0xff,
+					0, 0, NULL);
+		if (ret < 0)
+			goto out2;
+
+		msleep(5);
+	}
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, 0x80, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "send AX_CMD_WRITE_RX_CTL failed: %d", ret);
+		goto out2;
+	}
+
+	/* Get the MAC address */
+	memset(buf, 0, ETH_ALEN);
+	ret = ax8817x_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, 6, buf);
+	if (ret < 0) {
+		deverr(dev, "read AX_CMD_READ_NODE_ID failed: %d", ret);
+		goto out2;
+	}
+	memcpy(dev->net->dev_addr, buf, ETH_ALEN);
+
+	/* Get the PHY id */
+	ret = ax8817x_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf);
+	if (ret < 0) {
+		deverr(dev, "error on read AX_CMD_READ_PHY_ID: %02x", ret);
+		goto out2;
+	} else if (ret < 2) {
+		/* this should always return 2 bytes */
+		deverr(dev, "Read PHYID returned less than 2 bytes: ret=%02x",
+				ret);
+		ret = -EIO;
+		goto out2;
+	}
+
+	/* Initialize MII structure */
+	dev->mii.dev = dev->net;
+	dev->mii.mdio_read = ax8817x_mdio_read_le;
+	dev->mii.mdio_write = ax8817x_mdio_write_le;
+	dev->mii.phy_id_mask = 0x3f;
+	dev->mii.reg_num_mask = 0x1f;
+	dev->mii.phy_id = *((u8 *)buf + 1);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
+	dev->net->do_ioctl = ax8817x_ioctl;
+	dev->net->set_multicast_list = ax8817x_set_multicast;
+	dev->net->set_mac_address = ax8817x_set_mac_addr;
+#else
+	dev->net->netdev_ops = &ax88x72_netdev_ops;
+#endif
+
+	dev->net->ethtool_ops = &ax8817x_ethtool_ops;
+
+	/* Register suspend and resume functions */
+	data->suspend = axusbnet_suspend;
+	data->resume = axusbnet_resume;
+
+	ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
+	ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+		ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
+	mii_nway_restart(&dev->mii);
+
+	printk(version);
+
+	return 0;
+out2:
+	kfree(buf);
+out1:
+	return ret;
+}
+
+static struct ethtool_ops ax88772_ethtool_ops = {
+	.get_drvinfo		= ax8817x_get_drvinfo,
+	.get_link		= ethtool_op_get_link,
+	.get_msglevel		= axusbnet_get_msglevel,
+	.set_msglevel		= axusbnet_set_msglevel,
+	.get_wol		= ax8817x_get_wol,
+	.set_wol		= ax8817x_set_wol,
+	.get_eeprom_len		= ax8817x_get_eeprom_len,
+	.get_eeprom		= ax8817x_get_eeprom,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
+	.get_settings		= ax8817x_get_settings,
+	.set_settings		= ax8817x_set_settings,
+#else
+	.get_link_ksettings	= ax8817x_get_link_ksettings,
+	.set_link_ksettings	= ax8817x_set_link_ksettings,
+#endif
+};
+
+static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	int ret;
+	void *buf;
+	struct ax8817x_data *data = (struct ax8817x_data *)&dev->data;
+	struct ax88772_data *ax772_data = NULL;
+
+	axusbnet_get_endpoints(dev, intf);
+
+	buf = kmalloc(6, GFP_KERNEL);
+	if (!buf) {
+		deverr(dev, "Cannot allocate memory for buffer");
+		ret = -ENOMEM;
+		goto out1;
+	}
+
+	ax772_data = kmalloc(sizeof(*ax772_data), GFP_KERNEL);
+	if (!ax772_data) {
+		deverr(dev, "Cannot allocate memory for AX88772 data");
+		kfree(buf);
+		return -ENOMEM;
+	}
+
+	memset(ax772_data, 0, sizeof(*ax772_data));
+	dev->priv = ax772_data;
+
+	ax772_data->ax_work = create_singlethread_workqueue("ax88772");
+	if (!ax772_data->ax_work) {
+		kfree(ax772_data);
+		kfree(buf);
+		return -ENOMEM;
+	}
+
+	ax772_data->dev = dev;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+	INIT_WORK(&ax772_data->check_link, ax88772_link_reset, dev);
+#else
+	INIT_WORK(&ax772_data->check_link, ax88772_link_reset);
+#endif
+
+	/* reload eeprom data */
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS, 0x00B0, 0, 0, NULL);
+	if (ret < 0)
+		goto out2;
+
+	msleep(5);
+
+	/* Initialize MII structure */
+	dev->mii.dev = dev->net;
+	dev->mii.mdio_read = ax8817x_mdio_read_le;
+	dev->mii.mdio_write = ax8817x_mdio_write_le;
+	dev->mii.phy_id_mask = 0xff;
+	dev->mii.reg_num_mask = 0xff;
+
+	/* Get the PHY id */
+	ret = ax8817x_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf);
+	if (ret < 0) {
+		deverr(dev, "Error reading PHY ID: %02x", ret);
+		goto out2;
+	} else if (ret < 2) {
+		/* this should always return 2 bytes */
+		deverr(dev, "Read PHYID returned less than 2 bytes: ret=%02x",
+		    ret);
+		ret = -EIO;
+		goto out2;
+	}
+	dev->mii.phy_id = *((u8 *)buf + 1);
+
+	if (dev->mii.phy_id == 0x10) {
+		ret = ax8817x_write_cmd(dev, AX_CMD_SW_PHY_SELECT,
+					0x0001, 0, 0, NULL);
+		if (ret < 0) {
+			deverr(dev, "Select PHY #1 failed: %d", ret);
+			goto out2;
+		}
+
+		ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPPD,
+					0, 0, NULL);
+		if (ret < 0) {
+			deverr(dev, "Failed to power down PHY: %d", ret);
+			goto out2;
+		}
+
+		msleep(150);
+
+		ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_CLEAR,
+					0, 0, NULL);
+		if (ret < 0) {
+			deverr(dev, "Failed to perform software reset: %d",
+			       ret);
+			goto out2;
+		}
+
+		msleep(150);
+
+		ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+					AX_SWRESET_IPRL | AX_SWRESET_PRL,
+					0, 0, NULL);
+		if (ret < 0) {
+			deverr(dev,
+			      "Failed to set PHY reset control: %d", ret);
+			goto out2;
+		}
+	} else {
+		ret = ax8817x_write_cmd(dev, AX_CMD_SW_PHY_SELECT,
+					0x0000, 0, 0, NULL);
+		if (ret < 0) {
+			deverr(dev, "Select PHY #1 failed: %d", ret);
+			goto out2;
+		}
+
+		ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+					AX_SWRESET_IPPD | AX_SWRESET_PRL,
+					0, 0, NULL);
+		if (ret < 0) {
+			deverr(dev, "Failed to power down internal PHY: %d",
+			       ret);
+			goto out2;
+		}
+	}
+
+	msleep(150);
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL,
+				0x0000, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Failed to reset RX_CTL: %d", ret);
+		goto out2;
+	}
+
+	/* Get the MAC address */
+	memset(buf, 0, ETH_ALEN);
+	ret = ax8817x_get_mac(dev, buf);
+	if (ret < 0) {
+		deverr(dev, "Get HW address failed: %d", ret);
+		goto out2;
+	}
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Enabling software MII failed: %d", ret);
+		goto out2;
+	}
+
+	if (dev->mii.phy_id == 0x10) {
+		ret = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, 2);
+		if (ret != 0x003b) {
+			deverr(dev, "Read PHY register 2 must be 0x3b00: %d",
+			       ret);
+			goto out2;
+		}
+
+		ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_PRL,
+					0, 0, NULL);
+		if (ret < 0) {
+			deverr(dev, "Set external PHY reset pin level: %d",
+			       ret);
+			goto out2;
+		}
+		msleep(150);
+
+		ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+					AX_SWRESET_IPRL | AX_SWRESET_PRL,
+					0, 0, NULL);
+		if (ret < 0) {
+			deverr(dev,
+			       "Set Internal/External PHY reset control: %d",
+			       ret);
+			goto out2;
+		}
+		msleep(150);
+	}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
+	dev->net->do_ioctl = ax8817x_ioctl;
+	dev->net->set_multicast_list = ax8817x_set_multicast;
+	dev->net->set_mac_address = ax8817x_set_mac_addr;
+#else
+	dev->net->netdev_ops = &ax88x72_netdev_ops;
+#endif
+
+	dev->net->ethtool_ops = &ax88772_ethtool_ops;
+
+	/* Register suspend and resume functions */
+	data->suspend = ax88772_suspend;
+	data->resume = ax88772_resume;
+
+	ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
+	ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+			ADVERTISE_ALL | ADVERTISE_CSMA);
+
+	mii_nway_restart(&dev->mii);
+	ax772_data->autoneg_start = jiffies;
+	ax772_data->Event = WAIT_AUTONEG_COMPLETE;
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, 0, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Write medium mode register: %d", ret);
+		goto out2;
+	}
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_IPG0,
+				AX88772_IPG0_DEFAULT |
+				(AX88772_IPG1_DEFAULT << 8),
+				AX88772_IPG2_DEFAULT, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Write IPG,IPG1,IPG2 failed: %d", ret);
+		goto out2;
+	}
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Failed to set hardware MII: %02x", ret);
+		goto out2;
+	}
+
+	/* Set RX_CTL to default values with 2k buffer, and enable cactus */
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, 0x0088, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Reset RX_CTL failed: %d", ret);
+		goto out2;
+	}
+
+	/* Asix framing packs multiple eth frames into a 2K usb bulk transfer */
+	if (dev->driver_info->flags & FLAG_FRAMING_AX) {
+		/* hard_mtu  is still the default - the device does not support
+		   jumbo eth frames */
+		dev->rx_urb_size = 2048;
+	}
+
+	kfree(buf);
+	printk(version);
+	return 0;
+
+out2:
+	destroy_workqueue(ax772_data->ax_work);
+	kfree(ax772_data);
+	kfree(buf);
+out1:
+	return ret;
+}
+
+static void ax88772_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+	struct ax88772_data *ax772_data = (struct ax88772_data *)dev->priv;
+
+	if (ax772_data) {
+		flush_workqueue(ax772_data->ax_work);
+		destroy_workqueue(ax772_data->ax_work);
+
+		/* stop MAC operation */
+		ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, AX_RX_CTL_STOP,
+				  0, 0, NULL);
+
+		/* Power down PHY */
+		ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPPD,
+				  0, 0, NULL);
+
+		kfree(ax772_data);
+	}
+}
+
+static int ax88772a_phy_powerup(struct usbnet *dev)
+{
+	int ret;
+	/* set the embedded Ethernet PHY in power-down state */
+	ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+				AX_SWRESET_IPPD | AX_SWRESET_IPRL, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Failed to power down PHY: %d", ret);
+		return ret;
+	}
+
+	msleep(10);
+
+	/* set the embedded Ethernet PHY in power-up state */
+	ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPRL,
+				0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Failed to reset PHY: %d", ret);
+		return ret;
+	}
+
+	msleep(600);
+
+	/* set the embedded Ethernet PHY in reset state */
+	ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_CLEAR,
+				0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Failed to power up PHY: %d", ret);
+		return ret;
+	}
+
+	/* set the embedded Ethernet PHY in power-up state */
+	ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPRL,
+				0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Failed to reset PHY: %d", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ax88772a_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	int ret = -EIO;
+	void *buf;
+	struct ax8817x_data *data = (struct ax8817x_data *)&dev->data;
+	struct ax88772a_data *ax772a_data = NULL;
+
+	printk(version);
+
+	axusbnet_get_endpoints(dev, intf);
+
+	buf = kmalloc(6, GFP_KERNEL);
+	if (!buf) {
+		deverr(dev, "Cannot allocate memory for buffer");
+		ret = -ENOMEM;
+		goto out1;
+	}
+
+	ax772a_data = kmalloc(sizeof(*ax772a_data), GFP_KERNEL);
+	if (!ax772a_data) {
+		deverr(dev, "Cannot allocate memory for AX88772A data");
+		kfree(buf);
+		return -ENOMEM;
+	}
+	memset(ax772a_data, 0, sizeof(*ax772a_data));
+	dev->priv = ax772a_data;
+
+	ax772a_data->ax_work = create_singlethread_workqueue("ax88772a");
+	if (!ax772a_data->ax_work) {
+		kfree(ax772a_data);
+		kfree(buf);
+		return -ENOMEM;
+	}
+
+	ax772a_data->dev = dev;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+	INIT_WORK(&ax772a_data->check_link, ax88772a_link_reset, dev);
+#else
+	INIT_WORK(&ax772a_data->check_link, ax88772a_link_reset);
+#endif
+
+	/* Get the EEPROM data*/
+	ret = ax8817x_read_cmd(dev, AX_CMD_READ_EEPROM, 0x0017, 0, 2,
+			       (void *)&ax772a_data->EepromData);
+	if (ret < 0) {
+		deverr(dev, "read SROM address 17h failed: %d", ret);
+		goto out2;
+	}
+	le16_to_cpus(&ax772a_data->EepromData);
+	/* End of get EEPROM data */
+
+	/* reload eeprom data */
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+				AXGPIOS_RSE, 0, 0, NULL);
+	if (ret < 0)
+		goto out2;
+
+	msleep(5);
+
+	/* Initialize MII structure */
+	dev->mii.dev = dev->net;
+	dev->mii.mdio_read = ax8817x_mdio_read_le;
+	dev->mii.mdio_write = ax8817x_mdio_write_le;
+	dev->mii.phy_id_mask = 0xff;
+	dev->mii.reg_num_mask = 0xff;
+
+	/* Get the PHY id */
+	ret = ax8817x_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf);
+	if (ret < 0) {
+		deverr(dev, "Error reading PHY ID: %02x", ret);
+		goto out2;
+	} else if (ret < 2) {
+		/* this should always return 2 bytes */
+		deverr(dev, "Read PHYID returned less than 2 bytes: ret=%02x",
+			ret);
+		goto out2;
+	}
+	dev->mii.phy_id = *((u8 *)buf + 1);
+
+	if (dev->mii.phy_id != 0x10) {
+		deverr(dev, "Got wrong PHY ID: %02x", dev->mii.phy_id);
+		goto out2;
+	}
+
+	/* select the embedded 10/100 Ethernet PHY */
+	ret = ax8817x_write_cmd(dev, AX_CMD_SW_PHY_SELECT,
+			AX_PHYSEL_SSEN | AX_PHYSEL_PSEL | AX_PHYSEL_SSMII,
+			0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Select PHY #1 failed: %d", ret);
+		goto out2;
+	}
+
+	ret = ax88772a_phy_powerup(dev);
+	if (ret < 0)
+		goto out2;
+
+	/* stop MAC operation */
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, AX_RX_CTL_STOP,
+				0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Reset RX_CTL failed: %d", ret);
+		goto out2;
+	}
+
+	/* Get the MAC address */
+	memset(buf, 0, ETH_ALEN);
+	ret = ax8817x_get_mac(dev, buf);
+	if (ret < 0) {
+		deverr(dev, "Get HW address failed: %d", ret);
+		goto out2;
+	}	
+
+	/* make sure the driver can enable sw mii operation */
+	ret = ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Enabling software MII failed: %d", ret);
+		goto out2;
+	}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
+	dev->net->do_ioctl = ax8817x_ioctl;
+	dev->net->set_multicast_list = ax8817x_set_multicast;
+	dev->net->set_mac_address = ax8817x_set_mac_addr;
+#else
+	dev->net->netdev_ops = &ax88x72_netdev_ops;
+#endif
+
+	dev->net->ethtool_ops = &ax88772_ethtool_ops;
+
+	/* Register suspend and resume functions */
+	data->suspend = ax88772_suspend;
+	data->resume = ax88772_resume;
+
+	ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
+	ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+			ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
+
+	mii_nway_restart(&dev->mii);
+	ax772a_data->autoneg_start = jiffies;
+	ax772a_data->Event = WAIT_AUTONEG_COMPLETE;
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE,
+				0, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Write medium mode register: %d", ret);
+		goto out2;
+	}
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_IPG0,
+			AX88772A_IPG0_DEFAULT | AX88772A_IPG1_DEFAULT << 8,
+			AX88772A_IPG2_DEFAULT, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Write IPG,IPG1,IPG2 failed: %d", ret);
+		goto out2;
+	}
+
+	memset(buf, 0, 4);
+	ret = ax8817x_read_cmd(dev, AX_CMD_READ_IPG012, 0, 0, 3, buf);
+	*((u8 *)buf + 3) = 0x00;
+	if (ret < 0) {
+		deverr(dev, "Failed to read IPG,IPG1,IPG2 failed: %d", ret);
+		goto out2;
+	} else {
+		__u32 tmp32 = *((u32*)buf);
+		le32_to_cpus(&tmp32);
+		if (tmp32 != (AX88772A_IPG2_DEFAULT << 16 |
+			AX88772A_IPG1_DEFAULT << 8 | AX88772A_IPG0_DEFAULT)) {
+			printk("Non-authentic ASIX product\nASIX does not support it\n");
+			ret = -ENODEV;		
+			goto out2;
+		}
+	}
+
+	/* Set RX_CTL to default values with 2k buffer, and enable cactus */
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL,
+			(AX_RX_CTL_START | AX_RX_CTL_AB),
+			0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Reset RX_CTL failed: %d", ret);
+		goto out2;
+	}
+
+	/* Asix framing packs multiple eth frames into a 2K usb bulk transfer */
+	if (dev->driver_info->flags & FLAG_FRAMING_AX) {
+		/* hard_mtu  is still the default - the device does not support
+		   jumbo eth frames */
+		dev->rx_urb_size = 2048;
+	}
+
+	kfree(buf);
+
+	return ret;
+out2:
+	destroy_workqueue(ax772a_data->ax_work);
+	kfree(ax772a_data);
+	kfree(buf);
+out1:
+	return ret;
+}
+
+static void ax88772a_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+	struct ax88772a_data *ax772a_data = (struct ax88772a_data *)dev->priv;
+
+	if (ax772a_data) {
+
+		flush_workqueue(ax772a_data->ax_work);
+		destroy_workqueue(ax772a_data->ax_work);
+
+		/* stop MAC operation */
+		ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL,
+					AX_RX_CTL_STOP, 0, 0, NULL);
+
+		/* Power down PHY */
+		ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+					AX_SWRESET_IPPD, 0, 0, NULL);
+
+		kfree(ax772a_data);
+	}
+}
+
+static int ax88772b_set_csums(struct usbnet *dev)
+{
+	struct ax88772b_data *ax772b_data = (struct ax88772b_data *)dev->priv;
+	u16 checksum;
+
+	if (ax772b_data->checksum & AX_RX_CHECKSUM)
+		checksum = AX_RXCOE_DEF_CSUM;
+	else
+		checksum = 0;
+
+	ax8817x_write_cmd(dev, AX_CMD_WRITE_RXCOE_CTL,
+				 checksum, 0, 0, NULL);
+
+	if (ax772b_data->checksum & AX_TX_CHECKSUM)
+		checksum = AX_TXCOE_DEF_CSUM;
+	else
+		checksum = 0;
+
+	ax8817x_write_cmd(dev, AX_CMD_WRITE_TXCOE_CTL,
+				 checksum, 0, 0, NULL);
+
+	return 0;
+}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+static u32 ax88772b_get_tx_csum(struct net_device *netdev)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	struct ax88772b_data *ax772b_data = (struct ax88772b_data *)dev->priv;
+
+	return ax772b_data->checksum & AX_TX_CHECKSUM;
+}
+
+static u32 ax88772b_get_rx_csum(struct net_device *netdev)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	struct ax88772b_data *ax772b_data = (struct ax88772b_data *)dev->priv;
+
+	return ax772b_data->checksum & AX_RX_CHECKSUM;
+}
+
+static int ax88772b_set_rx_csum(struct net_device *netdev, u32 val)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	struct ax88772b_data *ax772b_data = (struct ax88772b_data *)dev->priv;
+
+	if (val)
+		ax772b_data->checksum |= AX_RX_CHECKSUM;
+	else
+		ax772b_data->checksum &= ~AX_RX_CHECKSUM;
+
+	return ax88772b_set_csums(dev);
+}
+
+static int ax88772b_set_tx_csum(struct net_device *netdev, u32 val)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	struct ax88772b_data *ax772b_data = (struct ax88772b_data *)dev->priv;
+
+	if (val)
+		ax772b_data->checksum |= AX_TX_CHECKSUM;
+	else
+		ax772b_data->checksum &= ~AX_TX_CHECKSUM;
+
+	ethtool_op_set_tx_csum(netdev, val);
+
+	return ax88772b_set_csums(dev);
+}
+#endif
+static struct ethtool_ops ax88772b_ethtool_ops = {
+	.get_drvinfo		= ax8817x_get_drvinfo,
+	.get_link		= ethtool_op_get_link,
+	.get_msglevel		= axusbnet_get_msglevel,
+	.set_msglevel		= axusbnet_set_msglevel,
+	.get_wol		= ax8817x_get_wol,
+	.set_wol		= ax8817x_set_wol,
+	.get_eeprom_len		= ax8817x_get_eeprom_len,
+	.get_eeprom		= ax8817x_get_eeprom,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
+	.get_settings		= ax8817x_get_settings,
+	.set_settings		= ax8817x_set_settings,
+#else
+	.get_link_ksettings	= ax8817x_get_link_ksettings,
+	.set_link_ksettings	= ax8817x_set_link_ksettings,
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+	.set_tx_csum		= ax88772b_set_tx_csum,
+	.get_tx_csum		= ax88772b_get_tx_csum,
+	.get_rx_csum		= ax88772b_get_rx_csum,
+	.set_rx_csum		= ax88772b_set_rx_csum,
+#endif
+};
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
+static const struct net_device_ops ax88772b_netdev_ops = {
+	.ndo_open		= axusbnet_open,
+	.ndo_stop		= axusbnet_stop,
+	.ndo_start_xmit		= axusbnet_start_xmit,
+	.ndo_tx_timeout		= axusbnet_tx_timeout,
+	.ndo_change_mtu		= axusbnet_change_mtu,
+	.ndo_do_ioctl		= ax8817x_ioctl,
+	.ndo_get_stats		= axusbnet_get_stats,
+	.ndo_set_mac_address	= ax8817x_set_mac_addr,
+	.ndo_validate_addr	= eth_validate_addr,
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 2, 0)
+	.ndo_set_multicast_list	= ax88772b_set_multicast,
+#else
+	.ndo_set_rx_mode	= ax88772b_set_multicast,
+#endif
+};
+#endif
+
+static int ax88772b_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	int ret;
+	void *buf;
+	struct ax8817x_data *data = (struct ax8817x_data *)&dev->data;
+	struct ax88772b_data *ax772b_data;
+	u16 *tmp16;
+	u8  *tmp8;
+	u8 tempphyselect;
+	bool internalphy;
+
+	printk(version);
+	axusbnet_get_endpoints(dev, intf);
+	buf = kmalloc(6, GFP_KERNEL);
+	if (!buf) {
+		deverr(dev, "Cannot allocate memory for buffer");
+		return -ENOMEM;
+	}
+	tmp16 = (u16 *)buf;
+	ax772b_data = kmalloc(sizeof(*ax772b_data), GFP_KERNEL);
+	if (!ax772b_data) {
+		deverr(dev, "Cannot allocate memory for AX88772B data");
+		kfree(buf);
+		return -ENOMEM;
+	}
+	memset(ax772b_data, 0, sizeof(*ax772b_data));
+	dev->priv = ax772b_data;
+	ax772b_data->ax_work = create_singlethread_workqueue("ax88772b");
+	if (!ax772b_data->ax_work) {
+		kfree(buf);
+		kfree(ax772b_data);
+		return -ENOMEM;
+	}
+
+	ax772b_data->dev = dev;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+	INIT_WORK(&ax772b_data->check_link, ax88772b_link_reset, dev);
+#else
+	INIT_WORK(&ax772b_data->check_link, ax88772b_link_reset);
+#endif
+
+	tmp8 = (u8 *)buf;
+	ret = ax8817x_read_cmd(dev, AX_CMD_SW_PHY_STATUS,
+			       0, 0, 1, tmp8);
+
+	if (ret < 0) {
+		deverr(dev,
+		       "read SW interface selection status register failed: %d\n",
+		       ret);
+		goto err_out;
+	}
+	tempphyselect = *tmp8;
+	tempphyselect &= 0x0C;
+
+	if (tempphyselect == AX_PHYSEL_SSRMII) {
+		internalphy = false;
+		ax772b_data->OperationMode = OPERATION_MAC_MODE;
+		ax772b_data->PhySelect = 0x00;
+	} else if (tempphyselect == AX_PHYSEL_SSRRMII) {
+		internalphy = true;
+		ax772b_data->OperationMode = OPERATION_PHY_MODE;
+		ax772b_data->PhySelect = 0x00;
+	} else if (tempphyselect == AX_PHYSEL_SSMII) {
+		internalphy = true;
+		ax772b_data->OperationMode = OPERATION_MAC_MODE;
+		ax772b_data->PhySelect = 0x01;
+	} else {
+		deverr(dev, "Unknown MII type\n");
+		goto err_out;
+	}
+
+	/* reload eeprom data */
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS, AXGPIOS_RSE,
+				0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Failed to enable GPIO function: %d", ret);
+		goto err_out;
+	}
+	msleep(5);
+
+	/* Get the EEPROM data*/
+	ret = ax8817x_read_cmd(dev, AX_CMD_READ_EEPROM, 0x18, 0, 2,
+			       (void *)tmp16);
+	if (ret < 0) {
+		deverr(dev, "read SROM address 18h failed: %d", ret);
+		goto err_out;
+	}
+	le16_to_cpus(tmp16);
+	ax772b_data->psc = *tmp16 & 0xFF00;
+	/* End of get EEPROM data */
+
+	/* Get the MAC address */
+	memset(buf, 0, ETH_ALEN);
+	ret = ax8817x_get_mac(dev, buf);
+	if (ret < 0) {
+		deverr(dev, "Get HW address failed: %d", ret);
+		goto err_out;
+	}
+
+	/* Initialize MII structure */
+	dev->mii.dev = dev->net;
+	dev->mii.mdio_read = ax8817x_mdio_read_le;
+	dev->mii.mdio_write = ax88772b_mdio_write_le;
+	dev->mii.phy_id_mask = 0xff;
+	dev->mii.reg_num_mask = 0xff;
+
+	/* Get the PHY id */
+	ret = ax8817x_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf);
+	if (ret < 0) {
+		deverr(dev, "Error reading PHY ID: %02x", ret);
+		goto err_out;
+	} else if (ret < 2) {
+		/* this should always return 2 bytes */
+		deverr(dev, "Read PHYID returned less than 2 bytes: ret=%02x",
+		    ret);
+		ret = -EIO;
+		goto err_out;
+	}
+
+	if (internalphy)
+		dev->mii.phy_id = *((u8 *)buf + 1);
+	else
+		dev->mii.phy_id = *((u8 *)buf);
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_SW_PHY_SELECT,
+				ax772b_data->PhySelect, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Select PHY #1 failed: %d", ret);
+		goto err_out;
+	}
+
+#if 0
+	/* select the embedded 10/100 Ethernet PHY */
+	ret = ax8817x_write_cmd(dev, AX_CMD_SW_PHY_SELECT,
+			AX_PHYSEL_SSEN | AX_PHYSEL_PSEL | AX_PHYSEL_SSMII,
+			0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Select PHY #1 failed: %d", ret);
+		goto err_out;
+	}
+
+	if (dev->mii.phy_id != 0x10) {
+		deverr(dev, "Got wrong PHY ID: %02x", dev->mii.phy_id);
+		ret = -EIO;
+		goto err_out;
+	}
+#endif
+	ret = ax88772a_phy_powerup(dev);
+	if (ret < 0)
+		goto err_out;
+
+	/* stop MAC operation */
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL,
+				AX_RX_CTL_STOP, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Reset RX_CTL failed: %d", ret);
+		goto err_out;
+	}
+
+	/* make sure the driver can enable sw mii operation */
+	ret = ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Enabling software MII failed: %d", ret);
+		goto err_out;
+	}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
+	dev->net->do_ioctl = ax8817x_ioctl;
+	dev->net->set_multicast_list = ax88772b_set_multicast;
+	dev->net->set_mac_address = ax8817x_set_mac_addr;
+#else
+	dev->net->netdev_ops = &ax88772b_netdev_ops;
+#endif
+
+	dev->net->ethtool_ops = &ax88772b_ethtool_ops;
+
+	/* Register suspend and resume functions */
+	data->suspend = ax88772b_suspend;
+	data->resume = ax88772b_resume;
+
+	if ((ax772b_data->OperationMode == OPERATION_MAC_MODE) &&
+	    (ax772b_data->PhySelect == 0x00)) {
+		if (ax88772b_external_phyinit(dev) != 0x00) {
+			deverr(dev, "Failed to initial the external phy");
+			goto err_out;
+		}
+	}
+
+	if (ax772b_data->OperationMode == OPERATION_PHY_MODE)
+		ax8817x_mdio_write_le(dev->net, dev->mii.phy_id
+						, MII_BMCR, 0x3900);
+
+	if (dev->mii.phy_id != 0x10)
+		ax8817x_mdio_write_le(dev->net, 0x10, MII_BMCR, 0x3900);
+
+	if (dev->mii.phy_id == 0x10 && ax772b_data->OperationMode
+						!= OPERATION_PHY_MODE) {
+
+		*tmp16 = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, 0x12);
+		ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, 0x12,
+					((*tmp16 & 0xFF9F) | 0x0040));
+	}	
+
+	ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+			ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
+
+	mii_nway_restart(&dev->mii);
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE,
+				0, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Failed to write medium mode: %d", ret);
+		goto err_out;
+	}
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_IPG0,
+			AX88772A_IPG0_DEFAULT | AX88772A_IPG1_DEFAULT << 8,
+			AX88772A_IPG2_DEFAULT, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Failed to write interframe gap: %d", ret);
+		goto err_out;
+	}
+
+	memset(buf, 0, 4);
+	ret = ax8817x_read_cmd(dev, AX_CMD_READ_IPG012, 0, 0, 3, buf);
+	*((u8 *)buf + 3) = 0x00;
+	if (ret < 0) {
+		deverr(dev, "Failed to read IPG,IPG1,IPG2 failed: %d", ret);
+		goto err_out;
+	} else {
+		__u32 tmp32 = *((u32*)buf);
+		le32_to_cpus(&tmp32);
+		if (tmp32 != (AX88772A_IPG2_DEFAULT << 16 |
+			AX88772A_IPG1_DEFAULT << 8 | AX88772A_IPG0_DEFAULT)) {
+			printk("Non-authentic ASIX product\nASIX does not support it\n");
+			ret = -ENODEV;		
+			goto err_out;
+		}
+	}
+
+	dev->net->features |= NETIF_F_IP_CSUM;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22)
+	dev->net->features |= NETIF_F_IPV6_CSUM;
+#endif
+
+	ax772b_data->checksum = AX_RX_CHECKSUM | AX_TX_CHECKSUM;
+	ret = ax88772b_set_csums(dev);
+	if (ret < 0) {
+		deverr(dev, "Write RX_COE/TX_COE failed: %d", ret);
+		goto err_out;
+	}
+
+	dev->rx_size = bsize & 0x07;
+	if (dev->udev->speed == USB_SPEED_HIGH) {
+
+		ret = ax8817x_write_cmd(dev, 0x2A,
+				AX88772B_BULKIN_SIZE[dev->rx_size].byte_cnt,
+				AX88772B_BULKIN_SIZE[dev->rx_size].threshold,
+				0, NULL);
+		if (ret < 0) {
+			deverr(dev, "Reset RX_CTL failed: %d", ret);
+			goto err_out;
+		}
+
+		dev->rx_urb_size = AX88772B_BULKIN_SIZE[dev->rx_size].size;
+	} else {
+		ret = ax8817x_write_cmd(dev, 0x2A,
+				0x8000, 0x8001, 0, NULL);
+		if (ret < 0) {
+			deverr(dev, "Reset RX_CTL failed: %d", ret);
+			goto err_out;
+		}
+		dev->rx_urb_size = 2048;
+	}
+
+	/* Configure RX header type */
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL,
+		      (AX_RX_CTL_START | AX_RX_CTL_AB | AX_RX_HEADER_DEFAULT),
+		      0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Reset RX_CTL failed: %d", ret);
+		goto err_out;
+	}
+
+	/* Overwrite power saving configuration from eeprom */
+	ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPRL |
+				(ax772b_data->psc & 0x7FFF), 0, 0, NULL);
+
+	if (ret < 0) {
+		deverr(dev, "Failed to configure PHY power saving: %d", ret);
+		goto err_out;
+	}
+
+	if (ax772b_data->OperationMode == OPERATION_PHY_MODE)
+		netif_carrier_on(dev->net);
+
+	kfree(buf);
+
+	return ret;
+err_out:
+	destroy_workqueue(ax772b_data->ax_work);
+	kfree(buf);
+	kfree(ax772b_data);
+	return ret;
+}
+
+static int ax88772b_external_phyinit(struct usbnet *dev) {
+	struct ax88772b_data *ax772b_data = (struct ax88772b_data *)dev->priv;
+	u16 phyid1, phyid2;
+
+	phyid1 = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, 0x02);
+	phyid2 = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, 0x03);
+	ax772b_data->ext_phy_oui = EXTPHY_ID_MASK_OUI(phyid1, phyid2);
+	ax772b_data->ext_phy_model = EXTPHY_ID_MASK_MODEL(phyid2);
+
+	if (ax772b_data->ext_phy_oui == EXTPHY_BROADCOM_OUI) { 
+		if(ax772b_data->ext_phy_model == EXTPHY_BCM89811_MODEL) {			
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x0, 0x8000);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x0028);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x0028);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x0028);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x0028);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x0028);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x0028);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1F, 0x0c00);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1E, 0x030A);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1F, 0x3440);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1E, 0x0166);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1F, 0x0020);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x012D);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1f, 0x9B52);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x012E);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1f, 0xA04D);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x0123);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1f, 0x00c0);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x0154);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1f, 0x81C4);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x0811);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1f, 0x0000);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x01D3);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1f, 0x0064);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x01C1);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1f, 0xA5F7);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x0028);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1f, 0x0400);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x001D);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1f, 0x3411);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x0820);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1f, 0x0401);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x002F);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1f, 0xF167);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x0045);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1f, 0x0500);
+			return 0;
+		}
+	}
+
+	return 0;
+}
+
+static void ax88772b_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+	struct ax88772b_data *ax772b_data = (struct ax88772b_data *)dev->priv;
+
+	if (ax772b_data) {
+
+		flush_workqueue(ax772b_data->ax_work);
+		destroy_workqueue(ax772b_data->ax_work);
+
+		/* stop MAC operation */
+		ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL,
+					AX_RX_CTL_STOP, 0, 0, NULL);
+
+		/* Power down PHY */
+		ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+					AX_SWRESET_IPPD, 0, 0, NULL);
+
+		kfree(ax772b_data);
+	}
+}
+
+static int
+ax88178_media_check(struct usbnet *dev, struct ax88178_data *ax178dataptr)
+{
+	int fullduplex, i = 0;
+	u16 tempshort = 0;
+	u16 media;
+	u16 advertise, lpa, result, stat1000, _lpa, _stat1000, delay = 5 * HZ;
+	unsigned long jtimeout;
+
+	jtimeout = jiffies + delay;
+	do {
+		_lpa = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_LPA);
+		_stat1000 = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id,
+						  MII_STAT1000);
+
+		lpa = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_LPA);
+		stat1000 = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id,
+						MII_STAT1000);
+
+		if (time_after(jiffies, jtimeout))
+			break;
+
+	} while ((_lpa != lpa) || (_stat1000 != stat1000) || i++ < 3);
+
+	advertise = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id,
+					 MII_ADVERTISE);
+	result = advertise & lpa;
+
+	if ((ax178dataptr->PhyMode == PHY_MODE_MARVELL) &&
+	    (ax178dataptr->LedMode == 1)) {
+		tempshort = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id,
+						 MARVELL_MANUAL_LED) & 0xfc0f;
+	}
+
+	fullduplex = 1;
+	if (stat1000 & LPA_1000FULL) {
+		media = MEDIUM_GIGA_MODE | MEDIUM_FULL_DUPLEX_MODE |
+			MEDIUM_ENABLE_125MHZ | MEDIUM_ENABLE_RECEIVE;
+		if ((ax178dataptr->PhyMode == PHY_MODE_MARVELL) &&
+		    (ax178dataptr->LedMode == 1))
+			tempshort |= 0x3e0;
+	} else if (result & LPA_100FULL) {
+		media = MEDIUM_FULL_DUPLEX_MODE | MEDIUM_ENABLE_RECEIVE |
+			MEDIUM_MII_100M_MODE;
+		if ((ax178dataptr->PhyMode == PHY_MODE_MARVELL) &&
+		    (ax178dataptr->LedMode == 1))
+			tempshort |= 0x3b0;
+	} else if (result & LPA_100HALF) {
+		fullduplex = 0;
+		media = MEDIUM_ENABLE_RECEIVE | MEDIUM_MII_100M_MODE;
+		if ((ax178dataptr->PhyMode == PHY_MODE_MARVELL) &&
+		    (ax178dataptr->LedMode == 1))
+			tempshort |= 0x3b0;
+	} else if (result & LPA_10FULL) {
+		media = MEDIUM_FULL_DUPLEX_MODE | MEDIUM_ENABLE_RECEIVE;
+		if ((ax178dataptr->PhyMode == PHY_MODE_MARVELL) &&
+		    (ax178dataptr->LedMode == 1))
+			tempshort |= 0x2f0;
+	} else {
+		media = MEDIUM_ENABLE_RECEIVE;
+		fullduplex = 0;
+		if ((ax178dataptr->PhyMode == PHY_MODE_MARVELL) &&
+		    (ax178dataptr->LedMode == 1))
+				tempshort |= 0x02f0;
+	}
+
+	if ((ax178dataptr->PhyMode == PHY_MODE_MARVELL) &&
+	    (ax178dataptr->LedMode == 1)) {
+		ax8817x_mdio_write_le(dev->net,
+			dev->mii.phy_id, MARVELL_MANUAL_LED, tempshort);
+	}
+
+	media |= 0x0004;
+	if (ax178dataptr->UseRgmii)
+		media |= 0x0008;
+	if (fullduplex) {
+		media |= 0x0020;  /* ebable tx flow control as default; */
+		media |= 0x0010;  /* ebable rx flow control as default; */
+	}
+
+	return media;
+}
+
+static void Vitess_8601_Init(struct usbnet *dev, int state)
+{
+	u16 reg;
+
+	switch (state) {
+	case 0:	/* tx, rx clock skew */
+		ax8817x_swmii_mdio_write_le(dev->net, dev->mii.phy_id, 31, 1);
+		ax8817x_swmii_mdio_write_le(dev->net, dev->mii.phy_id, 28, 0);
+		ax8817x_swmii_mdio_write_le(dev->net, dev->mii.phy_id, 31, 0);
+		break;
+
+	case 1:
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 31, 0x52B5);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 18, 0x009E);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 17, 0xDD39);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0x87AA);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0xA7B4);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 18,
+				ax8817x_swmii_mdio_read_le(dev->net,
+						dev->mii.phy_id, 18));
+
+		reg = (ax8817x_swmii_mdio_read_le(dev->net,
+				dev->mii.phy_id, 17) & ~0x003f) | 0x003c;
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 17, reg);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0x87B4);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0xa794);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 18,
+				ax8817x_swmii_mdio_read_le(dev->net,
+					dev->mii.phy_id, 18));
+
+		reg = (ax8817x_swmii_mdio_read_le(dev->net,
+				dev->mii.phy_id, 17) & ~0x003f) | 0x003e;
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 17, reg);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0x8794);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 18, 0x00f7);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 17, 0xbe36);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0x879e);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0xa7a0);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 18,
+				ax8817x_swmii_mdio_read_le(dev->net,
+						dev->mii.phy_id, 18));
+
+		reg = (ax8817x_swmii_mdio_read_le(dev->net,
+				dev->mii.phy_id, 17) & ~0x003f) | 0x0034;
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 17, reg);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0x87a0);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 18, 0x003c);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 17, 0xf3cf);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0x87a2);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 18, 0x003c);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 17, 0xf3cf);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0x87a4);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 18, 0x003c);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 17, 0xd287);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0x87a6);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0xa7a8);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 18,
+				ax8817x_swmii_mdio_read_le(dev->net,
+						dev->mii.phy_id, 18));
+
+		reg = (ax8817x_swmii_mdio_read_le(dev->net,
+				dev->mii.phy_id, 17) & ~0x0fff) | 0x0125;
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 17, reg);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0x87a8);
+
+		/* Enable Smart Pre-emphasis */
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0xa7fa);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 18,
+				ax8817x_swmii_mdio_read_le(dev->net,
+						dev->mii.phy_id, 18));
+
+		reg = (ax8817x_swmii_mdio_read_le(dev->net,
+				dev->mii.phy_id, 17) & ~0x0008) | 0x0008;
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 17, reg);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0x87fa);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 31, 0);
+
+		break;
+	}
+}
+
+static void
+marvell_88E1510_magic_init(struct usbnet *dev)
+{
+	ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 22, 0xff);
+	ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 17, 0x214b);
+	ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 16, 0x2144);
+	ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 17, 0x0c28);
+	ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 16, 0x2146);
+	ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 17, 0xb233);
+	ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 16, 0x214d);
+	ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 17, 0xcc0c);
+	ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 16, 0x2159);
+	ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 22, 0x00fb);
+	ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 7, 0xc00d);
+	ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 22, 0);
+}
+
+static int
+ax88178_phy_init(struct usbnet *dev, struct ax88178_data *ax178dataptr)
+{
+	int i;
+	u16 phyanar, phyauxctrl, phyctrl, tempshort, phyid1;
+	u16 phyreg = 0;
+
+	/* Disable MII operation of AX88178 Hardware */
+	ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0x0000, 0, 0, NULL);
+
+
+	/* Read SROM - MiiPhy Address (ID) */
+	ax8817x_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, &dev->mii.phy_id);
+	le32_to_cpus(&dev->mii.phy_id);
+
+	/* Initialize MII structure */
+	dev->mii.phy_id >>= 8;
+	dev->mii.phy_id &= PHY_ID_MASK;
+	dev->mii.dev = dev->net;
+	dev->mii.mdio_read = ax8817x_mdio_read_le;
+	dev->mii.mdio_write = ax8817x_mdio_write_le;
+	dev->mii.phy_id_mask = 0x3f;
+	dev->mii.reg_num_mask = 0x1f;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 11)
+	dev->mii.supports_gmii = 1;
+#endif
+
+	if (ax178dataptr->PhyMode == PHY_MODE_MAC_TO_MAC_GMII) {
+		ax178dataptr->UseRgmii = 0;
+		ax178dataptr->MediaLink = MEDIUM_GIGA_MODE |
+					  MEDIUM_FULL_DUPLEX_MODE |
+					  MEDIUM_ENABLE_125MHZ |
+					  MEDIUM_ENABLE_RECEIVE |
+					  MEDIUM_ENABLE_RX_FLOWCTRL |
+					  MEDIUM_ENABLE_TX_FLOWCTRL;
+		goto SKIPPHYSETTING;
+	}
+
+	/* test read phy register 2 */
+	if (!ax178dataptr->UseGpio0) {
+		i = 1000;
+		while (i--) {
+			phyid1 = ax8817x_swmii_mdio_read_le(dev->net,
+						dev->mii.phy_id, GMII_PHY_OUI);
+			if ((phyid1 == 0x000f) || (phyid1 == 0x0141) ||
+			    (phyid1 == 0x0282) || (phyid1 == 0x004d) ||
+			    (phyid1 == 0x0243) || (phyid1 == 0x001C) ||
+			    (phyid1 == 0x0007))
+				break;
+			msleep(5);
+		}
+		if (i < 0)
+			return -EIO;
+	}
+
+	ax178dataptr->UseRgmii = 0;
+	if (ax178dataptr->PhyMode == PHY_MODE_MARVELL) {
+		phyreg = ax8817x_swmii_mdio_read_le(dev->net,
+					dev->mii.phy_id, 27);
+		if (!(phyreg & 4) && !(ax178dataptr->LedMode & 0x10)) {
+			ax178dataptr->UseRgmii = 1;
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 20, 0x82);
+			ax178dataptr->MediaLink |= MEDIUM_ENABLE_125MHZ;
+		} else if (ax178dataptr->LedMode & 0x10) {
+
+			ax178dataptr->UseRgmii = 1;
+			ax178dataptr->MediaLink |= MEDIUM_ENABLE_125MHZ;
+			marvell_88E1510_magic_init(dev);
+
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 22, 2);
+
+			phyreg = ax8817x_swmii_mdio_read_le(dev->net,
+					dev->mii.phy_id, 21);
+
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 21, phyreg | 0x30);
+
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 22, 0);
+		}
+	} else if ((ax178dataptr->PhyMode == PHY_MODE_AGERE_V0) ||
+		 (ax178dataptr->PhyMode == PHY_MODE_AGERE_V0_GMII)) {
+		if (ax178dataptr->PhyMode == PHY_MODE_AGERE_V0) {
+			ax178dataptr->UseRgmii = 1;
+			ax178dataptr->MediaLink |= MEDIUM_ENABLE_125MHZ;
+		}
+	} else if (ax178dataptr->PhyMode == PHY_MODE_CICADA_V1) {
+		/* not Cameo */
+		if (!ax178dataptr->UseGpio0 || ax178dataptr->LedMode) {
+			ax178dataptr->UseRgmii = 1;
+			ax178dataptr->MediaLink |= MEDIUM_ENABLE_125MHZ;
+		}
+
+		for (i = 0; i < (sizeof(CICADA_FAMILY_HWINIT) /
+				 sizeof(CICADA_FAMILY_HWINIT[0])); i++) {
+			ax8817x_swmii_mdio_write_le(dev->net,
+						    dev->mii.phy_id,
+					CICADA_FAMILY_HWINIT[i].offset,
+					CICADA_FAMILY_HWINIT[i].value);
+		}
+
+	} else if (ax178dataptr->PhyMode == PHY_MODE_CICADA_V2) {
+		/* not Cameo */
+		if (!ax178dataptr->UseGpio0 || ax178dataptr->LedMode) {
+			ax178dataptr->UseRgmii = 1;
+			ax178dataptr->MediaLink |= MEDIUM_ENABLE_125MHZ;
+		}
+
+		for (i = 0; i < (sizeof(CICADA_V2_HWINIT) /
+				 sizeof(CICADA_V2_HWINIT[0])); i++) {
+			ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, CICADA_V2_HWINIT[i].offset,
+				CICADA_V2_HWINIT[i].value);
+		}
+	} else if (ax178dataptr->PhyMode == PHY_MODE_CICADA_V2_ASIX) {
+		/* not Cameo */
+		if (!ax178dataptr->UseGpio0 || ax178dataptr->LedMode) {
+			ax178dataptr->UseRgmii = 1;
+			ax178dataptr->MediaLink |= MEDIUM_ENABLE_125MHZ;
+		}
+
+		for (i = 0; i < (sizeof(CICADA_V2_HWINIT) /
+				 sizeof(CICADA_V2_HWINIT[0])); i++) {
+			ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, CICADA_V2_HWINIT[i].offset,
+				CICADA_V2_HWINIT[i].value);
+		}
+	} else if (ax178dataptr->PhyMode == PHY_MODE_RTL8211CL) {
+		ax178dataptr->UseRgmii = 1;
+		ax178dataptr->MediaLink |= MEDIUM_ENABLE_125MHZ;
+	} else if (ax178dataptr->PhyMode == PHY_MODE_RTL8211BN) {
+		ax178dataptr->UseRgmii = 1;
+		ax178dataptr->MediaLink |= MEDIUM_ENABLE_125MHZ;
+	} else if (ax178dataptr->PhyMode == PHY_MODE_RTL8251CL) {
+		ax178dataptr->UseRgmii = 1;
+		ax178dataptr->MediaLink |= MEDIUM_ENABLE_125MHZ;
+	} else if (ax178dataptr->PhyMode == PHY_MODE_VSC8601) {
+		ax178dataptr->UseRgmii = 1;
+		ax178dataptr->MediaLink |= MEDIUM_ENABLE_125MHZ;
+		/* Vitess_8601_Init (dev, 0); */
+	}
+
+	if (ax178dataptr->PhyMode != PHY_MODE_ATTANSIC_V0) {
+		/* software reset */
+		ax8817x_swmii_mdio_write_le(
+			dev->net, dev->mii.phy_id, GMII_PHY_CONTROL,
+			ax8817x_swmii_mdio_read_le(
+				dev->net, dev->mii.phy_id, GMII_PHY_CONTROL)
+				| GMII_CONTROL_RESET);
+		msleep(1);
+	}
+
+	if ((ax178dataptr->PhyMode == PHY_MODE_AGERE_V0) ||
+	    (ax178dataptr->PhyMode == PHY_MODE_AGERE_V0_GMII)) {
+		if (ax178dataptr->PhyMode == PHY_MODE_AGERE_V0) {
+			i = 1000;
+			while (i--) {
+				ax8817x_swmii_mdio_write_le(dev->net,
+						dev->mii.phy_id, 21, 0x1001);
+
+				phyreg = ax8817x_swmii_mdio_read_le(dev->net,
+						dev->mii.phy_id, 21);
+				if ((phyreg & 0xf00f) == 0x1001)
+					break;
+			}
+			if (i < 0)
+				return -EIO;
+		}
+
+		if (ax178dataptr->LedMode == 4) {
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 28, 0x7417);
+		} else if (ax178dataptr->LedMode == 9) {
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 28, 0x7a10);
+		} else if (ax178dataptr->LedMode == 10) {
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 28, 0x7a13);
+		}
+
+		for (i = 0; i < (sizeof(AGERE_FAMILY_HWINIT) /
+				 sizeof(AGERE_FAMILY_HWINIT[0])); i++) {
+			ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, AGERE_FAMILY_HWINIT[i].offset,
+				AGERE_FAMILY_HWINIT[i].value);
+		}
+	} else if (ax178dataptr->PhyMode == PHY_MODE_RTL8211CL) {
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 0x1f, 0x0005);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 0x0c, 0);
+
+		ax8817x_swmii_mdio_write_le(dev->net, dev->mii.phy_id, 0x01,
+					(ax8817x_swmii_mdio_read_le(dev->net,
+					dev->mii.phy_id, 0x01) | 0x0080));
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 0x1f, 0);
+
+		if (ax178dataptr->LedMode == 12) {
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 0x1f, 0x0002);
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 0x1a, 0x00cb);
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 0x1f, 0);
+		}
+	} else if (ax178dataptr->PhyMode == PHY_MODE_VSC8601) {
+		Vitess_8601_Init(dev, 1);
+	}
+
+	/* read phy register 0 */
+	phyctrl = ax8817x_swmii_mdio_read_le(dev->net,
+				dev->mii.phy_id, GMII_PHY_CONTROL);
+	tempshort = phyctrl;
+	phyctrl &= ~(GMII_CONTROL_POWER_DOWN | GMII_CONTROL_ISOLATE);
+	if (phyctrl != tempshort) {
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, GMII_PHY_CONTROL, phyctrl);
+	}
+
+	/* LED */
+	if (ax178dataptr->PhyMode == PHY_MODE_MARVELL) {
+		if (ax178dataptr->LedMode == 1)	{
+
+			phyreg = (ax8817x_swmii_mdio_read_le(dev->net,
+				dev->mii.phy_id, 24) & 0xf8ff) | (1 + 0x100);
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 24, phyreg);
+			phyreg = ax8817x_swmii_mdio_read_le(dev->net,
+					dev->mii.phy_id, 25) & 0xfc0f;
+		} else if (ax178dataptr->LedMode == 2) {
+
+			phyreg = (ax8817x_swmii_mdio_read_le(dev->net,
+					dev->mii.phy_id, 24) & 0xf886) |
+					(1 + 0x10 + 0x300);
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 24, phyreg);
+		} else if (ax178dataptr->LedMode == 5) {
+
+			phyreg = (ax8817x_swmii_mdio_read_le(dev->net,
+					dev->mii.phy_id, 24) & 0xf8be) |
+					(1 + 0x40 + 0x300);
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 24, phyreg);
+		} else if (ax178dataptr->LedMode == 7) {
+
+			phyreg = (ax8817x_swmii_mdio_read_le(dev->net,
+						dev->mii.phy_id, 24) & 0xf8ff) |
+						(1 + 0x100);
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 24, phyreg);
+
+		} else if (ax178dataptr->LedMode == 8) {
+
+			phyreg = (ax8817x_swmii_mdio_read_le(dev->net,
+					dev->mii.phy_id, 24) & 0xf8be) |
+					(1 + 0x40 + 0x100);
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 24, phyreg);
+
+		} else if (ax178dataptr->LedMode == 11) {
+
+			phyreg = ax8817x_swmii_mdio_read_le(dev->net,
+					dev->mii.phy_id, 24) & 0x4106;
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 24, phyreg);
+		} else if (ax178dataptr->LedMode == 0x10) {
+			/* MARVEL 88e1510 use default led setting */
+		}
+
+	} else if ((ax178dataptr->PhyMode == PHY_MODE_CICADA_V1) ||
+		   (ax178dataptr->PhyMode == PHY_MODE_CICADA_V2) ||
+		   (ax178dataptr->PhyMode == PHY_MODE_CICADA_V2_ASIX)) {
+
+		if (ax178dataptr->LedMode == 3) {
+
+			phyreg = (ax8817x_swmii_mdio_read_le(dev->net,
+					dev->mii.phy_id, 27) & 0xFCFF) | 0x0100;
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 27, phyreg);
+		}
+
+	}
+
+	if (ax178dataptr->PhyMode == PHY_MODE_MARVELL) {
+		if (ax178dataptr->LedMode == 1)
+			phyreg |= 0x3f0;
+	}
+
+	phyanar = 1 | (GMII_ANAR_PAUSE | GMII_ANAR_100TXFD | GMII_ANAR_100TX |
+		       GMII_ANAR_10TFD | GMII_ANAR_10T | GMII_ANAR_ASYM_PAUSE);
+
+	phyauxctrl = GMII_1000_AUX_CTRL_FD_CAPABLE;
+
+	ax8817x_swmii_mdio_write_le(dev->net,
+			dev->mii.phy_id, GMII_PHY_ANAR, phyanar);
+	ax8817x_swmii_mdio_write_le(dev->net,
+			dev->mii.phy_id, GMII_PHY_1000BT_CONTROL, phyauxctrl);
+
+	if (ax178dataptr->PhyMode == PHY_MODE_VSC8601) {
+		ax8817x_swmii_mdio_write_le(dev->net, dev->mii.phy_id,
+					    31, 0x52B5);
+		ax8817x_swmii_mdio_write_le(dev->net, dev->mii.phy_id,
+					    16, 0xA7F8);
+
+		tempshort = ax8817x_swmii_mdio_read_le(dev->net,
+					dev->mii.phy_id, 17) & (~0x0018);
+
+		ax8817x_swmii_mdio_write_le(dev->net, dev->mii.phy_id,
+					    17, tempshort);
+
+		tempshort = ax8817x_swmii_mdio_read_le(dev->net,
+						       dev->mii.phy_id, 18);
+
+		ax8817x_swmii_mdio_write_le(dev->net, dev->mii.phy_id, 18,
+					    tempshort);
+
+		ax8817x_swmii_mdio_write_le(dev->net, dev->mii.phy_id,
+					    16, 0x87F8);
+		ax8817x_swmii_mdio_write_le(dev->net, dev->mii.phy_id,
+					    31, 0);
+	}
+
+	if (ax178dataptr->PhyMode == PHY_MODE_ATTANSIC_V0) {
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, GMII_PHY_CONTROL, 0x9000);
+
+	} else {
+		phyctrl &= ~GMII_CONTROL_LOOPBACK;
+		phyctrl |= (GMII_CONTROL_ENABLE_AUTO | GMII_CONTROL_START_AUTO);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, GMII_PHY_CONTROL, phyctrl);
+	}
+
+	if (ax178dataptr->PhyMode == PHY_MODE_MARVELL) {
+		if (ax178dataptr->LedMode == 1)
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 25, phyreg);
+	}
+
+SKIPPHYSETTING:
+
+	ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE,
+			ax178dataptr->MediaLink, 0, 0, NULL);
+
+	ax8817x_write_cmd(dev, AX_CMD_WRITE_IPG0,
+			AX88772_IPG0_DEFAULT | (AX88772_IPG1_DEFAULT << 8),
+			AX88772_IPG2_DEFAULT, 0, NULL);
+
+	msleep(1);
+
+	ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, NULL);
+
+	return 0;
+}
+
+static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	int ret;
+	void *buf;
+	struct ax8817x_data *data = (struct ax8817x_data *)&dev->data;
+	struct ax88178_data *ax178dataptr = NULL;
+
+	axusbnet_get_endpoints(dev, intf);
+
+	buf = kmalloc(6, GFP_KERNEL);
+	if (!buf) {
+		deverr(dev, "Cannot allocate memory for buffer");
+		return -ENOMEM;
+	}
+
+	/* allocate 178 data */
+	ax178dataptr = kmalloc(sizeof(*ax178dataptr), GFP_KERNEL);
+	if (!ax178dataptr) {
+		deverr(dev, "Cannot allocate memory for AX88178 data");
+		ret = -ENOMEM;
+		goto error_out;
+	}
+	memset(ax178dataptr, 0, sizeof(struct ax88178_data));
+	dev->priv = ax178dataptr;
+	/* end of allocate 178 data */
+
+	/* Get the EEPROM data*/
+	ret = ax8817x_read_cmd(dev, AX_CMD_READ_EEPROM, 0x0017, 0, 2,
+			       (void *)(&ax178dataptr->EepromData));
+	if (ret < 0) {
+		deverr(dev, "read SROM address 17h failed: %d", ret);
+		goto error_out;
+	}
+	le16_to_cpus(&ax178dataptr->EepromData);
+	/* End of get EEPROM data */
+
+	if (ax178dataptr->EepromData == 0xffff) {
+		ax178dataptr->PhyMode  = PHY_MODE_MARVELL;
+		ax178dataptr->LedMode  = 0;
+		ax178dataptr->UseGpio0 = 1; /* True */
+	} else {
+		ax178dataptr->PhyMode = (u8)(ax178dataptr->EepromData &
+					     EEPROMMASK);
+		ax178dataptr->LedMode = (u8)(ax178dataptr->EepromData >> 8);
+
+		/* for buffalo new (use gpio2) */
+		if (ax178dataptr->LedMode == 6)
+			ax178dataptr->LedMode = 1;
+		else if (ax178dataptr->LedMode == 1)
+			ax178dataptr->BuffaloOld = 1;
+
+
+		if (ax178dataptr->EepromData & 0x80)
+			ax178dataptr->UseGpio0 = 0; /* MARVEL se and other */
+		else
+			ax178dataptr->UseGpio0 = 1; /* cameo */
+	}
+
+	if (ax178dataptr->UseGpio0) {
+
+		if (ax178dataptr->PhyMode == PHY_MODE_MARVELL) {
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+					AXGPIOS_GPO0EN | AXGPIOS_RSE,
+					0, 0, NULL);
+			if (ret < 0) {
+				deverr(dev, "write GPIO failed: %d", ret);
+				goto error_out;
+			}
+
+			msleep(25);
+
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+					(AXGPIOS_GPO2 | AXGPIOS_GPO2EN |
+					 AXGPIOS_GPO0EN), 0, 0, NULL);
+			if (ret < 0) {
+				deverr(dev, "write GPIO failed: %d", ret);
+				goto error_out;
+			}
+
+			msleep(15);
+
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+					AXGPIOS_GPO2EN | AXGPIOS_GPO0EN,
+					0, 0, NULL);
+			if (ret < 0) {
+				deverr(dev, "write GPIO failed: %d", ret);
+				goto error_out;
+			}
+
+			msleep(245);
+
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+					(AXGPIOS_GPO2 | AXGPIOS_GPO2EN |
+					 AXGPIOS_GPO0EN), 0, 0, NULL);
+			if (ret < 0) {
+				deverr(dev, "write GPIO failed: %d", ret);
+				goto error_out;
+			}
+
+		} else { /* vitesse */
+
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+					(AXGPIOS_RSE | AXGPIOS_GPO0EN |
+					 AXGPIOS_GPO0), 0, 0, NULL);
+			if (ret < 0) {
+				deverr(dev, "write GPIO failed: %d", ret);
+				goto error_out;
+			}
+
+			msleep(25);
+
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+					(AXGPIOS_GPO0EN | AXGPIOS_GPO0 |
+					 AXGPIOS_GPO2EN | AXGPIOS_GPO2),
+					0, 0, NULL);
+			if (ret < 0) {
+				deverr(dev, "write GPIO failed: %d", ret);
+				goto error_out;
+			}
+
+			msleep(25);
+
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+					(AXGPIOS_GPO0EN | AXGPIOS_GPO0 |
+					 AXGPIOS_GPO2EN), 0, 0, NULL);
+			if (ret < 0) {
+				deverr(dev, "write GPIO failed: %d", ret);
+				goto error_out;
+			}
+
+			msleep(245);
+
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+						(AXGPIOS_GPO0EN | AXGPIOS_GPO0 |
+						AXGPIOS_GPO2EN | AXGPIOS_GPO2),
+						0, 0, NULL);
+			if (ret < 0) {
+				deverr(dev, "write GPIO failed: %d", ret);
+				goto error_out;
+			}
+		}
+	} else {	/* use gpio1 */
+		ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+					(AXGPIOS_GPO1 | AXGPIOS_GPO1EN |
+					AXGPIOS_RSE), 0, 0, NULL);
+		if (ret < 0) {
+			deverr(dev, "write GPIO failed: %d", ret);
+			goto error_out;
+		}
+
+		if (ax178dataptr->BuffaloOld) {
+			msleep(350);
+
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+						AXGPIOS_GPO1EN, 0, 0, NULL);
+			if (ret < 0) {
+				deverr(dev, "write GPIO failed: %d", ret);
+				goto error_out;
+			}
+
+			msleep(350);
+
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+						AXGPIOS_GPO1EN | AXGPIOS_GPO1,
+						0, 0, NULL);
+			if (ret < 0) {
+				deverr(dev, "write GPIO failed: %d", ret);
+				goto error_out;
+			}
+		} else {
+			msleep(25);
+
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+						(AXGPIOS_GPO1EN | AXGPIOS_GPO1 |
+						AXGPIOS_GPO2EN | AXGPIOS_GPO2),
+						0, 0, NULL);
+			if (ret < 0) {
+				deverr(dev, "write GPIO failed: %d", ret);
+				goto error_out;
+			}
+
+			msleep(25);
+
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+						(AXGPIOS_GPO1EN | AXGPIOS_GPO1 |
+						AXGPIOS_GPO2EN), 0, 0, NULL);
+			if (ret < 0) {
+				deverr(dev, "write GPIO failed: %d", ret);
+				goto error_out;
+			}
+
+			msleep(245);
+
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+						(AXGPIOS_GPO1EN | AXGPIOS_GPO1 |
+						AXGPIOS_GPO2EN | AXGPIOS_GPO2),
+						0, 0, NULL);
+			if (ret < 0) {
+				deverr(dev, "write GPIO failed: %d", ret);
+				goto error_out;
+			}
+		}
+	}
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_SW_PHY_SELECT, 0, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Select PHY failed: %d", ret);
+		goto error_out;
+	}
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPPD |
+				AX_SWRESET_PRL, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Issue sw reset failed: %d", ret);
+		goto error_out;
+	}
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, 0, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Issue rx ctrl failed: %d", ret);
+		goto error_out;
+	}
+
+	/* Get the MAC address */
+	memset(buf, 0, ETH_ALEN);
+	ax8817x_get_mac(dev, buf);
+	if (ret < 0)
+		goto error_out;
+	/* End of get MAC address */
+
+	ret = ax88178_phy_init(dev, ax178dataptr);
+	if (ret < 0)
+		goto error_out;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
+	dev->net->do_ioctl = ax8817x_ioctl;
+	dev->net->set_multicast_list = ax88178_set_multicast;
+	dev->net->set_mac_address = ax8817x_set_mac_addr;
+#else
+	dev->net->netdev_ops = &ax88178_netdev_ops;
+#endif
+	dev->net->ethtool_ops = &ax8817x_ethtool_ops;
+
+	/* Register suspend and resume functions */
+	data->suspend = ax88772_suspend;
+	data->resume = ax88772_resume;
+
+	if (dev->driver_info->flags & FLAG_FRAMING_AX)
+		dev->rx_urb_size = 16384;
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, (AX_RX_CTL_MFB |
+				AX_RX_CTL_START | AX_RX_CTL_AB), 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "write RX ctrl reg failed: %d", ret);
+		goto error_out;
+	}
+
+	kfree(buf);
+	printk(version);
+	return ret;
+
+error_out:
+	kfree(ax178dataptr);
+	kfree(buf);
+	return ret;
+}
+
+static void ax88178_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+	struct ax88178_data *ax178dataptr = (struct ax88178_data *)dev->priv;
+
+	if (ax178dataptr) {
+
+		/* stop MAC operation */
+		ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL,
+					AX_RX_CTL_STOP, 0, 0, NULL);
+
+		kfree(ax178dataptr);
+	}
+}
+
+static int ax88772_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+	u8  *head;
+	u32  header;
+	char *packet;
+	struct sk_buff *ax_skb = NULL;
+	u16 size;
+
+	head = (u8 *) skb->data;
+	memcpy(&header, head, sizeof(header));
+	le32_to_cpus(&header);
+	packet = head + sizeof(header);
+
+	skb_pull(skb, 4);
+
+	while (skb->len > 0) {
+		if ((short)(header & 0x00007ff) !=
+		    ~((short)(((header & 0xffff0000) | 0xf8000000) >> 16))) {
+			deverr(dev, "header length data is error 0x%08x, %d\n",
+				header, skb->len);
+		}
+		/* get the packet length */
+		size = (u16) (header & 0x00007ff);
+
+		if ((skb->len) - ((size + 1) & 0xfffe) == 0) {
+
+			/* Make sure ip header is aligned on 32-bit boundary */
+			if (!((unsigned long)skb->data & 0x02)) {
+				memmove(skb->data - 2, skb->data, size);
+				skb->data -= 2;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
+				skb->tail = skb->data + size;
+#else
+				skb_set_tail_pointer(skb, size);
+#endif
+			}
+			skb->truesize = size + sizeof(struct sk_buff);
+			skb->len = size;
+
+			return 2;
+		}
+
+		if (size > ETH_FRAME_LEN) {
+			deverr(dev, "invalid rx length %d", size);
+			return 0;
+		}
+#ifndef RX_SKB_COPY
+		ax_skb = skb_clone(skb, GFP_ATOMIC);
+#else
+		ax_skb = alloc_skb(size + NET_IP_ALIGN, GFP_ATOMIC);	
+		skb_reserve(ax_skb, NET_IP_ALIGN);
+#endif
+		if (ax_skb) {
+#ifndef RX_SKB_COPY
+			/* Make sure ip header is aligned on 32-bit boundary */
+			if (!((unsigned long)packet & 0x02)) {
+				memmove(packet - 2, packet, size);
+				packet -= 2;
+			}
+			ax_skb->data = packet;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
+			ax_skb->tail = packet + size;
+#else
+			skb_set_tail_pointer(ax_skb, size);
+#endif
+
+#else
+			skb_put(ax_skb, size);
+			memcpy(ax_skb->data, packet , size);
+#endif
+			ax_skb->truesize = size + sizeof(struct sk_buff);
+			axusbnet_skb_return(dev, ax_skb);
+
+		} else {
+			return 0;
+		}
+
+		skb_pull(skb, (size + 1) & 0xfffe);
+
+		if (skb->len == 0)
+			break;
+
+		head = (u8 *) skb->data;
+		memcpy(&header, head, sizeof(header));
+		le32_to_cpus(&header);
+		packet = head + sizeof(header);
+		skb_pull(skb, 4);
+	}
+
+	if (skb->len < 0) {
+		deverr(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,
+					gfp_t flags)
+{
+	int padlen = ((skb->len + 4) % 512) ? 0 : 4;
+	u32 packet_len;
+	u32 padbytes = 0xffff0000;
+
+#if (!AX_FORCE_BUFF_ALIGN)
+	int headroom = skb_headroom(skb);
+	int tailroom = skb_tailroom(skb);
+
+	if ((!skb_cloned(skb))
+	    && ((headroom + tailroom) >= (4 + padlen))) {
+		if ((headroom < 4) || (tailroom < padlen)) {
+			skb->data = memmove(skb->head + 4, skb->data, skb->len);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
+		skb->tail = skb->data + skb->len;
+#else
+		skb_set_tail_pointer(skb, skb->len);
+#endif
+		}
+	} else
+#endif
+	{
+		struct sk_buff *skb2;
+		skb2 = skb_copy_expand(skb, 4, padlen, flags);
+		dev_kfree_skb_any(skb);
+		skb = skb2;
+		if (!skb)
+			return NULL;
+	}
+
+	skb_push(skb, 4);
+	packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4);
+	cpu_to_le32s(&packet_len);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
+	memcpy(skb->data, &packet_len, sizeof(packet_len));
+#else
+	skb_copy_to_linear_data(skb, &packet_len, sizeof(packet_len));
+#endif
+
+	if ((skb->len % 512) == 0) {
+		cpu_to_le32s(&padbytes);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
+		memcpy(skb->tail, &padbytes, sizeof(padbytes));
+#else
+		memcpy(skb_tail_pointer(skb), &padbytes, sizeof(padbytes));
+#endif
+		skb_put(skb, sizeof(padbytes));
+	}
+	return skb;
+}
+
+static void
+ax88772b_rx_checksum(struct sk_buff *skb, struct ax88772b_rx_header *rx_hdr)
+{
+	skb->ip_summed = CHECKSUM_NONE;
+
+	/* checksum error bit is set */
+	if (rx_hdr->l3_csum_err || rx_hdr->l4_csum_err)
+		return;
+
+	/* It must be a TCP or UDP packet with a valid checksum */
+	if ((rx_hdr->l4_type == AX_RXHDR_L4_TYPE_TCP) ||
+	    (rx_hdr->l4_type == AX_RXHDR_L4_TYPE_UDP)) {
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+	}
+}
+
+static int ax88772b_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+	struct ax88772b_rx_header rx_hdr;
+	struct sk_buff *ax_skb = NULL;
+	struct ax88772b_data *ax772b_data = (struct ax88772b_data *)dev->priv;
+
+	while (skb->len > 0) {
+
+		le16_to_cpus((u16 *)skb->data);
+		le16_to_cpus(((u16 *)skb->data) + 1);
+
+		memcpy(&rx_hdr, skb->data, sizeof(struct ax88772b_rx_header));
+
+		if ((short)rx_hdr.len != (~((short)rx_hdr.len_bar) & 0x7FF))
+			return 0;
+
+		if (rx_hdr.len > (ETH_FRAME_LEN + 4)) {
+			deverr(dev, "invalid rx length %d", rx_hdr.len);
+			return 0;
+		}
+
+		if (skb->len - ((rx_hdr.len +
+				 sizeof(struct ax88772b_rx_header) + 3) &
+				 0xfffc) == 0) {
+			skb_pull(skb, sizeof(struct ax88772b_rx_header));
+			skb->len = rx_hdr.len;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
+			skb->tail = skb->data + rx_hdr.len;
+#else
+			skb_set_tail_pointer(skb, rx_hdr.len);
+#endif
+			skb->truesize = rx_hdr.len + sizeof(struct sk_buff);
+
+			if (ax772b_data->checksum & AX_RX_CHECKSUM)
+				ax88772b_rx_checksum(skb, &rx_hdr);
+
+			return 2;
+		}
+#ifndef RX_SKB_COPY
+		ax_skb = skb_clone(skb, GFP_ATOMIC);
+#else
+		ax_skb = alloc_skb(rx_hdr.len + NET_IP_ALIGN, GFP_ATOMIC);
+		skb_reserve(ax_skb, NET_IP_ALIGN);	
+#endif
+		if (ax_skb) {
+#ifndef RX_SKB_COPY
+			ax_skb->len = rx_hdr.len;
+			ax_skb->data = skb->data +
+				       sizeof(struct ax88772b_rx_header);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
+			ax_skb->tail = ax_skb->data + rx_hdr.len;
+#else
+			skb_set_tail_pointer(ax_skb, rx_hdr.len);
+#endif
+
+#else
+			skb_put(ax_skb, rx_hdr.len);
+			memcpy(ax_skb->data, skb->data + sizeof(struct ax88772b_rx_header), rx_hdr.len); 
+#endif
+
+			ax_skb->truesize = rx_hdr.len + sizeof(struct sk_buff);
+
+			if (ax772b_data->checksum & AX_RX_CHECKSUM)
+				ax88772b_rx_checksum(ax_skb, &rx_hdr);
+
+			axusbnet_skb_return(dev, ax_skb);
+
+		} else {
+			return 0;
+		}
+
+		skb_pull(skb, ((rx_hdr.len +
+				sizeof(struct ax88772b_rx_header) + 3)
+				& 0xfffc));
+	}
+
+	if (skb->len < 0) {
+		deverr(dev, "invalid rx length %d", skb->len);
+		return 0;
+	}
+	return 1;
+}
+
+static struct sk_buff *
+ax88772b_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
+{
+	int padlen = ((skb->len + 4) % 512) ? 0 : 4;
+	u32 packet_len;
+	u32 padbytes = 0xffff0000;
+
+#if (!AX_FORCE_BUFF_ALIGN)
+	int headroom = skb_headroom(skb);
+	int tailroom = skb_tailroom(skb);
+
+	if ((!skb_cloned(skb))
+	    && ((headroom + tailroom) >= (4 + padlen))) {
+		if ((headroom < 4) || (tailroom < padlen)) {
+			skb->data = memmove(skb->head + 4, skb->data, skb->len);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
+			skb->tail = skb->data + skb->len;
+#else
+			skb_set_tail_pointer(skb, skb->len);
+#endif
+		}
+	} else
+#endif
+	{
+		struct sk_buff *skb2;
+		skb2 = skb_copy_expand(skb, 4, padlen, flags);
+		dev_kfree_skb_any(skb);
+		skb = skb2;
+		if (!skb)
+			return NULL;
+	}
+
+	skb_push(skb, 4);
+	packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4);
+
+	cpu_to_le32s(&packet_len);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
+	memcpy(skb->data, &packet_len, sizeof(packet_len));
+#else
+	skb_copy_to_linear_data(skb, &packet_len, sizeof(packet_len));
+#endif
+
+	if ((skb->len % 512) == 0) {
+		cpu_to_le32s(&padbytes);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
+		memcpy(skb->tail, &padbytes, sizeof(padbytes));
+#else
+		memcpy(skb_tail_pointer(skb), &padbytes, sizeof(padbytes));
+#endif
+		skb_put(skb, sizeof(padbytes));
+	}
+
+	return skb;
+}
+
+static const u8 chkcntsel[6][3] = {
+	{12, 23, 31},
+	{12, 31, 23},
+	{23, 31, 12},
+	{23, 12, 31},
+	{31, 12, 23},
+	{31, 23, 12}
+};
+
+static void ax88772_save_bmcr_anar(struct usbnet *dev)
+{
+	struct ax88772_data *ax772_data = (struct ax88772_data *)dev->priv;
+
+	if (ax772_data) {
+		/* Preserve BMCR for restoring */
+		ax772_data->presvd_phy_bmcr =
+			ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_BMCR);
+
+		/* Preserve Advertisement control reg for restoring */
+		ax772_data->presvd_phy_advertise =
+			ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_ADVERTISE);
+	}
+}
+
+static void ax88772_restore_bmcr_anar(struct usbnet *dev)
+{
+	struct ax88772_data *ax772_data = (struct ax88772_data *)dev->priv;
+
+	if (ax772_data && ax772_data->presvd_phy_advertise && ax772_data->presvd_phy_bmcr) {
+		/* Restore Advertisement control reg */
+		ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+				      ax772_data->presvd_phy_advertise);
+		/* Restore BMCR */
+		ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_BMCR,
+				      ax772_data->presvd_phy_bmcr);
+		ax772_data->presvd_phy_advertise = 0;
+		ax772_data->presvd_phy_bmcr = 0;
+	}
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+static void ax88772_link_reset(void *data)
+{
+	struct usbnet *dev = (struct usbnet *)data;
+	struct ax88772_data *ax772_data = (struct ax88772_data *)dev->priv;
+#else
+static void ax88772_link_reset(struct work_struct *work)
+{
+	struct ax88772_data *ax772_data = container_of(work,
+						       struct ax88772_data,
+						       check_link);
+	struct usbnet *dev = ax772_data->dev;
+#endif
+	if (ax772_data->Event == AX_SET_RX_CFG) {
+		u16 bmcr;
+		u16 mode;
+
+		ax772_data->Event = AX_NOP;
+
+		mode = AX88772_MEDIUM_DEFAULT;
+
+		bmcr = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id,
+					    MII_BMCR);
+		if (!(bmcr & BMCR_FULLDPLX))
+			mode &= ~AX88772_MEDIUM_FULL_DUPLEX;
+		if (!(bmcr & BMCR_SPEED100))
+			mode &= ~AX88772_MEDIUM_100MB;
+
+		ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE,
+				  mode, 0, 0, NULL);
+		return;
+	}
+
+	switch (ax772_data->Event) {
+	case WAIT_AUTONEG_COMPLETE:
+		if (jiffies > (ax772_data->autoneg_start + 5 * HZ)) {
+			ax772_data->Event = PHY_POWER_DOWN;
+			ax772_data->TickToExpire = 23;
+		}
+		break;
+	case PHY_POWER_DOWN:
+		if (ax772_data->TickToExpire == 23) {
+			ax88772_save_bmcr_anar(dev);
+			/* Set Phy Power Down */
+			ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPPD,
+					  0, 0, NULL);
+			--ax772_data->TickToExpire;
+		} else if (--ax772_data->TickToExpire == 0) {
+			/* Set Phy Power Up */
+			ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+				AX_SWRESET_IPRL, 0, 0, NULL);
+			ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+				AX_SWRESET_IPPD | AX_SWRESET_IPRL, 0, 0, NULL);
+			msleep(10);
+			ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+				AX_SWRESET_IPRL, 0, 0, NULL);
+			msleep(60);
+			ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+				AX_SWRESET_CLEAR, 0, 0, NULL);
+			ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+				AX_SWRESET_IPRL, 0, 0, NULL);
+
+			if (ax772_data->presvd_phy_advertise && ax772_data->presvd_phy_bmcr) {
+				ax88772_restore_bmcr_anar(dev);
+				
+			} else {
+				ax8817x_mdio_write_le(dev->net, dev->mii.phy_id,
+						      MII_ADVERTISE,
+						      ADVERTISE_ALL | ADVERTISE_CSMA |
+						      ADVERTISE_PAUSE_CAP);
+				mii_nway_restart(&dev->mii);
+			}
+
+			ax772_data->Event = PHY_POWER_UP;
+			ax772_data->TickToExpire = 47;
+		}
+		break;
+	case PHY_POWER_UP:
+		if (--ax772_data->TickToExpire == 0) {
+			ax772_data->Event = PHY_POWER_DOWN;
+			ax772_data->TickToExpire = 23;
+		}
+		break;
+	default:
+		break;
+	}
+	return;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+static void ax88772a_link_reset(void *data)
+{
+	struct usbnet *dev = (struct usbnet *)data;
+	struct ax88772a_data *ax772a_data = (struct ax88772a_data *)dev->priv;
+#else
+static void ax88772a_link_reset(struct work_struct *work)
+{
+	struct ax88772a_data *ax772a_data = container_of(work,
+							 struct ax88772a_data,
+							 check_link);
+	struct usbnet *dev = ax772a_data->dev;
+#endif
+	int powsave = (ax772a_data->EepromData >> 14);
+	u16 phy_reg;
+
+	if (ax772a_data->Event == AX_SET_RX_CFG) {
+		u16 bmcr;
+		u16 mode;
+
+		ax772a_data->Event = AX_NOP;
+
+		mode = AX88772_MEDIUM_DEFAULT;
+
+		bmcr = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id,
+					    MII_BMCR);
+		if (!(bmcr & BMCR_FULLDPLX))
+			mode &= ~AX88772_MEDIUM_FULL_DUPLEX;
+		if (!(bmcr & BMCR_SPEED100))
+			mode &= ~AX88772_MEDIUM_100MB;
+
+		ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode,
+				  0, 0, NULL);
+
+		if (ax772a_data->presvd_phy_advertise && ax772a_data->presvd_phy_bmcr) {
+
+			/* Restore Advertisement control reg */
+			ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+					      ax772a_data->presvd_phy_advertise);
+			/* Restore BMCR */
+			ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_BMCR,
+					      ax772a_data->presvd_phy_bmcr);
+			ax772a_data->presvd_phy_advertise = 0;
+			ax772a_data->presvd_phy_bmcr = 0;
+		}
+
+		return;
+	}
+
+	switch (ax772a_data->Event) {
+	case WAIT_AUTONEG_COMPLETE:
+		if (jiffies > (ax772a_data->autoneg_start + 5 * HZ)) {
+			ax772a_data->Event = CHK_CABLE_EXIST;
+			ax772a_data->TickToExpire = 14;
+		}
+		break;
+	case CHK_CABLE_EXIST:
+		phy_reg = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, 0x12);
+		if ((phy_reg != 0x8012) && (phy_reg != 0x8013)) {
+			ax8817x_mdio_write_le(dev->net,
+				dev->mii.phy_id, 0x16, 0x4040);
+			mii_nway_restart(&dev->mii);
+			ax772a_data->Event = CHK_CABLE_STATUS;
+			ax772a_data->TickToExpire = 31;
+		} else if (--ax772a_data->TickToExpire == 0) {
+			mii_nway_restart(&dev->mii);
+			ax772a_data->Event = CHK_CABLE_EXIST_AGAIN;
+			if (powsave == 0x03) {
+				ax772a_data->TickToExpire = 47;
+			} else if (powsave == 0x01) {
+				ax772a_data->DlyIndex = (u8)(jiffies % 6);
+				ax772a_data->DlySel = 0;
+				ax772a_data->TickToExpire =
+			chkcntsel[ax772a_data->DlyIndex][ax772a_data->DlySel];
+			}
+		}
+		break;
+	case CHK_CABLE_EXIST_AGAIN:
+		/* if cable disconnected */
+		phy_reg = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, 0x12);
+		if ((phy_reg != 0x8012) && (phy_reg != 0x8013)) {
+			mii_nway_restart(&dev->mii);
+			ax772a_data->Event = CHK_CABLE_STATUS;
+			ax772a_data->TickToExpire = 31;
+		} else if (--ax772a_data->TickToExpire == 0) {
+			if (!ax772a_data->presvd_phy_advertise && !ax772a_data->presvd_phy_bmcr) {
+				/* Preserve BMCR for restoring */
+				ax772a_data->presvd_phy_bmcr =
+					ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_BMCR);
+
+				/* Preserve Advertisement control reg for restoring */
+				ax772a_data->presvd_phy_advertise =
+					ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_ADVERTISE);
+			}
+
+
+			/* Power down PHY */
+			ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+					  AX_SWRESET_IPPD,
+					  0, 0, NULL);
+			ax772a_data->Event = PHY_POWER_DOWN;
+			if (powsave == 0x03)
+				ax772a_data->TickToExpire = 23;
+			else if (powsave == 0x01)
+				ax772a_data->TickToExpire = 31;
+		}
+		break;
+	case PHY_POWER_DOWN:
+		if (--ax772a_data->TickToExpire == 0)
+			ax772a_data->Event = PHY_POWER_UP;
+		break;
+	case CHK_CABLE_STATUS:
+		if (--ax772a_data->TickToExpire == 0) {
+			ax8817x_mdio_write_le(dev->net,
+					dev->mii.phy_id, 0x16, 0x4040);
+			mii_nway_restart(&dev->mii);
+			ax772a_data->Event = CHK_CABLE_EXIST_AGAIN;
+			if (powsave == 0x03) {
+				ax772a_data->TickToExpire = 47;
+			} else if (powsave == 0x01) {
+				ax772a_data->DlyIndex = (u8)(jiffies % 6);
+				ax772a_data->DlySel = 0;
+				ax772a_data->TickToExpire =
+			chkcntsel[ax772a_data->DlyIndex][ax772a_data->DlySel];
+			}
+		}
+		break;
+	case PHY_POWER_UP:
+
+		if (!ax772a_data->presvd_phy_advertise && !ax772a_data->presvd_phy_bmcr) {
+			/* Preserve BMCR for restoring */
+			ax772a_data->presvd_phy_bmcr =
+				ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_BMCR);
+
+			/* Preserve Advertisement control reg for restoring */
+			ax772a_data->presvd_phy_advertise =
+				ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_ADVERTISE);
+		}
+
+		ax88772a_phy_powerup(dev);
+
+		ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+			ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
+
+		mii_nway_restart(&dev->mii);
+
+		ax772a_data->Event = CHK_CABLE_EXIST_AGAIN;
+
+		if (powsave == 0x03) {
+			ax772a_data->TickToExpire = 47;
+		} else if (powsave == 0x01) {
+			if (++ax772a_data->DlySel >= 3) {
+				ax772a_data->DlyIndex = (u8)(jiffies % 6);
+				ax772a_data->DlySel = 0;
+			}
+			ax772a_data->TickToExpire =
+			chkcntsel[ax772a_data->DlyIndex][ax772a_data->DlySel];
+		}
+		break;
+	default:
+		break;
+	}
+
+	return;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+static void ax88772b_link_reset(void *data)
+{
+	struct usbnet *dev = (struct usbnet *)data;
+	struct ax88772b_data *ax772b_data = (struct ax88772b_data *)dev->priv;
+#else
+static void ax88772b_link_reset(struct work_struct *work)
+{
+	struct ax88772b_data *ax772b_data = container_of(work,
+							 struct ax88772b_data,
+							 check_link);
+	struct usbnet *dev = ax772b_data->dev;
+#endif
+
+	switch (ax772b_data->Event) {
+
+	case AX_SET_RX_CFG:
+	{
+		u16 bmcr = ax8817x_mdio_read_le(dev->net,
+					dev->mii.phy_id, MII_BMCR);
+		u16 mode = AX88772_MEDIUM_DEFAULT;
+
+		if (!(bmcr & BMCR_FULLDPLX))
+			mode &= ~AX88772_MEDIUM_FULL_DUPLEX;
+		if (!(bmcr & BMCR_SPEED100))
+			mode &= ~AX88772_MEDIUM_100MB;	
+
+		if (ax772b_data->ext_phy_oui == EXTPHY_BROADCOM_OUI) {
+			if(ax772b_data->ext_phy_model == EXTPHY_BCM89811_MODEL) {
+				mode = AX88772_MEDIUM_DEFAULT;
+			}
+		}
+
+		ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode,
+				  0, 0, NULL);
+		break;
+	}
+	case PHY_POWER_UP:
+	{
+		u16 tmp16;
+
+		if (!ax772b_data->presvd_phy_advertise && !ax772b_data->presvd_phy_bmcr) {
+			/* Preserve BMCR for restoring */
+			ax772b_data->presvd_phy_bmcr =
+				ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_BMCR);
+
+			/* Preserve Advertisement control reg for restoring */
+			ax772b_data->presvd_phy_advertise =
+				ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_ADVERTISE);
+		}
+
+		ax88772a_phy_powerup(dev);
+		tmp16 = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, 0x12);
+		ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, 0x12,
+				((tmp16 & 0xFF9F) | 0x0040));
+
+		/* Restore Advertisement control reg */
+		ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+				      ax772b_data->presvd_phy_advertise);
+		/* Restore BMCR */
+		ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_BMCR,
+				      ax772b_data->presvd_phy_bmcr);
+		ax772b_data->presvd_phy_advertise = 0;
+		ax772b_data->presvd_phy_bmcr = 0;
+
+		break;
+	}
+
+	case AX_CHK_AUTODETACH:
+	{
+		int ret;
+		ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+					AX_SWRESET_IPRL |
+					(ax772b_data->psc & 0x7FFF),
+					0, 0, NULL);
+		if (ret < 0) {
+			deverr(dev, "Failed to configure PHY power saving: %d",
+			       ret);
+		}
+
+		break;
+	}
+	default:
+		break;
+	}
+
+	ax772b_data->Event = AX_NOP;
+	
+	return;
+}
+
+static int ax88178_set_media(struct usbnet *dev)
+{
+	int	ret;
+	struct ax88178_data *ax178dataptr = (struct ax88178_data *)dev->priv;
+	int media;
+
+	media = ax88178_media_check(dev, ax178dataptr);
+	if (media < 0)
+		return media;
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, media, 0,
+				0, NULL);
+	if (ret < 0) {
+		deverr(dev, "write mode medium reg failed: %d", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ax88178_link_reset(struct usbnet *dev)
+{
+	return ax88178_set_media(dev);
+}
+
+static int ax_suspend(struct usb_interface *intf,
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 10)
+			pm_message_t message)
+#else
+			u32 message)
+#endif
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	struct ax8817x_data *data = (struct ax8817x_data *)&dev->data;
+
+	return data->suspend(intf, message);
+}
+
+static int ax_resume(struct usb_interface *intf)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	struct ax8817x_data *data = (struct ax8817x_data *)&dev->data;
+
+	return data->resume(intf);
+}
+
+static const struct driver_info ax88178_info = {
+	.description = "ASIX AX88178 USB 2.0 Ethernet",
+	.bind	= ax88178_bind,
+	.unbind = ax88178_unbind,
+	.status = ax88178_status,
+	.link_reset = ax88178_link_reset,
+	.reset	= ax88178_link_reset,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
+	.stop	= ax88772b_stop,
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX | FLAG_AVOID_UNLINK_URBS,
+#else
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX,
+#endif
+	.rx_fixup = ax88772_rx_fixup,
+	.tx_fixup = ax88772_tx_fixup,
+};
+
+static const struct driver_info belkin178_info = {
+	.description = "Belkin Gigabit USB 2.0 Network Adapter",
+	.bind	= ax88178_bind,
+	.unbind	= ax88178_unbind,
+	.status	= ax88178_status,
+	.link_reset = ax88178_link_reset,
+	.reset	= ax88178_link_reset,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
+	.stop	= ax88772b_stop,
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX | FLAG_AVOID_UNLINK_URBS,
+#else
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX,
+#endif
+	.rx_fixup = ax88772_rx_fixup,
+	.tx_fixup = ax88772_tx_fixup,
+};
+
+static const struct driver_info ax8817x_info = {
+	.description = "ASIX AX8817x USB 2.0 Ethernet",
+	.bind	= ax8817x_bind,
+	.status	= ax8817x_status,
+	.link_reset = ax88172_link_reset,
+	.reset	= ax88172_link_reset,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
+	.stop	= ax88772b_stop,
+	.flags	= FLAG_ETHER | FLAG_AVOID_UNLINK_URBS,
+#else
+	.flags	= FLAG_ETHER,
+#endif
+};
+
+static const struct driver_info dlink_dub_e100_info = {
+	.description = "DLink DUB-E100 USB Ethernet",
+	.bind	= ax8817x_bind,
+	.status	= ax8817x_status,
+	.link_reset = ax88172_link_reset,
+	.reset	= ax88172_link_reset,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
+	.stop	= ax88772b_stop,
+	.flags	= FLAG_ETHER | FLAG_AVOID_UNLINK_URBS,
+#else
+	.flags	= FLAG_ETHER,
+#endif
+};
+
+static const struct driver_info netgear_fa120_info = {
+	.description = "Netgear FA-120 USB Ethernet",
+	.bind	= ax8817x_bind,
+	.status = ax8817x_status,
+	.link_reset = ax88172_link_reset,
+	.reset	= ax88172_link_reset,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
+	.stop	= ax88772b_stop,
+	.flags	= FLAG_ETHER | FLAG_AVOID_UNLINK_URBS,
+#else
+	.flags	= FLAG_ETHER,
+#endif
+};
+
+static const struct driver_info hawking_uf200_info = {
+	.description = "Hawking UF200 USB Ethernet",
+	.bind	= ax8817x_bind,
+	.status = ax8817x_status,
+	.link_reset = ax88172_link_reset,
+	.reset	= ax88172_link_reset,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
+	.stop	= ax88772b_stop,
+	.flags	= FLAG_ETHER | FLAG_AVOID_UNLINK_URBS,
+#else
+	.flags	= FLAG_ETHER,
+#endif
+};
+
+static const struct driver_info ax88772_info = {
+	.description = "ASIX AX88772 USB 2.0 Ethernet",
+	.bind	= ax88772_bind,
+	.unbind = ax88772_unbind,
+	.status = ax88772_status,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
+	.stop	= ax88772b_stop,
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX | FLAG_AVOID_UNLINK_URBS,
+#else
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX,
+#endif
+	.rx_fixup = ax88772_rx_fixup,
+	.tx_fixup = ax88772_tx_fixup,
+	.reset	= ax88772b_reset,
+};
+
+static const struct driver_info dlink_dub_e100b_info = {
+	.description = "D-Link DUB-E100 USB 2.0 Fast Ethernet Adapter",
+	.bind	= ax88772_bind,
+	.unbind = ax88772_unbind,
+	.status = ax88772_status,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
+	.stop	= ax88772b_stop,
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX | FLAG_AVOID_UNLINK_URBS,
+#else
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX,
+#endif
+	.rx_fixup = ax88772_rx_fixup,
+	.tx_fixup = ax88772_tx_fixup,
+	.reset	= ax88772b_reset,
+};
+
+static const struct driver_info dlink_dub_e100_772b_info = {
+	.description = "D-Link DUB-E100 USB 2.0 Fast Ethernet Adapter",
+	.bind	= ax88772b_bind,
+	.unbind = ax88772b_unbind,
+	.status = ax88772b_status,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
+	.stop	= ax88772b_stop,
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX | FLAG_HW_IP_ALIGNMENT | FLAG_AVOID_UNLINK_URBS,
+#else
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX | FLAG_HW_IP_ALIGNMENT,
+#endif
+	.rx_fixup = ax88772b_rx_fixup,
+	.tx_fixup = ax88772b_tx_fixup,
+	.reset	= ax88772b_reset,
+};
+
+static const struct driver_info dlink_dub_e100_772c_info = {
+	.description = "D-Link DUB-E100 USB 2.0 Fast Ethernet Adapter",
+	.bind	= ax88772b_bind,
+	.unbind = ax88772b_unbind,
+	.status = ax88772b_status,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
+	.stop	= ax88772b_stop,
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX | FLAG_HW_IP_ALIGNMENT | FLAG_AVOID_UNLINK_URBS,
+#else
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX | FLAG_HW_IP_ALIGNMENT,
+#endif
+	.rx_fixup = ax88772b_rx_fixup,
+	.tx_fixup = ax88772b_tx_fixup,
+	.reset	= ax88772b_reset,
+};
+
+static const struct driver_info ax88772a_info = {
+	.description = "ASIX AX88772A USB 2.0 Ethernet",
+	.bind	= ax88772a_bind,
+	.unbind = ax88772a_unbind,
+	.status = ax88772a_status,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
+	.stop	= ax88772b_stop,
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX | FLAG_AVOID_UNLINK_URBS,
+#else
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX,
+#endif
+	.rx_fixup = ax88772_rx_fixup,
+	.tx_fixup = ax88772_tx_fixup,
+	.reset	= ax88772b_reset,
+};
+
+static const struct driver_info ax88772b_info = {
+	.description = "ASIX AX88772B USB 2.0 Ethernet",
+	.bind	= ax88772b_bind,
+	.unbind = ax88772b_unbind,
+	.status = ax88772b_status,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
+	.stop	= ax88772b_stop,
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX | FLAG_HW_IP_ALIGNMENT | FLAG_AVOID_UNLINK_URBS,
+#else
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX | FLAG_HW_IP_ALIGNMENT,
+#endif
+	.rx_fixup = ax88772b_rx_fixup,
+	.tx_fixup = ax88772b_tx_fixup,
+	.reset	= ax88772b_reset,
+};
+
+static const struct driver_info ax88772c_info = {
+	.description = "ASIX AX88772C USB 2.0 Ethernet",
+	.bind	= ax88772b_bind,
+	.unbind = ax88772b_unbind,
+	.status = ax88772c_status,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
+	.stop	= ax88772b_stop,
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX | FLAG_HW_IP_ALIGNMENT | FLAG_AVOID_UNLINK_URBS,
+#else
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX | FLAG_HW_IP_ALIGNMENT,
+#endif
+	.rx_fixup = ax88772b_rx_fixup,
+	.tx_fixup = ax88772b_tx_fixup,
+	.reset	= ax88772b_reset,
+};
+
+static const struct usb_device_id products[] = {
+{
+	/* 88178 */
+	USB_DEVICE(0x0b95, 0x1780),
+	.driver_info =	(unsigned long) &ax88178_info,
+}, {
+	/* 88178 for billianton linksys */
+	USB_DEVICE(0x077b, 0x2226),
+	.driver_info =	(unsigned long) &ax88178_info,
+}, {
+	/* ABOCOM for linksys */
+	USB_DEVICE(0x1737, 0x0039),
+	.driver_info =	(unsigned long) &ax88178_info,
+}, {
+	/* ABOCOM  for pci */
+	USB_DEVICE(0x14ea, 0xab11),
+	.driver_info =	(unsigned long) &ax88178_info,
+}, {
+	/* Belkin */
+	USB_DEVICE(0x050d, 0x5055),
+	.driver_info =	(unsigned long) &belkin178_info,
+}, {
+	/* 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,
+}, {
+	/* DLink DUB-E100B */
+	USB_DEVICE(0x2001, 0x3c05),
+	.driver_info =  (unsigned long) &dlink_dub_e100b_info,
+}, {
+	/* DLink DUB-E100B */
+	USB_DEVICE(0x07d1, 0x3c05),
+	.driver_info =  (unsigned long) &dlink_dub_e100b_info,
+}, {
+	/* DLink DUB-E100 (AX88772B)*/
+	USB_DEVICE_VER(0x2001, 0x1a02, 0, 1),
+	.driver_info =  (unsigned long) &dlink_dub_e100_772b_info,
+}, {
+	/* DLink DUB-E100 (AX88772C)*/
+	USB_DEVICE_VER(0x2001, 0x1a02, 0, 2),
+	.driver_info =  (unsigned long) &dlink_dub_e100_772c_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,
+}, {
+	/* ASIX AX88772 10/100 */
+	USB_DEVICE(0x125E, 0x180D),
+	.driver_info = (unsigned long) &ax88772_info,
+}, {
+	/* ASIX AX88772A 10/100 */
+	USB_DEVICE(0x0b95, 0x772A),
+	.driver_info = (unsigned long) &ax88772a_info,
+}, {
+	/* ASIX AX88772A 10/100 */
+	USB_DEVICE(0x0db0, 0xA877),
+	.driver_info = (unsigned long) &ax88772a_info,
+}, {
+	/* ASIX AX88772A 10/100 */
+	USB_DEVICE(0x0421, 0x772A),
+	.driver_info = (unsigned long) &ax88772a_info,
+}, {
+	/* Linksys 200M */
+	 USB_DEVICE(0x13B1, 0x0018),
+	.driver_info = (unsigned long) &ax88772a_info,
+}, {
+	USB_DEVICE(0x05ac, 0x1402),
+	.driver_info = (unsigned long) &ax88772a_info,
+}, {
+	/* ASIX AX88772B 10/100 */
+	USB_DEVICE_VER(0x0b95, 0x772B, 0, 1),
+	.driver_info = (unsigned long) &ax88772b_info,
+}, {
+	/* Asus AX88772B 10/100 */
+	USB_DEVICE_VER(0x0b95, 0x7e2b, 0, 1),
+	.driver_info = (unsigned long) &ax88772b_info,
+}, {
+	/* Lenovo AX88772B 10/100 */
+	USB_DEVICE_VER(0x17ef, 0x7203, 0, 1),
+	.driver_info = (unsigned long) &ax88772b_info,
+},{
+	/* ASIX AX88772B ver.2 10/100 */
+	USB_DEVICE_VER(0x0b95, 0x772B, 0, 2),
+	.driver_info = (unsigned long) &ax88772c_info,
+},
+	{ },		/* END */
+};
+MODULE_DEVICE_TABLE(usb, products);
+
+static struct usb_driver asix_driver = {
+	/* .owner =	THIS_MODULE, */
+	.name =		"asix",
+	.id_table =	products,
+	.probe =	axusbnet_probe,
+	.suspend =	ax_suspend,
+	.resume =	ax_resume,
+	.disconnect =	axusbnet_disconnect,
+};
+
+static int __init asix_init(void)
+{
+	return usb_register(&asix_driver);
+}
+module_init(asix_init);
+
+static void __exit asix_exit(void)
+{
+	usb_deregister(&asix_driver);
+}
+module_exit(asix_exit);
+
+MODULE_AUTHOR("David Hollis");
+MODULE_DESCRIPTION("ASIX AX8817X based USB 2.0 Ethernet Devices");
+MODULE_LICENSE("GPL");
+
diff --git a/kernel/drivers/net/usb/AX88772C_eeprom/asix.h b/kernel/drivers/net/usb/AX88772C_eeprom/asix.h
new file mode 100644
index 0000000..a0a59e8
--- /dev/null
+++ b/kernel/drivers/net/usb/AX88772C_eeprom/asix.h
@@ -0,0 +1,575 @@
+#ifndef	__LINUX_USBNET_ASIX_H
+#define	__LINUX_USBNET_ASIX_H
+
+/*
+ * Turn on this flag if the implementation of your USB host controller
+ * cannot handle non-double word aligned buffer.
+ * When turn on this flag, driver will fixup egress packet aligned on double
+ * word boundary before deliver to USB host controller. And will Disable the
+ * function "skb_reserve (skb, NET_IP_ALIGN)" to retain the buffer aligned on
+ * double word alignment for ingress packets.
+ */
+#define AX_FORCE_BUFF_ALIGN		0
+
+//#define RX_SKB_COPY
+
+#define AX_MONITOR_MODE			0x01
+#define AX_MONITOR_LINK			0x02
+#define AX_MONITOR_MAGIC		0x04
+#define AX_MONITOR_HSFS			0x10
+
+/* AX88172 Medium Status Register values */
+#define AX_MEDIUM_FULL_DUPLEX		0x02
+#define AX_MEDIUM_TX_ABORT_ALLOW	0x04
+#define AX_MEDIUM_FLOW_CONTROL_EN	0x10
+#define AX_MCAST_FILTER_SIZE		8
+#define AX_MAX_MCAST			64
+
+#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 AX_SWRESET_IPOSC		0x0080
+#define AX_SWRESET_IPPSL_0		0x0100
+#define AX_SWRESET_IPPSL_1		0x0200
+#define AX_SWRESET_IPCOPS		0x0400
+#define AX_SWRESET_IPCOPSC		0x0800
+#define AX_SWRESET_AUTODETACH		0x1000
+#define AX_SWRESET_WOLLP		0x8000
+
+#define AX88772_IPG0_DEFAULT		0x15
+#define AX88772_IPG1_DEFAULT		0x0c
+#define AX88772_IPG2_DEFAULT		0x0E
+
+#define AX88772A_IPG0_DEFAULT		0x15
+#define AX88772A_IPG1_DEFAULT		0x16
+#define AX88772A_IPG2_DEFAULT		0x1A
+
+#define AX88772_MEDIUM_FULL_DUPLEX	0x0002
+#define AX88772_MEDIUM_RESERVED		0x0004
+#define AX88772_MEDIUM_RX_FC_ENABLE	0x0010
+#define AX88772_MEDIUM_TX_FC_ENABLE	0x0020
+#define AX88772_MEDIUM_PAUSE_FORMAT	0x0080
+#define AX88772_MEDIUM_RX_ENABLE	0x0100
+#define AX88772_MEDIUM_100MB		0x0200
+#define AX88772_MEDIUM_DEFAULT	\
+	(AX88772_MEDIUM_FULL_DUPLEX | AX88772_MEDIUM_RX_FC_ENABLE | \
+	 AX88772_MEDIUM_TX_FC_ENABLE | AX88772_MEDIUM_100MB | \
+	 AX88772_MEDIUM_RESERVED | AX88772_MEDIUM_RX_ENABLE)
+
+#define AX_CMD_SET_SW_MII		0x06
+#define AX_CMD_READ_MII_REG		0x07
+#define AX_CMD_WRITE_MII_REG		0x08
+#define AX_CMD_READ_STATMNGSTS_REG	0x09
+	#define AX_HOST_EN		0x01
+
+#define AX_CMD_SET_HW_MII		0x0a
+#define AX_CMD_READ_EEPROM		0x0b
+#define AX_CMD_WRITE_EEPROM		0x0c
+#define AX_CMD_WRITE_EEPROM_EN		0x0d
+#define AX_CMD_WRITE_EEPROM_DIS		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_READ_MEDIUM_MODE		0x1a
+#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 AX_PHYSEL_PSEL		(1 << 0)
+	#define AX_PHYSEL_ASEL		(1 << 1)
+	#define AX_PHYSEL_SSMII		(0 << 2)
+	#define AX_PHYSEL_SSRMII	(1 << 2)
+	#define AX_PHYSEL_SSRRMII	(3 << 2)
+	#define AX_PHYSEL_SSEN		(1 << 4)
+#define AX88772_CMD_READ_NODE_ID	0x13
+#define AX88772_CMD_WRITE_NODE_ID	0x14
+#define AX_CMD_READ_WKFARY		0x23
+#define AX_CMD_WRITE_WKFARY		0x24
+#define AX_CMD_READ_RXCOE_CTL		0x2b
+#define AX_CMD_WRITE_RXCOE_CTL		0x2c
+#define AX_CMD_READ_TXCOE_CTL		0x2d
+#define AX_CMD_WRITE_TXCOE_CTL		0x2e
+
+#define REG_LENGTH			2
+#define PHY_ID_MASK			0x1f
+
+#define AX_RXCOE_IPCE			0x0001
+#define AX_RXCOE_IPVE			0x0002
+#define AX_RXCOE_V6VE			0x0004
+#define AX_RXCOE_TCPE			0x0008
+#define AX_RXCOE_UDPE			0x0010
+#define AX_RXCOE_ICMP			0x0020
+#define AX_RXCOE_IGMP			0x0040
+#define AX_RXCOE_ICV6			0x0080
+#define AX_RXCOE_TCPV6			0x0100
+#define AX_RXCOE_UDPV6			0x0200
+#define AX_RXCOE_ICMV6			0x0400
+#define AX_RXCOE_IGMV6			0x0800
+#define AX_RXCOE_ICV6V6			0x1000
+#define AX_RXCOE_FOPC			0x8000
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22)
+#define AX_RXCOE_DEF_CSUM		(AX_RXCOE_IPCE | AX_RXCOE_IPVE | \
+					 AX_RXCOE_V6VE | AX_RXCOE_TCPE | \
+					 AX_RXCOE_UDPE |  AX_RXCOE_ICV6 | \
+					 AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6)
+#else
+#define AX_RXCOE_DEF_CSUM		(AX_RXCOE_IPCE | AX_RXCOE_IPVE | \
+					 AX_RXCOE_TCPE | AX_RXCOE_UDPE)
+#endif
+
+#define AX_RXCOE_64TE			0x0100
+#define AX_RXCOE_PPPOE			0x0200
+#define AX_RXCOE_RPCE			0x8000
+
+#define AX_TXCOE_IP			0x0001
+#define AX_TXCOE_TCP			0x0002
+#define AX_TXCOE_UDP			0x0004
+#define AX_TXCOE_ICMP			0x0008
+#define AX_TXCOE_IGMP			0x0010
+#define AX_TXCOE_ICV6			0x0020
+
+#define AX_TXCOE_TCPV6			0x0100
+#define AX_TXCOE_UDPV6			0x0200
+#define AX_TXCOE_ICMV6			0x0400
+#define AX_TXCOE_IGMV6			0x0800
+#define AX_TXCOE_ICV6V6			0x1000
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22)
+#define AX_TXCOE_DEF_CSUM		(AX_TXCOE_TCP | AX_TXCOE_UDP | \
+					 AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6)
+#else
+#define AX_TXCOE_DEF_CSUM		(AX_TXCOE_TCP | AX_TXCOE_UDP)
+#endif
+
+#define AX_TXCOE_64TE			0x0001
+#define AX_TXCOE_PPPE			0x0002
+
+#define AX88772B_MAX_BULKIN_2K		0
+#define AX88772B_MAX_BULKIN_4K		1
+#define AX88772B_MAX_BULKIN_6K		2
+#define AX88772B_MAX_BULKIN_8K		3
+#define AX88772B_MAX_BULKIN_16K		4
+#define AX88772B_MAX_BULKIN_20K		5
+#define AX88772B_MAX_BULKIN_24K		6
+#define AX88772B_MAX_BULKIN_32K		7
+struct {unsigned short size, byte_cnt, threshold; } AX88772B_BULKIN_SIZE[] = {
+	/* 2k */
+	{2048, 0x8000, 0x8001},
+	/* 4k */
+	{4096, 0x8100, 0x8147},
+	/* 6k */
+	{6144, 0x8200, 0x81EB},
+	/* 8k */
+	{8192, 0x8300, 0x83D7},
+	/* 16 */
+	{16384, 0x8400, 0x851E},
+	/* 20k */
+	{20480, 0x8500, 0x8666},
+	/* 24k */
+	{24576, 0x8600, 0x87AE},
+	/* 32k */
+	{32768, 0x8700, 0x8A3D},
+};
+
+
+#define AX_RX_CTL_RH1M			0x0100		/* Enable RX-Header mode 0 */
+#define AX_RX_CTL_RH2M			0x0200		/* Enable IP header in receive buffer aligned on 32-bit aligment */
+#define AX_RX_CTL_RH3M			0x0400		/* checksum value in rx header 3 */
+#define AX_RX_HEADER_DEFAULT		(AX_RX_CTL_RH1M | AX_RX_CTL_RH2M)
+
+#define AX_RX_CTL_MFB			0x0300		/* Maximum Frame size 16384bytes */
+#define AX_RX_CTL_START			0x0080		/* Ethernet MAC start */
+#define AX_RX_CTL_AP			0x0020		/* Accept physcial address from Multicast array */
+#define AX_RX_CTL_AM			0x0010
+#define AX_RX_CTL_AB			0x0008		/* Accetp Brocadcast frames*/
+#define AX_RX_CTL_SEP			0x0004		/* Save error packets */
+#define AX_RX_CTL_AMALL			0x0002		/* Accetp all multicast frames */
+#define AX_RX_CTL_PRO			0x0001		/* Promiscuous Mode */
+#define AX_RX_CTL_STOP			0x0000		/* Stop MAC */
+
+#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_EEPROM_MAGIC			0xdeadbeef
+#define EEPROMMASK			0x7f
+
+/* GPIO REGISTER */
+#define AXGPIOS_GPO0EN			0X01 /* 1 << 0 */
+#define AXGPIOS_GPO0			0X02 /* 1 << 1 */
+#define AXGPIOS_GPO1EN			0X04 /*	1 << 2 */
+#define AXGPIOS_GPO1			0X08 /* 1 << 3 */
+#define AXGPIOS_GPO2EN			0X10 /* 1 << 4 */
+#define AXGPIOS_GPO2			0X20 /* 1 << 5 */
+#define AXGPIOS_RSE			0X80 /* 1 << 7 */
+
+/* TX-header format */
+#define AX_TX_HDR_CPHI			0x4000
+#define AX_TX_HDR_DICF			0x8000
+
+/* GMII register definitions */
+#define GMII_PHY_CONTROL		0x00	/* control reg */
+#define GMII_PHY_STATUS			0x01	/* status reg */
+#define GMII_PHY_OUI			0x02	/* most of the OUI bits */
+#define GMII_PHY_MODEL			0x03	/* model/rev bits, and rest of OUI */
+#define GMII_PHY_ANAR			0x04	/* AN advertisement reg */
+#define GMII_PHY_ANLPAR			0x05	/* AN Link Partner */
+#define GMII_PHY_ANER			0x06	/* AN expansion reg */
+#define GMII_PHY_1000BT_CONTROL		0x09	/* control reg for 1000BT */
+#define GMII_PHY_1000BT_STATUS		0x0A	/* status reg for 1000BT */
+
+/* Bit definitions: GMII Control */
+#define GMII_CONTROL_RESET		0x8000	/* reset bit in control reg */
+#define GMII_CONTROL_LOOPBACK		0x4000	/* loopback bit in control reg */
+#define GMII_CONTROL_10MB		0x0000	/* 10 Mbit */
+#define GMII_CONTROL_100MB		0x2000	/* 100Mbit */
+#define GMII_CONTROL_1000MB		0x0040	/* 1000Mbit */
+#define GMII_CONTROL_SPEED_BITS		0x2040	/* speed bit mask */
+#define GMII_CONTROL_ENABLE_AUTO	0x1000	/* autonegotiate enable */
+#define GMII_CONTROL_POWER_DOWN		0x0800
+#define GMII_CONTROL_ISOLATE		0x0400	/* islolate bit */
+#define GMII_CONTROL_START_AUTO		0x0200	/* restart autonegotiate */
+#define GMII_CONTROL_FULL_DUPLEX	0x0100
+
+/* Bit definitions: GMII Status */
+#define GMII_STATUS_100MB_MASK		0xE000	/* any of these indicate 100 Mbit */
+#define GMII_STATUS_10MB_MASK		0x1800	/* either of these indicate 10 Mbit */
+#define GMII_STATUS_AUTO_DONE		0x0020	/* auto negotiation complete */
+#define GMII_STATUS_AUTO		0x0008	/* auto negotiation is available */
+#define GMII_STATUS_LINK_UP		0x0004	/* link status bit */
+#define GMII_STATUS_EXTENDED		0x0001	/* extended regs exist */
+#define GMII_STATUS_100T4		0x8000	/* capable of 100BT4 */
+#define GMII_STATUS_100TXFD		0x4000	/* capable of 100BTX full duplex */
+#define GMII_STATUS_100TX		0x2000	/* capable of 100BTX */
+#define GMII_STATUS_10TFD		0x1000	/* capable of 10BT full duplex */
+#define GMII_STATUS_10T			0x0800	/* capable of 10BT */
+
+/* Bit definitions: Auto-Negotiation Advertisement */
+#define GMII_ANAR_ASYM_PAUSE		0x0800	/* support asymetric pause */
+#define GMII_ANAR_PAUSE			0x0400	/* support pause packets */
+#define GMII_ANAR_100T4			0x0200	/* support 100BT4 */
+#define GMII_ANAR_100TXFD		0x0100	/* support 100BTX full duplex */
+#define GMII_ANAR_100TX			0x0080	/* support 100BTX half duplex */
+#define GMII_ANAR_10TFD			0x0040	/* support 10BT full duplex */
+#define GMII_ANAR_10T			0x0020	/* support 10BT half duplex */
+#define GMII_SELECTOR_FIELD		0x001F	/* selector field. */
+
+/* Bit definitions: Auto-Negotiation Link Partner Ability */
+#define GMII_ANLPAR_100T4		0x0200	/* support 100BT4 */
+#define GMII_ANLPAR_100TXFD		0x0100	/* support 100BTX full duplex */
+#define GMII_ANLPAR_100TX		0x0080	/* support 100BTX half duplex */
+#define GMII_ANLPAR_10TFD		0x0040	/* support 10BT full duplex */
+#define GMII_ANLPAR_10T			0x0020	/* support 10BT half duplex */
+#define GMII_ANLPAR_PAUSE		0x0400	/* support pause packets */
+#define GMII_ANLPAR_ASYM_PAUSE		0x0800	/* support asymetric pause */
+#define GMII_ANLPAR_ACK			0x4000	/* means LCB was successfully rx'd */
+#define GMII_SELECTOR_8023		0x0001;
+
+/* Bit definitions: 1000BaseT AUX Control */
+#define GMII_1000_AUX_CTRL_MASTER_SLAVE		0x1000
+#define GMII_1000_AUX_CTRL_FD_CAPABLE		0x0200	/* full duplex capable */
+#define GMII_1000_AUX_CTRL_HD_CAPABLE		0x0100	/* half duplex capable */
+
+/* Bit definitions: 1000BaseT AUX Status */
+#define GMII_1000_AUX_STATUS_FD_CAPABLE		0x0800	/* full duplex capable */
+#define GMII_1000_AUX_STATUS_HD_CAPABLE		0x0400	/* half duplex capable */
+
+/* Cicada MII Registers */
+#define GMII_AUX_CTRL_STATUS			0x1C
+#define GMII_AUX_ANEG_CPLT			0x8000
+#define GMII_AUX_FDX				0x0020
+#define GMII_AUX_SPEED_1000			0x0010
+#define GMII_AUX_SPEED_100			0x0008
+
+#ifndef ADVERTISE_PAUSE_CAP
+#define ADVERTISE_PAUSE_CAP			0x0400
+#endif
+
+#ifndef MII_STAT1000
+#define MII_STAT1000				0x000A
+#endif
+
+#ifndef LPA_1000FULL
+#define LPA_1000FULL				0x0800
+#endif
+
+/* medium mode register */
+#define MEDIUM_GIGA_MODE			0x0001
+#define MEDIUM_FULL_DUPLEX_MODE			0x0002
+#define MEDIUM_TX_ABORT_MODE			0x0004
+#define MEDIUM_ENABLE_125MHZ			0x0008
+#define MEDIUM_ENABLE_RX_FLOWCTRL		0x0010
+#define MEDIUM_ENABLE_TX_FLOWCTRL		0x0020
+#define MEDIUM_ENABLE_JUMBO_FRAME		0x0040
+#define MEDIUM_CHECK_PAUSE_FRAME_MODE		0x0080
+#define MEDIUM_ENABLE_RECEIVE			0x0100
+#define MEDIUM_MII_100M_MODE			0x0200
+#define MEDIUM_ENABLE_JAM_PATTERN		0x0400
+#define MEDIUM_ENABLE_STOP_BACKPRESSURE		0x0800
+#define MEDIUM_ENABLE_SUPPER_MAC_SUPPORT	0x1000
+
+/* PHY mode */
+#define PHY_MODE_MARVELL		0
+#define PHY_MODE_CICADA_FAMILY		1
+#define PHY_MODE_CICADA_V1		1
+#define PHY_MODE_AGERE_FAMILY		2
+#define PHY_MODE_AGERE_V0		2
+#define PHY_MODE_CICADA_V2		5
+#define PHY_MODE_AGERE_V0_GMII		6
+#define PHY_MODE_CICADA_V2_ASIX		9
+#define PHY_MODE_VSC8601		10
+#define PHY_MODE_RTL8211CL		12
+#define PHY_MODE_RTL8211BN		13
+#define PHY_MODE_RTL8251CL		14
+#define PHY_MODE_ATTANSIC_V0		0x40
+#define PHY_MODE_ATTANSIC_FAMILY	0x40
+#define PHY_MODE_MAC_TO_MAC_GMII	0x7C
+
+/*  */
+#define LED_MODE_MARVELL		0
+#define LED_MODE_CAMEO			1
+
+#define MARVELL_LED_CTRL		0x18
+#define MARVELL_MANUAL_LED		0x19
+
+#define PHY_IDENTIFIER			0x0002
+#define PHY_AGERE_IDENTIFIER		0x0282
+#define PHY_CICADA_IDENTIFIER		0x000f
+#define PHY_MARVELL_IDENTIFIER		0x0141
+
+#define PHY_MARVELL_STATUS		0x001b
+#define MARVELL_STATUS_HWCFG		0x0004		/* SGMII without clock */
+
+#define PHY_MARVELL_CTRL		0x0014
+#define MARVELL_CTRL_RXDELAY		0x0080
+#define MARVELL_CTRL_TXDELAY		0x0002
+
+#define PHY_CICADA_EXTPAGE		0x001f
+#define CICADA_EXTPAGE_EN		0x0001
+#define CICADA_EXTPAGE_DIS		0x0000
+
+/* External ethernet phy */
+#define EXTPHY_ID_MASK_OUI(phyid1, phyid2) ((phyid1 << 6) | ((phyid2 & 0xFC00) >> 10))
+#define EXTPHY_ID_MASK_MODEL(phyid2) ((phyid2 & 0x3F0) >> 4) 
+
+#define EXTPHY_BROADCOM_OUI		0x2B8094
+#define EXTPHY_BCM89811_MODEL		0x02
+
+struct {unsigned short value, offset; } CICADA_FAMILY_HWINIT[] = {
+	{0x0001, 0x001f}, {0x1c25, 0x0017}, {0x2a30, 0x001f}, {0x234c, 0x0010},
+	{0x2a30, 0x001f}, {0x0212, 0x0008}, {0x52b5, 0x001f}, {0xa7fa, 0x0000},
+	{0x0012, 0x0002}, {0x3002, 0x0001}, {0x87fa, 0x0000}, {0x52b5, 0x001f},
+	{0xafac, 0x0000}, {0x000d, 0x0002}, {0x001c, 0x0001}, {0x8fac, 0x0000},
+	{0x2a30, 0x001f}, {0x0012, 0x0008}, {0x2a30, 0x001f}, {0x0400, 0x0014},
+	{0x2a30, 0x001f}, {0x0212, 0x0008}, {0x52b5, 0x001f}, {0xa760, 0x0000},
+	{0x0000, 0x0002}, {0xfaff, 0x0001}, {0x8760, 0x0000}, {0x52b5, 0x001f},
+	{0xa760, 0x0000}, {0x0000, 0x0002}, {0xfaff, 0x0001}, {0x8760, 0x0000},
+	{0x52b5, 0x001f}, {0xafae, 0x0000}, {0x0004, 0x0002}, {0x0671, 0x0001},
+	{0x8fae, 0x0000}, {0x2a30, 0x001f}, {0x0012, 0x0008}, {0x0000, 0x001f},
+};
+
+struct {unsigned short value, offset; } CICADA_V2_HWINIT[] = {
+	{0x2a30, 0x001f}, {0x0212, 0x0008}, {0x52b5, 0x001f}, {0x000f, 0x0002},
+	{0x472a, 0x0001}, {0x8fa4, 0x0000}, {0x2a30, 0x001f}, {0x0212, 0x0008},
+	{0x0000, 0x001f},
+};
+
+struct {unsigned short value, offset; } CICADA_V2_ASIX_HWINIT[] = {
+	{0x2a30, 0x001f}, {0x0212, 0x0008}, {0x52b5, 0x001f}, {0x0012, 0x0002},
+	{0x3002, 0x0001}, {0x87fa, 0x0000}, {0x52b5, 0x001f}, {0x000f, 0x0002},
+	{0x472a, 0x0001}, {0x8fa4, 0x0000}, {0x2a30, 0x001f}, {0x0212, 0x0008},
+	{0x0000, 0x001f},
+};
+
+struct {unsigned short value, offset; } AGERE_FAMILY_HWINIT[] = {
+	{0x0800, 0x0000}, {0x0007, 0x0012}, {0x8805, 0x0010}, {0xb03e, 0x0011},
+	{0x8808, 0x0010}, {0xe110, 0x0011}, {0x8806, 0x0010}, {0xb03e, 0x0011},
+	{0x8807, 0x0010}, {0xff00, 0x0011}, {0x880e, 0x0010}, {0xb4d3, 0x0011},
+	{0x880f, 0x0010}, {0xb4d3, 0x0011}, {0x8810, 0x0010}, {0xb4d3, 0x0011},
+	{0x8817, 0x0010}, {0x1c00, 0x0011}, {0x300d, 0x0010}, {0x0001, 0x0011},
+	{0x0002, 0x0012},
+};
+
+struct ax88178_data {
+	u16	EepromData;
+	u16	MediaLink;
+	int	UseGpio0;
+	int	UseRgmii;
+	u8	PhyMode;
+	u8	LedMode;
+	u8	BuffaloOld;
+};
+
+enum watchdog_state {
+	AX_NOP = 0,
+	CHK_LINK,			/* Routine A */
+	CHK_CABLE_EXIST,		/* Called by A */
+	CHK_CABLE_EXIST_AGAIN,		/* Routine B */
+	PHY_POWER_UP,			/* Called by B */
+	PHY_POWER_UP_BH,
+	PHY_POWER_DOWN,
+	CHK_CABLE_STATUS,		/* Routine C */
+	WAIT_AUTONEG_COMPLETE,
+	AX_SET_RX_CFG,
+	AX_CHK_AUTODETACH,
+};
+
+struct ax88772b_data {
+	struct usbnet *dev;
+	struct workqueue_struct *ax_work;
+	struct work_struct check_link;
+	unsigned long time_to_chk;
+	u16 psc;
+	u8 pw_enabled;
+	u8 Event;
+	u8 checksum;
+	u8 PhySelect:1;
+	u8 OperationMode:1;
+	u16 presvd_phy_advertise;
+	u16 presvd_phy_bmcr;
+
+	u32 ext_phy_oui;
+	u8  ext_phy_model;
+};
+
+/* define for MAC or PHY mode */
+#define OPERATION_MAC_MODE			0
+#define OPERATION_PHY_MODE			1
+
+struct ax88772a_data {
+	struct usbnet *dev;
+	struct workqueue_struct *ax_work;
+	struct work_struct check_link;
+	unsigned long autoneg_start;
+#define AX88772B_WATCHDOG	(6 * HZ)
+	u8 Event;
+	u8 TickToExpire;
+	u8 DlyIndex;
+	u8 DlySel;
+	u16 EepromData;
+	u16 presvd_phy_advertise;
+	u16 presvd_phy_bmcr;
+};
+
+struct ax88772_data {
+	struct usbnet *dev;
+	struct workqueue_struct *ax_work;
+	struct work_struct check_link;
+	unsigned long autoneg_start;
+	u8 Event;
+	u8 TickToExpire;
+	u16 presvd_phy_advertise;
+	u16 presvd_phy_bmcr;
+};
+
+#define AX_RX_CHECKSUM		1
+#define AX_TX_CHECKSUM		2
+
+/* This structure cannot exceed sizeof(unsigned long [5]) AKA 20 bytes */
+struct ax8817x_data {
+	u8 multi_filter[AX_MCAST_FILTER_SIZE];
+	int (*resume) (struct usb_interface *intf);
+	int (*suspend) (struct usb_interface *intf,
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 10)
+					pm_message_t message);
+#else
+					u32 message);
+#endif
+};
+
+struct ax88172_int_data {
+	u16 res1;
+#define AX_INT_PPLS_LINK		(1 << 0)
+#define AX_INT_SPLS_LINK		(1 << 1)
+#define AX_INT_CABOFF_UNPLUG		(1 << 7)
+	u8 link;
+	u16 res2;
+	u8 status;
+	u16 res3;
+} __attribute__ ((packed));
+
+#define AX_RXHDR_L4_ERR		(1 << 8)
+#define AX_RXHDR_L3_ERR		(1 << 9)
+
+#define AX_RXHDR_L4_TYPE_UDP		1
+#define AX_RXHDR_L4_TYPE_ICMP		2
+#define AX_RXHDR_L4_TYPE_IGMP		3
+#define AX_RXHDR_L4_TYPE_TCP		4
+#define AX_RXHDR_L4_TYPE_TCMPV6	5
+#define AX_RXHDR_L4_TYPE_MASK		7
+
+#define AX_RXHDR_L3_TYPE_IP		1
+#define AX_RXHDR_L3_TYPE_IPV6		2
+
+struct ax88772b_rx_header {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+	u16	len:11,
+		res1:1,
+		crc:1,
+		mii:1,
+		runt:1,
+		mc_bc:1;
+
+	u16	len_bar:11,
+		res2:5;
+
+	u8	vlan_ind:3,
+		vlan_tag_striped:1,
+		pri:3,
+		res3:1;
+
+	u8	l4_csum_err:1,
+		l3_csum_err:1,
+		l4_type:3,
+		l3_type:2,
+		ce:1;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	u16	mc_bc:1,
+		runt:1,
+		mii:1,
+		crc:1,
+		res1:1,
+		len:11;
+
+	u16	res2:5,
+		len_bar:11;
+
+	u8	res3:1,
+		pri:3,
+		vlan_tag_striped:1,
+		vlan_ind:3;
+
+	u8	ce:1,
+		l3_type:2,
+		l4_type:3,
+		l3_csum_err:1,
+		l4_csum_err:1;
+#else
+#error	"Please fix <asm/byteorder.h>"
+#endif
+
+} __attribute__ ((packed));
+
+
+#endif /* __LINUX_USBNET_ASIX_H */
+
diff --git a/kernel/drivers/net/usb/AX88772C_eeprom/axusbnet.c b/kernel/drivers/net/usb/AX88772C_eeprom/axusbnet.c
new file mode 100644
index 0000000..9c1c475
--- /dev/null
+++ b/kernel/drivers/net/usb/AX88772C_eeprom/axusbnet.c
@@ -0,0 +1,1488 @@
+/*
+ * USB Network driver infrastructure
+ * Copyright (C) 2000-2005 by David Brownell
+ * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
+ *
+ * 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:  host-to-host cables,
+ * smart usb peripherals, and actual Ethernet adapters.
+ *
+ * These devices usually differ in terms of control protocols (if they
+ * even have one!) and sometimes they define new framing to wrap or batch
+ * Ethernet packets.  Otherwise, they talk to USB pretty much the same,
+ * so interface (un)binding, endpoint I/O queues, fault handling, and other
+ * issues can usefully be addressed by this framework.
+ */
+
+/* error path messages, extra info */
+#define	DEBUG
+/* more; success messages */
+/* #define	VERBOSE	*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ctype.h>
+#include <linux/ethtool.h>
+#include <linux/workqueue.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+/*#include <linux/usb/usbnet.h>*/
+
+#include "asix.h"
+#include "axusbnet.h"
+
+#define DRIVER_VERSION		"22-Aug-2005"
+
+static void axusbnet_unlink_rx_urbs(struct usbnet *);
+
+static void
+ax8817x_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+				    u16 size, void *data);
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * 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).
+ *
+ * REVISIT qlens should be members of 'struct usbnet'; the goal is to
+ * let the USB host controller be busy for 5msec or more before an irq
+ * is required, under load.  Jumbograms change the equation.
+ */
+#define RX_MAX_QUEUE_MEMORY (60 * 1518)
+#define	RX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \
+			(RX_MAX_QUEUE_MEMORY/(dev)->rx_urb_size) : 4)
+#define	TX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \
+			(RX_MAX_QUEUE_MEMORY/(dev)->hard_mtu) : 4)
+
+/* reawaken network queue this soon after stopping; else watchdog barks */
+/* #define TX_TIMEOUT_JIFFIES	(5 * HZ) */
+#define TX_TIMEOUT_JIFFIES	(30 * 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)
+
+/* between wakeups */
+#define UNLINK_TIMEOUT_MS	3
+
+/*-------------------------------------------------------------------------*/
+
+static const char driver_name[] = "axusbnet";
+
+/* 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");
+
+/*-------------------------------------------------------------------------*/
+
+/* handles CDC Ethernet and many other network "bulk data" interfaces */
+static
+int axusbnet_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;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+static void intr_complete(struct urb *urb, struct pt_regs *regs);
+#else
+static void intr_complete(struct urb *urb);
+#endif
+
+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, GFP_KERNEL);
+	if (buf) {
+		dev->interrupt = usb_alloc_urb(0, GFP_KERNEL);
+		if (!dev->interrupt) {
+			kfree(buf);
+			return -ENOMEM;
+		} else {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14)
+			dev->interrupt->transfer_flags |= URB_ASYNC_UNLINK;
+#endif
+			usb_fill_int_urb(dev->interrupt, dev->udev, pipe,
+				buf, maxp, intr_complete, dev, period);
+			devdbg(dev,
+			       "status ep%din, %d bytes period %d",
+			       usb_pipeendpoint(pipe), maxp, period);
+		}
+	}
+	return 0;
+}
+
+/* Passes this packet up the stack, updating its accounting.
+ * Some link protocols batch packets, so their rx_fixup paths
+ * can return clones as well as just modify the original skb.
+ */
+static
+void axusbnet_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 %zu, 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);
+}
+
+/*-------------------------------------------------------------------------
+ *
+ * Network Device Driver (peer link to "Host Device", from USB host)
+ *
+ *-------------------------------------------------------------------------*/
+
+static
+int axusbnet_change_mtu(struct net_device *net, int new_mtu)
+{
+	struct usbnet	*dev = netdev_priv(net);
+	int		ll_mtu = new_mtu + net->hard_header_len;
+	int		old_hard_mtu = dev->hard_mtu;
+	int		old_rx_urb_size = dev->rx_urb_size;
+
+	if (new_mtu <= 0)
+		return -EINVAL;
+	/* no second zero-length packet read wanted after mtu-sized packets */
+	if ((ll_mtu % dev->maxpacket) == 0)
+		return -EDOM;
+	net->mtu = new_mtu;
+
+	dev->hard_mtu = net->mtu + net->hard_header_len;
+	if (dev->rx_urb_size == old_hard_mtu) {
+		dev->rx_urb_size = dev->hard_mtu;
+		if (dev->rx_urb_size > old_rx_urb_size)
+			axusbnet_unlink_rx_urbs(dev);
+	}
+
+	return 0;
+}
+
+static struct net_device_stats *axusbnet_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 enum skb_state defer_bh(struct usbnet *dev, struct sk_buff *skb,
+                struct sk_buff_head *list, enum skb_state state)
+{
+        unsigned long           flags;
+        enum skb_state          old_state;
+        struct skb_data *entry = (struct skb_data *) skb->cb;
+
+        spin_lock_irqsave(&list->lock, flags);
+        old_state = entry->state;
+        entry->state = state;
+        __skb_unlink(skb, list);
+
+        /* defer_bh() is never called with list == &dev->done.
+         * spin_lock_nested() tells lockdep that it is OK to take
+         * dev->done.lock here with list->lock held.
+         */
+        spin_lock_nested(&dev->done.lock, SINGLE_DEPTH_NESTING);
+
+        __skb_queue_tail(&dev->done, skb);
+        if (dev->done.qlen == 1)
+                tasklet_schedule(&dev->bh);
+
+        spin_unlock(&dev->done.lock);
+        spin_unlock_irqrestore(&list->lock, flags);
+	
+        return old_state;
+}
+
+/* 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 axusbnet_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);
+}
+
+/*-------------------------------------------------------------------------*/
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+static void rx_complete(struct urb *urb, struct pt_regs *regs);
+#else
+static void rx_complete(struct urb *urb);
+#endif
+
+static void rx_submit(struct usbnet *dev, struct urb *urb, gfp_t flags)
+{
+	struct sk_buff		*skb;
+	struct skb_data		*entry;
+	int			retval = 0;
+	unsigned long		lockflags;
+	size_t			size = dev->rx_urb_size;
+	struct driver_info	*info = dev->driver_info;
+	u8			align;
+
+	/* prevent rx skb allocation when error ratio is high */
+	if (test_bit(EVENT_RX_KILL, &dev->flags)) {
+		    usb_free_urb(urb);
+		    return;
+	}
+
+#if (AX_FORCE_BUFF_ALIGN)
+	align = 0;
+#else
+	if (!(info->flags & FLAG_HW_IP_ALIGNMENT))
+		align = NET_IP_ALIGN;
+	else
+		align = 0;
+#endif
+	skb = alloc_skb(size + align, flags);
+	if (skb == NULL) {
+
+		if (netif_msg_rx_err(dev))
+			devdbg(dev, "no rx skb");
+
+		if ((dev->rx_urb_size > 2048) && dev->rx_size) {
+			dev->rx_size--;
+			dev->rx_urb_size =
+				AX88772B_BULKIN_SIZE[dev->rx_size].size;
+
+			ax8817x_write_cmd_async(dev, 0x2A,
+				AX88772B_BULKIN_SIZE[dev->rx_size].byte_cnt,
+				AX88772B_BULKIN_SIZE[dev->rx_size].threshold,
+				0, NULL);
+		}
+
+		if (!(dev->flags & EVENT_RX_MEMORY))
+			axusbnet_defer_kevent(dev, EVENT_RX_MEMORY);
+		usb_free_urb(urb);
+		return;
+	}
+
+	if (align)
+		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);
+
+	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:
+			axusbnet_defer_kevent(dev, EVENT_RX_HALT);
+			break;
+		case -ENOMEM:
+			axusbnet_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)
+		axusbnet_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);
+	}
+}
+
+/*-------------------------------------------------------------------------*/
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+static void rx_complete(struct urb *urb, struct pt_regs *regs)
+#else
+static void rx_complete(struct urb *urb)
+#endif
+{
+	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;
+	enum skb_state          state;
+
+	skb_put(skb, urb->actual_length);
+	state = rx_done;
+	entry->urb = NULL;
+
+	switch (urb_status) {
+	/* success */
+	case 0:
+		if (skb->len < dev->net->hard_header_len) {
+			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 ETIMEDOUT/EILSEQ
+	 * storm, recovering as needed.
+	 */
+	case -EPIPE:
+		dev->stats.rx_errors++;
+		axusbnet_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:
+	case -ETIME:
+	case -EILSEQ:
+		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:
+		state = rx_cleanup;
+		entry->urb = urb;
+		urb = NULL;
+		break;
+
+	/* data overrun ... flush fifo? */
+	case -EOVERFLOW:
+		dev->stats.rx_over_errors++;
+		/* FALLTHROUGH */
+
+	default:
+		state = rx_cleanup;
+		dev->stats.rx_errors++;
+		if (netif_msg_rx_err(dev))
+			devdbg(dev, "rx status %d", urb_status);
+		break;
+	}
+
+	/* stop rx if packet error rate is high */
+        if (++dev->pkt_cnt > 30) {
+                dev->pkt_cnt = 0;
+                dev->pkt_err = 0;
+        } else {
+                if (state == rx_cleanup)
+                        dev->pkt_err++;
+                if (dev->pkt_err > 20)
+                        set_bit(EVENT_RX_KILL, &dev->flags);
+        }
+
+	state = defer_bh(dev, skb, &dev->rxq, state);
+
+	if (urb) {
+		if (netif_running(dev->net) &&
+		    !test_bit(EVENT_RX_HALT, &dev->flags) &&
+			 state != unlink_start) {
+			rx_submit(dev, urb, GFP_ATOMIC);
+			return;
+		}
+		usb_free_urb(urb);
+	}
+	if (netif_msg_rx_err(dev))
+		devdbg(dev, "no read resubmitted");
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+static void intr_complete(struct urb *urb, struct pt_regs *regs)
+#else
+static void intr_complete(struct urb *urb)
+#endif
+{
+	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 = NULL;
+        int                     count = 0;
+
+        spin_lock_irqsave (&q->lock, flags);
+        while (!skb_queue_empty(q)) {
+                struct skb_data         *entry;
+                struct urb              *urb;
+                int                     retval;
+
+                skb_queue_walk(q, skb) {
+                        entry = (struct skb_data *) skb->cb;
+                        if (entry->state != unlink_start)
+                                goto found;
+                }
+                break;
+found:
+                entry->state = unlink_start;
+                urb = entry->urb;
+
+                /*
+                 * Get reference count of the URB to avoid it to be
+                 * freed during usb_unlink_urb, which may trigger
+                 * use-after-free problem inside usb_unlink_urb since
+                 * usb_unlink_urb is always racing with .complete
+                 * handler(include defer_bh).
+                 */
+                usb_get_urb(urb);
+                spin_unlock_irqrestore(&q->lock, flags);
+                // during some PM-driven resume scenarios,
+                // these (async) unlinks complete immediately
+                retval = usb_unlink_urb (urb);
+                if (retval != -EINPROGRESS && retval != 0)
+                        printk(DEBUG "unlink urb err, %d\n", retval);
+                else
+                        count++;
+                usb_put_urb(urb);
+                spin_lock_irqsave(&q->lock, flags);
+        }
+        spin_unlock_irqrestore (&q->lock, flags);
+
+        return count;
+}
+
+/* Flush all pending rx urbs */
+/* minidrivers may need to do this when the MTU changes */
+
+static
+void axusbnet_unlink_rx_urbs(struct usbnet *dev)
+{
+	if (netif_running(dev->net)) {
+		(void) unlink_urbs(dev, &dev->rxq);
+		tasklet_schedule(&dev->bh);
+	}
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* precondition: never called in_interrupt */
+
+static
+int axusbnet_stop(struct net_device *net)
+{
+	struct usbnet		*dev = netdev_priv(net);
+	struct driver_info	*info = dev->driver_info;	
+	
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)
+	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(unlink_wakeup);
+#else
+	DECLARE_WAIT_QUEUE_HEAD(unlink_wakeup);
+#endif
+	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);
+
+	/* allow minidriver to stop correctly (wireless devices to turn off
+	 * radio etc) */
+	if (info->stop) {
+		int retval;
+		retval = info->stop(dev);
+		if (retval < 0 && netif_msg_ifdown(dev))
+			devinfo(dev,
+				"stop fail (%d) usbnet usb-%s-%s, %s",
+				retval,
+				dev->udev->bus->bus_name, dev->udev->devpath,
+				info->description);
+	}
+
+	if (!(info->flags & FLAG_AVOID_UNLINK_URBS)) {
+		int temp;
+		/* 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_empty(&dev->rxq)
+				&& !skb_queue_empty(&dev->txq)
+				&& !skb_queue_empty(&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 axusbnet_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);
+		if (retval < 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);
+		if (retval < 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;
+		}
+	}
+
+	/* reset rx error state */
+    dev->pkt_cnt = 0;
+    dev->pkt_err = 0;
+	clear_bit(EVENT_RX_KILL, &dev->flags);
+
+	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",
+			(int)RX_QLEN(dev), (int)TX_QLEN(dev), dev->net->mtu,
+			framing);
+	}
+
+	/* delay posting reads until we're fully open */
+	tasklet_schedule(&dev->bh);
+	return retval;
+done:
+	return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* ethtool methods; minidrivers may need to add some more, but
+ * they'll probably want to use this base set.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 20, 0)
+static
+int axusbnet_get_settings(struct net_device *net, struct ethtool_cmd *cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+
+	if (!dev->mii.mdio_read)
+		return -EOPNOTSUPP;
+
+	return mii_ethtool_gset(&dev->mii, cmd);
+}
+
+static
+int axusbnet_set_settings(struct net_device *net, struct ethtool_cmd *cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+	int retval;
+
+	if (!dev->mii.mdio_write)
+		return -EOPNOTSUPP;
+
+	retval = mii_ethtool_sset(&dev->mii, cmd);
+
+	/* link speed/duplex might have changed */
+	if (dev->driver_info->link_reset)
+		dev->driver_info->link_reset(dev);
+
+	return retval;
+
+}
+#endif
+static
+u32 axusbnet_get_link(struct net_device *net)
+{
+	struct usbnet *dev = netdev_priv(net);
+
+	/* If a check_connect is defined, return its result */
+	if (dev->driver_info->check_connect)
+		return dev->driver_info->check_connect(dev) == 0;
+
+	/* if the device has mii operations, use those */
+	if (dev->mii.mdio_read)
+		return mii_link_ok(&dev->mii);
+
+	/* Otherwise, dtrt for drivers calling netif_carrier_{on,off} */
+	return ethtool_op_get_link(net);
+}
+
+static
+int axusbnet_nway_reset(struct net_device *net)
+{
+	struct usbnet *dev = netdev_priv(net);
+
+	if (!dev->mii.mdio_write)
+		return -EOPNOTSUPP;
+
+	return mii_nway_restart(&dev->mii);
+}
+
+static
+void axusbnet_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info)
+{
+	struct usbnet *dev = netdev_priv(net);
+
+	strncpy(info->driver, dev->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 axusbnet_get_msglevel(struct net_device *net)
+{
+	struct usbnet *dev = netdev_priv(net);
+
+	return dev->msg_enable;
+}
+
+static
+void axusbnet_set_msglevel(struct net_device *net, u32 level)
+{
+	struct usbnet *dev = netdev_priv(net);
+
+	dev->msg_enable = level;
+}
+
+/* drivers may override default ethtool_ops in their bind() routine */
+static struct ethtool_ops axusbnet_ethtool_ops = {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 20, 0)
+	.get_settings		= axusbnet_get_settings,
+	.set_settings		= axusbnet_set_settings,
+#endif
+	.get_link		= axusbnet_get_link,
+	.nway_reset		= axusbnet_nway_reset,
+	.get_drvinfo		= axusbnet_get_drvinfo,
+	.get_msglevel		= axusbnet_get_msglevel,
+	.set_msglevel		= axusbnet_set_msglevel,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* 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.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+static void kevent(void *data)
+{
+	struct usbnet *dev = (struct usbnet *)data;
+#else
+static void kevent(struct work_struct *work)
+{
+	struct usbnet		*dev =
+		container_of(work, struct usbnet, kevent);
+#endif
+	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
+				&& status != -ESHUTDOWN) {
+			if (netif_msg_tx_err(dev))
+				deverr(dev, "can't clear tx halt, status %d",
+				       status);
+		} else {
+			clear_bit(EVENT_TX_HALT, &dev->flags);
+			if (status != -ESHUTDOWN)
+				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
+				&& status != -ESHUTDOWN) {
+			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);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14)
+			urb->transfer_flags |= URB_ASYNC_UNLINK;
+#endif
+			rx_submit(dev, urb, GFP_KERNEL);
+			tasklet_schedule(&dev->bh);
+		}
+	}
+
+	if (test_bit(EVENT_LINK_RESET, &dev->flags)) {
+		struct driver_info	*info = dev->driver_info;	
+
+		clear_bit(EVENT_LINK_RESET, &dev->flags);
+		if (info->link_reset) {
+			int retval;
+			retval = info->link_reset(dev);
+			if (retval < 0) {
+				devinfo(dev,
+					"link reset failed (%d) usbnet usb-%s-%s, %s",
+					retval,
+					dev->udev->bus->bus_name,
+					dev->udev->devpath,
+					info->description);
+			}
+		}
+	}
+
+	if (dev->flags)
+		devdbg(dev, "kevent done, flags = 0x%lx", dev->flags);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+static void tx_complete(struct urb *urb, struct pt_regs *regs)
+#else
+static void tx_complete(struct urb *urb)
+#endif
+{
+	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:
+			axusbnet_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:
+		case -ETIME:
+		case -EILSEQ:
+			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;
+	(void) defer_bh(dev, skb, &dev->txq, tx_done);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static
+void axusbnet_tx_timeout(struct net_device *net)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct driver_info	*info = dev->driver_info;
+
+	if (!(info->flags & FLAG_AVOID_UNLINK_URBS)) {
+		unlink_urbs(dev, &dev->txq);
+	}	
+	tasklet_schedule(&dev->bh);
+
+	/* FIXME: device recovery -- reset? */
+}
+
+/*-------------------------------------------------------------------------*/
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
+static int
+#else
+static netdev_tx_t
+#endif
+axusbnet_start_xmit(struct sk_buff *skb, struct net_device *net)
+{
+	struct usbnet		*dev = netdev_priv(net);
+	int			length;
+	struct urb		*urb = NULL;
+	struct skb_data		*entry;
+	struct driver_info	*info = dev->driver_info;
+	unsigned long		flags;
+	int retval;
+
+	/* 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;
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		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;
+
+	usb_fill_bulk_urb(urb, dev->udev, dev->out, skb->data,
+			  skb->len, tx_complete, skb);
+
+	/* 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.
+	 */
+	if (!(info->flags & FLAG_SEND_ZLP) && (length % dev->maxpacket) == 0) {
+		urb->transfer_buffer_length++;
+		if (skb_tailroom(skb)) {
+			skb->data[skb->len] = 0;
+			__skb_put(skb, 1);
+		}
+	}
+
+	spin_lock_irqsave(&dev->txq.lock, flags);
+
+	switch ((retval = usb_submit_urb(urb, GFP_ATOMIC))) {
+	case -EPIPE:
+		netif_stop_queue(net);
+		axusbnet_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:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0)
+		net->trans_start = jiffies;
+#else		
+		netif_trans_update(net);
+#endif
+		__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:
+		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 NETDEV_TX_OK;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* tasklet (work deferred from completions, in_irq) or timer */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
+static void axusbnet_bh(unsigned long param)
+#else
+static void axusbnet_bh (struct timer_list *t)
+#endif
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
+	struct usbnet		*dev = (struct usbnet *) param;
+#else
+	struct usbnet		*dev = from_timer(dev, t, delay);
+#endif	
+	struct sk_buff		*skb;
+	struct skb_data		*entry = NULL;
+
+	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);
+		}
+	}
+
+	/* restart RX again after disabling due to high error rate */
+         clear_bit(EVENT_RX_KILL, &dev->flags);
+
+	/* 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 = NULL;
+			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) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14)
+					urb->transfer_flags |= URB_ASYNC_UNLINK;
+#endif
+					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 axusbnet_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 '%s' usb-%s-%s, %s",
+			intf->dev.driver->name,
+			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);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* precondition: never called in_interrupt */
+
+static int
+axusbnet_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;
+	const char			*name;
+
+	name = udev->dev.driver->name;
+	info = (struct driver_info *) prod->driver_info;
+	if (!info) {
+		printk(KERN_ERR "blacklisted by %s\n", 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) {
+		printk(KERN_ERR "can't kmalloc dev");
+		goto out;
+	}
+
+	dev = netdev_priv(net);
+	dev->udev = xdev;
+	dev->intf = udev;
+	dev->driver_info = info;
+	dev->driver_name = name;
+	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);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
+	dev->bh.func = axusbnet_bh;
+	dev->bh.data = (unsigned long) dev;
+#else
+	dev->bh.func = (void (*)(unsigned long))axusbnet_bh;
+	dev->bh.data = (unsigned long)&dev->delay;
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+	INIT_WORK(&dev->kevent, kevent, dev);
+#else
+	INIT_WORK(&dev->kevent, kevent);
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
+	dev->delay.function = axusbnet_bh;
+	dev->delay.data = (unsigned long) dev;
+	init_timer(&dev->delay);
+#else
+	timer_setup(&dev->delay, axusbnet_bh, 0);
+#endif
+	/* mutex_init(&dev->phy_mutex); */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
+	init_MUTEX (&dev->sem);
+#else
+	sema_init(&dev->sem,1);
+#endif
+	dev->net = net;
+
+	/* rx and tx sides can use different message sizes;
+	 * bind() should set rx_urb_size in that case.
+	 */
+	dev->hard_mtu = net->mtu + net->hard_header_len;
+
+#if 0
+	/* dma_supported() is deeply broken on almost all architectures */
+	/* possible with some EHCI controllers */
+	if (dma_supported(&udev->dev, DMA_BIT_MASK(64)))
+		net->features |= NETIF_F_HIGHDMA;
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
+	net->open		= axusbnet_open,
+	net->stop		= axusbnet_stop,
+	net->hard_start_xmit	= axusbnet_start_xmit,
+	net->tx_timeout	= axusbnet_tx_timeout,
+	net->get_stats = axusbnet_get_stats;
+#endif
+
+	net->watchdog_timeo = TX_TIMEOUT_JIFFIES;
+	net->ethtool_ops = &axusbnet_ethtool_ops;
+
+	/* allow device-specific bind/init procedures */
+	/* NOTE net->name still not usable ... */
+	status = info->bind(dev, udev);
+	if (status < 0) {
+		deverr(dev, "Binding device failed: %d", status);
+		goto out1;
+	}
+
+	/* maybe the remote can't receive an Ethernet MTU */
+	if (net->mtu > (dev->hard_mtu - net->hard_header_len))
+		net->mtu = dev->hard_mtu - net->hard_header_len;
+
+	status = init_status(dev, udev);
+	if (status < 0)
+		goto out3;
+
+	if (!dev->rx_urb_size)
+		dev->rx_urb_size = dev->hard_mtu;
+	dev->maxpacket = usb_maxpacket(dev->udev, dev->out, 1);
+
+	SET_NETDEV_DEV(net, &udev->dev);
+	status = register_netdev(net);
+	if (status) {
+		deverr(dev, "net device registration failed: %d", status);
+		goto out3;
+	}
+
+	if (netif_msg_probe(dev))
+		devinfo(dev, "register '%s' at usb-%s-%s, %s, %pM",
+			udev->dev.driver->name,
+			xdev->bus->bus_name, xdev->devpath,
+			dev->driver_info->description,
+			net->dev_addr);
+
+	/* 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;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * suspend the whole driver as soon as the first interface is suspended
+ * resume only when the last interface is resumed
+ */
+
+static int axusbnet_suspend(struct usb_interface *intf,
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 10)
+pm_message_t message)
+#else
+u32 message)
+#endif
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+
+	if (!dev->suspend_count++) {
+		/*
+		 * accelerate emptying of the rx and queues, to avoid
+		 * having everything error out.
+		 */
+		netif_device_detach(dev->net);
+		(void) unlink_urbs(dev, &dev->rxq);
+		(void) unlink_urbs(dev, &dev->txq);
+		usb_kill_urb(dev->interrupt);
+		/*
+		 * reattach so runtime management can use and
+		 * wake the device
+		 */
+		netif_device_attach(dev->net);
+	}
+	return 0;
+}
+
+static int
+axusbnet_resume(struct usb_interface *intf)
+{
+	struct usbnet	*dev = usb_get_intfdata(intf);
+	int	retval = 0;
+
+	if (!--dev->suspend_count)
+		tasklet_schedule(&dev->bh);
+
+	retval = init_status(dev, intf);
+	if (retval < 0)
+		return retval;
+
+	if (dev->interrupt) {
+		retval = usb_submit_urb(dev->interrupt, GFP_KERNEL);
+		if (retval < 0 && netif_msg_ifup(dev))
+			deverr(dev, "intr submit %d", retval);
+	}
+
+	return retval;
+}
+
diff --git a/kernel/drivers/net/usb/AX88772C_eeprom/axusbnet.h b/kernel/drivers/net/usb/AX88772C_eeprom/axusbnet.h
new file mode 100644
index 0000000..a5df307
--- /dev/null
+++ b/kernel/drivers/net/usb/AX88772C_eeprom/axusbnet.h
@@ -0,0 +1,212 @@
+/*
+ * USB Networking Link Interface
+ *
+ * Copyright (C) 2000-2005 by David Brownell <dbrownell@users.sourceforge.net>
+ * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
+ *
+ * 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
+ */
+
+#ifndef	__LINUX_USB_USBNET_H
+#define	__LINUX_USB_USBNET_H
+
+#ifndef gfp_t
+#define gfp_t int
+#endif
+
+/* interface from usbnet core to each USB networking link we handle */
+struct usbnet {
+	/* housekeeping */
+	struct usb_device	*udev;
+	struct usb_interface	*intf;
+	struct driver_info	*driver_info;
+	const char		*driver_name;
+	void			*driver_priv;
+	wait_queue_head_t	*wait;
+	/* struct mutex		phy_mutex; */
+	unsigned char		suspend_count;
+	unsigned char           pkt_cnt, pkt_err;
+
+	/* 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];
+	u32			xid;
+	u32			hard_mtu;	/* count any extra framing */
+	size_t			rx_urb_size;	/* size for rx urbs */
+	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 sk_buff_head	rxq_pause;
+	struct urb		*interrupt;
+	struct tasklet_struct	bh;
+
+	struct work_struct	kevent;
+	struct semaphore	sem;
+	unsigned long		flags;
+#		define EVENT_TX_HALT	0
+#		define EVENT_RX_HALT	1
+#		define EVENT_RX_MEMORY	2
+#		define EVENT_STS_SPLIT	3
+#		define EVENT_LINK_RESET	4
+#		define EVENT_RX_PAUSED	5
+#		define EVENT_RX_KILL    10
+
+	void			*priv;	/* point to minidriver private data */
+	unsigned char		rx_size;
+};
+
+static inline struct usb_driver *driver_of(struct usb_interface *intf)
+{
+	return to_usb_driver(intf->dev.driver);
+}
+
+/* interface from the device/framing level "minidriver" to core */
+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 */
+#define FLAG_WLAN	0x0080		/* use "wlan%d" names */
+#define FLAG_AVOID_UNLINK_URBS	0x0100	/* don't unlink urbs at usbnet_stop() */
+#define FLAG_SEND_ZLP	0x0200		/* hw requires ZLPs are sent */
+#define FLAG_HW_IP_ALIGNMENT	0x0400	/* AX88772B support hardware IP alignment */
+
+
+	/* 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 *);
+
+	/* stop device ... can sleep */
+	int	(*stop)(struct usbnet *);
+
+	/* see if peer is connected ... can sleep */
+	int	(*check_connect)(struct usbnet *);
+
+	/* for status polling */
+	void	(*status)(struct usbnet *, struct urb *);
+
+	/* link reset handling, called from defer_kevent */
+	int	(*link_reset)(struct usbnet *);
+
+	/* 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, gfp_t flags);
+
+	/* early initialization code, can sleep. This is for minidrivers
+	 * having 'subminidrivers' that need to do extra initialization
+	 * right after minidriver have initialized hardware. */
+	int	(*early_init)(struct usbnet *dev);
+
+	/* called by minidriver when receiving indication */
+	void	(*indication)(struct usbnet *dev, void *ind, int indlen);
+
+	/* for new devices, use the descriptor-reading code instead */
+	int		in;		/* rx endpoint */
+	int		out;		/* tx endpoint */
+
+	unsigned long	data;		/* Misc driver specific data */
+};
+
+/* Drivers that reuse some of the standard USB CDC infrastructure
+ * (notably, using multiple interfaces according to the CDC
+ * union descriptor) get some helper code.
+ */
+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;
+};
+
+/* CDC and RNDIS support the same host-chosen packet filters for IN transfers */
+#define	DEFAULT_FILTER	(USB_CDC_PACKET_TYPE_BROADCAST \
+			|USB_CDC_PACKET_TYPE_ALL_MULTICAST \
+			|USB_CDC_PACKET_TYPE_PROMISCUOUS \
+			|USB_CDC_PACKET_TYPE_DIRECTED)
+
+
+/* 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,
+	unlink_start
+};
+
+struct skb_data {	/* skb->cb is one of these */
+	struct urb		*urb;
+	struct usbnet		*dev;
+	enum skb_state		state;
+	size_t			length;
+};
+
+#ifndef skb_queue_walk_safe
+#define skb_queue_walk_safe(queue, skb, tmp)				\
+			for (skb = (queue)->next, tmp = skb->next;	\
+			skb != (struct sk_buff *)(queue);		\
+			skb = tmp, tmp = skb->next)
+#endif
+
+/* messaging support includes the interface name, so it must not be
+ * used before it has one ... notably, in minidriver bind() calls.
+ */
+#ifdef DEBUG
+#define devdbg(usbnet, fmt, arg...) \
+	printk("%s: " fmt "\n" , (usbnet)->net->name , ## arg)
+#else
+#define devdbg(usbnet, fmt, arg...) \
+	({ if (0) printk("%s: " fmt "\n" , (usbnet)->net->name , \
+		## arg); 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); \
+
+
+#endif /* __LINUX_USB_USBNET_H */
diff --git a/kernel/drivers/net/usb/AX88772C_eeprom/command.h b/kernel/drivers/net/usb/AX88772C_eeprom/command.h
new file mode 100644
index 0000000..ea76b41
--- /dev/null
+++ b/kernel/drivers/net/usb/AX88772C_eeprom/command.h
@@ -0,0 +1,67 @@
+/*
+ *********************************************************************************
+ *     Copyright (c) 2005 ASIX Electronic Corporation      All rights reserved.
+ *
+ *     This is unpublished proprietary source code of ASIX Electronic Corporation
+ *
+ *     The copyright notice above does not evidence any actual or intended
+ *     publication of such source code.
+ *********************************************************************************
+ */
+ 
+#ifndef __COMMAND_H__
+#define __COMMAND_H__
+
+/*
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ethtool.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+#include <linux/random.h>
+#include <linux/mii.h>
+
+#include <linux/in.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+*/
+/* NAMING CONSTANT DECLARATIONS */
+#define AX88772B_SIGNATURE	"AX88772B"
+#define AX88772B_DRV_NAME	"AX88772B"
+
+#define DEBUG_PARAM	(DEB_NONE)
+#define DEB_NONE	(0)
+#define DEB_TOOL	(1 << 0)
+#define DEB_DRIVER	(1 << 1)
+#define DPRINT_D(...)	while (1) { \
+					if (DEBUG_PARAM & DEB_DRIVER) \
+						printk(__VA_ARGS__); \
+					break; \
+				}
+
+/* ioctl Command Definition */
+#define AX_PRIVATE		SIOCDEVPRIVATE
+
+/* private Command Definition */
+#define AX_SIGNATURE			0
+#define AX_READ_EEPROM			1
+#define AX_WRITE_EEPROM			2
+
+typedef struct _AX_IOCTL_COMMAND {
+	unsigned short	ioctl_cmd;
+	unsigned char	sig[16];
+	unsigned short *buf;
+	unsigned short size;
+	unsigned char delay;
+}AX_IOCTL_COMMAND;
+
+#endif /* end of command.h */
diff --git a/kernel/drivers/net/usb/AX88772C_eeprom/ioctl.c b/kernel/drivers/net/usb/AX88772C_eeprom/ioctl.c
new file mode 100644
index 0000000..a9f0d81
--- /dev/null
+++ b/kernel/drivers/net/usb/AX88772C_eeprom/ioctl.c
@@ -0,0 +1,481 @@
+/*==========================================================================
+ * Module Name : console_debug.c
+ * Purpose     : 
+ * Author      : 
+ * Date        : 
+ * Notes       :
+ * $Log$
+ *==========================================================================
+ */
+ 
+/* INCLUDE FILE DECLARATIONS */
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <linux/sockios.h>
+#include <ctype.h>
+#include <stdlib.h>
+#if NET_INTERFACE == INTERFACE_SCAN
+#include <ifaddrs.h>
+#endif
+#include "ioctl.h"
+
+/* STATIC VARIABLE DECLARATIONS */
+#define AX88772C_IOCTL_VERSION		"AX88772C/AX88772B/AX88772A/AX88760/AX88772/AX88178 Linux SROM IOCTL Tool version 1.6.0"
+
+/* LOCAL SUBPROGRAM DECLARATIONS */
+static unsigned long STR_TO_U32(const char *cp,char **endp,unsigned int base);
+
+
+/* LOCAL SUBPROGRAM BODIES */
+static void debug_func(char *func_name, unsigned short *buf, unsigned long wLen)
+{
+#if (DEBUG_PARAM & DEB_TOOL)
+	int i;
+	char str_buf[50];
+	printf("%s :\n", func_name);
+	printf("---------------------------------------\n");
+	for (i = 0; i < wLen / 8; i++) {
+		int j = 8 * i;
+		snprintf(str_buf, 50,
+				 "%04x %04x %04x %04x %04x %04x "
+				 "%04x %04x\n", 
+				 *(buf + j + 0), *(buf + j + 1),
+				 *(buf + j + 2), *(buf + j + 3),
+				 *(buf + j + 4), *(buf + j + 5),
+				 *(buf + j + 6), *(buf + j + 7));
+		printf("%s", str_buf);
+	}
+	printf("------------------------------------%3ld\n", wLen);
+#endif
+}
+
+
+static void show_usage(void)
+{
+	int i;
+	printf ("\n%s\n",AX88772C_IOCTL_VERSION);
+	printf ("Usage:\n");
+	for (i = 0; command_list[i].cmd != NULL; i++)
+		printf ("%s\n", command_list[i].help_ins);
+}
+
+static unsigned long STR_TO_U32(const char *cp,char **endp,unsigned int base)
+{
+	unsigned long result = 0,value;
+
+	if (*cp == '0') {
+		cp++;
+		if ((*cp == 'x') && isxdigit(cp[1])) {
+			base = 16;
+			cp++;
+		}
+		if (!base) {
+			base = 8;
+		}
+	}
+	if (!base) {
+		base = 10;
+	}
+	while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
+	    ? toupper(*cp) : *cp)-'A'+10) < base) {
+		result = result*base + value;
+		cp++;
+	}
+	if (endp)
+		*endp = (char *)cp;
+
+	return result;
+}
+
+void help_func (struct ax_command_info *info)
+{
+	int i;
+
+	if (info->argv[2] == NULL) {
+		for(i=0; command_list[i].cmd != NULL; i++) {
+			printf ("%s%s\n", command_list[i].help_ins, command_list[i].help_desc);
+		}
+	}
+
+	for (i = 0; command_list[i].cmd != NULL; i++)
+	{
+		if (strncmp(info->argv[1], command_list[i].cmd, strlen(command_list[i].cmd)) == 0 ) {
+			printf ("%s%s\n", command_list[i].help_ins, command_list[i].help_desc);
+			return;
+		}
+	}
+
+}
+
+int compare_file(struct ax_command_info *info)
+{
+	struct ifreq *ifr = (struct ifreq *)info->ifr;
+	unsigned short *rout_buf;
+	unsigned short *ori_buf;
+	AX_IOCTL_COMMAND *ioctl_cmd = (AX_IOCTL_COMMAND *)(ifr->ifr_data);
+	int i;
+
+	rout_buf = malloc(sizeof(unsigned short) * ioctl_cmd->size);
+
+	ori_buf = ioctl_cmd->buf;	
+
+	ioctl_cmd->ioctl_cmd = AX_READ_EEPROM;
+	ioctl_cmd->buf = rout_buf;
+
+	if (ioctl(info->inet_sock, AX_PRIVATE, ifr) < 0) {
+		perror("ioctl");
+		return -1;
+	}
+
+	
+
+	for (i = 0; i < ioctl_cmd->size; i++) {
+		if (*(ioctl_cmd->buf + i) != *(ori_buf + i)) {
+			debug_func("compare_file reeprom", rout_buf, ioctl_cmd->size);	
+			ioctl_cmd->buf = ori_buf;
+			free(rout_buf);				
+			return -2;
+		}
+	}
+
+	ioctl_cmd->buf = ori_buf;
+	free(rout_buf);
+	return 0;
+}
+
+void readeeprom_func(struct ax_command_info *info)
+{
+	struct ifreq *ifr = (struct ifreq *)info->ifr;
+	AX_IOCTL_COMMAND ioctl_cmd;
+	unsigned short *buf;
+	unsigned short wLen;
+	char str_buf[50];
+	FILE *pFile;
+	int i;	
+
+	if (info->argc < 4) {
+		for(i=0; command_list[i].cmd != NULL; i++) {
+			if (strncmp(info->argv[1], command_list[i].cmd, 
+					strlen(command_list[i].cmd)) == 0 ) {
+				printf ("%s%s\n", command_list[i].help_ins, 
+						command_list[i].help_desc);
+				return;
+			}
+		}
+	}	
+
+	wLen = STR_TO_U32(info->argv[3], NULL, 0) / 2;
+
+	pFile = fopen(info->argv[2],"w");
+	buf = (unsigned short *)malloc((sizeof(unsigned short) * wLen));
+
+	ioctl_cmd.ioctl_cmd = info->ioctl_cmd;
+	ioctl_cmd.size = wLen;
+	ioctl_cmd.buf = buf;
+	ioctl_cmd.delay = 0;
+
+	ifr->ifr_data = (caddr_t)&ioctl_cmd;
+	if (ioctl(info->inet_sock, AX_PRIVATE, ifr) < 0) {
+		perror("ioctl");
+		free(buf);
+		fclose(pFile);
+		return;
+	}
+	for (i = 0; i < wLen / 8; i++) {
+		int j = 8 * i;
+		snprintf(str_buf, 50,
+				 "%04x %04x %04x %04x %04x %04x "
+				 "%04x %04x\n", 
+				 *(buf + j + 0), *(buf + j + 1),
+				 *(buf + j + 2), *(buf + j + 3),
+				 *(buf + j + 4), *(buf + j + 5),
+				 *(buf + j + 6), *(buf + j + 7));
+		fputs(str_buf, pFile);
+	}
+	printf("Read completely\n\n");
+	free(buf);
+	fclose(pFile);
+	return;
+}
+
+void writeeeprom_func(struct ax_command_info *info)
+{
+	struct ifreq *ifr = (struct ifreq *)info->ifr;
+	AX_IOCTL_COMMAND ioctl_cmd;
+	int i;
+	unsigned short *buf;
+	unsigned short wLen;
+	char c[2] = {'\0'};
+	FILE *pFile;
+	unsigned char retried = 0;
+
+	if (info->argc < 4) {
+		for(i=0; command_list[i].cmd != NULL; i++) {
+			if (strncmp(info->argv[1], command_list[i].cmd,
+					strlen(command_list[i].cmd)) == 0) {
+				printf ("%s%s\n", command_list[i].help_ins,
+						command_list[i].help_desc);
+				return;
+			}
+		}
+	}
+
+	pFile = fopen(info->argv[2], "r");
+
+	wLen = STR_TO_U32(info->argv[3], NULL, 0) / 2;
+
+	buf = (unsigned short *)malloc(sizeof(unsigned short) * wLen);
+
+	for (i = 0; i < wLen / 8; i++) {
+		int j = 8 * i;
+		fscanf(pFile, "%04X %04X %04X %04X %04X %04X %04X %04X%c", 
+				(unsigned int *)&buf[j + 0], (unsigned int *)&buf[j + 1],
+				(unsigned int *)&buf[j + 2], (unsigned int *)&buf[j + 3],
+				(unsigned int *)&buf[j + 4], (unsigned int *)&buf[j + 5],
+				(unsigned int *)&buf[j + 6], (unsigned int *)&buf[j + 7], c);
+	}
+
+	ioctl_cmd.ioctl_cmd = info->ioctl_cmd;
+	ioctl_cmd.size = wLen;
+	ioctl_cmd.buf = buf;
+	ioctl_cmd.delay = 5;
+io:	
+	ifr->ifr_data = (caddr_t)&ioctl_cmd;
+
+debug_func("writeeeprom_func", buf, wLen);
+
+ 	if (ioctl(info->inet_sock, AX_PRIVATE, ifr) < 0) {
+		free(buf);
+		fclose(pFile);
+		perror("ioctl");
+		return;
+	}
+	else {
+		if (compare_file(info) && retried < 3) { 
+			ioctl_cmd.delay += 5;
+			ioctl_cmd.ioctl_cmd = info->ioctl_cmd;
+			retried++;
+			goto io;
+		}
+		if (retried == 3) {
+			printf("Failure to write\n\n");
+			free(buf);
+			fclose(pFile);
+			return;
+		}
+	}
+
+	printf("Write completely\n\n");
+	free(buf);
+	fclose(pFile);	
+	return;
+}
+
+void chgmac_func(struct ax_command_info *info)
+{
+	struct ifreq *ifr = (struct ifreq *)info->ifr;
+	AX_IOCTL_COMMAND ioctl_cmd;
+	int i;
+	unsigned short *buf;
+	unsigned int tmp;
+	unsigned short wLen;
+	unsigned char retried = 0;
+	char * pch;
+	unsigned int MAC[6] = {0};
+	int ret = 0;
+
+	if (info->argc < 4) {
+		for(i=0; command_list[i].cmd != NULL; i++) {
+			if (strncmp(info->argv[1], command_list[i].cmd,
+					strlen(command_list[i].cmd)) == 0) {
+				printf ("%s%s\n", command_list[i].help_ins,
+						command_list[i].help_desc);
+				return;
+			}
+		}
+	}
+
+	wLen = STR_TO_U32(info->argv[3], NULL, 0) / 2;
+
+	buf = (unsigned short *)malloc(sizeof(unsigned short) * wLen);
+
+	ioctl_cmd.ioctl_cmd = AX_READ_EEPROM;
+	ioctl_cmd.size = wLen;
+	ioctl_cmd.buf = buf;
+	ioctl_cmd.delay = 0;
+
+	ifr->ifr_data = (caddr_t)&ioctl_cmd;
+
+	if (ioctl(info->inet_sock, AX_PRIVATE, ifr) < 0) {
+		perror("ioctl");
+		free(buf);
+		return;
+	}
+
+	ret = sscanf(info->argv[2], "%02X:%02X:%02X:%02X:%02X:%02X",(unsigned int*)&MAC[0],
+								(unsigned int*)&MAC[1],
+								(unsigned int*)&MAC[2],
+								(unsigned int*)&MAC[3],
+								(unsigned int*)&MAC[4],
+								(unsigned int*)&MAC[5]);
+	if (6 != ret) {
+		printf("Invalid MAC address\n\n");
+		return;
+	}
+	
+	if (*buf == 0x1500) {
+		printf("No eeprom exists!\n");
+		printf("Or you must burn the default value first!\n\n");
+		return;
+	}
+
+	*(((char*)buf) + 8) = (unsigned char)MAC[1];
+	*(((char*)buf) + 9) = (unsigned char)MAC[0];
+	*(((char*)buf) + 10) = (unsigned char)MAC[3];
+	*(((char*)buf) + 11) = (unsigned char)MAC[2];
+	*(((char*)buf) + 12) = (unsigned char)MAC[5];
+	*(((char*)buf) + 13) = (unsigned char)MAC[4]; 
+
+	ioctl_cmd.ioctl_cmd = info->ioctl_cmd;
+	ioctl_cmd.size = wLen;
+	ioctl_cmd.buf = buf;
+	ioctl_cmd.delay = 5;
+
+io:	
+	ifr->ifr_data = (caddr_t)&ioctl_cmd;
+
+debug_func("chgmac_func", buf, wLen);
+
+  	if (ioctl(info->inet_sock, AX_PRIVATE, ifr) < 0) {
+		perror("ioctl");
+		free(buf);
+		return;
+	}
+
+	else {
+		if (compare_file(info) && retried < 3) { 
+			ioctl_cmd.delay += 5;
+			ioctl_cmd.ioctl_cmd = info->ioctl_cmd;
+			retried++;
+			goto io;
+		}
+		if (retried == 3) {
+			printf("Failure to write\n\n");
+			free(buf);
+			return;
+		}
+	}
+
+	printf("Write completely\n\n");
+	free(buf);
+	return;
+}
+
+/* EXPORTED SUBPROGRAM BODIES */
+int main(int argc, char **argv)
+{
+#if NET_INTERFACE == INTERFACE_SCAN
+	struct ifaddrs *addrs, *tmp;
+	unsigned char	dev_exist;
+#endif	
+	struct ifreq ifr;
+	struct ax_command_info info;
+	AX_IOCTL_COMMAND ioctl_cmd;
+	int inet_sock;
+	unsigned char i;	
+
+	if (argc < 2) {
+		show_usage();
+		return 0;
+	}
+
+	inet_sock = socket(AF_INET, SOCK_DGRAM, 0);
+
+#if NET_INTERFACE == INTERFACE_SCAN
+	/* Get Device */
+	getifaddrs(&addrs);
+	tmp = addrs;
+	dev_exist = 0;
+
+	while (tmp) {
+		memset (&ioctl_cmd, 0, sizeof (AX_IOCTL_COMMAND));
+		ioctl_cmd.ioctl_cmd = AX_SIGNATURE;
+		// get network interface name
+		sprintf (ifr.ifr_name, "%s", tmp->ifa_name);
+
+		ifr.ifr_data = (caddr_t)&ioctl_cmd;
+		tmp = tmp->ifa_next;
+
+
+		if (ioctl (inet_sock, AX_PRIVATE, &ifr) < 0) {			
+			continue;
+		}
+		
+		if (strncmp (ioctl_cmd.sig, AX88772B_DRV_NAME, strlen(AX88772B_DRV_NAME)) == 0 ) {
+			dev_exist = 1;
+			break;
+		}			
+	}
+
+	freeifaddrs(addrs);
+
+	if (dev_exist == 0) {
+		printf ("\n%s\n",AX88772C_IOCTL_VERSION);
+		printf("No %s found\n\n", AX88772B_SIGNATURE);
+		return 0;
+	}
+#else
+	for (i = 0; i < 255; i++) {
+
+		memset (&ioctl_cmd, 0, sizeof (AX_IOCTL_COMMAND));
+		ioctl_cmd.ioctl_cmd = AX_SIGNATURE;
+
+		sprintf (ifr.ifr_name, "eth%d", i);
+		ifr.ifr_data = (caddr_t)&ioctl_cmd;
+		
+		if (ioctl (inet_sock, AX_PRIVATE, &ifr) < 0) {
+			continue;
+		}
+
+		if (strncmp (ioctl_cmd.sig, AX88772B_DRV_NAME, strlen(AX88772B_DRV_NAME)) == 0 ) {
+			break;
+		}
+
+	}
+
+	if (i == 255) {
+		printf ("\n%s\n",AX88772C_IOCTL_VERSION);
+		printf ("No %s found\n\n", AX88772B_SIGNATURE);
+		return 0;
+	}
+#endif
+	for(i=0; command_list[i].cmd != NULL; i++)
+	{
+		if (strncmp(argv[1], command_list[i].cmd, strlen(command_list[i].cmd)) == 0 ) {
+			printf ("\n%s\n",AX88772C_IOCTL_VERSION);
+			info.help_ins = command_list[i].help_ins;
+			info.help_desc = command_list[i].help_desc;
+			info.ifr = &ifr;
+			info.argc = argc;
+			info.argv = argv;
+			info.inet_sock = inet_sock;
+			info.ioctl_cmd = command_list[i].ioctl_cmd;
+			(command_list[i].OptFunc)(&info);
+			return 0;
+		}
+	}
+
+	printf ("Wrong command\n\n");
+
+	return 0;
+}
+
diff --git a/kernel/drivers/net/usb/AX88772C_eeprom/ioctl.h b/kernel/drivers/net/usb/AX88772C_eeprom/ioctl.h
new file mode 100644
index 0000000..23b90d7
--- /dev/null
+++ b/kernel/drivers/net/usb/AX88772C_eeprom/ioctl.h
@@ -0,0 +1,75 @@
+#ifndef ioctl_h
+#define ioctl_h
+
+/* INCLUDE FILE DECLARATIONS */
+#include "command.h"
+
+/* CHANGE NETWORK INTERFACE WAY */
+// DEFAULT_SCAN   : scan "eth0" - "eth255"
+// INTERFACE_SCAN : scan all available network interfaces
+#define NET_INTERFACE	INTERFACE_SCAN
+#define	DEFAULT_SCAN	0x00
+#define	INTERFACE_SCAN	0x01
+
+/* NAMING CONSTANT DECLARATIONS */
+struct ax_command_info { 
+	int inet_sock;
+	struct ifreq *ifr;
+	int argc;
+	char **argv;
+	unsigned short ioctl_cmd;
+	const char *help_ins;
+	const char *help_desc;
+};
+
+const char help_str1[] =
+"./ioctl help [command]\n"
+"    -- command description\n";
+const char help_str2[] =
+"        [command] - Display usage of specified command\n";
+
+const char readeeprom_str1[] =
+"./ioctl reeprom [file] [size]\n"
+"    -- AX88772B EEPROM read tool\n";
+const char readeeprom_str2[] =
+"        [file]    - Output file\n"
+"        [size]    - EEPROM SIZE in bytes\n";
+
+const char writeeeprom_str1[] =
+"./ioctl weeprom [file] [size]\n"
+"    -- AX88772B EEPROM write tool\n";
+const char writeeeprom_str2[] =
+"        [file]    - Input file\n"
+"        [size]    - EEPROM SIZE in bytes\n";
+
+const char chgmac_str1[] =
+"./ioctl chgmac [mac_addr] [size]\n"
+"    -- AX88772B EEPROM write tool (specify MAC address)\n";
+const char chgmac_str2[] =
+"        [mac_addr]- MAC address (xx:xx:xx:xx:xx:xx)\n"
+"        [size]    - EEPROM SIZE in bytes\n";
+
+/* EXPORTED SUBPROGRAM SPECIFICATIONS */
+void help_func (struct ax_command_info *info);
+void readeeprom_func(struct ax_command_info *info);
+void writeeeprom_func(struct ax_command_info *info);
+void chgmac_func(struct ax_command_info *info);
+/* TYPE DECLARATIONS */
+
+typedef void (*MENU_FUNC)(struct ax_command_info *info);
+
+struct {
+	char *cmd;
+	unsigned short ioctl_cmd;
+	MENU_FUNC OptFunc;
+	const char *help_ins;
+	const char *help_desc;
+} command_list[] = {
+	{"help",	AX_SIGNATURE,		help_func,		help_str1,		help_str2},
+	{"reeprom",	AX_READ_EEPROM,		readeeprom_func,	readeeprom_str1,	readeeprom_str2},
+	{"weeprom", 	AX_WRITE_EEPROM, 	writeeeprom_func,	writeeeprom_str1,	writeeeprom_str2},
+	{"chgmac", 	AX_WRITE_EEPROM, 	chgmac_func,		chgmac_str1,		chgmac_str2},
+	{NULL},
+};
+
+#endif /* End of console_debug_h */
diff --git a/kernel/drivers/net/usb/AX88772C_eeprom/ioctl_readme b/kernel/drivers/net/usb/AX88772C_eeprom/ioctl_readme
new file mode 100644
index 0000000..f900731
--- /dev/null
+++ b/kernel/drivers/net/usb/AX88772C_eeprom/ioctl_readme
@@ -0,0 +1,61 @@
+AX88772C/AX88772B/AX88772A/AX88760/AX88772/AX88178 Linux SROM tool.
+
+This tool can be used to read/write the EEPROM of AX88772C/AX88772B/AX88772A/AX88760/AX88772/AX88178. 
+================
+Getting Start
+================
+
+1. Extract the compressed driver source file to your template directory by the
+   following command:
+
+	[root@localhost template]# tar -xf AX88772B_772A_760_772_178_Linux_EEPROM_Programming_Tool_Source_v1.x.0.tar.bz2
+
+2. Now, the driver source files should be extracted under the current directory.
+   Executing the following command to compile the driver:
+ 
+	[root@localhost template]# make
+			
+3. If the compilation is well, the ioctl will be created under the current
+   directory.
+
+Note:	The default way to find the interface is to scan the ASIX device using the ethx
+	(x: 0~255).It is defined in the file, ioctl.h.
+
+	(As follows)
+	...
+	// DEFAULT_SCAN   : scan "eth0" - "eth255"
+	// INTERFACE_SCAN : scan all available network interfaces
+	#define NET_INTERFACE	DEFAULT_SCAN
+	#define	DEFAULT_SCAN	0x00
+	#define	INTERFACE_SCAN	0x01
+	...
+	
+	Adjust the contents of #define NET_INTERFACE to select the method you want.
+ 
+================
+Usage
+================
+
+1. If you want to read out values of the EEPROM to a file, go to the driver directory and
+   execute the following command:
+
+	[root@localhost driver_dir]# ./ioctl reeprom file_name eeprom_size
+
+2. If you want to write values of a file to the EEPROM, go to the driver directory and 
+   execute the following command:
+
+	[root@localhost driver_dir]# ./ioctl weeprom file_name eeprom_size
+
+3. If you want to change the MAC address of a dongle, go to the driver directory and 
+   execute the following command:
+
+	[root@localhost driver_dir]# ./ioctl chgmac mac_addr eeprom_size
+
+4. If you need more information about the instructions, go to the driver directory and
+   execute the following commands:
+
+	[root@localhost driver_dir]# ./ioctl reeprom help
+
+or
+
+	[root@localhost driver_dir]# ./ioctl weeprom help
diff --git a/kernel/drivers/net/usb/AX88772C_eeprom/readme b/kernel/drivers/net/usb/AX88772C_eeprom/readme
new file mode 100644
index 0000000..3bb941d
--- /dev/null
+++ b/kernel/drivers/net/usb/AX88772C_eeprom/readme
@@ -0,0 +1,92 @@
+============================================================================
+ASIX AX88178 USB 2.0 Gigabit Ethernet Network Adapter
+ASIX AX88772 USB 2.0 Fast Ethernet Network Adapter
+ASIX AX88772A USB 2.0 Fast Ethernet Network Adapter
+ASIX AX88760 USB 2.0 MTT HUB and USB 2.0 to Fast Ethernet Combo Controller
+ASIX AX88772B USB 2.0 Fast Ethernet Network Adapter
+ASIX AX88772C USB 2.0 Fast Ethernet Network Adapter
+Driver Compilation & Configuration on the Linux
+============================================================================
+
+This driver has been verified on Linux kernel 2.6.14 and later.
+
+================
+Prerequisites
+================
+
+Prepare to build the driver, you need the Linux kernel sources installed on the
+build machine, and make sure that the version of the running kernel must match
+the installed kernel sources. If you don't have the kernel sources, you can get
+it from www.kernel.org or contact to your Linux distributor. If you don't know
+how to do, please refer to KERNEL-HOWTO.
+
+Note: Please make sure the kernel is built with one of the "Support for
+       Host-side, EHCI, OHCI, or UHCI" option support.
+
+
+===========================
+Conditional Compilation Flag
+===========================
+[AX_FORCE_BUFF_ALIGN]
+Description:
+       There are alignment issues of USB buffer in some USB host controllers.
+       Turn on this flag if the implementation of your USB host controller
+       cannot handle non-double word aligned buffer.
+       When turn on this flag, driver will fixup egress packet aligned on double
+       word boundary before deliver to USB host controller.
+Setting:
+	1 -> Enable TX buffers forced on double word alignment.
+	0 -> Disable TX buffers forced on double word alignment.
+Default:
+	0
+
+
+================
+Getting Start
+================
+
+1. Extract the compressed driver source file to your template directory by the
+   following command:
+
+	[root@localhost template]# tar -xf DRIVER_SOURCE_PACKAGE.tar.bz2
+
+2. Now, the driver source files should be extracted under the current directory.
+   Executing the following command to compile the driver:
+ 
+	[root@localhost template]# make
+			
+3. If the compilation is well, the asix.ko will be created under the current
+   directory.
+ 
+4. If you want to use modprobe command to mount the driver, executing the
+   following command to install the driver into your Linux:
+
+	[root@localhost template]# make install
+
+
+================
+Usage
+================
+
+1. If you want to load the driver manually, go to the driver directory and
+   execute the following commands:
+
+	[root@localhost template]# insmod asix.ko
+
+2. If you had installed the driver during driver compilation, then you can use
+   the following command to load the driver automatically.
+
+	[root@localhost anywhere]# modprobe asix
+
+If you want to unload the driver, just executing the following command:
+
+	[root@localhost anywhere]# rmmod asix
+
+================
+Special define
+================
+There is a RX_SKB_COPY preprocessor define in asix.h can solve rx_throttle problem
+in some version of 3.4 Linux kernel. Removing the comment before the define can enable
+this feature.
+
+
diff --git a/kernel/drivers/net/usb/Makefile b/kernel/drivers/net/usb/Makefile
index 99fd12b..9850013 100644
--- a/kernel/drivers/net/usb/Makefile
+++ b/kernel/drivers/net/usb/Makefile
@@ -41,3 +41,5 @@
 obj-$(CONFIG_USB_NET_CDC_MBIM)	+= cdc_mbim.o
 obj-$(CONFIG_USB_NET_CH9200)	+= ch9200.o
 obj-$(CONFIG_USB_NET_AQC111)	+= aqc111.o
+#obj-y += AX88772C_eeprom/
+obj-y += ax88772C/
diff --git a/kernel/drivers/net/usb/ax88772C/Makefile b/kernel/drivers/net/usb/ax88772C/Makefile
new file mode 100644
index 0000000..7505b41
--- /dev/null
+++ b/kernel/drivers/net/usb/ax88772C/Makefile
@@ -0,0 +1 @@
+obj-m += asix.o
diff --git a/kernel/drivers/net/usb/ax88772C/asix.c b/kernel/drivers/net/usb/ax88772C/asix.c
new file mode 100644
index 0000000..e96e625
--- /dev/null
+++ b/kernel/drivers/net/usb/ax88772C/asix.c
@@ -0,0 +1,4369 @@
+/*
+ * ASIX AX8817X based USB 2.0 Ethernet Devices
+ * 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
+ */
+
+/* debug messages, extra info */
+/* #define	DEBUG */
+
+#include <linux/version.h>
+/* #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/ethtool.h>
+#include <linux/workqueue.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/crc32.h>
+
+#include "axusbnet.c"
+#include "asix.h"
+
+static char version[] =
+KERN_INFO "ASIX USB Ethernet Adapter:v" DRIVER_VERSION
+	"    http://www.asix.com.tw\n";
+
+/* configuration of maximum bulk in size */
+static int bsize = AX88772B_MAX_BULKIN_16K;
+module_param(bsize, int, 0);
+MODULE_PARM_DESC(bsize, "Maximum transfer size per bulk");
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+static void ax88772b_link_reset(void *data);
+static void ax88772a_link_reset(void *data);
+static void ax88772_link_reset(void *data);
+#else
+static void ax88772b_link_reset(struct work_struct *work);
+static void ax88772a_link_reset(struct work_struct *work);
+static void ax88772_link_reset(struct work_struct *work);
+#endif
+static int ax88772a_phy_powerup(struct usbnet *dev);
+static void ax8817x_mdio_write_le(struct net_device *netdev, int phy_id,
+				  int loc, int val);
+static int ax8817x_mdio_read_le(struct net_device *netdev, int phy_id, int loc);
+static int ax88772b_set_csums(struct usbnet *dev);
+static int ax88772b_external_phyinit(struct usbnet *dev);
+
+/* ASIX AX8817X based USB 2.0 Ethernet Devices */
+
+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,
+		USB_CTRL_GET_TIMEOUT);
+}
+
+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,
+		USB_CTRL_SET_TIMEOUT);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+static void ax8817x_async_cmd_callback(struct urb *urb, struct pt_regs *regs)
+#else
+static void ax8817x_async_cmd_callback(struct urb *urb)
+#endif
+{
+	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 int ax8817x_set_mac_addr(struct net_device *net, void *p)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct sockaddr *addr = p;
+	int ret;
+
+	memcpy(net->dev_addr, addr->sa_data, ETH_ALEN);
+
+	/* Set the MAC address */
+	ret = ax8817x_write_cmd(dev, AX88772_CMD_WRITE_NODE_ID,
+				 0, 0, ETH_ALEN, net->dev_addr);
+	if (ret < 0)
+		return ret;
+	
+	return 0;
+}
+
+static void ax88178_status(struct usbnet *dev, struct urb *urb)
+{
+	struct ax88172_int_data *event;
+	struct ax88178_data *ax178dataptr = (struct ax88178_data *)dev->priv;
+	int link;
+
+	if (urb->actual_length < 8)
+		return;
+
+	if (ax178dataptr->EepromData == PHY_MODE_MAC_TO_MAC_GMII)
+		return;
+
+	event = urb->transfer_buffer;
+	link = event->link & 0x01;
+	if (netif_carrier_ok(dev->net) != link) {
+		if (link) {
+			netif_carrier_on(dev->net);
+			axusbnet_defer_kevent(dev, EVENT_LINK_RESET);
+		} else
+			netif_carrier_off(dev->net);
+		devwarn(dev, "ax88178 - Link status is: %d", link);
+	}
+}
+
+static void ax8817x_status(struct usbnet *dev, struct urb *urb)
+{
+	struct ax88172_int_data *event;
+	int link;
+
+	if (urb->actual_length < 8)
+		return;
+
+	event = urb->transfer_buffer;
+	link = event->link & 0x01;
+	if (netif_carrier_ok(dev->net) != link) {
+		if (link) {
+			netif_carrier_on(dev->net);
+			axusbnet_defer_kevent(dev, EVENT_LINK_RESET);
+		} else
+			netif_carrier_off(dev->net);
+		devwarn(dev, "ax8817x - Link status is: %d", link);
+	}
+}
+
+static void ax88772_status(struct usbnet *dev, struct urb *urb)
+{
+	struct ax88172_int_data *event;
+	struct ax88772_data *ax772_data = (struct ax88772_data *)dev->priv;
+	int link;
+
+	if (urb->actual_length < 8)
+		return;
+
+	event = urb->transfer_buffer;
+	link = event->link & 0x01;
+
+	if (netif_carrier_ok(dev->net) != link) {
+		if (link) {
+			netif_carrier_on(dev->net);
+			ax772_data->Event = AX_SET_RX_CFG;
+		} else {
+			netif_carrier_off(dev->net);
+			if (ax772_data->Event == AX_NOP) {
+				ax772_data->Event = PHY_POWER_DOWN;
+				ax772_data->TickToExpire = 25;
+			}
+		}
+
+		devwarn(dev, "ax88772 - Link status is: %d", link);
+	}
+
+	if (ax772_data->Event)
+		queue_work(ax772_data->ax_work, &ax772_data->check_link);
+}
+
+static void ax88772a_status(struct usbnet *dev, struct urb *urb)
+{
+	struct ax88172_int_data *event;
+	struct ax88772a_data *ax772a_data = (struct ax88772a_data *)dev->priv;
+	int link;
+	int powsave = (ax772a_data->EepromData >> 14);
+
+	if (urb->actual_length < 8)
+		return;
+
+	event = urb->transfer_buffer;
+	link = event->link & 0x01;
+
+	if (netif_carrier_ok(dev->net) != link) {
+
+		if (link) {
+			netif_carrier_on(dev->net);
+			ax772a_data->Event = AX_SET_RX_CFG;
+		} else if ((powsave == 0x3) || (powsave == 0x1)) {
+			netif_carrier_off(dev->net);
+			if (ax772a_data->Event == AX_NOP) {
+				ax772a_data->Event = CHK_CABLE_EXIST;
+				ax772a_data->TickToExpire = 14;
+			}
+		} else {
+			netif_carrier_off(dev->net);
+			ax772a_data->Event = AX_NOP;
+		}
+
+		devwarn(dev, "ax88772a - Link status is: %d", link);
+	}
+
+	if (ax772a_data->Event)
+		queue_work(ax772a_data->ax_work, &ax772a_data->check_link);
+}
+
+static int ax88772b_stop(struct usbnet *dev)
+{
+	u16 *medium;
+
+	medium = kmalloc(2, GFP_ATOMIC);
+	if (medium) {
+		ax8817x_read_cmd(dev, AX_CMD_READ_MEDIUM_MODE, 0, 0, 2, medium);
+		ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE,
+				  (*medium & ~AX88772_MEDIUM_RX_ENABLE),
+				  0, 0, NULL);
+
+		kfree(medium);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int ax88772b_reset(struct usbnet *dev)
+{
+	int ret;
+
+	/* Set the MAC address */
+	ret = ax8817x_write_cmd(dev, AX88772_CMD_WRITE_NODE_ID,
+				0, 0, ETH_ALEN, dev->net->dev_addr);
+	if (ret < 0)
+		deverr(dev, "set MAC address failed: %d", ret);
+
+	/* stop MAC operation */
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, AX_RX_CTL_STOP,
+				0, 0, NULL);
+	if (ret < 0)
+		deverr(dev, "Reset RX_CTL failed: %d", ret);
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE,
+				AX88772_MEDIUM_DEFAULT, 0, 0,
+				NULL);
+	if (ret < 0)
+		deverr(dev, "Write medium mode register: %d", ret);
+
+	return ret;
+}
+
+static void ax88772b_status(struct usbnet *dev, struct urb *urb)
+{
+	struct ax88772b_data *ax772b_data = (struct ax88772b_data *)dev->priv;
+	struct ax88172_int_data *event;
+	int link;
+
+	if (urb->actual_length < 8)
+		return;
+
+	if (ax772b_data->OperationMode == OPERATION_PHY_MODE)
+		return;
+
+	event = urb->transfer_buffer;
+	if (ax772b_data->PhySelect == 0 &&
+	    ax772b_data->OperationMode == OPERATION_MAC_MODE)
+		link = (event->link & AX_INT_SPLS_LINK) >> 1;
+	else
+		link = event->link & AX_INT_PPLS_LINK;
+
+	if (netif_carrier_ok(dev->net) != link) {
+		if (link) {
+			netif_carrier_on(dev->net);
+			ax772b_data->Event = AX_SET_RX_CFG;
+		} else {
+			netif_carrier_off(dev->net);
+			ax772b_data->time_to_chk = jiffies;
+		}
+		devwarn(dev, "ax88772b - Link status is: %d", link);
+	}
+
+	if (!link) {
+
+		int no_cable = (event->link & AX_INT_CABOFF_UNPLUG) ? 1 : 0;
+
+		if (no_cable) {
+			if ((ax772b_data->psc &
+			    (AX_SWRESET_IPPSL_0 | AX_SWRESET_IPPSL_1)) &&
+			     !ax772b_data->pw_enabled) {
+				/*
+				 * AX88772B already entered power saving state
+				 */
+				ax772b_data->pw_enabled = 1;
+			}
+			if (ax772b_data->psc & AX_SWRESET_AUTODETACH)
+				ax772b_data->Event = AX_CHK_AUTODETACH;
+
+		} else {
+			/* AX88772B resumed from power saving state */
+			if (ax772b_data->pw_enabled ||
+				(jiffies > (ax772b_data->time_to_chk +
+				 AX88772B_WATCHDOG))) {
+				if (ax772b_data->pw_enabled)
+					ax772b_data->pw_enabled = 0;
+				ax772b_data->Event = PHY_POWER_UP;
+				ax772b_data->time_to_chk = jiffies;
+			}
+		}
+	}
+
+	if (ax772b_data->Event)
+		queue_work(ax772b_data->ax_work, &ax772b_data->check_link);
+}
+
+static void ax88772c_status(struct usbnet *dev, struct urb *urb)
+{
+	struct ax88772b_data *ax772b_data = (struct ax88772b_data *)dev->priv;
+	struct ax88172_int_data *event;
+	int link;
+
+	if (urb->actual_length < 8)
+		return;
+
+	if (ax772b_data->OperationMode == OPERATION_PHY_MODE)
+		return;
+
+	event = urb->transfer_buffer;
+	if (ax772b_data->PhySelect == 0 &&
+	    ax772b_data->OperationMode == OPERATION_MAC_MODE)
+		link = (event->link & AX_INT_SPLS_LINK) >> 1;
+	else
+		link = event->link & AX_INT_PPLS_LINK;
+
+	if (netif_carrier_ok(dev->net) != link) {
+		if (link) {
+			netif_carrier_on(dev->net);
+			ax772b_data->Event = AX_SET_RX_CFG;
+		} else {
+			netif_carrier_off(dev->net);
+			ax772b_data->time_to_chk = jiffies;
+		}
+		devwarn(dev, "ax88772c - Link status is: %d", link);
+	}
+
+	if (!link) {
+
+		int no_cable = (event->link & AX_INT_CABOFF_UNPLUG) ? 1 : 0;
+
+		if (no_cable) {
+			if ((ax772b_data->psc &
+			    (AX_SWRESET_IPPSL_0 | AX_SWRESET_IPPSL_1)) &&
+			     !ax772b_data->pw_enabled) {
+				/*
+				 * AX88772B already entered power saving state
+				 */
+				ax772b_data->pw_enabled = 1;
+			}
+			if (ax772b_data->psc & AX_SWRESET_AUTODETACH)
+				ax772b_data->Event = AX_CHK_AUTODETACH;
+		} else {
+			/* AX88772B resumed from power saving state */
+			if (ax772b_data->pw_enabled ||
+				(jiffies > (ax772b_data->time_to_chk +
+				 AX88772B_WATCHDOG))) {
+				if (ax772b_data->pw_enabled)
+					ax772b_data->pw_enabled = 0;
+				ax772b_data->Event = PHY_POWER_UP;
+				ax772b_data->time_to_chk = jiffies;
+			}
+		}
+	}
+
+	if (ax772b_data->Event)
+		queue_work(ax772b_data->ax_work, &ax772b_data->check_link);
+}
+
+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;
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (urb == NULL) {
+		deverr(dev, "Error allocating URB in write_cmd_async!");
+		return;
+	}
+
+	req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
+	if (req == 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);
+
+	status = usb_submit_urb(urb, GFP_ATOMIC);
+	if (status < 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 = AX_RX_CTL_START | AX_RX_CTL_AB;
+	int mc_count;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
+	mc_count = net->mc_count;
+#else
+	mc_count = netdev_mc_count(net);
+#endif
+
+	if (net->flags & IFF_PROMISC) {
+		rx_ctl |= AX_RX_CTL_PRO;
+	} else if (net->flags & IFF_ALLMULTI
+		   || mc_count > AX_MAX_MCAST) {
+		rx_ctl |= AX_RX_CTL_AMALL;
+	} else if (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 */
+		u32 crc_bits;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
+		struct dev_mc_list *mc_list = net->mc_list;
+		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;
+		}
+#else
+		struct netdev_hw_addr *ha = NULL;
+		memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE);
+		netdev_for_each_mc_addr(ha, net) {
+			crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26;
+			data->multi_filter[crc_bits >> 3] |=
+				1 << (crc_bits & 7);
+		}
+#endif
+		ax8817x_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0,
+				   AX_MCAST_FILTER_SIZE, data->multi_filter);
+
+		rx_ctl |= AX_RX_CTL_AM;
+	}
+
+	ax8817x_write_cmd_async(dev, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL);
+}
+
+static void ax88178_set_multicast(struct net_device *net)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct ax8817x_data *data = (struct ax8817x_data *)&dev->data;
+	u16 rx_ctl = (AX_RX_CTL_START | AX_RX_CTL_AB |  AX_RX_CTL_MFB);
+	int mc_count;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
+	mc_count = net->mc_count;
+#else
+	mc_count = netdev_mc_count(net);
+#endif
+
+	if (net->flags & IFF_PROMISC) {
+		rx_ctl |= AX_RX_CTL_PRO;
+	} else if (net->flags & IFF_ALLMULTI
+		   || mc_count > AX_MAX_MCAST) {
+		rx_ctl |= AX_RX_CTL_AMALL;
+	} else if (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 */
+		u32 crc_bits;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
+		struct dev_mc_list *mc_list = net->mc_list;
+		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;
+		}
+#else
+		struct netdev_hw_addr *ha = NULL;
+		memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE);
+		netdev_for_each_mc_addr(ha, net) {
+			crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26;
+			data->multi_filter[crc_bits >> 3] |=
+				1 << (crc_bits & 7);
+		}
+#endif
+		ax8817x_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0,
+				   AX_MCAST_FILTER_SIZE, data->multi_filter);
+
+		rx_ctl |= AX_RX_CTL_AM;
+	}
+
+	ax8817x_write_cmd_async(dev, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL);
+}
+
+static void ax88772b_set_multicast(struct net_device *net)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct ax8817x_data *data = (struct ax8817x_data *)&dev->data;
+	u16 rx_ctl = (AX_RX_CTL_START | AX_RX_CTL_AB | AX_RX_HEADER_DEFAULT);
+	int mc_count;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
+	mc_count = net->mc_count;
+#else
+	mc_count = netdev_mc_count(net);
+#endif
+
+	if (net->flags & IFF_PROMISC) {
+		rx_ctl |= AX_RX_CTL_PRO;
+	} else if (net->flags & IFF_ALLMULTI
+		   || mc_count > AX_MAX_MCAST) {
+		rx_ctl |= AX_RX_CTL_AMALL;
+	} else if (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 */
+		u32 crc_bits;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
+		struct dev_mc_list *mc_list = net->mc_list;
+		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;
+		}
+#else
+		struct netdev_hw_addr *ha = NULL;
+		memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE);
+		netdev_for_each_mc_addr(ha, net) {
+			crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26;
+			data->multi_filter[crc_bits >> 3] |=
+				1 << (crc_bits & 7);
+		}
+#endif
+		ax8817x_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0,
+				   AX_MCAST_FILTER_SIZE, data->multi_filter);
+
+		rx_ctl |= AX_RX_CTL_AM;
+	}
+
+	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, ret;
+	u8* smsr;
+	int i = 0;
+
+	res = kmalloc(2, GFP_ATOMIC);
+	if (!res)
+		return 0;
+
+	do {
+		ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, NULL);
+		
+		msleep(1);
+
+		smsr = (u8*) res;
+		ax8817x_read_cmd(dev, AX_CMD_READ_STATMNGSTS_REG, 0, 0, 1, smsr);
+	} while (!(*smsr & AX_HOST_EN) && (i++ < 30));
+	
+	ax8817x_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id, (__u16)loc, 2, res);
+	ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, NULL);
+
+	ret = *res & 0xffff;
+	kfree(res);
+
+	return ret;
+}
+
+static int
+ax8817x_swmii_mdio_read(struct net_device *netdev, int phy_id, int loc)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	u16 *res;
+	u16 ret;
+
+	res = kmalloc(2, GFP_ATOMIC);
+	if (!res)
+		return 0;
+
+	ax8817x_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id,
+				(__u16)loc, 2, res);
+
+	ret = *res & 0xffff;
+	kfree(res);
+
+	return ret;
+}
+
+/* same as above, but converts resulting value to cpu byte order */
+static int ax8817x_mdio_read_le(struct net_device *netdev, int phy_id, int loc)
+{
+	return le16_to_cpu(ax8817x_mdio_read(netdev, phy_id, loc));
+}
+
+static int
+ax8817x_swmii_mdio_read_le(struct net_device *netdev, int phy_id, int loc)
+{
+	return le16_to_cpu(ax8817x_swmii_mdio_read(netdev, phy_id, loc));
+}
+
+static void
+ax8817x_mdio_write(struct net_device *netdev, int phy_id, int loc, int val)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	u16 *res;
+	u8* smsr;
+	int i = 0;
+
+	res = kmalloc(2, GFP_ATOMIC);
+	if (!res)
+		return;
+	smsr = (u8 *) res;
+
+	do {
+		ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, NULL);
+
+		msleep(1);
+
+		ax8817x_read_cmd(dev, AX_CMD_READ_STATMNGSTS_REG, 0, 0, 1, smsr);
+	} while (!(*smsr & AX_HOST_EN) && (i++ < 30));	
+	
+	*res = val;
+
+	ax8817x_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id,
+			  (__u16)loc, 2, res);
+	ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, NULL);
+
+	kfree(res);
+}
+
+static void ax8817x_swmii_mdio_write(struct net_device *netdev, int phy_id,
+				     int loc, int val)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	u16 *res;
+
+	res = kmalloc(2, GFP_ATOMIC);
+	if (!res)
+		return;
+	*res = val;
+
+	ax8817x_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id,
+				(__u16)loc, 2, res);
+
+	kfree(res);
+}
+
+static void
+ax88772b_mdio_write(struct net_device *netdev, int phy_id, int loc, int val)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	u16 *res;
+
+	res = kmalloc(2, GFP_ATOMIC);
+	if (!res)
+		return;
+	*res = val;
+
+	ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, NULL);
+	ax8817x_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id,
+				(__u16)loc, 2, res);
+
+	if (loc == MII_ADVERTISE) {
+		*res = cpu_to_le16(BMCR_ANENABLE | BMCR_ANRESTART);
+		ax8817x_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id,
+				(__u16)MII_BMCR, 2, res);
+	}
+
+	ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, NULL);
+
+	kfree(res);
+}
+
+/* same as above, but converts new value to le16 byte order before writing */
+static void
+ax8817x_mdio_write_le(struct net_device *netdev, int phy_id, int loc, int val)
+{
+	ax8817x_mdio_write(netdev, phy_id, loc, cpu_to_le16(val));
+}
+
+static void ax8817x_swmii_mdio_write_le(struct net_device *netdev,
+			int phy_id, int loc, int val)
+{
+	ax8817x_swmii_mdio_write(netdev, phy_id, loc, cpu_to_le16(val));
+}
+
+static void
+ax88772b_mdio_write_le(struct net_device *netdev, int phy_id, int loc, int val)
+{
+	ax88772b_mdio_write(netdev, phy_id, loc, cpu_to_le16(val));
+}
+
+static int ax88772_suspend(struct usb_interface *intf,
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 10)
+			   pm_message_t message)
+#else
+			   u32 message)
+#endif
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	u16 *medium;
+
+	medium = kmalloc(2, GFP_ATOMIC);
+	if (!medium)
+		return axusbnet_suspend(intf, message);
+
+	ax8817x_read_cmd(dev, AX_CMD_READ_MEDIUM_MODE, 0, 0, 2, medium);
+	ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE,
+			(*medium & ~AX88772_MEDIUM_RX_ENABLE), 0, 0, NULL);
+
+	kfree(medium);
+	return axusbnet_suspend(intf, message);
+}
+
+static int ax88772b_suspend(struct usb_interface *intf,
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 10)
+			    pm_message_t message)
+#else
+			    u32 message)
+#endif
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	struct ax88772b_data *ax772b_data = (struct ax88772b_data *)dev->priv;
+	u16 *tmp16;
+	u8 *opt;
+
+	tmp16 = kmalloc(2, GFP_ATOMIC);
+	if (!tmp16)
+		return axusbnet_suspend(intf, message);
+	opt = (u8 *)tmp16;
+#if 0
+	/* Read Wake-up Frame Array Register (Mask Wakeup Timer) */
+	ax8817x_read_cmd(dev, AX_CMD_READ_WKFARY, 0x11, 0, 4, &tmp32);
+	tmp32 &= 0xFFF0FFFF;
+	/* 8 second */
+	tmp32 |= 0x00020000;
+	ax8817x_write_cmd(dev, AX_CMD_WRITE_WKFARY, 0x11, 0, 4, &tmp32);
+#endif
+	/* Preserve BMCR for restoring */
+	ax772b_data->presvd_phy_bmcr = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_BMCR);
+
+	/* Preserve Advertisement control reg for restoring */
+	ax772b_data->presvd_phy_advertise = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_ADVERTISE);
+
+	ax8817x_read_cmd(dev, AX_CMD_READ_MEDIUM_MODE, 0, 0, 2, tmp16);
+	ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE,
+			  (*tmp16 & ~AX88772_MEDIUM_RX_ENABLE),
+			  0, 0, NULL);
+
+	ax8817x_read_cmd(dev, AX_CMD_READ_MONITOR_MODE, 0, 0, 1, opt);
+	if (!(*opt & AX_MONITOR_LINK) && !(*opt & AX_MONITOR_MAGIC)) {
+		ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+				  AX_SWRESET_IPRL | AX_SWRESET_IPPD,
+				  0, 0, NULL);
+				  
+	} else {
+
+		if (ax772b_data->psc & AX_SWRESET_WOLLP) {
+			*tmp16 = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id,
+						      MII_BMCR);
+			ax8817x_mdio_write_le(dev->net, dev->mii.phy_id,
+					      MII_BMCR, *tmp16 | BMCR_ANENABLE);
+
+			ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+					  AX_SWRESET_IPRL | ax772b_data->psc,
+					  0, 0, NULL);
+		}
+
+		if (ax772b_data->psc &
+		    (AX_SWRESET_IPPSL_0 | AX_SWRESET_IPPSL_1)) {
+			*opt |= AX_MONITOR_LINK;
+			ax8817x_write_cmd(dev, AX_CMD_WRITE_MONITOR_MODE, *opt,
+					  0, 0, NULL);
+		}
+	}
+	
+	dev->reg_monitor = *opt;
+
+	kfree(tmp16);
+	return axusbnet_suspend(intf, message);
+}
+
+static int ax88772_resume(struct usb_interface *intf)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+
+	netif_carrier_off(dev->net);
+
+	return axusbnet_resume(intf);
+}
+
+static int ax88772b_resume(struct usb_interface *intf)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	struct ax88772b_data *ax772b_data = (struct ax88772b_data *)dev->priv;
+
+	if (ax772b_data->psc & AX_SWRESET_WOLLP) {
+		ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+				  AX_SWRESET_IPRL | (ax772b_data->psc & 0x7FFF),
+				  0, 0, NULL);
+	}
+
+	if (ax772b_data->psc & (AX_SWRESET_IPPSL_0 | AX_SWRESET_IPPSL_1))
+		ax88772a_phy_powerup(dev);
+
+	netif_carrier_off(dev->net);
+
+	if (ax772b_data->OperationMode == OPERATION_PHY_MODE)
+		netif_carrier_on(dev->net);
+		
+	ax8817x_write_cmd(dev, AX_CMD_WRITE_MONITOR_MODE, dev->reg_monitor,
+			  0, 0, NULL);
+
+	return axusbnet_resume(intf);
+}
+
+static int ax88172_link_reset(struct usbnet *dev)
+{
+	u16 lpa;
+	u16 adv;
+	u16 res;
+	u8 mode;
+
+	mode = AX_MEDIUM_TX_ABORT_ALLOW | AX_MEDIUM_FLOW_CONTROL_EN;
+	lpa = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_LPA);
+	adv = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_ADVERTISE);
+	res = mii_nway_result(lpa|adv);
+	if (res & LPA_DUPLEX)
+		mode |= AX_MEDIUM_FULL_DUPLEX;
+	ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL);
+
+	return 0;
+}
+
+static void
+ax8817x_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u8 *opt;
+
+	wolinfo->supported = 0;
+	wolinfo->wolopts = 0;
+
+	opt = kmalloc(1, GFP_KERNEL);
+	if (!opt)
+		return;
+
+	if (ax8817x_read_cmd(dev, AX_CMD_READ_MONITOR_MODE, 0, 0, 1, opt) < 0) {
+		kfree(opt);
+		return;
+	}
+
+	wolinfo->supported = WAKE_PHY | WAKE_MAGIC;
+
+	if (*opt & AX_MONITOR_LINK)
+		wolinfo->wolopts |= WAKE_PHY;
+	if (*opt & AX_MONITOR_MAGIC)
+		wolinfo->wolopts |= WAKE_MAGIC;
+
+	kfree(opt);
+}
+
+static int
+ax8817x_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u8 opt;
+
+	opt = 0;
+	if (wolinfo->wolopts & WAKE_PHY)
+		opt |= AX_MONITOR_LINK;
+	if (wolinfo->wolopts & WAKE_MAGIC)
+		opt |= AX_MONITOR_MAGIC;
+
+	ax8817x_write_cmd(dev, AX_CMD_WRITE_MONITOR_MODE, opt, 0, 0, NULL);
+
+	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 */
+	axusbnet_get_drvinfo(net, info);
+	info->eedump_len = 0x3e;
+}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
+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);
+}
+#else
+static int ax8817x_get_link_ksettings(struct net_device *net,
+				      struct ethtool_link_ksettings *cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+
+	if (!dev->mii.mdio_read)
+		return -EOPNOTSUPP;
+	
+	mii_ethtool_get_link_ksettings(&dev->mii, cmd);	
+
+	return 0; 
+}
+
+static int ax8817x_set_link_ksettings(struct net_device *net,
+				      const struct ethtool_link_ksettings *cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+
+	if (!dev->mii.mdio_write)
+		return -EOPNOTSUPP;
+
+	return mii_ethtool_set_link_ksettings(&dev->mii, cmd);
+}
+#endif
+/* 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		= axusbnet_get_msglevel,
+	.set_msglevel		= axusbnet_set_msglevel,
+	.get_wol		= ax8817x_get_wol,
+	.set_wol		= ax8817x_set_wol,
+	.get_eeprom_len	= ax8817x_get_eeprom_len,
+	.get_eeprom		= ax8817x_get_eeprom,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
+	.get_settings		= ax8817x_get_settings,
+	.set_settings		= ax8817x_set_settings,
+#else
+	.get_link_ksettings	= ax8817x_get_link_ksettings,
+	.set_link_ksettings	= ax8817x_set_link_ksettings,
+#endif
+};
+
+static int ax8817x_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+
+	return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
+}
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
+static const struct net_device_ops ax88x72_netdev_ops = {
+	.ndo_open			= axusbnet_open,
+	.ndo_stop			= axusbnet_stop,
+	.ndo_start_xmit	= axusbnet_start_xmit,
+	.ndo_tx_timeout	= axusbnet_tx_timeout,
+	.ndo_change_mtu	= axusbnet_change_mtu,
+	.ndo_get_stats		= axusbnet_get_stats,
+	.ndo_do_ioctl		= ax8817x_ioctl,
+	.ndo_set_mac_address		= ax8817x_set_mac_addr,
+	.ndo_validate_addr		= eth_validate_addr,
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 2, 0)
+	.ndo_set_multicast_list	= ax8817x_set_multicast,
+#else
+	.ndo_set_rx_mode	= ax8817x_set_multicast,
+#endif
+};
+#endif
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
+static const struct net_device_ops ax88178_netdev_ops = {
+	.ndo_open			= axusbnet_open,
+	.ndo_stop			= axusbnet_stop,
+	.ndo_start_xmit	= axusbnet_start_xmit,
+	.ndo_tx_timeout	= axusbnet_tx_timeout,
+	.ndo_change_mtu	= axusbnet_change_mtu,
+	.ndo_get_stats		= axusbnet_get_stats,
+	.ndo_do_ioctl		= ax8817x_ioctl,
+	.ndo_set_mac_address		= ax8817x_set_mac_addr,
+	.ndo_validate_addr		= eth_validate_addr,
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 2, 0)
+	.ndo_set_multicast_list	= ax88178_set_multicast,
+#else
+	.ndo_set_rx_mode	= ax88178_set_multicast,
+#endif
+};
+#endif
+
+static int access_eeprom_mac(struct usbnet *dev, u8 *buf, u8 offset, bool wflag)
+{
+	int ret = 0, i;
+	u16* tmp = (u16*)buf;
+
+	if (wflag) {
+		ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_EEPROM_EN,
+						0, 0, 0, NULL);
+		if (ret < 0)
+			 return ret;
+
+		mdelay(15);
+	}
+
+	for (i = 0; i < (ETH_ALEN >> 1); i++) {
+		if (wflag) {
+			u16 wd = cpu_to_le16(*(tmp + i));
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_EEPROM, offset + i,
+						wd, 0, NULL);
+			if (ret < 0)
+				break;
+
+			mdelay(15);
+		}
+		else {
+			ret = ax8817x_read_cmd(dev, AX_CMD_READ_EEPROM,
+					       offset + i, 0, 2, tmp + i);
+			if (ret < 0)
+				break;
+		}
+	}
+
+	if (!wflag) {
+		if (ret < 0) {
+			#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)
+				netdev_dbg(dev->net, "Failed to read MAC address from EEPROM: %d\n", ret);
+			#else
+				devdbg(dev, "Failed to read MAC address from EEPROM: %d\n", ret);
+			#endif
+			return ret;
+		}
+		memcpy(dev->net->dev_addr, buf, ETH_ALEN);
+	}
+	else {
+		ax8817x_write_cmd(dev, AX_CMD_WRITE_EEPROM_DIS,
+				  0, 0, 0, NULL);
+		if (ret < 0)
+			 return ret;
+
+		/* reload eeprom data */
+		ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+					AXGPIOS_RSE, 0, 0, NULL);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int ax8817x_check_ether_addr(struct usbnet *dev)
+{
+	unsigned char *tmp = (unsigned char*)dev->net->dev_addr;
+	u8 default_mac[6] = {0, 0x0e, 0xc6, 0x87, 0x72, 0x01};
+
+	if (((*((u8*)tmp) == 0) && (*((u8*)tmp + 1) == 0) && (*((u8*)tmp + 2) == 0)) ||
+	    !is_valid_ether_addr((u8*)tmp) ||
+	    !memcmp(dev->net->dev_addr, default_mac, ETH_ALEN)) {
+		printk("Found invalid EEPROM MAC address value ");		
+		printk("%02X-%02X-%02X-%02X-%02X-%02X\n", *((u8*)tmp + 0),
+							*((u8*)tmp + 1),
+							*((u8*)tmp + 2),
+							*((u8*)tmp + 3),
+							*((u8*)tmp + 4),
+							*((u8*)tmp + 5));
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
+		eth_hw_addr_random(dev->net);
+#else
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
+		dev->net->addr_assign_type |= NET_ADDR_RANDOM;
+#endif
+		random_ether_addr(dev->net->dev_addr); 
+#endif
+		*tmp = 0;
+		*(tmp + 1) = 0x0E;
+		*(tmp + 2) = 0xC6;
+		*(tmp + 3) = 0x8F;
+
+		return -EADDRNOTAVAIL;	
+	} 
+	return 0;
+}
+
+static int ax8817x_get_mac(struct usbnet *dev, u8* buf)
+{
+	int ret, i;
+	
+
+	ret = access_eeprom_mac(dev, buf, 0x04, 0);
+	if (ret < 0)
+		goto out;
+
+	if (ax8817x_check_ether_addr(dev)) {
+		ret = access_eeprom_mac(dev, dev->net->dev_addr, 0x04, 1);
+		if (ret < 0) {
+			deverr(dev, "Failed to write MAC to EEPROM: %d", ret);
+			goto out;
+		}
+
+		msleep(5);
+
+		ret = ax8817x_read_cmd(dev, AX88772_CMD_READ_NODE_ID,
+				       0, 0, ETH_ALEN, buf);
+		if (ret < 0) {
+			deverr(dev, "Failed to read MAC address: %d", ret);
+			goto out;
+		}
+
+		for (i = 0; i < ETH_ALEN; i++)
+			if (*(dev->net->dev_addr + i) != *((u8*)buf + i)) {
+				devwarn(dev, "Found invalid EEPROM part or non-EEPROM");
+				break;
+			}
+	}
+
+	memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN);
+
+	/* Set the MAC address */
+	ax8817x_write_cmd (dev, AX88772_CMD_WRITE_NODE_ID, 0, 0,
+			   ETH_ALEN, dev->net->dev_addr);
+	
+	if (ret < 0) {
+		deverr(dev, "Failed to write MAC address: %d", ret);
+		goto out;
+	}
+
+	return 0;
+out:
+	return ret;
+}
+
+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;
+
+	axusbnet_get_endpoints(dev, intf);
+
+	buf = kmalloc(ETH_ALEN, GFP_KERNEL);
+	if (!buf) {
+		ret = -ENOMEM;
+		goto out1;
+	}
+
+	/* Toggle the GPIOs in a manufacturer/model specific way */
+	for (i = 2; i >= 0; i--) {
+		ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+					(gpio_bits >> (i * 8)) & 0xff,
+					0, 0, NULL);
+		if (ret < 0)
+			goto out2;
+
+		msleep(5);
+	}
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, 0x80, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "send AX_CMD_WRITE_RX_CTL failed: %d", ret);
+		goto out2;
+	}
+
+	/* Get the MAC address */
+	memset(buf, 0, ETH_ALEN);
+	ret = ax8817x_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, 6, buf);
+	if (ret < 0) {
+		deverr(dev, "read AX_CMD_READ_NODE_ID failed: %d", ret);
+		goto out2;
+	}
+	memcpy(dev->net->dev_addr, buf, ETH_ALEN);
+
+	/* Get the PHY id */
+	ret = ax8817x_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf);
+	if (ret < 0) {
+		deverr(dev, "error on read AX_CMD_READ_PHY_ID: %02x", ret);
+		goto out2;
+	} else if (ret < 2) {
+		/* this should always return 2 bytes */
+		deverr(dev, "Read PHYID returned less than 2 bytes: ret=%02x",
+				ret);
+		ret = -EIO;
+		goto out2;
+	}
+
+	/* Initialize MII structure */
+	dev->mii.dev = dev->net;
+	dev->mii.mdio_read = ax8817x_mdio_read_le;
+	dev->mii.mdio_write = ax8817x_mdio_write_le;
+	dev->mii.phy_id_mask = 0x3f;
+	dev->mii.reg_num_mask = 0x1f;
+	dev->mii.phy_id = *((u8 *)buf + 1);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
+	dev->net->do_ioctl = ax8817x_ioctl;
+	dev->net->set_multicast_list = ax8817x_set_multicast;
+	dev->net->set_mac_address = ax8817x_set_mac_addr;
+#else
+	dev->net->netdev_ops = &ax88x72_netdev_ops;
+#endif
+
+	dev->net->ethtool_ops = &ax8817x_ethtool_ops;
+
+	/* Register suspend and resume functions */
+	data->suspend = axusbnet_suspend;
+	data->resume = axusbnet_resume;
+
+	ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
+	ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+		ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
+	mii_nway_restart(&dev->mii);
+
+	printk(version);
+
+	return 0;
+out2:
+	kfree(buf);
+out1:
+	return ret;
+}
+
+static struct ethtool_ops ax88772_ethtool_ops = {
+	.get_drvinfo		= ax8817x_get_drvinfo,
+	.get_link		= ethtool_op_get_link,
+	.get_msglevel		= axusbnet_get_msglevel,
+	.set_msglevel		= axusbnet_set_msglevel,
+	.get_wol		= ax8817x_get_wol,
+	.set_wol		= ax8817x_set_wol,
+	.get_eeprom_len		= ax8817x_get_eeprom_len,
+	.get_eeprom		= ax8817x_get_eeprom,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
+	.get_settings		= ax8817x_get_settings,
+	.set_settings		= ax8817x_set_settings,
+#else
+	.get_link_ksettings	= ax8817x_get_link_ksettings,
+	.set_link_ksettings	= ax8817x_set_link_ksettings,
+#endif
+};
+
+static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	int ret;
+	void *buf;
+	struct ax8817x_data *data = (struct ax8817x_data *)&dev->data;
+	struct ax88772_data *ax772_data = NULL;
+
+	axusbnet_get_endpoints(dev, intf);
+
+	buf = kmalloc(6, GFP_KERNEL);
+	if (!buf) {
+		deverr(dev, "Cannot allocate memory for buffer");
+		ret = -ENOMEM;
+		goto out1;
+	}
+
+	ax772_data = kmalloc(sizeof(*ax772_data), GFP_KERNEL);
+	if (!ax772_data) {
+		deverr(dev, "Cannot allocate memory for AX88772 data");
+		kfree(buf);
+		return -ENOMEM;
+	}
+
+	memset(ax772_data, 0, sizeof(*ax772_data));
+	dev->priv = ax772_data;
+
+	ax772_data->ax_work = create_singlethread_workqueue("ax88772");
+	if (!ax772_data->ax_work) {
+		kfree(ax772_data);
+		kfree(buf);
+		return -ENOMEM;
+	}
+
+	ax772_data->dev = dev;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+	INIT_WORK(&ax772_data->check_link, ax88772_link_reset, dev);
+#else
+	INIT_WORK(&ax772_data->check_link, ax88772_link_reset);
+#endif
+
+	/* reload eeprom data */
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS, 0x00B0, 0, 0, NULL);
+	if (ret < 0)
+		goto out2;
+
+	msleep(5);
+
+	/* Initialize MII structure */
+	dev->mii.dev = dev->net;
+	dev->mii.mdio_read = ax8817x_mdio_read_le;
+	dev->mii.mdio_write = ax8817x_mdio_write_le;
+	dev->mii.phy_id_mask = 0xff;
+	dev->mii.reg_num_mask = 0xff;
+
+	/* Get the PHY id */
+	ret = ax8817x_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf);
+	if (ret < 0) {
+		deverr(dev, "Error reading PHY ID: %02x", ret);
+		goto out2;
+	} else if (ret < 2) {
+		/* this should always return 2 bytes */
+		deverr(dev, "Read PHYID returned less than 2 bytes: ret=%02x",
+		    ret);
+		ret = -EIO;
+		goto out2;
+	}
+	dev->mii.phy_id = *((u8 *)buf + 1);
+
+	if (dev->mii.phy_id == 0x10) {
+		ret = ax8817x_write_cmd(dev, AX_CMD_SW_PHY_SELECT,
+					0x0001, 0, 0, NULL);
+		if (ret < 0) {
+			deverr(dev, "Select PHY #1 failed: %d", ret);
+			goto out2;
+		}
+
+		ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPPD,
+					0, 0, NULL);
+		if (ret < 0) {
+			deverr(dev, "Failed to power down PHY: %d", ret);
+			goto out2;
+		}
+
+		msleep(150);
+
+		ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_CLEAR,
+					0, 0, NULL);
+		if (ret < 0) {
+			deverr(dev, "Failed to perform software reset: %d",
+			       ret);
+			goto out2;
+		}
+
+		msleep(150);
+
+		ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+					AX_SWRESET_IPRL | AX_SWRESET_PRL,
+					0, 0, NULL);
+		if (ret < 0) {
+			deverr(dev,
+			      "Failed to set PHY reset control: %d", ret);
+			goto out2;
+		}
+	} else {
+		ret = ax8817x_write_cmd(dev, AX_CMD_SW_PHY_SELECT,
+					0x0000, 0, 0, NULL);
+		if (ret < 0) {
+			deverr(dev, "Select PHY #1 failed: %d", ret);
+			goto out2;
+		}
+
+		ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+					AX_SWRESET_IPPD | AX_SWRESET_PRL,
+					0, 0, NULL);
+		if (ret < 0) {
+			deverr(dev, "Failed to power down internal PHY: %d",
+			       ret);
+			goto out2;
+		}
+	}
+
+	msleep(150);
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL,
+				0x0000, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Failed to reset RX_CTL: %d", ret);
+		goto out2;
+	}
+
+	/* Get the MAC address */
+	memset(buf, 0, ETH_ALEN);
+	ret = ax8817x_get_mac(dev, buf);
+	if (ret < 0) {
+		deverr(dev, "Get HW address failed: %d", ret);
+		goto out2;
+	}
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Enabling software MII failed: %d", ret);
+		goto out2;
+	}
+
+	if (dev->mii.phy_id == 0x10) {
+		ret = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, 2);
+		if (ret != 0x003b) {
+			deverr(dev, "Read PHY register 2 must be 0x3b00: %d",
+			       ret);
+			goto out2;
+		}
+
+		ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_PRL,
+					0, 0, NULL);
+		if (ret < 0) {
+			deverr(dev, "Set external PHY reset pin level: %d",
+			       ret);
+			goto out2;
+		}
+		msleep(150);
+
+		ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+					AX_SWRESET_IPRL | AX_SWRESET_PRL,
+					0, 0, NULL);
+		if (ret < 0) {
+			deverr(dev,
+			       "Set Internal/External PHY reset control: %d",
+			       ret);
+			goto out2;
+		}
+		msleep(150);
+	}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
+	dev->net->do_ioctl = ax8817x_ioctl;
+	dev->net->set_multicast_list = ax8817x_set_multicast;
+	dev->net->set_mac_address = ax8817x_set_mac_addr;
+#else
+	dev->net->netdev_ops = &ax88x72_netdev_ops;
+#endif
+
+	dev->net->ethtool_ops = &ax88772_ethtool_ops;
+
+	/* Register suspend and resume functions */
+	data->suspend = ax88772_suspend;
+	data->resume = ax88772_resume;
+
+	ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
+	ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+			ADVERTISE_ALL | ADVERTISE_CSMA);
+
+	mii_nway_restart(&dev->mii);
+	ax772_data->autoneg_start = jiffies;
+	ax772_data->Event = WAIT_AUTONEG_COMPLETE;
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, 0, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Write medium mode register: %d", ret);
+		goto out2;
+	}
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_IPG0,
+				AX88772_IPG0_DEFAULT |
+				(AX88772_IPG1_DEFAULT << 8),
+				AX88772_IPG2_DEFAULT, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Write IPG,IPG1,IPG2 failed: %d", ret);
+		goto out2;
+	}
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Failed to set hardware MII: %02x", ret);
+		goto out2;
+	}
+
+	/* Set RX_CTL to default values with 2k buffer, and enable cactus */
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, 0x0088, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Reset RX_CTL failed: %d", ret);
+		goto out2;
+	}
+
+	/* Asix framing packs multiple eth frames into a 2K usb bulk transfer */
+	if (dev->driver_info->flags & FLAG_FRAMING_AX) {
+		/* hard_mtu  is still the default - the device does not support
+		   jumbo eth frames */
+		dev->rx_urb_size = 2048;
+	}
+
+	kfree(buf);
+	printk(version);
+	return 0;
+
+out2:
+	destroy_workqueue(ax772_data->ax_work);
+	kfree(ax772_data);
+	kfree(buf);
+out1:
+	return ret;
+}
+
+static void ax88772_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+	struct ax88772_data *ax772_data = (struct ax88772_data *)dev->priv;
+
+	if (ax772_data) {
+		flush_workqueue(ax772_data->ax_work);
+		destroy_workqueue(ax772_data->ax_work);
+
+		/* stop MAC operation */
+		ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, AX_RX_CTL_STOP,
+				  0, 0, NULL);
+
+		/* Power down PHY */
+		ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPPD,
+				  0, 0, NULL);
+
+		kfree(ax772_data);
+	}
+}
+
+static int ax88772a_phy_powerup(struct usbnet *dev)
+{
+	int ret;
+	/* set the embedded Ethernet PHY in power-down state */
+	ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+				AX_SWRESET_IPPD | AX_SWRESET_IPRL, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Failed to power down PHY: %d", ret);
+		return ret;
+	}
+
+	msleep(10);
+
+	/* set the embedded Ethernet PHY in power-up state */
+	ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPRL,
+				0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Failed to reset PHY: %d", ret);
+		return ret;
+	}
+
+	msleep(600);
+
+	/* set the embedded Ethernet PHY in reset state */
+	ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_CLEAR,
+				0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Failed to power up PHY: %d", ret);
+		return ret;
+	}
+
+	/* set the embedded Ethernet PHY in power-up state */
+	ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPRL,
+				0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Failed to reset PHY: %d", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ax88772a_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	int ret = -EIO;
+	void *buf;
+	struct ax8817x_data *data = (struct ax8817x_data *)&dev->data;
+	struct ax88772a_data *ax772a_data = NULL;
+
+	printk(version);
+
+	axusbnet_get_endpoints(dev, intf);
+
+	buf = kmalloc(6, GFP_KERNEL);
+	if (!buf) {
+		deverr(dev, "Cannot allocate memory for buffer");
+		ret = -ENOMEM;
+		goto out1;
+	}
+
+	ax772a_data = kmalloc(sizeof(*ax772a_data), GFP_KERNEL);
+	if (!ax772a_data) {
+		deverr(dev, "Cannot allocate memory for AX88772A data");
+		kfree(buf);
+		return -ENOMEM;
+	}
+	memset(ax772a_data, 0, sizeof(*ax772a_data));
+	dev->priv = ax772a_data;
+
+	ax772a_data->ax_work = create_singlethread_workqueue("ax88772a");
+	if (!ax772a_data->ax_work) {
+		kfree(ax772a_data);
+		kfree(buf);
+		return -ENOMEM;
+	}
+
+	ax772a_data->dev = dev;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+	INIT_WORK(&ax772a_data->check_link, ax88772a_link_reset, dev);
+#else
+	INIT_WORK(&ax772a_data->check_link, ax88772a_link_reset);
+#endif
+
+	/* Get the EEPROM data*/
+	ret = ax8817x_read_cmd(dev, AX_CMD_READ_EEPROM, 0x0017, 0, 2,
+			       (void *)&ax772a_data->EepromData);
+	if (ret < 0) {
+		deverr(dev, "read SROM address 17h failed: %d", ret);
+		goto out2;
+	}
+	le16_to_cpus(&ax772a_data->EepromData);
+	/* End of get EEPROM data */
+
+	/* reload eeprom data */
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+				AXGPIOS_RSE, 0, 0, NULL);
+	if (ret < 0)
+		goto out2;
+
+	msleep(5);
+
+	/* Initialize MII structure */
+	dev->mii.dev = dev->net;
+	dev->mii.mdio_read = ax8817x_mdio_read_le;
+	dev->mii.mdio_write = ax8817x_mdio_write_le;
+	dev->mii.phy_id_mask = 0xff;
+	dev->mii.reg_num_mask = 0xff;
+
+	/* Get the PHY id */
+	ret = ax8817x_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf);
+	if (ret < 0) {
+		deverr(dev, "Error reading PHY ID: %02x", ret);
+		goto out2;
+	} else if (ret < 2) {
+		/* this should always return 2 bytes */
+		deverr(dev, "Read PHYID returned less than 2 bytes: ret=%02x",
+			ret);
+		goto out2;
+	}
+	dev->mii.phy_id = *((u8 *)buf + 1);
+
+	if (dev->mii.phy_id != 0x10) {
+		deverr(dev, "Got wrong PHY ID: %02x", dev->mii.phy_id);
+		goto out2;
+	}
+
+	/* select the embedded 10/100 Ethernet PHY */
+	ret = ax8817x_write_cmd(dev, AX_CMD_SW_PHY_SELECT,
+			AX_PHYSEL_SSEN | AX_PHYSEL_PSEL | AX_PHYSEL_SSMII,
+			0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Select PHY #1 failed: %d", ret);
+		goto out2;
+	}
+
+	ret = ax88772a_phy_powerup(dev);
+	if (ret < 0)
+		goto out2;
+
+	/* stop MAC operation */
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, AX_RX_CTL_STOP,
+				0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Reset RX_CTL failed: %d", ret);
+		goto out2;
+	}
+
+	/* Get the MAC address */
+	memset(buf, 0, ETH_ALEN);
+	ret = ax8817x_get_mac(dev, buf);
+	if (ret < 0) {
+		deverr(dev, "Get HW address failed: %d", ret);
+		goto out2;
+	}	
+
+	/* make sure the driver can enable sw mii operation */
+	ret = ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Enabling software MII failed: %d", ret);
+		goto out2;
+	}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
+	dev->net->do_ioctl = ax8817x_ioctl;
+	dev->net->set_multicast_list = ax8817x_set_multicast;
+	dev->net->set_mac_address = ax8817x_set_mac_addr;
+#else
+	dev->net->netdev_ops = &ax88x72_netdev_ops;
+#endif
+
+	dev->net->ethtool_ops = &ax88772_ethtool_ops;
+
+	/* Register suspend and resume functions */
+	data->suspend = ax88772_suspend;
+	data->resume = ax88772_resume;
+
+	ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
+	ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+			ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
+
+	mii_nway_restart(&dev->mii);
+	ax772a_data->autoneg_start = jiffies;
+	ax772a_data->Event = WAIT_AUTONEG_COMPLETE;
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE,
+				0, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Write medium mode register: %d", ret);
+		goto out2;
+	}
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_IPG0,
+			AX88772A_IPG0_DEFAULT | AX88772A_IPG1_DEFAULT << 8,
+			AX88772A_IPG2_DEFAULT, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Write IPG,IPG1,IPG2 failed: %d", ret);
+		goto out2;
+	}
+
+	memset(buf, 0, 4);
+	ret = ax8817x_read_cmd(dev, AX_CMD_READ_IPG012, 0, 0, 3, buf);
+	*((u8 *)buf + 3) = 0x00;
+	if (ret < 0) {
+		deverr(dev, "Failed to read IPG,IPG1,IPG2 failed: %d", ret);
+		goto out2;
+	} else {
+		__u32 tmp32 = *((u32*)buf);
+		le32_to_cpus(&tmp32);
+		if (tmp32 != (AX88772A_IPG2_DEFAULT << 16 |
+			AX88772A_IPG1_DEFAULT << 8 | AX88772A_IPG0_DEFAULT)) {
+			printk("Non-authentic ASIX product\nASIX does not support it\n");
+			ret = -ENODEV;		
+			goto out2;
+		}
+	}
+
+	/* Set RX_CTL to default values with 2k buffer, and enable cactus */
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL,
+			(AX_RX_CTL_START | AX_RX_CTL_AB),
+			0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Reset RX_CTL failed: %d", ret);
+		goto out2;
+	}
+
+	/* Asix framing packs multiple eth frames into a 2K usb bulk transfer */
+	if (dev->driver_info->flags & FLAG_FRAMING_AX) {
+		/* hard_mtu  is still the default - the device does not support
+		   jumbo eth frames */
+		dev->rx_urb_size = 2048;
+	}
+
+	kfree(buf);
+
+	return ret;
+out2:
+	destroy_workqueue(ax772a_data->ax_work);
+	kfree(ax772a_data);
+	kfree(buf);
+out1:
+	return ret;
+}
+
+static void ax88772a_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+	struct ax88772a_data *ax772a_data = (struct ax88772a_data *)dev->priv;
+
+	if (ax772a_data) {
+
+		flush_workqueue(ax772a_data->ax_work);
+		destroy_workqueue(ax772a_data->ax_work);
+
+		/* stop MAC operation */
+		ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL,
+					AX_RX_CTL_STOP, 0, 0, NULL);
+
+		/* Power down PHY */
+		ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+					AX_SWRESET_IPPD, 0, 0, NULL);
+
+		kfree(ax772a_data);
+	}
+}
+
+static int ax88772b_set_csums(struct usbnet *dev)
+{
+	struct ax88772b_data *ax772b_data = (struct ax88772b_data *)dev->priv;
+	u16 checksum;
+
+	if (ax772b_data->checksum & AX_RX_CHECKSUM)
+		checksum = AX_RXCOE_DEF_CSUM;
+	else
+		checksum = 0;
+
+	ax8817x_write_cmd(dev, AX_CMD_WRITE_RXCOE_CTL,
+				 checksum, 0, 0, NULL);
+
+	if (ax772b_data->checksum & AX_TX_CHECKSUM)
+		checksum = AX_TXCOE_DEF_CSUM;
+	else
+		checksum = 0;
+
+	ax8817x_write_cmd(dev, AX_CMD_WRITE_TXCOE_CTL,
+				 checksum, 0, 0, NULL);
+
+	return 0;
+}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+static u32 ax88772b_get_tx_csum(struct net_device *netdev)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	struct ax88772b_data *ax772b_data = (struct ax88772b_data *)dev->priv;
+
+	return ax772b_data->checksum & AX_TX_CHECKSUM;
+}
+
+static u32 ax88772b_get_rx_csum(struct net_device *netdev)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	struct ax88772b_data *ax772b_data = (struct ax88772b_data *)dev->priv;
+
+	return ax772b_data->checksum & AX_RX_CHECKSUM;
+}
+
+static int ax88772b_set_rx_csum(struct net_device *netdev, u32 val)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	struct ax88772b_data *ax772b_data = (struct ax88772b_data *)dev->priv;
+
+	if (val)
+		ax772b_data->checksum |= AX_RX_CHECKSUM;
+	else
+		ax772b_data->checksum &= ~AX_RX_CHECKSUM;
+
+	return ax88772b_set_csums(dev);
+}
+
+static int ax88772b_set_tx_csum(struct net_device *netdev, u32 val)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	struct ax88772b_data *ax772b_data = (struct ax88772b_data *)dev->priv;
+
+	if (val)
+		ax772b_data->checksum |= AX_TX_CHECKSUM;
+	else
+		ax772b_data->checksum &= ~AX_TX_CHECKSUM;
+
+	ethtool_op_set_tx_csum(netdev, val);
+
+	return ax88772b_set_csums(dev);
+}
+#endif
+static struct ethtool_ops ax88772b_ethtool_ops = {
+	.get_drvinfo		= ax8817x_get_drvinfo,
+	.get_link		= ethtool_op_get_link,
+	.get_msglevel		= axusbnet_get_msglevel,
+	.set_msglevel		= axusbnet_set_msglevel,
+	.get_wol		= ax8817x_get_wol,
+	.set_wol		= ax8817x_set_wol,
+	.get_eeprom_len		= ax8817x_get_eeprom_len,
+	.get_eeprom		= ax8817x_get_eeprom,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
+	.get_settings		= ax8817x_get_settings,
+	.set_settings		= ax8817x_set_settings,
+#else
+	.get_link_ksettings	= ax8817x_get_link_ksettings,
+	.set_link_ksettings	= ax8817x_set_link_ksettings,
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
+	.set_tx_csum		= ax88772b_set_tx_csum,
+	.get_tx_csum		= ax88772b_get_tx_csum,
+	.get_rx_csum		= ax88772b_get_rx_csum,
+	.set_rx_csum		= ax88772b_set_rx_csum,
+#endif
+};
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
+static const struct net_device_ops ax88772b_netdev_ops = {
+	.ndo_open		= axusbnet_open,
+	.ndo_stop		= axusbnet_stop,
+	.ndo_start_xmit		= axusbnet_start_xmit,
+	.ndo_tx_timeout		= axusbnet_tx_timeout,
+	.ndo_change_mtu		= axusbnet_change_mtu,
+	.ndo_do_ioctl		= ax8817x_ioctl,
+	.ndo_get_stats		= axusbnet_get_stats,
+	.ndo_set_mac_address	= ax8817x_set_mac_addr,
+	.ndo_validate_addr	= eth_validate_addr,
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 2, 0)
+	.ndo_set_multicast_list	= ax88772b_set_multicast,
+#else
+	.ndo_set_rx_mode	= ax88772b_set_multicast,
+#endif
+};
+#endif
+
+static int ax88772b_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	int ret;
+	void *buf;
+	struct ax8817x_data *data = (struct ax8817x_data *)&dev->data;
+	struct ax88772b_data *ax772b_data;
+	u16 *tmp16;
+	u8  *tmp8;
+	u8 tempphyselect;
+	bool internalphy;
+
+	printk(version);
+	axusbnet_get_endpoints(dev, intf);
+	buf = kmalloc(6, GFP_KERNEL);
+	if (!buf) {
+		deverr(dev, "Cannot allocate memory for buffer");
+		return -ENOMEM;
+	}
+	tmp16 = (u16 *)buf;
+	ax772b_data = kmalloc(sizeof(*ax772b_data), GFP_KERNEL);
+	if (!ax772b_data) {
+		deverr(dev, "Cannot allocate memory for AX88772B data");
+		kfree(buf);
+		return -ENOMEM;
+	}
+	memset(ax772b_data, 0, sizeof(*ax772b_data));
+	dev->priv = ax772b_data;
+	ax772b_data->ax_work = create_singlethread_workqueue("ax88772b");
+	if (!ax772b_data->ax_work) {
+		kfree(buf);
+		kfree(ax772b_data);
+		return -ENOMEM;
+	}
+
+	ax772b_data->dev = dev;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+	INIT_WORK(&ax772b_data->check_link, ax88772b_link_reset, dev);
+#else
+	INIT_WORK(&ax772b_data->check_link, ax88772b_link_reset);
+#endif
+
+	tmp8 = (u8 *)buf;
+	ret = ax8817x_read_cmd(dev, AX_CMD_SW_PHY_STATUS,
+			       0, 0, 1, tmp8);
+
+	if (ret < 0) {
+		deverr(dev,
+		       "read SW interface selection status register failed: %d\n",
+		       ret);
+		goto err_out;
+	}
+	tempphyselect = *tmp8;
+	tempphyselect &= 0x0C;
+
+	if (tempphyselect == AX_PHYSEL_SSRMII) {
+		internalphy = false;
+		ax772b_data->OperationMode = OPERATION_MAC_MODE;
+		ax772b_data->PhySelect = 0x00;
+	} else if (tempphyselect == AX_PHYSEL_SSRRMII) {
+		internalphy = true;
+		ax772b_data->OperationMode = OPERATION_PHY_MODE;
+		ax772b_data->PhySelect = 0x00;
+	} else if (tempphyselect == AX_PHYSEL_SSMII) {
+		internalphy = true;
+		ax772b_data->OperationMode = OPERATION_MAC_MODE;
+		ax772b_data->PhySelect = 0x01;
+	} else {
+		deverr(dev, "Unknown MII type\n");
+		goto err_out;
+	}
+
+	/* reload eeprom data */
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS, AXGPIOS_RSE,
+				0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Failed to enable GPIO function: %d", ret);
+		goto err_out;
+	}
+	msleep(5);
+
+	/* Get the EEPROM data*/
+	ret = ax8817x_read_cmd(dev, AX_CMD_READ_EEPROM, 0x18, 0, 2,
+			       (void *)tmp16);
+	if (ret < 0) {
+		deverr(dev, "read SROM address 18h failed: %d", ret);
+		goto err_out;
+	}
+	le16_to_cpus(tmp16);
+	ax772b_data->psc = *tmp16 & 0xFF00;
+	/* End of get EEPROM data */
+
+	/* Get the MAC address */
+	memset(buf, 0, ETH_ALEN);
+	ret = ax8817x_get_mac(dev, buf);
+	if (ret < 0) {
+		deverr(dev, "Get HW address failed: %d", ret);
+		goto err_out;
+	}
+
+	/* Initialize MII structure */
+	dev->mii.dev = dev->net;
+	dev->mii.mdio_read = ax8817x_mdio_read_le;
+	dev->mii.mdio_write = ax88772b_mdio_write_le;
+	dev->mii.phy_id_mask = 0xff;
+	dev->mii.reg_num_mask = 0xff;
+
+	/* Get the PHY id */
+	ret = ax8817x_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf);
+	if (ret < 0) {
+		deverr(dev, "Error reading PHY ID: %02x", ret);
+		goto err_out;
+	} else if (ret < 2) {
+		/* this should always return 2 bytes */
+		deverr(dev, "Read PHYID returned less than 2 bytes: ret=%02x",
+		    ret);
+		ret = -EIO;
+		goto err_out;
+	}
+
+	if (internalphy)
+		dev->mii.phy_id = *((u8 *)buf + 1);
+	else
+		dev->mii.phy_id = *((u8 *)buf);
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_SW_PHY_SELECT,
+				ax772b_data->PhySelect, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Select PHY #1 failed: %d", ret);
+		goto err_out;
+	}
+
+#if 0
+	/* select the embedded 10/100 Ethernet PHY */
+	ret = ax8817x_write_cmd(dev, AX_CMD_SW_PHY_SELECT,
+			AX_PHYSEL_SSEN | AX_PHYSEL_PSEL | AX_PHYSEL_SSMII,
+			0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Select PHY #1 failed: %d", ret);
+		goto err_out;
+	}
+
+	if (dev->mii.phy_id != 0x10) {
+		deverr(dev, "Got wrong PHY ID: %02x", dev->mii.phy_id);
+		ret = -EIO;
+		goto err_out;
+	}
+#endif
+	ret = ax88772a_phy_powerup(dev);
+	if (ret < 0)
+		goto err_out;
+
+	/* stop MAC operation */
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL,
+				AX_RX_CTL_STOP, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Reset RX_CTL failed: %d", ret);
+		goto err_out;
+	}
+
+	/* make sure the driver can enable sw mii operation */
+	ret = ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Enabling software MII failed: %d", ret);
+		goto err_out;
+	}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
+	dev->net->do_ioctl = ax8817x_ioctl;
+	dev->net->set_multicast_list = ax88772b_set_multicast;
+	dev->net->set_mac_address = ax8817x_set_mac_addr;
+#else
+	dev->net->netdev_ops = &ax88772b_netdev_ops;
+#endif
+
+	dev->net->ethtool_ops = &ax88772b_ethtool_ops;
+
+	/* Register suspend and resume functions */
+	data->suspend = ax88772b_suspend;
+	data->resume = ax88772b_resume;
+
+	if ((ax772b_data->OperationMode == OPERATION_MAC_MODE) &&
+	    (ax772b_data->PhySelect == 0x00)) {
+		if (ax88772b_external_phyinit(dev) != 0x00) {
+			deverr(dev, "Failed to initial the external phy");
+			goto err_out;
+		}
+	}
+
+	if (ax772b_data->OperationMode == OPERATION_PHY_MODE)
+		ax8817x_mdio_write_le(dev->net, dev->mii.phy_id
+						, MII_BMCR, 0x3900);
+
+	if (dev->mii.phy_id != 0x10)
+		ax8817x_mdio_write_le(dev->net, 0x10, MII_BMCR, 0x3900);
+
+	if (dev->mii.phy_id == 0x10 && ax772b_data->OperationMode
+						!= OPERATION_PHY_MODE) {
+
+		*tmp16 = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, 0x12);
+		ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, 0x12,
+					((*tmp16 & 0xFF9F) | 0x0040));
+	}	
+
+	ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+			ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
+
+	mii_nway_restart(&dev->mii);
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE,
+				0, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Failed to write medium mode: %d", ret);
+		goto err_out;
+	}
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_IPG0,
+			AX88772A_IPG0_DEFAULT | AX88772A_IPG1_DEFAULT << 8,
+			AX88772A_IPG2_DEFAULT, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Failed to write interframe gap: %d", ret);
+		goto err_out;
+	}
+
+	memset(buf, 0, 4);
+	ret = ax8817x_read_cmd(dev, AX_CMD_READ_IPG012, 0, 0, 3, buf);
+	*((u8 *)buf + 3) = 0x00;
+	if (ret < 0) {
+		deverr(dev, "Failed to read IPG,IPG1,IPG2 failed: %d", ret);
+		goto err_out;
+	} else {
+		__u32 tmp32 = *((u32*)buf);
+		le32_to_cpus(&tmp32);
+		if (tmp32 != (AX88772A_IPG2_DEFAULT << 16 |
+			AX88772A_IPG1_DEFAULT << 8 | AX88772A_IPG0_DEFAULT)) {
+			printk("Non-authentic ASIX product\nASIX does not support it\n");
+			ret = -ENODEV;		
+			goto err_out;
+		}
+	}
+
+	dev->net->features |= NETIF_F_IP_CSUM;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22)
+	dev->net->features |= NETIF_F_IPV6_CSUM;
+#endif
+
+	ax772b_data->checksum = AX_RX_CHECKSUM | AX_TX_CHECKSUM;
+	ret = ax88772b_set_csums(dev);
+	if (ret < 0) {
+		deverr(dev, "Write RX_COE/TX_COE failed: %d", ret);
+		goto err_out;
+	}
+
+	dev->rx_size = bsize & 0x07;
+	if (dev->udev->speed == USB_SPEED_HIGH) {
+
+		ret = ax8817x_write_cmd(dev, 0x2A,
+				AX88772B_BULKIN_SIZE[dev->rx_size].byte_cnt,
+				AX88772B_BULKIN_SIZE[dev->rx_size].threshold,
+				0, NULL);
+		if (ret < 0) {
+			deverr(dev, "Reset RX_CTL failed: %d", ret);
+			goto err_out;
+		}
+
+		dev->rx_urb_size = AX88772B_BULKIN_SIZE[dev->rx_size].size;
+	} else {
+		ret = ax8817x_write_cmd(dev, 0x2A,
+				0x8000, 0x8001, 0, NULL);
+		if (ret < 0) {
+			deverr(dev, "Reset RX_CTL failed: %d", ret);
+			goto err_out;
+		}
+		dev->rx_urb_size = 2048;
+	}
+
+	/* Configure RX header type */
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL,
+		      (AX_RX_CTL_START | AX_RX_CTL_AB | AX_RX_HEADER_DEFAULT),
+		      0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Reset RX_CTL failed: %d", ret);
+		goto err_out;
+	}
+
+	/* Overwrite power saving configuration from eeprom */
+	ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPRL |
+				(ax772b_data->psc & 0x7FFF), 0, 0, NULL);
+
+	if (ret < 0) {
+		deverr(dev, "Failed to configure PHY power saving: %d", ret);
+		goto err_out;
+	}
+
+	if (ax772b_data->OperationMode == OPERATION_PHY_MODE)
+		netif_carrier_on(dev->net);
+
+	kfree(buf);
+
+	return ret;
+err_out:
+	destroy_workqueue(ax772b_data->ax_work);
+	kfree(buf);
+	kfree(ax772b_data);
+	return ret;
+}
+
+static int ax88772b_external_phyinit(struct usbnet *dev) {
+	struct ax88772b_data *ax772b_data = (struct ax88772b_data *)dev->priv;
+	u16 phyid1, phyid2;
+
+	phyid1 = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, 0x02);
+	phyid2 = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, 0x03);
+	ax772b_data->ext_phy_oui = EXTPHY_ID_MASK_OUI(phyid1, phyid2);
+	ax772b_data->ext_phy_model = EXTPHY_ID_MASK_MODEL(phyid2);
+
+	if (ax772b_data->ext_phy_oui == EXTPHY_BROADCOM_OUI) { 
+		if(ax772b_data->ext_phy_model == EXTPHY_BCM89811_MODEL) {			
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x0, 0x8000);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x0028);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x0028);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x0028);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x0028);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x0028);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x0028);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1F, 0x0c00);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1E, 0x030A);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1F, 0x3440);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1E, 0x0166);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1F, 0x0020);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x012D);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1f, 0x9B52);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x012E);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1f, 0xA04D);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x0123);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1f, 0x00c0);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x0154);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1f, 0x81C4);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x0811);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1f, 0x0000);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x01D3);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1f, 0x0064);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x01C1);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1f, 0xA5F7);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x0028);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1f, 0x0400);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x001D);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1f, 0x3411);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x0820);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1f, 0x0401);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x002F);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1f, 0xF167);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1e, 0x0045);
+			ax88772b_mdio_write_le(dev->net, dev->mii.phy_id, 0x1f, 0x0500);
+			return 0;
+		}
+	}
+
+	return 0;
+}
+
+static void ax88772b_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+	struct ax88772b_data *ax772b_data = (struct ax88772b_data *)dev->priv;
+
+	if (ax772b_data) {
+
+		flush_workqueue(ax772b_data->ax_work);
+		destroy_workqueue(ax772b_data->ax_work);
+
+		/* stop MAC operation */
+		ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL,
+					AX_RX_CTL_STOP, 0, 0, NULL);
+
+		/* Power down PHY */
+		ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+					AX_SWRESET_IPPD, 0, 0, NULL);
+
+		kfree(ax772b_data);
+	}
+}
+
+static int
+ax88178_media_check(struct usbnet *dev, struct ax88178_data *ax178dataptr)
+{
+	int fullduplex, i = 0;
+	u16 tempshort = 0;
+	u16 media;
+	u16 advertise, lpa, result, stat1000, _lpa, _stat1000, delay = 5 * HZ;
+	unsigned long jtimeout;
+
+	jtimeout = jiffies + delay;
+	do {
+		_lpa = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_LPA);
+		_stat1000 = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id,
+						  MII_STAT1000);
+
+		lpa = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_LPA);
+		stat1000 = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id,
+						MII_STAT1000);
+
+		if (time_after(jiffies, jtimeout))
+			break;
+
+	} while ((_lpa != lpa) || (_stat1000 != stat1000) || i++ < 3);
+
+	advertise = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id,
+					 MII_ADVERTISE);
+	result = advertise & lpa;
+
+	if ((ax178dataptr->PhyMode == PHY_MODE_MARVELL) &&
+	    (ax178dataptr->LedMode == 1)) {
+		tempshort = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id,
+						 MARVELL_MANUAL_LED) & 0xfc0f;
+	}
+
+	fullduplex = 1;
+	if (stat1000 & LPA_1000FULL) {
+		media = MEDIUM_GIGA_MODE | MEDIUM_FULL_DUPLEX_MODE |
+			MEDIUM_ENABLE_125MHZ | MEDIUM_ENABLE_RECEIVE;
+		if ((ax178dataptr->PhyMode == PHY_MODE_MARVELL) &&
+		    (ax178dataptr->LedMode == 1))
+			tempshort |= 0x3e0;
+	} else if (result & LPA_100FULL) {
+		media = MEDIUM_FULL_DUPLEX_MODE | MEDIUM_ENABLE_RECEIVE |
+			MEDIUM_MII_100M_MODE;
+		if ((ax178dataptr->PhyMode == PHY_MODE_MARVELL) &&
+		    (ax178dataptr->LedMode == 1))
+			tempshort |= 0x3b0;
+	} else if (result & LPA_100HALF) {
+		fullduplex = 0;
+		media = MEDIUM_ENABLE_RECEIVE | MEDIUM_MII_100M_MODE;
+		if ((ax178dataptr->PhyMode == PHY_MODE_MARVELL) &&
+		    (ax178dataptr->LedMode == 1))
+			tempshort |= 0x3b0;
+	} else if (result & LPA_10FULL) {
+		media = MEDIUM_FULL_DUPLEX_MODE | MEDIUM_ENABLE_RECEIVE;
+		if ((ax178dataptr->PhyMode == PHY_MODE_MARVELL) &&
+		    (ax178dataptr->LedMode == 1))
+			tempshort |= 0x2f0;
+	} else {
+		media = MEDIUM_ENABLE_RECEIVE;
+		fullduplex = 0;
+		if ((ax178dataptr->PhyMode == PHY_MODE_MARVELL) &&
+		    (ax178dataptr->LedMode == 1))
+				tempshort |= 0x02f0;
+	}
+
+	if ((ax178dataptr->PhyMode == PHY_MODE_MARVELL) &&
+	    (ax178dataptr->LedMode == 1)) {
+		ax8817x_mdio_write_le(dev->net,
+			dev->mii.phy_id, MARVELL_MANUAL_LED, tempshort);
+	}
+
+	media |= 0x0004;
+	if (ax178dataptr->UseRgmii)
+		media |= 0x0008;
+	if (fullduplex) {
+		media |= 0x0020;  /* ebable tx flow control as default; */
+		media |= 0x0010;  /* ebable rx flow control as default; */
+	}
+
+	return media;
+}
+
+static void Vitess_8601_Init(struct usbnet *dev, int state)
+{
+	u16 reg;
+
+	switch (state) {
+	case 0:	/* tx, rx clock skew */
+		ax8817x_swmii_mdio_write_le(dev->net, dev->mii.phy_id, 31, 1);
+		ax8817x_swmii_mdio_write_le(dev->net, dev->mii.phy_id, 28, 0);
+		ax8817x_swmii_mdio_write_le(dev->net, dev->mii.phy_id, 31, 0);
+		break;
+
+	case 1:
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 31, 0x52B5);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 18, 0x009E);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 17, 0xDD39);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0x87AA);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0xA7B4);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 18,
+				ax8817x_swmii_mdio_read_le(dev->net,
+						dev->mii.phy_id, 18));
+
+		reg = (ax8817x_swmii_mdio_read_le(dev->net,
+				dev->mii.phy_id, 17) & ~0x003f) | 0x003c;
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 17, reg);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0x87B4);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0xa794);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 18,
+				ax8817x_swmii_mdio_read_le(dev->net,
+					dev->mii.phy_id, 18));
+
+		reg = (ax8817x_swmii_mdio_read_le(dev->net,
+				dev->mii.phy_id, 17) & ~0x003f) | 0x003e;
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 17, reg);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0x8794);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 18, 0x00f7);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 17, 0xbe36);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0x879e);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0xa7a0);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 18,
+				ax8817x_swmii_mdio_read_le(dev->net,
+						dev->mii.phy_id, 18));
+
+		reg = (ax8817x_swmii_mdio_read_le(dev->net,
+				dev->mii.phy_id, 17) & ~0x003f) | 0x0034;
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 17, reg);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0x87a0);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 18, 0x003c);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 17, 0xf3cf);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0x87a2);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 18, 0x003c);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 17, 0xf3cf);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0x87a4);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 18, 0x003c);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 17, 0xd287);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0x87a6);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0xa7a8);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 18,
+				ax8817x_swmii_mdio_read_le(dev->net,
+						dev->mii.phy_id, 18));
+
+		reg = (ax8817x_swmii_mdio_read_le(dev->net,
+				dev->mii.phy_id, 17) & ~0x0fff) | 0x0125;
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 17, reg);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0x87a8);
+
+		/* Enable Smart Pre-emphasis */
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0xa7fa);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 18,
+				ax8817x_swmii_mdio_read_le(dev->net,
+						dev->mii.phy_id, 18));
+
+		reg = (ax8817x_swmii_mdio_read_le(dev->net,
+				dev->mii.phy_id, 17) & ~0x0008) | 0x0008;
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 17, reg);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 16, 0x87fa);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 31, 0);
+
+		break;
+	}
+}
+
+static void
+marvell_88E1510_magic_init(struct usbnet *dev)
+{
+	ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 22, 0xff);
+	ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 17, 0x214b);
+	ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 16, 0x2144);
+	ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 17, 0x0c28);
+	ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 16, 0x2146);
+	ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 17, 0xb233);
+	ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 16, 0x214d);
+	ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 17, 0xcc0c);
+	ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 16, 0x2159);
+	ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 22, 0x00fb);
+	ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 7, 0xc00d);
+	ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 22, 0);
+}
+
+static int
+ax88178_phy_init(struct usbnet *dev, struct ax88178_data *ax178dataptr)
+{
+	int i;
+	u16 phyanar, phyauxctrl, phyctrl, tempshort, phyid1;
+	u16 phyreg = 0;
+
+	/* Disable MII operation of AX88178 Hardware */
+	ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0x0000, 0, 0, NULL);
+
+
+	/* Read SROM - MiiPhy Address (ID) */
+	ax8817x_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, &dev->mii.phy_id);
+	le32_to_cpus(&dev->mii.phy_id);
+
+	/* Initialize MII structure */
+	dev->mii.phy_id >>= 8;
+	dev->mii.phy_id &= PHY_ID_MASK;
+	dev->mii.dev = dev->net;
+	dev->mii.mdio_read = ax8817x_mdio_read_le;
+	dev->mii.mdio_write = ax8817x_mdio_write_le;
+	dev->mii.phy_id_mask = 0x3f;
+	dev->mii.reg_num_mask = 0x1f;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 11)
+	dev->mii.supports_gmii = 1;
+#endif
+
+	if (ax178dataptr->PhyMode == PHY_MODE_MAC_TO_MAC_GMII) {
+		ax178dataptr->UseRgmii = 0;
+		ax178dataptr->MediaLink = MEDIUM_GIGA_MODE |
+					  MEDIUM_FULL_DUPLEX_MODE |
+					  MEDIUM_ENABLE_125MHZ |
+					  MEDIUM_ENABLE_RECEIVE |
+					  MEDIUM_ENABLE_RX_FLOWCTRL |
+					  MEDIUM_ENABLE_TX_FLOWCTRL;
+		goto SKIPPHYSETTING;
+	}
+
+	/* test read phy register 2 */
+	if (!ax178dataptr->UseGpio0) {
+		i = 1000;
+		while (i--) {
+			phyid1 = ax8817x_swmii_mdio_read_le(dev->net,
+						dev->mii.phy_id, GMII_PHY_OUI);
+			if ((phyid1 == 0x000f) || (phyid1 == 0x0141) ||
+			    (phyid1 == 0x0282) || (phyid1 == 0x004d) ||
+			    (phyid1 == 0x0243) || (phyid1 == 0x001C) ||
+			    (phyid1 == 0x0007))
+				break;
+			msleep(5);
+		}
+		if (i < 0)
+			return -EIO;
+	}
+
+	ax178dataptr->UseRgmii = 0;
+	if (ax178dataptr->PhyMode == PHY_MODE_MARVELL) {
+		phyreg = ax8817x_swmii_mdio_read_le(dev->net,
+					dev->mii.phy_id, 27);
+		if (!(phyreg & 4) && !(ax178dataptr->LedMode & 0x10)) {
+			ax178dataptr->UseRgmii = 1;
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 20, 0x82);
+			ax178dataptr->MediaLink |= MEDIUM_ENABLE_125MHZ;
+		} else if (ax178dataptr->LedMode & 0x10) {
+
+			ax178dataptr->UseRgmii = 1;
+			ax178dataptr->MediaLink |= MEDIUM_ENABLE_125MHZ;
+			marvell_88E1510_magic_init(dev);
+
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 22, 2);
+
+			phyreg = ax8817x_swmii_mdio_read_le(dev->net,
+					dev->mii.phy_id, 21);
+
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 21, phyreg | 0x30);
+
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 22, 0);
+		}
+	} else if ((ax178dataptr->PhyMode == PHY_MODE_AGERE_V0) ||
+		 (ax178dataptr->PhyMode == PHY_MODE_AGERE_V0_GMII)) {
+		if (ax178dataptr->PhyMode == PHY_MODE_AGERE_V0) {
+			ax178dataptr->UseRgmii = 1;
+			ax178dataptr->MediaLink |= MEDIUM_ENABLE_125MHZ;
+		}
+	} else if (ax178dataptr->PhyMode == PHY_MODE_CICADA_V1) {
+		/* not Cameo */
+		if (!ax178dataptr->UseGpio0 || ax178dataptr->LedMode) {
+			ax178dataptr->UseRgmii = 1;
+			ax178dataptr->MediaLink |= MEDIUM_ENABLE_125MHZ;
+		}
+
+		for (i = 0; i < (sizeof(CICADA_FAMILY_HWINIT) /
+				 sizeof(CICADA_FAMILY_HWINIT[0])); i++) {
+			ax8817x_swmii_mdio_write_le(dev->net,
+						    dev->mii.phy_id,
+					CICADA_FAMILY_HWINIT[i].offset,
+					CICADA_FAMILY_HWINIT[i].value);
+		}
+
+	} else if (ax178dataptr->PhyMode == PHY_MODE_CICADA_V2) {
+		/* not Cameo */
+		if (!ax178dataptr->UseGpio0 || ax178dataptr->LedMode) {
+			ax178dataptr->UseRgmii = 1;
+			ax178dataptr->MediaLink |= MEDIUM_ENABLE_125MHZ;
+		}
+
+		for (i = 0; i < (sizeof(CICADA_V2_HWINIT) /
+				 sizeof(CICADA_V2_HWINIT[0])); i++) {
+			ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, CICADA_V2_HWINIT[i].offset,
+				CICADA_V2_HWINIT[i].value);
+		}
+	} else if (ax178dataptr->PhyMode == PHY_MODE_CICADA_V2_ASIX) {
+		/* not Cameo */
+		if (!ax178dataptr->UseGpio0 || ax178dataptr->LedMode) {
+			ax178dataptr->UseRgmii = 1;
+			ax178dataptr->MediaLink |= MEDIUM_ENABLE_125MHZ;
+		}
+
+		for (i = 0; i < (sizeof(CICADA_V2_HWINIT) /
+				 sizeof(CICADA_V2_HWINIT[0])); i++) {
+			ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, CICADA_V2_HWINIT[i].offset,
+				CICADA_V2_HWINIT[i].value);
+		}
+	} else if (ax178dataptr->PhyMode == PHY_MODE_RTL8211CL) {
+		ax178dataptr->UseRgmii = 1;
+		ax178dataptr->MediaLink |= MEDIUM_ENABLE_125MHZ;
+	} else if (ax178dataptr->PhyMode == PHY_MODE_RTL8211BN) {
+		ax178dataptr->UseRgmii = 1;
+		ax178dataptr->MediaLink |= MEDIUM_ENABLE_125MHZ;
+	} else if (ax178dataptr->PhyMode == PHY_MODE_RTL8251CL) {
+		ax178dataptr->UseRgmii = 1;
+		ax178dataptr->MediaLink |= MEDIUM_ENABLE_125MHZ;
+	} else if (ax178dataptr->PhyMode == PHY_MODE_VSC8601) {
+		ax178dataptr->UseRgmii = 1;
+		ax178dataptr->MediaLink |= MEDIUM_ENABLE_125MHZ;
+		/* Vitess_8601_Init (dev, 0); */
+	}
+
+	if (ax178dataptr->PhyMode != PHY_MODE_ATTANSIC_V0) {
+		/* software reset */
+		ax8817x_swmii_mdio_write_le(
+			dev->net, dev->mii.phy_id, GMII_PHY_CONTROL,
+			ax8817x_swmii_mdio_read_le(
+				dev->net, dev->mii.phy_id, GMII_PHY_CONTROL)
+				| GMII_CONTROL_RESET);
+		msleep(1);
+	}
+
+	if ((ax178dataptr->PhyMode == PHY_MODE_AGERE_V0) ||
+	    (ax178dataptr->PhyMode == PHY_MODE_AGERE_V0_GMII)) {
+		if (ax178dataptr->PhyMode == PHY_MODE_AGERE_V0) {
+			i = 1000;
+			while (i--) {
+				ax8817x_swmii_mdio_write_le(dev->net,
+						dev->mii.phy_id, 21, 0x1001);
+
+				phyreg = ax8817x_swmii_mdio_read_le(dev->net,
+						dev->mii.phy_id, 21);
+				if ((phyreg & 0xf00f) == 0x1001)
+					break;
+			}
+			if (i < 0)
+				return -EIO;
+		}
+
+		if (ax178dataptr->LedMode == 4) {
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 28, 0x7417);
+		} else if (ax178dataptr->LedMode == 9) {
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 28, 0x7a10);
+		} else if (ax178dataptr->LedMode == 10) {
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 28, 0x7a13);
+		}
+
+		for (i = 0; i < (sizeof(AGERE_FAMILY_HWINIT) /
+				 sizeof(AGERE_FAMILY_HWINIT[0])); i++) {
+			ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, AGERE_FAMILY_HWINIT[i].offset,
+				AGERE_FAMILY_HWINIT[i].value);
+		}
+	} else if (ax178dataptr->PhyMode == PHY_MODE_RTL8211CL) {
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 0x1f, 0x0005);
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 0x0c, 0);
+
+		ax8817x_swmii_mdio_write_le(dev->net, dev->mii.phy_id, 0x01,
+					(ax8817x_swmii_mdio_read_le(dev->net,
+					dev->mii.phy_id, 0x01) | 0x0080));
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, 0x1f, 0);
+
+		if (ax178dataptr->LedMode == 12) {
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 0x1f, 0x0002);
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 0x1a, 0x00cb);
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 0x1f, 0);
+		}
+	} else if (ax178dataptr->PhyMode == PHY_MODE_VSC8601) {
+		Vitess_8601_Init(dev, 1);
+	}
+
+	/* read phy register 0 */
+	phyctrl = ax8817x_swmii_mdio_read_le(dev->net,
+				dev->mii.phy_id, GMII_PHY_CONTROL);
+	tempshort = phyctrl;
+	phyctrl &= ~(GMII_CONTROL_POWER_DOWN | GMII_CONTROL_ISOLATE);
+	if (phyctrl != tempshort) {
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, GMII_PHY_CONTROL, phyctrl);
+	}
+
+	/* LED */
+	if (ax178dataptr->PhyMode == PHY_MODE_MARVELL) {
+		if (ax178dataptr->LedMode == 1)	{
+
+			phyreg = (ax8817x_swmii_mdio_read_le(dev->net,
+				dev->mii.phy_id, 24) & 0xf8ff) | (1 + 0x100);
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 24, phyreg);
+			phyreg = ax8817x_swmii_mdio_read_le(dev->net,
+					dev->mii.phy_id, 25) & 0xfc0f;
+		} else if (ax178dataptr->LedMode == 2) {
+
+			phyreg = (ax8817x_swmii_mdio_read_le(dev->net,
+					dev->mii.phy_id, 24) & 0xf886) |
+					(1 + 0x10 + 0x300);
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 24, phyreg);
+		} else if (ax178dataptr->LedMode == 5) {
+
+			phyreg = (ax8817x_swmii_mdio_read_le(dev->net,
+					dev->mii.phy_id, 24) & 0xf8be) |
+					(1 + 0x40 + 0x300);
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 24, phyreg);
+		} else if (ax178dataptr->LedMode == 7) {
+
+			phyreg = (ax8817x_swmii_mdio_read_le(dev->net,
+						dev->mii.phy_id, 24) & 0xf8ff) |
+						(1 + 0x100);
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 24, phyreg);
+
+		} else if (ax178dataptr->LedMode == 8) {
+
+			phyreg = (ax8817x_swmii_mdio_read_le(dev->net,
+					dev->mii.phy_id, 24) & 0xf8be) |
+					(1 + 0x40 + 0x100);
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 24, phyreg);
+
+		} else if (ax178dataptr->LedMode == 11) {
+
+			phyreg = ax8817x_swmii_mdio_read_le(dev->net,
+					dev->mii.phy_id, 24) & 0x4106;
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 24, phyreg);
+		} else if (ax178dataptr->LedMode == 0x10) {
+			/* MARVEL 88e1510 use default led setting */
+		}
+
+	} else if ((ax178dataptr->PhyMode == PHY_MODE_CICADA_V1) ||
+		   (ax178dataptr->PhyMode == PHY_MODE_CICADA_V2) ||
+		   (ax178dataptr->PhyMode == PHY_MODE_CICADA_V2_ASIX)) {
+
+		if (ax178dataptr->LedMode == 3) {
+
+			phyreg = (ax8817x_swmii_mdio_read_le(dev->net,
+					dev->mii.phy_id, 27) & 0xFCFF) | 0x0100;
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 27, phyreg);
+		}
+
+	}
+
+	if (ax178dataptr->PhyMode == PHY_MODE_MARVELL) {
+		if (ax178dataptr->LedMode == 1)
+			phyreg |= 0x3f0;
+	}
+
+	phyanar = 1 | (GMII_ANAR_PAUSE | GMII_ANAR_100TXFD | GMII_ANAR_100TX |
+		       GMII_ANAR_10TFD | GMII_ANAR_10T | GMII_ANAR_ASYM_PAUSE);
+
+	phyauxctrl = GMII_1000_AUX_CTRL_FD_CAPABLE;
+
+	ax8817x_swmii_mdio_write_le(dev->net,
+			dev->mii.phy_id, GMII_PHY_ANAR, phyanar);
+	ax8817x_swmii_mdio_write_le(dev->net,
+			dev->mii.phy_id, GMII_PHY_1000BT_CONTROL, phyauxctrl);
+
+	if (ax178dataptr->PhyMode == PHY_MODE_VSC8601) {
+		ax8817x_swmii_mdio_write_le(dev->net, dev->mii.phy_id,
+					    31, 0x52B5);
+		ax8817x_swmii_mdio_write_le(dev->net, dev->mii.phy_id,
+					    16, 0xA7F8);
+
+		tempshort = ax8817x_swmii_mdio_read_le(dev->net,
+					dev->mii.phy_id, 17) & (~0x0018);
+
+		ax8817x_swmii_mdio_write_le(dev->net, dev->mii.phy_id,
+					    17, tempshort);
+
+		tempshort = ax8817x_swmii_mdio_read_le(dev->net,
+						       dev->mii.phy_id, 18);
+
+		ax8817x_swmii_mdio_write_le(dev->net, dev->mii.phy_id, 18,
+					    tempshort);
+
+		ax8817x_swmii_mdio_write_le(dev->net, dev->mii.phy_id,
+					    16, 0x87F8);
+		ax8817x_swmii_mdio_write_le(dev->net, dev->mii.phy_id,
+					    31, 0);
+	}
+
+	if (ax178dataptr->PhyMode == PHY_MODE_ATTANSIC_V0) {
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, GMII_PHY_CONTROL, 0x9000);
+
+	} else {
+		phyctrl &= ~GMII_CONTROL_LOOPBACK;
+		phyctrl |= (GMII_CONTROL_ENABLE_AUTO | GMII_CONTROL_START_AUTO);
+
+		ax8817x_swmii_mdio_write_le(dev->net,
+				dev->mii.phy_id, GMII_PHY_CONTROL, phyctrl);
+	}
+
+	if (ax178dataptr->PhyMode == PHY_MODE_MARVELL) {
+		if (ax178dataptr->LedMode == 1)
+			ax8817x_swmii_mdio_write_le(dev->net,
+					dev->mii.phy_id, 25, phyreg);
+	}
+
+SKIPPHYSETTING:
+
+	ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE,
+			ax178dataptr->MediaLink, 0, 0, NULL);
+
+	ax8817x_write_cmd(dev, AX_CMD_WRITE_IPG0,
+			AX88772_IPG0_DEFAULT | (AX88772_IPG1_DEFAULT << 8),
+			AX88772_IPG2_DEFAULT, 0, NULL);
+
+	msleep(1);
+
+	ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, NULL);
+
+	return 0;
+}
+
+static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	int ret;
+	void *buf;
+	struct ax8817x_data *data = (struct ax8817x_data *)&dev->data;
+	struct ax88178_data *ax178dataptr = NULL;
+
+	axusbnet_get_endpoints(dev, intf);
+
+	buf = kmalloc(6, GFP_KERNEL);
+	if (!buf) {
+		deverr(dev, "Cannot allocate memory for buffer");
+		return -ENOMEM;
+	}
+
+	/* allocate 178 data */
+	ax178dataptr = kmalloc(sizeof(*ax178dataptr), GFP_KERNEL);
+	if (!ax178dataptr) {
+		deverr(dev, "Cannot allocate memory for AX88178 data");
+		ret = -ENOMEM;
+		goto error_out;
+	}
+	memset(ax178dataptr, 0, sizeof(struct ax88178_data));
+	dev->priv = ax178dataptr;
+	/* end of allocate 178 data */
+
+	/* Get the EEPROM data*/
+	ret = ax8817x_read_cmd(dev, AX_CMD_READ_EEPROM, 0x0017, 0, 2,
+			       (void *)(&ax178dataptr->EepromData));
+	if (ret < 0) {
+		deverr(dev, "read SROM address 17h failed: %d", ret);
+		goto error_out;
+	}
+	le16_to_cpus(&ax178dataptr->EepromData);
+	/* End of get EEPROM data */
+
+	if (ax178dataptr->EepromData == 0xffff) {
+		ax178dataptr->PhyMode  = PHY_MODE_MARVELL;
+		ax178dataptr->LedMode  = 0;
+		ax178dataptr->UseGpio0 = 1; /* True */
+	} else {
+		ax178dataptr->PhyMode = (u8)(ax178dataptr->EepromData &
+					     EEPROMMASK);
+		ax178dataptr->LedMode = (u8)(ax178dataptr->EepromData >> 8);
+
+		/* for buffalo new (use gpio2) */
+		if (ax178dataptr->LedMode == 6)
+			ax178dataptr->LedMode = 1;
+		else if (ax178dataptr->LedMode == 1)
+			ax178dataptr->BuffaloOld = 1;
+
+
+		if (ax178dataptr->EepromData & 0x80)
+			ax178dataptr->UseGpio0 = 0; /* MARVEL se and other */
+		else
+			ax178dataptr->UseGpio0 = 1; /* cameo */
+	}
+
+	if (ax178dataptr->UseGpio0) {
+
+		if (ax178dataptr->PhyMode == PHY_MODE_MARVELL) {
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+					AXGPIOS_GPO0EN | AXGPIOS_RSE,
+					0, 0, NULL);
+			if (ret < 0) {
+				deverr(dev, "write GPIO failed: %d", ret);
+				goto error_out;
+			}
+
+			msleep(25);
+
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+					(AXGPIOS_GPO2 | AXGPIOS_GPO2EN |
+					 AXGPIOS_GPO0EN), 0, 0, NULL);
+			if (ret < 0) {
+				deverr(dev, "write GPIO failed: %d", ret);
+				goto error_out;
+			}
+
+			msleep(15);
+
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+					AXGPIOS_GPO2EN | AXGPIOS_GPO0EN,
+					0, 0, NULL);
+			if (ret < 0) {
+				deverr(dev, "write GPIO failed: %d", ret);
+				goto error_out;
+			}
+
+			msleep(245);
+
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+					(AXGPIOS_GPO2 | AXGPIOS_GPO2EN |
+					 AXGPIOS_GPO0EN), 0, 0, NULL);
+			if (ret < 0) {
+				deverr(dev, "write GPIO failed: %d", ret);
+				goto error_out;
+			}
+
+		} else { /* vitesse */
+
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+					(AXGPIOS_RSE | AXGPIOS_GPO0EN |
+					 AXGPIOS_GPO0), 0, 0, NULL);
+			if (ret < 0) {
+				deverr(dev, "write GPIO failed: %d", ret);
+				goto error_out;
+			}
+
+			msleep(25);
+
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+					(AXGPIOS_GPO0EN | AXGPIOS_GPO0 |
+					 AXGPIOS_GPO2EN | AXGPIOS_GPO2),
+					0, 0, NULL);
+			if (ret < 0) {
+				deverr(dev, "write GPIO failed: %d", ret);
+				goto error_out;
+			}
+
+			msleep(25);
+
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+					(AXGPIOS_GPO0EN | AXGPIOS_GPO0 |
+					 AXGPIOS_GPO2EN), 0, 0, NULL);
+			if (ret < 0) {
+				deverr(dev, "write GPIO failed: %d", ret);
+				goto error_out;
+			}
+
+			msleep(245);
+
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+						(AXGPIOS_GPO0EN | AXGPIOS_GPO0 |
+						AXGPIOS_GPO2EN | AXGPIOS_GPO2),
+						0, 0, NULL);
+			if (ret < 0) {
+				deverr(dev, "write GPIO failed: %d", ret);
+				goto error_out;
+			}
+		}
+	} else {	/* use gpio1 */
+		ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+					(AXGPIOS_GPO1 | AXGPIOS_GPO1EN |
+					AXGPIOS_RSE), 0, 0, NULL);
+		if (ret < 0) {
+			deverr(dev, "write GPIO failed: %d", ret);
+			goto error_out;
+		}
+
+		if (ax178dataptr->BuffaloOld) {
+			msleep(350);
+
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+						AXGPIOS_GPO1EN, 0, 0, NULL);
+			if (ret < 0) {
+				deverr(dev, "write GPIO failed: %d", ret);
+				goto error_out;
+			}
+
+			msleep(350);
+
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+						AXGPIOS_GPO1EN | AXGPIOS_GPO1,
+						0, 0, NULL);
+			if (ret < 0) {
+				deverr(dev, "write GPIO failed: %d", ret);
+				goto error_out;
+			}
+		} else {
+			msleep(25);
+
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+						(AXGPIOS_GPO1EN | AXGPIOS_GPO1 |
+						AXGPIOS_GPO2EN | AXGPIOS_GPO2),
+						0, 0, NULL);
+			if (ret < 0) {
+				deverr(dev, "write GPIO failed: %d", ret);
+				goto error_out;
+			}
+
+			msleep(25);
+
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+						(AXGPIOS_GPO1EN | AXGPIOS_GPO1 |
+						AXGPIOS_GPO2EN), 0, 0, NULL);
+			if (ret < 0) {
+				deverr(dev, "write GPIO failed: %d", ret);
+				goto error_out;
+			}
+
+			msleep(245);
+
+			ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+						(AXGPIOS_GPO1EN | AXGPIOS_GPO1 |
+						AXGPIOS_GPO2EN | AXGPIOS_GPO2),
+						0, 0, NULL);
+			if (ret < 0) {
+				deverr(dev, "write GPIO failed: %d", ret);
+				goto error_out;
+			}
+		}
+	}
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_SW_PHY_SELECT, 0, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Select PHY failed: %d", ret);
+		goto error_out;
+	}
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPPD |
+				AX_SWRESET_PRL, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Issue sw reset failed: %d", ret);
+		goto error_out;
+	}
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, 0, 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "Issue rx ctrl failed: %d", ret);
+		goto error_out;
+	}
+
+	/* Get the MAC address */
+	memset(buf, 0, ETH_ALEN);
+	ax8817x_get_mac(dev, buf);
+	if (ret < 0)
+		goto error_out;
+	/* End of get MAC address */
+
+	ret = ax88178_phy_init(dev, ax178dataptr);
+	if (ret < 0)
+		goto error_out;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
+	dev->net->do_ioctl = ax8817x_ioctl;
+	dev->net->set_multicast_list = ax88178_set_multicast;
+	dev->net->set_mac_address = ax8817x_set_mac_addr;
+#else
+	dev->net->netdev_ops = &ax88178_netdev_ops;
+#endif
+	dev->net->ethtool_ops = &ax8817x_ethtool_ops;
+
+	/* Register suspend and resume functions */
+	data->suspend = ax88772_suspend;
+	data->resume = ax88772_resume;
+
+	if (dev->driver_info->flags & FLAG_FRAMING_AX)
+		dev->rx_urb_size = 16384;
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, (AX_RX_CTL_MFB |
+				AX_RX_CTL_START | AX_RX_CTL_AB), 0, 0, NULL);
+	if (ret < 0) {
+		deverr(dev, "write RX ctrl reg failed: %d", ret);
+		goto error_out;
+	}
+
+	kfree(buf);
+	printk(version);
+	return ret;
+
+error_out:
+	kfree(ax178dataptr);
+	kfree(buf);
+	return ret;
+}
+
+static void ax88178_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+	struct ax88178_data *ax178dataptr = (struct ax88178_data *)dev->priv;
+
+	if (ax178dataptr) {
+
+		/* stop MAC operation */
+		ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL,
+					AX_RX_CTL_STOP, 0, 0, NULL);
+
+		kfree(ax178dataptr);
+	}
+}
+
+static int ax88772_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+	u8  *head;
+	u32  header;
+	char *packet;
+	struct sk_buff *ax_skb = NULL;
+	u16 size;
+
+	head = (u8 *) skb->data;
+	memcpy(&header, head, sizeof(header));
+	le32_to_cpus(&header);
+	packet = head + sizeof(header);
+
+	skb_pull(skb, 4);
+
+	while (skb->len > 0) {
+		if ((short)(header & 0x00007ff) !=
+		    ~((short)(((header & 0xffff0000) | 0xf8000000) >> 16))) {
+			deverr(dev, "header length data is error 0x%08x, %d\n",
+				header, skb->len);
+		}
+		/* get the packet length */
+		size = (u16) (header & 0x00007ff);
+
+		if ((skb->len) - ((size + 1) & 0xfffe) == 0) {
+
+			/* Make sure ip header is aligned on 32-bit boundary */
+			if (!((unsigned long)skb->data & 0x02)) {
+				memmove(skb->data - 2, skb->data, size);
+				skb->data -= 2;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
+				skb->tail = skb->data + size;
+#else
+				skb_set_tail_pointer(skb, size);
+#endif
+			}
+			skb->truesize = size + sizeof(struct sk_buff);
+			skb->len = size;
+
+			return 2;
+		}
+
+		if (size > ETH_FRAME_LEN) {
+			deverr(dev, "invalid rx length %d", size);
+			return 0;
+		}
+#ifndef RX_SKB_COPY
+		ax_skb = skb_clone(skb, GFP_ATOMIC);
+#else
+		ax_skb = alloc_skb(size + NET_IP_ALIGN, GFP_ATOMIC);	
+		skb_reserve(ax_skb, NET_IP_ALIGN);
+#endif
+		if (ax_skb) {
+#ifndef RX_SKB_COPY
+			/* Make sure ip header is aligned on 32-bit boundary */
+			if (!((unsigned long)packet & 0x02)) {
+				memmove(packet - 2, packet, size);
+				packet -= 2;
+			}
+			ax_skb->data = packet;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
+			ax_skb->tail = packet + size;
+#else
+			skb_set_tail_pointer(ax_skb, size);
+#endif
+
+#else
+			skb_put(ax_skb, size);
+			memcpy(ax_skb->data, packet , size);
+#endif
+			ax_skb->truesize = size + sizeof(struct sk_buff);
+			axusbnet_skb_return(dev, ax_skb);
+
+		} else {
+			return 0;
+		}
+
+		skb_pull(skb, (size + 1) & 0xfffe);
+
+		if (skb->len == 0)
+			break;
+
+		head = (u8 *) skb->data;
+		memcpy(&header, head, sizeof(header));
+		le32_to_cpus(&header);
+		packet = head + sizeof(header);
+		skb_pull(skb, 4);
+	}
+
+	if (skb->len < 0) {
+		deverr(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,
+					gfp_t flags)
+{
+	int padlen = ((skb->len + 4) % 512) ? 0 : 4;
+	u32 packet_len;
+	u32 padbytes = 0xffff0000;
+
+#if (!AX_FORCE_BUFF_ALIGN)
+	int headroom = skb_headroom(skb);
+	int tailroom = skb_tailroom(skb);
+
+	if ((!skb_cloned(skb))
+	    && ((headroom + tailroom) >= (4 + padlen))) {
+		if ((headroom < 4) || (tailroom < padlen)) {
+			skb->data = memmove(skb->head + 4, skb->data, skb->len);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
+		skb->tail = skb->data + skb->len;
+#else
+		skb_set_tail_pointer(skb, skb->len);
+#endif
+		}
+	} else
+#endif
+	{
+		struct sk_buff *skb2;
+		skb2 = skb_copy_expand(skb, 4, padlen, flags);
+		dev_kfree_skb_any(skb);
+		skb = skb2;
+		if (!skb)
+			return NULL;
+	}
+
+	skb_push(skb, 4);
+	packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4);
+	cpu_to_le32s(&packet_len);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
+	memcpy(skb->data, &packet_len, sizeof(packet_len));
+#else
+	skb_copy_to_linear_data(skb, &packet_len, sizeof(packet_len));
+#endif
+
+	if ((skb->len % 512) == 0) {
+		cpu_to_le32s(&padbytes);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
+		memcpy(skb->tail, &padbytes, sizeof(padbytes));
+#else
+		memcpy(skb_tail_pointer(skb), &padbytes, sizeof(padbytes));
+#endif
+		skb_put(skb, sizeof(padbytes));
+	}
+	return skb;
+}
+
+static void
+ax88772b_rx_checksum(struct sk_buff *skb, struct ax88772b_rx_header *rx_hdr)
+{
+	skb->ip_summed = CHECKSUM_NONE;
+
+	/* checksum error bit is set */
+	if (rx_hdr->l3_csum_err || rx_hdr->l4_csum_err)
+		return;
+
+	/* It must be a TCP or UDP packet with a valid checksum */
+	if ((rx_hdr->l4_type == AX_RXHDR_L4_TYPE_TCP) ||
+	    (rx_hdr->l4_type == AX_RXHDR_L4_TYPE_UDP)) {
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+	}
+}
+
+static int ax88772b_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+	struct ax88772b_rx_header rx_hdr;
+	struct sk_buff *ax_skb = NULL;
+	struct ax88772b_data *ax772b_data = (struct ax88772b_data *)dev->priv;
+
+	while (skb->len > 0) {
+
+		le16_to_cpus((u16 *)skb->data);
+		le16_to_cpus(((u16 *)skb->data) + 1);
+
+		memcpy(&rx_hdr, skb->data, sizeof(struct ax88772b_rx_header));
+
+		if ((short)rx_hdr.len != (~((short)rx_hdr.len_bar) & 0x7FF))
+			return 0;
+
+		if (rx_hdr.len > (ETH_FRAME_LEN + 4)) {
+			deverr(dev, "invalid rx length %d", rx_hdr.len);
+			return 0;
+		}
+
+		if (skb->len - ((rx_hdr.len +
+				 sizeof(struct ax88772b_rx_header) + 3) &
+				 0xfffc) == 0) {
+			skb_pull(skb, sizeof(struct ax88772b_rx_header));
+			skb->len = rx_hdr.len;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
+			skb->tail = skb->data + rx_hdr.len;
+#else
+			skb_set_tail_pointer(skb, rx_hdr.len);
+#endif
+			skb->truesize = rx_hdr.len + sizeof(struct sk_buff);
+
+			if (ax772b_data->checksum & AX_RX_CHECKSUM)
+				ax88772b_rx_checksum(skb, &rx_hdr);
+
+			return 2;
+		}
+#ifndef RX_SKB_COPY
+		ax_skb = skb_clone(skb, GFP_ATOMIC);
+#else
+		ax_skb = alloc_skb(rx_hdr.len + NET_IP_ALIGN, GFP_ATOMIC);
+		skb_reserve(ax_skb, NET_IP_ALIGN);	
+#endif
+		if (ax_skb) {
+#ifndef RX_SKB_COPY
+			ax_skb->len = rx_hdr.len;
+			ax_skb->data = skb->data +
+				       sizeof(struct ax88772b_rx_header);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
+			ax_skb->tail = ax_skb->data + rx_hdr.len;
+#else
+			skb_set_tail_pointer(ax_skb, rx_hdr.len);
+#endif
+
+#else
+			skb_put(ax_skb, rx_hdr.len);
+			memcpy(ax_skb->data, skb->data + sizeof(struct ax88772b_rx_header), rx_hdr.len); 
+#endif
+
+			ax_skb->truesize = rx_hdr.len + sizeof(struct sk_buff);
+
+			if (ax772b_data->checksum & AX_RX_CHECKSUM)
+				ax88772b_rx_checksum(ax_skb, &rx_hdr);
+
+			axusbnet_skb_return(dev, ax_skb);
+
+		} else {
+			return 0;
+		}
+
+		skb_pull(skb, ((rx_hdr.len +
+				sizeof(struct ax88772b_rx_header) + 3)
+				& 0xfffc));
+	}
+
+	if (skb->len < 0) {
+		deverr(dev, "invalid rx length %d", skb->len);
+		return 0;
+	}
+	return 1;
+}
+
+static struct sk_buff *
+ax88772b_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
+{
+	int padlen = ((skb->len + 4) % 512) ? 0 : 4;
+	u32 packet_len;
+	u32 padbytes = 0xffff0000;
+
+#if (!AX_FORCE_BUFF_ALIGN)
+	int headroom = skb_headroom(skb);
+	int tailroom = skb_tailroom(skb);
+
+	if ((!skb_cloned(skb))
+	    && ((headroom + tailroom) >= (4 + padlen))) {
+		if ((headroom < 4) || (tailroom < padlen)) {
+			skb->data = memmove(skb->head + 4, skb->data, skb->len);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
+			skb->tail = skb->data + skb->len;
+#else
+			skb_set_tail_pointer(skb, skb->len);
+#endif
+		}
+	} else
+#endif
+	{
+		struct sk_buff *skb2;
+		skb2 = skb_copy_expand(skb, 4, padlen, flags);
+		dev_kfree_skb_any(skb);
+		skb = skb2;
+		if (!skb)
+			return NULL;
+	}
+
+	skb_push(skb, 4);
+	packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4);
+
+	cpu_to_le32s(&packet_len);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
+	memcpy(skb->data, &packet_len, sizeof(packet_len));
+#else
+	skb_copy_to_linear_data(skb, &packet_len, sizeof(packet_len));
+#endif
+
+	if ((skb->len % 512) == 0) {
+		cpu_to_le32s(&padbytes);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
+		memcpy(skb->tail, &padbytes, sizeof(padbytes));
+#else
+		memcpy(skb_tail_pointer(skb), &padbytes, sizeof(padbytes));
+#endif
+		skb_put(skb, sizeof(padbytes));
+	}
+
+	return skb;
+}
+
+static const u8 chkcntsel[6][3] = {
+	{12, 23, 31},
+	{12, 31, 23},
+	{23, 31, 12},
+	{23, 12, 31},
+	{31, 12, 23},
+	{31, 23, 12}
+};
+
+static void ax88772_save_bmcr_anar(struct usbnet *dev)
+{
+	struct ax88772_data *ax772_data = (struct ax88772_data *)dev->priv;
+
+	if (ax772_data) {
+		/* Preserve BMCR for restoring */
+		ax772_data->presvd_phy_bmcr =
+			ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_BMCR);
+
+		/* Preserve Advertisement control reg for restoring */
+		ax772_data->presvd_phy_advertise =
+			ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_ADVERTISE);
+	}
+}
+
+static void ax88772_restore_bmcr_anar(struct usbnet *dev)
+{
+	struct ax88772_data *ax772_data = (struct ax88772_data *)dev->priv;
+
+	if (ax772_data && ax772_data->presvd_phy_advertise && ax772_data->presvd_phy_bmcr) {
+		/* Restore Advertisement control reg */
+		ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+				      ax772_data->presvd_phy_advertise);
+		/* Restore BMCR */
+		ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_BMCR,
+				      ax772_data->presvd_phy_bmcr);
+		ax772_data->presvd_phy_advertise = 0;
+		ax772_data->presvd_phy_bmcr = 0;
+	}
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+static void ax88772_link_reset(void *data)
+{
+	struct usbnet *dev = (struct usbnet *)data;
+	struct ax88772_data *ax772_data = (struct ax88772_data *)dev->priv;
+#else
+static void ax88772_link_reset(struct work_struct *work)
+{
+	struct ax88772_data *ax772_data = container_of(work,
+						       struct ax88772_data,
+						       check_link);
+	struct usbnet *dev = ax772_data->dev;
+#endif
+	if (ax772_data->Event == AX_SET_RX_CFG) {
+		u16 bmcr;
+		u16 mode;
+
+		ax772_data->Event = AX_NOP;
+
+		mode = AX88772_MEDIUM_DEFAULT;
+
+		bmcr = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id,
+					    MII_BMCR);
+		if (!(bmcr & BMCR_FULLDPLX))
+			mode &= ~AX88772_MEDIUM_FULL_DUPLEX;
+		if (!(bmcr & BMCR_SPEED100))
+			mode &= ~AX88772_MEDIUM_100MB;
+
+		ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE,
+				  mode, 0, 0, NULL);
+		return;
+	}
+
+	switch (ax772_data->Event) {
+	case WAIT_AUTONEG_COMPLETE:
+		if (jiffies > (ax772_data->autoneg_start + 5 * HZ)) {
+			ax772_data->Event = PHY_POWER_DOWN;
+			ax772_data->TickToExpire = 23;
+		}
+		break;
+	case PHY_POWER_DOWN:
+		if (ax772_data->TickToExpire == 23) {
+			ax88772_save_bmcr_anar(dev);
+			/* Set Phy Power Down */
+			ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPPD,
+					  0, 0, NULL);
+			--ax772_data->TickToExpire;
+		} else if (--ax772_data->TickToExpire == 0) {
+			/* Set Phy Power Up */
+			ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+				AX_SWRESET_IPRL, 0, 0, NULL);
+			ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+				AX_SWRESET_IPPD | AX_SWRESET_IPRL, 0, 0, NULL);
+			msleep(10);
+			ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+				AX_SWRESET_IPRL, 0, 0, NULL);
+			msleep(60);
+			ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+				AX_SWRESET_CLEAR, 0, 0, NULL);
+			ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+				AX_SWRESET_IPRL, 0, 0, NULL);
+
+			if (ax772_data->presvd_phy_advertise && ax772_data->presvd_phy_bmcr) {
+				ax88772_restore_bmcr_anar(dev);
+				
+			} else {
+				ax8817x_mdio_write_le(dev->net, dev->mii.phy_id,
+						      MII_ADVERTISE,
+						      ADVERTISE_ALL | ADVERTISE_CSMA |
+						      ADVERTISE_PAUSE_CAP);
+				mii_nway_restart(&dev->mii);
+			}
+
+			ax772_data->Event = PHY_POWER_UP;
+			ax772_data->TickToExpire = 47;
+		}
+		break;
+	case PHY_POWER_UP:
+		if (--ax772_data->TickToExpire == 0) {
+			ax772_data->Event = PHY_POWER_DOWN;
+			ax772_data->TickToExpire = 23;
+		}
+		break;
+	default:
+		break;
+	}
+	return;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+static void ax88772a_link_reset(void *data)
+{
+	struct usbnet *dev = (struct usbnet *)data;
+	struct ax88772a_data *ax772a_data = (struct ax88772a_data *)dev->priv;
+#else
+static void ax88772a_link_reset(struct work_struct *work)
+{
+	struct ax88772a_data *ax772a_data = container_of(work,
+							 struct ax88772a_data,
+							 check_link);
+	struct usbnet *dev = ax772a_data->dev;
+#endif
+	int powsave = (ax772a_data->EepromData >> 14);
+	u16 phy_reg;
+
+	if (ax772a_data->Event == AX_SET_RX_CFG) {
+		u16 bmcr;
+		u16 mode;
+
+		ax772a_data->Event = AX_NOP;
+
+		mode = AX88772_MEDIUM_DEFAULT;
+
+		bmcr = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id,
+					    MII_BMCR);
+		if (!(bmcr & BMCR_FULLDPLX))
+			mode &= ~AX88772_MEDIUM_FULL_DUPLEX;
+		if (!(bmcr & BMCR_SPEED100))
+			mode &= ~AX88772_MEDIUM_100MB;
+
+		ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode,
+				  0, 0, NULL);
+
+		if (ax772a_data->presvd_phy_advertise && ax772a_data->presvd_phy_bmcr) {
+
+			/* Restore Advertisement control reg */
+			ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+					      ax772a_data->presvd_phy_advertise);
+			/* Restore BMCR */
+			ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_BMCR,
+					      ax772a_data->presvd_phy_bmcr);
+			ax772a_data->presvd_phy_advertise = 0;
+			ax772a_data->presvd_phy_bmcr = 0;
+		}
+
+		return;
+	}
+
+	switch (ax772a_data->Event) {
+	case WAIT_AUTONEG_COMPLETE:
+		if (jiffies > (ax772a_data->autoneg_start + 5 * HZ)) {
+			ax772a_data->Event = CHK_CABLE_EXIST;
+			ax772a_data->TickToExpire = 14;
+		}
+		break;
+	case CHK_CABLE_EXIST:
+		phy_reg = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, 0x12);
+		if ((phy_reg != 0x8012) && (phy_reg != 0x8013)) {
+			ax8817x_mdio_write_le(dev->net,
+				dev->mii.phy_id, 0x16, 0x4040);
+			mii_nway_restart(&dev->mii);
+			ax772a_data->Event = CHK_CABLE_STATUS;
+			ax772a_data->TickToExpire = 31;
+		} else if (--ax772a_data->TickToExpire == 0) {
+			mii_nway_restart(&dev->mii);
+			ax772a_data->Event = CHK_CABLE_EXIST_AGAIN;
+			if (powsave == 0x03) {
+				ax772a_data->TickToExpire = 47;
+			} else if (powsave == 0x01) {
+				ax772a_data->DlyIndex = (u8)(jiffies % 6);
+				ax772a_data->DlySel = 0;
+				ax772a_data->TickToExpire =
+			chkcntsel[ax772a_data->DlyIndex][ax772a_data->DlySel];
+			}
+		}
+		break;
+	case CHK_CABLE_EXIST_AGAIN:
+		/* if cable disconnected */
+		phy_reg = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, 0x12);
+		if ((phy_reg != 0x8012) && (phy_reg != 0x8013)) {
+			mii_nway_restart(&dev->mii);
+			ax772a_data->Event = CHK_CABLE_STATUS;
+			ax772a_data->TickToExpire = 31;
+		} else if (--ax772a_data->TickToExpire == 0) {
+			if (!ax772a_data->presvd_phy_advertise && !ax772a_data->presvd_phy_bmcr) {
+				/* Preserve BMCR for restoring */
+				ax772a_data->presvd_phy_bmcr =
+					ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_BMCR);
+
+				/* Preserve Advertisement control reg for restoring */
+				ax772a_data->presvd_phy_advertise =
+					ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_ADVERTISE);
+			}
+
+
+			/* Power down PHY */
+			ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+					  AX_SWRESET_IPPD,
+					  0, 0, NULL);
+			ax772a_data->Event = PHY_POWER_DOWN;
+			if (powsave == 0x03)
+				ax772a_data->TickToExpire = 23;
+			else if (powsave == 0x01)
+				ax772a_data->TickToExpire = 31;
+		}
+		break;
+	case PHY_POWER_DOWN:
+		if (--ax772a_data->TickToExpire == 0)
+			ax772a_data->Event = PHY_POWER_UP;
+		break;
+	case CHK_CABLE_STATUS:
+		if (--ax772a_data->TickToExpire == 0) {
+			ax8817x_mdio_write_le(dev->net,
+					dev->mii.phy_id, 0x16, 0x4040);
+			mii_nway_restart(&dev->mii);
+			ax772a_data->Event = CHK_CABLE_EXIST_AGAIN;
+			if (powsave == 0x03) {
+				ax772a_data->TickToExpire = 47;
+			} else if (powsave == 0x01) {
+				ax772a_data->DlyIndex = (u8)(jiffies % 6);
+				ax772a_data->DlySel = 0;
+				ax772a_data->TickToExpire =
+			chkcntsel[ax772a_data->DlyIndex][ax772a_data->DlySel];
+			}
+		}
+		break;
+	case PHY_POWER_UP:
+
+		if (!ax772a_data->presvd_phy_advertise && !ax772a_data->presvd_phy_bmcr) {
+			/* Preserve BMCR for restoring */
+			ax772a_data->presvd_phy_bmcr =
+				ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_BMCR);
+
+			/* Preserve Advertisement control reg for restoring */
+			ax772a_data->presvd_phy_advertise =
+				ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_ADVERTISE);
+		}
+
+		ax88772a_phy_powerup(dev);
+
+		ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+			ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
+
+		mii_nway_restart(&dev->mii);
+
+		ax772a_data->Event = CHK_CABLE_EXIST_AGAIN;
+
+		if (powsave == 0x03) {
+			ax772a_data->TickToExpire = 47;
+		} else if (powsave == 0x01) {
+			if (++ax772a_data->DlySel >= 3) {
+				ax772a_data->DlyIndex = (u8)(jiffies % 6);
+				ax772a_data->DlySel = 0;
+			}
+			ax772a_data->TickToExpire =
+			chkcntsel[ax772a_data->DlyIndex][ax772a_data->DlySel];
+		}
+		break;
+	default:
+		break;
+	}
+
+	return;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+static void ax88772b_link_reset(void *data)
+{
+	struct usbnet *dev = (struct usbnet *)data;
+	struct ax88772b_data *ax772b_data = (struct ax88772b_data *)dev->priv;
+#else
+static void ax88772b_link_reset(struct work_struct *work)
+{
+	struct ax88772b_data *ax772b_data = container_of(work,
+							 struct ax88772b_data,
+							 check_link);
+	struct usbnet *dev = ax772b_data->dev;
+#endif
+
+	switch (ax772b_data->Event) {
+
+	case AX_SET_RX_CFG:
+	{
+		u16 bmcr = ax8817x_mdio_read_le(dev->net,
+					dev->mii.phy_id, MII_BMCR);
+		u16 mode = AX88772_MEDIUM_DEFAULT;
+
+		if (!(bmcr & BMCR_FULLDPLX))
+			mode &= ~AX88772_MEDIUM_FULL_DUPLEX;
+		if (!(bmcr & BMCR_SPEED100))
+			mode &= ~AX88772_MEDIUM_100MB;	
+
+		if (ax772b_data->ext_phy_oui == EXTPHY_BROADCOM_OUI) {
+			if(ax772b_data->ext_phy_model == EXTPHY_BCM89811_MODEL) {
+				mode = AX88772_MEDIUM_DEFAULT;
+			}
+		}
+
+		ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode,
+				  0, 0, NULL);
+		break;
+	}
+	case PHY_POWER_UP:
+	{
+		u16 tmp16;
+
+		if (!ax772b_data->presvd_phy_advertise && !ax772b_data->presvd_phy_bmcr) {
+			/* Preserve BMCR for restoring */
+			ax772b_data->presvd_phy_bmcr =
+				ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_BMCR);
+
+			/* Preserve Advertisement control reg for restoring */
+			ax772b_data->presvd_phy_advertise =
+				ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, MII_ADVERTISE);
+		}
+
+		ax88772a_phy_powerup(dev);
+		tmp16 = ax8817x_mdio_read_le(dev->net, dev->mii.phy_id, 0x12);
+		ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, 0x12,
+				((tmp16 & 0xFF9F) | 0x0040));
+
+		/* Restore Advertisement control reg */
+		ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+				      ax772b_data->presvd_phy_advertise);
+		/* Restore BMCR */
+		ax8817x_mdio_write_le(dev->net, dev->mii.phy_id, MII_BMCR,
+				      ax772b_data->presvd_phy_bmcr);
+		ax772b_data->presvd_phy_advertise = 0;
+		ax772b_data->presvd_phy_bmcr = 0;
+
+		break;
+	}
+
+	case AX_CHK_AUTODETACH:
+	{
+		int ret;
+		ret = ax8817x_write_cmd(dev, AX_CMD_SW_RESET,
+					AX_SWRESET_IPRL |
+					(ax772b_data->psc & 0x7FFF),
+					0, 0, NULL);
+		if (ret < 0) {
+			deverr(dev, "Failed to configure PHY power saving: %d",
+			       ret);
+		}
+
+		break;
+	}
+	default:
+		break;
+	}
+
+	ax772b_data->Event = AX_NOP;
+	
+	return;
+}
+
+static int ax88178_set_media(struct usbnet *dev)
+{
+	int	ret;
+	struct ax88178_data *ax178dataptr = (struct ax88178_data *)dev->priv;
+	int media;
+
+	media = ax88178_media_check(dev, ax178dataptr);
+	if (media < 0)
+		return media;
+
+	ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, media, 0,
+				0, NULL);
+	if (ret < 0) {
+		deverr(dev, "write mode medium reg failed: %d", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ax88178_link_reset(struct usbnet *dev)
+{
+	return ax88178_set_media(dev);
+}
+
+static int ax_suspend(struct usb_interface *intf,
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 10)
+			pm_message_t message)
+#else
+			u32 message)
+#endif
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	struct ax8817x_data *data = (struct ax8817x_data *)&dev->data;
+
+	return data->suspend(intf, message);
+}
+
+static int ax_resume(struct usb_interface *intf)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	struct ax8817x_data *data = (struct ax8817x_data *)&dev->data;
+
+	return data->resume(intf);
+}
+
+static const struct driver_info ax88178_info = {
+	.description = "",
+//	.description = "ASIX AX88178 USB 2.0 Ethernet",
+	.bind	= ax88178_bind,
+	.unbind = ax88178_unbind,
+	.status = ax88178_status,
+	.link_reset = ax88178_link_reset,
+	.reset	= ax88178_link_reset,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
+	.stop	= ax88772b_stop,
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX | FLAG_AVOID_UNLINK_URBS,
+#else
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX,
+#endif
+	.rx_fixup = ax88772_rx_fixup,
+	.tx_fixup = ax88772_tx_fixup,
+};
+
+static const struct driver_info belkin178_info = {
+	.description = "Belkin Gigabit USB 2.0 Network Adapter",
+	.bind	= ax88178_bind,
+	.unbind	= ax88178_unbind,
+	.status	= ax88178_status,
+	.link_reset = ax88178_link_reset,
+	.reset	= ax88178_link_reset,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
+	.stop	= ax88772b_stop,
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX | FLAG_AVOID_UNLINK_URBS,
+#else
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX,
+#endif
+	.rx_fixup = ax88772_rx_fixup,
+	.tx_fixup = ax88772_tx_fixup,
+};
+
+static const struct driver_info ax8817x_info = {
+	.description = "",
+//	.description = "ASIX AX8817x USB 2.0 Ethernet",
+	.bind	= ax8817x_bind,
+	.status	= ax8817x_status,
+	.link_reset = ax88172_link_reset,
+	.reset	= ax88172_link_reset,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
+	.stop	= ax88772b_stop,
+	.flags	= FLAG_ETHER | FLAG_AVOID_UNLINK_URBS,
+#else
+	.flags	= FLAG_ETHER,
+#endif
+};
+
+static const struct driver_info dlink_dub_e100_info = {
+	.description = "DLink DUB-E100 USB Ethernet",
+	.bind	= ax8817x_bind,
+	.status	= ax8817x_status,
+	.link_reset = ax88172_link_reset,
+	.reset	= ax88172_link_reset,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
+	.stop	= ax88772b_stop,
+	.flags	= FLAG_ETHER | FLAG_AVOID_UNLINK_URBS,
+#else
+	.flags	= FLAG_ETHER,
+#endif
+};
+
+static const struct driver_info netgear_fa120_info = {
+	.description = "Netgear FA-120 USB Ethernet",
+	.bind	= ax8817x_bind,
+	.status = ax8817x_status,
+	.link_reset = ax88172_link_reset,
+	.reset	= ax88172_link_reset,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
+	.stop	= ax88772b_stop,
+	.flags	= FLAG_ETHER | FLAG_AVOID_UNLINK_URBS,
+#else
+	.flags	= FLAG_ETHER,
+#endif
+};
+
+static const struct driver_info hawking_uf200_info = {
+	.description = "Hawking UF200 USB Ethernet",
+	.bind	= ax8817x_bind,
+	.status = ax8817x_status,
+	.link_reset = ax88172_link_reset,
+	.reset	= ax88172_link_reset,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
+	.stop	= ax88772b_stop,
+	.flags	= FLAG_ETHER | FLAG_AVOID_UNLINK_URBS,
+#else
+	.flags	= FLAG_ETHER,
+#endif
+};
+
+static const struct driver_info ax88772_info = {
+	.description = "",
+//	.description = "ASIX AX88772 USB 2.0 Ethernet",
+	.bind	= ax88772_bind,
+	.unbind = ax88772_unbind,
+	.status = ax88772_status,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
+	.stop	= ax88772b_stop,
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX | FLAG_AVOID_UNLINK_URBS,
+#else
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX,
+#endif
+	.rx_fixup = ax88772_rx_fixup,
+	.tx_fixup = ax88772_tx_fixup,
+	.reset	= ax88772b_reset,
+};
+
+static const struct driver_info dlink_dub_e100b_info = {
+	.description = "D-Link DUB-E100 USB 2.0 Fast Ethernet Adapter",
+	.bind	= ax88772_bind,
+	.unbind = ax88772_unbind,
+	.status = ax88772_status,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
+	.stop	= ax88772b_stop,
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX | FLAG_AVOID_UNLINK_URBS,
+#else
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX,
+#endif
+	.rx_fixup = ax88772_rx_fixup,
+	.tx_fixup = ax88772_tx_fixup,
+	.reset	= ax88772b_reset,
+};
+
+static const struct driver_info dlink_dub_e100_772b_info = {
+	.description = "D-Link DUB-E100 USB 2.0 Fast Ethernet Adapter",
+	.bind	= ax88772b_bind,
+	.unbind = ax88772b_unbind,
+	.status = ax88772b_status,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
+	.stop	= ax88772b_stop,
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX | FLAG_HW_IP_ALIGNMENT | FLAG_AVOID_UNLINK_URBS,
+#else
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX | FLAG_HW_IP_ALIGNMENT,
+#endif
+	.rx_fixup = ax88772b_rx_fixup,
+	.tx_fixup = ax88772b_tx_fixup,
+	.reset	= ax88772b_reset,
+};
+
+static const struct driver_info dlink_dub_e100_772c_info = {
+	.description = "D-Link DUB-E100 USB 2.0 Fast Ethernet Adapter",
+	.bind	= ax88772b_bind,
+	.unbind = ax88772b_unbind,
+	.status = ax88772b_status,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
+	.stop	= ax88772b_stop,
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX | FLAG_HW_IP_ALIGNMENT | FLAG_AVOID_UNLINK_URBS,
+#else
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX | FLAG_HW_IP_ALIGNMENT,
+#endif
+	.rx_fixup = ax88772b_rx_fixup,
+	.tx_fixup = ax88772b_tx_fixup,
+	.reset	= ax88772b_reset,
+};
+
+static const struct driver_info ax88772a_info = {
+	.description = "",
+//	.description = "ASIX AX88772A USB 2.0 Ethernet",
+	.bind	= ax88772a_bind,
+	.unbind = ax88772a_unbind,
+	.status = ax88772a_status,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
+	.stop	= ax88772b_stop,
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX | FLAG_AVOID_UNLINK_URBS,
+#else
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX,
+#endif
+	.rx_fixup = ax88772_rx_fixup,
+	.tx_fixup = ax88772_tx_fixup,
+	.reset	= ax88772b_reset,
+};
+
+static const struct driver_info ax88772b_info = {
+	.description = "",
+//	.description = "ASIX AX88772B USB 2.0 Ethernet",
+	.bind	= ax88772b_bind,
+	.unbind = ax88772b_unbind,
+	.status = ax88772b_status,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
+	.stop	= ax88772b_stop,
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX | FLAG_HW_IP_ALIGNMENT | FLAG_AVOID_UNLINK_URBS,
+#else
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX | FLAG_HW_IP_ALIGNMENT,
+#endif
+	.rx_fixup = ax88772b_rx_fixup,
+	.tx_fixup = ax88772b_tx_fixup,
+	.reset	= ax88772b_reset,
+};
+
+static const struct driver_info ax88772c_info = {
+	.description = "",
+//	.description = "ASIX AX88772C USB 2.0 Ethernet",
+	.bind	= ax88772b_bind,
+	.unbind = ax88772b_unbind,
+	.status = ax88772c_status,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
+	.stop	= ax88772b_stop,
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX | FLAG_HW_IP_ALIGNMENT | FLAG_AVOID_UNLINK_URBS,
+#else
+	.flags	= FLAG_ETHER | FLAG_FRAMING_AX | FLAG_HW_IP_ALIGNMENT,
+#endif
+	.rx_fixup = ax88772b_rx_fixup,
+	.tx_fixup = ax88772b_tx_fixup,
+	.reset	= ax88772b_reset,
+};
+
+static const struct usb_device_id products[] = {
+{
+	/* 88178 */
+	USB_DEVICE(0x0b95, 0x1780),
+	.driver_info =	(unsigned long) &ax88178_info,
+}, {
+	/* 88178 for billianton linksys */
+	USB_DEVICE(0x077b, 0x2226),
+	.driver_info =	(unsigned long) &ax88178_info,
+}, {
+	/* ABOCOM for linksys */
+	USB_DEVICE(0x1737, 0x0039),
+	.driver_info =	(unsigned long) &ax88178_info,
+}, {
+	/* ABOCOM  for pci */
+	USB_DEVICE(0x14ea, 0xab11),
+	.driver_info =	(unsigned long) &ax88178_info,
+}, {
+	/* Belkin */
+	USB_DEVICE(0x050d, 0x5055),
+	.driver_info =	(unsigned long) &belkin178_info,
+}, {
+	/* 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,
+}, {
+	/* DLink DUB-E100B */
+	USB_DEVICE(0x2001, 0x3c05),
+	.driver_info =  (unsigned long) &dlink_dub_e100b_info,
+}, {
+	/* DLink DUB-E100B */
+	USB_DEVICE(0x07d1, 0x3c05),
+	.driver_info =  (unsigned long) &dlink_dub_e100b_info,
+}, {
+	/* DLink DUB-E100 (AX88772B)*/
+	USB_DEVICE_VER(0x2001, 0x1a02, 0, 1),
+	.driver_info =  (unsigned long) &dlink_dub_e100_772b_info,
+}, {
+	/* DLink DUB-E100 (AX88772C)*/
+	USB_DEVICE_VER(0x2001, 0x1a02, 0, 2),
+	.driver_info =  (unsigned long) &dlink_dub_e100_772c_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,
+}, {
+	/* ASIX AX88772 10/100 */
+	USB_DEVICE(0x125E, 0x180D),
+	.driver_info = (unsigned long) &ax88772_info,
+}, {
+	/* ASIX AX88772A 10/100 */
+	USB_DEVICE(0x0b95, 0x772A),
+	.driver_info = (unsigned long) &ax88772a_info,
+}, {
+	/* ASIX AX88772A 10/100 */
+	USB_DEVICE(0x0db0, 0xA877),
+	.driver_info = (unsigned long) &ax88772a_info,
+}, {
+	/* ASIX AX88772A 10/100 */
+	USB_DEVICE(0x0421, 0x772A),
+	.driver_info = (unsigned long) &ax88772a_info,
+}, {
+	/* Linksys 200M */
+	 USB_DEVICE(0x13B1, 0x0018),
+	.driver_info = (unsigned long) &ax88772a_info,
+}, {
+	USB_DEVICE(0x05ac, 0x1402),
+	.driver_info = (unsigned long) &ax88772a_info,
+}, {
+	/* ASIX AX88772B 10/100 */
+	USB_DEVICE_VER(0x0b95, 0x772B, 0, 1),
+	.driver_info = (unsigned long) &ax88772b_info,
+}, {
+	/* Asus AX88772B 10/100 */
+	USB_DEVICE_VER(0x0b95, 0x7e2b, 0, 1),
+	.driver_info = (unsigned long) &ax88772b_info,
+}, {
+	/* Lenovo AX88772B 10/100 */
+	USB_DEVICE_VER(0x17ef, 0x7203, 0, 1),
+	.driver_info = (unsigned long) &ax88772b_info,
+},{
+	/* ASIX AX88772B ver.2 10/100 */
+	USB_DEVICE_VER(0x0b95, 0x772B, 0, 2),
+	.driver_info = (unsigned long) &ax88772c_info,
+},
+	{ },		/* END */
+};
+MODULE_DEVICE_TABLE(usb, products);
+
+static struct usb_driver asix_driver = {
+	/* .owner =	THIS_MODULE, */
+	.name =		"asix",
+	.id_table =	products,
+	.probe =	axusbnet_probe,
+	.suspend =	ax_suspend,
+	.resume =	ax_resume,
+	.disconnect =	axusbnet_disconnect,
+};
+
+static int __init asix_init(void)
+{
+	return usb_register(&asix_driver);
+}
+module_init(asix_init);
+
+static void __exit asix_exit(void)
+{
+	usb_deregister(&asix_driver);
+}
+module_exit(asix_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
+MODULE_LICENSE(DRIVER_LICENSE);
diff --git a/kernel/drivers/net/usb/ax88772C/asix.h b/kernel/drivers/net/usb/ax88772C/asix.h
new file mode 100644
index 0000000..a0a59e8
--- /dev/null
+++ b/kernel/drivers/net/usb/ax88772C/asix.h
@@ -0,0 +1,575 @@
+#ifndef	__LINUX_USBNET_ASIX_H
+#define	__LINUX_USBNET_ASIX_H
+
+/*
+ * Turn on this flag if the implementation of your USB host controller
+ * cannot handle non-double word aligned buffer.
+ * When turn on this flag, driver will fixup egress packet aligned on double
+ * word boundary before deliver to USB host controller. And will Disable the
+ * function "skb_reserve (skb, NET_IP_ALIGN)" to retain the buffer aligned on
+ * double word alignment for ingress packets.
+ */
+#define AX_FORCE_BUFF_ALIGN		0
+
+//#define RX_SKB_COPY
+
+#define AX_MONITOR_MODE			0x01
+#define AX_MONITOR_LINK			0x02
+#define AX_MONITOR_MAGIC		0x04
+#define AX_MONITOR_HSFS			0x10
+
+/* AX88172 Medium Status Register values */
+#define AX_MEDIUM_FULL_DUPLEX		0x02
+#define AX_MEDIUM_TX_ABORT_ALLOW	0x04
+#define AX_MEDIUM_FLOW_CONTROL_EN	0x10
+#define AX_MCAST_FILTER_SIZE		8
+#define AX_MAX_MCAST			64
+
+#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 AX_SWRESET_IPOSC		0x0080
+#define AX_SWRESET_IPPSL_0		0x0100
+#define AX_SWRESET_IPPSL_1		0x0200
+#define AX_SWRESET_IPCOPS		0x0400
+#define AX_SWRESET_IPCOPSC		0x0800
+#define AX_SWRESET_AUTODETACH		0x1000
+#define AX_SWRESET_WOLLP		0x8000
+
+#define AX88772_IPG0_DEFAULT		0x15
+#define AX88772_IPG1_DEFAULT		0x0c
+#define AX88772_IPG2_DEFAULT		0x0E
+
+#define AX88772A_IPG0_DEFAULT		0x15
+#define AX88772A_IPG1_DEFAULT		0x16
+#define AX88772A_IPG2_DEFAULT		0x1A
+
+#define AX88772_MEDIUM_FULL_DUPLEX	0x0002
+#define AX88772_MEDIUM_RESERVED		0x0004
+#define AX88772_MEDIUM_RX_FC_ENABLE	0x0010
+#define AX88772_MEDIUM_TX_FC_ENABLE	0x0020
+#define AX88772_MEDIUM_PAUSE_FORMAT	0x0080
+#define AX88772_MEDIUM_RX_ENABLE	0x0100
+#define AX88772_MEDIUM_100MB		0x0200
+#define AX88772_MEDIUM_DEFAULT	\
+	(AX88772_MEDIUM_FULL_DUPLEX | AX88772_MEDIUM_RX_FC_ENABLE | \
+	 AX88772_MEDIUM_TX_FC_ENABLE | AX88772_MEDIUM_100MB | \
+	 AX88772_MEDIUM_RESERVED | AX88772_MEDIUM_RX_ENABLE)
+
+#define AX_CMD_SET_SW_MII		0x06
+#define AX_CMD_READ_MII_REG		0x07
+#define AX_CMD_WRITE_MII_REG		0x08
+#define AX_CMD_READ_STATMNGSTS_REG	0x09
+	#define AX_HOST_EN		0x01
+
+#define AX_CMD_SET_HW_MII		0x0a
+#define AX_CMD_READ_EEPROM		0x0b
+#define AX_CMD_WRITE_EEPROM		0x0c
+#define AX_CMD_WRITE_EEPROM_EN		0x0d
+#define AX_CMD_WRITE_EEPROM_DIS		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_READ_MEDIUM_MODE		0x1a
+#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 AX_PHYSEL_PSEL		(1 << 0)
+	#define AX_PHYSEL_ASEL		(1 << 1)
+	#define AX_PHYSEL_SSMII		(0 << 2)
+	#define AX_PHYSEL_SSRMII	(1 << 2)
+	#define AX_PHYSEL_SSRRMII	(3 << 2)
+	#define AX_PHYSEL_SSEN		(1 << 4)
+#define AX88772_CMD_READ_NODE_ID	0x13
+#define AX88772_CMD_WRITE_NODE_ID	0x14
+#define AX_CMD_READ_WKFARY		0x23
+#define AX_CMD_WRITE_WKFARY		0x24
+#define AX_CMD_READ_RXCOE_CTL		0x2b
+#define AX_CMD_WRITE_RXCOE_CTL		0x2c
+#define AX_CMD_READ_TXCOE_CTL		0x2d
+#define AX_CMD_WRITE_TXCOE_CTL		0x2e
+
+#define REG_LENGTH			2
+#define PHY_ID_MASK			0x1f
+
+#define AX_RXCOE_IPCE			0x0001
+#define AX_RXCOE_IPVE			0x0002
+#define AX_RXCOE_V6VE			0x0004
+#define AX_RXCOE_TCPE			0x0008
+#define AX_RXCOE_UDPE			0x0010
+#define AX_RXCOE_ICMP			0x0020
+#define AX_RXCOE_IGMP			0x0040
+#define AX_RXCOE_ICV6			0x0080
+#define AX_RXCOE_TCPV6			0x0100
+#define AX_RXCOE_UDPV6			0x0200
+#define AX_RXCOE_ICMV6			0x0400
+#define AX_RXCOE_IGMV6			0x0800
+#define AX_RXCOE_ICV6V6			0x1000
+#define AX_RXCOE_FOPC			0x8000
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22)
+#define AX_RXCOE_DEF_CSUM		(AX_RXCOE_IPCE | AX_RXCOE_IPVE | \
+					 AX_RXCOE_V6VE | AX_RXCOE_TCPE | \
+					 AX_RXCOE_UDPE |  AX_RXCOE_ICV6 | \
+					 AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6)
+#else
+#define AX_RXCOE_DEF_CSUM		(AX_RXCOE_IPCE | AX_RXCOE_IPVE | \
+					 AX_RXCOE_TCPE | AX_RXCOE_UDPE)
+#endif
+
+#define AX_RXCOE_64TE			0x0100
+#define AX_RXCOE_PPPOE			0x0200
+#define AX_RXCOE_RPCE			0x8000
+
+#define AX_TXCOE_IP			0x0001
+#define AX_TXCOE_TCP			0x0002
+#define AX_TXCOE_UDP			0x0004
+#define AX_TXCOE_ICMP			0x0008
+#define AX_TXCOE_IGMP			0x0010
+#define AX_TXCOE_ICV6			0x0020
+
+#define AX_TXCOE_TCPV6			0x0100
+#define AX_TXCOE_UDPV6			0x0200
+#define AX_TXCOE_ICMV6			0x0400
+#define AX_TXCOE_IGMV6			0x0800
+#define AX_TXCOE_ICV6V6			0x1000
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 22)
+#define AX_TXCOE_DEF_CSUM		(AX_TXCOE_TCP | AX_TXCOE_UDP | \
+					 AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6)
+#else
+#define AX_TXCOE_DEF_CSUM		(AX_TXCOE_TCP | AX_TXCOE_UDP)
+#endif
+
+#define AX_TXCOE_64TE			0x0001
+#define AX_TXCOE_PPPE			0x0002
+
+#define AX88772B_MAX_BULKIN_2K		0
+#define AX88772B_MAX_BULKIN_4K		1
+#define AX88772B_MAX_BULKIN_6K		2
+#define AX88772B_MAX_BULKIN_8K		3
+#define AX88772B_MAX_BULKIN_16K		4
+#define AX88772B_MAX_BULKIN_20K		5
+#define AX88772B_MAX_BULKIN_24K		6
+#define AX88772B_MAX_BULKIN_32K		7
+struct {unsigned short size, byte_cnt, threshold; } AX88772B_BULKIN_SIZE[] = {
+	/* 2k */
+	{2048, 0x8000, 0x8001},
+	/* 4k */
+	{4096, 0x8100, 0x8147},
+	/* 6k */
+	{6144, 0x8200, 0x81EB},
+	/* 8k */
+	{8192, 0x8300, 0x83D7},
+	/* 16 */
+	{16384, 0x8400, 0x851E},
+	/* 20k */
+	{20480, 0x8500, 0x8666},
+	/* 24k */
+	{24576, 0x8600, 0x87AE},
+	/* 32k */
+	{32768, 0x8700, 0x8A3D},
+};
+
+
+#define AX_RX_CTL_RH1M			0x0100		/* Enable RX-Header mode 0 */
+#define AX_RX_CTL_RH2M			0x0200		/* Enable IP header in receive buffer aligned on 32-bit aligment */
+#define AX_RX_CTL_RH3M			0x0400		/* checksum value in rx header 3 */
+#define AX_RX_HEADER_DEFAULT		(AX_RX_CTL_RH1M | AX_RX_CTL_RH2M)
+
+#define AX_RX_CTL_MFB			0x0300		/* Maximum Frame size 16384bytes */
+#define AX_RX_CTL_START			0x0080		/* Ethernet MAC start */
+#define AX_RX_CTL_AP			0x0020		/* Accept physcial address from Multicast array */
+#define AX_RX_CTL_AM			0x0010
+#define AX_RX_CTL_AB			0x0008		/* Accetp Brocadcast frames*/
+#define AX_RX_CTL_SEP			0x0004		/* Save error packets */
+#define AX_RX_CTL_AMALL			0x0002		/* Accetp all multicast frames */
+#define AX_RX_CTL_PRO			0x0001		/* Promiscuous Mode */
+#define AX_RX_CTL_STOP			0x0000		/* Stop MAC */
+
+#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_EEPROM_MAGIC			0xdeadbeef
+#define EEPROMMASK			0x7f
+
+/* GPIO REGISTER */
+#define AXGPIOS_GPO0EN			0X01 /* 1 << 0 */
+#define AXGPIOS_GPO0			0X02 /* 1 << 1 */
+#define AXGPIOS_GPO1EN			0X04 /*	1 << 2 */
+#define AXGPIOS_GPO1			0X08 /* 1 << 3 */
+#define AXGPIOS_GPO2EN			0X10 /* 1 << 4 */
+#define AXGPIOS_GPO2			0X20 /* 1 << 5 */
+#define AXGPIOS_RSE			0X80 /* 1 << 7 */
+
+/* TX-header format */
+#define AX_TX_HDR_CPHI			0x4000
+#define AX_TX_HDR_DICF			0x8000
+
+/* GMII register definitions */
+#define GMII_PHY_CONTROL		0x00	/* control reg */
+#define GMII_PHY_STATUS			0x01	/* status reg */
+#define GMII_PHY_OUI			0x02	/* most of the OUI bits */
+#define GMII_PHY_MODEL			0x03	/* model/rev bits, and rest of OUI */
+#define GMII_PHY_ANAR			0x04	/* AN advertisement reg */
+#define GMII_PHY_ANLPAR			0x05	/* AN Link Partner */
+#define GMII_PHY_ANER			0x06	/* AN expansion reg */
+#define GMII_PHY_1000BT_CONTROL		0x09	/* control reg for 1000BT */
+#define GMII_PHY_1000BT_STATUS		0x0A	/* status reg for 1000BT */
+
+/* Bit definitions: GMII Control */
+#define GMII_CONTROL_RESET		0x8000	/* reset bit in control reg */
+#define GMII_CONTROL_LOOPBACK		0x4000	/* loopback bit in control reg */
+#define GMII_CONTROL_10MB		0x0000	/* 10 Mbit */
+#define GMII_CONTROL_100MB		0x2000	/* 100Mbit */
+#define GMII_CONTROL_1000MB		0x0040	/* 1000Mbit */
+#define GMII_CONTROL_SPEED_BITS		0x2040	/* speed bit mask */
+#define GMII_CONTROL_ENABLE_AUTO	0x1000	/* autonegotiate enable */
+#define GMII_CONTROL_POWER_DOWN		0x0800
+#define GMII_CONTROL_ISOLATE		0x0400	/* islolate bit */
+#define GMII_CONTROL_START_AUTO		0x0200	/* restart autonegotiate */
+#define GMII_CONTROL_FULL_DUPLEX	0x0100
+
+/* Bit definitions: GMII Status */
+#define GMII_STATUS_100MB_MASK		0xE000	/* any of these indicate 100 Mbit */
+#define GMII_STATUS_10MB_MASK		0x1800	/* either of these indicate 10 Mbit */
+#define GMII_STATUS_AUTO_DONE		0x0020	/* auto negotiation complete */
+#define GMII_STATUS_AUTO		0x0008	/* auto negotiation is available */
+#define GMII_STATUS_LINK_UP		0x0004	/* link status bit */
+#define GMII_STATUS_EXTENDED		0x0001	/* extended regs exist */
+#define GMII_STATUS_100T4		0x8000	/* capable of 100BT4 */
+#define GMII_STATUS_100TXFD		0x4000	/* capable of 100BTX full duplex */
+#define GMII_STATUS_100TX		0x2000	/* capable of 100BTX */
+#define GMII_STATUS_10TFD		0x1000	/* capable of 10BT full duplex */
+#define GMII_STATUS_10T			0x0800	/* capable of 10BT */
+
+/* Bit definitions: Auto-Negotiation Advertisement */
+#define GMII_ANAR_ASYM_PAUSE		0x0800	/* support asymetric pause */
+#define GMII_ANAR_PAUSE			0x0400	/* support pause packets */
+#define GMII_ANAR_100T4			0x0200	/* support 100BT4 */
+#define GMII_ANAR_100TXFD		0x0100	/* support 100BTX full duplex */
+#define GMII_ANAR_100TX			0x0080	/* support 100BTX half duplex */
+#define GMII_ANAR_10TFD			0x0040	/* support 10BT full duplex */
+#define GMII_ANAR_10T			0x0020	/* support 10BT half duplex */
+#define GMII_SELECTOR_FIELD		0x001F	/* selector field. */
+
+/* Bit definitions: Auto-Negotiation Link Partner Ability */
+#define GMII_ANLPAR_100T4		0x0200	/* support 100BT4 */
+#define GMII_ANLPAR_100TXFD		0x0100	/* support 100BTX full duplex */
+#define GMII_ANLPAR_100TX		0x0080	/* support 100BTX half duplex */
+#define GMII_ANLPAR_10TFD		0x0040	/* support 10BT full duplex */
+#define GMII_ANLPAR_10T			0x0020	/* support 10BT half duplex */
+#define GMII_ANLPAR_PAUSE		0x0400	/* support pause packets */
+#define GMII_ANLPAR_ASYM_PAUSE		0x0800	/* support asymetric pause */
+#define GMII_ANLPAR_ACK			0x4000	/* means LCB was successfully rx'd */
+#define GMII_SELECTOR_8023		0x0001;
+
+/* Bit definitions: 1000BaseT AUX Control */
+#define GMII_1000_AUX_CTRL_MASTER_SLAVE		0x1000
+#define GMII_1000_AUX_CTRL_FD_CAPABLE		0x0200	/* full duplex capable */
+#define GMII_1000_AUX_CTRL_HD_CAPABLE		0x0100	/* half duplex capable */
+
+/* Bit definitions: 1000BaseT AUX Status */
+#define GMII_1000_AUX_STATUS_FD_CAPABLE		0x0800	/* full duplex capable */
+#define GMII_1000_AUX_STATUS_HD_CAPABLE		0x0400	/* half duplex capable */
+
+/* Cicada MII Registers */
+#define GMII_AUX_CTRL_STATUS			0x1C
+#define GMII_AUX_ANEG_CPLT			0x8000
+#define GMII_AUX_FDX				0x0020
+#define GMII_AUX_SPEED_1000			0x0010
+#define GMII_AUX_SPEED_100			0x0008
+
+#ifndef ADVERTISE_PAUSE_CAP
+#define ADVERTISE_PAUSE_CAP			0x0400
+#endif
+
+#ifndef MII_STAT1000
+#define MII_STAT1000				0x000A
+#endif
+
+#ifndef LPA_1000FULL
+#define LPA_1000FULL				0x0800
+#endif
+
+/* medium mode register */
+#define MEDIUM_GIGA_MODE			0x0001
+#define MEDIUM_FULL_DUPLEX_MODE			0x0002
+#define MEDIUM_TX_ABORT_MODE			0x0004
+#define MEDIUM_ENABLE_125MHZ			0x0008
+#define MEDIUM_ENABLE_RX_FLOWCTRL		0x0010
+#define MEDIUM_ENABLE_TX_FLOWCTRL		0x0020
+#define MEDIUM_ENABLE_JUMBO_FRAME		0x0040
+#define MEDIUM_CHECK_PAUSE_FRAME_MODE		0x0080
+#define MEDIUM_ENABLE_RECEIVE			0x0100
+#define MEDIUM_MII_100M_MODE			0x0200
+#define MEDIUM_ENABLE_JAM_PATTERN		0x0400
+#define MEDIUM_ENABLE_STOP_BACKPRESSURE		0x0800
+#define MEDIUM_ENABLE_SUPPER_MAC_SUPPORT	0x1000
+
+/* PHY mode */
+#define PHY_MODE_MARVELL		0
+#define PHY_MODE_CICADA_FAMILY		1
+#define PHY_MODE_CICADA_V1		1
+#define PHY_MODE_AGERE_FAMILY		2
+#define PHY_MODE_AGERE_V0		2
+#define PHY_MODE_CICADA_V2		5
+#define PHY_MODE_AGERE_V0_GMII		6
+#define PHY_MODE_CICADA_V2_ASIX		9
+#define PHY_MODE_VSC8601		10
+#define PHY_MODE_RTL8211CL		12
+#define PHY_MODE_RTL8211BN		13
+#define PHY_MODE_RTL8251CL		14
+#define PHY_MODE_ATTANSIC_V0		0x40
+#define PHY_MODE_ATTANSIC_FAMILY	0x40
+#define PHY_MODE_MAC_TO_MAC_GMII	0x7C
+
+/*  */
+#define LED_MODE_MARVELL		0
+#define LED_MODE_CAMEO			1
+
+#define MARVELL_LED_CTRL		0x18
+#define MARVELL_MANUAL_LED		0x19
+
+#define PHY_IDENTIFIER			0x0002
+#define PHY_AGERE_IDENTIFIER		0x0282
+#define PHY_CICADA_IDENTIFIER		0x000f
+#define PHY_MARVELL_IDENTIFIER		0x0141
+
+#define PHY_MARVELL_STATUS		0x001b
+#define MARVELL_STATUS_HWCFG		0x0004		/* SGMII without clock */
+
+#define PHY_MARVELL_CTRL		0x0014
+#define MARVELL_CTRL_RXDELAY		0x0080
+#define MARVELL_CTRL_TXDELAY		0x0002
+
+#define PHY_CICADA_EXTPAGE		0x001f
+#define CICADA_EXTPAGE_EN		0x0001
+#define CICADA_EXTPAGE_DIS		0x0000
+
+/* External ethernet phy */
+#define EXTPHY_ID_MASK_OUI(phyid1, phyid2) ((phyid1 << 6) | ((phyid2 & 0xFC00) >> 10))
+#define EXTPHY_ID_MASK_MODEL(phyid2) ((phyid2 & 0x3F0) >> 4) 
+
+#define EXTPHY_BROADCOM_OUI		0x2B8094
+#define EXTPHY_BCM89811_MODEL		0x02
+
+struct {unsigned short value, offset; } CICADA_FAMILY_HWINIT[] = {
+	{0x0001, 0x001f}, {0x1c25, 0x0017}, {0x2a30, 0x001f}, {0x234c, 0x0010},
+	{0x2a30, 0x001f}, {0x0212, 0x0008}, {0x52b5, 0x001f}, {0xa7fa, 0x0000},
+	{0x0012, 0x0002}, {0x3002, 0x0001}, {0x87fa, 0x0000}, {0x52b5, 0x001f},
+	{0xafac, 0x0000}, {0x000d, 0x0002}, {0x001c, 0x0001}, {0x8fac, 0x0000},
+	{0x2a30, 0x001f}, {0x0012, 0x0008}, {0x2a30, 0x001f}, {0x0400, 0x0014},
+	{0x2a30, 0x001f}, {0x0212, 0x0008}, {0x52b5, 0x001f}, {0xa760, 0x0000},
+	{0x0000, 0x0002}, {0xfaff, 0x0001}, {0x8760, 0x0000}, {0x52b5, 0x001f},
+	{0xa760, 0x0000}, {0x0000, 0x0002}, {0xfaff, 0x0001}, {0x8760, 0x0000},
+	{0x52b5, 0x001f}, {0xafae, 0x0000}, {0x0004, 0x0002}, {0x0671, 0x0001},
+	{0x8fae, 0x0000}, {0x2a30, 0x001f}, {0x0012, 0x0008}, {0x0000, 0x001f},
+};
+
+struct {unsigned short value, offset; } CICADA_V2_HWINIT[] = {
+	{0x2a30, 0x001f}, {0x0212, 0x0008}, {0x52b5, 0x001f}, {0x000f, 0x0002},
+	{0x472a, 0x0001}, {0x8fa4, 0x0000}, {0x2a30, 0x001f}, {0x0212, 0x0008},
+	{0x0000, 0x001f},
+};
+
+struct {unsigned short value, offset; } CICADA_V2_ASIX_HWINIT[] = {
+	{0x2a30, 0x001f}, {0x0212, 0x0008}, {0x52b5, 0x001f}, {0x0012, 0x0002},
+	{0x3002, 0x0001}, {0x87fa, 0x0000}, {0x52b5, 0x001f}, {0x000f, 0x0002},
+	{0x472a, 0x0001}, {0x8fa4, 0x0000}, {0x2a30, 0x001f}, {0x0212, 0x0008},
+	{0x0000, 0x001f},
+};
+
+struct {unsigned short value, offset; } AGERE_FAMILY_HWINIT[] = {
+	{0x0800, 0x0000}, {0x0007, 0x0012}, {0x8805, 0x0010}, {0xb03e, 0x0011},
+	{0x8808, 0x0010}, {0xe110, 0x0011}, {0x8806, 0x0010}, {0xb03e, 0x0011},
+	{0x8807, 0x0010}, {0xff00, 0x0011}, {0x880e, 0x0010}, {0xb4d3, 0x0011},
+	{0x880f, 0x0010}, {0xb4d3, 0x0011}, {0x8810, 0x0010}, {0xb4d3, 0x0011},
+	{0x8817, 0x0010}, {0x1c00, 0x0011}, {0x300d, 0x0010}, {0x0001, 0x0011},
+	{0x0002, 0x0012},
+};
+
+struct ax88178_data {
+	u16	EepromData;
+	u16	MediaLink;
+	int	UseGpio0;
+	int	UseRgmii;
+	u8	PhyMode;
+	u8	LedMode;
+	u8	BuffaloOld;
+};
+
+enum watchdog_state {
+	AX_NOP = 0,
+	CHK_LINK,			/* Routine A */
+	CHK_CABLE_EXIST,		/* Called by A */
+	CHK_CABLE_EXIST_AGAIN,		/* Routine B */
+	PHY_POWER_UP,			/* Called by B */
+	PHY_POWER_UP_BH,
+	PHY_POWER_DOWN,
+	CHK_CABLE_STATUS,		/* Routine C */
+	WAIT_AUTONEG_COMPLETE,
+	AX_SET_RX_CFG,
+	AX_CHK_AUTODETACH,
+};
+
+struct ax88772b_data {
+	struct usbnet *dev;
+	struct workqueue_struct *ax_work;
+	struct work_struct check_link;
+	unsigned long time_to_chk;
+	u16 psc;
+	u8 pw_enabled;
+	u8 Event;
+	u8 checksum;
+	u8 PhySelect:1;
+	u8 OperationMode:1;
+	u16 presvd_phy_advertise;
+	u16 presvd_phy_bmcr;
+
+	u32 ext_phy_oui;
+	u8  ext_phy_model;
+};
+
+/* define for MAC or PHY mode */
+#define OPERATION_MAC_MODE			0
+#define OPERATION_PHY_MODE			1
+
+struct ax88772a_data {
+	struct usbnet *dev;
+	struct workqueue_struct *ax_work;
+	struct work_struct check_link;
+	unsigned long autoneg_start;
+#define AX88772B_WATCHDOG	(6 * HZ)
+	u8 Event;
+	u8 TickToExpire;
+	u8 DlyIndex;
+	u8 DlySel;
+	u16 EepromData;
+	u16 presvd_phy_advertise;
+	u16 presvd_phy_bmcr;
+};
+
+struct ax88772_data {
+	struct usbnet *dev;
+	struct workqueue_struct *ax_work;
+	struct work_struct check_link;
+	unsigned long autoneg_start;
+	u8 Event;
+	u8 TickToExpire;
+	u16 presvd_phy_advertise;
+	u16 presvd_phy_bmcr;
+};
+
+#define AX_RX_CHECKSUM		1
+#define AX_TX_CHECKSUM		2
+
+/* This structure cannot exceed sizeof(unsigned long [5]) AKA 20 bytes */
+struct ax8817x_data {
+	u8 multi_filter[AX_MCAST_FILTER_SIZE];
+	int (*resume) (struct usb_interface *intf);
+	int (*suspend) (struct usb_interface *intf,
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 10)
+					pm_message_t message);
+#else
+					u32 message);
+#endif
+};
+
+struct ax88172_int_data {
+	u16 res1;
+#define AX_INT_PPLS_LINK		(1 << 0)
+#define AX_INT_SPLS_LINK		(1 << 1)
+#define AX_INT_CABOFF_UNPLUG		(1 << 7)
+	u8 link;
+	u16 res2;
+	u8 status;
+	u16 res3;
+} __attribute__ ((packed));
+
+#define AX_RXHDR_L4_ERR		(1 << 8)
+#define AX_RXHDR_L3_ERR		(1 << 9)
+
+#define AX_RXHDR_L4_TYPE_UDP		1
+#define AX_RXHDR_L4_TYPE_ICMP		2
+#define AX_RXHDR_L4_TYPE_IGMP		3
+#define AX_RXHDR_L4_TYPE_TCP		4
+#define AX_RXHDR_L4_TYPE_TCMPV6	5
+#define AX_RXHDR_L4_TYPE_MASK		7
+
+#define AX_RXHDR_L3_TYPE_IP		1
+#define AX_RXHDR_L3_TYPE_IPV6		2
+
+struct ax88772b_rx_header {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+	u16	len:11,
+		res1:1,
+		crc:1,
+		mii:1,
+		runt:1,
+		mc_bc:1;
+
+	u16	len_bar:11,
+		res2:5;
+
+	u8	vlan_ind:3,
+		vlan_tag_striped:1,
+		pri:3,
+		res3:1;
+
+	u8	l4_csum_err:1,
+		l3_csum_err:1,
+		l4_type:3,
+		l3_type:2,
+		ce:1;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+	u16	mc_bc:1,
+		runt:1,
+		mii:1,
+		crc:1,
+		res1:1,
+		len:11;
+
+	u16	res2:5,
+		len_bar:11;
+
+	u8	res3:1,
+		pri:3,
+		vlan_tag_striped:1,
+		vlan_ind:3;
+
+	u8	ce:1,
+		l3_type:2,
+		l4_type:3,
+		l3_csum_err:1,
+		l4_csum_err:1;
+#else
+#error	"Please fix <asm/byteorder.h>"
+#endif
+
+} __attribute__ ((packed));
+
+
+#endif /* __LINUX_USBNET_ASIX_H */
+
diff --git a/kernel/drivers/net/usb/ax88772C/axusbnet.c b/kernel/drivers/net/usb/ax88772C/axusbnet.c
new file mode 100644
index 0000000..2209a05
--- /dev/null
+++ b/kernel/drivers/net/usb/ax88772C/axusbnet.c
@@ -0,0 +1,1488 @@
+/*
+ * USB Network driver infrastructure
+ * Copyright (C) 2000-2005 by David Brownell
+ * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
+ *
+ * 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:  host-to-host cables,
+ * smart usb peripherals, and actual Ethernet adapters.
+ *
+ * These devices usually differ in terms of control protocols (if they
+ * even have one!) and sometimes they define new framing to wrap or batch
+ * Ethernet packets.  Otherwise, they talk to USB pretty much the same,
+ * so interface (un)binding, endpoint I/O queues, fault handling, and other
+ * issues can usefully be addressed by this framework.
+ */
+
+/* error path messages, extra info */
+#define	DEBUG
+/* more; success messages */
+/* #define	VERBOSE	*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ctype.h>
+#include <linux/ethtool.h>
+#include <linux/workqueue.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+/*#include <linux/usb/usbnet.h>*/
+
+#include "asix.h"
+#include "axusbnet.h"
+
+#define DRIVER_VERSION		"4.24.0"
+#define DRIVER_AUTHOR		"David Hollis"
+#define DRIVER_DESCRIPTION	"ASIX AX88772C_772B_772A_760_772_178 USB 2.0 \
+Ethernet Devices"
+#define DRIVER_LICENSE		"GPL"
+
+static void axusbnet_unlink_rx_urbs(struct usbnet *);
+
+static void
+ax8817x_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+				    u16 size, void *data);
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * 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).
+ *
+ * REVISIT qlens should be members of 'struct usbnet'; the goal is to
+ * let the USB host controller be busy for 5msec or more before an irq
+ * is required, under load.  Jumbograms change the equation.
+ */
+#define RX_MAX_QUEUE_MEMORY (60 * 1518)
+#define	RX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \
+			(RX_MAX_QUEUE_MEMORY/(dev)->rx_urb_size) : 4)
+#define	TX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \
+			(RX_MAX_QUEUE_MEMORY/(dev)->hard_mtu) : 4)
+
+/* reawaken network queue this soon after stopping; else watchdog barks */
+/* #define TX_TIMEOUT_JIFFIES	(5 * HZ) */
+#define TX_TIMEOUT_JIFFIES	(30 * 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)
+
+/* between wakeups */
+#define UNLINK_TIMEOUT_MS	3
+
+/*-------------------------------------------------------------------------*/
+
+static const char driver_name[] = "axusbnet";
+
+/* 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");
+
+/*-------------------------------------------------------------------------*/
+
+/* handles CDC Ethernet and many other network "bulk data" interfaces */
+static
+int axusbnet_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;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+static void intr_complete(struct urb *urb, struct pt_regs *regs);
+#else
+static void intr_complete(struct urb *urb);
+#endif
+
+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, GFP_KERNEL);
+	if (buf) {
+		dev->interrupt = usb_alloc_urb(0, GFP_KERNEL);
+		if (!dev->interrupt) {
+			kfree(buf);
+			return -ENOMEM;
+		} else {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14)
+			dev->interrupt->transfer_flags |= URB_ASYNC_UNLINK;
+#endif
+			usb_fill_int_urb(dev->interrupt, dev->udev, pipe,
+				buf, maxp, intr_complete, dev, period);
+			devdbg(dev,
+			       "status ep%din, %d bytes period %d",
+			       usb_pipeendpoint(pipe), maxp, period);
+		}
+	}
+	return 0;
+}
+
+/* Passes this packet up the stack, updating its accounting.
+ * Some link protocols batch packets, so their rx_fixup paths
+ * can return clones as well as just modify the original skb.
+ */
+static
+void axusbnet_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 %zu, 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);
+}
+
+/*-------------------------------------------------------------------------
+ *
+ * Network Device Driver (peer link to "Host Device", from USB host)
+ *
+ *-------------------------------------------------------------------------*/
+
+static
+int axusbnet_change_mtu(struct net_device *net, int new_mtu)
+{
+	struct usbnet	*dev = netdev_priv(net);
+	int		ll_mtu = new_mtu + net->hard_header_len;
+	int		old_hard_mtu = dev->hard_mtu;
+	int		old_rx_urb_size = dev->rx_urb_size;
+
+	if (new_mtu <= 0)
+		return -EINVAL;
+	/* no second zero-length packet read wanted after mtu-sized packets */
+	if ((ll_mtu % dev->maxpacket) == 0)
+		return -EDOM;
+	net->mtu = new_mtu;
+
+	dev->hard_mtu = net->mtu + net->hard_header_len;
+	if (dev->rx_urb_size == old_hard_mtu) {
+		dev->rx_urb_size = dev->hard_mtu;
+		if (dev->rx_urb_size > old_rx_urb_size)
+			axusbnet_unlink_rx_urbs(dev);
+	}
+
+	return 0;
+}
+
+static struct net_device_stats *axusbnet_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 enum skb_state defer_bh(struct usbnet *dev, struct sk_buff *skb,
+                struct sk_buff_head *list, enum skb_state state)
+{
+        unsigned long           flags;
+        enum skb_state          old_state;
+        struct skb_data *entry = (struct skb_data *) skb->cb;
+
+        spin_lock_irqsave(&list->lock, flags);
+        old_state = entry->state;
+        entry->state = state;
+        __skb_unlink(skb, list);
+
+        /* defer_bh() is never called with list == &dev->done.
+         * spin_lock_nested() tells lockdep that it is OK to take
+         * dev->done.lock here with list->lock held.
+         */
+        spin_lock_nested(&dev->done.lock, SINGLE_DEPTH_NESTING);
+
+        __skb_queue_tail(&dev->done, skb);
+        if (dev->done.qlen == 1)
+                tasklet_schedule(&dev->bh);
+
+        spin_unlock(&dev->done.lock);
+        spin_unlock_irqrestore(&list->lock, flags);
+	
+        return old_state;
+}
+
+/* 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 axusbnet_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);
+}
+
+/*-------------------------------------------------------------------------*/
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+static void rx_complete(struct urb *urb, struct pt_regs *regs);
+#else
+static void rx_complete(struct urb *urb);
+#endif
+
+static void rx_submit(struct usbnet *dev, struct urb *urb, gfp_t flags)
+{
+	struct sk_buff		*skb;
+	struct skb_data		*entry;
+	int			retval = 0;
+	unsigned long		lockflags;
+	size_t			size = dev->rx_urb_size;
+	struct driver_info	*info = dev->driver_info;
+	u8			align;
+
+	/* prevent rx skb allocation when error ratio is high */
+	if (test_bit(EVENT_RX_KILL, &dev->flags)) {
+		    usb_free_urb(urb);
+		    return;
+	}
+
+#if (AX_FORCE_BUFF_ALIGN)
+	align = 0;
+#else
+	if (!(info->flags & FLAG_HW_IP_ALIGNMENT))
+		align = NET_IP_ALIGN;
+	else
+		align = 0;
+#endif
+	skb = alloc_skb(size + align, flags);
+	if (skb == NULL) {
+
+		if (netif_msg_rx_err(dev))
+			devdbg(dev, "no rx skb");
+
+		if ((dev->rx_urb_size > 2048) && dev->rx_size) {
+			dev->rx_size--;
+			dev->rx_urb_size =
+				AX88772B_BULKIN_SIZE[dev->rx_size].size;
+
+			ax8817x_write_cmd_async(dev, 0x2A,
+				AX88772B_BULKIN_SIZE[dev->rx_size].byte_cnt,
+				AX88772B_BULKIN_SIZE[dev->rx_size].threshold,
+				0, NULL);
+		}
+
+		if (!(dev->flags & EVENT_RX_MEMORY))
+			axusbnet_defer_kevent(dev, EVENT_RX_MEMORY);
+		usb_free_urb(urb);
+		return;
+	}
+
+	if (align)
+		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);
+
+	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:
+			axusbnet_defer_kevent(dev, EVENT_RX_HALT);
+			break;
+		case -ENOMEM:
+			axusbnet_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)
+		axusbnet_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);
+	}
+}
+
+/*-------------------------------------------------------------------------*/
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+static void rx_complete(struct urb *urb, struct pt_regs *regs)
+#else
+static void rx_complete(struct urb *urb)
+#endif
+{
+	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;
+	enum skb_state          state;
+
+	skb_put(skb, urb->actual_length);
+	state = rx_done;
+	entry->urb = NULL;
+
+	switch (urb_status) {
+	/* success */
+	case 0:
+		if (skb->len < dev->net->hard_header_len) {
+			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 ETIMEDOUT/EILSEQ
+	 * storm, recovering as needed.
+	 */
+	case -EPIPE:
+		dev->stats.rx_errors++;
+		axusbnet_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:
+	case -ETIME:
+	case -EILSEQ:
+		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:
+		state = rx_cleanup;
+		entry->urb = urb;
+		urb = NULL;
+		break;
+
+	/* data overrun ... flush fifo? */
+	case -EOVERFLOW:
+		dev->stats.rx_over_errors++;
+		/* FALLTHROUGH */
+
+	default:
+		state = rx_cleanup;
+		dev->stats.rx_errors++;
+		if (netif_msg_rx_err(dev))
+			devdbg(dev, "rx status %d", urb_status);
+		break;
+	}
+
+	/* stop rx if packet error rate is high */
+        if (++dev->pkt_cnt > 30) {
+                dev->pkt_cnt = 0;
+                dev->pkt_err = 0;
+        } else {
+                if (state == rx_cleanup)
+                        dev->pkt_err++;
+                if (dev->pkt_err > 20)
+                        set_bit(EVENT_RX_KILL, &dev->flags);
+        }
+
+	state = defer_bh(dev, skb, &dev->rxq, state);
+
+	if (urb) {
+		if (netif_running(dev->net) &&
+		    !test_bit(EVENT_RX_HALT, &dev->flags) &&
+			 state != unlink_start) {
+			rx_submit(dev, urb, GFP_ATOMIC);
+			return;
+		}
+		usb_free_urb(urb);
+	}
+	if (netif_msg_rx_err(dev))
+		devdbg(dev, "no read resubmitted");
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+static void intr_complete(struct urb *urb, struct pt_regs *regs)
+#else
+static void intr_complete(struct urb *urb)
+#endif
+{
+	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 = NULL;
+        int                     count = 0;
+
+        spin_lock_irqsave (&q->lock, flags);
+        while (!skb_queue_empty(q)) {
+                struct skb_data         *entry;
+                struct urb              *urb;
+                int                     retval;
+
+                skb_queue_walk(q, skb) {
+                        entry = (struct skb_data *) skb->cb;
+                        if (entry->state != unlink_start)
+                                goto found;
+                }
+                break;
+found:
+                entry->state = unlink_start;
+                urb = entry->urb;
+
+                /*
+                 * Get reference count of the URB to avoid it to be
+                 * freed during usb_unlink_urb, which may trigger
+                 * use-after-free problem inside usb_unlink_urb since
+                 * usb_unlink_urb is always racing with .complete
+                 * handler(include defer_bh).
+                 */
+                usb_get_urb(urb);
+                spin_unlock_irqrestore(&q->lock, flags);
+                // during some PM-driven resume scenarios,
+                // these (async) unlinks complete immediately
+                retval = usb_unlink_urb (urb);
+                if (retval != -EINPROGRESS && retval != 0)
+                        printk(DEBUG "unlink urb err, %d\n", retval);
+                else
+                        count++;
+                usb_put_urb(urb);
+                spin_lock_irqsave(&q->lock, flags);
+        }
+        spin_unlock_irqrestore (&q->lock, flags);
+
+        return count;
+}
+
+/* Flush all pending rx urbs */
+/* minidrivers may need to do this when the MTU changes */
+
+static
+void axusbnet_unlink_rx_urbs(struct usbnet *dev)
+{
+	if (netif_running(dev->net)) {
+		(void) unlink_urbs(dev, &dev->rxq);
+		tasklet_schedule(&dev->bh);
+	}
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* precondition: never called in_interrupt */
+
+static
+int axusbnet_stop(struct net_device *net)
+{
+	struct usbnet		*dev = netdev_priv(net);
+	struct driver_info	*info = dev->driver_info;	
+	
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 18)
+	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(unlink_wakeup);
+#else
+	DECLARE_WAIT_QUEUE_HEAD(unlink_wakeup);
+#endif
+	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);
+
+	/* allow minidriver to stop correctly (wireless devices to turn off
+	 * radio etc) */
+	if (info->stop) {
+		int retval;
+		retval = info->stop(dev);
+		if (retval < 0 && netif_msg_ifdown(dev))
+			devinfo(dev,
+				"stop fail (%d) usbnet usb-%s-%s, %s",
+				retval,
+				dev->udev->bus->bus_name, dev->udev->devpath,
+				info->description);
+	}
+
+	if (!(info->flags & FLAG_AVOID_UNLINK_URBS)) {
+		int temp;
+		/* 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_empty(&dev->rxq)
+				&& !skb_queue_empty(&dev->txq)
+				&& !skb_queue_empty(&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 axusbnet_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);
+		if (retval < 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);
+		if (retval < 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;
+		}
+	}
+
+	/* reset rx error state */
+    dev->pkt_cnt = 0;
+    dev->pkt_err = 0;
+	clear_bit(EVENT_RX_KILL, &dev->flags);
+
+	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",
+			(int)RX_QLEN(dev), (int)TX_QLEN(dev), dev->net->mtu,
+			framing);
+	}
+
+	/* delay posting reads until we're fully open */
+	tasklet_schedule(&dev->bh);
+	return retval;
+done:
+	return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* ethtool methods; minidrivers may need to add some more, but
+ * they'll probably want to use this base set.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 20, 0)
+static
+int axusbnet_get_settings(struct net_device *net, struct ethtool_cmd *cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+
+	if (!dev->mii.mdio_read)
+		return -EOPNOTSUPP;
+
+	return mii_ethtool_gset(&dev->mii, cmd);
+}
+
+static
+int axusbnet_set_settings(struct net_device *net, struct ethtool_cmd *cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+	int retval;
+
+	if (!dev->mii.mdio_write)
+		return -EOPNOTSUPP;
+
+	retval = mii_ethtool_sset(&dev->mii, cmd);
+
+	/* link speed/duplex might have changed */
+	if (dev->driver_info->link_reset)
+		dev->driver_info->link_reset(dev);
+
+	return retval;
+
+}
+#endif
+static
+u32 axusbnet_get_link(struct net_device *net)
+{
+	struct usbnet *dev = netdev_priv(net);
+
+	/* If a check_connect is defined, return its result */
+	if (dev->driver_info->check_connect)
+		return dev->driver_info->check_connect(dev) == 0;
+
+	/* if the device has mii operations, use those */
+	if (dev->mii.mdio_read)
+		return mii_link_ok(&dev->mii);
+
+	/* Otherwise, dtrt for drivers calling netif_carrier_{on,off} */
+	return ethtool_op_get_link(net);
+}
+
+static
+int axusbnet_nway_reset(struct net_device *net)
+{
+	struct usbnet *dev = netdev_priv(net);
+
+	if (!dev->mii.mdio_write)
+		return -EOPNOTSUPP;
+
+	return mii_nway_restart(&dev->mii);
+}
+
+static
+void axusbnet_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info)
+{
+	struct usbnet *dev = netdev_priv(net);
+
+	strncpy(info->driver, dev->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 axusbnet_get_msglevel(struct net_device *net)
+{
+	struct usbnet *dev = netdev_priv(net);
+
+	return dev->msg_enable;
+}
+
+static
+void axusbnet_set_msglevel(struct net_device *net, u32 level)
+{
+	struct usbnet *dev = netdev_priv(net);
+
+	dev->msg_enable = level;
+}
+
+/* drivers may override default ethtool_ops in their bind() routine */
+static struct ethtool_ops axusbnet_ethtool_ops = {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 20, 0)
+	.get_settings		= axusbnet_get_settings,
+	.set_settings		= axusbnet_set_settings,
+#endif
+	.get_link		= axusbnet_get_link,
+	.nway_reset		= axusbnet_nway_reset,
+	.get_drvinfo		= axusbnet_get_drvinfo,
+	.get_msglevel		= axusbnet_get_msglevel,
+	.set_msglevel		= axusbnet_set_msglevel,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* 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.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+static void kevent(void *data)
+{
+	struct usbnet *dev = (struct usbnet *)data;
+#else
+static void kevent(struct work_struct *work)
+{
+	struct usbnet		*dev =
+		container_of(work, struct usbnet, kevent);
+#endif
+	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
+				&& status != -ESHUTDOWN) {
+			if (netif_msg_tx_err(dev))
+				deverr(dev, "can't clear tx halt, status %d",
+				       status);
+		} else {
+			clear_bit(EVENT_TX_HALT, &dev->flags);
+			if (status != -ESHUTDOWN)
+				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
+				&& status != -ESHUTDOWN) {
+			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);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14)
+			urb->transfer_flags |= URB_ASYNC_UNLINK;
+#endif
+			rx_submit(dev, urb, GFP_KERNEL);
+			tasklet_schedule(&dev->bh);
+		}
+	}
+
+	if (test_bit(EVENT_LINK_RESET, &dev->flags)) {
+		struct driver_info	*info = dev->driver_info;	
+
+		clear_bit(EVENT_LINK_RESET, &dev->flags);
+		if (info->link_reset) {
+			int retval;
+			retval = info->link_reset(dev);
+			if (retval < 0) {
+				devinfo(dev,
+					"link reset failed (%d) usbnet usb-%s-%s, %s",
+					retval,
+					dev->udev->bus->bus_name,
+					dev->udev->devpath,
+					info->description);
+			}
+		}
+	}
+
+	if (dev->flags)
+		devdbg(dev, "kevent done, flags = 0x%lx", dev->flags);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+static void tx_complete(struct urb *urb, struct pt_regs *regs)
+#else
+static void tx_complete(struct urb *urb)
+#endif
+{
+	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:
+			axusbnet_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:
+		case -ETIME:
+		case -EILSEQ:
+			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;
+	(void) defer_bh(dev, skb, &dev->txq, tx_done);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static
+void axusbnet_tx_timeout(struct net_device *net)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct driver_info	*info = dev->driver_info;
+
+	if (!(info->flags & FLAG_AVOID_UNLINK_URBS)) {
+		unlink_urbs(dev, &dev->txq);
+	}	
+	tasklet_schedule(&dev->bh);
+
+	/* FIXME: device recovery -- reset? */
+}
+
+/*-------------------------------------------------------------------------*/
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
+static int
+#else
+static netdev_tx_t
+#endif
+axusbnet_start_xmit(struct sk_buff *skb, struct net_device *net)
+{
+	struct usbnet		*dev = netdev_priv(net);
+	int			length;
+	struct urb		*urb = NULL;
+	struct skb_data		*entry;
+	struct driver_info	*info = dev->driver_info;
+	unsigned long		flags;
+	int retval;
+
+	/* 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;
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb) {
+		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;
+
+	usb_fill_bulk_urb(urb, dev->udev, dev->out, skb->data,
+			  skb->len, tx_complete, skb);
+
+	/* 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.
+	 */
+	if (!(info->flags & FLAG_SEND_ZLP) && (length % dev->maxpacket) == 0) {
+		urb->transfer_buffer_length++;
+		if (skb_tailroom(skb)) {
+			skb->data[skb->len] = 0;
+			__skb_put(skb, 1);
+		}
+	}
+
+	spin_lock_irqsave(&dev->txq.lock, flags);
+
+	switch ((retval = usb_submit_urb(urb, GFP_ATOMIC))) {
+	case -EPIPE:
+		netif_stop_queue(net);
+		axusbnet_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:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0)
+		net->trans_start = jiffies;
+#else		
+		netif_trans_update(net);
+#endif
+		__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:
+		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 NETDEV_TX_OK;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* tasklet (work deferred from completions, in_irq) or timer */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
+static void axusbnet_bh(unsigned long param)
+#else
+static void axusbnet_bh (struct timer_list *t)
+#endif
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
+	struct usbnet		*dev = (struct usbnet *) param;
+#else
+	struct usbnet		*dev = from_timer(dev, t, delay);
+#endif	
+	struct sk_buff		*skb;
+	struct skb_data		*entry = NULL;
+
+	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);
+		}
+	}
+
+	/* restart RX again after disabling due to high error rate */
+         clear_bit(EVENT_RX_KILL, &dev->flags);
+
+	/* 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 = NULL;
+			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) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14)
+					urb->transfer_flags |= URB_ASYNC_UNLINK;
+#endif
+					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 axusbnet_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 '%s' usb-%s-%s, %s",
+			intf->dev.driver->name,
+			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);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* precondition: never called in_interrupt */
+
+static int
+axusbnet_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;
+	const char			*name;
+
+	name = udev->dev.driver->name;
+	info = (struct driver_info *) prod->driver_info;
+	if (!info) {
+		printk(KERN_ERR "blacklisted by %s\n", 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) {
+		printk(KERN_ERR "can't kmalloc dev");
+		goto out;
+	}
+
+	dev = netdev_priv(net);
+	dev->udev = xdev;
+	dev->intf = udev;
+	dev->driver_info = info;
+	dev->driver_name = name;
+	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);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
+	dev->bh.func = axusbnet_bh;
+	dev->bh.data = (unsigned long) dev;
+#else
+	dev->bh.func = (void (*)(unsigned long))axusbnet_bh;
+	dev->bh.data = (unsigned long)&dev->delay;
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+	INIT_WORK(&dev->kevent, kevent, dev);
+#else
+	INIT_WORK(&dev->kevent, kevent);
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
+	dev->delay.function = axusbnet_bh;
+	dev->delay.data = (unsigned long) dev;
+	init_timer(&dev->delay);
+#else
+	timer_setup(&dev->delay, axusbnet_bh, 0);
+#endif
+	/* mutex_init(&dev->phy_mutex); */
+
+	dev->net = net;
+
+	/* rx and tx sides can use different message sizes;
+	 * bind() should set rx_urb_size in that case.
+	 */
+	dev->hard_mtu = net->mtu + net->hard_header_len;
+
+#if 0
+	/* dma_supported() is deeply broken on almost all architectures */
+	/* possible with some EHCI controllers */
+	if (dma_supported(&udev->dev, DMA_BIT_MASK(64)))
+		net->features |= NETIF_F_HIGHDMA;
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
+	net->open		= axusbnet_open,
+	net->stop		= axusbnet_stop,
+	net->hard_start_xmit	= axusbnet_start_xmit,
+	net->tx_timeout	= axusbnet_tx_timeout,
+	net->get_stats = axusbnet_get_stats;
+#endif
+
+	net->watchdog_timeo = TX_TIMEOUT_JIFFIES;
+	net->ethtool_ops = &axusbnet_ethtool_ops;
+
+	/* allow device-specific bind/init procedures */
+	/* NOTE net->name still not usable ... */
+	status = info->bind(dev, udev);
+	if (status < 0) {
+		deverr(dev, "Binding device failed: %d", status);
+		goto out1;
+	}
+
+	/* maybe the remote can't receive an Ethernet MTU */
+	if (net->mtu > (dev->hard_mtu - net->hard_header_len))
+		net->mtu = dev->hard_mtu - net->hard_header_len;
+
+	status = init_status(dev, udev);
+	if (status < 0)
+		goto out3;
+
+	if (!dev->rx_urb_size)
+		dev->rx_urb_size = dev->hard_mtu;
+	dev->maxpacket = usb_maxpacket(dev->udev, dev->out, 1);
+
+	SET_NETDEV_DEV(net, &udev->dev);
+	status = register_netdev(net);
+	if (status) {
+		deverr(dev, "net device registration failed: %d", status);
+		goto out3;
+	}
+
+	if (netif_msg_probe(dev))
+		devinfo(dev, "register '%s' at usb-%s-%s, %s, %pM",
+			udev->dev.driver->name,
+			xdev->bus->bus_name, xdev->devpath,
+			dev->driver_info->description,
+			net->dev_addr);
+
+	/* 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;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * suspend the whole driver as soon as the first interface is suspended
+ * resume only when the last interface is resumed
+ */
+
+static int axusbnet_suspend(struct usb_interface *intf,
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 10)
+pm_message_t message)
+#else
+u32 message)
+#endif
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+
+	if (!dev->suspend_count++) {
+		/*
+		 * accelerate emptying of the rx and queues, to avoid
+		 * having everything error out.
+		 */
+		netif_device_detach(dev->net);
+		(void) unlink_urbs(dev, &dev->rxq);
+		(void) unlink_urbs(dev, &dev->txq);
+		usb_kill_urb(dev->interrupt);
+		/*
+		 * reattach so runtime management can use and
+		 * wake the device
+		 */
+		netif_device_attach(dev->net);
+	}
+	return 0;
+}
+
+static int
+axusbnet_resume(struct usb_interface *intf)
+{
+	struct usbnet	*dev = usb_get_intfdata(intf);
+	int	retval = 0;
+
+	if (!--dev->suspend_count)
+		tasklet_schedule(&dev->bh);
+
+	retval = init_status(dev, intf);
+	if (retval < 0)
+		return retval;
+
+	if (dev->interrupt) {
+		retval = usb_submit_urb(dev->interrupt, GFP_KERNEL);
+		if (retval < 0 && netif_msg_ifup(dev))
+			deverr(dev, "intr submit %d", retval);
+	}
+
+	return retval;
+}
+
diff --git a/kernel/drivers/net/usb/ax88772C/axusbnet.h b/kernel/drivers/net/usb/ax88772C/axusbnet.h
new file mode 100644
index 0000000..d2eac24
--- /dev/null
+++ b/kernel/drivers/net/usb/ax88772C/axusbnet.h
@@ -0,0 +1,212 @@
+/*
+ * USB Networking Link Interface
+ *
+ * Copyright (C) 2000-2005 by David Brownell <dbrownell@users.sourceforge.net>
+ * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
+ *
+ * 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
+ */
+
+#ifndef	__LINUX_USB_USBNET_H
+#define	__LINUX_USB_USBNET_H
+
+#ifndef gfp_t
+#define gfp_t int
+#endif
+
+/* interface from usbnet core to each USB networking link we handle */
+struct usbnet {
+	/* housekeeping */
+	struct usb_device	*udev;
+	struct usb_interface	*intf;
+	struct driver_info	*driver_info;
+	const char		*driver_name;
+	void			*driver_priv;
+	wait_queue_head_t	*wait;
+	/* struct mutex		phy_mutex; */
+	unsigned char		suspend_count;
+	unsigned char           pkt_cnt, pkt_err;
+
+	/* 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];
+	u32			xid;
+	u32			hard_mtu;	/* count any extra framing */
+	size_t			rx_urb_size;	/* size for rx urbs */
+	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 sk_buff_head	rxq_pause;
+	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
+#		define EVENT_LINK_RESET	4
+#		define EVENT_RX_PAUSED	5
+#		define EVENT_RX_KILL    10
+
+	void			*priv;	/* point to minidriver private data */
+	unsigned char		rx_size;
+	unsigned char		reg_monitor;
+};
+
+static inline struct usb_driver *driver_of(struct usb_interface *intf)
+{
+	return to_usb_driver(intf->dev.driver);
+}
+
+/* interface from the device/framing level "minidriver" to core */
+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 */
+#define FLAG_WLAN	0x0080		/* use "wlan%d" names */
+#define FLAG_AVOID_UNLINK_URBS	0x0100	/* don't unlink urbs at usbnet_stop() */
+#define FLAG_SEND_ZLP	0x0200		/* hw requires ZLPs are sent */
+#define FLAG_HW_IP_ALIGNMENT	0x0400	/* AX88772B support hardware IP alignment */
+
+
+	/* 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 *);
+
+	/* stop device ... can sleep */
+	int	(*stop)(struct usbnet *);
+
+	/* see if peer is connected ... can sleep */
+	int	(*check_connect)(struct usbnet *);
+
+	/* for status polling */
+	void	(*status)(struct usbnet *, struct urb *);
+
+	/* link reset handling, called from defer_kevent */
+	int	(*link_reset)(struct usbnet *);
+
+	/* 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, gfp_t flags);
+
+	/* early initialization code, can sleep. This is for minidrivers
+	 * having 'subminidrivers' that need to do extra initialization
+	 * right after minidriver have initialized hardware. */
+	int	(*early_init)(struct usbnet *dev);
+
+	/* called by minidriver when receiving indication */
+	void	(*indication)(struct usbnet *dev, void *ind, int indlen);
+
+	/* for new devices, use the descriptor-reading code instead */
+	int		in;		/* rx endpoint */
+	int		out;		/* tx endpoint */
+
+	unsigned long	data;		/* Misc driver specific data */
+};
+
+/* Drivers that reuse some of the standard USB CDC infrastructure
+ * (notably, using multiple interfaces according to the CDC
+ * union descriptor) get some helper code.
+ */
+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;
+};
+
+/* CDC and RNDIS support the same host-chosen packet filters for IN transfers */
+#define	DEFAULT_FILTER	(USB_CDC_PACKET_TYPE_BROADCAST \
+			|USB_CDC_PACKET_TYPE_ALL_MULTICAST \
+			|USB_CDC_PACKET_TYPE_PROMISCUOUS \
+			|USB_CDC_PACKET_TYPE_DIRECTED)
+
+
+/* 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,
+	unlink_start
+};
+
+struct skb_data {	/* skb->cb is one of these */
+	struct urb		*urb;
+	struct usbnet		*dev;
+	enum skb_state		state;
+	size_t			length;
+};
+
+#ifndef skb_queue_walk_safe
+#define skb_queue_walk_safe(queue, skb, tmp)				\
+			for (skb = (queue)->next, tmp = skb->next;	\
+			skb != (struct sk_buff *)(queue);		\
+			skb = tmp, tmp = skb->next)
+#endif
+
+/* messaging support includes the interface name, so it must not be
+ * used before it has one ... notably, in minidriver bind() calls.
+ */
+#ifdef DEBUG
+#define devdbg(usbnet, fmt, arg...) \
+	printk("%s: " fmt "\n" , (usbnet)->net->name , ## arg)
+#else
+#define devdbg(usbnet, fmt, arg...) \
+	({ if (0) printk("%s: " fmt "\n" , (usbnet)->net->name , \
+		## arg); 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); \
+
+
+#endif /* __LINUX_USB_USBNET_H */
diff --git a/kernel/drivers/net/usb/ax88772C/readme b/kernel/drivers/net/usb/ax88772C/readme
new file mode 100644
index 0000000..3bb941d
--- /dev/null
+++ b/kernel/drivers/net/usb/ax88772C/readme
@@ -0,0 +1,92 @@
+============================================================================
+ASIX AX88178 USB 2.0 Gigabit Ethernet Network Adapter
+ASIX AX88772 USB 2.0 Fast Ethernet Network Adapter
+ASIX AX88772A USB 2.0 Fast Ethernet Network Adapter
+ASIX AX88760 USB 2.0 MTT HUB and USB 2.0 to Fast Ethernet Combo Controller
+ASIX AX88772B USB 2.0 Fast Ethernet Network Adapter
+ASIX AX88772C USB 2.0 Fast Ethernet Network Adapter
+Driver Compilation & Configuration on the Linux
+============================================================================
+
+This driver has been verified on Linux kernel 2.6.14 and later.
+
+================
+Prerequisites
+================
+
+Prepare to build the driver, you need the Linux kernel sources installed on the
+build machine, and make sure that the version of the running kernel must match
+the installed kernel sources. If you don't have the kernel sources, you can get
+it from www.kernel.org or contact to your Linux distributor. If you don't know
+how to do, please refer to KERNEL-HOWTO.
+
+Note: Please make sure the kernel is built with one of the "Support for
+       Host-side, EHCI, OHCI, or UHCI" option support.
+
+
+===========================
+Conditional Compilation Flag
+===========================
+[AX_FORCE_BUFF_ALIGN]
+Description:
+       There are alignment issues of USB buffer in some USB host controllers.
+       Turn on this flag if the implementation of your USB host controller
+       cannot handle non-double word aligned buffer.
+       When turn on this flag, driver will fixup egress packet aligned on double
+       word boundary before deliver to USB host controller.
+Setting:
+	1 -> Enable TX buffers forced on double word alignment.
+	0 -> Disable TX buffers forced on double word alignment.
+Default:
+	0
+
+
+================
+Getting Start
+================
+
+1. Extract the compressed driver source file to your template directory by the
+   following command:
+
+	[root@localhost template]# tar -xf DRIVER_SOURCE_PACKAGE.tar.bz2
+
+2. Now, the driver source files should be extracted under the current directory.
+   Executing the following command to compile the driver:
+ 
+	[root@localhost template]# make
+			
+3. If the compilation is well, the asix.ko will be created under the current
+   directory.
+ 
+4. If you want to use modprobe command to mount the driver, executing the
+   following command to install the driver into your Linux:
+
+	[root@localhost template]# make install
+
+
+================
+Usage
+================
+
+1. If you want to load the driver manually, go to the driver directory and
+   execute the following commands:
+
+	[root@localhost template]# insmod asix.ko
+
+2. If you had installed the driver during driver compilation, then you can use
+   the following command to load the driver automatically.
+
+	[root@localhost anywhere]# modprobe asix
+
+If you want to unload the driver, just executing the following command:
+
+	[root@localhost anywhere]# rmmod asix
+
+================
+Special define
+================
+There is a RX_SKB_COPY preprocessor define in asix.h can solve rx_throttle problem
+in some version of 3.4 Linux kernel. Removing the comment before the define can enable
+this feature.
+
+

--
Gitblit v1.6.2