Message ID | alpine.DEB.2.20.1807261011420.7546@axis700.grange (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [v9] uvcvideo: send a control event when a Control Change interrupt arrives | expand |
Hi Guennadi, Thank you for the patch. On Thursday, 26 July 2018 11:17:53 EEST Guennadi Liakhovetski wrote: > From: Guennadi Liakhovetski <guennadi.liakhovetski@intel.com> > > UVC defines a method of handling asynchronous controls, which sends a > USB packet over the interrupt pipe. This patch implements support for > such packets by sending a control event to the user. Since this can > involve USB traffic and, therefore, scheduling, this has to be done > in a work queue. > > Signed-off-by: Guennadi Liakhovetski <guennadi.liakhovetski@intel.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> applied to my tree and pushed to the uvc/next branch. > --- > > v9: > - multiple optimisations and style improvements from Laurent - thanks > - avoid the handle rewriting race at least in cases, when the user only > sends a new control after receiving an event for the previous one > > Laurent, you added a couple of comments, using DocBook markup like > "@param" but you didn't mark those comments for DocBook processing with > "/**" - I don't care that much, just wanted to check, that that was > intentional. Yes, it's not worth compiling that as kerneldoc, I just wanted to mark references to variable names for clarity. > drivers/media/usb/uvc/uvc_ctrl.c | 211 > ++++++++++++++++++++++++++++--------- drivers/media/usb/uvc/uvc_status.c | > 121 ++++++++++++++++++--- > drivers/media/usb/uvc/uvc_v4l2.c | 4 +- > drivers/media/usb/uvc/uvcvideo.h | 15 ++- > include/uapi/linux/uvcvideo.h | 2 + > 5 files changed, 286 insertions(+), 67 deletions(-) > > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c > b/drivers/media/usb/uvc/uvc_ctrl.c index 2a213c8..ddd069b 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; > } > @@ -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 != &handle->vfh || > + if (sev->fh != originator || > (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK) || > (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) { > diff --git a/drivers/media/usb/uvc/uvc_status.c > b/drivers/media/usb/uvc/uvc_status.c index 7b71041..0722dc6 100644 > --- a/drivers/media/usb/uvc/uvc_status.c > +++ b/drivers/media/usb/uvc/uvc_status.c > @@ -78,7 +78,24 @@ static void uvc_input_report_key(struct uvc_device *dev, > unsigned int code, /* > -------------------------------------------------------------------------- > * Status interrupt endpoint > */ > -static void uvc_event_streaming(struct uvc_device *dev, u8 *data, int len) > +struct uvc_streaming_status { > + u8 bStatusType; > + u8 bOriginator; > + u8 bEvent; > + u8 bValue[]; > +} __packed; > + > +struct uvc_control_status { > + u8 bStatusType; > + u8 bOriginator; > + u8 bEvent; > + u8 bSelector; > + u8 bAttribute; > + u8 bValue[]; > +} __packed; > + > +static void uvc_event_streaming(struct uvc_device *dev, > + struct uvc_streaming_status *status, int len) > { > if (len < 3) { > uvc_trace(UVC_TRACE_STATUS, "Invalid streaming status event " > @@ -86,31 +103,97 @@ static void uvc_event_streaming(struct uvc_device *dev, > u8 *data, int len) return; > } > > - if (data[2] == 0) { > + if (status->bEvent == 0) { > if (len < 4) > return; > uvc_trace(UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n", > - data[1], data[3] ? "pressed" : "released", len); > - uvc_input_report_key(dev, KEY_CAMERA, data[3]); > + status->bOriginator, > + status->bValue[0] ? "pressed" : "released", len); > + uvc_input_report_key(dev, KEY_CAMERA, status->bValue[0]); > } else { > uvc_trace(UVC_TRACE_STATUS, > "Stream %u error event %02x len %d.\n", > - data[1], data[2], len); > + status->bOriginator, status->bEvent, len); > } > } > > -static void uvc_event_control(struct uvc_device *dev, u8 *data, int len) > +#define UVC_CTRL_VALUE_CHANGE 0 > +#define UVC_CTRL_INFO_CHANGE 1 > +#define UVC_CTRL_FAILURE_CHANGE 2 > +#define UVC_CTRL_MIN_CHANGE 3 > +#define UVC_CTRL_MAX_CHANGE 4 > + > +static struct uvc_control *uvc_event_entity_find_ctrl(struct uvc_entity > *entity, + u8 selector) > { > - char *attrs[3] = { "value", "info", "failure" }; > + struct uvc_control *ctrl; > + unsigned int i; > + > + for (i = 0, ctrl = entity->controls; i < entity->ncontrols; i++, ctrl++) > + if (ctrl->info.selector == selector) > + return ctrl; > + > + return NULL; > +} > > - if (len < 6 || data[2] != 0 || data[4] > 2) { > +static struct uvc_control *uvc_event_find_ctrl(struct uvc_device *dev, > + const struct uvc_control_status *status, > + struct uvc_video_chain **chain) > +{ > + list_for_each_entry((*chain), &dev->chains, list) { > + struct uvc_entity *entity; > + struct uvc_control *ctrl; > + > + list_for_each_entry(entity, &(*chain)->entities, chain) { > + if (entity->id != status->bOriginator) > + continue; > + > + ctrl = uvc_event_entity_find_ctrl(entity, > + status->bSelector); > + if (ctrl) > + return ctrl; > + } > + } > + > + return NULL; > +} > + > +static bool uvc_event_control(struct urb *urb, > + const struct uvc_control_status *status, int len) > +{ > + static const char *attrs[] = { "value", "info", "failure", "min", "max" }; > + struct uvc_device *dev = urb->context; > + struct uvc_video_chain *chain; > + struct uvc_control *ctrl; > + > + if (len < 6 || status->bEvent != 0 || > + status->bAttribute >= ARRAY_SIZE(attrs)) { > uvc_trace(UVC_TRACE_STATUS, "Invalid control status event " > "received.\n"); > - return; > + return false; > } > > uvc_trace(UVC_TRACE_STATUS, "Control %u/%u %s change len %d.\n", > - data[1], data[3], attrs[data[4]], len); > + status->bOriginator, status->bSelector, > + attrs[status->bAttribute], len); > + > + /* Find the control. */ > + ctrl = uvc_event_find_ctrl(dev, status, &chain); > + if (!ctrl) > + return false; > + > + switch (status->bAttribute) { > + case UVC_CTRL_VALUE_CHANGE: > + return uvc_ctrl_status_event(urb, chain, ctrl, status->bValue); > + > + case UVC_CTRL_INFO_CHANGE: > + case UVC_CTRL_FAILURE_CHANGE: > + case UVC_CTRL_MIN_CHANGE: > + case UVC_CTRL_MAX_CHANGE: > + break; > + } > + > + return false; > } > > static void uvc_status_complete(struct urb *urb) > @@ -138,13 +221,23 @@ static void uvc_status_complete(struct urb *urb) > len = urb->actual_length; > if (len > 0) { > switch (dev->status[0] & 0x0f) { > - case UVC_STATUS_TYPE_CONTROL: > - uvc_event_control(dev, dev->status, len); > + case UVC_STATUS_TYPE_CONTROL: { > + struct uvc_control_status *status = > + (struct uvc_control_status *)dev->status; > + > + if (uvc_event_control(urb, status, len)) > + /* The URB will be resubmitted in work context. */ > + return; > break; > + } > > - case UVC_STATUS_TYPE_STREAMING: > - uvc_event_streaming(dev, dev->status, len); > + case UVC_STATUS_TYPE_STREAMING: { > + struct uvc_streaming_status *status = > + (struct uvc_streaming_status *)dev->status; > + > + uvc_event_streaming(dev, status, len); > break; > + } > > default: > uvc_trace(UVC_TRACE_STATUS, "Unknown status event " > diff --git a/drivers/media/usb/uvc/uvc_v4l2.c > b/drivers/media/usb/uvc/uvc_v4l2.c index bd32914..18a7384 100644 > --- a/drivers/media/usb/uvc/uvc_v4l2.c > +++ b/drivers/media/usb/uvc/uvc_v4l2.c > @@ -994,7 +994,7 @@ static int uvc_ioctl_s_ctrl(struct file *file, void *fh, > if (ret < 0) > return ret; > > - ret = uvc_ctrl_set(chain, &xctrl); > + ret = uvc_ctrl_set(handle, &xctrl); > if (ret < 0) { > uvc_ctrl_rollback(handle); > return ret; > @@ -1069,7 +1069,7 @@ static int uvc_ioctl_s_try_ext_ctrls(struct uvc_fh > *handle, return ret; > > for (i = 0; i < ctrls->count; ++ctrl, ++i) { > - ret = uvc_ctrl_set(chain, ctrl); > + ret = uvc_ctrl_set(handle, ctrl); > if (ret < 0) { > uvc_ctrl_rollback(handle); > ctrls->error_idx = commit ? ctrls->count : i; > diff --git a/drivers/media/usb/uvc/uvcvideo.h > b/drivers/media/usb/uvc/uvcvideo.h index be5cf17..8ae03a4 100644 > --- a/drivers/media/usb/uvc/uvcvideo.h > +++ b/drivers/media/usb/uvc/uvcvideo.h > @@ -12,6 +12,7 @@ > #include <linux/usb/video.h> > #include <linux/uvcvideo.h> > #include <linux/videodev2.h> > +#include <linux/workqueue.h> > #include <media/media-device.h> > #include <media/v4l2-device.h> > #include <media/v4l2-event.h> > @@ -256,6 +257,8 @@ struct uvc_control { > initialized:1; > > u8 *uvc_data; > + > + struct uvc_fh *handle; /* File handle that last changed the control. */ > }; > > struct uvc_format_desc { > @@ -600,6 +603,14 @@ struct uvc_device { > u8 *status; > struct input_dev *input; > char input_phys[64]; > + > + struct uvc_ctrl_work { > + struct work_struct work; > + struct urb *urb; > + struct uvc_video_chain *chain; > + struct uvc_control *ctrl; > + const void *data; > + } async_ctrl; > }; > > enum uvc_handle_state { > @@ -753,6 +764,8 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain, > int uvc_ctrl_init_device(struct uvc_device *dev); > void uvc_ctrl_cleanup_device(struct uvc_device *dev); > int uvc_ctrl_restore_values(struct uvc_device *dev); > +bool uvc_ctrl_status_event(struct urb *urb, struct uvc_video_chain *chain, > + struct uvc_control *ctrl, const u8 *data); > > int uvc_ctrl_begin(struct uvc_video_chain *chain); > int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback, > @@ -770,7 +783,7 @@ static inline int uvc_ctrl_rollback(struct uvc_fh > *handle) } > > int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control > *xctrl); -int uvc_ctrl_set(struct uvc_video_chain *chain, struct > v4l2_ext_control *xctrl); +int uvc_ctrl_set(struct uvc_fh *handle, struct > v4l2_ext_control *xctrl); > > int uvc_xu_ctrl_query(struct uvc_video_chain *chain, > struct uvc_xu_control_query *xqry); > diff --git a/include/uapi/linux/uvcvideo.h b/include/uapi/linux/uvcvideo.h > index 020714d..f80f05b 100644 > --- a/include/uapi/linux/uvcvideo.h > +++ b/include/uapi/linux/uvcvideo.h > @@ -28,6 +28,8 @@ > #define UVC_CTRL_FLAG_RESTORE (1 << 6) > /* Control can be updated by the camera. */ > #define UVC_CTRL_FLAG_AUTO_UPDATE (1 << 7) > +/* Control supports asynchronous reporting */ > +#define UVC_CTRL_FLAG_ASYNCHRONOUS (1 << 8) > > #define UVC_CTRL_FLAG_GET_RANGE \ > (UVC_CTRL_FLAG_GET_CUR | UVC_CTRL_FLAG_GET_MIN | \
Hi Laurent, Thanks. Now we can get to the next one: https://patchwork.linuxtv.org/patch/46184/ - without that one nobody can get complete D4XX metadata. After I get your comments to it I'll address them together with Sakari's ones. Thanks Guennadi On Thu, 26 Jul 2018, Laurent Pinchart wrote: > Hi Guennadi, > > Thank you for the patch. > > On Thursday, 26 July 2018 11:17:53 EEST Guennadi Liakhovetski wrote: > > From: Guennadi Liakhovetski <guennadi.liakhovetski@intel.com> > > > > UVC defines a method of handling asynchronous controls, which sends a > > USB packet over the interrupt pipe. This patch implements support for > > such packets by sending a control event to the user. Since this can > > involve USB traffic and, therefore, scheduling, this has to be done > > in a work queue. > > > > Signed-off-by: Guennadi Liakhovetski <guennadi.liakhovetski@intel.com> > > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> > > applied to my tree and pushed to the uvc/next branch. > > > --- > > > > v9: > > - multiple optimisations and style improvements from Laurent - thanks > > - avoid the handle rewriting race at least in cases, when the user only > > sends a new control after receiving an event for the previous one > > > > Laurent, you added a couple of comments, using DocBook markup like > > "@param" but you didn't mark those comments for DocBook processing with > > "/**" - I don't care that much, just wanted to check, that that was > > intentional. > > Yes, it's not worth compiling that as kerneldoc, I just wanted to mark > references to variable names for clarity. > > > drivers/media/usb/uvc/uvc_ctrl.c | 211 > > ++++++++++++++++++++++++++++--------- drivers/media/usb/uvc/uvc_status.c | > > 121 ++++++++++++++++++--- > > drivers/media/usb/uvc/uvc_v4l2.c | 4 +- > > drivers/media/usb/uvc/uvcvideo.h | 15 ++- > > include/uapi/linux/uvcvideo.h | 2 + > > 5 files changed, 286 insertions(+), 67 deletions(-) > > > > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c > > b/drivers/media/usb/uvc/uvc_ctrl.c index 2a213c8..ddd069b 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; > > } > > @@ -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 != &handle->vfh || > > + if (sev->fh != originator || > > (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK) || > > (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) { > > diff --git a/drivers/media/usb/uvc/uvc_status.c > > b/drivers/media/usb/uvc/uvc_status.c index 7b71041..0722dc6 100644 > > --- a/drivers/media/usb/uvc/uvc_status.c > > +++ b/drivers/media/usb/uvc/uvc_status.c > > @@ -78,7 +78,24 @@ static void uvc_input_report_key(struct uvc_device *dev, > > unsigned int code, /* > > -------------------------------------------------------------------------- > > * Status interrupt endpoint > > */ > > -static void uvc_event_streaming(struct uvc_device *dev, u8 *data, int len) > > +struct uvc_streaming_status { > > + u8 bStatusType; > > + u8 bOriginator; > > + u8 bEvent; > > + u8 bValue[]; > > +} __packed; > > + > > +struct uvc_control_status { > > + u8 bStatusType; > > + u8 bOriginator; > > + u8 bEvent; > > + u8 bSelector; > > + u8 bAttribute; > > + u8 bValue[]; > > +} __packed; > > + > > +static void uvc_event_streaming(struct uvc_device *dev, > > + struct uvc_streaming_status *status, int len) > > { > > if (len < 3) { > > uvc_trace(UVC_TRACE_STATUS, "Invalid streaming status event " > > @@ -86,31 +103,97 @@ static void uvc_event_streaming(struct uvc_device *dev, > > u8 *data, int len) return; > > } > > > > - if (data[2] == 0) { > > + if (status->bEvent == 0) { > > if (len < 4) > > return; > > uvc_trace(UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n", > > - data[1], data[3] ? "pressed" : "released", len); > > - uvc_input_report_key(dev, KEY_CAMERA, data[3]); > > + status->bOriginator, > > + status->bValue[0] ? "pressed" : "released", len); > > + uvc_input_report_key(dev, KEY_CAMERA, status->bValue[0]); > > } else { > > uvc_trace(UVC_TRACE_STATUS, > > "Stream %u error event %02x len %d.\n", > > - data[1], data[2], len); > > + status->bOriginator, status->bEvent, len); > > } > > } > > > > -static void uvc_event_control(struct uvc_device *dev, u8 *data, int len) > > +#define UVC_CTRL_VALUE_CHANGE 0 > > +#define UVC_CTRL_INFO_CHANGE 1 > > +#define UVC_CTRL_FAILURE_CHANGE 2 > > +#define UVC_CTRL_MIN_CHANGE 3 > > +#define UVC_CTRL_MAX_CHANGE 4 > > + > > +static struct uvc_control *uvc_event_entity_find_ctrl(struct uvc_entity > > *entity, + u8 selector) > > { > > - char *attrs[3] = { "value", "info", "failure" }; > > + struct uvc_control *ctrl; > > + unsigned int i; > > + > > + for (i = 0, ctrl = entity->controls; i < entity->ncontrols; i++, ctrl++) > > + if (ctrl->info.selector == selector) > > + return ctrl; > > + > > + return NULL; > > +} > > > > - if (len < 6 || data[2] != 0 || data[4] > 2) { > > +static struct uvc_control *uvc_event_find_ctrl(struct uvc_device *dev, > > + const struct uvc_control_status *status, > > + struct uvc_video_chain **chain) > > +{ > > + list_for_each_entry((*chain), &dev->chains, list) { > > + struct uvc_entity *entity; > > + struct uvc_control *ctrl; > > + > > + list_for_each_entry(entity, &(*chain)->entities, chain) { > > + if (entity->id != status->bOriginator) > > + continue; > > + > > + ctrl = uvc_event_entity_find_ctrl(entity, > > + status->bSelector); > > + if (ctrl) > > + return ctrl; > > + } > > + } > > + > > + return NULL; > > +} > > + > > +static bool uvc_event_control(struct urb *urb, > > + const struct uvc_control_status *status, int len) > > +{ > > + static const char *attrs[] = { "value", "info", "failure", "min", "max" }; > > + struct uvc_device *dev = urb->context; > > + struct uvc_video_chain *chain; > > + struct uvc_control *ctrl; > > + > > + if (len < 6 || status->bEvent != 0 || > > + status->bAttribute >= ARRAY_SIZE(attrs)) { > > uvc_trace(UVC_TRACE_STATUS, "Invalid control status event " > > "received.\n"); > > - return; > > + return false; > > } > > > > uvc_trace(UVC_TRACE_STATUS, "Control %u/%u %s change len %d.\n", > > - data[1], data[3], attrs[data[4]], len); > > + status->bOriginator, status->bSelector, > > + attrs[status->bAttribute], len); > > + > > + /* Find the control. */ > > + ctrl = uvc_event_find_ctrl(dev, status, &chain); > > + if (!ctrl) > > + return false; > > + > > + switch (status->bAttribute) { > > + case UVC_CTRL_VALUE_CHANGE: > > + return uvc_ctrl_status_event(urb, chain, ctrl, status->bValue); > > + > > + case UVC_CTRL_INFO_CHANGE: > > + case UVC_CTRL_FAILURE_CHANGE: > > + case UVC_CTRL_MIN_CHANGE: > > + case UVC_CTRL_MAX_CHANGE: > > + break; > > + } > > + > > + return false; > > } > > > > static void uvc_status_complete(struct urb *urb) > > @@ -138,13 +221,23 @@ static void uvc_status_complete(struct urb *urb) > > len = urb->actual_length; > > if (len > 0) { > > switch (dev->status[0] & 0x0f) { > > - case UVC_STATUS_TYPE_CONTROL: > > - uvc_event_control(dev, dev->status, len); > > + case UVC_STATUS_TYPE_CONTROL: { > > + struct uvc_control_status *status = > > + (struct uvc_control_status *)dev->status; > > + > > + if (uvc_event_control(urb, status, len)) > > + /* The URB will be resubmitted in work context. */ > > + return; > > break; > > + } > > > > - case UVC_STATUS_TYPE_STREAMING: > > - uvc_event_streaming(dev, dev->status, len); > > + case UVC_STATUS_TYPE_STREAMING: { > > + struct uvc_streaming_status *status = > > + (struct uvc_streaming_status *)dev->status; > > + > > + uvc_event_streaming(dev, status, len); > > break; > > + } > > > > default: > > uvc_trace(UVC_TRACE_STATUS, "Unknown status event " > > diff --git a/drivers/media/usb/uvc/uvc_v4l2.c > > b/drivers/media/usb/uvc/uvc_v4l2.c index bd32914..18a7384 100644 > > --- a/drivers/media/usb/uvc/uvc_v4l2.c > > +++ b/drivers/media/usb/uvc/uvc_v4l2.c > > @@ -994,7 +994,7 @@ static int uvc_ioctl_s_ctrl(struct file *file, void *fh, > > if (ret < 0) > > return ret; > > > > - ret = uvc_ctrl_set(chain, &xctrl); > > + ret = uvc_ctrl_set(handle, &xctrl); > > if (ret < 0) { > > uvc_ctrl_rollback(handle); > > return ret; > > @@ -1069,7 +1069,7 @@ static int uvc_ioctl_s_try_ext_ctrls(struct uvc_fh > > *handle, return ret; > > > > for (i = 0; i < ctrls->count; ++ctrl, ++i) { > > - ret = uvc_ctrl_set(chain, ctrl); > > + ret = uvc_ctrl_set(handle, ctrl); > > if (ret < 0) { > > uvc_ctrl_rollback(handle); > > ctrls->error_idx = commit ? ctrls->count : i; > > diff --git a/drivers/media/usb/uvc/uvcvideo.h > > b/drivers/media/usb/uvc/uvcvideo.h index be5cf17..8ae03a4 100644 > > --- a/drivers/media/usb/uvc/uvcvideo.h > > +++ b/drivers/media/usb/uvc/uvcvideo.h > > @@ -12,6 +12,7 @@ > > #include <linux/usb/video.h> > > #include <linux/uvcvideo.h> > > #include <linux/videodev2.h> > > +#include <linux/workqueue.h> > > #include <media/media-device.h> > > #include <media/v4l2-device.h> > > #include <media/v4l2-event.h> > > @@ -256,6 +257,8 @@ struct uvc_control { > > initialized:1; > > > > u8 *uvc_data; > > + > > + struct uvc_fh *handle; /* File handle that last changed the control. */ > > }; > > > > struct uvc_format_desc { > > @@ -600,6 +603,14 @@ struct uvc_device { > > u8 *status; > > struct input_dev *input; > > char input_phys[64]; > > + > > + struct uvc_ctrl_work { > > + struct work_struct work; > > + struct urb *urb; > > + struct uvc_video_chain *chain; > > + struct uvc_control *ctrl; > > + const void *data; > > + } async_ctrl; > > }; > > > > enum uvc_handle_state { > > @@ -753,6 +764,8 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain, > > int uvc_ctrl_init_device(struct uvc_device *dev); > > void uvc_ctrl_cleanup_device(struct uvc_device *dev); > > int uvc_ctrl_restore_values(struct uvc_device *dev); > > +bool uvc_ctrl_status_event(struct urb *urb, struct uvc_video_chain *chain, > > + struct uvc_control *ctrl, const u8 *data); > > > > int uvc_ctrl_begin(struct uvc_video_chain *chain); > > int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback, > > @@ -770,7 +783,7 @@ static inline int uvc_ctrl_rollback(struct uvc_fh > > *handle) } > > > > int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control > > *xctrl); -int uvc_ctrl_set(struct uvc_video_chain *chain, struct > > v4l2_ext_control *xctrl); +int uvc_ctrl_set(struct uvc_fh *handle, struct > > v4l2_ext_control *xctrl); > > > > int uvc_xu_ctrl_query(struct uvc_video_chain *chain, > > struct uvc_xu_control_query *xqry); > > diff --git a/include/uapi/linux/uvcvideo.h b/include/uapi/linux/uvcvideo.h > > index 020714d..f80f05b 100644 > > --- a/include/uapi/linux/uvcvideo.h > > +++ b/include/uapi/linux/uvcvideo.h > > @@ -28,6 +28,8 @@ > > #define UVC_CTRL_FLAG_RESTORE (1 << 6) > > /* Control can be updated by the camera. */ > > #define UVC_CTRL_FLAG_AUTO_UPDATE (1 << 7) > > +/* Control supports asynchronous reporting */ > > +#define UVC_CTRL_FLAG_ASYNCHRONOUS (1 << 8) > > > > #define UVC_CTRL_FLAG_GET_RANGE \ > > (UVC_CTRL_FLAG_GET_CUR | UVC_CTRL_FLAG_GET_MIN | \ > > > -- > Regards, > > Laurent Pinchart > > >
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c index 2a213c8..ddd069b 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; } @@ -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 != &handle->vfh || + if (sev->fh != originator || (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK) || (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) { diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c index 7b71041..0722dc6 100644 --- a/drivers/media/usb/uvc/uvc_status.c +++ b/drivers/media/usb/uvc/uvc_status.c @@ -78,7 +78,24 @@ static void uvc_input_report_key(struct uvc_device *dev, unsigned int code, /* -------------------------------------------------------------------------- * Status interrupt endpoint */ -static void uvc_event_streaming(struct uvc_device *dev, u8 *data, int len) +struct uvc_streaming_status { + u8 bStatusType; + u8 bOriginator; + u8 bEvent; + u8 bValue[]; +} __packed; + +struct uvc_control_status { + u8 bStatusType; + u8 bOriginator; + u8 bEvent; + u8 bSelector; + u8 bAttribute; + u8 bValue[]; +} __packed; + +static void uvc_event_streaming(struct uvc_device *dev, + struct uvc_streaming_status *status, int len) { if (len < 3) { uvc_trace(UVC_TRACE_STATUS, "Invalid streaming status event " @@ -86,31 +103,97 @@ static void uvc_event_streaming(struct uvc_device *dev, u8 *data, int len) return; } - if (data[2] == 0) { + if (status->bEvent == 0) { if (len < 4) return; uvc_trace(UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n", - data[1], data[3] ? "pressed" : "released", len); - uvc_input_report_key(dev, KEY_CAMERA, data[3]); + status->bOriginator, + status->bValue[0] ? "pressed" : "released", len); + uvc_input_report_key(dev, KEY_CAMERA, status->bValue[0]); } else { uvc_trace(UVC_TRACE_STATUS, "Stream %u error event %02x len %d.\n", - data[1], data[2], len); + status->bOriginator, status->bEvent, len); } } -static void uvc_event_control(struct uvc_device *dev, u8 *data, int len) +#define UVC_CTRL_VALUE_CHANGE 0 +#define UVC_CTRL_INFO_CHANGE 1 +#define UVC_CTRL_FAILURE_CHANGE 2 +#define UVC_CTRL_MIN_CHANGE 3 +#define UVC_CTRL_MAX_CHANGE 4 + +static struct uvc_control *uvc_event_entity_find_ctrl(struct uvc_entity *entity, + u8 selector) { - char *attrs[3] = { "value", "info", "failure" }; + struct uvc_control *ctrl; + unsigned int i; + + for (i = 0, ctrl = entity->controls; i < entity->ncontrols; i++, ctrl++) + if (ctrl->info.selector == selector) + return ctrl; + + return NULL; +} - if (len < 6 || data[2] != 0 || data[4] > 2) { +static struct uvc_control *uvc_event_find_ctrl(struct uvc_device *dev, + const struct uvc_control_status *status, + struct uvc_video_chain **chain) +{ + list_for_each_entry((*chain), &dev->chains, list) { + struct uvc_entity *entity; + struct uvc_control *ctrl; + + list_for_each_entry(entity, &(*chain)->entities, chain) { + if (entity->id != status->bOriginator) + continue; + + ctrl = uvc_event_entity_find_ctrl(entity, + status->bSelector); + if (ctrl) + return ctrl; + } + } + + return NULL; +} + +static bool uvc_event_control(struct urb *urb, + const struct uvc_control_status *status, int len) +{ + static const char *attrs[] = { "value", "info", "failure", "min", "max" }; + struct uvc_device *dev = urb->context; + struct uvc_video_chain *chain; + struct uvc_control *ctrl; + + if (len < 6 || status->bEvent != 0 || + status->bAttribute >= ARRAY_SIZE(attrs)) { uvc_trace(UVC_TRACE_STATUS, "Invalid control status event " "received.\n"); - return; + return false; } uvc_trace(UVC_TRACE_STATUS, "Control %u/%u %s change len %d.\n", - data[1], data[3], attrs[data[4]], len); + status->bOriginator, status->bSelector, + attrs[status->bAttribute], len); + + /* Find the control. */ + ctrl = uvc_event_find_ctrl(dev, status, &chain); + if (!ctrl) + return false; + + switch (status->bAttribute) { + case UVC_CTRL_VALUE_CHANGE: + return uvc_ctrl_status_event(urb, chain, ctrl, status->bValue); + + case UVC_CTRL_INFO_CHANGE: + case UVC_CTRL_FAILURE_CHANGE: + case UVC_CTRL_MIN_CHANGE: + case UVC_CTRL_MAX_CHANGE: + break; + } + + return false; } static void uvc_status_complete(struct urb *urb) @@ -138,13 +221,23 @@ static void uvc_status_complete(struct urb *urb) len = urb->actual_length; if (len > 0) { switch (dev->status[0] & 0x0f) { - case UVC_STATUS_TYPE_CONTROL: - uvc_event_control(dev, dev->status, len); + case UVC_STATUS_TYPE_CONTROL: { + struct uvc_control_status *status = + (struct uvc_control_status *)dev->status; + + if (uvc_event_control(urb, status, len)) + /* The URB will be resubmitted in work context. */ + return; break; + } - case UVC_STATUS_TYPE_STREAMING: - uvc_event_streaming(dev, dev->status, len); + case UVC_STATUS_TYPE_STREAMING: { + struct uvc_streaming_status *status = + (struct uvc_streaming_status *)dev->status; + + uvc_event_streaming(dev, status, len); break; + } default: uvc_trace(UVC_TRACE_STATUS, "Unknown status event " diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index bd32914..18a7384 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -994,7 +994,7 @@ static int uvc_ioctl_s_ctrl(struct file *file, void *fh, if (ret < 0) return ret; - ret = uvc_ctrl_set(chain, &xctrl); + ret = uvc_ctrl_set(handle, &xctrl); if (ret < 0) { uvc_ctrl_rollback(handle); return ret; @@ -1069,7 +1069,7 @@ static int uvc_ioctl_s_try_ext_ctrls(struct uvc_fh *handle, return ret; for (i = 0; i < ctrls->count; ++ctrl, ++i) { - ret = uvc_ctrl_set(chain, ctrl); + ret = uvc_ctrl_set(handle, ctrl); if (ret < 0) { uvc_ctrl_rollback(handle); ctrls->error_idx = commit ? ctrls->count : i; diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index be5cf17..8ae03a4 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -12,6 +12,7 @@ #include <linux/usb/video.h> #include <linux/uvcvideo.h> #include <linux/videodev2.h> +#include <linux/workqueue.h> #include <media/media-device.h> #include <media/v4l2-device.h> #include <media/v4l2-event.h> @@ -256,6 +257,8 @@ struct uvc_control { initialized:1; u8 *uvc_data; + + struct uvc_fh *handle; /* File handle that last changed the control. */ }; struct uvc_format_desc { @@ -600,6 +603,14 @@ struct uvc_device { u8 *status; struct input_dev *input; char input_phys[64]; + + struct uvc_ctrl_work { + struct work_struct work; + struct urb *urb; + struct uvc_video_chain *chain; + struct uvc_control *ctrl; + const void *data; + } async_ctrl; }; enum uvc_handle_state { @@ -753,6 +764,8 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain, int uvc_ctrl_init_device(struct uvc_device *dev); void uvc_ctrl_cleanup_device(struct uvc_device *dev); int uvc_ctrl_restore_values(struct uvc_device *dev); +bool uvc_ctrl_status_event(struct urb *urb, struct uvc_video_chain *chain, + struct uvc_control *ctrl, const u8 *data); int uvc_ctrl_begin(struct uvc_video_chain *chain); int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback, @@ -770,7 +783,7 @@ static inline int uvc_ctrl_rollback(struct uvc_fh *handle) } int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl); -int uvc_ctrl_set(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl); +int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl); int uvc_xu_ctrl_query(struct uvc_video_chain *chain, struct uvc_xu_control_query *xqry); diff --git a/include/uapi/linux/uvcvideo.h b/include/uapi/linux/uvcvideo.h index 020714d..f80f05b 100644 --- a/include/uapi/linux/uvcvideo.h +++ b/include/uapi/linux/uvcvideo.h @@ -28,6 +28,8 @@ #define UVC_CTRL_FLAG_RESTORE (1 << 6) /* Control can be updated by the camera. */ #define UVC_CTRL_FLAG_AUTO_UPDATE (1 << 7) +/* Control supports asynchronous reporting */ +#define UVC_CTRL_FLAG_ASYNCHRONOUS (1 << 8) #define UVC_CTRL_FLAG_GET_RANGE \ (UVC_CTRL_FLAG_GET_CUR | UVC_CTRL_FLAG_GET_MIN | \