提交 ff924203 编写于 作者: L Laurent Pinchart 提交者: 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: NLaurent Pinchart <laurent.pinchart@skynet.be>
Signed-off-by: NMauro Carvalho Chehab <mchehab@redhat.com>
上级 538e7a00
......@@ -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,30 +609,45 @@ 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;
}
p = buflen >= 4 ? buffer[3] : 0;
n = buflen >= size ? buffer[size-1] : 0;
if (buflen < 13 + p*n || buffer[2] != VS_INPUT_HEADER) {
if (buflen < size + p*n) {
uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming "
"interface %d INPUT HEADER descriptor is "
"invalid.\n", dev->udev->devnum,
alts->desc.bInterfaceNumber);
"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);
......@@ -641,13 +656,7 @@ static int uvc_parse_streaming(struct uvc_device *dev,
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);
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.
*/
......
......@@ -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 @@ 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;
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);
......
......@@ -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->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;
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))
......
......@@ -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->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;
}
......
......@@ -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);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册