Home | History | Annotate | Download | only in walt
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package org.chromium.latency.walt;
     18 
     19 import android.content.Context;
     20 import android.hardware.usb.UsbDevice;
     21 import android.hardware.usb.UsbEndpoint;
     22 import android.hardware.usb.UsbInterface;
     23 import android.util.Log;
     24 
     25 import java.io.IOException;
     26 
     27 /**
     28  * A singleton used as an interface for the physical WALT device.
     29  */
     30 public class WaltUsbConnection extends BaseUsbConnection implements WaltConnection {
     31 
     32     private static final int TEENSY_VID = 0x16c0;
     33     // TODO: refactor to demystify PID. See BaseUsbConnection.isCompatibleUsbDevice()
     34     private static final int TEENSY_PID = 0;
     35     private static final int HALFKAY_PID = 0x0478;
     36     private static final int USB_READ_TIMEOUT_MS = 200;
     37     private static final String TAG = "WaltUsbConnection";
     38 
     39     private UsbEndpoint endpointIn = null;
     40     private UsbEndpoint endpointOut = null;
     41 
     42     private RemoteClockInfo remoteClock = new RemoteClockInfo();
     43 
     44     private static final Object LOCK = new Object();
     45 
     46     private static WaltUsbConnection instance;
     47 
     48     private WaltUsbConnection(Context context) {
     49         super(context);
     50     }
     51 
     52     public static WaltUsbConnection getInstance(Context context) {
     53         synchronized (LOCK) {
     54             if (instance == null) {
     55                 instance = new WaltUsbConnection(context.getApplicationContext());
     56             }
     57             return instance;
     58         }
     59     }
     60 
     61     @Override
     62     public int getPid() {
     63         return TEENSY_PID;
     64     }
     65 
     66     @Override
     67     public int getVid() {
     68         return TEENSY_VID;
     69     }
     70 
     71     @Override
     72     protected boolean isCompatibleUsbDevice(UsbDevice usbDevice) {
     73         // Allow any Teensy, but not in HalfKay bootloader mode
     74         // Teensy PID depends on mode (e.g: Serail + MIDI) and also changed in TeensyDuino 1.31
     75         return ((usbDevice.getProductId() != HALFKAY_PID) &&
     76                 (usbDevice.getVendorId() == TEENSY_VID));
     77     }
     78 
     79 
     80     // Called when WALT is physically unplugged from USB
     81     @Override
     82     public void onDisconnect() {
     83         endpointIn = null;
     84         endpointOut = null;
     85         super.onDisconnect();
     86     }
     87 
     88 
     89     // Called when WALT is physically plugged into USB
     90     @Override
     91     public void onConnect() {
     92         // Serial mode only
     93         // TODO: find the interface and endpoint indexes no matter what mode it is
     94         int ifIdx = 1;
     95         int epInIdx = 1;
     96         int epOutIdx = 0;
     97 
     98         UsbInterface iface = usbDevice.getInterface(ifIdx);
     99 
    100         if (usbConnection.claimInterface(iface, true)) {
    101             logger.log("Interface claimed successfully\n");
    102         } else {
    103             logger.log("ERROR - can't claim interface\n");
    104             return;
    105         }
    106 
    107         endpointIn = iface.getEndpoint(epInIdx);
    108         endpointOut = iface.getEndpoint(epOutIdx);
    109 
    110         super.onConnect();
    111     }
    112 
    113     @Override
    114     public boolean isConnected() {
    115         return super.isConnected() && (endpointIn != null) && (endpointOut != null);
    116     }
    117 
    118 
    119     @Override
    120     public void sendByte(char c) throws IOException {
    121         if (!isConnected()) {
    122             throw new IOException("Not connected to WALT");
    123         }
    124         // logger.log("Sending char " + c);
    125         usbConnection.bulkTransfer(endpointOut, Utils.char2byte(c), 1, 100);
    126     }
    127 
    128     @Override
    129     public int blockingRead(byte[] buffer) {
    130         return usbConnection.bulkTransfer(endpointIn, buffer, buffer.length, USB_READ_TIMEOUT_MS);
    131     }
    132 
    133 
    134     @Override
    135     public RemoteClockInfo syncClock() throws IOException {
    136         if (!isConnected()) {
    137             throw new IOException("Not connected to WALT");
    138         }
    139 
    140         try {
    141             int fd = usbConnection.getFileDescriptor();
    142             int ep_out = endpointOut.getAddress();
    143             int ep_in = endpointIn.getAddress();
    144 
    145             remoteClock.baseTime = syncClock(fd, ep_out, ep_in);
    146             remoteClock.minLag = 0;
    147             remoteClock.maxLag = getMaxE();
    148         } catch (Exception e) {
    149             logger.log("Exception while syncing clocks: " + e.getStackTrace());
    150         }
    151         logger.log("Synced clocks, maxE=" + remoteClock.maxLag + "us");
    152         Log.i(TAG, remoteClock.toString());
    153         return remoteClock;
    154     }
    155 
    156     @Override
    157     public void updateLag() {
    158         if (! isConnected()) {
    159             logger.log("ERROR: Not connected, aborting checkDrift()");
    160             return;
    161         }
    162         updateBounds();
    163         remoteClock.minLag = getMinE();
    164         remoteClock.maxLag = getMaxE();
    165     }
    166 
    167 
    168 
    169     // NDK / JNI stuff
    170     // TODO: add guards to avoid calls to updateBounds and getter when listener is running.
    171     private native long syncClock(int fd, int endpoint_out, int endpoint_in);
    172 
    173     private native void updateBounds();
    174 
    175     private native int getMinE();
    176 
    177     private native int getMaxE();
    178 
    179     static {
    180         System.loadLibrary("sync_clock_jni");
    181     }
    182 }
    183