]> bbs.cooldavid.org Git - net-next-2.6.git/commitdiff
V4L/DVB (13829): uvcvideo: Fix alternate setting selection in isochronous mode
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Fri, 11 Dec 2009 00:19:31 +0000 (21:19 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Sun, 17 Jan 2010 13:31:35 +0000 (11:31 -0200)
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: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/uvc/uvc_video.c

index 9a9802830d41ea1676f456e335c2e53e8e6fb4cc..e8cc0a9ddadd94287d4de3ce52aa2ccdc6dea9f4 100644 (file)
@@ -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)
 {
        struct usb_interface *intf = stream->intf;
-       struct usb_host_interface *alts;
-       struct usb_host_endpoint *ep = NULL;
-       int intfnum = stream->intfnum;
-       unsigned int bandwidth, psize, i;
+       struct usb_host_endpoint *ep;
+       unsigned int i;
        int ret;
 
        stream->last_fid = -1;
@@ -936,6 +934,12 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
        stream->bulk.payload_size = 0;
 
        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. */
                bandwidth = stream->ctrl.dwMaxPayloadTransferSize;
 
@@ -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) {
+                       struct usb_host_interface *alts;
+                       unsigned int psize;
+
                        alts = &intf->altsetting[i];
                        ep = uvc_find_endpoint(alts,
                                stream->header.bEndpointAddress);
@@ -958,21 +965,27 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
                        /* Check if the bandwidth is high enough. */
                        psize = le16_to_cpu(ep->desc.wMaxPacketSize);
                        psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
-                       if (psize >= bandwidth)
-                               break;
+                       if (psize >= bandwidth && psize <= best_psize) {
+                               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 "
                                "for requested bandwidth.\n");
                        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)
                        return ret;
 
-               ret = uvc_init_video_isoc(stream, ep, gfp_flags);
+               ret = uvc_init_video_isoc(stream, best_ep, gfp_flags);
        } else {
                /* Bulk endpoint, proceed to URB initialization. */
                ep = uvc_find_endpoint(&intf->altsetting[0],