- Update the usb serial drivers usb-serial-for-android to version 3.7.3

This commit is contained in:
dgis 2024-06-16 19:57:05 +02:00
parent e847c0d120
commit 8025fe107d
21 changed files with 1059 additions and 511 deletions

View file

@ -58,6 +58,11 @@ LINKS
CHANGES 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) Version 2.7 (2024-06-14)
- Updated source code with Emu48 version 1.65+. This new version improve the serial communication. - Updated source code with Emu48 version 1.65+. This new version improve the serial communication.

View file

@ -34,8 +34,8 @@ android {
applicationId "org.emulator.forty.eight" applicationId "org.emulator.forty.eight"
minSdk 21 minSdk 21
targetSdk 34 targetSdk 34
versionCode 26 versionCode 27
versionName "2.7" versionName "2.8"
setProperty("archivesBaseName", "Emu48-v$versionName") setProperty("archivesBaseName", "Emu48-v$versionName")
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild { externalNativeBuild {

View file

@ -58,6 +58,11 @@ LINKS
CHANGES 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) Version 2.7 (2024-06-14)
- Updated source code with Emu48 version 1.65+. This new version improve the serial communication. - Updated source code with Emu48 version 1.65+. This new version improve the serial communication.

View file

@ -8,11 +8,13 @@ package org.emulator.calculator.usbserial.driver;
import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint; import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface; import android.hardware.usb.UsbInterface;
import android.util.Log; import android.util.Log;
import org.emulator.calculator.usbserial.util.HexDump;
import org.emulator.calculator.usbserial.util.UsbUtils;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.EnumSet; import java.util.EnumSet;
@ -30,6 +32,8 @@ import java.util.Map;
*/ */
public class CdcAcmSerialDriver implements UsbSerialDriver { public class CdcAcmSerialDriver implements UsbSerialDriver {
public static final int USB_SUBCLASS_ACM = 2;
private final String TAG = CdcAcmSerialDriver.class.getSimpleName(); private final String TAG = CdcAcmSerialDriver.class.getSimpleName();
private final UsbDevice mDevice; private final UsbDevice mDevice;
@ -38,23 +42,33 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
public CdcAcmSerialDriver(UsbDevice device) { public CdcAcmSerialDriver(UsbDevice device) {
mDevice = device; mDevice = device;
mPorts = new ArrayList<>(); mPorts = new ArrayList<>();
int ports = countPorts(device);
int controlInterfaceCount = 0; for (int port = 0; port < ports; port++) {
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++) {
mPorts.add(new CdcAcmSerialPort(mDevice, port)); mPorts.add(new CdcAcmSerialPort(mDevice, port));
} }
if(mPorts.size() == 0) { if (mPorts.size() == 0) {
mPorts.add(new CdcAcmSerialPort(mDevice, -1)); 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 @Override
public UsbDevice getDevice() { public UsbDevice getDevice() {
return mDevice; return mDevice;
@ -95,7 +109,11 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
} }
@Override @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) { if (mPortNumber == -1) {
Log.d(TAG,"device might be castrated ACM device, trying single interface logic"); Log.d(TAG,"device might be castrated ACM device, trying single interface logic");
openSingleInterface(); openSingleInterface();
@ -131,38 +149,57 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
} }
private void openInterface() throws IOException { private void openInterface() throws IOException {
Log.d(TAG, "claiming interfaces, count=" + mDevice.getInterfaceCount());
int controlInterfaceCount = 0;
int dataInterfaceCount = 0;
mControlInterface = null; mControlInterface = null;
mDataInterface = null; mDataInterface = null;
for (int i = 0; i < mDevice.getInterfaceCount(); i++) { int j = getInterfaceIdFromDescriptors();
UsbInterface usbInterface = mDevice.getInterface(i); Log.d(TAG, "interface count=" + mDevice.getInterfaceCount() + ", IAD=" + j);
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_COMM) { if (j >= 0) {
if(controlInterfaceCount == mPortNumber) { for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
mControlIndex = i; UsbInterface usbInterface = mDevice.getInterface(i);
mControlInterface = usbInterface; 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) { if (mControlInterface == null || mDataInterface == null) {
mDataInterface = usbInterface; 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) { if(mControlInterface == null) {
throw new IOException("No control interface"); 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)) { if (!mConnection.claimInterface(mControlInterface, true)) {
throw new IOException("Could not claim control interface"); throw new IOException("Could not claim control interface");
} }
mControlEndpoint = mControlInterface.getEndpoint(0); mControlEndpoint = mControlInterface.getEndpoint(0);
if (mControlEndpoint.getDirection() != UsbConstants.USB_DIR_IN || mControlEndpoint.getType() != UsbConstants.USB_ENDPOINT_XFER_INT) { if (mControlEndpoint.getDirection() != UsbConstants.USB_DIR_IN || mControlEndpoint.getType() != UsbConstants.USB_ENDPOINT_XFER_INT) {
throw new IOException("Invalid control endpoint"); throw new IOException("Invalid control endpoint");
@ -171,12 +208,10 @@ public class CdcAcmSerialDriver implements UsbSerialDriver {
if(mDataInterface == null) { if(mDataInterface == null) {
throw new IOException("No data interface"); 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)) { if (!mConnection.claimInterface(mDataInterface, true)) {
throw new IOException("Could not claim data interface"); throw new IOException("Could not claim data interface");
} }
for (int i = 0; i < mDataInterface.getEndpointCount(); i++) { for (int i = 0; i < mDataInterface.getEndpointCount(); i++) {
UsbEndpoint ep = mDataInterface.getEndpoint(i); UsbEndpoint ep = mDataInterface.getEndpoint(i);
if (ep.getDirection() == UsbConstants.USB_DIR_IN && ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) 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<byte[]> 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 { private int sendAcmControlMessage(int request, int value, byte[] buf) throws IOException {
int len = mConnection.controlTransfer( int len = mConnection.controlTransfer(
USB_RT_ACM, request, value, mControlIndex, buf, buf != null ? buf.length : 0, 5000); 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<Integer, int[]> getSupportedDevices() { public static Map<Integer, int[]> getSupportedDevices() {
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<>(); return 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;
} }
} }

View file

@ -7,9 +7,11 @@ package org.emulator.calculator.usbserial.driver;
import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint; import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface; import android.hardware.usb.UsbInterface;
import android.util.Log;
//import org.emulator.calculator.usbserial.BuildConfig;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;
@ -20,355 +22,366 @@ import java.util.Map;
public class Ch34xSerialDriver implements UsbSerialDriver { 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 UsbDevice mDevice;
private final UsbSerialPort mPort; private final UsbSerialPort mPort;
private static final int LCR_ENABLE_RX = 0x80; private static final int LCR_ENABLE_RX = 0x80;
private static final int LCR_ENABLE_TX = 0x40; private static final int LCR_ENABLE_TX = 0x40;
private static final int LCR_MARK_SPACE = 0x20; private static final int LCR_MARK_SPACE = 0x20;
private static final int LCR_PAR_EVEN = 0x10; private static final int LCR_PAR_EVEN = 0x10;
private static final int LCR_ENABLE_PAR = 0x08; private static final int LCR_ENABLE_PAR = 0x08;
private static final int LCR_STOP_BITS_2 = 0x04; private static final int LCR_STOP_BITS_2 = 0x04;
private static final int LCR_CS8 = 0x03; private static final int LCR_CS8 = 0x03;
private static final int LCR_CS7 = 0x02; private static final int LCR_CS7 = 0x02;
private static final int LCR_CS6 = 0x01; private static final int LCR_CS6 = 0x01;
private static final int LCR_CS5 = 0x00; private static final int LCR_CS5 = 0x00;
private static final int GCL_CTS = 0x01; private static final int GCL_CTS = 0x01;
private static final int GCL_DSR = 0x02; private static final int GCL_DSR = 0x02;
private static final int GCL_RI = 0x04; private static final int GCL_RI = 0x04;
private static final int GCL_CD = 0x08; private static final int GCL_CD = 0x08;
private static final int SCL_DTR = 0x20; private static final int SCL_DTR = 0x20;
private static final int SCL_RTS = 0x40; private static final int SCL_RTS = 0x40;
public Ch34xSerialDriver(UsbDevice device) { public Ch34xSerialDriver(UsbDevice device) {
mDevice = device; mDevice = device;
mPort = new Ch340SerialPort(mDevice, 0); mPort = new Ch340SerialPort(mDevice, 0);
} }
@Override @Override
public UsbDevice getDevice() { public UsbDevice getDevice() {
return mDevice; return mDevice;
} }
@Override @Override
public List<UsbSerialPort> getPorts() { public List<UsbSerialPort> getPorts() {
return Collections.singletonList(mPort); 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 dtr = false;
private boolean rts = false; private boolean rts = false;
public Ch340SerialPort(UsbDevice device, int portNumber) { public Ch340SerialPort(UsbDevice device, int portNumber) {
super(device, portNumber); super(device, portNumber);
} }
@Override @Override
public UsbSerialDriver getDriver() { public UsbSerialDriver getDriver() {
return Ch34xSerialDriver.this; return Ch34xSerialDriver.this;
} }
@Override @Override
protected void openInt(UsbDeviceConnection connection) throws IOException { protected void openInt() throws IOException {
for (int i = 0; i < mDevice.getInterfaceCount(); i++) { for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
UsbInterface usbIface = mDevice.getInterface(i); UsbInterface usbIface = mDevice.getInterface(i);
if (!mConnection.claimInterface(usbIface, true)) { if (!mConnection.claimInterface(usbIface, true)) {
throw new IOException("Could not claim data interface"); throw new IOException("Could not claim data interface");
} }
} }
UsbInterface dataIface = mDevice.getInterface(mDevice.getInterfaceCount() - 1); UsbInterface dataIface = mDevice.getInterface(mDevice.getInterfaceCount() - 1);
for (int i = 0; i < dataIface.getEndpointCount(); i++) { for (int i = 0; i < dataIface.getEndpointCount(); i++) {
UsbEndpoint ep = dataIface.getEndpoint(i); UsbEndpoint ep = dataIface.getEndpoint(i);
if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) { if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
if (ep.getDirection() == UsbConstants.USB_DIR_IN) { if (ep.getDirection() == UsbConstants.USB_DIR_IN) {
mReadEndpoint = ep; mReadEndpoint = ep;
} else { } else {
mWriteEndpoint = ep; mWriteEndpoint = ep;
} }
} }
} }
initialize(); initialize();
setBaudRate(DEFAULT_BAUD_RATE); setBaudRate(DEFAULT_BAUD_RATE);
} }
@Override @Override
protected void closeInt() { protected void closeInt() {
try { try {
for (int i = 0; i < mDevice.getInterfaceCount(); i++) for (int i = 0; i < mDevice.getInterfaceCount(); i++)
mConnection.releaseInterface(mDevice.getInterface(i)); mConnection.releaseInterface(mDevice.getInterface(i));
} catch(Exception ignored) {} } catch(Exception ignored) {}
} }
private int controlOut(int request, int value, int index) { private int controlOut(int request, int value, int index) {
final int REQTYPE_HOST_TO_DEVICE = UsbConstants.USB_TYPE_VENDOR | UsbConstants.USB_DIR_OUT; final int REQTYPE_HOST_TO_DEVICE = UsbConstants.USB_TYPE_VENDOR | UsbConstants.USB_DIR_OUT;
return mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, request, return mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, request,
value, index, null, 0, USB_TIMEOUT_MILLIS); value, index, null, 0, USB_TIMEOUT_MILLIS);
} }
private int controlIn(int request, int value, int index, byte[] buffer) { 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; final int REQTYPE_DEVICE_TO_HOST = UsbConstants.USB_TYPE_VENDOR | UsbConstants.USB_DIR_IN;
return mConnection.controlTransfer(REQTYPE_DEVICE_TO_HOST, request, return mConnection.controlTransfer(REQTYPE_DEVICE_TO_HOST, request,
value, index, buffer, buffer.length, USB_TIMEOUT_MILLIS); value, index, buffer, buffer.length, USB_TIMEOUT_MILLIS);
} }
private void checkState(String msg, int request, int value, int[] expected) throws IOException { private void checkState(String msg, int request, int value, int[] expected) throws IOException {
byte[] buffer = new byte[expected.length]; byte[] buffer = new byte[expected.length];
int ret = controlIn(request, value, 0, buffer); int ret = controlIn(request, value, 0, buffer);
if (ret < 0) { if (ret < 0) {
throw new IOException("Failed send cmd [" + msg + "]"); throw new IOException("Failed send cmd [" + msg + "]");
} }
if (ret != expected.length) { if (ret != expected.length) {
throw new IOException("Expected " + expected.length + " bytes, but get " + ret + " [" + msg + "]"); throw new IOException("Expected " + expected.length + " bytes, but get " + ret + " [" + msg + "]");
} }
for (int i = 0; i < expected.length; i++) { for (int i = 0; i < expected.length; i++) {
if (expected[i] == -1) { if (expected[i] == -1) {
continue; continue;
} }
int current = buffer[i] & 0xff; int current = buffer[i] & 0xff;
if (expected[i] != current) { if (expected[i] != current) {
throw new IOException("Expected 0x" + Integer.toHexString(expected[i]) + " byte, but get 0x" + Integer.toHexString(current) + " [" + msg + "]"); throw new IOException("Expected 0x" + Integer.toHexString(expected[i]) + " byte, but get 0x" + Integer.toHexString(current) + " [" + msg + "]");
} }
} }
} }
private void setControlLines() throws IOException { private void setControlLines() throws IOException {
if (controlOut(0xa4, ~((dtr ? SCL_DTR : 0) | (rts ? SCL_RTS : 0)), 0) < 0) { if (controlOut(0xa4, ~((dtr ? SCL_DTR : 0) | (rts ? SCL_RTS : 0)), 0) < 0) {
throw new IOException("Failed to set control lines"); throw new IOException("Failed to set control lines");
} }
} }
private byte getStatus() throws IOException { private byte getStatus() throws IOException {
byte[] buffer = new byte[2]; byte[] buffer = new byte[2];
int ret = controlIn(0x95, 0x0706, 0, buffer); int ret = controlIn(0x95, 0x0706, 0, buffer);
if (ret < 0) if (ret < 0)
throw new IOException("Error getting control lines"); throw new IOException("Error getting control lines");
return buffer[0]; return buffer[0];
} }
private void initialize() throws IOException { private void initialize() throws IOException {
checkState("init #1", 0x5f, 0, new int[]{-1 /* 0x27, 0x30 */, 0x00}); checkState("init #1", 0x5f, 0, new int[]{-1 /* 0x27, 0x30 */, 0x00});
if (controlOut(0xa1, 0, 0) < 0) { if (controlOut(0xa1, 0, 0) < 0) {
throw new IOException("Init failed: #2"); 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) { if (controlOut(0x9a, 0x2518, LCR_ENABLE_RX | LCR_ENABLE_TX | LCR_CS8) < 0) {
throw new IOException("Init failed: #5"); 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) { if (controlOut(0xa1, 0x501f, 0xd90a) < 0) {
throw new IOException("Init failed: #7"); 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 { private void setBaudRate(int baudRate) throws IOException {
final long CH341_BAUDBASE_FACTOR = 1532620800; long factor;
final int CH341_BAUDBASE_DIVMAX = 3; long divisor;
long factor = CH341_BAUDBASE_FACTOR / baudRate; if (baudRate == 921600) {
int divisor = CH341_BAUDBASE_DIVMAX; divisor = 7;
factor = 0xf300;
} else {
final long BAUDBASE_FACTOR = 1532620800;
final int BAUDBASE_DIVMAX = 3;
while ((factor > 0xfff0) && divisor > 0) { // if(BuildConfig.DEBUG && (baudRate & (3<<29)) == (1<<29))
factor >>= 3; // baudRate &= ~(1<<29); // for testing purpose bypass dedicated baud rate handling
divisor--; 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) { divisor |= 0x0080; // else ch341a waits until buffer full
throw new UnsupportedOperationException("Unsupported baud rate: " + baudRate); 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; @Override
divisor |= 0x0080; // else ch341a waits until buffer full public void setParameters(int baudRate, int dataBits, int stopBits, @Parity int parity) throws IOException {
int ret = controlOut(0x9a, 0x1312, (int) ((factor & 0xff00) | divisor)); if(baudRate <= 0) {
if (ret < 0) { throw new IllegalArgumentException("Invalid baud rate: " + baudRate);
throw new IOException("Error setting baud rate: #1)"); }
} setBaudRate(baudRate);
ret = controlOut(0x9a, 0x0f2c, (int) (factor & 0xff)); int lcr = LCR_ENABLE_RX | LCR_ENABLE_TX;
if (ret < 0) {
throw new IOException("Error setting baud rate: #2");
}
}
@Override switch (dataBits) {
public void setParameters(int baudRate, int dataBits, int stopBits, @Parity int parity) throws IOException { case DATABITS_5:
if(baudRate <= 0) { lcr |= LCR_CS5;
throw new IllegalArgumentException("Invalid baud rate: " + baudRate); break;
} case DATABITS_6:
setBaudRate(baudRate); 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) { switch (stopBits) {
case DATABITS_5: case STOPBITS_1:
lcr |= LCR_CS5; break;
break; case STOPBITS_1_5:
case DATABITS_6: throw new UnsupportedOperationException("Unsupported stop bits: 1.5");
lcr |= LCR_CS6; case STOPBITS_2:
break; lcr |= LCR_STOP_BITS_2;
case DATABITS_7: break;
lcr |= LCR_CS7; default:
break; throw new IllegalArgumentException("Invalid stop bits: " + stopBits);
case DATABITS_8: }
lcr |= LCR_CS8;
break;
default:
throw new IllegalArgumentException("Invalid data bits: " + dataBits);
}
switch (parity) { int ret = controlOut(0x9a, 0x2518, lcr);
case PARITY_NONE: if (ret < 0) {
break; throw new IOException("Error setting control byte");
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 (stopBits) { @Override
case STOPBITS_1: public boolean getCD() throws IOException {
break; return (getStatus() & GCL_CD) == 0;
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);
}
int ret = controlOut(0x9a, 0x2518, lcr); @Override
if (ret < 0) { public boolean getCTS() throws IOException {
throw new IOException("Error setting control byte"); return (getStatus() & GCL_CTS) == 0;
} }
}
@Override @Override
public boolean getCD() throws IOException { public boolean getDSR() throws IOException {
return (getStatus() & GCL_CD) == 0; return (getStatus() & GCL_DSR) == 0;
} }
@Override @Override
public boolean getCTS() throws IOException { public boolean getDTR() throws IOException {
return (getStatus() & GCL_CTS) == 0; return dtr;
} }
@Override @Override
public boolean getDSR() throws IOException { public void setDTR(boolean value) throws IOException {
return (getStatus() & GCL_DSR) == 0; dtr = value;
} setControlLines();
}
@Override @Override
public boolean getDTR() throws IOException { public boolean getRI() throws IOException {
return dtr; return (getStatus() & GCL_RI) == 0;
} }
@Override @Override
public void setDTR(boolean value) throws IOException { public boolean getRTS() throws IOException {
dtr = value; return rts;
setControlLines(); }
}
@Override @Override
public boolean getRI() throws IOException { public void setRTS(boolean value) throws IOException {
return (getStatus() & GCL_RI) == 0; rts = value;
} setControlLines();
}
@Override @Override
public boolean getRTS() throws IOException { public EnumSet<ControlLine> getControlLines() throws IOException {
return rts; int status = getStatus();
} EnumSet<ControlLine> 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 @Override
public void setRTS(boolean value) throws IOException { public EnumSet<ControlLine> getSupportedControlLines() throws IOException {
rts = value; return EnumSet.allOf(ControlLine.class);
setControlLines(); }
}
@Override @Override
public EnumSet<ControlLine> getControlLines() throws IOException { public void setBreak(boolean value) throws IOException {
int status = getStatus(); byte[] req = new byte[2];
EnumSet<ControlLine> set = EnumSet.noneOf(ControlLine.class); if(controlIn(0x95, 0x1805, 0, req) < 0) {
if(rts) set.add(ControlLine.RTS); throw new IOException("Error getting BREAK condition");
if((status & GCL_CTS) == 0) set.add(ControlLine.CTS); }
if(dtr) set.add(ControlLine.DTR); if(value) {
if((status & GCL_DSR) == 0) set.add(ControlLine.DSR); req[0] &= ~1;
if((status & GCL_CD) == 0) set.add(ControlLine.CD); req[1] &= ~0x40;
if((status & GCL_RI) == 0) set.add(ControlLine.RI); } else {
return set; 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 @SuppressWarnings({"unused"})
public EnumSet<ControlLine> getSupportedControlLines() throws IOException { public static Map<Integer, int[]> getSupportedDevices() {
return EnumSet.allOf(ControlLine.class); final Map<Integer, int[]> supportedDevices = new LinkedHashMap<>();
} supportedDevices.put(UsbId.VENDOR_QINHENG, new int[]{
UsbId.QINHENG_CH340,
@Override UsbId.QINHENG_CH341A,
public void setBreak(boolean value) throws IOException { });
byte[] req = new byte[2]; return supportedDevices;
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<Integer, int[]> getSupportedDevices() {
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<>();
supportedDevices.put(UsbId.VENDOR_QINHENG, new int[]{
UsbId.QINHENG_CH340,
UsbId.QINHENG_CH341A,
});
return supportedDevices;
}
} }

View file

@ -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<UsbSerialPort> mPorts;
@Override
public UsbDevice getDevice() {
return mDevice;
}
@Override
public List<UsbSerialPort> getPorts() {
return mPorts;
}
public ChromeCcdSerialDriver(UsbDevice mDevice) {
this.mDevice = mDevice;
mPorts = new ArrayList<UsbSerialPort>();
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<ControlLine> getSupportedControlLines() throws IOException {
return EnumSet.noneOf(ControlLine.class);
}
}
public static Map<Integer, int[]> getSupportedDevices() {
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<>();
supportedDevices.put(UsbId.VENDOR_GOOGLE, new int[]{
UsbId.GOOGLE_CR50,
});
return supportedDevices;
}
}

View file

@ -25,28 +25,32 @@ import java.util.EnumSet;
*/ */
public abstract class CommonUsbSerialPort implements UsbSerialPort { public abstract class CommonUsbSerialPort implements UsbSerialPort {
public static boolean DEBUG = false;
private static final String TAG = CommonUsbSerialPort.class.getSimpleName(); 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 private static final int MAX_READ_SIZE = 16 * 1024; // = old bulkTransfer limit
protected final UsbDevice mDevice; protected final UsbDevice mDevice;
protected final int mPortNumber; protected final int mPortNumber;
// non-null when open() // non-null when open()
protected UsbDeviceConnection mConnection = null; protected UsbDeviceConnection mConnection;
protected UsbEndpoint mReadEndpoint; protected UsbEndpoint mReadEndpoint;
protected UsbEndpoint mWriteEndpoint; protected UsbEndpoint mWriteEndpoint;
protected UsbRequest mUsbRequest; 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 byte[] mWriteBuffer;
protected final Object mWriteBufferLock = new Object();
public CommonUsbSerialPort(UsbDevice device, int portNumber) { public CommonUsbSerialPort(UsbDevice device, int portNumber) {
mDevice = device; mDevice = device;
mPortNumber = portNumber; mPortNumber = portNumber;
mWriteBuffer = new byte[DEFAULT_WRITE_BUFFER_SIZE];
} }
@Override @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 * 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. * 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) { public final void setWriteBufferSize(int bufferSize) {
synchronized (mWriteBufferLock) { 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; return;
} }
mWriteBuffer = new byte[bufferSize]; mWriteBuffer = new byte[bufferSize];
@ -106,7 +118,7 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
} }
mConnection = connection; mConnection = connection;
try { try {
openInt(connection); openInt();
if (mReadEndpoint == null || mWriteEndpoint == null) { if (mReadEndpoint == null || mWriteEndpoint == null) {
throw new IOException("Could not get read & write endpoints"); 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 @Override
public void close() throws IOException { public void close() throws IOException {
if (mConnection == null) { if (mConnection == null) {
throw new IOException("Already closed"); throw new IOException("Already closed");
} }
try { UsbRequest usbRequest = mUsbRequest;
mUsbRequest.cancel();
} catch(Exception ignored) {}
mUsbRequest = null; mUsbRequest = null;
try {
usbRequest.cancel();
} catch(Exception ignored) {}
try { try {
closeInt(); closeInt();
} catch(Exception ignored) {} } 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 * 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]; byte[] buf = new byte[2];
int len = mConnection.controlTransfer(0x80 /*DEVICE*/, 0 /*GET_STATUS*/, 0, 0, buf, buf.length, 200); int len = mConnection.controlTransfer(0x80 /*DEVICE*/, 0 /*GET_STATUS*/, 0, 0, buf, buf.length, 200);
if(len < 0) if(len < 0)
throw new IOException("USB get_status request failed"); throw new IOException(msg);
} }
@Override @Override
public int read(final byte[] dest, final int timeout) throws IOException { 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 { @Override
if(mConnection == null) { public int read(final byte[] dest, final int length, final int timeout) throws IOException {return read(dest, length, timeout, true);}
throw new IOException("Connection closed");
} protected int read(final byte[] dest, int length, final int timeout, boolean testConnection) throws IOException {
if(dest.length <= 0) { testConnection(false);
throw new IllegalArgumentException("Read buffer to small"); if(length <= 0) {
throw new IllegalArgumentException("Read length too small");
} }
length = Math.min(length, dest.length);
final int nread; final int nread;
if (timeout != 0) { if (timeout != 0) {
// bulkTransfer will cause data loss with short timeout + high baud rates + continuous transfer // 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) // /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 // data loss / crashes were observed with timeout up to 200 msec
long endTime = testConnection ? MonotonicClock.millis() + timeout : 0; 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); nread = mConnection.bulkTransfer(mReadEndpoint, dest, readMax, timeout);
// Android error propagation is improvable: // Android error propagation is improvable:
// nread == -1 can be: timeout, connection lost, buffer to small, ??? // nread == -1 can be: timeout, connection lost, buffer to small, ???
if(nread == -1 && testConnection && MonotonicClock.millis() < endTime) if(nread == -1 && testConnection)
testConnection(); testConnection(MonotonicClock.millis() < endTime);
} else { } else {
final ByteBuffer buf = ByteBuffer.wrap(dest); final ByteBuffer buf = ByteBuffer.wrap(dest, 0, length);
if (!mUsbRequest.queue(buf, dest.length)) { if (!mUsbRequest.queue(buf, length)) {
throw new IOException("Queueing USB request failed"); throw new IOException("Queueing USB request failed");
} }
final UsbRequest response = mConnection.requestWait(); final UsbRequest response = mConnection.requestWait();
@ -195,21 +223,23 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
// Android error propagation is improvable: // Android error propagation is improvable:
// response != null & nread == 0 can be: connection lost, buffer to small, ??? // response != null & nread == 0 can be: connection lost, buffer to small, ???
if(nread == 0) { if(nread == 0) {
testConnection(); testConnection(true);
} }
} }
return Math.max(nread, 0); return Math.max(nread, 0);
} }
@Override @Override
public void write(final byte[] src, final int timeout) throws IOException { public void write(byte[] src, int timeout) throws IOException {write(src, src.length, timeout);}
int offset = 0;
final long endTime = (timeout == 0) ? 0 : (MonotonicClock.millis() + timeout);
if(mConnection == null) { @Override
throw new IOException("Connection closed"); public void write(final byte[] src, int length, final int timeout) throws IOException {
} int offset = 0;
while (offset < src.length) { long startTime = MonotonicClock.millis();
length = Math.min(length, src.length);
testConnection(false);
while (offset < length) {
int requestTimeout; int requestTimeout;
final int requestLength; final int requestLength;
final int actualLength; final int actualLength;
@ -217,7 +247,10 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
synchronized (mWriteBufferLock) { synchronized (mWriteBufferLock) {
final byte[] writeBuffer; 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) { if (offset == 0) {
writeBuffer = src; writeBuffer = src;
} else { } else {
@ -228,7 +261,7 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
if (timeout == 0 || offset == 0) { if (timeout == 0 || offset == 0) {
requestTimeout = timeout; requestTimeout = timeout;
} else { } else {
requestTimeout = (int)(endTime - MonotonicClock.millis()); requestTimeout = (int)(startTime + timeout - MonotonicClock.millis());
if(requestTimeout == 0) if(requestTimeout == 0)
requestTimeout = -1; requestTimeout = -1;
} }
@ -238,14 +271,18 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
actualLength = mConnection.bulkTransfer(mWriteEndpoint, writeBuffer, requestLength, requestTimeout); 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 (actualLength <= 0) {
if (timeout != 0 && MonotonicClock.millis() >= endTime) { String msg = "Error writing " + requestLength + " bytes at offset " + offset + " of total " + src.length + " after " + elapsed + "msec, rc=" + actualLength;
SerialTimeoutException ex = new SerialTimeoutException("Error writing " + requestLength + " bytes at offset " + offset + " of total " + src.length + ", rc=" + actualLength); if (timeout != 0) {
ex.bytesTransferred = offset; testConnection(elapsed < timeout, msg);
throw ex; throw new SerialTimeoutException(msg, offset);
} else { } else {
throw new IOException("Error writing " + requestLength + " bytes at offset " + offset + " of total " + src.length); throw new IOException(msg);
} }
} }
offset += actualLength; offset += actualLength;
@ -254,7 +291,7 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
@Override @Override
public boolean isOpen() { public boolean isOpen() {
return mConnection != null; return mUsbRequest != null;
} }
@Override @Override
@ -285,7 +322,7 @@ public abstract class CommonUsbSerialPort implements UsbSerialPort {
public void setRTS(boolean value) throws IOException { throw new UnsupportedOperationException(); } public void setRTS(boolean value) throws IOException { throw new UnsupportedOperationException(); }
@Override @Override
public abstract EnumSet<ControlLine> getControlLines() throws IOException; public EnumSet<ControlLine> getControlLines() throws IOException { throw new UnsupportedOperationException(); }
@Override @Override
public abstract EnumSet<ControlLine> getSupportedControlLines() throws IOException; public abstract EnumSet<ControlLine> getSupportedControlLines() throws IOException;

View file

@ -8,7 +8,6 @@ package org.emulator.calculator.usbserial.driver;
import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint; import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface; import android.hardware.usb.UsbInterface;
@ -119,14 +118,14 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
byte[] buffer = new byte[1]; byte[] buffer = new byte[1];
int result = mConnection.controlTransfer(REQTYPE_DEVICE_TO_HOST, SILABSER_GET_MDMSTS_REQUEST_CODE, 0, int result = mConnection.controlTransfer(REQTYPE_DEVICE_TO_HOST, SILABSER_GET_MDMSTS_REQUEST_CODE, 0,
mPortNumber, buffer, buffer.length, USB_WRITE_TIMEOUT_MILLIS); 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); throw new IOException("Control transfer failed: " + SILABSER_GET_MDMSTS_REQUEST_CODE + " / " + 0 + " -> " + result);
} }
return buffer[0]; return buffer[0];
} }
@Override @Override
protected void openInt(UsbDeviceConnection connection) throws IOException { protected void openInt() throws IOException {
mIsRestrictedPort = mDevice.getInterfaceCount() == 2 && mPortNumber == 1; mIsRestrictedPort = mDevice.getInterfaceCount() == 2 && mPortNumber == 1;
if(mPortNumber >= mDevice.getInterfaceCount()) { if(mPortNumber >= mDevice.getInterfaceCount()) {
throw new IOException("Unknown port number"); throw new IOException("Unknown port number");
@ -321,6 +320,7 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
} }
} }
@SuppressWarnings({"unused"})
public static Map<Integer, int[]> getSupportedDevices() { public static Map<Integer, int[]> getSupportedDevices() {
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<>(); final Map<Integer, int[]> supportedDevices = new LinkedHashMap<>();
supportedDevices.put(UsbId.VENDOR_SILABS, supportedDevices.put(UsbId.VENDOR_SILABS,

View file

@ -9,7 +9,6 @@ package org.emulator.calculator.usbserial.driver;
import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.util.Log; import android.util.Log;
import org.emulator.calculator.usbserial.util.MonotonicClock; import org.emulator.calculator.usbserial.util.MonotonicClock;
@ -99,8 +98,8 @@ public class FtdiSerialDriver implements UsbSerialDriver {
@Override @Override
protected void openInt(UsbDeviceConnection connection) throws IOException { protected void openInt() throws IOException {
if (!connection.claimInterface(mDevice.getInterface(mPortNumber), true)) { if (!mConnection.claimInterface(mDevice.getInterface(mPortNumber), true)) {
throw new IOException("Could not claim interface " + mPortNumber); throw new IOException("Could not claim interface " + mPortNumber);
} }
if (mDevice.getInterface(mPortNumber).getEndpointCount() < 2) { if (mDevice.getInterface(mPortNumber).getEndpointCount() < 2) {
@ -123,12 +122,13 @@ public class FtdiSerialDriver implements UsbSerialDriver {
} }
// mDevice.getVersion() would require API 23 // mDevice.getVersion() would require API 23
byte[] rawDescriptors = connection.getRawDescriptors(); byte[] rawDescriptors = mConnection.getRawDescriptors();
if(rawDescriptors == null || rawDescriptors.length < 14) { if(rawDescriptors == null || rawDescriptors.length < 14) {
throw new IOException("Could not get device descriptors"); throw new IOException("Could not get device descriptors");
} }
int deviceType = rawDescriptors[13]; 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 @Override
@ -139,24 +139,37 @@ public class FtdiSerialDriver implements UsbSerialDriver {
} }
@Override @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) { 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 // 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 // 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. // 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; int nread;
if (timeout != 0) { if (timeout != 0) {
long endTime = MonotonicClock.millis() + timeout; long endTime = MonotonicClock.millis() + timeout;
do { 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); } while (nread == READ_HEADER_LENGTH && MonotonicClock.millis() < endTime);
if(nread <= 0 && MonotonicClock.millis() < endTime) if(nread <= 0)
testConnection(); testConnection(MonotonicClock.millis() < endTime);
} else { } else {
do { do {
nread = super.read(dest, timeout, false); nread = super.read(dest, length, timeout);
} while (nread == READ_HEADER_LENGTH); } while (nread == READ_HEADER_LENGTH);
} }
return readFilter(dest, nread); return readFilter(dest, nread);
@ -290,7 +303,7 @@ public class FtdiSerialDriver implements UsbSerialDriver {
byte[] data = new byte[2]; byte[] data = new byte[2];
int result = mConnection.controlTransfer(REQTYPE_DEVICE_TO_HOST, GET_MODEM_STATUS_REQUEST, int result = mConnection.controlTransfer(REQTYPE_DEVICE_TO_HOST, GET_MODEM_STATUS_REQUEST,
0, mPortNumber+1, data, data.length, USB_WRITE_TIMEOUT_MILLIS); 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); throw new IOException("Get modem status failed: result=" + result);
} }
return data[0]; return data[0];
@ -406,7 +419,7 @@ public class FtdiSerialDriver implements UsbSerialDriver {
byte[] data = new byte[1]; byte[] data = new byte[1];
int result = mConnection.controlTransfer(REQTYPE_DEVICE_TO_HOST, GET_LATENCY_TIMER_REQUEST, int result = mConnection.controlTransfer(REQTYPE_DEVICE_TO_HOST, GET_LATENCY_TIMER_REQUEST,
0, mPortNumber+1, data, data.length, USB_WRITE_TIMEOUT_MILLIS); 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); throw new IOException("Get latency timer failed: result=" + result);
} }
return data[0]; return data[0];
@ -414,6 +427,7 @@ public class FtdiSerialDriver implements UsbSerialDriver {
} }
@SuppressWarnings({"unused"})
public static Map<Integer, int[]> getSupportedDevices() { public static Map<Integer, int[]> getSupportedDevices() {
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<>(); final Map<Integer, int[]> supportedDevices = new LinkedHashMap<>();
supportedDevices.put(UsbId.VENDOR_FTDI, supportedDevices.put(UsbId.VENDOR_FTDI,

View file

@ -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<UsbSerialPort> 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<ControlLine> getSupportedControlLines() throws IOException {
return EnumSet.noneOf(ControlLine.class);
}
}
public static Map<Integer, int[]> getSupportedDevices() {
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<>();
supportedDevices.put(UsbId.VENDOR_UNISOC, new int[]{
UsbId.FIBOCOM_L610,
UsbId.FIBOCOM_L612,
});
return supportedDevices;
}
}

View file

@ -6,6 +6,7 @@
package org.emulator.calculator.usbserial.driver; package org.emulator.calculator.usbserial.driver;
import android.hardware.usb.UsbDevice;
import android.util.Pair; import android.util.Pair;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
@ -14,14 +15,14 @@ import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
/** /**
* Maps (vendor id, product id) pairs to the corresponding serial driver. * Maps (vendor id, product id) pairs to the corresponding serial driver,
* * or invoke 'probe' method to check actual USB devices for matching interfaces.
* @author mike wakerly (opensource@hoho.com)
*/ */
public class ProbeTable { public class ProbeTable {
private final Map<Pair<Integer, Integer>, Class<? extends UsbSerialDriver>> mProbeTable = private final Map<Pair<Integer, Integer>, Class<? extends UsbSerialDriver>> mVidPidProbeTable =
new LinkedHashMap<>(); new LinkedHashMap<>();
private final Map<Method, Class<? extends UsbSerialDriver>> mMethodProbeTable = new LinkedHashMap<>();
/** /**
* Adds or updates a (vendor, product) pair in the table. * Adds or updates a (vendor, product) pair in the table.
@ -33,7 +34,7 @@ public class ProbeTable {
*/ */
public ProbeTable addProduct(int vendorId, int productId, public ProbeTable addProduct(int vendorId, int productId,
Class<? extends UsbSerialDriver> driverClass) { Class<? extends UsbSerialDriver> driverClass) {
mProbeTable.put(Pair.create(vendorId, productId), driverClass); mVidPidProbeTable.put(Pair.create(vendorId, productId), driverClass);
return this; return this;
} }
@ -41,12 +42,11 @@ public class ProbeTable {
* Internal method to add all supported products from * Internal method to add all supported products from
* {@code getSupportedProducts} static method. * {@code getSupportedProducts} static method.
* *
* @param driverClass * @param driverClass to be added
* @return
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
ProbeTable addDriver(Class<? extends UsbSerialDriver> driverClass) { void addDriver(Class<? extends UsbSerialDriver> driverClass) {
final Method method; Method method;
try { try {
method = driverClass.getMethod("getSupportedDevices"); 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} * Returns the driver for the given USB device, or {@code null} if no match.
* if no match.
* *
* @param vendorId the USB vendor id * @param usbDevice the USB device to be probed
* @param productId the USB product id
* @return the driver class matching this pair, or {@code null} * @return the driver class matching this pair, or {@code null}
*/ */
public Class<? extends UsbSerialDriver> findDriver(int vendorId, int productId) { public Class<? extends UsbSerialDriver> findDriver(final UsbDevice usbDevice) {
final Pair<Integer, Integer> pair = Pair.create(vendorId, productId); final Pair<Integer, Integer> pair = Pair.create(usbDevice.getVendorId(), usbDevice.getProductId());
return mProbeTable.get(pair); Class<? extends UsbSerialDriver> driverClass = mVidPidProbeTable.get(pair);
if (driverClass != null)
return driverClass;
for (Map.Entry<Method, Class<? extends UsbSerialDriver>> 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;
} }
} }

View file

@ -11,7 +11,6 @@ package org.emulator.calculator.usbserial.driver;
import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint; import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface; import android.hardware.usb.UsbInterface;
import android.util.Log; import android.util.Log;
@ -35,7 +34,7 @@ public class ProlificSerialDriver implements UsbSerialDriver {
28800, 38400, 57600, 115200, 128000, 134400, 161280, 201600, 230400, 268800, 28800, 38400, 57600, 115200, 128000, 134400, 161280, 201600, 230400, 268800,
403200, 460800, 614400, 806400, 921600, 1228800, 2457600, 3000000, 6000000 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 UsbDevice mDevice;
private final UsbSerialPort mPort; private final UsbSerialPort mPort;
@ -123,7 +122,7 @@ public class ProlificSerialDriver implements UsbSerialDriver {
private volatile Thread mReadStatusThread = null; private volatile Thread mReadStatusThread = null;
private final Object mReadStatusThreadLock = new Object(); private final Object mReadStatusThreadLock = new Object();
private boolean mStopReadStatusThread = false; private boolean mStopReadStatusThread = false;
private IOException mReadStatusException = null; private Exception mReadStatusException = null;
public ProlificSerialPort(UsbDevice device, int portNumber) { public ProlificSerialPort(UsbDevice device, int portNumber) {
@ -139,7 +138,7 @@ public class ProlificSerialDriver implements UsbSerialDriver {
byte[] buffer = new byte[length]; byte[] buffer = new byte[length];
int result = mConnection.controlTransfer(requestType, request, value, index, buffer, length, USB_READ_TIMEOUT_MILLIS); int result = mConnection.controlTransfer(requestType, request, value, index, buffer, length, USB_READ_TIMEOUT_MILLIS);
if (result != length) { 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; return buffer;
} }
@ -148,7 +147,7 @@ public class ProlificSerialDriver implements UsbSerialDriver {
int length = (data == null) ? 0 : data.length; int length = (data == null) ? 0 : data.length;
int result = mConnection.controlTransfer(requestType, request, value, index, data, length, USB_WRITE_TIMEOUT_MILLIS); int result = mConnection.controlTransfer(requestType, request, value, index, data, length, USB_WRITE_TIMEOUT_MILLIS);
if (result != length) { 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() { private void readStatusThreadFunction() {
try { try {
byte[] buffer = new byte[STATUS_BUFFER_SIZE];
while (!mStopReadStatusThread) { while (!mStopReadStatusThread) {
byte[] buffer = new byte[STATUS_BUFFER_SIZE];
long endTime = MonotonicClock.millis() + 500; long endTime = MonotonicClock.millis() + 500;
int readBytesCount = mConnection.bulkTransfer(mInterruptEndpoint, buffer, STATUS_BUFFER_SIZE, 500); int readBytesCount = mConnection.bulkTransfer(mInterruptEndpoint, buffer, STATUS_BUFFER_SIZE, 500);
if(readBytesCount == -1 && MonotonicClock.millis() < endTime) if(readBytesCount == -1)
testConnection(); testConnection(MonotonicClock.millis() < endTime);
if (readBytesCount > 0) { if (readBytesCount > 0) {
if (readBytesCount != STATUS_BUFFER_SIZE) { if (readBytesCount != STATUS_BUFFER_SIZE) {
throw new IOException("Invalid status notification, expected " + STATUS_BUFFER_SIZE + " bytes, got " + readBytesCount); 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) { } catch (Exception e) {
mReadStatusException = e; if (isOpen())
mReadStatusException = e;
} }
//Log.d(TAG, "end control line status thread " + mStopReadStatusThread + " " + (mReadStatusException == null ? "-" : mReadStatusException.getMessage())); //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 */ /* throw and clear an exception which occurred in the status read thread */
IOException readStatusException = mReadStatusException; Exception readStatusException = mReadStatusException;
if (mReadStatusException != null) { if (mReadStatusException != null) {
mReadStatusException = null; mReadStatusException = null;
throw new IOException(readStatusException); throw new IOException(readStatusException);
@ -265,10 +265,10 @@ public class ProlificSerialDriver implements UsbSerialDriver {
} }
@Override @Override
public void openInt(UsbDeviceConnection connection) throws IOException { public void openInt() throws IOException {
UsbInterface usbInterface = mDevice.getInterface(0); UsbInterface usbInterface = mDevice.getInterface(0);
if (!connection.claimInterface(usbInterface, true)) { if (!mConnection.claimInterface(usbInterface, true)) {
throw new IOException("Error claiming Prolific interface 0"); 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) { if(rawDescriptors == null || rawDescriptors.length < 14) {
throw new IOException("Could not get device descriptors"); throw new IOException("Could not get device descriptors");
} }
@ -299,15 +299,19 @@ public class ProlificSerialDriver implements UsbSerialDriver {
byte maxPacketSize0 = rawDescriptors[7]; byte maxPacketSize0 = rawDescriptors[7];
if (mDevice.getDeviceClass() == 0x02 || maxPacketSize0 != 64) { if (mDevice.getDeviceClass() == 0x02 || maxPacketSize0 != 64) {
mDeviceType = DeviceType.DEVICE_TYPE_01; mDeviceType = DeviceType.DEVICE_TYPE_01;
} else if(deviceVersion == 0x300 && usbVersion == 0x200) { } else if(usbVersion == 0x200) {
mDeviceType = DeviceType.DEVICE_TYPE_T; // TA if(deviceVersion == 0x300 && testHxStatus()) {
} else if(deviceVersion == 0x500) { mDeviceType = DeviceType.DEVICE_TYPE_T; // TA
mDeviceType = DeviceType.DEVICE_TYPE_T; // TB } else if(deviceVersion == 0x500 && testHxStatus()) {
} else if(usbVersion == 0x200 && !testHxStatus()) { mDeviceType = DeviceType.DEVICE_TYPE_T; // TB
mDeviceType = DeviceType.DEVICE_TYPE_HXN; } else {
mDeviceType = DeviceType.DEVICE_TYPE_HXN;
}
} else { } else {
mDeviceType = DeviceType.DEVICE_TYPE_HX; 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(); resetDevice();
doBlackMagic(); doBlackMagic();
setControlLines(mControlLinesValue); setControlLines(mControlLinesValue);
@ -563,6 +567,7 @@ public class ProlificSerialDriver implements UsbSerialDriver {
} }
} }
@SuppressWarnings({"unused"})
public static Map<Integer, int[]> getSupportedDevices() { public static Map<Integer, int[]> getSupportedDevices() {
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<>(); final Map<Integer, int[]> supportedDevices = new LinkedHashMap<>();
supportedDevices.put(UsbId.VENDOR_PROLIFIC, supportedDevices.put(UsbId.VENDOR_PROLIFIC,
@ -571,7 +576,6 @@ public class ProlificSerialDriver implements UsbSerialDriver {
UsbId.PROLIFIC_PL2303GC, UsbId.PROLIFIC_PL2303GC,
UsbId.PROLIFIC_PL2303GB, UsbId.PROLIFIC_PL2303GB,
UsbId.PROLIFIC_PL2303GT, UsbId.PROLIFIC_PL2303GT,
UsbId.PROLIFIC_PL2303GT3,
UsbId.PROLIFIC_PL2303GL, UsbId.PROLIFIC_PL2303GL,
UsbId.PROLIFIC_PL2303GE, UsbId.PROLIFIC_PL2303GE,
UsbId.PROLIFIC_PL2303GS, UsbId.PROLIFIC_PL2303GS,

View file

@ -9,7 +9,8 @@ import java.io.InterruptedIOException;
* {@see InterruptedIOException#bytesTransferred} may contain bytes transferred * {@see InterruptedIOException#bytesTransferred} may contain bytes transferred
*/ */
public class SerialTimeoutException extends InterruptedIOException { public class SerialTimeoutException extends InterruptedIOException {
public SerialTimeoutException(String s) { public SerialTimeoutException(String s, int bytesTransferred) {
super(s); super(s);
this.bytesTransferred = bytesTransferred;
} }
} }

View file

@ -23,27 +23,6 @@ public final class UsbId {
public static final int FTDI_FT232H = 0x6014; public static final int FTDI_FT232H = 0x6014;
public static final int FTDI_FT231X = 0x6015; // same ID for FT230X, FT231X, FT234XD 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 VENDOR_SILABS = 0x10c4;
public static final int SILABS_CP2102 = 0xea60; // same ID for CP2101, CP2103, CP2104, CP2109 public static final int SILABS_CP2102 = 0xea60; // same ID for CP2101, CP2103, CP2104, CP2109
public static final int SILABS_CP2105 = 0xea70; 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_PL2303 = 0x2303; // device type 01, T, HX
public static final int PROLIFIC_PL2303GC = 0x23a3; // device type HXN public static final int PROLIFIC_PL2303GC = 0x23a3; // device type HXN
public static final int PROLIFIC_PL2303GB = 0x23b3; // " public static final int PROLIFIC_PL2303GB = 0x23b3; // "
public static final int PROLIFIC_PL2303GT = 0x23cd; // " public static final int PROLIFIC_PL2303GT = 0x23c3; // "
public static final int PROLIFIC_PL2303GT3 = 0x23c3; // " public static final int PROLIFIC_PL2303GL = 0x23d3; // "
public static final int PROLIFIC_PL2303GL = 0x23e3; // "
public static final int PROLIFIC_PL2303GE = 0x23e3; // " public static final int PROLIFIC_PL2303GE = 0x23e3; // "
public static final int PROLIFIC_PL2303GS = 0x23f3; // " 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 VENDOR_QINHENG = 0x1a86;
public static final int QINHENG_CH340 = 0x7523; public static final int QINHENG_CH340 = 0x7523;
public static final int QINHENG_CH341A = 0x5523; 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_UNISOC = 0x1782;
public static final int VENDOR_ARM = 0x0d28; public static final int FIBOCOM_L610 = 0x4D10;
public static final int ARM_MBED = 0x0204; public static final int FIBOCOM_L612 = 0x4D12;
public static final int VENDOR_ST = 0x0483;
public static final int ST_CDC = 0x5740;
private UsbId() { private UsbId() {
throw new IllegalAccessError("Non-instantiable class"); throw new IllegalAccessError("Non-instantiable class");

View file

@ -10,12 +10,17 @@ import android.hardware.usb.UsbDevice;
import java.util.List; import java.util.List;
/**
*
* @author mike wakerly (opensource@hoho.com)
*/
public interface UsbSerialDriver { public interface UsbSerialDriver {
/*
* Additional interface properties. Invoked thru reflection.
*
UsbSerialDriver(UsbDevice device); // constructor with device
static Map<Integer, int[]> getSupportedDevices();
static boolean probe(UsbDevice device); // optional
*/
/** /**
* Returns the raw {@link UsbDevice} backing this port. * Returns the raw {@link UsbDevice} backing this port.
* *

View file

@ -58,7 +58,7 @@ public interface UsbSerialPort extends Closeable {
int STOPBITS_2 = 2; int STOPBITS_2 = 2;
/** Values for get[Supported]ControlLines() */ /** 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. * 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; 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. * 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; 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. * Sets various serial port parameters.
* *

View file

@ -37,6 +37,8 @@ public class UsbSerialProber {
probeTable.addDriver(FtdiSerialDriver.class); probeTable.addDriver(FtdiSerialDriver.class);
probeTable.addDriver(ProlificSerialDriver.class); probeTable.addDriver(ProlificSerialDriver.class);
probeTable.addDriver(Ch34xSerialDriver.class); probeTable.addDriver(Ch34xSerialDriver.class);
probeTable.addDriver(GsmModemSerialDriver.class);
probeTable.addDriver(ChromeCcdSerialDriver.class);
return probeTable; return probeTable;
} }
@ -69,11 +71,7 @@ public class UsbSerialProber {
* {@code null} if none available. * {@code null} if none available.
*/ */
public UsbSerialDriver probeDevice(final UsbDevice usbDevice) { public UsbSerialDriver probeDevice(final UsbDevice usbDevice) {
final int vendorId = usbDevice.getVendorId(); final Class<? extends UsbSerialDriver> driverClass = mProbeTable.findDriver(usbDevice);
final int productId = usbDevice.getProductId();
final Class<? extends UsbSerialDriver> driverClass =
mProbeTable.findDriver(vendorId, productId);
if (driverClass != null) { if (driverClass != null) {
final UsbSerialDriver driver; final UsbSerialDriver driver;
try { try {

View file

@ -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;
}
}

View file

@ -9,6 +9,7 @@ package org.emulator.calculator.usbserial.util;
import android.os.Process; import android.os.Process;
import android.util.Log; import android.util.Log;
import org.emulator.calculator.usbserial.driver.SerialTimeoutException;
import org.emulator.calculator.usbserial.driver.UsbSerialPort; import org.emulator.calculator.usbserial.driver.UsbSerialPort;
import java.io.IOException; import java.io.IOException;
@ -21,8 +22,15 @@ import java.nio.ByteBuffer;
*/ */
public class SerialInputOutputManager implements Runnable { public class SerialInputOutputManager implements Runnable {
private static final String TAG = SerialInputOutputManager.class.getSimpleName(); public enum State {
STOPPED,
RUNNING,
STOPPING
}
public static boolean DEBUG = false; public static boolean DEBUG = false;
private static final String TAG = SerialInputOutputManager.class.getSimpleName();
private static final int BUFSIZ = 4096; private static final int BUFSIZ = 4096;
/** /**
@ -37,12 +45,6 @@ public class SerialInputOutputManager implements Runnable {
private ByteBuffer mReadBuffer; // default size = getReadEndpoint().getMaxPacketSize() private ByteBuffer mReadBuffer; // default size = getReadEndpoint().getMaxPacketSize()
private ByteBuffer mWriteBuffer = ByteBuffer.allocate(BUFSIZ); private ByteBuffer mWriteBuffer = ByteBuffer.allocate(BUFSIZ);
public enum State {
STOPPED,
RUNNING,
STOPPING
}
private int mThreadPriority = Process.THREAD_PRIORITY_URGENT_AUDIO; private int mThreadPriority = Process.THREAD_PRIORITY_URGENT_AUDIO;
private State mState = State.STOPPED; // Synchronized by 'this' private State mState = State.STOPPED; // Synchronized by 'this'
private Listener mListener; // Synchronized by 'this' private Listener mListener; // Synchronized by 'this'
@ -202,7 +204,11 @@ public class SerialInputOutputManager implements Runnable {
step(); step();
} }
} catch (Exception e) { } 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(); final Listener listener = getListener();
if (listener != null) { if (listener != null) {
listener.onRunError(e); listener.onRunError(e);
@ -223,7 +229,9 @@ public class SerialInputOutputManager implements Runnable {
} }
int len = mSerialPort.read(buffer, mReadTimeout); int len = mSerialPort.read(buffer, mReadTimeout);
if (len > 0) { 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(); final Listener listener = getListener();
if (listener != null) { if (listener != null) {
final byte[] data = new byte[len]; final byte[] data = new byte[len];
@ -247,7 +255,23 @@ public class SerialInputOutputManager implements Runnable {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "Writing data len=" + len); 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);
}
}
} }
} }

View file

@ -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<byte[]> getDescriptors(UsbDeviceConnection connection) {
ArrayList<byte[]> 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;
}
}

View file

@ -6,7 +6,7 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html # http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process. # Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings. # The setting is particularly useful for tweaking memory settings.
#android.defaults.buildfeatures.buildconfig=true android.defaults.buildfeatures.buildconfig=true
android.enableJetifier=true android.enableJetifier=true
android.nonFinalResIds=true android.nonFinalResIds=true
android.nonTransitiveRClass=true android.nonTransitiveRClass=true