mirror of
https://github.com/mik3y/usb-serial-for-android
synced 2025-06-07 16:06:10 +00:00
Merge pull request #212 from kai-morich/multiport
support ft_232h, cp210_ multiport devices
This commit is contained in:
commit
f54dd65624
4
.idea/.gitignore
generated
vendored
Normal file
4
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
caches
|
||||||
|
codeStyles
|
||||||
|
libraries
|
||||||
|
workspace.xml
|
1
.idea/.name
generated
1
.idea/.name
generated
@ -1 +0,0 @@
|
|||||||
usb-serial-for-android
|
|
21
.idea/compiler.xml
generated
21
.idea/compiler.xml
generated
@ -1,21 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="CompilerConfiguration">
|
|
||||||
<resourceExtensions />
|
|
||||||
<wildcardResourcePatterns>
|
|
||||||
<entry name="!?*.java" />
|
|
||||||
<entry name="!?*.form" />
|
|
||||||
<entry name="!?*.class" />
|
|
||||||
<entry name="!?*.groovy" />
|
|
||||||
<entry name="!?*.scala" />
|
|
||||||
<entry name="!?*.flex" />
|
|
||||||
<entry name="!?*.kt" />
|
|
||||||
<entry name="!?*.clj" />
|
|
||||||
</wildcardResourcePatterns>
|
|
||||||
<annotationProcessing>
|
|
||||||
<profile default="true" name="Default" enabled="false">
|
|
||||||
<processorPath useClasspath="true" />
|
|
||||||
</profile>
|
|
||||||
</annotationProcessing>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
3
.idea/copyright/profiles_settings.xml
generated
3
.idea/copyright/profiles_settings.xml
generated
@ -1,3 +0,0 @@
|
|||||||
<component name="CopyrightManager">
|
|
||||||
<settings default="" />
|
|
||||||
</component>
|
|
54
.idea/misc.xml
generated
54
.idea/misc.xml
generated
@ -1,38 +1,34 @@
|
|||||||
<?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="5">
|
||||||
|
<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="javax.annotation.CheckForNull" />
|
||||||
|
<item index="3" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
|
||||||
|
<item index="4" 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_8" 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">
|
||||||
<option name="id" value="Android" />
|
<option name="id" value="Android" />
|
||||||
</component>
|
</component>
|
||||||
<component name="masterDetails">
|
|
||||||
<states>
|
|
||||||
<state key="ProjectJDKs.UI">
|
|
||||||
<settings>
|
|
||||||
<last-edited>1.8</last-edited>
|
|
||||||
<splitter-proportions>
|
|
||||||
<option name="proportions">
|
|
||||||
<list>
|
|
||||||
<option value="0.2" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
</splitter-proportions>
|
|
||||||
</settings>
|
|
||||||
</state>
|
|
||||||
</states>
|
|
||||||
</component>
|
|
||||||
</project>
|
</project>
|
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.1.2'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
12
gradle/wrapper/gradle-wrapper.properties
vendored
12
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
|||||||
#Tue Jun 23 00:11:28 EDT 2015
|
#Tue Mar 27 21:28:01 CEST 2018
|
||||||
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.4-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 27
|
||||||
buildToolsVersion "22.0.1"
|
buildToolsVersion '27.0.3'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 14
|
minSdkVersion 14
|
||||||
targetSdkVersion 22
|
targetSdkVersion 27
|
||||||
|
|
||||||
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 27
|
||||||
buildToolsVersion "19.1"
|
buildToolsVersion '27.0.3'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 12
|
minSdkVersion 14
|
||||||
targetSdkVersion 19
|
targetSdkVersion 27
|
||||||
|
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.1'
|
||||||
|
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
||||||
|
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,972 @@
|
|||||||
|
/*
|
||||||
|
* 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 android.os.Process;
|
||||||
|
|
||||||
|
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.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 {
|
||||||
|
|
||||||
|
// configuration:
|
||||||
|
private final static String rfc2217_server_host = "192.168.0.100";
|
||||||
|
private final static int rfc2217_server_port = 2217;
|
||||||
|
private final static boolean rfc2217_server_nonstandard_baudrates = false; // false on Windows, Raspi
|
||||||
|
private final static boolean rfc2217_server_parity_mark_space = false; // false on Raspi
|
||||||
|
private final static int test_device_port = 0;
|
||||||
|
|
||||||
|
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 Integer SERIAL_INPUT_OUTPUT_MANAGER_THREAD_PRIORITY = Process.THREAD_PRIORITY_URGENT_AUDIO;
|
||||||
|
|
||||||
|
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 long usbReadTime = 0;
|
||||||
|
|
||||||
|
private static TelnetClient telnetClient;
|
||||||
|
private static InputStream telnetReadStream;
|
||||||
|
private static OutputStream telnetWriteStream;
|
||||||
|
private static Integer[] telnetComPortOptionCounter = {0};
|
||||||
|
private int telnetWriteDelay = 0;
|
||||||
|
private boolean isCp21xxRestrictedPort = false; // second port of Cp2105 has limited dataBits, stopBits, parity
|
||||||
|
|
||||||
|
@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 correctly 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);
|
||||||
|
assertTrue( usbSerialDriver.getPorts().size() > test_device_port);
|
||||||
|
usbSerialPort = usbSerialDriver.getPorts().get(test_device_port);
|
||||||
|
Log.i(TAG, "Using USB device "+ usbSerialDriver.getClass().getSimpleName());
|
||||||
|
isCp21xxRestrictedPort = usbSerialDriver instanceof Cp21xxSerialDriver && usbSerialDriver.getPorts().size()==2 && test_device_port == 1;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if(SERIAL_INPUT_OUTPUT_MANAGER_THREAD_PRIORITY != null)
|
||||||
|
Process.setThreadPriority(SERIAL_INPUT_OUTPUT_MANAGER_THREAD_PRIORITY);
|
||||||
|
super.run();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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(8192);
|
||||||
|
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) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
if(usbReadTime == 0)
|
||||||
|
usbReadTime = now;
|
||||||
|
if(data.length > 64) {
|
||||||
|
Log.d(TAG, "usb read: time+=" + String.format("%-3d",now-usbReadTime) + " len=" + String.format("%-4d",data.length) + " data=" + new String(data, 0, 32) + "..." + new String(data, data.length-32, 32));
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "usb read: time+=" + String.format("%-3d",now-usbReadTime) + " len=" + String.format("%-4d",data.length) + " data=" + new String(data));
|
||||||
|
}
|
||||||
|
usbReadTime = now;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clone of org.apache.commons.lang3.StringUtils.indexOfDifference + optional startpos
|
||||||
|
private static int indexOfDifference(final CharSequence cs1, final CharSequence cs2) {
|
||||||
|
return indexOfDifference(cs1, cs2, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int indexOfDifference(final CharSequence cs1, final CharSequence cs2, int cs1startpos, int cs2startpos) {
|
||||||
|
if (cs1 == cs2) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (cs1 == null || cs2 == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(cs1startpos < 0 || cs2startpos < 0)
|
||||||
|
return -1;
|
||||||
|
int i, j;
|
||||||
|
for (i = cs1startpos, j = cs2startpos; i < cs1.length() && j < cs2.length(); ++i, ++j) {
|
||||||
|
if (cs1.charAt(i) != cs2.charAt(j)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (j < cs2.length() || i < cs1.length()) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@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.io.IOException e) { // cp2105 second port
|
||||||
|
} 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.io.IOException e) { // cp2105 second port
|
||||||
|
} 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.io.IOException e) { // cp2105 second port
|
||||||
|
} catch (java.lang.IllegalArgumentException e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int baudRate : new int[] {300, 2400, 19200, 42000, 115200} ) {
|
||||||
|
if(baudRate == 42000 && !rfc2217_server_nonstandard_baudrates)
|
||||||
|
continue; // rfc2217_server.py would terminate
|
||||||
|
if(baudRate == 300 && isCp21xxRestrictedPort) {
|
||||||
|
try {
|
||||||
|
usbParameters(baudRate, 8, 1, UsbSerialPort.PARITY_NONE);
|
||||||
|
assertTrue(false);
|
||||||
|
} catch (java.io.IOException e) {
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
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
|
||||||
|
try {
|
||||||
|
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}));
|
||||||
|
} catch (java.lang.IllegalArgumentException e) {
|
||||||
|
if(!isCp21xxRestrictedPort)
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
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 (!(isCp21xxRestrictedPort || 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 (!(isCp21xxRestrictedPort || 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) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(isCp21xxRestrictedPort) {
|
||||||
|
usbParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
|
||||||
|
usbParameters(19200, 8, 1, UsbSerialPort.PARITY_EVEN);
|
||||||
|
usbParameters(19200, 8, 1, UsbSerialPort.PARITY_ODD);
|
||||||
|
try {
|
||||||
|
usbParameters(19200, 8, 1, UsbSerialPort.PARITY_MARK);
|
||||||
|
usbParameters(19200, 8, 1, UsbSerialPort.PARITY_SPACE);
|
||||||
|
} catch (java.lang.IllegalArgumentException e) {
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
// test below not possible as it requires unsupported 7 dataBits
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 if (rfc2217_server_parity_mark_space) {
|
||||||
|
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 {
|
||||||
|
if (rfc2217_server_parity_mark_space) {
|
||||||
|
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 doaddddddddo
|
||||||
|
// 1000001 0 10001111
|
||||||
|
// in 6N1: addddddo addddddo
|
||||||
|
// 100000 101000
|
||||||
|
usbParameters(19200, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE);
|
||||||
|
telnetParameters(19200, 6, 1, UsbSerialPort.PARITY_NONE);
|
||||||
|
usbWrite(new byte[]{(byte)0x41, (byte)0xf1});
|
||||||
|
data = telnetRead(2);
|
||||||
|
assertThat("19200/8N1", data, equalTo(new byte[]{1, 5}));
|
||||||
|
|
||||||
|
// out 8N2: addddddd dooaddddddddoo
|
||||||
|
// 1000001 0 10011111
|
||||||
|
// in 6N1: addddddo addddddo
|
||||||
|
// 100000 110100
|
||||||
|
try {
|
||||||
|
usbParameters(19200, 8, UsbSerialPort.STOPBITS_2, UsbSerialPort.PARITY_NONE);
|
||||||
|
telnetParameters(19200, 6, 1, UsbSerialPort.PARITY_NONE);
|
||||||
|
usbWrite(new byte[]{(byte) 0x41, (byte) 0xf9});
|
||||||
|
data = telnetRead(2);
|
||||||
|
assertThat("19200/8N1", data, equalTo(new byte[]{1, 11}));
|
||||||
|
} catch(java.lang.IllegalArgumentException e) {
|
||||||
|
if(!isCp21xxRestrictedPort)
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
// 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("%07d,", 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("%07d,", 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 = 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// this test can fail sporadically!
|
||||||
|
//
|
||||||
|
// Android is not a real time OS, so there is no guarantee that the usb thread is scheduled, or it might be blocked by Java garbage collection.
|
||||||
|
// The SerialInputOutputManager uses a buffer size of 4Kb. Reading of these blocks happen behind UsbRequest.queue / UsbDeviceConnection.requestWait
|
||||||
|
// The dump of data and error positions in logcat show, that data is lost somewhere in the UsbRequest handling,
|
||||||
|
// very likely when the individual 64 byte USB packets are not read fast enough, and the serial converter chip has to discard bytes.
|
||||||
|
//
|
||||||
|
// On some days SERIAL_INPUT_OUTPUT_MANAGER_THREAD_PRIORITY=THREAD_PRIORITY_URGENT_AUDIO reduced errors by factor 10, on other days it had no effect at all!
|
||||||
|
//
|
||||||
|
@Test
|
||||||
|
public void readSpeed() throws Exception {
|
||||||
|
// see logcat for performance results
|
||||||
|
//
|
||||||
|
// CDC arduino_leonardo_bridge.ini has transfer speed ~ 100 byte/sec
|
||||||
|
// all other devices are near physical limit with ~ 10-12k/sec
|
||||||
|
int baudrate = 115200;
|
||||||
|
usbParameters(baudrate, 8, 1, UsbSerialPort.PARITY_NONE);
|
||||||
|
telnetParameters(baudrate, 8, 1, UsbSerialPort.PARITY_NONE);
|
||||||
|
|
||||||
|
// fails more likely with larger or unlimited (-1) write ahead
|
||||||
|
int writeSeconds = 5;
|
||||||
|
int writeAhead = 5*baudrate/10; // write ahead for another 5 second read
|
||||||
|
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: 'read' should be near "+baudrate/10);
|
||||||
|
long begin = System.currentTimeMillis();
|
||||||
|
long next = System.currentTimeMillis();
|
||||||
|
for(int seconds=1; seconds <= writeSeconds; seconds++) {
|
||||||
|
next += 1000;
|
||||||
|
while (System.currentTimeMillis() < next) {
|
||||||
|
if((writeAhead < 0) || (expected.length() < data.length() + writeAhead)) {
|
||||||
|
line = String.format("%07d,", 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)+", read="+(data.length()-dlen)+", write="+(expected.length()-elen));
|
||||||
|
dlen = data.length();
|
||||||
|
elen = expected.length();
|
||||||
|
}
|
||||||
|
boolean found = false;
|
||||||
|
long maxwait = Math.max(1000, (expected.length() - data.length()) * 20000L / baudrate );
|
||||||
|
next = System.currentTimeMillis() + maxwait;
|
||||||
|
Log.d(TAG, "readSpeed: rest wait time " + maxwait + " for " + (expected.length() - data.length()) + " byte");
|
||||||
|
while(!found && System.currentTimeMillis() < next) {
|
||||||
|
data.append(new String(usbRead(0)));
|
||||||
|
found = data.toString().endsWith(line);
|
||||||
|
Thread.sleep(1);
|
||||||
|
}
|
||||||
|
//next = System.currentTimeMillis();
|
||||||
|
//Log.i(TAG, "readSpeed: t="+(next-begin)+", read="+(data.length()-dlen));
|
||||||
|
|
||||||
|
int errcnt = 0;
|
||||||
|
int errlen = 0;
|
||||||
|
int datapos = indexOfDifference(data, expected);
|
||||||
|
int expectedpos = datapos;
|
||||||
|
while(datapos != -1) {
|
||||||
|
errcnt += 1;
|
||||||
|
int nextexpectedpos = -1;
|
||||||
|
int nextdatapos = datapos + 2;
|
||||||
|
int len = -1;
|
||||||
|
if(nextdatapos + 10 < data.length()) { // try to sync data+expected, assuming that data is lost, but not corrupted
|
||||||
|
String nextsub = data.substring(nextdatapos, nextdatapos + 10);
|
||||||
|
nextexpectedpos = expected.indexOf(nextsub, expectedpos);
|
||||||
|
if(nextexpectedpos >= 0) {
|
||||||
|
len = nextexpectedpos - expectedpos - 2;
|
||||||
|
errlen += len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.i(TAG, "readSpeed: difference at " + datapos + " len " + len );
|
||||||
|
Log.d(TAG, "readSpeed: got " + data.substring(Math.max(datapos - 20, 0), Math.min(datapos + 20, data.length())));
|
||||||
|
Log.d(TAG, "readSpeed: expected " + expected.substring(Math.max(expectedpos - 20, 0), Math.min(expectedpos + 20, expected.length())));
|
||||||
|
datapos = indexOfDifference(data, expected, nextdatapos, nextexpectedpos);
|
||||||
|
expectedpos = nextexpectedpos + (datapos - nextdatapos);
|
||||||
|
}
|
||||||
|
if(errcnt != 0)
|
||||||
|
Log.i(TAG, "readSpeed: got " + errcnt + " errors, total len " + errlen+ ", avg. len " + errlen/errcnt);
|
||||||
|
assertTrue("end not found", found);
|
||||||
|
assertEquals("no errors", 0, errcnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void writeSpeed() throws Exception {
|
||||||
|
// see logcat for performance results
|
||||||
|
//
|
||||||
|
// 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: 'write' 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,", linenr++);
|
||||||
|
else
|
||||||
|
line = String.format("%07d,", linenr++);
|
||||||
|
usbWrite(line.getBytes());
|
||||||
|
expected.append(line);
|
||||||
|
data.append(new String(telnetRead(0)));
|
||||||
|
}
|
||||||
|
Log.i(TAG, "writeSpeed: t="+(next-begin)+", write="+(expected.length()-elen)+", read="+(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)+", read="+(data.length()-dlen));
|
||||||
|
assertTrue(found);
|
||||||
|
int pos = 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -84,9 +84,10 @@ public class Ch34xSerialDriver implements UsbSerialDriver {
|
|||||||
private boolean dtr = false;
|
private boolean dtr = false;
|
||||||
private boolean rts = false;
|
private boolean rts = false;
|
||||||
|
|
||||||
private final boolean mEnableAsyncReads;
|
private final Boolean mEnableAsyncReads;
|
||||||
private UsbEndpoint mReadEndpoint;
|
private UsbEndpoint mReadEndpoint;
|
||||||
private UsbEndpoint mWriteEndpoint;
|
private UsbEndpoint mWriteEndpoint;
|
||||||
|
private UsbRequest mUsbRequest;
|
||||||
|
|
||||||
public Ch340SerialPort(UsbDevice device, int portNumber) {
|
public Ch340SerialPort(UsbDevice device, int portNumber) {
|
||||||
super(device, portNumber);
|
super(device, portNumber);
|
||||||
@ -109,10 +110,8 @@ public class Ch34xSerialDriver implements UsbSerialDriver {
|
|||||||
try {
|
try {
|
||||||
for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
|
for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
|
||||||
UsbInterface usbIface = mDevice.getInterface(i);
|
UsbInterface usbIface = mDevice.getInterface(i);
|
||||||
if (mConnection.claimInterface(usbIface, true)) {
|
if (!mConnection.claimInterface(usbIface, true)) {
|
||||||
Log.d(TAG, "claimInterface " + i + " SUCCESS");
|
throw new IOException("Could not claim data interface.");
|
||||||
} else {
|
|
||||||
Log.d(TAG, "claimInterface " + i + " FAIL");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,7 +153,10 @@ public class Ch34xSerialDriver implements UsbSerialDriver {
|
|||||||
if (mConnection == null) {
|
if (mConnection == null) {
|
||||||
throw new IOException("Already closed");
|
throw new IOException("Already closed");
|
||||||
}
|
}
|
||||||
|
synchronized (mEnableAsyncReads) {
|
||||||
|
if (mUsbRequest != null)
|
||||||
|
mUsbRequest.cancel();
|
||||||
|
}
|
||||||
// TODO: nothing sended on close, maybe needed?
|
// TODO: nothing sended on close, maybe needed?
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -175,8 +177,11 @@ public class Ch34xSerialDriver implements UsbSerialDriver {
|
|||||||
if (!request.queue(buf, dest.length)) {
|
if (!request.queue(buf, dest.length)) {
|
||||||
throw new IOException("Error queueing request.");
|
throw new IOException("Error queueing request.");
|
||||||
}
|
}
|
||||||
|
mUsbRequest = request;
|
||||||
final UsbRequest response = mConnection.requestWait();
|
final UsbRequest response = mConnection.requestWait();
|
||||||
|
synchronized (mEnableAsyncReads) {
|
||||||
|
mUsbRequest = null;
|
||||||
|
}
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
throw new IOException("Null response");
|
throw new IOException("Null response");
|
||||||
}
|
}
|
||||||
@ -189,25 +194,26 @@ public class Ch34xSerialDriver implements UsbSerialDriver {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
mUsbRequest = null;
|
||||||
request.close();
|
request.close();
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
final int numBytesRead;
|
||||||
final int numBytesRead;
|
synchronized (mReadBufferLock) {
|
||||||
synchronized (mReadBufferLock) {
|
int readAmt = Math.min(dest.length, mReadBuffer.length);
|
||||||
int readAmt = Math.min(dest.length, mReadBuffer.length);
|
numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer, readAmt,
|
||||||
numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer, readAmt,
|
timeoutMillis);
|
||||||
timeoutMillis);
|
if (numBytesRead < 0) {
|
||||||
if (numBytesRead < 0) {
|
// This sucks: we get -1 on timeout, not 0 as preferred.
|
||||||
// This sucks: we get -1 on timeout, not 0 as preferred.
|
// We *should* use UsbRequest, except it has a bug/api oversight
|
||||||
// We *should* use UsbRequest, except it has a bug/api oversight
|
// where there is no way to determine the number of bytes read
|
||||||
// where there is no way to determine the number of bytes read
|
// in response :\ -- http://b.android.com/28023
|
||||||
// in response :\ -- http://b.android.com/28023
|
return 0;
|
||||||
return 0;
|
}
|
||||||
|
System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
|
||||||
}
|
}
|
||||||
System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
|
return numBytesRead;
|
||||||
}
|
}
|
||||||
return numBytesRead;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -26,10 +26,13 @@ import android.hardware.usb.UsbDevice;
|
|||||||
import android.hardware.usb.UsbDeviceConnection;
|
import android.hardware.usb.UsbDeviceConnection;
|
||||||
import android.hardware.usb.UsbEndpoint;
|
import android.hardware.usb.UsbEndpoint;
|
||||||
import android.hardware.usb.UsbInterface;
|
import android.hardware.usb.UsbInterface;
|
||||||
|
import android.hardware.usb.UsbRequest;
|
||||||
|
import android.os.Build;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -39,11 +42,14 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
|
|||||||
private static final String TAG = Cp21xxSerialDriver.class.getSimpleName();
|
private static final String TAG = Cp21xxSerialDriver.class.getSimpleName();
|
||||||
|
|
||||||
private final UsbDevice mDevice;
|
private final UsbDevice mDevice;
|
||||||
private final UsbSerialPort mPort;
|
private final List<UsbSerialPort> mPorts;
|
||||||
|
|
||||||
public Cp21xxSerialDriver(UsbDevice device) {
|
public Cp21xxSerialDriver(UsbDevice device) {
|
||||||
mDevice = device;
|
mDevice = device;
|
||||||
mPort = new Cp21xxSerialPort(mDevice, 0);
|
mPorts = new ArrayList<>();
|
||||||
|
for( int port = 0; port < device.getInterfaceCount(); port++) {
|
||||||
|
mPorts.add(new Cp21xxSerialPort(mDevice, port));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -53,7 +59,7 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<UsbSerialPort> getPorts() {
|
public List<UsbSerialPort> getPorts() {
|
||||||
return Collections.singletonList(mPort);
|
return mPorts;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Cp21xxSerialPort extends CommonUsbSerialPort {
|
public class Cp21xxSerialPort extends CommonUsbSerialPort {
|
||||||
@ -101,11 +107,18 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
|
|||||||
private static final int CONTROL_WRITE_DTR = 0x0100;
|
private static final int CONTROL_WRITE_DTR = 0x0100;
|
||||||
private static final int CONTROL_WRITE_RTS = 0x0200;
|
private static final int CONTROL_WRITE_RTS = 0x0200;
|
||||||
|
|
||||||
|
private final Boolean mEnableAsyncReads;
|
||||||
private UsbEndpoint mReadEndpoint;
|
private UsbEndpoint mReadEndpoint;
|
||||||
private UsbEndpoint mWriteEndpoint;
|
private UsbEndpoint mWriteEndpoint;
|
||||||
|
private UsbRequest mUsbRequest;
|
||||||
|
|
||||||
|
// second port of Cp2105 has limited baudRate, dataBits, stopBits, parity
|
||||||
|
// unsupported baudrate returns error at controlTransfer(), other parameters are silently ignored
|
||||||
|
private boolean mIsRestrictedPort;
|
||||||
|
|
||||||
public Cp21xxSerialPort(UsbDevice device, int portNumber) {
|
public Cp21xxSerialPort(UsbDevice device, int portNumber) {
|
||||||
super(device, portNumber);
|
super(device, portNumber);
|
||||||
|
mEnableAsyncReads = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -115,7 +128,7 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
|
|||||||
|
|
||||||
private int setConfigSingle(int request, int value) {
|
private int setConfigSingle(int request, int value) {
|
||||||
return mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, request, value,
|
return mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, request, value,
|
||||||
0, null, 0, USB_WRITE_TIMEOUT_MILLIS);
|
mPortNumber, null, 0, USB_WRITE_TIMEOUT_MILLIS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -126,17 +139,15 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
|
|||||||
|
|
||||||
mConnection = connection;
|
mConnection = connection;
|
||||||
boolean opened = false;
|
boolean opened = false;
|
||||||
|
mIsRestrictedPort = mDevice.getInterfaceCount() == 2 && mPortNumber == 1;
|
||||||
try {
|
try {
|
||||||
for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
|
if(mPortNumber >= mDevice.getInterfaceCount()) {
|
||||||
UsbInterface usbIface = mDevice.getInterface(i);
|
throw new IOException("Unknown port number");
|
||||||
if (mConnection.claimInterface(usbIface, true)) {
|
}
|
||||||
Log.d(TAG, "claimInterface " + i + " SUCCESS");
|
UsbInterface dataIface = mDevice.getInterface(mPortNumber);
|
||||||
} else {
|
if (!mConnection.claimInterface(dataIface, true)) {
|
||||||
Log.d(TAG, "claimInterface " + i + " FAIL");
|
throw new IOException("Could not claim interface " + mPortNumber);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UsbInterface dataIface = mDevice.getInterface(mDevice.getInterfaceCount() - 1);
|
|
||||||
for (int i = 0; i < dataIface.getEndpointCount(); i++) {
|
for (int i = 0; i < dataIface.getEndpointCount(); i++) {
|
||||||
UsbEndpoint ep = dataIface.getEndpoint(i);
|
UsbEndpoint ep = dataIface.getEndpoint(i);
|
||||||
if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
|
if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
|
||||||
@ -169,6 +180,11 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
|
|||||||
if (mConnection == null) {
|
if (mConnection == null) {
|
||||||
throw new IOException("Already closed");
|
throw new IOException("Already closed");
|
||||||
}
|
}
|
||||||
|
synchronized (mEnableAsyncReads) {
|
||||||
|
if(mUsbRequest != null) {
|
||||||
|
mUsbRequest.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
setConfigSingle(SILABSER_IFC_ENABLE_REQUEST_CODE, UART_DISABLE);
|
setConfigSingle(SILABSER_IFC_ENABLE_REQUEST_CODE, UART_DISABLE);
|
||||||
mConnection.close();
|
mConnection.close();
|
||||||
@ -179,21 +195,51 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read(byte[] dest, int timeoutMillis) throws IOException {
|
public int read(byte[] dest, int timeoutMillis) throws IOException {
|
||||||
final int numBytesRead;
|
if (mEnableAsyncReads) {
|
||||||
synchronized (mReadBufferLock) {
|
final UsbRequest request = new UsbRequest();
|
||||||
int readAmt = Math.min(dest.length, mReadBuffer.length);
|
try {
|
||||||
numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer, readAmt,
|
request.initialize(mConnection, mReadEndpoint);
|
||||||
timeoutMillis);
|
final ByteBuffer buf = ByteBuffer.wrap(dest);
|
||||||
if (numBytesRead < 0) {
|
if (!request.queue(buf, dest.length)) {
|
||||||
// This sucks: we get -1 on timeout, not 0 as preferred.
|
throw new IOException("Error queueing request.");
|
||||||
// We *should* use UsbRequest, except it has a bug/api oversight
|
}
|
||||||
// where there is no way to determine the number of bytes read
|
mUsbRequest = request;
|
||||||
// in response :\ -- http://b.android.com/28023
|
final UsbRequest response = mConnection.requestWait();
|
||||||
return 0;
|
synchronized (mEnableAsyncReads) {
|
||||||
|
mUsbRequest = null;
|
||||||
|
}
|
||||||
|
if (response == null) {
|
||||||
|
throw new IOException("Null response");
|
||||||
|
}
|
||||||
|
|
||||||
|
final int nread = buf.position();
|
||||||
|
if (nread > 0) {
|
||||||
|
//Log.d(TAG, HexDump.dumpHexString(dest, 0, Math.min(32, dest.length)));
|
||||||
|
return nread;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
mUsbRequest = null;
|
||||||
|
request.close();
|
||||||
}
|
}
|
||||||
System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
|
} else {
|
||||||
|
final int numBytesRead;
|
||||||
|
synchronized (mReadBufferLock) {
|
||||||
|
int readAmt = Math.min(dest.length, mReadBuffer.length);
|
||||||
|
numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer, readAmt,
|
||||||
|
timeoutMillis);
|
||||||
|
if (numBytesRead < 0) {
|
||||||
|
// This sucks: we get -1 on timeout, not 0 as preferred.
|
||||||
|
// We *should* use UsbRequest, except it has a bug/api oversight
|
||||||
|
// where there is no way to determine the number of bytes read
|
||||||
|
// in response :\ -- http://b.android.com/28023
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
|
||||||
|
}
|
||||||
|
return numBytesRead;
|
||||||
}
|
}
|
||||||
return numBytesRead;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -238,7 +284,7 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
|
|||||||
(byte) ((baudRate >> 24) & 0xff)
|
(byte) ((baudRate >> 24) & 0xff)
|
||||||
};
|
};
|
||||||
int ret = mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, SILABSER_SET_BAUDRATE,
|
int ret = mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, SILABSER_SET_BAUDRATE,
|
||||||
0, 0, data, 4, USB_WRITE_TIMEOUT_MILLIS);
|
0, mPortNumber, data, 4, USB_WRITE_TIMEOUT_MILLIS);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
throw new IOException("Error setting baud rate.");
|
throw new IOException("Error setting baud rate.");
|
||||||
}
|
}
|
||||||
@ -252,12 +298,18 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
|
|||||||
int configDataBits = 0;
|
int configDataBits = 0;
|
||||||
switch (dataBits) {
|
switch (dataBits) {
|
||||||
case DATABITS_5:
|
case DATABITS_5:
|
||||||
|
if(mIsRestrictedPort)
|
||||||
|
throw new IllegalArgumentException("Unsupported dataBits value: " + dataBits);
|
||||||
configDataBits |= 0x0500;
|
configDataBits |= 0x0500;
|
||||||
break;
|
break;
|
||||||
case DATABITS_6:
|
case DATABITS_6:
|
||||||
|
if(mIsRestrictedPort)
|
||||||
|
throw new IllegalArgumentException("Unsupported dataBits value: " + dataBits);
|
||||||
configDataBits |= 0x0600;
|
configDataBits |= 0x0600;
|
||||||
break;
|
break;
|
||||||
case DATABITS_7:
|
case DATABITS_7:
|
||||||
|
if(mIsRestrictedPort)
|
||||||
|
throw new IllegalArgumentException("Unsupported dataBits value: " + dataBits);
|
||||||
configDataBits |= 0x0700;
|
configDataBits |= 0x0700;
|
||||||
break;
|
break;
|
||||||
case DATABITS_8:
|
case DATABITS_8:
|
||||||
@ -277,9 +329,13 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
|
|||||||
configDataBits |= 0x0020;
|
configDataBits |= 0x0020;
|
||||||
break;
|
break;
|
||||||
case PARITY_MARK:
|
case PARITY_MARK:
|
||||||
|
if(mIsRestrictedPort)
|
||||||
|
throw new IllegalArgumentException("Unsupported parity value: mark");
|
||||||
configDataBits |= 0x0030;
|
configDataBits |= 0x0030;
|
||||||
break;
|
break;
|
||||||
case PARITY_SPACE:
|
case PARITY_SPACE:
|
||||||
|
if(mIsRestrictedPort)
|
||||||
|
throw new IllegalArgumentException("Unsupported parity value: space");
|
||||||
configDataBits |= 0x0040;
|
configDataBits |= 0x0040;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -292,6 +348,8 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
|
|||||||
case STOPBITS_1_5:
|
case STOPBITS_1_5:
|
||||||
throw new IllegalArgumentException("Unsupported stopBits value: 1.5");
|
throw new IllegalArgumentException("Unsupported stopBits value: 1.5");
|
||||||
case STOPBITS_2:
|
case STOPBITS_2:
|
||||||
|
if(mIsRestrictedPort)
|
||||||
|
throw new IllegalArgumentException("Unsupported stopBits value: 2");
|
||||||
configDataBits |= 2;
|
configDataBits |= 2;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -29,11 +29,9 @@ import android.hardware.usb.UsbRequest;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.hoho.android.usbserial.util.HexDump;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Collections;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -73,6 +71,8 @@ import java.util.Map;
|
|||||||
* Supported and tested devices:
|
* Supported and tested devices:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@value DeviceType#TYPE_R}</li>
|
* <li>{@value DeviceType#TYPE_R}</li>
|
||||||
|
* <li>{@value DeviceType#TYPE_2232H}</li>
|
||||||
|
* <li>{@value DeviceType#TYPE_4232H}</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
@ -80,8 +80,6 @@ import java.util.Map;
|
|||||||
* feedback or patches):
|
* feedback or patches):
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@value DeviceType#TYPE_2232C}</li>
|
* <li>{@value DeviceType#TYPE_2232C}</li>
|
||||||
* <li>{@value DeviceType#TYPE_2232H}</li>
|
|
||||||
* <li>{@value DeviceType#TYPE_4232H}</li>
|
|
||||||
* <li>{@value DeviceType#TYPE_AM}</li>
|
* <li>{@value DeviceType#TYPE_AM}</li>
|
||||||
* <li>{@value DeviceType#TYPE_BM}</li>
|
* <li>{@value DeviceType#TYPE_BM}</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
@ -96,7 +94,7 @@ import java.util.Map;
|
|||||||
public class FtdiSerialDriver implements UsbSerialDriver {
|
public class FtdiSerialDriver implements UsbSerialDriver {
|
||||||
|
|
||||||
private final UsbDevice mDevice;
|
private final UsbDevice mDevice;
|
||||||
private final UsbSerialPort mPort;
|
private final List<UsbSerialPort> mPorts;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FTDI chip types.
|
* FTDI chip types.
|
||||||
@ -107,8 +105,12 @@ public class FtdiSerialDriver implements UsbSerialDriver {
|
|||||||
|
|
||||||
public FtdiSerialDriver(UsbDevice device) {
|
public FtdiSerialDriver(UsbDevice device) {
|
||||||
mDevice = device;
|
mDevice = device;
|
||||||
mPort = new FtdiSerialPort(mDevice, 0);
|
mPorts = new ArrayList<>();
|
||||||
|
for( int port = 0; port < device.getInterfaceCount(); port++) {
|
||||||
|
mPorts.add(new FtdiSerialPort(mDevice, port));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UsbDevice getDevice() {
|
public UsbDevice getDevice() {
|
||||||
return mDevice;
|
return mDevice;
|
||||||
@ -116,7 +118,7 @@ public class FtdiSerialDriver implements UsbSerialDriver {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<UsbSerialPort> getPorts() {
|
public List<UsbSerialPort> getPorts() {
|
||||||
return Collections.singletonList(mPort);
|
return mPorts;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FtdiSerialPort extends CommonUsbSerialPort {
|
private class FtdiSerialPort extends CommonUsbSerialPort {
|
||||||
@ -182,9 +184,7 @@ public class FtdiSerialDriver implements UsbSerialDriver {
|
|||||||
|
|
||||||
private DeviceType mType;
|
private DeviceType mType;
|
||||||
|
|
||||||
private int mInterface = 0; /* INTERFACE_ANY */
|
private int mIndex = 0;
|
||||||
|
|
||||||
private int mMaxPacketSize = 64; // TODO(mikey): detect
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Due to http://b.android.com/28023 , we cannot use UsbRequest async reads
|
* Due to http://b.android.com/28023 , we cannot use UsbRequest async reads
|
||||||
@ -230,14 +230,21 @@ public class FtdiSerialDriver implements UsbSerialDriver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void reset() throws IOException {
|
public void reset() throws IOException {
|
||||||
|
// TODO(mikey): autodetect.
|
||||||
|
mType = DeviceType.TYPE_R;
|
||||||
|
if(mDevice.getInterfaceCount() > 1) {
|
||||||
|
mIndex = mPortNumber + 1;
|
||||||
|
if (mDevice.getInterfaceCount() == 2)
|
||||||
|
mType = DeviceType.TYPE_2232H;
|
||||||
|
if (mDevice.getInterfaceCount() == 4)
|
||||||
|
mType = DeviceType.TYPE_4232H;
|
||||||
|
}
|
||||||
|
|
||||||
int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE, SIO_RESET_REQUEST,
|
int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE, SIO_RESET_REQUEST,
|
||||||
SIO_RESET_SIO, 0 /* index */, null, 0, USB_WRITE_TIMEOUT_MILLIS);
|
SIO_RESET_SIO, mIndex, null, 0, USB_WRITE_TIMEOUT_MILLIS);
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
throw new IOException("Reset failed: result=" + result);
|
throw new IOException("Reset failed: result=" + result);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(mikey): autodetect.
|
|
||||||
mType = DeviceType.TYPE_R;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -249,12 +256,10 @@ public class FtdiSerialDriver implements UsbSerialDriver {
|
|||||||
|
|
||||||
boolean opened = false;
|
boolean opened = false;
|
||||||
try {
|
try {
|
||||||
for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
|
if (connection.claimInterface(mDevice.getInterface(mPortNumber), true)) {
|
||||||
if (connection.claimInterface(mDevice.getInterface(i), true)) {
|
Log.d(TAG, "claimInterface " + mPortNumber + " SUCCESS");
|
||||||
Log.d(TAG, "claimInterface " + i + " SUCCESS");
|
} else {
|
||||||
} else {
|
throw new IOException("Error claiming interface " + mPortNumber);
|
||||||
throw new IOException("Error claiming interface " + i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
reset();
|
reset();
|
||||||
opened = true;
|
opened = true;
|
||||||
@ -280,7 +285,7 @@ public class FtdiSerialDriver implements UsbSerialDriver {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read(byte[] dest, int timeoutMillis) throws IOException {
|
public int read(byte[] dest, int timeoutMillis) throws IOException {
|
||||||
final UsbEndpoint endpoint = mDevice.getInterface(0).getEndpoint(0);
|
final UsbEndpoint endpoint = mDevice.getInterface(mPortNumber).getEndpoint(0);
|
||||||
|
|
||||||
if (mEnableAsyncReads) {
|
if (mEnableAsyncReads) {
|
||||||
final UsbRequest request = new UsbRequest();
|
final UsbRequest request = new UsbRequest();
|
||||||
@ -325,7 +330,7 @@ public class FtdiSerialDriver implements UsbSerialDriver {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int write(byte[] src, int timeoutMillis) throws IOException {
|
public int write(byte[] src, int timeoutMillis) throws IOException {
|
||||||
final UsbEndpoint endpoint = mDevice.getInterface(0).getEndpoint(1);
|
final UsbEndpoint endpoint = mDevice.getInterface(mPortNumber).getEndpoint(1);
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
|
|
||||||
while (offset < src.length) {
|
while (offset < src.length) {
|
||||||
@ -425,7 +430,7 @@ public class FtdiSerialDriver implements UsbSerialDriver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE,
|
int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE,
|
||||||
SIO_SET_DATA_REQUEST, config, 0 /* index */,
|
SIO_SET_DATA_REQUEST, config, mIndex,
|
||||||
null, 0, USB_WRITE_TIMEOUT_MILLIS);
|
null, 0, USB_WRITE_TIMEOUT_MILLIS);
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
throw new IOException("Setting parameters failed: result=" + result);
|
throw new IOException("Setting parameters failed: result=" + result);
|
||||||
@ -505,9 +510,8 @@ public class FtdiSerialDriver implements UsbSerialDriver {
|
|||||||
long index;
|
long index;
|
||||||
if (mType == DeviceType.TYPE_2232C || mType == DeviceType.TYPE_2232H
|
if (mType == DeviceType.TYPE_2232C || mType == DeviceType.TYPE_2232H
|
||||||
|| mType == DeviceType.TYPE_4232H) {
|
|| mType == DeviceType.TYPE_4232H) {
|
||||||
index = (encodedDivisor >> 8) & 0xffff;
|
index = (encodedDivisor >> 8) & 0xff00;
|
||||||
index &= 0xFF00;
|
index |= mIndex;
|
||||||
index |= 0 /* TODO mIndex */;
|
|
||||||
} else {
|
} else {
|
||||||
index = (encodedDivisor >> 16) & 0xffff;
|
index = (encodedDivisor >> 16) & 0xffff;
|
||||||
}
|
}
|
||||||
@ -560,7 +564,7 @@ public class FtdiSerialDriver implements UsbSerialDriver {
|
|||||||
public boolean purgeHwBuffers(boolean purgeReadBuffers, boolean purgeWriteBuffers) throws IOException {
|
public boolean purgeHwBuffers(boolean purgeReadBuffers, boolean purgeWriteBuffers) throws IOException {
|
||||||
if (purgeReadBuffers) {
|
if (purgeReadBuffers) {
|
||||||
int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE, SIO_RESET_REQUEST,
|
int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE, SIO_RESET_REQUEST,
|
||||||
SIO_RESET_PURGE_RX, 0 /* index */, null, 0, USB_WRITE_TIMEOUT_MILLIS);
|
SIO_RESET_PURGE_RX, mIndex, null, 0, USB_WRITE_TIMEOUT_MILLIS);
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
throw new IOException("Flushing RX failed: result=" + result);
|
throw new IOException("Flushing RX failed: result=" + result);
|
||||||
}
|
}
|
||||||
@ -568,7 +572,7 @@ public class FtdiSerialDriver implements UsbSerialDriver {
|
|||||||
|
|
||||||
if (purgeWriteBuffers) {
|
if (purgeWriteBuffers) {
|
||||||
int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE, SIO_RESET_REQUEST,
|
int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE, SIO_RESET_REQUEST,
|
||||||
SIO_RESET_PURGE_TX, 0 /* index */, null, 0, USB_WRITE_TIMEOUT_MILLIS);
|
SIO_RESET_PURGE_TX, mIndex, null, 0, USB_WRITE_TIMEOUT_MILLIS);
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
throw new IOException("Flushing RX failed: result=" + result);
|
throw new IOException("Flushing RX failed: result=" + result);
|
||||||
}
|
}
|
||||||
@ -582,6 +586,9 @@ public class FtdiSerialDriver implements UsbSerialDriver {
|
|||||||
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_FTDI),
|
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_FTDI),
|
||||||
new int[] {
|
new int[] {
|
||||||
UsbId.FTDI_FT232R,
|
UsbId.FTDI_FT232R,
|
||||||
|
UsbId.FTDI_FT232H,
|
||||||
|
UsbId.FTDI_FT2232H,
|
||||||
|
UsbId.FTDI_FT4232H,
|
||||||
UsbId.FTDI_FT231X,
|
UsbId.FTDI_FT231X,
|
||||||
});
|
});
|
||||||
return supportedDevices;
|
return supportedDevices;
|
||||||
|
@ -32,10 +32,13 @@ import android.hardware.usb.UsbDevice;
|
|||||||
import android.hardware.usb.UsbDeviceConnection;
|
import android.hardware.usb.UsbDeviceConnection;
|
||||||
import android.hardware.usb.UsbEndpoint;
|
import android.hardware.usb.UsbEndpoint;
|
||||||
import android.hardware.usb.UsbInterface;
|
import android.hardware.usb.UsbInterface;
|
||||||
|
import android.hardware.usb.UsbRequest;
|
||||||
|
import android.os.Build;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -109,6 +112,7 @@ public class ProlificSerialDriver implements UsbSerialDriver {
|
|||||||
|
|
||||||
private int mDeviceType = DEVICE_TYPE_HX;
|
private int mDeviceType = DEVICE_TYPE_HX;
|
||||||
|
|
||||||
|
private final boolean mEnableAsyncReads;
|
||||||
private UsbEndpoint mReadEndpoint;
|
private UsbEndpoint mReadEndpoint;
|
||||||
private UsbEndpoint mWriteEndpoint;
|
private UsbEndpoint mWriteEndpoint;
|
||||||
private UsbEndpoint mInterruptEndpoint;
|
private UsbEndpoint mInterruptEndpoint;
|
||||||
@ -126,6 +130,7 @@ public class ProlificSerialDriver implements UsbSerialDriver {
|
|||||||
|
|
||||||
public ProlificSerialPort(UsbDevice device, int portNumber) {
|
public ProlificSerialPort(UsbDevice device, int portNumber) {
|
||||||
super(device, portNumber);
|
super(device, portNumber);
|
||||||
|
mEnableAsyncReads = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -368,15 +373,41 @@ public class ProlificSerialDriver implements UsbSerialDriver {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read(byte[] dest, int timeoutMillis) throws IOException {
|
public int read(byte[] dest, int timeoutMillis) throws IOException {
|
||||||
synchronized (mReadBufferLock) {
|
if (mEnableAsyncReads) {
|
||||||
int readAmt = Math.min(dest.length, mReadBuffer.length);
|
final UsbRequest request = new UsbRequest();
|
||||||
int numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer,
|
try {
|
||||||
readAmt, timeoutMillis);
|
request.initialize(mConnection, mReadEndpoint);
|
||||||
if (numBytesRead < 0) {
|
final ByteBuffer buf = ByteBuffer.wrap(dest);
|
||||||
return 0;
|
if (!request.queue(buf, dest.length)) {
|
||||||
|
throw new IOException("Error queueing request.");
|
||||||
|
}
|
||||||
|
|
||||||
|
final UsbRequest response = mConnection.requestWait();
|
||||||
|
if (response == null) {
|
||||||
|
throw new IOException("Null response");
|
||||||
|
}
|
||||||
|
|
||||||
|
final int nread = buf.position();
|
||||||
|
if (nread > 0) {
|
||||||
|
//Log.d(TAG, HexDump.dumpHexString(dest, 0, Math.min(32, dest.length)));
|
||||||
|
return nread;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
request.close();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
synchronized (mReadBufferLock) {
|
||||||
|
int readAmt = Math.min(dest.length, mReadBuffer.length);
|
||||||
|
int numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer,
|
||||||
|
readAmt, timeoutMillis);
|
||||||
|
if (numBytesRead < 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
|
||||||
|
return numBytesRead;
|
||||||
}
|
}
|
||||||
System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
|
|
||||||
return numBytesRead;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +33,9 @@ public final class UsbId {
|
|||||||
|
|
||||||
public static final int VENDOR_FTDI = 0x0403;
|
public static final int VENDOR_FTDI = 0x0403;
|
||||||
public static final int FTDI_FT232R = 0x6001;
|
public static final int FTDI_FT232R = 0x6001;
|
||||||
|
public static final int FTDI_FT2232H = 0x6010;
|
||||||
|
public static final int FTDI_FT4232H = 0x6011;
|
||||||
|
public static final int FTDI_FT232H = 0x6014;
|
||||||
public static final int FTDI_FT231X = 0x6015;
|
public static final int FTDI_FT231X = 0x6015;
|
||||||
|
|
||||||
public static final int VENDOR_ATMEL = 0x03EB;
|
public static final int VENDOR_ATMEL = 0x03EB;
|
||||||
|
@ -120,7 +120,6 @@ public class SerialInputOutputManager implements Runnable {
|
|||||||
* called, or until a driver exception is raised.
|
* called, or until a driver exception is raised.
|
||||||
*
|
*
|
||||||
* NOTE(mikey): Uses inefficient read/write-with-timeout.
|
* NOTE(mikey): Uses inefficient read/write-with-timeout.
|
||||||
* TODO(mikey): Read asynchronously with {@link UsbRequest#queue(ByteBuffer, int)}
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user