diff --git a/UsbSerialExamples/res/xml/device_filter.xml b/UsbSerialExamples/res/xml/device_filter.xml
index 495a735..d9036a7 100644
--- a/UsbSerialExamples/res/xml/device_filter.xml
+++ b/UsbSerialExamples/res/xml/device_filter.xml
@@ -12,4 +12,6 @@
+
+
diff --git a/UsbSerialLibrary/src/com/hoho/android/usbserial/driver/ProlificSerialDriver.java b/UsbSerialLibrary/src/com/hoho/android/usbserial/driver/ProlificSerialDriver.java
new file mode 100644
index 0000000..7846931
--- /dev/null
+++ b/UsbSerialLibrary/src/com/hoho/android/usbserial/driver/ProlificSerialDriver.java
@@ -0,0 +1,513 @@
+/* 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: http://code.google.com/p/usb-serial-for-android/
+ */
+
+/*
+ * Ported to usb-serial-for-android
+ * by Felix Hädicke
+ *
+ * Based on the pyprolific driver written
+ * by Emmanuel Blot
+ * See https://github.com/eblot/pyftdi
+ */
+
+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 android.util.Log;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class ProlificSerialDriver extends CommonUsbSerialDriver {
+ private static final int USB_READ_TIMEOUT_MILLIS = 1000;
+ private static final int USB_WRITE_TIMEOUT_MILLIS = 5000;
+
+ private static final int USB_RECIP_INTERFACE = 0x01;
+
+ private static final int PROLIFIC_VENDOR_READ_REQUEST = 0x01;
+ private static final int PROLIFIC_VENDOR_WRITE_REQUEST = 0x01;
+
+ private static final int PROLIFIC_VENDOR_OUT_REQTYPE = UsbConstants.USB_DIR_OUT
+ | UsbConstants.USB_TYPE_VENDOR;
+
+ private static final int PROLIFIC_VENDOR_IN_REQTYPE = UsbConstants.USB_DIR_IN
+ | UsbConstants.USB_TYPE_VENDOR;
+
+ private static final int PROLIFIC_CTRL_OUT_REQTYPE = UsbConstants.USB_DIR_OUT
+ | UsbConstants.USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+
+ private static final int WRITE_ENDPOINT = 0x02;
+ private static final int READ_ENDPOINT = 0x83;
+ private static final int INTERRUPT_ENDPOINT = 0x81;
+
+ private static final int FLUSH_RX_REQUEST = 0x08;
+ private static final int FLUSH_TX_REQUEST = 0x09;
+
+ private static final int SET_LINE_REQUEST = 0x20;
+ private static final int SET_CONTROL_REQUEST = 0x22;
+
+ private static final int CONTROL_DTR = 0x01;
+ private static final int CONTROL_RTS = 0x02;
+
+ private static final int STATUS_FLAG_CD = 0x01;
+ private static final int STATUS_FLAG_DSR = 0x02;
+ private static final int STATUS_FLAG_RI = 0x08;
+ private static final int STATUS_FLAG_CTS = 0x80;
+
+ private static final int STATUS_BUFFER_SIZE = 10;
+ private static final int STATUS_BYTE_IDX = 8;
+
+ private static final int DEVICE_TYPE_HX = 0;
+ private static final int DEVICE_TYPE_0 = 1;
+ private static final int DEVICE_TYPE_1 = 2;
+
+ private int mDeviceType = DEVICE_TYPE_HX;
+
+ private UsbEndpoint mReadEndpoint;
+ private UsbEndpoint mWriteEndpoint;
+ private UsbEndpoint mInterruptEndpoint;
+
+ private int mControlLinesValue = 0;
+
+ private int mBaudRate = -1, mDataBits = -1, mStopBits = -1, mParity = -1;
+
+ private int mStatus = 0;
+ private volatile Thread mReadStatusThread = null;
+ private final Object mReadStatusThreadLock = new Object();
+ boolean mStopReadStatusThread = false;
+ private IOException mReadStatusException = null;
+
+ private final String TAG = ProlificSerialDriver.class.getSimpleName();
+
+ private final byte[] inControlTransfer(int requestType, int request,
+ int value, int index, int length) throws IOException {
+ byte[] buffer = new byte[length];
+ int result = mConnection.controlTransfer(requestType, request, value,
+ index, buffer, length, USB_READ_TIMEOUT_MILLIS);
+ if (result != length) {
+ throw new IOException(
+ String.format("ControlTransfer with value 0x%x failed: %d",
+ value, result));
+ }
+ return buffer;
+ }
+
+ private final void outControlTransfer(int requestType, int request,
+ int value, int index, byte[] data) throws IOException {
+ int length = (data == null) ? 0 : data.length;
+ int result = mConnection.controlTransfer(requestType, request, value,
+ index, data, length, USB_WRITE_TIMEOUT_MILLIS);
+ if (result != length) {
+ throw new IOException(
+ String.format("ControlTransfer with value 0x%x failed: %d",
+ value, result));
+ }
+ }
+
+ private final byte[] vendorIn(int value, int index, int length)
+ throws IOException {
+ return inControlTransfer(PROLIFIC_VENDOR_IN_REQTYPE,
+ PROLIFIC_VENDOR_READ_REQUEST, value, index, length);
+ }
+
+ private final void vendorOut(int value, int index, byte[] data)
+ throws IOException {
+ outControlTransfer(PROLIFIC_VENDOR_OUT_REQTYPE,
+ PROLIFIC_VENDOR_WRITE_REQUEST, value, index, data);
+ }
+
+ private final void ctrlOut(int request, int value, int index, byte[] data)
+ throws IOException {
+ outControlTransfer(PROLIFIC_CTRL_OUT_REQTYPE, request, value, index,
+ data);
+ }
+
+ private void doBlackMagic() throws IOException {
+ vendorIn(0x8484, 0, 1);
+ vendorOut(0x0404, 0, null);
+ vendorIn(0x8484, 0, 1);
+ vendorIn(0x8383, 0, 1);
+ vendorIn(0x8484, 0, 1);
+ vendorOut(0x0404, 1, null);
+ vendorIn(0x8484, 0, 1);
+ vendorIn(0x8383, 0, 1);
+ vendorOut(0, 1, null);
+ vendorOut(1, 0, null);
+ vendorOut(2, (mDeviceType == DEVICE_TYPE_HX) ? 0x44 : 0x24, null);
+ }
+
+ private void resetDevice() throws IOException {
+ flush(true, true);
+ }
+
+ private void setControlLines(int newControlLinesValue) throws IOException {
+ ctrlOut(SET_CONTROL_REQUEST, newControlLinesValue, 0, null);
+ mControlLinesValue = newControlLinesValue;
+ }
+
+ private final void readStatusThreadFunction() {
+ try {
+ while (!mStopReadStatusThread) {
+ byte[] buffer = new byte[STATUS_BUFFER_SIZE];
+ int readBytesCount = mConnection.bulkTransfer(mInterruptEndpoint,
+ buffer,
+ STATUS_BUFFER_SIZE,
+ 500);
+ if (readBytesCount > 0) {
+ if (readBytesCount == STATUS_BUFFER_SIZE) {
+ mStatus = buffer[STATUS_BYTE_IDX] & 0xff;
+ } else {
+ throw new IOException(
+ String.format("Invalid CTS / DSR / CD / RI status buffer received, expected %d bytes, but received %d",
+ STATUS_BUFFER_SIZE,
+ readBytesCount));
+ }
+ }
+ }
+ } catch (IOException e) {
+ mReadStatusException = e;
+ }
+ }
+
+ private final int getStatus() throws IOException {
+ if ((mReadStatusThread == null) && (mReadStatusException == null)) {
+ synchronized (mReadStatusThreadLock) {
+ if (mReadStatusThread == null) {
+ byte[] buffer = new byte[STATUS_BUFFER_SIZE];
+ int readBytes = mConnection.bulkTransfer(mInterruptEndpoint,
+ buffer,
+ STATUS_BUFFER_SIZE,
+ 100);
+ if (readBytes != STATUS_BUFFER_SIZE) {
+ Log.w(TAG, "Could not read initial CTS / DSR / CD / RI status");
+ } else {
+ mStatus = buffer[STATUS_BYTE_IDX] & 0xff;
+ }
+
+ mReadStatusThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ readStatusThreadFunction();
+ }
+ });
+ mReadStatusThread.setDaemon(true);
+ mReadStatusThread.start();
+ }
+ }
+ }
+
+ /* throw and clear an exception which occured in the status read thread */
+ IOException readStatusException = mReadStatusException;
+ if (mReadStatusException != null) {
+ mReadStatusException = null;
+ throw readStatusException;
+ }
+
+ return mStatus;
+ }
+
+ private final boolean testStatusFlag(int flag) throws IOException {
+ return ((getStatus() & flag) == flag);
+ }
+
+ public ProlificSerialDriver(UsbDevice device, UsbDeviceConnection connection) {
+ super(device, connection);
+ }
+
+ @Override
+ public void open() throws IOException {
+ UsbInterface usbInterface = mDevice.getInterface(0);
+
+ if (!mConnection.claimInterface(usbInterface, true)) {
+ throw new IOException("Error claiming Prolific interface 0");
+ }
+
+ boolean openSuccessful = false;
+ try {
+ for (int i = 0; i < usbInterface.getEndpointCount(); ++i) {
+ UsbEndpoint currentEndpoint = usbInterface.getEndpoint(i);
+
+ switch (currentEndpoint.getAddress()) {
+ case READ_ENDPOINT:
+ mReadEndpoint = currentEndpoint;
+ break;
+
+ case WRITE_ENDPOINT:
+ mWriteEndpoint = currentEndpoint;
+ break;
+
+ case INTERRUPT_ENDPOINT:
+ mInterruptEndpoint = currentEndpoint;
+ break;
+ }
+ }
+
+ if (mDevice.getDeviceClass() == 0x02) {
+ mDeviceType = DEVICE_TYPE_0;
+ } else {
+ try {
+ Method getRawDescriptorsMethod
+ = mConnection.getClass().getMethod("getRawDescriptors");
+ byte[] rawDescriptors
+ = (byte[]) getRawDescriptorsMethod.invoke(mConnection);
+ byte maxPacketSize0 = rawDescriptors[7];
+ if (maxPacketSize0 == 64) {
+ mDeviceType = DEVICE_TYPE_HX;
+ } else if ((mDevice.getDeviceClass() == 0x00)
+ || (mDevice.getDeviceClass() == 0xff)) {
+ mDeviceType = DEVICE_TYPE_1;
+ } else {
+ Log.w(TAG, "Could not detect PL2303 subtype, "
+ + "Assuming that it is a HX device");
+ mDeviceType = DEVICE_TYPE_HX;
+ }
+ } catch (NoSuchMethodException e) {
+ Log.w(TAG, "Method UsbDeviceConnection.getRawDescriptors, "
+ + "required for PL2303 subtype detection, not "
+ + "available! Assuming that it is a HX device");
+ mDeviceType = DEVICE_TYPE_HX;
+ } catch (Exception e) {
+ Log.e(TAG, "An unexpected exception occured while trying "
+ + "to detect PL2303 subtype", e);
+ }
+ }
+
+ setControlLines(mControlLinesValue);
+ resetDevice();
+
+ doBlackMagic();
+ openSuccessful = true;
+ } finally {
+ if (!openSuccessful) {
+ try {
+ mConnection.releaseInterface(usbInterface);
+ } catch (Exception ingored) {
+ // Do not cover possible exceptions
+ }
+ }
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ try {
+ mStopReadStatusThread = true;
+ synchronized (mReadStatusThreadLock) {
+ if (mReadStatusThread != null) {
+ try {
+ mReadStatusThread.join();
+ } catch (Exception e) {
+ Log.w(TAG, "An error occured while waiting for status read thread", e);
+ }
+ }
+ }
+
+ resetDevice();
+ } finally {
+ mConnection.releaseInterface(mDevice.getInterface(0));
+ }
+ }
+
+ @Override
+ public int read(byte[] dest, int timeoutMillis) throws IOException {
+ synchronized (mReadBufferLock) {
+ int readAmt = Math.min(dest.length, mReadBuffer.length);
+ int numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer,
+ readAmt, timeoutMillis);
+ if (numBytesRead < 0) {
+ 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);
+ }
+
+ offset += amtWritten;
+ }
+ return offset;
+ }
+
+ @Override
+ public void setParameters(int baudRate, int dataBits, int stopBits,
+ int parity) throws IOException {
+ if ((mBaudRate == baudRate) && (mDataBits == dataBits)
+ && (mStopBits == stopBits) && (mParity == parity)) {
+ // Make sure no action is performed if there is nothing to change
+ return;
+ }
+
+ byte[] lineRequestData = new byte[7];
+
+ lineRequestData[0] = (byte) (baudRate & 0xff);
+ lineRequestData[1] = (byte) ((baudRate >> 8) & 0xff);
+ lineRequestData[2] = (byte) ((baudRate >> 16) & 0xff);
+ lineRequestData[3] = (byte) ((baudRate >> 24) & 0xff);
+
+ switch (stopBits) {
+ case STOPBITS_1:
+ lineRequestData[4] = 0;
+ break;
+
+ case STOPBITS_1_5:
+ lineRequestData[4] = 1;
+ break;
+
+ case STOPBITS_2:
+ lineRequestData[4] = 2;
+ break;
+ }
+
+ switch (parity) {
+ case PARITY_NONE:
+ lineRequestData[5] = 0;
+ break;
+
+ case PARITY_ODD:
+ lineRequestData[5] = 1;
+ break;
+
+ case PARITY_EVEN:
+ lineRequestData[5] = 2;
+ break;
+
+ case PARITY_MARK:
+ lineRequestData[5] = 3;
+ break;
+
+ case PARITY_SPACE:
+ lineRequestData[5] = 4;
+ break;
+ }
+
+ lineRequestData[6] = (byte) dataBits;
+
+ ctrlOut(SET_LINE_REQUEST, 0, 0, lineRequestData);
+
+ resetDevice();
+
+ mBaudRate = baudRate;
+ mDataBits = dataBits;
+ mStopBits = stopBits;
+ mParity = parity;
+ }
+
+ @Override
+ public boolean getCD() throws IOException {
+ return testStatusFlag(STATUS_FLAG_CD);
+ }
+
+ @Override
+ public boolean getCTS() throws IOException {
+ return testStatusFlag(STATUS_FLAG_CTS);
+ }
+
+ @Override
+ public boolean getDSR() throws IOException {
+ return testStatusFlag(STATUS_FLAG_DSR);
+ }
+
+ @Override
+ public boolean getDTR() throws IOException {
+ return ((mControlLinesValue & CONTROL_DTR) == CONTROL_DTR);
+ }
+
+ @Override
+ public void setDTR(boolean value) throws IOException {
+ int newControlLinesValue;
+ if (value) {
+ newControlLinesValue = mControlLinesValue | CONTROL_DTR;
+ } else {
+ newControlLinesValue = mControlLinesValue & ~CONTROL_DTR;
+ }
+ setControlLines(newControlLinesValue);
+ }
+
+ @Override
+ public boolean getRI() throws IOException {
+ return testStatusFlag(STATUS_FLAG_RI);
+ }
+
+ @Override
+ public boolean getRTS() throws IOException {
+ return ((mControlLinesValue & CONTROL_RTS) == CONTROL_RTS);
+ }
+
+ @Override
+ public void setRTS(boolean value) throws IOException {
+ int newControlLinesValue;
+ if (value) {
+ newControlLinesValue = mControlLinesValue | CONTROL_RTS;
+ } else {
+ newControlLinesValue = mControlLinesValue & ~CONTROL_RTS;
+ }
+ setControlLines(newControlLinesValue);
+ }
+
+ public void flush(boolean flushRX, boolean flushTX) throws IOException {
+ if (flushRX) {
+ vendorOut(FLUSH_RX_REQUEST, 0, null);
+ }
+
+ if (flushTX) {
+ vendorOut(FLUSH_TX_REQUEST, 0, null);
+ }
+ }
+
+ public static Map getSupportedDevices() {
+ final Map supportedDevices = new LinkedHashMap();
+ supportedDevices.put(Integer.valueOf(UsbId.VENDOR_PROLIFIC),
+ new int[] { UsbId.PROLIFIC_PL2303, });
+ return supportedDevices;
+ }
+}
diff --git a/UsbSerialLibrary/src/com/hoho/android/usbserial/driver/UsbId.java b/UsbSerialLibrary/src/com/hoho/android/usbserial/driver/UsbId.java
index a80368f..fb2764d 100644
--- a/UsbSerialLibrary/src/com/hoho/android/usbserial/driver/UsbId.java
+++ b/UsbSerialLibrary/src/com/hoho/android/usbserial/driver/UsbId.java
@@ -55,6 +55,9 @@ public final class UsbId {
public static final int VENDOR_SILAB = 0x10c4;
public static final int SILAB_CP2102 = 0xea60;
+ public static final int VENDOR_PROLIFIC = 0x067b;
+ public static final int PROLIFIC_PL2303 = 0x2303;
+
private UsbId() {
throw new IllegalAccessError("Non-instantiable class.");
}
diff --git a/UsbSerialLibrary/src/com/hoho/android/usbserial/driver/UsbSerialProber.java b/UsbSerialLibrary/src/com/hoho/android/usbserial/driver/UsbSerialProber.java
index fdad88a..420a8c2 100644
--- a/UsbSerialLibrary/src/com/hoho/android/usbserial/driver/UsbSerialProber.java
+++ b/UsbSerialLibrary/src/com/hoho/android/usbserial/driver/UsbSerialProber.java
@@ -97,6 +97,21 @@ public enum UsbSerialProber {
final UsbSerialDriver driver = new Cp2102SerialDriver(usbDevice, connection);
return Collections.singletonList(driver);
}
+ },
+
+ PROLIFIC_SERIAL {
+ @Override
+ public List probe(final UsbManager manager, final UsbDevice usbDevice) {
+ if (!testIfSupported(usbDevice, ProlificSerialDriver.getSupportedDevices())) {
+ return Collections.emptyList();
+ }
+ final UsbDeviceConnection connection = manager.openDevice(usbDevice);
+ if (connection == null) {
+ return Collections.emptyList();
+ }
+ final UsbSerialDriver driver = new ProlificSerialDriver(usbDevice, connection);
+ return Collections.singletonList(driver);
+ }
};
/**