diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..4a443ac
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,4 @@
+caches
+codeStyles
+libraries
+workspace.xml
diff --git a/.idea/.name b/.idea/.name
deleted file mode 100644
index 88cdcce..0000000
--- a/.idea/.name
+++ /dev/null
@@ -1 +0,0 @@
-usb-serial-for-android
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
deleted file mode 100644
index 1f2af51..0000000
--- a/.idea/compiler.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
deleted file mode 100644
index e7bedf3..0000000
--- a/.idea/copyright/profiles_settings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index aaaf922..047238c 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,38 +1,48 @@
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
- 1.8
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 50a9551..4b2b32b 100644
--- a/README.md
+++ b/README.md
@@ -1,143 +1,162 @@
-# usb-serial-for-android
-
-This is a driver library for communication with Arduinos and other USB serial hardware on
-Android, using the
-[Android USB Host API](http://developer.android.com/guide/topics/connectivity/usb/host.html)
-available on Android 3.1+.
-
-No root access, ADK, or special kernel drivers are required; all drivers are implemented in
-Java. You get a raw serial port with `read()`, `write()`, and other basic
-functions for use with your own protocols.
-
-* **Homepage**: https://github.com/mik3y/usb-serial-for-android
-* **Google group**: http://groups.google.com/group/usb-serial-for-android
-* **Latest release**: [v0.1.0](https://github.com/mik3y/usb-serial-for-android/releases)
-
-## Quick Start
-
-**1.** [Link your project](https://github.com/mik3y/usb-serial-for-android/wiki/Building-From-Source) to the library.
-
-**2.** Copy [device_filter.xml](https://github.com/mik3y/usb-serial-for-android/blob/master/usbSerialExamples/src/main/res/xml/device_filter.xml) to your project's `res/xml/` directory.
-
-**3.** Configure your `AndroidManifest.xml` to notify your app when a device is attached (see [Android USB Host documentation](http://developer.android.com/guide/topics/connectivity/usb/host.html#discovering-d) for help).
-
-```xml
-
-
-
-
-
-
-```
-
-**4.** Use it! Example code snippet:
-
-```java
-// Find all available drivers from attached devices.
-UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
-List availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager);
-if (availableDrivers.isEmpty()) {
- return;
-}
-
-// Open a connection to the first available driver.
-UsbSerialDriver driver = availableDrivers.get(0);
-UsbDeviceConnection connection = manager.openDevice(driver.getDevice());
-if (connection == null) {
- // You probably need to call UsbManager.requestPermission(driver.getDevice(), ..)
- return;
-}
-
-// Read some data! Most have just one port (port 0).
-UsbSerialPort port = driver.getPorts().get(0);
-try {
- port.open(connection);
- port.setParameters(115200, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE);
-
- byte buffer[] = new byte[16];
- int numBytesRead = port.read(buffer, 1000);
- Log.d(TAG, "Read " + numBytesRead + " bytes.");
-} catch (IOException e) {
- // Deal with error.
-} finally {
- port.close();
-}
-```
-
-For a more complete example, see the
-[UsbSerialExamples project](https://github.com/mik3y/usb-serial-for-android/blob/master/usbSerialExamples)
-in git, which is a simple application for reading and showing serial data.
-
-A [simple Arduino application](https://github.com/mik3y/usb-serial-for-android/blob/master/arduino)
-is also available which can be used for testing.
-
-
-## Probing for Unrecognized Devices
-
-Sometimes you may need to do a little extra work to support devices which
-usb-serial-for-android doesn't [yet] know about -- but which you know to be
-compatible with one of the built-in drivers. This may be the case for a brand
-new device or for one using a custom VID/PID pair.
-
-UsbSerialProber is a class to help you find and instantiate compatible
-UsbSerialDrivers from the tree of connected UsbDevices. Normally, you will use
-the default prober returned by ``UsbSerialProber.getDefaultProber()``, which
-uses the built-in list of well-known VIDs and PIDs that are supported by our
-drivers.
-
-To use your own set of rules, create and use a custom prober:
-
-```java
-// Probe for our custom CDC devices, which use VID 0x1234
-// and PIDS 0x0001 and 0x0002.
-ProbeTable customTable = new ProbeTable();
-customTable.addProduct(0x1234, 0x0001, CdcAcmSerialDriver.class);
-customTable.addProduct(0x1234, 0x0002, CdcAcmSerialDriver.class);
-
-UsbSerialProber prober = new UsbSerialProber(customTable);
-List drivers = prober.findAllDrivers(usbManager);
-// ...
-```
-
-Of course, nothing requires you to use UsbSerialProber at all: you can
-instantiate driver classes directly if you know what you're doing; just supply
-a compatible UsbDevice.
-
-
-## Compatible Devices
-
-* *Serial chips:* FT232R, CDC/ACM (eg Arduino Uno) and possibly others.
- See [CompatibleSerialDevices](https://github.com/mik3y/usb-serial-for-android/wiki/Compatible-Serial-Devices).
-* *Android phones and tablets:* Nexus 7, Motorola Xoom, and many others.
- See [CompatibleAndroidDevices](https://github.com/mik3y/usb-serial-for-android/wiki/Compatible-Android-Devices).
-
-
-## Author, License, and Copyright
-
-usb-serial-for-android is written and maintained by *mike wakerly*.
-
-This library is licensed under *LGPL Version 2.1*. Please see LICENSE.txt for the
-complete license.
-
-Copyright 2011-2012, Google Inc. All Rights Reserved.
-
-Portions of this library are based on libftdi
-(http://www.intra2net.com/en/developer/libftdi). Please see
-FtdiSerialDriver.java for more information.
-
-## Help & Discussion
-
-For common problems, see the
-[Troubleshooting](https://github.com/mik3y/usb-serial-for-android/wiki/Troubleshooting)
-wiki page.
-
-For other help and discussion, please join our Google Group,
-[usb-serial-for-android](https://groups.google.com/forum/?fromgroups#!forum/usb-serial-for-android).
-
-Are you using the library? Let us know on the group and we'll add your project to
-[ProjectsUsingUsbSerialForAndroid](https://github.com/mik3y/usb-serial-for-android/wiki/Projects-Using-usb-serial-for-android).
-
+[](https://jitpack.io/#mik3y/usb-serial-for-android)
+[](https://www.codacy.com/manual/kai-morich/usb-serial-for-android-mik3y?utm_source=github.com&utm_medium=referral&utm_content=mik3y/usb-serial-for-android&utm_campaign=Badge_Grade)
+[](https://codecov.io/gh/mik3y/usb-serial-for-android)
+
+# usb-serial-for-android
+
+This is a driver library for communication with Arduinos and other USB serial hardware on
+Android, using the
+[Android USB Host Mode (OTG)](http://developer.android.com/guide/topics/connectivity/usb/host.html)
+available since Android 3.1 and working reliably since Android 4.2.
+
+No root access, ADK, or special kernel drivers are required; all drivers are implemented in
+Java. You get a raw serial port with `read()`, `write()`, and other basic
+functions for use with your own protocols.
+
+## Quick Start
+
+**1.** Add library to your project:
+
+Add jitpack.io repository to your root build.gradle:
+```gradle
+allprojects {
+ repositories {
+ ...
+ maven { url 'https://jitpack.io' }
+ }
+}
+```
+Add library to dependencies
+```gradle
+dependencies {
+ implementation 'com.github.mik3y:usb-serial-for-android:Tag'
+}
+```
+
+**2.** If the app should be notified when a device is attached, add
+[device_filter.xml](https://github.com/mik3y/usb-serial-for-android/blob/master/usbSerialExamples/src/main/res/xml/device_filter.xml)
+to your project's `res/xml/` directory and configure in your `AndroidManifest.xml`.
+
+```xml
+
+
+
+
+
+
+```
+
+**3.** Use it! Example code snippet:
+
+```java
+ // Find all available drivers from attached devices.
+ UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
+ List availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager);
+ if (availableDrivers.isEmpty()) {
+ return;
+ }
+
+ // Open a connection to the first available driver.
+ UsbSerialDriver driver = availableDrivers.get(0);
+ UsbDeviceConnection connection = manager.openDevice(driver.getDevice());
+ if (connection == null) {
+ // add UsbManager.requestPermission(driver.getDevice(), ..) handling here
+ return;
+ }
+
+ UsbSerialPort port = driver.getPorts().get(0); // Most devices have just one port (port 0)
+ port.open(connection);
+ port.setParameters(115200, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE);
+ usbIoManager = new SerialInputOutputManager(usbSerialPort, this);
+ Executors.newSingleThreadExecutor().submit(usbIoManager);
+```
+```java
+ port.write("hello".getBytes(), WRITE_WAIT_MILLIS);
+```
+```java
+@Override
+public void onNewData(byte[] data) {
+ runOnUiThread(() -> { textView.append(new String(data)); });
+}
+```
+```java
+ port.close();
+```
+
+
+For a simple example, see
+[UsbSerialExamples](https://github.com/mik3y/usb-serial-for-android/blob/master/usbSerialExamples)
+folder in this project.
+
+For a more complete example, see separate github project
+[SimpleUsbTerminal](https://github.com/kai-morich/SimpleUsbTerminal).
+
+## Probing for Unrecognized Devices
+
+Sometimes you may need to do a little extra work to support devices which
+usb-serial-for-android doesn't (yet) know about -- but which you know to be
+compatible with one of the built-in drivers. This may be the case for a brand
+new device or for one using a custom VID/PID pair.
+
+UsbSerialProber is a class to help you find and instantiate compatible
+UsbSerialDrivers from the tree of connected UsbDevices. Normally, you will use
+the default prober returned by ``UsbSerialProber.getDefaultProber()``, which
+uses the built-in list of well-known VIDs and PIDs that are supported by our
+drivers.
+
+To use your own set of rules, create and use a custom prober:
+
+```java
+// Probe for our custom CDC devices, which use VID 0x1234
+// and PIDS 0x0001 and 0x0002.
+ProbeTable customTable = new ProbeTable();
+customTable.addProduct(0x1234, 0x0001, CdcAcmSerialDriver.class);
+customTable.addProduct(0x1234, 0x0002, CdcAcmSerialDriver.class);
+
+UsbSerialProber prober = new UsbSerialProber(customTable);
+List drivers = prober.findAllDrivers(usbManager);
+// ...
+```
+
+Of course, nothing requires you to use UsbSerialProber at all: you can
+instantiate driver classes directly if you know what you're doing; just supply
+a compatible UsbDevice.
+
+## Compatible Devices
+
+This library supports USB to serial converter chips:
+* FTDI FT232, FT2232, ...
+* Prolific PL2303
+* Silabs CP2102, CP2105, ...
+* Qinheng CH340
+
+and devices implementing the CDC/ACM protocol like
+* Arduino using ATmega32U4
+* Digispark using V-USB software USB
+* BBC micro:bit using ARM mbed DAPLink firmware
+* ...
+
+## Author, License, and Copyright
+
+usb-serial-for-android is written and maintained by *mike wakerly* and *kai morich*
+
+This library is licensed under *LGPL Version 2.1*. Please see LICENSE.txt for the
+complete license.
+
+Copyright 2011-2012, Google Inc. All Rights Reserved.
+
+Portions of this library are based on [libftdi](http://www.intra2net.com/en/developer/libftdi).
+Please see FtdiSerialDriver.java for more information.
+
+## Help & Discussion
+
+For common problems, see the
+[Troubleshooting](https://github.com/mik3y/usb-serial-for-android/wiki/Troubleshooting)
+wiki page.
+
+Are you using the library? Add your project to
+[ProjectsUsingUsbSerialForAndroid](https://github.com/mik3y/usb-serial-for-android/wiki/Projects-Using-usb-serial-for-android).
diff --git a/arduino/serial_test.ino b/arduino/serial_test.ino
deleted file mode 100644
index 6964001..0000000
--- a/arduino/serial_test.ino
+++ /dev/null
@@ -1,43 +0,0 @@
-/* Copyright 2012 Google Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
- *
- * Project home page: http://code.google.com/p/usb-serial-for-android/
- */
-
-// Sample Arduino sketch for use with usb-serial-for-android.
-// Prints an ever-increasing counter, and writes back anything
-// it receives.
-
-static int counter = 0;
-void setup() {
- Serial.begin(115200);
-}
-
-void loop() {
- Serial.print("Tick #");
- Serial.print(counter++, DEC);
- Serial.print("\n");
-
- if (Serial.peek() != -1) {
- Serial.print("Read: ");
- do {
- Serial.print((char) Serial.read());
- } while (Serial.peek() != -1);
- Serial.print("\n");
- }
- delay(1000);
-}
diff --git a/build.gradle b/build.gradle
index 0c42278..1d6c342 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,15 +2,17 @@
buildscript {
repositories {
- mavenCentral()
+ jcenter()
+ google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:1.2.3'
+ classpath 'com.android.tools.build:gradle:3.5.1'
}
}
allprojects {
repositories {
- mavenCentral()
+ jcenter()
+ google()
}
}
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 8c0fb64..5c2d1cf 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index f3d36d0..14dc15c 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Tue Jun 23 00:11:28 EDT 2015
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
+#Sun Oct 06 09:46:24 CEST 2019
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
diff --git a/gradlew b/gradlew
index 91a7e26..b0d6d0a 100755
--- a/gradlew
+++ b/gradlew
@@ -1,4 +1,20 @@
-#!/usr/bin/env bash
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
##############################################################################
##
@@ -6,47 +22,6 @@
##
##############################################################################
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
-
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
-
-warn ( ) {
- echo "$*"
-}
-
-die ( ) {
- echo
- echo "$*"
- echo
- exit 1
-}
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
-esac
-
-# For Cygwin, ensure paths are in UNIX format before anything is touched.
-if $cygwin ; then
- [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
-fi
-
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
@@ -61,9 +36,49 @@ while [ -h "$PRG" ] ; do
fi
done
SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >&-
+cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
-cd "$SAVED" >&-
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -90,7 +105,7 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
@@ -114,6 +129,7 @@ fi
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
@@ -154,11 +170,19 @@ if $cygwin ; then
esac
fi
-# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
-function splitJvmOpts() {
- JVM_OPTS=("$@")
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
}
-eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
-JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+APP_ARGS=$(save "$@")
-exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
index aec9973..15e1ee3 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,3 +1,19 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem http://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@@ -8,14 +24,14 @@
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
@@ -46,10 +62,9 @@ echo location of your Java installation.
goto fail
:init
-@rem Get command-line arguments, handling Windowz variants
+@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
-if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
@@ -60,11 +75,6 @@ set _SKIP=2
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
-goto execute
-
-:4NT_args
-@rem Get arguments from the 4NT Shell from JP Software
-set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
diff --git a/test/arduino_leonardo_bridge/arduino_leonardo_bridge.ino b/test/arduino_leonardo_bridge/arduino_leonardo_bridge.ino
new file mode 100644
index 0000000..e7071d9
--- /dev/null
+++ b/test/arduino_leonardo_bridge/arduino_leonardo_bridge.ino
@@ -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());
+}
diff --git a/test/rfc2217_server.diff b/test/rfc2217_server.diff
new file mode 100644
index 0000000..a5d09a2
--- /dev/null
+++ b/test/rfc2217_server.diff
@@ -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)
diff --git a/usbSerialExamples/build.gradle b/usbSerialExamples/build.gradle
index 2da9b1c..5f27979 100644
--- a/usbSerialExamples/build.gradle
+++ b/usbSerialExamples/build.gradle
@@ -1,15 +1,15 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 22
- buildToolsVersion "22.0.1"
+ compileSdkVersion 28
+ buildToolsVersion '28.0.3'
defaultConfig {
- minSdkVersion 14
- targetSdkVersion 22
+ minSdkVersion 17
+ targetSdkVersion 28
- testApplicationId "com.hoho.android.usbserial.examples"
testInstrumentationRunner "android.test.InstrumentationTestRunner"
+ missingDimensionStrategy 'device', 'anyDevice'
}
buildTypes {
@@ -20,5 +20,5 @@ android {
}
dependencies {
- compile project(':usbSerialForAndroid')
+ implementation project(':usbSerialForAndroid')
}
diff --git a/usbSerialExamples/src/main/AndroidManifest.xml b/usbSerialExamples/src/main/AndroidManifest.xml
index 26ca72c..efa205f 100644
--- a/usbSerialExamples/src/main/AndroidManifest.xml
+++ b/usbSerialExamples/src/main/AndroidManifest.xml
@@ -4,8 +4,6 @@
android:versionCode="1"
android:versionName="1.0" >
-
-
mEntries = new ArrayList();
private ArrayAdapter mAdapter;
@@ -89,11 +96,25 @@ public class DeviceListActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
+ final Context context = this;
mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
- mListView = (ListView) findViewById(R.id.deviceList);
- mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
- mProgressBarTitle = (TextView) findViewById(R.id.progressBarTitle);
+ mListView = findViewById(R.id.deviceList);
+ mProgressBar = findViewById(R.id.progressBar);
+ mProgressBarTitle = findViewById(R.id.progressBarTitle);
+
+ mUsbReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if(intent.getAction().equals(INTENT_ACTION_GRANT_USB)) {
+ if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
+ showConsoleActivity(mSerialPort);
+ } else {
+ Toast.makeText(context, "USB permission denied", Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+ };
mAdapter = new ArrayAdapter(this,
android.R.layout.simple_expandable_list_item_2, mEntries) {
@@ -112,9 +133,7 @@ public class DeviceListActivity extends Activity {
final UsbSerialDriver driver = port.getDriver();
final UsbDevice device = driver.getDevice();
- final String title = String.format("Vendor %s Product %s",
- HexDump.toHexString((short) device.getVendorId()),
- HexDump.toHexString((short) device.getProductId()));
+ final String title = String.format("Vendor %4X Product %4X", device.getVendorId(), device.getProductId());
row.getText1().setText(title);
final String subtitle = driver.getClass().getSimpleName();
@@ -135,8 +154,14 @@ public class DeviceListActivity extends Activity {
return;
}
- final UsbSerialPort port = mEntries.get(position);
- showConsoleActivity(port);
+ mSerialPort = mEntries.get(position);
+ UsbDevice device = mSerialPort.getDriver().getDevice();
+ if (!mUsbManager.hasPermission(device)) {
+ PendingIntent usbPermissionIntent = PendingIntent.getBroadcast(context, 0, new Intent(INTENT_ACTION_GRANT_USB), 0);
+ mUsbManager.requestPermission(device, usbPermissionIntent);
+ } else {
+ showConsoleActivity(mSerialPort);
+ }
}
});
}
@@ -145,12 +170,14 @@ public class DeviceListActivity extends Activity {
protected void onResume() {
super.onResume();
mHandler.sendEmptyMessage(MESSAGE_REFRESH);
+ registerReceiver(mUsbReceiver, new IntentFilter(INTENT_ACTION_GRANT_USB));
}
@Override
protected void onPause() {
super.onPause();
mHandler.removeMessages(MESSAGE_REFRESH);
+ unregisterReceiver(mUsbReceiver);
}
private void refreshDeviceList() {
diff --git a/usbSerialExamples/src/main/java/src/com/hoho/android/usbserial/examples/SerialConsoleActivity.java b/usbSerialExamples/src/main/java/com/hoho/android/usbserial/examples/SerialConsoleActivity.java
similarity index 100%
rename from usbSerialExamples/src/main/java/src/com/hoho/android/usbserial/examples/SerialConsoleActivity.java
rename to usbSerialExamples/src/main/java/com/hoho/android/usbserial/examples/SerialConsoleActivity.java
diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/util/HexDump.java b/usbSerialExamples/src/main/java/com/hoho/android/usbserial/util/HexDump.java
similarity index 100%
rename from usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/util/HexDump.java
rename to usbSerialExamples/src/main/java/com/hoho/android/usbserial/util/HexDump.java
diff --git a/usbSerialForAndroid/build.gradle b/usbSerialForAndroid/build.gradle
index c3d835c..5aa0170 100644
--- a/usbSerialForAndroid/build.gradle
+++ b/usbSerialForAndroid/build.gradle
@@ -1,14 +1,17 @@
apply plugin: 'com.android.library'
-apply plugin: 'maven'
-apply plugin: 'signing'
android {
- compileSdkVersion 19
- buildToolsVersion "19.1"
+ compileSdkVersion 28
+ buildToolsVersion '28.0.3'
defaultConfig {
- minSdkVersion 12
- targetSdkVersion 19
+ minSdkVersion 17
+ targetSdkVersion 28
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ testInstrumentationRunnerArguments = [ // Raspi Windows LinuxVM ...
+ 'rfc2217_server_host': '192.168.0.100',
+ 'rfc2217_server_nonstandard_baudrates': 'true', // true false false
+ ]
}
buildTypes {
@@ -19,81 +22,14 @@ android {
}
}
-group = "com.hoho.android"
-version = "0.2.0-SNAPSHOT"
-
-configurations {
- archives {
- extendsFrom configurations.default
- }
+dependencies {
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'com.android.support:support-annotations:28.0.0'
+ androidTestImplementation 'com.android.support.test:runner:1.0.2'
+ androidTestImplementation 'commons-net:commons-net:3.6'
+ androidTestImplementation 'org.apache.commons:commons-lang3:3.8.1'
}
-signing {
- required { has("release") && gradle.taskGraph.hasTask("uploadArchives") }
- sign configurations.archives
-}
+//apply from: 'publishToMavenLocal.gradle'
-def getRepositoryUsername() {
- return hasProperty('sonatypeUsername') ? sonatypeUsername : ""
-}
-
-def getRepositoryPassword() {
- return hasProperty('sonatypePassword') ? sonatypePassword : ""
-}
-
-def isReleaseBuild() {
- return version.contains("SNAPSHOT") == false
-}
-
-uploadArchives {
- def sonatypeRepositoryUrl
- if (isReleaseBuild()) {
- println 'RELEASE BUILD'
- sonatypeRepositoryUrl = hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL
- : "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
- } else {
- println 'SNAPSHOT BUILD'
- sonatypeRepositoryUrl = hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL
- : "https://oss.sonatype.org/content/repositories/snapshots/"
- }
-
- configuration = configurations.archives
- repositories.mavenDeployer {
- beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
-
- repository(url: sonatypeRepositoryUrl) {
- authentication(userName: getRepositoryUsername(),
- password: getRepositoryPassword())
- }
-
- pom.artifactId = 'usb-serial-for-android'
- pom.project {
- name 'usb-serial-for-android'
- packaging 'aar'
- description 'USB Serial Driver Library for Android'
- url 'https://github.com/mik3y/usb-serial-for-android'
-
- scm {
- url 'scm:git@github.com:mik3y/usb-serial-for-android.git'
- connection 'scm:git@github.com:mik3y/usb-serial-for-android.git'
- developerConnection 'scm:git@github.com:mik3y/usb-serial-for-android.git'
- }
-
- licenses {
- license {
- name 'GNU LGPL v2.1'
- url 'http://www.gnu.org/licenses/lgpl-2.1.txt'
- distribution 'repo'
- }
- }
-
- developers {
- developer {
- id 'mik3y'
- name 'mik3y'
- email 'opensource@hoho.com'
- }
- }
- }
- }
-}
+//apply from: 'coverage.gradle'
diff --git a/usbSerialForAndroid/coverage.gradle b/usbSerialForAndroid/coverage.gradle
new file mode 100644
index 0000000..f5c6fa4
--- /dev/null
+++ b/usbSerialForAndroid/coverage.gradle
@@ -0,0 +1,45 @@
+apply plugin: 'jacoco'
+
+android {
+ flavorDimensions 'device'
+ productFlavors {
+ anyDevice {
+ // Used as fallback in usbSerialExample/build.gradle -> missingDimensionStrategy, but not for coverage report
+ dimension 'device'
+ }
+ arduino {
+ dimension 'device'
+ testInstrumentationRunnerArguments = ['test_device_driver': 'CdcAcm']
+ }
+ ch340 {
+ dimension 'device'
+ testInstrumentationRunnerArguments = ['test_device_driver': 'Ch34x']
+ }
+ cp2102 { // and cp2105 first port
+ dimension 'device'
+ testInstrumentationRunnerArguments = ['test_device_driver': 'Cp21xx']
+ }
+ cp2105 { // second port
+ dimension 'device'
+ testInstrumentationRunnerArguments = ['test_device_driver': 'Cp21xx', 'test_device_port': '1']
+ }
+ ft232 { // and ft2232 first port
+ dimension 'device'
+ testInstrumentationRunnerArguments = ['test_device_driver': 'Ftdi']
+ }
+ ft2232 { // second port
+ dimension 'device'
+ testInstrumentationRunnerArguments = ['test_device_driver': 'Ftdi', 'test_device_port': '1']
+ }
+ pl2302 {
+ dimension 'device'
+ testInstrumentationRunnerArguments = ['test_device_driver': 'Prolific']
+ }
+ }
+
+ buildTypes {
+ debug {
+ testCoverageEnabled true
+ }
+ }
+}
diff --git a/usbSerialForAndroid/publishToMavenLocal.gradle b/usbSerialForAndroid/publishToMavenLocal.gradle
new file mode 100644
index 0000000..1212e0d
--- /dev/null
+++ b/usbSerialForAndroid/publishToMavenLocal.gradle
@@ -0,0 +1,20 @@
+apply plugin: 'maven-publish'
+
+publishing {
+ publications {
+ maven(MavenPublication) {
+ groupId 'com.github.mik3y'
+ artifactId 'usb-serial-for-android'
+ version '1.x.0'
+ afterEvaluate {
+ artifact androidSourcesJar
+ artifact bundleReleaseAar
+ }
+ }
+ }
+}
+
+task androidSourcesJar(type: Jar) {
+ classifier 'sources'
+ from android.sourceSets.main.java.srcDirs
+}
diff --git a/usbSerialForAndroid/src/androidTest/AndroidManifest.xml b/usbSerialForAndroid/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..51ad082
--- /dev/null
+++ b/usbSerialForAndroid/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/usbSerialForAndroid/src/androidTest/java/com/hoho/android/usbserial/DeviceTest.java b/usbSerialForAndroid/src/androidTest/java/com/hoho/android/usbserial/DeviceTest.java
new file mode 100644
index 0000000..4d92811
--- /dev/null
+++ b/usbSerialForAndroid/src/androidTest/java/com/hoho/android/usbserial/DeviceTest.java
@@ -0,0 +1,1200 @@
+/*
+ * restrictions
+ * - as real hardware is used, timing might need tuning. see:
+ * - Thread.sleep(...)
+ * - obj.wait(...)
+ * - 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.os.Process;
+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.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 {
+
+ // testInstrumentationRunnerArguments configuration
+ private static String rfc2217_server_host;
+ private static int rfc2217_server_port = 2217;
+ private static boolean rfc2217_server_nonstandard_baudrates;
+ private static String test_device_driver;
+ private static int test_device_port;
+
+ private final static int TELNET_READ_WAIT = 500;
+ private final static int TELNET_COMMAND_WAIT = 2000;
+ 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 final static byte RFC2217_PURGE_DATA = 12;
+
+ private Context context;
+ private UsbManager usbManager;
+ private UsbSerialDriver usbSerialDriver;
+ private UsbDeviceConnection usbDeviceConnection;
+ private UsbSerialPort usbSerialPort;
+ private SerialInputOutputManager usbIoManager;
+ private final Deque usbReadBuffer = new LinkedList<>();
+ private Exception usbReadError;
+ 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 {
+ rfc2217_server_host = InstrumentationRegistry.getArguments().getString("rfc2217_server_host");
+ rfc2217_server_nonstandard_baudrates = Boolean.valueOf(InstrumentationRegistry.getArguments().getString("rfc2217_server_nonstandard_baudrates"));
+ test_device_driver = InstrumentationRegistry.getArguments().getString("test_device_driver");
+ test_device_port = Integer.valueOf(InstrumentationRegistry.getArguments().getString("test_device_port","0"));
+
+ // postpone parts of fixture setup to first test, because exceptions are not reported for @BeforeClass
+ // and test terminates with misleading 'Empty test suite'
+ telnetClient = null;
+ }
+
+ 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;
+ telnetClient.sendCommand((byte)TelnetCommand.SB);
+ telnetWriteStream.write(new byte[] {RFC2217_COM_PORT_OPTION, RFC2217_PURGE_DATA, 3});
+ telnetClient.sendCommand((byte)TelnetCommand.SE);
+ for(int i=0; i availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(usbManager);
+ assertEquals("no USB device found", 1, availableDrivers.size());
+ usbSerialDriver = availableDrivers.get(0);
+ if(test_device_driver != null) {
+ String driverName = usbSerialDriver.getClass().getSimpleName();
+ assertEquals(test_device_driver+"SerialDriver", driverName);
+ }
+ assertTrue( usbSerialDriver.getPorts().size() > test_device_port);
+ usbSerialPort = usbSerialDriver.getPorts().get(test_device_port);
+ Log.i(TAG, "Using USB device "+ usbSerialPort.toString()+" driver="+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]);
+ }
+ usbOpen(true);
+ }
+
+ @After
+ public void tearDown() throws IOException {
+ try {
+ usbRead(0);
+ } catch (Exception ignored) {}
+ try {
+ telnetRead(0);
+ } catch (Exception ignored) {}
+ usbClose();
+ 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();
+ }
+ }
+
+ private void usbClose() {
+ if (usbIoManager != null) {
+ usbIoManager.setListener(null);
+ usbIoManager.stop();
+ }
+ if (usbSerialPort != null) {
+ try {
+ usbSerialPort.setDTR(false);
+ usbSerialPort.setRTS(false);
+ } catch (Exception ignored) {
+ }
+ try {
+ usbSerialPort.close();
+ } catch (IOException ignored) {
+ }
+ usbSerialPort = null;
+ }
+ if(usbDeviceConnection != null)
+ usbDeviceConnection.close();
+ usbDeviceConnection = null;
+ if(usbIoManager != null) {
+ for(int i=0; i<2000; i++) {
+ if(SerialInputOutputManager.State.STOPPED == usbIoManager.getState()) break;
+ try {
+ Thread.sleep(1);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ assertEquals(SerialInputOutputManager.State.STOPPED, usbIoManager.getState());
+ usbIoManager = null;
+ }
+ }
+
+ private void usbOpen(boolean withIoManager) throws Exception {
+ usbDeviceConnection = usbManager.openDevice(usbSerialDriver.getDevice());
+ usbSerialPort = usbSerialDriver.getPorts().get(test_device_port);
+ usbSerialPort.open(usbDeviceConnection);
+ usbSerialPort.setDTR(true);
+ usbSerialPort.setRTS(true);
+ if(withIoManager) {
+ 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();
+ }
+ usbReadError = null;
+ }
+
+ // 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) {
+ if(usbReadError != null)
+ throw usbReadError;
+ 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 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) {
+ usbReadError = e;
+ //fail("usb connection lost");
+ }
+
+ // 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;
+ }
+
+ private void logDifference(final StringBuilder data, final StringBuilder expected) {
+ int datapos = indexOfDifference(data, expected);
+ int expectedpos = datapos;
+ while(datapos != -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;
+ }
+ }
+ Log.i(TAG, "difference at " + datapos + " len " + len );
+ Log.d(TAG, " got " + data.substring(Math.max(datapos - 20, 0), Math.min(datapos + 20, data.length())));
+ Log.d(TAG, " expected " + expected.substring(Math.max(expectedpos - 20, 0), Math.min(expectedpos + 20, expected.length())));
+ datapos = indexOfDifference(data, expected, nextdatapos, nextexpectedpos);
+ expectedpos = nextexpectedpos + (datapos - nextdatapos);
+ }
+ }
+
+ private void doReadWrite(String reason) throws Exception {
+ byte[] buf1 = new byte[]{ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16};
+ byte[] buf2 = new byte[]{ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26};
+ byte[] data;
+
+ telnetWrite(buf1);
+ data = usbRead(buf1.length);
+ assertThat(reason, data, equalTo(buf1)); // includes array content in output
+ //assertArrayEquals("net2usb".getBytes(), data); // only includes array length in output
+ usbWrite(buf2);
+ data = telnetRead(buf2.length);
+ assertThat(reason, data, equalTo(buf2));
+ }
+
+ @Test
+ public void openClose() throws Exception {
+ byte[] data;
+ telnetParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
+ usbParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
+ doReadWrite("");
+
+ try {
+ usbSerialPort.open(usbDeviceConnection);
+ fail("already open expected");
+ } catch (IOException ignored) {
+ }
+ doReadWrite("");
+
+ usbSerialPort.close();
+ try {
+ usbSerialPort.close();
+ fail("already closed expected");
+ } catch (IOException ignored) {
+ }
+ try {
+ usbWrite(new byte[]{0x00});
+ fail("write error expected");
+ } catch (IOException ignored) {
+ } catch (NullPointerException ignored) {
+ }
+ try {
+ usbRead(1);
+ //fail("read error expected");
+ } catch (IOException ignored) {
+ } catch (NullPointerException ignored) {
+ }
+ try {
+ usbParameters(9600, 8, 1, UsbSerialPort.PARITY_NONE);
+ fail("error expected");
+ } catch (IOException ignored) {
+ } catch (NullPointerException ignored) {
+ }
+
+ // partial re-open not supported
+ try {
+ usbSerialPort.open(usbDeviceConnection);
+ //usbParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
+ doReadWrite("");
+ fail("re-open not supported");
+ } catch (IOException ignored) {
+ }
+ try {
+ usbSerialPort.close();
+ } catch (IOException ignored) {
+ }
+
+ if (usbSerialDriver instanceof Cp21xxSerialDriver) { // why needed?
+ usbIoManager.stop();
+ usbIoManager = null;
+ }
+ // full re-open supported
+ usbClose();
+ usbOpen(true);
+ telnetParameters(9600, 8, 1, UsbSerialPort.PARITY_NONE);
+ usbParameters(9600, 8, 1, UsbSerialPort.PARITY_NONE);
+ doReadWrite("");
+ }
+
+ @Test
+ public void baudRate() throws Exception {
+ 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);
+
+ doReadWrite("");
+ }
+
+ // 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 (IOException ignored) { // cp2105 second port
+ } catch (IllegalArgumentException ignored) {
+ }
+ 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 (ArithmeticException ignored) { // ch340
+ } catch (IOException ignored) { // cp2105 second port
+ } catch (IllegalArgumentException ignored) {
+ }
+ 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 (IOException ignored) { // ch340
+ } catch (IllegalArgumentException ignored) {
+ }
+ 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 (ArithmeticException ignored) { // ch340
+ } catch (IOException ignored) { // cp2105 second port
+ } catch (IllegalArgumentException ignored) {
+ }
+
+ for(int baudRate : new int[] {300, 2400, 19200, 115200} ) {
+ if(baudRate == 300 && isCp21xxRestrictedPort) {
+ try {
+ usbParameters(baudRate, 8, 1, UsbSerialPort.PARITY_NONE);
+ fail("baudrate 300 on cp21xx restricted port");
+ } catch (IOException ignored) {
+ }
+ continue;
+ }
+ telnetParameters(baudRate, 8, 1, UsbSerialPort.PARITY_NONE);
+ usbParameters(baudRate, 8, 1, UsbSerialPort.PARITY_NONE);
+
+ doReadWrite(baudRate+"/8N1");
+ }
+ if(rfc2217_server_nonstandard_baudrates && !isCp21xxRestrictedPort) {
+ // usbParameters does not fail on devices that do not support nonstandard baud rates
+ usbParameters(42000, 8, 1, UsbSerialPort.PARITY_NONE);
+ telnetParameters(42000, 8, 1, UsbSerialPort.PARITY_NONE);
+
+ byte[] buf1 = "abc".getBytes();
+ byte[] buf2 = "ABC".getBytes();
+ byte[] data1, data2;
+ usbWrite(buf1);
+ data1 = telnetRead();
+ telnetWrite(buf2);
+ data2 = usbRead();
+ if (usbSerialDriver instanceof ProlificSerialDriver) {
+ // not supported
+ assertNotEquals(data1, buf2);
+ assertNotEquals(data2, buf2);
+ } else if (usbSerialDriver instanceof Cp21xxSerialDriver) {
+ if (usbSerialDriver.getPorts().size() > 1) {
+ // supported on cp2105 first port
+ assertThat("42000/8N1", data1, equalTo(buf1));
+ assertThat("42000/8N1", data2, equalTo(buf2));
+ } else {
+ // not supported on cp2102
+ assertNotEquals(data1, buf1);
+ assertNotEquals(data2, buf2);
+ }
+ } else {
+ assertThat("42000/8N1", data1, equalTo(buf1));
+ assertThat("42000/8N1", data2, equalTo(buf2));
+ }
+ }
+ { // non matching baud rate
+ telnetParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
+ usbParameters(2400, 8, 1, UsbSerialPort.PARITY_NONE);
+
+ byte[] data;
+ 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 (IllegalArgumentException ignored) {
+ }
+ }
+
+ // 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 (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 (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 (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 (IllegalArgumentException ignored) {
+ }
+ }
+ 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);
+ fail("parity mark");
+ } catch (IllegalArgumentException ignored) {}
+ try {
+ usbParameters(19200, 8, 1, UsbSerialPort.PARITY_SPACE);
+ fail("parity space");
+ } catch (IllegalArgumentException ignored) {}
+ 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 {
+ 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 (IllegalArgumentException ignored) {
+ }
+ }
+
+ if (usbSerialDriver instanceof CdcAcmSerialDriver) {
+ usbParameters(19200, 8, UsbSerialPort.STOPBITS_2, UsbSerialPort.PARITY_NONE);
+ // software based bridge in arduino_leonardo_bridge.ino is to slow for real test, 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(IllegalArgumentException e) {
+ if(!isCp21xxRestrictedPort)
+ throw e;
+ }
+ try {
+ usbParameters(19200, 8, UsbSerialPort.STOPBITS_1_5, UsbSerialPort.PARITY_NONE);
+ // 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
+ } catch(IllegalArgumentException ignored) {
+ }
+ }
+ }
+
+
+ @Test
+ public void probeTable() throws Exception {
+ class DummyDriver implements UsbSerialDriver {
+ @Override
+ public UsbDevice getDevice() { return null; }
+ @Override
+ public List getPorts() { return null; }
+ }
+ List 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(availableDrivers.get(0).getClass(), usbSerialDriver.getClass());
+ }
+
+ @Test
+ // provoke data loss, when data is not read fast enough
+ public void readBufferOverflow() 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;
+ for(bufferSize = 8; bufferSize < (2<<15); bufferSize *= 2) {
+ int linenr;
+ 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 completely read from buffer and new data is received
+ 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);
+ }
+ while(!found) {
+ // use waiting read to clear input queue, else next test would see unexpected data
+ byte[] rest = usbRead(-1);
+ if(rest.length == 0)
+ fail("last line "+line+" not found");
+ data.append(new String(rest));
+ found = data.toString().endsWith(line);
+ }
+ if (data.length() != expected.length())
+ break;
+ }
+
+ logDifference(data, expected);
+ assertTrue(bufferSize > 16);
+ assertTrue(data.length() != expected.length());
+ }
+
+ @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
+ //
+ // readBufferOverflow provokes read errors, but they can also happen here where the data is actually read fast enough.
+ // 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.
+ // Using SERIAL_INPUT_OUTPUT_MANAGER_THREAD_PRIORITY=THREAD_PRIORITY_URGENT_AUDIO sometimes reduced errors by factor 10, sometimes not at all!
+ //
+ int baudrate = 115200;
+ usbParameters(baudrate, 8, 1, UsbSerialPort.PARITY_NONE);
+ telnetParameters(baudrate, 8, 1, UsbSerialPort.PARITY_NONE);
+
+ 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;
+ while(!found) {
+ // use waiting read to clear input queue, else next test would see unexpected data
+ byte[] rest = usbRead(-1);
+ if(rest.length == 0)
+ break;
+ data.append(new String(rest));
+ found = data.toString().endsWith(line);
+ }
+ logDifference(data, expected);
+ }
+
+ @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));
+ }
+ }
+
+ @Test
+ public void purgeHwBuffers() throws Exception {
+ // 2400 is slowest baud rate for isCp21xxRestrictedPort
+ usbParameters(2400, 8, 1, UsbSerialPort.PARITY_NONE);
+ telnetParameters(2400, 8, 1, UsbSerialPort.PARITY_NONE);
+ byte[] buf = new byte[64];
+ for(int i=0; i " + data.length());
+
+ assertTrue(data.length() > 5);
+ if(purged)
+ assertTrue(data.length() < buf.length+1);
+ else
+ assertEquals(data.length(), buf.length + 3);
+
+ // todo: purge receive buffer
+ }
+
+ @Test
+ // WriteAsync rarely makes sense, as data is not written until something is read
+ public void writeAsync() throws Exception {
+ if (usbSerialDriver instanceof FtdiSerialDriver)
+ return; // periodically sends status messages, so does not block here
+ usbParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
+ telnetParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
+
+ SerialInputOutputManager ioManager;
+ ioManager = new SerialInputOutputManager(null);
+ assertEquals(null, ioManager.getListener());
+ ioManager.setListener(this);
+ assertEquals(this, ioManager.getListener());
+ ioManager = new SerialInputOutputManager(null, this);
+ assertEquals(this, ioManager.getListener());
+
+ byte[] data, buf = new byte[]{1};
+ int len;
+ usbIoManager.writeAsync(buf);
+ usbIoManager.writeAsync(buf);
+ data = telnetRead(1);
+ assertEquals(0, data.length);
+ telnetWrite(buf);
+ data = usbRead(1);
+ assertEquals(1, data.length);
+ data = telnetRead(2);
+ assertEquals(2, data.length);
+ }
+
+ @Test
+ // Blocking read should be avoided in the UI thread, as it makes the app unresponsive.
+ // You better use the SerialInputOutputManager.
+ //
+ // With the change from bulkTransfer to queued requests, the read timeout has no effect
+ // and the call blocks until close() if no data is available!
+ // The change from bulkTransfer to queued requests was necessary to prevent data loss.
+ public void readSync() throws Exception {
+ if (usbSerialDriver instanceof FtdiSerialDriver)
+ return; // periodically sends status messages, so does not block here
+
+ Runnable closeThread = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ usbClose();
+ }
+ };
+
+ usbClose();
+ usbOpen(false);
+ usbParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
+ telnetParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
+
+ byte[] buf = new byte[]{1};
+ int len;
+ long time;
+ telnetWrite(buf);
+ len = usbSerialPort.read(buf, 0); // not blocking because data is available
+ assertEquals(1, len);
+
+ time = System.currentTimeMillis();
+ Executors.newSingleThreadExecutor().submit(closeThread);
+ len = usbSerialPort.read(buf, 0); // blocking until close()
+ assertEquals(0, len);
+ assertTrue(System.currentTimeMillis()-time >= 100);
+
+ usbOpen(false);
+ usbParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
+ telnetParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
+
+ time = System.currentTimeMillis();
+ Executors.newSingleThreadExecutor().submit(closeThread);
+ len = usbSerialPort.read(buf, 10); // timeout not used any more -> blocking until close()
+ assertEquals(0, len);
+ assertTrue(System.currentTimeMillis()-time >= 100);
+ }
+}
diff --git a/usbSerialForAndroid/src/main/AndroidManifest.xml b/usbSerialForAndroid/src/main/AndroidManifest.xml
index 9355f7b..e579693 100644
--- a/usbSerialForAndroid/src/main/AndroidManifest.xml
+++ b/usbSerialForAndroid/src/main/AndroidManifest.xml
@@ -1,7 +1,4 @@
-
+ package="com.hoho.android.usbserial">
diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/BuildInfo.java b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/BuildInfo.java
deleted file mode 100644
index 1f0d363..0000000
--- a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/BuildInfo.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.hoho.android.usbserial;
-
-/**
- * Static container of information about this library.
- */
-public final class BuildInfo {
-
- /**
- * The current version of this library. Values are of the form
- * "major.minor.micro[-suffix]". A suffix of "-pre" indicates a pre-release
- * of the version preceeding it.
- */
- public static final String VERSION = "0.2.0-pre";
-
- private BuildInfo() {
- throw new IllegalStateException("Non-instantiable class.");
- }
-
-}
diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java
index 4fd84bd..1e02232 100644
--- a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java
+++ b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java
@@ -27,7 +27,6 @@ import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbRequest;
-import android.os.Build;
import android.util.Log;
import java.io.IOException;
@@ -51,6 +50,7 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
private final UsbDevice mDevice;
private final UsbSerialPort mPort;
+ private UsbRequest mUsbRequest;
public CdcAcmSerialDriver(UsbDevice device) {
mDevice = device;
@@ -69,7 +69,6 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
class CdcAcmSerialPort extends CommonUsbSerialPort {
- private final boolean mEnableAsyncReads;
private UsbInterface mControlInterface;
private UsbInterface mDataInterface;
@@ -77,6 +76,8 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
private UsbEndpoint mReadEndpoint;
private UsbEndpoint mWriteEndpoint;
+ private int mControlIndex;
+
private boolean mRts = false;
private boolean mDtr = false;
@@ -90,7 +91,6 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
public CdcAcmSerialPort(UsbDevice device, int portNumber) {
super(device, portNumber);
- mEnableAsyncReads = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1);
}
@Override
@@ -116,13 +116,6 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
openInterface();
}
- if (mEnableAsyncReads) {
- Log.d(TAG, "Async reads enabled");
- } else {
- Log.d(TAG, "Async reads disabled.");
- }
-
-
opened = true;
} finally {
if (!opened) {
@@ -139,6 +132,7 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
// the following code is inspired by the cdc-acm driver
// in the linux kernel
+ mControlIndex = 0;
mControlInterface = mDevice.getInterface(0);
Log.d(TAG, "Control iface=" + mControlInterface);
@@ -196,34 +190,63 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
private void openInterface() throws IOException {
Log.d(TAG, "claiming interfaces, count=" + mDevice.getInterfaceCount());
- mControlInterface = mDevice.getInterface(0);
+ mControlInterface = null;
+ mDataInterface = null;
+ for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
+ UsbInterface usbInterface = mDevice.getInterface(i);
+ if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_COMM) {
+ mControlIndex = i;
+ mControlInterface = usbInterface;
+ }
+ if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA) {
+ mDataInterface = usbInterface;
+ }
+ }
+
+ if(mControlInterface == null) {
+ throw new IOException("no control interface.");
+ }
Log.d(TAG, "Control iface=" + mControlInterface);
- // class should be USB_CLASS_COMM
if (!mConnection.claimInterface(mControlInterface, true)) {
throw new IOException("Could not claim control interface.");
}
mControlEndpoint = mControlInterface.getEndpoint(0);
- Log.d(TAG, "Control endpoint direction: " + mControlEndpoint.getDirection());
+ if (mControlEndpoint.getDirection() != UsbConstants.USB_DIR_IN || mControlEndpoint.getType() != UsbConstants.USB_ENDPOINT_XFER_INT) {
+ throw new IOException("invalid control endpoint");
+ }
- Log.d(TAG, "Claiming data interface.");
- mDataInterface = mDevice.getInterface(1);
+ if(mDataInterface == null) {
+ throw new IOException("no data interface.");
+ }
Log.d(TAG, "data iface=" + mDataInterface);
- // class should be USB_CLASS_CDC_DATA
if (!mConnection.claimInterface(mDataInterface, true)) {
throw new IOException("Could not claim data interface.");
}
- mReadEndpoint = mDataInterface.getEndpoint(1);
- Log.d(TAG, "Read endpoint direction: " + mReadEndpoint.getDirection());
- mWriteEndpoint = mDataInterface.getEndpoint(0);
- Log.d(TAG, "Write endpoint direction: " + mWriteEndpoint.getDirection());
+
+ mReadEndpoint = null;
+ mWriteEndpoint = null;
+ for (int i = 0; i < mDataInterface.getEndpointCount(); i++) {
+ UsbEndpoint ep = mDataInterface.getEndpoint(i);
+ if (ep.getDirection() == UsbConstants.USB_DIR_IN && ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)
+ mReadEndpoint = ep;
+ if (ep.getDirection() == UsbConstants.USB_DIR_OUT && ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)
+ mWriteEndpoint = ep;
+ }
+ if (mReadEndpoint == null || mWriteEndpoint == null) {
+ throw new IOException("Could not get read&write endpoints.");
+ }
}
- private int sendAcmControlMessage(int request, int value, byte[] buf) {
- return mConnection.controlTransfer(
- USB_RT_ACM, request, value, 0, buf, buf != null ? buf.length : 0, 5000);
+ private int sendAcmControlMessage(int request, int value, byte[] buf) throws IOException {
+ int len = mConnection.controlTransfer(
+ USB_RT_ACM, request, value, mControlIndex, buf, buf != null ? buf.length : 0, 5000);
+ if(len < 0) {
+ throw new IOException("controlTransfer failed.");
+ }
+ return len;
}
@Override
@@ -231,57 +254,43 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
if (mConnection == null) {
throw new IOException("Already closed");
}
+ synchronized (this) {
+ if (mUsbRequest != null)
+ mUsbRequest.cancel();
+ }
mConnection.close();
mConnection = null;
}
@Override
public int read(byte[] dest, int timeoutMillis) throws IOException {
- if (mEnableAsyncReads) {
- final UsbRequest request = new UsbRequest();
- try {
+ final UsbRequest request = new UsbRequest();
+ try {
request.initialize(mConnection, mReadEndpoint);
final ByteBuffer buf = ByteBuffer.wrap(dest);
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();
+ synchronized (this) {
+ mUsbRequest = null;
+ }
if (response == null) {
- throw new IOException("Null response");
+ 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;
+ //Log.d(TAG, HexDump.dumpHexString(dest, 0, Math.min(32, dest.length)));
+ return nread;
} else {
- return 0;
- }
- } finally {
- request.close();
- }
- }
-
- 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
- if (timeoutMillis == Integer.MAX_VALUE) {
- // Hack: Special case "~infinite timeout" as an error.
- return -1;
- }
return 0;
}
- System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
+ } finally {
+ mUsbRequest = null;
+ request.close();
}
- return numBytesRead;
}
@Override
@@ -320,7 +329,7 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
}
@Override
- public void setParameters(int baudRate, int dataBits, int stopBits, int parity) {
+ public void setParameters(int baudRate, int dataBits, int stopBits, int parity) throws IOException {
byte stopBitsByte;
switch (stopBits) {
case STOPBITS_1: stopBitsByte = 0; break;
@@ -392,7 +401,7 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
setDtrRts();
}
- private void setDtrRts() {
+ private void setDtrRts() throws IOException {
int value = (mRts ? 0x2 : 0) | (mDtr ? 0x1 : 0);
sendAcmControlMessage(SET_CONTROL_LINE_STATE, value, null);
}
@@ -401,7 +410,7 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
public static Map getSupportedDevices() {
final Map supportedDevices = new LinkedHashMap();
- supportedDevices.put(Integer.valueOf(UsbId.VENDOR_ARDUINO),
+ supportedDevices.put(UsbId.VENDOR_ARDUINO,
new int[] {
UsbId.ARDUINO_UNO,
UsbId.ARDUINO_UNO_R3,
@@ -414,18 +423,22 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
UsbId.ARDUINO_LEONARDO,
UsbId.ARDUINO_MICRO,
});
- supportedDevices.put(Integer.valueOf(UsbId.VENDOR_VAN_OOIJEN_TECH),
+ supportedDevices.put(UsbId.VENDOR_VAN_OOIJEN_TECH,
new int[] {
UsbId.VAN_OOIJEN_TECH_TEENSYDUINO_SERIAL,
});
- supportedDevices.put(Integer.valueOf(UsbId.VENDOR_ATMEL),
+ supportedDevices.put(UsbId.VENDOR_ATMEL,
new int[] {
UsbId.ATMEL_LUFA_CDC_DEMO_APP,
});
- supportedDevices.put(Integer.valueOf(UsbId.VENDOR_LEAFLABS),
+ supportedDevices.put(UsbId.VENDOR_LEAFLABS,
new int[] {
UsbId.LEAFLABS_MAPLE,
});
+ supportedDevices.put(UsbId.VENDOR_ARM,
+ new int[] {
+ UsbId.ARM_MBED,
+ });
return supportedDevices;
}
diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/Ch34xSerialDriver.java b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/Ch34xSerialDriver.java
index 1155512..b35cbef 100644
--- a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/Ch34xSerialDriver.java
+++ b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/Ch34xSerialDriver.java
@@ -25,9 +25,11 @@ import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbRequest;
import android.util.Log;
import java.io.IOException;
+import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
@@ -46,6 +48,17 @@ public class Ch34xSerialDriver implements UsbSerialDriver {
private final UsbDevice mDevice;
private final UsbSerialPort mPort;
+ private static final int LCR_ENABLE_RX = 0x80;
+ private static final int LCR_ENABLE_TX = 0x40;
+ private static final int LCR_MARK_SPACE = 0x20;
+ private static final int LCR_PAR_EVEN = 0x10;
+ private static final int LCR_ENABLE_PAR = 0x08;
+ private static final int LCR_STOP_BITS_2 = 0x04;
+ private static final int LCR_CS8 = 0x03;
+ private static final int LCR_CS7 = 0x02;
+ private static final int LCR_CS6 = 0x01;
+ private static final int LCR_CS5 = 0x00;
+
public Ch34xSerialDriver(UsbDevice device) {
mDevice = device;
mPort = new Ch340SerialPort(mDevice, 0);
@@ -72,6 +85,7 @@ public class Ch34xSerialDriver implements UsbSerialDriver {
private UsbEndpoint mReadEndpoint;
private UsbEndpoint mWriteEndpoint;
+ private UsbRequest mUsbRequest;
public Ch340SerialPort(UsbDevice device, int portNumber) {
super(device, portNumber);
@@ -93,10 +107,8 @@ public class Ch34xSerialDriver implements UsbSerialDriver {
try {
for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
UsbInterface usbIface = mDevice.getInterface(i);
- if (mConnection.claimInterface(usbIface, true)) {
- Log.d(TAG, "claimInterface " + i + " SUCCESS");
- } else {
- Log.d(TAG, "claimInterface " + i + " FAIL");
+ if (!mConnection.claimInterface(usbIface, true)) {
+ throw new IOException("Could not claim data interface.");
}
}
@@ -112,7 +124,6 @@ public class Ch34xSerialDriver implements UsbSerialDriver {
}
}
-
initialize();
setBaudRate(DEFAULT_BAUD_RATE);
@@ -133,9 +144,10 @@ public class Ch34xSerialDriver implements UsbSerialDriver {
if (mConnection == null) {
throw new IOException("Already closed");
}
-
- // TODO: nothing sended on close, maybe needed?
-
+ synchronized (this) {
+ if (mUsbRequest != null)
+ mUsbRequest.cancel();
+ }
try {
mConnection.close();
} finally {
@@ -146,21 +158,33 @@ public class Ch34xSerialDriver implements UsbSerialDriver {
@Override
public int read(byte[] dest, int timeoutMillis) throws IOException {
- 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
+ final UsbRequest request = new UsbRequest();
+ try {
+ request.initialize(mConnection, mReadEndpoint);
+ final ByteBuffer buf = ByteBuffer.wrap(dest);
+ if (!request.queue(buf, dest.length)) {
+ throw new IOException("Error queueing request.");
+ }
+ mUsbRequest = request;
+ final UsbRequest response = mConnection.requestWait();
+ synchronized (this) {
+ 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;
}
- System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
+ } finally {
+ mUsbRequest = null;
+ request.close();
}
- return numBytesRead;
}
@Override
@@ -252,7 +276,7 @@ public class Ch34xSerialDriver implements UsbSerialDriver {
checkState("init #4", 0x95, 0x2518, new int[]{-1 /* 0x56, c3*/, 0x00});
- if (controlOut(0x9a, 0x2518, 0x0050) < 0) {
+ if (controlOut(0x9a, 0x2518, LCR_ENABLE_RX | LCR_ENABLE_TX | LCR_CS8) < 0) {
throw new IOException("init failed! #5");
}
@@ -271,36 +295,93 @@ public class Ch34xSerialDriver implements UsbSerialDriver {
private void setBaudRate(int baudRate) throws IOException {
- int[] baud = new int[]{2400, 0xd901, 0x0038, 4800, 0x6402,
- 0x001f, 9600, 0xb202, 0x0013, 19200, 0xd902, 0x000d, 38400,
- 0x6403, 0x000a, 115200, 0xcc03, 0x0008};
+ final long CH341_BAUDBASE_FACTOR = 1532620800;
+ final int CH341_BAUDBASE_DIVMAX = 3;
- for (int i = 0; i < baud.length / 3; i++) {
- if (baud[i * 3] == baudRate) {
- int ret = controlOut(0x9a, 0x1312, baud[i * 3 + 1]);
- if (ret < 0) {
- throw new IOException("Error setting baud rate. #1");
- }
- ret = controlOut(0x9a, 0x0f2c, baud[i * 3 + 2]);
- if (ret < 0) {
- throw new IOException("Error setting baud rate. #1");
+ long factor = CH341_BAUDBASE_FACTOR / baudRate;
+ int divisor = CH341_BAUDBASE_DIVMAX;
+
+ while ((factor > 0xfff0) && divisor > 0) {
+ factor >>= 3;
+ divisor--;
}
- return;
- }
+ if (factor > 0xfff0) {
+ throw new IOException("Baudrate " + baudRate + " not supported");
}
+ factor = 0x10000 - factor;
- throw new IOException("Baud rate " + baudRate + " currently not supported");
+ int ret = controlOut(0x9a, 0x1312, (int) ((factor & 0xff00) | divisor));
+ if (ret < 0) {
+ throw new IOException("Error setting baud rate. #1)");
+ }
+
+ ret = controlOut(0x9a, 0x0f2c, (int) (factor & 0xff));
+ if (ret < 0) {
+ throw new IOException("Error setting baud rate. #2");
+ }
}
-
@Override
public void setParameters(int baudRate, int dataBits, int stopBits, int parity)
throws IOException {
setBaudRate(baudRate);
- // TODO databit, stopbit and paraty set not implemented
+ int lcr = LCR_ENABLE_RX | LCR_ENABLE_TX;
+
+ switch (dataBits) {
+ case DATABITS_5:
+ lcr |= LCR_CS5;
+ break;
+ case DATABITS_6:
+ lcr |= LCR_CS6;
+ break;
+ case DATABITS_7:
+ lcr |= LCR_CS7;
+ break;
+ case DATABITS_8:
+ lcr |= LCR_CS8;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown dataBits value: " + dataBits);
+ }
+
+ switch (parity) {
+ case PARITY_NONE:
+ break;
+ case PARITY_ODD:
+ lcr |= LCR_ENABLE_PAR;
+ break;
+ case PARITY_EVEN:
+ lcr |= LCR_ENABLE_PAR | LCR_PAR_EVEN;
+ break;
+ case PARITY_MARK:
+ lcr |= LCR_ENABLE_PAR | LCR_MARK_SPACE;
+ break;
+ case PARITY_SPACE:
+ lcr |= LCR_ENABLE_PAR | LCR_MARK_SPACE | LCR_PAR_EVEN;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown parity value: " + parity);
+ }
+
+ switch (stopBits) {
+ case STOPBITS_1:
+ break;
+ case STOPBITS_1_5:
+ throw new IllegalArgumentException("Unsupported stopBits value: 1.5");
+ case STOPBITS_2:
+ lcr |= LCR_STOP_BITS_2;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown stopBits value: " + stopBits);
+ }
+
+ int ret = controlOut(0x9a, 0x2518, lcr);
+ if (ret < 0) {
+ throw new IOException("Error setting control byte");
+ }
}
@Override
@@ -345,11 +426,6 @@ public class Ch34xSerialDriver implements UsbSerialDriver {
writeHandshakeByte();
}
- @Override
- public boolean purgeHwBuffers(boolean purgeReadBuffers, boolean purgeWriteBuffers) throws IOException {
- return true;
- }
-
}
public static Map getSupportedDevices() {
diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CommonUsbSerialPort.java b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CommonUsbSerialPort.java
index d47c5a9..fb7ddc9 100644
--- a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CommonUsbSerialPort.java
+++ b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CommonUsbSerialPort.java
@@ -160,8 +160,8 @@ abstract class CommonUsbSerialPort implements UsbSerialPort {
public abstract void setRTS(boolean value) throws IOException;
@Override
- public boolean purgeHwBuffers(boolean flushReadBuffers, boolean flushWriteBuffers) throws IOException {
- return !flushReadBuffers && !flushWriteBuffers;
+ public boolean purgeHwBuffers(boolean purgeWriteBuffers, boolean purgeReadBuffers) throws IOException {
+ return false;
}
}
diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/Cp21xxSerialDriver.java b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/Cp21xxSerialDriver.java
index 1faf5df..8bd90f0 100644
--- a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/Cp21xxSerialDriver.java
+++ b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/Cp21xxSerialDriver.java
@@ -26,10 +26,12 @@ import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbRequest;
import android.util.Log;
import java.io.IOException;
-import java.util.Collections;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -39,11 +41,14 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
private static final String TAG = Cp21xxSerialDriver.class.getSimpleName();
private final UsbDevice mDevice;
- private final UsbSerialPort mPort;
+ private final List mPorts;
public Cp21xxSerialDriver(UsbDevice 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
@@ -53,7 +58,7 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
@Override
public List getPorts() {
- return Collections.singletonList(mPort);
+ return mPorts;
}
public class Cp21xxSerialPort extends CommonUsbSerialPort {
@@ -103,6 +108,11 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
private UsbEndpoint mReadEndpoint;
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) {
super(device, portNumber);
@@ -115,7 +125,7 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
private int setConfigSingle(int request, int 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
@@ -126,17 +136,15 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
mConnection = connection;
boolean opened = false;
+ mIsRestrictedPort = mDevice.getInterfaceCount() == 2 && mPortNumber == 1;
try {
- for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
- UsbInterface usbIface = mDevice.getInterface(i);
- if (mConnection.claimInterface(usbIface, true)) {
- Log.d(TAG, "claimInterface " + i + " SUCCESS");
- } else {
- Log.d(TAG, "claimInterface " + i + " FAIL");
- }
+ if(mPortNumber >= mDevice.getInterfaceCount()) {
+ throw new IOException("Unknown port number");
+ }
+ UsbInterface dataIface = mDevice.getInterface(mPortNumber);
+ if (!mConnection.claimInterface(dataIface, true)) {
+ throw new IOException("Could not claim interface " + mPortNumber);
}
-
- UsbInterface dataIface = mDevice.getInterface(mDevice.getInterfaceCount() - 1);
for (int i = 0; i < dataIface.getEndpointCount(); i++) {
UsbEndpoint ep = dataIface.getEndpoint(i);
if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
@@ -169,8 +177,16 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
if (mConnection == null) {
throw new IOException("Already closed");
}
+ synchronized (this) {
+ if(mUsbRequest != null) {
+ mUsbRequest.cancel();
+ }
+ }
try {
setConfigSingle(SILABSER_IFC_ENABLE_REQUEST_CODE, UART_DISABLE);
+ } catch (Exception ignored)
+ {}
+ try {
mConnection.close();
} finally {
mConnection = null;
@@ -179,21 +195,33 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
@Override
public int read(byte[] dest, int timeoutMillis) throws IOException {
- 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
+ final UsbRequest request = new UsbRequest();
+ try {
+ request.initialize(mConnection, mReadEndpoint);
+ final ByteBuffer buf = ByteBuffer.wrap(dest);
+ if (!request.queue(buf, dest.length)) {
+ throw new IOException("Error queueing request.");
+ }
+ mUsbRequest = request;
+ final UsbRequest response = mConnection.requestWait();
+ synchronized (this) {
+ 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;
}
- System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
+ } finally {
+ mUsbRequest = null;
+ request.close();
}
- return numBytesRead;
}
@Override
@@ -238,7 +266,7 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
(byte) ((baudRate >> 24) & 0xff)
};
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) {
throw new IOException("Error setting baud rate.");
}
@@ -252,38 +280,62 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
int configDataBits = 0;
switch (dataBits) {
case DATABITS_5:
+ if(mIsRestrictedPort)
+ throw new IllegalArgumentException("Unsupported dataBits value: " + dataBits);
configDataBits |= 0x0500;
break;
case DATABITS_6:
+ if(mIsRestrictedPort)
+ throw new IllegalArgumentException("Unsupported dataBits value: " + dataBits);
configDataBits |= 0x0600;
break;
case DATABITS_7:
+ if(mIsRestrictedPort)
+ throw new IllegalArgumentException("Unsupported dataBits value: " + dataBits);
configDataBits |= 0x0700;
break;
case DATABITS_8:
configDataBits |= 0x0800;
break;
default:
- configDataBits |= 0x0800;
- break;
+ throw new IllegalArgumentException("Unknown dataBits value: " + dataBits);
}
switch (parity) {
+ case PARITY_NONE:
+ break;
case PARITY_ODD:
configDataBits |= 0x0010;
break;
case PARITY_EVEN:
configDataBits |= 0x0020;
break;
+ case PARITY_MARK:
+ if(mIsRestrictedPort)
+ throw new IllegalArgumentException("Unsupported parity value: mark");
+ configDataBits |= 0x0030;
+ break;
+ case PARITY_SPACE:
+ if(mIsRestrictedPort)
+ throw new IllegalArgumentException("Unsupported parity value: space");
+ configDataBits |= 0x0040;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown parity value: " + parity);
}
switch (stopBits) {
case STOPBITS_1:
- configDataBits |= 0;
break;
+ case STOPBITS_1_5:
+ throw new IllegalArgumentException("Unsupported stopBits value: 1.5");
case STOPBITS_2:
+ if(mIsRestrictedPort)
+ throw new IllegalArgumentException("Unsupported stopBits value: 2");
configDataBits |= 2;
break;
+ default:
+ throw new IllegalArgumentException("Unknown stopBits value: " + stopBits);
}
setConfigSingle(SILABSER_SET_LINE_CTL_REQUEST_CODE, configDataBits);
}
@@ -327,8 +379,7 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
}
@Override
- public boolean purgeHwBuffers(boolean purgeReadBuffers,
- boolean purgeWriteBuffers) throws IOException {
+ public boolean purgeHwBuffers(boolean purgeWriteBuffers, boolean purgeReadBuffers) throws IOException {
int value = (purgeReadBuffers ? FLUSH_READ_CODE : 0)
| (purgeWriteBuffers ? FLUSH_WRITE_CODE : 0);
@@ -343,7 +394,7 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
public static Map getSupportedDevices() {
final Map supportedDevices = new LinkedHashMap();
- supportedDevices.put(Integer.valueOf(UsbId.VENDOR_SILABS),
+ supportedDevices.put(UsbId.VENDOR_SILABS,
new int[] {
UsbId.SILABS_CP2102,
UsbId.SILABS_CP2105,
diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/FtdiSerialDriver.java b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/FtdiSerialDriver.java
index 1a9a66b..60d0632 100644
--- a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/FtdiSerialDriver.java
+++ b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/FtdiSerialDriver.java
@@ -28,11 +28,9 @@ import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbRequest;
import android.util.Log;
-import com.hoho.android.usbserial.util.HexDump;
-
import java.io.IOException;
import java.nio.ByteBuffer;
-import java.util.Collections;
+import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -72,6 +70,8 @@ import java.util.Map;
* Supported and tested devices:
*
* - {@value DeviceType#TYPE_R}
+ * - {@value DeviceType#TYPE_2232H}
+ * - {@value DeviceType#TYPE_4232H}
*
*
*
@@ -79,8 +79,6 @@ import java.util.Map;
* feedback or patches):
*
* - {@value DeviceType#TYPE_2232C}
- * - {@value DeviceType#TYPE_2232H}
- * - {@value DeviceType#TYPE_4232H}
* - {@value DeviceType#TYPE_AM}
* - {@value DeviceType#TYPE_BM}
*
@@ -95,7 +93,7 @@ import java.util.Map;
public class FtdiSerialDriver implements UsbSerialDriver {
private final UsbDevice mDevice;
- private final UsbSerialPort mPort;
+ private final List mPorts;
/**
* FTDI chip types.
@@ -106,8 +104,12 @@ public class FtdiSerialDriver implements UsbSerialDriver {
public FtdiSerialDriver(UsbDevice 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
public UsbDevice getDevice() {
return mDevice;
@@ -115,7 +117,7 @@ public class FtdiSerialDriver implements UsbSerialDriver {
@Override
public List getPorts() {
- return Collections.singletonList(mPort);
+ return mPorts;
}
private class FtdiSerialPort extends CommonUsbSerialPort {
@@ -163,7 +165,7 @@ public class FtdiSerialDriver implements UsbSerialDriver {
private static final int SIO_SET_DATA_REQUEST = 4;
private static final int SIO_RESET_SIO = 0;
- private static final int SIO_RESET_PURGE_RX = 1;
+ private static final int SIO_RESET_PURGE_RX = 1; // RX @ FTDI device = write @ usb-serial-for-android library
private static final int SIO_RESET_PURGE_TX = 2;
public static final int FTDI_DEVICE_OUT_REQTYPE =
@@ -181,16 +183,7 @@ public class FtdiSerialDriver implements UsbSerialDriver {
private DeviceType mType;
- private int mInterface = 0; /* INTERFACE_ANY */
-
- private int mMaxPacketSize = 64; // TODO(mikey): detect
-
- /**
- * Due to http://b.android.com/28023 , we cannot use UsbRequest async reads
- * since it gives no indication of number of bytes read. Set this to
- * {@code true} on platforms where it is fixed.
- */
- private static final boolean ENABLE_ASYNC_READS = false;
+ private int mIndex = 0;
public FtdiSerialPort(UsbDevice device, int portNumber) {
super(device, portNumber);
@@ -210,10 +203,10 @@ public class FtdiSerialDriver implements UsbSerialDriver {
* @return The number of payload bytes
*/
private final int filterStatusBytes(byte[] src, byte[] dest, int totalBytesRead, int maxPacketSize) {
- final int packetsCount = totalBytesRead / maxPacketSize + (totalBytesRead % maxPacketSize == 0 ? 0 : 1);
+ final int packetsCount = (totalBytesRead + maxPacketSize -1 )/ maxPacketSize;
for (int packetIdx = 0; packetIdx < packetsCount; ++packetIdx) {
final int count = (packetIdx == (packetsCount - 1))
- ? (totalBytesRead % maxPacketSize) - MODEM_STATUS_HEADER_LENGTH
+ ? totalBytesRead - packetIdx * maxPacketSize - MODEM_STATUS_HEADER_LENGTH
: maxPacketSize - MODEM_STATUS_HEADER_LENGTH;
if (count > 0) {
System.arraycopy(src,
@@ -228,14 +221,21 @@ public class FtdiSerialDriver implements UsbSerialDriver {
}
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,
- SIO_RESET_SIO, 0 /* index */, null, 0, USB_WRITE_TIMEOUT_MILLIS);
+ SIO_RESET_SIO, mIndex, null, 0, USB_WRITE_TIMEOUT_MILLIS);
if (result != 0) {
throw new IOException("Reset failed: result=" + result);
}
-
- // TODO(mikey): autodetect.
- mType = DeviceType.TYPE_R;
}
@Override
@@ -247,12 +247,10 @@ public class FtdiSerialDriver implements UsbSerialDriver {
boolean opened = false;
try {
- for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
- if (connection.claimInterface(mDevice.getInterface(i), true)) {
- Log.d(TAG, "claimInterface " + i + " SUCCESS");
- } else {
- throw new IOException("Error claiming interface " + i);
- }
+ if (connection.claimInterface(mDevice.getInterface(mPortNumber), true)) {
+ Log.d(TAG, "claimInterface " + mPortNumber + " SUCCESS");
+ } else {
+ throw new IOException("Error claiming interface " + mPortNumber);
}
reset();
opened = true;
@@ -278,20 +276,12 @@ public class FtdiSerialDriver implements UsbSerialDriver {
@Override
public int read(byte[] dest, int timeoutMillis) throws IOException {
- final UsbEndpoint endpoint = mDevice.getInterface(0).getEndpoint(0);
-
- if (ENABLE_ASYNC_READS) {
- final int readAmt;
- synchronized (mReadBufferLock) {
- // mReadBuffer is only used for maximum read size.
- readAmt = Math.min(dest.length, mReadBuffer.length);
- }
-
- final UsbRequest request = new UsbRequest();
+ final UsbEndpoint endpoint = mDevice.getInterface(mPortNumber).getEndpoint(0);
+ final UsbRequest request = new UsbRequest();
+ final ByteBuffer buf = ByteBuffer.wrap(dest);
+ try {
request.initialize(mConnection, endpoint);
-
- final ByteBuffer buf = ByteBuffer.wrap(dest);
- if (!request.queue(buf, readAmt)) {
+ if (!request.queue(buf, dest.length)) {
throw new IOException("Error queueing request.");
}
@@ -299,34 +289,21 @@ public class FtdiSerialDriver implements UsbSerialDriver {
if (response == null) {
throw new IOException("Null response");
}
-
- final int payloadBytesRead = buf.position() - MODEM_STATUS_HEADER_LENGTH;
- if (payloadBytesRead > 0) {
- Log.d(TAG, HexDump.dumpHexString(dest, 0, Math.min(32, dest.length)));
- return payloadBytesRead;
- } else {
- return 0;
- }
- } else {
- final int totalBytesRead;
-
- synchronized (mReadBufferLock) {
- final int readAmt = Math.min(dest.length, mReadBuffer.length);
- totalBytesRead = mConnection.bulkTransfer(endpoint, mReadBuffer,
- readAmt, timeoutMillis);
-
- if (totalBytesRead < MODEM_STATUS_HEADER_LENGTH) {
- throw new IOException("Expected at least " + MODEM_STATUS_HEADER_LENGTH + " bytes");
- }
-
- return filterStatusBytes(mReadBuffer, dest, totalBytesRead, endpoint.getMaxPacketSize());
- }
+ } finally {
+ request.close();
}
+
+ final int totalBytesRead = buf.position();
+ if (totalBytesRead < MODEM_STATUS_HEADER_LENGTH) {
+ throw new IOException("Expected at least " + MODEM_STATUS_HEADER_LENGTH + " bytes");
+ }
+
+ return filterStatusBytes(dest, dest, totalBytesRead, endpoint.getMaxPacketSize());
}
@Override
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;
while (offset < src.length) {
@@ -379,7 +356,18 @@ public class FtdiSerialDriver implements UsbSerialDriver {
throws IOException {
setBaudRate(baudRate);
- int config = dataBits;
+ int config = 0;
+ switch (dataBits) {
+ case DATABITS_5:
+ case DATABITS_6:
+ throw new IllegalArgumentException("Unsupported dataBits value: " + dataBits);
+ case DATABITS_7:
+ case DATABITS_8:
+ config |= dataBits;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown dataBits value: " + dataBits);
+ }
switch (parity) {
case PARITY_NONE:
@@ -406,8 +394,7 @@ public class FtdiSerialDriver implements UsbSerialDriver {
config |= (0x00 << 11);
break;
case STOPBITS_1_5:
- config |= (0x01 << 11);
- break;
+ throw new IllegalArgumentException("Unsupported stopBits value: 1.5");
case STOPBITS_2:
config |= (0x02 << 11);
break;
@@ -416,7 +403,7 @@ public class FtdiSerialDriver implements UsbSerialDriver {
}
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);
if (result != 0) {
throw new IOException("Setting parameters failed: result=" + result);
@@ -496,9 +483,8 @@ public class FtdiSerialDriver implements UsbSerialDriver {
long index;
if (mType == DeviceType.TYPE_2232C || mType == DeviceType.TYPE_2232H
|| mType == DeviceType.TYPE_4232H) {
- index = (encodedDivisor >> 8) & 0xffff;
- index &= 0xFF00;
- index |= 0 /* TODO mIndex */;
+ index = (encodedDivisor >> 8) & 0xff00;
+ index |= mIndex;
} else {
index = (encodedDivisor >> 16) & 0xffff;
}
@@ -548,20 +534,20 @@ public class FtdiSerialDriver implements UsbSerialDriver {
}
@Override
- public boolean purgeHwBuffers(boolean purgeReadBuffers, boolean purgeWriteBuffers) throws IOException {
- if (purgeReadBuffers) {
+ public boolean purgeHwBuffers(boolean purgeWriteBuffers, boolean purgeReadBuffers) throws IOException {
+ if (purgeWriteBuffers) {
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) {
- throw new IOException("Flushing RX failed: result=" + result);
+ throw new IOException("purge write buffer failed: result=" + result);
}
}
- if (purgeWriteBuffers) {
+ if (purgeReadBuffers) {
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) {
- throw new IOException("Flushing RX failed: result=" + result);
+ throw new IOException("purge read buffer failed: result=" + result);
}
}
return true;
@@ -570,9 +556,12 @@ public class FtdiSerialDriver implements UsbSerialDriver {
public static Map getSupportedDevices() {
final Map supportedDevices = new LinkedHashMap();
- supportedDevices.put(Integer.valueOf(UsbId.VENDOR_FTDI),
+ supportedDevices.put(UsbId.VENDOR_FTDI,
new int[] {
UsbId.FTDI_FT232R,
+ UsbId.FTDI_FT232H,
+ UsbId.FTDI_FT2232H,
+ UsbId.FTDI_FT4232H,
UsbId.FTDI_FT231X,
});
return supportedDevices;
diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/ProlificSerialDriver.java b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/ProlificSerialDriver.java
index 01d3a85..24ae76e 100644
--- a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/ProlificSerialDriver.java
+++ b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/ProlificSerialDriver.java
@@ -32,10 +32,12 @@ import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbRequest;
import android.util.Log;
import java.io.IOException;
import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
@@ -86,7 +88,7 @@ public class ProlificSerialDriver implements UsbSerialDriver {
private static final int READ_ENDPOINT = 0x83;
private static final int INTERRUPT_ENDPOINT = 0x81;
- private static final int FLUSH_RX_REQUEST = 0x08;
+ private static final int FLUSH_RX_REQUEST = 0x08; // RX @ Prolific device = write @ usb-serial-for-android library
private static final int FLUSH_TX_REQUEST = 0x09;
private static final int SET_LINE_REQUEST = 0x20;
@@ -368,15 +370,28 @@ public class ProlificSerialDriver implements UsbSerialDriver {
@Override
public int read(byte[] dest, int timeoutMillis) throws IOException {
- synchronized (mReadBufferLock) {
- int readAmt = Math.min(dest.length, mReadBuffer.length);
- int numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer,
- readAmt, timeoutMillis);
- if (numBytesRead < 0) {
+ final UsbRequest request = new UsbRequest();
+ try {
+ request.initialize(mConnection, mReadEndpoint);
+ final ByteBuffer buf = ByteBuffer.wrap(dest);
+ 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;
}
- System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
- return numBytesRead;
+ } finally {
+ request.close();
}
}
@@ -538,22 +553,22 @@ public class ProlificSerialDriver implements UsbSerialDriver {
}
@Override
- public boolean purgeHwBuffers(boolean purgeReadBuffers, boolean purgeWriteBuffers) throws IOException {
- if (purgeReadBuffers) {
+ public boolean purgeHwBuffers(boolean purgeWriteBuffers, boolean purgeReadBuffers) throws IOException {
+ if (purgeWriteBuffers) {
vendorOut(FLUSH_RX_REQUEST, 0, null);
}
- if (purgeWriteBuffers) {
+ if (purgeReadBuffers) {
vendorOut(FLUSH_TX_REQUEST, 0, null);
}
- return purgeReadBuffers || purgeWriteBuffers;
+ return true;
}
}
public static Map getSupportedDevices() {
final Map supportedDevices = new LinkedHashMap();
- supportedDevices.put(Integer.valueOf(UsbId.VENDOR_PROLIFIC),
+ supportedDevices.put(UsbId.VENDOR_PROLIFIC,
new int[] { UsbId.PROLIFIC_PL2303, });
return supportedDevices;
}
diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/UsbId.java b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/UsbId.java
index c4050e1..7bba4db 100644
--- a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/UsbId.java
+++ b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/UsbId.java
@@ -33,6 +33,9 @@ public final class UsbId {
public static final int VENDOR_FTDI = 0x0403;
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 VENDOR_ATMEL = 0x03EB;
@@ -68,6 +71,10 @@ public final class UsbId {
public static final int VENDOR_QINHENG = 0x1a86;
public static final int QINHENG_HL340 = 0x7523;
+ // at www.linux-usb.org/usb.ids listed for NXP/LPC1768, but all processors supported by ARM mbed DAPLink firmware report these ids
+ public static final int VENDOR_ARM = 0x0d28;
+ public static final int ARM_MBED = 0x0204;
+
private UsbId() {
throw new IllegalAccessError("Non-instantiable class.");
}
diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/UsbSerialPort.java b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/UsbSerialPort.java
index 1341143..5cd3ca5 100644
--- a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/UsbSerialPort.java
+++ b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/UsbSerialPort.java
@@ -216,13 +216,13 @@ public interface UsbSerialPort {
public void setRTS(boolean value) throws IOException;
/**
- * Flush non-transmitted output data and / or non-read input data
- * @param flushRX {@code true} to flush non-transmitted output data
- * @param flushTX {@code true} to flush non-read input data
+ * purge non-transmitted output data and / or non-read input data
+ * @param purgeWriteBuffers {@code true} to discard non-transmitted output data
+ * @param purgeReadBuffers {@code true} to discard non-read input data
* @return {@code true} if the operation was successful, or
* {@code false} if the operation is not supported by the driver or device
* @throws IOException if an error occurred during flush
*/
- public boolean purgeHwBuffers(boolean flushRX, boolean flushTX) throws IOException;
+ public boolean purgeHwBuffers(boolean purgeWriteBuffers, boolean purgeReadBuffers) throws IOException;
}
diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/UsbSerialRuntimeException.java b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/UsbSerialRuntimeException.java
deleted file mode 100644
index b48607c..0000000
--- a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/UsbSerialRuntimeException.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2011 Google Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
- */
-
-package com.hoho.android.usbserial.driver;
-
-/**
- * Generic unchecked exception for the usbserial package.
- *
- * @author mike wakerly (opensource@hoho.com)
- */
-@SuppressWarnings("serial")
-public class UsbSerialRuntimeException extends RuntimeException {
-
- public UsbSerialRuntimeException() {
- super();
- }
-
- public UsbSerialRuntimeException(String detailMessage, Throwable throwable) {
- super(detailMessage, throwable);
- }
-
- public UsbSerialRuntimeException(String detailMessage) {
- super(detailMessage);
- }
-
- public UsbSerialRuntimeException(Throwable throwable) {
- super(throwable);
- }
-
-}
diff --git a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/util/SerialInputOutputManager.java b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/util/SerialInputOutputManager.java
index 51c5655..1ab3edf 100644
--- a/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/util/SerialInputOutputManager.java
+++ b/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/util/SerialInputOutputManager.java
@@ -50,7 +50,7 @@ public class SerialInputOutputManager implements Runnable {
// Synchronized by 'mWriteBuffer'
private final ByteBuffer mWriteBuffer = ByteBuffer.allocate(BUFSIZ);
- private enum State {
+ public enum State {
STOPPED,
RUNNING,
STOPPING
@@ -111,7 +111,7 @@ public class SerialInputOutputManager implements Runnable {
}
}
- private synchronized State getState() {
+ public synchronized State getState() {
return mState;
}
@@ -120,7 +120,6 @@ public class SerialInputOutputManager implements Runnable {
* called, or until a driver exception is raised.
*
* NOTE(mikey): Uses inefficient read/write-with-timeout.
- * TODO(mikey): Read asynchronously with {@link UsbRequest#queue(ByteBuffer, int)}
*/
@Override
public void run() {