* Re: [PATCH] stmmac: review Wol and enable the Unicast support
From: David Miller @ 2011-04-13 18:51 UTC (permalink / raw)
To: peppe.cavallaro; +Cc: netdev
In-Reply-To: <1302679635-7035-1-git-send-email-peppe.cavallaro@st.com>
From: Giuseppe CAVALLARO <peppe.cavallaro@st.com>
Date: Wed, 13 Apr 2011 09:27:15 +0200
> Signed-off-by: Giuseppe Cavallaro <peppe.cavallaro@st.com>
Applied, thanks.
^ permalink raw reply
* Re: [PATCHv2 NET-2.6 1/1] qlcnic: limit skb frags for non tso packet
From: David Miller @ 2011-04-13 18:53 UTC (permalink / raw)
To: amit.salecha; +Cc: netdev, ameen.rahman, anirban.chakraborty, stable
In-Reply-To: <1302663955-31849-2-git-send-email-amit.salecha@qlogic.com>
From: amit.salecha@qlogic.com
Date: Tue, 12 Apr 2011 20:05:55 -0700
> From: Amit Kumar Salecha <amit.salecha@qlogic.com>
>
> Machines are getting deadlock in four node cluster environment.
> All nodes are accessing (find /gfs2 -depth -print|cpio -ocv > /dev/null)
> 200 GB storage on a GFS2 filesystem.
> This result in memory fragmentation and driver receives 18 frags for
> 1448 byte packets.
> For non tso packet, fw drops the tx request, if it has >14 frags.
>
> Fixing it by pulling extra frags.
>
> Cc: stable@kernel.org
> Signed-off-by: Amit Kumar Salecha <amit.salecha@qlogic.com>
Applied.
But like Greg said, please kill that footer notice on your outgoing
emails when engaging in any discussion on a public mailing list.
Thanks.
^ permalink raw reply
* [PATCH] Add Qualcomm Gobi 2000/3000 driver.
From: Elly Jones @ 2011-04-13 19:00 UTC (permalink / raw)
To: netdev; +Cc: dcbw, mjg59, jglasgow, trond
From: Elizabeth Jones <ellyjones@google.com>
This is a rewrite and unification of the Qualcomm Gobi 2000 and Gobi 3000
drivers, available at:
https://www.codeaurora.org/patches/quic/gobi/VT773.Gobi2000Drivers_03022011.tar.gz
https://www.codeaurora.org/patches/quic/gobi/Gobi3000Drivers1040_03022011.tar.gz
Both devices need firmware loaded in order to be useful.
BUG=chromium-os:5521
TEST=suite_Cellular
Signed-off-by: Elizabeth Jones <ellyjones@chromium.org>
Signed-off-by: Jason Glasgow <jglasgow@chromium.org>
Signed-off-by: Olof Johansson <olofj@chromium.org>
Review URL: http://codereview.chromium.org/6539018
---
drivers/net/usb/Kconfig | 6 +
drivers/net/usb/Makefile | 1 +
drivers/net/usb/gobi/Makefile | 2 +
drivers/net/usb/gobi/README | 30 +
drivers/net/usb/gobi/qcusbnet.c | 717 +++++++++++++++++
drivers/net/usb/gobi/qcusbnet.h | 28 +
drivers/net/usb/gobi/qmi.c | 358 +++++++++
drivers/net/usb/gobi/qmi.h | 67 ++
drivers/net/usb/gobi/qmidevice.c | 1562 ++++++++++++++++++++++++++++++++++++++
drivers/net/usb/gobi/qmidevice.h | 35 +
drivers/net/usb/gobi/structs.h | 96 +++
11 files changed, 2902 insertions(+), 0 deletions(-)
create mode 100644 drivers/net/usb/gobi/Makefile
create mode 100644 drivers/net/usb/gobi/README
create mode 100644 drivers/net/usb/gobi/qcusbnet.c
create mode 100644 drivers/net/usb/gobi/qcusbnet.h
create mode 100644 drivers/net/usb/gobi/qmi.c
create mode 100644 drivers/net/usb/gobi/qmi.h
create mode 100644 drivers/net/usb/gobi/qmidevice.c
create mode 100644 drivers/net/usb/gobi/qmidevice.h
create mode 100644 drivers/net/usb/gobi/structs.h
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index 3ec22c3..3f43266 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -448,4 +448,10 @@ config USB_VL600
http://ubuntuforums.org/showpost.php?p=10589647&postcount=17
+config USB_NET_GOBI
+ tristate "Qualcomm Gobi"
+ depends on USB_USBNET
+ help
+ Qualcomm Gobi 2k/3k support.
+
endmenu
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index c7ec8a5..dd1f24c 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -28,4 +28,5 @@ obj-$(CONFIG_USB_SIERRA_NET) += sierra_net.o
obj-$(CONFIG_USB_NET_CX82310_ETH) += cx82310_eth.o
obj-$(CONFIG_USB_NET_CDC_NCM) += cdc_ncm.o
obj-$(CONFIG_USB_VL600) += lg-vl600.o
+obj-$(CONFIG_USB_NET_GOBI) += gobi/
diff --git a/drivers/net/usb/gobi/Makefile b/drivers/net/usb/gobi/Makefile
new file mode 100644
index 0000000..a6635f6
--- /dev/null
+++ b/drivers/net/usb/gobi/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_USB_NET_GOBI) += gobi.o
+gobi-objs += qcusbnet.o qmidevice.o qmi.o
diff --git a/drivers/net/usb/gobi/README b/drivers/net/usb/gobi/README
new file mode 100644
index 0000000..113c523
--- /dev/null
+++ b/drivers/net/usb/gobi/README
@@ -0,0 +1,30 @@
+Qualcomm Gobi 2000 driver
+-------------------------
+
+This directory contains a driver for the Qualcomm Gobi 2000 CDMA/GSM modem. The
+device speaks a protocol called QMI with its host; this driver's responsibility
+is to multiplex transactions over QMI connections. Note that the relatively
+simple encoding defined in qmi.h and qmi.c is only the encapsulating protocol;
+the device speaks a more complex set of protocols embodied in the closed-source
+Gobi SDK which are necessary to use advanced features of the device.
+Furthermore, firmware (also proprietary) is needed to make the device useful. An
+open-source firmware loader exists: http://www.codon.org.uk/~mjg59/gobi_loader/.
+
+Interfaces:
+This driver presents two separate interfaces to userspace:
+- usbN, an ordinary usb network interface
+- /dev/qcqmiN, a channel to speak to the device using QMI messages
+The latter is for use by the Gobi SDK.
+
+History:
+This driver's original incarnation (of which this is a rewrite) was released
+under GPLv2 by Qualcomm on their Code Aurora site:
+https://www.codeaurora.org/gitweb/quic/la/?p=kernel/msm.git;a=commit;h=b5135a880f8942f990e8c2e383f7f876beacc55b
+
+Issues:
+Help and commentary on any of the issues below would be appreciated.
+- The locking in devqmi_close() is not threadsafe: we're walking a list of tasks
+ without holding a lock on the list. The original driver did this and I have
+ left it intact because I do not fully understand what's going on here.
+- The driver seems to deadlock very occasionally (one in ~6000 reboots during
+ stress testing). I've had no luck tracking this problem down.
diff --git a/drivers/net/usb/gobi/qcusbnet.c b/drivers/net/usb/gobi/qcusbnet.c
new file mode 100644
index 0000000..954066d
--- /dev/null
+++ b/drivers/net/usb/gobi/qcusbnet.c
@@ -0,0 +1,717 @@
+/* qcusbnet.c - gobi network device
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "structs.h"
+#include "qmidevice.h"
+#include "qmi.h"
+#include "qcusbnet.h"
+
+#include <linux/ctype.h>
+
+#define DRIVER_VERSION "1.0.110+google"
+#define DRIVER_AUTHOR "Qualcomm Innovation Center"
+#define DRIVER_DESC "gobi"
+
+static LIST_HEAD(qcusbnet_list);
+static DEFINE_MUTEX(qcusbnet_lock);
+
+int qcusbnet_debug;
+static struct class *devclass;
+
+static void free_dev(struct kref *ref)
+{
+ struct qcusbnet *dev = container_of(ref, struct qcusbnet, refcount);
+ list_del(&dev->node);
+ kfree(dev);
+}
+
+void qcusbnet_put(struct qcusbnet *dev)
+{
+ mutex_lock(&qcusbnet_lock);
+ kref_put(&dev->refcount, free_dev);
+ mutex_unlock(&qcusbnet_lock);
+}
+
+struct qcusbnet *qcusbnet_get(struct qcusbnet *key)
+{
+ /* Given a putative qcusbnet struct, return either the struct itself
+ * (with a ref taken) if the struct is still visible, or NULL if it's
+ * not. This prevents object-visibility races where someone is looking
+ * up an object as the last ref gets dropped; dropping the last ref and
+ * removing the object from the list are atomic with respect to getting
+ * a new ref. */
+ struct qcusbnet *entry;
+ mutex_lock(&qcusbnet_lock);
+ list_for_each_entry(entry, &qcusbnet_list, node) {
+ if (entry == key) {
+ kref_get(&entry->refcount);
+ mutex_unlock(&qcusbnet_lock);
+ return entry;
+ }
+ }
+ mutex_unlock(&qcusbnet_lock);
+ return NULL;
+}
+
+int qc_suspend(struct usb_interface *iface, pm_message_t event)
+{
+ struct usbnet *usbnet;
+ struct qcusbnet *dev;
+
+ if (!iface)
+ return -ENOMEM;
+
+ usbnet = usb_get_intfdata(iface);
+
+ if (!usbnet || !usbnet->net) {
+ DBG("failed to get netdevice\n");
+ return -ENXIO;
+ }
+
+ dev = (struct qcusbnet *)usbnet->data[0];
+ if (!dev) {
+ DBG("failed to get QMIDevice\n");
+ return -ENXIO;
+ }
+
+ if (!(event.event & PM_EVENT_AUTO)) {
+ DBG("device suspended to power level %d\n",
+ event.event);
+ qc_setdown(dev, DOWN_DRIVER_SUSPENDED);
+ } else {
+ DBG("device autosuspend\n");
+ }
+
+ if (event.event & PM_EVENT_SUSPEND) {
+ qc_stopread(dev);
+ usbnet->udev->reset_resume = 0;
+ iface->dev.power.power_state.event = event.event;
+ } else {
+ usbnet->udev->reset_resume = 1;
+ }
+
+ return usbnet_suspend(iface, event);
+}
+
+static int qc_resume(struct usb_interface *iface)
+{
+ struct usbnet *usbnet;
+ struct qcusbnet *dev;
+ int ret;
+ int oldstate;
+
+ if (iface == 0)
+ return -ENOMEM;
+
+ usbnet = usb_get_intfdata(iface);
+
+ if (!usbnet || !usbnet->net) {
+ DBG("failed to get netdevice\n");
+ return -ENXIO;
+ }
+
+ dev = (struct qcusbnet *)usbnet->data[0];
+ if (!dev) {
+ DBG("failed to get QMIDevice\n");
+ return -ENXIO;
+ }
+
+ oldstate = iface->dev.power.power_state.event;
+ iface->dev.power.power_state.event = PM_EVENT_ON;
+ DBG("resuming from power mode %d\n", oldstate);
+
+ if (oldstate & PM_EVENT_SUSPEND) {
+ qc_cleardown(dev, DOWN_DRIVER_SUSPENDED);
+
+ ret = usbnet_resume(iface);
+ if (ret) {
+ DBG("usbnet_resume error %d\n", ret);
+ return ret;
+ }
+
+ ret = qc_startread(dev);
+ if (ret) {
+ DBG("qc_startread error %d\n", ret);
+ return ret;
+ }
+
+ complete(&dev->worker.work);
+ } else {
+ DBG("nothing to resume\n");
+ return 0;
+ }
+
+ return ret;
+}
+
+static int qcnet_bind(struct usbnet *usbnet, struct usb_interface *iface)
+{
+ int numends;
+ int i;
+ struct usb_host_endpoint *endpoint = NULL;
+ struct usb_host_endpoint *in = NULL;
+ struct usb_host_endpoint *out = NULL;
+
+ if (iface->num_altsetting != 1) {
+ DBG("invalid num_altsetting %u\n", iface->num_altsetting);
+ return -EINVAL;
+ }
+
+ if (iface->cur_altsetting->desc.bInterfaceNumber != 0
+ && iface->cur_altsetting->desc.bInterfaceNumber != 5) {
+ DBG("invalid interface %d\n",
+ iface->cur_altsetting->desc.bInterfaceNumber);
+ return -EINVAL;
+ }
+
+ numends = iface->cur_altsetting->desc.bNumEndpoints;
+ for (i = 0; i < numends; i++) {
+ endpoint = iface->cur_altsetting->endpoint + i;
+ if (!endpoint) {
+ DBG("invalid endpoint %u\n", i);
+ return -EINVAL;
+ }
+
+ if (usb_endpoint_dir_in(&endpoint->desc)
+ && !usb_endpoint_xfer_int(&endpoint->desc)) {
+ in = endpoint;
+ } else if (!usb_endpoint_dir_out(&endpoint->desc)) {
+ out = endpoint;
+ }
+ }
+
+ if (!in || !out) {
+ DBG("invalid endpoints\n");
+ return -EINVAL;
+ }
+
+ if (usb_set_interface(usbnet->udev,
+ iface->cur_altsetting->desc.bInterfaceNumber, 0)) {
+ DBG("unable to set interface\n");
+ return -EINVAL;
+ }
+
+ usbnet->in = usb_rcvbulkpipe(usbnet->udev, in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+ usbnet->out = usb_sndbulkpipe(usbnet->udev, out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+
+ DBG("in %x, out %x\n",
+ in->desc.bEndpointAddress,
+ out->desc.bEndpointAddress);
+
+ return 0;
+}
+
+static void qcnet_unbind(struct usbnet *usbnet, struct usb_interface *iface)
+{
+ struct qcusbnet *dev = (struct qcusbnet *)usbnet->data[0];
+
+ netif_carrier_off(usbnet->net);
+ qc_deregister(dev);
+
+ kfree(usbnet->net->netdev_ops);
+ usbnet->net->netdev_ops = NULL;
+ /* drop the list's ref */
+ qcusbnet_put(dev);
+}
+
+static void qcnet_urbhook(struct urb *urb)
+{
+ unsigned long flags;
+ struct worker *worker = urb->context;
+ if (!worker) {
+ DBG("bad context\n");
+ return;
+ }
+
+ if (urb->status) {
+ DBG("urb finished with error %d\n", urb->status);
+ }
+
+ spin_lock_irqsave(&worker->active_lock, flags);
+ worker->active = ERR_PTR(-EAGAIN);
+ spin_unlock_irqrestore(&worker->active_lock, flags);
+ /* XXX-fix race against qcnet_stop()? */
+ complete(&worker->work);
+ usb_free_urb(urb);
+}
+
+static void qcnet_txtimeout(struct net_device *netdev)
+{
+ struct list_head *node, *tmp;
+ struct qcusbnet *dev;
+ struct worker *worker;
+ struct urbreq *req;
+ unsigned long activeflags, listflags;
+ struct usbnet *usbnet = netdev_priv(netdev);
+
+ if (!usbnet || !usbnet->net) {
+ DBG("failed to get usbnet device\n");
+ return;
+ }
+
+ dev = (struct qcusbnet *)usbnet->data[0];
+ if (!dev) {
+ DBG("failed to get QMIDevice\n");
+ return;
+ }
+ worker = &dev->worker;
+
+ DBG("\n");
+
+ spin_lock_irqsave(&worker->active_lock, activeflags);
+ if (worker->active)
+ usb_kill_urb(worker->active);
+ spin_unlock_irqrestore(&worker->active_lock, activeflags);
+
+ spin_lock_irqsave(&worker->urbs_lock, listflags);
+ list_for_each_safe(node, tmp, &worker->urbs) {
+ req = list_entry(node, struct urbreq, node);
+ usb_free_urb(req->urb);
+ list_del(&req->node);
+ kfree(req);
+ }
+ spin_unlock_irqrestore(&worker->urbs_lock, listflags);
+
+ complete(&worker->work);
+}
+
+static int qcnet_worker(void *arg)
+{
+ struct list_head *node, *tmp;
+ unsigned long activeflags, listflags;
+ struct urbreq *req;
+ int status;
+ struct usb_device *usbdev;
+ struct worker *worker = arg;
+ if (!worker) {
+ DBG("passed null pointer\n");
+ return -EINVAL;
+ }
+
+ usbdev = interface_to_usbdev(worker->iface);
+
+ DBG("traffic thread started\n");
+
+ while (!kthread_should_stop()) {
+ wait_for_completion_interruptible(&worker->work);
+
+ if (kthread_should_stop()) {
+ spin_lock_irqsave(&worker->active_lock, activeflags);
+ if (worker->active) {
+ usb_kill_urb(worker->active);
+ }
+ spin_unlock_irqrestore(&worker->active_lock, activeflags);
+
+ spin_lock_irqsave(&worker->urbs_lock, listflags);
+ list_for_each_safe(node, tmp, &worker->urbs) {
+ req = list_entry(node, struct urbreq, node);
+ usb_free_urb(req->urb);
+ list_del(&req->node);
+ kfree(req);
+ }
+ spin_unlock_irqrestore(&worker->urbs_lock, listflags);
+
+ break;
+ }
+
+ spin_lock_irqsave(&worker->active_lock, activeflags);
+ if (IS_ERR(worker->active) && PTR_ERR(worker->active) == -EAGAIN) {
+ worker->active = NULL;
+ spin_unlock_irqrestore(&worker->active_lock, activeflags);
+ usb_autopm_put_interface(worker->iface);
+ spin_lock_irqsave(&worker->active_lock, activeflags);
+ }
+
+ if (worker->active) {
+ spin_unlock_irqrestore(&worker->active_lock, activeflags);
+ continue;
+ }
+
+ spin_lock_irqsave(&worker->urbs_lock, listflags);
+ if (list_empty(&worker->urbs)) {
+ spin_unlock_irqrestore(&worker->urbs_lock, listflags);
+ spin_unlock_irqrestore(&worker->active_lock, activeflags);
+ continue;
+ }
+
+ req = list_first_entry(&worker->urbs, struct urbreq, node);
+ list_del(&req->node);
+ spin_unlock_irqrestore(&worker->urbs_lock, listflags);
+
+ worker->active = req->urb;
+ spin_unlock_irqrestore(&worker->active_lock, activeflags);
+
+ status = usb_autopm_get_interface(worker->iface);
+ if (status < 0) {
+ DBG("unable to autoresume interface: %d\n", status);
+ if (status == -EPERM) {
+ qc_suspend(worker->iface, PMSG_SUSPEND);
+ }
+
+ spin_lock_irqsave(&worker->urbs_lock, listflags);
+ list_add(&req->node, &worker->urbs);
+ spin_unlock_irqrestore(&worker->urbs_lock, listflags);
+
+ spin_lock_irqsave(&worker->active_lock, activeflags);
+ worker->active = NULL;
+ spin_unlock_irqrestore(&worker->active_lock, activeflags);
+
+ continue;
+ }
+
+ status = usb_submit_urb(worker->active, GFP_KERNEL);
+ if (status < 0) {
+ DBG("Failed to submit URB: %d. Packet dropped\n", status);
+ spin_lock_irqsave(&worker->active_lock, activeflags);
+ usb_free_urb(worker->active);
+ worker->active = NULL;
+ spin_unlock_irqrestore(&worker->active_lock, activeflags);
+ usb_autopm_put_interface(worker->iface);
+ complete(&worker->work);
+ }
+
+ kfree(req);
+ }
+
+ DBG("traffic thread exiting\n");
+ worker->thread = NULL;
+ return 0;
+}
+
+static int qcnet_startxmit(struct sk_buff *skb, struct net_device *netdev)
+{
+ unsigned long listflags;
+ struct qcusbnet *dev;
+ struct worker *worker;
+ struct urbreq *req;
+ void *data;
+ struct usbnet *usbnet = netdev_priv(netdev);
+
+ DBG("\n");
+
+ if (!usbnet || !usbnet->net) {
+ DBG("failed to get usbnet device\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ dev = (struct qcusbnet *)usbnet->data[0];
+ if (!dev) {
+ DBG("failed to get QMIDevice\n");
+ return NETDEV_TX_BUSY;
+ }
+ worker = &dev->worker;
+
+ if (qc_isdown(dev, DOWN_DRIVER_SUSPENDED)) {
+ DBG("device is suspended\n");
+ dump_stack();
+ return NETDEV_TX_BUSY;
+ }
+
+ req = kmalloc(sizeof(*req), GFP_ATOMIC);
+ if (!req) {
+ DBG("unable to allocate URBList memory\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ req->urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+ if (!req->urb) {
+ kfree(req);
+ DBG("unable to allocate URB\n");
+ return NETDEV_TX_BUSY;
+ }
+
+ data = kmalloc(skb->len, GFP_ATOMIC);
+ if (!data) {
+ usb_free_urb(req->urb);
+ kfree(req);
+ DBG("unable to allocate URB data\n");
+ return NETDEV_TX_BUSY;
+ }
+ memcpy(data, skb->data, skb->len);
+
+ usb_fill_bulk_urb(req->urb, dev->usbnet->udev, dev->usbnet->out,
+ data, skb->len, qcnet_urbhook, worker);
+
+ spin_lock_irqsave(&worker->urbs_lock, listflags);
+ list_add_tail(&req->node, &worker->urbs);
+ spin_unlock_irqrestore(&worker->urbs_lock, listflags);
+
+ complete(&worker->work);
+
+ netdev->trans_start = jiffies;
+ dev_kfree_skb_any(skb);
+
+ return NETDEV_TX_OK;
+}
+
+static int qcnet_open(struct net_device *netdev)
+{
+ int status = 0;
+ struct qcusbnet *dev;
+ struct usbnet *usbnet = netdev_priv(netdev);
+
+ if (!usbnet) {
+ DBG("failed to get usbnet device\n");
+ return -ENXIO;
+ }
+
+ dev = (struct qcusbnet *)usbnet->data[0];
+ if (!dev) {
+ DBG("failed to get QMIDevice\n");
+ return -ENXIO;
+ }
+
+ DBG("\n");
+
+ dev->worker.iface = dev->iface;
+ INIT_LIST_HEAD(&dev->worker.urbs);
+ dev->worker.active = NULL;
+ spin_lock_init(&dev->worker.urbs_lock);
+ spin_lock_init(&dev->worker.active_lock);
+ init_completion(&dev->worker.work);
+
+ dev->worker.thread = kthread_run(qcnet_worker, &dev->worker, "qcnet_worker");
+ if (IS_ERR(dev->worker.thread)) {
+ DBG("AutoPM thread creation error\n");
+ return PTR_ERR(dev->worker.thread);
+ }
+
+ qc_cleardown(dev, DOWN_NET_IFACE_STOPPED);
+ if (dev->open) {
+ status = dev->open(netdev);
+ if (status == 0) {
+ usb_autopm_put_interface(dev->iface);
+ }
+ } else {
+ DBG("no USBNetOpen defined\n");
+ }
+
+ return status;
+}
+
+int qcnet_stop(struct net_device *netdev)
+{
+ struct qcusbnet *dev;
+ struct usbnet *usbnet = netdev_priv(netdev);
+
+ if (!usbnet || !usbnet->net) {
+ DBG("failed to get netdevice\n");
+ return -ENXIO;
+ }
+
+ dev = (struct qcusbnet *)usbnet->data[0];
+ if (!dev) {
+ DBG("failed to get QMIDevice\n");
+ return -ENXIO;
+ }
+
+ qc_setdown(dev, DOWN_NET_IFACE_STOPPED);
+ complete(&dev->worker.work);
+ kthread_stop(dev->worker.thread);
+ DBG("thread stopped\n");
+
+ if (dev->stop != NULL)
+ return dev->stop(netdev);
+ return 0;
+}
+
+static const struct driver_info qc_netinfo = {
+ .description = "QCUSBNet Ethernet Device",
+ .flags = FLAG_ETHER,
+ .bind = qcnet_bind,
+ .unbind = qcnet_unbind,
+ .data = 0,
+};
+
+#define MKVIDPID(v, p) \
+{ \
+ USB_DEVICE(v, p), \
+ .driver_info = (unsigned long)&qc_netinfo, \
+}
+
+static const struct usb_device_id qc_vidpids[] = {
+ MKVIDPID(0x05c6, 0x9215), /* Acer Gobi 2000 */
+ MKVIDPID(0x05c6, 0x9265), /* Asus Gobi 2000 */
+ MKVIDPID(0x16d8, 0x8002), /* CMOTech Gobi 2000 */
+ MKVIDPID(0x413c, 0x8186), /* Dell Gobi 2000 */
+ MKVIDPID(0x1410, 0xa010), /* Entourage Gobi 2000 */
+ MKVIDPID(0x1410, 0xa011), /* Entourage Gobi 2000 */
+ MKVIDPID(0x1410, 0xa012), /* Entourage Gobi 2000 */
+ MKVIDPID(0x1410, 0xa013), /* Entourage Gobi 2000 */
+ MKVIDPID(0x03f0, 0x251d), /* HP Gobi 2000 */
+ MKVIDPID(0x05c6, 0x9205), /* Lenovo Gobi 2000 */
+ MKVIDPID(0x05c6, 0x920b), /* Generic Gobi 2000 */
+ MKVIDPID(0x04da, 0x250f), /* Panasonic Gobi 2000 */
+ MKVIDPID(0x05c6, 0x9245), /* Samsung Gobi 2000 */
+ MKVIDPID(0x1199, 0x9001), /* Sierra Wireless Gobi 2000 */
+ MKVIDPID(0x1199, 0x9002), /* Sierra Wireless Gobi 2000 */
+ MKVIDPID(0x1199, 0x9003), /* Sierra Wireless Gobi 2000 */
+ MKVIDPID(0x1199, 0x9004), /* Sierra Wireless Gobi 2000 */
+ MKVIDPID(0x1199, 0x9005), /* Sierra Wireless Gobi 2000 */
+ MKVIDPID(0x1199, 0x9006), /* Sierra Wireless Gobi 2000 */
+ MKVIDPID(0x1199, 0x9007), /* Sierra Wireless Gobi 2000 */
+ MKVIDPID(0x1199, 0x9008), /* Sierra Wireless Gobi 2000 */
+ MKVIDPID(0x1199, 0x9009), /* Sierra Wireless Gobi 2000 */
+ MKVIDPID(0x1199, 0x900a), /* Sierra Wireless Gobi 2000 */
+ MKVIDPID(0x05c6, 0x9225), /* Sony Gobi 2000 */
+ MKVIDPID(0x05c6, 0x9235), /* Top Global Gobi 2000 */
+ MKVIDPID(0x05c6, 0x9275), /* iRex Technologies Gobi 2000 */
+
+ MKVIDPID(0x05c6, 0x920d), /* Qualcomm Gobi 3000 */
+ MKVIDPID(0x1410, 0xa021), /* Novatel Gobi 3000 */
+ { }
+};
+
+MODULE_DEVICE_TABLE(usb, qc_vidpids);
+
+static u8 nibble(unsigned char c)
+{
+ if (likely(isdigit(c)))
+ return c - '0';
+ c = toupper(c);
+ if (likely(isxdigit(c)))
+ return 10 + c - 'A';
+ return 0;
+}
+
+int qcnet_probe(struct usb_interface *iface, const struct usb_device_id *vidpids)
+{
+ int status;
+ struct usbnet *usbnet;
+ struct qcusbnet *dev;
+ struct net_device_ops *netdevops;
+ int i;
+ u8 *addr;
+
+ status = usbnet_probe(iface, vidpids);
+ if (status < 0) {
+ DBG("usbnet_probe failed %d\n", status);
+ return status;
+ }
+
+ usbnet = usb_get_intfdata(iface);
+
+ if (!usbnet || !usbnet->net) {
+ DBG("failed to get netdevice\n");
+ return -ENXIO;
+ }
+
+ dev = kmalloc(sizeof(struct qcusbnet), GFP_KERNEL);
+ if (!dev) {
+ DBG("failed to allocate device buffers\n");
+ return -ENOMEM;
+ }
+
+ usbnet->data[0] = (unsigned long)dev;
+
+ dev->usbnet = usbnet;
+
+ netdevops = kmalloc(sizeof(struct net_device_ops), GFP_KERNEL);
+ if (!netdevops) {
+ DBG("failed to allocate net device ops\n");
+ return -ENOMEM;
+ }
+ memcpy(netdevops, usbnet->net->netdev_ops, sizeof(struct net_device_ops));
+
+ dev->open = netdevops->ndo_open;
+ netdevops->ndo_open = qcnet_open;
+ dev->stop = netdevops->ndo_stop;
+ netdevops->ndo_stop = qcnet_stop;
+ netdevops->ndo_start_xmit = qcnet_startxmit;
+ netdevops->ndo_tx_timeout = qcnet_txtimeout;
+
+ usbnet->net->netdev_ops = netdevops;
+
+ memset(&(dev->usbnet->net->stats), 0, sizeof(struct net_device_stats));
+
+ dev->iface = iface;
+ memset(&(dev->meid), '0', 14);
+
+ dev->valid = false;
+ memset(&dev->qmi, 0, sizeof(dev->qmi));
+
+ dev->qmi.devclass = devclass;
+
+ kref_init(&dev->refcount);
+ INIT_LIST_HEAD(&dev->node);
+ INIT_LIST_HEAD(&dev->qmi.clients);
+ init_completion(&dev->worker.work);
+ spin_lock_init(&dev->qmi.clients_lock);
+
+ dev->down = 0;
+ qc_setdown(dev, DOWN_NO_NDIS_CONNECTION);
+ qc_setdown(dev, DOWN_NET_IFACE_STOPPED);
+
+ status = qc_register(dev);
+ if (status) {
+ qc_deregister(dev);
+ } else {
+ mutex_lock(&qcusbnet_lock);
+ /* Give our initial ref to the list */
+ list_add(&dev->node, &qcusbnet_list);
+ mutex_unlock(&qcusbnet_lock);
+ }
+ /* After calling qc_register, MEID is valid */
+ addr = &usbnet->net->dev_addr[0];
+ for (i = 0; i < 6; i++)
+ addr[i] = (nibble(dev->meid[i*2+2]) << 4)+
+ nibble(dev->meid[i*2+3]);
+ addr[0] &= 0xfe; /* clear multicast bit */
+ addr[0] |= 0x02; /* set local assignment bit (IEEE802) */
+
+ return status;
+}
+EXPORT_SYMBOL_GPL(qcnet_probe);
+
+static struct usb_driver qcusbnet = {
+ .name = "gobi",
+ .id_table = qc_vidpids,
+ .probe = qcnet_probe,
+ .disconnect = usbnet_disconnect,
+ .suspend = qc_suspend,
+ .resume = qc_resume,
+ .supports_autosuspend = true,
+};
+
+static int __init modinit(void)
+{
+ devclass = class_create(THIS_MODULE, "QCQMI");
+ if (IS_ERR(devclass)) {
+ DBG("error at class_create %ld\n", PTR_ERR(devclass));
+ return -ENOMEM;
+ }
+ printk(KERN_INFO "%s: %s\n", DRIVER_DESC, DRIVER_VERSION);
+ return usb_register(&qcusbnet);
+}
+module_init(modinit);
+
+static void __exit modexit(void)
+{
+ usb_deregister(&qcusbnet);
+ class_destroy(devclass);
+}
+module_exit(modexit);
+
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("Dual BSD/GPL");
+
+module_param(qcusbnet_debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(qcusbnet_debug, "Debugging enabled or not");
diff --git a/drivers/net/usb/gobi/qcusbnet.h b/drivers/net/usb/gobi/qcusbnet.h
new file mode 100644
index 0000000..00c3a7e
--- /dev/null
+++ b/drivers/net/usb/gobi/qcusbnet.h
@@ -0,0 +1,28 @@
+/* qcusbnet.h - gobi network device header
+ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef QCUSBNET_QCUSBNET_H
+#define QCUSBNET_QCUSBNET_H
+
+#include "structs.h"
+
+extern int qc_suspend(struct usb_interface *iface, pm_message_t event);
+extern void qcusbnet_put(struct qcusbnet *dev);
+extern struct qcusbnet *qcusbnet_get(struct qcusbnet *dev);
+
+#endif /* !QCUSBNET_QCUSBNET_H */
diff --git a/drivers/net/usb/gobi/qmi.c b/drivers/net/usb/gobi/qmi.c
new file mode 100644
index 0000000..cdbdbaf
--- /dev/null
+++ b/drivers/net/usb/gobi/qmi.c
@@ -0,0 +1,358 @@
+/* qmi.c - QMI protocol implementation
+ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "qmi.h"
+
+#include <linux/slab.h>
+
+struct qmux {
+ u8 tf; /* always 1 */
+ u16 len;
+ u8 ctrl;
+ u8 service;
+ u8 qmicid;
+} __attribute__((__packed__));
+
+struct getcid_req {
+ struct qmux header;
+ u8 req;
+ u8 tid;
+ u16 msgid;
+ u16 tlvsize;
+ u8 service;
+ u16 size;
+ u8 qmisvc;
+} __attribute__((__packed__));
+
+struct releasecid_req {
+ struct qmux header;
+ u8 req;
+ u8 tid;
+ u16 msgid;
+ u16 tlvsize;
+ u8 rlscid;
+ u16 size;
+ u16 cid;
+} __attribute__((__packed__));
+
+struct ready_req {
+ struct qmux header;
+ u8 req;
+ u8 tid;
+ u16 msgid;
+ u16 tlvsize;
+} __attribute__((__packed__));
+
+struct seteventreport_req {
+ struct qmux header;
+ u8 req;
+ u16 tid;
+ u16 msgid;
+ u16 tlvsize;
+ u8 reportchanrate;
+ u16 size;
+ u8 period;
+ u32 mask;
+} __attribute__((__packed__));
+
+struct getpkgsrvcstatus_req {
+ struct qmux header;
+ u8 req;
+ u16 tid;
+ u16 msgid;
+ u16 tlvsize;
+} __attribute__((__packed__));
+
+struct getmeid_req {
+ struct qmux header;
+ u8 req;
+ u16 tid;
+ u16 msgid;
+ u16 tlvsize;
+} __attribute__((__packed__));
+
+const size_t qmux_size = sizeof(struct qmux);
+
+void *qmictl_new_getcid(u8 tid, u8 svctype, size_t *size)
+{
+ struct getcid_req *req = kmalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return NULL;
+ req->req = 0x00;
+ req->tid = tid;
+ req->msgid = 0x0022;
+ req->tlvsize = 0x0004;
+ req->service = 0x01;
+ req->size = 0x0001;
+ req->qmisvc = svctype;
+ *size = sizeof(*req);
+ return req;
+}
+
+void *qmictl_new_releasecid(u8 tid, u16 cid, size_t *size)
+{
+ struct releasecid_req *req = kmalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return NULL;
+ req->req = 0x00;
+ req->tid = tid;
+ req->msgid = 0x0023;
+ req->tlvsize = 0x05;
+ req->rlscid = 0x01;
+ req->size = 0x0002;
+ req->cid = cid;
+ *size = sizeof(*req);
+ return req;
+}
+
+void *qmictl_new_ready(u8 tid, size_t *size)
+{
+ struct ready_req *req = kmalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return NULL;
+ req->req = 0x00;
+ req->tid = tid;
+ req->msgid = 0x21;
+ req->tlvsize = 0;
+ *size = sizeof(*req);
+ return req;
+}
+
+void *qmiwds_new_seteventreport(u8 tid, size_t *size)
+{
+ struct seteventreport_req *req = kmalloc(sizeof(*req), GFP_KERNEL);
+ req->req = 0x00;
+ req->tid = tid;
+ req->msgid = 0x0001;
+ req->tlvsize = 0x0008;
+ req->reportchanrate = 0x11;
+ req->size = 0x0005;
+ req->period = 0x01;
+ req->mask = 0x000000ff;
+ *size = sizeof(*req);
+ return req;
+}
+
+void *qmiwds_new_getpkgsrvcstatus(u8 tid, size_t *size)
+{
+ struct getpkgsrvcstatus_req *req = kmalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return NULL;
+ req->req = 0x00;
+ req->tid = tid;
+ req->msgid = 0x22;
+ req->tlvsize = 0x0000;
+ *size = sizeof(*req);
+ return req;
+}
+
+void *qmidms_new_getmeid(u8 tid, size_t *size)
+{
+ struct getmeid_req *req = kmalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return NULL;
+ req->req = 0x00;
+ req->tid = tid;
+ req->msgid = 0x25;
+ req->tlvsize = 0x0000;
+ *size = sizeof(*req);
+ return req;
+}
+
+int qmux_parse(u16 *cid, void *buf, size_t size)
+{
+ struct qmux *qmux = buf;
+
+ if (!buf || size < 12)
+ return -ENOMEM;
+
+ if (qmux->tf != 1 || qmux->len != size - 1 || qmux->ctrl != 0x80)
+ return -EINVAL;
+
+ *cid = (qmux->qmicid << 8) + qmux->service;
+ return sizeof(*qmux);
+}
+
+int qmux_fill(u16 cid, void *buf, size_t size)
+{
+ struct qmux *qmux = buf;
+
+ if (!buf || size < sizeof(*qmux))
+ return -ENOMEM;
+
+ qmux->tf = 1;
+ qmux->len = size - 1;
+ qmux->ctrl = 0;
+ qmux->service = cid & 0xff;
+ qmux->qmicid = cid >> 8;
+ return 0;
+}
+
+static u16 tlv_get(void *msg, u16 msgsize, u8 type, void *buf, u16 bufsize)
+{
+ u16 pos;
+ u16 msize = 0;
+
+ if (!msg || !buf)
+ return -ENOMEM;
+
+ for (pos = 4; pos + 3 < msgsize; pos += msize + 3) {
+ msize = *(u16 *)(msg + pos + 1);
+ if (*(u8 *)(msg + pos) == type) {
+ if (bufsize < msize)
+ return -ENOMEM;
+
+ memcpy(buf, msg + pos + 3, msize);
+ return msize;
+ }
+ }
+
+ return -ENOMSG;
+}
+
+int qmi_msgisvalid(void *msg, u16 size)
+{
+ char tlv[4];
+
+ if (tlv_get(msg, size, 2, &tlv[0], 4) == 4) {
+ if (*(u16 *)&tlv[0] != 0)
+ return *(u16 *)&tlv[2];
+ else
+ return 0;
+ }
+ return -ENOMSG;
+}
+
+int qmi_msgid(void *msg, u16 size)
+{
+ return size < 2 ? -ENODATA : *(u16 *)msg;
+}
+
+int qmictl_alloccid_resp(void *buf, u16 size, u16 *cid)
+{
+ int result;
+ u8 offset = sizeof(struct qmux) + 2;
+
+ if (!buf || size < offset)
+ return -ENOMEM;
+
+ buf = buf + offset;
+ size -= offset;
+
+ result = qmi_msgid(buf, size);
+ if (result != 0x22)
+ return -EFAULT;
+
+ result = qmi_msgisvalid(buf, size);
+ if (result != 0)
+ return -EFAULT;
+
+ result = tlv_get(buf, size, 0x01, cid, 2);
+ if (result != 2)
+ return -EFAULT;
+
+ return 0;
+}
+
+int qmictl_freecid_resp(void *buf, u16 size)
+{
+ int result;
+ u8 offset = sizeof(struct qmux) + 2;
+
+ if (!buf || size < offset)
+ return -ENOMEM;
+
+ buf = buf + offset;
+ size -= offset;
+
+ result = qmi_msgid(buf, size);
+ if (result != 0x23)
+ return -EFAULT;
+
+ result = qmi_msgisvalid(buf, size);
+ if (result != 0)
+ return -EFAULT;
+
+ return 0;
+}
+
+int qmiwds_event_resp(void *buf, u16 size, struct qmiwds_stats *stats)
+{
+ int result;
+ u8 status[2];
+
+ u8 offset = sizeof(struct qmux) + 3;
+
+ if (!buf || size < offset || !stats)
+ return -ENOMEM;
+
+ buf = buf + offset;
+ size -= offset;
+
+ result = qmi_msgid(buf, size);
+ if (result == 0x01) {
+ tlv_get(buf, size, 0x10, &stats->txok, 4);
+ tlv_get(buf, size, 0x11, &stats->rxok, 4);
+ tlv_get(buf, size, 0x12, &stats->txerr, 4);
+ tlv_get(buf, size, 0x13, &stats->rxerr, 4);
+ tlv_get(buf, size, 0x14, &stats->txofl, 4);
+ tlv_get(buf, size, 0x15, &stats->rxofl, 4);
+ tlv_get(buf, size, 0x19, &stats->txbytesok, 8);
+ tlv_get(buf, size, 0x1A, &stats->rxbytesok, 8);
+ } else if (result == 0x22) {
+ result = tlv_get(buf, size, 0x01, &status[0], 2);
+ if (result >= 1)
+ stats->linkstate = status[0] == 0x02;
+ if (result == 2)
+ stats->reconfigure = status[1] == 0x01;
+
+ if (result < 0)
+ return result;
+ } else {
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+int qmidms_meid_resp(void *buf, u16 size, char *meid, int meidsize)
+{
+ int result;
+
+ u8 offset = sizeof(struct qmux) + 3;
+
+ if (!buf || size < offset || meidsize < 14)
+ return -ENOMEM;
+
+ buf = buf + offset;
+ size -= offset;
+
+ result = qmi_msgid(buf, size);
+ if (result != 0x25)
+ return -EFAULT;
+
+ result = qmi_msgisvalid(buf, size);
+ if (result)
+ return -EFAULT;
+
+ result = tlv_get(buf, size, 0x12, meid, 14);
+ if (result != 14)
+ return -EFAULT;
+
+ return 0;
+}
diff --git a/drivers/net/usb/gobi/qmi.h b/drivers/net/usb/gobi/qmi.h
new file mode 100644
index 0000000..7954790
--- /dev/null
+++ b/drivers/net/usb/gobi/qmi.h
@@ -0,0 +1,67 @@
+/* qmi.h - QMI protocol header
+ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef QCUSBNET_QMI_H
+#define QCUSBNET_QMI_H
+
+#include <linux/types.h>
+
+#define QMICTL 0
+#define QMIWDS 1
+#define QMIDMS 2
+
+#define true 1
+#define false 0
+
+#define ENOMEM 12
+#define EFAULT 14
+#define EINVAL 22
+#define ENOMSG 42
+#define ENODATA 61
+
+int qmux_parse(u16 *cid, void *buf, size_t size);
+int qmux_fill(u16 cid, void *buf, size_t size);
+
+extern const size_t qmux_size;
+
+void *qmictl_new_getcid(u8 tid, u8 svctype, size_t *size);
+void *qmictl_new_releasecid(u8 tid, u16 cid, size_t *size);
+void *qmictl_new_ready(u8 tid, size_t *size);
+void *qmiwds_new_seteventreport(u8 tid, size_t *size);
+void *qmiwds_new_getpkgsrvcstatus(u8 tid, size_t *size);
+void *qmidms_new_getmeid(u8 tid, size_t *size);
+
+struct qmiwds_stats {
+ u32 txok;
+ u32 rxok;
+ u32 txerr;
+ u32 rxerr;
+ u32 txofl;
+ u32 rxofl;
+ u64 txbytesok;
+ u64 rxbytesok;
+ bool linkstate;
+ bool reconfigure;
+};
+
+int qmictl_alloccid_resp(void *buf, u16 size, u16 *cid);
+int qmictl_freecid_resp(void *buf, u16 size);
+int qmiwds_event_resp(void *buf, u16 size, struct qmiwds_stats *stats);
+int qmidms_meid_resp(void *buf, u16 size, char *meid, int meidsize);
+
+#endif /* !QCUSBNET_QMI_H */
diff --git a/drivers/net/usb/gobi/qmidevice.c b/drivers/net/usb/gobi/qmidevice.c
new file mode 100644
index 0000000..be61dda
--- /dev/null
+++ b/drivers/net/usb/gobi/qmidevice.c
@@ -0,0 +1,1562 @@
+/* qmidevice.c - gobi QMI device
+ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "qmidevice.h"
+#include "qcusbnet.h"
+
+struct readreq {
+ struct list_head node;
+ void *data;
+ u16 tid;
+ u16 size;
+};
+
+struct notifyreq {
+ struct list_head node;
+ void (*func)(struct qcusbnet *, u16, void *);
+ u16 tid;
+ void *data;
+};
+
+struct client {
+ struct list_head node;
+ u16 cid;
+ struct list_head reads;
+ struct list_head notifies;
+ struct list_head urbs;
+};
+
+struct urbsetup {
+ u8 type;
+ u8 code;
+ u16 value;
+ u16 index;
+ u16 len;
+};
+
+struct qmihandle {
+ u16 cid;
+ struct qcusbnet *dev;
+};
+
+extern int qcusbnet_debug;
+static int qcusbnet2k_fwdelay;
+
+static bool device_valid(struct qcusbnet *dev);
+static struct client *client_bycid(struct qcusbnet *dev, u16 cid);
+static bool client_addread(struct qcusbnet *dev, u16 cid, u16 tid, void *data, u16 size);
+static bool client_delread(struct qcusbnet *dev, u16 cid, u16 tid, void **data, u16 *size);
+static bool client_addnotify(struct qcusbnet *dev, u16 cid, u16 tid,
+ void (*hook)(struct qcusbnet *, u16 cid, void *),
+ void *data);
+static bool client_notify(struct qcusbnet *dev, u16 cid, u16 tid);
+static bool client_addurb(struct qcusbnet *dev, u16 cid, struct urb *urb);
+static struct urb *client_delurb(struct qcusbnet *dev, u16 cid);
+
+static int resubmit_int_urb(struct urb *urb);
+
+static int devqmi_open(struct inode *inode, struct file *file);
+static int devqmi_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+static int devqmi_release(struct inode *inode, struct file *file);
+static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size,
+ loff_t *pos);
+static ssize_t devqmi_write(struct file *file, const char __user *buf,
+ size_t size, loff_t *pos);
+
+static bool qmi_ready(struct qcusbnet *dev, u16 timeout);
+static void wds_callback(struct qcusbnet *dev, u16 cid, void *data);
+static int setup_wds_callback(struct qcusbnet *dev);
+static int qmidms_getmeid(struct qcusbnet *dev);
+
+#define IOCTL_QMI_GET_SERVICE_FILE (0x8BE0 + 1)
+#define IOCTL_QMI_GET_DEVICE_VIDPID (0x8BE0 + 2)
+#define IOCTL_QMI_GET_DEVICE_MEID (0x8BE0 + 3)
+#define IOCTL_QMI_CLOSE (0x8BE0 + 4)
+#define CDC_GET_ENCAPSULATED_RESPONSE 0x01A1ll
+#define CDC_CONNECTION_SPEED_CHANGE 0x08000000002AA1ll
+
+static const struct file_operations devqmi_fops = {
+ .owner = THIS_MODULE,
+ .read = devqmi_read,
+ .write = devqmi_write,
+ .ioctl = devqmi_ioctl,
+ .open = devqmi_open,
+ .release = devqmi_release,
+};
+
+#ifdef CONFIG_SMP
+static inline void assert_locked(struct qcusbnet *dev)
+{
+ BUG_ON(!spin_is_locked(&dev->qmi.clients_lock));
+}
+#else
+static inline void assert_locked(struct qcusbnet *dev)
+{
+
+}
+#endif
+
+static bool device_valid(struct qcusbnet *dev)
+{
+ return dev && dev->valid;
+}
+
+void qc_setdown(struct qcusbnet *dev, u8 reason)
+{
+ set_bit(reason, &dev->down);
+ netif_carrier_off(dev->usbnet->net);
+}
+
+void qc_cleardown(struct qcusbnet *dev, u8 reason)
+{
+ clear_bit(reason, &dev->down);
+ if (!dev->down)
+ netif_carrier_on(dev->usbnet->net);
+}
+
+bool qc_isdown(struct qcusbnet *dev, u8 reason)
+{
+ return test_bit(reason, &dev->down);
+}
+
+static int resubmit_int_urb(struct urb *urb)
+{
+ int status;
+ int interval;
+ if (!urb || !urb->dev)
+ return -EINVAL;
+ interval = urb->dev->speed == USB_SPEED_HIGH ? 7 : 3;
+ usb_fill_int_urb(urb, urb->dev, urb->pipe, urb->transfer_buffer,
+ urb->transfer_buffer_length, urb->complete,
+ urb->context, interval);
+ status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (status)
+ DBG("status %d", status);
+ return status;
+}
+
+static void read_callback(struct urb *urb)
+{
+ struct list_head *node;
+ int result;
+ u16 cid;
+ struct client *client;
+ void *data;
+ void *copy;
+ u16 size;
+ struct qcusbnet *dev;
+ unsigned long flags;
+ u16 tid;
+
+ if (!urb) {
+ DBG("bad read URB\n");
+ return;
+ }
+
+ dev = urb->context;
+ if (!device_valid(dev)) {
+ DBG("Invalid device!\n");
+ return;
+ }
+
+ if (urb->status) {
+ DBG("Read status = %d\n", urb->status);
+ resubmit_int_urb(dev->qmi.inturb);
+ return;
+ }
+
+ DBG("Read %d bytes\n", urb->actual_length);
+
+ data = urb->transfer_buffer;
+ size = urb->actual_length;
+
+ if (qcusbnet_debug)
+ print_hex_dump(KERN_INFO, "gobi-read: ", DUMP_PREFIX_OFFSET,
+ 16, 1, data, size, true);
+
+ result = qmux_parse(&cid, data, size);
+ if (result < 0) {
+ DBG("Read error parsing QMUX %d\n", result);
+ resubmit_int_urb(dev->qmi.inturb);
+ return;
+ }
+
+ if (size < result + 3) {
+ DBG("Data buffer too small to parse\n");
+ resubmit_int_urb(dev->qmi.inturb);
+ return;
+ }
+
+ if (cid == QMICTL)
+ tid = *(u8 *)(data + result + 1);
+ else
+ tid = *(u16 *)(data + result + 1);
+ spin_lock_irqsave(&dev->qmi.clients_lock, flags);
+
+ list_for_each(node, &dev->qmi.clients) {
+ client = list_entry(node, struct client, node);
+ if (client->cid == cid || (client->cid | 0xff00) == cid) {
+ copy = kmalloc(size, GFP_ATOMIC);
+ memcpy(copy, data, size);
+ if (!client_addread(dev, client->cid, tid, copy, size)) {
+ DBG("Error allocating pReadMemListEntry "
+ "read will be discarded\n");
+ kfree(copy);
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ resubmit_int_urb(dev->qmi.inturb);
+ return;
+ }
+
+ DBG("Creating new readListEntry for client 0x%04X, TID %x\n",
+ cid, tid);
+
+ client_notify(dev, client->cid, tid);
+
+ if (cid >> 8 != 0xff)
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ resubmit_int_urb(dev->qmi.inturb);
+}
+
+static void int_callback(struct urb *urb)
+{
+ int status;
+ int interval;
+ struct qcusbnet *dev = (struct qcusbnet *)urb->context;
+
+ if (!device_valid(dev)) {
+ DBG("Invalid device!\n");
+ return;
+ }
+
+ if (urb->status) {
+ DBG("Int status = %d\n", urb->status);
+ if (urb->status != -EOVERFLOW)
+ return;
+ } else {
+ if ((urb->actual_length == 8) &&
+ (*(u64 *)urb->transfer_buffer == CDC_GET_ENCAPSULATED_RESPONSE)) {
+ usb_fill_control_urb(dev->qmi.readurb, dev->usbnet->udev,
+ usb_rcvctrlpipe(dev->usbnet->udev, 0),
+ (unsigned char *)dev->qmi.readsetup,
+ dev->qmi.readbuf,
+ DEFAULT_READ_URB_LENGTH,
+ read_callback, dev);
+ status = usb_submit_urb(dev->qmi.readurb, GFP_ATOMIC);
+ if (status) {
+ DBG("Error submitting Read URB %d\n", status);
+ return;
+ }
+ } else if ((urb->actual_length == 16) &&
+ (*(u64 *)urb->transfer_buffer == CDC_CONNECTION_SPEED_CHANGE)) {
+ /* if upstream or downstream is 0, stop traffic.
+ * Otherwise resume it */
+ if ((*(u32 *)(urb->transfer_buffer + 8) == 0) ||
+ (*(u32 *)(urb->transfer_buffer + 12) == 0)) {
+ qc_setdown(dev, DOWN_CDC_CONNECTION_SPEED);
+ DBG("traffic stopping due to CONNECTION_SPEED_CHANGE\n");
+ } else {
+ qc_cleardown(dev, DOWN_CDC_CONNECTION_SPEED);
+ DBG("resuming traffic due to CONNECTION_SPEED_CHANGE\n");
+ }
+ } else {
+ DBG("ignoring invalid interrupt in packet\n");
+ if (qcusbnet_debug)
+ print_hex_dump(KERN_INFO, "gobi-int: ",
+ DUMP_PREFIX_OFFSET, 16, 1,
+ urb->transfer_buffer,
+ urb->actual_length, true);
+ }
+ }
+
+ resubmit_int_urb(dev->qmi.inturb);
+ return;
+}
+
+int qc_startread(struct qcusbnet *dev)
+{
+ int interval;
+
+ if (!device_valid(dev)) {
+ DBG("Invalid device!\n");
+ return -ENXIO;
+ }
+
+ dev->qmi.readurb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->qmi.readurb) {
+ DBG("Error allocating read urb\n");
+ return -ENOMEM;
+ }
+
+ dev->qmi.inturb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->qmi.inturb) {
+ usb_free_urb(dev->qmi.readurb);
+ DBG("Error allocating int urb\n");
+ return -ENOMEM;
+ }
+
+ dev->qmi.readbuf = kmalloc(DEFAULT_READ_URB_LENGTH, GFP_KERNEL);
+ if (!dev->qmi.readbuf) {
+ usb_free_urb(dev->qmi.readurb);
+ usb_free_urb(dev->qmi.inturb);
+ DBG("Error allocating read buffer\n");
+ return -ENOMEM;
+ }
+
+ dev->qmi.intbuf = kmalloc(DEFAULT_READ_URB_LENGTH, GFP_KERNEL);
+ if (!dev->qmi.intbuf) {
+ usb_free_urb(dev->qmi.readurb);
+ usb_free_urb(dev->qmi.inturb);
+ kfree(dev->qmi.readbuf);
+ DBG("Error allocating int buffer\n");
+ return -ENOMEM;
+ }
+
+ dev->qmi.readsetup = kmalloc(sizeof(*dev->qmi.readsetup), GFP_KERNEL);
+ if (!dev->qmi.readsetup) {
+ usb_free_urb(dev->qmi.readurb);
+ usb_free_urb(dev->qmi.inturb);
+ kfree(dev->qmi.readbuf);
+ kfree(dev->qmi.intbuf);
+ DBG("Error allocating setup packet buffer\n");
+ return -ENOMEM;
+ }
+
+ dev->qmi.readsetup->type = 0xA1;
+ dev->qmi.readsetup->code = 1;
+ dev->qmi.readsetup->value = 0;
+ dev->qmi.readsetup->index = 0;
+ dev->qmi.readsetup->len = DEFAULT_READ_URB_LENGTH;
+
+ interval = (dev->usbnet->udev->speed == USB_SPEED_HIGH) ? 7 : 3;
+
+ usb_fill_int_urb(dev->qmi.inturb, dev->usbnet->udev,
+ usb_rcvintpipe(dev->usbnet->udev, 0x81),
+ dev->qmi.intbuf, DEFAULT_READ_URB_LENGTH,
+ int_callback, dev, interval);
+ return usb_submit_urb(dev->qmi.inturb, GFP_KERNEL);
+}
+
+void qc_stopread(struct qcusbnet *dev)
+{
+ if (dev->qmi.readurb) {
+ DBG("Killing read URB\n");
+ usb_kill_urb(dev->qmi.readurb);
+ }
+
+ if (dev->qmi.inturb) {
+ DBG("Killing int URB\n");
+ usb_kill_urb(dev->qmi.inturb);
+ }
+
+ kfree(dev->qmi.readsetup);
+ dev->qmi.readsetup = NULL;
+ kfree(dev->qmi.readbuf);
+ dev->qmi.readbuf = NULL;
+ kfree(dev->qmi.intbuf);
+ dev->qmi.intbuf = NULL;
+
+ usb_free_urb(dev->qmi.readurb);
+ dev->qmi.readurb = NULL;
+ usb_free_urb(dev->qmi.inturb);
+ dev->qmi.inturb = NULL;
+}
+
+static int read_async(struct qcusbnet *dev, u16 cid, u16 tid,
+ void (*hook)(struct qcusbnet *, u16, void *),
+ void *data)
+{
+ struct list_head *node;
+ struct client *client;
+ struct readreq *readreq;
+
+ unsigned long flags;
+
+ if (!device_valid(dev)) {
+ DBG("Invalid device!\n");
+ return -ENXIO;
+ }
+
+ spin_lock_irqsave(&dev->qmi.clients_lock, flags);
+
+ client = client_bycid(dev, cid);
+ if (!client) {
+ DBG("Could not find matching client ID 0x%04X\n", cid);
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ return -ENXIO;
+ }
+
+ list_for_each(node, &client->reads) {
+ readreq = list_entry(node, struct readreq, node);
+ if (!tid || tid == readreq->tid) {
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ hook(dev, cid, data);
+ return 0;
+ }
+ }
+
+ if (!client_addnotify(dev, cid, tid, hook, data))
+ DBG("Unable to register for notification\n");
+
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ return 0;
+}
+
+static void upsem(struct qcusbnet *dev, u16 cid, void *data)
+{
+ DBG("0x%04X\n", cid);
+ up((struct semaphore *)data);
+}
+
+static int read_sync(struct qcusbnet *dev, void **buf, u16 cid, u16 tid)
+{
+ struct list_head *node;
+ int result;
+ struct client *client;
+ struct notifyreq *notify;
+ struct semaphore sem;
+ void *data;
+ unsigned long flags;
+ u16 size;
+
+ if (!device_valid(dev)) {
+ DBG("Invalid device!\n");
+ return -ENXIO;
+ }
+
+ spin_lock_irqsave(&dev->qmi.clients_lock, flags);
+
+ client = client_bycid(dev, cid);
+ if (!client) {
+ DBG("Could not find matching client ID 0x%04X\n", cid);
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ return -ENXIO;
+ }
+
+ while (!client_delread(dev, cid, tid, &data, &size)) {
+ sema_init(&sem, 0);
+ if (!client_addnotify(dev, cid, tid, upsem, &sem)) {
+ DBG("unable to register for notification\n");
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ return -EFAULT;
+ }
+
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+
+ result = down_interruptible(&sem);
+ if (result) {
+ DBG("Interrupted %d\n", result);
+ spin_lock_irqsave(&dev->qmi.clients_lock, flags);
+ list_for_each(node, &client->notifies) {
+ notify = list_entry(node, struct notifyreq, node);
+ if (notify->data == &sem) {
+ list_del(¬ify->node);
+ kfree(notify);
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ return -EINTR;
+ }
+
+ if (!device_valid(dev)) {
+ DBG("Invalid device!\n");
+ return -ENXIO;
+ }
+
+ spin_lock_irqsave(&dev->qmi.clients_lock, flags);
+ }
+
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ *buf = data;
+ return size;
+}
+
+static void write_callback(struct urb *urb)
+{
+ if (!urb) {
+ DBG("null urb\n");
+ return;
+ }
+
+ DBG("Write status/size %d/%d\n", urb->status, urb->actual_length);
+ up((struct semaphore *)urb->context);
+}
+
+static int write_sync(struct qcusbnet *dev, char *buf, int size, u16 cid)
+{
+ int result;
+ struct semaphore sem;
+ struct urb *urb;
+ struct urbsetup setup;
+ unsigned long flags;
+
+ if (!device_valid(dev)) {
+ DBG("Invalid device!\n");
+ return -ENXIO;
+ }
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ DBG("URB mem error\n");
+ return -ENOMEM;
+ }
+
+ result = qmux_fill(cid, buf, size);
+ if (result < 0) {
+ usb_free_urb(urb);
+ return result;
+ }
+
+ /* CDC Send Encapsulated Request packet */
+ setup.type = 0x21;
+ setup.code = 0;
+ setup.value = 0;
+ setup.index = 0;
+ setup.len = 0;
+ setup.len = size;
+
+ usb_fill_control_urb(urb, dev->usbnet->udev,
+ usb_sndctrlpipe(dev->usbnet->udev, 0),
+ (unsigned char *)&setup, (void *)buf, size,
+ NULL, dev);
+
+ DBG("Actual Write:\n");
+ if (qcusbnet_debug)
+ print_hex_dump(KERN_INFO, "gobi-write: ", DUMP_PREFIX_OFFSET,
+ 16, 1, buf, size, true);
+
+ sema_init(&sem, 0);
+
+ urb->complete = write_callback;
+ urb->context = &sem;
+
+ result = usb_autopm_get_interface(dev->iface);
+ if (result < 0) {
+ DBG("unable to resume interface: %d\n", result);
+ if (result == -EPERM) {
+ qc_suspend(dev->iface, PMSG_SUSPEND);
+ }
+ return result;
+ }
+
+ spin_lock_irqsave(&dev->qmi.clients_lock, flags);
+
+ if (!client_addurb(dev, cid, urb)) {
+ usb_free_urb(urb);
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ usb_autopm_put_interface(dev->iface);
+ return -EINVAL;
+ }
+
+ result = usb_submit_urb(urb, GFP_KERNEL);
+ if (result < 0) {
+ DBG("submit URB error %d\n", result);
+ if (client_delurb(dev, cid) != urb) {
+ DBG("Didn't get write URB back\n");
+ }
+
+ usb_free_urb(urb);
+
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ usb_autopm_put_interface(dev->iface);
+ return result;
+ }
+
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ result = down_interruptible(&sem);
+ if (!device_valid(dev)) {
+ DBG("Invalid device!\n");
+ return -ENXIO;
+ }
+
+ usb_autopm_put_interface(dev->iface);
+ spin_lock_irqsave(&dev->qmi.clients_lock, flags);
+ if (client_delurb(dev, cid) != urb) {
+ DBG("Didn't get write URB back\n");
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ return -EINVAL;
+ }
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+
+ if (!result) {
+ if (!urb->status) {
+ result = size;
+ } else {
+ DBG("bad status = %d\n", urb->status);
+ result = urb->status;
+ }
+ } else {
+ DBG("Interrupted %d !!!\n", result);
+ DBG("Device may be in bad state and need reset !!!\n");
+ usb_kill_urb(urb);
+ }
+
+ usb_free_urb(urb);
+ return result;
+}
+
+static int client_alloc(struct qcusbnet *dev, u8 type)
+{
+ u16 cid;
+ struct client *client;
+ int result;
+ void *wbuf;
+ size_t wbufsize;
+ void *rbuf;
+ u16 rbufsize;
+ unsigned long flags;
+ u8 tid;
+
+ if (!device_valid(dev)) {
+ DBG("Invalid device!\n");
+ return -ENXIO;
+ }
+
+ if (type) {
+ tid = atomic_add_return(1, &dev->qmi.qmitid);
+ if (!tid)
+ atomic_add_return(1, &dev->qmi.qmitid);
+ wbuf = qmictl_new_getcid(tid, type, &wbufsize);
+ if (!wbuf)
+ return -ENOMEM;
+ result = write_sync(dev, wbuf, wbufsize, QMICTL);
+ kfree(wbuf);
+
+ if (result < 0)
+ return result;
+
+ result = read_sync(dev, &rbuf, QMICTL, tid);
+ if (result < 0) {
+ DBG("bad read data %d\n", result);
+ return result;
+ }
+ rbufsize = result;
+
+ result = qmictl_alloccid_resp(rbuf, rbufsize, &cid);
+ kfree(rbuf);
+
+ if (result < 0)
+ return result;
+ } else {
+ cid = 0;
+ }
+
+ spin_lock_irqsave(&dev->qmi.clients_lock, flags);
+ if (client_bycid(dev, cid)) {
+ DBG("Client memory already exists\n");
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ return -ETOOMANYREFS;
+ }
+
+ client = kmalloc(sizeof(*client), GFP_ATOMIC);
+ if (!client) {
+ DBG("Error allocating read list\n");
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ return -ENOMEM;
+ }
+
+ list_add_tail(&client->node, &dev->qmi.clients);
+ client->cid = cid;
+ INIT_LIST_HEAD(&client->reads);
+ INIT_LIST_HEAD(&client->notifies);
+ INIT_LIST_HEAD(&client->urbs);
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ return cid;
+}
+
+static void client_free(struct qcusbnet *dev, u16 cid)
+{
+ struct list_head *node, *tmp;
+ int result;
+ struct client *client;
+ struct urb *urb;
+ void *data;
+ u16 size;
+ void *wbuf;
+ size_t wbufsize;
+ void *rbuf;
+ u16 rbufsize;
+ unsigned long flags;
+ u8 tid;
+
+ DBG("releasing 0x%04X\n", cid);
+
+ if (cid != QMICTL) {
+ tid = atomic_add_return(1, &dev->qmi.qmitid);
+ if (!tid)
+ tid = atomic_add_return(1, &dev->qmi.qmitid);
+ wbuf = qmictl_new_releasecid(tid, cid, &wbufsize);
+ if (!wbuf) {
+ DBG("memory error\n");
+ } else {
+ result = write_sync(dev, wbuf, wbufsize, QMICTL);
+ kfree(wbuf);
+
+ if (result < 0) {
+ DBG("bad write status %d\n", result);
+ } else {
+ result = read_sync(dev, &rbuf, QMICTL, tid);
+ if (result < 0) {
+ DBG("bad read status %d\n", result);
+ } else {
+ rbufsize = result;
+ result = qmictl_freecid_resp(rbuf, rbufsize);
+ kfree(rbuf);
+ if (result < 0)
+ DBG("error %d parsing response\n", result);
+ }
+ }
+ }
+ }
+
+ spin_lock_irqsave(&dev->qmi.clients_lock, flags);
+ list_for_each_safe(node, tmp, &dev->qmi.clients) {
+ client = list_entry(node, struct client, node);
+ if (client->cid == cid) {
+ while (client_notify(dev, cid, 0)) {
+ ;
+ }
+
+ urb = client_delurb(dev, cid);
+ while (urb != NULL) {
+ usb_kill_urb(urb);
+ usb_free_urb(urb);
+ urb = client_delurb(dev, cid);
+ }
+
+ while (client_delread(dev, cid, 0, &data, &size))
+ kfree(data);
+
+ list_del(&client->node);
+ kfree(client);
+ }
+ }
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+}
+
+struct client *client_bycid(struct qcusbnet *dev, u16 cid)
+{
+ struct list_head *node;
+ struct client *client;
+
+ if (!device_valid(dev)) {
+ DBG("Invalid device\n");
+ return NULL;
+ }
+
+ assert_locked(dev);
+
+ list_for_each(node, &dev->qmi.clients) {
+ client = list_entry(node, struct client, node);
+ if (client->cid == cid)
+ return client;
+ }
+
+ DBG("Could not find client mem 0x%04X\n", cid);
+ return NULL;
+}
+
+static bool client_addread(struct qcusbnet *dev, u16 cid, u16 tid, void *data,
+ u16 size)
+{
+ struct client *client;
+ struct readreq *req;
+
+ assert_locked(dev);
+
+ client = client_bycid(dev, cid);
+ if (!client) {
+ DBG("Could not find this client's memory 0x%04X\n", cid);
+ return false;
+ }
+
+ req = kmalloc(sizeof(*req), GFP_ATOMIC);
+ if (!req) {
+ DBG("Mem error\n");
+ return false;
+ }
+
+ req->data = data;
+ req->size = size;
+ req->tid = tid;
+
+ list_add_tail(&req->node, &client->reads);
+
+ return true;
+}
+
+static bool client_delread(struct qcusbnet *dev, u16 cid, u16 tid, void **data,
+ u16 *size)
+{
+ struct client *client;
+ struct readreq *req;
+ struct list_head *node;
+
+ assert_locked(dev);
+
+ client = client_bycid(dev, cid);
+ if (!client) {
+ DBG("Could not find this client's memory 0x%04X\n", cid);
+ return false;
+ }
+
+ list_for_each(node, &client->reads) {
+ req = list_entry(node, struct readreq, node);
+ if (!tid || tid == req->tid) {
+ *data = req->data;
+ *size = req->size;
+ list_del(&req->node);
+ kfree(req);
+ return true;
+ }
+
+ DBG("skipping 0x%04X data TID = %x\n", cid, req->tid);
+ }
+
+ DBG("No read memory to pop, Client 0x%04X, TID = %x\n", cid, tid);
+ return false;
+}
+
+static bool client_addnotify(struct qcusbnet *dev, u16 cid, u16 tid,
+ void (*hook)(struct qcusbnet *, u16, void *),
+ void *data)
+{
+ struct client *client;
+ struct notifyreq *req;
+
+ assert_locked(dev);
+
+ client = client_bycid(dev, cid);
+ if (!client) {
+ DBG("Could not find this client's memory 0x%04X\n", cid);
+ return false;
+ }
+
+ req = kmalloc(sizeof(*req), GFP_ATOMIC);
+ if (!req) {
+ DBG("Mem error\n");
+ return false;
+ }
+
+ list_add_tail(&req->node, &client->notifies);
+ req->func = hook;
+ req->data = data;
+ req->tid = tid;
+
+ return true;
+}
+
+static bool client_notify(struct qcusbnet *dev, u16 cid, u16 tid)
+{
+ struct client *client;
+ struct notifyreq *delnotify, *notify;
+ struct list_head *node;
+
+ assert_locked(dev);
+
+ client = client_bycid(dev, cid);
+ if (!client) {
+ DBG("Could not find this client's memory 0x%04X\n", cid);
+ return false;
+ }
+
+ delnotify = NULL;
+
+ list_for_each(node, &client->notifies) {
+ notify = list_entry(node, struct notifyreq, node);
+ if (!tid || !notify->tid || tid == notify->tid) {
+ delnotify = notify;
+ break;
+ }
+
+ DBG("skipping data TID = %x\n", notify->tid);
+ }
+
+ if (delnotify) {
+ list_del(&delnotify->node);
+ if (delnotify->func) {
+ spin_unlock(&dev->qmi.clients_lock);
+ delnotify->func(dev, cid, delnotify->data);
+ spin_lock(&dev->qmi.clients_lock);
+ }
+ kfree(delnotify);
+ return true;
+ }
+
+ DBG("no one to notify for TID %x\n", tid);
+ return false;
+}
+
+static bool client_addurb(struct qcusbnet *dev, u16 cid, struct urb *urb)
+{
+ struct client *client;
+ struct urbreq *req;
+
+ assert_locked(dev);
+
+ client = client_bycid(dev, cid);
+ if (!client) {
+ DBG("Could not find this client's memory 0x%04X\n", cid);
+ return false;
+ }
+
+ req = kmalloc(sizeof(*req), GFP_ATOMIC);
+ if (!req) {
+ DBG("Mem error\n");
+ return false;
+ }
+
+ req->urb = urb;
+ list_add_tail(&req->node, &client->urbs);
+
+ return true;
+}
+
+static struct urb *client_delurb(struct qcusbnet *dev, u16 cid)
+{
+ struct client *client;
+ struct urbreq *req;
+ struct urb *urb;
+
+ assert_locked(dev);
+
+ client = client_bycid(dev, cid);
+ if (!client) {
+ DBG("Could not find this client's memory 0x%04X\n", cid);
+ return NULL;
+ }
+
+ if (list_empty(&client->urbs)) {
+ DBG("No URB's to pop\n");
+ return NULL;
+ }
+
+ req = list_first_entry(&client->urbs, struct urbreq, node);
+ list_del(&req->node);
+ urb = req->urb;
+ kfree(req);
+ return urb;
+}
+
+static int devqmi_open(struct inode *inode, struct file *file)
+{
+ struct qmihandle *handle;
+ struct qmidev *qmidev = container_of(inode->i_cdev, struct qmidev, cdev);
+ struct qcusbnet *dev = container_of(qmidev, struct qcusbnet, qmi);
+
+ /* We need an extra ref on the device per fd, since we stash a ref
+ * inside the handle. If qcusbnet_get() returns NULL, that means the
+ * device has been removed from the list - no new refs for us. */
+ struct qcusbnet *ref = qcusbnet_get(dev);
+
+ if (!ref)
+ return -ENXIO;
+
+ file->private_data = kmalloc(sizeof(struct qmihandle), GFP_KERNEL);
+ if (!file->private_data) {
+ DBG("Mem error\n");
+ return -ENOMEM;
+ }
+
+ handle = (struct qmihandle *)file->private_data;
+ handle->cid = (u16)-1;
+ handle->dev = ref;
+
+ DBG("%p %04x", handle, handle->cid);
+
+ return 0;
+}
+
+static int devqmi_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int result;
+ u32 vidpid;
+
+ struct qmihandle *handle = (struct qmihandle *)file->private_data;
+
+ DBG("%p %04x %08x", handle, handle->cid, cmd);
+
+ if (!handle) {
+ DBG("Bad file data\n");
+ return -EBADF;
+ }
+
+ if (handle->dev->dying) {
+ DBG("Dying device");
+ return -ENXIO;
+ }
+
+ if (!device_valid(handle->dev)) {
+ DBG("Invalid device!\n");
+ return -ENXIO;
+ }
+
+ switch (cmd) {
+ case IOCTL_QMI_GET_SERVICE_FILE:
+
+ DBG("Setting up QMI for service %lu\n", arg);
+ if (!(u8)arg) {
+ DBG("Cannot use QMICTL from userspace\n");
+ return -EINVAL;
+ }
+
+ if (handle->cid != (u16)-1) {
+ DBG("Close the current connection before opening a new one\n");
+ return -EBADR;
+ }
+
+ result = client_alloc(handle->dev, (u8)arg);
+ if (result < 0)
+ return result;
+ handle->cid = result;
+
+ return 0;
+ break;
+
+ /* Okay, all aboard the nasty hack express. If we don't have this
+ * ioctl() (and we just rely on userspace to close() the file
+ * descriptors), if userspace has any refs left to this fd (like, say, a
+ * pending read()), then the read might hang around forever. Userspace
+ * needs a way to cause us to kick people off those waitqueues before
+ * closing the fd for good.
+ *
+ * If this driver used workqueues, the correct approach here would
+ * instead be to make the file descriptor select()able, and then just
+ * use select() instead of aio in userspace (thus allowing us to get
+ * away with one thread total and avoiding the recounting mess
+ * altogether).
+ */
+ case IOCTL_QMI_CLOSE:
+ DBG("Tearing down QMI for service %lu", arg);
+ if (handle->cid == (u16)-1) {
+ DBG("no qmi cid");
+ return -EBADR;
+ }
+
+ file->private_data = NULL;
+ client_free(handle->dev, handle->cid);
+ kfree(handle);
+ return 0;
+ break;
+
+ case IOCTL_QMI_GET_DEVICE_VIDPID:
+ if (!arg) {
+ DBG("Bad VIDPID buffer\n");
+ return -EINVAL;
+ }
+
+ if (!handle->dev->usbnet) {
+ DBG("Bad usbnet\n");
+ return -ENOMEM;
+ }
+
+ if (!handle->dev->usbnet->udev) {
+ DBG("Bad udev\n");
+ return -ENOMEM;
+ }
+
+ vidpid = ((le16_to_cpu(handle->dev->usbnet->udev->descriptor.idVendor) << 16)
+ + le16_to_cpu(handle->dev->usbnet->udev->descriptor.idProduct));
+
+ result = copy_to_user((unsigned int *)arg, &vidpid, 4);
+ if (result)
+ DBG("Copy to userspace failure\n");
+
+ return result;
+ break;
+
+ case IOCTL_QMI_GET_DEVICE_MEID:
+ if (!arg) {
+ DBG("Bad MEID buffer\n");
+ return -EINVAL;
+ }
+
+ result = copy_to_user((unsigned int *)arg, &handle->dev->meid[0], 14);
+ if (result)
+ DBG("copy to userspace failure\n");
+
+ return result;
+ break;
+ default:
+ return -EBADRQC;
+ }
+}
+
+static int devqmi_release(struct inode *inode, struct file *file)
+{
+ struct qmihandle *handle = (struct qmihandle *)file->private_data;
+ if (!handle)
+ return 0;
+ file->private_data = NULL;
+ if (handle->cid != (u16)-1)
+ client_free(handle->dev, handle->cid);
+ qcusbnet_put(handle->dev);
+ kfree(handle);
+ return 0;
+}
+
+static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size,
+ loff_t *pos)
+{
+ int result;
+ void *data = NULL;
+ void *smalldata;
+ struct qmihandle *handle = (struct qmihandle *)file->private_data;
+
+ if (!handle) {
+ DBG("Bad file data\n");
+ return -EBADF;
+ }
+
+ if (handle->dev->dying) {
+ DBG("Dying device");
+ return -ENXIO;
+ }
+
+ if (!device_valid(handle->dev)) {
+ DBG("Invalid device!\n");
+ return -ENXIO;
+ }
+
+ if (handle->cid == (u16)-1) {
+ DBG("Client ID must be set before reading 0x%04X\n",
+ handle->cid);
+ return -EBADR;
+ }
+
+ result = read_sync(handle->dev, &data, handle->cid, 0);
+ if (result <= 0)
+ return result;
+
+ result -= qmux_size;
+ smalldata = data + qmux_size;
+
+ if (result > size) {
+ DBG("Read data is too large for amount user has requested\n");
+ kfree(data);
+ return -EOVERFLOW;
+ }
+
+ if (copy_to_user(buf, smalldata, result)) {
+ DBG("Error copying read data to user\n");
+ result = -EFAULT;
+ }
+
+ kfree(data);
+ return result;
+}
+
+static ssize_t devqmi_write(struct file *file, const char __user * buf,
+ size_t size, loff_t *pos)
+{
+ int status;
+ void *wbuf;
+ struct qmihandle *handle = (struct qmihandle *)file->private_data;
+
+ if (!handle) {
+ DBG("Bad file data\n");
+ return -EBADF;
+ }
+
+ if (!device_valid(handle->dev)) {
+ DBG("Invalid device! Updating f_ops\n");
+ file->f_op = file->f_dentry->d_inode->i_fop;
+ return -ENXIO;
+ }
+
+ if (handle->cid == (u16)-1) {
+ DBG("Client ID must be set before writing 0x%04X\n",
+ handle->cid);
+ return -EBADR;
+ }
+
+ wbuf = kmalloc(size + qmux_size, GFP_KERNEL);
+ if (!wbuf)
+ return -ENOMEM;
+ status = copy_from_user(wbuf + qmux_size, buf, size);
+ if (status) {
+ DBG("Unable to copy data from userspace %d\n", status);
+ kfree(wbuf);
+ return status;
+ }
+
+ status = write_sync(handle->dev, wbuf, size + qmux_size,
+ handle->cid);
+
+ kfree(wbuf);
+ if (status == size + qmux_size)
+ return size;
+ return status;
+}
+
+int qc_register(struct qcusbnet *dev)
+{
+ int result;
+ int qmiidx = 0;
+ dev_t devno;
+ char *name;
+
+ dev->valid = true;
+ dev->dying = false;
+ result = client_alloc(dev, QMICTL);
+ if (result) {
+ dev->valid = false;
+ return result;
+ }
+ atomic_set(&dev->qmi.qmitid, 1);
+
+ result = qc_startread(dev);
+ if (result) {
+ dev->valid = false;
+ return result;
+ }
+
+ if (!qmi_ready(dev, 30000)) {
+ DBG("Device unresponsive to QMI\n");
+ return -ETIMEDOUT;
+ }
+
+ result = setup_wds_callback(dev);
+ if (result) {
+ dev->valid = false;
+ return result;
+ }
+
+ result = qmidms_getmeid(dev);
+ if (result) {
+ dev->valid = false;
+ return result;
+ }
+
+ result = alloc_chrdev_region(&devno, 0, 1, "qcqmi");
+ if (result < 0)
+ return result;
+
+ cdev_init(&dev->qmi.cdev, &devqmi_fops);
+ dev->qmi.cdev.owner = THIS_MODULE;
+ dev->qmi.cdev.ops = &devqmi_fops;
+
+ result = cdev_add(&dev->qmi.cdev, devno, 1);
+ if (result) {
+ DBG("error adding cdev\n");
+ return result;
+ }
+
+ name = strstr(dev->usbnet->net->name, "usb");
+ if (!name) {
+ DBG("Bad net name: %s\n", dev->usbnet->net->name);
+ return -ENXIO;
+ }
+ name += strlen("usb");
+ qmiidx = simple_strtoul(name, NULL, 10);
+ if (qmiidx < 0) {
+ DBG("Bad minor number\n");
+ return -ENXIO;
+ }
+
+ printk(KERN_INFO "creating qcqmi%d\n", qmiidx);
+ device_create(dev->qmi.devclass, &dev->iface->dev, devno, NULL, "qcqmi%d", qmiidx);
+
+ dev->qmi.devnum = devno;
+ return 0;
+}
+
+void qc_deregister(struct qcusbnet *dev)
+{
+ struct list_head *node, *tmp;
+ struct client *client;
+
+ dev->dying = true;
+ list_for_each_safe(node, tmp, &dev->qmi.clients) {
+ client = list_entry(node, struct client, node);
+ DBG("release 0x%04X\n", client->cid);
+ client_free(dev, client->cid);
+ }
+
+ qc_stopread(dev);
+ dev->valid = false;
+ if (!IS_ERR(dev->qmi.devclass))
+ device_destroy(dev->qmi.devclass, dev->qmi.devnum);
+ cdev_del(&dev->qmi.cdev);
+ unregister_chrdev_region(dev->qmi.devnum, 1);
+}
+
+static bool qmi_ready(struct qcusbnet *dev, u16 timeout)
+{
+ int result;
+ void *wbuf;
+ size_t wbufsize;
+ void *rbuf;
+ u16 rbufsize;
+ struct semaphore sem;
+ u16 now;
+ unsigned long flags;
+ u8 tid;
+
+ if (!device_valid(dev)) {
+ DBG("Invalid device\n");
+ return -EFAULT;
+ }
+
+
+ for (now = 0; now < timeout; now += 100) {
+ sema_init(&sem, 0);
+
+ tid = atomic_add_return(1, &dev->qmi.qmitid);
+ if (!tid)
+ tid = atomic_add_return(1, &dev->qmi.qmitid);
+ kfree(wbuf);
+ wbuf = qmictl_new_ready(tid, &wbufsize);
+ if (!wbuf)
+ return -ENOMEM;
+
+ result = read_async(dev, QMICTL, tid, upsem, &sem);
+ if (result) {
+ kfree(wbuf);
+ return false;
+ }
+
+ write_sync(dev, wbuf, wbufsize, QMICTL);
+
+ msleep(100);
+ if (!down_trylock(&sem)) {
+ spin_lock_irqsave(&dev->qmi.clients_lock, flags);
+ if (client_delread(dev, QMICTL, tid, &rbuf, &rbufsize)) {
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ kfree(rbuf);
+ break;
+ } else {
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ }
+ } else {
+ spin_lock_irqsave(&dev->qmi.clients_lock, flags);
+ client_notify(dev, QMICTL, tid);
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+ }
+ }
+
+ kfree(wbuf);
+
+ if (now >= timeout)
+ return false;
+
+ DBG("QMI Ready after %u milliseconds\n", now);
+
+ /* 3580 and newer doesn't need a delay; older needs 5000ms */
+ if (qcusbnet2k_fwdelay)
+ msleep(qcusbnet2k_fwdelay * 1000);
+
+ return true;
+}
+
+static void wds_callback(struct qcusbnet *dev, u16 cid, void *data)
+{
+ bool ret;
+ int result;
+ void *rbuf;
+ u16 rbufsize;
+
+ struct net_device_stats *stats = &(dev->usbnet->net->stats);
+
+ struct qmiwds_stats dstats = {
+ .txok = (u32)-1,
+ .rxok = (u32)-1,
+ .txerr = (u32)-1,
+ .rxerr = (u32)-1,
+ .txofl = (u32)-1,
+ .rxofl = (u32)-1,
+ .txbytesok = (u64)-1,
+ .rxbytesok = (u64)-1,
+ };
+ unsigned long flags;
+
+ if (!device_valid(dev)) {
+ DBG("Invalid device\n");
+ return;
+ }
+
+ spin_lock_irqsave(&dev->qmi.clients_lock, flags);
+ ret = client_delread(dev, cid, 0, &rbuf, &rbufsize);
+ spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
+
+ if (!ret) {
+ DBG("WDS callback failed to get data\n");
+ return;
+ }
+
+ dstats.linkstate = !qc_isdown(dev, DOWN_NO_NDIS_CONNECTION);
+ dstats.reconfigure = false;
+
+ result = qmiwds_event_resp(rbuf, rbufsize, &dstats);
+ if (result < 0) {
+ DBG("bad WDS packet\n");
+ } else {
+ if (dstats.txofl != (u32)-1)
+ stats->tx_fifo_errors = dstats.txofl;
+
+ if (dstats.rxofl != (u32)-1)
+ stats->rx_fifo_errors = dstats.rxofl;
+
+ if (dstats.txerr != (u32)-1)
+ stats->tx_errors = dstats.txerr;
+
+ if (dstats.rxerr != (u32)-1)
+ stats->rx_errors = dstats.rxerr;
+
+ if (dstats.txok != (u32)-1)
+ stats->tx_packets = dstats.txok + stats->tx_errors;
+
+ if (dstats.rxok != (u32)-1)
+ stats->rx_packets = dstats.rxok + stats->rx_errors;
+
+ if (dstats.txbytesok != (u64)-1)
+ stats->tx_bytes = dstats.txbytesok;
+
+ if (dstats.rxbytesok != (u64)-1)
+ stats->rx_bytes = dstats.rxbytesok;
+
+ if (dstats.reconfigure) {
+ DBG("Net device link reset\n");
+ qc_setdown(dev, DOWN_NO_NDIS_CONNECTION);
+ qc_cleardown(dev, DOWN_NO_NDIS_CONNECTION);
+ } else {
+ if (dstats.linkstate) {
+ DBG("Net device link is connected\n");
+ qc_cleardown(dev, DOWN_NO_NDIS_CONNECTION);
+ } else {
+ DBG("Net device link is disconnected\n");
+ qc_setdown(dev, DOWN_NO_NDIS_CONNECTION);
+ }
+ }
+ }
+
+ kfree(rbuf);
+
+ result = read_async(dev, cid, 0, wds_callback, data);
+ if (result != 0)
+ DBG("unable to setup next async read\n");
+}
+
+static int setup_wds_callback(struct qcusbnet *dev)
+{
+ int result;
+ void *buf;
+ size_t size;
+ u16 cid;
+
+ if (!device_valid(dev)) {
+ DBG("Invalid device\n");
+ return -EFAULT;
+ }
+
+ result = client_alloc(dev, QMIWDS);
+ if (result < 0)
+ return result;
+ cid = result;
+
+ buf = qmiwds_new_seteventreport(1, &size);
+ if (!buf)
+ return -ENOMEM;
+
+ result = write_sync(dev, buf, size, cid);
+ kfree(buf);
+
+ if (result < 0) {
+ return result;
+ }
+
+ buf = qmiwds_new_getpkgsrvcstatus(2, &size);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ result = write_sync(dev, buf, size, cid);
+ kfree(buf);
+
+ if (result < 0)
+ return result;
+
+ result = read_async(dev, cid, 0, wds_callback, NULL);
+ if (result) {
+ DBG("unable to setup async read\n");
+ return result;
+ }
+
+ result = usb_control_msg(dev->usbnet->udev,
+ usb_sndctrlpipe(dev->usbnet->udev, 0),
+ 0x22, 0x21, 1, 0, NULL, 0, 100);
+ if (result < 0) {
+ DBG("Bad SetControlLineState status %d\n", result);
+ return result;
+ }
+
+ return 0;
+}
+
+static int qmidms_getmeid(struct qcusbnet *dev)
+{
+ int result;
+ void *wbuf;
+ size_t wbufsize;
+ void *rbuf;
+ u16 rbufsize;
+ u16 cid;
+
+ if (!device_valid(dev)) {
+ DBG("Invalid device\n");
+ return -EFAULT;
+ }
+
+ result = client_alloc(dev, QMIDMS);
+ if (result < 0)
+ return result;
+ cid = result;
+
+ wbuf = qmidms_new_getmeid(1, &wbufsize);
+ if (!wbuf)
+ return -ENOMEM;
+
+ result = write_sync(dev, wbuf, wbufsize, cid);
+ kfree(wbuf);
+
+ if (result < 0)
+ return result;
+
+ result = read_sync(dev, &rbuf, cid, 1);
+ if (result < 0)
+ return result;
+ rbufsize = result;
+
+ result = qmidms_meid_resp(rbuf, rbufsize, &dev->meid[0], 14);
+ kfree(rbuf);
+
+ if (result < 0) {
+ DBG("bad get MEID resp\n");
+ memset(&dev->meid[0], '0', 14);
+ }
+
+ client_free(dev, cid);
+ return 0;
+}
+
+module_param(qcusbnet2k_fwdelay, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(qcusbnet2k_fwdelay, "Delay for old firmware");
diff --git a/drivers/net/usb/gobi/qmidevice.h b/drivers/net/usb/gobi/qmidevice.h
new file mode 100644
index 0000000..5274a0d
--- /dev/null
+++ b/drivers/net/usb/gobi/qmidevice.h
@@ -0,0 +1,35 @@
+/* qmidevice.h - gobi QMI device header
+ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef QCUSBNET_QMIDEVICE_H
+#define QCUSBNET_QMIDEVICE_H
+
+#include "structs.h"
+#include "qmi.h"
+
+void qc_setdown(struct qcusbnet *dev, u8 reason);
+void qc_cleardown(struct qcusbnet *dev, u8 reason);
+bool qc_isdown(struct qcusbnet *dev, u8 reason);
+
+int qc_startread(struct qcusbnet *dev);
+void qc_stopread(struct qcusbnet *dev);
+
+int qc_register(struct qcusbnet *dev);
+void qc_deregister(struct qcusbnet *dev);
+
+#endif /* !QCUSBNET_QMIDEVICE_H */
diff --git a/drivers/net/usb/gobi/structs.h b/drivers/net/usb/gobi/structs.h
new file mode 100644
index 0000000..13b3788
--- /dev/null
+++ b/drivers/net/usb/gobi/structs.h
@@ -0,0 +1,96 @@
+/* structs.h - shared structures
+ * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef QCUSBNET_STRUCTS_H
+#define QCUSBNET_STRUCTS_H
+
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/version.h>
+#include <linux/cdev.h>
+#include <linux/kobject.h>
+#include <linux/kthread.h>
+
+#include <linux/usb/usbnet.h>
+
+#include <linux/fdtable.h>
+
+#define DBG(fmt, arg...) \
+do { \
+ if (qcusbnet_debug == 1) \
+ printk(KERN_INFO "QCUSBNet2k::%s " fmt, __func__, ##arg); \
+} while (0)
+
+struct qcusbnet;
+
+struct urbreq {
+ struct list_head node;
+ struct urb *urb;
+};
+
+#define DEFAULT_READ_URB_LENGTH 0x1000
+
+struct worker {
+ struct task_struct *thread;
+ struct completion work;
+ struct list_head urbs;
+ spinlock_t urbs_lock;
+ struct urb *active;
+ spinlock_t active_lock;
+ struct usb_interface *iface;
+};
+
+struct qmidev {
+ dev_t devnum;
+ struct class *devclass;
+ struct cdev cdev;
+ struct urb *readurb;
+ struct urbsetup *readsetup;
+ void *readbuf;
+ struct urb *inturb;
+ void *intbuf;
+ struct list_head clients;
+ spinlock_t clients_lock;
+ atomic_t qmitid;
+};
+
+enum {
+ DOWN_NO_NDIS_CONNECTION = 0,
+ DOWN_CDC_CONNECTION_SPEED = 1,
+ DOWN_DRIVER_SUSPENDED = 2,
+ DOWN_NET_IFACE_STOPPED = 3,
+};
+
+struct qcusbnet {
+ struct list_head node;
+ struct kref refcount;
+ struct usbnet *usbnet;
+ struct usb_interface *iface;
+ int (*open)(struct net_device *);
+ int (*stop)(struct net_device *);
+ unsigned long down;
+ bool valid;
+ bool dying;
+ struct qmidev qmi;
+ char meid[14];
+ struct worker worker;
+};
+
+#endif /* !QCUSBNET_STRUCTS_H */
--
1.7.3.1
^ permalink raw reply related
* Re: [Bugme-new] [Bug 32832] New: shutdown(2) does not fully shut down socket any more
From: David Miller @ 2011-04-13 19:09 UTC (permalink / raw)
To: eric.dumazet; +Cc: akpm, netdev, bugzilla-daemon, bugme-daemon, kees
In-Reply-To: <1302663608.2811.62.camel@edumazet-laptop>
From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Wed, 13 Apr 2011 05:00:08 +0200
> Le mercredi 13 avril 2011 à 04:55 +0200, Eric Dumazet a écrit :
>
>> Since SO_REUSEPORT is not a 'stable fix', I suggest we revert the patch,
>> and eventually work on SO_REUSEPORT on net-next-2.6
>>
>> What do you think ?
>>
>
> Sorry, I should have mentioned commit id : c191a836a908d1dd6
> (tcp: disallow bind() to reuse addr/port)
I'm commiting the revert as follows to net-2.6, and will queue
it up for -stable as well:
--------------------
Revert "tcp: disallow bind() to reuse addr/port"
This reverts commit c191a836a908d1dd6b40c503741f91b914de3348.
It causes known regressions for programs that expect to be able to use
SO_REUSEADDR to shutdown a socket, then successfully rebind another
socket to the same ID.
Programs such as haproxy and amavisd expect this to work.
This should fix kernel bugzilla 32832.
Signed-off-by: David S. Miller <davem@davemloft.net>
---
net/ipv4/inet_connection_sock.c | 5 ++---
net/ipv6/inet6_connection_sock.c | 2 +-
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index 6c0b7f4..38f23e7 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -73,7 +73,7 @@ int inet_csk_bind_conflict(const struct sock *sk,
!sk2->sk_bound_dev_if ||
sk->sk_bound_dev_if == sk2->sk_bound_dev_if)) {
if (!reuse || !sk2->sk_reuse ||
- ((1 << sk2->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))) {
+ sk2->sk_state == TCP_LISTEN) {
const __be32 sk2_rcv_saddr = sk_rcv_saddr(sk2);
if (!sk2_rcv_saddr || !sk_rcv_saddr(sk) ||
sk2_rcv_saddr == sk_rcv_saddr(sk))
@@ -122,8 +122,7 @@ again:
(tb->num_owners < smallest_size || smallest_size == -1)) {
smallest_size = tb->num_owners;
smallest_rover = rover;
- if (atomic_read(&hashinfo->bsockets) > (high - low) + 1 &&
- !inet_csk(sk)->icsk_af_ops->bind_conflict(sk, tb)) {
+ if (atomic_read(&hashinfo->bsockets) > (high - low) + 1) {
spin_unlock(&head->lock);
snum = smallest_rover;
goto have_snum;
diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c
index 1660546..f2c5b0f 100644
--- a/net/ipv6/inet6_connection_sock.c
+++ b/net/ipv6/inet6_connection_sock.c
@@ -44,7 +44,7 @@ int inet6_csk_bind_conflict(const struct sock *sk,
!sk2->sk_bound_dev_if ||
sk->sk_bound_dev_if == sk2->sk_bound_dev_if) &&
(!sk->sk_reuse || !sk2->sk_reuse ||
- ((1 << sk2->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))) &&
+ sk2->sk_state == TCP_LISTEN) &&
ipv6_rcv_saddr_equal(sk, sk2))
break;
}
--
1.7.4.3
^ permalink raw reply related
* [net-next-2.6 RFC PATCH v2 00/13] ethtool: allow custom interval for
From: Bruce Allan @ 2011-04-13 19:58 UTC (permalink / raw)
To: netdev
physical identification
The following series changes the recently added ethtool set_phys_id
functions to allow drivers to provide a frequency at which to cycle
through an on/off identifier via software if/when the capability is
not provided by hardware.
---
Bruce Allan (13):
tg3: set ethtool set_phys_id on/off cycle frequency to 1/sec
sky2: set ethtool set_phys_id on/off cycle frequency to 1/sec
skge: set ethtool set_phys_id on/off cycle frequency to 2/sec
sfc: set ethtool set_phys_id on/off cycle frequency to 1/sec
s2io: set ethtool set_phys_id on/off cycle frequency to 1/sec
pcnet32: set ethtool set_phys_id on/off cycle frequency to 2/sec
niu: set ethtool set_phys_id on/off cycle frequency to 1/sec
ewrk3: set ethtool set_phys_id on/off cycle frequency to 2/sec
cxgb3: set ethtool set_phys_id on/off cycle frequency to 1/sec
bnx2x: set ethtool set_phys_id on/off cycle frequency to 1/sec
bnx2: set ethtool set_phys_id on/off cycle frequency to 1/sec
benet: set ethtool set_phys_id on/off cycle frequency to 1/sec
ethtool: allow custom interval for physical identification
drivers/net/benet/be_ethtool.c | 2 +-
drivers/net/bnx2.c | 2 +-
drivers/net/bnx2x/bnx2x_ethtool.c | 2 +-
drivers/net/cxgb3/cxgb3_main.c | 2 +-
drivers/net/ewrk3.c | 2 +-
drivers/net/niu.c | 2 +-
drivers/net/pcnet32.c | 2 +-
drivers/net/s2io.c | 2 +-
drivers/net/sfc/ethtool.c | 6 +++---
drivers/net/skge.c | 2 +-
drivers/net/sky2.c | 2 +-
drivers/net/tg3.c | 2 +-
include/linux/ethtool.h | 6 ++++--
net/core/ethtool.c | 13 ++++++++-----
14 files changed, 26 insertions(+), 21 deletions(-)
--
Thanks,
Bruce.
^ permalink raw reply
* [net-next-2.6 RFC PATCH v2 01/13] ethtool: allow custom interval for physical identification
From: Bruce Allan @ 2011-04-13 19:58 UTC (permalink / raw)
To: netdev; +Cc: Bruce Allan, Ben Hutchings
In-Reply-To: <20110413195146.25901.72193.stgit@gitlad.jf.intel.com>
When physical identification of an adapter is done by toggling the
mechanism on and off through software utilizing the set_phys_id operation,
it is done with a fixed duration for both on and off states. Some drivers
may want to set a custom duration for the on/off intervals. This patch
changes the API so the return code from the driver's entry point when it
is called with ETHTOOL_ID_ACTIVE can specify the frequency at which to
cycle the on/off states.
Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
Cc: Ben Hutchings <bhutchings@solarflare.com>
---
include/linux/ethtool.h | 6 ++++--
net/core/ethtool.c | 13 ++++++++-----
2 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index 12cfbd0..6191a84 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -770,8 +770,10 @@ bool ethtool_invalid_flags(struct net_device *dev, u32 data, u32 supported);
* attached to it. The implementation may update the indicator
* asynchronously or synchronously, but in either case it must return
* quickly. It is initially called with the argument %ETHTOOL_ID_ACTIVE,
- * and must either activate asynchronous updates or return -%EINVAL.
- * If it returns -%EINVAL then it will be called again at intervals with
+ * and must either activate asynchronous updates and return zero, return
+ * a negative error or return a positive frequency for synchronous
+ * indication (e.g. 1 for one on/off cycle per second). If it returns
+ * a frequency then it will be called again at intervals with the
* argument %ETHTOOL_ID_ON or %ETHTOOL_ID_OFF and should set the state of
* the indicator accordingly. Finally, it is called with the argument
* %ETHTOOL_ID_INACTIVE and must deactivate the indicator. Returns a
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 43ef09f..2dd7bde 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -1640,7 +1640,7 @@ static int ethtool_phys_id(struct net_device *dev, void __user *useraddr)
return dev->ethtool_ops->phys_id(dev, id.data);
rc = dev->ethtool_ops->set_phys_id(dev, ETHTOOL_ID_ACTIVE);
- if (rc && rc != -EINVAL)
+ if (rc < 0)
return rc;
/* Drop the RTNL lock while waiting, but prevent reentry or
@@ -1655,23 +1655,26 @@ static int ethtool_phys_id(struct net_device *dev, void __user *useraddr)
schedule_timeout_interruptible(
id.data ? (id.data * HZ) : MAX_SCHEDULE_TIMEOUT);
} else {
- /* Driver expects to be called periodically */
+ /* Driver expects to be called using the frequency in rc */
+ int i = 0, interval = (HZ / (rc * 2));
+
do {
rtnl_lock();
rc = dev->ethtool_ops->set_phys_id(dev, ETHTOOL_ID_ON);
rtnl_unlock();
if (rc)
break;
- schedule_timeout_interruptible(HZ / 2);
+ schedule_timeout_interruptible(interval);
rtnl_lock();
rc = dev->ethtool_ops->set_phys_id(dev, ETHTOOL_ID_OFF);
rtnl_unlock();
if (rc)
break;
- schedule_timeout_interruptible(HZ / 2);
+ schedule_timeout_interruptible(interval);
} while (!signal_pending(current) &&
- (id.data == 0 || --id.data != 0));
+ (id.data == 0 ||
+ (++i * 2 * interval) < (id.data * HZ)));
}
rtnl_lock();
^ permalink raw reply related
* [net-next-2.6 RFC PATCH v2 02/13] benet: set ethtool set_phys_id on/off cycle frequency to 1/sec
From: Bruce Allan @ 2011-04-13 19:58 UTC (permalink / raw)
To: netdev; +Cc: Bruce Allan, Sathya Perla, Subbu Seetharaman, Ajit Khaparde
In-Reply-To: <20110413195146.25901.72193.stgit@gitlad.jf.intel.com>
Physical identification frequency based on how it was done prior to the
introduction of set_phys_id. Compile tested only.
Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
Cc: Sathya Perla <sathya.perla@emulex.com>
Cc: Subbu Seetharaman <subbu.seetharaman@emulex.com>
Cc: Ajit Khaparde <ajit.khaparde@emulex.com>
---
drivers/net/benet/be_ethtool.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/benet/be_ethtool.c b/drivers/net/benet/be_ethtool.c
index 96f5502..80226e4 100644
--- a/drivers/net/benet/be_ethtool.c
+++ b/drivers/net/benet/be_ethtool.c
@@ -516,7 +516,7 @@ be_set_phys_id(struct net_device *netdev,
case ETHTOOL_ID_ACTIVE:
be_cmd_get_beacon_state(adapter, adapter->hba_port_num,
&adapter->beacon_state);
- return -EINVAL;
+ return 1; /* cycle on/off once per second */
case ETHTOOL_ID_ON:
be_cmd_set_beacon_state(adapter, adapter->hba_port_num, 0, 0,
^ permalink raw reply related
* [net-next-2.6 RFC PATCH v2 03/13] bnx2: set ethtool set_phys_id on/off cycle frequency to 1/sec
From: Bruce Allan @ 2011-04-13 19:59 UTC (permalink / raw)
To: netdev; +Cc: Bruce Allan, Michael Chan
In-Reply-To: <20110413195146.25901.72193.stgit@gitlad.jf.intel.com>
Physical identification frequency based on how it was done prior to the
introduction of set_phys_id. Compile tested only.
Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
Cc: Michael Chan <mchan@broadcom.com>
---
drivers/net/bnx2.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c
index 0a52079..bf729ee 100644
--- a/drivers/net/bnx2.c
+++ b/drivers/net/bnx2.c
@@ -7473,7 +7473,7 @@ bnx2_set_phys_id(struct net_device *dev, enum ethtool_phys_id_state state)
bp->leds_save = REG_RD(bp, BNX2_MISC_CFG);
REG_WR(bp, BNX2_MISC_CFG, BNX2_MISC_CFG_LEDMODE_MAC);
- return -EINVAL;
+ return 1; /* cycle on/off once per second */
case ETHTOOL_ID_ON:
REG_WR(bp, BNX2_EMAC_LED, BNX2_EMAC_LED_OVERRIDE |
^ permalink raw reply related
* [net-next-2.6 RFC PATCH v2 04/13] bnx2x: set ethtool set_phys_id on/off cycle frequency to 1/sec
From: Bruce Allan @ 2011-04-13 19:59 UTC (permalink / raw)
To: netdev; +Cc: Bruce Allan, Eilon Greenstein
In-Reply-To: <20110413195146.25901.72193.stgit@gitlad.jf.intel.com>
Physical identification frequency based on how it was done prior to the
introduction of set_phys_id. Compile tested only.
Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
Cc: Eilon Greenstein <eilong@broadcom.com>
---
drivers/net/bnx2x/bnx2x_ethtool.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/bnx2x/bnx2x_ethtool.c b/drivers/net/bnx2x/bnx2x_ethtool.c
index ad7d91e..0a5e88d 100644
--- a/drivers/net/bnx2x/bnx2x_ethtool.c
+++ b/drivers/net/bnx2x/bnx2x_ethtool.c
@@ -2025,7 +2025,7 @@ static int bnx2x_set_phys_id(struct net_device *dev,
switch (state) {
case ETHTOOL_ID_ACTIVE:
- return -EINVAL;
+ return 1; /* cycle on/off once per second */
case ETHTOOL_ID_ON:
bnx2x_set_led(&bp->link_params, &bp->link_vars,
^ permalink raw reply related
* [net-next-2.6 RFC PATCH v2 05/13] cxgb3: set ethtool set_phys_id on/off cycle frequency to 1/sec
From: Bruce Allan @ 2011-04-13 19:59 UTC (permalink / raw)
To: netdev; +Cc: Bruce Allan, Divy Le Ray
In-Reply-To: <20110413195146.25901.72193.stgit@gitlad.jf.intel.com>
Physical identification frequency based on how it was done prior to the
introduction of set_phys_id. Compile tested only.
Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
Cc: Divy Le Ray <divy@chelsio.com>
---
drivers/net/cxgb3/cxgb3_main.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/cxgb3/cxgb3_main.c b/drivers/net/cxgb3/cxgb3_main.c
index 802c7a7..a087e06 100644
--- a/drivers/net/cxgb3/cxgb3_main.c
+++ b/drivers/net/cxgb3/cxgb3_main.c
@@ -1757,7 +1757,7 @@ static int set_phys_id(struct net_device *dev,
switch (state) {
case ETHTOOL_ID_ACTIVE:
- return -EINVAL;
+ return 1; /* cycle on/off once per second */
case ETHTOOL_ID_OFF:
t3_set_reg_field(adapter, A_T3DBG_GPIO_EN, F_GPIO0_OUT_VAL, 0);
^ permalink raw reply related
* [net-next-2.6 RFC PATCH v2 06/13] ewrk3: set ethtool set_phys_id on/off cycle frequency to 2/sec
From: Bruce Allan @ 2011-04-13 19:59 UTC (permalink / raw)
To: netdev; +Cc: Bruce Allan
In-Reply-To: <20110413195146.25901.72193.stgit@gitlad.jf.intel.com>
Physical identification frequency based on how it was done prior to the
introduction of set_phys_id. Not tested.
Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
---
drivers/net/ewrk3.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/ewrk3.c b/drivers/net/ewrk3.c
index c7ce443..17b6027 100644
--- a/drivers/net/ewrk3.c
+++ b/drivers/net/ewrk3.c
@@ -1618,7 +1618,7 @@ static int ewrk3_set_phys_id(struct net_device *dev,
/* Prevent ISR from twiddling the LED */
lp->led_mask = 0;
spin_unlock_irq(&lp->hw_lock);
- return -EINVAL;
+ return 2; /* cycle on/off twice per second */
case ETHTOOL_ID_ON:
cr = inb(EWRK3_CR);
^ permalink raw reply related
* [net-next-2.6 RFC PATCH v2 07/13] niu: set ethtool set_phys_id on/off cycle frequency to 1/sec
From: Bruce Allan @ 2011-04-13 19:59 UTC (permalink / raw)
To: netdev; +Cc: Bruce Allan
In-Reply-To: <20110413195146.25901.72193.stgit@gitlad.jf.intel.com>
Physical identification frequency based on how it was done prior to the
introduction of set_phys_id. Compile tested only.
Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
---
drivers/net/niu.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/niu.c b/drivers/net/niu.c
index 3fa1e9c..ea2272f 100644
--- a/drivers/net/niu.c
+++ b/drivers/net/niu.c
@@ -7896,7 +7896,7 @@ static int niu_set_phys_id(struct net_device *dev,
switch (state) {
case ETHTOOL_ID_ACTIVE:
np->orig_led_state = niu_led_state_save(np);
- return -EINVAL;
+ return 1; /* cycle on/off once per second */
case ETHTOOL_ID_ON:
niu_force_led(np, 1);
^ permalink raw reply related
* [net-next-2.6 RFC PATCH v2 08/13] pcnet32: set ethtool set_phys_id on/off cycle frequency to 2/sec
From: Bruce Allan @ 2011-04-13 19:59 UTC (permalink / raw)
To: netdev; +Cc: Bruce Allan, Don Fry
In-Reply-To: <20110413195146.25901.72193.stgit@gitlad.jf.intel.com>
Physical identification frequency based on how it was done prior to the
introduction of set_phys_id. Compile tested only.
Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
Cc: Don Fry <pcnet32@frontier.com>
---
drivers/net/pcnet32.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c
index e89afb9..0a1efba 100644
--- a/drivers/net/pcnet32.c
+++ b/drivers/net/pcnet32.c
@@ -1038,7 +1038,7 @@ static int pcnet32_set_phys_id(struct net_device *dev,
for (i = 4; i < 8; i++)
lp->save_regs[i - 4] = a->read_bcr(ioaddr, i);
spin_unlock_irqrestore(&lp->lock, flags);
- return -EINVAL;
+ return 2; /* cycle on/off twice per second */
case ETHTOOL_ID_ON:
case ETHTOOL_ID_OFF:
^ permalink raw reply related
* [net-next-2.6 RFC PATCH v2 09/13] s2io: set ethtool set_phys_id on/off cycle frequency to 1/sec
From: Bruce Allan @ 2011-04-13 19:59 UTC (permalink / raw)
To: netdev; +Cc: Bruce Allan, Jon Mason
In-Reply-To: <20110413195146.25901.72193.stgit@gitlad.jf.intel.com>
Physical identification frequency based on how it was done prior to the
introduction of set_phys_id. Compile tested only.
Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
Cc: Jon Mason <jdmason@kudzu.us>
---
drivers/net/s2io.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c
index 2d5cc61..2302d97 100644
--- a/drivers/net/s2io.c
+++ b/drivers/net/s2io.c
@@ -5541,7 +5541,7 @@ static int s2io_ethtool_set_led(struct net_device *dev,
switch (state) {
case ETHTOOL_ID_ACTIVE:
sp->adapt_ctrl_org = readq(&bar0->gpio_control);
- return -EINVAL;
+ return 1; /* cycle on/off once per second */
case ETHTOOL_ID_ON:
s2io_set_led(sp, true);
^ permalink raw reply related
* [net-next-2.6 RFC PATCH v2 11/13] skge: set ethtool set_phys_id on/off cycle frequency to 2/sec
From: Bruce Allan @ 2011-04-13 19:59 UTC (permalink / raw)
To: netdev; +Cc: Bruce Allan, Stephen Hemminger
In-Reply-To: <20110413195146.25901.72193.stgit@gitlad.jf.intel.com>
Physical identification frequency based on how it was done prior to the
introduction of set_phys_id. Compile tested only.
Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
Cc: Stephen Hemminger <shemminger@linux-foundation.org>
---
drivers/net/skge.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/skge.c b/drivers/net/skge.c
index 310dcbc..176d784 100644
--- a/drivers/net/skge.c
+++ b/drivers/net/skge.c
@@ -753,7 +753,7 @@ static int skge_set_phys_id(struct net_device *dev,
switch (state) {
case ETHTOOL_ID_ACTIVE:
- return -EINVAL;
+ return 2; /* cycle on/off twice per second */
case ETHTOOL_ID_ON:
skge_led(skge, LED_MODE_TST);
^ permalink raw reply related
* [net-next-2.6 RFC PATCH v2 12/13] sky2: set ethtool set_phys_id on/off cycle frequency to 1/sec
From: Bruce Allan @ 2011-04-13 19:59 UTC (permalink / raw)
To: netdev; +Cc: Bruce Allan, Stephen Hemminger
In-Reply-To: <20110413195146.25901.72193.stgit@gitlad.jf.intel.com>
Physical identification frequency based on how it was done prior to the
introduction of set_phys_id. Compile tested only.
Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
Cc: Stephen Hemminger <shemminger@linux-foundation.org>
---
drivers/net/sky2.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c
index a4b8fe5..c8d0451 100644
--- a/drivers/net/sky2.c
+++ b/drivers/net/sky2.c
@@ -3813,7 +3813,7 @@ static int sky2_set_phys_id(struct net_device *dev,
switch (state) {
case ETHTOOL_ID_ACTIVE:
- return -EINVAL;
+ return 1; /* cycle on/off once per second */
case ETHTOOL_ID_INACTIVE:
sky2_led(sky2, MO_LED_NORM);
break;
^ permalink raw reply related
* [net-next-2.6 RFC PATCH v2 10/13] sfc: set ethtool set_phys_id on/off cycle frequency to 1/sec
From: Bruce Allan @ 2011-04-13 19:59 UTC (permalink / raw)
To: netdev
Cc: Bruce Allan, Solarflare linux maintainers, Steve Hodgson,
Ben Hutchings
In-Reply-To: <20110413195146.25901.72193.stgit@gitlad.jf.intel.com>
Physical identification frequency based on how it was done prior to the
introduction of set_phys_id. Compile tested only.
Also fixed the compile warning re. "mode" may be used uninitialized.
Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
Cc: Solarflare linux maintainers <linux-net-drivers@solarflare.com>
Cc: Steve Hodgson <shodgson@solarflare.com>
Cc: Ben Hutchings <bhutchings@solarflare.com>
---
drivers/net/sfc/ethtool.c | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c
index 644f7c1..5d8468f 100644
--- a/drivers/net/sfc/ethtool.c
+++ b/drivers/net/sfc/ethtool.c
@@ -182,7 +182,7 @@ static int efx_ethtool_phys_id(struct net_device *net_dev,
enum ethtool_phys_id_state state)
{
struct efx_nic *efx = netdev_priv(net_dev);
- enum efx_led_mode mode;
+ enum efx_led_mode mode = EFX_LED_DEFAULT;
switch (state) {
case ETHTOOL_ID_ON:
@@ -194,8 +194,8 @@ static int efx_ethtool_phys_id(struct net_device *net_dev,
case ETHTOOL_ID_INACTIVE:
mode = EFX_LED_DEFAULT;
break;
- default:
- return -EINVAL;
+ case ETHTOOL_ID_ACTIVE:
+ return 1; /* cycle on/off once per second */
}
efx->type->set_id_led(efx, mode);
^ permalink raw reply related
* [net-next-2.6 RFC PATCH v2 13/13] tg3: set ethtool set_phys_id on/off cycle frequency to 1/sec
From: Bruce Allan @ 2011-04-13 19:59 UTC (permalink / raw)
To: netdev; +Cc: Bruce Allan, Matt Carlson, Michael Chan
In-Reply-To: <20110413195146.25901.72193.stgit@gitlad.jf.intel.com>
Physical identification frequency based on how it was done prior to the
introduction of set_phys_id. Compile tested only.
Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
Cc: Matt Carlson <mcarlson@broadcom.com>
Cc: Michael Chan <mchan@broadcom.com>
---
drivers/net/tg3.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c
index 9d7defc..7c1a9dd 100644
--- a/drivers/net/tg3.c
+++ b/drivers/net/tg3.c
@@ -10292,7 +10292,7 @@ static int tg3_set_phys_id(struct net_device *dev,
switch (state) {
case ETHTOOL_ID_ACTIVE:
- return -EINVAL;
+ return 1; /* cycle on/off once per second */
case ETHTOOL_ID_ON:
tw32(MAC_LED_CTRL, LED_CTRL_LNKLED_OVERRIDE |
^ permalink raw reply related
* Re: [net-next-2.6 RFC PATCH v2 00/13] ethtool: allow custom interval for
From: Ben Hutchings @ 2011-04-13 20:11 UTC (permalink / raw)
To: Bruce Allan; +Cc: netdev
In-Reply-To: <20110413195146.25901.72193.stgit@gitlad.jf.intel.com>
On Wed, 2011-04-13 at 12:58 -0700, Bruce Allan wrote:
> physical identification
>
> The following series changes the recently added ethtool set_phys_id
> functions to allow drivers to provide a frequency at which to cycle
> through an on/off identifier via software if/when the capability is
> not provided by hardware.
[...]
The first patch leaves all the drivers broken temporarily. Since the
change in each driver is trivial, I think you can squash this all into
one patch.
Ben.
--
Ben Hutchings, Senior Software Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply
* Re: [Bug 32772] New: PROBLEM: kernel BUG at net/ipv4/inetpeer.c:386
From: Dmitry Novikov @ 2011-04-13 20:14 UTC (permalink / raw)
To: David Miller; +Cc: eric.dumazet, shemminger, netdev
In-Reply-To: <20110406.111649.193697123.davem@davemloft.net>
Hello.
Crash again after 7 days of uptime. slub_nomerge is set
[559353.216526] ------------[ cut here ]------------
[559353.217494] kernel BUG at net/ipv4/inetpeer.c:386!
[559353.217494] invalid opcode: 0000 [#1] SMP
[559353.217494] last sysfs file: /sys/module/nf_conntrack_pptp/initstate
[559353.217494] Modules linked in: nf_nat_pptp nf_nat_proto_gre
nf_conntrack_pptp nf_conntrack_proto_gre nf_nat_ftp nf_conntrack_ftp
ipt_REJECT xt_state xt_tcpudp xt_multiport ip_set iptable_filter
iptable_mangle iptable_nat nf_nat nf_conntrack_ipv4 nf_conntrack
nf_defrag_ipv4 ip_tables x_tables act_police cls_u32 sch_ingress
sch_tbf 8021q garp bridge ipv6 stp llc loop intel_agp intel_gtt
agpgart rng_core pcspkr i2c_i801 i2c_core processor thermal_sys
parport_pc evdev parport serio_raw tpm_tis tpm button tpm_bios ext3
jbd mbcache sd_mod crc_t10dif ata_generic ata_piix libata scsi_mod
uhci_hcd ide_pci_generic e1000e ehci_hcd igb r8169 ide_core dca mii
usbcore nls_base [last unloaded: scsi_wait_scan]
[559353.217494]
[559353.217494] Pid: 0, comm: kworker/0:0 Not tainted
2.6.38-demyan-1.1demyan #1 Gigabyte Technology Co., Ltd.
G41MT-ES2L/G41MT-ES2L
[559353.217494] EIP: 0060:[<c11e0caa>] EFLAGS: 00010287 CPU: 1
[559353.217494] EIP is at unlink_from_pool+0x85/0x14a
[559353.217494] EAX: c125ff04 EBX: efcb09c0 ECX: abfd6970 EDX: ee6d77c4
[559353.217494] ESI: c1333338 EDI: f4c91bfc EBP: abfea42e ESP: f4c91ba8
[559353.217494] DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068
[559353.217494] Process kworker/0:0 (pid: 0, ti=f4c90000 task=f4c6a400
task.ti=f4c8c000)
[559353.217494] Stack:
[559353.217494] f351c790 00000001 abfd6970 c133333c c1333338 efc6b384
efe2af80 efcd1c04
[559353.217494] f3cc2784 ef3f62c4 f251da80 ef054b40 efcdc300 f0373dc0
f429a144 ef254a80
[559353.217494] ed4e6340 f0705f40 efcdb580 f05261c0 ee6d77c4 f4c91cb4
f351c790 f4c91c78
[559353.217494] Call Trace:
[559353.217494] [<c120f068>] ? fib4_rule_action+0x40/0x4d
[559353.217494] [<c11d1be3>] ? fib_rules_lookup+0x8d/0xe4
[559353.217494] [<c11e0de9>] ? cleanup_once+0x7a/0x7f
[559353.217494] [<c11e0fa9>] ? inet_getpeer+0x1bb/0x1dc
[559353.217494] [<c11dc073>] ? nf_ct_attach+0x12/0x13
[559353.217494] [<c1202404>] ? icmp_glue_bits+0x65/0x6a
[559353.217494] [<c11e4109>] ? ip_append_data+0x595/0x850
[559353.217494] [<c11e025d>] ? rt_bind_peer+0x1d/0x3d
[559353.217494] [<c11e029f>] ? __ip_select_ident+0x22/0xa6
[559353.217494] [<c11e4f60>] ? ip_push_pending_frames+0x206/0x2cb
[559353.217494] [<c120301b>] ? icmp_send+0x4fe/0x523
[559353.217494] [<f81a6b09>] ? ____nf_conntrack_find+0xfa/0x142 [nf_conntrack]
[559353.217494] [<f81a8069>] ? nf_conntrack_in+0x4f3/0x5e3 [nf_conntrack]
[559353.217494] [<f815c536>] ? ipt_do_table+0x4bc/0x4eb [ip_tables]
[559353.217494] [<c11e2949>] ? ip_forward+0x2ef/0x316
[559353.217494] [<c11e13da>] ? ip_rcv_finish+0x2fa/0x31f
[559353.217494] [<c11c1b3c>] ? __netif_receive_skb+0x405/0x42c
[559353.217494] [<c11c1a63>] ? __netif_receive_skb+0x32c/0x42c
[559353.217494] [<c1047585>] ? ktime_get_real+0x10/0x2d
[559353.217494] [<c11c2547>] ? netif_receive_skb+0x5a/0x5f
[559353.217494] [<c11c25ff>] ? napi_skb_finish+0x1b/0x30
[559353.217494] [<f8104723>] ? igb_poll+0x649/0x94a [igb]
[559353.217494] [<c1007765>] ? sched_clock+0x9/0xd
[559353.217494] [<c1030094>] ? wait_consider_task+0x977/0xa91
[559353.217494] [<c104438f>] ? sched_clock_local+0x17/0x13d
[559353.217494] [<c11c2b7b>] ? net_rx_action+0x90/0x150
[559353.217494] [<c1031f12>] ? __do_softirq+0x75/0x10e
[559353.217494] [<c1031e9d>] ? __do_softirq+0x0/0x10e
[559353.217494] <IRQ>
[559353.217494] [<c1031df3>] ? irq_exit+0x31/0x64
[559353.217494] [<c1004397>] ? do_IRQ+0x73/0x84
[559353.217494] [<c1003429>] ? common_interrupt+0x29/0x30
[559353.217494] [<c10089b4>] ? mwait_idle+0x4f/0x59
[559353.217494] [<c10021ef>] ? cpu_idle+0x46/0x63
[559353.217494] Code: 24 08 39 cd 75 09 42 3b 54 24 04 7c e9 eb 18 3b
6c 24 08 8d 50 04 0f 42 d0 89 17 83 c7 04 8b 02 3d 04 ff 25 c1 75 bb
39 d8 74 04 <0f> 0b eb fe 8d 6f fc 81 3b 04 ff 25 c1 89 6c 24 08 75 0d
8b 47
[559353.217494] EIP: [<c11e0caa>] unlink_from_pool+0x85/0x14a SS:ESP
0068:f4c91ba8
[559354.302112] ---[ end trace 55cdab910854890a ]---
[559354.316239] Kernel panic - not syncing: Fatal exception in interrupt
[559354.335557] Pid: 0, comm: kworker/0:0 Tainted: G D
2.6.38-demyan-1.1demyan #1
[559354.359578] Call Trace:
[559354.367198] [<c1231f71>] ? panic+0x4d/0x137
[559354.380274] [<c1005722>] ? oops_end+0x8e/0x99
[559354.393871] [<c1003a0e>] ? do_invalid_op+0x0/0x75
[559354.408509] [<c1003a7a>] ? do_invalid_op+0x6c/0x75
[559354.423407] [<c11e0caa>] ? unlink_from_pool+0x85/0x14a
[559354.439345] [<c120f068>] ? fib4_rule_action+0x40/0x4d
[559354.455022] [<c11d1be3>] ? fib_rules_lookup+0x8d/0xe4
[559354.470700] [<c120f122>] ? fib_lookup+0x31/0x3f
[559354.484818] [<c11ca4f1>] ? neigh_lookup+0x8e/0x96
[559354.499454] [<c123464e>] ? error_code+0x5a/0x60
[559354.513571] [<c1003a0e>] ? do_invalid_op+0x0/0x75
[559354.528208] [<c11e0caa>] ? unlink_from_pool+0x85/0x14a
[559354.544146] [<c120f068>] ? fib4_rule_action+0x40/0x4d
[559354.559823] [<c11d1be3>] ? fib_rules_lookup+0x8d/0xe4
[559354.575500] [<c11e0de9>] ? cleanup_once+0x7a/0x7f
[559354.590137] [<c11e0fa9>] ? inet_getpeer+0x1bb/0x1dc
[559354.605297] [<c11dc073>] ? nf_ct_attach+0x12/0x13
[559354.619934] [<c1202404>] ? icmp_glue_bits+0x65/0x6a
[559354.635090] [<c11e4109>] ? ip_append_data+0x595/0x850
[559354.650767] [<c11e025d>] ? rt_bind_peer+0x1d/0x3d
[559354.665405] [<c11e029f>] ? __ip_select_ident+0x22/0xa6
[559354.681344] [<c11e4f60>] ? ip_push_pending_frames+0x206/0x2cb
[559354.699099] [<c120301b>] ? icmp_send+0x4fe/0x523
[559354.713479] [<f81a6b09>] ? ____nf_conntrack_find+0xfa/0x142 [nf_conntrack]
[559354.734615] [<f81a8069>] ? nf_conntrack_in+0x4f3/0x5e3 [nf_conntrack]
[559354.754452] [<f815c536>] ? ipt_do_table+0x4bc/0x4eb [ip_tables]
[559354.772731] [<c11e2949>] ? ip_forward+0x2ef/0x316
[559354.787366] [<c11e13da>] ? ip_rcv_finish+0x2fa/0x31f
[559354.802785] [<c11c1b3c>] ? __netif_receive_skb+0x405/0x42c
[559354.819762] [<c11c1a63>] ? __netif_receive_skb+0x32c/0x42c
[559354.836738] [<c1047585>] ? ktime_get_real+0x10/0x2d
[559354.851901] [<c11c2547>] ? netif_receive_skb+0x5a/0x5f
[559354.867835] [<c11c25ff>] ? napi_skb_finish+0x1b/0x30
[559354.883254] [<f8104723>] ? igb_poll+0x649/0x94a [igb]
[559354.898930] [<c1007765>] ? sched_clock+0x9/0xd
[559354.912786] [<c1030094>] ? wait_consider_task+0x977/0xa91
[559354.929502] [<c104438f>] ? sched_clock_local+0x17/0x13d
[559354.945701] [<c11c2b7b>] ? net_rx_action+0x90/0x150
[559354.960857] [<c1031f12>] ? __do_softirq+0x75/0x10e
[559354.975756] [<c1031e9d>] ? __do_softirq+0x0/0x10e
[559354.990393] <IRQ> [<c1031df3>] ? irq_exit+0x31/0x64
[559355.005862] [<c1004397>] ? do_IRQ+0x73/0x84
[559355.018941] [<c1003429>] ? common_interrupt+0x29/0x30
[559355.034618] [<c10089b4>] ? mwait_idle+0x4f/0x59
[559355.048734] [<c10021ef>] ? cpu_idle+0x46/0x63
[559355.062333] Rebooting in 5 seconds..
^ permalink raw reply
* Re: [Bug 32772] New: PROBLEM: kernel BUG at net/ipv4/inetpeer.c:386
From: David Miller @ 2011-04-13 20:24 UTC (permalink / raw)
To: dimetrios; +Cc: eric.dumazet, shemminger, netdev
In-Reply-To: <BANLkTi=PTqcYd1wO_QzQTtg_PWEq2fAJMg@mail.gmail.com>
From: Dmitry Novikov <dimetrios@gmail.com>
Date: Wed, 13 Apr 2011 23:14:03 +0300
> Crash again after 7 days of uptime. slub_nomerge is set
Looks like too deep stack, try this patch which is in net-2.6:
--------------------
inetpeer: reduce stack usage
On 64bit arches, we use 752 bytes of stack when cleanup_once() is called
from inet_getpeer().
Lets share the avl stack to save ~376 bytes.
Before patch :
# objdump -d net/ipv4/inetpeer.o | scripts/checkstack.pl
0x000006c3 unlink_from_pool [inetpeer.o]: 376
0x00000721 unlink_from_pool [inetpeer.o]: 376
0x00000cb1 inet_getpeer [inetpeer.o]: 376
0x00000e6d inet_getpeer [inetpeer.o]: 376
0x0004 inet_initpeers [inetpeer.o]: 112
# size net/ipv4/inetpeer.o
text data bss dec hex filename
5320 432 21 5773 168d net/ipv4/inetpeer.o
After patch :
objdump -d net/ipv4/inetpeer.o | scripts/checkstack.pl
0x00000c11 inet_getpeer [inetpeer.o]: 376
0x00000dcd inet_getpeer [inetpeer.o]: 376
0x00000ab9 peer_check_expire [inetpeer.o]: 328
0x00000b7f peer_check_expire [inetpeer.o]: 328
0x0004 inet_initpeers [inetpeer.o]: 112
# size net/ipv4/inetpeer.o
text data bss dec hex filename
5163 432 21 5616 15f0 net/ipv4/inetpeer.o
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Cc: Scot Doyle <lkml@scotdoyle.com>
Cc: Stephen Hemminger <shemminger@vyatta.com>
Cc: Hiroaki SHIMODA <shimoda.hiroaki@gmail.com>
Reviewed-by: Hiroaki SHIMODA <shimoda.hiroaki@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
---
net/ipv4/inetpeer.c | 13 +++++++------
1 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c
index dd1b20e..9df4e63 100644
--- a/net/ipv4/inetpeer.c
+++ b/net/ipv4/inetpeer.c
@@ -354,7 +354,8 @@ static void inetpeer_free_rcu(struct rcu_head *head)
}
/* May be called with local BH enabled. */
-static void unlink_from_pool(struct inet_peer *p, struct inet_peer_base *base)
+static void unlink_from_pool(struct inet_peer *p, struct inet_peer_base *base,
+ struct inet_peer __rcu **stack[PEER_MAXDEPTH])
{
int do_free;
@@ -368,7 +369,6 @@ static void unlink_from_pool(struct inet_peer *p, struct inet_peer_base *base)
* We use refcnt=-1 to alert lockless readers this entry is deleted.
*/
if (atomic_cmpxchg(&p->refcnt, 1, -1) == 1) {
- struct inet_peer __rcu **stack[PEER_MAXDEPTH];
struct inet_peer __rcu ***stackptr, ***delp;
if (lookup(&p->daddr, stack, base) != p)
BUG();
@@ -422,7 +422,7 @@ static struct inet_peer_base *peer_to_base(struct inet_peer *p)
}
/* May be called with local BH enabled. */
-static int cleanup_once(unsigned long ttl)
+static int cleanup_once(unsigned long ttl, struct inet_peer __rcu **stack[PEER_MAXDEPTH])
{
struct inet_peer *p = NULL;
@@ -454,7 +454,7 @@ static int cleanup_once(unsigned long ttl)
* happen because of entry limits in route cache. */
return -1;
- unlink_from_pool(p, peer_to_base(p));
+ unlink_from_pool(p, peer_to_base(p), stack);
return 0;
}
@@ -524,7 +524,7 @@ struct inet_peer *inet_getpeer(struct inetpeer_addr *daddr, int create)
if (base->total >= inet_peer_threshold)
/* Remove one less-recently-used entry. */
- cleanup_once(0);
+ cleanup_once(0, stack);
return p;
}
@@ -540,6 +540,7 @@ static void peer_check_expire(unsigned long dummy)
{
unsigned long now = jiffies;
int ttl, total;
+ struct inet_peer __rcu **stack[PEER_MAXDEPTH];
total = compute_total();
if (total >= inet_peer_threshold)
@@ -548,7 +549,7 @@ static void peer_check_expire(unsigned long dummy)
ttl = inet_peer_maxttl
- (inet_peer_maxttl - inet_peer_minttl) / HZ *
total / inet_peer_threshold * HZ;
- while (!cleanup_once(ttl)) {
+ while (!cleanup_once(ttl, stack)) {
if (jiffies != now)
break;
}
--
1.7.4.3
^ permalink raw reply related
* Re: [net-next-2.6 RFC PATCH v2 01/13] ethtool: allow custom interval for physical identification
From: Ben Hutchings @ 2011-04-13 20:25 UTC (permalink / raw)
To: Bruce Allan; +Cc: netdev
In-Reply-To: <20110413195851.25901.8139.stgit@gitlad.jf.intel.com>
On Wed, 2011-04-13 at 12:58 -0700, Bruce Allan wrote:
> When physical identification of an adapter is done by toggling the
> mechanism on and off through software utilizing the set_phys_id operation,
> it is done with a fixed duration for both on and off states. Some drivers
> may want to set a custom duration for the on/off intervals. This patch
> changes the API so the return code from the driver's entry point when it
> is called with ETHTOOL_ID_ACTIVE can specify the frequency at which to
> cycle the on/off states.
[...]
> @@ -1655,23 +1655,26 @@ static int ethtool_phys_id(struct net_device *dev, void __user *useraddr)
> schedule_timeout_interruptible(
> id.data ? (id.data * HZ) : MAX_SCHEDULE_TIMEOUT);
> } else {
> - /* Driver expects to be called periodically */
> + /* Driver expects to be called using the frequency in rc */
> + int i = 0, interval = (HZ / (rc * 2));
> +
> do {
> rtnl_lock();
> rc = dev->ethtool_ops->set_phys_id(dev, ETHTOOL_ID_ON);
> rtnl_unlock();
> if (rc)
> break;
> - schedule_timeout_interruptible(HZ / 2);
> + schedule_timeout_interruptible(interval);
>
> rtnl_lock();
> rc = dev->ethtool_ops->set_phys_id(dev, ETHTOOL_ID_OFF);
> rtnl_unlock();
> if (rc)
> break;
> - schedule_timeout_interruptible(HZ / 2);
> + schedule_timeout_interruptible(interval);
> } while (!signal_pending(current) &&
> - (id.data == 0 || --id.data != 0));
> + (id.data == 0 ||
> + (++i * 2 * interval) < (id.data * HZ)));
[...]
I'm sure there ought to be a clearer way to do this, and to avoid any
weird effects from integer overflow in the multiplication. How about
using an inner loop for each second:
/* Driver expects to be called at twice the frequency in rc */
int n = rc * 2, i, interval = HZ / n;
do {
i = n;
do {
rtnl_lock();
rc = dev->ethtool_ops->set_phys_id(
dev, (i & 1) ? ETHTOOL_ID_OFF : ETHTOOL_ID_ON);
rtnl_unlock();
if (rc)
break;
schedule_timeout_interruptible(interval);
} while (!signal_pending(current) && --i != 0);
} while (!signal_pending(current) &&
(id.data == 0 || --id.data != 0));
Ben.
--
Ben Hutchings, Senior Software Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
^ permalink raw reply
* Re: [net-next-2.6 RFC PATCH v2 00/13] ethtool: allow custom interval for
From: David Miller @ 2011-04-13 20:25 UTC (permalink / raw)
To: bhutchings; +Cc: bruce.w.allan, netdev
In-Reply-To: <1302725464.2873.7.camel@bwh-desktop>
From: Ben Hutchings <bhutchings@solarflare.com>
Date: Wed, 13 Apr 2011 21:11:04 +0100
> On Wed, 2011-04-13 at 12:58 -0700, Bruce Allan wrote:
>> physical identification
>>
>> The following series changes the recently added ethtool set_phys_id
>> functions to allow drivers to provide a frequency at which to cycle
>> through an on/off identifier via software if/when the capability is
>> not provided by hardware.
> [...]
>
> The first patch leaves all the drivers broken temporarily. Since the
> change in each driver is trivial, I think you can squash this all into
> one patch.
Agreed.
^ permalink raw reply
* Re: [PATCH] Add Qualcomm Gobi 2000/3000 driver.
From: David Miller @ 2011-04-13 20:37 UTC (permalink / raw)
To: ellyjones; +Cc: netdev, dcbw, mjg59, jglasgow, trond
In-Reply-To: <20110413190023.GC1652@google.com>
From: Elly Jones <ellyjones@google.com>
Date: Wed, 13 Apr 2011 15:00:24 -0400
> +void qcusbnet_put(struct qcusbnet *dev)
> +{
> + mutex_lock(&qcusbnet_lock);
> + kref_put(&dev->refcount, free_dev);
> + mutex_unlock(&qcusbnet_lock);
> +}
This locking looks excessive, and shouldn't be needed simply to
release a reference to an object.
> +int qc_suspend(struct usb_interface *iface, pm_message_t event)
> +{
> + struct usbnet *usbnet;
> + struct qcusbnet *dev;
> +
> + if (!iface)
> + return -ENOMEM;
When is qc_suspend() called with a NULL iface arguemnt?
> +static int qc_resume(struct usb_interface *iface)
> +{
> + struct usbnet *usbnet;
> + struct qcusbnet *dev;
> + int ret;
> + int oldstate;
> +
> + if (iface == 0)
> + return -ENOMEM;
Likewise, and if it is needed use consistent tests for NULL. Testing
against the integer "0" is definitely the wrong way.
> + if (usb_endpoint_dir_in(&endpoint->desc)
> + && !usb_endpoint_xfer_int(&endpoint->desc)) {
Please do it like this:
if (A &&
B) {
Not like:
if (A
&& B
the latter looks awful at best.
> + if (!usbnet || !usbnet->net) {
> + DBG("failed to get usbnet device\n");
> + return;
> + }
> +
> + dev = (struct qcusbnet *)usbnet->data[0];
> + if (!dev) {
> + DBG("failed to get QMIDevice\n");
> + return;
> + }
These NULL checks are everywhere! Do we really _ever_ create a full
registered netdev with any of these things being NULL? I severely
doubt it.
> +static int qcnet_worker(void *arg)
> +{
> + struct list_head *node, *tmp;
> + unsigned long activeflags, listflags;
> + struct urbreq *req;
> + int status;
> + struct usb_device *usbdev;
> + struct worker *worker = arg;
> + if (!worker) {
> + DBG("passed null pointer\n");
> + return -EINVAL;
> + }
This NULL check is impossible, you register the worker function with an
explicit &dev->worker argument, so seeing NULL here is impossible.
> +static int qcnet_startxmit(struct sk_buff *skb, struct net_device *netdev)
> +{
> + unsigned long listflags;
> + struct qcusbnet *dev;
> + struct worker *worker;
> + struct urbreq *req;
> + void *data;
> + struct usbnet *usbnet = netdev_priv(netdev);
> +
> + DBG("\n");
> +
> + if (!usbnet || !usbnet->net) {
> + DBG("failed to get usbnet device\n");
> + return NETDEV_TX_BUSY;
> + }
> +
> + dev = (struct qcusbnet *)usbnet->data[0];
> + if (!dev) {
Again, kill this NULL check noise, all of it can't be necessary.
> + netdev->trans_start = jiffies;
Setting netdev->trans_start in drivers is expensive and deprecated,
please set netdev_queue->trans_start instead.
> +static int qcnet_open(struct net_device *netdev)
> +{
> + int status = 0;
> + struct qcusbnet *dev;
> + struct usbnet *usbnet = netdev_priv(netdev);
> +
> + if (!usbnet) {
> + DBG("failed to get usbnet device\n");
> + return -ENXIO;
> + }
> +
> + dev = (struct qcusbnet *)usbnet->data[0];
> + if (!dev) {
> + DBG("failed to get QMIDevice\n");
> + return -ENXIO;
> + }
Again, excessive NULL checks.
> +int qcnet_stop(struct net_device *netdev)
> +{
> + struct qcusbnet *dev;
> + struct usbnet *usbnet = netdev_priv(netdev);
> +
> + if (!usbnet || !usbnet->net) {
> + DBG("failed to get netdevice\n");
> + return -ENXIO;
> + }
> +
> + dev = (struct qcusbnet *)usbnet->data[0];
> + if (!dev) {
> + DBG("failed to get QMIDevice\n");
> + return -ENXIO;
> + }
Here too.
> +static u8 nibble(unsigned char c)
> +{
> + if (likely(isdigit(c)))
> + return c - '0';
> + c = toupper(c);
> + if (likely(isxdigit(c)))
> + return 10 + c - 'A';
> + return 0;
> +}
Remove this function and use hex_to_bin() instead.
^ permalink raw reply
* Re: [PATCH] NFS: Fix infinite loop in gss_create_upcall()
From: Bryan Schumaker @ 2011-04-13 20:42 UTC (permalink / raw)
To: Jiri Slaby
Cc: Trond Myklebust, Jiri Slaby, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b,
mm-commits-u79uwXL29TY76Z2rM5mHXA, ML netdev,
linux-nfs-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <4DA49F7F.8060005-AlSwsSmVLrQ@public.gmane.org>
On 04/12/2011 02:52 PM, Jiri Slaby wrote:
> On 04/12/2011 08:43 PM, Bryan Schumaker wrote:
>> On 04/12/2011 02:34 PM, Jiri Slaby wrote:
>>> On 04/12/2011 08:31 PM, Trond Myklebust wrote:
>>>>> Yes, it fixes the problem. But it waits 15s before it times out. This is
>>>>> inacceptable for automounted NFS dirs.
>>>>
>>>> I'm still confused as to why you are hitting it at all. In the normal
>>>> autonegotiation case, the client should be trying to use AUTH_SYS first
>>>> and then trying rpcsec_gss if and only if that fails.
>>>>
>>>> Are you really exporting a filesystem using AUTH_NULL as the only
>>>> supported flavour?
>>>
>>> I don't know, I connect to a nfs server which is not maintained by me.
>>> It looks like that. How can I find out?
>>
>> If you're not using gss for anything, you could try rmmod-ing rpcsec_gss_krb5 (and other rpcsec_gss_* modules).
>
> I don't have NFS in modules. It's all built-in. And this one is
> unconditionally selected because of CONFIG_NFS_V4.
Does this patch help?
- Bryan
We should attempt an AUTH_NULL style mount before
trying gss flavors. This should prevent a hang if
gss modules are loaded but the userspace program
isn't running.
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 9bf41ea..4e3c16b 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -2218,8 +2218,8 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
rpc_authflavor_t flav_array[NFS_MAX_SECFLAVORS + 2];
flav_array[0] = RPC_AUTH_UNIX;
- len = gss_mech_list_pseudoflavors(&flav_array[1]);
- flav_array[1+len] = RPC_AUTH_NULL;
+ flav_array[1] = RPC_AUTH_NULL;
+ len = gss_mech_list_pseudoflavors(&flav_array[2]);
len += 2;
for (i = 0; i < len; i++) {
>
> regards,
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox