usb ethernet: support dm9601, dm9620
authorhwg <hwg@rock-chips.com>
Wed, 7 May 2014 09:29:10 +0000 (17:29 +0800)
committerhwg <hwg@rock-chips.com>
Wed, 7 May 2014 09:29:20 +0000 (17:29 +0800)
arch/arm/configs/rockchip_defconfig
drivers/net/usb/Kconfig [changed mode: 0644->0755]
drivers/net/usb/Makefile [changed mode: 0644->0755]
drivers/net/usb/axusbnet.c [deleted file]
drivers/net/usb/axusbnet.h [deleted file]
drivers/net/usb/dm9601.c
drivers/net/usb/dm9620.c

index 19b5832913f7bf64dafc486afbfd59db31a5dece..690a64f7dca1993c948c878d452fe632479200c9 100755 (executable)
@@ -256,6 +256,7 @@ CONFIG_USB_USBNET=y
 CONFIG_USB_NET_CDC_EEM=y
 CONFIG_USB_NET_CDC_MBIM=y
 CONFIG_USB_NET_DM9601=y
+CONFIG_USB_NET_DM9620=y
 CONFIG_USB_NET_SMSC75XX=y
 CONFIG_USB_NET_SMSC95XX=y
 CONFIG_USB_NET_GL620A=y
old mode 100644 (file)
new mode 100755 (executable)
index 287cc62..05e003d
@@ -272,6 +272,14 @@ config USB_NET_DM9601
          This option adds support for Davicom DM9601 based USB 1.1
          10/100 Ethernet adapters.
 
+config USB_NET_DM9620
+       tristate "Davicom DM9620 based USB 2.0 10/100 ethernet devices"
+       depends on USB_USBNET
+       select CRC32
+       help
+         This option adds support for Davicom DM9620 based USB 2.0
+         10/100 Ethernet adapters.
+
 config USB_NET_SMSC75XX
        tristate "SMSC LAN75XX based USB 2.0 gigabit ethernet devices"
        depends on USB_USBNET
old mode 100644 (file)
new mode 100755 (executable)
index 9ab5c9d..7d95e5c
@@ -14,6 +14,7 @@ obj-$(CONFIG_USB_NET_AX88179_178A)      += ax88179_178a.o
 obj-$(CONFIG_USB_NET_CDCETHER) += cdc_ether.o
 obj-$(CONFIG_USB_NET_CDC_EEM)  += cdc_eem.o
 obj-$(CONFIG_USB_NET_DM9601)   += dm9601.o
+obj-$(CONFIG_USB_NET_DM9620)   += dm9620.o
 obj-$(CONFIG_USB_NET_SMSC75XX) += smsc75xx.o
 obj-$(CONFIG_USB_NET_SMSC95XX) += smsc95xx.o
 obj-$(CONFIG_USB_NET_GL620A)   += gl620a.o
diff --git a/drivers/net/usb/axusbnet.c b/drivers/net/usb/axusbnet.c
deleted file mode 100755 (executable)
index 1424807..0000000
+++ /dev/null
@@ -1,1366 +0,0 @@
-/*
- * 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.
- */
-
-#define        DEBUG                   // error path messages, extra info
-// #define     VERBOSE                 // more; success messages
-
-#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"
-#define  TAG "AX88xx------>"
-
-static void axusbnet_unlink_rx_urbs(struct usbnet *);
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * 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 void
-defer_bh(struct usbnet *dev, struct sk_buff *skb, struct sk_buff_head *list)
-{
-       unsigned long           flags;
-
-       spin_lock_irqsave(&list->lock, flags);
-       __skb_unlink(skb, list);
-       spin_unlock(&list->lock);
-       spin_lock(&dev->done.lock);
-       __skb_queue_tail(&dev->done, skb);
-       if (dev->done.qlen == 1)
-               tasklet_schedule(&dev->bh);
-       spin_unlock_irqrestore(&dev->done.lock, flags);
-}
-
-/* some work can't be done in tasklets, so we use keventd
- *
- * NOTE:  annoying asymmetry:  if it's active, schedule_work() fails,
- * but tasklet_schedule() doesn't.  hope the failure is rare.
- */
-static
-void 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;
-
-#if (AX_FORCE_BUFF_ALIGN)
-       align = 0;
-#else
-       if (!(info->flags & FLAG_HW_IP_ALIGNMENT))
-               align = NET_IP_ALIGN;
-       else
-               align = 0;
-#endif
-
-       if ((skb = alloc_skb (size + align, flags)) == NULL) {
-               if (netif_msg_rx_err (dev))
-                       devdbg (dev, "no rx skb");
-               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))
-                       deverr (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;
-
-       skb_put (skb, urb->actual_length);
-       entry->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))
-                      ;//      deverr (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))
-               ;//     deverr (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))
-                       ;//     deverr (dev, "rx throttle %d", urb_status);
-               }
-block:
-               entry->state = rx_cleanup;
-               entry->urb = urb;
-               urb = NULL;
-               break;
-
-       /* data overrun ... flush fifo? */
-       case -EOVERFLOW:
-               dev->stats.rx_over_errors++;
-               // FALLTHROUGH
-
-       default:
-               entry->state = rx_cleanup;
-               dev->stats.rx_errors++;
-               if (netif_msg_rx_err (dev))
-       ;//             deverr (dev, "rx status %d", urb_status);
-               break;
-       }
-
-       defer_bh(dev, skb, &dev->rxq);
-
-       if (urb) {
-               if (netif_running (dev->net)
-                               && !test_bit (EVENT_RX_HALT, &dev->flags)) {
-                       rx_submit (dev, urb, GFP_ATOMIC);
-                       return;
-               }
-               usb_free_urb (urb);
-       }
-       if (netif_msg_rx_err (dev))
-               deverr (dev, "no read resubmitted");
-}
-extern void dwc_otg_clear_halt(struct urb *_urb);
-#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);
-               break;
-       //      return;
-
-       /* NOTE:  not throttling like RX/TX, since this endpoint
-        * already polls infrequently
-        */
-       default:
-               devdbg (dev, "intr status %d", status);
-               if(status < 0)
-                       dwc_otg_clear_halt(urb);
-               break;
-       }
-
-       if (!netif_running (dev->net))
-               return;
-
-       memset(urb->transfer_buffer, 0, urb->transfer_buffer_length);
-       status = usb_submit_urb (urb, GFP_ATOMIC);
-       if (status != 0 && netif_msg_timer (dev))
-               deverr(dev, "intr resubmit --> %d", status);
-}
-
-/*-------------------------------------------------------------------------*/
-
-// unlink pending rx/tx; completion handlers do all other cleanup
-
-static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
-{
-       unsigned long           flags;
-       struct sk_buff          *skb, *skbnext;
-       int                     count = 0;
-
-       spin_lock_irqsave (&q->lock, flags);
-       skb_queue_walk_safe(q, skb, skbnext) {
-               struct skb_data         *entry;
-               struct urb              *urb;
-               int                     retval;
-
-               entry = (struct skb_data *) skb->cb;
-               urb = entry->urb;
-
-               // during some PM-driven resume scenarios,
-               // these (async) unlinks complete immediately
-               retval = usb_unlink_urb (urb);
-               if (retval != -EINPROGRESS && retval != 0)
-                       devdbg (dev, "unlink urb err, %d", retval);
-               else
-                       count++;
-       }
-       spin_unlock_irqrestore (&q->lock, flags);
-       return count;
-}
-
-// 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;
-       int                     temp;
-       int                     retval;
-#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) {
-               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)) {
-               /* 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)) < 0) {
-               if (netif_msg_ifup (dev))
-                       devinfo (dev,
-                               "open reset fail (%d) usbnet usb-%s-%s, %s",
-                               retval,
-                               dev->udev->bus->bus_name, dev->udev->devpath,
-                       info->description);
-               goto done;
-       }
-
-       // insist peer be connected
-       if (info->check_connect && (retval = info->check_connect (dev)) < 0) {
-               if (netif_msg_ifup (dev))
-                       devdbg (dev, "can't open; %d", retval);
-               goto done;
-       }
-
-       /* start any status interrupt transfer */
-       if (dev->interrupt) {
-               retval = usb_submit_urb (dev->interrupt, GFP_KERNEL);
-               if (retval < 0) {
-                       if (netif_msg_ifup (dev))
-                               deverr (dev, "intr submit %d", retval);
-                       goto done;
-               }
-       }
-
-       netif_start_queue (net);
-       if (netif_msg_ifup (dev)) {
-               char    *framing;
-
-               if (dev->driver_info->flags & FLAG_FRAMING_NC)
-                       framing = "NetChip";
-               else if (dev->driver_info->flags & FLAG_FRAMING_GL)
-                       framing = "GeneSys";
-               else if (dev->driver_info->flags & FLAG_FRAMING_Z)
-                       framing = "Zaurus";
-               else if (dev->driver_info->flags & FLAG_FRAMING_RN)
-                       framing = "RNDIS";
-               else if (dev->driver_info->flags & FLAG_FRAMING_AX)
-                       framing = "ASIX";
-               else
-                       framing = "simple";
-
-               devinfo (dev, "open: enable queueing "
-                               "(rx %d, tx %d) mtu %d %s framing",
-                       (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.
- */
-
-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;
-
-}
-
-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 = {
-       .get_settings           = axusbnet_get_settings,
-       .set_settings           = axusbnet_set_settings,
-       .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)) {
-printk ("EVENT_TX_HALT\n");
-               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)) {
-printk ("EVENT_RX_HALT\n");
-               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;
-printk ("EVENT_RX_MEMORY\n");
-               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;
-               int                     retval = 0;
-
-               clear_bit (EVENT_LINK_RESET, &dev->flags);
-               if(info->link_reset && (retval = info->link_reset(dev)) < 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;
-       defer_bh(dev, skb, &dev->txq);
-}
-
-/*-------------------------------------------------------------------------*/
-
-static
-void axusbnet_tx_timeout (struct net_device *net)
-{
-       struct usbnet           *dev = netdev_priv(net);
-
-       devdbg(dev,"---> %s %d\n",__FUNCTION__, __LINE__);
-       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;
-
-       if (!(urb = usb_alloc_urb (0, GFP_ATOMIC))) {
-               if (netif_msg_tx_err (dev))
-                       devdbg (dev, "no urb");
-               goto drop;
-       }
-
-       entry = (struct skb_data *) skb->cb;
-       entry->urb = urb;
-       entry->dev = dev;
-       entry->state = tx_start;
-       entry->length = length;
-
-       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:
-               net->trans_start = jiffies;
-               __skb_queue_tail (&dev->txq, skb);
-               if (dev->txq.qlen >= TX_QLEN (dev))
-                       netif_stop_queue (net);
-       }
-       spin_unlock_irqrestore (&dev->txq.lock, flags);
-
-       if (retval) {
-               if (netif_msg_tx_err (dev))
-                       devdbg (dev, "drop, code %d", retval);
-drop:
-               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
-
-static void axusbnet_bh (unsigned long param)
-{
-       struct usbnet           *dev = (struct usbnet *) param;
-       struct sk_buff          *skb;
-       struct skb_data         *entry;
-
-       while ((skb = skb_dequeue (&dev->done))) {
-               entry = (struct skb_data *) skb->cb;
-               switch (entry->state) {
-               case rx_done:
-                       entry->state = rx_cleanup;
-                       rx_process (dev, skb);
-                       continue;
-               case tx_done:
-               case rx_cleanup:
-                       usb_free_urb (entry->urb);
-                       dev_kfree_skb (skb);
-                       continue;
-               default:
-                       devdbg (dev, "bogus skb state %d", entry->state);
-               }
-       }
-
-       // waiting for all pending urbs to complete?
-       if (dev->wait) {
-               if ((dev->txq.qlen + dev->rxq.qlen + dev->done.qlen) == 0) {
-                       wake_up (dev->wait);
-               }
-
-       // or are we maybe short a few urbs?
-       } else if (netif_running (dev->net)
-                       && netif_device_present (dev->net)
-                       && !timer_pending (&dev->delay)
-                       && !test_bit (EVENT_RX_HALT, &dev->flags)) {
-               int     temp = dev->rxq.qlen;
-               int     qlen = RX_QLEN (dev);
-
-               if (temp < qlen) {
-                       struct urb      *urb;
-                       int             i;
-
-                       // don't refill the queue all at once
-                       for (i = 0; i < 10 && dev->rxq.qlen < qlen; i++) {
-                               urb = usb_alloc_urb (0, GFP_ATOMIC);
-                               if (urb != NULL) {
-#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) {
-               dbg ("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 | NETIF_MSG_IFDOWN |NETIF_MSG_IFUP);
-       skb_queue_head_init (&dev->rxq);
-       skb_queue_head_init (&dev->txq);
-       skb_queue_head_init (&dev->done);
-       dev->bh.func = axusbnet_bh;
-       dev->bh.data = (unsigned long) dev;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
-       INIT_WORK (&dev->kevent, kevent, dev);
-#else
-       INIT_WORK (&dev->kevent, kevent);
-#endif
-
-       dev->delay.function = axusbnet_bh;
-       dev->delay.data = (unsigned long) dev;
-       init_timer (&dev->delay);
-//     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;
-
-       info->flags |= FLAG_AVOID_UNLINK_URBS;
-
-       // 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;
-       } else  {
-               printk("----> %s %d:bind %s\n",__FUNCTION__,__LINE__,info->description);
-       }
-
-       /* 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);
-               /*
-                * 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);
-
-       if (!--dev->suspend_count)
-               tasklet_schedule (&dev->bh);
-
-       return 0;
-}
-
diff --git a/drivers/net/usb/axusbnet.h b/drivers/net/usb/axusbnet.h
deleted file mode 100755 (executable)
index d492de3..0000000
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * 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;
-
-       /* 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
-
-       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
-};
-
-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 */
index c0bfc818c701ce8359f3f36caa494d570cf43963..f3d5e216933dd78cc577e1421f0e9313983fed8b 100644 (file)
@@ -370,6 +370,7 @@ static int dm9601_bind(struct usbnet *dev, struct usb_interface *intf)
         * ethernet frames.
         */
        dev->rx_urb_size = dev->net->mtu + ETH_HLEN + DM_RX_OVERHEAD + 1;
+        dev->rx_urb_size = (dev->rx_urb_size > 2048) ? dev->rx_urb_size : 2048;
 
        dev->mii.dev = dev->net;
        dev->mii.mdio_read = dm9601_mdio_read;
index 9df1e01d47d6688c9f23b071c74fac9dcb0ac846..699bab627866534033bcc89cd92a727a2e2223bc 100755 (executable)
  *        Support DM9685
  *        Transmit Check Sum Control by Optopn (Source Code Default: Disable)
  *        Recieve Drop Check Sum Error Packet Disable as chip default
- * V1.5 - Support RK2818 (Debug the Register Function) 
+ * V1.5 - Support RK2818 (Debug the Register Function)
+ * V1.6 - Solve compiler issue for Linux 2.6.35
+ * V1.7 - Enable MAC Layer Flow Control and define debug_message for linux version update.
+ * V1.8 - Enable PHY Layer Flow Control, clear debug code, setup default phy_id value is 1.
+ *        Update dm9620_mdio_read and dm9620_mdio_write.
+ *        Fix bug of ethtool eeprom write       
+ * V1.9 - Fixed "deverr" line 367 error in Linux 2.6.38
+ * V2.0 - Fixed "dm9620_set_multicast" function CRC bug.
+ * V2.1 - Add 802.3az for dm9621a
+ * V2.2 - Add PID=0x1269 support CDC mode.
+ * V2.3 - Add PID=0x0269 support CDC mode.       
+ * V2.41 - Support Linux 3.6.9    
+ * V2.42 - Work to V2.42 according to "DM9620 BulkOut Â¸Ã‰Â¤B¤À¸Ñ.doc"
+ * V2.43 - Special suport for DM9621A in the table 'products'
+ * V2.45 - Fix the function TxStyle(), correct to be (len%2) from (len%1). 20131211.
  */
 
 //#define DEBUG
-
-#define RK2818
+#define LNX_DM9620_VER_STR  "V2.45"
 
 
 #include <linux/module.h>
-//#include <linux/kernel.h> // new v1.3
 #include <linux/sched.h>
 #include <linux/stddef.h>
 #include <linux/init.h>
@@ -55,6 +67,7 @@
 /* registers */
 #define DM_NET_CTRL    0x00
 #define DM_RX_CTRL     0x05
+#define DM_FLOW_CTRL   0x0a
 #define DM_SHARED_CTRL 0x0b
 #define DM_SHARED_ADDR 0x0c
 #define DM_SHARED_DATA 0x0d    /* low + high */
 #define DM_MCAST_ADDR  0x16    /* 8 bytes */
 #define DM_GPR_CTRL    0x1e
 #define DM_GPR_DATA    0x1f
+#define DM_PID      0x2a
 #define DM_XPHY_CTRL   0x2e
 #define DM_TX_CRC_CTRL 0x31
 #define DM_RX_CRC_CTRL 0x32 
 #define DM_SMIREG       0x91
 #define USB_CTRL       0xf4
 #define PHY_SPEC_CFG   20
+#define DM_TXRX_M       0x5C
 
 #define MD96XX_EEPROM_MAGIC    0x9620
 #define DM_MAX_MCAST   64
 #define DM_TIMEOUT     1000
 #define DM_MODE9620     0x80
 #define DM_TX_CS_EN    0        /* Transmit Check Sum Control */
+#define DM9620_PHY_ID 1      /* Stone add For kernel read phy register */
 
 struct dm96xx_priv {
-       int     flag_fail_count;
-       u8     mode_9620;       
+  //int        flag_fail_count; // EVER RX-DBG
+    int flg_txdbg; // NOW TX-DBG
+       u8  mode_9620;  
+       u8      tx_fix_mod;     
 };     
-
-
+#if defined(DEBUG)
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,33)
+#define dm9620_print(__dev, format, args...) netdev_dbg((__dev)->net, format, ##args) 
+#define dm9620_err(__dev, format, args...) netdev_err((__dev)->net, format, ##args)
+#else if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,33)
+#define dm9620_print(dev, format, args...) devdbg(dev, format, ##args)
+#define dm9620_err(dev, format, args...) deverr(dev, format, ##args)
+#endif
+#else
+#define dm9620_print(dev, format, args...) printk(format, ##args)
+#define dm9620_err(dev, format, args...) printk(format, ##args)
+#endif
 static int dm_read(struct usbnet *dev, u8 reg, u16 length, void *data)
 {
-       devdbg(dev, "dm_read() reg=0x%02x length=%d", reg, length);
+//     dm9620_print(dev, "dm_read() reg=0x%02x length=%d", reg, length);
        return usb_control_msg(dev->udev,
                               usb_rcvctrlpipe(dev->udev, 0),
                               DM_READ_REGS,
@@ -101,12 +129,6 @@ static int dm_read(struct usbnet *dev, u8 reg, u16 length, void *data)
 
 static int dm_read_reg(struct usbnet *dev, u8 reg, u8 *value)
 {
-//     return dm_read(dev, reg, 1, value);
-
-       //__le16 w;
-       //int ret = dm_read(dev, reg, 2, &w);  // usb_submit_urb
-       //*value= (u8)(w & 0xff);
-       //return ret;
        u16 *tmpwPtr;
        int ret;
        tmpwPtr= kmalloc (2, GFP_ATOMIC);
@@ -125,7 +147,7 @@ static int dm_read_reg(struct usbnet *dev, u8 reg, u8 *value)
 
 static int dm_write(struct usbnet *dev, u8 reg, u16 length, void *data)
 {
-       devdbg(dev, "dm_write() reg=0x%02x, length=%d", reg, length);
+//  dm9620_print(dev, "dm_write() reg=0x%02x, length=%d", reg, length);
        return usb_control_msg(dev->udev,
                               usb_sndctrlpipe(dev->udev, 0),
                               DM_WRITE_REGS,
@@ -135,7 +157,7 @@ static int dm_write(struct usbnet *dev, u8 reg, u16 length, void *data)
 
 static int dm_write_reg(struct usbnet *dev, u8 reg, u8 value)
 {
-       devdbg(dev, "dm_write_reg() reg=0x%02x, value=0x%02x", reg, value);
+//     dm9620_print(dev , "dm_write_reg() reg=0x%02x, value=0x%02x", reg, value);
        return usb_control_msg(dev->udev,
                               usb_sndctrlpipe(dev->udev, 0),
                               DM_WRITE_REG,
@@ -164,13 +186,13 @@ static void dm_write_async_helper(struct usbnet *dev, u8 reg, u8 value,
 
        urb = usb_alloc_urb(0, GFP_ATOMIC);
        if (!urb) {
-               deverr(dev, "Error allocating URB in dm_write_async_helper!");
+               dm9620_err(dev, "Error allocating URB in dm_write_async_helper!");
                return;
        }
 
        req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC);
        if (!req) {
-               deverr(dev, "Failed to allocate memory for control request");
+               dm9620_err(dev, "Failed to allocate memory for control request");
                usb_free_urb(urb);
                return;
        }
@@ -188,8 +210,8 @@ static void dm_write_async_helper(struct usbnet *dev, u8 reg, u8 value,
 
        status = usb_submit_urb(urb, GFP_ATOMIC);
        if (status < 0) {
-               deverr(dev, "Error submitting the control message: status=%d",
-                      status);
+               dm9620_err(dev, "Error submitting the control message: status=%d",
+                      status);      
                kfree(req);
                usb_free_urb(urb);
        }
@@ -197,15 +219,14 @@ static void dm_write_async_helper(struct usbnet *dev, u8 reg, u8 value,
 
 static void dm_write_async(struct usbnet *dev, u8 reg, u16 length, void *data)
 {
-       devdbg(dev, "dm_write_async() reg=0x%02x length=%d", reg, length);
-
+//  dm9620_print(dev, "dm_write_async() reg=0x%02x length=%d", reg, length);
        dm_write_async_helper(dev, reg, 0, length, data);
 }
 
 static void dm_write_reg_async(struct usbnet *dev, u8 reg, u8 value)
 {
-       devdbg(dev, "dm_write_reg_async() reg=0x%02x value=0x%02x",
-              reg, value);
+//     dm9620_print(dev, "dm_write_reg_async() reg=0x%02x value=0x%02x",
+//            reg, value);      
 
        dm_write_async_helper(dev, reg, value, 0, NULL);
 }
@@ -213,6 +234,7 @@ static void dm_write_reg_async(struct usbnet *dev, u8 reg, u8 value)
 static int dm_read_shared_word(struct usbnet *dev, int phy, u8 reg, __le16 *value)
 {
        int ret, i;
+  u16 *tmpwPtr1;
 
        mutex_lock(&dev->phy_mutex);
 
@@ -233,16 +255,28 @@ static int dm_read_shared_word(struct usbnet *dev, int phy, u8 reg, __le16 *valu
        }
 
        if (i == DM_TIMEOUT) {
-               deverr(dev, "%s read timed out!", phy ? "phy" : "eeprom");
+               dm9620_err(dev, "%s read timed out!", phy ? "phy" : "eeprom");
                ret = -EIO;
                goto out;
        }
 
        dm_write_reg(dev, DM_SHARED_CTRL, 0x0);
-       ret = dm_read(dev, DM_SHARED_DATA, 2, value);
+//     ret = dm_read(dev, DM_SHARED_DATA, 2, value); 
+//Stone add
+       tmpwPtr1= kmalloc (2, GFP_ATOMIC);
+       if (!tmpwPtr1)
+       {
+               printk("+++++++++++ JJ5 dm_read_reg() Error: can not kmalloc!\n"); //usbnet_suspend (intf, message);
+               return 0; 
+       }
+       
+       ret = dm_read(dev, DM_SHARED_DATA, 2, tmpwPtr1);  // usb_submit_urb v.s. usb_control_msg
+       *value= (u16)(*tmpwPtr1 & 0xffff);
+       
+       kfree (tmpwPtr1); 
 
-       devdbg(dev, "read shared %d 0x%02x returned 0x%04x, %d",
-              phy, reg, *value, ret);
+//     dm9620_print(dev, "read shared %d 0x%02x returned 0x%04x, %d",
+//            phy, reg, *value, ret);      
 
  out:
        mutex_unlock(&dev->phy_mutex);
@@ -260,7 +294,6 @@ static int dm_write_shared_word(struct usbnet *dev, int phy, u8 reg, __le16 valu
                goto out;
 
        dm_write_reg(dev, DM_SHARED_ADDR, phy ? (reg | 0x40) : reg);
-       //dm_write_reg(dev, DM_SHARED_CTRL, phy ? 0x0a : 0x12);
        if (!phy) dm_write_reg(dev, DM_SHARED_CTRL, 0x10);
        dm_write_reg(dev, DM_SHARED_CTRL, phy ? 0x0a : 0x12);
        dm_write_reg(dev, DM_SHARED_CTRL, 0x10);
@@ -279,7 +312,7 @@ static int dm_write_shared_word(struct usbnet *dev, int phy, u8 reg, __le16 valu
        }
 
        if (i == DM_TIMEOUT) {
-               deverr(dev, "%s write timed out!", phy ? "phy" : "eeprom");
+               dm9620_err(dev,"%s write timed out!", phy ? "phy" : "eeprom");
                ret = -EIO;
                goto out;
        }
@@ -291,10 +324,11 @@ out:
        return ret;
 }
 
+
 static int dm_write_eeprom_word(struct usbnet *dev, int phy, u8 offset, u8 value)
 {
        int ret, i;
-       u8  reg,dloc;
+       u8  reg,dloc,tmp_H,tmp_L;
        __le16 eeword;
 
        //devwarn(dev, " offset =0x%x value = 0x%x ", offset,value);
@@ -308,16 +342,31 @@ static int dm_write_eeprom_word(struct usbnet *dev, int phy, u8 offset, u8 value
        /* retrieve high and low byte from the corresponding reg*/
        ret=dm_read_shared_word(dev,0,reg,&eeword);
        //devwarn(dev, " reg =0x%x dloc = 0x%x eeword = 0x%4x", reg,dloc,eeword);
+       //printk(" reg =0x%x dloc = 0x%x eeword = 0x%4x\n", reg,dloc,eeword);
+
+       tmp_H = (eeword & 0xff);
+       tmp_L = (eeword >> 8);
+
+       printk("tmp_L =0x%2x tmp_H =0x%2x eeword = 0x%4x\n", tmp_L,tmp_H,eeword);
+       /* determine new high and low byte */
+
+       if (offset & 0x01)  {
+               tmp_L = value;  }  else {
+       tmp_H = value;  }
+       
+       //printk("updated new: tmp_L =0x%2x tmp_H =0x%2x\n", tmp_L,tmp_H);
+
        mutex_lock(&dev->phy_mutex);
-       /* hank: write data to eeprom high/low byte reg */
-        dm_write(dev, (offset & 0x01)? DM_EE_PHY_H:DM_EE_PHY_L, 1, &value);
 
-       /* load the unaffected word to value */
-       (offset & 0x01)? (value = eeword << 8):(value = eeword >> 8);
+
+       /* hank: write low byte data first to eeprom reg */
+       // dm_write(dev, (offset & 0x01)? DM_EE_PHY_H:DM_EE_PHY_L, 1, &value);
+       dm_write(dev,DM_EE_PHY_L, 1, &tmp_H);
+       /* high byte will be zero */
+       //(offset & 0x01)? (value = eeword << 8):(value = eeword >> 8);
 
        /* write the not modified 8 bits back to its origional high/low byte reg */ 
-       dm_write(dev, (offset & 0x01)? DM_EE_PHY_L:DM_EE_PHY_H, 1, &value);
+       dm_write(dev,DM_EE_PHY_H, 1, &tmp_L);
        if (ret < 0)
                goto out;
 
@@ -342,7 +391,7 @@ static int dm_write_eeprom_word(struct usbnet *dev, int phy, u8 offset, u8 value
        }
 
        if (i == DM_TIMEOUT) {
-               deverr(dev, "%s write timed out!", phy ? "phy" : "eeprom");
+               dm9620_err(dev, "%s write timed out!", phy ? "phy" : "eeprom");
                ret = -EIO;
                goto out;
        }
@@ -353,6 +402,7 @@ out:
        mutex_unlock(&dev->phy_mutex);
        return ret;
 }
+
 static int dm_read_eeprom_word(struct usbnet *dev, u8 offset, void *value)
 {
        return dm_read_shared_word(dev, 0, offset, value);
@@ -363,10 +413,10 @@ static int dm9620_set_eeprom(struct net_device *net,struct ethtool_eeprom *eepro
 {
        struct usbnet *dev = netdev_priv(net);
        
-       devwarn(dev, "EEPROM: magic value, magic = 0x%x offset =0x%x data = 0x%x ",eeprom->magic, eeprom->offset,*data);
+       dm9620_print(dev, "EEPROM: magic value, magic = 0x%x offset =0x%x data = 0x%x ",eeprom->magic, eeprom->offset,*data);
        if (eeprom->magic != MD96XX_EEPROM_MAGIC) {
-               devwarn(dev, "EEPROM: magic value mismatch, magic = 0x%x",
-                       eeprom->magic);
+               dm9620_print(dev, "EEPROM: magic value mismatch, magic = 0x%x",
+                       eeprom->magic); 
                return -EINVAL;
        }
 
@@ -406,17 +456,10 @@ static int dm9620_mdio_read(struct net_device *netdev, int phy_id, int loc)
 
        __le16 res;
 
-       if (phy_id) {
-               devdbg(dev, "Only internal phy supported");
-               return 0;
-       }
-
-       dm_read_shared_word(dev, 1, loc, &res);
-
-       devdbg(dev,
-              "dm9620_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x",
-              phy_id, loc, le16_to_cpu(res));
+       dm_read_shared_word(dev, phy_id, loc, &res);
 
+//  dm9620_print(dev, "dm9620_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x",
+//            phy_id, loc, le16_to_cpu(res));
        return le16_to_cpu(res);
 }
 
@@ -427,15 +470,10 @@ static void dm9620_mdio_write(struct net_device *netdev, int phy_id, int loc,
        __le16 res = cpu_to_le16(val);
        int mdio_val;
 
-       if (phy_id) {
-               devdbg(dev, "Only internal phy supported");
-               return;
-       }
-
-       devdbg(dev,"dm9620_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x",
-              phy_id, loc, val);
+//     dm9620_print(dev, "dm9620_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x",
+//            phy_id, loc, val);      
 
-       dm_write_shared_word(dev, 1, loc, res);
+       dm_write_shared_word(dev, phy_id, loc, res);
        mdelay(1);
        mdio_val = dm9620_mdio_read(netdev, phy_id, loc);
 
@@ -503,7 +541,7 @@ dm9620_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
 
        dm_write_reg(dev, DM_NET_CTRL, 0x48);  // enable WAKEEN 
        
-       dm_write_reg(dev, 0x92, 0x3f); //keep clock on Hank Jun 30
+//     dm_write_reg(dev, 0x92, 0x3f); //keep clock on Hank Jun 30
        
        return dm_write_reg(dev, DM_WAKEUP_CTRL, opt);
 }
@@ -525,31 +563,43 @@ static struct ethtool_ops dm9620_ethtool_ops = {
 
 static void dm9620_set_multicast(struct net_device *net)
 {
-        struct usbnet *dev = netdev_priv(net);
-        /* We use the 20 byte dev->data for our 8 byte filter buffer
-         * to avoid allocating memory that is tricky to free later */
-        u8 *hashes = (u8 *) & dev->data;
-        u8 rx_ctl = 0x31;
-
-        memset(hashes, 0x00, DM_MCAST_SIZE);
-        hashes[DM_MCAST_SIZE - 1] |= 0x80;      /* broadcast address */
-
-        if (net->flags & IFF_PROMISC) {
-                rx_ctl |= 0x02;
-        } else if (net->flags & IFF_ALLMULTI ||
-                   netdev_mc_count(net) > DM_MAX_MCAST) {
-                rx_ctl |= 0x04;
-        } else if (!netdev_mc_empty(net)) {
-                struct netdev_hw_addr *ha;
-
-                netdev_for_each_mc_addr(ha, net) {
-                        u32 crc = ether_crc(ETH_ALEN, ha->addr) >> 26;
-                        hashes[crc >> 3] |= 1 << (crc & 0x7);
-                }
-        }
-
-        dm_write_async(dev, DM_MCAST_ADDR, DM_MCAST_SIZE, hashes);
-        dm_write_reg_async(dev, DM_RX_CTRL, rx_ctl);
+       struct usbnet *dev = netdev_priv(net);
+       /* We use the 20 byte dev->data for our 8 byte filter buffer
+        * to avoid allocating memory that is tricky to free later */
+       u8 *hashes = (u8 *) & dev->data;
+       u8 rx_ctl = 0x31;
+
+       memset(hashes, 0x00, DM_MCAST_SIZE);
+       hashes[DM_MCAST_SIZE - 1] |= 0x80;      /* broadcast address */
+
+       if (net->flags & IFF_PROMISC) {
+               rx_ctl |= 0x02;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,33)        
+       } else if (net->flags & IFF_ALLMULTI ||  netdev_mc_count(net) > DM_MAX_MCAST) {
+               rx_ctl |= 0x8;
+       } else if (!netdev_mc_empty(net)) {
+            struct netdev_hw_addr *ha;
+         netdev_for_each_mc_addr(ha, net) {
+              u32 crc = crc32_le(~0, ha->addr, ETH_ALEN) & 0x3f;
+              hashes[crc>>3] |= 1 << (crc & 0x7);
+               }
+#elif LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,33)
+       } else if (net->flags & IFF_ALLMULTI || net->mc_count > DM_MAX_MCAST) {
+               rx_ctl |= 0x08;
+       } else if (net->mc_count) {
+               struct dev_mc_list *mc_list = net->mc_list;
+               int i;
+
+               for (i = 0; i < net->mc_count; i++, mc_list = mc_list->next) {
+                       u32 crc = crc32_le(~0, mc_list->dmi_addr, ETH_ALEN) & 0x3f;
+                        hashes[crc>>3] |= 1 << (crc & 0x7);
+               } 
+#endif         
+       }
+       dm_write_async(dev, DM_MCAST_ADDR, DM_MCAST_SIZE, hashes);
+       dm_write_reg_async(dev, DM_RX_CTRL, rx_ctl);
 }
 
  
@@ -595,13 +645,18 @@ static const struct net_device_ops vm_netdev_ops= { // new kernel 2.6.31  (20091
             .ndo_change_mtu         = usbnet_change_mtu, 
             .ndo_validate_addr      = eth_validate_addr, 
            .ndo_do_ioctl           = dm9620_ioctl,   
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
+            .ndo_set_rx_mode        = dm9620_set_multicast,   
+#else
            .ndo_set_multicast_list = dm9620_set_multicast,   
+#endif
             .ndo_set_mac_address    = dm9620_set_mac_address,  
 };
 #endif
 
 static int dm9620_bind(struct usbnet *dev, struct usb_interface *intf)
 {
+  u16 *tmpwPtr2;
        int ret,mdio_val,i;
        struct dm96xx_priv* priv;
        u8 temp;
@@ -621,18 +676,17 @@ static int dm9620_bind(struct usbnet *dev, struct usb_interface *intf)
 #endif
        dev->net->hard_header_len += DM_TX_OVERHEAD;
        dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len;
-#ifdef RK2818   //harris 2010.12.27
-       dev->rx_urb_size = dev->net->mtu + ETH_HLEN + DM_RX_OVERHEAD+1;
-#else
-       dev->rx_urb_size = 2048;//dev->net->mtu + ETH_HLEN + DM_RX_OVERHEAD+1; // ftp fail fixed
-#endif
+       dev->rx_urb_size = dev->net->mtu + ETH_HLEN + DM_RX_OVERHEAD+1; // ftp fail fixed
+        dev->rx_urb_size = (dev->rx_urb_size > 2048) ? dev->rx_urb_size : 2048;
 
        dev->mii.dev = dev->net;
        dev->mii.mdio_read = dm9620_mdio_read;
        dev->mii.mdio_write = dm9620_mdio_write;
        dev->mii.phy_id_mask = 0x1f;
        dev->mii.reg_num_mask = 0x1f;
+       dev->mii.phy_id = DM9620_PHY_ID;
 
+       printk("[dm962] Linux Driver = %s\n", LNX_DM9620_VER_STR);
 //JJ1
        if ( (ret= dm_read_reg(dev, 0x29, &tmp)) >=0)
                printk("++++++[dm962]+++++ dm_read_reg() 0x29 0x%02x\n",tmp);
@@ -669,10 +723,13 @@ static int dm9620_bind(struct usbnet *dev, struct usb_interface *intf)
                printk("++++++[dm962]+++++  [Analysis.2] 0xF2, D[0] %d %s\n", (tmp>>0)&1, (tmp&(1<<0))? "Status: TX buffer has pkts": "Status: TX buffer 0 pkts" );
 
        /* reset */
-       dm_write_reg(dev, DM_XPHY_CTRL, 0); // dm9622/dm9685, bit[5](EXTERNAL), clear-it, add clk & limit to internal PHY
        dm_write_reg(dev, DM_NET_CTRL, 1);
        udelay(20);
-       
+       //Stone add Enable "MAC layer" Flow Control, TX Pause Packet Enable and 
+       dm_write_reg(dev, DM_FLOW_CTRL, 0x29);
+       //Stone add Enable "PHY layer" Flow Control support (phy register 0x04 bit 10)
+       temp = dm9620_mdio_read(dev->net, dev->mii.phy_id, 0x04);
+       dm9620_mdio_write(dev->net, dev->mii.phy_id, 0x04, temp | 0x400);
        
 
        /* Add V1.1, Enable auto link while plug in RJ45, Hank July 20, 2009*/
@@ -695,19 +752,23 @@ static int dm9620_bind(struct usbnet *dev, struct usb_interface *intf)
        /* read SMI mode register */
         priv = dev->driver_priv = kmalloc(sizeof(struct dm96xx_priv), GFP_ATOMIC);
        if (!priv) {
-               deverr(dev, "Failed to allocate memory for dm96xx_priv");
+               dm9620_err(dev,"Failed to allocate memory for dm96xx_priv");
                ret = -ENOMEM;
                goto out;
        }
        
         /* work-around for 9620 mode */
+       dm_read_reg(dev, 0x5c, &temp); 
+       priv->tx_fix_mod = temp;
+       printk(KERN_WARNING "[dm96] 9620 tx_fix_mod (DM9_NREV= %d)\n", priv->tx_fix_mod);
+
        printk("[dm96] Fixme: work around for 9620 mode\n");
        printk("[dm96] Add tx_fixup() debug...\n");
        dm_write_reg(dev, DM_MCAST_ADDR, 0);     // clear data bus to 0s
        dm_read_reg(dev, DM_MCAST_ADDR, &temp);  // clear data bus to 0s
        ret = dm_read_reg(dev, DM_SMIREG, &temp);   // Must clear data bus before we can read the 'MODE9620' bit
 
-       priv->flag_fail_count= 0;
+       priv->flg_txdbg= 0; //->flag_fail_count= 0;
        if (ret<0) {
                printk(KERN_ERR "[dm96] Error read SMI register\n");
        }
@@ -715,6 +776,31 @@ static int dm9620_bind(struct usbnet *dev, struct usb_interface *intf)
 
        printk(KERN_WARNING "[dm96] 9620 Mode = %d\n", priv->mode_9620);
        
+       dm_read_reg(dev, DM_TXRX_M, &temp);  // Need to check the Chipset version (register 0x5c is 0x02?)
+       if (temp == 0x02)
+       {
+        dm_read_reg(dev, 0x3f, &temp);
+        temp |= 0x80; 
+   dm_write_reg(dev, 0x3f, temp);
+   }
+  
+  //Stone add for check Product ID == 0x1269
+  tmpwPtr2= kmalloc (2, GFP_ATOMIC);
+       if (!tmpwPtr2)
+       {
+               printk("+++++++++++ JJ5 dm_read_reg() Error: can not kmalloc!\n"); //usbnet_suspend (intf, message);
+               return 0; 
+       } 
+  ret =dm_read(dev, DM_PID, 2, tmpwPtr2);
+
+  if (*tmpwPtr2 == 0x1269)
+   dm_write_reg(dev, DM_SMIREG, 0xa0);
+   
+  if (*tmpwPtr2 == 0x0269)
+   dm_write_reg(dev, DM_SMIREG, 0xa0); 
+  
+  kfree (tmpwPtr2); 
+       
        /* power up phy */
        dm_write_reg(dev, DM_GPR_CTRL, 1);
        dm_write_reg(dev, DM_GPR_DATA, 0);
@@ -732,9 +818,7 @@ static int dm9620_bind(struct usbnet *dev, struct usb_interface *intf)
        /* Hank add, work for comapubility issue (10M Power control) */ 
        
        dm9620_mdio_write(dev->net, dev->mii.phy_id, PHY_SPEC_CFG, 0x800);
-       //printk("[dm96] dm962++ write phy[20]= 0x800\n");
        mdio_val = dm9620_mdio_read(dev->net, dev->mii.phy_id, PHY_SPEC_CFG);
-       //printk("[dm96] dm962++ read  phy[20]= %x\n",mdio_val );
        
        dm9620_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
                          ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
@@ -747,11 +831,10 @@ out:
 void dm9620_unbind(struct usbnet *dev, struct usb_interface *intf)
 {
 struct dm96xx_priv* priv= dev->driver_priv;
-//u8 opt=0;
-//int i;
        printk("dm9620_unbind():\n");
 
-       printk("flag_fail_count  %lu\n", (long unsigned int)priv->flag_fail_count);
+   //printk("flag_fail_count  %lu\n", (long unsigned int)priv->flag_fail_count);
+       printk("flg_txdbg  %lu\n", (long unsigned int)priv->flg_txdbg);
        kfree(dev->driver_priv); // displayed dev->.. above, then can free dev 
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,31) 
@@ -770,9 +853,6 @@ struct dm96xx_priv* priv= dev->driver_priv;
        printk("rx_missed_errors %lu\n",dev->stats.rx_missed_errors);   
 #endif
 
-// check if dm9620 receive magic packet
-//i=dm_read_reg(dev, DM_WAKEUP_CTRL, &opt);
-//     printk("rx_magic_packet   %lu\n",i);
 
 }
 
@@ -807,11 +887,10 @@ static int dm9620_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
                        dev_err(&dev->udev->dev, "unexpected tiny rx frame\n");
                        return 0;
                }
-
-       //.     struct dm96xx_priv* priv= dev->driver_priv;
-       //      if (skb->data[0]!=0x01)
-       //              priv->flag_fail_count++;
-
+               
+               //      if (skb->data[0]!=0x01)
+               //              priv->flag_fail_count++;
+       
                status = skb->data[1];
                len = (skb->data[2] | (skb->data[3] << 8)) - 4;
                
@@ -864,10 +943,43 @@ static int dm9620_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
        return 1;
 } // 'priv'
 
+#define TX_LEN_E  (1<<0)  //EVEN, No action
+#define TX_LEN_O  (1<<1)  //ODD, Odd to even workaround
+#define TX_LEN_F  (1<<2)  //FULL, Full payload workaround
+u8 TxStyle(int len, unsigned full_payload){
+  u8 s= (len%2)? TX_LEN_O: TX_LEN_E;
+  len= ((len+1)/2)*2;
+  len += 2;
+  if ((len % full_payload)==0)
+    s |= TX_LEN_F;
+  return s;
+}
+struct sk_buff *TxExpend(struct dm96xx_priv* priv, u8 ts, struct sk_buff *skb, gfp_t flags)
+{
+    int newheadroom= 2, newtailroom= 0;  
+    if (ts&TX_LEN_O) newtailroom++;
+    if (ts&TX_LEN_F) newtailroom += 2;
+    if (skb_headroom(skb) >= newheadroom) newheadroom= 0; // head no need expend
+    if (skb_tailroom(skb) >= newtailroom) newtailroom= 0; // tail no need expend
+    if (newheadroom || newtailroom){
+               struct sk_buff *skb2;
+               skb2 = skb_copy_expand(skb, newheadroom, newtailroom, flags);
+               dev_kfree_skb_any(skb);
+               skb = skb2;
+               if (!skb){
+                       printk("[dm96-TxRound].%d expend copy fail, for head, tail= %d, %d\n", priv->flg_txdbg++, newheadroom, newtailroom);
+                       return NULL;
+               }
+               printk("[dm96-TxRound].%d expend copy OK, for head, tail= %d, %d\n", priv->flg_txdbg++, newheadroom, newtailroom);
+    }
+    return skb;
+}
 static struct sk_buff *dm9620_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
                                       gfp_t flags)
 {
        int len;
+    int newheadroom, newtailroom;  
+       struct dm96xx_priv* priv = (struct dm96xx_priv *)dev->driver_priv;
 
        /* format:
           b0: packet length low
@@ -877,6 +989,9 @@ static struct sk_buff *dm9620_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
 
        len = skb->len;
 
+  if (priv->tx_fix_mod<3)
+  {
+    /*
        if (skb_headroom(skb) < DM_TX_OVERHEAD) {
                struct sk_buff *skb2;
                skb2 = skb_copy_expand(skb, DM_TX_OVERHEAD, 0, flags);
@@ -888,10 +1003,52 @@ static struct sk_buff *dm9620_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
 
        __skb_push(skb, DM_TX_OVERHEAD);
 
-       /* usbnet adds padding if length is a multiple of packet size
-          if so, adjust length value in header */
        if ((skb->len % dev->maxpacket) == 0)
                len++;
+    */
+    //;DM9620-E4,E5, and E6
+       /* usbnet adds padding 1 byte if odd len */
+       /* usbnet adds padding 2 bytes if length is a multiple of packet size
+          if so, adjust length value in header */
+     u8 TS= TxStyle(len, dev->maxpacket); //
+     if (!(skb= TxExpend(priv, TS, skb, flags))) return NULL; //
+
+     if (TS & TX_LEN_F) len += 2;
+
+     newheadroom= 2; //2
+     newtailroom= 0; //0, 1, 2, or 3
+     if (TS & TX_LEN_O) newtailroom++;
+     if (TS & TX_LEN_F) newtailroom += 2;
+     
+     if (TS & TX_LEN_O) printk("[dm96-TxRound].%d for LEN_ODD tail_room +1, rslt add %d\n", priv->flg_txdbg, newtailroom);
+     if (TS & TX_LEN_F) printk("[dm96-TxRound].%d for LEN_PLOAD tail_room +2, rslt add %d\n", priv->flg_txdbg, newtailroom);
+     if (TS & TX_LEN_F) printk("[dm96-TxRound].%d for LEN_PLOAD data_len +2, len from %d to %d\n", priv->flg_txdbg, len-2, len);
+     if (TS & (TX_LEN_O|TX_LEN_F)) priv->flg_txdbg++;
+
+       __skb_push(skb, newheadroom); //2 bytes,for data[0],data[1]
+    __skb_put(skb, newtailroom); //0, 1, 2, or 3 bytes (for tailer), 
+                                 //Note: 0, NOTHING
+                                 //Note: 1, Odd to even WORKAROUND.
+                                 //Note: 2 or 3, the condition is full payload,
+                                 // This is the add more two bytes WORKAROUND
+                                 // for bulkout and buffLen.
+  }
+  else 
+  {
+    //;DM9620-E7
+       if (skb_headroom(skb) < DM_TX_OVERHEAD) {
+               struct sk_buff *skb2;
+               skb2 = skb_copy_expand(skb, DM_TX_OVERHEAD, 0, flags);
+               dev_kfree_skb_any(skb);
+               skb = skb2;
+               if (!skb)
+                       return NULL;
+       }
+
+   newheadroom= 2; //2
+       __skb_push(skb, newheadroom);  //2 bytes, for data[0],data[1]
+  }
+
 
        skb->data[0] = len;
        skb->data[1] = len >> 8;
@@ -931,7 +1088,7 @@ static void dm9620_status(struct usbnet *dev, struct urb *urb)
                }
                else
                        netif_carrier_off(dev->net);
-               devdbg(dev, "Link Status is: %d", link);
+               dm9620_print(dev, "Link Status is: %d", link);
        }
 }
 
@@ -942,8 +1099,8 @@ static int dm9620_link_reset(struct usbnet *dev)
        mii_ethtool_gset(&dev->mii, &ecmd);
        /* hank add*/
 dm9620_mdio_write(dev->net, dev->mii.phy_id, PHY_SPEC_CFG, 0x800);
-       devdbg(dev, "link_reset() speed: %d duplex: %d",
-              ecmd.speed, ecmd.duplex);
+       dm9620_print(dev, "link_reset() speed: %d duplex: %d",
+              ecmd.speed, ecmd.duplex);      
        
        return 0;
 }
@@ -955,12 +1112,8 @@ static const struct driver_info dm9620_info = {
        .rx_fixup       = dm9620_rx_fixup,
        .tx_fixup       = dm9620_tx_fixup,
        .status         = dm9620_status,
-#ifdef RK2818  //harris 2010.12.27
-
-#else
        .link_reset     = dm9620_link_reset,
        .reset          = dm9620_link_reset,
-#endif
        .unbind     = dm9620_unbind,
 };
 
@@ -989,29 +1142,350 @@ static const struct usb_device_id products[] = {
        USB_DEVICE(0x0a47, 0x9601),     /* Hirose USB-100 */
        .driver_info = (unsigned long)&dm9620_info,
         },
-        {
-        USB_DEVICE(0x0a46, 0x9620),     /* Davicom 9620 */
-        .driver_info = (unsigned long)&dm9620_info,
-         },
-        {
-        USB_DEVICE(0x0a46, 0x9621),     /* Davicom 9621 */
-        .driver_info = (unsigned long)&dm9620_info,
-         },
-        {
-        USB_DEVICE(0x0a46, 0x9622),     /* Davicom 9622 */
-        .driver_info = (unsigned long)&dm9620_info,
-         },
-        {
-       USB_DEVICE(0x0fe6, 0x8101),     /* Davicom 9601 USB to Fast Ethernet Adapter */
-        .driver_info = (unsigned long)&dm9620_info,
-         },
+  {
+   USB_DEVICE(0x0a46, 0x9620),     /* Davicom 9620 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0a46, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0a46, 0x9622),     /* Davicom 9622 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+        USB_DEVICE(0x0fe6, 0x8101),     /* Davicom 9601 USB to Fast Ethernet Adapter */
+   .driver_info = (unsigned long)&dm9620_info,
+  },
+  {
+   USB_DEVICE(0x0a46, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0a46, 0x0269),     /* Davicom 9620A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+   //+
+   //VID.00
+   //0.0000 0000 0000 
+  {
+   USB_DEVICE(0x0000, 0x9620),     /* Davicom 9620 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0000, 0x0269),     /* Davicom 9620A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0000, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0000, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+   //1.0000 0000 0010
+   //2.0000 0000 0100
+   //3.0000 0000 0110
+   //4.0000 0100 0000
+   //5.0000 0100 0010
+   //6.0000 0100 0100
+   //7.0000 0100 0110
+  {
+   USB_DEVICE(0x0002, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0002, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0004, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0004, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0006, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0006, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0040, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0040, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0042, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0042, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0044, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0044, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+   //7.0000 0100 0110 (0A46)
+  {
+   USB_DEVICE(0x0046, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0046, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+     // ..
+   
+   //8.0010 0000 0000
+   //9.0010 0000 0010
+   //A.0010 0000 0100
+   //B.0010 0000 0110
+   //C.0010 0100 0000
+   //D.0010 0100 0010
+   //E.0010 0100 0100
+   //F.0010 0100 0110
+  {
+   USB_DEVICE(0x0200, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0200, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0202, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0202, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0204, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0204, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0206, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0206, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0240, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0240, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0242, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0242, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0244, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0244, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+   //F.0010 0100 0110 (0A46)
+  {
+   USB_DEVICE(0x0246, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0246, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+     // ..
+   
+   //10.1000 0000 0000
+   //11.1000 0000 0010
+   //12.1000 0000 0100
+   //13.1000 0000 0110
+   //14.1000 0100 0000
+   //15.1000 0100 0010
+   //16.1000 0100 0100
+   //17.1000 0100 0110
+  {
+   USB_DEVICE(0x0800, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0800, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0802, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0802, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0804, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0804, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0806, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0806, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0840, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0840, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0842, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0842, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0844, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0844, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+   //17.1010 0100 0110 (0A46)
+  {
+   USB_DEVICE(0x0846, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0846, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+     // ..
+   
+   //18.1010 0000 0000
+   //19.1010 0000 0010
+   //1A.1010 0000 0100
+   //1B.1010 0000 0110
+   //1C.1010 0100 0000
+   //1D.1010 0100 0010
+   //1E.1010 0100 0100
+  {
+   USB_DEVICE(0x0A00, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0A00, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0A02, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0A02, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0A04, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0A04, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0A06, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0A06, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0A40, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0A40, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0A42, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0A42, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0A44, 0x9621),     /* Davicom 9621 */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0A44, 0x1269),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+   //1F.1010 0100 0110 (0A46)
+     // .Original Default.
+   //+
+   //1268, 1200
+  {
+   USB_DEVICE(0x0000, 0x1268),     /* Davicom 9621A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0000, 0x1200),     /* Davicom 9620A CDC */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
+  {
+   USB_DEVICE(0x0000, 0x1220),     /* Davicom 9620A CDC, 1220 test mode */
+   .driver_info = (unsigned long)&dm9620_info,
+   },
        {},                     // END
 };
 
 MODULE_DEVICE_TABLE(usb, products);
 
 static struct usb_driver dm9620_driver = {
-//     .name = "dm9601",
        .name = "dm9620",
        .id_table = products,
        .probe = usbnet_probe,