diff options
Diffstat (limited to 'drivers/media/video/uvc')
-rw-r--r-- | drivers/media/video/uvc/uvc_ctrl.c | 36 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvc_driver.c | 428 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvc_v4l2.c | 60 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvc_video.c | 26 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvcvideo.h | 23 |
5 files changed, 288 insertions, 285 deletions
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 1b89735..0469d7a 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -742,17 +742,7 @@ struct uvc_control *uvc_find_control(struct uvc_video_chain *chain, v4l2_id &= V4L2_CTRL_ID_MASK; /* Find the control. */ - __uvc_find_control(chain->processing, v4l2_id, mapping, &ctrl, next); - if (ctrl && !next) - return ctrl; - - list_for_each_entry(entity, &chain->iterms, chain) { - __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next); - if (ctrl && !next) - return ctrl; - } - - list_for_each_entry(entity, &chain->extensions, chain) { + list_for_each_entry(entity, &chain->entities, chain) { __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next); if (ctrl && !next) return ctrl; @@ -826,6 +816,13 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, ret = 0; goto out; + case V4L2_CTRL_TYPE_BUTTON: + v4l2_ctrl->minimum = 0; + v4l2_ctrl->maximum = 0; + v4l2_ctrl->step = 0; + ret = 0; + goto out; + default: break; } @@ -944,17 +941,7 @@ int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback) int ret = 0; /* Find the control. */ - ret = uvc_ctrl_commit_entity(chain->dev, chain->processing, rollback); - if (ret < 0) - goto done; - - list_for_each_entry(entity, &chain->iterms, chain) { - ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback); - if (ret < 0) - goto done; - } - - list_for_each_entry(entity, &chain->extensions, chain) { + list_for_each_entry(entity, &chain->entities, chain) { ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback); if (ret < 0) goto done; @@ -1068,8 +1055,9 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, int ret; /* Find the extension unit. */ - list_for_each_entry(entity, &chain->extensions, chain) { - if (entity->id == xctrl->unit) + list_for_each_entry(entity, &chain->entities, chain) { + if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT && + entity->id == xctrl->unit) break; } diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index 8756be5..c31bc50 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -46,6 +46,7 @@ unsigned int uvc_no_drop_param; static unsigned int uvc_quirks_param; unsigned int uvc_trace_param; +unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT; /* ------------------------------------------------------------------------ * Video formats @@ -248,29 +249,9 @@ static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev, entity = list_entry(&dev->entities, struct uvc_entity, list); list_for_each_entry_continue(entity, &dev->entities, list) { - switch (UVC_ENTITY_TYPE(entity)) { - case UVC_TT_STREAMING: - if (entity->output.bSourceID == id) - return entity; - break; - - case UVC_VC_PROCESSING_UNIT: - if (entity->processing.bSourceID == id) + for (i = 0; i < entity->bNrInPins; ++i) + if (entity->baSourceID[i] == id) return entity; - break; - - case UVC_VC_SELECTOR_UNIT: - for (i = 0; i < entity->selector.bNrInPins; ++i) - if (entity->selector.baSourceID[i] == id) - return entity; - break; - - case UVC_VC_EXTENSION_UNIT: - for (i = 0; i < entity->extension.bNrInPins; ++i) - if (entity->extension.baSourceID[i] == id) - return entity; - break; - } } return NULL; @@ -426,7 +407,8 @@ static int uvc_parse_format(struct uvc_device *dev, /* Parse the frame descriptors. Only uncompressed, MJPEG and frame * based formats have frame descriptors. */ - while (buflen > 2 && buffer[2] == ftype) { + while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && + buffer[2] == ftype) { frame = &format->frame[format->nframes]; if (ftype != UVC_VS_FRAME_FRAME_BASED) n = buflen > 25 ? buffer[25] : 0; @@ -503,12 +485,14 @@ static int uvc_parse_format(struct uvc_device *dev, buffer += buffer[0]; } - if (buflen > 2 && buffer[2] == UVC_VS_STILL_IMAGE_FRAME) { + if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && + buffer[2] == UVC_VS_STILL_IMAGE_FRAME) { buflen -= buffer[0]; buffer += buffer[0]; } - if (buflen > 2 && buffer[2] == UVC_VS_COLORFORMAT) { + if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && + buffer[2] == UVC_VS_COLORFORMAT) { if (buflen < 6) { uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " "interface %d COLORFORMAT error\n", @@ -749,6 +733,11 @@ static int uvc_parse_streaming(struct uvc_device *dev, buffer += buffer[0]; } + if (buflen) + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " + "%d has %u bytes of trailing descriptor garbage.\n", + dev->udev->devnum, alts->desc.bInterfaceNumber, buflen); + /* Parse the alternate settings to find the maximum bandwidth. */ for (i = 0; i < intf->num_altsetting; ++i) { struct usb_host_endpoint *ep; @@ -776,6 +765,28 @@ error: return ret; } +static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id, + unsigned int num_pads, unsigned int extra_size) +{ + struct uvc_entity *entity; + unsigned int num_inputs; + unsigned int size; + + num_inputs = (type & UVC_TERM_OUTPUT) ? num_pads : num_pads - 1; + size = sizeof(*entity) + extra_size + num_inputs; + entity = kzalloc(size, GFP_KERNEL); + if (entity == NULL) + return NULL; + + entity->id = id; + entity->type = type; + + entity->bNrInPins = num_inputs; + entity->baSourceID = ((__u8 *)entity) + sizeof(*entity) + extra_size; + + return entity; +} + /* Parse vendor-specific extensions. */ static int uvc_parse_vendor_control(struct uvc_device *dev, const unsigned char *buffer, int buflen) @@ -827,21 +838,18 @@ static int uvc_parse_vendor_control(struct uvc_device *dev, break; } - unit = kzalloc(sizeof *unit + p + 2*n, GFP_KERNEL); + unit = uvc_alloc_entity(UVC_VC_EXTENSION_UNIT, buffer[3], + p + 1, 2*n); if (unit == NULL) return -ENOMEM; - unit->id = buffer[3]; - unit->type = UVC_VC_EXTENSION_UNIT; memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); unit->extension.bNumControls = buffer[20]; - unit->extension.bNrInPins = get_unaligned_le16(&buffer[21]); - unit->extension.baSourceID = (__u8 *)unit + sizeof *unit; - memcpy(unit->extension.baSourceID, &buffer[22], p); + memcpy(unit->baSourceID, &buffer[22], p); unit->extension.bControlSize = buffer[22+p]; - unit->extension.bmControls = (__u8 *)unit + sizeof *unit + p; - unit->extension.bmControlsType = (__u8 *)unit + sizeof *unit - + p + n; + unit->extension.bmControls = (__u8 *)unit + sizeof(*unit); + unit->extension.bmControlsType = (__u8 *)unit + sizeof(*unit) + + n; memcpy(unit->extension.bmControls, &buffer[23+p], 2*n); if (buffer[24+p+2*n] != 0) @@ -938,13 +946,11 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return -EINVAL; } - term = kzalloc(sizeof *term + n + p, GFP_KERNEL); + term = uvc_alloc_entity(type | UVC_TERM_INPUT, buffer[3], + 1, n + p); if (term == NULL) return -ENOMEM; - term->id = buffer[3]; - term->type = type | UVC_TERM_INPUT; - if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) { term->camera.bControlSize = n; term->camera.bmControls = (__u8 *)term + sizeof *term; @@ -999,13 +1005,12 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return 0; } - term = kzalloc(sizeof *term, GFP_KERNEL); + term = uvc_alloc_entity(type | UVC_TERM_OUTPUT, buffer[3], + 1, 0); if (term == NULL) return -ENOMEM; - term->id = buffer[3]; - term->type = type | UVC_TERM_OUTPUT; - term->output.bSourceID = buffer[7]; + memcpy(term->baSourceID, &buffer[7], 1); if (buffer[8] != 0) usb_string(udev, buffer[8], term->name, @@ -1026,15 +1031,11 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return -EINVAL; } - unit = kzalloc(sizeof *unit + p, GFP_KERNEL); + unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, 0); if (unit == NULL) return -ENOMEM; - unit->id = buffer[3]; - unit->type = buffer[2]; - unit->selector.bNrInPins = buffer[4]; - unit->selector.baSourceID = (__u8 *)unit + sizeof *unit; - memcpy(unit->selector.baSourceID, &buffer[5], p); + memcpy(unit->baSourceID, &buffer[5], p); if (buffer[5+p] != 0) usb_string(udev, buffer[5+p], unit->name, @@ -1056,13 +1057,11 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return -EINVAL; } - unit = kzalloc(sizeof *unit + n, GFP_KERNEL); + unit = uvc_alloc_entity(buffer[2], buffer[3], 2, n); if (unit == NULL) return -ENOMEM; - unit->id = buffer[3]; - unit->type = buffer[2]; - unit->processing.bSourceID = buffer[4]; + memcpy(unit->baSourceID, &buffer[4], 1); unit->processing.wMaxMultiplier = get_unaligned_le16(&buffer[5]); unit->processing.bControlSize = buffer[7]; @@ -1091,19 +1090,15 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return -EINVAL; } - unit = kzalloc(sizeof *unit + p + n, GFP_KERNEL); + unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, n); if (unit == NULL) return -ENOMEM; - unit->id = buffer[3]; - unit->type = buffer[2]; memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); unit->extension.bNumControls = buffer[20]; - unit->extension.bNrInPins = get_unaligned_le16(&buffer[21]); - unit->extension.baSourceID = (__u8 *)unit + sizeof *unit; - memcpy(unit->extension.baSourceID, &buffer[22], p); + memcpy(unit->baSourceID, &buffer[22], p); unit->extension.bControlSize = buffer[22+p]; - unit->extension.bmControls = (__u8 *)unit + sizeof *unit + p; + unit->extension.bmControls = (__u8 *)unit + sizeof *unit; memcpy(unit->extension.bmControls, &buffer[23+p], n); if (buffer[23+p+n] != 0) @@ -1209,13 +1204,12 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- XU %d", entity->id); - if (entity->extension.bNrInPins != 1) { + if (entity->bNrInPins != 1) { uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more " "than 1 input pin.\n", entity->id); return -1; } - list_add_tail(&entity->chain, &chain->extensions); break; case UVC_VC_PROCESSING_UNIT: @@ -1236,7 +1230,7 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, printk(" <- SU %d", entity->id); /* Single-input selector units are ignored. */ - if (entity->selector.bNrInPins == 1) + if (entity->bNrInPins == 1) break; if (chain->selector != NULL) { @@ -1254,20 +1248,17 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- IT %d\n", entity->id); - list_add_tail(&entity->chain, &chain->iterms); break; case UVC_TT_STREAMING: - if (uvc_trace_param & UVC_TRACE_PROBE) - printk(" <- IT %d\n", entity->id); - - if (!UVC_ENTITY_IS_ITERM(entity)) { - uvc_trace(UVC_TRACE_DESCR, "Unsupported input " - "terminal %u.\n", entity->id); - return -1; + if (UVC_ENTITY_IS_ITERM(entity)) { + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" <- IT %d\n", entity->id); + } else { + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" OT %d", entity->id); } - list_add_tail(&entity->chain, &chain->iterms); break; default: @@ -1276,6 +1267,7 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, return -1; } + list_add_tail(&entity->chain, &chain->entities); return 0; } @@ -1299,14 +1291,14 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain, switch (UVC_ENTITY_TYPE(forward)) { case UVC_VC_EXTENSION_UNIT: - if (forward->extension.bNrInPins != 1) { + if (forward->bNrInPins != 1) { uvc_trace(UVC_TRACE_DESCR, "Extension unit %d " "has more than 1 input pin.\n", entity->id); return -EINVAL; } - list_add_tail(&forward->chain, &chain->extensions); + list_add_tail(&forward->chain, &chain->entities); if (uvc_trace_param & UVC_TRACE_PROBE) { if (!found) printk(" (->"); @@ -1326,7 +1318,7 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain, return -EINVAL; } - list_add_tail(&forward->chain, &chain->oterms); + list_add_tail(&forward->chain, &chain->entities); if (uvc_trace_param & UVC_TRACE_PROBE) { if (!found) printk(" (->"); @@ -1344,24 +1336,22 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain, } static int uvc_scan_chain_backward(struct uvc_video_chain *chain, - struct uvc_entity *entity) + struct uvc_entity **_entity) { + struct uvc_entity *entity = *_entity; struct uvc_entity *term; - int id = -1, i; + int id = -EINVAL, i; switch (UVC_ENTITY_TYPE(entity)) { case UVC_VC_EXTENSION_UNIT: - id = entity->extension.baSourceID[0]; - break; - case UVC_VC_PROCESSING_UNIT: - id = entity->processing.bSourceID; + id = entity->baSourceID[0]; break; case UVC_VC_SELECTOR_UNIT: /* Single-input selector units are ignored. */ - if (entity->selector.bNrInPins == 1) { - id = entity->selector.baSourceID[0]; + if (entity->bNrInPins == 1) { + id = entity->baSourceID[0]; break; } @@ -1369,8 +1359,8 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain, printk(" <- IT"); chain->selector = entity; - for (i = 0; i < entity->selector.bNrInPins; ++i) { - id = entity->selector.baSourceID[i]; + for (i = 0; i < entity->bNrInPins; ++i) { + id = entity->baSourceID[i]; term = uvc_entity_by_id(chain->dev, id); if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) { uvc_trace(UVC_TRACE_DESCR, "Selector unit %d " @@ -1382,7 +1372,7 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain, if (uvc_trace_param & UVC_TRACE_PROBE) printk(" %d", term->id); - list_add_tail(&term->chain, &chain->iterms); + list_add_tail(&term->chain, &chain->entities); uvc_scan_chain_forward(chain, term, entity); } @@ -1391,34 +1381,49 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain, id = 0; break; + + case UVC_ITT_VENDOR_SPECIFIC: + case UVC_ITT_CAMERA: + case UVC_ITT_MEDIA_TRANSPORT_INPUT: + case UVC_OTT_VENDOR_SPECIFIC: + case UVC_OTT_DISPLAY: + case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: + case UVC_TT_STREAMING: + id = UVC_ENTITY_IS_OTERM(entity) ? entity->baSourceID[0] : 0; + break; + } + + if (id <= 0) { + *_entity = NULL; + return id; } - return id; + entity = uvc_entity_by_id(chain->dev, id); + if (entity == NULL) { + uvc_trace(UVC_TRACE_DESCR, "Found reference to " + "unknown entity %d.\n", id); + return -EINVAL; + } + + *_entity = entity; + return 0; } static int uvc_scan_chain(struct uvc_video_chain *chain, - struct uvc_entity *oterm) + struct uvc_entity *term) { struct uvc_entity *entity, *prev; - int id; - entity = oterm; - list_add_tail(&entity->chain, &chain->oterms); - uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", entity->id); + uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain:"); - id = entity->output.bSourceID; - while (id != 0) { - prev = entity; - entity = uvc_entity_by_id(chain->dev, id); - if (entity == NULL) { - uvc_trace(UVC_TRACE_DESCR, "Found reference to " - "unknown entity %d.\n", id); - return -EINVAL; - } + entity = term; + prev = NULL; + while (entity != NULL) { + /* Entity must not be part of an existing chain */ if (entity->chain.next || entity->chain.prev) { uvc_trace(UVC_TRACE_DESCR, "Found reference to " - "entity %d already in chain.\n", id); + "entity %d already in chain.\n", entity->id); return -EINVAL; } @@ -1430,34 +1435,34 @@ static int uvc_scan_chain(struct uvc_video_chain *chain, if (uvc_scan_chain_forward(chain, entity, prev) < 0) return -EINVAL; - /* Stop when a terminal is found. */ - if (UVC_ENTITY_IS_TERM(entity)) - break; - /* Backward scan */ - id = uvc_scan_chain_backward(chain, entity); - if (id < 0) - return id; + prev = entity; + if (uvc_scan_chain_backward(chain, &entity) < 0) + return -EINVAL; } return 0; } -static unsigned int uvc_print_terms(struct list_head *terms, char *buffer) +static unsigned int uvc_print_terms(struct list_head *terms, u16 dir, + char *buffer) { struct uvc_entity *term; unsigned int nterms = 0; char *p = buffer; list_for_each_entry(term, terms, chain) { - p += sprintf(p, "%u", term->id); - if (term->chain.next != terms) { + if (!UVC_ENTITY_IS_TERM(term) || + UVC_TERM_DIRECTION(term) != dir) + continue; + + if (nterms) p += sprintf(p, ","); - if (++nterms >= 4) { - p += sprintf(p, "..."); - break; - } + if (++nterms >= 4) { + p += sprintf(p, "..."); + break; } + p += sprintf(p, "%u", term->id); } return p - buffer; @@ -1468,9 +1473,9 @@ static const char *uvc_print_chain(struct uvc_video_chain *chain) static char buffer[43]; char *p = buffer; - p += uvc_print_terms(&chain->iterms, p); + p += uvc_print_terms(&chain->entities, UVC_TERM_INPUT, p); p += sprintf(p, " -> "); - uvc_print_terms(&chain->oterms, p); + uvc_print_terms(&chain->entities, UVC_TERM_OUTPUT, p); return buffer; } @@ -1501,9 +1506,7 @@ static int uvc_scan_device(struct uvc_device *dev) if (chain == NULL) return -ENOMEM; - INIT_LIST_HEAD(&chain->iterms); - INIT_LIST_HEAD(&chain->oterms); - INIT_LIST_HEAD(&chain->extensions); + INIT_LIST_HEAD(&chain->entities); mutex_init(&chain->ctrl_mutex); chain->dev = dev; @@ -1531,22 +1534,92 @@ static int uvc_scan_device(struct uvc_device *dev) */ /* + * Delete the UVC device. + * + * Called by the kernel when the last reference to the uvc_device structure + * is released. + * + * As this function is called after or during disconnect(), all URBs have + * already been canceled by the USB core. There is no need to kill the + * interrupt URB manually. + */ +static void uvc_delete(struct uvc_device *dev) +{ + struct list_head *p, *n; + + usb_put_intf(dev->intf); + usb_put_dev(dev->udev); + + uvc_status_cleanup(dev); + uvc_ctrl_cleanup_device(dev); + + list_for_each_safe(p, n, &dev->chains) { + struct uvc_video_chain *chain; + chain = list_entry(p, struct uvc_video_chain, list); + kfree(chain); + } + + list_for_each_safe(p, n, &dev->entities) { + struct uvc_entity *entity; + entity = list_entry(p, struct uvc_entity, list); + kfree(entity); + } + + list_for_each_safe(p, n, &dev->streams) { + struct uvc_streaming *streaming; + streaming = list_entry(p, struct uvc_streaming, list); + usb_driver_release_interface(&uvc_driver.driver, + streaming->intf); + usb_put_intf(streaming->intf); + kfree(streaming->format); + kfree(streaming->header.bmaControls); + kfree(streaming); + } + + kfree(dev); +} + +static void uvc_release(struct video_device *vdev) +{ + struct uvc_streaming *stream = video_get_drvdata(vdev); + struct uvc_device *dev = stream->dev; + + video_device_release(vdev); + + /* Decrement the registered streams count and delete the device when it + * reaches zero. + */ + if (atomic_dec_and_test(&dev->nstreams)) + uvc_delete(dev); +} + +/* * Unregister the video devices. */ static void uvc_unregister_video(struct uvc_device *dev) { struct uvc_streaming *stream; + /* Unregistering all video devices might result in uvc_delete() being + * called from inside the loop if there's no open file handle. To avoid + * that, increment the stream count before iterating over the streams + * and decrement it when done. + */ + atomic_inc(&dev->nstreams); + list_for_each_entry(stream, &dev->streams, list) { if (stream->vdev == NULL) continue; - if (stream->vdev->minor == -1) - video_device_release(stream->vdev); - else - video_unregister_device(stream->vdev); + video_unregister_device(stream->vdev); stream->vdev = NULL; } + + /* Decrement the stream count and call uvc_delete explicitly if there + * are no stream left. + */ + if (atomic_dec_and_test(&dev->nstreams)) + uvc_delete(dev); } static int uvc_register_video(struct uvc_device *dev, @@ -1580,7 +1653,7 @@ static int uvc_register_video(struct uvc_device *dev, vdev->parent = &dev->intf->dev; vdev->minor = -1; vdev->fops = &uvc_fops; - vdev->release = video_device_release; + vdev->release = uvc_release; strlcpy(vdev->name, dev->name, sizeof vdev->name); /* Set the driver data before calling video_register_device, otherwise @@ -1598,6 +1671,7 @@ static int uvc_register_video(struct uvc_device *dev, return ret; } + atomic_inc(&dev->nstreams); return 0; } @@ -1605,13 +1679,13 @@ static int uvc_register_video(struct uvc_device *dev, * Register all video devices in all chains. */ static int uvc_register_terms(struct uvc_device *dev, - struct uvc_video_chain *chain, struct list_head *terms) + struct uvc_video_chain *chain) { struct uvc_streaming *stream; struct uvc_entity *term; int ret; - list_for_each_entry(term, terms, chain) { + list_for_each_entry(term, &chain->entities, chain) { if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING) continue; @@ -1637,11 +1711,7 @@ static int uvc_register_chains(struct uvc_device *dev) int ret; list_for_each_entry(chain, &dev->chains, list) { - ret = uvc_register_terms(dev, chain, &chain->iterms); - if (ret < 0) - return ret; - - ret = uvc_register_terms(dev, chain, &chain->oterms); + ret = uvc_register_terms(dev, chain); if (ret < 0) return ret; } @@ -1653,61 +1723,6 @@ static int uvc_register_chains(struct uvc_device *dev) * USB probe, disconnect, suspend and resume */ -/* - * Delete the UVC device. - * - * Called by the kernel when the last reference to the uvc_device structure - * is released. - * - * Unregistering the video devices is done here because every opened instance - * must be closed before the device can be unregistered. An alternative would - * have been to use another reference count for uvc_v4l2_open/uvc_release, and - * unregister the video devices on disconnect when that reference count drops - * to zero. - * - * As this function is called after or during disconnect(), all URBs have - * already been canceled by the USB core. There is no need to kill the - * interrupt URB manually. - */ -void uvc_delete(struct kref *kref) -{ - struct uvc_device *dev = container_of(kref, struct uvc_device, kref); - struct list_head *p, *n; - - /* Unregister the video devices. */ - uvc_unregister_video(dev); - usb_put_intf(dev->intf); - usb_put_dev(dev->udev); - - uvc_status_cleanup(dev); - uvc_ctrl_cleanup_device(dev); - - list_for_each_safe(p, n, &dev->chains) { - struct uvc_video_chain *chain; - chain = list_entry(p, struct uvc_video_chain, list); - kfree(chain); - } - - list_for_each_safe(p, n, &dev->entities) { - struct uvc_entity *entity; - entity = list_entry(p, struct uvc_entity, list); - kfree(entity); - } - - list_for_each_safe(p, n, &dev->streams) { - struct uvc_streaming *streaming; - streaming = list_entry(p, struct uvc_streaming, list); - usb_driver_release_interface(&uvc_driver.driver, - streaming->intf); - usb_put_intf(streaming->intf); - kfree(streaming->format); - kfree(streaming->header.bmaControls); - kfree(streaming); - } - - kfree(dev); -} - static int uvc_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1730,7 +1745,7 @@ static int uvc_probe(struct usb_interface *intf, INIT_LIST_HEAD(&dev->entities); INIT_LIST_HEAD(&dev->chains); INIT_LIST_HEAD(&dev->streams); - kref_init(&dev->kref); + atomic_set(&dev->nstreams, 0); atomic_set(&dev->users, 0); dev->udev = usb_get_dev(udev); @@ -1792,7 +1807,7 @@ static int uvc_probe(struct usb_interface *intf, return 0; error: - kref_put(&dev->kref, uvc_delete); + uvc_unregister_video(dev); return -ENODEV; } @@ -1809,21 +1824,9 @@ static void uvc_disconnect(struct usb_interface *intf) UVC_SC_VIDEOSTREAMING) return; - /* uvc_v4l2_open() might race uvc_disconnect(). A static driver-wide - * lock is needed to prevent uvc_disconnect from releasing its - * reference to the uvc_device instance after uvc_v4l2_open() received - * the pointer to the device (video_devdata) but before it got the - * chance to increase the reference count (kref_get). - * - * Note that the reference can't be released with the lock held, - * otherwise a AB-BA deadlock can occur with videodev_lock that - * videodev acquires in videodev_open() and video_unregister_device(). - */ - mutex_lock(&uvc_driver.open_mutex); dev->state |= UVC_DEV_DISCONNECTED; - mutex_unlock(&uvc_driver.open_mutex); - kref_put(&dev->kref, uvc_delete); + uvc_unregister_video(dev); } static int uvc_suspend(struct usb_interface *intf, pm_message_t message) @@ -1899,6 +1902,15 @@ static int uvc_reset_resume(struct usb_interface *intf) * though they are compliant. */ static struct usb_device_id uvc_ids[] = { + /* Genius eFace 2025 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0458, + .idProduct = 0x706e, + .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, @@ -2123,6 +2135,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STATUS_INTERVAL }, + /* MSI StarCam 370i */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x1b3b, + .idProduct = 0x2951, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* SiGma Micro USB Web Camera */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -2159,7 +2180,6 @@ static int __init uvc_init(void) INIT_LIST_HEAD(&uvc_driver.devices); INIT_LIST_HEAD(&uvc_driver.controls); - mutex_init(&uvc_driver.open_mutex); mutex_init(&uvc_driver.ctrl_mutex); uvc_ctrl_init(); @@ -2184,6 +2204,8 @@ 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); MODULE_PARM_DESC(trace, "Trace level bitmask"); +module_param_named(timeout, uvc_timeout_param, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(timeout, "Streaming control requests timeout"); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index a2bdd80..23239a4 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -364,37 +364,30 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream, * unprivileged state. Only a single instance can be in a privileged state at * a given time. Trying to perform an operation that requires privileges will * automatically acquire the required privileges if possible, or return -EBUSY - * otherwise. Privileges are dismissed when closing the instance. + * otherwise. Privileges are dismissed when closing the instance or when + * freeing the video buffers using VIDIOC_REQBUFS. * * Operations that require privileges are: * * - VIDIOC_S_INPUT * - VIDIOC_S_PARM * - VIDIOC_S_FMT - * - VIDIOC_TRY_FMT * - VIDIOC_REQBUFS */ static int uvc_acquire_privileges(struct uvc_fh *handle) { - int ret = 0; - /* Always succeed if the handle is already privileged. */ if (handle->state == UVC_HANDLE_ACTIVE) return 0; /* Check if the device already has a privileged handle. */ - mutex_lock(&uvc_driver.open_mutex); if (atomic_inc_return(&handle->stream->active) != 1) { atomic_dec(&handle->stream->active); - ret = -EBUSY; - goto done; + return -EBUSY; } handle->state = UVC_HANDLE_ACTIVE; - -done: - mutex_unlock(&uvc_driver.open_mutex); - return ret; + return 0; } static void uvc_dismiss_privileges(struct uvc_fh *handle) @@ -421,24 +414,20 @@ static int uvc_v4l2_open(struct file *file) int ret = 0; uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n"); - mutex_lock(&uvc_driver.open_mutex); stream = video_drvdata(file); - if (stream->dev->state & UVC_DEV_DISCONNECTED) { - ret = -ENODEV; - goto done; - } + if (stream->dev->state & UVC_DEV_DISCONNECTED) + return -ENODEV; ret = usb_autopm_get_interface(stream->dev->intf); if (ret < 0) - goto done; + return ret; /* Create the device handle. */ handle = kzalloc(sizeof *handle, GFP_KERNEL); if (handle == NULL) { usb_autopm_put_interface(stream->dev->intf); - ret = -ENOMEM; - goto done; + return -ENOMEM; } if (atomic_inc_return(&stream->dev->users) == 1) { @@ -447,7 +436,7 @@ static int uvc_v4l2_open(struct file *file) usb_autopm_put_interface(stream->dev->intf); atomic_dec(&stream->dev->users); kfree(handle); - goto done; + return ret; } } @@ -456,11 +445,7 @@ static int uvc_v4l2_open(struct file *file) handle->state = UVC_HANDLE_PASSIVE; file->private_data = handle; - kref_get(&stream->dev->kref); - -done: - mutex_unlock(&uvc_driver.open_mutex); - return ret; + return 0; } static int uvc_v4l2_release(struct file *file) @@ -490,7 +475,6 @@ static int uvc_v4l2_release(struct file *file) uvc_status_stop(stream->dev); usb_autopm_put_interface(stream->dev->intf); - kref_put(&stream->dev->kref, uvc_delete); return 0; } @@ -636,12 +620,16 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { if (index != 0) return -EINVAL; - iterm = list_first_entry(&chain->iterms, - struct uvc_entity, chain); + list_for_each_entry(iterm, &chain->entities, chain) { + if (UVC_ENTITY_IS_ITERM(iterm)) + break; + } pin = iterm->id; - } else if (pin < selector->selector.bNrInPins) { - pin = selector->selector.baSourceID[index]; - list_for_each_entry(iterm, chain->iterms.next, chain) { + } else if (pin < selector->bNrInPins) { + pin = selector->baSourceID[index]; + list_for_each_entry(iterm, &chain->entities, chain) { + if (!UVC_ENTITY_IS_ITERM(iterm)) + continue; if (iterm->id == pin) break; } @@ -692,7 +680,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) break; } - if (input == 0 || input > chain->selector->selector.bNrInPins) + if (input == 0 || input > chain->selector->bNrInPins) return -EINVAL; return uvc_query_ctrl(chain->dev, UVC_SET_CUR, @@ -731,9 +719,6 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct uvc_streaming_control probe; - if ((ret = uvc_acquire_privileges(handle)) < 0) - return ret; - return uvc_v4l2_try_format(stream, arg, &probe, NULL, NULL); } @@ -888,6 +873,9 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (ret < 0) return ret; + if (ret == 0) + uvc_dismiss_privileges(handle); + rb->count = ret; ret = 0; break; @@ -1051,7 +1039,7 @@ static ssize_t uvc_v4l2_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_read: not implemented.\n"); - return -ENODEV; + return -EINVAL; } /* diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index a6e41d1..05139a4 100644 --- a/drivers/media/video/uvc/uvc_video.c +++ b/drivers/media/video/uvc/uvc_video.c @@ -135,7 +135,7 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream, ret = __uvc_query_ctrl(stream->dev, query, 0, stream->intfnum, probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data, - size, UVC_CTRL_STREAMING_TIMEOUT); + size, uvc_timeout_param); if ((query == UVC_GET_MIN || query == UVC_GET_MAX) && ret == 2) { /* Some cameras, mostly based on Bison Electronics chipsets, @@ -239,7 +239,7 @@ static int uvc_set_video_ctrl(struct uvc_streaming *stream, ret = __uvc_query_ctrl(stream->dev, UVC_SET_CUR, 0, stream->intfnum, probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data, - size, UVC_CTRL_STREAMING_TIMEOUT); + size, uvc_timeout_param); if (ret != size) { uvc_printk(KERN_ERR, "Failed to set UVC %s control : " "%d (exp. %u).\n", probe ? "probe" : "commit", @@ -770,8 +770,9 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream, /* Retry allocations until one succeed. */ for (; npackets > 1; npackets /= 2) { for (i = 0; i < UVC_URBS; ++i) { + stream->urb_size = psize * npackets; stream->urb_buffer[i] = usb_buffer_alloc( - stream->dev->udev, psize * npackets, + stream->dev->udev, stream->urb_size, gfp_flags | __GFP_NOWARN, &stream->urb_dma[i]); if (!stream->urb_buffer[i]) { uvc_free_urb_buffers(stream); @@ -780,11 +781,15 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream, } if (i == UVC_URBS) { - stream->urb_size = psize * npackets; + uvc_trace(UVC_TRACE_VIDEO, "Allocated %u URB buffers " + "of %ux%u bytes each.\n", UVC_URBS, npackets, + psize); return npackets; } } + uvc_trace(UVC_TRACE_VIDEO, "Failed to allocate URB buffers (%u bytes " + "per packet).\n", psize); return 0; } @@ -935,10 +940,12 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags) bandwidth = stream->ctrl.dwMaxPayloadTransferSize; if (bandwidth == 0) { - uvc_printk(KERN_WARNING, "device %s requested null " - "bandwidth, defaulting to lowest.\n", - stream->dev->name); + uvc_trace(UVC_TRACE_VIDEO, "Device requested null " + "bandwidth, defaulting to lowest.\n"); bandwidth = 1; + } else { + uvc_trace(UVC_TRACE_VIDEO, "Device requested %u " + "B/frame bandwidth.\n", bandwidth); } for (i = 0; i < intf->num_altsetting; ++i) { @@ -955,8 +962,11 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags) break; } - if (i >= intf->num_altsetting) + if (i >= intf->num_altsetting) { + uvc_trace(UVC_TRACE_VIDEO, "No fast enough alt setting " + "for requested bandwidth.\n"); return -EIO; + } ret = usb_set_interface(stream->dev->udev, intfnum, i); if (ret < 0) diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index e7958aa..7ec9a04 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -75,6 +75,7 @@ struct uvc_xu_control { #define UVC_TERM_INPUT 0x0000 #define UVC_TERM_OUTPUT 0x8000 +#define UVC_TERM_DIRECTION(term) ((term)->type & 0x8000) #define UVC_ENTITY_TYPE(entity) ((entity)->type & 0x7fff) #define UVC_ENTITY_IS_UNIT(entity) (((entity)->type & 0xff00) == 0) @@ -148,7 +149,7 @@ struct uvc_xu_control { #define UVC_MAX_STATUS_SIZE 16 #define UVC_CTRL_CONTROL_TIMEOUT 300 -#define UVC_CTRL_STREAMING_TIMEOUT 1000 +#define UVC_CTRL_STREAMING_TIMEOUT 3000 /* Devices quirks */ #define UVC_QUIRK_STATUS_INTERVAL 0x00000001 @@ -292,11 +293,9 @@ struct uvc_entity { } media; struct { - __u8 bSourceID; } output; struct { - __u8 bSourceID; __u16 wMaxMultiplier; __u8 bControlSize; __u8 *bmControls; @@ -304,21 +303,20 @@ struct uvc_entity { } processing; struct { - __u8 bNrInPins; - __u8 *baSourceID; } selector; struct { __u8 guidExtensionCode[16]; __u8 bNumControls; - __u8 bNrInPins; - __u8 *baSourceID; __u8 bControlSize; __u8 *bmControls; __u8 *bmControlsType; } extension; }; + __u8 bNrInPins; + __u8 *baSourceID; + unsigned int ncontrols; struct uvc_control *controls; }; @@ -408,11 +406,9 @@ struct uvc_video_chain { struct uvc_device *dev; struct list_head list; - struct list_head iterms; /* Input terminals */ - struct list_head oterms; /* Output terminals */ + struct list_head entities; /* All entities */ struct uvc_entity *processing; /* Processing unit */ struct uvc_entity *selector; /* Selector unit */ - struct list_head extensions; /* Extension units */ struct mutex ctrl_mutex; }; @@ -475,7 +471,6 @@ struct uvc_device { char name[32]; enum uvc_device_state state; - struct kref kref; struct list_head list; atomic_t users; @@ -488,6 +483,7 @@ struct uvc_device { /* Video Streaming interfaces */ struct list_head streams; + atomic_t nstreams; /* Status Interrupt Endpoint */ struct usb_host_endpoint *int_ep; @@ -511,8 +507,6 @@ struct uvc_fh { struct uvc_driver { struct usb_driver driver; - struct mutex open_mutex; /* protects from open/disconnect race */ - struct list_head devices; /* struct uvc_device list */ struct list_head controls; /* struct uvc_control_info list */ struct mutex ctrl_mutex; /* protects controls and devices @@ -533,12 +527,14 @@ struct uvc_driver { #define UVC_TRACE_FRAME (1 << 7) #define UVC_TRACE_SUSPEND (1 << 8) #define UVC_TRACE_STATUS (1 << 9) +#define UVC_TRACE_VIDEO (1 << 10) #define UVC_WARN_MINMAX 0 #define UVC_WARN_PROBE_DEF 1 extern unsigned int uvc_no_drop_param; extern unsigned int uvc_trace_param; +extern unsigned int uvc_timeout_param; #define uvc_trace(flag, msg...) \ do { \ @@ -571,7 +567,6 @@ extern unsigned int uvc_trace_param; /* Core driver */ extern struct uvc_driver uvc_driver; -extern void uvc_delete(struct kref *kref); /* Video buffers queue management. */ extern void uvc_queue_init(struct uvc_video_queue *queue, |