2006-06-27 07:58:46 +08:00
|
|
|
/*
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
|
|
|
|
* Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2006-06-27 09:26:08 +08:00
|
|
|
#include <linux/version.h>
|
2006-06-27 07:58:46 +08:00
|
|
|
#include "pvrusb2-context.h"
|
|
|
|
#include "pvrusb2-hdw.h"
|
|
|
|
#include "pvrusb2.h"
|
|
|
|
#include "pvrusb2-debug.h"
|
|
|
|
#include "pvrusb2-v4l2.h"
|
|
|
|
#include "pvrusb2-ioread.h"
|
|
|
|
#include <linux/videodev2.h>
|
2006-09-24 10:47:50 +08:00
|
|
|
#include <media/v4l2-dev.h>
|
2006-06-27 07:58:46 +08:00
|
|
|
#include <media/v4l2-common.h>
|
2008-07-20 19:12:02 +08:00
|
|
|
#include <media/v4l2-ioctl.h>
|
2006-06-27 07:58:46 +08:00
|
|
|
|
|
|
|
struct pvr2_v4l2_dev;
|
|
|
|
struct pvr2_v4l2_fh;
|
|
|
|
struct pvr2_v4l2;
|
|
|
|
|
|
|
|
struct pvr2_v4l2_dev {
|
2006-09-24 09:30:50 +08:00
|
|
|
struct video_device devbase; /* MUST be first! */
|
2006-06-27 07:58:46 +08:00
|
|
|
struct pvr2_v4l2 *v4lp;
|
|
|
|
struct pvr2_context_stream *stream;
|
2006-12-31 05:27:32 +08:00
|
|
|
/* Information about this device: */
|
|
|
|
enum pvr2_config config; /* Expected stream format */
|
|
|
|
int v4l_type; /* V4L defined type for this device node */
|
|
|
|
enum pvr2_v4l_type minor_type; /* pvr2-understood minor device type */
|
2006-06-27 07:58:46 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct pvr2_v4l2_fh {
|
|
|
|
struct pvr2_channel channel;
|
2010-04-06 03:05:39 +08:00
|
|
|
struct pvr2_v4l2_dev *pdi;
|
2006-06-27 07:58:46 +08:00
|
|
|
enum v4l2_priority prio;
|
|
|
|
struct pvr2_ioread *rhp;
|
|
|
|
struct file *file;
|
|
|
|
struct pvr2_v4l2 *vhead;
|
|
|
|
struct pvr2_v4l2_fh *vnext;
|
|
|
|
struct pvr2_v4l2_fh *vprev;
|
|
|
|
wait_queue_head_t wait_data;
|
|
|
|
int fw_mode_flag;
|
2008-04-21 14:52:34 +08:00
|
|
|
/* Map contiguous ordinal value to input id */
|
|
|
|
unsigned char *input_map;
|
|
|
|
unsigned int input_cnt;
|
2006-06-27 07:58:46 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct pvr2_v4l2 {
|
|
|
|
struct pvr2_channel channel;
|
|
|
|
struct pvr2_v4l2_fh *vfirst;
|
|
|
|
struct pvr2_v4l2_fh *vlast;
|
|
|
|
|
|
|
|
struct v4l2_prio_state prio;
|
|
|
|
|
2006-12-28 10:19:42 +08:00
|
|
|
/* streams - Note that these must be separately, individually,
|
|
|
|
* allocated pointers. This is because the v4l core is going to
|
|
|
|
* manage their deletion - separately, individually... */
|
|
|
|
struct pvr2_v4l2_dev *dev_video;
|
|
|
|
struct pvr2_v4l2_dev *dev_radio;
|
2006-06-27 07:58:46 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static int video_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1};
|
|
|
|
module_param_array(video_nr, int, NULL, 0444);
|
2006-12-28 10:17:26 +08:00
|
|
|
MODULE_PARM_DESC(video_nr, "Offset for device's video dev minor");
|
|
|
|
static int radio_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1};
|
|
|
|
module_param_array(radio_nr, int, NULL, 0444);
|
|
|
|
MODULE_PARM_DESC(radio_nr, "Offset for device's radio dev minor");
|
|
|
|
static int vbi_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1};
|
|
|
|
module_param_array(vbi_nr, int, NULL, 0444);
|
|
|
|
MODULE_PARM_DESC(vbi_nr, "Offset for device's vbi dev minor");
|
2006-06-27 07:58:46 +08:00
|
|
|
|
2006-06-30 22:30:20 +08:00
|
|
|
static struct v4l2_capability pvr_capability ={
|
2006-06-27 07:58:46 +08:00
|
|
|
.driver = "pvrusb2",
|
|
|
|
.card = "Hauppauge WinTV pvr-usb2",
|
|
|
|
.bus_info = "usb",
|
2009-05-02 11:04:35 +08:00
|
|
|
.version = KERNEL_VERSION(0, 9, 0),
|
2009-01-14 15:21:29 +08:00
|
|
|
.capabilities = (V4L2_CAP_VIDEO_CAPTURE |
|
V4L/DVB (5039): Pvrusb2: Implement /dev/radioX
The "main" V4L2 interface patch. This is yet very incomplete, incorrect and
probably inappropriate for inclusion as-is, but at least with this I 'm able
to tune and play radio through a V4L2 program (pvr-radio.c, a "thumb" version
of ivtv-radio.c with just the essentials).
Therefore, it kinda gives an idea of what is needed to support this, hm,
interface (partly used also by e.g., kradio). Please point out any mistakes
on this code. I 'm sure I 'm messing up some struct initialization somewhere
but currently I 'm too lazy to actually think this through until I complete
the functionality (e.g., handle the VIDIOC_S_STD, ENUMINPUT, etc ioctls
appropriately).
Signed-off-by: Pantelis Koukousoulas <pakt223@freemail.gr>
Signed-off-by: Mike Isely <isely@pobox.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2006-12-28 10:09:55 +08:00
|
|
|
V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO |
|
2006-06-27 07:58:46 +08:00
|
|
|
V4L2_CAP_READWRITE),
|
|
|
|
.reserved = {0,0,0,0}
|
|
|
|
};
|
|
|
|
|
2006-06-30 22:30:20 +08:00
|
|
|
static struct v4l2_fmtdesc pvr_fmtdesc [] = {
|
2006-06-27 07:58:46 +08:00
|
|
|
{
|
|
|
|
.index = 0,
|
|
|
|
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
|
|
|
|
.flags = V4L2_FMT_FLAG_COMPRESSED,
|
|
|
|
.description = "MPEG1/2",
|
|
|
|
// This should really be V4L2_PIX_FMT_MPEG, but xawtv
|
|
|
|
// breaks when I do that.
|
|
|
|
.pixelformat = 0, // V4L2_PIX_FMT_MPEG,
|
|
|
|
.reserved = { 0, 0, 0, 0 }
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
#define PVR_FORMAT_PIX 0
|
|
|
|
#define PVR_FORMAT_VBI 1
|
|
|
|
|
2006-06-30 22:30:20 +08:00
|
|
|
static struct v4l2_format pvr_format [] = {
|
2006-06-27 07:58:46 +08:00
|
|
|
[PVR_FORMAT_PIX] = {
|
|
|
|
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
|
|
|
|
.fmt = {
|
|
|
|
.pix = {
|
|
|
|
.width = 720,
|
|
|
|
.height = 576,
|
|
|
|
// This should really be V4L2_PIX_FMT_MPEG,
|
|
|
|
// but xawtv breaks when I do that.
|
|
|
|
.pixelformat = 0, // V4L2_PIX_FMT_MPEG,
|
|
|
|
.field = V4L2_FIELD_INTERLACED,
|
|
|
|
.bytesperline = 0, // doesn't make sense
|
|
|
|
// here
|
|
|
|
//FIXME : Don't know what to put here...
|
|
|
|
.sizeimage = (32*1024),
|
|
|
|
.colorspace = 0, // doesn't make sense here
|
|
|
|
.priv = 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
[PVR_FORMAT_VBI] = {
|
|
|
|
.type = V4L2_BUF_TYPE_VBI_CAPTURE,
|
|
|
|
.fmt = {
|
|
|
|
.vbi = {
|
|
|
|
.sampling_rate = 27000000,
|
|
|
|
.offset = 248,
|
|
|
|
.samples_per_line = 1443,
|
|
|
|
.sample_format = V4L2_PIX_FMT_GREY,
|
|
|
|
.start = { 0, 0 },
|
|
|
|
.count = { 0, 0 },
|
|
|
|
.flags = 0,
|
|
|
|
.reserved = { 0, 0 }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2006-12-31 05:27:32 +08:00
|
|
|
|
2006-06-27 07:58:46 +08:00
|
|
|
/*
|
|
|
|
* pvr_ioctl()
|
|
|
|
*
|
|
|
|
* This is part of Video 4 Linux API. The procedure handles ioctl() calls.
|
|
|
|
*
|
|
|
|
*/
|
2008-12-30 18:04:34 +08:00
|
|
|
static long pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
|
2006-06-27 07:58:46 +08:00
|
|
|
{
|
|
|
|
struct pvr2_v4l2_fh *fh = file->private_data;
|
|
|
|
struct pvr2_v4l2 *vp = fh->vhead;
|
2010-04-06 03:05:39 +08:00
|
|
|
struct pvr2_v4l2_dev *pdi = fh->pdi;
|
2006-06-27 07:58:46 +08:00
|
|
|
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
|
2008-12-30 18:04:34 +08:00
|
|
|
long ret = -EINVAL;
|
2006-06-27 07:58:46 +08:00
|
|
|
|
|
|
|
if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) {
|
|
|
|
v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw),cmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pvr2_hdw_dev_ok(hdw)) {
|
|
|
|
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
|
|
|
|
"ioctl failed - bad or no context");
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check priority */
|
|
|
|
switch (cmd) {
|
|
|
|
case VIDIOC_S_CTRL:
|
|
|
|
case VIDIOC_S_STD:
|
|
|
|
case VIDIOC_S_INPUT:
|
|
|
|
case VIDIOC_S_TUNER:
|
|
|
|
case VIDIOC_S_FREQUENCY:
|
2010-05-01 19:03:24 +08:00
|
|
|
ret = v4l2_prio_check(&vp->prio, fh->prio);
|
2006-06-27 07:58:46 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case VIDIOC_QUERYCAP:
|
|
|
|
{
|
|
|
|
struct v4l2_capability *cap = arg;
|
|
|
|
|
|
|
|
memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability));
|
2007-04-08 12:11:47 +08:00
|
|
|
strlcpy(cap->bus_info,pvr2_hdw_get_bus_info(hdw),
|
|
|
|
sizeof(cap->bus_info));
|
2007-11-26 12:58:20 +08:00
|
|
|
strlcpy(cap->card,pvr2_hdw_get_desc(hdw),sizeof(cap->card));
|
2006-06-27 07:58:46 +08:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case VIDIOC_G_PRIORITY:
|
|
|
|
{
|
|
|
|
enum v4l2_priority *p = arg;
|
|
|
|
|
|
|
|
*p = v4l2_prio_max(&vp->prio);
|
|
|
|
ret = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case VIDIOC_S_PRIORITY:
|
|
|
|
{
|
|
|
|
enum v4l2_priority *prio = arg;
|
|
|
|
|
|
|
|
ret = v4l2_prio_change(&vp->prio, &fh->prio, *prio);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case VIDIOC_ENUMSTD:
|
|
|
|
{
|
|
|
|
struct v4l2_standard *vs = (struct v4l2_standard *)arg;
|
|
|
|
int idx = vs->index;
|
|
|
|
ret = pvr2_hdw_get_stdenum_value(hdw,vs,idx+1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case VIDIOC_G_STD:
|
|
|
|
{
|
|
|
|
int val = 0;
|
|
|
|
ret = pvr2_ctrl_get_value(
|
|
|
|
pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR),&val);
|
|
|
|
*(v4l2_std_id *)arg = val;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case VIDIOC_S_STD:
|
|
|
|
{
|
|
|
|
ret = pvr2_ctrl_set_value(
|
|
|
|
pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR),
|
|
|
|
*(v4l2_std_id *)arg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case VIDIOC_ENUMINPUT:
|
|
|
|
{
|
|
|
|
struct pvr2_ctrl *cptr;
|
|
|
|
struct v4l2_input *vi = (struct v4l2_input *)arg;
|
|
|
|
struct v4l2_input tmp;
|
|
|
|
unsigned int cnt;
|
2008-04-23 01:45:38 +08:00
|
|
|
int val;
|
2006-06-27 07:58:46 +08:00
|
|
|
|
|
|
|
cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT);
|
|
|
|
|
|
|
|
memset(&tmp,0,sizeof(tmp));
|
|
|
|
tmp.index = vi->index;
|
|
|
|
ret = 0;
|
2009-05-03 03:38:47 +08:00
|
|
|
if (vi->index >= fh->input_cnt) {
|
2008-04-23 01:45:38 +08:00
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
2008-04-21 14:52:34 +08:00
|
|
|
val = fh->input_map[vi->index];
|
2008-04-23 01:45:38 +08:00
|
|
|
switch (val) {
|
2006-06-27 07:58:46 +08:00
|
|
|
case PVR2_CVAL_INPUT_TV:
|
2008-04-23 01:45:37 +08:00
|
|
|
case PVR2_CVAL_INPUT_DTV:
|
2006-06-27 07:58:46 +08:00
|
|
|
case PVR2_CVAL_INPUT_RADIO:
|
|
|
|
tmp.type = V4L2_INPUT_TYPE_TUNER;
|
|
|
|
break;
|
|
|
|
case PVR2_CVAL_INPUT_SVIDEO:
|
|
|
|
case PVR2_CVAL_INPUT_COMPOSITE:
|
|
|
|
tmp.type = V4L2_INPUT_TYPE_CAMERA;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (ret < 0) break;
|
|
|
|
|
|
|
|
cnt = 0;
|
2008-04-23 01:45:38 +08:00
|
|
|
pvr2_ctrl_get_valname(cptr,val,
|
2006-06-27 07:58:46 +08:00
|
|
|
tmp.name,sizeof(tmp.name)-1,&cnt);
|
|
|
|
tmp.name[cnt] = 0;
|
|
|
|
|
|
|
|
/* Don't bother with audioset, since this driver currently
|
|
|
|
always switches the audio whenever the video is
|
|
|
|
switched. */
|
|
|
|
|
|
|
|
/* Handling std is a tougher problem. It doesn't make
|
|
|
|
sense in cases where a device might be multi-standard.
|
|
|
|
We could just copy out the current value for the
|
|
|
|
standard, but it can change over time. For now just
|
|
|
|
leave it zero. */
|
|
|
|
|
|
|
|
memcpy(vi, &tmp, sizeof(tmp));
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case VIDIOC_G_INPUT:
|
|
|
|
{
|
2008-04-23 01:45:38 +08:00
|
|
|
unsigned int idx;
|
2006-06-27 07:58:46 +08:00
|
|
|
struct pvr2_ctrl *cptr;
|
|
|
|
struct v4l2_input *vi = (struct v4l2_input *)arg;
|
|
|
|
int val;
|
|
|
|
cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT);
|
|
|
|
val = 0;
|
|
|
|
ret = pvr2_ctrl_get_value(cptr,&val);
|
2008-04-23 01:45:38 +08:00
|
|
|
vi->index = 0;
|
2008-04-21 14:52:34 +08:00
|
|
|
for (idx = 0; idx < fh->input_cnt; idx++) {
|
|
|
|
if (fh->input_map[idx] == val) {
|
2008-04-23 01:45:38 +08:00
|
|
|
vi->index = idx;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2006-06-27 07:58:46 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case VIDIOC_S_INPUT:
|
|
|
|
{
|
|
|
|
struct v4l2_input *vi = (struct v4l2_input *)arg;
|
2009-05-03 03:38:47 +08:00
|
|
|
if (vi->index >= fh->input_cnt) {
|
2008-04-23 01:45:38 +08:00
|
|
|
ret = -ERANGE;
|
|
|
|
break;
|
|
|
|
}
|
2006-06-27 07:58:46 +08:00
|
|
|
ret = pvr2_ctrl_set_value(
|
|
|
|
pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT),
|
2008-04-21 14:52:34 +08:00
|
|
|
fh->input_map[vi->index]);
|
2006-06-27 07:58:46 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case VIDIOC_ENUMAUDIO:
|
|
|
|
{
|
2007-01-20 12:57:36 +08:00
|
|
|
/* pkt: FIXME: We are returning one "fake" input here
|
|
|
|
which could very well be called "whatever_we_like".
|
|
|
|
This is for apps that want to see an audio input
|
|
|
|
just to feel comfortable, as well as to test if
|
|
|
|
it can do stereo or sth. There is actually no guarantee
|
|
|
|
that the actual audio input cannot change behind the app's
|
|
|
|
back, but most applications should not mind that either.
|
|
|
|
|
|
|
|
Hopefully, mplayer people will work with us on this (this
|
|
|
|
whole mess is to support mplayer pvr://), or Hans will come
|
|
|
|
up with a more standard way to say "we have inputs but we
|
|
|
|
don 't want you to change them independent of video" which
|
|
|
|
will sort this mess.
|
|
|
|
*/
|
|
|
|
struct v4l2_audio *vin = arg;
|
2006-06-27 07:58:46 +08:00
|
|
|
ret = -EINVAL;
|
2007-01-20 12:57:36 +08:00
|
|
|
if (vin->index > 0) break;
|
|
|
|
strncpy(vin->name, "PVRUSB2 Audio",14);
|
|
|
|
vin->capability = V4L2_AUDCAP_STEREO;
|
|
|
|
ret = 0;
|
|
|
|
break;
|
2006-06-27 07:58:46 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case VIDIOC_G_AUDIO:
|
|
|
|
{
|
2007-01-20 12:57:36 +08:00
|
|
|
/* pkt: FIXME: see above comment (VIDIOC_ENUMAUDIO) */
|
|
|
|
struct v4l2_audio *vin = arg;
|
|
|
|
memset(vin,0,sizeof(*vin));
|
|
|
|
vin->index = 0;
|
|
|
|
strncpy(vin->name, "PVRUSB2 Audio",14);
|
|
|
|
vin->capability = V4L2_AUDCAP_STEREO;
|
|
|
|
ret = 0;
|
2006-06-27 07:58:46 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case VIDIOC_S_AUDIO:
|
|
|
|
{
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case VIDIOC_G_TUNER:
|
|
|
|
{
|
|
|
|
struct v4l2_tuner *vt = (struct v4l2_tuner *)arg;
|
2007-01-22 09:02:58 +08:00
|
|
|
|
2007-01-22 13:17:55 +08:00
|
|
|
if (vt->index != 0) break; /* Only answer for the 1st tuner */
|
2007-01-22 09:02:58 +08:00
|
|
|
|
2007-01-20 11:09:47 +08:00
|
|
|
pvr2_hdw_execute_tuner_poll(hdw);
|
|
|
|
ret = pvr2_hdw_get_tuner_status(hdw,vt);
|
2006-06-27 07:58:46 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case VIDIOC_S_TUNER:
|
|
|
|
{
|
|
|
|
struct v4l2_tuner *vt=(struct v4l2_tuner *)arg;
|
|
|
|
|
|
|
|
if (vt->index != 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
ret = pvr2_ctrl_set_value(
|
|
|
|
pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_AUDIOMODE),
|
|
|
|
vt->audmode);
|
2007-01-20 11:24:52 +08:00
|
|
|
break;
|
2006-06-27 07:58:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
case VIDIOC_S_FREQUENCY:
|
|
|
|
{
|
|
|
|
const struct v4l2_frequency *vf = (struct v4l2_frequency *)arg;
|
2006-12-28 10:25:06 +08:00
|
|
|
unsigned long fv;
|
2007-01-20 11:09:47 +08:00
|
|
|
struct v4l2_tuner vt;
|
|
|
|
int cur_input;
|
|
|
|
struct pvr2_ctrl *ctrlp;
|
|
|
|
ret = pvr2_hdw_get_tuner_status(hdw,&vt);
|
|
|
|
if (ret != 0) break;
|
|
|
|
ctrlp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT);
|
|
|
|
ret = pvr2_ctrl_get_value(ctrlp,&cur_input);
|
|
|
|
if (ret != 0) break;
|
2006-12-28 10:25:06 +08:00
|
|
|
if (vf->type == V4L2_TUNER_RADIO) {
|
2007-01-20 11:09:47 +08:00
|
|
|
if (cur_input != PVR2_CVAL_INPUT_RADIO) {
|
|
|
|
pvr2_ctrl_set_value(ctrlp,
|
|
|
|
PVR2_CVAL_INPUT_RADIO);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (cur_input == PVR2_CVAL_INPUT_RADIO) {
|
|
|
|
pvr2_ctrl_set_value(ctrlp,
|
|
|
|
PVR2_CVAL_INPUT_TV);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fv = vf->frequency;
|
|
|
|
if (vt.capability & V4L2_TUNER_CAP_LOW) {
|
2006-12-28 10:25:06 +08:00
|
|
|
fv = (fv * 125) / 2;
|
|
|
|
} else {
|
|
|
|
fv = fv * 62500;
|
|
|
|
}
|
2006-06-27 07:58:46 +08:00
|
|
|
ret = pvr2_ctrl_set_value(
|
2006-12-28 10:25:06 +08:00
|
|
|
pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY),fv);
|
2006-06-27 07:58:46 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case VIDIOC_G_FREQUENCY:
|
|
|
|
{
|
|
|
|
struct v4l2_frequency *vf = (struct v4l2_frequency *)arg;
|
|
|
|
int val = 0;
|
2007-01-20 11:09:47 +08:00
|
|
|
int cur_input;
|
|
|
|
struct v4l2_tuner vt;
|
|
|
|
ret = pvr2_hdw_get_tuner_status(hdw,&vt);
|
|
|
|
if (ret != 0) break;
|
2006-06-27 07:58:46 +08:00
|
|
|
ret = pvr2_ctrl_get_value(
|
|
|
|
pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY),
|
|
|
|
&val);
|
2006-12-28 10:25:06 +08:00
|
|
|
if (ret != 0) break;
|
|
|
|
pvr2_ctrl_get_value(
|
|
|
|
pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT),
|
|
|
|
&cur_input);
|
|
|
|
if (cur_input == PVR2_CVAL_INPUT_RADIO) {
|
|
|
|
vf->type = V4L2_TUNER_RADIO;
|
|
|
|
} else {
|
|
|
|
vf->type = V4L2_TUNER_ANALOG_TV;
|
|
|
|
}
|
2007-01-20 11:09:47 +08:00
|
|
|
if (vt.capability & V4L2_TUNER_CAP_LOW) {
|
|
|
|
val = (val * 2) / 125;
|
|
|
|
} else {
|
|
|
|
val /= 62500;
|
|
|
|
}
|
|
|
|
vf->frequency = val;
|
2006-06-27 07:58:46 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case VIDIOC_ENUM_FMT:
|
|
|
|
{
|
|
|
|
struct v4l2_fmtdesc *fd = (struct v4l2_fmtdesc *)arg;
|
|
|
|
|
|
|
|
/* Only one format is supported : mpeg.*/
|
|
|
|
if (fd->index != 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
memcpy(fd, pvr_fmtdesc, sizeof(struct v4l2_fmtdesc));
|
|
|
|
ret = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case VIDIOC_G_FMT:
|
|
|
|
{
|
|
|
|
struct v4l2_format *vf = (struct v4l2_format *)arg;
|
|
|
|
int val;
|
|
|
|
switch(vf->type) {
|
|
|
|
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
|
|
|
|
memcpy(vf, &pvr_format[PVR_FORMAT_PIX],
|
|
|
|
sizeof(struct v4l2_format));
|
|
|
|
val = 0;
|
|
|
|
pvr2_ctrl_get_value(
|
|
|
|
pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_HRES),
|
|
|
|
&val);
|
|
|
|
vf->fmt.pix.width = val;
|
|
|
|
val = 0;
|
|
|
|
pvr2_ctrl_get_value(
|
|
|
|
pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_VRES),
|
|
|
|
&val);
|
|
|
|
vf->fmt.pix.height = val;
|
|
|
|
ret = 0;
|
|
|
|
break;
|
|
|
|
case V4L2_BUF_TYPE_VBI_CAPTURE:
|
|
|
|
// ????? Still need to figure out to do VBI correctly
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case VIDIOC_TRY_FMT:
|
|
|
|
case VIDIOC_S_FMT:
|
|
|
|
{
|
|
|
|
struct v4l2_format *vf = (struct v4l2_format *)arg;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
switch(vf->type) {
|
|
|
|
case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
|
2007-01-20 12:59:54 +08:00
|
|
|
int lmin,lmax,ldef;
|
2006-08-08 20:10:07 +08:00
|
|
|
struct pvr2_ctrl *hcp,*vcp;
|
2006-06-27 07:58:46 +08:00
|
|
|
int h = vf->fmt.pix.height;
|
|
|
|
int w = vf->fmt.pix.width;
|
2006-08-08 20:10:07 +08:00
|
|
|
hcp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_HRES);
|
|
|
|
vcp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_VRES);
|
|
|
|
|
|
|
|
lmin = pvr2_ctrl_get_min(hcp);
|
|
|
|
lmax = pvr2_ctrl_get_max(hcp);
|
2008-09-01 07:55:03 +08:00
|
|
|
pvr2_ctrl_get_def(hcp, &ldef);
|
2007-01-20 12:59:54 +08:00
|
|
|
if (w == -1) {
|
|
|
|
w = ldef;
|
|
|
|
} else if (w < lmin) {
|
2006-08-08 20:10:07 +08:00
|
|
|
w = lmin;
|
|
|
|
} else if (w > lmax) {
|
|
|
|
w = lmax;
|
2006-06-27 07:58:46 +08:00
|
|
|
}
|
2006-09-02 05:36:10 +08:00
|
|
|
lmin = pvr2_ctrl_get_min(vcp);
|
|
|
|
lmax = pvr2_ctrl_get_max(vcp);
|
2008-09-01 07:55:03 +08:00
|
|
|
pvr2_ctrl_get_def(vcp, &ldef);
|
2007-01-20 12:59:54 +08:00
|
|
|
if (h == -1) {
|
|
|
|
h = ldef;
|
|
|
|
} else if (h < lmin) {
|
2006-09-02 05:36:10 +08:00
|
|
|
h = lmin;
|
|
|
|
} else if (h > lmax) {
|
|
|
|
h = lmax;
|
|
|
|
}
|
2006-06-27 07:58:46 +08:00
|
|
|
|
|
|
|
memcpy(vf, &pvr_format[PVR_FORMAT_PIX],
|
|
|
|
sizeof(struct v4l2_format));
|
2006-06-26 07:04:16 +08:00
|
|
|
vf->fmt.pix.width = w;
|
|
|
|
vf->fmt.pix.height = h;
|
2006-06-27 07:58:46 +08:00
|
|
|
|
|
|
|
if (cmd == VIDIOC_S_FMT) {
|
2006-08-08 20:10:07 +08:00
|
|
|
pvr2_ctrl_set_value(hcp,vf->fmt.pix.width);
|
|
|
|
pvr2_ctrl_set_value(vcp,vf->fmt.pix.height);
|
2006-06-27 07:58:46 +08:00
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case V4L2_BUF_TYPE_VBI_CAPTURE:
|
|
|
|
// ????? Still need to figure out to do VBI correctly
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case VIDIOC_STREAMON:
|
|
|
|
{
|
2010-04-06 03:05:39 +08:00
|
|
|
if (!fh->pdi->stream) {
|
2006-12-31 05:27:32 +08:00
|
|
|
/* No stream defined for this node. This means
|
|
|
|
that we're not currently allowed to stream from
|
|
|
|
this node. */
|
|
|
|
ret = -EPERM;
|
|
|
|
break;
|
|
|
|
}
|
2010-04-06 03:05:39 +08:00
|
|
|
ret = pvr2_hdw_set_stream_type(hdw,pdi->config);
|
2006-06-27 07:58:46 +08:00
|
|
|
if (ret < 0) return ret;
|
|
|
|
ret = pvr2_hdw_set_streaming(hdw,!0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case VIDIOC_STREAMOFF:
|
|
|
|
{
|
2010-04-06 03:05:39 +08:00
|
|
|
if (!fh->pdi->stream) {
|
2006-12-31 05:27:32 +08:00
|
|
|
/* No stream defined for this node. This means
|
|
|
|
that we're not currently allowed to stream from
|
|
|
|
this node. */
|
|
|
|
ret = -EPERM;
|
|
|
|
break;
|
|
|
|
}
|
2006-06-27 07:58:46 +08:00
|
|
|
ret = pvr2_hdw_set_streaming(hdw,0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case VIDIOC_QUERYCTRL:
|
|
|
|
{
|
|
|
|
struct pvr2_ctrl *cptr;
|
2008-09-01 07:55:03 +08:00
|
|
|
int val;
|
2006-06-27 07:58:46 +08:00
|
|
|
struct v4l2_queryctrl *vc = (struct v4l2_queryctrl *)arg;
|
|
|
|
ret = 0;
|
2006-06-26 07:04:58 +08:00
|
|
|
if (vc->id & V4L2_CTRL_FLAG_NEXT_CTRL) {
|
|
|
|
cptr = pvr2_hdw_get_ctrl_nextv4l(
|
|
|
|
hdw,(vc->id & ~V4L2_CTRL_FLAG_NEXT_CTRL));
|
|
|
|
if (cptr) vc->id = pvr2_ctrl_get_v4lid(cptr);
|
|
|
|
} else {
|
|
|
|
cptr = pvr2_hdw_get_ctrl_v4l(hdw,vc->id);
|
|
|
|
}
|
2006-06-27 07:58:46 +08:00
|
|
|
if (!cptr) {
|
2006-06-26 08:30:47 +08:00
|
|
|
pvr2_trace(PVR2_TRACE_V4LIOCTL,
|
2006-06-26 07:04:44 +08:00
|
|
|
"QUERYCTRL id=0x%x not implemented here",
|
|
|
|
vc->id);
|
2006-06-27 07:58:46 +08:00
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2006-06-26 07:04:44 +08:00
|
|
|
pvr2_trace(PVR2_TRACE_V4LIOCTL,
|
|
|
|
"QUERYCTRL id=0x%x mapping name=%s (%s)",
|
|
|
|
vc->id,pvr2_ctrl_get_name(cptr),
|
|
|
|
pvr2_ctrl_get_desc(cptr));
|
|
|
|
strlcpy(vc->name,pvr2_ctrl_get_desc(cptr),sizeof(vc->name));
|
|
|
|
vc->flags = pvr2_ctrl_get_v4lflags(cptr);
|
2008-09-01 07:55:03 +08:00
|
|
|
pvr2_ctrl_get_def(cptr, &val);
|
|
|
|
vc->default_value = val;
|
2006-06-27 07:58:46 +08:00
|
|
|
switch (pvr2_ctrl_get_type(cptr)) {
|
|
|
|
case pvr2_ctl_enum:
|
|
|
|
vc->type = V4L2_CTRL_TYPE_MENU;
|
|
|
|
vc->minimum = 0;
|
|
|
|
vc->maximum = pvr2_ctrl_get_cnt(cptr) - 1;
|
|
|
|
vc->step = 1;
|
|
|
|
break;
|
2006-06-26 07:04:40 +08:00
|
|
|
case pvr2_ctl_bool:
|
2006-06-26 07:04:58 +08:00
|
|
|
vc->type = V4L2_CTRL_TYPE_BOOLEAN;
|
2006-06-26 07:04:40 +08:00
|
|
|
vc->minimum = 0;
|
|
|
|
vc->maximum = 1;
|
|
|
|
vc->step = 1;
|
|
|
|
break;
|
2006-06-27 07:58:46 +08:00
|
|
|
case pvr2_ctl_int:
|
|
|
|
vc->type = V4L2_CTRL_TYPE_INTEGER;
|
|
|
|
vc->minimum = pvr2_ctrl_get_min(cptr);
|
|
|
|
vc->maximum = pvr2_ctrl_get_max(cptr);
|
|
|
|
vc->step = 1;
|
|
|
|
break;
|
|
|
|
default:
|
2006-06-26 08:30:47 +08:00
|
|
|
pvr2_trace(PVR2_TRACE_V4LIOCTL,
|
2006-06-26 07:04:44 +08:00
|
|
|
"QUERYCTRL id=0x%x name=%s not mappable",
|
|
|
|
vc->id,pvr2_ctrl_get_name(cptr));
|
2006-06-27 07:58:46 +08:00
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case VIDIOC_QUERYMENU:
|
|
|
|
{
|
|
|
|
struct v4l2_querymenu *vm = (struct v4l2_querymenu *)arg;
|
|
|
|
unsigned int cnt = 0;
|
|
|
|
ret = pvr2_ctrl_get_valname(pvr2_hdw_get_ctrl_v4l(hdw,vm->id),
|
|
|
|
vm->index,
|
|
|
|
vm->name,sizeof(vm->name)-1,
|
|
|
|
&cnt);
|
|
|
|
vm->name[cnt] = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case VIDIOC_G_CTRL:
|
|
|
|
{
|
|
|
|
struct v4l2_control *vc = (struct v4l2_control *)arg;
|
|
|
|
int val = 0;
|
|
|
|
ret = pvr2_ctrl_get_value(pvr2_hdw_get_ctrl_v4l(hdw,vc->id),
|
|
|
|
&val);
|
|
|
|
vc->value = val;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case VIDIOC_S_CTRL:
|
|
|
|
{
|
|
|
|
struct v4l2_control *vc = (struct v4l2_control *)arg;
|
|
|
|
ret = pvr2_ctrl_set_value(pvr2_hdw_get_ctrl_v4l(hdw,vc->id),
|
|
|
|
vc->value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2006-06-26 07:04:58 +08:00
|
|
|
case VIDIOC_G_EXT_CTRLS:
|
|
|
|
{
|
|
|
|
struct v4l2_ext_controls *ctls =
|
|
|
|
(struct v4l2_ext_controls *)arg;
|
|
|
|
struct v4l2_ext_control *ctrl;
|
|
|
|
unsigned int idx;
|
|
|
|
int val;
|
2007-01-20 11:30:23 +08:00
|
|
|
ret = 0;
|
2006-06-26 07:04:58 +08:00
|
|
|
for (idx = 0; idx < ctls->count; idx++) {
|
|
|
|
ctrl = ctls->controls + idx;
|
|
|
|
ret = pvr2_ctrl_get_value(
|
|
|
|
pvr2_hdw_get_ctrl_v4l(hdw,ctrl->id),&val);
|
|
|
|
if (ret) {
|
|
|
|
ctls->error_idx = idx;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* Ensure that if read as a 64 bit value, the user
|
|
|
|
will still get a hopefully sane value */
|
|
|
|
ctrl->value64 = 0;
|
|
|
|
ctrl->value = val;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case VIDIOC_S_EXT_CTRLS:
|
|
|
|
{
|
|
|
|
struct v4l2_ext_controls *ctls =
|
|
|
|
(struct v4l2_ext_controls *)arg;
|
|
|
|
struct v4l2_ext_control *ctrl;
|
|
|
|
unsigned int idx;
|
2007-01-20 11:30:23 +08:00
|
|
|
ret = 0;
|
2006-06-26 07:04:58 +08:00
|
|
|
for (idx = 0; idx < ctls->count; idx++) {
|
|
|
|
ctrl = ctls->controls + idx;
|
|
|
|
ret = pvr2_ctrl_set_value(
|
|
|
|
pvr2_hdw_get_ctrl_v4l(hdw,ctrl->id),
|
|
|
|
ctrl->value);
|
|
|
|
if (ret) {
|
|
|
|
ctls->error_idx = idx;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case VIDIOC_TRY_EXT_CTRLS:
|
|
|
|
{
|
|
|
|
struct v4l2_ext_controls *ctls =
|
|
|
|
(struct v4l2_ext_controls *)arg;
|
|
|
|
struct v4l2_ext_control *ctrl;
|
|
|
|
struct pvr2_ctrl *pctl;
|
|
|
|
unsigned int idx;
|
|
|
|
/* For the moment just validate that the requested control
|
|
|
|
actually exists. */
|
2007-01-20 11:30:23 +08:00
|
|
|
ret = 0;
|
2006-06-26 07:04:58 +08:00
|
|
|
for (idx = 0; idx < ctls->count; idx++) {
|
|
|
|
ctrl = ctls->controls + idx;
|
|
|
|
pctl = pvr2_hdw_get_ctrl_v4l(hdw,ctrl->id);
|
|
|
|
if (!pctl) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
ctls->error_idx = idx;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-09-01 08:02:20 +08:00
|
|
|
case VIDIOC_CROPCAP:
|
|
|
|
{
|
|
|
|
struct v4l2_cropcap *cap = (struct v4l2_cropcap *)arg;
|
|
|
|
if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ret = pvr2_hdw_get_cropcap(hdw, cap);
|
|
|
|
cap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* paranoia */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case VIDIOC_G_CROP:
|
|
|
|
{
|
|
|
|
struct v4l2_crop *crop = (struct v4l2_crop *)arg;
|
|
|
|
int val = 0;
|
|
|
|
if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ret = pvr2_ctrl_get_value(
|
|
|
|
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), &val);
|
|
|
|
if (ret != 0) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
crop->c.left = val;
|
|
|
|
ret = pvr2_ctrl_get_value(
|
|
|
|
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), &val);
|
|
|
|
if (ret != 0) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
crop->c.top = val;
|
|
|
|
ret = pvr2_ctrl_get_value(
|
|
|
|
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), &val);
|
|
|
|
if (ret != 0) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
crop->c.width = val;
|
|
|
|
ret = pvr2_ctrl_get_value(
|
|
|
|
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), &val);
|
|
|
|
if (ret != 0) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
crop->c.height = val;
|
|
|
|
}
|
|
|
|
case VIDIOC_S_CROP:
|
|
|
|
{
|
|
|
|
struct v4l2_crop *crop = (struct v4l2_crop *)arg;
|
|
|
|
struct v4l2_cropcap cap;
|
|
|
|
if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
ret = pvr2_ctrl_set_value(
|
|
|
|
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL),
|
|
|
|
crop->c.left);
|
|
|
|
if (ret != 0) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ret = pvr2_ctrl_set_value(
|
|
|
|
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT),
|
|
|
|
crop->c.top);
|
|
|
|
if (ret != 0) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ret = pvr2_ctrl_set_value(
|
|
|
|
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW),
|
|
|
|
crop->c.width);
|
|
|
|
if (ret != 0) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ret = pvr2_ctrl_set_value(
|
|
|
|
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH),
|
|
|
|
crop->c.height);
|
|
|
|
if (ret != 0) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2006-06-27 07:58:46 +08:00
|
|
|
case VIDIOC_LOG_STATUS:
|
|
|
|
{
|
|
|
|
pvr2_hdw_trigger_module_log(hdw);
|
|
|
|
ret = 0;
|
|
|
|
break;
|
|
|
|
}
|
2006-09-24 09:26:52 +08:00
|
|
|
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
2007-01-24 09:38:13 +08:00
|
|
|
case VIDIOC_DBG_S_REGISTER:
|
|
|
|
case VIDIOC_DBG_G_REGISTER:
|
2006-09-24 09:26:52 +08:00
|
|
|
{
|
2007-02-24 07:55:14 +08:00
|
|
|
u64 val;
|
2008-12-30 18:14:19 +08:00
|
|
|
struct v4l2_dbg_register *req = (struct v4l2_dbg_register *)arg;
|
2007-01-24 09:38:13 +08:00
|
|
|
if (cmd == VIDIOC_DBG_S_REGISTER) val = req->val;
|
2006-09-24 09:26:52 +08:00
|
|
|
ret = pvr2_hdw_register_access(
|
2008-12-30 18:14:19 +08:00
|
|
|
hdw, &req->match, req->reg,
|
|
|
|
cmd == VIDIOC_DBG_S_REGISTER, &val);
|
2007-01-24 09:38:13 +08:00
|
|
|
if (cmd == VIDIOC_DBG_G_REGISTER) req->val = val;
|
2006-09-24 09:26:52 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
2006-06-27 07:58:46 +08:00
|
|
|
|
|
|
|
default :
|
2008-10-21 22:27:20 +08:00
|
|
|
ret = v4l_compat_translate_ioctl(file, cmd,
|
2008-11-01 19:25:11 +08:00
|
|
|
arg, pvr2_v4l2_do_ioctl);
|
2006-06-27 07:58:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pvr2_hdw_commit_ctl(hdw);
|
|
|
|
|
|
|
|
if (ret < 0) {
|
|
|
|
if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) {
|
2006-06-26 08:30:47 +08:00
|
|
|
pvr2_trace(PVR2_TRACE_V4LIOCTL,
|
2008-12-30 18:04:34 +08:00
|
|
|
"pvr2_v4l2_do_ioctl failure, ret=%ld", ret);
|
2006-06-27 07:58:46 +08:00
|
|
|
} else {
|
2006-06-26 08:30:47 +08:00
|
|
|
if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) {
|
|
|
|
pvr2_trace(PVR2_TRACE_V4LIOCTL,
|
2008-12-30 18:04:34 +08:00
|
|
|
"pvr2_v4l2_do_ioctl failure, ret=%ld"
|
|
|
|
" command was:", ret);
|
2006-06-27 07:58:46 +08:00
|
|
|
v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw),
|
|
|
|
cmd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
pvr2_trace(PVR2_TRACE_V4LIOCTL,
|
2008-12-30 18:04:34 +08:00
|
|
|
"pvr2_v4l2_do_ioctl complete, ret=%ld (0x%lx)",
|
|
|
|
ret, ret);
|
2006-06-27 07:58:46 +08:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip)
|
|
|
|
{
|
2006-12-28 10:19:42 +08:00
|
|
|
struct pvr2_hdw *hdw = dip->v4lp->channel.mc_head->hdw;
|
2006-12-31 05:27:32 +08:00
|
|
|
enum pvr2_config cfg = dip->config;
|
2006-12-28 10:19:42 +08:00
|
|
|
|
2006-12-31 05:27:32 +08:00
|
|
|
pvr2_hdw_v4l_store_minor_number(hdw,dip->minor_type,-1);
|
2006-09-24 09:30:50 +08:00
|
|
|
|
|
|
|
/* Paranoia */
|
2006-10-29 22:12:27 +08:00
|
|
|
dip->v4lp = NULL;
|
|
|
|
dip->stream = NULL;
|
2006-09-24 09:30:50 +08:00
|
|
|
|
|
|
|
/* Actual deallocation happens later when all internal references
|
|
|
|
are gone. */
|
|
|
|
video_unregister_device(&dip->devbase);
|
2006-12-28 10:19:42 +08:00
|
|
|
|
2009-11-28 00:57:15 +08:00
|
|
|
printk(KERN_INFO "pvrusb2: unregistered device %s [%s]\n",
|
|
|
|
video_device_node_name(&dip->devbase),
|
2006-12-31 05:27:32 +08:00
|
|
|
pvr2_config_get_name(cfg));
|
2006-12-28 10:19:42 +08:00
|
|
|
|
2006-06-27 07:58:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-12 11:13:28 +08:00
|
|
|
static void pvr2_v4l2_dev_disassociate_parent(struct pvr2_v4l2_dev *dip)
|
|
|
|
{
|
|
|
|
if (!dip) return;
|
|
|
|
if (!dip->devbase.parent) return;
|
|
|
|
dip->devbase.parent = NULL;
|
|
|
|
device_move(&dip->devbase.dev, NULL, DPM_ORDER_NONE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-06-27 07:58:46 +08:00
|
|
|
static void pvr2_v4l2_destroy_no_lock(struct pvr2_v4l2 *vp)
|
|
|
|
{
|
2006-12-28 10:19:42 +08:00
|
|
|
if (vp->dev_video) {
|
|
|
|
pvr2_v4l2_dev_destroy(vp->dev_video);
|
2007-03-14 17:17:59 +08:00
|
|
|
vp->dev_video = NULL;
|
2006-12-28 10:19:42 +08:00
|
|
|
}
|
|
|
|
if (vp->dev_radio) {
|
|
|
|
pvr2_v4l2_dev_destroy(vp->dev_radio);
|
2007-03-14 17:17:59 +08:00
|
|
|
vp->dev_radio = NULL;
|
2006-12-28 10:19:42 +08:00
|
|
|
}
|
2006-06-27 07:58:46 +08:00
|
|
|
|
|
|
|
pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_v4l2 id=%p",vp);
|
|
|
|
pvr2_channel_done(&vp->channel);
|
|
|
|
kfree(vp);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-09-24 09:30:50 +08:00
|
|
|
static void pvr2_video_device_release(struct video_device *vdev)
|
|
|
|
{
|
|
|
|
struct pvr2_v4l2_dev *dev;
|
|
|
|
dev = container_of(vdev,struct pvr2_v4l2_dev,devbase);
|
|
|
|
kfree(dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-06-30 22:30:20 +08:00
|
|
|
static void pvr2_v4l2_internal_check(struct pvr2_channel *chp)
|
2006-06-27 07:58:46 +08:00
|
|
|
{
|
|
|
|
struct pvr2_v4l2 *vp;
|
|
|
|
vp = container_of(chp,struct pvr2_v4l2,channel);
|
|
|
|
if (!vp->channel.mc_head->disconnect_flag) return;
|
2009-10-12 11:13:28 +08:00
|
|
|
pvr2_v4l2_dev_disassociate_parent(vp->dev_video);
|
|
|
|
pvr2_v4l2_dev_disassociate_parent(vp->dev_radio);
|
2006-06-27 07:58:46 +08:00
|
|
|
if (vp->vfirst) return;
|
|
|
|
pvr2_v4l2_destroy_no_lock(vp);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-12-30 18:04:34 +08:00
|
|
|
static long pvr2_v4l2_ioctl(struct file *file,
|
2006-06-30 22:30:20 +08:00
|
|
|
unsigned int cmd, unsigned long arg)
|
2006-06-27 07:58:46 +08:00
|
|
|
{
|
|
|
|
|
2008-11-01 19:25:11 +08:00
|
|
|
return video_usercopy(file, cmd, arg, pvr2_v4l2_do_ioctl);
|
2006-06-27 07:58:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-12-30 17:58:20 +08:00
|
|
|
static int pvr2_v4l2_release(struct file *file)
|
2006-06-27 07:58:46 +08:00
|
|
|
{
|
|
|
|
struct pvr2_v4l2_fh *fhp = file->private_data;
|
|
|
|
struct pvr2_v4l2 *vp = fhp->vhead;
|
2006-12-31 05:31:22 +08:00
|
|
|
struct pvr2_hdw *hdw = fhp->channel.mc_head->hdw;
|
2006-06-27 07:58:46 +08:00
|
|
|
|
|
|
|
pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_release");
|
|
|
|
|
|
|
|
if (fhp->rhp) {
|
|
|
|
struct pvr2_stream *sp;
|
|
|
|
pvr2_hdw_set_streaming(hdw,0);
|
|
|
|
sp = pvr2_ioread_get_stream(fhp->rhp);
|
2006-06-30 22:35:28 +08:00
|
|
|
if (sp) pvr2_stream_set_callback(sp,NULL,NULL);
|
2006-06-27 07:58:46 +08:00
|
|
|
pvr2_ioread_destroy(fhp->rhp);
|
2006-06-30 22:35:28 +08:00
|
|
|
fhp->rhp = NULL;
|
2006-06-27 07:58:46 +08:00
|
|
|
}
|
V4L/DVB (5039): Pvrusb2: Implement /dev/radioX
The "main" V4L2 interface patch. This is yet very incomplete, incorrect and
probably inappropriate for inclusion as-is, but at least with this I 'm able
to tune and play radio through a V4L2 program (pvr-radio.c, a "thumb" version
of ivtv-radio.c with just the essentials).
Therefore, it kinda gives an idea of what is needed to support this, hm,
interface (partly used also by e.g., kradio). Please point out any mistakes
on this code. I 'm sure I 'm messing up some struct initialization somewhere
but currently I 'm too lazy to actually think this through until I complete
the functionality (e.g., handle the VIDIOC_S_STD, ENUMINPUT, etc ioctls
appropriately).
Signed-off-by: Pantelis Koukousoulas <pakt223@freemail.gr>
Signed-off-by: Mike Isely <isely@pobox.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2006-12-28 10:09:55 +08:00
|
|
|
|
2010-05-01 19:03:24 +08:00
|
|
|
v4l2_prio_close(&vp->prio, fhp->prio);
|
2006-06-27 07:58:46 +08:00
|
|
|
file->private_data = NULL;
|
|
|
|
|
2008-04-23 01:45:45 +08:00
|
|
|
if (fhp->vnext) {
|
|
|
|
fhp->vnext->vprev = fhp->vprev;
|
|
|
|
} else {
|
|
|
|
vp->vlast = fhp->vprev;
|
|
|
|
}
|
|
|
|
if (fhp->vprev) {
|
|
|
|
fhp->vprev->vnext = fhp->vnext;
|
|
|
|
} else {
|
|
|
|
vp->vfirst = fhp->vnext;
|
|
|
|
}
|
|
|
|
fhp->vnext = NULL;
|
|
|
|
fhp->vprev = NULL;
|
|
|
|
fhp->vhead = NULL;
|
|
|
|
pvr2_channel_done(&fhp->channel);
|
|
|
|
pvr2_trace(PVR2_TRACE_STRUCT,
|
|
|
|
"Destroying pvr_v4l2_fh id=%p",fhp);
|
2008-04-21 14:52:34 +08:00
|
|
|
if (fhp->input_map) {
|
|
|
|
kfree(fhp->input_map);
|
|
|
|
fhp->input_map = NULL;
|
|
|
|
}
|
2008-04-23 01:45:45 +08:00
|
|
|
kfree(fhp);
|
|
|
|
if (vp->channel.mc_head->disconnect_flag && !vp->vfirst) {
|
|
|
|
pvr2_v4l2_destroy_no_lock(vp);
|
|
|
|
}
|
2006-06-27 07:58:46 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-12-30 17:58:20 +08:00
|
|
|
static int pvr2_v4l2_open(struct file *file)
|
2006-06-27 07:58:46 +08:00
|
|
|
{
|
2006-09-24 09:30:50 +08:00
|
|
|
struct pvr2_v4l2_dev *dip; /* Our own context pointer */
|
2006-06-27 07:58:46 +08:00
|
|
|
struct pvr2_v4l2_fh *fhp;
|
|
|
|
struct pvr2_v4l2 *vp;
|
|
|
|
struct pvr2_hdw *hdw;
|
2008-04-21 14:47:43 +08:00
|
|
|
unsigned int input_mask = 0;
|
2008-04-21 14:52:34 +08:00
|
|
|
unsigned int input_cnt,idx;
|
2008-04-21 14:47:43 +08:00
|
|
|
int ret = 0;
|
2006-06-27 07:58:46 +08:00
|
|
|
|
2006-09-24 09:30:50 +08:00
|
|
|
dip = container_of(video_devdata(file),struct pvr2_v4l2_dev,devbase);
|
2006-06-27 07:58:46 +08:00
|
|
|
|
|
|
|
vp = dip->v4lp;
|
|
|
|
hdw = vp->channel.hdw;
|
|
|
|
|
|
|
|
pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_open");
|
|
|
|
|
|
|
|
if (!pvr2_hdw_dev_ok(hdw)) {
|
|
|
|
pvr2_trace(PVR2_TRACE_OPEN_CLOSE,
|
|
|
|
"pvr2_v4l2_open: hardware not ready");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
2007-01-20 11:03:32 +08:00
|
|
|
fhp = kzalloc(sizeof(*fhp),GFP_KERNEL);
|
2006-06-27 07:58:46 +08:00
|
|
|
if (!fhp) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
init_waitqueue_head(&fhp->wait_data);
|
2010-04-06 03:05:39 +08:00
|
|
|
fhp->pdi = dip;
|
2006-06-27 07:58:46 +08:00
|
|
|
|
2008-04-23 01:45:45 +08:00
|
|
|
pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp);
|
|
|
|
pvr2_channel_init(&fhp->channel,vp->channel.mc_head);
|
V4L/DVB (5039): Pvrusb2: Implement /dev/radioX
The "main" V4L2 interface patch. This is yet very incomplete, incorrect and
probably inappropriate for inclusion as-is, but at least with this I 'm able
to tune and play radio through a V4L2 program (pvr-radio.c, a "thumb" version
of ivtv-radio.c with just the essentials).
Therefore, it kinda gives an idea of what is needed to support this, hm,
interface (partly used also by e.g., kradio). Please point out any mistakes
on this code. I 'm sure I 'm messing up some struct initialization somewhere
but currently I 'm too lazy to actually think this through until I complete
the functionality (e.g., handle the VIDIOC_S_STD, ENUMINPUT, etc ioctls
appropriately).
Signed-off-by: Pantelis Koukousoulas <pakt223@freemail.gr>
Signed-off-by: Mike Isely <isely@pobox.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2006-12-28 10:09:55 +08:00
|
|
|
|
2008-04-21 14:47:43 +08:00
|
|
|
if (dip->v4l_type == VFL_TYPE_RADIO) {
|
|
|
|
/* Opening device as a radio, legal input selection subset
|
|
|
|
is just the radio. */
|
|
|
|
input_mask = (1 << PVR2_CVAL_INPUT_RADIO);
|
|
|
|
} else {
|
|
|
|
/* Opening the main V4L device, legal input selection
|
|
|
|
subset includes all analog inputs. */
|
|
|
|
input_mask = ((1 << PVR2_CVAL_INPUT_RADIO) |
|
|
|
|
(1 << PVR2_CVAL_INPUT_TV) |
|
|
|
|
(1 << PVR2_CVAL_INPUT_COMPOSITE) |
|
|
|
|
(1 << PVR2_CVAL_INPUT_SVIDEO));
|
|
|
|
}
|
|
|
|
ret = pvr2_channel_limit_inputs(&fhp->channel,input_mask);
|
|
|
|
if (ret) {
|
|
|
|
pvr2_channel_done(&fhp->channel);
|
|
|
|
pvr2_trace(PVR2_TRACE_STRUCT,
|
|
|
|
"Destroying pvr_v4l2_fh id=%p (input mask error)",
|
|
|
|
fhp);
|
|
|
|
|
|
|
|
kfree(fhp);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-04-21 14:52:34 +08:00
|
|
|
input_mask &= pvr2_hdw_get_input_available(hdw);
|
|
|
|
input_cnt = 0;
|
|
|
|
for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) {
|
|
|
|
if (input_mask & (1 << idx)) input_cnt++;
|
|
|
|
}
|
|
|
|
fhp->input_cnt = input_cnt;
|
|
|
|
fhp->input_map = kzalloc(input_cnt,GFP_KERNEL);
|
|
|
|
if (!fhp->input_map) {
|
|
|
|
pvr2_channel_done(&fhp->channel);
|
|
|
|
pvr2_trace(PVR2_TRACE_STRUCT,
|
|
|
|
"Destroying pvr_v4l2_fh id=%p (input map failure)",
|
|
|
|
fhp);
|
|
|
|
kfree(fhp);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
input_cnt = 0;
|
|
|
|
for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) {
|
|
|
|
if (!(input_mask & (1 << idx))) continue;
|
|
|
|
fhp->input_map[input_cnt++] = idx;
|
|
|
|
}
|
|
|
|
|
2008-04-23 01:45:45 +08:00
|
|
|
fhp->vnext = NULL;
|
|
|
|
fhp->vprev = vp->vlast;
|
|
|
|
if (vp->vlast) {
|
|
|
|
vp->vlast->vnext = fhp;
|
|
|
|
} else {
|
|
|
|
vp->vfirst = fhp;
|
|
|
|
}
|
|
|
|
vp->vlast = fhp;
|
|
|
|
fhp->vhead = vp;
|
|
|
|
|
2006-06-27 07:58:46 +08:00
|
|
|
fhp->file = file;
|
|
|
|
file->private_data = fhp;
|
2010-05-01 19:03:24 +08:00
|
|
|
v4l2_prio_open(&vp->prio, &fhp->prio);
|
2006-06-27 07:58:46 +08:00
|
|
|
|
|
|
|
fhp->fw_mode_flag = pvr2_hdw_cpufw_get_enabled(hdw);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void pvr2_v4l2_notify(struct pvr2_v4l2_fh *fhp)
|
|
|
|
{
|
|
|
|
wake_up(&fhp->wait_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pvr2_v4l2_iosetup(struct pvr2_v4l2_fh *fh)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct pvr2_stream *sp;
|
|
|
|
struct pvr2_hdw *hdw;
|
|
|
|
if (fh->rhp) return 0;
|
|
|
|
|
2010-04-06 03:05:39 +08:00
|
|
|
if (!fh->pdi->stream) {
|
2006-12-31 05:27:32 +08:00
|
|
|
/* No stream defined for this node. This means that we're
|
|
|
|
not currently allowed to stream from this node. */
|
|
|
|
return -EPERM;
|
|
|
|
}
|
|
|
|
|
2006-06-27 07:58:46 +08:00
|
|
|
/* First read() attempt. Try to claim the stream and start
|
|
|
|
it... */
|
|
|
|
if ((ret = pvr2_channel_claim_stream(&fh->channel,
|
2010-04-06 03:05:39 +08:00
|
|
|
fh->pdi->stream)) != 0) {
|
2006-06-27 07:58:46 +08:00
|
|
|
/* Someone else must already have it */
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-04-06 03:05:39 +08:00
|
|
|
fh->rhp = pvr2_channel_create_mpeg_stream(fh->pdi->stream);
|
2006-06-27 07:58:46 +08:00
|
|
|
if (!fh->rhp) {
|
2006-06-30 22:35:28 +08:00
|
|
|
pvr2_channel_claim_stream(&fh->channel,NULL);
|
2006-06-27 07:58:46 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
hdw = fh->channel.mc_head->hdw;
|
2010-04-06 03:05:39 +08:00
|
|
|
sp = fh->pdi->stream->stream;
|
2006-06-27 07:58:46 +08:00
|
|
|
pvr2_stream_set_callback(sp,(pvr2_stream_callback)pvr2_v4l2_notify,fh);
|
2010-04-06 03:05:39 +08:00
|
|
|
pvr2_hdw_set_stream_type(hdw,fh->pdi->config);
|
2007-11-26 12:48:52 +08:00
|
|
|
if ((ret = pvr2_hdw_set_streaming(hdw,!0)) < 0) return ret;
|
|
|
|
return pvr2_ioread_set_enabled(fh->rhp,!0);
|
2006-06-27 07:58:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static ssize_t pvr2_v4l2_read(struct file *file,
|
|
|
|
char __user *buff, size_t count, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct pvr2_v4l2_fh *fh = file->private_data;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (fh->fw_mode_flag) {
|
|
|
|
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
|
|
|
|
char *tbuf;
|
|
|
|
int c1,c2;
|
|
|
|
int tcnt = 0;
|
|
|
|
unsigned int offs = *ppos;
|
|
|
|
|
|
|
|
tbuf = kmalloc(PAGE_SIZE,GFP_KERNEL);
|
|
|
|
if (!tbuf) return -ENOMEM;
|
|
|
|
|
|
|
|
while (count) {
|
|
|
|
c1 = count;
|
|
|
|
if (c1 > PAGE_SIZE) c1 = PAGE_SIZE;
|
|
|
|
c2 = pvr2_hdw_cpufw_get(hdw,offs,tbuf,c1);
|
|
|
|
if (c2 < 0) {
|
|
|
|
tcnt = c2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!c2) break;
|
|
|
|
if (copy_to_user(buff,tbuf,c2)) {
|
|
|
|
tcnt = -EFAULT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
offs += c2;
|
|
|
|
tcnt += c2;
|
|
|
|
buff += c2;
|
|
|
|
count -= c2;
|
|
|
|
*ppos += c2;
|
|
|
|
}
|
|
|
|
kfree(tbuf);
|
|
|
|
return tcnt;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!fh->rhp) {
|
|
|
|
ret = pvr2_v4l2_iosetup(fh);
|
|
|
|
if (ret) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
ret = pvr2_ioread_read(fh->rhp,buff,count);
|
|
|
|
if (ret >= 0) break;
|
|
|
|
if (ret != -EAGAIN) break;
|
|
|
|
if (file->f_flags & O_NONBLOCK) break;
|
|
|
|
/* Doing blocking I/O. Wait here. */
|
|
|
|
ret = wait_event_interruptible(
|
|
|
|
fh->wait_data,
|
|
|
|
pvr2_ioread_avail(fh->rhp) >= 0);
|
|
|
|
if (ret < 0) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static unsigned int pvr2_v4l2_poll(struct file *file, poll_table *wait)
|
|
|
|
{
|
|
|
|
unsigned int mask = 0;
|
|
|
|
struct pvr2_v4l2_fh *fh = file->private_data;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (fh->fw_mode_flag) {
|
|
|
|
mask |= POLLIN | POLLRDNORM;
|
|
|
|
return mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!fh->rhp) {
|
|
|
|
ret = pvr2_v4l2_iosetup(fh);
|
|
|
|
if (ret) return POLLERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
poll_wait(file,&fh->wait_data,wait);
|
|
|
|
|
|
|
|
if (pvr2_ioread_avail(fh->rhp) >= 0) {
|
|
|
|
mask |= POLLIN | POLLRDNORM;
|
|
|
|
}
|
|
|
|
|
|
|
|
return mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-12-30 17:58:20 +08:00
|
|
|
static const struct v4l2_file_operations vdev_fops = {
|
2006-06-27 07:58:46 +08:00
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.open = pvr2_v4l2_open,
|
|
|
|
.release = pvr2_v4l2_release,
|
|
|
|
.read = pvr2_v4l2_read,
|
|
|
|
.ioctl = pvr2_v4l2_ioctl,
|
|
|
|
.poll = pvr2_v4l2_poll,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static struct video_device vdev_template = {
|
|
|
|
.fops = &vdev_fops,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip,
|
|
|
|
struct pvr2_v4l2 *vp,
|
2006-12-31 05:27:32 +08:00
|
|
|
int v4l_type)
|
2006-06-27 07:58:46 +08:00
|
|
|
{
|
2009-10-12 11:13:28 +08:00
|
|
|
struct usb_device *usbdev;
|
2006-06-27 07:58:46 +08:00
|
|
|
int mindevnum;
|
|
|
|
int unit_number;
|
2007-03-14 17:17:59 +08:00
|
|
|
int *nr_ptr = NULL;
|
2006-06-27 07:58:46 +08:00
|
|
|
dip->v4lp = vp;
|
|
|
|
|
2009-10-12 11:13:28 +08:00
|
|
|
usbdev = pvr2_hdw_get_dev(vp->channel.mc_head->hdw);
|
2006-12-31 05:27:32 +08:00
|
|
|
dip->v4l_type = v4l_type;
|
|
|
|
switch (v4l_type) {
|
|
|
|
case VFL_TYPE_GRABBER:
|
2006-06-27 07:58:46 +08:00
|
|
|
dip->stream = &vp->channel.mc_head->video_stream;
|
2006-12-31 05:27:32 +08:00
|
|
|
dip->config = pvr2_config_mpeg;
|
|
|
|
dip->minor_type = pvr2_v4l_type_video;
|
|
|
|
nr_ptr = video_nr;
|
2006-12-31 05:31:22 +08:00
|
|
|
if (!dip->stream) {
|
2009-01-08 20:13:42 +08:00
|
|
|
pr_err(KBUILD_MODNAME
|
|
|
|
": Failed to set up pvrusb2 v4l video dev"
|
|
|
|
" due to missing stream instance\n");
|
2006-12-31 05:31:22 +08:00
|
|
|
return;
|
|
|
|
}
|
2006-06-27 07:58:46 +08:00
|
|
|
break;
|
2006-12-31 05:27:32 +08:00
|
|
|
case VFL_TYPE_VBI:
|
|
|
|
dip->config = pvr2_config_vbi;
|
|
|
|
dip->minor_type = pvr2_v4l_type_vbi;
|
|
|
|
nr_ptr = vbi_nr;
|
2006-06-27 07:58:46 +08:00
|
|
|
break;
|
2006-12-31 05:27:32 +08:00
|
|
|
case VFL_TYPE_RADIO:
|
2007-01-20 11:04:31 +08:00
|
|
|
dip->stream = &vp->channel.mc_head->video_stream;
|
|
|
|
dip->config = pvr2_config_mpeg;
|
2006-12-31 05:27:32 +08:00
|
|
|
dip->minor_type = pvr2_v4l_type_radio;
|
|
|
|
nr_ptr = radio_nr;
|
2006-06-27 07:58:46 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Bail out (this should be impossible) */
|
2009-01-08 20:13:42 +08:00
|
|
|
pr_err(KBUILD_MODNAME ": Failed to set up pvrusb2 v4l dev"
|
|
|
|
" due to unrecognized config\n");
|
2006-06-27 07:58:46 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2006-09-24 09:30:50 +08:00
|
|
|
memcpy(&dip->devbase,&vdev_template,sizeof(vdev_template));
|
|
|
|
dip->devbase.release = pvr2_video_device_release;
|
2006-06-27 07:58:46 +08:00
|
|
|
|
|
|
|
mindevnum = -1;
|
|
|
|
unit_number = pvr2_hdw_get_unit_number(vp->channel.mc_head->hdw);
|
2006-12-31 05:27:32 +08:00
|
|
|
if (nr_ptr && (unit_number >= 0) && (unit_number < PVR_NUM)) {
|
|
|
|
mindevnum = nr_ptr[unit_number];
|
2006-06-27 07:58:46 +08:00
|
|
|
}
|
2009-10-12 11:13:28 +08:00
|
|
|
dip->devbase.parent = &usbdev->dev;
|
2006-12-31 05:27:32 +08:00
|
|
|
if ((video_register_device(&dip->devbase,
|
|
|
|
dip->v4l_type, mindevnum) < 0) &&
|
|
|
|
(video_register_device(&dip->devbase,
|
|
|
|
dip->v4l_type, -1) < 0)) {
|
2009-01-08 20:13:42 +08:00
|
|
|
pr_err(KBUILD_MODNAME
|
|
|
|
": Failed to register pvrusb2 v4l device\n");
|
V4L/DVB (5039): Pvrusb2: Implement /dev/radioX
The "main" V4L2 interface patch. This is yet very incomplete, incorrect and
probably inappropriate for inclusion as-is, but at least with this I 'm able
to tune and play radio through a V4L2 program (pvr-radio.c, a "thumb" version
of ivtv-radio.c with just the essentials).
Therefore, it kinda gives an idea of what is needed to support this, hm,
interface (partly used also by e.g., kradio). Please point out any mistakes
on this code. I 'm sure I 'm messing up some struct initialization somewhere
but currently I 'm too lazy to actually think this through until I complete
the functionality (e.g., handle the VIDIOC_S_STD, ENUMINPUT, etc ioctls
appropriately).
Signed-off-by: Pantelis Koukousoulas <pakt223@freemail.gr>
Signed-off-by: Mike Isely <isely@pobox.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2006-12-28 10:09:55 +08:00
|
|
|
}
|
2006-12-31 05:27:32 +08:00
|
|
|
|
2009-11-28 00:57:15 +08:00
|
|
|
printk(KERN_INFO "pvrusb2: registered device %s [%s]\n",
|
|
|
|
video_device_node_name(&dip->devbase),
|
2006-12-31 05:27:32 +08:00
|
|
|
pvr2_config_get_name(dip->config));
|
2006-06-27 07:58:46 +08:00
|
|
|
|
|
|
|
pvr2_hdw_v4l_store_minor_number(vp->channel.mc_head->hdw,
|
2006-12-31 05:27:32 +08:00
|
|
|
dip->minor_type,dip->devbase.minor);
|
2006-06-27 07:58:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp)
|
|
|
|
{
|
|
|
|
struct pvr2_v4l2 *vp;
|
|
|
|
|
2007-01-20 11:03:32 +08:00
|
|
|
vp = kzalloc(sizeof(*vp),GFP_KERNEL);
|
2006-06-27 07:58:46 +08:00
|
|
|
if (!vp) return vp;
|
|
|
|
pvr2_channel_init(&vp->channel,mnp);
|
|
|
|
pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_v4l2 id=%p",vp);
|
|
|
|
|
|
|
|
vp->channel.check_func = pvr2_v4l2_internal_check;
|
|
|
|
|
|
|
|
/* register streams */
|
2008-04-23 01:45:38 +08:00
|
|
|
vp->dev_video = kzalloc(sizeof(*vp->dev_video),GFP_KERNEL);
|
|
|
|
if (!vp->dev_video) goto fail;
|
2006-12-31 05:27:32 +08:00
|
|
|
pvr2_v4l2_dev_init(vp->dev_video,vp,VFL_TYPE_GRABBER);
|
2008-04-21 14:52:34 +08:00
|
|
|
if (pvr2_hdw_get_input_available(vp->channel.mc_head->hdw) &
|
|
|
|
(1 << PVR2_CVAL_INPUT_RADIO)) {
|
2008-04-23 01:45:38 +08:00
|
|
|
vp->dev_radio = kzalloc(sizeof(*vp->dev_radio),GFP_KERNEL);
|
|
|
|
if (!vp->dev_radio) goto fail;
|
|
|
|
pvr2_v4l2_dev_init(vp->dev_radio,vp,VFL_TYPE_RADIO);
|
|
|
|
}
|
2006-06-27 07:58:46 +08:00
|
|
|
|
|
|
|
return vp;
|
2008-04-23 01:45:38 +08:00
|
|
|
fail:
|
|
|
|
pvr2_trace(PVR2_TRACE_STRUCT,"Failure creating pvr2_v4l2 id=%p",vp);
|
|
|
|
pvr2_v4l2_destroy_no_lock(vp);
|
2008-04-29 07:50:03 +08:00
|
|
|
return NULL;
|
2006-06-27 07:58:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Stuff for Emacs to see, in order to encourage consistent editing style:
|
|
|
|
*** Local Variables: ***
|
|
|
|
*** mode: c ***
|
|
|
|
*** fill-column: 75 ***
|
|
|
|
*** tab-width: 8 ***
|
|
|
|
*** c-basic-offset: 8 ***
|
|
|
|
*** End: ***
|
|
|
|
*/
|