content: 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.LinkedHashMap;
  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 extends CommonUsbSerialDriver {
  
      private final String TAG = CdcAcmSerialDriver.class.getSimpleName();
  
      private UsbInterface mControlInterface;
      private UsbInterface mDataInterface;
  
      private UsbEndpoint mControlEndpoint;
      private UsbEndpoint mReadEndpoint;
      private UsbEndpoint mWriteEndpoint;
  
      private int mBaudRate;
      private int mDataBits;
      private int mStopBits;
      private int mParity;
  
      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);
      }
  
      @Override
      public void open() throws IOException {
          Log.d(TAG, "claiming interfaces, count=" + mDevice.getInterfaceCount());
  
          Log.d(TAG, "Claiming control interface.");
          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());
  
          if (mReadEndpoint.getDirection() == 0) // *ADDED for Roboclaw and maybe others
          {
          	UsbEndpoint temp = mReadEndpoint;
          	mReadEndpoint = mWriteEndpoint;
          	mWriteEndpoint = temp;
          	Log.d(TAG, "Swapped endpoints");
          }
          
          Log.d(TAG, "Setting line coding to 115200/8N1");
          mBaudRate = 115200;
          mDataBits = DATABITS_8;
          mParity = PARITY_NONE;
          mStopBits = STOPBITS_1;
          setParameters(mBaudRate, mDataBits, mStopBits, mParity);
      }
  
      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;
              }
              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;
  
          while (offset < src.length) {
              final int writeLength;
              final int amtWritten;
  
              synchronized (mWriteBufferLock) {
                  final byte[] writeBuffer;
  
                  writeLength = Math.min(src.length - offset, mWriteBuffer.length);
                  if (offset == 0) {
                      writeBuffer = src;
                  } else {
                      // bulkTransfer does not support offsets, make a copy.
                      System.arraycopy(src, offset, mWriteBuffer, 0, writeLength);
                      writeBuffer = mWriteBuffer;
                  }
  
                  amtWritten = mConnection.bulkTransfer(mWriteEndpoint, writeBuffer, writeLength,
                          timeoutMillis);
              }
              if (amtWritten <= 0) {
                  throw new IOException("Error writing " + writeLength
                          + " bytes at offset " + offset + " length=" + src.length);
              }
  
              Log.d(TAG, "Wrote amt=" + amtWritten + " attempted=" + writeLength);
              offset += amtWritten;
          }
          return offset;
      }
  
      @Deprecated
      @Override
      public int setBaudRate(int baudRate) throws IOException {
          mBaudRate = baudRate;
          setParameters(mBaudRate, mDataBits, mStopBits, mParity);
          return mBaudRate;
      }
  
      @Override
      public void setParameters(int baudRate, int dataBits, int stopBits, int parity) {
          byte stopBitsByte;
          switch (stopBits) {
              case STOPBITS_1: stopBitsByte = 1; break;
              case STOPBITS_1_5: stopBitsByte = 2; break;
              case STOPBITS_2: stopBitsByte = 3; break;
              default: throw new IllegalArgumentException("Bad value for stopBits: " + 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("Bad value for 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() {
          int value = (mRts ? 0x2 : 0) | (mDtr ? 0x1 : 0);
          sendAcmControlMessage(SET_CONTROL_LINE_STATE, value, null);
      }
  
      public static Map<Integer, int[]> getSupportedDevices() {
          final Map<Integer, int[]> supportedDevices = new LinkedHashMap<Integer, int[]>();
          supportedDevices.put(Integer.valueOf(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,
                  });
          supportedDevices.put(Integer.valueOf(UsbId.VENDOR_VAN_OOIJEN_TECH),
                  new int[] {
                      UsbId.VAN_OOIJEN_TECH_TEENSYDUINO_SERIAL,
                  });
          supportedDevices.put(Integer.valueOf(UsbId.VENDOR_ATMEL),
                  new int[] {
                      UsbId.ATMEL_LUFA_CDC_DEMO_APP,
                      UsbId.ATMEL_ROBOCLAW,  // *ADDED
                  });
          if (UsbId.DRIVER_CUSTOM == UsbId.DRIVER_CDCACM) // *ADDED conditional
          {
          	supportedDevices.put(Integer.valueOf(UsbId.VENDOR_CUSTOM),
                  new int[] {
                          UsbId.PRODUCT_CUSTOM
                  });
          }
   
          return supportedDevices;
      }
  
  }
  