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

#
# $Id: init_card_4_30 4324 2008-05-28 16:39:08Z mattf $
#

#
# Written by Oron Peled <oron@actcom.co.il>
# Copyright (C) 2007, 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.
#

# This script is run from the xpp kernel module upon detection
# of a new XPD.
#
# Expects the following environment variables to be set:
#	XBUS_NAME	- bus name
#	UNIT_NUMBER	- xpd unit number
#	UNIT_SUBUNITS	- number of subunits in this xpd
#	UNIT_TYPE	- xpd type number (from protocol reply):
#			1 - FXS
#			2 - FXO
#			3 - BRI
#			4 - PRI
#	XBUS_REVISION	- xbus revision number
#	XBUS_CONNECTOR	- xbus connector string
#
# Output data format:
#	- An optional comment start with ';' or '#' until the end of line
#	- Optional Blank lines are ignored
#	- Fields are whitespace separated (spaces or tabs)
#
# The fields are (in command line order):
#	1. CHIP select in decimal (ignored, taken from 3 LSB's of subunit number)
#	2. Command word:
#		- RD	Read Direct register.
#		- WD	Write Direct register.
#	3. Register number in hexadecimal.
#	5. Data byte in hexadecimal. (for WD command only).
#

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

my $program = basename("$0");
my $init_dir = dirname("$0");
my $unit_id;
my %opts;
$ENV{XPP_BASE} = '/proc/xpp';
my @pri_specs;

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

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

# Arrange for error logging
if (-t STDERR) {
	$unit_id = 'Interactive';
	logit "Interactive startup";
} else {
	$unit_id = "$ENV{XBUS_NAME}/UNIT-$ENV{UNIT_NUMBER}";
	open (STDERR, "| logger -t $program -p kern.info") || die;
	logit "Non Interactive startup";
}

sub select_subunit($) {
	my $subunit = shift;
	die unless defined $subunit;
	my $output;

	if($opts{o}) {
		$output = $opts{o};
	} else {
		my $xpd_name = sprintf("XPD-%1d%1d", $ENV{UNIT_NUMBER}, $subunit);
		$output = "$ENV{XPP_BASE}/$ENV{XBUS_NAME}/$xpd_name/chipregs";
	}
	open(REG, ">$output") || die "Failed to open '$output': $!\n";
	my $oldfh = select REG;
	print "# Selecting subunit $subunit\n" if $opts{o};
	return $oldfh;
}

package PRI;

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

sub init_quad() {
	main::select_subunit(0);
	# Tuning of clocking unit to the 16.384 MHz reference frequence
	# by setting Global Clock Mode registers (GCM[1:8]), same for E1 and T1/J1
	PRI::gen "0 WD 92 00"; 		# GCM1
	PRI::gen "0 WD 93 18";		# GCM2
	PRI::gen "0 WD 94 FB";		# GCM3
	PRI::gen "0 WD 95 0B";		# GCM4
	PRI::gen "0 WD 96 01";		# GCM5
	PRI::gen "0 WD 97 0B";		# GCM6
	PRI::gen "0 WD 98 DB";		# GCM7
	PRI::gen "0 WD 99 DF";		# GCM8
}

sub finish_quad() {
	PRI::gen "0 WD BB FF"; 		# REGFP
	PRI::gen "0 WD BC AC"; 		# REGFD
	PRI::gen "0 WD BB 2B"; 		# REGFP
	PRI::gen "0 WD BC 00"; 		# REGFD
	PRI::gen "0 WD BB AB"; 		# REGFP
	PRI::gen "0 WD BC 2A"; 		# REGFD
	PRI::gen "0 WD BB FF"; 		# REGFP
	PRI::gen "0 WD BC AA"; 		# REGFD
	PRI::gen "0 WD BB 29"; 		# REGFP
	PRI::gen "0 WD BC FF"; 		# REGFD
	PRI::gen "0 WD BB A9"; 		# REGFP
	PRI::gen "0 WD BC 28"; 		# REGFD
	PRI::gen "0 WD BB 00"; 		# REGFP
	PRI::gen "0 WD BC A8"; 		# REGFD
	PRI::gen "0 WD BB 27"; 		# REGFP
	PRI::gen "0 WD BC FF"; 		# REGFD
	PRI::gen "0 WD BB A7"; 		# REGFP
	PRI::gen "0 WD BC 00"; 		# REGFD

#	PRI::gen "0 WD 80 00"; 	# PC1 (Port configuration 1): RPB_1.SYPR           , XPB_1.SYPX
	PRI::gen "0 WD 81 0B"; 	# PC2 (Port configuration 2): RPB_1.GPOH (ResetID ), XPB_1.GPOL (MUX_SEL0)
	PRI::gen "0 WD 82 9B"; 	# PC3 (Port configuration 3): RPC_1.GPI  (nConfig0), XPC_1.GPOL (MUX_SEL1)
	PRI::gen "0 WD 83 9B"; 	# PC4 (Port configuration 4): RPD_1.GPI  (nConfig1), XPD_1.GPOL (MUX_SEL2)
}

sub read_pri_specs() {
	# For lab tests
	my $labfile = "${0}.setup";

	# Source default files
	$ENV{ZAPTEL_DEFAULTS} = "$labfile" if -r "$labfile";
	my $setup_var = 'XPP_PRI_SETUP';
	my $setup_string;
	my ($default_file, %source_defaults) =
		Zaptel::Config::Defaults::source_vars($setup_var);
	$setup_string = $source_defaults{$setup_var};
	$setup_string =~ s/^\s+//;		# trim
	$setup_string =~ s/\s+$//;		# trim
	$setup_string =~ s/\s+/\n/g;		# cannonical spaces
	#main::logit "From $default_file: $setup_var=\n$setup_string";
	@pri_specs = split(/\s+/, $setup_string);
	push(@pri_specs, 'NUM/*=TE,E1');	# Fall back default (last)
	main::logit "pri_specs: @pri_specs";
}

package PRI::Port;

sub new {
	my $pack = shift;
	my $port = { @_ };
	bless $port, $pack;
	$port->process_pri_spec;
	return $port;
}

sub write_pri_info {
	my $port = shift;
	my $subunit = $port->{PORT_NUM};
	my @pri_setup = @{$port->{PRI_SETUP}};
	my $pri_type = $pri_setup[0] || die "Missing pri_type parameter";
	my $pri_proto = $pri_setup[1] || die "Missing pri_proto parameter";
	my $xpd_name = "XPD-$ENV{UNIT_NUMBER}$subunit";
	my $info = "$ENV{XPP_BASE}/$ENV{XBUS_NAME}/$xpd_name/pri_info";	

	main::logit "$xpd_name: PRI_SETUP $pri_type $pri_proto";
	open(INFO, ">$info") || die "Failed to open '$info': $!\n";
	print INFO "$pri_type $pri_proto\n" || die "Failed writing to '$info': $!\n";
	close INFO || die "Failed during close of '$info': $!\n";
}

sub process_pri_spec($) {
	my $port = shift;
	my $subunit = $port->{PORT_NUM};
	my $xpd_name = "XPD-$ENV{UNIT_NUMBER}$subunit";
	my $match;
	my $setup;
	my @pri_setup;
SPEC:
	for(my $i = 0; $i < @pri_specs; $i++) {
		my $spec = $pri_specs[$i];
		($match, $setup) = split(/=/, $spec);
		next unless defined $match and defined $setup;
		# Convert "globs" to regex
		$match =~ s/\*/.*/g;
		$match =~ s/\?/./g;
		#logit "match: $match";
		my @patlist = (
			"CONNECTOR/$ENV{XBUS_CONNECTOR}/$xpd_name",
			"NUM/$ENV{XBUS_NAME}/$xpd_name"
			);
		foreach my $pattern (@patlist) {
			#logit "testmatch: $pattern =~ $match";
			if($pattern =~ $match) {
				main::logit "$xpd_name: MATCH '$pattern' ~ '$match' setup=$setup";
				last SPEC;
			}
		}
	}
	die "No setup matching $ENV{XBUS_NAME}/$xpd_name\n" unless defined $setup;
	@pri_setup = split(/,/, $setup);
	die "Bad setup string '$setup'\n" unless @pri_setup;
	$port->{'PRI_SETUP'} = \@pri_setup;
}

sub port_setup($) {
	my $port = shift;
	my $portno = $port->{PORT_NUM};

	# only one of the following loopbacks can be activated in the same time 
	my $LIM1_RL  = 0 << 1; 	# RL  (Remote  Loopback)
	my $lim1 = 0xB0 | $LIM1_RL;

	PRI::gen "$portno WD 26 F6"; 	# XPM0: Pulse Shape Programming for R1=18Ohms 
	PRI::gen "$portno WD 27 02"; 	# XPM1: ...3V Pulse Level at the line (Vp-p=6v)
	PRI::gen "$portno WD 28 00"; 	# XPM2: ~XLT (transmit line is not in the high impedance state)

					# if (unchannelized)
	#PRI::gen "$portno WD 1F 22";	# LOOP (Channel Looback): 
					#      ECLB (Enable Channel Loop-Back) 
					#      CLA  (Channel Address)
	PRI::gen "$portno WD 2B EF";	# IDL (Idle): 
					#      If channel loopback is enabled than transmit this code on the outgoing
	PRI::gen "$portno WD 1F 00";	# LOOP (Channel Looback): 
	#if($portno eq 0){ 
	#	PRI::gen "0 WD 1F 00";	# LOOP (Channel Looback): 
	#				#      channels (XL1/XL2)
	#}else { 
	#	PRI::gen "0 WD 1F 20";	# LOOP (Channel Looback): 
	#}

	PRI::gen "$portno WD 37 %02X", $lim1;
					# LIM1: ~RL (Remote Loop bit 0x02),
					#       ~DRS (Dual Rail Select, latch receive data while trasmit),
					#       RIL1, RIL0 (Receive Input Treshold 0.62 V),
					#       CLOS (Clear data in case of LOS)
	PRI::gen "$portno WD 3A 20";	# LIM2: SLT1, SLT0 = 01 
					#            (Receiver Slicer Threshold, the receive slicer 
					#             generates a mark (digital one) if the voltage at
					#             RL1/2 exceeds 50% of the peak amplitude,
					#             default, recommended in E1 mode).
	  
	PRI::gen "$portno WD 38 0A"; 	# PCD: (Pulse Count Detection, LOS Detection after 176 consecutive 0s)
	PRI::gen "$portno WD 39 15"; 	# PCR: (Pulse Count Recovery, LOS Recovery after 22 ones in PCD interval)

	# Configure system interface
	PRI::gen "$portno WD 3E C2";	# SIC1: SSC1 (System clock    ) is 8.192 Mhz, 
					#       SSD1 (System Data rate) is 8.192 Mbit/s,
					#	~BIM (Byte interleaved mode),
					#	XBS  (Transmit Buffer Size) is 2 frames
	PRI::gen "$portno WD 40 04";	# SIC3: Edges for capture, Synchronous Pulse Receive @Rising Edge
	PRI::gen "$portno WD 41 04";	# CMR4: RCLK is 8.192 MHz
	PRI::gen "$portno WD 43 04";	# CMR5: TCLK is 8.192 MHz
	PRI::gen "$portno WD 44 34";	# CMR6: Receive reference clock generated by channel 1,
					#       RCLK is at 8.192 Mhz dejittered, Clock recovered from the line
					#       TCLK is at 8.192 MHz is de-jittered by DCO-R to drive a6.176 MHz 
					#       clock on RCLK.*/

	PRI::gen "$portno WD 22 00"; 	# XC0: (Transmit Counter Offset = 497/T=2)
	PRI::gen "$portno WD 23 04"; 	# XC1: 

	PRI::gen "$portno WD 24 00"; 	# RC0: (Receive  Counter Offset = 497/T=2)
	PRI::gen "$portno WD 25 05"; 	# RC1: 

	my $sic2 = sprintf("%x", 0x00 | ($portno << 1));

	PRI::gen "$portno WD 3F $sic2";	# SIC2: No FFS, no center receive elastic buffer, data active at phase ($sic >> 1)
		
	# enable the following interrupt sources
	PRI::gen "$portno WD 16 00"; 	# IMR2 (Interrupt Mask Register2): Enable ALL
		
	PRI::gen "$portno WD 17 3F"; 	# IMR3 ~ES, ~SEC (Enable ES and SEC interrupts)
	PRI::gen "$portno WD 18 00"; 	# IMR4: Enable ALL

	PRI::gen "$portno WD 08 04";	# IPC: SYNC is 8 Khz

	PRI::gen "$portno WD 02 51"; 	# CMDR (Command Register): RRES, XRES, SRES (Receiver/Transmitter reset)
	PRI::gen "$portno WD 02 00"; 	# CMDR


	#  Configure interrupts
	PRI::gen "$portno WD 46 40";	# GCR: Interrupt on Activation/Deactivation of AIX, LOS
								
	PRI::gen "$portno WD 45 00";	# CMR2: External sources for SYPR, SCLKR, SYPX, SCLKX for TX and RX.
	#PRI::gen "$portno WD 22 00";	# XC0: Normal operation of Sa-bits
	#PRI::gen "$portno WD 23 04";	# XC1: X=4  => T=4-X=0 offset
	#PRI::gen "$portno WD 24 00";	# RC0: 0 offset
	#PRI::gen "$portno WD 25 00";	# RC1: Remaining part of RC0

	#  Configure ports
	PRI::gen "$portno WD 85 80";		# GPC1 (Global Port Configuration 1):
	#PRI::gen "$portno WD 85 00";		# GPC1 (Global Port Configuration 1):
								#      SMM (System Interface Multiplex Mode)
	PRI::gen "$portno WD 80 00";	# PC1: SYPR/SYPX provided to RPA/XPA inputs

	PRI::gen "$portno WD 84 31";	# PC5: XMFS active low, SCLKR is input, RCLK is output (unused)
	PRI::gen "$portno WD 86 03";	# PC6: CLK1 is Tx Clock output, CLK2 is 8.192 Mhz from DCO-R
	PRI::gen "$portno WD 3B 00";	# Clear LCR1 - Loop Code Register 1

	#  printk("TE110P: Successfully initialized serial bus for card\n");

	# Initialize PCM and SIG regs
	PRI::gen "$portno WD A0 00";	# TSEO (Time Slot Even/Odd Select) 
	PRI::gen "$portno WD A1 FF";	# TSBS (Time Slot Bit Select)- only selected bits are used for HDLC channel 1
					#      in selected time slots
	PRI::gen "$portno WD 03 89";	# Mode Register:
					#      MDS  (Mode Select) = 100 (No address comparison)
					#      HRAC (Receiver Active - HDLC channel 1) 
					#      RFT2 (HDLC Receive FIFO is 64 byte deep)
	PRI::gen "$portno WD 09 18";	# CCR1 (Common Configuration Register1)
					#      EITS (Enable Internal Time Slot 0 to 31 Signalling)
					#      ITF  (Interframe Time Fill)
	PRI::gen "$portno WD 0A 04";	# CCR2 (Common Configuration Register2)
					#      RCRC (enable CRC - HDLC channel 1enable CRC - HDLC channel 1)
	PRI::gen "$portno WD 0C 00";	# RTR1 (Receive  Time Slot register 1)
	PRI::gen "$portno WD 0D 00";	# RTR2 (Receive  Time Slot register 2)
	PRI::gen "$portno WD 0E 00";	# RTR3 (Receive  Time Slot register 3), TS16 (Enable time slot 16) 
	PRI::gen "$portno WD 0F 00";	# RTR4 (Receive  Time Slot register 4)

	PRI::gen "$portno WD 10 00";	# TTR1 (Transmit Time Slot register 1)
	PRI::gen "$portno WD 11 00";	# TTR2 (Transmit Time Slot register 2)
	PRI::gen "$portno WD 12 00";	# TTR3 (Transmit Time Slot register 3), TS16 (Enable time slot 16) 
	PRI::gen "$portno WD 13 00";	# TTR4 (Transmit Time Slot register 4)

	# configure the best performance of the Bipolar Violation detection for all four channels
	PRI::gen "$portno WD BD 00";	# BFR (Bugfix Register): ~BVP (Bipolar Violations),
					#                         use Improved Bipolar Violation Detection instead
}

package main;
BEGIN { my $dir = dirname($0); unshift(@INC, "$dir", "$dir/zconf"); }
use Zaptel::Config::Defaults;

logit "Starting '$0'";

PRI::read_pri_specs;

sub main() {
	my @ports;
	my $subunit;

	logit "main(): Initializing chip ($ENV{UNIT_SUBUNITS} ports)";
	PRI::init_quad;
	# Must initialize all 4 ports, regardless how much there are
	for($subunit = 0; $subunit < 4; $subunit++) {
		my $is_nt = 0;

		#logit "main(): Initializing subunit $subunit is_nt=$is_nt";
		my $p = PRI::Port->new(
				'PORT_NUM'		=> $subunit,
				'PRI_NT'		=> $is_nt,
				'EXIST'			=> ($subunit < $ENV{UNIT_SUBUNITS})
				);
		$p->port_setup;
		push(@ports, $p);
	}
	PRI::finish_quad;
	foreach my $p (@ports) {
		if($p->{EXIST}) {
			$p->write_pri_info;
		}
	}
}

main;

logit "Ending '$0'";

close REG;
close STDERR;
exit 0;
