usb: gadget: function: uvc: make sure to balance ep enable/disable

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>
This commit is contained in:
Felipe Balbi 2014-09-29 09:20:35 -05:00
parent d7577b3892
commit c92bae7537
1 changed files with 20 additions and 9 deletions

View File

@ -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)
@ -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;
@ -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;
@ -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);