mirror of
				https://github.com/mik3y/usb-serial-for-android
				synced 2025-10-31 10:27:27 +00:00 
			
		
		
		
	flowcontrol for ftdi, pl2303, cp210x
This commit is contained in:
		
							parent
							
								
									843792001f
								
							
						
					
					
						commit
						88ca3f57c4
					
				
							
								
								
									
										1
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -6,3 +6,4 @@ androidTestResultsUserPreferences.xml | |||||||
| appInsightsSettings.xml | appInsightsSettings.xml | ||||||
| deploymentTargetDropDown.xml | deploymentTargetDropDown.xml | ||||||
| deploymentTargetSelector.xml | deploymentTargetSelector.xml | ||||||
|  | other.xml | ||||||
|  | |||||||
| @ -115,9 +115,10 @@ For a simple example, see | |||||||
| [UsbSerialExamples](https://github.com/mik3y/usb-serial-for-android/blob/master/usbSerialExamples) | [UsbSerialExamples](https://github.com/mik3y/usb-serial-for-android/blob/master/usbSerialExamples) | ||||||
| folder in this project. | folder in this project. | ||||||
| 
 | 
 | ||||||
| For a more complete example with background service to stay connected while | See separate github project [SimpleUsbTerminal](https://github.com/kai-morich/SimpleUsbTerminal)  | ||||||
| the app is not visible or rotating, see separate github project  | for a more complete example with: | ||||||
| [SimpleUsbTerminal](https://github.com/kai-morich/SimpleUsbTerminal). | * Background service to stay connected while the app is not visible or rotating | ||||||
|  | * Flow control  | ||||||
| 
 | 
 | ||||||
| ## Probing for Unrecognized Devices | ## Probing for Unrecognized Devices | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -50,7 +50,7 @@ project.afterEvaluate { | |||||||
|                 // values used for local maven repo, jitpack uses github release: |                 // values used for local maven repo, jitpack uses github release: | ||||||
|                 groupId 'com.github.mik3y' |                 groupId 'com.github.mik3y' | ||||||
|                 artifactId 'usb-serial-for-android' |                 artifactId 'usb-serial-for-android' | ||||||
|                 version '3.7.3beta' |                 version '3.8.0beta' | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -18,7 +18,7 @@ android { | |||||||
|         } |         } | ||||||
|         cp2102 { // and cp2105 first port |         cp2102 { // and cp2105 first port | ||||||
|             dimension 'device' |             dimension 'device' | ||||||
|             testInstrumentationRunnerArguments = ['test_device_driver': 'Cp21xx'] |             testInstrumentationRunnerArguments = ['test_device_driver': 'Cp21xx', 'test_device_port': '0'] | ||||||
|         } |         } | ||||||
|         cp2105 { // second port |         cp2105 { // second port | ||||||
|             dimension 'device' |             dimension 'device' | ||||||
| @ -26,7 +26,7 @@ android { | |||||||
|         } |         } | ||||||
|         ft232 { // and ft2232 first port |         ft232 { // and ft2232 first port | ||||||
|             dimension 'device' |             dimension 'device' | ||||||
|             testInstrumentationRunnerArguments = ['test_device_driver': 'Ftdi'] |             testInstrumentationRunnerArguments = ['test_device_driver': 'Ftdi', 'test_device_port': '0'] | ||||||
|         } |         } | ||||||
|         ft2232 { // second port |         ft2232 { // second port | ||||||
|             dimension 'device' |             dimension 'device' | ||||||
|  | |||||||
| @ -40,6 +40,8 @@ import com.hoho.android.usbserial.util.TelnetWrapper; | |||||||
| import com.hoho.android.usbserial.util.TestBuffer; | import com.hoho.android.usbserial.util.TestBuffer; | ||||||
| import com.hoho.android.usbserial.util.UsbWrapper; | import com.hoho.android.usbserial.util.UsbWrapper; | ||||||
| import com.hoho.android.usbserial.driver.UsbSerialPort.ControlLine; | import com.hoho.android.usbserial.driver.UsbSerialPort.ControlLine; | ||||||
|  | import com.hoho.android.usbserial.driver.UsbSerialPort.FlowControl; | ||||||
|  | import com.hoho.android.usbserial.util.XonXoffFilter; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| import org.junit.After; | import org.junit.After; | ||||||
| @ -80,7 +82,10 @@ import static org.junit.Assert.fail; | |||||||
| public class DeviceTest { | public class DeviceTest { | ||||||
|     private final static String  TAG = DeviceTest.class.getSimpleName(); |     private final static String  TAG = DeviceTest.class.getSimpleName(); | ||||||
| 
 | 
 | ||||||
|  |     enum FlowControl_OutputLineLocked { FALSE, ON_BUFFER_FULL, TRUE } | ||||||
|  | 
 | ||||||
|     // testInstrumentationRunnerArguments configuration |     // testInstrumentationRunnerArguments configuration | ||||||
|  | 
 | ||||||
|     private static String  rfc2217_server_host; |     private static String  rfc2217_server_host; | ||||||
|     private static int     rfc2217_server_port = 2217; |     private static int     rfc2217_server_port = 2217; | ||||||
|     private static boolean rfc2217_server_nonstandard_baudrates; |     private static boolean rfc2217_server_nonstandard_baudrates; | ||||||
| @ -104,7 +109,7 @@ public class DeviceTest { | |||||||
|         rfc2217_server_host                  =                 InstrumentationRegistry.getArguments().getString("rfc2217_server_host"); |         rfc2217_server_host                  =                 InstrumentationRegistry.getArguments().getString("rfc2217_server_host"); | ||||||
|         rfc2217_server_nonstandard_baudrates = Boolean.valueOf(InstrumentationRegistry.getArguments().getString("rfc2217_server_nonstandard_baudrates")); |         rfc2217_server_nonstandard_baudrates = Boolean.valueOf(InstrumentationRegistry.getArguments().getString("rfc2217_server_nonstandard_baudrates")); | ||||||
|         test_device_driver                   =                 InstrumentationRegistry.getArguments().getString("test_device_driver"); |         test_device_driver                   =                 InstrumentationRegistry.getArguments().getString("test_device_driver"); | ||||||
|         test_device_port                     = Integer.valueOf(InstrumentationRegistry.getArguments().getString("test_device_port","0")); |         test_device_port                     = Integer.valueOf(InstrumentationRegistry.getArguments().getString("test_device_port","-1")); | ||||||
| 
 | 
 | ||||||
|         // postpone parts of fixture setup to first test, because exceptions are not reported for @BeforeClass |         // postpone parts of fixture setup to first test, because exceptions are not reported for @BeforeClass | ||||||
|         // and test terminates with misleading 'Empty test suite' |         // and test terminates with misleading 'Empty test suite' | ||||||
| @ -129,12 +134,16 @@ public class DeviceTest { | |||||||
|             String driverName = usbSerialDriver.getClass().getSimpleName(); |             String driverName = usbSerialDriver.getClass().getSimpleName(); | ||||||
|             assertEquals(test_device_driver+"SerialDriver", driverName); |             assertEquals(test_device_driver+"SerialDriver", driverName); | ||||||
|         } |         } | ||||||
|  |         if (test_device_port == -1) { | ||||||
|  |             test_device_port = usbSerialDriver.getPorts().size() - 1; | ||||||
|  |         } else { | ||||||
|             assertTrue( usbSerialDriver.getPorts().size() > test_device_port); |             assertTrue( usbSerialDriver.getPorts().size() > test_device_port); | ||||||
|  |         } | ||||||
|         usb = new UsbWrapper(context, usbSerialDriver, test_device_port); |         usb = new UsbWrapper(context, usbSerialDriver, test_device_port); | ||||||
|         usb.setUp(); |         usb.setUp(); | ||||||
| 
 | 
 | ||||||
|         Log.i(TAG, "Using USB device "+ usb.serialPort.toString()+" driver="+usb.serialDriver.getClass().getSimpleName()); |         Log.i(TAG, "Using USB device "+ usb.serialPort.toString()+" driver="+usb.serialDriver.getClass().getSimpleName()); | ||||||
|         telnet.read(-1); // doesn't look related here, but very often after usb permission dialog the first test failed with telnet garbage |         telnet.read(-1); // doesn't look necessary here, but very often after usb permission dialog the first test failed with telnet garbage | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @After |     @After | ||||||
| @ -212,7 +221,8 @@ public class DeviceTest { | |||||||
|         telnet.write(buf1); |         telnet.write(buf1); | ||||||
|         data = usb.read(buf1.length, -1, readWait); |         data = usb.read(buf1.length, -1, readWait); | ||||||
|         assertThat(reason, data, equalTo(buf1)); // includes array content in output |         assertThat(reason, data, equalTo(buf1)); // includes array content in output | ||||||
|         //assertArrayEquals("net2usb".getBytes(), data); // only includes array length in output |         if(usb.isCp21xxRestrictedPort && usb.serialPort.getFlowControl() == FlowControl.XON_XOFF) | ||||||
|  |             data = telnet.read(); // discard flow control | ||||||
|         usb.write(buf2); |         usb.write(buf2); | ||||||
|         data = telnet.read(buf2.length, readWait); |         data = telnet.read(buf2.length, readWait); | ||||||
|         assertThat(reason, data, equalTo(buf2)); |         assertThat(reason, data, equalTo(buf2)); | ||||||
| @ -868,7 +878,7 @@ public class DeviceTest { | |||||||
|                      usb.serialPort.getReadEndpoint().getMaxPacketSize()); |                      usb.serialPort.getReadEndpoint().getMaxPacketSize()); | ||||||
| 
 | 
 | ||||||
|         int baudRate = 300; |         int baudRate = 300; | ||||||
|         if(usb.serialDriver instanceof Cp21xxSerialDriver && usb.serialDriver.getPorts().size() > 1) |         if(usb.serialDriver instanceof Cp21xxSerialDriver && usb.serialPort.getPortNumber() > 0) | ||||||
|             baudRate = 2400; |             baudRate = 2400; | ||||||
|         usb.setParameters(baudRate, 8, 1, UsbSerialPort.PARITY_NONE); |         usb.setParameters(baudRate, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|         telnet.setParameters(baudRate, 8, 1, UsbSerialPort.PARITY_NONE); |         telnet.setParameters(baudRate, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
| @ -1683,7 +1693,7 @@ public class DeviceTest { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         // FTDI only recovers from Cp21xx control commands with power toggle, so skip this combination! |         // FTDI only recovers from Cp21xx control commands with power toggle, so skip this combination! | ||||||
|         if(!(usb.serialDriver instanceof Cp21xxSerialDriver | usb.serialDriver instanceof FtdiSerialDriver)) { |         if(!(usb.serialDriver instanceof Cp21xxSerialDriver || usb.serialDriver instanceof FtdiSerialDriver)) { | ||||||
|             UsbDeviceConnection wrongDeviceConnection = usbManager.openDevice(usb.serialDriver.getDevice()); |             UsbDeviceConnection wrongDeviceConnection = usbManager.openDevice(usb.serialDriver.getDevice()); | ||||||
|             UsbSerialDriver wrongSerialDriver = new Cp21xxSerialDriver(usb.serialDriver.getDevice()); |             UsbSerialDriver wrongSerialDriver = new Cp21xxSerialDriver(usb.serialDriver.getDevice()); | ||||||
|             UsbSerialPort wrongSerialPort = wrongSerialDriver.getPorts().get(0); |             UsbSerialPort wrongSerialPort = wrongSerialDriver.getPorts().get(0); | ||||||
| @ -1701,7 +1711,8 @@ public class DeviceTest { | |||||||
|             } catch (IOException ignored) { |             } catch (IOException ignored) { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         if(!(usb.serialDriver instanceof FtdiSerialDriver)) { |         // CP2105 does not recover from FTDI commands | ||||||
|  |         if(!((usb.serialDriver instanceof Cp21xxSerialDriver && usb.serialDriver.getPorts().size() == 2) || usb.serialDriver instanceof FtdiSerialDriver)) { | ||||||
|             UsbDeviceConnection wrongDeviceConnection = usbManager.openDevice(usb.serialDriver.getDevice()); |             UsbDeviceConnection wrongDeviceConnection = usbManager.openDevice(usb.serialDriver.getDevice()); | ||||||
|             UsbSerialDriver wrongSerialDriver = new FtdiSerialDriver(usb.serialDriver.getDevice()); |             UsbSerialDriver wrongSerialDriver = new FtdiSerialDriver(usb.serialDriver.getDevice()); | ||||||
|             UsbSerialPort wrongSerialPort = wrongSerialDriver.getPorts().get(0); |             UsbSerialPort wrongSerialPort = wrongSerialDriver.getPorts().get(0); | ||||||
| @ -1745,7 +1756,6 @@ public class DeviceTest { | |||||||
|             assertEquals(usb.serialDriver.getDevice(), wrongSerialDriver.getDevice()); |             assertEquals(usb.serialDriver.getDevice(), wrongSerialDriver.getDevice()); | ||||||
|             assertEquals(wrongSerialDriver, wrongSerialPort.getDriver()); |             assertEquals(wrongSerialDriver, wrongSerialPort.getDriver()); | ||||||
|             assertThrows(UnsupportedOperationException.class, () -> wrongSerialPort.setParameters(9200, 8, 1, 0)); |             assertThrows(UnsupportedOperationException.class, () -> wrongSerialPort.setParameters(9200, 8, 1, 0)); | ||||||
|             assertEquals(EnumSet.noneOf(ControlLine.class), wrongSerialPort.getSupportedControlLines()); |  | ||||||
|             try { |             try { | ||||||
|                 wrongSerialPort.close(); |                 wrongSerialPort.close(); | ||||||
|             } catch (IOException ignored) { |             } catch (IOException ignored) { | ||||||
| @ -1762,7 +1772,6 @@ public class DeviceTest { | |||||||
|             assertEquals(usb.serialDriver.getDevice(), wrongSerialDriver.getDevice()); |             assertEquals(usb.serialDriver.getDevice(), wrongSerialDriver.getDevice()); | ||||||
|             assertEquals(wrongSerialDriver, wrongSerialPort.getDriver()); |             assertEquals(wrongSerialDriver, wrongSerialPort.getDriver()); | ||||||
|             assertThrows(UnsupportedOperationException.class, () -> wrongSerialPort.setParameters(9200, 8, 1, 0)); |             assertThrows(UnsupportedOperationException.class, () -> wrongSerialPort.setParameters(9200, 8, 1, 0)); | ||||||
|             assertEquals(EnumSet.noneOf(ControlLine.class), wrongSerialPort.getSupportedControlLines()); |  | ||||||
|             try { |             try { | ||||||
|                 wrongSerialPort.close(); |                 wrongSerialPort.close(); | ||||||
|             } catch (IOException ignored) { |             } catch (IOException ignored) { | ||||||
| @ -1988,6 +1997,360 @@ public class DeviceTest { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @Test | ||||||
|  |     public void flowControlXonXoff() throws Exception { | ||||||
|  |         final byte[] off_on = new byte[]{'x',CommonUsbSerialPort.CHAR_XOFF,'y',CommonUsbSerialPort.CHAR_XON,'z'}; | ||||||
|  |         final byte[] off_on_filtered = "xyz".getBytes(); | ||||||
|  |         final XonXoffFilter filter; | ||||||
|  | 
 | ||||||
|  |         usb.open(EnumSet.of(UsbWrapper.OpenCloseFlags.NO_IOMANAGER_THREAD, UsbWrapper.OpenCloseFlags.NO_CONTROL_LINE_INIT)); | ||||||
|  |         telnet.setParameters(115200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |         usb.setParameters(115200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |         assertEquals(FlowControl.NONE, usb.serialPort.getFlowControl()); | ||||||
|  |         if (!usb.serialPort.getSupportedFlowControl().contains(FlowControl.XON_XOFF_INLINE) && | ||||||
|  |                 !usb.serialPort.getSupportedFlowControl().contains(FlowControl.XON_XOFF)) { | ||||||
|  |             assertThrows(UnsupportedOperationException.class, () -> usb.serialPort.setFlowControl(FlowControl.XON_XOFF_INLINE)); | ||||||
|  |             assertThrows(UnsupportedOperationException.class, () -> usb.serialPort.setFlowControl(FlowControl.XON_XOFF)); | ||||||
|  |             assertThrows(UnsupportedOperationException.class, () -> usb.serialPort.getXON()); | ||||||
|  |             Assume.assumeTrue("flow control not supported", false); | ||||||
|  |         } | ||||||
|  |         if (usb.serialPort.getSupportedFlowControl().contains(FlowControl.XON_XOFF_INLINE) && | ||||||
|  |                 usb.serialPort.getSupportedFlowControl().contains(FlowControl.XON_XOFF)) { | ||||||
|  |             fail("only one of both XON_XOFF variants allowed"); | ||||||
|  |         } | ||||||
|  |         if (usb.serialPort.getSupportedFlowControl().contains(FlowControl.XON_XOFF_INLINE)) { | ||||||
|  |             filter = new XonXoffFilter(); | ||||||
|  |             assertThrows(UnsupportedOperationException.class, () -> usb.serialPort.setFlowControl(FlowControl.XON_XOFF)); | ||||||
|  |             usb.serialPort.setFlowControl(FlowControl.XON_XOFF_INLINE); | ||||||
|  |             assertEquals(FlowControl.XON_XOFF_INLINE, usb.serialPort.getFlowControl()); | ||||||
|  |             assertThrows(UnsupportedOperationException.class, () -> usb.serialPort.getXON()); | ||||||
|  | 
 | ||||||
|  |             assertTrue(filter.getXON()); | ||||||
|  |             assertThat(filter.filter(off_on), equalTo(off_on_filtered)); | ||||||
|  |             assertTrue(filter.getXON()); | ||||||
|  |             assertThat(filter.filter(new byte[]{CommonUsbSerialPort.CHAR_XOFF}), equalTo(new byte[]{})); | ||||||
|  |             assertFalse(filter.getXON()); | ||||||
|  |             assertThat(filter.filter(new byte[]{CommonUsbSerialPort.CHAR_XON}), equalTo(new byte[]{})); | ||||||
|  |             assertTrue(filter.getXON()); | ||||||
|  |         } else { | ||||||
|  |             filter = null; | ||||||
|  |             assertThrows(UnsupportedOperationException.class, () -> usb.serialPort.setFlowControl(FlowControl.XON_XOFF_INLINE)); | ||||||
|  |             usb.serialPort.setFlowControl(FlowControl.XON_XOFF); | ||||||
|  |             assertEquals(FlowControl.XON_XOFF, usb.serialPort.getFlowControl()); | ||||||
|  |             assertTrue(usb.serialPort.getXON()); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         class TelnetXonXoff { | ||||||
|  |             void write(boolean on) throws Exception { | ||||||
|  |                 byte[] data; | ||||||
|  |                 int i; | ||||||
|  |                 if (on) telnet.write(new byte[]{CommonUsbSerialPort.CHAR_XON}); | ||||||
|  |                 else    telnet.write(new byte[]{CommonUsbSerialPort.CHAR_XOFF}); | ||||||
|  |                 if (filter != null) { | ||||||
|  |                     data = usb.read(1); | ||||||
|  |                     assertEquals(1, data.length); | ||||||
|  |                     data = filter.filter(data); | ||||||
|  |                     assertEquals(on, filter.getXON()); | ||||||
|  |                     assertEquals(0, data.length); | ||||||
|  |                 } else { | ||||||
|  |                     for(i = 0; i < 20; i++) { | ||||||
|  |                         if (!usb.serialPort.getXON()) break; | ||||||
|  |                         Thread.sleep(10); | ||||||
|  |                     } | ||||||
|  |                     data = usb.read(-1, 0, 10); | ||||||
|  |                     assertEquals(0, data.length); | ||||||
|  |                     assertEquals(on, usb.serialPort.getXON()); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |         TelnetXonXoff telnetXonXoff = new TelnetXonXoff(); | ||||||
|  | 
 | ||||||
|  |         byte[] data; | ||||||
|  |         int i; | ||||||
|  |         int bufferSize; | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |             // fast off + on | ||||||
|  |             telnet.write(off_on); | ||||||
|  |             data = usb.read(); | ||||||
|  |             if (filter != null) { | ||||||
|  |                 assertThat(data, equalTo(off_on)); | ||||||
|  |                 assertTrue(filter.getXON()); | ||||||
|  |             } else { | ||||||
|  |                 assertThat(data, equalTo(off_on_filtered)); | ||||||
|  |                 assertTrue(usb.serialPort.getXON()); | ||||||
|  |             } | ||||||
|  |             doReadWrite(""); | ||||||
|  | 
 | ||||||
|  |             // USB write disabled -> send buffer full -> USB write -> SerialTimeoutException | ||||||
|  |             telnetXonXoff.write(false); | ||||||
|  |             bufferSize = usb.writeBufferSize; | ||||||
|  |             if (usb.serialDriver instanceof Cp21xxSerialDriver && usb.serialDriver.getPorts().size() > 1 && usb.serialPort.getPortNumber() == 0) | ||||||
|  |                 bufferSize -= 64; | ||||||
|  |             byte[] wbuf = new byte[bufferSize]; | ||||||
|  |             usb.write(wbuf); | ||||||
|  |             data = telnet.read(-1, 100); | ||||||
|  |             assertEquals(0, data.length); | ||||||
|  |             try { | ||||||
|  |                 usb.write(new byte[1]); | ||||||
|  |                 fail("write error expected when buffer full"); | ||||||
|  |             } catch (SerialTimeoutException ignored) { | ||||||
|  |             } | ||||||
|  |             telnetXonXoff.write(true); | ||||||
|  |             usb.write(new byte[]{1}); | ||||||
|  |             data = telnet.read(wbuf.length + 1); | ||||||
|  |             assertEquals(wbuf.length + 1, data.length); | ||||||
|  |             doReadWrite(""); | ||||||
|  | 
 | ||||||
|  |             // no USB read -> receive buffer full -> XOFF -> USB read -> XON | ||||||
|  |             bufferSize = usb.readBufferSize; | ||||||
|  |             telnet.write(new byte[bufferSize]); | ||||||
|  |             data = telnet.read(1); | ||||||
|  |             assertThat(data, equalTo(new byte[]{UsbSerialPort.CHAR_XOFF})); | ||||||
|  |             data = usb.read(bufferSize, 2*bufferSize); | ||||||
|  |             assertEquals(bufferSize, data.length); | ||||||
|  |             data = telnet.read(1); | ||||||
|  |             if(usb.isCp21xxRestrictedPort && data.length > 1) | ||||||
|  |                 data = new byte[]{data[data.length-1]}; | ||||||
|  |             assertThat(data, equalTo(new byte[]{UsbSerialPort.CHAR_XON})); | ||||||
|  |             doReadWrite(""); | ||||||
|  | 
 | ||||||
|  |             // retaining XOFF state over mode change is device specific | ||||||
|  |             telnetXonXoff.write(false); | ||||||
|  |             usb.serialPort.setFlowControl(FlowControl.NONE); | ||||||
|  |             usb.serialPort.setFlowControl(filter != null ? FlowControl.XON_XOFF_INLINE : FlowControl.XON_XOFF); | ||||||
|  |             if (usb.serialDriver instanceof ProlificSerialDriver) { // only PL3032 retains XOFF state | ||||||
|  |                 usb.write(new byte[1]); | ||||||
|  |                 data = telnet.read(1, 100); | ||||||
|  |                 assertEquals(0, data.length); | ||||||
|  |                 telnetXonXoff.write(true); | ||||||
|  |                 data = telnet.read(1, 100); | ||||||
|  |                 assertEquals(1, data.length); | ||||||
|  |             } | ||||||
|  |             doReadWrite(""); | ||||||
|  | 
 | ||||||
|  |             // mode retained over close, retaining XOFF state is device specific | ||||||
|  |             telnetXonXoff.write(false); | ||||||
|  |             usb.close(EnumSet.of(UsbWrapper.OpenCloseFlags.NO_IOMANAGER_THREAD, UsbWrapper.OpenCloseFlags.NO_CONTROL_LINE_INIT)); | ||||||
|  |             usb.open(EnumSet.of(UsbWrapper.OpenCloseFlags.NO_IOMANAGER_THREAD, UsbWrapper.OpenCloseFlags.NO_CONTROL_LINE_INIT)); | ||||||
|  |             usb.setParameters(115200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |             if (filter != null) { | ||||||
|  |                 assertEquals(FlowControl.XON_XOFF_INLINE, usb.serialPort.getFlowControl()); | ||||||
|  |             } else { | ||||||
|  |                 assertEquals(FlowControl.XON_XOFF, usb.serialPort.getFlowControl()); | ||||||
|  |                 for (i = 0; i < 20; i++) { | ||||||
|  |                     if (usb.serialPort.getXON()) break; | ||||||
|  |                     Thread.sleep(10); | ||||||
|  |                 } | ||||||
|  |                 assertTrue(usb.serialPort.getXON()); | ||||||
|  |             } | ||||||
|  |             if (usb.serialDriver instanceof ProlificSerialDriver) { // only PL3032 retains XOFF state | ||||||
|  |                 usb.write(new byte[1]); | ||||||
|  |                 data = telnet.read(1, 100); | ||||||
|  |                 assertEquals(0, data.length); | ||||||
|  |                 telnetXonXoff.write(true); | ||||||
|  |                 data = telnet.read(1, 100); | ||||||
|  |                 assertEquals(1, data.length); | ||||||
|  |             } | ||||||
|  |             doReadWrite(""); | ||||||
|  | 
 | ||||||
|  |         } finally { | ||||||
|  |             telnet.write(new byte[]{CommonUsbSerialPort.CHAR_XON}); | ||||||
|  |             if (filter != null) { | ||||||
|  |                 usb.read(1); | ||||||
|  |             } | ||||||
|  |             usb.write(new byte[]{CommonUsbSerialPort.CHAR_XON}); | ||||||
|  |             telnet.read(1); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void flowControlRtsCts() throws Exception { | ||||||
|  |         flowControlHw(FlowControl.RTS_CTS); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void flowControlDtrDsr() throws Exception { | ||||||
|  |         flowControlHw(FlowControl.DTR_DSR); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void flowControlHw(FlowControl flowControl) throws Exception { | ||||||
|  |         byte[] buf = new byte[]{0x30, 0x31, 0x32}; | ||||||
|  |         byte[] buf64 = new byte[64]; | ||||||
|  |         byte[] data; | ||||||
|  |         int i; | ||||||
|  | 
 | ||||||
|  |         int controlLineWait = 3; // msec | ||||||
|  |         boolean outputLineReadable = false; // getControlLines returns configured value | ||||||
|  |         FlowControl_OutputLineLocked outputLineLocked = FlowControl_OutputLineLocked.FALSE; | ||||||
|  |         boolean outputLineSet = false; | ||||||
|  |         if(usb.serialDriver instanceof ProlificSerialDriver) { | ||||||
|  |             outputLineSet = true; // line set to 'true' on setFlowControl | ||||||
|  |             outputLineLocked = FlowControl_OutputLineLocked.TRUE;  // setRts/Dtr has no effect | ||||||
|  |         } | ||||||
|  |         if(usb.serialDriver instanceof Cp21xxSerialDriver) { | ||||||
|  |             outputLineSet = true; | ||||||
|  |             outputLineLocked = FlowControl_OutputLineLocked.ON_BUFFER_FULL; | ||||||
|  |             outputLineReadable = true; // getControlLines returns actual value | ||||||
|  |         } | ||||||
|  |         if(usb.serialDriver instanceof FtdiSerialDriver) { | ||||||
|  |             outputLineLocked = FlowControl_OutputLineLocked.ON_BUFFER_FULL; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         usb.open(EnumSet.of(UsbWrapper.OpenCloseFlags.NO_CONTROL_LINE_INIT, UsbWrapper.OpenCloseFlags.NO_IOMANAGER_THREAD)); | ||||||
|  |         telnet.setParameters(115200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |         usb.setParameters(115200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  | 
 | ||||||
|  |         // early exit, if flow control not supported | ||||||
|  |         assertEquals(usb.inputLinesConnected ? EnumSet.of(ControlLine.RI) : EnumSet.noneOf(ControlLine.class), usb.serialPort.getControlLines()); // [1] | ||||||
|  |         assertEquals(FlowControl.NONE, usb.serialPort.getFlowControl()); | ||||||
|  |         try { | ||||||
|  |             usb.serialPort.setFlowControl(flowControl); | ||||||
|  |         } catch (UnsupportedOperationException ignored) { | ||||||
|  |             if (usb.serialPort.getSupportedFlowControl().contains(flowControl)) { | ||||||
|  |                 assertTrue("flow control support expected", false); | ||||||
|  |             } else { | ||||||
|  |                 Assume.assumeTrue("flow control not supported", false); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         assertEquals(flowControl, usb.serialPort.getFlowControl()); | ||||||
|  |         if (!usb.inputLinesConnected) | ||||||
|  |             Assume.assumeTrue("flow control lines not connected", false); | ||||||
|  | 
 | ||||||
|  |         // test output line state by reading corresponding input line | ||||||
|  |         boolean m = flowControl == FlowControl.RTS_CTS; | ||||||
|  |         Thread.sleep(controlLineWait); // required by pl2303 | ||||||
|  |         if(outputLineSet) { // was not set before enabling flow control at [1] | ||||||
|  |             assertTrue(usb.serialPort.getControlLines().contains(m ? ControlLine.CTS : ControlLine.DSR));       // actual value | ||||||
|  |             assertFalse(m ? usb.serialPort.getRTS() : usb.serialPort.getDTR());                                 // configured value | ||||||
|  |             if(outputLineReadable) { | ||||||
|  |                 assertTrue(usb.serialPort.getControlLines().contains(m ? ControlLine.RTS : ControlLine.DTR));   // actual value | ||||||
|  |             } else { | ||||||
|  |                 assertFalse(usb.serialPort.getControlLines().contains(m ? ControlLine.RTS : ControlLine.DTR));  // configured value | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             assertTrue(usb.serialPort.getControlLines().contains(ControlLine.RI)); | ||||||
|  |         } | ||||||
|  |         if(m) usb.serialPort.setRTS(true); else usb.serialPort.setDTR(true); | ||||||
|  |         assertTrue(usb.serialPort.getControlLines().contains(m ? ControlLine.CTS : ControlLine.DSR)); | ||||||
|  |         if(m) usb.serialPort.setRTS(false); else usb.serialPort.setDTR(false); | ||||||
|  |         if(outputLineLocked == FlowControl_OutputLineLocked.TRUE) { | ||||||
|  |             assertTrue(usb.serialPort.getControlLines().contains(m ? ControlLine.CTS : ControlLine.DSR)); | ||||||
|  |         } else { | ||||||
|  |             assertFalse(usb.serialPort.getControlLines().contains(m ? ControlLine.CTS : ControlLine.DSR)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // read & write | ||||||
|  |         usb.serialPort.setFlowControl(m ? FlowControl.RTS_CTS : FlowControl.DTR_DSR); | ||||||
|  |         if(!outputLineSet) { | ||||||
|  |             if (m) usb.serialPort.setRTS(true); else usb.serialPort.setDTR(true); | ||||||
|  |         } | ||||||
|  |         telnet.write(buf); | ||||||
|  |         data = usb.read(buf.length, -1, 100); | ||||||
|  |         assertThat(data, equalTo(buf)); | ||||||
|  |         usb.write(buf); | ||||||
|  |         data = telnet.read(buf.length, 100); | ||||||
|  |         assertThat(data, equalTo(buf)); | ||||||
|  | 
 | ||||||
|  |         // write disabled + continued | ||||||
|  |         if(m) usb.serialPort.setDTR(true); else usb.serialPort.setRTS(true); | ||||||
|  |         Thread.sleep(controlLineWait); | ||||||
|  |         assertFalse(usb.serialPort.getControlLines().contains(m ? ControlLine.CTS : ControlLine.DSR)); | ||||||
|  |         telnet.write(buf); | ||||||
|  |         data = usb.read(buf.length, -1, 100); | ||||||
|  |         assertThat(data, equalTo(buf)); | ||||||
|  |         usb.write(buf); | ||||||
|  |         data = telnet.read(buf.length, 200); // stopped | ||||||
|  |         assertThat(data, equalTo(new byte[0])); | ||||||
|  | 
 | ||||||
|  |         if(m) usb.serialPort.setDTR(false); else usb.serialPort.setRTS(false); | ||||||
|  |         Thread.sleep(controlLineWait); | ||||||
|  |         assertTrue(usb.serialPort.getControlLines().contains(m ? ControlLine.CTS : ControlLine.DSR)); | ||||||
|  |         data = telnet.read(buf.length, 100); // continued | ||||||
|  |         assertThat(data, equalTo(buf)); | ||||||
|  | 
 | ||||||
|  |         // write disabled -> buffer full -> SerialTimeoutException | ||||||
|  |         if(m) usb.serialPort.setDTR(true); else usb.serialPort.setRTS(true); | ||||||
|  |         Thread.sleep(controlLineWait); | ||||||
|  |         assertFalse(usb.serialPort.getControlLines().contains(m ? ControlLine.CTS : ControlLine.DSR)); | ||||||
|  |         usb.write(buf64); | ||||||
|  |         data = telnet.read(buf64.length, 200); | ||||||
|  |         assertThat(data, equalTo(new byte[0])); | ||||||
|  |         try { | ||||||
|  |             for (i = 0; i < 80; i++) { | ||||||
|  |                 usb.write(buf64); | ||||||
|  |             } | ||||||
|  |             fail("write error expected when buffer full"); | ||||||
|  |         } catch(SerialTimeoutException ignored) { | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         long t1 = System.currentTimeMillis(); | ||||||
|  |         try { | ||||||
|  |             usb.serialPort.write(buf64, 200); | ||||||
|  |             fail("write error expected when buffer full"); | ||||||
|  |         } catch(SerialTimeoutException ignored) { | ||||||
|  |             long t2 = System.currentTimeMillis(); | ||||||
|  |             assertTrue("expected IOException after 200msec timeout, got "+(t2-t1)+"msec", t2-t1 >= 200); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // continue write | ||||||
|  |         if(m) usb.serialPort.setDTR(false); else usb.serialPort.setRTS(false); | ||||||
|  |         Thread.sleep(controlLineWait); | ||||||
|  | 
 | ||||||
|  |         assertTrue(usb.serialPort.getControlLines().contains(m ? ControlLine.CTS : ControlLine.DSR)); | ||||||
|  |         data = telnet.read(buf64.length, 200); | ||||||
|  |         Thread.sleep(100); // 64 bytes are free again after 4.4ms | ||||||
|  |         usb.write(buf64); | ||||||
|  | 
 | ||||||
|  |         // no read -> buffer full -> RTS/DTR off | ||||||
|  |         class NoRead { | ||||||
|  |             void run() throws Exception { | ||||||
|  |                 assertTrue(usb.serialPort.getControlLines().contains(m ? ControlLine.CTS : ControlLine.DSR)); | ||||||
|  |                 int i; | ||||||
|  |                 for (i = 0; i < 120 && usb.serialPort.getControlLines().contains(m ? ControlLine.CTS : ControlLine.DSR); i++) { | ||||||
|  |                     telnet.write(buf64); | ||||||
|  |                     Thread.sleep(controlLineWait); | ||||||
|  |                 } | ||||||
|  |                 assertFalse(usb.serialPort.getControlLines().contains(m ? ControlLine.CTS : ControlLine.DSR)); | ||||||
|  | 
 | ||||||
|  |                 byte[] data = usb.read(-1, i*64, 100); | ||||||
|  |                 Thread.sleep(controlLineWait); | ||||||
|  |                 assertTrue(usb.serialPort.getControlLines().contains(m ? ControlLine.CTS : ControlLine.DSR)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         new NoRead().run(); | ||||||
|  | 
 | ||||||
|  |         // no read -> buffer full -> RTS/DTR off -> output line locked | ||||||
|  |         if(outputLineLocked != FlowControl_OutputLineLocked.TRUE) { | ||||||
|  |             assertTrue(usb.serialPort.getControlLines().contains(m ? ControlLine.CTS : ControlLine.DSR)); | ||||||
|  |             for(i = 0; i < 120 && usb.serialPort.getControlLines().contains(m ? ControlLine.CTS : ControlLine.DSR); i++) { | ||||||
|  |                 telnet.write(buf64); | ||||||
|  |                 Thread.sleep(controlLineWait); | ||||||
|  |             } | ||||||
|  |             assertFalse(usb.serialPort.getControlLines().contains(m ? ControlLine.CTS : ControlLine.DSR)); | ||||||
|  |             if(m) usb.serialPort.setRTS(true); else usb.serialPort.setDTR(true); | ||||||
|  |             Thread.sleep(controlLineWait); | ||||||
|  |             if(outputLineLocked == FlowControl_OutputLineLocked.ON_BUFFER_FULL) | ||||||
|  |                 assertFalse(usb.serialPort.getControlLines().contains(m ? ControlLine.CTS : ControlLine.DSR)); | ||||||
|  |             else | ||||||
|  |                 assertTrue(usb.serialPort.getControlLines().contains(m ? ControlLine.CTS : ControlLine.DSR)); | ||||||
|  |             data = usb.read(-1, i*64, 100); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // mode retained over close | ||||||
|  |         assertEquals(flowControl, usb.serialPort.getFlowControl()); | ||||||
|  |         if(m) usb.serialPort.setRTS(true); else usb.serialPort.setDTR(true); | ||||||
|  |         assertTrue(usb.serialPort.getControlLines().contains(m ? ControlLine.CTS : ControlLine.DSR)); | ||||||
|  |         usb.close(EnumSet.of(UsbWrapper.OpenCloseFlags.NO_CONTROL_LINE_INIT)); | ||||||
|  |         usb.open(EnumSet.of(UsbWrapper.OpenCloseFlags.NO_CONTROL_LINE_INIT, UsbWrapper.OpenCloseFlags.NO_IOMANAGER_THREAD)); | ||||||
|  |         assertEquals(flowControl, usb.serialPort.getFlowControl()); | ||||||
|  |         assertTrue(m ? usb.serialPort.getRTS() : usb.serialPort.getDTR()); | ||||||
|  |         assertTrue(usb.serialPort.getControlLines().contains(m ? ControlLine.CTS : ControlLine.DSR)); | ||||||
|  |         new NoRead().run(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @Test |     @Test | ||||||
|     public void setBreak() throws Exception { |     public void setBreak() throws Exception { | ||||||
|         usb.open(); |         usb.open(); | ||||||
| @ -2005,7 +2368,7 @@ public class DeviceTest { | |||||||
|             // BREAK forwarding not implemented by arduino_leonardo_bridge.ino |             // BREAK forwarding not implemented by arduino_leonardo_bridge.ino | ||||||
|             assertThat("<break>", data, equalTo(new byte[]{})); |             assertThat("<break>", data, equalTo(new byte[]{})); | ||||||
|         } else if(usb.isCp21xxRestrictedPort) { |         } else if(usb.isCp21xxRestrictedPort) { | ||||||
|             assertThat("<break>", data, equalTo(new byte[]{0x26})); // send the last byte again? |             assertThat("<break>", data, equalTo(new byte[]{0x55})); // send the last byte again? | ||||||
|         } else { |         } else { | ||||||
|             assertThat("<break>", data, equalTo(new byte[]{0})); |             assertThat("<break>", data, equalTo(new byte[]{0})); | ||||||
|         } |         } | ||||||
| @ -2161,7 +2524,11 @@ public class DeviceTest { | |||||||
|         assertThrows(UnsupportedOperationException.class, wrongSerialPort::getRI); |         assertThrows(UnsupportedOperationException.class, wrongSerialPort::getRI); | ||||||
|         assertThrows(UnsupportedOperationException.class, wrongSerialPort::getRTS); |         assertThrows(UnsupportedOperationException.class, wrongSerialPort::getRTS); | ||||||
|         assertThrows(UnsupportedOperationException.class, () -> wrongSerialPort.setRTS(true)); |         assertThrows(UnsupportedOperationException.class, () -> wrongSerialPort.setRTS(true)); | ||||||
|  |         assertEquals(EnumSet.noneOf(ControlLine.class), wrongSerialPort.getSupportedControlLines()); | ||||||
|         assertThrows(UnsupportedOperationException.class, wrongSerialPort::getControlLines); |         assertThrows(UnsupportedOperationException.class, wrongSerialPort::getControlLines); | ||||||
|  |         assertEquals(EnumSet.of(FlowControl.NONE), wrongSerialPort.getSupportedFlowControl()); | ||||||
|  |         assertEquals(FlowControl.NONE, wrongSerialPort.getFlowControl()); | ||||||
|  |         assertThrows(UnsupportedOperationException.class, () -> wrongSerialPort.setFlowControl(FlowControl.NONE)); | ||||||
|         assertThrows(UnsupportedOperationException.class, () -> wrongSerialPort.purgeHwBuffers(true, true)); |         assertThrows(UnsupportedOperationException.class, () -> wrongSerialPort.purgeHwBuffers(true, true)); | ||||||
|         assertThrows(UnsupportedOperationException.class, () -> wrongSerialPort.setBreak(true)); |         assertThrows(UnsupportedOperationException.class, () -> wrongSerialPort.setBreak(true)); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -18,6 +18,7 @@ import com.hoho.android.usbserial.driver.CommonUsbSerialPort; | |||||||
| import com.hoho.android.usbserial.driver.Cp21xxSerialDriver; | import com.hoho.android.usbserial.driver.Cp21xxSerialDriver; | ||||||
| import com.hoho.android.usbserial.driver.FtdiSerialDriver; | import com.hoho.android.usbserial.driver.FtdiSerialDriver; | ||||||
| import com.hoho.android.usbserial.driver.ProlificSerialDriver; | 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.UsbId; | ||||||
| import com.hoho.android.usbserial.driver.UsbSerialDriver; | import com.hoho.android.usbserial.driver.UsbSerialDriver; | ||||||
| import com.hoho.android.usbserial.driver.UsbSerialPort; | import com.hoho.android.usbserial.driver.UsbSerialPort; | ||||||
| @ -62,6 +63,7 @@ public class UsbWrapper implements SerialInputOutputManager.Listener { | |||||||
|     public boolean inputLinesOnlyRtsCts; |     public boolean inputLinesOnlyRtsCts; | ||||||
|     public int writePacketSize = -1; |     public int writePacketSize = -1; | ||||||
|     public int writeBufferSize = -1; |     public int writeBufferSize = -1; | ||||||
|  |     public int readBufferSize = -1; | ||||||
| 
 | 
 | ||||||
|     public UsbWrapper(Context context, UsbSerialDriver serialDriver, int devicePort) { |     public UsbWrapper(Context context, UsbSerialDriver serialDriver, int devicePort) { | ||||||
|         this.context = context; |         this.context = context; | ||||||
| @ -145,10 +147,18 @@ public class UsbWrapper implements SerialInputOutputManager.Listener { | |||||||
|                 case 2: writePacketSize = 512; writeBufferSize = 4096; break; |                 case 2: writePacketSize = 512; writeBufferSize = 4096; break; | ||||||
|                 case 4: writePacketSize = 512; writeBufferSize = 2048; break; |                 case 4: writePacketSize = 512; writeBufferSize = 2048; break; | ||||||
|             } |             } | ||||||
|  |             if(serialDriver.getDevice().getProductId() == UsbId.FTDI_FT231X) | ||||||
|  |                 writeBufferSize = 512; | ||||||
|         } else if (serialDriver instanceof CdcAcmSerialDriver) { |         } else if (serialDriver instanceof CdcAcmSerialDriver) { | ||||||
|             writePacketSize = 64; writeBufferSize = 128; |             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() { |     public void tearDown() { | ||||||
| @ -177,6 +187,8 @@ public class UsbWrapper implements SerialInputOutputManager.Listener { | |||||||
|                 if(!flags.contains(OpenCloseFlags.NO_CONTROL_LINE_INIT)) { |                 if(!flags.contains(OpenCloseFlags.NO_CONTROL_LINE_INIT)) { | ||||||
|                     serialPort.setDTR(false); |                     serialPort.setDTR(false); | ||||||
|                     serialPort.setRTS(false); |                     serialPort.setRTS(false); | ||||||
|  |                     if (serialPort.getFlowControl() != UsbSerialPort.FlowControl.NONE) | ||||||
|  |                         serialPort.setFlowControl(UsbSerialPort.FlowControl.NONE); | ||||||
|                 } |                 } | ||||||
|             } catch (Exception ignored) { |             } catch (Exception ignored) { | ||||||
|             } |             } | ||||||
| @ -226,6 +238,10 @@ public class UsbWrapper implements SerialInputOutputManager.Listener { | |||||||
|             readBuffer.clear(); |             readBuffer.clear(); | ||||||
|         } |         } | ||||||
|         readError = null; |         readError = null; | ||||||
|  | 
 | ||||||
|  |         if (serialDriver instanceof ProlificSerialDriver && ProlificSerialPortWrapper.isDeviceTypeHxn(serialPort)) { | ||||||
|  |             readBufferSize = 768; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public void waitForIoManagerStarted() throws IOException { |     public void waitForIoManagerStarted() throws IOException { | ||||||
|  | |||||||
| @ -79,11 +79,6 @@ public class ChromeCcdSerialDriver implements UsbSerialDriver{ | |||||||
|         public void setParameters(int baudRate, int dataBits, int stopBits, int parity) throws IOException { |         public void setParameters(int baudRate, int dataBits, int stopBits, int parity) throws IOException { | ||||||
|             throw new UnsupportedOperationException(); |             throw new UnsupportedOperationException(); | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public EnumSet<ControlLine> getSupportedControlLines() throws IOException { |  | ||||||
|             return EnumSet.noneOf(ControlLine.class); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static Map<Integer, int[]> getSupportedDevices() { |     public static Map<Integer, int[]> getSupportedDevices() { | ||||||
|  | |||||||
| @ -16,6 +16,7 @@ import com.hoho.android.usbserial.util.MonotonicClock; | |||||||
| 
 | 
 | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.nio.ByteBuffer; | import java.nio.ByteBuffer; | ||||||
|  | import java.security.InvalidParameterException; | ||||||
| import java.util.EnumSet; | import java.util.EnumSet; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -38,6 +39,7 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort { | |||||||
|     protected UsbEndpoint mReadEndpoint; |     protected UsbEndpoint mReadEndpoint; | ||||||
|     protected UsbEndpoint mWriteEndpoint; |     protected UsbEndpoint mWriteEndpoint; | ||||||
|     protected UsbRequest mUsbRequest; |     protected UsbRequest mUsbRequest; | ||||||
|  |     protected FlowControl mFlowControl = FlowControl.NONE; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Internal write buffer. |      * Internal write buffer. | ||||||
| @ -281,6 +283,7 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort { | |||||||
|             if (actualLength <= 0) { |             if (actualLength <= 0) { | ||||||
|                 String msg = "Error writing " + requestLength + " bytes at offset " + offset + " of total " + src.length + " after " + elapsed + "msec, rc=" + actualLength; |                 String msg = "Error writing " + requestLength + " bytes at offset " + offset + " of total " + src.length + " after " + elapsed + "msec, rc=" + actualLength; | ||||||
|                 if (timeout != 0) { |                 if (timeout != 0) { | ||||||
|  |                     // could be buffer full because: writing to fast, stopped by flow control | ||||||
|                     testConnection(elapsed < timeout, msg); |                     testConnection(elapsed < timeout, msg); | ||||||
|                     throw new SerialTimeoutException(msg, offset); |                     throw new SerialTimeoutException(msg, offset); | ||||||
|                 } else { |                 } else { | ||||||
| @ -328,12 +331,22 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort { | |||||||
|     public EnumSet<ControlLine> getControlLines() throws IOException { throw new UnsupportedOperationException(); } |     public EnumSet<ControlLine> getControlLines() throws IOException { throw new UnsupportedOperationException(); } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public abstract EnumSet<ControlLine> getSupportedControlLines() throws IOException; |     public EnumSet<ControlLine> getSupportedControlLines() throws IOException { return EnumSet.noneOf(ControlLine.class); } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public void purgeHwBuffers(boolean purgeWriteBuffers, boolean purgeReadBuffers) throws IOException { |     public void setFlowControl(FlowControl flowcontrol) throws IOException { throw new UnsupportedOperationException(); } | ||||||
|         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 |     @Override | ||||||
|     public void setBreak(boolean value) throws IOException { throw new UnsupportedOperationException(); } |     public void setBreak(boolean value) throws IOException { throw new UnsupportedOperationException(); } | ||||||
|  | |||||||
| @ -60,9 +60,14 @@ public class Cp21xxSerialDriver implements UsbSerialDriver { | |||||||
|         private static final int SILABSER_SET_LINE_CTL_REQUEST_CODE = 0x03; |         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_BREAK_REQUEST_CODE = 0x05; | ||||||
|         private static final int SILABSER_SET_MHS_REQUEST_CODE = 0x07; |         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_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_READ_CODE = 0x0a; | ||||||
|         private static final int FLUSH_WRITE_CODE = 0x05; |         private static final int FLUSH_WRITE_CODE = 0x05; | ||||||
| @ -84,6 +89,8 @@ public class Cp21xxSerialDriver implements UsbSerialDriver { | |||||||
|         /* |         /* | ||||||
|         * SILABSER_GET_MDMSTS_REQUEST_CODE |         * 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_CTS = 0x10; | ||||||
|         private static final int STATUS_DSR = 0x20; |         private static final int STATUS_DSR = 0x20; | ||||||
|         private static final int STATUS_RI = 0x40; |         private static final int STATUS_RI = 0x40; | ||||||
| @ -147,6 +154,7 @@ public class Cp21xxSerialDriver implements UsbSerialDriver { | |||||||
| 
 | 
 | ||||||
|             setConfigSingle(SILABSER_IFC_ENABLE_REQUEST_CODE, UART_ENABLE); |             setConfigSingle(SILABSER_IFC_ENABLE_REQUEST_CODE, UART_ENABLE); | ||||||
|             setConfigSingle(SILABSER_SET_MHS_REQUEST_CODE, (dtr ? DTR_ENABLE : DTR_DISABLE) | (rts ? RTS_ENABLE : RTS_DISABLE)); |             setConfigSingle(SILABSER_SET_MHS_REQUEST_CODE, (dtr ? DTR_ENABLE : DTR_DISABLE) | (rts ? RTS_ENABLE : RTS_DISABLE)); | ||||||
|  |             setFlowControl(mFlowControl); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         @Override |         @Override | ||||||
| @ -166,7 +174,7 @@ public class Cp21xxSerialDriver implements UsbSerialDriver { | |||||||
|                     (byte) ((baudRate >> 16) & 0xff), |                     (byte) ((baudRate >> 16) & 0xff), | ||||||
|                     (byte) ((baudRate >> 24) & 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); |                     0, mPortNumber, data, 4, USB_WRITE_TIMEOUT_MILLIS); | ||||||
|             if (ret < 0) { |             if (ret < 0) { | ||||||
|                 throw new IOException("Error setting baud rate"); |                 throw new IOException("Error setting baud rate"); | ||||||
| @ -289,9 +297,11 @@ public class Cp21xxSerialDriver implements UsbSerialDriver { | |||||||
|         public EnumSet<ControlLine> getControlLines() throws IOException { |         public EnumSet<ControlLine> getControlLines() throws IOException { | ||||||
|             byte status = getStatus(); |             byte status = getStatus(); | ||||||
|             EnumSet<ControlLine> set = EnumSet.noneOf(ControlLine.class); |             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((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_DSR) != 0) set.add(ControlLine.DSR); | ||||||
|             if((status & STATUS_CD) != 0) set.add(ControlLine.CD); |             if((status & STATUS_CD) != 0) set.add(ControlLine.CD); | ||||||
|             if((status & STATUS_RI) != 0) set.add(ControlLine.RI); |             if((status & STATUS_RI) != 0) set.add(ControlLine.RI); | ||||||
| @ -303,6 +313,73 @@ public class Cp21xxSerialDriver implements UsbSerialDriver { | |||||||
|             return EnumSet.allOf(ControlLine.class); |             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 |         @Override | ||||||
|         // note: only working on some devices, on other devices ignored w/o error |         // note: only working on some devices, on other devices ignored w/o error | ||||||
|         public void purgeHwBuffers(boolean purgeWriteBuffers, boolean purgeReadBuffers) throws IOException { |         public void purgeHwBuffers(boolean purgeWriteBuffers, boolean purgeReadBuffers) throws IOException { | ||||||
|  | |||||||
| @ -64,6 +64,7 @@ public class FtdiSerialDriver implements UsbSerialDriver { | |||||||
| 
 | 
 | ||||||
|         private static final int RESET_REQUEST = 0; |         private static final int RESET_REQUEST = 0; | ||||||
|         private static final int MODEM_CONTROL_REQUEST = 1; |         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_BAUD_RATE_REQUEST = 3; | ||||||
|         private static final int SET_DATA_REQUEST = 4; |         private static final int SET_DATA_REQUEST = 4; | ||||||
|         private static final int GET_MODEM_STATUS_REQUEST = 5; |         private static final int GET_MODEM_STATUS_REQUEST = 5; | ||||||
| @ -120,6 +121,7 @@ public class FtdiSerialDriver implements UsbSerialDriver { | |||||||
|             if (result != 0) { |             if (result != 0) { | ||||||
|                 throw new IOException("Init RTS,DTR failed: result=" + result); |                 throw new IOException("Init RTS,DTR failed: result=" + result); | ||||||
|             } |             } | ||||||
|  |             setFlowControl(mFlowControl); | ||||||
| 
 | 
 | ||||||
|             // mDevice.getVersion() would require API 23 |             // mDevice.getVersion() would require API 23 | ||||||
|             byte[] rawDescriptors = mConnection.getRawDescriptors(); |             byte[] rawDescriptors = mConnection.getRawDescriptors(); | ||||||
| @ -377,6 +379,38 @@ public class FtdiSerialDriver implements UsbSerialDriver { | |||||||
|             return EnumSet.allOf(ControlLine.class); |             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 |         @Override | ||||||
|         public void purgeHwBuffers(boolean purgeWriteBuffers, boolean purgeReadBuffers) throws IOException { |         public void purgeHwBuffers(boolean purgeWriteBuffers, boolean purgeReadBuffers) throws IOException { | ||||||
|             if (purgeWriteBuffers) { |             if (purgeWriteBuffers) { | ||||||
|  | |||||||
| @ -89,10 +89,6 @@ public class GsmModemSerialDriver implements UsbSerialDriver{ | |||||||
|             throw new UnsupportedOperationException(); |             throw new UnsupportedOperationException(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         @Override |  | ||||||
|         public EnumSet<ControlLine> getSupportedControlLines() throws IOException { |  | ||||||
|             return EnumSet.noneOf(ControlLine.class); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static Map<Integer, int[]> getSupportedDevices() { |     public static Map<Integer, int[]> getSupportedDevices() { | ||||||
|  | |||||||
| @ -315,6 +315,7 @@ public class ProlificSerialDriver implements UsbSerialDriver { | |||||||
|             resetDevice(); |             resetDevice(); | ||||||
|             doBlackMagic(); |             doBlackMagic(); | ||||||
|             setControlLines(mControlLinesValue); |             setControlLines(mControlLinesValue); | ||||||
|  |             setFlowControl(mFlowControl); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         @Override |         @Override | ||||||
| @ -526,7 +527,6 @@ public class ProlificSerialDriver implements UsbSerialDriver { | |||||||
|             setControlLines(newControlLinesValue); |             setControlLines(newControlLinesValue); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|         @Override |         @Override | ||||||
|         public EnumSet<ControlLine> getControlLines() throws IOException { |         public EnumSet<ControlLine> getControlLines() throws IOException { | ||||||
|             int status = getStatus(); |             int status = getStatus(); | ||||||
| @ -545,6 +545,39 @@ public class ProlificSerialDriver implements UsbSerialDriver { | |||||||
|             return EnumSet.allOf(ControlLine.class); |             return EnumSet.allOf(ControlLine.class); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         @Override | ||||||
|  |         public void setFlowControl(FlowControl flowControl) throws IOException { | ||||||
|  |             // vendorOut values from https://www.mail-archive.com/linux-usb@vger.kernel.org/msg110968.html | ||||||
|  |             switch (flowControl) { | ||||||
|  |                 case NONE: | ||||||
|  |                     if (mDeviceType == DeviceType.DEVICE_TYPE_HXN) | ||||||
|  |                         vendorOut(0x0a, 0xff, null); | ||||||
|  |                     else | ||||||
|  |                         vendorOut(0, 0, null); | ||||||
|  |                     break; | ||||||
|  |                 case RTS_CTS: | ||||||
|  |                     if (mDeviceType == DeviceType.DEVICE_TYPE_HXN) | ||||||
|  |                         vendorOut(0x0a, 0xfa, null); | ||||||
|  |                     else | ||||||
|  |                         vendorOut(0, 0x61, null); | ||||||
|  |                     break; | ||||||
|  |                 case XON_XOFF_INLINE: | ||||||
|  |                     if (mDeviceType == DeviceType.DEVICE_TYPE_HXN) | ||||||
|  |                         vendorOut(0x0a, 0xee, null); | ||||||
|  |                     else | ||||||
|  |                         vendorOut(0, 0xc1, null); | ||||||
|  |                     break; | ||||||
|  |                 default: | ||||||
|  |                     throw new UnsupportedOperationException(); | ||||||
|  |             } | ||||||
|  |             mFlowControl = flowControl; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         @Override | ||||||
|  |         public EnumSet<FlowControl> getSupportedFlowControl() { | ||||||
|  |             return EnumSet.of(FlowControl.NONE, FlowControl.RTS_CTS, FlowControl.XON_XOFF_INLINE); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         @Override |         @Override | ||||||
|         public void purgeHwBuffers(boolean purgeWriteBuffers, boolean purgeReadBuffers) throws IOException { |         public void purgeHwBuffers(boolean purgeWriteBuffers, boolean purgeReadBuffers) throws IOException { | ||||||
|             if (mDeviceType == DeviceType.DEVICE_TYPE_HXN) { |             if (mDeviceType == DeviceType.DEVICE_TYPE_HXN) { | ||||||
|  | |||||||
| @ -60,6 +60,15 @@ public interface UsbSerialPort extends Closeable { | |||||||
|     /** Values for get[Supported]ControlLines() */ |     /** Values for get[Supported]ControlLines() */ | ||||||
|     enum ControlLine { RTS, CTS, DTR, DSR, CD, RI } |     enum ControlLine { RTS, CTS, DTR, DSR, CD, RI } | ||||||
| 
 | 
 | ||||||
|  |     /** Values for (set|get|getSupported)FlowControl() */ | ||||||
|  |     enum FlowControl { NONE, RTS_CTS, DTR_DSR, XON_XOFF, XON_XOFF_INLINE } | ||||||
|  | 
 | ||||||
|  |     /** XON character used with flow control XON/XOFF */ | ||||||
|  |     char CHAR_XON = 17; | ||||||
|  |     /** XOFF character used with flow control XON/XOFF */ | ||||||
|  |     char CHAR_XOFF = 19; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Returns the driver used by this port. |      * Returns the driver used by this port. | ||||||
|      */ |      */ | ||||||
| @ -259,6 +268,36 @@ public interface UsbSerialPort extends Closeable { | |||||||
|      */ |      */ | ||||||
|     EnumSet<ControlLine> getSupportedControlLines() throws IOException; |     EnumSet<ControlLine> getSupportedControlLines() throws IOException; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Set flow control mode, if supported | ||||||
|  |      * @param flowControl @FlowControl | ||||||
|  |      * @throws IOException if an error occurred during writing | ||||||
|  |      * @throws UnsupportedOperationException if not supported | ||||||
|  |      */ | ||||||
|  |     void setFlowControl(FlowControl flowControl) throws IOException; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get flow control mode. | ||||||
|  |      * @return FlowControl | ||||||
|  |      */ | ||||||
|  |     FlowControl getFlowControl(); | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get supported flow control modes | ||||||
|  |      * @return EnumSet.contains(...) is {@code true} if supported, else {@code false} | ||||||
|  |      */ | ||||||
|  |     EnumSet<FlowControl> getSupportedFlowControl(); | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * If flow control = XON_XOFF, indicates that send is enabled by XON. | ||||||
|  |      * Devices supporting flow control = XON_XOFF_INLINE return CHAR_XON/CHAR_XOFF in read() data. | ||||||
|  |      * | ||||||
|  |      * @return the current state | ||||||
|  |      * @throws IOException if an error occurred during reading | ||||||
|  |      * @throws UnsupportedOperationException if not supported | ||||||
|  |      */ | ||||||
|  |     boolean getXON() throws IOException; | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Purge non-transmitted output data and / or non-read input data. |      * Purge non-transmitted output data and / or non-read input data. | ||||||
|      * |      * | ||||||
|  | |||||||
| @ -0,0 +1,47 @@ | |||||||
|  | package com.hoho.android.usbserial.util; | ||||||
|  | 
 | ||||||
|  | import com.hoho.android.usbserial.driver.UsbSerialPort; | ||||||
|  | 
 | ||||||
|  | import java.io.IOException; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Some devices return XON and XOFF characters inline in read() data. | ||||||
|  |  * Other devices return XON / XOFF condition thru getXOFF() method. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | public class XonXoffFilter { | ||||||
|  |     private boolean xon = true; | ||||||
|  | 
 | ||||||
|  |     public XonXoffFilter() { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public boolean getXON()  { | ||||||
|  |         return xon; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Filter XON/XOFF from read() data and remember | ||||||
|  |      * | ||||||
|  |      * @param data unfiltered data | ||||||
|  |      * @return filtered data | ||||||
|  |      */ | ||||||
|  |     public byte[] filter(byte[] data) { | ||||||
|  |         int found = 0; | ||||||
|  |         for (int i=0; i<data.length; i++) { | ||||||
|  |             if (data[i] == UsbSerialPort.CHAR_XON || data[i] == UsbSerialPort.CHAR_XOFF) | ||||||
|  |                 found++; | ||||||
|  |         } | ||||||
|  |         if(found == 0) | ||||||
|  |             return data; | ||||||
|  |         byte[] filtered = new byte[data.length - found]; | ||||||
|  |         for (int i=0, j=0; i<data.length; i++) { | ||||||
|  |             if (data[i] == UsbSerialPort.CHAR_XON) | ||||||
|  |                 xon = true; | ||||||
|  |             else if(data[i] == UsbSerialPort.CHAR_XOFF) | ||||||
|  |                 xon = false; | ||||||
|  |             else | ||||||
|  |                 filtered[j++] = data[i]; | ||||||
|  |         } | ||||||
|  |         return filtered; | ||||||
|  |     } | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user