summaryrefslogtreecommitdiffstats
path: root/net/mac80211/iface.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/iface.c')
-rw-r--r--net/mac80211/iface.c325
1 files changed, 153 insertions, 172 deletions
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 8664111d0566..bfb57dcc1538 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -43,6 +43,128 @@
*/
+static u32 ieee80211_idle_off(struct ieee80211_local *local,
+ const char *reason)
+{
+ if (!(local->hw.conf.flags & IEEE80211_CONF_IDLE))
+ return 0;
+
+ local->hw.conf.flags &= ~IEEE80211_CONF_IDLE;
+ return IEEE80211_CONF_CHANGE_IDLE;
+}
+
+static u32 ieee80211_idle_on(struct ieee80211_local *local)
+{
+ if (local->hw.conf.flags & IEEE80211_CONF_IDLE)
+ return 0;
+
+ drv_flush(local, false);
+
+ local->hw.conf.flags |= IEEE80211_CONF_IDLE;
+ return IEEE80211_CONF_CHANGE_IDLE;
+}
+
+static u32 __ieee80211_recalc_idle(struct ieee80211_local *local)
+{
+ struct ieee80211_sub_if_data *sdata;
+ int count = 0;
+ bool working = false, scanning = false;
+ unsigned int led_trig_start = 0, led_trig_stop = 0;
+ struct ieee80211_roc_work *roc;
+
+#ifdef CONFIG_PROVE_LOCKING
+ WARN_ON(debug_locks && !lockdep_rtnl_is_held() &&
+ !lockdep_is_held(&local->iflist_mtx));
+#endif
+ lockdep_assert_held(&local->mtx);
+
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (!ieee80211_sdata_running(sdata)) {
+ sdata->vif.bss_conf.idle = true;
+ continue;
+ }
+
+ sdata->old_idle = sdata->vif.bss_conf.idle;
+
+ /* do not count disabled managed interfaces */
+ if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+ !sdata->u.mgd.associated &&
+ !sdata->u.mgd.auth_data &&
+ !sdata->u.mgd.assoc_data) {
+ sdata->vif.bss_conf.idle = true;
+ continue;
+ }
+ /* do not count unused IBSS interfaces */
+ if (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
+ !sdata->u.ibss.ssid_len) {
+ sdata->vif.bss_conf.idle = true;
+ continue;
+ }
+ /* count everything else */
+ sdata->vif.bss_conf.idle = false;
+ count++;
+ }
+
+ if (!local->ops->remain_on_channel) {
+ list_for_each_entry(roc, &local->roc_list, list) {
+ working = true;
+ roc->sdata->vif.bss_conf.idle = false;
+ }
+ }
+
+ sdata = rcu_dereference_protected(local->scan_sdata,
+ lockdep_is_held(&local->mtx));
+ if (sdata && !(local->hw.flags & IEEE80211_HW_SCAN_WHILE_IDLE)) {
+ scanning = true;
+ sdata->vif.bss_conf.idle = false;
+ }
+
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (sdata->vif.type == NL80211_IFTYPE_MONITOR ||
+ sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ continue;
+ if (sdata->old_idle == sdata->vif.bss_conf.idle)
+ continue;
+ if (!ieee80211_sdata_running(sdata))
+ continue;
+ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
+ }
+
+ if (working || scanning)
+ led_trig_start |= IEEE80211_TPT_LEDTRIG_FL_WORK;
+ else
+ led_trig_stop |= IEEE80211_TPT_LEDTRIG_FL_WORK;
+
+ if (count)
+ 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)
+ return ieee80211_idle_off(local, "working");
+ if (scanning)
+ return ieee80211_idle_off(local, "scanning");
+ if (!count)
+ return ieee80211_idle_on(local);
+ else
+ return ieee80211_idle_off(local, "in use");
+
+ return 0;
+}
+
+void ieee80211_recalc_idle(struct ieee80211_local *local)
+{
+ u32 chg;
+
+ mutex_lock(&local->iflist_mtx);
+ chg = __ieee80211_recalc_idle(local);
+ mutex_unlock(&local->iflist_mtx);
+ if (chg)
+ ieee80211_hw_config(local, chg);
+}
+
static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
{
int meshhdrlen;
@@ -57,9 +179,6 @@ static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
return -EINVAL;
}
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: setting MTU %d\n", dev->name, new_mtu);
-#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
dev->mtu = new_mtu;
return 0;
}
@@ -100,15 +219,12 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_sub_if_data *nsdata;
- struct net_device *dev = sdata->dev;
ASSERT_RTNL();
/* we hold the RTNL here so can safely walk the list */
list_for_each_entry(nsdata, &local->interfaces, list) {
- struct net_device *ndev = nsdata->dev;
-
- if (ndev != dev && ieee80211_sdata_running(nsdata)) {
+ if (nsdata != sdata && ieee80211_sdata_running(nsdata)) {
/*
* Allow only a single IBSS interface to be up at any
* time. This is restricted because beacon distribution
@@ -127,7 +243,8 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
* The remaining checks are only performed for interfaces
* with the same MAC address.
*/
- if (!ether_addr_equal(dev->dev_addr, ndev->dev_addr))
+ if (!ether_addr_equal(sdata->vif.addr,
+ nsdata->vif.addr))
continue;
/*
@@ -217,17 +334,21 @@ static void ieee80211_set_default_queues(struct ieee80211_sub_if_data *sdata)
static int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
{
struct ieee80211_sub_if_data *sdata;
- int ret;
+ int ret = 0;
if (!(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF))
return 0;
+ mutex_lock(&local->iflist_mtx);
+
if (local->monitor_sdata)
- return 0;
+ goto out_unlock;
sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL);
- if (!sdata)
- return -ENOMEM;
+ if (!sdata) {
+ ret = -ENOMEM;
+ goto out_unlock;
+ }
/* set up data */
sdata->local = local;
@@ -241,18 +362,19 @@ static int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
if (WARN_ON(ret)) {
/* ok .. stupid driver, it asked for this! */
kfree(sdata);
- return ret;
+ goto out_unlock;
}
ret = ieee80211_check_queues(sdata);
if (ret) {
kfree(sdata);
- return ret;
+ goto out_unlock;
}
rcu_assign_pointer(local->monitor_sdata, sdata);
-
- return 0;
+ out_unlock:
+ mutex_unlock(&local->iflist_mtx);
+ return ret;
}
static void ieee80211_del_virtual_monitor(struct ieee80211_local *local)
@@ -262,10 +384,12 @@ static void ieee80211_del_virtual_monitor(struct ieee80211_local *local)
if (!(local->hw.flags & IEEE80211_HW_WANT_MONITOR_VIF))
return;
- sdata = rtnl_dereference(local->monitor_sdata);
+ mutex_lock(&local->iflist_mtx);
+ sdata = rcu_dereference_protected(local->monitor_sdata,
+ lockdep_is_held(&local->iflist_mtx));
if (!sdata)
- return;
+ goto out_unlock;
rcu_assign_pointer(local->monitor_sdata, NULL);
synchronize_net();
@@ -273,6 +397,8 @@ static void ieee80211_del_virtual_monitor(struct ieee80211_local *local)
drv_remove_interface(local, sdata);
kfree(sdata);
+ out_unlock:
+ mutex_unlock(&local->iflist_mtx);
}
/*
@@ -520,7 +646,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
clear_bit(SDATA_STATE_RUNNING, &sdata->state);
- if (local->scan_sdata == sdata)
+ if (rcu_access_pointer(local->scan_sdata) == sdata)
ieee80211_scan_cancel(local);
/*
@@ -528,10 +654,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
*/
netif_tx_stop_all_queues(sdata->dev);
- /*
- * Purge work for this interface.
- */
- ieee80211_work_purge(sdata);
+ ieee80211_roc_purge(sdata);
/*
* Remove all stations associated with this interface.
@@ -637,18 +760,6 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
ieee80211_configure_filter(local);
break;
default:
- mutex_lock(&local->mtx);
- if (local->hw_roc_dev == sdata->dev &&
- local->hw_roc_channel) {
- /* ignore return value since this is racy */
- drv_cancel_remain_on_channel(local);
- ieee80211_queue_work(&local->hw, &local->hw_roc_done);
- }
- mutex_unlock(&local->mtx);
-
- flush_work(&local->hw_roc_start);
- flush_work(&local->hw_roc_done);
-
flush_work(&sdata->work);
/*
* When we get here, the interface is marked down.
@@ -823,7 +934,7 @@ static u16 ieee80211_monitor_select_queue(struct net_device *dev,
hdr = (void *)((u8 *)skb->data + le16_to_cpu(rtap->it_len));
- return ieee80211_select_queue_80211(local, skb, hdr);
+ return ieee80211_select_queue_80211(sdata, skb, hdr);
}
static const struct net_device_ops ieee80211_monitorif_ops = {
@@ -1238,7 +1349,7 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local,
if (__ffs64(mask) + hweight64(mask) != fls64(mask)) {
/* not a contiguous mask ... not handled now! */
- printk(KERN_DEBUG "not contiguous\n");
+ pr_info("not contiguous\n");
break;
}
@@ -1284,7 +1395,7 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local,
}
int ieee80211_if_add(struct ieee80211_local *local, const char *name,
- struct net_device **new_dev, enum nl80211_iftype type,
+ struct wireless_dev **new_wdev, enum nl80211_iftype type,
struct vif_params *params)
{
struct net_device *ndev;
@@ -1364,6 +1475,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
sdata->u.mgd.use_4addr = params->use_4addr;
}
+ ndev->features |= local->hw.netdev_features;
+
ret = register_netdevice(ndev);
if (ret)
goto fail;
@@ -1372,8 +1485,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
list_add_tail_rcu(&sdata->list, &local->interfaces);
mutex_unlock(&local->iflist_mtx);
- if (new_dev)
- *new_dev = ndev;
+ if (new_wdev)
+ *new_wdev = &sdata->wdev;
return 0;
@@ -1421,138 +1534,6 @@ void ieee80211_remove_interfaces(struct ieee80211_local *local)
list_del(&unreg_list);
}
-static u32 ieee80211_idle_off(struct ieee80211_local *local,
- const char *reason)
-{
- if (!(local->hw.conf.flags & IEEE80211_CONF_IDLE))
- return 0;
-
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- wiphy_debug(local->hw.wiphy, "device no longer idle - %s\n", reason);
-#endif
-
- local->hw.conf.flags &= ~IEEE80211_CONF_IDLE;
- return IEEE80211_CONF_CHANGE_IDLE;
-}
-
-static u32 ieee80211_idle_on(struct ieee80211_local *local)
-{
- if (local->hw.conf.flags & IEEE80211_CONF_IDLE)
- return 0;
-
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- wiphy_debug(local->hw.wiphy, "device now idle\n");
-#endif
-
- drv_flush(local, false);
-
- local->hw.conf.flags |= IEEE80211_CONF_IDLE;
- return IEEE80211_CONF_CHANGE_IDLE;
-}
-
-u32 __ieee80211_recalc_idle(struct ieee80211_local *local)
-{
- struct ieee80211_sub_if_data *sdata;
- int count = 0;
- bool working = false, scanning = false, hw_roc = false;
- struct ieee80211_work *wk;
- unsigned int led_trig_start = 0, led_trig_stop = 0;
-
-#ifdef CONFIG_PROVE_LOCKING
- WARN_ON(debug_locks && !lockdep_rtnl_is_held() &&
- !lockdep_is_held(&local->iflist_mtx));
-#endif
- lockdep_assert_held(&local->mtx);
-
- list_for_each_entry(sdata, &local->interfaces, list) {
- if (!ieee80211_sdata_running(sdata)) {
- sdata->vif.bss_conf.idle = true;
- continue;
- }
-
- sdata->old_idle = sdata->vif.bss_conf.idle;
-
- /* do not count disabled managed interfaces */
- if (sdata->vif.type == NL80211_IFTYPE_STATION &&
- !sdata->u.mgd.associated &&
- !sdata->u.mgd.auth_data &&
- !sdata->u.mgd.assoc_data) {
- sdata->vif.bss_conf.idle = true;
- continue;
- }
- /* do not count unused IBSS interfaces */
- if (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
- !sdata->u.ibss.ssid_len) {
- sdata->vif.bss_conf.idle = true;
- continue;
- }
- /* count everything else */
- sdata->vif.bss_conf.idle = false;
- count++;
- }
-
- list_for_each_entry(wk, &local->work_list, list) {
- working = true;
- wk->sdata->vif.bss_conf.idle = false;
- }
-
- if (local->scan_sdata &&
- !(local->hw.flags & IEEE80211_HW_SCAN_WHILE_IDLE)) {
- scanning = true;
- local->scan_sdata->vif.bss_conf.idle = false;
- }
-
- if (local->hw_roc_channel)
- hw_roc = true;
-
- list_for_each_entry(sdata, &local->interfaces, list) {
- if (sdata->vif.type == NL80211_IFTYPE_MONITOR ||
- sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
- continue;
- if (sdata->old_idle == sdata->vif.bss_conf.idle)
- continue;
- if (!ieee80211_sdata_running(sdata))
- continue;
- ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
- }
-
- if (working || scanning || hw_roc)
- led_trig_start |= IEEE80211_TPT_LEDTRIG_FL_WORK;
- else
- led_trig_stop |= IEEE80211_TPT_LEDTRIG_FL_WORK;
-
- if (count)
- 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 (hw_roc)
- return ieee80211_idle_off(local, "hw remain-on-channel");
- if (working)
- return ieee80211_idle_off(local, "working");
- if (scanning)
- return ieee80211_idle_off(local, "scanning");
- if (!count)
- return ieee80211_idle_on(local);
- else
- return ieee80211_idle_off(local, "in use");
-
- return 0;
-}
-
-void ieee80211_recalc_idle(struct ieee80211_local *local)
-{
- u32 chg;
-
- mutex_lock(&local->iflist_mtx);
- chg = __ieee80211_recalc_idle(local);
- mutex_unlock(&local->iflist_mtx);
- if (chg)
- ieee80211_hw_config(local, chg);
-}
-
static int netdev_notify(struct notifier_block *nb,
unsigned long state,
void *ndev)