summaryrefslogtreecommitdiff
path: root/net/mac80211
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/Kconfig9
-rw-r--r--net/mac80211/agg-tx.c4
-rw-r--r--net/mac80211/cfg.c6
-rw-r--r--net/mac80211/driver-trace.h2
-rw-r--r--net/mac80211/ibss.c6
-rw-r--r--net/mac80211/ieee80211_i.h2
-rw-r--r--net/mac80211/iface.c40
-rw-r--r--net/mac80211/main.c15
-rw-r--r--net/mac80211/mesh.h5
-rw-r--r--net/mac80211/mesh_hwmp.c2
-rw-r--r--net/mac80211/mlme.c49
-rw-r--r--net/mac80211/rx.c25
-rw-r--r--net/mac80211/scan.c71
-rw-r--r--net/mac80211/tx.c17
-rw-r--r--net/mac80211/util.c36
-rw-r--r--net/mac80211/wme.c89
-rw-r--r--net/mac80211/wme.h8
17 files changed, 302 insertions, 84 deletions
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index 4d5543af3123..ea367cf9a052 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -15,8 +15,12 @@ comment "CFG80211 needs to be enabled for MAC80211"
if MAC80211 != n
+config MAC80211_HAS_RC
+ def_bool n
+
config MAC80211_RC_PID
bool "PID controller based rate control algorithm" if EMBEDDED
+ select MAC80211_HAS_RC
---help---
This option enables a TX rate control algorithm for
mac80211 that uses a PID controller to select the TX
@@ -24,12 +28,14 @@ config MAC80211_RC_PID
config MAC80211_RC_MINSTREL
bool "Minstrel" if EMBEDDED
+ select MAC80211_HAS_RC
default y
---help---
This option enables the 'minstrel' TX rate control algorithm
choice
prompt "Default rate control algorithm"
+ depends on MAC80211_HAS_RC
default MAC80211_RC_DEFAULT_MINSTREL
---help---
This option selects the default rate control algorithm
@@ -62,6 +68,9 @@ config MAC80211_RC_DEFAULT
endif
+comment "Some wireless drivers require a rate control algorithm"
+ depends on MAC80211_HAS_RC=n
+
config MAC80211_MESH
bool "Enable mac80211 mesh networking (pre-802.11s) support"
depends on MAC80211 && EXPERIMENTAL
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index 89e238b001de..2e08921b7cf6 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -177,10 +177,10 @@ static void sta_addba_resp_timer_expired(unsigned long data)
/* check if the TID waits for addBA response */
spin_lock_bh(&sta->lock);
- if ((*state & (HT_ADDBA_REQUESTED_MSK | HT_ADDBA_RECEIVED_MSK)) !=
+ if ((*state & (HT_ADDBA_REQUESTED_MSK | HT_ADDBA_RECEIVED_MSK |
+ HT_AGG_STATE_REQ_STOP_BA_MSK)) !=
HT_ADDBA_REQUESTED_MSK) {
spin_unlock_bh(&sta->lock);
- *state = HT_AGG_STATE_IDLE;
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "timer expired on tid %d but we are not "
"(or no longer) expecting addBA response there",
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 7b5131bd6fa1..fe2d3f8c5ae7 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -338,7 +338,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
sinfo->rx_packets = sta->rx_packets;
sinfo->tx_packets = sta->tx_packets;
- if (sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) {
+ if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) ||
+ (sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) {
sinfo->filled |= STATION_INFO_SIGNAL;
sinfo->signal = (s8)sta->last_signal;
}
@@ -1305,6 +1306,9 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_conf *conf = &local->hw.conf;
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ return -EOPNOTSUPP;
+
if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS))
return -EOPNOTSUPP;
diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h
index 37b9051afcf3..d87645e3e41a 100644
--- a/net/mac80211/driver-trace.h
+++ b/net/mac80211/driver-trace.h
@@ -655,7 +655,7 @@ TRACE_EVENT(drv_ampdu_action,
__entry->ret = ret;
__entry->action = action;
__entry->tid = tid;
- __entry->ssn = *ssn;
+ __entry->ssn = ssn ? *ssn : 0;
),
TP_printk(
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index f1362f32c17d..07600a6871a8 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -455,6 +455,10 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
ieee80211_sta_expire(sdata, IEEE80211_IBSS_INACTIVITY_LIMIT);
+ if (time_before(jiffies, ifibss->last_scan_completed +
+ IEEE80211_IBSS_MERGE_INTERVAL))
+ return;
+
if (ieee80211_sta_active_ibss(sdata))
return;
@@ -639,7 +643,7 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
}
if (pos[1] != 0 &&
(pos[1] != ifibss->ssid_len ||
- !memcmp(pos + 2, ifibss->ssid, ifibss->ssid_len))) {
+ memcmp(pos + 2, ifibss->ssid, ifibss->ssid_len))) {
/* Ignore ProbeReq for foreign SSID */
return;
}
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 10d316e455de..ca62bfe229dd 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -264,6 +264,7 @@ enum ieee80211_sta_flags {
IEEE80211_STA_DISABLE_11N = BIT(4),
IEEE80211_STA_CSA_RECEIVED = BIT(5),
IEEE80211_STA_MFP_ENABLED = BIT(6),
+ IEEE80211_STA_NULLFUNC_ACKED = BIT(7),
};
/* flags for MLME request */
@@ -808,6 +809,7 @@ struct ieee80211_local {
unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */
bool pspolling;
+ bool scan_ps_enabled;
/*
* PS can only be enabled when we have exactly one managed
* interface (and monitors) in PS, this then points there.
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index b8295cbd7e8f..079c500a7de5 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -15,12 +15,14 @@
#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"
/**
* DOC: Interface list locking
@@ -642,6 +644,12 @@ static void ieee80211_teardown_sdata(struct net_device *dev)
WARN_ON(flushed);
}
+static u16 ieee80211_netdev_select_queue(struct net_device *dev,
+ struct sk_buff *skb)
+{
+ return ieee80211_select_queue(IEEE80211_DEV_TO_SUB_IF(dev), skb);
+}
+
static const struct net_device_ops ieee80211_dataif_ops = {
.ndo_open = ieee80211_open,
.ndo_stop = ieee80211_stop,
@@ -650,8 +658,35 @@ static const struct net_device_ops ieee80211_dataif_ops = {
.ndo_set_multicast_list = ieee80211_set_multicast_list,
.ndo_change_mtu = ieee80211_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
+ .ndo_select_queue = ieee80211_netdev_select_queue,
};
+static u16 ieee80211_monitor_select_queue(struct net_device *dev,
+ struct sk_buff *skb)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_hdr *hdr;
+ struct ieee80211_radiotap_header *rtap = (void *)skb->data;
+
+ if (local->hw.queues < 4)
+ return 0;
+
+ if (skb->len < 4 ||
+ skb->len < le16_to_cpu(rtap->it_len) + 2 /* frame control */)
+ return 0; /* doesn't matter, frame will be dropped */
+
+ hdr = (void *)((u8 *)skb->data + le16_to_cpu(rtap->it_len));
+
+ if (!ieee80211_is_data(hdr->frame_control)) {
+ skb->priority = 7;
+ return ieee802_1d_to_ac[skb->priority];
+ }
+
+ skb->priority = 0;
+ return ieee80211_downgrade_queue(local, skb);
+}
+
static const struct net_device_ops ieee80211_monitorif_ops = {
.ndo_open = ieee80211_open,
.ndo_stop = ieee80211_stop,
@@ -660,6 +695,7 @@ static const struct net_device_ops ieee80211_monitorif_ops = {
.ndo_set_multicast_list = ieee80211_set_multicast_list,
.ndo_change_mtu = ieee80211_change_mtu,
.ndo_set_mac_address = eth_mac_addr,
+ .ndo_select_queue = ieee80211_monitor_select_queue,
};
static void ieee80211_if_setup(struct net_device *dev)
@@ -768,8 +804,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
ASSERT_RTNL();
- ndev = alloc_netdev(sizeof(*sdata) + local->hw.vif_data_size,
- name, ieee80211_if_setup);
+ ndev = alloc_netdev_mq(sizeof(*sdata) + local->hw.vif_data_size,
+ name, ieee80211_if_setup, local->hw.queues);
if (!ndev)
return -ENOMEM;
dev_net_set(ndev, wiphy_net(local->hw.wiphy));
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 797f53942e5f..19fbd25a705b 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -441,6 +441,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
rcu_read_lock();
sband = local->hw.wiphy->bands[info->band];
+ fc = hdr->frame_control;
sta = sta_info_get(local, hdr->addr1);
@@ -522,6 +523,20 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
local->dot11FailedCount++;
}
+ if (ieee80211_is_nullfunc(fc) && ieee80211_has_pm(fc) &&
+ (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) &&
+ !(info->flags & IEEE80211_TX_CTL_INJECTED) &&
+ local->ps_sdata && !(local->scanning)) {
+ if (info->flags & IEEE80211_TX_STAT_ACK) {
+ local->ps_sdata->u.mgd.flags |=
+ IEEE80211_STA_NULLFUNC_ACKED;
+ ieee80211_queue_work(&local->hw,
+ &local->dynamic_ps_enable_work);
+ } else
+ mod_timer(&local->dynamic_ps_timer, jiffies +
+ msecs_to_jiffies(10));
+ }
+
/* this was a transmitted frame, but now we want to reuse it */
skb_orphan(skb);
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index dd1c19319f0a..010ff2fc47f8 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -186,8 +186,9 @@ struct mesh_rmc {
*/
#define MESH_PREQ_MIN_INT 10
#define MESH_DIAM_TRAVERSAL_TIME 50
-/* Paths will be refreshed if they are closer than PATH_REFRESH_TIME to their
- * expiration
+/* A path will be refreshed if it is used PATH_REFRESH_TIME milliseconds before
+ * timing out. This way it will remain ACTIVE and no data frames will be
+ * unnecesarily held in the pending queue.
*/
#define MESH_PATH_REFRESH_TIME 1000
#define MESH_MIN_DISCOVERY_TIMEOUT (2 * MESH_DIAM_TRAVERSAL_TIME)
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 29b82e98effa..93c49fc8913d 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -813,7 +813,7 @@ int mesh_nexthop_lookup(struct sk_buff *skb,
}
if (mpath->flags & MESH_PATH_ACTIVE) {
- if (time_after(jiffies, mpath->exp_time +
+ if (time_after(jiffies, mpath->exp_time -
msecs_to_jiffies(sdata->u.mesh.mshcfg.path_refresh_time))
&& !memcmp(sdata->dev->dev_addr, hdr->addr4,
ETH_ALEN)
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index dc5049d58c51..5bea319e3e54 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -269,12 +269,6 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
if (wk->bss->wmm_used)
wmm = 1;
- /* get all rates supported by the device and the AP as
- * some APs don't like getting a superset of their rates
- * in the association request (e.g. D-Link DAP 1353 in
- * b-only mode) */
- rates_len = ieee80211_compatible_rates(wk->bss, sband, &rates);
-
if ((wk->bss->cbss.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) &&
(local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT))
capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
@@ -309,6 +303,17 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
*pos++ = wk->ssid_len;
memcpy(pos, wk->ssid, wk->ssid_len);
+ if (wk->bss->supp_rates_len) {
+ /* get all rates supported by the device and the AP as
+ * some APs don't like getting a superset of their rates
+ * in the association request (e.g. D-Link DAP 1353 in
+ * b-only mode) */
+ rates_len = ieee80211_compatible_rates(wk->bss, sband, &rates);
+ } else {
+ rates = ~0;
+ rates_len = sband->n_bitrates;
+ }
+
/* add all rates which were marked to be used above */
supp_rates_len = rates_len;
if (supp_rates_len > 8)
@@ -650,8 +655,11 @@ static void ieee80211_enable_ps(struct ieee80211_local *local,
} else {
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
ieee80211_send_nullfunc(local, sdata, 1);
- conf->flags |= IEEE80211_CONF_PS;
- ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+
+ if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
+ conf->flags |= IEEE80211_CONF_PS;
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+ }
}
}
@@ -742,6 +750,7 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
container_of(work, struct ieee80211_local,
dynamic_ps_enable_work);
struct ieee80211_sub_if_data *sdata = local->ps_sdata;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
/* can only happen when PS was just disabled anyway */
if (!sdata)
@@ -750,11 +759,16 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
if (local->hw.conf.flags & IEEE80211_CONF_PS)
return;
- if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
+ if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&
+ (!(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)))
ieee80211_send_nullfunc(local, sdata, 1);
- local->hw.conf.flags |= IEEE80211_CONF_PS;
- ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+ if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) ||
+ (ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) {
+ ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
+ local->hw.conf.flags |= IEEE80211_CONF_PS;
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+ }
}
void ieee80211_dynamic_ps_timer(unsigned long data)
@@ -904,6 +918,14 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL |
IEEE80211_STA_BEACON_POLL);
+ /*
+ * Always handle WMM once after association regardless
+ * of the first value the AP uses. Setting -1 here has
+ * that effect because the AP values is an unsigned
+ * 4-bit value.
+ */
+ sdata->u.mgd.wmm_last_param_set = -1;
+
ieee80211_led_assoc(local, 1);
sdata->vif.bss_conf.assoc = 1;
@@ -1945,7 +1967,9 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
rma = ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len);
break;
case IEEE80211_STYPE_ACTION:
- /* XXX: differentiate, can only happen for CSA now! */
+ if (mgmt->u.action.category != WLAN_CATEGORY_SPECTRUM_MGMT)
+ break;
+
ieee80211_sta_process_chanswitch(sdata,
&mgmt->u.action.u.chan_switch.sw_elem,
ifmgd->associated);
@@ -2448,6 +2472,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
list_add(&wk->list, &ifmgd->work_list);
ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N;
+ ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
for (i = 0; i < req->crypto.n_ciphers_pairwise; i++)
if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 ||
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 7170bf4565a8..38499c4e7106 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1220,7 +1220,8 @@ ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
(rx->key || rx->sdata->drop_unencrypted)))
return -EACCES;
if (rx->sta && test_sta_flags(rx->sta, WLAN_STA_MFP)) {
- if (unlikely(ieee80211_is_unicast_robust_mgmt_frame(rx->skb) &&
+ if (unlikely(!ieee80211_has_protected(fc) &&
+ ieee80211_is_unicast_robust_mgmt_frame(rx->skb) &&
rx->key))
return -EACCES;
/* BIP does not use Protected field, so need to check MMIE */
@@ -1514,7 +1515,6 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
mpp_path_add(mesh_hdr->eaddr2, hdr->addr4, sdata);
} else {
spin_lock_bh(&mppath->state_lock);
- mppath->exp_time = jiffies;
if (compare_ether_addr(mppath->mpp, hdr->addr4) != 0)
memcpy(mppath->mpp, hdr->addr4, ETH_ALEN);
spin_unlock_bh(&mppath->state_lock);
@@ -1549,7 +1549,9 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
memset(info, 0, sizeof(*info));
info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
info->control.vif = &rx->sdata->vif;
- ieee80211_select_queue(local, fwd_skb);
+ skb_set_queue_mapping(skb,
+ ieee80211_select_queue(rx->sdata, fwd_skb));
+ ieee80211_set_qos_hdr(local, skb);
if (is_multicast_ether_addr(fwd_hdr->addr1))
IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh,
fwded_mcast);
@@ -1589,6 +1591,7 @@ static ieee80211_rx_result debug_noinline
ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
{
struct net_device *dev = rx->dev;
+ struct ieee80211_local *local = rx->local;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
__le16 fc = hdr->frame_control;
int err;
@@ -1611,6 +1614,13 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
dev->stats.rx_packets++;
dev->stats.rx_bytes += rx->skb->len;
+ if (ieee80211_is_data(hdr->frame_control) &&
+ !is_multicast_ether_addr(hdr->addr1) &&
+ local->hw.conf.dynamic_ps_timeout > 0 && local->ps_sdata) {
+ mod_timer(&local->dynamic_ps_timer, jiffies +
+ msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
+ }
+
ieee80211_deliver_skb(rx);
return RX_QUEUED;
@@ -1808,7 +1818,16 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
return RX_CONTINUE;
}
break;
+ case WLAN_CATEGORY_MESH_PLINK:
+ case WLAN_CATEGORY_MESH_PATH_SEL:
+ if (ieee80211_vif_is_mesh(&sdata->vif))
+ return ieee80211_mesh_rx_mgmt(sdata, rx->skb);
+ break;
default:
+ /* do not process rejected action frames */
+ if (mgmt->u.action.category & 0x80)
+ return RX_DROP_MONITOR;
+
return RX_CONTINUE;
}
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 71e10cabf811..169111ad6c99 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -62,7 +62,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
bool beacon)
{
struct ieee80211_bss *bss;
- int clen;
+ int clen, srlen;
s32 signal = 0;
if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
@@ -94,23 +94,24 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
if (bss->dtim_period == 0)
bss->dtim_period = 1;
- bss->supp_rates_len = 0;
+ /* replace old supported rates if we get new values */
+ srlen = 0;
if (elems->supp_rates) {
- clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
+ clen = IEEE80211_MAX_SUPP_RATES;
if (clen > elems->supp_rates_len)
clen = elems->supp_rates_len;
- memcpy(&bss->supp_rates[bss->supp_rates_len], elems->supp_rates,
- clen);
- bss->supp_rates_len += clen;
+ memcpy(bss->supp_rates, elems->supp_rates, clen);
+ srlen += clen;
}
if (elems->ext_supp_rates) {
- clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
+ clen = IEEE80211_MAX_SUPP_RATES - srlen;
if (clen > elems->ext_supp_rates_len)
clen = elems->ext_supp_rates_len;
- memcpy(&bss->supp_rates[bss->supp_rates_len],
- elems->ext_supp_rates, clen);
- bss->supp_rates_len += clen;
+ memcpy(bss->supp_rates + srlen, elems->ext_supp_rates, clen);
+ srlen += clen;
}
+ if (srlen)
+ bss->supp_rates_len = srlen;
bss->wmm_used = elems->wmm_param || elems->wmm_info;
@@ -196,7 +197,8 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
static void ieee80211_scan_ps_enable(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
- bool ps = false;
+
+ local->scan_ps_enabled = false;
/* FIXME: what to do when local->pspolling is true? */
@@ -204,12 +206,13 @@ static void ieee80211_scan_ps_enable(struct ieee80211_sub_if_data *sdata)
cancel_work_sync(&local->dynamic_ps_enable_work);
if (local->hw.conf.flags & IEEE80211_CONF_PS) {
- ps = true;
+ local->scan_ps_enabled = true;
local->hw.conf.flags &= ~IEEE80211_CONF_PS;
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
}
- if (!ps || !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK))
+ if (!(local->scan_ps_enabled) ||
+ !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK))
/*
* If power save was enabled, no need to send a nullfunc
* frame because AP knows that we are sleeping. But if the
@@ -230,7 +233,7 @@ static void ieee80211_scan_ps_disable(struct ieee80211_sub_if_data *sdata)
if (!local->ps_sdata)
ieee80211_send_nullfunc(local, sdata, 0);
- else {
+ else if (local->scan_ps_enabled) {
/*
* In !IEEE80211_HW_PS_NULLFUNC_STACK case the hardware
* will send a nullfunc frame with the powersave bit set
@@ -246,6 +249,16 @@ static void ieee80211_scan_ps_disable(struct ieee80211_sub_if_data *sdata)
*/
local->hw.conf.flags |= IEEE80211_CONF_PS;
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+ } else if (local->hw.conf.dynamic_ps_timeout > 0) {
+ /*
+ * If IEEE80211_CONF_PS was not set and the dynamic_ps_timer
+ * had been running before leaving the operating channel,
+ * restart the timer now and send a nullfunc frame to inform
+ * the AP that we are awake.
+ */
+ ieee80211_send_nullfunc(local, sdata, 0);
+ mod_timer(&local->dynamic_ps_timer, jiffies +
+ msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
}
}
@@ -264,10 +277,14 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
mutex_lock(&local->scan_mtx);
- if (WARN_ON(!local->scanning)) {
- mutex_unlock(&local->scan_mtx);
- return;
- }
+ /*
+ * It's ok to abort a not-yet-running scan (that
+ * we have one at all will be verified by checking
+ * local->scan_req next), but not to complete it
+ * successfully.
+ */
+ if (WARN_ON(!local->scanning && !aborted))
+ aborted = true;
if (WARN_ON(!local->scan_req)) {
mutex_unlock(&local->scan_mtx);
@@ -392,6 +409,16 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
if (local->scan_req)
return -EBUSY;
+ if (req != local->int_scan_req &&
+ sdata->vif.type == NL80211_IFTYPE_STATION &&
+ !list_empty(&ifmgd->work_list)) {
+ /* actually wait for the work it's doing to finish/time out */
+ set_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request);
+ local->scan_req = req;
+ local->scan_sdata = sdata;
+ return 0;
+ }
+
if (local->ops->hw_scan) {
u8 *ies;
int ielen;
@@ -412,14 +439,6 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
local->scan_req = req;
local->scan_sdata = sdata;
- if (req != local->int_scan_req &&
- sdata->vif.type == NL80211_IFTYPE_STATION &&
- !list_empty(&ifmgd->work_list)) {
- /* actually wait for the work it's doing to finish/time out */
- set_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request);
- return 0;
- }
-
if (local->ops->hw_scan)
__set_bit(SCAN_HW_SCANNING, &local->scanning);
else
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index eaa4118de988..b1d79046257b 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -496,7 +496,8 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
struct ieee80211_hdr *hdr = (void *)tx->skb->data;
struct ieee80211_supported_band *sband;
struct ieee80211_rate *rate;
- int i, len;
+ int i;
+ u32 len;
bool inval = false, rts = false, short_preamble = false;
struct ieee80211_tx_rate_control txrc;
u32 sta_flags;
@@ -505,7 +506,7 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
sband = tx->local->hw.wiphy->bands[tx->channel->band];
- len = min_t(int, tx->skb->len + FCS_LEN,
+ len = min_t(u32, tx->skb->len + FCS_LEN,
tx->local->hw.wiphy->frag_threshold);
/* set up the tx rate control struct we give the RC algo */
@@ -1401,6 +1402,7 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&
local->hw.conf.dynamic_ps_timeout > 0 &&
+ !local->quiescing &&
!(local->scanning) && local->ps_sdata) {
if (local->hw.conf.flags & IEEE80211_CONF_PS) {
ieee80211_stop_queues_by_reason(&local->hw,
@@ -1481,7 +1483,7 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
return;
}
- ieee80211_select_queue(local, skb);
+ ieee80211_set_qos_hdr(local, skb);
ieee80211_tx(sdata, skb, false);
dev_put(sdata->dev);
}
@@ -1880,6 +1882,7 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,
void ieee80211_tx_pending(unsigned long data)
{
struct ieee80211_local *local = (struct ieee80211_local *)data;
+ struct ieee80211_sub_if_data *sdata;
unsigned long flags;
int i;
bool txok;
@@ -1920,6 +1923,11 @@ void ieee80211_tx_pending(unsigned long data)
if (!txok)
break;
}
+
+ if (skb_queue_empty(&local->pending[i]))
+ list_for_each_entry_rcu(sdata, &local->interfaces, list)
+ netif_tx_wake_queue(
+ netdev_get_tx_queue(sdata->dev, i));
}
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
@@ -2225,6 +2233,9 @@ void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
if (!encrypt)
info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+ /* send all internal mgmt frames on VO */
+ skb_set_queue_mapping(skb, 0);
+
/*
* The other path calling ieee80211_xmit is from the tasklet,
* and while we can handle concurrent transmissions locking
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index e6c08da8da26..31b10850905f 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -269,6 +269,7 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
enum queue_stop_reason reason)
{
struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_sub_if_data *sdata;
if (WARN_ON(queue >= hw->queues))
return;
@@ -279,7 +280,12 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
/* someone still has this queue stopped */
return;
- if (!skb_queue_empty(&local->pending[queue]))
+ if (skb_queue_empty(&local->pending[queue])) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces, list)
+ netif_tx_wake_queue(netdev_get_tx_queue(sdata->dev, queue));
+ rcu_read_unlock();
+ } else
tasklet_schedule(&local->tx_pending_tasklet);
}
@@ -305,11 +311,17 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
enum queue_stop_reason reason)
{
struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_sub_if_data *sdata;
if (WARN_ON(queue >= hw->queues))
return;
__set_bit(reason, &local->queue_stop_reasons[queue]);
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces, list)
+ netif_tx_stop_queue(netdev_get_tx_queue(sdata->dev, queue));
+ rcu_read_unlock();
}
void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
@@ -579,7 +591,7 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
if (elen > left)
break;
- if (calc_crc && id < 64 && (filter & BIT(id)))
+ if (calc_crc && id < 64 && (filter & (1ULL << id)))
crc = crc32_be(crc, pos - 2, elen + 2);
switch (id) {
@@ -1031,7 +1043,19 @@ int ieee80211_reconfig(struct ieee80211_local *local)
/* restart hardware */
if (local->open_count) {
+ /*
+ * Upon resume hardware can sometimes be goofy due to
+ * various platform / driver / bus issues, so restarting
+ * the device may at times not work immediately. Propagate
+ * the error.
+ */
res = drv_start(local);
+ if (res) {
+ WARN(local->suspended, "Harware became unavailable "
+ "upon resume. This is could be a software issue"
+ "prior to suspend or a harware issue\n");
+ return res;
+ }
ieee80211_led_radio(local, true);
}
@@ -1113,6 +1137,14 @@ int ieee80211_reconfig(struct ieee80211_local *local)
}
}
+ rcu_read_lock();
+ if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
+ list_for_each_entry_rcu(sta, &local->sta_list, list) {
+ ieee80211_sta_tear_down_BA_sessions(sta);
+ }
+ }
+ rcu_read_unlock();
+
/* add back keys */
list_for_each_entry(sdata, &local->interfaces, list)
if (netif_running(sdata->dev))
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index b19b7696f3a2..6d32ebf1ea37 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -44,22 +44,62 @@ static int wme_downgrade_ac(struct sk_buff *skb)
}
-/* Indicate which queue to use. */
-static u16 classify80211(struct ieee80211_local *local, struct sk_buff *skb)
+/* Indicate which queue to use. */
+u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb)
{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ struct ieee80211_local *local = sdata->local;
+ struct sta_info *sta = NULL;
+ u32 sta_flags = 0;
+ const u8 *ra = NULL;
+ bool qos = false;
- if (!ieee80211_is_data(hdr->frame_control)) {
- /* management frames go on AC_VO queue, but are sent
- * without QoS control fields */
- return 0;
+ if (local->hw.queues < 4 || skb->len < 6) {
+ skb->priority = 0; /* required for correct WPA/11i MIC */
+ return min_t(u16, local->hw.queues - 1,
+ ieee802_1d_to_ac[skb->priority]);
}
- if (0 /* injected */) {
- /* use AC from radiotap */
+ rcu_read_lock();
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_AP:
+ ra = skb->data;
+ break;
+ case NL80211_IFTYPE_WDS:
+ ra = sdata->u.wds.remote_addr;
+ break;
+#ifdef CONFIG_MAC80211_MESH
+ case NL80211_IFTYPE_MESH_POINT:
+ /*
+ * XXX: This is clearly broken ... but already was before,
+ * because ieee80211_fill_mesh_addresses() would clear A1
+ * except for multicast addresses.
+ */
+ break;
+#endif
+ case NL80211_IFTYPE_STATION:
+ ra = sdata->u.mgd.bssid;
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ ra = skb->data;
+ break;
+ default:
+ break;
}
- if (!ieee80211_is_data_qos(hdr->frame_control)) {
+ if (!sta && ra && !is_multicast_ether_addr(ra)) {
+ sta = sta_info_get(local, ra);
+ if (sta)
+ sta_flags = get_sta_flags(sta);
+ }
+
+ if (sta_flags & WLAN_STA_WME)
+ qos = true;
+
+ rcu_read_unlock();
+
+ if (!qos) {
skb->priority = 0; /* required for correct WPA/11i MIC */
return ieee802_1d_to_ac[skb->priority];
}
@@ -68,6 +108,12 @@ static u16 classify80211(struct ieee80211_local *local, struct sk_buff *skb)
* data frame has */
skb->priority = cfg80211_classify8021d(skb);
+ return ieee80211_downgrade_queue(local, skb);
+}
+
+u16 ieee80211_downgrade_queue(struct ieee80211_local *local,
+ struct sk_buff *skb)
+{
/* in case we are a client verify acm is not set for this ac */
while (unlikely(local->wmm_acm & BIT(skb->priority))) {
if (wme_downgrade_ac(skb)) {
@@ -85,24 +131,17 @@ static u16 classify80211(struct ieee80211_local *local, struct sk_buff *skb)
return ieee802_1d_to_ac[skb->priority];
}
-void ieee80211_select_queue(struct ieee80211_local *local, struct sk_buff *skb)
+void ieee80211_set_qos_hdr(struct ieee80211_local *local, struct sk_buff *skb)
{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- u16 queue;
- u8 tid;
-
- queue = classify80211(local, skb);
- if (unlikely(queue >= local->hw.queues))
- queue = local->hw.queues - 1;
-
- /*
- * Now we know the 1d priority, fill in the QoS header if
- * there is one (and we haven't done this before).
- */
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+
+ /* Fill in the QoS header if there is one. */
if (ieee80211_is_data_qos(hdr->frame_control)) {
u8 *p = ieee80211_get_qos_ctl(hdr);
- u8 ack_policy = 0;
+ u8 ack_policy = 0, tid;
+
tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
+
if (unlikely(local->wifi_wme_noack_test))
ack_policy |= QOS_CONTROL_ACK_POLICY_NOACK <<
QOS_CONTROL_ACK_POLICY_SHIFT;
@@ -110,6 +149,4 @@ void ieee80211_select_queue(struct ieee80211_local *local, struct sk_buff *skb)
*p++ = ack_policy | tid;
*p = 0;
}
-
- skb_set_queue_mapping(skb, queue);
}
diff --git a/net/mac80211/wme.h b/net/mac80211/wme.h
index d4fd87ca5118..6053b1c9feee 100644
--- a/net/mac80211/wme.h
+++ b/net/mac80211/wme.h
@@ -20,7 +20,11 @@
extern const int ieee802_1d_to_ac[8];
-void ieee80211_select_queue(struct ieee80211_local *local,
- struct sk_buff *skb);
+u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb);
+void ieee80211_set_qos_hdr(struct ieee80211_local *local, struct sk_buff *skb);
+u16 ieee80211_downgrade_queue(struct ieee80211_local *local,
+ struct sk_buff *skb);
+
#endif /* _WME_H */