autotest: add network_WiFiHECaps test
Add a test which checks if a DUT supports various HE capabilities.
This test will become more useful as more 11ax features are
implemented; it will serve to verify that the driver on the DUT at
least thinks it supports certain features.
BUG=None
TEST=`network_WiFiHECaps.HE160` on hatch. Verify it is functioning
correctly by checking that the log `client.0.INFO` prints all the
features specified in the control file.
Cq-Depend: chromium:1900141
Change-Id: I8b7c2560292135391714a10c9138276aba600840
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/autotest/+/1915294
Tested-by: Jared Pauletti <pauletti@google.com>
Commit-Queue: Jared Pauletti <pauletti@google.com>
Reviewed-by: Kirtika Ruchandani <kirtika@chromium.org>
diff --git a/client/common_lib/cros/network/iw_runner.py b/client/common_lib/cros/network/iw_runner.py
index 9a89704..2bb7208 100644
--- a/client/common_lib/cros/network/iw_runner.py
+++ b/client/common_lib/cros/network/iw_runner.py
@@ -97,6 +97,102 @@
IW_LINK_KEY_TX_RETRIES = 'tx retries'
IW_LOCAL_EVENT_LOG_FILE = './debug/iw_event_%d.log'
+# Strings from iw/util.c describing supported HE features
+HE_MAC_PLUS_HTC_HE = '+HTC HE Supported'
+HE_MAC_TWT_REQUESTER = 'TWT Requester'
+HE_MAC_TWT_RESPONDER = 'TWT Responder'
+HE_MAC_DYNAMIC_BA_FRAGMENTATION = 'Dynamic BA Fragementation Level'
+HE_MAC_MAX_MSDUS = 'Maximum number of MSDUS Fragments'
+HE_MAC_MIN_PAYLOAD_128 = 'Minimum Payload size of 128 bytes'
+HE_MAC_TRIGGER_FRAME_PADDING = 'Trigger Frame MAC Padding Duration'
+HE_MAC_MULTI_TID_AGGREGATION = 'Multi-TID Aggregation Support'
+HE_MAC_ALL_ACK = 'All Ack'
+HE_MAC_TRS = 'TRS'
+HE_MAC_BSR = 'BSR'
+HE_MAC_TWT_BROADCAST = 'Broadcast TWT'
+HE_MAC_32_BIT_BA_BITMAP = '32-bit BA Bitmap'
+HE_MAC_MU_CASCADING = 'MU Cascading'
+HE_MAC_ACK_AGGREGATION = 'Ack-Enabled Aggregation'
+HE_MAC_OM_CONTROL = 'OM Control'
+HE_MAC_OFDMA_RA = 'OFDMA RA'
+HE_MAC_MAX_AMPDU_LENGTH_EXPONENT = 'Maximum A-MPDU Length Exponent'
+HE_MAC_AMSDU_FRAGMENTATION = 'A-MSDU Fragmentation'
+HE_MAC_FLEXIBLE_TWT = 'Flexible TWT Scheduling'
+HE_MAC_RX_CONTROL_FRAME_TO_MULTIBSS = 'RX Control Frame to MultiBSS'
+HE_MAC_BSRP_BQRP_AMPDU_AGGREGATION = 'BSRP BQRP A-MPDU Aggregation'
+HE_MAC_QTP = 'QTP'
+HE_MAC_BQR = 'BQR'
+HE_MAC_SRP_RESPONDER_ROLE = 'SRP Responder Role'
+HE_MAC_NDP_FEEDBACK_REPORT = 'NDP Feedback Report'
+HE_MAC_OPS = 'OPS'
+HE_MAC_AMSDU_IN_AMPDU = 'A-MSDU in A-MPDU'
+HE_MAC_MULTI_TID_AGGREGATION_TX = 'Multi-TID Aggregation TX'
+HE_MAC_SUBCHANNEL_SELECTIVE = 'HE Subchannel Selective Transmission'
+HE_MAC_UL_2X966_TONE_RU = 'UL 2x996-Tone RU'
+HE_MAC_OM_CONTROL_DISABLE_RX = 'OM Control UL MU Data Disable RX'
+
+HE_PHY_24HE40 = 'HE40/2.4GHz'
+HE_PHY_5HE40_80 = 'HE40/HE80/5GHz'
+HE_PHY_5HE160 = 'HE160/5GHz'
+HE_PHY_5HE160_80_80 = 'HE160/HE80+80/5GHz'
+HE_PHY_242_TONE_RU_24 = '242 tone RUs/2.4GHz'
+HE_PHY_242_TONE_RU_5 = '242 tone RUs/5GHz'
+HE_PHY_PUNCTURED_PREAMBLE_RX = 'Punctured Preamble RX'
+HE_PHY_DEVICE_CLASS = 'Device Class'
+HE_PHY_LDPC_CODING_IN_PAYLOAD = 'LDPC Coding in Payload'
+HE_PHY_HE_SU_PPDU_1X_HE_LTF_08_GI = 'HE SU PPDU with 1x HE-LTF and 0.8us GI'
+HE_PHY_HE_MIDAMBLE_RX_MAX_NSTS = 'Midamble Rx Max NSTS'
+HE_PHY_NDP_4X_HE_LTF_32_GI = 'NDP with 4x HE-LTF and 3.2us GI'
+HE_PHY_STBC_TX_LEQ_80 = 'STBC Tx <= 80MHz'
+HE_PHY_STBC_RX_LEQ_80 = 'STBC Rx <= 80MHz'
+HE_PHY_DOPPLER_TX = 'Doppler Tx'
+HE_PHY_DOPPLER_RX = 'Doppler Rx'
+HE_PHY_FULL_BAND_UL_MU_MIMO = 'Full Bandwidth UL MU-MIMO'
+HE_PHY_PART_BAND_UL_MU_MIMO = 'Partial Bandwidth UL MU-MIMO'
+HE_PHY_DCM_MAX_CONSTELLATION = 'DCM Max Constellation'
+HE_PHY_DCM_MAX_NSS_TX = 'DCM Max NSS Tx'
+HE_PHY_DCM_MAX_CONSTELLATION_RX = 'DCM Max Constellation Rx'
+HE_PHY_DCM_MAX_NSS_RX = 'DCM Max NSS Rx'
+HE_PHY_RX_MU_PPDU_FROM_NON_AP = 'Rx HE MU PPDU from Non-AP STA'
+HE_PHY_SU_BEAMFORMER = 'SU Beamformer'
+HE_PHY_SU_BEAMFORMEE = 'SU Beamformee'
+HE_PHY_MU_BEAMFORMER = 'MU Beamformer'
+HE_PHY_BEAMFORMEE_STS_LEQ_80 = 'Beamformee STS <= 80Mhz'
+HE_PHY_BEAMFORMEE_STS_GT_80 = 'Beamformee STS > 80Mhz'
+HE_PHY_SOUNDING_DIMENSIONS_LEQ_80 = 'Sounding Dimensions <= 80Mhz'
+HE_PHY_SOUNDING_DIMENSIONS_GT_80 = 'Sounding Dimensions > 80Mhz'
+HE_PHY_NG_EQ_16_SU_FB = 'Ng = 16 SU Feedback'
+HE_PHY_NG_EQ_16_MU_FB = 'Ng = 16 MU Feedback'
+HE_PHY_CODEBOOK_SIZE_SU_FB = 'Codebook Size SU Feedback'
+HE_PHY_CODEBOOK_SIZE_MU_FB = 'Codebook Size MU Feedback'
+HE_PHY_TRIGGERED_SU_BEAMFORMING_FB = 'Triggered SU Beamforming Feedback'
+HE_PHY_TRIGGERED_MU_BEAMFORMING_FB = 'Triggered MU Beamforming Feedback'
+HE_PHY_TRIGGERED_CQI_FB = 'Triggered CQI Feedback'
+HE_PHY_PART_BAND_EXT_RANGE = 'Partial Bandwidth Extended Range'
+HE_PHY_PART_BAND_DL_MU_MIMO = 'Partial Bandwidth DL MU-MIMO'
+HE_PHY_PPE_THRESHOLD = 'PPE Threshold Present'
+HE_PHY_SRP_SR = 'SRP-based SR'
+HE_PHY_POWER_BOOST_FACTOR_AR = 'Power Boost Factor ar'
+HE_PHY_SU_PPDU_4X_HE_LTF_08_GI = 'HE SU PPDU & HE PPDU 4x HE-LTF 0.8us GI'
+HE_PHY_MAX_NC = 'Max NC'
+HE_PHY_STBC_TX_GT_80 = 'STBC Tx > 80MHz'
+HE_PHY_STBC_RX_GT_80 = 'STBC Rx > 80MHz'
+HE_PHY_ER_SU_PPDU_4X_HE_LTF_08_GI = 'HE ER SU PPDU 4x HE-LTF 0.8us GI'
+HE_PHY_20_IN_44_PPDU_24 = '20MHz in 40MHz HE PPDU 2.4GHz'
+HE_PHY_20_IN_160_80_80 = '20MHz in 160/80+80MHz HE PPDU'
+HE_PHY_80_IN_160_80_80 = '80MHz in 160/80+80MHz HE PPDU'
+HE_PHY_ER_SU_PPDU_1X_HE_LTF_08_GI = 'HE ER SU PPDU 1x HE-LTF 0.8us GI'
+HE_PHY_MIDAMBLE_RX_2X_AND_1X_HE_LTF = 'Midamble Rx 2x & 1x HE-LTF'
+HE_PHY_DCM_MAX_BW = 'DCM Max BW'
+HE_PHY_LONGER_THAN_16HE_OFDM_SYM = 'Longer Than 16HE SIG-B OFDM Symbols'
+HE_PHY_NON_TRIGGERED_CQI_FB = 'Non-Triggered CQI Feedback'
+HE_PHY_TX_1024_QAM = 'TX 1024-QAM'
+HE_PHY_RX_1024_QAM = 'RX 1024-QAM'
+HE_PHY_RX_FULL_BW_SU_USING_MU_COMPRESSION_SIGB = \
+ 'RX Full BW SU Using HE MU PPDU with Compression SIGB'
+HE_PHY_RX_FULL_BW_SU_USING_MU_NON_COMPRESSION_SIGB = \
+ 'RX Full BW SU Using HE MU PPDU with Non-Compression SIGB'
+
def _get_all_link_keys(link_information):
"""Parses link or station dump output for link key value pairs.
@@ -1004,3 +1100,24 @@
return int(match.group(1))
return None
+
+
+ def get_info(self, phy=None):
+ """
+ Returns the output of 'iw phy info' for |phy|, or 'iw list' if no phy
+ specified.
+
+ @param phy: optional string giving the name of the phy
+ @return string stdout of the command run
+ """
+ if phy and phy not in [iw_phy.name for iw_phy in self.list_phys()]:
+ logging.info('iw could not find phy %s', phy)
+ return None
+
+ if phy:
+ out = self._run('%s phy %s info' % (self._command_iw, phy)).stdout
+ else:
+ out = self._run('%s list' % self._command_iw).stdout
+ if 'Wiphy' in out:
+ return out
+ return None
\ No newline at end of file
diff --git a/client/site_tests/network_WiFiHECaps/control b/client/site_tests/network_WiFiHECaps/control
new file mode 100644
index 0000000..3f5eeb3
--- /dev/null
+++ b/client/site_tests/network_WiFiHECaps/control
@@ -0,0 +1,21 @@
+# Copyright 2019 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 = 'pauletti'
+NAME = 'network_WiFiHECaps'
+ATTRIBUTES = 'suite:wifi_flaky'
+TIME = 'SHORT'
+TEST_TYPE = 'client'
+
+DOC = """
+This test checks that a device which supports the 802.11ax WiFi standard is
+able to function on an High Efficiency 160 MHz wide channel (HE160). HE PHY
+implementation is expected on all devices which support 802.11ax frame formats
+(i.e. HE PPDU).
+"""
+
+from autotest_lib.client.common_lib.cros.network import iw_runner
+
+job.run_test('network_WiFiHECaps', features=[[iw_runner.HE_PHY_5HE160,
+ iw_runner.HE_PHY_5HE160_80_80]])
diff --git a/client/site_tests/network_WiFiHECaps/control.11ax_supported b/client/site_tests/network_WiFiHECaps/control.11ax_supported
new file mode 100644
index 0000000..a664888
--- /dev/null
+++ b/client/site_tests/network_WiFiHECaps/control.11ax_supported
@@ -0,0 +1,16 @@
+# Copyright 2019 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 = 'pauletti'
+NAME = 'network_WiFiHECaps.11ax_supported'
+ATTRIBUTES = 'suite:wifi_flaky'
+TIME = 'SHORT'
+TEST_TYPE = 'client'
+
+DOC = """
+This test checks that a device supports the 802.11ax WiFi standard (i.e., that
+it is capable of receiving and transmitting High Efficiency (HE) PPDUs).
+"""
+
+job.run_test('network_WiFiHECaps', tag=NAME.split('.')[1])
diff --git a/client/site_tests/network_WiFiHECaps/control.HE160 b/client/site_tests/network_WiFiHECaps/control.HE160
new file mode 100644
index 0000000..935d346
--- /dev/null
+++ b/client/site_tests/network_WiFiHECaps/control.HE160
@@ -0,0 +1,22 @@
+# Copyright 2019 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 = 'pauletti'
+NAME = 'network_WiFiHECaps.HE160'
+ATTRIBUTES = 'suite:wifi_flaky'
+TIME = 'SHORT'
+TEST_TYPE = 'client'
+
+DOC = """
+This test checks that a device which supports the 802.11ax WiFi standard is
+able to function on an High Efficiency 160 MHz wide channel (HE160). HE PHY
+implementation is expected on all devices which support 802.11ax frame formats
+(i.e. HE PPDU).
+"""
+
+from autotest_lib.client.common_lib.cros.network import iw_runner
+
+job.run_test('network_WiFiHECaps', tag=NAME.split('.')[1],
+ features=[[iw_runner.HE_PHY_5HE160,
+ iw_runner.HE_PHY_5HE160_80_80]])
diff --git a/client/site_tests/network_WiFiHECaps/control.MU_MIMO b/client/site_tests/network_WiFiHECaps/control.MU_MIMO
new file mode 100644
index 0000000..b578a21
--- /dev/null
+++ b/client/site_tests/network_WiFiHECaps/control.MU_MIMO
@@ -0,0 +1,22 @@
+# Copyright 2019 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 = 'pauletti'
+NAME = 'network_WiFiHECaps.MU_MIMO'
+ATTRIBUTES = 'suite:wifi_flaky'
+TIME = 'SHORT'
+TEST_TYPE = 'client'
+
+DOC = """
+This test checks that a device which supports the 802.11ax WiFi also supports
+Multi-User Multi Input Multi Output (MU-MIMO) downlink (DL) and uplink (UL).
+"""
+
+from autotest_lib.client.common_lib.cros.network import iw_runner
+
+job.run_test('network_WiFiHECaps', tag=NAME.split('.')[1],
+ features=[[iw_runner.HE_PHY_FULL_BAND_UL_MU_MIMO,
+ iw_runner.HE_PHY_PART_BAND_UL_MU_MIMO],
+ [iw_runner.HE_PHY_PART_BAND_DL_MU_MIMO]])
+
diff --git a/client/site_tests/network_WiFiHECaps/network_WiFiHECaps.py b/client/site_tests/network_WiFiHECaps/network_WiFiHECaps.py
new file mode 100644
index 0000000..4c41560
--- /dev/null
+++ b/client/site_tests/network_WiFiHECaps/network_WiFiHECaps.py
@@ -0,0 +1,80 @@
+# Copyright 2019 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.
+
+import logging
+
+from autotest_lib.client.bin import test
+from autotest_lib.client.common_lib import error
+from autotest_lib.client.common_lib.cros.network import iw_runner
+
+class network_WiFiHECaps(test.test):
+ """Test the specified HE client capabilities."""
+ version = 1
+
+ def run_once(self, phy=None, features=None):
+ """
+ Check for support of the features specified in control file.
+
+ Features are passed in as a list of lists containing string constants.
+ The test will pass if the DUT supports at least one string in each
+ list. Essentially, all the elements in the list are joined with AND
+ while all the elements of a list are joined with OR.
+
+ @param phy string name of wifi phy to use, or None to allow the
+ test to choose.
+ @param features list of lists of string constants from iw_runner
+ specifying the HE features to check on the DUT.
+
+ """
+ iw = iw_runner.IwRunner()
+ if not phy:
+ phys = iw.list_phys()
+ if not phys:
+ raise error.TestError('No valid WiFi phy found')
+ phy = phys[0].name
+ if not iw.he_supported():
+ raise error.TestNAError('HE not supported by DUT')
+
+ phy_info = iw.get_info()
+ if not phy_info:
+ raise error.TestError('Could not get phy info using iw_runner')
+
+ if not features:
+ features = []
+
+ featurelist = [f for inner_list in features for f in inner_list]
+ is_supported = {f : False for f in featurelist}
+ values = {}
+
+ for line in phy_info.splitlines():
+ line = line.strip()
+ for f in featurelist:
+ if not is_supported[f] and f in line:
+ is_supported[f] = True
+ l = line.split(':', 1)
+ if len(l) > 1:
+ values[f] = l[1].strip()
+ break
+
+ supported = ['These features are supported by the DUT:']
+ not_supported = ['These features are NOT supported by the DUT:']
+ for f in featurelist:
+ if is_supported[f]:
+ if values.get(f, None):
+ f += ('; has value %s' % values[f])
+ supported.append(f)
+ else:
+ not_supported.append(f)
+ logging.info(' '.join(supported))
+ logging.info(' '.join(not_supported))
+
+ for inner_list in features:
+ list_passed = False
+ for f in inner_list:
+ if is_supported[f]:
+ list_passed = True
+ break
+ if not list_passed:
+ raise error.TestError('Test failed because none of %r are '
+ 'supported by the DUT.' % inner_list)