summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2011-07-23 02:01:44 +0200
committerDavid S. Miller <davem@davemloft.net>2011-07-23 02:01:44 +0200
commitf9035cd498486d5a82ad8ae9bcfdb91b3e57ec9d (patch)
treef90d0fc8abc1edf7fd161252704bb5675f3460d0 /net
parentnet: allow netif_carrier to be called safely from IRQ (diff)
parentMerge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linvil... (diff)
downloadlinux-f9035cd498486d5a82ad8ae9bcfdb91b3e57ec9d.tar.xz
linux-f9035cd498486d5a82ad8ae9bcfdb91b3e57ec9d.zip
Merge branch 'for-davem' of ssh://master.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/agg-rx.c10
-rw-r--r--net/mac80211/cfg.c4
-rw-r--r--net/mac80211/driver-ops.h31
-rw-r--r--net/mac80211/driver-trace.h43
-rw-r--r--net/mac80211/ieee80211_i.h7
-rw-r--r--net/mac80211/key.c2
-rw-r--r--net/mac80211/mlme.c30
-rw-r--r--net/mac80211/pm.c3
-rw-r--r--net/mac80211/scan.c6
-rw-r--r--net/mac80211/tkip.c11
-rw-r--r--net/mac80211/util.c71
-rw-r--r--net/mac80211/work.c28
-rw-r--r--net/wireless/core.c7
-rw-r--r--net/wireless/core.h4
-rw-r--r--net/wireless/nl80211.c109
-rw-r--r--net/wireless/scan.c4
-rw-r--r--net/wireless/util.c38
17 files changed, 340 insertions, 68 deletions
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index ebadb9ac9a7e..fd1aaf2a4a6c 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -104,14 +104,22 @@ void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap,
const u8 *addr)
{
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
- struct sta_info *sta = sta_info_get(sdata, addr);
+ struct sta_info *sta;
int i;
+ rcu_read_lock();
+ sta = sta_info_get(sdata, addr);
+ if (!sta) {
+ rcu_read_unlock();
+ return;
+ }
+
for (i = 0; i < STA_TID_NUM; i++)
if (ba_rx_bitmap & BIT(i))
set_bit(i, sta->ampdu_mlme.tid_rx_stop_requested);
ieee80211_queue_work(&sta->local->hw, &sta->ampdu_mlme.work);
+ rcu_read_unlock();
}
EXPORT_SYMBOL(ieee80211_stop_rx_ba_session);
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index bfc36e904764..3d1b091d9b2e 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1255,6 +1255,10 @@ static int ieee80211_set_txq_params(struct wiphy *wiphy,
*/
p.uapsd = false;
+ if (params->queue >= local->hw.queues)
+ return -EINVAL;
+
+ local->tx_conf[params->queue] = p;
if (drv_conf_tx(local, params->queue, &p)) {
wiphy_debug(local->hw.wiphy,
"failed to set TX queue parameters for queue %d\n",
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index b2d6bba44054..1425380983f7 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -130,6 +130,37 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local,
trace_drv_return_void(local);
}
+static inline int drv_tx_sync(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ const u8 *bssid,
+ enum ieee80211_tx_sync_type type)
+{
+ int ret = 0;
+
+ might_sleep();
+
+ trace_drv_tx_sync(local, sdata, bssid, type);
+ if (local->ops->tx_sync)
+ ret = local->ops->tx_sync(&local->hw, &sdata->vif,
+ bssid, type);
+ trace_drv_return_int(local, ret);
+ return ret;
+}
+
+static inline void drv_finish_tx_sync(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ const u8 *bssid,
+ enum ieee80211_tx_sync_type type)
+{
+ might_sleep();
+
+ trace_drv_finish_tx_sync(local, sdata, bssid, type);
+ if (local->ops->finish_tx_sync)
+ local->ops->finish_tx_sync(&local->hw, &sdata->vif,
+ bssid, type);
+ trace_drv_return_void(local);
+}
+
static inline u64 drv_prepare_multicast(struct ieee80211_local *local,
struct netdev_hw_addr_list *mc_list)
{
diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h
index 4470f6e8b845..f47b00dc7afd 100644
--- a/net/mac80211/driver-trace.h
+++ b/net/mac80211/driver-trace.h
@@ -319,6 +319,49 @@ TRACE_EVENT(drv_bss_info_changed,
)
);
+DECLARE_EVENT_CLASS(tx_sync_evt,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ const u8 *bssid,
+ enum ieee80211_tx_sync_type type),
+ TP_ARGS(local, sdata, bssid, type),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ __array(char, bssid, ETH_ALEN)
+ __field(u32, sync_type)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ memcpy(__entry->bssid, bssid, ETH_ALEN);
+ __entry->sync_type = type;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT " bssid:%pM type:%d",
+ LOCAL_PR_ARG, VIF_PR_ARG, __entry->bssid, __entry->sync_type
+ )
+);
+
+DEFINE_EVENT(tx_sync_evt, drv_tx_sync,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ const u8 *bssid,
+ enum ieee80211_tx_sync_type type),
+ TP_ARGS(local, sdata, bssid, type)
+);
+
+DEFINE_EVENT(tx_sync_evt, drv_finish_tx_sync,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ const u8 *bssid,
+ enum ieee80211_tx_sync_type type),
+ TP_ARGS(local, sdata, bssid, type)
+);
+
TRACE_EVENT(drv_prepare_multicast,
TP_PROTO(struct ieee80211_local *local, int mc_count),
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index dda0d1ab34f3..400c09bea639 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -323,6 +323,7 @@ struct ieee80211_work {
u8 key[WLAN_KEY_LEN_WEP104];
u8 key_len, key_idx;
bool privacy;
+ bool synced;
} probe_auth;
struct {
struct cfg80211_bss *bss;
@@ -336,6 +337,7 @@ struct ieee80211_work {
u8 ssid_len;
u8 supp_rates_len;
bool wmm_used, use_11n, uapsd_used;
+ bool synced;
} assoc;
struct {
u32 duration;
@@ -746,6 +748,7 @@ struct ieee80211_local {
struct workqueue_struct *workqueue;
unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES];
+ struct ieee80211_tx_queue_params tx_conf[IEEE80211_MAX_QUEUES];
/* also used to protect ampdu_ac_queue and amdpu_ac_stop_refcnt */
spinlock_t queue_stop_reason_lock;
@@ -1376,14 +1379,14 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
enum ieee80211_band band, u32 rate_mask,
u8 channel);
struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
- u8 *dst,
+ u8 *dst, u32 ratemask,
const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len,
bool directed);
void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len,
- bool directed);
+ u32 ratemask, bool directed);
void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
const size_t supp_rates_len,
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 739bee13e813..5150c6d11b57 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -278,7 +278,7 @@ static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
bool defunikey, defmultikey, defmgmtkey;
if (new)
- list_add(&new->list, &sdata->key_list);
+ list_add_tail(&new->list, &sdata->key_list);
if (sta && pairwise) {
rcu_assign_pointer(sta->ptk, new);
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index c99237cd4b98..d6470c7fd6ce 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -917,6 +917,7 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
params.aifs, params.cw_min, params.cw_max,
params.txop, params.uapsd);
#endif
+ local->tx_conf[queue] = params;
if (drv_conf_tx(local, queue, &params))
wiphy_debug(local->hw.wiphy,
"failed to set TX queue parameters for queue %d\n",
@@ -1219,7 +1220,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
} else {
ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid[1], NULL, 0,
- true);
+ (u32) -1, true);
}
ifmgd->probe_send_count++;
@@ -1304,7 +1305,8 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
skb = ieee80211_build_probe_req(sdata, ifmgd->associated->bssid,
- ssid + 2, ssid[1], NULL, 0, true);
+ (u32) -1, ssid + 2, ssid[1],
+ NULL, 0, true);
return skb;
}
@@ -2333,14 +2335,16 @@ static enum work_done_result
ieee80211_probe_auth_done(struct ieee80211_work *wk,
struct sk_buff *skb)
{
+ struct ieee80211_local *local = wk->sdata->local;
+
if (!skb) {
cfg80211_send_auth_timeout(wk->sdata->dev, wk->filter_ta);
- return WORK_DONE_DESTROY;
+ goto destroy;
}
if (wk->type == IEEE80211_WORK_AUTH) {
cfg80211_send_rx_auth(wk->sdata->dev, skb->data, skb->len);
- return WORK_DONE_DESTROY;
+ goto destroy;
}
mutex_lock(&wk->sdata->u.mgd.mtx);
@@ -2350,6 +2354,12 @@ ieee80211_probe_auth_done(struct ieee80211_work *wk,
wk->type = IEEE80211_WORK_AUTH;
wk->probe_auth.tries = 0;
return WORK_DONE_REQUEUE;
+ destroy:
+ if (wk->probe_auth.synced)
+ drv_finish_tx_sync(local, wk->sdata, wk->filter_ta,
+ IEEE80211_TX_SYNC_AUTH);
+
+ return WORK_DONE_DESTROY;
}
int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
@@ -2422,6 +2432,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk,
struct sk_buff *skb)
{
+ struct ieee80211_local *local = wk->sdata->local;
struct ieee80211_mgmt *mgmt;
struct ieee80211_rx_status *rx_status;
struct ieee802_11_elems elems;
@@ -2429,7 +2440,7 @@ static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk,
if (!skb) {
cfg80211_send_assoc_timeout(wk->sdata->dev, wk->filter_ta);
- return WORK_DONE_DESTROY;
+ goto destroy;
}
if (wk->type == IEEE80211_WORK_ASSOC_BEACON_WAIT) {
@@ -2449,6 +2460,10 @@ static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk,
status = le16_to_cpu(mgmt->u.assoc_resp.status_code);
if (status == WLAN_STATUS_SUCCESS) {
+ if (wk->assoc.synced)
+ drv_finish_tx_sync(local, wk->sdata, wk->filter_ta,
+ IEEE80211_TX_SYNC_ASSOC);
+
mutex_lock(&wk->sdata->u.mgd.mtx);
if (!ieee80211_assoc_success(wk, mgmt, skb->len)) {
mutex_unlock(&wk->sdata->u.mgd.mtx);
@@ -2462,6 +2477,11 @@ static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk,
}
cfg80211_send_rx_assoc(wk->sdata->dev, skb->data, skb->len);
+ destroy:
+ if (wk->assoc.synced)
+ drv_finish_tx_sync(local, wk->sdata, wk->filter_ta,
+ IEEE80211_TX_SYNC_ASSOC);
+
return WORK_DONE_DESTROY;
}
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index f87e993e713b..6326d3439861 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -34,6 +34,9 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
+ if (!local->open_count)
+ goto suspend;
+
ieee80211_scan_cancel(local);
if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 08a45ac3d6f8..6f09eca01112 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -228,7 +228,6 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
{
struct cfg80211_scan_request *req = local->scan_req;
- struct ieee80211_sub_if_data *sdata = local->scan_sdata;
enum ieee80211_band band;
int i, ielen, n_chans;
@@ -253,7 +252,7 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie,
req->ie, req->ie_len, band,
- sdata->rc_rateidx_mask[band], 0);
+ req->rates[band], 0);
local->hw_scan_req->ie_len = ielen;
return true;
@@ -653,6 +652,7 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
{
int i;
struct ieee80211_sub_if_data *sdata = local->scan_sdata;
+ enum ieee80211_band band = local->hw.conf.channel->band;
for (i = 0; i < local->scan_req->n_ssids; i++)
ieee80211_send_probe_req(
@@ -660,7 +660,7 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
local->scan_req->ssids[i].ssid,
local->scan_req->ssids[i].ssid_len,
local->scan_req->ie, local->scan_req->ie_len,
- false);
+ local->scan_req->rates[band], false);
/*
* After sending probe requests, wait for probe responses
diff --git a/net/mac80211/tkip.c b/net/mac80211/tkip.c
index cc79e697cdb2..f49d00a4c7fd 100644
--- a/net/mac80211/tkip.c
+++ b/net/mac80211/tkip.c
@@ -185,6 +185,17 @@ void ieee80211_get_tkip_p1k_iv(struct ieee80211_key_conf *keyconf,
}
EXPORT_SYMBOL(ieee80211_get_tkip_p1k_iv);
+void ieee80211_get_tkip_rx_p1k(struct ieee80211_key_conf *keyconf,
+ const u8 *ta, u32 iv32, u16 *p1k)
+{
+ const u8 *tk = &keyconf->key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY];
+ struct tkip_ctx ctx;
+
+ tkip_mixing_phase1(tk, &ctx, ta, iv32);
+ memcpy(p1k, ctx.p1k, sizeof(ctx.p1k));
+}
+EXPORT_SYMBOL(ieee80211_get_tkip_rx_p1k);
+
void ieee80211_get_tkip_p2k(struct ieee80211_key_conf *keyconf,
struct sk_buff *skb, u8 *p2k)
{
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 5bfb80cba634..ddeb1b998383 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -799,6 +799,7 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata)
qparam.uapsd = false;
+ local->tx_conf[queue] = qparam;
drv_conf_tx(local, queue, &qparam);
}
@@ -1016,7 +1017,7 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
}
struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
- u8 *dst,
+ u8 *dst, u32 ratemask,
const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len,
bool directed)
@@ -1049,9 +1050,7 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
buf_len = ieee80211_build_preq_ies(local, buf, ie, ie_len,
local->hw.conf.channel->band,
- sdata->rc_rateidx_mask
- [local->hw.conf.channel->band],
- chan);
+ ratemask, chan);
skb = ieee80211_probereq_get(&local->hw, &sdata->vif,
ssid, ssid_len,
@@ -1072,12 +1071,12 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len,
- bool directed)
+ u32 ratemask, bool directed)
{
struct sk_buff *skb;
- skb = ieee80211_build_probe_req(sdata, dst, ssid, ssid_len, ie, ie_len,
- directed);
+ skb = ieee80211_build_probe_req(sdata, dst, ratemask, ssid, ssid_len,
+ ie, ie_len, directed);
if (skb)
ieee80211_tx_skb(sdata, skb);
}
@@ -1134,7 +1133,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
struct ieee80211_hw *hw = &local->hw;
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
- int res;
+ int res, i;
#ifdef CONFIG_PM
if (local->suspended)
@@ -1157,27 +1156,37 @@ int ieee80211_reconfig(struct ieee80211_local *local)
}
#endif
- /* 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, "Hardware became unavailable "
- "upon resume. This could be a software issue "
- "prior to suspend or a hardware issue.\n");
- return res;
- }
+ /* setup fragmentation threshold */
+ drv_set_frag_threshold(local, hw->wiphy->frag_threshold);
+
+ /* setup RTS threshold */
+ drv_set_rts_threshold(local, hw->wiphy->rts_threshold);
+
+ /* reset coverage class */
+ drv_set_coverage_class(local, hw->wiphy->coverage_class);
+
+ /* everything else happens only if HW was up & running */
+ if (!local->open_count)
+ goto wake_up;
- ieee80211_led_radio(local, true);
- ieee80211_mod_tpt_led_trig(local,
- IEEE80211_TPT_LEDTRIG_FL_RADIO, 0);
+ /*
+ * 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, "Hardware became unavailable "
+ "upon resume. This could be a software issue "
+ "prior to suspend or a hardware issue.\n");
+ return res;
}
+ ieee80211_led_radio(local, true);
+ ieee80211_mod_tpt_led_trig(local,
+ IEEE80211_TPT_LEDTRIG_FL_RADIO, 0);
+
/* add interfaces */
list_for_each_entry(sdata, &local->interfaces, list) {
if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
@@ -1201,11 +1210,9 @@ int ieee80211_reconfig(struct ieee80211_local *local)
}
mutex_unlock(&local->sta_mtx);
- /* setup fragmentation threshold */
- drv_set_frag_threshold(local, hw->wiphy->frag_threshold);
-
- /* setup RTS threshold */
- drv_set_rts_threshold(local, hw->wiphy->rts_threshold);
+ /* reconfigure tx conf */
+ for (i = 0; i < hw->queues; i++)
+ drv_conf_tx(local, i, &local->tx_conf[i]);
/* reconfigure hardware */
ieee80211_hw_config(local, ~0);
@@ -1287,9 +1294,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
if (ieee80211_sdata_running(sdata))
ieee80211_enable_keys(sdata);
-#ifdef CONFIG_PM
wake_up:
-#endif
ieee80211_wake_queues_by_reason(hw,
IEEE80211_QUEUE_STOP_REASON_SUSPEND);
diff --git a/net/mac80211/work.c b/net/mac80211/work.c
index edf8583280c9..380b9a7462b6 100644
--- a/net/mac80211/work.c
+++ b/net/mac80211/work.c
@@ -25,6 +25,7 @@
#include "ieee80211_i.h"
#include "rate.h"
+#include "driver-ops.h"
#define IEEE80211_AUTH_TIMEOUT (HZ / 5)
#define IEEE80211_AUTH_MAX_TRIES 3
@@ -427,6 +428,14 @@ ieee80211_direct_probe(struct ieee80211_work *wk)
struct ieee80211_sub_if_data *sdata = wk->sdata;
struct ieee80211_local *local = sdata->local;
+ if (!wk->probe_auth.synced) {
+ int ret = drv_tx_sync(local, sdata, wk->filter_ta,
+ IEEE80211_TX_SYNC_AUTH);
+ if (ret)
+ return WORK_ACT_TIMEOUT;
+ }
+ wk->probe_auth.synced = true;
+
wk->probe_auth.tries++;
if (wk->probe_auth.tries > IEEE80211_AUTH_MAX_TRIES) {
printk(KERN_DEBUG "%s: direct probe to %pM timed out\n",
@@ -450,7 +459,8 @@ ieee80211_direct_probe(struct ieee80211_work *wk)
* will not answer to direct packet in unassociated state.
*/
ieee80211_send_probe_req(sdata, NULL, wk->probe_auth.ssid,
- wk->probe_auth.ssid_len, NULL, 0, true);
+ wk->probe_auth.ssid_len, NULL, 0,
+ (u32) -1, true);
wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
run_again(local, wk->timeout);
@@ -465,6 +475,14 @@ ieee80211_authenticate(struct ieee80211_work *wk)
struct ieee80211_sub_if_data *sdata = wk->sdata;
struct ieee80211_local *local = sdata->local;
+ if (!wk->probe_auth.synced) {
+ int ret = drv_tx_sync(local, sdata, wk->filter_ta,
+ IEEE80211_TX_SYNC_AUTH);
+ if (ret)
+ return WORK_ACT_TIMEOUT;
+ }
+ wk->probe_auth.synced = true;
+
wk->probe_auth.tries++;
if (wk->probe_auth.tries > IEEE80211_AUTH_MAX_TRIES) {
printk(KERN_DEBUG "%s: authentication with %pM"
@@ -498,6 +516,14 @@ ieee80211_associate(struct ieee80211_work *wk)
struct ieee80211_sub_if_data *sdata = wk->sdata;
struct ieee80211_local *local = sdata->local;
+ if (!wk->assoc.synced) {
+ int ret = drv_tx_sync(local, sdata, wk->filter_ta,
+ IEEE80211_TX_SYNC_ASSOC);
+ if (ret)
+ return WORK_ACT_TIMEOUT;
+ }
+ wk->assoc.synced = true;
+
wk->assoc.tries++;
if (wk->assoc.tries > IEEE80211_ASSOC_MAX_TRIES) {
printk(KERN_DEBUG "%s: association with %pM"
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 880dbe2e6f94..645437cfc464 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -488,6 +488,10 @@ int wiphy_register(struct wiphy *wiphy)
int i;
u16 ifmodes = wiphy->interface_modes;
+ if (WARN_ON((wiphy->wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
+ !(wiphy->wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY)))
+ return -EINVAL;
+
if (WARN_ON(wiphy->addresses && !wiphy->n_addresses))
return -EINVAL;
@@ -918,7 +922,8 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
* Configure power management to the driver here so that its
* correctly set also after interface type changes etc.
*/
- if (wdev->iftype == NL80211_IFTYPE_STATION &&
+ if ((wdev->iftype == NL80211_IFTYPE_STATION ||
+ wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) &&
rdev->ops->set_power_mgmt)
if (rdev->ops->set_power_mgmt(wdev->wiphy, dev,
wdev->ps,
diff --git a/net/wireless/core.h b/net/wireless/core.h
index a570ff9214ec..8672e028022f 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -447,6 +447,10 @@ int cfg80211_set_freq(struct cfg80211_registered_device *rdev,
u16 cfg80211_calculate_bitrate(struct rate_info *rate);
+int ieee80211_get_ratemask(struct ieee80211_supported_band *sband,
+ const u8 *rates, unsigned int n_rates,
+ u32 *mask);
+
int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
u32 beacon_int);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 6a82c898f831..28d2aa109bee 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -177,6 +177,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 },
[NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 },
[NL80211_ATTR_REKEY_DATA] = { .type = NLA_NESTED },
+ [NL80211_ATTR_SCAN_SUPP_RATES] = { .type = NLA_NESTED },
};
/* policy for the key attributes */
@@ -205,6 +206,10 @@ nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
[NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED },
+ [NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE] = { .type = NLA_FLAG },
+ [NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG },
+ [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG },
+ [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG },
};
/* policy for GTK rekey offload attributes */
@@ -692,8 +697,12 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
dev->wiphy.coverage_class);
NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
dev->wiphy.max_scan_ssids);
+ NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
+ dev->wiphy.max_sched_scan_ssids);
NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
dev->wiphy.max_scan_ie_len);
+ NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
+ dev->wiphy.max_sched_scan_ie_len);
if (dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)
NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_IBSS_RSN);
@@ -929,6 +938,16 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT)
NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
+ if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED);
+ if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE);
+ if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST);
+ if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE);
+ if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE);
if (dev->wiphy.wowlan.n_patterns) {
struct nl80211_wowlan_pattern_support pat = {
.max_patterns = dev->wiphy.wowlan.n_patterns,
@@ -3306,7 +3325,6 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
struct nlattr *attr;
struct wiphy *wiphy;
int err, tmp, n_ssids = 0, n_channels, i;
- enum ieee80211_band band;
size_t ie_len;
if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
@@ -3326,6 +3344,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
if (!n_channels)
return -EINVAL;
} else {
+ enum ieee80211_band band;
n_channels = 0;
for (band = 0; band < IEEE80211_NUM_BANDS; band++)
@@ -3386,6 +3405,8 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
i++;
}
} else {
+ enum ieee80211_band band;
+
/* all channels */
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
int j;
@@ -3432,6 +3453,30 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
request->ie_len);
}
+ for (i = 0; i < IEEE80211_NUM_BANDS; i++)
+ if (wiphy->bands[i])
+ request->rates[i] =
+ (1 << wiphy->bands[i]->n_bitrates) - 1;
+
+ if (info->attrs[NL80211_ATTR_SCAN_SUPP_RATES]) {
+ nla_for_each_nested(attr,
+ info->attrs[NL80211_ATTR_SCAN_SUPP_RATES],
+ tmp) {
+ enum ieee80211_band band = nla_type(attr);
+
+ if (band < 0 || band > IEEE80211_NUM_BANDS) {
+ err = -EINVAL;
+ goto out_free;
+ }
+ err = ieee80211_get_ratemask(wiphy->bands[band],
+ nla_data(attr),
+ nla_len(attr),
+ &request->rates[band]);
+ if (err)
+ goto out_free;
+ }
+ }
+
request->dev = dev;
request->wiphy = &rdev->wiphy;
@@ -3497,7 +3542,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
tmp)
n_ssids++;
- if (n_ssids > wiphy->max_scan_ssids)
+ if (n_ssids > wiphy->max_sched_scan_ssids)
return -EINVAL;
if (info->attrs[NL80211_ATTR_IE])
@@ -3505,7 +3550,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
else
ie_len = 0;
- if (ie_len > wiphy->max_scan_ie_len)
+ if (ie_len > wiphy->max_sched_scan_ie_len)
return -EINVAL;
mutex_lock(&rdev->sched_scan_mtx);
@@ -4318,25 +4363,12 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
struct ieee80211_supported_band *sband =
wiphy->bands[ibss.channel->band];
- int i, j;
+ int err;
- if (n_rates == 0)
- return -EINVAL;
-
- for (i = 0; i < n_rates; i++) {
- int rate = (rates[i] & 0x7f) * 5;
- bool found = false;
-
- for (j = 0; j < sband->n_bitrates; j++) {
- if (sband->bitrates[j].bitrate == rate) {
- found = true;
- ibss.basic_rates |= BIT(j);
- break;
- }
- }
- if (!found)
- return -EINVAL;
- }
+ err = ieee80211_get_ratemask(sband, rates, n_rates,
+ &ibss.basic_rates);
+ if (err)
+ return err;
}
if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
@@ -5272,6 +5304,14 @@ static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
if (rdev->wowlan->magic_pkt)
NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
+ if (rdev->wowlan->gtk_rekey_failure)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE);
+ if (rdev->wowlan->eap_identity_req)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST);
+ if (rdev->wowlan->four_way_handshake)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE);
+ if (rdev->wowlan->rfkill_release)
+ NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE);
if (rdev->wowlan->n_patterns) {
struct nlattr *nl_pats, *nl_pat;
int i, pat_len;
@@ -5348,6 +5388,33 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
new_triggers.magic_pkt = true;
}
+ if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED])
+ return -EINVAL;
+
+ if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) {
+ if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE))
+ return -EINVAL;
+ new_triggers.gtk_rekey_failure = true;
+ }
+
+ if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) {
+ if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ))
+ return -EINVAL;
+ new_triggers.eap_identity_req = true;
+ }
+
+ if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) {
+ if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE))
+ return -EINVAL;
+ new_triggers.four_way_handshake = true;
+ }
+
+ if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) {
+ if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE))
+ return -EINVAL;
+ new_triggers.rfkill_release = true;
+ }
+
if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
struct nlattr *pat;
int n_patterns = 0;
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 1c4672e35144..2936cb809152 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -862,6 +862,10 @@ int cfg80211_wext_siwscan(struct net_device *dev,
creq->n_ssids = 0;
}
+ for (i = 0; i < IEEE80211_NUM_BANDS; i++)
+ if (wiphy->bands[i])
+ creq->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1;
+
rdev->scan_req = creq;
err = rdev->ops->scan(wiphy, dev, creq);
if (err) {
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 4d7b83fbc32f..be75a3a0424e 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -1006,3 +1006,41 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
return -EBUSY;
}
+
+int ieee80211_get_ratemask(struct ieee80211_supported_band *sband,
+ const u8 *rates, unsigned int n_rates,
+ u32 *mask)
+{
+ int i, j;
+
+ if (!sband)
+ return -EINVAL;
+
+ if (n_rates == 0 || n_rates > NL80211_MAX_SUPP_RATES)
+ return -EINVAL;
+
+ *mask = 0;
+
+ for (i = 0; i < n_rates; i++) {
+ int rate = (rates[i] & 0x7f) * 5;
+ bool found = false;
+
+ for (j = 0; j < sband->n_bitrates; j++) {
+ if (sband->bitrates[j].bitrate == rate) {
+ found = true;
+ *mask |= BIT(j);
+ break;
+ }
+ }
+ if (!found)
+ return -EINVAL;
+ }
+
+ /*
+ * mask must have at least one bit set here since we
+ * didn't accept a 0-length rates array nor allowed
+ * entries in the array that didn't exist
+ */
+
+ return 0;
+}