Always use async read, as bulkTransfer can cause data loss. Increase API version to 17 because async read only works reliably since Android 4.2 (http://b.android.com/28023)

pull/230/head
Kai Morich 2019-02-03 10:22:53 +01:00
rodzic e527afdf35
commit 0d48ed04e7
10 zmienionych plików z 103 dodań i 225 usunięć

Wyświetl plik

@ -3,7 +3,7 @@
This is a driver library for communication with Arduinos and other USB serial hardware on
Android, using the
[Android USB Host API](http://developer.android.com/guide/topics/connectivity/usb/host.html)
available on Android 3.1+.
available since Android 3.1 and asynchronous interrupt transfer working reliably since Android 4.2
No root access, ADK, or special kernel drivers are required; all drivers are implemented in
Java. You get a raw serial port with `read()`, `write()`, and other basic

Wyświetl plik

@ -6,7 +6,7 @@ buildscript {
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
classpath 'com.android.tools.build:gradle:3.3.0'
}
}

Wyświetl plik

@ -1,6 +1,6 @@
#Sun Nov 11 09:16:07 CET 2018
#Sun Feb 03 09:37:03 CET 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip

Wyświetl plik

@ -5,7 +5,7 @@ android {
buildToolsVersion '28.0.3'
defaultConfig {
minSdkVersion 14
minSdkVersion 17
targetSdkVersion 28
testInstrumentationRunner "android.test.InstrumentationTestRunner"

Wyświetl plik

@ -7,7 +7,7 @@ android {
buildToolsVersion '28.0.3'
defaultConfig {
minSdkVersion 14
minSdkVersion 17
targetSdkVersion 28
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}

Wyświetl plik

@ -27,7 +27,6 @@ import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbRequest;
import android.os.Build;
import android.util.Log;
import java.io.IOException;
@ -69,7 +68,6 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
class CdcAcmSerialPort extends CommonUsbSerialPort {
private final boolean mEnableAsyncReads;
private UsbInterface mControlInterface;
private UsbInterface mDataInterface;
@ -92,7 +90,6 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
public CdcAcmSerialPort(UsbDevice device, int portNumber) {
super(device, portNumber);
mEnableAsyncReads = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1);
}
@Override
@ -118,13 +115,6 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
openInterface();
}
if (mEnableAsyncReads) {
Log.d(TAG, "Async reads enabled");
} else {
Log.d(TAG, "Async reads disabled.");
}
opened = true;
} finally {
if (!opened) {
@ -269,51 +259,29 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
@Override
public int read(byte[] dest, int timeoutMillis) throws IOException {
if (mEnableAsyncReads) {
final UsbRequest request = new UsbRequest();
try {
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.");
throw new IOException("Error queueing request.");
}
final UsbRequest response = mConnection.requestWait();
if (response == null) {
throw new IOException("Null response");
throw new IOException("Null response");
}
final int nread = buf.position();
if (nread > 0) {
//Log.d(TAG, HexDump.dumpHexString(dest, 0, Math.min(32, dest.length)));
return nread;
//Log.d(TAG, HexDump.dumpHexString(dest, 0, Math.min(32, dest.length)));
return nread;
} else {
return 0;
}
} finally {
request.close();
}
}
final int numBytesRead;
synchronized (mReadBufferLock) {
int readAmt = Math.min(dest.length, mReadBuffer.length);
numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer, readAmt,
timeoutMillis);
if (numBytesRead < 0) {
// This sucks: we get -1 on timeout, not 0 as preferred.
// We *should* use UsbRequest, except it has a bug/api oversight
// where there is no way to determine the number of bytes read
// in response :\ -- http://b.android.com/28023
if (timeoutMillis == Integer.MAX_VALUE) {
// Hack: Special case "~infinite timeout" as an error.
return -1;
}
return 0;
}
System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
} finally {
request.close();
}
return numBytesRead;
}
@Override

Wyświetl plik

@ -26,7 +26,6 @@ import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbRequest;
import android.os.Build;
import android.util.Log;
import java.io.IOException;
@ -84,14 +83,12 @@ public class Ch34xSerialDriver implements UsbSerialDriver {
private boolean dtr = false;
private boolean rts = false;
private final Boolean mEnableAsyncReads;
private UsbEndpoint mReadEndpoint;
private UsbEndpoint mWriteEndpoint;
private UsbRequest mUsbRequest;
public Ch340SerialPort(UsbDevice device, int portNumber) {
super(device, portNumber);
mEnableAsyncReads = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1);
}
@Override
@ -127,12 +124,6 @@ public class Ch34xSerialDriver implements UsbSerialDriver {
}
}
if (mEnableAsyncReads) {
Log.d(TAG, "Async reads enabled");
} else {
Log.d(TAG, "Async reads disabled.");
}
initialize();
setBaudRate(DEFAULT_BAUD_RATE);
@ -153,12 +144,10 @@ public class Ch34xSerialDriver implements UsbSerialDriver {
if (mConnection == null) {
throw new IOException("Already closed");
}
synchronized (mEnableAsyncReads) {
synchronized (this) {
if (mUsbRequest != null)
mUsbRequest.cancel();
}
// TODO: nothing sended on close, maybe needed?
try {
mConnection.close();
} finally {
@ -169,50 +158,32 @@ public class Ch34xSerialDriver implements UsbSerialDriver {
@Override
public int read(byte[] dest, int timeoutMillis) throws IOException {
if (mEnableAsyncReads) {
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 (mEnableAsyncReads) {
mUsbRequest = null;
}
if (response == null) {
throw new IOException("Null response");
}
final int nread = buf.position();
if (nread > 0) {
//Log.d(TAG, HexDump.dumpHexString(dest, 0, Math.min(32, dest.length)));
return nread;
} else {
return 0;
}
} finally {
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;
request.close();
}
} else {
final int numBytesRead;
synchronized (mReadBufferLock) {
int readAmt = Math.min(dest.length, mReadBuffer.length);
numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer, readAmt,
timeoutMillis);
if (numBytesRead < 0) {
// This sucks: we get -1 on timeout, not 0 as preferred.
// We *should* use UsbRequest, except it has a bug/api oversight
// where there is no way to determine the number of bytes read
// in response :\ -- http://b.android.com/28023
return 0;
}
System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
if (response == null) {
throw new IOException("Null response");
}
return numBytesRead;
final int nread = buf.position();
if (nread > 0) {
//Log.d(TAG, HexDump.dumpHexString(dest, 0, Math.min(32, dest.length)));
return nread;
} else {
return 0;
}
} finally {
mUsbRequest = null;
request.close();
}
}

Wyświetl plik

@ -27,7 +27,6 @@ import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbRequest;
import android.os.Build;
import android.util.Log;
import java.io.IOException;
@ -107,7 +106,6 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
private static final int CONTROL_WRITE_DTR = 0x0100;
private static final int CONTROL_WRITE_RTS = 0x0200;
private final Boolean mEnableAsyncReads;
private UsbEndpoint mReadEndpoint;
private UsbEndpoint mWriteEndpoint;
private UsbRequest mUsbRequest;
@ -118,7 +116,6 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
public Cp21xxSerialPort(UsbDevice device, int portNumber) {
super(device, portNumber);
mEnableAsyncReads = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1);
}
@Override
@ -180,13 +177,16 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
if (mConnection == null) {
throw new IOException("Already closed");
}
synchronized (mEnableAsyncReads) {
synchronized (this) {
if(mUsbRequest != null) {
mUsbRequest.cancel();
}
}
try {
setConfigSingle(SILABSER_IFC_ENABLE_REQUEST_CODE, UART_DISABLE);
} catch (Exception ignored)
{}
try {
mConnection.close();
} finally {
mConnection = null;
@ -195,50 +195,32 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
@Override
public int read(byte[] dest, int timeoutMillis) throws IOException {
if (mEnableAsyncReads) {
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 (mEnableAsyncReads) {
mUsbRequest = null;
}
if (response == null) {
throw new IOException("Null response");
}
final int nread = buf.position();
if (nread > 0) {
//Log.d(TAG, HexDump.dumpHexString(dest, 0, Math.min(32, dest.length)));
return nread;
} else {
return 0;
}
} finally {
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;
request.close();
}
} else {
final int numBytesRead;
synchronized (mReadBufferLock) {
int readAmt = Math.min(dest.length, mReadBuffer.length);
numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer, readAmt,
timeoutMillis);
if (numBytesRead < 0) {
// This sucks: we get -1 on timeout, not 0 as preferred.
// We *should* use UsbRequest, except it has a bug/api oversight
// where there is no way to determine the number of bytes read
// in response :\ -- http://b.android.com/28023
return 0;
}
System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
if (response == null) {
throw new IOException("Null response");
}
return numBytesRead;
final int nread = buf.position();
if (nread > 0) {
//Log.d(TAG, HexDump.dumpHexString(dest, 0, Math.min(32, dest.length)));
return nread;
} else {
return 0;
}
} finally {
mUsbRequest = null;
request.close();
}
}

Wyświetl plik

@ -26,7 +26,6 @@ import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbRequest;
import android.os.Build;
import android.util.Log;
import java.io.IOException;
@ -186,16 +185,8 @@ public class FtdiSerialDriver implements UsbSerialDriver {
private int mIndex = 0;
/**
* Due to http://b.android.com/28023 , we cannot use UsbRequest async reads
* since it gives no indication of number of bytes read. Set this to
* {@code true} on platforms where it is fixed.
*/
private final boolean mEnableAsyncReads;
public FtdiSerialPort(UsbDevice device, int portNumber) {
super(device, portNumber);
mEnableAsyncReads = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1);
}
@Override
@ -286,46 +277,28 @@ public class FtdiSerialDriver implements UsbSerialDriver {
@Override
public int read(byte[] dest, int timeoutMillis) throws IOException {
final UsbEndpoint endpoint = mDevice.getInterface(mPortNumber).getEndpoint(0);
if (mEnableAsyncReads) {
final UsbRequest request = new UsbRequest();
final ByteBuffer buf = ByteBuffer.wrap(dest);
try {
request.initialize(mConnection, endpoint);
if (!request.queue(buf, dest.length)) {
throw new IOException("Error queueing request.");
}
final UsbRequest response = mConnection.requestWait();
if (response == null) {
throw new IOException("Null response");
}
} finally {
request.close();
final UsbRequest request = new UsbRequest();
final ByteBuffer buf = ByteBuffer.wrap(dest);
try {
request.initialize(mConnection, endpoint);
if (!request.queue(buf, dest.length)) {
throw new IOException("Error queueing request.");
}
final int totalBytesRead = buf.position();
if (totalBytesRead < MODEM_STATUS_HEADER_LENGTH) {
throw new IOException("Expected at least " + MODEM_STATUS_HEADER_LENGTH + " bytes");
}
return filterStatusBytes(dest, dest, totalBytesRead, endpoint.getMaxPacketSize());
} else {
final int totalBytesRead;
synchronized (mReadBufferLock) {
final int readAmt = Math.min(dest.length, mReadBuffer.length);
totalBytesRead = mConnection.bulkTransfer(endpoint, mReadBuffer,
readAmt, timeoutMillis);
if (totalBytesRead < MODEM_STATUS_HEADER_LENGTH) {
throw new IOException("Expected at least " + MODEM_STATUS_HEADER_LENGTH + " bytes");
}
return filterStatusBytes(mReadBuffer, dest, totalBytesRead, endpoint.getMaxPacketSize());
final UsbRequest response = mConnection.requestWait();
if (response == null) {
throw new IOException("Null response");
}
} finally {
request.close();
}
final int totalBytesRead = buf.position();
if (totalBytesRead < MODEM_STATUS_HEADER_LENGTH) {
throw new IOException("Expected at least " + MODEM_STATUS_HEADER_LENGTH + " bytes");
}
return filterStatusBytes(dest, dest, totalBytesRead, endpoint.getMaxPacketSize());
}
@Override

Wyświetl plik

@ -33,7 +33,6 @@ import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbRequest;
import android.os.Build;
import android.util.Log;
import java.io.IOException;
@ -112,7 +111,6 @@ public class ProlificSerialDriver implements UsbSerialDriver {
private int mDeviceType = DEVICE_TYPE_HX;
private final boolean mEnableAsyncReads;
private UsbEndpoint mReadEndpoint;
private UsbEndpoint mWriteEndpoint;
private UsbEndpoint mInterruptEndpoint;
@ -130,7 +128,6 @@ public class ProlificSerialDriver implements UsbSerialDriver {
public ProlificSerialPort(UsbDevice device, int portNumber) {
super(device, portNumber);
mEnableAsyncReads = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1);
}
@Override
@ -373,41 +370,28 @@ public class ProlificSerialDriver implements UsbSerialDriver {
@Override
public int read(byte[] dest, int timeoutMillis) throws IOException {
if (mEnableAsyncReads) {
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.");
}
final UsbRequest response = mConnection.requestWait();
if (response == null) {
throw new IOException("Null response");
}
final int nread = buf.position();
if (nread > 0) {
//Log.d(TAG, HexDump.dumpHexString(dest, 0, Math.min(32, dest.length)));
return nread;
} else {
return 0;
}
} finally {
request.close();
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.");
}
} else {
synchronized (mReadBufferLock) {
int readAmt = Math.min(dest.length, mReadBuffer.length);
int numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer,
readAmt, timeoutMillis);
if (numBytesRead < 0) {
return 0;
}
System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
return numBytesRead;
final UsbRequest response = mConnection.requestWait();
if (response == null) {
throw new IOException("Null response");
}
final int nread = buf.position();
if (nread > 0) {
//Log.d(TAG, HexDump.dumpHexString(dest, 0, Math.min(32, dest.length)));
return nread;
} else {
return 0;
}
} finally {
request.close();
}
}