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: Laurent Pinchart <laurent.pinchart@skynet.be> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
parent
7e21fda17e
commit
44f0079ec7
4 changed files with 66 additions and 141 deletions
|
@ -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) },
|
||||
{}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 @@ int uvc_probe_video(struct uvc_video_device *video,
|
|||
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
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue