xpp: sysfs access cleanups
[dahdi/tools.git] / xpp / perl_modules / Dahdi / Xpp.pm
1 package Dahdi::Xpp;
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::Hardware;
12 use Dahdi::Xpp::Xbus;
13
14 =head1 NAME
15
16 Dahdi::Xpp - Perl interface to the Xorcom Astribank drivers.
17
18 =head1 SYNOPSIS
19
20   # Listing all Astribanks:
21   use Dahdi::Xpp;
22   # scans hardware:
23   my @xbuses = Dahdi::Xpp::xbuses("SORT_CONNECTOR");
24   for my $xbus (@xbuses) {
25     print $xbus->name." (".$xbus->label .", ". $xbus->connector .")\n";
26     for my $xpd ($xbus->xpds) {
27       print " - ".$xpd->fqn,"\n";
28     }
29   }
30 =cut
31
32 #
33 # A global handle for all xbuses
34 #
35 my @xbuses;
36
37 our $sysfs_astribanks;
38 our $sysfs_xpds;
39 our $sysfs_ab_driver;
40
41 BEGIN {
42         my $virt_base = $Dahdi::virt_base;
43         $sysfs_astribanks = "$virt_base/sys/bus/astribanks/devices";
44         $sysfs_xpds = "$virt_base/sys/bus/xpds/devices";
45         $sysfs_ab_driver = "$virt_base/sys/bus/astribanks/drivers/xppdrv";
46 }
47
48 sub scan($) {
49         my $pack = shift || die;
50
51         opendir(D, $sysfs_astribanks) || return();
52         while(my $entry = readdir D) {
53                 next if $entry eq '.' or $entry eq '..';
54                 my $xbus = Dahdi::Xpp::Xbus->new($sysfs_astribanks, $entry);
55                 push(@xbuses, $xbus);
56         }
57         closedir D;
58         return @xbuses;
59 }
60
61 # Nominal sorters for xbuses
62 sub by_name {
63         return $a->name cmp $b->name;
64 }
65
66 sub by_connector {
67         return $a->connector cmp $b->connector;
68 }
69
70 sub by_label {
71         my $cmp = $a->label cmp $b->label;
72         return $cmp if $cmp != 0;
73         return $a->connector cmp $b->connector;
74 }
75
76 sub score_type {
77         my $score;
78
79         return 1 if grep(/\b[ETJ]1/, @_);
80         return 2 if grep(/\bBRI/, @_);
81         return 3 if grep(/\bFXO/, @_);
82         return 4;       # FXS
83 }
84
85 sub by_type {
86         my @a_types = map { $_->type } $a->xpds();
87         my @b_types = map { $_->type } $b->xpds();
88         my $res;
89
90         my $a_score = score_type(@a_types);
91         my $b_score = score_type(@b_types);
92         #printf STDERR "DEBUG-a: %s %s %s\n", $a->name, $a_score, join(',',@a_types);
93         #printf STDERR "DEBUG-b: %s %s %s\n", $b->name, $b_score, join(',',@b_types);
94         $res = $a_score <=> $b_score;
95         $res = $a->connector cmp $b->connector if $res == 0;
96         return $res;
97 }
98
99 sub by_xpporder {
100         my $cmp = $a->xpporder <=> $b->xpporder;
101         return $cmp if $cmp != 0;
102         return $a->connector cmp $b->connector;
103 }
104
105 =head1 xbuses([sort_order])
106
107 Scans system (via /sys) and returns a list of Astribank (Xbus)
108 objects. The optional parameter sort_order is the order in which
109 the Astribanks will be returns:
110
111
112 =head1 sorters([sort_order])
113
114 With no parameters, returns the names of built in sorters.
115 With a single parameter, returns a reference to the requested built in sorter.
116 Also, for convenience, a reference to a custom sorter function may be passed
117 and returned as is.
118
119 The built in sorters are:
120
121 =over
122
123 =item SORT_XPPORDER
124
125 Sort by ordering defined in F</etc/dahdi/xpp_order> file.
126 Astribanks can be listed in this file by their label or by
127 their connector string (prefixed with <@>).
128
129 Astribanks not listed in the F<xpp_order> file are sorted
130 via ordering number 999 -- So they come after the Astribanks
131 that are listed.
132
133 Astribanks with same ordering number (e.g: 999) are sorted
134 by their connector string (to preserve legacy behaviour).
135
136 =item SORT_CONNECTOR
137
138 Sort by the connector string. For USB this defines the "path" to get to
139 the device through controllers, hubs etc.
140
141 =item SORT_LABEL
142
143 Sorts by the label of the Astribank. The label field is unique to the
144 Astribank. It can also be viewed through 'lsusb -v' without the drivers
145 loaded (the iSerial field in the Device Descriptor). This is normally
146 relieble, but some older Astribanks have an empty label.
147
148 =item SORT_NAME
149
150 Sort by the "name". e.g: "XBUS-00". The order of Astribank names depends
151 on the load order, and hence may change between different runs.
152
153 =item SORT_TYPE
154
155 Sort by XPD types. First Astribanks with E1/T1/J1 XPDs, then with BRI,
156 then with FXO, then ones with only FXS ports. Within each type they
157 are sorted by the connector field (as in SORT_CONNECTOR above).
158
159 =item custom function
160
161 Instead of using a predefined sorter, you can pass your own sorting
162 function. See the example sorters in the code of this module.
163
164 =back
165
166 =cut
167
168 sub sorters {
169         my %sorter_table = (
170                 SORT_CONNECTOR  => \&by_connector,
171                 SORT_NAME       => \&by_name,
172                 SORT_LABEL      => \&by_label,
173                 SORT_TYPE       => \&by_type,
174                 SORT_XPPORDER   => \&by_xpporder,
175                 # Aliases
176                 connector       => \&by_connector,
177                 name            => \&by_name,
178                 label           => \&by_label,
179                 type            => \&by_type,
180                 xpporder        => \&by_xpporder,
181         );
182         my $which_sorter = shift || return sort keys %sorter_table;
183         return $which_sorter if ref($which_sorter) eq 'CODE';
184         return $sorter_table{$which_sorter};
185 }
186
187 sub add_xpporder(@) {
188         my @xbuses = @_;
189         my $cfg = $ENV{XPPORDER_CONF} || '/etc/dahdi/xpp_order';
190         my %order;
191
192         # Set defaults
193         foreach my $xbus (@xbuses) {
194                 $xbus->{XPPORDER} = 99;
195         }
196         # Read from optional config file
197         if(!open(F, $cfg)) {
198                 warn "$0: Failed opening '$cfg': $!"
199                         unless $! == 2;         # ENOENT
200                 return;
201         }
202         my $count = 1;
203         while(<F>) {
204                 chomp;
205                 s/#.*//;
206                 s/^\s*//;
207                 s/\s*$//;
208                 next unless /\S/;
209                 $order{$_} = $count++;
210         }
211         close F;
212         # Overrides from config file
213         foreach my $xbus (@xbuses) {
214                 my $label = $xbus->label;
215                 my $val;
216                 $val = $order{$label};
217                 $val = $order{$xbus->connector} unless defined $val;
218                 $xbus->{XPPORDER} = $val if defined $val;
219         }
220 }
221
222 sub xbuses {
223         my $optsort = shift || 'SORT_XPPORDER';
224         my @sorted_xbuses;
225
226         if(! @xbuses) {
227                 @xbuses = Dahdi::Xpp->scan();
228         }
229         add_xpporder(@xbuses);
230         my $sorter = sorters($optsort);
231         die "Unknown optional sorter '$optsort'" unless defined $sorter;
232         @sorted_xbuses = sort $sorter @xbuses;
233         return @sorted_xbuses;
234 }
235
236 sub xpd_of_span($) {
237         my $span = shift or die "Missing span parameter";
238         return undef unless defined $span;
239         foreach my $xbus (Dahdi::Xpp::xbuses) {
240                 foreach my $xpd ($xbus->xpds()) {
241                         return $xpd if $xpd->fqn eq $span->name;
242                 }
243         }
244         return undef;
245 }
246
247 =head1 sync([new_sync_source])
248
249 Gets (and optionally sets) the internal Astribanks synchronization
250 source. When used to set sync source, returns the original sync source.
251
252 A synchronization source is a value valid writing into
253   /sys/bus/astribanks/drivers/xppdrv/sync
254 For more information read that file and see README.Astribank .
255
256 =cut
257
258 sub sync {
259         my ($newsync) = @_;
260         my $result;
261         my $file = "$sysfs_ab_driver/sync";
262         die "Missing '$file'\n" unless -f $file;
263         open(F, "$file") or die "Failed to open $file for reading: $!";
264         $result = <F>;
265         close F;
266         chomp $result;
267         $result =~ s/^SYNC=\D*//;
268         if(defined $newsync) {          # Now change
269                 $newsync =~ s/.*/\U$&/;
270                 if($newsync =~ /^(\d+)$/) {
271                         $newsync = "SYNC=$1";
272                 } elsif($newsync ne 'DAHDI') {
273                         die "Bad sync parameter '$newsync'";
274                 }
275                 open(F, ">$file") or die "Failed to open $file for writing: $!";
276                 print F $newsync;
277                 close(F) or die "Failed in closing $file: $!";
278         }
279         return $result;
280 }
281
282 =head1 SEE ALSO
283
284 =over
285
286 =item L<Dahdi::Xpp::Xbus>
287
288 Xbus (Astribank) object.
289
290 =item L<Dahdi::Xpp::Xpd>
291
292 XPD (the rough equivalent of a Dahdi span) object.
293
294 =item L<Dahdi::Xpp::Line>
295
296 Object for a line: an analog port or a time-slot in a adapter.
297 Equivalent of a channel in Dahdi.
298
299 =item L<Dahdi>
300
301 General documentation in the master package.
302
303 =back
304
305 =cut
306
307 1;