V4L/DVB (10104): uvcvideo: Add support for video output devices

Extend the range of supported UVC devices by allowing video output devices
matching the following structure:

TT_STREAMING -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> OTT_*

Video output devices are reported with the V4L2_CAP_VIDEO_OUTPUT capability
flag and are subject to the same restrictions as video input devices.

Signed-off-by: Laurent Pinchart <laurent.pinchart@skynet.be>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c
index 18a6192..89d8bd1 100644
--- a/drivers/media/video/uvc/uvc_driver.c
+++ b/drivers/media/video/uvc/uvc_driver.c
@@ -12,8 +12,8 @@
  */
 
 /*
- * This driver aims to support video input devices compliant with the 'USB
- * Video Class' specification.
+ * This driver aims to support video input and ouput devices compliant with the
+ * 'USB Video Class' specification.
  *
  * The driver doesn't support the deprecated v4l1 interface. It implements the
  * mmap capture method only, and doesn't do any image format conversion in
@@ -609,46 +609,55 @@
 	}
 
 	/* Parse the header descriptor. */
-	if (buffer[2] == VS_OUTPUT_HEADER) {
-		uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
-			"%d OUTPUT HEADER descriptor is not supported.\n",
-			dev->udev->devnum, alts->desc.bInterfaceNumber);
-		goto error;
-	} else if (buffer[2] == VS_INPUT_HEADER) {
-		p = buflen >= 5 ? buffer[3] : 0;
-		n = buflen >= 12 ? buffer[12] : 0;
+	switch (buffer[2]) {
+	case VS_OUTPUT_HEADER:
+		streaming->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+		size = 9;
+		break;
 
-		if (buflen < 13 + p*n || buffer[2] != VS_INPUT_HEADER) {
-			uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
-				"interface %d INPUT HEADER descriptor is "
-				"invalid.\n", dev->udev->devnum,
-				alts->desc.bInterfaceNumber);
-			goto error;
-		}
+	case VS_INPUT_HEADER:
+		streaming->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		size = 13;
+		break;
 
-		streaming->header.bNumFormats = p;
-		streaming->header.bEndpointAddress = buffer[6];
-		streaming->header.bmInfo = buffer[7];
-		streaming->header.bTerminalLink = buffer[8];
-		streaming->header.bStillCaptureMethod = buffer[9];
-		streaming->header.bTriggerSupport = buffer[10];
-		streaming->header.bTriggerUsage = buffer[11];
-		streaming->header.bControlSize = n;
-
-		streaming->header.bmaControls = kmalloc(p*n, GFP_KERNEL);
-		if (streaming->header.bmaControls == NULL) {
-			ret = -ENOMEM;
-			goto error;
-		}
-
-		memcpy(streaming->header.bmaControls, &buffer[13], p*n);
-	} else {
+	default:
 		uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
 			"%d HEADER descriptor not found.\n", dev->udev->devnum,
 			alts->desc.bInterfaceNumber);
 		goto error;
 	}
 
