2 ###############################################################################
4 # IPFire.org - A linux based firewall #
5 # Copyright (C) 2012 IPFire Network Development Team #
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. #
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. #
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/>. #
20 ###############################################################################
22 # Exit codes from the chat(8) command:
34 while [ $# -gt 0 ]; do
37 timeout
=$
(cli_get_val
"${1}")
40 answer
=$
(cli_get_val
"${1}")
53 assert serial_exists
${device}
59 log DEBUG
"Sending command to ${device}: ${command}"
62 # This cannot be run with -x.
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}
70 local ret=${PIPESTATUS[0]}
72 # Return the exit code of the chat command.
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})
85 return ${EXIT_CONF_ERROR}
89 log WARNING "Received unknown
exit code from chat
(8): ${ret}"
93 __modem_chat_process_output() {
97 if enabled quiet; then
98 # Just throw everything away.
106 while read -r line; do
107 # Also skip empty lines.
108 [ -n "${line}" ] || continue
110 # Ignore all volatile messages.
111 [ "${line:0:1}" = "^
" ] && continue
113 log DEBUG "Output
[${counter}]: ${line}"
114 counter=$(( ${counter} + 1 ))
116 # Skip the first line, because that's out command.
117 [ ${counter} -eq 1 ] && continue
119 # Filter out the expected answer.
120 [ "${line}" = "${answer}" ] && continue
132 # Sleep for $sleep seconds, to give
133 # the modem a moment to initialize itself.
136 while [ $# -gt 0 ]; do
139 sleep="$
(cli_get_val
"${1}")"
142 warning "Unrecognized argument
: ${1}"
147 assert isinteger sleep
149 log INFO "Initializing modem
${device}"
152 modem_chat "${device}" "${AT_INITIALIZE}"
155 if [ ${sleep} -gt 0 ]; then
160 # Exit codes of the sim_status function.
171 output=$(modem_chat ${device} "AT
+CPIN?
")
172 assert_check_retval $?
174 # Strip leading +CPIN: from the output.
179 log DEBUG "${device}'s SIM is unlocked or doesn't need a PIN.
"
180 return ${EXIT_SIM_READY}
183 log DEBUG "${device}'s SIM is waiting for a PIN."
184 return ${EXIT_SIM_PIN}
187 log DEBUG "${device}'s SIM is PUK locked.
"
188 return ${EXIT_SIM_PUK}
192 log WARNING "${device}: Invalid output of the AT
+CPIN?
command.
"
193 return ${EXIT_SIM_UNKNOWN}
196 modem_sim_unlocked() {
200 modem_sim_status "${device}"
203 [ ${ret} -eq ${EXIT_SIM_READY} ] && return ${EXIT_TRUE} || return ${EXIT_FALSE}
207 modem_sim_unlocked "$@
" && return ${EXIT_FALSE} || return ${EXIT_TRUE}
217 local command="AT
+CPIN
=${pin}"
220 if isset new_pin; then
221 command="${command},${new_pin}"
224 modem_chat --timeout=2 --quiet "${device}" "${command}"
229 log INFO "Successfully unlocked SIM card on
${device}.
"
232 log ERROR "Could not unlock SIM card on
${device}.
"
240 modem_sim_auto_unlock() {
247 # Get the current state the SIM card is in.
248 modem_sim_status "${device}" &>/dev/null
249 local sim_status_code=$?
251 case "${sim_status_code}" in
253 # Everything's fine. The SIM card is
258 # Try to unlock the device.
259 if modem_sim_unlock ${device} ${pin}; then
266 log ERROR "SIM card is PUK locked. Please unlock manually.
"
274 # Returns the vendor of the modem.
275 # For example: "huawei
"
276 modem_get_manufacturer() {
281 output=$(modem_chat ${device} "AT
+GMI
")
282 assert_check_retval $?
284 [ "${output:0:1}" = "+" ] && output=${output#*: }
285 output=${output//\"/}
295 output=$(modem_chat ${device} "AT
+GMM
")
296 assert_check_retval $?
298 [ "${output:0:1}" = "+" ] && output=${output#*: }
299 output=${output//\"/}
304 modem_get_software_version() {
309 output=$(modem_chat ${device} "AT
+GMR
")
310 assert_check_retval $?
312 [ "${output:0:1}" = "+" ] && output=${output#*: }
313 output=${output//\"/}
318 modem_get_sim_imsi() {
322 modem_chat ${device} "AT
+CIMI
"
325 modem_get_device_imei() {
330 output=$(modem_chat --timeout=1 ${device} "AT
+CGSN
") || assert_check_retval $?
333 if [ ${ret} -eq ${EXIT_OK} ]; then
344 # Check if the device can return it's IMEI.
345 # If not, it's probably a serial 56k modem or something
348 modem_get_device_imei ${device} &>/dev/null
351 modem_mobile_network_status() {
356 isset ident || ident=1
358 cli_headline "${ident}" "Network status
"
360 modem_sim_status ${device} &>/dev/null
361 local sim_status_code=$?
363 local sim_status="unknown
"
364 case "${sim_status_code}" in
366 sim_status="SIM ready
"
369 sim_status="PIN locked
"
372 sim_status="PUK locked
"
375 cli_print_fmt1 "${ident}" "SIM status
" "${sim_status}"
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
"
389 local ber=$(modem_get_bit_error_rate ${device})
390 isset ber || ber="unknown
"
391 cli_print_fmt1 "${ident}" "Bit Error Rate
" "${ber}"
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
405 modem_get_network_registration() {
410 output=$(modem_chat ${device} "AT
+CREG?
")
411 assert_check_retval $?
413 # Cut out unneeded parts of the message.
418 local status=${output%,*}
420 # The status variable must be zero.
421 [ ${status} -ne 0 ] && break
423 local stat=${output#*,}
427 return ${EXIT_REG_NOT_REGISTERED_NOT_SEARCHING}
430 print "Registered to home network
"
431 return ${EXIT_REG_REGISTERED_TO_HOME_NETWORK}
434 print "Registered
, Searching
"
435 return ${EXIT_REG_NOT_REGISTERED_SEARCHING}
438 print "Registration denied
"
439 return ${EXIT_REG_REGISTRATION_DENIED}
442 print "Registered
, Roaming
"
443 return ${EXIT_REG_REGISTERED_ROAMING}
447 return ${EXIT_REG_UNKNOWN}
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
457 modem_set_network_registration ${device} 0
458 modem_get_network_registration ${device}
461 modem_set_network_registration() {
468 modem_chat ${device} "AT
+CREG
=${mode}"
471 modem_scan_networks() {
476 output=$(modem_chat --timeout=60 ${device} "AT
+COPS
=?
")
477 assert_check_retval $?
481 # XXX the output is not very nice to parse.
484 __modem_get_network_operator() {
489 assert isset argument
492 output=$(modem_chat ${device} "AT
+COPS?
")
493 assert_check_retval $?
496 output=${output//,/ }
498 local arg mode format operator act
500 while read -r arg; do
509 operator="$
(strip
${arg})"
519 done <<< "$
(args
${output})"
525 modem_get_network_operator() {
529 __modem_get_network_operator ${device} operator
532 # Exit codes of the network operator mode function.
534 EXIT_OPMODE_COMPACTGSM=1
536 EXIT_OPMODE_GSM_WITH_EGPRS=3
537 EXIT_OPMODE_UMTS_WITH_HSDPA=4
538 EXIT_OPMODE_UMTS_WITH_HSUPA=5
539 EXIT_OPMODE_UMTS_WITH_HSDPA_AND_HSUPA=6
541 EXIT_OPMODE_UNKNOWN=8
543 modem_get_network_mode() {
548 output=$(__modem_get_network_operator ${device} act)
549 assert_check_retval $?
554 return ${EXIT_OPMODE_GSM}
558 return ${EXIT_OPMODE_COMPACTGSM}
562 return ${EXIT_OPMODE_UMTS}
565 print "EDGE
(GSM
+EGPRS
)"
566 return ${EXIT_OPMODE_GSM_WITH_EGPRS}
570 return ${EXIT_OPMODE_UMTS_WITH_HSDPA}
574 return ${EXIT_OPMODE_UMTS_WITH_HSUPA}
577 print "UMTS
+HSDPA
+HSUPA
"
578 return ${EXIT_OPMODE_UMTS_WITH_HSDPA_AND_HSUPA}
582 return ${EXIT_OPMODE_LTE}
586 return ${EXIT_OPMODE_UNKNOWN}
591 __modem_get_signal_quality() {
596 assert isset argument
599 output=$(modem_chat ${device} "AT
+CSQ
")
600 assert_check_retval $?
606 local asu=${output%,*}
607 local ber=${output#*,}
613 log ERROR "Unknown format
for AT
+CSQ
: ${device}: ${output}"
620 __modem_rssi_to_dbm() {
623 # 99 indicates unknown signal strength
624 [ ${rssi} -eq 99 ] && return ${EXIT_UNKNOWN}
626 print "$
(( ${rssi} * 2 - 113 ))"
630 __modem_rscp_to_dbm() {
633 # 255 indicates unknown signal strength
634 [ ${rscp} -eq 255 ] && return ${EXIT_UNKNOWN}
636 print "$
(( ${rscp} - 116 ))"
640 __modem_rsrp_to_dbm() {
651 # This is only an approximation since RSRP references
652 # to a range of +/-1dbm
653 print "$
(( ${rsrp} - 141 ))"
660 modem_get_signal_quality() {
664 # Arbritrary Strength Unit
666 asu=$(__modem_get_signal_quality ${device} asu)
667 assert_check_retval $?
669 isset asu || return ${EXIT_ERROR}
671 local network_mode="$
(modem_get_network_mode
${device} &>/dev
/null
; echo $?
)"
674 case "${network_mode}" in
676 ${EXIT_OPMODE_GSM}|${EXIT_OPMODE_COMPACTGSM}|${GSM_WITH_EGPRS})
677 __modem_rssi_to_dbm "${asu}"
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}"
689 __modem_rsrp_to_dbm "${asu}"
702 modem_get_bit_error_rate() {
707 ber=$(__modem_get_signal_quality ${device} ber)
708 assert_check_retval $?
710 isset ber || return ${EXIT_ERROR}
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}
722 modem_ussd_send_command() {
730 local cleartext="false
"
733 while [ $# -gt 0 ]; do
739 timeout="$
(cli_get_val
"${1}")"
742 warning "Unrecognized argument
: ${1}"
748 local encoded_command="${command}"
749 if ! enabled cleartext; then
750 encoded_command="$
(modem_ussd_encode
"${command}")"
753 log INFO "Sending USSD
command '${command}' on
${device}"
755 local at_command="AT
+CUSD
=1,\"${encoded_command}\",${encoding}"
757 # Send the AT command and parse the output.
758 modem_chat --answer="nothing
" --timeout="${timeout}" \
759 "${device}" "${at_command}" | __modem_ussd_parse_output
761 local ret=${PIPESTATUS[1]}
765 __modem_ussd_parse_output() {
767 while read -r line; do
768 # Find the expected answer.
769 [ "${line:0:7}" = "+CUSD
: " ] || continue
776 local response_encoding
781 while read -r arg; do
784 response_type="${arg}"
790 response_encoding="${arg}"
796 section=$(( ${section} + 1 ))
797 done <<< "$
(args
${line})"
799 log DEBUG "USSD response
type: ${response_type}"
800 log DEBUG "USSD response encoding
: ${response_encoding}"
801 log DEBUG "USSD encoded response
: ${response}"
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
809 # Decode the string if needed.
810 case "${response_encoding}" in
812 response="$
(modem_ussd_decode
"${response}")"
815 log DEBUG "USSD response
: ${response}"
824 modem_ussd_encode() {
828 local output buffer char
829 while read -r char; do
830 char="$
(char2bin
"${char}")"
831 char="$
(string_reverse
"${char:1:7}")"
833 buffer="${buffer}${char}"
834 done <<< "$
(string_split
"${string}")"
837 while [ ${pos} -lt ${#buffer} ]; do
838 char="$
(string_reverse
"${buffer:${pos}:${len}}")"
839 pos=$(( ${pos} + ${len} ))
841 char="$
(bin2hex
"${char}")"
842 output="${output}${char}"
845 # Make everything uppercase.
851 modem_ussd_decode() {
855 local buffer1 buffer2
859 while [ ${pos} -lt ${#string} ]; do
860 char="${string:${pos}:${len}}"
861 pos=$(( ${pos} + ${len} ))
863 char="$
(hex2bin
"${char}")"
864 char="$
(string_reverse
"${char}")"
865 buffer1="${buffer1}${char}"
872 while [ ${pos} -lt ${#buffer1} ]; do
873 char="${buffer1:${pos}:${len}}"
874 pos=$(( ${pos} + ${len} ))
876 buffer2="${buffer2}0${char}"
878 buffer2="${buffer2:1}"
880 # Reset pointers again.
884 while [ ${pos} -lt ${#buffer2} ]; do
885 char="${buffer2:${pos}:${len}}"
886 pos=$(( ${pos} + ${len} ))
888 char="$
(string_reverse
"${char}")"
889 char="$
(bin2char
"${char}")"
890 output="${output}${char}"