Practical daily usage of smsd+set of USB-modems (19 items) shows, that /dev/ttyUSB1 can point to different modem after this modem has been reinserted or inserted into another USB hole at motherboard (usb-hub). Also, 2-port USB-device MODEM_A can occupy /devttyUSB3 and /dev/ttyUSB5 instead of /dev/ttyUSB0+/dev/ttyUSB1.
As a result, we have strong ravel in smsd.conf. Also, keep in mind, that Huawei 3G USB modems have 1 hardware port but 2 software (/dev/ttyUSB) ports.
The only reliable way to uniquely define modem in OS - is its IMEI. Other ways (e.g. udev+ATTRS{serial}) are not reliable and depend on capability of modem to show its serial number. We will create another virtual device with its personal number, a /dev/modemx (which actually is a symlink to /dev/ttyUSBy , interested for us) and set this /dev/modemx as a "device" parameter in section [GSMx] in smsd.conf . List of "IMEI-to-logical name" relations is stored in /etc/udev/rules.d/IDS.lst in following format:
Here is sample of how to link modem to its IMEI and define obtained device in smsd.conf permanently.
We use udev mechanism to catch event of device's insertion/removal:
/etc/udev/rules.d/50.modems.rules:
KERNEL=="ttyUSB*" ACTION=="remove" , RUN+="/etc/udev/rules.d/eventer.sh remove"
KERNEL=="ttyUSB*" ACTION=="add" , RUN+="/etc/udev/rules.d/eventer.sh add"
KERNEL=="ttyUSB*" ACTION=="add" , RUN+="/etc/udev/rules.d/eventer.sh add"
'smsdconf' Syntax Highlight powered by GeSHi
As we see, during insertion|removal event, OS calls /etc/udev/rules.d/eventer.sh with parameters "add" or "remove". Of couse, this parameter can be replaced with $ACTION environment variable, but let it stay like this. Except $ACTION, udev sets many env vars during insertion|removal, which provide many info about connected device, and eventer.sh user some of them.
Here is eventer.sh , who logs into /etc/udev/rules.d/eventer.log for better debugging. Keep in mind, that udev launches 2 eventer.sh according to number of /dev/ttyUSB's appeared in OS during insertion (for Huawei modem):
#!/bin/bash
dt=`date`
echo $dt Device $DEVNAME, Maker is $ID_VENDOR_FROM_DATABASE was $1 >> /etc/udev/rules.d/eventer.log #simple example of using env vars, pointed to device, provided by udev
if [ "$1" == "add" -a "$SUBSYSTEM" == "tty" ] # if exactly modem has been inserted , not usb-drive etc. To find it out , we take 2 env vars, which have been set just by udev engine. You can take "$ACTION" var instead of $1
then
iam=`whoami`
echo "We do it all under user $iam" >> /etc/udev/rules.d/eventer.log
echo Modem insertion detected >> /etc/udev/rules.d/eventer.log
echo Finding out IMEI for $DEVNAME >> /etc/udev/rules.d/eventer.log
#debug chmod 777 $DEVNAME #you may want to do it or not, it's optional
echo ---Calling "/etc/udev/rules.d/poll_modem.py $DEVNAME | grep IMEI | cut -d " " -f 2 | tr -d \\r\\n" >> /etc/udev/rules.d/eventer.log #here we call special Python script who communicates to modem and asks for IMEI via AT-coomands (listed below)
imei=`/etc/udev/rules.d/poll_modem.py $DEVNAME | grep IMEI | cut -d " " -f 2 | tr -d "\r"`
# is modem didn't answer instantly - we keep trying to poll it
if [ -z $imei ]
then
echo "---IMEI was not found, retry 1..." >> /etc/udev/rules.d/eventer.log
imei=`/etc/udev/rules.d/poll_modem.py $DEVNAME | grep IMEI | cut -d " " -f 2 | tr -d "\r"`
fi
if [ -z $imei ]
then
echo "---IMEI was not found, retry 2..." >> /etc/udev/rules.d/eventer.log
imei=`/etc/udev/rules.d/poll_modem.py $DEVNAME | grep IMEI | cut -d " " -f 2 | tr -d "\r"`
fi
if [ -z $imei ]
then
echo "---IMEI was not found, retry 3..." >> /etc/udev/rules.d/eventer.log
imei=`/etc/udev/rules.d/poll_modem.py $DEVNAME | grep IMEI | cut -d " " -f 2 | tr -d "\r"`
fi
if [ -z $imei ]
then
dt=`date`
echo "$dt +++ERROR+++ IMEI was not found, i give up. Please reinsert modem $ID_VENDOR_FROM_DATABASE. Exit." >> /etc/udev/rules.d/eventer.log
exit
fi
echo "Found IMEI $imei for $DEVNAME">> /etc/udev/rules.d/eventer.log
# as said before, 1 Huawei USB-modem has 2 virtual modems. I use first to send sms via smsd, and second is to get helpful info about modem's life. First one has ID_USB_INTERFACE_NUM=00, second one - ID_USB_INTERFACE_NUM=01. Here I call 00 and 01 "a role".
echo "Finding out role of $DEVNAME (main/help)..." >> /etc/udev/rules.d/eventer.log
echo "---ID_USB_INTERFACE_NUM for $DEVNAME is $ID_USB_INTERFACE_NUM" >> /etc/udev/rules.d/eventer.log
#now we try to make symlink for device
echo "Trying to find device alias for $imei (subdevice $ID_USB_INTERFACE_NUM) in /etc/udev/rules.d/IDS.lst" >> /etc/udev/rules.d/eventer.log
#grepping IDS.lst for IMEI and "role":
alias=`grep $imei /etc/udev/rules.d/IDS.lst | grep $ID_USB_INTERFACE_NUM | cut -d " " -f 3`
if [ -z $alias ]
then
dt=`date`
echo "$dt +++ERROR+++ No record for $imei and $ID_USB_INTERFACE_NUM was found in /etc/udev/rules.d/IDS.lst, check it. Exit." >> /etc/udev/rules.d/eventer.log
exit
fi
echo "Found alias - $alias" >> /etc/udev/rules.d/eventer.log
#set symlink in OS
echo "Setting alias in OS..." >> /etc/udev/rules.d/eventer.log
echo "---Executing ln -s $DEVNAME /dev/$alias" >> /etc/udev/rules.d/eventer.log
ln -s $DEVNAME /dev/$alias
echo "Done. All finished. Exit." >> /etc/udev/rules.d/eventer.log
fi
###action if modem is removed. We need to remove earlier created symlinks
if [ "$1" == "remove" -a "$SUBSYSTEM" == "tty" ]
then
dt=`date`
echo "$dt $DEVNAME Device has been removed" >> /etc/udev/rules.d/eventer.log
dt=`date`
echo "$dt $DEVNAME Finding symlink for this device and removing it (them)..." >> /etc/udev/rules.d/eventer.log
ls -l /dev/modem* | tr "\n" ";" | sed -e s/";"/";\n"/g | grep "$DEVNAME;" | ls -l /dev/modem* | tr "\n" ";" | sed -e s/";"/";\n"/g | grep "$DEVNAME;" | cut -d "/" -f 2,3 | cut -d " " -f 1 | sed -e s/"dev"/"\/dev"/g | xargs rm
dt=`date`
echo "$dt $DEVNAME Removing done." >> /etc/udev/rules.d/eventer.log
fi
dt=`date`
echo $dt Device $DEVNAME, Maker is $ID_VENDOR_FROM_DATABASE was $1 >> /etc/udev/rules.d/eventer.log #simple example of using env vars, pointed to device, provided by udev
if [ "$1" == "add" -a "$SUBSYSTEM" == "tty" ] # if exactly modem has been inserted , not usb-drive etc. To find it out , we take 2 env vars, which have been set just by udev engine. You can take "$ACTION" var instead of $1
then
iam=`whoami`
echo "We do it all under user $iam" >> /etc/udev/rules.d/eventer.log
echo Modem insertion detected >> /etc/udev/rules.d/eventer.log
echo Finding out IMEI for $DEVNAME >> /etc/udev/rules.d/eventer.log
#debug chmod 777 $DEVNAME #you may want to do it or not, it's optional
echo ---Calling "/etc/udev/rules.d/poll_modem.py $DEVNAME | grep IMEI | cut -d " " -f 2 | tr -d \\r\\n" >> /etc/udev/rules.d/eventer.log #here we call special Python script who communicates to modem and asks for IMEI via AT-coomands (listed below)
imei=`/etc/udev/rules.d/poll_modem.py $DEVNAME | grep IMEI | cut -d " " -f 2 | tr -d "\r"`
# is modem didn't answer instantly - we keep trying to poll it
if [ -z $imei ]
then
echo "---IMEI was not found, retry 1..." >> /etc/udev/rules.d/eventer.log
imei=`/etc/udev/rules.d/poll_modem.py $DEVNAME | grep IMEI | cut -d " " -f 2 | tr -d "\r"`
fi
if [ -z $imei ]
then
echo "---IMEI was not found, retry 2..." >> /etc/udev/rules.d/eventer.log
imei=`/etc/udev/rules.d/poll_modem.py $DEVNAME | grep IMEI | cut -d " " -f 2 | tr -d "\r"`
fi
if [ -z $imei ]
then
echo "---IMEI was not found, retry 3..." >> /etc/udev/rules.d/eventer.log
imei=`/etc/udev/rules.d/poll_modem.py $DEVNAME | grep IMEI | cut -d " " -f 2 | tr -d "\r"`
fi
if [ -z $imei ]
then
dt=`date`
echo "$dt +++ERROR+++ IMEI was not found, i give up. Please reinsert modem $ID_VENDOR_FROM_DATABASE. Exit." >> /etc/udev/rules.d/eventer.log
exit
fi
echo "Found IMEI $imei for $DEVNAME">> /etc/udev/rules.d/eventer.log
# as said before, 1 Huawei USB-modem has 2 virtual modems. I use first to send sms via smsd, and second is to get helpful info about modem's life. First one has ID_USB_INTERFACE_NUM=00, second one - ID_USB_INTERFACE_NUM=01. Here I call 00 and 01 "a role".
echo "Finding out role of $DEVNAME (main/help)..." >> /etc/udev/rules.d/eventer.log
echo "---ID_USB_INTERFACE_NUM for $DEVNAME is $ID_USB_INTERFACE_NUM" >> /etc/udev/rules.d/eventer.log
#now we try to make symlink for device
echo "Trying to find device alias for $imei (subdevice $ID_USB_INTERFACE_NUM) in /etc/udev/rules.d/IDS.lst" >> /etc/udev/rules.d/eventer.log
#grepping IDS.lst for IMEI and "role":
alias=`grep $imei /etc/udev/rules.d/IDS.lst | grep $ID_USB_INTERFACE_NUM | cut -d " " -f 3`
if [ -z $alias ]
then
dt=`date`
echo "$dt +++ERROR+++ No record for $imei and $ID_USB_INTERFACE_NUM was found in /etc/udev/rules.d/IDS.lst, check it. Exit." >> /etc/udev/rules.d/eventer.log
exit
fi
echo "Found alias - $alias" >> /etc/udev/rules.d/eventer.log
#set symlink in OS
echo "Setting alias in OS..." >> /etc/udev/rules.d/eventer.log
echo "---Executing ln -s $DEVNAME /dev/$alias" >> /etc/udev/rules.d/eventer.log
ln -s $DEVNAME /dev/$alias
echo "Done. All finished. Exit." >> /etc/udev/rules.d/eventer.log
fi
###action if modem is removed. We need to remove earlier created symlinks
if [ "$1" == "remove" -a "$SUBSYSTEM" == "tty" ]
then
dt=`date`
echo "$dt $DEVNAME Device has been removed" >> /etc/udev/rules.d/eventer.log
dt=`date`
echo "$dt $DEVNAME Finding symlink for this device and removing it (them)..." >> /etc/udev/rules.d/eventer.log
ls -l /dev/modem* | tr "\n" ";" | sed -e s/";"/";\n"/g | grep "$DEVNAME;" | ls -l /dev/modem* | tr "\n" ";" | sed -e s/";"/";\n"/g | grep "$DEVNAME;" | cut -d "/" -f 2,3 | cut -d " " -f 1 | sed -e s/"dev"/"\/dev"/g | xargs rm
dt=`date`
echo "$dt $DEVNAME Removing done." >> /etc/udev/rules.d/eventer.log
fi
Important part of this process is a Python script which polls modem, sending there ATI command and reading output with IMEI. Python has been selected after trying:
* bash+minicom - failed. (minicom -D /dev/ttyUSB0 -S poll.min -C /tmp/result.txt writes empty
/tmp/result.txt)
* bash+socat - failed. unpredictable output.
* php - failed. (php_dio.so extension unstable works in my PHP 5.4-5.6)
don't forget to install pyserial module.
poll_modem.py:
#!/usr/bin/python
import serial
import sys
#print 'Num of parameters passed '+str(len(sys.argv))
if len(sys.argv) < 2:
print('No modem path specified. Use '+sys.argv[0]+' /dev/ttyUSBx')
print('Exit')
sys.exit()
#print 'Polling '+sys.argv[1]
try:
ser = serial.Serial(sys.argv[1],9600,dsrdtr=True,rtscts=True) #Mandatorily set dsrdtr=True,rtscts=True. In case of unsetting them, You can get successfull polling /dev/ttyUSB0, but failed /dev/ttyUSB1
except:
sys.exit("ERROR. Unable to open "+sys.argv[1]+' at speed 115200')
#print ser.isOpen()
ser.write("ATI1\r")
response = ser.read(100)
print response
ser.close()
import serial
import sys
#print 'Num of parameters passed '+str(len(sys.argv))
if len(sys.argv) < 2:
print('No modem path specified. Use '+sys.argv[0]+' /dev/ttyUSBx')
print('Exit')
sys.exit()
#print 'Polling '+sys.argv[1]
try:
ser = serial.Serial(sys.argv[1],9600,dsrdtr=True,rtscts=True) #Mandatorily set dsrdtr=True,rtscts=True. In case of unsetting them, You can get successfull polling /dev/ttyUSB0, but failed /dev/ttyUSB1
except:
sys.exit("ERROR. Unable to open "+sys.argv[1]+' at speed 115200')
#print ser.isOpen()
ser.write("ATI1\r")
response = ser.read(100)
print response
ser.close()
'smsdconf' Syntax Highlight powered by GeSHi
Now, as a result, we can 1 000 times reinsert modem with IMEI 1111111 into different holes in M/B or USB-hub, reboot server 10000 times - device with IMEI 1111111 will always be /dev/modem1
« Last edit by kontrolsson on Mon Jul 04, 2016 12:19, 104 months ago. »