Skip to content

Commit 5db4554

Browse files
committed
probe CDC devices by USB interface types instead of fixed VID+PID
- no more custom prober required for standard CDC devices - legacy (singleInterface) CDC devices still have to be added by VID+PID - for autostart VID+PID still have to be added to device_filter.xml
1 parent 85f64af commit 5db4554

File tree

13 files changed

+170
-120
lines changed

13 files changed

+170
-120
lines changed

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -124,22 +124,23 @@ new device or for one using a custom VID/PID pair.
124124
UsbSerialProber is a class to help you find and instantiate compatible
125125
UsbSerialDrivers from the tree of connected UsbDevices. Normally, you will use
126126
the default prober returned by ``UsbSerialProber.getDefaultProber()``, which
127-
uses the built-in list of well-known VIDs and PIDs that are supported by our
128-
drivers.
127+
uses USB interface types and the built-in list of well-known VIDs and PIDs that
128+
are supported by our drivers.
129129

130130
To use your own set of rules, create and use a custom prober:
131131

132132
```java
133-
// Probe for our custom CDC devices, which use VID 0x1234
134-
// and PIDS 0x0001 and 0x0002.
133+
// Probe for our custom FTDI device, which use VID 0x1234 and PID 0x0001 and 0x0002.
135134
ProbeTable customTable = new ProbeTable();
136-
customTable.addProduct(0x1234, 0x0001, CdcAcmSerialDriver.class);
137-
customTable.addProduct(0x1234, 0x0002, CdcAcmSerialDriver.class);
135+
customTable.addProduct(0x1234, 0x0001, FtdiSerialDriver.class);
136+
customTable.addProduct(0x1234, 0x0002, FtdiSerialDriver.class);
138137

139138
UsbSerialProber prober = new UsbSerialProber(customTable);
140139
List<UsbSerialDriver> drivers = prober.findAllDrivers(usbManager);
141140
// ...
142141
```
142+
*Note*: as of v3.5.0 this library detects CDC devices by USB interface types instead of fixed VID+PID,
143+
so custom probers are typically not required any more for CDC devices.
143144

144145
Of course, nothing requires you to use UsbSerialProber at all: you can
145146
instantiate driver classes directly if you know what you're doing; just supply

usbSerialExamples/src/main/java/com/hoho/android/usbserial/examples/CustomProber.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.hoho.android.usbserial.examples;
22

3-
import com.hoho.android.usbserial.driver.CdcAcmSerialDriver;
3+
import com.hoho.android.usbserial.driver.FtdiSerialDriver;
44
import com.hoho.android.usbserial.driver.ProbeTable;
55
import com.hoho.android.usbserial.driver.UsbSerialProber;
66

@@ -14,7 +14,8 @@ class CustomProber {
1414

1515
static UsbSerialProber getCustomProber() {
1616
ProbeTable customTable = new ProbeTable();
17-
customTable.addProduct(0x16d0, 0x087e, CdcAcmSerialDriver.class); // e.g. Digispark CDC
17+
customTable.addProduct(0x1234, 0x0001, FtdiSerialDriver.class); // e.g. device with custom VID+PID
18+
customTable.addProduct(0x1234, 0x0002, FtdiSerialDriver.class); // e.g. device with custom VID+PID
1819
return new UsbSerialProber(customTable);
1920
}
2021

usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java

Lines changed: 21 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,30 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
3737
public CdcAcmSerialDriver(UsbDevice device) {
3838
mDevice = device;
3939
mPorts = new ArrayList<>();
40-
40+
int ports = countPorts(device);
41+
for (int port = 0; port < ports; port++) {
42+
mPorts.add(new CdcAcmSerialPort(mDevice, port));
43+
}
44+
if (mPorts.size() == 0) {
45+
mPorts.add(new CdcAcmSerialPort(mDevice, -1));
46+
}
47+
}
48+
49+
@SuppressWarnings({"unused"})
50+
public static boolean probe(UsbDevice device) {
51+
return countPorts(device) > 0;
52+
}
53+
54+
private static int countPorts(UsbDevice device) {
4155
int controlInterfaceCount = 0;
4256
int dataInterfaceCount = 0;
43-
for( int i = 0; i < device.getInterfaceCount(); i++) {
44-
if(device.getInterface(i).getInterfaceClass() == UsbConstants.USB_CLASS_COMM)
57+
for (int i = 0; i < device.getInterfaceCount(); i++) {
58+
if (device.getInterface(i).getInterfaceClass() == UsbConstants.USB_CLASS_COMM)
4559
controlInterfaceCount++;
46-
if(device.getInterface(i).getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA)
60+
if (device.getInterface(i).getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA)
4761
dataInterfaceCount++;
4862
}
49-
for( int port = 0; port < Math.min(controlInterfaceCount, dataInterfaceCount); port++) {
50-
mPorts.add(new CdcAcmSerialPort(mDevice, port));
51-
}
52-
if(mPorts.size() == 0) {
53-
mPorts.add(new CdcAcmSerialPort(mDevice, -1));
54-
}
63+
return Math.min(controlInterfaceCount, dataInterfaceCount);
5564
}
5665

5766
@Override
@@ -297,51 +306,9 @@ public void setBreak(boolean value) throws IOException {
297306

298307
}
299308

309+
@SuppressWarnings({"unused"})
300310
public static Map<Integer, int[]> getSupportedDevices() {
301-
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<>();
302-
supportedDevices.put(UsbId.VENDOR_ARDUINO,
303-
new int[] {
304-
UsbId.ARDUINO_UNO,
305-
UsbId.ARDUINO_UNO_R3,
306-
UsbId.ARDUINO_MEGA_2560,
307-
UsbId.ARDUINO_MEGA_2560_R3,
308-
UsbId.ARDUINO_SERIAL_ADAPTER,
309-
UsbId.ARDUINO_SERIAL_ADAPTER_R3,
310-
UsbId.ARDUINO_MEGA_ADK,
311-
UsbId.ARDUINO_MEGA_ADK_R3,
312-
UsbId.ARDUINO_LEONARDO,
313-
UsbId.ARDUINO_MICRO,
314-
});
315-
supportedDevices.put(UsbId.VENDOR_VAN_OOIJEN_TECH,
316-
new int[] {
317-
UsbId.VAN_OOIJEN_TECH_TEENSYDUINO_SERIAL,
318-
});
319-
supportedDevices.put(UsbId.VENDOR_ATMEL,
320-
new int[] {
321-
UsbId.ATMEL_LUFA_CDC_DEMO_APP,
322-
});
323-
supportedDevices.put(UsbId.VENDOR_LEAFLABS,
324-
new int[] {
325-
UsbId.LEAFLABS_MAPLE,
326-
});
327-
supportedDevices.put(UsbId.VENDOR_ARM,
328-
new int[] {
329-
UsbId.ARM_MBED,
330-
});
331-
supportedDevices.put(UsbId.VENDOR_ST,
332-
new int[] {
333-
UsbId.ST_CDC,
334-
});
335-
supportedDevices.put(UsbId.VENDOR_RASPBERRY_PI,
336-
new int[] {
337-
UsbId.RASPBERRY_PI_PICO_MICROPYTHON,
338-
UsbId.RASPBERRY_PI_PICO_SDK,
339-
});
340-
supportedDevices.put(UsbId.VENDOR_QINHENG,
341-
new int[] {
342-
UsbId.QINHENG_CH9102F,
343-
});
344-
return supportedDevices;
311+
return new LinkedHashMap<>();
345312
}
346313

347314
}

usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/Ch34xSerialDriver.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@ public void setBreak(boolean value) throws IOException {
374374
}
375375
}
376376

377+
@SuppressWarnings({"unused"})
377378
public static Map<Integer, int[]> getSupportedDevices() {
378379
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<>();
379380
supportedDevices.put(UsbId.VENDOR_QINHENG, new int[]{

usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/Cp21xxSerialDriver.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ public void setBreak(boolean value) throws IOException {
320320
}
321321
}
322322

323+
@SuppressWarnings({"unused"})
323324
public static Map<Integer, int[]> getSupportedDevices() {
324325
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<>();
325326
supportedDevices.put(UsbId.VENDOR_SILABS,

usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/FtdiSerialDriver.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ public int getLatencyTimer() throws IOException {
414414

415415
}
416416

417+
@SuppressWarnings({"unused"})
417418
public static Map<Integer, int[]> getSupportedDevices() {
418419
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<>();
419420
supportedDevices.put(UsbId.VENDOR_FTDI,

usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/ProbeTable.java

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
package com.hoho.android.usbserial.driver;
88

9+
import android.hardware.usb.UsbDevice;
910
import android.util.Pair;
1011

1112
import java.lang.reflect.InvocationTargetException;
@@ -14,14 +15,14 @@
1415
import java.util.Map;
1516

1617
/**
17-
* Maps (vendor id, product id) pairs to the corresponding serial driver.
18-
*
19-
* @author mike wakerly (opensource@hoho.com)
18+
* Maps (vendor id, product id) pairs to the corresponding serial driver,
19+
* or invoke 'probe' method to check actual USB devices for matching interfaces.
2020
*/
2121
public class ProbeTable {
2222

23-
private final Map<Pair<Integer, Integer>, Class<? extends UsbSerialDriver>> mProbeTable =
23+
private final Map<Pair<Integer, Integer>, Class<? extends UsbSerialDriver>> mVidPidProbeTable =
2424
new LinkedHashMap<>();
25+
private final Map<Method, Class<? extends UsbSerialDriver>> mMethodProbeTable = new LinkedHashMap<>();
2526

2627
/**
2728
* Adds or updates a (vendor, product) pair in the table.
@@ -33,20 +34,19 @@ public class ProbeTable {
3334
*/
3435
public ProbeTable addProduct(int vendorId, int productId,
3536
Class<? extends UsbSerialDriver> driverClass) {
36-
mProbeTable.put(Pair.create(vendorId, productId), driverClass);
37+
mVidPidProbeTable.put(Pair.create(vendorId, productId), driverClass);
3738
return this;
3839
}
3940

4041
/**
4142
* Internal method to add all supported products from
4243
* {@code getSupportedProducts} static method.
4344
*
44-
* @param driverClass
45-
* @return
45+
* @param driverClass to be added
4646
*/
4747
@SuppressWarnings("unchecked")
48-
ProbeTable addDriver(Class<? extends UsbSerialDriver> driverClass) {
49-
final Method method;
48+
void addDriver(Class<? extends UsbSerialDriver> driverClass) {
49+
Method method;
5050

5151
try {
5252
method = driverClass.getMethod("getSupportedDevices");
@@ -68,20 +68,35 @@ ProbeTable addDriver(Class<? extends UsbSerialDriver> driverClass) {
6868
}
6969
}
7070

71-
return this;
71+
try {
72+
method = driverClass.getMethod("probe", UsbDevice.class);
73+
mMethodProbeTable.put(method, driverClass);
74+
} catch (SecurityException | NoSuchMethodException ignored) {
75+
}
7276
}
7377

7478
/**
75-
* Returns the driver for the given (vendor, product) pair, or {@code null}
76-
* if no match.
79+
* Returns the driver for the given USB device, or {@code null} if no match.
7780
*
78-
* @param vendorId the USB vendor id
79-
* @param productId the USB product id
81+
* @param usbDevice the USB device to be probed
8082
* @return the driver class matching this pair, or {@code null}
8183
*/
82-
public Class<? extends UsbSerialDriver> findDriver(int vendorId, int productId) {
83-
final Pair<Integer, Integer> pair = Pair.create(vendorId, productId);
84-
return mProbeTable.get(pair);
84+
public Class<? extends UsbSerialDriver> findDriver(final UsbDevice usbDevice) {
85+
final Pair<Integer, Integer> pair = Pair.create(usbDevice.getVendorId(), usbDevice.getProductId());
86+
Class<? extends UsbSerialDriver> driverClass = mVidPidProbeTable.get(pair);
87+
if (driverClass != null)
88+
return driverClass;
89+
for (Map.Entry<Method, Class<? extends UsbSerialDriver>> entry : mMethodProbeTable.entrySet()) {
90+
try {
91+
Method method = entry.getKey();
92+
Object o = method.invoke(null, usbDevice);
93+
if((boolean)o)
94+
return entry.getValue();
95+
} catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
96+
throw new RuntimeException(e);
97+
}
98+
}
99+
return null;
85100
}
86101

87102
}

usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/ProlificSerialDriver.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,7 @@ public void setBreak(boolean value) throws IOException {
566566
}
567567
}
568568

569+
@SuppressWarnings({"unused"})
569570
public static Map<Integer, int[]> getSupportedDevices() {
570571
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<>();
571572
supportedDevices.put(UsbId.VENDOR_PROLIFIC,

usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/UsbId.java

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -23,27 +23,6 @@ public final class UsbId {
2323
public static final int FTDI_FT232H = 0x6014;
2424
public static final int FTDI_FT231X = 0x6015; // same ID for FT230X, FT231X, FT234XD
2525

26-
public static final int VENDOR_ATMEL = 0x03EB;
27-
public static final int ATMEL_LUFA_CDC_DEMO_APP = 0x2044;
28-
29-
public static final int VENDOR_ARDUINO = 0x2341;
30-
public static final int ARDUINO_UNO = 0x0001;
31-
public static final int ARDUINO_MEGA_2560 = 0x0010;
32-
public static final int ARDUINO_SERIAL_ADAPTER = 0x003b;
33-
public static final int ARDUINO_MEGA_ADK = 0x003f;
34-
public static final int ARDUINO_MEGA_2560_R3 = 0x0042;
35-
public static final int ARDUINO_UNO_R3 = 0x0043;
36-
public static final int ARDUINO_MEGA_ADK_R3 = 0x0044;
37-
public static final int ARDUINO_SERIAL_ADAPTER_R3 = 0x0044;
38-
public static final int ARDUINO_LEONARDO = 0x8036;
39-
public static final int ARDUINO_MICRO = 0x8037;
40-
41-
public static final int VENDOR_VAN_OOIJEN_TECH = 0x16c0;
42-
public static final int VAN_OOIJEN_TECH_TEENSYDUINO_SERIAL = 0x0483;
43-
44-
public static final int VENDOR_LEAFLABS = 0x1eaf;
45-
public static final int LEAFLABS_MAPLE = 0x0004;
46-
4726
public static final int VENDOR_SILABS = 0x10c4;
4827
public static final int SILABS_CP2102 = 0xea60; // same ID for CP2101, CP2103, CP2104, CP2109
4928
public static final int SILABS_CP2105 = 0xea70;
@@ -61,18 +40,7 @@ public final class UsbId {
6140
public static final int VENDOR_QINHENG = 0x1a86;
6241
public static final int QINHENG_CH340 = 0x7523;
6342
public static final int QINHENG_CH341A = 0x5523;
64-
public static final int QINHENG_CH9102F = 0x55D4;
65-
66-
// at www.linux-usb.org/usb.ids listed for NXP/LPC1768, but all processors supported by ARM mbed DAPLink firmware report these ids
67-
public static final int VENDOR_ARM = 0x0d28;
68-
public static final int ARM_MBED = 0x0204;
69-
70-
public static final int VENDOR_ST = 0x0483;
71-
public static final int ST_CDC = 0x5740;
7243

73-
public static final int VENDOR_RASPBERRY_PI = 0x2e8a;
74-
public static final int RASPBERRY_PI_PICO_MICROPYTHON = 0x0005;
75-
public static final int RASPBERRY_PI_PICO_SDK = 0x000a;
7644

7745
private UsbId() {
7846
throw new IllegalAccessError("Non-instantiable class");

usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/UsbSerialDriver.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,17 @@
1010

1111
import java.util.List;
1212

13-
/**
14-
*
15-
* @author mike wakerly (opensource@hoho.com)
16-
*/
1713
public interface UsbSerialDriver {
1814

15+
/*
16+
* Additional interface properties. Invoked thru reflection.
17+
*
18+
UsbSerialDriver(UsbDevice device); // constructor with device
19+
static Map<Integer, int[]> getSupportedDevices();
20+
static boolean probe(UsbDevice device); // optional
21+
*/
22+
23+
1924
/**
2025
* Returns the raw {@link UsbDevice} backing this port.
2126
*

0 commit comments

Comments
 (0)