bf3fe05dfb
This needs some more testing before it's on by default. If the card is otherwise functioning, these messages may be confusing to the user. If the card is not functioning, the driver can be reloaded with debug to check for this condition. Signed-off-by: Shaun Ruffell <sruffell@digium.com> git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/trunk@9205 a0bf4364-ded3-4de4-8d8a-66a801d63aff
591 lines
12 KiB
Perl
Executable File
591 lines
12 KiB
Perl
Executable File
#! /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
|