summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorJacob Meuser <jakemsr@cvs.openbsd.org>2011-04-11 02:04:49 +0000
committerJacob Meuser <jakemsr@cvs.openbsd.org>2011-04-11 02:04:49 +0000
commitcd6acbab36039ffe9683a9a91f9bcf249f805a9b (patch)
tree8f4c30a687b150823a47ff0fb2dc7faf53c56990 /sys/dev
parente64909ba110d040ab0358cba13ce99930acbc388 (diff)
uncompressed video formats have a fixed per-pixel bit depth, which
means the data size of a frame can be calculated if the dimensions are known. * calculate frame data sizes for uncompressed formats instead of believing what the hardware says. the UVC spec changed between 1.0 and 1.1, and as a result, some devices return bogus information. * skip under-sized as well as over-sized uncompressed frames; there is only one correct size for uncompressed frames. * remove quirk to fix uncompressed frame sizes on certain devices, since that now always happens. * check that the device is actually using the parameters we think it's using.
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/usb/uvideo.c163
-rw-r--r--sys/dev/usb/uvideo.h3
2 files changed, 101 insertions, 65 deletions
diff --git a/sys/dev/usb/uvideo.c b/sys/dev/usb/uvideo.c
index 03065c0244f..2e9712de891 100644
--- a/sys/dev/usb/uvideo.c
+++ b/sys/dev/usb/uvideo.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: uvideo.c,v 1.160 2011/04/04 17:38:24 jakemsr Exp $ */
+/* $OpenBSD: uvideo.c,v 1.161 2011/04/11 02:04:48 jakemsr Exp $ */
/*
* Copyright (c) 2008 Robert Nagy <robert@openbsd.org>
@@ -249,7 +249,6 @@ struct video_hw_if uvideo_hw_if = {
#define UVIDEO_FLAG_ISIGHT_STREAM_HEADER 0x1
#define UVIDEO_FLAG_REATTACH 0x2
#define UVIDEO_FLAG_VENDOR_CLASS 0x4
-#define UVIDEO_FLAG_FIX_MAX_VIDEO_FRAME_SIZE 0x8
struct uvideo_devs {
struct usb_devno uv_dev;
char *ucode_name;
@@ -325,27 +324,6 @@ struct uvideo_devs {
NULL,
UVIDEO_FLAG_VENDOR_CLASS
},
- {
- /* Needs to fix dwMaxVideoFrameSize */
- { USB_VENDOR_CHENSOURCE, USB_PRODUCT_CHENSOURCE_CM12402 },
- NULL,
- NULL,
- UVIDEO_FLAG_FIX_MAX_VIDEO_FRAME_SIZE
- },
- {
- /* Needs to fix dwMaxVideoFrameSize */
- { USB_VENDOR_MICRODIA, USB_PRODUCT_MICRODIA_CAM_1 },
- NULL,
- NULL,
- UVIDEO_FLAG_FIX_MAX_VIDEO_FRAME_SIZE
- },
- {
- /* Needs to fix dwMaxVideoFrameSize */
- { USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_LIFECAM },
- NULL,
- NULL,
- UVIDEO_FLAG_FIX_MAX_VIDEO_FRAME_SIZE
- },
};
#define uvideo_lookup(v, p) \
((struct uvideo_devs *)usb_lookup(uvideo_devs, v, p))
@@ -1077,15 +1055,20 @@ uvideo_vs_parse_desc_frame_sub(struct uvideo_softc *sc,
sc->sc_fmtgrp[fmtidx].frame_cur = fd;
/*
- * On some broken device, dwMaxVideoFrameBufferSize is not correct.
- * So fix it by frame width/height (XXX YUV2 format only).
+ * On some devices, dwMaxVideoFrameBufferSize is not correct.
+ * Version 1.1 of the UVC spec says this field is deprecated.
+ * For uncompressed pixel formats, the frame buffer size can
+ * be determined by multiplying width, height, and bytes per pixel.
+ * Uncompressed formats have a fixed number of bytes per pixel.
+ * Bytes per pixel can vary with compressed formats.
*/
- if (sc->sc_quirk &&
- sc->sc_quirk->flags & UVIDEO_FLAG_FIX_MAX_VIDEO_FRAME_SIZE &&
- sc->sc_fmtgrp[fmtidx].pixelformat == V4L2_PIX_FMT_YUYV) {
- fbuf_size = UGETW(fd->wWidth) * UGETW(fd->wHeight) * 4;
- DPRINTF(1, "wWidth = %d, wHeight = %d\n",
- UGETW(fd->wWidth), UGETW(fd->wHeight));
+ if (desc->bDescriptorSubtype == UDESCSUB_VS_FRAME_UNCOMPRESSED) {
+ fbuf_size = UGETW(fd->wWidth) * UGETW(fd->wHeight) *
+ sc->sc_fmtgrp[fmtidx].format->u.uc.bBitsPerPixel / NBBY;
+ DPRINTF(10, "%s: %s: frame buffer size=%d "
+ "width=%d height=%d bpp=%d\n", DEVNAME(sc), __func__,
+ fbuf_size, UGETW(fd->wWidth), UGETW(fd->wHeight),
+ sc->sc_fmtgrp[fmtidx].format->u.uc.bBitsPerPixel);
} else
fbuf_size = UGETDW(fd->dwMaxVideoFrameBufferSize);
@@ -1315,11 +1298,13 @@ uvideo_vs_negotiation(struct uvideo_softc *sc, int commit)
{
struct usb_video_probe_commit *pc;
struct uvideo_format_group *fmtgrp;
+ struct usb_video_header_desc *hd;
+ struct usb_video_frame_desc *frame;
uint8_t *p, *cur;
uint8_t probe_data[34];
uint32_t frame_ival, nivals, min, max, step, diff;
usbd_status error;
- int i, ival_bytes;
+ int i, ival_bytes, changed = 0;
pc = (struct usb_video_probe_commit *)probe_data;
@@ -1401,8 +1386,80 @@ uvideo_vs_negotiation(struct uvideo_softc *sc, int commit)
if (error != USBD_NORMAL_COMPLETION)
return (error);
+ /* check that the format and frame indexes are what we wanted */
+ if (pc->bFormatIndex != fmtgrp->format->bFormatIndex) {
+ changed++;
+ DPRINTF(1, "%s: %s: wanted format 0x%x, got format 0x%x\n",
+ DEVNAME(sc), __func__, fmtgrp->format->bFormatIndex,
+ pc->bFormatIndex);
+ for (i = 0; i < sc->sc_fmtgrp_num; i++) {
+ if (sc->sc_fmtgrp[i].format->bFormatIndex ==
+ pc->bFormatIndex) {
+ fmtgrp = &sc->sc_fmtgrp[i];
+ break;
+ }
+ }
+ if (i == sc->sc_fmtgrp_num) {
+ DPRINTF(1, "%s: %s: invalid format index 0x%x\n",
+ DEVNAME(sc), __func__, pc->bFormatIndex);
+ return (USBD_INVAL);
+ }
+ }
+ if (pc->bFrameIndex != fmtgrp->frame_cur->bFrameIndex) {
+ changed++;
+ DPRINTF(1, "%s: %s: wanted frame 0x%x, got frame 0x%x\n",
+ DEVNAME(sc), __func__, fmtgrp->frame_cur->bFrameIndex,
+ pc->bFrameIndex);
+ for (i = 0; i < fmtgrp->frame_num; i++) {
+ if (fmtgrp->frame[i]->bFrameIndex == pc->bFrameIndex) {
+ frame = fmtgrp->frame[i];
+ break;
+ }
+ if (i == fmtgrp->frame_num) {
+ DPRINTF(1, "%s: %s: invalid frame index 0x%x\n",
+ DEVNAME(sc), __func__, pc->bFrameIndex);
+ return (USBD_INVAL);
+ }
+ }
+ } else
+ frame = fmtgrp->frame_cur;
+
+ /*
+ * Uncompressed formats have fixed bits per pixel, which means
+ * the frame buffer size is fixed and can be calculated. Because
+ * some devices return incorrect values, always override the
+ * the frame size with a calculated value.
+ */
+ if (frame->bDescriptorSubtype == UDESCSUB_VS_FRAME_UNCOMPRESSED) {
+ USETDW(pc->dwMaxVideoFrameSize,
+ UGETW(frame->wWidth) * UGETW(frame->wHeight) *
+ fmtgrp->format->u.uc.bBitsPerPixel / NBBY);
+ DPRINTF(1, "fixed dwMaxVideoFrameSize=%d, "
+ "width=%d height=%d bpp=%d\n",
+ UGETDW(pc->dwMaxVideoFrameSize),
+ UGETW(frame->wWidth), UGETW(frame->wHeight),
+ fmtgrp->format->u.uc.bBitsPerPixel);
+ } else {
+ /*
+ * Some UVC 1.00 devices return dwMaxVideoFrameSize = 0.
+ * If so, fix it by format/frame descriptors.
+ */
+ hd = sc->sc_desc_vc_header.fix;
+ if (UGETDW(pc->dwMaxVideoFrameSize) == 0 &&
+ UGETW(hd->bcdUVC) < 0x0110 ) {
+ DPRINTF(1, "%s: dwMaxVideoFrameSize == 0, fixed\n",
+ DEVNAME(sc));
+ USETDW(pc->dwMaxVideoFrameSize,
+ UGETDW(frame->dwMaxVideoFrameBufferSize));
+ }
+ }
+
/* commit */
if (commit) {
+ if (changed > 0) {
+ /* didn't get the frame format or size we wanted */
+ return (USBD_INVAL);
+ }
error = uvideo_vs_set_commit(sc, probe_data);
if (error != USBD_NORMAL_COMPLETION)
return (error);
@@ -1466,7 +1523,6 @@ uvideo_vs_get_probe(struct uvideo_softc *sc, uint8_t *probe_data,
usbd_status error;
uint16_t tmp;
struct usb_video_probe_commit *pc;
- struct usb_video_header_desc *hd;
req.bmRequestType = UVIDEO_GET_IF;
req.bRequest = request;
@@ -1486,33 +1542,6 @@ uvideo_vs_get_probe(struct uvideo_softc *sc, uint8_t *probe_data,
}
DPRINTF(1, "%s: GET probe request successfully\n", DEVNAME(sc));
- /*
- * Some UVC 1.00 devices return dwMaxVideoFrameSize = 0.
- * If so, fix it by format/frame descriptors.
- */
- hd = sc->sc_desc_vc_header.fix;
- if (UGETDW(pc->dwMaxVideoFrameSize) == 0 &&
- UGETW(hd->bcdUVC) < 0x0110 ) {
- DPRINTF(1, "%s: dwMaxVideoFrameSize == 0, fixed\n",
- DEVNAME(sc));
- USETDW(pc->dwMaxVideoFrameSize,
- UGETDW(sc->sc_fmtgrp_cur->frame_cur
- ->dwMaxVideoFrameBufferSize));
-
- /*
- * On some broken device, the above value is not correct.
- * So fix it by frame width/height (XXX YUV2 format only).
- */
- if (sc->sc_quirk &&
- sc->sc_quirk->flags &
- UVIDEO_FLAG_FIX_MAX_VIDEO_FRAME_SIZE &&
- sc->sc_fmtgrp_cur->pixelformat == V4L2_PIX_FMT_YUYV) {
- USETDW(pc->dwMaxVideoFrameSize,
- UGETW(sc->sc_fmtgrp_cur->frame_cur->wWidth) *
- UGETW(sc->sc_fmtgrp_cur->frame_cur->wHeight) * 4);
- }
- }
-
DPRINTF(1, "bmHint=0x%02x\n", UGETW(pc->bmHint));
DPRINTF(1, "bFormatIndex=0x%02x\n", pc->bFormatIndex);
DPRINTF(1, "bFrameIndex=0x%02x\n", pc->bFrameIndex);
@@ -1584,6 +1613,8 @@ uvideo_vs_alloc_frame(struct uvideo_softc *sc)
fb->sample = 0;
fb->fid = 0;
fb->offset = 0;
+ fb->fmt_flags = sc->sc_fmtgrp_cur->frame_cur->bDescriptorSubtype ==
+ UDESCSUB_VS_FRAME_UNCOMPRESSED ? 0 : V4L2_FMT_FLAG_COMPRESSED;
return (USBD_NORMAL_COMPLETION);
}
@@ -2028,7 +2059,14 @@ uvideo_vs_decode_stream_header(struct uvideo_softc *sc, uint8_t *frame,
DPRINTF(2, "%s: %s: EOF (frame size = %d bytes)\n",
DEVNAME(sc), __func__, fb->offset);
- if (fb->offset <= fb->buf_size) {
+ if (fb->offset > fb->buf_size) {
+ DPRINTF(1, "%s: %s: frame too large, skipped!\n",
+ DEVNAME(sc), __func__);
+ } else if (fb->offset < fb->buf_size &&
+ !(fb->fmt_flags & V4L2_FMT_FLAG_COMPRESSED)) {
+ DPRINTF(1, "%s: %s: frame too small, skipped!\n",
+ DEVNAME(sc), __func__);
+ } else {
#ifdef UVIDEO_DUMP
/* do the file write in process context */
usb_rem_task(sc->sc_udev, &sc->sc_task_write);
@@ -2041,9 +2079,6 @@ uvideo_vs_decode_stream_header(struct uvideo_softc *sc, uint8_t *frame,
/* read */
uvideo_read(sc, fb->buf, fb->offset);
}
- } else {
- DPRINTF(1, "%s: %s: frame too large, skipped!\n",
- DEVNAME(sc), __func__);
}
fb->sample = 0;
diff --git a/sys/dev/usb/uvideo.h b/sys/dev/usb/uvideo.h
index dda40602a49..bd2727ab98c 100644
--- a/sys/dev/usb/uvideo.h
+++ b/sys/dev/usb/uvideo.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: uvideo.h,v 1.53 2011/03/26 08:13:05 jakemsr Exp $ */
+/* $OpenBSD: uvideo.h,v 1.54 2011/04/11 02:04:48 jakemsr Exp $ */
/*
* Copyright (c) 2007 Robert Nagy <robert@openbsd.org>
@@ -448,6 +448,7 @@ struct uvideo_frame_buffer {
int offset;
int buf_size;
uint8_t *buf;
+ uint32_t fmt_flags;
};
#define UVIDEO_MAX_BUFFERS 32