]> git.ipfire.org Git - people/ms/network.git/blame - src/functions/functions.modem
modem: Correctly calculate signal strength
[people/ms/network.git] / src / functions / functions.modem
CommitLineData
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:
23CHAT_OK=0
24CHAT_INVALID=1
25CHAT_ERROR=2
26CHAT_TIMEOUT=3
27
1c6a4e30 28modem_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 127modem_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.
161EXIT_SIM_READY=0
162EXIT_SIM_PIN=1
163EXIT_SIM_PUK=2
164EXIT_SIM_UNKNOWN=3
165
1c6a4e30 166modem_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 196modem_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 206modem_sim_locked() {
6c74a64c
MT
207 modem_sim_unlocked $@ && return ${EXIT_FALSE} || return ${EXIT_TRUE}
208}
209
1c6a4e30 210modem_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 240modem_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 276modem_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 290modem_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 304modem_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 318modem_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 325modem_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 340modem_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
351# Exit codes of the network registration function.
352EXIT_REG_REGISTERED_TO_HOME_NETWORK=0
353EXIT_REG_NOT_REGISTERED_NOT_SEARCHING=1
354EXIT_REG_NOT_REGISTERED_SEARCHING=2
355EXIT_REG_REGISTRATION_DENIED=3
356EXIT_REG_REGISTERED_ROAMING=4
357EXIT_REG_UNKNOWN=5
358
1c6a4e30 359modem_get_network_registration() {
6c74a64c
MT
360 local device=${1}
361 assert isset device
362
6c74a64c
MT
363 local output
364 output=$(modem_chat ${device} "AT+CREG?")
365 assert_check_retval $?
366
367 # Cut out unneeded parts of the message.
368 output=${output#*: }
369
370 case "${output}" in
371 [0-2],[0-5])
372 local status=${output%,*}
373
374 # The status variable must be zero.
375 [ ${status} -ne 0 ] && break
376
377 local stat=${output#*,}
378 case "${stat}" in
379 0)
d3096443 380 print "Registered"
6c74a64c
MT
381 return ${EXIT_REG_NOT_REGISTERED_NOT_SEARCHING}
382 ;;
383 1)
d3096443 384 print "Registered to home network"
6c74a64c
MT
385 return ${EXIT_REG_REGISTERED_TO_HOME_NETWORK}
386 ;;
387 2)
d3096443 388 print "Registered, Searching"
6c74a64c
MT
389 return ${EXIT_REG_NOT_REGISTERED_SEARCHING}
390 ;;
391 3)
d3096443 392 print "Registration denied"
6c74a64c
MT
393 return ${EXIT_REG_REGISTRATION_DENIED}
394 ;;
395 5)
d3096443 396 print "Registered, Roaming"
6c74a64c
MT
397 return ${EXIT_REG_REGISTERED_ROAMING}
398 ;;
399 *)
d3096443 400 print "Unknown"
6c74a64c
MT
401 return ${EXIT_REG_UNKNOWN}
402 ;;
403 esac
404 ;;
405 esac
406
407 # Apparently the output of the CREG? command was not in
408 # the right format. The modem will be tried to be set to the
409 # right mode.
410
411 modem_set_network_registration ${device} 0
412 modem_get_network_registration ${device}
413}
414
1c6a4e30 415modem_set_network_registration() {
6c74a64c
MT
416 local device=${1}
417 assert isset device
418
6c74a64c
MT
419 local mode=${2}
420 assert isset mode
421
422 modem_chat ${device} "AT+CREG=${mode}"
423}
424
1c6a4e30 425modem_scan_networks() {
6c74a64c
MT
426 local device=${1}
427 assert isset device
428
6c74a64c
MT
429 local output
430 output=$(modem_chat --timeout=60 ${device} "AT+COPS=?")
431 assert_check_retval $?
432
433 output=${output#*: }
434
435 # XXX the output is not very nice to parse.
436}
437
1c6a4e30 438__modem_get_network_operator() {
6c74a64c
MT
439 local device=${1}
440 assert isset device
441
6c74a64c
MT
442 local argument=${2}
443 assert isset argument
444
445 local output
446 output=$(modem_chat ${device} "AT+COPS?")
447 assert_check_retval $?
448
449 output=${output#*: }
c5feadb0
MT
450 output=${output//,/ }
451
452 local arg mode format operator act
453 local i=0
454 while read -r arg; do
455 case "${i}" in
456 0)
457 mode="${arg}"
458 ;;
459 1)
460 format="${arg}"
461 ;;
462 2)
463 operator="$(strip ${arg})"
464 ;;
465 3)
466 act="${arg}"
467 ;;
468 *)
469 break
470 ;;
471 esac
472 i="$(( ${i} + 1 ))"
473 done <<< "$(args ${output})"
6c74a64c
MT
474
475 print "${!argument}"
476 return ${EXIT_OK}
477}
478
1c6a4e30 479modem_get_network_operator() {
6c74a64c
MT
480 local device=${1}
481 assert isset device
482
483 __modem_get_network_operator ${device} operator
484}
485
486# Exit codes of the network operator mode function.
487EXIT_OPMODE_GSM=0
488EXIT_OPMODE_COMPACTGSM=1
aa6a4e88
MT
489EXIT_OPMODE_UMTS=2
490EXIT_OPMODE_GSM_WITH_EGPRS=3
6c74a64c
MT
491EXIT_OPMODE_UMTS_WITH_HSDPA=4
492EXIT_OPMODE_UMTS_WITH_HSUPA=5
493EXIT_OPMODE_UMTS_WITH_HSDPA_AND_HSUPA=6
aa6a4e88
MT
494EXIT_OPMODE_LTE=7
495EXIT_OPMODE_UNKNOWN=8
6c74a64c 496
1c6a4e30 497modem_get_network_mode() {
6c74a64c
MT
498 local device=${1}
499 assert isset device
500
501 local output
502 output=$(__modem_get_network_operator ${device} act)
503 assert_check_retval $?
504
505 case "${output}" in
506 0)
507 print "GSM"
508 return ${EXIT_OPMODE_GSM}
509 ;;
510 1)
511 print "Compact GSM"
512 return ${EXIT_OPMODE_COMPACTGSM}
513 ;;
514 2)
515 print "UMTS"
516 return ${EXIT_OPMODE_UMTS}
517 ;;
518 3)
aa6a4e88
MT
519 print "EDGE (GSM+EGPRS)"
520 return ${EXIT_OPMODE_GSM_WITH_EGPRS}
6c74a64c
MT
521 ;;
522 4)
aa6a4e88
MT
523 print "UMTS +HSDPA"
524 return ${EXIT_OPMODE_UMTS_WITH_HSDPA}
6c74a64c
MT
525 ;;
526 5)
aa6a4e88
MT
527 print "UMTS +HSUPA"
528 return ${EXIT_OPMODE_UMTS_WITH_HSUPA}
529 ;;
530 6)
531 print "UMTS +HSDPA +HSUPA"
6c74a64c
MT
532 return ${EXIT_OPMODE_UMTS_WITH_HSDPA_AND_HSUPA}
533 ;;
aa6a4e88
MT
534 7)
535 print "LTE"
536 return ${EXIT_OPMODE_LTE}
537 ;;
6c74a64c
MT
538 *)
539 print "Unknown"
540 return ${EXIT_OPMODE_UNKNOWN}
541 ;;
542 esac
543}
544
1c6a4e30 545__modem_get_signal_quality() {
6c74a64c
MT
546 local device=${1}
547 assert isset device
548
6c74a64c
MT
549 local argument=${2}
550 assert isset argument
551
552 local output
553 output=$(modem_chat ${device} "AT+CSQ")
554 assert_check_retval $?
555
556 output=${output#*: }
557
558 case "${output}" in
559 *,*)
fbf647ae 560 local asu=${output%,*}
6c74a64c
MT
561 local ber=${output#*,}
562
563 print "${!argument}"
564 return ${EXIT_OK}
565 ;;
566 *)
567 log ERROR "Unknown format for AT+CSQ: ${device}: ${output}"
568 ;;
569 esac
570
571 return ${EXIT_ERROR}
572}
573
fbf647ae
MT
574__modem_rssi_to_dbm() {
575 local rssi="${1}"
576
577 # 99 indicates unknown signal strength
578 [ ${rssi} -eq 99 ] && return ${EXIT_UNKNOWN}
579
580 print "$(( ${rssi} * 2 - 113 ))"
581 return ${EXIT_OK}
582}
583
584__modem_rscp_to_dbm() {
585 local rscp="${1}"
586
587 # 255 indicates unknown signal strength
588 [ ${rscp} -eq 255 ] && return ${EXIT_UNKNOWN}
589
590 print "$(( ${rscp} - 116 ))"
591 return ${EXIT_OK}
592}
593
594__modem_rsrp_to_dbm() {
595 local rsrp="${1}"
596
597 case "${rsrp}" in
598 0)
599 print "< -140"
600 ;;
601 97)
602 print "> -44"
603 ;;
604 *)
605 # This is only an approximation since RSRP references
606 # to a range of +/-1dbm
607 print "$(( ${rsrp} - 141 ))"
608 ;;
609 esac
610
611 return ${EXIT_OK}
612}
613
1c6a4e30 614modem_get_signal_quality() {
6c74a64c
MT
615 local device=${1}
616 assert isset device
617
fbf647ae
MT
618 # Arbritrary Strength Unit
619 local asu
620 asu=$(__modem_get_signal_quality ${device} asu)
6c74a64c
MT
621 assert_check_retval $?
622
fbf647ae 623 isset asu || return ${EXIT_ERROR}
6c74a64c 624
fbf647ae 625 local network_mode="$(modem_get_network_mode ${device} &>/dev/null; echo $?)"
6c74a64c 626
fbf647ae
MT
627 local ret
628 case "${network_mode}" in
629 # GSM
630 ${EXIT_OPMODE_GSM}|${EXIT_OPMODE_COMPACTGSM}|${GSM_WITH_EGPRS})
631 __modem_rssi_to_dbm "${asu}"
632 ret=${?}
633 ;;
6c74a64c 634
fbf647ae
MT
635 # UMTS
636 ${EXIT_OPMODE_UMTS}|${EXIT_OPMODE_UMTS_WITH_HSDPA}|${EXIT_OPMODE_UMTS_WITH_HSUPA}|${EXIT_OPMODE_UMTS_WITH_HSDPA_AND_HSUPA})
637 __modem_rscp_to_dbm "${asu}"
638 ret=${?}
639 ;;
640
641 # LTE
642 ${EXIT_OPMODE_LTE})
643 __modem_rsrp_to_dbm "${asu}"
644 ret=${?}
645 ;;
646
647 # unknown
648 *)
649 ret=${EXIT_ERROR}
650 ;;
651 esac
652
653 return ${ret}
6c74a64c
MT
654}
655
1c6a4e30 656modem_get_bit_error_rate() {
6c74a64c
MT
657 local device=${1}
658 assert isset device
659
660 local ber
661 ber=$(__modem_get_signal_quality ${device} ber)
662 assert_check_retval $?
663
664 isset ber || return ${EXIT_ERROR}
665
666 # 99 indicates that the bit error rate could not be detected or
667 # is unknown for some other reason.
668 [ ${ber} -eq 99 ] && return ${EXIT_UNKNOWN}
669
670 print "%d" "${ber}"
671 return ${EXIT_OK}
672}
5cf0edf9
MT
673
674# USDD stuff
675
1c6a4e30 676modem_ussd_send_command() {
5cf0edf9
MT
677 local device="${1}"
678 assert isset device
679
680 local command="${2}"
681 assert isset command
682 shift 2
683
684 local cleartext="false"
685 local timeout="20"
686
687 while [ $# -gt 0 ]; do
688 case "${1}" in
689 --cleartext)
690 cleartext="true"
691 ;;
692 --timeout=*)
693 timeout="$(cli_get_val "${1}")"
694 ;;
695 *)
696 warning "Unrecognized argument: ${1}"
697 ;;
698 esac
699 shift
700 done
701
702 local encoded_command="${command}"
703 if ! enabled cleartext; then
704 encoded_command="$(modem_ussd_encode "${command}")"
705 fi
706
707 log INFO "Sending USSD command '${command}' on ${device}"
708
709 local at_command="AT+CUSD=1,\"${encoded_command}\",${encoding}"
710
711 # Send the AT command and parse the output.
712 modem_chat --answer="nothing" --timeout="${timeout}" \
713 "${device}" "${at_command}" | __modem_ussd_parse_output
714
715 local ret=${PIPESTATUS[1]}
716 return ${ret}
717}
718
1c6a4e30 719__modem_ussd_parse_output() {
5cf0edf9
MT
720 local line
721 while read -r line; do
722 # Find the expected answer.
723 [ "${line:0:7}" = "+CUSD: " ] || continue
724
725 # Strip +CUSD:
726 line="${line:7}"
727
728 local response_type
729 local response
730 local response_encoding
731
732 line="${line//,/ }"
733
734 local section=0 arg
735 while read -r arg; do
736 case "${section}" in
737 0)
738 response_type="${arg}"
739 ;;
740 1)
741 response="${arg}"
742 ;;
743 2)
744 response_encoding="${arg}"
745 ;;
746 *)
747 break
748 ;;
749 esac
750 section=$(( ${section} + 1 ))
751 done <<< "$(args ${line})"
752
753 log DEBUG "USSD response type: ${response_type}"
754 log DEBUG "USSD response encoding: ${response_encoding}"
755 log DEBUG "USSD encoded response: ${response}"
756
757 # If we got anything else than a response (type 0),
758 # we don't know how to handle that.
759 if [ "${response_type}" -ne "0" ]; then
760 return ${EXIT_ERROR}
761 fi
762
763 # Decode the string if needed.
764 case "${response_encoding}" in
765 15)
766 response="$(modem_ussd_decode "${response}")"
767 ;;
768 esac
769 log DEBUG "USSD response: ${response}"
770
771 print "${response}"
772 return ${EXIT_OK}
773 done
774
775 return ${EXIT_ERROR}
776}
777
1c6a4e30 778modem_ussd_encode() {
5cf0edf9
MT
779 local string="${1}"
780 assert isset string
781
782 local output buffer char
783 while read -r char; do
784 char="$(char2bin "${char}")"
785 char="$(string_reverse "${char:1:7}")"
786
787 buffer="${buffer}${char}"
788 done <<< "$(string_split "${string}")"
789
790 local pos=0 len=8
791 while [ ${pos} -lt ${#buffer} ]; do
792 char="$(string_reverse "${buffer:${pos}:${len}}")"
793 pos=$(( ${pos} + ${len} ))
794
795 char="$(bin2hex "${char}")"
796 output="${output}${char}"
797 done
798
799 # Make everything uppercase.
800 output="${output^^}"
801
802 print "${output}"
803}
804
1c6a4e30 805modem_ussd_decode() {
5cf0edf9
MT
806 local string="${1}"
807 assert isset string
808
809 local buffer1 buffer2
810 local output char
811
812 local pos=0 len=2
813 while [ ${pos} -lt ${#string} ]; do
814 char="${string:${pos}:${len}}"
815 pos=$(( ${pos} + ${len} ))
816
817 char="$(hex2bin "${char}")"
818 char="$(string_reverse "${char}")"
819 buffer1="${buffer1}${char}"
820 done
821
822 # Reset pointers.
823 pos=0
824 len=7
825
826 while [ ${pos} -lt ${#buffer1} ]; do
827 char="${buffer1:${pos}:${len}}"
828 pos=$(( ${pos} + ${len} ))
829
830 buffer2="${buffer2}0${char}"
831 done
832 buffer2="${buffer2:1}"
833
834 # Reset pointers again.
835 pos=0
836 len=8
837
838 while [ ${pos} -lt ${#buffer2} ]; do
839 char="${buffer2:${pos}:${len}}"
840 pos=$(( ${pos} + ${len} ))
841
842 char="$(string_reverse "${char}")"
843 char="$(bin2char "${char}")"
844 output="${output}${char}"
845 done
846
847 print "${output}"
848 return ${EXIT_OK}
849}