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 collections
6import copy
7import logging
8
9from autotest_lib.client.common_lib import error
10from autotest_lib.client.common_lib.cros.network import xmlrpc_security_types
11
12
13class HostapConfig(object):
14    """Parameters for router configuration."""
15
16    # A mapping of frequency to channel number.  This includes some
17    # frequencies used outside the US.
18    CHANNEL_MAP = {2412: 1,
19                   2417: 2,
20                   2422: 3,
21                   2427: 4,
22                   2432: 5,
23                   2437: 6,
24                   2442: 7,
25                   2447: 8,
26                   2452: 9,
27                   2457: 10,
28                   2462: 11,
29                   # 12, 13 are only legitimate outside the US.
30                   2467: 12,
31                   2472: 13,
32                   # 14 is for Japan, DSSS and CCK only.
33                   2484: 14,
34                   # 34 valid in Japan.
35                   5170: 34,
36                   # 36-116 valid in the US, except 38, 42, and 46, which have
37                   # mixed international support.
38                   5180: 36,
39                   5190: 38,
40                   5200: 40,
41                   5210: 42,
42                   5220: 44,
43                   5230: 46,
44                   5240: 48,
45                   5260: 52,
46                   5280: 56,
47                   5300: 60,
48                   5320: 64,
49                   5500: 100,
50                   5520: 104,
51                   5540: 108,
52                   5560: 112,
53                   5580: 116,
54                   # 120, 124, 128 valid in Europe/Japan.
55                   5600: 120,
56                   5620: 124,
57                   5640: 128,
58                   # 132+ valid in US.
59                   5660: 132,
60                   5680: 136,
61                   5700: 140,
62                   # 144 is supported by a subset of WiFi chips
63                   # (e.g. bcm4354, but not ath9k).
64                   5720: 144,
65                   5745: 149,
66                   5765: 153,
67                   5785: 157,
68                   5805: 161,
69                   5825: 165}
70
71    MODE_11A = 'a'
72    MODE_11B = 'b'
73    MODE_11G = 'g'
74    MODE_11N_MIXED = 'n-mixed'
75    MODE_11N_PURE = 'n-only'
76    MODE_11AC_MIXED = 'ac-mixed'
77    MODE_11AC_PURE = 'ac-only'
78
79    N_CAPABILITY_HT20 = object()
80    N_CAPABILITY_HT40 = object()
81    N_CAPABILITY_HT40_PLUS = object()
82    N_CAPABILITY_HT40_MINUS = object()
83    N_CAPABILITY_GREENFIELD = object()
84    N_CAPABILITY_SGI20 = object()
85    N_CAPABILITY_SGI40 = object()
86    ALL_N_CAPABILITIES = [N_CAPABILITY_HT20,
87                          N_CAPABILITY_HT40,
88                          N_CAPABILITY_HT40_PLUS,
89                          N_CAPABILITY_HT40_MINUS,
90                          N_CAPABILITY_GREENFIELD,
91                          N_CAPABILITY_SGI20,
92                          N_CAPABILITY_SGI40]
93
94    AC_CAPABILITY_VHT160 = object()
95    AC_CAPABILITY_VHT160_80PLUS80 = object()
96    AC_CAPABILITY_RXLDPC = object()
97    AC_CAPABILITY_SHORT_GI_80 = object()
98    AC_CAPABILITY_SHORT_GI_160 = object()
99    AC_CAPABILITY_TX_STBC_2BY1 = object()
100    AC_CAPABILITY_RX_STBC_1 = object()
101    AC_CAPABILITY_RX_STBC_12 = object()
102    AC_CAPABILITY_RX_STBC_123 = object()
103    AC_CAPABILITY_RX_STBC_1234 = object()
104    AC_CAPABILITY_SU_BEAMFORMER = object()
105    AC_CAPABILITY_SU_BEAMFORMEE = object()
106    AC_CAPABILITY_BF_ANTENNA_2 = object()
107    AC_CAPABILITY_SOUNDING_DIMENSION_2 = object()
108    AC_CAPABILITY_MU_BEAMFORMER = object()
109    AC_CAPABILITY_MU_BEAMFORMEE = object()
110    AC_CAPABILITY_VHT_TXOP_PS = object()
111    AC_CAPABILITY_HTC_VHT = object()
112    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP0 = object()
113    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP1 = object()
114    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP2 = object()
115    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP3 = object()
116    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP4 = object()
117    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP5 = object()
118    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP6 = object()
119    AC_CAPABILITY_MAX_A_MPDU_LEN_EXP7 = object()
120    AC_CAPABILITY_VHT_LINK_ADAPT2 = object()
121    AC_CAPABILITY_VHT_LINK_ADAPT3 = object()
122    AC_CAPABILITY_RX_ANTENNA_PATTERN = object()
123    AC_CAPABILITY_TX_ANTENNA_PATTERN = object()
124    AC_CAPABILITIES_MAPPING = {
125            AC_CAPABILITY_VHT160: '[VHT160]',
126            AC_CAPABILITY_VHT160_80PLUS80: '[VHT160_80PLUS80]',
127            AC_CAPABILITY_RXLDPC: '[RXLDPC]',
128            AC_CAPABILITY_SHORT_GI_80: '[SHORT_GI_80]',
129            AC_CAPABILITY_SHORT_GI_160: '[SHORT_GI_160]',
130            AC_CAPABILITY_TX_STBC_2BY1: '[TX_STBC_2BY1',
131            AC_CAPABILITY_RX_STBC_1: '[RX_STBC_1]',
132            AC_CAPABILITY_RX_STBC_12: '[RX_STBC_12]',
133            AC_CAPABILITY_RX_STBC_123: '[RX_STBC_123]',
134            AC_CAPABILITY_RX_STBC_1234: '[RX_STBC_1234]',
135            AC_CAPABILITY_SU_BEAMFORMER: '[SU_BEAMFORMER]',
136            AC_CAPABILITY_SU_BEAMFORMEE: '[SU_BEAMFORMEE]',
137            AC_CAPABILITY_BF_ANTENNA_2: '[BF_ANTENNA_2]',
138            AC_CAPABILITY_SOUNDING_DIMENSION_2: '[SOUNDING_DIMENSION_2]',
139            AC_CAPABILITY_MU_BEAMFORMER: '[MU_BEAMFORMER]',
140            AC_CAPABILITY_MU_BEAMFORMEE: '[MU_BEAMFORMEE]',
141            AC_CAPABILITY_VHT_TXOP_PS: '[VHT_TXOP_PS]',
142            AC_CAPABILITY_HTC_VHT: '[HTC_VHT]',
143            AC_CAPABILITY_MAX_A_MPDU_LEN_EXP0: '[MAX_A_MPDU_LEN_EXP0]',
144            AC_CAPABILITY_MAX_A_MPDU_LEN_EXP1: '[MAX_A_MPDU_LEN_EXP1]',
145            AC_CAPABILITY_MAX_A_MPDU_LEN_EXP2: '[MAX_A_MPDU_LEN_EXP2]',
146            AC_CAPABILITY_MAX_A_MPDU_LEN_EXP3: '[MAX_A_MPDU_LEN_EXP3]',
147            AC_CAPABILITY_MAX_A_MPDU_LEN_EXP4: '[MAX_A_MPDU_LEN_EXP4]',
148            AC_CAPABILITY_MAX_A_MPDU_LEN_EXP5: '[MAX_A_MPDU_LEN_EXP5]',
149            AC_CAPABILITY_MAX_A_MPDU_LEN_EXP6: '[MAX_A_MPDU_LEN_EXP6]',
150            AC_CAPABILITY_MAX_A_MPDU_LEN_EXP7: '[MAX_A_MPDU_LEN_EXP7]',
151            AC_CAPABILITY_VHT_LINK_ADAPT2: '[VHT_LINK_ADAPT2]',
152            AC_CAPABILITY_VHT_LINK_ADAPT3: '[VHT_LINK_ADAPT3]',
153            AC_CAPABILITY_RX_ANTENNA_PATTERN: '[RX_ANTENNA_PATTERN]',
154            AC_CAPABILITY_TX_ANTENNA_PATTERN: '[TX_ANTENNA_PATTERN]'}
155
156    VHT_CHANNEL_WIDTH_40 = object()
157    VHT_CHANNEL_WIDTH_80 = object()
158    VHT_CHANNEL_WIDTH_160 = object()
159    VHT_CHANNEL_WIDTH_80_80 = object()
160
161    # This is a loose merging of the rules for US and EU regulatory
162    # domains as taken from IEEE Std 802.11-2012 Appendix E.  For instance,
163    # we tolerate HT40 in channels 149-161 (not allowed in EU), but also
164    # tolerate HT40+ on channel 7 (not allowed in the US).  We take the loose
165    # definition so that we don't prohibit testing in either domain.
166    HT40_ALLOW_MAP = {N_CAPABILITY_HT40_MINUS: range(6, 14) +
167                                               range(40, 65, 8) +
168                                               range(104, 137, 8) +
169                                               [153, 161],
170                      N_CAPABILITY_HT40_PLUS: range(1, 8) +
171                                              range(36, 61, 8) +
172                                              range(100, 133, 8) +
173                                              [149, 157]}
174
175    PMF_SUPPORT_DISABLED = 0
176    PMF_SUPPORT_ENABLED = 1
177    PMF_SUPPORT_REQUIRED = 2
178    PMF_SUPPORT_VALUES = (PMF_SUPPORT_DISABLED,
179                          PMF_SUPPORT_ENABLED,
180                          PMF_SUPPORT_REQUIRED)
181
182    DRIVER_NAME = 'nl80211'
183
184
185    @staticmethod
186    def get_channel_for_frequency(frequency):
187        """Returns the channel number associated with a given frequency.
188
189        @param value: int frequency in MHz.
190
191        @return int frequency associated with the channel.
192
193        """
194        return HostapConfig.CHANNEL_MAP[frequency]
195
196
197    @staticmethod
198    def get_frequency_for_channel(channel):
199        """Returns the frequency associated with a given channel number.
200
201        @param value: int channel number.
202
203        @return int frequency in MHz associated with the channel.
204
205        """
206        for frequency, channel_iter in HostapConfig.CHANNEL_MAP.iteritems():
207            if channel == channel_iter:
208                return frequency
209        else:
210            raise error.TestFail('Unknown channel value: %r.' % channel)
211
212
213    @property
214    def _get_default_config(self):
215        """@return dict of default options for hostapd."""
216        return collections.OrderedDict([
217                ('hw_mode', 'g'),
218                ('logger_syslog', '-1'),
219                ('logger_syslog_level', '0'),
220                # default RTS and frag threshold to ``off''
221                ('rts_threshold', '2347'),
222                ('fragm_threshold', '2346'),
223                ('driver', self.DRIVER_NAME)])
224
225
226    @property
227    def _ht40_plus_allowed(self):
228        """@return True iff HT40+ is enabled for this configuration."""
229        channel_supported = (self.channel in
230                             self.HT40_ALLOW_MAP[self.N_CAPABILITY_HT40_PLUS])
231        return ((self.N_CAPABILITY_HT40_PLUS in self._n_capabilities or
232                 self.N_CAPABILITY_HT40 in self._n_capabilities) and
233                channel_supported)
234
235
236    @property
237    def _ht40_minus_allowed(self):
238        """@return True iff HT40- is enabled for this configuration."""
239        channel_supported = (self.channel in
240                             self.HT40_ALLOW_MAP[self.N_CAPABILITY_HT40_MINUS])
241        return ((self.N_CAPABILITY_HT40_MINUS in self._n_capabilities or
242                 self.N_CAPABILITY_HT40 in self._n_capabilities) and
243                channel_supported)
244
245
246    @property
247    def _hostapd_ht_capabilities(self):
248        """@return string suitable for the ht_capab= line in a hostapd config"""
249        ret = []
250        if self._ht40_plus_allowed:
251            ret.append('[HT40+]')
252        elif self._ht40_minus_allowed:
253            ret.append('[HT40-]')
254        if self.N_CAPABILITY_GREENFIELD in self._n_capabilities:
255            logging.warning('Greenfield flag is ignored for hostap...')
256        if self.N_CAPABILITY_SGI20 in self._n_capabilities:
257            ret.append('[SHORT-GI-20]')
258        if self.N_CAPABILITY_SGI40 in self._n_capabilities:
259            ret.append('[SHORT-GI-40]')
260        return ''.join(ret)
261
262
263    @property
264    def _hostapd_vht_capabilities(self):
265        """@return string suitable for the vht_capab= line in a hostapd config.
266        """
267        ret = []
268        for cap in self.AC_CAPABILITIES_MAPPING.keys():
269            if cap in self._ac_capabilities:
270                ret.append(self.AC_CAPABILITIES_MAPPING[cap])
271        return ''.join(ret)
272
273
274    @property
275    def _require_ht(self):
276        """@return True iff clients should be required to support HT."""
277        # TODO(wiley) Why? (crbug.com/237370)
278        logging.warning('Not enforcing pure N mode because Snow does '
279                        'not seem to support it...')
280        return False
281
282
283    @property
284    def _require_vht(self):
285        """@return True iff clients should be required to support VHT."""
286        return self._mode == self.MODE_11AC_PURE
287
288
289    @property
290    def _hw_mode(self):
291        """@return string hardware mode understood by hostapd."""
292        if self._mode == self.MODE_11A:
293            return self.MODE_11A
294        if self._mode == self.MODE_11B:
295            return self.MODE_11B
296        if self._mode == self.MODE_11G:
297            return self.MODE_11G
298        if self._is_11n or self.is_11ac:
299            # For their own historical reasons, hostapd wants it this way.
300            if self._frequency > 5000:
301                return self.MODE_11A
302
303            return self.MODE_11G
304
305        raise error.TestFail('Invalid mode.')
306
307
308    @property
309    def _is_11n(self):
310        """@return True iff we're trying to host an 802.11n network."""
311        return self._mode in (self.MODE_11N_MIXED, self.MODE_11N_PURE)
312
313
314    @property
315    def is_11ac(self):
316        """@return True iff we're trying to host an 802.11ac network."""
317        return self._mode in (self.MODE_11AC_MIXED, self.MODE_11AC_PURE)
318
319
320    @property
321    def channel(self):
322        """@return int channel number for self.frequency."""
323        return self.get_channel_for_frequency(self.frequency)
324
325
326    @channel.setter
327    def channel(self, value):
328        """Sets the channel number to configure hostapd to listen on.
329
330        @param value: int channel number.
331
332        """
333        self.frequency = self.get_frequency_for_channel(value)
334
335
336    @property
337    def frequency(self):
338        """@return int frequency for hostapd to listen on."""
339        return self._frequency
340
341
342    @frequency.setter
343    def frequency(self, value):
344        """Sets the frequency for hostapd to listen on.
345
346        @param value: int frequency in MHz.
347
348        """
349        if value not in self.CHANNEL_MAP or not self.supports_frequency(value):
350            raise error.TestFail('Tried to set an invalid frequency: %r.' %
351                                 value)
352
353        self._frequency = value
354
355
356    @property
357    def ssid(self):
358        """@return string SSID."""
359        return self._ssid
360
361
362    @ssid.setter
363    def ssid(self, value):
364        """Sets the ssid for the hostapd.
365
366        @param value: string ssid name.
367
368        """
369        self._ssid = value
370
371
372    @property
373    def ht_packet_capture_mode(self):
374        """Get an appropriate packet capture HT parameter.
375
376        When we go to configure a raw monitor we need to configure
377        the phy to listen on the correct channel.  Part of doing
378        so is to specify the channel width for HT channels.  In the
379        case that the AP is configured to be either HT40+ or HT40-,
380        we could return the wrong parameter because we don't know which
381        configuration will be chosen by hostap.
382
383        @return string HT parameter for frequency configuration.
384
385        """
386        if not self._is_11n:
387            return None
388
389        if self._ht40_plus_allowed:
390            return 'HT40+'
391
392        if self._ht40_minus_allowed:
393            return 'HT40-'
394
395        return 'HT20'
396
397
398    @property
399    def perf_loggable_description(self):
400        """@return string test description suitable for performance logging."""
401        mode = 'mode%s' % (
402                self.printable_mode.replace('+', 'p').replace('-', 'm'))
403        channel = 'ch%03d' % self.channel
404        return '_'.join([channel, mode, self._security_config.security])
405
406
407    @property
408    def printable_mode(self):
409        """@return human readable mode string."""
410        if self._is_11n:
411            return self.ht_packet_capture_mode
412
413        return '11' + self._hw_mode.upper()
414
415
416    @property
417    def ssid_suffix(self):
418        """@return meaningful suffix for SSID."""
419        return 'ch%d' % self.channel
420
421
422    @property
423    def security_config(self):
424        """@return SecurityConfig security config object"""
425        return self._security_config
426
427
428    @property
429    def hide_ssid(self):
430        """@return bool _hide_ssid flag."""
431        return self._hide_ssid
432
433
434    @property
435    def beacon_footer(self):
436        """@return bool _beacon_footer value."""
437        return self._beacon_footer
438
439
440    @property
441    def scenario_name(self):
442        """@return string _scenario_name value, or None."""
443        return self._scenario_name
444
445
446    @property
447    def min_streams(self):
448        """@return int _min_streams value, or None."""
449        return self._min_streams
450
451
452    @property
453    def frag_threshold(self):
454        """@return int frag threshold value, or None."""
455        return self._frag_threshold
456
457
458    def __init__(self, mode=MODE_11B, channel=None, frequency=None,
459                 n_capabilities=[], hide_ssid=None, beacon_interval=None,
460                 dtim_period=None, frag_threshold=None, ssid=None, bssid=None,
461                 force_wmm=None, security_config=None,
462                 pmf_support=PMF_SUPPORT_DISABLED,
463                 obss_interval=None,
464                 vht_channel_width=None,
465                 vht_center_channel=None,
466                 ac_capabilities=[],
467                 beacon_footer='',
468                 spectrum_mgmt_required=None,
469                 scenario_name=None,
470                 min_streams=None):
471        """Construct a HostapConfig.
472
473        You may specify channel or frequency, but not both.  Both options
474        are checked for validity (i.e. you can't specify an invalid channel
475        or a frequency that will not be accepted).
476
477        @param mode string MODE_11x defined above.
478        @param channel int channel number.
479        @param frequency int frequency of channel.
480        @param n_capabilities list of N_CAPABILITY_x defined above.
481        @param hide_ssid True if we should set up a hidden SSID.
482        @param beacon_interval int beacon interval of AP.
483        @param dtim_period int include a DTIM every |dtim_period| beacons.
484        @param frag_threshold int maximum outgoing data frame size.
485        @param ssid string up to 32 byte SSID overriding the router default.
486        @param bssid string like 00:11:22:33:44:55.
487        @param force_wmm True if we should force WMM on, False if we should
488            force it off, None if we shouldn't force anything.
489        @param security_config SecurityConfig object.
490        @param pmf_support one of PMF_SUPPORT_* above.  Controls whether the
491            client supports/must support 802.11w.
492        @param obss_interval int interval in seconds that client should be
493            required to do background scans for overlapping BSSes.
494        @param vht_channel_width object channel width
495        @param vht_center_channel int center channel of segment 0.
496        @param ac_capabilities list of AC_CAPABILITY_x defined above.
497        @param beacon_footer string containing (unvalidated) IE data to be
498            placed at the end of the beacon.
499        @param spectrum_mgmt_required True if we require the DUT to support
500            spectrum management.
501        @param scenario_name string to be included in file names, instead
502            of the interface name.
503        @param min_streams int number of spatial streams required.
504
505        """
506        super(HostapConfig, self).__init__()
507        if channel is not None and frequency is not None:
508            raise error.TestError('Specify either frequency or channel '
509                                  'but not both.')
510
511        self._wmm_enabled = False
512        unknown_caps = [cap for cap in n_capabilities
513                        if cap not in self.ALL_N_CAPABILITIES]
514        if unknown_caps:
515            raise error.TestError('Unknown capabilities: %r' % unknown_caps)
516
517        self._n_capabilities = set(n_capabilities)
518        if self._n_capabilities:
519            self._wmm_enabled = True
520        if self._n_capabilities and mode is None:
521            mode = self.MODE_11N_PURE
522        self._mode = mode
523
524        self._frequency = None
525        if channel:
526            self.channel = channel
527        elif frequency:
528            self.frequency = frequency
529        else:
530            raise error.TestError('Specify either frequency or channel.')
531
532        if not self.supports_frequency(self.frequency):
533            raise error.TestFail('Configured a mode %s that does not support '
534                                 'frequency %d' % (self._mode, self.frequency))
535
536        self._hide_ssid = hide_ssid
537        self._beacon_interval = beacon_interval
538        self._dtim_period = dtim_period
539        self._frag_threshold = frag_threshold
540        if ssid and len(ssid) > 32:
541            raise error.TestFail('Tried to specify SSID that was too long.')
542
543        self._ssid = ssid
544        self._bssid = bssid
545        if force_wmm is not None:
546            self._wmm_enabled = force_wmm
547        if pmf_support not in self.PMF_SUPPORT_VALUES:
548            raise error.TestFail('Invalid value for pmf_support: %r' %
549                                 pmf_support)
550
551        self._pmf_support = pmf_support
552        self._security_config = (copy.copy(security_config) or
553                                xmlrpc_security_types.SecurityConfig())
554        self._obss_interval = obss_interval
555        if vht_channel_width == self.VHT_CHANNEL_WIDTH_40:
556            self._vht_oper_chwidth = 0
557        elif vht_channel_width == self.VHT_CHANNEL_WIDTH_80:
558            self._vht_oper_chwidth = 1
559        elif vht_channel_width == self.VHT_CHANNEL_WIDTH_160:
560            self._vht_oper_chwidth = 2
561        elif vht_channel_width == self.VHT_CHANNEL_WIDTH_80_80:
562            self._vht_oper_chwidth = 3
563        elif vht_channel_width is not None:
564            raise error.TestFail('Invalid channel width')
565        # TODO(zqiu) Add checking for center channel based on the channel width
566        # and operating channel.
567        self._vht_oper_centr_freq_seg0_idx = vht_center_channel
568        self._ac_capabilities = set(ac_capabilities)
569        self._beacon_footer = beacon_footer
570        self._spectrum_mgmt_required = spectrum_mgmt_required
571        self._scenario_name = scenario_name
572        self._min_streams = min_streams
573
574
575    def __repr__(self):
576        return ('%s(mode=%r, channel=%r, frequency=%r, '
577                'n_capabilities=%r, hide_ssid=%r, beacon_interval=%r, '
578                'dtim_period=%r, frag_threshold=%r, ssid=%r, bssid=%r, '
579                'wmm_enabled=%r, security_config=%r, '
580                'spectrum_mgmt_required=%r)' % (
581                        self.__class__.__name__,
582                        self._mode,
583                        self.channel,
584                        self.frequency,
585                        self._n_capabilities,
586                        self._hide_ssid,
587                        self._beacon_interval,
588                        self._dtim_period,
589                        self._frag_threshold,
590                        self._ssid,
591                        self._bssid,
592                        self._wmm_enabled,
593                        self._security_config,
594                        self._spectrum_mgmt_required))
595
596
597    def supports_channel(self, value):
598        """Check whether channel is supported by the current hardware mode.
599
600        @param value: int channel to check.
601        @return True iff the current mode supports the band of the channel.
602
603        """
604        for freq, channel in self.CHANNEL_MAP.iteritems():
605            if channel == value:
606                return self.supports_frequency(freq)
607
608        return False
609
610
611    def supports_frequency(self, frequency):
612        """Check whether frequency is supported by the current hardware mode.
613
614        @param frequency: int frequency to check.
615        @return True iff the current mode supports the band of the frequency.
616
617        """
618        if self._mode == self.MODE_11A and frequency < 5000:
619            return False
620
621        if self._mode in (self.MODE_11B, self.MODE_11G) and frequency > 5000:
622            return False
623
624        if frequency not in self.CHANNEL_MAP:
625            return False
626
627        channel = self.CHANNEL_MAP[frequency]
628        supports_plus = (channel in
629                         self.HT40_ALLOW_MAP[self.N_CAPABILITY_HT40_PLUS])
630        supports_minus = (channel in
631                          self.HT40_ALLOW_MAP[self.N_CAPABILITY_HT40_MINUS])
632        if (self.N_CAPABILITY_HT40_PLUS in self._n_capabilities and
633                not supports_plus):
634            return False
635
636        if (self.N_CAPABILITY_HT40_MINUS in self._n_capabilities and
637                not supports_minus):
638            return False
639
640        if (self.N_CAPABILITY_HT40 in self._n_capabilities and
641                not supports_plus and not supports_minus):
642            return False
643
644        return True
645
646
647    def generate_dict(self, interface, control_interface, ssid):
648        """Generate config dictionary.
649
650        Generate config dictionary for the given |interface|.
651
652        @param interface: string interface to generate config dict for.
653        @param control_interface: string control interface
654        @param ssid: string SSID of the AP.
655        @return dict of hostap configurations.
656
657        """
658        # Start with the default config parameters.
659        conf = self._get_default_config
660        conf['ssid'] = (self._ssid or ssid)
661        if self._bssid:
662            conf['bssid'] = self._bssid
663        conf['channel'] = self.channel
664        conf['hw_mode'] = self._hw_mode
665        if self._hide_ssid:
666            conf['ignore_broadcast_ssid'] = 1
667        if self._is_11n or self.is_11ac:
668            conf['ieee80211n'] = 1
669            conf['ht_capab'] = self._hostapd_ht_capabilities
670        if self.is_11ac:
671            conf['ieee80211ac'] = 1
672            conf['vht_oper_chwidth'] = self._vht_oper_chwidth
673            conf['vht_oper_centr_freq_seg0_idx'] = \
674                    self._vht_oper_centr_freq_seg0_idx
675            conf['vht_capab'] = self._hostapd_vht_capabilities
676        if self._wmm_enabled:
677            conf['wmm_enabled'] = 1
678        if self._require_ht:
679            conf['require_ht'] = 1
680        if self._require_vht:
681            conf['require_vht'] = 1
682        if self._beacon_interval:
683            conf['beacon_int'] = self._beacon_interval
684        if self._dtim_period:
685            conf['dtim_period'] = self._dtim_period
686        if self._frag_threshold:
687            conf['fragm_threshold'] = self._frag_threshold
688        if self._pmf_support:
689            conf['ieee80211w'] = self._pmf_support
690        if self._obss_interval:
691            conf['obss_interval'] = self._obss_interval
692        conf['interface'] = interface
693        conf['ctrl_interface'] = control_interface
694        if self._spectrum_mgmt_required:
695            # To set spectrum_mgmt_required, we must first set
696            # local_pwr_constraint. And to set local_pwr_constraint,
697            # we must first set ieee80211d. And to set ieee80211d, ...
698            # Point being: order matters here.
699            conf['country_code'] = 'US'  # Required for local_pwr_constraint
700            conf['ieee80211d'] = 1  # Required for local_pwr_constraint
701            conf['local_pwr_constraint'] = 0  # No local constraint
702            conf['spectrum_mgmt_required'] = 1  # Requires local_pwr_constraint
703        conf.update(self._security_config.get_hostapd_config())
704        return conf
705