summaryrefslogtreecommitdiff
path: root/xpp/perl_modules/Dahdi/Xpp.pm
blob: 8280b5edc3853fcddab6b8a214bd064cc8a1cf30 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
package Dahdi::Xpp;
#
# Written by Oron Peled <oron@actcom.co.il>
# Copyright (C) 2007, Xorcom
# This program is free software; you can redistribute and/or
# modify it under the same terms as Perl itself.
#
# $Id$
#
use strict;
use Dahdi::Hardware;
use Dahdi::Xpp::Xbus;

=head1 NAME

Dahdi::Xpp - Perl interface to the Xorcom Astribank drivers.

=head1 SYNOPSIS

  # Listing all Astribanks:
  use Dahdi::Xpp;
  # scans hardware:
  my @xbuses = Dahdi::Xpp::xbuses("SORT_CONNECTOR");
  for my $xbus (@xbuses) {
    print $xbus->name." (".$xbus->label .", ". $xbus->connector .")\n";
    for my $xpd ($xbus->xpds) {
      print " - ".$xpd->fqn,"\n";
    }
  }
=cut

#
# A global handle for all xbuses
#
my @xbuses;

our $sysfs_astribanks;
our $sysfs_xpds;
our $sysfs_ab_driver;

BEGIN {
	my $virt_base = $Dahdi::virt_base;
	$sysfs_astribanks = "$virt_base/sys/bus/astribanks/devices";
	$sysfs_xpds = "$virt_base/sys/bus/xpds/devices";
	$sysfs_ab_driver = "$virt_base/sys/bus/astribanks/drivers/xppdrv";
}

sub scan($) {
	my $pack = shift || die;

	opendir(D, $sysfs_astribanks) || return();
	while(my $entry = readdir D) {
		next unless $entry =~ /xbus-(\d+)/;
		my $xbus = Dahdi::Xpp::Xbus->new($1);
		push(@xbuses, $xbus);
	}
	closedir D;
	return @xbuses;
}

# Nominal sorters for xbuses
sub by_name {
	return $a->name cmp $b->name;
}

sub by_connector {
	return $a->connector cmp $b->connector;
}

sub by_label {
	my $cmp = $a->label cmp $b->label;
	return $cmp if $cmp != 0;
	return $a->connector cmp $b->connector;
}

sub score_type {
	my $score;

	return 1 if grep(/\b[ETJ]1/, @_);
	return 2 if grep(/\bBRI/, @_);
	return 3 if grep(/\bFXO/, @_);
	return 4;	# FXS
}

sub by_type {
	my @a_types = map { $_->type } $a->xpds();
	my @b_types = map { $_->type } $b->xpds();
	my $res;

	my $a_score = score_type(@a_types);
	my $b_score = score_type(@b_types);
	#printf STDERR "DEBUG-a: %s %s %s\n", $a->name, $a_score, join(',',@a_types);
	#printf STDERR "DEBUG-b: %s %s %s\n", $b->name, $b_score, join(',',@b_types);
	$res = $a_score <=> $b_score;
	$res = $a->connector cmp $b->connector if $res == 0;
	return $res;
}

sub by_xpporder {
	my $cmp = $a->xpporder <=> $b->xpporder;
	return $cmp if $cmp != 0;
	return $a->connector cmp $b->connector;
}

=head1 xbuses([sort_order])

Scans system (/proc and /sys) and returns a list of Astribank (Xbus) 
objects. The optional parameter sort_order is the order in which 
the Astribanks will be returns:


=head1 sorters([sort_order])

With no parameters, returns the names of built in sorters.
With a single parameter, returns a reference to the requested built in sorter.
Also, for convenience, a reference to a custom sorter function may be passed
and returned as is.

The built in sorters are:

=over

=item SORT_XPPORDER

Sort by ordering defined in F</etc/dahdi/xpp_order> file.
Astribanks can be listed in this file by their label or by
their connector string (prefixed with <@>).

Astribanks not listed in the F<xpp_order> file are sorted
via ordering number 999 -- So they come after the Astribanks
that are listed.

Astribanks with same ordering number (e.g: 999) are sorted
by their connector string (to preserve legacy behaviour).

=item SORT_CONNECTOR

Sort by the connector string. For USB this defines the "path" to get to
the device through controllers, hubs etc.

=item SORT_LABEL

Sorts by the label of the Astribank. The label field is unique to the
Astribank. It can also be viewed through 'lsusb -v' without the drivers
loaded (the iSerial field in the Device Descriptor). This is normally
relieble, but some older Astribanks have an empty label.

=item SORT_NAME

Sort by the "name". e.g: "XBUS-00". The order of Astribank names depends
on the load order, and hence may change between different runs.

=item SORT_TYPE

Sort by XPD types. First Astribanks with E1/T1/J1 XPDs, then with BRI,
then with FXO, then ones with only FXS ports. Within each type they
are sorted by the connector field (as in SORT_CONNECTOR above).

=item custom function

Instead of using a predefined sorter, you can pass your own sorting
function. See the example sorters in the code of this module.

=back

=cut

