bttv-driver.c

来自「是关于linux2.5.1的完全源码」· C语言 代码 · 共 2,527 行 · 第 1/5 页

C
2,527
字号
		return 0;	}	case VIDIOC_G_FMT:	{		struct v4l2_format *f = arg;		memset(f,0,sizeof(*f));		f->type = V4L2_BUF_TYPE_CAPTURE;		f->fmt.pix.width        = fh->buf.vb.width;		f->fmt.pix.height       = fh->buf.vb.height;		f->fmt.pix.depth        = fh->buf.fmt->depth;		f->fmt.pix.pixelformat  = fh->buf.fmt->fourcc;		f->fmt.pix.sizeimage    =			(fh->buf.vb.width*fh->buf.vb.height*fh->buf.fmt->depth)/8;		return 0;	}	case VIDIOC_S_FMT:	{		struct v4l2_format *f = arg;		const struct bttv_format *fmt;		if ((f->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_CAPTURE)			return -EINVAL;		fmt = format_by_fourcc(f->fmt.pix.pixelformat);		if (NULL == fmt)			return -EINVAL;		if (f->fmt.pix.width  < 48 ||		    f->fmt.pix.height < 32)			return -EINVAL;		if (f->fmt.pix.flags & V4L2_FMT_FLAG_BYTESPERLINE)			/* FIXME -- not implemented yet */			return -EINVAL;		down(&fh->lock);		/* fixup format */		if (f->fmt.pix.width  > bttv_tvnorms[btv->tvnorm].swidth)			f->fmt.pix.width = bttv_tvnorms[btv->tvnorm].swidth;		if (f->fmt.pix.height > bttv_tvnorms[btv->tvnorm].sheight)			f->fmt.pix.height = bttv_tvnorms[btv->tvnorm].sheight;		if (!(f->fmt.pix.flags & V4L2_FMT_FLAG_INTERLACED) &&		    f->fmt.pix.height>bttv_tvnorms[btv->tvnorm].sheight/2)			f->fmt.pix.height=bttv_tvnorms[btv->tvnorm].sheight/2;		if (f->fmt.pix.height > bttv_tvnorms[btv->tvnorm].sheight/2) {			/* must interlace -- no field splitting available */			f->fmt.pix.flags &= ~(V4L2_FMT_FLAG_TOPFIELD|					      V4L2_FMT_FLAG_BOTFIELD);		} else {			/* one field is enouth -- no interlace needed */			f->fmt.pix.flags &= ~V4L2_FMT_FLAG_INTERLACED;		}				/* update our state informations */		fh->buf.fmt             = fmt;		fh->buf.vb.width        = f->fmt.pix.width;		fh->buf.vb.height       = f->fmt.pix.height;		btv->init.buf.fmt       = fmt;		btv->init.buf.vb.width  = f->fmt.pix.width;		btv->init.buf.vb.height = f->fmt.pix.height;		/* update data for the application */		f->fmt.pix.depth = fmt->depth;		f->fmt.pix.sizeimage =			(fh->buf.vb.width * fh->buf.vb.height * fmt->depth)/8;		up(&fh->lock);		return 0;	}	case VIDIOC_G_FBUF:	{		struct v4l2_framebuffer *fb = arg;		memset(fb,0,sizeof(*fb));		fb->base[0]    = btv->fbuf.base;		fb->fmt.width  = btv->fbuf.width;		fb->fmt.height = btv->fbuf.height;		fb->fmt.bytesperline = btv->fbuf.bytesperline;		fb->fmt.flags  = V4L2_FMT_FLAG_BYTESPERLINE;		fb->capability = V4L2_FBUF_CAP_CLIPPING;		if (fh->ovfmt) {			fb->fmt.depth  = fh->ovfmt->depth;			fb->fmt.pixelformat  = fh->ovfmt->fourcc;		}		return 0;	}	case VIDIOC_S_FBUF:	{		struct v4l2_framebuffer *fb = arg;		const struct bttv_format *fmt;		unsigned long end;				if(!capable(CAP_SYS_ADMIN) &&		   !capable(CAP_SYS_RAWIO))			return -EPERM;		/* check args */		end = (unsigned long)fb->base[0] +			fb->fmt.height * fb->fmt.bytesperline;		if (0 == find_videomem((unsigned long)fb->base[0],end))			return -EINVAL;		fmt = format_by_fourcc(fb->fmt.pixelformat);		if (NULL == fmt)			return -EINVAL;		if (0 == (fmt->flags & FORMAT_FLAGS_PACKED))			return -EINVAL;		down(&fh->lock);		retval = -EINVAL;		if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {			if (fb->fmt.width > bttv_tvnorms[btv->tvnorm].swidth)				goto fh_unlock_and_return;			if (fb->fmt.height > bttv_tvnorms[btv->tvnorm].sheight)				goto fh_unlock_and_return;		}		/* ok, accept it */		btv->fbuf.base   = fb->base[0];		btv->fbuf.width  = fb->fmt.width;		btv->fbuf.height = fb->fmt.height;		btv->fbuf.depth  = fmt->depth;		if (fb->fmt.flags & V4L2_FMT_FLAG_BYTESPERLINE)			btv->fbuf.bytesperline = fb->fmt.bytesperline;		else			btv->fbuf.bytesperline = btv->fbuf.width*fmt->depth/8;				retval = 0;		fh->ovfmt = fmt;		btv->init.ovfmt = fmt;		if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {			fh->ov.x      = 0;			fh->ov.y      = 0;			fh->ov.width  = fb->fmt.width;			fh->ov.height = fb->fmt.height;			btv->init.ov.width  = fb->fmt.width;			btv->init.ov.height = fb->fmt.height;			if (fh->ov.clips)				kfree(fh->ov.clips);			fh->ov.clips = NULL;			fh->ov.nclips = 0;			if (check_btres(fh, RESOURCE_OVERLAY)) {				struct bttv_buffer *new;						new = videobuf_alloc(sizeof(*new),						     V4L2_BUF_TYPE_CAPTURE);				bttv_overlay_risc(btv,&fh->ov,fh->ovfmt,new);				retval = bttv_switch_overlay(btv,fh,new);			}		}		up(&fh->lock);		return retval;	}	case VIDIOC_G_WIN:	{		struct v4l2_window *win = arg;		memset(win,0,sizeof(*win));		win->x      = fh->ov.x;		win->y      = fh->ov.y;		win->width  = fh->ov.width;		win->height = fh->ov.height;		return 0;	}	case VIDIOC_S_WIN:	{		struct v4l2_window *win = arg;		return setup_window(fh,btv,win->x,win->y,				    win->width,win->height,				    (struct video_clip*)win->clips,				    win->clipcount);	}	case VIDIOC_REQBUFS:	{		struct v4l2_requestbuffers *req = arg;		int size,count;		if (!mmap)			return -EINVAL;		if ((req->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_CAPTURE)			return -EINVAL;		if (req->count < 0)			return -EINVAL;		down(&fh->lock);		retval = -EINVAL;		if (NULL == fh->buf.fmt    ||		    0 == fh->buf.vb.width  ||		    0 == fh->buf.vb.height)			goto fh_unlock_and_return;		size = (fh->buf.vb.width*fh->buf.vb.height*fh->buf.fmt->depth) >> 3;		size = (size + PAGE_SIZE - 1) & PAGE_MASK;		count = req->count;		if (count > VIDEO_MAX_FRAME)			count = VIDEO_MAX_FRAME;		while (size * count > gbuffers * gbufsize)			count--;		retval = videobuf_mmap_setup(file,					     (struct videobuf_buffer**)fh->bufs,					     sizeof(struct bttv_buffer),					     count,size,V4L2_BUF_TYPE_CAPTURE,					     release_buffer);		if (retval < 0)			goto fh_unlock_and_return;		req->type  = V4L2_BUF_TYPE_CAPTURE;		req->count = count;		up(&fh->lock);		return 0;	}	case VIDIOC_QUERYBUF:	{		struct v4l2_buffer *b = arg;		if ((b->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_CAPTURE)			return -EINVAL;		if (b->index < 0 || b->index > VIDEO_MAX_FRAME)			return -EINVAL;		if (NULL == fh->bufs[b->index])			return -EINVAL;		videobuf_status(b,&fh->bufs[b->index]->vb);		return 0;	}	case VIDIOC_QBUF:	{		struct v4l2_buffer *b = arg;		struct bttv_buffer *buf;		int field = 0;		if ((b->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_CAPTURE)			return -EINVAL;		if (b->index < 0 || b->index > VIDEO_MAX_FRAME)			return -EINVAL;		down(&fh->lock);		retval = -EINVAL;		buf = fh->bufs[b->index];		if (NULL == buf)			goto fh_unlock_and_return;		if (0 == buf->vb.baddr)			goto fh_unlock_and_return;		if (buf->vb.state == STATE_QUEUED ||		    buf->vb.state == STATE_ACTIVE)			goto fh_unlock_and_return;		if (b->flags & V4L2_BUF_FLAG_TOPFIELD)			field |= VBUF_FIELD_ODD;		if (b->flags & V4L2_BUF_FLAG_BOTFIELD)		        field |= VBUF_FIELD_EVEN;		retval = bttv_prepare_buffer(btv,buf,fh->buf.fmt,					     fh->buf.vb.width,fh->buf.vb.height,field);		if (0 != retval)			goto fh_unlock_and_return;		list_add_tail(&buf->vb.stream,&fh->stream);		if (check_btres(fh, RESOURCE_STREAMING))			bttv_queue_buffer(btv,buf);		up(&fh->lock);		return 0;	}	case VIDIOC_DQBUF:	{		struct v4l2_buffer *b = arg;		struct bttv_buffer *buf;		if ((b->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_CAPTURE)			return -EINVAL;		down(&fh->lock);		retval = -EINVAL;		if (list_empty(&fh->stream))			goto fh_unlock_and_return;		buf = list_entry(fh->stream.next, struct bttv_buffer, vb.stream);		retval = videobuf_waiton(&buf->vb,0,1);		if (retval < 0)			goto fh_unlock_and_return;		switch (buf->vb.state) {		case STATE_ERROR:			retval = -EIO;			/* fall through */		case STATE_DONE:			videobuf_dma_pci_sync(btv->dev,&buf->vb.dma);			buf->vb.state = STATE_IDLE;			break;		default:			retval = -EINVAL;			goto fh_unlock_and_return;		}		list_del(&buf->vb.stream);		memset(b,0,sizeof(*b));		videobuf_status(b,&buf->vb);		up(&fh->lock);		return retval;	}	case VIDIOC_STREAMON:	{		struct list_head *list;		struct bttv_buffer *buf;				if (!check_alloc_btres(btv,fh,RESOURCE_STREAMING))			return -EBUSY;		bttv_field_count(btv);		down(&fh->lock);		list_for_each(list,&fh->stream) {			buf = list_entry(list, struct bttv_buffer, vb.stream);			if (buf->vb.state == STATE_PREPARED)				bttv_queue_buffer(btv,buf);		}		up(&fh->lock);		return 0;	}	case VIDIOC_STREAMOFF:	{		down(&fh->lock);		retval = -EINVAL;		if (!check_btres(fh, RESOURCE_STREAMING))			goto fh_unlock_and_return;		bttv_stop_streaming(btv,fh);		up(&fh->lock);		return 0;	}	case VIDIOC_QUERYCTRL:	{		struct v4l2_queryctrl *c = arg;		int i;		v4l2_fill_ctrl_category(c);		if ((c->id <  V4L2_CID_BASE ||		     c->id >= V4L2_CID_LASTP1) &&		    (c->id <  V4L2_CID_PRIVATE_BASE ||		     c->id >= V4L2_CID_PRIVATE_LASTP1))			return -EINVAL;		for (i = 0; i < BTTV_CTLS; i++)			if (bttv_ctls[i].id == c->id)				break;		if (i == BTTV_CTLS) {			*c = no_ctl;			return 0;		}		*c = bttv_ctls[i];		if (bttv_ctls[i].category == V4L2_CTRL_CAT_AUDIO) {			struct video_audio va;			memset(&va,0,sizeof(va));			bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);			if (bttv_tvcards[btv->type].audio_hook)				bttv_tvcards[btv->type].audio_hook(btv,&va,0);			switch (bttv_ctls[i].id) {			case V4L2_CID_AUDIO_VOLUME:				if (!(va.flags & VIDEO_AUDIO_VOLUME))					*c = no_ctl;				break;			case V4L2_CID_AUDIO_BALANCE:				if (!(va.flags & VIDEO_AUDIO_BALANCE))					*c = no_ctl;				break;			case V4L2_CID_AUDIO_BASS:				if (!(va.flags & VIDEO_AUDIO_BASS))					*c = no_ctl;				break;			case V4L2_CID_AUDIO_TREBLE:				if (!(va.flags & VIDEO_AUDIO_TREBLE))					*c = no_ctl;				break;			}		}		return 0;	}	case VIDIOC_G_CTRL:		return get_control(btv,arg);	case VIDIOC_S_CTRL:		return set_control(btv,arg);	case VIDIOC_G_PARM:	{		struct v4l2_streamparm *parm = arg;		struct v4l2_standard s;		if (parm->type != V4L2_BUF_TYPE_CAPTURE)			return -EINVAL;		memset(parm,0,sizeof(*parm));		v4l2_video_std_construct(&s, bttv_tvnorms[btv->tvnorm].v4l2_id, 0);		parm->parm.capture.timeperframe = v4l2_video_std_tpf(&s);		return 0;	}		case VIDIOC_ENUMSTD:	case VIDIOC_G_STD:	case VIDIOC_S_STD:	case VIDIOC_ENUMINPUT:	case VIDIOC_G_INPUT:	case VIDIOC_S_INPUT:	case VIDIOC_G_TUNER:	case VIDIOC_S_TUNER:	case VIDIOC_G_FREQ:	case VIDIOC_S_FREQ:		return bttv_common_ioctls(btv,cmd,arg);#endif /* HAVE_V4L2 */	default:		return -ENOIOCTLCMD;	}	return 0; fh_unlock_and_return:	up(&fh->lock);	return retval;}static int bttv_ioctl(struct inode *inode, struct file *file,		      unsigned int cmd, unsigned long arg){	return video_usercopy(inode, file, cmd, arg, bttv_do_ioctl);}/* start capture to a kernel bounce buffer */static int bttv_read_capture(struct bttv_fh *fh){	struct bttv *btv = fh->btv;	int rc;		dprintk("bttv%d: read start\n",btv->nr);	rc = bttv_prepare_buffer(btv,&fh->read_buf,fh->buf.fmt,				 fh->buf.vb.width,fh->buf.vb.height,0);	if (0 != rc)		return rc;	bttv_queue_buffer(btv,&fh->read_buf);	fh->read_off = 0;	return 0;}/* * blocking read for a complete video frame * => no kernel bounce buffer needed. */static ssize_tbttv_read_zerocopy(struct bttv_fh *fh, struct bttv *btv,		   char *data,size_t count, loff_t *ppos){        int rc;        /* setup stuff */	dprintk("bttv%d: read zerocopy\n",btv->nr);        fh->read_buf.vb.baddr = (unsigned long)data;        fh->read_buf.vb.bsize = count;        rc = bttv_prepare_buffer(btv,&fh->read_buf,fh->buf.fmt,				 fh->buf.vb.width,fh->buf.vb.height,0);        if (0 != rc)		goto done;	        /* start capture & wait */        bttv_queue_buffer(btv,&fh->read_buf);        rc = videobuf_waiton(&fh->read_buf.vb,0,1);        if (0 == rc) {		videobuf_dma_pci_sync(btv->dev,&fh->read_buf.vb.dma);                rc = fh->read_buf.vb.size;	} done:	/* cleanup */	bttv_dma_free(btv,&fh->read_buf);	fh->read_buf.vb.baddr = 0;	fh->read_buf.vb.size = 0;	return rc;}static ssize_t bttv_read(struct file *file, char *data,			 size_t count, loff_t *ppos){	struct bttv_fh *fh = file->private_data;	struct bttv *btv = fh->btv;	int rc, bytes, size;	if (btv->errors)		bttv_reinit_bt848(btv);	if (locked_btres(btv,RESOURCE_STREAMING))		return -EBUSY;	down(&fh->lock);	size = fh->buf.fmt->depth*fh->buf.vb.width*fh->buf.vb.height >> 3;	if (-1 == fh->read_off  &&  count >= size  &&	    !(file->f_flags & O_NONBLOCK)) {		rc = bttv_read_zerocopy(fh,btv,data,count,ppos);		if (rc >= 0)			/* ok, all done */			goto unlock_out;		/* fallback to kernel bounce buffer on failures */	}		if (-1 == fh->read_off) {		/* need to capture a new frame */		rc = bttv_read_capture(fh);		if (0 != rc)			goto unlock_out;	}	/* wait until capture is done */        rc = videobuf_waiton(&fh->read_buf.vb, file->f_flags & O_NONBLOCK,1);	if (0 != rc)		goto unlock_

⌨️ 快捷键说明

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