media: vivid: fix 'disconnect' error injection
authorHans Verkuil <hverkuil-cisco@xs4all.nl>
Tue, 1 Dec 2020 12:44:40 +0000 (13:44 +0100)
committerMauro Carvalho Chehab <mchehab+huawei@kernel.org>
Mon, 7 Dec 2020 14:07:51 +0000 (15:07 +0100)
The 'disconnect' error injection functionality suffered from bit rot.

New device nodes were added without updating vivid_user_gen_s_ctrl(), so
that function had to be updated for the new device nodes.

Also, vivid didn't check if specific device nodes were actually ever
created, so the vivid_is_last_user() would fail on that (it would return
true instead of false in that case).

Finally, selecting Disconnect, then unbind the vivid driver would fail
since the remove() would think that the device nodes were already
unregistered. Keep track of whether disconnect was pressed and re-register
the device nodes in remove() before doing the real unregister.

[hverkuil: unsigned uses -> unsigned int uses]

Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
drivers/media/test-drivers/vivid/vivid-core.c
drivers/media/test-drivers/vivid/vivid-core.h
drivers/media/test-drivers/vivid/vivid-ctrls.c

index aa8d350..0dc65ef 100644 (file)
@@ -547,11 +547,13 @@ static int vivid_s_fmt_cap_mplane(struct file *file, void *priv,
        return vidioc_s_fmt_vid_cap_mplane(file, priv, f);
 }
 
-static bool vivid_is_in_use(struct video_device *vdev)
+static bool vivid_is_in_use(bool valid, struct video_device *vdev)
 {
        unsigned long flags;
        bool res;
 
+       if (!valid)
+               return false;
        spin_lock_irqsave(&vdev->fh_lock, flags);
        res = !list_empty(&vdev->fh_list);
        spin_unlock_irqrestore(&vdev->fh_lock, flags);
@@ -560,20 +562,46 @@ static bool vivid_is_in_use(struct video_device *vdev)
 
 static bool vivid_is_last_user(struct vivid_dev *dev)
 {
-       unsigned uses = vivid_is_in_use(&dev->vid_cap_dev) +
-                       vivid_is_in_use(&dev->vid_out_dev) +
-                       vivid_is_in_use(&dev->vbi_cap_dev) +
-                       vivid_is_in_use(&dev->vbi_out_dev) +
-                       vivid_is_in_use(&dev->sdr_cap_dev) +
-                       vivid_is_in_use(&dev->radio_rx_dev) +
-                       vivid_is_in_use(&dev->radio_tx_dev) +
-                       vivid_is_in_use(&dev->meta_cap_dev) +
-                       vivid_is_in_use(&dev->meta_out_dev) +
-                       vivid_is_in_use(&dev->touch_cap_dev);
+       unsigned int uses =
+               vivid_is_in_use(dev->has_vid_cap, &dev->vid_cap_dev) +
+               vivid_is_in_use(dev->has_vid_out, &dev->vid_out_dev) +
+               vivid_is_in_use(dev->has_vbi_cap, &dev->vbi_cap_dev) +
+               vivid_is_in_use(dev->has_vbi_out, &dev->vbi_out_dev) +
+               vivid_is_in_use(dev->has_radio_rx, &dev->radio_rx_dev) +
+               vivid_is_in_use(dev->has_radio_tx, &dev->radio_tx_dev) +
+               vivid_is_in_use(dev->has_sdr_cap, &dev->sdr_cap_dev) +
+               vivid_is_in_use(dev->has_meta_cap, &dev->meta_cap_dev) +
+               vivid_is_in_use(dev->has_meta_out, &dev->meta_out_dev) +
+               vivid_is_in_use(dev->has_touch_cap, &dev->touch_cap_dev);
 
        return uses == 1;
 }
 
+static void vivid_reconnect(struct vivid_dev *dev)
+{
+       if (dev->has_vid_cap)
+               set_bit(V4L2_FL_REGISTERED, &dev->vid_cap_dev.flags);
+       if (dev->has_vid_out)
+               set_bit(V4L2_FL_REGISTERED, &dev->vid_out_dev.flags);
+       if (dev->has_vbi_cap)
+               set_bit(V4L2_FL_REGISTERED, &dev->vbi_cap_dev.flags);
+       if (dev->has_vbi_out)
+               set_bit(V4L2_FL_REGISTERED, &dev->vbi_out_dev.flags);
+       if (dev->has_radio_rx)
+               set_bit(V4L2_FL_REGISTERED, &dev->radio_rx_dev.flags);
+       if (dev->has_radio_tx)
+               set_bit(V4L2_FL_REGISTERED, &dev->radio_tx_dev.flags);
+       if (dev->has_sdr_cap)
+               set_bit(V4L2_FL_REGISTERED, &dev->sdr_cap_dev.flags);
+       if (dev->has_meta_cap)
+               set_bit(V4L2_FL_REGISTERED, &dev->meta_cap_dev.flags);
+       if (dev->has_meta_out)
+               set_bit(V4L2_FL_REGISTERED, &dev->meta_out_dev.flags);
+       if (dev->has_touch_cap)
+               set_bit(V4L2_FL_REGISTERED, &dev->touch_cap_dev.flags);
+       dev->disconnect_error = false;
+}
+
 static int vivid_fop_release(struct file *file)
 {
        struct vivid_dev *dev = video_drvdata(file);
@@ -581,23 +609,15 @@ static int vivid_fop_release(struct file *file)
 
        mutex_lock(&dev->mutex);
        if (!no_error_inj && v4l2_fh_is_singular_file(file) &&
-           !video_is_registered(vdev) && vivid_is_last_user(dev)) {
+           dev->disconnect_error && !video_is_registered(vdev) &&
+           vivid_is_last_user(dev)) {
                /*
                 * I am the last user of this driver, and a disconnect
                 * was forced (since this video_device is unregistered),
                 * so re-register all video_device's again.
                 */
                v4l2_info(&dev->v4l2_dev, "reconnect\n");
-               set_bit(V4L2_FL_REGISTERED, &dev->vid_cap_dev.flags);
-               set_bit(V4L2_FL_REGISTERED, &dev->vid_out_dev.flags);
-               set_bit(V4L2_FL_REGISTERED, &dev->vbi_cap_dev.flags);
-               set_bit(V4L2_FL_REGISTERED, &dev->vbi_out_dev.flags);
-               set_bit(V4L2_FL_REGISTERED, &dev->sdr_cap_dev.flags);
-               set_bit(V4L2_FL_REGISTERED, &dev->radio_rx_dev.flags);
-               set_bit(V4L2_FL_REGISTERED, &dev->radio_tx_dev.flags);
-               set_bit(V4L2_FL_REGISTERED, &dev->meta_cap_dev.flags);
-               set_bit(V4L2_FL_REGISTERED, &dev->meta_out_dev.flags);
-               set_bit(V4L2_FL_REGISTERED, &dev->touch_cap_dev.flags);
+               vivid_reconnect(dev);
        }
        mutex_unlock(&dev->mutex);
        if (file->private_data == dev->overlay_cap_owner)
@@ -1968,6 +1988,8 @@ static int vivid_remove(struct platform_device *pdev)
                if (!dev)
                        continue;
 
+               if (dev->disconnect_error)
+                       vivid_reconnect(dev);
 #ifdef CONFIG_MEDIA_CONTROLLER
                media_device_unregister(&dev->mdev);
 #endif
index 99e69b8..9c2d147 100644 (file)
@@ -303,6 +303,7 @@ struct vivid_dev {
        struct fb_fix_screeninfo        fb_fix;
 
        /* Error injection */
+       bool                            disconnect_error;
        bool                            queue_setup_error;
        bool                            buf_prepare_error;
        bool                            start_streaming_error;
index 3341305..11e3b56 100644 (file)
@@ -107,14 +107,27 @@ static int vivid_user_gen_s_ctrl(struct v4l2_ctrl *ctrl)
        switch (ctrl->id) {
        case VIVID_CID_DISCONNECT:
                v4l2_info(&dev->v4l2_dev, "disconnect\n");
-               clear_bit(V4L2_FL_REGISTERED, &dev->vid_cap_dev.flags);
-               clear_bit(V4L2_FL_REGISTERED, &dev->vid_out_dev.flags);
-               clear_bit(V4L2_FL_REGISTERED, &dev->vbi_cap_dev.flags);
-               clear_bit(V4L2_FL_REGISTERED, &dev->vbi_out_dev.flags);
-               clear_bit(V4L2_FL_REGISTERED, &dev->sdr_cap_dev.flags);
-               clear_bit(V4L2_FL_REGISTERED, &dev->radio_rx_dev.flags);
-               clear_bit(V4L2_FL_REGISTERED, &dev->radio_tx_dev.flags);
-               clear_bit(V4L2_FL_REGISTERED, &dev->meta_cap_dev.flags);
+               dev->disconnect_error = true;
+               if (dev->has_vid_cap)
+                       clear_bit(V4L2_FL_REGISTERED, &dev->vid_cap_dev.flags);
+               if (dev->has_vid_out)
+                       clear_bit(V4L2_FL_REGISTERED, &dev->vid_out_dev.flags);
+               if (dev->has_vbi_cap)
+                       clear_bit(V4L2_FL_REGISTERED, &dev->vbi_cap_dev.flags);
+               if (dev->has_vbi_out)
+                       clear_bit(V4L2_FL_REGISTERED, &dev->vbi_out_dev.flags);
+               if (dev->has_radio_rx)
+                       clear_bit(V4L2_FL_REGISTERED, &dev->radio_rx_dev.flags);
+               if (dev->has_radio_tx)
+                       clear_bit(V4L2_FL_REGISTERED, &dev->radio_tx_dev.flags);
+               if (dev->has_sdr_cap)
+                       clear_bit(V4L2_FL_REGISTERED, &dev->sdr_cap_dev.flags);
+               if (dev->has_meta_cap)
+                       clear_bit(V4L2_FL_REGISTERED, &dev->meta_cap_dev.flags);
+               if (dev->has_meta_out)
+                       clear_bit(V4L2_FL_REGISTERED, &dev->meta_out_dev.flags);
+               if (dev->has_touch_cap)
+                       clear_bit(V4L2_FL_REGISTERED, &dev->touch_cap_dev.flags);
                break;
        case VIVID_CID_BUTTON:
                dev->button_pressed = 30;