mirror of
				https://github.com/mik3y/usb-serial-for-android
				synced 2025-10-31 02:17:23 +00:00 
			
		
		
		
	build tools update; instrumented device test
This commit is contained in:
		
							parent
							
								
									c89ca2c96a
								
							
						
					
					
						commit
						adb22f718e
					
				
							
								
								
									
										37
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										37
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
								
							| @ -1,19 +1,30 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8"?> | <?xml version="1.0" encoding="UTF-8"?> | ||||||
| <project version="4"> | <project version="4"> | ||||||
|   <component name="EntryPointsManager"> |   <component name="NullableNotNullManager"> | ||||||
|     <entry_points version="2.0" /> |     <option name="myDefaultNullable" value="android.support.annotation.Nullable" /> | ||||||
|  |     <option name="myDefaultNotNull" value="android.support.annotation.NonNull" /> | ||||||
|  |     <option name="myNullables"> | ||||||
|  |       <value> | ||||||
|  |         <list size="4"> | ||||||
|  |           <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" /> | ||||||
|  |           <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" /> | ||||||
|  |           <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" /> | ||||||
|  |           <item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" /> | ||||||
|  |         </list> | ||||||
|  |       </value> | ||||||
|  |     </option> | ||||||
|  |     <option name="myNotNulls"> | ||||||
|  |       <value> | ||||||
|  |         <list size="4"> | ||||||
|  |           <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" /> | ||||||
|  |           <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" /> | ||||||
|  |           <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" /> | ||||||
|  |           <item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" /> | ||||||
|  |         </list> | ||||||
|  |       </value> | ||||||
|  |     </option> | ||||||
|   </component> |   </component> | ||||||
|   <component name="ProjectLevelVcsManager" settingsEditedManually="false"> |   <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK"> | ||||||
|     <OptionsSetting value="true" id="Add" /> |  | ||||||
|     <OptionsSetting value="true" id="Remove" /> |  | ||||||
|     <OptionsSetting value="true" id="Checkout" /> |  | ||||||
|     <OptionsSetting value="true" id="Update" /> |  | ||||||
|     <OptionsSetting value="true" id="Status" /> |  | ||||||
|     <OptionsSetting value="true" id="Edit" /> |  | ||||||
|     <ConfirmationsSetting value="0" id="Add" /> |  | ||||||
|     <ConfirmationsSetting value="0" id="Remove" /> |  | ||||||
|   </component> |  | ||||||
|   <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK"> |  | ||||||
|     <output url="file://$PROJECT_DIR$/build/classes" /> |     <output url="file://$PROJECT_DIR$/build/classes" /> | ||||||
|   </component> |   </component> | ||||||
|   <component name="ProjectType"> |   <component name="ProjectType"> | ||||||
|  | |||||||
							
								
								
									
										12
									
								
								.idea/runConfigurations.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								.idea/runConfigurations.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <project version="4"> | ||||||
|  |   <component name="RunConfigurationProducerService"> | ||||||
|  |     <option name="ignoredProducers"> | ||||||
|  |       <set> | ||||||
|  |         <option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" /> | ||||||
|  |         <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" /> | ||||||
|  |         <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" /> | ||||||
|  |       </set> | ||||||
|  |     </option> | ||||||
|  |   </component> | ||||||
|  | </project> | ||||||
| @ -2,15 +2,17 @@ | |||||||
| 
 | 
 | ||||||
| buildscript { | buildscript { | ||||||
|     repositories { |     repositories { | ||||||
|         mavenCentral() |         jcenter() | ||||||
|  |         google() | ||||||
|     } |     } | ||||||
|     dependencies { |     dependencies { | ||||||
|         classpath 'com.android.tools.build:gradle:1.2.3' |         classpath 'com.android.tools.build:gradle:3.0.1' | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| allprojects { | allprojects { | ||||||
|     repositories { |     repositories { | ||||||
|         mavenCentral() |         jcenter() | ||||||
|  |         google() | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										
											BIN
										
									
								
								gradle/wrapper/gradle-wrapper.jar
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								gradle/wrapper/gradle-wrapper.jar
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										4
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							| @ -1,6 +1,6 @@ | |||||||
| #Tue Jun 23 00:11:28 EDT 2015 | #Thu Oct 26 20:06:55 CEST 2017 | ||||||
| distributionBase=GRADLE_USER_HOME | distributionBase=GRADLE_USER_HOME | ||||||
| distributionPath=wrapper/dists | distributionPath=wrapper/dists | ||||||
| zipStoreBase=GRADLE_USER_HOME | zipStoreBase=GRADLE_USER_HOME | ||||||
| zipStorePath=wrapper/dists | zipStorePath=wrapper/dists | ||||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip | ||||||
|  | |||||||
							
								
								
									
										64
									
								
								test/arduino_leonardo_bridge/arduino_leonardo_bridge.ino
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								test/arduino_leonardo_bridge/arduino_leonardo_bridge.ino
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,64 @@ | |||||||
|  | /*
 | ||||||
|  |   bridge USB-serial to hardware-serial | ||||||
|  | 
 | ||||||
|  |   for Arduinos based on ATmega32u4 (Leonardo and compatible Pro Micro, Micro) | ||||||
|  |   hardware serial is configured with baud-rate, databits, stopbits, parity as send over USB | ||||||
|  | 
 | ||||||
|  |   see https://github.com/arduino/Arduino/tree/master/hardware/arduino/avr/cores/arduino 
 | ||||||
|  |   -> CDC.cpp|HardwareSerial.cpp for serial implementation details | ||||||
|  | 
 | ||||||
|  |   this sketch is mainly for demonstration / test of CDC communication | ||||||
|  |   performance as real usb-serial bridge would be inacceptable as each byte is send in separate USB packet | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | uint32_t baud = 9600; | ||||||
|  | uint8_t databits = 8; | ||||||
|  | uint8_t stopbits = 1; | ||||||
|  | uint8_t parity = 0; | ||||||
|  | 
 | ||||||
|  | void setup() { | ||||||
|  |   Serial.begin(baud); // USB
 | ||||||
|  |   Serial1.begin(baud, SERIAL_8N1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void loop() { | ||||||
|  |   // show USB connected state
 | ||||||
|  |   if (Serial) TXLED1; | ||||||
|  |   else        TXLED0; | ||||||
|  | 
 | ||||||
|  |   // configure hardware serial
 | ||||||
|  |   if (Serial.baud() != baud || | ||||||
|  |       Serial.numbits() != databits || | ||||||
|  |       Serial.stopbits() != stopbits || | ||||||
|  |       Serial.paritytype() != parity) { | ||||||
|  |     baud = Serial.baud(); | ||||||
|  |     databits = Serial.numbits(); | ||||||
|  |     stopbits = Serial.stopbits(); | ||||||
|  |     parity = Serial.paritytype(); | ||||||
|  |     uint8_t config = 0; // ucsrc register
 | ||||||
|  |     switch (databits) { | ||||||
|  |       case 5: break; | ||||||
|  |       case 6: config |= 2; break; | ||||||
|  |       case 7: config |= 4; break; | ||||||
|  |       case 8: config |= 6; break; | ||||||
|  |       default: config |= 6; | ||||||
|  |     } | ||||||
|  |     switch (stopbits) { | ||||||
|  |       case 2: config |= 8; | ||||||
|  |         // 1.5 stopbits not supported
 | ||||||
|  |     } | ||||||
|  |     switch (parity) { | ||||||
|  |       case 1: config |= 0x30; break; // odd
 | ||||||
|  |       case 2: config |= 0x20; break; // even
 | ||||||
|  |         // mark, space not supported
 | ||||||
|  |     } | ||||||
|  |     Serial1.end(); | ||||||
|  |     Serial1.begin(baud, config); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // bridge
 | ||||||
|  |   if (Serial.available() > 0) | ||||||
|  |     Serial1.write(Serial.read()); | ||||||
|  |   if (Serial1.available() > 0) | ||||||
|  |     Serial.write(Serial1.read()); | ||||||
|  | } | ||||||
							
								
								
									
										42
									
								
								test/rfc2217_server.diff
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								test/rfc2217_server.diff
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | |||||||
|  | *** /n/archiv/python/rfc2217_server.py	2018-03-10 09:02:07.613771600 +0100 | ||||||
|  | --- rfc2217_server.py	2018-03-09 20:57:44.933717100 +0100
 | ||||||
|  | *************** | ||||||
|  | *** 26,31 **** | ||||||
|  | --- 26,32 ----
 | ||||||
|  |               self, | ||||||
|  |               logger=logging.getLogger('rfc2217.server') if debug else None) | ||||||
|  |           self.log = logging.getLogger('redirector') | ||||||
|  | +         self.dlog = logging.getLogger('data')
 | ||||||
|  |    | ||||||
|  |       def statusline_poller(self): | ||||||
|  |           self.log.debug('status line poll thread started') | ||||||
|  | *************** | ||||||
|  | *** 55,60 **** | ||||||
|  | --- 56,62 ----
 | ||||||
|  |               try: | ||||||
|  |                   data = self.serial.read(self.serial.in_waiting or 1) | ||||||
|  |                   if data: | ||||||
|  | +                     self.dlog.debug("serial read: "+data.encode('hex'))
 | ||||||
|  |                       # escape outgoing data when needed (Telnet IAC (0xff) character) | ||||||
|  |                       self.write(b''.join(self.rfc2217.escape(data))) | ||||||
|  |               except socket.error as msg: | ||||||
|  | *************** | ||||||
|  | *** 76,81 **** | ||||||
|  | --- 78,84 ----
 | ||||||
|  |                   data = self.socket.recv(1024) | ||||||
|  |                   if not data: | ||||||
|  |                       break | ||||||
|  | +                 self.dlog.debug("socket read: "+data.encode('hex'))
 | ||||||
|  |                   self.serial.write(b''.join(self.rfc2217.filter(data))) | ||||||
|  |               except socket.error as msg: | ||||||
|  |                   self.log.error('{}'.format(msg)) | ||||||
|  | *************** | ||||||
|  | *** 132,137 **** | ||||||
|  | --- 135,141 ----
 | ||||||
|  |       logging.basicConfig(level=logging.INFO) | ||||||
|  |       #~ logging.getLogger('root').setLevel(logging.INFO) | ||||||
|  |       logging.getLogger('rfc2217').setLevel(level) | ||||||
|  | +     logging.getLogger('data').setLevel(level)
 | ||||||
|  |    | ||||||
|  |       # connect to serial port | ||||||
|  |       ser = serial.serial_for_url(args.SERIALPORT, do_not_open=True) | ||||||
| @ -1,14 +1,13 @@ | |||||||
| apply plugin: 'com.android.application' | apply plugin: 'com.android.application' | ||||||
| 
 | 
 | ||||||
| android { | android { | ||||||
|     compileSdkVersion 22 |     compileSdkVersion 26 | ||||||
|     buildToolsVersion "22.0.1" |     buildToolsVersion "26.0.2" | ||||||
| 
 | 
 | ||||||
|     defaultConfig { |     defaultConfig { | ||||||
|         minSdkVersion 14 |         minSdkVersion 14 | ||||||
|         targetSdkVersion 22 |         targetSdkVersion 26 | ||||||
| 
 | 
 | ||||||
|         testApplicationId "com.hoho.android.usbserial.examples" |  | ||||||
|         testInstrumentationRunner "android.test.InstrumentationTestRunner" |         testInstrumentationRunner "android.test.InstrumentationTestRunner" | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -20,5 +19,5 @@ android { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| dependencies { | dependencies { | ||||||
|     compile project(':usbSerialForAndroid') |     implementation project(':usbSerialForAndroid') | ||||||
| } | } | ||||||
|  | |||||||
| @ -3,12 +3,13 @@ apply plugin: 'maven' | |||||||
| apply plugin: 'signing' | apply plugin: 'signing' | ||||||
| 
 | 
 | ||||||
| android { | android { | ||||||
|     compileSdkVersion 19 |     compileSdkVersion 26 | ||||||
|     buildToolsVersion "19.1" |     buildToolsVersion "26.0.2" | ||||||
| 
 | 
 | ||||||
|     defaultConfig { |     defaultConfig { | ||||||
|         minSdkVersion 12 |         minSdkVersion 14 | ||||||
|         targetSdkVersion 19 |         targetSdkVersion 26 | ||||||
|  |         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     buildTypes { |     buildTypes { | ||||||
| @ -19,6 +20,14 @@ android { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | dependencies { | ||||||
|  |     testImplementation 'junit:junit:4.12' | ||||||
|  |     androidTestImplementation 'com.android.support:support-annotations:27.1.0' | ||||||
|  |     androidTestImplementation 'com.android.support.test:runner:1.0.1' | ||||||
|  |     androidTestImplementation 'commons-net:commons-net:3.6' | ||||||
|  |     androidTestImplementation 'org.apache.commons:commons-lang3:3.7' | ||||||
|  | } | ||||||
|  | 
 | ||||||
| group = "com.hoho.android" | group = "com.hoho.android" | ||||||
| version = "0.2.0-SNAPSHOT" | version = "0.2.0-SNAPSHOT" | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										7
									
								
								usbSerialForAndroid/src/androidTest/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								usbSerialForAndroid/src/androidTest/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|  |       package="com.hoho.android.usbserial" | ||||||
|  |       android:versionCode="1" | ||||||
|  |       android:versionName="1.0"> | ||||||
|  |     <uses-permission android:name="android.permission.INTERNET"/> | ||||||
|  | </manifest> | ||||||
| @ -0,0 +1,855 @@ | |||||||
|  | /* | ||||||
|  |  * test setup | ||||||
|  |  * - android device with ADB over Wi-Fi | ||||||
|  |  *      - to set up ADB over Wi-Fi with custom roms you typically can do it from: Android settings -> Developer options | ||||||
|  |  *      - for other devices you first have to manually connect over USB and enable Wi-Fi as shown here: | ||||||
|  |  *         https://developer.android.com/studio/command-line/adb.html | ||||||
|  |  * - windows/linux machine running rfc2217_server.py | ||||||
|  |  *      python + pyserial + https://github.com/pyserial/pyserial/blob/master/examples/rfc2217_server.py | ||||||
|  |  *      for developing this test it was essential to see all data (see test/rfc2217_server.diff, run python script with '-v -v' option) | ||||||
|  |  * - all suppported usb <-> serial converter | ||||||
|  |  *      as CDC test device use an arduino leonardo / pro mini programmed with arduino_leonardo_bridge.ino | ||||||
|  |  * | ||||||
|  |  * restrictions | ||||||
|  |  *  - as real hardware is used, timing might need tuning. see: | ||||||
|  |  *      - Thread.sleep(...) | ||||||
|  |  *      - obj.wait(...) | ||||||
|  |  *  - some tests fail sporadically. typical workarounds are: | ||||||
|  |  *      - reconnect device | ||||||
|  |  *      - run test individually | ||||||
|  |  *      - increase sleep? | ||||||
|  |  *  - missing functionality on certain devices, see: | ||||||
|  |  *      - if(rfc2217_server_nonstandard_baudrates) | ||||||
|  |  *      - if(usbSerialDriver instanceof ...) | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | package com.hoho.android.usbserial; | ||||||
|  | 
 | ||||||
|  | import android.app.PendingIntent; | ||||||
|  | import android.content.BroadcastReceiver; | ||||||
|  | import android.content.Context; | ||||||
|  | import android.content.Intent; | ||||||
|  | import android.content.IntentFilter; | ||||||
|  | import android.hardware.usb.UsbDevice; | ||||||
|  | import android.hardware.usb.UsbDeviceConnection; | ||||||
|  | import android.hardware.usb.UsbManager; | ||||||
|  | import android.support.test.InstrumentationRegistry; | ||||||
|  | import android.support.test.runner.AndroidJUnit4; | ||||||
|  | import android.util.Log; | ||||||
|  | 
 | ||||||
|  | import com.hoho.android.usbserial.driver.CdcAcmSerialDriver; | ||||||
|  | import com.hoho.android.usbserial.driver.Ch34xSerialDriver; | ||||||
|  | import com.hoho.android.usbserial.driver.Cp21xxSerialDriver; | ||||||
|  | import com.hoho.android.usbserial.driver.FtdiSerialDriver; | ||||||
|  | import com.hoho.android.usbserial.driver.ProbeTable; | ||||||
|  | import com.hoho.android.usbserial.driver.ProlificSerialDriver; | ||||||
|  | import com.hoho.android.usbserial.driver.UsbSerialDriver; | ||||||
|  | import com.hoho.android.usbserial.driver.UsbSerialPort; | ||||||
|  | import com.hoho.android.usbserial.driver.UsbSerialProber; | ||||||
|  | import com.hoho.android.usbserial.util.SerialInputOutputManager; | ||||||
|  | 
 | ||||||
|  | import org.apache.commons.lang3.StringUtils; | ||||||
|  | import org.apache.commons.net.telnet.InvalidTelnetOptionException; | ||||||
|  | import org.apache.commons.net.telnet.TelnetClient; | ||||||
|  | import org.apache.commons.net.telnet.TelnetCommand; | ||||||
|  | import org.apache.commons.net.telnet.TelnetOptionHandler; | ||||||
|  | import org.junit.After; | ||||||
|  | import org.junit.AfterClass; | ||||||
|  | import org.junit.Before; | ||||||
|  | import org.junit.BeforeClass; | ||||||
|  | import org.junit.Test; | ||||||
|  | import org.junit.runner.RunWith; | ||||||
|  | 
 | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.io.InputStream; | ||||||
|  | import java.io.OutputStream; | ||||||
|  | import java.nio.ByteBuffer; | ||||||
|  | import java.util.Deque; | ||||||
|  | import java.util.LinkedList; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.concurrent.Executors; | ||||||
|  | 
 | ||||||
|  | import static org.hamcrest.CoreMatchers.equalTo; | ||||||
|  | import static org.junit.Assert.assertEquals; | ||||||
|  | import static org.junit.Assert.assertNotEquals; | ||||||
|  | import static org.junit.Assert.assertThat; | ||||||
|  | import static org.junit.Assert.assertTrue; | ||||||
|  | import static org.junit.Assert.fail; | ||||||
|  | 
 | ||||||
|  | @RunWith(AndroidJUnit4.class) | ||||||
|  | public class DeviceTest implements SerialInputOutputManager.Listener { | ||||||
|  | 
 | ||||||
|  |     private final static String  rfc2217_server_host = "192.168.0.171"; | ||||||
|  |     private final static int     rfc2217_server_port = 2217; | ||||||
|  |     private final static boolean rfc2217_server_nonstandard_baudrates = false; // false on Windows | ||||||
|  | 
 | ||||||
|  |     private final static int     TELNET_READ_WAIT = 500; | ||||||
|  |     private final static int     USB_READ_WAIT    = 500; | ||||||
|  |     private final static int     USB_WRITE_WAIT   = 500; | ||||||
|  | 
 | ||||||
|  |     private final static String  TAG = "DeviceTest"; | ||||||
|  |     private final static byte    RFC2217_COM_PORT_OPTION = 0x2c; | ||||||
|  |     private final static byte    RFC2217_SET_BAUDRATE = 1; | ||||||
|  |     private final static byte    RFC2217_SET_DATASIZE = 2; | ||||||
|  |     private final static byte    RFC2217_SET_PARITY   = 3; | ||||||
|  |     private final static byte    RFC2217_SET_STOPSIZE = 4; | ||||||
|  | 
 | ||||||
|  |     private Context context; | ||||||
|  |     private UsbSerialDriver usbSerialDriver; | ||||||
|  |     private UsbDeviceConnection usbDeviceConnection; | ||||||
|  |     private UsbSerialPort usbSerialPort; | ||||||
|  |     private SerialInputOutputManager usbIoManager; | ||||||
|  |     private final Deque<byte[]> usbReadBuffer = new LinkedList<>(); | ||||||
|  |     private boolean usbReadBlock = false; | ||||||
|  | 
 | ||||||
|  |     private static TelnetClient telnetClient; | ||||||
|  |     private static InputStream telnetReadStream; | ||||||
|  |     private static OutputStream telnetWriteStream; | ||||||
|  |     private static Integer[] telnetComPortOptionCounter = {0}; | ||||||
|  |     private int telnetWriteDelay = 0; | ||||||
|  | 
 | ||||||
|  |     @BeforeClass | ||||||
|  |     public static void setUpFixture() throws Exception { | ||||||
|  |         telnetClient = null; | ||||||
|  |         // postpone fixture setup to first test, because exceptions are not reported for @BeforeClass | ||||||
|  |         // and test terminates with missleading 'Empty test suite' | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public static void setUpFixtureInt() throws Exception { | ||||||
|  |         if(telnetClient != null) | ||||||
|  |             return; | ||||||
|  |         telnetClient = new TelnetClient(); | ||||||
|  |         telnetClient.addOptionHandler(new TelnetOptionHandler(RFC2217_COM_PORT_OPTION, false, false, false, false) { | ||||||
|  |             @Override | ||||||
|  |             public int[] answerSubnegotiation(int[] suboptionData, int suboptionLength) { | ||||||
|  |                 telnetComPortOptionCounter[0] += 1; | ||||||
|  |                 return super.answerSubnegotiation(suboptionData, suboptionLength); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         telnetClient.setConnectTimeout(2000); | ||||||
|  |         telnetClient.connect(rfc2217_server_host, rfc2217_server_port); | ||||||
|  |         telnetClient.setTcpNoDelay(true); | ||||||
|  |         telnetWriteStream = telnetClient.getOutputStream(); | ||||||
|  |         telnetReadStream = telnetClient.getInputStream(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Before | ||||||
|  |     public void setUp() throws Exception { | ||||||
|  |         setUpFixtureInt(); | ||||||
|  |         telnetClient.sendAYT(1000); // not corrctly handled by rfc2217_server.py, but WARNING output "ignoring Telnet command: '\xf6'" is a nice separator between tests | ||||||
|  |         telnetComPortOptionCounter[0] = 0; | ||||||
|  |         telnetWriteDelay = 0; | ||||||
|  | 
 | ||||||
|  |         context = InstrumentationRegistry.getContext(); | ||||||
|  |         final UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); | ||||||
|  |         List<UsbSerialDriver> availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(usbManager); | ||||||
|  |         assertEquals("no usb device found", 1, availableDrivers.size()); | ||||||
|  |         usbSerialDriver = availableDrivers.get(0); | ||||||
|  |         assertEquals(1, usbSerialDriver.getPorts().size()); | ||||||
|  |         usbSerialPort = usbSerialDriver.getPorts().get(0); | ||||||
|  |         Log.i(TAG, "Using USB device "+ usbSerialDriver.getClass().getSimpleName()); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         if (!usbManager.hasPermission(usbSerialPort.getDriver().getDevice())) { | ||||||
|  |             final Boolean[] granted = {Boolean.FALSE}; | ||||||
|  |             BroadcastReceiver usbReceiver = new BroadcastReceiver() { | ||||||
|  |                 @Override | ||||||
|  |                 public void onReceive(Context context, Intent intent) { | ||||||
|  |                     granted[0] = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false); | ||||||
|  |                     synchronized (granted) { | ||||||
|  |                         granted.notify(); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             }; | ||||||
|  |             PendingIntent permissionIntent = PendingIntent.getBroadcast(context, 0, new Intent("com.android.example.USB_PERMISSION"), 0); | ||||||
|  |             IntentFilter filter = new IntentFilter("com.android.example.USB_PERMISSION"); | ||||||
|  |             context.registerReceiver(usbReceiver, filter); | ||||||
|  |             usbManager.requestPermission(usbSerialDriver.getDevice(), permissionIntent); | ||||||
|  |             synchronized (granted) { | ||||||
|  |                 granted.wait(5000); | ||||||
|  |             } | ||||||
|  |             assertTrue("USB permission dialog not confirmed", granted[0]); | ||||||
|  |         } | ||||||
|  |         usbDeviceConnection = usbManager.openDevice(usbSerialDriver.getDevice()); | ||||||
|  |         usbSerialPort.open(usbDeviceConnection); | ||||||
|  |         usbSerialPort.setDTR(true); | ||||||
|  |         usbSerialPort.setRTS(true); | ||||||
|  |         usbIoManager = new SerialInputOutputManager(usbSerialPort, this); | ||||||
|  |         Executors.newSingleThreadExecutor().submit(usbIoManager); | ||||||
|  | 
 | ||||||
|  |         synchronized (usbReadBuffer) { | ||||||
|  |             usbReadBuffer.clear(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @After | ||||||
|  |     public void tearDown() throws IOException { | ||||||
|  |         try { | ||||||
|  |             usbRead(0); | ||||||
|  |         } catch (Exception ignored) {} | ||||||
|  |         try { | ||||||
|  |             telnetRead(0); | ||||||
|  |         } catch (Exception ignored) {} | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |             usbIoManager.setListener(null); | ||||||
|  |             usbIoManager.stop(); | ||||||
|  |         } catch (Exception ignored) {} | ||||||
|  |         try { | ||||||
|  |             usbSerialPort.setDTR(false); | ||||||
|  |             usbSerialPort.setRTS(false); | ||||||
|  |             usbSerialPort.close(); | ||||||
|  |         } catch (Exception ignored) {} | ||||||
|  |         try { | ||||||
|  |             usbDeviceConnection.close(); | ||||||
|  |         } catch (Exception ignored) {} | ||||||
|  |         usbIoManager = null; | ||||||
|  |         usbSerialPort = null; | ||||||
|  |         usbDeviceConnection = null; | ||||||
|  |         usbSerialDriver = null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @AfterClass | ||||||
|  |     public static void tearDownFixture() throws Exception { | ||||||
|  |         try { | ||||||
|  |             telnetClient.disconnect(); | ||||||
|  |         } catch (Exception ignored) {} | ||||||
|  |         telnetReadStream = null; | ||||||
|  |         telnetWriteStream = null; | ||||||
|  |         telnetClient = null; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // wait full time | ||||||
|  |     private byte[] telnetRead() throws Exception { | ||||||
|  |         return telnetRead(-1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private byte[] telnetRead(int expectedLength) throws Exception { | ||||||
|  |         long end = System.currentTimeMillis() + TELNET_READ_WAIT; | ||||||
|  |         ByteBuffer buf = ByteBuffer.allocate(4096); | ||||||
|  |         while(System.currentTimeMillis() < end) { | ||||||
|  |             if(telnetReadStream.available() > 0) { | ||||||
|  |                 buf.put((byte) telnetReadStream.read()); | ||||||
|  |             } else { | ||||||
|  |                 if (expectedLength >= 0 && buf.position() >= expectedLength) | ||||||
|  |                     break; | ||||||
|  |                 Thread.sleep(1); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         byte[] data = new byte[buf.position()]; | ||||||
|  |         buf.flip(); | ||||||
|  |         buf.get(data); | ||||||
|  |         return data; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void telnetWrite(byte[] data) throws Exception{ | ||||||
|  |         if(telnetWriteDelay != 0) { | ||||||
|  |             for(byte b : data) { | ||||||
|  |                 telnetWriteStream.write(b); | ||||||
|  |                 telnetWriteStream.flush(); | ||||||
|  |                 Thread.sleep(telnetWriteDelay); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             telnetWriteStream.write(data); | ||||||
|  |             telnetWriteStream.flush(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // wait full time | ||||||
|  |     private byte[] usbRead() throws Exception { | ||||||
|  |         return usbRead(-1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private byte[] usbRead(int expectedLength) throws Exception { | ||||||
|  |         long end = System.currentTimeMillis() + USB_READ_WAIT; | ||||||
|  |         ByteBuffer buf = ByteBuffer.allocate(4096); | ||||||
|  |         if(usbIoManager != null) { | ||||||
|  |             while (System.currentTimeMillis() < end) { | ||||||
|  |                 synchronized (usbReadBuffer) { | ||||||
|  |                     while(usbReadBuffer.peek() != null) | ||||||
|  |                         buf.put(usbReadBuffer.remove()); | ||||||
|  |                 } | ||||||
|  |                 if (expectedLength >= 0 && buf.position() >= expectedLength) | ||||||
|  |                     break; | ||||||
|  |                 Thread.sleep(1); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |         } else { | ||||||
|  |             byte[] b1 = new byte[256]; | ||||||
|  |             while (System.currentTimeMillis() < end) { | ||||||
|  |                 int len = usbSerialPort.read(b1, USB_READ_WAIT / 10); | ||||||
|  |                 if (len > 0) { | ||||||
|  |                     buf.put(b1, 0, len); | ||||||
|  |                 } else { | ||||||
|  |                     if (expectedLength >= 0 && buf.position() >= expectedLength) | ||||||
|  |                         break; | ||||||
|  |                     Thread.sleep(1); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         byte[] data = new byte[buf.position()]; | ||||||
|  |         buf.flip(); | ||||||
|  |         buf.get(data); | ||||||
|  |         return data; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void usbWrite(byte[] data) throws IOException { | ||||||
|  |         usbSerialPort.write(data, USB_WRITE_WAIT); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void usbParameters(int baudRate, int dataBits, int stopBits, int parity) throws IOException, InterruptedException { | ||||||
|  |         usbSerialPort.setParameters(baudRate, dataBits, stopBits, parity); | ||||||
|  |         if(usbSerialDriver instanceof CdcAcmSerialDriver) | ||||||
|  |             Thread.sleep(10); // arduino_leonardeo_bridge.ini needs some time | ||||||
|  |         else | ||||||
|  |             Thread.sleep(1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private void telnetParameters(int baudRate, int dataBits, int stopBits, int parity) throws IOException, InterruptedException, InvalidTelnetOptionException { | ||||||
|  |         telnetComPortOptionCounter[0] = 0; | ||||||
|  | 
 | ||||||
|  |         telnetClient.sendCommand((byte)TelnetCommand.SB); | ||||||
|  |         telnetWriteStream.write(new byte[] {RFC2217_COM_PORT_OPTION, RFC2217_SET_BAUDRATE, (byte)(baudRate>>24), (byte)(baudRate>>16), (byte)(baudRate>>8), (byte)baudRate}); | ||||||
|  |         telnetClient.sendCommand((byte)TelnetCommand.SE); | ||||||
|  | 
 | ||||||
|  |         telnetClient.sendCommand((byte)TelnetCommand.SB); | ||||||
|  |         telnetWriteStream.write(new byte[] {RFC2217_COM_PORT_OPTION, RFC2217_SET_DATASIZE, (byte)dataBits}); | ||||||
|  |         telnetClient.sendCommand((byte)TelnetCommand.SE); | ||||||
|  | 
 | ||||||
|  |         telnetClient.sendCommand((byte)TelnetCommand.SB); | ||||||
|  |         telnetWriteStream.write(new byte[] {RFC2217_COM_PORT_OPTION, RFC2217_SET_STOPSIZE, (byte)stopBits}); | ||||||
|  |         telnetClient.sendCommand((byte)TelnetCommand.SE); | ||||||
|  | 
 | ||||||
|  |         telnetClient.sendCommand((byte)TelnetCommand.SB); | ||||||
|  |         telnetWriteStream.write(new byte[] {RFC2217_COM_PORT_OPTION, RFC2217_SET_PARITY, (byte)(parity+1)}); | ||||||
|  |         telnetClient.sendCommand((byte)TelnetCommand.SE); | ||||||
|  | 
 | ||||||
|  |         // windows does not like nonstandard baudrates. rfc2217_server.py terminates w/o response | ||||||
|  |         for(int i=0; i<2000; i++) { | ||||||
|  |             if(telnetComPortOptionCounter[0] == 4) break; | ||||||
|  |             Thread.sleep(1); | ||||||
|  |         } | ||||||
|  |         assertEquals("telnet connection lost", 4, telnetComPortOptionCounter[0].intValue()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public void onNewData(byte[] data) { | ||||||
|  |         while(usbReadBlock) | ||||||
|  |             try { | ||||||
|  |                 Thread.sleep(1); | ||||||
|  |             } catch (InterruptedException e) { | ||||||
|  |                 e.printStackTrace(); | ||||||
|  |             } | ||||||
|  |         synchronized (usbReadBuffer) { | ||||||
|  |             usbReadBuffer.add(data); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public void onRunError(Exception e) { | ||||||
|  |         assertTrue("usb connection lost", false); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void baudRate() throws Exception { | ||||||
|  |         byte[] data; | ||||||
|  | 
 | ||||||
|  |         if (false) { // default baud rate | ||||||
|  |             // CP2102: only works if first connection after attaching device | ||||||
|  |             // PL2303, FTDI: it's not 9600 | ||||||
|  |             telnetParameters(9600, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  | 
 | ||||||
|  |             telnetWrite("net2usb".getBytes()); | ||||||
|  |             data = usbRead(7); | ||||||
|  |             assertThat(data, equalTo("net2usb".getBytes())); // includes array content in output | ||||||
|  |             //assertArrayEquals("net2usb".getBytes(), data); // only includes array length in output | ||||||
|  |             usbWrite("usb2net".getBytes()); | ||||||
|  |             data = telnetRead(7); | ||||||
|  |             assertThat(data, equalTo("usb2net".getBytes())); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // invalid values | ||||||
|  |         try { | ||||||
|  |             usbParameters(-1, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |             if (usbSerialDriver instanceof Ch34xSerialDriver) | ||||||
|  |                 ; // todo: add range check in driver | ||||||
|  |             else if (usbSerialDriver instanceof FtdiSerialDriver) | ||||||
|  |                 ; // todo: add range check in driver | ||||||
|  |             else if (usbSerialDriver instanceof ProlificSerialDriver) | ||||||
|  |                 ; // todo: add range check in driver | ||||||
|  |             else if (usbSerialDriver instanceof Cp21xxSerialDriver) | ||||||
|  |                 ; // todo: add range check in driver | ||||||
|  |             else if (usbSerialDriver instanceof CdcAcmSerialDriver) | ||||||
|  |                 ; // todo: add range check in driver | ||||||
|  |             else | ||||||
|  |                 fail("invalid baudrate 0"); | ||||||
|  |         } catch (java.lang.IllegalArgumentException e) { | ||||||
|  |         } | ||||||
|  |         try { | ||||||
|  |             usbParameters(0, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |             if (usbSerialDriver instanceof ProlificSerialDriver) | ||||||
|  |                 ; // todo: add range check in driver | ||||||
|  |             else if (usbSerialDriver instanceof Cp21xxSerialDriver) | ||||||
|  |                 ; // todo: add range check in driver | ||||||
|  |             else if (usbSerialDriver instanceof CdcAcmSerialDriver) | ||||||
|  |                 ; // todo: add range check in driver | ||||||
|  |             else | ||||||
|  |                 fail("invalid baudrate 0"); | ||||||
|  |         } catch (java.lang.ArithmeticException e) { // ch340 | ||||||
|  |         } catch (java.lang.IllegalArgumentException e) { | ||||||
|  |         } | ||||||
|  |         try { | ||||||
|  |             usbParameters(1, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |             if (usbSerialDriver instanceof FtdiSerialDriver) | ||||||
|  |                 ; | ||||||
|  |             else if (usbSerialDriver instanceof ProlificSerialDriver) | ||||||
|  |                 ; | ||||||
|  |             else if (usbSerialDriver instanceof Cp21xxSerialDriver) | ||||||
|  |                 ; | ||||||
|  |             else if (usbSerialDriver instanceof CdcAcmSerialDriver) | ||||||
|  |                 ; | ||||||
|  |             else | ||||||
|  |                 fail("invalid baudrate 0"); | ||||||
|  |         } catch (java.io.IOException e) { // ch340 | ||||||
|  |         } catch (java.lang.IllegalArgumentException e) { | ||||||
|  |         } | ||||||
|  |         try { | ||||||
|  |             usbParameters(2<<31, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |             if (usbSerialDriver instanceof ProlificSerialDriver) | ||||||
|  |                 ; | ||||||
|  |             else if (usbSerialDriver instanceof Cp21xxSerialDriver) | ||||||
|  |                 ; | ||||||
|  |             else if (usbSerialDriver instanceof CdcAcmSerialDriver) | ||||||
|  |                 ; | ||||||
|  |             else | ||||||
|  |                 fail("invalid baudrate 2^31"); | ||||||
|  |         } catch (java.lang.ArithmeticException e) { // ch340 | ||||||
|  |         } catch (java.lang.IllegalArgumentException e) { | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         for(int baudRate : new int[] {2400, 19200, 42000, 115200} ) { | ||||||
|  |             if(baudRate == 42000 && !rfc2217_server_nonstandard_baudrates) | ||||||
|  |                 continue; // rfc2217_server.py would terminate | ||||||
|  |             telnetParameters(baudRate, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |             usbParameters(baudRate, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  | 
 | ||||||
|  |             telnetWrite("net2usb".getBytes()); | ||||||
|  |             data = usbRead(7); | ||||||
|  |             assertThat(String.valueOf(baudRate)+"/8N1", data, equalTo("net2usb".getBytes())); | ||||||
|  |             usbWrite("usb2net".getBytes()); | ||||||
|  |             data = telnetRead(7); | ||||||
|  |             assertThat(String.valueOf(baudRate)+"/8N1", data, equalTo("usb2net".getBytes())); | ||||||
|  |         } | ||||||
|  |         { // non matching baud rate | ||||||
|  |             telnetParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |             usbParameters(2400, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  | 
 | ||||||
|  |             telnetWrite("net2usb".getBytes()); | ||||||
|  |             data = usbRead(); | ||||||
|  |             assertNotEquals(7, data.length); | ||||||
|  |             usbWrite("usb2net".getBytes()); | ||||||
|  |             data = telnetRead(); | ||||||
|  |             assertNotEquals(7, data.length); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void dataBits() throws Exception { | ||||||
|  |         byte[] data; | ||||||
|  | 
 | ||||||
|  |         for(int i: new int[] {0, 4, 9}) { | ||||||
|  |             try { | ||||||
|  |                 usbParameters(19200, i, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |                 if (usbSerialDriver instanceof ProlificSerialDriver) | ||||||
|  |                     ; // todo: add range check in driver | ||||||
|  |                 else if (usbSerialDriver instanceof CdcAcmSerialDriver) | ||||||
|  |                     ; // todo: add range check in driver | ||||||
|  |                 else | ||||||
|  |                     fail("invalid databits "+i); | ||||||
|  |             } catch (java.lang.IllegalArgumentException e) { | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // telnet -> usb | ||||||
|  |         usbParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |         telnetParameters(19200, 7, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |         telnetWrite(new byte[] {0x00}); | ||||||
|  |         Thread.sleep(1); // one bit is 0.05 milliseconds long, wait >> stop bit | ||||||
|  |         telnetWrite(new byte[] {(byte)0xff}); | ||||||
|  |         data = usbRead(2); | ||||||
|  |         assertThat("19200/7N1", data, equalTo(new byte[] {(byte)0x80, (byte)0xff})); | ||||||
|  | 
 | ||||||
|  |         telnetParameters(19200, 6, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |         telnetWrite(new byte[] {0x00}); | ||||||
|  |         Thread.sleep(1); | ||||||
|  |         telnetWrite(new byte[] {(byte)0xff}); | ||||||
|  |         data = usbRead(2); | ||||||
|  |         assertThat("19000/6N1", data, equalTo(new byte[] {(byte)0xc0, (byte)0xff})); | ||||||
|  | 
 | ||||||
|  |         telnetParameters(19200, 5, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |         telnetWrite(new byte[] {0x00}); | ||||||
|  |         Thread.sleep(1); | ||||||
|  |         telnetWrite(new byte[] {(byte)0xff}); | ||||||
|  |         data = usbRead(2); | ||||||
|  |         assertThat("19000/5N1", data, equalTo(new byte[] {(byte)0xe0, (byte)0xff})); | ||||||
|  | 
 | ||||||
|  |         // usb -> telnet | ||||||
|  |         telnetParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |         usbParameters(19200, 7, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |         usbWrite(new byte[] {0x00}); | ||||||
|  |         Thread.sleep(1); | ||||||
|  |         usbWrite(new byte[] {(byte)0xff}); | ||||||
|  |         data = telnetRead(2); | ||||||
|  |         assertThat("19000/7N1", data, equalTo(new byte[] {(byte)0x80, (byte)0xff})); | ||||||
|  | 
 | ||||||
|  |         try { | ||||||
|  |             usbParameters(19200, 6, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |             usbWrite(new byte[]{0x00}); | ||||||
|  |             Thread.sleep(1); | ||||||
|  |             usbWrite(new byte[]{(byte) 0xff}); | ||||||
|  |             data = telnetRead(2); | ||||||
|  |             assertThat("19000/6N1", data, equalTo(new byte[]{(byte) 0xc0, (byte) 0xff})); | ||||||
|  |         } catch (java.lang.IllegalArgumentException e) { | ||||||
|  |             if (!(usbSerialDriver instanceof FtdiSerialDriver)) | ||||||
|  |                 throw e; | ||||||
|  |         } | ||||||
|  |         try { | ||||||
|  |             usbParameters(19200, 5, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |             usbWrite(new byte[] {0x00}); | ||||||
|  |             Thread.sleep(1); | ||||||
|  |             usbWrite(new byte[] {(byte)0xff}); | ||||||
|  |             data = telnetRead(2); | ||||||
|  |             assertThat("19000/5N1", data, equalTo(new byte[] {(byte)0xe0, (byte)0xff})); | ||||||
|  |         } catch (java.lang.IllegalArgumentException e) { | ||||||
|  |             if (!(usbSerialDriver instanceof FtdiSerialDriver)) | ||||||
|  |                 throw e; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void parity() throws Exception { | ||||||
|  |         byte[] _8n1 = {(byte)0x00, (byte)0x01, (byte)0xfe, (byte)0xff}; | ||||||
|  |         byte[] _7n1 = {(byte)0x00, (byte)0x01, (byte)0x7e, (byte)0x7f}; | ||||||
|  |         byte[] _7o1 = {(byte)0x80, (byte)0x01, (byte)0xfe, (byte)0x7f}; | ||||||
|  |         byte[] _7e1 = {(byte)0x00, (byte)0x81, (byte)0x7e, (byte)0xff}; | ||||||
|  |         byte[] _7m1 = {(byte)0x80, (byte)0x81, (byte)0xfe, (byte)0xff}; | ||||||
|  |         byte[] _7s1 = {(byte)0x00, (byte)0x01, (byte)0x7e, (byte)0x7f}; | ||||||
|  |         byte[] data; | ||||||
|  | 
 | ||||||
|  |         for(int i: new int[] {-1, 5}) { | ||||||
|  |             try { | ||||||
|  |                 usbParameters(19200, 8, 1, i); | ||||||
|  |                 fail("invalid parity "+i); | ||||||
|  |             } catch (java.lang.IllegalArgumentException e) { | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // usb -> telnet | ||||||
|  |         telnetParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |         usbParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |         usbWrite(_8n1); | ||||||
|  |         data = telnetRead(4); | ||||||
|  |         assertThat("19200/8N1", data, equalTo(_8n1)); | ||||||
|  | 
 | ||||||
|  |         usbParameters(19200, 7, 1, UsbSerialPort.PARITY_ODD); | ||||||
|  |         usbWrite(_8n1); | ||||||
|  |         data = telnetRead(4); | ||||||
|  |         assertThat("19200/7O1", data, equalTo(_7o1)); | ||||||
|  | 
 | ||||||
|  |         usbParameters(19200, 7, 1, UsbSerialPort.PARITY_EVEN); | ||||||
|  |         usbWrite(_8n1); | ||||||
|  |         data = telnetRead(4); | ||||||
|  |         assertThat("19200/7E1", data, equalTo(_7e1)); | ||||||
|  | 
 | ||||||
|  |         if (usbSerialDriver instanceof CdcAcmSerialDriver) { | ||||||
|  |             // not supported by arduino_leonardo_bridge.ino, other devices might support it | ||||||
|  |         } else { | ||||||
|  |             usbParameters(19200, 7, 1, UsbSerialPort.PARITY_MARK); | ||||||
|  |             usbWrite(_8n1); | ||||||
|  |             data = telnetRead(4); | ||||||
|  |             assertThat("19200/7M1", data, equalTo(_7m1)); | ||||||
|  | 
 | ||||||
|  |             usbParameters(19200, 7, 1, UsbSerialPort.PARITY_SPACE); | ||||||
|  |             usbWrite(_8n1); | ||||||
|  |             data = telnetRead(4); | ||||||
|  |             assertThat("19200/7S1", data, equalTo(_7s1)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // telnet -> usb | ||||||
|  |         usbParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |         telnetParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |         telnetWrite(_8n1); | ||||||
|  |         data = usbRead(4); | ||||||
|  |         assertThat("19200/8N1", data, equalTo(_8n1)); | ||||||
|  | 
 | ||||||
|  |         telnetParameters(19200, 7, 1, UsbSerialPort.PARITY_ODD); | ||||||
|  |         telnetWrite(_8n1); | ||||||
|  |         data = usbRead(4); | ||||||
|  |         assertThat("19200/7O1", data, equalTo(_7o1)); | ||||||
|  | 
 | ||||||
|  |         telnetParameters(19200, 7, 1, UsbSerialPort.PARITY_EVEN); | ||||||
|  |         telnetWrite(_8n1); | ||||||
|  |         data = usbRead(4); | ||||||
|  |         assertThat("19200/7E1", data, equalTo(_7e1)); | ||||||
|  | 
 | ||||||
|  |         if (usbSerialDriver instanceof CdcAcmSerialDriver) { | ||||||
|  |             // not supported by arduino_leonardo_bridge.ino, other devices might support it | ||||||
|  |         } else { | ||||||
|  |             telnetParameters(19200, 7, 1, UsbSerialPort.PARITY_MARK); | ||||||
|  |             telnetWrite(_8n1); | ||||||
|  |             data = usbRead(4); | ||||||
|  |             assertThat("19200/7M1", data, equalTo(_7m1)); | ||||||
|  | 
 | ||||||
|  |             telnetParameters(19200, 7, 1, UsbSerialPort.PARITY_SPACE); | ||||||
|  |             telnetWrite(_8n1); | ||||||
|  |             data = usbRead(4); | ||||||
|  |             assertThat("19200/7S1", data, equalTo(_7s1)); | ||||||
|  | 
 | ||||||
|  |             usbParameters(19200, 7, 1, UsbSerialPort.PARITY_ODD); | ||||||
|  |             telnetParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |             telnetWrite(_8n1); | ||||||
|  |             data = usbRead(4); | ||||||
|  |             assertThat("19200/8N1", data, equalTo(_7n1)); // read is resilient against errors | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void stopBits() throws Exception { | ||||||
|  |         byte[] data; | ||||||
|  | 
 | ||||||
|  |         for (int i : new int[]{0, 4}) { | ||||||
|  |             try { | ||||||
|  |                 usbParameters(19200, 8, i, UsbSerialPort.PARITY_NONE); | ||||||
|  |                 fail("invalid stopbits " + i); | ||||||
|  |             } catch (java.lang.IllegalArgumentException e) { | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (usbSerialDriver instanceof CdcAcmSerialDriver) { | ||||||
|  |             // software based bridge in arduino_leonardo_bridge.ino is to slow, other devices might support it | ||||||
|  |         } else { | ||||||
|  |             // shift stopbits into next byte, by using different databits | ||||||
|  |             // a - start bit (0) | ||||||
|  |             // o - stop bit  (1) | ||||||
|  |             // d - data bit | ||||||
|  | 
 | ||||||
|  |             // out 8N2:   addddddd doadddddddoo | ||||||
|  |             //             1000001 0  1 | ||||||
|  |             // in 6N1:    addddddo adddddo | ||||||
|  |             //             100000   101 | ||||||
|  |             usbParameters(19200, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE); | ||||||
|  |             telnetParameters(19200, 6, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |             usbWrite(new byte[]{65, 1}); | ||||||
|  |             data = telnetRead(2); | ||||||
|  |             assertThat("19200/8N1", data, equalTo(new byte[]{1, 5})); | ||||||
|  | 
 | ||||||
|  |             // out 8N2:   addddddd dooadddddddoo | ||||||
|  |             //             1000001 0   1 | ||||||
|  |             // in 6N1:    addddddo addddddo | ||||||
|  |             //             100000   1101 | ||||||
|  |             usbParameters(19200, 8, UsbSerialPort.STOPBITS_2, UsbSerialPort.PARITY_NONE); | ||||||
|  |             telnetParameters(19200, 6, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |             usbWrite(new byte[]{65, 1}); | ||||||
|  |             data = telnetRead(2); | ||||||
|  |             assertThat("19200/8N1", data, equalTo(new byte[]{1, 11})); | ||||||
|  |             // todo: could create similar test for 1.5 stopbits, by reading at double speed | ||||||
|  |             //       but only some devices support 1.5 stopbits and it is basically not used any more | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     public void probeTable() throws Exception { | ||||||
|  |         class DummyDriver implements UsbSerialDriver { | ||||||
|  |             @Override | ||||||
|  |             public UsbDevice getDevice() { return null; } | ||||||
|  |             @Override | ||||||
|  |             public List<UsbSerialPort> getPorts() { return null; } | ||||||
|  |         } | ||||||
|  |         List<UsbSerialDriver> availableDrivers; | ||||||
|  |         ProbeTable probeTable = new ProbeTable(); | ||||||
|  |         UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); | ||||||
|  |         availableDrivers = new UsbSerialProber(probeTable).findAllDrivers(usbManager); | ||||||
|  |         assertEquals(0, availableDrivers.size()); | ||||||
|  | 
 | ||||||
|  |         probeTable.addProduct(0, 0, DummyDriver.class); | ||||||
|  |         availableDrivers = new UsbSerialProber(probeTable).findAllDrivers(usbManager); | ||||||
|  |         assertEquals(0, availableDrivers.size()); | ||||||
|  | 
 | ||||||
|  |         probeTable.addProduct(usbSerialDriver.getDevice().getVendorId(), usbSerialDriver.getDevice().getProductId(), usbSerialDriver.getClass()); | ||||||
|  |         availableDrivers = new UsbSerialProber(probeTable).findAllDrivers(usbManager); | ||||||
|  |         assertEquals(1, availableDrivers.size()); | ||||||
|  |         assertEquals(true, availableDrivers.get(0).getClass() == usbSerialDriver.getClass()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     // data loss es expected, if data is not consumed fast enough | ||||||
|  |     public void readBuffer() throws Exception { | ||||||
|  |         if(usbSerialDriver instanceof CdcAcmSerialDriver) | ||||||
|  |             telnetWriteDelay = 10; // arduino_leonardo_bridge.ino sends each byte in own USB packet, which is horribly slow | ||||||
|  |         usbParameters(115200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |         telnetParameters(115200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  | 
 | ||||||
|  |         StringBuilder expected = new StringBuilder(); | ||||||
|  |         StringBuilder data = new StringBuilder(); | ||||||
|  |         final int maxWait = 2000; | ||||||
|  |         int bufferSize = 0; | ||||||
|  |         for(bufferSize = 8; bufferSize < (2<<15); bufferSize *= 2) { | ||||||
|  |             int linenr = 0; | ||||||
|  |             String line; | ||||||
|  |             expected.setLength(0); | ||||||
|  |             data.setLength(0); | ||||||
|  | 
 | ||||||
|  |             Log.i(TAG, "bufferSize " + bufferSize); | ||||||
|  |             usbReadBlock = true; | ||||||
|  |             for (linenr = 0; linenr < bufferSize/8; linenr++) { | ||||||
|  |                 line = String.format("%06d\r\n", linenr); | ||||||
|  |                 telnetWrite(line.getBytes()); | ||||||
|  |                 expected.append(line); | ||||||
|  |             } | ||||||
|  |             usbReadBlock = false; | ||||||
|  | 
 | ||||||
|  |             // slowly write new data, until old data is comletely read from buffer and new data is received again | ||||||
|  |             boolean found = false; | ||||||
|  |             for (; linenr < bufferSize/8 + maxWait/10 && !found; linenr++) { | ||||||
|  |                 line = String.format("%06d\r\n", linenr); | ||||||
|  |                 telnetWrite(line.getBytes()); | ||||||
|  |                 Thread.sleep(10); | ||||||
|  |                 expected.append(line); | ||||||
|  |                 data.append(new String(usbRead(0))); | ||||||
|  |                 found = data.toString().endsWith(line); | ||||||
|  |             } | ||||||
|  |             if(!found) { | ||||||
|  |                 // use waiting read to clear input queue, else next test would see unexpected data | ||||||
|  |                 byte[] rest = null; | ||||||
|  |                 while(rest==null || rest.length>0) | ||||||
|  |                     rest = usbRead(-1); | ||||||
|  |                 fail("end not found"); | ||||||
|  |             } | ||||||
|  |             if (data.length() != expected.length()) | ||||||
|  |                 break; | ||||||
|  |         } | ||||||
|  |         int pos = StringUtils.indexOfDifference(data, expected); | ||||||
|  |         Log.i(TAG, "bufferSize " + bufferSize + ", first difference at " + pos); | ||||||
|  |         // actual values have large variance for same device, e.g. | ||||||
|  |         //  bufferSize 4096, first difference at 164 | ||||||
|  |         //  bufferSize 64, first difference at 57 | ||||||
|  |         assertTrue(bufferSize > 16); | ||||||
|  |         assertTrue(data.length() != expected.length()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     // see logcat for performance results | ||||||
|  |     public void readSpeed() throws Exception { | ||||||
|  |         // CDC arduino_leonardo_bridge.ini has transfer speed ~ 100 byte/sec | ||||||
|  |         // all other devices are near physical limit with ~ 10-12k/sec | ||||||
|  | 
 | ||||||
|  |         // CH340 w/o asyncReads (bulkTransfer) is much slower and fails reproducibly here | ||||||
|  |         // FTDI w/o asyncReads (bulkTransfer) does not continue to read after ~2k | ||||||
|  |         // CP2102 and PL2303 do not have data loss issues with bulkTransfer | ||||||
|  |         usbParameters(115200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |         telnetParameters(115200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  | 
 | ||||||
|  |         // limited write ahead to avoid buffer overrun | ||||||
|  |         // with unlimited write ahead all devices fail sporadically. is it windows/device/usb-buffer overrun? | ||||||
|  |         int writeAhead = 2000; | ||||||
|  |         if(usbSerialDriver instanceof CdcAcmSerialDriver) | ||||||
|  |             writeAhead = 50; | ||||||
|  | 
 | ||||||
|  |         int linenr = 0; | ||||||
|  |         String line=""; | ||||||
|  |         StringBuilder data = new StringBuilder(); | ||||||
|  |         StringBuilder expected = new StringBuilder(); | ||||||
|  |         int dlen = 0, elen = 0; | ||||||
|  |         Log.i(TAG, "readSpeed: 'in' should be near "+115200/10); | ||||||
|  |         long begin = System.currentTimeMillis(); | ||||||
|  |         long next = System.currentTimeMillis(); | ||||||
|  |         for(int seconds=1; seconds<=5; seconds++) { | ||||||
|  |             next += 1000; | ||||||
|  |             while (System.currentTimeMillis() < next) { | ||||||
|  |                 if(expected.length() < data.length() + writeAhead) { | ||||||
|  |                     line = String.format("%06d\r\n", linenr++); | ||||||
|  |                     telnetWrite(line.getBytes()); | ||||||
|  |                     expected.append(line); | ||||||
|  |                 } else { | ||||||
|  |                     Thread.sleep(0, 100000); | ||||||
|  |                 } | ||||||
|  |                 data.append(new String(usbRead(0))); | ||||||
|  |             } | ||||||
|  |             Log.i(TAG, "readSpeed: t="+(next-begin)+", in="+(data.length()-dlen)+", out="+(expected.length()-elen)); | ||||||
|  |             dlen = data.length(); | ||||||
|  |             elen = expected.length(); | ||||||
|  |         } | ||||||
|  |         boolean found = false; | ||||||
|  |         for (linenr=0; linenr < 2000 && !found; linenr++) { | ||||||
|  |             data.append(new String(usbRead(0))); | ||||||
|  |             Thread.sleep(1); | ||||||
|  |             found = data.toString().endsWith(line); | ||||||
|  |         } | ||||||
|  |         next = System.currentTimeMillis(); | ||||||
|  |         //Log.i(TAG, "readSpeed: t="+(next-begin)+", in="+(data.length()-dlen)); | ||||||
|  |         assertTrue(found); | ||||||
|  |         int pos = StringUtils.indexOfDifference(data, expected); | ||||||
|  |         if(pos!=-1) { | ||||||
|  |             Log.i(TAG, "readSpeed: first difference at " + pos); | ||||||
|  |             String datasub     =     data.substring(Math.max(pos - 20, 0), Math.min(pos + 20, data.length())); | ||||||
|  |             String expectedsub = expected.substring(Math.max(pos - 20, 0), Math.min(pos + 20, expected.length())); | ||||||
|  |             assertThat(datasub, equalTo(expectedsub)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Test | ||||||
|  |     // see logcat for performance results | ||||||
|  |     public void writeSpeed() throws Exception { | ||||||
|  |         // CDC arduino_leonardo_bridge.ini has transfer speed ~ 100 byte/sec | ||||||
|  |         // all other devices can get near physical limit: | ||||||
|  |         // longlines=true:, speed is near physical limit at 11.5k | ||||||
|  |         // longlines=false: speed is 3-4k for all devices, as more USB packets are required | ||||||
|  |         usbParameters(115200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |         telnetParameters(115200, 8, 1, UsbSerialPort.PARITY_NONE); | ||||||
|  |         boolean longlines = !(usbSerialDriver instanceof CdcAcmSerialDriver); | ||||||
|  | 
 | ||||||
|  |         int linenr = 0; | ||||||
|  |         String line=""; | ||||||
|  |         StringBuilder data = new StringBuilder(); | ||||||
|  |         StringBuilder expected = new StringBuilder(); | ||||||
|  |         int dlen = 0, elen = 0; | ||||||
|  |         Log.i(TAG, "writeSpeed: 'out' should be near "+115200/10); | ||||||
|  |         long begin = System.currentTimeMillis(); | ||||||
|  |         long next = System.currentTimeMillis(); | ||||||
|  |         for(int seconds=1; seconds<=5; seconds++) { | ||||||
|  |             next += 1000; | ||||||
|  |             while (System.currentTimeMillis() < next) { | ||||||
|  |                 if(longlines) | ||||||
|  |                     line = String.format("%060d\r\n", linenr++); | ||||||
|  |                 else | ||||||
|  |                     line = String.format("%06d\r\n", linenr++); | ||||||
|  |                 usbWrite(line.getBytes()); | ||||||
|  |                 expected.append(line); | ||||||
|  |                 data.append(new String(telnetRead(0))); | ||||||
|  |             } | ||||||
|  |             Log.i(TAG, "writeSpeed: t="+(next-begin)+", out="+(expected.length()-elen)+", in="+(data.length()-dlen)); | ||||||
|  |             dlen = data.length(); | ||||||
|  |             elen = expected.length(); | ||||||
|  |         } | ||||||
|  |         boolean found = false; | ||||||
|  |         for (linenr=0; linenr < 2000 && !found; linenr++) { | ||||||
|  |             data.append(new String(telnetRead(0))); | ||||||
|  |             Thread.sleep(1); | ||||||
|  |             found = data.toString().endsWith(line); | ||||||
|  |         } | ||||||
|  |         next = System.currentTimeMillis(); | ||||||
|  |         Log.i(TAG, "writeSpeed: t="+(next-begin)+", in="+(data.length()-dlen)); | ||||||
|  |         assertTrue(found); | ||||||
|  |         int pos = StringUtils.indexOfDifference(data, expected); | ||||||
|  |         if(pos!=-1) { | ||||||
|  |             Log.i(TAG, "writeSpeed: first difference at " + pos); | ||||||
|  |             String datasub     =     data.substring(Math.max(pos - 20, 0), Math.min(pos + 20, data.length())); | ||||||
|  |             String expectedsub = expected.substring(Math.max(pos - 20, 0), Math.min(pos + 20, expected.length())); | ||||||
|  |             assertThat(datasub, equalTo(expectedsub)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user