[PATCH v2] media: s2255: cancel the in-flight firmware URB before reloading

From: YuanZhe Liu

Date: Thu Oct 29 2026 - 06:59:22 EST


The driver uses a single URB to upload the firmware. s2255_timer() submits
it one second after s2255_fwload_start() filled in the first chunk, and
s2255_fwchunk_complete() resubmits it for each following chunk.

When the transfer does not complete within S2255_LOAD_TIMEOUT, s2255_open()
stops waiting, sets the firmware state to S2255_FW_FAILED and returns
-EAGAIN. The next open() calls s2255_fwload_start() again, which refills
the URB and arms the timer while the previous transfer may still be owned
by the USB core.

One second later s2255_timer() submits that URB again:

URB 00000000b9ed398a submitted while active
WARNING: drivers/usb/core/urb.c:379 at usb_submit_urb

usb_submit_urb() returns -EBUSY, the firmware state is set to
S2255_FW_FAILED and the device stays unusable. Rewriting the fields of a
URB that is still in flight is unsafe as well.

Cancel the outstanding transfer and the pending timer before reusing the
URB, and serialize restart attempts so that two open() calls cannot refill
the URB while another attempt is running. A killed transfer completes with
-ENOENT, so do not report it as a transfer error.

Fixes: 38f993ad8b1f ("V4L/DVB (8125): This driver adds support for the Sensoray 2255 devices.")
Cc: stable@xxxxxxxxxxxxxxx
Co-developed-by: Bin Yu <byu@xxxxxxxxxxxxx>
Signed-off-by: Bin Yu <byu@xxxxxxxxxxxxx>
Co-developed-by: Cheng Wen <wencheng@xxxxxxxxxxxxx>
Signed-off-by: Cheng Wen <wencheng@xxxxxxxxxxxxx>
Signed-off-by: YuanZhe Liu <25031212351@xxxxxxxxxxxxxxxxx>
---
v2:
- Serialize firmware restart attempts with a mutex so that concurrent
open() calls cannot refill the shared URB while another restart is in
progress.
---
drivers/media/usb/s2255/s2255drv.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c
index 15012d73975c..ba717cdf31ad 100644
--- a/drivers/media/usb/s2255/s2255drv.c
+++ b/drivers/media/usb/s2255/s2255drv.c
@@ -179,6 +179,7 @@ struct s2255_fw {
int fw_size;
struct urb *fw_urb;
atomic_t fw_state;
+ struct mutex fw_lock; /* serializes firmware restarts */
void *pfw_data;
wait_queue_head_t wait_fw;
const struct firmware *fw;
@@ -490,7 +491,9 @@ static void s2255_fwchunk_complete(struct urb *urb)
int len;

if (urb->status) {
- dev_err(&udev->dev, "URB failed with status %d\n", urb->status);
+ if (urb->status != -ENOENT)
+ dev_err(&udev->dev, "URB failed with status %d\n",
+ urb->status);
atomic_set(&data->fw_state, S2255_FW_FAILED);
/* wake up anything waiting for the firmware */
wake_up(&data->wait_fw);
@@ -1502,6 +1505,7 @@ static void s2255_destroy(struct s2255_dev *dev)
}
release_firmware(dev->fw_data->fw);
kfree(dev->fw_data->pfw_data);
+ mutex_destroy(&dev->fw_data->fw_lock);
kfree(dev->fw_data);
/* reset the DSP so firmware can be reloaded next time */
s2255_reset_dsppower(dev);
@@ -2187,6 +2191,16 @@ static void s2255_stop_readpipe(struct s2255_dev *dev)

static void s2255_fwload_start(struct s2255_dev *dev)
{
+ mutex_lock(&dev->fw_data->fw_lock);
+ /*
+ * A previous load attempt may still have a transfer in flight, e.g.
+ * when s2255_open() timed out and the next open() restarts the load.
+ * Serialize restart attempts and cancel it before rewriting the
+ * shared URB.
+ */
+ timer_delete_sync(&dev->timer);
+ usb_kill_urb(dev->fw_data->fw_urb);
+
s2255_reset_dsppower(dev);
dev->fw_data->fw_size = dev->fw_data->fw->size;
atomic_set(&dev->fw_data->fw_state, S2255_FW_NOTLOADED);
@@ -2199,6 +2213,7 @@ static void s2255_fwload_start(struct s2255_dev *dev)
CHUNK_SIZE, s2255_fwchunk_complete,
dev->fw_data);
mod_timer(&dev->timer, jiffies + HZ);
+ mutex_unlock(&dev->fw_data->fw_lock);
}

/* standard usb probe function */
@@ -2227,6 +2242,7 @@ static int s2255_probe(struct usb_interface *interface,
dev->fw_data = kzalloc_obj(struct s2255_fw);
if (!dev->fw_data)
goto err_fwdata1;
+ mutex_init(&dev->fw_data->fw_lock);
mutex_init(&dev->lock);
mutex_init(&dev->cmdlock);
/* grab usb_device and save it */
--
2.34.1