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

Commit 4803db7a authored by Pavankumar Kondeti's avatar Pavankumar Kondeti Committed by Gerrit - the friendly Code Review server
Browse files

usb: gadget: f_uvc: Fix unbind issues



Currently user space application which has v4l2 file handle has
no way to know that uvc function is unbounded from the composition.
Later when it tries to release the file handle, we end up accessing
stale gadget side data structures.

Address this issue by sending an uevent upon unbind. If the user space
application has subscribed to this uevent, block the unbind until
the last v4l2 file handle is released.

The function drivers which use function activate/deactivate method
to delay enumeration has no way to keep the de-activations count sync
across composition switch. Reset the cdev->deactivations to start
from the fresh when a new composition is selected.

Change-Id: Id389cf8cc59aba6dbeca7c1fb52a990a25c084de
Signed-off-by: default avatarPavankumar Kondeti <quic_pkondeti@quicinc.com>
parent 20f6203c
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -2316,6 +2316,11 @@ int composite_dev_prepare(struct usb_composite_driver *composite,
	 * drivers will zero ep->driver_data.
	 */
	usb_ep_autoconfig_reset(gadget);
	/*
	 * Reset deactivations as it is not possible to synchronize
	 * between bind/unbind and open/close by user space application.
	 */
	cdev->deactivations = 0;
	return 0;
fail_dev:
	kfree(cdev->req->buf);
+20 −1
Original line number Diff line number Diff line
@@ -423,6 +423,14 @@ static ssize_t function_name_show(struct device *dev,

static DEVICE_ATTR_RO(function_name);

static void uvc_video_device_release(struct video_device *vdev)
{
	struct uvc_device *uvc = container_of(vdev, struct uvc_device, vdev);

	memset(vdev, 0, sizeof(*vdev));
	complete(&uvc->unbind_ok);
}

static int
uvc_register_video(struct uvc_device *uvc)
{
@@ -433,7 +441,7 @@ uvc_register_video(struct uvc_device *uvc)
	uvc->vdev.v4l2_dev = &uvc->v4l2_dev;
	uvc->vdev.fops = &uvc_v4l2_fops;
	uvc->vdev.ioctl_ops = &uvc_v4l2_ioctl_ops;
	uvc->vdev.release = video_device_release_empty;
	uvc->vdev.release = uvc_video_device_release;
	uvc->vdev.vfl_dir = VFL_DIR_TX;
	uvc->vdev.lock = &uvc->video.mutex;
	strlcpy(uvc->vdev.name, cdev->gadget->name, sizeof(uvc->vdev.name));
@@ -769,6 +777,8 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
		goto error;
	}

	reinit_completion(&uvc->unbind_ok);
	uvc->wait_for_close = false;
	return 0;

error:
@@ -897,13 +907,21 @@ static void uvc_unbind(struct usb_configuration *c, struct usb_function *f)
{
	struct usb_composite_dev *cdev = c->cdev;
	struct uvc_device *uvc = to_uvc(f);
	struct v4l2_event v4l2_event;

	INFO(cdev, "%s\n", __func__);

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

	device_remove_file(&uvc->vdev.dev, &dev_attr_function_name);
	video_unregister_device(&uvc->vdev);
	v4l2_device_unregister(&uvc->v4l2_dev);

	if (uvc->wait_for_close)
		wait_for_completion(&uvc->unbind_ok);

	usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
	kfree(uvc->control_buf);

@@ -920,6 +938,7 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
	if (uvc == NULL)
		return ERR_PTR(-ENOMEM);

	init_completion(&uvc->unbind_ok);
	mutex_init(&uvc->video.mutex);
	uvc->state = UVC_STATE_DISCONNECTED;
	opts = fi_to_f_uvc_opts(fi);
+3 −0
Original line number Diff line number Diff line
@@ -133,6 +133,9 @@ struct uvc_device {
	/* Events */
	unsigned int event_length;
	unsigned int event_setup_out : 1;

	bool wait_for_close;
	struct completion unbind_ok;
};

static inline struct uvc_device *to_uvc(struct usb_function *f)
+8 −1
Original line number Diff line number Diff line
@@ -229,10 +229,17 @@ static int
uvc_v4l2_subscribe_event(struct v4l2_fh *fh,
			 const struct v4l2_event_subscription *sub)
{
	struct uvc_device *uvc = video_get_drvdata(fh->vdev);
	int ret;

	if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST)
		return -EINVAL;

	return v4l2_event_subscribe(fh, sub, 2, NULL);
	ret = v4l2_event_subscribe(fh, sub, 2, NULL);
	if (!ret && sub->type == UVC_EVENT_UNBIND)
		uvc->wait_for_close = true;

	return ret;
}

static int
+2 −1
Original line number Diff line number Diff line
@@ -19,7 +19,8 @@
#define UVC_EVENT_STREAMOFF		(V4L2_EVENT_PRIVATE_START + 3)
#define UVC_EVENT_SETUP			(V4L2_EVENT_PRIVATE_START + 4)
#define UVC_EVENT_DATA			(V4L2_EVENT_PRIVATE_START + 5)
#define UVC_EVENT_LAST			(V4L2_EVENT_PRIVATE_START + 5)
#define UVC_EVENT_UNBIND		(V4L2_EVENT_PRIVATE_START + 6)
#define UVC_EVENT_LAST			(V4L2_EVENT_PRIVATE_START + 6)

struct uvc_request_data {
	__s32 length;