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:
parent
d7577b3892
commit
c92bae7537
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue