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: Pavankumar Kondeti <quic_pkondeti@quicinc.com>
This commit is contained in:
1 parent
20f6203c30
commit
4803db7a0a
5 files changed
+38
-3
No files matched your search
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user