ChangeSet 1.1315.8.30, 2003/09/19 16:28:31-07:00, [EMAIL PROTECTED]

[PATCH] USB: ethtool_ops and ax8817x fixes for usbnet

This is a resend of all of my recent usbnet additions which
center on adding ethtool_ops support.  All have been re-diffed
against the latest usbnet from BK.

Changes:
* Add ethtool_ops support to usbnet
* Add GPIO bit toggling upon initialization of ax8817x devices
  so that xmit works properly
* Add Wake-On-Lan and eeprom dump support for ax8817x devices
  (this only adds the config support for WOL, actual operation
   is more of a USB/Power Management function)
* Change ax8817x_set_multicast to not kmalloc for the filter
  since there was no clean way to free the memory after URB submit.
  Instead, we make use of the 20 byte dev->data.


 drivers/usb/net/usbnet.c |  275 ++++++++++++++++++++++++++++++++---------------
 1 files changed, 188 insertions(+), 87 deletions(-)


diff -Nru a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c
--- a/drivers/usb/net/usbnet.c  Fri Sep 19 17:08:04 2003
+++ b/drivers/usb/net/usbnet.c  Fri Sep 19 17:08:04 2003
@@ -252,6 +252,8 @@
        /* for new devices, use the descriptor-reading code instead */
        int             in;             /* rx endpoint */
        int             out;            /* tx endpoint */
+
+       unsigned long   data;           /* Misc driver specific data */
 };
 
 // we record the state for each of our queued skbs
@@ -298,6 +300,9 @@
 
 /*-------------------------------------------------------------------------*/
 
+static struct ethtool_ops usbnet_ethtool_ops;
+static void usbnet_get_drvinfo (struct net_device *, struct ethtool_drvinfo *);
+
 /* mostly for PDA style devices, which are always connected if present */
 static int always_connected (struct usbnet *dev)
 {
@@ -383,10 +388,10 @@
 
 
 #ifdef CONFIG_USB_AX8817X
-#define NEED_MII
 /* ASIX AX8817X based USB 2.0 Ethernet Devices */
 
 #define HAVE_HARDWARE
+#define NEED_MII
 
 #include <linux/crc32.h>
 
@@ -394,6 +399,8 @@
 #define AX_CMD_READ_MII_REG            0x07
 #define AX_CMD_WRITE_MII_REG           0x08
 #define AX_CMD_SET_HW_MII              0x0a
+#define AX_CMD_READ_EEPROM             0x0b
+#define AX_CMD_WRITE_EEPROM            0x0c
 #define AX_CMD_WRITE_RX_CTL            0x10
 #define AX_CMD_READ_IPG012             0x11
 #define AX_CMD_WRITE_IPG0              0x12
@@ -403,8 +410,15 @@
 #define AX_CMD_READ_NODE_ID            0x17
 #define AX_CMD_READ_PHY_ID             0x19
 #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_MONITOR_MODE                        0x01
+#define AX_MONITOR_LINK                        0x02
+#define AX_MONITOR_MAGIC               0x04
+#define AX_MONITOR_HSFS                        0x10
+
 #define AX_MCAST_FILTER_SIZE           8
 #define AX_MAX_MCAST                   64
 
@@ -441,11 +455,11 @@
 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);
 }
@@ -456,12 +470,12 @@
        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);
@@ -496,34 +510,31 @@
        } else if (net->mc_count == 0) {
                /* just broadcast and directed */
        } else {
+               /* We use the 20 byte dev->data
+                * for our 8 byte filter buffer
+                * to avoid allocating memory that
+                * is tricky to free later */
+               u8 *multi_filter = (u8 *)dev->data;
                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 buffer for setting the multicast
-                          filter. Try all multi mode. */
-                       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);
+               memset(multi_filter, 0, AX_MCAST_FILTER_SIZE);
 
