aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/net/usbnet.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/usb/net/usbnet.c
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/usb/net/usbnet.c')
-rw-r--r--drivers/usb/net/usbnet.c4106
1 files changed, 4106 insertions, 0 deletions
diff --git a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c
new file mode 100644
index 00000000000..dd8b4456ea3
--- /dev/null
+++ b/drivers/usb/net/usbnet.c
@@ -0,0 +1,4106 @@
+/*
+ * USB Networking Links
+ * Copyright (C) 2000-2003 by David Brownell <dbrownell@users.sourceforge.net>
+ * Copyright (C) 2002 Pavel Machek <pavel@ucw.cz>
+ * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
+ * Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net>
+ * Copyright (c) 2002-2003 TiVo Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This is a generic "USB networking" framework that works with several
+ * kinds of full and high speed networking devices:
+ *
+ * + USB host-to-host "network cables", used for IP-over-USB links.
+ * These are often used for Laplink style connectivity products.
+ * - AnchorChip 2720
+ * - Belkin, eTEK (interops with Win32 drivers)
+ * - 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.
+ * - Linux PDAs like iPaq, Yopy, and Zaurus
+ * - The BLOB boot loader (for diskless booting)
+ * - Linux "gadgets", perhaps using PXA-2xx or Net2280 controllers
+ * - Devices using EPSON's sample USB firmware
+ * - CDC-Ethernet class devices, such as many cable modems
+ *
+ * + Adapters to networks such as Ethernet.
+ * - AX8817X based USB 2.0 products
+ *
+ * Links to these devices can be bridged using Linux Ethernet bridging.
+ * With minor exceptions, these all use similar USB framing for network
+ * traffic, but need different protocols for control traffic.
+ *
+ * USB devices can implement their side of this protocol at the cost
+ * of two bulk endpoints; it's not restricted to "cable" applications.
+ * See the SA1110, Zaurus, or EPSON device/client support in this driver;
+ * slave/target drivers such as "usb-eth" (on most SA-1100 PDAs) or
+ * "g_ether" (in the Linux "gadget" framework) implement that behavior
+ * within devices.
+ *
+ *
+ * CHANGELOG:
+ *
+ * 13-sep-2000 experimental, new
+ * 10-oct-2000 usb_device_id table created.
+ * 28-oct-2000 misc fixes; mostly, discard more TTL-mangled rx packets.
+ * 01-nov-2000 usb_device_id table and probing api update by
+ * Adam J. Richter <adam@yggdrasil.com>.
+ * 18-dec-2000 (db) tx watchdog, "net1080" renaming to "usbnet", device_info
+ * and prolific support, isolate net1080-specific bits, cleanup.
+ * fix unlink_urbs oops in D3 PM resume code path.
+ *
+ * 02-feb-2001 (db) fix tx skb sharing, packet length, match_flags, ...
+ * 08-feb-2001 stubbed in "linuxdev", maybe the SA-1100 folk can use it;
+ * AnchorChips 2720 support (from spec) for testing;
+ * fix bit-ordering problem with ethernet multicast addr
+ * 19-feb-2001 Support for clearing halt conditions. SA1100 UDC support
+ * updates. Oleg Drokin (green@iXcelerator.com)
+ * 25-mar-2001 More SA-1100 updates, including workaround for ip problem
+ * expecting cleared skb->cb and framing change to match latest
+ * handhelds.org version (Oleg). Enable device IDs from the
+ * Win32 Belkin driver; other cleanups (db).
+ * 16-jul-2001 Bugfixes for uhci oops-on-unplug, Belkin support, various
+ * cleanups for problems not yet seen in the field. (db)
+ * 17-oct-2001 Handle "Advance USBNET" product, like Belkin/eTEK devices,
+ * from Ioannis Mavroukakis <i.mavroukakis@btinternet.com>;
+ * rx unlinks somehow weren't async; minor cleanup.
+ * 03-nov-2001 Merged GeneSys driver; original code from Jiun-Jie Huang
+ * <huangjj@genesyslogic.com.tw>, updated by Stanislav Brabec
+ * <utx@penguin.cz>. Made framing options (NetChip/GeneSys)
+ * tie mostly to (sub)driver info. Workaround some PL-2302
+ * chips that seem to reject SET_INTERFACE requests.
+ *
+ * 06-apr-2002 Added ethtool support, based on a patch from Brad Hards.
+ * Level of diagnostics is more configurable; they use device
+ * location (usb_device->devpath) instead of address (2.5).
+ * For tx_fixup, memflags can't be NOIO.
+ * 07-may-2002 Generalize/cleanup keventd support, handling rx stalls (mostly
+ * for USB 2.0 TTs) and memory shortages (potential) too. (db)
+ * Use "locally assigned" IEEE802 address space. (Brad Hards)
+ * 18-oct-2002 Support for Zaurus (Pavel Machek), related cleanup (db).
+ * 14-dec-2002 Remove Zaurus-private crc32 code (Pavel); 2.5 oops fix,
+ * cleanups and stubbed PXA-250 support (db), fix for framing
+ * issues on Z, net1080, and gl620a (Toby Milne)
+ *
+ * 31-mar-2003 Use endpoint descriptors: high speed support, simpler sa1100
+ * vs pxa25x, and CDC Ethernet. Throttle down log floods on
+ * 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
+// #define VERBOSE // more; success messages
+
+#include <linux/config.h>
+#ifdef CONFIG_USB_DEBUG
+# define DEBUG
+#endif
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/random.h>
+#include <linux/ethtool.h>
+#include <linux/workqueue.h>
+#include <linux/mii.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+#include <linux/usb.h>
+#include <asm/io.h>
+#include <asm/scatterlist.h>
+#include <linux/mm.h>
+#include <linux/dma-mapping.h>
+
+#define DRIVER_VERSION "03-Nov-2004"
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Nineteen USB 1.1 max size bulk transactions per frame (ms), max.
+ * Several dozen bytes of IPv4 data can fit in two such transactions.
+ * One maximum size Ethernet packet takes twenty four of them.
+ * For high speed, each frame comfortably fits almost 36 max size
+ * Ethernet packets (so queues should be bigger).
+ */
+#define RX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? 60 : 4)
+#define TX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? 60 : 4)
+
+// packets are always ethernet inside
+// ... except they can be bigger (limit of 64K with NetChip framing)
+#define MIN_PACKET sizeof(struct ethhdr)
+#define MAX_PACKET 32768
+
+// reawaken network queue this soon after stopping; else watchdog barks
+#define TX_TIMEOUT_JIFFIES (5*HZ)
+
+// throttle rx/tx briefly after some faults, so khubd might disconnect()
+// us (it polls at HZ/4 usually) before we report too many false errors.
+#define THROTTLE_JIFFIES (HZ/8)
+
+// for vendor-specific control operations
+#define CONTROL_TIMEOUT_MS 500
+
+// between wakeups
+#define UNLINK_TIMEOUT_MS 3
+
+/*-------------------------------------------------------------------------*/
+
+// randomly generated ethernet address
+static u8 node_id [ETH_ALEN];
+
+// state we keep for each device we handle
+struct usbnet {
+ // housekeeping
+ struct usb_device *udev;
+ struct driver_info *driver_info;
+ wait_queue_head_t *wait;
+
+ // i/o info: pipes etc
+ unsigned in, out;
+ struct usb_host_endpoint *status;
+ unsigned maxpacket;
+ struct timer_list delay;
+
+ // protocol/interface state
+ struct net_device *net;
+ struct net_device_stats stats;
+ int msg_enable;
+ unsigned long data [5];
+
+ struct mii_if_info mii;
+
+ // various kinds of pending driver work
+ struct sk_buff_head rxq;
+ struct sk_buff_head txq;
+ struct sk_buff_head done;
+ struct urb *interrupt;
+ struct tasklet_struct bh;
+
+ struct work_struct kevent;
+ unsigned long flags;
+# define EVENT_TX_HALT 0
+# define EVENT_RX_HALT 1
+# define EVENT_RX_MEMORY 2
+# define EVENT_STS_SPLIT 3
+};
+
+// device-specific info used by the driver
+struct driver_info {
+ char *description;
+
+ int flags;
+/* framing is CDC Ethernet, not writing ZLPs (hw issues), or optionally: */
+#define FLAG_FRAMING_NC 0x0001 /* guard against device dropouts */
+#define FLAG_FRAMING_GL 0x0002 /* genelink batches packets */
+#define FLAG_FRAMING_Z 0x0004 /* zaurus adds a trailer */
+#define FLAG_FRAMING_RN 0x0008 /* RNDIS batches, plus huge header */
+
+#define FLAG_NO_SETINT 0x0010 /* device can't set_interface() */
+#define FLAG_ETHER 0x0020 /* maybe use "eth%d" names */
+
+#define FLAG_FRAMING_AX 0x0040 /* AX88772/178 packets */
+
+ /* init device ... can sleep, or cause probe() failure */
+ int (*bind)(struct usbnet *, struct usb_interface *);
+
+ /* cleanup device ... can sleep, but can't fail */
+ void (*unbind)(struct usbnet *, struct usb_interface *);
+
+ /* reset device ... can sleep */
+ int (*reset)(struct usbnet *);
+
+ /* see if peer is connected ... can sleep */
+ int (*check_connect)(struct usbnet *);
+
+ /* for status polling */
+ void (*status)(struct usbnet *, struct urb *);
+
+ /* fixup rx packet (strip framing) */
+ int (*rx_fixup)(struct usbnet *dev, struct sk_buff *skb);
+
+ /* fixup tx packet (add framing) */
+ struct sk_buff *(*tx_fixup)(struct usbnet *dev,
+ struct sk_buff *skb, int flags);
+
+ // FIXME -- also an interrupt mechanism
+ // useful for at least PL2301/2302 and GL620USB-A
+ // and CDC use them to report 'is it connected' changes
+
+ /* 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
+enum skb_state {
+ illegal = 0,
+ tx_start, tx_done,
+ rx_start, rx_done, rx_cleanup
+};
+
+struct skb_data { // skb->cb is one of these
+ struct urb *urb;
+ struct usbnet *dev;
+ enum skb_state state;
+ size_t length;
+};
+
+static const char driver_name [] = "usbnet";
+
+/* use ethtool to change the level for any given device */
+static int msg_level = -1;
+module_param (msg_level, int, 0);
+MODULE_PARM_DESC (msg_level, "Override default message level");
+
+
+#ifdef DEBUG
+#define devdbg(usbnet, fmt, arg...) \
+ printk(KERN_DEBUG "%s: " fmt "\n" , (usbnet)->net->name , ## arg)
+#else
+#define devdbg(usbnet, fmt, arg...) do {} while(0)
+#endif
+
+#define deverr(usbnet, fmt, arg...) \
+ printk(KERN_ERR "%s: " fmt "\n" , (usbnet)->net->name , ## arg)
+#define devwarn(usbnet, fmt, arg...) \
+ printk(KERN_WARNING "%s: " fmt "\n" , (usbnet)->net->name , ## arg)
+
+#define devinfo(usbnet, fmt, arg...) \
+ printk(KERN_INFO "%s: " fmt "\n" , (usbnet)->net->name , ## arg); \
+
+/*-------------------------------------------------------------------------*/
+
+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);
+
+/* mostly for PDA style devices, which are always connected if present */
+static int always_connected (struct usbnet *dev)
+{
+ return 0;
+}
+
+/* handles CDC Ethernet and many other network "bulk data" interfaces */
+static int
+get_endpoints (struct usbnet *dev, struct usb_interface *intf)
+{
+ int tmp;
+ struct usb_host_interface *alt = NULL;
+ struct usb_host_endpoint *in = NULL, *out = NULL;
+ struct usb_host_endpoint *status = NULL;
+
+ for (tmp = 0; tmp < intf->num_altsetting; tmp++) {
+ unsigned ep;
+
+ in = out = status = NULL;
+ alt = intf->altsetting + tmp;
+
+ /* take the first altsetting with in-bulk + out-bulk;
+ * remember any status endpoint, just in case;
+ * ignore other endpoints and altsetttings.
+ */
+ for (ep = 0; ep < alt->desc.bNumEndpoints; ep++) {
+ struct usb_host_endpoint *e;
+ int intr = 0;
+
+ e = alt->endpoint + ep;
+ switch (e->desc.bmAttributes) {
+ case USB_ENDPOINT_XFER_INT:
+ if (!(e->desc.bEndpointAddress & USB_DIR_IN))
+ continue;
+ intr = 1;
+ /* FALLTHROUGH */
+ case USB_ENDPOINT_XFER_BULK:
+ break;
+ default:
+ continue;
+ }
+ if (e->desc.bEndpointAddress & USB_DIR_IN) {
+ if (!intr && !in)
+ in = e;
+ else if (intr && !status)
+ status = e;
+ } else {
+ if (!out)
+ out = e;
+ }
+ }
+ if (in && out)
+ break;
+ }
+ if (!alt || !in || !out)
+ return -EINVAL;
+
+ if (alt->desc.bAlternateSetting != 0
+ || !(dev->driver_info->flags & FLAG_NO_SETINT)) {
+ tmp = usb_set_interface (dev->udev, alt->desc.bInterfaceNumber,
+ alt->desc.bAlternateSetting);
+ if (tmp < 0)
+ return tmp;
+ }
+
+ dev->in = usb_rcvbulkpipe (dev->udev,
+ in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+ dev->out = usb_sndbulkpipe (dev->udev,
+ out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+ dev->status = status;
+ return 0;
+}
+
+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;
+
+ skb->dev = dev->net;
+ skb->protocol = eth_type_trans (skb, dev->net);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += skb->len;
+
+ if (netif_msg_rx_status (dev))
+ devdbg (dev, "< rx, len %d, type 0x%x",
+ skb->len + sizeof (struct ethhdr), skb->protocol);
+ memset (skb->cb, 0, sizeof (struct skb_data));
+ status = netif_rx (skb);
+ if (status != NET_RX_SUCCESS && netif_msg_rx_err (dev))
+ devdbg (dev, "netif_rx status %d", status);
+}
+
+
+#ifdef CONFIG_USB_ALI_M5632
+#define HAVE_HARDWARE
+
+/*-------------------------------------------------------------------------
+ *
+ * ALi M5632 driver ... does high speed
+ *
+ *-------------------------------------------------------------------------*/
+
+static const struct driver_info ali_m5632_info = {
+ .description = "ALi M5632",
+};
+
+
+#endif
+
+
+#ifdef CONFIG_USB_AN2720
+#define HAVE_HARDWARE
+
+/*-------------------------------------------------------------------------
+ *
+ * AnchorChips 2720 driver ... http://www.cypress.com
+ *
+ * This doesn't seem to have a way to detect whether the peer is
+ * connected, or need any reset handshaking. It's got pretty big
+ * internal buffers (handles most of a frame's worth of data).
+ * Chip data sheets don't describe any vendor control messages.
+ *
+ *-------------------------------------------------------------------------*/
+
+static const struct driver_info an2720_info = {
+ .description = "AnchorChips/Cypress 2720",
+ // no reset available!
+ // no check_connect available!
+
+ .in = 2, .out = 2, // direction distinguishes these
+};
+
+#endif /* CONFIG_USB_AN2720 */
+
+
+#ifdef CONFIG_USB_AX8817X
+/* ASIX AX8817X based USB 2.0 Ethernet Devices */
+
+#define HAVE_HARDWARE
+#define NEED_MII
+
+#include <linux/crc32.h>
+
+#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_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
+#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_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
+
+#define AX_MCAST_FILTER_SIZE 8
+#define AX_MAX_MCAST 64
+
+#define AX_INTERRUPT_BUFSIZE 8
+
+#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 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 urb *int_urb;
+ u8 *int_buf;
+};
+
+static int ax8817x_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+ u16 size, void *data)
+{
+ return usb_control_msg(
+ dev->udev,
+ usb_rcvctrlpipe(dev->udev, 0),
+ cmd,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value,
+ index,
+ data,
+ size,
+ CONTROL_TIMEOUT_MS);
+}
+
+static int ax8817x_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+ u16 size, void *data)
+{
+ return usb_control_msg(
+ dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ cmd,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value,
+ index,
+ data,
+ size,
+ CONTROL_TIMEOUT_MS);
+}
+
+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_interrupt_complete(struct urb *urb, struct pt_regs *regs)
+{
+ struct usbnet *dev = (struct usbnet *)urb->context;
+ struct ax8817x_data *data = (struct ax8817x_data *)&dev->data;
+ int link;
+
+ if (urb->status < 0) {
+ devdbg(dev,"ax8817x_interrupt_complete() failed with %d",
+ urb->status);
+ } else {
+ link = data->int_buf[2] & 0x01;
+ if (netif_carrier_ok(dev->net) != link) {
+ if (link)
+ netif_carrier_on(dev->net);
+ else
+ netif_carrier_off(dev->net);
+ devdbg(dev, "ax8817x - Link Status is: %d", link);
+ }
+ usb_submit_urb(data->int_urb, GFP_ATOMIC);
+ }
+}
+
+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 = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+ req->bRequest = cmd;
+ req->wValue = cpu_to_le16(value);
+ req->wIndex = cpu_to_le16(index);
+ req->wLength = cpu_to_le16(size);
+
+ usb_fill_control_urb(urb, dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ (void *)req, data, size,
+ ax8817x_async_cmd_callback, req);
+
+ if((status = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+ deverr(dev, "Error submitting the control message: status=%d", status);
+ kfree(req);
+ usb_free_urb(urb);
+ }
+}
+
+static void ax8817x_set_multicast(struct net_device *net)
+{
+ struct usbnet *dev = netdev_priv(net);
+ struct ax8817x_data *data = (struct ax8817x_data *)&dev->data;
+ u8 rx_ctl = 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 {
+ /* We use the 20 byte dev->data
+ * for our 8 byte filter buffer
+ * to avoid allocating memory that
+ * is tricky to free later */
+ struct dev_mc_list *mc_list = net->mc_list;
+ u32 crc_bits;
+ int i;
+
+ memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE);
+
+ /* Build the multicast hash filter. */
+ for (i = 0; i < net->mc_count; i++) {
+ crc_bits =
+ ether_crc(ETH_ALEN,
+ mc_list->dmi_addr) >> 26;
+ data->multi_filter[crc_bits >> 3] |=
+ 1 << (crc_bits & 7);
+ mc_list = mc_list->next;
+ }
+
+ ax8817x_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0,
+ AX_MCAST_FILTER_SIZE, data->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(netdev);
+ u16 res;
+ 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);
+ 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(netdev);
+ u16 res = val;
+ 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);
+}
+
+static void ax8817x_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+ struct usbnet *dev = netdev_priv(net);
+ 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;
+ }
+}
+
+static int ax8817x_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+ struct usbnet *dev = netdev_priv(net);
+ 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;
+}
+
+static int ax8817x_get_eeprom_len(struct net_device *net)
+{
+ return AX_EEPROM_LEN;
+}
+
+static int ax8817x_get_eeprom(struct net_device *net,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ struct usbnet *dev = netdev_priv(net);
+ u16 *ebuf = (u16 *)data;
+ int i;
+
+ /* Crude hack to ensure that we don't overwrite memory
+ * if an odd length is supplied
+ */
+ if (eeprom->len % 2)
+ return -EINVAL;
+
+ eeprom->magic = AX_EEPROM_MAGIC;
+
+ /* ax8817x returns 2 bytes from eeprom on read */
+ for (i=0; i < eeprom->len / 2; i++) {
+ if (ax8817x_read_cmd(dev, AX_CMD_READ_EEPROM,
+ eeprom->offset + i, 0, 2, &ebuf[i]) < 0)
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void ax8817x_get_drvinfo (struct net_device *net,
+ struct ethtool_drvinfo *info)
+{
+ /* Inherit standard device info */
+ usbnet_get_drvinfo(net, info);
+ info->eedump_len = 0x3e;
+}
+
+static int ax8817x_get_settings(struct net_device *net, struct ethtool_cmd *cmd)
+{
+ struct usbnet *dev = netdev_priv(net);
+
+ return mii_ethtool_gset(&dev->mii,cmd);
+}
+
+static int ax8817x_set_settings(struct net_device *net, struct ethtool_cmd *cmd)
+{
+ struct usbnet *dev = netdev_priv(net);
+
+ return mii_ethtool_sset(&dev->mii,cmd);
+}
+
+/* We need to override some ethtool_ops so we require our
+ own structure so we don't interfere with other usbnet
+ devices that may be connected at the same time. */
+static struct ethtool_ops ax8817x_ethtool_ops = {
+ .get_drvinfo = ax8817x_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+ .get_msglevel = 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 ax8817x_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+ int ret = 0;
+ void *buf;
+ int i;
+ unsigned long gpio_bits = dev->driver_info->data;
+ struct ax8817x_data *data = (struct ax8817x_data *)dev->data;
+
+ get_endpoints(dev,intf);
+
+ if ((data->int_urb = usb_alloc_urb (0, GFP_KERNEL)) == NULL) {
+ dbg ("%s: cannot allocate interrupt URB",
+ dev->net->name);
+ ret = -ENOMEM;
+ goto out1;
+ }
+
+ if ((data->int_buf = kmalloc(AX_INTERRUPT_BUFSIZE, GFP_KERNEL)) == NULL) {
+ dbg ("%s: cannot allocate memory for interrupt buffer",
+ dev->net->name);
+ ret = -ENOMEM;
+ goto out1;
+ }
+ memset(data->int_buf, 0, AX_INTERRUPT_BUFSIZE);
+
+ usb_fill_int_urb (data->int_urb, dev->udev,
+ usb_rcvintpipe (dev->udev, 1),
+ data->int_buf, AX_INTERRUPT_BUFSIZE,
+ ax8817x_interrupt_complete, dev,
+ dev->udev->speed == USB_SPEED_HIGH ? 8 : 100);
+
+ buf = kmalloc(ETH_ALEN, GFP_KERNEL);
+ if(!buf) {
+ ret = -ENOMEM;
+ goto out2;
+ }
+
+ /* 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)
+ goto out3;
+ 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);
+ goto out3;
+ }
+
+ /* 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);
+ goto out3;
+ }
+ 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);
+ goto out3;
+ } 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 out3;
+ }
+
+ /* 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 = *((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,
+ cpu_to_le16(BMCR_RESET));
+ ax8817x_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+ cpu_to_le16(ADVERTISE_ALL | ADVERTISE_CSMA | 0x0400));
+ mii_nway_restart(&dev->mii);
+
+ if((ret = usb_submit_urb(data->int_urb, GFP_KERNEL)) < 0) {
+ dbg("Failed to submit interrupt URB: %02x", ret);
+ goto out2;
+ }
+
+ return 0;
+out3:
+ kfree(buf);
+out2:
+ kfree(data->int_buf);
+out1:
+ usb_free_urb(data->int_urb);
+ return ret;
+}
+
+static void ax8817x_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+ struct ax8817x_data *data = (struct ax8817x_data *)dev->data;
+
+ usb_kill_urb(data->int_urb);
+ usb_free_urb(data->int_urb);
+ kfree(data->int_buf);
+}
+
+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;
+ struct ax8817x_data *data = (struct ax8817x_data *)dev->data;
+
+ get_endpoints(dev,intf);
+
+ if ((data->int_urb = usb_alloc_urb (0, GFP_KERNEL)) == 0) {
+ dbg ("Cannot allocate interrupt URB");
+ ret = -ENOMEM;
+ goto out1;
+ }
+
+ if ((data->int_buf = kmalloc(AX_INTERRUPT_BUFSIZE, GFP_KERNEL)) == NULL) {
+ dbg ("Cannot allocate memory for interrupt buffer");
+ ret = -ENOMEM;
+ goto out1;
+ }
+ memset(data->int_buf, 0, AX_INTERRUPT_BUFSIZE);
+
+ usb_fill_int_urb (data->int_urb, dev->udev,
+ usb_rcvintpipe (dev->udev, 1),
+ data->int_buf, AX_INTERRUPT_BUFSIZE,
+ ax8817x_interrupt_complete, dev,
+ dev->udev->speed == USB_SPEED_HIGH ? 8 : 100);
+
+ buf = kmalloc(6, GFP_KERNEL);
+ if(!buf) {
+ dbg ("Cannot allocate memory for buffer");
+ ret = -ENOMEM;
+ goto out2;
+ }
+
+ if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_GPIOS,
+ 0x00B0, 0, 0, buf)) < 0)
+ goto out3;
+
+ 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 out3;
+ }
+
+ 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 out3;
+ }
+
+ 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 out3;
+ }
+
+ 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 out3;
+ }
+
+ 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 out3;
+ }
+
+ /* 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 out3;
+ }
+ 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 out3;
+ }
+
+ 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 out3;
+ }
+
+ /* 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 out3;
+ } 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 out3;
+ }
+ 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 out3;
+ }
+ 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 out3;
+ }
+ msleep(150);
+
+
+ dev->net->set_multicast_list = ax8817x_set_multicast;
+ dev->net->ethtool_ops = &ax88772_ethtool_ops;
+
+ ax8817x_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR,
+ cpu_to_le16(BMCR_RESET));
+ ax8817x_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+ cpu_to_le16(ADVERTISE_ALL | ADVERTISE_CSMA));
+ mii_nway_restart(&dev->mii);
+
+ if ((ret = ax8817x_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, 0x0336, 0, 0, buf)) < 0) {
+ dbg("Write medium mode register: %d", ret);
+ goto out3;
+ }
+
+ 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 out3;
+ }
+ 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 out3;
+ }