All of lore.kernel.org
 help / color / mirror / Atom feed
From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
To: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Cc: linux-media@vger.kernel.org
Subject: Re: [PATCH v7 1/2] uvcvideo: send a control event when a Control Change interrupt arrives
Date: Sat, 07 Apr 2018 13:50:11 +0300	[thread overview]
Message-ID: <9522794.8ROHdGlsdf@avalon> (raw)
In-Reply-To: <alpine.DEB.2.20.1804071129560.6140@axis700.grange>

Hi Guennadi,

On Saturday, 7 April 2018 12:30:44 EEST Guennadi Liakhovetski wrote:
> Hi Laurent,
> 
> Thanks for the review. As soon as the second patch has also been reviewed,
> I'll work on both of them.

I would have sworn I had sent the review for 2/2 as well. I'll get to it right 
away.

> On Fri, 23 Mar 2018, Laurent Pinchart wrote:
> > Hi Guennadi,
> > 
> > Thank you for the patch.
> > 
> > On Friday, 23 March 2018 11:24:00 EET Laurent Pinchart wrote:
> > > From: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> > > 
> > > 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>
> > > ---
> > > 
> > >  drivers/media/usb/uvc/uvc_ctrl.c   | 166
> > >  ++++++++++++++++++++++++++++++----
> > >  drivers/media/usb/uvc/uvc_status.c | 111 ++++++++++++++++++++++---
> > >  drivers/media/usb/uvc/uvc_v4l2.c   |   4 +-
> > >  drivers/media/usb/uvc/uvcvideo.h   |  15 +++-
> > >  include/uapi/linux/uvcvideo.h      |   2 +
> > >  5 files changed, 269 insertions(+), 29 deletions(-)
> > > 
> > > diff --git a/drivers/media/usb/uvc/uvc_ctrl.c
> > > b/drivers/media/usb/uvc/uvc_ctrl.c index 4042cbdb721b..f4773c56438c
> > > 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>
> > > 
> > > @@ -1222,30 +1223,134 @@ static void uvc_ctrl_send_event(struct uvc_fh
> > > *handle, {
> > > 
> > >  	struct v4l2_subscribed_event *sev;
> > >  	struct v4l2_event ev;
> > > 
> > > +	bool autoupdate;
> > > 
> > >  	if (list_empty(&mapping->ev_subs))
> > >  	
> > >  		return;
> > > 
> > > +	if (!handle) {
> > 
> > In which circumstances does this happen ? Is it when the device reports a
> > control change event without an prior control set ? Have you seen that
> > happening in practice ?
> > 
> > > +		autoupdate = true;
> > > +		sev = list_first_entry(&mapping->ev_subs,
> > > +				       struct v4l2_subscribed_event, node);
> > > +		handle = container_of(sev->fh, struct uvc_fh, vfh);
> > 
> > There's a check below that guards against sev->fh being NULL. Could
> > sev->fh be NULL here ?
> > 
> > > +	} else {
> > > +		autoupdate = false;
> > > +	}
> > > +
> > > 
> > >  	uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, value,
> > >  	changes);
> > >  	
> > >  	list_for_each_entry(sev, &mapping->ev_subs, node) {
> > >  	
> > >  		if (sev->fh && (sev->fh != &handle->vfh ||
> > >  		
> > >  		    (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK) ||
> > > 
> > > -		    (changes & V4L2_EVENT_CTRL_CH_FLAGS)))
> > > +		    (changes & V4L2_EVENT_CTRL_CH_FLAGS) || autoupdate))
> > > 
> > >  			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)
> > > +static void __uvc_ctrl_send_slave_event(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;
> > > 
> > > +	__uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0);
> > > +	if (ctrl == NULL)
> > > +		return;
> > > +
> > > +	if (__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0)
> > > +		changes |= V4L2_EVENT_CTRL_CH_VALUE;
> > > +
> > > +	uvc_ctrl_send_event(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_video_chain *chain;
> > > +	struct uvc_ctrl_work *w = &dev->async_ctrl;
> > > +	struct uvc_control_mapping *mapping;
> > > +	struct uvc_control *ctrl;
> > > +	struct uvc_fh *handle;
> > > +	unsigned int i;
> > > +	u8 *data;
> > > +
> > > +	spin_lock_irq(&w->lock);
> > > +	data = w->data;
> > > +	w->data = NULL;
> > > +	chain = w->chain;
> > > +	ctrl = w->ctrl;
> > > +	handle = ctrl->handle;
> > > +	ctrl->handle = NULL;
> > > +	spin_unlock_irq(&w->lock);
> > > +
> > > +	if (mutex_lock_interruptible(&chain->ctrl_mutex))
> > > +		goto free;
> > 
> > This will result in the event being lost, which isn't very nice (see below
> > for additional comments on this topic). Can't we use mutex_lock() ?
> > 
> > > +
> > > +	list_for_each_entry(mapping, &ctrl->info.mappings, list) {
> > > +		s32 value = mapping->get(mapping, UVC_GET_CUR, data);
> > > +
> > > +		for (i = 0; i < ARRAY_SIZE(mapping->slave_ids); ++i) {
> > > +			if (!mapping->slave_ids[i])
> > > +				break;
> > > +
> > > +			__uvc_ctrl_send_slave_event(handle, ctrl,
> > > +						    mapping->slave_ids[i]);
> > > +		}
> > > +
> > > +		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;
> > > +				}
> > > +		}
> > 
> > Would this be useful to move the mapping->get() and menu control handling
> > code out of __uvc_ctrl_get() into a __uvc_ctrl_get_value() function and
> > call it here ?
> > 
> > > +		uvc_ctrl_send_event(handle, ctrl, mapping, value,
> > > +				    V4L2_EVENT_CTRL_CH_VALUE);
> > > +	}
> > > +
> > > +	mutex_unlock(&chain->ctrl_mutex);
> > > +
> > > +free:
> > > +	kfree(data);
> > > +}
> > > +
> > > +void uvc_ctrl_status_event(struct uvc_video_chain *chain,
> > > +			   struct uvc_control *ctrl, u8 *data, size_t len)
> > > +{
> > > +	struct uvc_device *dev = chain->dev;
> > > +	struct uvc_ctrl_work *w = &dev->async_ctrl;
> > > +
> > > +	if (list_empty(&ctrl->info.mappings))
> > > +		return;
> > > +
> > > +	spin_lock(&w->lock);
> > > +	if (w->data)
> > > +		/* A previous event work hasn't run yet, we lose 1 event */
> > > +		kfree(w->data);
> > 
> > I really don't like losing events :/
> > 
> > > +	w->data = kmalloc(len, GFP_ATOMIC);
> > 
> > GFP_ATOMIC allocation isn't very nice either.
> > 
> > How about if we instead delayed resubmitting the status URB until the
> > event is fully processed by the work queue ? That way we wouldn't lose
> > events, we wouldn't need memory allocation in atomic context, and if the
> > work queue becomes a bottleneck we could even queue multiple status URBs
> > and easily add them to a list for processing by the work queue.
> > 
> > > +	if (w->data) {
> > > +		memcpy(w->data, data, len);
> > > +		w->chain = chain;
> > > +		w->ctrl = ctrl;
> > > +		schedule_work(&w->work);
> > > +	}
> > > +	spin_unlock(&w->lock);
> > > +}
> > > +
> > > +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)
> > > +{
> > > +	unsigned int i;
> > > +
> > > 
> > >  	/*
> > >  	
> > >  	 * We can skip sending an event for the slave if the slave
> > >  	 * is being modified in the same transaction.
> > > 
> > > @@ -1255,14 +1360,7 @@ static void uvc_ctrl_send_slave_event(struct
> > > uvc_fh
> > > *handle, 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)
> > > -		changes |= V4L2_EVENT_CTRL_CH_VALUE;
> > > -
> > > -	uvc_ctrl_send_event(handle, ctrl, mapping, val, changes);
> > > +	__uvc_ctrl_send_slave_event(handle, master, slave_id);
> > > 
> > >  }
> > >  
> > >  static void uvc_ctrl_send_events(struct uvc_fh *handle,
> > > 
> > > @@ -1277,6 +1375,10 @@ 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 */
> > 
> > Nitpicking, could you add a period at the end of sentences to match the
> > comment style in the driver ?
> > 
> > > +			continue;
> > > +
> > > 
> > >  		for (j = 0; j < ARRAY_SIZE(mapping->slave_ids); ++j) {
> > >  		
> > >  			if (!mapping->slave_ids[j])
> > >  			
> > >  				break;
> > > 
> > > @@ -1472,9 +1574,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;
> > > 
> > > @@ -1488,6 +1591,25 @@ int uvc_ctrl_set(struct uvc_video_chain *chain,
> > > 
> > >  		return -EINVAL;
> > >  	
> > >  	if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR))
> > >  	
> > >  		return -EACCES;
> > > 
> > > +	if (ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS) {
> > > +		if (ctrl->handle)
> > > +			/*
> > > +			 * We have already sent this control to the camera
> > > +			 * recently and are currently waiting for a completion
> > > +			 * notification. The camera might already have completed
> > > +			 * its processing and is ready to accept a new control
> > > +			 * or it's still busy processing. If we send a new
> > > +			 * instance of this control now, in the former case the
> > > +			 * camera will process this one too and we'll get
> > > +			 * completions for both, but we will only deliver an
> > > +			 * event for one of them back to the user. In the latter
> > > +			 * case the camera will reply with a STALL. It's easier
> > > +			 * and more reliable to return an error now and let the
> > > +			 * user retry.
> > > +			 */
> > > +			return -EBUSY;
> > > +		ctrl->handle = handle;
> > 
> > This part worries me. If the control change event isn't received for any
> > reason (such as a buggy device for instance, or uvc_ctrl_status_event()
> > being called with the previous event not processed yet), the control will
> > stay busy forever.
> > 
> > I see two approaches to fix this. One would be to forward all received
> > control change events to all file handles unconditionally and remove the
> > handle field from the uvc_control structure. Another one would be to add
> > a timeout, storing the time at which the control has been set in the
> > uvc_control structure, and checking whether the time difference exceeds a
> > fixed timeout here. We could also combine the two, replacing the handle
> > field with a timestamp field.
> > 
> > Please keep in mind if trying this out that simply storing jiffies as a
> > timestamp might cause counter overflow issues if the control is set, no
> > event is ever received, and a second control set happens much later after
> > one (or multiple) jiffies wraparounds. We could end up returning -EBUSY
> > incorrectly. That might not be a real issue given the size of the jiffies
> > counter though, I'll let you double-check.
> > 
> > > +	}
> > > 
> > >  	/* Clamp out of range values. */
> > >  	switch (mapping->v4l2_type) {
> > > 
> > > @@ -1612,7 +1734,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;
> > > 
> > > @@ -2158,6 +2282,13 @@ static void uvc_ctrl_init_ctrl(struct uvc_device
> > > *dev, struct uvc_control *ctrl) if (!ctrl->initialized)
> > > 
> > >  		return;
> > > 
> > > +	/* Temporarily abuse DATA_CURRENT buffer to avoid 1 byte allocation 
*/
> > > +	if (!uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id,
> > > +			    dev->intfnum, info->selector,
> > > +			    uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), 1) &&
> > > +	    uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)[0] & 0x10)
> > > +		ctrl->info.flags |= UVC_CTRL_FLAG_ASYNCHRONOUS;
> > 
> > I think this isn't needed anymore as the uvc_ctrl_add_info() function now
> > calls uvc_ctrl_get_flags() to update the flags. Could you please confirm ?
> > 
> > >  	for (; mapping < mend; ++mapping) {
> > >  	
> > >  		if (uvc_entity_match_guid(ctrl->entity, mapping->entity) &&
> > >  		
> > >  		    ctrl->info.selector == mapping->selector)
> > > 
> > > @@ -2173,6 +2304,9 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
> > > 
> > >  	struct uvc_entity *entity;
> > >  	unsigned int i;
> > > 
> > > +	spin_lock_init(&dev->async_ctrl.lock);
> > > +	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 +2375,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 7b710410584a..d1d83aed6a1d
> > > 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,101 @@ 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_ctrl(struct uvc_entity
> > > *entity, +					       u8 selector)
> > 
> > Should this be named uvc_event_entity_find_ctrl() ?
> > 
> > > +{
> > > +	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;
> > > +}
> > > +
> > > +static struct uvc_control *uvc_event_find_ctrl(struct uvc_device *dev,
> > > +					struct uvc_control_status *status,
> > > +					struct uvc_video_chain **chain)
> > > +{
> > > +	list_for_each_entry((*chain), &dev->chains, list) {
> > 
> > Do you need parentheses around *chain ?
> > 
> > > +		struct uvc_entity *entity;
> > > +		struct uvc_control *ctrl;
> > > +
> > > +		list_for_each_entry(entity, &(*chain)->entities, chain) {
> > > +			if (entity->id == status->bOriginator) {
> > 
> > If you invert the check here with
> > 
> > 			if (entity->id == status->bOriginator)
> > 			
> > 				continue;
> > 
> > you'll gain one level of indentation and you might be able to inline
> > uvc_event_entity_ctrl(). Up to you.
> > 
> > > +				ctrl = uvc_event_entity_ctrl(entity,
> > > +							     status->bSelector);
> > > +				/*
> > > +				 * Some buggy cameras send asynchronous Control
> > > +				 * Change events for control, other than the
> > > +				 * ones, that had been changed, even though the
> > > +				 * AutoUpdate flag isn't set for the control.
> > > +				 */
> > 
> > That's lots of commas, I'm not sure what you mean here. Are there cameras
> > that send event for controls that haven't changed ? Or cameras that send
> > events for controls that don't have the auto-update flag set ? Do you
> > know what cameras are affected ?
> > 
> > > +				if (ctrl && (!ctrl->handle ||
> > > +					     ctrl->handle->chain == *chain))
> > > +					return ctrl;
> > > +			}
> > > +		}
> > > +	}
> > > +
> > > +	return NULL;
> > > +}
> > > +
> > > +static void uvc_event_control(struct uvc_device *dev,
> > > +			      struct uvc_control_status *status, int len)
> > 
> > While at it you can make the status pointer const.
> > 
> > >  {
> > > 
> > > -	char *attrs[3] = { "value", "info", "failure" };
> > > +	struct uvc_video_chain *chain;
> > > +	struct uvc_control *ctrl;
> > > +	char *attrs[] = { "value", "info", "failure", "min", "max" };
> > 
> > You can also make these static const (and while at it could you please
> > move
> > them before the other two variables ?).
> > 
> > > -	if (len < 6 || data[2] != 0 || data[4] > 2) {
> > > +	if (len < 6 || status->bEvent != 0 ||
> > > +	    status->bAttribute >= ARRAY_SIZE(attrs)) {
> > > 
> > >  		uvc_trace(UVC_TRACE_STATUS, "Invalid control status event "
> > >  		
> > >  				"received.\n");
> > >  		
> > >  		return;
> > 
> > The min and max events are only defined in UVC 1.5 but I suppose there's
> > no
> > need to check for the UVC version here, it won't hurt to handle them on
> > UVC
> > 1.1 as well as they should not occur.
> > 
> > >  	}
> > >  	
> > >  	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;
> > > +
> > > +	switch (status->bAttribute) {
> > > +	case UVC_CTRL_VALUE_CHANGE:
> > > +		uvc_ctrl_status_event(chain, ctrl, status->bValue, len -
> > > +				      offsetof(struct uvc_control_status, bValue));
> > > +		break;
> > 
> > How about a blank line here ?
> > 
> > > +	case UVC_CTRL_INFO_CHANGE:
> > > +	case UVC_CTRL_FAILURE_CHANGE:
> > > +	case UVC_CTRL_MIN_CHANGE:
> > > +	case UVC_CTRL_MAX_CHANGE:
> > > +		break;
> > > +	}
> > > 
> > >  }
> > >  
> > >  static void uvc_status_complete(struct urb *urb)
> > > 
> > > @@ -139,11 +226,13 @@ static void uvc_status_complete(struct urb *urb)
> > > 
> > >  	if (len > 0) {
> > >  	
> > >  		switch (dev->status[0] & 0x0f) {
> > > 
> > >  		case UVC_STATUS_TYPE_CONTROL:
> > > -			uvc_event_control(dev, dev->status, len);
> > > +			uvc_event_control(dev,
> > > +				(struct uvc_control_status *)dev->status, len);
> > 
> > Is dev->status guaranteed to be aligned properly so that no unaligned
> > access fault can occur when dereferencing memory through the
> > uvc_control_status and uvc_streaming_status structures ? Unaligned access
> > are the reason why UVC descriptors are parsed manually instead of through
> > a structure.
> > 
> > >  			break;
> > >  		
> > >  		case UVC_STATUS_TYPE_STREAMING:
> > > -			uvc_event_streaming(dev, dev->status, len);
> > > +			uvc_event_streaming(dev,
> > > +				(struct uvc_streaming_status *)dev->status, len);
> > > 
> > >  			break;
> > >  		
> > >  		default:
> > > diff --git a/drivers/media/usb/uvc/uvc_v4l2.c
> > > b/drivers/media/usb/uvc/uvc_v4l2.c index 818a4369a51a..55f973eb16eb
> > > 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 6b955e0dd956..483182fe1b4d
> > > 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>
> > > 
> > > @@ -259,6 +260,8 @@ struct uvc_control {
> > > 
> > >  	   initialized:1;
> > >  	
> > >  	u8 *uvc_data;
> > > 
> > > +
> > > +	struct uvc_fh *handle;	/* Used for asynchronous event delivery */
> > > 
> > >  };
> > >  
> > >  struct uvc_format_desc {
> > > 
> > > @@ -603,6 +606,14 @@ struct uvc_device {
> > > 
> > >  	u8 *status;
> > >  	struct input_dev *input;
> > >  	char input_phys[64];
> > > 
> > > +
> > > +	struct uvc_ctrl_work {
> > > +		struct work_struct work;
> > > +		struct uvc_video_chain *chain;
> > > +		struct uvc_control *ctrl;
> > > +		spinlock_t lock;
> > > +		void *data;
> > > +	} async_ctrl;
> > > 
> > >  };
> > >  
> > >  enum uvc_handle_state {
> > > 
> > > @@ -756,6 +767,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);
> > > 
> > > +void uvc_ctrl_status_event(struct uvc_video_chain *chain,
> > > +			   struct uvc_control *ctrl, u8 *data, size_t len);
> > > 
> > >  int uvc_ctrl_begin(struct uvc_video_chain *chain);
> > >  int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback,
> > > 
> > > @@ -773,7 +786,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 020714d2c5bd..f80f05b3c423 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

  reply	other threads:[~2018-04-07 10:50 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-03-23  9:23 [PATCH v7 0/2] uvcvideo: asynchronous controls Laurent Pinchart
2018-03-23  9:24 ` [PATCH v7 1/2] uvcvideo: send a control event when a Control Change interrupt arrives Laurent Pinchart
2018-03-23 10:08   ` Laurent Pinchart
2018-04-07  9:30     ` Guennadi Liakhovetski
2018-04-07 10:50       ` Laurent Pinchart [this message]
2018-04-10 11:31     ` Guennadi Liakhovetski
2018-04-26  9:28       ` Guennadi Liakhovetski
2018-05-07 14:55         ` Laurent Pinchart
2018-05-07 14:51       ` Laurent Pinchart
2018-05-07 15:12         ` Guennadi Liakhovetski
2018-05-07 15:20           ` Laurent Pinchart
2018-05-08 11:05         ` Guennadi Liakhovetski
2018-03-23  9:24 ` [PATCH v7 2/2] uvcvideo: handle control pipe protocol STALLs Laurent Pinchart
2018-04-07 11:20   ` Laurent Pinchart
2018-04-11 12:44     ` Guennadi Liakhovetski
2018-04-20 13:21       ` Guennadi Liakhovetski
2018-05-07 15:12       ` Laurent Pinchart

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=9522794.8ROHdGlsdf@avalon \
    --to=laurent.pinchart@ideasonboard.com \
    --cc=g.liakhovetski@gmx.de \
    --cc=linux-media@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.