File 0003-Fix-application-hang-on-usb-error.patch of Package rtl-sdr

From 65f06585ea46160cd2d7bee8b3f641c14771c2ee Mon Sep 17 00:00:00 2001
From: Ramon Smits <ramon.smits@gmail.com>
Date: Sat, 10 Jan 2026 20:50:25 +0100
Subject: [PATCH] Fix application hang on USB transfer errors

When USB transfer errors occur (e.g., dongle movement in socket),
applications using rtlsdr_read_async() would hang indefinitely
instead of terminating gracefully.

The issue had two parts:

1. _libusb_callback() was calling rtlsdr_cancel_async() from within
   the libusb event handler, creating a race condition by modifying
   dev->async_cancel while libusb_handle_events_timeout_completed()
   was actively using it as a parameter.

2. Even when dev->dev_lost was set, the async read loop would not
   properly exit, and applications like rtl_fm had no way to detect
   the device loss.

This patch:
- Removes the problematic rtlsdr_cancel_async() call from the callback
- Adds proper dev->dev_lost detection in the main event loop to
  transition to RTLSDR_CANCELING state
- Returns -1 from rtlsdr_read_async() when device is lost
- Updates rtl_fm to check the return value and terminate gracefully
  with an error message

Tested with RTL2832U/FC0012 dongle by deliberately triggering USB
errors (dongle movement). Applications now terminate cleanly instead
of hanging indefinitely.

Signed-off-by: Ramon Smits <ramon.smits@gmail.com>
---
 src/librtlsdr.c | 9 ++++++++-
 src/rtl_fm.c    | 6 +++++-
 2 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/src/librtlsdr.c b/src/librtlsdr.c
index ee13556..c9e43a1 100644
--- a/src/librtlsdr.c
+++ b/src/librtlsdr.c
@@ -1732,7 +1732,6 @@ static void LIBUSB_CALL _libusb_callback(struct libusb_transfer *xfer)
 		    LIBUSB_TRANSFER_NO_DEVICE == xfer->status) {
 #endif
 			dev->dev_lost = 1;
-			rtlsdr_cancel_async(dev);
 			fprintf(stderr, "cb transfer status: %d, "
 				"canceling...\n", xfer->status);
 #ifndef _WIN32
@@ -1930,6 +1929,11 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx,
 			break;
 		}
 
+		/* Check if device was lost due to transfer errors */
+		if (dev->dev_lost && RTLSDR_RUNNING == dev->async_status) {
+			dev->async_status = RTLSDR_CANCELING;
+		}
+
 		if (RTLSDR_CANCELING == dev->async_status) {
 			next_status = RTLSDR_INACTIVE;
 
@@ -1973,6 +1977,9 @@ int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx,
 
 	dev->async_status = next_status;
 
+	if (dev->dev_lost)
+		return -1;
+
 	return r;
 }
 
diff --git a/src/rtl_fm.c b/src/rtl_fm.c
index 0929744..976887b 100644
--- a/src/rtl_fm.c
+++ b/src/rtl_fm.c
@@ -809,7 +809,11 @@ static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx)
 static void *dongle_thread_fn(void *arg)
 {
 	struct dongle_state *s = arg;
-	rtlsdr_read_async(s->dev, rtlsdr_callback, s, 0, s->buf_len);
+	int r = rtlsdr_read_async(s->dev, rtlsdr_callback, s, 0, s->buf_len);
+	if (r != 0 && !do_exit) {
+		fprintf(stderr, "\nDevice error detected, async read returned: %d\n", r);
+		do_exit = 1;
+	}
 	return 0;
 }
 
openSUSE Build Service is sponsored by