Enscript Output

extractedLnx/linux-2.6.15/drivers/usb/media/sn9c102_core.c_sn9c102_ioctl_v4l2.c

static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp,
                              unsigned int cmd, void __user * arg)
{
	struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp));

	switch (cmd) {

	case VIDIOC_QUERYCAP:
	{
		struct v4l2_capability cap = {
			.driver = "sn9c102",
			.version = SN9C102_MODULE_VERSION_CODE,
			.capabilities = V4L2_CAP_VIDEO_CAPTURE | 
			                V4L2_CAP_READWRITE |
			                V4L2_CAP_STREAMING,
		};

		strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card));
		if (usb_make_path(cam->usbdev, cap.bus_info,
		    sizeof(cap.bus_info)) < 0)
			strlcpy(cap.bus_info, cam->dev.bus_id,
			        sizeof(cap.bus_info));

		if (copy_to_user(arg, &cap, sizeof(cap)))
			return -EFAULT;

		return 0;
	}

	case VIDIOC_ENUMINPUT:
	{
		struct v4l2_input i;

		if (copy_from_user(&i, arg, sizeof(i)))
			return -EFAULT;

		if (i.index)
			return -EINVAL;

		memset(&i, 0, sizeof(i));
		strcpy(i.name, "USB");

		if (copy_to_user(arg, &i, sizeof(i)))
			return -EFAULT;

		return 0;
	}

	case VIDIOC_G_INPUT:
	case VIDIOC_S_INPUT:
	{
		int index;

		if (copy_from_user(&index, arg, sizeof(index)))
			return -EFAULT;

		if (index != 0)
			return -EINVAL;

		return 0;
	}

	case VIDIOC_QUERYCTRL:
	{
		struct sn9c102_sensor* s = cam->sensor;
		struct v4l2_queryctrl qc;
		u8 i, n;

		if (copy_from_user(&qc, arg, sizeof(qc)))
			return -EFAULT;

		n = sizeof(s->qctrl) / sizeof(s->qctrl[0]);
		for (i = 0; i < n; i++)
			if (qc.id && qc.id == s->qctrl[i].id) {
				memcpy(&qc, &(s->qctrl[i]), sizeof(qc));
				if (copy_to_user(arg, &qc, sizeof(qc)))
					return -EFAULT;
				return 0;
			}

		return -EINVAL;
	}

	case VIDIOC_G_CTRL:
	{
		struct sn9c102_sensor* s = cam->sensor;
		struct v4l2_control ctrl;
		int err = 0;

		if (!s->get_ctrl)
			return -EINVAL;

		if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
			return -EFAULT;

		err = s->get_ctrl(cam, &ctrl);

		if (copy_to_user(arg, &ctrl, sizeof(ctrl)))
			return -EFAULT;

		return err;
	}

	case VIDIOC_S_CTRL_OLD:
	case VIDIOC_S_CTRL:
	{
		struct sn9c102_sensor* s = cam->sensor;
		struct v4l2_control ctrl;
		u8 i, n;
		int err = 0;

		if (!s->set_ctrl)
			return -EINVAL;

		if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
			return -EFAULT;

		n = sizeof(s->qctrl) / sizeof(s->qctrl[0]);
		for (i = 0; i < n; i++)
			if (ctrl.id == s->qctrl[i].id) {
				if (ctrl.value < s->qctrl[i].minimum ||
				    ctrl.value > s->qctrl[i].maximum)
					return -ERANGE;
				ctrl.value -= ctrl.value % s->qctrl[i].step;
				break;
			}

		if ((err = s->set_ctrl(cam, &ctrl)))
			return err;

		s->_qctrl[i].default_value = ctrl.value;

		PDBGG("VIDIOC_S_CTRL: id %lu, value %lu",
		      (unsigned long)ctrl.id, (unsigned long)ctrl.value)

		return 0;
	}

	case VIDIOC_CROPCAP:
	{
		struct v4l2_cropcap* cc = &(cam->sensor->cropcap);

		cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		cc->pixelaspect.numerator = 1;
		cc->pixelaspect.denominator = 1;

		if (copy_to_user(arg, cc, sizeof(*cc)))
			return -EFAULT;

		return 0;
	}

	case VIDIOC_G_CROP:
	{
		struct sn9c102_sensor* s = cam->sensor;
		struct v4l2_crop crop = {
			.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
		};

		memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect));

		if (copy_to_user(arg, &crop, sizeof(crop)))
			return -EFAULT;

		return 0;
	}

	case VIDIOC_S_CROP:
	{
		struct sn9c102_sensor* s = cam->sensor;
		struct v4l2_crop crop;
		struct v4l2_rect* rect;
		struct v4l2_rect* bounds = &(s->cropcap.bounds);
		struct v4l2_pix_format* pix_format = &(s->pix_format);
		u8 scale;
		const enum sn9c102_stream_state stream = cam->stream;
		const u32 nbuffers = cam->nbuffers;
		u32 i;
		int err = 0;

		if (copy_from_user(&crop, arg, sizeof(crop)))
			return -EFAULT;

		rect = &(crop.c);

		if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
			return -EINVAL;

		if (cam->module_param.force_munmap)
			for (i = 0; i < cam->nbuffers; i++)
				if (cam->frame[i].vma_use_count) {
					DBG(3, "VIDIOC_S_CROP failed. "
					       "Unmap the buffers first.")
					return -EINVAL;
				}

		/* Preserve R,G or B origin */
		rect->left = (s->_rect.left & 1L) ?
		             rect->left | 1L : rect->left & ~1L;
		rect->top = (s->_rect.top & 1L) ?
		            rect->top | 1L : rect->top & ~1L;

		if (rect->width < 16)
			rect->width = 16;
		if (rect->height < 16)
			rect->height = 16;
		if (rect->width > bounds->width)
			rect->width = bounds->width;
		if (rect->height > bounds->height)
			rect->height = bounds->height;
		if (rect->left < bounds->left)
			rect->left = bounds->left;
		if (rect->top < bounds->top)
			rect->top = bounds->top;
		if (rect->left + rect->width > bounds->left + bounds->width)
			rect->left = bounds->left+bounds->width - rect->width;
		if (rect->top + rect->height > bounds->top + bounds->height)
			rect->top = bounds->top+bounds->height - rect->height;

		rect->width &= ~15L;
		rect->height &= ~15L;

		if (SN9C102_PRESERVE_IMGSCALE) {
			/* Calculate the actual scaling factor */
			u32 a, b;
			a = rect->width * rect->height;
			b = pix_format->width * pix_format->height;
			scale = b ? (u8)((a / b) < 4 ? 1 :
		                        ((a / b) < 16 ? 2 : 4)) : 1;
		} else
			scale = 1;

		if (cam->stream == STREAM_ON)
			if ((err = sn9c102_stream_interrupt(cam)))
				return err;

		if (copy_to_user(arg, &crop, sizeof(crop))) {
			cam->stream = stream;
			return -EFAULT;
		}

		if (cam->module_param.force_munmap || cam->io == IO_READ)
			sn9c102_release_buffers(cam);

		err = sn9c102_set_crop(cam, rect);
		if (s->set_crop)
			err += s->set_crop(cam, rect);
		err += sn9c102_set_scale(cam, scale);

		if (err) { /* atomic, no rollback in ioctl() */
			cam->state |= DEV_MISCONFIGURED;
			DBG(1, "VIDIOC_S_CROP failed because of hardware "
			       "problems. To use the camera, close and open "
			       "/dev/video%d again.", cam->v4ldev->minor)
			return -EIO;
		}

		s->pix_format.width = rect->width/scale;
		s->pix_format.height = rect->height/scale;
		memcpy(&(s->_rect), rect, sizeof(*rect));

		if ((cam->module_param.force_munmap || cam->io == IO_READ) &&
		    nbuffers != sn9c102_request_buffers(cam, nbuffers,
		                                        cam->io)) {
			cam->state |= DEV_MISCONFIGURED;
			DBG(1, "VIDIOC_S_CROP failed because of not enough "
			       "memory. To use the camera, close and open "
			       "/dev/video%d again.", cam->v4ldev->minor)
			return -ENOMEM;
		}

		cam->stream = stream;

		return 0;
	}

	case VIDIOC_ENUM_FMT:
	{
		struct v4l2_fmtdesc fmtd;

		if (copy_from_user(&fmtd, arg, sizeof(fmtd)))
			return -EFAULT;

		if (fmtd.index == 0) {
			strcpy(fmtd.description, "bayer rgb");
			fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8;
		} else if (fmtd.index == 1) {
			strcpy(fmtd.description, "compressed");
			fmtd.pixelformat = V4L2_PIX_FMT_SN9C10X;
			fmtd.flags = V4L2_FMT_FLAG_COMPRESSED;
		} else
			return -EINVAL;

		fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		memset(&fmtd.reserved, 0, sizeof(fmtd.reserved));

		if (copy_to_user(arg, &fmtd, sizeof(fmtd)))
			return -EFAULT;

		return 0;
	}

	case VIDIOC_G_FMT:
	{
		struct v4l2_format format;
		struct v4l2_pix_format* pfmt = &(cam->sensor->pix_format);

		if (copy_from_user(&format, arg, sizeof(format)))
			return -EFAULT;

		if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
			return -EINVAL;

		pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_SN9C10X)
		                     ? 0 : (pfmt->width * pfmt->priv) / 8;
		pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8);
		pfmt->field = V4L2_FIELD_NONE;
		memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt));

		if (copy_to_user(arg, &format, sizeof(format)))
			return -EFAULT;

		return 0;
	}

	case VIDIOC_TRY_FMT:
	case VIDIOC_S_FMT:
	{
		struct sn9c102_sensor* s = cam->sensor;
		struct v4l2_format format;
		struct v4l2_pix_format* pix;
		struct v4l2_pix_format* pfmt = &(s->pix_format);
		struct v4l2_rect* bounds = &(s->cropcap.bounds);
		struct v4l2_rect rect;
		u8 scale;
		const enum sn9c102_stream_state stream = cam->stream;
		const u32 nbuffers = cam->nbuffers;
		u32 i;
		int err = 0;

		if (copy_from_user(&format, arg, sizeof(format)))
			return -EFAULT;

		pix = &(format.fmt.pix);

		if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
			return -EINVAL;

		memcpy(&rect, &(s->_rect), sizeof(rect));

		{ /* calculate the actual scaling factor */
			u32 a, b;
			a = rect.width * rect.height;
			b = pix->width * pix->height;
			scale = b ? (u8)((a / b) < 4 ? 1 :
		                        ((a / b) < 16 ? 2 : 4)) : 1;
		}

		rect.width = scale * pix->width;
		rect.height = scale * pix->height;

		if (rect.width < 16)
			rect.width = 16;
		if (rect.height < 16)
			rect.height = 16;
		if (rect.width > bounds->left + bounds->width - rect.left)
			rect.width = bounds->left + bounds->width - rect.left;
		if (rect.height > bounds->top + bounds->height - rect.top)
			rect.height = bounds->top + bounds->height - rect.top;

		rect.width &= ~15L;
		rect.height &= ~15L;

		{ /* adjust the scaling factor */
			u32 a, b;
			a = rect.width * rect.height;
			b = pix->width * pix->height;
			scale = b ? (u8)((a / b) < 4 ? 1 :
		                        ((a / b) < 16 ? 2 : 4)) : 1;
		}

		pix->width = rect.width / scale;
		pix->height = rect.height / scale;

		if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X &&
		    pix->pixelformat != V4L2_PIX_FMT_SBGGR8)
			pix->pixelformat = pfmt->pixelformat;
		pix->priv = pfmt->priv; /* bpp */
		pix->colorspace = pfmt->colorspace;
		pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_SN9C10X)
		                    ? 0 : (pix->width * pix->priv) / 8;
		pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8);
		pix->field = V4L2_FIELD_NONE;

		if (cmd == VIDIOC_TRY_FMT) {
			if (copy_to_user(arg, &format, sizeof(format)))
				return -EFAULT;
			return 0;
		}

		if (cam->module_param.force_munmap)
			for (i = 0; i < cam->nbuffers; i++)
				if (cam->frame[i].vma_use_count) {
					DBG(3, "VIDIOC_S_FMT failed. "
					       "Unmap the buffers first.")
					return -EINVAL;
				}

		if (cam->stream == STREAM_ON)
			if ((err = sn9c102_stream_interrupt(cam)))
				return err;

		if (copy_to_user(arg, &format, sizeof(format))) {
			cam->stream = stream;
			return -EFAULT;
		}

		if (cam->module_param.force_munmap  || cam->io == IO_READ)
			sn9c102_release_buffers(cam);

		err += sn9c102_set_pix_format(cam, pix);
		err += sn9c102_set_crop(cam, &rect);
		if (s->set_pix_format)
			err += s->set_pix_format(cam, pix);
		if (s->set_crop)
			err += s->set_crop(cam, &rect);
		err += sn9c102_set_scale(cam, scale);

		if (err) { /* atomic, no rollback in ioctl() */
			cam->state |= DEV_MISCONFIGURED;
			DBG(1, "VIDIOC_S_FMT failed because of hardware "
			       "problems. To use the camera, close and open "
			       "/dev/video%d again.", cam->v4ldev->minor)
			return -EIO;
		}

		memcpy(pfmt, pix, sizeof(*pix));
		memcpy(&(s->_rect), &rect, sizeof(rect));

		if ((cam->module_param.force_munmap  || cam->io == IO_READ) &&
		    nbuffers != sn9c102_request_buffers(cam, nbuffers,
		                                        cam->io)) {
			cam->state |= DEV_MISCONFIGURED;
			DBG(1, "VIDIOC_S_FMT failed because of not enough "
			       "memory. To use the camera, close and open "
			       "/dev/video%d again.", cam->v4ldev->minor)
			return -ENOMEM;
		}

		cam->stream = stream;

		return 0;
	}

	case VIDIOC_G_JPEGCOMP:
	{
		if (copy_to_user(arg, &cam->compression,
		                 sizeof(cam->compression)))
			return -EFAULT;

		return 0;
	}

	case VIDIOC_S_JPEGCOMP:
	{
		struct v4l2_jpegcompression jc;
		const enum sn9c102_stream_state stream = cam->stream;
		int err = 0;

		if (copy_from_user(&jc, arg, sizeof(jc)))
			return -EFAULT;

		if (jc.quality != 0 && jc.quality != 1)
			return -EINVAL;

		if (cam->stream == STREAM_ON)
			if ((err = sn9c102_stream_interrupt(cam)))
				return err;

		err += sn9c102_set_compression(cam, &jc);
		if (err) { /* atomic, no rollback in ioctl() */
			cam->state |= DEV_MISCONFIGURED;
			DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware "
			       "problems. To use the camera, close and open "
			       "/dev/video%d again.", cam->v4ldev->minor)
			return -EIO;
		}

		cam->compression.quality = jc.quality;

		cam->stream = stream;

		return 0;
	}

	case VIDIOC_REQBUFS:
	{
		struct v4l2_requestbuffers rb;
		u32 i;
		int err;

		if (copy_from_user(&rb, arg, sizeof(rb)))
			return -EFAULT;

		if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
		    rb.memory != V4L2_MEMORY_MMAP)
			return -EINVAL;

		if (cam->io == IO_READ) {
			DBG(3, "Close and open the device again to choose "
			       "the mmap I/O method")
			return -EINVAL;
		}

		for (i = 0; i < cam->nbuffers; i++)
			if (cam->frame[i].vma_use_count) {
				DBG(3, "VIDIOC_REQBUFS failed. "
				       "Previous buffers are still mapped.")
				return -EINVAL;
			}

		if (cam->stream == STREAM_ON)
			if ((err = sn9c102_stream_interrupt(cam)))
				return err;

		sn9c102_empty_framequeues(cam);

		sn9c102_release_buffers(cam);
		if (rb.count)
			rb.count = sn9c102_request_buffers(cam, rb.count,
			                                   IO_MMAP);

		if (copy_to_user(arg, &rb, sizeof(rb))) {
			sn9c102_release_buffers(cam);
			cam->io = IO_NONE;
			return -EFAULT;
		}

		cam->io = rb.count ? IO_MMAP : IO_NONE;

		return 0;
	}

	case VIDIOC_QUERYBUF:
	{
		struct v4l2_buffer b;

		if (copy_from_user(&b, arg, sizeof(b)))
			return -EFAULT;

		if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
		    b.index >= cam->nbuffers || cam->io != IO_MMAP)
			return -EINVAL;

		memcpy(&b, &cam->frame[b.index].buf, sizeof(b));

		if (cam->frame[b.index].vma_use_count)
			b.flags |= V4L2_BUF_FLAG_MAPPED;

		if (cam->frame[b.index].state == F_DONE)
			b.flags |= V4L2_BUF_FLAG_DONE;
		else if (cam->frame[b.index].state != F_UNUSED)
			b.flags |= V4L2_BUF_FLAG_QUEUED;

		if (copy_to_user(arg, &b, sizeof(b)))
			return -EFAULT;

		return 0;
	}

	case VIDIOC_QBUF:
	{
		struct v4l2_buffer b;
		unsigned long lock_flags;

		if (copy_from_user(&b, arg, sizeof(b)))
			return -EFAULT;

		if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
		    b.index >= cam->nbuffers || cam->io != IO_MMAP)
			return -EINVAL;

		if (cam->frame[b.index].state != F_UNUSED)
			return -EINVAL;

		cam->frame[b.index].state = F_QUEUED;

		spin_lock_irqsave(&cam->queue_lock, lock_flags);
		list_add_tail(&cam->frame[b.index].frame, &cam->inqueue);
		spin_unlock_irqrestore(&cam->queue_lock, lock_flags);

		PDBGG("Frame #%lu queued", (unsigned long)b.index)

		return 0;
	}

	case VIDIOC_DQBUF:
	{
		struct v4l2_buffer b;
		struct sn9c102_frame_t *f;
		unsigned long lock_flags;
		int err = 0;

		if (copy_from_user(&b, arg, sizeof(b)))
			return -EFAULT;

		if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io!= IO_MMAP)
			return -EINVAL;

		if (list_empty(&cam->outqueue)) {
			if (cam->stream == STREAM_OFF)
				return -EINVAL;
			if (filp->f_flags & O_NONBLOCK)
				return -EAGAIN;
			err = wait_event_interruptible
			      ( cam->wait_frame, 
			        (!list_empty(&cam->outqueue)) ||
			        (cam->state & DEV_DISCONNECTED) ||
			        (cam->state & DEV_MISCONFIGURED) );
			if (err)
				return err;
			if (cam->state & DEV_DISCONNECTED)
				return -ENODEV;
			if (cam->state & DEV_MISCONFIGURED)
				return -EIO;
		}

		spin_lock_irqsave(&cam->queue_lock, lock_flags);
		f = list_entry(cam->outqueue.next, struct sn9c102_frame_t,
		               frame);
		list_del(cam->outqueue.next);
		spin_unlock_irqrestore(&cam->queue_lock, lock_flags);

		f->state = F_UNUSED;

		memcpy(&b, &f->buf, sizeof(b));
		if (f->vma_use_count)
			b.flags |= V4L2_BUF_FLAG_MAPPED;

		if (copy_to_user(arg, &b, sizeof(b)))
			return -EFAULT;

		PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index)

		return 0;
	}

	case VIDIOC_STREAMON:
	{
		int type;

		if (copy_from_user(&type, arg, sizeof(type)))
			return -EFAULT;

		if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
			return -EINVAL;

		if (list_empty(&cam->inqueue))
			return -EINVAL;

		cam->stream = STREAM_ON;

		DBG(3, "Stream on")

		return 0;
	}

	case VIDIOC_STREAMOFF:
	{
		int type, err;

		if (copy_from_user(&type, arg, sizeof(type)))
			return -EFAULT;

		if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
			return -EINVAL;

		if (cam->stream == STREAM_ON)
			if ((err = sn9c102_stream_interrupt(cam)))
				return err;

		sn9c102_empty_framequeues(cam);

		DBG(3, "Stream off")

		return 0;
	}

	case VIDIOC_G_PARM:
	{
		struct v4l2_streamparm sp;

		if (copy_from_user(&sp, arg, sizeof(sp)))
			return -EFAULT;

		if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
			return -EINVAL;

		sp.parm.capture.extendedmode = 0;
		sp.parm.capture.readbuffers = cam->nreadbuffers;

		if (copy_to_user(arg, &sp, sizeof(sp)))
			return -EFAULT;

		return 0;
	}

	case VIDIOC_S_PARM_OLD:
	case VIDIOC_S_PARM:
	{
		struct v4l2_streamparm sp;

		if (copy_from_user(&sp, arg, sizeof(sp)))
			return -EFAULT;

		if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
			return -EINVAL;

		sp.parm.capture.extendedmode = 0;

		if (sp.parm.capture.readbuffers == 0)
			sp.parm.capture.readbuffers = cam->nreadbuffers;

		if (sp.parm.capture.readbuffers > SN9C102_MAX_FRAMES)
			sp.parm.capture.readbuffers = SN9C102_MAX_FRAMES;

		if (copy_to_user(arg, &sp, sizeof(sp)))
			return -EFAULT;

		cam->nreadbuffers = sp.parm.capture.readbuffers;

		return 0;
	}

	case VIDIOC_G_STD:
	case VIDIOC_S_STD:
	case VIDIOC_QUERYSTD:
	case VIDIOC_ENUMSTD:
	case VIDIOC_QUERYMENU:
		return -EINVAL;

	default:
		return -EINVAL;

	}
}

Generated by GNU enscript 1.6.4.