1
0
mirror of https://github.com/mik3y/usb-serial-for-android.git synced 2026-08-13 17:33:00 +00:00

reimplement read timeout

This commit is contained in:
kai-morich
2019-11-15 21:45:22 +01:00
parent 669ab48e0f
commit e2e9df8463
4 changed files with 210 additions and 109 deletions
@@ -39,6 +39,7 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
private static final String TAG = CommonUsbSerialPort.class.getSimpleName();
private static final int DEFAULT_WRITE_BUFFER_SIZE = 16 * 1024;
private static final int MAX_READ_SIZE = 16 * 1024; // = old bulkTransfer limit
protected final UsbDevice mDevice;
protected final int mPortNumber;
@@ -131,41 +132,56 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
protected abstract void closeInt();
@Override
public int read(final byte[] dest, final int timeoutMillis) throws IOException {
public int read(final byte[] dest, final int timeout) throws IOException {
if(mConnection == null) {
throw new IOException("Connection closed");
}
final UsbRequest request = new UsbRequest();
try {
request.initialize(mConnection, mReadEndpoint);
final ByteBuffer buf = ByteBuffer.wrap(dest);
if (!request.queue(buf, dest.length)) {
throw new IOException("Error queueing request");
}
mUsbRequest = request;
final UsbRequest response = mConnection.requestWait();
synchronized (this) {
final int nread;
if (timeout != 0) {
// bulkTransfer will cause data loss with short timeout + high baud rates + continuous transfer
// https://stackoverflow.com/questions/9108548/android-usb-host-bulktransfer-is-losing-data
// but mConnection.requestWait(timeout) available since Android 8.0 es even worse,
// as it crashes with short timeout, e.g.
// A/libc: Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x276a in tid 29846 (pool-2-thread-1), pid 29618 (.usbserial.test)
// /system/lib64/libusbhost.so (usb_request_wait+192)
// /system/lib64/libandroid_runtime.so (android_hardware_UsbDeviceConnection_request_wait(_JNIEnv*, _jobject*, long)+84)
// data loss / crashes were observed with timeout up to 200 msec
int readMax = Math.min(dest.length, MAX_READ_SIZE);
nread = mConnection.bulkTransfer(mReadEndpoint, dest, readMax, timeout);
} else {
final UsbRequest request = new UsbRequest();
try {
request.initialize(mConnection, mReadEndpoint);
final ByteBuffer buf = ByteBuffer.wrap(dest);
if (!request.queue(buf, dest.length)) {
throw new IOException("Error queueing request");
}
mUsbRequest = request;
final UsbRequest response = mConnection.requestWait();
synchronized (this) {
mUsbRequest = null;
}
if (response == null) {
throw new IOException("Null response");
}
nread = buf.position();
} finally {
mUsbRequest = null;
request.close();
}
if (response == null) {
throw new IOException("Null response");
}
final int nread = buf.position();
if (nread > 0) {
return readFilter(dest, nread);
} else {
return 0;
}
} finally {
mUsbRequest = null;
request.close();
}
if (nread > 0) {
return readFilter(dest, nread);
} else {
return 0;
}
}
protected int readFilter(final byte[] buffer, int len) throws IOException { return len; }
@Override
public int write(final byte[] src, final int timeoutMillis) throws IOException {
public int write(final byte[] src, final int timeout) throws IOException {
int offset = 0;
if(mConnection == null) {
@@ -187,8 +203,7 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
writeBuffer = mWriteBuffer;
}
amtWritten = mConnection.bulkTransfer(mWriteEndpoint, writeBuffer, writeLength,
timeoutMillis);
amtWritten = mConnection.bulkTransfer(mWriteEndpoint, writeBuffer, writeLength, timeout);
}
if (amtWritten <= 0) {
throw new IOException("Error writing " + writeLength
@@ -24,6 +24,7 @@ package com.hoho.android.usbserial.driver;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager;
import java.io.Closeable;
import java.io.IOException;
/**
@@ -31,7 +32,7 @@ import java.io.IOException;
*
* @author mike wakerly (opensource@hoho.com)
*/
public interface UsbSerialPort {
public interface UsbSerialPort extends Closeable {
/** 5 data bits. */
public static final int DATABITS_5 = 5;
@@ -117,21 +118,21 @@ public interface UsbSerialPort {
* Reads as many bytes as possible into the destination buffer.
*
* @param dest the destination byte buffer
* @param timeoutMillis the timeout for reading
* @param timeout the timeout for reading in milliseconds, 0 is infinite
* @return the actual number of bytes read
* @throws IOException if an error occurred during reading
*/
public int read(final byte[] dest, final int timeoutMillis) throws IOException;
public int read(final byte[] dest, final int timeout) throws IOException;
/**
* Writes as many bytes as possible from the source buffer.
*
* @param src the source byte buffer
* @param timeoutMillis the timeout for writing
* @param timeout the timeout for writing in milliseconds, 0 is infinite
* @return the actual number of bytes written
* @throws IOException if an error occurred during writing
*/
public int write(final byte[] src, final int timeoutMillis) throws IOException;
public int write(final byte[] src, final int timeout) throws IOException;
/**
* Sets various serial port parameters.
@@ -21,7 +21,6 @@
package com.hoho.android.usbserial.util;
import android.hardware.usb.UsbRequest;
import android.util.Log;
import com.hoho.android.usbserial.driver.UsbSerialPort;
@@ -30,8 +29,7 @@ import java.io.IOException;
import java.nio.ByteBuffer;
/**
* Utility class which services a {@link UsbSerialPort} in its {@link #run()}
* method.
* Utility class which services a {@link UsbSerialPort} in its {@link #run()} method.
*
* @author mike wakerly (opensource@hoho.com)
*/
@@ -39,16 +37,16 @@ public class SerialInputOutputManager implements Runnable {
private static final String TAG = SerialInputOutputManager.class.getSimpleName();
private static final boolean DEBUG = true;
private static final int READ_WAIT_MILLIS = 200;
private static final int BUFSIZ = 4096;
private final UsbSerialPort mDriver;
/**
* default read timeout is infinite, to avoid data loss with bulkTransfer API
*/
private int mReadTimeout = 0;
private int mWriteTimeout = 0;
private final ByteBuffer mReadBuffer = ByteBuffer.allocate(BUFSIZ);
// Synchronized by 'mWriteBuffer'
private final ByteBuffer mWriteBuffer = ByteBuffer.allocate(BUFSIZ);
private final ByteBuffer mWriteBuffer = ByteBuffer.allocate(BUFSIZ); // Synchronized by 'mWriteBuffer'
public enum State {
STOPPED,
@@ -56,11 +54,9 @@ public class SerialInputOutputManager implements Runnable {
STOPPING
}
// Synchronized by 'this'
private State mState = State.STOPPED;
// Synchronized by 'this'
private Listener mListener;
private State mState = State.STOPPED; // Synchronized by 'this'
private Listener mListener; // Synchronized by 'this'
private final UsbSerialPort mSerialPort;
public interface Listener {
/**
@@ -69,24 +65,17 @@ public class SerialInputOutputManager implements Runnable {
public void onNewData(byte[] data);
/**
* Called when {@link SerialInputOutputManager#run()} aborts due to an
* error.
* Called when {@link SerialInputOutputManager#run()} aborts due to an error.
*/
public void onRunError(Exception e);
}
/**
* Creates a new instance with no listener.
*/
public SerialInputOutputManager(UsbSerialPort driver) {
this(driver, null);
public SerialInputOutputManager(UsbSerialPort serialPort) {
mSerialPort = serialPort;
}
/**
* Creates a new instance with the provided listener.
*/
public SerialInputOutputManager(UsbSerialPort driver, Listener listener) {
mDriver = driver;
public SerialInputOutputManager(UsbSerialPort serialPort, Listener listener) {
mSerialPort = serialPort;
mListener = listener;
}
@@ -98,6 +87,29 @@ public class SerialInputOutputManager implements Runnable {
return mListener;
}
public void setReadTimeout(int timeout) {
// when set if already running, read already blocks and the new value will not become effective now
if(mReadTimeout == 0 && timeout != 0 && mState != State.STOPPED)
throw new IllegalStateException("Set readTimeout before SerialInputOutputManager is started");
mReadTimeout = timeout;
}
public int getReadTimeout() {
return mReadTimeout;
}
public void setWriteTimeout(int timeout) {
mWriteTimeout = timeout;
}
public int getWriteTimeout() {
return mWriteTimeout;
}
/*
* when writeAsync is used, it is recommended to use readTimeout != 0,
* else the write will be delayed until read data is available
*/
public void writeAsync(byte[] data) {
synchronized (mWriteBuffer) {
mWriteBuffer.put(data);
@@ -155,7 +167,7 @@ public class SerialInputOutputManager implements Runnable {
private void step() throws IOException {
// Handle incoming data.
int len = mDriver.read(mReadBuffer.array(), READ_WAIT_MILLIS);
int len = mSerialPort.read(mReadBuffer.array(), mReadTimeout);
if (len > 0) {
if (DEBUG) Log.d(TAG, "Read data len=" + len);
final Listener listener = getListener();
@@ -182,7 +194,7 @@ public class SerialInputOutputManager implements Runnable {
if (DEBUG) {
Log.d(TAG, "Writing data len=" + len);
}
mDriver.write(outBuff, READ_WAIT_MILLIS);
mSerialPort.write(outBuff, mWriteTimeout);
}
}