Skip to content

Commit

Permalink
V4L/DVB (10104): uvcvideo: Add support for video output devices
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Laurent Pinchart authored and Mauro Carvalho Chehab committed Dec 30, 2008
1 parent 538e7a0 commit ff92420
Show file tree
Hide file tree
Showing 5 changed files with 247 additions and 80 deletions.
109 changes: 75 additions & 34 deletions drivers/media/video/uvc/uvc_driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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];

Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand All @@ -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
Expand All @@ -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);
Expand All @@ -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;
Expand All @@ -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.
*/
Expand Down
23 changes: 18 additions & 5 deletions drivers/media/video/uvc/uvc_queue.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/*
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -249,14 +250,25 @@ 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);
ret = -ENODEV;
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);
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down
Loading

0 comments on commit ff92420

Please sign in to comment.