summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Grzeschik <m.grzeschik@pengutronix.de>2024-10-16 15:58:07 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2024-10-17 08:42:22 +0200
commitdc97c956a4703de61cfa8ebe6285d5c7274ef8fd (patch)
tree41d2d37649bb1924bc1e61c1c6009663468ecf26
parentadc292d54de9db2e6b8ecb7f81f278bbbaf713e9 (diff)
usb: gadget: uvc: only enqueue zero length requests in potential underrun
The complete handler will at least be called after 16 requests have completed, but will still handle all finisher requests. Since we have to maintain a costant filling in the isoc queue we ensure this by adding zero length requests. By counting the amount enqueued requests we can ensure that the queue is never underrun and only need to get active if the queue is running critical. This patch is setting 32 as the critical level, which is twice the request amount that is needed to create interrupts. To properly solve the amount of zero length requests that needs to be held in the hardware after one interrupt needs to be measured and depends on the runtime of the first enqueue run after the interrupt triggered. For now we just use twice the amount of requests between an interrupt. Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de> Link: https://lore.kernel.org/r/20240403-uvc_request_length_by_interval-v7-2-e224bb1035f0@pengutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/usb/gadget/function/uvc.h5
-rw-r--r--drivers/usb/gadget/function/uvc_video.c17
2 files changed, 21 insertions, 1 deletions
diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h
index cb35687b11e7..55d796f5f5e8 100644
--- a/drivers/usb/gadget/function/uvc.h
+++ b/drivers/usb/gadget/function/uvc.h
@@ -71,6 +71,9 @@ extern unsigned int uvc_gadget_trace_param;
#define UVCG_REQUEST_HEADER_LEN 12
+#define UVCG_REQ_MAX_INT_COUNT 16
+#define UVCG_REQ_MAX_ZERO_COUNT (2 * UVCG_REQ_MAX_INT_COUNT)
+
/* ------------------------------------------------------------------------
* Structures
*/
@@ -91,6 +94,8 @@ struct uvc_video {
struct work_struct pump;
struct workqueue_struct *async_wq;
+ atomic_t queued;
+
/* Frame parameters */
u8 bpp;
u32 fcc;
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index 002bf724d802..c041873cf856 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -269,6 +269,8 @@ static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req)
}
}
+ atomic_inc(&video->queued);
+
return ret;
}
@@ -304,7 +306,7 @@ static int uvcg_video_usb_req_queue(struct uvc_video *video,
*/
if (list_empty(&video->req_free) || ureq->last_buf ||
!(video->req_int_count %
- DIV_ROUND_UP(video->uvc_num_requests, 4))) {
+ min(DIV_ROUND_UP(video->uvc_num_requests, 4), UVCG_REQ_MAX_INT_COUNT))) {
video->req_int_count = 0;
req->no_interrupt = 0;
} else {
@@ -379,6 +381,7 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
int ret = 0;
spin_lock_irqsave(&video->req_lock, flags);
+ atomic_dec(&video->queued);
if (!video->is_enabled) {
/*
* When is_enabled is false, uvcg_video_disable() ensures
@@ -466,6 +469,16 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
* happen.
*/
queue_work(video->async_wq, &video->pump);
+ } else if (atomic_read(&video->queued) > UVCG_REQ_MAX_ZERO_COUNT) {
+ list_add_tail(&to_queue->list, &video->req_free);
+ /*
+ * There is a new free request - wake up the pump.
+ */
+ queue_work(video->async_wq, &video->pump);
+
+ spin_unlock_irqrestore(&video->req_lock, flags);
+
+ return;
}
/*
* Queue to the endpoint. The actual queueing to ep will
@@ -756,6 +769,8 @@ int uvcg_video_enable(struct uvc_video *video)
video->req_int_count = 0;
+ atomic_set(&video->queued, 0);
+
uvc_video_ep_queue_initial_requests(video);
queue_work(video->async_wq, &video->pump);