Re: [RFC/PATCH] Introduce USB Gadget Class

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Matthieu,

On Thu, Sep 09, 2010 at 04:17:34AM -0500, Matthieu CASTET wrote:
For information I am developing a something similar
for allowing building more than one controller in kernel.
My patch is much simpler, but it does only that.

yeah, I saw that now. It also doesn't work if you have a board with two
gadget controllers, right ?

I wanted to abstract most of the stuff, so we can add an atomic notifier
sending information about the gadget to whoever is interested. Charger
chips wants to know when the gadget is enumerated so they can fix the
input current to USB charging (for example).

I will have a look to your proposal.

the version attached probably has more to see, it shows better the
approach, I think.

--
balbi
>From 520bdacafbf7b767e6fb1d033e4c85d0ce763af5 Mon Sep 17 00:00:00 2001
From: Felipe Balbi <balbi@xxxxxx>
Date: Wed, 8 Sep 2010 16:34:14 +0300
Subject: [PATCH] usb: gadget: Introduce the Gadget class
Organization: Texas Instruments\n

this will allow us to register N gadget
controller drivers and register gadget drivers
based on whether that is already busy or not.

It's good, at a minimum, for development, but
it's also a real-life possibility.

NYET-Signed-off-by: Felipe Balbi <balbi@xxxxxx>
---
 drivers/usb/gadget/Makefile    |    1 +
 drivers/usb/gadget/gadget.c    |  339 ++++++++++++++++++++++++++++++++++++++++
 drivers/usb/musb/musb_gadget.c |  194 ++++++-----------------
 include/linux/usb/gadget.h     |   33 +++-
 4 files changed, 416 insertions(+), 151 deletions(-)
 create mode 100644 drivers/usb/gadget/gadget.c

diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 27283df..60ff266 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -5,6 +5,7 @@ ifeq ($(CONFIG_USB_GADGET_DEBUG),y)
 	EXTRA_CFLAGS		+= -DDEBUG
 endif
 
+obj-$(CONFIG_USB_GADGET)	+= gadget.o
 obj-$(CONFIG_USB_DUMMY_HCD)	+= dummy_hcd.o
 obj-$(CONFIG_USB_NET2280)	+= net2280.o
 obj-$(CONFIG_USB_AMD5536UDC)	+= amd5536udc.o
diff --git a/drivers/usb/gadget/gadget.c b/drivers/usb/gadget/gadget.c
new file mode 100644
index 0000000..fb13a22
--- /dev/null
+++ b/drivers/usb/gadget/gadget.c
@@ -0,0 +1,339 @@
+/**
+ * gadget.c - Core Gadget Framework
+ *
+ * Copyright (C) 2010 Texas Instruments
+ * Author: Felipe Balbi <balbi@xxxxxx>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2  of
+ * the License 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/err.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+static struct class *gadget_class;
+static struct device_type gadget_device_type;
+static LIST_HEAD(gadget_list);
+static spinlock_t gadget_lock;
+
+/* ------------------------------------------------------------------------- */
+
+static void usb_gadget_release(struct device *dev)
+{
+	dev_dbg(dev, "releasing '%s'\n", dev_name(dev));
+	kfree(dev);
+}
+
+int usb_add_gadget(struct device *parent, struct usb_gadget *gadget)
+{
+	struct device		*dev;
+	unsigned long		flags;
+	int			ret;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev) {
+		ret = -ENOMEM;
+		goto err1;
+	}
+
+	device_initialize(dev);
+
+	dev->class = gadget_class;
+	dev->type = &gadget_device_type;
+	dev->parent = parent;
+	dev->release = usb_gadget_release;
+	dev_set_drvdata(dev, gadget);
+	gadget->dev = dev;
+
+	ret = kobject_set_name(&dev->kobj, "%s", gadget->name);
+	if (ret)
+		goto err2;
+
+	ret = device_add(dev);
+	if (ret)
+		goto err2;
+
+	spin_lock_irqsave(&gadget_lock, flags);
+	list_add_tail(&gadget->list, &gadget_list);
+	spin_unlock_irqrestore(&gadget_lock, flags);
+
+	return 0;
+
+err2:
+	kfree(dev);
+
+err1:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_add_gadget);
+
+void usb_del_gadget(struct usb_gadget *gadget)
+{
+	unsigned long		flags;
+
+	device_unregister(gadget->dev);
+
+	spin_lock_irqsave(&gadget_lock, flags);
+	list_del(&gadget->list);
+	spin_unlock_irqrestore(&gadget_lock, flags);
+}
+EXPORT_SYMBOL_GPL(usb_del_gadget);
+
+/* ------------------------------------------------------------------------- */
+
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+	struct usb_gadget	*gadget = NULL;
+	unsigned long		flags;
+	int			ret;
+
+	if (!driver || !driver->bind || !driver->setup)
+		return -EINVAL;
+
+	spin_lock_irqsave(&gadget_lock, flags);
+	list_for_each_entry(gadget, &gadget_list, list) {
+		if (gadget->busy)
+			continue;
+
+		dev_vdbg(gadget->dev, "registering driver [%s]\n",
+			       driver->function);
+
+		gadget->busy = true;
+		gadget->driver = driver;
+		gadget->dev->driver = &driver->driver;
+	}
+	spin_unlock_irqrestore(&gadget_lock, flags);
+
+	if (!gadget)
+		return -EBUSY;
+
+	ret = driver->bind(gadget);
+	if (ret) {
+		dev_dbg(gadget->dev, "bind to driver %s failed --> %d\n",
+				driver->function, ret);
+		goto err1;
+	}
+
+	ret = usb_gadget_start(gadget);
+	if (ret) {
+		dev_dbg(gadget->dev, "failed to start %s --> %d\n",
+				gadget->name, ret);
+		goto err1;
+	}
+
+	ret = usb_gadget_connect(gadget);
+	if (ret) {
+		dev_dbg(gadget->dev, "failed to start %s --> %d\n",
+				gadget->name, ret);
+		goto err2;
+	}
+
+	kobject_uevent(&gadget->dev->kobj, KOBJ_ADD);
+
+	return 0;
+
+err2:
+	usb_gadget_stop(gadget);
+
+err1:
+	gadget->busy = false;
+	gadget->driver = NULL;
+	gadget->dev->driver = NULL;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_register_driver);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+	struct usb_gadget	*gadget = NULL;
+	unsigned long		flags;
+
+	if (!driver || !driver->unbind)
+		return -EINVAL;
+
+	spin_lock_irqsave(&gadget_lock, flags);
+	list_for_each_entry(gadget, &gadget_list, list) {
+		if (!gadget->busy)
+			continue;
+
+		if (gadget->driver != driver)
+			continue;
+
+		gadget->busy = false;
+		gadget->driver = NULL;
+		gadget->dev->driver = NULL;
+	}
+	spin_unlock_irqrestore(&gadget_lock, flags);
+
+	if (!gadget)
+		return -ENODEV;
+
+	(void) usb_gadget_vbus_draw(gadget, 0);
+
+	dev_dbg(gadget->dev, "unregistering driver [%s]\n", driver->function);
+	kobject_uevent(&gadget->dev->kobj, KOBJ_REMOVE);
+
+	driver->unbind(gadget);
+	usb_gadget_stop(gadget);
+	usb_gadget_disconnect(gadget);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver);
+
+/* ------------------------------------------------------------------------- */
+
+static ssize_t usb_gadget_srp_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t n)
+{
+	struct usb_gadget	*gadget = dev_get_drvdata(dev);
+
+	if (sysfs_streq(buf, "1"))
+		usb_gadget_wakeup(gadget);
+
+	return n;
+}
+static DEVICE_ATTR(srp, S_IWUSR, NULL, usb_gadget_srp_store);
+
+static ssize_t usb_gadget_softconn_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t n)
+{
+	struct usb_gadget	*gadget = dev_get_drvdata(dev);
+
+	if (sysfs_streq(buf, "connect"))
+		usb_gadget_connect(gadget);
+	else if (sysfs_streq(buf, "disconnect"))
+		usb_gadget_disconnect(gadget);
+	else
+		dev_err(dev, "unknown value\n");
+
+	return n;
+}
+static DEVICE_ATTR(soft_connect, S_IWUSR, NULL, usb_gadget_softconn_store);
+
+static ssize_t usb_gadget_speed_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct usb_gadget	*gadget = dev_get_drvdata(dev);
+
+	switch (gadget->speed) {
+	case USB_SPEED_LOW:
+		return snprintf(buf, 10, "low-speed\n");
+	case USB_SPEED_FULL:
+		return snprintf(buf, 11, "full-speed\n");
+	case USB_SPEED_HIGH:
+		return snprintf(buf, 11, "high-speed\n");
+	case USB_SPEED_WIRELESS:
+		return snprintf(buf, 9, "wireless\n");
+	case USB_SPEED_SUPER:
+		return snprintf(buf, 12, "super-speed\n");
+	case USB_SPEED_UNKNOWN:	/* FALLTHROUGH */
+	default:
+		return snprintf(buf, 8, "UNKNOWN\n");
+	}
+}
+static DEVICE_ATTR(speed, S_IRUSR, usb_gadget_speed_show, NULL);
+
+#define USB_GADGET_ATTR(name)					\
+ssize_t usb_gadget_##name##_show(struct device *dev,		\
+		struct device_attribute *attr, char *buf)	\
+{								\
+	struct usb_gadget	*gadget = dev_get_drvdata(dev);	\
+								\
+	return snprintf(buf, 1, "%d\n", gadget->name);		\
+}								\
+static DEVICE_ATTR(name, S_IRUSR, usb_gadget_##name##_show, NULL)
+
+static USB_GADGET_ATTR(is_dualspeed);
+static USB_GADGET_ATTR(is_otg);
+static USB_GADGET_ATTR(is_a_peripheral);
+static USB_GADGET_ATTR(b_hnp_enable);
+static USB_GADGET_ATTR(a_hnp_support);
+static USB_GADGET_ATTR(a_alt_hnp_support);
+
+static struct attribute *usb_gadget_attrs[] = {
+	&dev_attr_srp.attr,
+	&dev_attr_soft_connect.attr,
+	&dev_attr_speed.attr,
+
+	&dev_attr_is_dualspeed.attr,
+	&dev_attr_is_otg.attr,
+	&dev_attr_is_a_peripheral.attr,
+	&dev_attr_b_hnp_enable.attr,
+	&dev_attr_a_hnp_support.attr,
+	&dev_attr_a_alt_hnp_support.attr,
+	NULL,
+};
+
+static const struct attribute_group usb_gadget_attr_group = {
+	.attrs = usb_gadget_attrs,
+};
+
+static const struct attribute_group *usb_gadget_attr_groups[] = {
+	&usb_gadget_attr_group,
+	NULL,
+};
+
+static int usb_gadget_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	struct usb_gadget	*gadget = dev_get_drvdata(dev);
+	int			ret;
+
+	dev_dbg(dev, "USB_GADGET_NAME=%s\n", gadget->name);
+
+	ret = add_uevent_var(env, "USB_GADGET_NAME=%s\n", gadget->name);
+	if (ret)
+		return ret;
+
+	ret = add_uevent_var(env, "USB_GADGET_DRIVER=%s\n",
+			gadget->driver->function);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
+MODULE_DESCRIPTION("USB Gadget Framework");
+MODULE_AUTHOR("Felipe Balbi <balbi@xxxxxx>");
+MODULE_LICENSE("GPL v2");
+
+static int __init usb_gadget_init(void)
+{
+	spin_lock_init(&gadget_lock);
+
+	gadget_class = class_create(THIS_MODULE, "gadget");
+	if (IS_ERR(gadget_class)) {
+		return PTR_ERR(gadget_class);
+	}
+
+	gadget_class->dev_uevent = usb_gadget_uevent;
+	gadget_device_type.groups = usb_gadget_attr_groups;
+
+	return 0;
+}
+subsys_initcall(usb_gadget_init);
+
+static void __exit usb_gadget_exit(void)
+{
+	class_destroy(gadget_class);
+}
+module_exit(usb_gadget_exit);
+
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 6fca870..56548d2 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -47,6 +47,8 @@
 
 #include "musb_core.h"
 
+static int musb_gadget_start(struct usb_gadget *gadget);
+static void musb_gadget_stop(struct usb_gadget *gadget);
 
 /* MUSB PERIPHERAL status 3-mar-2006:
  *
@@ -1552,6 +1554,8 @@ static const struct usb_gadget_ops musb_gadget_operations = {
 	/* .vbus_session		= musb_gadget_vbus_session, */
 	.vbus_draw		= musb_gadget_vbus_draw,
 	.pullup			= musb_gadget_pullup,
+	.start			= musb_gadget_start,
+	.stop			= musb_gadget_stop,
 };
 
 /* ----------------------------------------------------------------------- */
