Enscript Output

extractedLnx/linux-2.5.7/drivers/media/video/bttv-driver.c_bttv_ioctl.c

static int bttv_ioctl(struct inode *inode, struct file *file,
		      unsigned int cmd, void *arg)
{
	struct bttv_fh *fh  = file->private_data;
	struct bttv    *btv = fh->btv;
	int retval;

	if (bttv_debug > 1) {
		switch (_IOC_TYPE(cmd)) {
		case 'v':
			printk("bttv%d: ioctl 0x%x (v4l1, VIDIOC%s)\n",
			       btv->nr, cmd, (_IOC_NR(cmd) < V4L1_IOCTLS) ?
			       v4l1_ioctls[_IOC_NR(cmd)] : "???");
			break;
		case 'V':
			printk("bttv%d: ioctl 0x%x (v4l2, VIDIOC_%s)\n",
			       btv->nr, cmd,  (_IOC_NR(cmd) < V4L2_IOCTLS) ?
			       v4l2_ioctls[_IOC_NR(cmd)] : "???");
			break;
		default:
			printk("bttv%d: ioctl 0x%x (???)\n",
			       btv->nr, cmd);
		}
	}
	if (btv->errors)
		bttv_reinit_bt848(btv);

	switch (cmd) {

	/* ***  v4l1  *** ************************************************ */
	case VIDIOCGCAP:
	{
                struct video_capability *cap = arg;

		memset(cap,0,sizeof(*cap));
                strcpy(cap->name,btv->video_dev.name);
                cap->type = VID_TYPE_CAPTURE|
			VID_TYPE_TUNER|
			VID_TYPE_OVERLAY|
			VID_TYPE_CLIPPING|
                        VID_TYPE_SCALES;
                cap->channels  = bttv_tvcards[btv->type].video_inputs;
                cap->audios    = bttv_tvcards[btv->type].audio_inputs;
                cap->maxwidth  = bttv_tvnorms[btv->tvnorm].swidth;
                cap->maxheight = bttv_tvnorms[btv->tvnorm].sheight;
                cap->minwidth  = 48;
                cap->minheight = 32;
                return 0;
	}

	case VIDIOCGPICT:
	{
		struct video_picture *pic = arg;

		memset(pic,0,sizeof(*pic));
		pic->brightness = btv->bright;
		pic->contrast   = btv->contrast;
		pic->hue        = btv->hue;
		pic->colour     = btv->saturation;
		if (fh->buf.fmt) {
			pic->depth   = fh->buf.fmt->depth;
			pic->palette = fh->buf.fmt->palette;
		}
		return 0;
	}
	case VIDIOCSPICT:
	{
		struct video_picture *pic = arg;
		const struct bttv_format *fmt;
		
		fmt = format_by_palette(pic->palette);
		if (NULL == fmt)
			return -EINVAL;
		down(&fh->lock);
		retval = -EINVAL;
		if (fmt->depth != pic->depth && !sloppy)
			goto fh_unlock_and_return;
		fh->ovfmt   = fmt;
		fh->buf.fmt = fmt;
		btv->init.ovfmt   = fmt;
		btv->init.buf.fmt = fmt;
		if (bigendian) {
			/* dirty hack time:  swap bytes for overlay if the
			   display adaptor is big endian (insmod option) */
			if (fmt->palette == VIDEO_PALETTE_RGB555 ||
			    fmt->palette == VIDEO_PALETTE_RGB565 ||
			    fmt->palette == VIDEO_PALETTE_RGB32) {
				fh->ovfmt = fmt+1;
			}
		}
		bt848_bright(btv,pic->brightness);
		bt848_contrast(btv,pic->contrast);
		bt848_hue(btv,pic->hue);
		bt848_sat(btv,pic->colour);
		up(&fh->lock);
                return 0;
	}

	case VIDIOCGWIN:
	{
		struct video_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 VIDIOCSWIN:
	{
		struct video_window *win = arg;

		retval = setup_window(fh,btv,win->x,win->y,
				      win->width,win->height,
				      win->clips,
				      win->clipcount);
		if (0 == retval) {
			/* on v4l1 this ioctl affects the read() size too */
			fh->buf.vb.width  = fh->ov.width;
			fh->buf.vb.height = fh->ov.height;
			btv->init.buf.vb.width  = fh->ov.width;
			btv->init.buf.vb.height = fh->ov.height;
		}
		return retval;
	}

	case VIDIOCGFBUF:
	{
		struct video_buffer *fbuf = arg;
		*fbuf = btv->fbuf;
		return 0;
	}
	case VIDIOCSFBUF:
	{
		struct video_buffer *fbuf = arg;
		const struct bttv_format *fmt;
		unsigned long end;

		if(!capable(CAP_SYS_ADMIN) &&
                   !capable(CAP_SYS_RAWIO))
                        return -EPERM;
		end = (unsigned long)fbuf->base +
			fbuf->height * fbuf->bytesperline;
		if (0 == find_videomem((unsigned long)fbuf->base,end))
			return -EINVAL;
		down(&fh->lock);
		retval = -EINVAL;
		if (sloppy) {
			/* also set the default palette -- for backward
			   compatibility with older versions */
			switch (fbuf->depth) {
			case 8:
				fmt = format_by_palette(VIDEO_PALETTE_HI240);
				break;
			case 16:
				fmt = format_by_palette(VIDEO_PALETTE_RGB565);
				break;
			case 24:
				fmt = format_by_palette(VIDEO_PALETTE_RGB24);
				break;
			case 32:
				fmt = format_by_palette(VIDEO_PALETTE_RGB32);
				break;
			case 15:
				fbuf->depth = 16;
				fmt = format_by_palette(VIDEO_PALETTE_RGB555);
				break;
			default:
				fmt = NULL;
				break;
			}
			if (NULL == fmt)
				goto fh_unlock_and_return;
			fh->ovfmt   = fmt;
			fh->buf.fmt = fmt;
			btv->init.ovfmt   = fmt;
			btv->init.buf.fmt = fmt;
		} else {
			if (15 == fbuf->depth)
				fbuf->depth = 16;
			if (fbuf->depth !=  8 && fbuf->depth != 16 &&
			    fbuf->depth != 24 && fbuf->depth != 32)
				goto fh_unlock_and_return;
		}
		btv->fbuf = *fbuf;
		up(&fh->lock);
		return 0;
	}

	case VIDIOCCAPTURE:
#ifdef HAVE_V4L2
	case VIDIOC_PREVIEW:
#endif
	{
		struct bttv_buffer *new;
		int *on = arg;

		if (*on) {
			/* verify args */
			if (NULL == btv->fbuf.base)
				return -EINVAL;
			if (fh->ov.width <48 ||
			    fh->ov.height<32 ||
			    fh->ov.width >bttv_tvnorms[btv->tvnorm].swidth  ||
			    fh->ov.height>bttv_tvnorms[btv->tvnorm].sheight ||
			    NULL == fh->ovfmt)
				return -EINVAL;
		}

		if (!check_alloc_btres(btv,fh,RESOURCE_OVERLAY))
			return -EBUSY;
		
		down(&fh->lock);
		if (*on) {
			fh->ov.tvnorm = btv->tvnorm;
			new = videobuf_alloc(sizeof(*new),
					     V4L2_BUF_TYPE_CAPTURE);
			bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
		} else {
			new = NULL;
		}

		/* switch over */
	        retval = bttv_switch_overlay(btv,fh,new);
		up(&fh->lock);
		return retval;
	}

	case VIDIOCGMBUF:
	{
		struct video_mbuf *mbuf = arg;
		int i;

		if (!mmap)
			return -EINVAL;
		down(&fh->lock);
		retval = videobuf_mmap_setup
			(file,(struct videobuf_buffer**)fh->bufs,
			 sizeof(struct bttv_buffer),
			 gbuffers,gbufsize,V4L2_BUF_TYPE_CAPTURE,
			 release_buffer);
		if (retval < 0)
			goto fh_unlock_and_return;
		memset(mbuf,0,sizeof(*mbuf));
		mbuf->frames = gbuffers;
		mbuf->size   = gbuffers * gbufsize;
		for (i = 0; i < gbuffers; i++)
			mbuf->offsets[i] = i * gbufsize;
		up(&fh->lock);
		return 0;
	}
	case VIDIOCMCAPTURE:
	{
		struct video_mmap *vm = arg;
		struct bttv_buffer *buf;

		if (vm->frame >= VIDEO_MAX_FRAME)
			return -EINVAL;

		down(&fh->lock);
		retval = -EINVAL;
		buf = fh->bufs[vm->frame];
		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;
		
		retval = bttv_prepare_buffer(btv,buf,
					     format_by_palette(vm->format),
					     vm->width,vm->height,0);
		if (0 != retval)
			goto fh_unlock_and_return;
		bttv_queue_buffer(btv,buf);
		up(&fh->lock);
		return 0;
	}
	case VIDIOCSYNC:
	{
		int *frame = arg;
		struct bttv_buffer *buf;

		if (*frame >= VIDEO_MAX_FRAME)
			return -EINVAL;

		down(&fh->lock);
		retval = -EINVAL;
		buf = fh->bufs[*frame];
		if (NULL == buf)
			goto fh_unlock_and_return;
		retval = videobuf_waiton(&buf->vb,0,1);
		if (0 != retval)
			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);
			bttv_dma_free(btv,buf);
			break;
		default:
			retval = -EINVAL;
			break;
		}
		up(&fh->lock);
		return retval;
	}

        case BTTV_VERSION:
        case VIDIOCGFREQ:
        case VIDIOCSFREQ:
        case VIDIOCGTUNER:
        case VIDIOCSTUNER:
        case VIDIOCGCHAN:
        case VIDIOCSCHAN:
	case VIDIOCGAUDIO:
	case VIDIOCSAUDIO:
		return bttv_common_ioctls(btv,cmd,arg);


#ifdef HAVE_V4L2
	/* ***  v4l2  *** ************************************************ */
	case VIDIOC_QUERYCAP:
	{
		struct v4l2_capability *cap = arg;

		if (0 == v4l2)
			return -EINVAL;
                strcpy(cap->name,btv->video_dev.name);
		cap->type = V4L2_TYPE_CAPTURE;
		cap->flags = V4L2_FLAG_TUNER | V4L2_FLAG_PREVIEW
			| V4L2_FLAG_READ | V4L2_FLAG_SELECT;
		if (mmap)
			cap->flags |= V4L2_FLAG_STREAMING;
		cap->inputs = bttv_tvcards[btv->type].video_inputs;
		cap->outputs = 0;
		cap->audios = bttv_tvcards[btv->type].audio_inputs;
		cap->maxwidth = bttv_tvnorms[btv->tvnorm].swidth;
		cap->maxheight = bttv_tvnorms[btv->tvnorm].sheight;
		cap->minwidth = 48;
		cap->minheight = 32;
		cap->maxframerate = 30;
		return 0;
	}

	case VIDIOC_ENUM_PIXFMT:
	case VIDIOC_ENUM_FBUFFMT:
	{
		struct v4l2_fmtdesc *f = arg;
		int i, index;

		index = -1;
		for (i = 0; i < BTTV_FORMATS; i++) {
			if (bttv_formats[i].fourcc != -1)
				index++;
			if (index == f->index)
				break;
		}
		if (BTTV_FORMATS == i)
			return -EINVAL;
		if (cmd == VIDIOC_ENUM_FBUFFMT &&
		    0 == (bttv_formats[i].flags & FORMAT_FLAGS_PACKED))
			return -EINVAL;
		memset(f,0,sizeof(*f));
		f->index = index;
		strncpy(f->description,bttv_formats[i].name,31);
		f->pixelformat = bttv_formats[i].fourcc;
		f->depth = bttv_formats[i].depth;
		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;
}

Generated by GNU enscript 1.6.4.