mirror of
https://github.com/dgis/emu48android
synced 2024-12-26 09:58:49 +01:00
- Update the usb serial drivers usb-serial-for-android to version 3.7.3
This commit is contained in:
parent
e847c0d120
commit
8025fe107d
21 changed files with 1059 additions and 511 deletions
|
@ -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.
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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.
|
||||||
*
|
*
|
||||||
|
|
|
@ -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.
|
||||||
*
|
*
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue