diff options
Diffstat (limited to 'linux/drivers/media/video/uvc')
-rw-r--r-- | linux/drivers/media/video/uvc/uvc_ctrl.c | 145 | ||||
-rw-r--r-- | linux/drivers/media/video/uvc/uvc_driver.c | 216 | ||||
-rw-r--r-- | linux/drivers/media/video/uvc/uvc_status.c | 12 | ||||
-rw-r--r-- | linux/drivers/media/video/uvc/uvc_v4l2.c | 27 | ||||
-rw-r--r-- | linux/drivers/media/video/uvc/uvc_video.c | 97 | ||||
-rw-r--r-- | linux/drivers/media/video/uvc/uvcvideo.h | 23 |
6 files changed, 312 insertions, 208 deletions
diff --git a/linux/drivers/media/video/uvc/uvc_ctrl.c b/linux/drivers/media/video/uvc/uvc_ctrl.c index f16aafe9c..e9a75b064 100644 --- a/linux/drivers/media/video/uvc/uvc_ctrl.c +++ b/linux/drivers/media/video/uvc/uvc_ctrl.c @@ -15,7 +15,9 @@ #include <linux/version.h> #include <linux/list.h> #include <linux/module.h> +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 17) #include <linux/uaccess.h> +#endif #include <linux/usb.h> #include <linux/videodev2.h> #include <linux/vmalloc.h> @@ -327,6 +329,31 @@ static struct uvc_menu_info exposure_auto_controls[] = { { 8, "Aperture Priority Mode" }, }; +static __s32 uvc_ctrl_get_zoom(struct uvc_control_mapping *mapping, + __u8 query, const __u8 *data) +{ + __s8 zoom = (__s8)data[0]; + + switch (query) { + case GET_CUR: + return (zoom == 0) ? 0 : (zoom > 0 ? data[2] : -data[2]); + + case GET_MIN: + case GET_MAX: + case GET_RES: + case GET_DEF: + default: + return data[2]; + } +} + +static void uvc_ctrl_set_zoom(struct uvc_control_mapping *mapping, + __s32 value, __u8 *data) +{ + data[0] = value == 0 ? 0 : (value > 0) ? 1 : 0xff; + data[2] = min(abs(value), 0xff); +} + static struct uvc_control_mapping uvc_ctrl_mappings[] = { { .id = V4L2_CID_BRIGHTNESS, @@ -532,6 +559,38 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, }, + { + .id = V4L2_CID_ZOOM_ABSOLUTE, + .name = "Zoom, Absolute", + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_ZOOM_ABSOLUTE_CONTROL, + .size = 16, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_ZOOM_CONTINUOUS, + .name = "Zoom, Continuous", + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_ZOOM_RELATIVE_CONTROL, + .size = 0, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + .get = uvc_ctrl_get_zoom, + .set = uvc_ctrl_set_zoom, + }, + { + .id = V4L2_CID_PRIVACY, + .name = "Privacy", + .entity = UVC_GUID_UVC_CAMERA, + .selector = CT_PRIVACY_CONTROL, + .size = 1, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, + .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + }, }; /* ------------------------------------------------------------------------ @@ -543,18 +602,23 @@ static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id) return ctrl->data + id * ctrl->info->size; } -static inline int uvc_get_bit(const __u8 *data, int bit) +static inline int uvc_test_bit(const __u8 *data, int bit) { return (data[bit >> 3] >> (bit & 7)) & 1; } +static inline void uvc_clear_bit(__u8 *data, int bit) +{ + data[bit >> 3] &= ~(1 << (bit & 7)); +} + /* Extract the bit string specified by mapping->offset and mapping->size * from the little-endian data stored at 'data' and return the result as * a signed 32bit integer. Sign extension will be performed if the mapping * references a signed data type. */ -static __s32 uvc_get_le_value(const __u8 *data, - struct uvc_control_mapping *mapping) +static __s32 uvc_get_le_value(struct uvc_control_mapping *mapping, + __u8 query, const __u8 *data) { int bits = mapping->size; int offset = mapping->offset; @@ -583,8 +647,8 @@ static __s32 uvc_get_le_value(const __u8 *data, /* Set the bit string specified by mapping->offset and mapping->size * in the little-endian data stored at 'data' to the value 'value'. */ -static void uvc_set_le_value(__s32 value, __u8 *data, - struct uvc_control_mapping *mapping) +static void uvc_set_le_value(struct uvc_control_mapping *mapping, + __s32 value, __u8 *data) { int bits = mapping->size; int offset = mapping->offset; @@ -736,7 +800,7 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video, video->dev->intfnum, ctrl->info->selector, data, ctrl->info->size)) < 0) goto out; - v4l2_ctrl->default_value = uvc_get_le_value(data, mapping); + v4l2_ctrl->default_value = mapping->get(mapping, GET_DEF, data); } switch (mapping->v4l2_type) { @@ -772,21 +836,21 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video, video->dev->intfnum, ctrl->info->selector, data, ctrl->info->size)) < 0) goto out; - v4l2_ctrl->minimum = uvc_get_le_value(data, mapping); + v4l2_ctrl->minimum = mapping->get(mapping, GET_MIN, data); } if (ctrl->info->flags & UVC_CONTROL_GET_MAX) { if ((ret = uvc_query_ctrl(video->dev, GET_MAX, ctrl->entity->id, video->dev->intfnum, ctrl->info->selector, data, ctrl->info->size)) < 0) goto out; - v4l2_ctrl->maximum = uvc_get_le_value(data, mapping); + v4l2_ctrl->maximum = mapping->get(mapping, GET_MAX, data); } if (ctrl->info->flags & UVC_CONTROL_GET_RES) { if ((ret = uvc_query_ctrl(video->dev, GET_RES, ctrl->entity->id, video->dev->intfnum, ctrl->info->selector, data, ctrl->info->size)) < 0) goto out; - v4l2_ctrl->step = uvc_get_le_value(data, mapping); + v4l2_ctrl->step = mapping->get(mapping, GET_RES, data); } ret = 0; @@ -923,8 +987,8 @@ int uvc_ctrl_get(struct uvc_video_device *video, ctrl->loaded = 1; } - xctrl->value = uvc_get_le_value( - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping); + xctrl->value = mapping->get(mapping, GET_CUR, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { menu = mapping->menu_info; @@ -980,8 +1044,8 @@ int uvc_ctrl_set(struct uvc_video_device *video, ctrl->info->size); } - uvc_set_le_value(value, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping); + mapping->set(mapping, value, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); ctrl->dirty = 1; ctrl->modified = 1; @@ -1257,6 +1321,11 @@ int uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping) struct uvc_control_mapping *map; int ret = -EINVAL; + if (mapping->get == NULL) + mapping->get = uvc_get_le_value; + if (mapping->set == NULL) + mapping->set = uvc_set_le_value; + if (mapping->id & ~V4L2_CTRL_ID_MASK) { uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s' with " "invalid control id 0x%08x\n", mapping->name, @@ -1306,6 +1375,51 @@ end: } /* + * Prune an entity of its bogus controls. This currently includes processing + * unit auto controls for which no corresponding manual control is available. + * Such auto controls make little sense if any, and are known to crash at + * least the SiGma Micro webcam. + */ +static void +uvc_ctrl_prune_entity(struct uvc_entity *entity) +{ + static const struct { + u8 idx_manual; + u8 idx_auto; + } blacklist[] = { + { 2, 11 }, /* Hue */ + { 6, 12 }, /* White Balance Temperature */ + { 7, 13 }, /* White Balance Component */ + }; + + u8 *controls; + unsigned int size; + unsigned int i; + + if (UVC_ENTITY_TYPE(entity) != VC_PROCESSING_UNIT) + return; + + controls = entity->processing.bmControls; + size = entity->processing.bControlSize; + + for (i = 0; i < ARRAY_SIZE(blacklist); ++i) { + if (blacklist[i].idx_auto >= 8 * size || + blacklist[i].idx_manual >= 8 * size) + continue; + + if (!uvc_test_bit(controls, blacklist[i].idx_auto) || + uvc_test_bit(controls, blacklist[i].idx_manual)) + continue; + + uvc_trace(UVC_TRACE_CONTROL, "Auto control %u/%u has no " + "matching manual control, removing it.\n", entity->id, + blacklist[i].idx_auto); + + uvc_clear_bit(controls, blacklist[i].idx_auto); + } +} + +/* * Initialize device controls. */ int uvc_ctrl_init_device(struct uvc_device *dev) @@ -1331,6 +1445,9 @@ int uvc_ctrl_init_device(struct uvc_device *dev) bControlSize = entity->camera.bControlSize; } + if (dev->quirks & UVC_QUIRK_PRUNE_CONTROLS) + uvc_ctrl_prune_entity(entity); + for (i = 0; i < bControlSize; ++i) ncontrols += hweight8(bmControls[i]); @@ -1345,7 +1462,7 @@ int uvc_ctrl_init_device(struct uvc_device *dev) ctrl = entity->controls; for (i = 0; i < bControlSize * 8; ++i) { - if (uvc_get_bit(bmControls, i) == 0) + if (uvc_test_bit(bmControls, i) == 0) continue; ctrl->entity = entity; diff --git a/linux/drivers/media/video/uvc/uvc_driver.c b/linux/drivers/media/video/uvc/uvc_driver.c index cbe9d0452..343792f0f 100644 --- a/linux/drivers/media/video/uvc/uvc_driver.c +++ b/linux/drivers/media/video/uvc/uvc_driver.c @@ -32,6 +32,7 @@ #include <linux/vmalloc.h> #include <linux/wait.h> #include <asm/atomic.h> +#include <asm/unaligned.h> #include <media/v4l2-common.h> @@ -43,6 +44,7 @@ #define DRIVER_VERSION "v0.1.0" #endif +unsigned int uvc_no_drop_param; static unsigned int uvc_quirks_param; unsigned int uvc_trace_param; @@ -288,8 +290,10 @@ static int uvc_parse_format(struct uvc_device *dev, struct uvc_format_desc *fmtdesc; struct uvc_frame *frame; const unsigned char *start = buffer; + unsigned char *_buffer; unsigned int interval; unsigned int i, n; + int _buflen; __u8 ftype; format->type = buffer[2]; @@ -410,12 +414,20 @@ static int uvc_parse_format(struct uvc_device *dev, buflen -= buffer[0]; buffer += buffer[0]; + /* Count the number of frame descriptors to test the bFrameIndex + * field when parsing the descriptors. We can't rely on the + * bNumFrameDescriptors field as some cameras don't initialize it + * properly. + */ + for (_buflen = buflen, _buffer = buffer; + _buflen > 2 && _buffer[2] == ftype; + _buflen -= _buffer[0], _buffer += _buffer[0]) + format->nframes++; + /* Parse the frame descriptors. Only uncompressed, MJPEG and frame * based formats have frame descriptors. */ while (buflen > 2 && buffer[2] == ftype) { - frame = &format->frame[format->nframes]; - if (ftype != VS_FRAME_FRAME_BASED) n = buflen > 25 ? buffer[25] : 0; else @@ -430,22 +442,32 @@ static int uvc_parse_format(struct uvc_device *dev, return -EINVAL; } + if (buffer[3] - 1 >= format->nframes) { + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + "interface %d frame index %u out of range\n", + dev->udev->devnum, alts->desc.bInterfaceNumber, + buffer[3]); + return -EINVAL; + } + + frame = &format->frame[buffer[3] - 1]; + frame->bFrameIndex = buffer[3]; frame->bmCapabilities = buffer[4]; - frame->wWidth = le16_to_cpup((__le16 *)&buffer[5]); - frame->wHeight = le16_to_cpup((__le16 *)&buffer[7]); - frame->dwMinBitRate = le32_to_cpup((__le32 *)&buffer[9]); - frame->dwMaxBitRate = le32_to_cpup((__le32 *)&buffer[13]); + frame->wWidth = get_unaligned_le16(&buffer[5]); + frame->wHeight = get_unaligned_le16(&buffer[7]); + frame->dwMinBitRate = get_unaligned_le32(&buffer[9]); + frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]); if (ftype != VS_FRAME_FRAME_BASED) { frame->dwMaxVideoFrameBufferSize = - le32_to_cpup((__le32 *)&buffer[17]); + get_unaligned_le32(&buffer[17]); frame->dwDefaultFrameInterval = - le32_to_cpup((__le32 *)&buffer[21]); + get_unaligned_le32(&buffer[21]); frame->bFrameIntervalType = buffer[25]; } else { frame->dwMaxVideoFrameBufferSize = 0; frame->dwDefaultFrameInterval = - le32_to_cpup((__le32 *)&buffer[17]); + get_unaligned_le32(&buffer[17]); frame->bFrameIntervalType = buffer[21]; } frame->dwFrameInterval = *intervals; @@ -468,7 +490,7 @@ static int uvc_parse_format(struct uvc_device *dev, * some other divisions by zero which could happen. */ for (i = 0; i < n; ++i) { - interval = le32_to_cpup((__le32 *)&buffer[26+4*i]); + interval = get_unaligned_le32(&buffer[26+4*i]); *(*intervals)++ = interval ? interval : 1; } @@ -486,7 +508,6 @@ static int uvc_parse_format(struct uvc_device *dev, 10000000/frame->dwDefaultFrameInterval, (100000000/frame->dwDefaultFrameInterval)%10); - format->nframes++; buflen -= buffer[0]; buffer += buffer[0]; } @@ -813,8 +834,7 @@ static int uvc_parse_vendor_control(struct uvc_device *dev, unit->type = VC_EXTENSION_UNIT; memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); unit->extension.bNumControls = buffer[20]; - unit->extension.bNrInPins = - le16_to_cpup((__le16 *)&buffer[21]); + unit->extension.bNrInPins = get_unaligned_le16(&buffer[21]); unit->extension.baSourceID = (__u8 *)unit + sizeof *unit; memcpy(unit->extension.baSourceID, &buffer[22], p); unit->extension.bControlSize = buffer[22+p]; @@ -858,8 +878,8 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return -EINVAL; } - dev->uvc_version = le16_to_cpup((__le16 *)&buffer[3]); - dev->clock_frequency = le32_to_cpup((__le32 *)&buffer[7]); + dev->uvc_version = get_unaligned_le16(&buffer[3]); + dev->clock_frequency = get_unaligned_le32(&buffer[7]); /* Parse all USB Video Streaming interfaces. */ for (i = 0; i < n; ++i) { @@ -886,7 +906,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, /* Make sure the terminal type MSB is not null, otherwise it * could be confused with a unit. */ - type = le16_to_cpup((__le16 *)&buffer[4]); + type = get_unaligned_le16(&buffer[4]); if ((type & 0xff00) == 0) { uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " "interface %d INPUT_TERMINAL %d has invalid " @@ -928,11 +948,11 @@ static int uvc_parse_standard_control(struct uvc_device *dev, term->camera.bControlSize = n; term->camera.bmControls = (__u8 *)term + sizeof *term; term->camera.wObjectiveFocalLengthMin = - le16_to_cpup((__le16 *)&buffer[8]); + get_unaligned_le16(&buffer[8]); term->camera.wObjectiveFocalLengthMax = - le16_to_cpup((__le16 *)&buffer[10]); + get_unaligned_le16(&buffer[10]); term->camera.wOcularFocalLength = - le16_to_cpup((__le16 *)&buffer[12]); + get_unaligned_le16(&buffer[12]); memcpy(term->camera.bmControls, &buffer[15], n); } else if (UVC_ENTITY_TYPE(term) == ITT_MEDIA_TRANSPORT_INPUT) { term->media.bControlSize = n; @@ -968,7 +988,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, /* Make sure the terminal type MSB is not null, otherwise it * could be confused with a unit. */ - type = le16_to_cpup((__le16 *)&buffer[4]); + type = get_unaligned_le16(&buffer[4]); if ((type & 0xff00) == 0) { uvc_trace(UVC_TRACE_DESCR, "device %d videocontrol " "interface %d OUTPUT_TERMINAL %d has invalid " @@ -1042,7 +1062,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, unit->type = buffer[2]; unit->processing.bSourceID = buffer[4]; unit->processing.wMaxMultiplier = - le16_to_cpup((__le16 *)&buffer[5]); + get_unaligned_le16(&buffer[5]); unit->processing.bControlSize = buffer[7]; unit->processing.bmControls = (__u8 *)unit + sizeof *unit; memcpy(unit->processing.bmControls, &buffer[8], n); @@ -1077,8 +1097,7 @@ static int uvc_parse_standard_control(struct uvc_device *dev, unit->type = buffer[2]; memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); unit->extension.bNumControls = buffer[20]; - unit->extension.bNrInPins = - le16_to_cpup((__le16 *)&buffer[21]); + unit->extension.bNrInPins = get_unaligned_le16(&buffer[21]); unit->extension.baSourceID = (__u8 *)unit + sizeof *unit; memcpy(unit->extension.baSourceID, &buffer[22], p); unit->extension.bControlSize = buffer[22+p]; @@ -1709,24 +1728,6 @@ static int uvc_reset_resume(struct usb_interface *intf) * though they are compliant. */ static struct usb_device_id uvc_ids[] = { - /* ALi M5606 (Clevo M540SR) */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x0402, - .idProduct = 0x5606, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Creative Live! Optia */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x041e, - .idProduct = 0x4057, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* Microsoft Lifecam NX-6000 */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -1812,15 +1813,6 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, - /* Silicon Motion SM371 */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x090c, - .idProduct = 0xb371, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* MT6227 */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -1839,6 +1831,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Syntek (Samsung Q310) */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x174f, + .idProduct = 0x5931, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, /* Asus F9SG */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -1857,6 +1858,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Lenovo Thinkpad SL500 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x17ef, + .idProduct = 0x480b, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, /* Ecamm Pico iMage */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -1886,106 +1896,8 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_MINMAX - | UVC_QUIRK_IGNORE_SELECTOR_UNIT}, - /* Acer OEM Webcam - Unknown vendor */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0100, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Packard Bell OEM Webcam - Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0101, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Acer Crystal Eye webcam - Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0102, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Compaq Presario B1200 - Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0104, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Acer Travelmate 7720 - Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0105, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Medion Akoya Mini E1210 - Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0141, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Acer OrbiCam - Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0200, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Fujitsu Amilo SI2636 - Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0202, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Advent 4211 - Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0203, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0300, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, - /* Clevo M570TU - Bison Electronics */ - { .match_flags = USB_DEVICE_ID_MATCH_DEVICE - | USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x5986, - .idProduct = 0x0303, - .bInterfaceClass = USB_CLASS_VIDEO, - .bInterfaceSubClass = 1, - .bInterfaceProtocol = 0, - .driver_info = UVC_QUIRK_PROBE_MINMAX }, + | UVC_QUIRK_IGNORE_SELECTOR_UNIT + | UVC_QUIRK_PRUNE_CONTROLS }, /* Generic USB Video Class */ { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) }, {} @@ -2004,7 +1916,9 @@ struct uvc_driver uvc_driver = { .reset_resume = uvc_reset_resume, #endif .id_table = uvc_ids, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) .supports_autosuspend = 1, +#endif }, }; @@ -2033,6 +1947,8 @@ static void __exit uvc_cleanup(void) module_init(uvc_init); module_exit(uvc_cleanup); +module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames"); module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(quirks, "Forced device quirks"); module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR); diff --git a/linux/drivers/media/video/uvc/uvc_status.c b/linux/drivers/media/video/uvc/uvc_status.c index 5d60b264d..ab62d7bf9 100644 --- a/linux/drivers/media/video/uvc/uvc_status.c +++ b/linux/drivers/media/video/uvc/uvc_status.c @@ -15,7 +15,11 @@ #include <linux/version.h> #include <linux/input.h> #include <linux/usb.h> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) +#include <linux/usb_input.h> +#else #include <linux/usb/input.h> +#endif #include "uvcvideo.h" @@ -45,7 +49,11 @@ static int uvc_input_init(struct uvc_device *dev) input->name = dev->name; input->phys = phys; usb_to_input_id(udev, &input->id); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) input->dev.parent = &dev->intf->dev; +#else + input->cdev.dev = &dev->intf->dev; +#endif set_bit(EV_KEY, input->evbit); set_bit(BTN_0, input->keybit); @@ -118,7 +126,11 @@ static void uvc_event_control(struct uvc_device *dev, __u8 *data, int len) data[1], data[3], attrs[data[4]], len); } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) +static void uvc_status_complete(struct urb *urb, struct pt_regs *regs) +#else static void uvc_status_complete(struct urb *urb) +#endif { struct uvc_device *dev = urb->context; int len, ret; diff --git a/linux/drivers/media/video/uvc/uvc_v4l2.c b/linux/drivers/media/video/uvc/uvc_v4l2.c index 836136780..b6ee9aac6 100644 --- a/linux/drivers/media/video/uvc/uvc_v4l2.c +++ b/linux/drivers/media/video/uvc/uvc_v4l2.c @@ -252,9 +252,6 @@ static int uvc_v4l2_set_format(struct uvc_video_device *video, if (ret < 0) return ret; - if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0) - return ret; - memcpy(&video->streaming->ctrl, &probe, sizeof probe); video->streaming->cur_format = format; video->streaming->cur_frame = frame; @@ -315,10 +312,6 @@ static int uvc_v4l2_set_streamparm(struct uvc_video_device *video, if ((ret = uvc_probe_video(video, &probe)) < 0) return ret; - /* Commit the new settings. */ - if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0) - return ret; - memcpy(&video->streaming->ctrl, &probe, sizeof probe); /* Return the actual frame period. */ @@ -413,14 +406,18 @@ static int uvc_v4l2_open(struct inode *inode, struct file *file) goto done; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) ret = usb_autopm_get_interface(video->dev->intf); if (ret < 0) goto done; +#endif /* Create the device handle. */ handle = kzalloc(sizeof *handle, GFP_KERNEL); if (handle == NULL) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) usb_autopm_put_interface(video->dev->intf); +#endif ret = -ENOMEM; goto done; } @@ -459,7 +456,9 @@ static int uvc_v4l2_release(struct inode *inode, struct file *file) kfree(handle); file->private_data = NULL; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) usb_autopm_put_interface(video->dev->intf); +#endif kref_put(&video->dev->kref, uvc_delete); return 0; } @@ -471,9 +470,6 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) struct uvc_fh *handle = (struct uvc_fh *)file->private_data; int ret = 0; - if (uvc_trace_param & UVC_TRACE_IOCTL) - v4l_printk_ioctl(cmd); - switch (cmd) { /* Query capabilities */ case VIDIOC_QUERYCAP: @@ -924,7 +920,7 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (!capable(CAP_SYS_ADMIN)) return -EPERM; - info = kmalloc(sizeof *info, GFP_KERNEL); + info = kzalloc(sizeof *info, GFP_KERNEL); if (info == NULL) return -ENOMEM; @@ -951,7 +947,7 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (!capable(CAP_SYS_ADMIN)) return -EPERM; - map = kmalloc(sizeof *map, GFP_KERNEL); + map = kzalloc(sizeof *map, GFP_KERNEL); if (map == NULL) return -ENOMEM; @@ -990,7 +986,12 @@ static int uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) static int uvc_v4l2_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_ioctl\n"); + if (uvc_trace_param & UVC_TRACE_IOCTL) { + uvc_printk(KERN_DEBUG, "uvc_v4l2_ioctl("); + v4l_printk_ioctl(cmd); + printk(")\n"); + } + return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl); } diff --git a/linux/drivers/media/video/uvc/uvc_video.c b/linux/drivers/media/video/uvc/uvc_video.c index b7bb23820..b19e91dd5 100644 --- a/linux/drivers/media/video/uvc/uvc_video.c +++ b/linux/drivers/media/video/uvc/uvc_video.c @@ -36,15 +36,22 @@ static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, { __u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE; unsigned int pipe; - int ret; pipe = (query & 0x80) ? usb_rcvctrlpipe(dev->udev, 0) : usb_sndctrlpipe(dev->udev, 0); type |= (query & 0x80) ? USB_DIR_IN : USB_DIR_OUT; - ret = usb_control_msg(dev->udev, pipe, query, type, cs << 8, + return usb_control_msg(dev->udev, pipe, query, type, cs << 8, unit << 8 | intfnum, data, size, timeout); +} + +int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, + __u8 intfnum, __u8 cs, void *data, __u16 size) +{ + int ret; + 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, @@ -55,13 +62,6 @@ static int __uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, return 0; } -int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, - __u8 intfnum, __u8 cs, void *data, __u16 size) -{ - return __uvc_query_ctrl(dev, query, unit, intfnum, cs, data, size, - UVC_CTRL_CONTROL_TIMEOUT); -} - static void uvc_fixup_buffer_size(struct uvc_video_device *video, struct uvc_streaming_control *ctrl) { @@ -102,8 +102,36 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video, ret = __uvc_query_ctrl(video->dev, query, 0, video->streaming->intfnum, probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size, UVC_CTRL_STREAMING_TIMEOUT); - if (ret < 0) + + if ((query == GET_MIN || query == GET_MAX) && ret == 2) { + /* Some cameras, mostly based on Bison Electronics chipsets, + * answer a GET_MIN or GET_MAX request with the wCompQuality + * field only. + */ + uvc_warn_once(video->dev, UVC_WARN_MINMAX, "UVC non " + "compliance - GET_MIN/MAX(PROBE) incorrectly " + "supported. Enabling workaround.\n"); + memset(ctrl, 0, sizeof ctrl); + ctrl->wCompQuality = le16_to_cpup((__le16 *)data); + ret = 0; + goto out; + } else if (query == GET_DEF && probe == 1) { + /* Many cameras don't support the GET_DEF request on their + * video probe control. Warn once and return, the caller will + * fall back to GET_CUR. + */ + uvc_warn_once(video->dev, UVC_WARN_PROBE_DEF, "UVC non " + "compliance - GET_DEF(PROBE) not supported. " + "Enabling workaround.\n"); + ret = -EIO; + goto out; + } else if (ret != size) { + uvc_printk(KERN_ERR, "Failed to query (%u) UVC %s control : " + "%d (exp. %u).\n", query, probe ? "probe" : "commit", + ret, size); + ret = -EIO; goto out; + } ctrl->bmHint = le16_to_cpup((__le16 *)&data[0]); ctrl->bFormatIndex = data[2]; @@ -114,14 +142,11 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video, ctrl->wCompQuality = le16_to_cpup((__le16 *)&data[12]); ctrl->wCompWindowSize = le16_to_cpup((__le16 *)&data[14]); ctrl->wDelay = le16_to_cpup((__le16 *)&data[16]); - ctrl->dwMaxVideoFrameSize = - le32_to_cpu(get_unaligned((__le32 *)&data[18])); - ctrl->dwMaxPayloadTransferSize = - le32_to_cpu(get_unaligned((__le32 *)&data[22])); + ctrl->dwMaxVideoFrameSize = get_unaligned_le32(&data[18]); + ctrl->dwMaxPayloadTransferSize = get_unaligned_le32(&data[22]); if (size == 34) { - ctrl->dwClockFrequency = - le32_to_cpu(get_unaligned((__le32 *)&data[26])); + ctrl->dwClockFrequency = get_unaligned_le32(&data[26]); ctrl->bmFramingInfo = data[30]; ctrl->bPreferedVersion = data[31]; ctrl->bMinVersion = data[32]; @@ -138,13 +163,14 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video, * Try to get the value from the format and frame descriptor. */ uvc_fixup_buffer_size(video, ctrl); + ret = 0; out: kfree(data); return ret; } -int uvc_set_video_ctrl(struct uvc_video_device *video, +static int uvc_set_video_ctrl(struct uvc_video_device *video, struct uvc_streaming_control *ctrl, int probe) { __u8 *data; @@ -168,14 +194,11 @@ int uvc_set_video_ctrl(struct uvc_video_device *video, /* Note: Some of the fields below are not required for IN devices (see * UVC spec, 4.3.1.1), but we still copy them in case support for OUT * devices is added in the future. */ - put_unaligned(cpu_to_le32(ctrl->dwMaxVideoFrameSize), - (__le32 *)&data[18]); - put_unaligned(cpu_to_le32(ctrl->dwMaxPayloadTransferSize), - (__le32 *)&data[22]); + put_unaligned_le32(ctrl->dwMaxVideoFrameSize, &data[18]); + put_unaligned_le32(ctrl->dwMaxPayloadTransferSize, &data[22]); if (size == 34) { - put_unaligned(cpu_to_le32(ctrl->dwClockFrequency), - (__le32 *)&data[26]); + put_unaligned_le32(ctrl->dwClockFrequency, &data[26]); data[30] = ctrl->bmFramingInfo; data[31] = ctrl->bPreferedVersion; data[32] = ctrl->bMinVersion; @@ -186,6 +209,12 @@ int uvc_set_video_ctrl(struct uvc_video_device *video, video->streaming->intfnum, probe ? VS_PROBE_CONTROL : VS_COMMIT_CONTROL, data, size, UVC_CTRL_STREAMING_TIMEOUT); + if (ret != size) { + uvc_printk(KERN_ERR, "Failed to set UVC %s control : " + "%d (exp. %u).\n", probe ? "probe" : "commit", + ret, size); + ret = -EIO; + } kfree(data); return ret; @@ -252,6 +281,12 @@ done: return ret; } +int uvc_commit_video(struct uvc_video_device *video, + struct uvc_streaming_control *probe) +{ + return uvc_set_video_ctrl(video, probe, 0); +} + /* ------------------------------------------------------------------------ * Video codecs */ @@ -525,7 +560,11 @@ static void uvc_video_decode_bulk(struct urb *urb, } } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) +static void uvc_video_complete(struct urb *urb, struct pt_regs *regs) +#else static void uvc_video_complete(struct urb *urb) +#endif { struct uvc_video_device *video = urb->context; struct uvc_video_queue *queue = &video->queue; @@ -854,7 +893,7 @@ int uvc_video_resume(struct uvc_video_device *video) video->frozen = 0; - if ((ret = uvc_set_video_ctrl(video, &video->streaming->ctrl, 0)) < 0) { + if ((ret = uvc_commit_video(video, &video->streaming->ctrl)) < 0) { uvc_queue_enable(&video->queue, 0); return ret; } @@ -935,11 +974,8 @@ int uvc_video_init(struct uvc_video_device *video) break; } - /* Commit the default settings. */ probe->bFormatIndex = format->index; probe->bFrameIndex = frame->bFrameIndex; - if ((ret = uvc_set_video_ctrl(video, probe, 0)) < 0) - return ret; video->streaming->cur_format = format; video->streaming->cur_frame = frame; @@ -971,7 +1007,8 @@ int uvc_video_enable(struct uvc_video_device *video, int enable) return 0; } - if (video->streaming->cur_format->flags & UVC_FMT_FLAG_COMPRESSED) + if ((video->streaming->cur_format->flags & UVC_FMT_FLAG_COMPRESSED) || + uvc_no_drop_param) video->queue.flags &= ~UVC_QUEUE_DROP_INCOMPLETE; else video->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE; @@ -979,6 +1016,10 @@ int uvc_video_enable(struct uvc_video_device *video, int enable) if ((ret = uvc_queue_enable(&video->queue, 1)) < 0) return ret; + /* Commit the streaming parameters. */ + if ((ret = uvc_commit_video(video, &video->streaming->ctrl)) < 0) + return ret; + return uvc_init_video(video, GFP_KERNEL); } diff --git a/linux/drivers/media/video/uvc/uvcvideo.h b/linux/drivers/media/video/uvc/uvcvideo.h index 9a6bc1aaf..549d09fd3 100644 --- a/linux/drivers/media/video/uvc/uvcvideo.h +++ b/linux/drivers/media/video/uvc/uvcvideo.h @@ -4,7 +4,6 @@ #include <linux/kernel.h> #include <linux/videodev2.h> - /* * Dynamic controls */ @@ -68,6 +67,7 @@ struct uvc_xu_control { #ifdef __KERNEL__ #include <linux/poll.h> +#include "compat.h" /* -------------------------------------------------------------------------- * UVC constants @@ -316,6 +316,7 @@ struct uvc_xu_control { #define UVC_QUIRK_BUILTIN_ISIGHT 0x00000008 #define UVC_QUIRK_STREAM_NO_FID 0x00000010 #define UVC_QUIRK_IGNORE_SELECTOR_UNIT 0x00000020 +#define UVC_QUIRK_PRUNE_CONTROLS 0x00000040 /* Format flags */ #define UVC_FMT_FLAG_COMPRESSED 0x00000001 @@ -383,6 +384,11 @@ struct uvc_control_mapping { struct uvc_menu_info *menu_info; __u32 menu_count; + + __s32 (*get) (struct uvc_control_mapping *mapping, __u8 query, + const __u8 *data); + void (*set) (struct uvc_control_mapping *mapping, __s32 value, + __u8 *data); }; struct uvc_control { @@ -617,6 +623,7 @@ enum uvc_device_state { struct uvc_device { struct usb_device *udev; struct usb_interface *intf; + unsigned long warnings; __u32 quirks; int intfnum; char name[32]; @@ -679,6 +686,10 @@ struct uvc_driver { #define UVC_TRACE_SUSPEND (1 << 8) #define UVC_TRACE_STATUS (1 << 9) +#define UVC_WARN_MINMAX 0 +#define UVC_WARN_PROBE_DEF 1 + +extern unsigned int uvc_no_drop_param; extern unsigned int uvc_trace_param; #define uvc_trace(flag, msg...) \ @@ -687,6 +698,12 @@ extern unsigned int uvc_trace_param; printk(KERN_DEBUG "uvcvideo: " msg); \ } while (0) +#define uvc_warn_once(dev, warn, msg...) \ + do { \ + if (!test_and_set_bit(warn, &dev->warnings)) \ + printk(KERN_INFO "uvcvideo: " msg); \ + } while (0) + #define uvc_printk(level, msg...) \ printk(level "uvcvideo: " msg) @@ -740,10 +757,10 @@ extern int uvc_video_resume(struct uvc_video_device *video); extern int uvc_video_enable(struct uvc_video_device *video, int enable); extern int uvc_probe_video(struct uvc_video_device *video, struct uvc_streaming_control *probe); +extern int uvc_commit_video(struct uvc_video_device *video, + struct uvc_streaming_control *ctrl); extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, __u8 intfnum, __u8 cs, void *data, __u16 size); -extern int uvc_set_video_ctrl(struct uvc_video_device *video, - struct uvc_streaming_control *ctrl, int probe); /* Status */ extern int uvc_status_init(struct uvc_device *dev); |