Skip to content

Commit

Permalink
usb: gadget: function: uvc: make sure to balance ep enable/disable
Browse files Browse the repository at this point in the history
If a set_alt() to the same alternate setting that's
already selected is received, functions are required
to reset the interface state, this means we must disable
all endpoints and reenable them again.

This is also documented on our kdoc for struct usb_function

* @set_alt: (REQUIRED) Reconfigures altsettings; function drivers may
*	initialize usb_ep.driver data at this time (when it is used).
*	Note that setting an interface to its current altsetting resets
*	interface state, and that all interfaces have a disabled state.

Signed-off-by: Felipe Balbi <balbi@ti.com>
  • Loading branch information
Felipe Balbi committed Oct 20, 2014
1 parent d7577b3 commit c92bae7
Showing 1 changed file with 20 additions and 9 deletions.
29 changes: 20 additions & 9 deletions drivers/usb/gadget/function/f_uvc.c
Original file line number Diff line number Diff line change
Expand Up @@ -286,11 +286,12 @@ static int
uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
{
struct uvc_device *uvc = to_uvc(f);
struct usb_composite_dev *cdev = f->config->cdev;
struct v4l2_event v4l2_event;
struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
int ret;

INFO(f->config->cdev, "uvc_function_set_alt(%u, %u)\n", interface, alt);
INFO(cdev, "uvc_function_set_alt(%u, %u)\n", interface, alt);

if (interface == uvc->control_intf) {
if (alt)
Expand All @@ -299,7 +300,7 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
if (uvc->state == UVC_STATE_DISCONNECTED) {
memset(&v4l2_event, 0, sizeof(v4l2_event));
v4l2_event.type = UVC_EVENT_CONNECT;
uvc_event->speed = f->config->cdev->gadget->speed;
uvc_event->speed = cdev->gadget->speed;
v4l2_event_queue(uvc->vdev, &v4l2_event);

uvc->state = UVC_STATE_CONNECTED;
Expand All @@ -321,8 +322,10 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
if (uvc->state != UVC_STATE_STREAMING)
return 0;

if (uvc->video.ep)
if (uvc->video.ep) {
usb_ep_disable(uvc->video.ep);
uvc->video.ep->driver_data = NULL;
}

memset(&v4l2_event, 0, sizeof(v4l2_event));
v4l2_event.type = UVC_EVENT_STREAMOFF;
Expand All @@ -335,14 +338,22 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
if (uvc->state != UVC_STATE_CONNECTED)
return 0;

if (uvc->video.ep) {
ret = config_ep_by_speed(f->config->cdev->gadget,
&(uvc->func), uvc->video.ep);
if (ret)
return ret;
usb_ep_enable(uvc->video.ep);
if (!uvc->video.ep)
return -EINVAL;

if (uvc->video.ep->driver_data) {
INFO(cdev, "reset UVC\n");
usb_ep_disable(uvc->video.ep);
uvc->video.ep->driver_data = NULL;
}

ret = config_ep_by_speed(f->config->cdev->gadget,
&(uvc->func), uvc->video.ep);
if (ret)
return ret;
usb_ep_enable(uvc->video.ep);
uvc->video.ep->driver_data = uvc;

memset(&v4l2_event, 0, sizeof(v4l2_event));
v4l2_event.type = UVC_EVENT_STREAMON;
v4l2_event_queue(uvc->vdev, &v4l2_event);
Expand Down

0 comments on commit c92bae7

Please sign in to comment.