Commit 44f0079e authored by Laurent Pinchart's avatar Laurent Pinchart Committed by Mauro Carvalho Chehab

V4L/DVB (9570): uvcvideo: Handle failed video GET_{MIN|MAX|DEF} requests more gracefully

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: default avatarLaurent Pinchart <laurent.pinchart@skynet.be>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 7e21fda1
...@@ -1726,24 +1726,6 @@ static int uvc_reset_resume(struct usb_interface *intf) ...@@ -1726,24 +1726,6 @@ static int uvc_reset_resume(struct usb_interface *intf)
* though they are compliant. * though they are compliant.
*/ */
static struct usb_device_id uvc_ids[] = { 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 */ /* Microsoft Lifecam NX-6000 */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO, | USB_DEVICE_ID_MATCH_INT_INFO,
...@@ -1829,15 +1811,6 @@ static struct usb_device_id uvc_ids[] = { ...@@ -1829,15 +1811,6 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1, .bInterfaceSubClass = 1,
.bInterfaceProtocol = 0, .bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STREAM_NO_FID }, .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 */ /* MT6227 */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO, | USB_DEVICE_ID_MATCH_INT_INFO,
...@@ -1922,105 +1895,6 @@ static struct usb_device_id uvc_ids[] = { ...@@ -1922,105 +1895,6 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceProtocol = 0, .bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_MINMAX .driver_info = UVC_QUIRK_PROBE_MINMAX
| UVC_QUIRK_IGNORE_SELECTOR_UNIT}, | 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 */ /* Generic USB Video Class */
{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) }, { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },
{} {}
......
...@@ -252,7 +252,7 @@ static int uvc_v4l2_set_format(struct uvc_video_device *video, ...@@ -252,7 +252,7 @@ static int uvc_v4l2_set_format(struct uvc_video_device *video,
if (ret < 0) if (ret < 0)
return ret; return ret;
if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0) if ((ret = uvc_commit_video(video, &probe)) < 0)
return ret; return ret;
memcpy(&video->streaming->ctrl, &probe, sizeof probe); memcpy(&video->streaming->ctrl, &probe, sizeof probe);
...@@ -316,7 +316,7 @@ static int uvc_v4l2_set_streamparm(struct uvc_video_device *video, ...@@ -316,7 +316,7 @@ static int uvc_v4l2_set_streamparm(struct uvc_video_device *video,
return ret; return ret;
/* Commit the new settings. */ /* Commit the new settings. */
if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0) if ((ret = uvc_commit_video(video, &probe)) < 0)
return ret; return ret;
memcpy(&video->streaming->ctrl, &probe, sizeof probe); memcpy(&video->streaming->ctrl, &probe, sizeof probe);
......
...@@ -36,15 +36,22 @@ static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, ...@@ -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; __u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
unsigned int pipe; unsigned int pipe;
int ret;
pipe = (query & 0x80) ? usb_rcvctrlpipe(dev->udev, 0) pipe = (query & 0x80) ? usb_rcvctrlpipe(dev->udev, 0)
: usb_sndctrlpipe(dev->udev, 0); : usb_sndctrlpipe(dev->udev, 0);
type |= (query & 0x80) ? USB_DIR_IN : USB_DIR_OUT; 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); 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) { if (ret != size) {
uvc_printk(KERN_ERR, "Failed to query (%u) UVC control %u " uvc_printk(KERN_ERR, "Failed to query (%u) UVC control %u "
"(unit %u) : %d (exp. %u).\n", query, cs, unit, ret, "(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, ...@@ -55,13 +62,6 @@ static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
return 0; 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, static void uvc_fixup_buffer_size(struct uvc_video_device *video,
struct uvc_streaming_control *ctrl) struct uvc_streaming_control *ctrl)
{ {
...@@ -102,8 +102,36 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video, ...@@ -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, ret = __uvc_query_ctrl(video->dev, query, 0, video->streaming->intfnum,
probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size, probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size,
UVC_CTRL_STREAMING_TIMEOUT); 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; 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->bmHint = le16_to_cpup((__le16 *)&data[0]);
ctrl->bFormatIndex = data[2]; ctrl->bFormatIndex = data[2];
...@@ -138,13 +166,14 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video, ...@@ -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. * Try to get the value from the format and frame descriptor.
*/ */
uvc_fixup_buffer_size(video, ctrl); uvc_fixup_buffer_size(video, ctrl);
ret = 0;
out: out:
kfree(data); kfree(data);
return ret; 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) struct uvc_streaming_control *ctrl, int probe)
{ {
__u8 *data; __u8 *data;
...@@ -186,6 +215,12 @@ int uvc_set_video_ctrl(struct uvc_video_device *video, ...@@ -186,6 +215,12 @@ int uvc_set_video_ctrl(struct uvc_video_device *video,
video->streaming->intfnum, video->streaming->intfnum,
probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size, probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size,
UVC_CTRL_STREAMING_TIMEOUT); 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); kfree(data);
return ret; return ret;
...@@ -252,6 +287,12 @@ done: ...@@ -252,6 +287,12 @@ done:
return ret; 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 * Video codecs
*/ */
......
...@@ -617,6 +617,7 @@ enum uvc_device_state { ...@@ -617,6 +617,7 @@ enum uvc_device_state {
struct uvc_device { struct uvc_device {
struct usb_device *udev; struct usb_device *udev;
struct usb_interface *intf; struct usb_interface *intf;
unsigned long warnings;
__u32 quirks; __u32 quirks;
int intfnum; int intfnum;
char name[32]; char name[32];
...@@ -679,6 +680,9 @@ struct uvc_driver { ...@@ -679,6 +680,9 @@ struct uvc_driver {
#define UVC_TRACE_SUSPEND (1 << 8) #define UVC_TRACE_SUSPEND (1 << 8)
#define UVC_TRACE_STATUS (1 << 9) #define UVC_TRACE_STATUS (1 << 9)
#define UVC_WARN_MINMAX 0
#define UVC_WARN_PROBE_DEF 1
extern unsigned int uvc_trace_param; extern unsigned int uvc_trace_param;
#define uvc_trace(flag, msg...) \ #define uvc_trace(flag, msg...) \
...@@ -687,6 +691,12 @@ extern unsigned int uvc_trace_param; ...@@ -687,6 +691,12 @@ extern unsigned int uvc_trace_param;
printk(KERN_DEBUG "uvcvideo: " msg); \ printk(KERN_DEBUG "uvcvideo: " msg); \
} while (0) } 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...) \ #define uvc_printk(level, msg...) \
printk(level "uvcvideo: " msg) printk(level "uvcvideo: " msg)
...@@ -740,10 +750,10 @@ extern int uvc_video_resume(struct uvc_video_device *video); ...@@ -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_video_enable(struct uvc_video_device *video, int enable);
extern int uvc_probe_video(struct uvc_video_device *video, extern int uvc_probe_video(struct uvc_video_device *video,
struct uvc_streaming_control *probe); 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, extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
__u8 intfnum, __u8 cs, void *data, __u16 size); __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 */ /* Status */
extern int uvc_status_init(struct uvc_device *dev); extern int uvc_status_init(struct uvc_device *dev);
......
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