os.rename('/run/gsm-state/.new.' + key,
'/run/gsm-state/' + key)
-def recall(key):
+def recall(key, nofile = ""):
try:
fd = open("/run/gsm-state/" + key)
l = fd.read(1000)
+ l = l.strip()
fd.close()
except IOError:
- l = ""
- return l.strip()
+ l = nofile
+ return l
def set_alert(key, value):
path = '/run/alert/' + key
# channel.set_state('on-call')
pass
+def data_handle(channel, line, m):
+ # Response to _OWANDATA - should contain IP address etc
+ if not m:
+ if 'matched' in channel.state:
+ # already handled match
+ return
+ # no connection active
+ data_hungup(channel, failed=False)
+ return
+ # m[0] is gateway/IP addr. m[1] and m[2] are DNS servers
+ dns = (m.groups()[1], m.groups()[2])
+ if channel.data_DNS != dns:
+ record('dns', '%s %s' % dns)
+ channel.data_DNS = dns
+ ip = m.groups()[0]
+ if channel.data_IP != ip:
+ channel.data_IP = ip
+ os.system('/sbin/ifconfig hso0 up %s' % ip)
+ record('data', ip)
+
+def data_call(channel, line, m):
+ # delayed reponse to _OWANCALL. Maybe be async, may be
+ # polled with '_OWANCALL?'
+ if not m:
+ return
+ if m.groups()[0] != '1':
+ return
+ s = int(m.groups()[1])
+ # 0 = Disconnected, 1 = Connected, 2 = In setup, 3 = Call setup failed.
+ if s == 0:
+ data_hungup(channel, failed=False)
+ elif s == 1:
+ channel.set_state('idle')
+ elif s == 2:
+ # try again soon
+ pass
+ elif s == 3:
+ data_hungup(channel, failed=True)
+
+def data_hungup(channel, failed):
+ if channel.data_IP:
+ os.system('/sbin/ifconfig hso0 down')
+ record('dns', '')
+ record('data', '')
+ channel.data_IP = None
+ channel.data_DNS = None
+ # FIXME should I retry, or reset?
+ if channel.data_APN:
+ # We still want a connection
+ if failed:
+ channel.set_state('reset')
+ return
+ elif channel.next_data_call <= time.time():
+ channel.next_data_call = (time.time() +
+ time.time() - channel.last_data_call);
+ channel.last_data_call = time.time()
+ channel.set_state('data-call')
+ return
+ if channel.gstate == 'data-call':
+ channel.set_state('idle')
+
control = {}
# For flight mode, we turn the power off.
ChangeStateAction('idle')
]
+def if_data(channel):
+ if not channel.data_APN and not channel.data_IP:
+ # no data happening
+ return 0
+ if channel.data_APN and channel.data_IP:
+ # connection is set up - slow watch
+ return 30000
+ if channel.data_IP:
+ # must be shutting down - poll quickly, it shouldn't take long
+ return 2000
+ # we want a connection but don't have one, so we retry
+ if time.time() < self.next_data_call:
+ return int((self.next_data_call - time.time()) * 1000)
+ return 1000
+
control['idle'] = [
RouteVoice(False),
CheckSMS(),
# get signal string
AtAction(check='+CSQ', ok='\+CSQ: (\d+),(\d+)',
record=('signal_strength','\\1/32'), repeat=29000)
+ AtAction(check='_OWANDATA?',
+ ok='_OWANDATA: 1, ([0-9.]+), [0-9.]+, ([0-9.]+), ([0-9.]+), [0-9.]+, [0-9.]+,\d+$',
+ handle=data_handle, repeat=if_data),
]
+control['data-call'] = [
+ AtAction(at='+CGDCONT=1,"IP","%s"', arg='APN'),
+ AtAction(at='_OWANCALL=1,1,0'),
+ AtAction(at='_OWANCALL?', handle=data_call, repeat=2000),
+ #ChangeStateAction('idle')
+]
+
+control['data-hangup'] = [
+ AtAction(at='_OWANCALL=1,0,0'),
+ ChangeStateAction('idle')
+]
+
control['incoming'] = [
BlockSuspendAction(True),
AtAction(check='+CPAS', ok='\+CPAS: (\d)', handle = call_status, repeat=500),
Async(msg='\+CUSD: ([012])(,"(.*)"(,[0-9]+)?)?$', handle = ussd),
Async(msg='_OSIGQ: ([0-9]+),([0-9]*)$', handle = sigstr),
+ Async(msg='_OWANCALL: (\d), (\d)', handle = data_call),
]
class GsmD(AtChannel):
self.tasknum = None
self.altpath = altpath
self.altchan = CarrierDetect(altpath, self)
+ self.data_APN = None
+ self.data_IP = None
+ self.data_DNS = None
+ self.last_data_call = 0
+ self.next_data_call = 0
self.gstate = None
self.nextstate = []
record('status', '')
record('sim','')
record('sid','')
+ record('data-APN', '')
+ record('data', '')
+ record('dns', '')
# set the initial state
self.set_state('flight')
d = dnotify.dir('/run/gsm-state')
self.call_watcher = d.watch('call', self.check_call)
self.dtmf_watcher = d.watch('dtmf', self.check_dtmf)
+ self.data_watcher = d.watch('data-APN', self.check_data)
self.suspend_handle = suspend.monitor(self.do_suspend, self.do_resume)
self.suspend_blocker = suspend.blocker()
self.set_state('dtmf')
record('dtmf','')
+ def check_data(self, f = None):
+ l = recall('data-APN')
+ log("Check data got", l)
+ if l == "":
+ l = None
+ if self.data_APN != l:
+ self.data_APN = l
+ self.args['APN'] = self.data_APN
+ if self.data_IP:
+ self.set_state('data-hangup')
+ elif self.gstate == 'idle' and self.data_APN:
+ self.last_data_call = time.time()
+ self.next_data_call = time.time()
+ self.set_state('data-call')
+
def check_flightmode(self, f = None):
try:
fd = open("/var/lib/misc/flightmode/active")
call_status0: if 'on-call, incoming, call, clear call
if not idle or suspend, become idle
call_status3: if not 'incoming' or 'answer', become 'incoming'
+
+----------------------------
+
+I want gsmd to manage data too.
+We use a file: /run/gsm-state/data
+
+If it is removed, we abort any data connection.
+If it exists we start a data connection and write the
+IP address and DNS servers to it.
+
+If we fail to make a connection we record failure and retry
+with exponential backoff to 1024 seconds.
+If we are connected and want to be, we check every 30 seconds
+If not connected and don't want to be, re-check every hour
+
+Both 'idle' and 'on-call' need to periodically monitor
+status.
+
+So a significant change to the 'data' file triggers a switch
+to 'data' if idle, which switches back to 'idle'.
+'idle' and 'on-call' have 'check-data' action.
+
+BUT: I need to set APN somehow. Also need to auto-detect
+ roaming and disable data in that case.
+
+Commands are:
+ AT+CGDCONT=1,"IP","$APN"
+
+ AT_OWANCALL=1,1,1
+which should get async reply
+ first '1' is channel
+ second '1' is 'connect'
+ third '1' is 'async reply wanted'
+ _OWANCALL: 1, 1
+but gets
+ _OWANCALL: 1, 3
+on error
+ 0 = Disconnected, 1 = Connected, 2 = In setup, 3 = Call setup failed.
+
+ Once connected:
+ AT_OWANDATA?
+ returns
+_OWANDATA: 1, <gateway>, <IP>, <DNS 1>, <DNS 2>, <NBNS1>, <NBNS2>, <speed>
+e.g.
+_OWANDATA: 1, 115.70.17.232, 0.0.0.0, 58.96.1.28, 220.233.0.3, 0.0.0.0, 0.0.0.0,144000
+
+If we consistently get Call setup failure, a reset ($QCPWRDWN) might help.
+or maybe a reset; AT_ORESET
+
+
+Roaming can be detected by comparing SIM card to services.
+AT+CIMI
+gets sim card number: 505023202571742
+First 5 digits are carrier: 50502. 3 are country, 2 are service.
+Match against AT_OSIMOP?
+
+https://bugs.archlinux.org/task/20864?getfile=5724
+
+
+_ONCI: 3G
+_ONCI: "0898FED3"
+_ONCI: "50502"
+_ONCI: 1
+_ONCI: (188,0,126)
+_ONCI: 8,1,(193,0,),(187,0,),(183,0,158)
+_ONCI: 8,2,(182,0,158),(181,0,),(194,0,158)
+_ONCI: 8,3,(185,0,),(190,0,158),(204,0,158)
+_ONCI: 8,4,(202,0,158),(198,0,158),(197,0,158)
+_ONCI: 8,5,(196,0,),(201,0,158),(200,0,158)
+_ONCI: 8,6,(199,0,158),(186,0,158),(184,0,158)
+_ONCI: 8,7,(10,0,),(11,0,),(203,0,)
+_ONCI: 8,8,(189,0,158)
+
+
+_ONCI: 3G
+_ONCI: "0898FED3"
+_ONCI: "50502"
+_ONCI: 1
+_ONCI: (188,0,118)
+_ONCI: 8,1,(189,0,158),(187,0,),(183,0,158)
+_ONCI: 8,2,(182,0,158),(181,0,),(194,0,158)
+_ONCI: 8,3,(193,0,),(190,0,158),(204,0,158)
+_ONCI: 8,4,(203,0,),(202,0,158),(198,0,158)
+_ONCI: 8,5,(197,0,158),(196,0,),(201,0,158)
+_ONCI: 8,6,(200,0,158),(199,0,158),(186,0,158)
+_ONCI: 8,7,(185,0,),(10,0,),(11,0,)
+_ONCI: 8,8,(184,0,158)
+
+_ONCI: 3G
+_ONCI: "0898FED3"
+_ONCI: "50502"
+_ONCI: 1
+_ONCI: (188,0,158)
+_ONCI: 7,1,(203,0,134),(201,0,),(182,0,158)
+_ONCI: 7,2,(181,0,),(10,0,),(193,0,)
+_ONCI: 7,3,(190,0,158),(204,0,126),(187,0,158)
+_ONCI: 7,4,(183,0,158),(198,0,158),(197,0,158)
+_ONCI: 7,5,(196,0,158),(202,0,158),(200,0,138)
+_ONCI: 7,6,(199,0,139),(194,0,128),(184,0,158)
+_ONCI: 7,7,(11,0,158),(186,0,158),(185,0,158)
+
+
+_ONCI: 3G
+_ONCI: "08989C66"
+_ONCI: "50502"
+_ONCI: 0
+_ONCI: (143,0,)
+_ONCI: 5,1,(144,0,),(124,0,158),(125,0,158)
+_ONCI: 5,2,(126,0,),(121,0,120),(122,0,)
+_ONCI: 5,3,(123,0,141),(145,0,142),(146,0,)
+_ONCI: 5,4,(147,0,),(142,0,158),(135,0,)
+_ONCI: 5,5,(133,0,158),(134,0,158)
+
+
+
+First number is up to 204.
+Seconds is 0
+Third is blank or 158. but can br 126, 118 for first triple.
+
+_OPON: 1,594553204F50545553,3
+ Y E S spO P T U S
+
+_OSIMOP: "YES OPTUS","YES OPTUS","50502"
+
+_OSSYS 0 -> TECH_GPRS (use OCTI for details)
+_OSSYS 2 -> TECH_UMTS (use OWCTI for details)
+
+OCTI 1 -> GSM
+OCTI 2 -> GPRS
+OCTI 3 -> EDGE
+
+OWCTI 1 -> UMTS
+OWCTI 2 -> HSDPA
+OWCTI 3 -> HSUPA
+OWCTI 4 -> HSPA
+
+
+???
+Issues:
+ - Who manages the APN? I think netman should do that. So it requests
+ data service by writing the APN to /run/gsm-state/data-APN
+ - gsmd must check roaming status and report operator, SIM.
+ - gsmd can do the ifconfig, but leave routing and DNS to netman.
+ - gsmd should monitor usage, record current session in stable storage
+ and provide a log
+ - report +CSQ and OSIGQ separately?
+
+
+Data sequencing:
+
+ 'data' state is like 'idle' but regularly checks data status
+ 'data-hangup' state to disconnect data
+ 'data-call' state to start data call
+
+ DataCheck action which tracks usage...
+
+
+ADD CLCC monitoring to get out of 'idle' into 'on-call' ?