Hello,

I'm submitting a patch that fixes a critical issue where applications
using rtlsdr_read_async() hang indefinitely when USB transfer errors
occur.

## Background

I encountered this issue while running long-term monitoring with rtl_fm
for FLEX paging signals. When the RTL-SDR dongle would shift slightly
in the USB socket (causing USB transfer errors), rtl_fm would hang
indefinitely instead of exiting cleanly.

## Root Cause

The problem occurs in two parts:

1. The _libusb_callback() function calls rtlsdr_cancel_async() from
   within the libusb event handler callback. This creates a race
   condition where dev->async_cancel is modified while
   libusb_handle_events_timeout_completed() is actively using it as
   a parameter, leading to unpredictable behavior.

2. Even when the dev->dev_lost flag is set, the async read loop doesn't
   properly exit, and applications like rtl_fm have no way to detect
   that the device is lost.

## Solution

The attached patch:
- Removes the problematic rtlsdr_cancel_async() call from the callback
- Adds detection of dev->dev_lost in the main event loop to properly
  transition to RTLSDR_CANCELING state
- Makes rtlsdr_read_async() return -1 when the device is lost
- Updates rtl_fm to check the return value and terminate gracefully

## Testing

Tested with RTL2832U dongle (FC0012 tuner) by deliberately triggering
USB errors through physical movement. The applications now terminate
cleanly with an appropriate error message instead of hanging indefinitely.
Normal operation (Ctrl+C termination) continues to work correctly.

This fix makes rtl-sdr more robust for long-running monitoring applications
and deployment scenarios where physical interference might occur (e.g.,
Raspberry Pi installations).

Please review and consider merging.

Best regards,
Ramon Smits
From 151ca8b33ec559ff407ac9b3d1bbd8fceb80ba50 Mon Sep 17 00:00:00 2001
From: Ramon Smits <[email protected]>
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 <[email protected]>
---
 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;
 }
 
-- 
2.52.0

Reply via email to