+	p = buflen >= 4 ? buffer[3] : 0;
+	n = buflen >= size ? buffer[size-1] : 0;
+
+	if (buflen < size + p*n) {
+		uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
+			"interface %d HEADER descriptor is invalid.\n",
+			dev->udev->devnum, alts->desc.bInterfaceNumber);
+		goto error;
+	}
+
+	streaming->header.bNumFormats = p;
+	streaming->header.bEndpointAddress = buffer[6];
+	if (buffer[2] == VS_INPUT_HEADER) {
+		streaming->header.bmInfo = buffer[7];
+		streaming->header.bTerminalLink = buffer[8];
+		streaming->header.bStillCaptureMethod = buffer[9];
+		streaming->header.bTriggerSupport = buffer[10];
+		streaming->header.bTriggerUsage = buffer[11];
+	} else {
+		streaming->header.bTerminalLink = buffer[7];
+	}
+	streaming->header.bControlSize = n;
+
+	streaming->header.bmaControls = kmalloc(p*n, GFP_KERNEL);
+	if (streaming->header.bmaControls == NULL) {
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	memcpy(streaming->header.bmaControls, &buffer[size], p*n);
+
 	buflen -= buffer[0];
 	buffer += buffer[0];
 
@@ -1258,6 +1267,26 @@
 		list_add_tail(&entity->chain, &video->iterms);
 		break;
 
+	case TT_STREAMING:
+		if (uvc_trace_param & UVC_TRACE_PROBE)
+			printk(" <- IT %d\n", entity->id);
+
+		if (!UVC_ENTITY_IS_ITERM(entity)) {
+			uvc_trace(UVC_TRACE_DESCR, "Unsupported input "
+				"terminal %u.\n", entity->id);
+			return -1;
+		}
+
+		if (video->sterm != NULL) {
+			uvc_trace(UVC_TRACE_DESCR, "Found multiple streaming "
+				"entities in chain.\n");
+			return -1;
+		}
+
+		list_add_tail(&entity->chain, &video->iterms);
+		video->sterm = entity;
+		break;
+
 	default:
 		uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type "
 			"0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity));
@@ -1368,6 +1397,10 @@
 
 	entity = video->oterm;
 	uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", entity->id);
+
+	if (UVC_ENTITY_TYPE(entity) == TT_STREAMING)
+		video->sterm = entity;
+
 	id = entity->output.bSourceID;
 	while (id != 0) {
 		prev = entity;
@@ -1396,8 +1429,11 @@
 			return id;
 	}
 
-	/* Initialize the video buffers queue. */
-	uvc_queue_init(&video->queue);
+	if (video->sterm == NULL) {
+		uvc_trace(UVC_TRACE_DESCR, "No streaming entity found in "
+			"chain.\n");
+		return -1;
+	}
 
 	return 0;
 }
@@ -1408,7 +1444,8 @@
  * The driver currently supports a single video device per control interface
  * only. The terminal and units must match the following structure:
  *
- * ITT_CAMERA -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> TT_STREAMING
+ * ITT_* -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> TT_STREAMING
+ * TT_STREAMING -> VC_PROCESSING_UNIT -> VC_EXTENSION_UNIT{0,n} -> OTT_*
  *
  * The Extension Units, if present, must have a single input pin. The
  * Processing Unit and Extension Units can be in any order. Additional
