Peter Qiu | 326b6cf | 2015-09-02 11:11:42 -0700 | [diff] [blame] | 1 | // |
| 2 | // Copyright (C) 2014 The Android Open Source Project |
| 3 | // |
| 4 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | // you may not use this file except in compliance with the License. |
| 6 | // You may obtain a copy of the License at |
| 7 | // |
| 8 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | // |
| 10 | // Unless required by applicable law or agreed to in writing, software |
| 11 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | // See the License for the specific language governing permissions and |
| 14 | // limitations under the License. |
| 15 | // |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 16 | |
| 17 | #include "apmanager/device.h" |
| 18 | |
| 19 | #include <base/strings/stringprintf.h> |
Alex Vakulenko | 8d0c31b | 2015-10-13 09:14:24 -0700 | [diff] [blame] | 20 | #include <brillo/strings/string_utils.h> |
Peter Qiu | 8e785b9 | 2014-11-24 10:01:08 -0800 | [diff] [blame] | 21 | #include <shill/net/attribute_list.h> |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 22 | #include <shill/net/ieee80211.h> |
| 23 | |
Peter Qiu | 8e785b9 | 2014-11-24 10:01:08 -0800 | [diff] [blame] | 24 | #include "apmanager/config.h" |
Peter Qiu | f933540 | 2015-11-16 12:09:16 -0800 | [diff] [blame] | 25 | #include "apmanager/control_interface.h" |
Peter Qiu | 7e0ffcf | 2014-12-02 12:53:27 -0800 | [diff] [blame] | 26 | #include "apmanager/manager.h" |
Peter Qiu | 8e785b9 | 2014-11-24 10:01:08 -0800 | [diff] [blame] | 27 | |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 28 | using shill::ByteString; |
| 29 | using std::string; |
| 30 | |
| 31 | namespace apmanager { |
| 32 | |
Peter Qiu | f933540 | 2015-11-16 12:09:16 -0800 | [diff] [blame] | 33 | Device::Device(Manager* manager, |
| 34 | const string& device_name, |
| 35 | int identifier) |
| 36 | : manager_(manager), |
| 37 | supports_ap_mode_(false), |
| 38 | identifier_(identifier), |
| 39 | adaptor_(manager->control_interface()->CreateDeviceAdaptor(this)) { |
Peter Qiu | 1ff67a7 | 2014-11-22 07:06:10 -0800 | [diff] [blame] | 40 | SetDeviceName(device_name); |
Garret Kelly | 0c0e9e7 | 2015-09-01 17:28:01 -0400 | [diff] [blame] | 41 | SetInUse(false); |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 42 | } |
| 43 | |
| 44 | Device::~Device() {} |
| 45 | |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 46 | void Device::RegisterInterface(const WiFiInterface& new_interface) { |
| 47 | LOG(INFO) << "RegisteringInterface " << new_interface.iface_name |
| 48 | << " on device " << GetDeviceName(); |
| 49 | for (const auto& interface : interface_list_) { |
| 50 | // Done if interface already in the list. |
| 51 | if (interface.iface_index == new_interface.iface_index) { |
| 52 | LOG(INFO) << "Interface " << new_interface.iface_name |
| 53 | << " already registered."; |
| 54 | return; |
| 55 | } |
| 56 | } |
| 57 | interface_list_.push_back(new_interface); |
| 58 | UpdatePreferredAPInterface(); |
| 59 | } |
| 60 | |
| 61 | void Device::DeregisterInterface(const WiFiInterface& interface) { |
| 62 | LOG(INFO) << "DeregisteringInterface " << interface.iface_name |
| 63 | << " on device " << GetDeviceName(); |
| 64 | for (auto it = interface_list_.begin(); it != interface_list_.end(); ++it) { |
| 65 | if (it->iface_index == interface.iface_index) { |
| 66 | interface_list_.erase(it); |
| 67 | UpdatePreferredAPInterface(); |
| 68 | return; |
| 69 | } |
| 70 | } |
| 71 | } |
| 72 | |
Peter Qiu | 1ff67a7 | 2014-11-22 07:06:10 -0800 | [diff] [blame] | 73 | void Device::ParseWiphyCapability(const shill::Nl80211Message& msg) { |
Peter Qiu | 3d95ac7 | 2014-12-06 18:26:18 -0800 | [diff] [blame] | 74 | // Parse NL80211_ATTR_SUPPORTED_IFTYPES for AP mode interface support. |
| 75 | shill::AttributeListConstRefPtr supported_iftypes; |
| 76 | if (!msg.const_attributes()->ConstGetNestedAttributeList( |
| 77 | NL80211_ATTR_SUPPORTED_IFTYPES, &supported_iftypes)) { |
| 78 | LOG(ERROR) << "NL80211_CMD_NEW_WIPHY had no NL80211_ATTR_SUPPORTED_IFTYPES"; |
| 79 | return; |
| 80 | } |
| 81 | supported_iftypes->GetFlagAttributeValue(NL80211_IFTYPE_AP, |
| 82 | &supports_ap_mode_); |
Peter Qiu | 8e785b9 | 2014-11-24 10:01:08 -0800 | [diff] [blame] | 83 | |
| 84 | // Parse WiFi band capabilities. |
| 85 | shill::AttributeListConstRefPtr wiphy_bands; |
| 86 | if (!msg.const_attributes()->ConstGetNestedAttributeList( |
| 87 | NL80211_ATTR_WIPHY_BANDS, &wiphy_bands)) { |
| 88 | LOG(ERROR) << "NL80211_CMD_NEW_WIPHY had no NL80211_ATTR_WIPHY_BANDS"; |
| 89 | return; |
| 90 | } |
| 91 | |
| 92 | shill::AttributeIdIterator band_iter(*wiphy_bands); |
| 93 | for (; !band_iter.AtEnd(); band_iter.Advance()) { |
| 94 | BandCapability band_cap; |
| 95 | |
| 96 | shill::AttributeListConstRefPtr wiphy_band; |
| 97 | if (!wiphy_bands->ConstGetNestedAttributeList(band_iter.GetId(), |
| 98 | &wiphy_band)) { |
| 99 | LOG(WARNING) << "WiFi band " << band_iter.GetId() << " not found"; |
| 100 | continue; |
| 101 | } |
| 102 | |
| 103 | // ...Each band has a FREQS attribute... |
| 104 | shill::AttributeListConstRefPtr frequencies; |
| 105 | if (!wiphy_band->ConstGetNestedAttributeList(NL80211_BAND_ATTR_FREQS, |
| 106 | &frequencies)) { |
| 107 | LOG(ERROR) << "BAND " << band_iter.GetId() |
| 108 | << " had no 'frequencies' attribute"; |
| 109 | continue; |
| 110 | } |
| 111 | |
| 112 | // ...And each FREQS attribute contains an array of information about the |
| 113 | // frequency... |
| 114 | shill::AttributeIdIterator freq_iter(*frequencies); |
| 115 | for (; !freq_iter.AtEnd(); freq_iter.Advance()) { |
| 116 | shill::AttributeListConstRefPtr frequency; |
| 117 | if (frequencies->ConstGetNestedAttributeList(freq_iter.GetId(), |
| 118 | &frequency)) { |
| 119 | // ...Including the frequency, itself (the part we want). |
| 120 | uint32_t frequency_value = 0; |
| 121 | if (frequency->GetU32AttributeValue(NL80211_FREQUENCY_ATTR_FREQ, |
| 122 | &frequency_value)) { |
| 123 | band_cap.frequencies.push_back(frequency_value); |
| 124 | } |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | wiphy_band->GetU16AttributeValue(NL80211_BAND_ATTR_HT_CAPA, |
| 129 | &band_cap.ht_capability_mask); |
| 130 | wiphy_band->GetU16AttributeValue(NL80211_BAND_ATTR_VHT_CAPA, |
| 131 | &band_cap.vht_capability_mask); |
| 132 | band_capability_.push_back(band_cap); |
| 133 | } |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 134 | } |
| 135 | |
Peter Qiu | 0ca183b | 2015-03-09 13:41:06 -0700 | [diff] [blame] | 136 | bool Device::ClaimDevice(bool full_control) { |
Garret Kelly | 0c0e9e7 | 2015-09-01 17:28:01 -0400 | [diff] [blame] | 137 | if (GetInUse()) { |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 138 | LOG(ERROR) << "Failed to claim device [" << GetDeviceName() |
| 139 | << "]: already in used."; |
| 140 | return false; |
| 141 | } |
| 142 | |
Peter Qiu | 0ca183b | 2015-03-09 13:41:06 -0700 | [diff] [blame] | 143 | if (full_control) { |
| 144 | for (const auto& interface : interface_list_) { |
| 145 | manager_->ClaimInterface(interface.iface_name); |
| 146 | claimed_interfaces_.insert(interface.iface_name); |
| 147 | } |
| 148 | } else { |
| 149 | manager_->ClaimInterface(GetPreferredApInterface()); |
| 150 | claimed_interfaces_.insert(GetPreferredApInterface()); |
Peter Qiu | 7e0ffcf | 2014-12-02 12:53:27 -0800 | [diff] [blame] | 151 | } |
Garret Kelly | 0c0e9e7 | 2015-09-01 17:28:01 -0400 | [diff] [blame] | 152 | SetInUse(true); |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 153 | return true; |
| 154 | } |
| 155 | |
| 156 | bool Device::ReleaseDevice() { |
Garret Kelly | 0c0e9e7 | 2015-09-01 17:28:01 -0400 | [diff] [blame] | 157 | if (!GetInUse()) { |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 158 | LOG(ERROR) << "Failed to release device [" << GetDeviceName() |
| 159 | << "]: not currently in-used."; |
| 160 | return false; |
| 161 | } |
| 162 | |
Peter Qiu | 0ca183b | 2015-03-09 13:41:06 -0700 | [diff] [blame] | 163 | for (const auto& interface : claimed_interfaces_) { |
| 164 | manager_->ReleaseInterface(interface); |
Peter Qiu | 7e0ffcf | 2014-12-02 12:53:27 -0800 | [diff] [blame] | 165 | } |
Peter Qiu | 0ca183b | 2015-03-09 13:41:06 -0700 | [diff] [blame] | 166 | claimed_interfaces_.clear(); |
Garret Kelly | 0c0e9e7 | 2015-09-01 17:28:01 -0400 | [diff] [blame] | 167 | SetInUse(false); |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 168 | return true; |
| 169 | } |
| 170 | |
| 171 | bool Device::InterfaceExists(const string& interface_name) { |
| 172 | for (const auto& interface : interface_list_) { |
| 173 | if (interface.iface_name == interface_name) { |
| 174 | return true; |
| 175 | } |
| 176 | } |
| 177 | return false; |
| 178 | } |
| 179 | |
Peter Qiu | 8e785b9 | 2014-11-24 10:01:08 -0800 | [diff] [blame] | 180 | bool Device::GetHTCapability(uint16_t channel, string* ht_cap) { |
| 181 | // Get the band capability based on the channel. |
| 182 | BandCapability band_cap; |
| 183 | if (!GetBandCapability(channel, &band_cap)) { |
| 184 | LOG(ERROR) << "No band capability found for channel " << channel; |
| 185 | return false; |
| 186 | } |
| 187 | |
| 188 | std::vector<string> ht_capability; |
| 189 | // LDPC coding capability. |
| 190 | if (band_cap.ht_capability_mask & shill::IEEE_80211::kHTCapMaskLdpcCoding) { |
| 191 | ht_capability.push_back("LDPC"); |
| 192 | } |
| 193 | |
| 194 | // Supported channel width set. |
| 195 | if (band_cap.ht_capability_mask & |
| 196 | shill::IEEE_80211::kHTCapMaskSupWidth2040) { |
| 197 | // Determine secondary channel is below or above the primary. |
| 198 | bool above = false; |
| 199 | if (!GetHTSecondaryChannelLocation(channel, &above)) { |
| 200 | LOG(ERROR) << "Unable to determine secondary channel location for " |
| 201 | << "channel " << channel; |
| 202 | return false; |
| 203 | } |
| 204 | if (above) { |
| 205 | ht_capability.push_back("HT40+"); |
| 206 | } else { |
| 207 | ht_capability.push_back("HT40-"); |
| 208 | } |
| 209 | } |
| 210 | |
| 211 | // Spatial Multiplexing (SM) Power Save. |
| 212 | uint16_t power_save_mask = |
| 213 | (band_cap.ht_capability_mask >> |
| 214 | shill::IEEE_80211::kHTCapMaskSmPsShift) & 0x3; |
| 215 | if (power_save_mask == 0) { |
| 216 | ht_capability.push_back("SMPS-STATIC"); |
| 217 | } else if (power_save_mask == 1) { |
| 218 | ht_capability.push_back("SMPS-DYNAMIC"); |
| 219 | } |
| 220 | |
| 221 | // HT-greenfield. |
| 222 | if (band_cap.ht_capability_mask & shill::IEEE_80211::kHTCapMaskGrnFld) { |
| 223 | ht_capability.push_back("GF"); |
| 224 | } |
| 225 | |
| 226 | // Short GI for 20 MHz. |
| 227 | if (band_cap.ht_capability_mask & shill::IEEE_80211::kHTCapMaskSgi20) { |
| 228 | ht_capability.push_back("SHORT-GI-20"); |
| 229 | } |
| 230 | |
| 231 | // Short GI for 40 MHz. |
| 232 | if (band_cap.ht_capability_mask & shill::IEEE_80211::kHTCapMaskSgi40) { |
| 233 | ht_capability.push_back("SHORT-GI-40"); |
| 234 | } |
| 235 | |
| 236 | // Tx STBC. |
| 237 | if (band_cap.ht_capability_mask & shill::IEEE_80211::kHTCapMaskTxStbc) { |
| 238 | ht_capability.push_back("TX-STBC"); |
| 239 | } |
| 240 | |
| 241 | // Rx STBC. |
| 242 | uint16_t rx_stbc = |
| 243 | (band_cap.ht_capability_mask >> |
| 244 | shill::IEEE_80211::kHTCapMaskRxStbcShift) & 0x3; |
| 245 | if (rx_stbc == 1) { |
| 246 | ht_capability.push_back("RX-STBC1"); |
| 247 | } else if (rx_stbc == 2) { |
| 248 | ht_capability.push_back("RX-STBC12"); |
| 249 | } else if (rx_stbc == 3) { |
| 250 | ht_capability.push_back("RX-STBC123"); |
| 251 | } |
| 252 | |
| 253 | // HT-delayed Block Ack. |
| 254 | if (band_cap.ht_capability_mask & shill::IEEE_80211::kHTCapMaskDelayBA) { |
| 255 | ht_capability.push_back("DELAYED-BA"); |
| 256 | } |
| 257 | |
| 258 | // Maximum A-MSDU length. |
| 259 | if (band_cap.ht_capability_mask & shill::IEEE_80211::kHTCapMaskMaxAmsdu) { |
| 260 | ht_capability.push_back("MAX-AMSDU-7935"); |
| 261 | } |
| 262 | |
| 263 | // DSSS/CCK Mode in 40 MHz. |
| 264 | if (band_cap.ht_capability_mask & shill::IEEE_80211::kHTCapMaskDsssCck40) { |
| 265 | ht_capability.push_back("DSSS_CCK-40"); |
| 266 | } |
| 267 | |
| 268 | // 40 MHz intolerant. |
| 269 | if (band_cap.ht_capability_mask & |
| 270 | shill::IEEE_80211::kHTCapMask40MHzIntolerant) { |
| 271 | ht_capability.push_back("40-INTOLERANT"); |
| 272 | } |
| 273 | |
| 274 | *ht_cap = base::StringPrintf("[%s]", |
Alex Vakulenko | 8d0c31b | 2015-10-13 09:14:24 -0700 | [diff] [blame] | 275 | brillo::string_utils::Join(" ", ht_capability).c_str()); |
Peter Qiu | 8e785b9 | 2014-11-24 10:01:08 -0800 | [diff] [blame] | 276 | return true; |
| 277 | } |
| 278 | |
| 279 | bool Device::GetVHTCapability(uint16_t channel, string* vht_cap) { |
| 280 | // TODO(zqiu): to be implemented. |
| 281 | return false; |
| 282 | } |
| 283 | |
Peter Qiu | f933540 | 2015-11-16 12:09:16 -0800 | [diff] [blame] | 284 | void Device::SetDeviceName(const std::string& device_name) { |
| 285 | adaptor_->SetDeviceName(device_name); |
| 286 | } |
| 287 | |
| 288 | string Device::GetDeviceName() const { |
| 289 | return adaptor_->GetDeviceName(); |
| 290 | } |
| 291 | |
| 292 | void Device::SetPreferredApInterface(const std::string& interface_name) { |
| 293 | adaptor_->SetPreferredApInterface(interface_name); |
| 294 | } |
| 295 | |
| 296 | string Device::GetPreferredApInterface() const { |
| 297 | return adaptor_->GetPreferredApInterface(); |
| 298 | } |
| 299 | |
| 300 | void Device::SetInUse(bool in_use) { |
| 301 | return adaptor_->SetInUse(in_use); |
| 302 | } |
| 303 | |
| 304 | bool Device::GetInUse() const { |
| 305 | return adaptor_->GetInUse(); |
| 306 | } |
| 307 | |
Peter Qiu | 8e785b9 | 2014-11-24 10:01:08 -0800 | [diff] [blame] | 308 | // static |
| 309 | bool Device::GetHTSecondaryChannelLocation(uint16_t channel, bool* above) { |
| 310 | bool ret_val = true; |
| 311 | |
| 312 | // Determine secondary channel location base on the channel. Refer to |
| 313 | // ht_cap section in hostapd.conf documentation. |
| 314 | switch (channel) { |
| 315 | case 7: |
| 316 | case 8: |
| 317 | case 9: |
| 318 | case 10: |
| 319 | case 11: |
| 320 | case 12: |
| 321 | case 13: |
| 322 | case 40: |
| 323 | case 48: |
| 324 | case 56: |
| 325 | case 64: |
| 326 | *above = false; |
| 327 | break; |
| 328 | |
| 329 | case 1: |
| 330 | case 2: |
| 331 | case 3: |
| 332 | case 4: |
| 333 | case 5: |
| 334 | case 6: |
| 335 | case 36: |
| 336 | case 44: |
| 337 | case 52: |
| 338 | case 60: |
| 339 | *above = true; |
| 340 | break; |
| 341 | |
| 342 | default: |
| 343 | ret_val = false; |
| 344 | break; |
| 345 | } |
| 346 | |
| 347 | return ret_val; |
| 348 | } |
| 349 | |
| 350 | bool Device::GetBandCapability(uint16_t channel, BandCapability* capability) { |
| 351 | uint32_t frequency; |
| 352 | if (!Config::GetFrequencyFromChannel(channel, &frequency)) { |
| 353 | LOG(ERROR) << "Invalid channel " << channel; |
| 354 | return false; |
| 355 | } |
| 356 | |
| 357 | for (const auto& band : band_capability_) { |
| 358 | if (std::find(band.frequencies.begin(), |
| 359 | band.frequencies.end(), |
| 360 | frequency) != band.frequencies.end()) { |
| 361 | *capability = band; |
| 362 | return true; |
| 363 | } |
| 364 | } |
| 365 | return false; |
| 366 | } |
| 367 | |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 368 | void Device::UpdatePreferredAPInterface() { |
Peter Qiu | 3d95ac7 | 2014-12-06 18:26:18 -0800 | [diff] [blame] | 369 | // Return if device doesn't support AP interface mode. |
| 370 | if (!supports_ap_mode_) { |
| 371 | return; |
| 372 | } |
Peter Qiu | 8e785b9 | 2014-11-24 10:01:08 -0800 | [diff] [blame] | 373 | |
Peter Qiu | fb39ba4 | 2014-11-21 09:09:59 -0800 | [diff] [blame] | 374 | // Use the first registered AP mode interface if there is one, otherwise use |
| 375 | // the first registered managed mode interface. If none are available, then |
| 376 | // no interface can be used for AP operation on this device. |
| 377 | WiFiInterface preferred_interface; |
| 378 | for (const auto& interface : interface_list_) { |
| 379 | if (interface.iface_type == NL80211_IFTYPE_AP) { |
| 380 | preferred_interface = interface; |
| 381 | break; |
| 382 | } else if (interface.iface_type == NL80211_IFTYPE_STATION && |
| 383 | preferred_interface.iface_name.empty()) { |
| 384 | preferred_interface = interface; |
| 385 | } |
| 386 | // Ignore all other interface types. |
| 387 | } |
| 388 | // Update preferred AP interface property. |
| 389 | SetPreferredApInterface(preferred_interface.iface_name); |
| 390 | } |
| 391 | |
| 392 | } // namespace apmanager |