kopia lustrzana https://github.com/mik3y/usb-serial-for-android
API refactor, adding UsbSerialPort interface.
- UsbSerialDriver is now a discrete interface. - UsbSerialDriver provides getPorts() method, returning one or more usable UsbSerialPort. - Use of UsbDeviceConnection is deferred until open(), making it possible to probe for ports without permission from Android. (Thanks to Felix for inspiring some of these changes).pull/73/head
rodzic
d9db4e3607
commit
8abc3be1f1
|
@ -1,4 +1,5 @@
|
|||
/* Copyright 2011 Google Inc.
|
||||
/* 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
|
||||
|
@ -15,7 +16,7 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
* Project home page: http://code.google.com/p/usb-serial-for-android/
|
||||
* Project home page: https://github.com/mik3y/usb-serial-for-android
|
||||
*/
|
||||
|
||||
package com.hoho.android.usbserial.examples;
|
||||
|
@ -41,6 +42,7 @@ import android.widget.TextView;
|
|||
import android.widget.TwoLineListItem;
|
||||
|
||||
import com.hoho.android.usbserial.driver.UsbSerialDriver;
|
||||
import com.hoho.android.usbserial.driver.UsbSerialPort;
|
||||
import com.hoho.android.usbserial.driver.UsbSerialProber;
|
||||
import com.hoho.android.usbserial.util.HexDump;
|
||||
|
||||
|
@ -80,19 +82,8 @@ public class DeviceListActivity extends Activity {
|
|||
|
||||
};
|
||||
|
||||
/** Simple container for a UsbDevice and its driver. */
|
||||
private static class DeviceEntry {
|
||||
public UsbDevice device;
|
||||
public UsbSerialDriver driver;
|
||||
|
||||
DeviceEntry(UsbDevice device, UsbSerialDriver driver) {
|
||||
this.device = device;
|
||||
this.driver = driver;
|
||||
}
|
||||
}
|
||||
|
||||
private List<DeviceEntry> mEntries = new ArrayList<DeviceEntry>();
|
||||
private ArrayAdapter<DeviceEntry> mAdapter;
|
||||
private List<UsbSerialPort> mEntries = new ArrayList<UsbSerialPort>();
|
||||
private ArrayAdapter<UsbSerialPort> mAdapter;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -104,7 +95,8 @@ public class DeviceListActivity extends Activity {
|
|||
mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
|
||||
mProgressBarTitle = (TextView) findViewById(R.id.progressBarTitle);
|
||||
|
||||
mAdapter = new ArrayAdapter<DeviceEntry>(this, android.R.layout.simple_expandable_list_item_2, mEntries) {
|
||||
mAdapter = new ArrayAdapter<UsbSerialPort>(this,
|
||||
android.R.layout.simple_expandable_list_item_2, mEntries) {
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
final TwoLineListItem row;
|
||||
|
@ -116,14 +108,16 @@ public class DeviceListActivity extends Activity {
|
|||
row = (TwoLineListItem) convertView;
|
||||
}
|
||||
|
||||
final DeviceEntry entry = mEntries.get(position);
|
||||
final UsbSerialPort port = mEntries.get(position);
|
||||
final UsbSerialDriver driver = port.getDriver();
|
||||
final UsbDevice device = driver.getDevice();
|
||||
|
||||
final String title = String.format("Vendor %s Product %s",
|
||||
HexDump.toHexString((short) entry.device.getVendorId()),
|
||||
HexDump.toHexString((short) entry.device.getProductId()));
|
||||
HexDump.toHexString((short) device.getVendorId()),
|
||||
HexDump.toHexString((short) device.getProductId()));
|
||||
row.getText1().setText(title);
|
||||
|
||||
final String subtitle = entry.driver != null ?
|
||||
entry.driver.getClass().getSimpleName() : "No Driver";
|
||||
final String subtitle = driver.getClass().getSimpleName();
|
||||
row.getText2().setText(subtitle);
|
||||
|
||||
return row;
|
||||
|
@ -141,14 +135,8 @@ public class DeviceListActivity extends Activity {
|
|||
return;
|
||||
}
|
||||
|
||||
final DeviceEntry entry = mEntries.get(position);
|
||||
final UsbSerialDriver driver = entry.driver;
|
||||
if (driver == null) {
|
||||
Log.d(TAG, "No driver.");
|
||||
return;
|
||||
}
|
||||
|
||||
showConsoleActivity(driver);
|
||||
final UsbSerialPort port = mEntries.get(position);
|
||||
showConsoleActivity(port);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -168,31 +156,28 @@ public class DeviceListActivity extends Activity {
|
|||
private void refreshDeviceList() {
|
||||
showProgressBar();
|
||||
|
||||
new AsyncTask<Void, Void, List<DeviceEntry>>() {
|
||||
new AsyncTask<Void, Void, List<UsbSerialPort>>() {
|
||||
@Override
|
||||
protected List<DeviceEntry> doInBackground(Void... params) {
|
||||
protected List<UsbSerialPort> doInBackground(Void... params) {
|
||||
Log.d(TAG, "Refreshing device list ...");
|
||||
SystemClock.sleep(1000);
|
||||
final List<DeviceEntry> result = new ArrayList<DeviceEntry>();
|
||||
for (final UsbDevice device : mUsbManager.getDeviceList().values()) {
|
||||
final List<UsbSerialDriver> drivers =
|
||||
UsbSerialProber.probeSingleDevice(mUsbManager, device);
|
||||
Log.d(TAG, "Found usb device: " + device);
|
||||
if (drivers.isEmpty()) {
|
||||
Log.d(TAG, " - No UsbSerialDriver available.");
|
||||
result.add(new DeviceEntry(device, null));
|
||||
} else {
|
||||
for (UsbSerialDriver driver : drivers) {
|
||||
Log.d(TAG, " + " + driver);
|
||||
result.add(new DeviceEntry(device, driver));
|
||||
}
|
||||
}
|
||||
|
||||
final List<UsbSerialDriver> drivers =
|
||||
UsbSerialProber.getDefaultProber().findAllDrivers(mUsbManager);
|
||||
|
||||
final List<UsbSerialPort> result = new ArrayList<UsbSerialPort>();
|
||||
for (final UsbSerialDriver driver : drivers) {
|
||||
final List<UsbSerialPort> ports = driver.getPorts();
|
||||
Log.d(TAG, String.format("+ %s: %s port%s",
|
||||
driver, Integer.valueOf(ports.size()), ports.size() == 1 ? "" : "s"));
|
||||
result.addAll(ports);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(List<DeviceEntry> result) {
|
||||
protected void onPostExecute(List<UsbSerialPort> result) {
|
||||
mEntries.clear();
|
||||
mEntries.addAll(result);
|
||||
mAdapter.notifyDataSetChanged();
|
||||
|
@ -214,8 +199,8 @@ public class DeviceListActivity extends Activity {
|
|||
mProgressBar.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
private void showConsoleActivity(UsbSerialDriver driver) {
|
||||
SerialConsoleActivity.show(this, driver);
|
||||
private void showConsoleActivity(UsbSerialPort port) {
|
||||
SerialConsoleActivity.show(this, port);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
/* Copyright 2011 Google Inc.
|
||||
/* 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
|
||||
|
@ -15,7 +16,7 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
* Project home page: http://code.google.com/p/usb-serial-for-android/
|
||||
* Project home page: https://github.com/mik3y/usb-serial-for-android
|
||||
*/
|
||||
|
||||
package com.hoho.android.usbserial.examples;
|
||||
|
@ -23,12 +24,14 @@ package com.hoho.android.usbserial.examples;
|
|||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.hardware.usb.UsbDeviceConnection;
|
||||
import android.hardware.usb.UsbManager;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.hoho.android.usbserial.driver.UsbSerialDriver;
|
||||
import com.hoho.android.usbserial.driver.UsbSerialPort;
|
||||
import com.hoho.android.usbserial.util.HexDump;
|
||||
import com.hoho.android.usbserial.util.SerialInputOutputManager;
|
||||
|
||||
|
@ -37,7 +40,7 @@ import java.util.concurrent.ExecutorService;
|
|||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* Monitors a single {@link UsbSerialDriver} instance, showing all data
|
||||
* Monitors a single {@link UsbSerialPort} instance, showing all data
|
||||
* received.
|
||||
*
|
||||
* @author mike wakerly (opensource@hoho.com)
|
||||
|
@ -48,7 +51,7 @@ public class SerialConsoleActivity extends Activity {
|
|||
|
||||
/**
|
||||
* Driver instance, passed in statically via
|
||||
* {@link #show(Context, UsbSerialDriver)}.
|
||||
* {@link #show(Context, UsbSerialPort)}.
|
||||
*
|
||||
* <p/>
|
||||
* This is a devious hack; it'd be cleaner to re-create the driver using
|
||||
|
@ -56,7 +59,7 @@ public class SerialConsoleActivity extends Activity {
|
|||
* can get away with it because both activities will run in the same
|
||||
* process, and this is a simple demo.
|
||||
*/
|
||||
private static UsbSerialDriver sDriver = null;
|
||||
private static UsbSerialPort sPort = null;
|
||||
|
||||
private TextView mTitleTextView;
|
||||
private TextView mDumpTextView;
|
||||
|
@ -98,13 +101,13 @@ public class SerialConsoleActivity extends Activity {
|
|||
protected void onPause() {
|
||||
super.onPause();
|
||||
stopIoManager();
|
||||
if (sDriver != null) {
|
||||
if (sPort != null) {
|
||||
try {
|
||||
sDriver.close();
|
||||
sPort.close();
|
||||
} catch (IOException e) {
|
||||
// Ignore.
|
||||
}
|
||||
sDriver = null;
|
||||
sPort = null;
|
||||
}
|
||||
finish();
|
||||
}
|
||||
|
@ -112,25 +115,33 @@ public class SerialConsoleActivity extends Activity {
|
|||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
Log.d(TAG, "Resumed, sDriver=" + sDriver);
|
||||
if (sDriver == null) {
|
||||
Log.d(TAG, "Resumed, port=" + sPort);
|
||||
if (sPort == null) {
|
||||
mTitleTextView.setText("No serial device.");
|
||||
} else {
|
||||
final UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
|
||||
|
||||
UsbDeviceConnection connection = usbManager.openDevice(sPort.getDriver().getDevice());
|
||||
if (connection == null) {
|
||||
mTitleTextView.setText("Opening device failed");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
sDriver.open();
|
||||
sDriver.setParameters(115200, 8, UsbSerialDriver.STOPBITS_1, UsbSerialDriver.PARITY_NONE);
|
||||
sPort.open(connection);
|
||||
sPort.setParameters(115200, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error setting up device: " + e.getMessage(), e);
|
||||
mTitleTextView.setText("Error opening device: " + e.getMessage());
|
||||
try {
|
||||
sDriver.close();
|
||||
sPort.close();
|
||||
} catch (IOException e2) {
|
||||
// Ignore.
|
||||
}
|
||||
sDriver = null;
|
||||
sPort = null;
|
||||
return;
|
||||
}
|
||||
mTitleTextView.setText("Serial device: " + sDriver.getClass().getSimpleName());
|
||||
mTitleTextView.setText("Serial device: " + sPort.getClass().getSimpleName());
|
||||
}
|
||||
onDeviceStateChange();
|
||||
}
|
||||
|
@ -144,9 +155,9 @@ public class SerialConsoleActivity extends Activity {
|
|||
}
|
||||
|
||||
private void startIoManager() {
|
||||
if (sDriver != null) {
|
||||
if (sPort != null) {
|
||||
Log.i(TAG, "Starting io manager ..");
|
||||
mSerialIoManager = new SerialInputOutputManager(sDriver, mListener);
|
||||
mSerialIoManager = new SerialInputOutputManager(sPort, mListener);
|
||||
mExecutor.submit(mSerialIoManager);
|
||||
}
|
||||
}
|
||||
|
@ -169,8 +180,8 @@ public class SerialConsoleActivity extends Activity {
|
|||
* @param context
|
||||
* @param driver
|
||||
*/
|
||||
static void show(Context context, UsbSerialDriver driver) {
|
||||
sDriver = driver;
|
||||
static void show(Context context, UsbSerialPort port) {
|
||||
sPort = port;
|
||||
final Intent intent = new Intent(context, SerialConsoleActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NO_HISTORY);
|
||||
context.startActivity(intent);
|
||||
|
|
|
@ -1,3 +1,24 @@
|
|||
/* 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.hoho.android.usbserial.driver;
|
||||
|
||||
import android.hardware.usb.UsbConstants;
|
||||
|
@ -8,7 +29,9 @@ import android.hardware.usb.UsbInterface;
|
|||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
|
@ -19,201 +42,243 @@ import java.util.Map;
|
|||
* href="http://www.usb.org/developers/devclass_docs/usbcdc11.pdf">Universal
|
||||
* Serial Bus Class Definitions for Communication Devices, v1.1</a>
|
||||
*/
|
||||
public class CdcAcmSerialDriver extends CommonUsbSerialDriver {
|
||||
public class CdcAcmSerialDriver implements UsbSerialDriver {
|
||||
|
||||
private final String TAG = CdcAcmSerialDriver.class.getSimpleName();
|
||||
|
||||
private UsbInterface mControlInterface;
|
||||
private UsbInterface mDataInterface;
|
||||
private final UsbDevice mDevice;
|
||||
private final UsbSerialPort mPort;
|
||||
|
||||
private UsbEndpoint mControlEndpoint;
|
||||
private UsbEndpoint mReadEndpoint;
|
||||
private UsbEndpoint mWriteEndpoint;
|
||||
|
||||
private boolean mRts = false;
|
||||
private boolean mDtr = false;
|
||||
|
||||
private static final int USB_RECIP_INTERFACE = 0x01;
|
||||
private static final int USB_RT_ACM = UsbConstants.USB_TYPE_CLASS | USB_RECIP_INTERFACE;
|
||||
|
||||
private static final int SET_LINE_CODING = 0x20; // USB CDC 1.1 section 6.2
|
||||
private static final int GET_LINE_CODING = 0x21;
|
||||
private static final int SET_CONTROL_LINE_STATE = 0x22;
|
||||
private static final int SEND_BREAK = 0x23;
|
||||
|
||||
public CdcAcmSerialDriver(UsbDevice device, UsbDeviceConnection connection) {
|
||||
super(device, connection);
|
||||
public CdcAcmSerialDriver(UsbDevice device) {
|
||||
mDevice = device;
|
||||
mPort = new CdcAdcmSerialPort(device);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open() throws IOException {
|
||||
Log.d(TAG, "claiming interfaces, count=" + mDevice.getInterfaceCount());
|
||||
public UsbDevice getDevice() {
|
||||
return mDevice;
|
||||
}
|
||||
|
||||
Log.d(TAG, "Claiming control interface.");
|
||||
mControlInterface = mDevice.getInterface(0);
|
||||
Log.d(TAG, "Control iface=" + mControlInterface);
|
||||
// class should be USB_CLASS_COMM
|
||||
@Override
|
||||
public List<UsbSerialPort> getPorts() {
|
||||
return Collections.singletonList(mPort);
|
||||
}
|
||||
|
||||
if (!mConnection.claimInterface(mControlInterface, true)) {
|
||||
throw new IOException("Could not claim control interface.");
|
||||
class CdcAdcmSerialPort extends CommonUsbSerialPort {
|
||||
|
||||
private UsbInterface mControlInterface;
|
||||
private UsbInterface mDataInterface;
|
||||
|
||||
private UsbEndpoint mControlEndpoint;
|
||||
private UsbEndpoint mReadEndpoint;
|
||||
private UsbEndpoint mWriteEndpoint;
|
||||
|
||||
private boolean mRts = false;
|
||||
private boolean mDtr = false;
|
||||
|
||||
private static final int USB_RECIP_INTERFACE = 0x01;
|
||||
private static final int USB_RT_ACM = UsbConstants.USB_TYPE_CLASS | USB_RECIP_INTERFACE;
|
||||
|
||||
private static final int SET_LINE_CODING = 0x20; // USB CDC 1.1 section 6.2
|
||||
private static final int GET_LINE_CODING = 0x21;
|
||||
private static final int SET_CONTROL_LINE_STATE = 0x22;
|
||||
private static final int SEND_BREAK = 0x23;
|
||||
|
||||
public CdcAdcmSerialPort(UsbDevice device) {
|
||||
super(device);
|
||||
}
|
||||
mControlEndpoint = mControlInterface.getEndpoint(0);
|
||||
Log.d(TAG, "Control endpoint direction: " + mControlEndpoint.getDirection());
|
||||
|
||||
Log.d(TAG, "Claiming data interface.");
|
||||
mDataInterface = mDevice.getInterface(1);
|
||||
Log.d(TAG, "data iface=" + mDataInterface);
|
||||
// class should be USB_CLASS_CDC_DATA
|
||||
|
||||
if (!mConnection.claimInterface(mDataInterface, true)) {
|
||||
throw new IOException("Could not claim data interface.");
|
||||
@Override
|
||||
public UsbSerialDriver getDriver() {
|
||||
return CdcAcmSerialDriver.this;
|
||||
}
|
||||
mReadEndpoint = mDataInterface.getEndpoint(1);
|
||||
Log.d(TAG, "Read endpoint direction: " + mReadEndpoint.getDirection());
|
||||
mWriteEndpoint = mDataInterface.getEndpoint(0);
|
||||
Log.d(TAG, "Write endpoint direction: " + mWriteEndpoint.getDirection());
|
||||
}
|
||||
|
||||
private int sendAcmControlMessage(int request, int value, byte[] buf) {
|
||||
return mConnection.controlTransfer(
|
||||
USB_RT_ACM, request, value, 0, buf, buf != null ? buf.length : 0, 5000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
mConnection.close();
|
||||
}
|
||||
|
||||
@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;
|
||||
@Override
|
||||
public void open(UsbDeviceConnection connection) throws IOException {
|
||||
if (mConnection != null) {
|
||||
throw new IOException("Already open");
|
||||
}
|
||||
|
||||
mConnection = connection;
|
||||
boolean opened = false;
|
||||
try {
|
||||
Log.d(TAG, "claiming interfaces, count=" + mDevice.getInterfaceCount());
|
||||
mControlInterface = mDevice.getInterface(0);
|
||||
Log.d(TAG, "Control iface=" + mControlInterface);
|
||||
// class should be USB_CLASS_COMM
|
||||
|
||||
if (!mConnection.claimInterface(mControlInterface, true)) {
|
||||
throw new IOException("Could not claim control interface.");
|
||||
}
|
||||
mControlEndpoint = mControlInterface.getEndpoint(0);
|
||||
Log.d(TAG, "Control endpoint direction: " + mControlEndpoint.getDirection());
|
||||
|
||||
Log.d(TAG, "Claiming data interface.");
|
||||
mDataInterface = mDevice.getInterface(1);
|
||||
Log.d(TAG, "data iface=" + mDataInterface);
|
||||
// class should be USB_CLASS_CDC_DATA
|
||||
|
||||
if (!mConnection.claimInterface(mDataInterface, true)) {
|
||||
throw new IOException("Could not claim data interface.");
|
||||
}
|
||||
mReadEndpoint = mDataInterface.getEndpoint(1);
|
||||
Log.d(TAG, "Read endpoint direction: " + mReadEndpoint.getDirection());
|
||||
mWriteEndpoint = mDataInterface.getEndpoint(0);
|
||||
Log.d(TAG, "Write endpoint direction: " + mWriteEndpoint.getDirection());
|
||||
opened = true;
|
||||
} finally {
|
||||
if (!opened) {
|
||||
mConnection = null;
|
||||
}
|
||||
}
|
||||
System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
|
||||
}
|
||||
return numBytesRead;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write(byte[] src, int timeoutMillis) throws IOException {
|
||||
// TODO(mikey): Nearly identical to FtdiSerial write. Refactor.
|
||||
int offset = 0;
|
||||
private int sendAcmControlMessage(int request, int value, byte[] buf) {
|
||||
return mConnection.controlTransfer(
|
||||
USB_RT_ACM, request, value, 0, buf, buf != null ? buf.length : 0, 5000);
|
||||
}
|
||||
|
||||
while (offset < src.length) {
|
||||
final int writeLength;
|
||||
final int amtWritten;
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (mConnection == null) {
|
||||
throw new IOException("Already closed");
|
||||
}
|
||||
mConnection.close();
|
||||
mConnection = null;
|
||||
}
|
||||
|
||||
synchronized (mWriteBufferLock) {
|
||||
final byte[] writeBuffer;
|
||||
@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;
|
||||
}
|
||||
|
||||
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;
|
||||
@Override
|
||||
public int write(byte[] src, int timeoutMillis) throws IOException {
|
||||
// TODO(mikey): Nearly identical to FtdiSerial write. Refactor.
|
||||
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);
|
||||
}
|
||||
|
||||
amtWritten = mConnection.bulkTransfer(mWriteEndpoint, writeBuffer, writeLength,
|
||||
timeoutMillis);
|
||||
Log.d(TAG, "Wrote amt=" + amtWritten + " attempted=" + writeLength);
|
||||
offset += amtWritten;
|
||||
}
|
||||
if (amtWritten <= 0) {
|
||||
throw new IOException("Error writing " + writeLength
|
||||
+ " bytes at offset " + offset + " length=" + src.length);
|
||||
return offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameters(int baudRate, int dataBits, int stopBits, int parity) {
|
||||
byte stopBitsByte;
|
||||
switch (stopBits) {
|
||||
case STOPBITS_1: stopBitsByte = 0; break;
|
||||
case STOPBITS_1_5: stopBitsByte = 1; break;
|
||||
case STOPBITS_2: stopBitsByte = 2; break;
|
||||
default: throw new IllegalArgumentException("Bad value for stopBits: " + stopBits);
|
||||
}
|
||||
|
||||
Log.d(TAG, "Wrote amt=" + amtWritten + " attempted=" + writeLength);
|
||||
offset += amtWritten;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
byte parityBitesByte;
|
||||
switch (parity) {
|
||||
case PARITY_NONE: parityBitesByte = 0; break;
|
||||
case PARITY_ODD: parityBitesByte = 1; break;
|
||||
case PARITY_EVEN: parityBitesByte = 2; break;
|
||||
case PARITY_MARK: parityBitesByte = 3; break;
|
||||
case PARITY_SPACE: parityBitesByte = 4; break;
|
||||
default: throw new IllegalArgumentException("Bad value for parity: " + parity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameters(int baudRate, int dataBits, int stopBits, int parity) {
|
||||
byte stopBitsByte;
|
||||
switch (stopBits) {
|
||||
case STOPBITS_1: stopBitsByte = 0; break;
|
||||
case STOPBITS_1_5: stopBitsByte = 1; break;
|
||||
case STOPBITS_2: stopBitsByte = 2; break;
|
||||
default: throw new IllegalArgumentException("Bad value for stopBits: " + stopBits);
|
||||
byte[] msg = {
|
||||
(byte) ( baudRate & 0xff),
|
||||
(byte) ((baudRate >> 8 ) & 0xff),
|
||||
(byte) ((baudRate >> 16) & 0xff),
|
||||
(byte) ((baudRate >> 24) & 0xff),
|
||||
stopBitsByte,
|
||||
parityBitesByte,
|
||||
(byte) dataBits};
|
||||
sendAcmControlMessage(SET_LINE_CODING, 0, msg);
|
||||
}
|
||||
|
||||
byte parityBitesByte;
|
||||
switch (parity) {
|
||||
case PARITY_NONE: parityBitesByte = 0; break;
|
||||
case PARITY_ODD: parityBitesByte = 1; break;
|
||||
case PARITY_EVEN: parityBitesByte = 2; break;
|
||||
case PARITY_MARK: parityBitesByte = 3; break;
|
||||
case PARITY_SPACE: parityBitesByte = 4; break;
|
||||
default: throw new IllegalArgumentException("Bad value for parity: " + parity);
|
||||
@Override
|
||||
public boolean getCD() throws IOException {
|
||||
return false; // TODO
|
||||
}
|
||||
|
||||
byte[] msg = {
|
||||
(byte) ( baudRate & 0xff),
|
||||
(byte) ((baudRate >> 8 ) & 0xff),
|
||||
(byte) ((baudRate >> 16) & 0xff),
|
||||
(byte) ((baudRate >> 24) & 0xff),
|
||||
stopBitsByte,
|
||||
parityBitesByte,
|
||||
(byte) dataBits};
|
||||
sendAcmControlMessage(SET_LINE_CODING, 0, msg);
|
||||
}
|
||||
@Override
|
||||
public boolean getCTS() throws IOException {
|
||||
return false; // TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getCD() throws IOException {
|
||||
return false; // TODO
|
||||
}
|
||||
@Override
|
||||
public boolean getDSR() throws IOException {
|
||||
return false; // TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getCTS() throws IOException {
|
||||
return false; // TODO
|
||||
}
|
||||
@Override
|
||||
public boolean getDTR() throws IOException {
|
||||
return mDtr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getDSR() throws IOException {
|
||||
return false; // TODO
|
||||
}
|
||||
@Override
|
||||
public void setDTR(boolean value) throws IOException {
|
||||
mDtr = value;
|
||||
setDtrRts();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getDTR() throws IOException {
|
||||
return mDtr;
|
||||
}
|
||||
@Override
|
||||
public boolean getRI() throws IOException {
|
||||
return false; // TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDTR(boolean value) throws IOException {
|
||||
mDtr = value;
|
||||
setDtrRts();
|
||||
}
|
||||
@Override
|
||||
public boolean getRTS() throws IOException {
|
||||
return mRts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRI() throws IOException {
|
||||
return false; // TODO
|
||||
}
|
||||
@Override
|
||||
public void setRTS(boolean value) throws IOException {
|
||||
mRts = value;
|
||||
setDtrRts();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRTS() throws IOException {
|
||||
return mRts;
|
||||
}
|
||||
private void setDtrRts() {
|
||||
int value = (mRts ? 0x2 : 0) | (mDtr ? 0x1 : 0);
|
||||
sendAcmControlMessage(SET_CONTROL_LINE_STATE, value, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRTS(boolean value) throws IOException {
|
||||
mRts = value;
|
||||
setDtrRts();
|
||||
}
|
||||
|
||||
private void setDtrRts() {
|
||||
int value = (mRts ? 0x2 : 0) | (mDtr ? 0x1 : 0);
|
||||
sendAcmControlMessage(SET_CONTROL_LINE_STATE, value, null);
|
||||
}
|
||||
|
||||
public static Map<Integer, int[]> getSupportedDevices() {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
/* Copyright 2013 Google Inc.
|
||||
/* 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
|
||||
|
@ -15,7 +16,7 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
* Project home page: http://code.google.com/p/usb-serial-for-android/
|
||||
* Project home page: https://github.com/mik3y/usb-serial-for-android
|
||||
*/
|
||||
|
||||
package com.hoho.android.usbserial.driver;
|
||||
|
@ -30,13 +31,15 @@ import java.io.IOException;
|
|||
*
|
||||
* @author mike wakerly (opensource@hoho.com)
|
||||
*/
|
||||
abstract class CommonUsbSerialDriver implements UsbSerialDriver {
|
||||
abstract class CommonUsbSerialPort implements UsbSerialPort {
|
||||
|
||||
public static final int DEFAULT_READ_BUFFER_SIZE = 16 * 1024;
|
||||
public static final int DEFAULT_WRITE_BUFFER_SIZE = 16 * 1024;
|
||||
|
||||
protected final UsbDevice mDevice;
|
||||
protected final UsbDeviceConnection mConnection;
|
||||
|
||||
// non-null when open()
|
||||
protected UsbDeviceConnection mConnection = null;
|
||||
|
||||
protected final Object mReadBufferLock = new Object();
|
||||
protected final Object mWriteBufferLock = new Object();
|
||||
|
@ -47,9 +50,8 @@ abstract class CommonUsbSerialDriver implements UsbSerialDriver {
|
|||
/** Internal write buffer. Guarded by {@link #mWriteBufferLock}. */
|
||||
protected byte[] mWriteBuffer;
|
||||
|
||||
public CommonUsbSerialDriver(UsbDevice device, UsbDeviceConnection connection) {
|
||||
public CommonUsbSerialPort(UsbDevice device) {
|
||||
mDevice = device;
|
||||
mConnection = connection;
|
||||
|
||||
mReadBuffer = new byte[DEFAULT_READ_BUFFER_SIZE];
|
||||
mWriteBuffer = new byte[DEFAULT_WRITE_BUFFER_SIZE];
|
||||
|
@ -95,7 +97,7 @@ abstract class CommonUsbSerialDriver implements UsbSerialDriver {
|
|||
}
|
||||
|
||||
@Override
|
||||
public abstract void open() throws IOException;
|
||||
public abstract void open(UsbDeviceConnection connection) throws IOException;
|
||||
|
||||
@Override
|
||||
public abstract void close() throws IOException;
|
|
@ -1,8 +1,25 @@
|
|||
package com.hoho.android.usbserial.driver;
|
||||
/* 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
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
package com.hoho.android.usbserial.driver;
|
||||
|
||||
import android.hardware.usb.UsbConstants;
|
||||
import android.hardware.usb.UsbDevice;
|
||||
|
@ -11,273 +28,323 @@ import android.hardware.usb.UsbEndpoint;
|
|||
import android.hardware.usb.UsbInterface;
|
||||
import android.util.Log;
|
||||
|
||||
public class Cp2102SerialDriver extends CommonUsbSerialDriver {
|
||||
private static final String TAG = Cp2102SerialDriver.class.getSimpleName();
|
||||
|
||||
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;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
private UsbEndpoint mReadEndpoint;
|
||||
private UsbEndpoint mWriteEndpoint;
|
||||
|
||||
public Cp2102SerialDriver(UsbDevice device, UsbDeviceConnection connection) {
|
||||
super(device, connection);
|
||||
}
|
||||
|
||||
private int setConfigSingle(int request, int value) {
|
||||
return mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, request, value,
|
||||
0, null, 0, USB_WRITE_TIMEOUT_MILLIS);
|
||||
public class Cp2102SerialDriver implements UsbSerialDriver {
|
||||
|
||||
private static final String TAG = Cp2102SerialDriver.class.getSimpleName();
|
||||
|
||||
private final UsbDevice mDevice;
|
||||
private final UsbSerialPort mPort;
|
||||
|
||||
public Cp2102SerialDriver(UsbDevice device) {
|
||||
mDevice = device;
|
||||
mPort = new Cp2102SerialPort(mDevice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open() throws IOException {
|
||||
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;
|
||||
public UsbDevice getDevice() {
|
||||
return mDevice;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UsbSerialPort> getPorts() {
|
||||
return Collections.singletonList(mPort);
|
||||
}
|
||||
|
||||
class Cp2102SerialPort 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 Cp2102SerialPort(UsbDevice device) {
|
||||
super(device);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UsbSerialDriver getDriver() {
|
||||
return Cp2102SerialDriver.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 {
|
||||
mWriteEndpoint = ep;
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
setConfigSingle(SILABSER_IFC_ENABLE_REQUEST_CODE, UART_DISABLE);
|
||||
mConnection.close();
|
||||
}
|
||||
|
||||
@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;
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
while (offset < src.length) {
|
||||
final int writeLength;
|
||||
final int amtWritten;
|
||||
@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;
|
||||
}
|
||||
|
||||
synchronized (mWriteBufferLock) {
|
||||
final byte[] writeBuffer;
|
||||
@Override
|
||||
public int write(byte[] src, int timeoutMillis) throws IOException {
|
||||
int offset = 0;
|
||||
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
amtWritten = mConnection.bulkTransfer(mWriteEndpoint, writeBuffer, writeLength,
|
||||
timeoutMillis);
|
||||
Log.d(TAG, "Wrote amt=" + amtWritten + " attempted=" + writeLength);
|
||||
offset += amtWritten;
|
||||
}
|
||||
if (amtWritten <= 0) {
|
||||
throw new IOException("Error writing " + writeLength
|
||||
+ " bytes at offset " + offset + " length=" + src.length);
|
||||
return offset;
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
Log.d(TAG, "Wrote amt=" + amtWritten + " attempted=" + writeLength);
|
||||
offset += amtWritten;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
setConfigSingle(SILABSER_SET_LINE_CTL_REQUEST_CODE, configDataBits);
|
||||
|
||||
int configParityBits = 0; // PARITY_NONE
|
||||
switch (parity) {
|
||||
case PARITY_ODD:
|
||||
configParityBits |= 0x0010;
|
||||
break;
|
||||
case PARITY_EVEN:
|
||||
configParityBits |= 0x0020;
|
||||
break;
|
||||
}
|
||||
setConfigSingle(SILABSER_SET_LINE_CTL_REQUEST_CODE, configParityBits);
|
||||
|
||||
int configStopBits = 0;
|
||||
switch (stopBits) {
|
||||
case STOPBITS_1:
|
||||
configStopBits |= 0;
|
||||
break;
|
||||
case STOPBITS_2:
|
||||
configStopBits |= 2;
|
||||
break;
|
||||
}
|
||||
setConfigSingle(SILABSER_SET_LINE_CTL_REQUEST_CODE, configStopBits);
|
||||
}
|
||||
|
||||
@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 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;
|
||||
@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;
|
||||
}
|
||||
setConfigSingle(SILABSER_SET_LINE_CTL_REQUEST_CODE, configDataBits);
|
||||
|
||||
int configParityBits = 0; // PARITY_NONE
|
||||
switch (parity) {
|
||||
case PARITY_ODD:
|
||||
configParityBits |= 0x0010;
|
||||
break;
|
||||
case PARITY_EVEN:
|
||||
configParityBits |= 0x0020;
|
||||
break;
|
||||
}
|
||||
setConfigSingle(SILABSER_SET_LINE_CTL_REQUEST_CODE, configParityBits);
|
||||
|
||||
int configStopBits = 0;
|
||||
switch (stopBits) {
|
||||
case STOPBITS_1:
|
||||
configStopBits |= 0;
|
||||
break;
|
||||
case STOPBITS_2:
|
||||
configStopBits |= 2;
|
||||
break;
|
||||
}
|
||||
setConfigSingle(SILABSER_SET_LINE_CTL_REQUEST_CODE, configStopBits);
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRTS(boolean value) throws IOException {
|
||||
}
|
||||
|
||||
public static Map<Integer, int[]> getSupportedDevices() {
|
||||
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<Integer, int[]>();
|
||||
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_SILAB),
|
||||
|
@ -287,5 +354,4 @@ public class Cp2102SerialDriver extends CommonUsbSerialDriver {
|
|||
return supportedDevices;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
/* Copyright 2011 Google Inc.
|
||||
/* 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
|
||||
|
@ -15,7 +16,7 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
* Project home page: http://code.google.com/p/usb-serial-for-android/
|
||||
* Project home page: https://github.com/mik3y/usb-serial-for-android
|
||||
*/
|
||||
|
||||
package com.hoho.android.usbserial.driver;
|
||||
|
@ -31,14 +32,16 @@ import com.hoho.android.usbserial.util.HexDump;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A {@link CommonUsbSerialDriver} implementation for a variety of FTDI devices
|
||||
* A {@link CommonUsbSerialPort} implementation for a variety of FTDI devices
|
||||
* <p>
|
||||
* This driver is based on
|
||||
* <a href="http://www.intra2net.com/en/developer/libftdi">libftdi</a>, and is
|
||||
* This driver is based on <a
|
||||
* href="http://www.intra2net.com/en/developer/libftdi">libftdi</a>, and is
|
||||
* copyright and subject to the following terms:
|
||||
*
|
||||
* <pre>
|
||||
|
@ -58,9 +61,11 @@ import java.util.Map;
|
|||
* unsupported devices. Devices listed as "supported" support the following
|
||||
* features:
|
||||
* <ul>
|
||||
* <li>Read and write of serial data (see {@link #read(byte[], int)} and
|
||||
* {@link #write(byte[], int)}.
|
||||
* <li>Setting baud rate (see {@link #setBaudRate(int)}).
|
||||
* <li>Read and write of serial data (see
|
||||
* {@link CommonUsbSerialPort#read(byte[], int)} and
|
||||
* {@link CommonUsbSerialPort#write(byte[], int)}.</li>
|
||||
* <li>Setting serial line parameters (see
|
||||
* {@link CommonUsbSerialPort#setParameters(int, int, int, int)}.</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* <p>
|
||||
|
@ -82,73 +87,15 @@ import java.util.Map;
|
|||
* </p>
|
||||
*
|
||||
* @author mike wakerly (opensource@hoho.com)
|
||||
* @see <a href="http://code.google.com/p/usb-serial-for-android/">USB Serial
|
||||
* for Android project page</a>
|
||||
* @see <a href="https://github.com/mik3y/usb-serial-for-android">USB Serial
|
||||
* for Android project page</a>
|
||||
* @see <a href="http://www.ftdichip.com/">FTDI Homepage</a>
|
||||
* @see <a href="http://www.intra2net.com/en/developer/libftdi">libftdi</a>
|
||||
*/
|
||||
public class FtdiSerialDriver extends CommonUsbSerialDriver {
|
||||
public class FtdiSerialDriver implements UsbSerialDriver {
|
||||
|
||||
public static final int USB_TYPE_STANDARD = 0x00 << 5;
|
||||
public static final int USB_TYPE_CLASS = 0x01 << 5;
|
||||
public static final int USB_TYPE_VENDOR = 0x02 << 5;
|
||||
public static final int USB_TYPE_RESERVED = 0x03 << 5;
|
||||
|
||||
public static final int USB_RECIP_DEVICE = 0x00;
|
||||
public static final int USB_RECIP_INTERFACE = 0x01;
|
||||
public static final int USB_RECIP_ENDPOINT = 0x02;
|
||||
public static final int USB_RECIP_OTHER = 0x03;
|
||||
|
||||
public static final int USB_ENDPOINT_IN = 0x80;
|
||||
public static final int USB_ENDPOINT_OUT = 0x00;
|
||||
|
||||
public static final int USB_WRITE_TIMEOUT_MILLIS = 5000;
|
||||
public static final int USB_READ_TIMEOUT_MILLIS = 5000;
|
||||
|
||||
// From ftdi.h
|
||||
/**
|
||||
* Reset the port.
|
||||
*/
|
||||
private static final int SIO_RESET_REQUEST = 0;
|
||||
|
||||
/**
|
||||
* Set the modem control register.
|
||||
*/
|
||||
private static final int SIO_MODEM_CTRL_REQUEST = 1;
|
||||
|
||||
/**
|
||||
* Set flow control register.
|
||||
*/
|
||||
private static final int SIO_SET_FLOW_CTRL_REQUEST = 2;
|
||||
|
||||
/**
|
||||
* Set baud rate.
|
||||
*/
|
||||
private static final int SIO_SET_BAUD_RATE_REQUEST = 3;
|
||||
|
||||
/**
|
||||
* Set the data characteristics of the port.
|
||||
*/
|
||||
private static final int SIO_SET_DATA_REQUEST = 4;
|
||||
|
||||
private static final int SIO_RESET_SIO = 0;
|
||||
private static final int SIO_RESET_PURGE_RX = 1;
|
||||
private static final int SIO_RESET_PURGE_TX = 2;
|
||||
|
||||
public static final int FTDI_DEVICE_OUT_REQTYPE =
|
||||
UsbConstants.USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT;
|
||||
|
||||
public static final int FTDI_DEVICE_IN_REQTYPE =
|
||||
UsbConstants.USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN;
|
||||
|
||||
/**
|
||||
* Length of the modem status header, transmitted with every read.
|
||||
*/
|
||||
private static final int MODEM_STATUS_HEADER_LENGTH = 2;
|
||||
|
||||
private final String TAG = FtdiSerialDriver.class.getSimpleName();
|
||||
|
||||
private DeviceType mType;
|
||||
private final UsbDevice mDevice;
|
||||
private final UsbSerialPort mPort;
|
||||
|
||||
/**
|
||||
* FTDI chip types.
|
||||
|
@ -157,382 +104,467 @@ public class FtdiSerialDriver extends CommonUsbSerialDriver {
|
|||
TYPE_BM, TYPE_AM, TYPE_2232C, TYPE_R, TYPE_2232H, TYPE_4232H;
|
||||
}
|
||||
|
||||
private int mInterface = 0; /* INTERFACE_ANY */
|
||||
|
||||
private int mMaxPacketSize = 64; // TODO(mikey): detect
|
||||
|
||||
/**
|
||||
* Due to http://b.android.com/28023 , we cannot use UsbRequest async reads
|
||||
* since it gives no indication of number of bytes read. Set this to
|
||||
* {@code true} on platforms where it is fixed.
|
||||
*/
|
||||
private static final boolean ENABLE_ASYNC_READS = false;
|
||||
|
||||
/**
|
||||
* Filter FTDI status bytes from buffer
|
||||
* @param src The source buffer (which contains status bytes)
|
||||
* @param dest The destination buffer to write the status bytes into (can be src)
|
||||
* @param totalBytesRead Number of bytes read to src
|
||||
* @param maxPacketSize The USB endpoint max packet size
|
||||
* @return The number of payload bytes
|
||||
*/
|
||||
private final int filterStatusBytes(byte[] src, byte[] dest, int totalBytesRead, int maxPacketSize) {
|
||||
final int packetsCount = totalBytesRead / maxPacketSize + 1;
|
||||
for (int packetIdx = 0; packetIdx < packetsCount; ++packetIdx) {
|
||||
final int count = (packetIdx == (packetsCount - 1))
|
||||
? (totalBytesRead % maxPacketSize) - MODEM_STATUS_HEADER_LENGTH
|
||||
: maxPacketSize - MODEM_STATUS_HEADER_LENGTH;
|
||||
if (count > 0) {
|
||||
System.arraycopy(src,
|
||||
packetIdx * maxPacketSize + MODEM_STATUS_HEADER_LENGTH,
|
||||
dest,
|
||||
packetIdx * (maxPacketSize - MODEM_STATUS_HEADER_LENGTH),
|
||||
count);
|
||||
}
|
||||
}
|
||||
|
||||
return totalBytesRead - (packetsCount * 2);
|
||||
public FtdiSerialDriver(UsbDevice device) {
|
||||
mDevice = device;
|
||||
mPort = new FtdiSerialPort(mDevice);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param usbDevice the {@link UsbDevice} to use
|
||||
* @param usbConnection the {@link UsbDeviceConnection} to use
|
||||
* @throws UsbSerialRuntimeException if the given device is incompatible
|
||||
* with this driver
|
||||
*/
|
||||
public FtdiSerialDriver(UsbDevice usbDevice, UsbDeviceConnection usbConnection) {
|
||||
super(usbDevice, usbConnection);
|
||||
mType = null;
|
||||
}
|
||||
|
||||
public void reset() throws IOException {
|
||||
int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE, SIO_RESET_REQUEST,
|
||||
SIO_RESET_SIO, 0 /* index */, null, 0, USB_WRITE_TIMEOUT_MILLIS);
|
||||
if (result != 0) {
|
||||
throw new IOException("Reset failed: result=" + result);
|
||||
}
|
||||
|
||||
// TODO(mikey): autodetect.
|
||||
mType = DeviceType.TYPE_R;
|
||||
@Override
|
||||
public UsbDevice getDevice() {
|
||||
return mDevice;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open() throws IOException {
|
||||
boolean opened = false;
|
||||
try {
|
||||
for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
|
||||
if (mConnection.claimInterface(mDevice.getInterface(i), true)) {
|
||||
Log.d(TAG, "claimInterface " + i + " SUCCESS");
|
||||
} else {
|
||||
throw new IOException("Error claiming interface " + i);
|
||||
public List<UsbSerialPort> getPorts() {
|
||||
return Collections.singletonList(mPort);
|
||||
}
|
||||
|
||||
private class FtdiSerialPort extends CommonUsbSerialPort {
|
||||
|
||||
public static final int USB_TYPE_STANDARD = 0x00 << 5;
|
||||
public static final int USB_TYPE_CLASS = 0x00 << 5;
|
||||
public static final int USB_TYPE_VENDOR = 0x00 << 5;
|
||||
public static final int USB_TYPE_RESERVED = 0x00 << 5;
|
||||
|
||||
public static final int USB_RECIP_DEVICE = 0x00;
|
||||
public static final int USB_RECIP_INTERFACE = 0x01;
|
||||
public static final int USB_RECIP_ENDPOINT = 0x02;
|
||||
public static final int USB_RECIP_OTHER = 0x03;
|
||||
|
||||
public static final int USB_ENDPOINT_IN = 0x80;
|
||||
public static final int USB_ENDPOINT_OUT = 0x00;
|
||||
|
||||
public static final int USB_WRITE_TIMEOUT_MILLIS = 5000;
|
||||
public static final int USB_READ_TIMEOUT_MILLIS = 5000;
|
||||
|
||||
// From ftdi.h
|
||||
/**
|
||||
* Reset the port.
|
||||
*/
|
||||
private static final int SIO_RESET_REQUEST = 0;
|
||||
|
||||
/**
|
||||
* Set the modem control register.
|
||||
*/
|
||||
private static final int SIO_MODEM_CTRL_REQUEST = 1;
|
||||
|
||||
/**
|
||||
* Set flow control register.
|
||||
*/
|
||||
private static final int SIO_SET_FLOW_CTRL_REQUEST = 2;
|
||||
|
||||
/**
|
||||
* Set baud rate.
|
||||
*/
|
||||
private static final int SIO_SET_BAUD_RATE_REQUEST = 3;
|
||||
|
||||
/**
|
||||
* Set the data characteristics of the port.
|
||||
*/
|
||||
private static final int SIO_SET_DATA_REQUEST = 4;
|
||||
|
||||
private static final int SIO_RESET_SIO = 0;
|
||||
private static final int SIO_RESET_PURGE_RX = 1;
|
||||
private static final int SIO_RESET_PURGE_TX = 2;
|
||||
|
||||
public static final int FTDI_DEVICE_OUT_REQTYPE =
|
||||
UsbConstants.USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT;
|
||||
|
||||
public static final int FTDI_DEVICE_IN_REQTYPE =
|
||||
UsbConstants.USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN;
|
||||
|
||||
/**
|
||||
* Length of the modem status header, transmitted with every read.
|
||||
*/
|
||||
private static final int MODEM_STATUS_HEADER_LENGTH = 2;
|
||||
|
||||
private final String TAG = FtdiSerialDriver.class.getSimpleName();
|
||||
|
||||
private DeviceType mType;
|
||||
|
||||
private int mInterface = 0; /* INTERFACE_ANY */
|
||||
|
||||
private int mMaxPacketSize = 64; // TODO(mikey): detect
|
||||
|
||||
/**
|
||||
* Due to http://b.android.com/28023 , we cannot use UsbRequest async reads
|
||||
* since it gives no indication of number of bytes read. Set this to
|
||||
* {@code true} on platforms where it is fixed.
|
||||
*/
|
||||
private static final boolean ENABLE_ASYNC_READS = false;
|
||||
|
||||
public FtdiSerialPort(UsbDevice device) {
|
||||
super(device);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UsbSerialDriver getDriver() {
|
||||
return FtdiSerialDriver.this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter FTDI status bytes from buffer
|
||||
* @param src The source buffer (which contains status bytes)
|
||||
* @param dest The destination buffer to write the status bytes into (can be src)
|
||||
* @param totalBytesRead Number of bytes read to src
|
||||
* @param maxPacketSize The USB endpoint max packet size
|
||||
* @return The number of payload bytes
|
||||
*/
|
||||
private final int filterStatusBytes(byte[] src, byte[] dest, int totalBytesRead, int maxPacketSize) {
|
||||
final int packetsCount = totalBytesRead / maxPacketSize + 1;
|
||||
for (int packetIdx = 0; packetIdx < packetsCount; ++packetIdx) {
|
||||
final int count = (packetIdx == (packetsCount - 1))
|
||||
? (totalBytesRead % maxPacketSize) - MODEM_STATUS_HEADER_LENGTH
|
||||
: maxPacketSize - MODEM_STATUS_HEADER_LENGTH;
|
||||
if (count > 0) {
|
||||
System.arraycopy(src,
|
||||
packetIdx * maxPacketSize + MODEM_STATUS_HEADER_LENGTH,
|
||||
dest,
|
||||
packetIdx * (maxPacketSize - MODEM_STATUS_HEADER_LENGTH),
|
||||
count);
|
||||
}
|
||||
}
|
||||
reset();
|
||||
opened = true;
|
||||
} finally {
|
||||
if (!opened) {
|
||||
close();
|
||||
|
||||
return totalBytesRead - (packetsCount * 2);
|
||||
}
|
||||
|
||||
public void reset() throws IOException {
|
||||
int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE, SIO_RESET_REQUEST,
|
||||
SIO_RESET_SIO, 0 /* index */, null, 0, USB_WRITE_TIMEOUT_MILLIS);
|
||||
if (result != 0) {
|
||||
throw new IOException("Reset failed: result=" + result);
|
||||
}
|
||||
|
||||
// TODO(mikey): autodetect.
|
||||
mType = DeviceType.TYPE_R;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open(UsbDeviceConnection connection) throws IOException {
|
||||
if (mConnection != null) {
|
||||
throw new IOException("Already open");
|
||||
}
|
||||
boolean opened = false;
|
||||
try {
|
||||
for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
|
||||
if (mConnection.claimInterface(mDevice.getInterface(i), true)) {
|
||||
Log.d(TAG, "claimInterface " + i + " SUCCESS");
|
||||
} else {
|
||||
throw new IOException("Error claiming interface " + i);
|
||||
}
|
||||
}
|
||||
reset();
|
||||
opened = true;
|
||||
} finally {
|
||||
if (!opened) {
|
||||
close();
|
||||
} else {
|
||||
mConnection = connection;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
mConnection.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] dest, int timeoutMillis) throws IOException {
|
||||
final UsbEndpoint endpoint = mDevice.getInterface(0).getEndpoint(0);
|
||||
|
||||
if (ENABLE_ASYNC_READS) {
|
||||
final int readAmt;
|
||||
synchronized (mReadBufferLock) {
|
||||
// mReadBuffer is only used for maximum read size.
|
||||
readAmt = Math.min(dest.length, mReadBuffer.length);
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (mConnection == null) {
|
||||
throw new IOException("Already closed");
|
||||
}
|
||||
|
||||
final UsbRequest request = new UsbRequest();
|
||||
request.initialize(mConnection, endpoint);
|
||||
|
||||
final ByteBuffer buf = ByteBuffer.wrap(dest);
|
||||
if (!request.queue(buf, readAmt)) {
|
||||
throw new IOException("Error queueing request.");
|
||||
try {
|
||||
mConnection.close();
|
||||
} finally {
|
||||
mConnection = null;
|
||||
}
|
||||
}
|
||||
|
||||
final UsbRequest response = mConnection.requestWait();
|
||||
if (response == null) {
|
||||
throw new IOException("Null response");
|
||||
}
|
||||
@Override
|
||||
public int read(byte[] dest, int timeoutMillis) throws IOException {
|
||||
final UsbEndpoint endpoint = mDevice.getInterface(0).getEndpoint(0);
|
||||
|
||||
final int payloadBytesRead = buf.position() - MODEM_STATUS_HEADER_LENGTH;
|
||||
if (payloadBytesRead > 0) {
|
||||
Log.d(TAG, HexDump.dumpHexString(dest, 0, Math.min(32, dest.length)));
|
||||
return payloadBytesRead;
|
||||
if (ENABLE_ASYNC_READS) {
|
||||
final int readAmt;
|
||||
synchronized (mReadBufferLock) {
|
||||
// mReadBuffer is only used for maximum read size.
|
||||
readAmt = Math.min(dest.length, mReadBuffer.length);
|
||||
}
|
||||
|
||||
final UsbRequest request = new UsbRequest();
|
||||
request.initialize(mConnection, endpoint);
|
||||
|
||||
final ByteBuffer buf = ByteBuffer.wrap(dest);
|
||||
if (!request.queue(buf, readAmt)) {
|
||||
throw new IOException("Error queueing request.");
|
||||
}
|
||||
|
||||
final UsbRequest response = mConnection.requestWait();
|
||||
if (response == null) {
|
||||
throw new IOException("Null response");
|
||||
}
|
||||
|
||||
final int payloadBytesRead = buf.position() - MODEM_STATUS_HEADER_LENGTH;
|
||||
if (payloadBytesRead > 0) {
|
||||
Log.d(TAG, HexDump.dumpHexString(dest, 0, Math.min(32, dest.length)));
|
||||
return payloadBytesRead;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
final int totalBytesRead;
|
||||
final int totalBytesRead;
|
||||
|
||||
synchronized (mReadBufferLock) {
|
||||
final int readAmt = Math.min(dest.length, mReadBuffer.length);
|
||||
totalBytesRead = mConnection.bulkTransfer(endpoint, mReadBuffer,
|
||||
readAmt, timeoutMillis);
|
||||
synchronized (mReadBufferLock) {
|
||||
final int readAmt = Math.min(dest.length, mReadBuffer.length);
|
||||
totalBytesRead = mConnection.bulkTransfer(endpoint, mReadBuffer,
|
||||
readAmt, timeoutMillis);
|
||||
|
||||
if (totalBytesRead < MODEM_STATUS_HEADER_LENGTH) {
|
||||
throw new IOException("Expected at least " + MODEM_STATUS_HEADER_LENGTH + " bytes");
|
||||
if (totalBytesRead < MODEM_STATUS_HEADER_LENGTH) {
|
||||
throw new IOException("Expected at least " + MODEM_STATUS_HEADER_LENGTH + " bytes");
|
||||
}
|
||||
|
||||
return filterStatusBytes(mReadBuffer, dest, totalBytesRead, endpoint.getMaxPacketSize());
|
||||
}
|
||||
|
||||
return filterStatusBytes(mReadBuffer, dest, totalBytesRead, endpoint.getMaxPacketSize());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write(byte[] src, int timeoutMillis) throws IOException {
|
||||
final UsbEndpoint endpoint = mDevice.getInterface(0).getEndpoint(1);
|
||||
int offset = 0;
|
||||
@Override
|
||||
public int write(byte[] src, int timeoutMillis) throws IOException {
|
||||
final UsbEndpoint endpoint = mDevice.getInterface(0).getEndpoint(1);
|
||||
int offset = 0;
|
||||
|
||||
while (offset < src.length) {
|
||||
final int writeLength;
|
||||
final int amtWritten;
|
||||
while (offset < src.length) {
|
||||
final int writeLength;
|
||||
final int amtWritten;
|
||||
|
||||
synchronized (mWriteBufferLock) {
|
||||
final byte[] writeBuffer;
|
||||
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;
|
||||
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(endpoint, writeBuffer, writeLength,
|
||||
timeoutMillis);
|
||||
}
|
||||
|
||||
amtWritten = mConnection.bulkTransfer(endpoint, writeBuffer, writeLength,
|
||||
timeoutMillis);
|
||||
if (amtWritten <= 0) {
|
||||
throw new IOException("Error writing " + writeLength
|
||||
+ " bytes at offset " + offset + " length=" + src.length);
|
||||
}
|
||||
|
||||
Log.d(TAG, "Wrote amtWritten=" + amtWritten + " attempted=" + writeLength);
|
||||
offset += amtWritten;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
private int setBaudRate(int baudRate) throws IOException {
|
||||
long[] vals = convertBaudrate(baudRate);
|
||||
long actualBaudrate = vals[0];
|
||||
long index = vals[1];
|
||||
long value = vals[2];
|
||||
int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE,
|
||||
SIO_SET_BAUD_RATE_REQUEST, (int) value, (int) index,
|
||||
null, 0, USB_WRITE_TIMEOUT_MILLIS);
|
||||
if (result != 0) {
|
||||
throw new IOException("Setting baudrate failed: result=" + result);
|
||||
}
|
||||
return (int) actualBaudrate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameters(int baudRate, int dataBits, int stopBits, int parity)
|
||||
throws IOException {
|
||||
setBaudRate(baudRate);
|
||||
|
||||
int config = dataBits;
|
||||
|
||||
switch (parity) {
|
||||
case PARITY_NONE:
|
||||
config |= (0x00 << 8);
|
||||
break;
|
||||
case PARITY_ODD:
|
||||
config |= (0x01 << 8);
|
||||
break;
|
||||
case PARITY_EVEN:
|
||||
config |= (0x02 << 8);
|
||||
break;
|
||||
case PARITY_MARK:
|
||||
config |= (0x03 << 8);
|
||||
break;
|
||||
case PARITY_SPACE:
|
||||
config |= (0x04 << 8);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown parity value: " + parity);
|
||||
}
|
||||
|
||||
if (amtWritten <= 0) {
|
||||
throw new IOException("Error writing " + writeLength
|
||||
+ " bytes at offset " + offset + " length=" + src.length);
|
||||
switch (stopBits) {
|
||||
case STOPBITS_1:
|
||||
config |= (0x00 << 11);
|
||||
break;
|
||||
case STOPBITS_1_5:
|
||||
config |= (0x01 << 11);
|
||||
break;
|
||||
case STOPBITS_2:
|
||||
config |= (0x02 << 11);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown stopBits value: " + stopBits);
|
||||
}
|
||||
|
||||
Log.d(TAG, "Wrote amtWritten=" + amtWritten + " attempted=" + writeLength);
|
||||
offset += amtWritten;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
private int setBaudRate(int baudRate) throws IOException {
|
||||
long[] vals = convertBaudrate(baudRate);
|
||||
long actualBaudrate = vals[0];
|
||||
long index = vals[1];
|
||||
long value = vals[2];
|
||||
int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE,
|
||||
SIO_SET_BAUD_RATE_REQUEST, (int) value, (int) index,
|
||||
null, 0, USB_WRITE_TIMEOUT_MILLIS);
|
||||
if (result != 0) {
|
||||
throw new IOException("Setting baudrate failed: result=" + result);
|
||||
}
|
||||
return (int) actualBaudrate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameters(int baudRate, int dataBits, int stopBits, int parity)
|
||||
throws IOException {
|
||||
setBaudRate(baudRate);
|
||||
|
||||
int config = dataBits;
|
||||
|
||||
switch (parity) {
|
||||
case PARITY_NONE:
|
||||
config |= (0x00 << 8);
|
||||
break;
|
||||
case PARITY_ODD:
|
||||
config |= (0x01 << 8);
|
||||
break;
|
||||
case PARITY_EVEN:
|
||||
config |= (0x02 << 8);
|
||||
break;
|
||||
case PARITY_MARK:
|
||||
config |= (0x03 << 8);
|
||||
break;
|
||||
case PARITY_SPACE:
|
||||
config |= (0x04 << 8);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown parity value: " + parity);
|
||||
int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE,
|
||||
SIO_SET_DATA_REQUEST, config, 0 /* index */,
|
||||
null, 0, USB_WRITE_TIMEOUT_MILLIS);
|
||||
if (result != 0) {
|
||||
throw new IOException("Setting parameters failed: result=" + result);
|
||||
}
|
||||
}
|
||||
|
||||
switch (stopBits) {
|
||||
case STOPBITS_1:
|
||||
config |= (0x00 << 11);
|
||||
break;
|
||||
case STOPBITS_1_5:
|
||||
config |= (0x01 << 11);
|
||||
break;
|
||||
case STOPBITS_2:
|
||||
config |= (0x02 << 11);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown stopBits value: " + stopBits);
|
||||
}
|
||||
private long[] convertBaudrate(int baudrate) {
|
||||
// TODO(mikey): Braindead transcription of libfti method. Clean up,
|
||||
// using more idiomatic Java where possible.
|
||||
int divisor = 24000000 / baudrate;
|
||||
int bestDivisor = 0;
|
||||
int bestBaud = 0;
|
||||
int bestBaudDiff = 0;
|
||||
int fracCode[] = {
|
||||
0, 3, 2, 4, 1, 5, 6, 7
|
||||
};
|
||||
|
||||
int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE,
|
||||
SIO_SET_DATA_REQUEST, config, 0 /* index */,
|
||||
null, 0, USB_WRITE_TIMEOUT_MILLIS);
|
||||
if (result != 0) {
|
||||
throw new IOException("Setting parameters failed: result=" + result);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 2; i++) {
|
||||
int tryDivisor = divisor + i;
|
||||
int baudEstimate;
|
||||
int baudDiff;
|
||||
|
||||
private long[] convertBaudrate(int baudrate) {
|
||||
// TODO(mikey): Braindead transcription of libfti method. Clean up,
|
||||
// using more idiomatic Java where possible.
|
||||
int divisor = 24000000 / baudrate;
|
||||
int bestDivisor = 0;
|
||||
int bestBaud = 0;
|
||||
int bestBaudDiff = 0;
|
||||
int fracCode[] = {
|
||||
0, 3, 2, 4, 1, 5, 6, 7
|
||||
};
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
int tryDivisor = divisor + i;
|
||||
int baudEstimate;
|
||||
int baudDiff;
|
||||
|
||||
if (tryDivisor <= 8) {
|
||||
// Round up to minimum supported divisor
|
||||
tryDivisor = 8;
|
||||
} else if (mType != DeviceType.TYPE_AM && tryDivisor < 12) {
|
||||
// BM doesn't support divisors 9 through 11 inclusive
|
||||
tryDivisor = 12;
|
||||
} else if (divisor < 16) {
|
||||
// AM doesn't support divisors 9 through 15 inclusive
|
||||
tryDivisor = 16;
|
||||
} else {
|
||||
if (mType == DeviceType.TYPE_AM) {
|
||||
// TODO
|
||||
if (tryDivisor <= 8) {
|
||||
// Round up to minimum supported divisor
|
||||
tryDivisor = 8;
|
||||
} else if (mType != DeviceType.TYPE_AM && tryDivisor < 12) {
|
||||
// BM doesn't support divisors 9 through 11 inclusive
|
||||
tryDivisor = 12;
|
||||
} else if (divisor < 16) {
|
||||
// AM doesn't support divisors 9 through 15 inclusive
|
||||
tryDivisor = 16;
|
||||
} else {
|
||||
if (tryDivisor > 0x1FFFF) {
|
||||
// Round down to maximum supported divisor value (for
|
||||
// BM)
|
||||
tryDivisor = 0x1FFFF;
|
||||
if (mType == DeviceType.TYPE_AM) {
|
||||
// TODO
|
||||
} else {
|
||||
if (tryDivisor > 0x1FFFF) {
|
||||
// Round down to maximum supported divisor value (for
|
||||
// BM)
|
||||
tryDivisor = 0x1FFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get estimated baud rate (to nearest integer)
|
||||
baudEstimate = (24000000 + (tryDivisor / 2)) / tryDivisor;
|
||||
|
||||
// Get absolute difference from requested baud rate
|
||||
if (baudEstimate < baudrate) {
|
||||
baudDiff = baudrate - baudEstimate;
|
||||
} else {
|
||||
baudDiff = baudEstimate - baudrate;
|
||||
}
|
||||
|
||||
if (i == 0 || baudDiff < bestBaudDiff) {
|
||||
// Closest to requested baud rate so far
|
||||
bestDivisor = tryDivisor;
|
||||
bestBaud = baudEstimate;
|
||||
bestBaudDiff = baudDiff;
|
||||
if (baudDiff == 0) {
|
||||
// Spot on! No point trying
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get estimated baud rate (to nearest integer)
|
||||
baudEstimate = (24000000 + (tryDivisor / 2)) / tryDivisor;
|
||||
|
||||
// Get absolute difference from requested baud rate
|
||||
if (baudEstimate < baudrate) {
|
||||
baudDiff = baudrate - baudEstimate;
|
||||
} else {
|
||||
baudDiff = baudEstimate - baudrate;
|
||||
// Encode the best divisor value
|
||||
long encodedDivisor = (bestDivisor >> 3) | (fracCode[bestDivisor & 7] << 14);
|
||||
// Deal with special cases for encoded value
|
||||
if (encodedDivisor == 1) {
|
||||
encodedDivisor = 0; // 3000000 baud
|
||||
} else if (encodedDivisor == 0x4001) {
|
||||
encodedDivisor = 1; // 2000000 baud (BM only)
|
||||
}
|
||||
|
||||
if (i == 0 || baudDiff < bestBaudDiff) {
|
||||
// Closest to requested baud rate so far
|
||||
bestDivisor = tryDivisor;
|
||||
bestBaud = baudEstimate;
|
||||
bestBaudDiff = baudDiff;
|
||||
if (baudDiff == 0) {
|
||||
// Spot on! No point trying
|
||||
break;
|
||||
// Split into "value" and "index" values
|
||||
long value = encodedDivisor & 0xFFFF;
|
||||
long index;
|
||||
if (mType == DeviceType.TYPE_2232C || mType == DeviceType.TYPE_2232H
|
||||
|| mType == DeviceType.TYPE_4232H) {
|
||||
index = (encodedDivisor >> 8) & 0xffff;
|
||||
index &= 0xFF00;
|
||||
index |= 0 /* TODO mIndex */;
|
||||
} else {
|
||||
index = (encodedDivisor >> 16) & 0xffff;
|
||||
}
|
||||
|
||||
// Return the nearest baud rate
|
||||
return new long[] {
|
||||
bestBaud, index, value
|
||||
};
|
||||
}
|
||||
|
||||
@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 false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDTR(boolean value) throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRI() throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRTS() throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRTS(boolean value) throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean purgeHwBuffers(boolean purgeReadBuffers, boolean purgeWriteBuffers) throws IOException {
|
||||
if (purgeReadBuffers) {
|
||||
int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE, SIO_RESET_REQUEST,
|
||||
SIO_RESET_PURGE_RX, 0 /* index */, null, 0, USB_WRITE_TIMEOUT_MILLIS);
|
||||
if (result != 0) {
|
||||
throw new IOException("Flushing RX failed: result=" + result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Encode the best divisor value
|
||||
long encodedDivisor = (bestDivisor >> 3) | (fracCode[bestDivisor & 7] << 14);
|
||||
// Deal with special cases for encoded value
|
||||
if (encodedDivisor == 1) {
|
||||
encodedDivisor = 0; // 3000000 baud
|
||||
} else if (encodedDivisor == 0x4001) {
|
||||
encodedDivisor = 1; // 2000000 baud (BM only)
|
||||
}
|
||||
|
||||
// Split into "value" and "index" values
|
||||
long value = encodedDivisor & 0xFFFF;
|
||||
long index;
|
||||
if (mType == DeviceType.TYPE_2232C || mType == DeviceType.TYPE_2232H
|
||||
|| mType == DeviceType.TYPE_4232H) {
|
||||
index = (encodedDivisor >> 8) & 0xffff;
|
||||
index &= 0xFF00;
|
||||
index |= 0 /* TODO mIndex */;
|
||||
} else {
|
||||
index = (encodedDivisor >> 16) & 0xffff;
|
||||
}
|
||||
|
||||
// Return the nearest baud rate
|
||||
return new long[] {
|
||||
bestBaud, index, value
|
||||
};
|
||||
}
|
||||
|
||||
@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 false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDTR(boolean value) throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRI() throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRTS() throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRTS(boolean value) throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean purgeHwBuffers(boolean purgeReadBuffers, boolean purgeWriteBuffers) throws IOException {
|
||||
if (purgeReadBuffers) {
|
||||
int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE, SIO_RESET_REQUEST,
|
||||
SIO_RESET_PURGE_RX, 0 /* index */, null, 0, USB_WRITE_TIMEOUT_MILLIS);
|
||||
if (result != 0) {
|
||||
throw new IOException("Flushing RX failed: result=" + result);
|
||||
if (purgeWriteBuffers) {
|
||||
int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE, SIO_RESET_REQUEST,
|
||||
SIO_RESET_PURGE_TX, 0 /* index */, null, 0, USB_WRITE_TIMEOUT_MILLIS);
|
||||
if (result != 0) {
|
||||
throw new IOException("Flushing RX failed: result=" + result);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (purgeWriteBuffers) {
|
||||
int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE, SIO_RESET_REQUEST,
|
||||
SIO_RESET_PURGE_TX, 0 /* index */, null, 0, USB_WRITE_TIMEOUT_MILLIS);
|
||||
if (result != 0) {
|
||||
throw new IOException("Flushing RX failed: result=" + result);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Map<Integer, int[]> getSupportedDevices() {
|
||||
|
@ -540,7 +572,7 @@ public class FtdiSerialDriver extends CommonUsbSerialDriver {
|
|||
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_FTDI),
|
||||
new int[] {
|
||||
UsbId.FTDI_FT232R,
|
||||
UsbId.FTDI_FT231X,
|
||||
UsbId.FTDI_FT231X,
|
||||
});
|
||||
return supportedDevices;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
/* 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.hoho.android.usbserial.driver;
|
||||
|
||||
import android.util.Pair;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Maps (vendor id, product id) pairs to the corresponding serial driver.
|
||||
*
|
||||
* @author mike wakerly (opensource@hoho.com)
|
||||
*/
|
||||
public class ProbeTable {
|
||||
|
||||
private final Map<Pair<Integer, Integer>, Class<? extends UsbSerialDriver>> mProbeTable =
|
||||
new LinkedHashMap<Pair<Integer,Integer>, Class<? extends UsbSerialDriver>>();
|
||||
|
||||
/**
|
||||
* Adds or updates a (vendor, product) pair in the table.
|
||||
*
|
||||
* @param vendorId the USB vendor id
|
||||
* @param productId the USB product id
|
||||
* @param driverClass the driver class responsible for this pair
|
||||
* @return {@code this}, for chaining
|
||||
*/
|
||||
public ProbeTable addProduct(int vendorId, int productId,
|
||||
Class<? extends UsbSerialDriver> driverClass) {
|
||||
mProbeTable.put(Pair.create(vendorId, productId), driverClass);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to add all supported products from
|
||||
* {@code getSupportedProducts} static method.
|
||||
*
|
||||
* @param driverClass
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
ProbeTable addDriver(Class<? extends UsbSerialDriver> driverClass) {
|
||||
final Method method;
|
||||
|
||||
try {
|
||||
method = driverClass.getMethod("getSupportedDevices");
|
||||
} catch (SecurityException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
final Map<Integer, int[]> devices;
|
||||
try {
|
||||
devices = (Map<Integer, int[]>) method.invoke(null);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
for (Map.Entry<Integer, int[]> entry : devices.entrySet()) {
|
||||
final int vendorId = entry.getKey().intValue();
|
||||
for (int productId : entry.getValue()) {
|
||||
addProduct(vendorId, productId, driverClass);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the driver for the given (vendor, product) pair, or {@code null}
|
||||
* if no match.
|
||||
*
|
||||
* @param vendorId the USB vendor id
|
||||
* @param productId the USB product id
|
||||
* @return the driver class matching this pair, or {@code null}
|
||||
*/
|
||||
public Class<? extends UsbSerialDriver> findDriver(int vendorId, int productId) {
|
||||
final Pair<Integer, Integer> pair = Pair.create(vendorId, productId);
|
||||
return mProbeTable.get(pair);
|
||||
}
|
||||
|
||||
}
|
|
@ -13,7 +13,7 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
* Project home page: http://code.google.com/p/usb-serial-for-android/
|
||||
* Project home page: https://github.com/mik3y/usb-serial-for-android
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -36,481 +36,515 @@ import android.util.Log;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
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;
|
||||
public class ProlificSerialDriver implements UsbSerialDriver {
|
||||
|
||||
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));
|
||||
private final UsbDevice mDevice;
|
||||
private final UsbSerialPort mPort;
|
||||
|
||||
public ProlificSerialDriver(UsbDevice device) {
|
||||
mDevice = device;
|
||||
mPort = new ProlificSerialPort(mDevice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UsbSerialPort> getPorts() {
|
||||
return Collections.singletonList(mPort);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UsbDevice getDevice() {
|
||||
return mDevice;
|
||||
}
|
||||
|
||||
class ProlificSerialPort extends CommonUsbSerialPort {
|
||||
|
||||
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;
|
||||
|
||||
|
||||
public ProlificSerialPort(UsbDevice device) {
|
||||
super(device);
|
||||
}
|
||||
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));
|
||||
@Override
|
||||
public UsbSerialDriver getDriver() {
|
||||
return ProlificSerialDriver.this;
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
purgeHwBuffers(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));
|
||||
}
|
||||
}
|
||||
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));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
mReadStatusException = e;
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
private final int getStatus() throws IOException {
|
||||
if ((mReadStatusThread == null) && (mReadStatusException == null)) {
|
||||
synchronized (mReadStatusThreadLock) {
|
||||
if (mReadStatusThread == null) {
|
||||
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 void resetDevice() throws IOException {
|
||||
purgeHwBuffers(true, true);
|
||||
}
|
||||
|
||||
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 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 readBytes = mConnection.bulkTransfer(mInterruptEndpoint,
|
||||
int readBytesCount = 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();
|
||||
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));
|
||||
}
|
||||
});
|
||||
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
|
||||
}
|
||||
} catch (IOException e) {
|
||||
mReadStatusException = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
/* throw and clear an exception which occured in the status read thread */
|
||||
IOException readStatusException = mReadStatusException;
|
||||
if (mReadStatusException != null) {
|
||||
mReadStatusException = null;
|
||||
throw readStatusException;
|
||||
}
|
||||
System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
|
||||
return numBytesRead;
|
||||
|
||||
return mStatus;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write(byte[] src, int timeoutMillis) throws IOException {
|
||||
int offset = 0;
|
||||
private final boolean testStatusFlag(int flag) throws IOException {
|
||||
return ((getStatus() & flag) == flag);
|
||||
}
|
||||
|
||||
while (offset < src.length) {
|
||||
final int writeLength;
|
||||
final int amtWritten;
|
||||
@Override
|
||||
public void open(UsbDeviceConnection connection) throws IOException {
|
||||
if (mConnection != null) {
|
||||
throw new IOException("Already open");
|
||||
}
|
||||
|
||||
synchronized (mWriteBufferLock) {
|
||||
final byte[] writeBuffer;
|
||||
UsbInterface usbInterface = mDevice.getInterface(0);
|
||||
|
||||
writeLength = Math.min(src.length - offset, mWriteBuffer.length);
|
||||
if (offset == 0) {
|
||||
writeBuffer = src;
|
||||
if (!connection.claimInterface(usbInterface, true)) {
|
||||
throw new IOException("Error claiming Prolific interface 0");
|
||||
}
|
||||
|
||||
mConnection = connection;
|
||||
boolean opened = 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 {
|
||||
// bulkTransfer does not support offsets, make a copy.
|
||||
System.arraycopy(src, offset, mWriteBuffer, 0, writeLength);
|
||||
writeBuffer = mWriteBuffer;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
amtWritten = mConnection.bulkTransfer(mWriteEndpoint,
|
||||
writeBuffer, writeLength, timeoutMillis);
|
||||
setControlLines(mControlLinesValue);
|
||||
resetDevice();
|
||||
|
||||
doBlackMagic();
|
||||
opened = true;
|
||||
} finally {
|
||||
if (!opened) {
|
||||
mConnection = null;
|
||||
connection.releaseInterface(usbInterface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (mConnection == null) {
|
||||
throw new IOException("Already closed");
|
||||
}
|
||||
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 {
|
||||
try {
|
||||
mConnection.releaseInterface(mDevice.getInterface(0));
|
||||
} finally {
|
||||
mConnection = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
if (amtWritten <= 0) {
|
||||
throw new IOException("Error writing " + writeLength
|
||||
+ " bytes at offset " + offset + " length="
|
||||
+ src.length);
|
||||
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;
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown stopBits value: " + stopBits);
|
||||
}
|
||||
|
||||
offset += amtWritten;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
switch (parity) {
|
||||
case PARITY_NONE:
|
||||
lineRequestData[5] = 0;
|
||||
break;
|
||||
|
||||
@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;
|
||||
case PARITY_ODD:
|
||||
lineRequestData[5] = 1;
|
||||
break;
|
||||
|
||||
case PARITY_MARK:
|
||||
lineRequestData[5] = 3;
|
||||
break;
|
||||
|
||||
case PARITY_SPACE:
|
||||
lineRequestData[5] = 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown parity value: " + parity);
|
||||
}
|
||||
|
||||
lineRequestData[6] = (byte) dataBits;
|
||||
|
||||
ctrlOut(SET_LINE_REQUEST, 0, 0, lineRequestData);
|
||||
|
||||
resetDevice();
|
||||
|
||||
mBaudRate = baudRate;
|
||||
mDataBits = dataBits;
|
||||
mStopBits = stopBits;
|
||||
mParity = parity;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown stopBits value: " + stopBits);
|
||||
@Override
|
||||
public boolean getCD() throws IOException {
|
||||
return testStatusFlag(STATUS_FLAG_CD);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown parity value: " + parity);
|
||||
@Override
|
||||
public boolean getCTS() throws IOException {
|
||||
return testStatusFlag(STATUS_FLAG_CTS);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean purgeHwBuffers(boolean purgeReadBuffers, boolean purgeWriteBuffers) throws IOException {
|
||||
if (purgeReadBuffers) {
|
||||
vendorOut(FLUSH_RX_REQUEST, 0, null);
|
||||
@Override
|
||||
public boolean getDSR() throws IOException {
|
||||
return testStatusFlag(STATUS_FLAG_DSR);
|
||||
}
|
||||
|
||||
if (purgeWriteBuffers) {
|
||||
vendorOut(FLUSH_TX_REQUEST, 0, null);
|
||||
@Override
|
||||
public boolean getDTR() throws IOException {
|
||||
return ((mControlLinesValue & CONTROL_DTR) == CONTROL_DTR);
|
||||
}
|
||||
|
||||
return true;
|
||||
@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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean purgeHwBuffers(boolean purgeReadBuffers, boolean purgeWriteBuffers) throws IOException {
|
||||
if (purgeReadBuffers) {
|
||||
vendorOut(FLUSH_RX_REQUEST, 0, null);
|
||||
}
|
||||
|
||||
if (purgeWriteBuffers) {
|
||||
vendorOut(FLUSH_TX_REQUEST, 0, null);
|
||||
}
|
||||
|
||||
return purgeReadBuffers || purgeWriteBuffers;
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<Integer, int[]> getSupportedDevices() {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
/* Copyright 2012 Google Inc.
|
||||
/* 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
|
||||
|
@ -15,8 +16,9 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
* Project home page: http://code.google.com/p/usb-serial-for-android/
|
||||
* Project home page: https://github.com/mik3y/usb-serial-for-android
|
||||
*/
|
||||
|
||||
package com.hoho.android.usbserial.driver;
|
||||
|
||||
/**
|
||||
|
@ -52,7 +54,7 @@ public final class UsbId {
|
|||
|
||||
public static final int VENDOR_LEAFLABS = 0x1eaf;
|
||||
public static final int LEAFLABS_MAPLE = 0x0004;
|
||||
|
||||
|
||||
public static final int VENDOR_SILAB = 0x10c4;
|
||||
public static final int SILAB_CP2102 = 0xea60;
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
/* Copyright 2011 Google Inc.
|
||||
/* 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
|
||||
|
@ -15,196 +16,33 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
* Project home page: http://code.google.com/p/usb-serial-for-android/
|
||||
* Project home page: https://github.com/mik3y/usb-serial-for-android
|
||||
*/
|
||||
|
||||
package com.hoho.android.usbserial.driver;
|
||||
|
||||
import java.io.IOException;
|
||||
import android.hardware.usb.UsbDevice;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Driver interface for a USB serial device.
|
||||
*
|
||||
* @author mike wakerly (opensource@hoho.com)
|
||||
*/
|
||||
public interface UsbSerialDriver {
|
||||
|
||||
/** 5 data bits. */
|
||||
public static final int DATABITS_5 = 5;
|
||||
|
||||
/** 6 data bits. */
|
||||
public static final int DATABITS_6 = 6;
|
||||
|
||||
/** 7 data bits. */
|
||||
public static final int DATABITS_7 = 7;
|
||||
|
||||
/** 8 data bits. */
|
||||
public static final int DATABITS_8 = 8;
|
||||
|
||||
/** No flow control. */
|
||||
public static final int FLOWCONTROL_NONE = 0;
|
||||
|
||||
/** RTS/CTS input flow control. */
|
||||
public static final int FLOWCONTROL_RTSCTS_IN = 1;
|
||||
|
||||
/** RTS/CTS output flow control. */
|
||||
public static final int FLOWCONTROL_RTSCTS_OUT = 2;
|
||||
|
||||
/** XON/XOFF input flow control. */
|
||||
public static final int FLOWCONTROL_XONXOFF_IN = 4;
|
||||
|
||||
/** XON/XOFF output flow control. */
|
||||
public static final int FLOWCONTROL_XONXOFF_OUT = 8;
|
||||
|
||||
/** No parity. */
|
||||
public static final int PARITY_NONE = 0;
|
||||
|
||||
/** Odd parity. */
|
||||
public static final int PARITY_ODD = 1;
|
||||
|
||||
/** Even parity. */
|
||||
public static final int PARITY_EVEN = 2;
|
||||
|
||||
/** Mark parity. */
|
||||
public static final int PARITY_MARK = 3;
|
||||
|
||||
/** Space parity. */
|
||||
public static final int PARITY_SPACE = 4;
|
||||
|
||||
/** 1 stop bit. */
|
||||
public static final int STOPBITS_1 = 1;
|
||||
|
||||
/** 1.5 stop bits. */
|
||||
public static final int STOPBITS_1_5 = 3;
|
||||
|
||||
/** 2 stop bits. */
|
||||
public static final int STOPBITS_2 = 2;
|
||||
|
||||
/**
|
||||
* Opens and initializes the device as a USB serial device. Upon success,
|
||||
* caller must ensure that {@link #close()} is eventually called.
|
||||
* Returns the raw {@link UsbDevice} backing this port.
|
||||
*
|
||||
* @throws IOException on error opening or initializing the device.
|
||||
* @return the device
|
||||
*/
|
||||
public void open() throws IOException;
|
||||
public UsbDevice getDevice();
|
||||
|
||||
/**
|
||||
* Closes the serial device.
|
||||
* Returns all available ports for this device. This list must have at least
|
||||
* one entry.
|
||||
*
|
||||
* @throws IOException on error closing the device.
|
||||
* @return the ports
|
||||
*/
|
||||
public void close() throws IOException;
|
||||
|
||||
/**
|
||||
* Reads as many bytes as possible into the destination buffer.
|
||||
*
|
||||
* @param dest the destination byte buffer
|
||||
* @param timeoutMillis the timeout for reading
|
||||
* @return the actual number of bytes read
|
||||
* @throws IOException if an error occurred during reading
|
||||
*/
|
||||
public int read(final byte[] dest, final int timeoutMillis) throws IOException;
|
||||
|
||||
/**
|
||||
* Writes as many bytes as possible from the source buffer.
|
||||
*
|
||||
* @param src the source byte buffer
|
||||
* @param timeoutMillis the timeout for writing
|
||||
* @return the actual number of bytes written
|
||||
* @throws IOException if an error occurred during writing
|
||||
*/
|
||||
public int write(final byte[] src, final int timeoutMillis) throws IOException;
|
||||
|
||||
/**
|
||||
* Sets various serial port parameters.
|
||||
*
|
||||
* @param baudRate baud rate as an integer, for example {@code 115200}.
|
||||
* @param dataBits one of {@link #DATABITS_5}, {@link #DATABITS_6},
|
||||
* {@link #DATABITS_7}, or {@link #DATABITS_8}.
|
||||
* @param stopBits one of {@link #STOPBITS_1}, {@link #STOPBITS_1_5}, or
|
||||
* {@link #STOPBITS_2}.
|
||||
* @param parity one of {@link #PARITY_NONE}, {@link #PARITY_ODD},
|
||||
* {@link #PARITY_EVEN}, {@link #PARITY_MARK}, or
|
||||
* {@link #PARITY_SPACE}.
|
||||
* @throws IOException on error setting the port parameters
|
||||
*/
|
||||
public void setParameters(
|
||||
int baudRate, int dataBits, int stopBits, int parity) throws IOException;
|
||||
|
||||
/**
|
||||
* Gets the CD (Carrier Detect) bit from the underlying UART.
|
||||
*
|
||||
* @return the current state, or {@code false} if not supported.
|
||||
* @throws IOException if an error occurred during reading
|
||||
*/
|
||||
public boolean getCD() throws IOException;
|
||||
|
||||
/**
|
||||
* Gets the CTS (Clear To Send) bit from the underlying UART.
|
||||
*
|
||||
* @return the current state, or {@code false} if not supported.
|
||||
* @throws IOException if an error occurred during reading
|
||||
*/
|
||||
public boolean getCTS() throws IOException;
|
||||
|
||||
/**
|
||||
* Gets the DSR (Data Set Ready) bit from the underlying UART.
|
||||
*
|
||||
* @return the current state, or {@code false} if not supported.
|
||||
* @throws IOException if an error occurred during reading
|
||||
*/
|
||||
public boolean getDSR() throws IOException;
|
||||
|
||||
/**
|
||||
* Gets the DTR (Data Terminal Ready) bit from the underlying UART.
|
||||
*
|
||||
* @return the current state, or {@code false} if not supported.
|
||||
* @throws IOException if an error occurred during reading
|
||||
*/
|
||||
public boolean getDTR() throws IOException;
|
||||
|
||||
/**
|
||||
* Sets the DTR (Data Terminal Ready) bit on the underlying UART, if
|
||||
* supported.
|
||||
*
|
||||
* @param value the value to set
|
||||
* @throws IOException if an error occurred during writing
|
||||
*/
|
||||
public void setDTR(boolean value) throws IOException;
|
||||
|
||||
/**
|
||||
* Gets the RI (Ring Indicator) bit from the underlying UART.
|
||||
*
|
||||
* @return the current state, or {@code false} if not supported.
|
||||
* @throws IOException if an error occurred during reading
|
||||
*/
|
||||
public boolean getRI() throws IOException;
|
||||
|
||||
/**
|
||||
* Gets the RTS (Request To Send) bit from the underlying UART.
|
||||
*
|
||||
* @return the current state, or {@code false} if not supported.
|
||||
* @throws IOException if an error occurred during reading
|
||||
*/
|
||||
public boolean getRTS() throws IOException;
|
||||
|
||||
/**
|
||||
* Sets the RTS (Request To Send) bit on the underlying UART, if
|
||||
* supported.
|
||||
*
|
||||
* @param value the value to set
|
||||
* @throws IOException if an error occurred during writing
|
||||
*/
|
||||
public void setRTS(boolean value) throws IOException;
|
||||
|
||||
/**
|
||||
* Flush non-transmitted output data and / or non-read input data
|
||||
* @param flushRX {@code true} to flush non-transmitted output data
|
||||
* @param flushTX {@code true} to flush non-read input data
|
||||
* @return {@code true} if the operation was successful, or
|
||||
* {@code false} if the operation is not supported by the driver or device
|
||||
* @throws IOException if an error occurred during flush
|
||||
*/
|
||||
public boolean purgeHwBuffers(boolean flushRX, boolean flushTX) throws IOException;
|
||||
|
||||
public List<UsbSerialPort> getPorts();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,218 @@
|
|||
/* 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.hoho.android.usbserial.driver;
|
||||
|
||||
import android.hardware.usb.UsbDeviceConnection;
|
||||
import android.hardware.usb.UsbManager;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Interface for a single serial port.
|
||||
*
|
||||
* @author mike wakerly (opensource@hoho.com)
|
||||
*/
|
||||
public interface UsbSerialPort {
|
||||
|
||||
/** 5 data bits. */
|
||||
public static final int DATABITS_5 = 5;
|
||||
|
||||
/** 6 data bits. */
|
||||
public static final int DATABITS_6 = 6;
|
||||
|
||||
/** 7 data bits. */
|
||||
public static final int DATABITS_7 = 7;
|
||||
|
||||
/** 8 data bits. */
|
||||
public static final int DATABITS_8 = 8;
|
||||
|
||||
/** No flow control. */
|
||||
public static final int FLOWCONTROL_NONE = 0;
|
||||
|
||||
/** RTS/CTS input flow control. */
|
||||
public static final int FLOWCONTROL_RTSCTS_IN = 1;
|
||||
|
||||
/** RTS/CTS output flow control. */
|
||||
public static final int FLOWCONTROL_RTSCTS_OUT = 2;
|
||||
|
||||
/** XON/XOFF input flow control. */
|
||||
public static final int FLOWCONTROL_XONXOFF_IN = 4;
|
||||
|
||||
/** XON/XOFF output flow control. */
|
||||
public static final int FLOWCONTROL_XONXOFF_OUT = 8;
|
||||
|
||||
/** No parity. */
|
||||
public static final int PARITY_NONE = 0;
|
||||
|
||||
/** Odd parity. */
|
||||
public static final int PARITY_ODD = 1;
|
||||
|
||||
/** Even parity. */
|
||||
public static final int PARITY_EVEN = 2;
|
||||
|
||||
/** Mark parity. */
|
||||
public static final int PARITY_MARK = 3;
|
||||
|
||||
/** Space parity. */
|
||||
public static final int PARITY_SPACE = 4;
|
||||
|
||||
/** 1 stop bit. */
|
||||
public static final int STOPBITS_1 = 1;
|
||||
|
||||
/** 1.5 stop bits. */
|
||||
public static final int STOPBITS_1_5 = 3;
|
||||
|
||||
/** 2 stop bits. */
|
||||
public static final int STOPBITS_2 = 2;
|
||||
|
||||
public UsbSerialDriver getDriver();
|
||||
|
||||
/**
|
||||
* Opens and initializes the port. Upon success, caller must ensure that
|
||||
* {@link #close()} is eventually called.
|
||||
*
|
||||
* @param connection an open device connection, acquired with
|
||||
* {@link UsbManager#openDevice(android.hardware.usb.UsbDevice)}
|
||||
* @throws IOException on error opening or initializing the port.
|
||||
*/
|
||||
public void open(UsbDeviceConnection connection) throws IOException;
|
||||
|
||||
/**
|
||||
* Closes the port.
|
||||
*
|
||||
* @throws IOException on error closing the port.
|
||||
*/
|
||||
public void close() throws IOException;
|
||||
|
||||
/**
|
||||
* Reads as many bytes as possible into the destination buffer.
|
||||
*
|
||||
* @param dest the destination byte buffer
|
||||
* @param timeoutMillis the timeout for reading
|
||||
* @return the actual number of bytes read
|
||||
* @throws IOException if an error occurred during reading
|
||||
*/
|
||||
public int read(final byte[] dest, final int timeoutMillis) throws IOException;
|
||||
|
||||
/**
|
||||
* Writes as many bytes as possible from the source buffer.
|
||||
*
|
||||
* @param src the source byte buffer
|
||||
* @param timeoutMillis the timeout for writing
|
||||
* @return the actual number of bytes written
|
||||
* @throws IOException if an error occurred during writing
|
||||
*/
|
||||
public int write(final byte[] src, final int timeoutMillis) throws IOException;
|
||||
|
||||
/**
|
||||
* Sets various serial port parameters.
|
||||
*
|
||||
* @param baudRate baud rate as an integer, for example {@code 115200}.
|
||||
* @param dataBits one of {@link #DATABITS_5}, {@link #DATABITS_6},
|
||||
* {@link #DATABITS_7}, or {@link #DATABITS_8}.
|
||||
* @param stopBits one of {@link #STOPBITS_1}, {@link #STOPBITS_1_5}, or
|
||||
* {@link #STOPBITS_2}.
|
||||
* @param parity one of {@link #PARITY_NONE}, {@link #PARITY_ODD},
|
||||
* {@link #PARITY_EVEN}, {@link #PARITY_MARK}, or
|
||||
* {@link #PARITY_SPACE}.
|
||||
* @throws IOException on error setting the port parameters
|
||||
*/
|
||||
public void setParameters(
|
||||
int baudRate, int dataBits, int stopBits, int parity) throws IOException;
|
||||
|
||||
/**
|
||||
* Gets the CD (Carrier Detect) bit from the underlying UART.
|
||||
*
|
||||
* @return the current state, or {@code false} if not supported.
|
||||
* @throws IOException if an error occurred during reading
|
||||
*/
|
||||
public boolean getCD() throws IOException;
|
||||
|
||||
/**
|
||||
* Gets the CTS (Clear To Send) bit from the underlying UART.
|
||||
*
|
||||
* @return the current state, or {@code false} if not supported.
|
||||
* @throws IOException if an error occurred during reading
|
||||
*/
|
||||
public boolean getCTS() throws IOException;
|
||||
|
||||
/**
|
||||
* Gets the DSR (Data Set Ready) bit from the underlying UART.
|
||||
*
|
||||
* @return the current state, or {@code false} if not supported.
|
||||
* @throws IOException if an error occurred during reading
|
||||
*/
|
||||
public boolean getDSR() throws IOException;
|
||||
|
||||
/**
|
||||
* Gets the DTR (Data Terminal Ready) bit from the underlying UART.
|
||||
*
|
||||
* @return the current state, or {@code false} if not supported.
|
||||
* @throws IOException if an error occurred during reading
|
||||
*/
|
||||
public boolean getDTR() throws IOException;
|
||||
|
||||
/**
|
||||
* Sets the DTR (Data Terminal Ready) bit on the underlying UART, if
|
||||
* supported.
|
||||
*
|
||||
* @param value the value to set
|
||||
* @throws IOException if an error occurred during writing
|
||||
*/
|
||||
public void setDTR(boolean value) throws IOException;
|
||||
|
||||
/**
|
||||
* Gets the RI (Ring Indicator) bit from the underlying UART.
|
||||
*
|
||||
* @return the current state, or {@code false} if not supported.
|
||||
* @throws IOException if an error occurred during reading
|
||||
*/
|
||||
public boolean getRI() throws IOException;
|
||||
|
||||
/**
|
||||
* Gets the RTS (Request To Send) bit from the underlying UART.
|
||||
*
|
||||
* @return the current state, or {@code false} if not supported.
|
||||
* @throws IOException if an error occurred during reading
|
||||
*/
|
||||
public boolean getRTS() throws IOException;
|
||||
|
||||
/**
|
||||
* Sets the RTS (Request To Send) bit on the underlying UART, if
|
||||
* supported.
|
||||
*
|
||||
* @param value the value to set
|
||||
* @throws IOException if an error occurred during writing
|
||||
*/
|
||||
public void setRTS(boolean value) throws IOException;
|
||||
|
||||
/**
|
||||
* Flush non-transmitted output data and / or non-read input data
|
||||
* @param flushRX {@code true} to flush non-transmitted output data
|
||||
* @param flushTX {@code true} to flush non-read input data
|
||||
* @return {@code true} if the operation was successful, or
|
||||
* {@code false} if the operation is not supported by the driver or device
|
||||
* @throws IOException if an error occurred during flush
|
||||
*/
|
||||
public boolean purgeHwBuffers(boolean flushRX, boolean flushTX) throws IOException;
|
||||
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
/* Copyright 2011 Google Inc.
|
||||
/* 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
|
||||
|
@ -15,233 +16,79 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
* Project home page: http://code.google.com/p/usb-serial-for-android/
|
||||
* Project home page: https://github.com/mik3y/usb-serial-for-android
|
||||
*/
|
||||
|
||||
package com.hoho.android.usbserial.driver;
|
||||
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.hardware.usb.UsbDeviceConnection;
|
||||
import android.hardware.usb.UsbManager;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Helper class which finds compatible {@link UsbDevice}s and creates
|
||||
* {@link UsbSerialDriver} instances.
|
||||
*
|
||||
* <p/>
|
||||
* You don't need a Prober to use the rest of the library: it is perfectly
|
||||
* acceptable to instantiate driver instances manually. The Prober simply
|
||||
* provides convenience functions.
|
||||
*
|
||||
* <p/>
|
||||
* For most drivers, the corresponding {@link #probe(UsbManager, UsbDevice)}
|
||||
* method will either return an empty list (device unknown / unsupported) or a
|
||||
* singleton list. However, multi-port drivers may return multiple instances.
|
||||
*
|
||||
* @author mike wakerly (opensource@hoho.com)
|
||||
*/
|
||||
public enum UsbSerialProber {
|
||||
public class UsbSerialProber {
|
||||
|
||||
// TODO(mikey): Too much boilerplate.
|
||||
private final ProbeTable mProbeTable;
|
||||
|
||||
public UsbSerialProber(ProbeTable probeTable) {
|
||||
mProbeTable = probeTable;
|
||||
}
|
||||
|
||||
public static UsbSerialProber getDefaultProber() {
|
||||
final ProbeTable probeTable = new ProbeTable();
|
||||
probeTable.addDriver(CdcAcmSerialDriver.class);
|
||||
probeTable.addDriver(Cp2102SerialDriver.class);
|
||||
probeTable.addDriver(FtdiSerialDriver.class);
|
||||
probeTable.addDriver(ProlificSerialDriver.class);
|
||||
return new UsbSerialProber(probeTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prober for {@link FtdiSerialDriver}.
|
||||
* Finds and builds all possible {@link UsbSerialDriver UsbSerialDrivers}
|
||||
* from the currently-attached {@link UsbDevice} hierarchy. This method does
|
||||
* not require permission from the Android USB system, since it does not
|
||||
* open any of the devices.
|
||||
*
|
||||
* @see FtdiSerialDriver
|
||||
* @param usbManager
|
||||
* @return a list, possibly empty, of all compatible drivers
|
||||
*/
|
||||
FTDI_SERIAL {
|
||||
@Override
|
||||
public List<UsbSerialDriver> probe(final UsbManager manager, final UsbDevice usbDevice) {
|
||||
if (!testIfSupported(usbDevice, FtdiSerialDriver.getSupportedDevices())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final UsbDeviceConnection connection = manager.openDevice(usbDevice);
|
||||
if (connection == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final UsbSerialDriver driver = new FtdiSerialDriver(usbDevice, connection);
|
||||
return Collections.singletonList(driver);
|
||||
}
|
||||
},
|
||||
public List<UsbSerialDriver> findAllDrivers(final UsbManager usbManager) {
|
||||
final List<UsbSerialDriver> result = new ArrayList<UsbSerialDriver>();
|
||||
|
||||
CDC_ACM_SERIAL {
|
||||
@Override
|
||||
public List<UsbSerialDriver> probe(UsbManager manager, UsbDevice usbDevice) {
|
||||
if (!testIfSupported(usbDevice, CdcAcmSerialDriver.getSupportedDevices())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final UsbDeviceConnection connection = manager.openDevice(usbDevice);
|
||||
if (connection == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final UsbSerialDriver driver = new CdcAcmSerialDriver(usbDevice, connection);
|
||||
return Collections.singletonList(driver);
|
||||
}
|
||||
},
|
||||
|
||||
SILAB_SERIAL {
|
||||
@Override
|
||||
public List<UsbSerialDriver> probe(final UsbManager manager, final UsbDevice usbDevice) {
|
||||
if (!testIfSupported(usbDevice, Cp2102SerialDriver.getSupportedDevices())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final UsbDeviceConnection connection = manager.openDevice(usbDevice);
|
||||
if (connection == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final UsbSerialDriver driver = new Cp2102SerialDriver(usbDevice, connection);
|
||||
return Collections.singletonList(driver);
|
||||
}
|
||||
},
|
||||
|
||||
PROLIFIC_SERIAL {
|
||||
@Override
|
||||
public List<UsbSerialDriver> 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);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Tests the supplied {@link UsbDevice} for compatibility with this enum
|
||||
* member, returning one or more driver instances if compatible.
|
||||
*
|
||||
* @param manager the {@link UsbManager} to use
|
||||
* @param usbDevice the raw {@link UsbDevice} to use
|
||||
* @return zero or more {@link UsbSerialDriver}, depending on compatibility
|
||||
* (never {@code null}).
|
||||
*/
|
||||
protected abstract List<UsbSerialDriver> probe(final UsbManager manager, final UsbDevice usbDevice);
|
||||
|
||||
/**
|
||||
* Creates and returns a new {@link UsbSerialDriver} instance for the first
|
||||
* compatible {@link UsbDevice} found on the bus. If none are found,
|
||||
* returns {@code null}.
|
||||
*
|
||||
* <p/>
|
||||
* The order of devices is undefined, therefore if there are multiple
|
||||
* devices on the bus, the chosen device may not be predictable (clients
|
||||
* should use {@link #findAllDevices(UsbManager)} instead).
|
||||
*
|
||||
* @param usbManager the {@link UsbManager} to use.
|
||||
* @return the first available {@link UsbSerialDriver}, or {@code null} if
|
||||
* none are available.
|
||||
*/
|
||||
public static UsbSerialDriver findFirstDevice(final UsbManager usbManager) {
|
||||
for (final UsbDevice usbDevice : usbManager.getDeviceList().values()) {
|
||||
for (final UsbSerialProber prober : values()) {
|
||||
final List<UsbSerialDriver> probedDevices = prober.probe(usbManager, usbDevice);
|
||||
if (!probedDevices.isEmpty()) {
|
||||
return probedDevices.get(0);
|
||||
final int vendorId = usbDevice.getVendorId();
|
||||
final int productId = usbDevice.getProductId();
|
||||
|
||||
final Class<? extends UsbSerialDriver> driverClass =
|
||||
mProbeTable.findDriver(vendorId, productId);
|
||||
if (driverClass != null) {
|
||||
final UsbSerialDriver driver;
|
||||
try {
|
||||
final Constructor<? extends UsbSerialDriver> ctor =
|
||||
driverClass.getConstructor(UsbDevice.class);
|
||||
driver = ctor.newInstance(usbDevice);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (InstantiationException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
result.add(driver);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link UsbSerialDriver} instance for all compatible
|
||||
* {@link UsbDevice}s found on the bus. If no compatible devices are found,
|
||||
* the list will be empty.
|
||||
*
|
||||
* @param usbManager
|
||||
* @return
|
||||
*/
|
||||
public static List<UsbSerialDriver> findAllDevices(final UsbManager usbManager) {
|
||||
final List<UsbSerialDriver> result = new ArrayList<UsbSerialDriver>();
|
||||
|
||||
// For each UsbDevice, call probe() for each prober.
|
||||
for (final UsbDevice usbDevice : usbManager.getDeviceList().values()) {
|
||||
result.addAll(probeSingleDevice(usbManager, usbDevice));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Special method for testing a specific device for driver support,
|
||||
* returning any compatible driver(s).
|
||||
*
|
||||
* <p/>
|
||||
* Clients should ordinarily use {@link #findAllDevices(UsbManager)}, which
|
||||
* operates against the entire bus of devices. This method is useful when
|
||||
* testing against only a single target is desired.
|
||||
*
|
||||
* @param usbManager the {@link UsbManager} to use.
|
||||
* @param usbDevice the device to test against.
|
||||
* @return a list containing zero or more {@link UsbSerialDriver} instances.
|
||||
*/
|
||||
public static List<UsbSerialDriver> probeSingleDevice(final UsbManager usbManager,
|
||||
UsbDevice usbDevice) {
|
||||
final List<UsbSerialDriver> result = new ArrayList<UsbSerialDriver>();
|
||||
for (final UsbSerialProber prober : values()) {
|
||||
final List<UsbSerialDriver> probedDevices = prober.probe(usbManager, usbDevice);
|
||||
result.addAll(probedDevices);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated; Use {@link #findFirstDevice(UsbManager)}.
|
||||
*
|
||||
* @param usbManager
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
public static UsbSerialDriver acquire(final UsbManager usbManager) {
|
||||
return findFirstDevice(usbManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated; use {@link #probeSingleDevice(UsbManager, UsbDevice)}.
|
||||
*
|
||||
* @param usbManager
|
||||
* @param usbDevice
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
public static UsbSerialDriver acquire(final UsbManager usbManager, final UsbDevice usbDevice) {
|
||||
final List<UsbSerialDriver> probedDevices = probeSingleDevice(usbManager, usbDevice);
|
||||
if (!probedDevices.isEmpty()) {
|
||||
return probedDevices.get(0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the given device is found in the driver's
|
||||
* vendor/product map.
|
||||
*
|
||||
* @param usbDevice the device to test
|
||||
* @param supportedDevices map of vendor IDs to product ID(s)
|
||||
* @return {@code true} if supported
|
||||
*/
|
||||
private static boolean testIfSupported(final UsbDevice usbDevice,
|
||||
final Map<Integer, int[]> supportedDevices) {
|
||||
final int[] supportedProducts = supportedDevices.get(
|
||||
Integer.valueOf(usbDevice.getVendorId()));
|
||||
if (supportedProducts == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int productId = usbDevice.getProductId();
|
||||
for (int supportedProductId : supportedProducts) {
|
||||
if (productId == supportedProductId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
/* Copyright 2011 Google Inc.
|
||||
/* 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
|
||||
|
@ -15,7 +16,7 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
* Project home page: http://code.google.com/p/usb-serial-for-android/
|
||||
* Project home page: https://github.com/mik3y/usb-serial-for-android
|
||||
*/
|
||||
|
||||
package com.hoho.android.usbserial.util;
|
||||
|
@ -23,13 +24,13 @@ package com.hoho.android.usbserial.util;
|
|||
import android.hardware.usb.UsbRequest;
|
||||
import android.util.Log;
|
||||
|
||||
import com.hoho.android.usbserial.driver.UsbSerialDriver;
|
||||
import com.hoho.android.usbserial.driver.UsbSerialPort;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Utility class which services a {@link UsbSerialDriver} in its {@link #run()}
|
||||
* Utility class which services a {@link UsbSerialPort} in its {@link #run()}
|
||||
* method.
|
||||
*
|
||||
* @author mike wakerly (opensource@hoho.com)
|
||||
|
@ -42,7 +43,7 @@ public class SerialInputOutputManager implements Runnable {
|
|||
private static final int READ_WAIT_MILLIS = 200;
|
||||
private static final int BUFSIZ = 4096;
|
||||
|
||||
private final UsbSerialDriver mDriver;
|
||||
private final UsbSerialPort mDriver;
|
||||
|
||||
private final ByteBuffer mReadBuffer = ByteBuffer.allocate(BUFSIZ);
|
||||
|
||||
|
@ -77,14 +78,14 @@ public class SerialInputOutputManager implements Runnable {
|
|||
/**
|
||||
* Creates a new instance with no listener.
|
||||
*/
|
||||
public SerialInputOutputManager(UsbSerialDriver driver) {
|
||||
public SerialInputOutputManager(UsbSerialPort driver) {
|
||||
this(driver, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance with the provided listener.
|
||||
*/
|
||||
public SerialInputOutputManager(UsbSerialDriver driver, Listener listener) {
|
||||
public SerialInputOutputManager(UsbSerialPort driver, Listener listener) {
|
||||
mDriver = driver;
|
||||
mListener = listener;
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue