#! /usr/bin/perl -w
use strict;

#
# Written by Oron Peled <oron@actcom.co.il>
# Copyright (C) 2006, Xorcom
#
# All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# See the file LICENSE in the top level of this tarball.
#

#
# $Id: init_card_2_30 4266 2008-05-13 21:08:09Z tzafrir $
#
# Data format:
#	- A comment start with ';' or '#' until the end of line
#	- Blank lines are ignored
#	- Fields are whitespace separated (spaces or tabs)
#
# The fields are (in command line order):
#	1. SLIC select in decimal (range 0-7).
#	   * is a special value which means ALL SLICS (only some registers
#	   accept settings for ALL SLICS).
#	2. Command word:
#		- RD	Read Direct register.
#		- RI	Read Indirect register.
#		- WD	Write Direct register.
#		- WI	Write Indirect register.
#	3. Register number in hexadecimal.
#	4. Low data byte in hexadecimal. (for WD and WI commands).
#	5. High data byte in hexadecimal. (for WI command only).
#
#

package main;
use File::Basename;
use Getopt::Std;

my $program = basename("$0");
my $init_dir = dirname("$0");
BEGIN { $init_dir = dirname($0); unshift(@INC, "$init_dir", "$init_dir/zconf", "$init_dir/utils/zconf"); }
use Zaptel::Config::Defaults;
my $unit_id;
my %opts;
$ENV{XPP_BASE} = '/proc/xpp';

getopts('o:', \%opts);

my $debug;

my $xpd_name;
my $chipregs;

sub logit {
	print STDERR "$unit_id: @_\n";
}

sub debug {
	logit @_ if $debug;
}

# Arrange for error logging
if (-t STDERR) {
	$unit_id = 'Interactive';
	main::debug "Interactive startup";
} else {
	$unit_id = "$ENV{XBUS_NAME}/UNIT-$ENV{UNIT_NUMBER}";
	$xpd_name = sprintf("XPD-%1d0", $ENV{UNIT_NUMBER});
	$chipregs = "$ENV{XPP_BASE}/$ENV{XBUS_NAME}/$xpd_name/chipregs";
	open (STDERR, "| logger -t $program -p kern.info") || die;
	main::debug "Non Interactive startup";
}

sub set_output() {
	my $output;

	if($opts{o}) {
		$output = $opts{o};
	} else {
		# No subunits in FXS (everything is subunit 0)
		$output = $chipregs;
	}
	open(REG, ">$output") || die "Failed to open '$output': $!\n";
	my $oldfh = select REG;
	main::logit "# Setting output" if $opts{o};
	return $oldfh;
}

package FXO;

sub gen {
	my $fmt = shift;
	$| = 1;
	printf "$fmt\n", @_;
}

my $OPERMODE = 'FCC';

sub turn_off_leds() {
	# Turning off red LEDs
	# Warning: do not send WD 31 20 A0 !
	foreach my $i (0..7) {
		FXO::gen "$i WD 20 A0";
	}
}

# This data is manually taken from utils/init_fxo_modes which is generated
# during build.
# Running this script with a single 'verify' argument, during build,
# compare this data to a (possibly updated) utils/init_fxo_modes file.
my $OPERMODE_DATA = "
FCC            	reg16=01	reg26=C0	reg30=00	reg31=20		
TBR21          	reg16=00	reg26=C2	reg30=02	reg31=20	ring_osc=7E6C	ring_x=023A
ARGENTINA      	reg16=00	reg26=C0	reg30=00	reg31=20		
AUSTRALIA      	reg16=40	reg26=30	reg30=03	reg31=20		
AUSTRIA        	reg16=00	reg26=C2	reg30=03	reg31=28		
BAHRAIN        	reg16=00	reg26=C2	reg30=02	reg31=20		
BELGIUM        	reg16=00	reg26=C2	reg30=02	reg31=28		
BRAZIL         	reg16=00	reg26=30	reg30=00	reg31=20		
BULGARIA       	reg16=00	reg26=C2	reg30=03	reg31=20		
CANADA         	reg16=00	reg26=C0	reg30=00	reg31=20		
CHILE          	reg16=00	reg26=C0	reg30=00	reg31=20		
CHINA          	reg16=00	reg26=30	reg30=0F	reg31=20		
COLUMBIA       	reg16=00	reg26=C0	reg30=00	reg31=20		
CROATIA        	reg16=00	reg26=C2	reg30=02	reg31=20		
CYRPUS         	reg16=00	reg26=C2	reg30=02	reg31=20		
CZECH          	reg16=00	reg26=C2	reg30=02	reg31=20		
DENMARK        	reg16=00	reg26=C2	reg30=02	reg31=28		
ECUADOR        	reg16=00	reg26=C0	reg30=00	reg31=20		
EGYPT          	reg16=00	reg26=30	reg30=00	reg31=20		
ELSALVADOR     	reg16=00	reg26=C0	reg30=00	reg31=20		
FINLAND        	reg16=00	reg26=C2	reg30=02	reg31=28		
FRANCE         	reg16=00	reg26=C2	reg30=02	reg31=28		
GERMANY        	reg16=00	reg26=C2	reg30=03	reg31=28		
GREECE         	reg16=00	reg26=C2	reg30=02	reg31=28		
GUAM           	reg16=00	reg26=C0	reg30=00	reg31=20		
HONGKONG       	reg16=00	reg26=C0	reg30=00	reg31=20		
HUNGARY        	reg16=00	reg26=C0	reg30=00	reg31=20		
ICELAND        	reg16=00	reg26=C2	reg30=02	reg31=28		
INDIA          	reg16=00	reg26=C0	reg30=04	reg31=20		
INDONESIA      	reg16=00	reg26=C0	reg30=00	reg31=20		
IRELAND        	reg16=00	reg26=C2	reg30=02	reg31=28		
ISRAEL         	reg16=00	reg26=C2	reg30=02	reg31=20		
ITALY          	reg16=00	reg26=C2	reg30=02	reg31=28		
JAPAN          	reg16=00	reg26=30	reg30=00	reg31=20		
JORDAN         	reg16=00	reg26=30	reg30=00	reg31=20		
KAZAKHSTAN     	reg16=00	reg26=C0	reg30=00	reg31=20		
KUWAIT         	reg16=00	reg26=C0	reg30=00	reg31=20		
LATVIA         	reg16=00	reg26=C2	reg30=02	reg31=20		
LEBANON        	reg16=00	reg26=C2	reg30=02	reg31=20		
LUXEMBOURG     	reg16=00	reg26=C2	reg30=02	reg31=28		
MACAO          	reg16=00	reg26=C0	reg30=00	reg31=20		
MALAYSIA       	reg16=00	reg26=30	reg30=00	reg31=20		
MALTA          	reg16=00	reg26=C2	reg30=02	reg31=20		
MEXICO         	reg16=00	reg26=C0	reg30=00	reg31=20		
MOROCCO        	reg16=00	reg26=C2	reg30=02	reg31=20		
NETHERLANDS    	reg16=00	reg26=C2	reg30=02	reg31=28		
NEWZEALAND     	reg16=00	reg26=C0	reg30=04	reg31=20		
NIGERIA        	reg16=00	reg26=C2	reg30=02	reg31=20		
NORWAY         	reg16=00	reg26=C2	reg30=02	reg31=28		
OMAN           	reg16=00	reg26=30	reg30=00	reg31=20		
PAKISTAN       	reg16=00	reg26=30	reg30=00	reg31=20		
PERU           	reg16=00	reg26=C0	reg30=00	reg31=20		
PHILIPPINES    	reg16=00	reg26=30	reg30=00	reg31=20		
POLAND         	reg16=03	reg26=C0	reg30=00	reg31=20		
PORTUGAL       	reg16=00	reg26=C2	reg30=02	reg31=28		
ROMANIA        	reg16=00	reg26=C0	reg30=00	reg31=20		
RUSSIA         	reg16=00	reg26=30	reg30=00	reg31=20		
SAUDIARABIA    	reg16=00	reg26=C0	reg30=00	reg31=20		
SINGAPORE      	reg16=00	reg26=C0	reg30=00	reg31=20		
SLOVAKIA       	reg16=00	reg26=C0	reg30=03	reg31=20		
SLOVENIA       	reg16=00	reg26=C0	reg30=02	reg31=20		
SOUTHAFRICA    	reg16=42	reg26=C0	reg30=03	reg31=20		
SOUTHKOREA     	reg16=00	reg26=C0	reg30=00	reg31=20		
SPAIN          	reg16=00	reg26=C2	reg30=02	reg31=28		
SWEDEN         	reg16=00	reg26=C2	reg30=02	reg31=28		
SWITZERLAND    	reg16=00	reg26=C2	reg30=02	reg31=28		
SYRIA          	reg16=00	reg26=30	reg30=00	reg31=20		
TAIWAN         	reg16=00	reg26=30	reg30=00	reg31=20		
THAILAND       	reg16=00	reg26=30	reg30=00	reg31=20		
UAE            	reg16=00	reg26=C0	reg30=00	reg31=20		
UK             	reg16=00	reg26=C2	reg30=05	reg31=28		
USA            	reg16=00	reg26=C0	reg30=00	reg31=20		
YEMEN          	reg16=00	reg26=C0	reg30=00	reg31=20		
	";

