diff options
Diffstat (limited to 'drivers/media/video/uvc/uvc_video.c')
-rw-r--r-- | drivers/media/video/uvc/uvc_video.c | 71 |
1 files changed, 58 insertions, 13 deletions
diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index 53f3ef4635eb..5555f0102838 100644 --- a/drivers/media/video/uvc/uvc_video.c +++ b/drivers/media/video/uvc/uvc_video.c @@ -1,8 +1,8 @@ /* * uvc_video.c -- USB Video Class driver - Video handling * - * Copyright (C) 2005-2009 - * Laurent Pinchart (laurent.pinchart@skynet.be) + * Copyright (C) 2005-2010 + * Laurent Pinchart (laurent.pinchart@ideasonboard.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -45,6 +45,30 @@ static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, unit << 8 | intfnum, data, size, timeout); } +static const char *uvc_query_name(__u8 query) +{ + switch (query) { + case UVC_SET_CUR: + return "SET_CUR"; + case UVC_GET_CUR: + return "GET_CUR"; + case UVC_GET_MIN: + return "GET_MIN"; + case UVC_GET_MAX: + return "GET_MAX"; + case UVC_GET_RES: + return "GET_RES"; + case UVC_GET_LEN: + return "GET_LEN"; + case UVC_GET_INFO: + return "GET_INFO"; + case UVC_GET_DEF: + return "GET_DEF"; + default: + return "<invalid>"; + } +} + int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, __u8 intfnum, __u8 cs, void *data, __u16 size) { @@ -53,9 +77,9 @@ int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, ret = __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size, UVC_CTRL_CONTROL_TIMEOUT); if (ret != size) { - uvc_printk(KERN_ERR, "Failed to query (%u) UVC control %u " - "(unit %u) : %d (exp. %u).\n", query, cs, unit, ret, - size); + uvc_printk(KERN_ERR, "Failed to query (%s) UVC control %u on " + "unit %u: %d (exp. %u).\n", uvc_query_name(query), cs, + unit, ret, size); return -EIO; } @@ -114,6 +138,15 @@ static void uvc_fixup_video_ctrl(struct uvc_streaming *stream, bandwidth /= 8; bandwidth += 12; + /* The bandwidth estimate is too low for many cameras. Don't use + * maximum packet sizes lower than 1024 bytes to try and work + * around the problem. According to measurements done on two + * different camera models, the value is high enough to get most + * resolutions working while not preventing two simultaneous + * VGA streams at 15 fps. + */ + bandwidth = max_t(u32, bandwidth, 1024); + ctrl->dwMaxPayloadTransferSize = bandwidth; } } @@ -394,6 +427,12 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, fid = data[1] & UVC_STREAM_FID; + /* Increase the sequence number regardless of any buffer states, so + * that discontinuous sequence numbers always indicate lost frames. + */ + if (stream->last_fid != fid) + stream->sequence++; + /* Store the payload FID bit and return immediately when the buffer is * NULL. */ @@ -427,6 +466,7 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, else ktime_get_real_ts(&ts); + buf->buf.sequence = stream->sequence; buf->buf.timestamp.tv_sec = ts.tv_sec; buf->buf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC; @@ -555,6 +595,9 @@ static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream, if (urb->iso_frame_desc[i].status < 0) { uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame " "lost (%d).\n", urb->iso_frame_desc[i].status); + /* Mark the buffer as faulty. */ + if (buf != NULL) + buf->error = 1; continue; } @@ -579,8 +622,14 @@ static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream, uvc_video_decode_end(stream, buf, mem, urb->iso_frame_desc[i].actual_length); - if (buf->state == UVC_BUF_STATE_READY) + if (buf->state == UVC_BUF_STATE_READY) { + if (buf->buf.length != buf->buf.bytesused && + !(stream->cur_format->flags & + UVC_FMT_FLAG_COMPRESSED)) + buf->error = 1; + buf = uvc_queue_next_buffer(&stream->queue, buf); + } } } @@ -679,6 +728,7 @@ static void uvc_video_encode_bulk(struct urb *urb, struct uvc_streaming *stream, if (buf->buf.bytesused == stream->queue.buf_used) { stream->queue.buf_used = 0; buf->state = UVC_BUF_STATE_READY; + buf->buf.sequence = ++stream->sequence; uvc_queue_next_buffer(&stream->queue, buf); stream->last_fid ^= UVC_STREAM_FID; } @@ -937,6 +987,7 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags) unsigned int i; int ret; + stream->sequence = -1; stream->last_fid = -1; stream->bulk.header_size = 0; stream->bulk.skip_payload = 0; @@ -1104,7 +1155,7 @@ int uvc_video_init(struct uvc_streaming *stream) atomic_set(&stream->active, 0); /* Initialize the video buffers queue. */ - uvc_queue_init(&stream->queue, stream->type); + uvc_queue_init(&stream->queue, stream->type, !uvc_no_drop_param); /* Alternate setting 0 should be the default, yet the XBox Live Vision * Cam (and possibly other devices) crash or otherwise misbehave if @@ -1197,12 +1248,6 @@ int uvc_video_enable(struct uvc_streaming *stream, int enable) return 0; } - if ((stream->cur_format->flags & UVC_FMT_FLAG_COMPRESSED) || - uvc_no_drop_param) - stream->queue.flags &= ~UVC_QUEUE_DROP_INCOMPLETE; - else - stream->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE; - ret = uvc_queue_enable(&stream->queue, 1); if (ret < 0) return ret; |