]> git.ipfire.org Git - people/stevee/network.git/blob - functions.modem
headers: Add missing function and rename existing ones.
[people/stevee/network.git] / functions.modem
1 #!/bin/bash
2 ###############################################################################
3 # #
4 # IPFire.org - A linux based firewall #
5 # Copyright (C) 2012 IPFire Network Development Team #
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 # Exit codes from the chat(8) command:
23 CHAT_OK=0
24 CHAT_INVALID=1
25 CHAT_ERROR=2
26 CHAT_TIMEOUT=3
27
28 function modem_chat() {
29 local answer="OK"
30 local device
31 local timeout=2
32 local quiet="false"
33
34 while [ $# -gt 0 ]; do
35 case "${1}" in
36 --timeout=*)
37 timeout=$(cli_get_val ${1})
38 ;;
39 --answer=*)
40 answer=$(cli_get_val ${1})
41 ;;
42 --quiet)
43 quiet="true"
44 ;;
45 *)
46 device=${1}
47 shift; break
48 ;;
49 esac
50 shift
51 done
52
53 assert serial_exists ${device}
54 assert serial_is_unlocked ${device}
55 assert isset answer
56 assert isset timeout
57
58 local command=$@
59
60 log DEBUG "Sending command to ${device}: ${command}"
61
62 (
63 # This cannot be run with -x.
64 set +x 2>/dev/null
65
66 chat -V -s -t ${timeout} "" "${command}" "${answer}" \
67 < ${device} > ${device} || exit $?
68 print # Print line feed.
69 ) 2>&1 | __modem_chat_process_output "${answer}" ${quiet}
70
71 local ret=${PIPESTATUS[0]}
72
73 # Return the exit code of the chat command.
74 case "${ret}" in
75 ${CHAT_OK})
76 return ${EXIT_OK}
77 ;;
78
79 # When the timeout condition hit, the expected string has not
80 # been received in the given amount of time.
81 ${CHAT_TIMEOUT}|${CHAT_ERROR})
82 return ${EXIT_ERROR}
83 ;;
84
85 ${CHAT_INVALID})
86 return ${EXIT_CONF_ERROR}
87 ;;
88 esac
89
90 log WARNING "Received unknown exit code from chat(8): ${ret}"
91 return ${EXIT_ERROR}
92 }
93
94 function __modem_chat_process_output() {
95 local answer=${1}
96 local quiet=${2}
97
98 if enabled quiet; then
99 # Just throw everything away.
100 cat >/dev/null
101 return ${EXIT_OK}
102 fi
103
104 local counter=0
105
106 local line
107 while read line; do
108 log DEBUG "Output[${counter}]: ${line}"
109
110 # Also skip empty lines.
111 [ -n "${line}" ] || continue
112
113 # Ignore all volatile messages.
114 [ "${line:0:1}" = "^" ] && continue
115
116 counter=$(( ${counter} + 1 ))
117
118 # Skip the first line, because that's out command.
119 [ ${counter} -eq 1 ] && continue
120
121 # Filter out the expected answer.
122 [ "${line}" = "${answer}" ] && continue
123
124 # Print the rest.
125 print "${line}"
126 done
127 }
128
129 # Exit codes of the sim_status function.
130 EXIT_SIM_READY=0
131 EXIT_SIM_PIN=1
132 EXIT_SIM_PUK=2
133 EXIT_SIM_UNKNOWN=3
134
135 function modem_sim_status() {
136 local device=${1}
137 assert isset device
138
139 local output
140 output=$(modem_chat ${device} "AT+CPIN?")
141 assert_check_retval $?
142
143 # Strip leading +CPIN: from the output.
144 output=${output#*: }
145
146 case "${output}" in
147 "READY")
148 log DEBUG "${device}'s SIM is unlocked or doesn't need a PIN."
149 return ${EXIT_SIM_READY}
150 ;;
151 "SIM PIN")
152 log DEBUG "${device}'s SIM is waiting for a PIN."
153 return ${EXIT_SIM_PIN}
154 ;;
155 "SIM PUK")
156 log DEBUG "${device}'s SIM is PUK locked."
157 return ${EXIT_SIM_PUK}
158 ;;
159 esac
160
161 log WARNING "${device}: Invalid output of the AT+CPIN? command."
162 return ${EXIT_SIM_UNKNOWN}
163 }
164
165 function modem_sim_unlocked() {
166 local device=${1}
167 assert isset device
168
169 modem_sim_status "${device}"
170 local ret=$?
171
172 [ ${ret} -eq ${EXIT_SIM_READY} ] && return ${EXIT_TRUE} || return ${EXIT_FALSE}
173 }
174
175 function modem_sim_locked() {
176 modem_sim_unlocked $@ && return ${EXIT_FALSE} || return ${EXIT_TRUE}
177 }
178
179 function modem_sim_unlock() {
180 local device=${1}
181 assert isset device
182
183 local pin=${2}
184 assert isset pin
185
186 local command="AT+CPIN=${pin}"
187
188 local new_pin=${3}
189 if isset new_pin; then
190 command="${command},${new_pin}"
191 fi
192
193 modem_chat --timeout=2 --quiet "${device}" "${command}"
194
195 local ret=$?
196 case "${ret}" in
197 ${EXIT_OK})
198 log INFO "Successfully unlocked SIM card on ${device}."
199 ;;
200 *)
201 log ERROR "Could not unlock SIM card on ${device}."
202 ret=${EXIT_ERROR}
203 ;;
204 esac
205
206 return ${ret}
207 }
208
209 # Returns the vendor of the modem.
210 # For example: "huawei"
211 function modem_get_manufacturer() {
212 local device=${1}
213 assert isset device
214
215 local output
216 output=$(modem_chat ${device} "AT+GMI")
217 assert_check_retval $?
218
219 [ "${output:0:1}" = "+" ] && output=${output#*: }
220 output=${output//\"/}
221
222 print "${output}"
223 }
224
225 function modem_get_model() {
226 local device=${1}
227 assert isset device
228
229 local output
230 output=$(modem_chat ${device} "AT+GMM")
231 assert_check_retval $?
232
233 [ "${output:0:1}" = "+" ] && output=${output#*: }
234 output=${output//\"/}
235
236 print "${output}"
237 }
238
239 function modem_get_software_version() {
240 local device=${1}
241 assert isset device
242
243 local output
244 output=$(modem_chat ${device} "AT+GMR")
245 assert_check_retval $?
246
247 [ "${output:0:1}" = "+" ] && output=${output#*: }
248 output=${output//\"/}
249
250 print "${output}"
251 }
252
253 function modem_get_sim_imsi() {
254 local device=${1}
255 assert isset device
256
257 # Make sure the SIM card is unlocked for this operation.
258 assert modem_sim_unlocked ${device}
259
260 modem_chat ${device} "AT+CIMI"
261 }
262
263 function modem_get_device_imei() {
264 local device=${1}
265 assert isset device
266
267 local output
268 output=$(modem_chat --timeout=1 ${device} "AT+CGSN") || assert_check_retval $?
269 local ret=$?
270
271 if [ ${ret} -eq ${EXIT_OK} ]; then
272 print "${output}"
273 fi
274
275 return ${ret}
276 }
277
278 function modem_is_mobile() {
279 local device=${1}
280 assert isset device
281
282 # Check if the device can return it's IMEI.
283 # If not, it's probably a serial 56k modem or something
284 # in that category.
285
286 modem_get_device_imei ${device} &>/dev/null
287 }
288
289 # Exit codes of the network registration function.
290 EXIT_REG_REGISTERED_TO_HOME_NETWORK=0
291 EXIT_REG_NOT_REGISTERED_NOT_SEARCHING=1
292 EXIT_REG_NOT_REGISTERED_SEARCHING=2
293 EXIT_REG_REGISTRATION_DENIED=3
294 EXIT_REG_REGISTERED_ROAMING=4
295 EXIT_REG_UNKNOWN=5
296
297 function modem_get_network_registration() {
298 local device=${1}
299 assert isset device
300
301 # Make sure the SIM card is unlocked for this operation.
302 assert modem_sim_unlocked ${device}
303
304 local output
305 output=$(modem_chat ${device} "AT+CREG?")
306 assert_check_retval $?
307
308 # Cut out unneeded parts of the message.
309 output=${output#*: }
310
311 case "${output}" in
312 [0-2],[0-5])
313 local status=${output%,*}
314
315 # The status variable must be zero.
316 [ ${status} -ne 0 ] && break
317
318 local stat=${output#*,}
319 case "${stat}" in
320 0)
321 return ${EXIT_REG_NOT_REGISTERED_NOT_SEARCHING}
322 ;;
323 1)
324 return ${EXIT_REG_REGISTERED_TO_HOME_NETWORK}
325 ;;
326 2)
327 return ${EXIT_REG_NOT_REGISTERED_SEARCHING}
328 ;;
329 3)
330 return ${EXIT_REG_REGISTRATION_DENIED}
331 ;;
332 5)
333 return ${EXIT_REG_REGISTERED_ROAMING}
334 ;;
335 *)
336 return ${EXIT_REG_UNKNOWN}
337 ;;
338 esac
339 ;;
340 esac
341
342 # Apparently the output of the CREG? command was not in
343 # the right format. The modem will be tried to be set to the
344 # right mode.
345
346 modem_set_network_registration ${device} 0
347 modem_get_network_registration ${device}
348 }
349
350 function modem_set_network_registration() {
351 local device=${1}
352 assert isset device
353
354 # Make sure the SIM card is unlocked for this operation.
355 assert modem_sim_unlocked ${device}
356
357 local mode=${2}
358 assert isset mode
359
360 modem_chat ${device} "AT+CREG=${mode}"
361 }
362
363 function modem_scan_networks() {
364 local device=${1}
365 assert isset device
366
367 # Make sure the SIM card is unlocked for this operation.
368 assert modem_sim_unlocked ${device}
369
370 local output
371 output=$(modem_chat --timeout=60 ${device} "AT+COPS=?")
372 assert_check_retval $?
373
374 output=${output#*: }
375
376 # XXX the output is not very nice to parse.
377 }
378
379 function __modem_get_network_operator() {
380 local device=${1}
381 assert isset device
382
383 # Make sure the SIM card is unlocked for this operation.
384 assert modem_sim_unlocked ${device}
385
386 local argument=${2}
387 assert isset argument
388
389 local output
390 output=$(modem_chat ${device} "AT+COPS?")
391 assert_check_retval $?
392
393 output=${output#*: }
394
395 local mode format operator act
396 read mode format operator act <<< "${output//,/ }"
397
398 # Remove all " from the operator string.
399 operator=${operator//\"/}
400
401 print "${!argument}"
402 return ${EXIT_OK}
403 }
404
405 function modem_get_network_operator() {
406 local device=${1}
407 assert isset device
408
409 __modem_get_network_operator ${device} operator
410 }
411
412 # Exit codes of the network operator mode function.
413 EXIT_OPMODE_GSM=0
414 EXIT_OPMODE_COMPACTGSM=1
415 EXIT_OPMODE_GSM_WITH_EGPRS=2
416 EXIT_OPMODE_UMTS=3
417 EXIT_OPMODE_UMTS_WITH_HSDPA=4
418 EXIT_OPMODE_UMTS_WITH_HSUPA=5
419 EXIT_OPMODE_UMTS_WITH_HSDPA_AND_HSUPA=6
420 EXIT_OPMODE_UNKNOWN=7
421
422 function modem_get_network_mode() {
423 local device=${1}
424 assert isset device
425
426 local output
427 output=$(__modem_get_network_operator ${device} act)
428 assert_check_retval $?
429
430 case "${output}" in
431 0)
432 print "GSM"
433 return ${EXIT_OPMODE_GSM}
434 ;;
435 1)
436 print "Compact GSM"
437 return ${EXIT_OPMODE_COMPACTGSM}
438 ;;
439 2)
440 print "UMTS"
441 return ${EXIT_OPMODE_UMTS}
442 ;;
443 3)
444 print "UMTS with HSDPA"
445 return ${EXIT_OPMODE_UMTS_WITH_HSDPA}
446 ;;
447 4)
448 print "UMTS with HSUPA"
449 return ${EXIT_OPMODE_UMTS_WITH_HSUPA}
450 ;;
451 5)
452 print "UMTS with HSDPA and HSUPA"
453 return ${EXIT_OPMODE_UMTS_WITH_HSDPA_AND_HSUPA}
454 ;;
455 *)
456 print "Unknown"
457 return ${EXIT_OPMODE_UNKNOWN}
458 ;;
459 esac
460 }
461
462 function __modem_get_signal_quality() {
463 local device=${1}
464 assert isset device
465
466 # Make sure the SIM card is unlocked for this operation.
467 assert modem_sim_unlocked ${device}
468
469 local argument=${2}
470 assert isset argument
471
472 local output
473 output=$(modem_chat ${device} "AT+CSQ")
474 assert_check_retval $?
475
476 output=${output#*: }
477
478 case "${output}" in
479 *,*)
480 local rssi=${output%,*}
481 local ber=${output#*,}
482
483 print "${!argument}"
484 return ${EXIT_OK}
485 ;;
486 *)
487 log ERROR "Unknown format for AT+CSQ: ${device}: ${output}"
488 ;;
489 esac
490
491 return ${EXIT_ERROR}
492 }
493
494 function modem_get_signal_quality() {
495 local device=${1}
496 assert isset device
497
498 local rssi
499 rssi=$(__modem_get_signal_quality ${device} rssi)
500 assert_check_retval $?
501
502 isset rssi || return ${EXIT_ERROR}
503
504 # 99 indicates an unknown signal strength.
505 [ ${rssi} -eq 99 ] && return ${EXIT_UNKNOWN}
506
507 local dbm=$(( ${rssi} * 2 ))
508 dbm=$(( ${dbm} - 113 ))
509
510 print "%d" "${dbm}"
511 return ${EXIT_OK}
512 }
513
514 function modem_get_bit_error_rate() {
515 local device=${1}
516 assert isset device
517
518 local ber
519 ber=$(__modem_get_signal_quality ${device} ber)
520 assert_check_retval $?
521
522 isset ber || return ${EXIT_ERROR}
523
524 # 99 indicates that the bit error rate could not be detected or
525 # is unknown for some other reason.
526 [ ${ber} -eq 99 ] && return ${EXIT_UNKNOWN}
527
528 print "%d" "${ber}"
529 return ${EXIT_OK}
530 }