@@ -1564,13 +1568,6 @@ static const struct usb_gadget_ops musb_gadget_operations = {
  */
 static struct musb *the_gadget;
 
-static void musb_gadget_release(struct device *dev)
-{
-	/* kref_put(WHAT) */
-	dev_dbg(dev, "%s\n", __func__);
-}
-
-
 static void __init
 init_peripheral_ep(struct musb *musb, struct musb_ep *ep, u8 epnum, int is_in)
 {
@@ -1656,12 +1653,6 @@ int __init musb_gadget_setup(struct musb *musb)
 	musb->g.ops = &musb_gadget_operations;
 	musb->g.is_dualspeed = 1;
 	musb->g.speed = USB_SPEED_UNKNOWN;
-
-	/* this "gadget" abstracts/virtualizes the controller */
-	dev_set_name(&musb->g.dev, "gadget");
-	musb->g.dev.parent = musb->controller;
-	musb->g.dev.dma_mask = musb->controller->dma_mask;
-	musb->g.dev.release = musb_gadget_release;
 	musb->g.name = musb_driver_name;
 
 	if (is_otg_enabled(musb))
@@ -1672,9 +1663,10 @@ int __init musb_gadget_setup(struct musb *musb)
 	musb->is_active = 0;
 	musb_platform_try_idle(musb, 0);
 
-	status = device_register(&musb->g.dev);
+	status = usb_add_gadget(musb->controller, &musb->g);
 	if (status != 0)
 		the_gadget = NULL;
+
 	return status;
 }
 
@@ -1683,7 +1675,7 @@ void musb_gadget_cleanup(struct musb *musb)
 	if (musb != the_gadget)
 		return;
 
-	device_unregister(&musb->g.dev);
+	usb_del_gadget(&musb->g);
 	the_gadget = NULL;
 }
 
