From: NeilBrown Date: Mon, 31 Dec 2012 09:41:16 +0000 (+1100) Subject: GSMD - add management of data connection. X-Git-Url: http://git.neil.brown.name/?a=commitdiff_plain;h=54ff52ff8cb874f37947e7afb005ad97a046f164;p=plato.git GSMD - add management of data connection. If /run/gsm-state/data-APN contains an APN, then gsmd tries to establish and maintain a data connection. The 'hso' interface is ifconfiged and the IP is written to /run/gsm-state/data. DNS servers are written to /run/gsm-state/dns --- diff --git a/gsm/gsmd.py b/gsm/gsmd.py index 66d99c0..75f9122 100644 --- a/gsm/gsmd.py +++ b/gsm/gsmd.py @@ -29,14 +29,15 @@ def record(key, value): 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 @@ -425,6 +426,67 @@ def call_status(channel, line, m): # 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. @@ -518,6 +580,21 @@ control['init'] = [ 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(), @@ -531,8 +608,23 @@ control['idle'] = [ # 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), @@ -586,6 +678,7 @@ async = [ 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): @@ -641,6 +734,11 @@ 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 = [] @@ -651,6 +749,9 @@ class GsmD(AtChannel): record('status', '') record('sim','') record('sid','') + record('data-APN', '') + record('data', '') + record('dns', '') # set the initial state self.set_state('flight') @@ -661,6 +762,7 @@ class GsmD(AtChannel): 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() @@ -706,6 +808,21 @@ class GsmD(AtChannel): 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") diff --git a/gsm/notes b/gsm/notes index 9a9264d..47b4b0e 100644 --- a/gsm/notes +++ b/gsm/notes @@ -137,3 +137,161 @@ We sometimes check gstate directly: 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, , , , , , , +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' ?