From: Michael Tremer Date: Mon, 20 May 2013 20:00:07 +0000 (+0200) Subject: Add functionality to send USSD commands. X-Git-Tag: 007~123 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5cf0edf90adf31bfd0ac998c3dfbef9386a6c45d;p=network.git Add functionality to send USSD commands. --- diff --git a/functions.modem b/functions.modem index ab9f587a..8d96b1ab 100644 --- a/functions.modem +++ b/functions.modem @@ -612,3 +612,180 @@ function modem_get_bit_error_rate() { print "%d" "${ber}" return ${EXIT_OK} } + +# USDD stuff + +function modem_ussd_send_command() { + local device="${1}" + assert isset device + + local command="${2}" + assert isset command + shift 2 + + local cleartext="false" + local timeout="20" + + while [ $# -gt 0 ]; do + case "${1}" in + --cleartext) + cleartext="true" + ;; + --timeout=*) + timeout="$(cli_get_val "${1}")" + ;; + *) + warning "Unrecognized argument: ${1}" + ;; + esac + shift + done + + local encoded_command="${command}" + if ! enabled cleartext; then + encoded_command="$(modem_ussd_encode "${command}")" + fi + + log INFO "Sending USSD command '${command}' on ${device}" + + local at_command="AT+CUSD=1,\"${encoded_command}\",${encoding}" + + # Send the AT command and parse the output. + modem_chat --answer="nothing" --timeout="${timeout}" \ + "${device}" "${at_command}" | __modem_ussd_parse_output + + local ret=${PIPESTATUS[1]} + return ${ret} +} + +function __modem_ussd_parse_output() { + local line + while read -r line; do + # Find the expected answer. + [ "${line:0:7}" = "+CUSD: " ] || continue + + # Strip +CUSD: + line="${line:7}" + + local response_type + local response + local response_encoding + + line="${line//,/ }" + + local section=0 arg + while read -r arg; do + case "${section}" in + 0) + response_type="${arg}" + ;; + 1) + response="${arg}" + ;; + 2) + response_encoding="${arg}" + ;; + *) + break + ;; + esac + section=$(( ${section} + 1 )) + done <<< "$(args ${line})" + + log DEBUG "USSD response type: ${response_type}" + log DEBUG "USSD response encoding: ${response_encoding}" + log DEBUG "USSD encoded response: ${response}" + + # If we got anything else than a response (type 0), + # we don't know how to handle that. + if [ "${response_type}" -ne "0" ]; then + return ${EXIT_ERROR} + fi + + # Decode the string if needed. + case "${response_encoding}" in + 15) + response="$(modem_ussd_decode "${response}")" + ;; + esac + log DEBUG "USSD response: ${response}" + + print "${response}" + return ${EXIT_OK} + done + + return ${EXIT_ERROR} +} + +function modem_ussd_encode() { + local string="${1}" + assert isset string + + local output buffer char + while read -r char; do + char="$(char2bin "${char}")" + char="$(string_reverse "${char:1:7}")" + + buffer="${buffer}${char}" + done <<< "$(string_split "${string}")" + + local pos=0 len=8 + while [ ${pos} -lt ${#buffer} ]; do + char="$(string_reverse "${buffer:${pos}:${len}}")" + pos=$(( ${pos} + ${len} )) + + char="$(bin2hex "${char}")" + output="${output}${char}" + done + + # Make everything uppercase. + output="${output^^}" + + print "${output}" +} + +function modem_ussd_decode() { + local string="${1}" + assert isset string + + local buffer1 buffer2 + local output char + + local pos=0 len=2 + while [ ${pos} -lt ${#string} ]; do + char="${string:${pos}:${len}}" + pos=$(( ${pos} + ${len} )) + + char="$(hex2bin "${char}")" + char="$(string_reverse "${char}")" + buffer1="${buffer1}${char}" + done + + # Reset pointers. + pos=0 + len=7 + + while [ ${pos} -lt ${#buffer1} ]; do + char="${buffer1:${pos}:${len}}" + pos=$(( ${pos} + ${len} )) + + buffer2="${buffer2}0${char}" + done + buffer2="${buffer2:1}" + + # Reset pointers again. + pos=0 + len=8 + + while [ ${pos} -lt ${#buffer2} ]; do + char="${buffer2:${pos}:${len}}" + pos=$(( ${pos} + ${len} )) + + char="$(string_reverse "${char}")" + char="$(bin2char "${char}")" + output="${output}${char}" + done + + print "${output}" + return ${EXIT_OK} +} diff --git a/functions.util b/functions.util index 2eef4a7a..79ec87db 100644 --- a/functions.util +++ b/functions.util @@ -455,6 +455,22 @@ function dec() { printf "%d\n" "${hex}" } +function chr() { + local char="${1}" + + [ ${char} -lt 256 ] || return ${EXIT_ERROR} + + printf "\\$(( ${char} / 64 * 100 + ${char} % 64 / 8 * 10 + ${char} % 8 ))\n" +} + +function ord() { + LC_CTYPE="C" printf "%d\n" "'${1}" +} + +function hex() { + printf "%X\n" "${1}" +} + function network_is_running() { # Check, if the network service is running. service_is_active network @@ -472,3 +488,118 @@ function contains_spaces() { return ${EXIT_FALSE} } + +function string_split() { + local string="$@" + + local pos=0 + while [ ${pos} -lt ${#string} ]; do + print "${string:${pos}:1}" + pos=$(( ${pos} + 1 )) + done + + return ${EXIT_OK} +} + +function string_reverse() { + local string="$@" + + local output + local pos=0 + while [ ${pos} -lt ${#string} ]; do + output="${string:${pos}:1}${output}" + pos=$(( ${pos} + 1 )) + done + + print "${output}" + return ${EXIT_OK} +} + +function dec2bin() { + local number="${1}" + + local output + + local i div + for i in 7 6 5 4 3 2 1; do + div=$(( 2 ** ${i} )) + + if [ $(( ${number} / ${div} )) -eq 1 ]; then + output="${output}1" + else + output="${output}0" + fi + number="$(( ${number} % ${div} ))" + done + + if [ $(( ${number} % 2 )) -eq 1 ]; then + output="${output}1" + else + output="${output}0" + fi + + print "${output}" +} + +function bin2dec() { + local string="${1}" + local number=0 + + local pos=0 char + while [ ${pos} -lt ${#string} ]; do + char="${string:${pos}:1}" + pos=$(( ${pos} + 1 )) + + number=$(( ${number} << 1 )) + + case "${char}" in + 0) ;; + 1) + number=$(( ${number} + 1 )) + ;; + *) + assert false "Invalid character: ${char}" + ;; + esac + done + + print "${number}" + return ${EXIT_OK} +} + +function char2bin() { + local dec="$(ord "${1}")" + + dec2bin "${dec}" +} + +function bin2char() { + local dec="$(bin2dec "$@")" + + chr "${dec}" +} + +function bin2hex() { + local dec="$(bin2dec "$@")" + + dec2hex "${dec}" +} + +function hex2bin() { + local dec="$(hex2dec "$@")" + + dec2bin "${dec}" +} + +function hex2dec() { + local hex="${1}" + + # Prepend 0x if necessary. + [ "${hex:0:2}" = "0x" ] || hex="0x${hex}" + + printf "%d\n" "${hex}" +} + +function dec2hex() { + printf "%02x\n" "${1}" +} diff --git a/network b/network index 5a5aba28..f330f2bd 100755 --- a/network +++ b/network @@ -79,6 +79,9 @@ function cli_device() { unlock) cli_device_serial_unlock ${device} $@ ;; + ussd) + cli_device_send_ussd_command "${device}" $@ + ;; *) cli_show_man network-device ;; @@ -383,6 +386,41 @@ function cli_device_serial_unlock() { exit $? } +function cli_device_send_ussd_command() { + local device="${1}" + assert isset device + shift + + local command + local timeout + + while [ $# -gt 0 ]; do + case "${1}" in + --timeout=*) + timeout="$(cli_get_val "${1}")" + ;; + *) + if isset command; then + warning "Unrecognized argument: ${1}" + else + command="${1}" + fi + ;; + esac + shift + done + + assert device_is_serial "${device}" + + local args + if isset timeout; then + args="${args} --timeout=${timeout}" + fi + + modem_ussd_send_command "${device}" "${command}" ${args} + exit $? +} + function cli_hostname() { if cli_help_requested $@; then cli_show_man network