-                       rx_ctl |= 0x10;
+               /* 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);
@@ -533,7 +544,7 @@
 {
        struct usbnet *dev = netdev->priv;
        u16 res;
-       u8 buf[4];
+       u8 buf[1];
 
        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);
@@ -546,23 +557,103 @@
 {
        struct usbnet *dev = netdev->priv;
        u16 res = val;
-       u8 buf[4];
+       u8 buf[1];
 
        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);
 }
 
+void ax8817x_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+       struct usbnet *dev = (struct usbnet *)net->priv;
+       u8 opt;
+
+       if (ax8817x_read_cmd(dev, AX_CMD_READ_MONITOR_MODE, 0, 0, 1, &opt) < 0) {
+               wolinfo->supported = 0;
+               wolinfo->wolopts = 0;
+               return;
+       }
+       wolinfo->supported = WAKE_PHY | WAKE_MAGIC;
+       wolinfo->wolopts = 0;
+       if (opt & AX_MONITOR_MODE) {
+               if (opt & AX_MONITOR_LINK)
+                       wolinfo->wolopts |= WAKE_PHY;
+               if (opt & AX_MONITOR_MAGIC)
+                       wolinfo->wolopts |= WAKE_MAGIC;
+       }
+}
+
+int ax8817x_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+       struct usbnet *dev = (struct usbnet *)net->priv;
+       u8 opt = 0;
+       u8 buf[1];
+
+       if (wolinfo->wolopts & WAKE_PHY)
+               opt |= AX_MONITOR_LINK;
+       if (wolinfo->wolopts & WAKE_MAGIC)
+               opt |= AX_MONITOR_MAGIC;
+       if (opt != 0)
+               opt |= AX_MONITOR_MODE;
+
+       if (ax8817x_write_cmd(dev, AX_CMD_WRITE_MONITOR_MODE,
+                             opt, 0, 0, &buf) < 0)
+               return -EINVAL;
+
+       return 0;
+}
+
+int ax8817x_get_eeprom(struct net_device *net, 
+                      struct ethtool_eeprom *eeprom, u8 *data)
+{
+       struct usbnet *dev = (struct usbnet *)net->priv;
+       u16 *ebuf = (u16 *)data;
+       int i;
+
+       /* Crude hack to ensure that we don't overwrite memory
+        * if an odd length is supplied
+        */
+       if (eeprom->len % 2)
+               return -EINVAL;
+
+       /* 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;
+}
+
+static void ax8817x_get_drvinfo (struct net_device *net,
+                                struct ethtool_drvinfo *info)
+{
+       /* Inherit standard device info */
+       usbnet_get_drvinfo(net, info);
+       info->eedump_len = 0x3e;
+}
+
 static int ax8817x_bind(struct usbnet *dev, struct usb_interface *intf)
 {
        int ret;
        u8 buf[6];
        u16 *buf16 = (u16 *) 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);
 
+       /* 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;
+               wait_ms(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;
@@ -640,6 +731,11 @@
 
        dev->net->set_multicast_list = ax8817x_set_multicast;
 
+       usbnet_ethtool_ops.get_drvinfo = &ax8817x_get_drvinfo;
+       usbnet_ethtool_ops.get_wol = &ax8817x_get_wol;
+       usbnet_ethtool_ops.set_wol = &ax8817x_set_wol;
+       usbnet_ethtool_ops.get_eeprom = &ax8817x_get_eeprom;
+
        return 0;
 }
 
@@ -647,7 +743,30 @@
        .description = "ASIX AX8817x USB 2.0 Ethernet",
        .bind = ax8817x_bind,
        .flags =  FLAG_ETHER,
+       .data = 0x00130103,
+};
+
+static const struct driver_info dlink_dub_e100_info = {
+       .description = "DLink DUB-E100 USB Ethernet",
+       .bind = ax8817x_bind,
+       .flags =  FLAG_ETHER,
+       .data = 0x009f9d9f,
+};
+
+static const struct driver_info netgear_fa120_info = {
+       .description = "Netgear FA-120 USB Ethernet",
+       .bind = ax8817x_bind,
+       .flags =  FLAG_ETHER,
+       .data = 0x00130103,
 };
+
+static const struct driver_info hawking_uf200_info = {
+       .description = "Hawking UF200 USB Ethernet",
+       .bind = ax8817x_bind,
+       .flags =  FLAG_ETHER,
+       .data = 0x001f1d1f,
+};
+
 #endif /* CONFIG_USB_AX8817X */
 
 
@@ -2415,72 +2534,45 @@
 
 /*-------------------------------------------------------------------------*/
 
