// // Copyright (C) 2014 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "apmanager/device.h" #include #include #include #include #include "apmanager/config.h" #include "apmanager/control_interface.h" #include "apmanager/manager.h" using shill::ByteString; using std::string; namespace apmanager { Device::Device(Manager* manager, const string& device_name, int identifier) : manager_(manager), supports_ap_mode_(false), identifier_(identifier), adaptor_(manager->control_interface()->CreateDeviceAdaptor(this)) { SetDeviceName(device_name); SetInUse(false); } Device::~Device() {} void Device::RegisterInterface(const WiFiInterface& new_interface) { LOG(INFO) << "RegisteringInterface " << new_interface.iface_name << " on device " << GetDeviceName(); for (const auto& interface : interface_list_) { // Done if interface already in the list. if (interface.iface_index == new_interface.iface_index) { LOG(INFO) << "Interface " << new_interface.iface_name << " already registered."; return; } } interface_list_.push_back(new_interface); UpdatePreferredAPInterface(); } void Device::DeregisterInterface(const WiFiInterface& interface) { LOG(INFO) << "DeregisteringInterface " << interface.iface_name << " on device " << GetDeviceName(); for (auto it = interface_list_.begin(); it != interface_list_.end(); ++it) { if (it->iface_index == interface.iface_index) { interface_list_.erase(it); UpdatePreferredAPInterface(); return; } } } void Device::ParseWiphyCapability(const shill::Nl80211Message& msg) { // Parse NL80211_ATTR_SUPPORTED_IFTYPES for AP mode interface support. shill::AttributeListConstRefPtr supported_iftypes; if (!msg.const_attributes()->ConstGetNestedAttributeList( NL80211_ATTR_SUPPORTED_IFTYPES, &supported_iftypes)) { LOG(ERROR) << "NL80211_CMD_NEW_WIPHY had no NL80211_ATTR_SUPPORTED_IFTYPES"; return; } supported_iftypes->GetFlagAttributeValue(NL80211_IFTYPE_AP, &supports_ap_mode_); // Parse WiFi band capabilities. shill::AttributeListConstRefPtr wiphy_bands; if (!msg.const_attributes()->ConstGetNestedAttributeList( NL80211_ATTR_WIPHY_BANDS, &wiphy_bands)) { LOG(ERROR) << "NL80211_CMD_NEW_WIPHY had no NL80211_ATTR_WIPHY_BANDS"; return; } shill::AttributeIdIterator band_iter(*wiphy_bands); for (; !band_iter.AtEnd(); band_iter.Advance()) { BandCapability band_cap; shill::AttributeListConstRefPtr wiphy_band; if (!wiphy_bands->ConstGetNestedAttributeList(band_iter.GetId(), &wiphy_band)) { LOG(WARNING) << "WiFi band " << band_iter.GetId() << " not found"; continue; } // ...Each band has a FREQS attribute... shill::AttributeListConstRefPtr frequencies; if (!wiphy_band->ConstGetNestedAttributeList(NL80211_BAND_ATTR_FREQS, &frequencies)) { LOG(ERROR) << "BAND " << band_iter.GetId() << " had no 'frequencies' attribute"; continue; } // ...And each FREQS attribute contains an array of information about the // frequency... shill::AttributeIdIterator freq_iter(*frequencies); for (; !freq_iter.AtEnd(); freq_iter.Advance()) { shill::AttributeListConstRefPtr frequency; if (frequencies->ConstGetNestedAttributeList(freq_iter.GetId(), &frequency)) { // ...Including the frequency, itself (the part we want). uint32_t frequency_value = 0; if (frequency->GetU32AttributeValue(NL80211_FREQUENCY_ATTR_FREQ, &frequency_value)) { band_cap.frequencies.push_back(frequency_value); } } } wiphy_band->GetU16AttributeValue(NL80211_BAND_ATTR_HT_CAPA, &band_cap.ht_capability_mask); wiphy_band->GetU16AttributeValue(NL80211_BAND_ATTR_VHT_CAPA, &band_cap.vht_capability_mask); band_capability_.push_back(band_cap); } } bool Device::ClaimDevice(bool full_control) { if (GetInUse()) { LOG(ERROR) << "Failed to claim device [" << GetDeviceName() << "]: already in used."; return false; } if (full_control) { for (const auto& interface : interface_list_) { manager_->ClaimInterface(interface.iface_name); claimed_interfaces_.insert(interface.iface_name); } } else { manager_->ClaimInterface(GetPreferredApInterface()); claimed_interfaces_.insert(GetPreferredApInterface()); } SetInUse(true); return true; } bool Device::ReleaseDevice() { if (!GetInUse()) { LOG(ERROR) << "Failed to release device [" << GetDeviceName() << "]: not currently in-used."; return false; } for (const auto& interface : claimed_interfaces_) { manager_->ReleaseInterface(interface); } claimed_interfaces_.clear(); SetInUse(false); return true; } bool Device::InterfaceExists(const string& interface_name) { for (const auto& interface : interface_list_) { if (interface.iface_name == interface_name) { return true; } } return false; } bool Device::GetHTCapability(uint16_t channel, string* ht_cap) { // Get the band capability based on the channel. BandCapability band_cap; if (!GetBandCapability(channel, &band_cap)) { LOG(ERROR) << "No band capability found for channel " << channel; return false; } std::vector ht_capability; // LDPC coding capability. if (band_cap.ht_capability_mask & shill::IEEE_80211::kHTCapMaskLdpcCoding) { ht_capability.push_back("LDPC"); } // Supported channel width set. if (band_cap.ht_capability_mask & shill::IEEE_80211::kHTCapMaskSupWidth2040) { // Determine secondary channel is below or above the primary. bool above = false; if (!GetHTSecondaryChannelLocation(channel, &above)) { LOG(ERROR) << "Unable to determine secondary channel location for " << "channel " << channel; return false; } if (above) { ht_capability.push_back("HT40+"); } else { ht_capability.push_back("HT40-"); } } // Spatial Multiplexing (SM) Power Save. uint16_t power_save_mask = (band_cap.ht_capability_mask >> shill::IEEE_80211::kHTCapMaskSmPsShift) & 0x3; if (power_save_mask == 0) { ht_capability.push_back("SMPS-STATIC"); } else if (power_save_mask == 1) { ht_capability.push_back("SMPS-DYNAMIC"); } // HT-greenfield. if (band_cap.ht_capability_mask & shill::IEEE_80211::kHTCapMaskGrnFld) { ht_capability.push_back("GF"); } // Short GI for 20 MHz. if (band_cap.ht_capability_mask & shill::IEEE_80211::kHTCapMaskSgi20) { ht_capability.push_back("SHORT-GI-20"); } // Short GI for 40 MHz. if (band_cap.ht_capability_mask & shill::IEEE_80211::kHTCapMaskSgi40) { ht_capability.push_back("SHORT-GI-40"); } // Tx STBC. if (band_cap.ht_capability_mask & shill::IEEE_80211::kHTCapMaskTxStbc) { ht_capability.push_back("TX-STBC"); } // Rx STBC. uint16_t rx_stbc = (band_cap.ht_capability_mask >> shill::IEEE_80211::kHTCapMaskRxStbcShift) & 0x3; if (rx_stbc == 1) { ht_capability.push_back("RX-STBC1"); } else if (rx_stbc == 2) { ht_capability.push_back("RX-STBC12"); } else if (rx_stbc == 3) { ht_capability.push_back("RX-STBC123"); } // HT-delayed Block Ack. if (band_cap.ht_capability_mask & shill::IEEE_80211::kHTCapMaskDelayBA) { ht_capability.push_back("DELAYED-BA"); } // Maximum A-MSDU length. if (band_cap.ht_capability_mask & shill::IEEE_80211::kHTCapMaskMaxAmsdu) { ht_capability.push_back("MAX-AMSDU-7935"); } // DSSS/CCK Mode in 40 MHz. if (band_cap.ht_capability_mask & shill::IEEE_80211::kHTCapMaskDsssCck40) { ht_capability.push_back("DSSS_CCK-40"); } // 40 MHz intolerant. if (band_cap.ht_capability_mask & shill::IEEE_80211::kHTCapMask40MHzIntolerant) { ht_capability.push_back("40-INTOLERANT"); } *ht_cap = base::StringPrintf("[%s]", brillo::string_utils::Join(" ", ht_capability).c_str()); return true; } bool Device::GetVHTCapability(uint16_t channel, string* vht_cap) { // TODO(zqiu): to be implemented. return false; } void Device::SetDeviceName(const std::string& device_name) { adaptor_->SetDeviceName(device_name); } string Device::GetDeviceName() const { return adaptor_->GetDeviceName(); } void Device::SetPreferredApInterface(const std::string& interface_name) { adaptor_->SetPreferredApInterface(interface_name); } string Device::GetPreferredApInterface() const { return adaptor_->GetPreferredApInterface(); } void Device::SetInUse(bool in_use) { return adaptor_->SetInUse(in_use); } bool Device::GetInUse() const { return adaptor_->GetInUse(); } // static bool Device::GetHTSecondaryChannelLocation(uint16_t channel, bool* above) { bool ret_val = true; // Determine secondary channel location base on the channel. Refer to // ht_cap section in hostapd.conf documentation. switch (channel) { case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 40: case 48: case 56: case 64: *above = false; break; case 1: case 2: case 3: case 4: case 5: case 6: case 36: case 44: case 52: case 60: *above = true; break; default: ret_val = false; break; } return ret_val; } bool Device::GetBandCapability(uint16_t channel, BandCapability* capability) { uint32_t frequency; if (!Config::GetFrequencyFromChannel(channel, &frequency)) { LOG(ERROR) << "Invalid channel " << channel; return false; } for (const auto& band : band_capability_) { if (std::find(band.frequencies.begin(), band.frequencies.end(), frequency) != band.frequencies.end()) { *capability = band; return true; } } return false; } void Device::UpdatePreferredAPInterface() { // Return if device doesn't support AP interface mode. if (!supports_ap_mode_) { return; } // Use the first registered AP mode interface if there is one, otherwise use // the first registered managed mode interface. If none are available, then // no interface can be used for AP operation on this device. WiFiInterface preferred_interface; for (const auto& interface : interface_list_) { if (interface.iface_type == NL80211_IFTYPE_AP) { preferred_interface = interface; break; } else if (interface.iface_type == NL80211_IFTYPE_STATION && preferred_interface.iface_name.empty()) { preferred_interface = interface; } // Ignore all other interface types. } // Update preferred AP interface property. SetPreferredApInterface(preferred_interface.iface_name); } } // namespace apmanager