📄 v4l1-compat.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 <bdirks@pacbell.net>
* et al.
*
*/
#ifndef __KERNEL__
#define __KERNEL__
#endif
#include <linux/config.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/smp_lock.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 <asm/uaccess.h>
#include <asm/system.h>
#include <asm/pgtable.h>
#ifdef CONFIG_KMOD
#include <linux/kmod.h>
#endif
static unsigned int debug = 0;
MODULE_PARM(debug,"i");
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...) if (debug) \
printk(KERN_DEBUG "v4l1-compat: " fmt , ## arg)
/*
* 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 inode *inode,
struct file *file,
int cid,
v4l2_kioctl drv)
{
struct v4l2_queryctrl qctrl2;
struct v4l2_control ctrl2;
int err;
qctrl2.id = cid;
err = drv(inode, 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(inode, 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 inode *inode,
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(inode, 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(inode, file, VIDIOC_S_CTRL, &ctrl2);
if (err < 0)
dprintk("VIDIOC_S_CTRL: %d\n",err);
}
return 0;
}
/* ----------------------------------------------------------------- */
static 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
palette_to_pixelformat(unsigned int palette)
{
if (palette < ARRAY_SIZE(palette2pixelformat))
return palette2pixelformat[palette];
else
return 0;
}
static unsigned int
pixelformat_to_palette(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)
{
int retval = 1;
poll_table *table;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48)
poll_table wait_table;
poll_initwait(&wait_table);
table = &wait_table;
#else
struct poll_wqueues pwq;
poll_initwait(&pwq);
table = &pwq.pt;
#endif
for (;;) {
int mask;
set_current_state(TASK_INTERRUPTIBLE);
mask = file->f_op->poll(file, table);
if (mask & POLLIN)
break;
table = NULL;
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
schedule();
}
set_current_state(TASK_RUNNING);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48)
poll_freewait(&wait_table);
#else
poll_freewait(&pwq);
#endif
return retval;
}
static int count_inputs(struct inode *inode,
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(inode,file,VIDIOC_ENUMINPUT, &input2))
break;
}
return i;
}
static int check_size(struct inode *inode,
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(inode,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(inode,file,VIDIOC_TRY_FMT, &fmt2))
goto done;
*maxw = fmt2.fmt.pix.width;
*maxh = fmt2.fmt.pix.height;
done:
return 0;
}
/* ----------------------------------------------------------------- */
/*
* This function is exported.
*/
int
v4l_compat_translate_ioctl(struct inode *inode,
struct file *file,
int cmd,
void *arg,
v4l2_kioctl drv)
{
struct v4l2_capability *cap2 = NULL;
struct v4l2_format *fmt2 = NULL;
struct v4l2_framebuffer fbuf2;
struct v4l2_input input2;
struct v4l2_tuner tun2;
struct v4l2_standard std2;
struct v4l2_frequency freq2;
struct v4l2_audio aud2;
struct v4l2_queryctrl qctrl2;
struct v4l2_buffer buf2;
v4l2_std_id sid;
int i, err = 0;
switch (cmd) {
case VIDIOCGCAP: /* capability */
{
struct video_capability *cap = arg;
cap2 = kmalloc(sizeof(*cap2),GFP_KERNEL);
memset(cap, 0, sizeof(*cap));
memset(cap2, 0, sizeof(*cap2));
memset(&fbuf2, 0, sizeof(fbuf2));
err = drv(inode, file, VIDIOC_QUERYCAP, cap2);
if (err < 0) {
dprintk("VIDIOCGCAP / VIDIOC_QUERYCAP: %d\n",err);
break;
}
if (cap2->capabilities & V4L2_CAP_VIDEO_OVERLAY) {
err = drv(inode, file, VIDIOC_G_FBUF, &fbuf2);
if (err < 0) {
dprintk("VIDIOCGCAP / VIDIOC_G_FBUF: %d\n",err);
memset(&fbuf2, 0, sizeof(fbuf2));
}
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 (fbuf2.capability & V4L2_FBUF_CAP_LIST_CLIPPING)
cap->type |= VID_TYPE_CLIPPING;
cap->channels = count_inputs(inode,file,drv);
check_size(inode,file,drv,
&cap->maxwidth,&cap->maxheight);
cap->audios = 0; /* FIXME */
cap->minwidth = 48; /* FIXME */
cap->minheight = 32; /* FIXME */
break;
}
case VIDIOCGFBUF: /* get frame buffer */
{
struct video_buffer *buffer = arg;
err = drv(inode, file, VIDIOC_G_FBUF, &fbuf2);
if (err < 0) {
dprintk("VIDIOCGFBUF / VIDIOC_G_FBUF: %d\n",err);
break;
}
buffer->base = fbuf2.base;
buffer->height = fbuf2.fmt.height;
buffer->width = fbuf2.fmt.width;
switch (fbuf2.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 (0 != fbuf2.fmt.bytesperline)
buffer->bytesperline = fbuf2.fmt.bytesperline;
else {
buffer->bytesperline =
(buffer->width * buffer->depth + 7) & 7;
buffer->bytesperline >>= 3;
}
break;
}
case VIDIOCSFBUF: /* set frame buffer */
{
struct video_buffer *buffer = arg;
memset(&fbuf2, 0, sizeof(fbuf2));
fbuf2.base = buffer->base;
fbuf2.fmt.height = buffer->height;
fbuf2.fmt.width = buffer->width;
switch (buffer->depth) {
case 8:
fbuf2.fmt.pixelformat = V4L2_PIX_FMT_RGB332;
break;
case 15:
fbuf2.fmt.pixelformat = V4L2_PIX_FMT_RGB555;
break;
case 16:
fbuf2.fmt.pixelformat = V4L2_PIX_FMT_RGB565;
break;
case 24:
fbuf2.fmt.pixelformat = V4L2_PIX_FMT_BGR24;
break;
case 32:
fbuf2.fmt.pixelformat = V4L2_PIX_FMT_BGR32;
break;
}
fbuf2.fmt.bytesperline = buffer->bytesperline;
err = drv(inode, file, VIDIOC_S_FBUF, &fbuf2);
if (err < 0)
dprintk("VIDIOCSFBUF / VIDIOC_S_FBUF: %d\n",err);
break;
}
case VIDIOCGWIN: /* get window or capture dimensions */
{
struct video_window *win = arg;
fmt2 = kmalloc(sizeof(*fmt2),GFP_KERNEL);
memset(win,0,sizeof(*win));
memset(fmt2,0,sizeof(*fmt2));
fmt2->type = V4L2_BUF_TYPE_VIDEO_OVERLAY;
err = drv(inode, file, VIDIOC_G_FMT, fmt2);
if (err < 0)
dprintk("VIDIOCGWIN / VIDIOC_G_WIN: %d\n",err);
if (err == 0) {
win->x = fmt2->fmt.win.w.left;
win->y = fmt2->fmt.win.w.top;
win->width = fmt2->fmt.win.w.width;
win->height = fmt2->fmt.win.w.height;
win->chromakey = fmt2->fmt.win.chromakey;
win->clips = NULL;
win->clipcount = 0;
break;
}
fmt2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
err = drv(inode, file, VIDIOC_G_FMT, fmt2);
if (err < 0) {
dprintk("VIDIOCGWIN / VIDIOC_G_FMT: %d\n",err);
break;
}
win->x = 0;
win->y = 0;
win->width = fmt2->fmt.pix.width;
win->height = fmt2->fmt.pix.height;
win->chromakey = 0;
win->clips = NULL;
win->clipcount = 0;
break;
}
case VIDIOCSWIN: /* set window and/or capture dimensions */
{
struct video_window *win = arg;
int err1,err2;
fmt2 = kmalloc(sizeof(*fmt2),GFP_KERNEL);
memset(fmt2,0,sizeof(*fmt2));
fmt2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
err1 = drv(inode, file, VIDIOC_G_FMT, fmt2);
if (err1 < 0)
dprintk("VIDIOCSWIN / VIDIOC_G_FMT: %d\n",err);
if (err1 == 0) {
fmt2->fmt.pix.width = win->width;
fmt2->fmt.pix.height = win->height;
fmt2->fmt.pix.field = V4L2_FIELD_ANY;
fmt2->fmt.pix.bytesperline = 0;
err = drv(inode, file, VIDIOC_S_FMT, fmt2);
if (err < 0)
dprintk("VIDIOCSWIN / VIDIOC_S_FMT #1: %d\n",
err);
win->width = fmt2->fmt.pix.width;
win->height = fmt2->fmt.pix.height;
}
memset(fmt2,0,sizeof(*fmt2));
fmt2->type = V4L2_BUF_TYPE_VIDEO_OVERLAY;
fmt2->fmt.win.w.left = win->x;
fmt2->fmt.win.w.top = win->y;
fmt2->fmt.win.w.width = win->width;
fmt2->fmt.win.w.height = win->height;
fmt2->fmt.win.chromakey = win->chromakey;
fmt2->fmt.win.clips = (void *)win->clips;
fmt2->fmt.win.clipcount = win->clipcount;
err2 = drv(inode, file, VIDIOC_S_FMT, fmt2);
if (err2 < 0)
dprintk("VIDIOCSWIN / VIDIOC_S_FMT #2: %d\n",err);
if (err1 != 0 && err2 != 0)
err = err1;
break;
}
case VIDIOCCAPTURE: /* turn on/off preview */
{
int *on = arg;
if (0 == *on) {
/* dirty hack time. But v4l1 has no STREAMOFF
* equivalent in the API, and this one at
* least comes close ... */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -