* [PATCHv2 1/6] media: core: v4l2-async.c: unreg subdev if asc_list is empty
2026-06-16 13:10 [PATCHv2 0/6] media: em28xx: fix lifecycle issues Hans Verkuil
@ 2026-06-16 13:10 ` Hans Verkuil
2026-06-16 15:15 ` Sakari Ailus
2026-06-16 13:10 ` [PATCHv2 2/6] media: em28xx: use v4l2_device release callback Hans Verkuil
` (4 subsequent siblings)
5 siblings, 1 reply; 8+ messages in thread
From: Hans Verkuil @ 2026-06-16 13:10 UTC (permalink / raw)
To: linux-media; +Cc: Mauricio Faria de Oliveira, Sakari Ailus, Hans Verkuil
In v4l2_device_unregister_subdev(), if sd->asc_list is empty,
then v4l2_device_unregister_subdev() is never called, but that
should still happen.
This causes crashes with em28xx that uses tvp5150: that i2c
module uses v4l2_async, but em28xx does not as it predates
v4l2_async.
So if sd->asc_list is empty, then just call
v4l2_device_unregister_subdev().
Fixes: 28a1295795d8 ("media: v4l: async: Allow multiple connections between entities")
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
---
drivers/media/v4l2-core/v4l2-async.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 888a2e213b08..fd0404ce9247 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -897,7 +897,9 @@ void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
kfree(sd->subdev_notifier);
sd->subdev_notifier = NULL;
- if (sd->asc_list.next) {
+ if (list_empty(&sd->asc_list)) {
+ v4l2_device_unregister_subdev(sd);
+ } else {
list_for_each_entry_safe(asc, asc_tmp, &sd->asc_list,
asc_subdev_entry) {
v4l2_async_unbind_subdev_one(asc->notifier, asc);
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCHv2 1/6] media: core: v4l2-async.c: unreg subdev if asc_list is empty
2026-06-16 13:10 ` [PATCHv2 1/6] media: core: v4l2-async.c: unreg subdev if asc_list is empty Hans Verkuil
@ 2026-06-16 15:15 ` Sakari Ailus
0 siblings, 0 replies; 8+ messages in thread
From: Sakari Ailus @ 2026-06-16 15:15 UTC (permalink / raw)
To: Hans Verkuil; +Cc: linux-media, Mauricio Faria de Oliveira
Hi Hans,
On Tue, Jun 16, 2026 at 03:10:27PM +0200, Hans Verkuil wrote:
> In v4l2_device_unregister_subdev(), if sd->asc_list is empty,
> then v4l2_device_unregister_subdev() is never called, but that
> should still happen.
>
> This causes crashes with em28xx that uses tvp5150: that i2c
> module uses v4l2_async, but em28xx does not as it predates
> v4l2_async.
>
> So if sd->asc_list is empty, then just call
> v4l2_device_unregister_subdev().
>
> Fixes: 28a1295795d8 ("media: v4l: async: Allow multiple connections between entities")
> Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
> ---
> drivers/media/v4l2-core/v4l2-async.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
> index 888a2e213b08..fd0404ce9247 100644
> --- a/drivers/media/v4l2-core/v4l2-async.c
> +++ b/drivers/media/v4l2-core/v4l2-async.c
> @@ -897,7 +897,9 @@ void v4l2_async_unregister_subdev(struct v4l2_subdev *sd)
> kfree(sd->subdev_notifier);
> sd->subdev_notifier = NULL;
>
> - if (sd->asc_list.next) {
> + if (list_empty(&sd->asc_list)) {
If v4l2_async_unregister_subdev() without calling
v4l2_async_register_subdev() first, sd->asc_list.next will be NULL. That
case needs to be handled here, too.
> + v4l2_device_unregister_subdev(sd);
> + } else {
> list_for_each_entry_safe(asc, asc_tmp, &sd->asc_list,
> asc_subdev_entry) {
> v4l2_async_unbind_subdev_one(asc->notifier, asc);
--
Kind regards,
Sakari Ailus
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCHv2 2/6] media: em28xx: use v4l2_device release callback
2026-06-16 13:10 [PATCHv2 0/6] media: em28xx: fix lifecycle issues Hans Verkuil
2026-06-16 13:10 ` [PATCHv2 1/6] media: core: v4l2-async.c: unreg subdev if asc_list is empty Hans Verkuil
@ 2026-06-16 13:10 ` Hans Verkuil
2026-06-16 13:10 ` [PATCHv2 3/6] media: em28xx: drop 'users' field Hans Verkuil
` (3 subsequent siblings)
5 siblings, 0 replies; 8+ messages in thread
From: Hans Verkuil @ 2026-06-16 13:10 UTC (permalink / raw)
To: linux-media; +Cc: Mauricio Faria de Oliveira, Sakari Ailus, Hans Verkuil
The em28xx driver creates a lot of video devices, but life-time management
is really bad. Instead use the struct v4l2_device release() callback to
have a single place where memory can be freed once the last user has gone.
Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
---
drivers/media/usb/em28xx/em28xx-video.c | 66 +++++++++++++++----------
drivers/media/usb/em28xx/em28xx.h | 1 -
2 files changed, 41 insertions(+), 26 deletions(-)
diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c
index da0422c65e5f..42f5e7547cc4 100644
--- a/drivers/media/usb/em28xx/em28xx-video.c
+++ b/drivers/media/usb/em28xx/em28xx-video.c
@@ -2275,18 +2275,27 @@ static int radio_s_tuner(struct file *file, void *priv,
}
/*
- * em28xx_free_v4l2() - Free struct em28xx_v4l2
+ * em28xx_free_v4l2() - v4l2_device release callback
*
- * @ref: struct kref for struct em28xx_v4l2
+ * @v4l2_dev: pointer to struct v4l2_device embedded in struct em28xx_v4l2
*
- * Called when all users of struct em28xx_v4l2 are gone
+ * Called by the v4l2 core when the last reference to the v4l2_device is
+ * released. At this point no userspace file handle nor video_device node
+ * keeps the v4l2 instance alive anymore, so it is safe to release all
+ * v4l2-related resources and drop the em28xx device reference taken when
+ * the v4l2 extension was initialized.
*/
-static void em28xx_free_v4l2(struct kref *ref)
+static void em28xx_free_v4l2(struct v4l2_device *v4l2_dev)
{
- struct em28xx_v4l2 *v4l2 = container_of(ref, struct em28xx_v4l2, ref);
+ struct em28xx_v4l2 *v4l2 =
+ container_of(v4l2_dev, struct em28xx_v4l2, v4l2_dev);
+ struct em28xx *dev = v4l2->dev;
- v4l2->dev->v4l2 = NULL;
+ v4l2_ctrl_handler_free(&v4l2->ctrl_handler);
+ v4l2_device_unregister(v4l2_dev);
+ dev->v4l2 = NULL;
kfree(v4l2);
+ kref_put(&dev->ref, em28xx_free_device);
}
/*
@@ -2354,8 +2363,6 @@ static int em28xx_v4l2_open(struct file *filp)
v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_radio);
}
- kref_get(&dev->ref);
- kref_get(&v4l2->ref);
v4l2->users++;
mutex_unlock(&dev->lock);
@@ -2411,14 +2418,14 @@ static int em28xx_v4l2_fini(struct em28xx *dev)
video_unregister_device(&v4l2->vdev);
}
- v4l2_ctrl_handler_free(&v4l2->ctrl_handler);
- v4l2_device_unregister(&v4l2->v4l2_dev);
-
- kref_put(&v4l2->ref, em28xx_free_v4l2);
-
mutex_unlock(&dev->lock);
- kref_put(&dev->ref, em28xx_free_device);
+ /*
+ * Drop the initial reference taken at v4l2_device_register() time.
+ * The em28xx_free_v4l2() release callback will be invoked once all
+ * userspace file handles to the video device nodes are closed.
+ */
+ v4l2_device_put(&v4l2->v4l2_dev);
return 0;
}
@@ -2490,9 +2497,7 @@ static int em28xx_v4l2_close(struct file *filp)
exit:
v4l2->users--;
- kref_put(&v4l2->ref, em28xx_free_v4l2);
mutex_unlock(&dev->lock);
- kref_put(&dev->ref, em28xx_free_device);
return 0;
}
@@ -2711,7 +2716,6 @@ static int em28xx_v4l2_init(struct em28xx *dev)
mutex_unlock(&dev->lock);
return -ENOMEM;
}
- kref_init(&v4l2->ref);
v4l2->dev = dev;
dev->v4l2 = v4l2;
@@ -2722,9 +2726,21 @@ static int em28xx_v4l2_init(struct em28xx *dev)
if (ret < 0) {
dev_err(&dev->intf->dev,
"Call to v4l2_device_register() failed!\n");
- goto err;
+ dev->v4l2 = NULL;
+ kfree(v4l2);
+ mutex_unlock(&dev->lock);
+ return ret;
}
+ /*
+ * From this point on, em28xx_free_v4l2() will be used to release
+ * v4l2-related resources when the v4l2_device refcount reaches
+ * zero. Take a reference to the em28xx device so that it cannot
+ * be freed before the v4l2 instance is released.
+ */
+ v4l2->v4l2_dev.release = em28xx_free_v4l2;
+ kref_get(&dev->ref);
+
hdl = &v4l2->ctrl_handler;
v4l2_ctrl_handler_init(hdl, 9);
v4l2->v4l2_dev.ctrl_handler = hdl;
@@ -3048,8 +3064,6 @@ static int em28xx_v4l2_init(struct em28xx *dev)
dev_info(&dev->intf->dev,
"V4L2 extension successfully initialized\n");
- kref_get(&dev->ref);
-
mutex_unlock(&dev->lock);
return 0;
@@ -3073,12 +3087,14 @@ static int em28xx_v4l2_init(struct em28xx *dev)
video_unregister_device(&v4l2->vdev);
}
- v4l2_ctrl_handler_free(&v4l2->ctrl_handler);
- v4l2_device_unregister(&v4l2->v4l2_dev);
-err:
- dev->v4l2 = NULL;
- kref_put(&v4l2->ref, em28xx_free_v4l2);
mutex_unlock(&dev->lock);
+
+ /*
+ * Drop the initial reference. em28xx_free_v4l2() will be called
+ * once the last video_device node release has decremented the
+ * v4l2_device refcount to zero.
+ */
+ v4l2_device_put(&v4l2->v4l2_dev);
return ret;
}
diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h
index 21c912403efc..90b83e4598ac 100644
--- a/drivers/media/usb/em28xx/em28xx.h
+++ b/drivers/media/usb/em28xx/em28xx.h
@@ -557,7 +557,6 @@ struct em28xx_eeprom {
#define EM28XX_RESOURCE_VBI 0x02
struct em28xx_v4l2 {
- struct kref ref;
struct em28xx *dev;
struct v4l2_device v4l2_dev;
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCHv2 3/6] media: em28xx: drop 'users' field
2026-06-16 13:10 [PATCHv2 0/6] media: em28xx: fix lifecycle issues Hans Verkuil
2026-06-16 13:10 ` [PATCHv2 1/6] media: core: v4l2-async.c: unreg subdev if asc_list is empty Hans Verkuil
2026-06-16 13:10 ` [PATCHv2 2/6] media: em28xx: use v4l2_device release callback Hans Verkuil
@ 2026-06-16 13:10 ` Hans Verkuil
2026-06-16 13:10 ` [PATCHv2 4/6] media: em28xx: use vb2_video_unregister_device Hans Verkuil
` (2 subsequent siblings)
5 siblings, 0 replies; 8+ messages in thread
From: Hans Verkuil @ 2026-06-16 13:10 UTC (permalink / raw)
To: linux-media; +Cc: Mauricio Faria de Oliveira, Sakari Ailus, Hans Verkuil
Drop the em28xx_v4l2 'users' field, use v4l2_fh_is_singular_file()
instead.
Assisted-by: Claude:claude-opus-4-7
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
---
drivers/media/usb/em28xx/em28xx-video.c | 18 +++++++-----------
drivers/media/usb/em28xx/em28xx.h | 1 -
2 files changed, 7 insertions(+), 12 deletions(-)
diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c
index 42f5e7547cc4..6c726764a5f3 100644
--- a/drivers/media/usb/em28xx/em28xx-video.c
+++ b/drivers/media/usb/em28xx/em28xx-video.c
@@ -2332,9 +2332,8 @@ static int em28xx_v4l2_open(struct file *filp)
return -ENODEV;
}
- em28xx_videodbg("open dev=%s type=%s users=%d\n",
- video_device_node_name(vdev), v4l2_type_names[fh_type],
- v4l2->users);
+ em28xx_videodbg("open dev=%s type=%s\n",
+ video_device_node_name(vdev), v4l2_type_names[fh_type]);
ret = v4l2_fh_open(filp);
if (ret) {
@@ -2345,7 +2344,7 @@ static int em28xx_v4l2_open(struct file *filp)
return ret;
}
- if (v4l2->users == 0) {
+ if (v4l2_fh_is_singular_file(filp)) {
em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
if (vdev->vfl_type != VFL_TYPE_RADIO)
@@ -2363,8 +2362,6 @@ static int em28xx_v4l2_open(struct file *filp)
v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_radio);
}
- v4l2->users++;
-
mutex_unlock(&dev->lock);
return 0;
@@ -2467,13 +2464,13 @@ static int em28xx_v4l2_close(struct file *filp)
struct em28xx_v4l2 *v4l2 = dev->v4l2;
struct usb_device *udev = interface_to_usbdev(dev->intf);
int err;
+ bool last_user;
- em28xx_videodbg("users=%d\n", v4l2->users);
-
- vb2_fop_release(filp);
mutex_lock(&dev->lock);
+ last_user = v4l2_fh_is_singular_file(filp);
+ _vb2_fop_release(filp, NULL);
- if (v4l2->users == 1) {
+ if (last_user) {
/* No sense to try to write to the device */
if (dev->disconnected)
goto exit;
@@ -2496,7 +2493,6 @@ static int em28xx_v4l2_close(struct file *filp)
}
exit:
- v4l2->users--;
mutex_unlock(&dev->lock);
return 0;
diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h
index 90b83e4598ac..f68159fab345 100644
--- a/drivers/media/usb/em28xx/em28xx.h
+++ b/drivers/media/usb/em28xx/em28xx.h
@@ -580,7 +580,6 @@ struct em28xx_v4l2 {
int sensor_yres;
int sensor_xtal;
- int users; /* user count for exclusive use */
int streaming_users; /* number of actively streaming users */
u32 frequency; /* selected tuner frequency */
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCHv2 4/6] media: em28xx: use vb2_video_unregister_device
2026-06-16 13:10 [PATCHv2 0/6] media: em28xx: fix lifecycle issues Hans Verkuil
` (2 preceding siblings ...)
2026-06-16 13:10 ` [PATCHv2 3/6] media: em28xx: drop 'users' field Hans Verkuil
@ 2026-06-16 13:10 ` Hans Verkuil
2026-06-16 13:10 ` [PATCHv2 5/6] media: em28xx: dev_info->pr_info since dev has been freed Hans Verkuil
2026-06-16 13:10 ` [PATCHv2 6/6] media: em28xx: requeue buffers if start_streaming fails Hans Verkuil
5 siblings, 0 replies; 8+ messages in thread
From: Hans Verkuil @ 2026-06-16 13:10 UTC (permalink / raw)
To: linux-media; +Cc: Mauricio Faria de Oliveira, Sakari Ailus, Hans Verkuil
Use vb2_video_unregister_device instead of video_unregister_device
to ensure any streaming is correctly stopped at unregister time.
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
---
drivers/media/usb/em28xx/em28xx-video.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c
index 6c726764a5f3..e4554d015944 100644
--- a/drivers/media/usb/em28xx/em28xx-video.c
+++ b/drivers/media/usb/em28xx/em28xx-video.c
@@ -2402,17 +2402,17 @@ static int em28xx_v4l2_fini(struct em28xx *dev)
if (video_is_registered(&v4l2->radio_dev)) {
dev_info(&dev->intf->dev, "V4L2 device %s deregistered\n",
video_device_node_name(&v4l2->radio_dev));
- video_unregister_device(&v4l2->radio_dev);
+ vb2_video_unregister_device(&v4l2->radio_dev);
}
if (video_is_registered(&v4l2->vbi_dev)) {
dev_info(&dev->intf->dev, "V4L2 device %s deregistered\n",
video_device_node_name(&v4l2->vbi_dev));
- video_unregister_device(&v4l2->vbi_dev);
+ vb2_video_unregister_device(&v4l2->vbi_dev);
}
if (video_is_registered(&v4l2->vdev)) {
dev_info(&dev->intf->dev, "V4L2 device %s deregistered\n",
video_device_node_name(&v4l2->vdev));
- video_unregister_device(&v4l2->vdev);
+ vb2_video_unregister_device(&v4l2->vdev);
}
mutex_unlock(&dev->lock);
@@ -3068,19 +3068,19 @@ static int em28xx_v4l2_init(struct em28xx *dev)
dev_info(&dev->intf->dev,
"V4L2 device %s deregistered\n",
video_device_node_name(&v4l2->radio_dev));
- video_unregister_device(&v4l2->radio_dev);
+ vb2_video_unregister_device(&v4l2->radio_dev);
}
if (video_is_registered(&v4l2->vbi_dev)) {
dev_info(&dev->intf->dev,
"V4L2 device %s deregistered\n",
video_device_node_name(&v4l2->vbi_dev));
- video_unregister_device(&v4l2->vbi_dev);
+ vb2_video_unregister_device(&v4l2->vbi_dev);
}
if (video_is_registered(&v4l2->vdev)) {
dev_info(&dev->intf->dev,
"V4L2 device %s deregistered\n",
video_device_node_name(&v4l2->vdev));
- video_unregister_device(&v4l2->vdev);
+ vb2_video_unregister_device(&v4l2->vdev);
}
mutex_unlock(&dev->lock);
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCHv2 5/6] media: em28xx: dev_info->pr_info since dev has been freed
2026-06-16 13:10 [PATCHv2 0/6] media: em28xx: fix lifecycle issues Hans Verkuil
` (3 preceding siblings ...)
2026-06-16 13:10 ` [PATCHv2 4/6] media: em28xx: use vb2_video_unregister_device Hans Verkuil
@ 2026-06-16 13:10 ` Hans Verkuil
2026-06-16 13:10 ` [PATCHv2 6/6] media: em28xx: requeue buffers if start_streaming fails Hans Verkuil
5 siblings, 0 replies; 8+ messages in thread
From: Hans Verkuil @ 2026-06-16 13:10 UTC (permalink / raw)
To: linux-media; +Cc: Mauricio Faria de Oliveira, Sakari Ailus, Hans Verkuil
In em28xx_free_device() dev_info passed &dev->intf->dev,
but that device can be freed already.
Just use pr_info instead.
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
---
drivers/media/usb/em28xx/em28xx-cards.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c
index fbfb74eab475..4d1e48c86ce8 100644
--- a/drivers/media/usb/em28xx/em28xx-cards.c
+++ b/drivers/media/usb/em28xx/em28xx-cards.c
@@ -3758,7 +3758,7 @@ void em28xx_free_device(struct kref *ref)
{
struct em28xx *dev = kref_to_dev(ref);
- dev_info(&dev->intf->dev, "Freeing device\n");
+ pr_info("%s: Freeing device\n", dev->name);
if (!dev->disconnected)
em28xx_release_resources(dev);
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCHv2 6/6] media: em28xx: requeue buffers if start_streaming fails
2026-06-16 13:10 [PATCHv2 0/6] media: em28xx: fix lifecycle issues Hans Verkuil
` (4 preceding siblings ...)
2026-06-16 13:10 ` [PATCHv2 5/6] media: em28xx: dev_info->pr_info since dev has been freed Hans Verkuil
@ 2026-06-16 13:10 ` Hans Verkuil
5 siblings, 0 replies; 8+ messages in thread
From: Hans Verkuil @ 2026-06-16 13:10 UTC (permalink / raw)
To: linux-media; +Cc: Mauricio Faria de Oliveira, Sakari Ailus, Hans Verkuil
If start_streaming fails, then all queued buffers must be
returned to vb2 in state QUEUED.
Otherwise it will trigger a WARN_ON.
Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org>
---
drivers/media/usb/em28xx/em28xx-video.c | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c
index e4554d015944..c418add65bb5 100644
--- a/drivers/media/usb/em28xx/em28xx-video.c
+++ b/drivers/media/usb/em28xx/em28xx-video.c
@@ -1213,6 +1213,9 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count)
{
struct em28xx *dev = vb2_get_drv_priv(vq);
struct em28xx_v4l2 *v4l2 = dev->v4l2;
+ struct em28xx_dmaqueue *dmaq = vq->type == V4L2_BUF_TYPE_VBI_CAPTURE ?
+ &dev->vbiq : &dev->vidq;
+ unsigned long flags = 0;
struct v4l2_frequency f;
struct v4l2_fh *owner;
int rc = 0;
@@ -1227,7 +1230,7 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count)
*/
rc = res_get(dev, vq->type);
if (rc)
- return rc;
+ goto exit;
if (v4l2->streaming_users == 0) {
/* First active streaming user, so allocate all the URBs */
@@ -1250,7 +1253,7 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count)
em28xx_urb_data_copy);
if (rc < 0) {
res_free(dev, vq->type);
- return rc;
+ goto exit;
}
/*
@@ -1275,7 +1278,18 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count)
}
v4l2->streaming_users++;
+ return 0;
+exit:
+ spin_lock_irqsave(&dev->slock, flags);
+ while (!list_empty(&dmaq->active)) {
+ struct em28xx_buffer *buf;
+
+ buf = list_entry(dmaq->active.next, struct em28xx_buffer, list);
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
+ }
+ spin_unlock_irqrestore(&dev->slock, flags);
return rc;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread