org.bidib.jbidibc.ch341a.libusb.Ch341A Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jbidibc-usb Show documentation
Show all versions of jbidibc-usb Show documentation
jBiDiB jbidibc USB POM
Install the libusb drivers for using the libusb access. Under Windows OS you must install the Zadig driver as described here: https://github.com/libusb/libusb/wiki/Windows.
Zadic can be downloaded from: http://zadig.akeo.ie/
In the Zadig UI you must check Options > List all devices and the select the 'USB-EPP /I2C... CH341A' device. Then select the target driver with the spinners (I used libusb-win32 (v1.2.6.0))
and click the 'Install driver' button.
package org.bidib.jbidibc.ch341a.libusb;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import org.bidib.jbidibc.messages.utils.ByteUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.usb4java.BufferUtils;
import org.usb4java.ConfigDescriptor;
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 org.usb4java.Transfer;
import org.usb4java.TransferCallback;
public class Ch341A implements AutoCloseable {
private static final Logger LOGGER = LoggerFactory.getLogger(Ch341A.class);
public static final short USB_VENDOR_ID_CH341 = 0x1a86; // Dev : (1a86) QinHeng Electronics
public static final short USB_DEVICE_ID_CH341_I2C = 0x5512; // (5512) CH341A in I2C mode
public static final short USB_DEVICE_ID_CH341_UART = 0x5523; // (5523) CH341A in UART mode
/** The interface number. */
private static final byte DEFAULT_INTERFACE = 0;
private static final int DEFAULT_CONFIGURATION = 0x01;
private static final byte BULK_WRITE_ENDPOINT = 0x02;
private static final byte BULK_READ_ENDPOINT = (byte) 0x82;
private static final int DEFAULT_TIMEOUT = 300; // 300mS for USB timeouts
private static final int IN_BUF_SZ = 0x100;
private static final int EEPROM_WRITE_BUF_SZ = 0x2b; // only for 24c64 / 24c32 ??
private static final int EEPROM_READ_BULKIN_BUF_SZ = 0x20;
private static final int EEPROM_READ_BULKOUT_BUF_SZ = 0x65;
private static final byte mCH341A_CMD_I2C_STREAM = (byte) 0xAA;
private static final byte mCH341A_CMD_I2C_STM_SET = (byte) 0x60;
private static final byte mCH341A_CMD_I2C_STM_STA = (byte) 0x74;
private static final byte mCH341A_CMD_I2C_STM_STO = (byte) 0x75;
private static final byte mCH341A_CMD_I2C_STM_OUT = (byte) 0x80;
private static final byte mCH341A_CMD_I2C_STM_END = (byte) 0x00;
/** The communication timeout in milliseconds. */
private static final int TIMEOUT = 1000;
// CH341a READ EEPROM setup packet for the 24c64
// this needs putting into a struct to allow convenient access to individual elements
// #define CH341_EEPROM_READ_SETUP_CMD "\xaa\x74\x83\xa0\x00\x00\x74\x81\xa1\xe0\x00\x00\x06\x04\x00\x00" \
// "\x00\x00\x00\x00\x40\x00\x00\x00\x11\x4d\x40\x77\xcd\xab\xba\xdc" \
// "\xaa\xe0\x00\x00\xc4\xf1\x12\x00\x11\x4d\x40\x77\xf0\xf1\x12\x00" \
// "\xd9\x8b\x41\x7e\x00\xf0\xfd\x7f\xf0\xf1\x12\x00\x5a\x88\x41\x7e" \
// "\xaa\xe0\x00\x00\x2a\x88\x41\x7e\x06\x04\x00\x00\x11\x4d\x40\x77" \
// "\xe8\xf3\x12\x00\x14\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00" \
// "\xaa\xdf\xc0\x75\x00"
// @formatter:off
private static final byte[] CH341_EEPROM_READ_SETUP_CMD =
{ (byte) 0xAA, 0x74, (byte) 0x83, (byte) 0xa0, 0x00, 0x00, 0x74, (byte) 0x81, (byte) 0xa1, (byte) 0xe0, 0x00, 0x00, 0x06, 0x04, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x11, 0x4d, 0x40, 0x77, (byte) 0xcd, (byte) 0xab, (byte) 0xba, (byte) 0xdc,
(byte) 0xaa, (byte) 0xe0, 0x00, 0x00, (byte) 0xc4, (byte) 0xf1, 0x12, 0x00, 0x11, 0x4d, 0x40, 0x77, (byte) 0xf0, (byte) 0xf1, 0x12, 0x00,
(byte) 0xd9, (byte) 0x8b, 0x41, 0x7e, 0x00, (byte) 0xf0, (byte) 0xfd, 0x7f, (byte) 0xf0, (byte) 0xf1, 0x12, 0x00, 0x5a, (byte) 0x88, 0x41, 0x7e,
(byte) 0xaa, (byte) 0xe0, 0x00, 0x00, 0x2a, (byte) 0x88, 0x41, 0x7e, 0x06, 0x04, 0x00, 0x00, 0x11, 0x4d, 0x40, 0x77,
(byte) 0xe8, (byte) 0xf3, 0x12, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
(byte) 0xaa, (byte) 0xdf, (byte) 0xc0, 0x75, 0x00};
// @formatter:on
//
//// for 24c64
// #define CH341_EEPROM_READ_NEXT_CMD "\xaa\x74\x83\xa0\x00\x00\x74\x81\xa1\xe0\x00\x00\x10\x00\x00\x00" \
// "\x00\x00\x00\x00\x8c\xf1\x12\x00\x01\x00\x00\x00\x00\x00\x00\x00" \
// "\xaa\xe0\x00\x00\x4c\xf1\x12\x00\x5d\x22\xd7\x5a\xdc\xf1\x12\x00" \
// "\x8f\x04\x44\x7e\x30\x88\x41\x7e\xff\xff\xff\xff\x2a\x88\x41\x7e" \
// "\xaa\xe0\x00\x7e\x00\x00\x00\x00\x69\x0e\x3c\x00\x12\x01\x19\x00" \
// "\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x9c\x2e\x68\x00" \
// "\xaa\xdf\xc0\x75\x00"
// @formatter:off
private static final byte[] CH341_EEPROM_READ_NEXT_CMD =
{ (byte) 0xAA, 0x74, (byte) 0x83, (byte) 0xa0, 0x00, 0x00, 0x74, (byte) 0x81, (byte) 0xa1, (byte) 0xe0, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, (byte) 0x8c, (byte) 0xf1, 0x12, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
(byte) 0xaa, (byte) 0xe0, 0x00, 0x00, (byte) 0xc4, (byte) 0xf1, 0x12, 0x00, 0x5d, 0x22, (byte) 0xd7, 0x5a, (byte) 0xdc, (byte) 0xf1, 0x12, 0x00,
(byte) 0x8f, 0x04, 0x44, 0x7e, 0x30, (byte) 0x88, 0x41, 0x7e, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, 0x2a, (byte) 0x88, 0x41, 0x7e,
(byte) 0xaa, (byte) 0xe0, (byte) 0x00, (byte) 0x7e, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x69, (byte) 0x0e, (byte) 0x3c, (byte) 0x00, (byte) 0x12, (byte) 0x01, (byte) 0x19, (byte) 0x00,
(byte) 0x0f, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x9c, (byte) 0x2e, (byte) 0x68, (byte) 0x00,
(byte) 0xaa, (byte) 0xdf, (byte) 0xc0, 0x75, 0x00};
// @formatter:on
// private Context context;
private DeviceHandle handle;
private boolean detachKernelDriver;
public void setLibUsbDebugLevel(int logLevel) {
LibUsb.setOption(null, LibUsb.OPTION_LOG_LEVEL, logLevel);
}
public void init(Integer logLevel) {
// context = new Context();
int result = LibUsb.init(null);
if (result != LibUsb.SUCCESS) {
throw new LibUsbException("Unable to initialize libusb.", result);
}
if (logLevel != null) {
LibUsb.setOption(null, LibUsb.OPTION_LOG_LEVEL, logLevel);
}
// // Read the device descriptor
// final DeviceDescriptor descriptor = new DeviceDescriptor();
// result = LibUsb.getDeviceDescriptor(device, descriptor);
// if (result < 0) {
// throw new LibUsbException("Unable to read device descriptor", result);
// }
}
// vendor Silicon Laboratories 10C4
// product CP2112 HID USB-to-SMBus Bridge EA90
public Device findDevice() {
Device device = findDevice(USB_VENDOR_ID_CH341, USB_DEVICE_ID_CH341_I2C);
LOGGER.info("Found device: {}", device);
if (device == null) {
throw new RuntimeException("No CH341A device found!");
}
return device;
}
public DeviceHandle openDevice(/* Device device */) {
int result = -1;
// DeviceHandle handle = new DeviceHandle();
//
// device = LibUsb.refDevice(device);
//
// int result = LibUsb.open(device, handle);
// if (result != LibUsb.SUCCESS) {
// throw new LibUsbException("Unable to open USB device", result);
// }
// // Open test device
final DeviceHandle handle = LibUsb.openDeviceWithVidPid(null, USB_VENDOR_ID_CH341, USB_DEVICE_ID_CH341_I2C);
if (handle == null) {
throw new RuntimeException("Unable to open USB device");
}
LOGGER.info("Open device passed, handle: {}", handle);
this.handle = handle;
Device device = LibUsb.getDevice(handle);
LOGGER.info("Get device passed, device: {}", device);
LOGGER
.info("Found [{}:{}] as device [{}] on USB bus [{}]\n", USB_VENDOR_ID_CH341, USB_DEVICE_ID_CH341_I2C,
LibUsb.getDeviceAddress(device), LibUsb.getBusNumber(device));
detachKernelDriver = LibUsb.kernelDriverActive(handle, DEFAULT_INTERFACE) > 0;
if (detachKernelDriver) {
int ret = LibUsb.detachKernelDriver(handle, DEFAULT_INTERFACE);
if (ret > 0) {
LOGGER.warn("Failed to detach kernel driver: {}", ret);
throw new LibUsbException("Unable to open USB device", ret);
}
else {
LOGGER.info("Detached kernel driver.");
}
}
else {
LOGGER.info("No kernel driver active.");
}
IntBuffer currentConfig = IntBuffer.allocate(1);
result = LibUsb.getConfiguration(handle, currentConfig);
if (result != LibUsb.SUCCESS) {
LOGGER.warn("Failed to get current device configuration: {}", result);
// return NULL;
}
LOGGER.info("Current configuration: {}", currentConfig);
if (currentConfig.get(0) != DEFAULT_CONFIGURATION) {
result = LibUsb.setConfiguration(handle, DEFAULT_CONFIGURATION);
}
if (result != LibUsb.SUCCESS) {
LOGGER.warn("Failed to set device configuration to {}: {}", DEFAULT_CONFIGURATION, result);
// return NULL;
}
// Claim the ADB interface
result = LibUsb.claimInterface(handle, DEFAULT_INTERFACE);
if (result != LibUsb.SUCCESS) {
throw new LibUsbException("Unable to claim interface", result);
}
LOGGER.info("Claim interface passed.");
ByteBuffer ch341DescriptorBuffer = ByteBuffer.allocateDirect(0x12);
result = LibUsb.getDescriptor(handle, LibUsb.DT_DEVICE, (byte) 0x00, ch341DescriptorBuffer);
if (result < LibUsb.SUCCESS) {
// throw new LibUsbException("Unable to claim interface", result);
LOGGER.warn("Unable to get the device descriptor, result: {}", result);
}
else {
LOGGER
.info(String
.format("Device reported its revision [%d.%02d]", ch341DescriptorBuffer.get(13),
ch341DescriptorBuffer.get(12)));
byte[] temp = new byte[0x12];
ch341DescriptorBuffer.get(temp);
LOGGER.info("Device descriptor: {}", ByteUtils.bytesToHex(temp));
}
try {
// Use device handle here
ConfigDescriptor descriptor = new ConfigDescriptor();
result = LibUsb.getActiveConfigDescriptor(device, descriptor);
if (result != LibUsb.SUCCESS) {
throw new LibUsbException("Unable to get the config descriptor", result);
}
LOGGER.info("Current config descriptor: {}", descriptor);
}
finally {
// LOGGER.info("Release the interface.");
// result = LibUsb.releaseInterface(handle, DEFAULT_INTERFACE);
// if (result != LibUsb.SUCCESS) {
// // throw new LibUsbException("Unable to release interface", result);
// LOGGER.warn("Unable to release interface: {}", result);
// }
//
// // Attach the kernel driver again if needed
// if (detach) {
// result = LibUsb.attachKernelDriver(handle, DEFAULT_INTERFACE);
// if (result != LibUsb.SUCCESS) {
// LOGGER.warn("Unable to re-attach kernel driver: {}", result);
// // throw new LibUsbException("Unable to re-attach kernel driver", result);
// }
// }
//
// LibUsb.close(handle);
}
return handle;
}
public void close(DeviceHandle handle) {
int result = -1;
LOGGER.info("Release the interface.");
result = LibUsb.releaseInterface(handle, DEFAULT_INTERFACE);
if (result != LibUsb.SUCCESS) {
// throw new LibUsbException("Unable to release interface", result);
LOGGER.warn("Unable to release interface: {}", result);
}
// Attach the kernel driver again if needed
if (detachKernelDriver) {
LOGGER.info("Re-attach the kernel driver.");
result = LibUsb.attachKernelDriver(handle, DEFAULT_INTERFACE);
if (result != LibUsb.SUCCESS) {
LOGGER.warn("Unable to re-attach kernel driver: {}", result);
// throw new LibUsbException("Unable to re-attach kernel driver", result);
}
// reset the flag
detachKernelDriver = false;
}
LibUsb.close(handle);
// reset the handle
handle = null;
}
/**
* @param handle
* the device handle
* @param speed
* set the i2c bus speed (speed: 0 = 20kHz; 1 = 100kHz, 2 = 400kHz, 3 = 750kHz)
*/
public void setStreamSpeed(final DeviceHandle handle, int speed) {
LOGGER.info("Set the stream speed: {}", speed);
IntBuffer transferred = IntBuffer.allocate(1);
byte[] buffer = new byte[3];
buffer[0] = mCH341A_CMD_I2C_STREAM;
buffer[1] = (byte) (mCH341A_CMD_I2C_STM_SET | (speed & 0x3));
buffer[2] = mCH341A_CMD_I2C_STM_END;
ByteBuffer data = BufferUtils.allocateByteBuffer(/* EEPROM_READ_BULKOUT_BUF_SZ */buffer.length);
data.put(buffer);
int result = LibUsb.bulkTransfer(handle, BULK_WRITE_ENDPOINT, data, transferred, DEFAULT_TIMEOUT);
if (result < LibUsb.SUCCESS) {
LOGGER.warn("Set the stream speed failed, result: {}", result);
}
LOGGER.info("Set the stream speed passed. Total transferred bytes: {}", transferred.get());
}
/**
* Flag set during the asynchronous transfers to indicate the program is finished.
*/
private volatile boolean exit = false;
private volatile int getnextpacket = 0;
private int syncackpkt;
private static final class ReadBufferHolder {
private final byte[] readBuffer;
private int offset;
public ReadBufferHolder(byte[] readBuffer) {
this.readBuffer = readBuffer;
}
public void append(byte[] buffer) {
int len = buffer.length;
System.arraycopy(buffer, 0, readBuffer, offset, len);
offset += len;
}
public byte[] getReadBuffer() {
return readBuffer;
}
public int getReceivedCount() {
return offset;
}
}
public byte[] ch341readEEPROM(final DeviceHandle handle, int bytestoread) {
byte[] ch341outBuffer = new byte[EEPROM_READ_BULKOUT_BUF_SZ];
byte[] ch341inBuffer = new byte[EEPROM_READ_BULKIN_BUF_SZ /* IN_BUF_SZ */]; // 0x100 bytes
ByteBuffer bufferIn = BufferUtils.allocateByteBuffer(ch341inBuffer.length)/* .order(ByteOrder.LITTLE_ENDIAN) */;
ByteBuffer bufferOut = BufferUtils.allocateByteBuffer(ch341outBuffer.length);
bufferOut.put(CH341_EEPROM_READ_SETUP_CMD);
LOGGER
.info("Allocated USB transfer structures, size of CH341_EEPROM_READ_SETUP_CMD: {}",
CH341_EEPROM_READ_SETUP_CMD.length);
// prepare the read buffer
byte[] readBuffer = new byte[bytestoread];
final ReadBufferHolder holder = new ReadBufferHolder(readBuffer);
Transfer transferIn = LibUsb.allocTransfer(0);
Transfer transferOut = LibUsb.allocTransfer(0);
final TransferCallback callbackIn = new TransferCallback() {
@Override
public void processTransfer(Transfer transfer) {
LOGGER.info("In, transfer status: {}", transfer.status());
switch (transfer.status()) {
case LibUsb.TRANSFER_COMPLETED:
getnextpacket = 1;
break;
default:
LOGGER.warn("In, transfer failed: {}", transfer.status());
getnextpacket = -1;
break;
}
LOGGER.info("{} bytes received", transfer.actualLength());
// prepare the buffer for the received data
byte[] ch341Received = new byte[transfer.actualLength()];
transfer.buffer().get(ch341Received);
LOGGER
.info("Data received, len: {}, data: {}", transfer.actualLength(),
ByteUtils.bytesToHex(ch341Received));
transfer.buffer().clear();
if (transfer.userData() != null) {
ReadBufferHolder holder = (ReadBufferHolder) transfer.userData();
LOGGER.info("Append the received data to the ReadBufferHolder.");
holder.append(ch341Received);
}
}
};
final TransferCallback callbackOut = new TransferCallback() {
@Override
public void processTransfer(Transfer transfer) {
LOGGER.info("Out, Sync/Ack received, status {}\n", transfer.status());
LOGGER.info("{} bytes sent.", transfer.actualLength());
syncackpkt = 1;
}
};
LibUsb.fillBulkTransfer(transferIn, handle, BULK_READ_ENDPOINT, bufferIn, callbackIn, holder, TIMEOUT);
LibUsb.fillBulkTransfer(transferOut, handle, BULK_WRITE_ENDPOINT, bufferOut, callbackOut, null, TIMEOUT);
LOGGER.info("Filled USB transfer structures");
int result = -1;
result = LibUsb.submitTransfer(transferIn);
LOGGER.info("Submitted BULK IN start packet, result: {}", result);
result = LibUsb.submitTransfer(transferOut);
LOGGER.info("Submitted BULK OUT setup packet, result: {}", result);
long start = System.currentTimeMillis();
long timeout = 100;
int readpktcount = 0;
int byteoffset = 0;
// TODO wait for result data
while (!exit) {
LOGGER.info("Read [{}] of [{}] bytes", byteoffset, bytestoread);
IntBuffer completed = BufferUtils.allocateIntBuffer();
result = LibUsb.handleEventsTimeoutCompleted(null, timeout, completed);
LOGGER.info("After handle events timeout, result: {}, completed: {}", result, completed.get());
if (result != LibUsb.SUCCESS || getnextpacket == -1) { // indicates error
LOGGER.warn("Wait for timeout failed, result: {}", result);
exit = true;
}
if (getnextpacket == 1) {
getnextpacket = 0;
readpktcount++; // increment the read packet counter
// byteoffset += EEPROM_READ_BULKIN_BUF_SZ;
byteoffset = holder.getReceivedCount();
if (byteoffset == bytestoread) {
break;
}
LOGGER.info("Re-submitting transfer request to BULK IN endpoint.");
result = LibUsb.submitTransfer(transferIn);
LOGGER.info("Submitted BULK IN start packet, result: {}", result);
if (syncackpkt > 0) {
LOGGER.info("Reset the syncackpkt.");
syncackpkt = 0;
}
// if 4th packet received, we are at end of 0x80 byte data block,
// if it is not the last block, then resubmit request for data
if (readpktcount == 4) {
LOGGER
.info(
"Submitting next transfer request to BULK OUT endpoint, CH341_EEPROM_READ_NEXT_CMD.length: {}",
CH341_EEPROM_READ_NEXT_CMD.length);
readpktcount = 0;
// TODO if the buffer is not allocated new here we get an exception
bufferOut = BufferUtils.allocateByteBuffer(ch341outBuffer.length);
bufferOut.put(CH341_EEPROM_READ_NEXT_CMD);
bufferOut.put(4, (byte) (byteoffset >> 8 & 0xff));// MSB (big-endian) byte address
bufferOut.put(5, (byte) (byteoffset & 0xff)); // LSB of 16-bit byte address
LibUsb
.fillBulkTransfer(transferOut, handle, BULK_WRITE_ENDPOINT, bufferOut, callbackOut, null,
TIMEOUT);
LibUsb.submitTransfer(transferOut); // update transfer struct (with new EEPROM page offset)
// and re-submit next transfer request to BULK OUT endpoint
}
}
}
long end = System.currentTimeMillis();
LOGGER.info("Read eeprom finished, duration: {}ms", (end - start));
// finished, free the transfer objects
LibUsb.freeTransfer(transferIn);
LibUsb.freeTransfer(transferOut);
LOGGER.info("Received data: {}", ByteUtils.bytesToHex(readBuffer));
return readBuffer;
}
/**
* Asynchronously writes some data to the device.
*
* @param handle
* The device handle.
* @param data
* The data to send to the device.
* @param callback
* The callback to execute when data has been transfered.
*/
public void write(DeviceHandle handle, byte[] data, TransferCallback callback) {
ByteBuffer buffer = BufferUtils.allocateByteBuffer(data.length);
buffer.put(data);
Transfer transfer = LibUsb.allocTransfer();
// LibUsb.fillBulkTransfer(transfer, handle, OUT_ENDPOINT, buffer, callback, null, TIMEOUT);
LOGGER.info("Sending {} bytes to device", data.length);
int result = LibUsb.submitTransfer(transfer);
if (result != LibUsb.SUCCESS) {
throw new LibUsbException("Unable to submit transfer", result);
}
}
/**
* Asynchronously reads some data from the device.
*
* @param handle
* The device handle.
* @param size
* The number of bytes to read from the device.
* @param callback
* The callback to execute when data has been received.
*/
public void read(DeviceHandle handle, int size, TransferCallback callback) {
ByteBuffer buffer = BufferUtils.allocateByteBuffer(size).order(ByteOrder.LITTLE_ENDIAN);
Transfer transfer = LibUsb.allocTransfer();
// LibUsb.fillBulkTransfer(transfer, handle, IN_ENDPOINT, buffer, callback, null, TIMEOUT);
System.out.println("Reading " + size + " bytes from device");
int result = LibUsb.submitTransfer(transfer);
if (result != LibUsb.SUCCESS) {
throw new LibUsbException("Unable to submit transfer", result);
}
}
public Device findDevice(short vendorId, short productId) {
// Read the USB device list
DeviceList list = new DeviceList();
int result = LibUsb.getDeviceList(null, list);
if (result < 0) {
throw new LibUsbException("Unable to get device list", result);
}
LOGGER.info("Found devices: {}", list);
try {
// Iterate over all devices and scan for the right one
for (Device device : list) {
DeviceDescriptor descriptor = new DeviceDescriptor();
result = LibUsb.getDeviceDescriptor(device, descriptor);
if (result != LibUsb.SUCCESS) {
throw new LibUsbException("Unable to read device descriptor", result);
}
LOGGER.info("Current device: {}", descriptor);
if (descriptor.idVendor() == vendorId && descriptor.idProduct() == productId) {
int speed = LibUsb.getDeviceSpeed(device);
LOGGER.info("Current device speed: {}", speed);
return device;
}
}
}
catch (Exception ex) {
LOGGER.warn("Failed to load the device descriptot.", ex);
}
finally {
// Ensure the allocated device list is freed
LibUsb.freeDeviceList(list, true);
}
// Device not found
return null;
}
@Override
public void close() throws Exception {
if (handle != null) {
close(handle);
}
LibUsb.exit(null);
}
}