162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * uvc_video.c -- USB Video Class Gadget driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2009-2010 662306a36Sopenharmony_ci * Laurent Pinchart (laurent.pinchart@ideasonboard.com) 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/device.h> 1162306a36Sopenharmony_ci#include <linux/errno.h> 1262306a36Sopenharmony_ci#include <linux/usb/ch9.h> 1362306a36Sopenharmony_ci#include <linux/usb/gadget.h> 1462306a36Sopenharmony_ci#include <linux/usb/video.h> 1562306a36Sopenharmony_ci#include <asm/unaligned.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <media/v4l2-dev.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "uvc.h" 2062306a36Sopenharmony_ci#include "uvc_queue.h" 2162306a36Sopenharmony_ci#include "uvc_video.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* -------------------------------------------------------------------------- 2462306a36Sopenharmony_ci * Video codecs 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic int 2862306a36Sopenharmony_ciuvc_video_encode_header(struct uvc_video *video, struct uvc_buffer *buf, 2962306a36Sopenharmony_ci u8 *data, int len) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci struct uvc_device *uvc = container_of(video, struct uvc_device, video); 3262306a36Sopenharmony_ci struct usb_composite_dev *cdev = uvc->func.config->cdev; 3362306a36Sopenharmony_ci struct timespec64 ts = ns_to_timespec64(buf->buf.vb2_buf.timestamp); 3462306a36Sopenharmony_ci int pos = 2; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci data[1] = UVC_STREAM_EOH | video->fid; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci if (video->queue.buf_used == 0 && ts.tv_sec) { 3962306a36Sopenharmony_ci /* dwClockFrequency is 48 MHz */ 4062306a36Sopenharmony_ci u32 pts = ((u64)ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / NSEC_PER_USEC) * 48; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci data[1] |= UVC_STREAM_PTS; 4362306a36Sopenharmony_ci put_unaligned_le32(pts, &data[pos]); 4462306a36Sopenharmony_ci pos += 4; 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci if (cdev->gadget->ops->get_frame) { 4862306a36Sopenharmony_ci u32 sof, stc; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci sof = usb_gadget_frame_number(cdev->gadget); 5162306a36Sopenharmony_ci ktime_get_ts64(&ts); 5262306a36Sopenharmony_ci stc = ((u64)ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / NSEC_PER_USEC) * 48; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci data[1] |= UVC_STREAM_SCR; 5562306a36Sopenharmony_ci put_unaligned_le32(stc, &data[pos]); 5662306a36Sopenharmony_ci put_unaligned_le16(sof, &data[pos+4]); 5762306a36Sopenharmony_ci pos += 6; 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci data[0] = pos; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (buf->bytesused - video->queue.buf_used <= len - pos) 6362306a36Sopenharmony_ci data[1] |= UVC_STREAM_EOF; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci return pos; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic int 6962306a36Sopenharmony_ciuvc_video_encode_data(struct uvc_video *video, struct uvc_buffer *buf, 7062306a36Sopenharmony_ci u8 *data, int len) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci struct uvc_video_queue *queue = &video->queue; 7362306a36Sopenharmony_ci unsigned int nbytes; 7462306a36Sopenharmony_ci void *mem; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* Copy video data to the USB buffer. */ 7762306a36Sopenharmony_ci mem = buf->mem + queue->buf_used; 7862306a36Sopenharmony_ci nbytes = min((unsigned int)len, buf->bytesused - queue->buf_used); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci memcpy(data, mem, nbytes); 8162306a36Sopenharmony_ci queue->buf_used += nbytes; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return nbytes; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic void 8762306a36Sopenharmony_ciuvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video, 8862306a36Sopenharmony_ci struct uvc_buffer *buf) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci void *mem = req->buf; 9162306a36Sopenharmony_ci struct uvc_request *ureq = req->context; 9262306a36Sopenharmony_ci int len = video->req_size; 9362306a36Sopenharmony_ci int ret; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* Add a header at the beginning of the payload. */ 9662306a36Sopenharmony_ci if (video->payload_size == 0) { 9762306a36Sopenharmony_ci ret = uvc_video_encode_header(video, buf, mem, len); 9862306a36Sopenharmony_ci video->payload_size += ret; 9962306a36Sopenharmony_ci mem += ret; 10062306a36Sopenharmony_ci len -= ret; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* Process video data. */ 10462306a36Sopenharmony_ci len = min((int)(video->max_payload_size - video->payload_size), len); 10562306a36Sopenharmony_ci ret = uvc_video_encode_data(video, buf, mem, len); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci video->payload_size += ret; 10862306a36Sopenharmony_ci len -= ret; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci req->length = video->req_size - len; 11162306a36Sopenharmony_ci req->zero = video->payload_size == video->max_payload_size; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (buf->bytesused == video->queue.buf_used) { 11462306a36Sopenharmony_ci video->queue.buf_used = 0; 11562306a36Sopenharmony_ci buf->state = UVC_BUF_STATE_DONE; 11662306a36Sopenharmony_ci list_del(&buf->queue); 11762306a36Sopenharmony_ci video->fid ^= UVC_STREAM_FID; 11862306a36Sopenharmony_ci ureq->last_buf = buf; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci video->payload_size = 0; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (video->payload_size == video->max_payload_size || 12462306a36Sopenharmony_ci video->queue.flags & UVC_QUEUE_DROP_INCOMPLETE || 12562306a36Sopenharmony_ci buf->bytesused == video->queue.buf_used) 12662306a36Sopenharmony_ci video->payload_size = 0; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic void 13062306a36Sopenharmony_ciuvc_video_encode_isoc_sg(struct usb_request *req, struct uvc_video *video, 13162306a36Sopenharmony_ci struct uvc_buffer *buf) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci unsigned int pending = buf->bytesused - video->queue.buf_used; 13462306a36Sopenharmony_ci struct uvc_request *ureq = req->context; 13562306a36Sopenharmony_ci struct scatterlist *sg, *iter; 13662306a36Sopenharmony_ci unsigned int len = video->req_size; 13762306a36Sopenharmony_ci unsigned int sg_left, part = 0; 13862306a36Sopenharmony_ci unsigned int i; 13962306a36Sopenharmony_ci int header_len; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci sg = ureq->sgt.sgl; 14262306a36Sopenharmony_ci sg_init_table(sg, ureq->sgt.nents); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* Init the header. */ 14562306a36Sopenharmony_ci header_len = uvc_video_encode_header(video, buf, ureq->header, 14662306a36Sopenharmony_ci video->req_size); 14762306a36Sopenharmony_ci sg_set_buf(sg, ureq->header, header_len); 14862306a36Sopenharmony_ci len -= header_len; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (pending <= len) 15162306a36Sopenharmony_ci len = pending; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci req->length = (len == pending) ? 15462306a36Sopenharmony_ci len + header_len : video->req_size; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* Init the pending sgs with payload */ 15762306a36Sopenharmony_ci sg = sg_next(sg); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci for_each_sg(sg, iter, ureq->sgt.nents - 1, i) { 16062306a36Sopenharmony_ci if (!len || !buf->sg || !buf->sg->length) 16162306a36Sopenharmony_ci break; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci sg_left = buf->sg->length - buf->offset; 16462306a36Sopenharmony_ci part = min_t(unsigned int, len, sg_left); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci sg_set_page(iter, sg_page(buf->sg), part, buf->offset); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (part == sg_left) { 16962306a36Sopenharmony_ci buf->offset = 0; 17062306a36Sopenharmony_ci buf->sg = sg_next(buf->sg); 17162306a36Sopenharmony_ci } else { 17262306a36Sopenharmony_ci buf->offset += part; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci len -= part; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* Assign the video data with header. */ 17862306a36Sopenharmony_ci req->buf = NULL; 17962306a36Sopenharmony_ci req->sg = ureq->sgt.sgl; 18062306a36Sopenharmony_ci req->num_sgs = i + 1; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci req->length -= len; 18362306a36Sopenharmony_ci video->queue.buf_used += req->length - header_len; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (buf->bytesused == video->queue.buf_used || !buf->sg || 18662306a36Sopenharmony_ci video->queue.flags & UVC_QUEUE_DROP_INCOMPLETE) { 18762306a36Sopenharmony_ci video->queue.buf_used = 0; 18862306a36Sopenharmony_ci buf->state = UVC_BUF_STATE_DONE; 18962306a36Sopenharmony_ci buf->offset = 0; 19062306a36Sopenharmony_ci list_del(&buf->queue); 19162306a36Sopenharmony_ci video->fid ^= UVC_STREAM_FID; 19262306a36Sopenharmony_ci ureq->last_buf = buf; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic void 19762306a36Sopenharmony_ciuvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video, 19862306a36Sopenharmony_ci struct uvc_buffer *buf) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci void *mem = req->buf; 20162306a36Sopenharmony_ci struct uvc_request *ureq = req->context; 20262306a36Sopenharmony_ci int len = video->req_size; 20362306a36Sopenharmony_ci int ret; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* Add the header. */ 20662306a36Sopenharmony_ci ret = uvc_video_encode_header(video, buf, mem, len); 20762306a36Sopenharmony_ci mem += ret; 20862306a36Sopenharmony_ci len -= ret; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* Process video data. */ 21162306a36Sopenharmony_ci ret = uvc_video_encode_data(video, buf, mem, len); 21262306a36Sopenharmony_ci len -= ret; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci req->length = video->req_size - len; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (buf->bytesused == video->queue.buf_used || 21762306a36Sopenharmony_ci video->queue.flags & UVC_QUEUE_DROP_INCOMPLETE) { 21862306a36Sopenharmony_ci video->queue.buf_used = 0; 21962306a36Sopenharmony_ci buf->state = UVC_BUF_STATE_DONE; 22062306a36Sopenharmony_ci list_del(&buf->queue); 22162306a36Sopenharmony_ci video->fid ^= UVC_STREAM_FID; 22262306a36Sopenharmony_ci ureq->last_buf = buf; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci/* -------------------------------------------------------------------------- 22762306a36Sopenharmony_ci * Request handling 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci int ret; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci ret = usb_ep_queue(video->ep, req, GFP_ATOMIC); 23562306a36Sopenharmony_ci if (ret < 0) { 23662306a36Sopenharmony_ci uvcg_err(&video->uvc->func, "Failed to queue request (%d).\n", 23762306a36Sopenharmony_ci ret); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* If the endpoint is disabled the descriptor may be NULL. */ 24062306a36Sopenharmony_ci if (video->ep->desc) { 24162306a36Sopenharmony_ci /* Isochronous endpoints can't be halted. */ 24262306a36Sopenharmony_ci if (usb_endpoint_xfer_bulk(video->ep->desc)) 24362306a36Sopenharmony_ci usb_ep_set_halt(video->ep); 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci return ret; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic void 25162306a36Sopenharmony_ciuvc_video_complete(struct usb_ep *ep, struct usb_request *req) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct uvc_request *ureq = req->context; 25462306a36Sopenharmony_ci struct uvc_video *video = ureq->video; 25562306a36Sopenharmony_ci struct uvc_video_queue *queue = &video->queue; 25662306a36Sopenharmony_ci struct uvc_device *uvc = video->uvc; 25762306a36Sopenharmony_ci unsigned long flags; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci switch (req->status) { 26062306a36Sopenharmony_ci case 0: 26162306a36Sopenharmony_ci break; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci case -EXDEV: 26462306a36Sopenharmony_ci uvcg_dbg(&video->uvc->func, "VS request missed xfer.\n"); 26562306a36Sopenharmony_ci queue->flags |= UVC_QUEUE_DROP_INCOMPLETE; 26662306a36Sopenharmony_ci break; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci case -ESHUTDOWN: /* disconnect from host. */ 26962306a36Sopenharmony_ci uvcg_dbg(&video->uvc->func, "VS request cancelled.\n"); 27062306a36Sopenharmony_ci uvcg_queue_cancel(queue, 1); 27162306a36Sopenharmony_ci break; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci default: 27462306a36Sopenharmony_ci uvcg_warn(&video->uvc->func, 27562306a36Sopenharmony_ci "VS request completed with status %d.\n", 27662306a36Sopenharmony_ci req->status); 27762306a36Sopenharmony_ci uvcg_queue_cancel(queue, 0); 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (ureq->last_buf) { 28162306a36Sopenharmony_ci uvcg_complete_buffer(&video->queue, ureq->last_buf); 28262306a36Sopenharmony_ci ureq->last_buf = NULL; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci spin_lock_irqsave(&video->req_lock, flags); 28662306a36Sopenharmony_ci list_add_tail(&req->list, &video->req_free); 28762306a36Sopenharmony_ci spin_unlock_irqrestore(&video->req_lock, flags); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (uvc->state == UVC_STATE_STREAMING) 29062306a36Sopenharmony_ci queue_work(video->async_wq, &video->pump); 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic int 29462306a36Sopenharmony_ciuvc_video_free_requests(struct uvc_video *video) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci unsigned int i; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (video->ureq) { 29962306a36Sopenharmony_ci for (i = 0; i < video->uvc_num_requests; ++i) { 30062306a36Sopenharmony_ci sg_free_table(&video->ureq[i].sgt); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (video->ureq[i].req) { 30362306a36Sopenharmony_ci usb_ep_free_request(video->ep, video->ureq[i].req); 30462306a36Sopenharmony_ci video->ureq[i].req = NULL; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (video->ureq[i].req_buffer) { 30862306a36Sopenharmony_ci kfree(video->ureq[i].req_buffer); 30962306a36Sopenharmony_ci video->ureq[i].req_buffer = NULL; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci kfree(video->ureq); 31462306a36Sopenharmony_ci video->ureq = NULL; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci INIT_LIST_HEAD(&video->req_free); 31862306a36Sopenharmony_ci video->req_size = 0; 31962306a36Sopenharmony_ci return 0; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic int 32362306a36Sopenharmony_ciuvc_video_alloc_requests(struct uvc_video *video) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci unsigned int req_size; 32662306a36Sopenharmony_ci unsigned int i; 32762306a36Sopenharmony_ci int ret = -ENOMEM; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci BUG_ON(video->req_size); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci req_size = video->ep->maxpacket 33262306a36Sopenharmony_ci * max_t(unsigned int, video->ep->maxburst, 1) 33362306a36Sopenharmony_ci * (video->ep->mult); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci video->ureq = kcalloc(video->uvc_num_requests, sizeof(struct uvc_request), GFP_KERNEL); 33662306a36Sopenharmony_ci if (video->ureq == NULL) 33762306a36Sopenharmony_ci return -ENOMEM; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci for (i = 0; i < video->uvc_num_requests; ++i) { 34062306a36Sopenharmony_ci video->ureq[i].req_buffer = kmalloc(req_size, GFP_KERNEL); 34162306a36Sopenharmony_ci if (video->ureq[i].req_buffer == NULL) 34262306a36Sopenharmony_ci goto error; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci video->ureq[i].req = usb_ep_alloc_request(video->ep, GFP_KERNEL); 34562306a36Sopenharmony_ci if (video->ureq[i].req == NULL) 34662306a36Sopenharmony_ci goto error; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci video->ureq[i].req->buf = video->ureq[i].req_buffer; 34962306a36Sopenharmony_ci video->ureq[i].req->length = 0; 35062306a36Sopenharmony_ci video->ureq[i].req->complete = uvc_video_complete; 35162306a36Sopenharmony_ci video->ureq[i].req->context = &video->ureq[i]; 35262306a36Sopenharmony_ci video->ureq[i].video = video; 35362306a36Sopenharmony_ci video->ureq[i].last_buf = NULL; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci list_add_tail(&video->ureq[i].req->list, &video->req_free); 35662306a36Sopenharmony_ci /* req_size/PAGE_SIZE + 1 for overruns and + 1 for header */ 35762306a36Sopenharmony_ci sg_alloc_table(&video->ureq[i].sgt, 35862306a36Sopenharmony_ci DIV_ROUND_UP(req_size - UVCG_REQUEST_HEADER_LEN, 35962306a36Sopenharmony_ci PAGE_SIZE) + 2, GFP_KERNEL); 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci video->req_size = req_size; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci return 0; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cierror: 36762306a36Sopenharmony_ci uvc_video_free_requests(video); 36862306a36Sopenharmony_ci return ret; 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci/* -------------------------------------------------------------------------- 37262306a36Sopenharmony_ci * Video streaming 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci/* 37662306a36Sopenharmony_ci * uvcg_video_pump - Pump video data into the USB requests 37762306a36Sopenharmony_ci * 37862306a36Sopenharmony_ci * This function fills the available USB requests (listed in req_free) with 37962306a36Sopenharmony_ci * video data from the queued buffers. 38062306a36Sopenharmony_ci */ 38162306a36Sopenharmony_cistatic void uvcg_video_pump(struct work_struct *work) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci struct uvc_video *video = container_of(work, struct uvc_video, pump); 38462306a36Sopenharmony_ci struct uvc_video_queue *queue = &video->queue; 38562306a36Sopenharmony_ci /* video->max_payload_size is only set when using bulk transfer */ 38662306a36Sopenharmony_ci bool is_bulk = video->max_payload_size; 38762306a36Sopenharmony_ci struct usb_request *req = NULL; 38862306a36Sopenharmony_ci struct uvc_buffer *buf; 38962306a36Sopenharmony_ci unsigned long flags; 39062306a36Sopenharmony_ci bool buf_done; 39162306a36Sopenharmony_ci int ret; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci while (video->ep->enabled) { 39462306a36Sopenharmony_ci /* 39562306a36Sopenharmony_ci * Retrieve the first available USB request, protected by the 39662306a36Sopenharmony_ci * request lock. 39762306a36Sopenharmony_ci */ 39862306a36Sopenharmony_ci spin_lock_irqsave(&video->req_lock, flags); 39962306a36Sopenharmony_ci if (list_empty(&video->req_free)) { 40062306a36Sopenharmony_ci spin_unlock_irqrestore(&video->req_lock, flags); 40162306a36Sopenharmony_ci return; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci req = list_first_entry(&video->req_free, struct usb_request, 40462306a36Sopenharmony_ci list); 40562306a36Sopenharmony_ci list_del(&req->list); 40662306a36Sopenharmony_ci spin_unlock_irqrestore(&video->req_lock, flags); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* 40962306a36Sopenharmony_ci * Retrieve the first available video buffer and fill the 41062306a36Sopenharmony_ci * request, protected by the video queue irqlock. 41162306a36Sopenharmony_ci */ 41262306a36Sopenharmony_ci spin_lock_irqsave(&queue->irqlock, flags); 41362306a36Sopenharmony_ci buf = uvcg_queue_head(queue); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (buf != NULL) { 41662306a36Sopenharmony_ci video->encode(req, video, buf); 41762306a36Sopenharmony_ci buf_done = buf->state == UVC_BUF_STATE_DONE; 41862306a36Sopenharmony_ci } else if (!(queue->flags & UVC_QUEUE_DISCONNECTED) && !is_bulk) { 41962306a36Sopenharmony_ci /* 42062306a36Sopenharmony_ci * No video buffer available; the queue is still connected and 42162306a36Sopenharmony_ci * we're transferring over ISOC. Queue a 0 length request to 42262306a36Sopenharmony_ci * prevent missed ISOC transfers. 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_ci req->length = 0; 42562306a36Sopenharmony_ci buf_done = false; 42662306a36Sopenharmony_ci } else { 42762306a36Sopenharmony_ci /* 42862306a36Sopenharmony_ci * Either the queue has been disconnected or no video buffer 42962306a36Sopenharmony_ci * available for bulk transfer. Either way, stop processing 43062306a36Sopenharmony_ci * further. 43162306a36Sopenharmony_ci */ 43262306a36Sopenharmony_ci spin_unlock_irqrestore(&queue->irqlock, flags); 43362306a36Sopenharmony_ci break; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* 43762306a36Sopenharmony_ci * With USB3 handling more requests at a higher speed, we can't 43862306a36Sopenharmony_ci * afford to generate an interrupt for every request. Decide to 43962306a36Sopenharmony_ci * interrupt: 44062306a36Sopenharmony_ci * 44162306a36Sopenharmony_ci * - When no more requests are available in the free queue, as 44262306a36Sopenharmony_ci * this may be our last chance to refill the endpoint's 44362306a36Sopenharmony_ci * request queue. 44462306a36Sopenharmony_ci * 44562306a36Sopenharmony_ci * - When this is request is the last request for the video 44662306a36Sopenharmony_ci * buffer, as we want to start sending the next video buffer 44762306a36Sopenharmony_ci * ASAP in case it doesn't get started already in the next 44862306a36Sopenharmony_ci * iteration of this loop. 44962306a36Sopenharmony_ci * 45062306a36Sopenharmony_ci * - Four times over the length of the requests queue (as 45162306a36Sopenharmony_ci * indicated by video->uvc_num_requests), as a trade-off 45262306a36Sopenharmony_ci * between latency and interrupt load. 45362306a36Sopenharmony_ci */ 45462306a36Sopenharmony_ci if (list_empty(&video->req_free) || buf_done || 45562306a36Sopenharmony_ci !(video->req_int_count % 45662306a36Sopenharmony_ci DIV_ROUND_UP(video->uvc_num_requests, 4))) { 45762306a36Sopenharmony_ci video->req_int_count = 0; 45862306a36Sopenharmony_ci req->no_interrupt = 0; 45962306a36Sopenharmony_ci } else { 46062306a36Sopenharmony_ci req->no_interrupt = 1; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* Queue the USB request */ 46462306a36Sopenharmony_ci ret = uvcg_video_ep_queue(video, req); 46562306a36Sopenharmony_ci spin_unlock_irqrestore(&queue->irqlock, flags); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (ret < 0) { 46862306a36Sopenharmony_ci uvcg_queue_cancel(queue, 0); 46962306a36Sopenharmony_ci break; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* Endpoint now owns the request */ 47362306a36Sopenharmony_ci req = NULL; 47462306a36Sopenharmony_ci video->req_int_count++; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci if (!req) 47862306a36Sopenharmony_ci return; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci spin_lock_irqsave(&video->req_lock, flags); 48162306a36Sopenharmony_ci list_add_tail(&req->list, &video->req_free); 48262306a36Sopenharmony_ci spin_unlock_irqrestore(&video->req_lock, flags); 48362306a36Sopenharmony_ci return; 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci/* 48762306a36Sopenharmony_ci * Enable or disable the video stream. 48862306a36Sopenharmony_ci */ 48962306a36Sopenharmony_ciint uvcg_video_enable(struct uvc_video *video, int enable) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci unsigned int i; 49262306a36Sopenharmony_ci int ret; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (video->ep == NULL) { 49562306a36Sopenharmony_ci uvcg_info(&video->uvc->func, 49662306a36Sopenharmony_ci "Video enable failed, device is uninitialized.\n"); 49762306a36Sopenharmony_ci return -ENODEV; 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci if (!enable) { 50162306a36Sopenharmony_ci cancel_work_sync(&video->pump); 50262306a36Sopenharmony_ci uvcg_queue_cancel(&video->queue, 0); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci for (i = 0; i < video->uvc_num_requests; ++i) 50562306a36Sopenharmony_ci if (video->ureq && video->ureq[i].req) 50662306a36Sopenharmony_ci usb_ep_dequeue(video->ep, video->ureq[i].req); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci uvc_video_free_requests(video); 50962306a36Sopenharmony_ci uvcg_queue_enable(&video->queue, 0); 51062306a36Sopenharmony_ci return 0; 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if ((ret = uvcg_queue_enable(&video->queue, 1)) < 0) 51462306a36Sopenharmony_ci return ret; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if ((ret = uvc_video_alloc_requests(video)) < 0) 51762306a36Sopenharmony_ci return ret; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci if (video->max_payload_size) { 52062306a36Sopenharmony_ci video->encode = uvc_video_encode_bulk; 52162306a36Sopenharmony_ci video->payload_size = 0; 52262306a36Sopenharmony_ci } else 52362306a36Sopenharmony_ci video->encode = video->queue.use_sg ? 52462306a36Sopenharmony_ci uvc_video_encode_isoc_sg : uvc_video_encode_isoc; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci video->req_int_count = 0; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci queue_work(video->async_wq, &video->pump); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci return ret; 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci/* 53462306a36Sopenharmony_ci * Initialize the UVC video stream. 53562306a36Sopenharmony_ci */ 53662306a36Sopenharmony_ciint uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci INIT_LIST_HEAD(&video->req_free); 53962306a36Sopenharmony_ci spin_lock_init(&video->req_lock); 54062306a36Sopenharmony_ci INIT_WORK(&video->pump, uvcg_video_pump); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci /* Allocate a work queue for asynchronous video pump handler. */ 54362306a36Sopenharmony_ci video->async_wq = alloc_workqueue("uvcgadget", WQ_UNBOUND | WQ_HIGHPRI, 0); 54462306a36Sopenharmony_ci if (!video->async_wq) 54562306a36Sopenharmony_ci return -EINVAL; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci video->uvc = uvc; 54862306a36Sopenharmony_ci video->fcc = V4L2_PIX_FMT_YUYV; 54962306a36Sopenharmony_ci video->bpp = 16; 55062306a36Sopenharmony_ci video->width = 320; 55162306a36Sopenharmony_ci video->height = 240; 55262306a36Sopenharmony_ci video->imagesize = 320 * 240 * 2; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci /* Initialize the video buffers queue. */ 55562306a36Sopenharmony_ci uvcg_queue_init(&video->queue, uvc->v4l2_dev.dev->parent, 55662306a36Sopenharmony_ci V4L2_BUF_TYPE_VIDEO_OUTPUT, &video->mutex); 55762306a36Sopenharmony_ci return 0; 55862306a36Sopenharmony_ci} 559