/*
* Copyright 2011 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.impl;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import ioio.lib.api.DigitalInput;
import ioio.lib.api.DigitalOutput;
import ioio.lib.api.SpiMaster;
import ioio.lib.api.TwiMaster.Rate;
import ioio.lib.api.Uart;
import ioio.lib.spi.Log;
class IOIOProtocol {
static final int HARD_RESET = 0x00;
static final int ESTABLISH_CONNECTION = 0x00;
static final int SOFT_RESET = 0x01;
static final int CHECK_INTERFACE = 0x02;
static final int CHECK_INTERFACE_RESPONSE = 0x02;
static final int SET_PIN_DIGITAL_OUT = 0x03;
static final int SET_DIGITAL_OUT_LEVEL = 0x04;
static final int REPORT_DIGITAL_IN_STATUS = 0x04;
static final int SET_PIN_DIGITAL_IN = 0x05;
static final int REPORT_PERIODIC_DIGITAL_IN_STATUS = 0x05;
static final int SET_CHANGE_NOTIFY = 0x06;
static final int REGISTER_PERIODIC_DIGITAL_SAMPLING = 0x07;
static final int SET_PIN_PWM = 0x08;
static final int SET_PWM_DUTY_CYCLE = 0x09;
static final int SET_PWM_PERIOD = 0x0A;
static final int SET_PIN_ANALOG_IN = 0x0B;
static final int REPORT_ANALOG_IN_STATUS = 0x0B;
static final int SET_ANALOG_IN_SAMPLING = 0x0C;
static final int REPORT_ANALOG_IN_FORMAT = 0x0C;
static final int UART_CONFIG = 0x0D;
static final int UART_STATUS = 0x0D;
static final int UART_DATA = 0x0E;
static final int SET_PIN_UART = 0x0F;
static final int UART_REPORT_TX_STATUS = 0x0F;
static final int SPI_CONFIGURE_MASTER = 0x10;
static final int SPI_STATUS = 0x10;
static final int SPI_MASTER_REQUEST = 0x11;
static final int SPI_DATA = 0x11;
static final int SET_PIN_SPI = 0x12;
static final int SPI_REPORT_TX_STATUS = 0x12;
static final int I2C_CONFIGURE_MASTER = 0x13;
static final int I2C_STATUS = 0x13;
static final int I2C_WRITE_READ = 0x14;
static final int I2C_RESULT = 0x14;
static final int I2C_REPORT_TX_STATUS = 0x15;
static final int ICSP_SIX = 0x16;
static final int ICSP_REPORT_RX_STATUS = 0x16;
static final int ICSP_REGOUT = 0x17;
static final int ICSP_RESULT = 0x17;
static final int ICSP_PROG_ENTER = 0x18;
static final int ICSP_PROG_EXIT = 0x19;
static final int ICSP_CONFIG = 0x1A;
static final int INCAP_CONFIGURE = 0x1B;
static final int INCAP_STATUS = 0x1B;
static final int SET_PIN_INCAP = 0x1C;
static final int INCAP_REPORT = 0x1C;
static final int SOFT_CLOSE = 0x1D;
static final int SET_PIN_CAPSENSE = 0x1E;
static final int CAPSENSE_REPORT = 0x1E;
static final int SET_CAPSENSE_SAMPLING = 0x1F;
static final int SEQUENCER_CONFIGURE = 0x20;
static final int SEQUENCER_EVENT = 0x20;
static final int SEQUENCER_PUSH = 0x21;
static final int SEQUENCER_CONTROL = 0x22;
static final int SYNC = 0x23;
static final int[] SCALE_DIV = new int[]{
0x1F, // 31.25
0x1E, // 35.714
0x1D, // 41.667
0x1C, // 50
0x1B, // 62.5
0x1A, // 83.333
0x17, // 125
0x16, // 142.857
0x15, // 166.667
0x14, // 200
0x13, // 250
0x12, // 333.333
0x0F, // 500
0x0E, // 571.429
0x0D, // 666.667
0x0C, // 800
0x0B, // 1000
0x0A, // 1333.333
0x07, // 2000
0x06, // 2285.714
0x05, // 2666.667
0x04, // 3200
0x03, // 4000
0x02, // 5333.333
0x01 // 8000
};
private static final String TAG = "IOIOProtocol";
enum PwmScale {
SCALE_1X(1, 0), SCALE_8X(8, 3), SCALE_64X(64, 2), SCALE_256X(256, 1);
public final int scale;
private final int encoding;
PwmScale(int scale, int encoding) {
this.scale = scale;
this.encoding = encoding;
}
}
enum SequencerEvent {
PAUSED, STALLED, OPENED, NEXT_CUE, STOPPED, CLOSED
}
static class ProtocolError extends Exception {
private static final long serialVersionUID = -6973476719285599189L;
public ProtocolError() {
super();
}
public ProtocolError(String msg) {
super(msg);
}
public ProtocolError(Exception e) {
super(e);
}
}
private int batchCounter_ = 0;
private void writeByte(int b) throws IOException {
assert (b >= 0 && b < 256);
// Log.v(TAG, "sending: 0x" + Integer.toHexString(b));
out_.write(b);
}
private void writeBytes(byte[] buf, int offset, int size) throws IOException {
while (size-- > 0) {
writeByte(((int) buf[offset++]) & 0xFF);
}
}
public synchronized void beginBatch() {
++batchCounter_;
}
public synchronized void endBatch() throws IOException {
if (--batchCounter_ == 0) {
out_.flush();
}
}
private void writeTwoBytes(int i) throws IOException {
writeByte(i & 0xFF);
writeByte(i >> 8);
}
private void writeThreeBytes(int i) throws IOException {
writeByte(i & 0xFF);
writeByte((i >> 8) & 0xFF);
writeByte((i >> 16) & 0xFF);
}
public synchronized void sync() throws IOException {
beginBatch();
writeByte(SYNC);
endBatch();
}
synchronized public void hardReset() throws IOException {
beginBatch();
writeByte(HARD_RESET);
writeByte('I');
writeByte('O');
writeByte('I');
writeByte('O');
endBatch();
}
synchronized public void softReset() throws IOException {
beginBatch();
writeByte(SOFT_RESET);
endBatch();
}
synchronized public void softClose() throws IOException {
beginBatch();
writeByte(SOFT_CLOSE);
endBatch();
}
synchronized public void checkInterface(byte[] interfaceId) throws IOException {
if (interfaceId.length != 8) {
throw new IllegalArgumentException("interface ID must be exactly 8 bytes long");
}
beginBatch();
writeByte(CHECK_INTERFACE);
for (int i = 0; i < 8; ++i) {
writeByte(interfaceId[i]);
}
endBatch();
}
synchronized public void setDigitalOutLevel(int pin, boolean level) throws IOException {
beginBatch();
writeByte(SET_DIGITAL_OUT_LEVEL);
writeByte(pin << 2 | (level ? 1 : 0));
endBatch();
}
synchronized public void setPinPwm(int pin, int pwmNum, boolean enable) throws IOException {
beginBatch();
writeByte(SET_PIN_PWM);
writeByte(pin & 0x3F);
writeByte((enable ? 0x80 : 0x00) | (pwmNum & 0x0F));
endBatch();
}
synchronized public void setPwmDutyCycle(int pwmNum, int dutyCycle, int fraction)
throws IOException {
beginBatch();
writeByte(SET_PWM_DUTY_CYCLE);
writeByte(pwmNum << 2 | fraction);
writeTwoBytes(dutyCycle);
endBatch();
}
synchronized public void setPwmPeriod(int pwmNum, int period, PwmScale scale)
throws IOException {
beginBatch();
writeByte(SET_PWM_PERIOD);
writeByte(((scale.encoding & 0x02) << 6) | (pwmNum << 1) | (scale.encoding & 0x01));
writeTwoBytes(period);
endBatch();
}
synchronized public void setPinIncap(int pin, int incapNum, boolean enable) throws IOException {
beginBatch();
writeByte(SET_PIN_INCAP);
writeByte(pin);
writeByte(incapNum | (enable ? 0x80 : 0x00));
endBatch();
}
synchronized public void incapClose(int incapNum, boolean double_prec) throws IOException {
beginBatch();
writeByte(INCAP_CONFIGURE);
writeByte(incapNum);
writeByte(double_prec ? 0x80 : 0x00);
endBatch();
}
synchronized public void incapConfigure(int incapNum, boolean double_prec, int mode, int clock)
throws IOException {
beginBatch();
writeByte(INCAP_CONFIGURE);
writeByte(incapNum);
writeByte((double_prec ? 0x80 : 0x00) | (mode << 3) | clock);
endBatch();
}
synchronized public void i2cWriteRead(int i2cNum, boolean tenBitAddr, int address,
int writeSize, int readSize, byte[] writeData) throws IOException {
beginBatch();
writeByte(I2C_WRITE_READ);
writeByte(((address >> 8) << 6) | (tenBitAddr ? 0x20 : 0x00) | i2cNum);
writeByte(address & 0xFF);
writeByte(writeSize);
writeByte(readSize);
for (int i = 0; i < writeSize; ++i) {
writeByte(((int) writeData[i]) & 0xFF);
}
endBatch();
}
synchronized public void setPinDigitalOut(int pin, boolean value, DigitalOutput.Spec.Mode mode)
throws IOException {
beginBatch();
writeByte(SET_PIN_DIGITAL_OUT);
writeByte((pin << 2) | (mode == DigitalOutput.Spec.Mode.OPEN_DRAIN ? 0x01 : 0x00)
| (value ? 0x02 : 0x00));
endBatch();
}
synchronized public void setPinDigitalIn(int pin, DigitalInput.Spec.Mode mode)
throws IOException {
int pull = 0;
if (mode == DigitalInput.Spec.Mode.PULL_UP) {
pull = 1;
} else if (mode == DigitalInput.Spec.Mode.PULL_DOWN) {
pull = 2;
}
beginBatch();
writeByte(SET_PIN_DIGITAL_IN);
writeByte((pin << 2) | pull);
endBatch();
}
synchronized public void setChangeNotify(int pin, boolean changeNotify) throws IOException {
beginBatch();
writeByte(SET_CHANGE_NOTIFY);
writeByte((pin << 2) | (changeNotify ? 0x01 : 0x00));
endBatch();
}
synchronized public void registerPeriodicDigitalSampling(int pin, int freqScale)
throws IOException {
// TODO: implement
}
synchronized public void setPinAnalogIn(int pin) throws IOException {
beginBatch();
writeByte(SET_PIN_ANALOG_IN);
writeByte(pin);
endBatch();
}
synchronized public void setAnalogInSampling(int pin, boolean enable) throws IOException {
beginBatch();
writeByte(SET_ANALOG_IN_SAMPLING);
writeByte((enable ? 0x80 : 0x00) | (pin & 0x3F));
endBatch();
}
synchronized public void uartData(int uartNum, int numBytes, byte data[]) throws IOException {
if (numBytes > 64) {
throw new IllegalArgumentException(
"A maximum of 64 bytes can be sent in one uartData message. Got: " + numBytes);
}
beginBatch();
writeByte(UART_DATA);
writeByte((numBytes - 1) | uartNum << 6);
for (int i = 0; i < numBytes; ++i) {
writeByte(((int) data[i]) & 0xFF);
}
endBatch();
}
synchronized public void uartConfigure(int uartNum, int rate, boolean speed4x,
Uart.StopBits stopbits, Uart.Parity parity) throws IOException {
int parbits = parity == Uart.Parity.EVEN ? 1 : (parity == Uart.Parity.ODD ? 2 : 0);
beginBatch();
writeByte(UART_CONFIG);
writeByte((uartNum << 6) | (speed4x ? 0x08 : 0x00)
| (stopbits == Uart.StopBits.TWO ? 0x04 : 0x00) | parbits);
writeTwoBytes(rate);
endBatch();
}
synchronized public void uartClose(int uartNum) throws IOException {
beginBatch();
writeByte(UART_CONFIG);
writeByte(uartNum << 6);
writeTwoBytes(0);
endBatch();
}
synchronized public void setPinUart(int pin, int uartNum, boolean tx, boolean enable)
throws IOException {
beginBatch();
writeByte(SET_PIN_UART);
writeByte(pin);
writeByte((enable ? 0x80 : 0x00) | (tx ? 0x40 : 0x00) | uartNum);
endBatch();
}
synchronized public void spiConfigureMaster(int spiNum, SpiMaster.Config config)
throws IOException {
beginBatch();
writeByte(SPI_CONFIGURE_MASTER);
writeByte((spiNum << 5) | SCALE_DIV[config.rate.ordinal()]);
writeByte((config.sampleOnTrailing ? 0x00 : 0x02) | (config.invertClk ? 0x01 : 0x00));
endBatch();
}
synchronized public void spiClose(int spiNum) throws IOException {
beginBatch();
writeByte(SPI_CONFIGURE_MASTER);
writeByte(spiNum << 5);
writeByte(0x00);
endBatch();
}
synchronized public void setPinSpi(int pin, int mode, boolean enable, int spiNum)
throws IOException {
beginBatch();
writeByte(SET_PIN_SPI);
writeByte(pin);
writeByte((1 << 4) | (mode << 2) | spiNum);
endBatch();
}
synchronized public void spiMasterRequest(int spiNum, int ssPin, byte data[], int dataBytes,
int totalBytes, int responseBytes) throws IOException {
final boolean dataNeqTotal = (dataBytes != totalBytes);
final boolean resNeqTotal = (responseBytes != totalBytes);
beginBatch();
writeByte(SPI_MASTER_REQUEST);
writeByte((spiNum << 6) | ssPin);
writeByte((dataNeqTotal ? 0x80 : 0x00) | (resNeqTotal ? 0x40 : 0x00) | totalBytes - 1);
if (dataNeqTotal) {
writeByte(dataBytes);
}
if (resNeqTotal) {
writeByte(responseBytes);
}
for (int i = 0; i < dataBytes; ++i) {
writeByte(((int) data[i]) & 0xFF);
}
endBatch();
}
synchronized public void i2cConfigureMaster(int i2cNum, Rate rate, boolean smbusLevels)
throws IOException {
int rateBits = (rate == Rate.RATE_1MHz ? 3 : (rate == Rate.RATE_400KHz ? 2 : 1));
beginBatch();
writeByte(I2C_CONFIGURE_MASTER);
writeByte((smbusLevels ? 0x80 : 0) | (rateBits << 5) | i2cNum);
endBatch();
}
synchronized public void i2cClose(int i2cNum) throws IOException {
beginBatch();
writeByte(I2C_CONFIGURE_MASTER);
writeByte(i2cNum);
endBatch();
}
synchronized public void icspOpen() throws IOException {
beginBatch();
writeByte(ICSP_CONFIG);
writeByte(0x01);
endBatch();
}
synchronized public void icspClose() throws IOException {
beginBatch();
writeByte(ICSP_CONFIG);
writeByte(0x00);
endBatch();
}
synchronized public void icspEnter() throws IOException {
beginBatch();
writeByte(ICSP_PROG_ENTER);
endBatch();
}
synchronized public void icspExit() throws IOException {
beginBatch();
writeByte(ICSP_PROG_EXIT);
endBatch();
}
synchronized public void icspSix(int instruction) throws IOException {
beginBatch();
writeByte(ICSP_SIX);
writeThreeBytes(instruction);
endBatch();
}
synchronized public void icspRegout() throws IOException {
beginBatch();
writeByte(ICSP_REGOUT);
endBatch();
}
synchronized public void setPinCapSense(int pinNum) throws IOException {
beginBatch();
writeByte(SET_PIN_CAPSENSE);
writeByte(pinNum & 0x3F);
endBatch();
}
synchronized public void setCapSenseSampling(int pinNum, boolean enable) throws IOException {
beginBatch();
writeByte(SET_CAPSENSE_SAMPLING);
writeByte((pinNum & 0x3F) | (enable ? 0x80 : 0x00));
endBatch();
}
synchronized public void sequencerOpen(byte[] config, int size) throws IOException {
assert config != null;
assert size >= 0 && size <= 68;
beginBatch();
writeByte(SEQUENCER_CONFIGURE);
writeByte(size);
writeBytes(config, 0, size);
endBatch();
}
synchronized public void sequencerClose() throws IOException {
beginBatch();
writeByte(SEQUENCER_CONFIGURE);
writeByte(0);
endBatch();
}
synchronized public void sequencerPush(int duration, byte[] cue, int size) throws IOException {
assert cue != null;
assert size >= 0 && size <= 68;
assert duration < (1 << 16);
beginBatch();
writeByte(SEQUENCER_PUSH);
writeTwoBytes(duration);
writeBytes(cue, 0, size);
endBatch();
}
synchronized public void sequencerStop() throws IOException {
beginBatch();
writeByte(SEQUENCER_CONTROL);
writeByte(0);
endBatch();
}
synchronized public void sequencerStart() throws IOException {
beginBatch();
writeByte(SEQUENCER_CONTROL);
writeByte(1);
endBatch();
}
synchronized public void sequencerPause() throws IOException {
beginBatch();
writeByte(SEQUENCER_CONTROL);
writeByte(2);
endBatch();
}
synchronized public void sequencerManualStart(byte[] cue, int size) throws IOException {
beginBatch();
writeByte(SEQUENCER_CONTROL);
writeByte(3);
writeBytes(cue, 0, size);
endBatch();
}
synchronized public void sequencerManualStop() throws IOException {
beginBatch();
writeByte(SEQUENCER_CONTROL);
writeByte(4);
endBatch();
}
public interface IncomingHandler {
public void handleEstablishConnection(byte[] hardwareId, byte[] bootloaderId,
byte[] firmwareId);
public void handleConnectionLost();
public void handleSoftReset();
public void handleCheckInterfaceResponse(boolean supported);
public void handleSetChangeNotify(int pin, boolean changeNotify);
public void handleReportDigitalInStatus(int pin, boolean level);
public void handleRegisterPeriodicDigitalSampling(int pin, int freqScale);
public void handleReportPeriodicDigitalInStatus(int frameNum, boolean values[]);
public void handleAnalogPinStatus(int pin, boolean open);
public void handleReportAnalogInStatus(List<Integer> pins, List<Integer> values);
public void handleUartOpen(int uartNum);
public void handleUartClose(int uartNum);
public void handleUartData(int uartNum, int numBytes, byte data[]);
public void handleUartReportTxStatus(int uartNum, int bytesRemaining);
public void handleSpiOpen(int spiNum);
public void handleSpiClose(int spiNum);
public void handleSpiData(int spiNum, int ssPin, byte data[], int dataBytes);
public void handleSpiReportTxStatus(int spiNum, int bytesRemaining);
public void handleI2cOpen(int i2cNum);
public void handleI2cClose(int i2cNum);
public void handleI2cResult(int i2cNum, int size, byte[] data);
public void handleI2cReportTxStatus(int spiNum, int bytesRemaining);
void handleIcspOpen();
void handleIcspClose();
void handleIcspReportRxStatus(int bytesRemaining);
void handleIcspResult(int size, byte[] data);
public void handleIncapReport(int incapNum, int size, byte[] data);
public void handleIncapClose(int incapNum);
public void handleIncapOpen(int incapNum);
public void handleCapSenseReport(int pinNum, int value);
public void handleSetCapSenseSampling(int pinNum, boolean enable);
public void handleSequencerEvent(SequencerEvent event, int arg);
public void handleSync();
}
class IncomingThread extends Thread {
private List<Integer> analogPinValues_ = new ArrayList<Integer>();
private List<Integer> analogFramePins_ = new ArrayList<Integer>();
private List<Integer> newFramePins_ = new ArrayList<Integer>();
private Set<Integer> removedPins_ = new HashSet<Integer>();
private Set<Integer> addedPins_ = new HashSet<Integer>();
private void calculateAnalogFrameDelta() {
removedPins_.clear();
removedPins_.addAll(analogFramePins_);
addedPins_.clear();
addedPins_.addAll(newFramePins_);
// Remove the intersection from both.
for (Iterator<Integer> it = removedPins_.iterator(); it.hasNext(); ) {
Integer current = it.next();
if (addedPins_.contains(current)) {
it.remove();
addedPins_.remove(current);
}
}
// swap
List<Integer> temp = analogFramePins_;
analogFramePins_ = newFramePins_;
newFramePins_ = temp;
}
private int readByte() throws IOException {
try {
int b = in_.read();
if (b < 0) {
throw new IOException("Unexpected stream closure");
}
// Log.v(TAG, "received: 0x" + Integer.toHexString(b));
return b;
} catch (IOException e) {
Log.i(TAG, "IOIO disconnected");
throw e;
}
}
private void readBytes(int size, byte[] buffer) throws IOException {
for (int i = 0; i < size; ++i) {
buffer[i] = (byte) readByte();
}
}
@Override
public void run() {
super.run();
setPriority(MAX_PRIORITY);
int arg1;
int arg2;
int numPins;
int size;
byte[] data = new byte[256];
try {
while (true) {
switch (arg1 = readByte()) {
case ESTABLISH_CONNECTION:
if (readByte() != 'I' || readByte() != 'O' || readByte() != 'I'
|| readByte() != 'O') {
throw new IOException("Bad establish connection magic");
}
byte[] hardwareId = new byte[8];
byte[] bootloaderId = new byte[8];
byte[] firmwareId = new byte[8];
readBytes(8, hardwareId);
readBytes(8, bootloaderId);
readBytes(8, firmwareId);
handler_.handleEstablishConnection(hardwareId, bootloaderId, firmwareId);
break;
case SOFT_RESET:
analogFramePins_.clear();
handler_.handleSoftReset();
break;
case REPORT_DIGITAL_IN_STATUS:
arg1 = readByte();
handler_.handleReportDigitalInStatus(arg1 >> 2, (arg1 & 0x01) == 1);
break;
case SET_CHANGE_NOTIFY:
arg1 = readByte();
handler_.handleSetChangeNotify(arg1 >> 2, (arg1 & 0x01) == 1);
break;
case REGISTER_PERIODIC_DIGITAL_SAMPLING:
// TODO: implement
break;
case REPORT_PERIODIC_DIGITAL_IN_STATUS:
// TODO: implement
break;
case REPORT_ANALOG_IN_FORMAT:
numPins = readByte();
newFramePins_.clear();
for (int i = 0; i < numPins; ++i) {
newFramePins_.add(readByte());
}
calculateAnalogFrameDelta();
for (Integer i : removedPins_) {
handler_.handleAnalogPinStatus(i, false);
}
for (Integer i : addedPins_) {
handler_.handleAnalogPinStatus(i, true);
}
break;
case REPORT_ANALOG_IN_STATUS:
numPins = analogFramePins_.size();
int header = 0;
analogPinValues_.clear();
for (int i = 0; i < numPins; ++i) {
if (i % 4 == 0) {
header = readByte();
}
analogPinValues_.add((readByte() << 2) | (header & 0x03));
header >>= 2;
}
handler_.handleReportAnalogInStatus(analogFramePins_, analogPinValues_);
break;
case UART_REPORT_TX_STATUS:
arg1 = readByte();
arg2 = readByte();
handler_.handleUartReportTxStatus(arg1 & 0x03, (arg1 >> 2) | (arg2 << 6));
break;
case UART_DATA:
arg1 = readByte();
size = (arg1 & 0x3F) + 1;
readBytes(size, data);
handler_.handleUartData(arg1 >> 6, size, data);
break;
case UART_STATUS:
arg1 = readByte();
if ((arg1 & 0x80) != 0) {
handler_.handleUartOpen(arg1 & 0x03);
} else {
handler_.handleUartClose(arg1 & 0x03);
}
break;
case SPI_DATA:
arg1 = readByte();
arg2 = readByte();
size = (arg1 & 0x3F) + 1;
readBytes(size, data);
handler_.handleSpiData(arg1 >> 6, arg2 & 0x3F, data, size);
break;
case SPI_REPORT_TX_STATUS:
arg1 = readByte();
arg2 = readByte();
handler_.handleSpiReportTxStatus(arg1 & 0x03, (arg1 >> 2) | (arg2 << 6));
break;
case SPI_STATUS:
arg1 = readByte();
if ((arg1 & 0x80) != 0) {
handler_.handleSpiOpen(arg1 & 0x03);
} else {
handler_.handleSpiClose(arg1 & 0x03);
}
break;
case I2C_STATUS:
arg1 = readByte();
if ((arg1 & 0x80) != 0) {
handler_.handleI2cOpen(arg1 & 0x03);
} else {
handler_.handleI2cClose(arg1 & 0x03);
}
break;
case I2C_RESULT:
arg1 = readByte();
arg2 = readByte();
if (arg2 != 0xFF) {
readBytes(arg2, data);
}
handler_.handleI2cResult(arg1 & 0x03, arg2, data);
break;
case I2C_REPORT_TX_STATUS:
arg1 = readByte();
arg2 = readByte();
handler_.handleI2cReportTxStatus(arg1 & 0x03, (arg1 >> 2) | (arg2 << 6));
break;
case CHECK_INTERFACE_RESPONSE:
arg1 = readByte();
handler_.handleCheckInterfaceResponse((arg1 & 0x01) == 1);
break;
case ICSP_REPORT_RX_STATUS:
arg1 = readByte();
arg2 = readByte();
handler_.handleIcspReportRxStatus(arg1 | (arg2 << 8));
break;
case ICSP_RESULT:
readBytes(2, data);
handler_.handleIcspResult(2, data);
break;
case ICSP_CONFIG:
arg1 = readByte();
if ((arg1 & 0x01) == 1) {
handler_.handleIcspOpen();
} else {
handler_.handleIcspClose();
}
break;
case INCAP_STATUS:
arg1 = readByte();
if ((arg1 & 0x80) != 0) {
handler_.handleIncapOpen(arg1 & 0x0F);
} else {
handler_.handleIncapClose(arg1 & 0x0F);
}
break;
case INCAP_REPORT:
arg1 = readByte();
size = arg1 >> 6;
if (size == 0) {
size = 4;
}
readBytes(size, data);
handler_.handleIncapReport(arg1 & 0x0F, size, data);
break;
case SOFT_CLOSE:
Log.d(TAG, "Received soft close.");
throw new IOException("Soft close");
case CAPSENSE_REPORT:
arg1 = readByte();
arg2 = readByte();
handler_.handleCapSenseReport(arg1 & 0x3F, (arg1 >> 6) | (arg2 << 2));
break;
case SET_CAPSENSE_SAMPLING:
arg1 = readByte();
handler_.handleSetCapSenseSampling(arg1 & 0x3F, (arg1 & 0x80) != 0);
break;
case SEQUENCER_EVENT:
arg1 = readByte();
// OPEN and STOPPED events has an additional argument.
if (arg1 == 2 || arg1 == 4) {
arg2 = readByte();
} else {
arg2 = 0;
}
try {
handler_.handleSequencerEvent(SequencerEvent.values()[arg1], arg2);
} catch (ArrayIndexOutOfBoundsException e) {
throw new IOException("Unexpected eveent: " + arg1);
}
break;
case SYNC:
handler_.handleSync();
break;
default:
throw new ProtocolError("Received unexpected command: 0x"
+ Integer.toHexString(arg1));
}
}
} catch (IOException e) {
// This is the proper way to close -- nothing's wrong.
} catch (ProtocolError e) {
// This indicates invalid data coming in -- report the error.
Log.e(TAG, "Protocol error: ", e);
} catch (Exception e) {
// This also probably indicates invalid data coming in, which has been detected by
// the command handler -- report the error.
Log.e(TAG, "Protocol error: ", new ProtocolError(e));
} finally {
try {
in_.close();
} catch (IOException e) {
}
handler_.handleConnectionLost();
}
}
}
private final InputStream in_;
private final OutputStream out_;
private final IncomingHandler handler_;
private final IncomingThread thread_ = new IncomingThread();
public IOIOProtocol(InputStream in, OutputStream out, IncomingHandler handler) {
in_ = in;
out_ = out;
handler_ = handler;
thread_.start();
}
}