Public domain sources for NDIS mode dialer for e173 modem

425 views
Skip to first unread message

Wojciech M. Zabolotny

unread,
Sep 21, 2011, 10:23:04 AM9/21/11
to
Archive-name: e173-dialer
Submitted-by: Wojciech M. Zabolotny wzab<at>ise.pw.edu.pl

This is my first, quick and dirty dialer for e173-u2 in NDIS mode.
The archive contains:
1. 10-huawei-e173.rules , which should be located in /etc/udev/rules.d
2. wwan0_off and wwan0_on , which should be placed in /usr/local/bin
3. wwan0_on_off, which should be placed in /etc/sudoers.d
4. e173.glade, which should be placed in the same directory in
which you'll execute e173.py
5. e173.py itself

To avoid running the Python script with GUI from the root account,
I have splited the functionalities between the e173.py script
and two shell scripts (wwan0_on and wwan0_off), which may be run
with sudo by all members of the "dialout" group (as defined
in wwan0_on_off).
I've noticed, that sometimes different interfaces od e173 are
asigned to ttyUSB* nodes in different order. This is solved
by use of the 10-huawei-e173.rules.
The dialer uses four different threads - one for GUI, one
for sending commands to e173, one for receiving responses,
and the last one for monitoring of the pcui port.
Handling of modem responses should be improved - the thread
should detect not only "OK", but also "ERROR" and
probably "COMMAND NOT SUPPORT". Feel free to improve it ;-).

All the sources are published as PUBLIC DOMAIN and
WITHOUT ANY WARRANTY.
Use it only on your own risk!
Developing this dialer, I've used information found in many
websites, eg.:
http://jdtech.pl/2011/09/aero2-zmiany-na-stronach-www.html#IDComment193304288
(in Polish)
http://www.draisberghof.de/usb_modeswitch/bb
The udev rules are based on the original rules provided in the driver
http://www.t-mobile.de/downloads/neu/linux_driver_4.19.00.00.zip

#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.11).
# To extract the files from this archive, save it to some FILE, remove
# everything before the `#!/bin/sh' line above, then type `sh FILE'.
#
lock_dir=_sh09935
# Made on 2011-09-21 16:18 CEST by <wzab@wzlaphp>.
# Source directory was `/home/wzab/biezace/aero2_starter/pub'.
#
# Existing files will *not* be overwritten, unless `-c' is specified.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 497 -rw-r--r-- 10-huawei-e173.rules
# 2922 -rw-r--r-- e173.glade
# 6419 -rwxr--r-- e173.py
# 65 -rwxr-xr-x wwan0_off
# 82 -rwxr-xr-x wwan0_on
# 99 -r--r--r-- wwan0_on_off
#
MD5SUM=${MD5SUM-md5sum}
f=`${MD5SUM} --version | egrep '^md5sum .*(core|text)utils'`
test -n "${f}" && md5check=true || md5check=false
${md5check} || \
echo 'Note: not verifying md5sums. Consider installing GNU coreutils.'
if test "X$1" = "X-c"
then keep_file=''
else keep_file=true
fi
echo=echo
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=
locale_dir=
set_echo=false

for dir in $PATH
do
if test -f $dir/gettext \
&& ($dir/gettext --version >/dev/null 2>&1)
then
case `$dir/gettext --version 2>&1 | sed 1q` in
*GNU*) gettext_dir=$dir
set_echo=true
break ;;
esac
fi
done

if ${set_echo}
then
set_echo=false
for dir in $PATH
do
if test -f $dir/shar \
&& ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
then
locale_dir=`$dir/shar --print-text-domain-dir`
set_echo=true
break
fi
done

if ${set_echo}
then
TEXTDOMAINDIR=$locale_dir
export TEXTDOMAINDIR
TEXTDOMAIN=sharutils
export TEXTDOMAIN
echo="$gettext_dir/gettext -s"
fi
fi
IFS="$save_IFS"
if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null
then if (echo -n test; echo 1,2,3) | grep n >/dev/null
then shar_n= shar_c='
'
else shar_n=-n shar_c= ; fi
else shar_n= shar_c='\c' ; fi
f=shar-touch.$$
st1=200112312359.59
st2=123123592001.59
st2tr=123123592001.5 # old SysV 14-char limit
st3=1231235901

