Release 4.14 net/mac80211/iface.c
/*
* Interface handling
*
* Copyright 2002-2005, Instant802 Networks, Inc.
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright (c) 2006 Jiri Benc <jbenc@suse.cz>
* Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright (c) 2016 Intel Deutschland GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/if_arp.h>
#include <linux/netdevice.h>
#include <linux/rtnetlink.h>
#include <net/mac80211.h>
#include <net/ieee80211_radiotap.h>
#include "ieee80211_i.h"
#include "sta_info.h"
#include "debugfs_netdev.h"
#include "mesh.h"
#include "led.h"
#include "driver-ops.h"
#include "wme.h"
#include "rate.h"
/**
* DOC: Interface list locking
*
* The interface list in each struct ieee80211_local is protected
* three-fold:
*
* (1) modifications may only be done under the RTNL
* (2) modifications and readers are protected against each other by
* the iflist_mtx.
* (3) modifications are done in an RCU manner so atomic readers
* can traverse the list in RCU-safe blocks.
*
* As a consequence, reads (traversals) of the list can be protected
* by either the RTNL, the iflist_mtx or RCU.
*/
static void ieee80211_iface_work(struct work_struct *work);
bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_chanctx_conf *chanctx_conf;
int power;
rcu_read_lock();
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (!chanctx_conf) {
rcu_read_unlock();
return false;
}
power = ieee80211_chandef_max_power(&chanctx_conf->def);
rcu_read_unlock();
if (sdata->user_power_level != IEEE80211_UNSET_POWER_LEVEL)
power = min(power, sdata->user_power_level);
if (sdata->ap_power_level != IEEE80211_UNSET_POWER_LEVEL)
power = min(power, sdata->ap_power_level);
if (power != sdata->vif.bss_conf.txpower) {
sdata->vif.bss_conf.txpower = power;
ieee80211_hw_config(sdata->local, 0);
return true;
}
return false;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 131 | 97.04% | 2 | 66.67% |
Simon Wunderlich | 4 | 2.96% | 1 | 33.33% |
Total | 135 | 100.00% | 3 | 100.00% |
void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata,
bool update_bss)
{
if (__ieee80211_recalc_txpower(sdata) ||
(update_bss && ieee80211_sdata_running(sdata)))
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_TXPOWER);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 24 | 66.67% | 1 | 33.33% |
Emmanuel Grumbach | 7 | 19.44% | 1 | 33.33% |
Lorenzo Bianconi | 5 | 13.89% | 1 | 33.33% |
Total | 36 | 100.00% | 3 | 100.00% |
static u32 __ieee80211_idle_off(struct ieee80211_local *local)
{
if (!(local->hw.conf.flags & IEEE80211_CONF_IDLE))
return 0;
local->hw.conf.flags &= ~IEEE80211_CONF_IDLE;
return IEEE80211_CONF_CHANGE_IDLE;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 43 | 100.00% | 3 | 100.00% |
Total | 43 | 100.00% | 3 | 100.00% |
static u32 __ieee80211_idle_on(struct ieee80211_local *local)
{
if (local->hw.conf.flags & IEEE80211_CONF_IDLE)
return 0;
ieee80211_flush_queues(local, NULL, false);
local->hw.conf.flags |= IEEE80211_CONF_IDLE;
return IEEE80211_CONF_CHANGE_IDLE;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 46 | 95.83% | 4 | 80.00% |
Emmanuel Grumbach | 2 | 4.17% | 1 | 20.00% |
Total | 48 | 100.00% | 5 | 100.00% |
static u32 __ieee80211_recalc_idle(struct ieee80211_local *local,
bool force_active)
{
bool working, scanning, active;
unsigned int led_trig_start = 0, led_trig_stop = 0;
lockdep_assert_held(&local->mtx);
active = force_active ||
!list_empty(&local->chanctx_list) ||
local->monitors;
working = !local->ops->remain_on_channel &&
!list_empty(&local->roc_list);
scanning = test_bit(SCAN_SW_SCANNING, &local->scanning) ||
test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning);
if (working || scanning)
led_trig_start |= IEEE80211_TPT_LEDTRIG_FL_WORK;
else
led_trig_stop |= IEEE80211_TPT_LEDTRIG_FL_WORK;
if (active)
led_trig_start |= IEEE80211_TPT_LEDTRIG_FL_CONNECTED;
else
led_trig_stop |= IEEE80211_TPT_LEDTRIG_FL_CONNECTED;
ieee80211_mod_tpt_led_trig(local, led_trig_start, led_trig_stop);
if (working || scanning || active)
return __ieee80211_idle_off(local);
return __ieee80211_idle_on(local);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 143 | 93.46% | 9 | 69.23% |
Felix Fietkau | 4 | 2.61% | 1 | 7.69% |
Christian Lamparter | 3 | 1.96% | 1 | 7.69% |
Luciano Coelho | 2 | 1.31% | 1 | 7.69% |
Kalle Valo | 1 | 0.65% | 1 | 7.69% |
Total | 153 | 100.00% | 13 | 100.00% |
u32 ieee80211_idle_off(struct ieee80211_local *local)
{
return __ieee80211_recalc_idle(local, true);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 18 | 100.00% | 2 | 100.00% |
Total | 18 | 100.00% | 2 | 100.00% |
void ieee80211_recalc_idle(struct ieee80211_local *local)
{
u32 change = __ieee80211_recalc_idle(local, false);
if (change)
ieee80211_hw_config(local, change);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 31 | 100.00% | 5 | 100.00% |
Total | 31 | 100.00% | 5 | 100.00% |
static int ieee80211_verify_mac(struct ieee80211_sub_if_data *sdata, u8 *addr,
bool check_dup)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_sub_if_data *iter;
u64 new, mask, tmp;
u8 *m;
int ret = 0;
if (is_zero_ether_addr(local->hw.wiphy->addr_mask))
return 0;
m = addr;
new = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
m = local->hw.wiphy->addr_mask;
mask = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
if (!check_dup)
return ret;
mutex_lock(&local->iflist_mtx);
list_for_each_entry(iter, &local->interfaces, list) {
if (iter == sdata)
continue;
if (iter->vif.type == NL80211_IFTYPE_MONITOR &&
!(iter->u.mntr.flags & MONITOR_FLAG_ACTIVE))
continue;
m = iter->vif.addr;
tmp = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
if ((new & ~mask) != (tmp & ~mask)) {
ret = -EINVAL;
break;
}
}
mutex_unlock(&local->iflist_mtx);
return ret;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Helmut Schaa | 412 | 94.50% | 2 | 40.00% |
Felix Fietkau | 20 | 4.59% | 1 | 20.00% |
Aviya Erenfeld | 3 | 0.69% | 1 | 20.00% |
John W. Linville | 1 | 0.23% | 1 | 20.00% |
Total | 436 | 100.00% | 5 | 100.00% |
static int ieee80211_change_mac(struct net_device *dev, void *addr)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct sockaddr *sa = addr;
bool check_dup = true;
int ret;
if (ieee80211_sdata_running(sdata))
return -EBUSY;
if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))
check_dup = false;
ret = ieee80211_verify_mac(sdata, sa->sa_data, check_dup);
if (ret)
return ret;
ret = eth_mac_addr(dev, sa);
if (ret == 0)
memcpy(sdata->vif.addr, sa->sa_data, ETH_ALEN);
return ret;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 79 | 60.31% | 2 | 40.00% |
Felix Fietkau | 31 | 23.66% | 1 | 20.00% |
Helmut Schaa | 18 | 13.74% | 1 | 20.00% |
Aviya Erenfeld | 3 | 2.29% | 1 | 20.00% |
Total | 131 | 100.00% | 5 | 100.00% |
static inline int identical_mac_addr_allowed(int type1, int type2)
{
return type1 == NL80211_IFTYPE_MONITOR ||
type2 == NL80211_IFTYPE_MONITOR ||
type1 == NL80211_IFTYPE_P2P_DEVICE ||
type2 == NL80211_IFTYPE_P2P_DEVICE ||
(type1 == NL80211_IFTYPE_AP && type2 == NL80211_IFTYPE_WDS) ||
(type1 == NL80211_IFTYPE_WDS &&
(type2 == NL80211_IFTYPE_WDS ||
type2 == NL80211_IFTYPE_AP)) ||
(type1 == NL80211_IFTYPE_AP && type2 == NL80211_IFTYPE_AP_VLAN) ||
(type1 == NL80211_IFTYPE_AP_VLAN &&
(type2 == NL80211_IFTYPE_AP ||
type2 == NL80211_IFTYPE_AP_VLAN));
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 82 | 100.00% | 4 | 100.00% |
Total | 82 | 100.00% | 4 | 100.00% |
static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
enum nl80211_iftype iftype)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_sub_if_data *nsdata;
int ret;
ASSERT_RTNL();
/* we hold the RTNL here so can safely walk the list */
list_for_each_entry(nsdata, &local->interfaces, list) {
if (nsdata != sdata && ieee80211_sdata_running(nsdata)) {
/*
* Only OCB and monitor mode may coexist
*/
if ((sdata->vif.type == NL80211_IFTYPE_OCB &&
nsdata->vif.type != NL80211_IFTYPE_MONITOR) ||
(sdata->vif.type != NL80211_IFTYPE_MONITOR &&
nsdata->vif.type == NL80211_IFTYPE_OCB))
return -EBUSY;
/*
* Allow only a single IBSS interface to be up at any
* time. This is restricted because beacon distribution
* cannot work properly if both are in the same IBSS.
*
* To remove this restriction we'd have to disallow them
* from setting the same SSID on different IBSS interfaces
* belonging to the same hardware. Then, however, we're
* faced with having to adopt two different TSF timers...
*/
if (iftype == NL80211_IFTYPE_ADHOC &&
nsdata->vif.type == NL80211_IFTYPE_ADHOC)
return -EBUSY;
/*
* will not add another interface while any channel
* switch is active.
*/
if (nsdata->vif.csa_active)
return -EBUSY;
/*
* The remaining checks are only performed for interfaces
* with the same MAC address.
*/
if (!ether_addr_equal(sdata->vif.addr,
nsdata->vif.addr))
continue;
/*
* check whether it may have the same address
*/
if (!identical_mac_addr_allowed(iftype,
nsdata->vif.type))
return -ENOTUNIQ;
/*
* can only add VLANs to enabled APs
*/
if (iftype == NL80211_IFTYPE_AP_VLAN &&
nsdata->vif.type == NL80211_IFTYPE_AP)
sdata->bss = &nsdata->u.ap;
}
}
mutex_lock(&local->chanctx_mtx);
ret = ieee80211_check_combinations(sdata, NULL, 0, 0);
mutex_unlock(&local->chanctx_mtx);
return ret;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 142 | 61.47% | 4 | 57.14% |
Rostislav Lisovy | 43 | 18.61% | 1 | 14.29% |
Luciano Coelho | 33 | 14.29% | 1 | 14.29% |
Simon Wunderlich | 13 | 5.63% | 1 | 14.29% |
Total | 231 | 100.00% | 7 | 100.00% |
static int ieee80211_check_queues(struct ieee80211_sub_if_data *sdata,
enum nl80211_iftype iftype)
{
int n_queues = sdata->local->hw.queues;
int i;
if (iftype == NL80211_IFTYPE_NAN)
return 0;
if (iftype != NL80211_IFTYPE_P2P_DEVICE) {
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
if (WARN_ON_ONCE(sdata->vif.hw_queue[i] ==
IEEE80211_INVAL_HW_QUEUE))
return -EINVAL;
if (WARN_ON_ONCE(sdata->vif.hw_queue[i] >=
n_queues))
return -EINVAL;
}
}
if ((iftype != NL80211_IFTYPE_AP &&
iftype != NL80211_IFTYPE_P2P_GO &&
iftype != NL80211_IFTYPE_MESH_POINT) ||
!ieee80211_hw_check(&sdata->local->hw, QUEUE_CONTROL)) {
sdata->vif.cab_queue = IEEE80211_INVAL_HW_QUEUE;
return 0;
}
if (WARN_ON_ONCE(sdata->vif.cab_queue == IEEE80211_INVAL_HW_QUEUE))
return -EINVAL;
if (WARN_ON_ONCE(sdata->vif.cab_queue >= n_queues))
return -EINVAL;
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 157 | 87.22% | 10 | 71.43% |
Ayala Beker | 9 | 5.00% | 1 | 7.14% |
Ilan Peer | 7 | 3.89% | 1 | 7.14% |
John W. Linville | 4 | 2.22% | 1 | 7.14% |
Bob Copeland | 3 | 1.67% | 1 | 7.14% |
Total | 180 | 100.00% | 14 | 100.00% |
void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
const int offset)
{
struct ieee80211_local *local = sdata->local;
u32 flags = sdata->u.mntr.flags;
#define ADJUST(_f, _s) do { \
if (flags & MONITOR_FLAG_##_f) \
local->fif_##_s += offset; \
} while (0)
ADJUST(FCSFAIL, fcsfail);
ADJUST(PLCPFAIL, plcpfail);
ADJUST(CONTROL, control);
ADJUST(CONTROL, pspoll);
ADJUST(OTHER_BSS, other_bss);
#undef ADJUST
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 73 | 90.12% | 5 | 62.50% |
Aviya Erenfeld | 3 | 3.70% | 1 | 12.50% |
David Gnedt | 3 | 3.70% | 1 | 12.50% |
Christian Lamparter | 2 | 2.47% | 1 | 12.50% |
Total | 81 | 100.00% | 8 | 100.00% |
static void ieee80211_set_default_queues(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
int i;
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
if (ieee80211_hw_check(&local->hw, QUEUE_CONTROL))
sdata->vif.hw_queue[i] = IEEE80211_INVAL_HW_QUEUE;
else if (local->hw.queues >= IEEE80211_NUM_ACS)
sdata->vif.hw_queue[i] = i;
else
sdata->vif.hw_queue[i] = 0;
}
sdata->vif.cab_queue = IEEE80211_INVAL_HW_QUEUE;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 94 | 91.26% | 6 | 66.67% |
Eliad Peller | 5 | 4.85% | 2 | 22.22% |
Igor Perminov | 4 | 3.88% | 1 | 11.11% |
Total | 103 | 100.00% | 9 | 100.00% |
int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
{
struct ieee80211_sub_if_data *sdata;
int ret;
if (!ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF))
return 0;
ASSERT_RTNL();
if (local->monitor_sdata)
return 0;
sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL);
if (!sdata)
return -ENOMEM;
/* set up data */
sdata->local = local;
sdata->vif.type = NL80211_IFTYPE_MONITOR;
snprintf(sdata->name, IFNAMSIZ, "%s-monitor",
wiphy_name(local->hw.wiphy));
sdata->wdev.iftype = NL80211_IFTYPE_MONITOR;
sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
ieee80211_set_default_queues(sdata);
ret = drv_add_interface(local, sdata);
if (WARN_ON(ret)) {
/* ok .. stupid driver, it asked for this! */
kfree(sdata);
return ret;
}
ret = ieee80211_check_queues(sdata, NL80211_IFTYPE_MONITOR);
if (ret) {
kfree(sdata);
return ret;
}
mutex_lock(&local->iflist_mtx);
rcu_assign_pointer(local->monitor_sdata, sdata);
mutex_unlock(&local->iflist_mtx);
mutex_lock(&local->mtx);
ret = ieee80211_vif_use_channel(sdata, &local->monitor_chandef,
IEEE80211_CHANCTX_EXCLUSIVE);
mutex_unlock(&local->mtx);
if (ret) {
mutex_lock(&local->iflist_mtx);
RCU_INIT_POINTER(local->monitor_sdata, NULL);
mutex_unlock(&local->iflist_mtx);
synchronize_net();
drv_remove_interface(local, sdata);
kfree(sdata);
return ret;
}
skb_queue_head_init(&sdata->skb_queue);
INIT_WORK(&sdata->work, ieee80211_iface_work);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 267 | 88.12% | 15 | 75.00% |
Aviya Erenfeld | 18 | 5.94% | 1 | 5.00% |
Emmanuel Grumbach | 8 | 2.64% | 1 | 5.00% |
Max Stepanov | 6 | 1.98% | 1 | 5.00% |
Bill Jordan | 3 | 0.99% | 1 | 5.00% |
Monam Agarwal | 1 | 0.33% | 1 | 5.00% |
Total | 303 | 100.00% | 20 | 100.00% |
void ieee80211_del_virtual_monitor(struct ieee80211_local *local)
{
struct ieee80211_sub_if_data *sdata;
if (!ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF))
return;
ASSERT_RTNL();
mutex_lock(&local->iflist_mtx);
sdata = rcu_dereference_protected(local->monitor_sdata,
lockdep_is_held(&local->iflist_mtx));
if (!sdata) {
mutex_unlock(&local->iflist_mtx);
return;
}
RCU_INIT_POINTER(local->monitor_sdata, NULL);
mutex_unlock(&local->iflist_mtx);
synchronize_net();
mutex_lock(&local->mtx);
ieee80211_vif_release_channel(sdata);
mutex_unlock(&local->mtx);
drv_remove_interface(local, sdata);
kfree(sdata);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Johannes Berg | 125 | 99.21% | 9 | 90.00% |
Monam Agarwal | 1 | 0.79% | 1 | 10.00% |
Total | 126 | 100.00% | 10 | 100.00% |
/*
* NOTE: Be very careful when changing this function, it must NOT return
* an error on interface type changes that have been pre-checked, so most
* checks should be in ieee80211_check_concurrent_iface.
*/
int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
struct net_device *dev = wdev->netdev;
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
u32 changed = 0;
int res;
u32 hw_reconf_flags = 0;
switch (sdata->vif.type) {
case NL80211_IFTYPE_WDS:
if (!is_valid_ether_addr(sdata->u.wds.remote_addr))
return -ENOLINK;
break;
case NL80211_IFTYPE_AP_VLAN: {
struct ieee80211_sub_if_data *master;
if (!sdata->bss)
return -ENOLINK;
mutex_lock(&local->mtx);
list_add(&sdata->u.vlan.list, &sdata->bss->vlans);
mutex_unlock(&local->mtx);
master = container_of(sdata->bss,
struct ieee80211_sub_if_data, u.ap);
sdata->control_port_protocol =
master->control_port_protocol;
sdata->control_port_no_encrypt =
master->control_port_no_encrypt;
sdata->vif.cab_queue = master->vif.cab_queue;
memcpy(sdata->vif.hw_queue, master->vif.hw_queue,
sizeof(sdata->vif.hw_queue));
sdata->vif.bss_conf.chandef = master->vif.bss_conf.chandef;
mutex_lock(&local->key_mtx);
sdata->crypto_tx_tailroom_needed_cnt +=
master->crypto_tx_tailroom_needed_cnt;
mutex_unlock(&local->key_mtx);
break;
}
case NL80211_IFTYPE_AP:
sdata->bss = &sdata->u.ap;
break;
case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_P2P_DEVICE:
case NL80211_IFTYPE_OCB:
case NL80211_IFTYPE_NAN:
/* no special treatment */
break;
case NL80211_IFTYPE_UNSPECIFIED:
case NUM_NL80211_IFTYPES:
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_P2P_GO:
/* cannot happen */
WARN_ON(1);
break;
}
if (local->open_count == 0) {
res = drv_start(local);
if (res)
goto err_del_bss;
/* we're brought up, everything changes */
hw_reconf_flags = ~0;
ieee80211_led_radio(local, true);
ieee80211_mod_tpt_led_trig(local,
IEEE80211_TPT_LEDTRIG_FL_RADIO, 0);
}
/*
* Copy the hopefully now-present MAC address to
* this interface, if it has the special null one.
*/
if (dev && is_zero_ether_addr(dev->dev_addr)) {
memcpy(dev->dev_addr,
local->hw.wiphy->perm_addr,
ETH_ALEN);
memcpy(dev->perm_addr, dev->dev_addr, ETH_ALEN);
if (!is_valid_ether_addr(dev->dev_addr)) {
res = -EADDRNOTAVAIL;
goto err_stop;
}
}
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP_VLAN:
/* no need to tell driver, but set carrier and chanctx */
if (rtnl_dereference(sdata->bss->beacon)) {
ieee80211_vif_vlan_copy_chanctx(sdata);
netif_carrier_on(dev);
} else {
netif_carrier_off(dev);
}
break;
case NL80211_IFTYPE_MONITOR:
if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) {
local->cooked_mntrs++;
break;
}
if (sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) {
res = drv_add_interface(local, sdata);
if (res)
goto err_stop;
} else if (local->monitors == 0 && local->open_count == 0) {
res = ieee80211_add_virtual_monitor(local);
if (res)
goto err_stop;
}
/* must be before the call to ieee80211_configure_filter */
local->monitors++;
if (local->monitors == 1) {
local->hw.conf.flags |= IEEE80211_CONF_MONITOR;
hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR;
}
ieee80211_adjust_monitor_flags(sdata, 1);
ieee80211_configure_filter(local);
mutex_lock(&local->mtx);
ieee80211_recalc_idle(local);
mutex_unlock(&local->mtx);
netif_carrier_on(dev);
break;
default:
if (coming_up) {
ieee80211_del_virtual_monitor(local);
res = drv_add_interface(local, sdata);
if (res)
goto err_stop;
res = ieee80211_check_queues(sdata,
ieee80211_vif_type_p2p(&sdata->vif));
if (res)
goto err_del_interface;
}
if (sdata->vif.type == NL80211_IFTYPE_AP) {
local->fif_pspoll++;
local->fif_probe_req++;
ieee80211_configure_filter(local);
} else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
local->fif_probe_req++;
}
if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
sdata->vif.type != NL80211_IFTYPE_NAN)
changed |= ieee80211_reset_erp_info(sdata);
ieee80211_bss_info_change_notify(sdata, changed);
switch (sdata->vif.type) {
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_OCB:
netif_carrier_off(dev);
break;
case NL80211_IFTYPE_WDS:
case NL80211_IFTYPE_P2P_DEVICE:
case NL80211_IFTYPE_NAN:
break;
default:
/* not reached */
WARN_ON(1);
}
/*
* Set default queue parameters so drivers don't
* need to initialise the hardware if the hardware
* doesn't start up with sane defaults.
* Enable QoS for anything but station interfaces.
*/
ieee80211_set_wmm_default(sdata, true,
sdata->vif.type != NL80211_IFTYPE_STATION);
}
set_bit(SDATA_STATE_RUNNING, &sdata->state);
switch (sdata->vif.type) {
case NL80211_IFTYPE_WDS:
/* Create STA entry for the WDS peer */
sta = sta_info_alloc(