mirror of
https://github.com/mik3y/usb-serial-for-android.git
synced 2026-08-11 08:22:52 +00:00
support CDC input control lines
This commit is contained in:
+29
-24
@@ -510,21 +510,21 @@ public class DeviceTest {
|
||||
fail("invalid baudrate 1");
|
||||
} catch (UnsupportedOperationException ignored) { // ch340
|
||||
} catch (IOException ignored) { // cp2105 second port
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
try {
|
||||
usb.setParameters(1<<31, 8, 1, UsbSerialPort.PARITY_NONE);
|
||||
usb.setParameters(1<<30, 8, 1, UsbSerialPort.PARITY_NONE);
|
||||
if (usb.serialDriver instanceof ProlificSerialDriver)
|
||||
;
|
||||
else if (usb.serialDriver instanceof Cp21xxSerialDriver)
|
||||
;
|
||||
else if (usb.serialDriver instanceof CdcAcmSerialDriver)
|
||||
;
|
||||
else if (usb.serialDriver instanceof Ch34xSerialDriver)
|
||||
;
|
||||
else
|
||||
fail("invalid baudrate 2^31");
|
||||
} catch (ArithmeticException ignored) { // ch340
|
||||
fail("invalid baudrate 2^30");
|
||||
} catch (UnsupportedOperationException ignored) { // ftdi
|
||||
} catch (IOException ignored) { // cp2105 second port
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
|
||||
for(int baudRate : new int[] {300, 2400, 19200, 115200} ) {
|
||||
@@ -1657,6 +1657,7 @@ public class DeviceTest {
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
// CDC CH9143 only recovers from CH... commands with power toggle, so skip this combination
|
||||
if(!(usb.serialDriver instanceof Ch34xSerialDriver)) {
|
||||
UsbDeviceConnection wrongDeviceConnection = usbManager.openDevice(usb.serialDriver.getDevice());
|
||||
UsbSerialDriver wrongSerialDriver = new Ch34xSerialDriver(usb.serialDriver.getDevice());
|
||||
@@ -1795,7 +1796,8 @@ public class DeviceTest {
|
||||
supportedControlLines.add(ControlLine.DTR);
|
||||
}
|
||||
if(usb.inputLinesSupported) {
|
||||
supportedControlLines.add(ControlLine.CTS);
|
||||
if(usb.inputLineCtsSupported)
|
||||
supportedControlLines.add(ControlLine.CTS);
|
||||
supportedControlLines.add(ControlLine.DSR);
|
||||
supportedControlLines.add(ControlLine.CD);
|
||||
supportedControlLines.add(ControlLine.RI);
|
||||
@@ -1823,16 +1825,16 @@ public class DeviceTest {
|
||||
|
||||
// control lines reset on initial open
|
||||
data = "none".getBytes();
|
||||
assertEquals(usb.inputLinesConnected && !usb.inputLinesOnlyRtsCts
|
||||
assertEquals(usb.inputLinesConnected && !usb.inputLinesOnlyRtsCts && usb.inputLineCtsSupported
|
||||
? EnumSet.of(ControlLine.RI)
|
||||
: EnumSet.noneOf(ControlLine.class),
|
||||
usb.serialPort.getControlLines());
|
||||
assertThat(usb.getControlLine(usb.serialPort::getRTS), equalTo(Boolean.FALSE));
|
||||
assertThat(usb.getControlLine(usb.serialPort::getCTS), equalTo(inputLineFalse));
|
||||
assertThat(usb.getControlLine(usb.serialPort::getCTS), equalTo(usb.inputLineCtsSupported ? inputLineFalse : null));
|
||||
assertThat(usb.getControlLine(usb.serialPort::getDTR), equalTo(Boolean.FALSE));
|
||||
assertThat(usb.getControlLine(usb.serialPort::getDSR), equalTo(inputLineFalse));
|
||||
assertThat(usb.getControlLine(usb.serialPort::getCD), equalTo(inputLineFalse));
|
||||
assertThat(usb.getControlLine(usb.serialPort::getRI), equalTo(usb.inputLinesOnlyRtsCts ? Boolean.FALSE : inputLineTrue));
|
||||
assertThat(usb.getControlLine(usb.serialPort::getRI), equalTo(usb.inputLinesOnlyRtsCts || !usb.inputLineCtsSupported ? Boolean.FALSE : inputLineTrue));
|
||||
telnet.write(data);
|
||||
assertThat(Arrays.toString(data), usb.read(4), equalTo(data));
|
||||
usb.write(data);
|
||||
@@ -1841,12 +1843,12 @@ public class DeviceTest {
|
||||
data = "rts ".getBytes();
|
||||
usb.serialPort.setRTS(true);
|
||||
Thread.sleep(sleep);
|
||||
assertEquals(usb.inputLinesConnected
|
||||
assertEquals(usb.inputLinesConnected & usb.inputLineCtsSupported
|
||||
? EnumSet.of(ControlLine.RTS, ControlLine.CTS)
|
||||
: EnumSet.of(ControlLine.RTS),
|
||||
usb.serialPort.getControlLines());
|
||||
assertThat(usb.getControlLine(usb.serialPort::getRTS), equalTo(Boolean.TRUE));
|
||||
assertThat(usb.getControlLine(usb.serialPort::getCTS), equalTo(inputLineTrue));
|
||||
assertThat(usb.getControlLine(usb.serialPort::getCTS), equalTo(usb.inputLineCtsSupported ? inputLineTrue : null));
|
||||
assertThat(usb.getControlLine(usb.serialPort::getDTR), equalTo(Boolean.FALSE));
|
||||
assertThat(usb.getControlLine(usb.serialPort::getDSR), equalTo(inputLineFalse));
|
||||
assertThat(usb.getControlLine(usb.serialPort::getCD), equalTo(inputLineFalse));
|
||||
@@ -1859,14 +1861,15 @@ public class DeviceTest {
|
||||
data = "both".getBytes();
|
||||
usb.serialPort.setDTR(true);
|
||||
Thread.sleep(sleep);
|
||||
assertEquals(usb.inputLinesOnlyRtsCts
|
||||
? EnumSet.of(ControlLine.RTS, ControlLine.DTR, ControlLine.CTS)
|
||||
: usb.inputLinesConnected
|
||||
? EnumSet.of(ControlLine.RTS, ControlLine.DTR, ControlLine.CD)
|
||||
: EnumSet.of(ControlLine.RTS, ControlLine.DTR),
|
||||
usb.serialPort.getControlLines());
|
||||
if(usb.inputLinesOnlyRtsCts) {
|
||||
assertEquals(EnumSet.of(ControlLine.RTS, ControlLine.DTR, ControlLine.CTS), usb.serialPort.getControlLines());
|
||||
} else if(usb.inputLinesConnected) {
|
||||
assertEquals(EnumSet.of(ControlLine.RTS, ControlLine.DTR, ControlLine.CD), usb.serialPort.getControlLines());
|
||||
} else {
|
||||
assertEquals(EnumSet.of(ControlLine.RTS, ControlLine.DTR), usb.serialPort.getControlLines());
|
||||
}
|
||||
assertThat(usb.getControlLine(usb.serialPort::getRTS), equalTo(Boolean.TRUE));
|
||||
assertThat(usb.getControlLine(usb.serialPort::getCTS), equalTo(usb.inputLinesOnlyRtsCts ? Boolean.TRUE : inputLineFalse));
|
||||
assertThat(usb.getControlLine(usb.serialPort::getCTS), equalTo(usb.inputLineCtsSupported ? (usb.inputLinesOnlyRtsCts ? Boolean.TRUE : inputLineFalse) : null));
|
||||
assertThat(usb.getControlLine(usb.serialPort::getDTR), equalTo(Boolean.TRUE));
|
||||
assertThat(usb.getControlLine(usb.serialPort::getDSR), equalTo(inputLineFalse));
|
||||
assertThat(usb.getControlLine(usb.serialPort::getCD), equalTo(usb.inputLinesOnlyRtsCts ? Boolean.FALSE : inputLineTrue));
|
||||
@@ -1884,7 +1887,7 @@ public class DeviceTest {
|
||||
: EnumSet.of(ControlLine.DTR),
|
||||
usb.serialPort.getControlLines());
|
||||
assertThat(usb.getControlLine(usb.serialPort::getRTS), equalTo(Boolean.FALSE));
|
||||
assertThat(usb.getControlLine(usb.serialPort::getCTS), equalTo(inputLineFalse));
|
||||
assertThat(usb.getControlLine(usb.serialPort::getCTS), equalTo(usb.inputLineCtsSupported ? inputLineFalse : null));
|
||||
assertThat(usb.getControlLine(usb.serialPort::getDTR), equalTo(Boolean.TRUE));
|
||||
assertThat(usb.getControlLine(usb.serialPort::getDSR), equalTo(usb.inputLinesOnlyRtsCts ? Boolean.FALSE : inputLineTrue));
|
||||
assertThat(usb.getControlLine(usb.serialPort::getCD), equalTo(inputLineFalse));
|
||||
@@ -1905,10 +1908,10 @@ public class DeviceTest {
|
||||
|
||||
EnumSet<ControlLine> retainedControlLines = EnumSet.noneOf(ControlLine.class);
|
||||
if(outputRetained) retainedControlLines.add(ControlLine.RTS);
|
||||
if(inputRetained) retainedControlLines.add(ControlLine.CTS);
|
||||
if(inputRetained && usb.inputLineCtsSupported) retainedControlLines.add(ControlLine.CTS);
|
||||
assertEquals(retainedControlLines, usb.serialPort.getControlLines());
|
||||
assertThat(usb.getControlLine(usb.serialPort::getRTS), equalTo(outputRetained));
|
||||
assertThat(usb.getControlLine(usb.serialPort::getCTS), equalTo(inputRetained ? inputLineTrue : inputLineFalse));
|
||||
assertThat(usb.getControlLine(usb.serialPort::getCTS), equalTo(usb.inputLineCtsSupported ? (inputRetained ? inputLineTrue : inputLineFalse) : null));
|
||||
assertThat(usb.getControlLine(usb.serialPort::getDTR), equalTo(Boolean.FALSE));
|
||||
assertThat(usb.getControlLine(usb.serialPort::getDSR), equalTo(inputLineFalse));
|
||||
assertThat(usb.getControlLine(usb.serialPort::getCD), equalTo(inputLineFalse));
|
||||
@@ -1958,7 +1961,8 @@ public class DeviceTest {
|
||||
}
|
||||
|
||||
try {
|
||||
if (usb.serialDriver instanceof ProlificSerialDriver) {
|
||||
if (usb.serialDriver instanceof ProlificSerialDriver
|
||||
|| usb.serialDriver instanceof CdcAcmSerialDriver) {
|
||||
for(int i = 0; i < 10; i++) { // can take some time until background thread fails
|
||||
usb.serialPort.getRI();
|
||||
Thread.sleep(100);
|
||||
@@ -2413,8 +2417,9 @@ public class DeviceTest {
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
try {
|
||||
if(usb.serialDriver instanceof ProlificSerialDriver)
|
||||
Thread.sleep(600); // wait for background thread
|
||||
if(usb.serialDriver instanceof ProlificSerialDriver
|
||||
|| usb.serialDriver instanceof CdcAcmSerialDriver)
|
||||
Thread.sleep(600); // wait for background thread to fail
|
||||
usb.serialPort.getRI();
|
||||
fail("getRI error expected");
|
||||
} catch (IOException ignored) {
|
||||
|
||||
+10
-5
@@ -65,6 +65,7 @@ public class UsbWrapper implements SerialInputOutputManager.Listener {
|
||||
public boolean inputLinesSupported;
|
||||
public boolean inputLinesConnected;
|
||||
public boolean inputLinesOnlyRtsCts;
|
||||
public boolean inputLineCtsSupported;
|
||||
public int writePacketSize = -1;
|
||||
public int writeBufferSize = -1;
|
||||
public int readBufferSize = -1;
|
||||
@@ -111,7 +112,8 @@ public class UsbWrapper implements SerialInputOutputManager.Listener {
|
||||
isCdcAcmCh343 = serialDriver.getDevice().getVendorId() == UsbId.VENDOR_QINHENG && serialDriver.getDevice().getProductId() == 0x55D3;
|
||||
|
||||
// output lines are supported by all common drivers
|
||||
// input lines are supported by all common drivers except CDC
|
||||
// input lines are supported by all common drivers, but only partly by CDC
|
||||
inputLineCtsSupported = true;
|
||||
if (serialDriver instanceof FtdiSerialDriver) {
|
||||
outputLinesSupported = true;
|
||||
inputLinesSupported = true;
|
||||
@@ -137,6 +139,10 @@ public class UsbWrapper implements SerialInputOutputManager.Listener {
|
||||
inputLinesConnected = true; // I only have 74LS138 connected at CH340, not connected at CH341A
|
||||
} else if (serialDriver instanceof CdcAcmSerialDriver) {
|
||||
outputLinesSupported = true;
|
||||
inputLinesSupported = true;
|
||||
if(isCdcAcmCh343)
|
||||
inputLinesConnected = true;
|
||||
inputLineCtsSupported = false;
|
||||
}
|
||||
|
||||
if (serialDriver instanceof Cp21xxSerialDriver) {
|
||||
@@ -322,10 +328,9 @@ public class UsbWrapper implements SerialInputOutputManager.Listener {
|
||||
|
||||
public void setParameters(int baudRate, int dataBits, int stopBits, @UsbSerialPort.Parity int parity) throws IOException, InterruptedException {
|
||||
serialPort.setParameters(baudRate, dataBits, stopBits, parity);
|
||||
if(serialDriver instanceof CdcAcmSerialDriver)
|
||||
Thread.sleep(10); // arduino_leonardeo_bridge.ini needs some time
|
||||
else
|
||||
Thread.sleep(1);
|
||||
//if(serialDriver instanceof CdcAcmSerialDriver)
|
||||
//Thread.sleep(10); // arduino_leonardeo_bridge.ini needs some time
|
||||
Thread.sleep(1);
|
||||
}
|
||||
|
||||
/* return TRUE/FALSE/null instead of true/false/<throw UnsupportedOperationException> */
|
||||
|
||||
+98
-1
@@ -13,10 +13,12 @@ import android.hardware.usb.UsbInterface;
|
||||
import android.util.Log;
|
||||
|
||||
import com.hoho.android.usbserial.util.HexDump;
|
||||
import com.hoho.android.usbserial.util.MonotonicClock;
|
||||
import com.hoho.android.usbserial.util.UsbUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@@ -99,6 +101,19 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
|
||||
private static final int SET_CONTROL_LINE_STATE = 0x22;
|
||||
private static final int SEND_BREAK = 0x23;
|
||||
|
||||
private static final int NOTIFICATION_REQUEST_TYPE = 0xA1;
|
||||
private static final int SERIAL_STATE = 0x20;
|
||||
private static final int SERIAL_STATE_PACKET_SIZE = 10;
|
||||
private static final int SERIAL_STATE_FLAG_CD = 0x01;
|
||||
private static final int SERIAL_STATE_FLAG_DSR = 0x02;
|
||||
private static final int SERIAL_STATE_FLAG_RI = 0x08;
|
||||
|
||||
private volatile int mSerialState = 0;
|
||||
private volatile Thread mReadControlLinesThread = null;
|
||||
private final Object mReadControlLinesThreadLock = new Object();
|
||||
private boolean mStopReadControlLinesThread = false;
|
||||
private Exception mReadControlLinesException = null;
|
||||
|
||||
public CdcAcmSerialPort(UsbDevice device, int portNumber) {
|
||||
super(device, portNumber);
|
||||
}
|
||||
@@ -260,9 +275,71 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
|
||||
return len;
|
||||
}
|
||||
|
||||
private void readControlLinesThreadFunction() {
|
||||
try {
|
||||
byte[] buffer = new byte[Math.max(SERIAL_STATE_PACKET_SIZE, mControlEndpoint.getMaxPacketSize())];
|
||||
while (!mStopReadControlLinesThread) {
|
||||
long endTime = MonotonicClock.millis() + 500;
|
||||
int readBytesCount = mConnection.bulkTransfer(mControlEndpoint, buffer, buffer.length, 500);
|
||||
if (readBytesCount == -1) {
|
||||
testConnection(MonotonicClock.millis() < endTime);
|
||||
continue;
|
||||
}
|
||||
if (readBytesCount != SERIAL_STATE_PACKET_SIZE) continue;
|
||||
if (buffer[0] != (byte)NOTIFICATION_REQUEST_TYPE) continue;
|
||||
if (buffer[1] != SERIAL_STATE) continue;
|
||||
int index = ((buffer[5] & 0xff) << 8) | (buffer[4] & 0xff);
|
||||
if (index != mControlIndex) continue;
|
||||
int payloadLength = ((buffer[7] & 0xff) << 8) | (buffer[6] & 0xff);
|
||||
if (payloadLength < 2) continue;
|
||||
mSerialState = ((buffer[9] & 0xff) << 8) | (buffer[8] & 0xff);
|
||||
Log.d(TAG, "control line state " + Arrays.toString(buffer));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (isOpen()) {
|
||||
mReadControlLinesException = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int getSerialState() throws IOException {
|
||||
if ((mReadControlLinesThread == null) && (mReadControlLinesException == null)) {
|
||||
synchronized (mReadControlLinesThreadLock) {
|
||||
if (mReadControlLinesThread == null) {
|
||||
mSerialState = 0;
|
||||
mReadControlLinesThread = new Thread(this::readControlLinesThreadFunction);
|
||||
mReadControlLinesThread.setDaemon(true);
|
||||
mReadControlLinesThread.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Exception readControlLinesException = mReadControlLinesException;
|
||||
if (readControlLinesException != null) {
|
||||
mReadControlLinesException = null;
|
||||
throw new IOException(readControlLinesException);
|
||||
}
|
||||
|
||||
return mSerialState;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void closeInt() {
|
||||
try {
|
||||
synchronized (mReadControlLinesThreadLock) {
|
||||
if (mReadControlLinesThread != null) {
|
||||
try {
|
||||
mStopReadControlLinesThread = true;
|
||||
mReadControlLinesThread.join();
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "An error occurred while waiting for control line read thread", e);
|
||||
}
|
||||
mStopReadControlLinesThread = false;
|
||||
mReadControlLinesThread = null;
|
||||
mReadControlLinesException = null;
|
||||
}
|
||||
}
|
||||
mConnection.releaseInterface(mControlInterface);
|
||||
mConnection.releaseInterface(mDataInterface);
|
||||
} catch(Exception ignored) {}
|
||||
@@ -320,6 +397,21 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
|
||||
return mRts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getCD() throws IOException {
|
||||
return (getSerialState() & SERIAL_STATE_FLAG_CD) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getDSR() throws IOException {
|
||||
return (getSerialState() & SERIAL_STATE_FLAG_DSR) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRI() throws IOException {
|
||||
return (getSerialState() & SERIAL_STATE_FLAG_RI) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRTS(boolean value) throws IOException {
|
||||
mRts = value;
|
||||
@@ -333,15 +425,20 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
|
||||
|
||||
@Override
|
||||
public EnumSet<ControlLine> getControlLines() throws IOException {
|
||||
int serialState = getSerialState();
|
||||
EnumSet<ControlLine> set = EnumSet.noneOf(ControlLine.class);
|
||||
if(mRts) set.add(ControlLine.RTS);
|
||||
// no CTS
|
||||
if(mDtr) set.add(ControlLine.DTR);
|
||||
if((serialState & SERIAL_STATE_FLAG_DSR) != 0) set.add(ControlLine.DSR);
|
||||
if((serialState & SERIAL_STATE_FLAG_CD) != 0) set.add(ControlLine.CD);
|
||||
if((serialState & SERIAL_STATE_FLAG_RI) != 0) set.add(ControlLine.RI);
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumSet<ControlLine> getSupportedControlLines() throws IOException {
|
||||
return EnumSet.of(ControlLine.RTS, ControlLine.DTR);
|
||||
return EnumSet.of(ControlLine.RTS, ControlLine.DTR, ControlLine.DSR, ControlLine.CD, ControlLine.RI);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
-8
@@ -260,10 +260,6 @@ public class ProlificSerialDriver implements UsbSerialDriver {
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
private boolean testStatusFlag(int flag) throws IOException {
|
||||
return ((getStatus() & flag) == flag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openInt() throws IOException {
|
||||
UsbInterface usbInterface = mDevice.getInterface(0);
|
||||
@@ -474,17 +470,17 @@ public class ProlificSerialDriver implements UsbSerialDriver {
|
||||
|
||||
@Override
|
||||
public boolean getCD() throws IOException {
|
||||
return testStatusFlag(STATUS_FLAG_CD);
|
||||
return (getStatus() & STATUS_FLAG_CD) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getCTS() throws IOException {
|
||||
return testStatusFlag(STATUS_FLAG_CTS);
|
||||
return (getStatus() & STATUS_FLAG_CTS) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getDSR() throws IOException {
|
||||
return testStatusFlag(STATUS_FLAG_DSR);
|
||||
return (getStatus() & STATUS_FLAG_DSR) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -505,7 +501,7 @@ public class ProlificSerialDriver implements UsbSerialDriver {
|
||||
|
||||
@Override
|
||||
public boolean getRI() throws IOException {
|
||||
return testStatusFlag(STATUS_FLAG_RI);
|
||||
return (getStatus() & STATUS_FLAG_RI) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user