summaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/Kconfig13
-rw-r--r--net/mac80211/Makefile2
-rw-r--r--net/mac80211/agg-rx.c18
-rw-r--r--net/mac80211/agg-tx.c138
-rw-r--r--net/mac80211/cfg.c55
-rw-r--r--net/mac80211/debugfs.c75
-rw-r--r--net/mac80211/debugfs.h2
-rw-r--r--net/mac80211/debugfs_key.c44
-rw-r--r--net/mac80211/debugfs_netdev.c176
-rw-r--r--net/mac80211/debugfs_sta.c67
-rw-r--r--net/mac80211/driver-ops.h5
-rw-r--r--net/mac80211/driver-trace.h35
-rw-r--r--net/mac80211/ht.c10
-rw-r--r--net/mac80211/ibss.c7
-rw-r--r--net/mac80211/ieee80211_i.h190
-rw-r--r--net/mac80211/iface.c44
-rw-r--r--net/mac80211/key.h12
-rw-r--r--net/mac80211/main.c350
-rw-r--r--net/mac80211/mesh.c153
-rw-r--r--net/mac80211/mesh.h30
-rw-r--r--net/mac80211/mesh_hwmp.c413
-rw-r--r--net/mac80211/mesh_pathtbl.c21
-rw-r--r--net/mac80211/mesh_plink.c58
-rw-r--r--net/mac80211/mlme.c53
-rw-r--r--net/mac80211/rate.c19
-rw-r--r--net/mac80211/rate.h9
-rw-r--r--net/mac80211/rx.c991
-rw-r--r--net/mac80211/scan.c129
-rw-r--r--net/mac80211/spectmgmt.c2
-rw-r--r--net/mac80211/sta_info.c175
-rw-r--r--net/mac80211/sta_info.h51
-rw-r--r--net/mac80211/status.c341
-rw-r--r--net/mac80211/tkip.c4
-rw-r--r--net/mac80211/tx.c190
-rw-r--r--net/mac80211/util.c41
-rw-r--r--net/mac80211/wep.c8
-rw-r--r--net/mac80211/wpa.c25
37 files changed, 2105 insertions, 1851 deletions
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index 4d5543af312..a10d508b07e 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -194,6 +194,19 @@ config MAC80211_VERBOSE_MPL_DEBUG
Do not select this option.
+config MAC80211_VERBOSE_MHWMP_DEBUG
+ bool "Verbose mesh HWMP routing debugging"
+ depends on MAC80211_DEBUG_MENU
+ depends on MAC80211_MESH
+ ---help---
+ Selecting this option causes mac80211 to print out very
+ verbose mesh routing (HWMP) debugging messages (when mac80211
+ is taking part in a mesh network).
+ It should not be selected on production systems as those
+ messages are remotely triggerable.
+
+ Do not select this option.
+
config MAC80211_DEBUG_COUNTERS
bool "Extra statistics for TX/RX debugging"
depends on MAC80211_DEBUG_MENU
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index 9f3cf712932..298cfcc1bf8 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -2,7 +2,7 @@ obj-$(CONFIG_MAC80211) += mac80211.o
# mac80211 objects
mac80211-y := \
- main.o \
+ main.o status.o \
sta_info.o \
wep.o \
wpa.o \
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index bc064d7933f..51c7dc3c4c3 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -41,7 +41,8 @@ void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
sta->sta.addr, tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
- if (drv_ampdu_action(local, IEEE80211_AMPDU_RX_STOP,
+ if (drv_ampdu_action(local, &sta->sdata->vif,
+ IEEE80211_AMPDU_RX_STOP,
&sta->sta, tid, NULL))
printk(KERN_DEBUG "HW problem - can not stop rx "
"aggregation for tid %d\n", tid);
@@ -85,10 +86,6 @@ void ieee80211_sta_stop_rx_ba_session(struct ieee80211_sub_if_data *sdata, u8 *r
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
- /* stop HW Rx aggregation. ampdu_action existence
- * already verified in session init so we add the BUG_ON */
- BUG_ON(!local->ops->ampdu_action);
-
rcu_read_lock();
sta = sta_info_get(local, ra);
@@ -170,7 +167,7 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d
mgmt->u.action.u.addba_resp.timeout = cpu_to_le16(timeout);
mgmt->u.action.u.addba_resp.status = cpu_to_le16(status);
- ieee80211_tx_skb(sdata, skb, 1);
+ ieee80211_tx_skb(sdata, skb);
}
void ieee80211_process_addba_request(struct ieee80211_local *local,
@@ -210,9 +207,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
* check if configuration can support the BA policy
* and if buffer size does not exceeds max value */
/* XXX: check own ht delayed BA capability?? */
- if (((ba_policy != 1)
- && (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_DELAY_BA)))
- || (buf_size > IEEE80211_MAX_AMPDU_BUF)) {
+ if (((ba_policy != 1) &&
+ (!(sta->sta.ht_cap.cap & IEEE80211_HT_CAP_DELAY_BA))) ||
+ (buf_size > IEEE80211_MAX_AMPDU_BUF)) {
status = WLAN_STATUS_INVALID_QOS_PARAM;
#ifdef CONFIG_MAC80211_HT_DEBUG
if (net_ratelimit())
@@ -284,7 +281,8 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
goto end;
}
- ret = drv_ampdu_action(local, IEEE80211_AMPDU_RX_START,
+ ret = drv_ampdu_action(local, &sta->sdata->vif,
+ IEEE80211_AMPDU_RX_START,
&sta->sta, tid, &start_seq_num);
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Rx A-MPDU request on tid %d result %d\n", tid, ret);
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index b09948ceec4..5e3a7eccef5 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -91,7 +91,7 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
mgmt->u.action.u.addba_req.start_seq_num =
cpu_to_le16(start_seq_num << 4);
- ieee80211_tx_skb(sdata, skb, 1);
+ ieee80211_tx_skb(sdata, skb);
}
void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u16 ssn)
@@ -120,16 +120,22 @@ void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u1
bar->control = cpu_to_le16(bar_control);
bar->start_seq_num = cpu_to_le16(ssn);
- ieee80211_tx_skb(sdata, skb, 0);
+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+ ieee80211_tx_skb(sdata, skb);
}
-static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
- enum ieee80211_back_parties initiator)
+int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
+ enum ieee80211_back_parties initiator)
{
struct ieee80211_local *local = sta->local;
int ret;
u8 *state;
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "Tx BA session stop requested for %pM tid %u\n",
+ sta->sta.addr, tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+
state = &sta->ampdu_mlme.tid_state_tx[tid];
if (*state == HT_AGG_STATE_OPERATIONAL)
@@ -138,12 +144,12 @@ static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
*state = HT_AGG_STATE_REQ_STOP_BA_MSK |
(initiator << HT_AGG_STATE_INITIATOR_SHIFT);
- ret = drv_ampdu_action(local, IEEE80211_AMPDU_TX_STOP,
+ ret = drv_ampdu_action(local, &sta->sdata->vif,
+ IEEE80211_AMPDU_TX_STOP,
&sta->sta, tid, NULL);
/* 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.
@@ -173,12 +179,14 @@ static void sta_addba_resp_timer_expired(unsigned long data)
/* check if the TID waits for addBA response */
spin_lock_bh(&sta->lock);
- if (!(*state & HT_ADDBA_REQUESTED_MSK)) {
+ if ((*state & (HT_ADDBA_REQUESTED_MSK | HT_ADDBA_RECEIVED_MSK)) !=
+ HT_ADDBA_REQUESTED_MSK) {
spin_unlock_bh(&sta->lock);
*state = HT_AGG_STATE_IDLE;
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "timer expired on tid %d but we are not "
- "expecting addBA response there", tid);
+ "(or no longer) expecting addBA response there",
+ tid);
#endif
return;
}
@@ -196,11 +204,11 @@ static inline int ieee80211_ac_from_tid(int tid)
return ieee802_1d_to_ac[tid & 7];
}
-int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
+int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid)
{
- struct ieee80211_local *local = hw_to_local(hw);
- struct sta_info *sta;
- struct ieee80211_sub_if_data *sdata;
+ struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct ieee80211_local *local = sdata->local;
u8 *state;
int ret = 0;
u16 start_seq_num;
@@ -208,52 +216,37 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
if (WARN_ON(!local->ops->ampdu_action))
return -EINVAL;
- if ((tid >= STA_TID_NUM) || !(hw->flags & IEEE80211_HW_AMPDU_AGGREGATION))
+ if ((tid >= STA_TID_NUM) ||
+ !(local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION))
return -EINVAL;
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Open BA session requested for %pM tid %u\n",
- ra, tid);
+ pubsta->addr, tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
- rcu_read_lock();
-
- sta = sta_info_get(local, ra);
- if (!sta) {
-#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "Could not find the station\n");
-#endif
- ret = -ENOENT;
- goto unlock;
- }
-
/*
* The aggregation code is not prepared to handle
* anything but STA/AP due to the BSSID handling.
* IBSS could work in the code but isn't supported
* by drivers or the standard.
*/
- if (sta->sdata->vif.type != NL80211_IFTYPE_STATION &&
- sta->sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
- sta->sdata->vif.type != NL80211_IFTYPE_AP) {
- ret = -EINVAL;
- goto unlock;
- }
+ if (sdata->vif.type != NL80211_IFTYPE_STATION &&
+ sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
+ sdata->vif.type != NL80211_IFTYPE_AP)
+ return -EINVAL;
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;
+ return -EINVAL;
}
spin_lock_bh(&sta->lock);
spin_lock(&local->ampdu_lock);
- sdata = sta->sdata;
-
/* we have tried too many times, receiver does not want A-MPDU */
if (sta->ampdu_mlme.addba_req_num[tid] > HT_AGG_MAX_RETRIES) {
ret = -EBUSY;
@@ -310,8 +303,9 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
start_seq_num = sta->tid_seq[tid];
- ret = drv_ampdu_action(local, IEEE80211_AMPDU_TX_START,
- &sta->sta, tid, &start_seq_num);
+ ret = drv_ampdu_action(local, &sdata->vif,
+ IEEE80211_AMPDU_TX_START,
+ pubsta, tid, &start_seq_num);
if (ret) {
#ifdef CONFIG_MAC80211_HT_DEBUG
@@ -336,7 +330,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
sta->ampdu_mlme.dialog_token_allocator;
sta->ampdu_mlme.tid_tx[tid]->ssn = start_seq_num;
- ieee80211_send_addba_request(sta->sdata, ra, tid,
+ ieee80211_send_addba_request(sdata, pubsta->addr, tid,
sta->ampdu_mlme.tid_tx[tid]->dialog_token,
sta->ampdu_mlme.tid_tx[tid]->ssn,
0x40, 5000);
@@ -348,7 +342,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "activated addBA response timer on tid %d\n", tid);
#endif
- goto unlock;
+ return 0;
err_free:
kfree(sta->ampdu_mlme.tid_tx[tid]);
@@ -360,8 +354,6 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
err_unlock_sta:
spin_unlock(&local->ampdu_lock);
spin_unlock_bh(&sta->lock);
- unlock:
- rcu_read_unlock();
return ret;
}
EXPORT_SYMBOL(ieee80211_start_tx_ba_session);
@@ -428,13 +420,15 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local,
ieee80211_agg_splice_finish(local, sta, tid);
spin_unlock(&local->ampdu_lock);
- drv_ampdu_action(local, IEEE80211_AMPDU_TX_OPERATIONAL,
+ drv_ampdu_action(local, &sta->sdata->vif,
+ IEEE80211_AMPDU_TX_OPERATIONAL,
&sta->sta, tid, NULL);
}
-void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
+void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid)
{
- struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+ struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
u8 *state;
@@ -483,10 +477,11 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
}
EXPORT_SYMBOL(ieee80211_start_tx_ba_cb);
-void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_hw *hw,
+void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_vif *vif,
const u8 *ra, u16 tid)
{
- struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+ struct ieee80211_local *local = sdata->local;
struct ieee80211_ra_tid *ra_tid;
struct sk_buff *skb = dev_alloc_skb(0);
@@ -501,6 +496,7 @@ void ieee80211_start_tx_ba_cb_irqsafe(struct ieee80211_hw *hw,
ra_tid = (struct ieee80211_ra_tid *) &skb->cb;
memcpy(&ra_tid->ra, ra, ETH_ALEN);
ra_tid->tid = tid;
+ ra_tid->vif = vif;
skb->pkt_type = IEEE80211_ADDBA_MSG;
skb_queue_tail(&local->skb_queue, skb);
@@ -523,11 +519,6 @@ int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
goto unlock;
}
-#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "Tx BA session stop requested for %pM tid %u\n",
- sta->sta.addr, tid);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
-
ret = ___ieee80211_stop_tx_ba_session(sta, tid, initiator);
unlock:
@@ -535,36 +526,27 @@ int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
return ret;
}
-int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
- u8 *ra, u16 tid,
+int ieee80211_stop_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
enum ieee80211_back_parties initiator)
{
- struct ieee80211_local *local = hw_to_local(hw);
- struct sta_info *sta;
- int ret = 0;
+ struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct ieee80211_local *local = sdata->local;
- if (WARN_ON(!local->ops->ampdu_action))
+ if (!local->ops->ampdu_action)
return -EINVAL;
if (tid >= STA_TID_NUM)
return -EINVAL;
- rcu_read_lock();
- sta = sta_info_get(local, ra);
- if (!sta) {
- rcu_read_unlock();
- return -ENOENT;
- }
-
- ret = __ieee80211_stop_tx_ba_session(sta, tid, initiator);
- rcu_read_unlock();
- return ret;
+ return __ieee80211_stop_tx_ba_session(sta, tid, initiator);
}
EXPORT_SYMBOL(ieee80211_stop_tx_ba_session);
-void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
+void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid)
{
- struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+ struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
u8 *state;
@@ -627,10 +609,11 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
}
EXPORT_SYMBOL(ieee80211_stop_tx_ba_cb);
-void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_hw *hw,
+void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_vif *vif,
const u8 *ra, u16 tid)
{
- struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+ struct ieee80211_local *local = sdata->local;
struct ieee80211_ra_tid *ra_tid;
struct sk_buff *skb = dev_alloc_skb(0);
@@ -645,6 +628,7 @@ void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_hw *hw,
ra_tid = (struct ieee80211_ra_tid *) &skb->cb;
memcpy(&ra_tid->ra, ra, ETH_ALEN);
ra_tid->tid = tid;
+ ra_tid->vif = vif;
skb->pkt_type = IEEE80211_DELBA_MSG;
skb_queue_tail(&local->skb_queue, skb);
@@ -666,21 +650,21 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
state = &sta->ampdu_mlme.tid_state_tx[tid];
- del_timer_sync(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
-
spin_lock_bh(&sta->lock);
if (!(*state & HT_ADDBA_REQUESTED_MSK))
- goto timer_still_needed;
+ goto out;
if (mgmt->u.action.u.addba_resp.dialog_token !=
sta->ampdu_mlme.tid_tx[tid]->dialog_token) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "wrong addBA response token, tid %d\n", tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
- goto timer_still_needed;
+ goto out;
}
+ del_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
+
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "switched off addBA timer for tid %d \n", tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
@@ -699,10 +683,6 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
___ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR);
}
- goto out;
-
- timer_still_needed:
- add_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
out:
spin_unlock_bh(&sta->lock);
}
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 7b5131bd6fa..93ee1fd5c08 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -36,6 +36,15 @@ static bool nl80211_type_check(enum nl80211_iftype type)
}
}
+static bool nl80211_params_check(enum nl80211_iftype type,
+ struct vif_params *params)
+{
+ if (!nl80211_type_check(type))
+ return false;
+
+ return true;
+}
+
static int ieee80211_add_iface(struct wiphy *wiphy, char *name,
enum nl80211_iftype type, u32 *flags,
struct vif_params *params)
@@ -45,7 +54,7 @@ static int ieee80211_add_iface(struct wiphy *wiphy, char *name,
struct ieee80211_sub_if_data *sdata;
int err;
- if (!nl80211_type_check(type))
+ if (!nl80211_params_check(type, params))
return -EINVAL;
err = ieee80211_if_add(local, name, &dev, type, params);
@@ -75,7 +84,7 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
if (netif_running(dev))
return -EBUSY;
- if (!nl80211_type_check(type))
+ if (!nl80211_params_check(type, params))
return -EINVAL;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -92,6 +101,13 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
if (sdata->vif.type != NL80211_IFTYPE_MONITOR || !flags)
return 0;
+ if (type == NL80211_IFTYPE_AP_VLAN &&
+ params && params->use_4addr == 0)
+ rcu_assign_pointer(sdata->u.vlan.sta, NULL);
+ else if (type == NL80211_IFTYPE_STATION &&
+ params && params->use_4addr >= 0)
+ sdata->u.mgd.use_4addr = params->use_4addr;
+
sdata->u.mntr_flags = *flags;
return 0;
}
@@ -377,13 +393,13 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
int idx, u8 *mac, struct station_info *sinfo)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct sta_info *sta;
int ret = -ENOENT;
rcu_read_lock();
- sta = sta_info_get_by_idx(local, idx, dev);
+ sta = sta_info_get_by_idx(sdata, idx);
if (sta) {
ret = 0;
memcpy(mac, sta->sta.addr, ETH_ALEN);
@@ -738,13 +754,6 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
err = sta_info_insert(sta);
if (err) {
- /* STA has been freed */
- if (err == -EEXIST && layer2_update) {
- /* Need to update layer 2 devices on reassociation */
- sta = sta_info_get(local, mac);
- if (sta)
- ieee80211_send_layer2_update(sta);
- }
rcu_read_unlock();
return err;
}
@@ -813,6 +822,15 @@ static int ieee80211_change_station(struct wiphy *wiphy,
return -EINVAL;
}
+ if (params->vlan->ieee80211_ptr->use_4addr) {
+ if (vlansdata->u.vlan.sta) {
+ rcu_read_unlock();
+ return -EBUSY;
+ }
+
+ rcu_assign_pointer(vlansdata->u.vlan.sta, sta);
+ }
+
sta->sdata = vlansdata;
ieee80211_send_layer2_update(sta);
}
@@ -914,7 +932,7 @@ static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,
pinfo->generation = mesh_paths_generation;
pinfo->filled = MPATH_INFO_FRAME_QLEN |
- MPATH_INFO_DSN |
+ MPATH_INFO_SN |
MPATH_INFO_METRIC |
MPATH_INFO_EXPTIME |
MPATH_INFO_DISCOVERY_TIMEOUT |
@@ -922,7 +940,7 @@ static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,
MPATH_INFO_FLAGS;
pinfo->frame_qlen = mpath->frame_queue.qlen;
- pinfo->dsn = mpath->dsn;
+ pinfo->sn = mpath->sn;
pinfo->metric = mpath->metric;
if (time_before(jiffies, mpath->exp_time))
pinfo->exptime = jiffies_to_msecs(mpath->exp_time - jiffies);
@@ -934,8 +952,8 @@ static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,
pinfo->flags |= NL80211_MPATH_FLAG_ACTIVE;
if (mpath->flags & MESH_PATH_RESOLVING)
pinfo->flags |= NL80211_MPATH_FLAG_RESOLVING;
- if (mpath->flags & MESH_PATH_DSN_VALID)
- pinfo->flags |= NL80211_MPATH_FLAG_DSN_VALID;
+ if (mpath->flags & MESH_PATH_SN_VALID)
+ pinfo->flags |= NL80211_MPATH_FLAG_SN_VALID;
if (mpath->flags & MESH_PATH_FIXED)
pinfo->flags |= NL80211_MPATH_FLAG_FIXED;
if (mpath->flags & MESH_PATH_RESOLVING)
@@ -1008,7 +1026,10 @@ static int ieee80211_set_mesh_params(struct wiphy *wiphy,
{
struct mesh_config *conf;
struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_if_mesh *ifmsh;
+
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ ifmsh = &sdata->u.mesh;
/* Set the config options which we are interested in setting */
conf = &(sdata->u.mesh.mshcfg);
@@ -1043,6 +1064,10 @@ static int ieee80211_set_mesh_params(struct wiphy *wiphy,
mask))
conf->dot11MeshHWMPnetDiameterTraversalTime =
nconf->dot11MeshHWMPnetDiameterTraversalTime;
+ if (_chg_mesh_attr(NL80211_MESHCONF_HWMP_ROOTMODE, mask)) {
+ conf->dot11MeshHWMPRootMode = nconf->dot11MeshHWMPRootMode;
+ ieee80211_mesh_root_setup(ifmsh);
+ }
return 0;
}
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 96991b68f04..e4b54093d41 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -1,3 +1,4 @@
+
/*
* mac80211 debugfs for wireless PHYs
*
@@ -38,16 +39,10 @@ static const struct file_operations name## _ops = { \
};
#define DEBUGFS_ADD(name) \
- local->debugfs.name = debugfs_create_file(#name, 0400, phyd, \
- local, &name## _ops);
+ 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;
+ debugfs_create_file(#name, mode, phyd, local, &name## _ops);
DEBUGFS_READONLY_FILE(frequency, 20, "%d",
@@ -57,7 +52,7 @@ DEBUGFS_READONLY_FILE(total_ps_buffered, 20, "%d",
DEBUGFS_READONLY_FILE(wep_iv, 20, "%#08x",
local->wep_iv & 0xffffff);
DEBUGFS_READONLY_FILE(rate_ctrl_alg, 100, "%s",
- local->rate_ctrl ? local->rate_ctrl->ops->name : "<unset>");
+ local->rate_ctrl ? local->rate_ctrl->ops->name : "hw/driver");
static ssize_t tsf_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
@@ -233,12 +228,7 @@ static const struct file_operations stats_ ##name## _ops = { \
};
#define DEBUGFS_STATS_ADD(name) \
- local->debugfs.stats.name = debugfs_create_file(#name, 0400, statsd,\
- local, &stats_ ##name## _ops);
-
-#define DEBUGFS_STATS_DEL(name) \
- debugfs_remove(local->debugfs.stats.name); \
- local->debugfs.stats.name = NULL;
+ debugfs_create_file(#name, 0400, statsd, local, &stats_ ##name## _ops);
DEBUGFS_STATS_FILE(transmitted_fragment_count, 20, "%u",
local->dot11TransmittedFragmentCount);
@@ -326,7 +316,6 @@ void debugfs_hw_add(struct ieee80211_local *local)
DEBUGFS_ADD(noack);
statsd = debugfs_create_dir("statistics", phyd);
- local->debugfs.statistics = statsd;
/* if the dir failed, don't put all the other things into the root! */
if (!statsd)
@@ -367,57 +356,3 @@ void debugfs_hw_add(struct ieee80211_local *local)
DEBUGFS_STATS_ADD(dot11FCSErrorCount);
DEBUGFS_STATS_ADD(dot11RTSSuccessCount);
}
-
-void debugfs_hw_del(struct ieee80211_local *local)
-{
- DEBUGFS_DEL(frequency);
- DEBUGFS_DEL(total_ps_buffered);
- DEBUGFS_DEL(wep_iv);
- DEBUGFS_DEL(tsf);
- DEBUGFS_DEL(queues);
- DEBUGFS_DEL(reset);
- DEBUGFS_DEL(noack);
-
- DEBUGFS_STATS_DEL(transmitted_fragment_count);
- DEBUGFS_STATS_DEL(multicast_transmitted_frame_count);
- DEBUGFS_STATS_DEL(failed_count);
- DEBUGFS_STATS_DEL(retry_count);
- DEBUGFS_STATS_DEL(multiple_retry_count);
- DEBUGFS_STATS_DEL(frame_duplicate_count);
- DEBUGFS_STATS_DEL(received_fragment_count);
- DEBUGFS_STATS_DEL(multicast_received_frame_count);
- DEBUGFS_STATS_DEL(transmitted_frame_count);
- DEBUGFS_STATS_DEL(num_scans);
-#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
- DEBUGFS_STATS_DEL(tx_handlers_drop);
- DEBUGFS_STATS_DEL(tx_handlers_queued);
- DEBUGFS_STATS_DEL(tx_handlers_drop_unencrypted);
- DEBUGFS_STATS_DEL(tx_handlers_drop_fragment);
- DEBUGFS_STATS_DEL(tx_handlers_drop_wep);
- DEBUGFS_STATS_DEL(tx_handlers_drop_not_assoc);
- DEBUGFS_STATS_DEL(tx_handlers_drop_unauth_port);
- DEBUGFS_STATS_DEL(rx_handlers_drop);
- DEBUGFS_STATS_DEL(rx_handlers_queued);
- DEBUGFS_STATS_DEL(rx_handlers_drop_nullfunc);
- DEBUGFS_STATS_DEL(rx_handlers_drop_defrag);
- DEBUGFS_STATS_DEL(rx_handlers_drop_short);
- DEBUGFS_STATS_DEL(rx_handlers_drop_passive_scan);
- DEBUGFS_STATS_DEL(tx_expand_skb_head);
- DEBUGFS_STATS_DEL(tx_expand_skb_head_cloned);
- DEBUGFS_STATS_DEL(rx_expand_skb_head);
- DEBUGFS_STATS_DEL(rx_expand_skb_head2);
- DEBUGFS_STATS_DEL(rx_handlers_fragments);
- DEBUGFS_STATS_DEL(tx_status_drop);
-#endif
- DEBUGFS_STATS_DEL(dot11ACKFailureCount);
- DEBUGFS_STATS_DEL(dot11RTSFailureCount);
- DEBUGFS_STATS_DEL(dot11FCSErrorCount);
- DEBUGFS_STATS_DEL(dot11RTSSuccessCount);
-
- debugfs_remove(local->debugfs.statistics);
- local->debugfs.statistics = NULL;
- debugfs_remove(local->debugfs.stations);
- local->debugfs.stations = NULL;
- debugfs_remove(local->debugfs.keys);
- local->debugfs.keys = NULL;
-}
diff --git a/net/mac80211/debugfs.h b/net/mac80211/debugfs.h
index dd2541935c2..68e6a2050f9 100644
--- a/net/mac80211/debugfs.h
+++ b/net/mac80211/debugfs.h
@@ -3,14 +3,12 @@
#ifdef CONFIG_MAC80211_DEBUGFS
extern void debugfs_hw_add(struct ieee80211_local *local);
-extern void debugfs_hw_del(struct ieee80211_local *local);
extern int mac80211_open_file_generic(struct inode *inode, struct file *file);
#else
static inline void debugfs_hw_add(struct ieee80211_local *local)
{
return;
}
-static inline void debugfs_hw_del(struct ieee80211_local *local) {}
#endif
#endif /* __MAC80211_DEBUGFS_H */
diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c
index 99c752588b3..e0f5224630d 100644
--- a/net/mac80211/debugfs_key.c
+++ b/net/mac80211/debugfs_key.c
@@ -225,8 +225,8 @@ static ssize_t key_key_read(struct file *file, char __user *userbuf,
KEY_OPS(key);
#define DEBUGFS_ADD(name) \
- key->debugfs.name = debugfs_create_file(#name, 0400,\
- key->debugfs.dir, key, &key_##name##_ops);
+ debugfs_create_file(#name, 0400, key->debugfs.dir, \
+ key, &key_##name##_ops);
void ieee80211_debugfs_key_add(struct ieee80211_key *key)
{
@@ -271,30 +271,12 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key)
DEBUGFS_ADD(ifindex);
};
-#define DEBUGFS_DEL(name) \
- debugfs_remove(key->debugfs.name); key->debugfs.name = NULL;
-
void ieee80211_debugfs_key_remove(struct ieee80211_key *key)
{
if (!key)
return;
- DEBUGFS_DEL(keylen);
- DEBUGFS_DEL(flags);
- DEBUGFS_DEL(keyidx);
- DEBUGFS_DEL(hw_key_idx);
- DEBUGFS_DEL(tx_rx_count);
- DEBUGFS_DEL(algorithm);
- DEBUGFS_DEL(tx_spec);
- DEBUGFS_DEL(rx_spec);
- DEBUGFS_DEL(replays);
- DEBUGFS_DEL(icverrors);
- DEBUGFS_DEL(key);
- DEBUGFS_DEL(ifindex);
-
- debugfs_remove(key->debugfs.stalink);
- key->debugfs.stalink = NULL;
- debugfs_remove(key->debugfs.dir);
+ debugfs_remove_recursive(key->debugfs.dir);
key->debugfs.dir = NULL;
}
void ieee80211_debugfs_key_add_default(struct ieee80211_sub_if_data *sdata)
@@ -302,7 +284,7 @@ void ieee80211_debugfs_key_add_default(struct ieee80211_sub_if_data *sdata)
char buf[50];
struct ieee80211_key *key;
- if (!sdata->debugfsdir)
+ if (!sdata->debugfs.dir)
return;
/* this is running under the key lock */
@@ -310,9 +292,9 @@ void ieee80211_debugfs_key_add_default(struct ieee80211_sub_if_data *sdata)
key = sdata->default_key;
if (key) {
sprintf(buf, "../keys/%d", key->debugfs.cnt);
- sdata->common_debugfs.default_key =
+ sdata->debugfs.default_key =
debugfs_create_symlink("default_key",
- sdata->debugfsdir, buf);
+ sdata->debugfs.dir, buf);
} else
ieee80211_debugfs_key_remove_default(sdata);
}
@@ -322,8 +304,8 @@ void ieee80211_debugfs_key_remove_default(struct ieee80211_sub_if_data *sdata)
if (!sdata)
return;
- debugfs_remove(sdata->common_debugfs.default_key);
- sdata->common_debugfs.default_key = NULL;
+ debugfs_remove(sdata->debugfs.default_key);
+ sdata->debugfs.default_key = NULL;
}
void ieee80211_debugfs_key_add_mgmt_default(struct ieee80211_sub_if_data *sdata)
@@ -331,7 +313,7 @@ void ieee80211_debugfs_key_add_mgmt_default(struct ieee80211_sub_if_data *sdata)
char buf[50];
struct ieee80211_key *key;
- if (!sdata->debugfsdir)
+ if (!sdata->debugfs.dir)
return;
/* this is running under the key lock */
@@ -339,9 +321,9 @@ void ieee80211_debugfs_key_add_mgmt_default(struct ieee80211_sub_if_data *sdata)
key = sdata->default_mgmt_key;
if (key) {
sprintf(buf, "../keys/%d", key->debugfs.cnt);
- sdata->common_debugfs.default_mgmt_key =
+ sdata->debugfs.default_mgmt_key =
debugfs_create_symlink("default_mgmt_key",
- sdata->debugfsdir, buf);
+ sdata->debugfs.dir, buf);
} else
ieee80211_debugfs_key_remove_mgmt_default(sdata);
}
@@ -351,8 +333,8 @@ void ieee80211_debugfs_key_remove_mgmt_default(struct ieee80211_sub_if_data *sda
if (!sdata)
return;
- debugfs_remove(sdata->common_debugfs.default_mgmt_key);
- sdata->common_debugfs.default_mgmt_key = NULL;
+ debugfs_remove(sdata->debugfs.default_mgmt_key);
+ sdata->debugfs.default_mgmt_key = NULL;
}
void ieee80211_debugfs_key_sta_del(struct ieee80211_key *key,
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index 61234e79022..472b2039906 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -149,12 +149,14 @@ IEEE80211_IF_FILE(path_refresh_time,
u.mesh.mshcfg.path_refresh_time, DEC);
IEEE80211_IF_FILE(min_discovery_timeout,
u.mesh.mshcfg.min_discovery_timeout, DEC);
+IEEE80211_IF_FILE(dot11MeshHWMPRootMode,
+ u.mesh.mshcfg.dot11MeshHWMPRootMode, DEC);
#endif
-#define DEBUGFS_ADD(name, type)\
- sdata->debugfs.type.name = debugfs_create_file(#name, 0400,\
- sdata->debugfsdir, sdata, &name##_ops);
+#define DEBUGFS_ADD(name, type) \
+ debugfs_create_file(#name, 0400, sdata->debugfs.dir, \
+ sdata, &name##_ops);
static void add_sta_files(struct ieee80211_sub_if_data *sdata)
{
@@ -199,30 +201,32 @@ static void add_monitor_files(struct ieee80211_sub_if_data *sdata)
}
#ifdef CONFIG_MAC80211_MESH
-#define MESHSTATS_ADD(name)\
- sdata->mesh_stats.name = debugfs_create_file(#name, 0400,\
- sdata->mesh_stats_dir, sdata, &name##_ops);
static void add_mesh_stats(struct ieee80211_sub_if_data *sdata)
{
- sdata->mesh_stats_dir = debugfs_create_dir("mesh_stats",
- sdata->debugfsdir);
+ struct dentry *dir = debugfs_create_dir("mesh_stats",
+ sdata->debugfs.dir);
+
+#define MESHSTATS_ADD(name)\
+ debugfs_create_file(#name, 0400, dir, sdata, &name##_ops);
+
MESHSTATS_ADD(fwded_mcast);
MESHSTATS_ADD(fwded_unicast);
MESHSTATS_ADD(fwded_frames);
MESHSTATS_ADD(dropped_frames_ttl);
MESHSTATS_ADD(dropped_frames_no_route);
MESHSTATS_ADD(estab_plinks);
+#undef MESHSTATS_ADD
}
-#define MESHPARAMS_ADD(name)\
- sdata->mesh_config.name = debugfs_create_file(#name, 0600,\
- sdata->mesh_config_dir, sdata, &name##_ops);
-
static void add_mesh_config(struct ieee80211_sub_if_data *sdata)
{
- sdata->mesh_config_dir = debugfs_create_dir("mesh_config",
- sdata->debugfsdir);
+ struct dentry *dir = debugfs_create_dir("mesh_config",
+ sdata->debugfs.dir);
+
+#define MESHPARAMS_ADD(name) \
+ debugfs_create_file(#name, 0600, dir, sdata, &name##_ops);
+
MESHPARAMS_ADD(dot11MeshMaxRetries);
MESHPARAMS_ADD(dot11MeshRetryTimeout);
MESHPARAMS_ADD(dot11MeshConfirmTimeout);
@@ -236,12 +240,14 @@ static void add_mesh_config(struct ieee80211_sub_if_data *sdata)
MESHPARAMS_ADD(dot11MeshHWMPmaxPREQretries);
MESHPARAMS_ADD(path_refresh_time);
MESHPARAMS_ADD(min_discovery_timeout);
+
+#undef MESHPARAMS_ADD
}
#endif
static void add_files(struct ieee80211_sub_if_data *sdata)
{
- if (!sdata->debugfsdir)
+ if (!sdata->debugfs.dir)
return;
switch (sdata->vif.type) {
@@ -274,134 +280,6 @@ static void add_files(struct ieee80211_sub_if_data *sdata)
}
}
-#define DEBUGFS_DEL(name, type) \
- do { \
- debugfs_remove(sdata->debugfs.type.name); \
- sdata->debugfs.type.name = NULL; \
- } while (0)
-
-static void del_sta_files(struct ieee80211_sub_if_data *sdata)
-{
- DEBUGFS_DEL(drop_unencrypted, sta);
- DEBUGFS_DEL(force_unicast_rateidx, sta);
- DEBUGFS_DEL(max_ratectrl_rateidx, sta);
-
- DEBUGFS_DEL(bssid, sta);
- DEBUGFS_DEL(aid, sta);
- DEBUGFS_DEL(capab, sta);
-}
-
-static void del_ap_files(struct ieee80211_sub_if_data *sdata)
-{
- DEBUGFS_DEL(drop_unencrypted, ap);
- DEBUGFS_DEL(force_unicast_rateidx, ap);
- DEBUGFS_DEL(max_ratectrl_rateidx, ap);
-
- DEBUGFS_DEL(num_sta_ps, ap);
- DEBUGFS_DEL(dtim_count, ap);
- DEBUGFS_DEL(num_buffered_multicast, ap);
-}
-
-static void del_wds_files(struct ieee80211_sub_if_data *sdata)
-{
- DEBUGFS_DEL(drop_unencrypted, wds);
- DEBUGFS_DEL(force_unicast_rateidx, wds);
- DEBUGFS_DEL(max_ratectrl_rateidx, wds);
-
- DEBUGFS_DEL(peer, wds);
-}
-
-static void del_vlan_files(struct ieee80211_sub_if_data *sdata)
-{
- DEBUGFS_DEL(drop_unencrypted, vlan);
- DEBUGFS_DEL(force_unicast_rateidx, vlan);
- DEBUGFS_DEL(max_ratectrl_rateidx, vlan);
-}
-
-static void del_monitor_files(struct ieee80211_sub_if_data *sdata)
-{
-}
-
-#ifdef CONFIG_MAC80211_MESH
-#define MESHSTATS_DEL(name) \
- do { \
- debugfs_remove(sdata->mesh_stats.name); \
- sdata->mesh_stats.name = NULL; \
- } while (0)
-
-static void del_mesh_stats(struct ieee80211_sub_if_data *sdata)
-{
- MESHSTATS_DEL(fwded_mcast);
- MESHSTATS_DEL(fwded_unicast);
- MESHSTATS_DEL(fwded_frames);
- MESHSTATS_DEL(dropped_frames_ttl);
- MESHSTATS_DEL(dropped_frames_no_route);
- MESHSTATS_DEL(estab_plinks);
- debugfs_remove(sdata->mesh_stats_dir);
- sdata->mesh_stats_dir = NULL;
-}
-
-#define MESHPARAMS_DEL(name) \
- do { \
- debugfs_remove(sdata->mesh_config.name); \
- sdata->mesh_config.name = NULL; \
- } while (0)
-
-static void del_mesh_config(struct ieee80211_sub_if_data *sdata)
-{
- MESHPARAMS_DEL(dot11MeshMaxRetries);
- MESHPARAMS_DEL(dot11MeshRetryTimeout);
- MESHPARAMS_DEL(dot11MeshConfirmTimeout);
- MESHPARAMS_DEL(dot11MeshHoldingTimeout);
- MESHPARAMS_DEL(dot11MeshTTL);
- MESHPARAMS_DEL(auto_open_plinks);
- MESHPARAMS_DEL(dot11MeshMaxPeerLinks);
- MESHPARAMS_DEL(dot11MeshHWMPactivePathTimeout);
- MESHPARAMS_DEL(dot11MeshHWMPpreqMinInterval);
- MESHPARAMS_DEL(dot11MeshHWMPnetDiameterTraversalTime);
- MESHPARAMS_DEL(dot11MeshHWMPmaxPREQretries);
- MESHPARAMS_DEL(path_refresh_time);
- MESHPARAMS_DEL(min_discovery_timeout);
- debugfs_remove(sdata->mesh_config_dir);
- sdata->mesh_config_dir = NULL;
-}
-#endif
-
-static void del_files(struct ieee80211_sub_if_data *sdata)
-{
- if (!sdata->debugfsdir)
- return;
-
- switch (sdata->vif.type) {
- case NL80211_IFTYPE_MESH_POINT:
-#ifdef CONFIG_MAC80211_MESH
- del_mesh_stats(sdata);
- del_mesh_config(sdata);
-#endif
- break;
- case NL80211_IFTYPE_STATION:
- del_sta_files(sdata);
- break;
- case NL80211_IFTYPE_ADHOC:
- /* XXX */
- break;
- case NL80211_IFTYPE_AP:
- del_ap_files(sdata);
- break;
- case NL80211_IFTYPE_WDS:
- del_wds_files(sdata);
- break;
- case NL80211_IFTYPE_MONITOR:
- del_monitor_files(sdata);
- break;
- case NL80211_IFTYPE_AP_VLAN:
- del_vlan_files(sdata);
- break;
- default:
- break;
- }
-}
-
static int notif_registered;
void ieee80211_debugfs_add_netdev(struct ieee80211_sub_if_data *sdata)
@@ -412,16 +290,18 @@ void ieee80211_debugfs_add_netdev(struct ieee80211_sub_if_data *sdata)
return;
sprintf(buf, "netdev:%s", sdata->dev->name);
- sdata->debugfsdir = debugfs_create_dir(buf,
+ sdata->debugfs.dir = debugfs_create_dir(buf,
sdata->local->hw.wiphy->debugfsdir);
add_files(sdata);
}
void ieee80211_debugfs_remove_netdev(struct ieee80211_sub_if_data *sdata)
{
- del_files(sdata);
- debugfs_remove(sdata->debugfsdir);
- sdata->debugfsdir = NULL;
+ if (!sdata->debugfs.dir)
+ return;
+
+ debugfs_remove_recursive(sdata->debugfs.dir);
+ sdata->debugfs.dir = NULL;
}
static int netdev_notify(struct notifier_block *nb,
@@ -444,7 +324,7 @@ static int netdev_notify(struct notifier_block *nb,
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- dir = sdata->debugfsdir;
+ dir = sdata->debugfs.dir;
if (!dir)
return 0;
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index 33a2e892115..3f41608c808 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -57,7 +57,6 @@ STA_FILE(tx_filtered, tx_filtered_count, LU);
STA_FILE(tx_retry_failed, tx_retry_failed, LU);
STA_FILE(tx_retry_count, tx_retry_count, LU);
STA_FILE(last_signal, last_signal, D);
-STA_FILE(last_qual, last_qual, D);
STA_FILE(last_noise, last_noise, D);
STA_FILE(wep_weak_iv_count, wep_weak_iv_count, LU);
@@ -67,10 +66,11 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
char buf[100];
struct sta_info *sta = file->private_data;
u32 staflags = get_sta_flags(sta);
- int res = scnprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s%s",
+ int res = scnprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s%s%s",
staflags & WLAN_STA_AUTH ? "AUTH\n" : "",
staflags & WLAN_STA_ASSOC ? "ASSOC\n" : "",
- staflags & WLAN_STA_PS ? "PS\n" : "",
+ staflags & WLAN_STA_PS_STA ? "PS (sta)\n" : "",
+ staflags & WLAN_STA_PS_DRIVER ? "PS (driver)\n" : "",
staflags & WLAN_STA_AUTHORIZED ? "AUTHORIZED\n" : "",
staflags & WLAN_STA_SHORT_PREAMBLE ? "SHORT PREAMBLE\n" : "",
staflags & WLAN_STA_WME ? "WME\n" : "",
@@ -157,14 +157,38 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
}
STA_OPS(agg_status);
+static ssize_t sta_ht_capa_read(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ char buf[200], *p = buf;
+ int i;
+ struct sta_info *sta = file->private_data;
+ struct ieee80211_sta_ht_cap *htc = &sta->sta.ht_cap;
+
+ p += scnprintf(p, sizeof(buf) + buf - p, "ht %ssupported\n",
+ htc->ht_supported ? "" : "not ");
+ if (htc->ht_supported) {
+ p += scnprintf(p, sizeof(buf)+buf-p, "cap: %#.2x\n", htc->cap);
+ p += scnprintf(p, sizeof(buf)+buf-p, "ampdu factor/density: %d/%d\n",
+ htc->ampdu_factor, htc->ampdu_density);
+ p += scnprintf(p, sizeof(buf)+buf-p, "MCS mask:");
+ for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
+ p += scnprintf(p, sizeof(buf)+buf-p, " %.2x",
+ htc->mcs.rx_mask[i]);
+ p += scnprintf(p, sizeof(buf)+buf-p, "\nMCS rx highest: %d\n",
+ le16_to_cpu(htc->mcs.rx_highest));
+ p += scnprintf(p, sizeof(buf)+buf-p, "MCS tx params: %x\n",
+ htc->mcs.tx_params);
+ }
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
+}
+STA_OPS(ht_capa);
+
#define DEBUGFS_ADD(name) \
- sta->debugfs.name = debugfs_create_file(#name, 0400, \
+ debugfs_create_file(#name, 0400, \
sta->debugfs.dir, sta, &sta_ ##name## _ops);
-#define DEBUGFS_DEL(name) \
- debugfs_remove(sta->debugfs.name);\
- sta->debugfs.name = NULL;
-
void ieee80211_sta_debugfs_add(struct sta_info *sta)
{
@@ -209,36 +233,13 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
DEBUGFS_ADD(tx_retry_failed);
DEBUGFS_ADD(tx_retry_count);
DEBUGFS_ADD(last_signal);
- DEBUGFS_ADD(last_qual);
DEBUGFS_ADD(last_noise);
DEBUGFS_ADD(wep_weak_iv_count);
+ DEBUGFS_ADD(ht_capa);
}
void ieee80211_sta_debugfs_remove(struct sta_info *sta)
{
- DEBUGFS_DEL(flags);
- DEBUGFS_DEL(num_ps_buf_frames);
- DEBUGFS_DEL(inactive_ms);
- DEBUGFS_DEL(last_seq_ctrl);
- DEBUGFS_DEL(agg_status);
- DEBUGFS_DEL(aid);
- DEBUGFS_DEL(dev);
- DEBUGFS_DEL(rx_packets);
- DEBUGFS_DEL(tx_packets);
- DEBUGFS_DEL(rx_bytes);
- DEBUGFS_DEL(tx_bytes);
- DEBUGFS_DEL(rx_duplicates);
- DEBUGFS_DEL(rx_fragments);
- DEBUGFS_DEL(rx_dropped);
- DEBUGFS_DEL(tx_fragments);
- DEBUGFS_DEL(tx_filtered);
- DEBUGFS_DEL(tx_retry_failed);
- DEBUGFS_DEL(tx_retry_count);
- DEBUGFS_DEL(last_signal);
- DEBUGFS_DEL(last_qual);
- DEBUGFS_DEL(last_noise);
- DEBUGFS_DEL(wep_weak_iv_count);
-
- debugfs_remove(sta->debugfs.dir);
+ debugfs_remove_recursive(sta->debugfs.dir);
sta->debugfs.dir = NULL;
}
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 020a94a3110..921dd9c9ff6 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -239,15 +239,16 @@ static inline int drv_tx_last_beacon(struct ieee80211_local *local)
}
static inline int drv_ampdu_action(struct ieee80211_local *local,
+ struct ieee80211_vif *vif,
enum ieee80211_ampdu_mlme_action action,
struct ieee80211_sta *sta, u16 tid,
u16 *ssn)
{
int ret = -EOPNOTSUPP;
if (local->ops->ampdu_action)
- ret = local->ops->ampdu_action(&local->hw, action,
+ ret = local->ops->ampdu_action(&local->hw, vif, action,
sta, tid, ssn);
- trace_drv_ampdu_action(local, action, sta, tid, ssn, ret);
+ trace_drv_ampdu_action(local, vif, action, sta, tid, ssn, ret);
return ret;
}
diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h
index 37b9051afcf..ee94ea0c67e 100644
--- a/net/mac80211/driver-trace.h
+++ b/net/mac80211/driver-trace.h
@@ -131,17 +131,35 @@ TRACE_EVENT(drv_config,
LOCAL_ENTRY
__field(u32, changed)
__field(int, ret)
+ __field(u32, flags)
+ __field(int, power_level)
+ __field(int, dynamic_ps_timeout)
+ __field(int, max_sleep_period)
+ __field(u16, listen_interval)
+ __field(u8, long_frame_max_tx_count)
+ __field(u8, short_frame_max_tx_count)
+ __field(int, center_freq)
+ __field(int, channel_type)
),
TP_fast_assign(
LOCAL_ASSIGN;
__entry->changed = changed;
__entry->ret = ret;
+ __entry->flags = local->hw.conf.flags;
+ __entry->power_level = local->hw.conf.power_level;
+ __entry->dynamic_ps_timeout = local->hw.conf.dynamic_ps_timeout;
+ __entry->max_sleep_period = local->hw.conf.max_sleep_period;
+ __entry->listen_interval = local->hw.conf.listen_interval;
+ __entry->long_frame_max_tx_count = local->hw.conf.long_frame_max_tx_count;
+ __entry->short_frame_max_tx_count = local->hw.conf.short_frame_max_tx_count;
+ __entry->center_freq = local->hw.conf.channel->center_freq;
+ __entry->channel_type = local->hw.conf.channel_type;
),
TP_printk(
- LOCAL_PR_FMT " ch:%#x ret:%d",
- LOCAL_PR_ARG, __entry->changed, __entry->ret
+ LOCAL_PR_FMT " ch:%#x freq:%d ret:%d",
+ LOCAL_PR_ARG, __entry->changed, __entry->center_freq, __entry->ret
)
);
@@ -167,6 +185,8 @@ TRACE_EVENT(drv_bss_info_changed,
__field(u64, timestamp)
__field(u32, basic_rates)
__field(u32, changed)
+ __field(bool, enable_beacon)
+ __field(u16, ht_operation_mode)
),
TP_fast_assign(
@@ -183,6 +203,8 @@ TRACE_EVENT(drv_bss_info_changed,
__entry->assoc_cap = info->assoc_capability;
__entry->timestamp = info->timestamp;
__entry->basic_rates = info->basic_rates;
+ __entry->enable_beacon = info->enable_beacon;
+ __entry->ht_operation_mode = info->ht_operation_mode;
),
TP_printk(
@@ -634,11 +656,12 @@ TRACE_EVENT(drv_tx_last_beacon,
TRACE_EVENT(drv_ampdu_action,
TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_vif *vif,
enum ieee80211_ampdu_mlme_action action,
struct ieee80211_sta *sta, u16 tid,
u16 *ssn, int ret),
- TP_ARGS(local, action, sta, tid, ssn, ret),
+ TP_ARGS(local, vif, action, sta, tid, ssn, ret),
TP_STRUCT__entry(
LOCAL_ENTRY
@@ -647,10 +670,12 @@ TRACE_EVENT(drv_ampdu_action,
__field(u16, tid)
__field(u16, ssn)
__field(int, ret)
+ VIF_ENTRY
),
TP_fast_assign(
LOCAL_ASSIGN;
+ VIF_ASSIGN;
STA_ASSIGN;
__entry->ret = ret;
__entry->action = action;
@@ -659,8 +684,8 @@ TRACE_EVENT(drv_ampdu_action,
),
TP_printk(
- LOCAL_PR_FMT STA_PR_FMT " action:%d tid:%d ret:%d",
- LOCAL_PR_ARG, STA_PR_ARG, __entry->action, __entry->tid, __entry->ret
+ LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " action:%d tid:%d ret:%d",
+ LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->action, __entry->tid, __entry->ret
)
);
#endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 48ef1a282b9..3787455fb69 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -134,14 +134,13 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
mgmt->u.action.u.delba.params = cpu_to_le16(params);
mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code);
- ieee80211_tx_skb(sdata, skb, 1);
+ ieee80211_tx_skb(sdata, skb);
}
void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta,
struct ieee80211_mgmt *mgmt, size_t len)
{
- struct ieee80211_local *local = sdata->local;
u16 tid, params;
u16 initiator;
@@ -161,10 +160,9 @@ void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
WLAN_BACK_INITIATOR, 0);
else { /* WLAN_BACK_RECIPIENT */
spin_lock_bh(&sta->lock);
- sta->ampdu_mlme.tid_state_tx[tid] =
- HT_AGG_STATE_OPERATIONAL;
+ if (sta->ampdu_mlme.tid_state_tx[tid] & HT_ADDBA_REQUESTED_MSK)
+ ___ieee80211_stop_tx_ba_session(sta, tid,
+ WLAN_BACK_RECIPIENT);
spin_unlock_bh(&sta->lock);
- ieee80211_stop_tx_ba_session(&local->hw, sta->sta.addr, tid,
- WLAN_BACK_RECIPIENT);
}
}
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index f1362f32c17..10d13856f86 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -455,6 +455,10 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
ieee80211_sta_expire(sdata, IEEE80211_IBSS_INACTIVITY_LIMIT);
+ if (time_before(jiffies, ifibss->last_scan_completed +
+ IEEE80211_IBSS_MERGE_INTERVAL))
+ return;
+
if (ieee80211_sta_active_ibss(sdata))
return;
@@ -655,7 +659,8 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
printk(KERN_DEBUG "%s: Sending ProbeResp to %pM\n",
sdata->dev->name, resp->da);
#endif /* CONFIG_MAC80211_IBSS_DEBUG */
- ieee80211_tx_skb(sdata, skb, 0);
+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+ ieee80211_tx_skb(sdata, skb);
}
static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 588005c84a6..419f186cfcf 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -23,6 +23,7 @@
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/etherdevice.h>
+#include <net/ieee80211_radiotap.h>
#include <net/cfg80211.h>
#include <net/mac80211.h>
#include "key.h"
@@ -162,21 +163,17 @@ typedef unsigned __bitwise__ ieee80211_rx_result;
/* frame is destined to interface currently processed (incl. multicast frames) */
#define IEEE80211_RX_RA_MATCH BIT(1)
#define IEEE80211_RX_AMSDU BIT(2)
-#define IEEE80211_RX_CMNTR_REPORTED BIT(3)
-#define IEEE80211_RX_FRAGMENTED BIT(4)
+#define IEEE80211_RX_FRAGMENTED BIT(3)
+/* only add flags here that do not change with subframes of an aMPDU */
struct ieee80211_rx_data {
struct sk_buff *skb;
- struct net_device *dev;
struct ieee80211_local *local;
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
struct ieee80211_key *key;
- struct ieee80211_rx_status *status;
- struct ieee80211_rate *rate;
unsigned int flags;
- int sent_ps_buffered;
int queue;
u32 tkip_iv32;
u16 tkip_iv16;
@@ -209,6 +206,9 @@ struct ieee80211_if_wds {
struct ieee80211_if_vlan {
struct list_head list;
+
+ /* used for all tx if the VLAN is configured to 4-addr mode */
+ struct sta_info *sta;
};
struct mesh_stats {
@@ -312,6 +312,8 @@ struct ieee80211_if_managed {
} mfp; /* management frame protection */
int wmm_last_param_set;
+
+ u8 use_4addr;
};
enum ieee80211_ibss_request {
@@ -353,6 +355,7 @@ struct ieee80211_if_mesh {
struct work_struct work;
struct timer_list housekeeping_timer;
struct timer_list mesh_path_timer;
+ struct timer_list mesh_path_root_timer;
struct sk_buff_head skb_queue;
unsigned long timers_running;
@@ -362,23 +365,23 @@ struct ieee80211_if_mesh {
u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN];
size_t mesh_id_len;
/* Active Path Selection Protocol Identifier */
- u8 mesh_pp_id[4];
+ u8 mesh_pp_id;
/* Active Path Selection Metric Identifier */
- u8 mesh_pm_id[4];
+ u8 mesh_pm_id;
/* Congestion Control Mode Identifier */
- u8 mesh_cc_id[4];
+ u8 mesh_cc_id;
/* Synchronization Protocol Identifier */
- u8 mesh_sp_id[4];
+ u8 mesh_sp_id;
/* Authentication Protocol Identifier */
- u8 mesh_auth_id[4];
- /* Local mesh Destination Sequence Number */
- u32 dsn;
+ u8 mesh_auth_id;
+ /* Local mesh Sequence Number */
+ u32 sn;
/* Last used PREQ ID */
u32 preq_id;
atomic_t mpaths;
- /* Timestamp of last DSN update */
- unsigned long last_dsn_update;
- /* Timestamp of last DSN sent */
+ /* Timestamp of last SN update */
+ unsigned long last_sn_update;
+ /* Timestamp of last SN sent */
unsigned long last_preq;
struct mesh_rmc *rmc;
spinlock_t mesh_preq_queue_lock;
@@ -471,74 +474,11 @@ struct ieee80211_sub_if_data {
} u;
#ifdef CONFIG_MAC80211_DEBUGFS
- struct dentry *debugfsdir;
- union {
- struct {
- struct dentry *drop_unencrypted;
- struct dentry *bssid;
- struct dentry *aid;
- struct dentry *capab;
- struct dentry *force_unicast_rateidx;
- struct dentry *max_ratectrl_rateidx;
- } sta;
- struct {
- struct dentry *drop_unencrypted;
- struct dentry *num_sta_ps;
- struct dentry *dtim_count;
- struct dentry *force_unicast_rateidx;
- struct dentry *max_ratectrl_rateidx;
- struct dentry *num_buffered_multicast;
- } ap;
- struct {
- struct dentry *drop_unencrypted;
- struct dentry *peer;
- struct dentry *force_unicast_rateidx;
- struct dentry *max_ratectrl_rateidx;
- } wds;
- struct {
- struct dentry *drop_unencrypted;
- struct dentry *force_unicast_rateidx;
- struct dentry *max_ratectrl_rateidx;
- } vlan;
- struct {
- struct dentry *mode;
- } monitor;
- } debugfs;
struct {
+ struct dentry *dir;
struct dentry *default_key;
struct dentry *default_mgmt_key;
- } common_debugfs;
-
-#ifdef CONFIG_MAC80211_MESH
- struct dentry *mesh_stats_dir;
- struct {
- struct dentry *fwded_mcast;
- struct dentry *fwded_unicast;
- struct dentry *fwded_frames;
- struct dentry *dropped_frames_ttl;
- struct dentry *dropped_frames_no_route;
- struct dentry *estab_plinks;
- struct timer_list mesh_path_timer;
- } mesh_stats;
-
- struct dentry *mesh_config_dir;
- struct {
- struct dentry *dot11MeshRetryTimeout;
- struct dentry *dot11MeshConfirmTimeout;
- struct dentry *dot11MeshHoldingTimeout;
- struct dentry *dot11MeshMaxRetries;
- struct dentry *dot11MeshTTL;
- struct dentry *auto_open_plinks;
- struct dentry *dot11MeshMaxPeerLinks;
- struct dentry *dot11MeshHWMPactivePathTimeout;
- struct dentry *dot11MeshHWMPpreqMinInterval;
- struct dentry *dot11MeshHWMPnetDiameterTraversalTime;
- struct dentry *dot11MeshHWMPmaxPREQretries;
- struct dentry *path_refresh_time;
- struct dentry *min_discovery_timeout;
- } mesh_config;
-#endif
-
+ } debugfs;
#endif
/* must be last, dynamically sized area in this! */
struct ieee80211_vif vif;
@@ -639,7 +579,6 @@ struct ieee80211_local {
/* number of interfaces with corresponding FIF_ flags */
int fif_fcsfail, fif_plcpfail, fif_control, fif_other_bss, fif_pspoll;
unsigned int filter_flags; /* FIF_* */
- struct iw_statistics wstats;
/* protects the aggregated multicast list and filter calls */
spinlock_t filter_lock;
@@ -662,6 +601,14 @@ struct ieee80211_local {
bool suspended;
/*
+ * Resuming is true while suspended, but when we're reprogramming the
+ * hardware -- at that time it's allowed to use ieee80211_queue_work()
+ * again even though some other parts of the stack are still suspended
+ * and we still drop received frames to avoid waking the stack.
+ */
+ bool resuming;
+
+ /*
* quiescing is true during the suspend process _only_ to
* ease timer cancelling etc.
*/
@@ -730,10 +677,9 @@ struct ieee80211_local {
unsigned long scanning;
struct cfg80211_ssid scan_ssid;
struct cfg80211_scan_request *int_scan_req;
- struct cfg80211_scan_request *scan_req;
+ struct cfg80211_scan_request *scan_req, *hw_scan_req;
struct ieee80211_channel *scan_channel;
- const u8 *orig_ies;
- int orig_ies_len;
+ enum ieee80211_band hw_scan_band;
int scan_channel_idx;
int scan_ies_len;
@@ -818,53 +764,6 @@ struct ieee80211_local {
#ifdef CONFIG_MAC80211_DEBUGFS
struct local_debugfsdentries {
struct dentry *rcdir;
- struct dentry *rcname;
- struct dentry *frequency;
- struct dentry *total_ps_buffered;
- struct dentry *wep_iv;
- struct dentry *tsf;
- struct dentry *queues;
- struct dentry *reset;
- struct dentry *noack;
- struct dentry *statistics;
- struct local_debugfsdentries_statsdentries {
- struct dentry *transmitted_fragment_count;
- struct dentry *multicast_transmitted_frame_count;
- struct dentry *failed_count;
- struct dentry *retry_count;
- struct dentry *multiple_retry_count;
- struct dentry *frame_duplicate_count;
- struct dentry *received_fragment_count;
- struct dentry *multicast_received_frame_count;
- struct dentry *transmitted_frame_count;
- struct dentry *wep_undecryptable_count;
- struct dentry *num_scans;
-#ifdef CONFIG_MAC80211_DEBUG_COUNTERS
- struct dentry *tx_handlers_drop;
- struct dentry *tx_handlers_queued;
- struct dentry *tx_handlers_drop_unencrypted;
- struct dentry *tx_handlers_drop_fragment;
- struct dentry *tx_handlers_drop_wep;
- struct dentry *tx_handlers_drop_not_assoc;
- struct dentry *tx_handlers_drop_unauth_port;
- struct dentry *rx_handlers_drop;
- struct dentry *rx_handlers_queued;
- struct dentry *rx_handlers_drop_nullfunc;
- struct dentry *rx_handlers_drop_defrag;
- struct dentry *rx_handlers_drop_short;
- struct dentry *rx_handlers_drop_passive_scan;
- struct dentry *tx_expand_skb_head;
- struct dentry *tx_expand_skb_head_cloned;
- struct dentry *rx_expand_skb_head;
- struct dentry *rx_expand_skb_head2;
- struct dentry *rx_handlers_fragments;
- struct dentry *tx_status_drop;
-#endif
- struct dentry *dot11ACKFailureCount;
- struct dentry *dot11RTSFailureCount;
- struct dentry *dot11FCSErrorCount;
- struct dentry *dot11RTSSuccessCount;
- } stats;
struct dentry *stations;
struct dentry *keys;
} debugfs;
@@ -877,8 +776,9 @@ IEEE80211_DEV_TO_SUB_IF(struct net_device *dev)
return netdev_priv(dev);
}
-/* this struct represents 802.11n's RA/TID combination */
+/* this struct represents 802.11n's RA/TID combination along with our vif */
struct ieee80211_ra_tid {
+ struct ieee80211_vif *vif;
u8 ra[ETH_ALEN];
u16 tid;
};
@@ -905,12 +805,13 @@ struct ieee802_11_elems {
u8 *wmm_param;
struct ieee80211_ht_cap *ht_cap_elem;
struct ieee80211_ht_info *ht_info_elem;
- u8 *mesh_config;
+ struct ieee80211_meshconf_ie *mesh_config;
u8 *mesh_id;
u8 *peer_link;
u8 *preq;
u8 *prep;
u8 *perr;
+ struct ieee80211_rann_ie *rann;
u8 *ch_switch_elem;
u8 *country_elem;
u8 *pwr_constr_elem;
@@ -932,7 +833,6 @@ struct ieee802_11_elems {
u8 ext_supp_rates_len;
u8 wmm_info_len;
u8 wmm_param_len;
- u8 mesh_config_len;
u8 mesh_id_len;
u8 peer_link_len;
u8 preq_len;
@@ -1055,6 +955,18 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
struct net_device *dev);
+/*
+ * radiotap header for status frames
+ */
+struct ieee80211_tx_status_rtap_hdr {
+ struct ieee80211_radiotap_header hdr;
+ u8 rate;
+ u8 padding_for_rate;
+ __le16 tx_flags;
+ u8 data_retries;
+} __attribute__ ((packed));
+
+
/* HT */
void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband,
struct ieee80211_ht_cap *ht_cap_ie,
@@ -1083,6 +995,8 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
int __ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
enum ieee80211_back_parties initiator);
+int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
+ enum ieee80211_back_parties initiator);
/* Spectrum management */
void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
@@ -1122,8 +1036,7 @@ void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int ke
struct ieee80211_hdr *hdr, const u8 *tsc,
gfp_t gfp);
void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata);
-void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
- int encrypt);
+void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb);
void ieee802_11_parse_elems(u8 *start, size_t len,
struct ieee802_11_elems *elems);
u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
@@ -1160,7 +1073,8 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
u8 *extra, size_t extra_len, const u8 *bssid,
const u8 *key, u8 key_len, u8 key_idx);
int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
- const u8 *ie, size_t ie_len);
+ const u8 *ie, size_t ie_len,
+ enum ieee80211_band band);
void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len);
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index b8295cbd7e8..80c16f6e2af 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -184,10 +184,12 @@ static int ieee80211_open(struct net_device *dev)
* No need to check netif_running since we do not allow
* it to start up with this invalid address.
*/
- if (compare_ether_addr(null_addr, ndev->dev_addr) == 0)
+ if (compare_ether_addr(null_addr, ndev->dev_addr) == 0) {
memcpy(ndev->dev_addr,
local->hw.wiphy->perm_addr,
ETH_ALEN);
+ memcpy(ndev->perm_addr, ndev->dev_addr, ETH_ALEN);
+ }
}
/*
@@ -212,8 +214,8 @@ static int ieee80211_open(struct net_device *dev)
/* must be before the call to ieee80211_configure_filter */
local->monitors++;
if (local->monitors == 1) {
- local->hw.conf.flags |= IEEE80211_CONF_RADIOTAP;
- hw_reconf_flags |= IEEE80211_CONF_CHANGE_RADIOTAP;
+ local->hw.conf.flags |= IEEE80211_CONF_MONITOR;
+ hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR;
}
if (sdata->u.mntr_flags & MONITOR_FLAG_FCSFAIL)
@@ -312,7 +314,7 @@ static int ieee80211_open(struct net_device *dev)
if (sdata->vif.type == NL80211_IFTYPE_STATION)
ieee80211_queue_work(&local->hw, &sdata->u.mgd.work);
- netif_tx_start_all_queues(dev);
+ netif_start_queue(dev);
return 0;
err_del_interface:
@@ -341,7 +343,7 @@ static int ieee80211_stop(struct net_device *dev)
/*
* Stop TX on this interface first.
*/
- netif_tx_stop_all_queues(dev);
+ netif_stop_queue(dev);
/*
* Now delete all active aggregation sessions.
@@ -433,8 +435,8 @@ static int ieee80211_stop(struct net_device *dev)
local->monitors--;
if (local->monitors == 0) {
- local->hw.conf.flags &= ~IEEE80211_CONF_RADIOTAP;
- hw_reconf_flags |= IEEE80211_CONF_CHANGE_RADIOTAP;
+ local->hw.conf.flags &= ~IEEE80211_CONF_MONITOR;
+ hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR;
}
if (sdata->u.mntr_flags & MONITOR_FLAG_FCSFAIL)
@@ -750,14 +752,12 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
ieee80211_mandatory_rates(sdata->local,
sdata->local->hw.conf.channel->band);
sdata->drop_unencrypted = 0;
+ if (type == NL80211_IFTYPE_STATION)
+ sdata->u.mgd.use_4addr = false;
return 0;
}
-static struct device_type wiphy_type = {
- .name = "wlan",
-};
-
int ieee80211_if_add(struct ieee80211_local *local, const char *name,
struct net_device **new_dev, enum nl80211_iftype type,
struct vif_params *params)
@@ -788,8 +788,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
goto fail;
memcpy(ndev->dev_addr, local->hw.wiphy->perm_addr, ETH_ALEN);
+ memcpy(ndev->perm_addr, ndev->dev_addr, ETH_ALEN);
SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy));
- SET_NETDEV_DEVTYPE(ndev, &wiphy_type);
/* don't use IEEE80211_DEV_TO_SUB_IF because it checks too much */
sdata = netdev_priv(ndev);
@@ -811,6 +811,12 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
/* setup type-dependent data */
ieee80211_setup_sdata(sdata, type);
+ if (params) {
+ ndev->ieee80211_ptr->use_4addr = params->use_4addr;
+ if (type == NL80211_IFTYPE_STATION)
+ sdata->u.mgd.use_4addr = params->use_4addr;
+ }
+
ret = register_netdevice(ndev);
if (ret)
goto fail;
@@ -854,22 +860,18 @@ void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata)
void ieee80211_remove_interfaces(struct ieee80211_local *local)
{
struct ieee80211_sub_if_data *sdata, *tmp;
+ LIST_HEAD(unreg_list);
ASSERT_RTNL();
+ mutex_lock(&local->iflist_mtx);
list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) {
- /*
- * we cannot hold the iflist_mtx across unregister_netdevice,
- * but we only need to hold it for list modifications to lock
- * out readers since we're under the RTNL here as all other
- * writers.
- */
- mutex_lock(&local->iflist_mtx);
list_del(&sdata->list);
- mutex_unlock(&local->iflist_mtx);
- unregister_netdevice(sdata->dev);
+ unregister_netdevice_queue(sdata->dev, &unreg_list);
}
+ mutex_unlock(&local->iflist_mtx);
+ unregister_netdevice_many(&unreg_list);
}
static u32 ieee80211_idle_off(struct ieee80211_local *local,
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index 9572e00f532..a49f93b79e9 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -118,18 +118,6 @@ struct ieee80211_key {
struct {
struct dentry *stalink;
struct dentry *dir;
- struct dentry *keylen;
- struct dentry *flags;
- struct dentry *keyidx;
- struct dentry *hw_key_idx;
- struct dentry *tx_rx_count;
- struct dentry *algorithm;
- struct dentry *tx_spec;
- struct dentry *rx_spec;
- struct dentry *replays;
- struct dentry *icverrors;
- struct dentry *key;
- struct dentry *ifindex;
int cnt;
} debugfs;
#endif
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 797f53942e5..8116d1a96a4 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -9,7 +9,6 @@
*/
#include <net/mac80211.h>
-#include <net/ieee80211_radiotap.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/netdevice.h>
@@ -30,26 +29,11 @@
#include "rate.h"
#include "mesh.h"
#include "wep.h"
-#include "wme.h"
-#include "aes_ccm.h"
#include "led.h"
#include "cfg.h"
#include "debugfs.h"
#include "debugfs_netdev.h"
-/*
- * For seeing transmitted packets on monitor interfaces
- * we have a radiotap header too.
- */
-struct ieee80211_tx_status_rtap_hdr {
- struct ieee80211_radiotap_header hdr;
- u8 rate;
- u8 padding_for_rate;
- __le16 tx_flags;
- u8 data_retries;
-} __attribute__ ((packed));
-
-
void ieee80211_configure_filter(struct ieee80211_local *local)
{
u64 mc;
@@ -253,28 +237,6 @@ u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata)
BSS_CHANGED_ERP_SLOT;
}
-void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw,
- struct sk_buff *skb)
-{
- struct ieee80211_local *local = hw_to_local(hw);
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- int tmp;
-
- skb->pkt_type = IEEE80211_TX_STATUS_MSG;
- skb_queue_tail(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS ?
- &local->skb_queue : &local->skb_queue_unreliable, skb);
- tmp = skb_queue_len(&local->skb_queue) +
- skb_queue_len(&local->skb_queue_unreliable);
- while (tmp > IEEE80211_IRQSAFE_QUEUE_LIMIT &&
- (skb = skb_dequeue(&local->skb_queue_unreliable))) {
- dev_kfree_skb_irq(skb);
- tmp--;
- I802_DEBUG_INC(local->tx_status_drop);
- }
- tasklet_schedule(&local->tasklet);
-}
-EXPORT_SYMBOL(ieee80211_tx_status_irqsafe);
-
static void ieee80211_tasklet_handler(unsigned long data)
{
struct ieee80211_local *local = (struct ieee80211_local *) data;
@@ -296,14 +258,14 @@ static void ieee80211_tasklet_handler(unsigned long data)
break;
case IEEE80211_DELBA_MSG:
ra_tid = (struct ieee80211_ra_tid *) &skb->cb;
- ieee80211_stop_tx_ba_cb(local_to_hw(local),
- ra_tid->ra, ra_tid->tid);
+ ieee80211_stop_tx_ba_cb(ra_tid->vif, ra_tid->ra,
+ ra_tid->tid);
dev_kfree_skb(skb);
break;
case IEEE80211_ADDBA_MSG:
ra_tid = (struct ieee80211_ra_tid *) &skb->cb;
- ieee80211_start_tx_ba_cb(local_to_hw(local),
- ra_tid->ra, ra_tid->tid);
+ ieee80211_start_tx_ba_cb(ra_tid->vif, ra_tid->ra,
+ ra_tid->tid);
dev_kfree_skb(skb);
break ;
default:
@@ -315,299 +277,6 @@ static void ieee80211_tasklet_handler(unsigned long data)
}
}
-static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
- struct sta_info *sta,
- struct sk_buff *skb)
-{
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-
- /*
- * XXX: This is temporary!
- *
- * The problem here is that when we get here, the driver will
- * quite likely have pretty much overwritten info->control by
- * using info->driver_data or info->rate_driver_data. Thus,
- * when passing out the frame to the driver again, we would be
- * passing completely bogus data since the driver would then
- * expect a properly filled info->control. In mac80211 itself
- * the same problem occurs, since we need info->control.vif
- * internally.
- *
- * To fix this, we should send the frame through TX processing
- * again. However, it's not that simple, since the frame will
- * have been software-encrypted (if applicable) already, and
- * encrypting it again doesn't do much good. So to properly do
- * that, we not only have to skip the actual 'raw' encryption
- * (key selection etc. still has to be done!) but also the
- * sequence number assignment since that impacts the crypto
- * encapsulation, of course.
- *
- * Hence, for now, fix the bug by just dropping the frame.
- */
- goto drop;
-
- sta->tx_filtered_count++;
-
- /*
- * Clear the TX filter mask for this STA when sending the next
- * packet. If the STA went to power save mode, this will happen
- * when it wakes up for the next time.
- */
- set_sta_flags(sta, WLAN_STA_CLEAR_PS_FILT);
-
- /*
- * This code races in the following way:
- *
- * (1) STA sends frame indicating it will go to sleep and does so
- * (2) hardware/firmware adds STA to filter list, passes frame up
- * (3) hardware/firmware processes TX fifo and suppresses a frame
- * (4) we get TX status before having processed the frame and
- * knowing that the STA has gone to sleep.
- *
- * This is actually quite unlikely even when both those events are
- * processed from interrupts coming in quickly after one another or
- * even at the same time because we queue both TX status events and
- * RX frames to be processed by a tasklet and process them in the
- * same order that they were received or TX status last. Hence, there
- * is no race as long as the frame RX is processed before the next TX
- * status, which drivers can ensure, see below.
- *
- * Note that this can only happen if the hardware or firmware can
- * actually add STAs to the filter list, if this is done by the
- * driver in response to set_tim() (which will only reduce the race
- * this whole filtering tries to solve, not completely solve it)
- * this situation cannot happen.
- *
- * To completely solve this race drivers need to make sure that they
- * (a) don't mix the irq-safe/not irq-safe TX status/RX processing
- * functions and
- * (b) always process RX events before TX status events if ordering
- * can be unknown, for example with different interrupt status
- * bits.
- */
- if (test_sta_flags(sta, WLAN_STA_PS) &&
- skb_queue_len(&sta->tx_filtered) < STA_MAX_TX_BUFFER) {
- skb_queue_tail(&sta->tx_filtered, skb);
- return;
- }
-
- if (!test_sta_flags(sta, WLAN_STA_PS) &&
- !(info->flags & IEEE80211_TX_INTFL_RETRIED)) {
- /* Software retry the packet once */
- info->flags |= IEEE80211_TX_INTFL_RETRIED;
- ieee80211_add_pending_skb(local, skb);
- return;
- }
-
- drop:
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: dropped TX filtered frame, "
- "queue_len=%d PS=%d @%lu\n",
- wiphy_name(local->hw.wiphy),
- skb_queue_len(&sta->tx_filtered),
- !!test_sta_flags(sta, WLAN_STA_PS), jiffies);
-#endif
- dev_kfree_skb(skb);
-}
-
-void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
-{
- struct sk_buff *skb2;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- struct ieee80211_local *local = hw_to_local(hw);
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- u16 frag, type;
- __le16 fc;
- struct ieee80211_supported_band *sband;
- struct ieee80211_tx_status_rtap_hdr *rthdr;
- struct ieee80211_sub_if_data *sdata;
- struct net_device *prev_dev = NULL;
- struct sta_info *sta;
- int retry_count = -1, i;
-
- for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
- /* the HW cannot have attempted that rate */
- if (i >= hw->max_rates) {
- info->status.rates[i].idx = -1;
- info->status.rates[i].count = 0;
- }
-
- retry_count += info->status.rates[i].count;
- }
- if (retry_count < 0)
- retry_count = 0;
-
- rcu_read_lock();
-
- sband = local->hw.wiphy->bands[info->band];
-
- sta = sta_info_get(local, hdr->addr1);
-
- if (sta) {
- if (!(info->flags & IEEE80211_TX_STAT_ACK) &&
- test_sta_flags(sta, WLAN_STA_PS)) {
- /*
- * The STA is in power save mode, so assume
- * that this TX packet failed because of that.
- */
- ieee80211_handle_filtered_frame(local, sta, skb);
- rcu_read_unlock();
- return;
- }
-
- fc = hdr->frame_control;
-
- if ((info->flags & IEEE80211_TX_STAT_AMPDU_NO_BACK) &&
- (ieee80211_is_data_qos(fc))) {
- u16 tid, ssn;
- u8 *qc;
-
- qc = ieee80211_get_qos_ctl(hdr);
- tid = qc[0] & 0xf;
- ssn = ((le16_to_cpu(hdr->seq_ctrl) + 0x10)
- & IEEE80211_SCTL_SEQ);
- ieee80211_send_bar(sta->sdata, hdr->addr1,
- tid, ssn);
- }
-
- if (info->flags & IEEE80211_TX_STAT_TX_FILTERED) {
- ieee80211_handle_filtered_frame(local, sta, skb);
- rcu_read_unlock();
- return;
- } else {
- if (!(info->flags & IEEE80211_TX_STAT_ACK))
- sta->tx_retry_failed++;
- sta->tx_retry_count += retry_count;
- }
-
- rate_control_tx_status(local, sband, sta, skb);
- if (ieee80211_vif_is_mesh(&sta->sdata->vif))
- ieee80211s_update_metric(local, sta, skb);
- }
-
- rcu_read_unlock();
-
- ieee80211_led_tx(local, 0);
-
- /* SNMP counters
- * Fragments are passed to low-level drivers as separate skbs, so these
- * are actually fragments, not frames. Update frame counters only for
- * the first fragment of the frame. */
-
- frag = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG;
- type = le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_FTYPE;
-
- if (info->flags & IEEE80211_TX_STAT_ACK) {
- if (frag == 0) {
- local->dot11TransmittedFrameCount++;
- if (is_multicast_ether_addr(hdr->addr1))
- local->dot11MulticastTransmittedFrameCount++;
- if (retry_count > 0)
- local->dot11RetryCount++;
- if (retry_count > 1)
- local->dot11MultipleRetryCount++;
- }
-
- /* This counter shall be incremented for an acknowledged MPDU
- * with an individual address in the address 1 field or an MPDU
- * with a multicast address in the address 1 field of type Data
- * or Management. */
- if (!is_multicast_ether_addr(hdr->addr1) ||
- type == IEEE80211_FTYPE_DATA ||
- type == IEEE80211_FTYPE_MGMT)
- local->dot11TransmittedFragmentCount++;
- } else {
- if (frag == 0)
- local->dot11FailedCount++;
- }
-
- /* this was a transmitted frame, but now we want to reuse it */
- skb_orphan(skb);
-
- /*
- * This is a bit racy but we can avoid a lot of work
- * with this test...
- */
- if (!local->monitors && !local->cooked_mntrs) {
- dev_kfree_skb(skb);
- return;
- }
-
- /* send frame to monitor interfaces now */
-
- if (skb_headroom(skb) < sizeof(*rthdr)) {
- printk(KERN_ERR "ieee80211_tx_status: headroom too small\n");
- dev_kfree_skb(skb);
- return;
- }
-
- rthdr = (struct ieee80211_tx_status_rtap_hdr *)
- skb_push(skb, sizeof(*rthdr));
-
- memset(rthdr, 0, sizeof(*rthdr));
- rthdr->hdr.it_len = cpu_to_le16(sizeof(*rthdr));
- rthdr->hdr.it_present =
- cpu_to_le32((1 << IEEE80211_RADIOTAP_TX_FLAGS) |
- (1 << IEEE80211_RADIOTAP_DATA_RETRIES) |
- (1 << IEEE80211_RADIOTAP_RATE));
-
- if (!(info->flags & IEEE80211_TX_STAT_ACK) &&
- !is_multicast_ether_addr(hdr->addr1))
- rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_FAIL);
-
- /*
- * XXX: Once radiotap gets the bitmap reset thing the vendor
- * extensions proposal contains, we can actually report
- * the whole set of tries we did.
- */
- if ((info->status.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) ||
- (info->status.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT))
- rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_CTS);
- else if (info->status.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS)
- rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_RTS);
- if (info->status.rates[0].idx >= 0 &&
- !(info->status.rates[0].flags & IEEE80211_TX_RC_MCS))
- rthdr->rate = sband->bitrates[
- info->status.rates[0].idx].bitrate / 5;
-
- /* for now report the total retry_count */
- rthdr->data_retries = retry_count;
-
- /* XXX: is this sufficient for BPF? */
- skb_set_mac_header(skb, 0);
- skb->ip_summed = CHECKSUM_UNNECESSARY;
- skb->pkt_type = PACKET_OTHERHOST;
- skb->protocol = htons(ETH_P_802_2);
- memset(skb->cb, 0, sizeof(skb->cb));
-
- rcu_read_lock();
- list_for_each_entry_rcu(sdata, &local->interfaces, list) {
- if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
- if (!netif_running(sdata->dev))
- continue;
-
- if (prev_dev) {
- skb2 = skb_clone(skb, GFP_ATOMIC);
- if (skb2) {
- skb2->dev = prev_dev;
- netif_rx(skb2);
- }
- }
-
- prev_dev = sdata->dev;
- }
- }
- if (prev_dev) {
- skb->dev = prev_dev;
- netif_rx(skb);
- skb = NULL;
- }
- rcu_read_unlock();
- dev_kfree_skb(skb);
-}
-EXPORT_SYMBOL(ieee80211_tx_status);
-
static void ieee80211_restart_work(struct work_struct *work)
{
struct ieee80211_local *local =
@@ -659,7 +328,9 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
if (!wiphy)
return NULL;
- wiphy->netnsok = true;
+ wiphy->flags |= WIPHY_FLAG_NETNS_OK |
+ WIPHY_FLAG_4ADDR_AP |
+ WIPHY_FLAG_4ADDR_STATION;
wiphy->privid = mac80211_wiphy_privid;
/* Yes, putting cfg80211_bss into ieee80211_bss is a hack */
@@ -901,6 +572,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
i++;
}
}
+ local->int_scan_req->n_channels = i;
local->network_latency_notifier.notifier_call =
ieee80211_max_network_latency;
@@ -923,7 +595,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
fail_wep:
sta_info_stop(local);
fail_sta_info:
- debugfs_hw_del(local);
destroy_workqueue(local->workqueue);
fail_workqueue:
wiphy_unregister(local->hw.wiphy);
@@ -959,10 +630,9 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
ieee80211_clear_tx_pending(local);
sta_info_stop(local);
rate_control_deinitialize(local);
- debugfs_hw_del(local);
- if (skb_queue_len(&local->skb_queue)
- || skb_queue_len(&local->skb_queue_unreliable))
+ if (skb_queue_len(&local->skb_queue) ||
+ skb_queue_len(&local->skb_queue_unreliable))
printk(KERN_WARNING "%s: skb_queue not empty\n",
wiphy_name(local->hw.wiphy));
skb_queue_purge(&local->skb_queue);
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index f7364e56f1e..c0fe46493f7 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 open80211s Ltd.
+ * Copyright (c) 2008, 2009 open80211s Ltd.
* Authors: Luis Carlos Cobo <luisca@cozybit.com>
* Javier Cardona <javier@cozybit.com>
*
@@ -14,18 +14,14 @@
#define IEEE80211_MESH_PEER_INACTIVITY_LIMIT (1800 * HZ)
#define IEEE80211_MESH_HOUSEKEEPING_INTERVAL (60 * HZ)
+#define IEEE80211_MESH_RANN_INTERVAL (1 * HZ)
-#define PP_OFFSET 1 /* Path Selection Protocol */
-#define PM_OFFSET 5 /* Path Selection Metric */
-#define CC_OFFSET 9 /* Congestion Control Mode */
-#define SP_OFFSET 13 /* Synchronization Protocol */
-#define AUTH_OFFSET 17 /* Authentication Protocol */
-#define CAPAB_OFFSET 22
-#define CAPAB_ACCEPT_PLINKS 0x80
-#define CAPAB_FORWARDING 0x10
+#define MESHCONF_CAPAB_ACCEPT_PLINKS 0x01
+#define MESHCONF_CAPAB_FORWARDING 0x08
#define TMR_RUNNING_HK 0
#define TMR_RUNNING_MP 1
+#define TMR_RUNNING_MPR 2
int mesh_allocated;
static struct kmem_cache *rm_cache;
@@ -50,7 +46,7 @@ static void ieee80211_mesh_housekeeping_timer(unsigned long data)
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
- ifmsh->wrkq_flags |= MESH_WORK_HOUSEKEEPING;
+ set_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags);
if (local->quiescing) {
set_bit(TMR_RUNNING_HK, &ifmsh->timers_running);
@@ -85,11 +81,11 @@ bool mesh_matches_local(struct ieee802_11_elems *ie, struct ieee80211_sub_if_dat
*/
if (ifmsh->mesh_id_len == ie->mesh_id_len &&
memcmp(ifmsh->mesh_id, ie->mesh_id, ie->mesh_id_len) == 0 &&
- memcmp(ifmsh->mesh_pp_id, ie->mesh_config + PP_OFFSET, 4) == 0 &&
- memcmp(ifmsh->mesh_pm_id, ie->mesh_config + PM_OFFSET, 4) == 0 &&
- memcmp(ifmsh->mesh_cc_id, ie->mesh_config + CC_OFFSET, 4) == 0 &&
- memcmp(ifmsh->mesh_sp_id, ie->mesh_config + SP_OFFSET, 4) == 0 &&
- memcmp(ifmsh->mesh_auth_id, ie->mesh_config + AUTH_OFFSET, 4) == 0)
+ (ifmsh->mesh_pp_id == ie->mesh_config->meshconf_psel) &&
+ (ifmsh->mesh_pm_id == ie->mesh_config->meshconf_pmetric) &&
+ (ifmsh->mesh_cc_id == ie->mesh_config->meshconf_congest) &&
+ (ifmsh->mesh_sp_id == ie->mesh_config->meshconf_synch) &&
+ (ifmsh->mesh_auth_id == ie->mesh_config->meshconf_auth))
return true;
return false;
@@ -102,7 +98,8 @@ bool mesh_matches_local(struct ieee802_11_elems *ie, struct ieee80211_sub_if_dat
*/
bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie)
{
- return (*(ie->mesh_config + CAPAB_OFFSET) & CAPAB_ACCEPT_PLINKS) != 0;
+ return (ie->mesh_config->meshconf_cap &
+ MESHCONF_CAPAB_ACCEPT_PLINKS) != 0;
}
/**
@@ -128,18 +125,11 @@ void mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata)
void mesh_ids_set_default(struct ieee80211_if_mesh *sta)
{
- u8 oui[3] = {0x00, 0x0F, 0xAC};
-
- memcpy(sta->mesh_pp_id, oui, sizeof(oui));
- memcpy(sta->mesh_pm_id, oui, sizeof(oui));
- memcpy(sta->mesh_cc_id, oui, sizeof(oui));
- memcpy(sta->mesh_sp_id, oui, sizeof(oui));
- memcpy(sta->mesh_auth_id, oui, sizeof(oui));
- sta->mesh_pp_id[sizeof(oui)] = 0;
- sta->mesh_pm_id[sizeof(oui)] = 0;
- sta->mesh_cc_id[sizeof(oui)] = 0xff;
- sta->mesh_sp_id[sizeof(oui)] = 0xff;
- sta->mesh_auth_id[sizeof(oui)] = 0x0;
+ sta->mesh_pp_id = 0; /* HWMP */
+ sta->mesh_pm_id = 0; /* Airtime */
+ sta->mesh_cc_id = 0; /* Disabled */
+ sta->mesh_sp_id = 0; /* Neighbor Offset */
+ sta->mesh_auth_id = 0; /* Disabled */
}
int mesh_rmc_init(struct ieee80211_sub_if_data *sdata)
@@ -205,8 +195,8 @@ int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr,
list_del(&p->list);
kmem_cache_free(rm_cache, p);
--entries;
- } else if ((seqnum == p->seqnum)
- && (memcmp(sa, p->sa, ETH_ALEN) == 0))
+ } else if ((seqnum == p->seqnum) &&
+ (memcmp(sa, p->sa, ETH_ALEN) == 0))
return -1;
}
@@ -228,6 +218,7 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
struct ieee80211_supported_band *sband;
u8 *pos;
int len, i, rate;
+ u8 neighbors;
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
len = sband->n_bitrates;
@@ -251,46 +242,49 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
}
}
+ if (sband->band == IEEE80211_BAND_2GHZ) {
+ pos = skb_put(skb, 2 + 1);
+ *pos++ = WLAN_EID_DS_PARAMS;
+ *pos++ = 1;
+ *pos++ = ieee80211_frequency_to_channel(local->hw.conf.channel->center_freq);
+ }
+
pos = skb_put(skb, 2 + sdata->u.mesh.mesh_id_len);
*pos++ = WLAN_EID_MESH_ID;
*pos++ = sdata->u.mesh.mesh_id_len;
if (sdata->u.mesh.mesh_id_len)
memcpy(pos, sdata->u.mesh.mesh_id, sdata->u.mesh.mesh_id_len);
- pos = skb_put(skb, 2 + IEEE80211_MESH_CONFIG_LEN);
+ pos = skb_put(skb, 2 + sizeof(struct ieee80211_meshconf_ie));
*pos++ = WLAN_EID_MESH_CONFIG;
- *pos++ = IEEE80211_MESH_CONFIG_LEN;
- /* Version */
- *pos++ = 1;
+ *pos++ = sizeof(struct ieee80211_meshconf_ie);
/* Active path selection protocol ID */
- memcpy(pos, sdata->u.mesh.mesh_pp_id, 4);
- pos += 4;
+ *pos++ = sdata->u.mesh.mesh_pp_id;
/* Active path selection metric ID */
- memcpy(pos, sdata->u.mesh.mesh_pm_id, 4);
- pos += 4;
+ *pos++ = sdata->u.mesh.mesh_pm_id;
/* Congestion control mode identifier */
- memcpy(pos, sdata->u.mesh.mesh_cc_id, 4);
- pos += 4;
+ *pos++ = sdata->u.mesh.mesh_cc_id;
/* Synchronization protocol identifier */
- memcpy(pos, sdata->u.mesh.mesh_sp_id, 4);
- pos += 4;
+ *pos++ = sdata->u.mesh.mesh_sp_id;
/* Authentication Protocol identifier */
- memcpy(pos, sdata->u.mesh.mesh_auth_id, 4);
- pos += 4;
+ *pos++ = sdata->u.mesh.mesh_auth_id;
- /* Mesh Formation Info */
- memset(pos, 0x00, 1);
- pos += 1;
+ /* Mesh Formation Info - number of neighbors */
+ neighbors = atomic_read(&sdata->u.mesh.mshstats.estab_plinks);
+ /* Number of neighbor mesh STAs or 15 whichever is smaller */
+ neighbors = (neighbors > 15) ? 15 : neighbors;
+ *pos++ = neighbors << 1;
/* Mesh capability */
sdata->u.mesh.accepting_plinks = mesh_plink_availables(sdata);
- *pos = CAPAB_FORWARDING;
- *pos++ |= sdata->u.mesh.accepting_plinks ? CAPAB_ACCEPT_PLINKS : 0x00;
+ *pos = MESHCONF_CAPAB_FORWARDING;
+ *pos++ |= sdata->u.mesh.accepting_plinks ?
+ MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00;
*pos++ = 0x00;
return;
@@ -355,6 +349,34 @@ static void ieee80211_mesh_path_timer(unsigned long data)
ieee80211_queue_work(&local->hw, &ifmsh->work);
}
+static void ieee80211_mesh_path_root_timer(unsigned long data)
+{
+ struct ieee80211_sub_if_data *sdata =
+ (struct ieee80211_sub_if_data *) data;
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+ struct ieee80211_local *local = sdata->local;
+
+ set_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags);
+
+ if (local->quiescing) {
+ set_bit(TMR_RUNNING_MPR, &ifmsh->timers_running);
+ return;
+ }
+
+ ieee80211_queue_work(&local->hw, &ifmsh->work);
+}
+
+void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh)
+{
+ if (ifmsh->mshcfg.dot11MeshHWMPRootMode)
+ set_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags);
+ else {
+ clear_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags);
+ /* stop running timer */
+ del_timer_sync(&ifmsh->mesh_path_root_timer);
+ }
+}
+
/**
* ieee80211_fill_mesh_addresses - fill addresses of a locally originated mesh frame
* @hdr: 802.11 frame header
@@ -365,8 +387,9 @@ static void ieee80211_mesh_path_timer(unsigned long data)
*
* Return the length of the 802.11 (does not include a mesh control header)
*/
-int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc, char
- *meshda, char *meshsa) {
+int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc,
+ const u8 *meshda, const u8 *meshsa)
+{
if (is_multicast_ether_addr(meshda)) {
*fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
/* DA TA SA */
@@ -448,6 +471,15 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata,
round_jiffies(jiffies + IEEE80211_MESH_HOUSEKEEPING_INTERVAL));
}
+static void ieee80211_mesh_rootpath(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+
+ mesh_path_tx_root_frame(sdata);
+ mod_timer(&ifmsh->mesh_path_root_timer,
+ round_jiffies(jiffies + IEEE80211_MESH_RANN_INTERVAL));
+}
+
#ifdef CONFIG_PM
void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata)
{
@@ -462,6 +494,8 @@ void ieee80211_mesh_quiesce(struct ieee80211_sub_if_data *sdata)
set_bit(TMR_RUNNING_HK, &ifmsh->timers_running);
if (del_timer_sync(&ifmsh->mesh_path_timer))
set_bit(TMR_RUNNING_MP, &ifmsh->timers_running);
+ if (del_timer_sync(&ifmsh->mesh_path_root_timer))
+ set_bit(TMR_RUNNING_MPR, &ifmsh->timers_running);
}
void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata)
@@ -472,6 +506,9 @@ void ieee80211_mesh_restart(struct ieee80211_sub_if_data *sdata)
add_timer(&ifmsh->housekeeping_timer);
if (test_and_clear_bit(TMR_RUNNING_MP, &ifmsh->timers_running))
add_timer(&ifmsh->mesh_path_timer);
+ if (test_and_clear_bit(TMR_RUNNING_MPR, &ifmsh->timers_running))
+ add_timer(&ifmsh->mesh_path_root_timer);
+ ieee80211_mesh_root_setup(ifmsh);
}
#endif
@@ -480,7 +517,8 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct ieee80211_local *local = sdata->local;
- ifmsh->wrkq_flags |= MESH_WORK_HOUSEKEEPING;
+ set_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags);
+ ieee80211_mesh_root_setup(ifmsh);
ieee80211_queue_work(&local->hw, &ifmsh->work);
sdata->vif.bss_conf.beacon_int = MESH_DEFAULT_BEACON_INTERVAL;
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON |
@@ -491,6 +529,7 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
{
del_timer_sync(&sdata->u.mesh.housekeeping_timer);
+ del_timer_sync(&sdata->u.mesh.mesh_path_root_timer);
/*
* If the timer fired while we waited for it, it will have
* requeued the work. Now the work will be running again
@@ -561,7 +600,7 @@ static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata,
struct ieee80211_rx_status *rx_status)
{
switch (mgmt->u.action.category) {
- case PLINK_CATEGORY:
+ case MESH_PLINK_CATEGORY:
mesh_rx_plink_frame(sdata, mgmt, len, rx_status);
break;
case MESH_PATH_SEL_CATEGORY:
@@ -628,6 +667,9 @@ static void ieee80211_mesh_work(struct work_struct *work)
if (test_and_clear_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags))
ieee80211_mesh_housekeeping(sdata, ifmsh);
+
+ if (test_and_clear_bit(MESH_WORK_ROOT, &ifmsh->wrkq_flags))
+ ieee80211_mesh_rootpath(sdata);
}
void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local)
@@ -673,7 +715,7 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
MESH_MIN_DISCOVERY_TIMEOUT;
ifmsh->accepting_plinks = true;
ifmsh->preq_id = 0;
- ifmsh->dsn = 0;
+ ifmsh->sn = 0;
atomic_set(&ifmsh->mpaths, 0);
mesh_rmc_init(sdata);
ifmsh->last_preq = jiffies;
@@ -684,6 +726,9 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
setup_timer(&ifmsh->mesh_path_timer,
ieee80211_mesh_path_timer,
(unsigned long) sdata);
+ setup_timer(&ifmsh->mesh_path_root_timer,
+ ieee80211_mesh_path_root_timer,
+ (unsigned long) sdata);
INIT_LIST_HEAD(&ifmsh->preq_queue.list);
spin_lock_init(&ifmsh->mesh_preq_queue_lock);
}
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index dd1c19319f0..31e10254186 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 open80211s Ltd.
+ * Copyright (c) 2008, 2009 open80211s Ltd.
* Authors: Luis Carlos Cobo <luisca@cozybit.com>
* Javier Cardona <javier@cozybit.com>
*
@@ -26,7 +26,7 @@
*
* @MESH_PATH_ACTIVE: the mesh path can be used for forwarding
* @MESH_PATH_RESOLVING: the discovery process is running for this mesh path
- * @MESH_PATH_DSN_VALID: the mesh path contains a valid destination sequence
+ * @MESH_PATH_SN_VALID: the mesh path contains a valid destination sequence
* number
* @MESH_PATH_FIXED: the mesh path has been manually set and should not be
* modified
@@ -38,7 +38,7 @@
enum mesh_path_flags {
MESH_PATH_ACTIVE = BIT(0),
MESH_PATH_RESOLVING = BIT(1),
- MESH_PATH_DSN_VALID = BIT(2),
+ MESH_PATH_SN_VALID = BIT(2),
MESH_PATH_FIXED = BIT(3),
MESH_PATH_RESOLVED = BIT(4),
};
@@ -53,11 +53,13 @@ enum mesh_path_flags {
* to grow.
* @MESH_WORK_GROW_MPP_TABLE: the mesh portals table is full and needs to
* grow
+ * @MESH_WORK_ROOT: the mesh root station needs to send a frame
*/
enum mesh_deferred_task_flags {
MESH_WORK_HOUSEKEEPING,
MESH_WORK_GROW_MPATH_TABLE,
MESH_WORK_GROW_MPP_TABLE,
+ MESH_WORK_ROOT,
};
/**
@@ -70,7 +72,7 @@ enum mesh_deferred_task_flags {
* @timer: mesh path discovery timer
* @frame_queue: pending queue for frames sent to this destination while the
* path is unresolved
- * @dsn: destination sequence number of the destination
+ * @sn: target sequence number
* @metric: current metric to this destination
* @hop_count: hops to destination
* @exp_time: in jiffies, when the path will expire or when it expired
@@ -94,7 +96,7 @@ struct mesh_path {
struct timer_list timer;
struct sk_buff_head frame_queue;
struct rcu_head rcu;
- u32 dsn;
+ u32 sn;
u32 metric;
u8 hop_count;
unsigned long exp_time;
@@ -174,7 +176,7 @@ struct mesh_rmc {
#define MESH_CFG_CMP_LEN (IEEE80211_MESH_CONFIG_LEN - 2)
/* Default values, timeouts in ms */
-#define MESH_TTL 5
+#define MESH_TTL 31
#define MESH_MAX_RETR 3
#define MESH_RET_T 100
#define MESH_CONF_T 100
@@ -206,13 +208,19 @@ struct mesh_rmc {
#define MESH_MAX_MPATHS 1024
/* Pending ANA approval */
-#define PLINK_CATEGORY 30
+#define MESH_PLINK_CATEGORY 30
#define MESH_PATH_SEL_CATEGORY 32
+#define MESH_PATH_SEL_ACTION 0
+
+/* PERR reason codes */
+#define PEER_RCODE_UNSPECIFIED 11
+#define PERR_RCODE_NO_ROUTE 12
+#define PERR_RCODE_DEST_UNREACH 13
/* Public interfaces */
/* Various */
int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc,
- char *da, char *sa);
+ const u8 *da, const u8 *sa);
int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr,
struct ieee80211_sub_if_data *sdata, char *addr4,
char *addr5, char *addr6);
@@ -234,6 +242,7 @@ ieee80211_rx_result
ieee80211_mesh_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb);
void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata);
void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata);
+void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh);
/* Mesh paths */
int mesh_nexthop_lookup(struct sk_buff *skb,
@@ -274,8 +283,8 @@ void mesh_mpp_table_grow(void);
u32 mesh_table_hash(u8 *addr, struct ieee80211_sub_if_data *sdata,
struct mesh_table *tbl);
/* Mesh paths */
-int mesh_path_error_tx(u8 *dest, __le32 dest_dsn, u8 *ra,
- struct ieee80211_sub_if_data *sdata);
+int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn, __le16 target_rcode,
+ const u8 *ra, struct ieee80211_sub_if_data *sdata);
void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta);
void mesh_path_flush_pending(struct mesh_path *mpath);
void mesh_path_tx_pending(struct mesh_path *mpath);
@@ -288,6 +297,7 @@ void mesh_path_discard_frame(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata);
void mesh_path_quiesce(struct ieee80211_sub_if_data *sdata);
void mesh_path_restart(struct ieee80211_sub_if_data *sdata);
+void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata);
extern int mesh_paths_generation;
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 29b82e98eff..833b2f3670c 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 open80211s Ltd.
+ * Copyright (c) 2008, 2009 open80211s Ltd.
* Author: Luis Carlos Cobo <luisca@cozybit.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -9,6 +9,12 @@
#include "mesh.h"
+#ifdef CONFIG_MAC80211_VERBOSE_MHWMP_DEBUG
+#define mhwmp_dbg(fmt, args...) printk(KERN_DEBUG "Mesh HWMP: " fmt, ##args)
+#else
+#define mhwmp_dbg(fmt, args...) do { (void)(0); } while (0)
+#endif
+
#define TEST_FRAME_LEN 8192
#define MAX_METRIC 0xffffffff
#define ARITH_SHIFT 8
@@ -21,6 +27,12 @@
#define MP_F_DO 0x1
/* Reply and forward */
#define MP_F_RF 0x2
+/* Unknown Sequence Number */
+#define MP_F_USN 0x01
+/* Reason code Present */
+#define MP_F_RCODE 0x02
+
+static void mesh_queue_preq(struct mesh_path *, u8);
static inline u32 u32_field_get(u8 *preq_elem, int offset, bool ae)
{
@@ -29,6 +41,13 @@ static inline u32 u32_field_get(u8 *preq_elem, int offset, bool ae)
return get_unaligned_le32(preq_elem + offset);
}
+static inline u32 u16_field_get(u8 *preq_elem, int offset, bool ae)
+{
+ if (ae)
+ offset += 6;
+ return get_unaligned_le16(preq_elem + offset);
+}
+
/* HWMP IE processing macros */
#define AE_F (1<<6)
#define AE_F_SET(x) (*x & AE_F)
@@ -37,30 +56,33 @@ static inline u32 u32_field_get(u8 *preq_elem, int offset, bool ae)
#define PREQ_IE_TTL(x) (*(x + 2))
#define PREQ_IE_PREQ_ID(x) u32_field_get(x, 3, 0)
#define PREQ_IE_ORIG_ADDR(x) (x + 7)
-#define PREQ_IE_ORIG_DSN(x) u32_field_get(x, 13, 0);
+#define PREQ_IE_ORIG_SN(x) u32_field_get(x, 13, 0);
#define PREQ_IE_LIFETIME(x) u32_field_get(x, 17, AE_F_SET(x));
#define PREQ_IE_METRIC(x) u32_field_get(x, 21, AE_F_SET(x));
-#define PREQ_IE_DST_F(x) (*(AE_F_SET(x) ? x + 32 : x + 26))
-#define PREQ_IE_DST_ADDR(x) (AE_F_SET(x) ? x + 33 : x + 27)
-#define PREQ_IE_DST_DSN(x) u32_field_get(x, 33, AE_F_SET(x));
+#define PREQ_IE_TARGET_F(x) (*(AE_F_SET(x) ? x + 32 : x + 26))
+#define PREQ_IE_TARGET_ADDR(x) (AE_F_SET(x) ? x + 33 : x + 27)
+#define PREQ_IE_TARGET_SN(x) u32_field_get(x, 33, AE_F_SET(x));
#define PREP_IE_FLAGS(x) PREQ_IE_FLAGS(x)
#define PREP_IE_HOPCOUNT(x) PREQ_IE_HOPCOUNT(x)
#define PREP_IE_TTL(x) PREQ_IE_TTL(x)
#define PREP_IE_ORIG_ADDR(x) (x + 3)
-#define PREP_IE_ORIG_DSN(x) u32_field_get(x, 9, 0);
+#define PREP_IE_ORIG_SN(x) u32_field_get(x, 9, 0);
#define PREP_IE_LIFETIME(x) u32_field_get(x, 13, AE_F_SET(x));
#define PREP_IE_METRIC(x) u32_field_get(x, 17, AE_F_SET(x));
-#define PREP_IE_DST_ADDR(x) (AE_F_SET(x) ? x + 27 : x + 21)
-#define PREP_IE_DST_DSN(x) u32_field_get(x, 27, AE_F_SET(x));
+#define PREP_IE_TARGET_ADDR(x) (AE_F_SET(x) ? x + 27 : x + 21)
+#define PREP_IE_TARGET_SN(x) u32_field_get(x, 27, AE_F_SET(x));
-#define PERR_IE_DST_ADDR(x) (x + 2)
-#define PERR_IE_DST_DSN(x) u32_field_get(x, 8, 0);
+#define PERR_IE_TTL(x) (*(x))
+#define PERR_IE_TARGET_FLAGS(x) (*(x + 2))
+#define PERR_IE_TARGET_ADDR(x) (x + 3)
+#define PERR_IE_TARGET_SN(x) u32_field_get(x, 9, 0);
+#define PERR_IE_TARGET_RCODE(x) u16_field_get(x, 13, 0);
#define MSEC_TO_TU(x) (x*1000/1024)
-#define DSN_GT(x, y) ((long) (y) - (long) (x) < 0)
-#define DSN_LT(x, y) ((long) (x) - (long) (y) < 0)
+#define SN_GT(x, y) ((long) (y) - (long) (x) < 0)
+#define SN_LT(x, y) ((long) (x) - (long) (y) < 0)
#define net_traversal_jiffies(s) \
msecs_to_jiffies(s->u.mesh.mshcfg.dot11MeshHWMPnetDiameterTraversalTime)
@@ -75,13 +97,17 @@ static inline u32 u32_field_get(u8 *preq_elem, int offset, bool ae)
enum mpath_frame_type {
MPATH_PREQ = 0,
MPATH_PREP,
- MPATH_PERR
+ MPATH_PERR,
+ MPATH_RANN
};
+static const u8 broadcast_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags,
- u8 *orig_addr, __le32 orig_dsn, u8 dst_flags, u8 *dst,
- __le32 dst_dsn, u8 *da, u8 hop_count, u8 ttl, __le32 lifetime,
- __le32 metric, __le32 preq_id, struct ieee80211_sub_if_data *sdata)
+ u8 *orig_addr, __le32 orig_sn, u8 target_flags, u8 *target,
+ __le32 target_sn, const u8 *da, u8 hop_count, u8 ttl,
+ __le32 lifetime, __le32 metric, __le32 preq_id,
+ struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
@@ -103,21 +129,30 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags,
memcpy(mgmt->da, da, ETH_ALEN);
memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
- /* BSSID is left zeroed, wildcard value */
+ /* BSSID == SA */
+ memcpy(mgmt->bssid, sdata->dev->dev_addr, ETH_ALEN);
mgmt->u.action.category = MESH_PATH_SEL_CATEGORY;
- mgmt->u.action.u.mesh_action.action_code = action;
+ mgmt->u.action.u.mesh_action.action_code = MESH_PATH_SEL_ACTION;
switch (action) {
case MPATH_PREQ:
+ mhwmp_dbg("sending PREQ to %pM\n", target);
ie_len = 37;
pos = skb_put(skb, 2 + ie_len);
*pos++ = WLAN_EID_PREQ;
break;
case MPATH_PREP:
+ mhwmp_dbg("sending PREP to %pM\n", target);
ie_len = 31;
pos = skb_put(skb, 2 + ie_len);
*pos++ = WLAN_EID_PREP;
break;
+ case MPATH_RANN:
+ mhwmp_dbg("sending RANN from %pM\n", orig_addr);
+ ie_len = sizeof(struct ieee80211_rann_ie);
+ pos = skb_put(skb, 2 + ie_len);
+ *pos++ = WLAN_EID_RANN;
+ break;
default:
kfree_skb(skb);
return -ENOTSUPP;
@@ -133,34 +168,40 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags,
}
memcpy(pos, orig_addr, ETH_ALEN);
pos += ETH_ALEN;
- memcpy(pos, &orig_dsn, 4);
- pos += 4;
- memcpy(pos, &lifetime, 4);
+ memcpy(pos, &orig_sn, 4);
pos += 4;
+ if (action != MPATH_RANN) {
+ memcpy(pos, &lifetime, 4);
+ pos += 4;
+ }
memcpy(pos, &metric, 4);
pos += 4;
if (action == MPATH_PREQ) {
/* destination count */
*pos++ = 1;
- *pos++ = dst_flags;
+ *pos++ = target_flags;
+ }
+ if (action != MPATH_RANN) {
+ memcpy(pos, target, ETH_ALEN);
+ pos += ETH_ALEN;
+ memcpy(pos, &target_sn, 4);
}
- memcpy(pos, dst, ETH_ALEN);
- pos += ETH_ALEN;
- memcpy(pos, &dst_dsn, 4);
- ieee80211_tx_skb(sdata, skb, 1);
+ ieee80211_tx_skb(sdata, skb);
return 0;
}
/**
* mesh_send_path error - Sends a PERR mesh management frame
*
- * @dst: broken destination
- * @dst_dsn: dsn of the broken destination
+ * @target: broken destination
+ * @target_sn: SN of the broken destination
+ * @target_rcode: reason code for this PERR
* @ra: node this frame is addressed to
*/
-int mesh_path_error_tx(u8 *dst, __le32 dst_dsn, u8 *ra,
- struct ieee80211_sub_if_data *sdata)
+int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn,
+ __le16 target_rcode, const u8 *ra,
+ struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
@@ -184,20 +225,32 @@ int mesh_path_error_tx(u8 *dst, __le32 dst_dsn, u8 *ra,
memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
/* BSSID is left zeroed, wildcard value */
mgmt->u.action.category = MESH_PATH_SEL_CATEGORY;
- mgmt->u.action.u.mesh_action.action_code = MPATH_PERR;
- ie_len = 12;
+ mgmt->u.action.u.mesh_action.action_code = MESH_PATH_SEL_ACTION;
+ ie_len = 15;
pos = skb_put(skb, 2 + ie_len);
*pos++ = WLAN_EID_PERR;
*pos++ = ie_len;
- /* mode flags, reserved */
- *pos++ = 0;
+ /* ttl */
+ *pos++ = MESH_TTL;
/* number of destinations */
*pos++ = 1;
- memcpy(pos, dst, ETH_ALEN);
+ /*
+ * flags bit, bit 1 is unset if we know the sequence number and
+ * bit 2 is set if we have a reason code
+ */
+ *pos = 0;
+ if (!target_sn)
+ *pos |= MP_F_USN;
+ if (target_rcode)
+ *pos |= MP_F_RCODE;
+ pos++;
+ memcpy(pos, target, ETH_ALEN);
pos += ETH_ALEN;
- memcpy(pos, &dst_dsn, 4);
+ memcpy(pos, &target_sn, 4);
+ pos += 4;
+ memcpy(pos, &target_rcode, 2);
- ieee80211_tx_skb(sdata, skb, 1);
+ ieee80211_tx_skb(sdata, skb);
return 0;
}
@@ -269,18 +322,17 @@ static u32 airtime_link_metric_get(struct ieee80211_local *local,
*/
static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
- u8 *hwmp_ie)
+ u8 *hwmp_ie, enum mpath_frame_type action)
{
struct ieee80211_local *local = sdata->local;
struct mesh_path *mpath;
struct sta_info *sta;
bool fresh_info;
u8 *orig_addr, *ta;
- u32 orig_dsn, orig_metric;
+ u32 orig_sn, orig_metric;
unsigned long orig_lifetime, exp_time;
u32 last_hop_metric, new_metric;
bool process = true;
- u8 action = mgmt->u.action.u.mesh_action.action_code;
rcu_read_lock();
sta = sta_info_get(local, mgmt->sa);
@@ -296,7 +348,7 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
switch (action) {
case MPATH_PREQ:
orig_addr = PREQ_IE_ORIG_ADDR(hwmp_ie);
- orig_dsn = PREQ_IE_ORIG_DSN(hwmp_ie);
+ orig_sn = PREQ_IE_ORIG_SN(hwmp_ie);
orig_lifetime = PREQ_IE_LIFETIME(hwmp_ie);
orig_metric = PREQ_IE_METRIC(hwmp_ie);
break;
@@ -309,7 +361,7 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
* information from both PREQ and PREP frames.
*/
orig_addr = PREP_IE_ORIG_ADDR(hwmp_ie);
- orig_dsn = PREP_IE_ORIG_DSN(hwmp_ie);
+ orig_sn = PREP_IE_ORIG_SN(hwmp_ie);
orig_lifetime = PREP_IE_LIFETIME(hwmp_ie);
orig_metric = PREP_IE_METRIC(hwmp_ie);
break;
@@ -335,9 +387,9 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
if (mpath->flags & MESH_PATH_FIXED)
fresh_info = false;
else if ((mpath->flags & MESH_PATH_ACTIVE) &&
- (mpath->flags & MESH_PATH_DSN_VALID)) {
- if (DSN_GT(mpath->dsn, orig_dsn) ||
- (mpath->dsn == orig_dsn &&
+ (mpath->flags & MESH_PATH_SN_VALID)) {
+ if (SN_GT(mpath->sn, orig_sn) ||
+ (mpath->sn == orig_sn &&
action == MPATH_PREQ &&
new_metric > mpath->metric)) {
process = false;
@@ -356,9 +408,9 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
if (fresh_info) {
mesh_path_assign_nexthop(mpath, sta);
- mpath->flags |= MESH_PATH_DSN_VALID;
+ mpath->flags |= MESH_PATH_SN_VALID;
mpath->metric = new_metric;
- mpath->dsn = orig_dsn;
+ mpath->sn = orig_sn;
mpath->exp_time = time_after(mpath->exp_time, exp_time)
? mpath->exp_time : exp_time;
mesh_path_activate(mpath);
@@ -397,7 +449,7 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
if (fresh_info) {
mesh_path_assign_nexthop(mpath, sta);
- mpath->flags &= ~MESH_PATH_DSN_VALID;
+ mpath->flags &= ~MESH_PATH_SN_VALID;
mpath->metric = last_hop_metric;
mpath->exp_time = time_after(mpath->exp_time, exp_time)
? mpath->exp_time : exp_time;
@@ -419,44 +471,47 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct mesh_path *mpath;
- u8 *dst_addr, *orig_addr;
- u8 dst_flags, ttl;
- u32 orig_dsn, dst_dsn, lifetime;
+ u8 *target_addr, *orig_addr;
+ u8 target_flags, ttl;
+ u32 orig_sn, target_sn, lifetime;
bool reply = false;
bool forward = true;
- /* Update destination DSN, if present */
- dst_addr = PREQ_IE_DST_ADDR(preq_elem);
+ /* Update target SN, if present */
+ target_addr = PREQ_IE_TARGET_ADDR(preq_elem);
orig_addr = PREQ_IE_ORIG_ADDR(preq_elem);
- dst_dsn = PREQ_IE_DST_DSN(preq_elem);
- orig_dsn = PREQ_IE_ORIG_DSN(preq_elem);
- dst_flags = PREQ_IE_DST_F(preq_elem);
+ target_sn = PREQ_IE_TARGET_SN(preq_elem);
+ orig_sn = PREQ_IE_ORIG_SN(preq_elem);
+ target_flags = PREQ_IE_TARGET_F(preq_elem);
- if (memcmp(dst_addr, sdata->dev->dev_addr, ETH_ALEN) == 0) {
+ mhwmp_dbg("received PREQ from %pM\n", orig_addr);
+
+ if (memcmp(target_addr, sdata->dev->dev_addr, ETH_ALEN) == 0) {
+ mhwmp_dbg("PREQ is for us\n");
forward = false;
reply = true;
metric = 0;
- if (time_after(jiffies, ifmsh->last_dsn_update +
+ if (time_after(jiffies, ifmsh->last_sn_update +
net_traversal_jiffies(sdata)) ||
- time_before(jiffies, ifmsh->last_dsn_update)) {
- dst_dsn = ++ifmsh->dsn;
- ifmsh->last_dsn_update = jiffies;
+ time_before(jiffies, ifmsh->last_sn_update)) {
+ target_sn = ++ifmsh->sn;
+ ifmsh->last_sn_update = jiffies;
}
} else {
rcu_read_lock();
- mpath = mesh_path_lookup(dst_addr, sdata);
+ mpath = mesh_path_lookup(target_addr, sdata);
if (mpath) {
- if ((!(mpath->flags & MESH_PATH_DSN_VALID)) ||
- DSN_LT(mpath->dsn, dst_dsn)) {
- mpath->dsn = dst_dsn;
- mpath->flags |= MESH_PATH_DSN_VALID;
- } else if ((!(dst_flags & MP_F_DO)) &&
+ if ((!(mpath->flags & MESH_PATH_SN_VALID)) ||
+ SN_LT(mpath->sn, target_sn)) {
+ mpath->sn = target_sn;
+ mpath->flags |= MESH_PATH_SN_VALID;
+ } else if ((!(target_flags & MP_F_DO)) &&
(mpath->flags & MESH_PATH_ACTIVE)) {
reply = true;
metric = mpath->metric;
- dst_dsn = mpath->dsn;
- if (dst_flags & MP_F_RF)
- dst_flags |= MP_F_DO;
+ target_sn = mpath->sn;
+ if (target_flags & MP_F_RF)
+ target_flags |= MP_F_DO;
else
forward = false;
}
@@ -467,13 +522,14 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
if (reply) {
lifetime = PREQ_IE_LIFETIME(preq_elem);
ttl = ifmsh->mshcfg.dot11MeshTTL;
- if (ttl != 0)
- mesh_path_sel_frame_tx(MPATH_PREP, 0, dst_addr,
- cpu_to_le32(dst_dsn), 0, orig_addr,
- cpu_to_le32(orig_dsn), mgmt->sa, 0, ttl,
+ if (ttl != 0) {
+ mhwmp_dbg("replying to the PREQ\n");
+ mesh_path_sel_frame_tx(MPATH_PREP, 0, target_addr,
+ cpu_to_le32(target_sn), 0, orig_addr,
+ cpu_to_le32(orig_sn), mgmt->sa, 0, ttl,
cpu_to_le32(lifetime), cpu_to_le32(metric),
0, sdata);
- else
+ } else
ifmsh->mshstats.dropped_frames_ttl++;
}
@@ -487,13 +543,14 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
ifmsh->mshstats.dropped_frames_ttl++;
return;
}
+ mhwmp_dbg("forwarding the PREQ from %pM\n", orig_addr);
--ttl;
flags = PREQ_IE_FLAGS(preq_elem);
preq_id = PREQ_IE_PREQ_ID(preq_elem);
hopcount = PREQ_IE_HOPCOUNT(preq_elem) + 1;
mesh_path_sel_frame_tx(MPATH_PREQ, flags, orig_addr,
- cpu_to_le32(orig_dsn), dst_flags, dst_addr,
- cpu_to_le32(dst_dsn), sdata->dev->broadcast,
+ cpu_to_le32(orig_sn), target_flags, target_addr,
+ cpu_to_le32(target_sn), broadcast_addr,
hopcount, ttl, cpu_to_le32(lifetime),
cpu_to_le32(metric), cpu_to_le32(preq_id),
sdata);
@@ -508,10 +565,12 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,
u8 *prep_elem, u32 metric)
{
struct mesh_path *mpath;
- u8 *dst_addr, *orig_addr;
+ u8 *target_addr, *orig_addr;
u8 ttl, hopcount, flags;
u8 next_hop[ETH_ALEN];
- u32 dst_dsn, orig_dsn, lifetime;
+ u32 target_sn, orig_sn, lifetime;
+
+ mhwmp_dbg("received PREP from %pM\n", PREP_IE_ORIG_ADDR(prep_elem));
/* Note that we divert from the draft nomenclature and denominate
* destination to what the draft refers to as origininator. So in this
@@ -519,8 +578,8 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,
* which corresponds with the originator of the PREQ which this PREP
* replies
*/
- dst_addr = PREP_IE_DST_ADDR(prep_elem);
- if (memcmp(dst_addr, sdata->dev->dev_addr, ETH_ALEN) == 0)
+ target_addr = PREP_IE_TARGET_ADDR(prep_elem);
+ if (memcmp(target_addr, sdata->dev->dev_addr, ETH_ALEN) == 0)
/* destination, no forwarding required */
return;
@@ -531,7 +590,7 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,
}
rcu_read_lock();
- mpath = mesh_path_lookup(dst_addr, sdata);
+ mpath = mesh_path_lookup(target_addr, sdata);
if (mpath)
spin_lock_bh(&mpath->state_lock);
else
@@ -547,13 +606,13 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,
lifetime = PREP_IE_LIFETIME(prep_elem);
hopcount = PREP_IE_HOPCOUNT(prep_elem) + 1;
orig_addr = PREP_IE_ORIG_ADDR(prep_elem);
- dst_dsn = PREP_IE_DST_DSN(prep_elem);
- orig_dsn = PREP_IE_ORIG_DSN(prep_elem);
+ target_sn = PREP_IE_TARGET_SN(prep_elem);
+ orig_sn = PREP_IE_ORIG_SN(prep_elem);
mesh_path_sel_frame_tx(MPATH_PREP, flags, orig_addr,
- cpu_to_le32(orig_dsn), 0, dst_addr,
- cpu_to_le32(dst_dsn), mpath->next_hop->sta.addr, hopcount, ttl,
- cpu_to_le32(lifetime), cpu_to_le32(metric),
+ cpu_to_le32(orig_sn), 0, target_addr,
+ cpu_to_le32(target_sn), mpath->next_hop->sta.addr, hopcount,
+ ttl, cpu_to_le32(lifetime), cpu_to_le32(metric),
0, sdata);
rcu_read_unlock();
@@ -570,32 +629,96 @@ fail:
static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt, u8 *perr_elem)
{
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct mesh_path *mpath;
- u8 *ta, *dst_addr;
- u32 dst_dsn;
+ u8 ttl;
+ u8 *ta, *target_addr;
+ u8 target_flags;
+ u32 target_sn;
+ u16 target_rcode;
ta = mgmt->sa;
- dst_addr = PERR_IE_DST_ADDR(perr_elem);
- dst_dsn = PERR_IE_DST_DSN(perr_elem);
+ ttl = PERR_IE_TTL(perr_elem);
+ if (ttl <= 1) {
+ ifmsh->mshstats.dropped_frames_ttl++;
+ return;
+ }
+ ttl--;
+ target_flags = PERR_IE_TARGET_FLAGS(perr_elem);
+ target_addr = PERR_IE_TARGET_ADDR(perr_elem);
+ target_sn = PERR_IE_TARGET_SN(perr_elem);
+ target_rcode = PERR_IE_TARGET_RCODE(perr_elem);
+
rcu_read_lock();
- mpath = mesh_path_lookup(dst_addr, sdata);
+ mpath = mesh_path_lookup(target_addr, sdata);
if (mpath) {
spin_lock_bh(&mpath->state_lock);
if (mpath->flags & MESH_PATH_ACTIVE &&
memcmp(ta, mpath->next_hop->sta.addr, ETH_ALEN) == 0 &&
- (!(mpath->flags & MESH_PATH_DSN_VALID) ||
- DSN_GT(dst_dsn, mpath->dsn))) {
+ (!(mpath->flags & MESH_PATH_SN_VALID) ||
+ SN_GT(target_sn, mpath->sn))) {
mpath->flags &= ~MESH_PATH_ACTIVE;
- mpath->dsn = dst_dsn;
+ mpath->sn = target_sn;
spin_unlock_bh(&mpath->state_lock);
- mesh_path_error_tx(dst_addr, cpu_to_le32(dst_dsn),
- sdata->dev->broadcast, sdata);
+ mesh_path_error_tx(ttl, target_addr, cpu_to_le32(target_sn),
+ cpu_to_le16(target_rcode),
+ broadcast_addr, sdata);
} else
spin_unlock_bh(&mpath->state_lock);
}
rcu_read_unlock();
}
+static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt,
+ struct ieee80211_rann_ie *rann)
+{
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+ struct mesh_path *mpath;
+ u8 *ta;
+ u8 ttl, flags, hopcount;
+ u8 *orig_addr;
+ u32 orig_sn, metric;
+
+ ta = mgmt->sa;
+ ttl = rann->rann_ttl;
+ if (ttl <= 1) {
+ ifmsh->mshstats.dropped_frames_ttl++;
+ return;
+ }
+ ttl--;
+ flags = rann->rann_flags;
+ orig_addr = rann->rann_addr;
+ orig_sn = rann->rann_seq;
+ hopcount = rann->rann_hopcount;
+ hopcount++;
+ metric = rann->rann_metric;
+ mhwmp_dbg("received RANN from %pM\n", orig_addr);
+
+ rcu_read_lock();
+ mpath = mesh_path_lookup(orig_addr, sdata);
+ if (!mpath) {
+ mesh_path_add(orig_addr, sdata);
+ mpath = mesh_path_lookup(orig_addr, sdata);
+ if (!mpath) {
+ rcu_read_unlock();
+ sdata->u.mesh.mshstats.dropped_frames_no_route++;
+ return;
+ }
+ mesh_queue_preq(mpath,
+ PREQ_Q_F_START | PREQ_Q_F_REFRESH);
+ }
+ if (mpath->sn < orig_sn) {
+ mesh_path_sel_frame_tx(MPATH_RANN, flags, orig_addr,
+ cpu_to_le32(orig_sn),
+ 0, NULL, 0, broadcast_addr,
+ hopcount, ttl, 0,
+ cpu_to_le32(metric + mpath->metric),
+ 0, sdata);
+ mpath->sn = orig_sn;
+ }
+ rcu_read_unlock();
+}
void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
@@ -614,34 +737,34 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
ieee802_11_parse_elems(mgmt->u.action.u.mesh_action.variable,
len - baselen, &elems);
- switch (mgmt->u.action.u.mesh_action.action_code) {
- case MPATH_PREQ:
- if (!elems.preq || elems.preq_len != 37)
+ if (elems.preq) {
+ if (elems.preq_len != 37)
/* Right now we support just 1 destination and no AE */
return;
- last_hop_metric = hwmp_route_info_get(sdata, mgmt, elems.preq);
- if (!last_hop_metric)
- return;
- hwmp_preq_frame_process(sdata, mgmt, elems.preq, last_hop_metric);
- break;
- case MPATH_PREP:
- if (!elems.prep || elems.prep_len != 31)
+ last_hop_metric = hwmp_route_info_get(sdata, mgmt, elems.preq,
+ MPATH_PREQ);
+ if (last_hop_metric)
+ hwmp_preq_frame_process(sdata, mgmt, elems.preq,
+ last_hop_metric);
+ }
+ if (elems.prep) {
+ if (elems.prep_len != 31)
/* Right now we support no AE */
return;
- last_hop_metric = hwmp_route_info_get(sdata, mgmt, elems.prep);
- if (!last_hop_metric)
- return;
- hwmp_prep_frame_process(sdata, mgmt, elems.prep, last_hop_metric);
- break;
- case MPATH_PERR:
- if (!elems.perr || elems.perr_len != 12)
+ last_hop_metric = hwmp_route_info_get(sdata, mgmt, elems.prep,
+ MPATH_PREP);
+ if (last_hop_metric)
+ hwmp_prep_frame_process(sdata, mgmt, elems.prep,
+ last_hop_metric);
+ }
+ if (elems.perr) {
+ if (elems.perr_len != 15)
/* Right now we support only one destination per PERR */
return;
hwmp_perr_frame_process(sdata, mgmt, elems.perr);
- default:
- return;
}
-
+ if (elems.rann)
+ hwmp_rann_frame_process(sdata, mgmt, elems.rann);
}
/**
@@ -661,7 +784,7 @@ static void mesh_queue_preq(struct mesh_path *mpath, u8 flags)
preq_node = kmalloc(sizeof(struct mesh_preq_queue), GFP_ATOMIC);
if (!preq_node) {
- printk(KERN_DEBUG "Mesh HWMP: could not allocate PREQ node\n");
+ mhwmp_dbg("could not allocate PREQ node\n");
return;
}
@@ -670,7 +793,7 @@ static void mesh_queue_preq(struct mesh_path *mpath, u8 flags)
spin_unlock(&ifmsh->mesh_preq_queue_lock);
kfree(preq_node);
if (printk_ratelimit())
- printk(KERN_DEBUG "Mesh HWMP: PREQ node queue full\n");
+ mhwmp_dbg("PREQ node queue full\n");
return;
}
@@ -705,7 +828,7 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata)
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct mesh_preq_queue *preq_node;
struct mesh_path *mpath;
- u8 ttl, dst_flags;
+ u8 ttl, target_flags;
u32 lifetime;
spin_lock_bh(&ifmsh->mesh_preq_queue_lock);
@@ -747,11 +870,11 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata)
ifmsh->last_preq = jiffies;
- if (time_after(jiffies, ifmsh->last_dsn_update +
+ if (time_after(jiffies, ifmsh->last_sn_update +
net_traversal_jiffies(sdata)) ||
- time_before(jiffies, ifmsh->last_dsn_update)) {
- ++ifmsh->dsn;
- sdata->u.mesh.last_dsn_update = jiffies;
+ time_before(jiffies, ifmsh->last_sn_update)) {
+ ++ifmsh->sn;
+ sdata->u.mesh.last_sn_update = jiffies;
}
lifetime = default_lifetime(sdata);
ttl = sdata->u.mesh.mshcfg.dot11MeshTTL;
@@ -762,14 +885,14 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata)
}
if (preq_node->flags & PREQ_Q_F_REFRESH)
- dst_flags = MP_F_DO;
+ target_flags = MP_F_DO;
else
- dst_flags = MP_F_RF;
+ target_flags = MP_F_RF;
spin_unlock_bh(&mpath->state_lock);
mesh_path_sel_frame_tx(MPATH_PREQ, 0, sdata->dev->dev_addr,
- cpu_to_le32(ifmsh->dsn), dst_flags, mpath->dst,
- cpu_to_le32(mpath->dsn), sdata->dev->broadcast, 0,
+ cpu_to_le32(ifmsh->sn), target_flags, mpath->dst,
+ cpu_to_le32(mpath->sn), broadcast_addr, 0,
ttl, cpu_to_le32(lifetime), 0,
cpu_to_le32(ifmsh->preq_id++), sdata);
mod_timer(&mpath->timer, jiffies + mpath->discovery_timeout);
@@ -796,15 +919,15 @@ int mesh_nexthop_lookup(struct sk_buff *skb,
struct sk_buff *skb_to_free = NULL;
struct mesh_path *mpath;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- u8 *dst_addr = hdr->addr3;
+ u8 *target_addr = hdr->addr3;
int err = 0;
rcu_read_lock();
- mpath = mesh_path_lookup(dst_addr, sdata);
+ mpath = mesh_path_lookup(target_addr, sdata);
if (!mpath) {
- mesh_path_add(dst_addr, sdata);
- mpath = mesh_path_lookup(dst_addr, sdata);
+ mesh_path_add(target_addr, sdata);
+ mpath = mesh_path_lookup(target_addr, sdata);
if (!mpath) {
sdata->u.mesh.mshstats.dropped_frames_no_route++;
err = -ENOSPC;
@@ -813,17 +936,16 @@ int mesh_nexthop_lookup(struct sk_buff *skb,
}
if (mpath->flags & MESH_PATH_ACTIVE) {
- if (time_after(jiffies, mpath->exp_time +
- msecs_to_jiffies(sdata->u.mesh.mshcfg.path_refresh_time))
- && !memcmp(sdata->dev->dev_addr, hdr->addr4,
- ETH_ALEN)
- && !(mpath->flags & MESH_PATH_RESOLVING)
- && !(mpath->flags & MESH_PATH_FIXED)) {
+ if (time_after(jiffies,
+ mpath->exp_time +
+ msecs_to_jiffies(sdata->u.mesh.mshcfg.path_refresh_time)) &&
+ !memcmp(sdata->dev->dev_addr, hdr->addr4, ETH_ALEN) &&
+ !(mpath->flags & MESH_PATH_RESOLVING) &&
+ !(mpath->flags & MESH_PATH_FIXED)) {
mesh_queue_preq(mpath,
PREQ_Q_F_START | PREQ_Q_F_REFRESH);
}
- memcpy(hdr->addr1, mpath->next_hop->sta.addr,
- ETH_ALEN);
+ memcpy(hdr->addr1, mpath->next_hop->sta.addr, ETH_ALEN);
} else {
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
if (!(mpath->flags & MESH_PATH_RESOLVING)) {
@@ -882,3 +1004,14 @@ void mesh_path_timer(unsigned long data)
endmpathtimer:
rcu_read_unlock();
}
+
+void
+mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+
+ mesh_path_sel_frame_tx(MPATH_RANN, 0, sdata->dev->dev_addr,
+ cpu_to_le32(++ifmsh->sn),
+ 0, NULL, 0, broadcast_addr,
+ 0, MESH_TTL, 0, 0, 0, sdata);
+}
diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c
index 751c4d0e2b3..a8da23905c7 100644
--- a/net/mac80211/mesh_pathtbl.c
+++ b/net/mac80211/mesh_pathtbl.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 open80211s Ltd.
+ * Copyright (c) 2008, 2009 open80211s Ltd.
* Author: Luis Carlos Cobo <luisca@cozybit.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -449,6 +449,7 @@ err_path_alloc:
*/
void mesh_plink_broken(struct sta_info *sta)
{
+ static const u8 bcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
struct mesh_path *mpath;
struct mpath_node *node;
struct hlist_node *p;
@@ -463,11 +464,12 @@ void mesh_plink_broken(struct sta_info *sta)
mpath->flags & MESH_PATH_ACTIVE &&
!(mpath->flags & MESH_PATH_FIXED)) {
mpath->flags &= ~MESH_PATH_ACTIVE;
- ++mpath->dsn;
+ ++mpath->sn;
spin_unlock_bh(&mpath->state_lock);
- mesh_path_error_tx(mpath->dst,
- cpu_to_le32(mpath->dsn),
- sdata->dev->broadcast, sdata);
+ mesh_path_error_tx(MESH_TTL, mpath->dst,
+ cpu_to_le32(mpath->sn),
+ cpu_to_le16(PERR_RCODE_DEST_UNREACH),
+ bcast, sdata);
} else
spin_unlock_bh(&mpath->state_lock);
}
@@ -601,7 +603,7 @@ void mesh_path_discard_frame(struct sk_buff *skb,
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct mesh_path *mpath;
- u32 dsn = 0;
+ u32 sn = 0;
if (memcmp(hdr->addr4, sdata->dev->dev_addr, ETH_ALEN) != 0) {
u8 *ra, *da;
@@ -610,8 +612,9 @@ void mesh_path_discard_frame(struct sk_buff *skb,
ra = hdr->addr1;
mpath = mesh_path_lookup(da, sdata);
if (mpath)
- dsn = ++mpath->dsn;
- mesh_path_error_tx(skb->data, cpu_to_le32(dsn), ra, sdata);
+ sn = ++mpath->sn;
+ mesh_path_error_tx(MESH_TTL, skb->data, cpu_to_le32(sn),
+ cpu_to_le16(PERR_RCODE_NO_ROUTE), ra, sdata);
}
kfree_skb(skb);
@@ -646,7 +649,7 @@ void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop)
{
spin_lock_bh(&mpath->state_lock);
mesh_path_assign_nexthop(mpath, next_hop);
- mpath->dsn = 0xffff;
+ mpath->sn = 0xffff;
mpath->metric = 0;
mpath->hop_count = 0;
mpath->exp_time = 0;
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index ffcbad75e09..0f7c6e6a424 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 open80211s Ltd.
+ * Copyright (c) 2008, 2009 open80211s Ltd.
* Author: Luis Carlos Cobo <luisca@cozybit.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -18,9 +18,8 @@
#define mpl_dbg(fmt, args...) do { (void)(0); } while (0)
#endif
-#define PLINK_GET_FRAME_SUBTYPE(p) (p)
-#define PLINK_GET_LLID(p) (p + 1)
-#define PLINK_GET_PLID(p) (p + 3)
+#define PLINK_GET_LLID(p) (p + 4)
+#define PLINK_GET_PLID(p) (p + 6)
#define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \
jiffies + HZ * t / 1000))
@@ -65,6 +64,7 @@ void mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata)
{
atomic_inc(&sdata->u.mesh.mshstats.estab_plinks);
mesh_accept_plinks_update(sdata);
+ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
}
static inline
@@ -72,12 +72,13 @@ void mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata)
{
atomic_dec(&sdata->u.mesh.mshstats.estab_plinks);
mesh_accept_plinks_update(sdata);
+ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
}
/**
* mesh_plink_fsm_restart - restart a mesh peer link finite state machine
*
- * @sta: mes peer link to restart
+ * @sta: mesh peer link to restart
*
* Locking: this function must be called holding sta->lock
*/
@@ -152,6 +153,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
struct ieee80211_mgmt *mgmt;
bool include_plid = false;
+ static const u8 meshpeeringproto[] = { 0x00, 0x0F, 0xAC, 0x2A };
u8 *pos;
int ie_len;
@@ -169,7 +171,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
memcpy(mgmt->da, da, ETH_ALEN);
memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
/* BSSID is left zeroed, wildcard value */
- mgmt->u.action.category = PLINK_CATEGORY;
+ mgmt->u.action.category = MESH_PLINK_CATEGORY;
mgmt->u.action.u.plink_action.action_code = action;
if (action == PLINK_CLOSE)
@@ -179,7 +181,8 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
if (action == PLINK_CONFIRM) {
pos = skb_put(skb, 4);
/* two-byte status code followed by two-byte AID */
- memset(pos, 0, 4);
+ memset(pos, 0, 2);
+ memcpy(pos + 2, &plid, 2);
}
mesh_mgmt_ies_add(skb, sdata);
}
@@ -187,18 +190,18 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
/* Add Peer Link Management element */
switch (action) {
case PLINK_OPEN:
- ie_len = 3;
+ ie_len = 6;
break;
case PLINK_CONFIRM:
- ie_len = 5;
+ ie_len = 8;
include_plid = true;
break;
case PLINK_CLOSE:
default:
if (!plid)
- ie_len = 5;
+ ie_len = 8;
else {
- ie_len = 7;
+ ie_len = 10;
include_plid = true;
}
break;
@@ -207,7 +210,8 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
pos = skb_put(skb, 2 + ie_len);
*pos++ = WLAN_EID_PEER_LINK;
*pos++ = ie_len;
- *pos++ = action;
+ memcpy(pos, meshpeeringproto, sizeof(meshpeeringproto));
+ pos += 4;
memcpy(pos, &llid, 2);
if (include_plid) {
pos += 2;
@@ -218,7 +222,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
memcpy(pos, &reason, 2);
}
- ieee80211_tx_skb(sdata, skb, 1);
+ ieee80211_tx_skb(sdata, skb);
return 0;
}
@@ -395,6 +399,17 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
u8 ie_len;
u8 *baseaddr;
__le16 plid, llid, reason;
+#ifdef CONFIG_MAC80211_VERBOSE_MPL_DEBUG
+ static const char *mplstates[] = {
+ [PLINK_LISTEN] = "LISTEN",
+ [PLINK_OPN_SNT] = "OPN-SNT",
+ [PLINK_OPN_RCVD] = "OPN-RCVD",
+ [PLINK_CNF_RCVD] = "CNF_RCVD",
+ [PLINK_ESTAB] = "ESTAB",
+ [PLINK_HOLDING] = "HOLDING",
+ [PLINK_BLOCKED] = "BLOCKED"
+ };
+#endif
/* need action_code, aux */
if (len < IEEE80211_MIN_ACTION_SIZE + 3)
@@ -417,12 +432,13 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
return;
}
- ftype = *((u8 *)PLINK_GET_FRAME_SUBTYPE(elems.peer_link));
+ ftype = mgmt->u.action.u.plink_action.action_code;
ie_len = elems.peer_link_len;
- if ((ftype == PLINK_OPEN && ie_len != 3) ||
- (ftype == PLINK_CONFIRM && ie_len != 5) ||
- (ftype == PLINK_CLOSE && ie_len != 5 && ie_len != 7)) {
- mpl_dbg("Mesh plink: incorrect plink ie length\n");
+ if ((ftype == PLINK_OPEN && ie_len != 6) ||
+ (ftype == PLINK_CONFIRM && ie_len != 8) ||
+ (ftype == PLINK_CLOSE && ie_len != 8 && ie_len != 10)) {
+ mpl_dbg("Mesh plink: incorrect plink ie length %d %d\n",
+ ftype, ie_len);
return;
}
@@ -434,7 +450,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
* from the point of view of this host.
*/
memcpy(&plid, PLINK_GET_LLID(elems.peer_link), 2);
- if (ftype == PLINK_CONFIRM || (ftype == PLINK_CLOSE && ie_len == 7))
+ if (ftype == PLINK_CONFIRM || (ftype == PLINK_CLOSE && ie_len == 10))
memcpy(&llid, PLINK_GET_PLID(elems.peer_link), 2);
rcu_read_lock();
@@ -532,8 +548,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
}
}
- mpl_dbg("Mesh plink (peer, state, llid, plid, event): %pM %d %d %d %d\n",
- mgmt->sa, sta->plink_state,
+ mpl_dbg("Mesh plink (peer, state, llid, plid, event): %pM %s %d %d %d\n",
+ mgmt->sa, mplstates[sta->plink_state],
le16_to_cpu(sta->llid), le16_to_cpu(sta->plid),
event);
reason = 0;
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index dc5049d58c5..6dc7b5ad9a4 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -426,7 +426,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
}
- ieee80211_tx_skb(sdata, skb, 0);
+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+ ieee80211_tx_skb(sdata, skb);
}
@@ -458,10 +459,18 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
mgmt->u.deauth.reason_code = cpu_to_le16(reason);
if (stype == IEEE80211_STYPE_DEAUTH)
- cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len, cookie);
+ if (cookie)
+ __cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len);
+ else
+ cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len);
else
- cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len, cookie);
- ieee80211_tx_skb(sdata, skb, ifmgd->flags & IEEE80211_STA_MFP_ENABLED);
+ if (cookie)
+ __cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len);
+ else
+ cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len);
+ if (!(ifmgd->flags & IEEE80211_STA_MFP_ENABLED))
+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+ ieee80211_tx_skb(sdata, skb);
}
void ieee80211_send_pspoll(struct ieee80211_local *local,
@@ -492,7 +501,8 @@ void ieee80211_send_pspoll(struct ieee80211_local *local,
memcpy(pspoll->bssid, ifmgd->bssid, ETH_ALEN);
memcpy(pspoll->ta, sdata->dev->dev_addr, ETH_ALEN);
- ieee80211_tx_skb(sdata, skb, 0);
+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+ ieee80211_tx_skb(sdata, skb);
}
void ieee80211_send_nullfunc(struct ieee80211_local *local,
@@ -525,7 +535,8 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
memcpy(nullfunc->addr2, sdata->dev->dev_addr, ETH_ALEN);
memcpy(nullfunc->addr3, sdata->u.mgd.bssid, ETH_ALEN);
- ieee80211_tx_skb(sdata, skb, 0);
+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+ ieee80211_tx_skb(sdata, skb);
}
/* spectrum management related things */
@@ -923,7 +934,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
ieee80211_recalc_ps(local, -1);
mutex_unlock(&local->iflist_mtx);
- netif_tx_start_all_queues(sdata->dev);
+ netif_start_queue(sdata->dev);
netif_carrier_on(sdata->dev);
}
@@ -1055,7 +1066,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
* time -- we don't want the scan code to enable queues.
*/
- netif_tx_stop_all_queues(sdata->dev);
+ netif_stop_queue(sdata->dev);
netif_carrier_off(sdata->dev);
rcu_read_lock();
@@ -1892,7 +1903,6 @@ ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata,
fc = le16_to_cpu(mgmt->frame_control);
switch (fc & IEEE80211_FCTL_STYPE) {
- case IEEE80211_STYPE_PROBE_REQ:
case IEEE80211_STYPE_PROBE_RESP:
case IEEE80211_STYPE_BEACON:
case IEEE80211_STYPE_AUTH:
@@ -1958,12 +1968,10 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
/* no action */
break;
case RX_MGMT_CFG80211_DEAUTH:
- cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len,
- NULL);
+ cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len);
break;
case RX_MGMT_CFG80211_DISASSOC:
- cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len,
- NULL);
+ cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len);
break;
default:
WARN(1, "unexpected: %d", rma);
@@ -2018,7 +2026,7 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, skb->len);
break;
case RX_MGMT_CFG80211_DEAUTH:
- cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len, NULL);
+ cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len);
break;
default:
WARN(1, "unexpected: %d", rma);
@@ -2500,6 +2508,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_mgd_work *wk;
const u8 *bssid = NULL;
+ bool not_auth_yet = false;
mutex_lock(&ifmgd->mtx);
@@ -2509,6 +2518,8 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
} else list_for_each_entry(wk, &ifmgd->work_list, list) {
if (&wk->bss->cbss == req->bss) {
bssid = req->bss->bssid;
+ if (wk->state == IEEE80211_MGD_STATE_PROBE)
+ not_auth_yet = true;
list_del(&wk->list);
kfree(wk);
break;
@@ -2516,6 +2527,20 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
}
/*
+ * If somebody requests authentication and we haven't
+ * sent out an auth frame yet there's no need to send
+ * out a deauth frame either. If the state was PROBE,
+ * then this is the case. If it's AUTH we have sent a
+ * frame, and if it's IDLE we have completed the auth
+ * process already.
+ */
+ if (not_auth_yet) {
+ mutex_unlock(&ifmgd->mtx);
+ __cfg80211_auth_canceled(sdata->dev, bssid);
+ return 0;
+ }
+
+ /*
* cfg80211 should catch this ... but it's racy since
* we can receive a deauth frame, process it, hand it
* to cfg80211 while that's in a locked section already
diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index b33efc4fc26..b9007f80cb9 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -163,8 +163,7 @@ struct rate_control_ref *rate_control_alloc(const char *name,
#ifdef CONFIG_MAC80211_DEBUGFS
debugfsdir = debugfs_create_dir("rc", local->hw.wiphy->debugfsdir);
local->debugfs.rcdir = debugfsdir;
- local->debugfs.rcname = debugfs_create_file("name", 0400, debugfsdir,
- ref, &rcname_ops);
+ debugfs_create_file("name", 0400, debugfsdir, ref, &rcname_ops);
#endif
ref->priv = ref->ops->alloc(&local->hw, debugfsdir);
@@ -188,9 +187,7 @@ static void rate_control_release(struct kref *kref)
ctrl_ref->ops->free(ctrl_ref->priv);
#ifdef CONFIG_MAC80211_DEBUGFS
- debugfs_remove(ctrl_ref->local->debugfs.rcname);
- ctrl_ref->local->debugfs.rcname = NULL;
- debugfs_remove(ctrl_ref->local->debugfs.rcdir);
+ debugfs_remove_recursive(ctrl_ref->local->debugfs.rcdir);
ctrl_ref->local->debugfs.rcdir = NULL;
#endif
@@ -287,9 +284,16 @@ int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,
struct rate_control_ref *ref, *old;
ASSERT_RTNL();
+
if (local->open_count)
return -EBUSY;
+ if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) {
+ if (WARN_ON(!local->ops->set_rts_threshold))
+ return -EINVAL;
+ return 0;
+ }
+
ref = rate_control_alloc(name, local);
if (!ref) {
printk(KERN_WARNING "%s: Failed to select rate control "
@@ -308,7 +312,6 @@ int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,
"algorithm '%s'\n", wiphy_name(local->hw.wiphy),
ref->ops->name);
-
return 0;
}
@@ -317,6 +320,10 @@ void rate_control_deinitialize(struct ieee80211_local *local)
struct rate_control_ref *ref;
ref = local->rate_ctrl;
+
+ if (!ref)
+ return;
+
local->rate_ctrl = NULL;
rate_control_put(ref);
}
diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h
index 2ab5ad9e71c..cb9bd1f65e2 100644
--- a/net/mac80211/rate.h
+++ b/net/mac80211/rate.h
@@ -59,6 +59,9 @@ static inline void rate_control_rate_init(struct sta_info *sta)
void *priv_sta = sta->rate_ctrl_priv;
struct ieee80211_supported_band *sband;
+ if (!ref)
+ return;
+
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
ref->ops->rate_init(ref->priv, sband, ista, priv_sta);
@@ -72,7 +75,7 @@ static inline void rate_control_rate_update(struct ieee80211_local *local,
struct ieee80211_sta *ista = &sta->sta;
void *priv_sta = sta->rate_ctrl_priv;
- if (ref->ops->rate_update)
+ if (ref && ref->ops->rate_update)
ref->ops->rate_update(ref->priv, sband, ista,
priv_sta, changed);
}
@@ -97,7 +100,7 @@ static inline void rate_control_add_sta_debugfs(struct sta_info *sta)
{
#ifdef CONFIG_MAC80211_DEBUGFS
struct rate_control_ref *ref = sta->rate_ctrl;
- if (sta->debugfs.dir && ref->ops->add_sta_debugfs)
+ if (ref && sta->debugfs.dir && ref->ops->add_sta_debugfs)
ref->ops->add_sta_debugfs(ref->priv, sta->rate_ctrl_priv,
sta->debugfs.dir);
#endif
@@ -107,7 +110,7 @@ static inline void rate_control_remove_sta_debugfs(struct sta_info *sta)
{
#ifdef CONFIG_MAC80211_DEBUGFS
struct rate_control_ref *ref = sta->rate_ctrl;
- if (ref->ops->remove_sta_debugfs)
+ if (ref && ref->ops->remove_sta_debugfs)
ref->ops->remove_sta_debugfs(ref->priv, sta->rate_ctrl_priv);
#endif
}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 7170bf4565a..f237df40837 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -27,11 +27,6 @@
#include "tkip.h"
#include "wme.h"
-static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
- struct tid_ampdu_rx *tid_agg_rx,
- struct sk_buff *skb,
- u16 mpdu_seq_num,
- int bar_req);
/*
* monitor mode reception
*
@@ -39,11 +34,8 @@ static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
* only useful for monitoring.
*/
static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
- struct sk_buff *skb,
- int rtap_len)
+ struct sk_buff *skb)
{
- skb_pull(skb, rtap_len);
-
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) {
if (likely(skb->len > FCS_LEN))
skb_trim(skb, skb->len - FCS_LEN);
@@ -59,15 +51,14 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
}
static inline int should_drop_frame(struct sk_buff *skb,
- int present_fcs_len,
- int radiotap_len)
+ int present_fcs_len)
{
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC))
return 1;
- if (unlikely(skb->len < 16 + present_fcs_len + radiotap_len))
+ if (unlikely(skb->len < 16 + present_fcs_len))
return 1;
if (ieee80211_is_ctl(hdr->frame_control) &&
!ieee80211_is_pspoll(hdr->frame_control) &&
@@ -95,10 +86,6 @@ ieee80211_rx_radiotap_len(struct ieee80211_local *local,
if (len & 1) /* padding for RX_FLAGS if necessary */
len++;
- /* make sure radiotap starts at a naturally aligned address */
- if (len % 8)
- len = roundup(len, 8);
-
return len;
}
@@ -116,6 +103,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_radiotap_header *rthdr;
unsigned char *pos;
+ u16 rx_flags = 0;
rthdr = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len);
memset(rthdr, 0, rtap_len);
@@ -134,7 +122,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
/* IEEE80211_RADIOTAP_TSFT */
if (status->flag & RX_FLAG_TSFT) {
- *(__le64 *)pos = cpu_to_le64(status->mactime);
+ put_unaligned_le64(status->mactime, pos);
rthdr->it_present |=
cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT);
pos += 8;
@@ -166,17 +154,20 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
pos++;
/* IEEE80211_RADIOTAP_CHANNEL */
- *(__le16 *)pos = cpu_to_le16(status->freq);
+ put_unaligned_le16(status->freq, pos);
pos += 2;
if (status->band == IEEE80211_BAND_5GHZ)
- *(__le16 *)pos = cpu_to_le16(IEEE80211_CHAN_OFDM |
- IEEE80211_CHAN_5GHZ);
+ put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ,
+ pos);
+ else if (status->flag & RX_FLAG_HT)
+ put_unaligned_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ,
+ pos);
else if (rate->flags & IEEE80211_RATE_ERP_G)
- *(__le16 *)pos = cpu_to_le16(IEEE80211_CHAN_OFDM |
- IEEE80211_CHAN_2GHZ);
+ put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ,
+ pos);
else
- *(__le16 *)pos = cpu_to_le16(IEEE80211_CHAN_CCK |
- IEEE80211_CHAN_2GHZ);
+ put_unaligned_le16(IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ,
+ pos);
pos += 2;
/* IEEE80211_RADIOTAP_DBM_ANTSIGNAL */
@@ -205,10 +196,11 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
/* IEEE80211_RADIOTAP_RX_FLAGS */
/* ensure 2 byte alignment for the 2 byte field as required */
- if ((pos - (unsigned char *)rthdr) & 1)
+ if ((pos - (u8 *)rthdr) & 1)
pos++;
if (status->flag & RX_FLAG_FAILED_PLCP_CRC)
- *(__le16 *)pos |= cpu_to_le16(IEEE80211_RADIOTAP_F_RX_BADPLCP);
+ rx_flags |= IEEE80211_RADIOTAP_F_RX_BADPLCP;
+ put_unaligned_le16(rx_flags, pos);
pos += 2;
}
@@ -227,7 +219,6 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
struct sk_buff *skb, *skb2;
struct net_device *prev_dev = NULL;
int present_fcs_len = 0;
- int rtap_len = 0;
/*
* First, we may need to make a copy of the skb because
@@ -237,25 +228,23 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
* We don't need to, of course, if we aren't going to return
* the SKB because it has a bad FCS/PLCP checksum.
*/
- if (status->flag & RX_FLAG_RADIOTAP)
- rtap_len = ieee80211_get_radiotap_len(origskb->data);
- else
- /* room for the radiotap header based on driver features */
- needed_headroom = ieee80211_rx_radiotap_len(local, status);
+
+ /* room for the radiotap header based on driver features */
+ needed_headroom = ieee80211_rx_radiotap_len(local, status);
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
present_fcs_len = FCS_LEN;
if (!local->monitors) {
- if (should_drop_frame(origskb, present_fcs_len, rtap_len)) {
+ if (should_drop_frame(origskb, present_fcs_len)) {
dev_kfree_skb(origskb);
return NULL;
}
- return remove_monitor_info(local, origskb, rtap_len);
+ return remove_monitor_info(local, origskb);
}
- if (should_drop_frame(origskb, present_fcs_len, rtap_len)) {
+ if (should_drop_frame(origskb, present_fcs_len)) {
/* only need to expand headroom if necessary */
skb = origskb;
origskb = NULL;
@@ -279,16 +268,14 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
*/
skb = skb_copy_expand(origskb, needed_headroom, 0, GFP_ATOMIC);
- origskb = remove_monitor_info(local, origskb, rtap_len);
+ origskb = remove_monitor_info(local, origskb);
if (!skb)
return origskb;
}
- /* if necessary, prepend radiotap information */
- if (!(status->flag & RX_FLAG_RADIOTAP))
- ieee80211_add_rx_radiotap_header(local, skb, rate,
- needed_headroom);
+ /* prepend radiotap information */
+ ieee80211_add_rx_radiotap_header(local, skb, rate, needed_headroom);
skb_reset_mac_header(skb);
skb->ip_summed = CHECKSUM_UNNECESSARY;
@@ -489,7 +476,7 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control);
- char *dev_addr = rx->dev->dev_addr;
+ char *dev_addr = rx->sdata->dev->dev_addr;
if (ieee80211_is_data(hdr->frame_control)) {
if (is_multicast_ether_addr(hdr->addr1)) {
@@ -518,7 +505,7 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
if (ieee80211_is_action(hdr->frame_control)) {
mgmt = (struct ieee80211_mgmt *)hdr;
- if (mgmt->u.action.category != PLINK_CATEGORY)
+ if (mgmt->u.action.category != MESH_PLINK_CATEGORY)
return RX_DROP_MONITOR;
return RX_CONTINUE;
}
@@ -543,6 +530,242 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
return RX_CONTINUE;
}
+#define SEQ_MODULO 0x1000
+#define SEQ_MASK 0xfff
+
+static inline int seq_less(u16 sq1, u16 sq2)
+{
+ return ((sq1 - sq2) & SEQ_MASK) > (SEQ_MODULO >> 1);
+}
+
+static inline u16 seq_inc(u16 sq)
+{
+ return (sq + 1) & SEQ_MASK;
+}
+
+static inline u16 seq_sub(u16 sq1, u16 sq2)
+{
+ return (sq1 - sq2) & SEQ_MASK;
+}
+
+
+static void ieee80211_release_reorder_frame(struct ieee80211_hw *hw,
+ struct tid_ampdu_rx *tid_agg_rx,
+ int index,
+ struct sk_buff_head *frames)
+{
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_rate *rate = NULL;
+ struct sk_buff *skb = tid_agg_rx->reorder_buf[index];
+ struct ieee80211_rx_status *status;
+
+ if (!skb)
+ goto no_frame;
+
+ status = IEEE80211_SKB_RXCB(skb);
+
+ /* release the reordered frames to stack */
+ sband = hw->wiphy->bands[status->band];
+ if (!(status->flag & RX_FLAG_HT))
+ rate = &sband->bitrates[status->rate_idx];
+ tid_agg_rx->stored_mpdu_num--;
+ tid_agg_rx->reorder_buf[index] = NULL;
+ __skb_queue_tail(frames, skb);
+
+no_frame:
+ tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num);
+}
+
+static void ieee80211_release_reorder_frames(struct ieee80211_hw *hw,
+ struct tid_ampdu_rx *tid_agg_rx,
+ u16 head_seq_num,
+ struct sk_buff_head *frames)
+{
+ int index;
+
+ while (seq_less(tid_agg_rx->head_seq_num, head_seq_num)) {
+ index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) %
+ tid_agg_rx->buf_size;
+ ieee80211_release_reorder_frame(hw, tid_agg_rx, index, frames);
+ }
+}
+
+/*
+ * Timeout (in jiffies) for skb's that are waiting in the RX reorder buffer. If
+ * the skb was added to the buffer longer than this time ago, the earlier
+ * frames that have not yet been received are assumed to be lost and the skb
+ * can be released for processing. This may also release other skb's from the
+ * reorder buffer if there are no additional gaps between the frames.
+ */
+#define HT_RX_REORDER_BUF_TIMEOUT (HZ / 10)
+
+/*
+ * As this function belongs to the RX path it must be under
+ * rcu_read_lock protection. It returns false if the frame
+ * can be processed immediately, true if it was consumed.
+ */
+static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
+ struct tid_ampdu_rx *tid_agg_rx,
+ struct sk_buff *skb,
+ struct sk_buff_head *frames)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ u16 sc = le16_to_cpu(hdr->seq_ctrl);
+ u16 mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4;
+ u16 head_seq_num, buf_size;
+ int index;
+
+ buf_size = tid_agg_rx->buf_size;
+ head_seq_num = tid_agg_rx->head_seq_num;
+
+ /* frame with out of date sequence number */
+ if (seq_less(mpdu_seq_num, head_seq_num)) {
+ dev_kfree_skb(skb);
+ return true;
+ }
+
+ /*
+ * If frame the sequence number exceeds our buffering window
+ * size release some previous frames to make room for this one.
+ */
+ if (!seq_less(mpdu_seq_num, head_seq_num + buf_size)) {
+ head_seq_num = seq_inc(seq_sub(mpdu_seq_num, buf_size));
+ /* release stored frames up to new head to stack */
+ ieee80211_release_reorder_frames(hw, tid_agg_rx, head_seq_num,
+ frames);
+ }
+
+ /* Now the new frame is always in the range of the reordering buffer */
+
+ index = seq_sub(mpdu_seq_num, tid_agg_rx->ssn) % tid_agg_rx->buf_size;
+
+ /* check if we already stored this frame */
+ if (tid_agg_rx->reorder_buf[index]) {
+ dev_kfree_skb(skb);
+ return true;
+ }
+
+ /*
+ * If the current MPDU is in the right order and nothing else
+ * is stored we can process it directly, no need to buffer it.
+ */
+ if (mpdu_seq_num == tid_agg_rx->head_seq_num &&
+ tid_agg_rx->stored_mpdu_num == 0) {
+ tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num);
+ return false;
+ }
+
+ /* put the frame in the reordering buffer */
+ tid_agg_rx->reorder_buf[index] = skb;
+ tid_agg_rx->reorder_time[index] = jiffies;
+ tid_agg_rx->stored_mpdu_num++;
+ /* release the buffer until next missing frame */
+ index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) %
+ tid_agg_rx->buf_size;
+ if (!tid_agg_rx->reorder_buf[index] &&
+ tid_agg_rx->stored_mpdu_num > 1) {
+ /*
+ * No buffers ready to be released, but check whether any
+ * frames in the reorder buffer have timed out.
+ */
+ int j;
+ int skipped = 1;
+ for (j = (index + 1) % tid_agg_rx->buf_size; j != index;
+ j = (j + 1) % tid_agg_rx->buf_size) {
+ if (!tid_agg_rx->reorder_buf[j]) {
+ skipped++;
+ continue;
+ }
+ if (!time_after(jiffies, tid_agg_rx->reorder_time[j] +
+ HT_RX_REORDER_BUF_TIMEOUT))
+ break;
+
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ if (net_ratelimit())
+ printk(KERN_DEBUG "%s: release an RX reorder "
+ "frame due to timeout on earlier "
+ "frames\n",
+ wiphy_name(hw->wiphy));
+#endif
+ ieee80211_release_reorder_frame(hw, tid_agg_rx,
+ j, frames);
+
+ /*
+ * Increment the head seq# also for the skipped slots.
+ */
+ tid_agg_rx->head_seq_num =
+ (tid_agg_rx->head_seq_num + skipped) & SEQ_MASK;
+ skipped = 0;
+ }
+ } else while (tid_agg_rx->reorder_buf[index]) {
+ ieee80211_release_reorder_frame(hw, tid_agg_rx, index, frames);
+ index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn) %
+ tid_agg_rx->buf_size;
+ }
+
+ return true;
+}
+
+/*
+ * Reorder MPDUs from A-MPDUs, keeping them on a buffer. Returns
+ * true if the MPDU was buffered, false if it should be processed.
+ */
+static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
+ struct sk_buff_head *frames)
+{
+ struct sk_buff *skb = rx->skb;
+ struct ieee80211_local *local = rx->local;
+ struct ieee80211_hw *hw = &local->hw;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ struct sta_info *sta = rx->sta;
+ struct tid_ampdu_rx *tid_agg_rx;
+ u16 sc;
+ int tid;
+
+ if (!ieee80211_is_data_qos(hdr->frame_control))
+ goto dont_reorder;
+
+ /*
+ * filter the QoS data rx stream according to
+ * STA/TID and check if this STA/TID is on aggregation
+ */
+
+ if (!sta)
+ goto dont_reorder;
+
+ tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
+
+ if (sta->ampdu_mlme.tid_state_rx[tid] != HT_AGG_STATE_OPERATIONAL)
+ goto dont_reorder;
+
+ tid_agg_rx = sta->ampdu_mlme.tid_rx[tid];
+
+ /* qos null data frames are excluded */
+ if (unlikely(hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_NULLFUNC)))
+ goto dont_reorder;
+
+ /* new, potentially un-ordered, ampdu frame - process it */
+
+ /* reset session timer */
+ if (tid_agg_rx->timeout)
+ mod_timer(&tid_agg_rx->session_timer,
+ TU_TO_EXP_TIME(tid_agg_rx->timeout));
+
+ /* if this mpdu is fragmented - terminate rx aggregation session */
+ sc = le16_to_cpu(hdr->seq_ctrl);
+ if (sc & IEEE80211_SCTL_FRAG) {
+ ieee80211_sta_stop_rx_ba_session(sta->sdata, sta->sta.addr,
+ tid, 0, WLAN_REASON_QSTA_REQUIRE_SETUP);
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ if (ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb, frames))
+ return;
+
+ dont_reorder:
+ __skb_queue_tail(frames, skb);
+}
static ieee80211_rx_result debug_noinline
ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
@@ -603,7 +826,9 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
static ieee80211_rx_result debug_noinline
ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
+ struct sk_buff *skb = rx->skb;
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
int keyidx;
int hdrlen;
ieee80211_rx_result result = RX_DROP_UNUSABLE;
@@ -644,6 +869,9 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
if (!(rx->flags & IEEE80211_RX_RA_MATCH))
return RX_CONTINUE;
+ /* start without a key */
+ rx->key = NULL;
+
if (rx->sta)
stakey = rcu_dereference(rx->sta->key);
@@ -657,8 +885,8 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
return RX_CONTINUE;
} else if (mmie_keyidx >= 0) {
/* Broadcast/multicast robust management frame / BIP */
- if ((rx->status->flag & RX_FLAG_DECRYPTED) &&
- (rx->status->flag & RX_FLAG_IV_STRIPPED))
+ if ((status->flag & RX_FLAG_DECRYPTED) &&
+ (status->flag & RX_FLAG_IV_STRIPPED))
return RX_CONTINUE;
if (mmie_keyidx < NUM_DEFAULT_KEYS ||
@@ -690,8 +918,8 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
* we somehow allow the driver to tell us which key
* the hardware used if this flag is set?
*/
- if ((rx->status->flag & RX_FLAG_DECRYPTED) &&
- (rx->status->flag & RX_FLAG_IV_STRIPPED))
+ if ((status->flag & RX_FLAG_DECRYPTED) &&
+ (status->flag & RX_FLAG_IV_STRIPPED))
return RX_CONTINUE;
hdrlen = ieee80211_hdrlen(hdr->frame_control);
@@ -727,8 +955,8 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
/* Check for weak IVs if possible */
if (rx->sta && rx->key->conf.alg == ALG_WEP &&
ieee80211_is_data(hdr->frame_control) &&
- (!(rx->status->flag & RX_FLAG_IV_STRIPPED) ||
- !(rx->status->flag & RX_FLAG_DECRYPTED)) &&
+ (!(status->flag & RX_FLAG_IV_STRIPPED) ||
+ !(status->flag & RX_FLAG_DECRYPTED)) &&
ieee80211_wep_is_weak_iv(rx->skb, rx->key))
rx->sta->wep_weak_iv_count++;
@@ -748,7 +976,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
}
/* either the frame has been decrypted or will be dropped */
- rx->status->flag |= RX_FLAG_DECRYPTED;
+ status->flag |= RX_FLAG_DECRYPTED;
return result;
}
@@ -792,7 +1020,7 @@ static void ap_sta_ps_start(struct sta_info *sta)
struct ieee80211_local *local = sdata->local;
atomic_inc(&sdata->bss->num_sta_ps);
- set_sta_flags(sta, WLAN_STA_PS);
+ set_sta_flags(sta, WLAN_STA_PS_STA);
drv_sta_notify(local, &sdata->vif, STA_NOTIFY_SLEEP, &sta->sta);
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
printk(KERN_DEBUG "%s: STA %pM aid %d enters power save mode\n",
@@ -800,45 +1028,37 @@ static void ap_sta_ps_start(struct sta_info *sta)
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
}
-static int ap_sta_ps_end(struct sta_info *sta)
+static void ap_sta_ps_end(struct sta_info *sta)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
- struct ieee80211_local *local = sdata->local;
- int sent, buffered;
atomic_dec(&sdata->bss->num_sta_ps);
- clear_sta_flags(sta, WLAN_STA_PS);
- drv_sta_notify(local, &sdata->vif, STA_NOTIFY_AWAKE, &sta->sta);
-
- if (!skb_queue_empty(&sta->ps_tx_buf))
- sta_info_clear_tim_bit(sta);
+ clear_sta_flags(sta, WLAN_STA_PS_STA);
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
printk(KERN_DEBUG "%s: STA %pM aid %d exits power save mode\n",
sdata->dev->name, sta->sta.addr, sta->sta.aid);
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
- /* Send all buffered frames to the station */
- sent = ieee80211_add_pending_skbs(local, &sta->tx_filtered);
- buffered = ieee80211_add_pending_skbs(local, &sta->ps_tx_buf);
- sent += buffered;
- local->total_ps_buffered -= buffered;
-
+ if (test_sta_flags(sta, WLAN_STA_PS_DRIVER)) {
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- printk(KERN_DEBUG "%s: STA %pM aid %d sending %d filtered/%d PS frames "
- "since STA not sleeping anymore\n", sdata->dev->name,
- sta->sta.addr, sta->sta.aid, sent - buffered, buffered);
+ printk(KERN_DEBUG "%s: STA %pM aid %d driver-ps-blocked\n",
+ sdata->dev->name, sta->sta.addr, sta->sta.aid);
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
+ return;
+ }
- return sent;
+ ieee80211_sta_ps_deliver_wakeup(sta);
}
static ieee80211_rx_result debug_noinline
ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
{
struct sta_info *sta = rx->sta;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
+ struct sk_buff *skb = rx->skb;
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
if (!sta)
return RX_CONTINUE;
@@ -869,9 +1089,8 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
sta->rx_fragments++;
sta->rx_bytes += rx->skb->len;
- sta->last_signal = rx->status->signal;
- sta->last_qual = rx->status->qual;
- sta->last_noise = rx->status->noise;
+ sta->last_signal = status->signal;
+ sta->last_noise = status->noise;
/*
* Change STA power saving mode only at the end of a frame
@@ -880,7 +1099,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
if (!ieee80211_has_morefrags(hdr->frame_control) &&
(rx->sdata->vif.type == NL80211_IFTYPE_AP ||
rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)) {
- if (test_sta_flags(sta, WLAN_STA_PS)) {
+ if (test_sta_flags(sta, WLAN_STA_PS_STA)) {
/*
* Ignore doze->wake transitions that are
* indicated by non-data frames, the standard
@@ -891,19 +1110,24 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
*/
if (ieee80211_is_data(hdr->frame_control) &&
!ieee80211_has_pm(hdr->frame_control))
- rx->sent_ps_buffered += ap_sta_ps_end(sta);
+ ap_sta_ps_end(sta);
} else {
if (ieee80211_has_pm(hdr->frame_control))
ap_sta_ps_start(sta);
}
}
- /* Drop data::nullfunc frames silently, since they are used only to
- * control station power saving mode. */
- if (ieee80211_is_nullfunc(hdr->frame_control)) {
+ /*
+ * Drop (qos-)data::nullfunc frames silently, since they
+ * are used only to control station power saving mode.
+ */
+ if (ieee80211_is_nullfunc(hdr->frame_control) ||
+ ieee80211_is_qos_nullfunc(hdr->frame_control)) {
I802_DEBUG_INC(rx->local->rx_handlers_drop_nullfunc);
- /* Update counter and free packet here to avoid counting this
- * as a dropped packed. */
+ /*
+ * Update counter and free packet here to avoid
+ * counting this as a dropped packed.
+ */
sta->rx_packets++;
dev_kfree_skb(rx->skb);
return RX_QUEUED;
@@ -1103,9 +1327,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
static ieee80211_rx_result debug_noinline
ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
- struct sk_buff *skb;
- int no_pending_pkts;
+ struct ieee80211_sub_if_data *sdata = rx->sdata;
__le16 fc = ((struct ieee80211_hdr *)rx->skb->data)->frame_control;
if (likely(!rx->sta || !ieee80211_is_pspoll(fc) ||
@@ -1116,56 +1338,10 @@ ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx)
(sdata->vif.type != NL80211_IFTYPE_AP_VLAN))
return RX_DROP_UNUSABLE;
- skb = skb_dequeue(&rx->sta->tx_filtered);
- if (!skb) {
- skb = skb_dequeue(&rx->sta->ps_tx_buf);
- if (skb)
- rx->local->total_ps_buffered--;
- }
- no_pending_pkts = skb_queue_empty(&rx->sta->tx_filtered) &&
- skb_queue_empty(&rx->sta->ps_tx_buf);
-
- if (skb) {
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct ieee80211_hdr *hdr =
- (struct ieee80211_hdr *) skb->data;
-
- /*
- * Tell TX path to send this frame even though the STA may
- * still remain is PS mode after this frame exchange.
- */
- info->flags |= IEEE80211_TX_CTL_PSPOLL_RESPONSE;
-
-#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- printk(KERN_DEBUG "STA %pM aid %d: PS Poll (entries after %d)\n",
- rx->sta->sta.addr, rx->sta->sta.aid,
- skb_queue_len(&rx->sta->ps_tx_buf));
-#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
-
- /* Use MoreData flag to indicate whether there are more
- * buffered frames for this STA */
- if (no_pending_pkts)
- hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREDATA);
- else
- hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
-
- ieee80211_add_pending_skb(rx->local, skb);
-
- if (no_pending_pkts)
- sta_info_clear_tim_bit(rx->sta);
-#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- } else if (!rx->sent_ps_buffered) {
- /*
- * FIXME: This can be the result of a race condition between
- * us expiring a frame and the station polling for it.
- * Should we send it a null-func frame indicating we
- * have nothing buffered for it?
- */
- printk(KERN_DEBUG "%s: STA %pM sent PS Poll even "
- "though there are no buffered frames for it\n",
- rx->dev->name, rx->sta->sta.addr);
-#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
- }
+ if (!test_sta_flags(rx->sta, WLAN_STA_PS_DRIVER))
+ ieee80211_sta_ps_deliver_poll_response(rx->sta);
+ else
+ set_sta_flags(rx->sta, WLAN_STA_PSPOLL);
/* Free PS Poll skb here instead of returning RX_DROP that would
* count as an dropped frame. */
@@ -1206,11 +1382,14 @@ ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx)
static int
ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
{
+ struct sk_buff *skb = rx->skb;
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+
/*
* Pass through unencrypted frames if the hardware has
* decrypted them already.
*/
- if (rx->status->flag & RX_FLAG_DECRYPTED)
+ if (status->flag & RX_FLAG_DECRYPTED)
return 0;
/* Drop unencrypted frames if key is set. */
@@ -1224,8 +1403,8 @@ ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
rx->key))
return -EACCES;
/* BIP does not use Protected field, so need to check MMIE */
- if (unlikely(ieee80211_is_multicast_robust_mgmt_frame(rx->skb)
- && ieee80211_get_mmie_keyidx(rx->skb) < 0 &&
+ if (unlikely(ieee80211_is_multicast_robust_mgmt_frame(rx->skb) &&
+ ieee80211_get_mmie_keyidx(rx->skb) < 0 &&
rx->key))
return -EACCES;
/*
@@ -1244,8 +1423,18 @@ ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
static int
__ieee80211_data_to_8023(struct ieee80211_rx_data *rx)
{
- struct net_device *dev = rx->dev;
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_sub_if_data *sdata = rx->sdata;
+ struct net_device *dev = sdata->dev;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
+
+ if (ieee80211_has_a4(hdr->frame_control) &&
+ sdata->vif.type == NL80211_IFTYPE_AP_VLAN && !sdata->u.vlan.sta)
+ return -1;
+
+ if (is_multicast_ether_addr(hdr->addr1) &&
+ ((sdata->vif.type == NL80211_IFTYPE_AP_VLAN && sdata->u.vlan.sta) ||
+ (sdata->vif.type == NL80211_IFTYPE_STATION && sdata->u.mgd.use_4addr)))
+ return -1;
return ieee80211_data_to_8023(rx->skb, dev->dev_addr, sdata->vif.type);
}
@@ -1264,7 +1453,7 @@ static bool ieee80211_frame_allowed(struct ieee80211_rx_data *rx, __le16 fc)
* of whether the frame was encrypted or not.
*/
if (ehdr->h_proto == htons(ETH_P_PAE) &&
- (compare_ether_addr(ehdr->h_dest, rx->dev->dev_addr) == 0 ||
+ (compare_ether_addr(ehdr->h_dest, rx->sdata->dev->dev_addr) == 0 ||
compare_ether_addr(ehdr->h_dest, pae_group_addr) == 0))
return true;
@@ -1281,10 +1470,10 @@ static bool ieee80211_frame_allowed(struct ieee80211_rx_data *rx, __le16 fc)
static void
ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
{
- struct net_device *dev = rx->dev;
+ struct ieee80211_sub_if_data *sdata = rx->sdata;
+ struct net_device *dev = sdata->dev;
struct ieee80211_local *local = rx->local;
struct sk_buff *skb, *xmit_skb;
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ethhdr *ehdr = (struct ethhdr *) rx->skb->data;
struct sta_info *dsta;
@@ -1294,7 +1483,8 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
if ((sdata->vif.type == NL80211_IFTYPE_AP ||
sdata->vif.type == NL80211_IFTYPE_AP_VLAN) &&
!(sdata->flags & IEEE80211_SDATA_DONT_BRIDGE_PACKETS) &&
- (rx->flags & IEEE80211_RX_RA_MATCH)) {
+ (rx->flags & IEEE80211_RX_RA_MATCH) &&
+ (sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->u.vlan.sta)) {
if (is_multicast_ether_addr(ehdr->h_dest)) {
/*
* send multicast frames both to higher layers in
@@ -1337,10 +1527,10 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
skb = NULL;
} else {
u8 *data = skb->data;
- size_t len = skb->len;
- u8 *new = __skb_push(skb, align);
- memmove(new, data, len);
- __skb_trim(skb, len);
+ size_t len = skb_headlen(skb);
+ skb->data -= align;
+ memmove(skb->data, data, len);
+ skb_set_tail_pointer(skb, len);
}
}
#endif
@@ -1365,7 +1555,7 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
static ieee80211_rx_result debug_noinline
ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx)
{
- struct net_device *dev = rx->dev;
+ struct net_device *dev = rx->sdata->dev;
struct ieee80211_local *local = rx->local;
u16 ethertype;
u8 *payload;
@@ -1490,12 +1680,11 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
unsigned int hdrlen;
struct sk_buff *skb = rx->skb, *fwd_skb;
struct ieee80211_local *local = rx->local;
- struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_sub_if_data *sdata = rx->sdata;
hdr = (struct ieee80211_hdr *) skb->data;
hdrlen = ieee80211_hdrlen(hdr->frame_control);
mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen);
- sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
if (!ieee80211_is_data(hdr->frame_control))
return RX_CONTINUE;
@@ -1504,19 +1693,28 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
/* illegal frame */
return RX_DROP_MONITOR;
- if (!is_multicast_ether_addr(hdr->addr1) &&
- (mesh_hdr->flags & MESH_FLAGS_AE_A5_A6)) {
+ if (mesh_hdr->flags & MESH_FLAGS_AE) {
struct mesh_path *mppath;
+ char *proxied_addr;
+ char *mpp_addr;
+
+ if (is_multicast_ether_addr(hdr->addr1)) {
+ mpp_addr = hdr->addr3;
+ proxied_addr = mesh_hdr->eaddr1;
+ } else {
+ mpp_addr = hdr->addr4;
+ proxied_addr = mesh_hdr->eaddr2;
+ }
rcu_read_lock();
- mppath = mpp_path_lookup(mesh_hdr->eaddr2, sdata);
+ mppath = mpp_path_lookup(proxied_addr, sdata);
if (!mppath) {
- mpp_path_add(mesh_hdr->eaddr2, hdr->addr4, sdata);
+ mpp_path_add(proxied_addr, mpp_addr, sdata);
} else {
spin_lock_bh(&mppath->state_lock);
mppath->exp_time = jiffies;
- if (compare_ether_addr(mppath->mpp, hdr->addr4) != 0)
- memcpy(mppath->mpp, hdr->addr4, ETH_ALEN);
+ if (compare_ether_addr(mppath->mpp, mpp_addr) != 0)
+ memcpy(mppath->mpp, mpp_addr, ETH_ALEN);
spin_unlock_bh(&mppath->state_lock);
}
rcu_read_unlock();
@@ -1524,7 +1722,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
/* Frame has reached destination. Don't forward */
if (!is_multicast_ether_addr(hdr->addr1) &&
- compare_ether_addr(rx->dev->dev_addr, hdr->addr3) == 0)
+ compare_ether_addr(sdata->dev->dev_addr, hdr->addr3) == 0)
return RX_CONTINUE;
mesh_hdr->ttl--;
@@ -1541,10 +1739,10 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
if (!fwd_skb && net_ratelimit())
printk(KERN_DEBUG "%s: failed to clone mesh frame\n",
- rx->dev->name);
+ sdata->dev->name);
fwd_hdr = (struct ieee80211_hdr *) fwd_skb->data;
- memcpy(fwd_hdr->addr2, rx->dev->dev_addr, ETH_ALEN);
+ memcpy(fwd_hdr->addr2, sdata->dev->dev_addr, ETH_ALEN);
info = IEEE80211_SKB_CB(fwd_skb);
memset(info, 0, sizeof(*info));
info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
@@ -1578,7 +1776,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
}
if (is_multicast_ether_addr(hdr->addr1) ||
- rx->dev->flags & IFF_PROMISC)
+ sdata->dev->flags & IFF_PROMISC)
return RX_CONTINUE;
else
return RX_DROP_MONITOR;
@@ -1588,7 +1786,8 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
static ieee80211_rx_result debug_noinline
ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
{
- struct net_device *dev = rx->dev;
+ struct ieee80211_sub_if_data *sdata = rx->sdata;
+ struct net_device *dev = sdata->dev;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
__le16 fc = hdr->frame_control;
int err;
@@ -1599,6 +1798,14 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
return RX_DROP_MONITOR;
+ /*
+ * Allow the cooked monitor interface of an AP to see 4-addr frames so
+ * that a 4-addr station can be detected and moved into a separate VLAN
+ */
+ if (ieee80211_has_a4(hdr->frame_control) &&
+ sdata->vif.type == NL80211_IFTYPE_AP)
+ return RX_DROP_MONITOR;
+
err = __ieee80211_data_to_8023(rx);
if (unlikely(err))
return RX_DROP_UNUSABLE;
@@ -1617,7 +1824,7 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
}
static ieee80211_rx_result debug_noinline
-ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx)
+ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx, struct sk_buff_head *frames)
{
struct ieee80211_local *local = rx->local;
struct ieee80211_hw *hw = &local->hw;
@@ -1632,11 +1839,11 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx)
if (ieee80211_is_back_req(bar->frame_control)) {
if (!rx->sta)
- return RX_CONTINUE;
+ return RX_DROP_MONITOR;
tid = le16_to_cpu(bar->control) >> 12;
if (rx->sta->ampdu_mlme.tid_state_rx[tid]
!= HT_AGG_STATE_OPERATIONAL)
- return RX_CONTINUE;
+ return RX_DROP_MONITOR;
tid_agg_rx = rx->sta->ampdu_mlme.tid_rx[tid];
start_seq_num = le16_to_cpu(bar->start_seq_num) >> 4;
@@ -1646,13 +1853,11 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx)
mod_timer(&tid_agg_rx->session_timer,
TU_TO_EXP_TIME(tid_agg_rx->timeout));
- /* manage reordering buffer according to requested */
- /* sequence number */
- rcu_read_lock();
- ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, NULL,
- start_seq_num, 1);
- rcu_read_unlock();
- return RX_DROP_UNUSABLE;
+ /* release stored frames up to start of BAR */
+ ieee80211_release_reorder_frames(hw, tid_agg_rx, start_seq_num,
+ frames);
+ kfree_skb(skb);
+ return RX_QUEUED;
}
return RX_CONTINUE;
@@ -1701,14 +1906,14 @@ static void ieee80211_process_sa_query_req(struct ieee80211_sub_if_data *sdata,
mgmt->u.action.u.sa_query.trans_id,
WLAN_SA_QUERY_TR_ID_LEN);
- ieee80211_tx_skb(sdata, skb, 1);
+ ieee80211_tx_skb(sdata, skb);
}
static ieee80211_rx_result debug_noinline
ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
{
struct ieee80211_local *local = rx->local;
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
+ struct ieee80211_sub_if_data *sdata = rx->sdata;
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data;
int len = rx->skb->len;
@@ -1820,7 +2025,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
static ieee80211_rx_result debug_noinline
ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
+ struct ieee80211_sub_if_data *sdata = rx->sdata;
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data;
if (!(rx->flags & IEEE80211_RX_RA_MATCH))
@@ -1858,11 +2063,11 @@ static void ieee80211_rx_michael_mic_report(struct ieee80211_hdr *hdr,
* Some hardware seem to generate incorrect Michael MIC
* reports; ignore them to avoid triggering countermeasures.
*/
- goto ignore;
+ return;
}
if (!ieee80211_has_protected(hdr->frame_control))
- goto ignore;
+ return;
if (rx->sdata->vif.type == NL80211_IFTYPE_AP && keyidx) {
/*
@@ -1871,37 +2076,35 @@ static void ieee80211_rx_michael_mic_report(struct ieee80211_hdr *hdr,
* group keys and only the AP is sending real multicast
* frames in the BSS.
*/
- goto ignore;
+ return;
}
if (!ieee80211_is_data(hdr->frame_control) &&
!ieee80211_is_auth(hdr->frame_control))
- goto ignore;
+ return;
mac80211_ev_michael_mic_failure(rx->sdata, keyidx, hdr, NULL,
GFP_ATOMIC);
- ignore:
- dev_kfree_skb(rx->skb);
- rx->skb = NULL;
}
/* TODO: use IEEE80211_RX_FRAGMENTED */
-static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx)
+static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
+ struct ieee80211_rate *rate)
{
struct ieee80211_sub_if_data *sdata;
struct ieee80211_local *local = rx->local;
struct ieee80211_rtap_hdr {
struct ieee80211_radiotap_header hdr;
u8 flags;
- u8 rate;
+ u8 rate_or_pad;
__le16 chan_freq;
__le16 chan_flags;
} __attribute__ ((packed)) *rthdr;
struct sk_buff *skb = rx->skb, *skb2;
struct net_device *prev_dev = NULL;
- struct ieee80211_rx_status *status = rx->status;
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
- if (rx->flags & IEEE80211_RX_CMNTR_REPORTED)
+ if (status->flag & RX_FLAG_INTERNAL_CMTR)
goto out_free_skb;
if (skb_headroom(skb) < sizeof(*rthdr) &&
@@ -1913,10 +2116,13 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx)
rthdr->hdr.it_len = cpu_to_le16(sizeof(*rthdr));
rthdr->hdr.it_present =
cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
- (1 << IEEE80211_RADIOTAP_RATE) |
(1 << IEEE80211_RADIOTAP_CHANNEL));
- rthdr->rate = rx->rate->bitrate / 5;
+ if (rate) {
+ rthdr->rate_or_pad = rate->bitrate / 5;
+ rthdr->hdr.it_present |=
+ cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
+ }
rthdr->chan_freq = cpu_to_le16(status->freq);
if (status->band == IEEE80211_BAND_5GHZ)
@@ -1959,7 +2165,7 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx)
} else
goto out_free_skb;
- rx->flags |= IEEE80211_RX_CMNTR_REPORTED;
+ status->flag |= RX_FLAG_INTERNAL_CMTR;
return;
out_free_skb:
@@ -1969,62 +2175,87 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx)
static void ieee80211_invoke_rx_handlers(struct ieee80211_sub_if_data *sdata,
struct ieee80211_rx_data *rx,
- struct sk_buff *skb)
+ struct sk_buff *skb,
+ struct ieee80211_rate *rate)
{
+ struct sk_buff_head reorder_release;
ieee80211_rx_result res = RX_DROP_MONITOR;
+ __skb_queue_head_init(&reorder_release);
+
rx->skb = skb;
rx->sdata = sdata;
- rx->dev = sdata->dev;
#define CALL_RXH(rxh) \
do { \
res = rxh(rx); \
if (res != RX_CONTINUE) \
- goto rxh_done; \
+ goto rxh_next; \
} while (0);
+ /*
+ * NB: the rxh_next label works even if we jump
+ * to it from here because then the list will
+ * be empty, which is a trivial check
+ */
CALL_RXH(ieee80211_rx_h_passive_scan)
CALL_RXH(ieee80211_rx_h_check)
- CALL_RXH(ieee80211_rx_h_decrypt)
- CALL_RXH(ieee80211_rx_h_check_more_data)
- CALL_RXH(ieee80211_rx_h_sta_process)
- CALL_RXH(ieee80211_rx_h_defragment)
- CALL_RXH(ieee80211_rx_h_ps_poll)
- CALL_RXH(ieee80211_rx_h_michael_mic_verify)
- /* must be after MMIC verify so header is counted in MPDU mic */
- CALL_RXH(ieee80211_rx_h_remove_qos_control)
- CALL_RXH(ieee80211_rx_h_amsdu)
+
+ ieee80211_rx_reorder_ampdu(rx, &reorder_release);
+
+ while ((skb = __skb_dequeue(&reorder_release))) {
+ /*
+ * all the other fields are valid across frames
+ * that belong to an aMPDU since they are on the
+ * same TID from the same station
+ */
+ rx->skb = skb;
+
+ CALL_RXH(ieee80211_rx_h_decrypt)
+ CALL_RXH(ieee80211_rx_h_check_more_data)
+ CALL_RXH(ieee80211_rx_h_sta_process)
+ CALL_RXH(ieee80211_rx_h_defragment)
+ CALL_RXH(ieee80211_rx_h_ps_poll)
+ CALL_RXH(ieee80211_rx_h_michael_mic_verify)
+ /* must be after MMIC verify so header is counted in MPDU mic */
+ CALL_RXH(ieee80211_rx_h_remove_qos_control)
+ CALL_RXH(ieee80211_rx_h_amsdu)
#ifdef CONFIG_MAC80211_MESH
- if (ieee80211_vif_is_mesh(&sdata->vif))
- CALL_RXH(ieee80211_rx_h_mesh_fwding);
+ if (ieee80211_vif_is_mesh(&sdata->vif))
+ CALL_RXH(ieee80211_rx_h_mesh_fwding);
#endif
- CALL_RXH(ieee80211_rx_h_data)
- CALL_RXH(ieee80211_rx_h_ctrl)
- CALL_RXH(ieee80211_rx_h_action)
- CALL_RXH(ieee80211_rx_h_mgmt)
+ CALL_RXH(ieee80211_rx_h_data)
+
+ /* special treatment -- needs the queue */
+ res = ieee80211_rx_h_ctrl(rx, &reorder_release);
+ if (res != RX_CONTINUE)
+ goto rxh_next;
+
+ CALL_RXH(ieee80211_rx_h_action)
+ CALL_RXH(ieee80211_rx_h_mgmt)
#undef CALL_RXH
- rxh_done:
- switch (res) {
- case RX_DROP_MONITOR:
- I802_DEBUG_INC(sdata->local->rx_handlers_drop);
- if (rx->sta)
- rx->sta->rx_dropped++;
- /* fall through */
- case RX_CONTINUE:
- ieee80211_rx_cooked_monitor(rx);
- break;
- case RX_DROP_UNUSABLE:
- I802_DEBUG_INC(sdata->local->rx_handlers_drop);
- if (rx->sta)
- rx->sta->rx_dropped++;
- dev_kfree_skb(rx->skb);
- break;
- case RX_QUEUED:
- I802_DEBUG_INC(sdata->local->rx_handlers_queued);
- break;
+ rxh_next:
+ switch (res) {
+ case RX_DROP_MONITOR:
+ I802_DEBUG_INC(sdata->local->rx_handlers_drop);
+ if (rx->sta)
+ rx->sta->rx_dropped++;
+ /* fall through */
+ case RX_CONTINUE:
+ ieee80211_rx_cooked_monitor(rx, rate);
+ break;
+ case RX_DROP_UNUSABLE:
+ I802_DEBUG_INC(sdata->local->rx_handlers_drop);
+ if (rx->sta)
+ rx->sta->rx_dropped++;
+ dev_kfree_skb(rx->skb);
+ break;
+ case RX_QUEUED:
+ I802_DEBUG_INC(sdata->local->rx_handlers_queued);
+ break;
+ }
}
}
@@ -2034,12 +2265,14 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
struct ieee80211_rx_data *rx,
struct ieee80211_hdr *hdr)
{
- u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len, sdata->vif.type);
+ struct sk_buff *skb = rx->skb;
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+ u8 *bssid = ieee80211_get_bssid(hdr, skb->len, sdata->vif.type);
int multicast = is_multicast_ether_addr(hdr->addr1);
switch (sdata->vif.type) {
case NL80211_IFTYPE_STATION:
- if (!bssid)
+ if (!bssid && !sdata->u.mgd.use_4addr)
return 0;
if (!multicast &&
compare_ether_addr(sdata->dev->dev_addr, hdr->addr1) != 0) {
@@ -2066,10 +2299,10 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
rx->flags &= ~IEEE80211_RX_RA_MATCH;
} else if (!rx->sta) {
int rate_idx;
- if (rx->status->flag & RX_FLAG_HT)
+ if (status->flag & RX_FLAG_HT)
rate_idx = 0; /* TODO: HT rates */
else
- rate_idx = rx->status->rate_idx;
+ rate_idx = status->rate_idx;
rx->sta = ieee80211_ibss_add_sta(sdata, bssid, hdr->addr2,
BIT(rate_idx));
}
@@ -2104,8 +2337,6 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
return 0;
break;
case NL80211_IFTYPE_MONITOR:
- /* take everything */
- break;
case NL80211_IFTYPE_UNSPECIFIED:
case __NL80211_IFTYPE_AFTER_LAST:
/* should never get here */
@@ -2138,23 +2369,9 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
rx.skb = skb;
rx.local = local;
- rx.status = status;
- rx.rate = rate;
-
if (ieee80211_is_data(hdr->frame_control) || ieee80211_is_mgmt(hdr->frame_control))
local->dot11ReceivedFragmentCount++;
- rx.sta = sta_info_get(local, hdr->addr2);
- if (rx.sta) {
- rx.sdata = rx.sta->sdata;
- rx.dev = rx.sta->sdata->dev;
- }
-
- if ((status->flag & RX_FLAG_MMIC_ERROR)) {
- ieee80211_rx_michael_mic_report(hdr, &rx);
- return;
- }
-
if (unlikely(test_bit(SCAN_HW_SCANNING, &local->scanning) ||
test_bit(SCAN_OFF_CHANNEL, &local->scanning)))
rx.flags |= IEEE80211_RX_IN_SCAN;
@@ -2162,13 +2379,20 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
ieee80211_parse_qos(&rx);
ieee80211_verify_alignment(&rx);
- skb = rx.skb;
+ rx.sta = sta_info_get(local, hdr->addr2);
+ if (rx.sta)
+ rx.sdata = rx.sta->sdata;
if (rx.sdata && ieee80211_is_data(hdr->frame_control)) {
rx.flags |= IEEE80211_RX_RA_MATCH;
prepares = prepare_for_handlers(rx.sdata, &rx, hdr);
- if (prepares)
- prev = rx.sdata;
+ if (prepares) {
+ if (status->flag & RX_FLAG_MMIC_ERROR) {
+ if (rx.flags & IEEE80211_RX_RA_MATCH)
+ ieee80211_rx_michael_mic_report(hdr, &rx);
+ } else
+ prev = rx.sdata;
+ }
} else list_for_each_entry_rcu(sdata, &local->interfaces, list) {
if (!netif_running(sdata->dev))
continue;
@@ -2183,6 +2407,13 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
if (!prepares)
continue;
+ if (status->flag & RX_FLAG_MMIC_ERROR) {
+ rx.sdata = sdata;
+ if (rx.flags & IEEE80211_RX_RA_MATCH)
+ ieee80211_rx_michael_mic_report(hdr, &rx);
+ continue;
+ }
+
/*
* frame is destined for this interface, but if it's not
* also for the previous one we handle that after the
@@ -2208,240 +2439,15 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
prev->dev->name);
continue;
}
- ieee80211_invoke_rx_handlers(prev, &rx, skb_new);
+ ieee80211_invoke_rx_handlers(prev, &rx, skb_new, rate);
prev = sdata;
}
if (prev)
- ieee80211_invoke_rx_handlers(prev, &rx, skb);
+ ieee80211_invoke_rx_handlers(prev, &rx, skb, rate);
else
dev_kfree_skb(skb);
}
-#define SEQ_MODULO 0x1000
-#define SEQ_MASK 0xfff
-
-static inline int seq_less(u16 sq1, u16 sq2)
-{
- return ((sq1 - sq2) & SEQ_MASK) > (SEQ_MODULO >> 1);
-}
-
-static inline u16 seq_inc(u16 sq)
-{
- return (sq + 1) & SEQ_MASK;
-}
-
-static inline u16 seq_sub(u16 sq1, u16 sq2)
-{
- return (sq1 - sq2) & SEQ_MASK;
-}
-
-
-static void ieee80211_release_reorder_frame(struct ieee80211_hw *hw,
- struct tid_ampdu_rx *tid_agg_rx,
- int index)
-{
- struct ieee80211_supported_band *sband;
- struct ieee80211_rate *rate;
- struct sk_buff *skb = tid_agg_rx->reorder_buf[index];
- struct ieee80211_rx_status *status;
-
- if (!skb)
- goto no_frame;
-
- status = IEEE80211_SKB_RXCB(skb);
-
- /* release the reordered frames to stack */
- sband = hw->wiphy->bands[status->band];
- if (status->flag & RX_FLAG_HT)
- rate = sband->bitrates; /* TODO: HT rates */
- else
- rate = &sband->bitrates[status->rate_idx];
- __ieee80211_rx_handle_packet(hw, skb, rate);
- tid_agg_rx->stored_mpdu_num--;
- tid_agg_rx->reorder_buf[index] = NULL;
-
-no_frame:
- tid_agg_rx->head_seq_num = seq_inc(tid_agg_rx->head_seq_num);
-}
-
-
-/*
- * Timeout (in jiffies) for skb's that are waiting in the RX reorder buffer. If
- * the skb was added to the buffer longer than this time ago, the earlier
- * frames that have not yet been received are assumed to be lost and the skb
- * can be released for processing. This may also release other skb's from the
- * reorder buffer if there are no additional gaps between the frames.
- */
-#define HT_RX_REORDER_BUF_TIMEOUT (HZ / 10)
-
-/*
- * As it function blongs to Rx path it must be called with
- * the proper rcu_read_lock protection for its flow.
- */
-static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
- struct tid_ampdu_rx *tid_agg_rx,
- struct sk_buff *skb,
- u16 mpdu_seq_num,
- int bar_req)
-{
- u16 head_seq_num, buf_size;
- int index;
-
- buf_size = tid_agg_rx->buf_size;
- head_seq_num = tid_agg_rx->head_seq_num;
-
- /* frame with out of date sequence number */
- if (seq_less(mpdu_seq_num, head_seq_num)) {
- dev_kfree_skb(skb);
- return 1;
- }
-
- /* if frame sequence number exceeds our buffering window size or
- * block Ack Request arrived - release stored frames */
- if ((!seq_less(mpdu_seq_num, head_seq_num + buf_size)) || (bar_req)) {
- /* new head to the ordering buffer */
- if (bar_req)
- head_seq_num = mpdu_seq_num;
- else
- head_seq_num =
- seq_inc(seq_sub(mpdu_seq_num, buf_size));
- /* release stored frames up to new head to stack */
- while (seq_less(tid_agg_rx->head_seq_num, head_seq_num)) {
- index = seq_sub(tid_agg_rx->head_seq_num,
- tid_agg_rx->ssn)
- % tid_agg_rx->buf_size;
- ieee80211_release_reorder_frame(hw, tid_agg_rx,
- index);
- }
- if (bar_req)
- return 1;
- }
-
- /* now the new frame is always in the range of the reordering */
- /* buffer window */
- index = seq_sub(mpdu_seq_num, tid_agg_rx->ssn)
- % tid_agg_rx->buf_size;
- /* check if we already stored this frame */
- if (tid_agg_rx->reorder_buf[index]) {
- dev_kfree_skb(skb);
- return 1;
- }
-
- /* if arrived mpdu is in the right order and nothing else stored */
- /* release it immediately */
- if (mpdu_seq_num == tid_agg_rx->head_seq_num &&
- tid_agg_rx->stored_mpdu_num == 0) {
- tid_agg_rx->head_seq_num =
- seq_inc(tid_agg_rx->head_seq_num);
- return 0;
- }
-
- /* put the frame in the reordering buffer */
- tid_agg_rx->reorder_buf[index] = skb;
- tid_agg_rx->reorder_time[index] = jiffies;
- tid_agg_rx->stored_mpdu_num++;
- /* release the buffer until next missing frame */
- index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn)
- % tid_agg_rx->buf_size;
- if (!tid_agg_rx->reorder_buf[index] &&
- tid_agg_rx->stored_mpdu_num > 1) {
- /*
- * No buffers ready to be released, but check whether any
- * frames in the reorder buffer have timed out.
- */
- int j;
- int skipped = 1;
- for (j = (index + 1) % tid_agg_rx->buf_size; j != index;
- j = (j + 1) % tid_agg_rx->buf_size) {
- if (tid_agg_rx->reorder_buf[j] == NULL) {
- skipped++;
- continue;
- }
- if (!time_after(jiffies, tid_agg_rx->reorder_time[j] +
- HZ / 10))
- break;
-
-#ifdef CONFIG_MAC80211_HT_DEBUG
- if (net_ratelimit())
- printk(KERN_DEBUG "%s: release an RX reorder "
- "frame due to timeout on earlier "
- "frames\n",
- wiphy_name(hw->wiphy));
-#endif
- ieee80211_release_reorder_frame(hw, tid_agg_rx, j);
-
- /*
- * Increment the head seq# also for the skipped slots.
- */
- tid_agg_rx->head_seq_num =
- (tid_agg_rx->head_seq_num + skipped) &
- SEQ_MASK;
- skipped = 0;
- }
- } else while (tid_agg_rx->reorder_buf[index]) {
- ieee80211_release_reorder_frame(hw, tid_agg_rx, index);
- index = seq_sub(tid_agg_rx->head_seq_num,
- tid_agg_rx->ssn) % tid_agg_rx->buf_size;
- }
- return 1;
-}
-
-static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local,
- struct sk_buff *skb)
-{
- struct ieee80211_hw *hw = &local->hw;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- struct sta_info *sta;
- struct tid_ampdu_rx *tid_agg_rx;
- u16 sc;
- u16 mpdu_seq_num;
- u8 ret = 0;
- int tid;
-
- sta = sta_info_get(local, hdr->addr2);
- if (!sta)
- return ret;
-
- /* filter the QoS data rx stream according to
- * STA/TID and check if this STA/TID is on aggregation */
- if (!ieee80211_is_data_qos(hdr->frame_control))
- goto end_reorder;
-
- tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
-
- if (sta->ampdu_mlme.tid_state_rx[tid] != HT_AGG_STATE_OPERATIONAL)
- goto end_reorder;
-
- tid_agg_rx = sta->ampdu_mlme.tid_rx[tid];
-
- /* qos null data frames are excluded */
- if (unlikely(hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_NULLFUNC)))
- goto end_reorder;
-
- /* new un-ordered ampdu frame - process it */
-
- /* reset session timer */
- if (tid_agg_rx->timeout)
- mod_timer(&tid_agg_rx->session_timer,
- TU_TO_EXP_TIME(tid_agg_rx->timeout));
-
- /* if this mpdu is fragmented - terminate rx aggregation session */
- sc = le16_to_cpu(hdr->seq_ctrl);
- if (sc & IEEE80211_SCTL_FRAG) {
- ieee80211_sta_stop_rx_ba_session(sta->sdata, sta->sta.addr,
- tid, 0, WLAN_REASON_QSTA_REQUIRE_SETUP);
- ret = 1;
- goto end_reorder;
- }
-
- /* according to mpdu sequence number deal with reordering buffer */
- mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4;
- ret = ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb,
- mpdu_seq_num, 0);
- end_reorder:
- return ret;
-}
-
/*
* This is the receive path handler. It is called by a low level driver when an
* 802.11 MPDU is received from the hardware.
@@ -2481,14 +2487,22 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
goto drop;
if (status->flag & RX_FLAG_HT) {
- /* rate_idx is MCS index */
- if (WARN_ON(status->rate_idx < 0 ||
- status->rate_idx >= 76))
+ /*
+ * rate_idx is MCS index, which can be [0-76] as documented on:
+ *
+ * http://wireless.kernel.org/en/developers/Documentation/ieee80211/802.11n
+ *
+ * Anything else would be some sort of driver or hardware error.
+ * The driver should catch hardware errors.
+ */
+ if (WARN((status->rate_idx < 0 ||
+ status->rate_idx > 76),
+ "Rate marked as an HT rate but passed "
+ "status->rate_idx is not "
+ "an MCS index [0-76]: %d (0x%02x)\n",
+ status->rate_idx,
+ status->rate_idx))
goto drop;
- /* HT rates are not in the table - use the highest legacy rate
- * for now since other parts of mac80211 may not yet be fully
- * MCS aware. */
- rate = &sband->bitrates[sband->n_bitrates - 1];
} else {
if (WARN_ON(status->rate_idx < 0 ||
status->rate_idx >= sband->n_bitrates))
@@ -2515,20 +2529,7 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
return;
}
- /*
- * In theory, the block ack reordering should happen after duplicate
- * removal (ieee80211_rx_h_check(), which is an RX handler). As such,
- * the call to ieee80211_rx_reorder_ampdu() should really be moved to
- * happen as a new RX handler between ieee80211_rx_h_check and
- * ieee80211_rx_h_decrypt. This cleanup may eventually happen, but for
- * the time being, the call can be here since RX reorder buf processing
- * will implicitly skip duplicates. We could, in theory at least,
- * process frames that ieee80211_rx_h_passive_scan would drop (e.g.,
- * frames from other than operational channel), but that should not
- * happen in normal networks.
- */
- if (!ieee80211_rx_reorder_ampdu(local, skb))
- __ieee80211_rx_handle_packet(hw, skb, rate);
+ __ieee80211_rx_handle_packet(hw, skb, rate);
rcu_read_unlock();
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 71e10cabf81..4cf387c944b 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -12,8 +12,6 @@
* published by the Free Software Foundation.
*/
-/* TODO: figure out how to avoid that the "current BSS" expires */
-
#include <linux/wireless.h>
#include <linux/if_arp.h>
#include <linux/rtnetlink.h>
@@ -189,6 +187,39 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
return RX_QUEUED;
}
+/* return false if no more work */
+static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
+{
+ struct cfg80211_scan_request *req = local->scan_req;
+ enum ieee80211_band band;
+ int i, ielen, n_chans;
+
+ do {
+ if (local->hw_scan_band == IEEE80211_NUM_BANDS)
+ return false;
+
+ band = local->hw_scan_band;
+ n_chans = 0;
+ for (i = 0; i < req->n_channels; i++) {
+ if (req->channels[i]->band == band) {
+ local->hw_scan_req->channels[n_chans] =
+ req->channels[i];
+ n_chans++;
+ }
+ }
+
+ local->hw_scan_band++;
+ } while (!n_chans);
+
+ local->hw_scan_req->n_channels = n_chans;
+
+ ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie,
+ req->ie, req->ie_len, band);
+ local->hw_scan_req->ie_len = ielen;
+
+ return true;
+}
+
/*
* inform AP that we will go to sleep so that it will buffer the frames
* while we scan
@@ -249,13 +280,6 @@ static void ieee80211_scan_ps_disable(struct ieee80211_sub_if_data *sdata)
}
}
-static void ieee80211_restore_scan_ies(struct ieee80211_local *local)
-{
- kfree(local->scan_req->ie);
- local->scan_req->ie = local->orig_ies;
- local->scan_req->ie_len = local->orig_ies_len;
-}
-
void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
{
struct ieee80211_local *local = hw_to_local(hw);
@@ -264,25 +288,36 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
mutex_lock(&local->scan_mtx);
- if (WARN_ON(!local->scanning)) {
+ /*
+ * It's ok to abort a not-yet-running scan (that
+ * we have one at all will be verified by checking
+ * local->scan_req next), but not to complete it
+ * successfully.
+ */
+ if (WARN_ON(!local->scanning && !aborted))
+ aborted = true;
+
+ if (WARN_ON(!local->scan_req)) {
mutex_unlock(&local->scan_mtx);
return;
}
- if (WARN_ON(!local->scan_req)) {
+ was_hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning);
+ if (was_hw_scan && !aborted && ieee80211_prep_hw_scan(local)) {
+ ieee80211_queue_delayed_work(&local->hw,
+ &local->scan_work, 0);
mutex_unlock(&local->scan_mtx);
return;
}
- if (test_bit(SCAN_HW_SCANNING, &local->scanning))
- ieee80211_restore_scan_ies(local);
+ kfree(local->hw_scan_req);
+ local->hw_scan_req = NULL;
if (local->scan_req != local->int_scan_req)
cfg80211_scan_done(local->scan_req, aborted);
local->scan_req = NULL;
local->scan_sdata = NULL;
- was_hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning);
local->scanning = 0;
local->scan_channel = NULL;
@@ -306,10 +341,10 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
if (sdata->u.mgd.associated) {
ieee80211_scan_ps_disable(sdata);
- netif_tx_wake_all_queues(sdata->dev);
+ netif_wake_queue(sdata->dev);
}
} else
- netif_tx_wake_all_queues(sdata->dev);
+ netif_wake_queue(sdata->dev);
/* re-enable beaconing */
if (sdata->vif.type == NL80211_IFTYPE_AP ||
@@ -364,7 +399,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
* are handled in the scan state machine
*/
if (sdata->vif.type != NL80211_IFTYPE_STATION)
- netif_tx_stop_all_queues(sdata->dev);
+ netif_stop_queue(sdata->dev);
}
mutex_unlock(&local->iflist_mtx);
@@ -394,19 +429,23 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
if (local->ops->hw_scan) {
u8 *ies;
- int ielen;
- ies = kmalloc(2 + IEEE80211_MAX_SSID_LEN +
- local->scan_ies_len + req->ie_len, GFP_KERNEL);
- if (!ies)
+ local->hw_scan_req = kmalloc(
+ sizeof(*local->hw_scan_req) +
+ req->n_channels * sizeof(req->channels[0]) +
+ 2 + IEEE80211_MAX_SSID_LEN + local->scan_ies_len +
+ req->ie_len, GFP_KERNEL);
+ if (!local->hw_scan_req)
return -ENOMEM;
- ielen = ieee80211_build_preq_ies(local, ies,
- req->ie, req->ie_len);
- local->orig_ies = req->ie;
- local->orig_ies_len = req->ie_len;
- req->ie = ies;
- req->ie_len = ielen;
+ local->hw_scan_req->ssids = req->ssids;
+ local->hw_scan_req->n_ssids = req->n_ssids;
+ ies = (u8 *)local->hw_scan_req +
+ sizeof(*local->hw_scan_req) +
+ req->n_channels * sizeof(req->channels[0]);
+ local->hw_scan_req->ie = ies;
+
+ local->hw_scan_band = 0;
}
local->scan_req = req;
@@ -438,16 +477,17 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
ieee80211_recalc_idle(local);
mutex_unlock(&local->scan_mtx);
- if (local->ops->hw_scan)
- rc = drv_hw_scan(local, local->scan_req);
- else
+ if (local->ops->hw_scan) {
+ WARN_ON(!ieee80211_prep_hw_scan(local));
+ rc = drv_hw_scan(local, local->hw_scan_req);
+ } else
rc = ieee80211_start_sw_scan(local);
mutex_lock(&local->scan_mtx);
if (rc) {
- if (local->ops->hw_scan)
- ieee80211_restore_scan_ies(local);
+ kfree(local->hw_scan_req);
+ local->hw_scan_req = NULL;
local->scanning = 0;
ieee80211_recalc_idle(local);
@@ -523,7 +563,7 @@ static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *loca
continue;
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
- netif_tx_stop_all_queues(sdata->dev);
+ netif_stop_queue(sdata->dev);
if (sdata->u.mgd.associated)
ieee80211_scan_ps_enable(sdata);
}
@@ -558,7 +598,7 @@ static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *loca
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
if (sdata->u.mgd.associated)
ieee80211_scan_ps_disable(sdata);
- netif_tx_wake_all_queues(sdata->dev);
+ netif_wake_queue(sdata->dev);
}
}
mutex_unlock(&local->iflist_mtx);
@@ -574,23 +614,14 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
{
int skip;
struct ieee80211_channel *chan;
- struct ieee80211_sub_if_data *sdata = local->scan_sdata;
skip = 0;
chan = local->scan_req->channels[local->scan_channel_idx];
- if (chan->flags & IEEE80211_CHAN_DISABLED ||
- (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
- chan->flags & IEEE80211_CHAN_NO_IBSS))
+ local->scan_channel = chan;
+ if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL))
skip = 1;
- if (!skip) {
- local->scan_channel = chan;
- if (ieee80211_hw_config(local,
- IEEE80211_CONF_CHANGE_CHANNEL))
- skip = 1;
- }
-
/* advance state machine to next channel/band */
local->scan_channel_idx++;
@@ -656,6 +687,14 @@ void ieee80211_scan_work(struct work_struct *work)
return;
}
+ if (local->hw_scan_req) {
+ int rc = drv_hw_scan(local, local->hw_scan_req);
+ mutex_unlock(&local->scan_mtx);
+ if (rc)
+ ieee80211_scan_completed(&local->hw, true);
+ return;
+ }
+
if (local->scan_req && !local->scanning) {
struct cfg80211_scan_request *req = local->scan_req;
int rc;
diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c
index 68953033403..aa743a895cf 100644
--- a/net/mac80211/spectmgmt.c
+++ b/net/mac80211/spectmgmt.c
@@ -65,7 +65,7 @@ static void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_da
IEEE80211_SPCT_MSR_RPRT_MODE_REFUSED;
msr_report->u.action.u.measurement.msr_elem.type = request_ie->type;
- ieee80211_tx_skb(sdata, skb, 1);
+ ieee80211_tx_skb(sdata, skb);
}
void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 594f2318c3d..71f370dd24b 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -116,14 +116,15 @@ struct sta_info *sta_info_get(struct ieee80211_local *local, const u8 *addr)
return sta;
}
-struct sta_info *sta_info_get_by_idx(struct ieee80211_local *local, int idx,
- struct net_device *dev)
+struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata,
+ int idx)
{
+ struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
int i = 0;
list_for_each_entry_rcu(sta, &local->sta_list, list) {
- if (dev && dev != sta->sdata->dev)
+ if (sdata != sta->sdata)
continue;
if (i < idx) {
++i;
@@ -147,8 +148,10 @@ struct sta_info *sta_info_get_by_idx(struct ieee80211_local *local, int idx,
static void __sta_info_free(struct ieee80211_local *local,
struct sta_info *sta)
{
- rate_control_free_sta(sta);
- rate_control_put(sta->rate_ctrl);
+ if (sta->rate_ctrl) {
+ rate_control_free_sta(sta);
+ rate_control_put(sta->rate_ctrl);
+ }
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
printk(KERN_DEBUG "%s: Destroyed STA %pM\n",
@@ -171,6 +174,8 @@ void sta_info_destroy(struct sta_info *sta)
local = sta->local;
+ cancel_work_sync(&sta->drv_unblock_wk);
+
rate_control_remove_sta_debugfs(sta);
ieee80211_sta_debugfs_remove(sta);
@@ -259,6 +264,38 @@ static void sta_info_hash_add(struct ieee80211_local *local,
rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta);
}
+static void sta_unblock(struct work_struct *wk)
+{
+ struct sta_info *sta;
+
+ sta = container_of(wk, struct sta_info, drv_unblock_wk);
+
+ if (sta->dead)
+ return;
+
+ if (!test_sta_flags(sta, WLAN_STA_PS_STA))
+ ieee80211_sta_ps_deliver_wakeup(sta);
+ else if (test_and_clear_sta_flags(sta, WLAN_STA_PSPOLL))
+ ieee80211_sta_ps_deliver_poll_response(sta);
+}
+
+static int sta_prepare_rate_control(struct ieee80211_local *local,
+ struct sta_info *sta, gfp_t gfp)
+{
+ if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL)
+ return 0;
+
+ sta->rate_ctrl = rate_control_get(local->rate_ctrl);
+ sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl,
+ &sta->sta, gfp);
+ if (!sta->rate_ctrl_priv) {
+ rate_control_put(sta->rate_ctrl);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
u8 *addr, gfp_t gfp)
{
@@ -272,16 +309,13 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
spin_lock_init(&sta->lock);
spin_lock_init(&sta->flaglock);
+ INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
memcpy(sta->sta.addr, addr, ETH_ALEN);
sta->local = local;
sta->sdata = sdata;
- sta->rate_ctrl = rate_control_get(local->rate_ctrl);
- sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl,
- &sta->sta, gfp);
- if (!sta->rate_ctrl_priv) {
- rate_control_put(sta->rate_ctrl);
+ if (sta_prepare_rate_control(local, sta, gfp)) {
kfree(sta);
return NULL;
}
@@ -478,8 +512,10 @@ static void __sta_info_unlink(struct sta_info **sta)
}
list_del(&(*sta)->list);
+ (*sta)->dead = true;
- if (test_and_clear_sta_flags(*sta, WLAN_STA_PS)) {
+ if (test_and_clear_sta_flags(*sta,
+ WLAN_STA_PS_STA | WLAN_STA_PS_DRIVER)) {
BUG_ON(!sdata->bss);
atomic_dec(&sdata->bss->num_sta_ps);
@@ -489,6 +525,9 @@ static void __sta_info_unlink(struct sta_info **sta)
local->num_sta--;
local->sta_generation++;
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ rcu_assign_pointer(sdata->u.vlan.sta, NULL);
+
if (local->ops->sta_notify) {
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
sdata = container_of(sdata->bss,
@@ -801,8 +840,8 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
sta_info_destroy(sta);
}
-struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_hw *hw,
- const u8 *addr)
+struct ieee80211_sta *ieee80211_find_sta_by_hw(struct ieee80211_hw *hw,
+ const u8 *addr)
{
struct sta_info *sta = sta_info_get(hw_to_local(hw), addr);
@@ -810,4 +849,114 @@ struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_hw *hw,
return NULL;
return &sta->sta;
}
+EXPORT_SYMBOL_GPL(ieee80211_find_sta_by_hw);
+
+struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_vif *vif,
+ const u8 *addr)
+{
+ struct ieee80211_sub_if_data *sdata;
+
+ if (!vif)
+ return NULL;
+
+ sdata = vif_to_sdata(vif);
+
+ return ieee80211_find_sta_by_hw(&sdata->local->hw, addr);
+}
EXPORT_SYMBOL(ieee80211_find_sta);
+
+/* powersave support code */
+void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
+{
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct ieee80211_local *local = sdata->local;
+ int sent, buffered;
+
+ drv_sta_notify(local, &sdata->vif, STA_NOTIFY_AWAKE, &sta->sta);
+
+ if (!skb_queue_empty(&sta->ps_tx_buf))
+ sta_info_clear_tim_bit(sta);
+
+ /* Send all buffered frames to the station */
+ sent = ieee80211_add_pending_skbs(local, &sta->tx_filtered);
+ buffered = ieee80211_add_pending_skbs(local, &sta->ps_tx_buf);
+ sent += buffered;
+ local->total_ps_buffered -= buffered;
+
+#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
+ printk(KERN_DEBUG "%s: STA %pM aid %d sending %d filtered/%d PS frames "
+ "since STA not sleeping anymore\n", sdata->dev->name,
+ sta->sta.addr, sta->sta.aid, sent - buffered, buffered);
+#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
+}
+
+void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta)
+{
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct ieee80211_local *local = sdata->local;
+ struct sk_buff *skb;
+ int no_pending_pkts;
+
+ skb = skb_dequeue(&sta->tx_filtered);
+ if (!skb) {
+ skb = skb_dequeue(&sta->ps_tx_buf);
+ if (skb)
+ local->total_ps_buffered--;
+ }
+ no_pending_pkts = skb_queue_empty(&sta->tx_filtered) &&
+ skb_queue_empty(&sta->ps_tx_buf);
+
+ if (skb) {
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr =
+ (struct ieee80211_hdr *) skb->data;
+
+ /*
+ * Tell TX path to send this frame even though the STA may
+ * still remain is PS mode after this frame exchange.
+ */
+ info->flags |= IEEE80211_TX_CTL_PSPOLL_RESPONSE;
+
+#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
+ printk(KERN_DEBUG "STA %pM aid %d: PS Poll (entries after %d)\n",
+ sta->sta.addr, sta->sta.aid,
+ skb_queue_len(&sta->ps_tx_buf));
+#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
+
+ /* Use MoreData flag to indicate whether there are more
+ * buffered frames for this STA */
+ if (no_pending_pkts)
+ hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_MOREDATA);
+ else
+ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+
+ ieee80211_add_pending_skb(local, skb);
+
+ if (no_pending_pkts)
+ sta_info_clear_tim_bit(sta);
+#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
+ } else {
+ /*
+ * FIXME: This can be the result of a race condition between
+ * us expiring a frame and the station polling for it.
+ * Should we send it a null-func frame indicating we
+ * have nothing buffered for it?
+ */
+ printk(KERN_DEBUG "%s: STA %pM sent PS Poll even "
+ "though there are no buffered frames for it\n",
+ sdata->dev->name, sta->sta.addr);
+#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
+ }
+}
+
+void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
+ struct ieee80211_sta *pubsta, bool block)
+{
+ struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
+
+ if (block)
+ set_sta_flags(sta, WLAN_STA_PS_DRIVER);
+ else
+ ieee80211_queue_work(hw, &sta->drv_unblock_wk);
+}
+EXPORT_SYMBOL(ieee80211_sta_block_awake);
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index ccc3adf962c..b4810f6aa94 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -12,6 +12,7 @@
#include <linux/list.h>
#include <linux/types.h>
#include <linux/if_ether.h>
+#include <linux/workqueue.h>
#include "key.h"
/**
@@ -21,7 +22,7 @@
*
* @WLAN_STA_AUTH: Station is authenticated.
* @WLAN_STA_ASSOC: Station is associated.
- * @WLAN_STA_PS: Station is in power-save mode
+ * @WLAN_STA_PS_STA: Station is in power-save mode
* @WLAN_STA_AUTHORIZED: Station is authorized to send/receive traffic.
* This bit is always checked so needs to be enabled for all stations
* when virtual port control is not in use.
@@ -36,11 +37,16 @@
* @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).
+ * @WLAN_STA_PS_DRIVER: driver requires keeping this station in
+ * power-save mode logically to flush frames that might still
+ * be in the queues
+ * @WLAN_STA_PSPOLL: Station sent PS-poll while driver was keeping
+ * station in power-save mode, reply when the driver unblocks.
*/
enum ieee80211_sta_info_flags {
WLAN_STA_AUTH = 1<<0,
WLAN_STA_ASSOC = 1<<1,
- WLAN_STA_PS = 1<<2,
+ WLAN_STA_PS_STA = 1<<2,
WLAN_STA_AUTHORIZED = 1<<3,
WLAN_STA_SHORT_PREAMBLE = 1<<4,
WLAN_STA_ASSOC_AP = 1<<5,
@@ -48,7 +54,9 @@ enum ieee80211_sta_info_flags {
WLAN_STA_WDS = 1<<7,
WLAN_STA_CLEAR_PS_FILT = 1<<9,
WLAN_STA_MFP = 1<<10,
- WLAN_STA_SUSPEND = 1<<11
+ WLAN_STA_SUSPEND = 1<<11,
+ WLAN_STA_PS_DRIVER = 1<<12,
+ WLAN_STA_PSPOLL = 1<<13,
};
#define STA_TID_NUM 16
@@ -177,6 +185,7 @@ struct sta_ampdu_mlme {
* @lock: used for locking all fields that require locking, see comments
* in the header file.
* @flaglock: spinlock for flags accesses
+ * @drv_unblock_wk: used for driver PS unblocking
* @listen_interval: listen interval of this station, when we're acting as AP
* @pin_status: used internally for pinning a STA struct into memory
* @flags: STA flags, see &enum ieee80211_sta_info_flags
@@ -193,7 +202,6 @@ struct sta_ampdu_mlme {
* @rx_fragments: number of received MPDUs
* @rx_dropped: number of dropped MPDUs from this STA
* @last_signal: signal of last received frame from this STA
- * @last_qual: qual of last received frame from this STA
* @last_noise: noise of last received frame from this STA
* @last_seq_ctrl: last received seq/frag number from this STA (per RX queue)
* @tx_filtered_count: number of frames the hardware filtered for this STA
@@ -217,6 +225,7 @@ struct sta_ampdu_mlme {
* @plink_timer_was_running: used by suspend/resume to restore timers
* @debugfs: debug filesystem info
* @sta: station information we share with the driver
+ * @dead: set to true when sta is unlinked
*/
struct sta_info {
/* General information, mostly static */
@@ -230,8 +239,12 @@ struct sta_info {
spinlock_t lock;
spinlock_t flaglock;
+ struct work_struct drv_unblock_wk;
+
u16 listen_interval;
+ bool dead;
+
/*
* for use by the internal lifetime management,
* see __sta_info_unlink
@@ -259,7 +272,6 @@ struct sta_info {
unsigned long rx_fragments;
unsigned long rx_dropped;
int last_signal;
- int last_qual;
int last_noise;
__le16 last_seq_ctrl[NUM_RX_DATA_QUEUES];
@@ -301,28 +313,6 @@ struct sta_info {
#ifdef CONFIG_MAC80211_DEBUGFS
struct sta_info_debugfsdentries {
struct dentry *dir;
- struct dentry *flags;
- struct dentry *num_ps_buf_frames;
- struct dentry *inactive_ms;
- struct dentry *last_seq_ctrl;
- struct dentry *agg_status;
- struct dentry *aid;
- struct dentry *dev;
- struct dentry *rx_packets;
- struct dentry *tx_packets;
- struct dentry *rx_bytes;
- struct dentry *tx_bytes;
- struct dentry *rx_duplicates;
- struct dentry *rx_fragments;
- struct dentry *rx_dropped;
- struct dentry *tx_fragments;
- struct dentry *tx_filtered;
- struct dentry *tx_retry_failed;
- struct dentry *tx_retry_count;
- struct dentry *last_signal;
- struct dentry *last_qual;
- struct dentry *last_noise;
- struct dentry *wep_weak_iv_count;
bool add_has_run;
} debugfs;
#endif
@@ -419,8 +409,8 @@ struct sta_info *sta_info_get(struct ieee80211_local *local, const u8 *addr);
/*
* Get STA info by index, BROKEN!
*/
-struct sta_info *sta_info_get_by_idx(struct ieee80211_local *local, int idx,
- struct net_device *dev);
+struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata,
+ int idx);
/*
* Create a new STA info, caller owns returned structure
* until sta_info_insert().
@@ -454,4 +444,7 @@ int sta_info_flush(struct ieee80211_local *local,
void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
unsigned long exp_time);
+void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta);
+void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta);
+
#endif /* STA_INFO_H */
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
new file mode 100644
index 00000000000..d78f36c64c7
--- /dev/null
+++ b/net/mac80211/status.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright 2002-2005, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
+ * Copyright 2008-2009 Johannes Berg <johannes@sipsolutions.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <net/mac80211.h>
+#include "ieee80211_i.h"
+#include "rate.h"
+#include "mesh.h"
+#include "led.h"
+
+
+void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw,
+ struct sk_buff *skb)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ int tmp;
+
+ skb->pkt_type = IEEE80211_TX_STATUS_MSG;
+ skb_queue_tail(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS ?
+ &local->skb_queue : &local->skb_queue_unreliable, skb);
+ tmp = skb_queue_len(&local->skb_queue) +
+ skb_queue_len(&local->skb_queue_unreliable);
+ while (tmp > IEEE80211_IRQSAFE_QUEUE_LIMIT &&
+ (skb = skb_dequeue(&local->skb_queue_unreliable))) {
+ dev_kfree_skb_irq(skb);
+ tmp--;
+ I802_DEBUG_INC(local->tx_status_drop);
+ }
+ tasklet_schedule(&local->tasklet);
+}
+EXPORT_SYMBOL(ieee80211_tx_status_irqsafe);
+
+static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
+ struct sta_info *sta,
+ struct sk_buff *skb)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ /*
+ * XXX: This is temporary!
+ *
+ * The problem here is that when we get here, the driver will
+ * quite likely have pretty much overwritten info->control by
+ * using info->driver_data or info->rate_driver_data. Thus,
+ * when passing out the frame to the driver again, we would be
+ * passing completely bogus data since the driver would then
+ * expect a properly filled info->control. In mac80211 itself
+ * the same problem occurs, since we need info->control.vif
+ * internally.
+ *
+ * To fix this, we should send the frame through TX processing
+ * again. However, it's not that simple, since the frame will
+ * have been software-encrypted (if applicable) already, and
+ * encrypting it again doesn't do much good. So to properly do
+ * that, we not only have to skip the actual 'raw' encryption
+ * (key selection etc. still has to be done!) but also the
+ * sequence number assignment since that impacts the crypto
+ * encapsulation, of course.
+ *
+ * Hence, for now, fix the bug by just dropping the frame.
+ */
+ goto drop;
+
+ sta->tx_filtered_count++;
+
+ /*
+ * Clear the TX filter mask for this STA when sending the next
+ * packet. If the STA went to power save mode, this will happen
+ * when it wakes up for the next time.
+ */
+ set_sta_flags(sta, WLAN_STA_CLEAR_PS_FILT);
+
+ /*
+ * This code races in the following way:
+ *
+ * (1) STA sends frame indicating it will go to sleep and does so
+ * (2) hardware/firmware adds STA to filter list, passes frame up
+ * (3) hardware/firmware processes TX fifo and suppresses a frame
+ * (4) we get TX status before having processed the frame and
+ * knowing that the STA has gone to sleep.
+ *
+ * This is actually quite unlikely even when both those events are
+ * processed from interrupts coming in quickly after one another or
+ * even at the same time because we queue both TX status events and
+ * RX frames to be processed by a tasklet and process them in the
+ * same order that they were received or TX status last. Hence, there
+ * is no race as long as the frame RX is processed before the next TX
+ * status, which drivers can ensure, see below.
+ *
+ * Note that this can only happen if the hardware or firmware can
+ * actually add STAs to the filter list, if this is done by the
+ * driver in response to set_tim() (which will only reduce the race
+ * this whole filtering tries to solve, not completely solve it)
+ * this situation cannot happen.
+ *
+ * To completely solve this race drivers need to make sure that they
+ * (a) don't mix the irq-safe/not irq-safe TX status/RX processing
+ * functions and
+ * (b) always process RX events before TX status events if ordering
+ * can be unknown, for example with different interrupt status
+ * bits.
+ */
+ if (test_sta_flags(sta, WLAN_STA_PS_STA) &&
+ skb_queue_len(&sta->tx_filtered) < STA_MAX_TX_BUFFER) {
+ skb_queue_tail(&sta->tx_filtered, skb);
+ return;
+ }
+
+ if (!test_sta_flags(sta, WLAN_STA_PS_STA) &&
+ !(info->flags & IEEE80211_TX_INTFL_RETRIED)) {
+ /* Software retry the packet once */
+ info->flags |= IEEE80211_TX_INTFL_RETRIED;
+ ieee80211_add_pending_skb(local, skb);
+ return;
+ }
+
+ drop:
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+ if (net_ratelimit())
+ printk(KERN_DEBUG "%s: dropped TX filtered frame, "
+ "queue_len=%d PS=%d @%lu\n",
+ wiphy_name(local->hw.wiphy),
+ skb_queue_len(&sta->tx_filtered),
+ !!test_sta_flags(sta, WLAN_STA_PS_STA), jiffies);
+#endif
+ dev_kfree_skb(skb);
+}
+
+void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+ struct sk_buff *skb2;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ u16 frag, type;
+ __le16 fc;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_tx_status_rtap_hdr *rthdr;
+ struct ieee80211_sub_if_data *sdata;
+ struct net_device *prev_dev = NULL;
+ struct sta_info *sta;
+ int retry_count = -1, i;
+ bool injected;
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ /* the HW cannot have attempted that rate */
+ if (i >= hw->max_rates) {
+ info->status.rates[i].idx = -1;
+ info->status.rates[i].count = 0;
+ }
+
+ retry_count += info->status.rates[i].count;
+ }
+ if (retry_count < 0)
+ retry_count = 0;
+
+ rcu_read_lock();
+
+ sband = local->hw.wiphy->bands[info->band];
+
+ sta = sta_info_get(local, hdr->addr1);
+
+ if (sta) {
+ if (!(info->flags & IEEE80211_TX_STAT_ACK) &&
+ test_sta_flags(sta, WLAN_STA_PS_STA)) {
+ /*
+ * The STA is in power save mode, so assume
+ * that this TX packet failed because of that.
+ */
+ ieee80211_handle_filtered_frame(local, sta, skb);
+ rcu_read_unlock();
+ return;
+ }
+
+ fc = hdr->frame_control;
+
+ if ((info->flags & IEEE80211_TX_STAT_AMPDU_NO_BACK) &&
+ (ieee80211_is_data_qos(fc))) {
+ u16 tid, ssn;
+ u8 *qc;
+
+ qc = ieee80211_get_qos_ctl(hdr);
+ tid = qc[0] & 0xf;
+ ssn = ((le16_to_cpu(hdr->seq_ctrl) + 0x10)
+ & IEEE80211_SCTL_SEQ);
+ ieee80211_send_bar(sta->sdata, hdr->addr1,
+ tid, ssn);
+ }
+
+ if (info->flags & IEEE80211_TX_STAT_TX_FILTERED) {
+ ieee80211_handle_filtered_frame(local, sta, skb);
+ rcu_read_unlock();
+ return;
+ } else {
+ if (!(info->flags & IEEE80211_TX_STAT_ACK))
+ sta->tx_retry_failed++;
+ sta->tx_retry_count += retry_count;
+ }
+
+ rate_control_tx_status(local, sband, sta, skb);
+ if (ieee80211_vif_is_mesh(&sta->sdata->vif))
+ ieee80211s_update_metric(local, sta, skb);
+ }
+
+ rcu_read_unlock();
+
+ ieee80211_led_tx(local, 0);
+
+ /* SNMP counters
+ * Fragments are passed to low-level drivers as separate skbs, so these
+ * are actually fragments, not frames. Update frame counters only for
+ * the first fragment of the frame. */
+
+ frag = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG;
+ type = le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_FTYPE;
+
+ if (info->flags & IEEE80211_TX_STAT_ACK) {
+ if (frag == 0) {
+ local->dot11TransmittedFrameCount++;
+ if (is_multicast_ether_addr(hdr->addr1))
+ local->dot11MulticastTransmittedFrameCount++;
+ if (retry_count > 0)
+ local->dot11RetryCount++;
+ if (retry_count > 1)
+ local->dot11MultipleRetryCount++;
+ }
+
+ /* This counter shall be incremented for an acknowledged MPDU
+ * with an individual address in the address 1 field or an MPDU
+ * with a multicast address in the address 1 field of type Data
+ * or Management. */
+ if (!is_multicast_ether_addr(hdr->addr1) ||
+ type == IEEE80211_FTYPE_DATA ||
+ type == IEEE80211_FTYPE_MGMT)
+ local->dot11TransmittedFragmentCount++;
+ } else {
+ if (frag == 0)
+ local->dot11FailedCount++;
+ }
+
+ /* this was a transmitted frame, but now we want to reuse it */
+ skb_orphan(skb);
+
+ /*
+ * This is a bit racy but we can avoid a lot of work
+ * with this test...
+ */
+ if (!local->monitors && !local->cooked_mntrs) {
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ /* send frame to monitor interfaces now */
+
+ if (skb_headroom(skb) < sizeof(*rthdr)) {
+ printk(KERN_ERR "ieee80211_tx_status: headroom too small\n");
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ rthdr = (struct ieee80211_tx_status_rtap_hdr *)
+ skb_push(skb, sizeof(*rthdr));
+
+ memset(rthdr, 0, sizeof(*rthdr));
+ rthdr->hdr.it_len = cpu_to_le16(sizeof(*rthdr));
+ rthdr->hdr.it_present =
+ cpu_to_le32((1 << IEEE80211_RADIOTAP_TX_FLAGS) |
+ (1 << IEEE80211_RADIOTAP_DATA_RETRIES) |
+ (1 << IEEE80211_RADIOTAP_RATE));
+
+ if (!(info->flags & IEEE80211_TX_STAT_ACK) &&
+ !is_multicast_ether_addr(hdr->addr1))
+ rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_FAIL);
+
+ /*
+ * XXX: Once radiotap gets the bitmap reset thing the vendor
+ * extensions proposal contains, we can actually report
+ * the whole set of tries we did.
+ */
+ if ((info->status.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) ||
+ (info->status.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT))
+ rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_CTS);
+ else if (info->status.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS)
+ rthdr->tx_flags |= cpu_to_le16(IEEE80211_RADIOTAP_F_TX_RTS);
+ if (info->status.rates[0].idx >= 0 &&
+ !(info->status.rates[0].flags & IEEE80211_TX_RC_MCS))
+ rthdr->rate = sband->bitrates[
+ info->status.rates[0].idx].bitrate / 5;
+
+ /* for now report the total retry_count */
+ rthdr->data_retries = retry_count;
+
+ /* Need to make a copy before skb->cb gets cleared */
+ injected = !!(info->flags & IEEE80211_TX_CTL_INJECTED);
+
+ /* XXX: is this sufficient for BPF? */
+ skb_set_mac_header(skb, 0);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->protocol = htons(ETH_P_802_2);
+ memset(skb->cb, 0, sizeof(skb->cb));
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
+ if (!netif_running(sdata->dev))
+ continue;
+
+ if ((sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) &&
+ !injected &&
+ (type == IEEE80211_FTYPE_DATA))
+ continue;
+
+ if (prev_dev) {
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (skb2) {
+ skb2->dev = prev_dev;
+ netif_rx(skb2);
+ }
+ }
+
+ prev_dev = sdata->dev;
+ }
+ }
+ if (prev_dev) {
+ skb->dev = prev_dev;
+ netif_rx(skb);
+ skb = NULL;
+ }
+ rcu_read_unlock();
+ dev_kfree_skb(skb);
+}
+EXPORT_SYMBOL(ieee80211_tx_status);
diff --git a/net/mac80211/tkip.c b/net/mac80211/tkip.c
index 964b7faa7f1..4921d724b6c 100644
--- a/net/mac80211/tkip.c
+++ b/net/mac80211/tkip.c
@@ -301,9 +301,9 @@ int ieee80211_tkip_decrypt_data(struct crypto_blkcipher *tfm,
#endif
if (key->local->ops->update_tkip_key &&
key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) {
- u8 bcast[ETH_ALEN] =
+ static const u8 bcast[ETH_ALEN] =
{0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
- u8 *sta_addr = key->sta->sta.addr;
+ const u8 *sta_addr = key->sta->sta.addr;
if (is_multicast_ether_addr(ra))
sta_addr = bcast;
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index eaa4118de98..8834cc93c71 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -317,12 +317,11 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)
if (!atomic_read(&tx->sdata->bss->num_sta_ps))
return TX_CONTINUE;
- /* buffered in hardware */
- if (!(tx->local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING)) {
- info->flags |= IEEE80211_TX_CTL_SEND_AFTER_DTIM;
+ info->flags |= IEEE80211_TX_CTL_SEND_AFTER_DTIM;
+ /* device releases frame after DTIM beacon */
+ if (!(tx->local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING))
return TX_CONTINUE;
- }
/* buffered in mac80211 */
if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)
@@ -367,15 +366,16 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
u32 staflags;
- if (unlikely(!sta || ieee80211_is_probe_resp(hdr->frame_control)
- || ieee80211_is_auth(hdr->frame_control)
- || ieee80211_is_assoc_resp(hdr->frame_control)
- || ieee80211_is_reassoc_resp(hdr->frame_control)))
+ if (unlikely(!sta ||
+ ieee80211_is_probe_resp(hdr->frame_control) ||
+ ieee80211_is_auth(hdr->frame_control) ||
+ ieee80211_is_assoc_resp(hdr->frame_control) ||
+ ieee80211_is_reassoc_resp(hdr->frame_control)))
return TX_CONTINUE;
staflags = get_sta_flags(sta);
- if (unlikely((staflags & WLAN_STA_PS) &&
+ if (unlikely((staflags & (WLAN_STA_PS_STA | WLAN_STA_PS_DRIVER)) &&
!(info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE))) {
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
printk(KERN_DEBUG "STA %pM aid %d: PS buffer (entries "
@@ -398,8 +398,13 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
} else
tx->local->total_ps_buffered++;
- /* Queue frame to be sent after STA sends an PS Poll frame */
- if (skb_queue_empty(&sta->ps_tx_buf))
+ /*
+ * Queue frame to be sent after STA wakes up/polls,
+ * but don't set the TIM bit if the driver is blocking
+ * wakeup or poll response transmissions anyway.
+ */
+ if (skb_queue_empty(&sta->ps_tx_buf) &&
+ !(staflags & WLAN_STA_PS_DRIVER))
sta_info_set_tim_bit(sta);
info->control.jiffies = jiffies;
@@ -409,7 +414,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
return TX_QUEUED;
}
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- else if (unlikely(test_sta_flags(sta, WLAN_STA_PS))) {
+ else if (unlikely(staflags & WLAN_STA_PS_STA)) {
printk(KERN_DEBUG "%s: STA %pM in PS mode, but pspoll "
"set -> send frame\n", tx->dev->name,
sta->sta.addr);
@@ -1047,7 +1052,10 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
hdr = (struct ieee80211_hdr *) skb->data;
- tx->sta = sta_info_get(local, hdr->addr1);
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ tx->sta = rcu_dereference(sdata->u.vlan.sta);
+ if (!tx->sta)
+ tx->sta = sta_info_get(local, hdr->addr1);
if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) &&
(local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)) {
@@ -1201,23 +1209,26 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
struct sk_buff *skb = tx->skb;
ieee80211_tx_result res = TX_DROP;
-#define CALL_TXH(txh) \
- res = txh(tx); \
- if (res != TX_CONTINUE) \
- goto txh_done;
-
- CALL_TXH(ieee80211_tx_h_check_assoc)
- CALL_TXH(ieee80211_tx_h_ps_buf)
- CALL_TXH(ieee80211_tx_h_select_key)
- CALL_TXH(ieee80211_tx_h_michael_mic_add)
- CALL_TXH(ieee80211_tx_h_rate_ctrl)
- CALL_TXH(ieee80211_tx_h_misc)
- CALL_TXH(ieee80211_tx_h_sequence)
- CALL_TXH(ieee80211_tx_h_fragment)
+#define CALL_TXH(txh) \
+ do { \
+ res = txh(tx); \
+ if (res != TX_CONTINUE) \
+ goto txh_done; \
+ } while (0)
+
+ CALL_TXH(ieee80211_tx_h_check_assoc);
+ CALL_TXH(ieee80211_tx_h_ps_buf);
+ CALL_TXH(ieee80211_tx_h_select_key);
+ CALL_TXH(ieee80211_tx_h_michael_mic_add);
+ if (!(tx->local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL))
+ CALL_TXH(ieee80211_tx_h_rate_ctrl);
+ CALL_TXH(ieee80211_tx_h_misc);
+ CALL_TXH(ieee80211_tx_h_sequence);
+ CALL_TXH(ieee80211_tx_h_fragment);
/* handlers after fragment must be aware of tx info fragmentation! */
- CALL_TXH(ieee80211_tx_h_stats)
- CALL_TXH(ieee80211_tx_h_encrypt)
- CALL_TXH(ieee80211_tx_h_calculate_duration)
+ CALL_TXH(ieee80211_tx_h_stats);
+ CALL_TXH(ieee80211_tx_h_encrypt);
+ CALL_TXH(ieee80211_tx_h_calculate_duration);
#undef CALL_TXH
txh_done:
@@ -1387,6 +1398,30 @@ static int ieee80211_skb_resize(struct ieee80211_local *local,
return 0;
}
+static bool need_dynamic_ps(struct ieee80211_local *local)
+{
+ /* driver doesn't support power save */
+ if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS))
+ return false;
+
+ /* hardware does dynamic power save */
+ if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
+ return false;
+
+ /* dynamic power save disabled */
+ if (local->hw.conf.dynamic_ps_timeout <= 0)
+ return false;
+
+ /* we are scanning, don't enable power save */
+ if (local->scanning)
+ return false;
+
+ if (!local->ps_sdata)
+ return false;
+
+ return true;
+}
+
static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
@@ -1397,11 +1432,7 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
int headroom;
bool may_encrypt;
- dev_hold(sdata->dev);
-
- if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&
- local->hw.conf.dynamic_ps_timeout > 0 &&
- !(local->scanning) && local->ps_sdata) {
+ if (need_dynamic_ps(local)) {
if (local->hw.conf.flags & IEEE80211_CONF_PS) {
ieee80211_stop_queues_by_reason(&local->hw,
IEEE80211_QUEUE_STOP_REASON_PS);
@@ -1413,7 +1444,7 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
}
- info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
+ rcu_read_lock();
if (unlikely(sdata->vif.type == NL80211_IFTYPE_MONITOR)) {
int hdrlen;
@@ -1437,7 +1468,6 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
* support we will need a different mechanism.
*/
- rcu_read_lock();
list_for_each_entry_rcu(tmp_sdata, &local->interfaces,
list) {
if (!netif_running(tmp_sdata->dev))
@@ -1446,13 +1476,10 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
continue;
if (compare_ether_addr(tmp_sdata->dev->dev_addr,
hdr->addr2) == 0) {
- dev_hold(tmp_sdata->dev);
- dev_put(sdata->dev);
sdata = tmp_sdata;
break;
}
}
- rcu_read_unlock();
}
}
@@ -1466,7 +1493,7 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
if (ieee80211_skb_resize(local, skb, headroom, may_encrypt)) {
dev_kfree_skb(skb);
- dev_put(sdata->dev);
+ rcu_read_unlock();
return;
}
@@ -1477,13 +1504,13 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
!is_multicast_ether_addr(hdr->addr1))
if (mesh_nexthop_lookup(skb, sdata)) {
/* skb queued: don't free */
- dev_put(sdata->dev);
+ rcu_read_unlock();
return;
}
ieee80211_select_queue(local, skb);
ieee80211_tx(sdata, skb, false);
- dev_put(sdata->dev);
+ rcu_read_unlock();
}
netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
@@ -1547,6 +1574,8 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
memset(info, 0, sizeof(*info));
+ info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
+
/* pass the radiotap header up to xmit */
ieee80211_xmit(IEEE80211_DEV_TO_SUB_IF(dev), skb);
return NETDEV_TX_OK;
@@ -1585,7 +1614,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
const u8 *encaps_data;
int encaps_len, skip_header_bytes;
int nh_pos, h_pos;
- struct sta_info *sta;
+ struct sta_info *sta = NULL;
u32 sta_flags = 0;
if (unlikely(skb->len < ETH_HLEN)) {
@@ -1602,8 +1631,24 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA);
switch (sdata->vif.type) {
- case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_AP_VLAN:
+ rcu_read_lock();
+ sta = rcu_dereference(sdata->u.vlan.sta);
+ if (sta) {
+ fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
+ /* RA TA DA SA */
+ memcpy(hdr.addr1, sta->sta.addr, ETH_ALEN);
+ memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
+ memcpy(hdr.addr3, skb->data, ETH_ALEN);
+ memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
+ hdrlen = 30;
+ sta_flags = get_sta_flags(sta);
+ }
+ rcu_read_unlock();
+ if (sta)
+ break;
+ /* fall through */
+ case NL80211_IFTYPE_AP:
fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
/* DA BSSID SA */
memcpy(hdr.addr1, skb->data, ETH_ALEN);
@@ -1639,21 +1684,25 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
/* packet from other interface */
struct mesh_path *mppath;
int is_mesh_mcast = 1;
- char *mesh_da;
+ const u8 *mesh_da;
rcu_read_lock();
if (is_multicast_ether_addr(skb->data))
/* DA TA mSA AE:SA */
mesh_da = skb->data;
else {
+ static const u8 bcast[ETH_ALEN] =
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
mppath = mpp_path_lookup(skb->data, sdata);
if (mppath) {
/* RA TA mDA mSA AE:DA SA */
mesh_da = mppath->mpp;
is_mesh_mcast = 0;
- } else
+ } else {
/* DA TA mSA AE:SA */
- mesh_da = dev->broadcast;
+ mesh_da = bcast;
+ }
}
hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc,
mesh_da, dev->dev_addr);
@@ -1677,12 +1726,21 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
break;
#endif
case NL80211_IFTYPE_STATION:
- fc |= cpu_to_le16(IEEE80211_FCTL_TODS);
- /* BSSID SA DA */
memcpy(hdr.addr1, sdata->u.mgd.bssid, ETH_ALEN);
- memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
- memcpy(hdr.addr3, skb->data, ETH_ALEN);
- hdrlen = 24;
+ if (sdata->u.mgd.use_4addr && ethertype != ETH_P_PAE) {
+ fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
+ /* RA TA DA SA */
+ memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
+ memcpy(hdr.addr3, skb->data, ETH_ALEN);
+ memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
+ hdrlen = 30;
+ } else {
+ fc |= cpu_to_le16(IEEE80211_FCTL_TODS);
+ /* BSSID SA DA */
+ memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
+ memcpy(hdr.addr3, skb->data, ETH_ALEN);
+ hdrlen = 24;
+ }
break;
case NL80211_IFTYPE_ADHOC:
/* DA SA BSSID */
@@ -1907,12 +1965,10 @@ void ieee80211_tx_pending(unsigned long data)
}
sdata = vif_to_sdata(info->control.vif);
- dev_hold(sdata->dev);
spin_unlock_irqrestore(&local->queue_stop_reason_lock,
flags);
txok = ieee80211_tx_pending_skb(local, skb);
- dev_put(sdata->dev);
if (!txok)
__skb_queue_head(&local->pending[i], skb);
spin_lock_irqsave(&local->queue_stop_reason_lock,
@@ -1990,8 +2046,9 @@ static void ieee80211_beacon_add_tim(struct ieee80211_if_ap *bss,
}
}
-struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
+struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ u16 *tim_offset, u16 *tim_length)
{
struct ieee80211_local *local = hw_to_local(hw);
struct sk_buff *skb = NULL;
@@ -2008,6 +2065,11 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
sdata = vif_to_sdata(vif);
+ if (tim_offset)
+ *tim_offset = 0;
+ if (tim_length)
+ *tim_length = 0;
+
if (sdata->vif.type == NL80211_IFTYPE_AP) {
ap = &sdata->u.ap;
beacon = rcu_dereference(ap->beacon);
@@ -2043,6 +2105,11 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
spin_unlock_irqrestore(&local->sta_lock, flags);
}
+ if (tim_offset)
+ *tim_offset = beacon->head_len;
+ if (tim_length)
+ *tim_length = skb->len - beacon->head_len;
+
if (beacon->tail)
memcpy(skb_put(skb, beacon->tail_len),
beacon->tail, beacon->tail_len);
@@ -2080,7 +2147,7 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON);
memset(mgmt->da, 0xff, ETH_ALEN);
memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
- /* BSSID is left zeroed, wildcard value */
+ memcpy(mgmt->bssid, sdata->dev->dev_addr, ETH_ALEN);
mgmt->u.beacon.beacon_int =
cpu_to_le16(sdata->vif.bss_conf.beacon_int);
mgmt->u.beacon.capab_info = 0x0; /* 0x0 for MPs */
@@ -2119,7 +2186,7 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
rcu_read_unlock();
return skb;
}
-EXPORT_SYMBOL(ieee80211_beacon_get);
+EXPORT_SYMBOL(ieee80211_beacon_get_tim);
void ieee80211_rts_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
const void *frame, size_t frame_len,
@@ -2214,17 +2281,12 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(ieee80211_get_buffered_bc);
-void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
- int encrypt)
+void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
{
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
skb_set_mac_header(skb, 0);
skb_set_network_header(skb, 0);
skb_set_transport_header(skb, 0);
- if (!encrypt)
- info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
-
/*
* The other path calling ieee80211_xmit is from the tasklet,
* and while we can handle concurrent transmissions locking
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index aeb65b3d229..d09f78bb244 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -520,9 +520,9 @@ EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
*/
static bool ieee80211_can_queue_work(struct ieee80211_local *local)
{
- if (WARN(local->suspended, "queueing ieee80211 work while "
- "going to suspend\n"))
- return false;
+ if (WARN(local->suspended && !local->resuming,
+ "queueing ieee80211 work while going to suspend\n"))
+ return false;
return true;
}
@@ -666,8 +666,8 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
elems->mesh_id_len = elen;
break;
case WLAN_EID_MESH_CONFIG:
- elems->mesh_config = pos;
- elems->mesh_config_len = elen;
+ if (elen >= sizeof(struct ieee80211_meshconf_ie))
+ elems->mesh_config = (void *)pos;
break;
case WLAN_EID_PEER_LINK:
elems->peer_link = pos;
@@ -685,6 +685,10 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
elems->perr = pos;
elems->perr_len = elen;
break;
+ case WLAN_EID_RANN:
+ if (elen >= sizeof(struct ieee80211_rann_ie))
+ elems->rann = (void *)pos;
+ break;
case WLAN_EID_CHANNEL_SWITCH:
elems->ch_switch_elem = pos;
elems->ch_switch_elem_len = elen;
@@ -868,17 +872,19 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
WARN_ON(err);
}
- ieee80211_tx_skb(sdata, skb, 0);
+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+ ieee80211_tx_skb(sdata, skb);
}
int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
- const u8 *ie, size_t ie_len)
+ const u8 *ie, size_t ie_len,
+ enum ieee80211_band band)
{
struct ieee80211_supported_band *sband;
u8 *pos, *supp_rates_len, *esupp_rates_len = NULL;
int i;
- sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+ sband = local->hw.wiphy->bands[band];
pos = buffer;
@@ -966,9 +972,11 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
memcpy(pos, ssid, ssid_len);
pos += ssid_len;
- skb_put(skb, ieee80211_build_preq_ies(local, pos, ie, ie_len));
+ skb_put(skb, ieee80211_build_preq_ies(local, pos, ie, ie_len,
+ local->hw.conf.channel->band));
- ieee80211_tx_skb(sdata, skb, 0);
+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+ ieee80211_tx_skb(sdata, skb);
}
u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
@@ -1025,13 +1033,9 @@ int ieee80211_reconfig(struct ieee80211_local *local)
struct sta_info *sta;
unsigned long flags;
int res;
- bool from_suspend = local->suspended;
- /*
- * We're going to start the hardware, at that point
- * we are no longer suspended and can RX frames.
- */
- local->suspended = false;
+ if (local->suspended)
+ local->resuming = true;
/* restart hardware */
if (local->open_count) {
@@ -1129,11 +1133,14 @@ int ieee80211_reconfig(struct ieee80211_local *local)
* If this is for hw restart things are still running.
* We may want to change that later, however.
*/
- if (!from_suspend)
+ if (!local->suspended)
return 0;
#ifdef CONFIG_PM
+ /* first set suspended false, then resuming */
local->suspended = false;
+ mb();
+ local->resuming = false;
list_for_each_entry(sdata, &local->interfaces, list) {
switch(sdata->vif.type) {
diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c
index 8a980f13694..247123fe1a7 100644
--- a/net/mac80211/wep.c
+++ b/net/mac80211/wep.c
@@ -281,16 +281,18 @@ bool ieee80211_wep_is_weak_iv(struct sk_buff *skb, struct ieee80211_key *key)
ieee80211_rx_result
ieee80211_crypto_wep_decrypt(struct ieee80211_rx_data *rx)
{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
+ struct sk_buff *skb = rx->skb;
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
if (!ieee80211_is_data(hdr->frame_control) &&
!ieee80211_is_auth(hdr->frame_control))
return RX_CONTINUE;
- if (!(rx->status->flag & RX_FLAG_DECRYPTED)) {
+ if (!(status->flag & RX_FLAG_DECRYPTED)) {
if (ieee80211_wep_decrypt(rx->local, rx->skb, rx->key))
return RX_DROP_UNUSABLE;
- } else if (!(rx->status->flag & RX_FLAG_IV_STRIPPED)) {
+ } else if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key);
/* remove ICV */
skb_trim(rx->skb, rx->skb->len - WEP_ICV_LEN);
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index 70778694877..5332014cb22 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -85,16 +85,16 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx)
u8 *data, *key = NULL, key_offset;
size_t data_len;
unsigned int hdrlen;
- struct ieee80211_hdr *hdr;
u8 mic[MICHAEL_MIC_LEN];
struct sk_buff *skb = rx->skb;
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
int authenticator = 1, wpa_test = 0;
/* No way to verify the MIC if the hardware stripped it */
- if (rx->status->flag & RX_FLAG_MMIC_STRIPPED)
+ if (status->flag & RX_FLAG_MMIC_STRIPPED)
return RX_CONTINUE;
- hdr = (struct ieee80211_hdr *)skb->data;
if (!rx->key || rx->key->conf.alg != ALG_TKIP ||
!ieee80211_has_protected(hdr->frame_control) ||
!ieee80211_is_data_present(hdr->frame_control))
@@ -216,6 +216,7 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)
int hdrlen, res, hwaccel = 0, wpa_test = 0;
struct ieee80211_key *key = rx->key;
struct sk_buff *skb = rx->skb;
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
hdrlen = ieee80211_hdrlen(hdr->frame_control);
@@ -225,8 +226,8 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx)
if (!rx->sta || skb->len - hdrlen < 12)
return RX_DROP_UNUSABLE;
- if (rx->status->flag & RX_FLAG_DECRYPTED) {
- if (rx->status->flag & RX_FLAG_IV_STRIPPED) {
+ if (status->flag & RX_FLAG_DECRYPTED) {
+ if (status->flag & RX_FLAG_IV_STRIPPED) {
/*
* Hardware took care of all processing, including
* replay protection, and stripped the ICV/IV so
@@ -442,6 +443,7 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
int hdrlen;
struct ieee80211_key *key = rx->key;
struct sk_buff *skb = rx->skb;
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
u8 pn[CCMP_PN_LEN];
int data_len;
@@ -455,8 +457,8 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
if (!rx->sta || data_len < 0)
return RX_DROP_UNUSABLE;
- if ((rx->status->flag & RX_FLAG_DECRYPTED) &&
- (rx->status->flag & RX_FLAG_IV_STRIPPED))
+ if ((status->flag & RX_FLAG_DECRYPTED) &&
+ (status->flag & RX_FLAG_IV_STRIPPED))
return RX_CONTINUE;
ccmp_hdr2pn(pn, skb->data + hdrlen);
@@ -466,7 +468,7 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
return RX_DROP_UNUSABLE;
}
- if (!(rx->status->flag & RX_FLAG_DECRYPTED)) {
+ if (!(status->flag & RX_FLAG_DECRYPTED)) {
/* hardware didn't decrypt/verify MIC */
ccmp_special_blocks(skb, pn, key->u.ccmp.rx_crypto_buf, 1);
@@ -563,6 +565,7 @@ ieee80211_rx_result
ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx)
{
struct sk_buff *skb = rx->skb;
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_key *key = rx->key;
struct ieee80211_mmie *mmie;
u8 aad[20], mic[8], ipn[6];
@@ -571,8 +574,8 @@ ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx)
if (!ieee80211_is_mgmt(hdr->frame_control))
return RX_CONTINUE;
- if ((rx->status->flag & RX_FLAG_DECRYPTED) &&
- (rx->status->flag & RX_FLAG_IV_STRIPPED))
+ if ((status->flag & RX_FLAG_DECRYPTED) &&
+ (status->flag & RX_FLAG_IV_STRIPPED))
return RX_CONTINUE;
if (skb->len < 24 + sizeof(*mmie))
@@ -591,7 +594,7 @@ ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx)
return RX_DROP_UNUSABLE;
}
- if (!(rx->status->flag & RX_FLAG_DECRYPTED)) {
+ if (!(status->flag & RX_FLAG_DECRYPTED)) {
/* hardware didn't decrypt/verify MIC */
bip_aad(skb, aad);
ieee80211_aes_cmac(key->u.aes_cmac.tfm,