]>
Commit | Line | Data |
---|---|---|
6c74a64c MT |
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 | ||
1c6a4e30 | 28 | modem_chat() { |
6c74a64c MT |
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} | |
6c74a64c MT |
54 | assert isset answer |
55 | assert isset timeout | |
56 | ||
57 | local command=$@ | |
58 | ||
59 | log DEBUG "Sending command to ${device}: ${command}" | |
60 | ||
61 | ( | |
62 | # This cannot be run with -x. | |
63 | set +x 2>/dev/null | |
64 | ||
65 | chat -V -s -t ${timeout} "" "${command}" "${answer}" \ | |
66 | < ${device} > ${device} || exit $? | |
67 | print # Print line feed. | |
68 | ) 2>&1 | __modem_chat_process_output "${answer}" ${quiet} | |
69 | ||
70 | local ret=${PIPESTATUS[0]} | |
71 | ||
72 | # Return the exit code of the chat command. | |
73 | case "${ret}" in | |
74 | ${CHAT_OK}) | |
75 | return ${EXIT_OK} | |
76 | ;; | |
77 | ||
78 | # When the timeout condition hit, the expected string has not | |
79 | # been received in the given amount of time. | |
80 | ${CHAT_TIMEOUT}|${CHAT_ERROR}) | |
81 | return ${EXIT_ERROR} | |
82 | ;; | |
83 | ||
84 | ${CHAT_INVALID}) | |
85 | return ${EXIT_CONF_ERROR} | |
86 | ;; | |
87 | esac | |
88 | ||
89 | log WARNING "Received unknown exit code from chat(8): ${ret}" | |
90 | return ${EXIT_ERROR} | |
91 | } | |
92 | ||
1c6a4e30 | 93 | __modem_chat_process_output() { |
6c74a64c MT |
94 | local answer=${1} |
95 | local quiet=${2} | |
96 | ||
97 | if enabled quiet; then | |
98 | # Just throw everything away. | |
99 | cat >/dev/null | |
100 | return ${EXIT_OK} | |
101 | fi | |
102 | ||
103 | local counter=0 | |
104 | ||
105 | local line | |
daa5599a | 106 | while read -r line; do |
6c74a64c MT |
107 | # Also skip empty lines. |
108 | [ -n "${line}" ] || continue | |
109 | ||
110 | # Ignore all volatile messages. | |
111 | [ "${line:0:1}" = "^" ] && continue | |
112 | ||
daa5599a | 113 | log DEBUG "Output[${counter}]: ${line}" |
6c74a64c MT |
114 | counter=$(( ${counter} + 1 )) |
115 | ||
116 | # Skip the first line, because that's out command. | |
117 | [ ${counter} -eq 1 ] && continue | |
118 | ||
119 | # Filter out the expected answer. | |
120 | [ "${line}" = "${answer}" ] && continue | |
121 | ||
122 | # Print the rest. | |
123 | print "${line}" | |
124 | done | |
125 | } | |
126 | ||
1c6a4e30 | 127 | modem_initialize() { |
95416c1e MT |
128 | local device="${1}" |
129 | assert isset device | |
3255c894 MT |
130 | shift |
131 | ||
132 | # Sleep for $sleep seconds, to give | |
133 | # the modem a moment to initialize itself. | |
134 | local sleep=1 | |
135 | ||
136 | while [ $# -gt 0 ]; do | |
137 | case "${1}" in | |
138 | --sleep=*) | |
139 | sleep="$(cli_get_val "${1}")" | |
140 | ;; | |
141 | *) | |
142 | warning "Unrecognized argument: ${1}" | |
143 | ;; | |
144 | esac | |
145 | shift | |
146 | done | |
147 | assert isinteger sleep | |
95416c1e MT |
148 | |
149 | log INFO "Initializing modem ${device}" | |
150 | ||
151 | # Reset the modem. | |
152 | modem_chat "${device}" "${AT_INITIALIZE}" | |
3255c894 MT |
153 | |
154 | # Wait... | |
155 | if [ ${sleep} -gt 0 ]; then | |
156 | sleep ${sleep} | |
157 | fi | |
95416c1e MT |
158 | } |
159 | ||
6c74a64c MT |
160 | # Exit codes of the sim_status function. |
161 | EXIT_SIM_READY=0 | |
162 | EXIT_SIM_PIN=1 | |
163 | EXIT_SIM_PUK=2 | |
164 | EXIT_SIM_UNKNOWN=3 | |
165 | ||
1c6a4e30 | 166 | modem_sim_status() { |
6c74a64c MT |
167 | local device=${1} |
168 | assert isset device | |
169 | ||
170 | local output | |
171 | output=$(modem_chat ${device} "AT+CPIN?") | |
172 | assert_check_retval $? | |
173 | ||
174 | # Strip leading +CPIN: from the output. | |
175 | output=${output#*: } | |
176 | ||
177 | case "${output}" in | |
178 | "READY") | |
179 | log DEBUG "${device}'s SIM is unlocked or doesn't need a PIN." | |
180 | return ${EXIT_SIM_READY} | |
181 | ;; | |
182 | "SIM PIN") | |
183 | log DEBUG "${device}'s SIM is waiting for a PIN." | |
184 | return ${EXIT_SIM_PIN} | |
185 | ;; | |
186 | "SIM PUK") | |
187 | log DEBUG "${device}'s SIM is PUK locked." | |
188 | return ${EXIT_SIM_PUK} | |
189 | ;; | |
190 | esac | |
191 | ||
192 | log WARNING "${device}: Invalid output of the AT+CPIN? command." | |
193 | return ${EXIT_SIM_UNKNOWN} | |
194 | } | |
195 | ||
1c6a4e30 | 196 | modem_sim_unlocked() { |
6c74a64c MT |
197 | local device=${1} |
198 | assert isset device | |
199 | ||
200 | modem_sim_status "${device}" | |
201 | local ret=$? | |
202 | ||
203 | [ ${ret} -eq ${EXIT_SIM_READY} ] && return ${EXIT_TRUE} || return ${EXIT_FALSE} | |
204 | } | |
205 | ||
1c6a4e30 | 206 | modem_sim_locked() { |
6c74a64c MT |
207 | modem_sim_unlocked $@ && return ${EXIT_FALSE} || return ${EXIT_TRUE} |
208 | } | |
209 | ||
1c6a4e30 | 210 | modem_sim_unlock() { |
6c74a64c MT |
211 | local device=${1} |
212 | assert isset device | |
213 | ||
214 | local pin=${2} | |
215 | assert isset pin | |
216 | ||
217 | local command="AT+CPIN=${pin}" | |
218 | ||
219 | local new_pin=${3} | |
220 | if isset new_pin; then | |
221 | command="${command},${new_pin}" | |
222 | fi | |
223 | ||
224 | modem_chat --timeout=2 --quiet "${device}" "${command}" | |
225 | ||
226 | local ret=$? | |
227 | case "${ret}" in | |
228 | ${EXIT_OK}) | |
229 | log INFO "Successfully unlocked SIM card on ${device}." | |
230 | ;; | |
231 | *) | |
232 | log ERROR "Could not unlock SIM card on ${device}." | |
233 | ret=${EXIT_ERROR} | |
234 | ;; | |
235 | esac | |
236 | ||
237 | return ${ret} | |
238 | } | |
239 | ||
1c6a4e30 | 240 | modem_sim_auto_unlock() { |
95416c1e MT |
241 | local device="${1}" |
242 | assert isset device | |
243 | ||
244 | local pin="${2}" | |
245 | assert isset pin | |
246 | ||
247 | # Get the current state the SIM card is in. | |
248 | modem_sim_status "${device}" &>/dev/null | |
249 | local sim_status_code=$? | |
250 | ||
251 | case "${sim_status_code}" in | |
252 | ${EXIT_SIM_READY}) | |
253 | # Everything's fine. The SIM card is | |
254 | # already unlocked. | |
255 | return ${EXIT_OK} | |
256 | ;; | |
257 | ${EXIT_SIM_PIN}) | |
258 | # Try to unlock the device. | |
259 | if modem_sim_unlock ${device} ${pin}; then | |
260 | return ${EXIT_OK} | |
261 | else | |
262 | return ${EXIT_ERROR} | |
263 | fi | |
264 | ;; | |
265 | ${EXIT_SIM_PUK}) | |
266 | log ERROR "SIM card is PUK locked. Please unlock manually." | |
267 | return ${EXIT_ERROR} | |
268 | ;; | |
269 | esac | |
270 | ||
271 | return ${EXIT_ERROR} | |
272 | } | |
273 | ||
6c74a64c MT |
274 | # Returns the vendor of the modem. |
275 | # For example: "huawei" | |
1c6a4e30 | 276 | modem_get_manufacturer() { |
6c74a64c MT |
277 | local device=${1} |
278 | assert isset device | |
279 | ||
280 | local output | |
281 | output=$(modem_chat ${device} "AT+GMI") | |
282 | assert_check_retval $? | |
283 | ||
284 | [ "${output:0:1}" = "+" ] && output=${output#*: } | |
285 | output=${output//\"/} | |
286 | ||
287 | print "${output}" | |
288 | } | |
289 | ||
1c6a4e30 | 290 | modem_get_model() { |
6c74a64c MT |
291 | local device=${1} |
292 | assert isset device | |
293 | ||
294 | local output | |
295 | output=$(modem_chat ${device} "AT+GMM") | |
296 | assert_check_retval $? | |
297 | ||
298 | [ "${output:0:1}" = "+" ] && output=${output#*: } | |
299 | output=${output//\"/} | |
300 | ||
301 | print "${output}" | |
302 | } | |
303 | ||
1c6a4e30 | 304 | modem_get_software_version() { |
6c74a64c MT |
305 | local device=${1} |
306 | assert isset device | |
307 | ||
308 | local output | |
309 | output=$(modem_chat ${device} "AT+GMR") | |
310 | assert_check_retval $? | |
311 | ||
312 | [ "${output:0:1}" = "+" ] && output=${output#*: } | |
313 | output=${output//\"/} | |
314 | ||
315 | print "${output}" | |
316 | } | |
317 | ||
1c6a4e30 | 318 | modem_get_sim_imsi() { |
6c74a64c MT |
319 | local device=${1} |
320 | assert isset device | |
321 | ||
6c74a64c MT |
322 | modem_chat ${device} "AT+CIMI" |
323 | } | |
324 | ||
1c6a4e30 | 325 | modem_get_device_imei() { |
6c74a64c MT |
326 | local device=${1} |
327 | assert isset device | |
328 | ||
329 | local output | |
330 | output=$(modem_chat --timeout=1 ${device} "AT+CGSN") || assert_check_retval $? | |
331 | local ret=$? | |
332 | ||
333 | if [ ${ret} -eq ${EXIT_OK} ]; then | |
334 | print "${output}" | |
335 | fi | |
336 | ||
337 | return ${ret} | |
338 | } | |
339 | ||
1c6a4e30 | 340 | modem_is_mobile() { |
6c74a64c MT |
341 | local device=${1} |
342 | assert isset device | |
343 | ||
344 | # Check if the device can return it's IMEI. | |
345 | # If not, it's probably a serial 56k modem or something | |
346 | # in that category. | |
347 | ||
348 | modem_get_device_imei ${device} &>/dev/null | |
349 | } | |
350 | ||
36aeb387 MT |
351 | modem_mobile_network_status() { |
352 | local device="${1}" | |
353 | assert isset device | |
354 | ||
355 | local ident="${2}" | |
356 | isset ident || ident=1 | |
357 | ||
358 | cli_headline "${ident}" "Network status" | |
359 | ||
360 | modem_sim_status ${device} &>/dev/null | |
361 | local sim_status_code=$? | |
362 | ||
363 | local sim_status="unknown" | |
364 | case "${sim_status_code}" in | |
365 | ${EXIT_SIM_READY}) | |
366 | sim_status="SIM ready" | |
367 | ;; | |
368 | ${EXIT_SIM_PIN}) | |
369 | sim_status="PIN locked" | |
370 | ;; | |
371 | ${EXIT_SIM_PUK}) | |
372 | sim_status="PUK locked" | |
373 | ;; | |
374 | esac | |
375 | cli_print_fmt1 "${ident}" "SIM status" "${sim_status}" | |
376 | ||
377 | if [ ${sim_status_code} -eq ${EXIT_SIM_READY} ]; then | |
378 | cli_print_fmt1 "${ident}" "Network Registration" \ | |
379 | "$(modem_get_network_registration ${device})" | |
380 | cli_print_fmt1 "${ident}" "Operator" \ | |
381 | "$(modem_get_network_operator ${device})" | |
382 | cli_print_fmt1 "${ident}" "Mode" \ | |
383 | "$(modem_get_network_mode ${device})" | |
384 | cli_print_fmt1 "${ident}" "IMSI" \ | |
385 | "$(modem_get_sim_imsi ${device})" | |
386 | cli_print_fmt1 "${ident}" "Signal quality" \ | |
387 | "$(modem_get_signal_quality ${device}) dBm" | |
388 | ||
389 | local ber=$(modem_get_bit_error_rate ${device}) | |
390 | isset ber || ber="unknown" | |
391 | cli_print_fmt1 "${ident}" "Bit Error Rate" "${ber}" | |
392 | fi | |
393 | ||
394 | return ${EXIT_OK} | |
395 | } | |
396 | ||
6c74a64c MT |
397 | # Exit codes of the network registration function. |
398 | EXIT_REG_REGISTERED_TO_HOME_NETWORK=0 | |
399 | EXIT_REG_NOT_REGISTERED_NOT_SEARCHING=1 | |
400 | EXIT_REG_NOT_REGISTERED_SEARCHING=2 | |
401 | EXIT_REG_REGISTRATION_DENIED=3 | |
402 | EXIT_REG_REGISTERED_ROAMING=4 | |
403 | EXIT_REG_UNKNOWN=5 | |
404 | ||
1c6a4e30 | 405 | modem_get_network_registration() { |
6c74a64c MT |
406 | local device=${1} |
407 | assert isset device | |
408 | ||
6c74a64c MT |
409 | local output |
410 | output=$(modem_chat ${device} "AT+CREG?") | |
411 | assert_check_retval $? | |
412 | ||
413 | # Cut out unneeded parts of the message. | |
414 | output=${output#*: } | |
415 | ||
416 | case "${output}" in | |
417 | [0-2],[0-5]) | |
418 | local status=${output%,*} | |
419 | ||
420 | # The status variable must be zero. | |
421 | [ ${status} -ne 0 ] && break | |
422 | ||
423 | local stat=${output#*,} | |
424 | case "${stat}" in | |
425 | 0) | |
d3096443 | 426 | print "Registered" |
6c74a64c MT |
427 | return ${EXIT_REG_NOT_REGISTERED_NOT_SEARCHING} |
428 | ;; | |
429 | 1) | |
d3096443 | 430 | print "Registered to home network" |
6c74a64c MT |
431 | return ${EXIT_REG_REGISTERED_TO_HOME_NETWORK} |
432 | ;; | |
433 | 2) | |
d3096443 | 434 | print "Registered, Searching" |
6c74a64c MT |
435 | return ${EXIT_REG_NOT_REGISTERED_SEARCHING} |
436 | ;; | |
437 | 3) | |
d3096443 | 438 | print "Registration denied" |
6c74a64c MT |
439 | return ${EXIT_REG_REGISTRATION_DENIED} |
440 | ;; | |
441 | 5) | |
d3096443 | 442 | print "Registered, Roaming" |
6c74a64c MT |
443 | return ${EXIT_REG_REGISTERED_ROAMING} |
444 | ;; | |
445 | *) | |
d3096443 | 446 | print "Unknown" |
6c74a64c MT |
447 | return ${EXIT_REG_UNKNOWN} |
448 | ;; | |
449 | esac | |
450 | ;; | |
451 | esac | |
452 | ||
453 | # Apparently the output of the CREG? command was not in | |
454 | # the right format. The modem will be tried to be set to the | |
455 | # right mode. | |
456 | ||
457 | modem_set_network_registration ${device} 0 | |
458 | modem_get_network_registration ${device} | |
459 | } | |
460 | ||
1c6a4e30 | 461 | modem_set_network_registration() { |
6c74a64c MT |
462 | local device=${1} |
463 | assert isset device | |
464 | ||
6c74a64c MT |
465 | local mode=${2} |
466 | assert isset mode | |
467 | ||
468 | modem_chat ${device} "AT+CREG=${mode}" | |
469 | } | |
470 | ||
1c6a4e30 | 471 | modem_scan_networks() { |
6c74a64c MT |
472 | local device=${1} |
473 | assert isset device | |
474 | ||
6c74a64c MT |
475 | local output |
476 | output=$(modem_chat --timeout=60 ${device} "AT+COPS=?") | |
477 | assert_check_retval $? | |
478 | ||
479 | output=${output#*: } | |
480 | ||
481 | # XXX the output is not very nice to parse. | |
482 | } | |
483 | ||
1c6a4e30 | 484 | __modem_get_network_operator() { |
6c74a64c MT |
485 | local device=${1} |
486 | assert isset device | |
487 | ||
6c74a64c MT |
488 | local argument=${2} |
489 | assert isset argument | |
490 | ||
491 | local output | |
492 | output=$(modem_chat ${device} "AT+COPS?") | |
493 | assert_check_retval $? | |
494 | ||
495 | output=${output#*: } | |
c5feadb0 MT |
496 | output=${output//,/ } |
497 | ||
498 | local arg mode format operator act | |
499 | local i=0 | |
500 | while read -r arg; do | |
501 | case "${i}" in | |
502 | 0) | |
503 | mode="${arg}" | |
504 | ;; | |
505 | 1) | |
506 | format="${arg}" | |
507 | ;; | |
508 | 2) | |
509 | operator="$(strip ${arg})" | |
510 | ;; | |
511 | 3) | |
512 | act="${arg}" | |
513 | ;; | |
514 | *) | |
515 | break | |
516 | ;; | |
517 | esac | |
518 | i="$(( ${i} + 1 ))" | |
519 | done <<< "$(args ${output})" | |
6c74a64c MT |
520 | |
521 | print "${!argument}" | |
522 | return ${EXIT_OK} | |
523 | } | |
524 | ||
1c6a4e30 | 525 | modem_get_network_operator() { |
6c74a64c MT |
526 | local device=${1} |
527 | assert isset device | |
528 | ||
529 | __modem_get_network_operator ${device} operator | |
530 | } | |
531 | ||
532 | # Exit codes of the network operator mode function. | |
533 | EXIT_OPMODE_GSM=0 | |
534 | EXIT_OPMODE_COMPACTGSM=1 | |
aa6a4e88 MT |
535 | EXIT_OPMODE_UMTS=2 |
536 | EXIT_OPMODE_GSM_WITH_EGPRS=3 | |
6c74a64c MT |
537 | EXIT_OPMODE_UMTS_WITH_HSDPA=4 |
538 | EXIT_OPMODE_UMTS_WITH_HSUPA=5 | |
539 | EXIT_OPMODE_UMTS_WITH_HSDPA_AND_HSUPA=6 | |
aa6a4e88 MT |
540 | EXIT_OPMODE_LTE=7 |
541 | EXIT_OPMODE_UNKNOWN=8 | |
6c74a64c | 542 | |
1c6a4e30 | 543 | modem_get_network_mode() { |
6c74a64c MT |
544 | local device=${1} |
545 | assert isset device | |
546 | ||
547 | local output | |
548 | output=$(__modem_get_network_operator ${device} act) | |
549 | assert_check_retval $? | |
550 | ||
551 | case "${output}" in | |
552 | 0) | |
553 | print "GSM" | |
554 | return ${EXIT_OPMODE_GSM} | |
555 | ;; | |
556 | 1) | |
557 | print "Compact GSM" | |
558 | return ${EXIT_OPMODE_COMPACTGSM} | |
559 | ;; | |
560 | 2) | |
561 | print "UMTS" | |
562 | return ${EXIT_OPMODE_UMTS} | |
563 | ;; | |
564 | 3) | |
aa6a4e88 MT |
565 | print "EDGE (GSM+EGPRS)" |
566 | return ${EXIT_OPMODE_GSM_WITH_EGPRS} | |
6c74a64c MT |
567 | ;; |
568 | 4) | |
aa6a4e88 MT |
569 | print "UMTS +HSDPA" |
570 | return ${EXIT_OPMODE_UMTS_WITH_HSDPA} | |
6c74a64c MT |
571 | ;; |
572 | 5) | |
aa6a4e88 MT |
573 | print "UMTS +HSUPA" |
574 | return ${EXIT_OPMODE_UMTS_WITH_HSUPA} | |
575 | ;; | |
576 | 6) | |
577 | print "UMTS +HSDPA +HSUPA" | |
6c74a64c MT |
578 | return ${EXIT_OPMODE_UMTS_WITH_HSDPA_AND_HSUPA} |
579 | ;; | |
aa6a4e88 MT |
580 | 7) |
581 | print "LTE" | |
582 | return ${EXIT_OPMODE_LTE} | |
583 | ;; | |
6c74a64c MT |
584 | *) |
585 | print "Unknown" | |
586 | return ${EXIT_OPMODE_UNKNOWN} | |
587 | ;; | |
588 | esac | |
589 | } | |
590 | ||
1c6a4e30 | 591 | __modem_get_signal_quality() { |
6c74a64c MT |
592 | local device=${1} |
593 | assert isset device | |
594 | ||
6c74a64c MT |
595 | local argument=${2} |
596 | assert isset argument | |
597 | ||
598 | local output | |
599 | output=$(modem_chat ${device} "AT+CSQ") | |
600 | assert_check_retval $? | |
601 | ||
602 | output=${output#*: } | |
603 | ||
604 | case "${output}" in | |
605 | *,*) | |
fbf647ae | 606 | local asu=${output%,*} |
6c74a64c MT |
607 | local ber=${output#*,} |
608 | ||
609 | print "${!argument}" | |
610 | return ${EXIT_OK} | |
611 | ;; | |
612 | *) | |
613 | log ERROR "Unknown format for AT+CSQ: ${device}: ${output}" | |
614 | ;; | |
615 | esac | |
616 | ||
617 | return ${EXIT_ERROR} | |
618 | } | |
619 | ||
fbf647ae MT |
620 | __modem_rssi_to_dbm() { |
621 | local rssi="${1}" | |
622 | ||
623 | # 99 indicates unknown signal strength | |
624 | [ ${rssi} -eq 99 ] && return ${EXIT_UNKNOWN} | |
625 | ||
626 | print "$(( ${rssi} * 2 - 113 ))" | |
627 | return ${EXIT_OK} | |
628 | } | |
629 | ||
630 | __modem_rscp_to_dbm() { | |
631 | local rscp="${1}" | |
632 | ||
633 | # 255 indicates unknown signal strength | |
634 | [ ${rscp} -eq 255 ] && return ${EXIT_UNKNOWN} | |
635 | ||
636 | print "$(( ${rscp} - 116 ))" | |
637 | return ${EXIT_OK} | |
638 | } | |
639 | ||
640 | __modem_rsrp_to_dbm() { | |
641 | local rsrp="${1}" | |
642 | ||
643 | case "${rsrp}" in | |
644 | 0) | |
645 | print "< -140" | |
646 | ;; | |
647 | 97) | |
648 | print "> -44" | |
649 | ;; | |
650 | *) | |
651 | # This is only an approximation since RSRP references | |
652 | # to a range of +/-1dbm | |
653 | print "$(( ${rsrp} - 141 ))" | |
654 | ;; | |
655 | esac | |
656 | ||
657 | return ${EXIT_OK} | |
658 | } | |
659 | ||
1c6a4e30 | 660 | modem_get_signal_quality() { |
6c74a64c MT |
661 | local device=${1} |
662 | assert isset device | |
663 | ||
fbf647ae MT |
664 | # Arbritrary Strength Unit |
665 | local asu | |
666 | asu=$(__modem_get_signal_quality ${device} asu) | |
6c74a64c MT |
667 | assert_check_retval $? |
668 | ||
fbf647ae | 669 | isset asu || return ${EXIT_ERROR} |
6c74a64c | 670 | |
fbf647ae | 671 | local network_mode="$(modem_get_network_mode ${device} &>/dev/null; echo $?)" |
6c74a64c | 672 | |
fbf647ae MT |
673 | local ret |
674 | case "${network_mode}" in | |
675 | # GSM | |
676 | ${EXIT_OPMODE_GSM}|${EXIT_OPMODE_COMPACTGSM}|${GSM_WITH_EGPRS}) | |
677 | __modem_rssi_to_dbm "${asu}" | |
678 | ret=${?} | |
679 | ;; | |
6c74a64c | 680 | |
fbf647ae MT |
681 | # UMTS |
682 | ${EXIT_OPMODE_UMTS}|${EXIT_OPMODE_UMTS_WITH_HSDPA}|${EXIT_OPMODE_UMTS_WITH_HSUPA}|${EXIT_OPMODE_UMTS_WITH_HSDPA_AND_HSUPA}) | |
683 | __modem_rscp_to_dbm "${asu}" | |
684 | ret=${?} | |
685 | ;; | |
686 | ||
687 | # LTE | |
688 | ${EXIT_OPMODE_LTE}) | |
689 | __modem_rsrp_to_dbm "${asu}" | |
690 | ret=${?} | |
691 | ;; | |
692 | ||
693 | # unknown | |
694 | *) | |
695 | ret=${EXIT_ERROR} | |
696 | ;; | |
697 | esac | |
698 | ||
699 | return ${ret} | |
6c74a64c MT |
700 | } |
701 | ||
1c6a4e30 | 702 | modem_get_bit_error_rate() { |
6c74a64c MT |
703 | local device=${1} |
704 | assert isset device | |
705 | ||
706 | local ber | |
707 | ber=$(__modem_get_signal_quality ${device} ber) | |
708 | assert_check_retval $? | |
709 | ||
710 | isset ber || return ${EXIT_ERROR} | |
711 | ||
712 | # 99 indicates that the bit error rate could not be detected or | |
713 | # is unknown for some other reason. | |
714 | [ ${ber} -eq 99 ] && return ${EXIT_UNKNOWN} | |
715 | ||
716 | print "%d" "${ber}" | |
717 | return ${EXIT_OK} | |
718 | } | |
5cf0edf9 MT |
719 | |
720 | # USDD stuff | |
721 | ||
1c6a4e30 | 722 | modem_ussd_send_command() { |
5cf0edf9 MT |
723 | local device="${1}" |
724 | assert isset device | |
725 | ||
726 | local command="${2}" | |
727 | assert isset command | |
728 | shift 2 | |
729 | ||
730 | local cleartext="false" | |
731 | local timeout="20" | |
732 | ||
733 | while [ $# -gt 0 ]; do | |
734 | case "${1}" in | |
735 | --cleartext) | |
736 | cleartext="true" | |
737 | ;; | |
738 | --timeout=*) | |
739 | timeout="$(cli_get_val "${1}")" | |
740 | ;; | |
741 | *) | |
742 | warning "Unrecognized argument: ${1}" | |
743 | ;; | |
744 | esac | |
745 | shift | |
746 | done | |
747 | ||
748 | local encoded_command="${command}" | |
749 | if ! enabled cleartext; then | |
750 | encoded_command="$(modem_ussd_encode "${command}")" | |
751 | fi | |
752 | ||
753 | log INFO "Sending USSD command '${command}' on ${device}" | |
754 | ||
755 | local at_command="AT+CUSD=1,\"${encoded_command}\",${encoding}" | |
756 | ||
757 | # Send the AT command and parse the output. | |
758 | modem_chat --answer="nothing" --timeout="${timeout}" \ | |
759 | "${device}" "${at_command}" | __modem_ussd_parse_output | |
760 | ||
761 | local ret=${PIPESTATUS[1]} | |
762 | return ${ret} | |
763 | } | |
764 | ||
1c6a4e30 | 765 | __modem_ussd_parse_output() { |
5cf0edf9 MT |
766 | local line |
767 | while read -r line; do | |
768 | # Find the expected answer. | |
769 | [ "${line:0:7}" = "+CUSD: " ] || continue | |
770 | ||
771 | # Strip +CUSD: | |
772 | line="${line:7}" | |
773 | ||
774 | local response_type | |
775 | local response | |
776 | local response_encoding | |
777 | ||
778 | line="${line//,/ }" | |
779 | ||
780 | local section=0 arg | |
781 | while read -r arg; do | |
782 | case "${section}" in | |
783 | 0) | |
784 | response_type="${arg}" | |
785 | ;; | |
786 | 1) | |
787 | response="${arg}" | |
788 | ;; | |
789 | 2) | |
790 | response_encoding="${arg}" | |
791 | ;; | |
792 | *) | |
793 | break | |
794 | ;; | |
795 | esac | |
796 | section=$(( ${section} + 1 )) | |
797 | done <<< "$(args ${line})" | |
798 | ||
799 | log DEBUG "USSD response type: ${response_type}" | |
800 | log DEBUG "USSD response encoding: ${response_encoding}" | |
801 | log DEBUG "USSD encoded response: ${response}" | |
802 | ||
803 | # If we got anything else than a response (type 0), | |
804 | # we don't know how to handle that. | |
805 | if [ "${response_type}" -ne "0" ]; then | |
806 | return ${EXIT_ERROR} | |
807 | fi | |
808 | ||
809 | # Decode the string if needed. | |
810 | case "${response_encoding}" in | |
811 | 15) | |
812 | response="$(modem_ussd_decode "${response}")" | |
813 | ;; | |
814 | esac | |
815 | log DEBUG "USSD response: ${response}" | |
816 | ||
817 | print "${response}" | |
818 | return ${EXIT_OK} | |
819 | done | |
820 | ||
821 | return ${EXIT_ERROR} | |
822 | } | |
823 | ||
1c6a4e30 | 824 | modem_ussd_encode() { |
5cf0edf9 MT |
825 | local string="${1}" |
826 | assert isset string | |
827 | ||
828 | local output buffer char | |
829 | while read -r char; do | |
830 | char="$(char2bin "${char}")" | |
831 | char="$(string_reverse "${char:1:7}")" | |
832 | ||
833 | buffer="${buffer}${char}" | |
834 | done <<< "$(string_split "${string}")" | |
835 | ||
836 | local pos=0 len=8 | |
837 | while [ ${pos} -lt ${#buffer} ]; do | |
838 | char="$(string_reverse "${buffer:${pos}:${len}}")" | |
839 | pos=$(( ${pos} + ${len} )) | |
840 | ||
841 | char="$(bin2hex "${char}")" | |
842 | output="${output}${char}" | |
843 | done | |
844 | ||
845 | # Make everything uppercase. | |
846 | output="${output^^}" | |
847 | ||
848 | print "${output}" | |
849 | } | |
850 | ||
1c6a4e30 | 851 | modem_ussd_decode() { |
5cf0edf9 MT |
852 | local string="${1}" |
853 | assert isset string | |
854 | ||
855 | local buffer1 buffer2 | |
856 | local output char | |
857 | ||
858 | local pos=0 len=2 | |
859 | while [ ${pos} -lt ${#string} ]; do | |
860 | char="${string:${pos}:${len}}" | |
861 | pos=$(( ${pos} + ${len} )) | |
862 | ||
863 | char="$(hex2bin "${char}")" | |
864 | char="$(string_reverse "${char}")" | |
865 | buffer1="${buffer1}${char}" | |
866 | done | |
867 | ||
868 | # Reset pointers. | |
869 | pos=0 | |
870 | len=7 | |
871 | ||
872 | while [ ${pos} -lt ${#buffer1} ]; do | |
873 | char="${buffer1:${pos}:${len}}" | |
874 | pos=$(( ${pos} + ${len} )) | |
875 | ||
876 | buffer2="${buffer2}0${char}" | |
877 | done | |
878 | buffer2="${buffer2:1}" | |
879 | ||
880 | # Reset pointers again. | |
881 | pos=0 | |
882 | len=8 | |
883 | ||
884 | while [ ${pos} -lt ${#buffer2} ]; do | |
885 | char="${buffer2:${pos}:${len}}" | |
886 | pos=$(( ${pos} + ${len} )) | |
887 | ||
888 | char="$(string_reverse "${char}")" | |
889 | char="$(bin2char "${char}")" | |
890 | output="${output}${char}" | |
891 | done | |
892 | ||
893 | print "${output}" | |
894 | return ${EXIT_OK} | |
895 | } |