-static inline int
-usbnet_ethtool_ioctl (struct net_device *net, void __user *useraddr)
+static void usbnet_get_drvinfo (struct net_device *net, struct ethtool_drvinfo *info)
 {
-       struct usbnet   *dev = (struct usbnet *) net->priv;
-       u32             cmd;
+       struct usbnet *dev = net->priv;
 
-       if (get_user (cmd, (u32 *)useraddr))
-               return -EFAULT;
-       switch (cmd) {
-
-       case ETHTOOL_GDRVINFO: {        /* get driver info */
-               struct ethtool_drvinfo          info;
-
-               memset (&info, 0, sizeof info);
-               info.cmd = ETHTOOL_GDRVINFO;
-               strncpy (info.driver, driver_name, sizeof info.driver);
-               strncpy (info.version, DRIVER_VERSION, sizeof info.version);
-               strncpy (info.fw_version, dev->driver_info->description,
-                       sizeof info.fw_version);
-               usb_make_path (dev->udev, info.bus_info, sizeof info.bus_info);
-               if (copy_to_user (useraddr, &info, sizeof (info)))
-                       return -EFAULT;
-               return 0;
-               }
+       strncpy (info->driver, driver_name, sizeof info->driver);
+       strncpy (info->version, DRIVER_VERSION, sizeof info->version);
+       strncpy (info->fw_version, dev->driver_info->description,
+               sizeof info->fw_version);
+       usb_make_path (dev->udev, info->bus_info, sizeof info->bus_info);
+}
 
-       case ETHTOOL_GLINK:             /* get link status */
-               if (dev->driver_info->check_connect) {
-                       struct ethtool_value    edata = { ETHTOOL_GLINK };
-
-                       edata.data = dev->driver_info->check_connect (dev) == 0;
-                       if (copy_to_user (useraddr, &edata, sizeof (edata)))
-                               return -EFAULT;
-                       return 0;
-               }
-               break;
+static u32 usbnet_get_link (struct net_device *net)
+{
+       struct usbnet *dev = net->priv;
 
-       case ETHTOOL_GMSGLVL: {         /* get message-level */
-               struct ethtool_value    edata = {ETHTOOL_GMSGLVL};
+       /* If a check_connect is defined, return it's results */
+       if (dev->driver_info->check_connect)
+               return dev->driver_info->check_connect (dev) == 0;
 
-               edata.data = dev->msg_level;
-               if (copy_to_user (useraddr, &edata, sizeof (edata)))
-                       return -EFAULT;
-               return 0;
-               }
+       /* Otherwise, we're up to avoid breaking scripts */
+       return 1;
+}
 
-       case ETHTOOL_SMSGLVL: {         /* set message-level */
-               struct ethtool_value    edata;
+static u32 usbnet_get_msglevel (struct net_device *net)
+{
+       struct usbnet *dev = net->priv;
 
-               if (copy_from_user (&edata, useraddr, sizeof (edata)))
-                       return -EFAULT;
-               dev->msg_level = edata.data;
-               return 0;
-               }
-       
-       /* could also map RINGPARAM to RX/TX QLEN */
+       return dev->msg_level;
+}
 
-       }
-       /* Note that the ethtool user space code requires EOPNOTSUPP */
-       return -EOPNOTSUPP;
+static void usbnet_set_msglevel (struct net_device *net, u32 level)
+{
+       struct usbnet *dev = net->priv;
+
+       dev->msg_level = level;
 }
 
 static int usbnet_ioctl (struct net_device *net, struct ifreq *rq, int cmd)
 {
-       if (cmd == SIOCETHTOOL)
-               return usbnet_ethtool_ioctl (net, (void __user *)rq->ifr_data);
-
 #ifdef NEED_MII
        {
        struct usbnet *dev = (struct usbnet *)net->priv;
@@ -2889,6 +2981,7 @@
        net->watchdog_timeo = TX_TIMEOUT_JIFFIES;
        net->tx_timeout = usbnet_tx_timeout;
        net->do_ioctl = usbnet_ioctl;
+       net->ethtool_ops = &usbnet_ethtool_ops;
 
        // allow device-specific bind/init procedures
        // NOTE net->name still not usable ...
@@ -2991,19 +3084,19 @@
 }, {
        // Netgear FA120
        USB_DEVICE (0x0846, 0x1040),
-       .driver_info =  (unsigned long) &ax8817x_info,
+       .driver_info =  (unsigned long) &netgear_fa120_info,
 }, {
        // DLink DUB-E100
        USB_DEVICE (0x2001, 0x1a00),
-       .driver_info =  (unsigned long) &ax8817x_info,
+       .driver_info =  (unsigned long) &dlink_dub_e100_info,
 }, {
        // Intellinet, ST Lab USB Ethernet
        USB_DEVICE (0x0b95, 0x1720),
-       .driver_info =  (unsigned long) &ax8817x_info,
+       .driver_info =  (unsigned long) &ax8817x_info,
 }, {
        // Hawking UF200, TrendNet TU2-ET100
        USB_DEVICE (0x07b8, 0x420a),
-       .driver_info =  (unsigned long) &ax8817x_info,
+       .driver_info =  (unsigned long) &hawking_uf200_info,
 }, 
 #endif
 
@@ -3165,6 +3258,14 @@
        .id_table =     products,
        .probe =        usbnet_probe,
        .disconnect =   usbnet_disconnect,
+};
+
+/* Default ethtool_ops assigned.  Devices can override in their bind() routine */
+static struct ethtool_ops usbnet_ethtool_ops = {
+       .get_drvinfo            = usbnet_get_drvinfo,
+       .get_link               = usbnet_get_link,
+       .get_msglevel           = usbnet_get_msglevel,
+       .set_msglevel           = usbnet_set_msglevel,
 };
 
 /*-------------------------------------------------------------------------*/



-------------------------------------------------------
This sf.net email is sponsored by:ThinkGeek
Welcome to geek heaven.
http://thinkgeek.com/sf
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to