From 26999e3626e2e30012bac11baefea43225f4dcd5 Mon Sep 17 00:00:00 2001 From: kai-morich Date: Sun, 6 Sep 2020 19:58:18 +0200 Subject: [PATCH] read with timeout now throws error on connection lost, e.g. device disconnected and similar connection lost detection for prolific input control lines --- .../hoho/android/usbserial/DeviceTest.java | 20 ++++++++++++++----- .../usbserial/driver/CommonUsbSerialPort.java | 20 ++++++++++++++++++- .../usbserial/driver/FtdiSerialDriver.java | 8 +++++--- .../driver/ProlificSerialDriver.java | 8 ++++---- 4 files changed, 43 insertions(+), 13 deletions(-) diff --git a/usbSerialForAndroid/src/androidTest/java/com/hoho/android/usbserial/DeviceTest.java b/usbSerialForAndroid/src/androidTest/java/com/hoho/android/usbserial/DeviceTest.java index b610dcf..e48c41a 100644 --- a/usbSerialForAndroid/src/androidTest/java/com/hoho/android/usbserial/DeviceTest.java +++ b/usbSerialForAndroid/src/androidTest/java/com/hoho/android/usbserial/DeviceTest.java @@ -1660,18 +1660,27 @@ public class DeviceTest { purged = false; } usb.deviceConnection.close(); - try { + try { // only Prolific driver has early exit if nothing changed usb.setParameters(115200, 8, 1, UsbSerialPort.PARITY_NONE); if(!(usb.serialDriver instanceof ProlificSerialDriver)) fail("setParameters error expected"); } catch (IOException ignored) { } + try { + usb.setParameters(57600, 8, 1, UsbSerialPort.PARITY_NONE); + fail("setParameters error expected"); + } catch (IOException ignored) { + } try { usb.write("x".getBytes()); fail("write error expected"); } catch (IOException ignored) { } - usb.serialPort.read(buf, 1000); // bulkTransfer returns -1 on timeout and error, so no exception thrown here + try { + usb.serialPort.read(buf, 1000); + fail("read error expected"); + } catch (IOException ignored) { + } try { usb.serialPort.read(buf, 0); fail("read error expected"); @@ -1683,16 +1692,17 @@ public class DeviceTest { } catch (IOException ignored) { } try { + if(usb.serialDriver instanceof ProlificSerialDriver) + Thread.sleep(600); // wait for background thread usb.serialPort.getRI(); - if(!(usb.serialDriver instanceof ProlificSerialDriver)) - fail("getRI error expected"); + fail("getRI error expected"); } catch (IOException ignored) { } catch (UnsupportedOperationException ignored) { } if(purged) { try { usb.serialPort.purgeHwBuffers(true, true); - fail("setRts error expected"); + fail("purgeHwBuffers error expected"); } catch (IOException ignored) { } } diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CommonUsbSerialPort.java b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CommonUsbSerialPort.java index 9134991..b8bf9c8 100644 --- a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CommonUsbSerialPort.java +++ b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CommonUsbSerialPort.java @@ -129,13 +129,27 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort { protected abstract void closeInt(); + /** + * use simple USB request supported by all devices to test if connection is still valid + */ + protected void testConnection() throws IOException { + byte[] buf = new byte[2]; + int len = mConnection.controlTransfer(0x80 /*DEVICE*/, 0 /*GET_STATUS*/, 0, 0, buf, buf.length, 200); + if(len < 0) + throw new IOException("USB get_status request failed"); + } + @Override public int read(final byte[] dest, final int timeout) throws IOException { + return read(dest, timeout, true); + } + + protected int read(final byte[] dest, final int timeout, boolean testConnection) throws IOException { if(mConnection == null) { throw new IOException("Connection closed"); } if(dest.length <= 0) { - throw new IllegalArgumentException("read buffer to small"); + throw new IllegalArgumentException("Read buffer to small"); } final int nread; if (timeout != 0) { @@ -147,8 +161,12 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort { // /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 + long endTime = testConnection ? System.currentTimeMillis() + timeout : 0; int readMax = Math.min(dest.length, MAX_READ_SIZE); nread = mConnection.bulkTransfer(mReadEndpoint, dest, readMax, timeout); + // Android error propagation is improvable, nread == -1 can be: timeout, connection lost, buffer undersized, ... + if(nread == -1 && testConnection && System.currentTimeMillis() < endTime) + testConnection(); } else { final ByteBuffer buf = ByteBuffer.wrap(dest); diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/FtdiSerialDriver.java b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/FtdiSerialDriver.java index 54f6903..c0f257c 100644 --- a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/FtdiSerialDriver.java +++ b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/FtdiSerialDriver.java @@ -138,7 +138,7 @@ public class FtdiSerialDriver implements UsbSerialDriver { @Override public int read(final byte[] dest, final int timeout) throws IOException { if(dest.length <= READ_HEADER_LENGTH) { - throw new IllegalArgumentException("read buffer to small"); + throw new IllegalArgumentException("Read buffer to small"); // could allocate larger buffer, including space for 2 header bytes, but this would // result in buffers not being 64 byte aligned any more, causing data loss at continuous // data transfer at high baud rates when buffers are fully filled. @@ -147,11 +147,13 @@ public class FtdiSerialDriver implements UsbSerialDriver { if (timeout != 0) { long endTime = System.currentTimeMillis() + timeout; do { - nread = super.read(dest, Math.max(1, (int)(endTime - System.currentTimeMillis()))); + nread = super.read(dest, Math.max(1, (int)(endTime - System.currentTimeMillis())), false); } while (nread == READ_HEADER_LENGTH && System.currentTimeMillis() < endTime); + if(nread <= 0 && System.currentTimeMillis() < endTime) + testConnection(); } else { do { - nread = super.read(dest, timeout); + nread = super.read(dest, timeout, false); } while (nread == READ_HEADER_LENGTH); } return readFilter(dest, nread); diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/ProlificSerialDriver.java b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/ProlificSerialDriver.java index 0d28dba..2ca1afd 100644 --- a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/ProlificSerialDriver.java +++ b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/ProlificSerialDriver.java @@ -184,10 +184,10 @@ public class ProlificSerialDriver implements UsbSerialDriver { try { while (!mStopReadStatusThread) { byte[] buffer = new byte[STATUS_BUFFER_SIZE]; - int readBytesCount = mConnection.bulkTransfer(mInterruptEndpoint, - buffer, - STATUS_BUFFER_SIZE, - 500); + long endTime = System.currentTimeMillis() + 500; + int readBytesCount = mConnection.bulkTransfer(mInterruptEndpoint, buffer, STATUS_BUFFER_SIZE, 500); + if(readBytesCount == -1 && System.currentTimeMillis() < endTime) + testConnection(); if (readBytesCount > 0) { if (readBytesCount == STATUS_BUFFER_SIZE) { mStatus = buffer[STATUS_BYTE_IDX] & 0xff;