summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/agg-rx.c8
-rw-r--r--net/mac80211/agg-tx.c232
-rw-r--r--net/mac80211/cfg.c245
-rw-r--r--net/mac80211/debugfs.c24
-rw-r--r--net/mac80211/ibss.c3
-rw-r--r--net/mac80211/ieee80211_i.h78
-rw-r--r--net/mac80211/iface.c14
-rw-r--r--net/mac80211/main.c25
-rw-r--r--net/mac80211/mlme.c259
-rw-r--r--net/mac80211/pm.c78
-rw-r--r--net/mac80211/rate.c6
-rw-r--r--net/mac80211/rate.h4
-rw-r--r--net/mac80211/rx.c29
-rw-r--r--net/mac80211/scan.c77
-rw-r--r--net/mac80211/sta_info.c17
-rw-r--r--net/mac80211/sta_info.h7
-rw-r--r--net/mac80211/tx.c579
-rw-r--r--net/mac80211/util.c126
-rw-r--r--net/mac80211/wep.c21
-rw-r--r--net/mac80211/wext.c33
-rw-r--r--net/mac80211/wpa.c28
-rw-r--r--net/wireless/Kconfig50
-rw-r--r--net/wireless/Makefile3
-rw-r--r--net/wireless/core.c30
-rw-r--r--net/wireless/core.h5
-rw-r--r--net/wireless/mlme.c46
-rw-r--r--net/wireless/nl80211.c824
-rw-r--r--net/wireless/nl80211.h38
-rw-r--r--net/wireless/reg.c65
-rw-r--r--net/wireless/scan.c27
-rw-r--r--net/wireless/wext-compat.c11
31 files changed, 1889 insertions, 1103 deletions
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index a95affc94629..07656d830bc4 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -197,6 +197,14 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
status = WLAN_STATUS_REQUEST_DECLINED;
+ if (test_sta_flags(sta, WLAN_STA_SUSPEND)) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "Suspend in progress. "
+ "Denying ADDBA request\n");
+#endif
+ goto end_no_lock;
+ }
+
/* sanity check for incoming parameters:
* check if configuration can support the BA policy
* and if buffer size does not exceeds max value */
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index 1df116d4d6e7..947aaaad35d2 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -131,24 +131,6 @@ static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
state = &sta->ampdu_mlme.tid_state_tx[tid];
- if (local->hw.ampdu_queues) {
- if (initiator) {
- /*
- * Stop the AC queue to avoid issues where we send
- * unaggregated frames already before the delba.
- */
- ieee80211_stop_queue_by_reason(&local->hw,
- local->hw.queues + sta->tid_to_tx_q[tid],
- IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
- }
-
- /*
- * Pretend the driver woke the queue, just in case
- * it disabled it before the session was stopped.
- */
- ieee80211_wake_queue(
- &local->hw, local->hw.queues + sta->tid_to_tx_q[tid]);
- }
*state = HT_AGG_STATE_REQ_STOP_BA_MSK |
(initiator << HT_AGG_STATE_INITIATOR_SHIFT);
@@ -158,6 +140,10 @@ static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
/* HW shall not deny going back to legacy */
if (WARN_ON(ret)) {
*state = HT_AGG_STATE_OPERATIONAL;
+ /*
+ * We may have pending packets get stuck in this case...
+ * Not bothering with a workaround for now.
+ */
}
return ret;
@@ -212,7 +198,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
struct sta_info *sta;
struct ieee80211_sub_if_data *sdata;
u8 *state;
- int i, qn = -1, ret = 0;
+ int ret = 0;
u16 start_seq_num;
if (WARN_ON(!local->ops->ampdu_action))
@@ -226,13 +212,6 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
ra, tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
- if (hw->ampdu_queues && ieee80211_ac_from_tid(tid) == 0) {
-#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "rejecting on voice AC\n");
-#endif
- return -EINVAL;
- }
-
rcu_read_lock();
sta = sta_info_get(local, ra);
@@ -257,7 +236,17 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
goto unlock;
}
+ if (test_sta_flags(sta, WLAN_STA_SUSPEND)) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "Suspend in progress. "
+ "Denying BA session request\n");
+#endif
+ ret = -EINVAL;
+ goto unlock;
+ }
+
spin_lock_bh(&sta->lock);
+ spin_lock(&local->ampdu_lock);
sdata = sta->sdata;
@@ -278,41 +267,16 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
goto err_unlock_sta;
}
- if (hw->ampdu_queues) {
- spin_lock(&local->queue_stop_reason_lock);
- /* reserve a new queue for this session */
- for (i = 0; i < local->hw.ampdu_queues; i++) {
- if (local->ampdu_ac_queue[i] < 0) {
- qn = i;
- local->ampdu_ac_queue[qn] =
- ieee80211_ac_from_tid(tid);
- break;
- }
- }
- spin_unlock(&local->queue_stop_reason_lock);
-
- if (qn < 0) {
-#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "BA request denied - "
- "queue unavailable for tid %d\n", tid);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
- ret = -ENOSPC;
- goto err_unlock_sta;
- }
-
- /*
- * If we successfully allocate the session, we can't have
- * anything going on on the queue this TID maps into, so
- * stop it for now. This is a "virtual" stop using the same
- * mechanism that drivers will use.
- *
- * XXX: queue up frames for this session in the sta_info
- * struct instead to avoid hitting all other STAs.
- */
- ieee80211_stop_queue_by_reason(
- &local->hw, hw->queues + qn,
- IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
- }
+ /*
+ * While we're asking the driver about the aggregation,
+ * stop the AC queue so that we don't have to worry
+ * about frames that came in while we were doing that,
+ * which would require us to put them to the AC pending
+ * afterwards which just makes the code more complex.
+ */
+ ieee80211_stop_queue_by_reason(
+ &local->hw, ieee80211_ac_from_tid(tid),
+ IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
/* prepare A-MPDU MLME for Tx aggregation */
sta->ampdu_mlme.tid_tx[tid] =
@@ -324,9 +288,11 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
tid);
#endif
ret = -ENOMEM;
- goto err_return_queue;
+ goto err_wake_queue;
}
+ skb_queue_head_init(&sta->ampdu_mlme.tid_tx[tid]->pending);
+
/* Tx timer */
sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.function =
sta_addba_resp_timer_expired;
@@ -351,8 +317,13 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
*state = HT_AGG_STATE_IDLE;
goto err_free;
}
- sta->tid_to_tx_q[tid] = qn;
+ /* Driver vetoed or OKed, but we can take packets again now */
+ ieee80211_wake_queue_by_reason(
+ &local->hw, ieee80211_ac_from_tid(tid),
+ IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
+
+ spin_unlock(&local->ampdu_lock);
spin_unlock_bh(&sta->lock);
/* send an addBA request */
@@ -377,17 +348,12 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
err_free:
kfree(sta->ampdu_mlme.tid_tx[tid]);
sta->ampdu_mlme.tid_tx[tid] = NULL;
- err_return_queue:
- if (qn >= 0) {
- /* We failed, so start queue again right away. */
- ieee80211_wake_queue_by_reason(hw, hw->queues + qn,
- IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
- /* give queue back to pool */
- spin_lock(&local->queue_stop_reason_lock);
- local->ampdu_ac_queue[qn] = -1;
- spin_unlock(&local->queue_stop_reason_lock);
- }
+ err_wake_queue:
+ ieee80211_wake_queue_by_reason(
+ &local->hw, ieee80211_ac_from_tid(tid),
+ IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
err_unlock_sta:
+ spin_unlock(&local->ampdu_lock);
spin_unlock_bh(&sta->lock);
unlock:
rcu_read_unlock();
@@ -395,6 +361,67 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
}
EXPORT_SYMBOL(ieee80211_start_tx_ba_session);
+/*
+ * splice packets from the STA's pending to the local pending,
+ * requires a call to ieee80211_agg_splice_finish and holding
+ * local->ampdu_lock across both calls.
+ */
+static void ieee80211_agg_splice_packets(struct ieee80211_local *local,
+ struct sta_info *sta, u16 tid)
+{
+ unsigned long flags;
+ u16 queue = ieee80211_ac_from_tid(tid);
+
+ ieee80211_stop_queue_by_reason(
+ &local->hw, queue,
+ IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
+
+ if (!skb_queue_empty(&sta->ampdu_mlme.tid_tx[tid]->pending)) {
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+ /* mark queue as pending, it is stopped already */
+ __set_bit(IEEE80211_QUEUE_STOP_REASON_PENDING,
+ &local->queue_stop_reasons[queue]);
+ /* copy over remaining packets */
+ skb_queue_splice_tail_init(
+ &sta->ampdu_mlme.tid_tx[tid]->pending,
+ &local->pending[queue]);
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+ }
+}
+
+static void ieee80211_agg_splice_finish(struct ieee80211_local *local,
+ struct sta_info *sta, u16 tid)
+{
+ u16 queue = ieee80211_ac_from_tid(tid);
+
+ ieee80211_wake_queue_by_reason(
+ &local->hw, queue,
+ IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
+}
+
+/* caller must hold sta->lock */
+static void ieee80211_agg_tx_operational(struct ieee80211_local *local,
+ struct sta_info *sta, u16 tid)
+{
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid);
+#endif
+
+ spin_lock(&local->ampdu_lock);
+ ieee80211_agg_splice_packets(local, sta, tid);
+ /*
+ * NB: we rely on sta->lock being taken in the TX
+ * processing here when adding to the pending queue,
+ * otherwise we could only change the state of the
+ * session to OPERATIONAL _here_.
+ */
+ ieee80211_agg_splice_finish(local, sta, tid);
+ spin_unlock(&local->ampdu_lock);
+
+ local->ops->ampdu_action(&local->hw, IEEE80211_AMPDU_TX_OPERATIONAL,
+ &sta->sta, tid, NULL);
+}
+
void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
{
struct ieee80211_local *local = hw_to_local(hw);
@@ -437,20 +464,8 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
*state |= HT_ADDBA_DRV_READY_MSK;
- if (*state == HT_AGG_STATE_OPERATIONAL) {
-#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid);
-#endif
- if (hw->ampdu_queues) {
- /*
- * Wake up this queue, we stopped it earlier,
- * this will in turn wake the entire AC.
- */
- ieee80211_wake_queue_by_reason(hw,
- hw->queues + sta->tid_to_tx_q[tid],
- IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
- }
- }
+ if (*state == HT_AGG_STATE_OPERATIONAL)
+ ieee80211_agg_tx_operational(local, sta, tid);
out:
spin_unlock_bh(&sta->lock);
@@ -584,22 +599,19 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
spin_lock_bh(&sta->lock);
+ spin_lock(&local->ampdu_lock);
- if (*state & HT_AGG_STATE_INITIATOR_MSK &&
- hw->ampdu_queues) {
- /*
- * Wake up this queue, we stopped it earlier,
- * this will in turn wake the entire AC.
- */
- ieee80211_wake_queue_by_reason(hw,
- hw->queues + sta->tid_to_tx_q[tid],
- IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
- }
+ ieee80211_agg_splice_packets(local, sta, tid);
*state = HT_AGG_STATE_IDLE;
+ /* from now on packets are no longer put onto sta->pending */
sta->ampdu_mlme.addba_req_num[tid] = 0;
kfree(sta->ampdu_mlme.tid_tx[tid]);
sta->ampdu_mlme.tid_tx[tid] = NULL;
+
+ ieee80211_agg_splice_finish(local, sta, tid);
+
+ spin_unlock(&local->ampdu_lock);
spin_unlock_bh(&sta->lock);
rcu_read_unlock();
@@ -637,9 +649,7 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
struct ieee80211_mgmt *mgmt,
size_t len)
{
- struct ieee80211_hw *hw = &local->hw;
- u16 capab;
- u16 tid, start_seq_num;
+ u16 capab, tid;
u8 *state;
capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab);
@@ -673,26 +683,10 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
*state |= HT_ADDBA_RECEIVED_MSK;
- if (hw->ampdu_queues && *state != curstate &&
- *state == HT_AGG_STATE_OPERATIONAL) {
- /*
- * Wake up this queue, we stopped it earlier,
- * this will in turn wake the entire AC.
- */
- ieee80211_wake_queue_by_reason(hw,
- hw->queues + sta->tid_to_tx_q[tid],
- IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
- }
- sta->ampdu_mlme.addba_req_num[tid] = 0;
+ if (*state != curstate && *state == HT_AGG_STATE_OPERATIONAL)
+ ieee80211_agg_tx_operational(local, sta, tid);
- if (local->ops->ampdu_action) {
- (void)local->ops->ampdu_action(hw,
- IEEE80211_AMPDU_TX_RESUME,
- &sta->sta, tid, &start_seq_num);
- }
-#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "Resuming TX aggregation for tid %d\n", tid);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
+ sta->ampdu_mlme.addba_req_num[tid] = 0;
} else {
sta->ampdu_mlme.addba_req_num[tid]++;
___ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR);
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 58693e52d458..e677b751d468 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -540,9 +540,6 @@ static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev,
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->vif.type != NL80211_IFTYPE_AP)
- return -EINVAL;
-
old = sdata->u.ap.beacon;
if (old)
@@ -559,9 +556,6 @@ static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev,
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->vif.type != NL80211_IFTYPE_AP)
- return -EINVAL;
-
old = sdata->u.ap.beacon;
if (!old)
@@ -577,9 +571,6 @@ static int ieee80211_del_beacon(struct wiphy *wiphy, struct net_device *dev)
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->vif.type != NL80211_IFTYPE_AP)
- return -EINVAL;
-
old = sdata->u.ap.beacon;
if (!old)
@@ -728,10 +719,6 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
int err;
int layer2_update;
- /* Prevent a race with changing the rate control algorithm */
- if (!netif_running(dev))
- return -ENETDOWN;
-
if (params->vlan) {
sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
@@ -860,14 +847,8 @@ static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
struct sta_info *sta;
int err;
- if (!netif_running(dev))
- return -ENETDOWN;
-
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
- return -ENOTSUPP;
-
rcu_read_lock();
sta = sta_info_get(local, next_hop);
if (!sta) {
@@ -913,14 +894,8 @@ static int ieee80211_change_mpath(struct wiphy *wiphy,
struct mesh_path *mpath;
struct sta_info *sta;
- if (!netif_running(dev))
- return -ENETDOWN;
-
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
- return -ENOTSUPP;
-
rcu_read_lock();
sta = sta_info_get(local, next_hop);
@@ -989,9 +964,6 @@ static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev,
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
- return -ENOTSUPP;
-
rcu_read_lock();
mpath = mesh_path_lookup(dst, sdata);
if (!mpath) {
@@ -1013,9 +985,6 @@ static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
- return -ENOTSUPP;
-
rcu_read_lock();
mpath = mesh_path_lookup_by_idx(idx, sdata);
if (!mpath) {
@@ -1035,8 +1004,6 @@ static int ieee80211_get_mesh_params(struct wiphy *wiphy,
struct ieee80211_sub_if_data *sdata;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
- return -ENOTSUPP;
memcpy(conf, &(sdata->u.mesh.mshcfg), sizeof(struct mesh_config));
return 0;
}
@@ -1054,9 +1021,6 @@ static int ieee80211_set_mesh_params(struct wiphy *wiphy,
struct ieee80211_sub_if_data *sdata;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
- return -ENOTSUPP;
-
/* Set the config options which we are interested in setting */
conf = &(sdata->u.mesh.mshcfg);
if (_chg_mesh_attr(NL80211_MESHCONF_RETRY_TIMEOUT, mask))
@@ -1104,9 +1068,6 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->vif.type != NL80211_IFTYPE_AP)
- return -EINVAL;
-
if (params->use_cts_prot >= 0) {
sdata->vif.bss_conf.use_cts_prot = params->use_cts_prot;
changed |= BSS_CHANGED_ERP_CTS_PROT;
@@ -1181,91 +1142,6 @@ static int ieee80211_set_channel(struct wiphy *wiphy,
return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
}
-static int set_mgmt_extra_ie_sta(struct ieee80211_sub_if_data *sdata,
- u8 subtype, u8 *ies, size_t ies_len)
-{
- struct ieee80211_local *local = sdata->local;
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-
- switch (subtype) {
- case IEEE80211_STYPE_PROBE_REQ >> 4:
- if (local->ops->hw_scan)
- break;
- kfree(ifmgd->ie_probereq);
- ifmgd->ie_probereq = ies;
- ifmgd->ie_probereq_len = ies_len;
- return 0;
- case IEEE80211_STYPE_PROBE_RESP >> 4:
- kfree(ifmgd->ie_proberesp);
- ifmgd->ie_proberesp = ies;
- ifmgd->ie_proberesp_len = ies_len;
- return 0;
- case IEEE80211_STYPE_AUTH >> 4:
- kfree(ifmgd->ie_auth);
- ifmgd->ie_auth = ies;
- ifmgd->ie_auth_len = ies_len;
- return 0;
- case IEEE80211_STYPE_ASSOC_REQ >> 4:
- kfree(ifmgd->ie_assocreq);
- ifmgd->ie_assocreq = ies;
- ifmgd->ie_assocreq_len = ies_len;
- return 0;
- case IEEE80211_STYPE_REASSOC_REQ >> 4:
- kfree(ifmgd->ie_reassocreq);
- ifmgd->ie_reassocreq = ies;
- ifmgd->ie_reassocreq_len = ies_len;
- return 0;
- case IEEE80211_STYPE_DEAUTH >> 4:
- kfree(ifmgd->ie_deauth);
- ifmgd->ie_deauth = ies;
- ifmgd->ie_deauth_len = ies_len;
- return 0;
- case IEEE80211_STYPE_DISASSOC >> 4:
- kfree(ifmgd->ie_disassoc);
- ifmgd->ie_disassoc = ies;
- ifmgd->ie_disassoc_len = ies_len;
- return 0;
- }
-
- return -EOPNOTSUPP;
-}
-
-static int ieee80211_set_mgmt_extra_ie(struct wiphy *wiphy,
- struct net_device *dev,
- struct mgmt_extra_ie_params *params)
-{
- struct ieee80211_sub_if_data *sdata;
- u8 *ies;
- size_t ies_len;
- int ret = -EOPNOTSUPP;
-
- if (params->ies) {
- ies = kmemdup(params->ies, params->ies_len, GFP_KERNEL);
- if (ies == NULL)
- return -ENOMEM;
- ies_len = params->ies_len;
- } else {
- ies = NULL;
- ies_len = 0;
- }
-
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- switch (sdata->vif.type) {
- case NL80211_IFTYPE_STATION:
- ret = set_mgmt_extra_ie_sta(sdata, params->subtype,
- ies, ies_len);
- break;
- default:
- ret = -EOPNOTSUPP;
- break;
- }
-
- if (ret)
- kfree(ies);
- return ret;
-}
-
#ifdef CONFIG_PM
static int ieee80211_suspend(struct wiphy *wiphy)
{
@@ -1287,9 +1163,6 @@ static int ieee80211_scan(struct wiphy *wiphy,
{
struct ieee80211_sub_if_data *sdata;
- if (!netif_running(dev))
- return -ENETDOWN;
-
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (sdata->vif.type != NL80211_IFTYPE_STATION &&
@@ -1300,6 +1173,119 @@ static int ieee80211_scan(struct wiphy *wiphy,
return ieee80211_request_scan(sdata, req);
}
+static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_auth_request *req)
+{
+ struct ieee80211_sub_if_data *sdata;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ switch (req->auth_type) {
+ case NL80211_AUTHTYPE_OPEN_SYSTEM:
+ sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_OPEN;
+ break;
+ case NL80211_AUTHTYPE_SHARED_KEY:
+ sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_SHARED_KEY;
+ break;
+ case NL80211_AUTHTYPE_FT:
+ sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_FT;
+ break;
+ case NL80211_AUTHTYPE_NETWORK_EAP:
+ sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_LEAP;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ memcpy(sdata->u.mgd.bssid, req->peer_addr, ETH_ALEN);
+ sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
+ sdata->u.mgd.flags |= IEEE80211_STA_BSSID_SET;
+
+ /* TODO: req->chan */
+ sdata->u.mgd.flags |= IEEE80211_STA_AUTO_CHANNEL_SEL;
+
+ if (req->ssid) {
+ sdata->u.mgd.flags |= IEEE80211_STA_SSID_SET;
+ memcpy(sdata->u.mgd.ssid, req->ssid, req->ssid_len);
+ sdata->u.mgd.ssid_len = req->ssid_len;
+ sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL;
+ }
+
+ kfree(sdata->u.mgd.sme_auth_ie);
+ sdata->u.mgd.sme_auth_ie = NULL;
+ sdata->u.mgd.sme_auth_ie_len = 0;
+ if (req->ie) {
+ sdata->u.mgd.sme_auth_ie = kmalloc(req->ie_len, GFP_KERNEL);
+ if (sdata->u.mgd.sme_auth_ie == NULL)
+ return -ENOMEM;
+ memcpy(sdata->u.mgd.sme_auth_ie, req->ie, req->ie_len);
+ sdata->u.mgd.sme_auth_ie_len = req->ie_len;
+ }
+
+ sdata->u.mgd.flags |= IEEE80211_STA_EXT_SME;
+ sdata->u.mgd.state = IEEE80211_STA_MLME_DIRECT_PROBE;
+ ieee80211_sta_req_auth(sdata);
+ return 0;
+}
+
+static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_assoc_request *req)
+{
+ struct ieee80211_sub_if_data *sdata;
+ int ret;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ if (memcmp(sdata->u.mgd.bssid, req->peer_addr, ETH_ALEN) != 0 ||
+ !(sdata->u.mgd.flags & IEEE80211_STA_AUTHENTICATED))
+ return -ENOLINK; /* not authenticated */
+
+ sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
+ sdata->u.mgd.flags |= IEEE80211_STA_BSSID_SET;
+
+ /* TODO: req->chan */
+ sdata->u.mgd.flags |= IEEE80211_STA_AUTO_CHANNEL_SEL;
+
+ if (req->ssid) {
+ sdata->u.mgd.flags |= IEEE80211_STA_SSID_SET;
+ memcpy(sdata->u.mgd.ssid, req->ssid, req->ssid_len);
+ sdata->u.mgd.ssid_len = req->ssid_len;
+ sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL;
+ } else
+ sdata->u.mgd.flags |= IEEE80211_STA_AUTO_SSID_SEL;
+
+ ret = ieee80211_sta_set_extra_ie(sdata, req->ie, req->ie_len);
+ if (ret)
+ return ret;
+
+ sdata->u.mgd.flags |= IEEE80211_STA_EXT_SME;
+ sdata->u.mgd.state = IEEE80211_STA_MLME_ASSOCIATE;
+ ieee80211_sta_req_auth(sdata);
+ return 0;
+}
+
+static int ieee80211_deauth(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_deauth_request *req)
+{
+ struct ieee80211_sub_if_data *sdata;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ /* TODO: req->ie */
+ return ieee80211_sta_deauthenticate(sdata, req->reason_code);
+}
+
+static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_disassoc_request *req)
+{
+ struct ieee80211_sub_if_data *sdata;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ /* TODO: req->ie */
+ return ieee80211_sta_disassociate(sdata, req->reason_code);
+}
+
struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
@@ -1329,8 +1315,11 @@ struct cfg80211_ops mac80211_config_ops = {
.change_bss = ieee80211_change_bss,
.set_txq_params = ieee80211_set_txq_params,
.set_channel = ieee80211_set_channel,
- .set_mgmt_extra_ie = ieee80211_set_mgmt_extra_ie,
.suspend = ieee80211_suspend,
.resume = ieee80211_resume,
.scan = ieee80211_scan,
+ .auth = ieee80211_auth,
+ .assoc = ieee80211_assoc,
+ .deauth = ieee80211_deauth,
+ .disassoc = ieee80211_disassoc,
};
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index e37f557de3f3..210b9b6fecd2 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -40,6 +40,10 @@ static const struct file_operations name## _ops = { \
local->debugfs.name = debugfs_create_file(#name, 0400, phyd, \
local, &name## _ops);
+#define DEBUGFS_ADD_MODE(name, mode) \
+ local->debugfs.name = debugfs_create_file(#name, mode, phyd, \
+ local, &name## _ops);
+
#define DEBUGFS_DEL(name) \
debugfs_remove(local->debugfs.name); \
local->debugfs.name = NULL;
@@ -113,6 +117,24 @@ static const struct file_operations tsf_ops = {
.open = mac80211_open_file_generic
};
+static ssize_t reset_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_local *local = file->private_data;
+
+ rtnl_lock();
+ __ieee80211_suspend(&local->hw);
+ __ieee80211_resume(&local->hw);
+ rtnl_unlock();
+
+ return count;
+}
+
+static const struct file_operations reset_ops = {
+ .write = reset_write,
+ .open = mac80211_open_file_generic,
+};
+
/* statistics stuff */
#define DEBUGFS_STATS_FILE(name, buflen, fmt, value...) \
@@ -254,6 +276,7 @@ void debugfs_hw_add(struct ieee80211_local *local)
DEBUGFS_ADD(total_ps_buffered);
DEBUGFS_ADD(wep_iv);
DEBUGFS_ADD(tsf);
+ DEBUGFS_ADD_MODE(reset, 0200);
statsd = debugfs_create_dir("statistics", phyd);
local->debugfs.statistics = statsd;
@@ -308,6 +331,7 @@ void debugfs_hw_del(struct ieee80211_local *local)
DEBUGFS_DEL(total_ps_buffered);
DEBUGFS_DEL(wep_iv);
DEBUGFS_DEL(tsf);
+ DEBUGFS_DEL(reset);
DEBUGFS_STATS_DEL(transmitted_fragment_count);
DEBUGFS_STATS_DEL(multicast_transmitted_frame_count);
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index f4becc12904e..3201e1f96365 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -812,8 +812,9 @@ int ieee80211_ibss_commit(struct ieee80211_sub_if_data *sdata)
ifibss->ibss_join_req = jiffies;
ifibss->state = IEEE80211_IBSS_MLME_SEARCH;
+ set_bit(IEEE80211_IBSS_REQ_RUN, &ifibss->request);
- return ieee80211_sta_find_ibss(sdata);
+ return 0;
}
int ieee80211_ibss_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t len)
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index fbb91f1aebb2..e6ed78cb16b3 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -149,11 +149,6 @@ struct ieee80211_tx_data {
struct ieee80211_channel *channel;
- /* Extra fragments (in addition to the first fragment
- * in skb) */
- struct sk_buff **extra_frag;
- int num_extra_frag;
-
u16 ethertype;
unsigned int flags;
};
@@ -189,12 +184,6 @@ struct ieee80211_rx_data {
u16 tkip_iv16;
};
-struct ieee80211_tx_stored_packet {
- struct sk_buff *skb;
- struct sk_buff **extra_frag;
- int num_extra_frag;
-};
-
struct beacon_data {
u8 *head, *tail;
int head_len, tail_len;
@@ -247,8 +236,9 @@ struct mesh_preq_queue {
#define IEEE80211_STA_ASSOCIATED BIT(4)
#define IEEE80211_STA_PROBEREQ_POLL BIT(5)
#define IEEE80211_STA_CREATE_IBSS BIT(6)
-#define IEEE80211_STA_MIXED_CELL BIT(7)
+/* hole at 7, please re-use */
#define IEEE80211_STA_WMM_ENABLED BIT(8)
+/* hole at 9, please re-use */
#define IEEE80211_STA_AUTO_SSID_SEL BIT(10)
#define IEEE80211_STA_AUTO_BSSID_SEL BIT(11)
#define IEEE80211_STA_AUTO_CHANNEL_SEL BIT(12)
@@ -256,6 +246,7 @@ struct mesh_preq_queue {
#define IEEE80211_STA_TKIP_WEP_USED BIT(14)
#define IEEE80211_STA_CSA_RECEIVED BIT(15)
#define IEEE80211_STA_MFP_ENABLED BIT(16)
+#define IEEE80211_STA_EXT_SME BIT(17)
/* flags for MLME request */
#define IEEE80211_STA_REQ_SCAN 0
#define IEEE80211_STA_REQ_DIRECT_PROBE 1
@@ -266,12 +257,14 @@ struct mesh_preq_queue {
#define IEEE80211_AUTH_ALG_OPEN BIT(0)
#define IEEE80211_AUTH_ALG_SHARED_KEY BIT(1)
#define IEEE80211_AUTH_ALG_LEAP BIT(2)
+#define IEEE80211_AUTH_ALG_FT BIT(3)
struct ieee80211_if_managed {
struct timer_list timer;
struct timer_list chswitch_timer;
struct work_struct work;
struct work_struct chswitch_work;
+ struct work_struct beacon_loss_work;
u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
@@ -305,6 +298,7 @@ struct ieee80211_if_managed {
unsigned long request;
unsigned long last_probe;
+ unsigned long last_beacon;
unsigned int flags;
@@ -321,20 +315,8 @@ struct ieee80211_if_managed {
int wmm_last_param_set;
/* Extra IE data for management frames */
- u8 *ie_probereq;
- size_t ie_probereq_len;
- u8 *ie_proberesp;
- size_t ie_proberesp_len;
- u8 *ie_auth;
- size_t ie_auth_len;
- u8 *ie_assocreq;
- size_t ie_assocreq_len;
- u8 *ie_reassocreq;
- size_t ie_reassocreq_len;
- u8 *ie_deauth;
- size_t ie_deauth_len;
- u8 *ie_disassoc;
- size_t ie_disassoc_len;
+ u8 *sme_auth_ie;
+ size_t sme_auth_ie_len;
};
enum ieee80211_ibss_flags {
@@ -421,7 +403,6 @@ struct ieee80211_if_mesh {
*
* @IEEE80211_SDATA_ALLMULTI: interface wants all multicast packets
* @IEEE80211_SDATA_PROMISC: interface is promisc
- * @IEEE80211_SDATA_USERSPACE_MLME: userspace MLME is active
* @IEEE80211_SDATA_OPERATING_GMODE: operating in G-only mode
* @IEEE80211_SDATA_DONT_BRIDGE_PACKETS: bridge packets between
* associated stations and deliver multicast frames both
@@ -430,9 +411,8 @@ struct ieee80211_if_mesh {
enum ieee80211_sub_if_data_flags {
IEEE80211_SDATA_ALLMULTI = BIT(0),
IEEE80211_SDATA_PROMISC = BIT(1),
- IEEE80211_SDATA_USERSPACE_MLME = BIT(2),
- IEEE80211_SDATA_OPERATING_GMODE = BIT(3),
- IEEE80211_SDATA_DONT_BRIDGE_PACKETS = BIT(4),
+ IEEE80211_SDATA_OPERATING_GMODE = BIT(2),
+ IEEE80211_SDATA_DONT_BRIDGE_PACKETS = BIT(3),
};
struct ieee80211_sub_if_data {
@@ -598,6 +578,8 @@ enum queue_stop_reason {
IEEE80211_QUEUE_STOP_REASON_PS,
IEEE80211_QUEUE_STOP_REASON_CSA,
IEEE80211_QUEUE_STOP_REASON_AGGREGATION,
+ IEEE80211_QUEUE_STOP_REASON_SUSPEND,
+ IEEE80211_QUEUE_STOP_REASON_PENDING,
};
struct ieee80211_master_priv {
@@ -612,12 +594,7 @@ struct ieee80211_local {
const struct ieee80211_ops *ops;
- /* AC queue corresponding to each AMPDU queue */
- s8 ampdu_ac_queue[IEEE80211_MAX_AMPDU_QUEUES];
- unsigned int amdpu_ac_stop_refcnt[IEEE80211_MAX_AMPDU_QUEUES];
-
- unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES +
- IEEE80211_MAX_AMPDU_QUEUES];
+ unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES];
/* also used to protect ampdu_ac_queue and amdpu_ac_stop_refcnt */
spinlock_t queue_stop_reason_lock;
@@ -654,11 +631,17 @@ struct ieee80211_local {
struct sta_info *sta_hash[STA_HASH_SIZE];
struct timer_list sta_cleanup;
- unsigned long queues_pending[BITS_TO_LONGS(IEEE80211_MAX_QUEUES)];
- unsigned long queues_pending_run[BITS_TO_LONGS(IEEE80211_MAX_QUEUES)];
- struct ieee80211_tx_stored_packet pending_packet[IEEE80211_MAX_QUEUES];
+ struct sk_buff_head pending[IEEE80211_MAX_QUEUES];
struct tasklet_struct tx_pending_tasklet;
+ /*
+ * This lock is used to prevent concurrent A-MPDU
+ * session start/stop processing, this thus also
+ * synchronises the ->ampdu_action() callback to
+ * drivers and limits it to one at a time.
+ */
+ spinlock_t ampdu_lock;
+
/* number of interfaces with corresponding IFF_ flags */
atomic_t iff_allmultis, iff_promiscs;
@@ -774,6 +757,7 @@ struct ieee80211_local {
struct dentry *total_ps_buffered;
struct dentry *wep_iv;
struct dentry *tsf;
+ struct dentry *reset;
struct dentry *statistics;
struct local_debugfsdentries_statsdentries {
struct dentry *transmitted_fragment_count;
@@ -969,7 +953,7 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb,
struct ieee80211_rx_status *rx_status);
int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata,
- char *ie, size_t len);
+ const char *ie, size_t len);
void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local);
void ieee80211_scan_failed(struct ieee80211_local *local);
@@ -1053,8 +1037,19 @@ void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
u8 pwr_constr_elem_len);
/* Suspend/resume */
+#ifdef CONFIG_PM
int __ieee80211_suspend(struct ieee80211_hw *hw);
int __ieee80211_resume(struct ieee80211_hw *hw);
+#else
+static inline int __ieee80211_suspend(struct ieee80211_hw *hw)
+{
+ return 0;
+}
+static inline int __ieee80211_resume(struct ieee80211_hw *hw)
+{
+ return 0;
+}
+#endif
/* utility functions/constants */
extern void *mac80211_wiphy_privid; /* for wiphy privid */
@@ -1081,6 +1076,9 @@ void ieee80211_dynamic_ps_timer(unsigned long data);
void ieee80211_send_nullfunc(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
int powersave);
+void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_hdr *hdr);
+void ieee80211_beacon_loss_work(struct work_struct *work);
void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
enum queue_stop_reason reason);
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index f9f27b9cadbe..91e8e1bacaaa 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -261,8 +261,7 @@ static int ieee80211_open(struct net_device *dev)
ieee80211_bss_info_change_notify(sdata, changed);
ieee80211_enable_keys(sdata);
- if (sdata->vif.type == NL80211_IFTYPE_STATION &&
- !(sdata->flags & IEEE80211_SDATA_USERSPACE_MLME))
+ if (sdata->vif.type == NL80211_IFTYPE_STATION)
netif_carrier_off(dev);
else
netif_carrier_on(dev);
@@ -478,6 +477,9 @@ static int ieee80211_stop(struct net_device *dev)
*/
cancel_work_sync(&sdata->u.mgd.work);
cancel_work_sync(&sdata->u.mgd.chswitch_work);
+
+ cancel_work_sync(&sdata->u.mgd.beacon_loss_work);
+
/*
* When we get here, the interface is marked down.
* Call synchronize_rcu() to wait for the RX path
@@ -653,13 +655,7 @@ static void ieee80211_teardown_sdata(struct net_device *dev)
kfree(sdata->u.mgd.extra_ie);
kfree(sdata->u.mgd.assocreq_ies);
kfree(sdata->u.mgd.assocresp_ies);
- kfree(sdata->u.mgd.ie_probereq);
- kfree(sdata->u.mgd.ie_proberesp);
- kfree(sdata->u.mgd.ie_auth);
- kfree(sdata->u.mgd.ie_assocreq);
- kfree(sdata->u.mgd.ie_reassocreq);
- kfree(sdata->u.mgd.ie_deauth);
- kfree(sdata->u.mgd.ie_disassoc);
+ kfree(sdata->u.mgd.sme_auth_ie);
break;
case NL80211_IFTYPE_WDS:
case NL80211_IFTYPE_AP_VLAN:
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index f38db4d37e5d..a6f1d8a869bc 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -161,12 +161,6 @@ int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed)
if (WARN_ON(!netif_running(sdata->dev)))
return 0;
- if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN))
- return -EINVAL;
-
- if (!local->ops->config_interface)
- return 0;
-
memset(&conf, 0, sizeof(conf));
if (sdata->vif.type == NL80211_IFTYPE_STATION)
@@ -183,6 +177,9 @@ int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed)
return -EINVAL;
}
+ if (!local->ops->config_interface)
+ return 0;
+
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_ADHOC:
@@ -224,9 +221,6 @@ int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed)
}
}
- if (WARN_ON(!conf.bssid && (changed & IEEE80211_IFCC_BSSID)))
- return -EINVAL;
-
conf.changed = changed;
return local->ops->config_interface(local_to_hw(local),
@@ -780,13 +774,10 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
setup_timer(&local->dynamic_ps_timer,
ieee80211_dynamic_ps_timer, (unsigned long) local);
- for (i = 0; i < IEEE80211_MAX_AMPDU_QUEUES; i++)
- local->ampdu_ac_queue[i] = -1;
- /* using an s8 won't work with more than that */
- BUILD_BUG_ON(IEEE80211_MAX_AMPDU_QUEUES > 127);
-
sta_info_init(local);
+ for (i = 0; i < IEEE80211_MAX_QUEUES; i++)
+ skb_queue_head_init(&local->pending[i]);
tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending,
(unsigned long)local);
tasklet_disable(&local->tx_pending_tasklet);
@@ -799,6 +790,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
skb_queue_head_init(&local->skb_queue);
skb_queue_head_init(&local->skb_queue_unreliable);
+ spin_lock_init(&local->ampdu_lock);
+
return local_to_hw(local);
}
EXPORT_SYMBOL(ieee80211_alloc_hw);
@@ -876,10 +869,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
*/
if (hw->queues > IEEE80211_MAX_QUEUES)
hw->queues = IEEE80211_MAX_QUEUES;
- if (hw->ampdu_queues > IEEE80211_MAX_AMPDU_QUEUES)
- hw->ampdu_queues = IEEE80211_MAX_AMPDU_QUEUES;
- if (hw->queues < 4)
- hw->ampdu_queues = 0;
mdev = alloc_netdev_mq(sizeof(struct ieee80211_master_priv),
"wmaster%d", ieee80211_master_setup,
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 841b8450b3de..7ecda9d59d8a 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -30,7 +30,7 @@
#define IEEE80211_ASSOC_TIMEOUT (HZ / 5)
#define IEEE80211_ASSOC_MAX_TRIES 3
#define IEEE80211_MONITORING_INTERVAL (2 * HZ)
-#define IEEE80211_PROBE_INTERVAL (60 * HZ)
+#define IEEE80211_PROBE_IDLE_TIME (60 * HZ)
#define IEEE80211_RETRY_AUTH_INTERVAL (1 * HZ)
/* utils */
@@ -82,38 +82,23 @@ static int ieee80211_compatible_rates(struct ieee80211_bss *bss,
/* frame sending functions */
-static void add_extra_ies(struct sk_buff *skb, u8 *ies, size_t ies_len)
-{
- if (ies)
- memcpy(skb_put(skb, ies_len), ies, ies_len);
-}
-
static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
- u8 *pos, *ies, *ht_ie, *e_ies;
+ u8 *pos, *ies, *ht_ie;
int i, len, count, rates_len, supp_rates_len;
u16 capab;
struct ieee80211_bss *bss;
int wmm = 0;
struct ieee80211_supported_band *sband;
u32 rates = 0;
- size_t e_ies_len;
-
- if (ifmgd->flags & IEEE80211_IBSS_PREV_BSSID_SET) {
- e_ies = sdata->u.mgd.ie_reassocreq;
- e_ies_len = sdata->u.mgd.ie_reassocreq_len;
- } else {
- e_ies = sdata->u.mgd.ie_assocreq;
- e_ies_len = sdata->u.mgd.ie_assocreq_len;
- }
skb = dev_alloc_skb(local->hw.extra_tx_headroom +
sizeof(*mgmt) + 200 + ifmgd->extra_ie_len +
- ifmgd->ssid_len + e_ies_len);
+ ifmgd->ssid_len);
if (!skb) {
printk(KERN_DEBUG "%s: failed to allocate buffer for assoc "
"frame\n", sdata->dev->name);
@@ -304,8 +289,6 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
}
- add_extra_ies(skb, e_ies, e_ies_len);
-
kfree(ifmgd->assocreq_ies);
ifmgd->assocreq_ies_len = (skb->data + skb->len) - ies;
ifmgd->assocreq_ies = kmalloc(ifmgd->assocreq_ies_len, GFP_KERNEL);
@@ -323,19 +306,8 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
- u8 *ies;
- size_t ies_len;
-
- if (stype == IEEE80211_STYPE_DEAUTH) {
- ies = sdata->u.mgd.ie_deauth;
- ies_len = sdata->u.mgd.ie_deauth_len;
- } else {
- ies = sdata->u.mgd.ie_disassoc;
- ies_len = sdata->u.mgd.ie_disassoc_len;
- }
- skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) +
- ies_len);
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt));
if (!skb) {
printk(KERN_DEBUG "%s: failed to allocate buffer for "
"deauth/disassoc frame\n", sdata->dev->name);
@@ -353,8 +325,6 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
/* u.deauth.reason_code == u.disassoc.reason_code */
mgmt->u.deauth.reason_code = cpu_to_le16(reason);
- add_extra_ies(skb, ies, ies_len);
-
ieee80211_tx_skb(sdata, skb, ifmgd->flags & IEEE80211_STA_MFP_ENABLED);
}
@@ -640,6 +610,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
bss_info_changed |= ieee80211_handle_bss_capability(sdata,
bss->cbss.capability, bss->has_erp_value, bss->erp_value);
+ cfg80211_hold_bss(&bss->cbss);
+
ieee80211_rx_bss_put(local, bss);
}
@@ -682,6 +654,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ struct ieee80211_local *local = sdata->local;
ifmgd->direct_probe_tries++;
if (ifmgd->direct_probe_tries > IEEE80211_AUTH_MAX_TRIES) {
@@ -697,6 +670,13 @@ static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata)
ieee80211_rx_bss_remove(sdata, ifmgd->bssid,
sdata->local->hw.conf.channel->center_freq,
ifmgd->ssid, ifmgd->ssid_len);
+
+ /*
+ * We might have a pending scan which had no chance to run yet
+ * due to state == IEEE80211_STA_MLME_DIRECT_PROBE.
+ * Hence, queue the STAs work again
+ */
+ queue_work(local->hw.workqueue, &ifmgd->work);
return;
}
@@ -721,6 +701,9 @@ static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata)
static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ struct ieee80211_local *local = sdata->local;
+ u8 *ies;
+ size_t ies_len;
ifmgd->auth_tries++;
if (ifmgd->auth_tries > IEEE80211_AUTH_MAX_TRIES) {
@@ -732,6 +715,13 @@ static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata)
ieee80211_rx_bss_remove(sdata, ifmgd->bssid,
sdata->local->hw.conf.channel->center_freq,
ifmgd->ssid, ifmgd->ssid_len);
+
+ /*
+ * We might have a pending scan which had no chance to run yet
+ * due to state == IEEE80211_STA_MLME_AUTHENTICATE.
+ * Hence, queue the STAs work again
+ */
+ queue_work(local->hw.workqueue, &ifmgd->work);
return;
}
@@ -739,7 +729,14 @@ static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata)
printk(KERN_DEBUG "%s: authenticate with AP %pM\n",
sdata->dev->name, ifmgd->bssid);
- ieee80211_send_auth(sdata, 1, ifmgd->auth_alg, NULL, 0,
+ if (ifmgd->flags & IEEE80211_STA_EXT_SME) {
+ ies = ifmgd->sme_auth_ie;
+ ies_len = ifmgd->sme_auth_ie_len;
+ } else {
+ ies = NULL;
+ ies_len = 0;
+ }
+ ieee80211_send_auth(sdata, 1, ifmgd->auth_alg, ies, ies_len,
ifmgd->bssid, 0);
ifmgd->auth_transaction = 2;
@@ -756,6 +753,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
+ struct ieee80211_conf *conf = &local_to_hw(local)->conf;
+ struct ieee80211_bss *bss;
struct sta_info *sta;
u32 changed = 0, config_changed = 0;
@@ -779,6 +778,15 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
ieee80211_sta_tear_down_BA_sessions(sta);
+ bss = ieee80211_rx_bss_get(local, ifmgd->bssid,
+ conf->channel->center_freq,
+ ifmgd->ssid, ifmgd->ssid_len);
+
+ if (bss) {
+ cfg80211_unhold_bss(&bss->cbss);
+ ieee80211_rx_bss_put(local, bss);
+ }
+
if (self_disconnected) {
if (deauth)
ieee80211_send_deauth_disassoc(sdata,
@@ -854,7 +862,7 @@ static int ieee80211_privacy_mismatch(struct ieee80211_sub_if_data *sdata)
int wep_privacy;
int privacy_invoked;
- if (!ifmgd || (ifmgd->flags & IEEE80211_STA_MIXED_CELL))
+ if (!ifmgd || (ifmgd->flags & IEEE80211_STA_EXT_SME))
return 0;
bss = ieee80211_rx_bss_get(local, ifmgd->bssid,
@@ -878,6 +886,7 @@ static int ieee80211_privacy_mismatch(struct ieee80211_sub_if_data *sdata)
static void ieee80211_associate(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ struct ieee80211_local *local = sdata->local;
ifmgd->assoc_tries++;
if (ifmgd->assoc_tries > IEEE80211_ASSOC_MAX_TRIES) {
@@ -889,6 +898,12 @@ static void ieee80211_associate(struct ieee80211_sub_if_data *sdata)
ieee80211_rx_bss_remove(sdata, ifmgd->bssid,
sdata->local->hw.conf.channel->center_freq,
ifmgd->ssid, ifmgd->ssid_len);
+ /*
+ * We might have a pending scan which had no chance to run yet
+ * due to state == IEEE80211_STA_MLME_ASSOCIATE.
+ * Hence, queue the STAs work again
+ */
+ queue_work(local->hw.workqueue, &ifmgd->work);
return;
}
@@ -907,13 +922,55 @@ static void ieee80211_associate(struct ieee80211_sub_if_data *sdata)
mod_timer(&ifmgd->timer, jiffies + IEEE80211_ASSOC_TIMEOUT);
}
+void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_hdr *hdr)
+{
+ /*
+ * We can postpone the mgd.timer whenever receiving unicast frames
+ * from AP because we know that the connection is working both ways
+ * at that time. But multicast frames (and hence also beacons) must
+ * be ignored here, because we need to trigger the timer during
+ * data idle periods for sending the periodical probe request to
+ * the AP.
+ */
+ if (!is_multicast_ether_addr(hdr->addr1))
+ mod_timer(&sdata->u.mgd.timer,
+ jiffies + IEEE80211_MONITORING_INTERVAL);
+}
+
+void ieee80211_beacon_loss_work(struct work_struct *work)
+{
+ struct ieee80211_sub_if_data *sdata =
+ container_of(work, struct ieee80211_sub_if_data,
+ u.mgd.beacon_loss_work);
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
+ printk(KERN_DEBUG "%s: driver reports beacon loss from AP %pM "
+ "- sending probe request\n", sdata->dev->name,
+ sdata->u.mgd.bssid);
+
+ ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL;
+ ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid,
+ ifmgd->ssid_len, NULL, 0);
+
+ mod_timer(&ifmgd->timer, jiffies + IEEE80211_MONITORING_INTERVAL);
+}
+
+void ieee80211_beacon_loss(struct ieee80211_vif *vif)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+ queue_work(sdata->local->hw.workqueue,
+ &sdata->u.mgd.beacon_loss_work);
+}
+EXPORT_SYMBOL(ieee80211_beacon_loss);
static void ieee80211_associated(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
- int disassoc;
+ bool disassoc = false;
/* TODO: start monitoring current AP signal quality and number of
* missed beacons. Scan other channels every now and then and search
@@ -928,36 +985,45 @@ static void ieee80211_associated(struct ieee80211_sub_if_data *sdata)
if (!sta) {
printk(KERN_DEBUG "%s: No STA entry for own AP %pM\n",
sdata->dev->name, ifmgd->bssid);
- disassoc = 1;
- } else {
- disassoc = 0;
- if (time_after(jiffies,
- sta->last_rx + IEEE80211_MONITORING_INTERVAL)) {
- if (ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) {
- printk(KERN_DEBUG "%s: No ProbeResp from "
- "current AP %pM - assume out of "
- "range\n",
- sdata->dev->name, ifmgd->bssid);
- disassoc = 1;
- } else
- ieee80211_send_probe_req(sdata, ifmgd->bssid,
- ifmgd->ssid,
- ifmgd->ssid_len,
- NULL, 0);
- ifmgd->flags ^= IEEE80211_STA_PROBEREQ_POLL;
- } else {
- ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL;
- if (time_after(jiffies, ifmgd->last_probe +
- IEEE80211_PROBE_INTERVAL)) {
- ifmgd->last_probe = jiffies;
- ieee80211_send_probe_req(sdata, ifmgd->bssid,
- ifmgd->ssid,
- ifmgd->ssid_len,
- NULL, 0);
- }
- }
+ disassoc = true;
+ goto unlock;
}
+ if ((ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) &&
+ time_after(jiffies, sta->last_rx + IEEE80211_MONITORING_INTERVAL)) {
+ printk(KERN_DEBUG "%s: no probe response from AP %pM "
+ "- disassociating\n",
+ sdata->dev->name, ifmgd->bssid);
+ disassoc = true;
+ ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL;
+ goto unlock;
+ }
+
+ /*
+ * Beacon filtering is only enabled with power save and then the
+ * stack should not check for beacon loss.
+ */
+ if (!((local->hw.flags & IEEE80211_HW_BEACON_FILTER) &&
+ (local->hw.conf.flags & IEEE80211_CONF_PS)) &&
+ time_after(jiffies,
+ ifmgd->last_beacon + IEEE80211_MONITORING_INTERVAL)) {
+ printk(KERN_DEBUG "%s: beacon loss from AP %pM "
+ "- sending probe request\n",
+ sdata->dev->name, ifmgd->bssid);
+ ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL;
+ ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid,
+ ifmgd->ssid_len, NULL, 0);
+ goto unlock;
+
+ }
+
+ if (time_after(jiffies, sta->last_rx + IEEE80211_PROBE_IDLE_TIME)) {
+ ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL;
+ ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid,
+ ifmgd->ssid_len, NULL, 0);
+ }
+
+ unlock:
rcu_read_unlock();
if (disassoc)
@@ -975,7 +1041,11 @@ static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata)
printk(KERN_DEBUG "%s: authenticated\n", sdata->dev->name);
ifmgd->flags |= IEEE80211_STA_AUTHENTICATED;
- ieee80211_associate(sdata);
+ if (ifmgd->flags & IEEE80211_STA_EXT_SME) {
+ /* Wait for SME to request association */
+ ifmgd->state = IEEE80211_STA_MLME_DISABLED;
+ } else
+ ieee80211_associate(sdata);
}
@@ -1061,12 +1131,15 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
switch (ifmgd->auth_alg) {
case WLAN_AUTH_OPEN:
case WLAN_AUTH_LEAP:
+ case WLAN_AUTH_FT:
ieee80211_auth_completed(sdata);
+ cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len);
break;
case WLAN_AUTH_SHARED_KEY:
- if (ifmgd->auth_transaction == 4)
+ if (ifmgd->auth_transaction == 4) {
ieee80211_auth_completed(sdata);
- else
+ cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len);
+ } else
ieee80211_auth_challenge(sdata, mgmt, len);
break;
}
@@ -1092,9 +1165,10 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
printk(KERN_DEBUG "%s: deauthenticated (Reason: %u)\n",
sdata->dev->name, reason_code);
- if (ifmgd->state == IEEE80211_STA_MLME_AUTHENTICATE ||
- ifmgd->state == IEEE80211_STA_MLME_ASSOCIATE ||
- ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) {
+ if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) &&
+ (ifmgd->state == IEEE80211_STA_MLME_AUTHENTICATE ||
+ ifmgd->state == IEEE80211_STA_MLME_ASSOCIATE ||
+ ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED)) {
ifmgd->state = IEEE80211_STA_MLME_DIRECT_PROBE;
mod_timer(&ifmgd->timer, jiffies +
IEEE80211_RETRY_AUTH_INTERVAL);
@@ -1102,6 +1176,7 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
ieee80211_set_disassoc(sdata, true, false, 0);
ifmgd->flags &= ~IEEE80211_STA_AUTHENTICATED;
+ cfg80211_send_rx_deauth(sdata->dev, (u8 *) mgmt, len);
}
@@ -1124,13 +1199,15 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
printk(KERN_DEBUG "%s: disassociated (Reason: %u)\n",
sdata->dev->name, reason_code);
- if (ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) {
+ if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) &&
+ ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) {
ifmgd->state = IEEE80211_STA_MLME_ASSOCIATE;
mod_timer(&ifmgd->timer, jiffies +
IEEE80211_RETRY_AUTH_INTERVAL);
}
ieee80211_set_disassoc(sdata, false, false, reason_code);
+ cfg80211_send_rx_disassoc(sdata->dev, (u8 *) mgmt, len);
}
@@ -1346,7 +1423,14 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
bss_conf->assoc_capability = capab_info;
ieee80211_set_associated(sdata, changed);
+ /*
+ * initialise the time of last beacon to be the association time,
+ * otherwise beacon loss check will trigger immediately
+ */
+ ifmgd->last_beacon = jiffies;
+
ieee80211_associated(sdata);
+ cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, len);
}
@@ -1393,9 +1477,12 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
size_t len,
struct ieee80211_rx_status *rx_status)
{
+ struct ieee80211_if_managed *ifmgd;
size_t baselen;
struct ieee802_11_elems elems;
+ ifmgd = &sdata->u.mgd;
+
if (memcmp(mgmt->da, sdata->dev->dev_addr, ETH_ALEN))
return; /* ignore ProbeResp to foreign address */
@@ -1410,11 +1497,14 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
/* direct probe may be part of the association flow */
if (test_and_clear_bit(IEEE80211_STA_REQ_DIRECT_PROBE,
- &sdata->u.mgd.request)) {
+ &ifmgd->request)) {
printk(KERN_DEBUG "%s direct probe responded\n",
sdata->dev->name);
ieee80211_authenticate(sdata);
}
+
+ if (ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL)
+ ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL;
}
static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
@@ -1636,6 +1726,8 @@ static void ieee80211_sta_reset_auth(struct ieee80211_sub_if_data *sdata)
ifmgd->auth_alg = WLAN_AUTH_SHARED_KEY;
else if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_LEAP)
ifmgd->auth_alg = WLAN_AUTH_LEAP;
+ else if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_FT)
+ ifmgd->auth_alg = WLAN_AUTH_FT;
else
ifmgd->auth_alg = WLAN_AUTH_OPEN;
ifmgd->auth_transaction = -1;
@@ -1659,7 +1751,8 @@ static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata)
u16 capa_val = WLAN_CAPABILITY_ESS;
struct ieee80211_channel *chan = local->oper_channel;
- if (ifmgd->flags & (IEEE80211_STA_AUTO_SSID_SEL |
+ if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) &&
+ ifmgd->flags & (IEEE80211_STA_AUTO_SSID_SEL |
IEEE80211_STA_AUTO_BSSID_SEL |
IEEE80211_STA_AUTO_CHANNEL_SEL)) {
capa_mask |= WLAN_CAPABILITY_PRIVACY;
@@ -1822,6 +1915,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
ifmgd = &sdata->u.mgd;
INIT_WORK(&ifmgd->work, ieee80211_sta_work);
INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work);
+ INIT_WORK(&ifmgd->beacon_loss_work, ieee80211_beacon_loss_work);
setup_timer(&ifmgd->timer, ieee80211_sta_timer,
(unsigned long) sdata);
setup_timer(&ifmgd->chswitch_timer, ieee80211_chswitch_timer,
@@ -1834,7 +1928,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
ifmgd->flags |= IEEE80211_STA_CREATE_IBSS |
IEEE80211_STA_AUTO_BSSID_SEL |
IEEE80211_STA_AUTO_CHANNEL_SEL;
- if (ieee80211_num_regular_queues(&sdata->local->hw) >= 4)
+ if (sdata->local->hw.queues >= 4)
ifmgd->flags |= IEEE80211_STA_WMM_ENABLED;
}
@@ -1856,7 +1950,11 @@ void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata)
ieee80211_set_disassoc(sdata, true, true,
WLAN_REASON_DEAUTH_LEAVING);
- set_bit(IEEE80211_STA_REQ_AUTH, &ifmgd->request);
+ if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) ||
+ ifmgd->state != IEEE80211_STA_MLME_ASSOCIATE)
+ set_bit(IEEE80211_STA_REQ_AUTH, &ifmgd->request);
+ else if (ifmgd->flags & IEEE80211_STA_EXT_SME)
+ set_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request);
queue_work(local->hw.workqueue, &ifmgd->work);
}
}
@@ -1865,8 +1963,6 @@ int ieee80211_sta_commit(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- ifmgd->flags &= ~IEEE80211_STA_PREV_BSSID_SET;
-
if (ifmgd->ssid_len)
ifmgd->flags |= IEEE80211_STA_SSID_SET;
else
@@ -1885,6 +1981,10 @@ int ieee80211_sta_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size
ifmgd = &sdata->u.mgd;
if (ifmgd->ssid_len != len || memcmp(ifmgd->ssid, ssid, len) != 0) {
+ /*
+ * Do not use reassociation if SSID is changed (different ESS).
+ */
+ ifmgd->flags &= ~IEEE80211_STA_PREV_BSSID_SET;
memset(ifmgd->ssid, 0, sizeof(ifmgd->ssid));
memcpy(ifmgd->ssid, ssid, len);
ifmgd->ssid_len = len;
@@ -1923,7 +2023,8 @@ int ieee80211_sta_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid)
return ieee80211_sta_commit(sdata);
}
-int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata, char *ie, size_t len)
+int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata,
+ const char *ie, size_t len)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index 44525f517077..027302326498 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -10,6 +10,10 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
struct ieee80211_sub_if_data *sdata;
struct ieee80211_if_init_conf conf;
struct sta_info *sta;
+ unsigned long flags;
+
+ ieee80211_stop_queues_by_reason(hw,
+ IEEE80211_QUEUE_STOP_REASON_SUSPEND);
flush_workqueue(local->hw.workqueue);
@@ -17,10 +21,23 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
list_for_each_entry(sdata, &local->interfaces, list)
ieee80211_disable_keys(sdata);
- /* remove STAs */
- list_for_each_entry(sta, &local->sta_list, list) {
+ /* Tear down aggregation sessions */
+
+ rcu_read_lock();
+
+ if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
+ list_for_each_entry_rcu(sta, &local->sta_list, list) {
+ set_sta_flags(sta, WLAN_STA_SUSPEND);
+ ieee80211_sta_tear_down_BA_sessions(sta);
+ }
+ }
- if (local->ops->sta_notify) {
+ rcu_read_unlock();
+
+ /* remove STAs */
+ if (local->ops->sta_notify) {
+ spin_lock_irqsave(&local->sta_lock, flags);
+ list_for_each_entry(sta, &local->sta_list, list) {
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
sdata = container_of(sdata->bss,
struct ieee80211_sub_if_data,
@@ -29,11 +46,11 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
local->ops->sta_notify(hw, &sdata->vif,
STA_NOTIFY_REMOVE, &sta->sta);
}
+ spin_unlock_irqrestore(&local->sta_lock, flags);
}
/* remove all interfaces */
list_for_each_entry(sdata, &local->interfaces, list) {
-
if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
sdata->vif.type != NL80211_IFTYPE_MONITOR &&
netif_running(sdata->dev)) {
@@ -61,6 +78,7 @@ int __ieee80211_resume(struct ieee80211_hw *hw)
struct ieee80211_sub_if_data *sdata;
struct ieee80211_if_init_conf conf;
struct sta_info *sta;
+ unsigned long flags;
int res;
/* restart hardware */
@@ -72,7 +90,6 @@ int __ieee80211_resume(struct ieee80211_hw *hw)
/* add interfaces */
list_for_each_entry(sdata, &local->interfaces, list) {
-
if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
sdata->vif.type != NL80211_IFTYPE_MONITOR &&
netif_running(sdata->dev)) {
@@ -84,9 +101,9 @@ int __ieee80211_resume(struct ieee80211_hw *hw)
}
/* add STAs back */
- list_for_each_entry(sta, &local->sta_list, list) {
-
- if (local->ops->sta_notify) {
+ if (local->ops->sta_notify) {
+ spin_lock_irqsave(&local->sta_lock, flags);
+ list_for_each_entry(sta, &local->sta_list, list) {
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
sdata = container_of(sdata->bss,
struct ieee80211_sub_if_data,
@@ -95,8 +112,21 @@ int __ieee80211_resume(struct ieee80211_hw *hw)
local->ops->sta_notify(hw, &sdata->vif,
STA_NOTIFY_ADD, &sta->sta);
}
+ spin_unlock_irqrestore(&local->sta_lock, flags);
}
+ /* Clear Suspend state so that ADDBA requests can be processed */
+
+ rcu_read_lock();
+
+ if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
+ list_for_each_entry_rcu(sta, &local->sta_list, list) {
+ clear_sta_flags(sta, WLAN_STA_SUSPEND);
+ }
+ }
+
+ rcu_read_unlock();
+
/* add back keys */
list_for_each_entry(sdata, &local->interfaces, list)
if (netif_running(sdata->dev))
@@ -113,5 +143,37 @@ int __ieee80211_resume(struct ieee80211_hw *hw)
ieee80211_configure_filter(local);
netif_addr_unlock_bh(local->mdev);
+ /* Finally also reconfigure all the BSS information */
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ u32 changed = ~0;
+ if (!netif_running(sdata->dev))
+ continue;
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_STATION:
+ /* disable beacon change bits */
+ changed &= ~IEEE80211_IFCC_BEACON;
+ /* fall through */
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_MESH_POINT:
+ WARN_ON(ieee80211_if_config(sdata, changed));
+ ieee80211_bss_info_change_notify(sdata, ~0);
+ break;
+ case NL80211_IFTYPE_WDS:
+ break;
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_MONITOR:
+ /* ignore virtual */
+ break;
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case __NL80211_IFTYPE_AFTER_LAST:
+ WARN_ON(1);
+ break;
+ }
+ }
+
+ ieee80211_wake_queues_by_reason(hw,
+ IEEE80211_QUEUE_STOP_REASON_SUSPEND);
+
return 0;
}
diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index 3fa7ab285066..4641f00a1e5c 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -219,10 +219,12 @@ void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,
info->control.rates[i].count = 1;
}
- if (sta && sdata->force_unicast_rateidx > -1)
+ if (sta && sdata->force_unicast_rateidx > -1) {
info->control.rates[0].idx = sdata->force_unicast_rateidx;
- else
+ } else {
ref->ops->get_rate(ref->priv, ista, priv_sta, txrc);
+ info->flags |= IEEE80211_TX_INTFL_RCALGO;
+ }
/*
* try to enforce the maximum rate the user wanted
diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h
index b9164c9a9563..2ab5ad9e71ce 100644
--- a/net/mac80211/rate.h
+++ b/net/mac80211/rate.h
@@ -44,8 +44,10 @@ static inline void rate_control_tx_status(struct ieee80211_local *local,
struct rate_control_ref *ref = local->rate_ctrl;
struct ieee80211_sta *ista = &sta->sta;
void *priv_sta = sta->rate_ctrl_priv;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb);
+ if (likely(info->flags & IEEE80211_TX_INTFL_RCALGO))
+ ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb);
}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 66f7ecf51b92..64ebe664effc 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -142,6 +142,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
/* IEEE80211_RADIOTAP_FLAGS */
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
*pos |= IEEE80211_RADIOTAP_F_FCS;
+ if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC))
+ *pos |= IEEE80211_RADIOTAP_F_BADFCS;
if (status->flag & RX_FLAG_SHORTPRE)
*pos |= IEEE80211_RADIOTAP_F_SHORTPRE;
pos++;
@@ -204,9 +206,8 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
/* ensure 2 byte alignment for the 2 byte field as required */
if ((pos - (unsigned char *)rthdr) & 1)
pos++;
- /* FIXME: when radiotap gets a 'bad PLCP' flag use it here */
- if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC))
- *(__le16 *)pos |= cpu_to_le16(IEEE80211_RADIOTAP_F_RX_BADFCS);
+ if (status->flag & RX_FLAG_FAILED_PLCP_CRC)
+ *(__le16 *)pos |= cpu_to_le16(IEEE80211_RADIOTAP_F_RX_BADPLCP);
pos += 2;
}
@@ -849,12 +850,19 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
* Mesh beacons will update last_rx when if they are found to
* match the current local configuration when processed.
*/
- sta->last_rx = jiffies;
+ if (rx->sdata->vif.type == NL80211_IFTYPE_STATION &&
+ ieee80211_is_beacon(hdr->frame_control)) {
+ rx->sdata->u.mgd.last_beacon = jiffies;
+ } else
+ sta->last_rx = jiffies;
}
if (!(rx->flags & IEEE80211_RX_RA_MATCH))
return RX_CONTINUE;
+ if (rx->sdata->vif.type == NL80211_IFTYPE_STATION)
+ ieee80211_sta_rx_notify(rx->sdata, hdr);
+
sta->rx_fragments++;
sta->rx_bytes += rx->skb->len;
sta->last_signal = rx->status->signal;
@@ -1876,18 +1884,13 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
if (ieee80211_vif_is_mesh(&sdata->vif))
return ieee80211_mesh_rx_mgmt(sdata, rx->skb, rx->status);
- if (sdata->vif.type != NL80211_IFTYPE_STATION &&
- sdata->vif.type != NL80211_IFTYPE_ADHOC)
- return RX_DROP_MONITOR;
-
+ if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+ return ieee80211_ibss_rx_mgmt(sdata, rx->skb, rx->status);
- if (sdata->vif.type == NL80211_IFTYPE_STATION) {
- if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME)
- return RX_DROP_MONITOR;
+ if (sdata->vif.type == NL80211_IFTYPE_STATION)
return ieee80211_sta_rx_mgmt(sdata, rx->skb, rx->status);
- }
- return ieee80211_ibss_rx_mgmt(sdata, rx->skb, rx->status);
+ return RX_DROP_MONITOR;
}
static void ieee80211_rx_michael_mic_report(struct net_device *dev,
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 5030a3c87509..3bf9839f5916 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -214,6 +214,66 @@ void ieee80211_scan_failed(struct ieee80211_local *local)
local->scan_req = NULL;
}
+/*
+ * inform AP that we will go to sleep so that it will buffer the frames
+ * while we scan
+ */
+static void ieee80211_scan_ps_enable(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ bool ps = false;
+
+ /* FIXME: what to do when local->pspolling is true? */
+
+ del_timer_sync(&local->dynamic_ps_timer);
+ cancel_work_sync(&local->dynamic_ps_enable_work);
+
+ if (local->hw.conf.flags & IEEE80211_CONF_PS) {
+ ps = true;
+ local->hw.conf.flags &= ~IEEE80211_CONF_PS;
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+ }
+
+ if (!ps || !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK))
+ /*
+ * If power save was enabled, no need to send a nullfunc
+ * frame because AP knows that we are sleeping. But if the
+ * hardware is creating the nullfunc frame for power save
+ * status (ie. IEEE80211_HW_PS_NULLFUNC_STACK is not
+ * enabled) and power save was enabled, the firmware just
+ * sent a null frame with power save disabled. So we need
+ * to send a new nullfunc frame to inform the AP that we
+ * are again sleeping.
+ */
+ ieee80211_send_nullfunc(local, sdata, 1);
+}
+
+/* inform AP that we are awake again, unless power save is enabled */
+static void ieee80211_scan_ps_disable(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+
+ if (!local->powersave)
+ ieee80211_send_nullfunc(local, sdata, 0);
+ else {
+ /*
+ * In !IEEE80211_HW_PS_NULLFUNC_STACK case the hardware
+ * will send a nullfunc frame with the powersave bit set
+ * even though the AP already knows that we are sleeping.
+ * This could be avoided by sending a null frame with power
+ * save bit disabled before enabling the power save, but
+ * this doesn't gain anything.
+ *
+ * When IEEE80211_HW_PS_NULLFUNC_STACK is enabled, no need
+ * to send a nullfunc frame because AP already knows that
+ * we are sleeping, let's just enable power save mode in
+ * hardware.
+ */
+ local->hw.conf.flags |= IEEE80211_CONF_PS;
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+ }
+}
+
void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
{
struct ieee80211_local *local = hw_to_local(hw);
@@ -268,7 +328,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
/* Tell AP we're back */
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
if (sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED) {
- ieee80211_send_nullfunc(local, sdata, 0);
+ ieee80211_scan_ps_disable(sdata);
netif_tx_wake_all_queues(sdata->dev);
}
} else
@@ -409,6 +469,19 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata,
return 0;
}
+ /*
+ * Hardware/driver doesn't support hw_scan, so use software
+ * scanning instead. First send a nullfunc frame with power save
+ * bit on so that AP will buffer the frames for us while we are not
+ * listening, then send probe requests to each channel and wait for
+ * the responses. After all channels are scanned, tune back to the
+ * original channel and send a nullfunc frame with power save bit
+ * off to trigger the AP to send us all the buffered frames.
+ *
+ * Note that while local->sw_scanning is true everything else but
+ * nullfunc frames and probe requests will be dropped in
+ * ieee80211_tx_h_check_assoc().
+ */
local->sw_scanning = true;
if (local->ops->sw_scan_start)
local->ops->sw_scan_start(local_to_hw(local));
@@ -428,7 +501,7 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata,
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
if (sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED) {
netif_tx_stop_all_queues(sdata->dev);
- ieee80211_send_nullfunc(local, sdata, 1);
+ ieee80211_scan_ps_enable(sdata);
}
} else
netif_tx_stop_all_queues(sdata->dev);
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 4ba3c540fcf3..c5f14e6bbde2 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -203,17 +203,6 @@ void sta_info_destroy(struct sta_info *sta)
if (tid_rx)
tid_rx->shutdown = true;
- /*
- * The stop callback cannot find this station any more, but
- * it didn't complete its work -- start the queue if necessary
- */
- if (sta->ampdu_mlme.tid_state_tx[i] & HT_AGG_STATE_INITIATOR_MSK &&
- sta->ampdu_mlme.tid_state_tx[i] & HT_AGG_STATE_REQ_STOP_BA_MSK &&
- local->hw.ampdu_queues)
- ieee80211_wake_queue_by_reason(&local->hw,
- local->hw.queues + sta->tid_to_tx_q[i],
- IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
-
spin_unlock_bh(&sta->lock);
/*
@@ -239,6 +228,11 @@ void sta_info_destroy(struct sta_info *sta)
tid_tx = sta->ampdu_mlme.tid_tx[i];
if (tid_tx) {
del_timer_sync(&tid_tx->addba_resp_timer);
+ /*
+ * STA removed while aggregation session being
+ * started? Bit odd, but purge frames anyway.
+ */
+ skb_queue_purge(&tid_tx->pending);
kfree(tid_tx);
}
}
@@ -287,7 +281,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
* enable session_timer's data differentiation. refer to
* sta_rx_agg_session_timer_expired for useage */
sta->timer_to_tid[i] = i;
- sta->tid_to_tx_q[i] = -1;
/* rx */
sta->ampdu_mlme.tid_state_rx[i] = HT_AGG_STATE_IDLE;
sta->ampdu_mlme.tid_rx[i] = NULL;
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 1f45573c580c..5534d489f506 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -35,6 +35,8 @@
* IEEE80211_TX_CTL_CLEAR_PS_FILT control flag) when the next
* frame to this station is transmitted.
* @WLAN_STA_MFP: Management frame protection is used with this STA.
+ * @WLAN_STA_SUSPEND: Set/cleared during a suspend/resume cycle.
+ * Used to deny ADDBA requests (both TX and RX).
*/
enum ieee80211_sta_info_flags {
WLAN_STA_AUTH = 1<<0,
@@ -48,6 +50,7 @@ enum ieee80211_sta_info_flags {
WLAN_STA_PSPOLL = 1<<8,
WLAN_STA_CLEAR_PS_FILT = 1<<9,
WLAN_STA_MFP = 1<<10,
+ WLAN_STA_SUSPEND = 1<<11
};
#define STA_TID_NUM 16
@@ -70,11 +73,13 @@ enum ieee80211_sta_info_flags {
* struct tid_ampdu_tx - TID aggregation information (Tx).
*
* @addba_resp_timer: timer for peer's response to addba request
+ * @pending: pending frames queue -- use sta's spinlock to protect
* @ssn: Starting Sequence Number expected to be aggregated.
* @dialog_token: dialog token for aggregation session
*/
struct tid_ampdu_tx {
struct timer_list addba_resp_timer;
+ struct sk_buff_head pending;
u16 ssn;
u8 dialog_token;
};
@@ -201,7 +206,6 @@ struct sta_ampdu_mlme {
* @tid_seq: per-TID sequence numbers for sending to this STA
* @ampdu_mlme: A-MPDU state machine state
* @timer_to_tid: identity mapping to ID timers
- * @tid_to_tx_q: map tid to tx queue (invalid == negative values)
* @llid: Local link ID
* @plid: Peer link ID
* @reason: Cancel reason on PLINK_HOLDING state
@@ -276,7 +280,6 @@ struct sta_info {
*/
struct sta_ampdu_mlme ampdu_mlme;
u8 timer_to_tid[STA_TID_NUM];
- s8 tid_to_tx_q[STA_TID_NUM];
#ifdef CONFIG_MAC80211_MESH
/*
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 457238a2f3fc..3fb04a86444d 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -34,8 +34,7 @@
#define IEEE80211_TX_OK 0
#define IEEE80211_TX_AGAIN 1
-#define IEEE80211_TX_FRAG_AGAIN 2
-#define IEEE80211_TX_PENDING 3
+#define IEEE80211_TX_PENDING 2
/* misc utils */
@@ -193,7 +192,19 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
return TX_CONTINUE;
if (unlikely(tx->local->sw_scanning) &&
- !ieee80211_is_probe_req(hdr->frame_control))
+ !ieee80211_is_probe_req(hdr->frame_control) &&
+ !ieee80211_is_nullfunc(hdr->frame_control))
+ /*
+ * When software scanning only nullfunc frames (to notify
+ * the sleep state to the AP) and probe requests (for the
+ * active scan) are allowed, all other frames should not be
+ * sent and we should not get here, but if we do
+ * nonetheless, drop them to avoid sending them
+ * off-channel. See the link below and
+ * ieee80211_start_scan() for more.
+ *
+ * http://article.gmane.org/gmane.linux.kernel.wireless.general/30089
+ */
return TX_DROP;
if (tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
@@ -690,17 +701,62 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
return TX_CONTINUE;
}
+static int ieee80211_fragment(struct ieee80211_local *local,
+ struct sk_buff *skb, int hdrlen,
+ int frag_threshold)
+{
+ struct sk_buff *tail = skb, *tmp;
+ int per_fragm = frag_threshold - hdrlen - FCS_LEN;
+ int pos = hdrlen + per_fragm;
+ int rem = skb->len - hdrlen - per_fragm;
+
+ if (WARN_ON(rem < 0))
+ return -EINVAL;
+
+ while (rem) {
+ int fraglen = per_fragm;
+
+ if (fraglen > rem)
+ fraglen = rem;
+ rem -= fraglen;
+ tmp = dev_alloc_skb(local->tx_headroom +
+ frag_threshold +
+ IEEE80211_ENCRYPT_HEADROOM +
+ IEEE80211_ENCRYPT_TAILROOM);
+ if (!tmp)
+ return -ENOMEM;
+ tail->next = tmp;
+ tail = tmp;
+ skb_reserve(tmp, local->tx_headroom +
+ IEEE80211_ENCRYPT_HEADROOM);
+ /* copy control information */
+ memcpy(tmp->cb, skb->cb, sizeof(tmp->cb));
+ skb_copy_queue_mapping(tmp, skb);
+ tmp->priority = skb->priority;
+ tmp->do_not_encrypt = skb->do_not_encrypt;
+ tmp->dev = skb->dev;
+ tmp->iif = skb->iif;
+
+ /* copy header and data */
+ memcpy(skb_put(tmp, hdrlen), skb->data, hdrlen);
+ memcpy(skb_put(tmp, fraglen), skb->data + pos, fraglen);
+
+ pos += fraglen;
+ }
+
+ skb->len = hdrlen + per_fragm;
+ return 0;
+}
+
static ieee80211_tx_result debug_noinline
ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
{
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
- size_t hdrlen, per_fragm, num_fragm, payload_len, left;
- struct sk_buff **frags, *first, *frag;
- int i;
- u16 seq;
- u8 *pos;
+ struct sk_buff *skb = tx->skb;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (void *)skb->data;
int frag_threshold = tx->local->fragmentation_threshold;
+ int hdrlen;
+ int fragnum;
if (!(tx->flags & IEEE80211_TX_FRAGMENTED))
return TX_CONTINUE;
@@ -713,58 +769,35 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU))
return TX_DROP;
- first = tx->skb;
-
hdrlen = ieee80211_hdrlen(hdr->frame_control);
- payload_len = first->len - hdrlen;
- per_fragm = frag_threshold - hdrlen - FCS_LEN;
- num_fragm = DIV_ROUND_UP(payload_len, per_fragm);
-
- frags = kzalloc(num_fragm * sizeof(struct sk_buff *), GFP_ATOMIC);
- if (!frags)
- goto fail;
-
- hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREFRAGS);
- seq = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ;
- pos = first->data + hdrlen + per_fragm;
- left = payload_len - per_fragm;
- for (i = 0; i < num_fragm - 1; i++) {
- struct ieee80211_hdr *fhdr;
- size_t copylen;
-
- if (left <= 0)
- goto fail;
- /* reserve enough extra head and tail room for possible
- * encryption */
- frag = frags[i] =
- dev_alloc_skb(tx->local->tx_headroom +
- frag_threshold +
- IEEE80211_ENCRYPT_HEADROOM +
- IEEE80211_ENCRYPT_TAILROOM);
- if (!frag)
- goto fail;
-
- /* Make sure that all fragments use the same priority so
- * that they end up using the same TX queue */
- frag->priority = first->priority;
+ /* internal error, why is TX_FRAGMENTED set? */
+ if (WARN_ON(skb->len <= frag_threshold))
+ return TX_DROP;
- skb_reserve(frag, tx->local->tx_headroom +
- IEEE80211_ENCRYPT_HEADROOM);
+ /*
+ * Now fragment the frame. This will allocate all the fragments and
+ * chain them (using skb as the first fragment) to skb->next.
+ * During transmission, we will remove the successfully transmitted
+ * fragments from this list. When the low-level driver rejects one
+ * of the fragments then we will simply pretend to accept the skb
+ * but store it away as pending.
+ */
+ if (ieee80211_fragment(tx->local, skb, hdrlen, frag_threshold))
+ return TX_DROP;
- /* copy TX information */
- info = IEEE80211_SKB_CB(frag);
- memcpy(info, first->cb, sizeof(frag->cb));
+ /* update duration/seq/flags of fragments */
+ fragnum = 0;
+ do {
+ int next_len;
+ const __le16 morefrags = cpu_to_le16(IEEE80211_FCTL_MOREFRAGS);
- /* copy/fill in 802.11 header */
- fhdr = (struct ieee80211_hdr *) skb_put(frag, hdrlen);
- memcpy(fhdr, first->data, hdrlen);
- fhdr->seq_ctrl = cpu_to_le16(seq | ((i + 1) & IEEE80211_SCTL_FRAG));
+ hdr = (void *)skb->data;
+ info = IEEE80211_SKB_CB(skb);
- if (i == num_fragm - 2) {
- /* clear MOREFRAGS bit for the last fragment */
- fhdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREFRAGS);
- } else {
+ if (skb->next) {
+ hdr->frame_control |= morefrags;
+ next_len = skb->next->len;
/*
* No multi-rate retries for fragmented frames, that
* would completely throw off the NAV at other STAs.
@@ -775,37 +808,16 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
info->control.rates[4].idx = -1;
BUILD_BUG_ON(IEEE80211_TX_MAX_RATES != 5);
info->flags &= ~IEEE80211_TX_CTL_RATE_CTRL_PROBE;
+ } else {
+ hdr->frame_control &= ~morefrags;
+ next_len = 0;
}
-
- /* copy data */
- copylen = left > per_fragm ? per_fragm : left;
- memcpy(skb_put(frag, copylen), pos, copylen);
-
- skb_copy_queue_mapping(frag, first);
-
- frag->do_not_encrypt = first->do_not_encrypt;
- frag->dev = first->dev;
- frag->iif = first->iif;
-
- pos += copylen;
- left -= copylen;
- }
- skb_trim(first, hdrlen + per_fragm);
-
- tx->num_extra_frag = num_fragm - 1;
- tx->extra_frag = frags;
+ hdr->duration_id = ieee80211_duration(tx, 0, next_len);
+ hdr->seq_ctrl |= cpu_to_le16(fragnum & IEEE80211_SCTL_FRAG);
+ fragnum++;
+ } while ((skb = skb->next));
return TX_CONTINUE;
-
- fail:
- if (frags) {
- for (i = 0; i < num_fragm - 1; i++)
- if (frags[i])
- dev_kfree_skb(frags[i]);
- kfree(frags);
- }
- I802_DEBUG_INC(tx->local->tx_handlers_drop_fragment);
- return TX_DROP;
}
static ieee80211_tx_result debug_noinline
@@ -833,27 +845,19 @@ ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx)
static ieee80211_tx_result debug_noinline
ieee80211_tx_h_calculate_duration(struct ieee80211_tx_data *tx)
{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
- int next_len, i;
- int group_addr = is_multicast_ether_addr(hdr->addr1);
-
- if (!(tx->flags & IEEE80211_TX_FRAGMENTED)) {
- hdr->duration_id = ieee80211_duration(tx, group_addr, 0);
- return TX_CONTINUE;
- }
+ struct sk_buff *skb = tx->skb;
+ struct ieee80211_hdr *hdr;
+ int next_len;
+ bool group_addr;
- hdr->duration_id = ieee80211_duration(tx, group_addr,
- tx->extra_frag[0]->len);
+ do {
+ hdr = (void *) skb->data;
+ next_len = skb->next ? skb->next->len : 0;
+ group_addr = is_multicast_ether_addr(hdr->addr1);
- for (i = 0; i < tx->num_extra_frag; i++) {
- if (i + 1 < tx->num_extra_frag)
- next_len = tx->extra_frag[i + 1]->len;
- else
- next_len = 0;
-
- hdr = (struct ieee80211_hdr *)tx->extra_frag[i]->data;
- hdr->duration_id = ieee80211_duration(tx, 0, next_len);
- }
+ hdr->duration_id =
+ ieee80211_duration(tx, group_addr, next_len);
+ } while ((skb = skb->next));
return TX_CONTINUE;
}
@@ -861,19 +865,16 @@ ieee80211_tx_h_calculate_duration(struct ieee80211_tx_data *tx)
static ieee80211_tx_result debug_noinline
ieee80211_tx_h_stats(struct ieee80211_tx_data *tx)
{
- int i;
+ struct sk_buff *skb = tx->skb;
if (!tx->sta)
return TX_CONTINUE;
tx->sta->tx_packets++;
- tx->sta->tx_fragments++;
- tx->sta->tx_bytes += tx->skb->len;
- if (tx->extra_frag) {
- tx->sta->tx_fragments += tx->num_extra_frag;
- for (i = 0; i < tx->num_extra_frag; i++)
- tx->sta->tx_bytes += tx->extra_frag[i]->len;
- }
+ do {
+ tx->sta->tx_fragments++;
+ tx->sta->tx_bytes += skb->len;
+ } while ((skb = skb->next));
return TX_CONTINUE;
}
@@ -983,9 +984,9 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
struct ieee80211_hdr *hdr;
struct ieee80211_sub_if_data *sdata;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-
int hdrlen, tid;
u8 *qc, *state;
+ bool queued = false;
memset(tx, 0, sizeof(*tx));
tx->skb = skb;
@@ -1012,25 +1013,53 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
*/
}
+ /*
+ * If this flag is set to true anywhere, and we get here,
+ * we are doing the needed processing, so remove the flag
+ * now.
+ */
+ info->flags &= ~IEEE80211_TX_INTFL_NEED_TXPROCESSING;
+
hdr = (struct ieee80211_hdr *) skb->data;
tx->sta = sta_info_get(local, hdr->addr1);
- if (tx->sta && ieee80211_is_data_qos(hdr->frame_control)) {
+ if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) &&
+ (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)) {
unsigned long flags;
+ struct tid_ampdu_tx *tid_tx;
+
qc = ieee80211_get_qos_ctl(hdr);
tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
spin_lock_irqsave(&tx->sta->lock, flags);
+ /*
+ * XXX: This spinlock could be fairly expensive, but see the
+ * comment in agg-tx.c:ieee80211_agg_tx_operational().
+ * One way to solve this would be to do something RCU-like
+ * for managing the tid_tx struct and using atomic bitops
+ * for the actual state -- by introducing an actual
+ * 'operational' bit that would be possible. It would
+ * require changing ieee80211_agg_tx_operational() to
+ * set that bit, and changing the way tid_tx is managed
+ * everywhere, including races between that bit and
+ * tid_tx going away (tid_tx being added can be easily
+ * committed to memory before the 'operational' bit).
+ */
+ tid_tx = tx->sta->ampdu_mlme.tid_tx[tid];
state = &tx->sta->ampdu_mlme.tid_state_tx[tid];
if (*state == HT_AGG_STATE_OPERATIONAL) {
info->flags |= IEEE80211_TX_CTL_AMPDU;
- if (local->hw.ampdu_queues)
- skb_set_queue_mapping(
- skb, tx->local->hw.queues +
- tx->sta->tid_to_tx_q[tid]);
+ } else if (*state != HT_AGG_STATE_IDLE) {
+ /* in progress */
+ queued = true;
+ info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
+ __skb_queue_tail(&tid_tx->pending, skb);
}
spin_unlock_irqrestore(&tx->sta->lock, flags);
+
+ if (unlikely(queued))
+ return TX_QUEUED;
}
if (is_multicast_ether_addr(hdr->addr1)) {
@@ -1081,51 +1110,55 @@ static int ieee80211_tx_prepare(struct ieee80211_local *local,
}
if (unlikely(!dev))
return -ENODEV;
- /* initialises tx with control */
+ /*
+ * initialises tx with control
+ *
+ * return value is safe to ignore here because this function
+ * can only be invoked for multicast frames
+ *
+ * XXX: clean up
+ */
__ieee80211_tx_prepare(tx, skb, dev);
dev_put(dev);
return 0;
}
-static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb,
- struct ieee80211_tx_data *tx)
+static int __ieee80211_tx(struct ieee80211_local *local,
+ struct sk_buff **skbp,
+ struct sta_info *sta)
{
+ struct sk_buff *skb = *skbp, *next;
struct ieee80211_tx_info *info;
- int ret, i;
+ int ret, len;
+ bool fragm = false;
- if (skb) {
+ local->mdev->trans_start = jiffies;
+
+ while (skb) {
if (ieee80211_queue_stopped(&local->hw,
skb_get_queue_mapping(skb)))
return IEEE80211_TX_PENDING;
- ret = local->ops->tx(local_to_hw(local), skb);
- if (ret)
- return IEEE80211_TX_AGAIN;
- local->mdev->trans_start = jiffies;
- ieee80211_led_tx(local, 1);
- }
- if (tx->extra_frag) {
- for (i = 0; i < tx->num_extra_frag; i++) {
- if (!tx->extra_frag[i])
- continue;
- info = IEEE80211_SKB_CB(tx->extra_frag[i]);
+ info = IEEE80211_SKB_CB(skb);
+
+ if (fragm)
info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT |
IEEE80211_TX_CTL_FIRST_FRAGMENT);
- if (ieee80211_queue_stopped(&local->hw,
- skb_get_queue_mapping(tx->extra_frag[i])))
- return IEEE80211_TX_FRAG_AGAIN;
-
- ret = local->ops->tx(local_to_hw(local),
- tx->extra_frag[i]);
- if (ret)
- return IEEE80211_TX_FRAG_AGAIN;
- local->mdev->trans_start = jiffies;
- ieee80211_led_tx(local, 1);
- tx->extra_frag[i] = NULL;
+
+ next = skb->next;
+ len = skb->len;
+ ret = local->ops->tx(local_to_hw(local), skb);
+ if (WARN_ON(ret != NETDEV_TX_OK && skb->len != len)) {
+ dev_kfree_skb(skb);
+ ret = NETDEV_TX_OK;
}
- kfree(tx->extra_frag);
- tx->extra_frag = NULL;
+ if (ret != NETDEV_TX_OK)
+ return IEEE80211_TX_AGAIN;
+ *skbp = skb = next;
+ ieee80211_led_tx(local, 1);
+ fragm = true;
}
+
return IEEE80211_TX_OK;
}
@@ -1137,7 +1170,6 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
{
struct sk_buff *skb = tx->skb;
ieee80211_tx_result res = TX_DROP;
- int i;
#define CALL_TXH(txh) \
res = txh(tx); \
@@ -1161,11 +1193,13 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
txh_done:
if (unlikely(res == TX_DROP)) {
I802_DEBUG_INC(tx->local->tx_handlers_drop);
- dev_kfree_skb(skb);
- for (i = 0; i < tx->num_extra_frag; i++)
- if (tx->extra_frag[i])
- dev_kfree_skb(tx->extra_frag[i]);
- kfree(tx->extra_frag);
+ while (skb) {
+ struct sk_buff *next;
+
+ next = skb->next;
+ dev_kfree_skb(skb);
+ skb = next;
+ }
return -1;
} else if (unlikely(res == TX_QUEUED)) {
I802_DEBUG_INC(tx->local->tx_handlers_queued);
@@ -1175,23 +1209,26 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
return 0;
}
-static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb)
+static void ieee80211_tx(struct net_device *dev, struct sk_buff *skb,
+ bool txpending)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct sta_info *sta;
struct ieee80211_tx_data tx;
ieee80211_tx_result res_prepare;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- int ret, i;
+ struct sk_buff *next;
+ unsigned long flags;
+ int ret, retries;
u16 queue;
queue = skb_get_queue_mapping(skb);
- WARN_ON(test_bit(queue, local->queues_pending));
+ WARN_ON(!txpending && !skb_queue_empty(&local->pending[queue]));
if (unlikely(skb->len < 10)) {
dev_kfree_skb(skb);
- return 0;
+ return;
}
rcu_read_lock();
@@ -1199,10 +1236,13 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb)
/* initialises tx */
res_prepare = __ieee80211_tx_prepare(&tx, skb, dev);
- if (res_prepare == TX_DROP) {
+ if (unlikely(res_prepare == TX_DROP)) {
dev_kfree_skb(skb);
rcu_read_unlock();
- return 0;
+ return;
+ } else if (unlikely(res_prepare == TX_QUEUED)) {
+ rcu_read_unlock();
+ return;
}
sta = tx.sta;
@@ -1212,59 +1252,71 @@ static int ieee80211_tx(struct net_device *dev, struct sk_buff *skb)
if (invoke_tx_handlers(&tx))
goto out;
-retry:
- ret = __ieee80211_tx(local, skb, &tx);
- if (ret) {
- struct ieee80211_tx_stored_packet *store;
-
+ retries = 0;
+ retry:
+ ret = __ieee80211_tx(local, &tx.skb, tx.sta);
+ switch (ret) {
+ case IEEE80211_TX_OK:
+ break;
+ case IEEE80211_TX_AGAIN:
/*
* Since there are no fragmented frames on A-MPDU
* queues, there's no reason for a driver to reject
* a frame there, warn and drop it.
*/
- if (ret != IEEE80211_TX_PENDING)
- if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU))
- goto drop;
+ if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU))
+ goto drop;
+ /* fall through */
+ case IEEE80211_TX_PENDING:
+ skb = tx.skb;
+
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+
+ if (__netif_subqueue_stopped(local->mdev, queue)) {
+ do {
+ next = skb->next;
+ skb->next = NULL;
+ if (unlikely(txpending))
+ skb_queue_head(&local->pending[queue],
+ skb);
+ else
+ skb_queue_tail(&local->pending[queue],
+ skb);
+ } while ((skb = next));
- store = &local->pending_packet[queue];
+ /*
+ * Make sure nobody will enable the queue on us
+ * (without going through the tasklet) nor disable the
+ * netdev queue underneath the pending handling code.
+ */
+ __set_bit(IEEE80211_QUEUE_STOP_REASON_PENDING,
+ &local->queue_stop_reasons[queue]);
- if (ret == IEEE80211_TX_FRAG_AGAIN)
- skb = NULL;
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock,
+ flags);
+ } else {
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock,
+ flags);
- set_bit(queue, local->queues_pending);
- smp_mb();
- /*
- * When the driver gets out of buffers during sending of
- * fragments and calls ieee80211_stop_queue, the netif
- * subqueue is stopped. There is, however, a small window
- * in which the PENDING bit is not yet set. If a buffer
- * gets available in that window (i.e. driver calls
- * ieee80211_wake_queue), we would end up with ieee80211_tx
- * called with the PENDING bit still set. Prevent this by
- * continuing transmitting here when that situation is
- * possible to have happened.
- */
- if (!__netif_subqueue_stopped(local->mdev, queue)) {
- clear_bit(queue, local->queues_pending);
+ retries++;
+ if (WARN(retries > 10, "tx refused but queue active"))
+ goto drop;
goto retry;
}
- store->skb = skb;
- store->extra_frag = tx.extra_frag;
- store->num_extra_frag = tx.num_extra_frag;
}
out:
rcu_read_unlock();
- return 0;
+ return;
drop:
- if (skb)
- dev_kfree_skb(skb);
- for (i = 0; i < tx.num_extra_frag; i++)
- if (tx.extra_frag[i])
- dev_kfree_skb(tx.extra_frag[i]);
- kfree(tx.extra_frag);
rcu_read_unlock();
- return 0;
+
+ skb = tx.skb;
+ while (skb) {
+ next = skb->next;
+ dev_kfree_skb(skb);
+ skb = next;
+ }
}
/* device xmit handlers */
@@ -1323,7 +1375,6 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
FOUND_SDATA,
UNKNOWN_ADDRESS,
} monitor_iface = NOT_MONITOR;
- int ret;
if (skb->iif)
odev = dev_get_by_index(&init_net, skb->iif);
@@ -1337,7 +1388,7 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
"originating device\n", dev->name);
#endif
dev_kfree_skb(skb);
- return 0;
+ return NETDEV_TX_OK;
}
if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&
@@ -1366,7 +1417,7 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
else
if (mesh_nexthop_lookup(skb, osdata)) {
dev_put(odev);
- return 0;
+ return NETDEV_TX_OK;
}
if (memcmp(odev->dev_addr, hdr->addr4, ETH_ALEN) != 0)
IEEE80211_IFSTA_MESH_CTR_INC(&osdata->u.mesh,
@@ -1428,7 +1479,7 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (ieee80211_skb_resize(osdata->local, skb, headroom, may_encrypt)) {
dev_kfree_skb(skb);
dev_put(odev);
- return 0;
+ return NETDEV_TX_OK;
}
if (osdata->vif.type == NL80211_IFTYPE_AP_VLAN)
@@ -1437,10 +1488,11 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
u.ap);
if (likely(monitor_iface != UNKNOWN_ADDRESS))
info->control.vif = &osdata->vif;
- ret = ieee80211_tx(odev, skb);
+
+ ieee80211_tx(odev, skb, false);
dev_put(odev);
- return ret;
+ return NETDEV_TX_OK;
}
int ieee80211_monitor_start_xmit(struct sk_buff *skb,
@@ -1666,8 +1718,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
}
/* receiver and we are QoS enabled, use a QoS type frame */
- if (sta_flags & WLAN_STA_WME &&
- ieee80211_num_regular_queues(&local->hw) >= 4) {
+ if ((sta_flags & WLAN_STA_WME) && local->hw.queues >= 4) {
fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
hdrlen += 2;
}
@@ -1799,19 +1850,58 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
*/
void ieee80211_clear_tx_pending(struct ieee80211_local *local)
{
- int i, j;
- struct ieee80211_tx_stored_packet *store;
+ int i;
- for (i = 0; i < ieee80211_num_regular_queues(&local->hw); i++) {
- if (!test_bit(i, local->queues_pending))
- continue;
- store = &local->pending_packet[i];
- kfree_skb(store->skb);
- for (j = 0; j < store->num_extra_frag; j++)
- kfree_skb(store->extra_frag[j]);
- kfree(store->extra_frag);
- clear_bit(i, local->queues_pending);
+ for (i = 0; i < local->hw.queues; i++)
+ skb_queue_purge(&local->pending[i]);
+}
+
+static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,
+ struct sk_buff *skb)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_sub_if_data *sdata;
+ struct sta_info *sta;
+ struct ieee80211_hdr *hdr;
+ struct net_device *dev;
+ int ret;
+ bool result = true;
+
+ /* does interface still exist? */
+ dev = dev_get_by_index(&init_net, skb->iif);
+ if (!dev) {
+ dev_kfree_skb(skb);
+ return true;
}
+
+ /* validate info->control.vif against skb->iif */
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ sdata = container_of(sdata->bss,
+ struct ieee80211_sub_if_data,
+ u.ap);
+
+ if (unlikely(info->control.vif && info->control.vif != &sdata->vif)) {
+ dev_kfree_skb(skb);
+ result = true;
+ goto out;
+ }
+
+ if (info->flags & IEEE80211_TX_INTFL_NEED_TXPROCESSING) {
+ ieee80211_tx(dev, skb, true);
+ } else {
+ hdr = (struct ieee80211_hdr *)skb->data;
+ sta = sta_info_get(local, hdr->addr1);
+
+ ret = __ieee80211_tx(local, &skb, sta);
+ if (ret != IEEE80211_TX_OK)
+ result = false;
+ }
+
+ out:
+ dev_put(dev);
+
+ return result;
}
/*
@@ -1822,40 +1912,53 @@ void ieee80211_tx_pending(unsigned long data)
{
struct ieee80211_local *local = (struct ieee80211_local *)data;
struct net_device *dev = local->mdev;
- struct ieee80211_tx_stored_packet *store;
- struct ieee80211_tx_data tx;
- int i, ret;
+ unsigned long flags;
+ int i;
+ bool next;
+ rcu_read_lock();
netif_tx_lock_bh(dev);
- for (i = 0; i < ieee80211_num_regular_queues(&local->hw); i++) {
- /* Check that this queue is ok */
- if (__netif_subqueue_stopped(local->mdev, i) &&
- !test_bit(i, local->queues_pending_run))
- continue;
- if (!test_bit(i, local->queues_pending)) {
- clear_bit(i, local->queues_pending_run);
- ieee80211_wake_queue(&local->hw, i);
+ for (i = 0; i < local->hw.queues; i++) {
+ /*
+ * If queue is stopped by something other than due to pending
+ * frames, or we have no pending frames, proceed to next queue.
+ */
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+ next = false;
+ if (local->queue_stop_reasons[i] !=
+ BIT(IEEE80211_QUEUE_STOP_REASON_PENDING) ||
+ skb_queue_empty(&local->pending[i]))
+ next = true;
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+
+ if (next)
continue;
- }
- clear_bit(i, local->queues_pending_run);
+ /*
+ * start the queue now to allow processing our packets,
+ * we're under the tx lock here anyway so nothing will
+ * happen as a result of this
+ */
netif_start_subqueue(local->mdev, i);
- store = &local->pending_packet[i];
- tx.extra_frag = store->extra_frag;
- tx.num_extra_frag = store->num_extra_frag;
- tx.flags = 0;
- ret = __ieee80211_tx(local, store->skb, &tx);
- if (ret) {
- if (ret == IEEE80211_TX_FRAG_AGAIN)
- store->skb = NULL;
- } else {
- clear_bit(i, local->queues_pending);
- ieee80211_wake_queue(&local->hw, i);
+ while (!skb_queue_empty(&local->pending[i])) {
+ struct sk_buff *skb = skb_dequeue(&local->pending[i]);
+
+ if (!ieee80211_tx_pending_skb(local, skb)) {
+ skb_queue_head(&local->pending[i], skb);
+ break;
+ }
}
+
+ /* Start regular packet processing again. */
+ if (skb_queue_empty(&local->pending[i]))
+ ieee80211_wake_queue_by_reason(&local->hw, i,
+ IEEE80211_QUEUE_STOP_REASON_PENDING);
}
+
netif_tx_unlock_bh(dev);
+ rcu_read_unlock();
}
/* functions for drivers to get certain frames */
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index e0431a1d218b..fdf432f14554 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -166,18 +166,13 @@ int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx)
{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
-
- hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
- if (tx->extra_frag) {
- struct ieee80211_hdr *fhdr;
- int i;
- for (i = 0; i < tx->num_extra_frag; i++) {
- fhdr = (struct ieee80211_hdr *)
- tx->extra_frag[i]->data;
- fhdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
- }
- }
+ struct sk_buff *skb = tx->skb;
+ struct ieee80211_hdr *hdr;
+
+ do {
+ hdr = (struct ieee80211_hdr *) skb->data;
+ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+ } while ((skb = skb->next));
}
int ieee80211_frame_duration(struct ieee80211_local *local, size_t len,
@@ -344,42 +339,21 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
{
struct ieee80211_local *local = hw_to_local(hw);
- if (queue >= hw->queues) {
- if (local->ampdu_ac_queue[queue - hw->queues] < 0)
- return;
-
- /*
- * for virtual aggregation queues, we need to refcount the
- * internal mac80211 disable (multiple times!), keep track of
- * driver disable _and_ make sure the regular queue is
- * actually enabled.
- */
- if (reason == IEEE80211_QUEUE_STOP_REASON_AGGREGATION)
- local->amdpu_ac_stop_refcnt[queue - hw->queues]--;
- else
- __clear_bit(reason, &local->queue_stop_reasons[queue]);
-
- if (local->queue_stop_reasons[queue] ||
- local->amdpu_ac_stop_refcnt[queue - hw->queues])
- return;
-
- /* now go on to treat the corresponding regular queue */
- queue = local->ampdu_ac_queue[queue - hw->queues];
- reason = IEEE80211_QUEUE_STOP_REASON_AGGREGATION;
- }
+ if (WARN_ON(queue >= hw->queues))
+ return;
__clear_bit(reason, &local->queue_stop_reasons[queue]);
+ if (!skb_queue_empty(&local->pending[queue]) &&
+ local->queue_stop_reasons[queue] ==
+ BIT(IEEE80211_QUEUE_STOP_REASON_PENDING))
+ tasklet_schedule(&local->tx_pending_tasklet);
+
if (local->queue_stop_reasons[queue] != 0)
/* someone still has this queue stopped */
return;
- if (test_bit(queue, local->queues_pending)) {
- set_bit(queue, local->queues_pending_run);
- tasklet_schedule(&local->tx_pending_tasklet);
- } else {
- netif_wake_subqueue(local->mdev, queue);
- }
+ netif_wake_subqueue(local->mdev, queue);
}
void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
@@ -405,29 +379,18 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
{
struct ieee80211_local *local = hw_to_local(hw);
- if (queue >= hw->queues) {
- if (local->ampdu_ac_queue[queue - hw->queues] < 0)
- return;
-
- /*
- * for virtual aggregation queues, we need to refcount the
- * internal mac80211 disable (multiple times!), keep track of
- * driver disable _and_ make sure the regular queue is
- * actually enabled.
- */
- if (reason == IEEE80211_QUEUE_STOP_REASON_AGGREGATION)
- local->amdpu_ac_stop_refcnt[queue - hw->queues]++;
- else
- __set_bit(reason, &local->queue_stop_reasons[queue]);
+ if (WARN_ON(queue >= hw->queues))
+ return;
- /* now go on to treat the corresponding regular queue */
- queue = local->ampdu_ac_queue[queue - hw->queues];
- reason = IEEE80211_QUEUE_STOP_REASON_AGGREGATION;
- }
+ /*
+ * Only stop if it was previously running, this is necessary
+ * for correct pending packets handling because there we may
+ * start (but not wake) the queue and rely on that.
+ */
+ if (!local->queue_stop_reasons[queue])
+ netif_stop_subqueue(local->mdev, queue);
__set_bit(reason, &local->queue_stop_reasons[queue]);
-
- netif_stop_subqueue(local->mdev, queue);
}
void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
@@ -473,15 +436,9 @@ EXPORT_SYMBOL(ieee80211_stop_queues);
int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue)
{
struct ieee80211_local *local = hw_to_local(hw);
- unsigned long flags;
- if (queue >= hw->queues) {
- spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
- queue = local->ampdu_ac_queue[queue - hw->queues];
- spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
- if (queue < 0)
- return true;
- }
+ if (WARN_ON(queue >= hw->queues))
+ return true;
return __netif_subqueue_stopped(local->mdev, queue);
}
@@ -496,7 +453,7 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
- for (i = 0; i < hw->queues + hw->ampdu_queues; i++)
+ for (i = 0; i < hw->queues; i++)
__ieee80211_wake_queue(hw, i, reason);
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
@@ -846,16 +803,9 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
- const u8 *ie_auth = NULL;
- int ie_auth_len = 0;
-
- if (sdata->vif.type == NL80211_IFTYPE_STATION) {
- ie_auth_len = sdata->u.mgd.ie_auth_len;
- ie_auth = sdata->u.mgd.ie_auth;
- }
skb = dev_alloc_skb(local->hw.extra_tx_headroom +
- sizeof(*mgmt) + 6 + extra_len + ie_auth_len);
+ sizeof(*mgmt) + 6 + extra_len);
if (!skb) {
printk(KERN_DEBUG "%s: failed to allocate buffer for auth "
"frame\n", sdata->dev->name);
@@ -877,8 +827,6 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
mgmt->u.auth.status_code = cpu_to_le16(0);
if (extra)
memcpy(skb_put(skb, extra_len), extra, extra_len);
- if (ie_auth)
- memcpy(skb_put(skb, ie_auth_len), ie_auth, ie_auth_len);
ieee80211_tx_skb(sdata, skb, encrypt);
}
@@ -891,20 +839,11 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
struct ieee80211_supported_band *sband;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
- u8 *pos, *supp_rates, *esupp_rates = NULL, *extra_preq_ie = NULL;
- int i, extra_preq_ie_len = 0;
-
- switch (sdata->vif.type) {
- case NL80211_IFTYPE_STATION:
- extra_preq_ie_len = sdata->u.mgd.ie_probereq_len;
- extra_preq_ie = sdata->u.mgd.ie_probereq;
- break;
- default:
- break;
- }
+ u8 *pos, *supp_rates, *esupp_rates = NULL;
+ int i;
skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 +
- ie_len + extra_preq_ie_len);
+ ie_len);
if (!skb) {
printk(KERN_DEBUG "%s: failed to allocate buffer for probe "
"request\n", sdata->dev->name);
@@ -953,9 +892,6 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
if (ie)
memcpy(skb_put(skb, ie_len), ie, ie_len);
- if (extra_preq_ie)
- memcpy(skb_put(skb, extra_preq_ie_len), extra_preq_ie,
- extra_preq_ie_len);
ieee80211_tx_skb(sdata, skb, 0);
}
diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c
index 7043ddc75498..ef73105b3061 100644
--- a/net/mac80211/wep.c
+++ b/net/mac80211/wep.c
@@ -329,24 +329,17 @@ static int wep_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
ieee80211_tx_result
ieee80211_crypto_wep_encrypt(struct ieee80211_tx_data *tx)
{
- int i;
+ struct sk_buff *skb;
ieee80211_tx_set_protected(tx);
- if (wep_encrypt_skb(tx, tx->skb) < 0) {
- I802_DEBUG_INC(tx->local->tx_handlers_drop_wep);
- return TX_DROP;
- }
-
- if (tx->extra_frag) {
- for (i = 0; i < tx->num_extra_frag; i++) {
- if (wep_encrypt_skb(tx, tx->extra_frag[i])) {
- I802_DEBUG_INC(tx->local->
- tx_handlers_drop_wep);
- return TX_DROP;
- }
+ skb = tx->skb;
+ do {
+ if (wep_encrypt_skb(tx, skb) < 0) {
+ I802_DEBUG_INC(tx->local->tx_handlers_drop_wep);
+ return TX_DROP;
}
- }
+ } while ((skb = skb->next));
return TX_CONTINUE;
}
diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c
index 935c63ed3dfa..deb4ecec122a 100644
--- a/net/mac80211/wext.c
+++ b/net/mac80211/wext.c
@@ -129,14 +129,12 @@ static int ieee80211_ioctl_siwgenie(struct net_device *dev,
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME)
- return -EOPNOTSUPP;
-
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
int ret = ieee80211_sta_set_extra_ie(sdata, extra, data->length);
if (ret)
return ret;
sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
+ sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME;
ieee80211_sta_req_auth(sdata);
return 0;
}
@@ -207,14 +205,6 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev,
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
- if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) {
- if (len > IEEE80211_MAX_SSID_LEN)
- return -EINVAL;
- memcpy(sdata->u.mgd.ssid, ssid, len);
- sdata->u.mgd.ssid_len = len;
- return 0;
- }
-
if (data->flags)
sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL;
else
@@ -224,6 +214,7 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev,
if (ret)
return ret;
+ sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME;
ieee80211_sta_req_auth(sdata);
return 0;
} else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
@@ -272,11 +263,7 @@ static int ieee80211_ioctl_siwap(struct net_device *dev,
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
int ret;
- if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) {
- memcpy(sdata->u.mgd.bssid, (u8 *) &ap_addr->sa_data,
- ETH_ALEN);
- return 0;
- }
+
if (is_zero_ether_addr((u8 *) &ap_addr->sa_data))
sdata->u.mgd.flags |= IEEE80211_STA_AUTO_BSSID_SEL |
IEEE80211_STA_AUTO_CHANNEL_SEL;
@@ -287,6 +274,7 @@ static int ieee80211_ioctl_siwap(struct net_device *dev,
ret = ieee80211_sta_set_bssid(sdata, (u8 *) &ap_addr->sa_data);
if (ret)
return ret;
+ sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME;
ieee80211_sta_req_auth(sdata);
return 0;
} else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
@@ -630,7 +618,7 @@ static int ieee80211_ioctl_siwencode(struct net_device *dev,
struct ieee80211_sub_if_data *sdata;
int idx, i, alg = ALG_WEP;
u8 bcaddr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
- int remove = 0;
+ int remove = 0, ret;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -656,11 +644,20 @@ static int ieee80211_ioctl_siwencode(struct net_device *dev,
return 0;
}
- return ieee80211_set_encryption(
+ ret = ieee80211_set_encryption(
sdata, bcaddr,
idx, alg, remove,
!sdata->default_key,
keybuf, erq->length);
+
+ if (!ret) {
+ if (remove)
+ sdata->u.mgd.flags &= ~IEEE80211_STA_TKIP_WEP_USED;
+ else
+ sdata->u.mgd.flags |= IEEE80211_STA_TKIP_WEP_USED;
+ }
+
+ return ret;
}
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index 9101b48ec2ae..4f8bfea278f2 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -196,19 +196,13 @@ ieee80211_tx_result
ieee80211_crypto_tkip_encrypt(struct ieee80211_tx_data *tx)
{
struct sk_buff *skb = tx->skb;
- int i;
ieee80211_tx_set_protected(tx);
- if (tkip_encrypt_skb(tx, skb) < 0)
- return TX_DROP;
-
- if (tx->extra_frag) {
- for (i = 0; i < tx->num_extra_frag; i++) {
- if (tkip_encrypt_skb(tx, tx->extra_frag[i]))
- return TX_DROP;
- }
- }
+ do {
+ if (tkip_encrypt_skb(tx, skb) < 0)
+ return TX_DROP;
+ } while ((skb = skb->next));
return TX_CONTINUE;
}
@@ -428,19 +422,13 @@ ieee80211_tx_result
ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx)
{
struct sk_buff *skb = tx->skb;
- int i;
ieee80211_tx_set_protected(tx);
- if (ccmp_encrypt_skb(tx, skb) < 0)
- return TX_DROP;
-
- if (tx->extra_frag) {
- for (i = 0; i < tx->num_extra_frag; i++) {
- if (ccmp_encrypt_skb(tx, tx->extra_frag[i]))
- return TX_DROP;
- }
- }
+ do {
+ if (ccmp_encrypt_skb(tx, skb) < 0)
+ return TX_DROP;
+ } while ((skb = skb->next));
return TX_CONTINUE;
}
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig
index 092ae6faccca..3c3bc9e579ed 100644
--- a/net/wireless/Kconfig
+++ b/net/wireless/Kconfig
@@ -10,51 +10,19 @@ config CFG80211_REG_DEBUG
If unsure, say N.
-config NL80211
- bool "nl80211 new netlink interface support"
- depends on CFG80211
- default y
- ---help---
- This option turns on the new netlink interface
- (nl80211) support in cfg80211.
-
- If =n, drivers using mac80211 will be configured via
- wireless extension support provided by that subsystem.
-
- If unsure, say Y.
-
config WIRELESS_OLD_REGULATORY
bool "Old wireless static regulatory definitions"
- default y
+ default n
---help---
This option enables the old static regulatory information
- and uses it within the new framework. This is available
- temporarily as an option to help prevent immediate issues
- due to the switch to the new regulatory framework which
- does require a new userspace application which has the
- database of regulatory information (CRDA) and another for
- setting regulatory domains (iw).
-
- For more information see:
-
- http://wireless.kernel.org/en/developers/Regulatory/CRDA
- http://wireless.kernel.org/en/users/Documentation/iw
-
- It is important to note though that if you *do* have CRDA present
- and if this option is enabled CRDA *will* be called to update the
- regulatory domain (for US and JP only). Support for letting the user
- set the regulatory domain through iw is also supported. This option
- mainly exists to leave around for a kernel release some old static
- regulatory domains that were defined and to keep around the old
- ieee80211_regdom module parameter. This is being phased out and you
- should stop using them ASAP.
-
- Note: You will need CRDA if you want 802.11d support
-
- Say Y unless you have installed a new userspace application.
- Also say Y if have one currently depending on the ieee80211_regdom
- module parameter and cannot port it to use the new userspace
- interfaces.
+ and uses it within the new framework. This option is available
+ for historical reasons and it is advised to leave it off.
+
+ For details see:
+
+ http://wireless.kernel.org/en/developers/Regulatory
+
+ Say N and if you say Y, please tell us why. The default is N.
config WIRELESS_EXT
bool "Wireless extensions"
diff --git a/net/wireless/Makefile b/net/wireless/Makefile
index dad43c24f695..6d1e7b27b752 100644
--- a/net/wireless/Makefile
+++ b/net/wireless/Makefile
@@ -5,8 +5,7 @@ obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o
obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o
obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o
-cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o
+cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o
cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o
-cfg80211-$(CONFIG_NL80211) += nl80211.o
ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 17fe39049740..d1f556535f6d 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -87,7 +87,7 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx)
}
/* requires cfg80211_mutex to be held! */
-static struct cfg80211_registered_device *
+struct cfg80211_registered_device *
__cfg80211_drv_from_info(struct genl_info *info)
{
int ifindex;
@@ -176,13 +176,14 @@ void cfg80211_put_dev(struct cfg80211_registered_device *drv)
mutex_unlock(&drv->mtx);
}
+/* requires cfg80211_mutex to be held */
int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
char *newname)
{
struct cfg80211_registered_device *drv;
int wiphy_idx, taken = -1, result, digits;
- mutex_lock(&cfg80211_mutex);
+ assert_cfg80211_lock();
/* prohibit calling the thing phy%d when %d is not its number */
sscanf(newname, PHY_NAME "%d%n", &wiphy_idx, &taken);
@@ -195,30 +196,23 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
* deny the name if it is phy<idx> where <idx> is printed
* without leading zeroes. taken == strlen(newname) here
*/
- result = -EINVAL;
if (taken == strlen(PHY_NAME) + digits)
- goto out_unlock;
+ return -EINVAL;
}
/* Ignore nop renames */
- result = 0;
if (strcmp(newname, dev_name(&rdev->wiphy.dev)) == 0)
- goto out_unlock;
+ return 0;
/* Ensure another device does not already have this name. */
- list_for_each_entry(drv, &cfg80211_drv_list, list) {
- result = -EINVAL;
+ list_for_each_entry(drv, &cfg80211_drv_list, list)
if (strcmp(newname, dev_name(&drv->wiphy.dev)) == 0)
- goto out_unlock;
- }
+ return -EINVAL;
- /* this will only check for collisions in sysfs
- * which is not even always compiled in.
- */
result = device_rename(&rdev->wiphy.dev, newname);
if (result)
- goto out_unlock;
+ return result;
if (rdev->wiphy.debugfsdir &&
!debugfs_rename(rdev->wiphy.debugfsdir->d_parent,
@@ -228,13 +222,9 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
printk(KERN_ERR "cfg80211: failed to rename debugfs dir to %s!\n",
newname);
- result = 0;
-out_unlock:
- mutex_unlock(&cfg80211_mutex);
- if (result == 0)
- nl80211_notify_dev_rename(rdev);
+ nl80211_notify_dev_rename(rdev);
- return result;
+ return 0;
}
/* exported functions */
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 6acd483a61f8..d43daa236ef9 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -90,6 +90,8 @@ struct cfg80211_internal_bss {
struct rb_node rbn;
unsigned long ts;
struct kref ref;
+ bool hold;
+
/* must be last because of priv member */
struct cfg80211_bss pub;
};
@@ -97,6 +99,9 @@ struct cfg80211_internal_bss {
struct cfg80211_registered_device *cfg80211_drv_by_wiphy_idx(int wiphy_idx);
int get_wiphy_idx(struct wiphy *wiphy);
+struct cfg80211_registered_device *
+__cfg80211_drv_from_info(struct genl_info *info);
+
/*
* This function returns a pointer to the driver
* that the genl_info item that is passed refers to.
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
new file mode 100644
index 000000000000..bec5721b6f99
--- /dev/null
+++ b/net/wireless/mlme.c
@@ -0,0 +1,46 @@
+/*
+ * cfg80211 MLME SAP interface
+ *
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/nl80211.h>
+#include <net/cfg80211.h>
+#include "core.h"
+#include "nl80211.h"
+
+void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len)
+{
+ struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ nl80211_send_rx_auth(rdev, dev, buf, len);
+}
+EXPORT_SYMBOL(cfg80211_send_rx_auth);
+
+void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len)
+{
+ struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ nl80211_send_rx_assoc(rdev, dev, buf, len);
+}
+EXPORT_SYMBOL(cfg80211_send_rx_assoc);
+
+void cfg80211_send_rx_deauth(struct net_device *dev, const u8 *buf, size_t len)
+{
+ struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ nl80211_send_rx_deauth(rdev, dev, buf, len);
+}
+EXPORT_SYMBOL(cfg80211_send_rx_deauth);
+
+void cfg80211_send_rx_disassoc(struct net_device *dev, const u8 *buf,
+ size_t len)
+{
+ struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ nl80211_send_rx_disassoc(rdev, dev, buf, len);
+}
+EXPORT_SYMBOL(cfg80211_send_rx_disassoc);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index ab9d8f14e151..353e1a4ece83 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -111,6 +111,11 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
.len = IEEE80211_MAX_DATA_LEN },
[NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
[NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
+
+ [NL80211_ATTR_SSID] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_SSID_LEN },
+ [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 },
+ [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
};
/* message building helper */
@@ -131,6 +136,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
struct nlattr *nl_freqs, *nl_freq;
struct nlattr *nl_rates, *nl_rate;
struct nlattr *nl_modes;
+ struct nlattr *nl_cmds;
enum ieee80211_band band;
struct ieee80211_channel *chan;
struct ieee80211_rate *rate;
@@ -242,6 +248,35 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
}
nla_nest_end(msg, nl_bands);
+ nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
+ if (!nl_cmds)
+ goto nla_put_failure;
+
+ i = 0;
+#define CMD(op, n) \
+ do { \
+ if (dev->ops->op) { \
+ i++; \
+ NLA_PUT_U32(msg, i, NL80211_CMD_ ## n); \
+ } \
+ } while (0)
+
+ CMD(add_virtual_intf, NEW_INTERFACE);
+ CMD(change_virtual_intf, SET_INTERFACE);
+ CMD(add_key, NEW_KEY);
+ CMD(add_beacon, NEW_BEACON);
+ CMD(add_station, NEW_STATION);
+ CMD(add_mpath, NEW_MPATH);
+ CMD(set_mesh_params, SET_MESH_PARAMS);
+ CMD(change_bss, SET_BSS);
+ CMD(auth, AUTHENTICATE);
+ CMD(assoc, ASSOCIATE);
+ CMD(deauth, DEAUTHENTICATE);
+ CMD(disassoc, DISASSOCIATE);
+
+#undef CMD
+ nla_nest_end(msg, nl_cmds);
+
return genlmsg_end(msg, hdr);
nla_put_failure:
@@ -331,16 +366,26 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
int result = 0, rem_txq_params = 0;
struct nlattr *nl_txq_params;
- rdev = cfg80211_get_dev_from_info(info);
- if (IS_ERR(rdev))
- return PTR_ERR(rdev);
+ rtnl_lock();
+
+ mutex_lock(&cfg80211_mutex);
- if (info->attrs[NL80211_ATTR_WIPHY_NAME]) {
+ rdev = __cfg80211_drv_from_info(info);
+ if (IS_ERR(rdev)) {
+ result = PTR_ERR(rdev);
+ goto unlock;
+ }
+
+ mutex_lock(&rdev->mtx);
+
+ if (info->attrs[NL80211_ATTR_WIPHY_NAME])
result = cfg80211_dev_rename(
rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
- if (result)
- goto bad_res;
- }
+
+ mutex_unlock(&cfg80211_mutex);
+
+ if (result)
+ goto bad_res;
if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
struct ieee80211_txq_params txq_params;
@@ -436,7 +481,9 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
bad_res:
- cfg80211_put_dev(rdev);
+ mutex_unlock(&rdev->mtx);
+ unlock:
+ rtnl_unlock();
return result;
}
@@ -572,21 +619,31 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
enum nl80211_iftype type;
struct net_device *dev;
u32 _flags, *flags = NULL;
+ bool change = false;
memset(&params, 0, sizeof(params));
+ rtnl_lock();
+
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
if (err)
- return err;
+ goto unlock_rtnl;
+
ifindex = dev->ifindex;
type = dev->ieee80211_ptr->iftype;
dev_put(dev);
- err = -EINVAL;
if (info->attrs[NL80211_ATTR_IFTYPE]) {
- type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
- if (type > NL80211_IFTYPE_MAX)
+ enum nl80211_iftype ntype;
+
+ ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
+ if (type != ntype)
+ change = true;
+ type = ntype;
+ if (type > NL80211_IFTYPE_MAX) {
+ err = -EINVAL;
goto unlock;
+ }
}
if (!drv->ops->change_virtual_intf ||
@@ -602,6 +659,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
}
params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
+ change = true;
}
if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
@@ -611,20 +669,26 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
}
err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
&_flags);
- if (!err)
- flags = &_flags;
+ if (err)
+ goto unlock;
+
+ flags = &_flags;
+ change = true;
}
- rtnl_lock();
- err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex,
- type, flags, &params);
+
+ if (change)
+ err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex,
+ type, flags, &params);
+ else
+ err = 0;
dev = __dev_get_by_index(&init_net, ifindex);
WARN_ON(!dev || (!err && dev->ieee80211_ptr->iftype != type));
- rtnl_unlock();
-
unlock:
cfg80211_put_dev(drv);
+ unlock_rtnl:
+ rtnl_unlock();
return err;
}
@@ -647,9 +711,13 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
return -EINVAL;
}
+ rtnl_lock();
+
drv = cfg80211_get_dev_from_info(info);
- if (IS_ERR(drv))
- return PTR_ERR(drv);
+ if (IS_ERR(drv)) {
+ err = PTR_ERR(drv);
+ goto unlock_rtnl;
+ }
if (!drv->ops->add_virtual_intf ||
!(drv->wiphy.interface_modes & (1 << type))) {
@@ -663,18 +731,17 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
}
- rtnl_lock();
err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
&flags);
err = drv->ops->add_virtual_intf(&drv->wiphy,
nla_data(info->attrs[NL80211_ATTR_IFNAME]),
type, err ? NULL : &flags, &params);
- rtnl_unlock();
-
unlock:
cfg80211_put_dev(drv);
+ unlock_rtnl:
+ rtnl_unlock();
return err;
}
@@ -684,9 +751,11 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
int ifindex, err;
struct net_device *dev;
+ rtnl_lock();
+
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
if (err)
- return err;
+ goto unlock_rtnl;
ifindex = dev->ifindex;
dev_put(dev);
@@ -695,12 +764,12 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
goto out;
}
- rtnl_lock();
err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex);
- rtnl_unlock();
out:
cfg80211_put_dev(drv);
+ unlock_rtnl:
+ rtnl_unlock();
return err;
}
@@ -752,9 +821,11 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_MAC])
mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ rtnl_lock();
+
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
if (err)
- return err;
+ goto unlock_rtnl;
if (!drv->ops->get_key) {
err = -EOPNOTSUPP;
@@ -782,10 +853,8 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
if (mac_addr)
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
- rtnl_lock();
err = drv->ops->get_key(&drv->wiphy, dev, key_idx, mac_addr,
&cookie, get_key_callback);
- rtnl_unlock();
if (err)
goto out;
@@ -803,6 +872,9 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
out:
cfg80211_put_dev(drv);
dev_put(dev);
+ unlock_rtnl:
+ rtnl_unlock();
+
return err;
}
@@ -831,9 +903,11 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT])
return -EINVAL;
+ rtnl_lock();
+
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
if (err)
- return err;
+ goto unlock_rtnl;
if (info->attrs[NL80211_ATTR_KEY_DEFAULT])
func = drv->ops->set_default_key;
@@ -845,13 +919,15 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
goto out;
}
- rtnl_lock();
err = func(&drv->wiphy, dev, key_idx);
- rtnl_unlock();
out:
cfg80211_put_dev(drv);
dev_put(dev);
+
+ unlock_rtnl:
+ rtnl_unlock();
+
return err;
}
@@ -921,22 +997,25 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
return -EINVAL;
}
+ rtnl_lock();
+
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
if (err)
- return err;
+ goto unlock_rtnl;
if (!drv->ops->add_key) {
err = -EOPNOTSUPP;
goto out;
}
- rtnl_lock();
err = drv->ops->add_key(&drv->wiphy, dev, key_idx, mac_addr, &params);
- rtnl_unlock();
out:
cfg80211_put_dev(drv);
dev_put(dev);
+ unlock_rtnl:
+ rtnl_unlock();
+
return err;
}
@@ -957,22 +1036,26 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_MAC])
mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ rtnl_lock();
+
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
if (err)
- return err;
+ goto unlock_rtnl;
if (!drv->ops->del_key) {
err = -EOPNOTSUPP;
goto out;
}
- rtnl_lock();
err = drv->ops->del_key(&drv->wiphy, dev, key_idx, mac_addr);
- rtnl_unlock();
out:
cfg80211_put_dev(drv);
dev_put(dev);
+
+ unlock_rtnl:
+ rtnl_unlock();
+
return err;
}
@@ -986,9 +1069,16 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
struct beacon_parameters params;
int haveinfo = 0;
+ rtnl_lock();
+
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
if (err)
- return err;
+ goto unlock_rtnl;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
switch (info->genlhdr->cmd) {
case NL80211_CMD_NEW_BEACON:
@@ -1049,13 +1139,14 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
goto out;
}
- rtnl_lock();
err = call(&drv->wiphy, dev, &params);
- rtnl_unlock();
out:
cfg80211_put_dev(drv);
dev_put(dev);
+ unlock_rtnl:
+ rtnl_unlock();
+
return err;
}
@@ -1065,22 +1156,29 @@ static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
int err;
struct net_device *dev;
+ rtnl_lock();
+
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
if (err)
- return err;
+ goto unlock_rtnl;
if (!drv->ops->del_beacon) {
err = -EOPNOTSUPP;
goto out;
}
- rtnl_lock();
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
err = drv->ops->del_beacon(&drv->wiphy, dev);
- rtnl_unlock();
out:
cfg80211_put_dev(drv);
dev_put(dev);
+ unlock_rtnl:
+ rtnl_unlock();
+
return err;
}
@@ -1246,30 +1344,32 @@ static int nl80211_dump_station(struct sk_buff *skb,
return -EINVAL;
}
- netdev = dev_get_by_index(&init_net, ifidx);
- if (!netdev)
- return -ENODEV;
+ rtnl_lock();
+
+ netdev = __dev_get_by_index(&init_net, ifidx);
+ if (!netdev) {
+ err = -ENODEV;
+ goto out_rtnl;
+ }
dev = cfg80211_get_dev_from_ifindex(ifidx);
if (IS_ERR(dev)) {
err = PTR_ERR(dev);
- goto out_put_netdev;
+ goto out_rtnl;
}
if (!dev->ops->dump_station) {
- err = -ENOSYS;
+ err = -EOPNOTSUPP;
goto out_err;
}
- rtnl_lock();
-
while (1) {
err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
mac_addr, &sinfo);
if (err == -ENOENT)
break;
if (err)
- goto out_err_rtnl;
+ goto out_err;
if (nl80211_send_station(skb,
NETLINK_CB(cb->skb).pid,
@@ -1285,12 +1385,10 @@ static int nl80211_dump_station(struct sk_buff *skb,
out:
cb->args[1] = sta_idx;
err = skb->len;
- out_err_rtnl:
- rtnl_unlock();
out_err:
cfg80211_put_dev(dev);
- out_put_netdev:
- dev_put(netdev);
+ out_rtnl:
+ rtnl_unlock();
return err;
}
@@ -1311,19 +1409,18 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ rtnl_lock();
+
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
if (err)
- return err;
+ goto out_rtnl;
if (!drv->ops->get_station) {
err = -EOPNOTSUPP;
goto out;
}
- rtnl_lock();
err = drv->ops->get_station(&drv->wiphy, dev, mac_addr, &sinfo);
- rtnl_unlock();
-
if (err)
goto out;
@@ -1340,10 +1437,12 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
out_free:
nlmsg_free(msg);
-
out:
cfg80211_put_dev(drv);
dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
return err;
}
@@ -1411,9 +1510,11 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
params.plink_action =
nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
+ rtnl_lock();
+
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
if (err)
- return err;
+ goto out_rtnl;
err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, &params.vlan);
if (err)
@@ -1424,15 +1525,16 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
goto out;
}
- rtnl_lock();
err = drv->ops->change_station(&drv->wiphy, dev, mac_addr, &params);
- rtnl_unlock();
out:
if (params.vlan)
dev_put(params.vlan);
cfg80211_put_dev(drv);
dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
return err;
}
@@ -1474,9 +1576,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
&params.station_flags))
return -EINVAL;
+ rtnl_lock();
+
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
if (err)
- return err;
+ goto out_rtnl;
err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, &params.vlan);
if (err)
@@ -1487,15 +1591,21 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
goto out;
}
- rtnl_lock();
+ if (!netif_running(dev)) {
+ err = -ENETDOWN;
+ goto out;
+ }
+
err = drv->ops->add_station(&drv->wiphy, dev, mac_addr, &params);
- rtnl_unlock();
out:
if (params.vlan)
dev_put(params.vlan);
cfg80211_put_dev(drv);
dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
return err;
}
@@ -1509,22 +1619,25 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_MAC])
mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ rtnl_lock();
+
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
if (err)
- return err;
+ goto out_rtnl;
if (!drv->ops->del_station) {
err = -EOPNOTSUPP;
goto out;
}
- rtnl_lock();
err = drv->ops->del_station(&drv->wiphy, dev, mac_addr);
- rtnl_unlock();
out:
cfg80211_put_dev(drv);
dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
return err;
}
@@ -1605,22 +1718,29 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
return -EINVAL;
}
- netdev = dev_get_by_index(&init_net, ifidx);
- if (!netdev)
- return -ENODEV;
+ rtnl_lock();
+
+ netdev = __dev_get_by_index(&init_net, ifidx);
+ if (!netdev) {
+ err = -ENODEV;
+ goto out_rtnl;
+ }
dev = cfg80211_get_dev_from_ifindex(ifidx);
if (IS_ERR(dev)) {
err = PTR_ERR(dev);
- goto out_put_netdev;
+ goto out_rtnl;
}
if (!dev->ops->dump_mpath) {
- err = -ENOSYS;
+ err = -EOPNOTSUPP;
goto out_err;
}
- rtnl_lock();
+ if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
while (1) {
err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
@@ -1628,7 +1748,7 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
if (err == -ENOENT)
break;
if (err)
- goto out_err_rtnl;
+ goto out_err;
if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid,
cb->nlh->nlmsg_seq, NLM_F_MULTI,
@@ -1643,12 +1763,10 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
out:
cb->args[1] = path_idx;
err = skb->len;
- out_err_rtnl:
- rtnl_unlock();
out_err:
cfg80211_put_dev(dev);
- out_put_netdev:
- dev_put(netdev);
+ out_rtnl:
+ rtnl_unlock();
return err;
}
@@ -1670,19 +1788,23 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ rtnl_lock();
+
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
if (err)
- return err;
+ goto out_rtnl;
if (!drv->ops->get_mpath) {
err = -EOPNOTSUPP;
goto out;
}
- rtnl_lock();
- err = drv->ops->get_mpath(&drv->wiphy, dev, dst, next_hop, &pinfo);
- rtnl_unlock();
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+ err = drv->ops->get_mpath(&drv->wiphy, dev, dst, next_hop, &pinfo);
if (err)
goto out;
@@ -1699,10 +1821,12 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
out_free:
nlmsg_free(msg);
-
out:
cfg80211_put_dev(drv);
dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
return err;
}
@@ -1723,22 +1847,35 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
+ rtnl_lock();
+
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
if (err)
- return err;
+ goto out_rtnl;
if (!drv->ops->change_mpath) {
err = -EOPNOTSUPP;
goto out;
}
- rtnl_lock();
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (!netif_running(dev)) {
+ err = -ENETDOWN;
+ goto out;
+ }
+
err = drv->ops->change_mpath(&drv->wiphy, dev, dst, next_hop);
- rtnl_unlock();
out:
cfg80211_put_dev(drv);
dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
return err;
}
static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
@@ -1758,22 +1895,35 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
+ rtnl_lock();
+
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
if (err)
- return err;
+ goto out_rtnl;
if (!drv->ops->add_mpath) {
err = -EOPNOTSUPP;
goto out;
}
- rtnl_lock();
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (!netif_running(dev)) {
+ err = -ENETDOWN;
+ goto out;
+ }
+
err = drv->ops->add_mpath(&drv->wiphy, dev, dst, next_hop);
- rtnl_unlock();
out:
cfg80211_put_dev(drv);
dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
return err;
}
@@ -1787,22 +1937,25 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_MAC])
dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ rtnl_lock();
+
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
if (err)
- return err;
+ goto out_rtnl;
if (!drv->ops->del_mpath) {
err = -EOPNOTSUPP;
goto out;
}
- rtnl_lock();
err = drv->ops->del_mpath(&drv->wiphy, dev, dst);
- rtnl_unlock();
out:
cfg80211_put_dev(drv);
dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
return err;
}
@@ -1835,22 +1988,30 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
}
+ rtnl_lock();
+
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
if (err)
- return err;
+ goto out_rtnl;
if (!drv->ops->change_bss) {
err = -EOPNOTSUPP;
goto out;
}
- rtnl_lock();
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
err = drv->ops->change_bss(&drv->wiphy, dev, &params);
- rtnl_unlock();
out:
cfg80211_put_dev(drv);
dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
return err;
}
@@ -1945,10 +2106,12 @@ static int nl80211_get_mesh_params(struct sk_buff *skb,
struct nlattr *pinfoattr;
struct sk_buff *msg;
+ rtnl_lock();
+
/* Look up our device */
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
if (err)
- return err;
+ goto out_rtnl;
if (!drv->ops->get_mesh_params) {
err = -EOPNOTSUPP;
@@ -1956,9 +2119,7 @@ static int nl80211_get_mesh_params(struct sk_buff *skb,
}
/* Get the mesh params */
- rtnl_lock();
err = drv->ops->get_mesh_params(&drv->wiphy, dev, &cur_params);
- rtnl_unlock();
if (err)
goto out;
@@ -2007,13 +2168,16 @@ static int nl80211_get_mesh_params(struct sk_buff *skb,
err = genlmsg_unicast(msg, info->snd_pid);
goto out;
-nla_put_failure:
+ nla_put_failure:
genlmsg_cancel(msg, hdr);
err = -EMSGSIZE;
-out:
+ out:
/* Cleanup */
cfg80211_put_dev(drv);
dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
return err;
}
@@ -2060,9 +2224,11 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
parent_attr, nl80211_meshconf_params_policy))
return -EINVAL;
+ rtnl_lock();
+
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
if (err)
- return err;
+ goto out_rtnl;
if (!drv->ops->set_mesh_params) {
err = -EOPNOTSUPP;
@@ -2109,14 +2275,15 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
nla_get_u16);
/* Apply changes */
- rtnl_lock();
err = drv->ops->set_mesh_params(&drv->wiphy, dev, &cfg, mask);
- rtnl_unlock();
out:
/* cleanup */
cfg80211_put_dev(drv);
dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
return err;
}
@@ -2262,43 +2429,6 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
return -EINVAL;
}
-static int nl80211_set_mgmt_extra_ie(struct sk_buff *skb,
- struct genl_info *info)
-{
- struct cfg80211_registered_device *drv;
- int err;
- struct net_device *dev;
- struct mgmt_extra_ie_params params;
-
- memset(&params, 0, sizeof(params));
-
- if (!info->attrs[NL80211_ATTR_MGMT_SUBTYPE])
- return -EINVAL;
- params.subtype = nla_get_u8(info->attrs[NL80211_ATTR_MGMT_SUBTYPE]);
- if (params.subtype > 15)
- return -EINVAL; /* FC Subtype field is 4 bits (0..15) */
-
- if (info->attrs[NL80211_ATTR_IE]) {
- params.ies = nla_data(info->attrs[NL80211_ATTR_IE]);
- params.ies_len = nla_len(info->attrs[NL80211_ATTR_IE]);
- }
-
- err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
- if (err)
- return err;
-
- if (drv->ops->set_mgmt_extra_ie) {
- rtnl_lock();
- err = drv->ops->set_mgmt_extra_ie(&drv->wiphy, dev, &params);
- rtnl_unlock();
- } else
- err = -EOPNOTSUPP;
-
- cfg80211_put_dev(drv);
- dev_put(dev);
- return err;
-}
-
static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *drv;
@@ -2312,9 +2442,11 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
enum ieee80211_band band;
size_t ie_len;
+ rtnl_lock();
+
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
if (err)
- return err;
+ goto out_rtnl;
wiphy = &drv->wiphy;
@@ -2323,11 +2455,14 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
goto out;
}
- rtnl_lock();
+ if (!netif_running(dev)) {
+ err = -ENETDOWN;
+ goto out;
+ }
if (drv->scan_req) {
err = -EBUSY;
- goto out_unlock;
+ goto out;
}
if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
@@ -2335,7 +2470,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
n_channels++;
if (!n_channels) {
err = -EINVAL;
- goto out_unlock;
+ goto out;
}
} else {
for (band = 0; band < IEEE80211_NUM_BANDS; band++)
@@ -2349,7 +2484,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
if (n_ssids > wiphy->max_scan_ssids) {
err = -EINVAL;
- goto out_unlock;
+ goto out;
}
if (info->attrs[NL80211_ATTR_IE])
@@ -2363,7 +2498,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
+ ie_len, GFP_KERNEL);
if (!request) {
err = -ENOMEM;
- goto out_unlock;
+ goto out;
}
request->channels = (void *)((char *)request + sizeof(*request));
@@ -2434,11 +2569,12 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
drv->scan_req = NULL;
kfree(request);
}
- out_unlock:
- rtnl_unlock();
out:
cfg80211_put_dev(drv);
dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
return err;
}
@@ -2558,6 +2694,288 @@ static int nl80211_dump_scan(struct sk_buff *skb,
return err;
}
+static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
+{
+ return auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM ||
+ auth_type == NL80211_AUTHTYPE_SHARED_KEY ||
+ auth_type == NL80211_AUTHTYPE_FT ||
+ auth_type == NL80211_AUTHTYPE_NETWORK_EAP;
+}
+
+static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *drv;
+ struct net_device *dev;
+ struct cfg80211_auth_request req;
+ struct wiphy *wiphy;
+ int err;
+
+ rtnl_lock();
+
+ err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+ if (err)
+ goto unlock_rtnl;
+
+ if (!drv->ops->auth) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (!netif_running(dev)) {
+ err = -ENETDOWN;
+ goto out;
+ }
+
+ if (!info->attrs[NL80211_ATTR_MAC]) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ wiphy = &drv->wiphy;
+ memset(&req, 0, sizeof(req));
+
+ req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+ req.chan = ieee80211_get_channel(
+ wiphy,
+ nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
+ if (!req.chan) {
+ err = -EINVAL;
+ goto out;
+ }
+ }
+
+ if (info->attrs[NL80211_ATTR_SSID]) {
+ req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
+ req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
+ }
+
+ if (info->attrs[NL80211_ATTR_IE]) {
+ req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+ req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ }
+
+ if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
+ req.auth_type =
+ nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
+ if (!nl80211_valid_auth_type(req.auth_type)) {
+ err = -EINVAL;
+ goto out;
+ }
+ }
+
+ err = drv->ops->auth(&drv->wiphy, dev, &req);
+
+out:
+ cfg80211_put_dev(drv);
+ dev_put(dev);
+unlock_rtnl:
+ rtnl_unlock();
+ return err;
+}
+
+static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *drv;
+ struct net_device *dev;
+ struct cfg80211_assoc_request req;
+ struct wiphy *wiphy;
+ int err;
+
+ rtnl_lock();
+
+ err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+ if (err)
+ goto unlock_rtnl;
+
+ if (!drv->ops->assoc) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (!netif_running(dev)) {
+ err = -ENETDOWN;
+ goto out;
+ }
+
+ if (!info->attrs[NL80211_ATTR_MAC] ||
+ !info->attrs[NL80211_ATTR_SSID]) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ wiphy = &drv->wiphy;
+ memset(&req, 0, sizeof(req));
+
+ req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+ req.chan = ieee80211_get_channel(
+ wiphy,
+ nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
+ if (!req.chan) {
+ err = -EINVAL;
+ goto out;
+ }
+ }
+
+ req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
+ req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
+
+ if (info->attrs[NL80211_ATTR_IE]) {
+ req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+ req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ }
+
+ err = drv->ops->assoc(&drv->wiphy, dev, &req);
+
+out:
+ cfg80211_put_dev(drv);
+ dev_put(dev);
+unlock_rtnl:
+ rtnl_unlock();
+ return err;
+}
+
+static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *drv;
+ struct net_device *dev;
+ struct cfg80211_deauth_request req;
+ struct wiphy *wiphy;
+ int err;
+
+ rtnl_lock();
+
+ err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+ if (err)
+ goto unlock_rtnl;
+
+ if (!drv->ops->deauth) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (!netif_running(dev)) {
+ err = -ENETDOWN;
+ goto out;
+ }
+
+ if (!info->attrs[NL80211_ATTR_MAC]) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ wiphy = &drv->wiphy;
+ memset(&req, 0, sizeof(req));
+
+ req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ if (info->attrs[NL80211_ATTR_REASON_CODE]) {
+ req.reason_code =
+ nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+ if (req.reason_code == 0) {
+ /* Reason Code 0 is reserved */
+ err = -EINVAL;
+ goto out;
+ }
+ }
+
+ if (info->attrs[NL80211_ATTR_IE]) {
+ req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+ req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ }
+
+ err = drv->ops->deauth(&drv->wiphy, dev, &req);
+
+out:
+ cfg80211_put_dev(drv);
+ dev_put(dev);
+unlock_rtnl:
+ rtnl_unlock();
+ return err;
+}
+
+static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *drv;
+ struct net_device *dev;
+ struct cfg80211_disassoc_request req;
+ struct wiphy *wiphy;
+ int err;
+
+ rtnl_lock();
+
+ err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+ if (err)
+ goto unlock_rtnl;
+
+ if (!drv->ops->disassoc) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (!netif_running(dev)) {
+ err = -ENETDOWN;
+ goto out;
+ }
+
+ if (!info->attrs[NL80211_ATTR_MAC]) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ wiphy = &drv->wiphy;
+ memset(&req, 0, sizeof(req));
+
+ req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ if (info->attrs[NL80211_ATTR_REASON_CODE]) {
+ req.reason_code =
+ nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+ if (req.reason_code == 0) {
+ /* Reason Code 0 is reserved */
+ err = -EINVAL;
+ goto out;
+ }
+ }
+
+ if (info->attrs[NL80211_ATTR_IE]) {
+ req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+ req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ }
+
+ err = drv->ops->disassoc(&drv->wiphy, dev, &req);
+
+out:
+ cfg80211_put_dev(drv);
+ dev_put(dev);
+unlock_rtnl:
+ rtnl_unlock();
+ return err;
+}
+
static struct genl_ops nl80211_ops[] = {
{
.cmd = NL80211_CMD_GET_WIPHY,
@@ -2725,12 +3143,6 @@ static struct genl_ops nl80211_ops[] = {
.flags = GENL_ADMIN_PERM,
},
{
- .cmd = NL80211_CMD_SET_MGMT_EXTRA_IE,
- .doit = nl80211_set_mgmt_extra_ie,
- .policy = nl80211_policy,
- .flags = GENL_ADMIN_PERM,
- },
- {
.cmd = NL80211_CMD_TRIGGER_SCAN,
.doit = nl80211_trigger_scan,
.policy = nl80211_policy,
@@ -2741,6 +3153,33 @@ static struct genl_ops nl80211_ops[] = {
.policy = nl80211_policy,
.dumpit = nl80211_dump_scan,
},
+ {
+ .cmd = NL80211_CMD_AUTHENTICATE,
+ .doit = nl80211_authenticate,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NL80211_CMD_ASSOCIATE,
+ .doit = nl80211_associate,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NL80211_CMD_DEAUTHENTICATE,
+ .doit = nl80211_deauthenticate,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NL80211_CMD_DISASSOCIATE,
+ .doit = nl80211_disassociate,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+};
+static struct genl_multicast_group nl80211_mlme_mcgrp = {
+ .name = "mlme",
};
/* multicast groups */
@@ -2887,6 +3326,71 @@ nla_put_failure:
nlmsg_free(msg);
}
+static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ const u8 *buf, size_t len,
+ enum nl80211_commands cmd)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+ NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
+
+ if (genlmsg_end(msg, hdr) < 0) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_KERNEL);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
+void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *buf, size_t len)
+{
+ nl80211_send_mlme_event(rdev, netdev, buf, len,
+ NL80211_CMD_AUTHENTICATE);
+}
+
+void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *buf,
+ size_t len)
+{
+ nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_ASSOCIATE);
+}
+
+void nl80211_send_rx_deauth(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *buf,
+ size_t len)
+{
+ nl80211_send_mlme_event(rdev, netdev, buf, len,
+ NL80211_CMD_DEAUTHENTICATE);
+}
+
+void nl80211_send_rx_disassoc(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, const u8 *buf,
+ size_t len)
+{
+ nl80211_send_mlme_event(rdev, netdev, buf, len,
+ NL80211_CMD_DISASSOCIATE);
+}
+
/* initialisation/exit functions */
int nl80211_init(void)
@@ -2915,6 +3419,10 @@ int nl80211_init(void)
if (err)
goto err_out;
+ err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
+ if (err)
+ goto err_out;
+
return 0;
err_out:
genl_unregister_family(&nl80211_fam);
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index e65a3c38c52f..b77af4ab80be 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -3,7 +3,6 @@
#include "core.h"
-#ifdef CONFIG_NL80211
extern int nl80211_init(void);
extern void nl80211_exit(void);
extern void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev);
@@ -12,30 +11,17 @@ extern void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
extern void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
struct net_device *netdev);
extern void nl80211_send_reg_change_event(struct regulatory_request *request);
-#else
-static inline int nl80211_init(void)
-{
- return 0;
-}
-static inline void nl80211_exit(void)
-{
-}
-static inline void nl80211_notify_dev_rename(
- struct cfg80211_registered_device *rdev)
-{
-}
-static inline void
-nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
- struct net_device *netdev)
-{}
-static inline void nl80211_send_scan_aborted(
- struct cfg80211_registered_device *rdev,
- struct net_device *netdev)
-{}
-static inline void
-nl80211_send_reg_change_event(struct regulatory_request *request)
-{
-}
-#endif /* CONFIG_NL80211 */
+extern void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ const u8 *buf, size_t len);
+extern void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ const u8 *buf, size_t len);
+extern void nl80211_send_rx_deauth(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ const u8 *buf, size_t len);
+extern void nl80211_send_rx_disassoc(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ const u8 *buf, size_t len);
#endif /* __NET_WIRELESS_NL80211_H */
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index eb8b8ed16155..6327e1617acb 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -122,9 +122,14 @@ static const struct ieee80211_regdomain *cfg80211_world_regdom =
#ifdef CONFIG_WIRELESS_OLD_REGULATORY
static char *ieee80211_regdom = "US";
+#else
+static char *ieee80211_regdom = "00";
+#endif
+
module_param(ieee80211_regdom, charp, 0444);
MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
+#ifdef CONFIG_WIRELESS_OLD_REGULATORY
/*
* We assume 40 MHz bandwidth for the old regulatory work.
* We make emphasis we are using the exact same frequencies
@@ -1415,16 +1420,6 @@ new_request:
return r;
}
- /*
- * Note: When CONFIG_WIRELESS_OLD_REGULATORY is enabled
- * AND if CRDA is NOT present nothing will happen, if someone
- * wants to bother with 11d with OLD_REG you can add a timer.
- * If after x amount of time nothing happens you can call:
- *
- * return set_regdom(country_ie_regdomain);
- *
- * to intersect with the static rd
- */
return call_crda(last_request->alpha2);
}
@@ -1601,6 +1596,10 @@ static bool reg_same_country_ie_hint(struct wiphy *wiphy,
assert_cfg80211_lock();
+ if (unlikely(last_request->initiator !=
+ NL80211_REGDOM_SET_BY_COUNTRY_IE))
+ return false;
+
request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
if (!request_wiphy)
@@ -1663,7 +1662,9 @@ void regulatory_hint_11d(struct wiphy *wiphy,
* we optimize an early check to exit out early if we don't have to
* do anything
*/
- if (likely(wiphy_idx_valid(last_request->wiphy_idx))) {
+ if (likely(last_request->initiator ==
+ NL80211_REGDOM_SET_BY_COUNTRY_IE &&
+ wiphy_idx_valid(last_request->wiphy_idx))) {
struct cfg80211_registered_device *drv_last_ie;
drv_last_ie =
@@ -2022,28 +2023,21 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
*/
BUG_ON(!country_ie_regdomain);
+ BUG_ON(rd == country_ie_regdomain);
- if (rd != country_ie_regdomain) {
- /*
- * Intersect what CRDA returned and our what we
- * had built from the Country IE received
- */
+ /*
+ * Intersect what CRDA returned and our what we
+ * had built from the Country IE received
+ */
- intersected_rd = regdom_intersect(rd, country_ie_regdomain);
+ intersected_rd = regdom_intersect(rd, country_ie_regdomain);
- reg_country_ie_process_debug(rd, country_ie_regdomain,
- intersected_rd);
+ reg_country_ie_process_debug(rd,
+ country_ie_regdomain,
+ intersected_rd);
- kfree(country_ie_regdomain);
- country_ie_regdomain = NULL;
- } else {
- /*
- * This would happen when CRDA was not present and
- * OLD_REGULATORY was enabled. We intersect our Country
- * IE rd and what was set on cfg80211 originally
- */
- intersected_rd = regdom_intersect(rd, cfg80211_regdomain);
- }
+ kfree(country_ie_regdomain);
+ country_ie_regdomain = NULL;
if (!intersected_rd)
return -EINVAL;
@@ -2135,15 +2129,18 @@ int regulatory_init(void)
/*
* The old code still requests for a new regdomain and if
* you have CRDA you get it updated, otherwise you get
- * stuck with the static values. We ignore "EU" code as
- * that is not a valid ISO / IEC 3166 alpha2
+ * stuck with the static values. Since "EU" is not a valid
+ * ISO / IEC 3166 alpha2 code we can't expect userpace to
+ * give us a regulatory domain for it. We need last_request
+ * iniitalized though so lets just send a request which we
+ * know will be ignored... this crap will be removed once
+ * OLD_REG dies.
*/
- if (ieee80211_regdom[0] != 'E' || ieee80211_regdom[1] != 'U')
- err = regulatory_hint_core(ieee80211_regdom);
+ err = regulatory_hint_core(ieee80211_regdom);
#else
cfg80211_regdomain = cfg80211_world_regdom;
- err = regulatory_hint_core("00");
+ err = regulatory_hint_core(ieee80211_regdom);
#endif
if (err) {
if (err == -ENOMEM)
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 280dbcd02c15..2a00e362f5fe 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -80,7 +80,8 @@ void cfg80211_bss_expire(struct cfg80211_registered_device *dev)
bool expired = false;
list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) {
- if (!time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE))
+ if (bss->hold ||
+ !time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE))
continue;
list_del(&bss->list);
rb_erase(&bss->rbn, &dev->bss_tree);
@@ -471,6 +472,30 @@ void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
}
EXPORT_SYMBOL(cfg80211_unlink_bss);
+void cfg80211_hold_bss(struct cfg80211_bss *pub)
+{
+ struct cfg80211_internal_bss *bss;
+
+ if (!pub)
+ return;
+
+ bss = container_of(pub, struct cfg80211_internal_bss, pub);
+ bss->hold = true;
+}
+EXPORT_SYMBOL(cfg80211_hold_bss);
+
+void cfg80211_unhold_bss(struct cfg80211_bss *pub)
+{
+ struct cfg80211_internal_bss *bss;
+
+ if (!pub)
+ return;
+
+ bss = container_of(pub, struct cfg80211_internal_bss, pub);
+ bss->hold = false;
+}
+EXPORT_SYMBOL(cfg80211_unhold_bss);
+
#ifdef CONFIG_WIRELESS_EXT
int cfg80211_wext_siwscan(struct net_device *dev,
struct iw_request_info *info,
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index b84a9b4fe96a..0fd1db6e95bb 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -66,6 +66,7 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info,
struct cfg80211_registered_device *rdev;
struct vif_params vifparams;
enum nl80211_iftype type;
+ int ret;
if (!wdev)
return -EOPNOTSUPP;
@@ -96,10 +97,16 @@ int cfg80211_wext_siwmode(struct net_device *dev, struct iw_request_info *info,
return -EINVAL;
}
+ if (type == wdev->iftype)
+ return 0;
+
memset(&vifparams, 0, sizeof(vifparams));
- return rdev->ops->change_virtual_intf(wdev->wiphy, dev->ifindex, type,
- NULL, &vifparams);
+ ret = rdev->ops->change_virtual_intf(wdev->wiphy, dev->ifindex, type,
+ NULL, &vifparams);
+ WARN_ON(!ret && wdev->iftype != type);
+
+ return ret;
}
EXPORT_SYMBOL(cfg80211_wext_siwmode);