fxotune: Use DAHDI_SPECIFY when opening by integer channel number.
[dahdi/tools.git] / xpp / perl_modules / Dahdi / Span.pm
1 package Dahdi::Span;
2 #
3 # Written by Oron Peled <oron@actcom.co.il>
4 # Copyright (C) 2007, Xorcom
5 # This program is free software; you can redistribute and/or
6 # modify it under the same terms as Perl itself.
7 #
8 # $Id$
9 #
10 use strict;
11 use Dahdi::Utils;
12 use Dahdi::Chans;
13
14 =head1 NAME
15
16 Dahdi::Spans - Perl interface to a Dahdi span information
17
18 This package allows access from perl to information about a Dahdi
19 channel. It is part of the Dahdi Perl package.
20
21 A span is a logical unit of Dahdi channels. Normally a port in a
22 digital card or a whole analog card.
23
24 See documentation of module L<Dahdi> for usage example. Specifically
25 C<Dahdi::spans()> must be run initially.
26
27 =head1 by_number()
28
29 Get a span by its Dahdi span number.
30
31 =head1 Span Properties
32
33 =head2 num()
34
35 The span number.
36
37 =head2 name()
38
39 The name field of a Dahdi span. E.g.:
40
41   TE2/0/1
42
43 =head2 description()
44
45 The description field of the span. e.g:
46
47   "T2XXP (PCI) Card 0 Span 1" HDB3/CCS/CRC4 RED
48
49 =head2 chans()
50
51 The list of the channels (L<Dahdi::Chan> objects) of this span.
52 In a scalar context returns the number of channels this span has.
53
54 =head2 bchans()
55
56 Likewise a list of bchannels (or a count in a scalar context).
57
58 =head2 is_sync_master()
59
60 Is this span the source of timing for Dahdi?
61
62 =head2 type()
63
64 Type of span, or "UNKNOWN" if could not be detected. Current known
65 types: 
66
67 BRI_TE, BRI_NT, E1_TE, E1_NT, J1_TE, J1_NT, T1_TE, T1_NT, FXS, FXO
68
69 =head2 is_pri()
70
71 Is this an E1/J1/T1 span?
72
73 =head2 is_bri()
74
75 Is this a BRI span?
76
77 =head2 is_digital()
78
79 Is this a digital (as opposed to analog) span?
80
81 =head2 termtype()
82
83 Set for digital spans. "TE" or "NT". Will probably be assumed to be "TE"
84 if there's no information pointing either way.
85
86 =head2 coding()
87
88 Suggested sane coding type (e.g.: "hdb3", "b8zs") for this type of span. 
89
90 =head2 framing()
91
92 Suggested sane framing type (e.g.: "ccs", "esf") for this type of span. 
93
94 =head2 yellow(), crc4()
95
96 Likewise, suggestions ofr the respective fields in the span= line in
97 /etc/dahdi/system.conf for this span.
98
99 =head2 signalling()
100
101 Suggested chan_dahdi.conf signalling for channels of this span.
102
103 =head2 switchtype()
104
105 Suggested chan_dahdi.conf switchtype for channels of this span.
106
107 =head1 Note
108
109 Most of those properties are normally used as lower-case functions, but
110 actually set in the module as capital-letter propeties. To look at e.g.
111 "signalling" is set, look for "SIGNALLING".
112
113 =cut
114
115 sub chans($) {
116         my $span = shift;
117         return @{$span->{CHANS}};
118 }
119
120 sub by_number($) {
121         my $span_number = shift;
122         die "Missing span number" unless defined $span_number;
123         my @spans = Dahdi::spans();
124
125         my ($span) = grep { $_->num == $span_number } @spans;
126         return $span;
127 }
128
129 my @bri_strings = (
130                 'BRI_(NT|TE)',
131                 '(?:quad|octo)BRI PCI ISDN Card.* \[(NT|TE)\]',
132                 'octoBRI \[(NT|TE)\] ',
133                 'HFC-S PCI A ISDN.* \[(NT|TE)\] ',
134                 '(B4XXP) \(PCI\) Card', # Use dahdi_scan to determine TE/NT mode
135                 '(WCBRI)', # has selectable NT/TE modes via dahdi_cfg
136                 );
137
138 my @pri_strings = (
139                 'Tormenta 2 .*Quad (E1|T1)',       # tor2.
140                 'Xorcom XPD.*: (E1|T1)',           # Astribank PRI
141                 'Digium Wildcard .100P (T1|E1)/', # wct1xxp
142                 'ISA Tormenta Span 1',             # torisa
143                 'TE110P T1/E1',                    # wcte11xp
144                 'Wildcard TE120P',                 # wcte12xp
145                 'Wildcard TE121',                  # wcte12xp
146                 'Wildcard TE122',                  # wcte12xp
147                 'T[248]XXP \(PCI\) Card ',          # wct4xxp
148                 'R[24]T1 \(PCI\) Card',            # rxt1
149                 'Rhino R1T1 (E1)/PRA Card',        # r1t1
150                 'Rhino R1T1 (T1)/PRI Card',        # r1t1
151                 'WP(E1|T1)/.* "wanpipe',           # Sangoma E1/T1
152                 );
153
154 my @soft_term_type_strings = (
155                 'Xorcom XPD.*: (E1|T1)',           # Astribank PRI
156                 '(WCBRI)', # has selectable NT/TE modes via dahdi_cfg
157 );
158
159 our $DAHDI_BRI_NET = 'bri_net';
160 our $DAHDI_BRI_CPE = 'bri_cpe';
161
162 our $DAHDI_PRI_NET = 'pri_net';
163 our $DAHDI_PRI_CPE = 'pri_cpe';
164
165 sub init_proto($$) {
166         my $self = shift;
167         my $proto = shift;
168
169         $self->{PROTO} = $proto;
170         if($proto eq 'E1') {
171                 $self->{DCHAN_IDX} = 15;
172                 $self->{BCHAN_LIST} = [ 0 .. 14, 16 .. 30 ];
173         } elsif($proto eq 'T1') {
174                 $self->{DCHAN_IDX} = 23;
175                 $self->{BCHAN_LIST} = [ 0 .. 22 ];
176         }
177         $self->{TYPE} = "${proto}_$self->{TERMTYPE}";
178 }
179
180 sub get_digital_spantype {
181         my $span_no = shift;
182         my @lines = split /\n/, `dahdi_scan`;
183         my $found_span = 0;
184         foreach my $line (@lines) {
185                 if (! $found_span) {
186                         if ($line =~ m/\[$span_no\]/) {
187                                 $found_span = 1;
188                         }
189                 } else {
190                         if ($line !~ m/^\[/) {
191                                 if ($line =~ m/digital-(TE|NT)/ ){
192                                         return $1;
193                                 }
194                         } else {
195                                 $found_span = 0;
196                         }
197                 }
198         }
199         die "Cannot determine digital spantype";
200 }
201
202 sub new($$) {
203         my $pack = shift or die "Wasn't called as a class method\n";
204         my $proc_file = shift or die "Missing a proc file parameter\n";
205         $proc_file =~ m{[^/]*/(\d+)$};
206         my $num = $1 or die " Invalid span file name: $proc_file\n";
207         my $self = { NUM => $num };
208         bless $self, $pack;
209         $self->{TYPE} = "UNKNOWN";
210         open(F, "$proc_file") or die "Failed to open '$proc_file\n";
211         my $head = <F>;
212         chomp $head;
213         $self->{IS_DIGITAL} = 0;
214         $self->{IS_BRI} = 0;
215         $self->{IS_PRI} = 0;
216         $self->{TERMTYPE} = "UNKNOWN";
217         foreach my $cardtype (@bri_strings) {
218                 if($head =~ m/$cardtype/) {
219                         my $termtype = $1;
220                         if ($1 eq 'B4XXP') {
221                                 $termtype = get_digital_spantype($num);
222                         }
223                         if ($1 eq 'WCBRI') {
224                                 $termtype = 'TE';
225                         }
226                         $self->{IS_DIGITAL} = 1;
227                         $self->{IS_BRI} = 1;
228                         $self->{TERMTYPE} = $termtype;
229                         $self->{TYPE} = "BRI_$termtype";
230                         $self->{DCHAN_IDX} = 2;
231                         $self->{BCHAN_LIST} = [ 0, 1 ];
232                         $self->init_proto('BRI');
233                         last;
234                 }
235         }
236         foreach my $cardtype (@pri_strings) {
237                 if($head =~ m/$cardtype/) {
238                         my @info;
239
240                         push(@info, $1) if defined $1;
241                         push(@info, $2) if defined $2;
242                         my ($proto) = grep(/(E1|T1|J1)/, @info);
243                         $proto = 'UNKNOWN' unless defined $proto;
244                         my ($termtype) = grep(/(NT|TE)/, @info);
245                         $termtype = 'UNKNOWN' unless defined $termtype;
246
247                         $self->{IS_DIGITAL} = 1;
248                         $self->{IS_PRI} = 1;
249                         $self->{TERMTYPE} = $termtype;
250                         $self->init_proto($proto);
251                         last;
252                 }
253         }
254         $self->{IS_SOFT_TERM_TYPE} = 0;
255         foreach my $cardtype (@soft_term_type_strings) {
256                 if($head =~ m/$cardtype/) {
257                         $self->{IS_SOFT_TERM_TYPE} = 1;
258                         last;
259                 }
260         }
261
262         if (($self->is_soft_term_type == 0) and ($self->termtype eq "UNKNOWN")) {
263                 $self->{IS_SOFT_TERM_TYPE} = 1;
264         }
265
266         ($self->{NAME}, $self->{DESCRIPTION}) = (split(/\s+/, $head, 4))[2, 3];
267         $self->{IS_DAHDI_SYNC_MASTER} =
268                 ($self->{DESCRIPTION} =~ /\(MASTER\)/) ? 1 : 0;
269         $self->{CHANS} = [];
270         my @channels;
271         my $index = 0;
272         my @channel_lines = <F>;
273         foreach (@channel_lines) {
274                 chomp;
275                 s/^\s*//;
276                 s/\s*$//;
277                 next unless /\S/;
278                 next unless /^\s*\d+/; # must be a real channel string.
279                 my $c = Dahdi::Chans->new($self, $index, $_);
280                 push(@channels, $c);
281                 $index++;
282         }
283         close F;
284         if($self->is_pri()) {
285                 # Check for PRI with unknown type strings
286                 if($index == 31) {
287                         if($self->{PROTO} eq 'UNKNOWN') {
288                                 $self->init_proto('E1');
289                         } elsif($self->{PROTO} ne 'E1')  {
290                                 die "$index channels in a $self->{PROTO} span";
291                         }
292                 } elsif($index == 24) {
293                         if($self->{PROTO} eq 'UNKNOWN') {
294                                 $self->init_proto('T1');        # FIXME: J1?
295                         } elsif($self->{PROTO} ne 'T1') {
296                                 die "$index channels in a $self->{PROTO} span";
297                         }
298                 }
299         }
300         @channels = sort { $a->num <=> $b->num } @channels;
301         $self->{CHANS} = \@channels;
302         $self->{YELLOW} = undef;
303         $self->{CRC4} = undef;
304         $self->{SOFTNTTE} = undef;
305         $self->{TERMINATION} = undef;
306         if($self->is_bri()) {
307                 $self->{CODING} = 'ami';
308                 $self->{DCHAN} = ($self->chans())[$self->{DCHAN_IDX}];
309                 $self->{BCHANS} = [ ($self->chans())[@{$self->{BCHAN_LIST}}] ];
310                 # Infer some info from channel name:
311                 my $first_chan = ($self->chans())[0] || die "$0: No channels in span #$num\n";
312                 my $chan_fqn = $first_chan->fqn();
313                 if($chan_fqn =~ m(ZTHFC.*/|ztqoz.*/|XPP_BRI_.*|B4/.*|WCBRI/.*)) {               # BRI
314                         if($chan_fqn =~ m(WCBRI/.*)) {          # make sure to set termination resistors on hybrid cards
315                                 $self->{TERMINATION} = 'term';
316                                 $self->{SOFTNTTE} = 'te';
317                         }
318                         $self->{FRAMING} = 'ccs';
319                         $self->{SWITCHTYPE} = 'euroisdn';
320                         $self->{SIGNALLING} = ($self->{TERMTYPE} eq 'NT') ? $DAHDI_BRI_NET : $DAHDI_BRI_CPE ;
321                 } elsif($chan_fqn =~ m(ztgsm.*/)) {                             # Junghanns's GSM cards. 
322                         $self->{FRAMING} = 'ccs';
323                         $self->{SIGNALLING} = 'gsm';
324                 }
325         }
326         if($self->is_pri()) {
327                 $self->{DCHAN} = ($self->chans())[$self->{DCHAN_IDX}];
328                 $self->{BCHANS} = [ ($self->chans())[@{$self->{BCHAN_LIST}}] ];
329                 if($self->{PROTO} eq 'E1') {
330                         $self->{CODING} = 'hdb3';
331                         $self->{FRAMING} = 'ccs';
332                         $self->{SWITCHTYPE} = 'euroisdn';
333                         $self->{CRC4} = 'crc4';
334                 } elsif($self->{PROTO} eq 'T1') {
335                         $self->{CODING} = 'b8zs';
336                         $self->{FRAMING} = 'esf';
337                         $self->{SWITCHTYPE} = 'national';
338                 } else {
339                         die "'$self->{PROTO}' unsupported yet";
340                 }
341         }
342         return $self;
343 }
344
345 sub bchans($) {
346         my $self = shift || die;
347
348         return @{$self->{BCHANS}};
349 }
350
351 sub set_termtype($$) {
352         my $span = shift || die;
353         my $termtype = shift || die;
354         $span->{TERMTYPE} = $termtype;
355         if ($span->is_pri) {
356                 $span->{SIGNALLING} = ($termtype eq 'NT') ? $DAHDI_PRI_NET : $DAHDI_PRI_CPE ;
357         } elsif ($span->is_bri) {
358                 $span->{SIGNALLING} = ($termtype eq 'NT') ? $DAHDI_BRI_NET : $DAHDI_BRI_CPE ;
359         }
360         $span->{TYPE} = $span->proto . "_$termtype";
361 }
362
363 sub pri_set_fromconfig($$) {
364         my $span = shift || die;
365         my $genconf = shift || die;
366         my $name = $span->name;
367         return unless $span->is_soft_term_type;
368 #       if(defined $termtype) {
369 #               die "Termtype for $name already defined as $termtype\n";
370 #       }
371         my $pri_termtype = $genconf->{pri_termtype};
372         my @pri_specs;
373         if(defined $pri_termtype) {
374                 @pri_specs = @{$pri_termtype};
375         }
376         push(@pri_specs , 'SPAN/* TE');         # Default
377         my @patlist = ( "SPAN/" . $span->num );
378         my $xpd = Dahdi::Xpp::xpd_of_span($span);
379         if(defined $xpd) {
380                 my $xbus = $xpd->xbus;
381                 my $xbus_name = $xbus->name;
382                 my $xpd_name = "XPD-" . $xpd->id;
383                 my $label = $xbus->label;
384                 my $connector = $xbus->connector;
385                 #print "DEBUG: '$xbus_name/$xpd_name' LABEL='$label' CONNECTOR='$connector'\n";
386                 push(@patlist, "NUM/$xbus_name/$xpd_name");
387                 push(@patlist, "LABEL/$label/$xpd_name");
388                 push(@patlist, "CONNECTOR/$connector/$xpd_name");
389         }
390         #print STDERR "PATLIST=@patlist\n";
391         my $match_termtype;
392 SPEC:
393         for(my $i = 0; $i < @pri_specs; $i++) {
394                 my $spec = $pri_specs[$i];
395                 #print STDERR "spec: $spec\n";
396                 my ($match, $termtype) = split(/\s+/, $spec);
397                 next unless defined $match and defined $termtype;
398                 # Convert "globs" to regex
399                 $match =~ s/\*/.*/g;
400                 $match =~ s/\?/./g;
401                 #print STDERR "match: $match\n";
402                 foreach my $pattern (@patlist) {
403                         #print STDERR "testmatch: $pattern =~ $match\n";
404                         if($pattern =~ /^$match$/) {
405                                 #print STDERR "MATCH '$pattern' ~ '$match' termtype=$termtype\n";
406                                 $match_termtype = $termtype;
407                                 last SPEC;
408                         }
409                 }
410         }
411         die "Unknown pri_termtype" unless defined $match_termtype;
412         $span->set_termtype($match_termtype);
413 }
414
415
416 1;