From 28acc1a88c75b08b2ab502a546da61b131b86f5c Mon Sep 17 00:00:00 2001
From: Mike Lockwood <lockwood@android.com>
Date: Mon, 28 Jun 2010 16:19:32 -0400
Subject: [PATCH 101/696] USB: composite: Add usb_composite_force_reset utility to force enumeration

Use this rather than calling usb_gadget_disconnect and usb_gadget_connect
directly to avoid sending USB disconnect events to userspace when resetting
the bus.

Signed-off-by: Mike Lockwood <lockwood@android.com>
---
 drivers/usb/gadget/android.c   |   11 ++---------
 drivers/usb/gadget/composite.c |   29 +++++++++++++++++++++++++++--
 include/linux/usb/composite.h  |    4 ++++
 3 files changed, 33 insertions(+), 11 deletions(-)

diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index 7618a07..c7a5478 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -337,7 +337,7 @@ void android_enable_function(struct usb_function *f, int enable)
 			 */
 			list_for_each_entry(func, &android_config_driver.functions, list) {
 				if (!strcmp(func->name, "usb_mass_storage")) {
-					usb_function_set_enabled(f, !enable);
+					usb_function_set_enabled(func, !enable);
 					break;
 				}
 			}
@@ -348,14 +348,7 @@ void android_enable_function(struct usb_function *f, int enable)
 		device_desc.idProduct = __constant_cpu_to_le16(product_id);
 		if (dev->cdev)
 			dev->cdev->desc.idProduct = device_desc.idProduct;
-
-		/* force reenumeration */
-		if (dev->cdev && dev->cdev->gadget &&
-				dev->cdev->gadget->speed != USB_SPEED_UNKNOWN) {
-			usb_gadget_disconnect(dev->cdev->gadget);
-			msleep(10);
-			usb_gadget_connect(dev->cdev->gadget);
-		}
+		usb_composite_force_reset(dev->cdev);
 	}
 }
 
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index bf55532..c2684b5 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -107,6 +107,27 @@ void usb_function_set_enabled(struct usb_function *f, int enabled)
 	kobject_uevent(&f->dev->kobj, KOBJ_CHANGE);
 }
 
+
+void usb_composite_force_reset(struct usb_composite_dev *cdev)
+{
+	unsigned long			flags;
+
+	spin_lock_irqsave(&cdev->lock, flags);
+	/* force reenumeration */
+	if (cdev && cdev->gadget &&
+			cdev->gadget->speed != USB_SPEED_UNKNOWN) {
+		/* avoid sending a disconnect switch event until after we disconnect */
+		cdev->mute_switch = 1;
+		spin_unlock_irqrestore(&cdev->lock, flags);
+
+		usb_gadget_disconnect(cdev->gadget);
+		msleep(10);
+		usb_gadget_connect(cdev->gadget);
+	} else {
+		spin_unlock_irqrestore(&cdev->lock, flags);
+	}
+}
+
 /**
  * usb_add_function() - add a function to a configuration
  * @config: the configuration
@@ -1114,11 +1135,15 @@ static void composite_disconnect(struct usb_gadget *gadget)
 	spin_lock_irqsave(&cdev->lock, flags);
 	if (cdev->config)
 		reset_config(cdev);
+
 	if (composite->disconnect)
 		composite->disconnect(cdev);
-	spin_unlock_irqrestore(&cdev->lock, flags);
 
-	schedule_work(&cdev->switch_work);
+	if (cdev->mute_switch)
+		cdev->mute_switch = 0;
+	else
+		schedule_work(&cdev->switch_work);
+	spin_unlock_irqrestore(&cdev->lock, flags);
 }
 
 /*-------------------------------------------------------------------------*/
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index 4cc4680..37352fc 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -47,6 +47,7 @@
  */
 #define USB_GADGET_DELAYED_STATUS       0x7fff	/* Impossibly large value */
 
+struct usb_composite_dev;
 struct usb_configuration;
 
 /**
@@ -151,6 +152,7 @@ int usb_function_activate(struct usb_function *);
 int usb_interface_id(struct usb_configuration *, struct usb_function *);
 
 void usb_function_set_enabled(struct usb_function *, int);
+void usb_composite_force_reset(struct usb_composite_dev *);
 
 /**
  * ep_choose - select descriptor endpoint at current device speed
@@ -372,6 +374,8 @@ struct usb_composite_dev {
 	spinlock_t			lock;
 
 	struct switch_dev sdev;
+	/* used by usb_composite_force_reset to avoid signalling switch changes */
+	bool				mute_switch;
 	struct work_struct switch_work;
 };
 
-- 
1.7.1