sub sorters {
	my %sorter_table = (
		SORT_CONNECTOR	=> \&by_connector,
		SORT_NAME	=> \&by_name,
		SORT_LABEL	=> \&by_label,
		SORT_TYPE	=> \&by_type,
		SORT_XPPORDER	=> \&by_xpporder,
		# Aliases
		connector	=> \&by_connector,
		name		=> \&by_name,
		label		=> \&by_label,
		type		=> \&by_type,
		xpporder	=> \&by_xpporder,
	);
	my $which_sorter = shift || return sort keys %sorter_table;
	return $which_sorter if ref($which_sorter) eq 'CODE';
	return $sorter_table{$which_sorter};
}

sub add_xpporder(@) {
	my @xbuses = @_;
	my $cfg = $ENV{XPPORDER_CONF} || '/etc/dahdi/xpp_order';
	my %order;

	# Set defaults
	foreach my $xbus (@xbuses) {
		$xbus->{XPPORDER} = 99;
	}
	# Read from optional config file
	if(!open(F, $cfg)) {
		warn "$0: Failed opening '$cfg': $!"
			unless $! == 2;		# ENOENT
		return;
	}
	my $count = 1;
	while(<F>) {
		chomp;
		s/#.*//;
		s/^\s*//;
		s/\s*$//;
		next unless /\S/;
		$order{$_} = $count++;
	}
	close F;
	# Overrides from config file
	foreach my $xbus (@xbuses) {
		my $label = $xbus->label;
		my $val;
		$val = $order{$label};
		$val = $order{$xbus->connector} unless defined $val;
		$xbus->{XPPORDER} = $val if defined $val;
	}
}

sub xbuses {
	my $optsort = shift || 'SORT_XPPORDER';
	my @sorted_xbuses;

	if(! @xbuses) {
		@xbuses = Dahdi::Xpp->scan();
	}
	add_xpporder(@xbuses);
	my $sorter = sorters($optsort);
	die "Unknown optional sorter '$optsort'" unless defined $sorter;
	@sorted_xbuses = sort $sorter @xbuses;
	return @sorted_xbuses;
}

sub xpd_of_span($) {
	my $span = shift or die "Missing span parameter";
	return undef unless defined $span;
	foreach my $xbus (Dahdi::Xpp::xbuses) {
		foreach my $xpd ($xbus->xpds()) {
			return $xpd if $xpd->fqn eq $span->name;
		}
	}
	return undef;
}

=head1 sync([new_sync_source])

Gets (and optionally sets) the internal Astribanks synchronization
source. When used to set sync source, returns the original sync source.

A synchronization source is a value valid writing into /proc/xpp/sync .
For more information read that file and see README.Astribank .

=cut

sub sync_via_proc {
	my $newsync = shift;
	my $result;
	my $newapi = 0;

	my $proc_base = $Dahdi::proc_xpp_base;
	my $file = "$proc_base/sync";
	return '' unless -f $file;
	# First query
	open(F, "$file") or die "Failed to open $file for reading: $!";
	while(<F>) {
		chomp;
		/SYNC=/ and $newapi = 1;
		s/#.*//;
		if(/\S/) {	# First non-comment line
			s/^SYNC=\D*// if $newapi;
			$result = $_;
			last;
		}
	}
	close F;
	if(defined($newsync)) {		# Now change
		$newsync =~ s/.*/\U$&/;
		if($newsync =~ /^(\d+)$/) {
			$newsync = ($newapi)? "SYNC=$1" : "$1 0";
		} elsif($newsync ne 'DAHDI') {
			die "Bad sync parameter '$newsync'";
		}
		open(F, ">$file") or die "Failed to open $file for writing: $!";
		print F $newsync;
		close(F) or die "Failed in closing $file: $!";
	}
	return $result;
}

sub sync {
	my ($newsync) = @_;
	my $result;
	my $file = "$sysfs_ab_driver/sync";
	if(! -f $file) {	# Old /proc interface
		return sync_via_proc(@_);
	}
	open(F, "$file") or die "Failed to open $file for reading: $!";
	$result = <F>;
	close F;
	chomp $result;
	$result =~ s/^SYNC=\D*//;
	if(defined $newsync) {		# Now change
		$newsync =~ s/.*/\U$&/;
		if($newsync =~ /^(\d+)$/) {
			$newsync = "SYNC=$1";
		} elsif($newsync ne 'DAHDI') {
			die "Bad sync parameter '$newsync'";
		}
		open(F, ">$file") or die "Failed to open $file for writing: $!";
		print F $newsync;
		close(F) or die "Failed in closing $file: $!";
	}
	return $result;
}

=head1 SEE ALSO

=over

=item L<Dahdi::Xpp::Xbus>

Xbus (Astribank) object.

=item L<Dahdi::Xpp::Xpd>

XPD (the rough equivalent of a Dahdi span) object.

=item L<Dahdi::Xpp::Line>

Object for a line: an analog port or a time-slot in a adapter. 
Equivalent of a channel in Dahdi.

=item L<Dahdi>

General documentation in the master package.

=back

=cut

1;