mirror of
				https://github.com/mik3y/usb-serial-for-android
				synced 2025-10-28 00:47:27 +00:00 
			
		
		
		
	flowcontrol for ftdi, pl2303, cp210x
This commit is contained in:
		
							parent
							
								
									843792001f
								
							
						
					
					
						commit
						88ca3f57c4
					
				
							
								
								
									
										3
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -5,4 +5,5 @@ workspace.xml | ||||
| androidTestResultsUserPreferences.xml | ||||
| appInsightsSettings.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) | ||||
| folder in this project. | ||||
| 
 | ||||
| For a more complete example with background service to stay connected while | ||||
| the app is not visible or rotating, see separate github project  | ||||
| [SimpleUsbTerminal](https://github.com/kai-morich/SimpleUsbTerminal). | ||||
| See separate github project [SimpleUsbTerminal](https://github.com/kai-morich/SimpleUsbTerminal)  | ||||
| for a more complete example with: | ||||
| * Background service to stay connected while the app is not visible or rotating | ||||
| * Flow control  | ||||
| 
 | ||||
| ## Probing for Unrecognized Devices | ||||
| 
 | ||||
|  | ||||
| @ -50,7 +50,7 @@ project.afterEvaluate { | ||||
|                 // values used for local maven repo, jitpack uses github release: | ||||
|                 groupId 'com.github.mik3y' | ||||
|                 artifactId 'usb-serial-for-android' | ||||
|                 version '3.7.3beta' | ||||
|                 version '3.8.0beta' | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -18,7 +18,7 @@ android { | ||||
|         } | ||||
|         cp2102 { // and cp2105 first port | ||||
|             dimension 'device' | ||||
|             testInstrumentationRunnerArguments = ['test_device_driver': 'Cp21xx'] | ||||
|             testInstrumentationRunnerArguments = ['test_device_driver': 'Cp21xx', 'test_device_port': '0'] | ||||
|         } | ||||
|         cp2105 { // second port | ||||
|             dimension 'device' | ||||
| @ -26,7 +26,7 @@ android { | ||||
|         } | ||||
|         ft232 { // and ft2232 first port | ||||
|             dimension 'device' | ||||
|             testInstrumentationRunnerArguments = ['test_device_driver': 'Ftdi'] | ||||
|             testInstrumentationRunnerArguments = ['test_device_driver': 'Ftdi', 'test_device_port': '0'] | ||||
|         } | ||||
|         ft2232 { // second port | ||||
|             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.UsbWrapper; | ||||
| 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; | ||||
| @ -80,7 +82,10 @@ import static org.junit.Assert.fail; | ||||
| public class DeviceTest { | ||||
|     private final static String  TAG = DeviceTest.class.getSimpleName(); | ||||
| 
 | ||||
|     enum FlowControl_OutputLineLocked { FALSE, ON_BUFFER_FULL, TRUE } | ||||
| 
 | ||||
|     // testInstrumentationRunnerArguments configuration | ||||
| 
 | ||||
|     private static String  rfc2217_server_host; | ||||
|     private static int     rfc2217_server_port = 2217; | ||||
|     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_nonstandard_baudrates = Boolean.valueOf(InstrumentationRegistry.getArguments().getString("rfc2217_server_nonstandard_baudrates")); | ||||
|         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 | ||||
|         // and test terminates with misleading 'Empty test suite' | ||||
| @ -129,12 +134,16 @@ public class DeviceTest { | ||||
|             String driverName = usbSerialDriver.getClass().getSimpleName(); | ||||
|             assertEquals(test_device_driver+"SerialDriver", driverName); | ||||
|         } | ||||
|         assertTrue( usbSerialDriver.getPorts().size() > test_device_port); | ||||
|         if (test_device_port == -1) { | ||||
|             test_device_port = usbSerialDriver.getPorts().size() - 1; | ||||
|         } else { | ||||
|             assertTrue( usbSerialDriver.getPorts().size() > test_device_port); | ||||
|         } | ||||
|         usb = new UsbWrapper(context, usbSerialDriver, test_device_port); | ||||
|         usb.setUp(); | ||||
| 
 | ||||
|         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 | ||||
| @ -212,7 +221,8 @@ public class DeviceTest { | ||||
|         telnet.write(buf1); | ||||
|         data = usb.read(buf1.length, -1, readWait); | ||||
|         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); | ||||
|         data = telnet.read(buf2.length, readWait); | ||||
|         assertThat(reason, data, equalTo(buf2)); | ||||
| @ -868,7 +878,7 @@ public class DeviceTest { | ||||
|                      usb.serialPort.getReadEndpoint().getMaxPacketSize()); | ||||
| 
 | ||||
|         int baudRate = 300; | ||||
|         if(usb.serialDriver instanceof Cp21xxSerialDriver && usb.serialDriver.getPorts().size() > 1) | ||||
|         if(usb.serialDriver instanceof Cp21xxSerialDriver && usb.serialPort.getPortNumber() > 0) | ||||
|             baudRate = 2400; | ||||
|         usb.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! | ||||
|         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()); | ||||
|             UsbSerialDriver wrongSerialDriver = new Cp21xxSerialDriver(usb.serialDriver.getDevice()); | ||||
|             UsbSerialPort wrongSerialPort = wrongSerialDriver.getPorts().get(0); | ||||
| @ -1701,7 +1711,8 @@ public class DeviceTest { | ||||
|             } 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()); | ||||
|             UsbSerialDriver wrongSerialDriver = new FtdiSerialDriver(usb.serialDriver.getDevice()); | ||||
|             UsbSerialPort wrongSerialPort = wrongSerialDriver.getPorts().get(0); | ||||
| @ -1745,7 +1756,6 @@ public class DeviceTest { | ||||
|             assertEquals(usb.serialDriver.getDevice(), wrongSerialDriver.getDevice()); | ||||
|             assertEquals(wrongSerialDriver, wrongSerialPort.getDriver()); | ||||
|             assertThrows(UnsupportedOperationException.class, () -> wrongSerialPort.setParameters(9200, 8, 1, 0)); | ||||
|             assertEquals(EnumSet.noneOf(ControlLine.class), wrongSerialPort.getSupportedControlLines()); | ||||
|             try { | ||||
|                 wrongSerialPort.close(); | ||||
|             } catch (IOException ignored) { | ||||
| @ -1762,7 +1772,6 @@ public class DeviceTest { | ||||
|             assertEquals(usb.serialDriver.getDevice(), wrongSerialDriver.getDevice()); | ||||
|             assertEquals(wrongSerialDriver, wrongSerialPort.getDriver()); | ||||
|             assertThrows(UnsupportedOperationException.class, () -> wrongSerialPort.setParameters(9200, 8, 1, 0)); | ||||
|             assertEquals(EnumSet.noneOf(ControlLine.class), wrongSerialPort.getSupportedControlLines()); | ||||
|             try { | ||||
|                 wrongSerialPort.close(); | ||||
|             } 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 | ||||
|     public void setBreak() throws Exception { | ||||
|         usb.open(); | ||||
| @ -2005,7 +2368,7 @@ public class DeviceTest { | ||||
|             // BREAK forwarding not implemented by arduino_leonardo_bridge.ino | ||||
|             assertThat("<break>", data, equalTo(new byte[]{})); | ||||
|         } 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 { | ||||
|             assertThat("<break>", data, equalTo(new byte[]{0})); | ||||
|         } | ||||
| @ -2161,7 +2524,11 @@ public class DeviceTest { | ||||
|         assertThrows(UnsupportedOperationException.class, wrongSerialPort::getRI); | ||||
|         assertThrows(UnsupportedOperationException.class, wrongSerialPort::getRTS); | ||||
|         assertThrows(UnsupportedOperationException.class, () -> wrongSerialPort.setRTS(true)); | ||||
|         assertEquals(EnumSet.noneOf(ControlLine.class), wrongSerialPort.getSupportedControlLines()); | ||||
|         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.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.FtdiSerialDriver; | ||||
| 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.UsbSerialDriver; | ||||
| import com.hoho.android.usbserial.driver.UsbSerialPort; | ||||
| @ -62,6 +63,7 @@ public class UsbWrapper implements SerialInputOutputManager.Listener { | ||||
|     public boolean inputLinesOnlyRtsCts; | ||||
|     public int writePacketSize = -1; | ||||
|     public int writeBufferSize = -1; | ||||
|     public int readBufferSize = -1; | ||||
| 
 | ||||
|     public UsbWrapper(Context context, UsbSerialDriver serialDriver, int devicePort) { | ||||
|         this.context = context; | ||||
| @ -145,10 +147,18 @@ public class UsbWrapper implements SerialInputOutputManager.Listener { | ||||
|                 case 2: writePacketSize = 512; writeBufferSize = 4096; break; | ||||
|                 case 4: writePacketSize = 512; writeBufferSize = 2048; break; | ||||
|             } | ||||
|             if(serialDriver.getDevice().getProductId() == UsbId.FTDI_FT231X) | ||||
|                 writeBufferSize = 512; | ||||
|         } else if (serialDriver instanceof CdcAcmSerialDriver) { | ||||
|             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() { | ||||
| @ -177,6 +187,8 @@ public class UsbWrapper implements SerialInputOutputManager.Listener { | ||||
|                 if(!flags.contains(OpenCloseFlags.NO_CONTROL_LINE_INIT)) { | ||||
|                     serialPort.setDTR(false); | ||||
|                     serialPort.setRTS(false); | ||||
|                     if (serialPort.getFlowControl() != UsbSerialPort.FlowControl.NONE) | ||||
|                         serialPort.setFlowControl(UsbSerialPort.FlowControl.NONE); | ||||
|                 } | ||||
|             } catch (Exception ignored) { | ||||
|             } | ||||
| @ -226,6 +238,10 @@ public class UsbWrapper implements SerialInputOutputManager.Listener { | ||||
|             readBuffer.clear(); | ||||
|         } | ||||
|         readError = null; | ||||
| 
 | ||||
|         if (serialDriver instanceof ProlificSerialDriver && ProlificSerialPortWrapper.isDeviceTypeHxn(serialPort)) { | ||||
|             readBufferSize = 768; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     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 { | ||||
|             throw new UnsupportedOperationException(); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public EnumSet<ControlLine> getSupportedControlLines() throws IOException { | ||||
|             return EnumSet.noneOf(ControlLine.class); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static Map<Integer, int[]> getSupportedDevices() { | ||||
|  | ||||
| @ -16,6 +16,7 @@ import com.hoho.android.usbserial.util.MonotonicClock; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| import java.nio.ByteBuffer; | ||||
| import java.security.InvalidParameterException; | ||||
| import java.util.EnumSet; | ||||
| 
 | ||||
| /** | ||||
| @ -38,6 +39,7 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort { | ||||
|     protected UsbEndpoint mReadEndpoint; | ||||
|     protected UsbEndpoint mWriteEndpoint; | ||||
|     protected UsbRequest mUsbRequest; | ||||
|     protected FlowControl mFlowControl = FlowControl.NONE; | ||||
| 
 | ||||
|     /** | ||||
|      * Internal write buffer. | ||||
| @ -281,6 +283,7 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort { | ||||
|             if (actualLength <= 0) { | ||||
|                 String msg = "Error writing " + requestLength + " bytes at offset " + offset + " of total " + src.length + " after " + elapsed + "msec, rc=" + actualLength; | ||||
|                 if (timeout != 0) { | ||||
|                     // could be buffer full because: writing to fast, stopped by flow control | ||||
|                     testConnection(elapsed < timeout, msg); | ||||
|                     throw new SerialTimeoutException(msg, offset); | ||||
|                 } else { | ||||
| @ -328,12 +331,22 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort { | ||||
|     public EnumSet<ControlLine> getControlLines() throws IOException { throw new UnsupportedOperationException(); } | ||||
| 
 | ||||
|     @Override | ||||
|     public abstract EnumSet<ControlLine> getSupportedControlLines() throws IOException; | ||||
|     public EnumSet<ControlLine> getSupportedControlLines() throws IOException { return EnumSet.noneOf(ControlLine.class); } | ||||
| 
 | ||||
|     @Override | ||||
|     public void purgeHwBuffers(boolean purgeWriteBuffers, boolean purgeReadBuffers) throws IOException { | ||||
|         throw new UnsupportedOperationException(); | ||||
|     } | ||||
|     public void setFlowControl(FlowControl flowcontrol) throws IOException { 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 | ||||
|     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_BREAK_REQUEST_CODE = 0x05; | ||||
|         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_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_WRITE_CODE = 0x05; | ||||
| @ -84,6 +89,8 @@ public class Cp21xxSerialDriver implements UsbSerialDriver { | ||||
|         /* | ||||
|         * 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_DSR = 0x20; | ||||
|         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_SET_MHS_REQUEST_CODE, (dtr ? DTR_ENABLE : DTR_DISABLE) | (rts ? RTS_ENABLE : RTS_DISABLE)); | ||||
|             setFlowControl(mFlowControl); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
| @ -166,7 +174,7 @@ public class Cp21xxSerialDriver implements UsbSerialDriver { | ||||
|                     (byte) ((baudRate >> 16) & 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); | ||||
|             if (ret < 0) { | ||||
|                 throw new IOException("Error setting baud rate"); | ||||
| @ -289,9 +297,11 @@ public class Cp21xxSerialDriver implements UsbSerialDriver { | ||||
|         public EnumSet<ControlLine> getControlLines() throws IOException { | ||||
|             byte status = getStatus(); | ||||
|             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(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_CD) != 0) set.add(ControlLine.CD); | ||||
|             if((status & STATUS_RI) != 0) set.add(ControlLine.RI); | ||||
| @ -303,6 +313,73 @@ public class Cp21xxSerialDriver implements UsbSerialDriver { | ||||
|             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 | ||||
|         // note: only working on some devices, on other devices ignored w/o error | ||||
|         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 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_DATA_REQUEST = 4; | ||||
|         private static final int GET_MODEM_STATUS_REQUEST = 5; | ||||
| @ -120,6 +121,7 @@ public class FtdiSerialDriver implements UsbSerialDriver { | ||||
|             if (result != 0) { | ||||
|                 throw new IOException("Init RTS,DTR failed: result=" + result); | ||||
|             } | ||||
|             setFlowControl(mFlowControl); | ||||
| 
 | ||||
|             // mDevice.getVersion() would require API 23 | ||||
|             byte[] rawDescriptors = mConnection.getRawDescriptors(); | ||||
| @ -377,6 +379,38 @@ public class FtdiSerialDriver implements UsbSerialDriver { | ||||
|             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 | ||||
|         public void purgeHwBuffers(boolean purgeWriteBuffers, boolean purgeReadBuffers) throws IOException { | ||||
|             if (purgeWriteBuffers) { | ||||
|  | ||||
| @ -89,10 +89,6 @@ public class GsmModemSerialDriver implements UsbSerialDriver{ | ||||
|             throw new UnsupportedOperationException(); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public EnumSet<ControlLine> getSupportedControlLines() throws IOException { | ||||
|             return EnumSet.noneOf(ControlLine.class); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static Map<Integer, int[]> getSupportedDevices() { | ||||
|  | ||||
| @ -315,6 +315,7 @@ public class ProlificSerialDriver implements UsbSerialDriver { | ||||
|             resetDevice(); | ||||
|             doBlackMagic(); | ||||
|             setControlLines(mControlLinesValue); | ||||
|             setFlowControl(mFlowControl); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
| @ -526,7 +527,6 @@ public class ProlificSerialDriver implements UsbSerialDriver { | ||||
|             setControlLines(newControlLinesValue); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         @Override | ||||
|         public EnumSet<ControlLine> getControlLines() throws IOException { | ||||
|             int status = getStatus(); | ||||
| @ -545,6 +545,39 @@ public class ProlificSerialDriver implements UsbSerialDriver { | ||||
|             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 | ||||
|         public void purgeHwBuffers(boolean purgeWriteBuffers, boolean purgeReadBuffers) throws IOException { | ||||
|             if (mDeviceType == DeviceType.DEVICE_TYPE_HXN) { | ||||
|  | ||||
| @ -60,6 +60,15 @@ public interface UsbSerialPort extends Closeable { | ||||
|     /** Values for get[Supported]ControlLines() */ | ||||
|     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. | ||||
|      */ | ||||
| @ -259,6 +268,36 @@ public interface UsbSerialPort extends Closeable { | ||||
|      */ | ||||
|     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. | ||||
|      * | ||||
|  | ||||
| @ -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