18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * uvc_v4l2.c -- USB Video Class driver - V4L2 API 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2005-2010 68c2ecf20Sopenharmony_ci * Laurent Pinchart (laurent.pinchart@ideasonboard.com) 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/compat.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/list.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/usb.h> 158c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 168c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 178c2ecf20Sopenharmony_ci#include <linux/mm.h> 188c2ecf20Sopenharmony_ci#include <linux/wait.h> 198c2ecf20Sopenharmony_ci#include <linux/atomic.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <media/v4l2-common.h> 228c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 238c2ecf20Sopenharmony_ci#include <media/v4l2-event.h> 248c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "uvcvideo.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ 298c2ecf20Sopenharmony_ci * UVC ioctls 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_cistatic int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain, 328c2ecf20Sopenharmony_ci struct uvc_xu_control_mapping *xmap) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci struct uvc_control_mapping *map; 358c2ecf20Sopenharmony_ci unsigned int size; 368c2ecf20Sopenharmony_ci int ret; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci map = kzalloc(sizeof(*map), GFP_KERNEL); 398c2ecf20Sopenharmony_ci if (map == NULL) 408c2ecf20Sopenharmony_ci return -ENOMEM; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci map->id = xmap->id; 438c2ecf20Sopenharmony_ci memcpy(map->name, xmap->name, sizeof(map->name)); 448c2ecf20Sopenharmony_ci memcpy(map->entity, xmap->entity, sizeof(map->entity)); 458c2ecf20Sopenharmony_ci map->selector = xmap->selector; 468c2ecf20Sopenharmony_ci map->size = xmap->size; 478c2ecf20Sopenharmony_ci map->offset = xmap->offset; 488c2ecf20Sopenharmony_ci map->v4l2_type = xmap->v4l2_type; 498c2ecf20Sopenharmony_ci map->data_type = xmap->data_type; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci switch (xmap->v4l2_type) { 528c2ecf20Sopenharmony_ci case V4L2_CTRL_TYPE_INTEGER: 538c2ecf20Sopenharmony_ci case V4L2_CTRL_TYPE_BOOLEAN: 548c2ecf20Sopenharmony_ci case V4L2_CTRL_TYPE_BUTTON: 558c2ecf20Sopenharmony_ci break; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci case V4L2_CTRL_TYPE_MENU: 588c2ecf20Sopenharmony_ci /* Prevent excessive memory consumption, as well as integer 598c2ecf20Sopenharmony_ci * overflows. 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_ci if (xmap->menu_count == 0 || 628c2ecf20Sopenharmony_ci xmap->menu_count > UVC_MAX_CONTROL_MENU_ENTRIES) { 638c2ecf20Sopenharmony_ci ret = -EINVAL; 648c2ecf20Sopenharmony_ci goto free_map; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci size = xmap->menu_count * sizeof(*map->menu_info); 688c2ecf20Sopenharmony_ci map->menu_info = memdup_user(xmap->menu_info, size); 698c2ecf20Sopenharmony_ci if (IS_ERR(map->menu_info)) { 708c2ecf20Sopenharmony_ci ret = PTR_ERR(map->menu_info); 718c2ecf20Sopenharmony_ci goto free_map; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci map->menu_count = xmap->menu_count; 758c2ecf20Sopenharmony_ci break; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci default: 788c2ecf20Sopenharmony_ci uvc_trace(UVC_TRACE_CONTROL, "Unsupported V4L2 control type " 798c2ecf20Sopenharmony_ci "%u.\n", xmap->v4l2_type); 808c2ecf20Sopenharmony_ci ret = -ENOTTY; 818c2ecf20Sopenharmony_ci goto free_map; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci ret = uvc_ctrl_add_mapping(chain, map); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci kfree(map->menu_info); 878c2ecf20Sopenharmony_cifree_map: 888c2ecf20Sopenharmony_ci kfree(map); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci return ret; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ 948c2ecf20Sopenharmony_ci * V4L2 interface 958c2ecf20Sopenharmony_ci */ 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/* 988c2ecf20Sopenharmony_ci * Find the frame interval closest to the requested frame interval for the 998c2ecf20Sopenharmony_ci * given frame format and size. This should be done by the device as part of 1008c2ecf20Sopenharmony_ci * the Video Probe and Commit negotiation, but some hardware don't implement 1018c2ecf20Sopenharmony_ci * that feature. 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_cistatic u32 uvc_try_frame_interval(struct uvc_frame *frame, u32 interval) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci unsigned int i; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (frame->bFrameIntervalType) { 1088c2ecf20Sopenharmony_ci u32 best = -1, dist; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci for (i = 0; i < frame->bFrameIntervalType; ++i) { 1118c2ecf20Sopenharmony_ci dist = interval > frame->dwFrameInterval[i] 1128c2ecf20Sopenharmony_ci ? interval - frame->dwFrameInterval[i] 1138c2ecf20Sopenharmony_ci : frame->dwFrameInterval[i] - interval; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (dist > best) 1168c2ecf20Sopenharmony_ci break; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci best = dist; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci interval = frame->dwFrameInterval[i-1]; 1228c2ecf20Sopenharmony_ci } else { 1238c2ecf20Sopenharmony_ci const u32 min = frame->dwFrameInterval[0]; 1248c2ecf20Sopenharmony_ci const u32 max = frame->dwFrameInterval[1]; 1258c2ecf20Sopenharmony_ci const u32 step = frame->dwFrameInterval[2]; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci interval = min + (interval - min + step/2) / step * step; 1288c2ecf20Sopenharmony_ci if (interval > max) 1298c2ecf20Sopenharmony_ci interval = max; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci return interval; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic u32 uvc_v4l2_get_bytesperline(const struct uvc_format *format, 1368c2ecf20Sopenharmony_ci const struct uvc_frame *frame) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci switch (format->fcc) { 1398c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV12: 1408c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_YVU420: 1418c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_YUV420: 1428c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_M420: 1438c2ecf20Sopenharmony_ci return frame->wWidth; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci default: 1468c2ecf20Sopenharmony_ci return format->bpp * frame->wWidth / 8; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic int uvc_v4l2_try_format(struct uvc_streaming *stream, 1518c2ecf20Sopenharmony_ci struct v4l2_format *fmt, struct uvc_streaming_control *probe, 1528c2ecf20Sopenharmony_ci struct uvc_format **uvc_format, struct uvc_frame **uvc_frame) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct uvc_format *format = NULL; 1558c2ecf20Sopenharmony_ci struct uvc_frame *frame = NULL; 1568c2ecf20Sopenharmony_ci u16 rw, rh; 1578c2ecf20Sopenharmony_ci unsigned int d, maxd; 1588c2ecf20Sopenharmony_ci unsigned int i; 1598c2ecf20Sopenharmony_ci u32 interval; 1608c2ecf20Sopenharmony_ci int ret = 0; 1618c2ecf20Sopenharmony_ci u8 *fcc; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (fmt->type != stream->type) 1648c2ecf20Sopenharmony_ci return -EINVAL; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci fcc = (u8 *)&fmt->fmt.pix.pixelformat; 1678c2ecf20Sopenharmony_ci uvc_trace(UVC_TRACE_FORMAT, "Trying format 0x%08x (%c%c%c%c): %ux%u.\n", 1688c2ecf20Sopenharmony_ci fmt->fmt.pix.pixelformat, 1698c2ecf20Sopenharmony_ci fcc[0], fcc[1], fcc[2], fcc[3], 1708c2ecf20Sopenharmony_ci fmt->fmt.pix.width, fmt->fmt.pix.height); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* Check if the hardware supports the requested format, use the default 1738c2ecf20Sopenharmony_ci * format otherwise. 1748c2ecf20Sopenharmony_ci */ 1758c2ecf20Sopenharmony_ci for (i = 0; i < stream->nformats; ++i) { 1768c2ecf20Sopenharmony_ci format = &stream->format[i]; 1778c2ecf20Sopenharmony_ci if (format->fcc == fmt->fmt.pix.pixelformat) 1788c2ecf20Sopenharmony_ci break; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (i == stream->nformats) { 1828c2ecf20Sopenharmony_ci format = stream->def_format; 1838c2ecf20Sopenharmony_ci fmt->fmt.pix.pixelformat = format->fcc; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* Find the closest image size. The distance between image sizes is 1878c2ecf20Sopenharmony_ci * the size in pixels of the non-overlapping regions between the 1888c2ecf20Sopenharmony_ci * requested size and the frame-specified size. 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_ci rw = fmt->fmt.pix.width; 1918c2ecf20Sopenharmony_ci rh = fmt->fmt.pix.height; 1928c2ecf20Sopenharmony_ci maxd = (unsigned int)-1; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci for (i = 0; i < format->nframes; ++i) { 1958c2ecf20Sopenharmony_ci u16 w = format->frame[i].wWidth; 1968c2ecf20Sopenharmony_ci u16 h = format->frame[i].wHeight; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci d = min(w, rw) * min(h, rh); 1998c2ecf20Sopenharmony_ci d = w*h + rw*rh - 2*d; 2008c2ecf20Sopenharmony_ci if (d < maxd) { 2018c2ecf20Sopenharmony_ci maxd = d; 2028c2ecf20Sopenharmony_ci frame = &format->frame[i]; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (maxd == 0) 2068c2ecf20Sopenharmony_ci break; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (frame == NULL) { 2108c2ecf20Sopenharmony_ci uvc_trace(UVC_TRACE_FORMAT, "Unsupported size %ux%u.\n", 2118c2ecf20Sopenharmony_ci fmt->fmt.pix.width, fmt->fmt.pix.height); 2128c2ecf20Sopenharmony_ci return -EINVAL; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci /* Use the default frame interval. */ 2168c2ecf20Sopenharmony_ci interval = frame->dwDefaultFrameInterval; 2178c2ecf20Sopenharmony_ci uvc_trace(UVC_TRACE_FORMAT, "Using default frame interval %u.%u us " 2188c2ecf20Sopenharmony_ci "(%u.%u fps).\n", interval/10, interval%10, 10000000/interval, 2198c2ecf20Sopenharmony_ci (100000000/interval)%10); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* Set the format index, frame index and frame interval. */ 2228c2ecf20Sopenharmony_ci memset(probe, 0, sizeof(*probe)); 2238c2ecf20Sopenharmony_ci probe->bmHint = 1; /* dwFrameInterval */ 2248c2ecf20Sopenharmony_ci probe->bFormatIndex = format->index; 2258c2ecf20Sopenharmony_ci probe->bFrameIndex = frame->bFrameIndex; 2268c2ecf20Sopenharmony_ci probe->dwFrameInterval = uvc_try_frame_interval(frame, interval); 2278c2ecf20Sopenharmony_ci /* Some webcams stall the probe control set request when the 2288c2ecf20Sopenharmony_ci * dwMaxVideoFrameSize field is set to zero. The UVC specification 2298c2ecf20Sopenharmony_ci * clearly states that the field is read-only from the host, so this 2308c2ecf20Sopenharmony_ci * is a webcam bug. Set dwMaxVideoFrameSize to the value reported by 2318c2ecf20Sopenharmony_ci * the webcam to work around the problem. 2328c2ecf20Sopenharmony_ci * 2338c2ecf20Sopenharmony_ci * The workaround could probably be enabled for all webcams, so the 2348c2ecf20Sopenharmony_ci * quirk can be removed if needed. It's currently useful to detect 2358c2ecf20Sopenharmony_ci * webcam bugs and fix them before they hit the market (providing 2368c2ecf20Sopenharmony_ci * developers test their webcams with the Linux driver as well as with 2378c2ecf20Sopenharmony_ci * the Windows driver). 2388c2ecf20Sopenharmony_ci */ 2398c2ecf20Sopenharmony_ci mutex_lock(&stream->mutex); 2408c2ecf20Sopenharmony_ci if (stream->dev->quirks & UVC_QUIRK_PROBE_EXTRAFIELDS) 2418c2ecf20Sopenharmony_ci probe->dwMaxVideoFrameSize = 2428c2ecf20Sopenharmony_ci stream->ctrl.dwMaxVideoFrameSize; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* Probe the device. */ 2458c2ecf20Sopenharmony_ci ret = uvc_probe_video(stream, probe); 2468c2ecf20Sopenharmony_ci mutex_unlock(&stream->mutex); 2478c2ecf20Sopenharmony_ci if (ret < 0) 2488c2ecf20Sopenharmony_ci goto done; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci /* After the probe, update fmt with the values returned from 2518c2ecf20Sopenharmony_ci * negotiation with the device. Some devices return invalid bFormatIndex 2528c2ecf20Sopenharmony_ci * and bFrameIndex values, in which case we can only assume they have 2538c2ecf20Sopenharmony_ci * accepted the requested format as-is. 2548c2ecf20Sopenharmony_ci */ 2558c2ecf20Sopenharmony_ci for (i = 0; i < stream->nformats; ++i) { 2568c2ecf20Sopenharmony_ci if (probe->bFormatIndex == stream->format[i].index) { 2578c2ecf20Sopenharmony_ci format = &stream->format[i]; 2588c2ecf20Sopenharmony_ci break; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (i == stream->nformats) 2638c2ecf20Sopenharmony_ci uvc_trace(UVC_TRACE_FORMAT, 2648c2ecf20Sopenharmony_ci "Unknown bFormatIndex %u, using default\n", 2658c2ecf20Sopenharmony_ci probe->bFormatIndex); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci for (i = 0; i < format->nframes; ++i) { 2688c2ecf20Sopenharmony_ci if (probe->bFrameIndex == format->frame[i].bFrameIndex) { 2698c2ecf20Sopenharmony_ci frame = &format->frame[i]; 2708c2ecf20Sopenharmony_ci break; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if (i == format->nframes) 2758c2ecf20Sopenharmony_ci uvc_trace(UVC_TRACE_FORMAT, 2768c2ecf20Sopenharmony_ci "Unknown bFrameIndex %u, using default\n", 2778c2ecf20Sopenharmony_ci probe->bFrameIndex); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci fmt->fmt.pix.width = frame->wWidth; 2808c2ecf20Sopenharmony_ci fmt->fmt.pix.height = frame->wHeight; 2818c2ecf20Sopenharmony_ci fmt->fmt.pix.field = V4L2_FIELD_NONE; 2828c2ecf20Sopenharmony_ci fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(format, frame); 2838c2ecf20Sopenharmony_ci fmt->fmt.pix.sizeimage = probe->dwMaxVideoFrameSize; 2848c2ecf20Sopenharmony_ci fmt->fmt.pix.pixelformat = format->fcc; 2858c2ecf20Sopenharmony_ci fmt->fmt.pix.colorspace = format->colorspace; 2868c2ecf20Sopenharmony_ci fmt->fmt.pix.xfer_func = format->xfer_func; 2878c2ecf20Sopenharmony_ci fmt->fmt.pix.ycbcr_enc = format->ycbcr_enc; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (uvc_format != NULL) 2908c2ecf20Sopenharmony_ci *uvc_format = format; 2918c2ecf20Sopenharmony_ci if (uvc_frame != NULL) 2928c2ecf20Sopenharmony_ci *uvc_frame = frame; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cidone: 2958c2ecf20Sopenharmony_ci return ret; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int uvc_v4l2_get_format(struct uvc_streaming *stream, 2998c2ecf20Sopenharmony_ci struct v4l2_format *fmt) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci struct uvc_format *format; 3028c2ecf20Sopenharmony_ci struct uvc_frame *frame; 3038c2ecf20Sopenharmony_ci int ret = 0; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (fmt->type != stream->type) 3068c2ecf20Sopenharmony_ci return -EINVAL; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci mutex_lock(&stream->mutex); 3098c2ecf20Sopenharmony_ci format = stream->cur_format; 3108c2ecf20Sopenharmony_ci frame = stream->cur_frame; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (format == NULL || frame == NULL) { 3138c2ecf20Sopenharmony_ci ret = -EINVAL; 3148c2ecf20Sopenharmony_ci goto done; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci fmt->fmt.pix.pixelformat = format->fcc; 3188c2ecf20Sopenharmony_ci fmt->fmt.pix.width = frame->wWidth; 3198c2ecf20Sopenharmony_ci fmt->fmt.pix.height = frame->wHeight; 3208c2ecf20Sopenharmony_ci fmt->fmt.pix.field = V4L2_FIELD_NONE; 3218c2ecf20Sopenharmony_ci fmt->fmt.pix.bytesperline = uvc_v4l2_get_bytesperline(format, frame); 3228c2ecf20Sopenharmony_ci fmt->fmt.pix.sizeimage = stream->ctrl.dwMaxVideoFrameSize; 3238c2ecf20Sopenharmony_ci fmt->fmt.pix.colorspace = format->colorspace; 3248c2ecf20Sopenharmony_ci fmt->fmt.pix.xfer_func = format->xfer_func; 3258c2ecf20Sopenharmony_ci fmt->fmt.pix.ycbcr_enc = format->ycbcr_enc; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cidone: 3288c2ecf20Sopenharmony_ci mutex_unlock(&stream->mutex); 3298c2ecf20Sopenharmony_ci return ret; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic int uvc_v4l2_set_format(struct uvc_streaming *stream, 3338c2ecf20Sopenharmony_ci struct v4l2_format *fmt) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci struct uvc_streaming_control probe; 3368c2ecf20Sopenharmony_ci struct uvc_format *format; 3378c2ecf20Sopenharmony_ci struct uvc_frame *frame; 3388c2ecf20Sopenharmony_ci int ret; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (fmt->type != stream->type) 3418c2ecf20Sopenharmony_ci return -EINVAL; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci ret = uvc_v4l2_try_format(stream, fmt, &probe, &format, &frame); 3448c2ecf20Sopenharmony_ci if (ret < 0) 3458c2ecf20Sopenharmony_ci return ret; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci mutex_lock(&stream->mutex); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (uvc_queue_allocated(&stream->queue)) { 3508c2ecf20Sopenharmony_ci ret = -EBUSY; 3518c2ecf20Sopenharmony_ci goto done; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci stream->ctrl = probe; 3558c2ecf20Sopenharmony_ci stream->cur_format = format; 3568c2ecf20Sopenharmony_ci stream->cur_frame = frame; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cidone: 3598c2ecf20Sopenharmony_ci mutex_unlock(&stream->mutex); 3608c2ecf20Sopenharmony_ci return ret; 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic int uvc_v4l2_get_streamparm(struct uvc_streaming *stream, 3648c2ecf20Sopenharmony_ci struct v4l2_streamparm *parm) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci u32 numerator, denominator; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (parm->type != stream->type) 3698c2ecf20Sopenharmony_ci return -EINVAL; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci mutex_lock(&stream->mutex); 3728c2ecf20Sopenharmony_ci numerator = stream->ctrl.dwFrameInterval; 3738c2ecf20Sopenharmony_ci mutex_unlock(&stream->mutex); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci denominator = 10000000; 3768c2ecf20Sopenharmony_ci uvc_simplify_fraction(&numerator, &denominator, 8, 333); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci memset(parm, 0, sizeof(*parm)); 3798c2ecf20Sopenharmony_ci parm->type = stream->type; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { 3828c2ecf20Sopenharmony_ci parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; 3838c2ecf20Sopenharmony_ci parm->parm.capture.capturemode = 0; 3848c2ecf20Sopenharmony_ci parm->parm.capture.timeperframe.numerator = numerator; 3858c2ecf20Sopenharmony_ci parm->parm.capture.timeperframe.denominator = denominator; 3868c2ecf20Sopenharmony_ci parm->parm.capture.extendedmode = 0; 3878c2ecf20Sopenharmony_ci parm->parm.capture.readbuffers = 0; 3888c2ecf20Sopenharmony_ci } else { 3898c2ecf20Sopenharmony_ci parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME; 3908c2ecf20Sopenharmony_ci parm->parm.output.outputmode = 0; 3918c2ecf20Sopenharmony_ci parm->parm.output.timeperframe.numerator = numerator; 3928c2ecf20Sopenharmony_ci parm->parm.output.timeperframe.denominator = denominator; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci return 0; 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic int uvc_v4l2_set_streamparm(struct uvc_streaming *stream, 3998c2ecf20Sopenharmony_ci struct v4l2_streamparm *parm) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci struct uvc_streaming_control probe; 4028c2ecf20Sopenharmony_ci struct v4l2_fract timeperframe; 4038c2ecf20Sopenharmony_ci struct uvc_format *format; 4048c2ecf20Sopenharmony_ci struct uvc_frame *frame; 4058c2ecf20Sopenharmony_ci u32 interval, maxd; 4068c2ecf20Sopenharmony_ci unsigned int i; 4078c2ecf20Sopenharmony_ci int ret; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci if (parm->type != stream->type) 4108c2ecf20Sopenharmony_ci return -EINVAL; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) 4138c2ecf20Sopenharmony_ci timeperframe = parm->parm.capture.timeperframe; 4148c2ecf20Sopenharmony_ci else 4158c2ecf20Sopenharmony_ci timeperframe = parm->parm.output.timeperframe; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci interval = uvc_fraction_to_interval(timeperframe.numerator, 4188c2ecf20Sopenharmony_ci timeperframe.denominator); 4198c2ecf20Sopenharmony_ci uvc_trace(UVC_TRACE_FORMAT, "Setting frame interval to %u/%u (%u).\n", 4208c2ecf20Sopenharmony_ci timeperframe.numerator, timeperframe.denominator, interval); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci mutex_lock(&stream->mutex); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if (uvc_queue_streaming(&stream->queue)) { 4258c2ecf20Sopenharmony_ci mutex_unlock(&stream->mutex); 4268c2ecf20Sopenharmony_ci return -EBUSY; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci format = stream->cur_format; 4308c2ecf20Sopenharmony_ci frame = stream->cur_frame; 4318c2ecf20Sopenharmony_ci probe = stream->ctrl; 4328c2ecf20Sopenharmony_ci probe.dwFrameInterval = uvc_try_frame_interval(frame, interval); 4338c2ecf20Sopenharmony_ci maxd = abs((s32)probe.dwFrameInterval - interval); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci /* Try frames with matching size to find the best frame interval. */ 4368c2ecf20Sopenharmony_ci for (i = 0; i < format->nframes && maxd != 0; i++) { 4378c2ecf20Sopenharmony_ci u32 d, ival; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci if (&format->frame[i] == stream->cur_frame) 4408c2ecf20Sopenharmony_ci continue; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci if (format->frame[i].wWidth != stream->cur_frame->wWidth || 4438c2ecf20Sopenharmony_ci format->frame[i].wHeight != stream->cur_frame->wHeight) 4448c2ecf20Sopenharmony_ci continue; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci ival = uvc_try_frame_interval(&format->frame[i], interval); 4478c2ecf20Sopenharmony_ci d = abs((s32)ival - interval); 4488c2ecf20Sopenharmony_ci if (d >= maxd) 4498c2ecf20Sopenharmony_ci continue; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci frame = &format->frame[i]; 4528c2ecf20Sopenharmony_ci probe.bFrameIndex = frame->bFrameIndex; 4538c2ecf20Sopenharmony_ci probe.dwFrameInterval = ival; 4548c2ecf20Sopenharmony_ci maxd = d; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* Probe the device with the new settings. */ 4588c2ecf20Sopenharmony_ci ret = uvc_probe_video(stream, &probe); 4598c2ecf20Sopenharmony_ci if (ret < 0) { 4608c2ecf20Sopenharmony_ci mutex_unlock(&stream->mutex); 4618c2ecf20Sopenharmony_ci return ret; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci stream->ctrl = probe; 4658c2ecf20Sopenharmony_ci stream->cur_frame = frame; 4668c2ecf20Sopenharmony_ci mutex_unlock(&stream->mutex); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* Return the actual frame period. */ 4698c2ecf20Sopenharmony_ci timeperframe.numerator = probe.dwFrameInterval; 4708c2ecf20Sopenharmony_ci timeperframe.denominator = 10000000; 4718c2ecf20Sopenharmony_ci uvc_simplify_fraction(&timeperframe.numerator, 4728c2ecf20Sopenharmony_ci &timeperframe.denominator, 8, 333); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { 4758c2ecf20Sopenharmony_ci parm->parm.capture.timeperframe = timeperframe; 4768c2ecf20Sopenharmony_ci parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; 4778c2ecf20Sopenharmony_ci } else { 4788c2ecf20Sopenharmony_ci parm->parm.output.timeperframe = timeperframe; 4798c2ecf20Sopenharmony_ci parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci return 0; 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ 4868c2ecf20Sopenharmony_ci * Privilege management 4878c2ecf20Sopenharmony_ci */ 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci/* 4908c2ecf20Sopenharmony_ci * Privilege management is the multiple-open implementation basis. The current 4918c2ecf20Sopenharmony_ci * implementation is completely transparent for the end-user and doesn't 4928c2ecf20Sopenharmony_ci * require explicit use of the VIDIOC_G_PRIORITY and VIDIOC_S_PRIORITY ioctls. 4938c2ecf20Sopenharmony_ci * Those ioctls enable finer control on the device (by making possible for a 4948c2ecf20Sopenharmony_ci * user to request exclusive access to a device), but are not mature yet. 4958c2ecf20Sopenharmony_ci * Switching to the V4L2 priority mechanism might be considered in the future 4968c2ecf20Sopenharmony_ci * if this situation changes. 4978c2ecf20Sopenharmony_ci * 4988c2ecf20Sopenharmony_ci * Each open instance of a UVC device can either be in a privileged or 4998c2ecf20Sopenharmony_ci * unprivileged state. Only a single instance can be in a privileged state at 5008c2ecf20Sopenharmony_ci * a given time. Trying to perform an operation that requires privileges will 5018c2ecf20Sopenharmony_ci * automatically acquire the required privileges if possible, or return -EBUSY 5028c2ecf20Sopenharmony_ci * otherwise. Privileges are dismissed when closing the instance or when 5038c2ecf20Sopenharmony_ci * freeing the video buffers using VIDIOC_REQBUFS. 5048c2ecf20Sopenharmony_ci * 5058c2ecf20Sopenharmony_ci * Operations that require privileges are: 5068c2ecf20Sopenharmony_ci * 5078c2ecf20Sopenharmony_ci * - VIDIOC_S_INPUT 5088c2ecf20Sopenharmony_ci * - VIDIOC_S_PARM 5098c2ecf20Sopenharmony_ci * - VIDIOC_S_FMT 5108c2ecf20Sopenharmony_ci * - VIDIOC_REQBUFS 5118c2ecf20Sopenharmony_ci */ 5128c2ecf20Sopenharmony_cistatic int uvc_acquire_privileges(struct uvc_fh *handle) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci /* Always succeed if the handle is already privileged. */ 5158c2ecf20Sopenharmony_ci if (handle->state == UVC_HANDLE_ACTIVE) 5168c2ecf20Sopenharmony_ci return 0; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* Check if the device already has a privileged handle. */ 5198c2ecf20Sopenharmony_ci if (atomic_inc_return(&handle->stream->active) != 1) { 5208c2ecf20Sopenharmony_ci atomic_dec(&handle->stream->active); 5218c2ecf20Sopenharmony_ci return -EBUSY; 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci handle->state = UVC_HANDLE_ACTIVE; 5258c2ecf20Sopenharmony_ci return 0; 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cistatic void uvc_dismiss_privileges(struct uvc_fh *handle) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci if (handle->state == UVC_HANDLE_ACTIVE) 5318c2ecf20Sopenharmony_ci atomic_dec(&handle->stream->active); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci handle->state = UVC_HANDLE_PASSIVE; 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_cistatic int uvc_has_privileges(struct uvc_fh *handle) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci return handle->state == UVC_HANDLE_ACTIVE; 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ 5428c2ecf20Sopenharmony_ci * V4L2 file operations 5438c2ecf20Sopenharmony_ci */ 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistatic int uvc_v4l2_open(struct file *file) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci struct uvc_streaming *stream; 5488c2ecf20Sopenharmony_ci struct uvc_fh *handle; 5498c2ecf20Sopenharmony_ci int ret = 0; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n"); 5528c2ecf20Sopenharmony_ci stream = video_drvdata(file); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci ret = usb_autopm_get_interface(stream->dev->intf); 5558c2ecf20Sopenharmony_ci if (ret < 0) 5568c2ecf20Sopenharmony_ci return ret; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci /* Create the device handle. */ 5598c2ecf20Sopenharmony_ci handle = kzalloc(sizeof(*handle), GFP_KERNEL); 5608c2ecf20Sopenharmony_ci if (handle == NULL) { 5618c2ecf20Sopenharmony_ci usb_autopm_put_interface(stream->dev->intf); 5628c2ecf20Sopenharmony_ci return -ENOMEM; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci mutex_lock(&stream->dev->lock); 5668c2ecf20Sopenharmony_ci if (stream->dev->users == 0) { 5678c2ecf20Sopenharmony_ci ret = uvc_status_start(stream->dev, GFP_KERNEL); 5688c2ecf20Sopenharmony_ci if (ret < 0) { 5698c2ecf20Sopenharmony_ci mutex_unlock(&stream->dev->lock); 5708c2ecf20Sopenharmony_ci usb_autopm_put_interface(stream->dev->intf); 5718c2ecf20Sopenharmony_ci kfree(handle); 5728c2ecf20Sopenharmony_ci return ret; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci stream->dev->users++; 5778c2ecf20Sopenharmony_ci mutex_unlock(&stream->dev->lock); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci v4l2_fh_init(&handle->vfh, &stream->vdev); 5808c2ecf20Sopenharmony_ci v4l2_fh_add(&handle->vfh); 5818c2ecf20Sopenharmony_ci handle->chain = stream->chain; 5828c2ecf20Sopenharmony_ci handle->stream = stream; 5838c2ecf20Sopenharmony_ci handle->state = UVC_HANDLE_PASSIVE; 5848c2ecf20Sopenharmony_ci file->private_data = handle; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci return 0; 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cistatic int uvc_v4l2_release(struct file *file) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci struct uvc_fh *handle = file->private_data; 5928c2ecf20Sopenharmony_ci struct uvc_streaming *stream = handle->stream; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n"); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci /* Only free resources if this is a privileged handle. */ 5978c2ecf20Sopenharmony_ci if (uvc_has_privileges(handle)) 5988c2ecf20Sopenharmony_ci uvc_queue_release(&stream->queue); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci /* Release the file handle. */ 6018c2ecf20Sopenharmony_ci uvc_dismiss_privileges(handle); 6028c2ecf20Sopenharmony_ci v4l2_fh_del(&handle->vfh); 6038c2ecf20Sopenharmony_ci v4l2_fh_exit(&handle->vfh); 6048c2ecf20Sopenharmony_ci kfree(handle); 6058c2ecf20Sopenharmony_ci file->private_data = NULL; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci mutex_lock(&stream->dev->lock); 6088c2ecf20Sopenharmony_ci if (--stream->dev->users == 0) 6098c2ecf20Sopenharmony_ci uvc_status_stop(stream->dev); 6108c2ecf20Sopenharmony_ci mutex_unlock(&stream->dev->lock); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci usb_autopm_put_interface(stream->dev->intf); 6138c2ecf20Sopenharmony_ci return 0; 6148c2ecf20Sopenharmony_ci} 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_cistatic int uvc_ioctl_querycap(struct file *file, void *fh, 6178c2ecf20Sopenharmony_ci struct v4l2_capability *cap) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci struct video_device *vdev = video_devdata(file); 6208c2ecf20Sopenharmony_ci struct uvc_fh *handle = file->private_data; 6218c2ecf20Sopenharmony_ci struct uvc_video_chain *chain = handle->chain; 6228c2ecf20Sopenharmony_ci struct uvc_streaming *stream = handle->stream; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci strscpy(cap->driver, "uvcvideo", sizeof(cap->driver)); 6258c2ecf20Sopenharmony_ci strscpy(cap->card, vdev->name, sizeof(cap->card)); 6268c2ecf20Sopenharmony_ci usb_make_path(stream->dev->udev, cap->bus_info, sizeof(cap->bus_info)); 6278c2ecf20Sopenharmony_ci cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING 6288c2ecf20Sopenharmony_ci | chain->caps; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci return 0; 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cistatic int uvc_ioctl_enum_fmt(struct uvc_streaming *stream, 6348c2ecf20Sopenharmony_ci struct v4l2_fmtdesc *fmt) 6358c2ecf20Sopenharmony_ci{ 6368c2ecf20Sopenharmony_ci struct uvc_format *format; 6378c2ecf20Sopenharmony_ci enum v4l2_buf_type type = fmt->type; 6388c2ecf20Sopenharmony_ci u32 index = fmt->index; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci if (fmt->type != stream->type || fmt->index >= stream->nformats) 6418c2ecf20Sopenharmony_ci return -EINVAL; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci memset(fmt, 0, sizeof(*fmt)); 6448c2ecf20Sopenharmony_ci fmt->index = index; 6458c2ecf20Sopenharmony_ci fmt->type = type; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci format = &stream->format[fmt->index]; 6488c2ecf20Sopenharmony_ci fmt->flags = 0; 6498c2ecf20Sopenharmony_ci if (format->flags & UVC_FMT_FLAG_COMPRESSED) 6508c2ecf20Sopenharmony_ci fmt->flags |= V4L2_FMT_FLAG_COMPRESSED; 6518c2ecf20Sopenharmony_ci strscpy(fmt->description, format->name, sizeof(fmt->description)); 6528c2ecf20Sopenharmony_ci fmt->description[sizeof(fmt->description) - 1] = 0; 6538c2ecf20Sopenharmony_ci fmt->pixelformat = format->fcc; 6548c2ecf20Sopenharmony_ci return 0; 6558c2ecf20Sopenharmony_ci} 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_cistatic int uvc_ioctl_enum_fmt_vid_cap(struct file *file, void *fh, 6588c2ecf20Sopenharmony_ci struct v4l2_fmtdesc *fmt) 6598c2ecf20Sopenharmony_ci{ 6608c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 6618c2ecf20Sopenharmony_ci struct uvc_streaming *stream = handle->stream; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci return uvc_ioctl_enum_fmt(stream, fmt); 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_cistatic int uvc_ioctl_enum_fmt_vid_out(struct file *file, void *fh, 6678c2ecf20Sopenharmony_ci struct v4l2_fmtdesc *fmt) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 6708c2ecf20Sopenharmony_ci struct uvc_streaming *stream = handle->stream; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci return uvc_ioctl_enum_fmt(stream, fmt); 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_cistatic int uvc_ioctl_g_fmt_vid_cap(struct file *file, void *fh, 6768c2ecf20Sopenharmony_ci struct v4l2_format *fmt) 6778c2ecf20Sopenharmony_ci{ 6788c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 6798c2ecf20Sopenharmony_ci struct uvc_streaming *stream = handle->stream; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci return uvc_v4l2_get_format(stream, fmt); 6828c2ecf20Sopenharmony_ci} 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_cistatic int uvc_ioctl_g_fmt_vid_out(struct file *file, void *fh, 6858c2ecf20Sopenharmony_ci struct v4l2_format *fmt) 6868c2ecf20Sopenharmony_ci{ 6878c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 6888c2ecf20Sopenharmony_ci struct uvc_streaming *stream = handle->stream; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci return uvc_v4l2_get_format(stream, fmt); 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cistatic int uvc_ioctl_s_fmt_vid_cap(struct file *file, void *fh, 6948c2ecf20Sopenharmony_ci struct v4l2_format *fmt) 6958c2ecf20Sopenharmony_ci{ 6968c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 6978c2ecf20Sopenharmony_ci struct uvc_streaming *stream = handle->stream; 6988c2ecf20Sopenharmony_ci int ret; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci ret = uvc_acquire_privileges(handle); 7018c2ecf20Sopenharmony_ci if (ret < 0) 7028c2ecf20Sopenharmony_ci return ret; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci return uvc_v4l2_set_format(stream, fmt); 7058c2ecf20Sopenharmony_ci} 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cistatic int uvc_ioctl_s_fmt_vid_out(struct file *file, void *fh, 7088c2ecf20Sopenharmony_ci struct v4l2_format *fmt) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 7118c2ecf20Sopenharmony_ci struct uvc_streaming *stream = handle->stream; 7128c2ecf20Sopenharmony_ci int ret; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci ret = uvc_acquire_privileges(handle); 7158c2ecf20Sopenharmony_ci if (ret < 0) 7168c2ecf20Sopenharmony_ci return ret; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci return uvc_v4l2_set_format(stream, fmt); 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_cistatic int uvc_ioctl_try_fmt_vid_cap(struct file *file, void *fh, 7228c2ecf20Sopenharmony_ci struct v4l2_format *fmt) 7238c2ecf20Sopenharmony_ci{ 7248c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 7258c2ecf20Sopenharmony_ci struct uvc_streaming *stream = handle->stream; 7268c2ecf20Sopenharmony_ci struct uvc_streaming_control probe; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci return uvc_v4l2_try_format(stream, fmt, &probe, NULL, NULL); 7298c2ecf20Sopenharmony_ci} 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_cistatic int uvc_ioctl_try_fmt_vid_out(struct file *file, void *fh, 7328c2ecf20Sopenharmony_ci struct v4l2_format *fmt) 7338c2ecf20Sopenharmony_ci{ 7348c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 7358c2ecf20Sopenharmony_ci struct uvc_streaming *stream = handle->stream; 7368c2ecf20Sopenharmony_ci struct uvc_streaming_control probe; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci return uvc_v4l2_try_format(stream, fmt, &probe, NULL, NULL); 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_cistatic int uvc_ioctl_reqbufs(struct file *file, void *fh, 7428c2ecf20Sopenharmony_ci struct v4l2_requestbuffers *rb) 7438c2ecf20Sopenharmony_ci{ 7448c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 7458c2ecf20Sopenharmony_ci struct uvc_streaming *stream = handle->stream; 7468c2ecf20Sopenharmony_ci int ret; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci ret = uvc_acquire_privileges(handle); 7498c2ecf20Sopenharmony_ci if (ret < 0) 7508c2ecf20Sopenharmony_ci return ret; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci mutex_lock(&stream->mutex); 7538c2ecf20Sopenharmony_ci ret = uvc_request_buffers(&stream->queue, rb); 7548c2ecf20Sopenharmony_ci mutex_unlock(&stream->mutex); 7558c2ecf20Sopenharmony_ci if (ret < 0) 7568c2ecf20Sopenharmony_ci return ret; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci if (ret == 0) 7598c2ecf20Sopenharmony_ci uvc_dismiss_privileges(handle); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci return 0; 7628c2ecf20Sopenharmony_ci} 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_cistatic int uvc_ioctl_querybuf(struct file *file, void *fh, 7658c2ecf20Sopenharmony_ci struct v4l2_buffer *buf) 7668c2ecf20Sopenharmony_ci{ 7678c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 7688c2ecf20Sopenharmony_ci struct uvc_streaming *stream = handle->stream; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci if (!uvc_has_privileges(handle)) 7718c2ecf20Sopenharmony_ci return -EBUSY; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci return uvc_query_buffer(&stream->queue, buf); 7748c2ecf20Sopenharmony_ci} 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_cistatic int uvc_ioctl_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) 7778c2ecf20Sopenharmony_ci{ 7788c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 7798c2ecf20Sopenharmony_ci struct uvc_streaming *stream = handle->stream; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci if (!uvc_has_privileges(handle)) 7828c2ecf20Sopenharmony_ci return -EBUSY; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci return uvc_queue_buffer(&stream->queue, 7858c2ecf20Sopenharmony_ci stream->vdev.v4l2_dev->mdev, buf); 7868c2ecf20Sopenharmony_ci} 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_cistatic int uvc_ioctl_expbuf(struct file *file, void *fh, 7898c2ecf20Sopenharmony_ci struct v4l2_exportbuffer *exp) 7908c2ecf20Sopenharmony_ci{ 7918c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 7928c2ecf20Sopenharmony_ci struct uvc_streaming *stream = handle->stream; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci if (!uvc_has_privileges(handle)) 7958c2ecf20Sopenharmony_ci return -EBUSY; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci return uvc_export_buffer(&stream->queue, exp); 7988c2ecf20Sopenharmony_ci} 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_cistatic int uvc_ioctl_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) 8018c2ecf20Sopenharmony_ci{ 8028c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 8038c2ecf20Sopenharmony_ci struct uvc_streaming *stream = handle->stream; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci if (!uvc_has_privileges(handle)) 8068c2ecf20Sopenharmony_ci return -EBUSY; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci return uvc_dequeue_buffer(&stream->queue, buf, 8098c2ecf20Sopenharmony_ci file->f_flags & O_NONBLOCK); 8108c2ecf20Sopenharmony_ci} 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_cistatic int uvc_ioctl_create_bufs(struct file *file, void *fh, 8138c2ecf20Sopenharmony_ci struct v4l2_create_buffers *cb) 8148c2ecf20Sopenharmony_ci{ 8158c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 8168c2ecf20Sopenharmony_ci struct uvc_streaming *stream = handle->stream; 8178c2ecf20Sopenharmony_ci int ret; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci ret = uvc_acquire_privileges(handle); 8208c2ecf20Sopenharmony_ci if (ret < 0) 8218c2ecf20Sopenharmony_ci return ret; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci return uvc_create_buffers(&stream->queue, cb); 8248c2ecf20Sopenharmony_ci} 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_cistatic int uvc_ioctl_streamon(struct file *file, void *fh, 8278c2ecf20Sopenharmony_ci enum v4l2_buf_type type) 8288c2ecf20Sopenharmony_ci{ 8298c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 8308c2ecf20Sopenharmony_ci struct uvc_streaming *stream = handle->stream; 8318c2ecf20Sopenharmony_ci int ret; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci if (!uvc_has_privileges(handle)) 8348c2ecf20Sopenharmony_ci return -EBUSY; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci mutex_lock(&stream->mutex); 8378c2ecf20Sopenharmony_ci ret = uvc_queue_streamon(&stream->queue, type); 8388c2ecf20Sopenharmony_ci mutex_unlock(&stream->mutex); 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci return ret; 8418c2ecf20Sopenharmony_ci} 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_cistatic int uvc_ioctl_streamoff(struct file *file, void *fh, 8448c2ecf20Sopenharmony_ci enum v4l2_buf_type type) 8458c2ecf20Sopenharmony_ci{ 8468c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 8478c2ecf20Sopenharmony_ci struct uvc_streaming *stream = handle->stream; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci if (!uvc_has_privileges(handle)) 8508c2ecf20Sopenharmony_ci return -EBUSY; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci mutex_lock(&stream->mutex); 8538c2ecf20Sopenharmony_ci uvc_queue_streamoff(&stream->queue, type); 8548c2ecf20Sopenharmony_ci mutex_unlock(&stream->mutex); 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci return 0; 8578c2ecf20Sopenharmony_ci} 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_cistatic int uvc_ioctl_enum_input(struct file *file, void *fh, 8608c2ecf20Sopenharmony_ci struct v4l2_input *input) 8618c2ecf20Sopenharmony_ci{ 8628c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 8638c2ecf20Sopenharmony_ci struct uvc_video_chain *chain = handle->chain; 8648c2ecf20Sopenharmony_ci const struct uvc_entity *selector = chain->selector; 8658c2ecf20Sopenharmony_ci struct uvc_entity *iterm = NULL; 8668c2ecf20Sopenharmony_ci struct uvc_entity *it; 8678c2ecf20Sopenharmony_ci u32 index = input->index; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if (selector == NULL || 8708c2ecf20Sopenharmony_ci (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { 8718c2ecf20Sopenharmony_ci if (index != 0) 8728c2ecf20Sopenharmony_ci return -EINVAL; 8738c2ecf20Sopenharmony_ci list_for_each_entry(it, &chain->entities, chain) { 8748c2ecf20Sopenharmony_ci if (UVC_ENTITY_IS_ITERM(it)) { 8758c2ecf20Sopenharmony_ci iterm = it; 8768c2ecf20Sopenharmony_ci break; 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci } 8798c2ecf20Sopenharmony_ci } else if (index < selector->bNrInPins) { 8808c2ecf20Sopenharmony_ci list_for_each_entry(it, &chain->entities, chain) { 8818c2ecf20Sopenharmony_ci if (!UVC_ENTITY_IS_ITERM(it)) 8828c2ecf20Sopenharmony_ci continue; 8838c2ecf20Sopenharmony_ci if (it->id == selector->baSourceID[index]) { 8848c2ecf20Sopenharmony_ci iterm = it; 8858c2ecf20Sopenharmony_ci break; 8868c2ecf20Sopenharmony_ci } 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci } 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci if (iterm == NULL) 8918c2ecf20Sopenharmony_ci return -EINVAL; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci memset(input, 0, sizeof(*input)); 8948c2ecf20Sopenharmony_ci input->index = index; 8958c2ecf20Sopenharmony_ci strscpy(input->name, iterm->name, sizeof(input->name)); 8968c2ecf20Sopenharmony_ci if (UVC_ENTITY_TYPE(iterm) == UVC_ITT_CAMERA) 8978c2ecf20Sopenharmony_ci input->type = V4L2_INPUT_TYPE_CAMERA; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci return 0; 9008c2ecf20Sopenharmony_ci} 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_cistatic int uvc_ioctl_g_input(struct file *file, void *fh, unsigned int *input) 9038c2ecf20Sopenharmony_ci{ 9048c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 9058c2ecf20Sopenharmony_ci struct uvc_video_chain *chain = handle->chain; 9068c2ecf20Sopenharmony_ci u8 *buf; 9078c2ecf20Sopenharmony_ci int ret; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci if (chain->selector == NULL || 9108c2ecf20Sopenharmony_ci (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { 9118c2ecf20Sopenharmony_ci *input = 0; 9128c2ecf20Sopenharmony_ci return 0; 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci buf = kmalloc(1, GFP_KERNEL); 9168c2ecf20Sopenharmony_ci if (!buf) 9178c2ecf20Sopenharmony_ci return -ENOMEM; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, chain->selector->id, 9208c2ecf20Sopenharmony_ci chain->dev->intfnum, UVC_SU_INPUT_SELECT_CONTROL, 9218c2ecf20Sopenharmony_ci buf, 1); 9228c2ecf20Sopenharmony_ci if (!ret) 9238c2ecf20Sopenharmony_ci *input = *buf - 1; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci kfree(buf); 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci return ret; 9288c2ecf20Sopenharmony_ci} 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_cistatic int uvc_ioctl_s_input(struct file *file, void *fh, unsigned int input) 9318c2ecf20Sopenharmony_ci{ 9328c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 9338c2ecf20Sopenharmony_ci struct uvc_video_chain *chain = handle->chain; 9348c2ecf20Sopenharmony_ci u8 *buf; 9358c2ecf20Sopenharmony_ci int ret; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci ret = uvc_acquire_privileges(handle); 9388c2ecf20Sopenharmony_ci if (ret < 0) 9398c2ecf20Sopenharmony_ci return ret; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci if (chain->selector == NULL || 9428c2ecf20Sopenharmony_ci (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { 9438c2ecf20Sopenharmony_ci if (input) 9448c2ecf20Sopenharmony_ci return -EINVAL; 9458c2ecf20Sopenharmony_ci return 0; 9468c2ecf20Sopenharmony_ci } 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci if (input >= chain->selector->bNrInPins) 9498c2ecf20Sopenharmony_ci return -EINVAL; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci buf = kmalloc(1, GFP_KERNEL); 9528c2ecf20Sopenharmony_ci if (!buf) 9538c2ecf20Sopenharmony_ci return -ENOMEM; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci *buf = input + 1; 9568c2ecf20Sopenharmony_ci ret = uvc_query_ctrl(chain->dev, UVC_SET_CUR, chain->selector->id, 9578c2ecf20Sopenharmony_ci chain->dev->intfnum, UVC_SU_INPUT_SELECT_CONTROL, 9588c2ecf20Sopenharmony_ci buf, 1); 9598c2ecf20Sopenharmony_ci kfree(buf); 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci return ret; 9628c2ecf20Sopenharmony_ci} 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_cistatic int uvc_ioctl_queryctrl(struct file *file, void *fh, 9658c2ecf20Sopenharmony_ci struct v4l2_queryctrl *qc) 9668c2ecf20Sopenharmony_ci{ 9678c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 9688c2ecf20Sopenharmony_ci struct uvc_video_chain *chain = handle->chain; 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci return uvc_query_v4l2_ctrl(chain, qc); 9718c2ecf20Sopenharmony_ci} 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_cistatic int uvc_ioctl_query_ext_ctrl(struct file *file, void *fh, 9748c2ecf20Sopenharmony_ci struct v4l2_query_ext_ctrl *qec) 9758c2ecf20Sopenharmony_ci{ 9768c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 9778c2ecf20Sopenharmony_ci struct uvc_video_chain *chain = handle->chain; 9788c2ecf20Sopenharmony_ci struct v4l2_queryctrl qc = { qec->id }; 9798c2ecf20Sopenharmony_ci int ret; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci ret = uvc_query_v4l2_ctrl(chain, &qc); 9828c2ecf20Sopenharmony_ci if (ret) 9838c2ecf20Sopenharmony_ci return ret; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci qec->id = qc.id; 9868c2ecf20Sopenharmony_ci qec->type = qc.type; 9878c2ecf20Sopenharmony_ci strscpy(qec->name, qc.name, sizeof(qec->name)); 9888c2ecf20Sopenharmony_ci qec->minimum = qc.minimum; 9898c2ecf20Sopenharmony_ci qec->maximum = qc.maximum; 9908c2ecf20Sopenharmony_ci qec->step = qc.step; 9918c2ecf20Sopenharmony_ci qec->default_value = qc.default_value; 9928c2ecf20Sopenharmony_ci qec->flags = qc.flags; 9938c2ecf20Sopenharmony_ci qec->elem_size = 4; 9948c2ecf20Sopenharmony_ci qec->elems = 1; 9958c2ecf20Sopenharmony_ci qec->nr_of_dims = 0; 9968c2ecf20Sopenharmony_ci memset(qec->dims, 0, sizeof(qec->dims)); 9978c2ecf20Sopenharmony_ci memset(qec->reserved, 0, sizeof(qec->reserved)); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci return 0; 10008c2ecf20Sopenharmony_ci} 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_cistatic int uvc_ioctl_g_ctrl(struct file *file, void *fh, 10038c2ecf20Sopenharmony_ci struct v4l2_control *ctrl) 10048c2ecf20Sopenharmony_ci{ 10058c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 10068c2ecf20Sopenharmony_ci struct uvc_video_chain *chain = handle->chain; 10078c2ecf20Sopenharmony_ci struct v4l2_ext_control xctrl; 10088c2ecf20Sopenharmony_ci int ret; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci memset(&xctrl, 0, sizeof(xctrl)); 10118c2ecf20Sopenharmony_ci xctrl.id = ctrl->id; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci ret = uvc_ctrl_begin(chain); 10148c2ecf20Sopenharmony_ci if (ret < 0) 10158c2ecf20Sopenharmony_ci return ret; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci ret = uvc_ctrl_get(chain, &xctrl); 10188c2ecf20Sopenharmony_ci uvc_ctrl_rollback(handle); 10198c2ecf20Sopenharmony_ci if (ret < 0) 10208c2ecf20Sopenharmony_ci return ret; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci ctrl->value = xctrl.value; 10238c2ecf20Sopenharmony_ci return 0; 10248c2ecf20Sopenharmony_ci} 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_cistatic int uvc_ioctl_s_ctrl(struct file *file, void *fh, 10278c2ecf20Sopenharmony_ci struct v4l2_control *ctrl) 10288c2ecf20Sopenharmony_ci{ 10298c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 10308c2ecf20Sopenharmony_ci struct uvc_video_chain *chain = handle->chain; 10318c2ecf20Sopenharmony_ci struct v4l2_ext_control xctrl; 10328c2ecf20Sopenharmony_ci int ret; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci memset(&xctrl, 0, sizeof(xctrl)); 10358c2ecf20Sopenharmony_ci xctrl.id = ctrl->id; 10368c2ecf20Sopenharmony_ci xctrl.value = ctrl->value; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci ret = uvc_ctrl_begin(chain); 10398c2ecf20Sopenharmony_ci if (ret < 0) 10408c2ecf20Sopenharmony_ci return ret; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci ret = uvc_ctrl_set(handle, &xctrl); 10438c2ecf20Sopenharmony_ci if (ret < 0) { 10448c2ecf20Sopenharmony_ci uvc_ctrl_rollback(handle); 10458c2ecf20Sopenharmony_ci return ret; 10468c2ecf20Sopenharmony_ci } 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci ret = uvc_ctrl_commit(handle, &xctrl, 1); 10498c2ecf20Sopenharmony_ci if (ret < 0) 10508c2ecf20Sopenharmony_ci return ret; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci ctrl->value = xctrl.value; 10538c2ecf20Sopenharmony_ci return 0; 10548c2ecf20Sopenharmony_ci} 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_cistatic int uvc_ioctl_g_ext_ctrls(struct file *file, void *fh, 10578c2ecf20Sopenharmony_ci struct v4l2_ext_controls *ctrls) 10588c2ecf20Sopenharmony_ci{ 10598c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 10608c2ecf20Sopenharmony_ci struct uvc_video_chain *chain = handle->chain; 10618c2ecf20Sopenharmony_ci struct v4l2_ext_control *ctrl = ctrls->controls; 10628c2ecf20Sopenharmony_ci unsigned int i; 10638c2ecf20Sopenharmony_ci int ret; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL) { 10668c2ecf20Sopenharmony_ci for (i = 0; i < ctrls->count; ++ctrl, ++i) { 10678c2ecf20Sopenharmony_ci struct v4l2_queryctrl qc = { .id = ctrl->id }; 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci ret = uvc_query_v4l2_ctrl(chain, &qc); 10708c2ecf20Sopenharmony_ci if (ret < 0) { 10718c2ecf20Sopenharmony_ci ctrls->error_idx = i; 10728c2ecf20Sopenharmony_ci return ret; 10738c2ecf20Sopenharmony_ci } 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci ctrl->value = qc.default_value; 10768c2ecf20Sopenharmony_ci } 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci return 0; 10798c2ecf20Sopenharmony_ci } 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci ret = uvc_ctrl_begin(chain); 10828c2ecf20Sopenharmony_ci if (ret < 0) 10838c2ecf20Sopenharmony_ci return ret; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci for (i = 0; i < ctrls->count; ++ctrl, ++i) { 10868c2ecf20Sopenharmony_ci ret = uvc_ctrl_get(chain, ctrl); 10878c2ecf20Sopenharmony_ci if (ret < 0) { 10888c2ecf20Sopenharmony_ci uvc_ctrl_rollback(handle); 10898c2ecf20Sopenharmony_ci ctrls->error_idx = i; 10908c2ecf20Sopenharmony_ci return ret; 10918c2ecf20Sopenharmony_ci } 10928c2ecf20Sopenharmony_ci } 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci ctrls->error_idx = 0; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci return uvc_ctrl_rollback(handle); 10978c2ecf20Sopenharmony_ci} 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_cistatic int uvc_ioctl_s_try_ext_ctrls(struct uvc_fh *handle, 11008c2ecf20Sopenharmony_ci struct v4l2_ext_controls *ctrls, 11018c2ecf20Sopenharmony_ci bool commit) 11028c2ecf20Sopenharmony_ci{ 11038c2ecf20Sopenharmony_ci struct v4l2_ext_control *ctrl = ctrls->controls; 11048c2ecf20Sopenharmony_ci struct uvc_video_chain *chain = handle->chain; 11058c2ecf20Sopenharmony_ci unsigned int i; 11068c2ecf20Sopenharmony_ci int ret; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci /* Default value cannot be changed */ 11098c2ecf20Sopenharmony_ci if (ctrls->which == V4L2_CTRL_WHICH_DEF_VAL) 11108c2ecf20Sopenharmony_ci return -EINVAL; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci ret = uvc_ctrl_begin(chain); 11138c2ecf20Sopenharmony_ci if (ret < 0) 11148c2ecf20Sopenharmony_ci return ret; 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci for (i = 0; i < ctrls->count; ++ctrl, ++i) { 11178c2ecf20Sopenharmony_ci ret = uvc_ctrl_set(handle, ctrl); 11188c2ecf20Sopenharmony_ci if (ret < 0) { 11198c2ecf20Sopenharmony_ci uvc_ctrl_rollback(handle); 11208c2ecf20Sopenharmony_ci ctrls->error_idx = commit ? ctrls->count : i; 11218c2ecf20Sopenharmony_ci return ret; 11228c2ecf20Sopenharmony_ci } 11238c2ecf20Sopenharmony_ci } 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci ctrls->error_idx = 0; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci if (commit) 11288c2ecf20Sopenharmony_ci return uvc_ctrl_commit(handle, ctrls->controls, ctrls->count); 11298c2ecf20Sopenharmony_ci else 11308c2ecf20Sopenharmony_ci return uvc_ctrl_rollback(handle); 11318c2ecf20Sopenharmony_ci} 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_cistatic int uvc_ioctl_s_ext_ctrls(struct file *file, void *fh, 11348c2ecf20Sopenharmony_ci struct v4l2_ext_controls *ctrls) 11358c2ecf20Sopenharmony_ci{ 11368c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci return uvc_ioctl_s_try_ext_ctrls(handle, ctrls, true); 11398c2ecf20Sopenharmony_ci} 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_cistatic int uvc_ioctl_try_ext_ctrls(struct file *file, void *fh, 11428c2ecf20Sopenharmony_ci struct v4l2_ext_controls *ctrls) 11438c2ecf20Sopenharmony_ci{ 11448c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci return uvc_ioctl_s_try_ext_ctrls(handle, ctrls, false); 11478c2ecf20Sopenharmony_ci} 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_cistatic int uvc_ioctl_querymenu(struct file *file, void *fh, 11508c2ecf20Sopenharmony_ci struct v4l2_querymenu *qm) 11518c2ecf20Sopenharmony_ci{ 11528c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 11538c2ecf20Sopenharmony_ci struct uvc_video_chain *chain = handle->chain; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci return uvc_query_v4l2_menu(chain, qm); 11568c2ecf20Sopenharmony_ci} 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_cistatic int uvc_ioctl_g_selection(struct file *file, void *fh, 11598c2ecf20Sopenharmony_ci struct v4l2_selection *sel) 11608c2ecf20Sopenharmony_ci{ 11618c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 11628c2ecf20Sopenharmony_ci struct uvc_streaming *stream = handle->stream; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci if (sel->type != stream->type) 11658c2ecf20Sopenharmony_ci return -EINVAL; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci switch (sel->target) { 11688c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_DEFAULT: 11698c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 11708c2ecf20Sopenharmony_ci if (stream->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 11718c2ecf20Sopenharmony_ci return -EINVAL; 11728c2ecf20Sopenharmony_ci break; 11738c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_DEFAULT: 11748c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 11758c2ecf20Sopenharmony_ci if (stream->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) 11768c2ecf20Sopenharmony_ci return -EINVAL; 11778c2ecf20Sopenharmony_ci break; 11788c2ecf20Sopenharmony_ci default: 11798c2ecf20Sopenharmony_ci return -EINVAL; 11808c2ecf20Sopenharmony_ci } 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci sel->r.left = 0; 11838c2ecf20Sopenharmony_ci sel->r.top = 0; 11848c2ecf20Sopenharmony_ci mutex_lock(&stream->mutex); 11858c2ecf20Sopenharmony_ci sel->r.width = stream->cur_frame->wWidth; 11868c2ecf20Sopenharmony_ci sel->r.height = stream->cur_frame->wHeight; 11878c2ecf20Sopenharmony_ci mutex_unlock(&stream->mutex); 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci return 0; 11908c2ecf20Sopenharmony_ci} 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_cistatic int uvc_ioctl_g_parm(struct file *file, void *fh, 11938c2ecf20Sopenharmony_ci struct v4l2_streamparm *parm) 11948c2ecf20Sopenharmony_ci{ 11958c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 11968c2ecf20Sopenharmony_ci struct uvc_streaming *stream = handle->stream; 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci return uvc_v4l2_get_streamparm(stream, parm); 11998c2ecf20Sopenharmony_ci} 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_cistatic int uvc_ioctl_s_parm(struct file *file, void *fh, 12028c2ecf20Sopenharmony_ci struct v4l2_streamparm *parm) 12038c2ecf20Sopenharmony_ci{ 12048c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 12058c2ecf20Sopenharmony_ci struct uvc_streaming *stream = handle->stream; 12068c2ecf20Sopenharmony_ci int ret; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci ret = uvc_acquire_privileges(handle); 12098c2ecf20Sopenharmony_ci if (ret < 0) 12108c2ecf20Sopenharmony_ci return ret; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci return uvc_v4l2_set_streamparm(stream, parm); 12138c2ecf20Sopenharmony_ci} 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_cistatic int uvc_ioctl_enum_framesizes(struct file *file, void *fh, 12168c2ecf20Sopenharmony_ci struct v4l2_frmsizeenum *fsize) 12178c2ecf20Sopenharmony_ci{ 12188c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 12198c2ecf20Sopenharmony_ci struct uvc_streaming *stream = handle->stream; 12208c2ecf20Sopenharmony_ci struct uvc_format *format = NULL; 12218c2ecf20Sopenharmony_ci struct uvc_frame *frame = NULL; 12228c2ecf20Sopenharmony_ci unsigned int index; 12238c2ecf20Sopenharmony_ci unsigned int i; 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci /* Look for the given pixel format */ 12268c2ecf20Sopenharmony_ci for (i = 0; i < stream->nformats; i++) { 12278c2ecf20Sopenharmony_ci if (stream->format[i].fcc == fsize->pixel_format) { 12288c2ecf20Sopenharmony_ci format = &stream->format[i]; 12298c2ecf20Sopenharmony_ci break; 12308c2ecf20Sopenharmony_ci } 12318c2ecf20Sopenharmony_ci } 12328c2ecf20Sopenharmony_ci if (format == NULL) 12338c2ecf20Sopenharmony_ci return -EINVAL; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci /* Skip duplicate frame sizes */ 12368c2ecf20Sopenharmony_ci for (i = 0, index = 0; i < format->nframes; i++) { 12378c2ecf20Sopenharmony_ci if (frame && frame->wWidth == format->frame[i].wWidth && 12388c2ecf20Sopenharmony_ci frame->wHeight == format->frame[i].wHeight) 12398c2ecf20Sopenharmony_ci continue; 12408c2ecf20Sopenharmony_ci frame = &format->frame[i]; 12418c2ecf20Sopenharmony_ci if (index == fsize->index) 12428c2ecf20Sopenharmony_ci break; 12438c2ecf20Sopenharmony_ci index++; 12448c2ecf20Sopenharmony_ci } 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci if (i == format->nframes) 12478c2ecf20Sopenharmony_ci return -EINVAL; 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; 12508c2ecf20Sopenharmony_ci fsize->discrete.width = frame->wWidth; 12518c2ecf20Sopenharmony_ci fsize->discrete.height = frame->wHeight; 12528c2ecf20Sopenharmony_ci return 0; 12538c2ecf20Sopenharmony_ci} 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_cistatic int uvc_ioctl_enum_frameintervals(struct file *file, void *fh, 12568c2ecf20Sopenharmony_ci struct v4l2_frmivalenum *fival) 12578c2ecf20Sopenharmony_ci{ 12588c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 12598c2ecf20Sopenharmony_ci struct uvc_streaming *stream = handle->stream; 12608c2ecf20Sopenharmony_ci struct uvc_format *format = NULL; 12618c2ecf20Sopenharmony_ci struct uvc_frame *frame = NULL; 12628c2ecf20Sopenharmony_ci unsigned int nintervals; 12638c2ecf20Sopenharmony_ci unsigned int index; 12648c2ecf20Sopenharmony_ci unsigned int i; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci /* Look for the given pixel format and frame size */ 12678c2ecf20Sopenharmony_ci for (i = 0; i < stream->nformats; i++) { 12688c2ecf20Sopenharmony_ci if (stream->format[i].fcc == fival->pixel_format) { 12698c2ecf20Sopenharmony_ci format = &stream->format[i]; 12708c2ecf20Sopenharmony_ci break; 12718c2ecf20Sopenharmony_ci } 12728c2ecf20Sopenharmony_ci } 12738c2ecf20Sopenharmony_ci if (format == NULL) 12748c2ecf20Sopenharmony_ci return -EINVAL; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci index = fival->index; 12778c2ecf20Sopenharmony_ci for (i = 0; i < format->nframes; i++) { 12788c2ecf20Sopenharmony_ci if (format->frame[i].wWidth == fival->width && 12798c2ecf20Sopenharmony_ci format->frame[i].wHeight == fival->height) { 12808c2ecf20Sopenharmony_ci frame = &format->frame[i]; 12818c2ecf20Sopenharmony_ci nintervals = frame->bFrameIntervalType ?: 1; 12828c2ecf20Sopenharmony_ci if (index < nintervals) 12838c2ecf20Sopenharmony_ci break; 12848c2ecf20Sopenharmony_ci index -= nintervals; 12858c2ecf20Sopenharmony_ci } 12868c2ecf20Sopenharmony_ci } 12878c2ecf20Sopenharmony_ci if (i == format->nframes) 12888c2ecf20Sopenharmony_ci return -EINVAL; 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci if (frame->bFrameIntervalType) { 12918c2ecf20Sopenharmony_ci fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; 12928c2ecf20Sopenharmony_ci fival->discrete.numerator = 12938c2ecf20Sopenharmony_ci frame->dwFrameInterval[index]; 12948c2ecf20Sopenharmony_ci fival->discrete.denominator = 10000000; 12958c2ecf20Sopenharmony_ci uvc_simplify_fraction(&fival->discrete.numerator, 12968c2ecf20Sopenharmony_ci &fival->discrete.denominator, 8, 333); 12978c2ecf20Sopenharmony_ci } else { 12988c2ecf20Sopenharmony_ci fival->type = V4L2_FRMIVAL_TYPE_STEPWISE; 12998c2ecf20Sopenharmony_ci fival->stepwise.min.numerator = frame->dwFrameInterval[0]; 13008c2ecf20Sopenharmony_ci fival->stepwise.min.denominator = 10000000; 13018c2ecf20Sopenharmony_ci fival->stepwise.max.numerator = frame->dwFrameInterval[1]; 13028c2ecf20Sopenharmony_ci fival->stepwise.max.denominator = 10000000; 13038c2ecf20Sopenharmony_ci fival->stepwise.step.numerator = frame->dwFrameInterval[2]; 13048c2ecf20Sopenharmony_ci fival->stepwise.step.denominator = 10000000; 13058c2ecf20Sopenharmony_ci uvc_simplify_fraction(&fival->stepwise.min.numerator, 13068c2ecf20Sopenharmony_ci &fival->stepwise.min.denominator, 8, 333); 13078c2ecf20Sopenharmony_ci uvc_simplify_fraction(&fival->stepwise.max.numerator, 13088c2ecf20Sopenharmony_ci &fival->stepwise.max.denominator, 8, 333); 13098c2ecf20Sopenharmony_ci uvc_simplify_fraction(&fival->stepwise.step.numerator, 13108c2ecf20Sopenharmony_ci &fival->stepwise.step.denominator, 8, 333); 13118c2ecf20Sopenharmony_ci } 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci return 0; 13148c2ecf20Sopenharmony_ci} 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_cistatic int uvc_ioctl_subscribe_event(struct v4l2_fh *fh, 13178c2ecf20Sopenharmony_ci const struct v4l2_event_subscription *sub) 13188c2ecf20Sopenharmony_ci{ 13198c2ecf20Sopenharmony_ci switch (sub->type) { 13208c2ecf20Sopenharmony_ci case V4L2_EVENT_CTRL: 13218c2ecf20Sopenharmony_ci return v4l2_event_subscribe(fh, sub, 0, &uvc_ctrl_sub_ev_ops); 13228c2ecf20Sopenharmony_ci default: 13238c2ecf20Sopenharmony_ci return -EINVAL; 13248c2ecf20Sopenharmony_ci } 13258c2ecf20Sopenharmony_ci} 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_cistatic long uvc_ioctl_default(struct file *file, void *fh, bool valid_prio, 13288c2ecf20Sopenharmony_ci unsigned int cmd, void *arg) 13298c2ecf20Sopenharmony_ci{ 13308c2ecf20Sopenharmony_ci struct uvc_fh *handle = fh; 13318c2ecf20Sopenharmony_ci struct uvc_video_chain *chain = handle->chain; 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci switch (cmd) { 13348c2ecf20Sopenharmony_ci /* Dynamic controls. */ 13358c2ecf20Sopenharmony_ci case UVCIOC_CTRL_MAP: 13368c2ecf20Sopenharmony_ci return uvc_ioctl_ctrl_map(chain, arg); 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci case UVCIOC_CTRL_QUERY: 13398c2ecf20Sopenharmony_ci return uvc_xu_ctrl_query(chain, arg); 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci default: 13428c2ecf20Sopenharmony_ci return -ENOTTY; 13438c2ecf20Sopenharmony_ci } 13448c2ecf20Sopenharmony_ci} 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 13478c2ecf20Sopenharmony_cistruct uvc_xu_control_mapping32 { 13488c2ecf20Sopenharmony_ci u32 id; 13498c2ecf20Sopenharmony_ci u8 name[32]; 13508c2ecf20Sopenharmony_ci u8 entity[16]; 13518c2ecf20Sopenharmony_ci u8 selector; 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci u8 size; 13548c2ecf20Sopenharmony_ci u8 offset; 13558c2ecf20Sopenharmony_ci u32 v4l2_type; 13568c2ecf20Sopenharmony_ci u32 data_type; 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci compat_caddr_t menu_info; 13598c2ecf20Sopenharmony_ci u32 menu_count; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci u32 reserved[4]; 13628c2ecf20Sopenharmony_ci}; 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_cistatic int uvc_v4l2_get_xu_mapping(struct uvc_xu_control_mapping *kp, 13658c2ecf20Sopenharmony_ci const struct uvc_xu_control_mapping32 __user *up) 13668c2ecf20Sopenharmony_ci{ 13678c2ecf20Sopenharmony_ci struct uvc_xu_control_mapping32 *p = (void *)kp; 13688c2ecf20Sopenharmony_ci compat_caddr_t info; 13698c2ecf20Sopenharmony_ci u32 count; 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci if (copy_from_user(p, up, sizeof(*p))) 13728c2ecf20Sopenharmony_ci return -EFAULT; 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci count = p->menu_count; 13758c2ecf20Sopenharmony_ci info = p->menu_info; 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci memset(kp->reserved, 0, sizeof(kp->reserved)); 13788c2ecf20Sopenharmony_ci kp->menu_info = count ? compat_ptr(info) : NULL; 13798c2ecf20Sopenharmony_ci kp->menu_count = count; 13808c2ecf20Sopenharmony_ci return 0; 13818c2ecf20Sopenharmony_ci} 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_cistatic int uvc_v4l2_put_xu_mapping(const struct uvc_xu_control_mapping *kp, 13848c2ecf20Sopenharmony_ci struct uvc_xu_control_mapping32 __user *up) 13858c2ecf20Sopenharmony_ci{ 13868c2ecf20Sopenharmony_ci if (copy_to_user(up, kp, offsetof(typeof(*up), menu_info)) || 13878c2ecf20Sopenharmony_ci put_user(kp->menu_count, &up->menu_count)) 13888c2ecf20Sopenharmony_ci return -EFAULT; 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci if (clear_user(up->reserved, sizeof(up->reserved))) 13918c2ecf20Sopenharmony_ci return -EFAULT; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci return 0; 13948c2ecf20Sopenharmony_ci} 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_cistruct uvc_xu_control_query32 { 13978c2ecf20Sopenharmony_ci u8 unit; 13988c2ecf20Sopenharmony_ci u8 selector; 13998c2ecf20Sopenharmony_ci u8 query; 14008c2ecf20Sopenharmony_ci u16 size; 14018c2ecf20Sopenharmony_ci compat_caddr_t data; 14028c2ecf20Sopenharmony_ci}; 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_cistatic int uvc_v4l2_get_xu_query(struct uvc_xu_control_query *kp, 14058c2ecf20Sopenharmony_ci const struct uvc_xu_control_query32 __user *up) 14068c2ecf20Sopenharmony_ci{ 14078c2ecf20Sopenharmony_ci struct uvc_xu_control_query32 v; 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci if (copy_from_user(&v, up, sizeof(v))) 14108c2ecf20Sopenharmony_ci return -EFAULT; 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci *kp = (struct uvc_xu_control_query){ 14138c2ecf20Sopenharmony_ci .unit = v.unit, 14148c2ecf20Sopenharmony_ci .selector = v.selector, 14158c2ecf20Sopenharmony_ci .query = v.query, 14168c2ecf20Sopenharmony_ci .size = v.size, 14178c2ecf20Sopenharmony_ci .data = v.size ? compat_ptr(v.data) : NULL 14188c2ecf20Sopenharmony_ci }; 14198c2ecf20Sopenharmony_ci return 0; 14208c2ecf20Sopenharmony_ci} 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_cistatic int uvc_v4l2_put_xu_query(const struct uvc_xu_control_query *kp, 14238c2ecf20Sopenharmony_ci struct uvc_xu_control_query32 __user *up) 14248c2ecf20Sopenharmony_ci{ 14258c2ecf20Sopenharmony_ci if (copy_to_user(up, kp, offsetof(typeof(*up), data))) 14268c2ecf20Sopenharmony_ci return -EFAULT; 14278c2ecf20Sopenharmony_ci return 0; 14288c2ecf20Sopenharmony_ci} 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ci#define UVCIOC_CTRL_MAP32 _IOWR('u', 0x20, struct uvc_xu_control_mapping32) 14318c2ecf20Sopenharmony_ci#define UVCIOC_CTRL_QUERY32 _IOWR('u', 0x21, struct uvc_xu_control_query32) 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_cistatic long uvc_v4l2_compat_ioctl32(struct file *file, 14348c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 14358c2ecf20Sopenharmony_ci{ 14368c2ecf20Sopenharmony_ci struct uvc_fh *handle = file->private_data; 14378c2ecf20Sopenharmony_ci union { 14388c2ecf20Sopenharmony_ci struct uvc_xu_control_mapping xmap; 14398c2ecf20Sopenharmony_ci struct uvc_xu_control_query xqry; 14408c2ecf20Sopenharmony_ci } karg; 14418c2ecf20Sopenharmony_ci void __user *up = compat_ptr(arg); 14428c2ecf20Sopenharmony_ci long ret; 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci switch (cmd) { 14458c2ecf20Sopenharmony_ci case UVCIOC_CTRL_MAP32: 14468c2ecf20Sopenharmony_ci ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up); 14478c2ecf20Sopenharmony_ci if (ret) 14488c2ecf20Sopenharmony_ci return ret; 14498c2ecf20Sopenharmony_ci ret = uvc_ioctl_ctrl_map(handle->chain, &karg.xmap); 14508c2ecf20Sopenharmony_ci if (ret) 14518c2ecf20Sopenharmony_ci return ret; 14528c2ecf20Sopenharmony_ci ret = uvc_v4l2_put_xu_mapping(&karg.xmap, up); 14538c2ecf20Sopenharmony_ci if (ret) 14548c2ecf20Sopenharmony_ci return ret; 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci break; 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci case UVCIOC_CTRL_QUERY32: 14598c2ecf20Sopenharmony_ci ret = uvc_v4l2_get_xu_query(&karg.xqry, up); 14608c2ecf20Sopenharmony_ci if (ret) 14618c2ecf20Sopenharmony_ci return ret; 14628c2ecf20Sopenharmony_ci ret = uvc_xu_ctrl_query(handle->chain, &karg.xqry); 14638c2ecf20Sopenharmony_ci if (ret) 14648c2ecf20Sopenharmony_ci return ret; 14658c2ecf20Sopenharmony_ci ret = uvc_v4l2_put_xu_query(&karg.xqry, up); 14668c2ecf20Sopenharmony_ci if (ret) 14678c2ecf20Sopenharmony_ci return ret; 14688c2ecf20Sopenharmony_ci break; 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci default: 14718c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 14728c2ecf20Sopenharmony_ci } 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci return ret; 14758c2ecf20Sopenharmony_ci} 14768c2ecf20Sopenharmony_ci#endif 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_cistatic ssize_t uvc_v4l2_read(struct file *file, char __user *data, 14798c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 14808c2ecf20Sopenharmony_ci{ 14818c2ecf20Sopenharmony_ci uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_read: not implemented.\n"); 14828c2ecf20Sopenharmony_ci return -EINVAL; 14838c2ecf20Sopenharmony_ci} 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_cistatic int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) 14868c2ecf20Sopenharmony_ci{ 14878c2ecf20Sopenharmony_ci struct uvc_fh *handle = file->private_data; 14888c2ecf20Sopenharmony_ci struct uvc_streaming *stream = handle->stream; 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_mmap\n"); 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci return uvc_queue_mmap(&stream->queue, vma); 14938c2ecf20Sopenharmony_ci} 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_cistatic __poll_t uvc_v4l2_poll(struct file *file, poll_table *wait) 14968c2ecf20Sopenharmony_ci{ 14978c2ecf20Sopenharmony_ci struct uvc_fh *handle = file->private_data; 14988c2ecf20Sopenharmony_ci struct uvc_streaming *stream = handle->stream; 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n"); 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci return uvc_queue_poll(&stream->queue, file, wait); 15038c2ecf20Sopenharmony_ci} 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci#ifndef CONFIG_MMU 15068c2ecf20Sopenharmony_cistatic unsigned long uvc_v4l2_get_unmapped_area(struct file *file, 15078c2ecf20Sopenharmony_ci unsigned long addr, unsigned long len, unsigned long pgoff, 15088c2ecf20Sopenharmony_ci unsigned long flags) 15098c2ecf20Sopenharmony_ci{ 15108c2ecf20Sopenharmony_ci struct uvc_fh *handle = file->private_data; 15118c2ecf20Sopenharmony_ci struct uvc_streaming *stream = handle->stream; 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_get_unmapped_area\n"); 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci return uvc_queue_get_unmapped_area(&stream->queue, pgoff); 15168c2ecf20Sopenharmony_ci} 15178c2ecf20Sopenharmony_ci#endif 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ciconst struct v4l2_ioctl_ops uvc_ioctl_ops = { 15208c2ecf20Sopenharmony_ci .vidioc_querycap = uvc_ioctl_querycap, 15218c2ecf20Sopenharmony_ci .vidioc_enum_fmt_vid_cap = uvc_ioctl_enum_fmt_vid_cap, 15228c2ecf20Sopenharmony_ci .vidioc_enum_fmt_vid_out = uvc_ioctl_enum_fmt_vid_out, 15238c2ecf20Sopenharmony_ci .vidioc_g_fmt_vid_cap = uvc_ioctl_g_fmt_vid_cap, 15248c2ecf20Sopenharmony_ci .vidioc_g_fmt_vid_out = uvc_ioctl_g_fmt_vid_out, 15258c2ecf20Sopenharmony_ci .vidioc_s_fmt_vid_cap = uvc_ioctl_s_fmt_vid_cap, 15268c2ecf20Sopenharmony_ci .vidioc_s_fmt_vid_out = uvc_ioctl_s_fmt_vid_out, 15278c2ecf20Sopenharmony_ci .vidioc_try_fmt_vid_cap = uvc_ioctl_try_fmt_vid_cap, 15288c2ecf20Sopenharmony_ci .vidioc_try_fmt_vid_out = uvc_ioctl_try_fmt_vid_out, 15298c2ecf20Sopenharmony_ci .vidioc_reqbufs = uvc_ioctl_reqbufs, 15308c2ecf20Sopenharmony_ci .vidioc_querybuf = uvc_ioctl_querybuf, 15318c2ecf20Sopenharmony_ci .vidioc_qbuf = uvc_ioctl_qbuf, 15328c2ecf20Sopenharmony_ci .vidioc_expbuf = uvc_ioctl_expbuf, 15338c2ecf20Sopenharmony_ci .vidioc_dqbuf = uvc_ioctl_dqbuf, 15348c2ecf20Sopenharmony_ci .vidioc_create_bufs = uvc_ioctl_create_bufs, 15358c2ecf20Sopenharmony_ci .vidioc_streamon = uvc_ioctl_streamon, 15368c2ecf20Sopenharmony_ci .vidioc_streamoff = uvc_ioctl_streamoff, 15378c2ecf20Sopenharmony_ci .vidioc_enum_input = uvc_ioctl_enum_input, 15388c2ecf20Sopenharmony_ci .vidioc_g_input = uvc_ioctl_g_input, 15398c2ecf20Sopenharmony_ci .vidioc_s_input = uvc_ioctl_s_input, 15408c2ecf20Sopenharmony_ci .vidioc_queryctrl = uvc_ioctl_queryctrl, 15418c2ecf20Sopenharmony_ci .vidioc_query_ext_ctrl = uvc_ioctl_query_ext_ctrl, 15428c2ecf20Sopenharmony_ci .vidioc_g_ctrl = uvc_ioctl_g_ctrl, 15438c2ecf20Sopenharmony_ci .vidioc_s_ctrl = uvc_ioctl_s_ctrl, 15448c2ecf20Sopenharmony_ci .vidioc_g_ext_ctrls = uvc_ioctl_g_ext_ctrls, 15458c2ecf20Sopenharmony_ci .vidioc_s_ext_ctrls = uvc_ioctl_s_ext_ctrls, 15468c2ecf20Sopenharmony_ci .vidioc_try_ext_ctrls = uvc_ioctl_try_ext_ctrls, 15478c2ecf20Sopenharmony_ci .vidioc_querymenu = uvc_ioctl_querymenu, 15488c2ecf20Sopenharmony_ci .vidioc_g_selection = uvc_ioctl_g_selection, 15498c2ecf20Sopenharmony_ci .vidioc_g_parm = uvc_ioctl_g_parm, 15508c2ecf20Sopenharmony_ci .vidioc_s_parm = uvc_ioctl_s_parm, 15518c2ecf20Sopenharmony_ci .vidioc_enum_framesizes = uvc_ioctl_enum_framesizes, 15528c2ecf20Sopenharmony_ci .vidioc_enum_frameintervals = uvc_ioctl_enum_frameintervals, 15538c2ecf20Sopenharmony_ci .vidioc_subscribe_event = uvc_ioctl_subscribe_event, 15548c2ecf20Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 15558c2ecf20Sopenharmony_ci .vidioc_default = uvc_ioctl_default, 15568c2ecf20Sopenharmony_ci}; 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ciconst struct v4l2_file_operations uvc_fops = { 15598c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 15608c2ecf20Sopenharmony_ci .open = uvc_v4l2_open, 15618c2ecf20Sopenharmony_ci .release = uvc_v4l2_release, 15628c2ecf20Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 15638c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 15648c2ecf20Sopenharmony_ci .compat_ioctl32 = uvc_v4l2_compat_ioctl32, 15658c2ecf20Sopenharmony_ci#endif 15668c2ecf20Sopenharmony_ci .read = uvc_v4l2_read, 15678c2ecf20Sopenharmony_ci .mmap = uvc_v4l2_mmap, 15688c2ecf20Sopenharmony_ci .poll = uvc_v4l2_poll, 15698c2ecf20Sopenharmony_ci#ifndef CONFIG_MMU 15708c2ecf20Sopenharmony_ci .get_unmapped_area = uvc_v4l2_get_unmapped_area, 15718c2ecf20Sopenharmony_ci#endif 15728c2ecf20Sopenharmony_ci}; 15738c2ecf20Sopenharmony_ci 1574