Paul Stewart | 2ee7fdf | 2011-05-19 16:29:23 -0700 | [diff] [blame] | 1 | # Copyright (c) 2011 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 | |
Christopher Wiley | 7c97ded | 2013-09-24 23:24:33 -0700 | [diff] [blame] | 5 | import datetime |
Christopher Wiley | f671a5a | 2013-12-13 15:44:41 -0800 | [diff] [blame] | 6 | import collections |
Christopher Wiley | f19c28c | 2013-05-06 15:42:57 -0700 | [diff] [blame] | 7 | import logging |
Christopher Wiley | 7c97ded | 2013-09-24 23:24:33 -0700 | [diff] [blame] | 8 | import time |
Christopher Wiley | 4c6a32c | 2013-04-24 13:37:51 -0700 | [diff] [blame] | 9 | |
Paul Stewart | 2ee7fdf | 2011-05-19 16:29:23 -0700 | [diff] [blame] | 10 | from autotest_lib.client.common_lib import error |
Christopher Wiley | f9cb092 | 2013-11-01 09:12:21 -0700 | [diff] [blame] | 11 | from autotest_lib.client.common_lib.cros.network import iw_runner |
Christopher Wiley | 7337ff6 | 2013-10-03 17:21:46 -0700 | [diff] [blame] | 12 | from autotest_lib.server.cros import wifi_test_utils |
Christopher Wiley | dc7c462 | 2013-07-09 11:44:04 -0700 | [diff] [blame] | 13 | from autotest_lib.server.cros.network import packet_capturer |
Paul Stewart | 2ee7fdf | 2011-05-19 16:29:23 -0700 | [diff] [blame] | 14 | |
Christopher Wiley | f671a5a | 2013-12-13 15:44:41 -0800 | [diff] [blame] | 15 | NetDev = collections.namedtuple('NetDev', |
| 16 | ['inherited', 'phy', 'if_name', 'if_type']) |
| 17 | |
Paul Stewart | 2ee7fdf | 2011-05-19 16:29:23 -0700 | [diff] [blame] | 18 | class LinuxSystem(object): |
Christopher Wiley | 4c6a32c | 2013-04-24 13:37:51 -0700 | [diff] [blame] | 19 | """Superclass for test machines running Linux. |
| 20 | |
| 21 | Provides a common point for routines that use the cfg80211 userspace tools |
| 22 | to manipulate the wireless stack, regardless of the role they play. |
| 23 | Currently the commands shared are the init, which queries for wireless |
| 24 | devices, along with start_capture and stop_capture. More commands may |
| 25 | migrate from site_linux_router as appropriate to share. |
| 26 | |
Paul Stewart | 2ee7fdf | 2011-05-19 16:29:23 -0700 | [diff] [blame] | 27 | """ |
Christopher Wiley | 4c6a32c | 2013-04-24 13:37:51 -0700 | [diff] [blame] | 28 | |
Christopher Wiley | f19c28c | 2013-05-06 15:42:57 -0700 | [diff] [blame] | 29 | CAPABILITY_5GHZ = '5ghz' |
| 30 | CAPABILITY_MULTI_AP = 'multi_ap' |
| 31 | CAPABILITY_MULTI_AP_SAME_BAND = 'multi_ap_same_band' |
Christopher Wiley | 061f138 | 2013-06-17 18:17:58 -0700 | [diff] [blame] | 32 | CAPABILITY_IBSS = 'ibss_supported' |
Paul Stewart | 51b0f38 | 2013-06-12 09:03:02 -0700 | [diff] [blame] | 33 | CAPABILITY_SEND_MANAGEMENT_FRAME = 'send_management_frame' |
Paul Stewart | 27ecc5d | 2013-11-13 16:56:41 -0800 | [diff] [blame] | 34 | CAPABILITY_TDLS = 'tdls' |
Christopher Wiley | f19c28c | 2013-05-06 15:42:57 -0700 | [diff] [blame] | 35 | |
| 36 | |
| 37 | @property |
| 38 | def capabilities(self): |
Christopher Wiley | 061f138 | 2013-06-17 18:17:58 -0700 | [diff] [blame] | 39 | """@return iterable object of AP capabilities for this system.""" |
| 40 | if self._capabilities is None: |
| 41 | self._capabilities = self.get_capabilities() |
| 42 | logging.info('%s system capabilities: %r', |
| 43 | self.role, self._capabilities) |
Christopher Wiley | f19c28c | 2013-05-06 15:42:57 -0700 | [diff] [blame] | 44 | return self._capabilities |
| 45 | |
| 46 | |
Christopher Wiley | 408d181 | 2014-01-13 15:27:43 -0800 | [diff] [blame] | 47 | def __init__(self, host, role, inherit_interfaces=False): |
Paul Stewart | 2ee7fdf | 2011-05-19 16:29:23 -0700 | [diff] [blame] | 48 | # Command locations. |
Christopher Wiley | 7bd2e08 | 2013-10-16 17:40:40 -0700 | [diff] [blame] | 49 | cmd_iw = wifi_test_utils.must_be_installed( |
Christopher Wiley | 408d181 | 2014-01-13 15:27:43 -0800 | [diff] [blame] | 50 | host, '/usr/sbin/iw') |
Christopher Wiley | 7337ff6 | 2013-10-03 17:21:46 -0700 | [diff] [blame] | 51 | self.cmd_ip = wifi_test_utils.must_be_installed( |
Christopher Wiley | 408d181 | 2014-01-13 15:27:43 -0800 | [diff] [blame] | 52 | host, '/usr/sbin/ip') |
Christopher Wiley | 7337ff6 | 2013-10-03 17:21:46 -0700 | [diff] [blame] | 53 | self.cmd_readlink = '%s -l' % wifi_test_utils.must_be_installed( |
Christopher Wiley | 408d181 | 2014-01-13 15:27:43 -0800 | [diff] [blame] | 54 | host, '/bin/ls') |
Paul Stewart | 6423bb0 | 2012-11-27 17:46:23 -0800 | [diff] [blame] | 55 | |
Paul Stewart | 2ee7fdf | 2011-05-19 16:29:23 -0700 | [diff] [blame] | 56 | self.host = host |
| 57 | self.role = role |
| 58 | |
Christopher Wiley | 09ad206 | 2013-09-13 13:34:49 -0700 | [diff] [blame] | 59 | self._packet_capturer = packet_capturer.get_packet_capturer( |
Christopher Wiley | 408d181 | 2014-01-13 15:27:43 -0800 | [diff] [blame] | 60 | self.host, host_description=role, cmd_ip=self.cmd_ip, |
| 61 | cmd_iw=cmd_iw, ignore_failures=True) |
Christopher Wiley | f9cb092 | 2013-11-01 09:12:21 -0700 | [diff] [blame] | 62 | self.iw_runner = iw_runner.IwRunner(remote_host=host, command_iw=cmd_iw) |
Paul Stewart | 2ee7fdf | 2011-05-19 16:29:23 -0700 | [diff] [blame] | 63 | |
Paul Stewart | 27ecc5d | 2013-11-13 16:56:41 -0800 | [diff] [blame] | 64 | self._phy_list = None |
Paul Stewart | 6423bb0 | 2012-11-27 17:46:23 -0800 | [diff] [blame] | 65 | self.phys_for_frequency, self.phy_bus_type = self._get_phy_info() |
Christopher Wiley | f671a5a | 2013-12-13 15:44:41 -0800 | [diff] [blame] | 66 | self._interfaces = [] |
| 67 | for interface in self.iw_runner.list_interfaces(): |
| 68 | if inherit_interfaces: |
| 69 | self._interfaces.append(NetDev(inherited=True, |
| 70 | if_name=interface.if_name, |
| 71 | if_type=interface.if_type, |
| 72 | phy=interface.phy)) |
| 73 | else: |
| 74 | self.iw_runner.remove_interface(interface.if_name) |
| 75 | |
| 76 | self._wlanifs_in_use = [] |
| 77 | self._capture_interface = None |
Christopher Wiley | 80e4092 | 2013-10-22 13:14:56 -0700 | [diff] [blame] | 78 | # Some uses of LinuxSystem don't use the interface allocation facility. |
| 79 | # Don't force us to remove all the existing interfaces if this facility |
| 80 | # is not desired. |
| 81 | self._wlanifs_initialized = False |
Christopher Wiley | f19c28c | 2013-05-06 15:42:57 -0700 | [diff] [blame] | 82 | self._capabilities = None |
Paul Stewart | 2ee7fdf | 2011-05-19 16:29:23 -0700 | [diff] [blame] | 83 | |
Paul Stewart | 6423bb0 | 2012-11-27 17:46:23 -0800 | [diff] [blame] | 84 | |
Paul Stewart | 27ecc5d | 2013-11-13 16:56:41 -0800 | [diff] [blame] | 85 | @property |
| 86 | def phy_list(self): |
| 87 | """@return iterable object of PHY descriptions for this system.""" |
| 88 | if self._phy_list is None: |
| 89 | self._phy_list = self.iw_runner.list_phys() |
| 90 | return self._phy_list |
| 91 | |
| 92 | |
Paul Stewart | 6423bb0 | 2012-11-27 17:46:23 -0800 | [diff] [blame] | 93 | def _get_phy_info(self): |
Christopher Wiley | 4c6a32c | 2013-04-24 13:37:51 -0700 | [diff] [blame] | 94 | """Get information about WiFi devices. |
| 95 | |
| 96 | Parse the output of 'iw list' and some of sysfs and return: |
| 97 | |
| 98 | A dict |phys_for_frequency| which maps from each frequency to a |
| 99 | list of phys that support that channel. |
| 100 | |
| 101 | A dict |phy_bus_type| which maps from each phy to the bus type for |
| 102 | each phy. |
| 103 | |
| 104 | @return phys_for_frequency, phy_bus_type tuple as described. |
| 105 | |
Paul Stewart | 6423bb0 | 2012-11-27 17:46:23 -0800 | [diff] [blame] | 106 | """ |
Paul Stewart | 6423bb0 | 2012-11-27 17:46:23 -0800 | [diff] [blame] | 107 | phys_for_frequency = {} |
Paul Stewart | 27ecc5d | 2013-11-13 16:56:41 -0800 | [diff] [blame] | 108 | phy_caps = {} |
Christopher Wiley | 7bd2e08 | 2013-10-16 17:40:40 -0700 | [diff] [blame] | 109 | phy_list = [] |
Paul Stewart | 27ecc5d | 2013-11-13 16:56:41 -0800 | [diff] [blame] | 110 | for phy in self.phy_list: |
Christopher Wiley | 7bd2e08 | 2013-10-16 17:40:40 -0700 | [diff] [blame] | 111 | phy_list.append(phy.name) |
| 112 | for band in phy.bands: |
| 113 | for mhz in band.frequencies: |
| 114 | if mhz not in phys_for_frequency: |
| 115 | phys_for_frequency[mhz] = [phy.name] |
| 116 | else: |
| 117 | phys_for_frequency[mhz].append(phy.name) |
Paul Stewart | 6423bb0 | 2012-11-27 17:46:23 -0800 | [diff] [blame] | 118 | |
| 119 | phy_bus_type = {} |
| 120 | for phy in phy_list: |
| 121 | phybus = 'unknown' |
Christopher Wiley | 4c6a32c | 2013-04-24 13:37:51 -0700 | [diff] [blame] | 122 | command = '%s /sys/class/ieee80211/%s' % (self.cmd_readlink, phy) |
Paul Stewart | 6423bb0 | 2012-11-27 17:46:23 -0800 | [diff] [blame] | 123 | devpath = self.host.run(command).stdout |
| 124 | if '/usb' in devpath: |
| 125 | phybus = 'usb' |
| 126 | elif '/mmc' in devpath: |
| 127 | phybus = 'sdio' |
| 128 | elif '/pci' in devpath: |
| 129 | phybus = 'pci' |
| 130 | phy_bus_type[phy] = phybus |
Christopher Wiley | 9b40620 | 2013-05-06 14:07:49 -0700 | [diff] [blame] | 131 | logging.debug('Got phys for frequency: %r', phys_for_frequency) |
Paul Stewart | 6423bb0 | 2012-11-27 17:46:23 -0800 | [diff] [blame] | 132 | return phys_for_frequency, phy_bus_type |
Paul Stewart | 2ee7fdf | 2011-05-19 16:29:23 -0700 | [diff] [blame] | 133 | |
Paul Stewart | 64cc429 | 2011-06-01 10:59:36 -0700 | [diff] [blame] | 134 | |
Christopher Wiley | f671a5a | 2013-12-13 15:44:41 -0800 | [diff] [blame] | 135 | def remove_interface(self, interface): |
Christopher Wiley | 4c6a32c | 2013-04-24 13:37:51 -0700 | [diff] [blame] | 136 | """Remove an interface from a WiFi device. |
| 137 | |
| 138 | @param interface string interface to remove (e.g. wlan0). |
Christopher Wiley | 4c6a32c | 2013-04-24 13:37:51 -0700 | [diff] [blame] | 139 | |
| 140 | """ |
Christopher Wiley | f671a5a | 2013-12-13 15:44:41 -0800 | [diff] [blame] | 141 | self.release_interface(interface) |
Christopher Wiley | 4c6a32c | 2013-04-24 13:37:51 -0700 | [diff] [blame] | 142 | self.host.run('%s link set %s down' % (self.cmd_ip, interface)) |
Christopher Wiley | 7bd2e08 | 2013-10-16 17:40:40 -0700 | [diff] [blame] | 143 | self.iw_runner.remove_interface(interface) |
Christopher Wiley | f671a5a | 2013-12-13 15:44:41 -0800 | [diff] [blame] | 144 | for net_dev in self._interfaces: |
| 145 | if net_dev.if_name == interface: |
| 146 | self._interfaces.remove(net_dev) |
| 147 | break |
Paul Stewart | 2ee7fdf | 2011-05-19 16:29:23 -0700 | [diff] [blame] | 148 | |
Christopher Wiley | 061f138 | 2013-06-17 18:17:58 -0700 | [diff] [blame] | 149 | |
Christopher Wiley | f4bc88b | 2013-08-29 16:45:15 -0700 | [diff] [blame] | 150 | def close(self): |
| 151 | """Close global resources held by this system.""" |
| 152 | logging.debug('Cleaning up host object for %s', self.role) |
Christopher Wiley | 618e52b | 2013-10-14 16:21:07 -0700 | [diff] [blame] | 153 | self._packet_capturer.close() |
Christopher Wiley | f671a5a | 2013-12-13 15:44:41 -0800 | [diff] [blame] | 154 | # Release and remove any interfaces that we create. |
| 155 | for net_dev in self._wlanifs_in_use: |
| 156 | self.release_interface(net_dev.if_name) |
| 157 | for net_dev in self._interfaces: |
| 158 | if net_dev.inherited: |
| 159 | continue |
| 160 | self.remove_interface(net_dev.if_name) |
Christopher Wiley | f4bc88b | 2013-08-29 16:45:15 -0700 | [diff] [blame] | 161 | self.host.close() |
| 162 | self.host = None |
| 163 | |
| 164 | |
Christopher Wiley | 061f138 | 2013-06-17 18:17:58 -0700 | [diff] [blame] | 165 | def get_capabilities(self): |
Christopher Wiley | 16e494f | 2013-06-18 17:31:28 -0700 | [diff] [blame] | 166 | caps = set() |
Christopher Wiley | 061f138 | 2013-06-17 18:17:58 -0700 | [diff] [blame] | 167 | phymap = self.phys_for_frequency |
| 168 | if [freq for freq in phymap.iterkeys() if freq > 5000]: |
| 169 | # The frequencies are expressed in megaherz |
Christopher Wiley | 16e494f | 2013-06-18 17:31:28 -0700 | [diff] [blame] | 170 | caps.add(self.CAPABILITY_5GHZ) |
Christopher Wiley | 061f138 | 2013-06-17 18:17:58 -0700 | [diff] [blame] | 171 | if [freq for freq in phymap.iterkeys() if len(phymap[freq]) > 1]: |
Christopher Wiley | 16e494f | 2013-06-18 17:31:28 -0700 | [diff] [blame] | 172 | caps.add(self.CAPABILITY_MULTI_AP_SAME_BAND) |
| 173 | caps.add(self.CAPABILITY_MULTI_AP) |
Christopher Wiley | 061f138 | 2013-06-17 18:17:58 -0700 | [diff] [blame] | 174 | elif len(self.phy_bus_type) > 1: |
Christopher Wiley | 16e494f | 2013-06-18 17:31:28 -0700 | [diff] [blame] | 175 | caps.add(self.CAPABILITY_MULTI_AP) |
Paul Stewart | 27ecc5d | 2013-11-13 16:56:41 -0800 | [diff] [blame] | 176 | for phy in self.phy_list: |
| 177 | if 'tdls_mgmt' in phy.commands or 'tdls_oper' in phy.commands: |
| 178 | caps.add(self.CAPABILITY_TDLS) |
Christopher Wiley | 16e494f | 2013-06-18 17:31:28 -0700 | [diff] [blame] | 179 | return caps |
Christopher Wiley | 061f138 | 2013-06-17 18:17:58 -0700 | [diff] [blame] | 180 | |
| 181 | |
Christopher Wiley | 618e52b | 2013-10-14 16:21:07 -0700 | [diff] [blame] | 182 | def start_capture(self, frequency, ht_type=None, snaplen=None): |
Christopher Wiley | 6823b4f | 2013-04-22 18:40:24 -0700 | [diff] [blame] | 183 | """Start a packet capture. |
| 184 | |
| 185 | @param frequency int frequency of channel to capture on. |
| 186 | @param ht_type string one of (None, 'HT20', 'HT40+', 'HT40-'). |
Christopher Wiley | 618e52b | 2013-10-14 16:21:07 -0700 | [diff] [blame] | 187 | @param snaplen int number of bytes to retain per capture frame. |
Christopher Wiley | 6823b4f | 2013-04-22 18:40:24 -0700 | [diff] [blame] | 188 | |
| 189 | """ |
| 190 | if self._packet_capturer.capture_running: |
| 191 | self.stop_capture() |
Christopher Wiley | f671a5a | 2013-12-13 15:44:41 -0800 | [diff] [blame] | 192 | self._capture_interface = self.get_wlanif(frequency, 'monitor') |
| 193 | full_interface = [net_dev for net_dev in self._interfaces |
| 194 | if net_dev.if_name == self._capture_interface][0] |
| 195 | # If this is the only interface on this phy, we ought to configure |
| 196 | # the phy with a channel and ht_type. Otherwise, inherit the settings |
| 197 | # of the phy as they stand. |
| 198 | if len([net_dev for net_dev in self._interfaces |
| 199 | if net_dev.phy == full_interface.phy]) == 1: |
| 200 | self._packet_capturer.configure_raw_monitor( |
| 201 | self._capture_interface, frequency, ht_type=ht_type) |
Christopher Wiley | f737e02 | 2014-01-23 13:48:00 -0800 | [diff] [blame] | 202 | else: |
| 203 | self.host.run('%s link set %s up' % |
| 204 | (self.cmd_ip, self._capture_interface)) |
| 205 | |
Christopher Wiley | 6823b4f | 2013-04-22 18:40:24 -0700 | [diff] [blame] | 206 | # Start the capture. |
Christopher Wiley | f671a5a | 2013-12-13 15:44:41 -0800 | [diff] [blame] | 207 | self._packet_capturer.start_capture(self._capture_interface, './debug/', |
Christopher Wiley | 618e52b | 2013-10-14 16:21:07 -0700 | [diff] [blame] | 208 | snaplen=snaplen) |
Christopher Wiley | 6823b4f | 2013-04-22 18:40:24 -0700 | [diff] [blame] | 209 | |
| 210 | |
Christopher Wiley | 062a981 | 2013-10-03 15:55:50 -0700 | [diff] [blame] | 211 | def stop_capture(self, save_dir=None, save_filename=None): |
| 212 | """Stop a packet capture. |
| 213 | |
| 214 | @param save_dir string path to directory to save pcap files in. |
| 215 | @param save_filename string basename of file to save pcap in locally. |
| 216 | |
| 217 | """ |
Christopher Wiley | 6823b4f | 2013-04-22 18:40:24 -0700 | [diff] [blame] | 218 | if not self._packet_capturer.capture_running: |
Paul Stewart | 2ee7fdf | 2011-05-19 16:29:23 -0700 | [diff] [blame] | 219 | return |
Christopher Wiley | 618e52b | 2013-10-14 16:21:07 -0700 | [diff] [blame] | 220 | results = self._packet_capturer.stop_capture( |
| 221 | local_save_dir=save_dir, local_pcap_filename=save_filename) |
Christopher Wiley | f671a5a | 2013-12-13 15:44:41 -0800 | [diff] [blame] | 222 | self.release_interface(self._capture_interface) |
| 223 | self._capture_interface = None |
Christopher Wiley | 618e52b | 2013-10-14 16:21:07 -0700 | [diff] [blame] | 224 | return results |
Paul Stewart | 6423bb0 | 2012-11-27 17:46:23 -0800 | [diff] [blame] | 225 | |
| 226 | |
Christopher Wiley | 7c97ded | 2013-09-24 23:24:33 -0700 | [diff] [blame] | 227 | def sync_host_times(self): |
| 228 | """Set time on our DUT to match local time.""" |
| 229 | epoch_seconds = time.time() |
| 230 | busybox_format = '%Y%m%d%H%M.%S' |
| 231 | busybox_date = datetime.datetime.utcnow().strftime(busybox_format) |
| 232 | self.host.run('date -u --set=@%s 2>/dev/null || date -u %s' % |
| 233 | (epoch_seconds, busybox_date)) |
| 234 | |
Christopher Wiley | f671a5a | 2013-12-13 15:44:41 -0800 | [diff] [blame] | 235 | |
Paul Stewart | 6423bb0 | 2012-11-27 17:46:23 -0800 | [diff] [blame] | 236 | def _get_phy_for_frequency(self, frequency, phytype): |
Christopher Wiley | 4c6a32c | 2013-04-24 13:37:51 -0700 | [diff] [blame] | 237 | """Get a phy appropriate for a frequency and phytype. |
| 238 | |
| 239 | Return the most appropriate phy interface for operating on the |
| 240 | frequency |frequency| in the role indicated by |phytype|. Prefer idle |
| 241 | phys to busy phys if any exist. Secondarily, show affinity for phys |
| 242 | that use the bus type associated with this phy type. |
| 243 | |
| 244 | @param frequency int WiFi frequency of phy. |
| 245 | @param phytype string key of phytype registered at construction time. |
| 246 | @return string name of phy to use. |
| 247 | |
Paul Stewart | 6423bb0 | 2012-11-27 17:46:23 -0800 | [diff] [blame] | 248 | """ |
| 249 | phys = self.phys_for_frequency[frequency] |
| 250 | |
Christopher Wiley | f671a5a | 2013-12-13 15:44:41 -0800 | [diff] [blame] | 251 | busy_phys = set(net_dev.phy for net_dev in self._wlanifs_in_use) |
Paul Stewart | 6423bb0 | 2012-11-27 17:46:23 -0800 | [diff] [blame] | 252 | idle_phys = [phy for phy in phys if phy not in busy_phys] |
| 253 | phys = idle_phys or phys |
| 254 | |
Christopher Wiley | 408d181 | 2014-01-13 15:27:43 -0800 | [diff] [blame] | 255 | preferred_bus = {'monitor': 'usb', 'managed': 'pci'}.get(phytype) |
Paul Stewart | 6423bb0 | 2012-11-27 17:46:23 -0800 | [diff] [blame] | 256 | preferred_phys = [phy for phy in phys |
| 257 | if self.phy_bus_type[phy] == preferred_bus] |
| 258 | phys = preferred_phys or phys |
| 259 | |
| 260 | return phys[0] |
Paul Stewart | 2ee7fdf | 2011-05-19 16:29:23 -0700 | [diff] [blame] | 261 | |
| 262 | |
Christopher Wiley | 408d181 | 2014-01-13 15:27:43 -0800 | [diff] [blame] | 263 | def get_wlanif(self, frequency, phytype, same_phy_as=None): |
| 264 | """Get a WiFi device that supports the given frequency and type. |
Christopher Wiley | 4c6a32c | 2013-04-24 13:37:51 -0700 | [diff] [blame] | 265 | |
| 266 | @param frequency int WiFi frequency to support. |
| 267 | @param phytype string type of phy (e.g. 'monitor'). |
Paul Stewart | 51b0f38 | 2013-06-12 09:03:02 -0700 | [diff] [blame] | 268 | @param same_phy_as string create the interface on the same phy as this. |
Christopher Wiley | 4c6a32c | 2013-04-24 13:37:51 -0700 | [diff] [blame] | 269 | @return string WiFi device. |
| 270 | |
Paul Stewart | 2ee7fdf | 2011-05-19 16:29:23 -0700 | [diff] [blame] | 271 | """ |
Paul Stewart | 51b0f38 | 2013-06-12 09:03:02 -0700 | [diff] [blame] | 272 | if same_phy_as: |
Christopher Wiley | f671a5a | 2013-12-13 15:44:41 -0800 | [diff] [blame] | 273 | for net_dev in self._interfaces: |
| 274 | if net_dev.if_name == same_phy_as: |
| 275 | phy = net_dev.phy |
| 276 | break |
Paul Stewart | 51b0f38 | 2013-06-12 09:03:02 -0700 | [diff] [blame] | 277 | else: |
| 278 | raise error.TestFail('Unable to find phy for interface %s' % |
| 279 | same_phy_as) |
Paul Stewart | 6423bb0 | 2012-11-27 17:46:23 -0800 | [diff] [blame] | 280 | elif frequency in self.phys_for_frequency: |
| 281 | phy = self._get_phy_for_frequency(frequency, phytype) |
Paul Stewart | 2ee7fdf | 2011-05-19 16:29:23 -0700 | [diff] [blame] | 282 | else: |
Christopher Wiley | 408d181 | 2014-01-13 15:27:43 -0800 | [diff] [blame] | 283 | raise error.TestFail('Unable to find phy for frequency %d' % |
| 284 | frequency) |
Paul Stewart | 2ee7fdf | 2011-05-19 16:29:23 -0700 | [diff] [blame] | 285 | |
Christopher Wiley | f671a5a | 2013-12-13 15:44:41 -0800 | [diff] [blame] | 286 | # If we have a suitable unused interface sitting around on this |
| 287 | # phy, reuse it. |
| 288 | for net_dev in set(self._interfaces) - set(self._wlanifs_in_use): |
| 289 | if net_dev.phy == phy and net_dev.if_type == phytype: |
| 290 | self._wlanifs_in_use.append(net_dev) |
| 291 | return net_dev.if_name |
Paul Stewart | 2ee7fdf | 2011-05-19 16:29:23 -0700 | [diff] [blame] | 292 | |
Christopher Wiley | f671a5a | 2013-12-13 15:44:41 -0800 | [diff] [blame] | 293 | # Because we can reuse interfaces, we have to iteratively find a good |
| 294 | # interface name. |
| 295 | name_exists = lambda name: bool([net_dev |
| 296 | for net_dev in self._interfaces |
| 297 | if net_dev.if_name == name]) |
| 298 | if_name = lambda index: '%s%d' % (phytype, index) |
| 299 | if_index = len(self._interfaces) |
| 300 | while name_exists(if_name(if_index)): |
| 301 | if_index += 1 |
| 302 | net_dev = NetDev(phy=phy, if_name=if_name(if_index), if_type=phytype, |
| 303 | inherited=False) |
| 304 | self._interfaces.append(net_dev) |
| 305 | self._wlanifs_in_use.append(net_dev) |
| 306 | self.iw_runner.add_interface(phy, net_dev.if_name, phytype) |
| 307 | return net_dev.if_name |
Paul Stewart | 6423bb0 | 2012-11-27 17:46:23 -0800 | [diff] [blame] | 308 | |
| 309 | |
Christopher Wiley | f671a5a | 2013-12-13 15:44:41 -0800 | [diff] [blame] | 310 | def release_interface(self, wlanif): |
| 311 | """Release a device allocated throuhg get_wlanif(). |
Christopher Wiley | 4c6a32c | 2013-04-24 13:37:51 -0700 | [diff] [blame] | 312 | |
| 313 | @param wlanif string name of device to release. |
| 314 | |
| 315 | """ |
Christopher Wiley | f671a5a | 2013-12-13 15:44:41 -0800 | [diff] [blame] | 316 | for net_dev in self._wlanifs_in_use: |
| 317 | if net_dev.if_name == wlanif: |
| 318 | self._wlanifs_in_use.remove(net_dev) |
Christopher Wiley | 9b40620 | 2013-05-06 14:07:49 -0700 | [diff] [blame] | 319 | |
| 320 | |
| 321 | def require_capabilities(self, requirements, fatal_failure=False): |
| 322 | """Require capabilities of this LinuxSystem. |
| 323 | |
| 324 | Check that capabilities in |requirements| exist on this system. |
| 325 | Raise and exception to skip but not fail the test if said |
| 326 | capabilities are not found. Pass |fatal_failure| to cause this |
| 327 | error to become a test failure. |
| 328 | |
| 329 | @param requirements list of CAPABILITY_* defined above. |
| 330 | @param fatal_failure bool True iff failures should be fatal. |
| 331 | |
| 332 | """ |
| 333 | to_be_raised = error.TestNAError |
| 334 | if fatal_failure: |
| 335 | to_be_raised = error.TestFail |
| 336 | missing = [cap for cap in requirements if not cap in self.capabilities] |
| 337 | if missing: |
| 338 | raise to_be_raised('AP on %s is missing required capabilites: %r' % |
| 339 | (self.role, missing)) |
Peter Qiu | 2f97325 | 2014-02-20 15:30:37 -0800 | [diff] [blame^] | 340 | |
| 341 | |
| 342 | def set_antenna_bitmap(self, tx_bitmap, rx_bitmap): |
| 343 | """Setup antenna bitmaps for all the phys. |
| 344 | |
| 345 | @param tx_bitmap int bitmap of allowed antennas to use for TX |
| 346 | @param rx_bitmap int bitmap of allowed antennas to use for RX |
| 347 | |
| 348 | """ |
| 349 | for phy in self.phy_list: |
| 350 | self.iw_runner.set_antenna_bitmap(phy.name, tx_bitmap, rx_bitmap) |
| 351 | |
| 352 | |
| 353 | def set_default_antenna_bitmap(self): |
| 354 | """Setup default antenna bitmaps for all the phys.""" |
| 355 | for phy in self.phy_list: |
| 356 | self.iw_runner.set_antenna_bitmap(phy.name, phy.avail_tx_antennas, |
| 357 | phy.avail_rx_antennas) |