diff options
Diffstat (limited to 'drivers/media/usb/uvc/uvc_driver.c')
-rw-r--r-- | drivers/media/usb/uvc/uvc_driver.c | 368 |
1 files changed, 208 insertions, 160 deletions
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index b3c8411dc05c..deadbcea5e22 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -32,7 +32,7 @@ unsigned int uvc_clock_param = CLOCK_MONOTONIC; unsigned int uvc_hw_timestamps_param; -unsigned int uvc_no_drop_param; +unsigned int uvc_no_drop_param = 1; static unsigned int uvc_quirks_param = -1; unsigned int uvc_dbg_param; unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT; @@ -220,20 +220,127 @@ static struct uvc_streaming *uvc_stream_new(struct uvc_device *dev, * Descriptors parsing */ +static int uvc_parse_frame(struct uvc_device *dev, + struct uvc_streaming *streaming, + struct uvc_format *format, struct uvc_frame *frame, + u32 **intervals, u8 ftype, int width_multiplier, + const unsigned char *buffer, int buflen) +{ + struct usb_host_interface *alts = streaming->intf->cur_altsetting; + unsigned int maxIntervalIndex; + unsigned int interval; + unsigned int i, n; + + if (ftype != UVC_VS_FRAME_FRAME_BASED) + n = buflen > 25 ? buffer[25] : 0; + else + n = buflen > 21 ? buffer[21] : 0; + + n = n ? n : 3; + + if (buflen < 26 + 4 * n) { + uvc_dbg(dev, DESCR, + "device %d videostreaming interface %d FRAME error\n", + dev->udev->devnum, alts->desc.bInterfaceNumber); + return -EINVAL; + } + + frame->bFrameIndex = buffer[3]; + frame->bmCapabilities = buffer[4]; + frame->wWidth = get_unaligned_le16(&buffer[5]) * width_multiplier; + frame->wHeight = get_unaligned_le16(&buffer[7]); + frame->dwMinBitRate = get_unaligned_le32(&buffer[9]); + frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]); + if (ftype != UVC_VS_FRAME_FRAME_BASED) { + frame->dwMaxVideoFrameBufferSize = + get_unaligned_le32(&buffer[17]); + frame->dwDefaultFrameInterval = + get_unaligned_le32(&buffer[21]); + frame->bFrameIntervalType = buffer[25]; + } else { + frame->dwMaxVideoFrameBufferSize = 0; + frame->dwDefaultFrameInterval = + get_unaligned_le32(&buffer[17]); + frame->bFrameIntervalType = buffer[21]; + } + + /* + * Copy the frame intervals. + * + * Some bogus devices report dwMinFrameInterval equal to + * dwMaxFrameInterval and have dwFrameIntervalStep set to zero. Setting + * all null intervals to 1 fixes the problem and some other divisions + * by zero that could happen. + */ + frame->dwFrameInterval = *intervals; + + for (i = 0; i < n; ++i) { + interval = get_unaligned_le32(&buffer[26 + 4 * i]); + (*intervals)[i] = interval ? interval : 1; + } + + /* + * Apply more fixes, quirks and workarounds to handle incorrect or + * broken descriptors. + */ + + /* + * Several UVC chipsets screw up dwMaxVideoFrameBufferSize completely. + * Observed behaviours range from setting the value to 1.1x the actual + * frame size to hardwiring the 16 low bits to 0. This results in a + * higher than necessary memory usage as well as a wrong image size + * information. For uncompressed formats this can be fixed by computing + * the value from the frame size. + */ + if (!(format->flags & UVC_FMT_FLAG_COMPRESSED)) + frame->dwMaxVideoFrameBufferSize = format->bpp * frame->wWidth + * frame->wHeight / 8; + + /* + * Clamp the default frame interval to the boundaries. A zero + * bFrameIntervalType value indicates a continuous frame interval + * range, with dwFrameInterval[0] storing the minimum value and + * dwFrameInterval[1] storing the maximum value. + */ + maxIntervalIndex = frame->bFrameIntervalType ? n - 1 : 1; + frame->dwDefaultFrameInterval = + clamp(frame->dwDefaultFrameInterval, + frame->dwFrameInterval[0], + frame->dwFrameInterval[maxIntervalIndex]); + + /* + * Some devices report frame intervals that are not functional. If the + * corresponding quirk is set, restrict operation to the first interval + * only. + */ + if (dev->quirks & UVC_QUIRK_RESTRICT_FRAME_RATE) { + frame->bFrameIntervalType = 1; + (*intervals)[0] = frame->dwDefaultFrameInterval; + } + + uvc_dbg(dev, DESCR, "- %ux%u (%u.%u fps)\n", + frame->wWidth, frame->wHeight, + 10000000 / frame->dwDefaultFrameInterval, + (100000000 / frame->dwDefaultFrameInterval) % 10); + + *intervals += n; + + return buffer[0]; +} + static int uvc_parse_format(struct uvc_device *dev, struct uvc_streaming *streaming, struct uvc_format *format, struct uvc_frame *frames, u32 **intervals, const unsigned char *buffer, int buflen) { - struct usb_interface *intf = streaming->intf; - struct usb_host_interface *alts = intf->cur_altsetting; + struct usb_host_interface *alts = streaming->intf->cur_altsetting; const struct uvc_format_desc *fmtdesc; struct uvc_frame *frame; const unsigned char *start = buffer; unsigned int width_multiplier = 1; - unsigned int interval; unsigned int i, n; u8 ftype; + int ret; format->type = buffer[2]; format->index = buffer[3]; @@ -371,111 +478,19 @@ static int uvc_parse_format(struct uvc_device *dev, * Parse the frame descriptors. Only uncompressed, MJPEG and frame * based formats have frame descriptors. */ - while (ftype && buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && - buffer[2] == ftype) { - unsigned int maxIntervalIndex; - - frame = &frames[format->nframes]; - if (ftype != UVC_VS_FRAME_FRAME_BASED) - n = buflen > 25 ? buffer[25] : 0; - else - n = buflen > 21 ? buffer[21] : 0; - - n = n ? n : 3; - - if (buflen < 26 + 4*n) { - uvc_dbg(dev, DESCR, - "device %d videostreaming interface %d FRAME error\n", - dev->udev->devnum, - alts->desc.bInterfaceNumber); - return -EINVAL; - } - - frame->bFrameIndex = buffer[3]; - frame->bmCapabilities = buffer[4]; - frame->wWidth = get_unaligned_le16(&buffer[5]) - * width_multiplier; - frame->wHeight = get_unaligned_le16(&buffer[7]); - frame->dwMinBitRate = get_unaligned_le32(&buffer[9]); - frame->dwMaxBitRate = get_unaligned_le32(&buffer[13]); - if (ftype != UVC_VS_FRAME_FRAME_BASED) { - frame->dwMaxVideoFrameBufferSize = - get_unaligned_le32(&buffer[17]); - frame->dwDefaultFrameInterval = - get_unaligned_le32(&buffer[21]); - frame->bFrameIntervalType = buffer[25]; - } else { - frame->dwMaxVideoFrameBufferSize = 0; - frame->dwDefaultFrameInterval = - get_unaligned_le32(&buffer[17]); - frame->bFrameIntervalType = buffer[21]; - } - - /* - * Copy the frame intervals. - * - * Some bogus devices report dwMinFrameInterval equal to - * dwMaxFrameInterval and have dwFrameIntervalStep set to - * zero. Setting all null intervals to 1 fixes the problem and - * some other divisions by zero that could happen. - */ - frame->dwFrameInterval = *intervals; - - for (i = 0; i < n; ++i) { - interval = get_unaligned_le32(&buffer[26+4*i]); - (*intervals)[i] = interval ? interval : 1; - } - - /* - * Apply more fixes, quirks and workarounds to handle incorrect - * or broken descriptors. - */ - - /* - * Several UVC chipsets screw up dwMaxVideoFrameBufferSize - * completely. Observed behaviours range from setting the - * value to 1.1x the actual frame size to hardwiring the - * 16 low bits to 0. This results in a higher than necessary - * memory usage as well as a wrong image size information. For - * uncompressed formats this can be fixed by computing the - * value from the frame size. - */ - if (!(format->flags & UVC_FMT_FLAG_COMPRESSED)) - frame->dwMaxVideoFrameBufferSize = format->bpp - * frame->wWidth * frame->wHeight / 8; - - /* - * Clamp the default frame interval to the boundaries. A zero - * bFrameIntervalType value indicates a continuous frame - * interval range, with dwFrameInterval[0] storing the minimum - * value and dwFrameInterval[1] storing the maximum value. - */ - maxIntervalIndex = frame->bFrameIntervalType ? n - 1 : 1; - frame->dwDefaultFrameInterval = - clamp(frame->dwDefaultFrameInterval, - frame->dwFrameInterval[0], - frame->dwFrameInterval[maxIntervalIndex]); - - /* - * Some devices report frame intervals that are not functional. - * If the corresponding quirk is set, restrict operation to the - * first interval only. - */ - if (dev->quirks & UVC_QUIRK_RESTRICT_FRAME_RATE) { - frame->bFrameIntervalType = 1; - (*intervals)[0] = frame->dwDefaultFrameInterval; + if (ftype) { + while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && + buffer[2] == ftype) { + frame = &frames[format->nframes]; + ret = uvc_parse_frame(dev, streaming, format, frame, + intervals, ftype, width_multiplier, + buffer, buflen); + if (ret < 0) + return ret; + format->nframes++; + buflen -= ret; + buffer += ret; } - - uvc_dbg(dev, DESCR, "- %ux%u (%u.%u fps)\n", - frame->wWidth, frame->wHeight, - 10000000 / frame->dwDefaultFrameInterval, - (100000000 / frame->dwDefaultFrameInterval) % 10); - - format->nframes++; - *intervals += n; - - buflen -= buffer[0]; - buffer += buffer[0]; } if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && @@ -775,27 +790,14 @@ static const u8 uvc_media_transport_input_guid[16] = UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT; static const u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING; -static struct uvc_entity *uvc_alloc_new_entity(struct uvc_device *dev, u16 type, - u16 id, unsigned int num_pads, - unsigned int extra_size) +static struct uvc_entity *uvc_alloc_entity(u16 type, u16 id, + unsigned int num_pads, unsigned int extra_size) { struct uvc_entity *entity; unsigned int num_inputs; unsigned int size; unsigned int i; - /* Per UVC 1.1+ spec 3.7.2, the ID should be non-zero. */ - if (id == 0) { - dev_err(&dev->udev->dev, "Found Unit with invalid ID 0.\n"); - return ERR_PTR(-EINVAL); - } - - /* Per UVC 1.1+ spec 3.7.2, the ID is unique. */ - if (uvc_entity_by_id(dev, id)) { - dev_err(&dev->udev->dev, "Found multiple Units with ID %u\n", id); - return ERR_PTR(-EINVAL); - } - extra_size = roundup(extra_size, sizeof(*entity->pads)); if (num_pads) num_inputs = type & UVC_TERM_OUTPUT ? num_pads : num_pads - 1; @@ -805,7 +807,7 @@ static struct uvc_entity *uvc_alloc_new_entity(struct uvc_device *dev, u16 type, + num_inputs; entity = kzalloc(size, GFP_KERNEL); if (entity == NULL) - return ERR_PTR(-ENOMEM); + return NULL; entity->id = id; entity->type = type; @@ -917,10 +919,10 @@ static int uvc_parse_vendor_control(struct uvc_device *dev, break; } - unit = uvc_alloc_new_entity(dev, UVC_VC_EXTENSION_UNIT, - buffer[3], p + 1, 2 * n); - if (IS_ERR(unit)) - return PTR_ERR(unit); + unit = uvc_alloc_entity(UVC_VC_EXTENSION_UNIT, buffer[3], + p + 1, 2*n); + if (unit == NULL) + return -ENOMEM; memcpy(unit->guid, &buffer[4], 16); unit->extension.bNumControls = buffer[20]; @@ -1029,10 +1031,10 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return -EINVAL; } - term = uvc_alloc_new_entity(dev, type | UVC_TERM_INPUT, - buffer[3], 1, n + p); - if (IS_ERR(term)) - return PTR_ERR(term); + term = uvc_alloc_entity(type | UVC_TERM_INPUT, buffer[3], + 1, n + p); + if (term == NULL) + return -ENOMEM; if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) { term->camera.bControlSize = n; @@ -1088,10 +1090,10 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return 0; } - term = uvc_alloc_new_entity(dev, type | UVC_TERM_OUTPUT, - buffer[3], 1, 0); - if (IS_ERR(term)) - return PTR_ERR(term); + term = uvc_alloc_entity(type | UVC_TERM_OUTPUT, buffer[3], + 1, 0); + if (term == NULL) + return -ENOMEM; memcpy(term->baSourceID, &buffer[7], 1); @@ -1110,10 +1112,9 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return -EINVAL; } - unit = uvc_alloc_new_entity(dev, buffer[2], buffer[3], - p + 1, 0); - if (IS_ERR(unit)) - return PTR_ERR(unit); + unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, 0); + if (unit == NULL) + return -ENOMEM; memcpy(unit->baSourceID, &buffer[5], p); @@ -1133,9 +1134,9 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return -EINVAL; } - unit = uvc_alloc_new_entity(dev, buffer[2], buffer[3], 2, n); - if (IS_ERR(unit)) - return PTR_ERR(unit); + unit = uvc_alloc_entity(buffer[2], buffer[3], 2, n); + if (unit == NULL) + return -ENOMEM; memcpy(unit->baSourceID, &buffer[4], 1); unit->processing.wMaxMultiplier = @@ -1162,10 +1163,9 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return -EINVAL; } - unit = uvc_alloc_new_entity(dev, buffer[2], buffer[3], - p + 1, n); - if (IS_ERR(unit)) - return PTR_ERR(unit); + unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, n); + if (unit == NULL) + return -ENOMEM; memcpy(unit->guid, &buffer[4], 16); unit->extension.bNumControls = buffer[20]; @@ -1295,20 +1295,19 @@ static int uvc_gpio_parse(struct uvc_device *dev) struct gpio_desc *gpio_privacy; int irq; - gpio_privacy = devm_gpiod_get_optional(&dev->udev->dev, "privacy", + gpio_privacy = devm_gpiod_get_optional(&dev->intf->dev, "privacy", GPIOD_IN); if (IS_ERR_OR_NULL(gpio_privacy)) return PTR_ERR_OR_ZERO(gpio_privacy); irq = gpiod_to_irq(gpio_privacy); if (irq < 0) - return dev_err_probe(&dev->udev->dev, irq, + return dev_err_probe(&dev->intf->dev, irq, "No IRQ for privacy GPIO\n"); - unit = uvc_alloc_new_entity(dev, UVC_EXT_GPIO_UNIT, - UVC_EXT_GPIO_UNIT_ID, 0, 1); - if (IS_ERR(unit)) - return PTR_ERR(unit); + unit = uvc_alloc_entity(UVC_EXT_GPIO_UNIT, UVC_EXT_GPIO_UNIT_ID, 0, 1); + if (!unit) + return -ENOMEM; unit->gpio.gpio_privacy = gpio_privacy; unit->gpio.irq = irq; @@ -1329,15 +1328,27 @@ static int uvc_gpio_parse(struct uvc_device *dev) static int uvc_gpio_init_irq(struct uvc_device *dev) { struct uvc_entity *unit = dev->gpio_unit; + int ret; if (!unit || unit->gpio.irq < 0) return 0; - return devm_request_threaded_irq(&dev->udev->dev, unit->gpio.irq, NULL, - uvc_gpio_irq, - IRQF_ONESHOT | IRQF_TRIGGER_FALLING | - IRQF_TRIGGER_RISING, - "uvc_privacy_gpio", dev); + ret = request_threaded_irq(unit->gpio.irq, NULL, uvc_gpio_irq, + IRQF_ONESHOT | IRQF_TRIGGER_FALLING | + IRQF_TRIGGER_RISING, + "uvc_privacy_gpio", dev); + + unit->gpio.initialized = !ret; + + return ret; +} + +static void uvc_gpio_deinit(struct uvc_device *dev) +{ + if (!dev->gpio_unit || !dev->gpio_unit->gpio.initialized) + return; + + free_irq(dev->gpio_unit->gpio.irq, dev); } /* ------------------------------------------------------------------------ @@ -1934,6 +1945,8 @@ static void uvc_unregister_video(struct uvc_device *dev) { struct uvc_streaming *stream; + uvc_gpio_deinit(dev); + list_for_each_entry(stream, &dev->streams, list) { /* Nothing to do here, continue. */ if (!video_is_registered(&stream->vdev)) @@ -1995,7 +2008,7 @@ int uvc_register_video_device(struct uvc_device *dev, int ret; /* Initialize the video buffers queue. */ - ret = uvc_queue_init(queue, type, !uvc_no_drop_param); + ret = uvc_queue_init(queue, type); if (ret) return ret; @@ -2424,8 +2437,25 @@ module_param_call(clock, uvc_clock_param_set, uvc_clock_param_get, MODULE_PARM_DESC(clock, "Video buffers timestamp clock"); module_param_named(hwtimestamps, uvc_hw_timestamps_param, uint, 0644); MODULE_PARM_DESC(hwtimestamps, "Use hardware timestamps"); -module_param_named(nodrop, uvc_no_drop_param, uint, 0644); + +static int param_set_nodrop(const char *val, const struct kernel_param *kp) +{ + pr_warn_once("uvcvideo: " + DEPRECATED + "nodrop parameter will be eventually removed.\n"); + return param_set_bool(val, kp); +} + +static const struct kernel_param_ops param_ops_nodrop = { + .set = param_set_nodrop, + .get = param_get_uint, +}; + +param_check_uint(nodrop, &uvc_no_drop_param); +module_param_cb(nodrop, ¶m_ops_nodrop, &uvc_no_drop_param, 0644); +__MODULE_PARM_TYPE(nodrop, "uint"); MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames"); + module_param_named(quirks, uvc_quirks_param, uint, 0644); MODULE_PARM_DESC(quirks, "Forced device quirks"); module_param_named(trace, uvc_dbg_param, uint, 0644); @@ -2802,6 +2832,15 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, + /* Sonix Technology Co. Ltd. - 292A IPC AR0330 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0c45, + .idProduct = 0x6366, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_MJPEG_NO_EOF) }, /* MT6227 */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -2830,6 +2869,15 @@ static const struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = (kernel_ulong_t)&uvc_quirk_probe_minmax }, + /* Kurokesu C1 PRO */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x16d0, + .idProduct = 0x0ed1, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_INFO_QUIRK(UVC_QUIRK_MJPEG_NO_EOF) }, /* Syntek (HP Spartan) */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, |