my %opermode_table;

sub opermode_setup() {
	main::logit "Setting OPERMODE=$OPERMODE";
	# Several countries (South Africa, UAE, anybody else)
	# require a shorter delay:
	if($OPERMODE eq 'SOUTHAFRICA' or $OPERMODE eq 'UAE') {
		FXO::gen "* WD 17 2B";
	}
	# defaults, based on fxo_modes from wctdm.c . 
	# Decimal register numbers!
	my %regs = (
			16	=> 0,
			26	=> 0,
			30	=> 0,
			31	=> 0x20,
		);
	my $mode = $opermode_table{$OPERMODE};
	if(defined $mode) {
		foreach my $k (keys %regs) {
			my $fullkey = "reg$k";
			$regs{$k} = $mode->{$fullkey};
		}
	}
	foreach my $k (keys %regs) {
		# Our values are HEXADECIMAL without a 0x prefix!!!
		my $cmd = sprintf "* WD %02X %02X", $k, hex($regs{$k});
		main::debug "    regs: '$cmd'";
		FXO::gen "$cmd";
	}
	main::debug "Finished Opermode";
}

sub parse_opermode_line($) {
	my $line = shift or return();

	chomp $line;
	$line =~ s/#.*//;
	my @params = split(/\s+/, $line);
	my $location = shift @params;
	my $entry = {};
	foreach my $p (@params) {
		my ($key, $val) = split(/=/, $p, 2);
		$entry->{$key} = $val;
	}
	return ($location, $entry);
}

sub opermode_preprocess() {
	undef %opermode_table;
	foreach my $line (split(/\n/, $OPERMODE_DATA)) {
		my ($location, $entry) = parse_opermode_line($line);
		next unless defined $location;
		#print "$location\t", ref($entry), "\n";
		die "An entry for '$location' already exists\n"
			if exists $opermode_table{$location};
		$opermode_table{$location} = $entry;
	}
}

sub opermode_to_string($) {
	my $mode = shift or die;
	my @params;

	foreach my $k (sort keys %{$mode}) {
		push(@params, "$k=$mode->{$k}");
	}
	return join(" ", @params);
}

