]> git.ipfire.org Git - people/ms/network.git/commitdiff
Add functionality to send USSD commands.
authorMichael Tremer <michael.tremer@ipfire.org>
Mon, 20 May 2013 20:00:07 +0000 (22:00 +0200)
committerMichael Tremer <michael.tremer@ipfire.org>
Mon, 20 May 2013 20:00:07 +0000 (22:00 +0200)
functions.modem
functions.util
network

index ab9f587a68384600608e599a15c519800af41731..8d96b1abe37c05720574a169b036708c0788a0b2 100644 (file)
@@ -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}
+}
index 2eef4a7a77bd260547fefa7d887b9997167d3d1f..79ec87db7e579fa078f0f37f8443332ee4feec19 100644 (file)
@@ -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 5a5aba2899fb36e7fd1207aa07051b14a58ccbad..f330f2bdbb6c5f4d1b096bd573990a5bdad81647 100755 (executable)
--- 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