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