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 six 11import time 12 13import common 14 15from autotest_lib.client.bin import utils 16from autotest_lib.client.cros.cellular import air_state_verifier 17from autotest_lib.client.cros.cellular import base_station_interface 18from autotest_lib.client.cros.cellular import cellular 19from autotest_lib.client.cros.cellular import cellular_logging 20from autotest_lib.client.cros.cellular import cellular_system_error 21 22POLL_SLEEP = 0.2 23 24log = cellular_logging.SetupCellularLogging('base_station_pxt') 25 26 27class BaseStationPxt(base_station_interface.BaseStationInterface): 28 """Wrap an Agilent PXT""" 29 30 def __init__(self, scpi_connection, no_initialization=False): 31 """ 32 Creates a PXT call-box object. 33 TODO(byronk): Make a factory that returns a call_box, of either 34 a 8960 or a PXT, or a... 35 36 @param scpi_connection: The scpi port to send commands over. 37 @param no_initialization: Don't do anything. Useful for unit testing 38 and debugging when you don't want to run all the usual functions. 39 """ 40 # TODO(byronk): Use variables longer then 1 char. 41 self.c = scpi_connection 42 if no_initialization: 43 return 44 self.checker_context = self.c.checker_context 45 with self.checker_context: 46 self._Verify() 47 self._Reset() 48 49 def _Verify(self): 50 idn = self.c.Query('*IDN?') 51 if 'E6621' not in idn: 52 raise cellular_system_error.BadScpiCommand( 53 'Not actually a E6621 PXT: *IDN? says ' + idn) 54 55 def _Reset(self): 56 self.c.Reset() 57 self.Stop() 58 59 def _IsIdle(self): 60 bs_active = self.c.Query('BSE:SIMULator?') 61 return bs_active == 'STOP' 62 63 def Close(self): 64 self.c.Close() 65 66 def GetAirStateVerifier(self): 67 return air_state_verifier.AirStateVerifierBasestation(self) 68 69 def GetDataCounters(self): 70 raise NotImplementedError 71 72 def GetRatUeDataStatus(self): 73 """Get the radio-access-technology-specific status of the UE. 74 75 Unlike GetUeDataStatus, below, this returns a status that depends 76 on the RAT being used. 77 """ 78 status = self.c.Query('BSE:STATus:ACELL?') 79 rat = \ 80 ConfigDictionaries.FORMAT_TO_DATA_STATUS_TYPE[self.format][status] 81 return rat 82 83 def GetUeDataStatus(self): 84 """Get the UeGenericDataStatus status of the device.""" 85 rat = self.GetRatUeDataStatus() 86 return cellular.RatToGenericDataStatus[rat] 87 88 def ResetDataCounters(self): 89 # Keep this here until the implementation of this class 90 # is done. If we never hit this, then we can safely remove it, 91 # but we should think twice about that. The 8960 needs it, 92 # it seems likely that the PXT should. 93 raise NotImplementedError 94 95 def ClearErrors(self): 96 self.c.RetrieveErrors() 97 98 def LogStats(self): 99 # Keep this here until the implementation of this class 100 # is done. If we never hit this, then we can safely remove it, 101 # but we should think twice about that. The 8960 needs it, 102 # it seems likely that the PXT should. 103 raise NotImplementedError 104 105 def SetBsIpV4(self, ip1, ip2): 106 return # TODO(byronk): Configure the PXT to find.crbug.com:/235643 107 108 def SetBsNetmaskV4(self, netmask): 109 return # TODO(byronk): Configure the PXT to find. crbug.com:/235643 110 111 def SetPlmn(self, mcc, mnc): 112 raise NotImplementedError 113 114 def SetPower(self, dbm): 115 # TODO(byronk): Setting the RF output of the PXT. crbug.com/235655 116 # and 8960 call boxes to OFF should be off 117 if dbm <= cellular.Power.OFF: 118 self.c.SendStanza(['AMPLitude:ALL -120']) 119 else: 120 self.c.SendStanza(['AMPLitude:ALL %s' % dbm]) 121 122 def SetTechnology(self, technology): 123 # TODO(byronk): The set technology step likely belongs in the 124 # constructor. 125 126 # Print out a helpful message on a key error. 127 try: 128 self.format = ConfigDictionaries.TECHNOLOGY_TO_FORMAT[technology] 129 except KeyError: 130 raise KeyError('%s not in %s ' % ( 131 technology, 132 ConfigDictionaries.TECHNOLOGY_TO_FORMAT)) 133 self.technology = technology 134 135 def SetUeDnsV4(self, dns1, dns2): 136 """Set the DNS values provided to the UE. Emulator must be stopped.""" 137 return # TODO(byronk): Configure the PXT to find. crbug.com/235643 138 # the data server 139 140 def SetUeIpV4(self, ip1, ip2=None): 141 """ 142 Set the IP addresses provided to the UE. Emulator must be stopped. 143 """ 144 return # TODO(byronk) crbug.com:/235643: Configure the PXT to find 145 # the data server 146 147 def Start(self): 148 commands = [ 149 '*CLS', 150 'STATus:PRESet', 151 # Enable conn checks 152 'BSE:CONFig:RRC:CTIMer:STATus ON', 153 # Check freq (secs) 154 'BSE:CONFig:RRC:CTIMer:LENGth 5', 155 'SIGN:MODE BSE', 156 'SCENArio:LOAD "FDD_Combined_v6.3.lbmf"', 157 'BSE:CONF:PROF 20MH', 158 'FREQ:BAND 13', 159 'BSE:SIMULator RUN' 160 ] 161 self.c.SendStanza(commands) 162 163 def Stop(self): 164 self.c.SendStanza(['BSE:SIMULator STOP']) 165 # Make sure the call status goes to idle before continuing. 166 utils.poll_for_condition( 167 self._IsIdle, 168 timeout=cellular.DEFAULT_TIMEOUT, 169 exception=cellular_system_error.BadState( 170 'PXT did not enter IDLE state')) 171 172 def SupportedTechnologies(self): 173 return [cellular.Technology.LTE] 174 175 def WaitForStatusChange(self, 176 interested=None, 177 timeout=cellular.DEFAULT_TIMEOUT): 178 """When UE status changes (to a value in |interested|), 179 return the value. 180 181 Arguments: 182 interested: if non-None, only transitions to these states will 183 cause a return 184 timeout: in seconds. 185 Returns: state 186 Raises: cellular_system_error.InstrumentTimeout 187 """ 188 start = time.time() 189 # TODO(byronk): consider utils.poll_for_condition() 190 while time.time() - start <= timeout: 191 state = self.GetUeDataStatus() 192 if state in interested: 193 return state 194 time.sleep(POLL_SLEEP) 195 196 state = self.GetUeDataStatus() 197 if state in interested: 198 return state 199 200 raise cellular_system_error.InstrumentTimeout( 201 'Timed out waiting for state in %s. State was %s.' % 202 (interested, state)) 203 204 205class ConfigStanzas(object): 206 # p 22 of http://cp.literature.agilent.com/litweb/pdf/5989-5932EN.pdf 207 208 def _Parse( command_sequence): 209 """Split and remove comments from a config stanza.""" 210 return [line for line in command_sequence.splitlines() 211 if line and not line.startswith('#')] 212 213 LTE = _Parse(""" """) 214 215 # TODO(byronk): ConfigStanza should not be. These belong somewhere in 216 # the PXT class. 217 WCDMA_MAX = _Parse(""" 218# RAB3: 64 Up/384 down 219# http://wireless.agilent.com/rfcomms/refdocs/ 220# wcdma/wcdmala_hpib_call_service.html#CACBDEAH 221CALL:UPLink:TXPower:LEVel:MAXimum 24 222CALL:SERVICE:GPRS:RAB GPRSRAB3 223""") 224 225 # p 20 of http://cp.literature.agilent.com/litweb/pdf/5989-5932EN.pdf 226 CDMA_2000_MAX = _Parse(""" 227CALL:SCHannel:FORWard:DRATe BPS153600 228CALL:CELL:SOPTion:RCONfig3 SOFS33 229""") 230 231 # p 19 of http://cp.literature.agilent.com/litweb/pdf/5989-5932EN.pdf 232 EVDO_1X_MAX = _Parse(""" 233CALL:CELL:CONTrol:CATTribute:ISTate:PCCCycle ATSP 234# Default data application 235CALL:APPLication:SESSion DPAPlication 236# Give DUT 100% of channel 237CALL:CELL:APPLication:ATDPackets 100 238""") 239 240 GPRS_MAX = _Parse(""" 241call:bch:scel gprs 242call:pdtch:mslot:config d1u1 243call:cell:tbflow:t3192 ms1500 244""") 245 246 EGPRS_MAX = _Parse(""" 247call:bch:scel egprs 248call:pdtch:mslot:config d4u1 249call:cell:tbflow:t3192 ms1500 250""") 251 252 CAT_08 = _Parse(""" 253call:pow:stat ON 254call:ms:pow:targ 0 255call:cell:rlc:rees OFF 256call:hsdpa:ms:hsdschannel:cat:control:auto off 257call:hsdpa:ms:hsdschannel:cat:man 8 258call:hsdpa:service:psdata:hsdschannel:config cqiv 259call:hsdpa:service:psdata:cqi 22 260call:serv:gprs:rab PHSP 261call:serv:rbt:rab HSDP12 262call:serv:psd:srb:mapp UEDD 263call:hsup:serv:psd:edpd:ccod:max T2T4 264call:hsup:edch:tti MS10 265call:hsup:serv:psd:ergc:inf:stat Off 266""") 267 268 CAT_10 = _Parse(""" 269call:pow:stat ON 270call:ms:pow:targ 0 271call:cell:rlc:rees OFF 272call:hsdpa:ms:hsdschannel:cat:control:auto off 273call:hsdpa:ms:hsdschannel:cat:man 10 274call:serv:gprs:rab PHSP 275call:serv:rbt:rab HSDP12 276call:hsdpa:service:psdata:hsdschannel:config cqiv 277call:hsdpa:service:psdata:cqi 22 278call:serv:psd:srb:mapp UEDD 279call:hsup:serv:psd:edpd:ccod:max T2T4 280call:hsup:edch:tti MS2 281call:hsup:serv:psd:ergc:inf:stat Off 282""") 283 284 285class ConfigDictionaries(object): 286 TECHNOLOGY_TO_FORMAT_RAW = { 287 cellular.Technology.GPRS: 'GSM/GPRS', 288 cellular.Technology.EGPRS: 'GSM/GPRS', 289 290 cellular.Technology.WCDMA: 'WCDMA', 291 cellular.Technology.HSDPA: 'WCDMA', 292 cellular.Technology.HSUPA: 'WCDMA', 293 cellular.Technology.HSDUPA: 'WCDMA', 294 cellular.Technology.HSPA_PLUS: 'WCDMA', 295 296 cellular.Technology.CDMA_2000: 'IS-2000/IS-95/AMPS', 297 298 cellular.Technology.EVDO_1X: 'IS-856', 299 300 cellular.Technology.LTE: 'LTE', 301 } 302 303 # Put each value in "" marks to quote it for GPIB 304 TECHNOLOGY_TO_FORMAT = dict([ 305 (x, '"%s"' % y) for 306 x, y in six.iteritems(TECHNOLOGY_TO_FORMAT_RAW)]) 307 308 TECHNOLOGY_TO_CONFIG_STANZA = { 309 cellular.Technology.CDMA_2000: ConfigStanzas.CDMA_2000_MAX, 310 cellular.Technology.EVDO_1X: ConfigStanzas.EVDO_1X_MAX, 311 cellular.Technology.GPRS: ConfigStanzas.GPRS_MAX, 312 cellular.Technology.EGPRS: ConfigStanzas.EGPRS_MAX, 313 cellular.Technology.WCDMA: ConfigStanzas.WCDMA_MAX, 314 cellular.Technology.HSDPA: ConfigStanzas.CAT_08, 315 cellular.Technology.HSUPA: ConfigStanzas.CAT_08, 316 cellular.Technology.HSDUPA: ConfigStanzas.CAT_08, 317 cellular.Technology.HSPA_PLUS: ConfigStanzas.CAT_10, 318 cellular.Technology.LTE: ConfigStanzas.LTE, 319 } 320 # TODO(byronk): remove these. Not used for LTE. Check for external deps 321 # http://wireless.agilent.com/rfcomms/refdocs/ 322 # gsmgprs/prog_synch_callstategprs.html#CHDDFBAJ 323 # NB: We have elided a few states of the GSM state machine here. 324 CALL_STATUS_DATA_TO_STATUS_GSM_GPRS = { 325 'IDLE': cellular.UeGsmDataStatus.IDLE, 326 'ATTG': cellular.UeGsmDataStatus.ATTACHING, 327 'DET': cellular.UeGsmDataStatus.DETACHING, 328 'ATT': cellular.UeGsmDataStatus.ATTACHED, 329 'STAR': cellular.UeGsmDataStatus.ATTACHING, 330 'END': cellular.UeGsmDataStatus.PDP_DEACTIVATING, 331 'TRAN': cellular.UeGsmDataStatus.PDP_ACTIVE, 332 'PDPAG': cellular.UeGsmDataStatus.PDP_ACTIVATING, 333 'PDP': cellular.UeGsmDataStatus.PDP_ACTIVE, 334 'PDPD': cellular.UeGsmDataStatus.PDP_DEACTIVATING, 335 'DCON': cellular.UeGsmDataStatus.PDP_ACTIVE, 336 'SUSP': cellular.UeGsmDataStatus.IDLE, 337 } 338 339 # http://wireless.agilent.com/rfcomms/refdocs/ 340 # wcdma/wcdma_gen_call_proc_status.html#CJADGAHG 341 CALL_STATUS_DATA_TO_STATUS_WCDMA = { 342 'IDLE': cellular.UeGsmDataStatus.IDLE, 343 'ATTG': cellular.UeGsmDataStatus.ATTACHING, 344 'DET': cellular.UeGsmDataStatus.DETACHING, 345 'OFF': cellular.UeGsmDataStatus.NONE, 346 'PDPAG': cellular.UeGsmDataStatus.PDP_ACTIVATING, 347 'PDP': cellular.UeGsmDataStatus.PDP_ACTIVE, 348 'PDPD': cellular.UeGsmDataStatus.PDP_DEACTIVATING, 349 } 350 351 # http://wireless.agilent.com/rfcomms/refdocs/ 352 # cdma2k/cdma2000_hpib_call_status.html#CJABGBCF 353 CALL_STATUS_DATA_TO_STATUS_CDMA_2000 = { 354 'OFF': cellular.UeC2kDataStatus.OFF, 355 'DORM': cellular.UeC2kDataStatus.DORMANT, 356 'DCON': cellular.UeC2kDataStatus.DATA_CONNECTED, 357 } 358 359 # http://wireless.agilent.com/rfcomms/refdocs/ 360 # 1xevdo/1xevdo_hpib_call_status.html#BABCGBCD 361 CALL_STATUS_DATA_TO_STATUS_EVDO = { 362 'CCL': cellular.UeEvdoDataStatus.CONNECTION_CLOSING, 363 'CNEG': cellular.UeEvdoDataStatus.CONNECTION_NEGOTIATE, 364 'CREQ': cellular.UeEvdoDataStatus.CONNECTION_REQUEST, 365 'DCON': cellular.UeEvdoDataStatus.DATA_CONNECTED, 366 'DORM': cellular.UeEvdoDataStatus.DORMANT, 367 'HAND': cellular.UeEvdoDataStatus.HANDOFF, 368 'IDLE': cellular.UeEvdoDataStatus.IDLE, 369 'PAG': cellular.UeEvdoDataStatus.PAGING, 370 'SCL': cellular.UeEvdoDataStatus.SESSION_CLOSING, 371 'SNEG': cellular.UeEvdoDataStatus.SESSION_NEGOTIATE, 372 'SOP': cellular.UeEvdoDataStatus.SESSION_OPEN, 373 'UREQ': cellular.UeEvdoDataStatus.UATI_REQUEST, 374 } 375 376 #lte status from BSE:STATus:ACELL? on the PXT 377 #OFF | IDLE | CON | REG | 378 #LOOP | REL | UNAV 379 380 CALL_STATUS_DATA_TO_STATUS_LTE = { 381 'OFF': cellular.UeLteDataStatus.OFF, 382 'IDLE': cellular.UeLteDataStatus.IDLE, 383 'CON': cellular.UeLteDataStatus.CONNECTED, 384 'REG': cellular.UeLteDataStatus.REGISTERED, 385 'LOOP': cellular.UeLteDataStatus.LOOPBACK, 386 'REL': cellular.UeLteDataStatus.RELEASE, 387 'UNAV': cellular.UeLteDataStatus.UNAVAILABLE, 388 } 389 FORMAT_TO_DATA_STATUS_TYPE = { 390 '"GSM/GPRS"': CALL_STATUS_DATA_TO_STATUS_GSM_GPRS, 391 '"WCDMA"': CALL_STATUS_DATA_TO_STATUS_WCDMA, 392 '"IS-2000/IS-95/AMPS"': CALL_STATUS_DATA_TO_STATUS_CDMA_2000, 393 '"IS-856"': CALL_STATUS_DATA_TO_STATUS_EVDO, 394 '"LTE"': CALL_STATUS_DATA_TO_STATUS_LTE, 395 } 396