kopia lustrzana https://github.com/mik3y/usb-serial-for-android
355 wiersze
9.6 KiB
Java
355 wiersze
9.6 KiB
Java
/* Copyright 2014 Andreas Butti
|
|
*
|
|
* Project home page: https://github.com/mik3y/usb-serial-for-android
|
|
*/
|
|
|
|
package com.hoho.android.usbserial.driver;
|
|
|
|
import android.hardware.usb.UsbConstants;
|
|
import android.hardware.usb.UsbDevice;
|
|
import android.hardware.usb.UsbDeviceConnection;
|
|
import android.hardware.usb.UsbEndpoint;
|
|
import android.hardware.usb.UsbInterface;
|
|
|
|
import 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 Ch34xSerialDriver implements UsbSerialDriver {
|
|
|
|
private static final String TAG = Ch34xSerialDriver.class.getSimpleName();
|
|
|
|
private final UsbDevice mDevice;
|
|
private final UsbSerialPort mPort;
|
|
|
|
private static final int LCR_ENABLE_RX = 0x80;
|
|
private static final int LCR_ENABLE_TX = 0x40;
|
|
private static final int LCR_MARK_SPACE = 0x20;
|
|
private static final int LCR_PAR_EVEN = 0x10;
|
|
private static final int LCR_ENABLE_PAR = 0x08;
|
|
private static final int LCR_STOP_BITS_2 = 0x04;
|
|
private static final int LCR_CS8 = 0x03;
|
|
private static final int LCR_CS7 = 0x02;
|
|
private static final int LCR_CS6 = 0x01;
|
|
private static final int LCR_CS5 = 0x00;
|
|
|
|
private static final int GCL_CTS = 0x01;
|
|
private static final int GCL_DSR = 0x02;
|
|
private static final int GCL_RI = 0x04;
|
|
private static final int GCL_CD = 0x08;
|
|
private static final int SCL_DTR = 0x20;
|
|
private static final int SCL_RTS = 0x40;
|
|
|
|
public Ch34xSerialDriver(UsbDevice device) {
|
|
mDevice = device;
|
|
mPort = new Ch340SerialPort(mDevice, 0);
|
|
}
|
|
|
|
@Override
|
|
public UsbDevice getDevice() {
|
|
return mDevice;
|
|
}
|
|
|
|
@Override
|
|
public List<UsbSerialPort> getPorts() {
|
|
return Collections.singletonList(mPort);
|
|
}
|
|
|
|
public class Ch340SerialPort extends CommonUsbSerialPort {
|
|
|
|
private static final int USB_TIMEOUT_MILLIS = 5000;
|
|
|
|
private final int DEFAULT_BAUD_RATE = 9600;
|
|
|
|
private boolean dtr = false;
|
|
private boolean rts = false;
|
|
|
|
public Ch340SerialPort(UsbDevice device, int portNumber) {
|
|
super(device, portNumber);
|
|
}
|
|
|
|
@Override
|
|
public UsbSerialDriver getDriver() {
|
|
return Ch34xSerialDriver.this;
|
|
}
|
|
|
|
@Override
|
|
protected void openInt(UsbDeviceConnection connection) throws IOException {
|
|
for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
|
|
UsbInterface usbIface = mDevice.getInterface(i);
|
|
if (!mConnection.claimInterface(usbIface, true)) {
|
|
throw new IOException("Could not claim data interface");
|
|
}
|
|
}
|
|
|
|
UsbInterface dataIface = mDevice.getInterface(mDevice.getInterfaceCount() - 1);
|
|
for (int i = 0; i < dataIface.getEndpointCount(); i++) {
|
|
UsbEndpoint ep = dataIface.getEndpoint(i);
|
|
if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
|
|
if (ep.getDirection() == UsbConstants.USB_DIR_IN) {
|
|
mReadEndpoint = ep;
|
|
} else {
|
|
mWriteEndpoint = ep;
|
|
}
|
|
}
|
|
}
|
|
|
|
initialize();
|
|
setBaudRate(DEFAULT_BAUD_RATE);
|
|
}
|
|
|
|
@Override
|
|
protected void closeInt() {
|
|
try {
|
|
for (int i = 0; i < mDevice.getInterfaceCount(); i++)
|
|
mConnection.releaseInterface(mDevice.getInterface(i));
|
|
} catch(Exception ignored) {}
|
|
}
|
|
|
|
private int controlOut(int request, int value, int index) {
|
|
final int REQTYPE_HOST_TO_DEVICE = UsbConstants.USB_TYPE_VENDOR | UsbConstants.USB_DIR_OUT;
|
|
return mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, request,
|
|
value, index, null, 0, USB_TIMEOUT_MILLIS);
|
|
}
|
|
|
|
|
|
private int controlIn(int request, int value, int index, byte[] buffer) {
|
|
final int REQTYPE_DEVICE_TO_HOST = UsbConstants.USB_TYPE_VENDOR | UsbConstants.USB_DIR_IN;
|
|
return mConnection.controlTransfer(REQTYPE_DEVICE_TO_HOST, request,
|
|
value, index, buffer, buffer.length, USB_TIMEOUT_MILLIS);
|
|
}
|
|
|
|
|
|
private void checkState(String msg, int request, int value, int[] expected) throws IOException {
|
|
byte[] buffer = new byte[expected.length];
|
|
int ret = controlIn(request, value, 0, buffer);
|
|
|
|
if (ret < 0) {
|
|
throw new IOException("Failed send cmd [" + msg + "]");
|
|
}
|
|
|
|
if (ret != expected.length) {
|
|
throw new IOException("Expected " + expected.length + " bytes, but get " + ret + " [" + msg + "]");
|
|
}
|
|
|
|
for (int i = 0; i < expected.length; i++) {
|
|
if (expected[i] == -1) {
|
|
continue;
|
|
}
|
|
|
|
int current = buffer[i] & 0xff;
|
|
if (expected[i] != current) {
|
|
throw new IOException("Expected 0x" + Integer.toHexString(expected[i]) + " byte, but get 0x" + Integer.toHexString(current) + " [" + msg + "]");
|
|
}
|
|
}
|
|
}
|
|
|
|
private void setControlLines() throws IOException {
|
|
if (controlOut(0xa4, ~((dtr ? SCL_DTR : 0) | (rts ? SCL_RTS : 0)), 0) < 0) {
|
|
throw new IOException("Failed to set control lines");
|
|
}
|
|
}
|
|
|
|
private byte getStatus() throws IOException {
|
|
byte[] buffer = new byte[2];
|
|
int ret = controlIn(0x95, 0x0706, 0, buffer);
|
|
if (ret < 0)
|
|
throw new IOException("Error getting control lines");
|
|
return buffer[0];
|
|
}
|
|
|
|
private void initialize() throws IOException {
|
|
checkState("init #1", 0x5f, 0, new int[]{-1 /* 0x27, 0x30 */, 0x00});
|
|
|
|
if (controlOut(0xa1, 0, 0) < 0) {
|
|
throw new IOException("Init failed: #2");
|
|
}
|
|
|
|
setBaudRate(DEFAULT_BAUD_RATE);
|
|
|
|
checkState("init #4", 0x95, 0x2518, new int[]{-1 /* 0x56, c3*/, 0x00});
|
|
|
|
if (controlOut(0x9a, 0x2518, LCR_ENABLE_RX | LCR_ENABLE_TX | LCR_CS8) < 0) {
|
|
throw new IOException("Init failed: #5");
|
|
}
|
|
|
|
checkState("init #6", 0x95, 0x0706, new int[]{-1/*0xf?*/, -1/*0xec,0xee*/});
|
|
|
|
if (controlOut(0xa1, 0x501f, 0xd90a) < 0) {
|
|
throw new IOException("Init failed: #7");
|
|
}
|
|
|
|
setBaudRate(DEFAULT_BAUD_RATE);
|
|
|
|
setControlLines();
|
|
|
|
checkState("init #10", 0x95, 0x0706, new int[]{-1/* 0x9f, 0xff*/, -1/*0xec,0xee*/});
|
|
}
|
|
|
|
|
|
private void setBaudRate(int baudRate) throws IOException {
|
|
final long CH341_BAUDBASE_FACTOR = 1532620800;
|
|
final int CH341_BAUDBASE_DIVMAX = 3;
|
|
|
|
long factor = CH341_BAUDBASE_FACTOR / baudRate;
|
|
int divisor = CH341_BAUDBASE_DIVMAX;
|
|
|
|
while ((factor > 0xfff0) && divisor > 0) {
|
|
factor >>= 3;
|
|
divisor--;
|
|
}
|
|
|
|
if (factor > 0xfff0) {
|
|
throw new UnsupportedOperationException("Unsupported baud rate: " + baudRate);
|
|
}
|
|
|
|
factor = 0x10000 - factor;
|
|
divisor |= 0x0080; // else ch341a waits until buffer full
|
|
int ret = controlOut(0x9a, 0x1312, (int) ((factor & 0xff00) | divisor));
|
|
if (ret < 0) {
|
|
throw new IOException("Error setting baud rate: #1)");
|
|
}
|
|
|
|
ret = controlOut(0x9a, 0x0f2c, (int) (factor & 0xff));
|
|
if (ret < 0) {
|
|
throw new IOException("Error setting baud rate: #2");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setParameters(int baudRate, int dataBits, int stopBits, int parity) throws IOException {
|
|
if(baudRate <= 0) {
|
|
throw new IllegalArgumentException("Invalid baud rate: " + baudRate);
|
|
}
|
|
setBaudRate(baudRate);
|
|
|
|
int lcr = LCR_ENABLE_RX | LCR_ENABLE_TX;
|
|
|
|
switch (dataBits) {
|
|
case DATABITS_5:
|
|
lcr |= LCR_CS5;
|
|
break;
|
|
case DATABITS_6:
|
|
lcr |= LCR_CS6;
|
|
break;
|
|
case DATABITS_7:
|
|
lcr |= LCR_CS7;
|
|
break;
|
|
case DATABITS_8:
|
|
lcr |= LCR_CS8;
|
|
break;
|
|
default:
|
|
throw new IllegalArgumentException("Invalid data bits: " + dataBits);
|
|
}
|
|
|
|
switch (parity) {
|
|
case PARITY_NONE:
|
|
break;
|
|
case PARITY_ODD:
|
|
lcr |= LCR_ENABLE_PAR;
|
|
break;
|
|
case PARITY_EVEN:
|
|
lcr |= LCR_ENABLE_PAR | LCR_PAR_EVEN;
|
|
break;
|
|
case PARITY_MARK:
|
|
lcr |= LCR_ENABLE_PAR | LCR_MARK_SPACE;
|
|
break;
|
|
case PARITY_SPACE:
|
|
lcr |= LCR_ENABLE_PAR | LCR_MARK_SPACE | LCR_PAR_EVEN;
|
|
break;
|
|
default:
|
|
throw new IllegalArgumentException("Invalid parity: " + parity);
|
|
}
|
|
|
|
switch (stopBits) {
|
|
case STOPBITS_1:
|
|
break;
|
|
case STOPBITS_1_5:
|
|
throw new UnsupportedOperationException("Unsupported stop bits: 1.5");
|
|
case STOPBITS_2:
|
|
lcr |= LCR_STOP_BITS_2;
|
|
break;
|
|
default:
|
|
throw new IllegalArgumentException("Invalid stop bits: " + stopBits);
|
|
}
|
|
|
|
int ret = controlOut(0x9a, 0x2518, lcr);
|
|
if (ret < 0) {
|
|
throw new IOException("Error setting control byte");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean getCD() throws IOException {
|
|
return (getStatus() & GCL_CD) == 0;
|
|
}
|
|
|
|
@Override
|
|
public boolean getCTS() throws IOException {
|
|
return (getStatus() & GCL_CTS) == 0;
|
|
}
|
|
|
|
@Override
|
|
public boolean getDSR() throws IOException {
|
|
return (getStatus() & GCL_DSR) == 0;
|
|
}
|
|
|
|
@Override
|
|
public boolean getDTR() throws IOException {
|
|
return dtr;
|
|
}
|
|
|
|
@Override
|
|
public void setDTR(boolean value) throws IOException {
|
|
dtr = value;
|
|
setControlLines();
|
|
}
|
|
|
|
@Override
|
|
public boolean getRI() throws IOException {
|
|
return (getStatus() & GCL_RI) == 0;
|
|
}
|
|
|
|
@Override
|
|
public boolean getRTS() throws IOException {
|
|
return rts;
|
|
}
|
|
|
|
@Override
|
|
public void setRTS(boolean value) throws IOException {
|
|
rts = value;
|
|
setControlLines();
|
|
}
|
|
|
|
@Override
|
|
public EnumSet<ControlLine> getControlLines() throws IOException {
|
|
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
|
|
public EnumSet<ControlLine> getSupportedControlLines() throws IOException {
|
|
return EnumSet.allOf(ControlLine.class);
|
|
}
|
|
}
|
|
|
|
public static Map<Integer, int[]> getSupportedDevices() {
|
|
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<Integer, int[]>();
|
|
supportedDevices.put(UsbId.VENDOR_QINHENG, new int[]{
|
|
UsbId.QINHENG_CH340,
|
|
UsbId.QINHENG_CH341A,
|
|
});
|
|
return supportedDevices;
|
|
}
|
|
|
|
} |