aboutsummaryrefslogtreecommitdiff
path: root/android/WALT/app/src/main/java/org/chromium/latency/walt/WaltUsbConnection.java
diff options
context:
space:
mode:
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.java182
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");
+ }
+}