mirror of
				https://github.com/mik3y/usb-serial-for-android
				synced 2025-10-30 18:07:21 +00:00 
			
		
		
		
	simplify write timeout handling
This commit is contained in:
		
							parent
							
								
									f60414f8ec
								
							
						
					
					
						commit
						4ffcc8d0fb
					
				| @ -28,5 +28,5 @@ android { | |||||||
| dependencies { | dependencies { | ||||||
|     implementation project(':usbSerialForAndroid') |     implementation project(':usbSerialForAndroid') | ||||||
|     implementation 'androidx.appcompat:appcompat:1.2.0' |     implementation 'androidx.appcompat:appcompat:1.2.0' | ||||||
|     implementation 'com.google.android.material:material:1.2.1' |     implementation 'com.google.android.material:material:1.3.0' | ||||||
| } | } | ||||||
|  | |||||||
| @ -780,6 +780,30 @@ public class DeviceTest { | |||||||
|         assertEquals(availableDrivers.get(0).getClass(), usb.serialDriver.getClass()); |         assertEquals(availableDrivers.get(0).getClass(), usb.serialDriver.getClass()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     // return [write packet size, write buffer size(s)] | ||||||
|  |     private int[] getWriteSizes() { | ||||||
|  |         if (usb.serialDriver instanceof Cp21xxSerialDriver) { | ||||||
|  |             if (usb.serialDriver.getPorts().size() == 1) return new int[]{64, 576}; | ||||||
|  |             else if (usb.serialPort.getPortNumber() == 0) return new int[]{64, 320}; | ||||||
|  |             else return new int[]{32, 128, 160}; // write buffer size detection is unreliable | ||||||
|  |         } else if (usb.serialDriver instanceof Ch34xSerialDriver) { | ||||||
|  |             return new int[]{32, 64}; | ||||||
|  |         } else if (usb.serialDriver instanceof ProlificSerialDriver) { | ||||||
|  |             return new int[]{64, 256}; | ||||||
|  |         } else if (usb.serialDriver instanceof FtdiSerialDriver) { | ||||||
|  |             switch (usb.serialDriver.getPorts().size()) { | ||||||
|  |                 case 1: return new int[]{64, 128}; | ||||||
|  |                 case 2: return new int[]{512, 4096}; | ||||||
|  |                 case 4: return new int[]{512, 2048}; | ||||||
|  |                 default: return null; | ||||||
|  |             } | ||||||
|  |         } else if (usb.serialDriver instanceof CdcAcmSerialDriver) { | ||||||
|  |             return new int[]{64, 128}; | ||||||
|  |         } else { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @Test |     @Test | ||||||
|     public void writeTimeout() throws Exception { |     public void writeTimeout() throws Exception { | ||||||
|         usb.open(); |         usb.open(); | ||||||
| @ -806,29 +830,10 @@ public class DeviceTest { | |||||||
| 
 | 
 | ||||||
|         int writeBufferSize = writePacketSize * writePackets; |         int writeBufferSize = writePacketSize * writePackets; | ||||||
|         Log.d(TAG, "write packet size = " + writePacketSize + ", write buffer size = " + writeBufferSize); |         Log.d(TAG, "write packet size = " + writePacketSize + ", write buffer size = " + writeBufferSize); | ||||||
|         if(usb.serialDriver instanceof Cp21xxSerialDriver) { |         int[] writeSizes = getWriteSizes(); | ||||||
|             switch(usb.serialDriver.getPorts().size()*0x10 + usb.serialPort.getPortNumber()) { |         assertNotNull(writeSizes); | ||||||
|                 case 0x10: assertEquals("write packet size", 64, writePacketSize); assertEquals("write buffer size", 576, writeBufferSize); break; |         assertEquals("write packet size", writeSizes[0], writePacketSize); | ||||||
|                 case 0x20: assertEquals("write packet size", 64, writePacketSize); assertEquals("write buffer size", 320, writeBufferSize); break; |         assertTrue("write buffer size", Arrays.binarySearch(writeSizes, writeBufferSize) > 0); | ||||||
|                 case 0x21: assertEquals("write packet size", 32, writePacketSize); assertTrue("write buffer size", writeBufferSize == 128 || writeBufferSize == 160); break; |  | ||||||
|                 default: fail("unknown port number"); |  | ||||||
|             } |  | ||||||
|         } else if(usb.serialDriver instanceof Ch34xSerialDriver) { |  | ||||||
|             assertEquals("write packet size", 32, writePacketSize); assertEquals("write buffer size", 64, writeBufferSize); |  | ||||||
|         } else if(usb.serialDriver instanceof ProlificSerialDriver) { |  | ||||||
|             assertEquals("write packet size", 64, writePacketSize); assertEquals("write buffer size", 256, writeBufferSize); |  | ||||||
|         } else if(usb.serialDriver instanceof FtdiSerialDriver) { |  | ||||||
|             switch(usb.serialDriver.getPorts().size()) { |  | ||||||
|                 case 1: assertEquals("write packet size", 64, writePacketSize);  assertEquals("write buffer size", 128, writeBufferSize); break; |  | ||||||
|                 case 2: assertEquals("write packet size", 512, writePacketSize); assertEquals("write buffer size", 4096, writeBufferSize); break; |  | ||||||
|                 case 4: assertEquals("write packet size", 512, writePacketSize); assertEquals( "write buffer size", 2048, writeBufferSize); break; |  | ||||||
|                 default: fail("unknown port number"); |  | ||||||
|             } |  | ||||||
|         } else if(usb.serialDriver instanceof CdcAcmSerialDriver) { |  | ||||||
|             assertEquals(64, writePacketSize); assertEquals(128, writeBufferSize); // values for ATMega32U4 |  | ||||||
|         } else { |  | ||||||
|             fail("unknown driver " + usb.serialDriver.getClass().getSimpleName()); |  | ||||||
|         } |  | ||||||
|         purgeWriteBuffer(purgeTimeout); |         purgeWriteBuffer(purgeTimeout); | ||||||
|         if(usb.serialDriver instanceof CdcAcmSerialDriver) |         if(usb.serialDriver instanceof CdcAcmSerialDriver) | ||||||
|             return; // serial processing to slow for tests below, but they anyway only check shared code in CommonUsbSerialPort |             return; // serial processing to slow for tests below, but they anyway only check shared code in CommonUsbSerialPort | ||||||
| @ -839,26 +844,6 @@ public class DeviceTest { | |||||||
|         telnet.setParameters(9600, 8, 1, UsbSerialPort.PARITY_NONE); |         telnet.setParameters(9600, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|         TestBuffer tbuf; |         TestBuffer tbuf; | ||||||
| 
 | 
 | ||||||
|         // compare write time |  | ||||||
|         //   full write @ 1-2 msec |  | ||||||
|         //   packet write @ 5-10 msec |  | ||||||
|         if(false) { |  | ||||||
|             tbuf = new TestBuffer(writeBufferSize); |  | ||||||
|             ((CommonUsbSerialPort) usb.serialPort).setWriteBufferSize(tbuf.buf.length); |  | ||||||
|             Log.d(TAG, "full write begin"); |  | ||||||
|             long begin = System.currentTimeMillis(); |  | ||||||
|             usb.serialPort.write(tbuf.buf, 0); |  | ||||||
|             Log.d(TAG, "full write end, duration=" + (System.currentTimeMillis() - begin)); |  | ||||||
|             purgeWriteBuffer(purgeTimeout); |  | ||||||
|             ((CommonUsbSerialPort) usb.serialPort).setWriteBufferSize(writePacketSize); |  | ||||||
|             Log.d(TAG, "packet write begin"); |  | ||||||
|             begin = System.currentTimeMillis(); |  | ||||||
|             usb.serialPort.write(tbuf.buf, 0); |  | ||||||
|             Log.d(TAG, "packet write end, duration=" + (System.currentTimeMillis() - begin)); |  | ||||||
|             purgeWriteBuffer(purgeTimeout); |  | ||||||
|             Assume.assumeTrue(false); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // total write timeout |         // total write timeout | ||||||
|         tbuf = new TestBuffer(writeBufferSize + writePacketSize); |         tbuf = new TestBuffer(writeBufferSize + writePacketSize); | ||||||
|         int timeout = writePacketSize / 32 * 50; // time for 1.5 packets. write 48 byte in 50 msec at 9600 baud |         int timeout = writePacketSize / 32 * 50; // time for 1.5 packets. write 48 byte in 50 msec at 9600 baud | ||||||
| @ -873,17 +858,18 @@ public class DeviceTest { | |||||||
|         purgeWriteBuffer(purgeTimeout); |         purgeWriteBuffer(purgeTimeout); | ||||||
| 
 | 
 | ||||||
|         // infinite wait |         // infinite wait | ||||||
|         //((CommonUsbSerialPort)usb.serialPort).setWriteBufferSize(writePacketSize); |         ((CommonUsbSerialPort)usb.serialPort).setWriteBufferSize(writePacketSize); | ||||||
|         usb.serialPort.write(tbuf.buf, 0); |         usb.serialPort.write(tbuf.buf, 0); | ||||||
|         purgeWriteBuffer(purgeTimeout); |         purgeWriteBuffer(purgeTimeout); | ||||||
| 
 | 
 | ||||||
|         // SerialTimeoutException.bytesTransferred |         // timeout in bulkTransfer + SerialTimeoutException.bytesTransferred | ||||||
|         int readWait = writePacketSize > 64 ? 250 : 50; |         int readWait = writePacketSize > 64 ? 250 : 50; | ||||||
|         ((CommonUsbSerialPort)usb.serialPort).setWriteBufferSize(tbuf.buf.length); |         ((CommonUsbSerialPort)usb.serialPort).setWriteBufferSize(tbuf.buf.length); | ||||||
|         try { |         try { | ||||||
|             usb.serialPort.write(tbuf.buf, timeout); |             usb.serialPort.write(tbuf.buf, timeout); | ||||||
|             fail("write error expected"); |             fail("write error expected"); | ||||||
|         } catch(SerialTimeoutException ex) { |         } catch(SerialTimeoutException ex) { | ||||||
|  |             assertTrue(ex.getMessage(), ex.getMessage().endsWith("rc=-1")); // timeout in bulkTransfer | ||||||
|             for(byte[] data = telnet.read(-1, readWait); data.length != 0; |             for(byte[] data = telnet.read(-1, readWait); data.length != 0; | ||||||
|                        data = telnet.read(-1, readWait)) { |                        data = telnet.read(-1, readWait)) { | ||||||
|                 tbuf.testRead(data); |                 tbuf.testRead(data); | ||||||
| @ -898,6 +884,7 @@ public class DeviceTest { | |||||||
|             usb.serialPort.write(tbuf.buf, timeout); |             usb.serialPort.write(tbuf.buf, timeout); | ||||||
|             fail("write error expected"); |             fail("write error expected"); | ||||||
|         } catch(SerialTimeoutException ex) { |         } catch(SerialTimeoutException ex) { | ||||||
|  |             assertTrue(ex.getMessage(), ex.getMessage().endsWith("rc=-1")); // timeout in bulkTransfer | ||||||
|             for(byte[] data = telnet.read(-1, readWait); data.length != 0; |             for(byte[] data = telnet.read(-1, readWait); data.length != 0; | ||||||
|                        data = telnet.read(-1, readWait)) { |                        data = telnet.read(-1, readWait)) { | ||||||
|                 tbuf.testRead(data); |                 tbuf.testRead(data); | ||||||
| @ -906,6 +893,79 @@ public class DeviceTest { | |||||||
|             assertEquals(writeBufferSize + writePacketSize, tbuf.len); |             assertEquals(writeBufferSize + writePacketSize, tbuf.len); | ||||||
|         } |         } | ||||||
|         purgeWriteBuffer(purgeTimeout); |         purgeWriteBuffer(purgeTimeout); | ||||||
|  | 
 | ||||||
|  |         // timeout in library | ||||||
|  |         timeout = 1; | ||||||
|  |         try { | ||||||
|  |             usb.serialPort.write(tbuf.buf, timeout); | ||||||
|  |             fail("write error expected"); | ||||||
|  |         } catch (SerialTimeoutException ex) { | ||||||
|  |             assertTrue(ex.getMessage(), ex.getMessage().endsWith("rc=-2")); // timeout in library | ||||||
|  |         } | ||||||
|  |         purgeWriteBuffer(purgeTimeout); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     // compare write duration. | ||||||
|  |     // | ||||||
|  |     // multiple packet sized writes typically take 2-3X time of single full buffer write. | ||||||
|  |     // here some typical durations: | ||||||
|  |     //          full    packet [msec] | ||||||
|  |     // Prolific 4       8 | ||||||
|  |     // Cp2102   3       10 | ||||||
|  |     // CP2105   1.x     2-3 | ||||||
|  |     // FT232    1.5-2   2-3 | ||||||
|  |     // Ch34x    1.x     2-3 | ||||||
|  |     // CDC      1.x     2-3 | ||||||
|  |     public void writeDuration() throws Exception { | ||||||
|  |         usb.open(); | ||||||
|  |         usb.setParameters(9600, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |         telnet.setParameters(9600, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  | 
 | ||||||
|  |         boolean purge = true; | ||||||
|  |         try { | ||||||
|  |             usb.serialPort.purgeHwBuffers(true, false); | ||||||
|  |         } catch(Exception ignored) { | ||||||
|  |             purge = false; | ||||||
|  |         } | ||||||
|  |         if(usb.serialDriver instanceof Cp21xxSerialDriver && usb.serialDriver.getPorts().size() == 1) | ||||||
|  |             purge = false; // purge is blocking | ||||||
|  | 
 | ||||||
|  |         int[] writeSizes = getWriteSizes(); | ||||||
|  |         int writePacketSize = writeSizes[0]; | ||||||
|  |         int writeBufferSize = writeSizes[1]; | ||||||
|  |         int purgeTimeout = 250; | ||||||
|  |         TestBuffer tbuf; | ||||||
|  |         long begin; | ||||||
|  |         int duration1, duration2, retries, i, timeout; | ||||||
|  |         retries = purge ? 10 : 1; | ||||||
|  |         tbuf = new TestBuffer(writeBufferSize); | ||||||
|  | 
 | ||||||
|  |         ((CommonUsbSerialPort) usb.serialPort).setWriteBufferSize(tbuf.buf.length); | ||||||
|  |         Log.d(TAG, "writeDuration: full write begin"); | ||||||
|  |         begin = System.currentTimeMillis(); | ||||||
|  |         for(i=0; i<retries; i++) { | ||||||
|  |             usb.serialPort.write(tbuf.buf, 0); | ||||||
|  |             if(purge) | ||||||
|  |                 usb.serialPort.purgeHwBuffers(true, false); | ||||||
|  |         } | ||||||
|  |         duration1 = (int)(System.currentTimeMillis() - begin); | ||||||
|  |         if(!purge) | ||||||
|  |             purgeWriteBuffer(purgeTimeout); | ||||||
|  |         Log.d(TAG, "writeDuration: full write end, duration " + duration1/(float)(retries) + " msec"); | ||||||
|  |         ((CommonUsbSerialPort) usb.serialPort).setWriteBufferSize(writePacketSize); | ||||||
|  |         Log.d(TAG, "writeDuration: packet write begin"); | ||||||
|  |         begin = System.currentTimeMillis(); | ||||||
|  |         for(i=0; i<retries; i++) { | ||||||
|  |             usb.serialPort.write(tbuf.buf, 0); | ||||||
|  |             if(purge) | ||||||
|  |                 usb.serialPort.purgeHwBuffers(true, false); | ||||||
|  |         } | ||||||
|  |         duration2 = (int)(System.currentTimeMillis() - begin); | ||||||
|  |         purgeWriteBuffer(purgeTimeout); | ||||||
|  |         Log.d(TAG, "writeDuration: packet write end, duration " + duration2/(float)(retries) + " msec"); | ||||||
|  |         assertTrue("full duration " + duration1 + ", packet duration " + duration2, duration1 < duration2); | ||||||
|  |         assertTrue("full duration " + duration1 + ", packet duration " + duration2, duration2 < 5*duration1); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Test |     @Test | ||||||
|  | |||||||
| @ -5,10 +5,10 @@ import android.content.BroadcastReceiver; | |||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.content.Intent; | import android.content.Intent; | ||||||
| import android.content.IntentFilter; | import android.content.IntentFilter; | ||||||
| import android.hardware.usb.UsbDevice; |  | ||||||
| import android.hardware.usb.UsbDeviceConnection; | import android.hardware.usb.UsbDeviceConnection; | ||||||
| import android.hardware.usb.UsbManager; | import android.hardware.usb.UsbManager; | ||||||
| import android.os.Process; | import android.media.RingtoneManager; | ||||||
|  | import android.net.Uri; | ||||||
| import android.util.Log; | import android.util.Log; | ||||||
| 
 | 
 | ||||||
| import com.hoho.android.usbserial.driver.CdcAcmSerialDriver; | import com.hoho.android.usbserial.driver.CdcAcmSerialDriver; | ||||||
| @ -24,12 +24,7 @@ import java.util.concurrent.Callable; | |||||||
| import java.util.concurrent.Executors; | import java.util.concurrent.Executors; | ||||||
| 
 | 
 | ||||||
| import static org.junit.Assert.assertEquals; | import static org.junit.Assert.assertEquals; | ||||||
| import static org.junit.Assert.assertNotEquals; |  | ||||||
| import static org.junit.Assert.assertThat; |  | ||||||
| import static org.junit.Assert.assertTrue; | import static org.junit.Assert.assertTrue; | ||||||
| import static org.junit.Assert.fail; |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| public class UsbWrapper implements SerialInputOutputManager.Listener { | public class UsbWrapper implements SerialInputOutputManager.Listener { | ||||||
| 
 | 
 | ||||||
| @ -64,6 +59,9 @@ public class UsbWrapper implements SerialInputOutputManager.Listener { | |||||||
|     public void setUp() throws Exception { |     public void setUp() throws Exception { | ||||||
|         UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); |         UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); | ||||||
|         if (!usbManager.hasPermission(serialDriver.getDevice())) { |         if (!usbManager.hasPermission(serialDriver.getDevice())) { | ||||||
|  |             Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); | ||||||
|  |             RingtoneManager.getRingtone(context, notification).play(); | ||||||
|  | 
 | ||||||
|             Log.d(TAG,"USB permission ..."); |             Log.d(TAG,"USB permission ..."); | ||||||
|             final Boolean[] granted = {null}; |             final Boolean[] granted = {null}; | ||||||
|             BroadcastReceiver usbReceiver = new BroadcastReceiver() { |             BroadcastReceiver usbReceiver = new BroadcastReceiver() { | ||||||
|  | |||||||
| @ -205,15 +205,15 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort { | |||||||
|     @Override |     @Override | ||||||
|     public void write(final byte[] src, final int timeout) throws IOException { |     public void write(final byte[] src, final int timeout) throws IOException { | ||||||
|         int offset = 0; |         int offset = 0; | ||||||
|         int requestTimeout = timeout; |         final long endTime = (timeout == 0) ? 0 : (System.currentTimeMillis() + timeout); | ||||||
| 
 | 
 | ||||||
|         if(mConnection == null) { |         if(mConnection == null) { | ||||||
|             throw new IOException("Connection closed"); |             throw new IOException("Connection closed"); | ||||||
|         } |         } | ||||||
|         while (offset < src.length) { |         while (offset < src.length) { | ||||||
|  |             final int requestTimeout; | ||||||
|             final int requestLength; |             final int requestLength; | ||||||
|             final int actualLength; |             final int actualLength; | ||||||
|             final int requestDuration; |  | ||||||
| 
 | 
 | ||||||
|             synchronized (mWriteBufferLock) { |             synchronized (mWriteBufferLock) { | ||||||
|                 final byte[] writeBuffer; |                 final byte[] writeBuffer; | ||||||
| @ -226,24 +226,21 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort { | |||||||
|                     System.arraycopy(src, offset, mWriteBuffer, 0, requestLength); |                     System.arraycopy(src, offset, mWriteBuffer, 0, requestLength); | ||||||
|                     writeBuffer = mWriteBuffer; |                     writeBuffer = mWriteBuffer; | ||||||
|                 } |                 } | ||||||
|  |                 if (timeout == 0 || offset == 0) { | ||||||
|  |                     requestTimeout = timeout; | ||||||
|  |                 } else { | ||||||
|  |                     requestTimeout = (int)(endTime - System.currentTimeMillis()); | ||||||
|  |                 } | ||||||
|                 if (requestTimeout < 0) { |                 if (requestTimeout < 0) { | ||||||
|                     actualLength = -2; |                     actualLength = -2; | ||||||
|                     requestDuration = 0; |  | ||||||
|                 } else { |                 } else { | ||||||
|                     final long startTime = System.currentTimeMillis(); |  | ||||||
|                     actualLength = mConnection.bulkTransfer(mWriteEndpoint, writeBuffer, requestLength, requestTimeout); |                     actualLength = mConnection.bulkTransfer(mWriteEndpoint, writeBuffer, requestLength, requestTimeout); | ||||||
|                     requestDuration = (int) (System.currentTimeMillis() - startTime); |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             Log.d(TAG, "Wrote " + actualLength + "/" + requestLength + " offset " + offset + "/" + src.length + " timeout " + requestTimeout); |             Log.d(TAG, "Wrote " + actualLength + "/" + requestLength + " offset " + offset + "/" + src.length + " timeout " + requestTimeout); | ||||||
|             if (requestTimeout > 0) { |  | ||||||
|                 requestTimeout -= requestDuration; |  | ||||||
|                 if (requestTimeout == 0) |  | ||||||
|                     requestTimeout = -1; |  | ||||||
|             } |  | ||||||
|             if (actualLength <= 0) { |             if (actualLength <= 0) { | ||||||
|                 if(requestTimeout < 0) { |                 if (timeout != 0 && System.currentTimeMillis() >= endTime) { | ||||||
|                     SerialTimeoutException ex = new SerialTimeoutException("Error writing " + requestLength + " bytes at offset " + offset + " of total " + src.length); |                     SerialTimeoutException ex = new SerialTimeoutException("Error writing " + requestLength + " bytes at offset " + offset + " of total " + src.length + ", rc=" + actualLength); | ||||||
|                     ex.bytesTransferred = offset; |                     ex.bytesTransferred = offset; | ||||||
|                     throw ex; |                     throw ex; | ||||||
|                 } else { |                 } else { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user