pothosware / SoapyPlutoSDR

Soapy SDR plugin for PlutoSDR
https://github.com/pothosware/SoapyPlutoSDR/wiki
GNU Lesser General Public License v2.1
57 stars 22 forks source link

SoapySDR Enumerate still finds Pluto after being unplugged #66

Open rherban opened 1 month ago

rherban commented 1 month ago

I am testing some code to detect when a Soapy-compatible SDR is physically unplugged and to remove it from an internal list. This works for an RTLSDR and HackRF, but the Pluto continues to be recognized by the SoapySDR.Device.enumerate() call.

Host: BeagleBone Black Debian 9 (I know it's old, but our stack is locked to this)

SoapySDRUtil --info
######################################################
##     Soapy SDR -- the SDR abstraction library     ##
######################################################

Lib Version: v0.8.1-gbb33b2d2
API Version: v0.8.200
ABI Version: v0.8-3
Install root: /usr/local
Search path:  /usr/local/lib/SoapySDR/modules0.8-3
Module found: /usr/local/lib/SoapySDR/modules0.8-3/libHackRFSupport.so   (0.3.4-6c0c33f)
Module found: /usr/local/lib/SoapySDR/modules0.8-3/libPlutoSDRSupport.so (0.2.2-03b5ae2)
Module found: /usr/local/lib/SoapySDR/modules0.8-3/librtlsdrSupport.so   (0.3.3-068aa77)
Available factories... hackrf, plutosdr, rtlsdr
Available converters...
 -  CF32 -> [CF32, CS16, CS8, CU16, CU8]
 -  CS16 -> [CF32, CS16, CS8, CU16, CU8]
 -  CS32 -> [CS32]
 -   CS8 -> [CF32, CS16, CS8, CU16, CU8]
 -  CU16 -> [CF32, CS16, CS8]
 -   CU8 -> [CF32, CS16, CS8]
 -   F32 -> [F32, S16, S8, U16, U8]
 -   S16 -> [F32, S16, S8, U16, U8]
 -   S32 -> [S32]
 -    S8 -> [F32, S16, S8, U16, U8]
 -   U16 -> [F32, S16, S8]
 -    U8 -> [F32, S16, S8]

dmesg output:

[  310.030746] usb 1-1.4: new high-speed USB device number 3 using musb-hdrc
[  310.132529] usb 1-1.4: New USB device found, idVendor=0456, idProduct=b673
[  310.132551] usb 1-1.4: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[  310.132560] usb 1-1.4: Product: PlutoSDR (ADALM-PLUTO)
[  310.132567] usb 1-1.4: Manufacturer: Analog Devices Inc.
[  310.132575] usb 1-1.4: SerialNumber: 10447384b9040006230015004923dbd425
[  310.147353] usb-storage 1-1.4:1.2: USB Mass Storage device detected
[  310.162808] scsi host0: usb-storage 1-1.4:1.2
[  310.520873] usbcore: registered new interface driver uas
[  310.540204] usbcore: registered new interface driver cdc_ether
[  310.548324] cdc_acm 1-1.4:1.3: ttyACM0: USB ACM device
[  310.570338] usbcore: registered new interface driver cdc_acm
[  310.570356] cdc_acm: USB Abstract Control Model driver for USB modems and ISDN adapters
[  310.605459] rndis_host 1-1.4:1.0 eth1: register 'rndis_host' at usb-musb-hdrc.1-1.4, RNDIS device, 00:e0:22:51:cb:ad
[  310.605743] usbcore: registered new interface driver rndis_host
[  310.622292] usbcore: registered new interface driver rndis_wlan
[  311.193646] scsi 0:0:0:0: Direct-Access     Linux    File-Stor Gadget 0419 PQ: 0 ANSI: 2
[  311.208133] sd 0:0:0:0: [sda] 61441 512-byte logical blocks: (31.5 MB/30.0 MiB)
[  311.208385] sd 0:0:0:0: [sda] Write Protect is off
[  311.208399] sd 0:0:0:0: [sda] Mode Sense: 0f 00 00 00
[  311.208683] sd 0:0:0:0: [sda] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
[  311.222205]  sda: sda1
[  311.231770] sd 0:0:0:0: [sda] Attached SCSI removable disk
[  311.306073] sd 0:0:0:0: Attached scsi generic sg0 type 0
[  341.441472] usb 1-1.4: USB disconnect, device number 3
[  341.441755] rndis_host 1-1.4:1.0 eth1: unregister 'rndis_host' usb-musb-hdrc.1-1.4, RNDIS device
[  341.469373] sd 0:0:0:0: [sda] Synchronizing SCSI cache
[  341.469632] sd 0:0:0:0: [sda] Synchronize Cache(10) failed: Result: hostbyte=DID_NO_CONNECT driverbyte=DRIVER_OK

Here is code that replicates the problem:

#!/opt/radiohound/.virtualenvs/python3.9/bin/python
import SoapySDR
import time
sensors = []
soapy_time = cur_time = 0
SOAPY_CHECK_INTERVAL = 10

def probe():
    global sensors
    results = SoapySDR.Device.enumerate()
    if len(results) == 0:
        print(f"No devices detected")
    for result in results:
        print(f"probe found: {result}")
        if 'product' not in result and 'label' in result:
            result['product'] = result['label']
        if 'serial' not in result and 'uri' in result:
            result['serial'] = result['uri']

        if not any(sensor['serial'] == result['serial'] for sensor in sensors):
            print(f"Adding {result['product']} - {result['serial']} ")
            sensors.append(result)

    for sensor in sensors:
        if sensor['serial'] not in [result["serial"] for result in results]:
            print(f'Removing Soapy device: {sensor["product"]}')

            #handle = SoapySDR.Device(dict(driver=sensor['driver'], serial=sensor['serial']))
            #print(f"handle: {handle}")
            #SoapySDR.Device.unmake(handle)
            sensors.remove(sensor)

while True:
    cur_time = time.time()
    if cur_time - soapy_time > SOAPY_CHECK_INTERVAL:
        soapy_time = time.time()
        probe()
        print(f"There are {len(sensors)} sensors: {sensors}")

Code output (showing the RTL being detected, then removed, then the pluto being detected but not removed). Newlines were added to distinguish physical events.

./soapy_test.py
No devices detected
There are 0 sensors: []
Detached kernel driver
Found Rafael Micro R820T tuner
Reattached kernel driver
probe found: {driver=rtlsdr, label=Generic RTL2832U OEM :: dfd9f36f58b4, manufacturer=Realtek, product=RTL2838UHIDIR, serial=dfd9f36f58b4, tuner=Rafael Micro R820T}
Adding RTL2838UHIDIR - dfd9f36f58b4
There are 1 sensors: [{driver=rtlsdr, label=Generic RTL2832U OEM :: dfd9f36f58b4, manufacturer=Realtek, product=RTL2838UHIDIR, serial=dfd9f36f58b4, tuner=Rafael Micro R820T}]

No devices detected
Removing Soapy device: RTL2838UHIDIR
There are 0 sensors: []

probe found: {device=PlutoSDR, driver=plutosdr, label=PlutoSDR #0 usb:1.5.5, uri=usb:1.5.5}
Adding PlutoSDR #0 usb:1.5.5 - usb:1.5.5
There are 1 sensors: [{device=PlutoSDR, driver=plutosdr, label=PlutoSDR #0 usb:1.5.5, product=PlutoSDR #0 usb:1.5.5, serial=usb:1.5.5, uri=usb:1.5.5}]

probe found: {device=PlutoSDR, driver=plutosdr, label=PlutoSDR #0 usb:1.5.5, uri=usb:1.5.5}
There are 1 sensors: [{device=PlutoSDR, driver=plutosdr, label=PlutoSDR #0 usb:1.5.5, product=PlutoSDR #0 usb:1.5.5, serial=usb:1.5.5, uri=usb:1.5.5}]
zuckschwerdt commented 1 month ago

The code is here: https://github.com/pothosware/SoapyPlutoSDR/blob/master/PlutoSDR_Registration.cpp#L10 The find_PlutoSDR() call will cache results and always return previously found results.

Not great at all. I imagine newly added devices will also not show up if at least one device was discovered once. The call is expensive, but we should find out if caching is needed at all.

Can you try to remove if (!results.empty()) return results; and check if it fixes the problem as expected?

The caching was added with a3dcbd3 and we should also check if the iio_context can really be safely reused.

rherban commented 1 month ago

Removing those lines did not work, that is I still see the device after being unplugged.

[icarus@7afa] /opt/soapysdr/SoapyPlutoSDR> vi PlutoSDR_Registration.cpp
[icarus@7afa] /opt/soapysdr/SoapyPlutoSDR> cd build
[icarus@7afa] /opt/soapysdr/SoapyPlutoSDR/build> ../../cmake-3.27.4/bin/cmake ..
CMake Deprecation Warning at CMakeLists.txt:4 (cmake_minimum_required):
  Compatibility with CMake < 3.5 will be removed from a future version of
  CMake.

  Update the VERSION argument <min> value or use a ...<max> suffix to tell
  CMake that the project does not need compatibility with older versions.

-- Build type not specified: defaulting to release.
-- LibIIO_INCLUDE_DIRS: /usr/include
-- LibIIO_LIBRARIES: /usr/lib/arm-linux-gnueabihf/libiio.so
-- LibIIO_DEFINITIONS:
-- Could NOT find LibAD9361 (missing: LibAD9361_LIBRARY LibAD9361_INCLUDE_DIR)
-- LibUSB_INCLUDE_DIRS: /usr/include/libusb-1.0
-- LibUSB_LIBRARIES: /usr/lib/arm-linux-gnueabihf/libusb-1.0.so
-- LibUSB_DEFINITIONS:
-- Module PlutoSDRSupport configured with version: 0.2.2-03b5ae2
-- Configuring done (0.8s)
-- Generating done (0.1s)
-- Build files have been written to: /opt/soapysdr/SoapyPlutoSDR/build
[icarus@7afa] /opt/soapysdr/SoapyPlutoSDR/build> make
[ 20%] Building CXX object CMakeFiles/PlutoSDRSupport.dir/PlutoSDR_Registration.cpp.o
In file included from /opt/soapysdr/SoapyPlutoSDR/PlutoSDR_Registration.cpp:7:0:
/usr/include/libusb-1.0/libusb.h:736:4: warning: ISO C++ forbids zero-size array ‘dev_capability_data’ [-Wpedantic]
  [0] /* non-standard, but usually working code */
    ^
/usr/include/libusb-1.0/libusb.h:767:4: warning: ISO C++ forbids zero-size array ‘dev_capability’ [-Wpedantic]
  [0] /* non-standard, but usually working code */
    ^
/usr/include/libusb-1.0/libusb.h:1263:4: warning: ISO C++ forbids zero-size array ‘iso_packet_desc’ [-Wpedantic]
  [0] /* non-standard, but usually working code */
    ^
cc1plus: warning: unrecognized command line option ‘-Wno-zero-length-array’
[ 40%] Building CXX object CMakeFiles/PlutoSDRSupport.dir/Version.cpp.o
[ 60%] Linking CXX shared module libPlutoSDRSupport.so
[100%] Built target PlutoSDRSupport
[icarus@7afa] /opt/soapysdr/SoapyPlutoSDR/build> sudo make install
[100%] Built target PlutoSDRSupport
Install the project...
-- Install configuration: "Release"
-- Installing: /usr/local/lib/SoapySDR/modules0.8-3/libPlutoSDRSupport.so
-- Set runtime path of "/usr/local/lib/SoapySDR/modules0.8-3/libPlutoSDRSupport.so" to ""
zuckschwerdt commented 1 month ago

Sorry, one more line of changes is required: You need to move the line static std::vector<SoapySDR::Kwargs> results; down into the function and remove the static. Or call results.clear(); first thing in the find_PlutoSDR() function.

rherban commented 1 month ago

That did the trick!

probe found: {device=PlutoSDR, driver=plutosdr, label=PlutoSDR #0 usb:1.4.5, uri=usb:1.4.5}
Adding PlutoSDR #0 usb:1.4.5 - usb:1.4.5
There are 1 sensors: [{device=PlutoSDR, driver=plutosdr, label=PlutoSDR #0 usb:1.4.5, product=PlutoSDR #0 usb:1.4.5, serial=usb:1.4.5, uri=usb:1.4.5}]

No devices detected
Removing Soapy device: PlutoSDR #0 usb:1.4.5
There are 0 sensors: []

For reference, the code looks like this now:

...
#ifdef HAS_LIBUSB1
#include <libusb.h>
#endif

static std::vector<SoapySDR::Kwargs> find_PlutoSDR(const SoapySDR::Kwargs &args) {
    std::vector<SoapySDR::Kwargs> results;

    ssize_t ret = 0;
    iio_context *ctx = nullptr;
    iio_scan_context *scan_ctx;
...

Thanks for the pointers!

zuckschwerdt commented 1 month ago

Thanks for testing! Let's keep this open as it really needs to be fixed, the behaviour right only works for one fixed device.