• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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