return 0;
}
-/*
- * Must only be called from uvcg_video_enable - since after that we only want to
- * queue requests to the endpoint from the uvc_video_complete complete handler.
- * This function is needed in order to 'kick start' the flow of requests from
- * gadget driver to the usb controller.
- */
-static void uvc_video_ep_queue_initial_requests(struct uvc_video *video)
-{
- struct usb_request *req = NULL;
- unsigned long flags = 0;
- unsigned int count = 0;
- int ret = 0;
-
- /*
- * We only queue half of the free list since we still want to have
- * some free usb_requests in the free list for the video_pump async_wq
- * thread to encode uvc buffers into. Otherwise we could get into a
- * situation where the free list does not have any usb requests to
- * encode into - we always end up queueing 0 length requests to the
- * end point.
- */
- unsigned int half_list_size = video->uvc_num_requests / 2;
-
- spin_lock_irqsave(&video->req_lock, flags);
- /*
- * Take these requests off the free list and queue them all to the
- * endpoint. Since we queue 0 length requests with the req_lock held,
- * there isn't any 'data' race involved here with the complete handler.
- */
- while (count < half_list_size) {
- req = list_first_entry(&video->req_free, struct usb_request,
- list);
- list_del(&req->list);
- req->length = 0;
- ret = uvcg_video_ep_queue(video, req);
- if (ret < 0) {
- uvcg_queue_cancel(&video->queue, 0);
- break;
- }
- count++;
- }
- spin_unlock_irqrestore(&video->req_lock, flags);
-}
-
static void
uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
{
struct uvc_video *video = ureq->video;
struct uvc_video_queue *queue = &video->queue;
struct uvc_buffer *last_buf;
- struct usb_request *to_queue = req;
unsigned long flags;
- bool is_bulk = video->max_payload_size;
- int ret = 0;
spin_lock_irqsave(&video->req_lock, flags);
atomic_dec(&video->queued);
return;
}
+ list_add_tail(&req->list, &video->req_free);
/*
- * Here we check whether any request is available in the ready
- * list. If it is, queue it to the ep and add the current
- * usb_request to the req_free list - for video_pump to fill in.
- * Otherwise, just use the current usb_request to queue a 0
- * length request to the ep. Since we always add to the req_free
- * list if we dequeue from the ready list, there will never
- * be a situation where the req_free list is completely out of
- * requests and cannot recover.
+ * Queue work to the wq as well since it is possible that a
+ * buffer may not have been completely encoded with the set of
+ * in-flight usb requests for whih the complete callbacks are
+ * firing.
+ * In that case, if we do not queue work to the worker thread,
+ * the buffer will never be marked as complete - and therefore
+ * not be returned to userpsace. As a result,
+ * dequeue -> queue -> dequeue flow of uvc buffers will not
+ * happen. Since there are is a new free request wake up the pump.
*/
- to_queue->length = 0;
- if (!list_empty(&video->req_ready)) {
- to_queue = list_first_entry(&video->req_ready,
- struct usb_request, list);
- list_del(&to_queue->list);
- list_add_tail(&req->list, &video->req_free);
- /*
- * Queue work to the wq as well since it is possible that a
- * buffer may not have been completely encoded with the set of
- * in-flight usb requests for whih the complete callbacks are
- * firing.
- * In that case, if we do not queue work to the worker thread,
- * the buffer will never be marked as complete - and therefore
- * not be returned to userpsace. As a result,
- * dequeue -> queue -> dequeue flow of uvc buffers will not
- * 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);
+ queue_work(video->async_wq, &video->pump);
- spin_unlock_irqrestore(&video->req_lock, flags);
+ spin_unlock_irqrestore(&video->req_lock, flags);
- return;
- }
- /*
- * Queue to the endpoint. The actual queueing to ep will
- * only happen on one thread - the async_wq for bulk endpoints
- * and this thread for isoc endpoints.
- */
- ret = uvcg_video_usb_req_queue(video, to_queue, !is_bulk);
- if (ret < 0) {
+ kthread_queue_work(video->kworker, &video->hw_submit);
+}
+
+static void uvcg_video_hw_submit(struct kthread_work *work)
+{
+ struct uvc_video *video = container_of(work, struct uvc_video, hw_submit);
+ bool is_bulk = video->max_payload_size;
+ unsigned long flags;
+ struct usb_request *req;
+ int ret = 0;
+
+ while (true) {
+ if (!video->ep->enabled)
+ return;
+ spin_lock_irqsave(&video->req_lock, flags);
/*
- * Endpoint error, but the stream is still enabled.
- * Put request back in req_free for it to be cleaned
- * up later.
+ * Here we check whether any request is available in the ready
+ * list. If it is, queue it to the ep and add the current
+ * usb_request to the req_free list - for video_pump to fill in.
+ * Otherwise, just use the current usb_request to queue a 0
+ * length request to the ep. Since we always add to the req_free
+ * list if we dequeue from the ready list, there will never
+ * be a situation where the req_free list is completely out of
+ * requests and cannot recover.
*/
- list_add_tail(&to_queue->list, &video->req_free);
+ if (!list_empty(&video->req_ready)) {
+ req = list_first_entry(&video->req_ready,
+ struct usb_request, list);
+ } else {
+ if (list_empty(&video->req_free) ||
+ (atomic_read(&video->queued) > UVCG_REQ_MAX_ZERO_COUNT)) {
+ spin_unlock_irqrestore(&video->req_lock, flags);
+
+ return;
+ }
+ req = list_first_entry(&video->req_free, struct usb_request,
+ list);
+ req->length = 0;
+ }
+ list_del(&req->list);
+
/*
- * There is a new free request - wake up the pump.
+ * Queue to the endpoint. The actual queueing to ep will
+ * only happen on one thread - the async_wq for bulk endpoints
+ * and this thread for isoc endpoints.
*/
- queue_work(video->async_wq, &video->pump);
- }
+ ret = uvcg_video_usb_req_queue(video, req, !is_bulk);
+ if (ret < 0) {
+ /*
+ * Endpoint error, but the stream is still enabled.
+ * Put request back in req_free for it to be cleaned
+ * up later.
+ */
+ list_add_tail(&req->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);
+ }
+
+ spin_unlock_irqrestore(&video->req_lock, flags);
+ }
}
static int
atomic_set(&video->queued, 0);
- uvc_video_ep_queue_initial_requests(video);
+ kthread_queue_work(video->kworker, &video->hw_submit);
queue_work(video->async_wq, &video->pump);
return ret;
if (!video->async_wq)
return -EINVAL;
+ /* Allocate a kthread for asynchronous hw submit handler. */
+ video->kworker = kthread_create_worker(0, "UVCG");
+ if (IS_ERR(video->kworker)) {
+ uvcg_err(&video->uvc->func, "failed to create UVCG kworker\n");
+ return PTR_ERR(video->kworker);
+ }
+
+ kthread_init_work(&video->hw_submit, uvcg_video_hw_submit);
+
+ sched_set_fifo(video->kworker->task);
+
video->uvc = uvc;
video->fcc = V4L2_PIX_FMT_YUYV;
video->bpp = 16;