/*
* uvc_v4l2.c -- USB Video Class driver - V4L2 API
*
* Copyright (C) 2005-2010
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
*/
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/videodev2.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <linux/wait.h>
#include <asm/atomic.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include "uvcvideo.h"
/* ------------------------------------------------------------------------
* UVC ioctls
*/
static int uvc_ioctl_ctrl_map(struct uvc_video_chain *chain,
struct uvc_xu_control_mapping *xmap, int old)
{
struct uvc_control_mapping *map;
unsigned int size;
int ret;
map = kzalloc(sizeof *map, GFP_KERNEL);
if (map == NULL)
return -ENOMEM;
map->id = xmap->id;
memcpy(map->name, xmap->name, sizeof map->name);
memcpy(map->entity, xmap->entity, sizeof map->entity);
map->selector = xmap->selector;
map->size = xmap->size;
map->offset = xmap->offset;
map->v4l2_type = xmap->v4l2_type;
map->data_type = xmap->data_type;
switch (xmap->v4l2_type) {
case V4L2_CTRL_TYPE_INTEGER:
case V4L2_CTRL_TYPE_BOOLEAN:
case V4L2_CTRL_TYPE_BUTTON:
break;
case V4L2_CTRL_TYPE_MENU:
if (old) {
uvc_trace(UVC_TRACE_CONTROL, "V4L2_CTRL_TYPE_MENU not "
"supported for UVCIOC_CTRL_MAP_OLD.\n");
ret = -EINVAL;
goto done;
}
size = xmap->menu_count * sizeof(*map->menu_info);
map->menu_info = kmalloc(size, GFP_KERNEL);
if (map->menu_info == NULL) {
ret = -ENOMEM;
goto done;
}
if (copy_from_user(map->menu_info, xmap->menu_info, size)) {
ret = -EFAULT;
goto done;
}
map->menu_count = xmap->menu_count;
break;
default:
uvc_trace(UVC_TRACE_CONTROL, "Unsupported V4L2 control type "
"%u.\n", xmap->v4l2_type);
ret = -EINVAL;
goto done;
}
ret = uvc_ctrl_add_mapping(chain, map);
done:
kfree(map->menu_info);
kfree(map);
return ret;
}
/* ------------------------------------------------------------------------
* V4L2 interface
*/
/*
* Find the frame interval closest to the requested frame interval for the
* given frame format and size. This should be done by the device as part of
* the Video Probe and Commit negotiation, but some hardware don't implement
* that feature.
*/
static __u32 uvc_try_frame_interval(struct uvc_frame *frame, __u32 interval)
{
unsigned int i;
if (frame->bFrameIntervalType) {
__u32 best = -1, dist;
for (i = 0; i < frame->bFrameIntervalType; ++i) {
dist = interval > frame->dwFrameInterval[i]
? interval - frame->dwFrameInterval[i]
: frame->dwFrameInterval[i] - interval;
if (dist > best)
break;
best = dist;
}
interval = frame->dwFrameInterval[i-1];
} else {
const __u32 min = frame->dwFrameInterval[0];
const __u32 max = frame->dwFrameInterval[1];
const __u32 step =