Commit 2c4d9de8 authored by Laurent Pinchart's avatar Laurent Pinchart Committed by Mauro Carvalho Chehab

V4L/DVB (13829): uvcvideo: Fix alternate setting selection in isochronous mode

Unlike assumed by the driver, alternate settings are not sorted by
endpoint max packet size. Iterate over all alternate settings to find
the one with the smallest compatible max packet size.
Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 385097e0
...@@ -924,10 +924,8 @@ static int uvc_init_video_bulk(struct uvc_streaming *stream, ...@@ -924,10 +924,8 @@ static int uvc_init_video_bulk(struct uvc_streaming *stream,
static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags) static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
{ {
struct usb_interface *intf = stream->intf; struct usb_interface *intf = stream->intf;
struct usb_host_interface *alts; struct usb_host_endpoint *ep;
struct usb_host_endpoint *ep = NULL; unsigned int i;
int intfnum = stream->intfnum;
unsigned int bandwidth, psize, i;
int ret; int ret;
stream->last_fid = -1; stream->last_fid = -1;
...@@ -936,6 +934,12 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags) ...@@ -936,6 +934,12 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
stream->bulk.payload_size = 0; stream->bulk.payload_size = 0;
if (intf->num_altsetting > 1) { if (intf->num_altsetting > 1) {
struct usb_host_endpoint *best_ep = NULL;
unsigned int best_psize = 3 * 1024;
unsigned int bandwidth;
unsigned int uninitialized_var(altsetting);
int intfnum = stream->intfnum;
/* Isochronous endpoint, select the alternate setting. */ /* Isochronous endpoint, select the alternate setting. */
bandwidth = stream->ctrl.dwMaxPayloadTransferSize; bandwidth = stream->ctrl.dwMaxPayloadTransferSize;
...@@ -949,6 +953,9 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags) ...@@ -949,6 +953,9 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
} }
for (i = 0; i < intf->num_altsetting; ++i) { for (i = 0; i < intf->num_altsetting; ++i) {
struct usb_host_interface *alts;
unsigned int psize;
alts = &intf->altsetting[i]; alts = &intf->altsetting[i];
ep = uvc_find_endpoint(alts, ep = uvc_find_endpoint(alts,
stream->header.bEndpointAddress); stream->header.bEndpointAddress);
...@@ -958,21 +965,27 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags) ...@@ -958,21 +965,27 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
/* Check if the bandwidth is high enough. */ /* Check if the bandwidth is high enough. */
psize = le16_to_cpu(ep->desc.wMaxPacketSize); psize = le16_to_cpu(ep->desc.wMaxPacketSize);
psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
if (psize >= bandwidth) if (psize >= bandwidth && psize <= best_psize) {
break; altsetting = i;
best_psize = psize;
best_ep = ep;
}
} }
if (i >= intf->num_altsetting) { if (best_ep == NULL) {
uvc_trace(UVC_TRACE_VIDEO, "No fast enough alt setting " uvc_trace(UVC_TRACE_VIDEO, "No fast enough alt setting "
"for requested bandwidth.\n"); "for requested bandwidth.\n");
return -EIO; return -EIO;
} }
ret = usb_set_interface(stream->dev->udev, intfnum, i); uvc_trace(UVC_TRACE_VIDEO, "Selecting alternate setting %u "
"(%u B/frame bandwidth).\n", altsetting, best_psize);
ret = usb_set_interface(stream->dev->udev, intfnum, altsetting);
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = uvc_init_video_isoc(stream, ep, gfp_flags); ret = uvc_init_video_isoc(stream, best_ep, gfp_flags);
} else { } else {
/* Bulk endpoint, proceed to URB initialization. */ /* Bulk endpoint, proceed to URB initialization. */
ep = uvc_find_endpoint(&intf->altsetting[0], ep = uvc_find_endpoint(&intf->altsetting[0],
......
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