Commit 314b3e08 authored by Hans de Goede's avatar Hans de Goede Committed by Mauro Carvalho Chehab

[media] gspca: submit interrupt urbs *after* isoc urbs

Currently gspca supported usb-1.1 webcams for which we support the input
button through an interrupt endpoint won't stream (not enough bandwidth
error) when used through an USB-2.0 hub.

After much debugging I've found out that the cause for this is that the
ehci-sched.c schedeling code does not like it when there are already urb's
scheduled when (large) isoc urbs are queued. By moving the submission
of the interrupt urbs to after submitting the isoc urbs the camera
starts working again through usb-2.0 hubs.

Note that this does not fix isoc. streaming through a usb-hub while another
1.1 usb device (like the microphone of the same cam) is also active
at the same time :(

I've spend a long time analyzing the linux kernel ehci scheduler code,
resulting in this (long) mail:
http://www.spinics.net/lists/linux-usb/msg37982.html

The conclusion of the following mail thread is that yes there are several
issues when using usb-1.1 devices through a usb-2.0 hub, but these are not
easily fixable in the current code. Fixing this in ehci-sched.c requires
an almost full rewrite, which is not bound to happen anytime soon.

So with this patch gspca driven usb-1.1 webcams will atleast work when
connected through an usb-2.0 hub when the microphone is not used.

As an added bonus this patch avoids extra destroy/create input urb cycles
when we end up falling back to a lower speed alt setting because of bandwidth
limitations.
Signed-off-by: default avatarHans de Goede <hdegoede@redhat.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 25ad9847
...@@ -676,13 +676,11 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev) ...@@ -676,13 +676,11 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev)
i, ep->desc.bEndpointAddress); i, ep->desc.bEndpointAddress);
gspca_dev->alt = i; /* memorize the current alt setting */ gspca_dev->alt = i; /* memorize the current alt setting */
if (gspca_dev->nbalt > 1) { if (gspca_dev->nbalt > 1) {
gspca_input_destroy_urb(gspca_dev);
ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i); ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i);
if (ret < 0) { if (ret < 0) {
err("set alt %d err %d", i, ret); err("set alt %d err %d", i, ret);
ep = NULL; ep = NULL;
} }
gspca_input_create_urb(gspca_dev);
} }
return ep; return ep;
} }
...@@ -781,7 +779,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) ...@@ -781,7 +779,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
if (!gspca_dev->present) { if (!gspca_dev->present) {
ret = -ENODEV; ret = -ENODEV;
goto out; goto unlock;
} }
/* reset the streaming variables */ /* reset the streaming variables */
...@@ -802,8 +800,10 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) ...@@ -802,8 +800,10 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
if (gspca_dev->sd_desc->isoc_init) { if (gspca_dev->sd_desc->isoc_init) {
ret = gspca_dev->sd_desc->isoc_init(gspca_dev); ret = gspca_dev->sd_desc->isoc_init(gspca_dev);
if (ret < 0) if (ret < 0)
goto out; goto unlock;
} }
gspca_input_destroy_urb(gspca_dev);
ep = get_ep(gspca_dev); ep = get_ep(gspca_dev);
if (ep == NULL) { if (ep == NULL) {
ret = -EIO; ret = -EIO;
...@@ -873,6 +873,8 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) ...@@ -873,6 +873,8 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
} }
} }
out: out:
gspca_input_create_urb(gspca_dev);
unlock:
mutex_unlock(&gspca_dev->usb_lock); mutex_unlock(&gspca_dev->usb_lock);
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment