Skip to content

Instantly share code, notes, and snippets.

@FlorianHeigl
Created December 4, 2021 15:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save FlorianHeigl/684eccc28272de3c484af550566d4889 to your computer and use it in GitHub Desktop.
Save FlorianHeigl/684eccc28272de3c484af550566d4889 to your computer and use it in GitHub Desktop.
zte modemswitch
#!/bin/sh
# source in blogpost comment at
# https://technicalexperiments.wordpress.com/2015/10/30/zte-mf831-for-use-with-openwrt-serial-modem-instead-of-cdc_ether/comment-page-1/
wLog(){
case $debug in
0)
logger "${0##*/}: $1"
;;
esac
return 0
}
now(){
let now=`date +%s`
echo $now
}
cleanup(){
wLog "Removing Lockfile."
progname=$0
proclist=$(echo -en "$(pgrep -s0 -lf ash; pgrep -lf ash)" | sort | uniq -u | awk '{ print $1 }')
for i in $proclist; do
kill $i;
done
[ -e /tmp/modem-reset.tmp ] && rm /tmp/modem-reset.tmp
return 0
}
findttyUSB(){
if [ $# -eq 0 ]; then
r=""
for i in $(find -L /sys/bus/usb/devices/ -maxdepth 2 -name "ttyUSB*"); do
egrep -i "v19d2p0016(.*)in02" $i/../modalias >/dev/null
if [ $? -eq 0 ]; then
r=`echo -en "$r $(echo "/dev/${i##*/}")\r\n"`
fi
done
echo $r
return 0
else
case $1 in
"is_true")
for i in $(find -L /sys/bus/usb/devices/ -maxdepth 2 -name "ttyUSB*"); do
egrep -i "v19d2p0016(.*)in02" $i/../modalias >/dev/null
if [ $? -eq 0 ]; then
echo 0
return 0
fi
done
;;
esac
fi
echo 1
return 0
}
setModeModem(){
if [ $(testRoute device) -eq 0 ]; then
prepareModeModem
if [ $# -ge 1 ]; then
wLog "setModeModem will now try to switch $1 ..."
/usr/bin/curl -d "goformId=USB_MODE_SWITCH&usb_mode=FACTORY" http://$1/goform/goform_set_cmd_process >/dev/null
wLog "setModeModem will now wait for a tty device to come up."
until [ $(findttyUSB is_true) -eq 0 ]; do
sleep 5
done
else
wLog "setModeModem has been invoked without a target ip address. Not switching anything."
return 1
fi
fi
return 0
}
setModeEthernet(){
let timeOut=$(now)+45
modemMode=$(determineMode)
until [ $modemMode = "0016" ]; do
case $modemMode in
"1405")
wLog "Already found ZTE Ethernet device. Not switching. Bye."
return 0
;;
*)
if [ $timeOut -le $(now) ]; then
wLog "setModeEthernet: Softly resetting USB after timeout."
usbSoftResetMF831
fi
sleep 5
modemMode=$(determineMode)
;;
esac
done
if [ $modemMode = "0016" ]; then
for chatfile in mf831-reboot mf831-modeswitch; do
until [ $(waitForUsbDevice 0) -eq 0 ]; do
sleep 5
done
if [ $(determineMode) = "1405" ]; then
wLog "Already found ZTE Ethernet device. Not switching. Bye."
return 0
fi
wLog "A device just came up."
let count=0
until [ $(findttyUSB is_true) -eq 0 ] || [ $count -eq 6 ]; do
sleep 5
let count++
done
wLog "Modem terminals are now present."
for m in $(findttyUSB); do
wLog "Modem terminal found at $m."
pppproc=`pgrep -lf "pppd(.*)$m" | awk '{ print $1 }'`
if [ ${#pppproc} -gt 1 ]; then
kill -9 $pppproc
fi
sleep 2
/usr/sbin/pppd debug $(echo ${m} | egrep -oe "/dev/[[:alnum:]]*") \
115200 modem crtscts asyncmap 0 lock noauth nodetach \
connect "/usr/sbin/chat -v -t 30 -f /etc/scripts/${chatfile}.chat"
sleep 10
done
done
fi
let count=0
until [ $(testRoute device) -eq 0 ] || [ $count -eq 3 ]; do
sleep 1
let count++
done
return 0
}
prepareModeModem(){
lsmod | grep "^option" >/dev/null || /usr/sbin/modprobe option
grep "19d2\ 0016" /sys/bus/usb-serial/drivers/option1/new_id >/dev/null || echo "19d2 0016 ff" | tee /sys/bus/usb-serial/drivers/option1/new_id
return 0
}
createLockFile(){
if [ $# -gt 0 ]; then
let offset=$1
else
let offset=0
fi
let timestamp=$(now)+offset
wLog "Creating lockfile with offset value $offset."
echo $timestamp > /tmp/modem-reset.tmp
return 0
}
determineMode() {
mode=`lsusb -d 19d2: | egrep -oe "19d2\:([[:digit:]]){4}"`
if [ ${#mode} -gt 0 ]; then
id=`echo $mode | cut -d ":" -f2`
else
id="error"
fi
echo $id
return 0
}
testEthernet(){
let count=0
let success=1
until [ $success -eq 0 ]; do
LANGUAGE=POSIX ifconfig -a | egrep -oe "usb[[:digit:]]" >/dev/null
let success=$?
[ $success -gt 0 ] && sleep 3
if [ $count -eq 6 ] && [ $# -eq 1 ]; then
echo $success
return $success
fi
let count++
done
[ $success -eq 0 ] && wLog "Virtual USB Ethernet interface has just been detected."
if [ $# -eq 1 ]; then
echo $success
fi
return $success
}
pingtest(){
ping -c3 -W3 $1 >/dev/null
let result=$?
if [ $# -ge 2 ]; then
echo $result
fi
return $result
}
waitForEthernet(){
wLog "Waiting for USB ethernet device to appear."
let runs=0
let isup=1
until [ $isup -eq 0 ]; do
let isup=$(testEthernet 0)
if [ $runs -eq 6 ]; then
break
else
sleep 5
fi
let runs++
done
if [ $# -eq 1 ]; then
echo $isup
fi
return $isup
}
handleModemErrorConditions(){
wLog "Error condition handler invoked."
if [ $# -eq 1 ]; then
let count=$1
else
let count=1
fi
until [ $count -eq 101 ]; do
case $count in
1)
wLog "Error condition handler tries ifupdown."
ifupdown usb0
waitForEthernet
;;
2)
wLog "Error condition handler tries modreload and reset."
unloadResetLoad
waitForEthernet
;;
3)
wLog "Error condition handler tries modeswitching."
if [ $(ifconfig usb0 2&>1 /dev/null; echo $?) -eq 0 ]; then
if [ $(pingtest $modemip 0) -eq 0 ]; then
resetModem
waitForEthernet
fi
fi
;;
4)
wLog "Error condition handler tries usb soft reset."
usbSoftResetMF831
waitForEthernet
;;
5)
wLog "Error condition handler tries usb hard reset."
usbReset
usbPowerCycle
waitForUsbDevice
;;
100)
wLog "Error condition handler gives up and triggers a reboot."
if [ $(testRoute device 1) -gt 0 ] || [ $(testRoute default 1) -gt 0 ] || [ $(pingtest $modemip 0) -gt 0 ]; then
wLog "Now going bananas... reboot."
reboot -f
fi
;;
*)
sleep 1
;;
esac
if [ $(testRoute device 1) -eq 0 ] && [ $(testRoute default 1) -eq 0 ]; then
if [ $(pingtest $modemip 0) -eq 0 ]; then
if [ $(pingtest $externalip 0) -eq 0 ]; then
wLog "Recovery seems to be successful. Device and routes are now up."
echo 0
return 0
else
wLog "Recovery seemed successful, but internet test failed."
fi
fi
fi
let count++
done
echo 1
return 1
}
testRoute(){
case $1 in
"device")
invert="-v"
text="device"
text2="Device"
;;
default)
invert=""
text="default"
text2="Default"
;;
esac
case $2 in
[0-10])
let runs=$2
;;
*)
let runs=1
esac
let re=1
let count=0
wLog "Looking for existing default route."
until [ $re -eq 0 ]; do
ip r s dev usb0 | grep $invert "default" >/dev/null
let re=$?
if [ $re -eq 0 ]; then
wLog "$1 route ok."
break
else
wLog "$1 route does not exist on try #$count."
fi
if [ $count -eq 2 ]; then
break
fi
sleep 3
let count++
done
echo $re
return $re
}
testInternet(){
let isup=1
if [ $(testRoute default) -eq 0 ]; then
let count=1
until [ $isup -eq 0 ]; do
wLog "Now pinging internet hosts - try #$count."
let isup=$(pingtest $externalip 0)
if [ $count -eq 2 ];then
break
fi
if [ $isup -eq 0 ]; then
wLog "Ping test succeeded on try #$count."
break
else
sleep 10
fi
let count++
done
fi
echo $isup
return $isup
}
usbSoftResetMF831(){
wLog "Performing usb reset on device."
for i in $(find -L /sys/bus/usb/devices/usb1 -maxdepth 3 -name "modalias"); do
j=`egrep -ioe "v19d2p1405|v19d2p0016" $i >/dev/null && echo ${i%%/*modalias}authorized`
echo 0 | tee ${j}
prepareModeModem
echo 1 | tee ${j}
sleep 20
done
return 0
}
resetModem(){
setModeModem $modemip
setModeEthernet
return 0
}
usbReset(){
usbreset 19d2:1405
usbreset 1a40:0101
# for xhci in /sys/bus/usb/drivers/?hci_hcd ; do
for xhci in /sys/bus/platform/drivers/?hci-platform ; do
if ! cd $xhci ; then
echo Weird error. Failed to change directory to $xhci
exit 1
fi
echo Resetting devices from $xhci...
for i in ????:??:??.? ; do
echo -n "$i" > unbind
echo -n "$i" > bind
done
done
}
usbPowerCycle(){
pin=""
router=grep machine /proc/cpuinfo | egrep -oe "TL(.+)([[:alpha:]]+)([[:digit:]]{3,4})([[:alpha:]]{0,2})"
case $router in
"TL-MR3420")
pin=6
;;
"TL-WR1043ND")
# pin=22
pin=""
;;
esac
if [ ${#pin} -gt 0 ]; then
echo 0 > /sys/class/gpio/gpio${pin}/value
sleep 10
echo 1 > /sys/class/gpio/gpio${pin}/value
fi
return 0
}
ifupdown(){
wLog "Now trying to toggle virtual ethernet interface status. This will initiate a dhcp request, too."
if [ $(lsmod | egrep "rndis_host|cdc_ether" >/dev/null && echo 0 || echo 1) -eq 0 ]; then
netif=$(uci show network | grep $1 | egrep -oe "\.(.*)\.ifname=" | cut -d "." -f2)
ifdown $netif
udhcpcproc=`ps w | grep udhcpc | grep usb0 | awk '{ print $1 }'`
if [ ${#udhcpcproc} -gt 1 ]; then
kill -9 $udhcpcproc
fi
ifup $netif
else
unloadResetLoad
fi
return 0
}
unloadResetLoad(){
wLog "removing modules, usbresetting modem, reloading"
rmmod rndis_host
rmmod cdc_ether
usbSoftResetMF831
sleep 1
modprobe cdc_ether
modprobe rndis_host
return 0
}
waitForUsbDevice(){
wLog "Waiting for USB device to initialize"
let runs=0
let devhere=$(lsusb -d 19d2: | grep "0016\|1225\|1404\|1405" >/dev/null && echo 0 || echo 1)
until [ $devhere -eq 0 ]; do
let devhere=$(lsusb -d 19d2: | grep "0016\|1225\|1404\|1405" >/dev/null && echo 0 || echo 1)
if [ $devhere -eq 1 ] && [ $runs -eq 4 ]; then
wLog "USB device did not come up within 60 seconds after reset."
break
else
[ $runs -gt 0 ] && sleep 15 || sleep 3
fi
let runs++
done
if [ $# -eq 1 ]; then
echo $devhere
fi
return $devhere
}
recoveryProcedure(){
[ $# -eq 1 ] && let force=0 || let force=1
case $(determineMode) in
"0016")
wLog "Modem in serial mode detected. Modeswitching to ethernet mode by sending an AT command sequence."
setModeEthernet
;;
"0076")
wLog "Modem in real download mode detected. Resetting USB Power."
usbPowerCycle
;;
"1405")
wLog "Modem in ethernet mode detected."
if [ $force -eq 0 ] ; then
wLog "recovery forced: soft reset of modem forced."
resetModem
return $?
fi
wLog "Recovery is trying to resolve error conditions."
handleModemErrorConditions
return $?
;;
"error")
wLog "Modem not present or other problems...preparing for reboot (120s)."
sleep 120
if [ $(determineMode) = "error" ]; then
reboot -f
fi
;;
esac
return 0
}
main(){
[ $# -eq 1 ] && let force=0 || let force=1
if [ ! -e /tmp/modem-reset.tmp ] || [ $force -eq 0 ]; then
createLockFile 0
let count=0
until [ $(testInternet) -eq 0 ]; do
case $count in
[0-2])
wLog "Entering recovery procedure."
recoveryProcedure $force
;;
3)
wLog "The recovery procedure has timed out."
return 1
;;
esac
let count++
done
wLog "Internet test succeeded on turn: $count."
else
wLog "Modem reset script exiting due to existing lockfile"
exit 1
fi
return 0
}
modemip='192.168.0.1'
externalip='8.8.4.4'
let debug=1
let force=1
if [ -e /tmp/modem-reset.tmp ]; then
let ageOfLockFileInSec=$(now)-`date -r /tmp/modem-reset.tmp +"%s"`
if [ $ageOfLockFileInSec -gt 300 ]; then
wLog "Stale lockfile detected. Removing it."
cleanup 0
fi
fi
if [ $# -ge 1 ]; then
sortedparams=`printf "%s\n" $@ | sort -t " " -k2 | tr "\n" " "`
for i in $sortedparams; do
case $i in
"--help")
echo -en "usage:\r\n"
echo -en "\t$0 --help \r\n\t\t show this help message\r\n"
echo -en "\t$0 --debug \r\n\t\t force debug log\r\n"
echo -en "\t$0 --force \r\n\t\t force modem reset\r\n"
echo -en "\t$0 modem \r\n\t\t manually set serial modem mode on device\r\n"
echo -en "\t$0 ethernet \r\n\t\t manually set ethernet mode \(tethering\) on device\r\n"
echo -en "\t$0 status \r\n\t\t show current device state\r\n"
;;
"--debug")
let debug=0
wLog "debug mode forced."
;;
"--force")
let debug=0
wLog "Forcing reset."
main force
;;
"modem")
wLog "manually setting modem Mode on IP $modemip."
setModeModem $modemip
;;
"ethernet")
wLog "manually resetting to ethernet mode."
setModeEthernet
;;
"usb")
usbReset
;;
"status")
mode=$(determineMode)
case $mode in
"0016")
message="Modem is in serial mode ($mode)."
;;
"0076")
message="Modem is in download mode ($mode)."
;;
"1405")
message="Modem is in ethernet mode ($mode)."
;;
"1404")
message="Modem is in adb+cdc mode ($mode)."
;;
*)
message="Modem is in an unknown mode ($mode)."
;;
esac
echo $message
wLog $message
;;
esac
done
else
let debug=0
main
cleanup
fi
exit 0
Corresponding chat-files:
mf831-modeswitch.chat (+ZSNT:will reset to carrier mode autoselection (2g/3g/4g); +ZBANDI: will reset to band autoselection mode. You could try to add +ZOPRT=5 to reset to “always on” mode, but that didn’t work for me.)
ABORT 'BUSY'
ABORT 'NO CARRIER'
TIMEOUT 30
ECHO ON
'' AT&F
OK ATE1
OK AT+ZSNT=0,0,0
OK AT+ZBANDI=0
OK AT+ZCDRUN=9
OK AT+ZCDRUN=F
mf831-reboot.chat:
ABORT 'BUSY'
ABORT 'NO CARRIER'
TIMEOUT 30
ECHO ON
'' AT&F
OK ATE1
OK AT+CFUN=1,1
@FlorianHeigl
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment