- sync fedora branch
[linux-2.6.git] / drivers / usb / net / usbnet.c
index e2e328f..4cbb408 100644 (file)
@@ -1,8 +1,9 @@
 /*
  * USB Networking Links
- * Copyright (C) 2000-2003 by David Brownell <dbrownell@users.sourceforge.net>
+ * Copyright (C) 2000-2005 by David Brownell <dbrownell@users.sourceforge.net>
  * Copyright (C) 2002 Pavel Machek <pavel@ucw.cz>
- * Copyright (C) 2003 David Hollis <dhollis@davehollis.com>
+ * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
+ * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net>
  * Copyright (c) 2002-2003 TiVo Inc.
  *
  * This program is free software; you can redistribute it and/or modify
@@ -31,6 +32,7 @@
  *     - GeneSys GL620USB-A
  *     - NetChip 1080 (interoperates with NetChip Win32 drivers)
  *     - Prolific PL-2301/2302 (replaces "plusb" driver)
+ *     - KC Technology KC2190
  *
  *   + Smart USB devices can support such links directly, using Internet
  *     standard protocols instead of proprietary host-to-device links.
  *             disconnect; other cleanups. (db)  Flush net1080 fifos
  *             after several sequential framing errors. (Johannes Erdfelt)
  * 22-aug-2003 AX8817X support (Dave Hollis).
+ * 14-jun-2004  Trivial patch for AX8817X based Buffalo LUA-U2-KTX in Japan
+ *             (Neil Bortnak)
+ * 03-nov-2004 Trivial patch for KC2190 (KC-190) chip. (Jonathan McDowell)
  *
+ * 01-feb-2005 AX88772 support (Phil Chang & Dave Hollis)
  *-------------------------------------------------------------------------*/
 
 // #define     DEBUG                   // error path messages, extra info
 #include <linux/mm.h>
 #include <linux/dma-mapping.h>
 
-#define DRIVER_VERSION         "25-Aug-2003"
+#define DRIVER_VERSION         "03-Nov-2004"
 
 
 /*-------------------------------------------------------------------------*/
 #define THROTTLE_JIFFIES       (HZ/8)
 
 // for vendor-specific control operations
-#define        CONTROL_TIMEOUT_MS      (500)                   /* msec */
-#define CONTROL_TIMEOUT_JIFFIES ((CONTROL_TIMEOUT_MS * HZ)/1000)
+#define        CONTROL_TIMEOUT_MS      500
 
 // between wakeups
-#define UNLINK_TIMEOUT_JIFFIES ((3  /*ms*/ * HZ)/1000)
+#define UNLINK_TIMEOUT_MS      3
 
 /*-------------------------------------------------------------------------*/
 
@@ -180,13 +185,14 @@ struct usbnet {
 
        // 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_level;
+       int                     msg_enable;
        unsigned long           data [5];
 
        struct mii_if_info      mii;
@@ -195,6 +201,7 @@ struct usbnet {
        struct sk_buff_head     rxq;
        struct sk_buff_head     txq;
        struct sk_buff_head     done;
+       struct urb              *interrupt;
        struct tasklet_struct   bh;
 
        struct work_struct      kevent;
@@ -202,6 +209,8 @@ struct usbnet {
 #              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
 };
 
 // device-specific info used by the driver
@@ -218,6 +227,8 @@ struct driver_info {
 #define FLAG_NO_SETINT 0x0010          /* device can't set_interface() */
 #define FLAG_ETHER     0x0020          /* maybe use "eth%d" names */
 
+#define FLAG_FRAMING_AX 0x0040          /* AX88772/178 packets */
+
        /* init device ... can sleep, or cause probe() failure */
        int     (*bind)(struct usbnet *, struct usb_interface *);
 
@@ -230,6 +241,12 @@ struct driver_info {
        /* 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);
 
@@ -265,14 +282,11 @@ struct skb_data { // skb->cb is one of these
 static const char driver_name [] = "usbnet";
 
 /* use ethtool to change the level for any given device */
-static int msg_level = 1;
-MODULE_PARM (msg_level, "i");
-MODULE_PARM_DESC (msg_level, "Initial message level (default = 1)");
+static int msg_level = -1;
+module_param (msg_level, int, 0);
+MODULE_PARM_DESC (msg_level, "Override default message level");
 
 
-#define        RUN_CONTEXT (in_irq () ? "in_irq" \
-                       : (in_interrupt () ? "in_interrupt" : "can sleep"))
-
 #ifdef DEBUG
 #define devdbg(usbnet, fmt, arg...) \
        printk(KERN_DEBUG "%s: " fmt "\n" , (usbnet)->net->name , ## arg)
@@ -286,9 +300,7 @@ MODULE_PARM_DESC (msg_level, "Initial message level (default = 1)");
        printk(KERN_WARNING "%s: " fmt "\n" , (usbnet)->net->name , ## arg)
 
 #define devinfo(usbnet, fmt, arg...) \
-       do { if ((usbnet)->msg_level >= 1) \
        printk(KERN_INFO "%s: " fmt "\n" , (usbnet)->net->name , ## arg); \
-       } while (0)
 
 /*-------------------------------------------------------------------------*/
 
@@ -296,6 +308,7 @@ static void usbnet_get_drvinfo (struct net_device *, struct ethtool_drvinfo *);
 static u32 usbnet_get_link (struct net_device *);
 static u32 usbnet_get_msglevel (struct net_device *);
 static void usbnet_set_msglevel (struct net_device *, u32);
+static void defer_kevent (struct usbnet *, int);
 
 /* mostly for PDA style devices, which are always connected if present */
 static int always_connected (struct usbnet *dev)
@@ -308,38 +321,52 @@ static int
 get_endpoints (struct usbnet *dev, struct usb_interface *intf)
 {
        int                             tmp;
-       struct usb_host_interface       *alt;
-       struct usb_host_endpoint        *in, *out;
+       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 = 0;
+               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;
-                       if (e->desc.bmAttributes != USB_ENDPOINT_XFER_BULK)
+                       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 (!in)
+                               if (!intr && !in)
                                        in = e;
+                               else if (intr && !status)
+                                       status = e;
                        } else {
                                if (!out)
                                        out = e;
                        }
-                       if (in && out)
-                               goto found;
                }
+               if (in && out)
+                       break;
        }
-       return -EINVAL;
+       if (!alt || !in || !out)
+               return -EINVAL;
 
-found:
        if (alt->desc.bAlternateSetting != 0
                        || !(dev->driver_info->flags & FLAG_NO_SETINT)) {
                tmp = usb_set_interface (dev->udev, alt->desc.bInterfaceNumber,
@@ -352,9 +379,48 @@ found:
                        in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
        dev->out = usb_sndbulkpipe (dev->udev,
                        out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+       dev->status = status;
        return 0;
 }
 
+static void intr_complete (struct urb *urb, struct pt_regs *regs);
+
+static int init_status (struct usbnet *dev, struct usb_interface *intf)
+{
+       char            *buf = NULL;
+       unsigned        pipe = 0;
+       unsigned        maxp;
+       unsigned        period;
+
+       if (!dev->driver_info->status)
+               return 0;
+
+       pipe = usb_rcvintpipe (dev->udev,
+                       dev->status->desc.bEndpointAddress
+                               & USB_ENDPOINT_NUMBER_MASK);
+       maxp = usb_maxpacket (dev->udev, pipe, 0);
+
+       /* avoid 1 msec chatter:  min 8 msec poll rate */
+       period = max ((int) dev->status->desc.bInterval,
+               (dev->udev->speed == USB_SPEED_HIGH) ? 7 : 3);
+
+       buf = kmalloc (maxp, SLAB_KERNEL);
+       if (buf) {
+               dev->interrupt = usb_alloc_urb (0, SLAB_KERNEL);
+               if (!dev->interrupt) {
+                       kfree (buf);
+                       return -ENOMEM;
+               } else {
+                       usb_fill_int_urb(dev->interrupt, dev->udev, pipe,
+                               buf, maxp, intr_complete, dev, period);
+                       dev_dbg(&intf->dev,
+                               "status ep%din, %d bytes period %d\n",
+                               usb_pipeendpoint(pipe), maxp, period);
+               }
+       }
+       return  0;
+}
+
 static void skb_return (struct usbnet *dev, struct sk_buff *skb)
 {
        int     status;
@@ -364,13 +430,12 @@ static void skb_return (struct usbnet *dev, struct sk_buff *skb)
        dev->stats.rx_packets++;
        dev->stats.rx_bytes += skb->len;
 
-#ifdef VERBOSE
-       devdbg (dev, "< rx, len %d, type 0x%x",
-               skb->len + sizeof (struct ethhdr), skb->protocol);
-#endif
+       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)
+       if (status != NET_RX_SUCCESS && netif_msg_rx_err (dev))
                devdbg (dev, "netif_rx status %d", status);
 }
 
@@ -431,6 +496,8 @@ static const struct driver_info     an2720_info = {
 #define AX_CMD_SET_HW_MII              0x0a
 #define AX_CMD_READ_EEPROM             0x0b
 #define AX_CMD_WRITE_EEPROM            0x0c
+#define AX_CMD_WRITE_ENABLE            0x0d
+#define AX_CMD_WRITE_DISABLE           0x0e
 #define AX_CMD_WRITE_RX_CTL            0x10
 #define AX_CMD_READ_IPG012             0x11
 #define AX_CMD_WRITE_IPG0              0x12
@@ -439,19 +506,71 @@ static const struct driver_info   an2720_info = {
 #define AX_CMD_WRITE_MULTI_FILTER      0x16
 #define AX_CMD_READ_NODE_ID            0x17
 #define AX_CMD_READ_PHY_ID             0x19
+#define AX_CMD_READ_MEDIUM_STATUS      0x1a
 #define AX_CMD_WRITE_MEDIUM_MODE       0x1b
 #define AX_CMD_READ_MONITOR_MODE       0x1c
 #define AX_CMD_WRITE_MONITOR_MODE      0x1d
 #define AX_CMD_WRITE_GPIOS             0x1f
+#define AX_CMD_SW_RESET                        0x20
+#define AX_CMD_SW_PHY_STATUS           0x21
+#define AX_CMD_SW_PHY_SELECT           0x22
+#define AX88772_CMD_READ_NODE_ID       0x13
 
 #define AX_MONITOR_MODE                        0x01
 #define AX_MONITOR_LINK                        0x02
 #define AX_MONITOR_MAGIC               0x04
 #define AX_MONITOR_HSFS                        0x10
 
+/* AX88172 Medium Status Register values */
+#define AX_MEDIUM_FULL_DUPLEX          0x02
+#define AX_MEDIUM_TX_ABORT_ALLOW       0x04
+#define AX_MEDIUM_FLOW_CONTROL_EN      0x10
+
 #define AX_MCAST_FILTER_SIZE           8
 #define AX_MAX_MCAST                   64
 
+#define AX_EEPROM_LEN                  0x40
+
+#define AX_SWRESET_CLEAR               0x00
+#define AX_SWRESET_RR                  0x01
+#define AX_SWRESET_RT                  0x02
+#define AX_SWRESET_PRTE                        0x04
+#define AX_SWRESET_PRL                 0x08
+#define AX_SWRESET_BZ                  0x10
+#define AX_SWRESET_IPRL                        0x20
+#define AX_SWRESET_IPPD                        0x40
+
+#define AX88772_IPG0_DEFAULT           0x15
+#define AX88772_IPG1_DEFAULT           0x0c
+#define AX88772_IPG2_DEFAULT           0x12
+
+#define AX88772_MEDIUM_FULL_DUPLEX     0x0002
+#define AX88772_MEDIUM_RESERVED                0x0004
+#define AX88772_MEDIUM_RX_FC_ENABLE    0x0010
+#define AX88772_MEDIUM_TX_FC_ENABLE    0x0020
+#define AX88772_MEDIUM_PAUSE_FORMAT    0x0080
+#define AX88772_MEDIUM_RX_ENABLE       0x0100
+#define AX88772_MEDIUM_100MB           0x0200
+#define AX88772_MEDIUM_DEFAULT \
+       (AX88772_MEDIUM_FULL_DUPLEX | AX88772_MEDIUM_RX_FC_ENABLE | \
+        AX88772_MEDIUM_TX_FC_ENABLE | AX88772_MEDIUM_100MB | \
+        AX88772_MEDIUM_RESERVED | AX88772_MEDIUM_RX_ENABLE )
+
+#define AX_EEPROM_MAGIC                        0xdeadbeef
+
+/* This structure cannot exceed sizeof(unsigned long [5]) AKA 20 bytes */
+struct ax8817x_data {
+       u8 multi_filter[AX_MCAST_FILTER_SIZE];
+};
+
+struct ax88172_int_data {
+       u16 res1;
+       u8 link;
+       u16 res2;
+       u8 status;
+       u16 res3;
+} __attribute__ ((packed));
+
 static int ax8817x_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
                            u16 size, void *data)
 {
@@ -464,7 +583,7 @@ static int ax8817x_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
                index,
                data,
                size,
-               CONTROL_TIMEOUT_JIFFIES);
+               CONTROL_TIMEOUT_MS);
 }
 
 static int ax8817x_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
@@ -479,7 +598,7 @@ static int ax8817x_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
                index,
                data,
                size,
-               CONTROL_TIMEOUT_JIFFIES);
+               CONTROL_TIMEOUT_MS);
 }
 
 static void ax8817x_async_cmd_callback(struct urb *urb, struct pt_regs *regs)
@@ -494,6 +613,26 @@ static void ax8817x_async_cmd_callback(struct urb *urb, struct pt_regs *regs)
        usb_free_urb(urb);
 }
 
+static void ax8817x_status(struct usbnet *dev, struct urb *urb)
+{
+       struct ax88172_int_data *event;
+       int link;
+
+       if (urb->actual_length < 8)
+               return;
+
+       event = urb->transfer_buffer;
+       link = event->link & 0x01;
+       if (netif_carrier_ok(dev->net) != link) {
+               if (link) {
+                       netif_carrier_on(dev->net);
+                       defer_kevent (dev, EVENT_LINK_RESET );
+               } else
+                       netif_carrier_off(dev->net);
+               devdbg(dev, "ax8817x - Link Status is: %d", link);
+       }
+}
+
 static void ax8817x_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,
                                    u16 size, void *data)
 {
@@ -532,7 +671,8 @@ static void ax8817x_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 i
 
 static void ax8817x_set_multicast(struct net_device *net)
 {
-       struct usbnet *dev = (struct usbnet *) net->priv;
+       struct usbnet *dev = netdev_priv(net);
+       struct ax8817x_data *data = (struct ax8817x_data *)&dev->data;
        u8 rx_ctl = 0x8c;
 
        if (net->flags & IFF_PROMISC) {
@@ -547,25 +687,24 @@ static void ax8817x_set_multicast(struct net_device *net)
                 * for our 8 byte filter buffer
                 * to avoid allocating memory that
                 * is tricky to free later */
-               u8 *multi_filter = (u8 *)&dev->data;
                struct dev_mc_list *mc_list = net->mc_list;
                u32 crc_bits;
                int i;
 
-               memset(multi_filter, 0, AX_MCAST_FILTER_SIZE);
+               memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE);
 
                /* Build the multicast hash filter. */
                for (i = 0; i < net->mc_count; i++) {
                        crc_bits =
                            ether_crc(ETH_ALEN,
                                      mc_list->dmi_addr) >> 26;
-                       multi_filter[crc_bits >> 3] |=
+                       data->multi_filter[crc_bits >> 3] |=
                            1 << (crc_bits & 7);
                        mc_list = mc_list->next;
                }
 
                ax8817x_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0,
-                                  AX_MCAST_FILTER_SIZE, multi_filter);
+                                  AX_MCAST_FILTER_SIZE, data->multi_filter);
 
                rx_ctl |= 0x10;
        }
@@ -575,7 +714,7 @@ static void ax8817x_set_multicast(struct net_device *net)
 
 static int ax8817x_mdio_read(struct net_device *netdev, int phy_id, int loc)
 {
-       struct usbnet *dev = netdev->priv;
+       struct usbnet *dev = netdev_priv(netdev);
        u16 res;
        u8 buf[1];
 
@@ -588,7 +727,7 @@ static int ax8817x_mdio_read(struct net_device *netdev, int phy_id, int loc)
 
 static void ax8817x_mdio_write(struct net_device *netdev, int phy_id, int loc, int val)
 {
-       struct usbnet *dev = netdev->priv;
+       struct usbnet *dev = netdev_priv(netdev);
        u16 res = val;
        u8 buf[1];
 
@@ -597,9 +736,23 @@ static void ax8817x_mdio_write(struct net_device *netdev, int phy_id, int loc, i
        ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, &buf);
 }
 
+static int ax88172_link_reset(struct usbnet *dev)
+{
+       u16 lpa;
+       u8 mode;
+
+       mode = AX_MEDIUM_TX_ABORT_ALLOW | AX_MEDIUM_FLOW_CONTROL_EN;
+       lpa = ax8817x_mdio_read(dev->net, dev->mii.phy_id, MII_LPA);
+       if (lpa & LPA_DUPLEX)
+               mode |= AX_MEDIUM_FULL_DUPLEX;
+       ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL);
+
+       return 0;
+}
+
 static void ax8817x_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
 {
-       struct usbnet *dev = (struct usbnet *)net->priv;
+       struct usbnet *dev = netdev_priv(net);
        u8 opt;
 
        if (ax8817x_read_cmd(dev, AX_CMD_READ_MONITOR_MODE, 0, 0, 1, &opt) < 0) {
@@ -619,7 +772,7 @@ static void ax8817x_get_wol(struct net_device *net, struct ethtool_wolinfo *woli
 
 static int ax8817x_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
 {
-       struct usbnet *dev = (struct usbnet *)net->priv;
+       struct usbnet *dev = netdev_priv(net);
        u8 opt = 0;
        u8 buf[1];
 
@@ -637,10 +790,15 @@ static int ax8817x_set_wol(struct net_device *net, struct ethtool_wolinfo *wolin
        return 0;
 }
 
+static int ax8817x_get_eeprom_len(struct net_device *net)
+{
+       return AX_EEPROM_LEN;
+}
+
 static int ax8817x_get_eeprom(struct net_device *net,
                              struct ethtool_eeprom *eeprom, u8 *data)
 {
-       struct usbnet *dev = (struct usbnet *)net->priv;
+       struct usbnet *dev = netdev_priv(net);
        u16 *ebuf = (u16 *)data;
        int i;
 
@@ -650,13 +808,15 @@ static int ax8817x_get_eeprom(struct net_device *net,
        if (eeprom->len % 2)
                return -EINVAL;
 
+       eeprom->magic = AX_EEPROM_MAGIC;
+
        /* ax8817x returns 2 bytes from eeprom on read */
        for (i=0; i < eeprom->len / 2; i++) {
                if (ax8817x_read_cmd(dev, AX_CMD_READ_EEPROM, 
                        eeprom->offset + i, 0, 2, &ebuf[i]) < 0)
                        return -EINVAL;
        }
-       return i * 2;
+       return 0;
 }
 
 static void ax8817x_get_drvinfo (struct net_device *net,
@@ -667,23 +827,16 @@ static void ax8817x_get_drvinfo (struct net_device *net,
        info->eedump_len = 0x3e;
 }
 
-static u32 ax8817x_get_link (struct net_device *net)
-{
-       struct usbnet *dev = (struct usbnet *)net->priv;
-
-       return (u32)mii_link_ok(&dev->mii);
-}
-
 static int ax8817x_get_settings(struct net_device *net, struct ethtool_cmd *cmd)
 {
-       struct usbnet *dev = (struct usbnet *)net->priv;
+       struct usbnet *dev = netdev_priv(net);
 
        return mii_ethtool_gset(&dev->mii,cmd);
 }
 
 static int ax8817x_set_settings(struct net_device *net, struct ethtool_cmd *cmd)
 {
-       struct usbnet *dev = (struct usbnet *)net->priv;
+       struct usbnet *dev = netdev_priv(net);
 
        return mii_ethtool_sset(&dev->mii,cmd);
 }
@@ -693,11 +846,12 @@ static int ax8817x_set_settings(struct net_device *net, struct ethtool_cmd *cmd)
    devices that may be connected at the same time. */
 static struct ethtool_ops ax8817x_ethtool_ops = {
        .get_drvinfo            = ax8817x_get_drvinfo,
-       .get_link               = ax8817x_get_link,
+       .get_link               = ethtool_op_get_link,
        .get_msglevel           = usbnet_get_msglevel,
        .set_msglevel           = usbnet_set_msglevel,
        .get_wol                = ax8817x_get_wol,
        .set_wol                = ax8817x_set_wol,
+       .get_eeprom_len         = ax8817x_get_eeprom_len,
        .get_eeprom             = ax8817x_get_eeprom,
        .get_settings           = ax8817x_get_settings,
        .set_settings           = ax8817x_set_settings,
@@ -705,45 +859,50 @@ static struct ethtool_ops ax8817x_ethtool_ops = {
 
 static int ax8817x_bind(struct usbnet *dev, struct usb_interface *intf)
 {
-       int ret;
-       u8 buf[6];
-       u16 *buf16 = (u16 *) buf;
+       int ret = 0;
+       void *buf;
        int i;
        unsigned long gpio_bits = dev->driver_info->data;
 
-       dev->in = usb_rcvbulkpipe(dev->udev, 3);
-       dev->out = usb_sndbulkpipe(dev->udev, 2);
+       get_endpoints(dev,intf);
+
+       buf = kmalloc(ETH_ALEN, GFP_KERNEL);
+       if(!buf) {
+               ret = -ENOMEM;
+               goto out1;
+       }
 
        /* Toggle the GPIOs in a manufacturer/model specific way */
        for (i = 2; i >= 0; i--) {
                if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
                                        (gpio_bits >> (i * 8)) & 0xff, 0, 0,
                                        buf)) < 0)
-                       return ret;
+                       goto out2;
                msleep(5);
        }
 
        if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, 0x80, 0, 0, buf)) < 0) {
                dbg("send AX_CMD_WRITE_RX_CTL failed: %d", ret);
-               return ret;
+               goto out2;
        }
 
        /* Get the MAC address */
        memset(buf, 0, ETH_ALEN);
        if ((ret = ax8817x_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, 6, buf)) < 0) {
                dbg("read AX_CMD_READ_NODE_ID failed: %d", ret);
-               return ret;
+               goto out2;
        }
        memcpy(dev->net->dev_addr, buf, ETH_ALEN);
 
        /* Get the PHY id */
        if ((ret = ax8817x_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf)) < 0) {
                dbg("error on read AX_CMD_READ_PHY_ID: %02x", ret);
-               return ret;
+               goto out2;
        } else if (ret < 2) {
                /* this should always return 2 bytes */
                dbg("AX_CMD_READ_PHY_ID returned less than 2 bytes: ret=%02x", ret);
-               return -EIO;
+               ret = -EIO;
+               goto out2;
        }
 
        /* Initialize MII structure */
@@ -752,44 +911,290 @@ static int ax8817x_bind(struct usbnet *dev, struct usb_interface *intf)
        dev->mii.mdio_write = ax8817x_mdio_write;
        dev->mii.phy_id_mask = 0x3f;
        dev->mii.reg_num_mask = 0x1f;
-       dev->mii.phy_id = buf[1];
+       dev->mii.phy_id = *((u8 *)buf + 1);
+
+       dev->net->set_multicast_list = ax8817x_set_multicast;
+       dev->net->ethtool_ops = &ax8817x_ethtool_ops;
+
+       ax8817x_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
+       ax8817x_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+               ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
+       mii_nway_restart(&dev->mii);
+
+       return 0;
+out2:
+       kfree(buf);
+out1:
+       return ret;
+}
+
+static struct ethtool_ops ax88772_ethtool_ops = {
+       .get_drvinfo            = ax8817x_get_drvinfo,
+       .get_link               = ethtool_op_get_link,
+       .get_msglevel           = usbnet_get_msglevel,
+       .set_msglevel           = usbnet_set_msglevel,
+       .get_wol                = ax8817x_get_wol,
+       .set_wol                = ax8817x_set_wol,
+       .get_eeprom_len         = ax8817x_get_eeprom_len,
+       .get_eeprom             = ax8817x_get_eeprom,
+       .get_settings           = ax8817x_get_settings,
+       .set_settings           = ax8817x_set_settings,
+};
+
+static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+       int ret;
+       void *buf;
+
+       get_endpoints(dev,intf);
 
-       if ((ret = ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, &buf)) < 0) {
-               dbg("Failed to go to software MII mode: %02x", ret);
-               return ret;
+       buf = kmalloc(6, GFP_KERNEL);
+       if(!buf) {
+               dbg ("Cannot allocate memory for buffer");
+               ret = -ENOMEM;
+               goto out1;
        }
 
-       *buf16 = cpu_to_le16(BMCR_RESET);
-       if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MII_REG,
-                                    dev->mii.phy_id, MII_BMCR, 2, buf16)) < 0) {
-               dbg("Failed to write MII reg - MII_BMCR: %02x", ret);
-               return ret;
+       if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+                                    0x00B0, 0, 0, buf)) < 0)
+               goto out2;
+
+       msleep(5);
+       if ((ret = ax8817x_write_cmd(dev, AX_CMD_SW_PHY_SELECT, 0x0001, 0, 0, buf)) < 0) {
+               dbg("Select PHY #1 failed: %d", ret);
+               goto out2;
        }
 
-       /* Advertise that we can do full-duplex pause */
-       *buf16 = cpu_to_le16(ADVERTISE_ALL | ADVERTISE_CSMA | 0x0400);
-       if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MII_REG,
-                                    dev->mii.phy_id, MII_ADVERTISE, 
-                                    2, buf16)) < 0) {
-               dbg("Failed to write MII_REG advertisement: %02x", ret);
-               return ret;
+       if ((ret =
+            ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPPD, 0, 0, buf)) < 0) {
+               dbg("Failed to power down internal PHY: %d", ret);
+               goto out2;
        }
 
-       *buf16 = cpu_to_le16(BMCR_ANENABLE | BMCR_ANRESTART);
-       if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MII_REG,
-                                    dev->mii.phy_id, MII_BMCR, 
-                                    2, buf16)) < 0) {
-               dbg("Failed to write MII reg autonegotiate: %02x", ret);
-               return ret;
+       msleep(150);
+       if ((ret =
+            ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_CLEAR, 0, 0, buf)) < 0) {
+               dbg("Failed to perform software reset: %d", ret);
+               goto out2;
        }
 
-       if ((ret = ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, &buf)) < 0) {
-               dbg("Failed to set hardware MII: %02x", ret);
-               return ret;
+       msleep(150);
+       if ((ret =
+            ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPRL | AX_SWRESET_PRL, 0, 0, buf)) < 0) {
+               dbg("Failed to set Internal/External PHY reset control: %d", ret);
+               goto out2;
+       }
+
+       msleep(150);
+       if ((ret =
+            ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, 0x0000, 0, 0,
+                              buf)) < 0) {
+               dbg("Failed to reset RX_CTL: %d", ret);
+               goto out2;
+       }
+
+       /* Get the MAC address */
+       memset(buf, 0, ETH_ALEN);
+       if ((ret = ax8817x_read_cmd(dev, AX88772_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf)) < 0) {
+               dbg("Failed to read MAC address: %d", ret);
+               goto out2;
+       }
+       memcpy(dev->net->dev_addr, buf, ETH_ALEN);
+
+       if ((ret = ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, buf)) < 0) {
+               dbg("Enabling software MII failed: %d", ret);
+               goto out2;
+       }
+
+       if (((ret =
+             ax8817x_read_cmd(dev, AX_CMD_READ_MII_REG, 0x0010, 2, 2, buf)) < 0)
+           || (*((u16 *)buf) != 0x003b)) {
+               dbg("Read PHY register 2 must be 0x3b00: %d", ret);
+               goto out2;
+       }
+
+       /* Initialize MII structure */
+       dev->mii.dev = dev->net;
+       dev->mii.mdio_read = ax8817x_mdio_read;
+       dev->mii.mdio_write = ax8817x_mdio_write;
+       dev->mii.phy_id_mask = 0xff;
+       dev->mii.reg_num_mask = 0xff;
+
+       /* Get the PHY id */
+       if ((ret = ax8817x_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf)) < 0) {
+               dbg("Error reading PHY ID: %02x", ret);
+               goto out2;
+       } else if (ret < 2) {
+               /* this should always return 2 bytes */
+               dbg("AX_CMD_READ_PHY_ID returned less than 2 bytes: ret=%02x",
+                   ret);
+               ret = -EIO;
+               goto out2;
+       }
+       dev->mii.phy_id = *((u8 *)buf + 1);
+
+       if ((ret =
+            ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_PRL, 0, 0, buf)) < 0) {
+               dbg("Set external PHY reset pin level: %d", ret);
+               goto out2;
+       }
+       msleep(150);
+       if ((ret =
+            ax8817x_write_cmd(dev, AX_CMD_SW_RESET, AX_SWRESET_IPRL | AX_SWRESET_PRL, 0, 0, buf)) < 0) {
+               dbg("Set Internal/External PHY reset control: %d", ret);
+               goto out2;
        }
+       msleep(150);
+
 
        dev->net->set_multicast_list = ax8817x_set_multicast;
-       dev->net->ethtool_ops = &ax8817x_ethtool_ops;
+       dev->net->ethtool_ops = &ax88772_ethtool_ops;
+
+       ax8817x_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
+       ax8817x_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+                       ADVERTISE_ALL | ADVERTISE_CSMA);
+       mii_nway_restart(&dev->mii);
+
+       if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, AX88772_MEDIUM_DEFAULT, 0, 0, buf)) < 0) {
+               dbg("Write medium mode register: %d", ret);
+               goto out2;
+       }
+
+       if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_IPG0, AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,AX88772_IPG2_DEFAULT, 0, buf)) < 0) {
+               dbg("Write IPG,IPG1,IPG2 failed: %d", ret);
+               goto out2;
+       }
+       if ((ret =
+            ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, &buf)) < 0) {
+               dbg("Failed to set hardware MII: %02x", ret);
+               goto out2;
+       }
+
+       /* Set RX_CTL to default values with 2k buffer, and enable cactus */
+       if ((ret =
+            ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, 0x0088, 0, 0,
+                              buf)) < 0) {
+               dbg("Reset RX_CTL failed: %d", ret);
+               goto out2;
+       }
+
+       kfree(buf);
+
+       return 0;
+
+out2:
+       kfree(buf);
+out1:
+       return ret;
+}
+
+static int ax88772_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+       u32 *header;
+       char *packet;
+       struct sk_buff *ax_skb;
+       u16 size;
+
+       header = (u32 *) skb->data;
+       le32_to_cpus(header);
+       packet = (char *)(header + 1);
+
+       skb_pull(skb, 4);
+
+       while (skb->len > 0) {
+               if ((short)(*header & 0x0000ffff) !=
+                   ~((short)((*header & 0xffff0000) >> 16))) {
+                       devdbg(dev,"header length data is error");
+               }
+               /* get the packet length */
+               size = (u16) (*header & 0x0000ffff);
+
+               if ((skb->len) - ((size + 1) & 0xfffe) == 0)
+                       return 2;
+               if (size > ETH_FRAME_LEN) {
+                       devdbg(dev,"invalid rx length %d", size);
+                       return 0;
+               }
+               ax_skb = skb_clone(skb, GFP_ATOMIC);
+               if (ax_skb) {
+                       ax_skb->len = size;
+                       ax_skb->data = packet;
+                       ax_skb->tail = packet + size;
+                       skb_return(dev, ax_skb);
+               } else {
+                       return 0;
+               }
+
+               skb_pull(skb, (size + 1) & 0xfffe);
+
+               if (skb->len == 0)
+                       break;
+
+               header = (u32 *) skb->data;
+               le32_to_cpus(header);
+               packet = (char *)(header + 1);
+               skb_pull(skb, 4);
+       }
+
+       if (skb->len < 0) {
+               devdbg(dev,"invalid rx length %d", skb->len);
+               return 0;
+       }
+       return 1;
+}
+
+static struct sk_buff *ax88772_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
+                                       int flags)
+{
+       int padlen;
+       int headroom = skb_headroom(skb);
+       int tailroom = skb_tailroom(skb);
+       u32 *packet_len;
+       u32 *padbytes_ptr;
+
+       padlen = ((skb->len + 4) % 512) ? 0 : 4;
+
+       if ((!skb_cloned(skb))
+           && ((headroom + tailroom) >= (4 + padlen))) {
+               if ((headroom < 4) || (tailroom < padlen)) {
+                       skb->data = memmove(skb->head + 4, skb->data, skb->len);
+                       skb->tail = skb->data + skb->len;
+               }
+       } else {
+               struct sk_buff *skb2;
+               skb2 = skb_copy_expand(skb, 4, padlen, flags);
+               dev_kfree_skb_any(skb);
+               skb = skb2;
+               if (!skb)
+                       return NULL;
+       }
+
+       packet_len = (u32 *) skb_push(skb, 4);
+
+       packet_len = (u32 *) skb->data;
+       *packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4);
+
+       if ((skb->len % 512) == 0) {
+               padbytes_ptr = (u32 *) skb->tail;
+               *padbytes_ptr = 0xffff0000;
+               skb_put(skb, padlen);
+       }
+       return skb;
+}
+
+static int ax88772_link_reset(struct usbnet *dev)
+{
+       u16 lpa;
+       u16 mode;
+
+       mode = AX88772_MEDIUM_DEFAULT;
+       lpa = ax8817x_mdio_read(dev->net, dev->mii.phy_id, MII_LPA);
+
+       if ((lpa & LPA_DUPLEX) == 0)
+               mode &= ~AX88772_MEDIUM_FULL_DUPLEX;
+       if ((lpa & LPA_100) == 0)
+               mode &= ~AX88772_MEDIUM_100MB;
+       ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL);
 
        return 0;
 }
@@ -797,6 +1202,9 @@ static int ax8817x_bind(struct usbnet *dev, struct usb_interface *intf)
 static const struct driver_info ax8817x_info = {
        .description = "ASIX AX8817x USB 2.0 Ethernet",
        .bind = ax8817x_bind,
+       .status = ax8817x_status,
+       .link_reset = ax88172_link_reset,
+       .reset = ax88172_link_reset,
        .flags =  FLAG_ETHER,
        .data = 0x00130103,
 };
@@ -804,6 +1212,9 @@ static const struct driver_info ax8817x_info = {
 static const struct driver_info dlink_dub_e100_info = {
        .description = "DLink DUB-E100 USB Ethernet",
        .bind = ax8817x_bind,
+       .status = ax8817x_status,
+       .link_reset = ax88172_link_reset,
+       .reset = ax88172_link_reset,
        .flags =  FLAG_ETHER,
        .data = 0x009f9d9f,
 };
@@ -811,6 +1222,9 @@ static const struct driver_info dlink_dub_e100_info = {
 static const struct driver_info netgear_fa120_info = {
        .description = "Netgear FA-120 USB Ethernet",
        .bind = ax8817x_bind,
+       .status = ax8817x_status,
+       .link_reset = ax88172_link_reset,
+       .reset = ax88172_link_reset,
        .flags =  FLAG_ETHER,
        .data = 0x00130103,
 };
@@ -818,10 +1232,25 @@ static const struct driver_info netgear_fa120_info = {
 static const struct driver_info hawking_uf200_info = {
        .description = "Hawking UF200 USB Ethernet",
        .bind = ax8817x_bind,
+       .status = ax8817x_status,
+       .link_reset = ax88172_link_reset,
+       .reset = ax88172_link_reset,
        .flags =  FLAG_ETHER,
        .data = 0x001f1d1f,
 };
 
+static const struct driver_info ax88772_info = {
+       .description = "ASIX AX88772 USB 2.0 Ethernet",
+       .bind = ax88772_bind,
+       .status = ax8817x_status,
+       .link_reset = ax88772_link_reset,
+       .reset = ax88772_link_reset,
+       .flags = FLAG_ETHER | FLAG_FRAMING_AX,
+       .rx_fixup = ax88772_rx_fixup,
+       .tx_fixup = ax88772_tx_fixup,
+       .data = 0x00130103,
+};
+
 #endif /* CONFIG_USB_AX8817X */
 
 
@@ -869,45 +1298,14 @@ static const struct driver_info  belkin_info = {
 
 #ifdef NEED_GENERIC_CDC
 
-/* "Header Functional Descriptor" from CDC spec  5.2.3.1 */
-struct header_desc {
-       u8      bLength;
-       u8      bDescriptorType;
-       u8      bDescriptorSubType;
-
-       u16     bcdCDC;
-} __attribute__ ((packed));
-
-/* "Union Functional Descriptor" from CDC spec 5.2.3.X */
-struct union_desc {
-       u8      bLength;
-       u8      bDescriptorType;
-       u8      bDescriptorSubType;
-
-       u8      bMasterInterface0;
-       u8      bSlaveInterface0;
-       /* ... and there could be other slave interfaces */
-} __attribute__ ((packed));
-
-/* "Ethernet Networking Functional Descriptor" from CDC spec 5.2.3.16 */
-struct ether_desc {
-       u8      bLength;
-       u8      bDescriptorType;
-       u8      bDescriptorSubType;
-
-       u8      iMACAddress;
-       u32     bmEthernetStatistics;
-       u16     wMaxSegmentSize;
-       u16     wNumberMCFilters;
-       u8      bNumberPowerFilters;
-} __attribute__ ((packed));
+#include <linux/usb_cdc.h>
 
 struct cdc_state {
-       struct header_desc      *header;
-       struct union_desc       *u;
-       struct ether_desc       *ether;
-       struct usb_interface    *control;
-       struct usb_interface    *data;
+       struct usb_cdc_header_desc      *header;
+       struct usb_cdc_union_desc       *u;
+       struct usb_cdc_ether_desc       *ether;
+       struct usb_interface            *control;
+       struct usb_interface            *data;
 };
 
 static struct usb_driver usbnet_driver;
@@ -962,7 +1360,7 @@ static int generic_cdc_bind (struct usbnet *dev, struct usb_interface *intf)
                 * CDC Ethernet achieves with a simple descriptor.
                 */
                switch (buf [2]) {
-               case 0x00:              /* Header, mostly useless */
+               case USB_CDC_HEADER_TYPE:
                        if (info->header) {
                                dev_dbg (&intf->dev, "extra CDC header\n");
                                goto bad_desc;
@@ -974,7 +1372,7 @@ static int generic_cdc_bind (struct usbnet *dev, struct usb_interface *intf)
                                goto bad_desc;
                        }
                        break;
-               case 0x06:              /* Union (groups interfaces) */
+               case USB_CDC_UNION_TYPE:
                        if (info->u) {
                                dev_dbg (&intf->dev, "extra CDC union\n");
                                goto bad_desc;
@@ -1023,7 +1421,7 @@ static int generic_cdc_bind (struct usbnet *dev, struct usb_interface *intf)
                                goto bad_desc;
                        }
                        break;
-               case 0x0F:              /* Ethernet Networking */
+               case USB_CDC_ETHERNET_TYPE:
                        if (info->ether) {
                                dev_dbg (&intf->dev, "extra CDC ether\n");
                                goto bad_desc;
@@ -1034,7 +1432,7 @@ static int generic_cdc_bind (struct usbnet *dev, struct usb_interface *intf)
                                        info->u->bLength);
                                goto bad_desc;
                        }
-                       dev->net->mtu = cpu_to_le16p (
+                       dev->net->mtu = le16_to_cpup (
                                                &info->ether->wMaxSegmentSize)
                                        - ETH_HLEN;
                        /* because of Zaurus, we may be ignoring the host
@@ -1068,6 +1466,29 @@ next_desc:
                usb_driver_release_interface (&usbnet_driver, info->data);
                return status;
        }
+
+       /* status endpoint: optional for CDC Ethernet, not RNDIS (or ACM) */
+       dev->status = NULL;
+       if (info->control->cur_altsetting->desc.bNumEndpoints == 1) {
+               struct usb_endpoint_descriptor  *desc;
+
+               dev->status = &info->control->cur_altsetting->endpoint [0];
+               desc = &dev->status->desc;
+               if (desc->bmAttributes != USB_ENDPOINT_XFER_INT
+                               || !(desc->bEndpointAddress & USB_DIR_IN)
+                               || (le16_to_cpu(desc->wMaxPacketSize)
+                                       < sizeof (struct usb_cdc_notification))
+                               || !desc->bInterval) {
+                       dev_dbg (&intf->dev, "bad notification endpoint\n");
+                       dev->status = NULL;
+               }
+       }
+       if (rndis && !dev->status) {
+               dev_dbg (&intf->dev, "missing RNDIS status endpoint\n");
+               usb_set_intfdata(info->data, NULL);
+               usb_driver_release_interface (&usbnet_driver, info->data);
+               return -ENODEV;
+       }
        return 0;
 
 bad_desc:
@@ -1084,7 +1505,7 @@ static void cdc_unbind (struct usbnet *dev, struct usb_interface *intf)
                /* ensure immediate exit from usbnet_disconnect */
                usb_set_intfdata(info->data, NULL);
                usb_driver_release_interface (&usbnet_driver, info->data);
-               info->data = 0;
+               info->data = NULL;
        }
 
        /* and vice versa (just in case) */
@@ -1092,7 +1513,7 @@ static void cdc_unbind (struct usbnet *dev, struct usb_interface *intf)
                /* ensure immediate exit from usbnet_disconnect */
                usb_set_intfdata(info->control, NULL);
                usb_driver_release_interface (&usbnet_driver, info->control);
-               info->control = 0;
+               info->control = NULL;
        }
 }
 
@@ -1105,7 +1526,7 @@ static void cdc_unbind (struct usbnet *dev, struct usb_interface *intf)
 /*-------------------------------------------------------------------------
  *
  * Communications Device Class, Ethernet Control model
- * 
+ *
  * Takes two interfaces.  The DATA interface is inactive till an altsetting
  * is selected.  Configuration data includes class descriptors.
  *
@@ -1116,6 +1537,56 @@ static void cdc_unbind (struct usbnet *dev, struct usb_interface *intf)
 
 #include <linux/ctype.h>
 
+
+static void dumpspeed (struct usbnet *dev, __le32 *speeds)
+{
+       if (netif_msg_timer (dev))
+               devinfo (dev, "link speeds: %u kbps up, %u kbps down",
+                       __le32_to_cpu(speeds[0]) / 1000,
+               __le32_to_cpu(speeds[1]) / 1000);
+}
+
+static void cdc_status (struct usbnet *dev, struct urb *urb)
+{
+       struct usb_cdc_notification     *event;
+
+       if (urb->actual_length < sizeof *event)
+               return;
+       
+       /* SPEED_CHANGE can get split into two 8-byte packets */
+       if (test_and_clear_bit (EVENT_STS_SPLIT, &dev->flags)) {
+               dumpspeed (dev, (__le32 *) urb->transfer_buffer);
+               return;
+       }
+
+       event = urb->transfer_buffer;
+       switch (event->bNotificationType) {
+       case USB_CDC_NOTIFY_NETWORK_CONNECTION:
+               if (netif_msg_timer (dev))
+                       devdbg (dev, "CDC: carrier %s",
+                                       event->wValue ? "on" : "off");
+               if (event->wValue)
+                       netif_carrier_on(dev->net);
+               else
+                       netif_carrier_off(dev->net);
+               break;
+       case USB_CDC_NOTIFY_SPEED_CHANGE:       /* tx/rx rates */
+               if (netif_msg_timer (dev))
+                       devdbg (dev, "CDC: speed change (len %d)",
+                                       urb->actual_length);
+               if (urb->actual_length != (sizeof *event + 8))
+                       set_bit (EVENT_STS_SPLIT, &dev->flags);
+               else
+                       dumpspeed (dev, (__le32 *) &event[1]);
+               break;
+       // case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:      /* RNDIS; or unsolicited */
+       default:
+               deverr (dev, "CDC: unexpected notification %02x!",
+                                event->bNotificationType);
+               break;
+       }
+}
+
 static u8 nibble (unsigned char c)
 {
        if (likely (isdigit (c)))
@@ -1127,7 +1598,7 @@ static u8 nibble (unsigned char c)
 }
 
 static inline int
-get_ethernet_addr (struct usbnet *dev, struct ether_desc *e)
+get_ethernet_addr (struct usbnet *dev, struct usb_cdc_ether_desc *e)
 {
        int             tmp, i;
        unsigned char   buf [13];
@@ -1157,6 +1628,7 @@ static int cdc_bind (struct usbnet *dev, struct usb_interface *intf)
 
        status = get_ethernet_addr (dev, info->ether);
        if (status < 0) {
+               usb_set_intfdata(info->data, NULL);
                usb_driver_release_interface (&usbnet_driver, info->data);
                return status;
        }
@@ -1173,6 +1645,7 @@ static const struct driver_info   cdc_info = {
        // .check_connect = cdc_check_connect,
        .bind =         cdc_bind,
        .unbind =       cdc_unbind,
+       .status =       cdc_status,
 };
 
 #endif /* CONFIG_USB_CDCETHER */
@@ -1255,7 +1728,7 @@ struct gl_header {
        struct gl_packet        packets;
 };
 
-#ifdef GENLINK_ACK
+#ifdef GENELINK_ACK
 
 // FIXME:  this code is incomplete, not debugged; it doesn't
 // handle interrupts correctly.  interrupts should be generic
@@ -1278,7 +1751,7 @@ static inline int gl_control_write (struct usbnet *dev, u8 request, u16 value)
                      0,                        // index
                      0,                        // data buffer
                      0,                        // size
-                     CONTROL_TIMEOUT_JIFFIES);
+                     CONTROL_TIMEOUT_MS);
        return retval;
 }
 
@@ -1396,7 +1869,7 @@ static int genelink_free (struct usbnet *dev)
 // handling needs to be generic)
 
        // cancel irq urb first
-       usb_unlink_urb (priv->irq_urb);
+       usb_kill_urb (priv->irq_urb);
 
        // free irq urb
        usb_free_urb (priv->irq_urb);
@@ -1559,9 +2032,9 @@ static const struct driver_info   genelink_info = {
  */
 
 struct nc_header {             // packed:
-       u16     hdr_len;                // sizeof nc_header (LE, all)
-       u16     packet_len;             // payload size (including ethhdr)
-       u16     packet_id;              // detects dropped packets
+       __le16  hdr_len;                // sizeof nc_header (LE, all)
+       __le16  packet_len;             // payload size (including ethhdr)
+       __le16  packet_id;              // detects dropped packets
 #define MIN_HEADER     6
 
        // all else is optional, and must start with:
@@ -1572,7 +2045,7 @@ struct nc_header {                // packed:
 #define        PAD_BYTE        ((unsigned char)0xAC)
 
 struct nc_trailer {
-       u16     packet_id;
+       __le16  packet_id;
 } __attribute__((__packed__));
 
 // packets may use FLAG_FRAMING_NC and optional pad
@@ -1615,7 +2088,7 @@ nc_vendor_read (struct usbnet *dev, u8 req, u8 regnum, u16 *retval_ptr)
                USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
                0, regnum,
                retval_ptr, sizeof *retval_ptr,
-               CONTROL_TIMEOUT_JIFFIES);
+               CONTROL_TIMEOUT_MS);
        if (status > 0)
                status = 0;
        if (!status)
@@ -1638,8 +2111,8 @@ nc_vendor_write (struct usbnet *dev, u8 req, u8 regnum, u16 value)
                req,
                USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
                value, regnum,
-               0, 0,                   // data is in setup packet
-               CONTROL_TIMEOUT_JIFFIES);
+               NULL, 0,                        // data is in setup packet
+               CONTROL_TIMEOUT_MS);
 }
 
 static inline void
@@ -1704,7 +2177,8 @@ static void nc_dump_registers (struct usbnet *dev)
 
 static inline void nc_dump_usbctl (struct usbnet *dev, u16 usbctl)
 {
-#ifdef DEBUG
+       if (!netif_msg_link (dev))
+               return;
        devdbg (dev, "net1080 %s-%s usbctl 0x%x:%s%s%s%s%s;"
                        " this%s%s;"
                        " other%s%s; r/o 0x%x",
@@ -1722,7 +2196,6 @@ static inline void nc_dump_usbctl (struct usbnet *dev, u16 usbctl)
                (usbctl & USBCTL_DISCONN_THIS) ? " DIS" : "",
                usbctl & ~USBCTL_WRITABLE_MASK
                );
-#endif
 }
 
 /*-------------------------------------------------------------------------*/
@@ -1749,7 +2222,8 @@ static inline void nc_dump_usbctl (struct usbnet *dev, u16 usbctl)
 
 static inline void nc_dump_status (struct usbnet *dev, u16 status)
 {
-#ifdef DEBUG
+       if (!netif_msg_link (dev))
+               return;
        devdbg (dev, "net1080 %s-%s status 0x%x:"
                        " this (%c) PKT=%d%s%s%s;"
                        " other PKT=%d%s%s%s; unspec 0x%x",
@@ -1772,7 +2246,6 @@ static inline void nc_dump_status (struct usbnet *dev, u16 status)
 
                status & STATUS_UNSPEC_MASK
                );
-#endif
 }
 
 /*-------------------------------------------------------------------------*/
@@ -1787,15 +2260,10 @@ static inline void nc_dump_status (struct usbnet *dev, u16 status)
 
 static inline void nc_dump_ttl (struct usbnet *dev, u16 ttl)
 {
-#ifdef DEBUG
-       devdbg (dev, "net1080 %s-%s ttl 0x%x this = %d, other = %d",
-               dev->udev->bus->bus_name, dev->udev->devpath,
-               ttl,
-
-               TTL_THIS (ttl),
-               TTL_OTHER (ttl)
-               );
-#endif
+       if (netif_msg_link (dev))
+               devdbg (dev, "net1080 %s-%s ttl 0x%x this = %d, other = %d",
+                       dev->udev->bus->bus_name, dev->udev->devpath,
+                       ttl, TTL_THIS (ttl), TTL_OTHER (ttl));
 }
 
 /*-------------------------------------------------------------------------*/
@@ -1817,14 +2285,14 @@ static int net1080_reset (struct usbnet *dev)
                goto done;
        }
        status = *vp;
-       // nc_dump_status (dev, status);
+       nc_dump_status (dev, status);
 
        if ((retval = nc_register_read (dev, REG_USBCTL, vp)) < 0) {
                dbg ("can't read USBCTL, %d", retval);
                goto done;
        }
        usbctl = *vp;
-       // nc_dump_usbctl (dev, usbctl);
+       nc_dump_usbctl (dev, usbctl);
 
        nc_register_write (dev, REG_USBCTL,
                        USBCTL_FLUSH_THIS | USBCTL_FLUSH_OTHER);
@@ -1840,7 +2308,7 @@ static int net1080_reset (struct usbnet *dev)
                        MK_TTL (NC_READ_TTL_MS, TTL_OTHER (ttl)) );
        dbg ("%s: assigned TTL, %d ms", dev->net->name, NC_READ_TTL_MS);
 
-       if (dev->msg_level >= 2)
+       if (netif_msg_link (dev))
                devinfo (dev, "port %c, peer %sconnected",
                        (status & STATUS_PORT_A) ? 'A' : 'B',
                        (status & STATUS_CONN_OTHER) ? "" : "dis"
@@ -1921,7 +2389,8 @@ static void nc_ensure_sync (struct usbnet *dev)
                        return;
                }
 
-               devdbg (dev, "flush net1080; too many framing errors");
+               if (netif_msg_rx_err (dev))
+                       devdbg (dev, "flush net1080; too many framing errors");
                dev->frame_errors = 0;
        }
 }
@@ -1930,6 +2399,7 @@ static int net1080_rx_fixup (struct usbnet *dev, struct sk_buff *skb)
 {
        struct nc_header        *header;
        struct nc_trailer       *trailer;
+       u16                     hdr_len, packet_len;
 
        if (!(skb->len & 0x01)
                        || MIN_FRAMED > skb->len
@@ -1943,50 +2413,50 @@ static int net1080_rx_fixup (struct usbnet *dev, struct sk_buff *skb)
        }
 
        header = (struct nc_header *) skb->data;
-       le16_to_cpus (&header->hdr_len);
-       le16_to_cpus (&header->packet_len);
-       if (FRAMED_SIZE (header->packet_len) > MAX_PACKET) {
+       hdr_len = le16_to_cpup (&header->hdr_len);
+       packet_len = le16_to_cpup (&header->packet_len);
+       if (FRAMED_SIZE (packet_len) > MAX_PACKET) {
                dev->stats.rx_frame_errors++;
-               dbg ("packet too big, %d", header->packet_len);
+               dbg ("packet too big, %d", packet_len);
                nc_ensure_sync (dev);
                return 0;
-       } else if (header->hdr_len < MIN_HEADER) {
+       } else if (hdr_len < MIN_HEADER) {
                dev->stats.rx_frame_errors++;
-               dbg ("header too short, %d", header->hdr_len);
+               dbg ("header too short, %d", hdr_len);
                nc_ensure_sync (dev);
                return 0;
-       } else if (header->hdr_len > MIN_HEADER) {
+       } else if (hdr_len > MIN_HEADER) {
                // out of band data for us?
-               dbg ("header OOB, %d bytes",
-                       header->hdr_len - MIN_HEADER);
+               dbg ("header OOB, %d bytes", hdr_len - MIN_HEADER);
                nc_ensure_sync (dev);
                // switch (vendor/product ids) { ... }
        }
-       skb_pull (skb, header->hdr_len);
+       skb_pull (skb, hdr_len);
 
        trailer = (struct nc_trailer *)
                (skb->data + skb->len - sizeof *trailer);
        skb_trim (skb, skb->len - sizeof *trailer);
 
-       if ((header->packet_len & 0x01) == 0) {
-               if (skb->data [header->packet_len] != PAD_BYTE) {
+       if ((packet_len & 0x01) == 0) {
+               if (skb->data [packet_len] != PAD_BYTE) {
                        dev->stats.rx_frame_errors++;
                        dbg ("bad pad");
                        return 0;
                }
                skb_trim (skb, skb->len - 1);
        }
-       if (skb->len != header->packet_len) {
+       if (skb->len != packet_len) {
                dev->stats.rx_frame_errors++;
                dbg ("bad packet len %d (expected %d)",
-                       skb->len, header->packet_len);
+                       skb->len, packet_len);
                nc_ensure_sync (dev);
                return 0;
        }
        if (header->packet_id != get_unaligned (&trailer->packet_id)) {
                dev->stats.rx_fifo_errors++;
                dbg ("(2+ dropped) rx packet_id mismatch 0x%x 0x%x",
-                       header->packet_id, trailer->packet_id);
+                       le16_to_cpu (header->packet_id),
+                       le16_to_cpu (trailer->packet_id));
                return 0;
        }
 #if 0
@@ -2081,8 +2551,8 @@ pl_vendor_req (struct usbnet *dev, u8 req, u8 val, u8 index)
                req,
                USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
                val, index,
-               0, 0,
-               CONTROL_TIMEOUT_JIFFIES);
+               NULL, 0,
+               CONTROL_TIMEOUT_MS);
 }
 
 static inline int
@@ -2118,6 +2588,13 @@ static const struct driver_info  prolific_info = {
 
 #endif /* CONFIG_USB_PL2301 */
 
+\f
+#ifdef CONFIG_USB_KC2190
+#define HAVE_HARDWARE
+static const struct driver_info kc2190_info = {
+       .description =  "KC Technology KC-190",
+};
+#endif /* CONFIG_USB_KC2190 */
 
 \f
 #ifdef CONFIG_USB_ARMLINUX
@@ -2171,11 +2648,17 @@ static const struct driver_info blob_info = {
  * For the current version of that driver, the main way that framing is
  * nonstandard (also from perspective of the CDC ethernet model!) is a
  * crc32, added to help detect when some sa1100 usb-to-memory DMA errata
- * haven't been fully worked around.
+ * haven't been fully worked around.  Also, all Zaurii use the same
+ * default Ethernet address.
  *
  * PXA based models use the same framing, and also can't implement
  * set_interface properly.
  *
+ * All known Zaurii lie about their standards conformance.  Most lie by
+ * saying they support CDC Ethernet.  Some lie and say they support CDC
+ * MDLM (as if for access to cell phone modems).  Someone, please beat 
+ * on Sharp (and other such vendors) for a while with a cluestick.
+ *
  *-------------------------------------------------------------------------*/
 
 static struct sk_buff *
@@ -2215,14 +2698,163 @@ static const struct driver_info        zaurus_sl5x00_info = {
        .unbind =       cdc_unbind,
        .tx_fixup =     zaurus_tx_fixup,
 };
+#define        ZAURUS_STRONGARM_INFO   ((unsigned long)&zaurus_sl5x00_info)
+
 static const struct driver_info        zaurus_pxa_info = {
        .description =  "Sharp Zaurus, PXA-2xx based",
        .flags =        FLAG_FRAMING_Z,
        .check_connect = always_connected,
+       .bind =         generic_cdc_bind,
+       .unbind =       cdc_unbind,
        .tx_fixup =     zaurus_tx_fixup,
+};
+#define        ZAURUS_PXA_INFO         ((unsigned long)&zaurus_pxa_info)
 
-       .in = 1, .out = 2,
+static const struct driver_info        olympus_mxl_info = {
+       .description =  "Olympus R1000",
+       .flags =        FLAG_FRAMING_Z,
+       .check_connect = always_connected,
+       .bind =         generic_cdc_bind,
+       .unbind =       cdc_unbind,
+       .tx_fixup =     zaurus_tx_fixup,
 };
+#define        OLYMPUS_MXL_INFO        ((unsigned long)&olympus_mxl_info)
+
+
+/* Some more recent products using Lineo/Belcarra code will wrongly claim
+ * CDC MDLM conformance.  They aren't conformant:  data endpoints live
+ * in the control interface, there's no data interface, and it's not used
+ * to talk to a cell phone radio.  But at least we can detect these two
+ * pseudo-classes, rather than growing this product list with entries for
+ * each new nonconformant product (sigh).
+ */
+static const u8 safe_guid[16] = {
+       0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6,
+       0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f,
+};
+static const u8 blan_guid[16] = {
+       0x74, 0xf0, 0x3d, 0xbd, 0x1e, 0xc1, 0x44, 0x70,
+       0xa3, 0x67, 0x71, 0x34, 0xc9, 0xf5, 0x54, 0x37,
+};
+
+static int blan_mdlm_bind (struct usbnet *dev, struct usb_interface *intf)
+{
+       u8                              *buf = intf->cur_altsetting->extra;
+       int                             len = intf->cur_altsetting->extralen;
+       struct usb_cdc_mdlm_desc        *desc = NULL;
+       struct usb_cdc_mdlm_detail_desc *detail = NULL;
+
+       while (len > 3) {
+               if (buf [1] != USB_DT_CS_INTERFACE)
+                       goto next_desc;
+
+               /* use bDescriptorSubType, and just verify that we get a
+                * "BLAN" (or "SAFE") descriptor.
+                */
+               switch (buf [2]) {
+               case USB_CDC_MDLM_TYPE:
+                       if (desc) {
+                               dev_dbg (&intf->dev, "extra MDLM\n");
+                               goto bad_desc;
+                       }
+                       desc = (void *) buf;
+                       if (desc->bLength != sizeof *desc) {
+                               dev_dbg (&intf->dev, "MDLM len %u\n",
+                                       desc->bLength);
+                               goto bad_desc;
+                       }
+                       /* expect bcdVersion 1.0, ignore */
+                       if (memcmp(&desc->bGUID, blan_guid, 16)
+                                   && memcmp(&desc->bGUID, safe_guid, 16) ) {
+                               /* hey, this one might _really_ be MDLM! */
+                               dev_dbg (&intf->dev, "MDLM guid\n");
+                               goto bad_desc;
+                       }
+                       break;
+               case USB_CDC_MDLM_DETAIL_TYPE:
+                       if (detail) {
+                               dev_dbg (&intf->dev, "extra MDLM detail\n");
+                               goto bad_desc;
+                       }
+                       detail = (void *) buf;
+                       switch (detail->bGuidDescriptorType) {
+                       case 0:                 /* "SAFE" */
+                               if (detail->bLength != (sizeof *detail + 2))
+                                       goto bad_detail;
+                               break;
+                       case 1:                 /* "BLAN" */
+                               if (detail->bLength != (sizeof *detail + 3))
+                                       goto bad_detail;
+                               break;
+                       default:
+                               goto bad_detail;
+                       }
+
+                       /* assuming we either noticed BLAN already, or will
+                        * find it soon, there are some data bytes here:
+                        *  - bmNetworkCapabilities (unused)
+                        *  - bmDataCapabilities (bits, see below)
+                        *  - bPad (ignored, for PADAFTER -- BLAN-only)
+                        * bits are:
+                        *  - 0x01 -- Zaurus framing (add CRC)
+                        *  - 0x02 -- PADBEFORE (CRC includes some padding)
+                        *  - 0x04 -- PADAFTER (some padding after CRC)
+                        *  - 0x08 -- "fermat" packet mangling (for hw bugs)
+                        * the PADBEFORE appears not to matter; we interop
+                        * with devices that use it and those that don't.
+                        */
+                       if ((detail->bDetailData[1] & ~02) != 0x01) {
+                               /* bmDataCapabilites == 0 would be fine too,
+                                * but framing is minidriver-coupled for now.
+                                */
+bad_detail:
+                               dev_dbg (&intf->dev,
+                                               "bad MDLM detail, %d %d %d\n",
+                                               detail->bLength,
+                                               detail->bDetailData[0],
+                                               detail->bDetailData[2]);
+                               goto bad_desc;
+                       }
+                       break;
+               }
+next_desc:
+               len -= buf [0]; /* bLength */
+               buf += buf [0];
+       }
+
+       if (!desc || !detail) {
+               dev_dbg (&intf->dev, "missing cdc mdlm %s%sdescriptor\n",
+                       desc ? "" : "func ",
+                       detail ? "" : "detail ");
+               goto bad_desc;
+       }
+
+       /* There's probably a CDC Ethernet descriptor there, but we can't
+        * rely on the Ethernet address it provides since not all vendors
+        * bother to make it unique.  Likewise there's no point in tracking
+        * of the CDC event notifications.
+        */
+       return get_endpoints (dev, intf);
+
+bad_desc:
+       dev_info (&dev->udev->dev, "unsupported MDLM descriptors\n");
+       return -ENODEV;
+}
+
+static const struct driver_info        bogus_mdlm_info = {
+       .description =  "pseudo-MDLM (BLAN) device",
+       .flags =        FLAG_FRAMING_Z,
+       .check_connect = always_connected,
+       .tx_fixup =     zaurus_tx_fixup,
+       .bind =         blan_mdlm_bind,
+};
+
+#else
+
+/* blacklist all those devices */
+#define        ZAURUS_STRONGARM_INFO   0
+#define        ZAURUS_PXA_INFO         0
+#define        OLYMPUS_MXL_INFO        0
 
 #endif
 
@@ -2235,7 +2867,7 @@ static const struct driver_info   zaurus_pxa_info = {
 
 static int usbnet_change_mtu (struct net_device *net, int new_mtu)
 {
-       struct usbnet   *dev = (struct usbnet *) net->priv;
+       struct usbnet   *dev = netdev_priv(net);
 
        if (new_mtu <= MIN_PACKET || new_mtu > MAX_PACKET)
                return -EINVAL;
@@ -2261,7 +2893,8 @@ static int usbnet_change_mtu (struct net_device *net, int new_mtu)
 
 static struct net_device_stats *usbnet_get_stats (struct net_device *net)
 {
-       return &((struct usbnet *) net->priv)->stats;
+       struct usbnet   *dev = netdev_priv(net);
+       return &dev->stats;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -2330,15 +2963,22 @@ static void rx_submit (struct usbnet *dev, struct urb *urb, int flags)
        if (dev->driver_info->flags & FLAG_FRAMING_RN)
                size = RNDIS_MAX_TRANSFER;
        else
+#endif
+#ifdef CONFIG_USB_AX8817X
+       if (dev->driver_info->flags & FLAG_FRAMING_AX)
+               size = 2048;
+       else
 #endif
                size = (sizeof (struct ethhdr) + dev->net->mtu);
 
-       if ((skb = alloc_skb (size, flags)) == 0) {
-               devdbg (dev, "no rx skb");
+       if ((skb = alloc_skb (size + NET_IP_ALIGN, flags)) == NULL) {
+               if (netif_msg_rx_err (dev))
+                       devdbg (dev, "no rx skb");
                defer_kevent (dev, EVENT_RX_MEMORY);
                usb_free_urb (urb);
                return;
        }
+       skb_reserve (skb, NET_IP_ALIGN);
 
        entry = (struct skb_data *) skb->cb;
        entry->urb = urb;
@@ -2363,18 +3003,21 @@ static void rx_submit (struct usbnet *dev, struct urb *urb, int flags)
                        defer_kevent (dev, EVENT_RX_MEMORY);
                        break;
                case -ENODEV:
-                       devdbg (dev, "device gone");
+                       if (netif_msg_ifdown (dev))
+                               devdbg (dev, "device gone");
                        netif_device_detach (dev->net);
                        break;
                default:
-                       devdbg (dev, "rx submit, %d", retval);
+                       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 {
-               devdbg (dev, "rx: stopped");
+               if (netif_msg_ifdown (dev))
+                       devdbg (dev, "rx: stopped");
                retval = -ENOLINK;
        }
        spin_unlock_irqrestore (&dev->rxq.lock, lockflags);
@@ -2397,7 +3040,8 @@ static inline void rx_process (struct usbnet *dev, struct sk_buff *skb)
        if (skb->len)
                skb_return (dev, skb);
        else {
-               devdbg (dev, "drop");
+               if (netif_msg_rx_err (dev))
+                       devdbg (dev, "drop");
 error:
                dev->stats.rx_errors++;
                skb_queue_tail (&dev->done, skb);
@@ -2415,7 +3059,7 @@ static void rx_complete (struct urb *urb, struct pt_regs *regs)
 
        skb_put (skb, urb->actual_length);
        entry->state = rx_done;
-       entry->urb = 0;
+       entry->urb = NULL;
 
        switch (urb_status) {
            // success
@@ -2424,7 +3068,8 @@ static void rx_complete (struct urb *urb, struct pt_regs *regs)
                        entry->state = rx_cleanup;
                        dev->stats.rx_errors++;
                        dev->stats.rx_length_errors++;
-                       devdbg (dev, "rx length %d", skb->len);
+                       if (netif_msg_rx_err (dev))
+                               devdbg (dev, "rx length %d", skb->len);
                }
                break;
 
@@ -2440,9 +3085,8 @@ static void rx_complete (struct urb *urb, struct pt_regs *regs)
            // software-driven interface shutdown
            case -ECONNRESET:           // async unlink
            case -ESHUTDOWN:            // hardware gone
-#ifdef VERBOSE
-               devdbg (dev, "rx shutdown, code %d", urb_status);
-#endif
+               if (netif_msg_ifdown (dev))
+                       devdbg (dev, "rx shutdown, code %d", urb_status);
                goto block;
 
            // we get controller i/o faults during khubd disconnect() delays.
@@ -2454,12 +3098,13 @@ static void rx_complete (struct urb *urb, struct pt_regs *regs)
                dev->stats.rx_errors++;
                if (!timer_pending (&dev->delay)) {
                        mod_timer (&dev->delay, jiffies + THROTTLE_JIFFIES);
-                       devdbg (dev, "rx throttle %d", urb_status);
+                       if (netif_msg_link (dev))
+                               devdbg (dev, "rx throttle %d", urb_status);
                }
 block:
                entry->state = rx_cleanup;
                entry->urb = urb;
-               urb = 0;
+               urb = NULL;
                break;
 
            // data overrun ... flush fifo?
@@ -2470,7 +3115,8 @@ block:
            default:
                entry->state = rx_cleanup;
                dev->stats.rx_errors++;
-               devdbg (dev, "rx status %d", urb_status);
+               if (netif_msg_rx_err (dev))
+                       devdbg (dev, "rx status %d", urb_status);
                break;
        }
 
@@ -2484,9 +3130,43 @@ block:
                }
                usb_free_urb (urb);
        }
-#ifdef VERBOSE
-       devdbg (dev, "no read resubmitted");
-#endif /* VERBOSE */
+       if (netif_msg_rx_err (dev))
+               devdbg (dev, "no read resubmitted");
+}
+
+static void intr_complete (struct urb *urb, struct pt_regs *regs)
+{
+       struct usbnet   *dev = urb->context;
+       int             status = urb->status;
+
+       switch (status) {
+           /* success */
+           case 0:
+               dev->driver_info->status(dev, urb);
+               break;
+
+           /* software-driven interface shutdown */
+           case -ENOENT:               // urb killed
+           case -ESHUTDOWN:            // hardware gone
+               if (netif_msg_ifdown (dev))
+                       devdbg (dev, "intr shutdown, code %d", status);
+               return;
+
+           /* NOTE:  not throttling like RX/TX, since this endpoint
+            * already polls infrequently
+            */
+           default:
+               devdbg (dev, "intr status %d", status);
+               break;
+       }
+
+       if (!netif_running (dev->net))
+               return;
+
+       memset(urb->transfer_buffer, 0, urb->transfer_buffer_length);
+       status = usb_submit_urb (urb, GFP_ATOMIC);
+       if (status != 0 && netif_msg_timer (dev))
+               deverr(dev, "intr resubmit --> %d", status);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -2528,14 +3208,14 @@ static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
 
 static int usbnet_stop (struct net_device *net)
 {
-       struct usbnet           *dev = (struct usbnet *) net->priv;
+       struct usbnet           *dev = netdev_priv(net);
        int                     temp;
        DECLARE_WAIT_QUEUE_HEAD (unlink_wakeup); 
        DECLARE_WAITQUEUE (wait, current);
 
        netif_stop_queue (net);
 
-       if (dev->msg_level >= 2)
+       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
@@ -2550,13 +3230,15 @@ static int usbnet_stop (struct net_device *net)
        while (skb_queue_len (&dev->rxq)
                        && skb_queue_len (&dev->txq)
                        && skb_queue_len (&dev->done)) {
-               set_current_state (TASK_UNINTERRUPTIBLE);
-               schedule_timeout (UNLINK_TIMEOUT_JIFFIES);
-               devdbg (dev, "waited for %d urb completions", temp);
+               msleep(UNLINK_TIMEOUT_MS);
+               if (netif_msg_ifdown (dev))
+                       devdbg (dev, "waited for %d urb completions", temp);
        }
-       dev->wait = 0;
+       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.
@@ -2576,27 +3258,40 @@ static int usbnet_stop (struct net_device *net)
 
 static int usbnet_open (struct net_device *net)
 {
-       struct usbnet           *dev = (struct usbnet *) net->priv;
+       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) {
-               devinfo (dev, "open reset fail (%d) usbnet usb-%s-%s, %s",
-                       retval,
-                       dev->udev->bus->bus_name, dev->udev->devpath,
+               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) {
-               devdbg (dev, "can't open; %d", retval);
+               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 (dev->msg_level >= 2) {
+       if (netif_msg_ifup (dev)) {
                char    *framing;
 
                if (dev->driver_info->flags & FLAG_FRAMING_NC)
@@ -2607,6 +3302,8 @@ static int usbnet_open (struct net_device *net)
                        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";
 
@@ -2626,7 +3323,7 @@ done:
 
 static void usbnet_get_drvinfo (struct net_device *net, struct ethtool_drvinfo *info)
 {
-       struct usbnet *dev = net->priv;
+       struct usbnet *dev = netdev_priv(net);
 
        strncpy (info->driver, driver_name, sizeof info->driver);
        strncpy (info->version, DRIVER_VERSION, sizeof info->version);
@@ -2637,7 +3334,7 @@ static void usbnet_get_drvinfo (struct net_device *net, struct ethtool_drvinfo *
 
 static u32 usbnet_get_link (struct net_device *net)
 {
-       struct usbnet *dev = net->priv;
+       struct usbnet *dev = netdev_priv(net);
 
        /* If a check_connect is defined, return it's results */
        if (dev->driver_info->check_connect)
@@ -2649,28 +3346,26 @@ static u32 usbnet_get_link (struct net_device *net)
 
 static u32 usbnet_get_msglevel (struct net_device *net)
 {
-       struct usbnet *dev = net->priv;
+       struct usbnet *dev = netdev_priv(net);
 
-       return dev->msg_level;
+       return dev->msg_enable;
 }
 
 static void usbnet_set_msglevel (struct net_device *net, u32 level)
 {
-       struct usbnet *dev = net->priv;
+       struct usbnet *dev = netdev_priv(net);
 
-       dev->msg_level = level;
+       dev->msg_enable = level;
 }
 
 static int usbnet_ioctl (struct net_device *net, struct ifreq *rq, int cmd)
 {
 #ifdef NEED_MII
        {
-       struct usbnet *dev = (struct usbnet *)net->priv;
+       struct usbnet *dev = netdev_priv(net);
 
        if (dev->mii.mdio_read != NULL && dev->mii.mdio_write != NULL)
-               return generic_mii_ioctl(&dev->mii,
-                               (struct mii_ioctl_data *) &rq->ifr_data,
-                               cmd, NULL);
+               return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
        }
 #endif
        return -EOPNOTSUPP;
@@ -2693,10 +3388,11 @@ kevent (void *data)
        if (test_bit (EVENT_TX_HALT, &dev->flags)) {
                unlink_urbs (dev, &dev->txq);
                status = usb_clear_halt (dev->udev, dev->out);
-               if (status < 0)
-                       deverr (dev, "can't clear tx halt, status %d",
-                               status);
-               else {
+               if (status < 0 && status != -EPIPE) {
+                       if (netif_msg_tx_err (dev))
+                               deverr (dev, "can't clear tx halt, status %d",
+                                       status);
+               } else {
                        clear_bit (EVENT_TX_HALT, &dev->flags);
                        netif_wake_queue (dev->net);
                }
@@ -2704,10 +3400,11 @@ kevent (void *data)
        if (test_bit (EVENT_RX_HALT, &dev->flags)) {
                unlink_urbs (dev, &dev->rxq);
                status = usb_clear_halt (dev->udev, dev->in);
-               if (status < 0)
-                       deverr (dev, "can't clear rx halt, status %d",
-                               status);
-               else {
+               if (status < 0 && status != -EPIPE) {
+                       if (netif_msg_rx_err (dev))
+                               deverr (dev, "can't clear rx halt, status %d",
+                                       status);
+               } else {
                        clear_bit (EVENT_RX_HALT, &dev->flags);
                        tasklet_schedule (&dev->bh);
                }
@@ -2715,19 +3412,32 @@ kevent (void *data)
 
        /* tasklet could resubmit itself forever if memory is tight */
        if (test_bit (EVENT_RX_MEMORY, &dev->flags)) {
-               struct urb      *urb = 0;
+               struct urb      *urb = NULL;
 
                if (netif_running (dev->net))
                        urb = usb_alloc_urb (0, GFP_KERNEL);
                else
                        clear_bit (EVENT_RX_MEMORY, &dev->flags);
-               if (urb != 0) {
+               if (urb != NULL) {
                        clear_bit (EVENT_RX_MEMORY, &dev->flags);
                        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);
@@ -2752,6 +3462,11 @@ static void tx_complete (struct urb *urb, struct pt_regs *regs)
                        defer_kevent (dev, EVENT_TX_HALT);
                        break;
 
+               /* software-driven interface shutdown */
+               case -ECONNRESET:               // async unlink
+               case -ESHUTDOWN:                // hardware gone
+                       break;
+
                // like rx, tx gets controller i/o faults during khubd delays
                // and so it uses the same throttling mechanism.
                case -EPROTO:           // ehci
@@ -2760,17 +3475,20 @@ static void tx_complete (struct urb *urb, struct pt_regs *regs)
                        if (!timer_pending (&dev->delay)) {
                                mod_timer (&dev->delay,
                                        jiffies + THROTTLE_JIFFIES);
-                               devdbg (dev, "tx throttle %d", urb->status);
+                               if (netif_msg_link (dev))
+                                       devdbg (dev, "tx throttle %d",
+                                                       urb->status);
                        }
                        netif_stop_queue (dev->net);
                        break;
                default:
-                       devdbg (dev, "tx err %d", entry->urb->status);
+                       if (netif_msg_tx_err (dev))
+                               devdbg (dev, "tx err %d", entry->urb->status);
                        break;
                }
        }
 
-       urb->dev = 0;
+       urb->dev = NULL;
        entry->state = tx_done;
        defer_bh (dev, skb);
 }
@@ -2779,7 +3497,7 @@ static void tx_complete (struct urb *urb, struct pt_regs *regs)
 
 static void usbnet_tx_timeout (struct net_device *net)
 {
-       struct usbnet           *dev = (struct usbnet *) net->priv;
+       struct usbnet           *dev = netdev_priv(net);
 
        unlink_urbs (dev, &dev->txq);
        tasklet_schedule (&dev->bh);
@@ -2791,16 +3509,16 @@ static void usbnet_tx_timeout (struct net_device *net)
 
 static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net)
 {
-       struct usbnet           *dev = (struct usbnet *) net->priv;
+       struct usbnet           *dev = netdev_priv(net);
        int                     length;
        int                     retval = NET_XMIT_SUCCESS;
-       struct urb              *urb = 0;
+       struct urb              *urb = NULL;
        struct skb_data         *entry;
        struct driver_info      *info = dev->driver_info;
        unsigned long           flags;
 #ifdef CONFIG_USB_NET1080
-       struct nc_header        *header = 0;
-       struct nc_trailer       *trailer = 0;
+       struct nc_header        *header = NULL;
+       struct nc_trailer       *trailer = NULL;
 #endif /* CONFIG_USB_NET1080 */
 
        // some devices want funky USB-level framing, for
@@ -2808,14 +3526,16 @@ static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net)
        if (info->tx_fixup) {
                skb = info->tx_fixup (dev, skb, GFP_ATOMIC);
                if (!skb) {
-                       devdbg (dev, "can't tx_fixup 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))) {
-               devdbg (dev, "no urb");
+               if (netif_msg_tx_err (dev))
+                       devdbg (dev, "no urb");
                goto drop;
        }
 
@@ -2873,7 +3593,8 @@ static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net)
                defer_kevent (dev, EVENT_TX_HALT);
                break;
        default:
-               devdbg (dev, "tx: submit urb err %d", retval);
+               if (netif_msg_tx_err (dev))
+                       devdbg (dev, "tx: submit urb err %d", retval);
                break;
        case 0:
                net->trans_start = jiffies;
@@ -2884,18 +3605,17 @@ static int usbnet_start_xmit (struct sk_buff *skb, struct net_device *net)
        spin_unlock_irqrestore (&dev->txq.lock, flags);
 
        if (retval) {
-               devdbg (dev, "drop, code %d", retval);
+               if (netif_msg_tx_err (dev))
+                       devdbg (dev, "drop, code %d", retval);
 drop:
                retval = NET_XMIT_SUCCESS;
                dev->stats.tx_dropped++;
                if (skb)
                        dev_kfree_skb_any (skb);
                usb_free_urb (urb);
-#ifdef VERBOSE
-       } else {
+       } else if (netif_msg_tx_queued (dev)) {
                devdbg (dev, "> tx, len %d, type 0x%x",
                        length, skb->protocol);
-#endif
        }
        return retval;
 }
@@ -2948,10 +3668,11 @@ static void usbnet_bh (unsigned long param)
 
                        // don't refill the queue all at once
                        for (i = 0; i < 10 && dev->rxq.qlen < qlen; i++) {
-                               if ((urb = usb_alloc_urb (0, GFP_ATOMIC)) != 0)
+                               urb = usb_alloc_urb (0, GFP_ATOMIC);
+                               if (urb != NULL)
                                        rx_submit (dev, urb, GFP_ATOMIC);
                        }
-                       if (temp != dev->rxq.qlen)
+                       if (temp != dev->rxq.qlen && netif_msg_link (dev))
                                devdbg (dev, "rxqlen %d --> %d",
                                                temp, dev->rxq.qlen);
                        if (dev->rxq.qlen < qlen)
@@ -2976,6 +3697,7 @@ static void usbnet_disconnect (struct usb_interface *intf)
 {
        struct usbnet           *dev;
        struct usb_device       *xdev;
+       struct net_device       *net;
 
        dev = usb_get_intfdata(intf);
        usb_set_intfdata(intf, NULL);
@@ -2984,11 +3706,13 @@ static void usbnet_disconnect (struct usb_interface *intf)
 
        xdev = interface_to_usbdev (intf);
 
-       devinfo (dev, "unregister usbnet usb-%s-%s, %s",
-               xdev->bus->bus_name, xdev->devpath,
-               dev->driver_info->description);
+       if (netif_msg_probe (dev))
+               devinfo (dev, "unregister usbnet usb-%s-%s, %s",
+                       xdev->bus->bus_name, xdev->devpath,
+                       dev->driver_info->description);
        
-       unregister_netdev (dev->net);
+       net = dev->net;
+       unregister_netdev (net);
 
        /* we don't hold rtnl here ... */
        flush_scheduled_work ();
@@ -2996,8 +3720,7 @@ static void usbnet_disconnect (struct usb_interface *intf)
        if (dev->driver_info->unbind)
                dev->driver_info->unbind (dev, intf);
 
-       free_netdev(dev->net);
-       kfree (dev);
+       free_netdev(net);
        usb_put_dev (xdev);
 }
 
@@ -3031,15 +3754,17 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
        status = -ENOMEM;
 
        // set up our own records
-       if (!(dev = kmalloc (sizeof *dev, GFP_KERNEL))) {
+       net = alloc_etherdev(sizeof(*dev));
+       if (!net) {
                dbg ("can't kmalloc dev");
                goto out;
        }
-       memset (dev, 0, sizeof *dev);
 
+       dev = netdev_priv(net);
        dev->udev = xdev;
        dev->driver_info = info;
-       dev->msg_level = msg_level;
+       dev->msg_enable = netif_msg_init (msg_level, NETIF_MSG_DRV
+                               | NETIF_MSG_PROBE | NETIF_MSG_LINK);
        skb_queue_head_init (&dev->rxq);
        skb_queue_head_init (&dev->txq);
        skb_queue_head_init (&dev->done);
@@ -3050,21 +3775,15 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
        dev->delay.data = (unsigned long) dev;
        init_timer (&dev->delay);
 
-       // set up network interface records
-       net = alloc_etherdev(0);
-       if (!net)
-               goto out1;
-
        SET_MODULE_OWNER (net);
        dev->net = net;
-       net->priv = dev;
        strcpy (net->name, "usb%d");
        memcpy (net->dev_addr, node_id, sizeof node_id);
 
 #if 0
 // dma_supported() is deeply broken on almost all architectures
        // possible with some EHCI controllers
-       if (dma_supported (&udev->dev, 0xffffffffffffffffULL))
+       if (dma_supported (&udev->dev, DMA_64BIT_MASK))
                net->features |= NETIF_F_HIGHDMA;
 #endif
 
@@ -3101,38 +3820,79 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
                        status = 0;
 
        }
+
+       if (status == 0 && dev->status)
+               status = init_status (dev, udev);
        if (status < 0)
                goto out1;
 
        dev->maxpacket = usb_maxpacket (dev->udev, dev->out, 1);
        
-       SET_NETDEV_DEV(dev->net, &udev->dev);
-       status = register_netdev (dev->net);
+       SET_NETDEV_DEV(net, &udev->dev);
+       status = register_netdev (net);
        if (status)
                goto out3;
-       devinfo (dev, "register usbnet at usb-%s-%s, %s",
-               xdev->bus->bus_name, xdev->devpath,
-               dev->driver_info->description);
+       if (netif_msg_probe (dev))
+               devinfo (dev, "register usbnet at usb-%s-%s, %s, "
+                               "%02x:%02x:%02x:%02x:%02x:%02x",
+                       xdev->bus->bus_name, xdev->devpath,
+                       dev->driver_info->description,
+                       net->dev_addr [0], net->dev_addr [1],
+                       net->dev_addr [2], net->dev_addr [3],
+                       net->dev_addr [4], net->dev_addr [5]);
 
        // ok, it's ready to go.
        usb_set_intfdata (udev, dev);
 
        // start as if the link is up
-       netif_device_attach (dev->net);
+       netif_device_attach (net);
 
        return 0;
 
 out3:
        if (info->unbind)
                info->unbind (dev, udev);
-       free_netdev(net);
 out1:
-       kfree(dev);
+       free_netdev(net);
 out:
        usb_put_dev(xdev);
        return status;
 }
 
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_PM
+
+static int usbnet_suspend (struct usb_interface *intf, pm_message_t message)
+{
+       struct usbnet           *dev = usb_get_intfdata(intf);
+       
+       /* 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);
+       intf->dev.power.power_state = PMSG_SUSPEND;
+       return 0;
+}
+
+static int usbnet_resume (struct usb_interface *intf)
+{
+       struct usbnet           *dev = usb_get_intfdata(intf);
+
+       intf->dev.power.power_state = PMSG_ON;
+       netif_device_attach (dev->net);
+       tasklet_schedule (&dev->bh);
+       return 0;
+}
+
+#else  /* !CONFIG_PM */
+
+#define        usbnet_suspend  NULL
+#define        usbnet_resume   NULL
+
+#endif /* CONFIG_PM */
 
 /*-------------------------------------------------------------------------*/
 
@@ -3206,6 +3966,30 @@ static const struct usb_device_id        products [] = {
        // ATEN UC210T
        USB_DEVICE (0x0557, 0x2009),
        .driver_info =  (unsigned long) &ax8817x_info,
+}, {
+       // Buffalo LUA-U2-KTX
+       USB_DEVICE (0x0411, 0x003d),
+       .driver_info =  (unsigned long) &ax8817x_info,
+}, {
+       // Sitecom LN-029 "USB 2.0 10/100 Ethernet adapter"
+       USB_DEVICE (0x6189, 0x182d),
+       .driver_info =  (unsigned long) &ax8817x_info,
+}, {
+       // corega FEther USB2-TX
+       USB_DEVICE (0x07aa, 0x0017),
+       .driver_info =  (unsigned long) &ax8817x_info,
+}, {
+       // Surecom EP-1427X-2
+       USB_DEVICE (0x1189, 0x0893),
+       .driver_info = (unsigned long) &ax8817x_info,
+}, {
+       // goodway corp usb gwusb2e
+       USB_DEVICE (0x1631, 0x6200),
+       .driver_info = (unsigned long) &ax8817x_info,
+}, {
+       // ASIX AX88772 10/100
+        USB_DEVICE (0x0b95, 0x7720),
+        .driver_info = (unsigned long) &ax88772_info,
 },
 #endif
 
@@ -3246,6 +4030,13 @@ static const struct usb_device_id        products [] = {
 },
 #endif
 
+#ifdef CONFIG_USB_KC2190
+{
+       USB_DEVICE (0x050f, 0x0190),    // KC-190
+       .driver_info =  (unsigned long) &kc2190_info,
+},
+#endif
+
 #ifdef CONFIG_USB_RNDIS
 {
        /* RNDIS is MSFT's un-official variant of CDC ACM */
@@ -3262,11 +4053,18 @@ static const struct usb_device_id       products [] = {
  *
  * PXA25x or PXA210 ...  these use a "usb-eth" driver much like
  * the sa1100 one, but hardware uses different endpoint numbers.
+ *
+ * Or the Linux "Ethernet" gadget on hardware that can't talk
+ * CDC Ethernet (e.g., no altsettings), in either of two modes:
+ *  - acting just like the old "usb-eth" firmware, though
+ *    the implementation is different 
+ *  - supporting RNDIS as the first/default configuration for
+ *    MS-Windows interop; Linux needs to use the other config
  */
 {
        // 1183 = 0x049F, both used as hex values?
        // Compaq "Itsy" vendor/product id
-       USB_DEVICE (0x049F, 0x505A),
+       USB_DEVICE (0x049F, 0x505A),    // usb-eth, or compatible
        .driver_info =  (unsigned long) &linuxdev_info,
 }, {
        USB_DEVICE (0x0E7E, 0x1001),    // G.Mate "Yopy"
@@ -3274,101 +4072,110 @@ static const struct usb_device_id     products [] = {
 }, {
        USB_DEVICE (0x8086, 0x07d3),    // "blob" bootloader
        .driver_info =  (unsigned long) &blob_info,
+}, {
+       // Linux Ethernet/RNDIS gadget on pxa210/25x/26x
+       // e.g. Gumstix, current OpenZaurus, ...
+       USB_DEVICE_VER (0x0525, 0xa4a2, 0x0203, 0x0203),
+       .driver_info =  (unsigned long) &linuxdev_info,
 }, 
 #endif
 
-#ifdef CONFIG_USB_ZAURUS
+#if    defined(CONFIG_USB_ZAURUS) || defined(CONFIG_USB_CDCETHER)
 /*
  * SA-1100 based Sharp Zaurus ("collie"), or compatible.
  * Same idea as above, but different framing.
  *
  * PXA-2xx based models are also lying-about-cdc.
+ * Some models don't even tell the same lies ...
+ *
+ * NOTE:  OpenZaurus versions with 2.6 kernels won't use these entries,
+ * unlike the older ones with 2.4 "embedix" kernels.
+ *
+ * NOTE:  These entries do double-duty, serving as blacklist entries
+ * whenever Zaurus support isn't enabled, but CDC Ethernet is.
  */
+#define        ZAURUS_MASTER_INTERFACE \
+       .bInterfaceClass        = USB_CLASS_COMM, \
+       .bInterfaceSubClass     = USB_CDC_SUBCLASS_ETHERNET, \
+       .bInterfaceProtocol     = USB_CDC_PROTO_NONE
 {
        .match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO
                          | USB_DEVICE_ID_MATCH_DEVICE, 
        .idVendor               = 0x04DD,
        .idProduct              = 0x8004,
-       /* match the master interface */
-       .bInterfaceClass        = USB_CLASS_COMM,
-       .bInterfaceSubClass     = 6 /* Ethernet model */,
-       .bInterfaceProtocol     = 0,
-       .driver_info =  (unsigned long) &zaurus_sl5x00_info,
+       ZAURUS_MASTER_INTERFACE,
+       .driver_info = ZAURUS_STRONGARM_INFO,
 }, {
        .match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO
                          | USB_DEVICE_ID_MATCH_DEVICE, 
        .idVendor               = 0x04DD,
        .idProduct              = 0x8005,       /* A-300 */
-       .bInterfaceClass        = 0x02,
-       .bInterfaceSubClass     = 0x0a,
-       .bInterfaceProtocol     = 0x00,
-       .driver_info =  (unsigned long) &zaurus_pxa_info,
+       ZAURUS_MASTER_INTERFACE,
+       .driver_info = ZAURUS_PXA_INFO,
 }, {
        .match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO
                          | USB_DEVICE_ID_MATCH_DEVICE, 
        .idVendor               = 0x04DD,
        .idProduct              = 0x8006,       /* B-500/SL-5600 */
-       .bInterfaceClass        = 0x02,
-       .bInterfaceSubClass     = 0x0a,
-       .bInterfaceProtocol     = 0x00,
-       .driver_info =  (unsigned long) &zaurus_pxa_info,
+       ZAURUS_MASTER_INTERFACE,
+       .driver_info = ZAURUS_PXA_INFO,
 }, {
        .match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO
                  | USB_DEVICE_ID_MATCH_DEVICE,
        .idVendor               = 0x04DD,
        .idProduct              = 0x8007,       /* C-700 */
-       .bInterfaceClass    = 0x02,
-       .bInterfaceSubClass = 0x0a,
-       .bInterfaceProtocol = 0x00,
-       .driver_info =  (unsigned long) &zaurus_pxa_info,
+       ZAURUS_MASTER_INTERFACE,
+       .driver_info = ZAURUS_PXA_INFO,
 }, {
        .match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO
                 | USB_DEVICE_ID_MATCH_DEVICE,
        .idVendor               = 0x04DD,
        .idProduct              = 0x9031,       /* C-750 C-760 */
-       .bInterfaceClass        = 0x02,
-       .bInterfaceSubClass     = 0x0a,
-       .bInterfaceProtocol     = 0x00,
-       .driver_info =  (unsigned long) &zaurus_pxa_info,
+       ZAURUS_MASTER_INTERFACE,
+       .driver_info = ZAURUS_PXA_INFO,
 }, {
        .match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO
                 | USB_DEVICE_ID_MATCH_DEVICE,
        .idVendor               = 0x04DD,
        .idProduct              = 0x9032,       /* SL-6000 */
-       .bInterfaceClass        = 0x02,
-       .bInterfaceSubClass     = 0x0a,
-       .bInterfaceProtocol     = 0x00,
-       .driver_info =  (unsigned long) &zaurus_pxa_info,
+       ZAURUS_MASTER_INTERFACE,
+       .driver_info = ZAURUS_PXA_INFO,
 }, {
        .match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO
                 | USB_DEVICE_ID_MATCH_DEVICE,
        .idVendor               = 0x04DD,
+       /* reported with some C860 units */
        .idProduct              = 0x9050,       /* C-860 */
-       .bInterfaceClass        = 0x02,
-       .bInterfaceSubClass     = 0x0a,
-       .bInterfaceProtocol     = 0x00,
-       .driver_info =  (unsigned long) &zaurus_pxa_info,
+       ZAURUS_MASTER_INTERFACE,
+       .driver_info = ZAURUS_PXA_INFO,
 },
-#endif
 
-#ifdef CONFIG_USB_CDCETHER
+#ifdef CONFIG_USB_ZAURUS
+       /* At least some (reports vary) PXA units have very different lies
+        * about their standards support:  they claim to be cell phones with
+        * direct access to their radios.  (They don't conform to CDC MDLM.)
+        */
+{
+       USB_INTERFACE_INFO (USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM,
+                       USB_CDC_PROTO_NONE),
+       .driver_info = (unsigned long) &bogus_mdlm_info,
+},
+#endif
 
-#ifndef        CONFIG_USB_ZAURUS
-       /* if we couldn't whitelist Zaurus, we must blacklist it */
+/* Olympus has some models with a Zaurus-compatible option.
+ * R-1000 uses a FreeScale i.MXL cpu (ARMv4T)
+ */
 {
-       .match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO
-                         | USB_DEVICE_ID_MATCH_DEVICE, 
-       .idVendor               = 0x04DD,
-       .idProduct              = 0x8004,
-       /* match the master interface */
-       .bInterfaceClass        = USB_CLASS_COMM,
-       .bInterfaceSubClass     = 6 /* Ethernet model */,
-       .bInterfaceProtocol     = 0,
-       .driver_info            = 0, /* BLACKLIST */
+       .match_flags    =   USB_DEVICE_ID_MATCH_INT_INFO
+                | USB_DEVICE_ID_MATCH_DEVICE,
+       .idVendor               = 0x07B4,
+       .idProduct              = 0x0F02,       /* R-1000 */
+       ZAURUS_MASTER_INTERFACE,
+       .driver_info = OLYMPUS_MXL_INFO,
 },
-       // FIXME blacklist the other Zaurus models too, sigh
 #endif
 
+#ifdef CONFIG_USB_CDCETHER
 {
        /* CDC Ether uses two interfaces, not necessarily consecutive.
         * We match the main interface, ignoring the optional device
@@ -3378,7 +4185,8 @@ static const struct usb_device_id products [] = {
         * NOTE:  this match must come AFTER entries working around
         * bugs/quirks in a given product (like Zaurus, above).
         */
-       USB_INTERFACE_INFO (USB_CLASS_COMM, 6 /* Ethernet model */, 0),
+       USB_INTERFACE_INFO (USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET,
+                       USB_CDC_PROTO_NONE),
        .driver_info = (unsigned long) &cdc_info,
 },
 #endif
@@ -3393,6 +4201,8 @@ static struct usb_driver usbnet_driver = {
        .id_table =     products,
        .probe =        usbnet_probe,
        .disconnect =   usbnet_disconnect,
+       .suspend =      usbnet_suspend,
+       .resume =       usbnet_resume,
 };
 
 /* Default ethtool_ops assigned.  Devices can override in their bind() routine */