#!/bin/bash
#
# Copyright (c) 2018, Intel Corporation
#
# SPDX-License-Identifier: BSD-3-Clause
#

#
# DISCLAIMER
#
# This script is meant for testing purposes only.
# It provides an oversimplified approach for having a simple PTP
# network up and running, with each local node having its CLOCK_TAI
# offset adjusted.
#
# TODO:
#	- 	find a way to fetch the TAI offset from ptp4l directly. Ivan
#		suggested using pmc for that.
#

set -e

INTERFACE=none
TAI_OFFSET=37
PTP4L_VERBOSE=''
PHC2SYS_VERBOSE=''
if [ -z $PTP4L ]; then
	PTP4L=$(which ptp4l)
fi
if [ -z $PHC2SYS ]; then
	PHC2SYS=$(which phc2sys)
fi


# On the PTP master, if started with -M parameter, synchronize the
# system clock to PHC first, then propagate that to network using ptp4l.
# We trust that the system clock was initially setup correctly or adjusted
# to some other source (i.e. NTP, GPS, etc).
#
# For this -M mode, clocks are kept synchronized by phc2sys.
# This is provided for the scenarios in which the PTP master on this network
# is also running one end of the TSN application (either the listener or the
# talker), which requires the local clocks to be synchronized.
#
# When that isn't the case (i.e. the tbs experiment, in which all we care
# about is the network clock sync), then just start this script with -m
# instead so phc2sys is not used and the jitter of the network clock sync is
# not affected.
#
setup_ptp_master() {
	ptp4l -i $INTERFACE $PTP4L_VERBOSE &
}

setup_ptp_master_and_sync() {
	phc2sys -c $INTERFACE -s CLOCK_REALTIME -w $PHC2SYS_VERBOSE &
	setup_ptp_master
}


# On PTP slaves, first synchronize the PHC to the PTP master,
# then synchronize the system clock to the PHC.
setup_ptp_slave() {
	phc2sys -a -r $PHC2SYS_VERBOSE &
	ptp4l -s -i $INTERFACE $PTP4L_VERBOSE &
}


# Use adjtimex to set the TAI offset to CLOCK_TAI.
adjust_clock_tai_offset() {
	tmp_src=$(mktemp /tmp/XXXXXX.c)
	tmp_bin=$(mktemp)
	cat <<EOF > $tmp_src
#include <stdio.h>
#include <stdlib.h>
#include <sys/timex.h>

int main(void)
{
	struct timex timex = {
		.modes		= ADJ_TAI,
		.constant	= $TAI_OFFSET
	};

	if (adjtimex(&timex) == -1) {
		perror("adjtimex failed to set CLOCK_TAI offset");
		return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}
EOF

	gcc -o $tmp_bin $tmp_src
	$tmp_bin
	rm -f $tmp_bin $tmp_src
}


test_dependencies() {
	if [ ! -x $PTP4L ]; then
		echo "ptp4l must be available from your \$PATH or set \$PTP4L."
		exit -1
	fi
	if [ ! -x $PHC2SYS ]; then
		echo "phc2sys must be available from your \$PATH or set \$PHC2SYS."
		exit -1
	fi
}


test_dependencies

ptp_master_mode=f
while getopts "Mmsvi:" opt; do
	case ${opt} in
	i)	INTERFACE=$OPTARG ;;
	m)	ptp_master_mode=y ;;
	s)	ptp_master_mode=n ;;
	M)	ptp_master_mode=M ;;
	v)	PTP4L_VERBOSE='-m --summary_interval=5' ;
		PHC2SYS_VERBOSE='-m -u 20' ;;
	*)	exit -1 ;;
	esac
done

if [ ${INTERFACE} = none ]; then
	echo "You must set the network interface using '-i'."
	exit -1
fi

if [ ${ptp_master_mode} = y ]; then
	setup_ptp_master
	adjust_clock_tai_offset
elif [ ${ptp_master_mode} = M ]; then
	setup_ptp_master_and_sync
	adjust_clock_tai_offset
elif [ ${ptp_master_mode} = n ]; then
	setup_ptp_slave
	adjust_clock_tai_offset
else
	echo "You must select PTP master (-m) OR PTP slave (-s) mode."
	exit -1
fi