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 = 𝔦
+ 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