#! /usr/bin/perl -w use strict; # # Written by Oron Peled # 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$ # # 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. # - RS Read Sub-register. # - WD Write Direct register. # - WS Write Sub-register. # 3. Register number in hexadecimal. # 4. Low data byte in hexadecimal. (for WD and WS commands). # 5. High data byte in hexadecimal. (for WS 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"); } use Zaptel::Config::Defaults; my $unit_id; my %opts; $ENV{XPP_BASE} = '/proc/xpp'; getopts('o:', \%opts); my $debug; my $skip_calib; my $xpd_name = sprintf("XPD-%1d0", $ENV{UNIT_NUMBER}); my $chipregs = "$ENV{XPP_BASE}/$ENV{XBUS_NAME}/$xpd_name/chipregs"; sub logit { print STDERR "$unit_id: @_\n"; } sub debug { logit @_ if $debug; } # 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 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; print "# Setting output\n" if $opts{o}; return $oldfh; } sub mysleep($) { my $timeout = shift; select(undef,undef,undef,$timeout); } package FXS; sub gen { my $fmt = shift; $| = 1; printf "$fmt\n", @_; } my @SlicNums = (0 .. 7); sub write_to_slic_file($) { my $write_str = shift; open(SLICS,">$chipregs") or die("Failed writing to chipregs file $chipregs"); print SLICS $write_str; close(SLICS) or die "Failed writing '$write_str' to '$chipregs': $!"; main::mysleep(0.001); } sub read_reg($$$) { my $read_slic = shift; my $read_reg = shift; my $direct = shift; write_to_slic_file( sprintf("%s R%s %02X", $read_slic, $direct, $read_reg)); main::mysleep(0.005); open(SLICS,$chipregs) or die("Failed reading from chipregs file $chipregs"); #awk '/^SLIC_REPLY:/{print $5}' $SLICS | cut -dx -f2 my @reply = (); while(){ #if (/^ /) { # main::debug "answer line: $_"; #} s/#.*//; next unless /\S/; if (/^ \d*\s+[RW][DI]\s+[[:xdigit:]]+\s+([[:xdigit:]]+)\s+([[:xdigit:]]*)/){ @reply = (hex($1), hex($2)); #main::debug "got [$reply]"; last; } else { main::logit("Got from '$chipregs' a non-matching line '$_'"); } } close(SLICS); die("Failed reading from '$chipregs' ($read_slic,$read_reg,$direct)") unless @reply; if ($direct eq 'S') { 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 "%s W%s %02X %02X", $read_slic, $direct, $read_reg, $reg_val_low; if ($direct eq 'S') { $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'); } main::debug($line); } } sub init_indirect_registers() { return write_to_slic_file("# * WS 1E 00 C2 55 * WS 1E 01 E6 51 * WS 1E 02 85 4B * WS 1E 03 37 49 * WS 1E 04 33 33 * WS 1E 05 02 02 * WS 1E 06 02 02 * WS 1E 07 98 01 * WS 1E 08 98 01 * WS 1E 09 11 06 * WS 1E 0A 02 02 * WS 1E 0B E5 00 * WS 1E 0C 1C 0A * WS 1E 0D 30 7B * WS 1E 0E 63 00 * WS 1E 0F 00 00 * WS 1E 10 70 78 * WS 1E 11 7D 00 * WS 1E 12 00 00 * WS 1E 13 00 00 * WS 1E 14 F0 7E * WS 1E 15 C0 01 * WS 1E 16 00 00 * WS 1E 17 00 20 * WS 1E 18 00 20 * WS 1E 19 00 00 * WS 1E 1A 00 20 * WS 1E 1B 00 40 * WS 1E 1C 00 10 * WS 1E 1D 00 36 * WS 1E 1E 00 10 * WS 1E 1F 00 02 * WS 1E 20 C0 07 * WS 1E 21 00 26 * WS 1E 22 F4 0F * WS 1E 23 00 80 #* WS 1E 24 20 03 #* WS 1E 25 8C 08 #* WS 1E 26 00 01 #* WS 1E 27 10 00 * WS 1E 24 00 08 * WS 1E 25 00 08 * WS 1E 26 00 08 * WS 1E 27 00 08 * WS 1E 28 00 0C * WS 1E 29 00 0C * WS 1E 2B 00 01 * WS 1E 63 DA 00 * WS 1E 64 60 6B * WS 1E 65 74 00 * WS 1E 66 C0 79 * WS 1E 67 20 11 * WS 1E 68 E0 3B #"); } sub init_early_direct_regs() { return write_to_slic_file("# * WD 08 00 # Audio Path Loopback Control * WD 4A 34 # High Battery Voltage * WD 4B 10 # Low Battery Voltage * WD 40 00 # Line Feed Control #") } my @FilterParams = (); sub save_indirect_filter_params() { for my $slic (@SlicNums) { for my $reg (35 .. 39) { $FilterParams[$slic][$reg] = [read_reg($slic, $reg, 'S')]; write_reg($slic, $reg, 'S', 0, 0x80); } } } sub restore_indirect_filter_params() { for my $slic (@SlicNums) { for my $reg (35 .. 39) { write_reg($slic, $reg, 'S', @{$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('*', $write_reg, 'D', 0x1F); main::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]); } } main::debug($debug_calib_str); # TODO: unnecessary sleep in the last round: main::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 "* WD 60 %02X\n". "* WD 61 %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) { main::debug("checking slic $slic"); while(1) { if ((read_reg($slic, 60, 'D')) == 0) { # move to next register main::debug("slic $slic calibrated"); last; } if ( $sleep_cnt > $timeout_time/$sleep_time) { main::debug("Auto Calibration: Exiting on timeout: $timeout_time."); last CALIB_LOOP; } main::debug("auto_calibrate not done yet: slic #$slic"); main::mysleep(0.1); $sleep_cnt++; } } #log_calib_params(); } sub calibrate_slics() { main::logit "Calibrating '$0'"; auto_calibrate(0x47, 0x1E); main::debug "after auto_calibrate"; manual_calibrate(); main::debug "after manul_calibrate"; auto_calibrate(0x40, 0x01); main::debug "after auto_calibrate 2"; main::logit "Continue '$0'"; } 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_FXS'; my $var_skip_calib = 'INIT_FXS_SKIP_CALIB'; my ($default_file, %source_defaults) = Zaptel::Config::Defaults::source_vars($var_debug, $var_skip_calib); $debug = $source_defaults{$var_debug}; $skip_calib = $source_defaults{$var_skip_calib}; main::logit "From $default_file: $var_debug=$debug $var_skip_calib=$skip_calib"; } package main; main::logit "Starting '$0'"; FXS::read_defaults; main::debug "before init_indirect_registers"; FXS::init_indirect_registers(); main::debug "after init_indirect_registers"; FXS::init_early_direct_regs(); main::debug "after init_early_direct_regs"; if($skip_calib) { main::logit "==== WARNING: SKIPPED SLIC CALIBRATION ====="; } else { FXS::calibrate_slics; } set_output; while() { 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: '$_'"; print "$_\n"; } close REG; main::logit "Ending '$0'"; close STDERR; exit 0; # ----------------------------------==== 8-channel FXS unit initialization ===----------------------------------------- __DATA__ # Change SLICs states to "Open state"s (Off,all transfers tristated to avoid data collision), Voltage sense * WD 40 00 # Flush out energy accumulators * WS 1E 58 00 00 * WS 1E 59 00 00 * WS 1E 5A 00 00 * WS 1E 5B 00 00 * WS 1E 5C 00 00 * WS 1E 5D 00 00 * WS 1E 5E 00 00 * WS 1E 5F 00 00 * WS 1E 61 00 00 * WS 1E 58 00 00 * WS 1E C1 00 00 * WS 1E C2 00 00 * WS 1E C3 00 00 * WS 1E C4 00 00 * WS 1E C5 00 00 * WS 1E C6 00 00 * WS 1E C7 00 00 * WS 1E C8 00 00 * WS 1E C9 00 00 * WS 1E CA 00 00 * WS 1E CB 00 00 * WS 1E CC 00 00 * WS 1E CD 00 00 * WS 1E CE 00 00 * WS 1E CF 00 00 * WS 1E D0 00 00 * WS 1E D1 00 00 * WS 1E D2 00 00 * WS 1E D3 00 00 # Setting of SLICs offsets # New card initialization 0 WD 02 00 0 WD 04 00 1 WD 02 08 1 WD 04 08 2 WD 02 10 2 WD 04 10 3 WD 02 18 3 WD 04 18 4 WD 02 20 4 WD 04 20 5 WD 02 28 5 WD 04 28 6 WD 02 30 6 WD 04 30 7 WD 02 38 7 WD 04 38 * WD 03 00 * WD 05 00 # Audio path. (also initialize 0A and 0B here if necessary) * WD 08 00 * WD 09 00 # Automatic/Manual Control: defaults but: # Cancel AOPN - Power Alarm # Cancel ABAT - Battery Feed Automatic Select * WD 43 16 # Loop Closure Debounce Interval * WD 45 0A # Ring Detect Debounce Interval * WD 46 47 # Battery Feed Control: Battery low (DCSW low) * WD 42 00 # Loop Current Limit * WD 47 00 # Ring VBath: * WD 4A 3F * WD 6C 01 * WS 1E 23 00 80 * WS 1E 24 20 03 * WS 1E 25 8C 08 * WS 1E 26 00 01 * WS 1E 27 10 00 #------ Metering tone * WS 1E 17 61 15 # Amplitue Ramp-up * WS 1E 18 61 15 # Max Amplitude * WS 1E 19 FB 30 # Frequency * WD 2C 00 # Timer dL * WD 2D 03 # Timer dH # ------------------------------------- Initialization of direct registers -------------------------------------------- # Mode(8-bit,u-Law,1 PCLK ) setting, Loopbacks and Interrupts clear * WD 01 29 #* WD 0E 00 #* WD 15 00 #* WD 16 03 # Clear pending interrupts * WD 12 FF * WD 13 FF * WD 14 FF #* WD 4A 34 #* WD 4B 10