blob: 5219466be7cce045276b83ff1f81a635c65cb9b9 [file] [log] [blame]
//
// 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 <base/strings/stringprintf.h>
#include <brillo/strings/string_utils.h>
#include <shill/net/attribute_list.h>
#include <shill/net/ieee80211.h>
#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<string> 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