This patch adds support for the ASIX AX8817x based ethernet devices to the usbnet driver (deprecating the current ax8817x driver). At this point, I consider it highly experimental, don't deploy in production, etc etc etc. It is in the "works for me" category and I seem to get pretty decent transfer speeds out of it (on some occassions, even better than the existing ax8817x driver). I see this driver being the long-term for ax8817x support in that it makes use of all of the plumbing already provided by usbnet and gives us a net loss of about 1000 lines of code.

If current users of the ax8817x driver could apply this and offer any feedback, it would be greatly appreciated. Also, if major "what the heck are you doing there???" kinds of things can be brought up, it would also be appreciated.


--- linux-2.6.0-test3.orig/drivers/usb/net/usbnet.c     2003-08-09 00:37:18.000000000 
-0400
+++ linux-2.6.0-test3/drivers/usb/net/usbnet.c  2003-08-09 15:03:34.493451128 -0400
@@ -2,6 +2,8 @@
  * USB Host-to-Host Links
  * Copyright (C) 2000-2002 by David Brownell <[EMAIL PROTECTED]>
  * Copyright (C) 2002 Pavel Machek <[EMAIL PROTECTED]>
+ * Copyright (C) 2003 David Hollis <[EMAIL PROTECTED]>
+ * Copyright (c) 2002-2003 TiVo Inc.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -138,6 +140,7 @@
 #include <linux/etherdevice.h>
 #include <linux/random.h>
 #include <linux/ethtool.h>
+#include <linux/mii.h>
 #include <linux/workqueue.h>
 #include <asm/uaccess.h>
 #include <asm/unaligned.h>
@@ -217,6 +220,7 @@
        struct net_device_stats stats;
        int                     msg_level;
        unsigned long           data [5];
+       struct mii_if_info      mii;
 
        // various kinds of pending driver work
        struct sk_buff_head     rxq;
@@ -240,6 +244,7 @@
 #define FLAG_FRAMING_NC        0x0001          /* guard against device dropouts */ 
 #define FLAG_FRAMING_GL        0x0002          /* genelink batches packets */
 #define FLAG_FRAMING_Z 0x0004          /* zaurus adds a trailer */
+#define FLAG_FRAMING_ASIX 0x0008       /* AX8817x URB sizing */
 
 #define FLAG_NO_SETINT 0x0010          /* device can't set_interface() */
 #define FLAG_ETHER     0x0020          /* maybe use "eth%d" names */
@@ -1708,6 +1713,251 @@
 
 #endif
 
