Commit ff924203 authored by Laurent Pinchart's avatar Laurent Pinchart Committed by Mauro Carvalho Chehab

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: default avatarLaurent Pinchart <laurent.pinchart@skynet.be>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 538e7a00
...@@ -12,8 +12,8 @@ ...@@ -12,8 +12,8 @@
*/ */
/* /*
* This driver aims to support video input devices compliant with the 'USB * This driver aims to support video input and ouput devices compliant with the
* Video Class' specification. * 'USB Video Class' specification.
* *
* The driver doesn't support the deprecated v4l1 interface. It implements the * 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 * 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, ...@@ -609,46 +609,55 @@ static int uvc_parse_streaming(struct uvc_device *dev,
} }
/* Parse the header descriptor. */ /* 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 " uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface "
"%d OUTPUT HEADER descriptor is not supported.\n", "%d HEADER descriptor not found.\n", dev->udev->devnum,
dev->udev->devnum, alts->desc.bInterfaceNumber); alts->desc.bInterfaceNumber);
goto error; 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) { p = buflen >= 4 ? buffer[3] : 0;
uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " n = buflen >= size ? buffer[size-1] : 0;
"interface %d INPUT HEADER descriptor is "
"invalid.\n", dev->udev->devnum, if (buflen < size + p*n) {
alts->desc.bInterfaceNumber); uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
goto error; "interface %d HEADER descriptor is invalid.\n",
} dev->udev->devnum, alts->desc.bInterfaceNumber);
goto error;
}
streaming->header.bNumFormats = p; streaming->header.bNumFormats = p;
streaming->header.bEndpointAddress = buffer[6]; streaming->header.bEndpointAddress = buffer[6];
if (buffer[2] == VS_INPUT_HEADER) {
streaming->header.bmInfo = buffer[7]; streaming->header.bmInfo = buffer[7];
streaming->header.bTerminalLink = buffer[8]; streaming->header.bTerminalLink = buffer[8];
streaming->header.bStillCaptureMethod = buffer[9]; streaming->header.bStillCaptureMethod = buffer[9];
streaming->header.bTriggerSupport = buffer[10]; streaming->header.bTriggerSupport = buffer[10];
streaming->header.bTriggerUsage = buffer[11]; 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 { } else {
uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " streaming->header.bTerminalLink = buffer[7];
"%d HEADER descriptor not found.\n", dev->udev->devnum, }
alts->desc.bInterfaceNumber); streaming->header.bControlSize = n;
streaming->header.bmaControls = kmalloc(p*n, GFP_KERNEL);
if (streaming->header.bmaControls == NULL) {
ret = -ENOMEM;
goto error; goto error;
} }
memcpy(streaming->header.bmaControls, &buffer[size], p*n);
buflen -= buffer[0]; buflen -= buffer[0];
buffer += buffer[0]; buffer += buffer[0];
...@@ -1258,6 +1267,26 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video, ...@@ -1258,6 +1267,26 @@ static int uvc_scan_chain_entity(struct uvc_video_device *video,
list_add_tail(&entity->chain, &video->iterms); list_add_tail(&entity->chain, &video->iterms);
break; 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: default:
uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type " uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type "
"0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity)); "0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity));
...@@ -1368,6 +1397,10 @@ static int uvc_scan_chain(struct uvc_video_device *video) ...@@ -1368,6 +1397,10 @@ static int uvc_scan_chain(struct uvc_video_device *video)
entity = video->oterm; entity = video->oterm;
uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", entity->id); 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; id = entity->output.bSourceID;
while (id != 0) { while (id != 0) {
prev = entity; prev = entity;
...@@ -1396,8 +1429,11 @@ static int uvc_scan_chain(struct uvc_video_device *video) ...@@ -1396,8 +1429,11 @@ static int uvc_scan_chain(struct uvc_video_device *video)
return id; return id;
} }
/* Initialize the video buffers queue. */ if (video->sterm == NULL) {
uvc_queue_init(&video->queue); uvc_trace(UVC_TRACE_DESCR, "No streaming entity found in "
"chain.\n");
return -1;
}
return 0; return 0;
} }
...@@ -1408,7 +1444,8 @@ static int uvc_scan_chain(struct uvc_video_device *video) ...@@ -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 * The driver currently supports a single video device per control interface
* only. The terminal and units must match the following structure: * 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 * The Extension Units, if present, must have a single input pin. The
* Processing Unit and Extension Units can be in any order. Additional * Processing Unit and Extension Units can be in any order. Additional
...@@ -1425,7 +1462,7 @@ static int uvc_register_video(struct uvc_device *dev) ...@@ -1425,7 +1462,7 @@ static int uvc_register_video(struct uvc_device *dev)
list_for_each_entry(term, &dev->entities, list) { list_for_each_entry(term, &dev->entities, list) {
struct uvc_streaming *streaming; struct uvc_streaming *streaming;
if (UVC_ENTITY_TYPE(term) != TT_STREAMING) if (!UVC_ENTITY_IS_TERM(term) || !UVC_ENTITY_IS_OTERM(term))
continue; continue;
memset(&dev->video, 0, sizeof dev->video); memset(&dev->video, 0, sizeof dev->video);
...@@ -1438,7 +1475,8 @@ static int uvc_register_video(struct uvc_device *dev) ...@@ -1438,7 +1475,8 @@ static int uvc_register_video(struct uvc_device *dev)
continue; continue;
list_for_each_entry(streaming, &dev->streaming, list) { 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; dev->video.streaming = streaming;
found = 1; found = 1;
break; break;
...@@ -1464,6 +1502,9 @@ static int uvc_register_video(struct uvc_device *dev) ...@@ -1464,6 +1502,9 @@ static int uvc_register_video(struct uvc_device *dev)
printk(" -> %d).\n", dev->video.oterm->id); 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 /* Initialize the streaming interface with default streaming
* parameters. * parameters.
*/ */
......
...@@ -79,12 +79,13 @@ ...@@ -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); mutex_init(&queue->mutex);
spin_lock_init(&queue->irqlock); spin_lock_init(&queue->irqlock);
INIT_LIST_HEAD(&queue->mainqueue); INIT_LIST_HEAD(&queue->mainqueue);
INIT_LIST_HEAD(&queue->irqqueue); INIT_LIST_HEAD(&queue->irqqueue);
queue->type = type;
} }
/* /*
...@@ -132,7 +133,7 @@ int uvc_alloc_buffers(struct uvc_video_queue *queue, unsigned int nbuffers, ...@@ -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.index = i;
queue->buffer[i].buf.m.offset = i * bufsize; queue->buffer[i].buf.m.offset = i * bufsize;
queue->buffer[i].buf.length = buflength; 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.sequence = 0;
queue->buffer[i].buf.field = V4L2_FIELD_NONE; queue->buffer[i].buf.field = V4L2_FIELD_NONE;
queue->buffer[i].buf.memory = V4L2_MEMORY_MMAP; queue->buffer[i].buf.memory = V4L2_MEMORY_MMAP;
...@@ -226,7 +227,7 @@ int uvc_queue_buffer(struct uvc_video_queue *queue, ...@@ -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); 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) { v4l2_buf->memory != V4L2_MEMORY_MMAP) {
uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) " uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) "
"and/or memory (%u).\n", v4l2_buf->type, "and/or memory (%u).\n", v4l2_buf->type,
...@@ -249,6 +250,13 @@ int uvc_queue_buffer(struct uvc_video_queue *queue, ...@@ -249,6 +250,13 @@ int uvc_queue_buffer(struct uvc_video_queue *queue,
goto done; 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); spin_lock_irqsave(&queue->irqlock, flags);
if (queue->flags & UVC_QUEUE_DISCONNECTED) { if (queue->flags & UVC_QUEUE_DISCONNECTED) {
spin_unlock_irqrestore(&queue->irqlock, flags); spin_unlock_irqrestore(&queue->irqlock, flags);
...@@ -256,7 +264,11 @@ int uvc_queue_buffer(struct uvc_video_queue *queue, ...@@ -256,7 +264,11 @@ int uvc_queue_buffer(struct uvc_video_queue *queue,
goto done; goto done;
} }
buf->state = UVC_BUF_STATE_QUEUED; 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->stream, &queue->mainqueue);
list_add_tail(&buf->queue, &queue->irqqueue); list_add_tail(&buf->queue, &queue->irqqueue);
spin_unlock_irqrestore(&queue->irqlock, flags); spin_unlock_irqrestore(&queue->irqlock, flags);
...@@ -289,7 +301,7 @@ int uvc_dequeue_buffer(struct uvc_video_queue *queue, ...@@ -289,7 +301,7 @@ int uvc_dequeue_buffer(struct uvc_video_queue *queue,
struct uvc_buffer *buf; struct uvc_buffer *buf;
int ret = 0; int ret = 0;
if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || if (v4l2_buf->type != queue->type ||
v4l2_buf->memory != V4L2_MEMORY_MMAP) { v4l2_buf->memory != V4L2_MEMORY_MMAP) {
uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) " uvc_trace(UVC_TRACE_CAPTURE, "[E] Invalid buffer type (%u) "
"and/or memory (%u).\n", v4l2_buf->type, "and/or memory (%u).\n", v4l2_buf->type,
...@@ -397,6 +409,7 @@ int uvc_queue_enable(struct uvc_video_queue *queue, int enable) ...@@ -397,6 +409,7 @@ int uvc_queue_enable(struct uvc_video_queue *queue, int enable)
} }
queue->sequence = 0; queue->sequence = 0;
queue->flags |= UVC_QUEUE_STREAMING; queue->flags |= UVC_QUEUE_STREAMING;
queue->buf_used = 0;
} else { } else {
uvc_queue_cancel(queue, 0); uvc_queue_cancel(queue, 0);
INIT_LIST_HEAD(&queue->mainqueue); INIT_LIST_HEAD(&queue->mainqueue);
......
...@@ -110,7 +110,7 @@ static int uvc_v4l2_try_format(struct uvc_video_device *video, ...@@ -110,7 +110,7 @@ static int uvc_v4l2_try_format(struct uvc_video_device *video,
int ret = 0; int ret = 0;
__u8 *fcc; __u8 *fcc;
if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) if (fmt->type != video->streaming->type)
return -EINVAL; return -EINVAL;
fcc = (__u8 *)&fmt->fmt.pix.pixelformat; fcc = (__u8 *)&fmt->fmt.pix.pixelformat;
...@@ -216,7 +216,7 @@ static int uvc_v4l2_get_format(struct uvc_video_device *video, ...@@ -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_format *format = video->streaming->cur_format;
struct uvc_frame *frame = video->streaming->cur_frame; struct uvc_frame *frame = video->streaming->cur_frame;
if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) if (fmt->type != video->streaming->type)
return -EINVAL; return -EINVAL;
if (format == NULL || frame == NULL) if (format == NULL || frame == NULL)
...@@ -242,7 +242,7 @@ static int uvc_v4l2_set_format(struct uvc_video_device *video, ...@@ -242,7 +242,7 @@ static int uvc_v4l2_set_format(struct uvc_video_device *video,
struct uvc_frame *frame; struct uvc_frame *frame;
int ret; int ret;
if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) if (fmt->type != video->streaming->type)
return -EINVAL; return -EINVAL;
if (uvc_queue_streaming(&video->queue)) if (uvc_queue_streaming(&video->queue))
...@@ -264,7 +264,7 @@ static int uvc_v4l2_get_streamparm(struct uvc_video_device *video, ...@@ -264,7 +264,7 @@ static int uvc_v4l2_get_streamparm(struct uvc_video_device *video,
{ {
uint32_t numerator, denominator; uint32_t numerator, denominator;
if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) if (parm->type != video->streaming->type)
return -EINVAL; return -EINVAL;
numerator = video->streaming->ctrl.dwFrameInterval; numerator = video->streaming->ctrl.dwFrameInterval;
...@@ -272,13 +272,21 @@ static int uvc_v4l2_get_streamparm(struct uvc_video_device *video, ...@@ -272,13 +272,21 @@ static int uvc_v4l2_get_streamparm(struct uvc_video_device *video,
uvc_simplify_fraction(&numerator, &denominator, 8, 333); uvc_simplify_fraction(&numerator, &denominator, 8, 333);
memset(parm, 0, sizeof *parm); memset(parm, 0, sizeof *parm);
parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; parm->type = video->streaming->type;
parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
parm->parm.capture.capturemode = 0; if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
parm->parm.capture.timeperframe.numerator = numerator; parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
parm->parm.capture.timeperframe.denominator = denominator; parm->parm.capture.capturemode = 0;
parm->parm.capture.extendedmode = 0; parm->parm.capture.timeperframe.numerator = numerator;
parm->parm.capture.readbuffers = 0; 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; return 0;
} }
...@@ -288,24 +296,27 @@ static int uvc_v4l2_set_streamparm(struct uvc_video_device *video, ...@@ -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_frame *frame = video->streaming->cur_frame;
struct uvc_streaming_control probe; struct uvc_streaming_control probe;
struct v4l2_fract timeperframe;
uint32_t interval; uint32_t interval;
int ret; int ret;
if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) if (parm->type != video->streaming->type)
return -EINVAL; return -EINVAL;
if (uvc_queue_streaming(&video->queue)) if (uvc_queue_streaming(&video->queue))
return -EBUSY; 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); memcpy(&probe, &video->streaming->ctrl, sizeof probe);
interval = uvc_fraction_to_interval( interval = uvc_fraction_to_interval(timeperframe.numerator,
parm->parm.capture.timeperframe.numerator, timeperframe.denominator);
parm->parm.capture.timeperframe.denominator);
uvc_trace(UVC_TRACE_FORMAT, "Setting frame interval to %u/%u (%u).\n", uvc_trace(UVC_TRACE_FORMAT, "Setting frame interval to %u/%u (%u).\n",
parm->parm.capture.timeperframe.numerator, timeperframe.numerator, timeperframe.denominator, interval);
parm->parm.capture.timeperframe.denominator,
interval);
probe.dwFrameInterval = uvc_try_frame_interval(frame, interval); probe.dwFrameInterval = uvc_try_frame_interval(frame, interval);
/* Probe the device with the new settings. */ /* Probe the device with the new settings. */
...@@ -315,11 +326,15 @@ static int uvc_v4l2_set_streamparm(struct uvc_video_device *video, ...@@ -315,11 +326,15 @@ static int uvc_v4l2_set_streamparm(struct uvc_video_device *video,
memcpy(&video->streaming->ctrl, &probe, sizeof probe); memcpy(&video->streaming->ctrl, &probe, sizeof probe);
/* Return the actual frame period. */ /* Return the actual frame period. */
parm->parm.capture.timeperframe.numerator = probe.dwFrameInterval; timeperframe.numerator = probe.dwFrameInterval;
parm->parm.capture.timeperframe.denominator = 10000000; timeperframe.denominator = 10000000;
uvc_simplify_fraction(&parm->parm.capture.timeperframe.numerator, uvc_simplify_fraction(&timeperframe.numerator,
&parm->parm.capture.timeperframe.denominator, &timeperframe.denominator, 8, 333);
8, 333);
if (parm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
parm->parm.capture.timeperframe = timeperframe;
else
parm->parm.output.timeperframe = timeperframe;
return 0; return 0;
} }
...@@ -476,8 +491,12 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ...@@ -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, strncpy(cap->bus_info, video->dev->udev->bus->bus_name,
sizeof cap->bus_info); sizeof cap->bus_info);
cap->version = DRIVER_VERSION_NUMBER; cap->version = DRIVER_VERSION_NUMBER;
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
| V4L2_CAP_STREAMING; cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
| V4L2_CAP_STREAMING;
else
cap->capabilities = V4L2_CAP_VIDEO_OUTPUT
| V4L2_CAP_STREAMING;
break; break;
} }
...@@ -655,7 +674,7 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ...@@ -655,7 +674,7 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
struct v4l2_fmtdesc *fmt = arg; struct v4l2_fmtdesc *fmt = arg;
struct uvc_format *format; struct uvc_format *format;
if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || if (fmt->type != video->streaming->type ||
fmt->index >= video->streaming->nformats) fmt->index >= video->streaming->nformats)
return -EINVAL; return -EINVAL;
...@@ -794,7 +813,7 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ...@@ -794,7 +813,7 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
struct v4l2_cropcap *ccap = arg; struct v4l2_cropcap *ccap = arg;
struct uvc_frame *frame = video->streaming->cur_frame; struct uvc_frame *frame = video->streaming->cur_frame;
if (ccap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) if (ccap->type != video->streaming->type)
return -EINVAL; return -EINVAL;
ccap->bounds.left = 0; ccap->bounds.left = 0;
...@@ -820,7 +839,7 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ...@@ -820,7 +839,7 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
unsigned int bufsize = unsigned int bufsize =
video->streaming->ctrl.dwMaxVideoFrameSize; video->streaming->ctrl.dwMaxVideoFrameSize;
if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || if (rb->type != video->streaming->type ||
rb->memory != V4L2_MEMORY_MMAP) rb->memory != V4L2_MEMORY_MMAP)
return -EINVAL; return -EINVAL;
...@@ -840,7 +859,7 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ...@@ -840,7 +859,7 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{ {
struct v4l2_buffer *buf = arg; struct v4l2_buffer *buf = arg;
if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) if (buf->type != video->streaming->type)
return -EINVAL; return -EINVAL;
if (!uvc_has_privileges(handle)) if (!uvc_has_privileges(handle))
...@@ -866,7 +885,7 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ...@@ -866,7 +885,7 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{ {
int *type = arg; int *type = arg;
if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE) if (*type != video->streaming->type)
return -EINVAL; return -EINVAL;
if (!uvc_has_privileges(handle)) if (!uvc_has_privileges(handle))
...@@ -881,7 +900,7 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ...@@ -881,7 +900,7 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{ {
int *type = arg; int *type = arg;
if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE) if (*type != video->streaming->type)
return -EINVAL; return -EINVAL;
if (!uvc_has_privileges(handle)) if (!uvc_has_privileges(handle))
......
...@@ -453,6 +453,34 @@ static void uvc_video_decode_end(struct uvc_video_device *video, ...@@ -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 * URB handling
*/ */
...@@ -559,6 +587,48 @@ static void uvc_video_decode_bulk(struct urb *urb, ...@@ -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) static void uvc_video_complete(struct urb *urb)
{ {
struct uvc_video_device *video = urb->context; struct uvc_video_device *video = urb->context;
...@@ -756,7 +826,15 @@ static int uvc_init_video_bulk(struct uvc_video_device *video, ...@@ -756,7 +826,15 @@ static int uvc_init_video_bulk(struct uvc_video_device *video,
if (uvc_alloc_urb_buffers(video, size) < 0) if (uvc_alloc_urb_buffers(video, size) < 0)
return -ENOMEM; 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) { for (i = 0; i < UVC_URBS; ++i) {
urb = usb_alloc_urb(0, gfp_flags); urb = usb_alloc_urb(0, gfp_flags);
...@@ -977,12 +1055,22 @@ int uvc_video_init(struct uvc_video_device *video) ...@@ -977,12 +1055,22 @@ int uvc_video_init(struct uvc_video_device *video)
atomic_set(&video->active, 0); atomic_set(&video->active, 0);
/* Select the video decoding function */ /* Select the video decoding function */
if (video->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT) if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
video->decode = uvc_video_decode_isight; if (video->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)
else if (video->streaming->intf->num_altsetting > 1) video->decode = uvc_video_decode_isight;
video->decode = uvc_video_decode_isoc; else if (video->streaming->intf->num_altsetting > 1)
else video->decode = uvc_video_decode_isoc;
video->decode = uvc_video_decode_bulk; 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; return 0;
} }
......
...@@ -529,6 +529,7 @@ struct uvc_streaming { ...@@ -529,6 +529,7 @@ struct uvc_streaming {
__u16 maxpsize; __u16 maxpsize;
struct uvc_streaming_header header; struct uvc_streaming_header header;
enum v4l2_buf_type type;
unsigned int nformats; unsigned int nformats;
struct uvc_format *format; struct uvc_format *format;
...@@ -564,12 +565,15 @@ struct uvc_buffer { ...@@ -564,12 +565,15 @@ struct uvc_buffer {
#define UVC_QUEUE_DROP_INCOMPLETE (1 << 2) #define UVC_QUEUE_DROP_INCOMPLETE (1 << 2)
struct uvc_video_queue { struct uvc_video_queue {
enum v4l2_buf_type type;
void *mem; void *mem;
unsigned int flags; unsigned int flags;
__u32 sequence; __u32 sequence;
unsigned int count; unsigned int count;
unsigned int buf_size; unsigned int buf_size;
unsigned int buf_used;
struct uvc_buffer buffer[UVC_MAX_VIDEO_BUFFERS]; struct uvc_buffer buffer[UVC_MAX_VIDEO_BUFFERS];
struct mutex mutex; /* protects buffers and mainqueue */ struct mutex mutex; /* protects buffers and mainqueue */
spinlock_t irqlock; /* protects irqqueue */ spinlock_t irqlock; /* protects irqqueue */
...@@ -584,8 +588,9 @@ struct uvc_video_device { ...@@ -584,8 +588,9 @@ struct uvc_video_device {
atomic_t active; atomic_t active;
unsigned int frozen : 1; unsigned int frozen : 1;
struct list_head iterms; struct list_head iterms; /* Input terminals */
struct uvc_entity *oterm; struct uvc_entity *oterm; /* Output terminal */
struct uvc_entity *sterm; /* USB streaming terminal */
struct uvc_entity *processing; struct uvc_entity *processing;
struct uvc_entity *selector; struct uvc_entity *selector;
struct list_head extensions; struct list_head extensions;
...@@ -726,7 +731,8 @@ extern struct uvc_driver uvc_driver; ...@@ -726,7 +731,8 @@ extern struct uvc_driver uvc_driver;
extern void uvc_delete(struct kref *kref); extern void uvc_delete(struct kref *kref);
/* Video buffers queue management. */ /* 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, extern int uvc_alloc_buffers(struct uvc_video_queue *queue,
unsigned int nbuffers, unsigned int buflength); unsigned int nbuffers, unsigned int buflength);
extern int uvc_free_buffers(struct uvc_video_queue *queue); extern int uvc_free_buffers(struct uvc_video_queue *queue);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment