mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-09 15:17:24 -05:00
Co-authored-by: denny <dennis.sheirer@gmail.com>
This commit is contained in:
27
build.gradle
27
build.gradle
@@ -124,20 +124,16 @@ dependencies {
|
|||||||
implementation 'com.mpatric:mp3agic:0.9.1'
|
implementation 'com.mpatric:mp3agic:0.9.1'
|
||||||
implementation 'commons-io:commons-io:2.22.0'
|
implementation 'commons-io:commons-io:2.22.0'
|
||||||
implementation 'eu.hansolo:charts:1.0.5'
|
implementation 'eu.hansolo:charts:1.0.5'
|
||||||
implementation 'io.github.dsheirer:jairspy:1.0.12' //Airspy tuners - GitHub release, see settings.gradle
|
implementation 'io.github.dsheirer:jairspy:1.0.12.1' //Airspy tuners - GitHub release, see settings.gradle
|
||||||
implementation 'io.github.dsheirer:jairspyhf:1.8.1' //Airspy HF+ tuners - GitHub release, see settings.gradle
|
implementation 'io.github.dsheirer:jairspyhf:1.8.1.1' //Airspy HF+ tuners - GitHub release, see settings.gradle
|
||||||
implementation 'io.github.dsheirer:jbladerf:2.6.0' //bladeRF tuners - GitHub release, see settings.gradle
|
implementation 'io.github.dsheirer:jbladerf:2.6.0.1' //bladeRF tuners - GitHub release, see settings.gradle
|
||||||
implementation 'io.github.dsheirer:jhackrf:2026.01.3' //HackRF tuners - GitHub release, see settings.gradle
|
implementation 'io.github.dsheirer:jhackrf:2026.01.3.1' //HackRF tuners - GitHub release, see settings.gradle
|
||||||
implementation 'io.github.dsheirer:jhydrasdr:1.1.4' //HydraSDR tuners - GitHub release, see settings.gradle
|
implementation 'io.github.dsheirer:jlibusb:1.0.30.1' //libusb and USB hotplug for the tuner libraries - GitHub release; not pulled in transitively
|
||||||
implementation 'io.github.dsheirer:jlimesdr:25.1.0.1' //LimeSDR tuners - GitHub release, see settings.gradle
|
implementation 'io.github.dsheirer:jhydrasdr:1.1.4.1' //HydraSDR tuners - GitHub release, see settings.gradle
|
||||||
implementation 'io.github.dsheirer:jrtl:1.4.0.1' //RTL-SDR tuners - GitHub release, see settings.gradle
|
implementation 'io.github.dsheirer:jlimesdr:25.1.0.2' //LimeSDR tuners - GitHub release, see settings.gradle
|
||||||
|
implementation 'io.github.dsheirer:jrtl:1.4.0.2' //RTL-SDR tuners - GitHub release, see settings.gradle
|
||||||
implementation 'io.github.dsheirer:jsdrplay:3.15.3' //SDRplay RSP tuners - GitHub release, see settings.gradle
|
implementation 'io.github.dsheirer:jsdrplay:3.15.3' //SDRplay RSP tuners - GitHub release, see settings.gradle
|
||||||
implementation 'io.github.dsheirer:radio-reference-api:18.0.0'
|
implementation 'io.github.dsheirer:radio-reference-api:18.0.0'
|
||||||
implementation 'io.github.dsheirer:usb4java-native-libraries:1.3.3' //OSX & Windows aarch64 native libs
|
|
||||||
implementation('io.github.dsheirer:usb4java:1.3.5') { //Fork with Loader support for OSX Multi-Version
|
|
||||||
//Excludes the javax.usb API and the duplicate original usb4java classes. The native libraries are listed below.
|
|
||||||
exclude group: 'org.usb4java', module: 'usb4java-javax'
|
|
||||||
}
|
|
||||||
implementation 'net.coderazzi:tablefilter-swing:5.5.4'
|
implementation 'net.coderazzi:tablefilter-swing:5.5.4'
|
||||||
implementation 'org.apache.commons:commons-compress:1.28.0'
|
implementation 'org.apache.commons:commons-compress:1.28.0'
|
||||||
implementation 'org.apache.commons:commons-lang3:3.20.0'
|
implementation 'org.apache.commons:commons-lang3:3.20.0'
|
||||||
@@ -148,13 +144,6 @@ dependencies {
|
|||||||
implementation 'org.controlsfx:controlsfx:11.2.3'
|
implementation 'org.controlsfx:controlsfx:11.2.3'
|
||||||
implementation 'org.rauschig:jarchivelib:1.2.0'
|
implementation 'org.rauschig:jarchivelib:1.2.0'
|
||||||
implementation 'org.slf4j:slf4j-api:2.0.17'
|
implementation 'org.slf4j:slf4j-api:2.0.17'
|
||||||
implementation 'org.usb4java:libusb4java:1.3.0:darwin-x86-64' //usb4java native library
|
|
||||||
implementation 'org.usb4java:libusb4java:1.3.0:linux-aarch64' //usb4java native library
|
|
||||||
implementation 'org.usb4java:libusb4java:1.3.0:linux-arm' //usb4java native library
|
|
||||||
implementation 'org.usb4java:libusb4java:1.3.0:linux-x86' //usb4java native library
|
|
||||||
implementation 'org.usb4java:libusb4java:1.3.0:linux-x86-64' //usb4java native library
|
|
||||||
implementation 'org.usb4java:libusb4java:1.3.0:win32-x86' //usb4java native library
|
|
||||||
implementation 'org.usb4java:libusb4java:1.3.0:win32-x86-64' //usb4java native library
|
|
||||||
implementation 'pl.edu.icm:JLargeArrays:1.6'
|
implementation 'pl.edu.icm:JLargeArrays:1.6'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,15 @@ dependencyResolutionManagement {
|
|||||||
content { includeModule 'io.github.dsheirer', 'jrtl' }
|
content { includeModule 'io.github.dsheirer', 'jrtl' }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//jlibusb is published as a GitHub release, with libusb for all platforms in the jar. Tuner libraries built on
|
||||||
|
//jlibusb (jAirspy, jAirspyHF, jBladeRF, jHackRF, jHydraSDR, jLimeSDR, jRTL) load their native library next to its libusb, so the process shares one copy.
|
||||||
|
ivy {
|
||||||
|
url = 'https://github.com/DSheirer/jlibusb/releases/download'
|
||||||
|
patternLayout { artifact 'v[revision]/[artifact]-[revision].[ext]' }
|
||||||
|
metadataSources { artifact() }
|
||||||
|
content { includeModule 'io.github.dsheirer', 'jlibusb' }
|
||||||
|
}
|
||||||
|
|
||||||
//jHackRF is published as a GitHub release, with the native libraries for all platforms in the jar
|
//jHackRF is published as a GitHub release, with the native libraries for all platforms in the jar
|
||||||
ivy {
|
ivy {
|
||||||
url = 'https://github.com/DSheirer/jHackRF/releases/download'
|
url = 'https://github.com/DSheirer/jHackRF/releases/download'
|
||||||
|
|||||||
@@ -32,19 +32,13 @@ import io.github.dsheirer.source.tuner.ITunerErrorListener;
|
|||||||
import io.github.dsheirer.source.tuner.TunerController;
|
import io.github.dsheirer.source.tuner.TunerController;
|
||||||
import io.github.dsheirer.source.tuner.TunerType;
|
import io.github.dsheirer.source.tuner.TunerType;
|
||||||
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.manager.TunerManager;
|
import io.github.dsheirer.source.tuner.manager.UsbDeviceLookup;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.usb4java.Context;
|
|
||||||
import org.usb4java.Device;
|
|
||||||
import org.usb4java.DeviceDescriptor;
|
|
||||||
import org.usb4java.DeviceHandle;
|
|
||||||
import org.usb4java.DeviceList;
|
|
||||||
import org.usb4java.LibUsb;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Airspy tuner controller, implemented with the jAirspy library (libairspy).
|
* Airspy tuner controller, implemented with the jAirspy library (libairspy).
|
||||||
@@ -212,98 +206,17 @@ public class AirspyTunerController extends TunerController
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads the serial number of the device at this controller's USB bus and port, since libairspy opens a specific
|
* Reads the serial number of the device at this controller's USB bus and port, since libairspy opens a specific
|
||||||
* device by serial number. The device is closed again before returning so that libairspy can open it.
|
* device by serial number. The USB serial string is formatted as: AIRSPY SN:0123456789ABCDEF
|
||||||
* @return serial number
|
* @return serial number
|
||||||
* @throws SourceException if the device isn't found or can't be accessed.
|
* @throws SourceException if the device isn't found or can't be accessed.
|
||||||
*/
|
*/
|
||||||
private long readSerialNumber() throws SourceException
|
private long readSerialNumber() throws SourceException
|
||||||
{
|
{
|
||||||
Context context = new Context();
|
String serial = UsbDeviceLookup.readSerialNumber(mBus, mPortAddress);
|
||||||
int status = LibUsb.init(context);
|
|
||||||
|
|
||||||
if(status != LibUsb.SUCCESS)
|
|
||||||
{
|
|
||||||
throw new SourceException("Can't initialize libusb library - " + LibUsb.errorName(status));
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
DeviceList deviceList = new DeviceList();
|
return Long.parseUnsignedLong(serial.substring(serial.lastIndexOf(':') + 1).trim(), 16);
|
||||||
int count = LibUsb.getDeviceList(context, deviceList);
|
|
||||||
|
|
||||||
if(count < 0)
|
|
||||||
{
|
|
||||||
throw new SourceException("Can't get the USB device list - " + LibUsb.errorName(count));
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for(Device device : deviceList)
|
|
||||||
{
|
|
||||||
if(LibUsb.getBusNumber(device) == mBus && LibUsb.getPortNumber(device) > 0 &&
|
|
||||||
mPortAddress != null && mPortAddress.equals(TunerManager.getPortAddress(device)))
|
|
||||||
{
|
|
||||||
return readSerialNumber(device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
LibUsb.freeDeviceList(deviceList, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
LibUsb.exit(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new SourceException("Couldn't find USB device at bus [" + mBus + "] port [" + mPortAddress + "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the serial number from the device's USB serial string, formatted as: AIRSPY SN:0123456789ABCDEF
|
|
||||||
* @param device to read
|
|
||||||
* @return serial number
|
|
||||||
* @throws SourceException if the device can't be opened or the serial string isn't valid.
|
|
||||||
*/
|
|
||||||
private static long readSerialNumber(Device device) throws SourceException
|
|
||||||
{
|
|
||||||
DeviceDescriptor descriptor = new DeviceDescriptor();
|
|
||||||
int status = LibUsb.getDeviceDescriptor(device, descriptor);
|
|
||||||
|
|
||||||
if(status != LibUsb.SUCCESS)
|
|
||||||
{
|
|
||||||
throw new SourceException("Can't obtain tuner's device descriptor - " + LibUsb.errorName(status));
|
|
||||||
}
|
|
||||||
|
|
||||||
DeviceHandle handle = new DeviceHandle();
|
|
||||||
status = LibUsb.open(device, handle);
|
|
||||||
|
|
||||||
if(status == LibUsb.ERROR_ACCESS)
|
|
||||||
{
|
|
||||||
mLog.error("Access to USB tuner denied - (windows) reinstall zadig driver or (linux) blacklist driver and/or check udev rules");
|
|
||||||
throw new SourceException("access denied - if using linux, blacklist the default driver and/or install udev rules");
|
|
||||||
}
|
|
||||||
else if(status != LibUsb.SUCCESS)
|
|
||||||
{
|
|
||||||
mLog.error("Can't open USB tuner - check driver or Linux udev rules");
|
|
||||||
throw new SourceException("Can't open USB tuner - reinstall driver? - " + LibUsb.errorName(status));
|
|
||||||
}
|
|
||||||
|
|
||||||
String serial;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
serial = LibUsb.getStringDescriptor(handle, descriptor.iSerialNumber());
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
LibUsb.close(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return Long.parseUnsignedLong(serial.substring(serial.lastIndexOf(':') + 1), 16);
|
|
||||||
}
|
}
|
||||||
catch(NullPointerException | NumberFormatException e)
|
catch(NullPointerException | NumberFormatException e)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -32,18 +32,12 @@ import io.github.dsheirer.source.tuner.ITunerErrorListener;
|
|||||||
import io.github.dsheirer.source.tuner.TunerController;
|
import io.github.dsheirer.source.tuner.TunerController;
|
||||||
import io.github.dsheirer.source.tuner.TunerType;
|
import io.github.dsheirer.source.tuner.TunerType;
|
||||||
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.manager.TunerManager;
|
import io.github.dsheirer.source.tuner.manager.UsbDeviceLookup;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.usb4java.Context;
|
|
||||||
import org.usb4java.Device;
|
|
||||||
import org.usb4java.DeviceDescriptor;
|
|
||||||
import org.usb4java.DeviceHandle;
|
|
||||||
import org.usb4java.DeviceList;
|
|
||||||
import org.usb4java.LibUsb;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Airspy HF+ Dual Port, HF+ Discovery and Ranger tuner controller, implemented with the jAirspyHF library
|
* Airspy HF+ Dual Port, HF+ Discovery and Ranger tuner controller, implemented with the jAirspyHF library
|
||||||
@@ -258,94 +252,13 @@ public class AirspyHfTunerController extends TunerController
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads the serial number of the device at this controller's USB bus and port, since libairspyhf opens a specific
|
* Reads the serial number of the device at this controller's USB bus and port, since libairspyhf opens a specific
|
||||||
* device by serial number. The device is closed again before returning so that libairspyhf can open it.
|
* device by serial number. The USB serial string is formatted as: AIRSPYHF SN:0123456789ABCDEF
|
||||||
* @return serial number
|
* @return serial number
|
||||||
* @throws SourceException if the device isn't found or can't be accessed.
|
* @throws SourceException if the device isn't found or can't be accessed.
|
||||||
*/
|
*/
|
||||||
private long readSerialNumber() throws SourceException
|
private long readSerialNumber() throws SourceException
|
||||||
{
|
{
|
||||||
Context context = new Context();
|
String serial = UsbDeviceLookup.readSerialNumber(mBus, mPortAddress);
|
||||||
int status = LibUsb.init(context);
|
|
||||||
|
|
||||||
if(status != LibUsb.SUCCESS)
|
|
||||||
{
|
|
||||||
throw new SourceException("Can't initialize libusb library - " + LibUsb.errorName(status));
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
DeviceList deviceList = new DeviceList();
|
|
||||||
int count = LibUsb.getDeviceList(context, deviceList);
|
|
||||||
|
|
||||||
if(count < 0)
|
|
||||||
{
|
|
||||||
throw new SourceException("Can't get the USB device list - " + LibUsb.errorName(count));
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for(Device device : deviceList)
|
|
||||||
{
|
|
||||||
if(LibUsb.getBusNumber(device) == mBus && LibUsb.getPortNumber(device) > 0 &&
|
|
||||||
mPortAddress != null && mPortAddress.equals(TunerManager.getPortAddress(device)))
|
|
||||||
{
|
|
||||||
return readSerialNumber(device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
LibUsb.freeDeviceList(deviceList, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
LibUsb.exit(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new SourceException("Couldn't find USB device at bus [" + mBus + "] port [" + mPortAddress + "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the serial number from the device's USB serial string, formatted as: AIRSPYHF SN:0123456789ABCDEF
|
|
||||||
* @param device to read
|
|
||||||
* @return serial number
|
|
||||||
* @throws SourceException if the device can't be opened or the serial string isn't valid.
|
|
||||||
*/
|
|
||||||
private static long readSerialNumber(Device device) throws SourceException
|
|
||||||
{
|
|
||||||
DeviceDescriptor descriptor = new DeviceDescriptor();
|
|
||||||
int status = LibUsb.getDeviceDescriptor(device, descriptor);
|
|
||||||
|
|
||||||
if(status != LibUsb.SUCCESS)
|
|
||||||
{
|
|
||||||
throw new SourceException("Can't obtain tuner's device descriptor - " + LibUsb.errorName(status));
|
|
||||||
}
|
|
||||||
|
|
||||||
DeviceHandle handle = new DeviceHandle();
|
|
||||||
status = LibUsb.open(device, handle);
|
|
||||||
|
|
||||||
if(status == LibUsb.ERROR_ACCESS)
|
|
||||||
{
|
|
||||||
mLog.error("Access to USB tuner denied - (windows) reinstall zadig driver or (linux) blacklist driver and/or check udev rules");
|
|
||||||
throw new SourceException("access denied - if using linux, blacklist the default driver and/or install udev rules");
|
|
||||||
}
|
|
||||||
else if(status != LibUsb.SUCCESS)
|
|
||||||
{
|
|
||||||
mLog.error("Can't open USB tuner - check driver or Linux udev rules");
|
|
||||||
throw new SourceException("Can't open USB tuner - reinstall driver? - " + LibUsb.errorName(status));
|
|
||||||
}
|
|
||||||
|
|
||||||
String serial;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
serial = LibUsb.getStringDescriptor(handle, descriptor.iSerialNumber());
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
LibUsb.close(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -45,17 +45,13 @@ import io.github.dsheirer.source.tuner.ITunerErrorListener;
|
|||||||
import io.github.dsheirer.source.tuner.TunerController;
|
import io.github.dsheirer.source.tuner.TunerController;
|
||||||
import io.github.dsheirer.source.tuner.TunerType;
|
import io.github.dsheirer.source.tuner.TunerType;
|
||||||
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.manager.TunerManager;
|
import io.github.dsheirer.source.tuner.manager.UsbDeviceLookup;
|
||||||
import io.github.dsheirer.util.ThreadPool;
|
import io.github.dsheirer.util.ThreadPool;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.usb4java.Context;
|
|
||||||
import org.usb4java.Device;
|
|
||||||
import org.usb4java.DeviceList;
|
|
||||||
import org.usb4java.LibUsb;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* bladeRF tuner controller, implemented with the jBladeRF library (libbladeRF).
|
* bladeRF tuner controller, implemented with the jBladeRF library (libbladeRF).
|
||||||
@@ -376,46 +372,7 @@ public class BladeRfTunerController extends TunerController
|
|||||||
*/
|
*/
|
||||||
private int readUsbAddress() throws SourceException
|
private int readUsbAddress() throws SourceException
|
||||||
{
|
{
|
||||||
Context context = new Context();
|
return UsbDeviceLookup.find(mBus, mPortAddress).address();
|
||||||
int status = LibUsb.init(context);
|
|
||||||
|
|
||||||
if(status != LibUsb.SUCCESS)
|
|
||||||
{
|
|
||||||
throw new SourceException("Can't initialize libusb library - " + LibUsb.errorName(status));
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
DeviceList deviceList = new DeviceList();
|
|
||||||
int count = LibUsb.getDeviceList(context, deviceList);
|
|
||||||
|
|
||||||
if(count < 0)
|
|
||||||
{
|
|
||||||
throw new SourceException("Can't get the USB device list - " + LibUsb.errorName(count));
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for(Device device : deviceList)
|
|
||||||
{
|
|
||||||
if(LibUsb.getBusNumber(device) == mBus && LibUsb.getPortNumber(device) > 0 &&
|
|
||||||
mPortAddress != null && mPortAddress.equals(TunerManager.getPortAddress(device)))
|
|
||||||
{
|
|
||||||
return LibUsb.getDeviceAddress(device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
LibUsb.freeDeviceList(deviceList, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
LibUsb.exit(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new SourceException("Couldn't find USB device at bus [" + mBus + "] port [" + mPortAddress + "]");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ package io.github.dsheirer.source.tuner.fcd;
|
|||||||
public enum FCDCommand
|
public enum FCDCommand
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Note: the javaxUSB and usb4Java libraries are slightly different than
|
* Note: the libusb interrupt transfers used here (through jlibusb) are slightly different than
|
||||||
* the signal9 HID libraries used in C, so you will see variations in the
|
* the signal9 HID libraries used in C, so you will see variations in the
|
||||||
* byte array length and indexes, when comparing this setup to the qthid
|
* byte array length and indexes, when comparing this setup to the qthid
|
||||||
* source code.
|
* source code.
|
||||||
|
|||||||
@@ -19,6 +19,11 @@
|
|||||||
package io.github.dsheirer.source.tuner.fcd;
|
package io.github.dsheirer.source.tuner.fcd;
|
||||||
|
|
||||||
import io.github.dsheirer.buffer.INativeBuffer;
|
import io.github.dsheirer.buffer.INativeBuffer;
|
||||||
|
import io.github.dsheirer.libusb.LibUsb;
|
||||||
|
import io.github.dsheirer.libusb.UsbContext;
|
||||||
|
import io.github.dsheirer.libusb.UsbDeviceHandle;
|
||||||
|
import io.github.dsheirer.libusb.UsbDeviceInfo;
|
||||||
|
import io.github.dsheirer.libusb.UsbException;
|
||||||
import io.github.dsheirer.sample.Listener;
|
import io.github.dsheirer.sample.Listener;
|
||||||
import io.github.dsheirer.sample.adapter.ComplexShortAdapter;
|
import io.github.dsheirer.sample.adapter.ComplexShortAdapter;
|
||||||
import io.github.dsheirer.source.SourceException;
|
import io.github.dsheirer.source.SourceException;
|
||||||
@@ -28,21 +33,13 @@ import io.github.dsheirer.source.tuner.MixerTunerType;
|
|||||||
import io.github.dsheirer.source.tuner.TunerClass;
|
import io.github.dsheirer.source.tuner.TunerClass;
|
||||||
import io.github.dsheirer.source.tuner.TunerController;
|
import io.github.dsheirer.source.tuner.TunerController;
|
||||||
import io.github.dsheirer.source.tuner.TunerType;
|
import io.github.dsheirer.source.tuner.TunerType;
|
||||||
import io.github.dsheirer.source.tuner.manager.TunerManager;
|
import io.github.dsheirer.source.tuner.manager.UsbDeviceLookup;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.usb4java.Context;
|
|
||||||
import org.usb4java.Device;
|
|
||||||
import org.usb4java.DeviceDescriptor;
|
|
||||||
import org.usb4java.DeviceHandle;
|
|
||||||
import org.usb4java.DeviceList;
|
|
||||||
import org.usb4java.LibUsb;
|
|
||||||
import org.usb4java.LibUsbException;
|
|
||||||
|
|
||||||
import javax.sound.sampled.TargetDataLine;
|
import javax.sound.sampled.TargetDataLine;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.nio.IntBuffer;
|
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
public abstract class FCDTunerController extends TunerController
|
public abstract class FCDTunerController extends TunerController
|
||||||
@@ -50,16 +47,19 @@ public abstract class FCDTunerController extends TunerController
|
|||||||
private final static Logger mLog = LoggerFactory.getLogger(FCDTunerController.class);
|
private final static Logger mLog = LoggerFactory.getLogger(FCDTunerController.class);
|
||||||
private static final double USABLE_BANDWIDTH_PERCENT = 1.0;
|
private static final double USABLE_BANDWIDTH_PERCENT = 1.0;
|
||||||
private static final int DC_SPIKE_AVOID_HALF_BANDWIDTH = 5000;
|
private static final int DC_SPIKE_AVOID_HALF_BANDWIDTH = 5000;
|
||||||
private final static byte FCD_INTERFACE = (byte) 0x2;
|
private final static int FCD_INTERFACE = 0x2;
|
||||||
private final static byte FCD_ENDPOINT_IN = (byte) 0x82;
|
private final static int FCD_ENDPOINT_IN = 0x82;
|
||||||
private final static byte FCD_ENDPOINT_OUT = (byte) 0x2;
|
private final static int FCD_ENDPOINT_OUT = 0x2;
|
||||||
|
private final static int FCD_PACKET_SIZE = 64;
|
||||||
|
private final static int FCD_TRANSFER_TIMEOUT_MS = 500;
|
||||||
|
private final static int LIBUSB_ERROR_NO_DEVICE = -4;
|
||||||
|
|
||||||
private int mBus;
|
private int mBus;
|
||||||
private String mPortAddress;
|
private String mPortAddress;
|
||||||
private Context mDeviceContext = new Context();
|
private UsbContext mUsbContext;
|
||||||
private Device mDevice;
|
private UsbDeviceInfo mDevice;
|
||||||
private DeviceDescriptor mDeviceDescriptor = new DeviceDescriptor();
|
private volatile UsbDeviceHandle mDeviceHandle;
|
||||||
private DeviceHandle mDeviceHandle = new DeviceHandle();
|
private boolean mShutdown = false;
|
||||||
|
|
||||||
private FCDConfiguration mConfiguration = new FCDConfiguration();
|
private FCDConfiguration mConfiguration = new FCDConfiguration();
|
||||||
protected ComplexMixer mComplexMixer;
|
protected ComplexMixer mComplexMixer;
|
||||||
@@ -168,58 +168,6 @@ public abstract class FCDTunerController extends TunerController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds the USB device for this tuner at the specified USB bus and port.
|
|
||||||
* @return discovered USB device
|
|
||||||
* @throws SourceException if there is an error or the device is not discovered.
|
|
||||||
*/
|
|
||||||
private Device findDevice() throws SourceException
|
|
||||||
{
|
|
||||||
Device foundDevice = null;
|
|
||||||
|
|
||||||
DeviceList deviceList = new DeviceList();
|
|
||||||
int count = LibUsb.getDeviceList(mDeviceContext, deviceList);
|
|
||||||
|
|
||||||
if(count >= 0)
|
|
||||||
{
|
|
||||||
for(Device device: deviceList)
|
|
||||||
{
|
|
||||||
int bus = LibUsb.getBusNumber(device);
|
|
||||||
int port = LibUsb.getPortNumber(device);
|
|
||||||
|
|
||||||
if(port > 0)
|
|
||||||
{
|
|
||||||
String portAddress = TunerManager.getPortAddress(device);
|
|
||||||
|
|
||||||
if(mBus == bus && mPortAddress != null && mPortAddress.equals(portAddress))
|
|
||||||
{
|
|
||||||
foundDevice = device;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LibUsb.unrefDevice(device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LibUsb.unrefDevice(device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new SourceException("LibUsb couldn't discover USB device [" + mBus + ":" + mPortAddress +
|
|
||||||
"] from device list" + (count < 0 ? " - error: " + LibUsb.errorName(count) : ""));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(foundDevice == null)
|
|
||||||
{
|
|
||||||
throw new SourceException("LibUsb couldn't find the matching USB device");
|
|
||||||
}
|
|
||||||
|
|
||||||
return foundDevice;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the controller by opening the USB device and claiming the HID interface.
|
* Initializes the controller by opening the USB device and claiming the HID interface.
|
||||||
* Invoke this method after constructing this class to setup the controller.
|
* Invoke this method after constructing this class to setup the controller.
|
||||||
@@ -228,80 +176,102 @@ public abstract class FCDTunerController extends TunerController
|
|||||||
*/
|
*/
|
||||||
public void start() throws SourceException
|
public void start() throws SourceException
|
||||||
{
|
{
|
||||||
if(mDeviceContext == null)
|
if(mShutdown)
|
||||||
{
|
{
|
||||||
throw new SourceException("Device cannot be reused once it has been shutdown");
|
throw new SourceException("Device cannot be reused once it has been shutdown");
|
||||||
}
|
}
|
||||||
|
|
||||||
int status = LibUsb.init(mDeviceContext);
|
mUsbContext = UsbDeviceLookup.createContext();
|
||||||
|
|
||||||
if(status != LibUsb.SUCCESS)
|
|
||||||
{
|
|
||||||
throw new SourceException("Can't initialize libusb library - " + LibUsb.errorName(status));
|
|
||||||
}
|
|
||||||
|
|
||||||
mDevice = findDevice();
|
|
||||||
mDeviceDescriptor = new DeviceDescriptor();
|
|
||||||
status = LibUsb.getDeviceDescriptor(mDevice, mDeviceDescriptor);
|
|
||||||
|
|
||||||
if(status != LibUsb.SUCCESS)
|
|
||||||
{
|
|
||||||
mDeviceDescriptor = null;
|
|
||||||
throw new SourceException("Can't obtain tuner's device descriptor - " + LibUsb.errorName(status));
|
|
||||||
}
|
|
||||||
|
|
||||||
mDeviceHandle = new DeviceHandle();
|
|
||||||
status = LibUsb.open(mDevice, mDeviceHandle);
|
|
||||||
|
|
||||||
if(status != LibUsb.SUCCESS)
|
|
||||||
{
|
|
||||||
mDeviceHandle = null;
|
|
||||||
mDeviceDescriptor = null;
|
|
||||||
|
|
||||||
mLog.error("Can't open USB tuner [" + getTunerType() + "] - check driver or Linux udev rules");
|
|
||||||
throw new SourceException("Can't open USB tuner - check driver or Linux udev rules - " + LibUsb.errorName(status));
|
|
||||||
}
|
|
||||||
|
|
||||||
//Remove a kernel driver if active
|
|
||||||
status = LibUsb.kernelDriverActive(mDeviceHandle, FCD_INTERFACE);
|
|
||||||
|
|
||||||
if(status == 1)
|
|
||||||
{
|
|
||||||
status = LibUsb.detachKernelDriver(mDeviceHandle, FCD_INTERFACE);
|
|
||||||
|
|
||||||
if(status != LibUsb.SUCCESS)
|
|
||||||
{
|
|
||||||
mLog.error("Unable to detach kernel driver for USB tuner device - bus:" + mBus + " port:" + mPortAddress);
|
|
||||||
mDeviceHandle = null;
|
|
||||||
mDeviceDescriptor = null;
|
|
||||||
throw new SourceException("Can't detach kernel driver");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Claim the interface
|
|
||||||
status = LibUsb.claimInterface(mDeviceHandle, FCD_INTERFACE);
|
|
||||||
|
|
||||||
if(status == LibUsb.ERROR_BUSY)
|
|
||||||
{
|
|
||||||
mDeviceHandle = null;
|
|
||||||
mDeviceDescriptor = null;
|
|
||||||
throw new SourceException("USB tuner is in-use by another application");
|
|
||||||
}
|
|
||||||
else if(status != LibUsb.SUCCESS)
|
|
||||||
{
|
|
||||||
mDeviceHandle = null;
|
|
||||||
mDeviceDescriptor = null;
|
|
||||||
throw new SourceException("Can't claim interface on USB tuner - " + LibUsb.errorName(status));
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
deviceStart();
|
mDevice = UsbDeviceLookup.find(mUsbContext, mBus, mPortAddress);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mDeviceHandle = mUsbContext.open(mDevice);
|
||||||
|
}
|
||||||
|
catch(UsbException e)
|
||||||
|
{
|
||||||
|
mLog.error("Can't open USB tuner [" + getTunerType() + "] - check driver or Linux udev rules");
|
||||||
|
throw new SourceException("Can't open USB tuner - check driver or Linux udev rules - " +
|
||||||
|
LibUsb.errorName(e.code()));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Remove a kernel driver if active
|
||||||
|
boolean kernelDriverActive;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
kernelDriverActive = mDeviceHandle.isKernelDriverActive(FCD_INTERFACE);
|
||||||
|
}
|
||||||
|
catch(UsbException e)
|
||||||
|
{
|
||||||
|
kernelDriverActive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(kernelDriverActive)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mDeviceHandle.detachKernelDriver(FCD_INTERFACE);
|
||||||
|
}
|
||||||
|
catch(UsbException e)
|
||||||
|
{
|
||||||
|
mLog.error("Unable to detach kernel driver for USB tuner device - bus:" + mBus + " port:" + mPortAddress);
|
||||||
|
throw new SourceException("Can't detach kernel driver");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Claim the interface
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mDeviceHandle.claimInterface(FCD_INTERFACE);
|
||||||
|
}
|
||||||
|
catch(UsbException e)
|
||||||
|
{
|
||||||
|
if(e.code() == UsbDeviceLookup.ERROR_BUSY)
|
||||||
|
{
|
||||||
|
throw new SourceException("USB tuner is in-use by another application");
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new SourceException("Can't claim interface on USB tuner - " + LibUsb.errorName(e.code()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch(SourceException se)
|
catch(SourceException se)
|
||||||
{
|
{
|
||||||
|
closeUsb();
|
||||||
throw se;
|
throw se;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deviceStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the USB device handle and the libusb context.
|
||||||
|
*/
|
||||||
|
private void closeUsb()
|
||||||
|
{
|
||||||
|
UsbDeviceHandle handle = mDeviceHandle;
|
||||||
|
mDeviceHandle = null;
|
||||||
|
|
||||||
|
if(handle != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
handle.close();
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
mLog.error("error while closing device handle", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mUsbContext != null)
|
||||||
|
{
|
||||||
|
mUsbContext.close();
|
||||||
|
mUsbContext = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -321,23 +291,9 @@ public abstract class FCDTunerController extends TunerController
|
|||||||
mComplexMixer = null;
|
mComplexMixer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mDeviceHandle != null)
|
closeUsb();
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
LibUsb.close(mDeviceHandle);
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
mLog.error("error while closing device handle", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mDeviceDescriptor = null;
|
|
||||||
mDeviceHandle = null;
|
|
||||||
mDevice = null;
|
mDevice = null;
|
||||||
LibUsb.exit(mDeviceContext);
|
mShutdown = true;
|
||||||
mDeviceContext = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -360,19 +316,14 @@ public abstract class FCDTunerController extends TunerController
|
|||||||
*/
|
*/
|
||||||
public String getUSBAddress()
|
public String getUSBAddress()
|
||||||
{
|
{
|
||||||
if(mDevice != null)
|
UsbDeviceInfo device = mDevice;
|
||||||
|
|
||||||
|
//This is the tuner's unique ID for saved tuner configurations, so the format must not change: the port is the
|
||||||
|
//last port number in the path from the root hub, as libusb_get_port_number() reports it.
|
||||||
|
if(device != null)
|
||||||
{
|
{
|
||||||
StringBuilder sb = new StringBuilder();
|
int port = device.portNumbers().isEmpty() ? 0 : device.portNumbers().getLast();
|
||||||
|
return "Bus:" + device.busNumber() + " Port:" + port;
|
||||||
sb.append("Bus:");
|
|
||||||
int bus = LibUsb.getBusNumber(mDevice);
|
|
||||||
sb.append(bus);
|
|
||||||
|
|
||||||
sb.append(" Port:");
|
|
||||||
int port = LibUsb.getPortNumber(mDevice);
|
|
||||||
sb.append(port);
|
|
||||||
|
|
||||||
return sb.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
@@ -383,38 +334,34 @@ public abstract class FCDTunerController extends TunerController
|
|||||||
*/
|
*/
|
||||||
public String getUSBID()
|
public String getUSBID()
|
||||||
{
|
{
|
||||||
if(mDeviceDescriptor != null)
|
UsbDeviceInfo device = mDevice;
|
||||||
|
|
||||||
|
if(device != null)
|
||||||
{
|
{
|
||||||
StringBuilder sb = new StringBuilder();
|
return String.format("%04X:%04X", device.vendorId(), device.productId());
|
||||||
|
|
||||||
sb.append(String.format("%04X", (int) (mDeviceDescriptor.idVendor() & 0xFFFF)));
|
|
||||||
sb.append(":");
|
|
||||||
sb.append(String.format("%04X", (int) (mDeviceDescriptor.idProduct() & 0xFFFF)));
|
|
||||||
|
|
||||||
return sb.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* USB Port Speed. Should be 2.0 for both types of funcube dongles
|
* USB Port Speed. The FUNcube Dongle Pro is a full speed (USB 1.1) device.
|
||||||
*/
|
*/
|
||||||
public String getUSBSpeed()
|
public String getUSBSpeed()
|
||||||
{
|
{
|
||||||
if(mDevice != null)
|
UsbDeviceInfo device = mDevice;
|
||||||
{
|
|
||||||
int speed = LibUsb.getDeviceSpeed(mDevice);
|
|
||||||
|
|
||||||
switch(speed)
|
if(device != null)
|
||||||
|
{
|
||||||
|
switch(device.speed())
|
||||||
{
|
{
|
||||||
case 0:
|
case LOW:
|
||||||
return "1.1 LOW";
|
return "1.1 LOW";
|
||||||
case 1:
|
case FULL:
|
||||||
return "1.1 FULL";
|
return "1.1 FULL";
|
||||||
case 2:
|
case HIGH:
|
||||||
return "2.0 HIGH";
|
return "2.0 HIGH";
|
||||||
case 3:
|
case SUPER:
|
||||||
return "3.0 SUPER";
|
return "3.0 SUPER";
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
@@ -500,26 +447,27 @@ public abstract class FCDTunerController extends TunerController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs an interrupt write to the OUT endpoint.
|
* Performs an interrupt write of one 64 byte packet to the OUT endpoint.
|
||||||
*
|
*
|
||||||
* @param buffer - direct allocated buffer. Must be 64 bytes in length.
|
* @param packet to write. Must be 64 bytes in length.
|
||||||
* @throws LibUsbException on error
|
* @throws FCDUsbException on error
|
||||||
*/
|
*/
|
||||||
private void write(ByteBuffer buffer) throws LibUsbException
|
private void write(byte[] packet) throws FCDUsbException
|
||||||
{
|
{
|
||||||
if(mDeviceHandle != null)
|
UsbDeviceHandle handle = mDeviceHandle;
|
||||||
{
|
|
||||||
IntBuffer transferred = IntBuffer.allocate(1);
|
|
||||||
int result = LibUsb.interruptTransfer(mDeviceHandle, FCD_ENDPOINT_OUT, buffer, transferred, 500l);
|
|
||||||
|
|
||||||
if(result != LibUsb.SUCCESS)
|
if(handle == null)
|
||||||
{
|
|
||||||
throw new LibUsbException("error writing byte buffer", result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
throw new LibUsbException("device handle is null", LibUsb.ERROR_NO_DEVICE);
|
throw new FCDUsbException("device handle is null", LIBUSB_ERROR_NO_DEVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
handle.interruptTransfer(FCD_ENDPOINT_OUT, packet, packet.length, FCD_TRANSFER_TIMEOUT_MS);
|
||||||
|
}
|
||||||
|
catch(UsbException e)
|
||||||
|
{
|
||||||
|
throw new FCDUsbException("error writing byte buffer", e.code());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -527,14 +475,14 @@ public abstract class FCDTunerController extends TunerController
|
|||||||
* Performs an interrupt write to the OUT endpoint for the FCD command.
|
* Performs an interrupt write to the OUT endpoint for the FCD command.
|
||||||
*
|
*
|
||||||
* @param command - no-argument command to write
|
* @param command - no-argument command to write
|
||||||
* @throws LibUsbException - on error
|
* @throws FCDUsbException - on error
|
||||||
*/
|
*/
|
||||||
private void write(FCDCommand command) throws LibUsbException
|
private void write(FCDCommand command) throws FCDUsbException
|
||||||
{
|
{
|
||||||
ByteBuffer buffer = ByteBuffer.allocateDirect(64);
|
byte[] packet = new byte[FCD_PACKET_SIZE];
|
||||||
buffer.put(0, command.getCommand());
|
packet[0] = command.getCommand();
|
||||||
buffer.put(1, (byte) 0x00);
|
packet[1] = (byte) 0x00;
|
||||||
write(buffer);
|
write(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -542,46 +490,53 @@ public abstract class FCDTunerController extends TunerController
|
|||||||
*
|
*
|
||||||
* @param command - command to write
|
* @param command - command to write
|
||||||
* @param argument - value to write with the command
|
* @param argument - value to write with the command
|
||||||
* @throws LibUsbException - on error
|
* @throws FCDUsbException - on error
|
||||||
*/
|
*/
|
||||||
private void write(FCDCommand command, long argument) throws LibUsbException
|
private void write(FCDCommand command, long argument) throws FCDUsbException
|
||||||
{
|
{
|
||||||
ByteBuffer buffer = ByteBuffer.allocateDirect(64);
|
byte[] packet = new byte[FCD_PACKET_SIZE];
|
||||||
|
ByteBuffer buffer = ByteBuffer.wrap(packet);
|
||||||
/* The FCD expects little-endian formatted values */
|
/* The FCD expects little-endian formatted values */
|
||||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||||
buffer.put(0, command.getCommand());
|
buffer.put(0, command.getCommand());
|
||||||
buffer.putLong(1, argument);
|
buffer.putLong(1, argument);
|
||||||
write(buffer);
|
write(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs an interrupt read against the endpoint
|
* Performs an interrupt read of one 64 byte packet from the IN endpoint
|
||||||
*
|
*
|
||||||
* @return buffer read from FCD
|
* @return buffer read from FCD
|
||||||
* @throws LibUsbException on error
|
* @throws FCDUsbException on error
|
||||||
*/
|
*/
|
||||||
private ByteBuffer read() throws LibUsbException
|
private ByteBuffer read() throws FCDUsbException
|
||||||
{
|
{
|
||||||
if(mDeviceHandle != null)
|
UsbDeviceHandle handle = mDeviceHandle;
|
||||||
|
|
||||||
|
if(handle == null)
|
||||||
{
|
{
|
||||||
ByteBuffer buffer = ByteBuffer.allocateDirect(64);
|
throw new FCDUsbException("device handle is null", LIBUSB_ERROR_NO_DEVICE);
|
||||||
IntBuffer transferred = IntBuffer.allocate(1);
|
|
||||||
int result = LibUsb.interruptTransfer(mDeviceHandle, FCD_ENDPOINT_IN, buffer, transferred, 500l);
|
|
||||||
|
|
||||||
if(result != LibUsb.SUCCESS)
|
|
||||||
{
|
|
||||||
throw new LibUsbException("error reading byte buffer", result);
|
|
||||||
}
|
|
||||||
else if(transferred.get(0) != buffer.capacity())
|
|
||||||
{
|
|
||||||
throw new LibUsbException("received bytes [" + transferred.get(0) + "] didn't match expected length [" +
|
|
||||||
buffer.capacity() + "]", result);
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new LibUsbException("device handle is null", LibUsb.ERROR_NO_DEVICE);
|
byte[] packet = new byte[FCD_PACKET_SIZE];
|
||||||
|
int transferred;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
transferred = handle.interruptTransfer(FCD_ENDPOINT_IN, packet, packet.length, FCD_TRANSFER_TIMEOUT_MS);
|
||||||
|
}
|
||||||
|
catch(UsbException e)
|
||||||
|
{
|
||||||
|
throw new FCDUsbException("error reading byte buffer", e.code());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(transferred != packet.length)
|
||||||
|
{
|
||||||
|
throw new FCDUsbException("received bytes [" + transferred + "] didn't match expected length [" +
|
||||||
|
packet.length + "]", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ByteBuffer.wrap(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -590,9 +545,9 @@ public abstract class FCDTunerController extends TunerController
|
|||||||
*
|
*
|
||||||
* @param command - command to send
|
* @param command - command to send
|
||||||
* @param argument - command argument to send
|
* @param argument - command argument to send
|
||||||
* @throws LibUsbException - on error
|
* @throws FCDUsbException - on error
|
||||||
*/
|
*/
|
||||||
protected synchronized void send(FCDCommand command, long argument) throws LibUsbException
|
protected synchronized void send(FCDCommand command, long argument) throws FCDUsbException
|
||||||
{
|
{
|
||||||
write(command, argument);
|
write(command, argument);
|
||||||
read();
|
read();
|
||||||
@@ -603,9 +558,9 @@ public abstract class FCDTunerController extends TunerController
|
|||||||
* command.
|
* command.
|
||||||
*
|
*
|
||||||
* @param command - command to send
|
* @param command - command to send
|
||||||
* @throws LibUsbException - on error
|
* @throws FCDUsbException - on error
|
||||||
*/
|
*/
|
||||||
protected ByteBuffer send(FCDCommand command) throws LibUsbException
|
protected ByteBuffer send(FCDCommand command) throws FCDUsbException
|
||||||
{
|
{
|
||||||
write(command);
|
write(command);
|
||||||
return read();
|
return read();
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* *****************************************************************************
|
||||||
|
* Copyright (C) 2014-2026 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
* ****************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.github.dsheirer.source.tuner.fcd;
|
||||||
|
|
||||||
|
import io.github.dsheirer.libusb.LibUsb;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* USB transfer error while communicating with a FUNcube Dongle. Unchecked, like the usb4java exception it replaces,
|
||||||
|
* since the FCD command methods are called from many places that handle any exception.
|
||||||
|
*/
|
||||||
|
public class FCDUsbException extends RuntimeException
|
||||||
|
{
|
||||||
|
private final int mErrorCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an instance
|
||||||
|
* @param message describing the failed operation
|
||||||
|
* @param errorCode libusb error code, or 0 when the transfer succeeded but returned unexpected data
|
||||||
|
*/
|
||||||
|
public FCDUsbException(String message, int errorCode)
|
||||||
|
{
|
||||||
|
super(errorCode < 0 ? message + " - " + LibUsb.errorName(errorCode) : message);
|
||||||
|
mErrorCode = errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* libusb error code, such as -7 for LIBUSB_ERROR_TIMEOUT, or 0
|
||||||
|
*/
|
||||||
|
public int getErrorCode()
|
||||||
|
{
|
||||||
|
return mErrorCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,17 +32,11 @@ import io.github.dsheirer.source.tuner.ITunerErrorListener;
|
|||||||
import io.github.dsheirer.source.tuner.TunerController;
|
import io.github.dsheirer.source.tuner.TunerController;
|
||||||
import io.github.dsheirer.source.tuner.TunerType;
|
import io.github.dsheirer.source.tuner.TunerType;
|
||||||
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.manager.TunerManager;
|
import io.github.dsheirer.source.tuner.manager.UsbDeviceLookup;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.usb4java.Context;
|
|
||||||
import org.usb4java.Device;
|
|
||||||
import org.usb4java.DeviceDescriptor;
|
|
||||||
import org.usb4java.DeviceHandle;
|
|
||||||
import org.usb4java.DeviceList;
|
|
||||||
import org.usb4java.LibUsb;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HackRF One, HackRF Pro, Jawbreaker and rad1o tuner controller, implemented with the jHackRF library (libhackrf).
|
* HackRF One, HackRF Pro, Jawbreaker and rad1o tuner controller, implemented with the jHackRF library (libhackrf).
|
||||||
@@ -252,110 +246,21 @@ public class HackRFTunerController extends TunerController
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads the USB serial number of the device at this controller's USB bus and port, since libhackrf opens a
|
* Reads the USB serial number of the device at this controller's USB bus and port, since libhackrf opens a
|
||||||
* specific device by serial number. The device is closed again before returning so that libhackrf can open it.
|
* specific device by serial number.
|
||||||
* @return serial number or null if the device doesn't report a serial number.
|
* @return serial number or null if the device doesn't report a serial number.
|
||||||
* @throws SourceException if the device isn't found or can't be accessed.
|
* @throws SourceException if the device isn't found or can't be accessed.
|
||||||
*/
|
*/
|
||||||
private String readSerialNumber() throws SourceException
|
private String readSerialNumber() throws SourceException
|
||||||
{
|
{
|
||||||
Context context = new Context();
|
String serial = UsbDeviceLookup.readSerialNumber(mBus, mPortAddress);
|
||||||
int status = LibUsb.init(context);
|
|
||||||
|
|
||||||
if(status != LibUsb.SUCCESS)
|
//libhackrf compares, at most, the last 32 characters of the serial string
|
||||||
|
if(serial != null && serial.length() > 32)
|
||||||
{
|
{
|
||||||
throw new SourceException("Can't initialize libusb library - " + LibUsb.errorName(status));
|
serial = serial.substring(serial.length() - 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
return serial == null || serial.isEmpty() ? null : serial;
|
||||||
{
|
|
||||||
DeviceList deviceList = new DeviceList();
|
|
||||||
int count = LibUsb.getDeviceList(context, deviceList);
|
|
||||||
|
|
||||||
if(count < 0)
|
|
||||||
{
|
|
||||||
throw new SourceException("Can't get the USB device list - " + LibUsb.errorName(count));
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for(Device device : deviceList)
|
|
||||||
{
|
|
||||||
if(LibUsb.getBusNumber(device) == mBus && LibUsb.getPortNumber(device) > 0 &&
|
|
||||||
mPortAddress != null && mPortAddress.equals(TunerManager.getPortAddress(device)))
|
|
||||||
{
|
|
||||||
return readSerialNumber(device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
LibUsb.freeDeviceList(deviceList, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
LibUsb.exit(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new SourceException("Couldn't find USB device at bus [" + mBus + "] port [" + mPortAddress + "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the serial number from the device's USB serial string descriptor.
|
|
||||||
* @param device to read
|
|
||||||
* @return serial number or null if the device doesn't have a serial string.
|
|
||||||
* @throws SourceException if the device can't be opened.
|
|
||||||
*/
|
|
||||||
private static String readSerialNumber(Device device) throws SourceException
|
|
||||||
{
|
|
||||||
DeviceDescriptor descriptor = new DeviceDescriptor();
|
|
||||||
int status = LibUsb.getDeviceDescriptor(device, descriptor);
|
|
||||||
|
|
||||||
if(status != LibUsb.SUCCESS)
|
|
||||||
{
|
|
||||||
throw new SourceException("Can't obtain tuner's device descriptor - " + LibUsb.errorName(status));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(descriptor.iSerialNumber() == 0)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
DeviceHandle handle = new DeviceHandle();
|
|
||||||
status = LibUsb.open(device, handle);
|
|
||||||
|
|
||||||
if(status == LibUsb.ERROR_ACCESS)
|
|
||||||
{
|
|
||||||
mLog.error("Access to USB tuner denied - (windows) reinstall zadig driver or (linux) blacklist driver and/or check udev rules");
|
|
||||||
throw new SourceException("access denied - if using linux, blacklist the default driver and/or install udev rules");
|
|
||||||
}
|
|
||||||
else if(status != LibUsb.SUCCESS)
|
|
||||||
{
|
|
||||||
mLog.error("Can't open USB tuner - check driver or Linux udev rules");
|
|
||||||
throw new SourceException("Can't open USB tuner - reinstall driver? - " + LibUsb.errorName(status));
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
String serial = LibUsb.getStringDescriptor(handle, descriptor.iSerialNumber());
|
|
||||||
|
|
||||||
if(serial != null)
|
|
||||||
{
|
|
||||||
serial = serial.trim();
|
|
||||||
|
|
||||||
//libhackrf compares, at most, the last 32 characters of the serial string
|
|
||||||
if(serial.length() > 32)
|
|
||||||
{
|
|
||||||
serial = serial.substring(serial.length() - 32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return serial == null || serial.isEmpty() ? null : serial;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
LibUsb.close(handle);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -34,19 +34,13 @@ import io.github.dsheirer.source.tuner.ITunerErrorListener;
|
|||||||
import io.github.dsheirer.source.tuner.TunerController;
|
import io.github.dsheirer.source.tuner.TunerController;
|
||||||
import io.github.dsheirer.source.tuner.TunerType;
|
import io.github.dsheirer.source.tuner.TunerType;
|
||||||
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.manager.TunerManager;
|
import io.github.dsheirer.source.tuner.manager.UsbDeviceLookup;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.usb4java.Context;
|
|
||||||
import org.usb4java.Device;
|
|
||||||
import org.usb4java.DeviceDescriptor;
|
|
||||||
import org.usb4java.DeviceHandle;
|
|
||||||
import org.usb4java.DeviceList;
|
|
||||||
import org.usb4java.LibUsb;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HydraSDR tuner controller, implemented with the jHydraSDR library (libhydrasdr).
|
* HydraSDR tuner controller, implemented with the jHydraSDR library (libhydrasdr).
|
||||||
@@ -209,98 +203,17 @@ public class HydraSdrTunerController extends TunerController
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads the serial number of the device at this controller's USB bus and port, since libhydrasdr opens a specific
|
* Reads the serial number of the device at this controller's USB bus and port, since libhydrasdr opens a specific
|
||||||
* device by serial number. The device is closed again before returning so that libhydrasdr can open it.
|
* device by serial number. The USB serial string is formatted as: HYDRASDR SN:0123456789ABCDEF
|
||||||
* @return serial number
|
* @return serial number
|
||||||
* @throws SourceException if the device isn't found or can't be accessed.
|
* @throws SourceException if the device isn't found or can't be accessed.
|
||||||
*/
|
*/
|
||||||
private long readSerialNumber() throws SourceException
|
private long readSerialNumber() throws SourceException
|
||||||
{
|
{
|
||||||
Context context = new Context();
|
String serial = UsbDeviceLookup.readSerialNumber(mBus, mPortAddress);
|
||||||
int status = LibUsb.init(context);
|
|
||||||
|
|
||||||
if(status != LibUsb.SUCCESS)
|
|
||||||
{
|
|
||||||
throw new SourceException("Can't initialize libusb library - " + LibUsb.errorName(status));
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
DeviceList deviceList = new DeviceList();
|
return Long.parseUnsignedLong(serial.substring(serial.lastIndexOf(':') + 1).trim(), 16);
|
||||||
int count = LibUsb.getDeviceList(context, deviceList);
|
|
||||||
|
|
||||||
if(count < 0)
|
|
||||||
{
|
|
||||||
throw new SourceException("Can't get the USB device list - " + LibUsb.errorName(count));
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for(Device device : deviceList)
|
|
||||||
{
|
|
||||||
if(LibUsb.getBusNumber(device) == mBus && LibUsb.getPortNumber(device) > 0 &&
|
|
||||||
mPortAddress != null && mPortAddress.equals(TunerManager.getPortAddress(device)))
|
|
||||||
{
|
|
||||||
return readSerialNumber(device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
LibUsb.freeDeviceList(deviceList, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
LibUsb.exit(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new SourceException("Couldn't find USB device at bus [" + mBus + "] port [" + mPortAddress + "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the serial number from the device's USB serial string, formatted as: HYDRASDR SN:0123456789ABCDEF
|
|
||||||
* @param device to read
|
|
||||||
* @return serial number
|
|
||||||
* @throws SourceException if the device can't be opened or the serial string isn't valid.
|
|
||||||
*/
|
|
||||||
private static long readSerialNumber(Device device) throws SourceException
|
|
||||||
{
|
|
||||||
DeviceDescriptor descriptor = new DeviceDescriptor();
|
|
||||||
int status = LibUsb.getDeviceDescriptor(device, descriptor);
|
|
||||||
|
|
||||||
if(status != LibUsb.SUCCESS)
|
|
||||||
{
|
|
||||||
throw new SourceException("Can't obtain tuner's device descriptor - " + LibUsb.errorName(status));
|
|
||||||
}
|
|
||||||
|
|
||||||
DeviceHandle handle = new DeviceHandle();
|
|
||||||
status = LibUsb.open(device, handle);
|
|
||||||
|
|
||||||
if(status == LibUsb.ERROR_ACCESS)
|
|
||||||
{
|
|
||||||
mLog.error("Access to USB tuner denied - (windows) reinstall zadig driver or (linux) blacklist driver and/or check udev rules");
|
|
||||||
throw new SourceException("access denied - if using linux, blacklist the default driver and/or install udev rules");
|
|
||||||
}
|
|
||||||
else if(status != LibUsb.SUCCESS)
|
|
||||||
{
|
|
||||||
mLog.error("Can't open USB tuner - check driver or Linux udev rules");
|
|
||||||
throw new SourceException("Can't open USB tuner - reinstall driver? - " + LibUsb.errorName(status));
|
|
||||||
}
|
|
||||||
|
|
||||||
String serial;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
serial = LibUsb.getStringDescriptor(handle, descriptor.iSerialNumber());
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
LibUsb.close(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return Long.parseUnsignedLong(serial.substring(serial.lastIndexOf(':') + 1), 16);
|
|
||||||
}
|
}
|
||||||
catch(NullPointerException | NumberFormatException e)
|
catch(NullPointerException | NumberFormatException e)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -19,6 +19,11 @@
|
|||||||
|
|
||||||
package io.github.dsheirer.source.tuner.limesdr;
|
package io.github.dsheirer.source.tuner.limesdr;
|
||||||
|
|
||||||
|
import io.github.dsheirer.libusb.LibUsb;
|
||||||
|
import io.github.dsheirer.libusb.UsbContext;
|
||||||
|
import io.github.dsheirer.libusb.UsbDeviceHandle;
|
||||||
|
import io.github.dsheirer.libusb.UsbDeviceInfo;
|
||||||
|
import io.github.dsheirer.libusb.UsbException;
|
||||||
import io.github.dsheirer.buffer.FloatNativeBuffer;
|
import io.github.dsheirer.buffer.FloatNativeBuffer;
|
||||||
import io.github.dsheirer.buffer.INativeBuffer;
|
import io.github.dsheirer.buffer.INativeBuffer;
|
||||||
import io.github.dsheirer.limesdr.DeviceEntry;
|
import io.github.dsheirer.limesdr.DeviceEntry;
|
||||||
@@ -38,7 +43,7 @@ import io.github.dsheirer.source.tuner.ITunerErrorListener;
|
|||||||
import io.github.dsheirer.source.tuner.TunerController;
|
import io.github.dsheirer.source.tuner.TunerController;
|
||||||
import io.github.dsheirer.source.tuner.TunerType;
|
import io.github.dsheirer.source.tuner.TunerType;
|
||||||
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.manager.TunerManager;
|
import io.github.dsheirer.source.tuner.manager.UsbDeviceLookup;
|
||||||
import io.github.dsheirer.util.ThreadPool;
|
import io.github.dsheirer.util.ThreadPool;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -49,12 +54,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.usb4java.Context;
|
|
||||||
import org.usb4java.Device;
|
|
||||||
import org.usb4java.DeviceDescriptor;
|
|
||||||
import org.usb4java.DeviceHandle;
|
|
||||||
import org.usb4java.DeviceList;
|
|
||||||
import org.usb4java.LibUsb;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LimeSDR tuner controller, implemented with the jLimeSDR library (LimeSuiteNG).
|
* LimeSDR tuner controller, implemented with the jLimeSDR library (LimeSuiteNG).
|
||||||
@@ -448,103 +447,42 @@ public class LimeSdrTunerController extends TunerController
|
|||||||
*/
|
*/
|
||||||
private UsbIdentity readUsbIdentity() throws SourceException
|
private UsbIdentity readUsbIdentity() throws SourceException
|
||||||
{
|
{
|
||||||
Context context = new Context();
|
try(UsbContext usb = UsbDeviceLookup.createContext())
|
||||||
int status = LibUsb.init(context);
|
|
||||||
|
|
||||||
if(status != LibUsb.SUCCESS)
|
|
||||||
{
|
{
|
||||||
throw new SourceException("Can't initialize libusb library - " + LibUsb.errorName(status));
|
UsbDeviceInfo device = UsbDeviceLookup.find(usb, mBus, mPortAddress);
|
||||||
}
|
String serial = null;
|
||||||
|
|
||||||
try
|
if(device.serialNumberIndex() != 0)
|
||||||
{
|
|
||||||
DeviceList deviceList = new DeviceList();
|
|
||||||
int count = LibUsb.getDeviceList(context, deviceList);
|
|
||||||
|
|
||||||
if(count < 0)
|
|
||||||
{
|
{
|
||||||
throw new SourceException("Can't get the USB device list - " + LibUsb.errorName(count));
|
try(UsbDeviceHandle handle = usb.open(device))
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for(Device device : deviceList)
|
|
||||||
{
|
|
||||||
if(LibUsb.getBusNumber(device) == mBus && LibUsb.getPortNumber(device) > 0 &&
|
|
||||||
mPortAddress != null && mPortAddress.equals(TunerManager.getPortAddress(device)))
|
|
||||||
{
|
|
||||||
return readUsbIdentity(device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
LibUsb.freeDeviceList(deviceList, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
LibUsb.exit(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new SourceException("Couldn't find USB device at bus [" + mBus + "] port [" + mPortAddress + "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the vendor ID, product ID and serial number of the USB device.
|
|
||||||
*/
|
|
||||||
private static UsbIdentity readUsbIdentity(Device device) throws SourceException
|
|
||||||
{
|
|
||||||
DeviceDescriptor descriptor = new DeviceDescriptor();
|
|
||||||
int status = LibUsb.getDeviceDescriptor(device, descriptor);
|
|
||||||
|
|
||||||
if(status != LibUsb.SUCCESS)
|
|
||||||
{
|
|
||||||
throw new SourceException("Can't obtain tuner's device descriptor - " + LibUsb.errorName(status));
|
|
||||||
}
|
|
||||||
|
|
||||||
int vendor = descriptor.idVendor() & 0xFFFF;
|
|
||||||
int product = descriptor.idProduct() & 0xFFFF;
|
|
||||||
String serial = null;
|
|
||||||
|
|
||||||
if(descriptor.iSerialNumber() != 0)
|
|
||||||
{
|
|
||||||
DeviceHandle handle = new DeviceHandle();
|
|
||||||
status = LibUsb.open(device, handle);
|
|
||||||
|
|
||||||
if(status == LibUsb.SUCCESS)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
//LimeSuiteNG aborts the application when it opens a device that another application is using,
|
//LimeSuiteNG aborts the application when it opens a device that another application is using,
|
||||||
//since the open throws a C++ exception through its C API. Check that the device is free first.
|
//since the open throws a C++ exception through its C API. Check that the device is free first.
|
||||||
status = LibUsb.claimInterface(handle, 0);
|
try
|
||||||
|
|
||||||
if(status == LibUsb.ERROR_BUSY)
|
|
||||||
{
|
{
|
||||||
throw new SourceException("LimeSDR is in use by another application");
|
handle.claimInterface(0);
|
||||||
|
handle.releaseInterface(0);
|
||||||
}
|
}
|
||||||
else if(status == LibUsb.SUCCESS)
|
catch(UsbException e)
|
||||||
{
|
{
|
||||||
LibUsb.releaseInterface(handle, 0);
|
if(e.code() == UsbDeviceLookup.ERROR_BUSY)
|
||||||
|
{
|
||||||
|
throw new SourceException("LimeSDR is in use by another application");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
serial = LibUsb.getStringDescriptor(handle, descriptor.iSerialNumber());
|
serial = handle.readString(device.serialNumberIndex());
|
||||||
serial = serial != null && !serial.isBlank() ? serial.trim() : null;
|
serial = serial != null && !serial.isBlank() ? serial : null;
|
||||||
}
|
}
|
||||||
finally
|
catch(UsbException e)
|
||||||
{
|
{
|
||||||
LibUsb.close(handle);
|
//Expected on Windows, where libusb can't open a device that uses the Cypress or FTDI driver
|
||||||
|
mLog.debug("Can't open LimeSDR to read its USB serial number - " + LibUsb.errorName(e.code()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
//Expected on Windows, where libusb can't open a device that uses the Cypress or FTDI driver
|
|
||||||
mLog.debug("Can't open LimeSDR to read its USB serial number - " + LibUsb.errorName(status));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new UsbIdentity(vendor, product, serial);
|
return new UsbIdentity(device.vendorId(), device.productId(), serial);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -20,6 +20,13 @@
|
|||||||
package io.github.dsheirer.source.tuner.manager;
|
package io.github.dsheirer.source.tuner.manager;
|
||||||
|
|
||||||
import io.github.dsheirer.gui.preference.tuner.RspDuoSelectionMode;
|
import io.github.dsheirer.gui.preference.tuner.RspDuoSelectionMode;
|
||||||
|
import io.github.dsheirer.libusb.LibUsb;
|
||||||
|
import io.github.dsheirer.libusb.UsbContext;
|
||||||
|
import io.github.dsheirer.libusb.UsbDeviceInfo;
|
||||||
|
import io.github.dsheirer.libusb.UsbException;
|
||||||
|
import io.github.dsheirer.libusb.UsbHotplug;
|
||||||
|
import io.github.dsheirer.libusb.UsbHotplugListener;
|
||||||
|
import io.github.dsheirer.libusb.UsbLogLevel;
|
||||||
import io.github.dsheirer.preference.UserPreferences;
|
import io.github.dsheirer.preference.UserPreferences;
|
||||||
import io.github.dsheirer.preference.source.ChannelizerType;
|
import io.github.dsheirer.preference.source.ChannelizerType;
|
||||||
import io.github.dsheirer.sdrplay.DeviceInfo;
|
import io.github.dsheirer.sdrplay.DeviceInfo;
|
||||||
@@ -45,22 +52,13 @@ import io.github.dsheirer.source.tuner.sdrplay.DiscoveredRspTuner;
|
|||||||
import io.github.dsheirer.source.tuner.sdrplay.rspDuo.DiscoveredRspDuoTuner1;
|
import io.github.dsheirer.source.tuner.sdrplay.rspDuo.DiscoveredRspDuoTuner1;
|
||||||
import io.github.dsheirer.source.tuner.ui.DiscoveredTunerModel;
|
import io.github.dsheirer.source.tuner.ui.DiscoveredTunerModel;
|
||||||
import io.github.dsheirer.util.ThreadPool;
|
import io.github.dsheirer.util.ThreadPool;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.usb4java.Context;
|
|
||||||
import org.usb4java.Device;
|
|
||||||
import org.usb4java.DeviceDescriptor;
|
|
||||||
import org.usb4java.DeviceList;
|
|
||||||
import org.usb4java.HotplugCallback;
|
|
||||||
import org.usb4java.HotplugCallbackHandle;
|
|
||||||
import org.usb4java.LibUsb;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tuner manager provides access to tuners using USB, recording, sound-card and system-daemon accessible devices. This
|
* Tuner manager provides access to tuners using USB, recording, sound-card and system-daemon accessible devices. This
|
||||||
@@ -73,9 +71,12 @@ public class TunerManager implements IDiscoveredTunerStatusListener
|
|||||||
private final UserPreferences mUserPreferences;
|
private final UserPreferences mUserPreferences;
|
||||||
private final DiscoveredTunerModel mDiscoveredTunerModel;
|
private final DiscoveredTunerModel mDiscoveredTunerModel;
|
||||||
private final TunerConfigurationManager mTunerConfigurationManager;
|
private final TunerConfigurationManager mTunerConfigurationManager;
|
||||||
|
/**
|
||||||
|
* libusb error code for an unspecified error (LIBUSB_ERROR_OTHER)
|
||||||
|
*/
|
||||||
|
private static final int LIBUSB_ERROR_OTHER = -99;
|
||||||
private final HotplugEventSupport mHotplugEventSupport = new HotplugEventSupport();
|
private final HotplugEventSupport mHotplugEventSupport = new HotplugEventSupport();
|
||||||
private final Context mLibUsbApplicationContext = new Context();
|
private boolean mLibUsbAvailable = false;
|
||||||
private boolean mLibUsbInitialized = false;
|
|
||||||
private SDRplay mSDRplay;
|
private SDRplay mSDRplay;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -112,60 +113,26 @@ public class TunerManager implements IDiscoveredTunerStatusListener
|
|||||||
{
|
{
|
||||||
mLog.info("Discovering tuners ...");
|
mLog.info("Discovering tuners ...");
|
||||||
|
|
||||||
boolean libUsbAvailable = false;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
int api = LibUsb.getApiVersion();
|
//Loads the libusb library that jlibusb bundles, which the USB tuner libraries share
|
||||||
libUsbAvailable = true;
|
mLog.info("LibUsb Version: " + LibUsb.version() + " - hotplug detection: " +
|
||||||
mLog.info("LibUsb API Version: " + ((api >> 24) & 0xFF) + "." + ((api >> 16) & 0xFF) + "." + (api & 0xFFFF));
|
(LibUsb.hasHotplug() ? "native" : "polling"));
|
||||||
mLog.info("LibUsb Version: " + LibUsb.getVersion());
|
mLibUsbAvailable = true;
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(LinkageError | RuntimeException e)
|
||||||
{
|
{
|
||||||
final String os = System.getProperty("os.name").toLowerCase();
|
String os = System.getProperty("os.name");
|
||||||
final String version = System.getProperty("os.version");
|
String version = System.getProperty("os.version");
|
||||||
|
mLog.error("Unable to load the libusb library for communication with USB tuner(s) on os [" + os +
|
||||||
//Special handling for MacOS Tahoe version 26 issue with LibUsb.
|
"] version [" + version + "]. To use a different libusb build, start sdrtrunk with " +
|
||||||
if(os != null && os.startsWith("macos") && version != null && version.startsWith("26"))
|
"-Djlibusb.library.path=<directory containing libusb>", e);
|
||||||
{
|
|
||||||
mLog.warn("Unable to load libusb library for communication with USB tuner(s)");
|
|
||||||
mLog.warn("Mac OS Tahoe [" + os + "] version [" + version + "] detected. There is an issue");
|
|
||||||
mLog.warn("with libusb for this OS version. You might consider manually installing the latest");
|
|
||||||
mLog.warn("version of the libusb library. In a terminal window type:");
|
|
||||||
mLog.warn("");
|
|
||||||
mLog.warn("\t\tbrew install libusb --HEAD");
|
|
||||||
mLog.warn("");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mLog.error("Error while loading libusb for os [" + os + "] version [" + version + "]", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(libUsbAvailable)
|
if(mLibUsbAvailable)
|
||||||
{
|
{
|
||||||
try
|
discoverUSBTuners();
|
||||||
{
|
mHotplugEventSupport.start();
|
||||||
int status = LibUsb.init(mLibUsbApplicationContext);
|
|
||||||
|
|
||||||
if(status == LibUsb.SUCCESS)
|
|
||||||
{
|
|
||||||
mLibUsbInitialized = true;
|
|
||||||
// LibUsb.setOption(mLibUsbApplicationContext, LibUsb.OPTION_LOG_LEVEL, LibUsb.LOG_LEVEL_DEBUG);
|
|
||||||
discoverUSBTuners();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
mLog.error("Error initializing LibUsb and usb4java library", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(mLibUsbInitialized)
|
|
||||||
{
|
|
||||||
mHotplugEventSupport.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
discoverSdrPlayTuners();
|
discoverSdrPlayTuners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,19 +144,14 @@ public class TunerManager implements IDiscoveredTunerStatusListener
|
|||||||
*/
|
*/
|
||||||
public void stop()
|
public void stop()
|
||||||
{
|
{
|
||||||
|
//Stop hotplug detection first so that no tuner is added or removed while the tuners are released
|
||||||
|
mHotplugEventSupport.stop();
|
||||||
|
|
||||||
//Stop all tuners
|
//Stop all tuners
|
||||||
mDiscoveredTunerModel.releaseDiscoveredTuners();
|
mDiscoveredTunerModel.releaseDiscoveredTuners();
|
||||||
|
|
||||||
//Shutdown SDRplay API instance
|
//Shutdown SDRplay API instance
|
||||||
closeSDRplay();
|
closeSDRplay();
|
||||||
|
|
||||||
//Shutdown LibUsb
|
|
||||||
if(mLibUsbInitialized)
|
|
||||||
{
|
|
||||||
mHotplugEventSupport.stop();
|
|
||||||
LibUsb.exit(mLibUsbApplicationContext);
|
|
||||||
mLibUsbInitialized = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -205,101 +167,123 @@ public class TunerManager implements IDiscoveredTunerStatusListener
|
|||||||
*/
|
*/
|
||||||
private void discoverUSBTuners()
|
private void discoverUSBTuners()
|
||||||
{
|
{
|
||||||
if(mLibUsbInitialized)
|
List<DiscoveredUSBTuner> discoveredUSBTuners = new ArrayList<>();
|
||||||
|
|
||||||
|
try(UsbContext usb = UsbContext.create())
|
||||||
{
|
{
|
||||||
List<DiscoveredUSBTuner> discoveredUSBTuners = new ArrayList<>();
|
List<UsbDeviceInfo> devices = getDeviceList(usb);
|
||||||
|
|
||||||
try
|
if(devices != null)
|
||||||
{
|
{
|
||||||
DeviceList deviceList = new DeviceList();
|
mLog.info("LibUsb - discovered [" + devices.size() + "] potential usb devices");
|
||||||
int deviceCount = LibUsb.getDeviceList(mLibUsbApplicationContext, deviceList);
|
|
||||||
|
|
||||||
if(deviceCount >= 0)
|
for(UsbDeviceInfo device: devices)
|
||||||
{
|
{
|
||||||
mLog.info("LibUsb - discovered [" + deviceCount + "] potential usb devices");
|
DiscoveredUSBTuner discoveredUSBTuner = createDiscoveredTuner(device);
|
||||||
|
|
||||||
for(Device device: deviceList)
|
if(discoveredUSBTuner != null)
|
||||||
{
|
{
|
||||||
int bus = LibUsb.getBusNumber(device);
|
mLog.info("Discovered tuner at USB Bus [" + discoveredUSBTuner.getBus() + "] Port [" +
|
||||||
int port = LibUsb.getPortNumber(device);
|
discoveredUSBTuner.getPortAddress() + "] Tuner Class [" +
|
||||||
|
discoveredUSBTuner.getTunerClass() + "]");
|
||||||
if(port > 0)
|
discoveredUSBTuners.add(discoveredUSBTuner);
|
||||||
{
|
|
||||||
DeviceDescriptor deviceDescriptor = new DeviceDescriptor();
|
|
||||||
int status = LibUsb.getDeviceDescriptor(device, deviceDescriptor);
|
|
||||||
|
|
||||||
if(status == LibUsb.SUCCESS)
|
|
||||||
{
|
|
||||||
TunerClass tunerClass = TunerClass.lookup(deviceDescriptor.idVendor(),
|
|
||||||
deviceDescriptor.idProduct());
|
|
||||||
|
|
||||||
String portAddress = getPortAddress(device);
|
|
||||||
|
|
||||||
if(tunerClass.isSupportedUsbTuner())
|
|
||||||
{
|
|
||||||
mLog.info("Discovered tuner at USB Bus [" + bus + "] Port [" + portAddress +
|
|
||||||
"] Tuner Class [" + tunerClass + "]");
|
|
||||||
ChannelizerType channelizerType = mUserPreferences.getTunerPreference().getChannelizerType();
|
|
||||||
DiscoveredUSBTuner discoveredUSBTuner = new DiscoveredUSBTuner(tunerClass, bus,
|
|
||||||
portAddress, channelizerType);
|
|
||||||
discoveredUSBTuners.add(discoveredUSBTuner);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mLog.error("LibUsb - unable to get device descriptor for device on bus [" + bus +
|
|
||||||
"] port [" + port + "] - status [" + status + "] - " + LibUsb.errorName(status));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Unref the device - it will be rediscovered under the device context when it is started
|
|
||||||
LibUsb.unrefDevice(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
LibUsb.freeDeviceList(deviceList, false);
|
|
||||||
}
|
|
||||||
//If the get device list operation generated error -99, turn on warning logging and try device list
|
|
||||||
// again to see what's causing it.
|
|
||||||
else if(deviceCount == LibUsb.ERROR_OTHER)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
mLog.info("LibUsb reported error -99 during get device list - increasing logging level and retrying");
|
|
||||||
LibUsb.setOption(mLibUsbApplicationContext, LibUsb.OPTION_LOG_LEVEL, LibUsb.LOG_LEVEL_WARNING);
|
|
||||||
deviceCount = LibUsb.getDeviceList(mLibUsbApplicationContext, deviceList);
|
|
||||||
if(deviceCount >=0)
|
|
||||||
{
|
|
||||||
mLog.info("LibUsb - get device list count [" + deviceCount + "] during retry");
|
|
||||||
LibUsb.freeDeviceList(deviceList, false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mLog.info("LibUsb - get device list reported error [" + deviceCount + "/" +
|
|
||||||
LibUsb.errorName(deviceCount) + "] during retry");
|
|
||||||
}
|
|
||||||
LibUsb.setOption(mLibUsbApplicationContext, LibUsb.OPTION_LOG_LEVEL, LibUsb.LOG_LEVEL_NONE);
|
|
||||||
mLog.info("LibUse - get device list reattempt complete - if additional logging was generated please notify developer");
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
mLog.info("LibUsb error during get device list reattempt", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
mLog.error("LibUsb - error [" + deviceCount + "/" + LibUsb.errorName(deviceCount) + "] during get device list");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
mLog.error("LibUsb - error during USB device discovery", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(DiscoveredUSBTuner discoveredUSBTuner: discoveredUSBTuners)
|
|
||||||
{
|
|
||||||
addUsbTuner(discoveredUSBTuner);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch(UsbException | RuntimeException e)
|
||||||
|
{
|
||||||
|
mLog.error("LibUsb - error during USB device discovery", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(DiscoveredUSBTuner discoveredUSBTuner: discoveredUSBTuners)
|
||||||
|
{
|
||||||
|
addUsbTuner(discoveredUSBTuner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists the USB devices. If listing fails with error -99, retries once with libusb warning logging turned on so
|
||||||
|
* that the log shows what caused it.
|
||||||
|
* @param usb context to list devices from
|
||||||
|
* @return devices, or null if they couldn't be listed
|
||||||
|
*/
|
||||||
|
private List<UsbDeviceInfo> getDeviceList(UsbContext usb)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return usb.devices();
|
||||||
|
}
|
||||||
|
catch(UsbException e)
|
||||||
|
{
|
||||||
|
if(e.code() != LIBUSB_ERROR_OTHER)
|
||||||
|
{
|
||||||
|
mLog.error("LibUsb - error during get device list - " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mLog.info("LibUsb reported error -99 during get device list - increasing logging level and retrying");
|
||||||
|
usb.setLogLevel(UsbLogLevel.WARNING);
|
||||||
|
List<UsbDeviceInfo> devices = usb.devices();
|
||||||
|
mLog.info("LibUsb - get device list count [" + devices.size() + "] during retry");
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
catch(UsbException e)
|
||||||
|
{
|
||||||
|
mLog.info("LibUsb - get device list reported error during retry - " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
usb.setLogLevel(UsbLogLevel.NONE);
|
||||||
|
}
|
||||||
|
catch(UsbException e)
|
||||||
|
{
|
||||||
|
//Logging level only - nothing else to do
|
||||||
|
}
|
||||||
|
|
||||||
|
mLog.info("LibUsb - get device list reattempt complete - if additional logging was generated please notify developer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifies the USB tuner class of a device.
|
||||||
|
* @param device to identify
|
||||||
|
* @return tuner class, or null for a root hub or a device that isn't a supported tuner
|
||||||
|
*/
|
||||||
|
private static TunerClass getSupportedTunerClass(UsbDeviceInfo device)
|
||||||
|
{
|
||||||
|
//A root hub has no port numbers
|
||||||
|
if(device.portNumbers().isEmpty())
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
TunerClass tunerClass = TunerClass.lookup((short)device.vendorId(), (short)device.productId());
|
||||||
|
return tunerClass.isSupportedUsbTuner() ? tunerClass : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a discovered tuner for a USB device.
|
||||||
|
* @param device to create a tuner for
|
||||||
|
* @return discovered tuner, or null if the device isn't a supported tuner
|
||||||
|
*/
|
||||||
|
private DiscoveredUSBTuner createDiscoveredTuner(UsbDeviceInfo device)
|
||||||
|
{
|
||||||
|
TunerClass tunerClass = getSupportedTunerClass(device);
|
||||||
|
|
||||||
|
if(tunerClass == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChannelizerType channelizerType = mUserPreferences.getTunerPreference().getChannelizerType();
|
||||||
|
return new DiscoveredUSBTuner(tunerClass, device.busNumber(), UsbPortAddress.of(device), channelizerType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -680,161 +664,104 @@ public class TunerManager implements IDiscoveredTunerStatusListener
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a dotted-notation string representing the port numbers for the device from the root hub through any
|
* USB hotplug support to detect when USB tuners are added or removed. jlibusb reports changes as they happen
|
||||||
* intermediate hubs.
|
* where libusb supports hotplug (Linux and macOS) and polls the USB device list elsewhere (Windows).
|
||||||
* @param device to inspect
|
|
||||||
* @return formatted address
|
|
||||||
*/
|
|
||||||
public static String getPortAddress(Device device)
|
|
||||||
{
|
|
||||||
ByteBuffer portNumberArray = ByteBuffer.allocateDirect(7); //7 ports maximum.
|
|
||||||
int portCount = LibUsb.getPortNumbers(device, portNumberArray);
|
|
||||||
portNumberArray.rewind();
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
|
|
||||||
for(int x = 0; x < portCount; x++)
|
|
||||||
{
|
|
||||||
if(x > 0)
|
|
||||||
{
|
|
||||||
sb.append(".");
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.append(portNumberArray.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* USB hotplug event listener to register, unregister and detect when USB devices are added or removed.
|
|
||||||
*
|
*
|
||||||
* Note: hotplug is not supported on all platforms.
|
* Tuners already connected when detection starts are reported as arrivals too. Those were added during discovery
|
||||||
|
* at startup and are ignored here because the tuner model already has a tuner at that bus and port.
|
||||||
*/
|
*/
|
||||||
public class HotplugEventSupport implements HotplugCallback
|
public class HotplugEventSupport implements UsbHotplugListener
|
||||||
{
|
{
|
||||||
private static final int HOTPLUG_CONTINUE_EVENT_SUPPORT = 0;
|
private UsbHotplug mUsbHotplug;
|
||||||
private HotplugCallbackHandle mHotplugCallbackHandle;
|
|
||||||
private ScheduledFuture<?> mEventProcessorFuture;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LibUsb hotplug event notification
|
* Starts hotplug detection of supported USB tuners.
|
||||||
* @param context used for hotplug event registration
|
|
||||||
* @param device that arrived or left
|
|
||||||
* @param event value, arrived event or left event
|
|
||||||
* @param userData not used
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public int processEvent(Context context, Device device, int event, Object userData)
|
|
||||||
{
|
|
||||||
int bus = LibUsb.getBusNumber(device);
|
|
||||||
int port = LibUsb.getPortNumber(device);
|
|
||||||
|
|
||||||
if(port > 0)
|
|
||||||
{
|
|
||||||
String portAddress = getPortAddress(device);
|
|
||||||
|
|
||||||
switch(event)
|
|
||||||
{
|
|
||||||
case LibUsb.HOTPLUG_EVENT_DEVICE_ARRIVED:
|
|
||||||
DeviceDescriptor deviceDescriptor = new DeviceDescriptor();
|
|
||||||
int status = LibUsb.getDeviceDescriptor(device, deviceDescriptor);
|
|
||||||
|
|
||||||
if(status == LibUsb.SUCCESS)
|
|
||||||
{
|
|
||||||
TunerClass tunerClass = TunerClass.lookup(deviceDescriptor.idVendor(), deviceDescriptor.idProduct());
|
|
||||||
|
|
||||||
if(tunerClass.isSupportedUsbTuner())
|
|
||||||
{
|
|
||||||
mLog.info("Tuner plug-in detected at USB Bus [" + bus + "] Port [" + port +
|
|
||||||
"] Tuner Class [" + tunerClass + "]");
|
|
||||||
ChannelizerType channelizerType = mUserPreferences.getTunerPreference().getChannelizerType();
|
|
||||||
DiscoveredUSBTuner discoveredUSBTuner = new DiscoveredUSBTuner(tunerClass, bus,
|
|
||||||
portAddress, channelizerType);
|
|
||||||
|
|
||||||
if(tunerClass.isFuncubeTuner())
|
|
||||||
{
|
|
||||||
//Funcube tuners take a few moments to init the sound card interface. Delay adding
|
|
||||||
//the tuner so that it can be started correctly.
|
|
||||||
ThreadPool.SCHEDULED.schedule(() ->
|
|
||||||
{
|
|
||||||
addUsbTuner(discoveredUSBTuner);
|
|
||||||
}, 2, TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
addUsbTuner(discoveredUSBTuner);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case LibUsb.HOTPLUG_EVENT_DEVICE_LEFT:
|
|
||||||
DiscoveredTuner removed = removeUsbTuner(bus, portAddress);
|
|
||||||
|
|
||||||
if(removed != null)
|
|
||||||
{
|
|
||||||
mLog.info("Tuner Unplugged: " + removed.getId());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return HOTPLUG_CONTINUE_EVENT_SUPPORT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers for hotplug event notifications, if the capability is supported on this platform and starts an
|
|
||||||
* event processing timer to process hotplug events.
|
|
||||||
*/
|
*/
|
||||||
public void start()
|
public void start()
|
||||||
{
|
{
|
||||||
if(LibUsb.hasCapability(LibUsb.CAP_HAS_HOTPLUG))
|
try
|
||||||
{
|
{
|
||||||
mHotplugCallbackHandle = new HotplugCallbackHandle();
|
mUsbHotplug = UsbHotplug.start(device -> getSupportedTunerClass(device) != null, this);
|
||||||
int events = LibUsb.HOTPLUG_EVENT_DEVICE_ARRIVED | LibUsb.HOTPLUG_EVENT_DEVICE_LEFT;
|
mLog.info("LibUsb - started USB hotplug detection (" +
|
||||||
int flags = LibUsb.HOTPLUG_ENUMERATE;
|
(mUsbHotplug.mode() == UsbHotplug.Mode.NATIVE ? "native" : "polling") + ")");
|
||||||
int vendorId = LibUsb.HOTPLUG_MATCH_ANY;
|
|
||||||
int productId = LibUsb.HOTPLUG_MATCH_ANY;
|
|
||||||
int deviceClass = LibUsb.HOTPLUG_MATCH_ANY;
|
|
||||||
|
|
||||||
mLog.info("LibUsb - starting HotPlug detection");
|
|
||||||
int status = LibUsb.hotplugRegisterCallback(mLibUsbApplicationContext, events, flags, vendorId,
|
|
||||||
productId, deviceClass, this, "sdrtrunk hotplug support", mHotplugCallbackHandle);
|
|
||||||
|
|
||||||
if(status == LibUsb.SUCCESS)
|
|
||||||
{
|
|
||||||
Runnable eventHandler = () -> LibUsb.handleEvents(mLibUsbApplicationContext);
|
|
||||||
mEventProcessorFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(eventHandler,
|
|
||||||
0, 1, TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mHotplugCallbackHandle = null;
|
|
||||||
mLog.info("LibUsb - unable to register device hotplug listener - " + LibUsb.errorName(status));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
catch(UsbException | RuntimeException e)
|
||||||
{
|
{
|
||||||
mLog.info("LibUsb Hotplug event notification Is Not Supported on this platform.");
|
mLog.error("LibUsb - unable to start USB hotplug detection", e);
|
||||||
|
mUsbHotplug = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregisters from hotplug event notifications and stops the event processing timer
|
* Stops hotplug detection. Waits for an arrival or removal that is being processed to finish.
|
||||||
*/
|
*/
|
||||||
public void stop()
|
public void stop()
|
||||||
{
|
{
|
||||||
if(mHotplugCallbackHandle != null)
|
if(mUsbHotplug != null)
|
||||||
{
|
{
|
||||||
LibUsb.hotplugDeregisterCallback(mLibUsbApplicationContext, mHotplugCallbackHandle);
|
mUsbHotplug.close();
|
||||||
mHotplugCallbackHandle = null;
|
mUsbHotplug = null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(mEventProcessorFuture != null)
|
/**
|
||||||
|
* A supported USB tuner was connected. Called on the jlibusb hotplug dispatch thread.
|
||||||
|
* @param device that arrived
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void arrived(UsbDeviceInfo device)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
mEventProcessorFuture.cancel(true);
|
DiscoveredUSBTuner discoveredUSBTuner = createDiscoveredTuner(device);
|
||||||
mEventProcessorFuture = null;
|
|
||||||
|
if(discoveredUSBTuner == null ||
|
||||||
|
mDiscoveredTunerModel.hasUsbTuner(discoveredUSBTuner.getBus(), discoveredUSBTuner.getPortAddress()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mLog.info("Tuner plug-in detected at USB Bus [" + discoveredUSBTuner.getBus() + "] Port [" +
|
||||||
|
discoveredUSBTuner.getPortAddress() + "] Tuner Class [" + discoveredUSBTuner.getTunerClass() + "]");
|
||||||
|
|
||||||
|
if(discoveredUSBTuner.getTunerClass().isFuncubeTuner())
|
||||||
|
{
|
||||||
|
//Funcube tuners take a few moments to init the sound card interface. Delay adding
|
||||||
|
//the tuner so that it can be started correctly.
|
||||||
|
ThreadPool.SCHEDULED.schedule(() -> addUsbTuner(discoveredUSBTuner), 2, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
addUsbTuner(discoveredUSBTuner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
mLog.error("Error adding USB tuner after plug-in at USB Bus [" + device.busNumber() + "] Port [" +
|
||||||
|
UsbPortAddress.of(device) + "]", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A supported USB tuner was disconnected. Called on the jlibusb hotplug dispatch thread.
|
||||||
|
* @param device that left
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void left(UsbDeviceInfo device)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DiscoveredTuner removed = removeUsbTuner(device.busNumber(), UsbPortAddress.of(device));
|
||||||
|
|
||||||
|
if(removed != null)
|
||||||
|
{
|
||||||
|
mLog.info("Tuner Unplugged: " + removed.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
mLog.error("Error removing USB tuner after unplug at USB Bus [" + device.busNumber() + "] Port [" +
|
||||||
|
UsbPortAddress.of(device) + "]", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,195 @@
|
|||||||
|
/*
|
||||||
|
* *****************************************************************************
|
||||||
|
* Copyright (C) 2014-2026 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
* ****************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.github.dsheirer.source.tuner.manager;
|
||||||
|
|
||||||
|
import io.github.dsheirer.libusb.LibUsb;
|
||||||
|
import io.github.dsheirer.libusb.UsbContext;
|
||||||
|
import io.github.dsheirer.libusb.UsbDeviceHandle;
|
||||||
|
import io.github.dsheirer.libusb.UsbDeviceInfo;
|
||||||
|
import io.github.dsheirer.libusb.UsbException;
|
||||||
|
import io.github.dsheirer.source.SourceException;
|
||||||
|
import java.util.List;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the USB device at a tuner's bus and port address and reads its USB identity through jlibusb.
|
||||||
|
*
|
||||||
|
* The tuner libraries open a device by serial number or by index rather than by bus and port, so a tuner controller
|
||||||
|
* uses this to identify the device that the tuner manager discovered at a bus and port before handing it to its
|
||||||
|
* library. Devices are closed again before returning so that the library can open them.
|
||||||
|
*/
|
||||||
|
public final class UsbDeviceLookup
|
||||||
|
{
|
||||||
|
private static final Logger mLog = LoggerFactory.getLogger(UsbDeviceLookup.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* libusb error code when the user doesn't have permission to open the device (LIBUSB_ERROR_ACCESS)
|
||||||
|
*/
|
||||||
|
public static final int ERROR_ACCESS = -3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* libusb error code when another program or driver has claimed the interface (LIBUSB_ERROR_BUSY)
|
||||||
|
*/
|
||||||
|
public static final int ERROR_BUSY = -6;
|
||||||
|
|
||||||
|
private UsbDeviceLookup()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a libusb context. Close it when done.
|
||||||
|
* @throws SourceException if libusb can't be initialized
|
||||||
|
*/
|
||||||
|
public static UsbContext createContext() throws SourceException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return UsbContext.create();
|
||||||
|
}
|
||||||
|
catch(UsbException e)
|
||||||
|
{
|
||||||
|
throw new SourceException("Can't initialize libusb library - " + LibUsb.errorName(e.code()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists the USB devices, in libusb device list order.
|
||||||
|
* @param usb context
|
||||||
|
* @throws SourceException if the device list isn't available
|
||||||
|
*/
|
||||||
|
public static List<UsbDeviceInfo> devices(UsbContext usb) throws SourceException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return usb.devices();
|
||||||
|
}
|
||||||
|
catch(UsbException e)
|
||||||
|
{
|
||||||
|
throw new SourceException("Can't get the USB device list - " + LibUsb.errorName(e.code()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if the device is at the bus and port address.
|
||||||
|
* @param device to test
|
||||||
|
* @param bus number
|
||||||
|
* @param portAddress in dotted notation, such as 2.3
|
||||||
|
*/
|
||||||
|
public static boolean isAt(UsbDeviceInfo device, int bus, String portAddress)
|
||||||
|
{
|
||||||
|
return device.busNumber() == bus && !device.portNumbers().isEmpty() && portAddress != null &&
|
||||||
|
portAddress.equals(UsbPortAddress.of(device));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the device at the bus and port address.
|
||||||
|
* @param usb context
|
||||||
|
* @param bus number
|
||||||
|
* @param portAddress in dotted notation, such as 2.3
|
||||||
|
* @throws SourceException if no device is at that bus and port
|
||||||
|
*/
|
||||||
|
public static UsbDeviceInfo find(UsbContext usb, int bus, String portAddress) throws SourceException
|
||||||
|
{
|
||||||
|
for(UsbDeviceInfo device: devices(usb))
|
||||||
|
{
|
||||||
|
if(isAt(device, bus, portAddress))
|
||||||
|
{
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new SourceException("Couldn't find USB device at bus [" + bus + "] port [" + portAddress + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the device at the bus and port address.
|
||||||
|
* @param bus number
|
||||||
|
* @param portAddress in dotted notation, such as 2.3
|
||||||
|
* @throws SourceException if no device is at that bus and port
|
||||||
|
*/
|
||||||
|
public static UsbDeviceInfo find(int bus, String portAddress) throws SourceException
|
||||||
|
{
|
||||||
|
try(UsbContext usb = createContext())
|
||||||
|
{
|
||||||
|
return find(usb, bus, portAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the device. Close the handle before the context.
|
||||||
|
* @param usb context
|
||||||
|
* @param device to open
|
||||||
|
* @throws SourceException if the device can't be opened, logging the likely cause
|
||||||
|
*/
|
||||||
|
public static UsbDeviceHandle open(UsbContext usb, UsbDeviceInfo device) throws SourceException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return usb.open(device);
|
||||||
|
}
|
||||||
|
catch(UsbException e)
|
||||||
|
{
|
||||||
|
if(e.code() == ERROR_ACCESS)
|
||||||
|
{
|
||||||
|
mLog.error("Access to USB tuner denied - (windows) reinstall zadig driver or (linux) blacklist driver and/or check udev rules");
|
||||||
|
throw new SourceException("access denied - if using linux, blacklist the default driver and/or install udev rules");
|
||||||
|
}
|
||||||
|
|
||||||
|
mLog.error("Can't open USB tuner - check driver or Linux udev rules");
|
||||||
|
throw new SourceException("Can't open USB tuner - reinstall driver? - " + LibUsb.errorName(e.code()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the device's USB serial string.
|
||||||
|
* @param usb context
|
||||||
|
* @param device to read
|
||||||
|
* @return serial, or null if the device doesn't have a serial string or it can't be read
|
||||||
|
* @throws SourceException if the device has a serial string but can't be opened
|
||||||
|
*/
|
||||||
|
public static String readSerialNumber(UsbContext usb, UsbDeviceInfo device) throws SourceException
|
||||||
|
{
|
||||||
|
if(device.serialNumberIndex() == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try(UsbDeviceHandle handle = open(usb, device))
|
||||||
|
{
|
||||||
|
return handle.readString(device.serialNumberIndex());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the USB serial string of the device at the bus and port address.
|
||||||
|
* @param bus number
|
||||||
|
* @param portAddress in dotted notation, such as 2.3
|
||||||
|
* @return serial, or null if the device doesn't have a serial string or it can't be read
|
||||||
|
* @throws SourceException if no device is at that bus and port, or it can't be opened
|
||||||
|
*/
|
||||||
|
public static String readSerialNumber(int bus, String portAddress) throws SourceException
|
||||||
|
{
|
||||||
|
try(UsbContext usb = createContext())
|
||||||
|
{
|
||||||
|
return readSerialNumber(usb, find(usb, bus, portAddress));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* *****************************************************************************
|
||||||
|
* Copyright (C) 2014-2026 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
* ****************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.github.dsheirer.source.tuner.manager;
|
||||||
|
|
||||||
|
import io.github.dsheirer.libusb.UsbDeviceInfo;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats the USB port address of a device: the port numbers from the root hub through any intermediate hubs, in
|
||||||
|
* dotted notation such as 2.3. Together with the bus number it identifies the physical USB socket of a tuner.
|
||||||
|
*
|
||||||
|
* The tuner manager discovers tuners at this address and the tuner controllers find their device again by it, so it
|
||||||
|
* is formatted in one place.
|
||||||
|
*/
|
||||||
|
public final class UsbPortAddress
|
||||||
|
{
|
||||||
|
private UsbPortAddress()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Port address of a device.
|
||||||
|
* @param device to inspect
|
||||||
|
* @return formatted address, or an empty string for a root hub
|
||||||
|
*/
|
||||||
|
public static String of(UsbDeviceInfo device)
|
||||||
|
{
|
||||||
|
return format(device.portNumbers());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String format(List<Integer> ports)
|
||||||
|
{
|
||||||
|
return ports.stream().map(String::valueOf).collect(Collectors.joining("."));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,10 @@
|
|||||||
*/
|
*/
|
||||||
package io.github.dsheirer.source.tuner.rtl;
|
package io.github.dsheirer.source.tuner.rtl;
|
||||||
|
|
||||||
|
import io.github.dsheirer.libusb.UsbContext;
|
||||||
|
import io.github.dsheirer.libusb.UsbDeviceHandle;
|
||||||
|
import io.github.dsheirer.libusb.UsbDeviceInfo;
|
||||||
|
import io.github.dsheirer.libusb.UsbException;
|
||||||
import io.github.dsheirer.buffer.ByteNativeBufferFactory;
|
import io.github.dsheirer.buffer.ByteNativeBufferFactory;
|
||||||
import io.github.dsheirer.buffer.INativeBuffer;
|
import io.github.dsheirer.buffer.INativeBuffer;
|
||||||
import io.github.dsheirer.rtlsdr.DeviceInfo;
|
import io.github.dsheirer.rtlsdr.DeviceInfo;
|
||||||
@@ -32,7 +36,7 @@ import io.github.dsheirer.source.tuner.TunerClass;
|
|||||||
import io.github.dsheirer.source.tuner.TunerController;
|
import io.github.dsheirer.source.tuner.TunerController;
|
||||||
import io.github.dsheirer.source.tuner.TunerType;
|
import io.github.dsheirer.source.tuner.TunerType;
|
||||||
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.manager.TunerManager;
|
import io.github.dsheirer.source.tuner.manager.UsbDeviceLookup;
|
||||||
import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerConfiguration;
|
import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.rtl.fc0012.FC0012TunerConfiguration;
|
import io.github.dsheirer.source.tuner.rtl.fc0012.FC0012TunerConfiguration;
|
||||||
import io.github.dsheirer.source.tuner.rtl.fc0013.FC0013TunerConfiguration;
|
import io.github.dsheirer.source.tuner.rtl.fc0013.FC0013TunerConfiguration;
|
||||||
@@ -44,12 +48,6 @@ import java.util.Arrays;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.usb4java.Context;
|
|
||||||
import org.usb4java.Device;
|
|
||||||
import org.usb4java.DeviceDescriptor;
|
|
||||||
import org.usb4java.DeviceHandle;
|
|
||||||
import org.usb4java.DeviceList;
|
|
||||||
import org.usb4java.LibUsb;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RTL-2832 tuner controller, implemented with the jRTL library (librtlsdr from rtl-sdr-blog).
|
* RTL-2832 tuner controller, implemented with the jRTL library (librtlsdr from rtl-sdr-blog).
|
||||||
@@ -328,73 +326,40 @@ public class RTL2832TunerController extends TunerController
|
|||||||
*/
|
*/
|
||||||
private UsbIdentity readUsbIdentity() throws SourceException
|
private UsbIdentity readUsbIdentity() throws SourceException
|
||||||
{
|
{
|
||||||
Context context = new Context();
|
try(UsbContext usb = UsbDeviceLookup.createContext())
|
||||||
int status = LibUsb.init(context);
|
|
||||||
|
|
||||||
if(status != LibUsb.SUCCESS)
|
|
||||||
{
|
{
|
||||||
throw new SourceException("Can't initialize libusb library - " + LibUsb.errorName(status));
|
//RTL-2832 devices that precede this controller's device in the device list
|
||||||
}
|
List<UsbDeviceInfo> earlierDevices = new ArrayList<>();
|
||||||
|
|
||||||
try
|
for(UsbDeviceInfo device : UsbDeviceLookup.devices(usb))
|
||||||
{
|
|
||||||
DeviceList deviceList = new DeviceList();
|
|
||||||
int count = LibUsb.getDeviceList(context, deviceList);
|
|
||||||
|
|
||||||
if(count < 0)
|
|
||||||
{
|
{
|
||||||
throw new SourceException("Can't get the USB device list - " + LibUsb.errorName(count));
|
if(TunerClass.lookup((short)device.vendorId(), (short)device.productId()) != TunerClass.RTL2832)
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
//RTL-2832 devices that precede this controller's device in the device list
|
|
||||||
List<Device> earlierDevices = new ArrayList<>();
|
|
||||||
List<DeviceDescriptor> earlierDescriptors = new ArrayList<>();
|
|
||||||
|
|
||||||
for(Device device : deviceList)
|
|
||||||
{
|
{
|
||||||
DeviceDescriptor descriptor = new DeviceDescriptor();
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if(LibUsb.getDeviceDescriptor(device, descriptor) != LibUsb.SUCCESS ||
|
if(UsbDeviceLookup.isAt(device, mBus, mPortAddress))
|
||||||
TunerClass.lookup(descriptor.idVendor(), descriptor.idProduct()) != TunerClass.RTL2832)
|
{
|
||||||
|
mUsbSerialNumberIndex = device.serialNumberIndex();
|
||||||
|
String serial = readSerial(usb, device, true);
|
||||||
|
int serialPosition = 0;
|
||||||
|
|
||||||
|
if(serial != null)
|
||||||
{
|
{
|
||||||
continue;
|
for(UsbDeviceInfo earlierDevice : earlierDevices)
|
||||||
}
|
|
||||||
|
|
||||||
if(LibUsb.getBusNumber(device) == mBus && LibUsb.getPortNumber(device) > 0 &&
|
|
||||||
mPortAddress != null && mPortAddress.equals(TunerManager.getPortAddress(device)))
|
|
||||||
{
|
|
||||||
mUsbSerialNumberIndex = 0xFF & descriptor.iSerialNumber();
|
|
||||||
String serial = readSerial(device, descriptor, true);
|
|
||||||
int serialPosition = 0;
|
|
||||||
|
|
||||||
if(serial != null)
|
|
||||||
{
|
{
|
||||||
for(int x = 0; x < earlierDevices.size(); x++)
|
if(serial.equals(readSerial(usb, earlierDevice, false)))
|
||||||
{
|
{
|
||||||
if(serial.equals(readSerial(earlierDevices.get(x), earlierDescriptors.get(x), false)))
|
serialPosition++;
|
||||||
{
|
|
||||||
serialPosition++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new UsbIdentity(serial, serialPosition, earlierDevices.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
earlierDevices.add(device);
|
return new UsbIdentity(serial, serialPosition, earlierDevices.size());
|
||||||
earlierDescriptors.add(descriptor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
earlierDevices.add(device);
|
||||||
}
|
}
|
||||||
finally
|
|
||||||
{
|
|
||||||
LibUsb.freeDeviceList(deviceList, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
LibUsb.exit(context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new SourceException("Couldn't find USB device at bus [" + mBus + "] port [" + mPortAddress + "]");
|
throw new SourceException("Couldn't find USB device at bus [" + mBus + "] port [" + mPortAddress + "]");
|
||||||
@@ -402,51 +367,32 @@ public class RTL2832TunerController extends TunerController
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads the device's USB serial string.
|
* Reads the device's USB serial string.
|
||||||
|
* @param usb context
|
||||||
* @param device to read
|
* @param device to read
|
||||||
* @param descriptor for the device
|
|
||||||
* @param required true to throw an exception when the device can't be opened, false to return null
|
* @param required true to throw an exception when the device can't be opened, false to return null
|
||||||
* @return serial or null if the device doesn't have a serial string
|
* @return serial or null if the device doesn't have a serial string
|
||||||
* @throws SourceException if the device is required and it can't be opened.
|
* @throws SourceException if the device is required and it can't be opened.
|
||||||
*/
|
*/
|
||||||
private static String readSerial(Device device, DeviceDescriptor descriptor, boolean required) throws SourceException
|
private static String readSerial(UsbContext usb, UsbDeviceInfo device, boolean required) throws SourceException
|
||||||
{
|
{
|
||||||
if(descriptor.iSerialNumber() == 0)
|
if(!required)
|
||||||
{
|
{
|
||||||
return null;
|
if(device.serialNumberIndex() == 0)
|
||||||
}
|
|
||||||
|
|
||||||
DeviceHandle handle = new DeviceHandle();
|
|
||||||
int status = LibUsb.open(device, handle);
|
|
||||||
|
|
||||||
if(status != LibUsb.SUCCESS)
|
|
||||||
{
|
|
||||||
if(!required)
|
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(status == LibUsb.ERROR_ACCESS)
|
try(UsbDeviceHandle handle = usb.open(device))
|
||||||
{
|
{
|
||||||
mLog.error("Access to USB tuner denied - (windows) reinstall zadig driver or (linux) check udev rules");
|
return handle.readString(device.serialNumberIndex());
|
||||||
throw new SourceException("access denied - if using linux, install udev rules");
|
}
|
||||||
|
catch(UsbException e)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
mLog.error("Can't open USB tuner - check driver or Linux udev rules");
|
|
||||||
throw new SourceException("Can't open USB tuner - reinstall driver? - " + LibUsb.errorName(status));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
return UsbDeviceLookup.readSerialNumber(usb, device);
|
||||||
{
|
|
||||||
return LibUsb.getStringDescriptor(handle, descriptor.iSerialNumber());
|
|
||||||
}
|
|
||||||
catch(RuntimeException e)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
LibUsb.close(handle);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -320,7 +320,7 @@ public class DiscoveredTunerModel extends AbstractTableModel implements Listener
|
|||||||
{
|
{
|
||||||
DiscoveredTuner discoveredTuner = mDiscoveredTuners.stream()
|
DiscoveredTuner discoveredTuner = mDiscoveredTuners.stream()
|
||||||
.filter(tuner -> tuner instanceof DiscoveredUSBTuner usbTuner &&
|
.filter(tuner -> tuner instanceof DiscoveredUSBTuner usbTuner &&
|
||||||
usbTuner.isAt(bus, portAddress)).findFirst().get();
|
usbTuner.isAt(bus, portAddress)).findFirst().orElse(null);
|
||||||
|
|
||||||
if(discoveredTuner != null)
|
if(discoveredTuner != null)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user