V4L/DVB (10104): uvcvideo: Add support for video output devices
Laurent Pinchart [Mon, 29 Dec 2008 01:32:29 +0000 (22:32 -0300)]
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>

drivers/media/video/uvc/uvc_driver.c
drivers/media/video/uvc/uvc_queue.c
drivers/media/video/uvc/uvc_v4l2.c
drivers/media/video/uvc/uvc_video.c
drivers/media/video/uvc/uvcvideo.h

index 18a6192..89d8bd1 100644 (file)
@@ -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 @@ static int uvc_parse_streaming(struct uvc_device *dev,
        }
 
        /* Parse the header descriptor. */
-       if (buffer[2] == VS_OUTPUT_HEADER) {
+       switch (buffer[2]) {
+       case VS_OUTPUT_HEADER:
+               streaming->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+               size = 9;
+               break;
+
+       case VS_INPUT_HEADER:
+               streaming->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+               size = 13;
+               break;
+
+       default:
                uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
-                       "%d OUTPUT HEADER descriptor is not supported.\n",
-                       dev->udev->devnum, alts->desc.bInterfaceNumber);
+                       "%d HEADER descriptor not found.\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;
+       }
 
-               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;
-               }
+       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];
+       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];
-               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 {
-               uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
-                       "%d HEADER descriptor not found.\n", dev->udev->devnum,
-                       alts->desc.bInterfaceNumber);
+               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 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video,
                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 @@ static int uvc_scan_chain(struct uvc_video_device *video)
 
        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 @@ static int uvc_scan_chain(struct uvc_video_device *video)
                        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 @@ static int uvc_scan_chain(struct uvc_video_device *video)
  * 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 @@ static int uvc_register_video(struct uvc_device *dev)
        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 @@ static int uvc_register_video(struct uvc_device *dev)
                        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 @@ static int uvc_register_video(struct uvc_device *dev)
                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.
         */
index 5646a6a..4254634 100644 (file)
  *
  */
 
-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 @@ int uvc_alloc_buffers(struct uvc_video_queue *queue, unsigned int nbuffers,
                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 @@ int uvc_queue_buffer(struct uvc_video_queue *queue,
 
        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 @@ int uvc_queue_buffer(struct uvc_video_queue *queue,
                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 @@ int uvc_queue_buffer(struct uvc_video_queue *queue,
                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 @@ int uvc_dequeue_buffer(struct uvc_video_queue *queue,
        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 @@ int uvc_queue_enable(struct uvc_video_queue *queue, int enable)
                }
                queue->sequence = 0;
                queue->flags |= UVC_QUEUE_STREAMING;
+               queue->buf_used = 0;
        } else {
                uvc_queue_cancel(queue, 0);
                INIT_LIST_HEAD(&queue->mainqueue);
index 2e1fd1b..afcc693 100644 (file)
@@ -110,7 +110,7 @@ static int uvc_v4l2_try_format(struct uvc_video_device *video,
        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 @@ static int uvc_v4l2_get_format(struct uvc_video_device *video,
        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 @@ static int uvc_v4l2_set_format(struct uvc_video_device *video,
        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 @@ static int uvc_v4l2_get_streamparm(struct uvc_video_device *video,
 {
        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 @@ static int uvc_v4l2_get_streamparm(struct uvc_video_device *video,
        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 @@ static int uvc_v4l2_set_streamparm(struct uvc_video_device *video,
 {
        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 @@ static int uvc_v4l2_set_streamparm(struct uvc_video_device *video,
        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 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
                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 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
                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 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
                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 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
                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 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
        {
                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 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
        {
                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 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
        {
                int *type = arg;
 
-               if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               if (*type != video->streaming->type)
                        return -EINVAL;
 
                if (!uvc_has_privileges(handle))
index 6d0ac3b..e7c3199 100644 (file)
@@ -453,6 +453,34 @@ static void uvc_video_decode_end(struct uvc_video_device *video,
        }
 }
 
+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_decode_bulk(struct urb *urb,
        }
 }
 
+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 @@ static int uvc_init_video_bulk(struct uvc_video_device *video,
        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 @@ int uvc_video_init(struct uvc_video_device *video)
        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;
 }
index 2a5bdac..731cf79 100644 (file)
@@ -529,6 +529,7 @@ struct uvc_streaming {
        __u16 maxpsize;
 
        struct uvc_streaming_header header;
+       enum v4l2_buf_type type;
 
        unsigned int nformats;
        struct uvc_format *format;
@@ -564,12 +565,15 @@ struct uvc_buffer {
 #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 @@ struct uvc_video_device {
        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 struct uvc_driver uvc_driver;
 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);