diff options
Diffstat (limited to 'drivers/media/usb/uvc/uvc_video.c')
| -rw-r--r-- | drivers/media/usb/uvc/uvc_video.c | 169 |
1 files changed, 89 insertions, 80 deletions
diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index e00f38dd07d9..2094e059d7d3 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -20,6 +20,7 @@ #include <linux/atomic.h> #include <linux/unaligned.h> +#include <media/jpeg.h> #include <media/v4l2-common.h> #include "uvcvideo.h" @@ -79,8 +80,29 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit, if (likely(ret == size)) return 0; + /* + * Some devices return shorter USB control packets than expected if the + * returned value can fit in less bytes. Zero all the bytes that the + * device has not written. + * + * This quirk is applied to all controls, regardless of their data type. + * Most controls are little-endian integers, in which case the missing + * bytes become 0 MSBs. For other data types, a different heuristic + * could be implemented if a device is found needing it. + * + * We exclude UVC_GET_INFO from the quirk. UVC_GET_LEN does not need + * to be excluded because its size is always 1. + */ + if (ret > 0 && query != UVC_GET_INFO) { + memset(data + ret, 0, size - ret); + dev_warn_once(&dev->intf->dev, + "UVC non compliance: %s control %u on unit %u returned %d bytes when we expected %u.\n", + uvc_query_name(query), cs, unit, ret, size); + return 0; + } + if (ret != -EPIPE) { - dev_err(&dev->udev->dev, + dev_err(&dev->intf->dev, "Failed to query (%s) UVC control %u on unit %u: %d (exp. %u).\n", uvc_query_name(query), cs, unit, ret, size); return ret < 0 ? ret : -EPIPE; @@ -96,8 +118,12 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit, error = *(u8 *)data; *(u8 *)data = tmp; - if (ret != 1) + if (ret != 1) { + dev_err_ratelimited(&dev->intf->dev, + "Failed to query (%s) UVC error code control %u on unit %u: %d (exp. 1).\n", + uvc_query_name(query), cs, unit, ret); return ret < 0 ? ret : -EPIPE; + } uvc_dbg(dev, CONTROL, "Control error %u\n", error); @@ -236,6 +262,15 @@ static void uvc_fixup_video_ctrl(struct uvc_streaming *stream, ctrl->dwMaxPayloadTransferSize = bandwidth; } + + if (stream->intf->num_altsetting > 1 && + ctrl->dwMaxPayloadTransferSize > stream->maxpsize) { + dev_warn_ratelimited(&stream->intf->dev, + "UVC non compliance: Reducing max payload transfer size (%u) to fit endpoint limit (%u).\n", + ctrl->dwMaxPayloadTransferSize, + stream->maxpsize); + ctrl->dwMaxPayloadTransferSize = stream->maxpsize; + } } static size_t uvc_video_ctrl_size(struct uvc_streaming *stream) @@ -297,8 +332,9 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream, goto out; } else if (ret != size) { dev_err(&stream->intf->dev, - "Failed to query (%u) UVC %s control : %d (exp. %u).\n", - query, probe ? "probe" : "commit", ret, size); + "Failed to query (%s) UVC %s control : %d (exp. %u).\n", + uvc_query_name(query), probe ? "probe" : "commit", + ret, size); ret = (ret == -EPROTO) ? -EPROTO : -EIO; goto out; } @@ -1116,6 +1152,7 @@ static void uvc_video_stats_stop(struct uvc_streaming *stream) static int uvc_video_decode_start(struct uvc_streaming *stream, struct uvc_buffer *buf, const u8 *data, int len) { + u8 header_len; u8 fid; /* @@ -1129,6 +1166,7 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, return -EINVAL; } + header_len = data[0]; fid = data[1] & UVC_STREAM_FID; /* @@ -1210,9 +1248,31 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, return -EAGAIN; } + /* + * Some cameras, when running two parallel streams (one MJPEG alongside + * another non-MJPEG stream), are known to lose the EOF packet for a frame. + * We can detect the end of a frame by checking for a new SOI marker, as + * the SOI always lies on the packet boundary between two frames for + * these devices. + */ + if (stream->dev->quirks & UVC_QUIRK_MJPEG_NO_EOF && + (stream->cur_format->fcc == V4L2_PIX_FMT_MJPEG || + stream->cur_format->fcc == V4L2_PIX_FMT_JPEG)) { + const u8 *packet = data + header_len; + + if (len >= header_len + 2 && + packet[0] == 0xff && packet[1] == JPEG_MARKER_SOI && + buf->bytesused != 0) { + buf->state = UVC_BUF_STATE_READY; + buf->error = 1; + stream->last_fid ^= UVC_STREAM_FID; + return -EAGAIN; + } + } + stream->last_fid = fid; - return data[0]; + return header_len; } static inline enum dma_data_direction uvc_stream_dir( @@ -1224,20 +1284,6 @@ static inline enum dma_data_direction uvc_stream_dir( return DMA_TO_DEVICE; } -static inline struct device *uvc_stream_to_dmadev(struct uvc_streaming *stream) -{ - return bus_to_hcd(stream->dev->udev->bus)->self.sysdev; -} - -static int uvc_submit_urb(struct uvc_urb *uvc_urb, gfp_t mem_flags) -{ - /* Sync DMA. */ - dma_sync_sgtable_for_device(uvc_stream_to_dmadev(uvc_urb->stream), - uvc_urb->sgt, - uvc_stream_dir(uvc_urb->stream)); - return usb_submit_urb(uvc_urb->urb, mem_flags); -} - /* * uvc_video_decode_data_work: Asynchronous memcpy processing * @@ -1259,7 +1305,7 @@ static void uvc_video_copy_data_work(struct work_struct *work) uvc_queue_buffer_release(op->buf); } - ret = uvc_submit_urb(uvc_urb, GFP_KERNEL); + ret = usb_submit_urb(uvc_urb->urb, GFP_KERNEL); if (ret < 0) dev_err(&uvc_urb->stream->intf->dev, "Failed to resubmit video URB (%d).\n", ret); @@ -1382,12 +1428,6 @@ static void uvc_video_decode_meta(struct uvc_streaming *stream, if (!meta_buf || length == 2) return; - if (meta_buf->length - meta_buf->bytesused < - length + sizeof(meta->ns) + sizeof(meta->sof)) { - meta_buf->error = 1; - return; - } - has_pts = mem[1] & UVC_STREAM_PTS; has_scr = mem[1] & UVC_STREAM_SCR; @@ -1408,6 +1448,12 @@ static void uvc_video_decode_meta(struct uvc_streaming *stream, !memcmp(scr, stream->clock.last_scr, 6))) return; + if (meta_buf->length - meta_buf->bytesused < + length + sizeof(meta->ns) + sizeof(meta->sof)) { + meta_buf->error = 1; + return; + } + meta = (struct uvc_meta_buf *)((u8 *)meta_buf->mem + meta_buf->bytesused); local_irq_save(flags); time = uvc_video_get_time(); @@ -1645,7 +1691,7 @@ static void uvc_video_complete(struct urb *urb) struct uvc_streaming *stream = uvc_urb->stream; struct uvc_video_queue *queue = &stream->queue; struct uvc_video_queue *qmeta = &stream->meta.queue; - struct vb2_queue *vb2_qmeta = stream->meta.vdev.queue; + struct vb2_queue *vb2_qmeta = stream->meta.queue.vdev.queue; struct uvc_buffer *buf = NULL; struct uvc_buffer *buf_meta = NULL; unsigned long flags; @@ -1685,12 +1731,6 @@ static void uvc_video_complete(struct urb *urb) /* Re-initialise the URB async work. */ uvc_urb->async_operations = 0; - /* Sync DMA and invalidate vmap range. */ - dma_sync_sgtable_for_cpu(uvc_stream_to_dmadev(uvc_urb->stream), - uvc_urb->sgt, uvc_stream_dir(stream)); - invalidate_kernel_vmap_range(uvc_urb->buffer, - uvc_urb->stream->urb_size); - /* * Process the URB headers, and optionally queue expensive memcpy tasks * to be deferred to a work queue. @@ -1699,7 +1739,7 @@ static void uvc_video_complete(struct urb *urb) /* If no async work is needed, resubmit the URB immediately. */ if (!uvc_urb->async_operations) { - ret = uvc_submit_urb(uvc_urb, GFP_ATOMIC); + ret = usb_submit_urb(uvc_urb->urb, GFP_ATOMIC); if (ret < 0) dev_err(&stream->intf->dev, "Failed to resubmit video URB (%d).\n", ret); @@ -1714,17 +1754,15 @@ static void uvc_video_complete(struct urb *urb) */ static void uvc_free_urb_buffers(struct uvc_streaming *stream) { - struct device *dma_dev = uvc_stream_to_dmadev(stream); + struct usb_device *udev = stream->dev->udev; struct uvc_urb *uvc_urb; for_each_uvc_urb(uvc_urb, stream) { if (!uvc_urb->buffer) continue; - dma_vunmap_noncontiguous(dma_dev, uvc_urb->buffer); - dma_free_noncontiguous(dma_dev, stream->urb_size, uvc_urb->sgt, - uvc_stream_dir(stream)); - + usb_free_noncoherent(udev, stream->urb_size, uvc_urb->buffer, + uvc_stream_dir(stream), uvc_urb->sgt); uvc_urb->buffer = NULL; uvc_urb->sgt = NULL; } @@ -1735,26 +1773,13 @@ static void uvc_free_urb_buffers(struct uvc_streaming *stream) static bool uvc_alloc_urb_buffer(struct uvc_streaming *stream, struct uvc_urb *uvc_urb, gfp_t gfp_flags) { - struct device *dma_dev = uvc_stream_to_dmadev(stream); - - uvc_urb->sgt = dma_alloc_noncontiguous(dma_dev, stream->urb_size, - uvc_stream_dir(stream), - gfp_flags, 0); - if (!uvc_urb->sgt) - return false; - uvc_urb->dma = uvc_urb->sgt->sgl->dma_address; - - uvc_urb->buffer = dma_vmap_noncontiguous(dma_dev, stream->urb_size, - uvc_urb->sgt); - if (!uvc_urb->buffer) { - dma_free_noncontiguous(dma_dev, stream->urb_size, - uvc_urb->sgt, - uvc_stream_dir(stream)); - uvc_urb->sgt = NULL; - return false; - } + struct usb_device *udev = stream->dev->udev; - return true; + uvc_urb->buffer = usb_alloc_noncoherent(udev, stream->urb_size, + gfp_flags, &uvc_urb->dma, + uvc_stream_dir(stream), + &uvc_urb->sgt); + return !!uvc_urb->buffer; } /* @@ -1845,24 +1870,6 @@ static void uvc_video_stop_transfer(struct uvc_streaming *stream, } /* - * Compute the maximum number of bytes per interval for an endpoint. - */ -u16 uvc_endpoint_max_bpi(struct usb_device *dev, struct usb_host_endpoint *ep) -{ - u16 psize; - - switch (dev->speed) { - case USB_SPEED_SUPER: - case USB_SPEED_SUPER_PLUS: - return le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval); - default: - psize = usb_endpoint_maxp(&ep->desc); - psize *= usb_endpoint_maxp_mult(&ep->desc); - return psize; - } -} - -/* * Initialize isochronous URBs and allocate transfer buffers. The packet size * is given by the endpoint. */ @@ -1872,10 +1879,10 @@ static int uvc_init_video_isoc(struct uvc_streaming *stream, struct urb *urb; struct uvc_urb *uvc_urb; unsigned int npackets, i; - u16 psize; + u32 psize; u32 size; - psize = uvc_endpoint_max_bpi(stream->dev->udev, ep); + psize = usb_endpoint_max_periodic_payload(stream->dev->udev, ep); size = stream->ctrl.dwMaxVideoFrameSize; npackets = uvc_alloc_urb_buffers(stream, size, psize, gfp_flags); @@ -1902,6 +1909,7 @@ static int uvc_init_video_isoc(struct uvc_streaming *stream, urb->complete = uvc_video_complete; urb->number_of_packets = npackets; urb->transfer_buffer_length = size; + urb->sgt = uvc_urb->sgt; for (i = 0; i < npackets; ++i) { urb->iso_frame_desc[i].offset = i * psize; @@ -1958,6 +1966,7 @@ static int uvc_init_video_bulk(struct uvc_streaming *stream, size, uvc_video_complete, uvc_urb); urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; urb->transfer_dma = uvc_urb->dma; + urb->sgt = uvc_urb->sgt; uvc_urb->urb = urb; } @@ -2016,7 +2025,7 @@ static int uvc_video_start_transfer(struct uvc_streaming *stream, continue; /* Check if the bandwidth is high enough. */ - psize = uvc_endpoint_max_bpi(stream->dev->udev, ep); + psize = usb_endpoint_max_periodic_payload(stream->dev->udev, ep); if (psize >= bandwidth && psize < best_psize) { altsetting = alts->desc.bAlternateSetting; best_psize = psize; @@ -2069,7 +2078,7 @@ static int uvc_video_start_transfer(struct uvc_streaming *stream, /* Submit the URBs. */ for_each_uvc_urb(uvc_urb, stream) { - ret = uvc_submit_urb(uvc_urb, gfp_flags); + ret = usb_submit_urb(uvc_urb->urb, gfp_flags); if (ret < 0) { dev_err(&stream->intf->dev, "Failed to submit URB %u (%d).\n", |
