2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2024-12-29 23:53:55 +08:00
linux-next/drivers/media/video/v4l1-compat.c
Tejun Heo 5f820f648c poll: allow f_op->poll to sleep
f_op->poll is the only vfs operation which is not allowed to sleep.  It's
because poll and select implementation used task state to synchronize
against wake ups, which doesn't have to be the case anymore as wait/wake
interface can now use custom wake up functions.  The non-sleep restriction
can be a bit tricky because ->poll is not called from an atomic context
and the result of accidentally sleeping in ->poll only shows up as
temporary busy looping when the timing is right or rather wrong.

This patch converts poll/select to use custom wake up function and use
separate triggered variable to synchronize against wake up events.  The
only added overhead is an extra function call during wake up and
negligible.

This patch removes the one non-sleep exception from vfs locking rules and
is beneficial to userland filesystem implementations like FUSE, 9p or
peculiar fs like spufs as it's very difficult for those to implement
non-sleeping poll method.

While at it, make the following cosmetic changes to make poll.h and
select.c checkpatch friendly.

* s/type * symbol/type *symbol/		   : three places in poll.h
* remove blank line before EXPORT_SYMBOL() : two places in select.c

Oleg: spotted missing barrier in poll_schedule_timeout()
Davide: spotted missing write barrier in pollwake()

Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Eric Van Hensbergen <ericvh@gmail.com>
Cc: Ron Minnich <rminnich@sandia.gov>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Cc: Davide Libenzi <davidel@xmailserver.org>
Cc: Brad Boyer <flar@allandria.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Roland McGrath <roland@redhat.com>
Cc: Mauro Carvalho Chehab <mchehab@infradead.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Cc: Davide Libenzi <davidel@xmailserver.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-06 15:59:12 -08:00

1269 lines
31 KiB
C

/*
*
* Video for Linux Two
* Backward Compatibility Layer
*
* Support subroutines for providing V4L2 drivers with backward
* compatibility with applications using the old API.
*
* 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.
*
* Author: Bill Dirks <bill@thedirks.org>
* et al.
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/videodev.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/pgtable.h>
static unsigned int debug;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "enable debug messages");
MODULE_AUTHOR("Bill Dirks");
MODULE_DESCRIPTION("v4l(1) compatibility layer for v4l2 drivers.");
MODULE_LICENSE("GPL");
#define dprintk(fmt, arg...) \
do { \
if (debug) \
printk(KERN_DEBUG "v4l1-compat: " fmt , ## arg);\
} while (0)
/*
* I O C T L T R A N S L A T I O N
*
* From here on down is the code for translating the numerous
* ioctl commands from the old API to the new API.
*/
static int
get_v4l_control(struct file *file,
int cid,
v4l2_kioctl drv)
{
struct v4l2_queryctrl qctrl2;
struct v4l2_control ctrl2;
int err;
qctrl2.id = cid;
err = drv(file, VIDIOC_QUERYCTRL, &qctrl2);
if (err < 0)
dprintk("VIDIOC_QUERYCTRL: %d\n", err);
if (err == 0 && !(qctrl2.flags & V4L2_CTRL_FLAG_DISABLED)) {
ctrl2.id = qctrl2.id;
err = drv(file, VIDIOC_G_CTRL, &ctrl2);
if (err < 0) {
dprintk("VIDIOC_G_CTRL: %d\n", err);
return 0;
}
return ((ctrl2.value - qctrl2.minimum) * 65535
+ (qctrl2.maximum - qctrl2.minimum) / 2)
/ (qctrl2.maximum - qctrl2.minimum);
}
return 0;
}
static int
set_v4l_control(struct file *file,
int cid,
int value,
v4l2_kioctl drv)
{
struct v4l2_queryctrl qctrl2;
struct v4l2_control ctrl2;
int err;
qctrl2.id = cid;
err = drv(file, VIDIOC_QUERYCTRL, &qctrl2);
if (err < 0)
dprintk("VIDIOC_QUERYCTRL: %d\n", err);
if (err == 0 &&
!(qctrl2.flags & V4L2_CTRL_FLAG_DISABLED) &&
!(qctrl2.flags & V4L2_CTRL_FLAG_GRABBED)) {
if (value < 0)
value = 0;
if (value > 65535)
value = 65535;
if (value && qctrl2.type == V4L2_CTRL_TYPE_BOOLEAN)
value = 65535;
ctrl2.id = qctrl2.id;
ctrl2.value =
(value * (qctrl2.maximum - qctrl2.minimum)
+ 32767)
/ 65535;
ctrl2.value += qctrl2.minimum;
err = drv(file, VIDIOC_S_CTRL, &ctrl2);
if (err < 0)
dprintk("VIDIOC_S_CTRL: %d\n", err);
}
return 0;
}
/* ----------------------------------------------------------------- */
static const unsigned int palette2pixelformat[] = {
[VIDEO_PALETTE_GREY] = V4L2_PIX_FMT_GREY,
[VIDEO_PALETTE_RGB555] = V4L2_PIX_FMT_RGB555,
[VIDEO_PALETTE_RGB565] = V4L2_PIX_FMT_RGB565,
[VIDEO_PALETTE_RGB24] = V4L2_PIX_FMT_BGR24,
[VIDEO_PALETTE_RGB32] = V4L2_PIX_FMT_BGR32,
/* yuv packed pixel */
[VIDEO_PALETTE_YUYV] = V4L2_PIX_FMT_YUYV,
[VIDEO_PALETTE_YUV422] = V4L2_PIX_FMT_YUYV,
[VIDEO_PALETTE_UYVY] = V4L2_PIX_FMT_UYVY,
/* yuv planar */
[VIDEO_PALETTE_YUV410P] = V4L2_PIX_FMT_YUV410,
[VIDEO_PALETTE_YUV420] = V4L2_PIX_FMT_YUV420,
[VIDEO_PALETTE_YUV420P] = V4L2_PIX_FMT_YUV420,
[VIDEO_PALETTE_YUV411P] = V4L2_PIX_FMT_YUV411P,
[VIDEO_PALETTE_YUV422P] = V4L2_PIX_FMT_YUV422P,
};
static unsigned int __pure
palette_to_pixelformat(unsigned int palette)
{
if (palette < ARRAY_SIZE(palette2pixelformat))
return palette2pixelformat[palette];
else
return 0;
}
static unsigned int __attribute_const__
pixelformat_to_palette(unsigned int pixelformat)
{
int palette = 0;
switch (pixelformat) {
case V4L2_PIX_FMT_GREY:
palette = VIDEO_PALETTE_GREY;
break;
case V4L2_PIX_FMT_RGB555:
palette = VIDEO_PALETTE_RGB555;
break;
case V4L2_PIX_FMT_RGB565:
palette = VIDEO_PALETTE_RGB565;
break;
case V4L2_PIX_FMT_BGR24:
palette = VIDEO_PALETTE_RGB24;
break;
case V4L2_PIX_FMT_BGR32:
palette = VIDEO_PALETTE_RGB32;
break;
/* yuv packed pixel */
case V4L2_PIX_FMT_YUYV:
palette = VIDEO_PALETTE_YUYV;
break;
case V4L2_PIX_FMT_UYVY:
palette = VIDEO_PALETTE_UYVY;
break;
/* yuv planar */
case V4L2_PIX_FMT_YUV410:
palette = VIDEO_PALETTE_YUV420;
break;
case V4L2_PIX_FMT_YUV420:
palette = VIDEO_PALETTE_YUV420;
break;
case V4L2_PIX_FMT_YUV411P:
palette = VIDEO_PALETTE_YUV411P;
break;
case V4L2_PIX_FMT_YUV422P:
palette = VIDEO_PALETTE_YUV422P;
break;
}
return palette;
}
/* ----------------------------------------------------------------- */
static int poll_one(struct file *file, struct poll_wqueues *pwq)
{
int retval = 1;
poll_table *table;
poll_initwait(pwq);
table = &pwq->pt;
for (;;) {
int mask;
mask = file->f_op->poll(file, table);
if (mask & POLLIN)
break;
table = NULL;
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
poll_schedule(pwq, TASK_INTERRUPTIBLE);
}
poll_freewait(pwq);
return retval;
}
static int count_inputs(
struct file *file,
v4l2_kioctl drv)
{
struct v4l2_input input2;
int i;
for (i = 0;; i++) {
memset(&input2, 0, sizeof(input2));
input2.index = i;
if (0 != drv(file, VIDIOC_ENUMINPUT, &input2))
break;
}
return i;
}
static int check_size(
struct file *file,
v4l2_kioctl drv,
int *maxw,
int *maxh)
{
struct v4l2_fmtdesc desc2;
struct v4l2_format fmt2;
memset(&desc2, 0, sizeof(desc2));
memset(&fmt2, 0, sizeof(fmt2));
desc2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (0 != drv(file, VIDIOC_ENUM_FMT, &desc2))
goto done;
fmt2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt2.fmt.pix.width = 10000;
fmt2.fmt.pix.height = 10000;
fmt2.fmt.pix.pixelformat = desc2.pixelformat;
if (0 != drv(file, VIDIOC_TRY_FMT, &fmt2))
goto done;
*maxw = fmt2.fmt.pix.width;
*maxh = fmt2.fmt.pix.height;
done:
return 0;
}
/* ----------------------------------------------------------------- */
static noinline long v4l1_compat_get_capabilities(
struct video_capability *cap,
struct file *file,
v4l2_kioctl drv)
{
long err;
struct v4l2_framebuffer fbuf;
struct v4l2_capability *cap2;
cap2 = kzalloc(sizeof(*cap2), GFP_KERNEL);
if (!cap2) {
err = -ENOMEM;
return err;
}
memset(cap, 0, sizeof(*cap));
memset(&fbuf, 0, sizeof(fbuf));
err = drv(file, VIDIOC_QUERYCAP, cap2);
if (err < 0) {
dprintk("VIDIOCGCAP / VIDIOC_QUERYCAP: %ld\n", err);
goto done;
}
if (cap2->capabilities & V4L2_CAP_VIDEO_OVERLAY) {
err = drv(file, VIDIOC_G_FBUF, &fbuf);
if (err < 0) {
dprintk("VIDIOCGCAP / VIDIOC_G_FBUF: %ld\n", err);
memset(&fbuf, 0, sizeof(fbuf));
}
err = 0;
}
memcpy(cap->name, cap2->card,
min(sizeof(cap->name), sizeof(cap2->card)));
cap->name[sizeof(cap->name) - 1] = 0;
if (cap2->capabilities & V4L2_CAP_VIDEO_CAPTURE)
cap->type |= VID_TYPE_CAPTURE;
if (cap2->capabilities & V4L2_CAP_TUNER)
cap->type |= VID_TYPE_TUNER;
if (cap2->capabilities & V4L2_CAP_VBI_CAPTURE)
cap->type |= VID_TYPE_TELETEXT;
if (cap2->capabilities & V4L2_CAP_VIDEO_OVERLAY)
cap->type |= VID_TYPE_OVERLAY;
if (fbuf.capability & V4L2_FBUF_CAP_LIST_CLIPPING)
cap->type |= VID_TYPE_CLIPPING;
cap->channels = count_inputs(file, drv);
check_size(file, drv,
&cap->maxwidth, &cap->maxheight);
cap->audios = 0; /* FIXME */
cap->minwidth = 48; /* FIXME */
cap->minheight = 32; /* FIXME */
done:
kfree(cap2);
return err;
}
static noinline long v4l1_compat_get_frame_buffer(
struct video_buffer *buffer,
struct file *file,
v4l2_kioctl drv)
{
long err;
struct v4l2_framebuffer fbuf;
memset(buffer, 0, sizeof(*buffer));
memset(&fbuf, 0, sizeof(fbuf));
err = drv(file, VIDIOC_G_FBUF, &fbuf);
if (err < 0) {
dprintk("VIDIOCGFBUF / VIDIOC_G_FBUF: %ld\n", err);
goto done;
}
buffer->base = fbuf.base;
buffer->height = fbuf.fmt.height;
buffer->width = fbuf.fmt.width;
switch (fbuf.fmt.pixelformat) {
case V4L2_PIX_FMT_RGB332:
buffer->depth = 8;
break;
case V4L2_PIX_FMT_RGB555:
buffer->depth = 15;
break;
case V4L2_PIX_FMT_RGB565:
buffer->depth = 16;
break;
case V4L2_PIX_FMT_BGR24:
buffer->depth = 24;
break;
case V4L2_PIX_FMT_BGR32:
buffer->depth = 32;
break;
default:
buffer->depth = 0;
}
if (fbuf.fmt.bytesperline) {
buffer->bytesperline = fbuf.fmt.bytesperline;
if (!buffer->depth && buffer->width)
buffer->depth = ((fbuf.fmt.bytesperline<<3)
+ (buffer->width-1))
/ buffer->width;
} else {
buffer->bytesperline =
(buffer->width * buffer->depth + 7) & 7;
buffer->bytesperline >>= 3;
}
done:
return err;
}
static noinline long v4l1_compat_set_frame_buffer(
struct video_buffer *buffer,
struct file *file,
v4l2_kioctl drv)
{
long err;
struct v4l2_framebuffer fbuf;
memset(&fbuf, 0, sizeof(fbuf));
fbuf.base = buffer->base;
fbuf.fmt.height = buffer->height;
fbuf.fmt.width = buffer->width;
switch (buffer->depth) {
case 8:
fbuf.fmt.pixelformat = V4L2_PIX_FMT_RGB332;
break;
case 15:
fbuf.fmt.pixelformat = V4L2_PIX_FMT_RGB555;
break;
case 16:
fbuf.fmt.pixelformat = V4L2_PIX_FMT_RGB565;
break;
case 24:
fbuf.fmt.pixelformat = V4L2_PIX_FMT_BGR24;
break;
case 32:
fbuf.fmt.pixelformat = V4L2_PIX_FMT_BGR32;
break;
}
fbuf.fmt.bytesperline = buffer->bytesperline;
err = drv(file, VIDIOC_S_FBUF, &fbuf);
if (err < 0)
dprintk("VIDIOCSFBUF / VIDIOC_S_FBUF: %ld\n", err);
return err;
}
static noinline long v4l1_compat_get_win_cap_dimensions(
struct video_window *win,
struct file *file,
v4l2_kioctl drv)
{
long err;
struct v4l2_format *fmt;
fmt = kzalloc(sizeof(*fmt), GFP_KERNEL);
if (!fmt) {
err = -ENOMEM;
return err;
}
memset(win, 0, sizeof(*win));
fmt->type = V4L2_BUF_TYPE_VIDEO_OVERLAY;
err = drv(file, VIDIOC_G_FMT, fmt);
if (err < 0)
dprintk("VIDIOCGWIN / VIDIOC_G_WIN: %ld\n", err);
if (err == 0) {
win->x = fmt->fmt.win.w.left;
win->y = fmt->fmt.win.w.top;
win->width = fmt->fmt.win.w.width;
win->height = fmt->fmt.win.w.height;
win->chromakey = fmt->fmt.win.chromakey;
win->clips = NULL;
win->clipcount = 0;
goto done;
}
fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
err = drv(file, VIDIOC_G_FMT, fmt);
if (err < 0) {
dprintk("VIDIOCGWIN / VIDIOC_G_FMT: %ld\n", err);
goto done;
}
win->x = 0;
win->y = 0;
win->width = fmt->fmt.pix.width;
win->height = fmt->fmt.pix.height;
win->chromakey = 0;
win->clips = NULL;
win->clipcount = 0;
done:
kfree(fmt);
return err;
}
static noinline long v4l1_compat_set_win_cap_dimensions(
struct video_window *win,
struct file *file,
v4l2_kioctl drv)
{
long err, err1, err2;
struct v4l2_format *fmt;
fmt = kzalloc(sizeof(*fmt), GFP_KERNEL);
if (!fmt) {
err = -ENOMEM;
return err;
}
fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
drv(file, VIDIOC_STREAMOFF, &fmt->type);
err1 = drv(file, VIDIOC_G_FMT, fmt);
if (err1 < 0)
dprintk("VIDIOCSWIN / VIDIOC_G_FMT: %ld\n", err1);
if (err1 == 0) {
fmt->fmt.pix.width = win->width;
fmt->fmt.pix.height = win->height;
fmt->fmt.pix.field = V4L2_FIELD_ANY;
fmt->fmt.pix.bytesperline = 0;
err = drv(file, VIDIOC_S_FMT, fmt);
if (err < 0)
dprintk("VIDIOCSWIN / VIDIOC_S_FMT #1: %ld\n",
err);
win->width = fmt->fmt.pix.width;
win->height = fmt->fmt.pix.height;
}
memset(fmt, 0, sizeof(*fmt));
fmt->type = V4L2_BUF_TYPE_VIDEO_OVERLAY;
fmt->fmt.win.w.left = win->x;
fmt->fmt.win.w.top = win->y;
fmt->fmt.win.w.width = win->width;
fmt->fmt.win.w.height = win->height;
fmt->fmt.win.chromakey = win->chromakey;
fmt->fmt.win.clips = (void __user *)win->clips;
fmt->fmt.win.clipcount = win->clipcount;
err2 = drv(file, VIDIOC_S_FMT, fmt);
if (err2 < 0)
dprintk("VIDIOCSWIN / VIDIOC_S_FMT #2: %ld\n", err2);
if (err1 != 0 && err2 != 0)
err = err1;
else
err = 0;
kfree(fmt);
return err;
}
static noinline long v4l1_compat_turn_preview_on_off(
int *on,
struct file *file,
v4l2_kioctl drv)
{
long err;
enum v4l2_buf_type captype = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (0 == *on) {
/* dirty hack time. But v4l1 has no STREAMOFF
* equivalent in the API, and this one at
* least comes close ... */
drv(file, VIDIOC_STREAMOFF, &captype);
}
err = drv(file, VIDIOC_OVERLAY, on);
if (err < 0)
dprintk("VIDIOCCAPTURE / VIDIOC_PREVIEW: %ld\n", err);
return err;
}
static noinline long v4l1_compat_get_input_info(
struct video_channel *chan,
struct file *file,
v4l2_kioctl drv)
{
long err;
struct v4l2_input input2;
v4l2_std_id sid;
memset(&input2, 0, sizeof(input2));
input2.index = chan->channel;
err = drv(file, VIDIOC_ENUMINPUT, &input2);
if (err < 0) {
dprintk("VIDIOCGCHAN / VIDIOC_ENUMINPUT: "
"channel=%d err=%ld\n", chan->channel, err);
goto done;
}
chan->channel = input2.index;
memcpy(chan->name, input2.name,
min(sizeof(chan->name), sizeof(input2.name)));
chan->name[sizeof(chan->name) - 1] = 0;
chan->tuners = (input2.type == V4L2_INPUT_TYPE_TUNER) ? 1 : 0;
chan->flags = (chan->tuners) ? VIDEO_VC_TUNER : 0;
switch (input2.type) {
case V4L2_INPUT_TYPE_TUNER:
chan->type = VIDEO_TYPE_TV;
break;
default:
case V4L2_INPUT_TYPE_CAMERA:
chan->type = VIDEO_TYPE_CAMERA;
break;
}
chan->norm = 0;
err = drv(file, VIDIOC_G_STD, &sid);
if (err < 0)
dprintk("VIDIOCGCHAN / VIDIOC_G_STD: %ld\n", err);
if (err == 0) {
if (sid & V4L2_STD_PAL)
chan->norm = VIDEO_MODE_PAL;
if (sid & V4L2_STD_NTSC)
chan->norm = VIDEO_MODE_NTSC;
if (sid & V4L2_STD_SECAM)
chan->norm = VIDEO_MODE_SECAM;
}
done:
return err;
}
static noinline long v4l1_compat_set_input(
struct video_channel *chan,
struct file *file,
v4l2_kioctl drv)
{
long err;
v4l2_std_id sid = 0;
err = drv(file, VIDIOC_S_INPUT, &chan->channel);
if (err < 0)
dprintk("VIDIOCSCHAN / VIDIOC_S_INPUT: %ld\n", err);
switch (chan->norm) {
case VIDEO_MODE_PAL:
sid = V4L2_STD_PAL;
break;
case VIDEO_MODE_NTSC:
sid = V4L2_STD_NTSC;
break;
case VIDEO_MODE_SECAM:
sid = V4L2_STD_SECAM;
break;
}
if (0 != sid) {
err = drv(file, VIDIOC_S_STD, &sid);
if (err < 0)
dprintk("VIDIOCSCHAN / VIDIOC_S_STD: %ld\n", err);
}
return err;
}
static noinline long v4l1_compat_get_picture(
struct video_picture *pict,
struct file *file,
v4l2_kioctl drv)
{
long err;
struct v4l2_format *fmt;
fmt = kzalloc(sizeof(*fmt), GFP_KERNEL);
if (!fmt) {
err = -ENOMEM;
return err;
}
pict->brightness = get_v4l_control(file,
V4L2_CID_BRIGHTNESS, drv);
pict->hue = get_v4l_control(file,
V4L2_CID_HUE, drv);
pict->contrast = get_v4l_control(file,
V4L2_CID_CONTRAST, drv);
pict->colour = get_v4l_control(file,
V4L2_CID_SATURATION, drv);
pict->whiteness = get_v4l_control(file,
V4L2_CID_WHITENESS, drv);
fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
err = drv(file, VIDIOC_G_FMT, fmt);
if (err < 0) {
dprintk("VIDIOCGPICT / VIDIOC_G_FMT: %ld\n", err);
goto done;
}
pict->depth = ((fmt->fmt.pix.bytesperline << 3)
+ (fmt->fmt.pix.width - 1))
/ fmt->fmt.pix.width;
pict->palette = pixelformat_to_palette(
fmt->fmt.pix.pixelformat);
done:
kfree(fmt);
return err;
}
static noinline long v4l1_compat_set_picture(
struct video_picture *pict,
struct file *file,
v4l2_kioctl drv)
{
long err;
struct v4l2_framebuffer fbuf;
int mem_err = 0, ovl_err = 0;
struct v4l2_format *fmt;
fmt = kzalloc(sizeof(*fmt), GFP_KERNEL);
if (!fmt) {
err = -ENOMEM;
return err;
}
memset(&fbuf, 0, sizeof(fbuf));
set_v4l_control(file,
V4L2_CID_BRIGHTNESS, pict->brightness, drv);
set_v4l_control(file,
V4L2_CID_HUE, pict->hue, drv);
set_v4l_control(file,
V4L2_CID_CONTRAST, pict->contrast, drv);
set_v4l_control(file,
V4L2_CID_SATURATION, pict->colour, drv);
set_v4l_control(file,
V4L2_CID_WHITENESS, pict->whiteness, drv);
/*
* V4L1 uses this ioctl to set both memory capture and overlay
* pixel format, while V4L2 has two different ioctls for this.
* Some cards may not support one or the other, and may support
* different pixel formats for memory vs overlay.
*/
fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
err = drv(file, VIDIOC_G_FMT, fmt);
/* If VIDIOC_G_FMT failed, then the driver likely doesn't
support memory capture. Trying to set the memory capture
parameters would be pointless. */
if (err < 0) {
dprintk("VIDIOCSPICT / VIDIOC_G_FMT: %ld\n", err);
mem_err = -1000; /* didn't even try */
} else if (fmt->fmt.pix.pixelformat !=
palette_to_pixelformat(pict->palette)) {
fmt->fmt.pix.pixelformat = palette_to_pixelformat(
pict->palette);
mem_err = drv(file, VIDIOC_S_FMT, fmt);
if (mem_err < 0)
dprintk("VIDIOCSPICT / VIDIOC_S_FMT: %d\n",
mem_err);
}
err = drv(file, VIDIOC_G_FBUF, &fbuf);
/* If VIDIOC_G_FBUF failed, then the driver likely doesn't
support overlay. Trying to set the overlay parameters
would be quite pointless. */
if (err < 0) {
dprintk("VIDIOCSPICT / VIDIOC_G_FBUF: %ld\n", err);
ovl_err = -1000; /* didn't even try */
} else if (fbuf.fmt.pixelformat !=
palette_to_pixelformat(pict->palette)) {
fbuf.fmt.pixelformat = palette_to_pixelformat(
pict->palette);
ovl_err = drv(file, VIDIOC_S_FBUF, &fbuf);
if (ovl_err < 0)
dprintk("VIDIOCSPICT / VIDIOC_S_FBUF: %d\n",
ovl_err);
}
if (ovl_err < 0 && mem_err < 0) {
/* ioctl failed, couldn't set either parameter */
if (mem_err != -1000)
err = mem_err;
else if (ovl_err == -EPERM)
err = 0;
else
err = ovl_err;
} else
err = 0;
kfree(fmt);
return err;
}
static noinline long v4l1_compat_get_tuner(
struct video_tuner *tun,
struct file *file,
v4l2_kioctl drv)
{
long err;
int i;
struct v4l2_tuner tun2;
struct v4l2_standard std2;
v4l2_std_id sid;
memset(&tun2, 0, sizeof(tun2));
err = drv(file, VIDIOC_G_TUNER, &tun2);
if (err < 0) {
dprintk("VIDIOCGTUNER / VIDIOC_G_TUNER: %ld\n", err);
goto done;
}
memcpy(tun->name, tun2.name,
min(sizeof(tun->name), sizeof(tun2.name)));
tun->name[sizeof(tun->name) - 1] = 0;
tun->rangelow = tun2.rangelow;
tun->rangehigh = tun2.rangehigh;
tun->flags = 0;
tun->mode = VIDEO_MODE_AUTO;
for (i = 0; i < 64; i++) {
memset(&std2, 0, sizeof(std2));
std2.index = i;
if (0 != drv(file, VIDIOC_ENUMSTD, &std2))
break;
if (std2.id & V4L2_STD_PAL)
tun->flags |= VIDEO_TUNER_PAL;
if (std2.id & V4L2_STD_NTSC)
tun->flags |= VIDEO_TUNER_NTSC;
if (std2.id & V4L2_STD_SECAM)
tun->flags |= VIDEO_TUNER_SECAM;
}
err = drv(file, VIDIOC_G_STD, &sid);
if (err < 0)
dprintk("VIDIOCGTUNER / VIDIOC_G_STD: %ld\n", err);
if (err == 0) {
if (sid & V4L2_STD_PAL)
tun->mode = VIDEO_MODE_PAL;
if (sid & V4L2_STD_NTSC)
tun->mode = VIDEO_MODE_NTSC;
if (sid & V4L2_STD_SECAM)
tun->mode = VIDEO_MODE_SECAM;
}
if (tun2.capability & V4L2_TUNER_CAP_LOW)
tun->flags |= VIDEO_TUNER_LOW;
if (tun2.rxsubchans & V4L2_TUNER_SUB_STEREO)
tun->flags |= VIDEO_TUNER_STEREO_ON;
tun->signal = tun2.signal;
done:
return err;
}
static noinline long v4l1_compat_select_tuner(
struct video_tuner *tun,
struct file *file,
v4l2_kioctl drv)
{
long err;
struct v4l2_tuner t;/*84 bytes on x86_64*/
memset(&t, 0, sizeof(t));
t.index = tun->tuner;
err = drv(file, VIDIOC_S_INPUT, &t);
if (err < 0)
dprintk("VIDIOCSTUNER / VIDIOC_S_INPUT: %ld\n", err);
return err;
}
static noinline long v4l1_compat_get_frequency(
unsigned long *freq,
struct file *file,
v4l2_kioctl drv)
{
long err;
struct v4l2_frequency freq2;
memset(&freq2, 0, sizeof(freq2));
freq2.tuner = 0;
err = drv(file, VIDIOC_G_FREQUENCY, &freq2);
if (err < 0)
dprintk("VIDIOCGFREQ / VIDIOC_G_FREQUENCY: %ld\n", err);
if (0 == err)
*freq = freq2.frequency;
return err;
}
static noinline long v4l1_compat_set_frequency(
unsigned long *freq,
struct file *file,
v4l2_kioctl drv)
{
long err;
struct v4l2_frequency freq2;
memset(&freq2, 0, sizeof(freq2));
drv(file, VIDIOC_G_FREQUENCY, &freq2);
freq2.frequency = *freq;
err = drv(file, VIDIOC_S_FREQUENCY, &freq2);
if (err < 0)
dprintk("VIDIOCSFREQ / VIDIOC_S_FREQUENCY: %ld\n", err);
return err;
}
static noinline long v4l1_compat_get_audio(
struct video_audio *aud,
struct file *file,
v4l2_kioctl drv)
{
long err;
int i;
struct v4l2_queryctrl qctrl2;
struct v4l2_audio aud2;
struct v4l2_tuner tun2;
memset(&aud2, 0, sizeof(aud2));
err = drv(file, VIDIOC_G_AUDIO, &aud2);
if (err < 0) {
dprintk("VIDIOCGAUDIO / VIDIOC_G_AUDIO: %ld\n", err);
goto done;
}
memcpy(aud->name, aud2.name,
min(sizeof(aud->name), sizeof(aud2.name)));
aud->name[sizeof(aud->name) - 1] = 0;
aud->audio = aud2.index;
aud->flags = 0;
i = get_v4l_control(file, V4L2_CID_AUDIO_VOLUME, drv);
if (i >= 0) {
aud->volume = i;
aud->flags |= VIDEO_AUDIO_VOLUME;
}
i = get_v4l_control(file, V4L2_CID_AUDIO_BASS, drv);
if (i >= 0) {
aud->bass = i;
aud->flags |= VIDEO_AUDIO_BASS;
}
i = get_v4l_control(file, V4L2_CID_AUDIO_TREBLE, drv);
if (i >= 0) {
aud->treble = i;
aud->flags |= VIDEO_AUDIO_TREBLE;
}
i = get_v4l_control(file, V4L2_CID_AUDIO_BALANCE, drv);
if (i >= 0) {
aud->balance = i;
aud->flags |= VIDEO_AUDIO_BALANCE;
}
i = get_v4l_control(file, V4L2_CID_AUDIO_MUTE, drv);
if (i >= 0) {
if (i)
aud->flags |= VIDEO_AUDIO_MUTE;
aud->flags |= VIDEO_AUDIO_MUTABLE;
}
aud->step = 1;
qctrl2.id = V4L2_CID_AUDIO_VOLUME;
if (drv(file, VIDIOC_QUERYCTRL, &qctrl2) == 0 &&
!(qctrl2.flags & V4L2_CTRL_FLAG_DISABLED))
aud->step = qctrl2.step;
aud->mode = 0;
memset(&tun2, 0, sizeof(tun2));
err = drv(file, VIDIOC_G_TUNER, &tun2);
if (err < 0) {
dprintk("VIDIOCGAUDIO / VIDIOC_G_TUNER: %ld\n", err);
err = 0;
goto done;
}
if (tun2.rxsubchans & V4L2_TUNER_SUB_LANG2)
aud->mode = VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
else if (tun2.rxsubchans & V4L2_TUNER_SUB_STEREO)
aud->mode = VIDEO_SOUND_STEREO;
else if (tun2.rxsubchans & V4L2_TUNER_SUB_MONO)
aud->mode = VIDEO_SOUND_MONO;
done:
return err;
}
static noinline long v4l1_compat_set_audio(
struct video_audio *aud,
struct file *file,
v4l2_kioctl drv)
{
long err;
struct v4l2_audio aud2;
struct v4l2_tuner tun2;
memset(&aud2, 0, sizeof(aud2));
memset(&tun2, 0, sizeof(tun2));
aud2.index = aud->audio;
err = drv(file, VIDIOC_S_AUDIO, &aud2);
if (err < 0) {
dprintk("VIDIOCSAUDIO / VIDIOC_S_AUDIO: %ld\n", err);
goto done;
}
set_v4l_control(file, V4L2_CID_AUDIO_VOLUME,
aud->volume, drv);
set_v4l_control(file, V4L2_CID_AUDIO_BASS,
aud->bass, drv);
set_v4l_control(file, V4L2_CID_AUDIO_TREBLE,
aud->treble, drv);
set_v4l_control(file, V4L2_CID_AUDIO_BALANCE,
aud->balance, drv);
set_v4l_control(file, V4L2_CID_AUDIO_MUTE,
!!(aud->flags & VIDEO_AUDIO_MUTE), drv);
err = drv(file, VIDIOC_G_TUNER, &tun2);
if (err < 0)
dprintk("VIDIOCSAUDIO / VIDIOC_G_TUNER: %ld\n", err);
if (err == 0) {
switch (aud->mode) {
default:
case VIDEO_SOUND_MONO:
case VIDEO_SOUND_LANG1:
tun2.audmode = V4L2_TUNER_MODE_MONO;
break;
case VIDEO_SOUND_STEREO:
tun2.audmode = V4L2_TUNER_MODE_STEREO;
break;
case VIDEO_SOUND_LANG2:
tun2.audmode = V4L2_TUNER_MODE_LANG2;
break;
}
err = drv(file, VIDIOC_S_TUNER, &tun2);
if (err < 0)
dprintk("VIDIOCSAUDIO / VIDIOC_S_TUNER: %ld\n", err);
}
err = 0;
done:
return err;
}
static noinline long v4l1_compat_capture_frame(
struct video_mmap *mm,
struct file *file,
v4l2_kioctl drv)
{
long err;
enum v4l2_buf_type captype = V4L2_BUF_TYPE_VIDEO_CAPTURE;
struct v4l2_buffer buf;
struct v4l2_format *fmt;
fmt = kzalloc(sizeof(*fmt), GFP_KERNEL);
if (!fmt) {
err = -ENOMEM;
return err;
}
memset(&buf, 0, sizeof(buf));
fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
err = drv(file, VIDIOC_G_FMT, fmt);
if (err < 0) {
dprintk("VIDIOCMCAPTURE / VIDIOC_G_FMT: %ld\n", err);
goto done;
}
if (mm->width != fmt->fmt.pix.width ||
mm->height != fmt->fmt.pix.height ||
palette_to_pixelformat(mm->format) !=
fmt->fmt.pix.pixelformat) {
/* New capture format... */
fmt->fmt.pix.width = mm->width;
fmt->fmt.pix.height = mm->height;
fmt->fmt.pix.pixelformat =
palette_to_pixelformat(mm->format);
fmt->fmt.pix.field = V4L2_FIELD_ANY;
fmt->fmt.pix.bytesperline = 0;
err = drv(file, VIDIOC_S_FMT, fmt);
if (err < 0) {
dprintk("VIDIOCMCAPTURE / VIDIOC_S_FMT: %ld\n", err);
goto done;
}
}
buf.index = mm->frame;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
err = drv(file, VIDIOC_QUERYBUF, &buf);
if (err < 0) {
dprintk("VIDIOCMCAPTURE / VIDIOC_QUERYBUF: %ld\n", err);
goto done;
}
err = drv(file, VIDIOC_QBUF, &buf);
if (err < 0) {
dprintk("VIDIOCMCAPTURE / VIDIOC_QBUF: %ld\n", err);
goto done;
}
err = drv(file, VIDIOC_STREAMON, &captype);
if (err < 0)
dprintk("VIDIOCMCAPTURE / VIDIOC_STREAMON: %ld\n", err);
done:
kfree(fmt);
return err;
}
static noinline long v4l1_compat_sync(
int *i,
struct file *file,
v4l2_kioctl drv)
{
long err;
enum v4l2_buf_type captype = V4L2_BUF_TYPE_VIDEO_CAPTURE;
struct v4l2_buffer buf;
struct poll_wqueues *pwq;
memset(&buf, 0, sizeof(buf));
buf.index = *i;
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
err = drv(file, VIDIOC_QUERYBUF, &buf);
if (err < 0) {
/* No such buffer */
dprintk("VIDIOCSYNC / VIDIOC_QUERYBUF: %ld\n", err);
goto done;
}
if (!(buf.flags & V4L2_BUF_FLAG_MAPPED)) {
/* Buffer is not mapped */
err = -EINVAL;
goto done;
}
/* make sure capture actually runs so we don't block forever */
err = drv(file, VIDIOC_STREAMON, &captype);
if (err < 0) {
dprintk("VIDIOCSYNC / VIDIOC_STREAMON: %ld\n", err);
goto done;
}
pwq = kmalloc(sizeof(*pwq), GFP_KERNEL);
/* Loop as long as the buffer is queued, but not done */
while ((buf.flags & (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE))
== V4L2_BUF_FLAG_QUEUED) {
err = poll_one(file, pwq);
if (err < 0 || /* error or sleep was interrupted */
err == 0) /* timeout? Shouldn't occur. */
break;
err = drv(file, VIDIOC_QUERYBUF, &buf);
if (err < 0)
dprintk("VIDIOCSYNC / VIDIOC_QUERYBUF: %ld\n", err);
}
kfree(pwq);
if (!(buf.flags & V4L2_BUF_FLAG_DONE)) /* not done */
goto done;
do {
err = drv(file, VIDIOC_DQBUF, &buf);
if (err < 0)
dprintk("VIDIOCSYNC / VIDIOC_DQBUF: %ld\n", err);
} while (err == 0 && buf.index != *i);
done:
return err;
}
static noinline long v4l1_compat_get_vbi_format(
struct vbi_format *fmt,
struct file *file,
v4l2_kioctl drv)
{
long err;
struct v4l2_format *fmt2;
fmt2 = kzalloc(sizeof(*fmt2), GFP_KERNEL);
if (!fmt2) {
err = -ENOMEM;
return err;
}
fmt2->type = V4L2_BUF_TYPE_VBI_CAPTURE;
err = drv(file, VIDIOC_G_FMT, fmt2);
if (err < 0) {
dprintk("VIDIOCGVBIFMT / VIDIOC_G_FMT: %ld\n", err);
goto done;
}
if (fmt2->fmt.vbi.sample_format != V4L2_PIX_FMT_GREY) {
err = -EINVAL;
goto done;
}
memset(fmt, 0, sizeof(*fmt));
fmt->samples_per_line = fmt2->fmt.vbi.samples_per_line;
fmt->sampling_rate = fmt2->fmt.vbi.sampling_rate;
fmt->sample_format = VIDEO_PALETTE_RAW;
fmt->start[0] = fmt2->fmt.vbi.start[0];
fmt->count[0] = fmt2->fmt.vbi.count[0];
fmt->start[1] = fmt2->fmt.vbi.start[1];
fmt->count[1] = fmt2->fmt.vbi.count[1];
fmt->flags = fmt2->fmt.vbi.flags & 0x03;
done:
kfree(fmt2);
return err;
}
static noinline long v4l1_compat_set_vbi_format(
struct vbi_format *fmt,
struct file *file,
v4l2_kioctl drv)
{
long err;
struct v4l2_format *fmt2 = NULL;
if (VIDEO_PALETTE_RAW != fmt->sample_format) {
err = -EINVAL;
return err;
}
fmt2 = kzalloc(sizeof(*fmt2), GFP_KERNEL);
if (!fmt2) {
err = -ENOMEM;
return err;
}
fmt2->type = V4L2_BUF_TYPE_VBI_CAPTURE;
fmt2->fmt.vbi.samples_per_line = fmt->samples_per_line;
fmt2->fmt.vbi.sampling_rate = fmt->sampling_rate;
fmt2->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
fmt2->fmt.vbi.start[0] = fmt->start[0];
fmt2->fmt.vbi.count[0] = fmt->count[0];
fmt2->fmt.vbi.start[1] = fmt->start[1];
fmt2->fmt.vbi.count[1] = fmt->count[1];
fmt2->fmt.vbi.flags = fmt->flags;
err = drv(file, VIDIOC_TRY_FMT, fmt2);
if (err < 0) {
dprintk("VIDIOCSVBIFMT / VIDIOC_TRY_FMT: %ld\n", err);
goto done;
}
if (fmt2->fmt.vbi.samples_per_line != fmt->samples_per_line ||
fmt2->fmt.vbi.sampling_rate != fmt->sampling_rate ||
fmt2->fmt.vbi.sample_format != V4L2_PIX_FMT_GREY ||
fmt2->fmt.vbi.start[0] != fmt->start[0] ||
fmt2->fmt.vbi.count[0] != fmt->count[0] ||
fmt2->fmt.vbi.start[1] != fmt->start[1] ||
fmt2->fmt.vbi.count[1] != fmt->count[1] ||
fmt2->fmt.vbi.flags != fmt->flags) {
err = -EINVAL;
goto done;
}
err = drv(file, VIDIOC_S_FMT, fmt2);
if (err < 0)
dprintk("VIDIOCSVBIFMT / VIDIOC_S_FMT: %ld\n", err);
done:
kfree(fmt2);
return err;
}
/*
* This function is exported.
*/
long
v4l_compat_translate_ioctl(struct file *file,
int cmd,
void *arg,
v4l2_kioctl drv)
{
long err;
switch (cmd) {
case VIDIOCGCAP: /* capability */
err = v4l1_compat_get_capabilities(arg, file, drv);
break;
case VIDIOCGFBUF: /* get frame buffer */
err = v4l1_compat_get_frame_buffer(arg, file, drv);
break;
case VIDIOCSFBUF: /* set frame buffer */
err = v4l1_compat_set_frame_buffer(arg, file, drv);
break;
case VIDIOCGWIN: /* get window or capture dimensions */
err = v4l1_compat_get_win_cap_dimensions(arg, file, drv);
break;
case VIDIOCSWIN: /* set window and/or capture dimensions */
err = v4l1_compat_set_win_cap_dimensions(arg, file, drv);
break;
case VIDIOCCAPTURE: /* turn on/off preview */
err = v4l1_compat_turn_preview_on_off(arg, file, drv);
break;
case VIDIOCGCHAN: /* get input information */
err = v4l1_compat_get_input_info(arg, file, drv);
break;
case VIDIOCSCHAN: /* set input */
err = v4l1_compat_set_input(arg, file, drv);
break;
case VIDIOCGPICT: /* get tone controls & partial capture format */
err = v4l1_compat_get_picture(arg, file, drv);
break;
case VIDIOCSPICT: /* set tone controls & partial capture format */
err = v4l1_compat_set_picture(arg, file, drv);
break;
case VIDIOCGTUNER: /* get tuner information */
err = v4l1_compat_get_tuner(arg, file, drv);
break;
case VIDIOCSTUNER: /* select a tuner input */
err = v4l1_compat_select_tuner(arg, file, drv);
break;
case VIDIOCGFREQ: /* get frequency */
err = v4l1_compat_get_frequency(arg, file, drv);
break;
case VIDIOCSFREQ: /* set frequency */
err = v4l1_compat_set_frequency(arg, file, drv);
break;
case VIDIOCGAUDIO: /* get audio properties/controls */
err = v4l1_compat_get_audio(arg, file, drv);
break;
case VIDIOCSAUDIO: /* set audio controls */
err = v4l1_compat_set_audio(arg, file, drv);
break;
case VIDIOCMCAPTURE: /* capture a frame */
err = v4l1_compat_capture_frame(arg, file, drv);
break;
case VIDIOCSYNC: /* wait for a frame */
err = v4l1_compat_sync(arg, file, drv);
break;
case VIDIOCGVBIFMT: /* query VBI data capture format */
err = v4l1_compat_get_vbi_format(arg, file, drv);
break;
case VIDIOCSVBIFMT:
err = v4l1_compat_set_vbi_format(arg, file, drv);
break;
default:
err = -ENOIOCTLCMD;
break;
}
return err;
}
EXPORT_SYMBOL(v4l_compat_translate_ioctl);
/*
* Local variables:
* c-basic-offset: 8
* End:
*/