From 8025fe107d51d84e1779d84124ad1f4abadc3ad0 Mon Sep 17 00:00:00 2001 From: dgis Date: Sun, 16 Jun 2024 19:57:05 +0200 Subject: [PATCH] - Update the usb serial drivers usb-serial-for-android to version 3.7.3 --- ReadMe.txt | 5 + app/build.gradle | 4 +- app/src/main/assets/ReadMe.txt | 5 + .../usbserial/driver/CdcAcmSerialDriver.java | 166 +++-- .../usbserial/driver/Ch34xSerialDriver.java | 595 +++++++++--------- .../driver/ChromeCcdSerialDriver.java | 96 +++ .../usbserial/driver/CommonUsbSerialPort.java | 127 ++-- .../usbserial/driver/Cp21xxSerialDriver.java | 6 +- .../usbserial/driver/FtdiSerialDriver.java | 40 +- .../driver/GsmModemSerialDriver.java | 106 ++++ .../usbserial/driver/ProbeTable.java | 49 +- .../driver/ProlificSerialDriver.java | 48 +- .../driver/SerialTimeoutException.java | 3 +- .../calculator/usbserial/driver/UsbId.java | 37 +- .../usbserial/driver/UsbSerialDriver.java | 13 +- .../usbserial/driver/UsbSerialPort.java | 25 +- .../usbserial/driver/UsbSerialProber.java | 8 +- .../calculator/usbserial/util/HexDump.java | 158 +++++ .../util/SerialInputOutputManager.java | 44 +- .../calculator/usbserial/util/UsbUtils.java | 33 + gradle.properties | 2 +- 21 files changed, 1059 insertions(+), 511 deletions(-) create mode 100644 app/src/main/java/org/emulator/calculator/usbserial/driver/ChromeCcdSerialDriver.java create mode 100644 app/src/main/java/org/emulator/calculator/usbserial/driver/GsmModemSerialDriver.java create mode 100644 app/src/main/java/org/emulator/calculator/usbserial/util/HexDump.java create mode 100644 app/src/main/java/org/emulator/calculator/usbserial/util/UsbUtils.java diff --git a/ReadMe.txt b/ReadMe.txt index 3c41d2e..0bb0f1a 100644 --- a/ReadMe.txt +++ b/ReadMe.txt @@ -58,6 +58,11 @@ LINKS CHANGES +Version 2.8 (2024-xx-xx) + +- Update the usb serial drivers usb-serial-for-android to version 3.7.3 + + Version 2.7 (2024-06-14) - Updated source code with Emu48 version 1.65+. This new version improve the serial communication. diff --git a/app/build.gradle b/app/build.gradle index a82288b..206140f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -34,8 +34,8 @@ android { applicationId "org.emulator.forty.eight" minSdk 21 targetSdk 34 - versionCode 26 - versionName "2.7" + versionCode 27 + versionName "2.8" setProperty("archivesBaseName", "Emu48-v$versionName") testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" externalNativeBuild { diff --git a/app/src/main/assets/ReadMe.txt b/app/src/main/assets/ReadMe.txt index 71490fd..5ae997e 100644 --- a/app/src/main/assets/ReadMe.txt +++ b/app/src/main/assets/ReadMe.txt @@ -58,6 +58,11 @@ LINKS CHANGES +Version 2.8 (2024-xx-xx) + +- Update the usb serial drivers usb-serial-for-android to version 3.7.3 + + Version 2.7 (2024-06-14) - Updated source code with Emu48 version 1.65+. This new version improve the serial communication. diff --git a/app/src/main/java/org/emulator/calculator/usbserial/driver/CdcAcmSerialDriver.java b/app/src/main/java/org/emulator/calculator/usbserial/driver/CdcAcmSerialDriver.java index df4d081..1dde5a0 100644 --- a/app/src/main/java/org/emulator/calculator/usbserial/driver/CdcAcmSerialDriver.java +++ b/app/src/main/java/org/emulator/calculator/usbserial/driver/CdcAcmSerialDriver.java @@ -8,11 +8,13 @@ package org.emulator.calculator.usbserial.driver; import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDevice; -import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbEndpoint; import android.hardware.usb.UsbInterface; import android.util.Log; +import org.emulator.calculator.usbserial.util.HexDump; +import org.emulator.calculator.usbserial.util.UsbUtils; + import java.io.IOException; import java.util.ArrayList; import java.util.EnumSet; @@ -30,6 +32,8 @@ import java.util.Map; */ public class CdcAcmSerialDriver implements UsbSerialDriver { + public static final int USB_SUBCLASS_ACM = 2; + private final String TAG = CdcAcmSerialDriver.class.getSimpleName(); private final UsbDevice mDevice; @@ -38,23 +42,33 @@ public class CdcAcmSerialDriver implements UsbSerialDriver { public CdcAcmSerialDriver(UsbDevice device) { mDevice = device; mPorts = new ArrayList<>(); - - int controlInterfaceCount = 0; - int dataInterfaceCount = 0; - for( int i = 0; i < device.getInterfaceCount(); i++) { - if(device.getInterface(i).getInterfaceClass() == UsbConstants.USB_CLASS_COMM) - controlInterfaceCount++; - if(device.getInterface(i).getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA) - dataInterfaceCount++; - } - for( int port = 0; port < Math.min(controlInterfaceCount, dataInterfaceCount); port++) { + int ports = countPorts(device); + for (int port = 0; port < ports; port++) { mPorts.add(new CdcAcmSerialPort(mDevice, port)); } - if(mPorts.size() == 0) { + if (mPorts.size() == 0) { mPorts.add(new CdcAcmSerialPort(mDevice, -1)); } } + @SuppressWarnings({"unused"}) + public static boolean probe(UsbDevice device) { + return countPorts(device) > 0; + } + + private static int countPorts(UsbDevice device) { + int controlInterfaceCount = 0; + int dataInterfaceCount = 0; + for (int i = 0; i < device.getInterfaceCount(); i++) { + if (device.getInterface(i).getInterfaceClass() == UsbConstants.USB_CLASS_COMM && + device.getInterface(i).getInterfaceSubclass() == USB_SUBCLASS_ACM) + controlInterfaceCount++; + if (device.getInterface(i).getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA) + dataInterfaceCount++; + } + return Math.min(controlInterfaceCount, dataInterfaceCount); + } + @Override public UsbDevice getDevice() { return mDevice; @@ -95,7 +109,11 @@ public class CdcAcmSerialDriver implements UsbSerialDriver { } @Override - protected void openInt(UsbDeviceConnection connection) throws IOException { + protected void openInt() throws IOException { + Log.d(TAG, "interfaces:"); + for (int i = 0; i < mDevice.getInterfaceCount(); i++) { + Log.d(TAG, mDevice.getInterface(i).toString()); + } if (mPortNumber == -1) { Log.d(TAG,"device might be castrated ACM device, trying single interface logic"); openSingleInterface(); @@ -131,38 +149,57 @@ public class CdcAcmSerialDriver implements UsbSerialDriver { } private void openInterface() throws IOException { - Log.d(TAG, "claiming interfaces, count=" + mDevice.getInterfaceCount()); - int controlInterfaceCount = 0; - int dataInterfaceCount = 0; mControlInterface = null; mDataInterface = null; - for (int i = 0; i < mDevice.getInterfaceCount(); i++) { - UsbInterface usbInterface = mDevice.getInterface(i); - if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_COMM) { - if(controlInterfaceCount == mPortNumber) { - mControlIndex = i; - mControlInterface = usbInterface; + int j = getInterfaceIdFromDescriptors(); + Log.d(TAG, "interface count=" + mDevice.getInterfaceCount() + ", IAD=" + j); + if (j >= 0) { + for (int i = 0; i < mDevice.getInterfaceCount(); i++) { + UsbInterface usbInterface = mDevice.getInterface(i); + if (usbInterface.getId() == j || usbInterface.getId() == j+1) { + if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_COMM && + usbInterface.getInterfaceSubclass() == USB_SUBCLASS_ACM) { + mControlIndex = usbInterface.getId(); + mControlInterface = usbInterface; + } + if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA) { + mDataInterface = usbInterface; + } } - controlInterfaceCount++; } - if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA) { - if(dataInterfaceCount == mPortNumber) { - mDataInterface = usbInterface; + } + if (mControlInterface == null || mDataInterface == null) { + Log.d(TAG, "no IAD fallback"); + int controlInterfaceCount = 0; + int dataInterfaceCount = 0; + for (int i = 0; i < mDevice.getInterfaceCount(); i++) { + UsbInterface usbInterface = mDevice.getInterface(i); + if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_COMM && + usbInterface.getInterfaceSubclass() == USB_SUBCLASS_ACM) { + if (controlInterfaceCount == mPortNumber) { + mControlIndex = usbInterface.getId(); + mControlInterface = usbInterface; + } + controlInterfaceCount++; + } + if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA) { + if (dataInterfaceCount == mPortNumber) { + mDataInterface = usbInterface; + } + dataInterfaceCount++; } - dataInterfaceCount++; } } if(mControlInterface == null) { throw new IOException("No control interface"); } - Log.d(TAG, "Control iface=" + mControlInterface); + Log.d(TAG, "Control interface id " + mControlInterface.getId()); if (!mConnection.claimInterface(mControlInterface, true)) { throw new IOException("Could not claim control interface"); } - mControlEndpoint = mControlInterface.getEndpoint(0); if (mControlEndpoint.getDirection() != UsbConstants.USB_DIR_IN || mControlEndpoint.getType() != UsbConstants.USB_ENDPOINT_XFER_INT) { throw new IOException("Invalid control endpoint"); @@ -171,12 +208,10 @@ public class CdcAcmSerialDriver implements UsbSerialDriver { if(mDataInterface == null) { throw new IOException("No data interface"); } - Log.d(TAG, "data iface=" + mDataInterface); - + Log.d(TAG, "data interface id " + mDataInterface.getId()); if (!mConnection.claimInterface(mDataInterface, true)) { throw new IOException("Could not claim data interface"); } - for (int i = 0; i < mDataInterface.getEndpointCount(); i++) { UsbEndpoint ep = mDataInterface.getEndpoint(i); if (ep.getDirection() == UsbConstants.USB_DIR_IN && ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) @@ -186,6 +221,36 @@ public class CdcAcmSerialDriver implements UsbSerialDriver { } } + private int getInterfaceIdFromDescriptors() { + ArrayList descriptors = UsbUtils.getDescriptors(mConnection); + Log.d(TAG, "USB descriptor:"); + for(byte[] descriptor : descriptors) + Log.d(TAG, HexDump.toHexString(descriptor)); + + if (descriptors.size() > 0 && + descriptors.get(0).length == 18 && + descriptors.get(0)[1] == 1 && // bDescriptorType + descriptors.get(0)[4] == (byte)(UsbConstants.USB_CLASS_MISC) && //bDeviceClass + descriptors.get(0)[5] == 2 && // bDeviceSubClass + descriptors.get(0)[6] == 1) { // bDeviceProtocol + // is IAD device, see https://www.usb.org/sites/default/files/iadclasscode_r10.pdf + int port = -1; + for (int d = 1; d < descriptors.size(); d++) { + if (descriptors.get(d).length == 8 && + descriptors.get(d)[1] == 0x0b && // bDescriptorType == IAD + descriptors.get(d)[4] == UsbConstants.USB_CLASS_COMM && // bFunctionClass == CDC + descriptors.get(d)[5] == USB_SUBCLASS_ACM) { // bFunctionSubClass == ACM + port++; + if (port == mPortNumber && + descriptors.get(d)[3] == 2) { // bInterfaceCount + return descriptors.get(d)[2]; // bFirstInterface + } + } + } + } + return -1; + } + private int sendAcmControlMessage(int request, int value, byte[] buf) throws IOException { int len = mConnection.controlTransfer( USB_RT_ACM, request, value, mControlIndex, buf, buf != null ? buf.length : 0, 5000); @@ -286,42 +351,9 @@ public class CdcAcmSerialDriver implements UsbSerialDriver { } + @SuppressWarnings({"unused"}) public static Map getSupportedDevices() { - final Map supportedDevices = new LinkedHashMap<>(); - supportedDevices.put(UsbId.VENDOR_ARDUINO, - new int[] { - UsbId.ARDUINO_UNO, - UsbId.ARDUINO_UNO_R3, - UsbId.ARDUINO_MEGA_2560, - UsbId.ARDUINO_MEGA_2560_R3, - UsbId.ARDUINO_SERIAL_ADAPTER, - UsbId.ARDUINO_SERIAL_ADAPTER_R3, - UsbId.ARDUINO_MEGA_ADK, - UsbId.ARDUINO_MEGA_ADK_R3, - UsbId.ARDUINO_LEONARDO, - UsbId.ARDUINO_MICRO, - }); - supportedDevices.put(UsbId.VENDOR_VAN_OOIJEN_TECH, - new int[] { - UsbId.VAN_OOIJEN_TECH_TEENSYDUINO_SERIAL, - }); - supportedDevices.put(UsbId.VENDOR_ATMEL, - new int[] { - UsbId.ATMEL_LUFA_CDC_DEMO_APP, - }); - supportedDevices.put(UsbId.VENDOR_LEAFLABS, - new int[] { - UsbId.LEAFLABS_MAPLE, - }); - supportedDevices.put(UsbId.VENDOR_ARM, - new int[] { - UsbId.ARM_MBED, - }); - supportedDevices.put(UsbId.VENDOR_ST, - new int[] { - UsbId.ST_CDC, - }); - return supportedDevices; + return new LinkedHashMap<>(); } } diff --git a/app/src/main/java/org/emulator/calculator/usbserial/driver/Ch34xSerialDriver.java b/app/src/main/java/org/emulator/calculator/usbserial/driver/Ch34xSerialDriver.java index dd09763..415acee 100644 --- a/app/src/main/java/org/emulator/calculator/usbserial/driver/Ch34xSerialDriver.java +++ b/app/src/main/java/org/emulator/calculator/usbserial/driver/Ch34xSerialDriver.java @@ -7,9 +7,11 @@ package org.emulator.calculator.usbserial.driver; import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDevice; -import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbEndpoint; import android.hardware.usb.UsbInterface; +import android.util.Log; + +//import org.emulator.calculator.usbserial.BuildConfig; import java.io.IOException; import java.util.Collections; @@ -20,355 +22,366 @@ import java.util.Map; public class Ch34xSerialDriver implements UsbSerialDriver { - private static final String TAG = Ch34xSerialDriver.class.getSimpleName(); + private static final String TAG = Ch34xSerialDriver.class.getSimpleName(); - private final UsbDevice mDevice; - private final UsbSerialPort mPort; + private final UsbDevice mDevice; + private final UsbSerialPort mPort; - private static final int LCR_ENABLE_RX = 0x80; - private static final int LCR_ENABLE_TX = 0x40; - private static final int LCR_MARK_SPACE = 0x20; - private static final int LCR_PAR_EVEN = 0x10; - private static final int LCR_ENABLE_PAR = 0x08; - private static final int LCR_STOP_BITS_2 = 0x04; - private static final int LCR_CS8 = 0x03; - private static final int LCR_CS7 = 0x02; - private static final int LCR_CS6 = 0x01; - private static final int LCR_CS5 = 0x00; + private static final int LCR_ENABLE_RX = 0x80; + private static final int LCR_ENABLE_TX = 0x40; + private static final int LCR_MARK_SPACE = 0x20; + private static final int LCR_PAR_EVEN = 0x10; + private static final int LCR_ENABLE_PAR = 0x08; + private static final int LCR_STOP_BITS_2 = 0x04; + private static final int LCR_CS8 = 0x03; + private static final int LCR_CS7 = 0x02; + private static final int LCR_CS6 = 0x01; + private static final int LCR_CS5 = 0x00; - private static final int GCL_CTS = 0x01; - private static final int GCL_DSR = 0x02; - private static final int GCL_RI = 0x04; - private static final int GCL_CD = 0x08; - private static final int SCL_DTR = 0x20; - private static final int SCL_RTS = 0x40; + private static final int GCL_CTS = 0x01; + private static final int GCL_DSR = 0x02; + private static final int GCL_RI = 0x04; + private static final int GCL_CD = 0x08; + private static final int SCL_DTR = 0x20; + private static final int SCL_RTS = 0x40; - public Ch34xSerialDriver(UsbDevice device) { - mDevice = device; - mPort = new Ch340SerialPort(mDevice, 0); - } + public Ch34xSerialDriver(UsbDevice device) { + mDevice = device; + mPort = new Ch340SerialPort(mDevice, 0); + } - @Override - public UsbDevice getDevice() { - return mDevice; - } + @Override + public UsbDevice getDevice() { + return mDevice; + } - @Override - public List getPorts() { - return Collections.singletonList(mPort); - } + @Override + public List getPorts() { + return Collections.singletonList(mPort); + } - public class Ch340SerialPort extends CommonUsbSerialPort { + public class Ch340SerialPort extends CommonUsbSerialPort { - private static final int USB_TIMEOUT_MILLIS = 5000; + private static final int USB_TIMEOUT_MILLIS = 5000; - private final int DEFAULT_BAUD_RATE = 9600; + private final int DEFAULT_BAUD_RATE = 9600; - private boolean dtr = false; - private boolean rts = false; + private boolean dtr = false; + private boolean rts = false; - public Ch340SerialPort(UsbDevice device, int portNumber) { - super(device, portNumber); - } + public Ch340SerialPort(UsbDevice device, int portNumber) { + super(device, portNumber); + } - @Override - public UsbSerialDriver getDriver() { - return Ch34xSerialDriver.this; - } + @Override + public UsbSerialDriver getDriver() { + return Ch34xSerialDriver.this; + } - @Override - protected void openInt(UsbDeviceConnection connection) throws IOException { - for (int i = 0; i < mDevice.getInterfaceCount(); i++) { - UsbInterface usbIface = mDevice.getInterface(i); - if (!mConnection.claimInterface(usbIface, true)) { - throw new IOException("Could not claim data interface"); - } - } + @Override + protected void openInt() throws IOException { + for (int i = 0; i < mDevice.getInterfaceCount(); i++) { + UsbInterface usbIface = mDevice.getInterface(i); + if (!mConnection.claimInterface(usbIface, true)) { + throw new IOException("Could not claim data interface"); + } + } - UsbInterface dataIface = mDevice.getInterface(mDevice.getInterfaceCount() - 1); - for (int i = 0; i < dataIface.getEndpointCount(); i++) { - UsbEndpoint ep = dataIface.getEndpoint(i); - if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) { - if (ep.getDirection() == UsbConstants.USB_DIR_IN) { - mReadEndpoint = ep; - } else { - mWriteEndpoint = ep; - } - } - } + UsbInterface dataIface = mDevice.getInterface(mDevice.getInterfaceCount() - 1); + for (int i = 0; i < dataIface.getEndpointCount(); i++) { + UsbEndpoint ep = dataIface.getEndpoint(i); + if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) { + if (ep.getDirection() == UsbConstants.USB_DIR_IN) { + mReadEndpoint = ep; + } else { + mWriteEndpoint = ep; + } + } + } - initialize(); - setBaudRate(DEFAULT_BAUD_RATE); - } + initialize(); + setBaudRate(DEFAULT_BAUD_RATE); + } - @Override - protected void closeInt() { - try { - for (int i = 0; i < mDevice.getInterfaceCount(); i++) - mConnection.releaseInterface(mDevice.getInterface(i)); - } catch(Exception ignored) {} - } + @Override + protected void closeInt() { + try { + for (int i = 0; i < mDevice.getInterfaceCount(); i++) + mConnection.releaseInterface(mDevice.getInterface(i)); + } catch(Exception ignored) {} + } - private int controlOut(int request, int value, int index) { - final int REQTYPE_HOST_TO_DEVICE = UsbConstants.USB_TYPE_VENDOR | UsbConstants.USB_DIR_OUT; - return mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, request, - value, index, null, 0, USB_TIMEOUT_MILLIS); - } + private int controlOut(int request, int value, int index) { + final int REQTYPE_HOST_TO_DEVICE = UsbConstants.USB_TYPE_VENDOR | UsbConstants.USB_DIR_OUT; + return mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, request, + value, index, null, 0, USB_TIMEOUT_MILLIS); + } - private int controlIn(int request, int value, int index, byte[] buffer) { - final int REQTYPE_DEVICE_TO_HOST = UsbConstants.USB_TYPE_VENDOR | UsbConstants.USB_DIR_IN; - return mConnection.controlTransfer(REQTYPE_DEVICE_TO_HOST, request, - value, index, buffer, buffer.length, USB_TIMEOUT_MILLIS); - } + private int controlIn(int request, int value, int index, byte[] buffer) { + final int REQTYPE_DEVICE_TO_HOST = UsbConstants.USB_TYPE_VENDOR | UsbConstants.USB_DIR_IN; + return mConnection.controlTransfer(REQTYPE_DEVICE_TO_HOST, request, + value, index, buffer, buffer.length, USB_TIMEOUT_MILLIS); + } - private void checkState(String msg, int request, int value, int[] expected) throws IOException { - byte[] buffer = new byte[expected.length]; - int ret = controlIn(request, value, 0, buffer); + private void checkState(String msg, int request, int value, int[] expected) throws IOException { + byte[] buffer = new byte[expected.length]; + int ret = controlIn(request, value, 0, buffer); - if (ret < 0) { - throw new IOException("Failed send cmd [" + msg + "]"); - } + if (ret < 0) { + throw new IOException("Failed send cmd [" + msg + "]"); + } - if (ret != expected.length) { - throw new IOException("Expected " + expected.length + " bytes, but get " + ret + " [" + msg + "]"); - } + if (ret != expected.length) { + throw new IOException("Expected " + expected.length + " bytes, but get " + ret + " [" + msg + "]"); + } - for (int i = 0; i < expected.length; i++) { - if (expected[i] == -1) { - continue; - } + for (int i = 0; i < expected.length; i++) { + if (expected[i] == -1) { + continue; + } - int current = buffer[i] & 0xff; - if (expected[i] != current) { - throw new IOException("Expected 0x" + Integer.toHexString(expected[i]) + " byte, but get 0x" + Integer.toHexString(current) + " [" + msg + "]"); - } - } - } + int current = buffer[i] & 0xff; + if (expected[i] != current) { + throw new IOException("Expected 0x" + Integer.toHexString(expected[i]) + " byte, but get 0x" + Integer.toHexString(current) + " [" + msg + "]"); + } + } + } - private void setControlLines() throws IOException { - if (controlOut(0xa4, ~((dtr ? SCL_DTR : 0) | (rts ? SCL_RTS : 0)), 0) < 0) { - throw new IOException("Failed to set control lines"); - } - } + private void setControlLines() throws IOException { + if (controlOut(0xa4, ~((dtr ? SCL_DTR : 0) | (rts ? SCL_RTS : 0)), 0) < 0) { + throw new IOException("Failed to set control lines"); + } + } - private byte getStatus() throws IOException { - byte[] buffer = new byte[2]; - int ret = controlIn(0x95, 0x0706, 0, buffer); - if (ret < 0) - throw new IOException("Error getting control lines"); - return buffer[0]; - } + private byte getStatus() throws IOException { + byte[] buffer = new byte[2]; + int ret = controlIn(0x95, 0x0706, 0, buffer); + if (ret < 0) + throw new IOException("Error getting control lines"); + return buffer[0]; + } - private void initialize() throws IOException { - checkState("init #1", 0x5f, 0, new int[]{-1 /* 0x27, 0x30 */, 0x00}); + private void initialize() throws IOException { + checkState("init #1", 0x5f, 0, new int[]{-1 /* 0x27, 0x30 */, 0x00}); - if (controlOut(0xa1, 0, 0) < 0) { - throw new IOException("Init failed: #2"); - } + if (controlOut(0xa1, 0, 0) < 0) { + throw new IOException("Init failed: #2"); + } - setBaudRate(DEFAULT_BAUD_RATE); + setBaudRate(DEFAULT_BAUD_RATE); - checkState("init #4", 0x95, 0x2518, new int[]{-1 /* 0x56, c3*/, 0x00}); + checkState("init #4", 0x95, 0x2518, new int[]{-1 /* 0x56, c3*/, 0x00}); - if (controlOut(0x9a, 0x2518, LCR_ENABLE_RX | LCR_ENABLE_TX | LCR_CS8) < 0) { - throw new IOException("Init failed: #5"); - } + if (controlOut(0x9a, 0x2518, LCR_ENABLE_RX | LCR_ENABLE_TX | LCR_CS8) < 0) { + throw new IOException("Init failed: #5"); + } - checkState("init #6", 0x95, 0x0706, new int[]{-1/*0xf?*/, -1/*0xec,0xee*/}); + checkState("init #6", 0x95, 0x0706, new int[]{-1/*0xf?*/, -1/*0xec,0xee*/}); - if (controlOut(0xa1, 0x501f, 0xd90a) < 0) { - throw new IOException("Init failed: #7"); - } + if (controlOut(0xa1, 0x501f, 0xd90a) < 0) { + throw new IOException("Init failed: #7"); + } - setBaudRate(DEFAULT_BAUD_RATE); + setBaudRate(DEFAULT_BAUD_RATE); - setControlLines(); + setControlLines(); - checkState("init #10", 0x95, 0x0706, new int[]{-1/* 0x9f, 0xff*/, -1/*0xec,0xee*/}); - } + checkState("init #10", 0x95, 0x0706, new int[]{-1/* 0x9f, 0xff*/, -1/*0xec,0xee*/}); + } - private void setBaudRate(int baudRate) throws IOException { - final long CH341_BAUDBASE_FACTOR = 1532620800; - final int CH341_BAUDBASE_DIVMAX = 3; + private void setBaudRate(int baudRate) throws IOException { + long factor; + long divisor; - long factor = CH341_BAUDBASE_FACTOR / baudRate; - int divisor = CH341_BAUDBASE_DIVMAX; + if (baudRate == 921600) { + divisor = 7; + factor = 0xf300; + } else { + final long BAUDBASE_FACTOR = 1532620800; + final int BAUDBASE_DIVMAX = 3; - while ((factor > 0xfff0) && divisor > 0) { - factor >>= 3; - divisor--; - } +// if(BuildConfig.DEBUG && (baudRate & (3<<29)) == (1<<29)) +// baudRate &= ~(1<<29); // for testing purpose bypass dedicated baud rate handling + factor = BAUDBASE_FACTOR / baudRate; + divisor = BAUDBASE_DIVMAX; + while ((factor > 0xfff0) && divisor > 0) { + factor >>= 3; + divisor--; + } + if (factor > 0xfff0) { + throw new UnsupportedOperationException("Unsupported baud rate: " + baudRate); + } + factor = 0x10000 - factor; + } - if (factor > 0xfff0) { - throw new UnsupportedOperationException("Unsupported baud rate: " + baudRate); - } + divisor |= 0x0080; // else ch341a waits until buffer full + int val1 = (int) ((factor & 0xff00) | divisor); + int val2 = (int) (factor & 0xff); + Log.d(TAG, String.format("baud rate=%d, 0x1312=0x%04x, 0x0f2c=0x%04x", baudRate, val1, val2)); + int ret = controlOut(0x9a, 0x1312, val1); + if (ret < 0) { + throw new IOException("Error setting baud rate: #1)"); + } + ret = controlOut(0x9a, 0x0f2c, val2); + if (ret < 0) { + throw new IOException("Error setting baud rate: #2"); + } + } - factor = 0x10000 - factor; - divisor |= 0x0080; // else ch341a waits until buffer full - int ret = controlOut(0x9a, 0x1312, (int) ((factor & 0xff00) | divisor)); - if (ret < 0) { - throw new IOException("Error setting baud rate: #1)"); - } + @Override + public void setParameters(int baudRate, int dataBits, int stopBits, @Parity int parity) throws IOException { + if(baudRate <= 0) { + throw new IllegalArgumentException("Invalid baud rate: " + baudRate); + } + setBaudRate(baudRate); - ret = controlOut(0x9a, 0x0f2c, (int) (factor & 0xff)); - if (ret < 0) { - throw new IOException("Error setting baud rate: #2"); - } - } + int lcr = LCR_ENABLE_RX | LCR_ENABLE_TX; - @Override - public void setParameters(int baudRate, int dataBits, int stopBits, @Parity int parity) throws IOException { - if(baudRate <= 0) { - throw new IllegalArgumentException("Invalid baud rate: " + baudRate); - } - setBaudRate(baudRate); + switch (dataBits) { + case DATABITS_5: + lcr |= LCR_CS5; + break; + case DATABITS_6: + lcr |= LCR_CS6; + break; + case DATABITS_7: + lcr |= LCR_CS7; + break; + case DATABITS_8: + lcr |= LCR_CS8; + break; + default: + throw new IllegalArgumentException("Invalid data bits: " + dataBits); + } - int lcr = LCR_ENABLE_RX | LCR_ENABLE_TX; + switch (parity) { + case PARITY_NONE: + break; + case PARITY_ODD: + lcr |= LCR_ENABLE_PAR; + break; + case PARITY_EVEN: + lcr |= LCR_ENABLE_PAR | LCR_PAR_EVEN; + break; + case PARITY_MARK: + lcr |= LCR_ENABLE_PAR | LCR_MARK_SPACE; + break; + case PARITY_SPACE: + lcr |= LCR_ENABLE_PAR | LCR_MARK_SPACE | LCR_PAR_EVEN; + break; + default: + throw new IllegalArgumentException("Invalid parity: " + parity); + } - switch (dataBits) { - case DATABITS_5: - lcr |= LCR_CS5; - break; - case DATABITS_6: - lcr |= LCR_CS6; - break; - case DATABITS_7: - lcr |= LCR_CS7; - break; - case DATABITS_8: - lcr |= LCR_CS8; - break; - default: - throw new IllegalArgumentException("Invalid data bits: " + dataBits); - } + switch (stopBits) { + case STOPBITS_1: + break; + case STOPBITS_1_5: + throw new UnsupportedOperationException("Unsupported stop bits: 1.5"); + case STOPBITS_2: + lcr |= LCR_STOP_BITS_2; + break; + default: + throw new IllegalArgumentException("Invalid stop bits: " + stopBits); + } - switch (parity) { - case PARITY_NONE: - break; - case PARITY_ODD: - lcr |= LCR_ENABLE_PAR; - break; - case PARITY_EVEN: - lcr |= LCR_ENABLE_PAR | LCR_PAR_EVEN; - break; - case PARITY_MARK: - lcr |= LCR_ENABLE_PAR | LCR_MARK_SPACE; - break; - case PARITY_SPACE: - lcr |= LCR_ENABLE_PAR | LCR_MARK_SPACE | LCR_PAR_EVEN; - break; - default: - throw new IllegalArgumentException("Invalid parity: " + parity); - } + int ret = controlOut(0x9a, 0x2518, lcr); + if (ret < 0) { + throw new IOException("Error setting control byte"); + } + } - switch (stopBits) { - case STOPBITS_1: - break; - case STOPBITS_1_5: - throw new UnsupportedOperationException("Unsupported stop bits: 1.5"); - case STOPBITS_2: - lcr |= LCR_STOP_BITS_2; - break; - default: - throw new IllegalArgumentException("Invalid stop bits: " + stopBits); - } + @Override + public boolean getCD() throws IOException { + return (getStatus() & GCL_CD) == 0; + } - int ret = controlOut(0x9a, 0x2518, lcr); - if (ret < 0) { - throw new IOException("Error setting control byte"); - } - } + @Override + public boolean getCTS() throws IOException { + return (getStatus() & GCL_CTS) == 0; + } - @Override - public boolean getCD() throws IOException { - return (getStatus() & GCL_CD) == 0; - } + @Override + public boolean getDSR() throws IOException { + return (getStatus() & GCL_DSR) == 0; + } - @Override - public boolean getCTS() throws IOException { - return (getStatus() & GCL_CTS) == 0; - } + @Override + public boolean getDTR() throws IOException { + return dtr; + } - @Override - public boolean getDSR() throws IOException { - return (getStatus() & GCL_DSR) == 0; - } + @Override + public void setDTR(boolean value) throws IOException { + dtr = value; + setControlLines(); + } - @Override - public boolean getDTR() throws IOException { - return dtr; - } + @Override + public boolean getRI() throws IOException { + return (getStatus() & GCL_RI) == 0; + } - @Override - public void setDTR(boolean value) throws IOException { - dtr = value; - setControlLines(); - } + @Override + public boolean getRTS() throws IOException { + return rts; + } - @Override - public boolean getRI() throws IOException { - return (getStatus() & GCL_RI) == 0; - } + @Override + public void setRTS(boolean value) throws IOException { + rts = value; + setControlLines(); + } - @Override - public boolean getRTS() throws IOException { - return rts; - } + @Override + public EnumSet getControlLines() throws IOException { + int status = getStatus(); + EnumSet set = EnumSet.noneOf(ControlLine.class); + if(rts) set.add(ControlLine.RTS); + if((status & GCL_CTS) == 0) set.add(ControlLine.CTS); + if(dtr) set.add(ControlLine.DTR); + if((status & GCL_DSR) == 0) set.add(ControlLine.DSR); + if((status & GCL_CD) == 0) set.add(ControlLine.CD); + if((status & GCL_RI) == 0) set.add(ControlLine.RI); + return set; + } - @Override - public void setRTS(boolean value) throws IOException { - rts = value; - setControlLines(); - } + @Override + public EnumSet getSupportedControlLines() throws IOException { + return EnumSet.allOf(ControlLine.class); + } - @Override - public EnumSet getControlLines() throws IOException { - int status = getStatus(); - EnumSet set = EnumSet.noneOf(ControlLine.class); - if(rts) set.add(ControlLine.RTS); - if((status & GCL_CTS) == 0) set.add(ControlLine.CTS); - if(dtr) set.add(ControlLine.DTR); - if((status & GCL_DSR) == 0) set.add(ControlLine.DSR); - if((status & GCL_CD) == 0) set.add(ControlLine.CD); - if((status & GCL_RI) == 0) set.add(ControlLine.RI); - return set; - } + @Override + public void setBreak(boolean value) throws IOException { + byte[] req = new byte[2]; + if(controlIn(0x95, 0x1805, 0, req) < 0) { + throw new IOException("Error getting BREAK condition"); + } + if(value) { + req[0] &= ~1; + req[1] &= ~0x40; + } else { + req[0] |= 1; + req[1] |= 0x40; + } + int val = (req[1] & 0xff) << 8 | (req[0] & 0xff); + if(controlOut(0x9a, 0x1805, val) < 0) { + throw new IOException("Error setting BREAK condition"); + } + } + } - @Override - public EnumSet getSupportedControlLines() throws IOException { - return EnumSet.allOf(ControlLine.class); - } - - @Override - public void setBreak(boolean value) throws IOException { - byte[] req = new byte[2]; - if(controlIn(0x95, 0x1805, 0, req) < 0) { - throw new IOException("Error getting BREAK condition"); - } - if(value) { - req[0] &= ~1; - req[1] &= ~0x40; - } else { - req[0] |= 1; - req[1] |= 0x40; - } - int val = (req[1] & 0xff) << 8 | (req[0] & 0xff); - if(controlOut(0x9a, 0x1805, val) < 0) { - throw new IOException("Error setting BREAK condition"); - } - } - } - - public static Map getSupportedDevices() { - final Map supportedDevices = new LinkedHashMap<>(); - supportedDevices.put(UsbId.VENDOR_QINHENG, new int[]{ - UsbId.QINHENG_CH340, - UsbId.QINHENG_CH341A, - }); - return supportedDevices; - } + @SuppressWarnings({"unused"}) + public static Map getSupportedDevices() { + final Map supportedDevices = new LinkedHashMap<>(); + supportedDevices.put(UsbId.VENDOR_QINHENG, new int[]{ + UsbId.QINHENG_CH340, + UsbId.QINHENG_CH341A, + }); + return supportedDevices; + } } \ No newline at end of file diff --git a/app/src/main/java/org/emulator/calculator/usbserial/driver/ChromeCcdSerialDriver.java b/app/src/main/java/org/emulator/calculator/usbserial/driver/ChromeCcdSerialDriver.java new file mode 100644 index 0000000..ef93042 --- /dev/null +++ b/app/src/main/java/org/emulator/calculator/usbserial/driver/ChromeCcdSerialDriver.java @@ -0,0 +1,96 @@ +package org.emulator.calculator.usbserial.driver; + +import android.hardware.usb.UsbConstants; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbEndpoint; +import android.hardware.usb.UsbInterface; +import android.util.Log; + +import java.io.IOException; +import java.util.Collections; +import java.util.EnumSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.ArrayList; + +public class ChromeCcdSerialDriver implements UsbSerialDriver{ + + private final String TAG = ChromeCcdSerialDriver.class.getSimpleName(); + + private final UsbDevice mDevice; + private final List mPorts; + + @Override + public UsbDevice getDevice() { + return mDevice; + } + + @Override + public List getPorts() { + return mPorts; + } + + public ChromeCcdSerialDriver(UsbDevice mDevice) { + this.mDevice = mDevice; + mPorts = new ArrayList(); + for (int i = 0; i < 3; i++) + mPorts.add(new ChromeCcdSerialPort(mDevice, i)); + } + + public class ChromeCcdSerialPort extends CommonUsbSerialPort { + private UsbInterface mDataInterface; + + public ChromeCcdSerialPort(UsbDevice device, int portNumber) { + super(device, portNumber); + } + + @Override + protected void openInt() throws IOException { + Log.d(TAG, "claiming interfaces, count=" + mDevice.getInterfaceCount()); + mDataInterface = mDevice.getInterface(mPortNumber); + if (!mConnection.claimInterface(mDataInterface, true)) { + throw new IOException("Could not claim shared control/data interface"); + } + Log.d(TAG, "endpoint count=" + mDataInterface.getEndpointCount()); + for (int i = 0; i < mDataInterface.getEndpointCount(); ++i) { + UsbEndpoint ep = mDataInterface.getEndpoint(i); + if ((ep.getDirection() == UsbConstants.USB_DIR_IN) && (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) { + mReadEndpoint = ep; + } else if ((ep.getDirection() == UsbConstants.USB_DIR_OUT) && (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) { + mWriteEndpoint = ep; + } + } + } + + @Override + protected void closeInt() { + try { + mConnection.releaseInterface(mDataInterface); + } catch(Exception ignored) {} + } + + @Override + public UsbSerialDriver getDriver() { + return ChromeCcdSerialDriver.this; + } + + @Override + public void setParameters(int baudRate, int dataBits, int stopBits, int parity) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public EnumSet getSupportedControlLines() throws IOException { + return EnumSet.noneOf(ControlLine.class); + } + } + + public static Map getSupportedDevices() { + final Map supportedDevices = new LinkedHashMap<>(); + supportedDevices.put(UsbId.VENDOR_GOOGLE, new int[]{ + UsbId.GOOGLE_CR50, + }); + return supportedDevices; + } +} diff --git a/app/src/main/java/org/emulator/calculator/usbserial/driver/CommonUsbSerialPort.java b/app/src/main/java/org/emulator/calculator/usbserial/driver/CommonUsbSerialPort.java index 0a7e93b..d9faa34 100644 --- a/app/src/main/java/org/emulator/calculator/usbserial/driver/CommonUsbSerialPort.java +++ b/app/src/main/java/org/emulator/calculator/usbserial/driver/CommonUsbSerialPort.java @@ -25,28 +25,32 @@ import java.util.EnumSet; */ public abstract class CommonUsbSerialPort implements UsbSerialPort { + public static boolean DEBUG = false; + private static final String TAG = CommonUsbSerialPort.class.getSimpleName(); - private static final int DEFAULT_WRITE_BUFFER_SIZE = 16 * 1024; private static final int MAX_READ_SIZE = 16 * 1024; // = old bulkTransfer limit protected final UsbDevice mDevice; protected final int mPortNumber; // non-null when open() - protected UsbDeviceConnection mConnection = null; + protected UsbDeviceConnection mConnection; protected UsbEndpoint mReadEndpoint; protected UsbEndpoint mWriteEndpoint; protected UsbRequest mUsbRequest; - protected final Object mWriteBufferLock = new Object(); - /** Internal write buffer. Guarded by {@link #mWriteBufferLock}. */ + /** + * Internal write buffer. + * Guarded by {@link #mWriteBufferLock}. + * Default length = mReadEndpoint.getMaxPacketSize() + **/ protected byte[] mWriteBuffer; + protected final Object mWriteBufferLock = new Object(); + public CommonUsbSerialPort(UsbDevice device, int portNumber) { mDevice = device; mPortNumber = portNumber; - - mWriteBuffer = new byte[DEFAULT_WRITE_BUFFER_SIZE]; } @Override @@ -85,11 +89,19 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort { * Sets the size of the internal buffer used to exchange data with the USB * stack for write operations. Most users should not need to change this. * - * @param bufferSize the size in bytes + * @param bufferSize the size in bytes, <= 0 resets original size */ public final void setWriteBufferSize(int bufferSize) { synchronized (mWriteBufferLock) { - if (bufferSize == mWriteBuffer.length) { + if (bufferSize <= 0) { + if (mWriteEndpoint != null) { + bufferSize = mWriteEndpoint.getMaxPacketSize(); + } else { + mWriteBuffer = null; + return; + } + } + if (mWriteBuffer != null && bufferSize == mWriteBuffer.length) { return; } mWriteBuffer = new byte[bufferSize]; @@ -106,7 +118,7 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort { } mConnection = connection; try { - openInt(connection); + openInt(); if (mReadEndpoint == null || mWriteEndpoint == null) { throw new IOException("Could not get read & write endpoints"); } @@ -120,17 +132,18 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort { } } - protected abstract void openInt(UsbDeviceConnection connection) throws IOException; + protected abstract void openInt() throws IOException; @Override public void close() throws IOException { if (mConnection == null) { throw new IOException("Already closed"); } - try { - mUsbRequest.cancel(); - } catch(Exception ignored) {} + UsbRequest usbRequest = mUsbRequest; mUsbRequest = null; + try { + usbRequest.cancel(); + } catch(Exception ignored) {} try { closeInt(); } catch(Exception ignored) {} @@ -145,25 +158,40 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort { /** * use simple USB request supported by all devices to test if connection is still valid */ - protected void testConnection() throws IOException { + protected void testConnection(boolean full) throws IOException { + testConnection(full, "USB get_status request failed"); + } + + protected void testConnection(boolean full, String msg) throws IOException { + if(mUsbRequest == null) { + throw new IOException("Connection closed"); + } + if(!full) { + return; + } byte[] buf = new byte[2]; int len = mConnection.controlTransfer(0x80 /*DEVICE*/, 0 /*GET_STATUS*/, 0, 0, buf, buf.length, 200); if(len < 0) - throw new IOException("USB get_status request failed"); + throw new IOException(msg); } @Override public int read(final byte[] dest, final int timeout) throws IOException { - return read(dest, timeout, true); + if(dest.length == 0) { + throw new IllegalArgumentException("Read buffer too small"); + } + return read(dest, dest.length, timeout); } - protected int read(final byte[] dest, final int timeout, boolean testConnection) throws IOException { - if(mConnection == null) { - throw new IOException("Connection closed"); - } - if(dest.length <= 0) { - throw new IllegalArgumentException("Read buffer to small"); + @Override + public int read(final byte[] dest, final int length, final int timeout) throws IOException {return read(dest, length, timeout, true);} + + protected int read(final byte[] dest, int length, final int timeout, boolean testConnection) throws IOException { + testConnection(false); + if(length <= 0) { + throw new IllegalArgumentException("Read length too small"); } + length = Math.min(length, dest.length); final int nread; if (timeout != 0) { // bulkTransfer will cause data loss with short timeout + high baud rates + continuous transfer @@ -175,16 +203,16 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort { // /system/lib64/libandroid_runtime.so (android_hardware_UsbDeviceConnection_request_wait(_JNIEnv*, _jobject*, long)+84) // data loss / crashes were observed with timeout up to 200 msec long endTime = testConnection ? MonotonicClock.millis() + timeout : 0; - int readMax = Math.min(dest.length, MAX_READ_SIZE); + int readMax = Math.min(length, MAX_READ_SIZE); nread = mConnection.bulkTransfer(mReadEndpoint, dest, readMax, timeout); // Android error propagation is improvable: // nread == -1 can be: timeout, connection lost, buffer to small, ??? - if(nread == -1 && testConnection && MonotonicClock.millis() < endTime) - testConnection(); + if(nread == -1 && testConnection) + testConnection(MonotonicClock.millis() < endTime); } else { - final ByteBuffer buf = ByteBuffer.wrap(dest); - if (!mUsbRequest.queue(buf, dest.length)) { + final ByteBuffer buf = ByteBuffer.wrap(dest, 0, length); + if (!mUsbRequest.queue(buf, length)) { throw new IOException("Queueing USB request failed"); } final UsbRequest response = mConnection.requestWait(); @@ -195,21 +223,23 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort { // Android error propagation is improvable: // response != null & nread == 0 can be: connection lost, buffer to small, ??? if(nread == 0) { - testConnection(); + testConnection(true); } } return Math.max(nread, 0); } @Override - public void write(final byte[] src, final int timeout) throws IOException { - int offset = 0; - final long endTime = (timeout == 0) ? 0 : (MonotonicClock.millis() + timeout); + public void write(byte[] src, int timeout) throws IOException {write(src, src.length, timeout);} - if(mConnection == null) { - throw new IOException("Connection closed"); - } - while (offset < src.length) { + @Override + public void write(final byte[] src, int length, final int timeout) throws IOException { + int offset = 0; + long startTime = MonotonicClock.millis(); + length = Math.min(length, src.length); + + testConnection(false); + while (offset < length) { int requestTimeout; final int requestLength; final int actualLength; @@ -217,7 +247,10 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort { synchronized (mWriteBufferLock) { final byte[] writeBuffer; - requestLength = Math.min(src.length - offset, mWriteBuffer.length); + if (mWriteBuffer == null) { + mWriteBuffer = new byte[mWriteEndpoint.getMaxPacketSize()]; + } + requestLength = Math.min(length - offset, mWriteBuffer.length); if (offset == 0) { writeBuffer = src; } else { @@ -228,7 +261,7 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort { if (timeout == 0 || offset == 0) { requestTimeout = timeout; } else { - requestTimeout = (int)(endTime - MonotonicClock.millis()); + requestTimeout = (int)(startTime + timeout - MonotonicClock.millis()); if(requestTimeout == 0) requestTimeout = -1; } @@ -238,14 +271,18 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort { actualLength = mConnection.bulkTransfer(mWriteEndpoint, writeBuffer, requestLength, requestTimeout); } } - Log.d(TAG, "Wrote " + actualLength + "/" + requestLength + " offset " + offset + "/" + src.length + " timeout " + requestTimeout); + long elapsed = MonotonicClock.millis() - startTime; + if (DEBUG) { + Log.d(TAG, "Wrote " + actualLength + "/" + requestLength + " offset " + offset + "/" + length + " time " + elapsed + "/" + requestTimeout); + } if (actualLength <= 0) { - if (timeout != 0 && MonotonicClock.millis() >= endTime) { - SerialTimeoutException ex = new SerialTimeoutException("Error writing " + requestLength + " bytes at offset " + offset + " of total " + src.length + ", rc=" + actualLength); - ex.bytesTransferred = offset; - throw ex; + String msg = "Error writing " + requestLength + " bytes at offset " + offset + " of total " + src.length + " after " + elapsed + "msec, rc=" + actualLength; + if (timeout != 0) { + testConnection(elapsed < timeout, msg); + throw new SerialTimeoutException(msg, offset); } else { - throw new IOException("Error writing " + requestLength + " bytes at offset " + offset + " of total " + src.length); + throw new IOException(msg); + } } offset += actualLength; @@ -254,7 +291,7 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort { @Override public boolean isOpen() { - return mConnection != null; + return mUsbRequest != null; } @Override @@ -285,7 +322,7 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort { public void setRTS(boolean value) throws IOException { throw new UnsupportedOperationException(); } @Override - public abstract EnumSet getControlLines() throws IOException; + public EnumSet getControlLines() throws IOException { throw new UnsupportedOperationException(); } @Override public abstract EnumSet getSupportedControlLines() throws IOException; diff --git a/app/src/main/java/org/emulator/calculator/usbserial/driver/Cp21xxSerialDriver.java b/app/src/main/java/org/emulator/calculator/usbserial/driver/Cp21xxSerialDriver.java index c8f02c0..906486c 100644 --- a/app/src/main/java/org/emulator/calculator/usbserial/driver/Cp21xxSerialDriver.java +++ b/app/src/main/java/org/emulator/calculator/usbserial/driver/Cp21xxSerialDriver.java @@ -8,7 +8,6 @@ package org.emulator.calculator.usbserial.driver; import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDevice; -import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbEndpoint; import android.hardware.usb.UsbInterface; @@ -119,14 +118,14 @@ public class Cp21xxSerialDriver implements UsbSerialDriver { byte[] buffer = new byte[1]; int result = mConnection.controlTransfer(REQTYPE_DEVICE_TO_HOST, SILABSER_GET_MDMSTS_REQUEST_CODE, 0, mPortNumber, buffer, buffer.length, USB_WRITE_TIMEOUT_MILLIS); - if (result != 1) { + if (result != buffer.length) { throw new IOException("Control transfer failed: " + SILABSER_GET_MDMSTS_REQUEST_CODE + " / " + 0 + " -> " + result); } return buffer[0]; } @Override - protected void openInt(UsbDeviceConnection connection) throws IOException { + protected void openInt() throws IOException { mIsRestrictedPort = mDevice.getInterfaceCount() == 2 && mPortNumber == 1; if(mPortNumber >= mDevice.getInterfaceCount()) { throw new IOException("Unknown port number"); @@ -321,6 +320,7 @@ public class Cp21xxSerialDriver implements UsbSerialDriver { } } + @SuppressWarnings({"unused"}) public static Map getSupportedDevices() { final Map supportedDevices = new LinkedHashMap<>(); supportedDevices.put(UsbId.VENDOR_SILABS, diff --git a/app/src/main/java/org/emulator/calculator/usbserial/driver/FtdiSerialDriver.java b/app/src/main/java/org/emulator/calculator/usbserial/driver/FtdiSerialDriver.java index 4fa50e0..d28e729 100644 --- a/app/src/main/java/org/emulator/calculator/usbserial/driver/FtdiSerialDriver.java +++ b/app/src/main/java/org/emulator/calculator/usbserial/driver/FtdiSerialDriver.java @@ -9,7 +9,6 @@ package org.emulator.calculator.usbserial.driver; import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDevice; -import android.hardware.usb.UsbDeviceConnection; import android.util.Log; import org.emulator.calculator.usbserial.util.MonotonicClock; @@ -99,8 +98,8 @@ public class FtdiSerialDriver implements UsbSerialDriver { @Override - protected void openInt(UsbDeviceConnection connection) throws IOException { - if (!connection.claimInterface(mDevice.getInterface(mPortNumber), true)) { + protected void openInt() throws IOException { + if (!mConnection.claimInterface(mDevice.getInterface(mPortNumber), true)) { throw new IOException("Could not claim interface " + mPortNumber); } if (mDevice.getInterface(mPortNumber).getEndpointCount() < 2) { @@ -123,12 +122,13 @@ public class FtdiSerialDriver implements UsbSerialDriver { } // mDevice.getVersion() would require API 23 - byte[] rawDescriptors = connection.getRawDescriptors(); + byte[] rawDescriptors = mConnection.getRawDescriptors(); if(rawDescriptors == null || rawDescriptors.length < 14) { throw new IOException("Could not get device descriptors"); } int deviceType = rawDescriptors[13]; - baudRateWithPort = deviceType == 7 || deviceType == 8 || deviceType == 9; // ...H devices + baudRateWithPort = deviceType == 7 || deviceType == 8 || deviceType == 9 // ...H devices + || mDevice.getInterfaceCount() > 1; // FT2232C } @Override @@ -139,24 +139,37 @@ public class FtdiSerialDriver implements UsbSerialDriver { } @Override - public int read(final byte[] dest, final int timeout) throws IOException { + public int read(final byte[] dest, final int timeout) throws IOException + { if(dest.length <= READ_HEADER_LENGTH) { - throw new IllegalArgumentException("Read buffer to small"); + throw new IllegalArgumentException("Read buffer too small"); // could allocate larger buffer, including space for 2 header bytes, but this would // result in buffers not being 64 byte aligned any more, causing data loss at continuous // data transfer at high baud rates when buffers are fully filled. } + return read(dest, dest.length, timeout); + } + + @Override + public int read(final byte[] dest, int length, final int timeout) throws IOException { + if(length <= READ_HEADER_LENGTH) { + throw new IllegalArgumentException("Read length too small"); + // could allocate larger buffer, including space for 2 header bytes, but this would + // result in buffers not being 64 byte aligned any more, causing data loss at continuous + // data transfer at high baud rates when buffers are fully filled. + } + length = Math.min(length, dest.length); int nread; if (timeout != 0) { long endTime = MonotonicClock.millis() + timeout; do { - nread = super.read(dest, Math.max(1, (int)(endTime - MonotonicClock.millis())), false); + nread = super.read(dest, length, Math.max(1, (int)(endTime - MonotonicClock.millis())), false); } while (nread == READ_HEADER_LENGTH && MonotonicClock.millis() < endTime); - if(nread <= 0 && MonotonicClock.millis() < endTime) - testConnection(); + if(nread <= 0) + testConnection(MonotonicClock.millis() < endTime); } else { do { - nread = super.read(dest, timeout, false); + nread = super.read(dest, length, timeout); } while (nread == READ_HEADER_LENGTH); } return readFilter(dest, nread); @@ -290,7 +303,7 @@ public class FtdiSerialDriver implements UsbSerialDriver { byte[] data = new byte[2]; int result = mConnection.controlTransfer(REQTYPE_DEVICE_TO_HOST, GET_MODEM_STATUS_REQUEST, 0, mPortNumber+1, data, data.length, USB_WRITE_TIMEOUT_MILLIS); - if (result != 2) { + if (result != data.length) { throw new IOException("Get modem status failed: result=" + result); } return data[0]; @@ -406,7 +419,7 @@ public class FtdiSerialDriver implements UsbSerialDriver { byte[] data = new byte[1]; int result = mConnection.controlTransfer(REQTYPE_DEVICE_TO_HOST, GET_LATENCY_TIMER_REQUEST, 0, mPortNumber+1, data, data.length, USB_WRITE_TIMEOUT_MILLIS); - if (result != 1) { + if (result != data.length) { throw new IOException("Get latency timer failed: result=" + result); } return data[0]; @@ -414,6 +427,7 @@ public class FtdiSerialDriver implements UsbSerialDriver { } + @SuppressWarnings({"unused"}) public static Map getSupportedDevices() { final Map supportedDevices = new LinkedHashMap<>(); supportedDevices.put(UsbId.VENDOR_FTDI, diff --git a/app/src/main/java/org/emulator/calculator/usbserial/driver/GsmModemSerialDriver.java b/app/src/main/java/org/emulator/calculator/usbserial/driver/GsmModemSerialDriver.java new file mode 100644 index 0000000..c180401 --- /dev/null +++ b/app/src/main/java/org/emulator/calculator/usbserial/driver/GsmModemSerialDriver.java @@ -0,0 +1,106 @@ +package org.emulator.calculator.usbserial.driver; + +import android.hardware.usb.UsbConstants; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbEndpoint; +import android.hardware.usb.UsbInterface; +import android.util.Log; + +import java.io.IOException; +import java.util.Collections; +import java.util.EnumSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class GsmModemSerialDriver implements UsbSerialDriver{ + + private final String TAG = GsmModemSerialDriver.class.getSimpleName(); + + private final UsbDevice mDevice; + private final UsbSerialPort mPort; + + @Override + public UsbDevice getDevice() { + return mDevice; + } + + @Override + public List getPorts() { + return Collections.singletonList(mPort); + } + + public GsmModemSerialDriver(UsbDevice mDevice) { + this.mDevice = mDevice; + mPort = new GsmModemSerialPort(mDevice, 0); + } + + public class GsmModemSerialPort extends CommonUsbSerialPort { + + private UsbInterface mDataInterface; + + public GsmModemSerialPort(UsbDevice device, int portNumber) { + super(device, portNumber); + } + + @Override + protected void openInt() throws IOException { + Log.d(TAG, "claiming interfaces, count=" + mDevice.getInterfaceCount()); + mDataInterface = mDevice.getInterface(0); + if (!mConnection.claimInterface(mDataInterface, true)) { + throw new IOException("Could not claim shared control/data interface"); + } + Log.d(TAG, "endpoint count=" + mDataInterface.getEndpointCount()); + for (int i = 0; i < mDataInterface.getEndpointCount(); ++i) { + UsbEndpoint ep = mDataInterface.getEndpoint(i); + if ((ep.getDirection() == UsbConstants.USB_DIR_IN) && (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) { + mReadEndpoint = ep; + } else if ((ep.getDirection() == UsbConstants.USB_DIR_OUT) && (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) { + mWriteEndpoint = ep; + } + } + initGsmModem(); + } + + @Override + protected void closeInt() { + try { + mConnection.releaseInterface(mDataInterface); + } catch(Exception ignored) {} + + } + + private int initGsmModem() throws IOException { + int len = mConnection.controlTransfer( + 0x21, 0x22, 0x01, 0, null, 0, 5000); + if(len < 0) { + throw new IOException("init failed"); + } + return len; + } + + @Override + public UsbSerialDriver getDriver() { + return GsmModemSerialDriver.this; + } + + @Override + public void setParameters(int baudRate, int dataBits, int stopBits, int parity) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public EnumSet getSupportedControlLines() throws IOException { + return EnumSet.noneOf(ControlLine.class); + } + } + + public static Map getSupportedDevices() { + final Map supportedDevices = new LinkedHashMap<>(); + supportedDevices.put(UsbId.VENDOR_UNISOC, new int[]{ + UsbId.FIBOCOM_L610, + UsbId.FIBOCOM_L612, + }); + return supportedDevices; + } +} diff --git a/app/src/main/java/org/emulator/calculator/usbserial/driver/ProbeTable.java b/app/src/main/java/org/emulator/calculator/usbserial/driver/ProbeTable.java index b64918e..bcf6e0d 100644 --- a/app/src/main/java/org/emulator/calculator/usbserial/driver/ProbeTable.java +++ b/app/src/main/java/org/emulator/calculator/usbserial/driver/ProbeTable.java @@ -6,6 +6,7 @@ package org.emulator.calculator.usbserial.driver; +import android.hardware.usb.UsbDevice; import android.util.Pair; import java.lang.reflect.InvocationTargetException; @@ -14,14 +15,14 @@ import java.util.LinkedHashMap; import java.util.Map; /** - * Maps (vendor id, product id) pairs to the corresponding serial driver. - * - * @author mike wakerly (opensource@hoho.com) + * Maps (vendor id, product id) pairs to the corresponding serial driver, + * or invoke 'probe' method to check actual USB devices for matching interfaces. */ public class ProbeTable { - private final Map, Class> mProbeTable = + private final Map, Class> mVidPidProbeTable = new LinkedHashMap<>(); + private final Map> mMethodProbeTable = new LinkedHashMap<>(); /** * Adds or updates a (vendor, product) pair in the table. @@ -33,7 +34,7 @@ public class ProbeTable { */ public ProbeTable addProduct(int vendorId, int productId, Class driverClass) { - mProbeTable.put(Pair.create(vendorId, productId), driverClass); + mVidPidProbeTable.put(Pair.create(vendorId, productId), driverClass); return this; } @@ -41,12 +42,11 @@ public class ProbeTable { * Internal method to add all supported products from * {@code getSupportedProducts} static method. * - * @param driverClass - * @return + * @param driverClass to be added */ @SuppressWarnings("unchecked") - ProbeTable addDriver(Class driverClass) { - final Method method; + void addDriver(Class driverClass) { + Method method; try { method = driverClass.getMethod("getSupportedDevices"); @@ -68,20 +68,35 @@ public class ProbeTable { } } - return this; + try { + method = driverClass.getMethod("probe", UsbDevice.class); + mMethodProbeTable.put(method, driverClass); + } catch (SecurityException | NoSuchMethodException ignored) { + } } /** - * Returns the driver for the given (vendor, product) pair, or {@code null} - * if no match. + * Returns the driver for the given USB device, or {@code null} if no match. * - * @param vendorId the USB vendor id - * @param productId the USB product id + * @param usbDevice the USB device to be probed * @return the driver class matching this pair, or {@code null} */ - public Class findDriver(int vendorId, int productId) { - final Pair pair = Pair.create(vendorId, productId); - return mProbeTable.get(pair); + public Class findDriver(final UsbDevice usbDevice) { + final Pair pair = Pair.create(usbDevice.getVendorId(), usbDevice.getProductId()); + Class driverClass = mVidPidProbeTable.get(pair); + if (driverClass != null) + return driverClass; + for (Map.Entry> entry : mMethodProbeTable.entrySet()) { + try { + Method method = entry.getKey(); + Object o = method.invoke(null, usbDevice); + if((boolean)o) + return entry.getValue(); + } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + return null; } } diff --git a/app/src/main/java/org/emulator/calculator/usbserial/driver/ProlificSerialDriver.java b/app/src/main/java/org/emulator/calculator/usbserial/driver/ProlificSerialDriver.java index 234215c..2ae4968 100644 --- a/app/src/main/java/org/emulator/calculator/usbserial/driver/ProlificSerialDriver.java +++ b/app/src/main/java/org/emulator/calculator/usbserial/driver/ProlificSerialDriver.java @@ -11,7 +11,6 @@ package org.emulator.calculator.usbserial.driver; import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDevice; -import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbEndpoint; import android.hardware.usb.UsbInterface; import android.util.Log; @@ -35,7 +34,7 @@ public class ProlificSerialDriver implements UsbSerialDriver { 28800, 38400, 57600, 115200, 128000, 134400, 161280, 201600, 230400, 268800, 403200, 460800, 614400, 806400, 921600, 1228800, 2457600, 3000000, 6000000 }; - protected enum DeviceType { DEVICE_TYPE_01, DEVICE_TYPE_T, DEVICE_TYPE_HX, DEVICE_TYPE_HXN} + protected enum DeviceType { DEVICE_TYPE_01, DEVICE_TYPE_T, DEVICE_TYPE_HX, DEVICE_TYPE_HXN } private final UsbDevice mDevice; private final UsbSerialPort mPort; @@ -123,7 +122,7 @@ public class ProlificSerialDriver implements UsbSerialDriver { private volatile Thread mReadStatusThread = null; private final Object mReadStatusThreadLock = new Object(); private boolean mStopReadStatusThread = false; - private IOException mReadStatusException = null; + private Exception mReadStatusException = null; public ProlificSerialPort(UsbDevice device, int portNumber) { @@ -139,7 +138,7 @@ public class ProlificSerialDriver implements UsbSerialDriver { byte[] buffer = new byte[length]; int result = mConnection.controlTransfer(requestType, request, value, index, buffer, length, USB_READ_TIMEOUT_MILLIS); if (result != length) { - throw new IOException(String.format("ControlTransfer 0x%x failed: %d",value, result)); + throw new IOException(String.format("ControlTransfer %s 0x%x failed: %d",mDeviceType.name(), value, result)); } return buffer; } @@ -148,7 +147,7 @@ public class ProlificSerialDriver implements UsbSerialDriver { int length = (data == null) ? 0 : data.length; int result = mConnection.controlTransfer(requestType, request, value, index, data, length, USB_WRITE_TIMEOUT_MILLIS); if (result != length) { - throw new IOException( String.format("ControlTransfer 0x%x failed: %d", value, result)); + throw new IOException( String.format("ControlTransfer %s 0x%x failed: %d", mDeviceType.name(), value, result)); } } @@ -202,12 +201,12 @@ public class ProlificSerialDriver implements UsbSerialDriver { private void readStatusThreadFunction() { try { + byte[] buffer = new byte[STATUS_BUFFER_SIZE]; while (!mStopReadStatusThread) { - byte[] buffer = new byte[STATUS_BUFFER_SIZE]; long endTime = MonotonicClock.millis() + 500; int readBytesCount = mConnection.bulkTransfer(mInterruptEndpoint, buffer, STATUS_BUFFER_SIZE, 500); - if(readBytesCount == -1 && MonotonicClock.millis() < endTime) - testConnection(); + if(readBytesCount == -1) + testConnection(MonotonicClock.millis() < endTime); if (readBytesCount > 0) { if (readBytesCount != STATUS_BUFFER_SIZE) { throw new IOException("Invalid status notification, expected " + STATUS_BUFFER_SIZE + " bytes, got " + readBytesCount); @@ -218,8 +217,9 @@ public class ProlificSerialDriver implements UsbSerialDriver { } } } - } catch (IOException e) { - mReadStatusException = e; + } catch (Exception e) { + if (isOpen()) + mReadStatusException = e; } //Log.d(TAG, "end control line status thread " + mStopReadStatusThread + " " + (mReadStatusException == null ? "-" : mReadStatusException.getMessage())); } @@ -250,8 +250,8 @@ public class ProlificSerialDriver implements UsbSerialDriver { } } - /* throw and clear an exception which occured in the status read thread */ - IOException readStatusException = mReadStatusException; + /* throw and clear an exception which occurred in the status read thread */ + Exception readStatusException = mReadStatusException; if (mReadStatusException != null) { mReadStatusException = null; throw new IOException(readStatusException); @@ -265,10 +265,10 @@ public class ProlificSerialDriver implements UsbSerialDriver { } @Override - public void openInt(UsbDeviceConnection connection) throws IOException { + public void openInt() throws IOException { UsbInterface usbInterface = mDevice.getInterface(0); - if (!connection.claimInterface(usbInterface, true)) { + if (!mConnection.claimInterface(usbInterface, true)) { throw new IOException("Error claiming Prolific interface 0"); } @@ -290,7 +290,7 @@ public class ProlificSerialDriver implements UsbSerialDriver { } } - byte[] rawDescriptors = connection.getRawDescriptors(); + byte[] rawDescriptors = mConnection.getRawDescriptors(); if(rawDescriptors == null || rawDescriptors.length < 14) { throw new IOException("Could not get device descriptors"); } @@ -299,15 +299,19 @@ public class ProlificSerialDriver implements UsbSerialDriver { byte maxPacketSize0 = rawDescriptors[7]; if (mDevice.getDeviceClass() == 0x02 || maxPacketSize0 != 64) { mDeviceType = DeviceType.DEVICE_TYPE_01; - } else if(deviceVersion == 0x300 && usbVersion == 0x200) { - mDeviceType = DeviceType.DEVICE_TYPE_T; // TA - } else if(deviceVersion == 0x500) { - mDeviceType = DeviceType.DEVICE_TYPE_T; // TB - } else if(usbVersion == 0x200 && !testHxStatus()) { - mDeviceType = DeviceType.DEVICE_TYPE_HXN; + } else if(usbVersion == 0x200) { + if(deviceVersion == 0x300 && testHxStatus()) { + mDeviceType = DeviceType.DEVICE_TYPE_T; // TA + } else if(deviceVersion == 0x500 && testHxStatus()) { + mDeviceType = DeviceType.DEVICE_TYPE_T; // TB + } else { + mDeviceType = DeviceType.DEVICE_TYPE_HXN; + } } else { mDeviceType = DeviceType.DEVICE_TYPE_HX; } + Log.d(TAG, String.format("usbVersion=%x, deviceVersion=%x, deviceClass=%d, packetSize=%d => deviceType=%s", + usbVersion, deviceVersion, mDevice.getDeviceClass(), maxPacketSize0, mDeviceType.name())); resetDevice(); doBlackMagic(); setControlLines(mControlLinesValue); @@ -563,6 +567,7 @@ public class ProlificSerialDriver implements UsbSerialDriver { } } + @SuppressWarnings({"unused"}) public static Map getSupportedDevices() { final Map supportedDevices = new LinkedHashMap<>(); supportedDevices.put(UsbId.VENDOR_PROLIFIC, @@ -571,7 +576,6 @@ public class ProlificSerialDriver implements UsbSerialDriver { UsbId.PROLIFIC_PL2303GC, UsbId.PROLIFIC_PL2303GB, UsbId.PROLIFIC_PL2303GT, - UsbId.PROLIFIC_PL2303GT3, UsbId.PROLIFIC_PL2303GL, UsbId.PROLIFIC_PL2303GE, UsbId.PROLIFIC_PL2303GS, diff --git a/app/src/main/java/org/emulator/calculator/usbserial/driver/SerialTimeoutException.java b/app/src/main/java/org/emulator/calculator/usbserial/driver/SerialTimeoutException.java index c45ebac..41bae99 100644 --- a/app/src/main/java/org/emulator/calculator/usbserial/driver/SerialTimeoutException.java +++ b/app/src/main/java/org/emulator/calculator/usbserial/driver/SerialTimeoutException.java @@ -9,7 +9,8 @@ import java.io.InterruptedIOException; * {@see InterruptedIOException#bytesTransferred} may contain bytes transferred */ public class SerialTimeoutException extends InterruptedIOException { - public SerialTimeoutException(String s) { + public SerialTimeoutException(String s, int bytesTransferred) { super(s); + this.bytesTransferred = bytesTransferred; } } diff --git a/app/src/main/java/org/emulator/calculator/usbserial/driver/UsbId.java b/app/src/main/java/org/emulator/calculator/usbserial/driver/UsbId.java index b94206f..755f008 100644 --- a/app/src/main/java/org/emulator/calculator/usbserial/driver/UsbId.java +++ b/app/src/main/java/org/emulator/calculator/usbserial/driver/UsbId.java @@ -23,27 +23,6 @@ public final class UsbId { public static final int FTDI_FT232H = 0x6014; public static final int FTDI_FT231X = 0x6015; // same ID for FT230X, FT231X, FT234XD - public static final int VENDOR_ATMEL = 0x03EB; - public static final int ATMEL_LUFA_CDC_DEMO_APP = 0x2044; - - public static final int VENDOR_ARDUINO = 0x2341; - public static final int ARDUINO_UNO = 0x0001; - public static final int ARDUINO_MEGA_2560 = 0x0010; - public static final int ARDUINO_SERIAL_ADAPTER = 0x003b; - public static final int ARDUINO_MEGA_ADK = 0x003f; - public static final int ARDUINO_MEGA_2560_R3 = 0x0042; - public static final int ARDUINO_UNO_R3 = 0x0043; - public static final int ARDUINO_MEGA_ADK_R3 = 0x0044; - public static final int ARDUINO_SERIAL_ADAPTER_R3 = 0x0044; - public static final int ARDUINO_LEONARDO = 0x8036; - public static final int ARDUINO_MICRO = 0x8037; - - public static final int VENDOR_VAN_OOIJEN_TECH = 0x16c0; - public static final int VAN_OOIJEN_TECH_TEENSYDUINO_SERIAL = 0x0483; - - public static final int VENDOR_LEAFLABS = 0x1eaf; - public static final int LEAFLABS_MAPLE = 0x0004; - public static final int VENDOR_SILABS = 0x10c4; public static final int SILABS_CP2102 = 0xea60; // same ID for CP2101, CP2103, CP2104, CP2109 public static final int SILABS_CP2105 = 0xea70; @@ -53,22 +32,22 @@ public final class UsbId { public static final int PROLIFIC_PL2303 = 0x2303; // device type 01, T, HX public static final int PROLIFIC_PL2303GC = 0x23a3; // device type HXN public static final int PROLIFIC_PL2303GB = 0x23b3; // " - public static final int PROLIFIC_PL2303GT = 0x23cd; // " - public static final int PROLIFIC_PL2303GT3 = 0x23c3; // " - public static final int PROLIFIC_PL2303GL = 0x23e3; // " + public static final int PROLIFIC_PL2303GT = 0x23c3; // " + public static final int PROLIFIC_PL2303GL = 0x23d3; // " public static final int PROLIFIC_PL2303GE = 0x23e3; // " public static final int PROLIFIC_PL2303GS = 0x23f3; // " + public static final int VENDOR_GOOGLE = 0x18d1; + public static final int GOOGLE_CR50 = 0x5014; + public static final int VENDOR_QINHENG = 0x1a86; public static final int QINHENG_CH340 = 0x7523; public static final int QINHENG_CH341A = 0x5523; - // at www.linux-usb.org/usb.ids listed for NXP/LPC1768, but all processors supported by ARM mbed DAPLink firmware report these ids - public static final int VENDOR_ARM = 0x0d28; - public static final int ARM_MBED = 0x0204; + public static final int VENDOR_UNISOC = 0x1782; + public static final int FIBOCOM_L610 = 0x4D10; + public static final int FIBOCOM_L612 = 0x4D12; - public static final int VENDOR_ST = 0x0483; - public static final int ST_CDC = 0x5740; private UsbId() { throw new IllegalAccessError("Non-instantiable class"); diff --git a/app/src/main/java/org/emulator/calculator/usbserial/driver/UsbSerialDriver.java b/app/src/main/java/org/emulator/calculator/usbserial/driver/UsbSerialDriver.java index bbc761a..df8912f 100644 --- a/app/src/main/java/org/emulator/calculator/usbserial/driver/UsbSerialDriver.java +++ b/app/src/main/java/org/emulator/calculator/usbserial/driver/UsbSerialDriver.java @@ -10,12 +10,17 @@ import android.hardware.usb.UsbDevice; import java.util.List; -/** - * - * @author mike wakerly (opensource@hoho.com) - */ public interface UsbSerialDriver { + /* + * Additional interface properties. Invoked thru reflection. + * + UsbSerialDriver(UsbDevice device); // constructor with device + static Map getSupportedDevices(); + static boolean probe(UsbDevice device); // optional + */ + + /** * Returns the raw {@link UsbDevice} backing this port. * diff --git a/app/src/main/java/org/emulator/calculator/usbserial/driver/UsbSerialPort.java b/app/src/main/java/org/emulator/calculator/usbserial/driver/UsbSerialPort.java index 03f1e6b..c162ac8 100644 --- a/app/src/main/java/org/emulator/calculator/usbserial/driver/UsbSerialPort.java +++ b/app/src/main/java/org/emulator/calculator/usbserial/driver/UsbSerialPort.java @@ -58,7 +58,7 @@ public interface UsbSerialPort extends Closeable { int STOPBITS_2 = 2; /** Values for get[Supported]ControlLines() */ - enum ControlLine { RTS, CTS, DTR, DSR, CD, RI } + enum ControlLine { RTS, CTS, DTR, DSR, CD, RI } /** * Returns the driver used by this port. @@ -122,6 +122,17 @@ public interface UsbSerialPort extends Closeable { */ int read(final byte[] dest, final int timeout) throws IOException; + /** + * Reads bytes with specified length into the destination buffer. + * + * @param dest the destination byte buffer + * @param length the maximum length of the data to read + * @param timeout the timeout for reading in milliseconds, 0 is infinite + * @return the actual number of bytes read + * @throws IOException if an error occurred during reading + */ + int read(final byte[] dest, int length, final int timeout) throws IOException; + /** * Writes as many bytes as possible from the source buffer. * @@ -133,6 +144,18 @@ public interface UsbSerialPort extends Closeable { */ void write(final byte[] src, final int timeout) throws IOException; + /** + * Writes bytes with specified length from the source buffer. + * + * @param src the source byte buffer + * @param length the length of the data to write + * @param timeout the timeout for writing in milliseconds, 0 is infinite + * @throws SerialTimeoutException if timeout reached before sending all data. + * ex.bytesTransferred may contain bytes transferred + * @throws IOException if an error occurred during writing + */ + void write(final byte[] src, int length, final int timeout) throws IOException; + /** * Sets various serial port parameters. * diff --git a/app/src/main/java/org/emulator/calculator/usbserial/driver/UsbSerialProber.java b/app/src/main/java/org/emulator/calculator/usbserial/driver/UsbSerialProber.java index 6755bb0..13c8f4b 100644 --- a/app/src/main/java/org/emulator/calculator/usbserial/driver/UsbSerialProber.java +++ b/app/src/main/java/org/emulator/calculator/usbserial/driver/UsbSerialProber.java @@ -37,6 +37,8 @@ public class UsbSerialProber { probeTable.addDriver(FtdiSerialDriver.class); probeTable.addDriver(ProlificSerialDriver.class); probeTable.addDriver(Ch34xSerialDriver.class); + probeTable.addDriver(GsmModemSerialDriver.class); + probeTable.addDriver(ChromeCcdSerialDriver.class); return probeTable; } @@ -69,11 +71,7 @@ public class UsbSerialProber { * {@code null} if none available. */ public UsbSerialDriver probeDevice(final UsbDevice usbDevice) { - final int vendorId = usbDevice.getVendorId(); - final int productId = usbDevice.getProductId(); - - final Class driverClass = - mProbeTable.findDriver(vendorId, productId); + final Class driverClass = mProbeTable.findDriver(usbDevice); if (driverClass != null) { final UsbSerialDriver driver; try { diff --git a/app/src/main/java/org/emulator/calculator/usbserial/util/HexDump.java b/app/src/main/java/org/emulator/calculator/usbserial/util/HexDump.java new file mode 100644 index 0000000..42a1eeb --- /dev/null +++ b/app/src/main/java/org/emulator/calculator/usbserial/util/HexDump.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.emulator.calculator.usbserial.util; + +import java.security.InvalidParameterException; + +/** + * Clone of Android's /core/java/com/android/internal/util/HexDump class, for use in debugging. + * Changes: space separated hex strings + */ +public class HexDump { + private final static char[] HEX_DIGITS = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + + private HexDump() { + } + + public static String dumpHexString(byte[] array) { + return dumpHexString(array, 0, array.length); + } + + public static String dumpHexString(byte[] array, int offset, int length) { + StringBuilder result = new StringBuilder(); + + byte[] line = new byte[8]; + int lineIndex = 0; + + for (int i = offset; i < offset + length; i++) { + if (lineIndex == line.length) { + for (int j = 0; j < line.length; j++) { + if (line[j] > ' ' && line[j] < '~') { + result.append(new String(line, j, 1)); + } else { + result.append("."); + } + } + + result.append("\n"); + lineIndex = 0; + } + + byte b = array[i]; + result.append(HEX_DIGITS[(b >>> 4) & 0x0F]); + result.append(HEX_DIGITS[b & 0x0F]); + result.append(" "); + + line[lineIndex++] = b; + } + + for (int i = 0; i < (line.length - lineIndex); i++) { + result.append(" "); + } + for (int i = 0; i < lineIndex; i++) { + if (line[i] > ' ' && line[i] < '~') { + result.append(new String(line, i, 1)); + } else { + result.append("."); + } + } + + return result.toString(); + } + + public static String toHexString(byte b) { + return toHexString(toByteArray(b)); + } + + public static String toHexString(byte[] array) { + return toHexString(array, 0, array.length); + } + + public static String toHexString(byte[] array, int offset, int length) { + char[] buf = new char[length > 0 ? length * 3 - 1 : 0]; + + int bufIndex = 0; + for (int i = offset; i < offset + length; i++) { + if (i > offset) + buf[bufIndex++] = ' '; + byte b = array[i]; + buf[bufIndex++] = HEX_DIGITS[(b >>> 4) & 0x0F]; + buf[bufIndex++] = HEX_DIGITS[b & 0x0F]; + } + + return new String(buf); + } + + public static String toHexString(int i) { + return toHexString(toByteArray(i)); + } + + public static String toHexString(short i) { + return toHexString(toByteArray(i)); + } + + public static byte[] toByteArray(byte b) { + byte[] array = new byte[1]; + array[0] = b; + return array; + } + + public static byte[] toByteArray(int i) { + byte[] array = new byte[4]; + + array[3] = (byte) (i & 0xFF); + array[2] = (byte) ((i >> 8) & 0xFF); + array[1] = (byte) ((i >> 16) & 0xFF); + array[0] = (byte) ((i >> 24) & 0xFF); + + return array; + } + + public static byte[] toByteArray(short i) { + byte[] array = new byte[2]; + + array[1] = (byte) (i & 0xFF); + array[0] = (byte) ((i >> 8) & 0xFF); + + return array; + } + + private static int toByte(char c) { + if (c >= '0' && c <= '9') + return (c - '0'); + if (c >= 'A' && c <= 'F') + return (c - 'A' + 10); + if (c >= 'a' && c <= 'f') + return (c - 'a' + 10); + + throw new InvalidParameterException("Invalid hex char '" + c + "'"); + } + + /** accepts any separator, e.g. space or newline */ + public static byte[] hexStringToByteArray(String hexString) { + int length = hexString.length(); + byte[] buffer = new byte[(length + 1) / 3]; + + for (int i = 0; i < length; i += 3) { + buffer[i / 3] = (byte) ((toByte(hexString.charAt(i)) << 4) | toByte(hexString.charAt(i + 1))); + } + + return buffer; + } +} diff --git a/app/src/main/java/org/emulator/calculator/usbserial/util/SerialInputOutputManager.java b/app/src/main/java/org/emulator/calculator/usbserial/util/SerialInputOutputManager.java index 1bb9cf3..f37dfbe 100644 --- a/app/src/main/java/org/emulator/calculator/usbserial/util/SerialInputOutputManager.java +++ b/app/src/main/java/org/emulator/calculator/usbserial/util/SerialInputOutputManager.java @@ -9,6 +9,7 @@ package org.emulator.calculator.usbserial.util; import android.os.Process; import android.util.Log; +import org.emulator.calculator.usbserial.driver.SerialTimeoutException; import org.emulator.calculator.usbserial.driver.UsbSerialPort; import java.io.IOException; @@ -21,8 +22,15 @@ import java.nio.ByteBuffer; */ public class SerialInputOutputManager implements Runnable { - private static final String TAG = SerialInputOutputManager.class.getSimpleName(); + public enum State { + STOPPED, + RUNNING, + STOPPING + } + public static boolean DEBUG = false; + + private static final String TAG = SerialInputOutputManager.class.getSimpleName(); private static final int BUFSIZ = 4096; /** @@ -37,12 +45,6 @@ public class SerialInputOutputManager implements Runnable { private ByteBuffer mReadBuffer; // default size = getReadEndpoint().getMaxPacketSize() private ByteBuffer mWriteBuffer = ByteBuffer.allocate(BUFSIZ); - public enum State { - STOPPED, - RUNNING, - STOPPING - } - private int mThreadPriority = Process.THREAD_PRIORITY_URGENT_AUDIO; private State mState = State.STOPPED; // Synchronized by 'this' private Listener mListener; // Synchronized by 'this' @@ -202,7 +204,11 @@ public class SerialInputOutputManager implements Runnable { step(); } } catch (Exception e) { - Log.w(TAG, "Run ending due to exception: " + e.getMessage(), e); + if(mSerialPort.isOpen()) { + Log.w(TAG, "Run ending due to exception: " + e.getMessage(), e); + } else { + Log.i(TAG, "Socket closed"); + } final Listener listener = getListener(); if (listener != null) { listener.onRunError(e); @@ -223,7 +229,9 @@ public class SerialInputOutputManager implements Runnable { } int len = mSerialPort.read(buffer, mReadTimeout); if (len > 0) { - if (DEBUG) Log.d(TAG, "Read data len=" + len); + if (DEBUG) { + Log.d(TAG, "Read data len=" + len); + } final Listener listener = getListener(); if (listener != null) { final byte[] data = new byte[len]; @@ -247,7 +255,23 @@ public class SerialInputOutputManager implements Runnable { if (DEBUG) { Log.d(TAG, "Writing data len=" + len); } - mSerialPort.write(buffer, mWriteTimeout); + try { + mSerialPort.write(buffer, mWriteTimeout); + } catch (SerialTimeoutException ex) { + synchronized (mWriteBufferLock) { + byte[] buffer2 = null; + int len2 = mWriteBuffer.position(); + if (len2 > 0) { + buffer2 = new byte[len2]; + mWriteBuffer.rewind(); + mWriteBuffer.get(buffer2, 0, len2); + mWriteBuffer.clear(); + } + mWriteBuffer.put(buffer, ex.bytesTransferred, buffer.length - ex.bytesTransferred); + if (buffer2 != null) + mWriteBuffer.put(buffer2); + } + } } } diff --git a/app/src/main/java/org/emulator/calculator/usbserial/util/UsbUtils.java b/app/src/main/java/org/emulator/calculator/usbserial/util/UsbUtils.java new file mode 100644 index 0000000..69aae75 --- /dev/null +++ b/app/src/main/java/org/emulator/calculator/usbserial/util/UsbUtils.java @@ -0,0 +1,33 @@ +package org.emulator.calculator.usbserial.util; + +import android.hardware.usb.UsbDeviceConnection; + +import java.util.ArrayList; + +public class UsbUtils { + + private UsbUtils() { + } + + public static ArrayList getDescriptors(UsbDeviceConnection connection) { + ArrayList descriptors = new ArrayList<>(); + byte[] rawDescriptors = connection.getRawDescriptors(); + if (rawDescriptors != null) { + int pos = 0; + while (pos < rawDescriptors.length) { + int len = rawDescriptors[pos] & 0xFF; + if (len == 0) + break; + if (pos + len > rawDescriptors.length) + len = rawDescriptors.length - pos; + byte[] descriptor = new byte[len]; + System.arraycopy(rawDescriptors, pos, descriptor, 0, len); + descriptors.add(descriptor); + pos += len; + } + } + return descriptors; + } + + +} diff --git a/gradle.properties b/gradle.properties index 0471563..70517d2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -#android.defaults.buildfeatures.buildconfig=true +android.defaults.buildfeatures.buildconfig=true android.enableJetifier=true android.nonFinalResIds=true android.nonTransitiveRClass=true