blob: 1591f883dfebb6258409bd3769c602e6088cd2f1 [file] [log] [blame]
/*
* Copyright (C) 2015 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.
*/
#include "sync_clock.h"
#include <asm/byteorder.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <linux/usbdevice_fs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/inotify.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#ifdef __ANDROID__
#include <android/log.h>
#define LOGD(...) __android_log_print(ANDROID_LOG_VERBOSE, "ClockSyncNative", __VA_ARGS__)
#else
#define LOGD(...) printf(__VA_ARGS__)
#endif
// How many times to repeat the 1..9 digit sequence it's a tradeoff between
// precision and how long it takes.
// TODO: investigate better combination of constants for repeats and wait times
const int kSyncRepeats = 7;
const int kMillion = 1000000;
/**
uptimeMicros() - returns microseconds elapsed since boot.
Same time as Android's SystemClock.uptimeMillis() but in microseconds.
Adapted from Android:
platform/system/core/libutils/Timers.cpp
platform/system/core/include/utils/Timers.h
See:
http://developer.android.com/reference/android/os/SystemClock.html
https://android.googlesource.com/platform/system/core/+/master/libutils/Timers.cpp
*/
int64_t uptimeMicros() {
struct timespec ts = {0};
clock_gettime(CLOCK_MONOTONIC, &ts);
return ((int64_t)ts.tv_sec) * kMillion + ts.tv_nsec / 1000;
}
// Sleeps us microseconds
int microsleep(int us) {
struct timespec ts = {0};
ts.tv_sec = us / kMillion;
us %= kMillion;
ts.tv_nsec = us*1000;
nanosleep(&ts, NULL);
}
// *********************** Generic USB functions *******************************
static int send_char_async(int fd, int endpoint, char msg, char * label) {
// TODO: Do we really need a buffer longer than 1 char here?
char buffer[256] = {0};
buffer[0] = msg;
int length = 1;
// TODO: free() the memory used for URBs.
// Circular buffer of URBs? Cleanup at the end of clock sync?
// Several may be used simultaneously, no signal when done.
struct usbdevfs_urb *urb = calloc(1, sizeof(struct usbdevfs_urb));
memset(urb, 0, sizeof(struct usbdevfs_urb));
int res;
urb->status = -1;
urb->buffer = buffer;
urb->buffer_length = length;
urb->endpoint = endpoint;
urb->type = USBDEVFS_URB_TYPE_BULK;
urb->usercontext = label; // This is hackish
do {
res = ioctl(fd, USBDEVFS_SUBMITURB, urb);
} while((res < 0) && (errno == EINTR));
return res;
}
// Send or read using USBDEVFS_BULK. Allows to set a timeout.
static int bulk_talk(int fd, int endpoint, char * buffer, int length) {
// Set some reasonable timeout. 20ms is plenty time for most transfers but
// short enough to fail quickly if all transfers and retries fail with
// timeout.
const int kTimeoutMs = 20;
struct usbdevfs_bulktransfer ctrl = {0};
// TODO: need to limit request size to avoid EINVAL
ctrl.ep = endpoint;
ctrl.len = length;
ctrl.data = buffer;
ctrl.timeout = kTimeoutMs;
int ret = ioctl(fd, USBDEVFS_BULK, &ctrl);
return ret;
}
/*******************************************************************************
* Clock sync specific stuff below.
* Most data is stored in the clock_connection struct variable.
*/
// Send a single character to the remote in a blocking mode
int send_cmd(struct clock_connection *clk, char cmd) {
return bulk_talk(clk->fd, clk->endpoint_out, &cmd, 1);
}
// Schedule a single character to be sent to the remote - async.
int send_async(struct clock_connection *clk, char cmd) {
return send_char_async(clk->fd, clk->endpoint_out, cmd, NULL);
}
int bulk_read(struct clock_connection *clk) {
memset(clk->buffer, 0, sizeof(clk->buffer));
int ret = bulk_talk(clk->fd, clk->endpoint_in, clk->buffer, sizeof(clk->buffer));
return ret;
}
// microseconds elapsed since clk->t_base
int micros(struct clock_connection *clk) {
return uptimeMicros() - clk->t_base;
}
// Clear all incoming data that's already waiting somewhere in kernel buffers
// and discard it.
void flush_incoming(struct clock_connection *clk) {
// When bulk_read times out errno = ETIMEDOUT=110, retval =-1
// should we check for this?
while(bulk_read(clk) >= 0) {
// TODO: fail nicely if waiting too long to avoid hangs
}
}
// Ask the remote to send its timestamps
// for the digits previously sent to it.
void read_remote_timestamps(struct clock_connection *clk, int * times_remote) {
int i;
int t_remote;
// Go over the digits [1, 2 ... 9]
for (i = 0; i < 9; i++) {
char digit = i + '1';
send_cmd(clk, CMD_SYNC_READOUT);
bulk_read(clk);
if (clk->buffer[0] != digit) {
LOGD("Error, bad reply for R%d: %s", i+1, clk->buffer);
}
// The reply string looks like digit + space + timestamp
// Offset by 2 to ignore the digit and the space
t_remote = atoi(clk->buffer + 2);
times_remote[i] = t_remote;
}
}
// Preliminary rough sync with a single message - CMD_SYNC_ZERO = 'Z'.
// This is not strictly necessary but greatly simplifies debugging
// by removing the need to look at very long numbers.
void zero_remote(struct clock_connection *clk) {
flush_incoming(clk);
clk->t_base = uptimeMicros();
send_cmd(clk, CMD_SYNC_ZERO);
bulk_read(clk); // TODO, make sure we got 'z'
clk->maxE = micros(clk);
clk->minE = 0;
LOGD("Sent a 'Z', reply '%c' in %d us\n", clk->buffer[0], clk->maxE);
}
void improve_minE(struct clock_connection *clk) {
int times_local_sent[9] = {0};
int times_remote_received[9] = {0};
// Set sleep time as 1/kSleepTimeDivider of the current bounds interval,
// but never less or more than k(Min/Max)SleepUs. All pretty random
// numbers that could use some tuning and may behave differently on
// different devices.
const int kMaxSleepUs = 700;
const int kMinSleepUs = 70;
const int kSleepTimeDivider = 10;
int minE = clk->minE;
int sleep_time = (clk->maxE - minE) / kSleepTimeDivider;
if(sleep_time > kMaxSleepUs) sleep_time = kMaxSleepUs;
if(sleep_time < kMinSleepUs) sleep_time = kMinSleepUs;
flush_incoming(clk);
// Send digits to remote side
int i;
for (i = 0; i < 9; i++) {
char c = i + '1';
times_local_sent[i] = micros(clk);
send_async(clk, c);
microsleep(sleep_time);
}
// Read out receive times from the other side
read_remote_timestamps(clk, times_remote_received);
// Do stats
for (i = 0; i < 9; i++) {
int tls = times_local_sent[i];
int trr = times_remote_received[i];
int dt;
// Look at outgoing digits
dt = tls - trr;
if (tls != 0 && trr != 0 && dt > minE) {
minE = dt;
}
}
clk->minE = minE;
LOGD("E is between %d and %d us, sleep_time=%d\n", clk->minE, clk->maxE, sleep_time);
}
void improve_maxE(struct clock_connection *clk) {
int times_remote_sent[9] = {0};
int times_local_received[9] = {0};
// Tell the remote to send us digits with delays
// TODO: try tuning / configuring the delay time on remote side
send_async(clk, CMD_SYNC_SEND);
// Read and timestamp the incoming digits, they may arrive out of order.
// TODO: Try he same with USBDEVFS_REAPURB, it might be faster
int i;
for (i = 0; i < 9; ++i) {
int retval = bulk_read(clk);
// TODO: deal with retval = (bytes returned) > 1. shouldn't happen.
// Can it happen on some devices?
int t_local = micros(clk);
int digit = atoi(clk->buffer);
if (digit <=0 || digit > 9) {
LOGD("Error, bad incoming digit: %s\n", clk->buffer);
}
times_local_received[digit-1] = t_local;
}
// Flush whatever came after the digits. As of this writing, it's usually
// a single linefeed character.
flush_incoming(clk);
// Read out the remote timestamps of when the digits were sent
read_remote_timestamps(clk, times_remote_sent);
// Do stats
int maxE = clk->maxE;
for (i = 0; i < 9; i++) {
int trs = times_remote_sent[i];
int tlr = times_local_received[i];
int dt = tlr - trs;
if (tlr != 0 && trs != 0 && dt < maxE) {
maxE = dt;
}
}
clk->maxE = maxE;
LOGD("E is between %d and %d us\n", clk->minE, clk->maxE);
}
void improve_bounds(struct clock_connection *clk) {
improve_minE(clk);
improve_maxE(clk);
}
// get minE and maxE again after some time to check for clock drift
void update_bounds(struct clock_connection *clk) {
// Reset the bounds to some unrealistically large numbers
int i;
clk->minE = -1e7;
clk->maxE = 1e7;
// Talk to remote to get bounds on minE and maxE
for (i=0; i < kSyncRepeats; i++) {
improve_bounds(clk);
}
}
void sync_clocks(struct clock_connection *clk) {
// Send CMD_SYNC_ZERO to remote for rough initial sync
zero_remote(clk);
int rep;
for (rep=0; rep < kSyncRepeats; rep++) {
improve_bounds(clk);
}
// Shift the base time to set minE = 0
clk->t_base += clk->minE;
clk->maxE -= clk->minE;
clk->minE = 0;
LOGD("Base time shifted for zero minE\n");
}