/* * Copyright 2013 Ytai Ben-Tsvi. All rights reserved. * * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARSHAN POURSOHI OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied. */ package ioio.lib.android.device; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbEndpoint; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * Wrappers around USB bulk endpoints, which expose them as I/O streams. * * @author Misha Seltzer * @author Nadir Izrael * @author Ytai Ben-Tsvi */ public class Streams { // It seems that for bulkTransfer, giving timeout of 0 does what we need. // The documentation on the Android side of things is not clear about what // we should choose, so as long as it works - great. static final int TRANSFER_TIMEOUT_MILLIS = 0; public static class DeviceInputStream extends InputStream { private final byte[] buffer_ = new byte[1024]; private final UsbDeviceConnection connection_; private final UsbEndpoint ep_; DeviceInputStream(UsbDeviceConnection connection, UsbEndpoint ep) { this.connection_ = connection; this.ep_ = ep; } @Override public int read() throws IOException { return read(buffer_, 0, 1) == 1 ? buffer_[0] : -1; } @Override public int read(byte[] buffer) throws IOException { return read(buffer, 0, buffer.length); } @Override public synchronized int read(byte[] buffer, int offset, int length) throws IOException { if (offset == 0) { return connection_.bulkTransfer(ep_, buffer, length, TRANSFER_TIMEOUT_MILLIS); } // We have to go through an intermediate buffer and copy, since the API won't let us // write to a non-0 // offset. int readAmount = connection_.bulkTransfer(ep_, buffer_, Math.min(length, buffer_.length), TRANSFER_TIMEOUT_MILLIS); System.arraycopy(buffer_, 0, buffer, offset, readAmount); return readAmount; } } public static class DeviceOutputStream extends OutputStream { private final UsbDeviceConnection connection_; private final UsbEndpoint ep_; private final byte[] buffer_ = new byte[1024]; DeviceOutputStream(UsbDeviceConnection connection, UsbEndpoint ep) { this.connection_ = connection; this.ep_ = ep; } @Override public synchronized void write(int oneByte) throws IOException { buffer_[0] = (byte) oneByte; write(buffer_, 0, 1); } @Override public void write(byte[] buffer) throws IOException { write(buffer, 0, buffer.length); } @Override public synchronized void write(byte[] buffer, int offset, int count) throws IOException { if (count > (buffer.length - offset)) { throw new IOException("Count is too big"); } while (count > 0) { if (offset == 0) { // This is an optimization: when the offset is 0, we can pass the original // buffer and avoid a copy. offset = connection_.bulkTransfer(ep_, buffer, count, TRANSFER_TIMEOUT_MILLIS); if (offset < 0) { throw new IOException("Couldn't write to USB"); } count -= offset; } else { // We cannot write directly from the buffer, since the API won't let us use a // non-0 offset. // So we have to use an intermediate buffer and copy. int copied = Math.min(count, buffer_.length); System.arraycopy(buffer, offset, buffer_, 0, copied); int written = connection_.bulkTransfer(ep_, buffer_, copied, TRANSFER_TIMEOUT_MILLIS); if (written < 0) { throw new IOException("Couldn't write to USB"); } offset += written; count -= written; } } } } }