Enscript Output

extractedLnx/linux-2.5.54/drivers/media/video/zr36067.c_do_zoran_ioctl.c

static int do_zoran_ioctl(struct zoran *zr, unsigned int cmd,
		       void *arg)
{
	switch (cmd) {

	case VIDIOCGCAP:
		{
			struct video_capability b;
			DEBUG2(printk("%s: ioctl VIDIOCGCAP\n", zr->name));

			strncpy(b.name, zr->video_dev.name,
				sizeof(b.name));
			b.type =
			    VID_TYPE_CAPTURE | VID_TYPE_OVERLAY |
			    VID_TYPE_CLIPPING | VID_TYPE_FRAMERAM |
			    VID_TYPE_SCALES;
			/* theoretically we could also flag VID_TYPE_SUBCAPTURE
			   but this is not even implemented in the BTTV driver */

			if (zr->card == DC10 || zr->card == DC10plus) {
				b.channels = 3;	/* composite, svhs, internal */
			} else {
				b.channels = 2;	/* composite, svhs */
			}
			b.audios = 0;
			b.maxwidth = BUZ_MAX_WIDTH;
			b.maxheight = BUZ_MAX_HEIGHT;
			b.minwidth = BUZ_MIN_WIDTH;
			b.minheight = BUZ_MIN_HEIGHT;
			if (copy_to_user(arg, &b, sizeof(b))) {
				return -EFAULT;
			}
			return 0;
		}
		break;

	case VIDIOCGCHAN:
		{
			struct video_channel v;

			if (copy_from_user(&v, arg, sizeof(v))) {
				return -EFAULT;
			}
			DEBUG2(printk
			       ("%s: ioctl VIDIOCGCHAN for channel %d\n",
				zr->name, v.channel));
			switch (v.channel) {
			case 0:
				strcpy(v.name, "Composite");
				break;
			case 1:
				strcpy(v.name, "SVHS");
				break;
			case 2:
				if (zr->card == DC10
				    || zr->card == DC10plus) {
					strcpy(v.name, "Internal/comp");
					break;
				}
			default:
				DEBUG1(printk(KERN_ERR
				       "%s: VIDIOCGCHAN on not existing channel %d\n",
				       zr->name, v.channel));
				return -EINVAL;
			}
			v.tuners = 0;
			v.flags = 0;
			v.type = VIDEO_TYPE_CAMERA;
			v.norm = zr->params.norm;
			if (copy_to_user(arg, &v, sizeof(v))) {
				return -EFAULT;
			}
			return 0;
		}
		break;

		/* RJ: the documentation at http://roadrunner.swansea.linux.org.uk/v4lapi.shtml says:

		 * "The VIDIOCSCHAN ioctl takes an integer argument and switches the capture to this input."
		 *                                 ^^^^^^^
		 * The famos BTTV driver has it implemented with a struct video_channel argument
		 * and we follow it for compatibility reasons
		 *
		 * BTW: this is the only way the user can set the norm!
		 */

	case VIDIOCSCHAN:
		{
			struct video_channel v;
			int input;
			int on, res;
			int encoder_norm;

			if (copy_from_user(&v, arg, sizeof(v))) {
				return -EFAULT;
			}

			if (zr->codec_mode != BUZ_MODE_IDLE) {
				if (v.norm != zr->params.norm
				    || v.channel != zr->params.input) {
					DEBUG1(printk(KERN_ERR
					       "%s: VIDIOCSCHAN called while the card in capture/playback mode\n",
					       zr->name));
					return -EINVAL;
				} else {
					DEBUG1(printk(BUZ_WARNING
					       "%s: Warning: VIDIOCSCHAN called while the card in capture/playback mode\n",
					       zr->name));
				}
			}

			DEBUG2(printk
			       ("%s: ioctl VIDIOCSCHAN: channel=%d, norm=%d\n",
				zr->name, v.channel, v.norm));
			switch (v.channel) {
			case 0:
				if (zr->card == BUZ)
					input = 3;
				else
					input = 0;
				break;
			case 1:
				input = 7;
				break;
			case 2:
				if (zr->card == DC10
				    || zr->card == DC10plus) {
					input = 5;
					break;
				}
			default:
				DEBUG1(printk(KERN_ERR
				       "%s: VIDIOCSCHAN on not existing channel %d\n",
				       zr->name, v.channel));
				return -EINVAL;
				break;
			}

			if (lock_norm && v.norm != zr->params.norm) {
				if (lock_norm > 1) {
					DEBUG1(printk(KERN_WARNING
					       "%s: VIDIOCSCHAN: TV standard is locked, can not switch norm.\n",
					       zr->name));
					return -EINVAL;
				} else {
					DEBUG1(printk(KERN_WARNING
					       "%s: VIDIOCSCHAN: TV standard is locked, norm was not changed.\n",
					       zr->name));
					v.norm = zr->params.norm;
				}
			}
			
			if(v.norm >= 2)
				return -EINVAL;

			if (!cardnorms[zr->card][v.norm]) {
				DEBUG1(printk(KERN_ERR
				       "%s: VIDIOCSCHAN with not supported norm %d\n",
				       zr->name, v.norm));
				return -EOPNOTSUPP;
				break;
			}
			encoder_norm = v.norm;

			zr->params.norm = v.norm;
			zr->params.input = v.channel;
			zr->timing = cardnorms[zr->card][zr->params.norm];

			/* We switch overlay off and on since a change in the norm
			   needs different VFE settings */

			on = zr->v4l_overlay_active
			    && !zr->v4l_memgrab_active;
			if (on)
				zr36057_overlay(zr, 0);

			set_videobus_enable(zr, 0);
			i2c_control_device(&zr->i2c,
					   I2C_DRIVERID_VIDEODECODER,
					   DECODER_SET_INPUT, &input);
			i2c_control_device(&zr->i2c,
					   I2C_DRIVERID_VIDEODECODER,
					   DECODER_SET_NORM,
					   &zr->params.norm);
			i2c_control_device(&zr->i2c,
					   I2C_DRIVERID_VIDEOENCODER,
					   ENCODER_SET_NORM,
					   &encoder_norm);
			set_videobus_enable(zr, 1);

			if (on)
				zr36057_overlay(zr, 1);

			/* Make sure the changes come into effect */
			res = wait_grab_pending(zr);
			if (res)
				return res;

			return 0;
		}
		break;

	case VIDIOCGTUNER:
		{
			DEBUG1(printk(KERN_ERR
			       "%s: ioctl VIDIOCGTUNER not supported\n",
			       zr->name));
			return -EINVAL;
		}
		break;

	case VIDIOCSTUNER:
		{
			DEBUG1(printk(KERN_ERR
			       "%s: ioctl VIDIOCSTUNER not supported\n",
			       zr->name));
			return -EINVAL;
		}
		break;

	case VIDIOCGPICT:
		{
			struct video_picture p = zr->picture;

			DEBUG2(printk
			       ("%s: ioctl VIDIOCGPICT\n", zr->name));
			p.depth = zr->buffer.depth;
			switch (zr->buffer.depth) {
			case 15:
				p.palette = VIDEO_PALETTE_RGB555;
				break;

			case 16:
				p.palette = VIDEO_PALETTE_RGB565;
				break;

			case 24:
				p.palette = VIDEO_PALETTE_RGB24;
				break;

			case 32:
				p.palette = VIDEO_PALETTE_RGB32;
				break;
			}

			if (copy_to_user(arg, &p, sizeof(p))) {
				return -EFAULT;
			}
			return 0;
		}
		break;

	case VIDIOCSPICT:
		{
			struct video_picture p;

			if (copy_from_user(&p, arg, sizeof(p))) {
				return -EFAULT;
			}
			i2c_control_device(&zr->i2c,
					   I2C_DRIVERID_VIDEODECODER,
					   DECODER_SET_PICTURE, &p);
			DEBUG2(printk
			       ("%s: ioctl VIDIOCSPICT bri=%d hue=%d col=%d con=%d dep=%d pal=%d\n",
				zr->name, p.brightness, p.hue, p.colour,
				p.contrast, p.depth, p.palette));
			/* The depth and palette values have no meaning to us,
			   should we return  -EINVAL if they don't fit ? */
			zr->picture = p;
			return 0;
		}
		break;

	case VIDIOCCAPTURE:
		{
			int v, res;

			if (copy_from_user(&v, arg, sizeof(v))) {
				return -EFAULT;
			}
			DEBUG2(printk
			       ("%s: ioctl VIDIOCCAPTURE: %d\n", zr->name,
				v));

			/* If there is nothing to do, return immediatly */

			if ((v && zr->v4l_overlay_active)
			    || (!v && !zr->v4l_overlay_active))
				return 0;

			if (v == 0) {
				zr->v4l_overlay_active = 0;
				if (!zr->v4l_memgrab_active)
					zr36057_overlay(zr, 0);
				/* When a grab is running, the video simply won't be switched on any more */
			} else {
				if (!zr->buffer_set || !zr->window_set) {
					DEBUG1(printk(KERN_ERR
					       "%s: VIDIOCCAPTURE: buffer or window not set\n",
					       zr->name));
					return -EINVAL;
				}
				zr->v4l_overlay_active = 1;
				if (!zr->v4l_memgrab_active)
					zr36057_overlay(zr, 1);
				/* When a grab is running, the video will be switched on when grab is finished */
			}
			/* Make sure the changes come into effect */
			res = wait_grab_pending(zr);
			if (res)
				return res;
			return 0;
		}
		break;

	case VIDIOCGWIN:
		{
			DEBUG2(printk("%s: ioctl VIDIOCGWIN\n", zr->name));
			if (copy_to_user
			    (arg, &zr->window, sizeof(zr->window))) {
				return -EFAULT;
			}
			return 0;
		}
		break;

	case VIDIOCSWIN:
		{
			struct video_clip *vcp;
			struct video_window vw;
			struct tvnorm *tvn;
			int on, end, res, Wa, Ha;

			tvn = zr->timing;

			Wa = tvn->Wa;
			Ha = tvn->Ha;

			if (copy_from_user(&vw, arg, sizeof(vw))) {
				return -EFAULT;
			}

			DEBUG2(printk
			       ("%s: ioctl VIDIOCSWIN: x=%d y=%d w=%d h=%d clipcount=%d\n",
				zr->name, vw.x, vw.y, vw.width, vw.height,
				vw.clipcount));

			if (!zr->buffer_set) {
				DEBUG1(printk(KERN_ERR
				       "%s: VIDIOCSWIN: frame buffer has to be set first\n",
				       zr->name));
				return -EINVAL;
			}

			/*
			 * The video front end needs 4-byte alinged line sizes, we correct that
			 * silently here if necessary
			 */

			if (zr->buffer.depth == 15
			    || zr->buffer.depth == 16) {
				end = (vw.x + vw.width) & ~1;	/* round down */
				vw.x = (vw.x + 1) & ~1;	/* round up */
				vw.width = end - vw.x;
			}

			if (zr->buffer.depth == 24) {
				end = (vw.x + vw.width) & ~3;	/* round down */
				vw.x = (vw.x + 3) & ~3;	/* round up */
				vw.width = end - vw.x;
			}

			if (vw.width > Wa)
				vw.width = Wa;
			if (vw.height > Ha)
				vw.height = Ha;

			/* Check for vaild parameters */
			if (vw.width < BUZ_MIN_WIDTH
			    || vw.height < BUZ_MIN_HEIGHT
			    || vw.width > BUZ_MAX_WIDTH
			    || vw.height > BUZ_MAX_HEIGHT) {
				DEBUG1(printk(KERN_ERR
				       "%s: VIDIOCSWIN: width = %d or height = %d invalid\n",
				       zr->name, vw.width, vw.height));
				return -EINVAL;
			}

			zr->window.x = vw.x;
			zr->window.y = vw.y;
			zr->window.width = vw.width;
			zr->window.height = vw.height;
			zr->window.chromakey = 0;
			zr->window.flags = 0;	// RJ: Is this intended for interlace on/off ?
			zr->window.clips = NULL;

			/*
			 * If an overlay is running, we have to switch it off
			 * and switch it on again in order to get the new settings in effect.
			 *
			 * We also want to avoid that the overlay mask is written
			 * when an overlay is running.
			 */

			on = zr->v4l_overlay_active
			    && !zr->v4l_memgrab_active;
			if (on)
				zr36057_overlay(zr, 0);

			/*
			 *   Write the overlay mask if clips are wanted.
			 */
			 
			if (vw.clipcount < 0 || vw.clipcount > 2048)
				return -EINVAL;
			if (vw.clipcount) {
				vcp =
				    vmalloc(sizeof(struct video_clip) *
					    (vw.clipcount + 4));
				if (vcp == NULL) {
					printk(KERN_ERR
					       "%s: zoran_ioctl: Alloc of clip mask failed\n",
					       zr->name);
					return -ENOMEM;
				}
				if (copy_from_user
				    (vcp, vw.clips,
				     sizeof(struct video_clip) *
				     vw.clipcount)) {
					vfree(vcp);
					return -EFAULT;
				}
				write_overlay_mask(zr, vcp, vw.clipcount);
				vfree(vcp);
			}
			zr->window.clipcount = vw.clipcount;
	
			if (on)
				zr36057_overlay(zr, 1);
			zr->window_set = 1;

			/* Make sure the changes come into effect */
			res = wait_grab_pending(zr);
			if (res)
				return res;

			return 0;
		}
		break;

	case VIDIOCGFBUF:
		{
			DEBUG2(printk
			       ("%s: ioctl VIDIOCGFBUF\n", zr->name));
			if (copy_to_user
			    (arg, &zr->buffer, sizeof(zr->buffer))) {
				return -EFAULT;
			}
			return 0;
		}
		break;

	case VIDIOCSFBUF:
		{
			struct video_buffer v;

			/* RJ: Isn't this too restrictive? As long as the user doesn't set
			   the base address it shouldn't be too dangerous */

			if (!capable(CAP_SYS_ADMIN)) {
				DEBUG1(printk(KERN_ERR
				       "%s: Only the superuser may issue VIDIOCSFBUF ioctl\n",
				       zr->name));
				return -EPERM;
			}
			if (copy_from_user(&v, arg, sizeof(v))) {
				return -EFAULT;
			}
			DEBUG2(printk
			       ("%s: ioctl VIDIOCSFBUF: base=0x%x w=%d h=%d depth=%d bpl=%d\n",
				zr->name, (u32) v.base, v.width, v.height,
				v.depth, v.bytesperline));
			if (zr->v4l_overlay_active) {
				/* Has the user gotten crazy ... ? */
				DEBUG1(printk(KERN_ERR
				       "%s: VIDIOCSFBUF not allowed when overlay active\n",
				       zr->name));
				return -EINVAL;
			}
			if (v.depth != 15 && v.depth != 16 && v.depth != 24
			    && v.depth != 32) {
				DEBUG1(printk(KERN_ERR
				       "%s: VIDIOCSFBUF: depth=%d not supported\n",
				       zr->name, v.depth));
				return -EINVAL;
			}
			if (v.height <= 0 || v.width <= 0
			    || v.bytesperline <= 0) {
				DEBUG1(printk(KERN_ERR
				       "%s: VIDIOCSFBUF: invalid height/width/bpl value\n",
				       zr->name));
				return -EINVAL;
			}
			if (v.bytesperline & 3) {
				DEBUG1(printk(KERN_ERR
				       "%s: VIDIOCSFBUF: bytesperline must be 4-byte aligned\n",
				       zr->name));
				return -EINVAL;
			}
			if (v.base) {
				zr->buffer.base =
				    (void *) ((unsigned long) v.base & ~3);
			}
			zr->buffer.height = v.height;
			zr->buffer.width = v.width;
			zr->buffer.depth = v.depth;
			zr->buffer.bytesperline = v.bytesperline;

			if (zr->buffer.base)
				zr->buffer_set = 1;
			zr->window_set = 0;	/* The user should set new window parameters */
			return 0;
		}
		break;

		/* RJ: what is VIDIOCKEY intended to do ??? */

	case VIDIOCKEY:
		{
			/* Will be handled higher up .. */
			DEBUG2(printk("%s: ioctl VIDIOCKEY\n", zr->name));
			return 0;
		}
		break;

	case VIDIOCGFREQ:
		{
			DEBUG1(printk(KERN_ERR
			       "%s: ioctl VIDIOCGFREQ not supported\n",
			       zr->name));
			return -EINVAL;
		}
		break;

	case VIDIOCSFREQ:
		{
			DEBUG1(printk(KERN_ERR
			       "%s: ioctl VIDIOCSFREQ not supported\n",
			       zr->name));
			return -EINVAL;
		}
		break;

	case VIDIOCGAUDIO:
		{
			DEBUG1(printk(KERN_ERR
			       "%s: ioctl VIDIOCGAUDIO not supported\n",
			       zr->name));
			return -EINVAL;
		}
		break;

	case VIDIOCSAUDIO:
		{
			DEBUG1(printk(KERN_ERR
			       "%s: ioctl VIDIOCSAUDIO not supported\n",
			       zr->name));
			return -EINVAL;
		}
		break;

	case VIDIOCSYNC:
		{
			int v;

			if (copy_from_user(&v, arg, sizeof(v))) {
				return -EFAULT;
			}
			DEBUG3(printk
			       ("%s: ioctl VIDIOCSYNC %d\n", zr->name, v));
			return v4l_sync(zr, v);
		}
		break;

	case VIDIOCMCAPTURE:
		{
			struct video_mmap vm;

			if (copy_from_user
			    ((void *) &vm, (void *) arg, sizeof(vm))) {
				return -EFAULT;
			}
			DEBUG2(printk
			       ("%s: ioctl VIDIOCMCAPTURE frame=%d geom=%dx%d fmt=%d\n",
				zr->name, vm.frame, vm.width, vm.height,
				vm.format));
			return v4l_grab(zr, &vm);
		}
		break;

	case VIDIOCGMBUF:
		{
			struct video_mbuf vm;
			int i;

			DEBUG2(printk
			       ("%s: ioctl VIDIOCGMBUF\n", zr->name));
			vm.size = v4l_nbufs * v4l_bufsize;
			vm.frames = v4l_nbufs;
			for (i = 0; i < v4l_nbufs; i++) {
				vm.offsets[i] = i * v4l_bufsize;
			}

			/* The next mmap will map the V4L buffers */
			zr->map_mjpeg_buffers = 0;

			if (copy_to_user(arg, &vm, sizeof(vm))) {
				return -EFAULT;
			}
			return 0;
		}
		break;

	case VIDIOCGUNIT:
		{
			struct video_unit vu;

			DEBUG2(printk
			       ("%s: ioctl VIDIOCGUNIT\n", zr->name));
			vu.video = zr->video_dev.minor;
			vu.vbi = VIDEO_NO_UNIT;
			vu.radio = VIDEO_NO_UNIT;
			vu.audio = VIDEO_NO_UNIT;
			vu.teletext = VIDEO_NO_UNIT;
			if (copy_to_user(arg, &vu, sizeof(vu))) {
				return -EFAULT;
			}
			return 0;
		}
		break;

		/*
		 * RJ: In principal we could support subcaptures for V4L grabbing.
		 *     Not even the famous BTTV driver has them, however.
		 *     If there should be a strong demand, one could consider
		 *     to implement them.
		 */
	case VIDIOCGCAPTURE:
		{
			DEBUG1(printk(KERN_ERR
			       "%s: ioctl VIDIOCGCAPTURE not supported\n",
			       zr->name));
			return -EINVAL;
		}
		break;

	case VIDIOCSCAPTURE:
		{
			DEBUG1(printk(KERN_ERR
			       "%s: ioctl VIDIOCSCAPTURE not supported\n",
			       zr->name));
			return -EINVAL;
		}
		break;

	case BUZIOC_G_PARAMS:
		{
			DEBUG2(printk
			       ("%s: ioctl BUZIOC_G_PARAMS\n", zr->name));

			if (copy_to_user
			    (arg, &(zr->params), sizeof(zr->params))) {
				return -EFAULT;
			}
			return 0;
		}
		break;

	case BUZIOC_S_PARAMS:
		{
			struct zoran_params bp;
			/* int input, on; */

			if (zr->codec_mode != BUZ_MODE_IDLE) {
				DEBUG1(printk(KERN_ERR
				       "%s: BUZIOC_S_PARAMS called but Buz in capture/playback mode\n",
				       zr->name));
				return -EINVAL;
			}

			if (copy_from_user(&bp, arg, sizeof(bp))) {
				return -EFAULT;
			}
			DEBUG2(printk
			       ("%s: ioctl BUZIOC_S_PARAMS\n", zr->name));

			/* Check the params first before overwriting our internal values */

			if (zoran_check_params(zr, &bp))
				return -EINVAL;

			zr->params = bp;

			/* Make changes of input and norm go into effect immediatly */

			/* We switch overlay off and on since a change in the norm
			   needs different VFE settings */

			if (copy_to_user(arg, &bp, sizeof(bp))) {
				return -EFAULT;
			}
			return 0;
		}
		break;

	case BUZIOC_REQBUFS:
		{
			struct zoran_requestbuffers br;

			if (zr->jpg_buffers_allocated) {
				DEBUG1(printk(KERN_ERR
				       "%s: BUZIOC_REQBUFS: buffers allready allocated\n",
				       zr->name));
				return -EINVAL;
			}
			if (copy_from_user(&br, arg, sizeof(br))) {
				return -EFAULT;
			}
			DEBUG2(printk
			       ("%s: ioctl BUZIOC_REQBUFS count = %lu size=%lu\n",
				zr->name, br.count, br.size));

			/* Enforce reasonable lower and upper limits */
			if (br.count < 4)
				br.count = 4;	/* Could be choosen smaller */
			if (br.count > BUZ_MAX_FRAME)
				br.count = BUZ_MAX_FRAME;
			br.size = PAGE_ALIGN(br.size);
			if (br.size < 8192)
				br.size = 8192;	/* Arbitrary */
			/* br.size is limited by 1 page for the stat_com tables to a Maximum of 2 MB */
			if (br.size > (512 * 1024))
				br.size = (512 * 1024);	/* 512 K should be enough */
			if (zr->need_contiguous
			    && br.size > MAX_KMALLOC_MEM)
				br.size = MAX_KMALLOC_MEM;

			zr->jpg_nbufs = br.count;
			zr->jpg_bufsize = br.size;

			if (jpg_fbuffer_alloc(zr))
				return -ENOMEM;

			/* The next mmap will map the MJPEG buffers */
			zr->map_mjpeg_buffers = 1;

			if (copy_to_user(arg, &br, sizeof(br))) {
				return -EFAULT;
			}
			return 0;
		}
		break;

	case BUZIOC_QBUF_CAPT:
		{
			int nb;

			if (copy_from_user
			    ((void *) &nb, (void *) arg, sizeof(int))) {
				return -EFAULT;
			}
			DEBUG4(printk
			       ("%s: ioctl BUZIOC_QBUF_CAPT %d\n",
				zr->name, nb));
			return jpg_qbuf(zr, nb, BUZ_MODE_MOTION_COMPRESS);
		}
		break;

	case BUZIOC_QBUF_PLAY:
		{
			int nb;

			if (copy_from_user
			    ((void *) &nb, (void *) arg, sizeof(int))) {
				return -EFAULT;
			}
			DEBUG4(printk
			       ("%s: ioctl BUZIOC_QBUF_PLAY %d\n",
				zr->name, nb));
			return jpg_qbuf(zr, nb,
					BUZ_MODE_MOTION_DECOMPRESS);
		}
		break;

	case BUZIOC_SYNC:
		{
			struct zoran_sync bs;
			int res;

			DEBUG4(printk
			       ("%s: ioctl BUZIOC_SYNC\n", zr->name));
			res = jpg_sync(zr, &bs);
			if (copy_to_user(arg, &bs, sizeof(bs))) {
				return -EFAULT;
			}
			return res;
		}
		break;

	case BUZIOC_G_STATUS:
		{
			struct zoran_status bs;
			int norm, input, status;
			unsigned long timeout;

			if (zr->codec_mode != BUZ_MODE_IDLE) {
				DEBUG1(printk(KERN_ERR
				       "%s: BUZIOC_G_STATUS called but Buz in capture/playback mode\n",
				       zr->name));
				return -EINVAL;
			}

			if (copy_from_user(&bs, arg, sizeof(bs))) {
				return -EFAULT;
			}
			DEBUG2(printk
			       ("%s: ioctl BUZIOC_G_STATUS\n", zr->name));

			switch (bs.input) {
			case 0:
				if (zr->card == BUZ)
					input = 3;
				else
					input = 0;
				break;
			case 1:
				input = 7;
				break;
			default:
				DEBUG1(printk(KERN_ERR
				       "%s: BUZIOC_G_STATUS on not existing input %d\n",
				       zr->name, bs.input));
				return -EINVAL;
			}

			/* Set video norm to VIDEO_MODE_AUTO */

			norm = VIDEO_MODE_AUTO;
			set_videobus_enable(zr, 0);
			i2c_control_device(&zr->i2c,
					   I2C_DRIVERID_VIDEODECODER,
					   DECODER_SET_INPUT, &input);
			i2c_control_device(&zr->i2c,
					   I2C_DRIVERID_VIDEODECODER,
					   DECODER_SET_NORM, &norm);
			set_videobus_enable(zr, 1);

			/* sleep 1 second */

			timeout = jiffies + 1 * HZ;
			while (jiffies < timeout)
				schedule();

			/* Get status of video decoder */

			i2c_control_device(&zr->i2c,
					   I2C_DRIVERID_VIDEODECODER,
					   DECODER_GET_STATUS, &status);
			bs.signal = (status & DECODER_STATUS_GOOD) ? 1 : 0;

			if (status & DECODER_STATUS_NTSC)
				bs.norm = VIDEO_MODE_NTSC;
			else if (status & DECODER_STATUS_SECAM)
				bs.norm = VIDEO_MODE_SECAM;
			else
				bs.norm = VIDEO_MODE_PAL;

			bs.color = (status & DECODER_STATUS_COLOR) ? 1 : 0;

			/* restore previous input and norm */
			if (zr->card == BUZ)
				input = zr->params.input == 0 ? 3 : 7;
			else
				input = zr->params.input == 0 ? 0 : 7;
			set_videobus_enable(zr, 0);
			i2c_control_device(&zr->i2c,
					   I2C_DRIVERID_VIDEODECODER,
					   DECODER_SET_INPUT, &input);
			i2c_control_device(&zr->i2c,
					   I2C_DRIVERID_VIDEODECODER,
					   DECODER_SET_NORM,
					   &zr->params.norm);
			set_videobus_enable(zr, 1);

			if (copy_to_user(arg, &bs, sizeof(bs))) {
				return -EFAULT;
			}
			return 0;
		}
		break;

	default:
		DEBUG1(printk
		       ("%s: UNKNOWN ioctl cmd: 0x%x\n", zr->name, cmd));
		return -ENOIOCTLCMD;
	}
	return 0;
}

Generated by GNU enscript 1.6.4.