aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/Kconfig8
-rw-r--r--net/mac80211/agg-rx.c11
-rw-r--r--net/mac80211/agg-tx.c7
-rw-r--r--net/mac80211/cfg.c195
-rw-r--r--net/mac80211/debugfs_key.c37
-rw-r--r--net/mac80211/debugfs_key.h8
-rw-r--r--net/mac80211/debugfs_sta.c2
-rw-r--r--net/mac80211/driver-ops.h32
-rw-r--r--net/mac80211/driver-trace.h80
-rw-r--r--net/mac80211/ieee80211_i.h46
-rw-r--r--net/mac80211/iface.c22
-rw-r--r--net/mac80211/key.c89
-rw-r--r--net/mac80211/key.h3
-rw-r--r--net/mac80211/led.c186
-rw-r--r--net/mac80211/led.h45
-rw-r--r--net/mac80211/main.c33
-rw-r--r--net/mac80211/mesh.c52
-rw-r--r--net/mac80211/mesh.h22
-rw-r--r--net/mac80211/mesh_plink.c3
-rw-r--r--net/mac80211/offchannel.c85
-rw-r--r--net/mac80211/rc80211_minstrel_ht.c2
-rw-r--r--net/mac80211/rx.c164
-rw-r--r--net/mac80211/sta_info.h2
-rw-r--r--net/mac80211/tx.c42
-rw-r--r--net/mac80211/util.c3
-rw-r--r--net/mac80211/wme.c20
26 files changed, 932 insertions, 267 deletions
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index 798d9b9462e2..c766056d0488 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -20,7 +20,7 @@ config MAC80211_HAS_RC
def_bool n
config MAC80211_RC_PID
- bool "PID controller based rate control algorithm" if EMBEDDED
+ bool "PID controller based rate control algorithm" if EXPERT
select MAC80211_HAS_RC
---help---
This option enables a TX rate control algorithm for
@@ -28,14 +28,14 @@ config MAC80211_RC_PID
rate.
config MAC80211_RC_MINSTREL
- bool "Minstrel" if EMBEDDED
+ bool "Minstrel" if EXPERT
select MAC80211_HAS_RC
default y
---help---
This option enables the 'minstrel' TX rate control algorithm
config MAC80211_RC_MINSTREL_HT
- bool "Minstrel 802.11n support" if EMBEDDED
+ bool "Minstrel 802.11n support" if EXPERT
depends on MAC80211_RC_MINSTREL
default y
---help---
@@ -93,7 +93,7 @@ config MAC80211_MESH
config MAC80211_LEDS
bool "Enable LED triggers"
depends on MAC80211
- select NEW_LEDS
+ depends on LEDS_CLASS
select LEDS_TRIGGERS
---help---
This option enables a few LED triggers for different
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index f138b195d657..227ca82eef72 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -185,8 +185,6 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
struct ieee80211_mgmt *mgmt,
size_t len)
{
- struct ieee80211_hw *hw = &local->hw;
- struct ieee80211_conf *conf = &hw->conf;
struct tid_ampdu_rx *tid_agg_rx;
u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num, status;
u8 dialog_token;
@@ -231,13 +229,8 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
goto end_no_lock;
}
/* determine default buffer size */
- if (buf_size == 0) {
- struct ieee80211_supported_band *sband;
-
- sband = local->hw.wiphy->bands[conf->channel->band];
- buf_size = IEEE80211_MIN_AMPDU_BUF;
- buf_size = buf_size << sband->ht_cap.ampdu_factor;
- }
+ if (buf_size == 0)
+ buf_size = IEEE80211_MAX_AMPDU_BUF;
/* examine state machine */
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index d4679b265ba8..9cc472c6a6a5 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -342,10 +342,11 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
/* send AddBA request */
ieee80211_send_addba_request(sdata, sta->sta.addr, tid,
tid_tx->dialog_token, start_seq_num,
- 0x40, 5000);
+ 0x40, tid_tx->timeout);
}
-int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid)
+int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
+ u16 timeout)
{
struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
struct ieee80211_sub_if_data *sdata = sta->sdata;
@@ -420,6 +421,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid)
skb_queue_head_init(&tid_tx->pending);
__set_bit(HT_AGG_STATE_WANT_START, &tid_tx->state);
+ tid_tx->timeout = timeout;
+
/* Tx timer */
tid_tx->addba_resp_timer.function = sta_addba_resp_timer_expired;
tid_tx->addba_resp_timer.data = (unsigned long)&sta->timer_to_tid[tid];
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index c30b8b72eedb..4bc8a9250cfd 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -295,11 +295,12 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
static int ieee80211_config_default_key(struct wiphy *wiphy,
struct net_device *dev,
- u8 key_idx)
+ u8 key_idx, bool uni,
+ bool multi)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- ieee80211_set_default_key(sdata, key_idx);
+ ieee80211_set_default_key(sdata, key_idx, uni, multi);
return 0;
}
@@ -983,7 +984,7 @@ static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
return 0;
}
-static int ieee80211_get_mesh_params(struct wiphy *wiphy,
+static int ieee80211_get_mesh_config(struct wiphy *wiphy,
struct net_device *dev,
struct mesh_config *conf)
{
@@ -999,7 +1000,37 @@ static inline bool _chg_mesh_attr(enum nl80211_meshconf_params parm, u32 mask)
return (mask >> (parm-1)) & 0x1;
}
-static int ieee80211_update_mesh_params(struct wiphy *wiphy,
+static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh,
+ const struct mesh_setup *setup)
+{
+ u8 *new_ie;
+ const u8 *old_ie;
+
+ /* first allocate the new vendor information element */
+ new_ie = NULL;
+ old_ie = ifmsh->vendor_ie;
+
+ ifmsh->vendor_ie_len = setup->vendor_ie_len;
+ if (setup->vendor_ie_len) {
+ new_ie = kmemdup(setup->vendor_ie, setup->vendor_ie_len,
+ GFP_KERNEL);
+ if (!new_ie)
+ return -ENOMEM;
+ }
+
+ /* now copy the rest of the setup parameters */
+ ifmsh->mesh_id_len = setup->mesh_id_len;
+ memcpy(ifmsh->mesh_id, setup->mesh_id, ifmsh->mesh_id_len);
+ ifmsh->mesh_pp_id = setup->path_sel_proto;
+ ifmsh->mesh_pm_id = setup->path_metric;
+ ifmsh->vendor_ie = new_ie;
+
+ kfree(old_ie);
+
+ return 0;
+}
+
+static int ieee80211_update_mesh_config(struct wiphy *wiphy,
struct net_device *dev, u32 mask,
const struct mesh_config *nconf)
{
@@ -1058,11 +1089,12 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev,
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+ int err;
- memcpy(&sdata->u.mesh.mshcfg, conf, sizeof(struct mesh_config));
- ifmsh->mesh_id_len = setup->mesh_id_len;
- memcpy(ifmsh->mesh_id, setup->mesh_id, ifmsh->mesh_id_len);
-
+ memcpy(&ifmsh->mshcfg, conf, sizeof(struct mesh_config));
+ err = copy_mesh_setup(ifmsh, setup);
+ if (err)
+ return err;
ieee80211_start_mesh(sdata);
return 0;
@@ -1561,6 +1593,37 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
return 0;
}
+static int ieee80211_remain_on_channel_hw(struct ieee80211_local *local,
+ struct net_device *dev,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type chantype,
+ unsigned int duration, u64 *cookie)
+{
+ int ret;
+ u32 random_cookie;
+
+ lockdep_assert_held(&local->mtx);
+
+ if (local->hw_roc_cookie)
+ return -EBUSY;
+ /* must be nonzero */
+ random_cookie = random32() | 1;
+
+ *cookie = random_cookie;
+ local->hw_roc_dev = dev;
+ local->hw_roc_cookie = random_cookie;
+ local->hw_roc_channel = chan;
+ local->hw_roc_channel_type = chantype;
+ local->hw_roc_duration = duration;
+ ret = drv_remain_on_channel(local, chan, chantype, duration);
+ if (ret) {
+ local->hw_roc_channel = NULL;
+ local->hw_roc_cookie = 0;
+ }
+
+ return ret;
+}
+
static int ieee80211_remain_on_channel(struct wiphy *wiphy,
struct net_device *dev,
struct ieee80211_channel *chan,
@@ -1569,16 +1632,63 @@ static int ieee80211_remain_on_channel(struct wiphy *wiphy,
u64 *cookie)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
+
+ if (local->ops->remain_on_channel) {
+ int ret;
+
+ mutex_lock(&local->mtx);
+ ret = ieee80211_remain_on_channel_hw(local, dev,
+ chan, channel_type,
+ duration, cookie);
+ local->hw_roc_for_tx = false;
+ mutex_unlock(&local->mtx);
+
+ return ret;
+ }
return ieee80211_wk_remain_on_channel(sdata, chan, channel_type,
duration, cookie);
}
+static int ieee80211_cancel_remain_on_channel_hw(struct ieee80211_local *local,
+ u64 cookie)
+{
+ int ret;
+
+ lockdep_assert_held(&local->mtx);
+
+ if (local->hw_roc_cookie != cookie)
+ return -ENOENT;
+
+ ret = drv_cancel_remain_on_channel(local);
+ if (ret)
+ return ret;
+
+ local->hw_roc_cookie = 0;
+ local->hw_roc_channel = NULL;
+
+ ieee80211_recalc_idle(local);
+
+ return 0;
+}
+
static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
struct net_device *dev,
u64 cookie)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
+
+ if (local->ops->cancel_remain_on_channel) {
+ int ret;
+
+ mutex_lock(&local->mtx);
+ ret = ieee80211_cancel_remain_on_channel_hw(local, cookie);
+ mutex_unlock(&local->mtx);
+
+ return ret;
+ }
return ieee80211_wk_cancel_remain_on_channel(sdata, cookie);
}
@@ -1630,6 +1740,12 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
channel_type != local->_oper_channel_type))
is_offchan = true;
+ if (chan == local->hw_roc_channel) {
+ /* TODO: check channel type? */
+ is_offchan = false;
+ flags |= IEEE80211_TX_CTL_TX_OFFCHAN;
+ }
+
if (is_offchan && !offchan)
return -EBUSY;
@@ -1638,6 +1754,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_MESH_POINT:
if (!ieee80211_is_action(mgmt->frame_control) ||
mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)
break;
@@ -1667,6 +1784,49 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev,
*cookie = (unsigned long) skb;
+ if (is_offchan && local->ops->remain_on_channel) {
+ unsigned int duration;
+ int ret;
+
+ mutex_lock(&local->mtx);
+ /*
+ * If the duration is zero, then the driver
+ * wouldn't actually do anything. Set it to
+ * 100 for now.
+ *
+ * TODO: cancel the off-channel operation
+ * when we get the SKB's TX status and
+ * the wait time was zero before.
+ */
+ duration = 100;
+ if (wait)
+ duration = wait;
+ ret = ieee80211_remain_on_channel_hw(local, dev, chan,
+ channel_type,
+ duration, cookie);
+ if (ret) {
+ kfree_skb(skb);
+ mutex_unlock(&local->mtx);
+ return ret;
+ }
+
+ local->hw_roc_for_tx = true;
+ local->hw_roc_duration = wait;
+
+ /*
+ * queue up frame for transmission after
+ * ieee80211_ready_on_channel call
+ */
+
+ /* modify cookie to prevent API mismatches */
+ *cookie ^= 2;
+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_TX_OFFCHAN;
+ local->hw_roc_skb = skb;
+ mutex_unlock(&local->mtx);
+
+ return 0;
+ }
+
/*
* Can transmit right away if the channel was the
* right one and there's no wait involved... If a
@@ -1707,6 +1867,21 @@ static int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
int ret = -ENOENT;
mutex_lock(&local->mtx);
+
+ if (local->ops->cancel_remain_on_channel) {
+ cookie ^= 2;
+ ret = ieee80211_cancel_remain_on_channel_hw(local, cookie);
+
+ if (ret == 0) {
+ kfree_skb(local->hw_roc_skb);
+ local->hw_roc_skb = NULL;
+ }
+
+ mutex_unlock(&local->mtx);
+
+ return ret;
+ }
+
list_for_each_entry(wk, &local->work_list, list) {
if (wk->sdata != sdata)
continue;
@@ -1786,8 +1961,8 @@ struct cfg80211_ops mac80211_config_ops = {
.change_mpath = ieee80211_change_mpath,
.get_mpath = ieee80211_get_mpath,
.dump_mpath = ieee80211_dump_mpath,
- .update_mesh_params = ieee80211_update_mesh_params,
- .get_mesh_params = ieee80211_get_mesh_params,
+ .update_mesh_config = ieee80211_update_mesh_config,
+ .get_mesh_config = ieee80211_get_mesh_config,
.join_mesh = ieee80211_join_mesh,
.leave_mesh = ieee80211_leave_mesh,
#endif
diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c
index 5822a6ce7671..f7ef3477c24a 100644
--- a/net/mac80211/debugfs_key.c
+++ b/net/mac80211/debugfs_key.c
@@ -274,7 +274,8 @@ void ieee80211_debugfs_key_remove(struct ieee80211_key *key)
debugfs_remove_recursive(key->debugfs.dir);
key->debugfs.dir = NULL;
}
-void ieee80211_debugfs_key_add_default(struct ieee80211_sub_if_data *sdata)
+
+void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata)
{
char buf[50];
struct ieee80211_key *key;
@@ -282,25 +283,29 @@ void ieee80211_debugfs_key_add_default(struct ieee80211_sub_if_data *sdata)
if (!sdata->debugfs.dir)
return;
- /* this is running under the key lock */
+ lockdep_assert_held(&sdata->local->key_mtx);
- key = sdata->default_key;
- if (key) {
+ if (sdata->default_unicast_key) {
+ key = sdata->default_unicast_key;
sprintf(buf, "../keys/%d", key->debugfs.cnt);
- sdata->debugfs.default_key =
- debugfs_create_symlink("default_key",
+ sdata->debugfs.default_unicast_key =
+ debugfs_create_symlink("default_unicast_key",
sdata->debugfs.dir, buf);
- } else
- ieee80211_debugfs_key_remove_default(sdata);
-}
-
-void ieee80211_debugfs_key_remove_default(struct ieee80211_sub_if_data *sdata)
-{
- if (!sdata)
- return;
+ } else {
+ debugfs_remove(sdata->debugfs.default_unicast_key);
+ sdata->debugfs.default_unicast_key = NULL;
+ }
- debugfs_remove(sdata->debugfs.default_key);
- sdata->debugfs.default_key = NULL;
+ if (sdata->default_multicast_key) {
+ key = sdata->default_multicast_key;
+ sprintf(buf, "../keys/%d", key->debugfs.cnt);
+ sdata->debugfs.default_multicast_key =
+ debugfs_create_symlink("default_multicast_key",
+ sdata->debugfs.dir, buf);
+ } else {
+ debugfs_remove(sdata->debugfs.default_multicast_key);
+ sdata->debugfs.default_multicast_key = NULL;
+ }
}
void ieee80211_debugfs_key_add_mgmt_default(struct ieee80211_sub_if_data *sdata)
diff --git a/net/mac80211/debugfs_key.h b/net/mac80211/debugfs_key.h
index 54717b4e1371..32adc77e9c77 100644
--- a/net/mac80211/debugfs_key.h
+++ b/net/mac80211/debugfs_key.h
@@ -4,8 +4,7 @@
#ifdef CONFIG_MAC80211_DEBUGFS
void ieee80211_debugfs_key_add(struct ieee80211_key *key);
void ieee80211_debugfs_key_remove(struct ieee80211_key *key);
-void ieee80211_debugfs_key_add_default(struct ieee80211_sub_if_data *sdata);
-void ieee80211_debugfs_key_remove_default(struct ieee80211_sub_if_data *sdata);
+void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata);
void ieee80211_debugfs_key_add_mgmt_default(
struct ieee80211_sub_if_data *sdata);
void ieee80211_debugfs_key_remove_mgmt_default(
@@ -17,10 +16,7 @@ static inline void ieee80211_debugfs_key_add(struct ieee80211_key *key)
{}
static inline void ieee80211_debugfs_key_remove(struct ieee80211_key *key)
{}
-static inline void ieee80211_debugfs_key_add_default(
- struct ieee80211_sub_if_data *sdata)
-{}
-static inline void ieee80211_debugfs_key_remove_default(
+static inline void ieee80211_debugfs_key_update_default(
struct ieee80211_sub_if_data *sdata)
{}
static inline void ieee80211_debugfs_key_add_mgmt_default(
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index 8bb5af85f469..c04a1396cf8d 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -189,7 +189,7 @@ static ssize_t sta_agg_status_write(struct file *file, const char __user *userbu
if (tx) {
if (start)
- ret = ieee80211_start_tx_ba_session(&sta->sta, tid);
+ ret = ieee80211_start_tx_ba_session(&sta->sta, tid, 5000);
else
ret = ieee80211_stop_tx_ba_session(&sta->sta, tid);
} else {
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 4244554d218a..98d589960a49 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -367,7 +367,7 @@ static inline void drv_reset_tsf(struct ieee80211_local *local)
static inline int drv_tx_last_beacon(struct ieee80211_local *local)
{
- int ret = 1;
+ int ret = 0; /* default unsuported op for less congestion */
might_sleep();
@@ -465,4 +465,34 @@ static inline int drv_get_antenna(struct ieee80211_local *local,
return ret;
}
+static inline int drv_remain_on_channel(struct ieee80211_local *local,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type chantype,
+ unsigned int duration)
+{
+ int ret;
+
+ might_sleep();
+
+ trace_drv_remain_on_channel(local, chan, chantype, duration);
+ ret = local->ops->remain_on_channel(&local->hw, chan, chantype,
+ duration);
+ trace_drv_return_int(local, ret);
+
+ return ret;
+}
+
+static inline int drv_cancel_remain_on_channel(struct ieee80211_local *local)
+{
+ int ret;
+
+ might_sleep();
+
+ trace_drv_cancel_remain_on_channel(local);
+ ret = local->ops->cancel_remain_on_channel(&local->hw);
+ trace_drv_return_int(local, ret);
+
+ return ret;
+}
+
#endif /* __MAC80211_DRIVER_OPS */
diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h
index c2772f23ac9c..49c84218b2f4 100644
--- a/net/mac80211/driver-trace.h
+++ b/net/mac80211/driver-trace.h
@@ -933,6 +933,50 @@ TRACE_EVENT(drv_get_antenna,
)
);
+TRACE_EVENT(drv_remain_on_channel,
+ TP_PROTO(struct ieee80211_local *local, struct ieee80211_channel *chan,
+ enum nl80211_channel_type chantype, unsigned int duration),
+
+ TP_ARGS(local, chan, chantype, duration),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ __field(int, center_freq)
+ __field(int, channel_type)
+ __field(unsigned int, duration)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ __entry->center_freq = chan->center_freq;
+ __entry->channel_type = chantype;
+ __entry->duration = duration;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT " freq:%dMHz duration:%dms",
+ LOCAL_PR_ARG, __entry->center_freq, __entry->duration
+ )
+);
+
+TRACE_EVENT(drv_cancel_remain_on_channel,
+ TP_PROTO(struct ieee80211_local *local),
+
+ TP_ARGS(local),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT, LOCAL_PR_ARG
+ )
+);
+
/*
* Tracing for API calls that drivers call.
*/
@@ -1170,6 +1214,42 @@ TRACE_EVENT(api_chswitch_done,
)
);
+TRACE_EVENT(api_ready_on_channel,
+ TP_PROTO(struct ieee80211_local *local),
+
+ TP_ARGS(local),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT, LOCAL_PR_ARG
+ )
+);
+
+TRACE_EVENT(api_remain_on_channel_expired,
+ TP_PROTO(struct ieee80211_local *local),
+
+ TP_ARGS(local),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT, LOCAL_PR_ARG
+ )
+);
+
/*
* Tracing for internal functions
* (which may also be called in response to driver calls)
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 72499fe5fc36..c47d7c0e48a4 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -23,6 +23,7 @@
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/etherdevice.h>
+#include <linux/leds.h>
#include <net/ieee80211_radiotap.h>
#include <net/cfg80211.h>
#include <net/mac80211.h>
@@ -167,6 +168,7 @@ typedef unsigned __bitwise__ ieee80211_rx_result;
* @IEEE80211_RX_FRAGMENTED: fragmented frame
* @IEEE80211_RX_AMSDU: a-MSDU packet
* @IEEE80211_RX_MALFORMED_ACTION_FRM: action frame is malformed
+ * @IEEE80211_RX_DEFERRED_RELEASE: frame was subjected to receive reordering
*
* These are per-frame flags that are attached to a frame in the
* @rx_flags field of &struct ieee80211_rx_status.
@@ -177,6 +179,7 @@ enum ieee80211_packet_rx_flags {
IEEE80211_RX_FRAGMENTED = BIT(2),
IEEE80211_RX_AMSDU = BIT(3),
IEEE80211_RX_MALFORMED_ACTION_FRM = BIT(4),
+ IEEE80211_RX_DEFERRED_RELEASE = BIT(5),
};
/**
@@ -484,6 +487,8 @@ struct ieee80211_if_mesh {
struct mesh_config mshcfg;
u32 mesh_seqnum;
bool accepting_plinks;
+ const u8 *vendor_ie;
+ u8 vendor_ie_len;
};
#ifdef CONFIG_MAC80211_MESH
@@ -557,7 +562,7 @@ struct ieee80211_sub_if_data {
unsigned int fragment_next;
struct ieee80211_key *keys[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS];
- struct ieee80211_key *default_key;
+ struct ieee80211_key *default_unicast_key, *default_multicast_key;
struct ieee80211_key *default_mgmt_key;
u16 sequence_number;
@@ -585,9 +590,7 @@ struct ieee80211_sub_if_data {
struct ieee80211_if_vlan vlan;
struct ieee80211_if_managed mgd;
struct ieee80211_if_ibss ibss;
-#ifdef CONFIG_MAC80211_MESH
struct ieee80211_if_mesh mesh;
-#endif
u32 mntr_flags;
} u;
@@ -595,7 +598,8 @@ struct ieee80211_sub_if_data {
struct {
struct dentry *dir;
struct dentry *subdir_stations;
- struct dentry *default_key;
+ struct dentry *default_unicast_key;
+ struct dentry *default_multicast_key;
struct dentry *default_mgmt_key;
} debugfs;
#endif
@@ -629,6 +633,20 @@ enum queue_stop_reason {
IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
};
+#ifdef CONFIG_MAC80211_LEDS
+struct tpt_led_trigger {
+ struct led_trigger trig;
+ char name[32];
+ const struct ieee80211_tpt_blink *blink_table;
+ unsigned int blink_table_len;
+ struct timer_list timer;
+ unsigned long prev_traffic;
+ unsigned long tx_bytes, rx_bytes;
+ unsigned int active, want;
+ bool running;
+};
+#endif
+
/**
* mac80211 scan flags - currently active scan mode
*
@@ -758,6 +776,15 @@ struct ieee80211_local {
struct sk_buff_head skb_queue;
struct sk_buff_head skb_queue_unreliable;
+ /*
+ * Internal FIFO queue which is shared between multiple rx path
+ * stages. Its main task is to provide a serialization mechanism,
+ * so all rx handlers can enjoy having exclusive access to their
+ * private data structures.
+ */
+ struct sk_buff_head rx_skb_queue;
+ bool running_rx_handler; /* protected by rx_skb_queue.lock */
+
/* Station data */
/*
* The mutex only protects the list and counter,
@@ -837,6 +864,7 @@ struct ieee80211_local {
#ifdef CONFIG_MAC80211_LEDS
int tx_led_counter, rx_led_counter;
struct led_trigger *tx_led, *rx_led, *assoc_led, *radio_led;
+ struct tpt_led_trigger *tpt_led_trigger;
char tx_led_name[32], rx_led_name[32],
assoc_led_name[32], radio_led_name[32];
#endif
@@ -923,6 +951,15 @@ struct ieee80211_local {
} debugfs;
#endif
+ struct ieee80211_channel *hw_roc_channel;
+ struct net_device *hw_roc_dev;
+ struct sk_buff *hw_roc_skb;
+ struct work_struct hw_roc_start, hw_roc_done;
+ enum nl80211_channel_type hw_roc_channel_type;
+ unsigned int hw_roc_duration;
+ u32 hw_roc_cookie;
+ bool hw_roc_for_tx;
+
/* dummy netdev for use w/ NAPI */
struct net_device napi_dev;
@@ -1114,6 +1151,7 @@ void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local);
void ieee80211_offchannel_stop_station(struct ieee80211_local *local);
void ieee80211_offchannel_return(struct ieee80211_local *local,
bool enable_beaconing);
+void ieee80211_hw_roc_setup(struct ieee80211_local *local);
/* interface handling */
int ieee80211_iface_init(void);
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index f0f11bb794af..8acba456744e 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -220,6 +220,8 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up)
/* 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);
}
/*
@@ -1262,8 +1264,9 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local)
{
struct ieee80211_sub_if_data *sdata;
int count = 0;
- bool working = false, scanning = false;
+ 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() &&
@@ -1305,6 +1308,9 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local)
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->old_idle == sdata->vif.bss_conf.idle)
continue;
@@ -1313,6 +1319,20 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local)
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)
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 72df1ca7299b..8c02469b7176 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -30,19 +30,20 @@
* keys and per-station keys. Since each station belongs to an interface,
* each station key also belongs to that interface.
*
- * Hardware acceleration is done on a best-effort basis, for each key
- * that is eligible the hardware is asked to enable that key but if
- * it cannot do that they key is simply kept for software encryption.
- * There is currently no way of knowing this except by looking into
- * debugfs.
+ * Hardware acceleration is done on a best-effort basis for algorithms
+ * that are implemented in software, for each key the hardware is asked
+ * to enable that key for offloading but if it cannot do that the key is
+ * simply kept for software encryption (unless it is for an algorithm
+ * that isn't implemented in software).
+ * There is currently no way of knowing whether a key is handled in SW
+ * or HW except by looking into debugfs.
*
- * All key operations are protected internally.
- *
- * Within mac80211, key references are, just as STA structure references,
- * protected by RCU. Note, however, that some things are unprotected,
- * namely the key->sta dereferences within the hardware acceleration
- * functions. This means that sta_info_destroy() must remove the key
- * which waits for an RCU grace period.
+ * All key management is internally protected by a mutex. Within all
+ * other parts of mac80211, key references are, just as STA structure
+ * references, protected by RCU. Note, however, that some things are
+ * unprotected, namely the key->sta dereferences within the hardware
+ * acceleration functions. This means that sta_info_destroy() must
+ * remove the key which waits for an RCU grace period.
*/
static const u8 bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
@@ -178,7 +179,7 @@ void ieee80211_key_removed(struct ieee80211_key_conf *key_conf)
EXPORT_SYMBOL_GPL(ieee80211_key_removed);
static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata,
- int idx)
+ int idx, bool uni, bool multi)
{
struct ieee80211_key *key = NULL;
@@ -187,18 +188,19 @@ static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata,
if (idx >= 0 && idx < NUM_DEFAULT_KEYS)
key = sdata->keys[idx];
- rcu_assign_pointer(sdata->default_key, key);
+ if (uni)
+ rcu_assign_pointer(sdata->default_unicast_key, key);
+ if (multi)
+ rcu_assign_pointer(sdata->default_multicast_key, key);
- if (key) {
- ieee80211_debugfs_key_remove_default(key->sdata);
- ieee80211_debugfs_key_add_default(key->sdata);
- }
+ ieee80211_debugfs_key_update_default(sdata);
}
-void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx)
+void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx,
+ bool uni, bool multi)
{
mutex_lock(&sdata->local->key_mtx);
- __ieee80211_set_default_key(sdata, idx);
+ __ieee80211_set_default_key(sdata, idx, uni, multi);
mutex_unlock(&sdata->local->key_mtx);
}
@@ -215,10 +217,7 @@ __ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata, int idx)
rcu_assign_pointer(sdata->default_mgmt_key, key);
- if (key) {
- ieee80211_debugfs_key_remove_mgmt_default(key->sdata);
- ieee80211_debugfs_key_add_mgmt_default(key->sdata);
- }
+ ieee80211_debugfs_key_update_default(sdata);
}
void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata,
@@ -236,7 +235,8 @@ static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
struct ieee80211_key *old,
struct ieee80211_key *new)
{
- int idx, defkey, defmgmtkey;
+ int idx;
+ bool defunikey, defmultikey, defmgmtkey;
if (new)
list_add(&new->list, &sdata->key_list);
@@ -257,29 +257,31 @@ static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
else
idx = new->conf.keyidx;
- defkey = old && sdata->default_key == old;
+ defunikey = old && sdata->default_unicast_key == old;
+ defmultikey = old && sdata->default_multicast_key == old;
defmgmtkey = old && sdata->default_mgmt_key == old;
- if (defkey && !new)
- __ieee80211_set_default_key(sdata, -1);
+ if (defunikey && !new)
+ __ieee80211_set_default_key(sdata, -1, true, false);
+ if (defmultikey && !new)
+ __ieee80211_set_default_key(sdata, -1, false, true);
if (defmgmtkey && !new)
__ieee80211_set_default_mgmt_key(sdata, -1);
rcu_assign_pointer(sdata->keys[idx], new);
- if (defkey && new)
- __ieee80211_set_default_key(sdata, new->conf.keyidx);
+ if (defunikey && new)
+ __ieee80211_set_default_key(sdata, new->conf.keyidx,
+ true, false);
+ if (defmultikey && new)
+ __ieee80211_set_default_key(sdata, new->conf.keyidx,
+ false, true);
if (defmgmtkey && new)
__ieee80211_set_default_mgmt_key(sdata,
new->conf.keyidx);
}
- if (old) {
- /*
- * We'll use an empty list to indicate that the key
- * has already been removed.
- */
- list_del_init(&old->list);
- }
+ if (old)
+ list_del(&old->list);
}
struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
@@ -373,6 +375,12 @@ static void __ieee80211_key_destroy(struct ieee80211_key *key)
if (!key)
return;
+ /*
+ * Synchronize so the TX path can no longer be using
+ * this key before we free/remove it.
+ */
+ synchronize_rcu();
+
if (key->local)
ieee80211_key_disable_hw_accel(key);
@@ -414,8 +422,8 @@ int ieee80211_key_link(struct ieee80211_key *key,
struct sta_info *ap;
/*
- * We're getting a sta pointer in,
- * so must be under RCU read lock.
+ * We're getting a sta pointer in, so must be under
+ * appropriate locking for sta_info_get().
*/
/* same here, the AP could be using QoS */
@@ -509,11 +517,12 @@ void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata)
mutex_lock(&sdata->local->key_mtx);
- ieee80211_debugfs_key_remove_default(sdata);
ieee80211_debugfs_key_remove_mgmt_default(sdata);
list_for_each_entry_safe(key, tmp, &sdata->key_list, list)
__ieee80211_key_free(key);
+ ieee80211_debugfs_key_update_default(sdata);
+
mutex_unlock(&sdata->local->key_mtx);
}
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index 0db1c0f5f697..8106aa1b7466 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -138,7 +138,8 @@ int __must_check ieee80211_key_link(struct ieee80211_key *key,
struct sta_info *sta);
void ieee80211_key_free(struct ieee80211_local *local,
struct ieee80211_key *key);
-void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx);
+void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx,
+ bool uni, bool multi);
void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata,
int idx);
void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata);
diff --git a/net/mac80211/led.c b/net/mac80211/led.c
index 063aad944246..14590332c81c 100644
--- a/net/mac80211/led.c
+++ b/net/mac80211/led.c
@@ -54,12 +54,22 @@ void ieee80211_led_radio(struct ieee80211_local *local, bool enabled)
led_trigger_event(local->radio_led, LED_OFF);
}
+void ieee80211_led_names(struct ieee80211_local *local)
+{
+ snprintf(local->rx_led_name, sizeof(local->rx_led_name),
+ "%srx", wiphy_name(local->hw.wiphy));
+ snprintf(local->tx_led_name, sizeof(local->tx_led_name),
+ "%stx", wiphy_name(local->hw.wiphy));
+ snprintf(local->assoc_led_name, sizeof(local->assoc_led_name),
+ "%sassoc", wiphy_name(local->hw.wiphy));
+ snprintf(local->radio_led_name, sizeof(local->radio_led_name),
+ "%sradio", wiphy_name(local->hw.wiphy));
+}
+
void ieee80211_led_init(struct ieee80211_local *local)
{
local->rx_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
if (local->rx_led) {
- snprintf(local->rx_led_name, sizeof(local->rx_led_name),
- "%srx", wiphy_name(local->hw.wiphy));
local->rx_led->name = local->rx_led_name;
if (led_trigger_register(local->rx_led)) {
kfree(local->rx_led);
@@ -69,8 +79,6 @@ void ieee80211_led_init(struct ieee80211_local *local)
local->tx_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
if (local->tx_led) {
- snprintf(local->tx_led_name, sizeof(local->tx_led_name),
- "%stx", wiphy_name(local->hw.wiphy));
local->tx_led->name = local->tx_led_name;
if (led_trigger_register(local->tx_led)) {
kfree(local->tx_led);
@@ -80,8 +88,6 @@ void ieee80211_led_init(struct ieee80211_local *local)
local->assoc_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
if (local->assoc_led) {
- snprintf(local->assoc_led_name, sizeof(local->assoc_led_name),
- "%sassoc", wiphy_name(local->hw.wiphy));
local->assoc_led->name = local->assoc_led_name;
if (led_trigger_register(local->assoc_led)) {
kfree(local->assoc_led);
@@ -91,14 +97,19 @@ void ieee80211_led_init(struct ieee80211_local *local)
local->radio_led = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
if (local->radio_led) {
- snprintf(local->radio_led_name, sizeof(local->radio_led_name),
- "%sradio", wiphy_name(local->hw.wiphy));
local->radio_led->name = local->radio_led_name;
if (led_trigger_register(local->radio_led)) {
kfree(local->radio_led);
local->radio_led = NULL;
}
}
+
+ if (local->tpt_led_trigger) {
+ if (led_trigger_register(&local->tpt_led_trigger->trig)) {
+ kfree(local->tpt_led_trigger);
+ local->tpt_led_trigger = NULL;
+ }
+ }
}
void ieee80211_led_exit(struct ieee80211_local *local)
@@ -119,15 +130,18 @@ void ieee80211_led_exit(struct ieee80211_local *local)
led_trigger_unregister(local->rx_led);
kfree(local->rx_led);
}
+
+ if (local->tpt_led_trigger) {
+ led_trigger_unregister(&local->tpt_led_trigger->trig);
+ kfree(local->tpt_led_trigger);
+ }
}
char *__ieee80211_get_radio_led_name(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
- if (local->radio_led)
- return local->radio_led_name;
- return NULL;
+ return local->radio_led_name;
}
EXPORT_SYMBOL(__ieee80211_get_radio_led_name);
@@ -135,9 +149,7 @@ char *__ieee80211_get_assoc_led_name(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
- if (local->assoc_led)
- return local->assoc_led_name;
- return NULL;
+ return local->assoc_led_name;
}
EXPORT_SYMBOL(__ieee80211_get_assoc_led_name);
@@ -145,9 +157,7 @@ char *__ieee80211_get_tx_led_name(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
- if (local->tx_led)
- return local->tx_led_name;
- return NULL;
+ return local->tx_led_name;
}
EXPORT_SYMBOL(__ieee80211_get_tx_led_name);
@@ -155,8 +165,144 @@ char *__ieee80211_get_rx_led_name(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
- if (local->rx_led)
- return local->rx_led_name;
- return NULL;
+ return local->rx_led_name;
}
EXPORT_SYMBOL(__ieee80211_get_rx_led_name);
+
+static unsigned long tpt_trig_traffic(struct ieee80211_local *local,
+ struct tpt_led_trigger *tpt_trig)
+{
+ unsigned long traffic, delta;
+
+ traffic = tpt_trig->tx_bytes + tpt_trig->rx_bytes;
+
+ delta = traffic - tpt_trig->prev_traffic;
+ tpt_trig->prev_traffic = traffic;
+ return DIV_ROUND_UP(delta, 1024 / 8);
+}
+
+static void tpt_trig_timer(unsigned long data)
+{
+ struct ieee80211_local *local = (void *)data;
+ struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
+ struct led_classdev *led_cdev;
+ unsigned long on, off, tpt;
+ int i;
+
+ if (!tpt_trig->running)
+ return;
+
+ mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
+
+ tpt = tpt_trig_traffic(local, tpt_trig);
+
+ /* default to just solid on */
+ on = 1;
+ off = 0;
+
+ for (i = tpt_trig->blink_table_len - 1; i >= 0; i--) {
+ if (tpt_trig->blink_table[i].throughput < 0 ||
+ tpt > tpt_trig->blink_table[i].throughput) {
+ off = tpt_trig->blink_table[i].blink_time / 2;
+ on = tpt_trig->blink_table[i].blink_time - off;
+ break;
+ }
+ }
+
+ read_lock(&tpt_trig->trig.leddev_list_lock);
+ list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list)
+ led_blink_set(led_cdev, &on, &off);
+ read_unlock(&tpt_trig->trig.leddev_list_lock);
+}
+
+char *__ieee80211_create_tpt_led_trigger(struct ieee80211_hw *hw,
+ unsigned int flags,
+ const struct ieee80211_tpt_blink *blink_table,
+ unsigned int blink_table_len)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct tpt_led_trigger *tpt_trig;
+
+ if (WARN_ON(local->tpt_led_trigger))
+ return NULL;
+
+ tpt_trig = kzalloc(sizeof(struct tpt_led_trigger), GFP_KERNEL);
+ if (!tpt_trig)
+ return NULL;
+
+ snprintf(tpt_trig->name, sizeof(tpt_trig->name),
+ "%stpt", wiphy_name(local->hw.wiphy));
+
+ tpt_trig->trig.name = tpt_trig->name;
+
+ tpt_trig->blink_table = blink_table;
+ tpt_trig->blink_table_len = blink_table_len;
+ tpt_trig->want = flags;
+
+ setup_timer(&tpt_trig->timer, tpt_trig_timer, (unsigned long)local);
+
+ local->tpt_led_trigger = tpt_trig;
+
+ return tpt_trig->name;
+}
+EXPORT_SYMBOL(__ieee80211_create_tpt_led_trigger);
+
+static void ieee80211_start_tpt_led_trig(struct ieee80211_local *local)
+{
+ struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
+
+ if (tpt_trig->running)
+ return;
+
+ /* reset traffic */
+ tpt_trig_traffic(local, tpt_trig);
+ tpt_trig->running = true;
+
+ tpt_trig_timer((unsigned long)local);
+ mod_timer(&tpt_trig->timer, round_jiffies(jiffies + HZ));
+}
+
+static void ieee80211_stop_tpt_led_trig(struct ieee80211_local *local)
+{
+ struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
+ struct led_classdev *led_cdev;
+
+ if (!tpt_trig->running)
+ return;
+
+ tpt_trig->running = false;
+ del_timer_sync(&tpt_trig->timer);
+
+ read_lock(&tpt_trig->trig.leddev_list_lock);
+ list_for_each_entry(led_cdev, &tpt_trig->trig.led_cdevs, trig_list)
+ led_brightness_set(led_cdev, LED_OFF);
+ read_unlock(&tpt_trig->trig.leddev_list_lock);
+}
+
+void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local,
+ unsigned int types_on, unsigned int types_off)
+{
+ struct tpt_led_trigger *tpt_trig = local->tpt_led_trigger;
+ bool allowed;
+
+ WARN_ON(types_on & types_off);
+
+ if (!tpt_trig)
+ return;
+
+ tpt_trig->active &= ~types_off;
+ tpt_trig->active |= types_on;
+
+ /*
+ * Regardless of wanted state, we shouldn't blink when
+ * the radio is disabled -- this can happen due to some
+ * code ordering issues with __ieee80211_recalc_idle()
+ * being called before the radio is started.
+ */
+ allowed = tpt_trig->active & IEEE80211_TPT_LEDTRIG_FL_RADIO;
+
+ if (!allowed || !(tpt_trig->active & tpt_trig->want))
+ ieee80211_stop_tpt_led_trig(local);
+ else
+ ieee80211_start_tpt_led_trig(local);
+}
diff --git a/net/mac80211/led.h b/net/mac80211/led.h
index 77b1e1ba6039..e0275d9befa8 100644
--- a/net/mac80211/led.h
+++ b/net/mac80211/led.h
@@ -12,14 +12,17 @@
#include "ieee80211_i.h"
#ifdef CONFIG_MAC80211_LEDS
-extern void ieee80211_led_rx(struct ieee80211_local *local);
-extern void ieee80211_led_tx(struct ieee80211_local *local, int q);
-extern void ieee80211_led_assoc(struct ieee80211_local *local,
- bool associated);
-extern void ieee80211_led_radio(struct ieee80211_local *local,
- bool enabled);
-extern void ieee80211_led_init(struct ieee80211_local *local);
-extern void ieee80211_led_exit(struct ieee80211_local *local);
+void ieee80211_led_rx(struct ieee80211_local *local);
+void ieee80211_led_tx(struct ieee80211_local *local, int q);
+void ieee80211_led_assoc(struct ieee80211_local *local,
+ bool associated);
+void ieee80211_led_radio(struct ieee80211_local *local,
+ bool enabled);
+void ieee80211_led_names(struct ieee80211_local *local);
+void ieee80211_led_init(struct ieee80211_local *local);
+void ieee80211_led_exit(struct ieee80211_local *local);
+void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local,
+ unsigned int types_on, unsigned int types_off);
#else
static inline void ieee80211_led_rx(struct ieee80211_local *local)
{
@@ -35,10 +38,36 @@ static inline void ieee80211_led_radio(struct ieee80211_local *local,
bool enabled)
{
}
+static inline void ieee80211_led_names(struct ieee80211_local *local)
+{
+}
static inline void ieee80211_led_init(struct ieee80211_local *local)
{
}
static inline void ieee80211_led_exit(struct ieee80211_local *local)
{
}
+static inline void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local,
+ unsigned int types_on,
+ unsigned int types_off)
+{
+}
+#endif
+
+static inline void
+ieee80211_tpt_led_trig_tx(struct ieee80211_local *local, __le16 fc, int bytes)
+{
+#ifdef CONFIG_MAC80211_LEDS
+ if (local->tpt_led_trigger && ieee80211_is_data(fc))
+ local->tpt_led_trigger->tx_bytes += bytes;
+#endif
+}
+
+static inline void
+ieee80211_tpt_led_trig_rx(struct ieee80211_local *local, __le16 fc, int bytes)
+{
+#ifdef CONFIG_MAC80211_LEDS
+ if (local->tpt_led_trigger && ieee80211_is_data(fc))
+ local->tpt_led_trigger->rx_bytes += bytes;
#endif
+}
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 973fee9f7d69..a46ff06d7cb8 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -39,6 +39,8 @@ module_param(ieee80211_disable_40mhz_24ghz, bool, 0644);
MODULE_PARM_DESC(ieee80211_disable_40mhz_24ghz,
"Disable 40MHz support in the 2.4GHz band");
+static struct lock_class_key ieee80211_rx_skb_queue_class;
+
void ieee80211_configure_filter(struct ieee80211_local *local)
{
u64 mc;
@@ -484,6 +486,10 @@ ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {
BIT(IEEE80211_STYPE_DEAUTH >> 4) |
BIT(IEEE80211_STYPE_ACTION >> 4),
},
+ [NL80211_IFTYPE_MESH_POINT] = {
+ .tx = 0xffff,
+ .rx = BIT(IEEE80211_STYPE_ACTION >> 4),
+ },
};
struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
@@ -517,10 +523,15 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
wiphy->mgmt_stypes = ieee80211_default_mgmt_stypes;
+ wiphy->privid = mac80211_wiphy_privid;
+
wiphy->flags |= WIPHY_FLAG_NETNS_OK |
WIPHY_FLAG_4ADDR_AP |
- WIPHY_FLAG_4ADDR_STATION;
- wiphy->privid = mac80211_wiphy_privid;
+ WIPHY_FLAG_4ADDR_STATION |
+ WIPHY_FLAG_SUPPORTS_SEPARATE_DEFAULT_KEYS;
+
+ if (!ops->set_key)
+ wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
wiphy->bss_priv_size = sizeof(struct ieee80211_bss);
@@ -560,6 +571,16 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
spin_lock_init(&local->filter_lock);
spin_lock_init(&local->queue_stop_reason_lock);
+ /*
+ * The rx_skb_queue is only accessed from tasklets,
+ * but other SKB queues are used from within IRQ
+ * context. Therefore, this one needs a different
+ * locking class so our direct, non-irq-safe use of
+ * the queue's lock doesn't throw lockdep warnings.
+ */
+ skb_queue_head_init_class(&local->rx_skb_queue,
+ &ieee80211_rx_skb_queue_class);
+
INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work);
ieee80211_work_init(local);
@@ -596,6 +617,10 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
/* init dummy netdev for use w/ NAPI */
init_dummy_netdev(&local->napi_dev);
+ ieee80211_led_names(local);
+
+ ieee80211_hw_roc_setup(local);
+
return local_to_hw(local);
}
EXPORT_SYMBOL(ieee80211_alloc_hw);
@@ -740,6 +765,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
}
}
+ if (!local->ops->remain_on_channel)
+ local->hw.wiphy->max_remain_on_channel_duration = 5000;
+
result = wiphy_register(local->hw.wiphy);
if (result < 0)
goto fail_wiphy_register;
@@ -901,6 +929,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
wiphy_warn(local->hw.wiphy, "skb_queue not empty\n");
skb_queue_purge(&local->skb_queue);
skb_queue_purge(&local->skb_queue_unreliable);
+ skb_queue_purge(&local->rx_skb_queue);
destroy_workqueue(local->workqueue);
wiphy_unregister(local->hw.wiphy);
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 63e1188d5062..ca3af4685b0a 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -124,15 +124,6 @@ void mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata)
ieee80211_mesh_housekeeping_timer((unsigned long) sdata);
}
-void mesh_ids_set_default(struct ieee80211_if_mesh *sta)
-{
- sta->mesh_pp_id = 0; /* HWMP */
- sta->mesh_pm_id = 0; /* Airtime */
- sta->mesh_cc_id = 0; /* Disabled */
- sta->mesh_sp_id = 0; /* Neighbor Offset */
- sta->mesh_auth_id = 0; /* Disabled */
-}
-
int mesh_rmc_init(struct ieee80211_sub_if_data *sdata)
{
int i;
@@ -287,6 +278,13 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
*pos++ |= sdata->u.mesh.accepting_plinks ?
MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00;
*pos++ = 0x00;
+
+ if (sdata->u.mesh.vendor_ie) {
+ int len = sdata->u.mesh.vendor_ie_len;
+ const u8 *data = sdata->u.mesh.vendor_ie;
+ if (skb_tailroom(skb) > len)
+ memcpy(skb_put(skb, len), data, len);
+ }
}
u32 mesh_table_hash(u8 *addr, struct ieee80211_sub_if_data *sdata, struct mesh_table *tbl)
@@ -412,39 +410,33 @@ int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc,
* ieee80211_new_mesh_header - create a new mesh header
* @meshhdr: uninitialized mesh header
* @sdata: mesh interface to be used
- * @addr4: addr4 of the mesh frame (1st in ae header)
- * may be NULL
- * @addr5: addr5 of the mesh frame (1st or 2nd in ae header)
- * may be NULL unless addr6 is present
- * @addr6: addr6 of the mesh frame (2nd or 3rd in ae header)
- * may be NULL unless addr5 is present
+ * @addr4or5: 1st address in the ae header, which may correspond to address 4
+ * (if addr6 is NULL) or address 5 (if addr6 is present). It may
+ * be NULL.
+ * @addr6: 2nd address in the ae header, which corresponds to addr6 of the
+ * mesh frame
*
* Return the header length.
*/
int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr,
- struct ieee80211_sub_if_data *sdata, char *addr4,
- char *addr5, char *addr6)
+ struct ieee80211_sub_if_data *sdata, char *addr4or5,
+ char *addr6)
{
int aelen = 0;
+ BUG_ON(!addr4or5 && addr6);
memset(meshhdr, 0, sizeof(*meshhdr));
meshhdr->ttl = sdata->u.mesh.mshcfg.dot11MeshTTL;
put_unaligned(cpu_to_le32(sdata->u.mesh.mesh_seqnum), &meshhdr->seqnum);
sdata->u.mesh.mesh_seqnum++;
- if (addr4) {
+ if (addr4or5 && !addr6) {
meshhdr->flags |= MESH_FLAGS_AE_A4;
aelen += ETH_ALEN;
- memcpy(meshhdr->eaddr1, addr4, ETH_ALEN);
- }
- if (addr5 && addr6) {
+ memcpy(meshhdr->eaddr1, addr4or5, ETH_ALEN);
+ } else if (addr4or5 && addr6) {
meshhdr->flags |= MESH_FLAGS_AE_A5_A6;
aelen += 2 * ETH_ALEN;
- if (!addr4) {
- memcpy(meshhdr->eaddr1, addr5, ETH_ALEN);
- memcpy(meshhdr->eaddr2, addr6, ETH_ALEN);
- } else {
- memcpy(meshhdr->eaddr2, addr5, ETH_ALEN);
- memcpy(meshhdr->eaddr3, addr6, ETH_ALEN);
- }
+ memcpy(meshhdr->eaddr1, addr4or5, ETH_ALEN);
+ memcpy(meshhdr->eaddr2, addr6, ETH_ALEN);
}
return 6 + aelen;
}
@@ -518,6 +510,9 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
atomic_inc(&local->iff_allmultis);
ieee80211_configure_filter(local);
+ ifmsh->mesh_cc_id = 0; /* Disabled */
+ ifmsh->mesh_sp_id = 0; /* Neighbor Offset */
+ ifmsh->mesh_auth_id = 0; /* Disabled */
set_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags);
ieee80211_mesh_root_setup(ifmsh);
ieee80211_queue_work(&local->hw, &sdata->work);
@@ -688,7 +683,6 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
/* Allocate all mesh structures when creating the first mesh interface. */
if (!mesh_allocated)
ieee80211s_init();
- mesh_ids_set_default(ifmsh);
setup_timer(&ifmsh->mesh_path_timer,
ieee80211_mesh_path_timer,
(unsigned long) sdata);
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index 039d7fa0af74..b99e230fe31c 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -164,17 +164,6 @@ struct mesh_rmc {
};
-/*
- * MESH_CFG_COMP_LEN Includes:
- * - Active path selection protocol ID.
- * - Active path selection metric ID.
- * - Congestion control mode identifier.
- * - Channel precedence.
- * Does not include mesh capabilities, which may vary across nodes in the same
- * mesh
- */
-#define MESH_CFG_CMP_LEN (IEEE80211_MESH_CONFIG_LEN - 2)
-
#define MESH_DEFAULT_BEACON_INTERVAL 1000 /* in 1024 us units */
#define MESH_PATH_EXPIRE (600 * HZ)
@@ -198,8 +187,8 @@ struct mesh_rmc {
int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc,
const u8 *da, const u8 *sa);
int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr,
- struct ieee80211_sub_if_data *sdata, char *addr4,
- char *addr5, char *addr6);
+ struct ieee80211_sub_if_data *sdata, char *addr4or5,
+ char *addr6);
int mesh_rmc_check(u8 *addr, struct ieee80211s_hdr *mesh_hdr,
struct ieee80211_sub_if_data *sdata);
bool mesh_matches_local(struct ieee802_11_elems *ie,
@@ -295,6 +284,11 @@ static inline void mesh_path_activate(struct mesh_path *mpath)
mpath->flags |= MESH_PATH_ACTIVE | MESH_PATH_RESOLVED;
}
+static inline bool mesh_path_sel_is_hwmp(struct ieee80211_sub_if_data *sdata)
+{
+ return sdata->u.mesh.mesh_pp_id == IEEE80211_PATH_PROTOCOL_HWMP;
+}
+
#define for_each_mesh_entry(x, p, node, i) \
for (i = 0; i <= x->hash_mask; i++) \
hlist_for_each_entry_rcu(node, p, &x->hash_buckets[i], list)
@@ -315,6 +309,8 @@ static inline void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata)
{}
static inline void mesh_plink_quiesce(struct sta_info *sta) {}
static inline void mesh_plink_restart(struct sta_info *sta) {}
+static inline bool mesh_path_sel_is_hwmp(struct ieee80211_sub_if_data *sdata)
+{ return false; }
#endif
#endif /* IEEE80211S_H */
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 1c91f0f3c307..44b53931ba5e 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -160,7 +160,8 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
enum plink_frame_type action, u8 *da, __le16 llid, __le16 plid,
__le16 reason) {
struct ieee80211_local *local = sdata->local;
- struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
+ struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400 +
+ sdata->u.mesh.vendor_ie_len);
struct ieee80211_mgmt *mgmt;
bool include_plid = false;
static const u8 meshpeeringproto[] = { 0x00, 0x0F, 0xAC, 0x2A };
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index 4b564091e51d..b4e52676f3fb 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -14,6 +14,7 @@
*/
#include <net/mac80211.h>
#include "ieee80211_i.h"
+#include "driver-trace.h"
/*
* inform AP that we will go to sleep so that it will buffer the frames
@@ -190,3 +191,87 @@ void ieee80211_offchannel_return(struct ieee80211_local *local,
}
mutex_unlock(&local->iflist_mtx);
}
+
+static void ieee80211_hw_roc_start(struct work_struct *work)
+{
+ struct ieee80211_local *local =
+ container_of(work, struct ieee80211_local, hw_roc_start);
+ struct ieee80211_sub_if_data *sdata;
+
+ mutex_lock(&local->mtx);
+
+ if (!local->hw_roc_channel) {
+ mutex_unlock(&local->mtx);
+ return;
+ }
+
+ ieee80211_recalc_idle(local);
+
+ if (local->hw_roc_skb) {
+ sdata = IEEE80211_DEV_TO_SUB_IF(local->hw_roc_dev);
+ ieee80211_tx_skb(sdata, local->hw_roc_skb);
+ local->hw_roc_skb = NULL;
+ } else {
+ cfg80211_ready_on_channel(local->hw_roc_dev,
+ local->hw_roc_cookie,
+ local->hw_roc_channel,
+ local->hw_roc_channel_type,
+ local->hw_roc_duration,
+ GFP_KERNEL);
+ }
+
+ mutex_unlock(&local->mtx);
+}
+
+void ieee80211_ready_on_channel(struct ieee80211_hw *hw)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+
+ trace_api_ready_on_channel(local);
+
+ ieee80211_queue_work(hw, &local->hw_roc_start);
+}
+EXPORT_SYMBOL_GPL(ieee80211_ready_on_channel);
+
+static void ieee80211_hw_roc_done(struct work_struct *work)
+{
+ struct ieee80211_local *local =
+ container_of(work, struct ieee80211_local, hw_roc_done);
+
+ mutex_lock(&local->mtx);
+
+ if (!local->hw_roc_channel) {
+ mutex_unlock(&local->mtx);
+ return;
+ }
+
+ if (!local->hw_roc_for_tx)
+ cfg80211_remain_on_channel_expired(local->hw_roc_dev,
+ local->hw_roc_cookie,
+ local->hw_roc_channel,
+ local->hw_roc_channel_type,
+ GFP_KERNEL);
+
+ local->hw_roc_channel = NULL;
+ local->hw_roc_cookie = 0;
+
+ ieee80211_recalc_idle(local);
+
+ mutex_unlock(&local->mtx);
+}
+
+void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+
+ trace_api_remain_on_channel_expired(local);
+
+ ieee80211_queue_work(hw, &local->hw_roc_done);
+}
+EXPORT_SYMBOL_GPL(ieee80211_remain_on_channel_expired);
+
+void ieee80211_hw_roc_setup(struct ieee80211_local *local)
+{
+ INIT_WORK(&local->hw_roc_start, ieee80211_hw_roc_start);
+ INIT_WORK(&local->hw_roc_done, ieee80211_hw_roc_done);
+}
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
index 4ad7a362fcc1..165a4518bb48 100644
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -374,7 +374,7 @@ minstrel_aggr_check(struct minstrel_priv *mp, struct ieee80211_sta *pubsta, stru
if (skb_get_queue_mapping(skb) == IEEE80211_AC_VO)
return;
- ieee80211_start_tx_ba_session(pubsta, tid);
+ ieee80211_start_tx_ba_session(pubsta, tid, 5000);
}
static void
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 2fe8f5f86499..a6701ed87f0d 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -533,10 +533,11 @@ static inline u16 seq_sub(u16 sq1, u16 sq2)
static void ieee80211_release_reorder_frame(struct ieee80211_hw *hw,
struct tid_ampdu_rx *tid_agg_rx,
- int index,
- struct sk_buff_head *frames)
+ int index)
{
+ struct ieee80211_local *local = hw_to_local(hw);
struct sk_buff *skb = tid_agg_rx->reorder_buf[index];
+ struct ieee80211_rx_status *status;
lockdep_assert_held(&tid_agg_rx->reorder_lock);
@@ -546,7 +547,9 @@ static void ieee80211_release_reorder_frame(struct ieee80211_hw *hw,
/* release the frame from the reorder ring buffer */
tid_agg_rx->stored_mpdu_num--;
tid_agg_rx->reorder_buf[index] = NULL;
- __skb_queue_tail(frames, skb);
+ status = IEEE80211_SKB_RXCB(skb);
+ status->rx_flags |= IEEE80211_RX_DEFERRED_RELEASE;
+ skb_queue_tail(&local->rx_skb_queue, skb);
no_frame:
tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num);
@@ -554,8 +557,7 @@ no_frame:
static void ieee80211_release_reorder_frames(struct ieee80211_hw *hw,
struct tid_ampdu_rx *tid_agg_rx,
- u16 head_seq_num,
- struct sk_buff_head *frames)
+ u16 head_seq_num)
{
int index;
@@ -564,7 +566,7 @@ static void ieee80211_release_reorder_frames(struct ieee80211_hw *hw,
while (seq_less(tid_agg_rx->head_seq_num, head_seq_num)) {
index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) %
tid_agg_rx->buf_size;
- ieee80211_release_reorder_frame(hw, tid_agg_rx, index, frames);
+ ieee80211_release_reorder_frame(hw, tid_agg_rx, index);
}
}
@@ -580,8 +582,7 @@ static void ieee80211_release_reorder_frames(struct ieee80211_hw *hw,
#define HT_RX_REORDER_BUF_TIMEOUT (HZ / 10)
static void ieee80211_sta_reorder_release(struct ieee80211_hw *hw,
- struct tid_ampdu_rx *tid_agg_rx,
- struct sk_buff_head *frames)
+ struct tid_ampdu_rx *tid_agg_rx)
{
int index, j;
@@ -612,8 +613,7 @@ static void ieee80211_sta_reorder_release(struct ieee80211_hw *hw,
wiphy_debug(hw->wiphy,
"release an RX reorder frame due to timeout on earlier frames\n");
#endif
- ieee80211_release_reorder_frame(hw, tid_agg_rx,
- j, frames);
+ ieee80211_release_reorder_frame(hw, tid_agg_rx, j);
/*
* Increment the head seq# also for the skipped slots.
@@ -623,31 +623,11 @@ static void ieee80211_sta_reorder_release(struct ieee80211_hw *hw,
skipped = 0;
}
} else while (tid_agg_rx->reorder_buf[index]) {
- ieee80211_release_reorder_frame(hw, tid_agg_rx, index, frames);
+ ieee80211_release_reorder_frame(hw, tid_agg_rx, index);
index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) %
tid_agg_rx->buf_size;
}
- /*
- * Disable the reorder release timer for now.
- *
- * The current implementation lacks a proper locking scheme
- * which would protect vital statistic and debug counters
- * from being updated by two different but concurrent BHs.
- *
- * More information about the topic is available from:
- * - thread: http://marc.info/?t=128635927000001
- *
- * What was wrong:
- * => http://marc.info/?l=linux-wireless&m=128636170811964
- * "Basically the thing is that until your patch, the data
- * in the struct didn't actually need locking because it
- * was accessed by the RX path only which is not concurrent."
- *
- * List of what needs to be fixed:
- * => http://marc.info/?l=linux-wireless&m=128656352920957
- *
-
if (tid_agg_rx->stored_mpdu_num) {
j = index = seq_sub(tid_agg_rx->head_seq_num,
tid_agg_rx->ssn) % tid_agg_rx->buf_size;
@@ -666,10 +646,6 @@ static void ieee80211_sta_reorder_release(struct ieee80211_hw *hw,
} else {
del_timer(&tid_agg_rx->reorder_timer);
}
- */
-
-set_release_timer:
- return;
}
/*
@@ -679,8 +655,7 @@ set_release_timer:
*/
static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
struct tid_ampdu_rx *tid_agg_rx,
- struct sk_buff *skb,
- struct sk_buff_head *frames)
+ struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
u16 sc = le16_to_cpu(hdr->seq_ctrl);
@@ -707,8 +682,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
if (!seq_less(mpdu_seq_num, head_seq_num + buf_size)) {
head_seq_num = seq_inc(seq_sub(mpdu_seq_num, buf_size));
/* release stored frames up to new head to stack */
- ieee80211_release_reorder_frames(hw, tid_agg_rx, head_seq_num,
- frames);
+ ieee80211_release_reorder_frames(hw, tid_agg_rx, head_seq_num);
}
/* Now the new frame is always in the range of the reordering buffer */
@@ -736,7 +710,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
tid_agg_rx->reorder_buf[index] = skb;
tid_agg_rx->reorder_time[index] = jiffies;
tid_agg_rx->stored_mpdu_num++;
- ieee80211_sta_reorder_release(hw, tid_agg_rx, frames);
+ ieee80211_sta_reorder_release(hw, tid_agg_rx);
out:
spin_unlock(&tid_agg_rx->reorder_lock);
@@ -747,8 +721,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
* Reorder MPDUs from A-MPDUs, keeping them on a buffer. Returns
* true if the MPDU was buffered, false if it should be processed.
*/
-static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
- struct sk_buff_head *frames)
+static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx)
{
struct sk_buff *skb = rx->skb;
struct ieee80211_local *local = rx->local;
@@ -803,11 +776,11 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
* sure that we cannot get to it any more before doing
* anything with it.
*/
- if (ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb, frames))
+ if (ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb))
return;
dont_reorder:
- __skb_queue_tail(frames, skb);
+ skb_queue_tail(&local->rx_skb_queue, skb);
}
static ieee80211_rx_result debug_noinline
@@ -955,12 +928,31 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
* have been expected.
*/
struct ieee80211_key *key = NULL;
+ struct ieee80211_sub_if_data *sdata = rx->sdata;
+ int i;
+
if (ieee80211_is_mgmt(fc) &&
is_multicast_ether_addr(hdr->addr1) &&
(key = rcu_dereference(rx->sdata->default_mgmt_key)))
rx->key = key;
- else if ((key = rcu_dereference(rx->sdata->default_key)))
- rx->key = key;
+ else {
+ if (rx->sta) {
+ for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
+ key = rcu_dereference(rx->sta->gtk[i]);
+ if (key)
+ break;
+ }
+ }
+ if (!key) {
+ for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
+ key = rcu_dereference(sdata->keys[i]);
+ if (key)
+ break;
+ }
+ }
+ if (key)
+ rx->key = key;
+ }
return RX_CONTINUE;
} else {
u8 keyid;
@@ -1170,6 +1162,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
* exchange sequence.
*/
if (!ieee80211_has_morefrags(hdr->frame_control) &&
+ !(status->rx_flags & IEEE80211_RX_DEFERRED_RELEASE) &&
(rx->sdata->vif.type == NL80211_IFTYPE_AP ||
rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) {
if (test_sta_flags(sta, WLAN_STA_PS_STA)) {
@@ -1521,12 +1514,30 @@ ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx)
if (rx->sta && test_sta_flags(rx->sta, WLAN_STA_MFP)) {
if (unlikely(!ieee80211_has_protected(fc) &&
ieee80211_is_unicast_robust_mgmt_frame(rx->skb) &&
- rx->key))
+ rx->key)) {
+ if (ieee80211_is_deauth(fc))
+ cfg80211_send_unprot_deauth(rx->sdata->dev,
+ rx->skb->data,
+ rx->skb->len);
+ else if (ieee80211_is_disassoc(fc))
+ cfg80211_send_unprot_disassoc(rx->sdata->dev,
+ rx->skb->data,
+ rx->skb->len);
return -EACCES;
+ }
/* BIP does not use Protected field, so need to check MMIE */
if (unlikely(ieee80211_is_multicast_robust_mgmt_frame(rx->skb) &&
- ieee80211_get_mmie_keyidx(rx->skb) < 0))
+ ieee80211_get_mmie_keyidx(rx->skb) < 0)) {
+ if (ieee80211_is_deauth(fc))
+ cfg80211_send_unprot_deauth(rx->sdata->dev,
+ rx->skb->data,
+ rx->skb->len);
+ else if (ieee80211_is_disassoc(fc))
+ cfg80211_send_unprot_disassoc(rx->sdata->dev,
+ rx->skb->data,
+ rx->skb->len);
return -EACCES;
+ }
/*
* When using MFP, Action frames are not allowed prior to
* having configured keys.
@@ -1797,6 +1808,8 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
if (!fwd_skb && net_ratelimit())
printk(KERN_DEBUG "%s: failed to clone mesh frame\n",
sdata->name);
+ if (!fwd_skb)
+ goto out;
fwd_hdr = (struct ieee80211_hdr *) fwd_skb->data;
memcpy(fwd_hdr->addr2, sdata->vif.addr, ETH_ALEN);
@@ -1834,6 +1847,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
}
}
+ out:
if (is_multicast_ether_addr(hdr->addr1) ||
sdata->dev->flags & IFF_PROMISC)
return RX_CONTINUE;
@@ -1890,7 +1904,7 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
}
static ieee80211_rx_result debug_noinline
-ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames)
+ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx)
{
struct ieee80211_local *local = rx->local;
struct ieee80211_hw *hw = &local->hw;
@@ -1930,8 +1944,7 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames)
spin_lock(&tid_agg_rx->reorder_lock);
/* release stored frames up to start of BAR */
- ieee80211_release_reorder_frames(hw, tid_agg_rx, start_seq_num,
- frames);
+ ieee80211_release_reorder_frames(hw, tid_agg_rx, start_seq_num);
spin_unlock(&tid_agg_rx->reorder_lock);
kfree_skb(skb);
@@ -2124,10 +2137,13 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
}
break;
case WLAN_CATEGORY_MESH_PLINK:
- case WLAN_CATEGORY_MESH_PATH_SEL:
if (!ieee80211_vif_is_mesh(&sdata->vif))
break;
goto queue;
+ case WLAN_CATEGORY_MESH_PATH_SEL:
+ if (!mesh_path_sel_is_hwmp(sdata))
+ break;
+ goto queue;
}
return RX_CONTINUE;
@@ -2445,8 +2461,7 @@ static void ieee80211_rx_handlers_result(struct ieee80211_rx_data *rx,
}
}
-static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx,
- struct sk_buff_head *frames)
+static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx)
{
ieee80211_rx_result res = RX_DROP_MONITOR;
struct sk_buff *skb;
@@ -2458,7 +2473,15 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx,
goto rxh_next; \
} while (0);
- while ((skb = __skb_dequeue(frames))) {
+ spin_lock(&rx->local->rx_skb_queue.lock);
+ if (rx->local->running_rx_handler)
+ goto unlock;
+
+ rx->local->running_rx_handler = true;
+
+ while ((skb = __skb_dequeue(&rx->local->rx_skb_queue))) {
+ spin_unlock(&rx->local->rx_skb_queue.lock);
+
/*
* all the other fields are valid across frames
* that belong to an aMPDU since they are on the
@@ -2481,12 +2504,7 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx,
CALL_RXH(ieee80211_rx_h_mesh_fwding);
#endif
CALL_RXH(ieee80211_rx_h_data)
-
- /* special treatment -- needs the queue */
- res = ieee80211_rx_h_ctrl(rx, frames);
- if (res != RX_CONTINUE)
- goto rxh_next;
-
+ CALL_RXH(ieee80211_rx_h_ctrl);
CALL_RXH(ieee80211_rx_h_mgmt_check)
CALL_RXH(ieee80211_rx_h_action)
CALL_RXH(ieee80211_rx_h_userspace_mgmt)
@@ -2495,18 +2513,20 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx,
rxh_next:
ieee80211_rx_handlers_result(rx, res);
-
+ spin_lock(&rx->local->rx_skb_queue.lock);
#undef CALL_RXH
}
+
+ rx->local->running_rx_handler = false;
+
+ unlock:
+ spin_unlock(&rx->local->rx_skb_queue.lock);
}
static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx)
{
- struct sk_buff_head reorder_release;
ieee80211_rx_result res = RX_DROP_MONITOR;
- __skb_queue_head_init(&reorder_release);
-
#define CALL_RXH(rxh) \
do { \
res = rxh(rx); \
@@ -2517,9 +2537,9 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx)
CALL_RXH(ieee80211_rx_h_passive_scan)
CALL_RXH(ieee80211_rx_h_check)
- ieee80211_rx_reorder_ampdu(rx, &reorder_release);
+ ieee80211_rx_reorder_ampdu(rx);
- ieee80211_rx_handlers(rx, &reorder_release);
+ ieee80211_rx_handlers(rx);
return;
rxh_next:
@@ -2534,7 +2554,6 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx)
*/
void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)
{
- struct sk_buff_head frames;
struct ieee80211_rx_data rx = {
.sta = sta,
.sdata = sta->sdata,
@@ -2547,13 +2566,11 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)
if (!tid_agg_rx)
return;
- __skb_queue_head_init(&frames);
-
spin_lock(&tid_agg_rx->reorder_lock);
- ieee80211_sta_reorder_release(&sta->local->hw, tid_agg_rx, &frames);
+ ieee80211_sta_reorder_release(&sta->local->hw, tid_agg_rx);
spin_unlock(&tid_agg_rx->reorder_lock);
- ieee80211_rx_handlers(&rx, &frames);
+ ieee80211_rx_handlers(&rx);
}
/* main receive path */
@@ -2888,6 +2905,9 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
return;
}
+ ieee80211_tpt_led_trig_rx(local,
+ ((struct ieee80211_hdr *)skb->data)->frame_control,
+ skb->len);
__ieee80211_rx_handle_packet(hw, skb);
rcu_read_unlock();
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index fdca52cf88de..bbdd2a86a94b 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -78,6 +78,7 @@ enum ieee80211_sta_info_flags {
* @addba_resp_timer: timer for peer's response to addba request
* @pending: pending frames queue -- use sta's spinlock to protect
* @dialog_token: dialog token for aggregation session
+ * @timeout: session timeout value to be filled in ADDBA requests
* @state: session state (see above)
* @stop_initiator: initiator of a session stop
* @tx_stop: TX DelBA frame when stopping
@@ -96,6 +97,7 @@ struct tid_ampdu_tx {
struct timer_list addba_resp_timer;
struct sk_buff_head pending;
unsigned long state;
+ u16 timeout;
u8 dialog_token;
u8 stop_initiator;
bool tx_stop;
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 0ee56bb0ea7e..b64b42bc774b 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -539,7 +539,11 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
ieee80211_is_robust_mgmt_frame(hdr) &&
(key = rcu_dereference(tx->sdata->default_mgmt_key)))
tx->key = key;
- else if ((key = rcu_dereference(tx->sdata->default_key)))
+ else if (is_multicast_ether_addr(hdr->addr1) &&
+ (key = rcu_dereference(tx->sdata->default_multicast_key)))
+ tx->key = key;
+ else if (!is_multicast_ether_addr(hdr->addr1) &&
+ (key = rcu_dereference(tx->sdata->default_unicast_key)))
tx->key = key;
else if (tx->sdata->drop_unencrypted &&
(tx->skb->protocol != tx->sdata->control_port_protocol) &&
@@ -1293,6 +1297,7 @@ static int __ieee80211_tx(struct ieee80211_local *local,
while (skb) {
int q = skb_get_queue_mapping(skb);
+ __le16 fc;
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
ret = IEEE80211_TX_OK;
@@ -1335,6 +1340,7 @@ static int __ieee80211_tx(struct ieee80211_local *local,
else
info->control.sta = NULL;
+ fc = ((struct ieee80211_hdr *)skb->data)->frame_control;
ret = drv_tx(local, skb);
if (WARN_ON(ret != NETDEV_TX_OK && skb->len != len)) {
dev_kfree_skb(skb);
@@ -1345,6 +1351,7 @@ static int __ieee80211_tx(struct ieee80211_local *local,
return IEEE80211_TX_AGAIN;
}
+ ieee80211_tpt_led_trig_tx(local, fc, len);
*skbp = skb = next;
ieee80211_led_tx(local, 1);
fragm = true;
@@ -1542,8 +1549,10 @@ static int ieee80211_skb_resize(struct ieee80211_local *local,
if (skb_header_cloned(skb))
I802_DEBUG_INC(local->tx_expand_skb_head_cloned);
- else
+ else if (head_need || tail_need)
I802_DEBUG_INC(local->tx_expand_skb_head);
+ else
+ return 0;
if (pskb_expand_head(skb, head_need, tail_need, GFP_ATOMIC)) {
wiphy_debug(local->hw.wiphy,
@@ -1735,12 +1744,13 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_tx_info *info;
int ret = NETDEV_TX_BUSY, head_need;
u16 ethertype, hdrlen, meshhdrlen = 0;
__le16 fc;
struct ieee80211_hdr hdr;
struct ieee80211s_hdr mesh_hdr __maybe_unused;
+ struct mesh_path *mppath = NULL;
const u8 *encaps_data;
int encaps_len, skip_header_bytes;
int nh_pos, h_pos;
@@ -1801,16 +1811,23 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
ret = NETDEV_TX_OK;
goto fail;
}
+ if (!is_multicast_ether_addr(skb->data))
+ mppath = mpp_path_lookup(skb->data, sdata);
+ /*
+ * Do not use address extension, if it is a packet from
+ * the same interface and the destination is not being
+ * proxied by any other mest point.
+ */
if (compare_ether_addr(sdata->vif.addr,
- skb->data + ETH_ALEN) == 0) {
+ skb->data + ETH_ALEN) == 0 &&
+ (!mppath || !compare_ether_addr(mppath->mpp, skb->data))) {
hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc,
skb->data, skb->data + ETH_ALEN);
meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr,
- sdata, NULL, NULL, NULL);
+ sdata, NULL, NULL);
} else {
/* packet from other interface */
- struct mesh_path *mppath;
int is_mesh_mcast = 1;
const u8 *mesh_da;
@@ -1821,8 +1838,6 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
else {
static const u8 bcast[ETH_ALEN] =
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
-
- mppath = mpp_path_lookup(skb->data, sdata);
if (mppath) {
/* RA TA mDA mSA AE:DA SA */
mesh_da = mppath->mpp;
@@ -1840,13 +1855,11 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
ieee80211_new_mesh_header(&mesh_hdr,
sdata,
skb->data + ETH_ALEN,
- NULL,
NULL);
else
meshhdrlen =
ieee80211_new_mesh_header(&mesh_hdr,
sdata,
- NULL,
skb->data,
skb->data + ETH_ALEN);
@@ -1930,7 +1943,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
*/
if (skb_shared(skb)) {
tmp_skb = skb;
- skb = skb_copy(skb, GFP_ATOMIC);
+ skb = skb_clone(skb, GFP_ATOMIC);
kfree_skb(tmp_skb);
if (!skb) {
@@ -2026,6 +2039,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
skb_set_network_header(skb, nh_pos);
skb_set_transport_header(skb, h_pos);
+ info = IEEE80211_SKB_CB(skb);
memset(info, 0, sizeof(*info));
dev->trans_start = jiffies;
@@ -2216,6 +2230,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
sdata = vif_to_sdata(vif);
+ if (!ieee80211_sdata_running(sdata))
+ goto out;
+
if (tim_offset)
*tim_offset = 0;
if (tim_length)
@@ -2286,7 +2303,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
u8 *pos;
/* headroom, head length, tail length and maximum TIM length */
- skb = dev_alloc_skb(local->tx_headroom + 400);
+ skb = dev_alloc_skb(local->tx_headroom + 400 +
+ sdata->u.mesh.vendor_ie_len);
if (!skb)
goto out;
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index e497476174ce..cf68700abffa 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1116,6 +1116,7 @@ u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
void ieee80211_stop_device(struct ieee80211_local *local)
{
ieee80211_led_radio(local, false);
+ ieee80211_mod_tpt_led_trig(local, 0, IEEE80211_TPT_LEDTRIG_FL_RADIO);
cancel_work_sync(&local->reconfig_filter);
@@ -1150,6 +1151,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)
}
ieee80211_led_radio(local, true);
+ ieee80211_mod_tpt_led_trig(local,
+ IEEE80211_TPT_LEDTRIG_FL_RADIO, 0);
}
/* add interfaces */
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index 58e75bbc1f91..28bc084dbfb9 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -59,26 +59,22 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_local *local = sdata->local;
struct sta_info *sta = NULL;
- u32 sta_flags = 0;
const u8 *ra = NULL;
bool qos = false;
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]);
+ return min_t(u16, local->hw.queues - 1, IEEE80211_AC_BE);
}
rcu_read_lock();
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP_VLAN:
- rcu_read_lock();
sta = rcu_dereference(sdata->u.vlan.sta);
- if (sta)
- sta_flags = get_sta_flags(sta);
- rcu_read_unlock();
- if (sta)
+ if (sta) {
+ qos = get_sta_flags(sta) & WLAN_STA_WME;
break;
+ }
case NL80211_IFTYPE_AP:
ra = skb->data;
break;
@@ -107,17 +103,13 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
if (!sta && ra && !is_multicast_ether_addr(ra)) {
sta = sta_info_get(sdata, ra);
if (sta)
- sta_flags = get_sta_flags(sta);
+ qos = get_sta_flags(sta) & WLAN_STA_WME;
}
-
- 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];
+ return IEEE80211_AC_BE;
}
/* use the data classifier to determine what 802.1d tag the