+#ifdef CONFIG_USB_AX8817X
+/* ASIX AX8817X based USB 2.0 Ethernet Devices */
+
+#define AX_REQ_READ                    ( USB_DIR_IN | USB_TYPE_VENDOR | 
USB_RECIP_DEVICE )
+#define AX_REQ_WRITE                   ( USB_DIR_OUT | USB_TYPE_VENDOR | 
USB_RECIP_DEVICE )
+
+#define AX_CMD_SET_SW_MII              0x06
+#define AX_CMD_READ_MII_REG            0x07
+#define AX_CMD_WRITE_MII_REG           0x08
+#define AX_CMD_SET_HW_MII              0x0a
+#define AX_CMD_WRITE_RX_CTL            0x10
+#define AX_CMD_READ_IPG012             0x11
+#define AX_CMD_WRITE_IPG0              0x12
+#define AX_CMD_WRITE_IPG1              0x13
+#define AX_CMD_WRITE_IPG2              0x14
+#define AX_CMD_WRITE_MULTI_FILTER      0x16
+#define AX_CMD_READ_NODE_ID            0x17
+#define AX_CMD_READ_PHY_ID             0x19
+#define AX_CMD_WRITE_MEDIUM_MODE       0x1b
+#define AX_CMD_WRITE_GPIOS             0x1f
+
+#define AX_MCAST_FILTER_SIZE           8
+#define AX_MAX_MCAST                   64
+#define AX_TIMEOUT_CMD                 ( HZ / 10 )
+
+static int ax8817x_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, u16 
size, void *data)
+{
+       return usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
+                              cmd, AX_REQ_READ, value, index, data, size, 
AX_TIMEOUT_CMD);
+}
+
+static int ax8817x_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, u16 
size, void *data)
+{
+       return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
+                              cmd, AX_REQ_WRITE, value, index, data, size, 
AX_TIMEOUT_CMD);
+}
+
+static void ax8817x_async_cmd_callback(struct urb *urb, struct pt_regs *regs)
+{
+       struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context;
+       
+       if (urb->status < 0)
+               printk(KERN_DEBUG "ax8817x_async_cmd_callback() failed with %d", 
urb->status);
+       
+       kfree(req);
+       usb_free_urb(urb);
+}
+
+static void ax8817x_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index, 
u16 size, void *data)
+{
+       struct usb_ctrlrequest *req;
+       int status;
+       struct urb *urb;
+       
+       if ((urb = usb_alloc_urb(0, GFP_ATOMIC)) == NULL) {
+               devdbg(dev, "Error allocating URB in write_cmd_async!");
+               return;
+       }
+       
+       if ((req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC)) == NULL) {
+               deverr(dev, "Failed to allocate memory for control request");
+               usb_free_urb(urb);
+               return;
+       }
+
+       req->bRequestType = AX_REQ_WRITE;
+       req->bRequest = cmd;
+       req->wValue = cpu_to_le16(value);
+       req->wIndex = cpu_to_le16(index); 
+       req->wLength = cpu_to_le16(size);
+
+       usb_fill_control_urb(urb, dev->udev,
+                            usb_sndctrlpipe(dev->udev, 0),
+                            (void *)req, data, size,
+                            ax8817x_async_cmd_callback, req);
+
+       if((status = usb_submit_urb(urb, GFP_NOIO)) < 0)
+               deverr(dev, "Error submitting the control message: status=%d", status);
+}
+
+static void ax8817x_set_multicast(struct net_device *net)
+{
+       struct usbnet *dev = (struct usbnet *) net->priv;
+       u8 rx_ctl = 0x8c;
+
+       if (net->flags & IFF_PROMISC) {
+               rx_ctl |= 0x01;
+       } else if (net->flags & IFF_ALLMULTI
+                  || net->mc_count > AX_MAX_MCAST) {
+               rx_ctl |= 0x02;
+       } else if (net->mc_count == 0) {
+               /* just broadcast and directed */
+       } else {
+               struct dev_mc_list *mc_list = net->mc_list;
+               u8 *multi_filter;
+               u32 crc_bits;
+               int i;
+
+               multi_filter = kmalloc(AX_MCAST_FILTER_SIZE, GFP_ATOMIC);
+               if (multi_filter == NULL) {
+                       /* Oops, couldn't allocate a DMA buffer for setting the 
multicast
+                          filter. Try all multi mode, although the ax_write_cmd_async
+                          will almost certainly fail, too... (but it will printk). */
+                       rx_ctl |= 0x02;
+               } else {
+                       memset(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] |=
+                                   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);
+
+                       rx_ctl |= 0x10;
+               }
+       }
+
+       ax8817x_write_cmd_async(dev, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL);
+}
+
+static int ax8817x_mdio_read(struct net_device *netdev, int phy_id, int loc)
+{
+       struct usbnet *dev = netdev->priv;
+       u16 res;
+       u8 buf[4];
+
+       ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, &buf);
+       ax8817x_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id, (__u16)loc, 2, (u16 *)&res);
+       ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, &buf);
+
+       return res & 0xffff;
+}
+
+static void ax8817x_mdio_write(struct net_device *netdev, int phy_id, int loc, int 
val)
+{
+       struct usbnet *dev = netdev->priv;
+       u16 res = val;
+       u8 buf[4];
+
+       ax8817x_write_cmd(dev, AX_CMD_SET_SW_MII, 0, 0, 0, &buf);
+       ax8817x_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, (__u16)loc, 2, (u16 
*)&res);
+       ax8817x_write_cmd(dev, AX_CMD_SET_HW_MII, 0, 0, 0, &buf);
+}
+
+static int ax8817x_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+       int ret;
+       u8 buf[6];
+       u16 *buf16 = (u16 *) buf;
+       int i;
+
+       dev->in = usb_rcvbulkpipe(dev->udev, 3);
+       dev->out = usb_sndbulkpipe(dev->udev, 2);
+
+       if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_RX_CTL, 0x80, 0, 0, buf)) < 0) {
+               dbg("send AX_CMD_WRITE_RX_CTL failed: %02x", ret);
+               return ret;
+       }
+
+       /* 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: %02x", ret);
+               return ret;
+       }
+       memcpy(dev->net->dev_addr, buf, ETH_ALEN);
+
+       /* Get IPG values */
+       if ((ret = ax8817x_read_cmd(dev, AX_CMD_READ_IPG012, 0, 0, 3, buf)) < 0) {
+               dbg("Error reading IPG values: %02x", ret);
+               return ret;
+       }
+
+       for(i = 0;i < 3;i++) {
+               ax8817x_write_cmd(dev, AX_CMD_WRITE_IPG0 + i, 0, 0, 1, &buf[i]);
+       }
+
+       /* 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;
+       } 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;
+       }
+
+       /* Initialize MII structure */
+       dev->mii.dev = dev->net;
+       dev->mii.mdio_read = ax8817x_mdio_read;
+       dev->mii.mdio_write = ax8817x_mdio_write;
+       dev->mii.phy_id_mask = 0x3f;
+       dev->mii.reg_num_mask = 0x1f;
+       dev->mii.phy_id = buf[1];
+
+       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;
+       }
+
+       *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;
+       }
+
+       /* 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;
+       }
+
+       *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;
+       }
+
+       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;
+       }
+
+       dev->net->set_multicast_list = ax8817x_set_multicast;
+
+       return 0;
+}
+
+static const struct driver_info ax8817x_info = {
+       .description = "ASIX AX8817x USB 2.0 Ethernet",
+       .bind = ax8817x_bind,
+       .flags =  FLAG_ETHER | FLAG_FRAMING_ASIX,
+};
+#endif /* CONFIG_USB_AX8817X */
 
 /*-------------------------------------------------------------------------
  *
@@ -1808,6 +2058,11 @@
                size = 6 + (sizeof (struct ethhdr) + dev->net->mtu);
        else
 #endif
+#ifdef CONFIG_USB_AX8817X
+       if (dev->driver_info->flags & FLAG_FRAMING_ASIX)
+               size = ETH_FRAME_LEN + 2;
+       else
+#endif
                size = (sizeof (struct ethhdr) + dev->net->mtu);
 
        if ((skb = alloc_skb (size, flags)) == 0) {
@@ -1816,6 +2071,10 @@
                usb_free_urb (urb);
                return;
        }
+#ifdef CONFIG_USB_AX8817X
+       if (dev->driver_info->flags & FLAG_FRAMING_ASIX)
+               skb_reserve(skb, 2);
+#endif
 
        entry = (struct skb_data *) skb->cb;
        entry->urb = urb;
@@ -2169,12 +2428,15 @@
 
 static int usbnet_ioctl (struct net_device *net, struct ifreq *rq, int cmd)
 {
-       switch (cmd) {
-       case SIOCETHTOOL:
+       struct usbnet *dev = (struct usbnet *)net->priv;
+
+       if (cmd == SIOCETHTOOL)
                return usbnet_ethtool_ioctl (net, (void __user *)rq->ifr_data);
-       default:
-               return -EOPNOTSUPP;
-       }
+       
+       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 -EOPNOTSUPP;
 }
 
 /*-------------------------------------------------------------------------*/
@@ -2344,6 +2606,10 @@
        usb_fill_bulk_urb (urb, dev->udev, dev->out,
                        skb->data, skb->len, tx_complete, skb);
        urb->transfer_flags |= URB_ASYNC_UNLINK;
+       
+#ifdef CONFIG_USB_AX8817X
+       urb->transfer_flags |= URB_ZERO_PACKET;
+#endif
 
        /* don't assume the hardware handles USB_ZERO_PACKET
         * NOTE:  strictly conforming cdc-ether devices should expect
@@ -2814,6 +3080,29 @@
 },
 #endif
 
+#ifdef CONFIG_USB_AX8817X
+{
+       // Linksys USB200M
+       USB_DEVICE (0x077b, 0x2226),
+       .driver_info =  (unsigned long) &ax8817x_info,
+}, {
+       // Netgear FA120
+       USB_DEVICE (0x0846, 0x1040),
+       .driver_info =  (unsigned long) &ax8817x_info,
+}, {
+       // DLink DUB-E100
+       USB_DEVICE (0x2001, 0x1a00),
+       .driver_info =  (unsigned long) &ax8817x_info,
+}, {
+       // Intellinet, ST Lab USB Ethernet
+       USB_DEVICE (0x0b95, 0x1720),
+       .driver_info =  (unsigned long) &ax8817x_info,
+}, {
+       // Hawking UF200, TrendNet TU2-ET100
+       USB_DEVICE (0x07b8, 0x420a),
+       .driver_info =  (unsigned long) &ax8817x_info,
+}, 
+#endif
        { },            // END
 };
 MODULE_DEVICE_TABLE (usb, products);
--- linux-2.6.0-test3.orig/drivers/usb/net/Kconfig      2003-08-09 15:05:07.131368024 
-0400
+++ linux-2.6.0-test3/drivers/usb/net/Kconfig   2003-08-09 15:10:17.219227504 -0400
@@ -7,7 +7,7 @@
 comment "Networking support is needed for USB Networking device support"
        depends on USB && !NET
 
-config USB_AX8817X
+config USB_AX8817X_STANDALONE
        tristate "USB ASIX AX8817X Ethernet device support (EXPERIMENTAL)"
        depends on USB && NET && EXPERIMENTAL
        ---help---
@@ -266,3 +266,26 @@
          IEEE 802 "local assignment" bit is set in the address, a "usbX"
          name is used instead.
 
+config USB_AX8817X
+       boolean "ASIX AX88172 Based USB 2.0 Ethernet Devices"
+       depends on USB_USBNET && EXPERIMENTAL
+       default y
+       help
+
+         This option adds support for ASIX AX88172 based USB 2.0
+         10/100 Ethernet devices.
+
+         This driver should work with at least the following devices:
+           * ASIX AX88172
+           * D-Link DUB-E100
+           * Hawking UF200
+           * Linksys USB200M
+           * Netgear FA120
+           * Intellinet
+           * ST Lab USB Ethernet
+           * TrendNet TU2-ET100
+
+         This driver creates an interface named "ethX", where X depends on
+         what other networking devices you have in use.  
+
+
--- linux-2.6.0-test3.orig/drivers/usb/net/Makefile.mii 2003-08-09 15:05:07.131368024 
-0400
+++ linux-2.6.0-test3/drivers/usb/net/Makefile.mii      2003-08-09 15:05:31.564653600 
-0400
@@ -4,3 +4,4 @@
 
 obj-$(CONFIG_USB_AX8817X)      += mii.o
 obj-$(CONFIG_USB_PEGASUS)      += mii.o
+obj-$(CONFIG_USB_USBNET)       += mii.o

Reply via email to