autotest: Add test support for 802.11ac
Update hostapd_config to support 802.11ac, and add a SimpleConnect test
for it as well. For routers without 802.11ac support, TEST_NA will be
raised.
Test is performed using Panther router with WP2, which only supports
channel width of 80MHz.
The support for 802.11ac AP for Panther requires test-ap-group build #10
or newer.
BUG=chromium:419930
TEST=Run network_WiFi_SimpleConnect.wifi_check5VHT80
Change-Id: I3fd6bc834a18d0b33af79061a8a3c2f35d0f88ec
Reviewed-on: https://chromium-review.googlesource.com/224118
Reviewed-by: Peter Qiu <[email protected]>
Tested-by: Peter Qiu <[email protected]>
Reviewed-by: Paul Stewart <[email protected]>
Commit-Queue: Peter Qiu <[email protected]>
diff --git a/client/common_lib/cros/network/iw_runner.py b/client/common_lib/cros/network/iw_runner.py
index 256fff2..be95ff2 100644
--- a/client/common_lib/cros/network/iw_runner.py
+++ b/client/common_lib/cros/network/iw_runner.py
@@ -46,7 +46,7 @@
IwPhy = collections.namedtuple(
'Phy', ['name', 'bands', 'modes', 'commands', 'max_scan_ssids',
'avail_tx_antennas', 'avail_rx_antennas',
- 'supports_setting_antenna_mask'])
+ 'supports_setting_antenna_mask', 'support_vht'])
DEFAULT_COMMAND_IW = 'iw'
@@ -312,7 +312,8 @@
pending_phy_max_scan_ssids,
pending_phy_tx_antennas,
pending_phy_rx_antennas,
- pending_phy_tx_antennas and pending_phy_rx_antennas)
+ pending_phy_tx_antennas and pending_phy_rx_antennas,
+ pending_phy_support_vht)
all_phys.append(new_phy)
for line in output.splitlines():
@@ -327,6 +328,7 @@
pending_phy_max_scan_ssids = None
pending_phy_tx_antennas = 0
pending_phy_rx_antennas = 0
+ pending_phy_support_vht = False
continue
match_section = re.match('\s*(\w.*):\s*$', line)
@@ -362,6 +364,11 @@
pending_phy_commands.append(command_match.group(1))
continue
+ if current_section.startswith('VHT Capabilities') and \
+ pending_phy_name:
+ pending_phy_support_vht = True
+ continue
+
match_avail_antennas = re.match('\s*Available Antennas: TX (\S+)'
' RX (\S+)', line)
if match_avail_antennas and pending_phy_name:
diff --git a/server/cros/network/hostap_config.py b/server/cros/network/hostap_config.py
index bf6bac8..6e3ecc2 100644
--- a/server/cros/network/hostap_config.py
+++ b/server/cros/network/hostap_config.py
@@ -67,6 +67,8 @@
MODE_11G = 'g'
MODE_11N_MIXED = 'n-mixed'
MODE_11N_PURE = 'n-only'
+ MODE_11AC_MIXED = 'ac-mixed'
+ MODE_11AC_PURE = 'ac-only'
N_CAPABILITY_HT20 = object()
N_CAPABILITY_HT40 = object()
@@ -83,6 +85,73 @@
N_CAPABILITY_SGI20,
N_CAPABILITY_SGI40]
+ AC_CAPABILITY_VHT160 = object()
+ AC_CAPABILITY_VHT160_80PLUS80 = object()
+ AC_CAPABILITY_RXLDPC = object()
+ AC_CAPABILITY_SHORT_GI_80 = object()
+ AC_CAPABILITY_SHORT_GI_160 = object()
+ AC_CAPABILITY_TX_STBC_2BY1 = object()
+ AC_CAPABILITY_RX_STBC_1 = object()
+ AC_CAPABILITY_RX_STBC_12 = object()
+ AC_CAPABILITY_RX_STBC_123 = object()
+ AC_CAPABILITY_RX_STBC_1234 = object()
+ AC_CAPABILITY_SU_BEAMFORMER = object()
+ AC_CAPABILITY_SU_BEAMFORMEE = object()
+ AC_CAPABILITY_BF_ANTENNA_2 = object()
+ AC_CAPABILITY_SOUNDING_DIMENSION_2 = object()
+ AC_CAPABILITY_MU_BEAMFORMER = object()
+ AC_CAPABILITY_MU_BEAMFORMEE = object()
+ AC_CAPABILITY_VHT_TXOP_PS = object()
+ AC_CAPABILITY_HTC_VHT = object()
+ AC_CAPABILITY_MAX_A_MPDU_LEN_EXP0 = object()
+ AC_CAPABILITY_MAX_A_MPDU_LEN_EXP1 = object()
+ AC_CAPABILITY_MAX_A_MPDU_LEN_EXP2 = object()
+ AC_CAPABILITY_MAX_A_MPDU_LEN_EXP3 = object()
+ AC_CAPABILITY_MAX_A_MPDU_LEN_EXP4 = object()
+ AC_CAPABILITY_MAX_A_MPDU_LEN_EXP5 = object()
+ AC_CAPABILITY_MAX_A_MPDU_LEN_EXP6 = object()
+ AC_CAPABILITY_MAX_A_MPDU_LEN_EXP7 = object()
+ AC_CAPABILITY_VHT_LINK_ADAPT2 = object()
+ AC_CAPABILITY_VHT_LINK_ADAPT3 = object()
+ AC_CAPABILITY_RX_ANTENNA_PATTERN = object()
+ AC_CAPABILITY_TX_ANTENNA_PATTERN = object()
+ AC_CAPABILITIES_MAPPING = {
+ AC_CAPABILITY_VHT160: '[VHT160]',
+ AC_CAPABILITY_VHT160_80PLUS80: '[VHT160_80PLUS80]',
+ AC_CAPABILITY_RXLDPC: '[RXLDPC]',
+ AC_CAPABILITY_SHORT_GI_80: '[SHORT_GI_80]',
+ AC_CAPABILITY_SHORT_GI_160: '[SHORT_GI_160]',
+ AC_CAPABILITY_TX_STBC_2BY1: '[TX_STBC_2BY1',
+ AC_CAPABILITY_RX_STBC_1: '[RX_STBC_1]',
+ AC_CAPABILITY_RX_STBC_12: '[RX_STBC_12]',
+ AC_CAPABILITY_RX_STBC_123: '[RX_STBC_123]',
+ AC_CAPABILITY_RX_STBC_1234: '[RX_STBC_1234]',
+ AC_CAPABILITY_SU_BEAMFORMER: '[SU_BEAMFORMER]',
+ AC_CAPABILITY_SU_BEAMFORMEE: '[SU_BEAMFORMEE]',
+ AC_CAPABILITY_BF_ANTENNA_2: '[BF_ANTENNA_2]',
+ AC_CAPABILITY_SOUNDING_DIMENSION_2: '[SOUNDING_DIMENSION_2]',
+ AC_CAPABILITY_MU_BEAMFORMER: '[MU_BEAMFORMER]',
+ AC_CAPABILITY_MU_BEAMFORMEE: '[MU_BEAMFORMEE]',
+ AC_CAPABILITY_VHT_TXOP_PS: '[VHT_TXOP_PS]',
+ AC_CAPABILITY_HTC_VHT: '[HTC_VHT]',
+ AC_CAPABILITY_MAX_A_MPDU_LEN_EXP0: '[MAX_A_MPDU_LEN_EXP0]',
+ AC_CAPABILITY_MAX_A_MPDU_LEN_EXP1: '[MAX_A_MPDU_LEN_EXP1]',
+ AC_CAPABILITY_MAX_A_MPDU_LEN_EXP2: '[MAX_A_MPDU_LEN_EXP2]',
+ AC_CAPABILITY_MAX_A_MPDU_LEN_EXP3: '[MAX_A_MPDU_LEN_EXP3]',
+ AC_CAPABILITY_MAX_A_MPDU_LEN_EXP4: '[MAX_A_MPDU_LEN_EXP4]',
+ AC_CAPABILITY_MAX_A_MPDU_LEN_EXP5: '[MAX_A_MPDU_LEN_EXP5]',
+ AC_CAPABILITY_MAX_A_MPDU_LEN_EXP6: '[MAX_A_MPDU_LEN_EXP6]',
+ AC_CAPABILITY_MAX_A_MPDU_LEN_EXP7: '[MAX_A_MPDU_LEN_EXP7]',
+ AC_CAPABILITY_VHT_LINK_ADAPT2: '[VHT_LINK_ADAPT2]',
+ AC_CAPABILITY_VHT_LINK_ADAPT3: '[VHT_LINK_ADAPT3]',
+ AC_CAPABILITY_RX_ANTENNA_PATTERN: '[RX_ANTENNA_PATTERN]',
+ AC_CAPABILITY_TX_ANTENNA_PATTERN: '[TX_ANTENNA_PATTERN]'}
+
+ VHT_CHANNEL_WIDTH_40 = object()
+ VHT_CHANNEL_WIDTH_80 = object()
+ VHT_CHANNEL_WIDTH_160 = object()
+ VHT_CHANNEL_WIDTH_80_80 = object()
+
# This is a loose merging of the rules for US and EU regulatory
# domains as taken from IEEE Std 802.11-2012 Appendix E. For instance,
# we tolerate HT40 in channels 149-161 (not allowed in EU), but also
@@ -185,6 +254,17 @@
@property
+ def _hostapd_vht_capabilities(self):
+ """@return string suitable for the vht_capab= line in a hostapd config.
+ """
+ ret = []
+ for cap in self.AC_CAPABILITIES_MAPPING.keys():
+ if cap in self._ac_capabilities:
+ ret.append(self.AC_CAPABILITIES_MAPPING[cap])
+ return ''.join(ret)
+
+
+ @property
def _require_ht(self):
"""@return True iff clients should be required to support HT."""
# TODO(wiley) Why? (crbug.com/237370)
@@ -194,6 +274,12 @@
@property
+ def _require_vht(self):
+ """@return True iff clients should be required to support VHT."""
+ return self._mode == self.MODE_11AC_PURE
+
+
+ @property
def _hw_mode(self):
"""@return string hardware mode understood by hostapd."""
if self._mode == self.MODE_11A:
@@ -202,7 +288,7 @@
return self.MODE_11B
if self._mode == self.MODE_11G:
return self.MODE_11G
- if self._mode in (self.MODE_11N_MIXED, self.MODE_11N_PURE):
+ if self._is_11n or self.is_11ac:
# For their own historical reasons, hostapd wants it this way.
if self._frequency > 5000:
return self.MODE_11A
@@ -219,6 +305,12 @@
@property
+ def is_11ac(self):
+ """@return True iff we're trying to host an 802.11ac network."""
+ return self._mode in (self.MODE_11AC_MIXED, self.MODE_11AC_PURE)
+
+
+ @property
def channel(self):
"""@return int channel number for self.frequency."""
return self.get_channel_for_frequency(self.frequency)
@@ -337,7 +429,10 @@
dtim_period=None, frag_threshold=None, ssid=None, bssid=None,
force_wmm=None, security_config=None,
pmf_support=PMF_SUPPORT_DISABLED,
- obss_interval=None):
+ obss_interval=None,
+ vht_channel_width=None,
+ vht_center_channel=None,
+ ac_capabilities=[]):
"""Construct a HostapConfig.
You may specify channel or frequency, but not both. Both options
@@ -361,6 +456,9 @@
client supports/must support 802.11w.
@param obss_interval int interval in seconds that client should be
required to do background scans for overlapping BSSes.
+ @param vht_channel_width object channel width
+ @param vht_center_channel int center channel of segment 0.
+ @param ac_capabilities list of AC_CAPABILITY_x defined above.
"""
super(HostapConfig, self).__init__()
@@ -412,6 +510,20 @@
self._security_config = (copy.copy(security_config) or
xmlrpc_security_types.SecurityConfig())
self._obss_interval = obss_interval
+ if vht_channel_width == self.VHT_CHANNEL_WIDTH_40:
+ self._vht_oper_chwidth = 0
+ elif vht_channel_width == self.VHT_CHANNEL_WIDTH_80:
+ self._vht_oper_chwidth = 1
+ elif vht_channel_width == self.VHT_CHANNEL_WIDTH_160:
+ self._vht_oper_chwidth = 2
+ elif vht_channel_width == self.VHT_CHANNEL_WIDTH_80_80:
+ self._vht_oper_chwidth = 3
+ elif vht_channel_width is not None:
+ raise error.TestFail('Invalid channel width')
+ # TODO(zqiu) Add checking for center channel based on the channel width
+ # and operating channel.
+ self._vht_oper_centr_freq_seg0_idx = vht_center_channel
+ self._ac_capabilities = set(ac_capabilities)
def __repr__(self):
@@ -504,13 +616,21 @@
conf['hw_mode'] = self._hw_mode
if self._hide_ssid:
conf['ignore_broadcast_ssid'] = 1
- if self._is_11n:
+ if self._is_11n or self.is_11ac:
conf['ieee80211n'] = 1
conf['ht_capab'] = self._hostapd_ht_capabilities
+ if self.is_11ac:
+ conf['ieee80211ac'] = 1
+ conf['vht_oper_chwidth'] = self._vht_oper_chwidth
+ conf['vht_oper_centr_freq_seg0_idx'] = \
+ self._vht_oper_centr_freq_seg0_idx
+ conf['vht_capab'] = self._hostapd_vht_capabilities
if self._wmm_enabled:
conf['wmm_enabled'] = 1
if self._require_ht:
conf['require_ht'] = 1
+ if self._require_vht:
+ conf['require_vht'] = 1
if self._beacon_interval:
conf['beacon_int'] = self._beacon_interval
if self._dtim_period:
diff --git a/server/site_linux_system.py b/server/site_linux_system.py
index 3739a55..4e519fe 100644
--- a/server/site_linux_system.py
+++ b/server/site_linux_system.py
@@ -33,6 +33,7 @@
CAPABILITY_IBSS = 'ibss_supported'
CAPABILITY_SEND_MANAGEMENT_FRAME = 'send_management_frame'
CAPABILITY_TDLS = 'tdls'
+ CAPABILITY_VHT = 'vht'
@property
@@ -178,6 +179,8 @@
for phy in self.phy_list:
if 'tdls_mgmt' in phy.commands or 'tdls_oper' in phy.commands:
caps.add(self.CAPABILITY_TDLS)
+ if phy.support_vht:
+ caps.add(self.CAPABILITY_VHT)
return caps
diff --git a/server/site_tests/network_WiFi_SimpleConnect/control.wifi_check5VHT80 b/server/site_tests/network_WiFi_SimpleConnect/control.wifi_check5VHT80
new file mode 100644
index 0000000..dad5c7d
--- /dev/null
+++ b/server/site_tests/network_WiFi_SimpleConnect/control.wifi_check5VHT80
@@ -0,0 +1,43 @@
+# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+AUTHOR = 'zqiu, wiley, pstew, quiche'
+NAME = 'network_WiFi_SimpleConnect.wifi_check5VHT80'
+TIME = 'SHORT'
+TEST_TYPE = 'Server'
+SUITE = 'wifi_matfunc, wifi_correctness_cros_core, wifi_release'
+DEPENDENCIES = 'wificell'
+
+DOC = """
+This test verifies that DUT can connect to an open 802.11ac network
+on channel 36 with center channel of 42 and channel width of 80MHz.
+"""
+
+
+from autotest_lib.client.common_lib.cros.network import xmlrpc_datatypes
+from autotest_lib.server.cros.network import hostap_config
+
+
+def run(machine):
+ n_caps = [hostap_config.HostapConfig.N_CAPABILITY_HT40_PLUS]
+ ac_caps = [hostap_config.HostapConfig.AC_CAPABILITY_SHORT_GI_80]
+ ac_mode = hostap_config.HostapConfig.MODE_11AC_MIXED
+ channel_width_80_mhz = hostap_config.HostapConfig.VHT_CHANNEL_WIDTH_80
+ configurations = [(hostap_config.HostapConfig(
+ channel=36,
+ mode=ac_mode,
+ n_capabilities=n_caps,
+ vht_channel_width=channel_width_80_mhz,
+ vht_center_channel=42,
+ ac_capabilities=ac_caps),
+ xmlrpc_datatypes.AssociationParameters())]
+ host = hosts.create_host(machine)
+ job.run_test('network_WiFi_SimpleConnect',
+ tag=NAME.split('.')[1],
+ host=host,
+ raw_cmdline_args=args,
+ additional_params=configurations)
+
+
+parallel_simple(run, machines)
diff --git a/server/site_tests/network_WiFi_SimpleConnect/network_WiFi_SimpleConnect.py b/server/site_tests/network_WiFi_SimpleConnect/network_WiFi_SimpleConnect.py
index 63522b6..91d9b0f 100644
--- a/server/site_tests/network_WiFi_SimpleConnect/network_WiFi_SimpleConnect.py
+++ b/server/site_tests/network_WiFi_SimpleConnect/network_WiFi_SimpleConnect.py
@@ -4,6 +4,8 @@
import logging
+from autotest_lib.client.common_lib import error
+from autotest_lib.server import site_linux_system
from autotest_lib.server.cros.network import wifi_cell_test_base
@@ -25,6 +27,12 @@
def run_once(self):
"""Sets up a router, connects to it, pings it, and repeats."""
for router_conf, client_conf in self._configurations:
+ if router_conf.is_11ac:
+ router_caps = self.context.router.capabilities
+ if site_linux_system.LinuxSystem.CAPABILITY_VHT not in \
+ router_caps:
+ raise error.TestNAError('Router does not have AC support')
+
self.context.configure(router_conf)
self.context.router.start_capture(
router_conf.frequency,