Commit d5e90b7a authored by Laurent Pinchart's avatar Laurent Pinchart Committed by Mauro Carvalho Chehab

[media] uvcvideo: Move to video_ioctl2

Simplify ioctl handling by using video_ioctl2.
Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@osg.samsung.com>
parent 59b702ea
...@@ -1760,6 +1760,7 @@ static int uvc_register_video(struct uvc_device *dev, ...@@ -1760,6 +1760,7 @@ static int uvc_register_video(struct uvc_device *dev,
*/ */
vdev->v4l2_dev = &dev->vdev; vdev->v4l2_dev = &dev->vdev;
vdev->fops = &uvc_fops; vdev->fops = &uvc_fops;
vdev->ioctl_ops = &uvc_ioctl_ops;
vdev->release = uvc_release; vdev->release = uvc_release;
vdev->prio = &stream->chain->prio; vdev->prio = &stream->chain->prio;
if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
......
...@@ -551,553 +551,635 @@ static int uvc_v4l2_release(struct file *file) ...@@ -551,553 +551,635 @@ static int uvc_v4l2_release(struct file *file)
return 0; return 0;
} }
static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) static int uvc_ioctl_querycap(struct file *file, void *fh,
struct v4l2_capability *cap)
{ {
struct video_device *vdev = video_devdata(file); struct video_device *vdev = video_devdata(file);
struct uvc_fh *handle = file->private_data; struct uvc_fh *handle = file->private_data;
struct uvc_video_chain *chain = handle->chain; struct uvc_video_chain *chain = handle->chain;
struct uvc_streaming *stream = handle->stream; struct uvc_streaming *stream = handle->stream;
long ret = 0;
switch (cmd) { strlcpy(cap->driver, "uvcvideo", sizeof(cap->driver));
/* Query capabilities */ strlcpy(cap->card, vdev->name, sizeof(cap->card));
case VIDIOC_QUERYCAP: usb_make_path(stream->dev->udev, cap->bus_info, sizeof(cap->bus_info));
{ cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING
struct v4l2_capability *cap = arg; | chain->caps;
if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
memset(cap, 0, sizeof *cap); cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
strlcpy(cap->driver, "uvcvideo", sizeof cap->driver); else
strlcpy(cap->card, vdev->name, sizeof cap->card); cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
usb_make_path(stream->dev->udev,
cap->bus_info, sizeof(cap->bus_info));
cap->version = LINUX_VERSION_CODE;
cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING
| chain->caps;
if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
cap->device_caps = V4L2_CAP_VIDEO_CAPTURE
| V4L2_CAP_STREAMING;
else
cap->device_caps = V4L2_CAP_VIDEO_OUTPUT
| V4L2_CAP_STREAMING;
break;
}
/* Priority */ return 0;
case VIDIOC_G_PRIORITY: }
*(u32 *)arg = v4l2_prio_max(vdev->prio);
break;
case VIDIOC_S_PRIORITY: static int uvc_ioctl_enum_fmt(struct uvc_streaming *stream,
ret = v4l2_prio_check(vdev->prio, handle->vfh.prio); struct v4l2_fmtdesc *fmt)
if (ret < 0) {
return ret; struct uvc_format *format;
enum v4l2_buf_type type = fmt->type;
__u32 index = fmt->index;
return v4l2_prio_change(vdev->prio, &handle->vfh.prio, if (fmt->type != stream->type || fmt->index >= stream->nformats)
*(u32 *)arg); return -EINVAL;
/* Get, Set & Query control */ memset(fmt, 0, sizeof(*fmt));
case VIDIOC_QUERYCTRL: fmt->index = index;
return uvc_query_v4l2_ctrl(chain, arg); fmt->type = type;
format = &stream->format[fmt->index];
fmt->flags = 0;
if (format->flags & UVC_FMT_FLAG_COMPRESSED)
fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
strlcpy(fmt->description, format->name, sizeof(fmt->description));
fmt->description[sizeof(fmt->description) - 1] = 0;
fmt->pixelformat = format->fcc;
return 0;
}
case VIDIOC_G_CTRL: static int uvc_ioctl_enum_fmt_vid_cap(struct file *file, void *fh,
{ struct v4l2_fmtdesc *fmt)
struct v4l2_control *ctrl = arg; {
struct v4l2_ext_control xctrl; struct uvc_fh *handle = fh;
struct uvc_streaming *stream = handle->stream;
memset(&xctrl, 0, sizeof xctrl); return uvc_ioctl_enum_fmt(stream, fmt);
xctrl.id = ctrl->id; }
ret = uvc_ctrl_begin(chain); static int uvc_ioctl_enum_fmt_vid_out(struct file *file, void *fh,
if (ret < 0) struct v4l2_fmtdesc *fmt)
return ret; {
struct uvc_fh *handle = fh;
struct uvc_streaming *stream = handle->stream;
ret = uvc_ctrl_get(chain, &xctrl); return uvc_ioctl_enum_fmt(stream, fmt);
uvc_ctrl_rollback(handle); }
if (ret >= 0)
ctrl->value = xctrl.value;
break;
}
case VIDIOC_S_CTRL: static int uvc_ioctl_g_fmt_vid_cap(struct file *file, void *fh,
{ struct v4l2_format *fmt)
struct v4l2_control *ctrl = arg; {
struct v4l2_ext_control xctrl; struct uvc_fh *handle = fh;
struct uvc_streaming *stream = handle->stream;
ret = v4l2_prio_check(vdev->prio, handle->vfh.prio); return uvc_v4l2_get_format(stream, fmt);
if (ret < 0) }
return ret;
memset(&xctrl, 0, sizeof xctrl); static int uvc_ioctl_g_fmt_vid_out(struct file *file, void *fh,
xctrl.id = ctrl->id; struct v4l2_format *fmt)
xctrl.value = ctrl->value; {
struct uvc_fh *handle = fh;
struct uvc_streaming *stream = handle->stream;
ret = uvc_ctrl_begin(chain); return uvc_v4l2_get_format(stream, fmt);
if (ret < 0) }
return ret;
ret = uvc_ctrl_set(chain, &xctrl); static int uvc_ioctl_s_fmt_vid_cap(struct file *file, void *fh,
if (ret < 0) { struct v4l2_format *fmt)
uvc_ctrl_rollback(handle); {
return ret; struct uvc_fh *handle = fh;
} struct uvc_streaming *stream = handle->stream;
ret = uvc_ctrl_commit(handle, &xctrl, 1); int ret;
if (ret == 0)
ctrl->value = xctrl.value;
break;
}
case VIDIOC_QUERYMENU: ret = uvc_acquire_privileges(handle);
return uvc_query_v4l2_menu(chain, arg); if (ret < 0)
return ret;
case VIDIOC_G_EXT_CTRLS: return uvc_v4l2_set_format(stream, fmt);
{ }
struct v4l2_ext_controls *ctrls = arg;
struct v4l2_ext_control *ctrl = ctrls->controls;
unsigned int i;
ret = uvc_ctrl_begin(chain); static int uvc_ioctl_s_fmt_vid_out(struct file *file, void *fh,
if (ret < 0) struct v4l2_format *fmt)
return ret; {
struct uvc_fh *handle = fh;
struct uvc_streaming *stream = handle->stream;
int ret;
for (i = 0; i < ctrls->count; ++ctrl, ++i) { ret = uvc_acquire_privileges(handle);
ret = uvc_ctrl_get(chain, ctrl); if (ret < 0)
if (ret < 0) { return ret;
uvc_ctrl_rollback(handle);
ctrls->error_idx = i;
return ret;
}
}
ctrls->error_idx = 0;
ret = uvc_ctrl_rollback(handle);
break;
}
case VIDIOC_S_EXT_CTRLS: return uvc_v4l2_set_format(stream, fmt);
ret = v4l2_prio_check(vdev->prio, handle->vfh.prio); }
if (ret < 0)
return ret;
/* Fall through */
case VIDIOC_TRY_EXT_CTRLS:
{
struct v4l2_ext_controls *ctrls = arg;
struct v4l2_ext_control *ctrl = ctrls->controls;
unsigned int i;
ret = uvc_ctrl_begin(chain);
if (ret < 0)
return ret;
for (i = 0; i < ctrls->count; ++ctrl, ++i) { static int uvc_ioctl_try_fmt_vid_cap(struct file *file, void *fh,
ret = uvc_ctrl_set(chain, ctrl); struct v4l2_format *fmt)
if (ret < 0) { {
uvc_ctrl_rollback(handle); struct uvc_fh *handle = fh;
ctrls->error_idx = cmd == VIDIOC_S_EXT_CTRLS struct uvc_streaming *stream = handle->stream;
? ctrls->count : i; struct uvc_streaming_control probe;
return ret;
}
}
ctrls->error_idx = 0; return uvc_v4l2_try_format(stream, fmt, &probe, NULL, NULL);
}
if (cmd == VIDIOC_S_EXT_CTRLS) static int uvc_ioctl_try_fmt_vid_out(struct file *file, void *fh,
ret = uvc_ctrl_commit(handle, struct v4l2_format *fmt)
ctrls->controls, ctrls->count); {
else struct uvc_fh *handle = fh;
ret = uvc_ctrl_rollback(handle); struct uvc_streaming *stream = handle->stream;
break; struct uvc_streaming_control probe;
}
/* Get, Set & Enum input */ return uvc_v4l2_try_format(stream, fmt, &probe, NULL, NULL);
case VIDIOC_ENUMINPUT: }
{
const struct uvc_entity *selector = chain->selector;
struct v4l2_input *input = arg;
struct uvc_entity *iterm = NULL;
u32 index = input->index;
int pin = 0;
if (selector == NULL ||
(chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
if (index != 0)
return -EINVAL;
list_for_each_entry(iterm, &chain->entities, chain) {
if (UVC_ENTITY_IS_ITERM(iterm))
break;
}
pin = iterm->id;
} else if (index < selector->bNrInPins) {
pin = selector->baSourceID[index];
list_for_each_entry(iterm, &chain->entities, chain) {
if (!UVC_ENTITY_IS_ITERM(iterm))
continue;
if (iterm->id == pin)
break;
}
}
if (iterm == NULL || iterm->id != pin) static int uvc_ioctl_reqbufs(struct file *file, void *fh,
return -EINVAL; struct v4l2_requestbuffers *rb)
{
struct uvc_fh *handle = fh;
struct uvc_streaming *stream = handle->stream;
int ret;
memset(input, 0, sizeof *input); ret = uvc_acquire_privileges(handle);
input->index = index; if (ret < 0)
strlcpy(input->name, iterm->name, sizeof input->name); return ret;
if (UVC_ENTITY_TYPE(iterm) == UVC_ITT_CAMERA)
input->type = V4L2_INPUT_TYPE_CAMERA;
break;
}
case VIDIOC_G_INPUT: mutex_lock(&stream->mutex);
{ ret = uvc_alloc_buffers(&stream->queue, rb);
u8 input; mutex_unlock(&stream->mutex);
if (ret < 0)
return ret;
if (chain->selector == NULL || if (ret == 0)
(chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { uvc_dismiss_privileges(handle);
*(int *)arg = 0;
break;
}
ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, return 0;
chain->selector->id, chain->dev->intfnum, }
UVC_SU_INPUT_SELECT_CONTROL, &input, 1);
if (ret < 0)
return ret;
*(int *)arg = input - 1; static int uvc_ioctl_querybuf(struct file *file, void *fh,
break; struct v4l2_buffer *buf)
} {
struct uvc_fh *handle = fh;
struct uvc_streaming *stream = handle->stream;
case VIDIOC_S_INPUT: if (!uvc_has_privileges(handle))
{ return -EBUSY;
u32 input = *(u32 *)arg + 1;
ret = v4l2_prio_check(vdev->prio, handle->vfh.prio); return uvc_query_buffer(&stream->queue, buf);
if (ret < 0) }
return ret;
if ((ret = uvc_acquire_privileges(handle)) < 0) static int uvc_ioctl_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
return ret; {
struct uvc_fh *handle = fh;
struct uvc_streaming *stream = handle->stream;
if (chain->selector == NULL || if (!uvc_has_privileges(handle))
(chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { return -EBUSY;
if (input != 1)
return -EINVAL;
break;
}
if (input == 0 || input > chain->selector->bNrInPins) return uvc_queue_buffer(&stream->queue, buf);
return -EINVAL; }
return uvc_query_ctrl(chain->dev, UVC_SET_CUR, static int uvc_ioctl_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
chain->selector->id, chain->dev->intfnum, {
UVC_SU_INPUT_SELECT_CONTROL, &input, 1); struct uvc_fh *handle = fh;
} struct uvc_streaming *stream = handle->stream;
/* Try, Get, Set & Enum format */ if (!uvc_has_privileges(handle))
case VIDIOC_ENUM_FMT: return -EBUSY;
{
struct v4l2_fmtdesc *fmt = arg;
struct uvc_format *format;
enum v4l2_buf_type type = fmt->type;
__u32 index = fmt->index;
if (fmt->type != stream->type || return uvc_dequeue_buffer(&stream->queue, buf,
fmt->index >= stream->nformats) file->f_flags & O_NONBLOCK);
return -EINVAL; }
memset(fmt, 0, sizeof(*fmt)); static int uvc_ioctl_create_bufs(struct file *file, void *fh,
fmt->index = index; struct v4l2_create_buffers *cb)
fmt->type = type; {
struct uvc_fh *handle = fh;
format = &stream->format[fmt->index]; struct uvc_streaming *stream = handle->stream;
fmt->flags = 0; int ret;
if (format->flags & UVC_FMT_FLAG_COMPRESSED)
fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
strlcpy(fmt->description, format->name,
sizeof fmt->description);
fmt->description[sizeof fmt->description - 1] = 0;
fmt->pixelformat = format->fcc;
break;
}
case VIDIOC_TRY_FMT: ret = uvc_acquire_privileges(handle);
{ if (ret < 0)
struct uvc_streaming_control probe; return ret;
return uvc_v4l2_try_format(stream, arg, &probe, NULL, NULL); return uvc_create_buffers(&stream->queue, cb);
} }
case VIDIOC_S_FMT: static int uvc_ioctl_streamon(struct file *file, void *fh,
ret = v4l2_prio_check(vdev->prio, handle->vfh.prio); enum v4l2_buf_type type)
if (ret < 0) {
return ret; struct uvc_fh *handle = fh;
struct uvc_streaming *stream = handle->stream;
int ret;
if ((ret = uvc_acquire_privileges(handle)) < 0) if (type != stream->type)
return ret; return -EINVAL;
if (!uvc_has_privileges(handle))
return -EBUSY;
mutex_lock(&stream->mutex);
ret = uvc_video_enable(stream, 1);
mutex_unlock(&stream->mutex);
return uvc_v4l2_set_format(stream, arg); return ret;
}
case VIDIOC_G_FMT: static int uvc_ioctl_streamoff(struct file *file, void *fh,
return uvc_v4l2_get_format(stream, arg); enum v4l2_buf_type type)
{
struct uvc_fh *handle = fh;
struct uvc_streaming *stream = handle->stream;
int ret;
/* Frame size enumeration */ if (type != stream->type)
case VIDIOC_ENUM_FRAMESIZES: return -EINVAL;
{
struct v4l2_frmsizeenum *fsize = arg;
struct uvc_format *format = NULL;
struct uvc_frame *frame;
int i;
/* Look for the given pixel format */ if (!uvc_has_privileges(handle))
for (i = 0; i < stream->nformats; i++) { return -EBUSY;
if (stream->format[i].fcc ==
fsize->pixel_format) {
format = &stream->format[i];
break;
}
}
if (format == NULL)
return -EINVAL;
if (fsize->index >= format->nframes) mutex_lock(&stream->mutex);
return -EINVAL; ret = uvc_video_enable(stream, 0);
mutex_unlock(&stream->mutex);
frame = &format->frame[fsize->index]; return ret;
fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; }
fsize->discrete.width = frame->wWidth;
fsize->discrete.height = frame->wHeight;
break;
}
/* Frame interval enumeration */ static int uvc_ioctl_enum_input(struct file *file, void *fh,
case VIDIOC_ENUM_FRAMEINTERVALS: struct v4l2_input *input)
{ {
struct v4l2_frmivalenum *fival = arg; struct uvc_fh *handle = fh;
struct uvc_format *format = NULL; struct uvc_video_chain *chain = handle->chain;
struct uvc_frame *frame = NULL; const struct uvc_entity *selector = chain->selector;
int i; struct uvc_entity *iterm = NULL;
u32 index = input->index;
/* Look for the given pixel format and frame size */ int pin = 0;
for (i = 0; i < stream->nformats; i++) {
if (stream->format[i].fcc == if (selector == NULL ||
fival->pixel_format) { (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
format = &stream->format[i]; if (index != 0)
break;
}
}
if (format == NULL)
return -EINVAL; return -EINVAL;
list_for_each_entry(iterm, &chain->entities, chain) {
for (i = 0; i < format->nframes; i++) { if (UVC_ENTITY_IS_ITERM(iterm))
if (format->frame[i].wWidth == fival->width &&
format->frame[i].wHeight == fival->height) {
frame = &format->frame[i];
break; break;
}
} }
if (frame == NULL) pin = iterm->id;
return -EINVAL; } else if (index < selector->bNrInPins) {
pin = selector->baSourceID[index];
if (frame->bFrameIntervalType) { list_for_each_entry(iterm, &chain->entities, chain) {
if (fival->index >= frame->bFrameIntervalType) if (!UVC_ENTITY_IS_ITERM(iterm))
return -EINVAL; continue;
if (iterm->id == pin)
fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; break;
fival->discrete.numerator =
frame->dwFrameInterval[fival->index];
fival->discrete.denominator = 10000000;
uvc_simplify_fraction(&fival->discrete.numerator,
&fival->discrete.denominator, 8, 333);
} else {
fival->type = V4L2_FRMIVAL_TYPE_STEPWISE;
fival->stepwise.min.numerator =
frame->dwFrameInterval[0];
fival->stepwise.min.denominator = 10000000;
fival->stepwise.max.numerator =
frame->dwFrameInterval[1];
fival->stepwise.max.denominator = 10000000;
fival->stepwise.step.numerator =
frame->dwFrameInterval[2];
fival->stepwise.step.denominator = 10000000;
uvc_simplify_fraction(&fival->stepwise.min.numerator,
&fival->stepwise.min.denominator, 8, 333);
uvc_simplify_fraction(&fival->stepwise.max.numerator,
&fival->stepwise.max.denominator, 8, 333);
uvc_simplify_fraction(&fival->stepwise.step.numerator,
&fival->stepwise.step.denominator, 8, 333);
} }
break;
} }
/* Get & Set streaming parameters */ if (iterm == NULL || iterm->id != pin)
case VIDIOC_G_PARM: return -EINVAL;
return uvc_v4l2_get_streamparm(stream, arg);
case VIDIOC_S_PARM: memset(input, 0, sizeof(*input));
ret = v4l2_prio_check(vdev->prio, handle->vfh.prio); input->index = index;
if (ret < 0) strlcpy(input->name, iterm->name, sizeof(input->name));
return ret; if (UVC_ENTITY_TYPE(iterm) == UVC_ITT_CAMERA)
input->type = V4L2_INPUT_TYPE_CAMERA;
if ((ret = uvc_acquire_privileges(handle)) < 0) return 0;
return ret; }
return uvc_v4l2_set_streamparm(stream, arg); static int uvc_ioctl_g_input(struct file *file, void *fh, unsigned int *input)
{
struct uvc_fh *handle = fh;
struct uvc_video_chain *chain = handle->chain;
int ret;
u8 i;
/* Cropping and scaling */ if (chain->selector == NULL ||
case VIDIOC_CROPCAP: (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
{ *input = 0;
struct v4l2_cropcap *ccap = arg; return 0;
}
if (ccap->type != stream->type) ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, chain->selector->id,
return -EINVAL; chain->dev->intfnum, UVC_SU_INPUT_SELECT_CONTROL,
&i, 1);
if (ret < 0)
return ret;
ccap->bounds.left = 0; *input = i - 1;
ccap->bounds.top = 0; return 0;
}
mutex_lock(&stream->mutex); static int uvc_ioctl_s_input(struct file *file, void *fh, unsigned int input)
ccap->bounds.width = stream->cur_frame->wWidth; {
ccap->bounds.height = stream->cur_frame->wHeight; struct uvc_fh *handle = fh;
mutex_unlock(&stream->mutex); struct uvc_video_chain *chain = handle->chain;
int ret;
u32 i;
ccap->defrect = ccap->bounds; ret = uvc_acquire_privileges(handle);
if (ret < 0)
return ret;
ccap->pixelaspect.numerator = 1; if (chain->selector == NULL ||
ccap->pixelaspect.denominator = 1; (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
break; if (input)
return -EINVAL;
return 0;
} }
case VIDIOC_G_CROP: if (input >= chain->selector->bNrInPins)
case VIDIOC_S_CROP: return -EINVAL;
return -ENOTTY;
/* Buffers & streaming */ i = input + 1;
case VIDIOC_REQBUFS: return uvc_query_ctrl(chain->dev, UVC_SET_CUR, chain->selector->id,
ret = v4l2_prio_check(vdev->prio, handle->vfh.prio); chain->dev->intfnum, UVC_SU_INPUT_SELECT_CONTROL,
if (ret < 0) &i, 1);
return ret; }
if ((ret = uvc_acquire_privileges(handle)) < 0) static int uvc_ioctl_queryctrl(struct file *file, void *fh,
return ret; struct v4l2_queryctrl *qc)
{
struct uvc_fh *handle = fh;
struct uvc_video_chain *chain = handle->chain;
mutex_lock(&stream->mutex); return uvc_query_v4l2_ctrl(chain, qc);
ret = uvc_alloc_buffers(&stream->queue, arg); }
mutex_unlock(&stream->mutex);
if (ret < 0)
return ret;
if (ret == 0) static int uvc_ioctl_g_ctrl(struct file *file, void *fh,
uvc_dismiss_privileges(handle); struct v4l2_control *ctrl)
{
struct uvc_fh *handle = fh;
struct uvc_video_chain *chain = handle->chain;
struct v4l2_ext_control xctrl;
int ret;
ret = 0; memset(&xctrl, 0, sizeof(xctrl));
break; xctrl.id = ctrl->id;
case VIDIOC_QUERYBUF: ret = uvc_ctrl_begin(chain);
{ if (ret < 0)
struct v4l2_buffer *buf = arg; return ret;
if (!uvc_has_privileges(handle)) ret = uvc_ctrl_get(chain, &xctrl);
return -EBUSY; uvc_ctrl_rollback(handle);
if (ret < 0)
return ret;
return uvc_query_buffer(&stream->queue, buf); ctrl->value = xctrl.value;
} return 0;
}
case VIDIOC_CREATE_BUFS: static int uvc_ioctl_s_ctrl(struct file *file, void *fh,
{ struct v4l2_control *ctrl)
struct v4l2_create_buffers *cb = arg; {
struct uvc_fh *handle = fh;
struct uvc_video_chain *chain = handle->chain;
struct v4l2_ext_control xctrl;
int ret;
ret = uvc_acquire_privileges(handle); memset(&xctrl, 0, sizeof(xctrl));
if (ret < 0) xctrl.id = ctrl->id;
return ret; xctrl.value = ctrl->value;
return uvc_create_buffers(&stream->queue, cb); ret = uvc_ctrl_begin(chain);
if (ret < 0)
return ret;
ret = uvc_ctrl_set(chain, &xctrl);
if (ret < 0) {
uvc_ctrl_rollback(handle);
return ret;
} }
case VIDIOC_QBUF: ret = uvc_ctrl_commit(handle, &xctrl, 1);
if (!uvc_has_privileges(handle)) if (ret < 0)
return -EBUSY; return ret;
return uvc_queue_buffer(&stream->queue, arg); ctrl->value = xctrl.value;
return 0;
}
case VIDIOC_DQBUF: static int uvc_ioctl_g_ext_ctrls(struct file *file, void *fh,
if (!uvc_has_privileges(handle)) struct v4l2_ext_controls *ctrls)
return -EBUSY; {
struct uvc_fh *handle = fh;
struct uvc_video_chain *chain = handle->chain;
struct v4l2_ext_control *ctrl = ctrls->controls;
unsigned int i;
int ret;
return uvc_dequeue_buffer(&stream->queue, arg, ret = uvc_ctrl_begin(chain);
file->f_flags & O_NONBLOCK); if (ret < 0)
return ret;
case VIDIOC_STREAMON: for (i = 0; i < ctrls->count; ++ctrl, ++i) {
{ ret = uvc_ctrl_get(chain, ctrl);
int *type = arg; if (ret < 0) {
uvc_ctrl_rollback(handle);
ctrls->error_idx = i;
return ret;
}
}
if (*type != stream->type) ctrls->error_idx = 0;
return -EINVAL;
ret = v4l2_prio_check(vdev->prio, handle->vfh.prio); return uvc_ctrl_rollback(handle);
if (ret < 0) }
return ret;
if (!uvc_has_privileges(handle)) static int uvc_ioctl_s_try_ext_ctrls(struct uvc_fh *handle,
return -EBUSY; struct v4l2_ext_controls *ctrls,
bool commit)
{
struct v4l2_ext_control *ctrl = ctrls->controls;
struct uvc_video_chain *chain = handle->chain;
unsigned int i;
int ret;
mutex_lock(&stream->mutex); ret = uvc_ctrl_begin(chain);
ret = uvc_video_enable(stream, 1); if (ret < 0)
mutex_unlock(&stream->mutex); return ret;
if (ret < 0)
for (i = 0; i < ctrls->count; ++ctrl, ++i) {
ret = uvc_ctrl_set(chain, ctrl);
if (ret < 0) {
uvc_ctrl_rollback(handle);
ctrls->error_idx = commit ? ctrls->count : i;
return ret; return ret;
break; }
} }
case VIDIOC_STREAMOFF: ctrls->error_idx = 0;
{
int *type = arg;
if (*type != stream->type) if (commit)
return -EINVAL; return uvc_ctrl_commit(handle, ctrls->controls, ctrls->count);
else
return uvc_ctrl_rollback(handle);
}
ret = v4l2_prio_check(vdev->prio, handle->vfh.prio); static int uvc_ioctl_s_ext_ctrls(struct file *file, void *fh,
if (ret < 0) struct v4l2_ext_controls *ctrls)
return ret; {
struct uvc_fh *handle = fh;
if (!uvc_has_privileges(handle)) return uvc_ioctl_s_try_ext_ctrls(handle, ctrls, true);
return -EBUSY; }
static int uvc_ioctl_try_ext_ctrls(struct file *file, void *fh,
struct v4l2_ext_controls *ctrls)
{
struct uvc_fh *handle = fh;
return uvc_ioctl_s_try_ext_ctrls(handle, ctrls, false);
}
return uvc_video_enable(stream, 0); static int uvc_ioctl_querymenu(struct file *file, void *fh,
struct v4l2_querymenu *qm)
{
struct uvc_fh *handle = fh;
struct uvc_video_chain *chain = handle->chain;
return uvc_query_v4l2_menu(chain, qm);
}
static int uvc_ioctl_cropcap(struct file *file, void *fh,
struct v4l2_cropcap *ccap)
{
struct uvc_fh *handle = fh;
struct uvc_streaming *stream = handle->stream;
if (ccap->type != stream->type)
return -EINVAL;
ccap->bounds.left = 0;
ccap->bounds.top = 0;
mutex_lock(&stream->mutex);
ccap->bounds.width = stream->cur_frame->wWidth;
ccap->bounds.height = stream->cur_frame->wHeight;
mutex_unlock(&stream->mutex);
ccap->defrect = ccap->bounds;
ccap->pixelaspect.numerator = 1;
ccap->pixelaspect.denominator = 1;
return 0;
}
static int uvc_ioctl_g_parm(struct file *file, void *fh,
struct v4l2_streamparm *parm)
{
struct uvc_fh *handle = fh;
struct uvc_streaming *stream = handle->stream;
return uvc_v4l2_get_streamparm(stream, parm);
}
static int uvc_ioctl_s_parm(struct file *file, void *fh,
struct v4l2_streamparm *parm)
{
struct uvc_fh *handle = fh;
struct uvc_streaming *stream = handle->stream;
int ret;
ret = uvc_acquire_privileges(handle);
if (ret < 0)
return ret;
return uvc_v4l2_set_streamparm(stream, parm);
}
static int uvc_ioctl_enum_framesizes(struct file *file, void *fh,
struct v4l2_frmsizeenum *fsize)
{
struct uvc_fh *handle = fh;
struct uvc_streaming *stream = handle->stream;
struct uvc_format *format = NULL;
struct uvc_frame *frame;
int i;
/* Look for the given pixel format */
for (i = 0; i < stream->nformats; i++) {
if (stream->format[i].fcc == fsize->pixel_format) {
format = &stream->format[i];
break;
}
} }
if (format == NULL)
return -EINVAL;
case VIDIOC_SUBSCRIBE_EVENT: if (fsize->index >= format->nframes)
{ return -EINVAL;
struct v4l2_event_subscription *sub = arg;
switch (sub->type) { frame = &format->frame[fsize->index];
case V4L2_EVENT_CTRL: fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
return v4l2_event_subscribe(&handle->vfh, sub, 0, fsize->discrete.width = frame->wWidth;
&uvc_ctrl_sub_ev_ops); fsize->discrete.height = frame->wHeight;
default: return 0;
return -EINVAL; }
static int uvc_ioctl_enum_frameintervals(struct file *file, void *fh,
struct v4l2_frmivalenum *fival)
{
struct uvc_fh *handle = fh;
struct uvc_streaming *stream = handle->stream;
struct uvc_format *format = NULL;
struct uvc_frame *frame = NULL;
int i;
/* Look for the given pixel format and frame size */
for (i = 0; i < stream->nformats; i++) {
if (stream->format[i].fcc == fival->pixel_format) {
format = &stream->format[i];
break;
} }
} }
if (format == NULL)
return -EINVAL;
case VIDIOC_UNSUBSCRIBE_EVENT: for (i = 0; i < format->nframes; i++) {
return v4l2_event_unsubscribe(&handle->vfh, arg); if (format->frame[i].wWidth == fival->width &&
format->frame[i].wHeight == fival->height) {
frame = &format->frame[i];
break;
}
}
if (frame == NULL)
return -EINVAL;
case VIDIOC_DQEVENT: if (frame->bFrameIntervalType) {
return v4l2_event_dequeue(&handle->vfh, arg, if (fival->index >= frame->bFrameIntervalType)
file->f_flags & O_NONBLOCK); return -EINVAL;
/* Analog video standards make no sense for digital cameras. */ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
case VIDIOC_ENUMSTD: fival->discrete.numerator =
case VIDIOC_QUERYSTD: frame->dwFrameInterval[fival->index];
case VIDIOC_G_STD: fival->discrete.denominator = 10000000;
case VIDIOC_S_STD: uvc_simplify_fraction(&fival->discrete.numerator,
&fival->discrete.denominator, 8, 333);
} else {
fival->type = V4L2_FRMIVAL_TYPE_STEPWISE;
fival->stepwise.min.numerator = frame->dwFrameInterval[0];
fival->stepwise.min.denominator = 10000000;
fival->stepwise.max.numerator = frame->dwFrameInterval[1];
fival->stepwise.max.denominator = 10000000;
fival->stepwise.step.numerator = frame->dwFrameInterval[2];
fival->stepwise.step.denominator = 10000000;
uvc_simplify_fraction(&fival->stepwise.min.numerator,
&fival->stepwise.min.denominator, 8, 333);
uvc_simplify_fraction(&fival->stepwise.max.numerator,
&fival->stepwise.max.denominator, 8, 333);
uvc_simplify_fraction(&fival->stepwise.step.numerator,
&fival->stepwise.step.denominator, 8, 333);
}
case VIDIOC_OVERLAY: return 0;
}
case VIDIOC_ENUMAUDIO: static int uvc_ioctl_subscribe_event(struct v4l2_fh *fh,
case VIDIOC_ENUMAUDOUT: const struct v4l2_event_subscription *sub)
{
switch (sub->type) {
case V4L2_EVENT_CTRL:
return v4l2_event_subscribe(fh, sub, 0, &uvc_ctrl_sub_ev_ops);
default:
return -EINVAL;
}
}
case VIDIOC_ENUMOUTPUT: static long uvc_ioctl_default(struct file *file, void *fh, bool valid_prio,
uvc_trace(UVC_TRACE_IOCTL, "Unsupported ioctl 0x%08x\n", cmd); unsigned int cmd, void *arg)
return -ENOTTY; {
struct uvc_fh *handle = fh;
struct uvc_video_chain *chain = handle->chain;
switch (cmd) {
/* Dynamic controls. */
case UVCIOC_CTRL_MAP: case UVCIOC_CTRL_MAP:
return uvc_ioctl_ctrl_map(chain, arg); return uvc_ioctl_ctrl_map(chain, arg);
...@@ -1105,23 +1187,8 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ...@@ -1105,23 +1187,8 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
return uvc_xu_ctrl_query(chain, arg); return uvc_xu_ctrl_query(chain, arg);
default: default:
uvc_trace(UVC_TRACE_IOCTL, "Unknown ioctl 0x%08x\n", cmd);
return -ENOTTY; return -ENOTTY;
} }
return ret;
}
static long uvc_v4l2_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
if (uvc_trace_param & UVC_TRACE_IOCTL) {
uvc_printk(KERN_DEBUG, "uvc_v4l2_ioctl(");
v4l_printk_ioctl(NULL, cmd);
printk(")\n");
}
return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl);
} }
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
...@@ -1304,7 +1371,7 @@ static long uvc_v4l2_compat_ioctl32(struct file *file, ...@@ -1304,7 +1371,7 @@ static long uvc_v4l2_compat_ioctl32(struct file *file,
old_fs = get_fs(); old_fs = get_fs();
set_fs(KERNEL_DS); set_fs(KERNEL_DS);
ret = uvc_v4l2_ioctl(file, cmd, (unsigned long)&karg); ret = video_ioctl2(file, cmd, (unsigned long)&karg);
set_fs(old_fs); set_fs(old_fs);
if (ret < 0) if (ret < 0)
...@@ -1365,11 +1432,48 @@ static unsigned long uvc_v4l2_get_unmapped_area(struct file *file, ...@@ -1365,11 +1432,48 @@ static unsigned long uvc_v4l2_get_unmapped_area(struct file *file,
} }
#endif #endif
const struct v4l2_ioctl_ops uvc_ioctl_ops = {
.vidioc_querycap = uvc_ioctl_querycap,
.vidioc_enum_fmt_vid_cap = uvc_ioctl_enum_fmt_vid_cap,
.vidioc_enum_fmt_vid_out = uvc_ioctl_enum_fmt_vid_out,
.vidioc_g_fmt_vid_cap = uvc_ioctl_g_fmt_vid_cap,
.vidioc_g_fmt_vid_out = uvc_ioctl_g_fmt_vid_out,
.vidioc_s_fmt_vid_cap = uvc_ioctl_s_fmt_vid_cap,
.vidioc_s_fmt_vid_out = uvc_ioctl_s_fmt_vid_out,
.vidioc_try_fmt_vid_cap = uvc_ioctl_try_fmt_vid_cap,
.vidioc_try_fmt_vid_out = uvc_ioctl_try_fmt_vid_out,
.vidioc_reqbufs = uvc_ioctl_reqbufs,
.vidioc_querybuf = uvc_ioctl_querybuf,
.vidioc_qbuf = uvc_ioctl_qbuf,
.vidioc_dqbuf = uvc_ioctl_dqbuf,
.vidioc_create_bufs = uvc_ioctl_create_bufs,
.vidioc_streamon = uvc_ioctl_streamon,
.vidioc_streamoff = uvc_ioctl_streamoff,
.vidioc_enum_input = uvc_ioctl_enum_input,
.vidioc_g_input = uvc_ioctl_g_input,
.vidioc_s_input = uvc_ioctl_s_input,
.vidioc_queryctrl = uvc_ioctl_queryctrl,
.vidioc_g_ctrl = uvc_ioctl_g_ctrl,
.vidioc_s_ctrl = uvc_ioctl_s_ctrl,
.vidioc_g_ext_ctrls = uvc_ioctl_g_ext_ctrls,
.vidioc_s_ext_ctrls = uvc_ioctl_s_ext_ctrls,
.vidioc_try_ext_ctrls = uvc_ioctl_try_ext_ctrls,
.vidioc_querymenu = uvc_ioctl_querymenu,
.vidioc_cropcap = uvc_ioctl_cropcap,
.vidioc_g_parm = uvc_ioctl_g_parm,
.vidioc_s_parm = uvc_ioctl_s_parm,
.vidioc_enum_framesizes = uvc_ioctl_enum_framesizes,
.vidioc_enum_frameintervals = uvc_ioctl_enum_frameintervals,
.vidioc_subscribe_event = uvc_ioctl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
.vidioc_default = uvc_ioctl_default,
};
const struct v4l2_file_operations uvc_fops = { const struct v4l2_file_operations uvc_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.open = uvc_v4l2_open, .open = uvc_v4l2_open,
.release = uvc_v4l2_release, .release = uvc_v4l2_release,
.unlocked_ioctl = uvc_v4l2_ioctl, .unlocked_ioctl = video_ioctl2,
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
.compat_ioctl32 = uvc_v4l2_compat_ioctl32, .compat_ioctl32 = uvc_v4l2_compat_ioctl32,
#endif #endif
......
...@@ -580,7 +580,6 @@ struct uvc_driver { ...@@ -580,7 +580,6 @@ struct uvc_driver {
#define UVC_TRACE_FORMAT (1 << 3) #define UVC_TRACE_FORMAT (1 << 3)
#define UVC_TRACE_CAPTURE (1 << 4) #define UVC_TRACE_CAPTURE (1 << 4)
#define UVC_TRACE_CALLS (1 << 5) #define UVC_TRACE_CALLS (1 << 5)
#define UVC_TRACE_IOCTL (1 << 6)
#define UVC_TRACE_FRAME (1 << 7) #define UVC_TRACE_FRAME (1 << 7)
#define UVC_TRACE_SUSPEND (1 << 8) #define UVC_TRACE_SUSPEND (1 << 8)
#define UVC_TRACE_STATUS (1 << 9) #define UVC_TRACE_STATUS (1 << 9)
...@@ -654,6 +653,7 @@ static inline int uvc_queue_streaming(struct uvc_video_queue *queue) ...@@ -654,6 +653,7 @@ static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
} }
/* V4L2 interface */ /* V4L2 interface */
extern const struct v4l2_ioctl_ops uvc_ioctl_ops;
extern const struct v4l2_file_operations uvc_fops; extern const struct v4l2_file_operations uvc_fops;
/* Media controller */ /* Media controller */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment