diff options
author | Guennadi Liakhovetski <guennadi.liakhovetski@intel.com> | 2018-05-08 11:07:44 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab+samsung@kernel.org> | 2018-07-27 06:39:57 -0400 |
commit | 8e7a1dbc7b0ae048bf3f4b9707fe1a845e69aab7 (patch) | |
tree | 7b37fafe4ae28b1c2b7f04ecb89454aceb75f2fd /drivers/media/usb/uvc | |
parent | 222964eaf2472053221341c331eb81e87c8b04f2 (diff) |
media: uvcvideo: Handle control pipe protocol STALLs
When a command ends up in a STALL on the control pipe, use the Request
Error Code control to provide a more precise error information to the
user. For example, if a camera is still busy processing a control,
when the same or an interrelated control set request arrives, the
camera can react with a STALL and then return the "Not ready" status
in response to a UVC_VC_REQUEST_ERROR_CODE_CONTROL command. With this
patch the user would then get an EBUSY error code instead of a
generic EPIPE.
Signed-off-by: Guennadi Liakhovetski <guennadi.liakhovetski@intel.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
Diffstat (limited to 'drivers/media/usb/uvc')
-rw-r--r-- | drivers/media/usb/uvc/uvc_video.c | 52 |
1 files changed, 46 insertions, 6 deletions
diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 3ed5f27e786b..86a99f461fd8 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -73,17 +73,57 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit, u8 intfnum, u8 cs, void *data, u16 size) { int ret; + u8 error; + u8 tmp; 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 (%s) UVC control %u on " - "unit %u: %d (exp. %u).\n", uvc_query_name(query), cs, - unit, ret, size); - return -EIO; + if (likely(ret == size)) + return 0; + + uvc_printk(KERN_ERR, + "Failed to query (%s) UVC control %u on unit %u: %d (exp. %u).\n", + uvc_query_name(query), cs, unit, ret, size); + + if (ret != -EPIPE) + return ret; + + tmp = *(u8 *)data; + + ret = __uvc_query_ctrl(dev, UVC_GET_CUR, 0, intfnum, + UVC_VC_REQUEST_ERROR_CODE_CONTROL, data, 1, + UVC_CTRL_CONTROL_TIMEOUT); + + error = *(u8 *)data; + *(u8 *)data = tmp; + + if (ret != 1) + return ret < 0 ? ret : -EPIPE; + + uvc_trace(UVC_TRACE_CONTROL, "Control error %u\n", error); + + switch (error) { + case 0: + /* Cannot happen - we received a STALL */ + return -EPIPE; + case 1: /* Not ready */ + return -EBUSY; + case 2: /* Wrong state */ + return -EILSEQ; + case 3: /* Power */ + return -EREMOTE; + case 4: /* Out of range */ + return -ERANGE; + case 5: /* Invalid unit */ + case 6: /* Invalid control */ + case 7: /* Invalid Request */ + case 8: /* Invalid value within range */ + return -EINVAL; + default: /* reserved or unknown */ + break; } - return 0; + return -EPIPE; } static void uvc_fixup_video_ctrl(struct uvc_streaming *stream, |