Pull Elly's Change from R12 in to 0.11.241.B Factory Branch Manually:

CHROMIUM: gobi: Remove worker.exit hack.

The exit member was added to hack around crosbug.com/14190, but we need a proper
fix. I discussed with olofj, and we think the right thing is to use a waitqueue
and wait for there to be work available or for the thread to be asked to exit.

BUG=chromium-os:14205
TEST=Adhoc
Build, modem status, suspend/resume

http://codereview.chromium.org/6913008/
diff --git a/drivers/net/usb/gobi/qcusbnet.c b/drivers/net/usb/gobi/qcusbnet.c
index 33e26a4..871a7e2 100644
--- a/drivers/net/usb/gobi/qcusbnet.c
+++ b/drivers/net/usb/gobi/qcusbnet.c
@@ -1,5 +1,5 @@
 /* qcusbnet.c - gobi network device
- * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ * 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
@@ -21,6 +21,8 @@
 #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"
@@ -66,6 +68,12 @@
 	return NULL;
 }
 
+static void wake_worker(struct worker *worker)
+{
+	atomic_inc(&worker->work_count);
+	wake_up(&worker->waitq);
+}
+
 int qc_suspend(struct usb_interface *iface, pm_message_t event)
 {
 	struct usbnet *usbnet;
@@ -148,7 +156,7 @@
 			return ret;
 		}
 
-		complete(&dev->worker.work);
+		wake_worker(&dev->worker);
 	} else {
 		DBG("nothing to resume\n");
 		return 0;
@@ -244,7 +252,7 @@
 	worker->active = ERR_PTR(-EAGAIN);
 	spin_unlock_irqrestore(&worker->active_lock, flags);
 	/* XXX-fix race against qcnet_stop()? */
-	complete(&worker->work);
+	wake_worker(worker);
 	usb_free_urb(urb);
 }
 
@@ -285,7 +293,18 @@
 	}
 	spin_unlock_irqrestore(&worker->urbs_lock, listflags);
 
-	complete(&worker->work);
+	wake_worker(worker);
+}
+
+static int worker_should_wake(struct worker *worker)
+{
+	if (kthread_should_stop())
+		return 1;
+	/* This is safe only because we are the only place that decrements this
+	 * counter. */
+	if (atomic_read(&worker->work_count))
+		return 1;
+	return 0;
 }
 
 static int qcnet_worker(void *arg)
@@ -306,26 +325,11 @@
 	DBG("traffic thread started\n");
 
 	while (!kthread_should_stop()) {
-		wait_for_completion_interruptible(&worker->work);
+		wait_event(worker->waitq, worker_should_wake(worker));
 
-		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);
-
+		if (kthread_should_stop())
 			break;
-		}
+		atomic_dec(&worker->work_count);
 
 		spin_lock_irqsave(&worker->active_lock, activeflags);
 		if (IS_ERR(worker->active) && PTR_ERR(worker->active) == -EAGAIN) {
@@ -380,12 +384,27 @@
 			worker->active = NULL;
 			spin_unlock_irqrestore(&worker->active_lock, activeflags);
 			usb_autopm_put_interface(worker->iface);
-			complete(&worker->work);
+			wake_worker(worker);
 		}
 
 		kfree(req);
 	}
 
+	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);
+
 	DBG("traffic thread exiting\n");
 	worker->thread = NULL;
 	return 0;
@@ -450,7 +469,7 @@
 	list_add_tail(&req->node, &worker->urbs);
 	spin_unlock_irqrestore(&worker->urbs_lock, listflags);
 
-	complete(&worker->work);
+	wake_worker(worker);
 
 	netdev->trans_start = jiffies;
 	dev_kfree_skb_any(skb);
@@ -482,7 +501,8 @@
 	dev->worker.active = NULL;
 	spin_lock_init(&dev->worker.urbs_lock);
 	spin_lock_init(&dev->worker.active_lock);
-	init_completion(&dev->worker.work);
+	atomic_set(&dev->worker.work_count, 0);
+	init_waitqueue_head(&dev->worker.waitq);
 
 	dev->worker.thread = kthread_run(qcnet_worker, &dev->worker, "qcnet_worker");
 	if (IS_ERR(dev->worker.thread)) {
@@ -520,7 +540,6 @@
 	}
 
 	qc_setdown(dev, DOWN_NET_IFACE_STOPPED);
-	complete(&dev->worker.work);
 	kthread_stop(dev->worker.thread);
 	DBG("thread stopped\n");
 
@@ -578,12 +597,24 @@
 
 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) {
@@ -629,8 +660,6 @@
 	dev->iface = iface;
 	memset(&(dev->meid), '0', 14);
 
-	DBG("Mac Address: %pM\n", dev->usbnet->net->dev_addr);
-
 	dev->valid = false;
 	memset(&dev->qmi, 0, sizeof(dev->qmi));
 
@@ -639,7 +668,8 @@
 	kref_init(&dev->refcount);
 	INIT_LIST_HEAD(&dev->node);
 	INIT_LIST_HEAD(&dev->qmi.clients);
-	init_completion(&dev->worker.work);
+	atomic_set(&dev->worker.work_count, 0);
+	init_waitqueue_head(&dev->worker.waitq);
 	spin_lock_init(&dev->qmi.clients_lock);
 
 	dev->down = 0;
@@ -655,6 +685,13 @@
 		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;
 }
diff --git a/drivers/net/usb/gobi/structs.h b/drivers/net/usb/gobi/structs.h
index 13b3788..4c8c23e 100644
--- a/drivers/net/usb/gobi/structs.h
+++ b/drivers/net/usb/gobi/structs.h
@@ -27,6 +27,7 @@
 #include <linux/cdev.h>
 #include <linux/kobject.h>
 #include <linux/kthread.h>
+#include <linux/wait.h>
 
 #include <linux/usb/usbnet.h>
 
@@ -49,7 +50,8 @@
 
 struct worker {
 	struct task_struct *thread;
-	struct completion work;
+	atomic_t work_count;
+	wait_queue_head_t waitq;
 	struct list_head urbs;
 	spinlock_t urbs_lock;
 	struct urb *active;