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 logging
6import os
7import pprint
8import sys
9
10import wardmodem_exceptions as wme
11
12CONF_DIR_NAME = 'configurations'
13DEFAULT_CONF_FILE = 'base.conf'
14MODEM_CONF_FILE = {
15        'e362': 'e362.conf',
16}
17
18class ModemConfiguration(object):
19    """
20    All modem specific configuration needed by WardModem.
21
22    This class serves the dual purpose of loading the configuration data needed
23    by different parts of wardmodem in a single place, and providing
24    documentation regarding the configuration parameters available in the file.
25
26    """
27
28    def __init__(self, modem=None):
29        """
30        @param modem The modem for which the configuration needs to be loaded.
31                |modem| can be None. In that case, only the configuration for
32                the base modem is loaded.
33                Otherwise, |modem| must be a key in |MODEM_CONF_FILE|.
34        """
35        self._logger = logging.getLogger(__name__)
36
37        if modem and modem not in MODEM_CONF_FILE:
38            raise wme.WardModemSetupException('Unknown modem: |%s|' % modem)
39
40        # TODO(pprabhu) Figure out if it makes sense to use Configurable to
41        # (de)serialize configuration. See crbug.com/252475
42        # First load the default configuration
43        self._logger.info('Loading basic configuration.')
44        self.base_conf = self._load_conf(DEFAULT_CONF_FILE)
45        self._logger.debug('Basic configuration:\n%s',
46                           pprint.pformat(self.base_conf))
47
48        # Now load the plugin conf data.
49        self.plugin_conf = {}
50        if modem:
51            self._logger.info('Loading modem specific configuration for modem '
52                              '|%s|', modem)
53            self.plugin_conf = self._load_conf(MODEM_CONF_FILE[modem])
54        self._logger.debug('Plugin configuration:\n%s',
55                           pprint.pformat(self.plugin_conf))
56
57        self._populate_config()
58
59
60    def _populate_config(self):
61        """
62        Assign configuration data loaded into self variable for easy access.
63
64        """
65        # The basic map from AT commands to wardmodem actions common to all
66        # modems.
67        self.base_at_to_wm_action_map = self.base_conf['at_to_wm_action_map']
68
69        # The map from AT commands to wardmodem actions specific to the current
70        # modem.
71        self.plugin_at_to_wm_action_map = (
72                self.plugin_conf.get('at_to_wm_action_map', {}))
73
74        # The basic map from wardmodem responses to AT commands common to all
75        # modems.
76        self.base_wm_response_to_at_map = (
77                self.base_conf['wm_response_to_at_map'])
78
79        # The map from wardmodem responses to AT commands specific to the
80        # current modem.
81        self.plugin_wm_response_to_at_map = (
82                self.plugin_conf.get('wm_response_to_at_map', {}))
83
84        # State-less request response map.
85        self.base_wm_request_response_map = (
86                self.base_conf.get('wm_request_response_map', {}))
87        self.plugin_wm_request_response_map = (
88                self.plugin_conf.get('wm_request_response_map', {}))
89
90        # The state machines loaded by all modems.
91        self.base_state_machines = self.base_conf['state_machines']
92
93        # The state machines specific to the current modem.
94        self.plugin_state_machines =  self.plugin_conf.get('state_machines', [])
95
96        # The fallback state machine for unmatched AT commands.
97        self._load_variable('fallback_machine', strict=False, default='')
98        self._load_variable('fallback_function', strict=False, default='')
99
100
101        # The modemmanager plugin to be used for the modem.
102        self._load_variable('mm_plugin')
103
104        # The string to be prepended to all AT commands from modemmanager to
105        # modem.
106        self._load_variable('mm_to_modem_at_prefix')
107
108        # The string to be appended to all AT commands from modemmanager to
109        # modem.
110        self._load_variable('mm_to_modem_at_suffix')
111
112        # The string to be prepended to all AT commands from modem to
113        # modemmanager.
114        self._load_variable('modem_to_mm_at_prefix')
115
116        # The string to be appended to all AT commands from modem to
117        # modemmanager.
118        self._load_variable('modem_to_mm_at_suffix')
119
120        # ######################################################################
121        # Configuration data for various state machines.
122        self._load_variable('modem_power_level_allowed_levels')
123        self._load_variable('modem_power_level_initial_level')
124        self._load_variable('modem_power_level_reset_by_default')
125
126        self._load_variable('network_identity_default_mcc')
127        self._load_variable('network_identity_default_mnc')
128        self._load_variable('network_identity_default_msin')
129        self._load_variable('network_identity_default_mdn')
130
131        self._load_variable('network_operators')
132        self._load_variable('network_operator_default_index')
133
134        self._load_variable('level_indicators_items')
135        self._load_variable('level_indicators_defaults')
136
137
138    def _load_variable(self, varname, strict=True, default=None):
139        """
140        Load a variable from the configuration files.
141
142        Implement the most common way of loading variables from configuration.
143
144        @param varname: The name of the variable to load.
145
146        @param strict: If True, we expect some value to be available.
147
148        @param default: Value to assign if none can be loaded. Only makes sense
149                when |strint| is False.
150
151        """
152        if strict:
153            value = self.plugin_conf.get(varname, self.base_conf[varname])
154        else:
155            value = self.plugin_conf.get(varname,
156                                         self.base_conf.get(varname, default))
157        setattr(self, varname, value)
158
159
160    def _load_conf(self, conf_file):
161        """
162        Load the configuration data from file.
163
164        @param conf_file Name of the file to load from.
165
166        @return The conf data loaded from file.
167
168        """
169        # The configuration file is an executable python file. Since the file
170        # name is known only at run-time, we must find the module directory and
171        # manually point execfile to the directory for loading the configuration
172        # file.
173        current_module = sys.modules[__name__]
174        dir_name = os.path.dirname(current_module.__file__)
175        full_path = os.path.join(dir_name, CONF_DIR_NAME, conf_file)
176        conf = {}
177        execfile(full_path, conf)
178        # These entries added by execfile are a nuisance in pprint'ing
179        del conf['__builtins__']
180        return conf
181