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 5 import logging 6 import time 7 8 from autotest_lib.client.common_lib import error 9 from autotest_lib.client.common_lib.cros.network import xmlrpc_datatypes 10 from autotest_lib.server import site_linux_system 11 from autotest_lib.server.cros.network import hostap_config 12 from autotest_lib.server.cros.network import wifi_cell_test_base 13 14 15 class network_WiFi_VerifyRouter(wifi_cell_test_base.WiFiCellTestBase): 16 """Test that a dual radio router can use both radios.""" 17 version = 1 18 MAX_ASSOCIATION_RETRIES = 8 # Super lucky number. Not science. 19 20 21 def _connect(self, wifi_params): 22 assoc_result = xmlrpc_datatypes.deserialize( 23 self.context.client.shill.connect_wifi(wifi_params)) 24 logging.info('Finished connection attempt to %s with times: ' 25 'discovery=%.2f, association=%.2f, configuration=%.2f.', 26 wifi_params.ssid, 27 assoc_result.discovery_time, 28 assoc_result.association_time, 29 assoc_result.configuration_time) 30 return assoc_result.success 31 32 33 def _antenna_test(self, bitmap, channel): 34 """Test that we can connect on |channel|, with given antenna |bitmap|. 35 36 Sets up two radios on |channel|, configures both radios with the 37 given antenna |bitmap|, and then verifies that a client can connect 38 to the AP on each radio. 39 40 Why do we run the two radios concurrently, instead of iterating over 41 them? That's simply because our lower-layer code doesn't provide an 42 interface for specifiying which PHY to run an AP on. 43 44 To work around the API limitaiton, we bring up multiple APs, and let 45 the lower-layer code spread them across radios. For stumpy/panther, 46 this works in an obvious way. That is, each call to this method 47 exercises phy0 and phy1. 48 49 For whirlwind, we still cover all radios, but in a less obvious way. 50 Calls with a 2.4 GHz channel exercise phy0 and phy2, while calls 51 with a 5 GHz channel exercise phy1 and phy2. 52 53 @param bitmap: int bitmask controlling which antennas to enable. 54 @param channel: int Wifi channel to conduct test on 55 56 """ 57 # Antenna can only be configured when the wireless interface is down. 58 self.context.router.deconfig() 59 # Set the bitmasks to both antennas on before turning one off. 60 self.context.router.disable_antennas_except(3) 61 # This seems to increase the probability that our association 62 # attempts pass. It is the very definition of a dark incantation. 63 time.sleep(5) 64 if bitmap != 3: 65 self.context.router.disable_antennas_except(bitmap) 66 # Setup two APs on |channel|. configure() will spread these across 67 # radios. 68 n_mode = hostap_config.HostapConfig.MODE_11N_MIXED 69 ap_config = hostap_config.HostapConfig(channel=channel, mode=n_mode) 70 self.context.configure(ap_config) 71 self.context.configure(ap_config, multi_interface=True) 72 failures = [] 73 # Verify connectivity to both APs. As the APs are spread 74 # across radios, this exercises multiple radios. 75 for instance in range(2): 76 context_message = ('bitmap=%d, ap_instance=%d, channel=%d' % 77 (bitmap, instance, channel)) 78 logging.info('Connecting to AP with settings %s.', 79 context_message) 80 client_conf = xmlrpc_datatypes.AssociationParameters( 81 ssid=self.context.router.get_ssid(instance=instance)) 82 if self._connect(client_conf): 83 signal_level = self.context.client.wifi_signal_level 84 logging.info('Signal level for AP %d with bitmap %d is %d', 85 instance, bitmap, signal_level) 86 self.write_perf_keyval( 87 {'signal_for_ap_%d_bm_%d_ch_%d' % 88 (instance, bitmap, channel): 89 signal_level}) 90 else: 91 failures.append(context_message) 92 # Don't automatically reconnect to this AP. 93 self.context.client.shill.disconnect( 94 self.context.router.get_ssid(instance=instance)) 95 return failures 96 97 98 def cleanup(self): 99 """Clean up after the test is completed 100 101 Perform additional cleanups after the test, the important thing is 102 to re-enable all antennas. 103 """ 104 self.context.router.deconfig() 105 self.context.router.enable_all_antennas() 106 super(network_WiFi_VerifyRouter, self).cleanup() 107 108 109 def run_once(self): 110 """Verify that all radios on this router are functional.""" 111 self.context.router.require_capabilities( 112 [site_linux_system.LinuxSystem.CAPABILITY_MULTI_AP_SAME_BAND]) 113 114 all_failures = [] 115 # Run antenna test for 2GHz band and 5GHz band 116 for channel in (6, 149): 117 # First connect with both antennas enabled. Then connect with just 118 # one antenna enabled at a time. 119 for bitmap in (3, 1, 2): 120 failures = set() 121 for attempt in range(self.MAX_ASSOCIATION_RETRIES): 122 new_failures = self._antenna_test(bitmap, channel) 123 if not new_failures: 124 break 125 failures.update(new_failures) 126 else: 127 all_failures += failures 128 129 if all_failures: 130 failure_message = ', '.join( 131 ['(' + message + ')' for message in all_failures]) 132 raise error.TestFail('Failed to connect when %s.' % failure_message) 133