usb-serial-for-android/usbSerialForAndroid/src/main/java/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java

338 wiersze
13 KiB
Java

/* Copyright 2011-2013 Google Inc.
* Copyright 2013 mike wakerly <opensource@hoho.com>
*
* Project home page: https://github.com/mik3y/usb-serial-for-android
*/
package com.hoho.android.usbserial.driver;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.util.Log;
import java.io.IOException;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* USB CDC/ACM serial driver implementation.
*
* @author mike wakerly (opensource@hoho.com)
* @see <a
* href="http://www.usb.org/developers/devclass_docs/usbcdc11.pdf">Universal
* Serial Bus Class Definitions for Communication Devices, v1.1</a>
*/
public class CdcAcmSerialDriver implements UsbSerialDriver {
private final String TAG = CdcAcmSerialDriver.class.getSimpleName();
private final UsbDevice mDevice;
private final List<UsbSerialPort> mPorts;
public CdcAcmSerialDriver(UsbDevice device) {
mDevice = device;
mPorts = new ArrayList<>();
int controlInterfaceCount = 0;
int dataInterfaceCount = 0;
for( int i = 0; i < device.getInterfaceCount(); i++) {
if(device.getInterface(i).getInterfaceClass() == UsbConstants.USB_CLASS_COMM)
controlInterfaceCount++;
if(device.getInterface(i).getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA)
dataInterfaceCount++;
}
for( int port = 0; port < Math.min(controlInterfaceCount, dataInterfaceCount); port++) {
mPorts.add(new CdcAcmSerialPort(mDevice, port));
}
if(mPorts.size() == 0) {
mPorts.add(new CdcAcmSerialPort(mDevice, -1));
}
}
@Override
public UsbDevice getDevice() {
return mDevice;
}
@Override
public List<UsbSerialPort> getPorts() {
return mPorts;
}
public class CdcAcmSerialPort extends CommonUsbSerialPort {
private UsbInterface mControlInterface;
private UsbInterface mDataInterface;
private UsbEndpoint mControlEndpoint;
private int mControlIndex;
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 CdcAcmSerialPort(UsbDevice device, int portNumber) {
super(device, portNumber);
}
@Override
public UsbSerialDriver getDriver() {
return CdcAcmSerialDriver.this;
}
@Override
protected void openInt(UsbDeviceConnection connection) throws IOException {
if (mPortNumber == -1) {
Log.d(TAG,"device might be castrated ACM device, trying single interface logic");
openSingleInterface();
} else {
Log.d(TAG,"trying default interface logic");
openInterface();
}
}
private void openSingleInterface() throws IOException {
// the following code is inspired by the cdc-acm driver in the linux kernel
mControlIndex = 0;
mControlInterface = mDevice.getInterface(0);
mDataInterface = mDevice.getInterface(0);
if (!mConnection.claimInterface(mControlInterface, true)) {
throw new IOException("Could not claim shared control/data interface");
}
for (int i = 0; i < mControlInterface.getEndpointCount(); ++i) {
UsbEndpoint ep = mControlInterface.getEndpoint(i);
if ((ep.getDirection() == UsbConstants.USB_DIR_IN) && (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_INT)) {
mControlEndpoint = ep;
} else if ((ep.getDirection() == UsbConstants.USB_DIR_IN) && (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) {
mReadEndpoint = ep;
} else if ((ep.getDirection() == UsbConstants.USB_DIR_OUT) && (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) {
mWriteEndpoint = ep;
}
}
if (mControlEndpoint == null) {
throw new IOException("No control endpoint");
}
}
private void openInterface() throws IOException {
Log.d(TAG, "claiming interfaces, count=" + mDevice.getInterfaceCount());
int controlInterfaceCount = 0;
int dataInterfaceCount = 0;
mControlInterface = null;
mDataInterface = null;
for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
UsbInterface usbInterface = mDevice.getInterface(i);
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_COMM) {
if(controlInterfaceCount == mPortNumber) {
mControlIndex = i;
mControlInterface = usbInterface;
}
controlInterfaceCount++;
}
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA) {
if(dataInterfaceCount == mPortNumber) {
mDataInterface = usbInterface;
}
dataInterfaceCount++;
}
}
if(mControlInterface == null) {
throw new IOException("No control interface");
}
Log.d(TAG, "Control iface=" + mControlInterface);
if (!mConnection.claimInterface(mControlInterface, true)) {
throw new IOException("Could not claim control interface");
}
mControlEndpoint = mControlInterface.getEndpoint(0);
if (mControlEndpoint.getDirection() != UsbConstants.USB_DIR_IN || mControlEndpoint.getType() != UsbConstants.USB_ENDPOINT_XFER_INT) {
throw new IOException("Invalid control endpoint");
}
if(mDataInterface == null) {
throw new IOException("No data interface");
}
Log.d(TAG, "data iface=" + mDataInterface);
if (!mConnection.claimInterface(mDataInterface, true)) {
throw new IOException("Could not claim data interface");
}
for (int i = 0; i < mDataInterface.getEndpointCount(); i++) {
UsbEndpoint ep = mDataInterface.getEndpoint(i);
if (ep.getDirection() == UsbConstants.USB_DIR_IN && ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)
mReadEndpoint = ep;
if (ep.getDirection() == UsbConstants.USB_DIR_OUT && ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)
mWriteEndpoint = ep;
}
}
private int sendAcmControlMessage(int request, int value, byte[] buf) throws IOException {
int len = mConnection.controlTransfer(
USB_RT_ACM, request, value, mControlIndex, buf, buf != null ? buf.length : 0, 5000);
if(len < 0) {
throw new IOException("controlTransfer failed");
}
return len;
}
@Override
protected void closeInt() {
try {
mConnection.releaseInterface(mControlInterface);
mConnection.releaseInterface(mDataInterface);
} catch(Exception ignored) {}
}
@Override
public void setParameters(int baudRate, int dataBits, int stopBits, int parity) throws IOException {
if(baudRate <= 0) {
throw new IllegalArgumentException("Invalid baud rate: " + baudRate);
}
if(dataBits < DATABITS_5 || dataBits > DATABITS_8) {
throw new IllegalArgumentException("Invalid data bits: " + dataBits);
}
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("Invalid stop bits: " + stopBits);
}
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("Invalid parity: " + parity);
}
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 getCD() throws IOException {
return false; // TODO
}
@Override
public boolean getCTS() throws IOException {
return false; // TODO
}
@Override
public boolean getDSR() throws IOException {
return false; // TODO
}
@Override
public boolean getDTR() throws IOException {
return mDtr;
}
@Override
public void setDTR(boolean value) throws IOException {
mDtr = value;
setDtrRts();
}
@Override
public boolean getRI() throws IOException {
return false; // TODO
}
@Override
public boolean getRTS() throws IOException {
return mRts;
}
@Override
public void setRTS(boolean value) throws IOException {
mRts = value;
setDtrRts();
}
private void setDtrRts() throws IOException {
int value = (mRts ? 0x2 : 0) | (mDtr ? 0x1 : 0);
sendAcmControlMessage(SET_CONTROL_LINE_STATE, value, null);
}
@Override
public EnumSet<ControlLine> getControlLines() throws IOException {
EnumSet<ControlLine> set = EnumSet.noneOf(ControlLine.class);
if(mRts) set.add(ControlLine.RTS);
if(mDtr) set.add(ControlLine.DTR);
return set;
}
@Override
public EnumSet<ControlLine> getSupportedControlLines() throws IOException {
return EnumSet.of(ControlLine.RTS, ControlLine.DTR);
}
}
public static Map<Integer, int[]> getSupportedDevices() {
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<Integer, int[]>();
supportedDevices.put(UsbId.VENDOR_ARDUINO,
new int[] {
UsbId.ARDUINO_UNO,
UsbId.ARDUINO_UNO_R3,
UsbId.ARDUINO_MEGA_2560,
UsbId.ARDUINO_MEGA_2560_R3,
UsbId.ARDUINO_SERIAL_ADAPTER,
UsbId.ARDUINO_SERIAL_ADAPTER_R3,
UsbId.ARDUINO_MEGA_ADK,
UsbId.ARDUINO_MEGA_ADK_R3,
UsbId.ARDUINO_LEONARDO,
UsbId.ARDUINO_MICRO,
});
supportedDevices.put(UsbId.VENDOR_VAN_OOIJEN_TECH,
new int[] {
UsbId.VAN_OOIJEN_TECH_TEENSYDUINO_SERIAL,
});
supportedDevices.put(UsbId.VENDOR_ATMEL,
new int[] {
UsbId.ATMEL_LUFA_CDC_DEMO_APP,
});
supportedDevices.put(UsbId.VENDOR_LEAFLABS,
new int[] {
UsbId.LEAFLABS_MAPLE,
});
supportedDevices.put(UsbId.VENDOR_ARM,
new int[] {
UsbId.ARM_MBED,
});
return supportedDevices;
}
}