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