#!/usr/bin/perl -w

#
# $Id: calibrate_slics 3957 2008-03-07 00:45:53Z tzafrir $
#

use strict;

my $SlicsFile = "$ENV{XPP_BASE}/$ENV{XPD_BUS}/$ENV{XPD_NAME}/slics";

my @SlicNums = (0 .. 7);

if ( ! -f $SlicsFile ) {
	exit 1
}

my $debug = 0;
# set DEBUG_CALIBRATION in /etc/default/zaptel or similar
if (exists $ENV{DEBUG_CALIBRATION}) {
	$debug = 1;
}

sub mysleep($) {
	my $timeout = shift;
	select(undef,undef,undef,$timeout);
}

sub logger($) {
	print STDERR "LOG: @_\n";
	system("logger @_");
}

sub debug($) {
	logger(@_) if ($debug);
}

sub write_to_slic_file($) {
	my $write_str = shift;

	open(SLICS,">$SlicsFile") or 
		die("Failed writing to slics file $SlicsFile");
	print SLICS $write_str;
	close(SLICS);
	mysleep(0.001);
	
}

sub read_reg($$$) {
	my $read_slic = shift;
	my $read_reg = shift;
	my $direct = shift;
	
	write_to_slic_file(
		sprintf("%d R%s %02X", $read_slic, $direct, $read_reg));
	mysleep(0.001);
	open(SLICS,$SlicsFile) or 
		die("Failed reading from slics file $SlicsFile");
	#awk '/^SLIC_REPLY:/{print $5}' $SLICS | cut -dx -f2
	my @reply = ();
	while(<SLICS>){
		#if (/^ /) {
		#	debug "answer line: $_";
		#}
		if (/^ \d*\s+[RW][DIS]\s+[[:xdigit:]]+\s+([[:xdigit:]]+)\s+([[:xdigit:]]+)/){
			@reply = (hex($1), hex($2)); 
			#debug "got [$reply]\n";
			last;
		}
	}
	close(SLICS);
	if ($direct eq 'I') {
		return @reply;
	} else {
		return $reply[0];
	}
}

# TODO: rearange arguments
sub write_reg{#($$$$$) {
	my $read_slic = shift;
	my $read_reg = shift;
	my $direct = shift;
	my $reg_val_low = shift;
	my $reg_val_hi = shift;
	
	my $str  = sprintf "%d W%s %02X %02X", 
		$read_slic, $direct, $read_reg, $reg_val_low;
	if ($direct eq 'I') {
		$str .= sprintf " %02X", $reg_val_hi;
	}
	write_to_slic_file($str);
}

sub log_calib_params() {
	for my $i (100 .. 107) {
		my $line="Calib Reg $i:  ";
		for my $slic (@SlicNums) {
			$line .= " ".read_reg($slic, $i, 'D');
		}
		debug($line);
	}
}

sub init_indirect_registers() {
	return write_to_slic_file("#
31	WI	00	C2	55
31	WI	01	E6	51
31	WI	02	85	4B
31	WI	03	37	49
                          
31	WI	04	33	33
31	WI	05	02	02
31	WI	06	02	02
31	WI	07	98	01
                          
31	WI	08	98	01
31	WI	09	11	06
31	WI	0A	02	02
31	WI	0B	E5	00
                          
31	WI	0C	1C	0A
31	WI	0D	30	7B
31	WI	0E	63	00
31	WI	0F	00	00
                          
31	WI	10	70	78
31	WI	11	7D	00
31	WI	12	00	00
31	WI	13	00	00
                          
31	WI	14	F0	7E
31	WI	15	C0	01
31	WI	16	00	00
31	WI	17	00	20
                          
31	WI	18	00	20
31	WI	19	00	00
31	WI	1A	00	20
31	WI	1B	00	40
                          
31	WI	1C	00	10
31	WI	1D	00	36
31	WI	1E	00	10
31	WI	1F	00	02
                          
31	WI	20	C0	07
31	WI	21	00	26
31	WI	22	F4	0F
31	WI	23	00	80

#31	WI	24	20	03
#31	WI	25	8C	08
#31	WI	26	00	01
#31	WI	27	10	00
                          
31	WI	24	00	08
31	WI	25	00	08
31	WI	26	00	08
31	WI	27	00	08
                          
31	WI	28	00	0C
31	WI	29	00	0C
31	WI	2B	00	01
                          
31	WI	63	DA	00
31	WI	64	60	6B
31	WI	65	74	00
31	WI	66	C0	79
                          
31	WI	67	20	11
31	WI	68	E0	3B	
#");
}

sub init_early_direct_regs() {
	return write_to_slic_file("#
31	WD	08 00
31	WD	4A 34
31	WD	4B 10
31	WD	40	00
#")
}

my @FilterParams = ();

sub save_indirect_filter_params() {
	for my $slic (@SlicNums) {
		for my $reg (35 .. 39) {
			$FilterParams[$slic][$reg] = 
				[read_reg($slic, $reg, 'I')];
			write_reg($slic, $reg, 'I', 0, 0x80);
		}
	}
	
}

sub restore_indirect_filter_params() {
	for my $slic (@SlicNums) {
		for my $reg (35 .. 39) {
			write_reg($slic, $reg, 'I', 
				@{$FilterParams[$slic][$reg]});
		}
	}
}

my $ManualCalibrationSleepTime = 0.04; # 40ms

sub manual_calibrate_loop($$) {
	my $write_reg = shift;
	my $read_reg = shift;
	
	# counters to count down to (at most) 0
	my @slic_counters = ();
	for my $i (0 .. $#SlicNums) {
		$slic_counters[$i] = 0x1F;
	}
	
	# start calibration:
	my $calibration_in_progress = 1;
	write_reg(31, $write_reg, 'D', 0x1F);
	mysleep $ManualCalibrationSleepTime;
	
	# wait until all slics have finished calibration, or for timeout
	while ($calibration_in_progress) {
		$calibration_in_progress = 0; # until proven otherwise
		my $debug_calib_str = "ManualCalib:: ";
		for my $slic(@SlicNums) {
			my $value = read_reg($slic, $read_reg, 'D');
			$debug_calib_str .= " [$slic_counters[$slic]:$value]";
			if ($value != 0 && $slic_counters[$slic] >= 0) {
				$calibration_in_progress = 1;
				$slic_counters[$slic]--;
				write_reg($slic,$write_reg,'D',$slic_counters[$slic]);
			}
		}
		debug($debug_calib_str);
		# TODO: unnecessary sleep in the last round:
		mysleep $ManualCalibrationSleepTime;
	}
}

sub manual_calibrate() {
	manual_calibrate_loop(98, 88);
	manual_calibrate_loop(99, 89);
}

sub auto_calibrate($$) {
	my $calib_96 = shift;
	my $calib_97 = shift;

	#log_calib_params();
	# start calibration:
	write_to_slic_file(
		sprintf
			"31 WD 61 %02X\n".
			"31 WD 60 %02X\n".
			"", $calib_96, $calib_97
			
	);
	# wait until all slics have finished calibration, or for timeout
	my $sleep_cnt = 0;
	# time periods in seconds:
	my $sleep_time = 0.1;
	my $timeout_time = 2; 
	CALIB_LOOP: for my $slic (@SlicNums) {
		debug("checking slic $slic");
		while(1) {
			if ((read_reg($slic, 60, 'D')) == 0) {
				# move to next register
				debug("slic $slic calibrated");
				last;
			}
			if ( $sleep_cnt > $timeout_time/$sleep_time) {
				debug("Auto Calibration: Exiting on timeout: $timeout_time.");
				last CALIB_LOOP;
			}
			debug("auto_calibrate not done yet: slic #$slic\n");
			mysleep(0.1);
			$sleep_cnt++;
		}
	}
	#log_calib_params();
}

###########################################################
#
# main
#

# TODO: for all slics check the following reads to check communication
#read_reg($slic, 0x08, 'D'): 0x02
#read_reg($slic, 0x0B, 'D'): 0x33
#read_reg($slic, 0x40, 'D'): 0x00 (?)

debug "starting\n";

init_indirect_registers();
debug "after init_indirect_registers\n";
init_early_direct_regs();
debug "after init_early_direct_regs\n";
auto_calibrate(0x47, 0x1E);
debug "after auto_calibrate\n";
manual_calibrate();
debug "after manul_calibrate\n";
auto_calibrate(0x40, 0x01);
debug "after auto_calibrate 2\n";


