Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 35f02a68 authored by Laurent Pinchart's avatar Laurent Pinchart Committed by Mauro Carvalho Chehab
Browse files

V4L/DVB (12378): uvcvideo: Restructure the driver to support multiple simultaneous streams.



As a first step towards multiple streaming interfaces support, reorganize the
driver's data structures to cleanly separate video control and video streaming
data.

Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart@skynet.be>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 6c428b57
Loading
Loading
Loading
Loading
+94 −80
Original line number Diff line number Diff line
@@ -551,6 +551,7 @@ static int uvc_parse_streaming(struct uvc_device *dev,
	}

	mutex_init(&streaming->mutex);
	streaming->dev = dev;
	streaming->intf = usb_get_intf(intf);
	streaming->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;

@@ -751,7 +752,7 @@ static int uvc_parse_streaming(struct uvc_device *dev,
			streaming->maxpsize = psize;
	}

	list_add_tail(&streaming->list, &dev->streaming);
	list_add_tail(&streaming->list, &dev->streams);
	return 0;

error:
@@ -1167,15 +1168,77 @@ static int uvc_parse_control(struct uvc_device *dev)
 */
static void uvc_unregister_video(struct uvc_device *dev)
{
	if (dev->video.vdev) {
		if (dev->video.vdev->minor == -1)
			video_device_release(dev->video.vdev);
	struct uvc_streaming *streaming;

	list_for_each_entry(streaming, &dev->streams, list) {
		if (streaming->vdev == NULL)
			continue;

		if (streaming->vdev->minor == -1)
			video_device_release(streaming->vdev);
		else
			video_unregister_device(dev->video.vdev);
		dev->video.vdev = NULL;
			video_unregister_device(streaming->vdev);
		streaming->vdev = NULL;
	}
}

static int uvc_register_video(struct uvc_device *dev,
		struct uvc_streaming *stream)
{
	struct video_device *vdev;
	struct uvc_entity *term;
	int ret;

	if (uvc_trace_param & UVC_TRACE_PROBE) {
		uvc_printk(KERN_INFO, "Found a valid video chain (");
		list_for_each_entry(term, &dev->video.iterms, chain) {
			printk("%d", term->id);
			if (term->chain.next != &dev->video.iterms)
				printk(",");
		}
		printk(" -> %d).\n", dev->video.oterm->id);
	}

	/* Initialize the streaming interface with default streaming
	 * parameters.
	 */
	ret = uvc_video_init(stream);
	if (ret < 0) {
		uvc_printk(KERN_ERR, "Failed to initialize the device "
			"(%d).\n", ret);
		return ret;
	}

	/* Register the device with V4L. */
	vdev = video_device_alloc();
	if (vdev == NULL)
		return -1;

	/* We already hold a reference to dev->udev. The video device will be
	 * unregistered before the reference is released, so we don't need to
	 * get another one.
	 */
	vdev->parent = &dev->intf->dev;
	vdev->minor = -1;
	vdev->fops = &uvc_fops;
	vdev->release = video_device_release;
	strlcpy(vdev->name, dev->name, sizeof vdev->name);

	/* Set the driver data before calling video_register_device, otherwise
	 * uvc_v4l2_open might race us.
	 */
	stream->vdev = vdev;
	video_set_drvdata(vdev, stream);

	if (video_register_device(vdev, VFL_TYPE_GRABBER, -1) < 0) {
		stream->vdev = NULL;
		video_device_release(vdev);
		return -1;
	}

	return 0;
}

/*
 * Scan the UVC descriptors to locate a chain starting at an Output Terminal
 * and containing the following units:
@@ -1419,7 +1482,7 @@ static int uvc_scan_chain(struct uvc_video_device *video)
}

/*
 * Register the video devices.
 * Scan the device for video chains and register video devices.
 *
 * The driver currently supports a single video device per control interface
 * only. The terminal and units must match the following structure:
@@ -1432,15 +1495,14 @@ static int uvc_scan_chain(struct uvc_video_device *video)
 * Extension Units connected to the main chain as single-unit branches are
 * also supported.
 */
static int uvc_register_video(struct uvc_device *dev)
static int uvc_scan_device(struct uvc_device *dev)
{
	struct video_device *vdev;
	struct uvc_entity *term;
	int found = 0, ret;
	int found = 0;

	/* Check if the control interface matches the structure we expect. */
	list_for_each_entry(term, &dev->entities, list) {
		struct uvc_streaming *streaming;
		struct uvc_streaming *stream;

		if (!UVC_ENTITY_IS_TERM(term) || !UVC_ENTITY_IS_OTERM(term))
			continue;
@@ -1454,17 +1516,14 @@ static int uvc_register_video(struct uvc_device *dev)
		if (uvc_scan_chain(&dev->video) < 0)
			continue;

		list_for_each_entry(streaming, &dev->streaming, list) {
			if (streaming->header.bTerminalLink ==
		list_for_each_entry(stream, &dev->streams, list) {
			if (stream->header.bTerminalLink ==
			    dev->video.sterm->id) {
				dev->video.streaming = streaming;
				uvc_register_video(dev, stream);
				found = 1;
				break;
			}
		}

		if (found)
			break;
	}

	if (!found) {
@@ -1472,55 +1531,6 @@ static int uvc_register_video(struct uvc_device *dev)
		return -1;
	}

	if (uvc_trace_param & UVC_TRACE_PROBE) {
		uvc_printk(KERN_INFO, "Found a valid video chain (");
		list_for_each_entry(term, &dev->video.iterms, chain) {
			printk("%d", term->id);
			if (term->chain.next != &dev->video.iterms)
				printk(",");
		}
		printk(" -> %d).\n", dev->video.oterm->id);
	}

	/* Initialize the video buffers queue. */
	uvc_queue_init(&dev->video.queue, dev->video.streaming->type);

	/* Initialize the streaming interface with default streaming
	 * parameters.
	 */
	if ((ret = uvc_video_init(&dev->video)) < 0) {
		uvc_printk(KERN_ERR, "Failed to initialize the device "
			"(%d).\n", ret);
		return ret;
	}

	/* Register the device with V4L. */
	vdev = video_device_alloc();
	if (vdev == NULL)
		return -1;

	/* We already hold a reference to dev->udev. The video device will be
	 * unregistered before the reference is released, so we don't need to
	 * get another one.
	 */
	vdev->parent = &dev->intf->dev;
	vdev->minor = -1;
	vdev->fops = &uvc_fops;
	vdev->release = video_device_release;
	strlcpy(vdev->name, dev->name, sizeof vdev->name);

	/* Set the driver data before calling video_register_device, otherwise
	 * uvc_v4l2_open might race us.
	 */
	dev->video.vdev = vdev;
	video_set_drvdata(vdev, &dev->video);

	if (video_register_device(vdev, VFL_TYPE_GRABBER, -1) < 0) {
		dev->video.vdev = NULL;
		video_device_release(vdev);
		return -1;
	}

	return 0;
}

@@ -1559,7 +1569,7 @@ void uvc_delete(struct kref *kref)
		kfree(entity);
	}

	list_for_each_safe(p, n, &dev->streaming) {
	list_for_each_safe(p, n, &dev->streams) {
		struct uvc_streaming *streaming;
		streaming = list_entry(p, struct uvc_streaming, list);
		usb_driver_release_interface(&uvc_driver.driver,
@@ -1593,7 +1603,7 @@ static int uvc_probe(struct usb_interface *intf,
		return -ENOMEM;

	INIT_LIST_HEAD(&dev->entities);
	INIT_LIST_HEAD(&dev->streaming);
	INIT_LIST_HEAD(&dev->streams);
	kref_init(&dev->kref);
	atomic_set(&dev->users, 0);

@@ -1634,8 +1644,8 @@ static int uvc_probe(struct usb_interface *intf,
	if (uvc_ctrl_init_device(dev) < 0)
		goto error;

	/* Register the video devices. */
	if (uvc_register_video(dev) < 0)
	/* Scan the device for video chains and register video devices. */
	if (uvc_scan_device(dev) < 0)
		goto error;

	/* Save our data pointer in the interface data. */
@@ -1689,6 +1699,7 @@ static void uvc_disconnect(struct usb_interface *intf)
static int uvc_suspend(struct usb_interface *intf, pm_message_t message)
{
	struct uvc_device *dev = usb_get_intfdata(intf);
	struct uvc_streaming *stream;

	uvc_trace(UVC_TRACE_SUSPEND, "Suspending interface %u\n",
		intf->cur_altsetting->desc.bInterfaceNumber);
@@ -1698,18 +1709,20 @@ static int uvc_suspend(struct usb_interface *intf, pm_message_t message)
	    UVC_SC_VIDEOCONTROL)
		return uvc_status_suspend(dev);

	if (dev->video.streaming->intf != intf) {
		uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB "
				"interface mismatch.\n");
		return -EINVAL;
	list_for_each_entry(stream, &dev->streams, list) {
		if (stream->intf == intf)
			return uvc_video_suspend(stream);
	}

	return uvc_video_suspend(&dev->video);
	uvc_trace(UVC_TRACE_SUSPEND, "Suspend: video streaming USB interface "
			"mismatch.\n");
	return -EINVAL;
}

static int __uvc_resume(struct usb_interface *intf, int reset)
{
	struct uvc_device *dev = usb_get_intfdata(intf);
	struct uvc_streaming *stream;

	uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n",
		intf->cur_altsetting->desc.bInterfaceNumber);
@@ -1726,13 +1739,14 @@ static int __uvc_resume(struct usb_interface *intf, int reset)
		return uvc_status_resume(dev);
	}

	if (dev->video.streaming->intf != intf) {
		uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB "
				"interface mismatch.\n");
		return -EINVAL;
	list_for_each_entry(stream, &dev->streams, list) {
		if (stream->intf == intf)
			return uvc_video_resume(stream);
	}

	return uvc_video_resume(&dev->video);
	uvc_trace(UVC_TRACE_SUSPEND, "Resume: video streaming USB interface "
			"mismatch.\n");
	return -EINVAL;
}

static int uvc_resume(struct usb_interface *intf)
+4 −3
Original line number Diff line number Diff line
@@ -99,7 +99,7 @@ static int isight_decode(struct uvc_video_queue *queue, struct uvc_buffer *buf,
	return 0;
}

void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video,
void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream,
		struct uvc_buffer *buf)
{
	int ret, i;
@@ -120,7 +120,7 @@ void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video,
		 * processes the data of the first payload of the new frame.
		 */
		do {
			ret = isight_decode(&video->queue, buf,
			ret = isight_decode(&stream->queue, buf,
					urb->transfer_buffer +
					urb->iso_frame_desc[i].offset,
					urb->iso_frame_desc[i].actual_length);
@@ -130,7 +130,8 @@ void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video,

			if (buf->state == UVC_BUF_STATE_DONE ||
			    buf->state == UVC_BUF_STATE_ERROR)
				buf = uvc_queue_next_buffer(&video->queue, buf);
				buf = uvc_queue_next_buffer(&stream->queue,
							buf);
		} while (ret == -EAGAIN);
	}
}
+102 −93
Original line number Diff line number Diff line
@@ -103,7 +103,7 @@ static __u32 uvc_try_frame_interval(struct uvc_frame *frame, __u32 interval)
	return interval;
}

static int uvc_v4l2_try_format(struct uvc_video_device *video,
static int uvc_v4l2_try_format(struct uvc_streaming *stream,
	struct v4l2_format *fmt, struct uvc_streaming_control *probe,
	struct uvc_format **uvc_format, struct uvc_frame **uvc_frame)
{
@@ -116,7 +116,7 @@ static int uvc_v4l2_try_format(struct uvc_video_device *video,
	int ret = 0;
	__u8 *fcc;

	if (fmt->type != video->streaming->type)
	if (fmt->type != stream->type)
		return -EINVAL;

	fcc = (__u8 *)&fmt->fmt.pix.pixelformat;
@@ -126,8 +126,8 @@ static int uvc_v4l2_try_format(struct uvc_video_device *video,
			fmt->fmt.pix.width, fmt->fmt.pix.height);

	/* Check if the hardware supports the requested format. */
	for (i = 0; i < video->streaming->nformats; ++i) {
		format = &video->streaming->format[i];
	for (i = 0; i < stream->nformats; ++i) {
		format = &stream->format[i];
		if (format->fcc == fmt->fmt.pix.pixelformat)
			break;
	}
@@ -191,12 +191,13 @@ static int uvc_v4l2_try_format(struct uvc_video_device *video,
	 * developers test their webcams with the Linux driver as well as with
	 * the Windows driver).
	 */
	if (video->dev->quirks & UVC_QUIRK_PROBE_EXTRAFIELDS)
	if (stream->dev->quirks & UVC_QUIRK_PROBE_EXTRAFIELDS)
		probe->dwMaxVideoFrameSize =
			video->streaming->ctrl.dwMaxVideoFrameSize;
			stream->ctrl.dwMaxVideoFrameSize;

	/* Probe the device. */
	if ((ret = uvc_probe_video(video, probe)) < 0)
	ret = uvc_probe_video(stream, probe);
	if (ret < 0)
		goto done;

	fmt->fmt.pix.width = frame->wWidth;
@@ -216,13 +217,13 @@ static int uvc_v4l2_try_format(struct uvc_video_device *video,
	return ret;
}

static int uvc_v4l2_get_format(struct uvc_video_device *video,
static int uvc_v4l2_get_format(struct uvc_streaming *stream,
	struct v4l2_format *fmt)
{
	struct uvc_format *format = video->streaming->cur_format;
	struct uvc_frame *frame = video->streaming->cur_frame;
	struct uvc_format *format = stream->cur_format;
	struct uvc_frame *frame = stream->cur_frame;

	if (fmt->type != video->streaming->type)
	if (fmt->type != stream->type)
		return -EINVAL;

	if (format == NULL || frame == NULL)
@@ -233,14 +234,14 @@ static int uvc_v4l2_get_format(struct uvc_video_device *video,
	fmt->fmt.pix.height = frame->wHeight;
	fmt->fmt.pix.field = V4L2_FIELD_NONE;
	fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth / 8;
	fmt->fmt.pix.sizeimage = video->streaming->ctrl.dwMaxVideoFrameSize;
	fmt->fmt.pix.sizeimage = stream->ctrl.dwMaxVideoFrameSize;
	fmt->fmt.pix.colorspace = format->colorspace;
	fmt->fmt.pix.priv = 0;

	return 0;
}

static int uvc_v4l2_set_format(struct uvc_video_device *video,
static int uvc_v4l2_set_format(struct uvc_streaming *stream,
	struct v4l2_format *fmt)
{
	struct uvc_streaming_control probe;
@@ -248,39 +249,39 @@ static int uvc_v4l2_set_format(struct uvc_video_device *video,
	struct uvc_frame *frame;
	int ret;

	if (fmt->type != video->streaming->type)
	if (fmt->type != stream->type)
		return -EINVAL;

	if (uvc_queue_allocated(&video->queue))
	if (uvc_queue_allocated(&stream->queue))
		return -EBUSY;

	ret = uvc_v4l2_try_format(video, fmt, &probe, &format, &frame);
	ret = uvc_v4l2_try_format(stream, fmt, &probe, &format, &frame);
	if (ret < 0)
		return ret;

	memcpy(&video->streaming->ctrl, &probe, sizeof probe);
	video->streaming->cur_format = format;
	video->streaming->cur_frame = frame;
	memcpy(&stream->ctrl, &probe, sizeof probe);
	stream->cur_format = format;
	stream->cur_frame = frame;

	return 0;
}

static int uvc_v4l2_get_streamparm(struct uvc_video_device *video,
static int uvc_v4l2_get_streamparm(struct uvc_streaming *stream,
		struct v4l2_streamparm *parm)
{
	uint32_t numerator, denominator;

	if (parm->type != video->streaming->type)
	if (parm->type != stream->type)
		return -EINVAL;

	numerator = video->streaming->ctrl.dwFrameInterval;
	numerator = stream->ctrl.dwFrameInterval;
	denominator = 10000000;
	uvc_simplify_fraction(&numerator, &denominator, 8, 333);

	memset(parm, 0, sizeof *parm);
	parm->type = video->streaming->type;
	parm->type = stream->type;

	if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
	if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
		parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
		parm->parm.capture.capturemode = 0;
		parm->parm.capture.timeperframe.numerator = numerator;
@@ -297,19 +298,19 @@ static int uvc_v4l2_get_streamparm(struct uvc_video_device *video,
	return 0;
}

static int uvc_v4l2_set_streamparm(struct uvc_video_device *video,
static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream,
		struct v4l2_streamparm *parm)
{
	struct uvc_frame *frame = video->streaming->cur_frame;
	struct uvc_frame *frame = stream->cur_frame;
	struct uvc_streaming_control probe;
	struct v4l2_fract timeperframe;
	uint32_t interval;
	int ret;

	if (parm->type != video->streaming->type)
	if (parm->type != stream->type)
		return -EINVAL;

	if (uvc_queue_streaming(&video->queue))
	if (uvc_queue_streaming(&stream->queue))
		return -EBUSY;

	if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
@@ -317,7 +318,7 @@ static int uvc_v4l2_set_streamparm(struct uvc_video_device *video,
	else
		timeperframe = parm->parm.output.timeperframe;

	memcpy(&probe, &video->streaming->ctrl, sizeof probe);
	memcpy(&probe, &stream->ctrl, sizeof probe);
	interval = uvc_fraction_to_interval(timeperframe.numerator,
		timeperframe.denominator);

@@ -326,10 +327,11 @@ static int uvc_v4l2_set_streamparm(struct uvc_video_device *video,
	probe.dwFrameInterval = uvc_try_frame_interval(frame, interval);

	/* Probe the device with the new settings. */
	if ((ret = uvc_probe_video(video, &probe)) < 0)
	ret = uvc_probe_video(stream, &probe);
	if (ret < 0)
		return ret;

	memcpy(&video->streaming->ctrl, &probe, sizeof probe);
	memcpy(&stream->ctrl, &probe, sizeof probe);

	/* Return the actual frame period. */
	timeperframe.numerator = probe.dwFrameInterval;
@@ -382,8 +384,8 @@ static int uvc_acquire_privileges(struct uvc_fh *handle)

	/* Check if the device already has a privileged handle. */
	mutex_lock(&uvc_driver.open_mutex);
	if (atomic_inc_return(&handle->device->active) != 1) {
		atomic_dec(&handle->device->active);
	if (atomic_inc_return(&handle->stream->active) != 1) {
		atomic_dec(&handle->stream->active);
		ret = -EBUSY;
		goto done;
	}
@@ -398,7 +400,7 @@ static int uvc_acquire_privileges(struct uvc_fh *handle)
static void uvc_dismiss_privileges(struct uvc_fh *handle)
{
	if (handle->state == UVC_HANDLE_ACTIVE)
		atomic_dec(&handle->device->active);
		atomic_dec(&handle->stream->active);

	handle->state = UVC_HANDLE_PASSIVE;
}
@@ -414,45 +416,47 @@ static int uvc_has_privileges(struct uvc_fh *handle)

static int uvc_v4l2_open(struct file *file)
{
	struct uvc_video_device *video;
	struct uvc_streaming *stream;
	struct uvc_fh *handle;
	int ret = 0;

	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n");
	mutex_lock(&uvc_driver.open_mutex);
	video = video_drvdata(file);
	stream = video_drvdata(file);

	if (video->dev->state & UVC_DEV_DISCONNECTED) {
	if (stream->dev->state & UVC_DEV_DISCONNECTED) {
		ret = -ENODEV;
		goto done;
	}

	ret = usb_autopm_get_interface(video->dev->intf);
	ret = usb_autopm_get_interface(stream->dev->intf);
	if (ret < 0)
		goto done;

	/* Create the device handle. */
	handle = kzalloc(sizeof *handle, GFP_KERNEL);
	if (handle == NULL) {
		usb_autopm_put_interface(video->dev->intf);
		usb_autopm_put_interface(stream->dev->intf);
		ret = -ENOMEM;
		goto done;
	}

	if (atomic_inc_return(&video->dev->users) == 1) {
		if ((ret = uvc_status_start(video->dev)) < 0) {
			usb_autopm_put_interface(video->dev->intf);
			atomic_dec(&video->dev->users);
	if (atomic_inc_return(&stream->dev->users) == 1) {
		ret = uvc_status_start(stream->dev);
		if (ret < 0) {
			usb_autopm_put_interface(stream->dev->intf);
			atomic_dec(&stream->dev->users);
			kfree(handle);
			goto done;
		}
	}

	handle->device = video;
	handle->video = &stream->dev->video;
	handle->stream = stream;
	handle->state = UVC_HANDLE_PASSIVE;
	file->private_data = handle;

	kref_get(&video->dev->kref);
	kref_get(&stream->dev->kref);

done:
	mutex_unlock(&uvc_driver.open_mutex);
@@ -461,20 +465,20 @@ static int uvc_v4l2_open(struct file *file)

static int uvc_v4l2_release(struct file *file)
{
	struct uvc_video_device *video = video_drvdata(file);
	struct uvc_fh *handle = (struct uvc_fh *)file->private_data;
	struct uvc_streaming *stream = handle->stream;

	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n");

	/* Only free resources if this is a privileged handle. */
	if (uvc_has_privileges(handle)) {
		uvc_video_enable(video, 0);
		uvc_video_enable(stream, 0);

		mutex_lock(&video->queue.mutex);
		if (uvc_free_buffers(&video->queue) < 0)
		mutex_lock(&stream->queue.mutex);
		if (uvc_free_buffers(&stream->queue) < 0)
			uvc_printk(KERN_ERR, "uvc_v4l2_release: Unable to "
					"free buffers.\n");
		mutex_unlock(&video->queue.mutex);
		mutex_unlock(&stream->queue.mutex);
	}

	/* Release the file handle. */
@@ -482,19 +486,20 @@ static int uvc_v4l2_release(struct file *file)
	kfree(handle);
	file->private_data = NULL;

	if (atomic_dec_return(&video->dev->users) == 0)
		uvc_status_stop(video->dev);
	if (atomic_dec_return(&stream->dev->users) == 0)
		uvc_status_stop(stream->dev);

	usb_autopm_put_interface(video->dev->intf);
	kref_put(&video->dev->kref, uvc_delete);
	usb_autopm_put_interface(stream->dev->intf);
	kref_put(&stream->dev->kref, uvc_delete);
	return 0;
}

static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
	struct video_device *vdev = video_devdata(file);
	struct uvc_video_device *video = video_get_drvdata(vdev);
	struct uvc_fh *handle = (struct uvc_fh *)file->private_data;
	struct uvc_video_device *video = handle->video;
	struct uvc_streaming *stream = handle->stream;
	long ret = 0;

	switch (cmd) {
@@ -506,10 +511,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
		memset(cap, 0, sizeof *cap);
		strlcpy(cap->driver, "uvcvideo", sizeof cap->driver);
		strlcpy(cap->card, vdev->name, sizeof cap->card);
		usb_make_path(video->dev->udev,
		usb_make_path(stream->dev->udev,
			      cap->bus_info, sizeof(cap->bus_info));
		cap->version = DRIVER_VERSION_NUMBER;
		if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
		if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
			cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
					  | V4L2_CAP_STREAMING;
		else
@@ -703,15 +708,15 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
		enum v4l2_buf_type type = fmt->type;
		__u32 index = fmt->index;

		if (fmt->type != video->streaming->type ||
		    fmt->index >= video->streaming->nformats)
		if (fmt->type != stream->type ||
		    fmt->index >= stream->nformats)
			return -EINVAL;

		memset(fmt, 0, sizeof(*fmt));
		fmt->index = index;
		fmt->type = type;

		format = &video->streaming->format[fmt->index];
		format = &stream->format[fmt->index];
		fmt->flags = 0;
		if (format->flags & UVC_FMT_FLAG_COMPRESSED)
			fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
@@ -729,17 +734,17 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
		if ((ret = uvc_acquire_privileges(handle)) < 0)
			return ret;

		return uvc_v4l2_try_format(video, arg, &probe, NULL, NULL);
		return uvc_v4l2_try_format(stream, arg, &probe, NULL, NULL);
	}

	case VIDIOC_S_FMT:
		if ((ret = uvc_acquire_privileges(handle)) < 0)
			return ret;

		return uvc_v4l2_set_format(video, arg);
		return uvc_v4l2_set_format(stream, arg);

	case VIDIOC_G_FMT:
		return uvc_v4l2_get_format(video, arg);
		return uvc_v4l2_get_format(stream, arg);

	/* Frame size enumeration */
	case VIDIOC_ENUM_FRAMESIZES:
@@ -750,10 +755,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
		int i;

		/* Look for the given pixel format */
		for (i = 0; i < video->streaming->nformats; i++) {
			if (video->streaming->format[i].fcc ==
		for (i = 0; i < stream->nformats; i++) {
			if (stream->format[i].fcc ==
					fsize->pixel_format) {
				format = &video->streaming->format[i];
				format = &stream->format[i];
				break;
			}
		}
@@ -779,10 +784,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
		int i;

		/* Look for the given pixel format and frame size */
		for (i = 0; i < video->streaming->nformats; i++) {
			if (video->streaming->format[i].fcc ==
		for (i = 0; i < stream->nformats; i++) {
			if (stream->format[i].fcc ==
					fival->pixel_format) {
				format = &video->streaming->format[i];
				format = &stream->format[i];
				break;
			}
		}
@@ -832,21 +837,21 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)

	/* Get & Set streaming parameters */
	case VIDIOC_G_PARM:
		return uvc_v4l2_get_streamparm(video, arg);
		return uvc_v4l2_get_streamparm(stream, arg);

	case VIDIOC_S_PARM:
		if ((ret = uvc_acquire_privileges(handle)) < 0)
			return ret;

		return uvc_v4l2_set_streamparm(video, arg);
		return uvc_v4l2_set_streamparm(stream, arg);

	/* Cropping and scaling */
	case VIDIOC_CROPCAP:
	{
		struct v4l2_cropcap *ccap = arg;
		struct uvc_frame *frame = video->streaming->cur_frame;
		struct uvc_frame *frame = stream->cur_frame;

		if (ccap->type != video->streaming->type)
		if (ccap->type != stream->type)
			return -EINVAL;

		ccap->bounds.left = 0;
@@ -870,16 +875,16 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
	{
		struct v4l2_requestbuffers *rb = arg;
		unsigned int bufsize =
			video->streaming->ctrl.dwMaxVideoFrameSize;
			stream->ctrl.dwMaxVideoFrameSize;

		if (rb->type != video->streaming->type ||
		if (rb->type != stream->type ||
		    rb->memory != V4L2_MEMORY_MMAP)
			return -EINVAL;

		if ((ret = uvc_acquire_privileges(handle)) < 0)
			return ret;

		ret = uvc_alloc_buffers(&video->queue, rb->count, bufsize);
		ret = uvc_alloc_buffers(&stream->queue, rb->count, bufsize);
		if (ret < 0)
			return ret;

@@ -892,39 +897,40 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
	{
		struct v4l2_buffer *buf = arg;

		if (buf->type != video->streaming->type)
		if (buf->type != stream->type)
			return -EINVAL;

		if (!uvc_has_privileges(handle))
			return -EBUSY;

		return uvc_query_buffer(&video->queue, buf);
		return uvc_query_buffer(&stream->queue, buf);
	}

	case VIDIOC_QBUF:
		if (!uvc_has_privileges(handle))
			return -EBUSY;

		return uvc_queue_buffer(&video->queue, arg);
		return uvc_queue_buffer(&stream->queue, arg);

	case VIDIOC_DQBUF:
		if (!uvc_has_privileges(handle))
			return -EBUSY;

		return uvc_dequeue_buffer(&video->queue, arg,
		return uvc_dequeue_buffer(&stream->queue, arg,
			file->f_flags & O_NONBLOCK);

	case VIDIOC_STREAMON:
	{
		int *type = arg;

		if (*type != video->streaming->type)
		if (*type != stream->type)
			return -EINVAL;

		if (!uvc_has_privileges(handle))
			return -EBUSY;

		if ((ret = uvc_video_enable(video, 1)) < 0)
		ret = uvc_video_enable(stream, 1);
		if (ret < 0)
			return ret;
		break;
	}
@@ -933,13 +939,13 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
	{
		int *type = arg;

		if (*type != video->streaming->type)
		if (*type != stream->type)
			return -EINVAL;

		if (!uvc_has_privileges(handle))
			return -EBUSY;

		return uvc_video_enable(video, 0);
		return uvc_video_enable(stream, 0);
	}

	/* Analog video standards make no sense for digital cameras. */
@@ -1070,7 +1076,9 @@ static struct vm_operations_struct uvc_vm_ops = {

static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
{
	struct uvc_video_device *video = video_drvdata(file);
	struct uvc_fh *handle = (struct uvc_fh *)file->private_data;
	struct uvc_streaming *stream = handle->stream;
	struct uvc_video_queue *queue = &stream->queue;
	struct uvc_buffer *uninitialized_var(buffer);
	struct page *page;
	unsigned long addr, start, size;
@@ -1082,15 +1090,15 @@ static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
	start = vma->vm_start;
	size = vma->vm_end - vma->vm_start;

	mutex_lock(&video->queue.mutex);
	mutex_lock(&queue->mutex);

	for (i = 0; i < video->queue.count; ++i) {
		buffer = &video->queue.buffer[i];
	for (i = 0; i < queue->count; ++i) {
		buffer = &queue->buffer[i];
		if ((buffer->buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff)
			break;
	}

	if (i == video->queue.count || size != video->queue.buf_size) {
	if (i == queue->count || size != queue->buf_size) {
		ret = -EINVAL;
		goto done;
	}
@@ -1101,7 +1109,7 @@ static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
	 */
	vma->vm_flags |= VM_IO;

	addr = (unsigned long)video->queue.mem + buffer->buf.m.offset;
	addr = (unsigned long)queue->mem + buffer->buf.m.offset;
	while (size > 0) {
		page = vmalloc_to_page((void *)addr);
		if ((ret = vm_insert_page(vma, start, page)) < 0)
@@ -1117,17 +1125,18 @@ static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
	uvc_vm_open(vma);

done:
	mutex_unlock(&video->queue.mutex);
	mutex_unlock(&queue->mutex);
	return ret;
}

static unsigned int uvc_v4l2_poll(struct file *file, poll_table *wait)
{
	struct uvc_video_device *video = video_drvdata(file);
	struct uvc_fh *handle = (struct uvc_fh *)file->private_data;
	struct uvc_streaming *stream = handle->stream;

	uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n");

	return uvc_queue_poll(&video->queue, file, wait);
	return uvc_queue_poll(&stream->queue, file, wait);
}

const struct v4l2_file_operations uvc_fops = {
+214 −201

File changed.

Preview size limit exceeded, changes collapsed.

+42 −44
Original line number Diff line number Diff line
@@ -361,26 +361,6 @@ struct uvc_streaming_header {
	__u8 bTriggerUsage;
};

struct uvc_streaming {
	struct list_head list;

	struct usb_interface *intf;
	int intfnum;
	__u16 maxpsize;

	struct uvc_streaming_header header;
	enum v4l2_buf_type type;

	unsigned int nformats;
	struct uvc_format *format;

	struct uvc_streaming_control ctrl;
	struct uvc_format *cur_format;
	struct uvc_frame *cur_frame;

	struct mutex mutex;
};

enum uvc_buffer_state {
	UVC_BUF_STATE_IDLE	= 0,
	UVC_BUF_STATE_QUEUED	= 1,
@@ -422,26 +402,31 @@ struct uvc_video_queue {
	struct list_head irqqueue;
};

struct uvc_video_device {
struct uvc_streaming {
	struct list_head list;
	struct uvc_device *dev;
	struct video_device *vdev;
	atomic_t active;
	unsigned int frozen : 1;

	struct list_head iterms;		/* Input terminals */
	struct uvc_entity *oterm;		/* Output terminal */
	struct uvc_entity *sterm;		/* USB streaming terminal */
	struct uvc_entity *processing;
	struct uvc_entity *selector;
	struct list_head extensions;
	struct mutex ctrl_mutex;
	struct usb_interface *intf;
	int intfnum;
	__u16 maxpsize;

	struct uvc_video_queue queue;
	struct uvc_streaming_header header;
	enum v4l2_buf_type type;

	unsigned int nformats;
	struct uvc_format *format;

	/* Video streaming object, must always be non-NULL. */
	struct uvc_streaming *streaming;
	struct uvc_streaming_control ctrl;
	struct uvc_format *cur_format;
	struct uvc_frame *cur_frame;

	struct mutex mutex;

	void (*decode) (struct urb *urb, struct uvc_video_device *video,
	unsigned int frozen : 1;
	struct uvc_video_queue queue;
	void (*decode) (struct urb *urb, struct uvc_streaming *video,
			struct uvc_buffer *buf);

	/* Context data used by the bulk completion handler. */
@@ -461,6 +446,18 @@ struct uvc_video_device {
	__u8 last_fid;
};

struct uvc_video_device {
	struct uvc_device *dev;

	struct list_head iterms;		/* Input terminals */
	struct uvc_entity *oterm;		/* Output terminal */
	struct uvc_entity *sterm;		/* USB streaming terminal */
	struct uvc_entity *processing;
	struct uvc_entity *selector;
	struct list_head extensions;
	struct mutex ctrl_mutex;
};

enum uvc_device_state {
	UVC_DEV_DISCONNECTED = 1,
};
@@ -486,15 +483,15 @@ struct uvc_device {

	struct uvc_video_device video;

	/* Video Streaming interfaces */
	struct list_head streams;

	/* Status Interrupt Endpoint */
	struct usb_host_endpoint *int_ep;
	struct urb *int_urb;
	__u8 *status;
	struct input_dev *input;
	char input_phys[64];

	/* Video Streaming interfaces */
	struct list_head streaming;
};

enum uvc_handle_state {
@@ -503,7 +500,8 @@ enum uvc_handle_state {
};

struct uvc_fh {
	struct uvc_video_device *device;
	struct uvc_video_device *video;
	struct uvc_streaming *stream;
	enum uvc_handle_state state;
};

@@ -600,13 +598,13 @@ static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
extern const struct v4l2_file_operations uvc_fops;

/* Video */
extern int uvc_video_init(struct uvc_video_device *video);
extern int uvc_video_suspend(struct uvc_video_device *video);
extern int uvc_video_resume(struct uvc_video_device *video);
extern int uvc_video_enable(struct uvc_video_device *video, int enable);
extern int uvc_probe_video(struct uvc_video_device *video,
extern int uvc_video_init(struct uvc_streaming *stream);
extern int uvc_video_suspend(struct uvc_streaming *stream);
extern int uvc_video_resume(struct uvc_streaming *stream);
extern int uvc_video_enable(struct uvc_streaming *stream, int enable);
extern int uvc_probe_video(struct uvc_streaming *stream,
		struct uvc_streaming_control *probe);
extern int uvc_commit_video(struct uvc_video_device *video,
extern int uvc_commit_video(struct uvc_streaming *stream,
		struct uvc_streaming_control *ctrl);
extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
		__u8 intfnum, __u8 cs, void *data, __u16 size);
@@ -660,7 +658,7 @@ extern struct usb_host_endpoint *uvc_find_endpoint(
		struct usb_host_interface *alts, __u8 epaddr);

/* Quirks support */
void uvc_video_decode_isight(struct urb *urb, struct uvc_video_device *video,
void uvc_video_decode_isight(struct urb *urb, struct uvc_streaming *stream,
		struct uvc_buffer *buf);

#endif /* __KERNEL__ */