[PATCH] usb/gadget: push USB_REQ_SET_INTERFACE and USB_REQ_SET_CONFIGURATION into process context

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

 



This patch pushes two USB requests
- USB_REQ_SET_INTERFACE
- USB_REQ_SET_CONFIGURATION

to be processed in process context. Both may perform some kind
initialisation which is better solved in process context than in atomic.
USB_REQ_SET_CONFIGURATION with config 0 also triggers disabling /
resetting which causes the dequeue of all requests. Having them in
process context will allow the UDC sleep and synchronize before it will
return.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@xxxxxxxxxxxxx>
---
 drivers/usb/gadget/composite.c |  114 +++++++++++++++++++++++++++++-----------
 include/linux/usb/composite.h  |    2 +
 2 files changed, 85 insertions(+), 31 deletions(-)

diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index a95de6a..4e1aa31 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -673,23 +673,12 @@ static int set_config(struct usb_composite_dev *cdev,
 			reset_config(cdev);
 			goto done;
 		}
-
-		if (result == USB_GADGET_DELAYED_STATUS) {
-			DBG(cdev,
-			 "%s: interface %d (%s) requested delayed status\n",
-					__func__, tmp, f->name);
-			cdev->delayed_status++;
-			DBG(cdev, "delayed_status count %d\n",
-					cdev->delayed_status);
-		}
 	}
 
 	/* when we return, be sure our power usage is valid */
 	power = c->bMaxPower ? (2 * c->bMaxPower) : CONFIG_USB_GADGET_VBUS_DRAW;
 done:
 	usb_gadget_vbus_draw(gadget, power);
-	if (result >= 0 && cdev->delayed_status)
-		result = USB_GADGET_DELAYED_STATUS;
 	return result;
 }
 
@@ -1011,6 +1000,85 @@ static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
 				req->status, req->actual, req->length);
 }
 
+
+static void composite_delayed_setup(struct work_struct *wq)
+{
+	struct usb_composite_dev *cdev = container_of(wq,
+			struct usb_composite_dev, wq);
+	const struct usb_ctrlrequest *ctrl = &cdev->ctrl;
+	struct usb_function *f = NULL;
+	int value = -EINVAL;
+	u16 w_index;
+	u16 w_value;
+	u16 w_length;
+	u8 intf;
+
+	w_index = le16_to_cpu(ctrl->wIndex);
+	w_value = le16_to_cpu(ctrl->wValue);
+	w_length = le16_to_cpu(ctrl->wLength);
+	intf = w_index & 0xFF;
+
+	switch (ctrl->bRequest) {
+	case USB_REQ_SET_INTERFACE:
+		if (!cdev->config)
+			break;
+		f = cdev->config->interface[intf];
+		if (!f)
+			break;
+		if (w_value && !f->set_alt)
+			break;
+
+		value = f->set_alt(f, w_index, w_value);
+		break;
+	case USB_REQ_SET_CONFIGURATION:
+		if (gadget_is_otg(cdev->gadget)) {
+			if (cdev->gadget->a_hnp_support)
+				DBG(cdev, "HNP available\n");
+			else if (cdev->gadget->a_alt_hnp_support)
+				DBG(cdev, "HNP on another port\n");
+			else
+				VDBG(cdev, "HNP inactive\n");
+		}
+		spin_lock_irq(&cdev->lock);
+		value = set_config(cdev, ctrl, w_value);
+		spin_unlock_irq(&cdev->lock);
+	}
+
+	if (value == USB_GADGET_DELAYED_STATUS) {
+		/* delayed status requested again */
+		DBG(cdev,
+		 "%s: interface %d (%s) requested delayed status (again)\n",
+				__func__, intf, f ? f->name : NULL);
+		DBG(cdev, "delayed_status count %d\n",
+				cdev->delayed_status);
+	} else if (value < 0) {
+		/* an error occured, stall the endpoint */
+		usb_ep_set_halt(cdev->gadget->ep0);
+	} else {
+		/* everything went according to the plan, resume */
+		usb_composite_setup_continue(cdev);
+	}
+}
+
+static int composite_delay_setup(struct usb_gadget *gadget,
+		const struct usb_ctrlrequest *ctrl)
+{
+	struct usb_composite_dev	*cdev = get_gadget_data(gadget);
+	u16				w_length = le16_to_cpu(ctrl->wLength);
+	u16				w_index = le16_to_cpu(ctrl->wIndex);
+
+	cdev->ctrl = *ctrl;
+	queue_work(system_long_wq, &cdev->wq);
+	if (!w_length) {
+		DBG(cdev, "interface %d requested delayed status\n",
+				w_index & 0xff);
+		cdev->delayed_status++;
+		return USB_GADGET_DELAYED_STATUS;
+	}
+	WARN_ON(1);
+	return 0;
+}
+
 /*
  * The setup() callback implements all the ep0 functionality that's
  * not handled lower down, in hardware or the hardware driver(like
@@ -1103,17 +1171,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
 	case USB_REQ_SET_CONFIGURATION:
 		if (ctrl->bRequestType != 0)
 			goto unknown;
-		if (gadget_is_otg(gadget)) {
-			if (gadget->a_hnp_support)
-				DBG(cdev, "HNP available\n");
-			else if (gadget->a_alt_hnp_support)
-				DBG(cdev, "HNP on another port\n");
-			else
-				VDBG(cdev, "HNP inactive\n");
-		}
-		spin_lock(&cdev->lock);
-		value = set_config(cdev, ctrl, w_value);
-		spin_unlock(&cdev->lock);
+		value = composite_delay_setup(gadget, ctrl);
 		break;
 	case USB_REQ_GET_CONFIGURATION:
 		if (ctrl->bRequestType != USB_DIR_IN)
@@ -1138,15 +1196,8 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
 			break;
 		if (w_value && !f->set_alt)
 			break;
-		value = f->set_alt(f, w_index, w_value);
-		if (value == USB_GADGET_DELAYED_STATUS) {
-			DBG(cdev,
-			 "%s: interface %d (%s) requested delayed status\n",
-					__func__, intf, f->name);
-			cdev->delayed_status++;
-			DBG(cdev, "delayed_status count %d\n",
-					cdev->delayed_status);
-		}
+
+		value = composite_delay_setup(gadget, ctrl);
 		break;
 	case USB_REQ_GET_INTERFACE:
 		if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
@@ -1388,6 +1439,7 @@ static int composite_bind(struct usb_gadget *gadget)
 		return status;
 
 	spin_lock_init(&cdev->lock);
+	INIT_WORK(&cdev->wq, composite_delayed_setup);
 	cdev->gadget = gadget;
 	set_gadget_data(gadget, cdev);
 	INIT_LIST_HEAD(&cdev->configs);
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index a316fba..be4c1b2 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -359,6 +359,8 @@ struct usb_composite_dev {
 	 * data/status stages till delayed_status is zero.
 	 */
 	int				delayed_status;
+	struct work_struct		wq;
+	struct usb_ctrlrequest		ctrl;
 
 	/* protects deactivations and delayed_status counts*/
 	spinlock_t			lock;
-- 
1.7.8.3

--
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