Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c92bae75 authored by Felipe Balbi's avatar Felipe Balbi
Browse files

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: default avatarFelipe Balbi <balbi@ti.com>
parent d7577b38
Loading
Loading
Loading
Loading
+20 −9
Original line number Original line Diff line number Diff line
@@ -286,11 +286,12 @@ static int
uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
{
{
	struct uvc_device *uvc = to_uvc(f);
	struct uvc_device *uvc = to_uvc(f);
	struct usb_composite_dev *cdev = f->config->cdev;
	struct v4l2_event v4l2_event;
	struct v4l2_event v4l2_event;
	struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
	struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
	int ret;
	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 (interface == uvc->control_intf) {
		if (alt)
		if (alt)
@@ -299,7 +300,7 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
		if (uvc->state == UVC_STATE_DISCONNECTED) {
		if (uvc->state == UVC_STATE_DISCONNECTED) {
			memset(&v4l2_event, 0, sizeof(v4l2_event));
			memset(&v4l2_event, 0, sizeof(v4l2_event));
			v4l2_event.type = UVC_EVENT_CONNECT;
			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);
			v4l2_event_queue(uvc->vdev, &v4l2_event);


			uvc->state = UVC_STATE_CONNECTED;
			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)
		if (uvc->state != UVC_STATE_STREAMING)
			return 0;
			return 0;


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


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


		if (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,
		ret = config_ep_by_speed(f->config->cdev->gadget,
				&(uvc->func), uvc->video.ep);
				&(uvc->func), uvc->video.ep);
		if (ret)
		if (ret)
			return ret;
			return ret;
		usb_ep_enable(uvc->video.ep);
		usb_ep_enable(uvc->video.ep);
		}
		uvc->video.ep->driver_data = uvc;


		memset(&v4l2_event, 0, sizeof(v4l2_event));
		memset(&v4l2_event, 0, sizeof(v4l2_event));
		v4l2_event.type = UVC_EVENT_STREAMON;
		v4l2_event.type = UVC_EVENT_STREAMON;