diff options
Diffstat (limited to 'drivers/media/video/uvc')
-rw-r--r-- | drivers/media/video/uvc/uvc_ctrl.c | 105 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvc_driver.c | 24 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvc_queue.c | 13 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvc_v4l2.c | 103 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvc_video.c | 19 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvcvideo.h | 50 |
6 files changed, 225 insertions, 89 deletions
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 27a79f087b1..a350fad0db4 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -606,6 +606,26 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .set = uvc_ctrl_set_zoom, }, { + .id = V4L2_CID_PAN_ABSOLUTE, + .name = "Pan (Absolute)", + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL, + .size = 32, + .offset = 0, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { + .id = V4L2_CID_TILT_ABSOLUTE, + .name = "Tilt (Absolute)", + .entity = UVC_GUID_UVC_CAMERA, + .selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL, + .size = 32, + .offset = 32, + .v4l2_type = V4L2_CTRL_TYPE_INTEGER, + .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + }, + { .id = V4L2_CID_PRIVACY, .name = "Privacy", .entity = UVC_GUID_UVC_CAMERA, @@ -623,7 +643,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id) { - return ctrl->data + id * ctrl->info->size; + return ctrl->uvc_data + id * ctrl->info->size; } static inline int uvc_test_bit(const __u8 *data, int bit) @@ -678,6 +698,14 @@ static void uvc_set_le_value(struct uvc_control_mapping *mapping, int offset = mapping->offset; __u8 mask; + /* According to the v4l2 spec, writing any value to a button control + * should result in the action belonging to the button control being + * triggered. UVC devices however want to see a 1 written -> override + * value. + */ + if (mapping->v4l2_type == V4L2_CTRL_TYPE_BUTTON) + value = -1; + data += offset / 8; offset &= 7; @@ -1265,13 +1293,15 @@ int uvc_ctrl_resume_device(struct uvc_device *dev) * Control and mapping handling */ -static void uvc_ctrl_add_ctrl(struct uvc_device *dev, +static int uvc_ctrl_add_ctrl(struct uvc_device *dev, struct uvc_control_info *info) { struct uvc_entity *entity; struct uvc_control *ctrl = NULL; - int ret, found = 0; + int ret = 0, found = 0; unsigned int i; + u8 *uvc_info; + u8 *uvc_data; list_for_each_entry(entity, &dev->entities, list) { if (!uvc_entity_match_guid(entity, info->entity)) @@ -1290,56 +1320,69 @@ static void uvc_ctrl_add_ctrl(struct uvc_device *dev, } if (!found) - return; + return 0; + + uvc_data = kmalloc(info->size * UVC_CTRL_DATA_LAST + 1, GFP_KERNEL); + if (uvc_data == NULL) + return -ENOMEM; + + uvc_info = uvc_data + info->size * UVC_CTRL_DATA_LAST; if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) { /* Check if the device control information and length match * the user supplied information. */ - __u32 flags; - __le16 size; - __u8 inf; - ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id, - dev->intfnum, info->selector, (__u8 *)&size, 2); + dev->intfnum, info->selector, uvc_data, 2); if (ret < 0) { uvc_trace(UVC_TRACE_CONTROL, "GET_LEN failed on control %pUl/%u (%d).\n", info->entity, info->selector, ret); - return; + goto done; } - if (info->size != le16_to_cpu(size)) { + if (info->size != le16_to_cpu(*(__le16 *)uvc_data)) { uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u size " "doesn't match user supplied value.\n", info->entity, info->selector); - return; + ret = -EINVAL; + goto done; } ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, - dev->intfnum, info->selector, &inf, 1); + dev->intfnum, info->selector, uvc_info, 1); if (ret < 0) { uvc_trace(UVC_TRACE_CONTROL, "GET_INFO failed on control %pUl/%u (%d).\n", info->entity, info->selector, ret); - return; + goto done; } - flags = info->flags; - if (((flags & UVC_CONTROL_GET_CUR) && !(inf & (1 << 0))) || - ((flags & UVC_CONTROL_SET_CUR) && !(inf & (1 << 1)))) { + if (((info->flags & UVC_CONTROL_GET_CUR) && + !(*uvc_info & UVC_CONTROL_CAP_GET)) || + ((info->flags & UVC_CONTROL_SET_CUR) && + !(*uvc_info & UVC_CONTROL_CAP_SET))) { uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u flags " "don't match supported operations.\n", info->entity, info->selector); - return; + ret = -EINVAL; + goto done; } } ctrl->info = info; - ctrl->data = kmalloc(ctrl->info->size * UVC_CTRL_DATA_LAST, GFP_KERNEL); + ctrl->uvc_data = uvc_data; + ctrl->uvc_info = uvc_info; + uvc_trace(UVC_TRACE_CONTROL, "Added control %pUl/%u to device %s " "entity %u\n", ctrl->info->entity, ctrl->info->selector, dev->udev->devpath, entity->id); + +done: + if (ret < 0) + kfree(uvc_data); + + return ret; } /* @@ -1572,12 +1615,34 @@ void uvc_ctrl_cleanup_device(struct uvc_device *dev) list_for_each_entry(entity, &dev->entities, list) { for (i = 0; i < entity->ncontrols; ++i) - kfree(entity->controls[i].data); + kfree(entity->controls[i].uvc_data); kfree(entity->controls); } } +void uvc_ctrl_cleanup(void) +{ + struct uvc_control_info *info; + struct uvc_control_info *ni; + struct uvc_control_mapping *mapping; + struct uvc_control_mapping *nm; + + list_for_each_entry_safe(info, ni, &uvc_driver.controls, list) { + if (!(info->flags & UVC_CONTROL_EXTENSION)) + continue; + + list_for_each_entry_safe(mapping, nm, &info->mappings, list) { + list_del(&mapping->list); + kfree(mapping->menu_info); + kfree(mapping); + } + + list_del(&info->list); + kfree(info); + } +} + void uvc_ctrl_init(void) { struct uvc_control_info *ctrl = uvc_ctrls; diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index 838b56f097c..8bdd940f32e 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -637,14 +637,13 @@ static int uvc_parse_streaming(struct uvc_device *dev, } streaming->header.bControlSize = n; - streaming->header.bmaControls = kmalloc(p*n, GFP_KERNEL); + streaming->header.bmaControls = kmemdup(&buffer[size], p * n, + GFP_KERNEL); if (streaming->header.bmaControls == NULL) { ret = -ENOMEM; goto error; } - memcpy(streaming->header.bmaControls, &buffer[size], p*n); - buflen -= buffer[0]; buffer += buffer[0]; @@ -2146,6 +2145,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Miricle 307K */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x17dc, + .idProduct = 0x0202, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, /* Lenovo Thinkpad SL400/SL500 */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -2174,6 +2182,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_EXTRAFIELDS }, + /* Manta MM-353 Plako */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x18ec, + .idProduct = 0x3188, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* FSC WebCam V30S */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -2261,6 +2278,7 @@ static int __init uvc_init(void) static void __exit uvc_cleanup(void) { usb_deregister(&uvc_driver.driver); + uvc_ctrl_cleanup(); } module_init(uvc_init); diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c index 133c78d113a..e9928a41508 100644 --- a/drivers/media/video/uvc/uvc_queue.c +++ b/drivers/media/video/uvc/uvc_queue.c @@ -78,12 +78,14 @@ * */ -void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type) +void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type, + int drop_corrupted) { mutex_init(&queue->mutex); spin_lock_init(&queue->irqlock); INIT_LIST_HEAD(&queue->mainqueue); INIT_LIST_HEAD(&queue->irqqueue); + queue->flags = drop_corrupted ? UVC_QUEUE_DROP_CORRUPTED : 0; queue->type = type; } @@ -435,8 +437,10 @@ int uvc_queue_enable(struct uvc_video_queue *queue, int enable) uvc_queue_cancel(queue, 0); INIT_LIST_HEAD(&queue->mainqueue); - for (i = 0; i < queue->count; ++i) + for (i = 0; i < queue->count; ++i) { + queue->buffer[i].error = 0; queue->buffer[i].state = UVC_BUF_STATE_IDLE; + } queue->flags &= ~UVC_QUEUE_STREAMING; } @@ -488,8 +492,8 @@ struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, struct uvc_buffer *nextbuf; unsigned long flags; - if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) && - buf->buf.length != buf->buf.bytesused) { + if ((queue->flags & UVC_QUEUE_DROP_CORRUPTED) && buf->error) { + buf->error = 0; buf->state = UVC_BUF_STATE_QUEUED; buf->buf.bytesused = 0; return buf; @@ -497,6 +501,7 @@ struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, spin_lock_irqsave(&queue->irqlock, flags); list_del(&buf->queue); + buf->error = 0; buf->state = UVC_BUF_STATE_DONE; if (!list_empty(&queue->irqqueue)) nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer, diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 7c9ab293349..86db32697b8 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -29,6 +29,71 @@ #include "uvcvideo.h" /* ------------------------------------------------------------------------ + * UVC ioctls + */ +static int uvc_ioctl_ctrl_map(struct uvc_xu_control_mapping *xmap, int old) +{ + struct uvc_control_mapping *map; + unsigned int size; + int ret; + + map = kzalloc(sizeof *map, GFP_KERNEL); + if (map == NULL) + return -ENOMEM; + + map->id = xmap->id; + memcpy(map->name, xmap->name, sizeof map->name); + memcpy(map->entity, xmap->entity, sizeof map->entity); + map->selector = xmap->selector; + map->size = xmap->size; + map->offset = xmap->offset; + map->v4l2_type = xmap->v4l2_type; + map->data_type = xmap->data_type; + + switch (xmap->v4l2_type) { + case V4L2_CTRL_TYPE_INTEGER: + case V4L2_CTRL_TYPE_BOOLEAN: + case V4L2_CTRL_TYPE_BUTTON: + break; + + case V4L2_CTRL_TYPE_MENU: + if (old) { + ret = -EINVAL; + goto done; + } + + size = xmap->menu_count * sizeof(*map->menu_info); + map->menu_info = kmalloc(size, GFP_KERNEL); + if (map->menu_info == NULL) { + ret = -ENOMEM; + goto done; + } + + if (copy_from_user(map->menu_info, xmap->menu_info, size)) { + ret = -EFAULT; + goto done; + } + + map->menu_count = xmap->menu_count; + break; + + default: + ret = -EINVAL; + goto done; + } + + ret = uvc_ctrl_add_mapping(map); + +done: + if (ret < 0) { + kfree(map->menu_info); + kfree(map); + } + + return ret; +} + +/* ------------------------------------------------------------------------ * V4L2 interface */ @@ -451,7 +516,7 @@ static int uvc_v4l2_open(struct file *file) static int uvc_v4l2_release(struct file *file) { - struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + struct uvc_fh *handle = file->private_data; struct uvc_streaming *stream = handle->stream; uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n"); @@ -482,7 +547,7 @@ static int uvc_v4l2_release(struct file *file) static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = video_devdata(file); - struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + struct uvc_fh *handle = file->private_data; struct uvc_video_chain *chain = handle->chain; struct uvc_streaming *stream = handle->stream; long ret = 0; @@ -963,6 +1028,9 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (!capable(CAP_SYS_ADMIN)) return -EPERM; + if (xinfo->size == 0) + return -EINVAL; + info = kzalloc(sizeof *info, GFP_KERNEL); if (info == NULL) return -ENOMEM; @@ -974,7 +1042,8 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) info->flags = xinfo->flags; info->flags |= UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX | - UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF; + UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF | + UVC_CONTROL_EXTENSION; ret = uvc_ctrl_add_info(info); if (ret < 0) @@ -982,32 +1051,12 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) break; } + case UVCIOC_CTRL_MAP_OLD: case UVCIOC_CTRL_MAP: - { - struct uvc_xu_control_mapping *xmap = arg; - struct uvc_control_mapping *map; - if (!capable(CAP_SYS_ADMIN)) return -EPERM; - map = kzalloc(sizeof *map, GFP_KERNEL); - if (map == NULL) - return -ENOMEM; - - map->id = xmap->id; - memcpy(map->name, xmap->name, sizeof map->name); - memcpy(map->entity, xmap->entity, sizeof map->entity); - map->selector = xmap->selector; - map->size = xmap->size; - map->offset = xmap->offset; - map->v4l2_type = xmap->v4l2_type; - map->data_type = xmap->data_type; - - ret = uvc_ctrl_add_mapping(map); - if (ret < 0) - kfree(map); - break; - } + return uvc_ioctl_ctrl_map(arg, cmd == UVCIOC_CTRL_MAP_OLD); case UVCIOC_CTRL_GET: return uvc_xu_ctrl_query(chain, arg, 0); @@ -1067,7 +1116,7 @@ static const struct vm_operations_struct uvc_vm_ops = { static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma) { - struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + struct uvc_fh *handle = file->private_data; struct uvc_streaming *stream = handle->stream; struct uvc_video_queue *queue = &stream->queue; struct uvc_buffer *uninitialized_var(buffer); @@ -1122,7 +1171,7 @@ done: static unsigned int uvc_v4l2_poll(struct file *file, poll_table *wait) { - struct uvc_fh *handle = (struct uvc_fh *)file->private_data; + struct uvc_fh *handle = file->private_data; struct uvc_streaming *stream = handle->stream; uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n"); diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index 53f3ef4635e..e27cf0d3b6d 100644 --- a/drivers/media/video/uvc/uvc_video.c +++ b/drivers/media/video/uvc/uvc_video.c @@ -555,6 +555,9 @@ static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream, if (urb->iso_frame_desc[i].status < 0) { uvc_trace(UVC_TRACE_FRAME, "USB isochronous frame " "lost (%d).\n", urb->iso_frame_desc[i].status); + /* Mark the buffer as faulty. */ + if (buf != NULL) + buf->error = 1; continue; } @@ -579,8 +582,14 @@ static void uvc_video_decode_isoc(struct urb *urb, struct uvc_streaming *stream, uvc_video_decode_end(stream, buf, mem, urb->iso_frame_desc[i].actual_length); - if (buf->state == UVC_BUF_STATE_READY) + if (buf->state == UVC_BUF_STATE_READY) { + if (buf->buf.length != buf->buf.bytesused && + !(stream->cur_format->flags & + UVC_FMT_FLAG_COMPRESSED)) + buf->error = 1; + buf = uvc_queue_next_buffer(&stream->queue, buf); + } } } @@ -1104,7 +1113,7 @@ int uvc_video_init(struct uvc_streaming *stream) atomic_set(&stream->active, 0); /* Initialize the video buffers queue. */ - uvc_queue_init(&stream->queue, stream->type); + uvc_queue_init(&stream->queue, stream->type, !uvc_no_drop_param); /* Alternate setting 0 should be the default, yet the XBox Live Vision * Cam (and possibly other devices) crash or otherwise misbehave if @@ -1197,12 +1206,6 @@ int uvc_video_enable(struct uvc_streaming *stream, int enable) return 0; } - if ((stream->cur_format->flags & UVC_FMT_FLAG_COMPRESSED) || - uvc_no_drop_param) - stream->queue.flags &= ~UVC_QUEUE_DROP_INCOMPLETE; - else - stream->queue.flags |= UVC_QUEUE_DROP_INCOMPLETE; - ret = uvc_queue_enable(&stream->queue, 1); if (ret < 0) return ret; diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index d1f88406a5e..bdacf3beabf 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -27,6 +27,8 @@ #define UVC_CONTROL_RESTORE (1 << 6) /* Control can be updated by the camera. */ #define UVC_CONTROL_AUTO_UPDATE (1 << 7) +/* Control is an extension unit control. */ +#define UVC_CONTROL_EXTENSION (1 << 8) #define UVC_CONTROL_GET_RANGE (UVC_CONTROL_GET_CUR | UVC_CONTROL_GET_MIN | \ UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES | \ @@ -40,6 +42,15 @@ struct uvc_xu_control_info { __u32 flags; }; +struct uvc_menu_info { + __u32 value; + __u8 name[32]; +}; + +struct uvc_xu_control_mapping_old { + __u8 reserved[64]; +}; + struct uvc_xu_control_mapping { __u32 id; __u8 name[32]; @@ -50,6 +61,11 @@ struct uvc_xu_control_mapping { __u8 offset; enum v4l2_ctrl_type v4l2_type; __u32 data_type; + + struct uvc_menu_info __user *menu_info; + __u32 menu_count; + + __u32 reserved[4]; }; struct uvc_xu_control { @@ -60,6 +76,7 @@ struct uvc_xu_control { }; #define UVCIOC_CTRL_ADD _IOW('U', 1, struct uvc_xu_control_info) +#define UVCIOC_CTRL_MAP_OLD _IOWR('U', 2, struct uvc_xu_control_mapping_old) #define UVCIOC_CTRL_MAP _IOWR('U', 2, struct uvc_xu_control_mapping) #define UVCIOC_CTRL_GET _IOWR('U', 3, struct uvc_xu_control) #define UVCIOC_CTRL_SET _IOW('U', 4, struct uvc_xu_control) @@ -179,30 +196,6 @@ struct uvc_device; /* TODO: Put the most frequently accessed fields at the beginning of * structures to maximize cache efficiency. */ -struct uvc_streaming_control { - __u16 bmHint; - __u8 bFormatIndex; - __u8 bFrameIndex; - __u32 dwFrameInterval; - __u16 wKeyFrameRate; - __u16 wPFrameRate; - __u16 wCompQuality; - __u16 wCompWindowSize; - __u16 wDelay; - __u32 dwMaxVideoFrameSize; - __u32 dwMaxPayloadTransferSize; - __u32 dwClockFrequency; - __u8 bmFramingInfo; - __u8 bPreferedVersion; - __u8 bMinVersion; - __u8 bMaxVersion; -}; - -struct uvc_menu_info { - __u32 value; - __u8 name[32]; -}; - struct uvc_control_info { struct list_head list; struct list_head mappings; @@ -250,7 +243,8 @@ struct uvc_control { modified : 1, cached : 1; - __u8 *data; + __u8 *uvc_data; + __u8 *uvc_info; }; struct uvc_format_desc { @@ -385,11 +379,12 @@ struct uvc_buffer { struct list_head queue; wait_queue_head_t wait; enum uvc_buffer_state state; + unsigned int error; }; #define UVC_QUEUE_STREAMING (1 << 0) #define UVC_QUEUE_DISCONNECTED (1 << 1) -#define UVC_QUEUE_DROP_INCOMPLETE (1 << 2) +#define UVC_QUEUE_DROP_CORRUPTED (1 << 2) struct uvc_video_queue { enum v4l2_buf_type type; @@ -568,7 +563,7 @@ extern struct uvc_driver uvc_driver; /* Video buffers queue management. */ extern void uvc_queue_init(struct uvc_video_queue *queue, - enum v4l2_buf_type type); + enum v4l2_buf_type type, int drop_corrupted); extern int uvc_alloc_buffers(struct uvc_video_queue *queue, unsigned int nbuffers, unsigned int buflength); extern int uvc_free_buffers(struct uvc_video_queue *queue); @@ -625,6 +620,7 @@ extern int uvc_ctrl_init_device(struct uvc_device *dev); extern void uvc_ctrl_cleanup_device(struct uvc_device *dev); extern int uvc_ctrl_resume_device(struct uvc_device *dev); extern void uvc_ctrl_init(void); +extern void uvc_ctrl_cleanup(void); extern int uvc_ctrl_begin(struct uvc_video_chain *chain); extern int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback); |