123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591 |
- #! /usr/bin/perl -w
- use strict;
- # Make warnings fatal
- local $SIG{__WARN__} = sub { die @_ };
- #
- # 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$
- #
- # 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"); }
- use XppConfig $init_dir;
- my $unit_id;
- my %opts;
- getopts('o:', \%opts);
- my %settings;
- $settings{debug} = 0;
- $settings{fxs_skip_calib} = 0;
- my $chipregs;
- sub logit {
- print STDERR "$unit_id: @_\n";
- }
- sub debug {
- logit @_ if $settings{debug};
- }
- # Arrange for error logging
- if (-t STDERR) {
- $unit_id = 'Interactive';
- debug "Interactive startup";
- } else {
- $unit_id = "$ENV{XBUS_NAME}/UNIT-$ENV{UNIT_NUMBER}";
- open (STDERR, "| logger -t $program -p kern.info") || die;
- debug "Non Interactive startup";
- foreach my $k (qw(
- XBUS_NAME
- XBUS_NUMBER
- UNIT_NUMBER
- UNIT_TYPE
- UNIT_SUBUNITS
- UNIT_SUBUNITS_DIR
- XBUS_REVISION
- XBUS_CONNECTOR
- XBUS_LABEL)) {
- unless(defined $ENV{$k}) {
- logit "Missing ENV{$k}\n";
- die;
- }
- }
- $chipregs = sprintf "/sys/bus/xpds/devices/%02d:%1d:0/chipregs",
- $ENV{XBUS_NUMBER}, $ENV{UNIT_NUMBER};
- if(! -f $chipregs) {
- my $xpd_name = sprintf("XPD-%1d0", $ENV{UNIT_NUMBER});
- $chipregs = "/proc/xpp/$ENV{XBUS_NAME}/$xpd_name/chipregs";
- logit "OLD DRIVER: does not use /sys chipregs. Falling back to /proc"
- if -f $chipregs;
- }
- }
- 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;
- }
- 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));
- my $retries = 10;
- my @reply;
- # If the command queue is long, we may need to wait...
- WAIT_RESULTS:
- {
- my @results;
- # The time to sleep is a tradeoff:
- # - Too long is a waste of time.
- # - Too short will cause many retries, wastes time.
- # So the current value (after trial and error) is...
- main::mysleep(0.013);
- open(SLICS,$chipregs) or
- die("Failed reading from chipregs file $chipregs");
- while(<SLICS>){
- s/#.*//;
- next unless /\S/;
- @results = /^\s*(\d+)\s+[RW][DI]\s+([[:xdigit:]]+)\s+([[:xdigit:]]+)\s+([[:xdigit:]]*)/;
- if(@results != 4) {
- main::logit "Failed reading from '$chipregs' ($read_slic,$read_reg,$direct)";
- die;
- }
- }
- close(SLICS);
- my $reg = hex($results[1]);
- if($results[0] ne $read_slic || $reg ne $read_reg) {
- # We read obsolete values, need to wait some more
- if(--$retries) {
- main::debug "$read_slic RD $read_reg -- retry ($results[0], $reg)";
- redo WAIT_RESULTS;
- } else {
- main::logit "Failed: $read_slic RD $read_reg returned $results[0], $reg";
- die;
- }
- }
- # Good.
- @reply = (hex($results[2]), hex($results[3]));
- }
- 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 FD 7E
- * WS 1E 15 77 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 6F 37
- * WS 1E 22 80 1B
- * WS 1E 23 00 80
-
- * WS 1E 24 00 08
- * WS 1E 25 00 08
- * WS 1E 26 00 08
- * WS 1E 27 00 08
-
- * WS 1E 28 00 00
- * WS 1E 2B 00 08 # LCRTL = 5.08 mA
-
- * 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 6C 01
- * WD 4A 3F # 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;
- my @curr_slics = @SlicNums;
- # initialize counters
- my @slic_counters = map { 0x1F } @curr_slics;
- # wait until all slics have finished calibration, or for timeout
- while (@curr_slics) {
- my $debug_calib_str = "ManualCalib:: ";
- my @next_slics;
- for my $slic (@curr_slics) {
- write_reg($slic,$write_reg,'D',$slic_counters[$slic]);
- }
- main::mysleep $ManualCalibrationSleepTime;
- for my $slic (@curr_slics) {
- my $value = read_reg($slic, $read_reg, 'D');
- $debug_calib_str .= sprintf " [%d:%d:%X]",
- $slic, $slic_counters[$slic], $value;
- next if $value == 0; # This one is calibrated.
- if ($slic_counters[$slic] > 0) {
- $slic_counters[$slic]--;
- push(@next_slics, $slic);
- } else {
- main::logit("ERROR: SLIC $slic reached 0 during manual calibration");
- }
- }
- @curr_slics = @next_slics;
- main::debug($debug_calib_str);
- }
- main::debug("No more slics to calibrate");
- }
- 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:
- for my $slic(@SlicNums) {
- write_to_slic_file(
- sprintf
- "$slic WD 61 %02X\n".
- "$slic WD 60 %02X\n".
- "", $calib_97, $calib_96
-
- );
- }
- # wait until all slics have finished calibration, or for timeout
- # time periods in seconds:
- my $sleep_time = 0.001;
- my $timeout_time = 0.600; # Maximum from the spec
- my @curr_slics = @SlicNums;
- my $sleep_cnt = 0;
- CALIB_LOOP:
- while(1) {
- main::mysleep($sleep_time);
- my @next_slics;
- for my $slic (@curr_slics) {
- main::debug("checking slic $slic");
- my $val = read_reg($slic, 96, 'D');
- push(@next_slics, $slic) if $val != 0;
- }
- @curr_slics = @next_slics;
- last unless @curr_slics;
- if ($sleep_cnt * $sleep_time > $timeout_time) {
- main::logit("Auto Calibration: Exiting on timeout: $timeout_time.");
- last CALIB_LOOP;
- }
- main::debug("auto_calibrate not done yet($sleep_cnt): @curr_slics");
- $sleep_cnt++;
- }
- #log_calib_params();
- }
- sub calibrate_slics() {
- main::debug "Calibrating '$0'";
- auto_calibrate(0x40, 0x1E);
- main::debug "after auto_calibrate";
- manual_calibrate();
- main::debug "after manul_calibrate";
- auto_calibrate(0x40, 0x01);
- main::debug "after auto_calibrate 2";
- main::debug "Continue '$0'";
- }
- sub read_defaults() {
- if(XppConfig::read_config(\%settings)) {
- main::logit "Defaults from $settings{xppconf}";
- } else {
- main::logit "No defaults file, use hard-coded defaults.";
- }
- }
- # Try to identify which slics are valid
- sub check_slics() {
- my @slics;
- foreach my $slic (0 .. 7) {
- my $value = read_reg($slic, 0, 'D');
- push(@slics, $slic) if $value != 0xFF;
- }
- main::logit "Found " . scalar(@slics) . " SLICS (@slics)";
- return @slics;
- }
- package main;
- main::debug "Starting '$0'";
- FXS::read_defaults;
- @SlicNums = FXS::check_slics;
- 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($settings{fxs_skip_calib}) {
- main::logit "==== WARNING: SKIPPED SLIC CALIBRATION =====";
- } else {
- FXS::calibrate_slics;
- }
- set_output;
- 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: '$_'";
- print "$_\n";
- }
- close REG;
- main::debug "Ending '$0'";
- close STDERR;
- exit 0;
- # ----------------------------------==== 8-channel FXS unit initialization ===-----------------------------------------
- __DATA__
- # 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 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
- # Clear and disable interrupts
- * WD 12 FF
- * WD 13 FF
- * WD 14 FF
- * WD 15 00
- * WD 16 00
- * WD 17 00
-
- ## Mode(8-bit,u-Law,1 PCLK )
- * WD 01 08 # Disable PCM transfers
- # Setting of SLICs offsets
- # New card initialization
- * WD 03 00
- * WD 05 00
- 0 WD 02 00
- 0 WD 04 00
- 0 WD 01 28 # Enable PCM transfers
- 1 WD 02 08
- 1 WD 04 08
- 1 WD 01 28
- 2 WD 02 10
- 2 WD 04 10
- 2 WD 01 28
- 3 WD 02 18
- 3 WD 04 18
- 3 WD 01 28
- 4 WD 02 20
- 4 WD 04 20
- 4 WD 01 28
- 5 WD 02 28
- 5 WD 04 28
- 5 WD 01 28
- 6 WD 02 30
- 6 WD 04 30
- 6 WD 01 28
- 7 WD 02 38
- 7 WD 04 38
- 7 WD 01 28
- # Audio path. (also initialize 0A and 0B here if necessary)
- * WD 08 00
- * WD 09 00
- * WD 0A 08
- * WD 0B 33
- #------ Metering tone
- * WD 2C 00 # Timer dL
- * WD 2D 03 # Timer dH
- * WS 1E 17 61 15 # Amplitue Ramp-up
- * WS 1E 18 61 15 # Max Amplitude
- * WS 1E 19 FB 30 # Frequency
- # Ring regs are set by driver
- # 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
- # On-Hook Line Voltage (VOC)
- * WD 48 20
- # Common Mode Voltage (VCM)
- * WD 49 03
- * WS 1E 23 00 80
- * WS 1E 24 20 03
- * WS 1E 25 8C 00
- * WS 1E 26 00 00
- * WS 1E 27 10 00
- * WD 0E 00
|