├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── .idea
├── .gitignore
├── AndroidProjectSystem.xml
├── compiler.xml
├── misc.xml
├── runConfigurations.xml
└── vcs.xml
├── CHANGELOG.txt
├── LICENSE.txt
├── README.md
├── build.gradle
├── codecov.yml
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── jitpack.yml
├── settings.gradle
├── test
├── arduino_leonardo_bridge
│ └── arduino_leonardo_bridge.ino
├── arduino_leonardo_bridge_castrated_cdc
│ ├── CDC.cpp
│ ├── README.md
│ ├── USBCore.h
│ └── arduino_leonardo_bridge_castrated_cdc.ino
├── arduino_leonardo_bridge_multi_cdc
│ ├── CDC.cpp
│ ├── README.md
│ ├── USBCore.cpp
│ ├── USBCore.h
│ ├── USBDesc.h
│ └── arduino_leonardo_bridge_multi_cdc.ino
├── pi_pico
│ ├── README.md
│ └── tinyusb_dev_cdc_dual_ports.uf2
└── rfc2217_server.diff
├── usbSerialExamples
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── hoho
│ │ └── android
│ │ └── usbserial
│ │ └── examples
│ │ ├── CustomProber.java
│ │ ├── DevicesFragment.java
│ │ ├── MainActivity.java
│ │ └── TerminalFragment.java
│ └── res
│ ├── drawable
│ ├── ic_delete_white_24dp.xml
│ └── ic_send_white_24dp.xml
│ ├── layout
│ ├── activity_main.xml
│ ├── device_list_header.xml
│ ├── device_list_item.xml
│ └── fragment_terminal.xml
│ ├── menu
│ ├── menu_devices.xml
│ └── menu_terminal.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxxhdpi
│ └── ic_launcher.png
│ ├── values
│ ├── arrays.xml
│ ├── colors.xml
│ ├── strings.xml
│ └── styles.xml
│ └── xml
│ └── device_filter.xml
└── usbSerialForAndroid
├── build.gradle
├── coverage.gradle
├── proguard-rules.pro
└── src
├── androidTest
├── AndroidManifest.xml
└── java
│ └── com
│ └── hoho
│ └── android
│ └── usbserial
│ ├── CrossoverTest.java
│ ├── DeviceTest.java
│ ├── driver
│ ├── CommonUsbSerialPortWrapper.java
│ └── ProlificSerialPortWrapper.java
│ └── util
│ ├── TelnetWrapper.java
│ ├── TestBuffer.java
│ └── UsbWrapper.java
├── main
├── AndroidManifest.xml
└── java
│ └── com
│ └── hoho
│ └── android
│ └── usbserial
│ ├── driver
│ ├── CdcAcmSerialDriver.java
│ ├── Ch34xSerialDriver.java
│ ├── ChromeCcdSerialDriver.java
│ ├── CommonUsbSerialPort.java
│ ├── Cp21xxSerialDriver.java
│ ├── FtdiSerialDriver.java
│ ├── GsmModemSerialDriver.java
│ ├── ProbeTable.java
│ ├── ProlificSerialDriver.java
│ ├── SerialTimeoutException.java
│ ├── UsbId.java
│ ├── UsbSerialDriver.java
│ ├── UsbSerialPort.java
│ └── UsbSerialProber.java
│ └── util
│ ├── HexDump.java
│ ├── MonotonicClock.java
│ ├── SerialInputOutputManager.java
│ ├── UsbUtils.java
│ └── XonXoffFilter.java
└── test
└── java
├── android
└── util
│ ├── Log.java
│ └── Pair.java
└── com
└── hoho
└── android
└── usbserial
├── driver
├── CdcAcmSerialDriverTest.java
└── FtdiSerialDriverTest.java
└── util
├── HexDumpText.java
└── SerialInputOutputManagerTest.java
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v3
12 | - uses: actions/setup-java@v3
13 | with:
14 | distribution: 'temurin'
15 | java-version: '17'
16 | - name: Build with Gradle
17 | run: ./gradlew assembleDebug
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 |
15 | # Studio/Gradle
16 | *.iml
17 | .gradle/
18 | build/
19 |
20 | # User-specific stuff:
21 | .idea/workspace.xml
22 | .idea/tasks.xml
23 | .idea/dictionaries
24 | .idea/markdown-navigator*.xml
25 |
26 | # Sensitive or high-churn files:
27 | .idea/dataSources.ids
28 | .idea/dataSources.xml
29 | .idea/sqlDataSources.xml
30 | .idea/dynamic.xml
31 | .idea/uiDesigner.xml
32 |
33 | # Gradle:
34 | .idea/gradle.xml
35 | .idea/libraries
36 | .idea/jarRepositories.xml
37 |
38 | # Eclipse/Android/Misc
39 | .metadata/
40 | local.properties
41 | *.DS_Store
42 | proguard/
43 | jacoco.exec
44 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | caches
2 | codeStyles
3 | libraries
4 | workspace.xml
5 | androidTestResultsUserPreferences.xml
6 | appInsightsSettings.xml
7 | deploymentTargetDropDown.xml
8 | deploymentTargetSelector.xml
9 | other.xml
10 |
--------------------------------------------------------------------------------
/.idea/AndroidProjectSystem.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
31 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
16 |
17 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/CHANGELOG.txt:
--------------------------------------------------------------------------------
1 | For recent changelog look here:
2 | * https://github.com/mik3y/usb-serial-for-android/releases
3 |
4 | v0.2.0 (unreleased)
5 | * Gradle, Android Studio support.
6 | * New driver: CP2102 (thanks Ducky).
7 | * New prober support: LUFA Virtual Serial, Leaflabs Maple, Teensyduino.
8 | * New driver methods: getCD, getCTS, getDSR, getDTR, setDTR, getRI, getRTS,
9 | setRTS.
10 | * API change: setBaudrate() has been removed; use setParameters().
11 | * API change: open() no longer implicitly sets the baud rate. Clients should
12 | call setParameters() immediately after open(), when necessary.
13 | * Library version is available in `com.hoho.android.usbserial.BuildInfo`.
14 |
15 | v0.1.0 (2012-10-12)
16 | * New driver: CdcAcmSerialDriver.
17 | * Better tuned read and write buffer sizes.
18 |
19 | v0.0.1 (2011-12-28)
20 | * Initial release.
21 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2011-2013 Google Inc.
4 | Copyright (c) 2013 Mike Wakerly
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/mik3y/usb-serial-for-android/actions)
2 | [](https://jitpack.io/#mik3y/usb-serial-for-android)
3 | [](https://app.codacy.com/gh/kai-morich/usb-serial-for-android-mik3y/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
4 | [](https://codecov.io/gh/mik3y/usb-serial-for-android)
5 |
6 | # usb-serial-for-android
7 |
8 | This is a driver library for communication with Arduinos and other USB serial hardware on
9 | Android, using the
10 | [Android USB Host Mode (OTG)](http://developer.android.com/guide/topics/connectivity/usb/host.html)
11 | available since Android 3.1 and working reliably since Android 4.2.
12 |
13 | No root access, ADK, or special kernel drivers are required; all drivers are implemented in
14 | Java. You get a raw serial port with `read()`, `write()`, and [other functions](https://github.com/mik3y/usb-serial-for-android/wiki/FAQ#Feature_Matrix) for use with your own protocols.
15 |
16 | ## Quick Start
17 |
18 | **1.** Add library to your project:
19 |
20 | Add jitpack.io repository to your root build.gradle:
21 | ```gradle
22 | allprojects {
23 | repositories {
24 | ...
25 | maven { url 'https://jitpack.io' }
26 | }
27 | }
28 | ```
29 |
30 | Starting with gradle 6.8 you can alternatively add jitpack.io repository to your settings.gradle:
31 | ```gradle
32 | dependencyResolutionManagement {
33 | repositories {
34 | ...
35 | maven { url 'https://jitpack.io' }
36 | }
37 | }
38 | ```
39 |
40 | If using gradle kotlin use line
41 | ```gradle.kts
42 | maven(url = "https://jitpack.io")
43 | ```
44 |
45 | Add library to dependencies
46 | ```gradle
47 | dependencies {
48 | implementation 'com.github.mik3y:usb-serial-for-android:3.9.0'
49 | }
50 | ```
51 |
52 | **2.** If the app should be notified when a device is attached, add
53 | [device_filter.xml](https://github.com/mik3y/usb-serial-for-android/blob/master/usbSerialExamples/src/main/res/xml/device_filter.xml)
54 | to your project's `res/xml/` directory and configure in your `AndroidManifest.xml`.
55 |
56 | ```xml
57 |
60 |
61 |
62 |
63 |
66 |
67 | ```
68 |
69 | **3.** Use it! Example code snippet:
70 |
71 | open device:
72 | ```java
73 | // Find all available drivers from attached devices.
74 | UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
75 | List availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager);
76 | if (availableDrivers.isEmpty()) {
77 | return;
78 | }
79 |
80 | // Open a connection to the first available driver.
81 | UsbSerialDriver driver = availableDrivers.get(0);
82 | UsbDeviceConnection connection = manager.openDevice(driver.getDevice());
83 | if (connection == null) {
84 | // add UsbManager.requestPermission(driver.getDevice(), ..) handling here
85 | return;
86 | }
87 |
88 | UsbSerialPort port = driver.getPorts().get(0); // Most devices have just one port (port 0)
89 | port.open(connection);
90 | port.setParameters(115200, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE);
91 | ```
92 | then use direct read/write
93 | ```java
94 | port.write(request, WRITE_WAIT_MILLIS);
95 | len = port.read(response, READ_WAIT_MILLIS);
96 | ```
97 | or direct write + event driven read:
98 | ```java
99 | usbIoManager = new SerialInputOutputManager(usbSerialPort, this);
100 | usbIoManager.start();
101 | ...
102 | port.write("hello".getBytes(), WRITE_WAIT_MILLIS);
103 |
104 | @Override
105 | public void onNewData(byte[] data) {
106 | runOnUiThread(() -> { textView.append(new String(data)); });
107 | }
108 | ```
109 | and finally:
110 | ```java
111 | port.close();
112 | ```
113 |
114 | For a simple example, see
115 | [UsbSerialExamples](https://github.com/mik3y/usb-serial-for-android/blob/master/usbSerialExamples)
116 | folder in this project.
117 |
118 | See separate github project [SimpleUsbTerminal](https://github.com/kai-morich/SimpleUsbTerminal)
119 | for a more complete example with:
120 | * Background service to stay connected while the app is not visible or rotating
121 | * Flow control
122 |
123 | ## Probing for Unrecognized Devices
124 |
125 | Sometimes you may need to do a little extra work to support devices which
126 | usb-serial-for-android doesn't (yet) know about -- but which you know to be
127 | compatible with one of the built-in drivers. This may be the case for a brand
128 | new device or for one using a custom VID/PID pair.
129 |
130 | UsbSerialProber is a class to help you find and instantiate compatible
131 | UsbSerialDrivers from the tree of connected UsbDevices. Normally, you will use
132 | the default prober returned by ``UsbSerialProber.getDefaultProber()``, which
133 | uses USB interface types and the built-in list of well-known VIDs and PIDs that
134 | are supported by our drivers.
135 |
136 | To use your own set of rules, create and use a custom prober:
137 |
138 | ```java
139 | // Probe for our custom FTDI device, which use VID 0x1234 and PID 0x0001 and 0x0002.
140 | ProbeTable customTable = new ProbeTable();
141 | customTable.addProduct(0x1234, 0x0001, FtdiSerialDriver.class);
142 | customTable.addProduct(0x1234, 0x0002, FtdiSerialDriver.class);
143 |
144 | UsbSerialProber prober = new UsbSerialProber(customTable);
145 | List drivers = prober.findAllDrivers(usbManager);
146 | // ...
147 | ```
148 | *Note*: as of v3.5.0 this library detects CDC/ACM devices by USB interface types instead of fixed VID+PID,
149 | so custom probers are typically not required any more for CDC/ACM devices.
150 |
151 | Of course, nothing requires you to use UsbSerialProber at all: you can
152 | instantiate driver classes directly if you know what you're doing; just supply
153 | a compatible UsbDevice.
154 |
155 | ## Compatible Devices
156 |
157 | This library supports USB to serial converter chips with specific drivers
158 | * FTDI FT232R, FT232H, FT2232H, FT4232H, FT230X, FT231X, FT234XD
159 | * Prolific PL2303
160 | * Silabs CP2102, CP210*
161 | * Qinheng CH340, CH341A
162 |
163 | some other device specific drivers
164 | * GsmModem devices, e.g. for Unisoc based Fibocom GSM modems
165 | * Chrome OS CCD (Closed Case Debugging)
166 |
167 | and devices implementing the generic CDC/ACM protocol like
168 | * Qinheng CH9102
169 | * Microchip MCP2221
170 | * Arduino using ATmega32U4
171 | * Digispark using V-USB software USB
172 | * ...
173 |
174 | ## Help & Discussion
175 |
176 | For common problems, see the [FAQ](https://github.com/mik3y/usb-serial-for-android/wiki/FAQ) wiki page.
177 |
178 | Are you using the library? Add your project to
179 | [ProjectsUsingUsbSerialForAndroid](https://github.com/mik3y/usb-serial-for-android/wiki/Projects-Using-usb-serial-for-android).
180 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level gradle script.
2 |
3 | buildscript {
4 | repositories {
5 | mavenCentral()
6 | google()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:8.9.1'
10 | }
11 | }
12 |
13 | allprojects {
14 | repositories {
15 | mavenCentral()
16 | google()
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | codecov:
2 | max_report_age: off
3 | require_ci_to_pass: no
4 | notify:
5 | wait_for_ci: no
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | android.enableJetifier=true
2 | android.useAndroidX=true
3 | android.defaults.buildfeatures.buildconfig=true
4 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mik3y/usb-serial-for-android/c608aadc59d2d09f5e34ab928de42c16bf69864f/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Oct 13 21:20:09 CEST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 | # Determine the Java command to use to start the JVM.
86 | if [ -n "$JAVA_HOME" ] ; then
87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
88 | # IBM's JDK on AIX uses strange locations for the executables
89 | JAVACMD="$JAVA_HOME/jre/sh/java"
90 | else
91 | JAVACMD="$JAVA_HOME/bin/java"
92 | fi
93 | if [ ! -x "$JAVACMD" ] ; then
94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
95 |
96 | Please set the JAVA_HOME variable in your environment to match the
97 | location of your Java installation."
98 | fi
99 | else
100 | JAVACMD="java"
101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
102 |
103 | Please set the JAVA_HOME variable in your environment to match the
104 | location of your Java installation."
105 | fi
106 |
107 | # Increase the maximum file descriptors if we can.
108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
109 | MAX_FD_LIMIT=`ulimit -H -n`
110 | if [ $? -eq 0 ] ; then
111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
112 | MAX_FD="$MAX_FD_LIMIT"
113 | fi
114 | ulimit -n $MAX_FD
115 | if [ $? -ne 0 ] ; then
116 | warn "Could not set maximum file descriptor limit: $MAX_FD"
117 | fi
118 | else
119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
120 | fi
121 | fi
122 |
123 | # For Darwin, add options to specify how the application appears in the dock
124 | if $darwin; then
125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
126 | fi
127 |
128 | # For Cygwin, switch paths to Windows format before running java
129 | if $cygwin ; then
130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
132 | JAVACMD=`cygpath --unix "$JAVACMD"`
133 |
134 | # We build the pattern for arguments to be converted via cygpath
135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
136 | SEP=""
137 | for dir in $ROOTDIRSRAW ; do
138 | ROOTDIRS="$ROOTDIRS$SEP$dir"
139 | SEP="|"
140 | done
141 | OURCYGPATTERN="(^($ROOTDIRS))"
142 | # Add a user-defined pattern to the cygpath arguments
143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
145 | fi
146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
147 | i=0
148 | for arg in "$@" ; do
149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
151 |
152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
154 | else
155 | eval `echo args$i`="\"$arg\""
156 | fi
157 | i=$((i+1))
158 | done
159 | case $i in
160 | (0) set -- ;;
161 | (1) set -- "$args0" ;;
162 | (2) set -- "$args0" "$args1" ;;
163 | (3) set -- "$args0" "$args1" "$args2" ;;
164 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
165 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
166 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
167 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
168 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
169 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
170 | esac
171 | fi
172 |
173 | # Escape application args
174 | save () {
175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
176 | echo " "
177 | }
178 | APP_ARGS=$(save "$@")
179 |
180 | # Collect all arguments for the java command, following the shell quoting and substitution rules
181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
182 |
183 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
184 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
185 | cd "$(dirname "$0")"
186 | fi
187 |
188 | exec "$JAVACMD" "$@"
189 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem http://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
34 |
35 | @rem Find java.exe
36 | if defined JAVA_HOME goto findJavaFromJavaHome
37 |
38 | set JAVA_EXE=java.exe
39 | %JAVA_EXE% -version >NUL 2>&1
40 | if "%ERRORLEVEL%" == "0" goto init
41 |
42 | echo.
43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
44 | echo.
45 | echo Please set the JAVA_HOME variable in your environment to match the
46 | echo location of your Java installation.
47 |
48 | goto fail
49 |
50 | :findJavaFromJavaHome
51 | set JAVA_HOME=%JAVA_HOME:"=%
52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
53 |
54 | if exist "%JAVA_EXE%" goto init
55 |
56 | echo.
57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
58 | echo.
59 | echo Please set the JAVA_HOME variable in your environment to match the
60 | echo location of your Java installation.
61 |
62 | goto fail
63 |
64 | :init
65 | @rem Get command-line arguments, handling Windows variants
66 |
67 | if not "%OS%" == "Windows_NT" goto win9xME_args
68 |
69 | :win9xME_args
70 | @rem Slurp the command line arguments.
71 | set CMD_LINE_ARGS=
72 | set _SKIP=2
73 |
74 | :win9xME_args_slurp
75 | if "x%~1" == "x" goto execute
76 |
77 | set CMD_LINE_ARGS=%*
78 |
79 | :execute
80 | @rem Setup the command line
81 |
82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
83 |
84 | @rem Execute Gradle
85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
86 |
87 | :end
88 | @rem End local scope for the variables with windows NT shell
89 | if "%ERRORLEVEL%"=="0" goto mainEnd
90 |
91 | :fail
92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
93 | rem the _cmd.exe /c_ return code!
94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
95 | exit /b 1
96 |
97 | :mainEnd
98 | if "%OS%"=="Windows_NT" endlocal
99 |
100 | :omega
101 |
--------------------------------------------------------------------------------
/jitpack.yml:
--------------------------------------------------------------------------------
1 | jdk:
2 | - openjdk17
3 | install:
4 | - ./gradlew :usbSerialForAndroid:publishToMavenLocal
5 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include 'usbSerialForAndroid', 'usbSerialExamples'
2 |
--------------------------------------------------------------------------------
/test/arduino_leonardo_bridge/arduino_leonardo_bridge.ino:
--------------------------------------------------------------------------------
1 | /*
2 | bridge USB-serial to hardware-serial
3 |
4 | for Arduinos based on ATmega32u4 (Leonardo and compatible Pro Micro, Micro)
5 | hardware serial is configured with baud-rate, databits, stopbits, parity as send over USB
6 |
7 | see https://github.com/arduino/Arduino/tree/master/hardware/arduino/avr/cores/arduino
8 | -> CDC.cpp|HardwareSerial.cpp for serial implementation details
9 |
10 | this sketch is mainly for demonstration / test of CDC communication
11 | performance as real usb-serial bridge would be inacceptable as each byte is send in separate USB packet
12 | */
13 |
14 | uint32_t baud = 9600;
15 | uint8_t databits = 8;
16 | uint8_t stopbits = 1;
17 | uint8_t parity = 0;
18 |
19 | void setup() {
20 | Serial.begin(baud); // USB
21 | Serial1.begin(baud, SERIAL_8N1);
22 | }
23 |
24 | void loop() {
25 | // show USB connected state
26 | if (Serial) TXLED1;
27 | else TXLED0;
28 |
29 | // configure hardware serial
30 | if (Serial.baud() != baud ||
31 | Serial.numbits() != databits ||
32 | Serial.stopbits() != stopbits ||
33 | Serial.paritytype() != parity) {
34 | baud = Serial.baud();
35 | databits = Serial.numbits();
36 | stopbits = Serial.stopbits();
37 | parity = Serial.paritytype();
38 | uint8_t config = 0; // ucsrc register
39 | switch (databits) {
40 | case 5: break;
41 | case 6: config |= 2; break;
42 | case 7: config |= 4; break;
43 | case 8: config |= 6; break;
44 | default: config |= 6;
45 | }
46 | switch (stopbits) {
47 | case 2: config |= 8;
48 | // 1.5 stopbits not supported
49 | }
50 | switch (parity) {
51 | case 1: config |= 0x30; break; // odd
52 | case 2: config |= 0x20; break; // even
53 | // mark, space not supported
54 | }
55 | Serial1.end();
56 | Serial1.begin(baud, config);
57 | }
58 |
59 | // bridge
60 | if (Serial.available() > 0)
61 | Serial1.write(Serial.read());
62 | if (Serial1.available() > 0)
63 | Serial.write(Serial1.read());
64 | }
65 |
--------------------------------------------------------------------------------
/test/arduino_leonardo_bridge_castrated_cdc/CDC.cpp:
--------------------------------------------------------------------------------
1 |
2 |
3 | /* Copyright (c) 2011, Peter Barrett
4 | **
5 | ** Permission to use, copy, modify, and/or distribute this software for
6 | ** any purpose with or without fee is hereby granted, provided that the
7 | ** above copyright notice and this permission notice appear in all copies.
8 | **
9 | ** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10 | ** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11 | ** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
12 | ** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
13 | ** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
14 | ** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
15 | ** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
16 | ** SOFTWARE.
17 | */
18 |
19 | #include "USBCore.h" // kai:added
20 | #include "USBAPI.h"
21 | #include
22 | #include
23 |
24 | #if defined(USBCON)
25 |
26 | typedef struct
27 | {
28 | u32 dwDTERate;
29 | u8 bCharFormat;
30 | u8 bParityType;
31 | u8 bDataBits;
32 | u8 lineState;
33 | } LineInfo;
34 |
35 | static volatile LineInfo _usbLineInfo = { 57600, 0x00, 0x00, 0x00, 0x00 };
36 | static volatile int32_t breakValue = -1;
37 |
38 | static u8 wdtcsr_save;
39 |
40 | #define WEAK __attribute__ ((weak))
41 |
42 | extern const CDCDescriptor _cdcInterface PROGMEM;
43 | const CDCDescriptor _cdcInterface =
44 | {
45 | D_IAD(0,2,CDC_COMMUNICATION_INTERFACE_CLASS,CDC_ABSTRACT_CONTROL_MODEL,1),
46 |
47 | // CDC communication interface
48 | D_INTERFACE(CDC_ACM_INTERFACE,3,CDC_COMMUNICATION_INTERFACE_CLASS,CDC_ABSTRACT_CONTROL_MODEL,0), // kai
49 | D_CDCCS(CDC_HEADER,0x10,0x01), // Header (1.10 bcd)
50 | D_CDCCS(CDC_CALL_MANAGEMENT,1,1), // Device handles call management (not)
51 | D_CDCCS4(CDC_ABSTRACT_CONTROL_MANAGEMENT,6), // SET_LINE_CODING, GET_LINE_CODING, SET_CONTROL_LINE_STATE supported
52 | D_CDCCS(CDC_UNION,CDC_ACM_INTERFACE,CDC_DATA_INTERFACE), // Communication interface is master, data interface is slave 0
53 | D_ENDPOINT(USB_ENDPOINT_IN (CDC_ENDPOINT_ACM),USB_ENDPOINT_TYPE_INTERRUPT,0x10,0x40),
54 |
55 | // CDC data interface
56 | //D_INTERFACE(CDC_DATA_INTERFACE,2,CDC_DATA_INTERFACE_CLASS,0,0), // kai:removed
57 | D_ENDPOINT(USB_ENDPOINT_OUT(CDC_ENDPOINT_OUT),USB_ENDPOINT_TYPE_BULK,USB_EP_SIZE,0),
58 | D_ENDPOINT(USB_ENDPOINT_IN (CDC_ENDPOINT_IN ),USB_ENDPOINT_TYPE_BULK,USB_EP_SIZE,0)
59 | };
60 |
61 | bool isLUFAbootloader()
62 | {
63 | return pgm_read_word(FLASHEND - 1) == NEW_LUFA_SIGNATURE;
64 | }
65 |
66 | int CDC_GetInterface(u8* interfaceNum)
67 | {
68 | interfaceNum[0] += 1; // kai
69 | return USB_SendControl(TRANSFER_PGM,&_cdcInterface,sizeof(_cdcInterface));
70 | }
71 |
72 | bool CDC_Setup(USBSetup& setup)
73 | {
74 | u8 r = setup.bRequest;
75 | u8 requestType = setup.bmRequestType;
76 |
77 | if (REQUEST_DEVICETOHOST_CLASS_INTERFACE == requestType)
78 | {
79 | if (CDC_GET_LINE_CODING == r)
80 | {
81 | USB_SendControl(0,(void*)&_usbLineInfo,7);
82 | return true;
83 | }
84 | }
85 |
86 | if (REQUEST_HOSTTODEVICE_CLASS_INTERFACE == requestType)
87 | {
88 | if (CDC_SEND_BREAK == r)
89 | {
90 | breakValue = ((uint16_t)setup.wValueH << 8) | setup.wValueL;
91 | }
92 |
93 | if (CDC_SET_LINE_CODING == r)
94 | {
95 | USB_RecvControl((void*)&_usbLineInfo,7);
96 | }
97 |
98 | if (CDC_SET_CONTROL_LINE_STATE == r)
99 | {
100 | _usbLineInfo.lineState = setup.wValueL;
101 |
102 | // auto-reset into the bootloader is triggered when the port, already
103 | // open at 1200 bps, is closed. this is the signal to start the watchdog
104 | // with a relatively long period so it can finish housekeeping tasks
105 | // like servicing endpoints before the sketch ends
106 |
107 | uint16_t magic_key_pos = MAGIC_KEY_POS;
108 |
109 | // If we don't use the new RAMEND directly, check manually if we have a newer bootloader.
110 | // This is used to keep compatible with the old leonardo bootloaders.
111 | // You are still able to set the magic key position manually to RAMEND-1 to save a few bytes for this check.
112 | #if MAGIC_KEY_POS != (RAMEND-1)
113 | // For future boards save the key in the inproblematic RAMEND
114 | // Which is reserved for the main() return value (which will never return)
115 | if (isLUFAbootloader()) {
116 | // horray, we got a new bootloader!
117 | magic_key_pos = (RAMEND-1);
118 | }
119 | #endif
120 |
121 | // We check DTR state to determine if host port is open (bit 0 of lineState).
122 | if (1200 == _usbLineInfo.dwDTERate && (_usbLineInfo.lineState & 0x01) == 0)
123 | {
124 | #if MAGIC_KEY_POS != (RAMEND-1)
125 | // Backup ram value if its not a newer bootloader and it hasn't already been saved.
126 | // This should avoid memory corruption at least a bit, not fully
127 | if (magic_key_pos != (RAMEND-1) && *(uint16_t *)magic_key_pos != MAGIC_KEY) {
128 | *(uint16_t *)(RAMEND-1) = *(uint16_t *)magic_key_pos;
129 | }
130 | #endif
131 | // Store boot key
132 | *(uint16_t *)magic_key_pos = MAGIC_KEY;
133 | // Save the watchdog state in case the reset is aborted.
134 | wdtcsr_save = WDTCSR;
135 | wdt_enable(WDTO_120MS);
136 | }
137 | else if (*(uint16_t *)magic_key_pos == MAGIC_KEY)
138 | {
139 | // Most OSs do some intermediate steps when configuring ports and DTR can
140 | // twiggle more than once before stabilizing.
141 | // To avoid spurious resets we set the watchdog to 120ms and eventually
142 | // cancel if DTR goes back high.
143 | // Cancellation is only done if an auto-reset was started, which is
144 | // indicated by the magic key having been set.
145 |
146 | wdt_reset();
147 | // Restore the watchdog state in case the sketch was using it.
148 | WDTCSR |= (1<= 0) {
185 | return 1 + USB_Available(CDC_RX);
186 | }
187 | return USB_Available(CDC_RX);
188 | }
189 |
190 | int Serial_::peek(void)
191 | {
192 | if (peek_buffer < 0)
193 | peek_buffer = USB_Recv(CDC_RX);
194 | return peek_buffer;
195 | }
196 |
197 | int Serial_::read(void)
198 | {
199 | if (peek_buffer >= 0) {
200 | int c = peek_buffer;
201 | peek_buffer = -1;
202 | return c;
203 | }
204 | return USB_Recv(CDC_RX);
205 | }
206 |
207 | int Serial_::availableForWrite(void)
208 | {
209 | return USB_SendSpace(CDC_TX);
210 | }
211 |
212 | void Serial_::flush(void)
213 | {
214 | USB_Flush(CDC_TX);
215 | }
216 |
217 | size_t Serial_::write(uint8_t c)
218 | {
219 | return write(&c, 1);
220 | }
221 |
222 | size_t Serial_::write(const uint8_t *buffer, size_t size)
223 | {
224 | /* only try to send bytes if the high-level CDC connection itself
225 | is open (not just the pipe) - the OS should set lineState when the port
226 | is opened and clear lineState when the port is closed.
227 | bytes sent before the user opens the connection or after
228 | the connection is closed are lost - just like with a UART. */
229 |
230 | // TODO - ZE - check behavior on different OSes and test what happens if an
231 | // open connection isn't broken cleanly (cable is yanked out, host dies
232 | // or locks up, or host virtual serial port hangs)
233 | if (_usbLineInfo.lineState > 0) {
234 | int r = USB_Send(CDC_TX,buffer,size);
235 | if (r > 0) {
236 | return r;
237 | } else {
238 | setWriteError();
239 | return 0;
240 | }
241 | }
242 | setWriteError();
243 | return 0;
244 | }
245 |
246 | // This operator is a convenient way for a sketch to check whether the
247 | // port has actually been configured and opened by the host (as opposed
248 | // to just being connected to the host). It can be used, for example, in
249 | // setup() before printing to ensure that an application on the host is
250 | // actually ready to receive and display the data.
251 | // We add a short delay before returning to fix a bug observed by Federico
252 | // where the port is configured (lineState != 0) but not quite opened.
253 | Serial_::operator bool() {
254 | bool result = false;
255 | if (_usbLineInfo.lineState > 0)
256 | result = true;
257 | delay(10);
258 | return result;
259 | }
260 |
261 | unsigned long Serial_::baud() {
262 | // Disable interrupts while reading a multi-byte value
263 | uint32_t baudrate;
264 | ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
265 | baudrate = _usbLineInfo.dwDTERate;
266 | }
267 | return baudrate;
268 | }
269 |
270 | uint8_t Serial_::stopbits() {
271 | return _usbLineInfo.bCharFormat;
272 | }
273 |
274 | uint8_t Serial_::paritytype() {
275 | return _usbLineInfo.bParityType;
276 | }
277 |
278 | uint8_t Serial_::numbits() {
279 | return _usbLineInfo.bDataBits;
280 | }
281 |
282 | bool Serial_::dtr() {
283 | return _usbLineInfo.lineState & 0x1;
284 | }
285 |
286 | bool Serial_::rts() {
287 | return _usbLineInfo.lineState & 0x2;
288 | }
289 |
290 | int32_t Serial_::readBreak() {
291 | int32_t ret;
292 | // Disable IRQs while reading and clearing breakValue to make
293 | // sure we don't overwrite a value just set by the ISR.
294 | ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
295 | ret = breakValue;
296 | breakValue = -1;
297 | }
298 | return ret;
299 | }
300 |
301 | Serial_ Serial;
302 |
303 | #endif /* if defined(USBCON) */
304 |
--------------------------------------------------------------------------------
/test/arduino_leonardo_bridge_castrated_cdc/README.md:
--------------------------------------------------------------------------------
1 | ## castrated CDC test (single interface with 3 endpoints)
2 |
3 | As mentioned [here](https://arduino.stackexchange.com/a/31695/62145), Arduino functions can be _overwritten_ by copying complete files into the own project.
4 |
5 | This is used to create a castrated CDC device with a single interface containing 3 endpoints.
6 |
7 | The modifications have been done against Arduino 1.8.10, for changes see comments containing `kai`.
8 |
9 |
--------------------------------------------------------------------------------
/test/arduino_leonardo_bridge_castrated_cdc/USBCore.h:
--------------------------------------------------------------------------------
1 |
2 | // Copyright (c) 2010, Peter Barrett
3 | /*
4 | ** Permission to use, copy, modify, and/or distribute this software for
5 | ** any purpose with or without fee is hereby granted, provided that the
6 | ** above copyright notice and this permission notice appear in all copies.
7 | **
8 | ** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
9 | ** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
10 | ** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
11 | ** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
12 | ** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
13 | ** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
14 | ** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
15 | ** SOFTWARE.
16 | */
17 |
18 | #ifndef __USBCORE_H__
19 | #define __USBCORE_H__
20 |
21 | #include "USBAPI.h"
22 |
23 | // Standard requests
24 | #define GET_STATUS 0
25 | #define CLEAR_FEATURE 1
26 | #define SET_FEATURE 3
27 | #define SET_ADDRESS 5
28 | #define GET_DESCRIPTOR 6
29 | #define SET_DESCRIPTOR 7
30 | #define GET_CONFIGURATION 8
31 | #define SET_CONFIGURATION 9
32 | #define GET_INTERFACE 10
33 | #define SET_INTERFACE 11
34 |
35 |
36 | // bmRequestType
37 | #define REQUEST_HOSTTODEVICE 0x00
38 | #define REQUEST_DEVICETOHOST 0x80
39 | #define REQUEST_DIRECTION 0x80
40 |
41 | #define REQUEST_STANDARD 0x00
42 | #define REQUEST_CLASS 0x20
43 | #define REQUEST_VENDOR 0x40
44 | #define REQUEST_TYPE 0x60
45 |
46 | #define REQUEST_DEVICE 0x00
47 | #define REQUEST_INTERFACE 0x01
48 | #define REQUEST_ENDPOINT 0x02
49 | #define REQUEST_OTHER 0x03
50 | #define REQUEST_RECIPIENT 0x03
51 |
52 | #define REQUEST_DEVICETOHOST_CLASS_INTERFACE (REQUEST_DEVICETOHOST | REQUEST_CLASS | REQUEST_INTERFACE)
53 | #define REQUEST_HOSTTODEVICE_CLASS_INTERFACE (REQUEST_HOSTTODEVICE | REQUEST_CLASS | REQUEST_INTERFACE)
54 | #define REQUEST_DEVICETOHOST_STANDARD_INTERFACE (REQUEST_DEVICETOHOST | REQUEST_STANDARD | REQUEST_INTERFACE)
55 |
56 | // Class requests
57 |
58 | #define CDC_SET_LINE_CODING 0x20
59 | #define CDC_GET_LINE_CODING 0x21
60 | #define CDC_SET_CONTROL_LINE_STATE 0x22
61 | #define CDC_SEND_BREAK 0x23
62 |
63 | #define MSC_RESET 0xFF
64 | #define MSC_GET_MAX_LUN 0xFE
65 |
66 | // Descriptors
67 |
68 | #define USB_DEVICE_DESC_SIZE 18
69 | #define USB_CONFIGUARTION_DESC_SIZE 9
70 | #define USB_INTERFACE_DESC_SIZE 9
71 | #define USB_ENDPOINT_DESC_SIZE 7
72 |
73 | #define USB_DEVICE_DESCRIPTOR_TYPE 1
74 | #define USB_CONFIGURATION_DESCRIPTOR_TYPE 2
75 | #define USB_STRING_DESCRIPTOR_TYPE 3
76 | #define USB_INTERFACE_DESCRIPTOR_TYPE 4
77 | #define USB_ENDPOINT_DESCRIPTOR_TYPE 5
78 |
79 | // usb_20.pdf Table 9.6 Standard Feature Selectors
80 | #define DEVICE_REMOTE_WAKEUP 1
81 | #define ENDPOINT_HALT 2
82 | #define TEST_MODE 3
83 |
84 | // usb_20.pdf Figure 9-4. Information Returned by a GetStatus() Request to a Device
85 | #define FEATURE_SELFPOWERED_ENABLED (1 << 0)
86 | #define FEATURE_REMOTE_WAKEUP_ENABLED (1 << 1)
87 |
88 | #define USB_DEVICE_CLASS_COMMUNICATIONS 0x02
89 | #define USB_DEVICE_CLASS_HUMAN_INTERFACE 0x03
90 | #define USB_DEVICE_CLASS_STORAGE 0x08
91 | #define USB_DEVICE_CLASS_VENDOR_SPECIFIC 0xFF
92 |
93 | #define USB_CONFIG_POWERED_MASK 0x40
94 | #define USB_CONFIG_BUS_POWERED 0x80
95 | #define USB_CONFIG_SELF_POWERED 0xC0
96 | #define USB_CONFIG_REMOTE_WAKEUP 0x20
97 |
98 | // bMaxPower in Configuration Descriptor
99 | #define USB_CONFIG_POWER_MA(mA) ((mA)/2)
100 |
101 | // bEndpointAddress in Endpoint Descriptor
102 | #define USB_ENDPOINT_DIRECTION_MASK 0x80
103 | #define USB_ENDPOINT_OUT(addr) (lowByte((addr) | 0x00))
104 | #define USB_ENDPOINT_IN(addr) (lowByte((addr) | 0x80))
105 |
106 | #define USB_ENDPOINT_TYPE_MASK 0x03
107 | #define USB_ENDPOINT_TYPE_CONTROL 0x00
108 | #define USB_ENDPOINT_TYPE_ISOCHRONOUS 0x01
109 | #define USB_ENDPOINT_TYPE_BULK 0x02
110 | #define USB_ENDPOINT_TYPE_INTERRUPT 0x03
111 |
112 | #define TOBYTES(x) ((x) & 0xFF),(((x) >> 8) & 0xFF)
113 |
114 | #define CDC_V1_10 0x0110
115 | #define CDC_COMMUNICATION_INTERFACE_CLASS 0x02
116 |
117 | #define CDC_CALL_MANAGEMENT 0x01
118 | #define CDC_ABSTRACT_CONTROL_MODEL 0x02
119 | #define CDC_HEADER 0x00
120 | #define CDC_ABSTRACT_CONTROL_MANAGEMENT 0x02
121 | #define CDC_UNION 0x06
122 | #define CDC_CS_INTERFACE 0x24
123 | #define CDC_CS_ENDPOINT 0x25
124 | #define CDC_DATA_INTERFACE_CLASS 0x0A
125 |
126 | #define MSC_SUBCLASS_SCSI 0x06
127 | #define MSC_PROTOCOL_BULK_ONLY 0x50
128 |
129 | #ifndef USB_VERSION
130 | #define USB_VERSION 0x200
131 | #endif
132 |
133 | // Device
134 | typedef struct {
135 | u8 len; // 18
136 | u8 dtype; // 1 USB_DEVICE_DESCRIPTOR_TYPE
137 | u16 usbVersion; // 0x200 or 0x210
138 | u8 deviceClass;
139 | u8 deviceSubClass;
140 | u8 deviceProtocol;
141 | u8 packetSize0; // Packet 0
142 | u16 idVendor;
143 | u16 idProduct;
144 | u16 deviceVersion; // 0x100
145 | u8 iManufacturer;
146 | u8 iProduct;
147 | u8 iSerialNumber;
148 | u8 bNumConfigurations;
149 | } DeviceDescriptor;
150 |
151 | // Config
152 | typedef struct {
153 | u8 len; // 9
154 | u8 dtype; // 2
155 | u16 clen; // total length
156 | u8 numInterfaces;
157 | u8 config;
158 | u8 iconfig;
159 | u8 attributes;
160 | u8 maxPower;
161 | } ConfigDescriptor;
162 |
163 | // String
164 |
165 | // Interface
166 | typedef struct
167 | {
168 | u8 len; // 9
169 | u8 dtype; // 4
170 | u8 number;
171 | u8 alternate;
172 | u8 numEndpoints;
173 | u8 interfaceClass;
174 | u8 interfaceSubClass;
175 | u8 protocol;
176 | u8 iInterface;
177 | } InterfaceDescriptor;
178 |
179 | // Endpoint
180 | typedef struct
181 | {
182 | u8 len; // 7
183 | u8 dtype; // 5
184 | u8 addr;
185 | u8 attr;
186 | u16 packetSize;
187 | u8 interval;
188 | } EndpointDescriptor;
189 |
190 | // Interface Association Descriptor
191 | // Used to bind 2 interfaces together in CDC compostite device
192 | typedef struct
193 | {
194 | u8 len; // 8
195 | u8 dtype; // 11
196 | u8 firstInterface;
197 | u8 interfaceCount;
198 | u8 functionClass;
199 | u8 funtionSubClass;
200 | u8 functionProtocol;
201 | u8 iInterface;
202 | } IADDescriptor;
203 |
204 | // CDC CS interface descriptor
205 | typedef struct
206 | {
207 | u8 len; // 5
208 | u8 dtype; // 0x24
209 | u8 subtype;
210 | u8 d0;
211 | u8 d1;
212 | } CDCCSInterfaceDescriptor;
213 |
214 | typedef struct
215 | {
216 | u8 len; // 4
217 | u8 dtype; // 0x24
218 | u8 subtype;
219 | u8 d0;
220 | } CDCCSInterfaceDescriptor4;
221 |
222 | typedef struct
223 | {
224 | u8 len;
225 | u8 dtype; // 0x24
226 | u8 subtype; // 1
227 | u8 bmCapabilities;
228 | u8 bDataInterface;
229 | } CMFunctionalDescriptor;
230 |
231 | typedef struct
232 | {
233 | u8 len;
234 | u8 dtype; // 0x24
235 | u8 subtype; // 1
236 | u8 bmCapabilities;
237 | } ACMFunctionalDescriptor;
238 |
239 | typedef struct
240 | {
241 | // IAD
242 | IADDescriptor iad; // Only needed on compound device
243 |
244 | // Control
245 | InterfaceDescriptor cif; //
246 | CDCCSInterfaceDescriptor header;
247 | CMFunctionalDescriptor callManagement; // Call Management
248 | ACMFunctionalDescriptor controlManagement; // ACM
249 | CDCCSInterfaceDescriptor functionalDescriptor; // CDC_UNION
250 | EndpointDescriptor cifin;
251 |
252 | // Data
253 | //InterfaceDescriptor dif; // kai:removed
254 | EndpointDescriptor in;
255 | EndpointDescriptor out;
256 | } CDCDescriptor;
257 |
258 | typedef struct
259 | {
260 | InterfaceDescriptor msc;
261 | EndpointDescriptor in;
262 | EndpointDescriptor out;
263 | } MSCDescriptor;
264 |
265 |
266 | #define D_DEVICE(_class,_subClass,_proto,_packetSize0,_vid,_pid,_version,_im,_ip,_is,_configs) \
267 | { 18, 1, USB_VERSION, _class,_subClass,_proto,_packetSize0,_vid,_pid,_version,_im,_ip,_is,_configs }
268 |
269 | #define D_CONFIG(_totalLength,_interfaces) \
270 | { 9, 2, _totalLength,_interfaces, 1, 0, USB_CONFIG_BUS_POWERED | USB_CONFIG_REMOTE_WAKEUP, USB_CONFIG_POWER_MA(500) }
271 |
272 | #define D_INTERFACE(_n,_numEndpoints,_class,_subClass,_protocol) \
273 | { 9, 4, _n, 0, _numEndpoints, _class,_subClass, _protocol, 0 }
274 |
275 | #define D_ENDPOINT(_addr,_attr,_packetSize, _interval) \
276 | { 7, 5, _addr,_attr,_packetSize, _interval }
277 |
278 | #define D_IAD(_firstInterface, _count, _class, _subClass, _protocol) \
279 | { 8, 11, _firstInterface, _count, _class, _subClass, _protocol, 0 }
280 |
281 | #define D_CDCCS(_subtype,_d0,_d1) { 5, 0x24, _subtype, _d0, _d1 }
282 | #define D_CDCCS4(_subtype,_d0) { 4, 0x24, _subtype, _d0 }
283 |
284 | // Bootloader related fields
285 | // Old Caterina bootloader places the MAGIC key into unsafe RAM locations (it can be rewritten
286 | // by the running sketch before to actual reboot).
287 | // Newer bootloaders, recognizable by the LUFA "signature" at the end of the flash, can handle both
288 | // the usafe and the safe location.
289 | #ifndef MAGIC_KEY
290 | #define MAGIC_KEY 0x7777
291 | #endif
292 |
293 | #ifndef MAGIC_KEY_POS
294 | #define MAGIC_KEY_POS 0x0800
295 | #endif
296 |
297 | #ifndef NEW_LUFA_SIGNATURE
298 | #define NEW_LUFA_SIGNATURE 0xDCFB
299 | #endif
300 |
301 | #endif
302 |
--------------------------------------------------------------------------------
/test/arduino_leonardo_bridge_castrated_cdc/arduino_leonardo_bridge_castrated_cdc.ino:
--------------------------------------------------------------------------------
1 | /*
2 | bridge USB-serial to hardware-serial
3 |
4 | for Arduinos based on ATmega32u4 (Leonardo and compatible Pro Micro, Micro)
5 | hardware serial is configured with baud-rate, databits, stopbits, parity as send over USB
6 |
7 | see https://github.com/arduino/Arduino/tree/master/hardware/arduino/avr/cores/arduino
8 | -> CDC.cpp|HardwareSerial.cpp for serial implementation details
9 |
10 | this sketch is mainly for demonstration / test of CDC communication
11 | performance as real usb-serial bridge would be inacceptable as each byte is send in separate USB packet
12 | */
13 |
14 | uint32_t baud = 9600;
15 | uint8_t databits = 8;
16 | uint8_t stopbits = 1;
17 | uint8_t parity = 0;
18 |
19 | void setup() {
20 | Serial.begin(baud); // USB
21 | Serial1.begin(baud, SERIAL_8N1);
22 | }
23 |
24 | void loop() {
25 | // show USB connected state
26 | if (Serial) TXLED1;
27 | else TXLED0;
28 |
29 | // configure hardware serial
30 | if (Serial.baud() != baud ||
31 | Serial.numbits() != databits ||
32 | Serial.stopbits() != stopbits ||
33 | Serial.paritytype() != parity) {
34 | baud = Serial.baud();
35 | databits = Serial.numbits();
36 | stopbits = Serial.stopbits();
37 | parity = Serial.paritytype();
38 | uint8_t config = 0; // ucsrc register
39 | switch (databits) {
40 | case 5: break;
41 | case 6: config |= 2; break;
42 | case 7: config |= 4; break;
43 | case 8: config |= 6; break;
44 | default: config |= 6;
45 | }
46 | switch (stopbits) {
47 | case 2: config |= 8;
48 | // 1.5 stopbits not supported
49 | }
50 | switch (parity) {
51 | case 1: config |= 0x30; break; // odd
52 | case 2: config |= 0x20; break; // even
53 | // mark, space not supported
54 | }
55 | Serial1.end();
56 | Serial1.begin(baud, config);
57 | }
58 |
59 | // bridge
60 | if (Serial.available() > 0)
61 | Serial1.write(Serial.read());
62 | if (Serial1.available() > 0)
63 | Serial.write(Serial1.read());
64 | }
65 |
--------------------------------------------------------------------------------
/test/arduino_leonardo_bridge_multi_cdc/CDC.cpp:
--------------------------------------------------------------------------------
1 |
2 |
3 | /* Copyright (c) 2011, Peter Barrett
4 | **
5 | ** Permission to use, copy, modify, and/or distribute this software for
6 | ** any purpose with or without fee is hereby granted, provided that the
7 | ** above copyright notice and this permission notice appear in all copies.
8 | **
9 | ** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10 | ** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11 | ** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
12 | ** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
13 | ** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
14 | ** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
15 | ** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
16 | ** SOFTWARE.
17 | */
18 |
19 | #include "USBCore.h" // kai:added
20 | #include "USBAPI.h"
21 | #include "USBDesc.h" // kai:added
22 | #include
23 | #include
24 |
25 | #if defined(USBCON)
26 |
27 | typedef struct
28 | {
29 | u32 dwDTERate;
30 | u8 bCharFormat;
31 | u8 bParityType;
32 | u8 bDataBits;
33 | u8 lineState;
34 | } LineInfo;
35 |
36 | static volatile LineInfo _usbLineInfo = { 57600, 0x00, 0x00, 0x00, 0x00 };
37 | static volatile int32_t breakValue = -1;
38 |
39 | static u8 wdtcsr_save;
40 |
41 | #define WEAK __attribute__ ((weak))
42 |
43 | extern const CDCDescriptor _cdcInterface PROGMEM;
44 | const CDCDescriptor _cdcInterface =
45 | {
46 | D_IAD(0,2,CDC_COMMUNICATION_INTERFACE_CLASS,CDC_ABSTRACT_CONTROL_MODEL,1),
47 |
48 | // CDC communication interface
49 | D_INTERFACE(CDC_ACM_INTERFACE1,1,CDC_COMMUNICATION_INTERFACE_CLASS,CDC_ABSTRACT_CONTROL_MODEL,0),
50 | D_CDCCS(CDC_HEADER,0x10,0x01), // Header (1.10 bcd)
51 | D_CDCCS(CDC_CALL_MANAGEMENT,1,1), // Device handles call management (not)
52 | D_CDCCS4(CDC_ABSTRACT_CONTROL_MANAGEMENT,6), // SET_LINE_CODING, GET_LINE_CODING, SET_CONTROL_LINE_STATE supported
53 | D_CDCCS(CDC_UNION,CDC_ACM_INTERFACE1,CDC_DATA_INTERFACE1), // Communication interface is master, data interface is slave 0
54 | D_ENDPOINT(USB_ENDPOINT_IN (CDC_ENDPOINT_ACM1),USB_ENDPOINT_TYPE_INTERRUPT,0x10,0x40),
55 |
56 | // CDC data interface
57 | D_INTERFACE(CDC_DATA_INTERFACE1,2,CDC_DATA_INTERFACE_CLASS,0,0),
58 | D_ENDPOINT(USB_ENDPOINT_OUT(CDC_ENDPOINT_OUT1),USB_ENDPOINT_TYPE_BULK,USB_EP_SIZE,0),
59 | D_ENDPOINT(USB_ENDPOINT_IN (CDC_ENDPOINT_IN1 ),USB_ENDPOINT_TYPE_BULK,USB_EP_SIZE,0)
60 |
61 | , // kai: added
62 | D_IAD(2,2,CDC_COMMUNICATION_INTERFACE_CLASS,CDC_ABSTRACT_CONTROL_MODEL,1),
63 |
64 | // CDC communication interface
65 | D_INTERFACE(CDC_ACM_INTERFACE2,1,CDC_COMMUNICATION_INTERFACE_CLASS,CDC_ABSTRACT_CONTROL_MODEL,0),
66 | D_CDCCS(CDC_HEADER,0x10,0x01), // Header (1.10 bcd)
67 | D_CDCCS(CDC_CALL_MANAGEMENT,1,1), // Device handles call management (not)
68 | D_CDCCS4(CDC_ABSTRACT_CONTROL_MANAGEMENT,6), // SET_LINE_CODING, GET_LINE_CODING, SET_CONTROL_LINE_STATE supported
69 | D_CDCCS(CDC_UNION,CDC_ACM_INTERFACE2,CDC_DATA_INTERFACE2), // Communication interface is master, data interface is slave 0
70 | D_ENDPOINT(USB_ENDPOINT_IN (CDC_ENDPOINT_ACM2),USB_ENDPOINT_TYPE_INTERRUPT,0x10,0x40),
71 |
72 | // CDC data interface
73 | D_INTERFACE(CDC_DATA_INTERFACE2,2,CDC_DATA_INTERFACE_CLASS,0,0),
74 | D_ENDPOINT(USB_ENDPOINT_OUT(CDC_ENDPOINT_OUT2),USB_ENDPOINT_TYPE_BULK,USB_EP_SIZE,0),
75 | D_ENDPOINT(USB_ENDPOINT_IN (CDC_ENDPOINT_IN2),USB_ENDPOINT_TYPE_BULK,USB_EP_SIZE,0)
76 | };
77 |
78 | bool isLUFAbootloader()
79 | {
80 | return pgm_read_word(FLASHEND - 1) == NEW_LUFA_SIGNATURE;
81 | }
82 |
83 | int CDC_GetInterface(u8* interfaceNum)
84 | {
85 | interfaceNum[0] += 4; // kai
86 | return USB_SendControl(TRANSFER_PGM,&_cdcInterface,sizeof(_cdcInterface));
87 | }
88 |
89 | bool CDC_Setup(USBSetup& setup)
90 | {
91 | u8 r = setup.bRequest;
92 | u8 requestType = setup.bmRequestType;
93 |
94 | if (REQUEST_DEVICETOHOST_CLASS_INTERFACE == requestType)
95 | {
96 | if (CDC_GET_LINE_CODING == r)
97 | {
98 | USB_SendControl(0,(void*)&_usbLineInfo,7);
99 | return true;
100 | }
101 | }
102 |
103 | if (REQUEST_HOSTTODEVICE_CLASS_INTERFACE == requestType)
104 | {
105 | if (CDC_SEND_BREAK == r)
106 | {
107 | breakValue = ((uint16_t)setup.wValueH << 8) | setup.wValueL;
108 | }
109 |
110 | if (CDC_SET_LINE_CODING == r)
111 | {
112 | USB_RecvControl((void*)&_usbLineInfo,7);
113 | }
114 |
115 | if (CDC_SET_CONTROL_LINE_STATE == r)
116 | {
117 | _usbLineInfo.lineState = setup.wValueL;
118 |
119 | // auto-reset into the bootloader is triggered when the port, already
120 | // open at 1200 bps, is closed. this is the signal to start the watchdog
121 | // with a relatively long period so it can finish housekeeping tasks
122 | // like servicing endpoints before the sketch ends
123 |
124 | uint16_t magic_key_pos = MAGIC_KEY_POS;
125 |
126 | // If we don't use the new RAMEND directly, check manually if we have a newer bootloader.
127 | // This is used to keep compatible with the old leonardo bootloaders.
128 | // You are still able to set the magic key position manually to RAMEND-1 to save a few bytes for this check.
129 | #if MAGIC_KEY_POS != (RAMEND-1)
130 | // For future boards save the key in the inproblematic RAMEND
131 | // Which is reserved for the main() return value (which will never return)
132 | if (isLUFAbootloader()) {
133 | // horray, we got a new bootloader!
134 | magic_key_pos = (RAMEND-1);
135 | }
136 | #endif
137 |
138 | // We check DTR state to determine if host port is open (bit 0 of lineState).
139 | if (1200 == _usbLineInfo.dwDTERate && (_usbLineInfo.lineState & 0x01) == 0)
140 | {
141 | #if MAGIC_KEY_POS != (RAMEND-1)
142 | // Backup ram value if its not a newer bootloader and it hasn't already been saved.
143 | // This should avoid memory corruption at least a bit, not fully
144 | if (magic_key_pos != (RAMEND-1) && *(uint16_t *)magic_key_pos != MAGIC_KEY) {
145 | *(uint16_t *)(RAMEND-1) = *(uint16_t *)magic_key_pos;
146 | }
147 | #endif
148 | // Store boot key
149 | *(uint16_t *)magic_key_pos = MAGIC_KEY;
150 | // Save the watchdog state in case the reset is aborted.
151 | wdtcsr_save = WDTCSR;
152 | wdt_enable(WDTO_120MS);
153 | }
154 | else if (*(uint16_t *)magic_key_pos == MAGIC_KEY)
155 | {
156 | // Most OSs do some intermediate steps when configuring ports and DTR can
157 | // twiggle more than once before stabilizing.
158 | // To avoid spurious resets we set the watchdog to 120ms and eventually
159 | // cancel if DTR goes back high.
160 | // Cancellation is only done if an auto-reset was started, which is
161 | // indicated by the magic key having been set.
162 |
163 | wdt_reset();
164 | // Restore the watchdog state in case the sketch was using it.
165 | WDTCSR |= (1<= 0) {
202 | return 1 + USB_Available(CDC_RX);
203 | }
204 | return USB_Available(CDC_RX);
205 | }
206 |
207 | int Serial_::peek(void)
208 | {
209 | if (peek_buffer < 0)
210 | peek_buffer = USB_Recv(CDC_RX);
211 | return peek_buffer;
212 | }
213 |
214 | int Serial_::read(void)
215 | {
216 | if (peek_buffer >= 0) {
217 | int c = peek_buffer;
218 | peek_buffer = -1;
219 | return c;
220 | }
221 | return USB_Recv(CDC_RX);
222 | }
223 |
224 | int Serial_::availableForWrite(void)
225 | {
226 | return USB_SendSpace(CDC_TX);
227 | }
228 |
229 | void Serial_::flush(void)
230 | {
231 | USB_Flush(CDC_TX);
232 | }
233 |
234 | size_t Serial_::write(uint8_t c)
235 | {
236 | return write(&c, 1);
237 | }
238 |
239 | size_t Serial_::write(const uint8_t *buffer, size_t size)
240 | {
241 | /* only try to send bytes if the high-level CDC connection itself
242 | is open (not just the pipe) - the OS should set lineState when the port
243 | is opened and clear lineState when the port is closed.
244 | bytes sent before the user opens the connection or after
245 | the connection is closed are lost - just like with a UART. */
246 |
247 | // TODO - ZE - check behavior on different OSes and test what happens if an
248 | // open connection isn't broken cleanly (cable is yanked out, host dies
249 | // or locks up, or host virtual serial port hangs)
250 | if (_usbLineInfo.lineState > 0) {
251 | int r = USB_Send(CDC_TX,buffer,size);
252 | if (r > 0) {
253 | return r;
254 | } else {
255 | setWriteError();
256 | return 0;
257 | }
258 | }
259 | setWriteError();
260 | return 0;
261 | }
262 |
263 | // This operator is a convenient way for a sketch to check whether the
264 | // port has actually been configured and opened by the host (as opposed
265 | // to just being connected to the host). It can be used, for example, in
266 | // setup() before printing to ensure that an application on the host is
267 | // actually ready to receive and display the data.
268 | // We add a short delay before returning to fix a bug observed by Federico
269 | // where the port is configured (lineState != 0) but not quite opened.
270 | Serial_::operator bool() {
271 | bool result = false;
272 | if (_usbLineInfo.lineState > 0)
273 | result = true;
274 | delay(10);
275 | return result;
276 | }
277 |
278 | unsigned long Serial_::baud() {
279 | // Disable interrupts while reading a multi-byte value
280 | uint32_t baudrate;
281 | ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
282 | baudrate = _usbLineInfo.dwDTERate;
283 | }
284 | return baudrate;
285 | }
286 |
287 | uint8_t Serial_::stopbits() {
288 | return _usbLineInfo.bCharFormat;
289 | }
290 |
291 | uint8_t Serial_::paritytype() {
292 | return _usbLineInfo.bParityType;
293 | }
294 |
295 | uint8_t Serial_::numbits() {
296 | return _usbLineInfo.bDataBits;
297 | }
298 |
299 | bool Serial_::dtr() {
300 | return _usbLineInfo.lineState & 0x1;
301 | }
302 |
303 | bool Serial_::rts() {
304 | return _usbLineInfo.lineState & 0x2;
305 | }
306 |
307 | int32_t Serial_::readBreak() {
308 | int32_t ret;
309 | // Disable IRQs while reading and clearing breakValue to make
310 | // sure we don't overwrite a value just set by the ISR.
311 | ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
312 | ret = breakValue;
313 | breakValue = -1;
314 | }
315 | return ret;
316 | }
317 |
318 | Serial_ Serial;
319 |
320 | #endif /* if defined(USBCON) */
321 |
--------------------------------------------------------------------------------
/test/arduino_leonardo_bridge_multi_cdc/README.md:
--------------------------------------------------------------------------------
1 | ## multiple CDC interface test
2 |
3 | As mentioned [here](https://arduino.stackexchange.com/a/31695/62145), Arduino functions can be _overwritten_ by copying complete files into the own project.
4 |
5 | This is used to create a device with 2 CDC interfaces. For simplicity only one of both is functional (configurable in USBDesc.h)
6 |
7 | The modifications have been done against Arduino 1.8.10, for changes see comments containing `kai`.
8 |
9 |
--------------------------------------------------------------------------------
/test/arduino_leonardo_bridge_multi_cdc/USBCore.h:
--------------------------------------------------------------------------------
1 |
2 | // Copyright (c) 2010, Peter Barrett
3 | /*
4 | ** Permission to use, copy, modify, and/or distribute this software for
5 | ** any purpose with or without fee is hereby granted, provided that the
6 | ** above copyright notice and this permission notice appear in all copies.
7 | **
8 | ** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
9 | ** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
10 | ** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
11 | ** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
12 | ** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
13 | ** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
14 | ** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
15 | ** SOFTWARE.
16 | */
17 |
18 | #ifndef __USBCORE_H__
19 | #define __USBCORE_H__
20 |
21 | #include "USBAPI.h"
22 |
23 | // Standard requests
24 | #define GET_STATUS 0
25 | #define CLEAR_FEATURE 1
26 | #define SET_FEATURE 3
27 | #define SET_ADDRESS 5
28 | #define GET_DESCRIPTOR 6
29 | #define SET_DESCRIPTOR 7
30 | #define GET_CONFIGURATION 8
31 | #define SET_CONFIGURATION 9
32 | #define GET_INTERFACE 10
33 | #define SET_INTERFACE 11
34 |
35 |
36 | // bmRequestType
37 | #define REQUEST_HOSTTODEVICE 0x00
38 | #define REQUEST_DEVICETOHOST 0x80
39 | #define REQUEST_DIRECTION 0x80
40 |
41 | #define REQUEST_STANDARD 0x00
42 | #define REQUEST_CLASS 0x20
43 | #define REQUEST_VENDOR 0x40
44 | #define REQUEST_TYPE 0x60
45 |
46 | #define REQUEST_DEVICE 0x00
47 | #define REQUEST_INTERFACE 0x01
48 | #define REQUEST_ENDPOINT 0x02
49 | #define REQUEST_OTHER 0x03
50 | #define REQUEST_RECIPIENT 0x03
51 |
52 | #define REQUEST_DEVICETOHOST_CLASS_INTERFACE (REQUEST_DEVICETOHOST | REQUEST_CLASS | REQUEST_INTERFACE)
53 | #define REQUEST_HOSTTODEVICE_CLASS_INTERFACE (REQUEST_HOSTTODEVICE | REQUEST_CLASS | REQUEST_INTERFACE)
54 | #define REQUEST_DEVICETOHOST_STANDARD_INTERFACE (REQUEST_DEVICETOHOST | REQUEST_STANDARD | REQUEST_INTERFACE)
55 |
56 | // Class requests
57 |
58 | #define CDC_SET_LINE_CODING 0x20
59 | #define CDC_GET_LINE_CODING 0x21
60 | #define CDC_SET_CONTROL_LINE_STATE 0x22
61 | #define CDC_SEND_BREAK 0x23
62 |
63 | #define MSC_RESET 0xFF
64 | #define MSC_GET_MAX_LUN 0xFE
65 |
66 | // Descriptors
67 |
68 | #define USB_DEVICE_DESC_SIZE 18
69 | #define USB_CONFIGUARTION_DESC_SIZE 9
70 | #define USB_INTERFACE_DESC_SIZE 9
71 | #define USB_ENDPOINT_DESC_SIZE 7
72 |
73 | #define USB_DEVICE_DESCRIPTOR_TYPE 1
74 | #define USB_CONFIGURATION_DESCRIPTOR_TYPE 2
75 | #define USB_STRING_DESCRIPTOR_TYPE 3
76 | #define USB_INTERFACE_DESCRIPTOR_TYPE 4
77 | #define USB_ENDPOINT_DESCRIPTOR_TYPE 5
78 |
79 | // usb_20.pdf Table 9.6 Standard Feature Selectors
80 | #define DEVICE_REMOTE_WAKEUP 1
81 | #define ENDPOINT_HALT 2
82 | #define TEST_MODE 3
83 |
84 | // usb_20.pdf Figure 9-4. Information Returned by a GetStatus() Request to a Device
85 | #define FEATURE_SELFPOWERED_ENABLED (1 << 0)
86 | #define FEATURE_REMOTE_WAKEUP_ENABLED (1 << 1)
87 |
88 | #define USB_DEVICE_CLASS_COMMUNICATIONS 0x02
89 | #define USB_DEVICE_CLASS_HUMAN_INTERFACE 0x03
90 | #define USB_DEVICE_CLASS_STORAGE 0x08
91 | #define USB_DEVICE_CLASS_VENDOR_SPECIFIC 0xFF
92 |
93 | #define USB_CONFIG_POWERED_MASK 0x40
94 | #define USB_CONFIG_BUS_POWERED 0x80
95 | #define USB_CONFIG_SELF_POWERED 0xC0
96 | #define USB_CONFIG_REMOTE_WAKEUP 0x20
97 |
98 | // bMaxPower in Configuration Descriptor
99 | #define USB_CONFIG_POWER_MA(mA) ((mA)/2)
100 |
101 | // bEndpointAddress in Endpoint Descriptor
102 | #define USB_ENDPOINT_DIRECTION_MASK 0x80
103 | #define USB_ENDPOINT_OUT(addr) (lowByte((addr) | 0x00))
104 | #define USB_ENDPOINT_IN(addr) (lowByte((addr) | 0x80))
105 |
106 | #define USB_ENDPOINT_TYPE_MASK 0x03
107 | #define USB_ENDPOINT_TYPE_CONTROL 0x00
108 | #define USB_ENDPOINT_TYPE_ISOCHRONOUS 0x01
109 | #define USB_ENDPOINT_TYPE_BULK 0x02
110 | #define USB_ENDPOINT_TYPE_INTERRUPT 0x03
111 |
112 | #define TOBYTES(x) ((x) & 0xFF),(((x) >> 8) & 0xFF)
113 |
114 | #define CDC_V1_10 0x0110
115 | #define CDC_COMMUNICATION_INTERFACE_CLASS 0x02
116 |
117 | #define CDC_CALL_MANAGEMENT 0x01
118 | #define CDC_ABSTRACT_CONTROL_MODEL 0x02
119 | #define CDC_HEADER 0x00
120 | #define CDC_ABSTRACT_CONTROL_MANAGEMENT 0x02
121 | #define CDC_UNION 0x06
122 | #define CDC_CS_INTERFACE 0x24
123 | #define CDC_CS_ENDPOINT 0x25
124 | #define CDC_DATA_INTERFACE_CLASS 0x0A
125 |
126 | #define MSC_SUBCLASS_SCSI 0x06
127 | #define MSC_PROTOCOL_BULK_ONLY 0x50
128 |
129 | #ifndef USB_VERSION
130 | #define USB_VERSION 0x200
131 | #endif
132 |
133 | // Device
134 | typedef struct {
135 | u8 len; // 18
136 | u8 dtype; // 1 USB_DEVICE_DESCRIPTOR_TYPE
137 | u16 usbVersion; // 0x200 or 0x210
138 | u8 deviceClass;
139 | u8 deviceSubClass;
140 | u8 deviceProtocol;
141 | u8 packetSize0; // Packet 0
142 | u16 idVendor;
143 | u16 idProduct;
144 | u16 deviceVersion; // 0x100
145 | u8 iManufacturer;
146 | u8 iProduct;
147 | u8 iSerialNumber;
148 | u8 bNumConfigurations;
149 | } DeviceDescriptor;
150 |
151 | // Config
152 | typedef struct {
153 | u8 len; // 9
154 | u8 dtype; // 2
155 | u16 clen; // total length
156 | u8 numInterfaces;
157 | u8 config;
158 | u8 iconfig;
159 | u8 attributes;
160 | u8 maxPower;
161 | } ConfigDescriptor;
162 |
163 | // String
164 |
165 | // Interface
166 | typedef struct
167 | {
168 | u8 len; // 9
169 | u8 dtype; // 4
170 | u8 number;
171 | u8 alternate;
172 | u8 numEndpoints;
173 | u8 interfaceClass;
174 | u8 interfaceSubClass;
175 | u8 protocol;
176 | u8 iInterface;
177 | } InterfaceDescriptor;
178 |
179 | // Endpoint
180 | typedef struct
181 | {
182 | u8 len; // 7
183 | u8 dtype; // 5
184 | u8 addr;
185 | u8 attr;
186 | u16 packetSize;
187 | u8 interval;
188 | } EndpointDescriptor;
189 |
190 | // Interface Association Descriptor
191 | // Used to bind 2 interfaces together in CDC compostite device
192 | typedef struct
193 | {
194 | u8 len; // 8
195 | u8 dtype; // 11
196 | u8 firstInterface;
197 | u8 interfaceCount;
198 | u8 functionClass;
199 | u8 funtionSubClass;
200 | u8 functionProtocol;
201 | u8 iInterface;
202 | } IADDescriptor;
203 |
204 | // CDC CS interface descriptor
205 | typedef struct
206 | {
207 | u8 len; // 5
208 | u8 dtype; // 0x24
209 | u8 subtype;
210 | u8 d0;
211 | u8 d1;
212 | } CDCCSInterfaceDescriptor;
213 |
214 | typedef struct
215 | {
216 | u8 len; // 4
217 | u8 dtype; // 0x24
218 | u8 subtype;
219 | u8 d0;
220 | } CDCCSInterfaceDescriptor4;
221 |
222 | typedef struct
223 | {
224 | u8 len;
225 | u8 dtype; // 0x24
226 | u8 subtype; // 1
227 | u8 bmCapabilities;
228 | u8 bDataInterface;
229 | } CMFunctionalDescriptor;
230 |
231 | typedef struct
232 | {
233 | u8 len;
234 | u8 dtype; // 0x24
235 | u8 subtype; // 1
236 | u8 bmCapabilities;
237 | } ACMFunctionalDescriptor;
238 |
239 | typedef struct
240 | {
241 | // IAD
242 | IADDescriptor iad; // Only needed on compound device
243 |
244 | // Control
245 | InterfaceDescriptor cif; //
246 | CDCCSInterfaceDescriptor header;
247 | CMFunctionalDescriptor callManagement; // Call Management
248 | ACMFunctionalDescriptor controlManagement; // ACM
249 | CDCCSInterfaceDescriptor functionalDescriptor; // CDC_UNION
250 | EndpointDescriptor cifin;
251 |
252 | // Data
253 | InterfaceDescriptor dif;
254 | EndpointDescriptor in;
255 | EndpointDescriptor out;
256 |
257 | // kai:added
258 | IADDescriptor iad2; // Only needed on compound device
259 |
260 | // Control
261 | InterfaceDescriptor cif2; //
262 | CDCCSInterfaceDescriptor header2;
263 | CMFunctionalDescriptor callManagement2; // Call Management
264 | ACMFunctionalDescriptor controlManagement2; // ACM
265 | CDCCSInterfaceDescriptor functionalDescriptor2; // CDC_UNION
266 | EndpointDescriptor cifin2;
267 |
268 | // Data
269 | InterfaceDescriptor dif2;
270 | EndpointDescriptor in2;
271 | EndpointDescriptor out2;
272 | } CDCDescriptor;
273 |
274 | typedef struct
275 | {
276 | InterfaceDescriptor msc;
277 | EndpointDescriptor in;
278 | EndpointDescriptor out;
279 | } MSCDescriptor;
280 |
281 |
282 | #define D_DEVICE(_class,_subClass,_proto,_packetSize0,_vid,_pid,_version,_im,_ip,_is,_configs) \
283 | { 18, 1, USB_VERSION, _class,_subClass,_proto,_packetSize0,_vid,_pid,_version,_im,_ip,_is,_configs }
284 |
285 | #define D_CONFIG(_totalLength,_interfaces) \
286 | { 9, 2, _totalLength,_interfaces, 1, 0, USB_CONFIG_BUS_POWERED | USB_CONFIG_REMOTE_WAKEUP, USB_CONFIG_POWER_MA(500) }
287 |
288 | #define D_INTERFACE(_n,_numEndpoints,_class,_subClass,_protocol) \
289 | { 9, 4, _n, 0, _numEndpoints, _class,_subClass, _protocol, 0 }
290 |
291 | #define D_ENDPOINT(_addr,_attr,_packetSize, _interval) \
292 | { 7, 5, _addr,_attr,_packetSize, _interval }
293 |
294 | #define D_IAD(_firstInterface, _count, _class, _subClass, _protocol) \
295 | { 8, 11, _firstInterface, _count, _class, _subClass, _protocol, 0 }
296 |
297 | #define D_CDCCS(_subtype,_d0,_d1) { 5, 0x24, _subtype, _d0, _d1 }
298 | #define D_CDCCS4(_subtype,_d0) { 4, 0x24, _subtype, _d0 }
299 |
300 | // Bootloader related fields
301 | // Old Caterina bootloader places the MAGIC key into unsafe RAM locations (it can be rewritten
302 | // by the running sketch before to actual reboot).
303 | // Newer bootloaders, recognizable by the LUFA "signature" at the end of the flash, can handle both
304 | // the usafe and the safe location.
305 | #ifndef MAGIC_KEY
306 | #define MAGIC_KEY 0x7777
307 | #endif
308 |
309 | #ifndef MAGIC_KEY_POS
310 | #define MAGIC_KEY_POS 0x0800
311 | #endif
312 |
313 | #ifndef NEW_LUFA_SIGNATURE
314 | #define NEW_LUFA_SIGNATURE 0xDCFB
315 | #endif
316 |
317 | #endif
318 |
--------------------------------------------------------------------------------
/test/arduino_leonardo_bridge_multi_cdc/USBDesc.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2011, Peter Barrett
3 | Copyright (c) 2015, Arduino LLC
4 |
5 | Permission to use, copy, modify, and/or distribute this software for
6 | any purpose with or without fee is hereby granted, provided that the
7 | above copyright notice and this permission notice appear in all copies.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11 | WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
12 | BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
13 | OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
14 | WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
15 | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
16 | SOFTWARE.
17 | */
18 |
19 | #define PLUGGABLE_USB_ENABLED
20 |
21 | #if defined(EPRST6)
22 | #define USB_ENDPOINTS 7 // AtMegaxxU4
23 | #else
24 | #define USB_ENDPOINTS 5 // AtMegaxxU2
25 | #endif
26 |
27 | #define ISERIAL_MAX_LEN 20
28 |
29 | #define CDC_INTERFACE_COUNT 2
30 | #define CDC_ENPOINT_COUNT 3
31 |
32 | // kai:begin
33 | #undef CDC_ACM_INTERFACE
34 | #undef CDC_DATA_INTERFACE
35 | #undef CDC_FIRST_ENDPOINT
36 | #undef CDC_ENDPOINT_ACM
37 | #undef CDC_ENDPOINT_OUT
38 | #undef CDC_ENDPOINT_IN
39 | #undef CDC_RX
40 | #undef CDC_TX
41 |
42 | #define CDC_ACM_INTERFACE1 0 // CDC ACM
43 | #define CDC_DATA_INTERFACE1 1 // CDC Data
44 | #define CDC_FIRST_ENDPOINT1 1
45 | #define CDC_ENDPOINT_ACM1 (CDC_FIRST_ENDPOINT1) // CDC First
46 | #define CDC_ENDPOINT_OUT1 (CDC_FIRST_ENDPOINT1+1)
47 | #define CDC_ENDPOINT_IN1 (CDC_FIRST_ENDPOINT1+2)
48 |
49 | #define CDC_ACM_INTERFACE2 2 // CDC ACM
50 | #define CDC_DATA_INTERFACE2 3 // CDC Data
51 | #define CDC_FIRST_ENDPOINT2 4
52 | #define CDC_ENDPOINT_ACM2 (CDC_FIRST_ENDPOINT2) // CDC First
53 | #define CDC_ENDPOINT_OUT2 (CDC_FIRST_ENDPOINT2+1)
54 | #define CDC_ENDPOINT_IN2 (CDC_FIRST_ENDPOINT2+2)
55 |
56 | // only one of both interfaces is functional:
57 | #define ACTIVE_INTERFACE 1
58 |
59 | #if ACTIVE_INTERFACE == 1
60 | # define CDC_CLASS_INTERFACE CDC_ACM_INTERFACE1
61 | # define CDC_RX CDC_ENDPOINT_OUT1
62 | # define CDC_TX CDC_ENDPOINT_IN1
63 | #else
64 | # define CDC_CLASS_INTERFACE CDC_ACM_INTERFACE2
65 | # define CDC_RX CDC_ENDPOINT_OUT2
66 | # define CDC_TX CDC_ENDPOINT_IN2
67 | #endif
68 |
69 | #undef USB_VID
70 | #define USB_VID 0x2342
71 | // kai:end
72 |
73 | #define INTERFACE_COUNT (MSC_INTERFACE + MSC_INTERFACE_COUNT)
74 |
75 | #define IMANUFACTURER 1
76 | #define IPRODUCT 2
77 | #define ISERIAL 3
78 |
--------------------------------------------------------------------------------
/test/arduino_leonardo_bridge_multi_cdc/arduino_leonardo_bridge_multi_cdc.ino:
--------------------------------------------------------------------------------
1 | /*
2 | bridge USB-serial to hardware-serial
3 |
4 | for Arduinos based on ATmega32u4 (Leonardo and compatible Pro Micro, Micro)
5 | hardware serial is configured with baud-rate, databits, stopbits, parity as send over USB
6 |
7 | see https://github.com/arduino/Arduino/tree/master/hardware/arduino/avr/cores/arduino
8 | -> CDC.cpp|HardwareSerial.cpp for serial implementation details
9 |
10 | this sketch is mainly for demonstration / test of CDC communication
11 | performance as real usb-serial bridge would be inacceptable as each byte is send in separate USB packet
12 | */
13 |
14 | uint32_t baud = 9600;
15 | uint8_t databits = 8;
16 | uint8_t stopbits = 1;
17 | uint8_t parity = 0;
18 |
19 | void setup() {
20 | Serial.begin(baud); // USB
21 | Serial1.begin(baud, SERIAL_8N1);
22 | }
23 |
24 | void loop() {
25 | // show USB connected state
26 | if (Serial) TXLED1;
27 | else TXLED0;
28 |
29 | // configure hardware serial
30 | if (Serial.baud() != baud ||
31 | Serial.numbits() != databits ||
32 | Serial.stopbits() != stopbits ||
33 | Serial.paritytype() != parity) {
34 | baud = Serial.baud();
35 | databits = Serial.numbits();
36 | stopbits = Serial.stopbits();
37 | parity = Serial.paritytype();
38 | uint8_t config = 0; // ucsrc register
39 | switch (databits) {
40 | case 5: break;
41 | case 6: config |= 2; break;
42 | case 7: config |= 4; break;
43 | case 8: config |= 6; break;
44 | default: config |= 6;
45 | }
46 | switch (stopbits) {
47 | case 2: config |= 8;
48 | // 1.5 stopbits not supported
49 | }
50 | switch (parity) {
51 | case 1: config |= 0x30; break; // odd
52 | case 2: config |= 0x20; break; // even
53 | // mark, space not supported
54 | }
55 | Serial1.end();
56 | Serial1.begin(baud, config);
57 | }
58 |
59 | // bridge
60 | if (Serial.available() > 0)
61 | Serial1.write(Serial.read());
62 | if (Serial1.available() > 0)
63 | Serial.write(Serial1.read());
64 | }
65 |
--------------------------------------------------------------------------------
/test/pi_pico/README.md:
--------------------------------------------------------------------------------
1 |
2 | # `tinyusb_dev_cdc_dual_ports.uf2`
3 |
4 | compiled from `C:/Program Files/Raspberry Pi/Pico SDK v1.5.1/pico-sdk/lib/tinyusb/examples/device/cdc_dual_ports`
5 | to `C:/Users/` _user_`/Documents/Pico-v1.5.1/pico-examples/build/usb/device/tinyusb_device_examples/cdc_dual_ports/tinyusb_dev_cdc_dual_ports.uf2`
6 |
7 |
--------------------------------------------------------------------------------
/test/pi_pico/tinyusb_dev_cdc_dual_ports.uf2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mik3y/usb-serial-for-android/c608aadc59d2d09f5e34ab928de42c16bf69864f/test/pi_pico/tinyusb_dev_cdc_dual_ports.uf2
--------------------------------------------------------------------------------
/test/rfc2217_server.diff:
--------------------------------------------------------------------------------
1 | *** /n/archiv/python/rfc2217_server.py 2018-03-10 09:02:07.613771600 +0100
2 | --- rfc2217_server.py 2018-03-09 20:57:44.933717100 +0100
3 | ***************
4 | *** 26,31 ****
5 | --- 26,32 ----
6 | self,
7 | logger=logging.getLogger('rfc2217.server') if debug else None)
8 | self.log = logging.getLogger('redirector')
9 | + self.dlog = logging.getLogger('data')
10 |
11 | def statusline_poller(self):
12 | self.log.debug('status line poll thread started')
13 | ***************
14 | *** 55,60 ****
15 | --- 56,62 ----
16 | try:
17 | data = self.serial.read(self.serial.in_waiting or 1)
18 | if data:
19 | + self.dlog.debug("serial read: "+data.encode('hex'))
20 | # escape outgoing data when needed (Telnet IAC (0xff) character)
21 | self.write(b''.join(self.rfc2217.escape(data)))
22 | except socket.error as msg:
23 | ***************
24 | *** 76,81 ****
25 | --- 78,84 ----
26 | data = self.socket.recv(1024)
27 | if not data:
28 | break
29 | + self.dlog.debug("socket read: "+data.encode('hex'))
30 | self.serial.write(b''.join(self.rfc2217.filter(data)))
31 | except socket.error as msg:
32 | self.log.error('{}'.format(msg))
33 | ***************
34 | *** 132,137 ****
35 | --- 135,141 ----
36 | logging.basicConfig(level=logging.INFO)
37 | #~ logging.getLogger('root').setLevel(logging.INFO)
38 | logging.getLogger('rfc2217').setLevel(level)
39 | + logging.getLogger('data').setLevel(level)
40 |
41 | # connect to serial port
42 | ser = serial.serial_for_url(args.SERIALPORT, do_not_open=True)
43 |
--------------------------------------------------------------------------------
/usbSerialExamples/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | }
4 |
5 | android {
6 | compileSdkVersion 35
7 |
8 | compileOptions {
9 | sourceCompatibility JavaVersion.VERSION_1_8
10 | targetCompatibility JavaVersion.VERSION_1_8
11 | }
12 |
13 | defaultConfig {
14 | minSdkVersion 17
15 | targetSdkVersion 35
16 | vectorDrawables.useSupportLibrary = true
17 |
18 | missingDimensionStrategy 'device', 'anyDevice'
19 | }
20 |
21 | buildTypes {
22 | release {
23 | minifyEnabled true
24 | }
25 | }
26 | namespace 'com.hoho.android.usbserial.examples'
27 | }
28 |
29 | dependencies {
30 | implementation project(':usbSerialForAndroid')
31 | implementation 'androidx.appcompat:appcompat:1.6.1' // later versions have minsdk 21
32 | implementation 'com.google.android.material:material:1.11.0' // later versions have minsdk 19
33 | }
34 |
--------------------------------------------------------------------------------
/usbSerialExamples/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
11 |
15 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/usbSerialExamples/src/main/java/com/hoho/android/usbserial/examples/CustomProber.java:
--------------------------------------------------------------------------------
1 | package com.hoho.android.usbserial.examples;
2 |
3 | import com.hoho.android.usbserial.driver.FtdiSerialDriver;
4 | import com.hoho.android.usbserial.driver.ProbeTable;
5 | import com.hoho.android.usbserial.driver.UsbSerialProber;
6 |
7 | /**
8 | * add devices here, that are not known to DefaultProber
9 | *
10 | * if the App should auto start for these devices, also
11 | * add IDs to app/src/main/res/xml/device_filter.xml
12 | */
13 | class CustomProber {
14 |
15 | static UsbSerialProber getCustomProber() {
16 | ProbeTable customTable = new ProbeTable();
17 | customTable.addProduct(0x1234, 0x0001, FtdiSerialDriver.class); // e.g. device with custom VID+PID
18 | customTable.addProduct(0x1234, 0x0002, FtdiSerialDriver.class); // e.g. device with custom VID+PID
19 | return new UsbSerialProber(customTable);
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/usbSerialExamples/src/main/java/com/hoho/android/usbserial/examples/DevicesFragment.java:
--------------------------------------------------------------------------------
1 | package com.hoho.android.usbserial.examples;
2 |
3 | import android.app.AlertDialog;
4 | import android.content.Context;
5 | import android.hardware.usb.UsbDevice;
6 | import android.hardware.usb.UsbManager;
7 | import android.os.Bundle;
8 |
9 | import androidx.annotation.NonNull;
10 | import androidx.fragment.app.Fragment;
11 | import androidx.fragment.app.ListFragment;
12 | import android.view.Menu;
13 | import android.view.MenuInflater;
14 | import android.view.MenuItem;
15 | import android.view.View;
16 | import android.view.ViewGroup;
17 | import android.widget.ArrayAdapter;
18 | import android.widget.ListView;
19 | import android.widget.TextView;
20 | import android.widget.Toast;
21 |
22 | import com.hoho.android.usbserial.driver.UsbSerialDriver;
23 | import com.hoho.android.usbserial.driver.UsbSerialProber;
24 |
25 | import java.util.ArrayList;
26 | import java.util.Locale;
27 |
28 | public class DevicesFragment extends ListFragment {
29 |
30 | static class ListItem {
31 | UsbDevice device;
32 | int port;
33 | UsbSerialDriver driver;
34 |
35 | ListItem(UsbDevice device, int port, UsbSerialDriver driver) {
36 | this.device = device;
37 | this.port = port;
38 | this.driver = driver;
39 | }
40 | }
41 |
42 | private final ArrayList listItems = new ArrayList<>();
43 | private ArrayAdapter listAdapter;
44 | private int baudRate = 19200;
45 | private boolean withIoManager = true;
46 |
47 | @Override
48 | public void onCreate(Bundle savedInstanceState) {
49 | super.onCreate(savedInstanceState);
50 | setHasOptionsMenu(true);
51 | listAdapter = new ArrayAdapter(getActivity(), 0, listItems) {
52 | @NonNull
53 | @Override
54 | public View getView(int position, View view, @NonNull ViewGroup parent) {
55 | ListItem item = listItems.get(position);
56 | if (view == null)
57 | view = getActivity().getLayoutInflater().inflate(R.layout.device_list_item, parent, false);
58 | TextView text1 = view.findViewById(R.id.text1);
59 | TextView text2 = view.findViewById(R.id.text2);
60 | if(item.driver == null)
61 | text1.setText("");
62 | else if(item.driver.getPorts().size() == 1)
63 | text1.setText(item.driver.getClass().getSimpleName().replace("SerialDriver",""));
64 | else
65 | text1.setText(item.driver.getClass().getSimpleName().replace("SerialDriver","")+", Port "+item.port);
66 | text2.setText(String.format(Locale.US, "Vendor %04X, Product %04X", item.device.getVendorId(), item.device.getProductId()));
67 | return view;
68 | }
69 | };
70 | }
71 |
72 | @Override
73 | public void onActivityCreated(Bundle savedInstanceState) {
74 | super.onActivityCreated(savedInstanceState);
75 | setListAdapter(null);
76 | View header = getActivity().getLayoutInflater().inflate(R.layout.device_list_header, null, false);
77 | getListView().addHeaderView(header, null, false);
78 | setEmptyText("");
79 | ((TextView) getListView().getEmptyView()).setTextSize(18);
80 | setListAdapter(listAdapter);
81 | }
82 |
83 | @Override
84 | public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {
85 | inflater.inflate(R.menu.menu_devices, menu);
86 | }
87 |
88 | @Override
89 | public void onResume() {
90 | super.onResume();
91 | refresh();
92 | }
93 |
94 | @Override
95 | public boolean onOptionsItemSelected(MenuItem item) {
96 | int id = item.getItemId();
97 | if(id == R.id.refresh) {
98 | refresh();
99 | return true;
100 | } else if (id ==R.id.baud_rate) {
101 | final String[] values = getResources().getStringArray(R.array.baud_rates);
102 | int pos = java.util.Arrays.asList(values).indexOf(String.valueOf(baudRate));
103 | AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
104 | builder.setTitle("Baud rate");
105 | builder.setSingleChoiceItems(values, pos, (dialog, which) -> {
106 | baudRate = Integer.parseInt(values[which]);
107 | dialog.dismiss();
108 | });
109 | builder.create().show();
110 | return true;
111 | } else if (id ==R.id.read_mode) {
112 | final String[] values = getResources().getStringArray(R.array.read_modes);
113 | int pos = withIoManager ? 0 : 1; // read_modes[0]=event/io-manager, read_modes[1]=direct
114 | AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
115 | builder.setTitle("Read mode");
116 | builder.setSingleChoiceItems(values, pos, (dialog, which) -> {
117 | withIoManager = (which == 0);
118 | dialog.dismiss();
119 | });
120 | builder.create().show();
121 | return true;
122 | } else {
123 | return super.onOptionsItemSelected(item);
124 | }
125 | }
126 |
127 | void refresh() {
128 | UsbManager usbManager = (UsbManager) getActivity().getSystemService(Context.USB_SERVICE);
129 | UsbSerialProber usbDefaultProber = UsbSerialProber.getDefaultProber();
130 | UsbSerialProber usbCustomProber = CustomProber.getCustomProber();
131 | listItems.clear();
132 | for(UsbDevice device : usbManager.getDeviceList().values()) {
133 | UsbSerialDriver driver = usbDefaultProber.probeDevice(device);
134 | if(driver == null) {
135 | driver = usbCustomProber.probeDevice(device);
136 | }
137 | if(driver != null) {
138 | for(int port = 0; port < driver.getPorts().size(); port++)
139 | listItems.add(new ListItem(device, port, driver));
140 | } else {
141 | listItems.add(new ListItem(device, 0, null));
142 | }
143 | }
144 | listAdapter.notifyDataSetChanged();
145 | }
146 |
147 | @Override
148 | public void onListItemClick(@NonNull ListView l, @NonNull View v, int position, long id) {
149 | ListItem item = listItems.get(position-1);
150 | if(item.driver == null) {
151 | Toast.makeText(getActivity(), "no driver", Toast.LENGTH_SHORT).show();
152 | } else {
153 | Bundle args = new Bundle();
154 | args.putInt("device", item.device.getDeviceId());
155 | args.putInt("port", item.port);
156 | args.putInt("baud", baudRate);
157 | args.putBoolean("withIoManager", withIoManager);
158 | Fragment fragment = new TerminalFragment();
159 | fragment.setArguments(args);
160 | getFragmentManager().beginTransaction().replace(R.id.fragment, fragment, "terminal").addToBackStack(null).commit();
161 | }
162 | }
163 |
164 | }
165 |
--------------------------------------------------------------------------------
/usbSerialExamples/src/main/java/com/hoho/android/usbserial/examples/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.hoho.android.usbserial.examples;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import androidx.fragment.app.FragmentManager;
6 | import androidx.appcompat.app.AppCompatActivity;
7 | import androidx.appcompat.widget.Toolbar;
8 |
9 | public class MainActivity extends AppCompatActivity implements FragmentManager.OnBackStackChangedListener {
10 |
11 | @Override
12 | protected void onCreate(Bundle savedInstanceState) {
13 | super.onCreate(savedInstanceState);
14 | setContentView(R.layout.activity_main);
15 | Toolbar toolbar = findViewById(R.id.toolbar);
16 | setSupportActionBar(toolbar);
17 | getSupportFragmentManager().addOnBackStackChangedListener(this);
18 | if (savedInstanceState == null)
19 | getSupportFragmentManager().beginTransaction().add(R.id.fragment, new DevicesFragment(), "devices").commit();
20 | else
21 | onBackStackChanged();
22 | }
23 |
24 | @Override
25 | public void onBackStackChanged() {
26 | getSupportActionBar().setDisplayHomeAsUpEnabled(getSupportFragmentManager().getBackStackEntryCount()>0);
27 | }
28 |
29 | @Override
30 | public boolean onSupportNavigateUp() {
31 | onBackPressed();
32 | return true;
33 | }
34 |
35 | @Override
36 | protected void onNewIntent(Intent intent) {
37 | if("android.hardware.usb.action.USB_DEVICE_ATTACHED".equals(intent.getAction())) {
38 | TerminalFragment terminal = (TerminalFragment)getSupportFragmentManager().findFragmentByTag("terminal");
39 | if (terminal != null)
40 | terminal.status("USB device detected");
41 | }
42 | super.onNewIntent(intent);
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/usbSerialExamples/src/main/res/drawable/ic_delete_white_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/usbSerialExamples/src/main/res/drawable/ic_send_white_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/usbSerialExamples/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
13 |
14 |
19 |
20 |
21 |
22 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/usbSerialExamples/src/main/res/layout/device_list_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
--------------------------------------------------------------------------------
/usbSerialExamples/src/main/res/layout/device_list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
16 |
17 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/usbSerialExamples/src/main/res/layout/fragment_terminal.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
21 |
22 |
31 |
32 |
35 |
36 |
43 |
44 |
53 |
54 |
57 |
58 |
67 |
68 |
77 |
78 |
79 |
80 |
84 |
85 |
95 |
96 |
101 |
102 |
106 |
107 |
111 |
112 |
119 |
120 |
125 |
126 |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/usbSerialExamples/src/main/res/menu/menu_devices.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
--------------------------------------------------------------------------------
/usbSerialExamples/src/main/res/menu/menu_terminal.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
--------------------------------------------------------------------------------
/usbSerialExamples/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mik3y/usb-serial-for-android/c608aadc59d2d09f5e34ab928de42c16bf69864f/usbSerialExamples/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/usbSerialExamples/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mik3y/usb-serial-for-android/c608aadc59d2d09f5e34ab928de42c16bf69864f/usbSerialExamples/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/usbSerialExamples/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mik3y/usb-serial-for-android/c608aadc59d2d09f5e34ab928de42c16bf69864f/usbSerialExamples/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/usbSerialExamples/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mik3y/usb-serial-for-android/c608aadc59d2d09f5e34ab928de42c16bf69864f/usbSerialExamples/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/usbSerialExamples/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mik3y/usb-serial-for-android/c608aadc59d2d09f5e34ab928de42c16bf69864f/usbSerialExamples/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/usbSerialExamples/src/main/res/values/arrays.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - 2400
5 | - 9600
6 | - 19200
7 | - 57600
8 | - 115200
9 |
10 |
11 | - event: SerialInputOutputManager.onNewData()
12 | - direct: UsbSerialPort.read()
13 |
14 |
15 |
--------------------------------------------------------------------------------
/usbSerialExamples/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #949C29
4 | #61671B
5 | #D8E33B
6 |
7 | #00FF00
8 | #82CAFF
9 | #FFDB58
10 |
11 |
--------------------------------------------------------------------------------
/usbSerialExamples/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | USB Serial For Android Example
4 | USB Serial Example
5 | USB Devices
6 |
7 |
--------------------------------------------------------------------------------
/usbSerialExamples/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/usbSerialExamples/src/main/res/xml/device_filter.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/usbSerialForAndroid/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | id 'maven-publish'
4 | }
5 |
6 | android {
7 | compileSdkVersion 35
8 |
9 | defaultConfig {
10 | minSdkVersion 17
11 | targetSdkVersion 35
12 | consumerProguardFiles 'proguard-rules.pro'
13 |
14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
15 | testInstrumentationRunnerArguments = [ // Raspi Windows LinuxVM ...
16 | 'rfc2217_server_host': '192.168.0.78',
17 | 'rfc2217_server_nonstandard_baudrates': 'true', // true false false
18 | ]
19 | }
20 | compileOptions {
21 | sourceCompatibility JavaVersion.VERSION_1_8
22 | targetCompatibility JavaVersion.VERSION_1_8
23 | }
24 | namespace 'com.hoho.android.usbserial'
25 | publishing {
26 | // if coverage is enabled, change 'release' to 'anyDeviceRelease' or comment out publishing rule
27 | singleVariant('release') {
28 | withSourcesJar()
29 | }
30 | }
31 | }
32 |
33 | dependencies {
34 | implementation "androidx.annotation:annotation:1.9.1"
35 | testImplementation 'junit:junit:4.13.2'
36 | testImplementation 'org.mockito:mockito-core:5.15.2'
37 | androidTestImplementation 'androidx.appcompat:appcompat:1.6.1' // later versions have minsdk 21
38 | androidTestImplementation 'androidx.test:core:1.5.0' // later versions have minsdk 19
39 | androidTestImplementation 'androidx.test:runner:1.5.2' // later versions have minsdk 19
40 | androidTestImplementation 'commons-net:commons-net:3.9.0' // later versions fail on old Android devices with missing java.time.Duration class
41 | androidTestImplementation 'org.apache.commons:commons-lang3:3.14.0'
42 | }
43 |
44 | // gradle task: publishToMavenLocal
45 | project.afterEvaluate {
46 | publishing {
47 | publications {
48 | release(MavenPublication) {
49 | from components.release
50 |
51 | // values used for local maven repo, jitpack uses github release:
52 | groupId 'com.github.mik3y'
53 | artifactId 'usb-serial-for-android'
54 | version '3.8.0beta'
55 | }
56 | }
57 | }
58 | }
59 |
60 | //apply from: 'coverage.gradle'
61 |
--------------------------------------------------------------------------------
/usbSerialForAndroid/coverage.gradle:
--------------------------------------------------------------------------------
1 | // see https://github.com/mik3y/usb-serial-for-android/wiki/Device-Tests-&-Coverage-Report for instructions
2 | apply plugin: 'jacoco'
3 |
4 | android {
5 | flavorDimensions += 'device'
6 | productFlavors {
7 | anyDevice {
8 | // Used as fallback in usbSerialExample/build.gradle -> missingDimensionStrategy, but not for coverage report
9 | dimension 'device'
10 | }
11 | mcp2221 {
12 | dimension 'device'
13 | testInstrumentationRunnerArguments = ['test_device_driver': 'CdcAcm']
14 | }
15 | ch340 {
16 | dimension 'device'
17 | testInstrumentationRunnerArguments = ['test_device_driver': 'Ch34x']
18 | }
19 | cp2102 { // and cp2105 first port
20 | dimension 'device'
21 | testInstrumentationRunnerArguments = ['test_device_driver': 'Cp21xx', 'test_device_port': '0']
22 | }
23 | cp2105 { // second port
24 | dimension 'device'
25 | testInstrumentationRunnerArguments = ['test_device_driver': 'Cp21xx', 'test_device_port': '1']
26 | }
27 | ft232 { // and ft2232 first port
28 | dimension 'device'
29 | testInstrumentationRunnerArguments = ['test_device_driver': 'Ftdi', 'test_device_port': '0']
30 | }
31 | ft2232 { // second port
32 | dimension 'device'
33 | testInstrumentationRunnerArguments = ['test_device_driver': 'Ftdi', 'test_device_port': '1']
34 | }
35 | pl2303 {
36 | dimension 'device'
37 | testInstrumentationRunnerArguments = ['test_device_driver': 'Prolific']
38 | }
39 | pl2303t {
40 | dimension 'device'
41 | testInstrumentationRunnerArguments = ['test_device_driver': 'Prolific']
42 | }
43 | pl2303g {
44 | dimension 'device'
45 | testInstrumentationRunnerArguments = ['test_device_driver': 'Prolific']
46 | }
47 | }
48 |
49 | buildTypes {
50 | debug {
51 | enableUnitTestCoverage true
52 | enableAndroidTestCoverage true
53 | }
54 | }
55 | }
56 |
57 | // create report even if tests fail
58 | project.gradle.taskGraph.whenReady {
59 | -> project.tasks.findAll { it.name =~ /connected.+AndroidTest/ }.each {
60 | it.ignoreFailures = true
61 | }
62 | }
63 |
64 | task jacocoTestReport(type: JacocoReport, dependsOn: ['compileAnyDeviceDebugSources'
65 | /*, 'testAnyDeviceDebugUnitTest' */
66 | /*, 'createDebugCoverageReport' */]) {
67 | def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*']
68 | def debugTree = fileTree(dir: "$project.buildDir/intermediates/javac/anyDeviceDebug", excludes: fileFilter)
69 | def mainSrc = "$project.projectDir/src/main/java"
70 |
71 | reports.xml.required = true
72 | sourceDirectories.from files([mainSrc])
73 | classDirectories.from files([debugTree])
74 | executionData.from fileTree(dir: project.buildDir, includes: [
75 | 'outputs/unit_test_code_coverage/anyDeviceDebugUnitTest/testAnyDeviceDebugUnitTest.exec',
76 | 'outputs/code_coverage/*DebugAndroidTest/connected/*/coverage.ec'
77 | ])
78 | }
79 |
--------------------------------------------------------------------------------
/usbSerialForAndroid/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | -keep class com.hoho.android.usbserial.driver.* { *; }
2 |
--------------------------------------------------------------------------------
/usbSerialForAndroid/src/androidTest/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/usbSerialForAndroid/src/androidTest/java/com/hoho/android/usbserial/CrossoverTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * test multiple devices or multiple ports on same device
3 | *
4 | * TxD and RxD have to be cross connected
5 | */
6 | package com.hoho.android.usbserial;
7 |
8 | import android.content.Context;
9 | import android.hardware.usb.UsbManager;
10 | import androidx.test.core.app.ApplicationProvider;
11 | import androidx.test.platform.app.InstrumentationRegistry;
12 | import androidx.test.runner.AndroidJUnit4;
13 | import android.util.Log;
14 |
15 | import com.hoho.android.usbserial.driver.UsbSerialDriver;
16 | import com.hoho.android.usbserial.driver.UsbSerialPort;
17 | import com.hoho.android.usbserial.driver.UsbSerialProber;
18 | import com.hoho.android.usbserial.util.TestBuffer;
19 | import com.hoho.android.usbserial.util.UsbWrapper;
20 |
21 | import org.junit.Before;
22 | import org.junit.Rule;
23 | import org.junit.Test;
24 | import org.junit.rules.TestRule;
25 | import org.junit.rules.TestWatcher;
26 | import org.junit.runner.Description;
27 | import org.junit.runner.RunWith;
28 |
29 | import java.io.IOException;
30 | import java.util.EnumSet;
31 | import java.util.List;
32 |
33 | import static org.hamcrest.CoreMatchers.equalTo;
34 | import static org.junit.Assert.assertEquals;
35 | import static org.junit.Assert.assertNotEquals;
36 | import static org.junit.Assert.assertNull;
37 | import static org.junit.Assert.assertThat;
38 | import static org.junit.Assert.fail;
39 | import static org.junit.Assume.assumeTrue;
40 |
41 | @RunWith(AndroidJUnit4.class)
42 | public class CrossoverTest {
43 |
44 | private final static String TAG = CrossoverTest.class.getSimpleName();
45 |
46 | private Context context;
47 | private UsbManager usbManager;
48 | private UsbWrapper usb1, usb2;
49 |
50 | @Rule
51 | public TestRule watcher = new TestWatcher() {
52 | protected void starting(Description description) {
53 | Log.i(TAG, "===== starting test: " + description.getMethodName()+ " =====");
54 | }
55 | };
56 |
57 | @Before
58 | public void setUp() throws Exception {
59 | assumeTrue("ignore test for device specific coverage report",
60 | InstrumentationRegistry.getArguments().getString("test_device_driver") == null);
61 |
62 | context = ApplicationProvider.getApplicationContext();
63 | usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
64 | List availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(usbManager);
65 | assertNotEquals("no USB device found", 0, availableDrivers.size());
66 | if (availableDrivers.size() == 0) {
67 | fail("no USB device found");
68 | } else if (availableDrivers.size() == 1) {
69 | assertEquals("expected device with 2 ports.", 2, availableDrivers.get(0).getPorts().size());
70 | usb1 = new UsbWrapper(context, availableDrivers.get(0), 0);
71 | usb2 = new UsbWrapper(context, availableDrivers.get(0), 1);
72 | } else {
73 | assertEquals("expected 2 devices with 1 port.", 1, availableDrivers.get(0).getPorts().size());
74 | assertEquals("expected 2 devices with 1 port.", 1, availableDrivers.get(1).getPorts().size());
75 | usb1 = new UsbWrapper(context, availableDrivers.get(0), 0);
76 | usb2 = new UsbWrapper(context, availableDrivers.get(1), 0);
77 | }
78 | usb1.setUp();
79 | usb2.setUp();
80 | }
81 |
82 | @Test
83 | public void reopen() throws Exception {
84 | byte[] buf;
85 |
86 | usb1.open(EnumSet.of(UsbWrapper.OpenCloseFlags.NO_IOMANAGER_THREAD));
87 | usb2.open(EnumSet.of(UsbWrapper.OpenCloseFlags.NO_IOMANAGER_THREAD));
88 | usb1.setParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
89 | usb2.setParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
90 |
91 | usb1.write("x".getBytes());
92 | buf = usb2.read(1);
93 | assertThat(buf, equalTo("x".getBytes()));
94 |
95 | usb2.write("y".getBytes());
96 | buf = usb1.read(1);
97 | assertThat(buf, equalTo("y".getBytes()));
98 |
99 | usb2.close(); // does not affect usb1 with individual UsbDeviceConnection on same device
100 |
101 | usb2.open(EnumSet.of(UsbWrapper.OpenCloseFlags.NO_IOMANAGER_THREAD));
102 | usb2.setParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
103 |
104 | usb1.write("x".getBytes());
105 | buf = usb2.read(1);
106 | assertThat(buf, equalTo("x".getBytes()));
107 |
108 | usb2.write("y".getBytes());
109 | buf = usb1.read(1);
110 | assertThat(buf, equalTo("y".getBytes()));
111 |
112 | usb1.close();
113 | usb2.close();
114 | }
115 |
116 | @Test
117 | public void ioManager() throws Exception {
118 | byte[] buf;
119 |
120 | // each SerialInputOutputManager thread runs in it's own SingleThreadExecutor
121 | usb1.open();
122 | usb2.open();
123 | usb1.setParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
124 | usb2.setParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
125 |
126 | usb1.write("x".getBytes());
127 | buf = usb2.read(1);
128 | assertThat(buf, equalTo("x".getBytes()));
129 |
130 | usb2.write("y".getBytes());
131 | buf = usb1.read(1);
132 | assertThat(buf, equalTo("y".getBytes()));
133 |
134 | usb1.close();
135 | usb2.close();
136 | }
137 |
138 | @Test
139 | public void baudRate() throws Exception {
140 | byte[] buf;
141 |
142 | usb1.open(EnumSet.of(UsbWrapper.OpenCloseFlags.NO_IOMANAGER_THREAD));
143 | usb2.open(EnumSet.of(UsbWrapper.OpenCloseFlags.NO_IOMANAGER_THREAD));
144 | usb1.setParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
145 | usb2.setParameters(9600, 8, 1, UsbSerialPort.PARITY_NONE);
146 |
147 | // a - start bit (0)
148 | // o - stop bit (1)
149 | // 0/1 - data bit
150 |
151 | // out 19k2: a00011001o
152 | // in 9k6: a 0 1 0 1 1 1 1 1 o
153 | usb1.write(new byte[]{(byte)0x98});
154 | buf = usb2.read(1);
155 | assertThat(buf, equalTo(new byte[]{(byte)0xfa}));
156 |
157 | // out 9k6: a 1 0 1 1 1 1 1 1 o
158 | // in 19k2: a01100111o
159 | usb2.write(new byte[]{(byte)0xfd});
160 | buf = usb1.read(1);
161 | assertThat(buf, equalTo(new byte[]{(byte)0xe6}));
162 |
163 | usb1.close();
164 | usb2.close();
165 | }
166 |
167 | @Test
168 | public void concurrent() throws Exception {
169 | // 115200 baud ~= 11kB/sec => ~1.5 second test duration with 16kB tbuf
170 | // concurrent (+ blocking) write calls as tbuf larger than any buffer size returned by UsbWrapper.getWriteSizes()
171 | // concurrent read calls in IoManager threads
172 | TestBuffer tbuf1 = new TestBuffer(16*1024);
173 | TestBuffer tbuf2 = new TestBuffer(16*1024);
174 |
175 | class WriteRunnable implements Runnable {
176 | public WriteRunnable(int port) { this.port = port; }
177 | private final int port;
178 | Exception exc;
179 | @Override
180 | public void run() {
181 | byte[] buf = new byte[1024];
182 | try {
183 | for(int i=0; i commandResponse = new ArrayList<>();
38 | public int writeDelay = 0;
39 |
40 | public TelnetWrapper(String host, int port) {
41 | this.host = host;
42 | this.port = port;
43 | telnetClient = null;
44 | }
45 |
46 | private void setUpFixtureInt() throws Exception {
47 | if(telnetClient != null)
48 | return;
49 | telnetClient = new TelnetClient();
50 | telnetClient.addOptionHandler(new TelnetOptionHandler(RFC2217_COM_PORT_OPTION, false, false, false, false) {
51 | @Override
52 | public int[] answerSubnegotiation(int[] suboptionData, int suboptionLength) {
53 | int[] data = new int[suboptionLength];
54 | System.arraycopy(suboptionData, 0, data, 0, suboptionLength);
55 | commandResponse.add(data);
56 | return super.answerSubnegotiation(suboptionData, suboptionLength);
57 | }
58 | });
59 |
60 | telnetClient.setConnectTimeout(2000);
61 | telnetClient.connect(host, port);
62 | telnetClient.setTcpNoDelay(true);
63 | writeStream = telnetClient.getOutputStream();
64 | readStream = telnetClient.getInputStream();
65 | }
66 |
67 | private int[] doCommand(String name, byte[] command) throws IOException, InterruptedException {
68 | commandResponse.clear();
69 | telnetClient.sendCommand((byte) TelnetCommand.SB);
70 | writeStream.write(command);
71 | telnetClient.sendCommand((byte)TelnetCommand.SE);
72 |
73 | for(int i=0; i 0) break;
75 | Thread.sleep(1);
76 | }
77 | assertEquals("RFC2217 " + name+ " w/o response.", 1, commandResponse.size());
78 | //Log.d(TAG, name + " -> " + Arrays.toString(commandResponse.get(0)));
79 | return commandResponse.get(0);
80 | }
81 |
82 | public void setUp() throws Exception {
83 | setUpFixtureInt();
84 | telnetClient.sendAYT(1000); // not correctly handled by rfc2217_server.py, but WARNING output "ignoring Telnet command: '\xf6'" is a nice separator between tests
85 | doCommand("purge-data", new byte[] {RFC2217_COM_PORT_OPTION, RFC2217_PURGE_DATA, 3});
86 | writeDelay = 0;
87 | }
88 |
89 | public void tearDown() {
90 | try {
91 | read(0);
92 | } catch (Exception ignored) {
93 | }
94 | }
95 |
96 | public void tearDownFixture() throws Exception {
97 | try {
98 | telnetClient.disconnect();
99 | } catch (Exception ignored) {}
100 | readStream = null;
101 | writeStream = null;
102 | telnetClient = null;
103 | }
104 |
105 | // wait full time
106 | public byte[] read() throws Exception {
107 | return read(-1, -1);
108 | }
109 | public byte[] read(int expectedLength) throws Exception {
110 | return read(expectedLength, -1);
111 | }
112 | public byte[] read(int expectedLength, int readWait) throws Exception {
113 | if(readWait == -1)
114 | readWait = TELNET_READ_WAIT;
115 | long end = System.currentTimeMillis() + readWait;
116 | ByteBuffer buf = ByteBuffer.allocate(65536);
117 | while(System.currentTimeMillis() < end) {
118 | if(readStream.available() > 0) {
119 | buf.put((byte) readStream.read());
120 | } else {
121 | if (expectedLength >= 0 && buf.position() >= expectedLength)
122 | break;
123 | Thread.sleep(1);
124 | }
125 | }
126 | byte[] data = new byte[buf.position()];
127 | buf.flip();
128 | buf.get(data);
129 | return data;
130 | }
131 |
132 | public void write(byte[] data) throws Exception{
133 | if(writeDelay != 0) {
134 | for(byte b : data) {
135 | writeStream.write(b);
136 | writeStream.flush();
137 | Thread.sleep(writeDelay);
138 | }
139 | } else {
140 | writeStream.write(data);
141 | writeStream.flush();
142 | }
143 | }
144 |
145 | public void setParameters(int baudRate, int dataBits, int stopBits, @UsbSerialPort.Parity int parity) throws IOException, InterruptedException, InvalidTelnetOptionException {
146 | doCommand("set-baudrate", new byte[] {RFC2217_COM_PORT_OPTION, RFC2217_SET_BAUDRATE, (byte)(baudRate>>24), (byte)(baudRate>>16), (byte)(baudRate>>8), (byte)baudRate});
147 | doCommand("set-datasize", new byte[] {RFC2217_COM_PORT_OPTION, RFC2217_SET_DATASIZE, (byte)dataBits});
148 | doCommand("set-stopsize", new byte[] {RFC2217_COM_PORT_OPTION, RFC2217_SET_STOPSIZE, (byte)stopBits});
149 | doCommand("set-parity", new byte[] {RFC2217_COM_PORT_OPTION, RFC2217_SET_PARITY, (byte)(parity+1)});
150 | }
151 |
152 | }
153 |
--------------------------------------------------------------------------------
/usbSerialForAndroid/src/androidTest/java/com/hoho/android/usbserial/util/TestBuffer.java:
--------------------------------------------------------------------------------
1 | package com.hoho.android.usbserial.util;
2 |
3 | import static org.junit.Assert.assertEquals;
4 | import static org.junit.Assert.assertNotEquals;
5 | import static org.junit.Assert.assertTrue;
6 |
7 | public class TestBuffer {
8 | public final byte[] buf;
9 | public int len;
10 |
11 | public TestBuffer(int length) {
12 | len = 0;
13 | buf = new byte[length];
14 | int i = 0;
15 | int j = 0;
16 | for (j = 0; j < length / 16; j++)
17 | for (int k = 0; k < 16; k++)
18 | buf[i++] = (byte) j;
19 | while (i < length)
20 | buf[i++] = (byte) j;
21 | }
22 |
23 | public boolean testRead(byte[] data) {
24 | assertNotEquals(0, data.length);
25 | assertTrue("got " + (len + data.length) + " bytes", (len + data.length) <= buf.length);
26 | for (int j = 0; j < data.length; j++)
27 | assertEquals("at pos " + (len + j), (byte) ((len + j) / 16), data[j]);
28 | len += data.length;
29 | //Log.d(TAG, "read " + len);
30 | return len == buf.length;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/usbSerialForAndroid/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/ChromeCcdSerialDriver.java:
--------------------------------------------------------------------------------
1 | package com.hoho.android.usbserial.driver;
2 |
3 | import android.hardware.usb.UsbConstants;
4 | import android.hardware.usb.UsbDevice;
5 | import android.hardware.usb.UsbEndpoint;
6 | import android.hardware.usb.UsbInterface;
7 | import android.util.Log;
8 |
9 | import java.io.IOException;
10 | import java.util.Collections;
11 | import java.util.EnumSet;
12 | import java.util.LinkedHashMap;
13 | import java.util.List;
14 | import java.util.Map;
15 | import java.util.ArrayList;
16 |
17 | public class ChromeCcdSerialDriver implements UsbSerialDriver{
18 |
19 | private final String TAG = ChromeCcdSerialDriver.class.getSimpleName();
20 |
21 | private final UsbDevice mDevice;
22 | private final List mPorts;
23 |
24 | @Override
25 | public UsbDevice getDevice() {
26 | return mDevice;
27 | }
28 |
29 | @Override
30 | public List getPorts() {
31 | return mPorts;
32 | }
33 |
34 | public ChromeCcdSerialDriver(UsbDevice mDevice) {
35 | this.mDevice = mDevice;
36 | mPorts = new ArrayList();
37 | for (int i = 0; i < 3; i++)
38 | mPorts.add(new ChromeCcdSerialPort(mDevice, i));
39 | }
40 |
41 | public class ChromeCcdSerialPort extends CommonUsbSerialPort {
42 | private UsbInterface mDataInterface;
43 |
44 | public ChromeCcdSerialPort(UsbDevice device, int portNumber) {
45 | super(device, portNumber);
46 | }
47 |
48 | @Override
49 | protected void openInt() throws IOException {
50 | Log.d(TAG, "claiming interfaces, count=" + mDevice.getInterfaceCount());
51 | mDataInterface = mDevice.getInterface(mPortNumber);
52 | if (!mConnection.claimInterface(mDataInterface, true)) {
53 | throw new IOException("Could not claim shared control/data interface");
54 | }
55 | Log.d(TAG, "endpoint count=" + mDataInterface.getEndpointCount());
56 | for (int i = 0; i < mDataInterface.getEndpointCount(); ++i) {
57 | UsbEndpoint ep = mDataInterface.getEndpoint(i);
58 | if ((ep.getDirection() == UsbConstants.USB_DIR_IN) && (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) {
59 | mReadEndpoint = ep;
60 | } else if ((ep.getDirection() == UsbConstants.USB_DIR_OUT) && (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) {
61 | mWriteEndpoint = ep;
62 | }
63 | }
64 | }
65 |
66 | @Override
67 | protected void closeInt() {
68 | try {
69 | mConnection.releaseInterface(mDataInterface);
70 | } catch(Exception ignored) {}
71 | }
72 |
73 | @Override
74 | public UsbSerialDriver getDriver() {
75 | return ChromeCcdSerialDriver.this;
76 | }
77 |
78 | @Override
79 | public void setParameters(int baudRate, int dataBits, int stopBits, int parity) throws IOException {
80 | throw new UnsupportedOperationException();
81 | }
82 | }
83 |
84 | public static Map getSupportedDevices() {
85 | final Map supportedDevices = new LinkedHashMap<>();
86 | supportedDevices.put(UsbId.VENDOR_GOOGLE, new int[]{
87 | UsbId.GOOGLE_CR50,
88 | });
89 | return supportedDevices;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CommonUsbSerialPort.java:
--------------------------------------------------------------------------------
1 | /* Copyright 2011-2013 Google Inc.
2 | * Copyright 2013 mike wakerly
3 | *
4 | * Project home page: https://github.com/mik3y/usb-serial-for-android
5 | */
6 |
7 | package com.hoho.android.usbserial.driver;
8 |
9 | import android.hardware.usb.UsbDevice;
10 | import android.hardware.usb.UsbDeviceConnection;
11 | import android.hardware.usb.UsbEndpoint;
12 | import android.hardware.usb.UsbRequest;
13 | import android.os.Build;
14 | import android.util.Log;
15 |
16 | import com.hoho.android.usbserial.util.MonotonicClock;
17 |
18 | import java.io.IOException;
19 | import java.nio.ByteBuffer;
20 | import java.util.EnumSet;
21 |
22 | /**
23 | * A base class shared by several driver implementations.
24 | *
25 | * @author mike wakerly (opensource@hoho.com)
26 | */
27 | public abstract class CommonUsbSerialPort implements UsbSerialPort {
28 |
29 | public static boolean DEBUG = false;
30 |
31 | private static final String TAG = CommonUsbSerialPort.class.getSimpleName();
32 | private static final int MAX_READ_SIZE = 16 * 1024; // = old bulkTransfer limit prior to Android 9
33 |
34 | protected final UsbDevice mDevice;
35 | protected final int mPortNumber;
36 |
37 | // non-null when open()
38 | protected UsbDeviceConnection mConnection;
39 | protected UsbEndpoint mReadEndpoint;
40 | protected UsbEndpoint mWriteEndpoint;
41 | protected UsbRequest mUsbRequest;
42 | protected FlowControl mFlowControl = FlowControl.NONE;
43 |
44 | /**
45 | * Internal write buffer.
46 | * Guarded by {@link #mWriteBufferLock}.
47 | * Default length = mReadEndpoint.getMaxPacketSize()
48 | **/
49 | protected byte[] mWriteBuffer;
50 | protected final Object mWriteBufferLock = new Object();
51 |
52 |
53 | public CommonUsbSerialPort(UsbDevice device, int portNumber) {
54 | mDevice = device;
55 | mPortNumber = portNumber;
56 | }
57 |
58 | @Override
59 | public String toString() {
60 | return String.format("<%s device_name=%s device_id=%s port_number=%s>",
61 | getClass().getSimpleName(), mDevice.getDeviceName(),
62 | mDevice.getDeviceId(), mPortNumber);
63 | }
64 |
65 | @Override
66 | public UsbDevice getDevice() {
67 | return mDevice;
68 | }
69 |
70 | @Override
71 | public int getPortNumber() {
72 | return mPortNumber;
73 | }
74 |
75 | @Override
76 | public UsbEndpoint getWriteEndpoint() { return mWriteEndpoint; }
77 |
78 | @Override
79 | public UsbEndpoint getReadEndpoint() { return mReadEndpoint; }
80 |
81 | /**
82 | * Returns the device serial number
83 | * @return serial number
84 | */
85 | @Override
86 | public String getSerial() {
87 | return mConnection.getSerial();
88 | }
89 |
90 | /**
91 | * Sets the size of the internal buffer used to exchange data with the USB
92 | * stack for write operations. Most users should not need to change this.
93 | *
94 | * @param bufferSize the size in bytes, <= 0 resets original size
95 | */
96 | public final void setWriteBufferSize(int bufferSize) {
97 | synchronized (mWriteBufferLock) {
98 | if (bufferSize <= 0) {
99 | if (mWriteEndpoint != null) {
100 | bufferSize = mWriteEndpoint.getMaxPacketSize();
101 | } else {
102 | mWriteBuffer = null;
103 | return;
104 | }
105 | }
106 | if (mWriteBuffer != null && bufferSize == mWriteBuffer.length) {
107 | return;
108 | }
109 | mWriteBuffer = new byte[bufferSize];
110 | }
111 | }
112 |
113 | @Override
114 | public void open(UsbDeviceConnection connection) throws IOException {
115 | if (mConnection != null) {
116 | throw new IOException("Already open");
117 | }
118 | if(connection == null) {
119 | throw new IllegalArgumentException("Connection is null");
120 | }
121 | mConnection = connection;
122 | boolean ok = false;
123 | try {
124 | openInt();
125 | if (mReadEndpoint == null || mWriteEndpoint == null) {
126 | throw new IOException("Could not get read & write endpoints");
127 | }
128 | mUsbRequest = new UsbRequest();
129 | mUsbRequest.initialize(mConnection, mReadEndpoint);
130 | ok = true;
131 | } finally {
132 | if (!ok) {
133 | try {
134 | close();
135 | } catch (Exception ignored) {}
136 | }
137 | }
138 | }
139 |
140 | protected abstract void openInt() throws IOException;
141 |
142 | @Override
143 | public void close() throws IOException {
144 | if (mConnection == null) {
145 | throw new IOException("Already closed");
146 | }
147 | UsbRequest usbRequest = mUsbRequest;
148 | mUsbRequest = null;
149 | try {
150 | usbRequest.cancel();
151 | } catch(Exception ignored) {}
152 | try {
153 | closeInt();
154 | } catch(Exception ignored) {}
155 | try {
156 | mConnection.close();
157 | } catch(Exception ignored) {}
158 | mConnection = null;
159 | }
160 |
161 | protected abstract void closeInt();
162 |
163 | /**
164 | * use simple USB request supported by all devices to test if connection is still valid
165 | */
166 | protected void testConnection(boolean full) throws IOException {
167 | testConnection(full, "USB get_status request failed");
168 | }
169 |
170 | protected void testConnection(boolean full, String msg) throws IOException {
171 | if(mUsbRequest == null) {
172 | throw new IOException("Connection closed");
173 | }
174 | if(!full) {
175 | return;
176 | }
177 | byte[] buf = new byte[2];
178 | int len = mConnection.controlTransfer(0x80 /*DEVICE*/, 0 /*GET_STATUS*/, 0, 0, buf, buf.length, 200);
179 | if(len < 0)
180 | throw new IOException(msg);
181 | }
182 |
183 | @Override
184 | public int read(final byte[] dest, final int timeout) throws IOException {
185 | if(dest.length == 0) {
186 | throw new IllegalArgumentException("Read buffer too small");
187 | }
188 | return read(dest, dest.length, timeout);
189 | }
190 |
191 | @Override
192 | public int read(final byte[] dest, final int length, final int timeout) throws IOException {return read(dest, length, timeout, true);}
193 |
194 | protected int read(final byte[] dest, int length, final int timeout, boolean testConnection) throws IOException {
195 | testConnection(false);
196 | if(length <= 0) {
197 | throw new IllegalArgumentException("Read length too small");
198 | }
199 | length = Math.min(length, dest.length);
200 | final int nread;
201 | if (timeout != 0) {
202 | // bulkTransfer will cause data loss with short timeout + high baud rates + continuous transfer
203 | // https://stackoverflow.com/questions/9108548/android-usb-host-bulktransfer-is-losing-data
204 | // but mConnection.requestWait(timeout) available since Android 8.0 es even worse,
205 | // as it crashes with short timeout, e.g.
206 | // A/libc: Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x276a in tid 29846 (pool-2-thread-1), pid 29618 (.usbserial.test)
207 | // /system/lib64/libusbhost.so (usb_request_wait+192)
208 | // /system/lib64/libandroid_runtime.so (android_hardware_UsbDeviceConnection_request_wait(_JNIEnv*, _jobject*, long)+84)
209 | // data loss / crashes were observed with timeout up to 200 msec
210 | long endTime = testConnection ? MonotonicClock.millis() + timeout : 0;
211 | int readMax = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) ? length : Math.min(length, MAX_READ_SIZE);
212 | nread = mConnection.bulkTransfer(mReadEndpoint, dest, readMax, timeout);
213 | // Android error propagation is improvable:
214 | // nread == -1 can be: timeout, connection lost, buffer to small, ???
215 | if(nread == -1 && testConnection)
216 | testConnection(MonotonicClock.millis() < endTime);
217 |
218 | } else {
219 | final ByteBuffer buf = ByteBuffer.wrap(dest, 0, length);
220 | if (!mUsbRequest.queue(buf, length)) {
221 | throw new IOException("Queueing USB request failed");
222 | }
223 | final UsbRequest response = mConnection.requestWait();
224 | if (response == null) {
225 | throw new IOException("Waiting for USB request failed");
226 | }
227 | nread = buf.position();
228 | // Android error propagation is improvable:
229 | // response != null & nread == 0 can be: connection lost, buffer to small, ???
230 | if(nread == 0) {
231 | testConnection(true);
232 | }
233 | }
234 | return Math.max(nread, 0);
235 | }
236 |
237 | @Override
238 | public void write(byte[] src, int timeout) throws IOException {write(src, src.length, timeout);}
239 |
240 | @Override
241 | public void write(final byte[] src, int length, final int timeout) throws IOException {
242 | int offset = 0;
243 | long startTime = MonotonicClock.millis();
244 | length = Math.min(length, src.length);
245 |
246 | testConnection(false);
247 | while (offset < length) {
248 | int requestTimeout;
249 | final int requestLength;
250 | final int actualLength;
251 |
252 | synchronized (mWriteBufferLock) {
253 | final byte[] writeBuffer;
254 |
255 | if (mWriteBuffer == null) {
256 | mWriteBuffer = new byte[mWriteEndpoint.getMaxPacketSize()];
257 | }
258 | requestLength = Math.min(length - offset, mWriteBuffer.length);
259 | if (offset == 0) {
260 | writeBuffer = src;
261 | } else {
262 | // bulkTransfer does not support offsets, make a copy.
263 | System.arraycopy(src, offset, mWriteBuffer, 0, requestLength);
264 | writeBuffer = mWriteBuffer;
265 | }
266 | if (timeout == 0 || offset == 0) {
267 | requestTimeout = timeout;
268 | } else {
269 | requestTimeout = (int)(startTime + timeout - MonotonicClock.millis());
270 | if(requestTimeout == 0)
271 | requestTimeout = -1;
272 | }
273 | if (requestTimeout < 0) {
274 | actualLength = -2;
275 | } else {
276 | actualLength = mConnection.bulkTransfer(mWriteEndpoint, writeBuffer, requestLength, requestTimeout);
277 | }
278 | }
279 | long elapsed = MonotonicClock.millis() - startTime;
280 | if (DEBUG) {
281 | Log.d(TAG, "Wrote " + actualLength + "/" + requestLength + " offset " + offset + "/" + length + " time " + elapsed + "/" + requestTimeout);
282 | }
283 | if (actualLength <= 0) {
284 | String msg = "Error writing " + requestLength + " bytes at offset " + offset + " of total " + src.length + " after " + elapsed + "msec, rc=" + actualLength;
285 | if (timeout != 0) {
286 | // could be buffer full because: writing to fast, stopped by flow control
287 | testConnection(elapsed < timeout, msg);
288 | throw new SerialTimeoutException(msg, offset);
289 | } else {
290 | throw new IOException(msg);
291 |
292 | }
293 | }
294 | offset += actualLength;
295 | }
296 | }
297 |
298 | @Override
299 | public boolean isOpen() {
300 | return mUsbRequest != null;
301 | }
302 |
303 | @Override
304 | public abstract void setParameters(int baudRate, int dataBits, int stopBits, @Parity int parity) throws IOException;
305 |
306 | @Override
307 | public boolean getCD() throws IOException { throw new UnsupportedOperationException(); }
308 |
309 | @Override
310 | public boolean getCTS() throws IOException { throw new UnsupportedOperationException(); }
311 |
312 | @Override
313 | public boolean getDSR() throws IOException { throw new UnsupportedOperationException(); }
314 |
315 | @Override
316 | public boolean getDTR() throws IOException { throw new UnsupportedOperationException(); }
317 |
318 | @Override
319 | public void setDTR(boolean value) throws IOException { throw new UnsupportedOperationException(); }
320 |
321 | @Override
322 | public boolean getRI() throws IOException { throw new UnsupportedOperationException(); }
323 |
324 | @Override
325 | public boolean getRTS() throws IOException { throw new UnsupportedOperationException(); }
326 |
327 | @Override
328 | public void setRTS(boolean value) throws IOException { throw new UnsupportedOperationException(); }
329 |
330 | @Override
331 | public EnumSet getControlLines() throws IOException { throw new UnsupportedOperationException(); }
332 |
333 | @Override
334 | public EnumSet getSupportedControlLines() throws IOException { return EnumSet.noneOf(ControlLine.class); }
335 |
336 | @Override
337 | public void setFlowControl(FlowControl flowcontrol) throws IOException {
338 | if (flowcontrol != FlowControl.NONE)
339 | throw new UnsupportedOperationException();
340 | }
341 |
342 | @Override
343 | public FlowControl getFlowControl() { return mFlowControl; }
344 |
345 | @Override
346 | public EnumSet getSupportedFlowControl() { return EnumSet.of(FlowControl.NONE); }
347 |
348 | @Override
349 | public boolean getXON() throws IOException { throw new UnsupportedOperationException(); }
350 |
351 | @Override
352 | public void purgeHwBuffers(boolean purgeWriteBuffers, boolean purgeReadBuffers) throws IOException { throw new UnsupportedOperationException(); }
353 |
354 | @Override
355 | public void setBreak(boolean value) throws IOException { throw new UnsupportedOperationException(); }
356 |
357 | }
358 |
--------------------------------------------------------------------------------
/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/GsmModemSerialDriver.java:
--------------------------------------------------------------------------------
1 | package com.hoho.android.usbserial.driver;
2 |
3 | import android.hardware.usb.UsbConstants;
4 | import android.hardware.usb.UsbDevice;
5 | import android.hardware.usb.UsbEndpoint;
6 | import android.hardware.usb.UsbInterface;
7 | import android.util.Log;
8 |
9 | import java.io.IOException;
10 | import java.util.Collections;
11 | import java.util.EnumSet;
12 | import java.util.LinkedHashMap;
13 | import java.util.List;
14 | import java.util.Map;
15 |
16 | public class GsmModemSerialDriver implements UsbSerialDriver{
17 |
18 | private final String TAG = GsmModemSerialDriver.class.getSimpleName();
19 |
20 | private final UsbDevice mDevice;
21 | private final UsbSerialPort mPort;
22 |
23 | @Override
24 | public UsbDevice getDevice() {
25 | return mDevice;
26 | }
27 |
28 | @Override
29 | public List getPorts() {
30 | return Collections.singletonList(mPort);
31 | }
32 |
33 | public GsmModemSerialDriver(UsbDevice mDevice) {
34 | this.mDevice = mDevice;
35 | mPort = new GsmModemSerialPort(mDevice, 0);
36 | }
37 |
38 | public class GsmModemSerialPort extends CommonUsbSerialPort {
39 |
40 | private UsbInterface mDataInterface;
41 |
42 | public GsmModemSerialPort(UsbDevice device, int portNumber) {
43 | super(device, portNumber);
44 | }
45 |
46 | @Override
47 | protected void openInt() throws IOException {
48 | Log.d(TAG, "claiming interfaces, count=" + mDevice.getInterfaceCount());
49 | mDataInterface = mDevice.getInterface(0);
50 | if (!mConnection.claimInterface(mDataInterface, true)) {
51 | throw new IOException("Could not claim shared control/data interface");
52 | }
53 | Log.d(TAG, "endpoint count=" + mDataInterface.getEndpointCount());
54 | for (int i = 0; i < mDataInterface.getEndpointCount(); ++i) {
55 | UsbEndpoint ep = mDataInterface.getEndpoint(i);
56 | if ((ep.getDirection() == UsbConstants.USB_DIR_IN) && (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) {
57 | mReadEndpoint = ep;
58 | } else if ((ep.getDirection() == UsbConstants.USB_DIR_OUT) && (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) {
59 | mWriteEndpoint = ep;
60 | }
61 | }
62 | initGsmModem();
63 | }
64 |
65 | @Override
66 | protected void closeInt() {
67 | try {
68 | mConnection.releaseInterface(mDataInterface);
69 | } catch(Exception ignored) {}
70 |
71 | }
72 |
73 | private int initGsmModem() throws IOException {
74 | int len = mConnection.controlTransfer(
75 | 0x21, 0x22, 0x01, 0, null, 0, 5000);
76 | if(len < 0) {
77 | throw new IOException("init failed");
78 | }
79 | return len;
80 | }
81 |
82 | @Override
83 | public UsbSerialDriver getDriver() {
84 | return GsmModemSerialDriver.this;
85 | }
86 |
87 | @Override
88 | public void setParameters(int baudRate, int dataBits, int stopBits, int parity) throws IOException {
89 | throw new UnsupportedOperationException();
90 | }
91 |
92 | }
93 |
94 | public static Map getSupportedDevices() {
95 | final Map supportedDevices = new LinkedHashMap<>();
96 | supportedDevices.put(UsbId.VENDOR_UNISOC, new int[]{
97 | UsbId.FIBOCOM_L610,
98 | UsbId.FIBOCOM_L612,
99 | });
100 | return supportedDevices;
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/ProbeTable.java:
--------------------------------------------------------------------------------
1 | /* Copyright 2011-2013 Google Inc.
2 | * Copyright 2013 mike wakerly
3 | *
4 | * Project home page: https://github.com/mik3y/usb-serial-for-android
5 | */
6 |
7 | package com.hoho.android.usbserial.driver;
8 |
9 | import android.hardware.usb.UsbDevice;
10 | import android.util.Pair;
11 |
12 | import java.lang.reflect.InvocationTargetException;
13 | import java.lang.reflect.Method;
14 | import java.util.LinkedHashMap;
15 | import java.util.Map;
16 |
17 | /**
18 | * Maps (vendor id, product id) pairs to the corresponding serial driver,
19 | * or invoke 'probe' method to check actual USB devices for matching interfaces.
20 | */
21 | public class ProbeTable {
22 |
23 | private final Map, Class extends UsbSerialDriver>> mVidPidProbeTable =
24 | new LinkedHashMap<>();
25 | private final Map> mMethodProbeTable = new LinkedHashMap<>();
26 |
27 | /**
28 | * Adds or updates a (vendor, product) pair in the table.
29 | *
30 | * @param vendorId the USB vendor id
31 | * @param productId the USB product id
32 | * @param driverClass the driver class responsible for this pair
33 | * @return {@code this}, for chaining
34 | */
35 | public ProbeTable addProduct(int vendorId, int productId,
36 | Class extends UsbSerialDriver> driverClass) {
37 | mVidPidProbeTable.put(Pair.create(vendorId, productId), driverClass);
38 | return this;
39 | }
40 |
41 | /**
42 | * Internal method to add all supported products from
43 | * {@code getSupportedProducts} static method.
44 | *
45 | * @param driverClass to be added
46 | */
47 | @SuppressWarnings("unchecked")
48 | void addDriver(Class extends UsbSerialDriver> driverClass) {
49 | Method method;
50 |
51 | try {
52 | method = driverClass.getMethod("getSupportedDevices");
53 | } catch (SecurityException | NoSuchMethodException e) {
54 | throw new RuntimeException(e);
55 | }
56 |
57 | final Map devices;
58 | try {
59 | devices = (Map) method.invoke(null);
60 | } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
61 | throw new RuntimeException(e);
62 | }
63 |
64 | for (Map.Entry entry : devices.entrySet()) {
65 | final int vendorId = entry.getKey();
66 | for (int productId : entry.getValue()) {
67 | addProduct(vendorId, productId, driverClass);
68 | }
69 | }
70 |
71 | try {
72 | method = driverClass.getMethod("probe", UsbDevice.class);
73 | mMethodProbeTable.put(method, driverClass);
74 | } catch (SecurityException | NoSuchMethodException ignored) {
75 | }
76 | }
77 |
78 | /**
79 | * Returns the driver for the given USB device, or {@code null} if no match.
80 | *
81 | * @param usbDevice the USB device to be probed
82 | * @return the driver class matching this pair, or {@code null}
83 | */
84 | public Class extends UsbSerialDriver> findDriver(final UsbDevice usbDevice) {
85 | final Pair pair = Pair.create(usbDevice.getVendorId(), usbDevice.getProductId());
86 | Class extends UsbSerialDriver> driverClass = mVidPidProbeTable.get(pair);
87 | if (driverClass != null)
88 | return driverClass;
89 | for (Map.Entry> entry : mMethodProbeTable.entrySet()) {
90 | try {
91 | Method method = entry.getKey();
92 | Object o = method.invoke(null, usbDevice);
93 | if((boolean)o)
94 | return entry.getValue();
95 | } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
96 | throw new RuntimeException(e);
97 | }
98 | }
99 | return null;
100 | }
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/SerialTimeoutException.java:
--------------------------------------------------------------------------------
1 | package com.hoho.android.usbserial.driver;
2 |
3 | import java.io.InterruptedIOException;
4 |
5 | /**
6 | * Signals that a timeout has occurred on serial write.
7 | * Similar to SocketTimeoutException.
8 | *
9 | * {@see InterruptedIOException#bytesTransferred} may contain bytes transferred
10 | */
11 | public class SerialTimeoutException extends InterruptedIOException {
12 | public SerialTimeoutException(String s, int bytesTransferred) {
13 | super(s);
14 | this.bytesTransferred = bytesTransferred;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/UsbId.java:
--------------------------------------------------------------------------------
1 | /* Copyright 2011-2013 Google Inc.
2 | * Copyright 2013 mike wakerly
3 | *
4 | * Project home page: https://github.com/mik3y/usb-serial-for-android
5 | */
6 |
7 | package com.hoho.android.usbserial.driver;
8 |
9 | /**
10 | * Registry of USB vendor/product ID constants.
11 | *
12 | * Culled from various sources; see
13 | * usb.ids for one listing.
14 | *
15 | * @author mike wakerly (opensource@hoho.com)
16 | */
17 | public final class UsbId {
18 |
19 | public static final int VENDOR_FTDI = 0x0403;
20 | public static final int FTDI_FT232R = 0x6001;
21 | public static final int FTDI_FT2232H = 0x6010;
22 | public static final int FTDI_FT4232H = 0x6011;
23 | public static final int FTDI_FT232H = 0x6014;
24 | public static final int FTDI_FT231X = 0x6015; // same ID for FT230X, FT231X, FT234XD
25 |
26 | public static final int VENDOR_SILABS = 0x10c4;
27 | public static final int SILABS_CP2102 = 0xea60; // same ID for CP2101, CP2103, CP2104, CP2109
28 | public static final int SILABS_CP2105 = 0xea70;
29 | public static final int SILABS_CP2108 = 0xea71;
30 |
31 | public static final int VENDOR_PROLIFIC = 0x067b;
32 | public static final int PROLIFIC_PL2303 = 0x2303; // device type 01, T, HX
33 | public static final int PROLIFIC_PL2303GC = 0x23a3; // device type HXN
34 | public static final int PROLIFIC_PL2303GB = 0x23b3; // "
35 | public static final int PROLIFIC_PL2303GT = 0x23c3; // "
36 | public static final int PROLIFIC_PL2303GL = 0x23d3; // "
37 | public static final int PROLIFIC_PL2303GE = 0x23e3; // "
38 | public static final int PROLIFIC_PL2303GS = 0x23f3; // "
39 |
40 | public static final int VENDOR_GOOGLE = 0x18d1;
41 | public static final int GOOGLE_CR50 = 0x5014;
42 |
43 | public static final int VENDOR_QINHENG = 0x1a86;
44 | public static final int QINHENG_CH340 = 0x7523;
45 | public static final int QINHENG_CH341A = 0x5523;
46 |
47 | public static final int VENDOR_UNISOC = 0x1782;
48 | public static final int FIBOCOM_L610 = 0x4D10;
49 | public static final int FIBOCOM_L612 = 0x4D12;
50 |
51 |
52 | private UsbId() {
53 | throw new IllegalAccessError("Non-instantiable class");
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/UsbSerialDriver.java:
--------------------------------------------------------------------------------
1 | /* Copyright 2011-2013 Google Inc.
2 | * Copyright 2013 mike wakerly
3 | *
4 | * Project home page: https://github.com/mik3y/usb-serial-for-android
5 | */
6 |
7 | package com.hoho.android.usbserial.driver;
8 |
9 | import android.hardware.usb.UsbDevice;
10 |
11 | import java.util.List;
12 |
13 | public interface UsbSerialDriver {
14 |
15 | /*
16 | * Additional interface properties. Invoked thru reflection.
17 | *
18 | UsbSerialDriver(UsbDevice device); // constructor with device
19 | static Map getSupportedDevices();
20 | static boolean probe(UsbDevice device); // optional
21 | */
22 |
23 |
24 | /**
25 | * Returns the raw {@link UsbDevice} backing this port.
26 | *
27 | * @return the device
28 | */
29 | UsbDevice getDevice();
30 |
31 | /**
32 | * Returns all available ports for this device. This list must have at least
33 | * one entry.
34 | *
35 | * @return the ports
36 | */
37 | List getPorts();
38 | }
39 |
--------------------------------------------------------------------------------
/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/UsbSerialPort.java:
--------------------------------------------------------------------------------
1 | /* Copyright 2011-2013 Google Inc.
2 | * Copyright 2013 mike wakerly
3 | *
4 | * Project home page: https://github.com/mik3y/usb-serial-for-android
5 | */
6 |
7 | package com.hoho.android.usbserial.driver;
8 |
9 | import android.hardware.usb.UsbDevice;
10 | import android.hardware.usb.UsbDeviceConnection;
11 | import android.hardware.usb.UsbEndpoint;
12 | import android.hardware.usb.UsbManager;
13 |
14 | import androidx.annotation.IntDef;
15 |
16 | import java.io.Closeable;
17 | import java.io.IOException;
18 | import java.lang.annotation.Retention;
19 | import java.lang.annotation.RetentionPolicy;
20 | import java.util.EnumSet;
21 |
22 | /**
23 | * Interface for a single serial port.
24 | *
25 | * @author mike wakerly (opensource@hoho.com)
26 | */
27 | public interface UsbSerialPort extends Closeable {
28 |
29 | /** 5 data bits. */
30 | int DATABITS_5 = 5;
31 | /** 6 data bits. */
32 | int DATABITS_6 = 6;
33 | /** 7 data bits. */
34 | int DATABITS_7 = 7;
35 | /** 8 data bits. */
36 | int DATABITS_8 = 8;
37 |
38 | /** Values for setParameters(..., parity) */
39 | @Retention(RetentionPolicy.SOURCE)
40 | @IntDef({PARITY_NONE, PARITY_ODD, PARITY_EVEN, PARITY_MARK, PARITY_SPACE})
41 | @interface Parity {}
42 | /** No parity. */
43 | int PARITY_NONE = 0;
44 | /** Odd parity. */
45 | int PARITY_ODD = 1;
46 | /** Even parity. */
47 | int PARITY_EVEN = 2;
48 | /** Mark parity. */
49 | int PARITY_MARK = 3;
50 | /** Space parity. */
51 | int PARITY_SPACE = 4;
52 |
53 | /** 1 stop bit. */
54 | int STOPBITS_1 = 1;
55 | /** 1.5 stop bits. */
56 | int STOPBITS_1_5 = 3;
57 | /** 2 stop bits. */
58 | int STOPBITS_2 = 2;
59 |
60 | /** Values for get[Supported]ControlLines() */
61 | enum ControlLine { RTS, CTS, DTR, DSR, CD, RI }
62 |
63 | /** Values for (set|get|getSupported)FlowControl() */
64 | enum FlowControl { NONE, RTS_CTS, DTR_DSR, XON_XOFF, XON_XOFF_INLINE }
65 |
66 | /** XON character used with flow control XON/XOFF */
67 | char CHAR_XON = 17;
68 | /** XOFF character used with flow control XON/XOFF */
69 | char CHAR_XOFF = 19;
70 |
71 |
72 | /**
73 | * Returns the driver used by this port.
74 | */
75 | UsbSerialDriver getDriver();
76 |
77 | /**
78 | * Returns the currently-bound USB device.
79 | */
80 | UsbDevice getDevice();
81 |
82 | /**
83 | * Port number within driver.
84 | */
85 | int getPortNumber();
86 |
87 | /**
88 | * Returns the write endpoint.
89 | * @return write endpoint
90 | */
91 | UsbEndpoint getWriteEndpoint();
92 |
93 | /**
94 | * Returns the read endpoint.
95 | * @return read endpoint
96 | */
97 | UsbEndpoint getReadEndpoint();
98 |
99 | /**
100 | * The serial number of the underlying UsbDeviceConnection, or {@code null}.
101 | *
102 | * @return value from {@link UsbDeviceConnection#getSerial()}
103 | * @throws SecurityException starting with target SDK 29 (Android 10) if permission for USB device is not granted
104 | */
105 | String getSerial();
106 |
107 | /**
108 | * Opens and initializes the port. Upon success, caller must ensure that
109 | * {@link #close()} is eventually called.
110 | *
111 | * @param connection an open device connection, acquired with
112 | * {@link UsbManager#openDevice(android.hardware.usb.UsbDevice)}
113 | * @throws IOException on error opening or initializing the port.
114 | */
115 | void open(UsbDeviceConnection connection) throws IOException;
116 |
117 | /**
118 | * Closes the port and {@link UsbDeviceConnection}
119 | *
120 | * @throws IOException on error closing the port.
121 | */
122 | void close() throws IOException;
123 |
124 | /**
125 | * Reads as many bytes as possible into the destination buffer.
126 | *
127 | * @param dest the destination byte buffer
128 | * @param timeout the timeout for reading in milliseconds, 0 is infinite
129 | * @return the actual number of bytes read
130 | * @throws IOException if an error occurred during reading
131 | */
132 | int read(final byte[] dest, final int timeout) throws IOException;
133 |
134 | /**
135 | * Reads bytes with specified length into the destination buffer.
136 | *
137 | * @param dest the destination byte buffer
138 | * @param length the maximum length of the data to read
139 | * @param timeout the timeout for reading in milliseconds, 0 is infinite
140 | * @return the actual number of bytes read
141 | * @throws IOException if an error occurred during reading
142 | */
143 | int read(final byte[] dest, int length, final int timeout) throws IOException;
144 |
145 | /**
146 | * Writes as many bytes as possible from the source buffer.
147 | *
148 | * @param src the source byte buffer
149 | * @param timeout the timeout for writing in milliseconds, 0 is infinite
150 | * @throws SerialTimeoutException if timeout reached before sending all data.
151 | * ex.bytesTransferred may contain bytes transferred
152 | * @throws IOException if an error occurred during writing
153 | */
154 | void write(final byte[] src, final int timeout) throws IOException;
155 |
156 | /**
157 | * Writes bytes with specified length from the source buffer.
158 | *
159 | * @param src the source byte buffer
160 | * @param length the length of the data to write
161 | * @param timeout the timeout for writing in milliseconds, 0 is infinite
162 | * @throws SerialTimeoutException if timeout reached before sending all data.
163 | * ex.bytesTransferred may contain bytes transferred
164 | * @throws IOException if an error occurred during writing
165 | */
166 | void write(final byte[] src, int length, final int timeout) throws IOException;
167 |
168 | /**
169 | * Sets various serial port parameters.
170 | *
171 | * @param baudRate baud rate as an integer, for example {@code 115200}.
172 | * @param dataBits one of {@link #DATABITS_5}, {@link #DATABITS_6},
173 | * {@link #DATABITS_7}, or {@link #DATABITS_8}.
174 | * @param stopBits one of {@link #STOPBITS_1}, {@link #STOPBITS_1_5}, or {@link #STOPBITS_2}.
175 | * @param parity one of {@link #PARITY_NONE}, {@link #PARITY_ODD},
176 | * {@link #PARITY_EVEN}, {@link #PARITY_MARK}, or {@link #PARITY_SPACE}.
177 | * @throws IOException on error setting the port parameters
178 | * @throws UnsupportedOperationException if not supported or values are not supported by a specific device
179 | */
180 | void setParameters(int baudRate, int dataBits, int stopBits, @Parity int parity) throws IOException;
181 |
182 | /**
183 | * Gets the CD (Carrier Detect) bit from the underlying UART.
184 | *
185 | * @return the current state
186 | * @throws IOException if an error occurred during reading
187 | * @throws UnsupportedOperationException if not supported
188 | */
189 | boolean getCD() throws IOException;
190 |
191 | /**
192 | * Gets the CTS (Clear To Send) bit from the underlying UART.
193 | *
194 | * @return the current state
195 | * @throws IOException if an error occurred during reading
196 | * @throws UnsupportedOperationException if not supported
197 | */
198 | boolean getCTS() throws IOException;
199 |
200 | /**
201 | * Gets the DSR (Data Set Ready) bit from the underlying UART.
202 | *
203 | * @return the current state
204 | * @throws IOException if an error occurred during reading
205 | * @throws UnsupportedOperationException if not supported
206 | */
207 | boolean getDSR() throws IOException;
208 |
209 | /**
210 | * Gets the DTR (Data Terminal Ready) bit from the underlying UART.
211 | *
212 | * @return the current state
213 | * @throws IOException if an error occurred during reading
214 | * @throws UnsupportedOperationException if not supported
215 | */
216 | boolean getDTR() throws IOException;
217 |
218 | /**
219 | * Sets the DTR (Data Terminal Ready) bit on the underlying UART, if supported.
220 | *
221 | * @param value the value to set
222 | * @throws IOException if an error occurred during writing
223 | * @throws UnsupportedOperationException if not supported
224 | */
225 | void setDTR(boolean value) throws IOException;
226 |
227 | /**
228 | * Gets the RI (Ring Indicator) bit from the underlying UART.
229 | *
230 | * @return the current state
231 | * @throws IOException if an error occurred during reading
232 | * @throws UnsupportedOperationException if not supported
233 | */
234 | boolean getRI() throws IOException;
235 |
236 | /**
237 | * Gets the RTS (Request To Send) bit from the underlying UART.
238 | *
239 | * @return the current state
240 | * @throws IOException if an error occurred during reading
241 | * @throws UnsupportedOperationException if not supported
242 | */
243 | boolean getRTS() throws IOException;
244 |
245 | /**
246 | * Sets the RTS (Request To Send) bit on the underlying UART, if supported.
247 | *
248 | * @param value the value to set
249 | * @throws IOException if an error occurred during writing
250 | * @throws UnsupportedOperationException if not supported
251 | */
252 | void setRTS(boolean value) throws IOException;
253 |
254 | /**
255 | * Gets all control line values from the underlying UART, if supported.
256 | * Requires less USB calls than calling getRTS() + ... + getRI() individually.
257 | *
258 | * @return EnumSet.contains(...) is {@code true} if set, else {@code false}
259 | * @throws IOException if an error occurred during reading
260 | * @throws UnsupportedOperationException if not supported
261 | */
262 | EnumSet getControlLines() throws IOException;
263 |
264 | /**
265 | * Gets all control line supported flags.
266 | *
267 | * @return EnumSet.contains(...) is {@code true} if supported, else {@code false}
268 | * @throws IOException if an error occurred during reading
269 | */
270 | EnumSet getSupportedControlLines() throws IOException;
271 |
272 | /**
273 | * Set flow control mode, if supported
274 | * @param flowControl @FlowControl
275 | * @throws IOException if an error occurred during writing
276 | * @throws UnsupportedOperationException if not supported
277 | */
278 | void setFlowControl(FlowControl flowControl) throws IOException;
279 |
280 | /**
281 | * Get flow control mode.
282 | * @return FlowControl
283 | */
284 | FlowControl getFlowControl();
285 |
286 | /**
287 | * Get supported flow control modes
288 | * @return EnumSet.contains(...) is {@code true} if supported, else {@code false}
289 | */
290 | EnumSet getSupportedFlowControl();
291 |
292 | /**
293 | * If flow control = XON_XOFF, indicates that send is enabled by XON.
294 | * Devices supporting flow control = XON_XOFF_INLINE return CHAR_XON/CHAR_XOFF in read() data.
295 | *
296 | * @return the current state
297 | * @throws IOException if an error occurred during reading
298 | * @throws UnsupportedOperationException if not supported
299 | */
300 | boolean getXON() throws IOException;
301 |
302 | /**
303 | * Purge non-transmitted output data and / or non-read input data.
304 | *
305 | * @param purgeWriteBuffers {@code true} to discard non-transmitted output data
306 | * @param purgeReadBuffers {@code true} to discard non-read input data
307 | * @throws IOException if an error occurred during flush
308 | * @throws UnsupportedOperationException if not supported
309 | */
310 | void purgeHwBuffers(boolean purgeWriteBuffers, boolean purgeReadBuffers) throws IOException;
311 |
312 | /**
313 | * send BREAK condition.
314 | *
315 | * @param value set/reset
316 | */
317 | void setBreak(boolean value) throws IOException;
318 |
319 | /**
320 | * Returns the current state of the connection.
321 | */
322 | boolean isOpen();
323 |
324 | }
325 |
--------------------------------------------------------------------------------
/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/UsbSerialProber.java:
--------------------------------------------------------------------------------
1 | /* Copyright 2011-2013 Google Inc.
2 | * Copyright 2013 mike wakerly
3 | *
4 | * Project home page: https://github.com/mik3y/usb-serial-for-android
5 | */
6 |
7 | package com.hoho.android.usbserial.driver;
8 |
9 | import android.hardware.usb.UsbDevice;
10 | import android.hardware.usb.UsbManager;
11 |
12 | import java.lang.reflect.Constructor;
13 | import java.lang.reflect.InvocationTargetException;
14 | import java.util.ArrayList;
15 | import java.util.List;
16 |
17 | /**
18 | *
19 | * @author mike wakerly (opensource@hoho.com)
20 | */
21 | public class UsbSerialProber {
22 |
23 | private final ProbeTable mProbeTable;
24 |
25 | public UsbSerialProber(ProbeTable probeTable) {
26 | mProbeTable = probeTable;
27 | }
28 |
29 | public static UsbSerialProber getDefaultProber() {
30 | return new UsbSerialProber(getDefaultProbeTable());
31 | }
32 |
33 | public static ProbeTable getDefaultProbeTable() {
34 | final ProbeTable probeTable = new ProbeTable();
35 | probeTable.addDriver(CdcAcmSerialDriver.class);
36 | probeTable.addDriver(Cp21xxSerialDriver.class);
37 | probeTable.addDriver(FtdiSerialDriver.class);
38 | probeTable.addDriver(ProlificSerialDriver.class);
39 | probeTable.addDriver(Ch34xSerialDriver.class);
40 | probeTable.addDriver(GsmModemSerialDriver.class);
41 | probeTable.addDriver(ChromeCcdSerialDriver.class);
42 | return probeTable;
43 | }
44 |
45 | /**
46 | * Finds and builds all possible {@link UsbSerialDriver UsbSerialDrivers}
47 | * from the currently-attached {@link UsbDevice} hierarchy. This method does
48 | * not require permission from the Android USB system, since it does not
49 | * open any of the devices.
50 | *
51 | * @param usbManager usb manager
52 | * @return a list, possibly empty, of all compatible drivers
53 | */
54 | public List findAllDrivers(final UsbManager usbManager) {
55 | final List result = new ArrayList<>();
56 |
57 | for (final UsbDevice usbDevice : usbManager.getDeviceList().values()) {
58 | final UsbSerialDriver driver = probeDevice(usbDevice);
59 | if (driver != null) {
60 | result.add(driver);
61 | }
62 | }
63 | return result;
64 | }
65 |
66 | /**
67 | * Probes a single device for a compatible driver.
68 | *
69 | * @param usbDevice the usb device to probe
70 | * @return a new {@link UsbSerialDriver} compatible with this device, or
71 | * {@code null} if none available.
72 | */
73 | public UsbSerialDriver probeDevice(final UsbDevice usbDevice) {
74 | final Class extends UsbSerialDriver> driverClass = mProbeTable.findDriver(usbDevice);
75 | if (driverClass != null) {
76 | final UsbSerialDriver driver;
77 | try {
78 | final Constructor extends UsbSerialDriver> ctor =
79 | driverClass.getConstructor(UsbDevice.class);
80 | driver = ctor.newInstance(usbDevice);
81 | } catch (NoSuchMethodException | IllegalArgumentException | InstantiationException |
82 | IllegalAccessException | InvocationTargetException e) {
83 | throw new RuntimeException(e);
84 | }
85 | return driver;
86 | }
87 | return null;
88 | }
89 |
90 | }
91 |
--------------------------------------------------------------------------------
/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/util/HexDump.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2006 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.hoho.android.usbserial.util;
18 |
19 | import java.security.InvalidParameterException;
20 |
21 | /**
22 | * Clone of Android's /core/java/com/android/internal/util/HexDump class, for use in debugging.
23 | * Changes: space separated hex strings
24 | */
25 | public class HexDump {
26 | private final static char[] HEX_DIGITS = {
27 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
28 | };
29 |
30 | private HexDump() {
31 | }
32 |
33 | public static String dumpHexString(byte[] array) {
34 | return dumpHexString(array, 0, array.length);
35 | }
36 |
37 | public static String dumpHexString(byte[] array, int offset, int length) {
38 | StringBuilder result = new StringBuilder();
39 |
40 | byte[] line = new byte[8];
41 | int lineIndex = 0;
42 |
43 | for (int i = offset; i < offset + length; i++) {
44 | if (lineIndex == line.length) {
45 | for (int j = 0; j < line.length; j++) {
46 | if (line[j] > ' ' && line[j] < '~') {
47 | result.append(new String(line, j, 1));
48 | } else {
49 | result.append(".");
50 | }
51 | }
52 |
53 | result.append("\n");
54 | lineIndex = 0;
55 | }
56 |
57 | byte b = array[i];
58 | result.append(HEX_DIGITS[(b >>> 4) & 0x0F]);
59 | result.append(HEX_DIGITS[b & 0x0F]);
60 | result.append(" ");
61 |
62 | line[lineIndex++] = b;
63 | }
64 |
65 | for (int i = 0; i < (line.length - lineIndex); i++) {
66 | result.append(" ");
67 | }
68 | for (int i = 0; i < lineIndex; i++) {
69 | if (line[i] > ' ' && line[i] < '~') {
70 | result.append(new String(line, i, 1));
71 | } else {
72 | result.append(".");
73 | }
74 | }
75 |
76 | return result.toString();
77 | }
78 |
79 | public static String toHexString(byte b) {
80 | return toHexString(toByteArray(b));
81 | }
82 |
83 | public static String toHexString(byte[] array) {
84 | return toHexString(array, 0, array.length);
85 | }
86 |
87 | public static String toHexString(byte[] array, int offset, int length) {
88 | char[] buf = new char[length > 0 ? length * 3 - 1 : 0];
89 |
90 | int bufIndex = 0;
91 | for (int i = offset; i < offset + length; i++) {
92 | if (i > offset)
93 | buf[bufIndex++] = ' ';
94 | byte b = array[i];
95 | buf[bufIndex++] = HEX_DIGITS[(b >>> 4) & 0x0F];
96 | buf[bufIndex++] = HEX_DIGITS[b & 0x0F];
97 | }
98 |
99 | return new String(buf);
100 | }
101 |
102 | public static String toHexString(int i) {
103 | return toHexString(toByteArray(i));
104 | }
105 |
106 | public static String toHexString(short i) {
107 | return toHexString(toByteArray(i));
108 | }
109 |
110 | public static byte[] toByteArray(byte b) {
111 | byte[] array = new byte[1];
112 | array[0] = b;
113 | return array;
114 | }
115 |
116 | public static byte[] toByteArray(int i) {
117 | byte[] array = new byte[4];
118 |
119 | array[3] = (byte) (i & 0xFF);
120 | array[2] = (byte) ((i >> 8) & 0xFF);
121 | array[1] = (byte) ((i >> 16) & 0xFF);
122 | array[0] = (byte) ((i >> 24) & 0xFF);
123 |
124 | return array;
125 | }
126 |
127 | public static byte[] toByteArray(short i) {
128 | byte[] array = new byte[2];
129 |
130 | array[1] = (byte) (i & 0xFF);
131 | array[0] = (byte) ((i >> 8) & 0xFF);
132 |
133 | return array;
134 | }
135 |
136 | private static int toByte(char c) {
137 | if (c >= '0' && c <= '9')
138 | return (c - '0');
139 | if (c >= 'A' && c <= 'F')
140 | return (c - 'A' + 10);
141 | if (c >= 'a' && c <= 'f')
142 | return (c - 'a' + 10);
143 |
144 | throw new InvalidParameterException("Invalid hex char '" + c + "'");
145 | }
146 |
147 | /** accepts any separator, e.g. space or newline */
148 | public static byte[] hexStringToByteArray(String hexString) {
149 | int length = hexString.length();
150 | byte[] buffer = new byte[(length + 1) / 3];
151 |
152 | for (int i = 0; i < length; i += 3) {
153 | buffer[i / 3] = (byte) ((toByte(hexString.charAt(i)) << 4) | toByte(hexString.charAt(i + 1)));
154 | }
155 |
156 | return buffer;
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/util/MonotonicClock.java:
--------------------------------------------------------------------------------
1 | package com.hoho.android.usbserial.util;
2 |
3 | public final class MonotonicClock {
4 |
5 | private static final long NS_PER_MS = 1_000_000;
6 |
7 | private MonotonicClock() {
8 | }
9 |
10 | public static long millis() {
11 | return System.nanoTime() / NS_PER_MS;
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/util/SerialInputOutputManager.java:
--------------------------------------------------------------------------------
1 | /* Copyright 2011-2013 Google Inc.
2 | * Copyright 2013 mike wakerly
3 | *
4 | * Project home page: https://github.com/mik3y/usb-serial-for-android
5 | */
6 |
7 | package com.hoho.android.usbserial.util;
8 |
9 | import android.os.Process;
10 | import android.util.Log;
11 |
12 | import com.hoho.android.usbserial.driver.UsbSerialPort;
13 |
14 | import java.io.IOException;
15 | import java.nio.ByteBuffer;
16 | import java.util.concurrent.CountDownLatch;
17 | import java.util.concurrent.atomic.AtomicReference;
18 |
19 | /**
20 | * Utility class which services a {@link UsbSerialPort} in its {@link #runWrite()} ()} and {@link #runRead()} ()} ()} methods.
21 | *
22 | * @author mike wakerly (opensource@hoho.com)
23 | */
24 | public class SerialInputOutputManager {
25 |
26 | public enum State {
27 | STOPPED,
28 | STARTING,
29 | RUNNING,
30 | STOPPING
31 | }
32 |
33 | public static boolean DEBUG = false;
34 |
35 | private static final String TAG = SerialInputOutputManager.class.getSimpleName();
36 | private static final int BUFSIZ = 4096;
37 |
38 | private int mReadTimeout = 0;
39 | private int mWriteTimeout = 0;
40 |
41 | private final Object mReadBufferLock = new Object();
42 | private final Object mWriteBufferLock = new Object();
43 |
44 | private ByteBuffer mReadBuffer; // default size = getReadEndpoint().getMaxPacketSize()
45 | private ByteBuffer mWriteBuffer = ByteBuffer.allocate(BUFSIZ);
46 |
47 | private int mThreadPriority = Process.THREAD_PRIORITY_URGENT_AUDIO;
48 | private final AtomicReference mState = new AtomicReference<>(State.STOPPED);
49 | private CountDownLatch mStartuplatch = new CountDownLatch(2);
50 | private Listener mListener; // Synchronized by 'this'
51 | private final UsbSerialPort mSerialPort;
52 |
53 | public interface Listener {
54 | /**
55 | * Called when new incoming data is available.
56 | */
57 | void onNewData(byte[] data);
58 |
59 | /**
60 | * Called when {@link SerialInputOutputManager#runRead()} ()} or {@link SerialInputOutputManager#runWrite()} ()} ()} aborts due to an error.
61 | */
62 | void onRunError(Exception e);
63 | }
64 |
65 | public SerialInputOutputManager(UsbSerialPort serialPort) {
66 | mSerialPort = serialPort;
67 | mReadBuffer = ByteBuffer.allocate(serialPort.getReadEndpoint().getMaxPacketSize());
68 | }
69 |
70 | public SerialInputOutputManager(UsbSerialPort serialPort, Listener listener) {
71 | mSerialPort = serialPort;
72 | mListener = listener;
73 | mReadBuffer = ByteBuffer.allocate(serialPort.getReadEndpoint().getMaxPacketSize());
74 | }
75 |
76 | public synchronized void setListener(Listener listener) {
77 | mListener = listener;
78 | }
79 |
80 | public synchronized Listener getListener() {
81 | return mListener;
82 | }
83 |
84 | /**
85 | * setThreadPriority. By default a higher priority than UI thread is used to prevent data loss
86 | *
87 | * @param threadPriority see {@link Process#setThreadPriority(int)}
88 | * */
89 | public void setThreadPriority(int threadPriority) {
90 | if (!mState.compareAndSet(State.STOPPED, State.STOPPED)) {
91 | throw new IllegalStateException("threadPriority only configurable before SerialInputOutputManager is started");
92 | }
93 | mThreadPriority = threadPriority;
94 | }
95 |
96 | /**
97 | * read/write timeout
98 | */
99 | public void setReadTimeout(int timeout) {
100 | // when set if already running, read already blocks and the new value will not become effective now
101 | if(mReadTimeout == 0 && timeout != 0 && mState.get() != State.STOPPED)
102 | throw new IllegalStateException("readTimeout only configurable before SerialInputOutputManager is started");
103 | mReadTimeout = timeout;
104 | }
105 |
106 | public int getReadTimeout() {
107 | return mReadTimeout;
108 | }
109 |
110 | public void setWriteTimeout(int timeout) {
111 | mWriteTimeout = timeout;
112 | }
113 |
114 | public int getWriteTimeout() {
115 | return mWriteTimeout;
116 | }
117 |
118 | /**
119 | * read/write buffer size
120 | */
121 | public void setReadBufferSize(int bufferSize) {
122 | if (getReadBufferSize() == bufferSize)
123 | return;
124 | synchronized (mReadBufferLock) {
125 | mReadBuffer = ByteBuffer.allocate(bufferSize);
126 | }
127 | }
128 |
129 | public int getReadBufferSize() {
130 | return mReadBuffer.capacity();
131 | }
132 |
133 | public void setWriteBufferSize(int bufferSize) {
134 | if(getWriteBufferSize() == bufferSize)
135 | return;
136 | synchronized (mWriteBufferLock) {
137 | ByteBuffer newWriteBuffer = ByteBuffer.allocate(bufferSize);
138 | if(mWriteBuffer.position() > 0)
139 | newWriteBuffer.put(mWriteBuffer.array(), 0, mWriteBuffer.position());
140 | mWriteBuffer = newWriteBuffer;
141 | }
142 | }
143 |
144 | public int getWriteBufferSize() {
145 | return mWriteBuffer.capacity();
146 | }
147 |
148 | /**
149 | * write data asynchronously
150 | */
151 | public void writeAsync(byte[] data) {
152 | synchronized (mWriteBufferLock) {
153 | mWriteBuffer.put(data);
154 | mWriteBufferLock.notifyAll(); // Notify waiting threads
155 | }
156 | }
157 |
158 | /**
159 | * start SerialInputOutputManager in separate threads
160 | */
161 | public void start() {
162 | if(mState.compareAndSet(State.STOPPED, State.STARTING)) {
163 | mStartuplatch = new CountDownLatch(2);
164 | new Thread(this::runRead, this.getClass().getSimpleName() + "_read").start();
165 | new Thread(this::runWrite, this.getClass().getSimpleName() + "_write").start();
166 | try {
167 | mStartuplatch.await();
168 | mState.set(State.RUNNING);
169 | } catch (InterruptedException e) {
170 | Thread.currentThread().interrupt();
171 | }
172 | } else {
173 | throw new IllegalStateException("already started");
174 | }
175 | }
176 |
177 | /**
178 | * stop SerialInputOutputManager threads
179 | *
180 | * when using readTimeout == 0 (default), additionally use usbSerialPort.close() to
181 | * interrupt blocking read
182 | */
183 | public void stop() {
184 | if(mState.compareAndSet(State.RUNNING, State.STOPPING)) {
185 | synchronized (mWriteBufferLock) {
186 | mWriteBufferLock.notifyAll(); // wake up write thread to check the stop condition
187 | }
188 | Log.i(TAG, "Stop requested");
189 | }
190 | }
191 |
192 | public State getState() {
193 | return mState.get();
194 | }
195 |
196 | /**
197 | * @return true if the thread is still running
198 | */
199 | private boolean isStillRunning() {
200 | State state = mState.get();
201 | return ((state == State.RUNNING) || (state == State.STARTING))
202 | && !Thread.currentThread().isInterrupted();
203 | }
204 |
205 | /**
206 | * Notify listener of an error
207 | *
208 | * @param e the exception
209 | */
210 | private void notifyErrorListener(Throwable e) {
211 | Listener listener = getListener();
212 | if (listener != null) {
213 | try {
214 | listener.onRunError(e instanceof Exception ? (Exception) e : new Exception(e));
215 | } catch (Throwable t) {
216 | Log.w(TAG, "Exception in onRunError: " + t.getMessage(), t);
217 | }
218 | }
219 | }
220 |
221 | /**
222 | * Set the thread priority
223 | */
224 | private void setThreadPriority() {
225 | if (mThreadPriority != Process.THREAD_PRIORITY_DEFAULT) {
226 | Process.setThreadPriority(mThreadPriority);
227 | }
228 | }
229 |
230 | /**
231 | * Continuously services the read buffers until {@link #stop()} is called, or until a driver exception is
232 | * raised.
233 | */
234 | void runRead() {
235 | Log.i(TAG, "runRead running ...");
236 | try {
237 | setThreadPriority();
238 | mStartuplatch.countDown();
239 | do {
240 | stepRead();
241 | } while (isStillRunning());
242 | Log.i(TAG, "runRead: Stopping mState=" + getState());
243 | } catch (Throwable e) {
244 | if (Thread.currentThread().isInterrupted()) {
245 | Log.w(TAG, "runRead: interrupted");
246 | } else if(mSerialPort.isOpen()) {
247 | Log.w(TAG, "runRead ending due to exception: " + e.getMessage(), e);
248 | } else {
249 | Log.i(TAG, "runRead: Socket closed");
250 | }
251 | notifyErrorListener(e);
252 | } finally {
253 | if (mState.compareAndSet(State.RUNNING, State.STOPPING)) {
254 | synchronized (mWriteBufferLock) {
255 | mWriteBufferLock.notifyAll(); // wake up write thread to check the stop condition
256 | }
257 | } else if (mState.compareAndSet(State.STOPPING, State.STOPPED)) {
258 | Log.i(TAG, "runRead: Stopped mState=" + getState());
259 | }
260 | }
261 | }
262 |
263 | /**
264 | * Continuously services the write buffers until {@link #stop()} is called, or until a driver exception is
265 | * raised.
266 | */
267 | void runWrite() {
268 | Log.i(TAG, "runWrite running ...");
269 | try {
270 | setThreadPriority();
271 | mStartuplatch.countDown();
272 | do {
273 | stepWrite();
274 | } while (isStillRunning());
275 | Log.i(TAG, "runWrite: Stopping mState=" + getState());
276 | } catch (Throwable e) {
277 | if (Thread.currentThread().isInterrupted()) {
278 | Log.w(TAG, "runWrite: interrupted");
279 | } else if(mSerialPort.isOpen()) {
280 | Log.w(TAG, "runWrite ending due to exception: " + e.getMessage(), e);
281 | } else {
282 | Log.i(TAG, "runWrite: Socket closed");
283 | }
284 | notifyErrorListener(e);
285 | } finally {
286 | if (!mState.compareAndSet(State.RUNNING, State.STOPPING)) {
287 | if (mState.compareAndSet(State.STOPPING, State.STOPPED)) {
288 | Log.i(TAG, "runWrite: Stopped mState=" + getState());
289 | }
290 | }
291 | }
292 | }
293 |
294 | private void stepRead() throws IOException {
295 | // Handle incoming data.
296 | byte[] buffer;
297 | synchronized (mReadBufferLock) {
298 | buffer = mReadBuffer.array();
299 | }
300 | int len = mSerialPort.read(buffer, mReadTimeout);
301 | if (len > 0) {
302 | if (DEBUG) {
303 | Log.d(TAG, "Read data len=" + len);
304 | }
305 | final Listener listener = getListener();
306 | if (listener != null) {
307 | final byte[] data = new byte[len];
308 | System.arraycopy(buffer, 0, data, 0, len);
309 | listener.onNewData(data);
310 | }
311 | }
312 | }
313 |
314 | private void stepWrite() throws IOException, InterruptedException {
315 | // Handle outgoing data.
316 | byte[] buffer = null;
317 | synchronized (mWriteBufferLock) {
318 | int len = mWriteBuffer.position();
319 | if (len > 0) {
320 | buffer = new byte[len];
321 | mWriteBuffer.rewind();
322 | mWriteBuffer.get(buffer, 0, len);
323 | mWriteBuffer.clear();
324 | mWriteBufferLock.notifyAll(); // Notify writeAsync that there is space in the buffer
325 | } else {
326 | mWriteBufferLock.wait();
327 | }
328 | }
329 | if (buffer != null) {
330 | if (DEBUG) {
331 | Log.d(TAG, "Writing data len=" + buffer.length);
332 | }
333 | mSerialPort.write(buffer, mWriteTimeout);
334 | }
335 | }
336 |
337 | }
338 |
--------------------------------------------------------------------------------
/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/util/UsbUtils.java:
--------------------------------------------------------------------------------
1 | package com.hoho.android.usbserial.util;
2 |
3 | import android.hardware.usb.UsbDeviceConnection;
4 |
5 | import java.util.ArrayList;
6 |
7 | public class UsbUtils {
8 |
9 | private UsbUtils() {
10 | }
11 |
12 | public static ArrayList getDescriptors(UsbDeviceConnection connection) {
13 | ArrayList descriptors = new ArrayList<>();
14 | byte[] rawDescriptors = connection.getRawDescriptors();
15 | if (rawDescriptors != null) {
16 | int pos = 0;
17 | while (pos < rawDescriptors.length) {
18 | int len = rawDescriptors[pos] & 0xFF;
19 | if (len == 0)
20 | break;
21 | if (pos + len > rawDescriptors.length)
22 | len = rawDescriptors.length - pos;
23 | byte[] descriptor = new byte[len];
24 | System.arraycopy(rawDescriptors, pos, descriptor, 0, len);
25 | descriptors.add(descriptor);
26 | pos += len;
27 | }
28 | }
29 | return descriptors;
30 | }
31 |
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/util/XonXoffFilter.java:
--------------------------------------------------------------------------------
1 | package com.hoho.android.usbserial.util;
2 |
3 | import com.hoho.android.usbserial.driver.UsbSerialPort;
4 |
5 | import java.io.IOException;
6 |
7 | /**
8 | * Some devices return XON and XOFF characters inline in read() data.
9 | * Other devices return XON / XOFF condition thru getXOFF() method.
10 | */
11 |
12 | public class XonXoffFilter {
13 | private boolean xon = true;
14 |
15 | public XonXoffFilter() {
16 | }
17 |
18 | public boolean getXON() {
19 | return xon;
20 | }
21 |
22 | /**
23 | * Filter XON/XOFF from read() data and remember
24 | *
25 | * @param data unfiltered data
26 | * @return filtered data
27 | */
28 | public byte[] filter(byte[] data) {
29 | int found = 0;
30 | for (int i=0; i {
30 | public final F first;
31 | public final S second;
32 |
33 | /**
34 | * Constructor for a Pair.
35 | *
36 | * @param first the first object in the Pair
37 | * @param second the second object in the pair
38 | */
39 | public Pair(F first, S second) {
40 | this.first = first;
41 | this.second = second;
42 | }
43 |
44 | /**
45 | * Checks the two objects for equality by delegating to their respective
46 | * {@link Object#equals(Object)} methods.
47 | *
48 | * @param o the {@link Pair} to which this one is to be checked for equality
49 | * @return true if the underlying objects of the Pair are both considered
50 | * equal
51 | */
52 | @Override
53 | public boolean equals(@Nullable Object o) {
54 | if (!(o instanceof Pair)) {
55 | return false;
56 | }
57 | Pair, ?> p = (Pair, ?>) o;
58 | return Objects.equals(p.first, first) && Objects.equals(p.second, second);
59 | }
60 |
61 | /**
62 | * Compute a hash code using the hash codes of the underlying objects
63 | *
64 | * @return a hashcode of the Pair
65 | */
66 | @Override
67 | public int hashCode() {
68 | return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode());
69 | }
70 |
71 | @Override
72 | public String toString() {
73 | return "Pair{" + String.valueOf(first) + " " + String.valueOf(second) + "}";
74 | }
75 |
76 | /**
77 | * Convenience method for creating an appropriately typed pair.
78 | * @param a the first object in the Pair
79 | * @param b the second object in the pair
80 | * @return a Pair that is templatized with the types of a and b
81 | */
82 | public static Pair create(A a, B b) {
83 | return new Pair(a, b);
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/usbSerialForAndroid/src/test/java/com/hoho/android/usbserial/driver/FtdiSerialDriverTest.java:
--------------------------------------------------------------------------------
1 | package com.hoho.android.usbserial.driver;
2 |
3 | import android.hardware.usb.UsbDevice;
4 | import android.hardware.usb.UsbEndpoint;
5 |
6 | import org.junit.Test;
7 |
8 | import java.io.IOException;
9 |
10 | import static org.junit.Assert.assertEquals;
11 | import static org.junit.Assert.assertThrows;
12 | import static org.junit.Assert.assertTrue;
13 | import static org.mockito.Mockito.mock;
14 | import static org.mockito.Mockito.when;
15 |
16 | public class FtdiSerialDriverTest {
17 |
18 | private final UsbDevice usbDevice = mock(UsbDevice.class);
19 | private final UsbEndpoint readEndpoint = mock(UsbEndpoint.class);
20 |
21 | private void initBuf(byte[] buf) {
22 | for(int i=0; i port.readFilter(buf, 1));
52 |
53 | initBuf(buf);
54 | len = port.readFilter(buf, 2);
55 | assertEquals(len, 0);
56 |
57 | initBuf(buf);
58 | len = port.readFilter(buf, 3);
59 | assertEquals(len, 1);
60 | assertTrue(testBuf(buf, len));
61 |
62 | initBuf(buf);
63 | len = port.readFilter(buf, 4);
64 | assertEquals(len, 2);
65 | assertTrue(testBuf(buf, len));
66 |
67 | initBuf(buf);
68 | len = port.readFilter(buf, 64);
69 | assertEquals(len, 62);
70 | assertTrue(testBuf(buf, len));
71 |
72 | assertThrows(IOException.class, () -> port.readFilter(buf, 65));
73 |
74 | initBuf(buf);
75 | len = port.readFilter(buf, 66);
76 | assertEquals(len, 62);
77 | assertTrue(testBuf(buf, len));
78 |
79 | initBuf(buf);
80 | len = port.readFilter(buf, 68);
81 | assertEquals(len, 64);
82 | assertTrue(testBuf(buf, len));
83 |
84 | initBuf(buf);
85 | len = port.readFilter(buf, 16*64+11);
86 | assertEquals(len, 16*62+9);
87 | assertTrue(testBuf(buf, len));
88 | }
89 | }
--------------------------------------------------------------------------------
/usbSerialForAndroid/src/test/java/com/hoho/android/usbserial/util/HexDumpText.java:
--------------------------------------------------------------------------------
1 | package com.hoho.android.usbserial.util;
2 |
3 | import static org.hamcrest.CoreMatchers.equalTo;
4 | import static org.hamcrest.MatcherAssert.assertThat;
5 | import static org.junit.Assert.assertEquals;
6 | import static org.junit.Assert.assertThrows;
7 |
8 | import org.junit.Test;
9 |
10 | import java.security.InvalidParameterException;
11 |
12 | public class HexDumpText {
13 |
14 | @Test
15 | public void toByteArray() throws Exception {
16 | assertThat(HexDump.toByteArray((byte)0x4a), equalTo(new byte[]{ 0x4A}));
17 | assertThat(HexDump.toByteArray((short)0x4a5b), equalTo(new byte[]{ 0x4A, 0x5B}));
18 | assertThat(HexDump.toByteArray((int)0x4a5b6c7d), equalTo(new byte[]{ 0x4A, 0x5B, 0x6C, 0x7D}));
19 | }
20 |
21 | @Test
22 | public void toHexString() throws Exception {
23 | assertEquals("4A", HexDump.toHexString((byte)0x4a));
24 | assertEquals("4A 5B", HexDump.toHexString((short)0x4a5b));
25 | assertEquals("4A 5B 6C 7D", HexDump.toHexString((int)0x4a5b6c7d));
26 | assertEquals("4A 5B 6C 7D", HexDump.toHexString(new byte[]{ 0x4A, 0x5B, 0x6C, 0x7D}));
27 | assertEquals("5B 6C", HexDump.toHexString(new byte[]{ 0x4A, 0x5B, 0x6C, 0x7D}, 1, 2));
28 | }
29 |
30 | @Test
31 | public void dumpHexString() throws Exception {
32 | assertEquals("10 31 32 33 34 35 36 37 .1234567\n18 39 .9", HexDump.dumpHexString(new byte[]{ 0x10, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x18, 0x39}));
33 | assertEquals("31 32 12", HexDump.dumpHexString(new byte[]{ 0x30, 0x31, 0x32, 0x33}, 1, 2));
34 | }
35 |
36 | @Test
37 | public void toByte() throws Exception {
38 | assertThat(HexDump.hexStringToByteArray("4a 5B-6c\n7d"), equalTo(new byte[]{ 0x4A, 0x5B, 0x6C, 0x7D}));
39 | assertThrows(InvalidParameterException.class, () -> HexDump.hexStringToByteArray("3 "));
40 | assertThrows(InvalidParameterException.class, () -> HexDump.hexStringToByteArray("3z"));
41 | assertThrows(InvalidParameterException.class, () -> HexDump.hexStringToByteArray("3Z"));
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/usbSerialForAndroid/src/test/java/com/hoho/android/usbserial/util/SerialInputOutputManagerTest.java:
--------------------------------------------------------------------------------
1 | package com.hoho.android.usbserial.util;
2 |
3 | import static org.junit.Assert.assertEquals;
4 | import static org.mockito.Mockito.mock;
5 | import static org.mockito.Mockito.when;
6 |
7 | import android.hardware.usb.UsbEndpoint;
8 | import android.os.Process;
9 |
10 | import com.hoho.android.usbserial.driver.CommonUsbSerialPort;
11 |
12 | import org.junit.Test;
13 |
14 | public class SerialInputOutputManagerTest {
15 |
16 |
17 | // catch all Throwables in onNewData() and onRunError()
18 | @Test
19 | public void throwable() throws Exception {
20 |
21 | class ExceptionListener implements SerialInputOutputManager.Listener {
22 | public Exception e;
23 | @Override public void onNewData(byte[] data) { throw new RuntimeException("exception1"); }
24 | @Override public void onRunError(Exception e) { this.e = e; throw new RuntimeException("exception2"); }
25 | }
26 | class ErrorListener implements SerialInputOutputManager.Listener {
27 | public Exception e;
28 | @Override public void onNewData(byte[] data) { throw new UnknownError("error1"); }
29 | @Override public void onRunError(Exception e) { this.e = e; throw new UnknownError("error2");}
30 | }
31 |
32 | UsbEndpoint readEndpoint = mock(UsbEndpoint.class);
33 | when(readEndpoint.getMaxPacketSize()).thenReturn(16);
34 | CommonUsbSerialPort port = mock(CommonUsbSerialPort.class);
35 | when(port.getReadEndpoint()).thenReturn(readEndpoint);
36 | when(port.read(new byte[16], 0)).thenReturn(1);
37 | when(port.isOpen()).thenReturn(true);
38 | SerialInputOutputManager manager = new SerialInputOutputManager(port);
39 | manager.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
40 |
41 | ExceptionListener exceptionListener = new ExceptionListener();
42 | manager.setListener(exceptionListener);
43 | manager.runRead();
44 | assertEquals(RuntimeException.class, exceptionListener.e.getClass());
45 | assertEquals("exception1", exceptionListener.e.getMessage());
46 |
47 | ErrorListener errorListener = new ErrorListener();
48 | manager.setListener(errorListener);
49 | manager.runRead();
50 | assertEquals(Exception.class, errorListener.e.getClass());
51 | assertEquals("java.lang.UnknownError: error1", errorListener.e.getMessage());
52 | assertEquals(UnknownError.class, errorListener.e.getCause().getClass());
53 | assertEquals("error1", errorListener.e.getCause().getMessage());
54 | }
55 | }
56 |
--------------------------------------------------------------------------------