@@ -1425,7 +1462,7 @@
 	list_for_each_entry(term, &dev->entities, list) {
 		struct uvc_streaming *streaming;
 
-		if (UVC_ENTITY_TYPE(term) != TT_STREAMING)
+		if (!UVC_ENTITY_IS_TERM(term) || !UVC_ENTITY_IS_OTERM(term))
 			continue;
 
 		memset(&dev->video, 0, sizeof dev->video);
@@ -1438,7 +1475,8 @@
 			continue;
 
 		list_for_each_entry(streaming, &dev->streaming, list) {
-			if (streaming->header.bTerminalLink == term->id) {
+			if (streaming->header.bTerminalLink ==
+			    dev->video.sterm->id) {
 				dev->video.streaming = streaming;
 				found = 1;
 				break;
@@ -1464,6 +1502,9 @@
 		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.
 	 */
diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c
index 5646a6a..4254634 100644
--- a/drivers/media/video/uvc/uvc_queue.c
+++ b/drivers/media/video/uvc/uvc_queue.c
@@ -79,12 +79,13 @@
  *
  */
 
-void uvc_queue_init(struct uvc_video_queue *queue)
+void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type)
 {
 	mutex_init(&queue->mutex);
 	spin_lock_init(&queue->irqlock);
 	INIT_LIST_HEAD(&queue->mainqueue);
 	INIT_LIST_HEAD(&queue->irqqueue);
+	queue->type = type;
 }
 
 /*
@@ -132,7 +133,7 @@
 		queue->buffer[i].buf.index = i;
 		queue->buffer[i].buf.m.offset = i * bufsize;
 		queue->buffer[i].buf.length = buflength;
-		queue->buffer[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		queue->buffer[i].buf.type = queue->type;
 		queue->buffer[i].buf.sequence = 0;
 		queue->buffer[i].buf.field = V4L2_FIELD_NONE;
 		queue->buffer[i].buf.memory = V4L2_MEMORY_MMAP;
@@ -226,7 +227,7 @@
 
 	uvc_trace(UVC_TRACE_CAPTURE, "Queuing buffer %u.\n", v4l2_buf->index);
 
-	if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+	if (v4l2_buf->type != queue->type ||
 	    v4l2_buf->memory != V4L2_MEMORY_MMAP) {
 		uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) "
 			"and/or memory (%u).\n", v4l2_buf->type,
@@ -249,6 +250,13 @@
 		goto done;
 	}
 
+	if (v4l2_buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+	    v4l2_buf->bytesused > buf->buf.length) {
+		uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n");
+		ret = -EINVAL;
+		goto done;
+	}
+
 	spin_lock_irqsave(&queue->irqlock, flags);
 	if (queue->flags & UVC_QUEUE_DISCONNECTED) {
 		spin_unlock_irqrestore(&queue->irqlock, flags);
@@ -256,7 +264,11 @@
 		goto done;
 	}
 	buf->state = UVC_BUF_STATE_QUEUED;
-	buf->buf.bytesused = 0;
+	if (v4l2_buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		buf->buf.bytesused = 0;
+	else
+		buf->buf.bytesused = v4l2_buf->bytesused;
+
 	list_add_tail(&buf->stream, &queue->mainqueue);
 	list_add_tail(&buf->queue, &queue->irqqueue);
 	spin_unlock_irqrestore(&queue->irqlock, flags);
@@ -289,7 +301,7 @@
 	struct uvc_buffer *buf;
 	int ret = 0;
 
-	if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+	if (v4l2_buf->type != queue->type ||
 	    v4l2_buf->memory != V4L2_MEMORY_MMAP) {
 		uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) "
 			"and/or memory (%u).\n", v4l2_buf->type,
@@ -397,6 +409,7 @@
 		}
 		queue->sequence = 0;
 		queue->flags |= UVC_QUEUE_STREAMING;
+		queue->buf_used = 0;
 	} else {
 		uvc_queue_cancel(queue, 0);
 		INIT_LIST_HEAD(&queue->mainqueue);
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c
index 2e1fd1b..afcc693 100644
--- a/drivers/media/video/uvc/uvc_v4l2.c
+++ b/drivers/media/video/uvc/uvc_v4l2.c
@@ -110,7 +110,7 @@
 	int ret = 0;
 	__u8 *fcc;
 
-	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+	if (fmt->type != video->streaming->type)
 		return -EINVAL;
 
 	fcc = (__u8 *)&fmt->fmt.pix.pixelformat;
@@ -216,7 +216,7 @@
 	struct uvc_format *format = video->streaming->cur_format;
 	struct uvc_frame *frame = video->streaming->cur_frame;
 
-	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+	if (fmt->type != video->streaming->type)
 		return -EINVAL;
 
 	if (format == NULL || frame == NULL)
@@ -242,7 +242,7 @@
 	struct uvc_frame *frame;
 	int ret;
 
-	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+	if (fmt->type != video->streaming->type)
 		return -EINVAL;
 
 	if (uvc_queue_streaming(&video->queue))
@@ -264,7 +264,7 @@
 {
 	uint32_t numerator, denominator;
 
-	if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+	if (parm->type != video->streaming->type)
 		return -EINVAL;
 
 	numerator = video->streaming->ctrl.dwFrameInterval;
@@ -272,13 +272,21 @@
 	uvc_simplify_fraction(&numerator, &denominator, 8, 333);
 
 	memset(parm, 0, sizeof *parm);
-	parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
-	parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
-	parm->parm.capture.capturemode = 0;
-	parm->parm.capture.timeperframe.numerator = numerator;
-	parm->parm.capture.timeperframe.denominator = denominator;
-	parm->parm.capture.extendedmode = 0;
-	parm->parm.capture.readbuffers = 0;
+	parm->type = video->streaming->type;
+
+	if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+		parm->parm.capture.capturemode = 0;
+		parm->parm.capture.timeperframe.numerator = numerator;
+		parm->parm.capture.timeperframe.denominator = denominator;
+		parm->parm.capture.extendedmode = 0;
+		parm->parm.capture.readbuffers = 0;
+	} else {
+		parm->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
+		parm->parm.output.outputmode = 0;
+		parm->parm.output.timeperframe.numerator = numerator;
+		parm->parm.output.timeperframe.denominator = denominator;
+	}
 
 	return 0;
 }
@@ -288,24 +296,27 @@
 {
 	struct uvc_frame *frame = video->streaming->cur_frame;
 	struct uvc_streaming_control probe;
+	struct v4l2_fract timeperframe;
 	uint32_t interval;
 	int ret;
 
-	if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+	if (parm->type != video->streaming->type)
 		return -EINVAL;
 
 	if (uvc_queue_streaming(&video->queue))
 		return -EBUSY;
 
+	if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		timeperframe = parm->parm.capture.timeperframe;
+	else
+		timeperframe = parm->parm.output.timeperframe;
+
 	memcpy(&probe, &video->streaming->ctrl, sizeof probe);
-	interval = uvc_fraction_to_interval(
-			parm->parm.capture.timeperframe.numerator,
-			parm->parm.capture.timeperframe.denominator);
+	interval = uvc_fraction_to_interval(timeperframe.numerator,
+		timeperframe.denominator);
 
 	uvc_trace(UVC_TRACE_FORMAT, "Setting frame interval to %u/%u (%u).\n",
-			parm->parm.capture.timeperframe.numerator,
-			parm->parm.capture.timeperframe.denominator,
-			interval);
+		timeperframe.numerator, timeperframe.denominator, interval);
 	probe.dwFrameInterval = uvc_try_frame_interval(frame, interval);
 
 	/* Probe the device with the new settings. */
@@ -315,11 +326,15 @@
 	memcpy(&video->streaming->ctrl, &probe, sizeof probe);
 
 	/* Return the actual frame period. */
-	parm->parm.capture.timeperframe.numerator = probe.dwFrameInterval;
-	parm->parm.capture.timeperframe.denominator = 10000000;
-	uvc_simplify_fraction(&parm->parm.capture.timeperframe.numerator,
-				&parm->parm.capture.timeperframe.denominator,
-				8, 333);
+	timeperframe.numerator = probe.dwFrameInterval;
+	timeperframe.denominator = 10000000;
+	uvc_simplify_fraction(&timeperframe.numerator,
+		&timeperframe.denominator, 8, 333);
+
+	if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		parm->parm.capture.timeperframe = timeperframe;
+	else
+		parm->parm.output.timeperframe = timeperframe;
 
 	return 0;
 }
@@ -476,8 +491,12 @@
 		strncpy(cap->bus_info, video->dev->udev->bus->bus_name,
 			sizeof cap->bus_info);
 		cap->version = DRIVER_VERSION_NUMBER;
-		cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
-				  | V4L2_CAP_STREAMING;
+		if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
+					  | V4L2_CAP_STREAMING;
+		else
+			cap->capabilities = V4L2_CAP_VIDEO_OUTPUT
+					  | V4L2_CAP_STREAMING;
 		break;
 	}
 
@@ -655,7 +674,7 @@
 		struct v4l2_fmtdesc *fmt = arg;
 		struct uvc_format *format;
 
-		if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+		if (fmt->type != video->streaming->type ||
 		    fmt->index >= video->streaming->nformats)
 			return -EINVAL;
 
@@ -794,7 +813,7 @@
 		struct v4l2_cropcap *ccap = arg;
 		struct uvc_frame *frame = video->streaming->cur_frame;
 
-		if (ccap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		if (ccap->type != video->streaming->type)
 			return -EINVAL;
 
 		ccap->bounds.left = 0;
@@ -820,7 +839,7 @@
 		unsigned int bufsize =
 			video->streaming->ctrl.dwMaxVideoFrameSize;
 
-		if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+		if (rb->type != video->streaming->type ||
 		    rb->memory != V4L2_MEMORY_MMAP)
 			return -EINVAL;
 
@@ -840,7 +859,7 @@
 	{
 		struct v4l2_buffer *buf = arg;
 
-		if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		if (buf->type != video->streaming->type)
 			return -EINVAL;
 
 		if (!uvc_has_privileges(handle))
@@ -866,7 +885,7 @@
 	{
 		int *type = arg;
 
-		if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		if (*type != video->streaming->type)
 			return -EINVAL;
 
 		if (!uvc_has_privileges(handle))
@@ -881,7 +900,7 @@
 	{
 		int *type = arg;
 
-		if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		if (*type != video->streaming->type)
 			return -EINVAL;
 
 		if (!uvc_has_privileges(handle))
diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c
index 6d0ac3b..e7c3199 100644
--- a/drivers/media/video/uvc/uvc_video.c
+++ b/drivers/media/video/uvc/uvc_video.c
@@ -453,6 +453,34 @@
 	}
 }
 
+static int uvc_video_encode_header(struct uvc_video_device *video,
+		struct uvc_buffer *buf, __u8 *data, int len)
+{
+	data[0] = 2;	/* Header length */
+	data[1] = UVC_STREAM_EOH | UVC_STREAM_EOF
+		| (video->last_fid & UVC_STREAM_FID);
+	return 2;
+}
+
+static int uvc_video_encode_data(struct uvc_video_device *video,
+		struct uvc_buffer *buf, __u8 *data, int len)
+{
+	struct uvc_video_queue *queue = &video->queue;
+	unsigned int nbytes;
+	void *mem;
+
+	/* Copy video data to the URB buffer. */
+	mem = queue->mem + buf->buf.m.offset + queue->buf_used;
+	nbytes = min((unsigned int)len, buf->buf.bytesused - queue->buf_used);
+	nbytes = min(video->bulk.max_payload_size - video->bulk.payload_size,
+			nbytes);
+	memcpy(data, mem, nbytes);
+
+	queue->buf_used += nbytes;
+
+	return nbytes;
+}
+
 /* ------------------------------------------------------------------------
  * URB handling
  */
@@ -559,6 +587,48 @@
 	}
 }
 
+static void uvc_video_encode_bulk(struct urb *urb,
+	struct uvc_video_device *video, struct uvc_buffer *buf)
+{
+	u8 *mem = urb->transfer_buffer;
+	int len = video->urb_size, ret;
+
+	if (buf == NULL) {
+		urb->transfer_buffer_length = 0;
+		return;
+	}
+
+	/* If the URB is the first of its payload, add the header. */
+	if (video->bulk.header_size == 0) {
+		ret = uvc_video_encode_header(video, buf, mem, len);
+		video->bulk.header_size = ret;
+		video->bulk.payload_size += ret;
+		mem += ret;
+		len -= ret;
+	}
+
+	/* Process video data. */
+	ret = uvc_video_encode_data(video, buf, mem, len);
+
+	video->bulk.payload_size += ret;
+	len -= ret;
+
+	if (buf->buf.bytesused == video->queue.buf_used ||
+	    video->bulk.payload_size == video->bulk.max_payload_size) {
+		if (buf->buf.bytesused == video->queue.buf_used) {
+			video->queue.buf_used = 0;
+			buf->state = UVC_BUF_STATE_DONE;
+			uvc_queue_next_buffer(&video->queue, buf);
+			video->last_fid ^= UVC_STREAM_FID;
+		}
+
+		video->bulk.header_size = 0;
+		video->bulk.payload_size = 0;
+	}
+
+	urb->transfer_buffer_length = video->urb_size - len;
+}
+
 static void uvc_video_complete(struct urb *urb)
 {
 	struct uvc_video_device *video = urb->context;
@@ -756,7 +826,15 @@
 	if (uvc_alloc_urb_buffers(video, size) < 0)
 		return -ENOMEM;
 
-	pipe = usb_rcvbulkpipe(video->dev->udev, ep->desc.bEndpointAddress);
+	if (usb_endpoint_dir_in(&ep->desc))
+		pipe = usb_rcvbulkpipe(video->dev->udev,
+				       ep->desc.bEndpointAddress);
+	else
+		pipe = usb_sndbulkpipe(video->dev->udev,
+				       ep->desc.bEndpointAddress);
+
+	if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+		size = 0;
 
 	for (i = 0; i < UVC_URBS; ++i) {
 		urb = usb_alloc_urb(0, gfp_flags);
@@ -977,12 +1055,22 @@
 	atomic_set(&video->active, 0);
 
 	/* Select the video decoding function */
-	if (video->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)
-		video->decode = uvc_video_decode_isight;
-	else if (video->streaming->intf->num_altsetting > 1)
-		video->decode = uvc_video_decode_isoc;
-	else
-		video->decode = uvc_video_decode_bulk;
+	if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+		if (video->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)
+			video->decode = uvc_video_decode_isight;
+		else if (video->streaming->intf->num_altsetting > 1)
+			video->decode = uvc_video_decode_isoc;
+		else
+			video->decode = uvc_video_decode_bulk;
+	} else {
+		if (video->streaming->intf->num_altsetting == 1)
+			video->decode = uvc_video_encode_bulk;
+		else {
+			uvc_printk(KERN_INFO, "Isochronous endpoints are not "
+				"supported for video output devices.\n");
+			return -EINVAL;
+		}
+	}
 
 	return 0;
 }
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h
index 2a5bdac..731cf79 100644
--- a/drivers/media/video/uvc/uvcvideo.h
+++ b/drivers/media/video/uvc/uvcvideo.h
@@ -529,6 +529,7 @@
 	__u16 maxpsize;
 
 	struct uvc_streaming_header header;
+	enum v4l2_buf_type type;
 
 	unsigned int nformats;
 	struct uvc_format *format;
@@ -564,12 +565,15 @@
 #define UVC_QUEUE_DROP_INCOMPLETE	(1 << 2)
 
 struct uvc_video_queue {
+	enum v4l2_buf_type type;
+
 	void *mem;
 	unsigned int flags;
 	__u32 sequence;
 
 	unsigned int count;
 	unsigned int buf_size;
+	unsigned int buf_used;
 	struct uvc_buffer buffer[UVC_MAX_VIDEO_BUFFERS];
 	struct mutex mutex;	/* protects buffers and mainqueue */
 	spinlock_t irqlock;	/* protects irqqueue */
@@ -584,8 +588,9 @@
 	atomic_t active;
 	unsigned int frozen : 1;
 
-	struct list_head iterms;
-	struct uvc_entity *oterm;
+	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;
@@ -726,7 +731,8 @@
 extern void uvc_delete(struct kref *kref);
 
 /* Video buffers queue management. */
-extern void uvc_queue_init(struct uvc_video_queue *queue);
+extern void uvc_queue_init(struct uvc_video_queue *queue,
+		enum v4l2_buf_type type);
 extern int uvc_alloc_buffers(struct uvc_video_queue *queue,
 		unsigned int nbuffers, unsigned int buflength);
 extern int uvc_free_buffers(struct uvc_video_queue *queue);