|
@@ -711,6 +711,142 @@ ehci_hub_descriptor (
|
|
|
desc->wHubCharacteristics = cpu_to_le16(temp);
|
|
|
}
|
|
|
|
|
|
+/*-------------------------------------------------------------------------*/
|
|
|
+#define EHSET_TEST_SINGLE_STEP_SET_FEATURE 0x06
|
|
|
+
|
|
|
+static void usb_ehset_completion(struct urb *urb)
|
|
|
+{
|
|
|
+ struct completion *done = urb->context;
|
|
|
+
|
|
|
+ complete(done);
|
|
|
+}
|
|
|
+static int submit_single_step_set_feature(
|
|
|
+ struct usb_hcd *hcd,
|
|
|
+ struct urb *urb,
|
|
|
+ int is_setup
|
|
|
+);
|
|
|
+
|
|
|
+/*
|
|
|
+ * Allocate and initialize a control URB. This request will be used by the
|
|
|
+ * EHSET SINGLE_STEP_SET_FEATURE test in which the DATA and STATUS stages
|
|
|
+ * of the GetDescriptor request are sent 15 seconds after the SETUP stage.
|
|
|
+ * Return NULL if failed.
|
|
|
+ */
|
|
|
+static struct urb *request_single_step_set_feature_urb(
|
|
|
+ struct usb_device *udev,
|
|
|
+ void *dr,
|
|
|
+ void *buf,
|
|
|
+ struct completion *done
|
|
|
+) {
|
|
|
+ struct urb *urb;
|
|
|
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
|
|
|
+ struct usb_host_endpoint *ep;
|
|
|
+
|
|
|
+ urb = usb_alloc_urb(0, GFP_KERNEL);
|
|
|
+ if (!urb)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ urb->pipe = usb_rcvctrlpipe(udev, 0);
|
|
|
+ ep = (usb_pipein(urb->pipe) ? udev->ep_in : udev->ep_out)
|
|
|
+ [usb_pipeendpoint(urb->pipe)];
|
|
|
+ if (!ep) {
|
|
|
+ usb_free_urb(urb);
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ urb->ep = ep;
|
|
|
+ urb->dev = udev;
|
|
|
+ urb->setup_packet = (void *)dr;
|
|
|
+ urb->transfer_buffer = buf;
|
|
|
+ urb->transfer_buffer_length = USB_DT_DEVICE_SIZE;
|
|
|
+ urb->complete = usb_ehset_completion;
|
|
|
+ urb->status = -EINPROGRESS;
|
|
|
+ urb->actual_length = 0;
|
|
|
+ urb->transfer_flags = URB_DIR_IN;
|
|
|
+ usb_get_urb(urb);
|
|
|
+ atomic_inc(&urb->use_count);
|
|
|
+ atomic_inc(&urb->dev->urbnum);
|
|
|
+ urb->setup_dma = dma_map_single(
|
|
|
+ hcd->self.controller,
|
|
|
+ urb->setup_packet,
|
|
|
+ sizeof(struct usb_ctrlrequest),
|
|
|
+ DMA_TO_DEVICE);
|
|
|
+ urb->transfer_dma = dma_map_single(
|
|
|
+ hcd->self.controller,
|
|
|
+ urb->transfer_buffer,
|
|
|
+ urb->transfer_buffer_length,
|
|
|
+ DMA_FROM_DEVICE);
|
|
|
+ urb->context = done;
|
|
|
+ return urb;
|
|
|
+}
|
|
|
+
|
|
|
+static int ehset_single_step_set_feature(struct usb_hcd *hcd, int port)
|
|
|
+{
|
|
|
+ int retval = -ENOMEM;
|
|
|
+ struct usb_ctrlrequest *dr;
|
|
|
+ struct urb *urb;
|
|
|
+ struct usb_device *udev;
|
|
|
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
|
|
+ struct usb_device_descriptor *buf;
|
|
|
+ DECLARE_COMPLETION_ONSTACK(done);
|
|
|
+
|
|
|
+ /* Obtain udev of the rhub's child port */
|
|
|
+ udev = usb_hub_find_child(hcd->self.root_hub, port);
|
|
|
+ if (!udev) {
|
|
|
+ ehci_err(ehci, "No device attached to the RootHub\n");
|
|
|
+ return -ENODEV;
|
|
|
+ }
|
|
|
+ buf = kmalloc(USB_DT_DEVICE_SIZE, GFP_KERNEL);
|
|
|
+ if (!buf)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
|
|
|
+ if (!dr) {
|
|
|
+ kfree(buf);
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Fill Setup packet for GetDescriptor */
|
|
|
+ dr->bRequestType = USB_DIR_IN;
|
|
|
+ dr->bRequest = USB_REQ_GET_DESCRIPTOR;
|
|
|
+ dr->wValue = cpu_to_le16(USB_DT_DEVICE << 8);
|
|
|
+ dr->wIndex = 0;
|
|
|
+ dr->wLength = cpu_to_le16(USB_DT_DEVICE_SIZE);
|
|
|
+ urb = request_single_step_set_feature_urb(udev, dr, buf, &done);
|
|
|
+ if (!urb)
|
|
|
+ goto cleanup;
|
|
|
+
|
|
|
+ /* Submit just the SETUP stage */
|
|
|
+ retval = submit_single_step_set_feature(hcd, urb, 1);
|
|
|
+ if (retval)
|
|
|
+ goto out1;
|
|
|
+ if (!wait_for_completion_timeout(&done, msecs_to_jiffies(2000))) {
|
|
|
+ usb_kill_urb(urb);
|
|
|
+ retval = -ETIMEDOUT;
|
|
|
+ ehci_err(ehci, "%s SETUP stage timed out on ep0\n", __func__);
|
|
|
+ goto out1;
|
|
|
+ }
|
|
|
+ msleep(15 * 1000);
|
|
|
+
|
|
|
+ /* Complete remaining DATA and STATUS stages using the same URB */
|
|
|
+ urb->status = -EINPROGRESS;
|
|
|
+ usb_get_urb(urb);
|
|
|
+ atomic_inc(&urb->use_count);
|
|
|
+ atomic_inc(&urb->dev->urbnum);
|
|
|
+ retval = submit_single_step_set_feature(hcd, urb, 0);
|
|
|
+ if (!retval && !wait_for_completion_timeout(&done,
|
|
|
+ msecs_to_jiffies(2000))) {
|
|
|
+ usb_kill_urb(urb);
|
|
|
+ retval = -ETIMEDOUT;
|
|
|
+ ehci_err(ehci, "%s IN stage timed out on ep0\n", __func__);
|
|
|
+ }
|
|
|
+out1:
|
|
|
+ usb_free_urb(urb);
|
|
|
+cleanup:
|
|
|
+ kfree(dr);
|
|
|
+ kfree(buf);
|
|
|
+ return retval;
|
|
|
+}
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
|
static int ehci_hub_control (
|
|
@@ -1086,7 +1222,13 @@ static int ehci_hub_control (
|
|
|
* about the EHCI-specific stuff.
|
|
|
*/
|
|
|
case USB_PORT_FEAT_TEST:
|
|
|
- if (!selector || selector > 5)
|
|
|
+ if (selector == EHSET_TEST_SINGLE_STEP_SET_FEATURE) {
|
|
|
+ spin_unlock_irqrestore(&ehci->lock, flags);
|
|
|
+ retval = ehset_single_step_set_feature(hcd,
|
|
|
+ wIndex);
|
|
|
+ spin_lock_irqsave(&ehci->lock, flags);
|
|
|
+ break;
|
|
|
+ } else if (!selector || selector > 5)
|
|
|
goto error;
|
|
|
spin_unlock_irqrestore(&ehci->lock, flags);
|
|
|
ehci_quiesce(ehci);
|