V4L/DVB (9570): uvcvideo: Handle failed video GET_{MIN|MAX|DEF} requests more gracefully
authorLaurent Pinchart <laurent.pinchart@skynet.be>
Sat, 8 Nov 2008 22:14:50 +0000 (19:14 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Mon, 29 Dec 2008 19:53:31 +0000 (17:53 -0200)
Failed requests will now generate a one-time warning message instead of the
usual "Failed to query..." error, which should be more user-friendly. The
driver will also recover automatically from failed GET_MIN/GET_MAX requests
when the device is half-broken without requiring the MINMAX quirk (fully
broken devices still need the quirk).

Signed-off-by: Laurent Pinchart <laurent.pinchart@skynet.be>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/uvc/uvc_driver.c
drivers/media/video/uvc/uvc_v4l2.c
drivers/media/video/uvc/uvc_video.c
drivers/media/video/uvc/uvcvideo.h

index 11398ee66320c6d81fdc40652e43d3a178ace028..1e3b4fe5ae93f93983ef0a6b951ddb61a26fc0ea 100644 (file)
@@ -1726,24 +1726,6 @@ static int uvc_reset_resume(struct usb_interface *intf)
  * though they are compliant.
  */
 static struct usb_device_id uvc_ids[] = {
-       /* ALi M5606 (Clevo M540SR) */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x0402,
-         .idProduct            = 0x5606,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
-       /* Creative Live! Optia */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x041e,
-         .idProduct            = 0x4057,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
        /* Microsoft Lifecam NX-6000 */
        { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
                                | USB_DEVICE_ID_MATCH_INT_INFO,
@@ -1829,15 +1811,6 @@ static struct usb_device_id uvc_ids[] = {
          .bInterfaceSubClass   = 1,
          .bInterfaceProtocol   = 0,
          .driver_info          = UVC_QUIRK_STREAM_NO_FID },
-       /* Silicon Motion SM371 */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x090c,
-         .idProduct            = 0xb371,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
        /* MT6227 */
        { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
                                | USB_DEVICE_ID_MATCH_INT_INFO,
@@ -1922,105 +1895,6 @@ static struct usb_device_id uvc_ids[] = {
          .bInterfaceProtocol   = 0,
          .driver_info          = UVC_QUIRK_PROBE_MINMAX
                                | UVC_QUIRK_IGNORE_SELECTOR_UNIT},
-       /* Acer OEM Webcam - Unknown vendor */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x5986,
-         .idProduct            = 0x0100,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
-       /* Packard Bell OEM Webcam - Bison Electronics */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x5986,
-         .idProduct            = 0x0101,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
-       /* Acer Crystal Eye webcam - Bison Electronics */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x5986,
-         .idProduct            = 0x0102,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
-       /* Compaq Presario B1200 - Bison Electronics */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x5986,
-         .idProduct            = 0x0104,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
-       /* Acer Travelmate 7720 - Bison Electronics */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x5986,
-         .idProduct            = 0x0105,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
-       /* Medion Akoya Mini E1210 - Bison Electronics */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x5986,
-         .idProduct            = 0x0141,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
-       /* Acer OrbiCam - Bison Electronics */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x5986,
-         .idProduct            = 0x0200,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
-       /*  Fujitsu Amilo SI2636 - Bison Electronics */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x5986,
-         .idProduct            = 0x0202,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
-       /*  Advent 4211 - Bison Electronics */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x5986,
-         .idProduct            = 0x0203,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
-       /* Bison Electronics */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x5986,
-         .idProduct            = 0x0300,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
-       /* Clevo M570TU - Bison Electronics */
-       { .match_flags          = USB_DEVICE_ID_MATCH_DEVICE
-                               | USB_DEVICE_ID_MATCH_INT_INFO,
-         .idVendor             = 0x5986,
-         .idProduct            = 0x0303,
-         .bInterfaceClass      = USB_CLASS_VIDEO,
-         .bInterfaceSubClass   = 1,
-         .bInterfaceProtocol   = 0,
-         .driver_info          = UVC_QUIRK_PROBE_MINMAX },
        /* Generic USB Video Class */
        { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },
        {}
index 8361367806a98f89e171b4f075b91a199dcdd1d2..624bf74de673468b30ad786886ab75052ef9645d 100644 (file)
@@ -252,7 +252,7 @@ static int uvc_v4l2_set_format(struct uvc_video_device *video,
        if (ret < 0)
                return ret;
 
-       if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0)
+       if ((ret = uvc_commit_video(video, &probe)) < 0)
                return ret;
 
        memcpy(&video->streaming->ctrl, &probe, sizeof probe);
@@ -316,7 +316,7 @@ static int uvc_v4l2_set_streamparm(struct uvc_video_device *video,
                return ret;
 
        /* Commit the new settings. */
-       if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0)
+       if ((ret = uvc_commit_video(video, &probe)) < 0)
                return ret;
 
        memcpy(&video->streaming->ctrl, &probe, sizeof probe);
index b7bb23820d803013548ef99ce91ccd52039102fb..4af94707ef69418cbaf5723fe1cd23b0d9e6d103 100644 (file)
@@ -36,15 +36,22 @@ static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
 {
        __u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
        unsigned int pipe;
-       int ret;
 
        pipe = (query & 0x80) ? usb_rcvctrlpipe(dev->udev, 0)
                              : usb_sndctrlpipe(dev->udev, 0);
        type |= (query & 0x80) ? USB_DIR_IN : USB_DIR_OUT;
 
-       ret = usb_control_msg(dev->udev, pipe, query, type, cs << 8,
+       return usb_control_msg(dev->udev, pipe, query, type, cs << 8,
                        unit << 8 | intfnum, data, size, timeout);
+}
+
+int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
+                       __u8 intfnum, __u8 cs, void *data, __u16 size)
+{
+       int ret;
 
+       ret = __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size,
+                               UVC_CTRL_CONTROL_TIMEOUT);
        if (ret != size) {
                uvc_printk(KERN_ERR, "Failed to query (%u) UVC control %u "
                        "(unit %u) : %d (exp. %u).\n", query, cs, unit, ret,
@@ -55,13 +62,6 @@ static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
        return 0;
 }
 
-int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
-                       __u8 intfnum, __u8 cs, void *data, __u16 size)
-{
-       return __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size,
-                               UVC_CTRL_CONTROL_TIMEOUT);
-}
-
 static void uvc_fixup_buffer_size(struct uvc_video_device *video,
        struct uvc_streaming_control *ctrl)
 {
@@ -102,8 +102,36 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video,
        ret = __uvc_query_ctrl(video->dev, query, 0, video->streaming->intfnum,
                probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size,
                UVC_CTRL_STREAMING_TIMEOUT);
-       if (ret < 0)
+
+       if ((query == GET_MIN || query == GET_MAX) && ret == 2) {
+               /* Some cameras, mostly based on Bison Electronics chipsets,
+                * answer a GET_MIN or GET_MAX request with the wCompQuality
+                * field only.
+                */
+               uvc_warn_once(video->dev, UVC_WARN_MINMAX, "UVC non "
+                       "compliance - GET_MIN/MAX(PROBE) incorrectly "
+                       "supported. Enabling workaround.\n");
+               memset(ctrl, 0, sizeof ctrl);
+               ctrl->wCompQuality = le16_to_cpup((__le16 *)data);
+               ret = 0;
                goto out;
+       } else if (query == GET_DEF && probe == 1) {
+               /* Many cameras don't support the GET_DEF request on their
+                * video probe control. Warn once and return, the caller will
+                * fall back to GET_CUR.
+                */
+               uvc_warn_once(video->dev, UVC_WARN_PROBE_DEF, "UVC non "
+                       "compliance - GET_DEF(PROBE) not supported. "
+                       "Enabling workaround.\n");
+               ret = -EIO;
+               goto out;
+       } else if (ret != size) {
+               uvc_printk(KERN_ERR, "Failed to query (%u) UVC %s control : "
+                       "%d (exp. %u).\n", query, probe ? "probe" : "commit",
+                       ret, size);
+               ret = -EIO;
+               goto out;
+       }
 
        ctrl->bmHint = le16_to_cpup((__le16 *)&data[0]);
        ctrl->bFormatIndex = data[2];
@@ -138,13 +166,14 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video,
         * Try to get the value from the format and frame descriptor.
         */
        uvc_fixup_buffer_size(video, ctrl);
+       ret = 0;
 
 out:
        kfree(data);
        return ret;
 }
 
-int uvc_set_video_ctrl(struct uvc_video_device *video,
+static int uvc_set_video_ctrl(struct uvc_video_device *video,
        struct uvc_streaming_control *ctrl, int probe)
 {
        __u8 *data;
@@ -186,6 +215,12 @@ int uvc_set_video_ctrl(struct uvc_video_device *video,
                video->streaming->intfnum,
                probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size,
                UVC_CTRL_STREAMING_TIMEOUT);
+       if (ret != size) {
+               uvc_printk(KERN_ERR, "Failed to set UVC %s control : "
+                       "%d (exp. %u).\n", probe ? "probe" : "commit",
+                       ret, size);
+               ret = -EIO;
+       }
 
        kfree(data);
        return ret;
@@ -252,6 +287,12 @@ done:
        return ret;
 }
 
+int uvc_commit_video(struct uvc_video_device *video,
+       struct uvc_streaming_control *probe)
+{
+       return uvc_set_video_ctrl(video, probe, 0);
+}
+
 /* ------------------------------------------------------------------------
  * Video codecs
  */
index 9a6bc1aafb166bf5533f94494c7a71d68bdae7a1..595b90ee6528ba90158b32c050f16b2dde4892df 100644 (file)
@@ -617,6 +617,7 @@ enum uvc_device_state {
 struct uvc_device {
        struct usb_device *udev;
        struct usb_interface *intf;
+       unsigned long warnings;
        __u32 quirks;
        int intfnum;
        char name[32];
@@ -679,6 +680,9 @@ struct uvc_driver {
 #define UVC_TRACE_SUSPEND      (1 << 8)
 #define UVC_TRACE_STATUS       (1 << 9)
 
+#define UVC_WARN_MINMAX                0
+#define UVC_WARN_PROBE_DEF     1
+
 extern unsigned int uvc_trace_param;
 
 #define uvc_trace(flag, msg...) \
@@ -687,6 +691,12 @@ extern unsigned int uvc_trace_param;
                        printk(KERN_DEBUG "uvcvideo: " msg); \
        } while (0)
 
+#define uvc_warn_once(dev, warn, msg...) \
+       do { \
+               if (!test_and_set_bit(warn, &dev->warnings)) \
+                       printk(KERN_INFO "uvcvideo: " msg); \
+       } while (0)
+
 #define uvc_printk(level, msg...) \
        printk(level "uvcvideo: " msg)
 
@@ -740,10 +750,10 @@ extern int uvc_video_resume(struct uvc_video_device *video);
 extern int uvc_video_enable(struct uvc_video_device *video, int enable);
 extern int uvc_probe_video(struct uvc_video_device *video,
                struct uvc_streaming_control *probe);
+extern int uvc_commit_video(struct uvc_video_device *video,
+               struct uvc_streaming_control *ctrl);
 extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
                __u8 intfnum, __u8 cs, void *data, __u16 size);
-extern int uvc_set_video_ctrl(struct uvc_video_device *video,
-               struct uvc_streaming_control *ctrl, int probe);
 
 /* Status */
 extern int uvc_status_init(struct uvc_device *dev);