diff options
Diffstat (limited to 'android/WALT/app/src/main/java/org/chromium/latency/walt/WaltUsbConnection.java')
-rw-r--r-- | android/WALT/app/src/main/java/org/chromium/latency/walt/WaltUsbConnection.java | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/WaltUsbConnection.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/WaltUsbConnection.java new file mode 100644 index 0000000..f118ae2 --- /dev/null +++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/WaltUsbConnection.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.chromium.latency.walt; + +import android.content.Context; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbEndpoint; +import android.hardware.usb.UsbInterface; +import android.util.Log; + +import java.io.IOException; + +/** + * A singleton used as an interface for the physical WALT device. + */ +public class WaltUsbConnection extends BaseUsbConnection implements WaltConnection { + + private static final int TEENSY_VID = 0x16c0; + // TODO: refactor to demystify PID. See BaseUsbConnection.isCompatibleUsbDevice() + private static final int TEENSY_PID = 0; + private static final int HALFKAY_PID = 0x0478; + private static final int USB_READ_TIMEOUT_MS = 200; + private static final String TAG = "WaltUsbConnection"; + + private UsbEndpoint endpointIn = null; + private UsbEndpoint endpointOut = null; + + private RemoteClockInfo remoteClock = new RemoteClockInfo(); + + private static final Object LOCK = new Object(); + + private static WaltUsbConnection instance; + + private WaltUsbConnection(Context context) { + super(context); + } + + public static WaltUsbConnection getInstance(Context context) { + synchronized (LOCK) { + if (instance == null) { + instance = new WaltUsbConnection(context.getApplicationContext()); + } + return instance; + } + } + + @Override + public int getPid() { + return TEENSY_PID; + } + + @Override + public int getVid() { + return TEENSY_VID; + } + + @Override + protected boolean isCompatibleUsbDevice(UsbDevice usbDevice) { + // Allow any Teensy, but not in HalfKay bootloader mode + // Teensy PID depends on mode (e.g: Serail + MIDI) and also changed in TeensyDuino 1.31 + return ((usbDevice.getProductId() != HALFKAY_PID) && + (usbDevice.getVendorId() == TEENSY_VID)); + } + + + // Called when WALT is physically unplugged from USB + @Override + public void onDisconnect() { + endpointIn = null; + endpointOut = null; + super.onDisconnect(); + } + + + // Called when WALT is physically plugged into USB + @Override + public void onConnect() { + // Serial mode only + // TODO: find the interface and endpoint indexes no matter what mode it is + int ifIdx = 1; + int epInIdx = 1; + int epOutIdx = 0; + + UsbInterface iface = usbDevice.getInterface(ifIdx); + + if (usbConnection.claimInterface(iface, true)) { + logger.log("Interface claimed successfully\n"); + } else { + logger.log("ERROR - can't claim interface\n"); + return; + } + + endpointIn = iface.getEndpoint(epInIdx); + endpointOut = iface.getEndpoint(epOutIdx); + + super.onConnect(); + } + + @Override + public boolean isConnected() { + return super.isConnected() && (endpointIn != null) && (endpointOut != null); + } + + + @Override + public void sendByte(char c) throws IOException { + if (!isConnected()) { + throw new IOException("Not connected to WALT"); + } + // logger.log("Sending char " + c); + usbConnection.bulkTransfer(endpointOut, Utils.char2byte(c), 1, 100); + } + + @Override + public int blockingRead(byte[] buffer) { + return usbConnection.bulkTransfer(endpointIn, buffer, buffer.length, USB_READ_TIMEOUT_MS); + } + + + @Override + public RemoteClockInfo syncClock() throws IOException { + if (!isConnected()) { + throw new IOException("Not connected to WALT"); + } + + try { + int fd = usbConnection.getFileDescriptor(); + int ep_out = endpointOut.getAddress(); + int ep_in = endpointIn.getAddress(); + + remoteClock.baseTime = syncClock(fd, ep_out, ep_in); + remoteClock.minLag = 0; + remoteClock.maxLag = getMaxE(); + } catch (Exception e) { + logger.log("Exception while syncing clocks: " + e.getStackTrace()); + } + logger.log("Synced clocks, maxE=" + remoteClock.maxLag + "us"); + Log.i(TAG, remoteClock.toString()); + return remoteClock; + } + + @Override + public void updateLag() { + if (! isConnected()) { + logger.log("ERROR: Not connected, aborting checkDrift()"); + return; + } + updateBounds(); + remoteClock.minLag = getMinE(); + remoteClock.maxLag = getMaxE(); + } + + + + // NDK / JNI stuff + // TODO: add guards to avoid calls to updateBounds and getter when listener is running. + private native long syncClock(int fd, int endpoint_out, int endpoint_in); + + private native void updateBounds(); + + private native int getMinE(); + + private native int getMaxE(); + + static { + System.loadLibrary("sync_clock_jni"); + } +} |