Home | History | Annotate | Download | only in cellular
      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 
      5 import logging
      6 import re
      7 import time
      8 import common
      9 from autotest_lib.client.cros.cellular import cellular_logging
     10 from autotest_lib.client.cros.cellular import cellular_system_error
     11 from autotest_lib.client.cros.cellular import air_state_verifier
     12 from autotest_lib.client.cros.cellular import base_station_interface
     13 from autotest_lib.client.cros.cellular import cellular
     14 from autotest_lib.client.bin import utils
     15 
     16 POLL_SLEEP = 0.2
     17 
     18 log = cellular_logging.SetupCellularLogging('base_station_8960')
     19 
     20 class 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 
    220 def _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 
    229 class 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
    234 CALL:UPLink:TXPower:LEVel:MAXimum 24
    235 CALL: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("""
    240 CALL:SCHannel:FORWard:DRATe BPS153600
    241 CALL: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("""
    246 CALL:CELL:CONTrol:CATTribute:ISTate:PCCCycle ATSP
    247 # Default data application
    248 CALL:APPLication:SESSion DPAPlication
    249 # Give DUT 100% of channel
    250 CALL:CELL:APPLication:ATDPackets 100
    251 """)
    252 
    253     GPRS_MAX = _Parse("""
    254 call:bch:scel gprs
    255 call:pdtch:mslot:config d1u1
    256 call:cell:tbflow:t3192 ms1500
    257 """)
    258 
    259     EGPRS_MAX = _Parse("""
    260 call:bch:scel egprs
    261 call:pdtch:mslot:config d4u1
    262 call:cell:tbflow:t3192 ms1500
    263 """)
    264 
    265     CAT_08 = _Parse("""
    266 call:pow:stat ON
    267 call:ms:pow:targ 0
    268 call:cell:rlc:rees OFF
    269 call:hsdpa:ms:hsdschannel:cat:control:auto off
    270 call:hsdpa:ms:hsdschannel:cat:man 8
    271 call:hsdpa:service:psdata:hsdschannel:config cqiv
    272 call:hsdpa:service:psdata:cqi 22
    273 call:serv:gprs:rab PHSP
    274 call:serv:rbt:rab HSDP12
    275 call:serv:psd:srb:mapp UEDD
    276 call:hsup:serv:psd:edpd:ccod:max T2T4
    277 call:hsup:edch:tti MS10
    278 call:hsup:serv:psd:ergc:inf:stat Off
    279 """)
    280 
    281     CAT_10 = _Parse("""
    282 call:pow:stat ON
    283 call:ms:pow:targ 0
    284 call:cell:rlc:rees OFF
    285 call:hsdpa:ms:hsdschannel:cat:control:auto off
    286 call:hsdpa:ms:hsdschannel:cat:man 10
    287 call:serv:gprs:rab PHSP
    288 call:serv:rbt:rab HSDP12
    289 call:hsdpa:service:psdata:hsdschannel:config cqiv
    290 call:hsdpa:service:psdata:cqi 22
    291 call:serv:psd:srb:mapp UEDD
    292 call:hsup:serv:psd:edpd:ccod:max T2T4
    293 call:hsup:edch:tti MS2
    294 call:hsup:serv:psd:ergc:inf:stat Off
    295 """)
    296 
    297 class 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