summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/gadget/udc-core.c75
-rw-r--r--include/linux/usb/gadget.h6
2 files changed, 77 insertions, 4 deletions
diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c
index 83e9e5f99b4..2ddb7c8b5b3 100644
--- a/drivers/usb/gadget/udc-core.c
+++ b/drivers/usb/gadget/udc-core.c
@@ -73,6 +73,26 @@ static inline int usb_gadget_start(struct usb_gadget *gadget,
}
/**
+ * usb_gadget_udc_start - tells usb device controller to start up
+ * @gadget: The gadget we want to get started
+ * @driver: The driver we want to bind to @gadget
+ *
+ * This call is issued by the UDC Class driver when it's about
+ * to register a gadget driver to the device controller, before
+ * calling gadget driver's bind() method.
+ *
+ * It allows the controller to be powered off until strictly
+ * necessary to have it powered on.
+ *
+ * Returns zero on success, else negative errno.
+ */
+static inline int usb_gadget_udc_start(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ return gadget->ops->udc_start(gadget, driver);
+}
+
+/**
* usb_gadget_stop - tells usb device controller we don't need it anymore
* @gadget: The device we want to stop activity
* @driver: The driver to unbind from @gadget
@@ -91,6 +111,24 @@ static inline void usb_gadget_stop(struct usb_gadget *gadget,
}
/**
+ * usb_gadget_udc_stop - tells usb device controller we don't need it anymore
+ * @gadget: The device we want to stop activity
+ * @driver: The driver to unbind from @gadget
+ *
+ * This call is issued by the UDC Class driver after calling
+ * gadget driver's unbind() method.
+ *
+ * The details are implementation specific, but it can go as
+ * far as powering off UDC completely and disable its data
+ * line pullups.
+ */
+static inline void usb_gadget_udc_stop(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ gadget->ops->udc_stop(gadget, driver);
+}
+
+/**
* usb_udc_release - release the usb_udc struct
* @dev: the dev member within usb_udc
*
@@ -155,6 +193,14 @@ err1:
}
EXPORT_SYMBOL_GPL(usb_add_gadget_udc);
+static int udc_is_newstyle(struct usb_udc *udc)
+{
+ if (udc->gadget->ops->udc_start && udc->gadget->ops->udc_stop)
+ return 1;
+ return 0;
+}
+
+
static void usb_gadget_remove_driver(struct usb_udc *udc)
{
dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n",
@@ -162,7 +208,14 @@ static void usb_gadget_remove_driver(struct usb_udc *udc)
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
- usb_gadget_stop(udc->gadget, udc->driver);
+ if (udc_is_newstyle(udc)) {
+ usb_gadget_disconnect(udc->gadget);
+ udc->driver->unbind(udc->gadget);
+ usb_gadget_udc_stop(udc->gadget, udc->driver);
+
+ } else {
+ usb_gadget_stop(udc->gadget, udc->driver);
+ }
udc->driver = NULL;
udc->dev.driver = NULL;
@@ -232,9 +285,23 @@ found:
udc->driver = driver;
udc->dev.driver = &driver->driver;
- ret = usb_gadget_start(udc->gadget, driver, bind);
- if (ret)
- goto err1;
+ if (udc_is_newstyle(udc)) {
+ ret = bind(udc->gadget);
+ if (ret)
+ goto err1;
+ ret = usb_gadget_udc_start(udc->gadget, driver);
+ if (ret) {
+ driver->unbind(udc->gadget);
+ goto err1;
+ }
+ usb_gadget_connect(udc->gadget);
+ } else {
+
+ ret = usb_gadget_start(udc->gadget, driver, bind);
+ if (ret)
+ goto err1;
+
+ }
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
mutex_unlock(&udc_lock);
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index afb67d997f0..087f4b93183 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -459,6 +459,12 @@ struct usb_gadget_ops {
int (*ioctl)(struct usb_gadget *,
unsigned code, unsigned long param);
void (*get_config_params)(struct usb_dcd_config_params *);
+ int (*udc_start)(struct usb_gadget *,
+ struct usb_gadget_driver *);
+ int (*udc_stop)(struct usb_gadget *,
+ struct usb_gadget_driver *);
+
+ /* Those two are deprecated */
int (*start)(struct usb_gadget_driver *,
int (*bind)(struct usb_gadget *));
int (*stop)(struct usb_gadget_driver *);