[RFC] usb: gadget: Ensure correct functionality for gadgets that wish to 'connect' only when directed by user-space

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

 



Hi Felipe, All,

While working with the webcam gadget in recent past I came to realize that if the webcam gadget
is compiled as a static part of the kernel, the USB webcam device when connected to the USB
host will not successfully enumerate (as the USB host will send a GET_DEF class specific UVC
request), which is handled by a user-space application responsible for ensuring command/data
flow between the UVC gadget and a real V4L2 device.

I believe similar will be the case for other gadgets like obex while working with an underlying
UDC during the start-up phase (during the enumeration process).

Gadgets like webcam/obex explicitly call the 'pullup' routine of the underlying
UDC (with is_on argument set to 0) to issue soft-disconnect command at the very start. This is
done to prevent the enumeration process to proceed unless a supporting user-space application
is first up and running (which can handle the class-specific control requests on the basis of
certain negotiations with other entities, like for e.g. a real video device in case of webcam).
(see [1] for reference).

However with the current framework (newstyle UDC) just after the 'bind' of the
function (f_uvc/f_obex) is called, the UDC framework calls the 'pullup' routine of the
UDC again with a parameter is_on set to 1 to initiate connection with the Host.
(see [2] for reference).

This seems to be an incorrect implementation as it causes the enumeration to
start even for gadgets like webcam/obex which have explicitly requested
only a soft-connect (as the user-space application has not been launched yet).

Please let me know your ideas on the same.
At the moment I have used a temporary approach like the one given below (though it is in
no way a very good way out :) ).


[1] http://lxr.linux.no/linux+v3.4.4/drivers/usb/gadget/f_uvc.c#L548
[2] http://lxr.linux.no/linux+v3.4.4/drivers/usb/gadget/udc-core.c#L341


Signed-off-by: Bhupesh Sharma <bhupesh.sharma@xxxxxx>
---
 drivers/usb/dwc3/core.h       |    1 +
 drivers/usb/dwc3/gadget.c     |   24 ++++++++++++++++++------
 drivers/usb/gadget/udc-core.c |   19 ++++++++++++++++++-
 include/linux/usb/gadget.h    |    1 +
 4 files changed, 38 insertions(+), 7 deletions(-)

diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 2d8676c..9125ec9 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -712,6 +712,7 @@ struct dwc3 {
 	struct dwc3_hwparams	hwparams;
 	struct dentry		*root;
+	bool			deactivated_at_start;
 
 	u8			test_mode;
 	u8			test_mode_nr;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index b82fe61..0d6ea17 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1341,6 +1341,12 @@ static const struct usb_ep_ops dwc3_gadget_ep_ops = {
 };
 
 /* -------------------------------------------------------------------------- */
+static bool dwc3_gadget_is_deactivated_at_start(struct usb_gadget *g)
+{
+	struct dwc3		*dwc = gadget_to_dwc(g);
+
+	return dwc->deactivated_at_start;
+}
 
 static int dwc3_gadget_get_frame(struct usb_gadget *g)
 {
@@ -1448,6 +1454,7 @@ static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
 {
 	u32			reg;
 	u32			timeout = 500;
+	static bool		first_deactivation;
 
 	reg = dwc3_readl(dwc->regs, DWC3_DCTL);
 	if (is_on) {
@@ -1461,6 +1468,10 @@ static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
 		reg |= DWC3_DCTL_RUN_STOP;
 	} else {
 		reg &= ~DWC3_DCTL_RUN_STOP;
+		if (first_deactivation == false) {
+			dwc->deactivated_at_start = true;
+			first_deactivation = true;
+		}
 	}
 
 	dwc3_writel(dwc->regs, DWC3_DCTL, reg);
@@ -1600,12 +1611,13 @@ static int dwc3_gadget_stop(struct usb_gadget *g,
 }
 
 static const struct usb_gadget_ops dwc3_gadget_ops = {
-	.get_frame		= dwc3_gadget_get_frame,
-	.wakeup			= dwc3_gadget_wakeup,
-	.set_selfpowered	= dwc3_gadget_set_selfpowered,
-	.pullup			= dwc3_gadget_pullup,
-	.udc_start		= dwc3_gadget_start,
-	.udc_stop		= dwc3_gadget_stop,
+	.is_deactivated_at_start	= dwc3_gadget_is_deactivated_at_start,
+	.get_frame			= dwc3_gadget_get_frame,
+	.wakeup				= dwc3_gadget_wakeup,
+	.set_selfpowered		= dwc3_gadget_set_selfpowered,
+	.pullup				= dwc3_gadget_pullup,
+	.udc_start			= dwc3_gadget_start,
+	.udc_stop			= dwc3_gadget_stop,
 };
 
 /* -------------------------------------------------------------------------- */
diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c
index a6ebeec..c42ea92 100644
--- a/drivers/usb/gadget/udc-core.c
+++ b/drivers/usb/gadget/udc-core.c
@@ -261,6 +261,13 @@ static int udc_is_newstyle(struct usb_udc *udc)
 	return 0;
 }
 
+static int udc_is_deactivated_at_start(struct usb_udc *udc)
+{
+	if (udc->gadget->ops->is_deactivated_at_start)
+		return udc->gadget->ops->is_deactivated_at_start(udc->gadget);
+
+	return 0;
+}
 
 static void usb_gadget_remove_driver(struct usb_udc *udc)
 {
@@ -355,7 +362,17 @@ found:
 			driver->unbind(udc->gadget);
 			goto err1;
 		}
-		usb_gadget_connect(udc->gadget);
+
+		/*
+		 * do not connect if the function has requested an
+		 * explicit disconnect at the very start and hence
+		 * does not want to connect unless a user-space application
+		 * explicitly calls the 'function_activate' routine
+		 * to connect (this is required in case of certain
+		 * functions like obex and webcam).
+		 */
+		if (!udc_is_deactivated_at_start(udc))
+			usb_gadget_connect(udc->gadget);
 	} else {
 
 		ret = usb_gadget_start(udc->gadget, driver, bind);
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index 665b1d1..4c4e665 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -507,6 +507,7 @@ struct usb_gadget_driver;
  * which don't involve endpoints (or i/o).
  */
 struct usb_gadget_ops {
+	bool	(*is_deactivated_at_start)(struct usb_gadget *);
 	int	(*get_frame)(struct usb_gadget *);
 	int	(*wakeup)(struct usb_gadget *);
 	int	(*set_selfpowered) (struct usb_gadget *, int is_selfpowered);
-- 
1.6.0.2


Regards,
Bhupesh
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


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

  Powered by Linux