📄 pvrusb2-v4l2.c
字号:
/* * * $Id$ * * 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 "compat.h"#include <linux/kernel.h>#include <linux/videodev.h>#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>#include <media/v4l2-common.h>#define PVR_WIDTH_DVD 720#define PVR_WIDTH_SVCD 480#define PVR_WIDTH_VCD 352#define PVR_HEIGHT_PAL 480#define PVR_HEIGHT_NTSC 576struct pvr2_v4l2_dev;struct pvr2_v4l2_fh;struct pvr2_v4l2;struct pvr2_v4l2_dev { struct pvr2_v4l2 *v4lp; struct video_device *vdev; struct pvr2_context_stream *stream; enum pvr2_config config;};struct pvr2_v4l2_fh { struct pvr2_channel channel; struct pvr2_v4l2_dev *dev_info; 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;};struct pvr2_v4l2 { struct pvr2_channel channel; struct pvr2_v4l2_fh *vfirst; struct pvr2_v4l2_fh *vlast; struct v4l2_prio_state prio; /* streams */ struct pvr2_v4l2_dev video_dev;};static int video_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1};module_param_array(video_nr, int, NULL, 0444);MODULE_PARM_DESC(video_nr, "Offset for device's minor");struct v4l2_capability pvr_capability ={ .driver = "pvrusb2", .card = "Hauppauge WinTV pvr-usb2", .bus_info = "usb", .version = KERNEL_VERSION(0,8,0), .capabilities = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE), .reserved = {0,0,0,0}};static struct v4l2_tuner pvr_v4l2_tuners[]= { { .index = 0, .name = "TV Tuner", .type = V4L2_TUNER_ANALOG_TV, .capability = (V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2), .rangelow = 0, .rangehigh = 0, .rxsubchans = V4L2_TUNER_SUB_STEREO, .audmode = V4L2_TUNER_MODE_STEREO, .signal = 0, .afc = 0, .reserved = {0,0,0,0} }#if 0 { .index = 1, .name = "Radio Tuner", .type = V4L2_TUNER_RADIO, .capability = (V4L2_TUNER_CAP_STEREO), .rangelow = 0, .rangehigh = 0, .rxsubchans = 0, .audmode = V4L2_TUNER_MODE_STEREO, .signal = 0, .afc = 0, .reserved = {0,0,0,0} }#endif};struct v4l2_fmtdesc pvr_fmtdesc [] = { { .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 1struct v4l2_format pvr_format [] = { [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 } } } }};/* * pvr_ioctl() * * This is part of Video 4 Linux API. The procedure handles ioctl() calls. * */static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg){ struct pvr2_v4l2_fh *fh = file->private_data; struct pvr2_v4l2 *vp = fh->vhead; struct pvr2_v4l2_dev *dev_info = fh->dev_info; struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; int ret = -EINVAL; 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: ret = v4l2_prio_check(&vp->prio, &fh->prio); if (ret) return ret; } switch (cmd) { case VIDIOC_QUERYCAP: { struct v4l2_capability *cap = arg; memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability)); 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; const struct v4l2_standard *src; int idx = vs->index; src = pvr2_hdw_get_stdenum_value(hdw,idx); if (!src) break; memcpy(vs, src, sizeof(struct v4l2_standard)); vs->index = idx; ret = 0; break; } case VIDIOC_G_STD: { struct pvr2_ctrl *cptr; v4l2_std_id *vs = (v4l2_std_id *)arg; cptr = pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_STDCUR); if (!pvr2_ctrl_is_valid(cptr)) { ret = -EINVAL; break; } *vs = pvr2_ctrl_get_value(cptr); ret = 0; break; } case VIDIOC_S_STD: { struct pvr2_ctrl *cptr; v4l2_std_id *vs = (v4l2_std_id *)arg; cptr = pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_STDCUR); if (!pvr2_ctrl_is_valid(cptr)) { ret = -EINVAL; break; } pvr2_ctrl_set_value(cptr,*vs); ret = 0; break; } case VIDIOC_ENUMINPUT: { struct pvr2_ctrl *cptr; struct v4l2_input *vi = (struct v4l2_input *)arg; struct v4l2_input tmp; if ((vi->index > PVR2_CVAL_INPUT_MAX) || (vi->index < PVR2_CVAL_INPUT_MIN)) { break; } cptr = pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_INPUT); memset(&tmp,0,sizeof(tmp)); tmp.index = vi->index; switch (vi->index) { case PVR2_CVAL_INPUT_TV: 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; } strlcpy(tmp.name, pvr2_ctrl_get_value_name(cptr,vi->index), sizeof(tmp.name)); /* 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: { struct pvr2_ctrl *cptr; struct v4l2_input *vi = (struct v4l2_input *)arg; cptr = pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_INPUT); if (!pvr2_ctrl_is_valid(cptr)) { ret = -EINVAL; break; } vi->index = pvr2_ctrl_get_value(cptr); ret = 0; break; } case VIDIOC_S_INPUT: { struct pvr2_ctrl *cptr; struct v4l2_input *vi = (struct v4l2_input *)arg; cptr = pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_INPUT); if (!pvr2_ctrl_is_valid(cptr)) { ret = -EINVAL; break; } ret = 0; if (pvr2_ctrl_set_value(cptr,vi->index)) { ret = -EINVAL; } break; } case VIDIOC_ENUMAUDIO: { ret = -EINVAL; break; } case VIDIOC_G_AUDIO: { ret = -EINVAL; break; } case VIDIOC_S_AUDIO: { ret = -EINVAL; break; } case VIDIOC_G_TUNER: { struct pvr2_ctrl *cptr; struct v4l2_tuner *vt = (struct v4l2_tuner *)arg; unsigned int status_mask; if (vt->index !=0) break; status_mask = pvr2_hdw_get_signal_status(hdw); memcpy(vt, &pvr_v4l2_tuners[vt->index], sizeof(struct v4l2_tuner)); vt->signal = 0; if (status_mask & PVR2_SIGNAL_OK) { if (status_mask & PVR2_SIGNAL_STEREO) { vt->rxsubchans = V4L2_TUNER_SUB_STEREO; } else { vt->rxsubchans = V4L2_TUNER_SUB_MONO; } if (status_mask & PVR2_SIGNAL_SAP) { vt->rxsubchans |= (V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2); } vt->signal = 65535; } cptr = pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_AUDIOMODE); vt->audmode = pvr2_ctrl_get_value(cptr); ret = 0; break; } case VIDIOC_S_TUNER: { struct pvr2_ctrl *cptr; struct v4l2_tuner *vt=(struct v4l2_tuner *)arg; if (vt->index != 0) break; cptr = pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_AUDIOMODE); if (!pvr2_ctrl_is_valid(cptr)) { ret = -EINVAL; break; } pvr2_ctrl_set_value(cptr,vt->audmode); ret = 0; } case VIDIOC_S_FREQUENCY: { struct pvr2_ctrl *cptr; const struct v4l2_frequency *vf = (struct v4l2_frequency *)arg; cptr = pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_FREQUENCY); if (!pvr2_ctrl_is_valid(cptr)) { ret = -EINVAL; break; } pvr2_ctrl_set_value(cptr,vf->frequency * 62500); ret = 0; break; } case VIDIOC_G_FREQUENCY: { struct pvr2_ctrl *cptr; struct v4l2_frequency *vf = (struct v4l2_frequency *)arg; int val; cptr = pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_FREQUENCY); if (!pvr2_ctrl_is_valid(cptr)) { ret = -EINVAL; break; } val = pvr2_ctrl_get_value(cptr); val /= 62500; vf->frequency = val; ret = 0; 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; switch(vf->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: memcpy(vf, &pvr_format[PVR_FORMAT_PIX], sizeof(struct v4l2_format)); vf->fmt.pix.width = pvr2_ctrl_get_value( pvr2_hdw_get_ctrl(hdw, V4L2_CID_PVR_HRES)); if (pvr2_ctrl_get_value( pvr2_hdw_get_ctrl( hdw,V4L2_CID_PVR_INTERLACE))) { vf->fmt.pix.width /= 2; } vf->fmt.pix.height = pvr2_ctrl_get_value( pvr2_hdw_get_ctrl(hdw, V4L2_CID_PVR_VRES)); 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: { int h = vf->fmt.pix.height; int w = vf->fmt.pix.width; int vd_std, hf, hh; vd_std = pvr2_ctrl_get_value( pvr2_hdw_get_ctrl(hdw,V4L2_CID_PVR_STDCUR)); if (vd_std & V4L2_STD_525_60) { hf=480; } else { hf=576; } hh = (int) (hf / 2); memcpy(vf, &pvr_format[PVR_FORMAT_PIX], sizeof(struct v4l2_format)); if (w > 720) vf->fmt.pix.width = 720; vf->fmt.pix.width &= 0xff0; vf->fmt.pix.height = (h > hh) ? hf : hh; if (cmd == VIDIOC_S_FMT){ pvr2_ctrl_set_value( pvr2_hdw_get_ctrl(hdw,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -