⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pvrusb2-v4l2.c

📁 linux内核源码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * *  $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 <linux/kernel.h>#include <linux/version.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-dev.h>#include <media/v4l2-common.h>struct pvr2_v4l2_dev;struct pvr2_v4l2_fh;struct pvr2_v4l2;struct pvr2_v4l2_dev {	struct video_device devbase; /* MUST be first! */	struct pvr2_v4l2 *v4lp;	struct pvr2_context_stream *stream;	/* 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 */};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;	int prev_input_val;};struct pvr2_v4l2 {	struct pvr2_channel channel;	struct pvr2_v4l2_fh *vfirst;	struct pvr2_v4l2_fh *vlast;	struct v4l2_prio_state prio;	/* 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;};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 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");static 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_RADIO |			   V4L2_CAP_READWRITE),	.reserved       = {0,0,0,0}};static 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  1static struct 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 }			}		}	}};static const char *get_v4l_name(int v4l_type){	switch (v4l_type) {	case VFL_TYPE_GRABBER: return "video";	case VFL_TYPE_RADIO: return "radio";	case VFL_TYPE_VBI: return "vbi";	default: return "?";	}}/* * 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));		strlcpy(cap->bus_info,pvr2_hdw_get_bus_info(hdw),			sizeof(cap->bus_info));		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;		cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT);		memset(&tmp,0,sizeof(tmp));		tmp.index = vi->index;		ret = 0;		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;		default:			ret = -EINVAL;			break;		}		if (ret < 0) break;		cnt = 0;		pvr2_ctrl_get_valname(cptr,vi->index,				      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:	{		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);		vi->index = val;		break;	}	case VIDIOC_S_INPUT:	{		struct v4l2_input *vi = (struct v4l2_input *)arg;		ret = pvr2_ctrl_set_value(			pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT),			vi->index);		break;	}	case VIDIOC_ENUMAUDIO:	{		/* 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;		ret = -EINVAL;		if (vin->index > 0) break;		strncpy(vin->name, "PVRUSB2 Audio",14);		vin->capability = V4L2_AUDCAP_STEREO;		ret = 0;		break;		break;	}	case VIDIOC_G_AUDIO:	{		/* 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;		break;	}	case VIDIOC_S_AUDIO:	{		ret = -EINVAL;		break;	}	case VIDIOC_G_TUNER:	{		struct v4l2_tuner *vt = (struct v4l2_tuner *)arg;		if (vt->index != 0) break; /* Only answer for the 1st tuner */		pvr2_hdw_execute_tuner_poll(hdw);		ret = pvr2_hdw_get_tuner_status(hdw,vt);		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);		break;	}	case VIDIOC_S_FREQUENCY:	{		const struct v4l2_frequency *vf = (struct v4l2_frequency *)arg;		unsigned long fv;		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;		if (vf->type == V4L2_TUNER_RADIO) {			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) {			fv = (fv * 125) / 2;		} else {			fv = fv * 62500;		}		ret = pvr2_ctrl_set_value(			pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY),fv);		break;	}	case VIDIOC_G_FREQUENCY:	{		struct v4l2_frequency *vf = (struct v4l2_frequency *)arg;		int val = 0;		int cur_input;		struct v4l2_tuner vt;		ret = pvr2_hdw_get_tuner_status(hdw,&vt);		if (ret != 0) break;		ret = pvr2_ctrl_get_value(			pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY),			&val);		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;		}		if (vt.capability & V4L2_TUNER_CAP_LOW) {			val = (val * 2) / 125;		} else {			val /= 62500;		}		vf->frequency = val;		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: {			int lmin,lmax,ldef;			struct pvr2_ctrl *hcp,*vcp;			int h = vf->fmt.pix.height;			int w = vf->fmt.pix.width;			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);			ldef = pvr2_ctrl_get_def(hcp);			if (w == -1) {				w = ldef;			} else if (w < lmin) {				w = lmin;			} else if (w > lmax) {				w = lmax;			}			lmin = pvr2_ctrl_get_min(vcp);			lmax = pvr2_ctrl_get_max(vcp);			ldef = pvr2_ctrl_get_def(vcp);			if (h == -1) {				h = ldef;			} else if (h < lmin) {				h = lmin;			} else if (h > lmax) {				h = lmax;			}			memcpy(vf, &pvr_format[PVR_FORMAT_PIX],			       sizeof(struct v4l2_format));			vf->fmt.pix.width = w;			vf->fmt.pix.height = h;			if (cmd == VIDIOC_S_FMT) {				pvr2_ctrl_set_value(hcp,vf->fmt.pix.width);				pvr2_ctrl_set_value(vcp,vf->fmt.pix.height);			}		} 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:	{		if (!fh->dev_info->stream) {			/* No stream defined for this node.  This means			   that we're not currently allowed to stream from			   this node. */			ret = -EPERM;			break;		}		ret = pvr2_hdw_set_stream_type(hdw,dev_info->config);		if (ret < 0) return ret;		ret = pvr2_hdw_set_streaming(hdw,!0);		break;	}	case VIDIOC_STREAMOFF:	{		if (!fh->dev_info->stream) {			/* No stream defined for this node.  This means			   that we're not currently allowed to stream from			   this node. */			ret = -EPERM;			break;		}		ret = pvr2_hdw_set_streaming(hdw,0);		break;	}	case VIDIOC_QUERYCTRL:	{		struct pvr2_ctrl *cptr;		struct v4l2_queryctrl *vc = (struct v4l2_queryctrl *)arg;		ret = 0;		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);		}		if (!cptr) {			pvr2_trace(PVR2_TRACE_V4LIOCTL,				   "QUERYCTRL id=0x%x not implemented here",				   vc->id);			ret = -EINVAL;			break;		}		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);		vc->default_value = pvr2_ctrl_get_def(cptr);		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;		case pvr2_ctl_bool:

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -