diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 71563d8f4bcf..ee4f54d68349 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -32,6 +32,7 @@ unsigned int uvc_clock_param = CLOCK_MONOTONIC; unsigned int uvc_hw_timestamps_param; +unsigned int uvc_no_drop_param = 1; static unsigned int uvc_quirks_param = -1; unsigned int uvc_dbg_param; unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT; @@ -2467,6 +2468,24 @@ MODULE_PARM_DESC(clock, "Video buffers timestamp clock"); module_param_named(hwtimestamps, uvc_hw_timestamps_param, uint, 0644); MODULE_PARM_DESC(hwtimestamps, "Use hardware timestamps"); +static int param_set_nodrop(const char *val, const struct kernel_param *kp) +{ + pr_warn_once("uvcvideo: " + DEPRECATED + "nodrop parameter will be eventually removed.\n"); + return param_set_bool(val, kp); +} + +static const struct kernel_param_ops param_ops_nodrop = { + .set = param_set_nodrop, + .get = param_get_uint, +}; + +param_check_uint(nodrop, &uvc_no_drop_param); +module_param_cb(nodrop, ¶m_ops_nodrop, &uvc_no_drop_param, 0644); +__MODULE_PARM_TYPE(nodrop, "uint"); +MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames"); + module_param_named(quirks, uvc_quirks_param, uint, 0644); MODULE_PARM_DESC(quirks, "Forced device quirks"); module_param_named(trace, uvc_dbg_param, uint, 0644); diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c index 3bc54456b4d9..681a74ed09fb 100644 --- a/drivers/media/usb/uvc/uvc_queue.c +++ b/drivers/media/usb/uvc/uvc_queue.c @@ -331,9 +331,34 @@ struct uvc_buffer *uvc_queue_get_current_buffer(struct uvc_video_queue *queue) return nextbuf; } +/* + * uvc_queue_buffer_requeue: Requeue a buffer on our internal irqqueue + * + * Reuse a buffer through our internal queue without the need to 'prepare'. + * The buffer will be returned to userspace through the uvc_buffer_queue call if + * the device has been disconnected. + */ +static void uvc_queue_buffer_requeue(struct uvc_video_queue *queue, + struct uvc_buffer *buf) +{ + buf->error = 0; + buf->state = UVC_BUF_STATE_QUEUED; + buf->bytesused = 0; + vb2_set_plane_payload(&buf->buf.vb2_buf, 0, 0); + + uvc_buffer_queue(&buf->buf.vb2_buf); +} + static void uvc_queue_buffer_complete(struct kref *ref) { struct uvc_buffer *buf = container_of(ref, struct uvc_buffer, ref); + struct vb2_buffer *vb = &buf->buf.vb2_buf; + struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue); + + if (buf->error && !uvc_no_drop_param) { + uvc_queue_buffer_requeue(queue, buf); + return; + } buf->state = buf->error ? UVC_BUF_STATE_ERROR : UVC_BUF_STATE_DONE; vb2_set_plane_payload(&buf->buf.vb2_buf, 0, buf->bytesused); diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index 911016047687..d583425893a5 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -659,6 +659,7 @@ static inline struct uvc_fh *to_uvc_fh(struct file *filp) #define UVC_WARN_XU_GET_RES 2 extern unsigned int uvc_clock_param; +extern unsigned int uvc_no_drop_param; extern unsigned int uvc_dbg_param; extern unsigned int uvc_timeout_param; extern unsigned int uvc_hw_timestamps_param;