sub opermode_verify($) {
	my $input = shift or die;
	my %verification_table;

	open(F, $input) or die "$0: Failed opening '$input': $!\n";
	while(<F>) {
		chomp;
		#print "$_\n";
		s/#.*//;
		my @params = split;
		my $location = shift @params;
		foreach my $p (@params) {
			my ($key, $val) = split(/=/, $p, 2);
			$verification_table{$location}{$key} = $val;
		}
	}
	close F;
	# First test: check for missing data in our program
	foreach my $location (sort keys %verification_table) {
		my $mode = $opermode_table{$location};
		if(! defined $mode) {
			printf STDERR  "Missing $location\n";
			next;
		}
		my $verify_mode = $verification_table{$location};
		my $str1 = opermode_to_string($mode);
		my $str2 = opermode_to_string($verify_mode);
		if($str1 ne $str2) {
			print STDERR  "DIFF: $location:\n";
			printf STDERR  "\t%-20s: %s\n", "program", $str1;
			printf STDERR  "\t%-20s: %s\n", "verify", $str2;
		}
	}
	# Second test: check for extra data in our program
	foreach my $location (sort keys %opermode_table) {
		my $mode = $verification_table{$location};
		if(! defined $mode) {
			printf STDERR  "Extra $location\n";
			next;
		}
	}
}

sub read_defaults() {
	# For lab tests
	my $labfile = "$init_dir/genzaptelconf.env";

	# Source default files
	$ENV{ZAPTEL_DEFAULTS} = "$labfile" if -r "$labfile";
	my $var_debug = 'DEBUG_INIT_FXO';
	my $var_opermode = 'opermode';
	my ($default_file, %source_defaults) =
		Zaptel::Config::Defaults::source_vars($var_debug, $var_opermode);
	$debug = $source_defaults{$var_debug};
	my $tmp_opermode = $source_defaults{$var_opermode};
	if(defined($tmp_opermode) and $tmp_opermode) {
		# Verify
		my $mode = $opermode_table{$tmp_opermode};
		if(! defined $mode) {
			main::logit "Unknown opermode='$tmp_opermode'";
			die;
		}
		$OPERMODE = $tmp_opermode;
	}
	main::logit "From $default_file: $var_debug=$debug $var_opermode=$tmp_opermode";
}

package main;

FXO::opermode_preprocess;	# Must be first

if(@ARGV and $ARGV[0] =~ /^verify(.*)/) {
	my $verify_file = $1;
	die "Usage: $0 verify=filename\n" unless $verify_file =~ s/^=//;
	main::debug "$0: opermode verification (input='$verify_file')";
	FXO::opermode_verify($verify_file);
	exit 0;
}

main::logit "Starting";

FXO::read_defaults;
die "OPERMODE is undefined" unless $OPERMODE;
set_output;
FXO::turn_off_leds;
while(<DATA>) {
	chomp;
	s/[#;].*$//;		# remove comments
	s/^\s+//;		# trim whitespace
	s/\s+$//;		# trim whitespace
	s/\t+/ /g;		# replace tabs with spaces (for logs)
	next unless /\S/;	# Skip empty lines
	main::debug "writing: '$_'";
	FXO::gen "$_";
}
FXO::opermode_setup;
close REG;

main::logit "Ending '$0'";
close STDERR;
exit 0;

__DATA__
*	WD	21	28
*	WD	18	99
*	WD	06	00

# ----------- DAA PCM start offset ----------

0	WD	22	00
0	WD	23	00
0	WD	24	00
0	WD	25	00

1	WD	22	08
1	WD	23	00
1	WD	24	08
1	WD	25	00

2	WD	22	10
2	WD	23	00
2	WD	24	10
2	WD	25	00

3	WD	22	18
3	WD	23	00
3	WD	24	18
3	WD	25	00

4	WD	22	20
4	WD	23	00
4	WD	24	20
4	WD	25	00

5	WD	22	28
5	WD	23	00
5	WD	24	28
5	WD	25	00

6	WD	22	30
6	WD	23	00
6	WD	24	30
6	WD	25	00

7	WD	22	38
7	WD	23	00
7	WD	24	38
7	WD	25	00

# ----------- DAA ONHOOK --------------------
*	WD	05	00

# Set tip to ring voltage to 3.5 volts while off-hook
# instead of default of 3.1
*	WD	1A	C0
