1# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import logging 6import re 7import time 8import common 9from autotest_lib.client.cros.cellular import cellular_logging 10from autotest_lib.client.cros.cellular import cellular_system_error 11from autotest_lib.client.cros.cellular import air_state_verifier 12from autotest_lib.client.cros.cellular import base_station_interface 13from autotest_lib.client.cros.cellular import cellular 14from autotest_lib.client.bin import utils 15 16POLL_SLEEP = 0.2 17 18log = cellular_logging.SetupCellularLogging('base_station_8960') 19 20class BaseStation8960(base_station_interface.BaseStationInterface): 21 """Wrap an Agilent 8960 Series 10.""" 22 23 def __init__(self, scpi_connection, no_initialization=False): 24 """ 25 Creates an 8960 call-box object. 26 TODO (byrok): make a factory that returns a call_box, of either 27 a 8960 or a PXT, or a... 28 29 @param scpi_connection: The scpi port to send commands over 30 @param no_initialization: Don't do anything. Useful for unit testing 31 and debugging when you don't want to run all the usual functions. 32 """ 33 self.c = scpi_connection 34 if no_initialization: 35 return 36 self.checker_context = self.c.checker_context 37 with self.checker_context: 38 self._Verify() 39 self._Reset() 40 self.SetPower(cellular.Power.DEFAULT) 41 42 def _Verify(self): 43 idn = self.c.Query('*IDN?') 44 if '8960 Series 10 E5515C' not in idn: 45 raise cellular_system_error.BadState( 46 'Not actually an 8960: *IDN? says ' + idn) 47 48 def _Reset(self): 49 self.c.Reset() 50 self.Stop() 51 # Perform a partial reset to workaround a problem with the 8960 52 # failing to accept CDMA connections after switching from a 53 # GSM technology. 54 self.c.SendStanza(['SYSTEM:PRESet3']) 55 56 def _IsIdle(self): 57 call_state = self.c.Query('CALL:STATus?') 58 data_state = self.c.Query('CALL:STATus:DATa?') 59 return call_state == 'IDLE' and data_state in ['IDLE', 'OFF'] 60 61 def Close(self): 62 self.c.Close() 63 64 def GetAirStateVerifier(self): 65 return air_state_verifier.AirStateVerifierBasestation(self) 66 67 def GetDataCounters(self): 68 output = {} 69 for counter in ['OTATx', 'OTARx', 'IPTX', 'IPRX']: 70 result_text = self.c.Query('CALL:COUNT:DTMonitor:%s:DRATe?' % 71 counter) 72 result = [float(x) for x in result_text.rstrip().split(',')] 73 output[counter] = dict(zip(['Mean', 'Current', 'Max', 'Total'], 74 result)) 75 logging.info('Data counters: %s', output) 76 return output 77 78 def GetRatUeDataStatus(self): 79 """Get the radio-access-technology-specific status of the UE. 80 81 Unlike GetUeDataStatus, below, this returns a status that depends 82 on the RAT being used. 83 """ 84 status = self.c.Query('CALL:STATus:DATa?') 85 rat = \ 86 ConfigDictionaries.FORMAT_TO_DATA_STATUS_TYPE[self.format][status] 87 return rat 88 89 def GetUeDataStatus(self): 90 """Get the UeGenericDataStatus status of the device.""" 91 rat = self.GetRatUeDataStatus() 92 return cellular.RatToGenericDataStatus[rat] 93 94 def ResetDataCounters(self): 95 self.c.SendStanza(['CALL:COUNt:DTMonitor:CLEar']) 96 97 def ClearErrors(self): 98 self.c.RetrieveErrors() 99 100 def LogStats(self): 101 self.c.Query("CALL:HSDPa:SERVice:PSData:HSDSchannel:CONFig?") 102 103 # Category reported by UE 104 self.c.Query("CALL:HSDPa:MS:REPorted:HSDSChannel:CATegory?") 105 # The category in use 106 self.c.Query("CALL:STATUS:MS:HSDSChannel:CATegory?") 107 self.c.Query("CALL:HSDPA:SERV:PSD:CQI?") 108 109 def SetBsIpV4(self, ip1, ip2): 110 self.c.SendStanza([ 111 'SYSTem:COMMunicate:LAN:SELF:ADDRess:IP4 "%s"' % ip1, 112 'SYSTem:COMMunicate:LAN:SELF:ADDRess2:IP4 "%s"' % ip2,]) 113 114 def SetBsNetmaskV4(self, netmask): 115 self.c.SendStanza([ 116 'SYSTem:COMMunicate:LAN:SELF:SMASk:IP4 "%s"' % netmask,]) 117 118 def SetPlmn(self, mcc, mnc): 119 # Doing this appears to set the WCDMa versions as well 120 self.c.SendStanza([ 121 'CALL:MCCode %s' % mcc, 122 'CALL:MNCode %s' % mnc,]) 123 124 def SetPower(self, dbm): 125 if dbm <= cellular.Power.OFF : 126 self.c.SendStanza([ 127 'CALL:CELL:POWer:STATe off',]) 128 else: 129 self.c.SendStanza([ 130 'CALL:CELL:POWer %s' % dbm,]) 131 132 def SetTechnology(self, technology): 133 # TODO(rochberg): Check that we're not already in chosen tech for 134 # speed boost 135 136 # Print out a helpful message on a key error. 137 try: 138 self.format = ConfigDictionaries.TECHNOLOGY_TO_FORMAT[technology] 139 except KeyError: 140 raise KeyError('%s not in %s ' % 141 (technology, 142 ConfigDictionaries.TECHNOLOGY_TO_FORMAT)) 143 self.technology = technology 144 145 self.c.SimpleVerify('SYSTem:APPLication:FORMat', self.format) 146 # Setting the format will start the call box, we need to stop it so we 147 # can configure the new format. 148 self.Stop() 149 self.c.SendStanza( 150 ConfigDictionaries.TECHNOLOGY_TO_CONFIG_STANZA.get(technology, [])) 151 152 def SetUeDnsV4(self, dns1, dns2): 153 """Set the DNS values provided to the UE. Emulator must be stopped.""" 154 stanza = ['CALL:MS:DNSServer:PRIMary:IP:ADDRess "%s"' % dns1] 155 if dns2: 156 stanza.append('CALL:MS:DNSServer:SECondary:IP:ADDRess "%s"' % dns2) 157 self.c.SendStanza(stanza) 158 159 def SetUeIpV4(self, ip1, ip2=None): 160 """ 161 Set the IP addresses provided to the UE. Emulator must be stopped. 162 """ 163 stanza = ['CALL:MS:IP:ADDRess1 "%s"' % ip1] 164 if ip2: 165 stanza.append('CALL:MS:IP:ADDRess2 "%s"' % ip2) 166 self.c.SendStanza(stanza) 167 168 def Start(self): 169 self.c.SendStanza(['CALL:OPERating:MODE CALL']) 170 171 def Stop(self): 172 self.c.SendStanza(['CALL:OPERating:MODE OFF']) 173 # Make sure the call status goes to idle before continuing. 174 utils.poll_for_condition( 175 self._IsIdle, 176 timeout=cellular.DEFAULT_TIMEOUT, 177 exception=cellular_system_error.BadState( 178 '8960 did not enter IDLE state')) 179 180 def SupportedTechnologies(self): 181 return [ 182 cellular.Technology.GPRS, 183 cellular.Technology.EGPRS, 184 cellular.Technology.WCDMA, 185 cellular.Technology.HSDPA, 186 cellular.Technology.HSUPA, 187 cellular.Technology.HSDUPA, 188 cellular.Technology.HSPA_PLUS, 189 cellular.Technology.CDMA_2000, 190 cellular.Technology.EVDO_1X, 191 ] 192 193 def WaitForStatusChange(self, 194 interested=None, 195 timeout=cellular.DEFAULT_TIMEOUT): 196 """When UE status changes (to a value in interested), return the value. 197 198 Arguments: 199 interested: if non-None, only transitions to these states will 200 cause a return 201 timeout: in seconds. 202 Returns: state 203 Raises: cellular_system_error.InstrumentTimeout 204 """ 205 start = time.time() 206 while time.time() - start <= timeout: 207 state = self.GetUeDataStatus() 208 if state in interested: 209 return state 210 time.sleep(POLL_SLEEP) 211 212 state = self.GetUeDataStatus() 213 if state in interested: 214 return state 215 216 raise cellular_system_error.InstrumentTimeout( 217 'Timed out waiting for state in %s. State was %s' % 218 (interested, state)) 219 220def _Parse(command_sequence): 221 """Split and remove comments from a config stanza.""" 222 uncommented = [re.sub(r'\s*#.*', '', line) 223 for line in command_sequence.split('\n')] 224 225 # Return only nonempty lines 226 return [line for line in uncommented if line] 227 228 229class ConfigStanzas(object): 230 # p 22 of http://cp.literature.agilent.com/litweb/pdf/5989-5932EN.pdf 231 WCDMA_MAX = _Parse(""" 232# RAB3: 64 Up/384 down 233# http://wireless.agilent.com/rfcomms/refdocs/wcdma/wcdmala_hpib_call_service.html#CACBDEAH 234CALL:UPLink:TXPower:LEVel:MAXimum 24 235CALL:SERVICE:GPRS:RAB GPRSRAB3 236""") 237 238 # p 20 of http://cp.literature.agilent.com/litweb/pdf/5989-5932EN.pdf 239 CDMA_2000_MAX = _Parse(""" 240CALL:SCHannel:FORWard:DRATe BPS153600 241CALL:CELL:SOPTion:RCONfig3 SOFS33 242""") 243 244 # p 19 of http://cp.literature.agilent.com/litweb/pdf/5989-5932EN.pdf 245 EVDO_1X_MAX = _Parse(""" 246CALL:CELL:CONTrol:CATTribute:ISTate:PCCCycle ATSP 247# Default data application 248CALL:APPLication:SESSion DPAPlication 249# Give DUT 100% of channel 250CALL:CELL:APPLication:ATDPackets 100 251""") 252 253 GPRS_MAX = _Parse(""" 254call:bch:scel gprs 255call:pdtch:mslot:config d1u1 256call:cell:tbflow:t3192 ms1500 257""") 258 259 EGPRS_MAX = _Parse(""" 260call:bch:scel egprs 261call:pdtch:mslot:config d4u1 262call:cell:tbflow:t3192 ms1500 263""") 264 265 CAT_08 = _Parse(""" 266call:pow:stat ON 267call:ms:pow:targ 0 268call:cell:rlc:rees OFF 269call:hsdpa:ms:hsdschannel:cat:control:auto off 270call:hsdpa:ms:hsdschannel:cat:man 8 271call:hsdpa:service:psdata:hsdschannel:config cqiv 272call:hsdpa:service:psdata:cqi 22 273call:serv:gprs:rab PHSP 274call:serv:rbt:rab HSDP12 275call:serv:psd:srb:mapp UEDD 276call:hsup:serv:psd:edpd:ccod:max T2T4 277call:hsup:edch:tti MS10 278call:hsup:serv:psd:ergc:inf:stat Off 279""") 280 281 CAT_10 = _Parse(""" 282call:pow:stat ON 283call:ms:pow:targ 0 284call:cell:rlc:rees OFF 285call:hsdpa:ms:hsdschannel:cat:control:auto off 286call:hsdpa:ms:hsdschannel:cat:man 10 287call:serv:gprs:rab PHSP 288call:serv:rbt:rab HSDP12 289call:hsdpa:service:psdata:hsdschannel:config cqiv 290call:hsdpa:service:psdata:cqi 22 291call:serv:psd:srb:mapp UEDD 292call:hsup:serv:psd:edpd:ccod:max T2T4 293call:hsup:edch:tti MS2 294call:hsup:serv:psd:ergc:inf:stat Off 295""") 296 297class ConfigDictionaries(object): 298 TECHNOLOGY_TO_FORMAT_RAW = { 299 cellular.Technology.GPRS: 'GSM/GPRS', 300 cellular.Technology.EGPRS: 'GSM/GPRS', 301 302 cellular.Technology.WCDMA: 'WCDMA', 303 cellular.Technology.HSDPA: 'WCDMA', 304 cellular.Technology.HSUPA: 'WCDMA', 305 cellular.Technology.HSDUPA: 'WCDMA', 306 cellular.Technology.HSPA_PLUS: 'WCDMA', 307 308 cellular.Technology.CDMA_2000: 'IS-2000/IS-95/AMPS', 309 310 cellular.Technology.EVDO_1X: 'IS-856', 311 } 312 313 # Put each value in "" marks to quote it for GPIB 314 TECHNOLOGY_TO_FORMAT = dict([ 315 (x, '"%s"' % y) for 316 x, y in TECHNOLOGY_TO_FORMAT_RAW.iteritems()]) 317 318 TECHNOLOGY_TO_CONFIG_STANZA = { 319 cellular.Technology.CDMA_2000: ConfigStanzas.CDMA_2000_MAX, 320 cellular.Technology.EVDO_1X: ConfigStanzas.EVDO_1X_MAX, 321 cellular.Technology.GPRS: ConfigStanzas.GPRS_MAX, 322 cellular.Technology.EGPRS: ConfigStanzas.EGPRS_MAX, 323 cellular.Technology.WCDMA: ConfigStanzas.WCDMA_MAX, 324 cellular.Technology.HSDPA: ConfigStanzas.CAT_08, 325 cellular.Technology.HSUPA: ConfigStanzas.CAT_08, 326 cellular.Technology.HSDUPA: ConfigStanzas.CAT_08, 327 cellular.Technology.HSPA_PLUS: ConfigStanzas.CAT_10, 328 } 329 330 # http://wireless.agilent.com/rfcomms/refdocs/ 331 # gsmgprs/prog_synch_callstategprs.html#CHDDFBAJ 332 # NB: We have elided a few states of the GSM state machine here. 333 CALL_STATUS_DATA_TO_STATUS_GSM_GPRS = { 334 'IDLE': cellular.UeGsmDataStatus.IDLE, 335 'ATTG': cellular.UeGsmDataStatus.ATTACHING, 336 'DET': cellular.UeGsmDataStatus.DETACHING, 337 'ATT': cellular.UeGsmDataStatus.ATTACHED, 338 'STAR': cellular.UeGsmDataStatus.ATTACHING, 339 'END': cellular.UeGsmDataStatus.PDP_DEACTIVATING, 340 'TRAN': cellular.UeGsmDataStatus.PDP_ACTIVE, 341 'PDPAG': cellular.UeGsmDataStatus.PDP_ACTIVATING, 342 'PDP': cellular.UeGsmDataStatus.PDP_ACTIVE, 343 'PDPD': cellular.UeGsmDataStatus.PDP_DEACTIVATING, 344 'DCON': cellular.UeGsmDataStatus.PDP_ACTIVE, 345 'SUSP': cellular.UeGsmDataStatus.IDLE, 346 } 347 348 # http://wireless.agilent.com/rfcomms/refdocs/ 349 # wcdma/wcdma_gen_call_proc_status.html#CJADGAHG 350 CALL_STATUS_DATA_TO_STATUS_WCDMA = { 351 'IDLE': cellular.UeGsmDataStatus.IDLE, 352 'ATTG': cellular.UeGsmDataStatus.ATTACHING, 353 'DET': cellular.UeGsmDataStatus.DETACHING, 354 'OFF': cellular.UeGsmDataStatus.NONE, 355 'PDPAG': cellular.UeGsmDataStatus.PDP_ACTIVATING, 356 'PDP': cellular.UeGsmDataStatus.PDP_ACTIVE, 357 'PDPD': cellular.UeGsmDataStatus.PDP_DEACTIVATING, 358 } 359 360 # http://wireless.agilent.com/rfcomms/refdocs/ 361 # cdma2k/cdma2000_hpib_call_status.html#CJABGBCF 362 CALL_STATUS_DATA_TO_STATUS_CDMA_2000 = { 363 'OFF': cellular.UeC2kDataStatus.OFF, 364 'DORM': cellular.UeC2kDataStatus.DORMANT, 365 'DCON': cellular.UeC2kDataStatus.DATA_CONNECTED, 366 } 367 368 # http://wireless.agilent.com/rfcomms/refdocs/ 369 # 1xevdo/1xevdo_hpib_call_status.html#BABCGBCD 370 CALL_STATUS_DATA_TO_STATUS_EVDO = { 371 'CCL': cellular.UeEvdoDataStatus.CONNECTION_CLOSING, 372 'CNEG': cellular.UeEvdoDataStatus.CONNECTION_NEGOTIATE, 373 'CREQ': cellular.UeEvdoDataStatus.CONNECTION_REQUEST, 374 'DCON': cellular.UeEvdoDataStatus.DATA_CONNECTED, 375 'DORM': cellular.UeEvdoDataStatus.DORMANT, 376 'HAND': cellular.UeEvdoDataStatus.HANDOFF, 377 'IDLE': cellular.UeEvdoDataStatus.IDLE, 378 'PAG': cellular.UeEvdoDataStatus.PAGING, 379 'SCL': cellular.UeEvdoDataStatus.SESSION_CLOSING, 380 'SNEG': cellular.UeEvdoDataStatus.SESSION_NEGOTIATE, 381 'SOP': cellular.UeEvdoDataStatus.SESSION_OPEN, 382 'UREQ': cellular.UeEvdoDataStatus.UATI_REQUEST, 383 } 384 385 FORMAT_TO_DATA_STATUS_TYPE = { 386 '"GSM/GPRS"': CALL_STATUS_DATA_TO_STATUS_GSM_GPRS, 387 '"WCDMA"': CALL_STATUS_DATA_TO_STATUS_WCDMA, 388 '"IS-2000/IS-95/AMPS"': CALL_STATUS_DATA_TO_STATUS_CDMA_2000, 389 '"IS-856"': CALL_STATUS_DATA_TO_STATUS_EVDO, 390 } 391