diff options
Diffstat (limited to 'drivers/media/usb/uvc/uvc_ctrl.c')
-rw-r--r-- | drivers/media/usb/uvc/uvc_ctrl.c | 215 |
1 files changed, 163 insertions, 52 deletions
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index a36b4fb949fa..c2ad102bd693 100644 --- a/drivers/media/usb/uvc/uvc_ctrl.c +++ b/drivers/media/usb/uvc/uvc_ctrl.c @@ -20,6 +20,7 @@ #include <linux/videodev2.h> #include <linux/vmalloc.h> #include <linux/wait.h> +#include <linux/workqueue.h> #include <linux/atomic.h> #include <media/v4l2-ctrls.h> @@ -971,12 +972,30 @@ static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain, return 0; } +static s32 __uvc_ctrl_get_value(struct uvc_control_mapping *mapping, + const u8 *data) +{ + s32 value = mapping->get(mapping, UVC_GET_CUR, data); + + if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { + struct uvc_menu_info *menu = mapping->menu_info; + unsigned int i; + + for (i = 0; i < mapping->menu_count; ++i, ++menu) { + if (menu->value == value) { + value = i; + break; + } + } + } + + return value; +} + static int __uvc_ctrl_get(struct uvc_video_chain *chain, struct uvc_control *ctrl, struct uvc_control_mapping *mapping, s32 *value) { - struct uvc_menu_info *menu; - unsigned int i; int ret; if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0) @@ -993,18 +1012,8 @@ static int __uvc_ctrl_get(struct uvc_video_chain *chain, ctrl->loaded = 1; } - *value = mapping->get(mapping, UVC_GET_CUR, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); - - if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { - menu = mapping->menu_info; - for (i = 0; i < mapping->menu_count; ++i, ++menu) { - if (menu->value == *value) { - *value = i; - break; - } - } - } + *value = __uvc_ctrl_get_value(mapping, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); return 0; } @@ -1125,7 +1134,7 @@ done: } /* - * Mapping V4L2 controls to UVC controls can be straighforward if done well. + * Mapping V4L2 controls to UVC controls can be straightforward if done well. * Most of the UVC controls exist in V4L2, and can be mapped directly. Some * must be grouped (for instance the Red Balance, Blue Balance and Do White * Balance V4L2 controls use the White Balance Component UVC control) or @@ -1216,53 +1225,135 @@ static void uvc_ctrl_fill_event(struct uvc_video_chain *chain, ev->u.ctrl.default_value = v4l2_ctrl.default_value; } -static void uvc_ctrl_send_event(struct uvc_fh *handle, - struct uvc_control *ctrl, struct uvc_control_mapping *mapping, - s32 value, u32 changes) +/* + * Send control change events to all subscribers for the @ctrl control. By + * default the subscriber that generated the event, as identified by @handle, + * is not notified unless it has set the V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK flag. + * @handle can be NULL for asynchronous events related to auto-update controls, + * in which case all subscribers are notified. + */ +static void uvc_ctrl_send_event(struct uvc_video_chain *chain, + struct uvc_fh *handle, struct uvc_control *ctrl, + struct uvc_control_mapping *mapping, s32 value, u32 changes) { + struct v4l2_fh *originator = handle ? &handle->vfh : NULL; struct v4l2_subscribed_event *sev; struct v4l2_event ev; if (list_empty(&mapping->ev_subs)) return; - uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, value, changes); + uvc_ctrl_fill_event(chain, &ev, ctrl, mapping, value, changes); list_for_each_entry(sev, &mapping->ev_subs, node) { - if (sev->fh && (sev->fh != &handle->vfh || + if (sev->fh != originator || (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK) || - (changes & V4L2_EVENT_CTRL_CH_FLAGS))) + (changes & V4L2_EVENT_CTRL_CH_FLAGS)) v4l2_event_queue_fh(sev->fh, &ev); } } -static void uvc_ctrl_send_slave_event(struct uvc_fh *handle, - struct uvc_control *master, u32 slave_id, - const struct v4l2_ext_control *xctrls, unsigned int xctrls_count) +/* + * Send control change events for the slave of the @master control identified + * by the V4L2 ID @slave_id. The @handle identifies the event subscriber that + * generated the event and may be NULL for auto-update events. + */ +static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain, + struct uvc_fh *handle, struct uvc_control *master, u32 slave_id) { struct uvc_control_mapping *mapping = NULL; struct uvc_control *ctrl = NULL; u32 changes = V4L2_EVENT_CTRL_CH_FLAGS; - unsigned int i; s32 val = 0; - /* - * We can skip sending an event for the slave if the slave - * is being modified in the same transaction. - */ - for (i = 0; i < xctrls_count; i++) { - if (xctrls[i].id == slave_id) - return; - } - __uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0); if (ctrl == NULL) return; - if (__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0) + if (__uvc_ctrl_get(chain, ctrl, mapping, &val) == 0) changes |= V4L2_EVENT_CTRL_CH_VALUE; - uvc_ctrl_send_event(handle, ctrl, mapping, val, changes); + uvc_ctrl_send_event(chain, handle, ctrl, mapping, val, changes); +} + +static void uvc_ctrl_status_event_work(struct work_struct *work) +{ + struct uvc_device *dev = container_of(work, struct uvc_device, + async_ctrl.work); + struct uvc_ctrl_work *w = &dev->async_ctrl; + struct uvc_video_chain *chain = w->chain; + struct uvc_control_mapping *mapping; + struct uvc_control *ctrl = w->ctrl; + struct uvc_fh *handle; + unsigned int i; + int ret; + + mutex_lock(&chain->ctrl_mutex); + + handle = ctrl->handle; + ctrl->handle = NULL; + + list_for_each_entry(mapping, &ctrl->info.mappings, list) { + s32 value = __uvc_ctrl_get_value(mapping, w->data); + + /* + * handle may be NULL here if the device sends auto-update + * events without a prior related control set from userspace. + */ + for (i = 0; i < ARRAY_SIZE(mapping->slave_ids); ++i) { + if (!mapping->slave_ids[i]) + break; + + uvc_ctrl_send_slave_event(chain, handle, ctrl, + mapping->slave_ids[i]); + } + + uvc_ctrl_send_event(chain, handle, ctrl, mapping, value, + V4L2_EVENT_CTRL_CH_VALUE); + } + + mutex_unlock(&chain->ctrl_mutex); + + /* Resubmit the URB. */ + w->urb->interval = dev->int_ep->desc.bInterval; + ret = usb_submit_urb(w->urb, GFP_KERNEL); + if (ret < 0) + uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n", + ret); +} + +bool uvc_ctrl_status_event(struct urb *urb, struct uvc_video_chain *chain, + struct uvc_control *ctrl, const u8 *data) +{ + struct uvc_device *dev = chain->dev; + struct uvc_ctrl_work *w = &dev->async_ctrl; + + if (list_empty(&ctrl->info.mappings)) { + ctrl->handle = NULL; + return false; + } + + w->data = data; + w->urb = urb; + w->chain = chain; + w->ctrl = ctrl; + + schedule_work(&w->work); + + return true; +} + +static bool uvc_ctrl_xctrls_has_control(const struct v4l2_ext_control *xctrls, + unsigned int xctrls_count, u32 id) +{ + unsigned int i; + + for (i = 0; i < xctrls_count; ++i) { + if (xctrls[i].id == id) + return true; + } + + return false; } static void uvc_ctrl_send_events(struct uvc_fh *handle, @@ -1277,29 +1368,39 @@ static void uvc_ctrl_send_events(struct uvc_fh *handle, for (i = 0; i < xctrls_count; ++i) { ctrl = uvc_find_control(handle->chain, xctrls[i].id, &mapping); + if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS) + /* Notification will be sent from an Interrupt event. */ + continue; + for (j = 0; j < ARRAY_SIZE(mapping->slave_ids); ++j) { - if (!mapping->slave_ids[j]) + u32 slave_id = mapping->slave_ids[j]; + + if (!slave_id) break; - uvc_ctrl_send_slave_event(handle, ctrl, - mapping->slave_ids[j], - xctrls, xctrls_count); + + /* + * We can skip sending an event for the slave if the + * slave is being modified in the same transaction. + */ + if (uvc_ctrl_xctrls_has_control(xctrls, xctrls_count, + slave_id)) + continue; + + uvc_ctrl_send_slave_event(handle->chain, handle, ctrl, + slave_id); } /* * If the master is being modified in the same transaction * flags may change too. */ - if (mapping->master_id) { - for (j = 0; j < xctrls_count; j++) { - if (xctrls[j].id == mapping->master_id) { - changes |= V4L2_EVENT_CTRL_CH_FLAGS; - break; - } - } - } + if (mapping->master_id && + uvc_ctrl_xctrls_has_control(xctrls, xctrls_count, + mapping->master_id)) + changes |= V4L2_EVENT_CTRL_CH_FLAGS; - uvc_ctrl_send_event(handle, ctrl, mapping, xctrls[i].value, - changes); + uvc_ctrl_send_event(handle->chain, handle, ctrl, mapping, + xctrls[i].value, changes); } } @@ -1472,9 +1573,10 @@ int uvc_ctrl_get(struct uvc_video_chain *chain, return __uvc_ctrl_get(chain, ctrl, mapping, &xctrl->value); } -int uvc_ctrl_set(struct uvc_video_chain *chain, +int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl) { + struct uvc_video_chain *chain = handle->chain; struct uvc_control *ctrl; struct uvc_control_mapping *mapping; s32 value; @@ -1581,6 +1683,9 @@ int uvc_ctrl_set(struct uvc_video_chain *chain, mapping->set(mapping, value, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); + if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS) + ctrl->handle = handle; + ctrl->dirty = 1; ctrl->modified = 1; return 0; @@ -1612,7 +1717,9 @@ static int uvc_ctrl_get_flags(struct uvc_device *dev, | (data[0] & UVC_CONTROL_CAP_SET ? UVC_CTRL_FLAG_SET_CUR : 0) | (data[0] & UVC_CONTROL_CAP_AUTOUPDATE ? - UVC_CTRL_FLAG_AUTO_UPDATE : 0); + UVC_CTRL_FLAG_AUTO_UPDATE : 0) + | (data[0] & UVC_CONTROL_CAP_ASYNCHRONOUS ? + UVC_CTRL_FLAG_ASYNCHRONOUS : 0); kfree(data); return ret; @@ -2173,6 +2280,8 @@ int uvc_ctrl_init_device(struct uvc_device *dev) struct uvc_entity *entity; unsigned int i; + INIT_WORK(&dev->async_ctrl.work, uvc_ctrl_status_event_work); + /* Walk the entities list and instantiate controls */ list_for_each_entry(entity, &dev->entities, list) { struct uvc_control *ctrl; @@ -2241,6 +2350,8 @@ void uvc_ctrl_cleanup_device(struct uvc_device *dev) struct uvc_entity *entity; unsigned int i; + cancel_work_sync(&dev->async_ctrl.work); + /* Free controls and control mappings for all entities. */ list_for_each_entry(entity, &dev->entities, list) { for (i = 0; i < entity->ncontrols; ++i) { |