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