@@ -1698,132 +1690,68 @@ void musb_gadget_cleanup(struct musb *musb)
  * @param driver the gadget driver
  * @return <0 if error, 0 if everything is fine
  */
-int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+static int musb_gadget_start(struct usb_gadget *gadget)
 {
-	int retval;
+	struct musb *musb = gadget_to_musb(gadget);
 	unsigned long flags;
-	struct musb *musb = the_gadget;
-
-	if (!driver
-			|| driver->speed != USB_SPEED_HIGH
-			|| !driver->bind
-			|| !driver->setup)
-		return -EINVAL;
-
-	/* driver must be initialized to support peripheral mode */
-	if (!musb) {
-		DBG(1, "%s, no dev??\n", __func__);
-		return -ENODEV;
-	}
+	int retval = 0;
 
-	DBG(3, "registering driver %s\n", driver->function);
 	spin_lock_irqsave(&musb->lock, flags);
 
-	if (musb->gadget_driver) {
-		DBG(1, "%s is already bound to %s\n",
-				musb_driver_name,
-				musb->gadget_driver->driver.name);
-		retval = -EBUSY;
-	} else {
-		musb->gadget_driver = driver;
-		musb->g.dev.driver = &driver->driver;
-		driver->driver.bus = NULL;
-		musb->softconnect = 1;
-		retval = 0;
-	}
-
-	spin_unlock_irqrestore(&musb->lock, flags);
+	otg_set_peripheral(musb->xceiv, gadget);
+	musb->xceiv->state = OTG_STATE_B_IDLE;
+	musb->is_active = 1;
 
-	if (retval == 0) {
-		retval = driver->bind(&musb->g);
-		if (retval != 0) {
-			DBG(3, "bind to driver %s failed --> %d\n",
-					driver->driver.name, retval);
-			musb->gadget_driver = NULL;
-			musb->g.dev.driver = NULL;
-		}
+	/* FIXME this ignores the softconnect flag.  Drivers are
+	 * allowed hold the peripheral inactive until for example
+	 * userspace hooks up printer hardware or DSP codecs, so
+	 * hosts only see fully functional devices.
+	 */
 
-		spin_lock_irqsave(&musb->lock, flags);
+	if (!is_otg_enabled(musb))
+		musb_start(musb);
 
-		otg_set_peripheral(musb->xceiv, &musb->g);
-		musb->xceiv->state = OTG_STATE_B_IDLE;
-		musb->is_active = 1;
+	if (is_otg_enabled(musb)) {
+		DBG(3, "OTG startup...\n");
 
-		/* FIXME this ignores the softconnect flag.  Drivers are
-		 * allowed hold the peripheral inactive until for example
-		 * userspace hooks up printer hardware or DSP codecs, so
-		 * hosts only see fully functional devices.
+		/* REVISIT:  funcall to other code, which also
+		 * handles power budgeting ... this way also
+		 * ensures HdrcStart is indirectly called.
 		 */
-
-		if (!is_otg_enabled(musb))
-			musb_start(musb);
-
-		otg_set_peripheral(musb->xceiv, &musb->g);
-
-		spin_unlock_irqrestore(&musb->lock, flags);
-
-		if (is_otg_enabled(musb)) {
-			DBG(3, "OTG startup...\n");
-
-			/* REVISIT:  funcall to other code, which also
-			 * handles power budgeting ... this way also
-			 * ensures HdrcStart is indirectly called.
-			 */
-			retval = usb_add_hcd(musb_to_hcd(musb), -1, 0);
-			if (retval < 0) {
-				DBG(1, "add_hcd failed, %d\n", retval);
-				spin_lock_irqsave(&musb->lock, flags);
-				otg_set_peripheral(musb->xceiv, NULL);
-				musb->gadget_driver = NULL;
-				musb->g.dev.driver = NULL;
-				spin_unlock_irqrestore(&musb->lock, flags);
-			}
+		retval = usb_add_hcd(musb_to_hcd(musb), -1, 0);
+		if (retval < 0) {
+			DBG(1, "add_hcd failed, %d\n", retval);
+			otg_set_peripheral(musb->xceiv, NULL);
 		}
 	}
 
+	spin_unlock_irqrestore(&musb->lock, flags);
+
 	return retval;
 }
-EXPORT_SYMBOL(usb_gadget_register_driver);
 
-static void stop_activity(struct musb *musb, struct usb_gadget_driver *driver)
+static void stop_activity(struct musb *musb)
 {
 	int			i;
 	struct musb_hw_ep	*hw_ep;
 
-	/* don't disconnect if it's not connected */
-	if (musb->g.speed == USB_SPEED_UNKNOWN)
-		driver = NULL;
-	else
-		musb->g.speed = USB_SPEED_UNKNOWN;
-
-	/* deactivate the hardware */
-	if (musb->softconnect) {
-		musb->softconnect = 0;
-		musb_pullup(musb, 0);
-	}
 	musb_stop(musb);
 
 	/* killing any outstanding requests will quiesce the driver;
 	 * then report disconnect
 	 */
-	if (driver) {
-		for (i = 0, hw_ep = musb->endpoints;
-				i < musb->nr_endpoints;
-				i++, hw_ep++) {
-			musb_ep_select(musb->mregs, i);
-			if (hw_ep->is_shared_fifo /* || !epnum */) {
+	for (i = 0, hw_ep = musb->endpoints;
+			i < musb->nr_endpoints;
+			i++, hw_ep++) {
+		musb_ep_select(musb->mregs, i);
+		if (hw_ep->is_shared_fifo /* || !epnum */) {
+			nuke(&hw_ep->ep_in, -ESHUTDOWN);
+		} else {
+			if (hw_ep->max_packet_sz_tx)
 				nuke(&hw_ep->ep_in, -ESHUTDOWN);
-			} else {
-				if (hw_ep->max_packet_sz_tx)
-					nuke(&hw_ep->ep_in, -ESHUTDOWN);
-				if (hw_ep->max_packet_sz_rx)
-					nuke(&hw_ep->ep_out, -ESHUTDOWN);
-			}
+			if (hw_ep->max_packet_sz_rx)
+				nuke(&hw_ep->ep_out, -ESHUTDOWN);
 		}
-
-		spin_unlock(&musb->lock);
-		driver->disconnect(&musb->g);
-		spin_lock(&musb->lock);
 	}
 }
 
@@ -1833,14 +1761,10 @@ static void stop_activity(struct musb *musb, struct usb_gadget_driver *driver)
  *
  * @param driver the gadget driver to unregister
  */
-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+static void musb_gadget_stop(struct usb_gadget *gadget)
 {
+	struct musb	*musb = gadget_to_musb(gadget);
 	unsigned long	flags;
-	int		retval = 0;
-	struct musb	*musb = the_gadget;
-
-	if (!driver || !driver->unbind || !musb)
-		return -EINVAL;
 
 	/* REVISIT always use otg_set_peripheral() here too;
 	 * this needs to shut down the OTG engine.
@@ -1852,40 +1776,22 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
 	musb_hnp_stop(musb);
 #endif
 
-	if (musb->gadget_driver == driver) {
-
-		(void) musb_gadget_vbus_draw(&musb->g, 0);
-
-		musb->xceiv->state = OTG_STATE_UNDEFINED;
-		stop_activity(musb, driver);
-		otg_set_peripheral(musb->xceiv, NULL);
-
-		DBG(3, "unregistering driver %s\n", driver->function);
-		spin_unlock_irqrestore(&musb->lock, flags);
-		driver->unbind(&musb->g);
-		spin_lock_irqsave(&musb->lock, flags);
-
-		musb->gadget_driver = NULL;
-		musb->g.dev.driver = NULL;
+	musb->xceiv->state = OTG_STATE_UNDEFINED;
+	stop_activity(musb);
+	otg_set_peripheral(musb->xceiv, NULL);
 
-		musb->is_active = 0;
-		musb_platform_try_idle(musb, 0);
-	} else
-		retval = -EINVAL;
+	musb->is_active = 0;
+	musb_platform_try_idle(musb, 0);
 	spin_unlock_irqrestore(&musb->lock, flags);
 
-	if (is_otg_enabled(musb) && retval == 0) {
+	if (is_otg_enabled(musb)) {
 		usb_remove_hcd(musb_to_hcd(musb));
 		/* FIXME we need to be able to register another
 		 * gadget driver here and have everything work;
 		 * that currently misbehaves.
 		 */
 	}
-
-	return retval;
 }
-EXPORT_SYMBOL(usb_gadget_unregister_driver);
-
 
 /* ----------------------------------------------------------------------- */
 
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index d3ef42d..42e3d6f 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -18,6 +18,7 @@
 #include <linux/slab.h>
 
 struct usb_ep;
+struct usb_gadget_driver;
 
 /**
  * struct usb_request - describes one i/o request
@@ -430,6 +431,8 @@ struct usb_gadget_ops {
 	int	(*pullup) (struct usb_gadget *, int is_on);
 	int	(*ioctl)(struct usb_gadget *,
 				unsigned code, unsigned long param);
+	int	(*start)(struct usb_gadget *);
+	void	(*stop)(struct usb_gadget *);
 };
 
 /**
@@ -479,6 +482,8 @@ struct usb_gadget {
 	const struct usb_gadget_ops	*ops;
 	struct usb_ep			*ep0;
 	struct list_head		ep_list;	/* of usb_ep */
+	struct usb_gadget_driver	*driver;
+	struct list_head		list;
 	enum usb_device_speed		speed;
 	unsigned			is_dualspeed:1;
 	unsigned			is_otg:1;
@@ -486,18 +491,15 @@ struct usb_gadget {
 	unsigned			b_hnp_enable:1;
 	unsigned			a_hnp_support:1;
 	unsigned			a_alt_hnp_support:1;
+	unsigned			busy:1;
 	const char			*name;
-	struct device			dev;
+	struct device			*dev;
 };
 
 static inline void set_gadget_data(struct usb_gadget *gadget, void *data)
-	{ dev_set_drvdata(&gadget->dev, data); }
+	{ dev_set_drvdata(gadget->dev, data); }
 static inline void *get_gadget_data(struct usb_gadget *gadget)
-	{ return dev_get_drvdata(&gadget->dev); }
-static inline struct usb_gadget *dev_to_usb_gadget(struct device *dev)
-{
-	return container_of(dev, struct usb_gadget, dev);
-}
+	{ return dev_get_drvdata(gadget->dev); }
 
 /* iterates the non-control endpoints; 'tmp' is a struct usb_ep pointer */
 #define gadget_for_each_ep(tmp, gadget) \
@@ -640,6 +642,20 @@ static inline int usb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA)
 	return gadget->ops->vbus_draw(gadget, mA);
 }
 
+static inline int usb_gadget_start(struct usb_gadget *gadget)
+{
+	if (!gadget->ops->start)
+		return -EOPNOTSUPP;
+	return gadget->ops->start(gadget);
+}
+
+static inline void usb_gadget_stop(struct usb_gadget *gadget)
+{
+	if (!gadget->ops->stop)
+		return;
+	gadget->ops->stop(gadget);
+}
+
 /**
  * usb_gadget_vbus_disconnect - notify controller about VBUS session end
  * @gadget:the device whose VBUS supply is being described
@@ -898,4 +914,7 @@ extern struct usb_ep *usb_ep_autoconfig(struct usb_gadget *,
 
 extern void usb_ep_autoconfig_reset(struct usb_gadget *) __devinit;
 
+extern int usb_add_gadget(struct device *, struct usb_gadget *);
+extern void usb_del_gadget(struct usb_gadget *);
+
 #endif /* __LINUX_USB_GADGET_H */
-- 
1.7.3.rc0.6.g7505a


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux