/* Copyright 2011-2013 Google Inc.
* Copyright 2013 mike wakerly <opensource@hoho.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* Project home page: https://github.com/mik3y/usb-serial-for-android
*/
package com.eveningoutpost.dexdrip.ImportedLibraries.usbserial.driver;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbRequest;
import android.util.Log;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class Cp21xxSerialDriver implements UsbSerialDriver {
private static final String TAG = Cp21xxSerialDriver.class.getSimpleName();
private final UsbDevice mDevice;
private final UsbSerialPort mPort;
public Cp21xxSerialDriver(UsbDevice device) {
mDevice = device;
mPort = new Cp21xxSerialPort(mDevice, 0);
}
@Override
public UsbDevice getDevice() {
return mDevice;
}
@Override
public List<UsbSerialPort> getPorts() {
return Collections.singletonList(mPort);
}
public class Cp21xxSerialPort extends CommonUsbSerialPort {
private static final int DEFAULT_BAUD_RATE = 9600;
private static final int USB_WRITE_TIMEOUT_MILLIS = 5000;
/*
* Configuration Request Types
*/
private static final int REQTYPE_HOST_TO_DEVICE = 0x41;
/*
* Configuration Request Codes
*/
private static final int SILABSER_IFC_ENABLE_REQUEST_CODE = 0x00;
private static final int SILABSER_SET_BAUDDIV_REQUEST_CODE = 0x01;
private static final int SILABSER_SET_LINE_CTL_REQUEST_CODE = 0x03;
private static final int SILABSER_SET_MHS_REQUEST_CODE = 0x07;
private static final int SILABSER_SET_BAUDRATE = 0x1E;
private static final int SILABSER_FLUSH_REQUEST_CODE = 0x12;
private static final int FLUSH_READ_CODE = 0x0a;
private static final int FLUSH_WRITE_CODE = 0x05;
/*
* SILABSER_IFC_ENABLE_REQUEST_CODE
*/
private static final int UART_ENABLE = 0x0001;
private static final int UART_DISABLE = 0x0000;
/*
* SILABSER_SET_BAUDDIV_REQUEST_CODE
*/
private static final int BAUD_RATE_GEN_FREQ = 0x384000;
/*
* SILABSER_SET_MHS_REQUEST_CODE
*/
private static final int MCR_DTR = 0x0001;
private static final int MCR_RTS = 0x0002;
private static final int MCR_ALL = 0x0003;
private static final int CONTROL_WRITE_DTR = 0x0100;
private static final int CONTROL_WRITE_RTS = 0x0200;
private UsbEndpoint mReadEndpoint;
private UsbEndpoint mWriteEndpoint;
public Cp21xxSerialPort(UsbDevice device, int portNumber) {
super(device, portNumber);
}
@Override
public UsbSerialDriver getDriver() {
return Cp21xxSerialDriver.this;
}
private int setConfigSingle(int request, int value) {
return mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, request, value,
0, null, 0, USB_WRITE_TIMEOUT_MILLIS);
}
@Override
public void open(UsbDeviceConnection connection) throws IOException {
if (mConnection != null) {
throw new IOException("Already opened.");
}
mConnection = connection;
boolean opened = false;
try {
for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
UsbInterface usbIface = mDevice.getInterface(i);
if (mConnection.claimInterface(usbIface, true)) {
Log.d(TAG, "claimInterface " + i + " SUCCESS");
} else {
Log.d(TAG, "claimInterface " + i + " FAIL");
}
}
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;
}
}
}
setConfigSingle(SILABSER_IFC_ENABLE_REQUEST_CODE, UART_ENABLE);
setConfigSingle(SILABSER_SET_MHS_REQUEST_CODE, MCR_ALL | CONTROL_WRITE_DTR | CONTROL_WRITE_RTS);
setConfigSingle(SILABSER_SET_BAUDDIV_REQUEST_CODE, BAUD_RATE_GEN_FREQ / DEFAULT_BAUD_RATE);
// setParameters(DEFAULT_BAUD_RATE, DEFAULT_DATA_BITS, DEFAULT_STOP_BITS, DEFAULT_PARITY);
opened = true;
} finally {
if (!opened) {
try {
close();
} catch (IOException e) {
// Ignore IOExceptions during close()
}
}
}
}
@Override
public void close() throws IOException {
if (mConnection == null) {
throw new IOException("Already closed");
}
try {
setConfigSingle(SILABSER_IFC_ENABLE_REQUEST_CODE, UART_DISABLE);
mConnection.close();
} finally {
mConnection = null;
}
}
@Override
public int read(byte[] dest, int timeoutMillis) throws IOException {
final int numBytesRead;
synchronized (mReadBufferLock) {
int readAmt = Math.min(dest.length, mReadBuffer.length);
numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer, readAmt,
timeoutMillis);
if (numBytesRead < 0) {
// This sucks: we get -1 on timeout, not 0 as preferred.
// We *should* use UsbRequest, except it has a bug/api oversight
// where there is no way to determine the number of bytes read
// in response :\ -- http://b.android.com/28023
return 0;
}
System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
}
return numBytesRead;
}
@Override
public int write(byte[] src, int timeoutMillis) throws IOException {
int offset = 0;
while (offset < src.length) {
final int writeLength;
final int amtWritten;
synchronized (mWriteBufferLock) {
final byte[] writeBuffer;
writeLength = Math.min(src.length - offset, mWriteBuffer.length);
if (offset == 0) {
writeBuffer = src;
} else {
// bulkTransfer does not support offsets, make a copy.
System.arraycopy(src, offset, mWriteBuffer, 0, writeLength);
writeBuffer = mWriteBuffer;
}
amtWritten = mConnection.bulkTransfer(mWriteEndpoint, writeBuffer, writeLength,
timeoutMillis);
}
if (amtWritten <= 0) {
throw new IOException("Error writing " + writeLength
+ " bytes at offset " + offset + " length=" + src.length);
}
Log.d(TAG, "Wrote amt=" + amtWritten + " attempted=" + writeLength);
offset += amtWritten;
}
return offset;
}
@Override
public int read(byte[] dest, int timeoutMillis, UsbDeviceConnection connection) throws IOException {
if (false) {
final UsbRequest request = new UsbRequest();
try {
request.initialize(connection, mReadEndpoint);
final ByteBuffer buf = ByteBuffer.wrap(dest);
if (!request.queue(buf, dest.length)) {
throw new IOException("Error queueing request.");
}
final UsbRequest response = connection.requestWait();
if (response == null) {
throw new IOException("Null response");
}
final int nread = buf.position();
if (nread > 0) {
//Log.d(TAG, HexDump.dumpHexString(dest, 0, Math.min(32, dest.length)));
return nread;
} else {
return 0;
}
} finally {
request.close();
}
}
final int numBytesRead;
synchronized (mReadBufferLock) {
int readAmt = Math.min(dest.length, mReadBuffer.length);
numBytesRead = connection.bulkTransfer(mReadEndpoint, mReadBuffer, readAmt,
timeoutMillis);
if (numBytesRead < 0) {
// This sucks: we get -1 on timeout, not 0 as preferred.
// We *should* use UsbRequest, except it has a bug/api oversight
// where there is no way to determine the number of bytes read
// in response :\ -- http://b.android.com/28023
if (timeoutMillis == Integer.MAX_VALUE) {
// Hack: Special case "~infinite timeout" as an error.
return -1;
}
return 0;
}
System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
}
return numBytesRead;
}
private void setBaudRate(int baudRate) throws IOException {
byte[] data = new byte[] {
(byte) ( baudRate & 0xff),
(byte) ((baudRate >> 8 ) & 0xff),
(byte) ((baudRate >> 16) & 0xff),
(byte) ((baudRate >> 24) & 0xff)
};
int ret = mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, SILABSER_SET_BAUDRATE,
0, 0, data, 4, USB_WRITE_TIMEOUT_MILLIS);
if (ret < 0) {
throw new IOException("Error setting baud rate.");
}
}
@Override
public void setParameters(int baudRate, int dataBits, int stopBits, int parity)
throws IOException {
setBaudRate(baudRate);
int configDataBits = 0;
switch (dataBits) {
case DATABITS_5:
configDataBits |= 0x0500;
break;
case DATABITS_6:
configDataBits |= 0x0600;
break;
case DATABITS_7:
configDataBits |= 0x0700;
break;
case DATABITS_8:
configDataBits |= 0x0800;
break;
default:
configDataBits |= 0x0800;
break;
}
switch (parity) {
case PARITY_ODD:
configDataBits |= 0x0010;
break;
case PARITY_EVEN:
configDataBits |= 0x0020;
break;
}
switch (stopBits) {
case STOPBITS_1:
configDataBits |= 0;
break;
case STOPBITS_2:
configDataBits |= 2;
break;
}
setConfigSingle(SILABSER_SET_LINE_CTL_REQUEST_CODE, configDataBits);
}
@Override
public boolean getCD() throws IOException {
return false;
}
@Override
public boolean getCTS() throws IOException {
return false;
}
@Override
public boolean getDSR() throws IOException {
return false;
}
@Override
public boolean getDTR() throws IOException {
return true;
}
@Override
public void setDTR(boolean value) throws IOException {
}
@Override
public boolean getRI() throws IOException {
return false;
}
@Override
public boolean getRTS() throws IOException {
return true;
}
@Override
public void setRTS(boolean value) throws IOException {
}
@Override
public boolean purgeHwBuffers(boolean purgeReadBuffers,
boolean purgeWriteBuffers) throws IOException {
int value = (purgeReadBuffers ? FLUSH_READ_CODE : 0)
| (purgeWriteBuffers ? FLUSH_WRITE_CODE : 0);
if (value != 0) {
setConfigSingle(SILABSER_FLUSH_REQUEST_CODE, value);
}
return true;
}
}
public static Map<Integer, int[]> getSupportedDevices() {
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<Integer, int[]>();
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_SILABS),
new int[] {
UsbId.SILABS_CP2102,
UsbId.SILABS_CP2105,
UsbId.SILABS_CP2108,
UsbId.SILABS_CP2110
});
return supportedDevices;
}
}