Skip to content

Commit

Permalink
flowcontrol for ftdi, pl2303, cp210x
Browse files Browse the repository at this point in the history
  • Loading branch information
kai-morich committed Jul 5, 2024
1 parent 8437920 commit 88ca3f5
Show file tree
Hide file tree
Showing 14 changed files with 655 additions and 36 deletions.
3 changes: 2 additions & 1 deletion .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,10 @@ For a simple example, see
[UsbSerialExamples](https://github.com/mik3y/usb-serial-for-android/blob/master/usbSerialExamples)
folder in this project.

For a more complete example with background service to stay connected while
the app is not visible or rotating, see separate github project
[SimpleUsbTerminal](https://github.com/kai-morich/SimpleUsbTerminal).
See separate github project [SimpleUsbTerminal](https://github.com/kai-morich/SimpleUsbTerminal)
for a more complete example with:
* Background service to stay connected while the app is not visible or rotating
* Flow control

## Probing for Unrecognized Devices

Expand Down
2 changes: 1 addition & 1 deletion usbSerialForAndroid/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ project.afterEvaluate {
// values used for local maven repo, jitpack uses github release:
groupId 'com.github.mik3y'
artifactId 'usb-serial-for-android'
version '3.7.3beta'
version '3.8.0beta'
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions usbSerialForAndroid/coverage.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ android {
}
cp2102 { // and cp2105 first port
dimension 'device'
testInstrumentationRunnerArguments = ['test_device_driver': 'Cp21xx']
testInstrumentationRunnerArguments = ['test_device_driver': 'Cp21xx', 'test_device_port': '0']
}
cp2105 { // second port
dimension 'device'
testInstrumentationRunnerArguments = ['test_device_driver': 'Cp21xx', 'test_device_port': '1']
}
ft232 { // and ft2232 first port
dimension 'device'
testInstrumentationRunnerArguments = ['test_device_driver': 'Ftdi']
testInstrumentationRunnerArguments = ['test_device_driver': 'Ftdi', 'test_device_port': '0']
}
ft2232 { // second port
dimension 'device'
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.hoho.android.usbserial.driver.Cp21xxSerialDriver;
import com.hoho.android.usbserial.driver.FtdiSerialDriver;
import com.hoho.android.usbserial.driver.ProlificSerialDriver;
import com.hoho.android.usbserial.driver.ProlificSerialPortWrapper;
import com.hoho.android.usbserial.driver.UsbId;
import com.hoho.android.usbserial.driver.UsbSerialDriver;
import com.hoho.android.usbserial.driver.UsbSerialPort;
Expand Down Expand Up @@ -62,6 +63,7 @@ public enum OpenCloseFlags { NO_IOMANAGER_THREAD, NO_IOMANAGER_START, NO_CONTROL
public boolean inputLinesOnlyRtsCts;
public int writePacketSize = -1;
public int writeBufferSize = -1;
public int readBufferSize = -1;

public UsbWrapper(Context context, UsbSerialDriver serialDriver, int devicePort) {
this.context = context;
Expand Down Expand Up @@ -145,10 +147,18 @@ public void onReceive(Context context, Intent intent) {
case 2: writePacketSize = 512; writeBufferSize = 4096; break;
case 4: writePacketSize = 512; writeBufferSize = 2048; break;
}
if(serialDriver.getDevice().getProductId() == UsbId.FTDI_FT231X)
writeBufferSize = 512;
} else if (serialDriver instanceof CdcAcmSerialDriver) {
writePacketSize = 64; writeBufferSize = 128;
}

readBufferSize = writeBufferSize;
if (serialDriver instanceof Cp21xxSerialDriver && serialDriver.getPorts().size() == 2) {
readBufferSize = 256;
} else if (serialDriver instanceof FtdiSerialDriver && serialDriver.getPorts().size() == 1 && serialDriver.getDevice().getProductId() != UsbId.FTDI_FT231X) {
readBufferSize = 256;
} // PL2303 HXN checked in open()
}

public void tearDown() {
Expand Down Expand Up @@ -177,6 +187,8 @@ public void close(EnumSet<OpenCloseFlags> flags) {
if(!flags.contains(OpenCloseFlags.NO_CONTROL_LINE_INIT)) {
serialPort.setDTR(false);
serialPort.setRTS(false);
if (serialPort.getFlowControl() != UsbSerialPort.FlowControl.NONE)
serialPort.setFlowControl(UsbSerialPort.FlowControl.NONE);
}
} catch (Exception ignored) {
}
Expand Down Expand Up @@ -226,6 +238,10 @@ public void open(EnumSet<OpenCloseFlags> flags) throws Exception {
readBuffer.clear();
}
readError = null;

if (serialDriver instanceof ProlificSerialDriver && ProlificSerialPortWrapper.isDeviceTypeHxn(serialPort)) {
readBufferSize = 768;
}
}

public void waitForIoManagerStarted() throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,6 @@ public UsbSerialDriver getDriver() {
public void setParameters(int baudRate, int dataBits, int stopBits, int parity) throws IOException {
throw new UnsupportedOperationException();
}

@Override
public EnumSet<ControlLine> getSupportedControlLines() throws IOException {
return EnumSet.noneOf(ControlLine.class);
}
}

public static Map<Integer, int[]> getSupportedDevices() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.InvalidParameterException;
import java.util.EnumSet;

/**
Expand All @@ -38,6 +39,7 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
protected UsbEndpoint mReadEndpoint;
protected UsbEndpoint mWriteEndpoint;
protected UsbRequest mUsbRequest;
protected FlowControl mFlowControl = FlowControl.NONE;

/**
* Internal write buffer.
Expand Down Expand Up @@ -281,6 +283,7 @@ public void write(final byte[] src, int length, final int timeout) throws IOExce
if (actualLength <= 0) {
String msg = "Error writing " + requestLength + " bytes at offset " + offset + " of total " + src.length + " after " + elapsed + "msec, rc=" + actualLength;
if (timeout != 0) {
// could be buffer full because: writing to fast, stopped by flow control
testConnection(elapsed < timeout, msg);
throw new SerialTimeoutException(msg, offset);
} else {
Expand Down Expand Up @@ -328,12 +331,22 @@ public boolean isOpen() {
public EnumSet<ControlLine> getControlLines() throws IOException { throw new UnsupportedOperationException(); }

@Override
public abstract EnumSet<ControlLine> getSupportedControlLines() throws IOException;
public EnumSet<ControlLine> getSupportedControlLines() throws IOException { return EnumSet.noneOf(ControlLine.class); }

@Override
public void purgeHwBuffers(boolean purgeWriteBuffers, boolean purgeReadBuffers) throws IOException {
throw new UnsupportedOperationException();
}
public void setFlowControl(FlowControl flowcontrol) throws IOException { throw new UnsupportedOperationException(); }

@Override
public FlowControl getFlowControl() { return mFlowControl; }

@Override
public EnumSet<FlowControl> getSupportedFlowControl() { return EnumSet.of(FlowControl.NONE); }

@Override
public boolean getXON() throws IOException { throw new UnsupportedOperationException(); }

@Override
public void purgeHwBuffers(boolean purgeWriteBuffers, boolean purgeReadBuffers) throws IOException { throw new UnsupportedOperationException(); }

@Override
public void setBreak(boolean value) throws IOException { throw new UnsupportedOperationException(); }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,14 @@ public class Cp21xxSerialPort extends CommonUsbSerialPort {
private static final int SILABSER_SET_LINE_CTL_REQUEST_CODE = 0x03;
private static final int SILABSER_SET_BREAK_REQUEST_CODE = 0x05;
private static final int SILABSER_SET_MHS_REQUEST_CODE = 0x07;
private static final int SILABSER_SET_BAUDRATE = 0x1E;
private static final int SILABSER_FLUSH_REQUEST_CODE = 0x12;
private static final int SILABSER_GET_MDMSTS_REQUEST_CODE = 0x08;
private static final int SILABSER_SET_XON_REQUEST_CODE = 0x09;
private static final int SILABSER_SET_XOFF_REQUEST_CODE = 0x0A;
private static final int SILABSER_GET_COMM_STATUS_REQUEST_CODE = 0x10;
private static final int SILABSER_FLUSH_REQUEST_CODE = 0x12;
private static final int SILABSER_SET_FLOW_REQUEST_CODE = 0x13;
private static final int SILABSER_SET_CHARS_REQUEST_CODE = 0x19;
private static final int SILABSER_SET_BAUDRATE_REQUEST_CODE = 0x1E;

private static final int FLUSH_READ_CODE = 0x0a;
private static final int FLUSH_WRITE_CODE = 0x05;
Expand All @@ -84,6 +89,8 @@ public class Cp21xxSerialPort extends CommonUsbSerialPort {
/*
* SILABSER_GET_MDMSTS_REQUEST_CODE
*/
private static final int STATUS_DTR = 0x01;
private static final int STATUS_RTS = 0x02;
private static final int STATUS_CTS = 0x10;
private static final int STATUS_DSR = 0x20;
private static final int STATUS_RI = 0x40;
Expand Down Expand Up @@ -147,6 +154,7 @@ protected void openInt() throws IOException {

setConfigSingle(SILABSER_IFC_ENABLE_REQUEST_CODE, UART_ENABLE);
setConfigSingle(SILABSER_SET_MHS_REQUEST_CODE, (dtr ? DTR_ENABLE : DTR_DISABLE) | (rts ? RTS_ENABLE : RTS_DISABLE));
setFlowControl(mFlowControl);
}

@Override
Expand All @@ -166,7 +174,7 @@ private void setBaudRate(int baudRate) throws IOException {
(byte) ((baudRate >> 16) & 0xff),
(byte) ((baudRate >> 24) & 0xff)
};
int ret = mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, SILABSER_SET_BAUDRATE,
int ret = mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, SILABSER_SET_BAUDRATE_REQUEST_CODE,
0, mPortNumber, data, 4, USB_WRITE_TIMEOUT_MILLIS);
if (ret < 0) {
throw new IOException("Error setting baud rate");
Expand Down Expand Up @@ -289,9 +297,11 @@ public void setRTS(boolean value) throws IOException {
public EnumSet<ControlLine> getControlLines() throws IOException {
byte status = getStatus();
EnumSet<ControlLine> set = EnumSet.noneOf(ControlLine.class);
if(rts) set.add(ControlLine.RTS);
//if(rts) set.add(ControlLine.RTS); // configured value
if((status & STATUS_RTS) != 0) set.add(ControlLine.RTS); // actual value
if((status & STATUS_CTS) != 0) set.add(ControlLine.CTS);
if(dtr) set.add(ControlLine.DTR);
//if(dtr) set.add(ControlLine.DTR); // configured value
if((status & STATUS_DTR) != 0) set.add(ControlLine.DTR); // actual value
if((status & STATUS_DSR) != 0) set.add(ControlLine.DSR);
if((status & STATUS_CD) != 0) set.add(ControlLine.CD);
if((status & STATUS_RI) != 0) set.add(ControlLine.RI);
Expand All @@ -303,6 +313,73 @@ public EnumSet<ControlLine> getSupportedControlLines() throws IOException {
return EnumSet.allOf(ControlLine.class);
}

@Override
public boolean getXON() throws IOException {
byte[] buffer = new byte[0x13];
int result = mConnection.controlTransfer(REQTYPE_DEVICE_TO_HOST, SILABSER_GET_COMM_STATUS_REQUEST_CODE, 0,
mPortNumber, buffer, buffer.length, USB_WRITE_TIMEOUT_MILLIS);
if (result != buffer.length) {
throw new IOException("Control transfer failed: " + SILABSER_GET_COMM_STATUS_REQUEST_CODE + " -> " + result);
}
return (buffer[4] & 8) == 0;
}

/**
* emulate external XON/OFF
* @throws IOException
*/
public void setXON(boolean value) throws IOException {
setConfigSingle(value ? SILABSER_SET_XON_REQUEST_CODE : SILABSER_SET_XOFF_REQUEST_CODE, 0);
}

@Override
public void setFlowControl(FlowControl flowControl) throws IOException {
byte[] data = new byte[16];
if(flowControl == FlowControl.RTS_CTS) {
data[4] |= 0b1000_0000; // RTS
data[0] |= 0b0000_1000; // CTS
} else {
if(rts)
data[4] |= 0b0100_0000;
}
if(flowControl == FlowControl.DTR_DSR) {
data[0] |= 0b0000_0010; // DTR
data[0] |= 0b0001_0000; // DSR
} else {
if(dtr)
data[0] |= 0b0000_0001;
}
if(flowControl == FlowControl.XON_XOFF) {
byte[] chars = new byte[]{0, 0, 0, 0, CHAR_XON, CHAR_XOFF};
int ret = mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, SILABSER_SET_CHARS_REQUEST_CODE,
0, mPortNumber, chars, chars.length, USB_WRITE_TIMEOUT_MILLIS);
if (ret != chars.length) {
throw new IOException("Error setting XON/XOFF chars");
}
data[4] |= 0b0000_0011;
data[7] |= 0b1000_0000;
data[8] = (byte)128;
data[12] = (byte)128;
}
if(flowControl == FlowControl.XON_XOFF_INLINE) {
throw new UnsupportedOperationException();
}
int ret = mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, SILABSER_SET_FLOW_REQUEST_CODE,
0, mPortNumber, data, data.length, USB_WRITE_TIMEOUT_MILLIS);
if (ret != data.length) {
throw new IOException("Error setting flow control");
}
if(flowControl == FlowControl.XON_XOFF) {
setXON(true);
}
mFlowControl = flowControl;
}

@Override
public EnumSet<FlowControl> getSupportedFlowControl() {
return EnumSet.of(FlowControl.NONE, FlowControl.RTS_CTS, FlowControl.DTR_DSR, FlowControl.XON_XOFF);
}

@Override
// note: only working on some devices, on other devices ignored w/o error
public void purgeHwBuffers(boolean purgeWriteBuffers, boolean purgeReadBuffers) throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public class FtdiSerialPort extends CommonUsbSerialPort {

private static final int RESET_REQUEST = 0;
private static final int MODEM_CONTROL_REQUEST = 1;
private static final int SET_FLOW_CONTROL_REQUEST = 2;
private static final int SET_BAUD_RATE_REQUEST = 3;
private static final int SET_DATA_REQUEST = 4;
private static final int GET_MODEM_STATUS_REQUEST = 5;
Expand Down Expand Up @@ -120,6 +121,7 @@ protected void openInt() throws IOException {
if (result != 0) {
throw new IOException("Init RTS,DTR failed: result=" + result);
}
setFlowControl(mFlowControl);

// mDevice.getVersion() would require API 23
byte[] rawDescriptors = mConnection.getRawDescriptors();
Expand Down Expand Up @@ -377,6 +379,38 @@ public EnumSet<ControlLine> getSupportedControlLines() throws IOException {
return EnumSet.allOf(ControlLine.class);
}

@Override
public void setFlowControl(FlowControl flowControl) throws IOException {
int value = 0;
int index = mPortNumber+1;
switch (flowControl) {
case NONE:
break;
case RTS_CTS:
index |= 0x100;
break;
case DTR_DSR:
index |= 0x200;
break;
case XON_XOFF_INLINE:
value = CHAR_XON + (CHAR_XOFF << 8);
index |= 0x400;
break;
default:
throw new UnsupportedOperationException();
}
int result = mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, SET_FLOW_CONTROL_REQUEST,
value, index, null, 0, USB_WRITE_TIMEOUT_MILLIS);
if (result != 0)
throw new IOException("Set flow control failed: result=" + result);
mFlowControl = flowControl;
}

@Override
public EnumSet<FlowControl> getSupportedFlowControl() {
return EnumSet.of(FlowControl.NONE, FlowControl.RTS_CTS, FlowControl.DTR_DSR, FlowControl.XON_XOFF_INLINE);
}

@Override
public void purgeHwBuffers(boolean purgeWriteBuffers, boolean purgeReadBuffers) throws IOException {
if (purgeWriteBuffers) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,6 @@ public void setParameters(int baudRate, int dataBits, int stopBits, int parity)
throw new UnsupportedOperationException();
}

@Override
public EnumSet<ControlLine> getSupportedControlLines() throws IOException {
return EnumSet.noneOf(ControlLine.class);
}
}

public static Map<Integer, int[]> getSupportedDevices() {
Expand Down
Loading

0 comments on commit 88ca3f5

Please sign in to comment.