162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * uvc_v4l2.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/device.h> 1062306a36Sopenharmony_ci#include <linux/errno.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/list.h> 1362306a36Sopenharmony_ci#include <linux/usb/g_uvc.h> 1462306a36Sopenharmony_ci#include <linux/usb/uvc.h> 1562306a36Sopenharmony_ci#include <linux/videodev2.h> 1662306a36Sopenharmony_ci#include <linux/vmalloc.h> 1762306a36Sopenharmony_ci#include <linux/wait.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <media/v4l2-dev.h> 2062306a36Sopenharmony_ci#include <media/v4l2-event.h> 2162306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "f_uvc.h" 2462306a36Sopenharmony_ci#include "uvc.h" 2562306a36Sopenharmony_ci#include "uvc_queue.h" 2662306a36Sopenharmony_ci#include "uvc_video.h" 2762306a36Sopenharmony_ci#include "uvc_v4l2.h" 2862306a36Sopenharmony_ci#include "uvc_configfs.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic const struct uvc_format_desc *to_uvc_format(struct uvcg_format *uformat) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci char guid[16] = UVC_GUID_FORMAT_MJPEG; 3362306a36Sopenharmony_ci const struct uvc_format_desc *format; 3462306a36Sopenharmony_ci struct uvcg_uncompressed *unc; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci if (uformat->type == UVCG_UNCOMPRESSED) { 3762306a36Sopenharmony_ci unc = to_uvcg_uncompressed(&uformat->group.cg_item); 3862306a36Sopenharmony_ci if (!unc) 3962306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci memcpy(guid, unc->desc.guidFormat, sizeof(guid)); 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci format = uvc_format_by_guid(guid); 4562306a36Sopenharmony_ci if (!format) 4662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci return format; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic int uvc_v4l2_get_bytesperline(struct uvcg_format *uformat, 5262306a36Sopenharmony_ci struct uvcg_frame *uframe) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct uvcg_uncompressed *u; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (uformat->type == UVCG_UNCOMPRESSED) { 5762306a36Sopenharmony_ci u = to_uvcg_uncompressed(&uformat->group.cg_item); 5862306a36Sopenharmony_ci if (!u) 5962306a36Sopenharmony_ci return 0; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return u->desc.bBitsPerPixel * uframe->frame.w_width / 8; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci return 0; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic int uvc_get_frame_size(struct uvcg_format *uformat, 6862306a36Sopenharmony_ci struct uvcg_frame *uframe) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci unsigned int bpl = uvc_v4l2_get_bytesperline(uformat, uframe); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return bpl ? bpl * uframe->frame.w_height : 7362306a36Sopenharmony_ci uframe->frame.dw_max_video_frame_buffer_size; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic struct uvcg_format *find_format_by_index(struct uvc_device *uvc, int index) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct uvcg_format_ptr *format; 7962306a36Sopenharmony_ci struct uvcg_format *uformat = NULL; 8062306a36Sopenharmony_ci int i = 1; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci list_for_each_entry(format, &uvc->header->formats, entry) { 8362306a36Sopenharmony_ci if (index == i) { 8462306a36Sopenharmony_ci uformat = format->fmt; 8562306a36Sopenharmony_ci break; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci i++; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci return uformat; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic struct uvcg_frame *find_frame_by_index(struct uvc_device *uvc, 9462306a36Sopenharmony_ci struct uvcg_format *uformat, 9562306a36Sopenharmony_ci int index) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci struct uvcg_format_ptr *format; 9862306a36Sopenharmony_ci struct uvcg_frame_ptr *frame; 9962306a36Sopenharmony_ci struct uvcg_frame *uframe = NULL; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci list_for_each_entry(format, &uvc->header->formats, entry) { 10262306a36Sopenharmony_ci if (format->fmt->type != uformat->type) 10362306a36Sopenharmony_ci continue; 10462306a36Sopenharmony_ci list_for_each_entry(frame, &format->fmt->frames, entry) { 10562306a36Sopenharmony_ci if (index == frame->frm->frame.b_frame_index) { 10662306a36Sopenharmony_ci uframe = frame->frm; 10762306a36Sopenharmony_ci break; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return uframe; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic struct uvcg_format *find_format_by_pix(struct uvc_device *uvc, 11662306a36Sopenharmony_ci u32 pixelformat) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct uvcg_format_ptr *format; 11962306a36Sopenharmony_ci struct uvcg_format *uformat = NULL; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci list_for_each_entry(format, &uvc->header->formats, entry) { 12262306a36Sopenharmony_ci const struct uvc_format_desc *fmtdesc = to_uvc_format(format->fmt); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (fmtdesc->fcc == pixelformat) { 12562306a36Sopenharmony_ci uformat = format->fmt; 12662306a36Sopenharmony_ci break; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return uformat; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic struct uvcg_frame *find_closest_frame_by_size(struct uvc_device *uvc, 13462306a36Sopenharmony_ci struct uvcg_format *uformat, 13562306a36Sopenharmony_ci u16 rw, u16 rh) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct uvc_video *video = &uvc->video; 13862306a36Sopenharmony_ci struct uvcg_format_ptr *format; 13962306a36Sopenharmony_ci struct uvcg_frame_ptr *frame; 14062306a36Sopenharmony_ci struct uvcg_frame *uframe = NULL; 14162306a36Sopenharmony_ci unsigned int d, maxd; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* Find the closest image size. The distance between image sizes is 14462306a36Sopenharmony_ci * the size in pixels of the non-overlapping regions between the 14562306a36Sopenharmony_ci * requested size and the frame-specified size. 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_ci maxd = (unsigned int)-1; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci list_for_each_entry(format, &uvc->header->formats, entry) { 15062306a36Sopenharmony_ci if (format->fmt->type != uformat->type) 15162306a36Sopenharmony_ci continue; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci list_for_each_entry(frame, &format->fmt->frames, entry) { 15462306a36Sopenharmony_ci u16 w, h; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci w = frame->frm->frame.w_width; 15762306a36Sopenharmony_ci h = frame->frm->frame.w_height; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci d = min(w, rw) * min(h, rh); 16062306a36Sopenharmony_ci d = w*h + rw*rh - 2*d; 16162306a36Sopenharmony_ci if (d < maxd) { 16262306a36Sopenharmony_ci maxd = d; 16362306a36Sopenharmony_ci uframe = frame->frm; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (maxd == 0) 16762306a36Sopenharmony_ci break; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (!uframe) 17262306a36Sopenharmony_ci uvcg_dbg(&video->uvc->func, "Unsupported size %ux%u\n", rw, rh); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci return uframe; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci/* -------------------------------------------------------------------------- 17862306a36Sopenharmony_ci * Requests handling 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic int 18262306a36Sopenharmony_ciuvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct usb_composite_dev *cdev = uvc->func.config->cdev; 18562306a36Sopenharmony_ci struct usb_request *req = uvc->control_req; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (data->length < 0) 18862306a36Sopenharmony_ci return usb_ep_set_halt(cdev->gadget->ep0); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci req->length = min_t(unsigned int, uvc->event_length, data->length); 19162306a36Sopenharmony_ci req->zero = data->length < uvc->event_length; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci memcpy(req->buf, data->data, req->length); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci return usb_ep_queue(cdev->gadget->ep0, req, GFP_KERNEL); 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci/* -------------------------------------------------------------------------- 19962306a36Sopenharmony_ci * V4L2 ioctls 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic int 20362306a36Sopenharmony_ciuvc_v4l2_querycap(struct file *file, void *fh, struct v4l2_capability *cap) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct video_device *vdev = video_devdata(file); 20662306a36Sopenharmony_ci struct uvc_device *uvc = video_get_drvdata(vdev); 20762306a36Sopenharmony_ci struct usb_composite_dev *cdev = uvc->func.config->cdev; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci strscpy(cap->driver, "g_uvc", sizeof(cap->driver)); 21062306a36Sopenharmony_ci strscpy(cap->card, cdev->gadget->name, sizeof(cap->card)); 21162306a36Sopenharmony_ci strscpy(cap->bus_info, dev_name(&cdev->gadget->dev), 21262306a36Sopenharmony_ci sizeof(cap->bus_info)); 21362306a36Sopenharmony_ci return 0; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic int 21762306a36Sopenharmony_ciuvc_v4l2_get_format(struct file *file, void *fh, struct v4l2_format *fmt) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct video_device *vdev = video_devdata(file); 22062306a36Sopenharmony_ci struct uvc_device *uvc = video_get_drvdata(vdev); 22162306a36Sopenharmony_ci struct uvc_video *video = &uvc->video; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci fmt->fmt.pix.pixelformat = video->fcc; 22462306a36Sopenharmony_ci fmt->fmt.pix.width = video->width; 22562306a36Sopenharmony_ci fmt->fmt.pix.height = video->height; 22662306a36Sopenharmony_ci fmt->fmt.pix.field = V4L2_FIELD_NONE; 22762306a36Sopenharmony_ci fmt->fmt.pix.bytesperline = video->bpp * video->width / 8; 22862306a36Sopenharmony_ci fmt->fmt.pix.sizeimage = video->imagesize; 22962306a36Sopenharmony_ci fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; 23062306a36Sopenharmony_ci fmt->fmt.pix.priv = 0; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci return 0; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic int 23662306a36Sopenharmony_ciuvc_v4l2_try_format(struct file *file, void *fh, struct v4l2_format *fmt) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct video_device *vdev = video_devdata(file); 23962306a36Sopenharmony_ci struct uvc_device *uvc = video_get_drvdata(vdev); 24062306a36Sopenharmony_ci struct uvc_video *video = &uvc->video; 24162306a36Sopenharmony_ci struct uvcg_format *uformat; 24262306a36Sopenharmony_ci struct uvcg_frame *uframe; 24362306a36Sopenharmony_ci u8 *fcc; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (fmt->type != video->queue.queue.type) 24662306a36Sopenharmony_ci return -EINVAL; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci fcc = (u8 *)&fmt->fmt.pix.pixelformat; 24962306a36Sopenharmony_ci uvcg_dbg(&uvc->func, "Trying format 0x%08x (%c%c%c%c): %ux%u\n", 25062306a36Sopenharmony_ci fmt->fmt.pix.pixelformat, 25162306a36Sopenharmony_ci fcc[0], fcc[1], fcc[2], fcc[3], 25262306a36Sopenharmony_ci fmt->fmt.pix.width, fmt->fmt.pix.height); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci uformat = find_format_by_pix(uvc, fmt->fmt.pix.pixelformat); 25562306a36Sopenharmony_ci if (!uformat) 25662306a36Sopenharmony_ci return -EINVAL; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci uframe = find_closest_frame_by_size(uvc, uformat, 25962306a36Sopenharmony_ci fmt->fmt.pix.width, fmt->fmt.pix.height); 26062306a36Sopenharmony_ci if (!uframe) 26162306a36Sopenharmony_ci return -EINVAL; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci fmt->fmt.pix.width = uframe->frame.w_width; 26462306a36Sopenharmony_ci fmt->fmt.pix.height = uframe->frame.w_height; 26562306a36Sopenharmony_ci fmt->fmt.pix.field = V4L2_FIELD_NONE; 26662306a36Sopenharmony_ci fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(uformat, uframe); 26762306a36Sopenharmony_ci fmt->fmt.pix.sizeimage = uvc_get_frame_size(uformat, uframe); 26862306a36Sopenharmony_ci fmt->fmt.pix.pixelformat = to_uvc_format(uformat)->fcc; 26962306a36Sopenharmony_ci fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; 27062306a36Sopenharmony_ci fmt->fmt.pix.priv = 0; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci return 0; 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic int 27662306a36Sopenharmony_ciuvc_v4l2_set_format(struct file *file, void *fh, struct v4l2_format *fmt) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct video_device *vdev = video_devdata(file); 27962306a36Sopenharmony_ci struct uvc_device *uvc = video_get_drvdata(vdev); 28062306a36Sopenharmony_ci struct uvc_video *video = &uvc->video; 28162306a36Sopenharmony_ci int ret; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci ret = uvc_v4l2_try_format(file, fh, fmt); 28462306a36Sopenharmony_ci if (ret) 28562306a36Sopenharmony_ci return ret; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci video->fcc = fmt->fmt.pix.pixelformat; 28862306a36Sopenharmony_ci video->bpp = fmt->fmt.pix.bytesperline * 8 / video->width; 28962306a36Sopenharmony_ci video->width = fmt->fmt.pix.width; 29062306a36Sopenharmony_ci video->height = fmt->fmt.pix.height; 29162306a36Sopenharmony_ci video->imagesize = fmt->fmt.pix.sizeimage; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci return ret; 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic int 29762306a36Sopenharmony_ciuvc_v4l2_enum_frameintervals(struct file *file, void *fh, 29862306a36Sopenharmony_ci struct v4l2_frmivalenum *fival) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci struct video_device *vdev = video_devdata(file); 30162306a36Sopenharmony_ci struct uvc_device *uvc = video_get_drvdata(vdev); 30262306a36Sopenharmony_ci struct uvcg_format *uformat = NULL; 30362306a36Sopenharmony_ci struct uvcg_frame *uframe = NULL; 30462306a36Sopenharmony_ci struct uvcg_frame_ptr *frame; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci uformat = find_format_by_pix(uvc, fival->pixel_format); 30762306a36Sopenharmony_ci if (!uformat) 30862306a36Sopenharmony_ci return -EINVAL; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci list_for_each_entry(frame, &uformat->frames, entry) { 31162306a36Sopenharmony_ci if (frame->frm->frame.w_width == fival->width && 31262306a36Sopenharmony_ci frame->frm->frame.w_height == fival->height) { 31362306a36Sopenharmony_ci uframe = frame->frm; 31462306a36Sopenharmony_ci break; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci if (!uframe) 31862306a36Sopenharmony_ci return -EINVAL; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (fival->index >= uframe->frame.b_frame_interval_type) 32162306a36Sopenharmony_ci return -EINVAL; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci fival->discrete.numerator = 32462306a36Sopenharmony_ci uframe->dw_frame_interval[fival->index]; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci /* TODO: handle V4L2_FRMIVAL_TYPE_STEPWISE */ 32762306a36Sopenharmony_ci fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; 32862306a36Sopenharmony_ci fival->discrete.denominator = 10000000; 32962306a36Sopenharmony_ci v4l2_simplify_fraction(&fival->discrete.numerator, 33062306a36Sopenharmony_ci &fival->discrete.denominator, 8, 333); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci return 0; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic int 33662306a36Sopenharmony_ciuvc_v4l2_enum_framesizes(struct file *file, void *fh, 33762306a36Sopenharmony_ci struct v4l2_frmsizeenum *fsize) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct video_device *vdev = video_devdata(file); 34062306a36Sopenharmony_ci struct uvc_device *uvc = video_get_drvdata(vdev); 34162306a36Sopenharmony_ci struct uvcg_format *uformat = NULL; 34262306a36Sopenharmony_ci struct uvcg_frame *uframe = NULL; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci uformat = find_format_by_pix(uvc, fsize->pixel_format); 34562306a36Sopenharmony_ci if (!uformat) 34662306a36Sopenharmony_ci return -EINVAL; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (fsize->index >= uformat->num_frames) 34962306a36Sopenharmony_ci return -EINVAL; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci uframe = find_frame_by_index(uvc, uformat, fsize->index + 1); 35262306a36Sopenharmony_ci if (!uframe) 35362306a36Sopenharmony_ci return -EINVAL; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; 35662306a36Sopenharmony_ci fsize->discrete.width = uframe->frame.w_width; 35762306a36Sopenharmony_ci fsize->discrete.height = uframe->frame.w_height; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci return 0; 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic int 36362306a36Sopenharmony_ciuvc_v4l2_enum_format(struct file *file, void *fh, struct v4l2_fmtdesc *f) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci struct video_device *vdev = video_devdata(file); 36662306a36Sopenharmony_ci struct uvc_device *uvc = video_get_drvdata(vdev); 36762306a36Sopenharmony_ci const struct uvc_format_desc *fmtdesc; 36862306a36Sopenharmony_ci struct uvcg_format *uformat; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (f->index >= uvc->header->num_fmt) 37162306a36Sopenharmony_ci return -EINVAL; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci uformat = find_format_by_index(uvc, f->index + 1); 37462306a36Sopenharmony_ci if (!uformat) 37562306a36Sopenharmony_ci return -EINVAL; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci fmtdesc = to_uvc_format(uformat); 37862306a36Sopenharmony_ci f->pixelformat = fmtdesc->fcc; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci return 0; 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic int 38462306a36Sopenharmony_ciuvc_v4l2_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *b) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci struct video_device *vdev = video_devdata(file); 38762306a36Sopenharmony_ci struct uvc_device *uvc = video_get_drvdata(vdev); 38862306a36Sopenharmony_ci struct uvc_video *video = &uvc->video; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (b->type != video->queue.queue.type) 39162306a36Sopenharmony_ci return -EINVAL; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci return uvcg_alloc_buffers(&video->queue, b); 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic int 39762306a36Sopenharmony_ciuvc_v4l2_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci struct video_device *vdev = video_devdata(file); 40062306a36Sopenharmony_ci struct uvc_device *uvc = video_get_drvdata(vdev); 40162306a36Sopenharmony_ci struct uvc_video *video = &uvc->video; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return uvcg_query_buffer(&video->queue, b); 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic int 40762306a36Sopenharmony_ciuvc_v4l2_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci struct video_device *vdev = video_devdata(file); 41062306a36Sopenharmony_ci struct uvc_device *uvc = video_get_drvdata(vdev); 41162306a36Sopenharmony_ci struct uvc_video *video = &uvc->video; 41262306a36Sopenharmony_ci int ret; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci ret = uvcg_queue_buffer(&video->queue, b); 41562306a36Sopenharmony_ci if (ret < 0) 41662306a36Sopenharmony_ci return ret; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (uvc->state == UVC_STATE_STREAMING) 41962306a36Sopenharmony_ci queue_work(video->async_wq, &video->pump); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return ret; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic int 42562306a36Sopenharmony_ciuvc_v4l2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct video_device *vdev = video_devdata(file); 42862306a36Sopenharmony_ci struct uvc_device *uvc = video_get_drvdata(vdev); 42962306a36Sopenharmony_ci struct uvc_video *video = &uvc->video; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci return uvcg_dequeue_buffer(&video->queue, b, file->f_flags & O_NONBLOCK); 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic int 43562306a36Sopenharmony_ciuvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci struct video_device *vdev = video_devdata(file); 43862306a36Sopenharmony_ci struct uvc_device *uvc = video_get_drvdata(vdev); 43962306a36Sopenharmony_ci struct uvc_video *video = &uvc->video; 44062306a36Sopenharmony_ci int ret; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (type != video->queue.queue.type) 44362306a36Sopenharmony_ci return -EINVAL; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* Enable UVC video. */ 44662306a36Sopenharmony_ci ret = uvcg_video_enable(video, 1); 44762306a36Sopenharmony_ci if (ret < 0) 44862306a36Sopenharmony_ci return ret; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* 45162306a36Sopenharmony_ci * Complete the alternate setting selection setup phase now that 45262306a36Sopenharmony_ci * userspace is ready to provide video frames. 45362306a36Sopenharmony_ci */ 45462306a36Sopenharmony_ci uvc_function_setup_continue(uvc); 45562306a36Sopenharmony_ci uvc->state = UVC_STATE_STREAMING; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci return 0; 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic int 46162306a36Sopenharmony_ciuvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci struct video_device *vdev = video_devdata(file); 46462306a36Sopenharmony_ci struct uvc_device *uvc = video_get_drvdata(vdev); 46562306a36Sopenharmony_ci struct uvc_video *video = &uvc->video; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (type != video->queue.queue.type) 46862306a36Sopenharmony_ci return -EINVAL; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci return uvcg_video_enable(video, 0); 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic int 47462306a36Sopenharmony_ciuvc_v4l2_subscribe_event(struct v4l2_fh *fh, 47562306a36Sopenharmony_ci const struct v4l2_event_subscription *sub) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct uvc_device *uvc = video_get_drvdata(fh->vdev); 47862306a36Sopenharmony_ci struct uvc_file_handle *handle = to_uvc_file_handle(fh); 47962306a36Sopenharmony_ci int ret; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST) 48262306a36Sopenharmony_ci return -EINVAL; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if (sub->type == UVC_EVENT_SETUP && uvc->func_connected) 48562306a36Sopenharmony_ci return -EBUSY; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci ret = v4l2_event_subscribe(fh, sub, 2, NULL); 48862306a36Sopenharmony_ci if (ret < 0) 48962306a36Sopenharmony_ci return ret; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (sub->type == UVC_EVENT_SETUP) { 49262306a36Sopenharmony_ci uvc->func_connected = true; 49362306a36Sopenharmony_ci handle->is_uvc_app_handle = true; 49462306a36Sopenharmony_ci uvc_function_connect(uvc); 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci return 0; 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic void uvc_v4l2_disable(struct uvc_device *uvc) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci uvc_function_disconnect(uvc); 50362306a36Sopenharmony_ci uvcg_video_enable(&uvc->video, 0); 50462306a36Sopenharmony_ci uvcg_free_buffers(&uvc->video.queue); 50562306a36Sopenharmony_ci uvc->func_connected = false; 50662306a36Sopenharmony_ci wake_up_interruptible(&uvc->func_connected_queue); 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic int 51062306a36Sopenharmony_ciuvc_v4l2_unsubscribe_event(struct v4l2_fh *fh, 51162306a36Sopenharmony_ci const struct v4l2_event_subscription *sub) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct uvc_device *uvc = video_get_drvdata(fh->vdev); 51462306a36Sopenharmony_ci struct uvc_file_handle *handle = to_uvc_file_handle(fh); 51562306a36Sopenharmony_ci int ret; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci ret = v4l2_event_unsubscribe(fh, sub); 51862306a36Sopenharmony_ci if (ret < 0) 51962306a36Sopenharmony_ci return ret; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (sub->type == UVC_EVENT_SETUP && handle->is_uvc_app_handle) { 52262306a36Sopenharmony_ci uvc_v4l2_disable(uvc); 52362306a36Sopenharmony_ci handle->is_uvc_app_handle = false; 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci return 0; 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic long 53062306a36Sopenharmony_ciuvc_v4l2_ioctl_default(struct file *file, void *fh, bool valid_prio, 53162306a36Sopenharmony_ci unsigned int cmd, void *arg) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci struct video_device *vdev = video_devdata(file); 53462306a36Sopenharmony_ci struct uvc_device *uvc = video_get_drvdata(vdev); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci switch (cmd) { 53762306a36Sopenharmony_ci case UVCIOC_SEND_RESPONSE: 53862306a36Sopenharmony_ci return uvc_send_response(uvc, arg); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci default: 54162306a36Sopenharmony_ci return -ENOIOCTLCMD; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ciconst struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops = { 54662306a36Sopenharmony_ci .vidioc_querycap = uvc_v4l2_querycap, 54762306a36Sopenharmony_ci .vidioc_try_fmt_vid_out = uvc_v4l2_try_format, 54862306a36Sopenharmony_ci .vidioc_g_fmt_vid_out = uvc_v4l2_get_format, 54962306a36Sopenharmony_ci .vidioc_s_fmt_vid_out = uvc_v4l2_set_format, 55062306a36Sopenharmony_ci .vidioc_enum_frameintervals = uvc_v4l2_enum_frameintervals, 55162306a36Sopenharmony_ci .vidioc_enum_framesizes = uvc_v4l2_enum_framesizes, 55262306a36Sopenharmony_ci .vidioc_enum_fmt_vid_out = uvc_v4l2_enum_format, 55362306a36Sopenharmony_ci .vidioc_reqbufs = uvc_v4l2_reqbufs, 55462306a36Sopenharmony_ci .vidioc_querybuf = uvc_v4l2_querybuf, 55562306a36Sopenharmony_ci .vidioc_qbuf = uvc_v4l2_qbuf, 55662306a36Sopenharmony_ci .vidioc_dqbuf = uvc_v4l2_dqbuf, 55762306a36Sopenharmony_ci .vidioc_streamon = uvc_v4l2_streamon, 55862306a36Sopenharmony_ci .vidioc_streamoff = uvc_v4l2_streamoff, 55962306a36Sopenharmony_ci .vidioc_subscribe_event = uvc_v4l2_subscribe_event, 56062306a36Sopenharmony_ci .vidioc_unsubscribe_event = uvc_v4l2_unsubscribe_event, 56162306a36Sopenharmony_ci .vidioc_default = uvc_v4l2_ioctl_default, 56262306a36Sopenharmony_ci}; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci/* -------------------------------------------------------------------------- 56562306a36Sopenharmony_ci * V4L2 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic int 56962306a36Sopenharmony_ciuvc_v4l2_open(struct file *file) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci struct video_device *vdev = video_devdata(file); 57262306a36Sopenharmony_ci struct uvc_device *uvc = video_get_drvdata(vdev); 57362306a36Sopenharmony_ci struct uvc_file_handle *handle; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci handle = kzalloc(sizeof(*handle), GFP_KERNEL); 57662306a36Sopenharmony_ci if (handle == NULL) 57762306a36Sopenharmony_ci return -ENOMEM; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci v4l2_fh_init(&handle->vfh, vdev); 58062306a36Sopenharmony_ci v4l2_fh_add(&handle->vfh); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci handle->device = &uvc->video; 58362306a36Sopenharmony_ci file->private_data = &handle->vfh; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci return 0; 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_cistatic int 58962306a36Sopenharmony_ciuvc_v4l2_release(struct file *file) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci struct video_device *vdev = video_devdata(file); 59262306a36Sopenharmony_ci struct uvc_device *uvc = video_get_drvdata(vdev); 59362306a36Sopenharmony_ci struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data); 59462306a36Sopenharmony_ci struct uvc_video *video = handle->device; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci mutex_lock(&video->mutex); 59762306a36Sopenharmony_ci if (handle->is_uvc_app_handle) 59862306a36Sopenharmony_ci uvc_v4l2_disable(uvc); 59962306a36Sopenharmony_ci mutex_unlock(&video->mutex); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci file->private_data = NULL; 60262306a36Sopenharmony_ci v4l2_fh_del(&handle->vfh); 60362306a36Sopenharmony_ci v4l2_fh_exit(&handle->vfh); 60462306a36Sopenharmony_ci kfree(handle); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci return 0; 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic int 61062306a36Sopenharmony_ciuvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci struct video_device *vdev = video_devdata(file); 61362306a36Sopenharmony_ci struct uvc_device *uvc = video_get_drvdata(vdev); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci return uvcg_queue_mmap(&uvc->video.queue, vma); 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_cistatic __poll_t 61962306a36Sopenharmony_ciuvc_v4l2_poll(struct file *file, poll_table *wait) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci struct video_device *vdev = video_devdata(file); 62262306a36Sopenharmony_ci struct uvc_device *uvc = video_get_drvdata(vdev); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci return uvcg_queue_poll(&uvc->video.queue, file, wait); 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci#ifndef CONFIG_MMU 62862306a36Sopenharmony_cistatic unsigned long uvcg_v4l2_get_unmapped_area(struct file *file, 62962306a36Sopenharmony_ci unsigned long addr, unsigned long len, unsigned long pgoff, 63062306a36Sopenharmony_ci unsigned long flags) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci struct video_device *vdev = video_devdata(file); 63362306a36Sopenharmony_ci struct uvc_device *uvc = video_get_drvdata(vdev); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci return uvcg_queue_get_unmapped_area(&uvc->video.queue, pgoff); 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci#endif 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ciconst struct v4l2_file_operations uvc_v4l2_fops = { 64062306a36Sopenharmony_ci .owner = THIS_MODULE, 64162306a36Sopenharmony_ci .open = uvc_v4l2_open, 64262306a36Sopenharmony_ci .release = uvc_v4l2_release, 64362306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 64462306a36Sopenharmony_ci .mmap = uvc_v4l2_mmap, 64562306a36Sopenharmony_ci .poll = uvc_v4l2_poll, 64662306a36Sopenharmony_ci#ifndef CONFIG_MMU 64762306a36Sopenharmony_ci .get_unmapped_area = uvcg_v4l2_get_unmapped_area, 64862306a36Sopenharmony_ci#endif 64962306a36Sopenharmony_ci}; 65062306a36Sopenharmony_ci 651