if touch -am -t ${st1} ${f} >/dev/null 2>&1 && \
test ! -f ${st1} && test -f ${f}; then
shar_touch='touch -am -t $1$2$3$4$5$6.$7 "$8"'

elif touch -am ${st2} ${f} >/dev/null 2>&1 && \
test ! -f ${st2} && test ! -f ${st2tr} && test -f ${f}; then
shar_touch='touch -am $3$4$5$6$1$2.$7 "$8"'

elif touch -am ${st3} ${f} >/dev/null 2>&1 && \
test ! -f ${st3} && test -f ${f}; then
shar_touch='touch -am $3$4$5$6$2 "$8"'

else
shar_touch=:
echo
${echo} 'WARNING: not restoring timestamps. Consider getting and
installing GNU `touch'\'', distributed in GNU coreutils...'
echo
fi
rm -f ${st1} ${st2} ${st2tr} ${st3} ${f}
#
if test ! -d ${lock_dir} ; then :
else ${echo} "lock directory ${lock_dir} exists"
exit 1
fi
if mkdir ${lock_dir}
then ${echo} "x - created lock directory ${lock_dir}."
else ${echo} "x - failed to create lock directory ${lock_dir}."
exit 1
fi
# ============= 10-huawei-e173.rules ==============
if test -n "${keep_file}" && test -f '10-huawei-e173.rules'
then
${echo} "x - SKIPPING 10-huawei-e173.rules (file already exists)"
else
${echo} "x - extracting 10-huawei-e173.rules (text)"
sed 's/^X//' << 'SHAR_EOF' > '10-huawei-e173.rules' &&
SUBSYSTEMS=="usb", ATTRS{modalias}=="usb:v12D1p1436*", KERNEL=="ttyUSB*", ATTRS{bInterfaceNumber}=="00", ATTRS{bInterfaceProtocol}=="ff", SYMLINK="ttyUSB_utps_modem"
SUBSYSTEMS=="usb", ATTRS{modalias}=="usb:v12D1p1436*", KERNEL=="ttyUSB*", ATTRS{bInterfaceNumber}=="03", ATTRS{bInterfaceProtocol}=="ff", SYMLINK="ttyUSB_utps_diag"
SUBSYSTEMS=="usb", ATTRS{modalias}=="usb:v12D1p1436*", KERNEL=="ttyUSB*", ATTRS{bInterfaceNumber}=="04", ATTRS{bInterfaceProtocol}=="ff", SYMLINK="ttyUSB_utps_pcui"
SHAR_EOF
(set 20 11 09 21 15 52 26 '10-huawei-e173.rules'
eval "${shar_touch}") && \
chmod 0644 '10-huawei-e173.rules'
if test $? -ne 0
then ${echo} "restore of 10-huawei-e173.rules failed"
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} '10-huawei-e173.rules': 'MD5 check failed'
) << \SHAR_EOF
c13ec668ef0519d1f20b22e2e3b4fa39 10-huawei-e173.rules
SHAR_EOF
else
test `LC_ALL=C wc -c < '10-huawei-e173.rules'` -ne 497 && \
${echo} "restoration warning: size of '10-huawei-e173.rules' is not 497"
fi
fi
# ============= e173.glade ==============
if test -n "${keep_file}" && test -f 'e173.glade'
then
${echo} "x - SKIPPING e173.glade (file already exists)"
else
${echo} "x - extracting e173.glade (text)"
sed 's/^X//' << 'SHAR_EOF' > 'e173.glade' &&
<?xml version="1.0" encoding="UTF-8"?>
<interface>
X <requires lib="gtk+" version="2.20"/>
X <object class="GtkWindow" id="mainwin">
X <property name="width_request">0</property>
X <property name="height_request">0</property>
X <property name="can_focus">False</property>
X <child>
X <object class="GtkVBox" id="box2">
X <property name="width_request">0</property>
X <property name="height_request">0</property>
X <property name="visible">True</property>
X <property name="can_focus">False</property>
X <child>
X <object class="GtkProgressBar" id="Strength">
X <property name="visible">True</property>
X <property name="can_focus">False</property>
X <property name="fraction">0.0</property>
X <property name="pulse_step">0.01</property>
X </object>
X <packing>
X <property name="expand">False</property>
X <property name="fill">True</property>
X <property name="position">0</property>
X </packing>
X </child>
X <child>
X <object class="GtkHButtonBox" id="buttonbox1">
X <property name="visible">True</property>
X <property name="can_focus">False</property>
X <property name="layout_style">start</property>
X <child>
X <object class="GtkButton" id="Connect">
X <property name="label" translatable="yes">Connect</property>
X <property name="visible">True</property>
X <property name="can_focus">True</property>
X <property name="receives_default">True</property>
X <property name="use_action_appearance">False</property>
X </object>
X <packing>
X <property name="expand">False</property>
X <property name="fill">True</property>
X <property name="position">0</property>
X </packing>
X </child>
X <child>
X <object class="GtkButton" id="Disconnect">
X <property name="label" translatable="yes">Disconnect</property>
X <property name="visible">True</property>
X <property name="can_focus">True</property>
X <property name="receives_default">True</property>
X <property name="use_action_appearance">False</property>
X </object>
X <packing>
X <property name="expand">False</property>
X <property name="fill">True</property>
X <property name="position">2</property>
X </packing>
X </child>
X </object>
X <packing>
X <property name="expand">False</property>
X <property name="fill">True</property>
X <property name="position">1</property>
X </packing>
X </child>
X </object>
X </child>
X </object>
</interface>
SHAR_EOF
(set 20 11 09 21 16 08 14 'e173.glade'
eval "${shar_touch}") && \
chmod 0644 'e173.glade'
if test $? -ne 0
then ${echo} "restore of e173.glade failed"
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'e173.glade': 'MD5 check failed'
) << \SHAR_EOF
090364ba59ea48422242c2f7dd3b9cbd e173.glade
SHAR_EOF
else
test `LC_ALL=C wc -c < 'e173.glade'` -ne 2922 && \
${echo} "restoration warning: size of 'e173.glade' is not 2922"
fi
fi
# ============= e173.py ==============
if test -n "${keep_file}" && test -f 'e173.py'
then
${echo} "x - SKIPPING e173.py (file already exists)"
else
${echo} "x - extracting e173.py (text)"
sed 's/^X//' << 'SHAR_EOF' > 'e173.py' &&
#!/usr/bin/python
import os
import thread
import threading
import pygtk
pygtk.require("2.0")
import gtk
gtk.gdk.threads_init()
X
# This scripts allows you to control the NDIS connetion
# via e173-u2 modem.
# It also provides you with simple GUI showing the strength
# of the signal
X
#What is the maximum RSSI level reported by e173?
#I assume 30. Correct it, if you have more accurate information
rssi_max=30.0
class msg_fifo():
X def __init__(self):
X self.lock=threading.Condition()
X self.ready=threading.Condition()
X self.queue=[]
X def append(self,data):
X self.lock.acquire()
X self.queue.append(data)
X self.lock.notifyAll()
X self.lock.release()
X def pop(self,tmout):
X self.lock.acquire()
X if len(self.queue) == 0:
X self.lock.wait(tmout)
X if len(self.queue) == 0:
X self.lock.release()
X return None
X res=self.queue.pop(0)
X self.lock.release()
X return res
X def ack(self):
X self.lock.acquire()
X if len(self.queue)==0:
X self.ready.acquire()
X self.ready.notifyAll()
X self.ready.release()
X self.lock.release()
X def wait_ready(self):
X self.ready.acquire()
X self.ready.wait()
X self.ready.release()
X
responses = msg_fifo()
commands = msg_fifo()
X
class gui_class:
X def do_Connect(self):
X if not gui.required_connection:
X return
X commands.append("ATZ")
X commands.append("AT^NDISDUP=1,1,\"my_apn_name\"")
X commands.wait_ready()
X os.system("/usr/bin/sudo /usr/local/bin/wwan0_off")
X os.system("/usr/bin/sudo /usr/local/bin/wwan0_on")
X print "Connect"+"\n"
X return
X def cb_Connect(self,o):
X gui.required_connection = True
X self.do_Connect()
X return
X def cb_Disconnect(self,o):
X gui.required_connection = False
X self.do_Disconnect()
X return
X def do_Disconnect(self):
X commands.append("AT^NDISDUP=1,0")
X commands.wait_ready()
X os.system("/usr/bin/sudo /usr/local/bin/wwan0_off")
X print "Disconnect"+"\n"
X return
X def cb_Destroy(self,o):
X print "Destroy the window!\n"
X gui.exit_program=True
X gtk.main_quit()
X return
X def __init__(self):
X #Create GUI
X builder = gtk.Builder()
X builder.add_from_file("e173.glade")
X builder.connect_signals({})
X builder.get_object("Connect").connect("clicked",self.cb_Connect)
X builder.get_object("Disconnect").connect("clicked",self.cb_Disconnect)
X self.mainwin = builder.get_object("mainwin")
X self.mainwin.connect("destroy",self.cb_Destroy)
X self.strength = builder.get_object("Strength")
X self.mainwin.show()
X self.required_connection = False
X self.exit_program = False
X
resp_lock=thread.allocate_lock()
X
# I have noticed, that sometimes the me173-u2 modem resets
# spontaneously, and ttyUSB... ports disappear from the system
# Therefore the threads used to send command, receive responses
# and to monitor connection are run in a loop with exception
# handling
X
def sending(gui,out_fifo,in_fifo):
X modem="/dev/ttyUSB_utps_modem"
X while not gui.exit_program:
X try:
X out_file=open(modem,"w")
X except IOError:
X print "Can't open modem for writing\n"
X continue
X try:
X cmd=out_fifo.pop(None)
X out_file.write(cmd+"\r\n")
X out_file.flush()
X while not gui.exit_program:
X l = responses.pop(12)
X print l
X if l=="OK":
X out_fifo.ack()
X break
X except IOError:
X print "Modem write port has disappeared...\n"
X continue
X try:
X out_file.close()
X except IOError:
X pass
X
def receiving(gui,out_fifo):
X modem="/dev/ttyUSB_utps_modem"
X while not gui.exit_program:
X try:
X in_file=open(modem,"r")
X except IOError:
X print "No modem port\n"
X continue
X try:
X line=""
X while not gui.exit_program:
X c=in_file.read(1)
X if c=="\n" or c=="\r":
X out_fifo.append(line)
X line = ""
X else:
X line += c
X except IOError:
X print "Modem port disappeared\n"
X try:
X in_file.close()
X except IOError:
X pass
X
def monitoring(gui):
X monitor="/dev/ttyUSB_utps_pcui"
X while not gui.exit_program:
X try:
X in_file=open(monitor,"r")
X except IOError:
X #Service the opening error
X print "No PCUI port\n"
X continue
X try:
X line=""
X while not gui.exit_program:
X c=in_file.read(1)
X #print c+":"+hex(ord(c))
X if c=="\n" or c=="\r":
X print line
X #Now analyze the received line
X if line[0:6]=="^RSSI:":
X #Set the signal strength to the appropriate value
X print line+"\n"
X gtk.gdk.threads_enter()
X sig_level=int(line[6:])
X if sig_level==99:
X sig_level=0
X sig_level = sig_level/rssi_max
X if sig_level > 1:
X sig_level=1
X gui.strength.set_fraction(sig_level)
X gtk.gdk.threads_leave()
X elif line[0:7]=="^SRVST:":
X print line+"\n"
X #Set the connection status
X if line[7]=="1":
X gtk.gdk.threads_enter()
X gui.strength.set_text("Disconnected")
X gtk.gdk.threads_leave()
X #Disconnected, update the indicator and trigger reconnection
X gui.do_Connect()
X elif line[7]=="2":
X #Connected, update the indicator
X gtk.gdk.threads_enter()
X gui.strength.set_text("Connected")
X gtk.gdk.threads_leave()
X status=1
X line = ""
X else:
X line += c
X except IOError:
X print "PCUI port disappeared :-( \n"
X try:
X in_file.close()
X except IOError:
X continue
X continue
X
#Initialization of different threads...
print "gui\n"
gui = gui_class()
# Start the sending thread
print "sending\n"
snd_thread = thread.start_new_thread(sending,(gui,commands,responses))
# Start the receiving thread
print "receiving\n"
rcv_thread = thread.start_new_thread(receiving,(gui,responses,))
# Start the monitoring thread
print "monitoring\n"
mon_thread = thread.start_new_thread(monitoring,(gui,))
print "main loop\n"
gtk.gdk.threads_enter()
gtk.main()
gtk.gdk.threads_leave()
X
X
SHAR_EOF
(set 20 11 09 21 16 17 59 'e173.py'
eval "${shar_touch}") && \
chmod 0744 'e173.py'
if test $? -ne 0
then ${echo} "restore of e173.py failed"
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'e173.py': 'MD5 check failed'
) << \SHAR_EOF
ae82c030176912bb4fb03626a30f2310 e173.py
SHAR_EOF
else
test `LC_ALL=C wc -c < 'e173.py'` -ne 6419 && \
${echo} "restoration warning: size of 'e173.py' is not 6419"
fi
fi
# ============= wwan0_off ==============
if test -n "${keep_file}" && test -f 'wwan0_off'
then
${echo} "x - SKIPPING wwan0_off (file already exists)"
else
${echo} "x - extracting wwan0_off (text)"
sed 's/^X//' << 'SHAR_EOF' > 'wwan0_off' &&
#!/bin/bash
/sbin/ifconfig wwan0 down
/usr/bin/killall dhclient
X
SHAR_EOF
(set 20 11 09 20 23 01 00 'wwan0_off'
eval "${shar_touch}") && \
chmod 0755 'wwan0_off'
if test $? -ne 0
then ${echo} "restore of wwan0_off failed"
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'wwan0_off': 'MD5 check failed'
) << \SHAR_EOF
056fb4e2e0ba608a6adf34d1b5b9a0b3 wwan0_off
SHAR_EOF
else
test `LC_ALL=C wc -c < 'wwan0_off'` -ne 65 && \
${echo} "restoration warning: size of 'wwan0_off' is not 65"
fi
fi
# ============= wwan0_on ==============
if test -n "${keep_file}" && test -f 'wwan0_on'
then
${echo} "x - SKIPPING wwan0_on (file already exists)"
else
${echo} "x - extracting wwan0_on (text)"
sed 's/^X//' << 'SHAR_EOF' > 'wwan0_on' &&
#!/bin/bash
/sbin/ifconfig wwan0 hw ether 00:01:02:03:04:05
/sbin/dhclient wwan0
X
SHAR_EOF
(set 20 11 09 20 23 02 00 'wwan0_on'
eval "${shar_touch}") && \
chmod 0755 'wwan0_on'
if test $? -ne 0
then ${echo} "restore of wwan0_on failed"
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'wwan0_on': 'MD5 check failed'
) << \SHAR_EOF
e8ea6cd60f5215eaa0d26eb21205003e wwan0_on
SHAR_EOF
else
test `LC_ALL=C wc -c < 'wwan0_on'` -ne 82 && \
${echo} "restoration warning: size of 'wwan0_on' is not 82"
fi
fi
# ============= wwan0_on_off ==============
if test -n "${keep_file}" && test -f 'wwan0_on_off'
then
${echo} "x - SKIPPING wwan0_on_off (file already exists)"
else
${echo} "x - extracting wwan0_on_off (text)"
sed 's/^X//' << 'SHAR_EOF' > 'wwan0_on_off' &&
%dialout ALL = NOPASSWD: /usr/local/bin/wwan0_on
%dialout ALL = NOPASSWD: /usr/local/bin/wwan0_off
SHAR_EOF
(set 20 11 09 20 23 10 00 'wwan0_on_off'
eval "${shar_touch}") && \
chmod 0444 'wwan0_on_off'
if test $? -ne 0
then ${echo} "restore of wwan0_on_off failed"
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'wwan0_on_off': 'MD5 check failed'
) << \SHAR_EOF
440b921dd51b85f660c7ed2748a2bdc5 wwan0_on_off
SHAR_EOF
else
test `LC_ALL=C wc -c < 'wwan0_on_off'` -ne 99 && \
${echo} "restoration warning: size of 'wwan0_on_off' is not 99"
fi
fi
if rm -fr ${lock_dir}
then ${echo} "x - removed lock directory ${lock_dir}."
else ${echo} "x - failed to remove lock directory ${lock_dir}."
exit 1
fi
exit 0


wzab

unread,
Sep 21, 2011, 10:55:58 AM9/21/11
to
One important functionality, which is lacking in the e173.py is
locking of the modem port.
If someone knows how to do it reliably in a situation when our port is
accessible via symlink, please let me know.

wzab

unread,
Sep 21, 2011, 1:34:58 PM9/21/11
to
I've stated, that in some systems it is necessary to switch on
reporting on the ttyUSB_utps_pcui port.
To achieve that, one should send "AT^CURC=1" command to the
ttyUSB_utps_modem port.

Therefore the code:
[...]
def sending(gui,out_fifo,in_fifo):
modem="/dev/ttyUSB_utps_modem"
while not gui.exit_program:
try:
out_file=open(modem,"w")
except IOError:
print "Can't open modem for writing\n"
continue
try:
cmd=out_fifo.pop(None)
out_file.write(cmd+"\r\n")
out_file.flush()
[...]

Should be modified to:

[...]
def sending(gui,out_fifo,in_fifo):
modem="/dev/ttyUSB_utps_modem"
while not gui.exit_program:
try:
out_file=open(modem,"w")
out_file.write("AT^CURC=1\r\n")
out_file.flush()
except IOError:
print "Can't open modem for writing\n"
continue
try:
cmd=out_fifo.pop(None)
out_file.write(cmd+"\r\n")
out_file.flush()
[...]

wzab

unread,
Sep 21, 2011, 1:04:20 PM9/21/11
to

I don't know why, but in one of my systems the udev rules with SYMLINK
do not work, and I had to replace SYMLINK with NAME, as below:

SUBSYSTEMS=="usb", ATTRS{modalias}=="usb:v12D1p1436*",
KERNEL=="ttyUSB*", ATTRS{bInterfaceNumber}=="00",

ATTRS{bInterfaceProtocol}=="ff", NAME="ttyUSB_utps_modem"


SUBSYSTEMS=="usb", ATTRS{modalias}=="usb:v12D1p1436*",
KERNEL=="ttyUSB*", ATTRS{bInterfaceNumber}=="03",

ATTRS{bInterfaceProtocol}=="ff", NAME="ttyUSB_utps_diag"


SUBSYSTEMS=="usb", ATTRS{modalias}=="usb:v12D1p1436*",
KERNEL=="ttyUSB*", ATTRS{bInterfaceNumber}=="04",

ATTRS{bInterfaceProtocol}=="ff", NAME="ttyUSB_utps_pcui"

In another system (also Debian/testing) the version with "NAME" works,
but generates warnings, that
name provided by the kernel differs from the name in rule, and
suggests using SYMLINK...

Reply all
Reply to author
Forward
0 new messages