]> git.ipfire.org Git - people/teissler/ipfire-2.x.git/blob - config/cfgroot/modem-lib.pl
Merge remote-tracking branch 'ms/modem-status' into next
[people/teissler/ipfire-2.x.git] / config / cfgroot / modem-lib.pl
1 #!/usr/bin/perl
2 ###############################################################################
3 # #
4 # IPFire.org - A linux based firewall #
5 # Copyright (C) 2014 IPFire Team <info@ipfire.org> #
6 # #
7 # This program is free software: you can redistribute it and/or modify #
8 # it under the terms of the GNU General Public License as published by #
9 # the Free Software Foundation, either version 3 of the License, or #
10 # (at your option) any later version. #
11 # #
12 # This program is distributed in the hope that it will be useful, #
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15 # GNU General Public License for more details. #
16 # #
17 # You should have received a copy of the GNU General Public License #
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
19 # #
20 ###############################################################################
21
22 use Device::Modem;
23
24 package Modem;
25
26 sub new() {
27 my $class = shift;
28
29 my $port = shift;
30 my $baud = shift;
31
32 my $self = {};
33 bless $self, $class;
34
35 # Initialize the connetion to the modem.
36 $self->_initialize($port, $baud);
37
38 if ($self->_is_working()) {
39 return $self;
40 }
41
42 return undef;
43 }
44
45 sub DESTROY() {
46 my $self = shift;
47
48 # Close connection to modem.
49 if ($self->{modem}) {
50 $self->{modem}->close();
51 }
52 }
53
54 sub _initialize() {
55 my ($self, $port, $baud) = @_;
56
57 # Establish connection to the modem.
58 $self->{modem} = new Device::Modem(port => $port);
59 $self->{modem}->connect(baudrate => $baud);
60 }
61
62 sub _is_working() {
63 my $self = shift;
64
65 # Check if the modem responds to AT commands.
66 $self->{modem}->atsend("AT\r\n");
67
68 my $response = $self->{modem}->answer();
69 return ($response eq "OK");
70 }
71
72 sub _command() {
73 my $self = shift;
74 my $cmd = shift;
75
76 # Terminate the AT command with newline.
77 $cmd .= "\r\n";
78
79 $self->{modem}->atsend($cmd);
80
81 my $response = $self->{modem}->answer();
82 my @response = split(/\n/, $response);
83
84 # Trim leading and trailing spaces.
85 foreach my $line (@response) {
86 $line =~ s/^\s+|\s+$//g;
87 chomp($line);
88 }
89
90 my $last_element = pop(@response);
91 unless ($last_element eq "OK") {
92 push(@response, $last_element);
93 }
94
95 $response = join("\n", @response);
96
97 return $self->_trim($response);
98 }
99
100 sub _trim() {
101 my $self = shift;
102 my $input = shift;
103
104 my $first_char = substr($input, 0, 1);
105 if ($first_char eq "+") {
106 my @output = split(/:/, $input);
107 if ($#output == 1) {
108 return $output[1];
109 }
110 }
111
112 return $input;
113 }
114
115 sub get_vendor() {
116 my $self = shift;
117
118 return $self->_command("AT+GMI");
119 }
120
121 sub get_model() {
122 my $self = shift;
123
124 return $self->_command("AT+GMM");
125 }
126
127 sub get_software_version() {
128 my $self = shift;
129
130 return $self->_command("AT+GMR");
131 }
132
133 sub get_imei() {
134 my $self = shift;
135
136 return $self->_command("AT+GSN");
137 }
138
139 sub get_capabilities() {
140 my $self = shift;
141
142 my $output = $self->_command("AT+GCAP");
143 return split(/,/, $output);
144 }
145
146 sub is_sim_unlocked() {
147 my $self = shift;
148
149 # TODO
150 return 1;
151 }
152
153 sub get_sim_imsi() {
154 my $self = shift;
155
156 if ($self->is_sim_unlocked()) {
157 return $self->_command("AT+CIMI");
158 }
159 }
160
161 sub get_network_registration() {
162 my $self = shift;
163
164 my @elements;
165 foreach my $i ([0, 1]) {
166 my $output = $self->_command("AT+CREG?");
167
168 @elements = split(/,/, $output);
169 if ($#elements != 2) {
170 # Output in wrong format. Resetting.
171 $self->_command("AT+CREG=0");
172 }
173 }
174
175 if ($elements[0] == 0) {
176 if ($elements[1] == 0) {
177 return "NOT REGISTERED, NOT SEARCHING";
178 } elsif ($elements[1] == 1) {
179 return "REGISTERED TO HOME NETWORK";
180 } elsif ($elements[1] == 2) {
181 return "NOT REGISTERED, SEARCHING";
182 } elsif ($elements[1] == 3) {
183 return "REGISTRATION DENIED";
184 } elsif ($elements[1] == 5) {
185 return "REGISTERED, ROAMING";
186 } else {
187 return "UNKNOWN";
188 }
189 }
190 }
191
192 sub _get_network_operator() {
193 my $self = shift;
194
195 my $output = $self->_command("AT+COPS?");
196 $output =~ s/\"//g;
197
198 my @elements = split(/,/, $output);
199 if ($#elements == 3) {
200 return @elements;
201 }
202 }
203
204 sub get_network_operator() {
205 my $self = shift;
206
207 my ($mode, $format, $operator, $act) = $self->_get_network_operator();
208
209 return $operator;
210 }
211
212 sub get_network_mode() {
213 my $self = shift;
214
215 my ($mode, $format, $operator, $act) = $self->_get_network_operator();
216
217 if ($act == 0) {
218 return "GSM";
219 } elsif ($act == 1) {
220 return "Compact GSM";
221 } elsif ($act == 2) {
222 return "UMTS";
223 } elsif ($act == 3) {
224 return "GSM WITH EGPRS";
225 } elsif ($act == 4) {
226 return "UMTS WITH HSDPA";
227 } elsif ($act == 5) {
228 return "UMTS WITH HSUPA";
229 } elsif ($act == 6) {
230 return "UMTS WITH HSDPA+HSUPA";
231 } elsif ($act == 7) {
232 return "LTE";
233 } else {
234 return "UNKNOWN ($act)";
235 }
236 }
237
238 sub _get_signal_quality() {
239 my $self = shift;
240
241 my $output = $self->_command("AT+CSQ");
242
243 my @elements = split(/,/, $output);
244 if ($#elements == 1) {
245 return @elements;
246 }
247 }
248
249 sub get_signal_quality() {
250 my $self = shift;
251
252 my ($rssi, $ber) = $self->_get_signal_quality();
253
254 # 99 equals unknown.
255 unless ($rssi == 99) {
256 my $dbm = ($rssi * 2) - 113;
257 return $dbm;
258 }
259
260 return undef;
261 }
262
263 sub get_bit_error_rate() {
264 my $self = shift;
265
266 my ($rssi, $ber) = $self->_get_signal_quality();
267
268 # 99 indicates unknown.
269 unless ($ber == 99) {
270 return $ber;
271 }
272
273 return undef;
274 }
275
276 1;