aboutsummaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2009-02-28 22:32:16 -0800
committerDavid S. Miller <davem@davemloft.net>2009-02-28 22:32:16 -0800
commit8010dc306ba39a8cdb3993d1e809fcb7dfdf089a (patch)
treed1fa3fe2e1501a6780fc007f2f57b1fe0995e18e /net
parent5d242f1cee2c85721bbe9d8205e98c1c01f5d805 (diff)
parent2a07954b83a3d4dc93031d3ce030fb9380a8e15a (diff)
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/Makefile1
-rw-r--r--net/mac80211/agg-rx.c6
-rw-r--r--net/mac80211/agg-tx.c191
-rw-r--r--net/mac80211/cfg.c49
-rw-r--r--net/mac80211/debugfs_netdev.c48
-rw-r--r--net/mac80211/ht.c19
-rw-r--r--net/mac80211/ibss.c905
-rw-r--r--net/mac80211/ieee80211_i.h140
-rw-r--r--net/mac80211/iface.c77
-rw-r--r--net/mac80211/key.c2
-rw-r--r--net/mac80211/main.c24
-rw-r--r--net/mac80211/mlme.c1665
-rw-r--r--net/mac80211/rate.h12
-rw-r--r--net/mac80211/rx.c37
-rw-r--r--net/mac80211/scan.c62
-rw-r--r--net/mac80211/spectmgmt.c26
-rw-r--r--net/mac80211/sta_info.c15
-rw-r--r--net/mac80211/sta_info.h5
-rw-r--r--net/mac80211/tx.c29
-rw-r--r--net/mac80211/util.c254
-rw-r--r--net/mac80211/wext.c262
-rw-r--r--net/mac80211/wme.c161
-rw-r--r--net/mac80211/wme.h6
-rw-r--r--net/wireless/core.c103
-rw-r--r--net/wireless/core.h36
-rw-r--r--net/wireless/nl80211.c75
-rw-r--r--net/wireless/nl80211.h4
-rw-r--r--net/wireless/reg.c945
-rw-r--r--net/wireless/reg.h36
-rw-r--r--net/wireless/scan.c64
-rw-r--r--net/wireless/sysfs.c9
-rw-r--r--net/wireless/wext-compat.c97
32 files changed, 3216 insertions, 2149 deletions
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index 3503a3d2131..0e3ab88bb70 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -9,6 +9,7 @@ mac80211-y := \
wpa.o \
scan.o \
ht.o agg-tx.o agg-rx.o \
+ ibss.o \
mlme.o \
iface.o \
rate.o \
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index 3112bfd441b..a95affc9462 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -129,7 +129,6 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d
u8 dialog_token, u16 status, u16 policy,
u16 buf_size, u16 timeout)
{
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
@@ -151,8 +150,9 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d
if (sdata->vif.type == NL80211_IFTYPE_AP ||
sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
memcpy(mgmt->bssid, sdata->dev->dev_addr, ETH_ALEN);
- else
- memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+ else if (sdata->vif.type == NL80211_IFTYPE_STATION)
+ memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN);
+
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ACTION);
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index 1232d9f01ca..1df116d4d6e 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -49,7 +49,6 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
u16 agg_size, u16 timeout)
{
struct ieee80211_local *local = sdata->local;
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
u16 capab;
@@ -69,8 +68,8 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata,
if (sdata->vif.type == NL80211_IFTYPE_AP ||
sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
memcpy(mgmt->bssid, sdata->dev->dev_addr, ETH_ALEN);
- else
- memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+ else if (sdata->vif.type == NL80211_IFTYPE_STATION)
+ memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN);
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ACTION);
@@ -132,9 +131,24 @@ static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
state = &sta->ampdu_mlme.tid_state_tx[tid];
- if (local->hw.ampdu_queues)
- ieee80211_stop_queue(&local->hw, sta->tid_to_tx_q[tid]);
+ if (local->hw.ampdu_queues) {
+ if (initiator) {
+ /*
+ * Stop the AC queue to avoid issues where we send
+ * unaggregated frames already before the delba.
+ */
+ ieee80211_stop_queue_by_reason(&local->hw,
+ local->hw.queues + sta->tid_to_tx_q[tid],
+ IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
+ }
+ /*
+ * Pretend the driver woke the queue, just in case
+ * it disabled it before the session was stopped.
+ */
+ ieee80211_wake_queue(
+ &local->hw, local->hw.queues + sta->tid_to_tx_q[tid]);
+ }
*state = HT_AGG_STATE_REQ_STOP_BA_MSK |
(initiator << HT_AGG_STATE_INITIATOR_SHIFT);
@@ -144,8 +158,6 @@ static int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
/* HW shall not deny going back to legacy */
if (WARN_ON(ret)) {
*state = HT_AGG_STATE_OPERATIONAL;
- if (local->hw.ampdu_queues)
- ieee80211_wake_queue(&local->hw, sta->tid_to_tx_q[tid]);
}
return ret;
@@ -189,14 +201,19 @@ static void sta_addba_resp_timer_expired(unsigned long data)
spin_unlock_bh(&sta->lock);
}
+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)
{
struct ieee80211_local *local = hw_to_local(hw);
struct sta_info *sta;
struct ieee80211_sub_if_data *sdata;
- u16 start_seq_num;
u8 *state;
- int ret = 0;
+ int i, qn = -1, ret = 0;
+ u16 start_seq_num;
if (WARN_ON(!local->ops->ampdu_action))
return -EINVAL;
@@ -209,6 +226,13 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
ra, tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
+ if (hw->ampdu_queues && ieee80211_ac_from_tid(tid) == 0) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "rejecting on voice AC\n");
+#endif
+ return -EINVAL;
+ }
+
rcu_read_lock();
sta = sta_info_get(local, ra);
@@ -217,7 +241,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
printk(KERN_DEBUG "Could not find the station\n");
#endif
ret = -ENOENT;
- goto exit;
+ goto unlock;
}
/*
@@ -230,11 +254,13 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
sta->sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
sta->sdata->vif.type != NL80211_IFTYPE_AP) {
ret = -EINVAL;
- goto exit;
+ goto unlock;
}
spin_lock_bh(&sta->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;
@@ -252,6 +278,42 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
goto err_unlock_sta;
}
+ if (hw->ampdu_queues) {
+ spin_lock(&local->queue_stop_reason_lock);
+ /* reserve a new queue for this session */
+ for (i = 0; i < local->hw.ampdu_queues; i++) {
+ if (local->ampdu_ac_queue[i] < 0) {
+ qn = i;
+ local->ampdu_ac_queue[qn] =
+ ieee80211_ac_from_tid(tid);
+ break;
+ }
+ }
+ spin_unlock(&local->queue_stop_reason_lock);
+
+ if (qn < 0) {
+#ifdef CONFIG_MAC80211_HT_DEBUG
+ printk(KERN_DEBUG "BA request denied - "
+ "queue unavailable for tid %d\n", tid);
+#endif /* CONFIG_MAC80211_HT_DEBUG */
+ ret = -ENOSPC;
+ goto err_unlock_sta;
+ }
+
+ /*
+ * If we successfully allocate the session, we can't have
+ * anything going on on the queue this TID maps into, so
+ * stop it for now. This is a "virtual" stop using the same
+ * mechanism that drivers will use.
+ *
+ * XXX: queue up frames for this session in the sta_info
+ * struct instead to avoid hitting all other STAs.
+ */
+ ieee80211_stop_queue_by_reason(
+ &local->hw, hw->queues + qn,
+ IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
+ }
+
/* prepare A-MPDU MLME for Tx aggregation */
sta->ampdu_mlme.tid_tx[tid] =
kmalloc(sizeof(struct tid_ampdu_tx), GFP_ATOMIC);
@@ -262,8 +324,9 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
tid);
#endif
ret = -ENOMEM;
- goto err_unlock_sta;
+ goto err_return_queue;
}
+
/* Tx timer */
sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer.function =
sta_addba_resp_timer_expired;
@@ -271,49 +334,25 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
(unsigned long)&sta->timer_to_tid[tid];
init_timer(&sta->ampdu_mlme.tid_tx[tid]->addba_resp_timer);
- if (hw->ampdu_queues) {
- /* create a new queue for this aggregation */
- ret = ieee80211_ht_agg_queue_add(local, sta, tid);
-
- /* case no queue is available to aggregation
- * don't switch to aggregation */
- if (ret) {
-#ifdef CONFIG_MAC80211_HT_DEBUG
- printk(KERN_DEBUG "BA request denied - "
- "queue unavailable for tid %d\n", tid);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
- goto err_unlock_queue;
- }
- }
- sdata = sta->sdata;
-
/* Ok, the Addba frame hasn't been sent yet, but if the driver calls the
* call back right away, it must see that the flow has begun */
*state |= HT_ADDBA_REQUESTED_MSK;
- /* This is slightly racy because the queue isn't stopped */
start_seq_num = sta->tid_seq[tid];
ret = local->ops->ampdu_action(hw, IEEE80211_AMPDU_TX_START,
&sta->sta, tid, &start_seq_num);
if (ret) {
- /* No need to requeue the packets in the agg queue, since we
- * held the tx lock: no packet could be enqueued to the newly
- * allocated queue */
- if (hw->ampdu_queues)
- ieee80211_ht_agg_queue_remove(local, sta, tid, 0);
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "BA request denied - HW unavailable for"
" tid %d\n", tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
*state = HT_AGG_STATE_IDLE;
- goto err_unlock_queue;
+ goto err_free;
}
+ sta->tid_to_tx_q[tid] = qn;
- /* Will put all the packets in the new SW queue */
- if (hw->ampdu_queues)
- ieee80211_requeue(local, ieee802_1d_to_ac[tid]);
spin_unlock_bh(&sta->lock);
/* send an addBA request */
@@ -322,7 +361,6 @@ 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,
sta->ampdu_mlme.tid_tx[tid]->dialog_token,
sta->ampdu_mlme.tid_tx[tid]->ssn,
@@ -334,15 +372,24 @@ 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 exit;
+ goto unlock;
-err_unlock_queue:
+ err_free:
kfree(sta->ampdu_mlme.tid_tx[tid]);
sta->ampdu_mlme.tid_tx[tid] = NULL;
- ret = -EBUSY;
-err_unlock_sta:
+ err_return_queue:
+ if (qn >= 0) {
+ /* We failed, so start queue again right away. */
+ ieee80211_wake_queue_by_reason(hw, hw->queues + qn,
+ IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
+ /* give queue back to pool */
+ spin_lock(&local->queue_stop_reason_lock);
+ local->ampdu_ac_queue[qn] = -1;
+ spin_unlock(&local->queue_stop_reason_lock);
+ }
+ err_unlock_sta:
spin_unlock_bh(&sta->lock);
-exit:
+ unlock:
rcu_read_unlock();
return ret;
}
@@ -375,7 +422,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
state = &sta->ampdu_mlme.tid_state_tx[tid];
spin_lock_bh(&sta->lock);
- if (!(*state & HT_ADDBA_REQUESTED_MSK)) {
+ if (WARN_ON(!(*state & HT_ADDBA_REQUESTED_MSK))) {
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "addBA was not requested yet, state is %d\n",
*state);
@@ -385,7 +432,8 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
return;
}
- WARN_ON_ONCE(*state & HT_ADDBA_DRV_READY_MSK);
+ if (WARN_ON(*state & HT_ADDBA_DRV_READY_MSK))
+ goto out;
*state |= HT_ADDBA_DRV_READY_MSK;
@@ -393,9 +441,18 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
#ifdef CONFIG_MAC80211_HT_DEBUG
printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid);
#endif
- if (hw->ampdu_queues)
- ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
+ if (hw->ampdu_queues) {
+ /*
+ * Wake up this queue, we stopped it earlier,
+ * this will in turn wake the entire AC.
+ */
+ ieee80211_wake_queue_by_reason(hw,
+ hw->queues + sta->tid_to_tx_q[tid],
+ IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
+ }
}
+
+ out:
spin_unlock_bh(&sta->lock);
rcu_read_unlock();
}
@@ -485,7 +542,6 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
struct ieee80211_local *local = hw_to_local(hw);
struct sta_info *sta;
u8 *state;
- int agg_queue;
if (tid >= STA_TID_NUM) {
#ifdef CONFIG_MAC80211_HT_DEBUG
@@ -527,19 +583,19 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
ieee80211_send_delba(sta->sdata, ra, tid,
WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);
- if (hw->ampdu_queues) {
- agg_queue = sta->tid_to_tx_q[tid];
- ieee80211_ht_agg_queue_remove(local, sta, tid, 1);
+ spin_lock_bh(&sta->lock);
- /* We just requeued the all the frames that were in the
- * removed queue, and since we might miss a softirq we do
- * netif_schedule_queue. ieee80211_wake_queue is not used
- * here as this queue is not necessarily stopped
+ if (*state & HT_AGG_STATE_INITIATOR_MSK &&
+ hw->ampdu_queues) {
+ /*
+ * Wake up this queue, we stopped it earlier,
+ * this will in turn wake the entire AC.
*/
- netif_schedule_queue(netdev_get_tx_queue(local->mdev,
- agg_queue));
+ ieee80211_wake_queue_by_reason(hw,
+ hw->queues + sta->tid_to_tx_q[tid],
+ IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
}
- spin_lock_bh(&sta->lock);
+
*state = HT_AGG_STATE_IDLE;
sta->ampdu_mlme.addba_req_num[tid] = 0;
kfree(sta->ampdu_mlme.tid_tx[tid]);
@@ -613,12 +669,21 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
#endif /* CONFIG_MAC80211_HT_DEBUG */
if (le16_to_cpu(mgmt->u.action.u.addba_resp.status)
== WLAN_STATUS_SUCCESS) {
+ u8 curstate = *state;
+
*state |= HT_ADDBA_RECEIVED_MSK;
- sta->ampdu_mlme.addba_req_num[tid] = 0;
- if (*state == HT_AGG_STATE_OPERATIONAL &&
- local->hw.ampdu_queues)
- ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
+ if (hw->ampdu_queues && *state != curstate &&
+ *state == HT_AGG_STATE_OPERATIONAL) {
+ /*
+ * Wake up this queue, we stopped it earlier,
+ * this will in turn wake the entire AC.
+ */
+ ieee80211_wake_queue_by_reason(hw,
+ hw->queues + sta->tid_to_tx_q[tid],
+ IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
+ }
+ sta->ampdu_mlme.addba_req_num[tid] = 0;
if (local->ops->ampdu_action) {
(void)local->ops->ampdu_action(hw,
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index c8d969be440..c43129efc3b 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -341,11 +341,15 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
sinfo->filled = STATION_INFO_INACTIVE_TIME |
STATION_INFO_RX_BYTES |
STATION_INFO_TX_BYTES |
+ STATION_INFO_RX_PACKETS |
+ STATION_INFO_TX_PACKETS |
STATION_INFO_TX_BITRATE;
sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
sinfo->rx_bytes = sta->rx_bytes;
sinfo->tx_bytes = sta->tx_bytes;
+ sinfo->rx_packets = sta->rx_packets;
+ sinfo->tx_packets = sta->tx_packets;
if (sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) {
sinfo->filled |= STATION_INFO_SIGNAL;
@@ -1180,45 +1184,45 @@ static int set_mgmt_extra_ie_sta(struct ieee80211_sub_if_data *sdata,
u8 subtype, u8 *ies, size_t ies_len)
{
struct ieee80211_local *local = sdata->local;
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
switch (subtype) {
case IEEE80211_STYPE_PROBE_REQ >> 4:
if (local->ops->hw_scan)
break;
- kfree(ifsta->ie_probereq);
- ifsta->ie_probereq = ies;
- ifsta->ie_probereq_len = ies_len;
+ kfree(ifmgd->ie_probereq);
+ ifmgd->ie_probereq = ies;
+ ifmgd->ie_probereq_len = ies_len;
return 0;
case IEEE80211_STYPE_PROBE_RESP >> 4:
- kfree(ifsta->ie_proberesp);
- ifsta->ie_proberesp = ies;
- ifsta->ie_proberesp_len = ies_len;
+ kfree(ifmgd->ie_proberesp);
+ ifmgd->ie_proberesp = ies;
+ ifmgd->ie_proberesp_len = ies_len;
return 0;
case IEEE80211_STYPE_AUTH >> 4:
- kfree(ifsta->ie_auth);
- ifsta->ie_auth = ies;
- ifsta->ie_auth_len = ies_len;
+ kfree(ifmgd->ie_auth);
+ ifmgd->ie_auth = ies;
+ ifmgd->ie_auth_len = ies_len;
return 0;
case IEEE80211_STYPE_ASSOC_REQ >> 4:
- kfree(ifsta->ie_assocreq);
- ifsta->ie_assocreq = ies;
- ifsta->ie_assocreq_len = ies_len;
+ kfree(ifmgd->ie_assocreq);
+ ifmgd->ie_assocreq = ies;
+ ifmgd->ie_assocreq_len = ies_len;
return 0;
case IEEE80211_STYPE_REASSOC_REQ >> 4:
- kfree(ifsta->ie_reassocreq);
- ifsta->ie_reassocreq = ies;
- ifsta->ie_reassocreq_len = ies_len;
+ kfree(ifmgd->ie_reassocreq);
+ ifmgd->ie_reassocreq = ies;
+ ifmgd->ie_reassocreq_len = ies_len;
return 0;
case IEEE80211_STYPE_DEAUTH >> 4:
- kfree(ifsta->ie_deauth);
- ifsta->ie_deauth = ies;
- ifsta->ie_deauth_len = ies_len;
+ kfree(ifmgd->ie_deauth);
+ ifmgd->ie_deauth = ies;
+ ifmgd->ie_deauth_len = ies_len;
return 0;
case IEEE80211_STYPE_DISASSOC >> 4:
- kfree(ifsta->ie_disassoc);
- ifsta->ie_disassoc = ies;
- ifsta->ie_disassoc_len = ies_len;
+ kfree(ifmgd->ie_disassoc);
+ ifmgd->ie_disassoc = ies;
+ ifmgd->ie_disassoc_len = ies_len;
return 0;
}
@@ -1248,7 +1252,6 @@ static int ieee80211_set_mgmt_extra_ie(struct wiphy *wiphy,
switch (sdata->vif.type) {
case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_ADHOC:
ret = set_mgmt_extra_ie_sta(sdata, params->subtype,
ies, ies_len);
break;
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index c5421930172..e3420329f4e 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -94,31 +94,31 @@ IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC);
IEEE80211_IF_FILE(force_unicast_rateidx, force_unicast_rateidx, DEC);
IEEE80211_IF_FILE(max_ratectrl_rateidx, max_ratectrl_rateidx, DEC);
-/* STA/IBSS attributes */
-IEEE80211_IF_FILE(state, u.sta.state, DEC);
-IEEE80211_IF_FILE(bssid, u.sta.bssid, MAC);
-IEEE80211_IF_FILE(prev_bssid, u.sta.prev_bssid, MAC);
-IEEE80211_IF_FILE(ssid_len, u.sta.ssid_len, SIZE);
-IEEE80211_IF_FILE(aid, u.sta.aid, DEC);
-IEEE80211_IF_FILE(ap_capab, u.sta.ap_capab, HEX);
-IEEE80211_IF_FILE(capab, u.sta.capab, HEX);
-IEEE80211_IF_FILE(extra_ie_len, u.sta.extra_ie_len, SIZE);
-IEEE80211_IF_FILE(auth_tries, u.sta.auth_tries, DEC);
-IEEE80211_IF_FILE(assoc_tries, u.sta.assoc_tries, DEC);
-IEEE80211_IF_FILE(auth_algs, u.sta.auth_algs, HEX);
-IEEE80211_IF_FILE(auth_alg, u.sta.auth_alg, DEC);
-IEEE80211_IF_FILE(auth_transaction, u.sta.auth_transaction, DEC);
+/* STA attributes */
+IEEE80211_IF_FILE(state, u.mgd.state, DEC);
+IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC);
+IEEE80211_IF_FILE(prev_bssid, u.mgd.prev_bssid, MAC);
+IEEE80211_IF_FILE(ssid_len, u.mgd.ssid_len, SIZE);
+IEEE80211_IF_FILE(aid, u.mgd.aid, DEC);
+IEEE80211_IF_FILE(ap_capab, u.mgd.ap_capab, HEX);
+IEEE80211_IF_FILE(capab, u.mgd.capab, HEX);
+IEEE80211_IF_FILE(extra_ie_len, u.mgd.extra_ie_len, SIZE);
+IEEE80211_IF_FILE(auth_tries, u.mgd.auth_tries, DEC);
+IEEE80211_IF_FILE(assoc_tries, u.mgd.assoc_tries, DEC);
+IEEE80211_IF_FILE(auth_algs, u.mgd.auth_algs, HEX);
+IEEE80211_IF_FILE(auth_alg, u.mgd.auth_alg, DEC);
+IEEE80211_IF_FILE(auth_transaction, u.mgd.auth_transaction, DEC);
static ssize_t ieee80211_if_fmt_flags(
const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
{
return scnprintf(buf, buflen, "%s%s%s%s%s%s%s\n",
- sdata->u.sta.flags & IEEE80211_STA_SSID_SET ? "SSID\n" : "",
- sdata->u.sta.flags & IEEE80211_STA_BSSID_SET ? "BSSID\n" : "",
- sdata->u.sta.flags & IEEE80211_STA_PREV_BSSID_SET ? "prev BSSID\n" : "",
- sdata->u.sta.flags & IEEE80211_STA_AUTHENTICATED ? "AUTH\n" : "",
- sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED ? "ASSOC\n" : "",
- sdata->u.sta.flags & IEEE80211_STA_PROBEREQ_POLL ? "PROBEREQ POLL\n" : "",
+ sdata->u.mgd.flags & IEEE80211_STA_SSID_SET ? "SSID\n" : "",
+ sdata->u.mgd.flags & IEEE80211_STA_BSSID_SET ? "BSSID\n" : "",
+ sdata->u.mgd.flags & IEEE80211_STA_PREV_BSSID_SET ? "prev BSSID\n" : "",
+ sdata->u.mgd.flags & IEEE80211_STA_AUTHENTICATED ? "AUTH\n" : "",
+ sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED ? "ASSOC\n" : "",
+ sdata->u.mgd.flags & IEEE80211_STA_PROBEREQ_POLL ? "PROBEREQ POLL\n" : "",
sdata->vif.bss_conf.use_cts_prot ? "CTS prot\n" : "");
}
__IEEE80211_IF_FILE(flags);
@@ -283,9 +283,11 @@ static void add_files(struct ieee80211_sub_if_data *sdata)
#endif
break;
case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_ADHOC:
add_sta_files(sdata);
break;
+ case NL80211_IFTYPE_ADHOC:
+ /* XXX */
+ break;
case NL80211_IFTYPE_AP:
add_ap_files(sdata);
break;
@@ -418,9 +420,11 @@ static void del_files(struct ieee80211_sub_if_data *sdata)
#endif
break;
case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_ADHOC:
del_sta_files(sdata);
break;
+ case NL80211_IFTYPE_ADHOC:
+ /* XXX */
+ break;
case NL80211_IFTYPE_AP:
del_ap_files(sdata);
break;
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 82ea0b63a38..4e3c72f20de 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -17,6 +17,7 @@
#include <net/wireless.h>
#include <net/mac80211.h>
#include "ieee80211_i.h"
+#include "rate.h"
void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband,
struct ieee80211_ht_cap *ht_cap_ie,
@@ -93,7 +94,9 @@ u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_bss_ht_conf ht;
+ struct sta_info *sta;
u32 changed = 0;
bool enable_ht = true, ht_changed;
enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
@@ -136,6 +139,16 @@ u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
if (ht_changed) {
/* channel_type change automatically detected */
ieee80211_hw_config(local, 0);
+
+ rcu_read_lock();
+
+ sta = sta_info_get(local, ifmgd->bssid);
+ if (sta)
+ rate_control_rate_update(local, sband, sta,
+ IEEE80211_RC_HT_CHANGED);
+
+ rcu_read_unlock();
+
}
/* disable HT */
@@ -169,7 +182,6 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
u16 initiator, u16 reason_code)
{
struct ieee80211_local *local = sdata->local;
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
u16 params;
@@ -190,8 +202,9 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
if (sdata->vif.type == NL80211_IFTYPE_AP ||
sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
memcpy(mgmt->bssid, sdata->dev->dev_addr, ETH_ALEN);
- else
- memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+ else if (sdata->vif.type == NL80211_IFTYPE_STATION)
+ memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN);
+
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ACTION);
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
new file mode 100644
index 00000000000..a96ce9dfc6b
--- /dev/null
+++ b/net/mac80211/ibss.c
@@ -0,0 +1,905 @@
+/*
+ * IBSS mode implementation
+ * Copyright 2003-2008, Jouni Malinen <j@w1.fi>
+ * Copyright 2004, Instant802 Networks, Inc.
+ * Copyright 2005, Devicescape Software, Inc.
+ * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
+ * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
+ * Copyright 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 <linux/delay.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+
+#include "ieee80211_i.h"
+#include "rate.h"
+
+#define IEEE80211_SCAN_INTERVAL (2 * HZ)
+#define IEEE80211_SCAN_INTERVAL_SLOW (15 * HZ)
+#define IEEE80211_IBSS_JOIN_TIMEOUT (7 * HZ)
+
+#define IEEE80211_IBSS_MERGE_INTERVAL (30 * HZ)
+#define IEEE80211_IBSS_MERGE_DELAY 0x400000
+#define IEEE80211_IBSS_INACTIVITY_LIMIT (60 * HZ)
+
+#define IEEE80211_IBSS_MAX_STA_ENTRIES 128
+
+
+static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ u16 auth_alg, auth_transaction, status_code;
+
+ if (len < 24 + 6)
+ return;
+
+ auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
+ auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
+ status_code = le16_to_cpu(mgmt->u.auth.status_code);
+
+ /*
+ * IEEE 802.11 standard does not require authentication in IBSS
+ * networks and most implementations do not seem to use it.
+ * However, try to reply to authentication attempts if someone
+ * has actually implemented this.
+ */
+ if (auth_alg == WLAN_AUTH_OPEN && auth_transaction == 1)
+ ieee80211_send_auth(sdata, 2, WLAN_AUTH_OPEN, NULL, 0,
+ sdata->u.ibss.bssid, 0);
+}
+
+static int __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
+ const u8 *bssid, const int beacon_int,
+ const int freq,
+ const size_t supp_rates_len,
+ const u8 *supp_rates,
+ const u16 capability)
+{
+ struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+ struct ieee80211_local *local = sdata->local;
+ int res = 0, rates, i, j;
+ struct sk_buff *skb;
+ struct ieee80211_mgmt *mgmt;
+ u8 *pos;
+ struct ieee80211_supported_band *sband;
+ union iwreq_data wrqu;
+
+ if (local->ops->reset_tsf) {
+ /* Reset own TSF to allow time synchronization work. */
+ local->ops->reset_tsf(local_to_hw(local));
+ }
+
+ if ((ifibss->flags & IEEE80211_IBSS_PREV_BSSID_SET) &&
+ memcmp(ifibss->bssid, bssid, ETH_ALEN) == 0)
+ return res;
+
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
+ if (!skb) {
+ printk(KERN_DEBUG "%s: failed to allocate buffer for probe "
+ "response\n", sdata->dev->name);
+ return -ENOMEM;
+ }
+
+ if (!(ifibss->flags & IEEE80211_IBSS_PREV_BSSID_SET)) {
+ /* Remove possible STA entries from other IBSS networks. */
+ sta_info_flush_delayed(sdata);
+ }
+
+ memcpy(ifibss->bssid, bssid, ETH_ALEN);
+ res = ieee80211_if_config(sdata, IEEE80211_IFCC_BSSID);
+ if (res)
+ return res;
+
+ local->hw.conf.beacon_int = beacon_int >= 10 ? beacon_int : 10;
+
+ sdata->drop_unencrypted = capability &
+ WLAN_CAPABILITY_PRIVACY ? 1 : 0;
+
+ res = ieee80211_set_freq(sdata, freq);
+
+ if (res)
+ return res;
+
+ sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+
+ /* Build IBSS probe response */
+
+ skb_reserve(skb, local->hw.extra_tx_headroom);
+
+ mgmt = (struct ieee80211_mgmt *)
+ skb_put(skb, 24 + sizeof(mgmt->u.beacon));
+ memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon));
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_PROBE_RESP);
+ memset(mgmt->da, 0xff, ETH_ALEN);
+ memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+ memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
+ mgmt->u.beacon.beacon_int =
+ cpu_to_le16(local->hw.conf.beacon_int);
+ mgmt->u.beacon.capab_info = cpu_to_le16(capability);
+
+ pos = skb_put(skb, 2 + ifibss->ssid_len);
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = ifibss->ssid_len;
+ memcpy(pos, ifibss->ssid, ifibss->ssid_len);
+
+ rates = supp_rates_len;
+ if (rates > 8)
+ rates = 8;
+ pos = skb_put(skb, 2 + rates);
+ *pos++ = WLAN_EID_SUPP_RATES;
+ *pos++ = rates;
+ memcpy(pos, supp_rates, rates);
+
+ if (sband->band == IEEE80211_BAND_2GHZ) {
+ pos = skb_put(skb, 2 + 1);
+ *pos++ = WLAN_EID_DS_PARAMS;
+ *pos++ = 1;
+ *pos++ = ieee80211_frequency_to_channel(freq);
+ }
+
+ pos = skb_put(skb, 2 + 2);
+ *pos++ = WLAN_EID_IBSS_PARAMS;
+ *pos++ = 2;
+ /* FIX: set ATIM window based on scan results */
+ *pos++ = 0;
+ *pos++ = 0;
+
+ if (supp_rates_len > 8) {
+ rates = supp_rates_len - 8;
+ pos = skb_put(skb, 2 + rates);
+ *pos++ = WLAN_EID_EXT_SUPP_RATES;
+ *pos++ = rates;
+ memcpy(pos, &supp_rates[8], rates);
+ }
+
+ ifibss->probe_resp = skb;
+
+ ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON |
+ IEEE80211_IFCC_BEACON_ENABLED);
+
+
+ rates = 0;
+ for (i = 0; i < supp_rates_len; i++) {
+ int bitrate = (supp_rates[i] & 0x7f) * 5;
+ for (j = 0; j < sband->n_bitrates; j++)
+ if (sband->bitrates[j].bitrate == bitrate)
+ rates |= BIT(j);
+ }
+
+ ieee80211_sta_def_wmm_params(sdata, supp_rates_len, supp_rates);
+
+ ifibss->flags |= IEEE80211_IBSS_PREV_BSSID_SET;
+ ifibss->state = IEEE80211_IBSS_MLME_JOINED;
+ mod_timer(&ifibss->timer, jiffies + IEEE80211_IBSS_MERGE_INTERVAL);
+
+ memset(&wrqu, 0, sizeof(wrqu));
+ memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
+ wireless_send_event(sdata->dev, SIOCGIWAP, &wrqu, NULL);
+
+ return res;
+}
+
+static int ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_bss *bss)
+{
+ return __ieee80211_sta_join_ibss(sdata,
+ bss->cbss.bssid,
+ bss->cbss.beacon_interval,
+ bss->cbss.channel->center_freq,
+ bss->supp_rates_len, bss->supp_rates,
+ bss->cbss.capability);
+}
+
+static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt,
+ size_t len,
+ struct ieee80211_rx_status *rx_status,
+ struct ieee802_11_elems *elems,
+ bool beacon)
+{
+ struct ieee80211_local *local = sdata->local;
+ int freq;
+ struct ieee80211_bss *bss;
+ struct sta_info *sta;
+ struct ieee80211_channel *channel;
+ u64 beacon_timestamp, rx_timestamp;
+ u32 supp_rates = 0;
+ enum ieee80211_band band = rx_status->band;
+
+ if (elems->ds_params && elems->ds_params_len == 1)
+ freq = ieee80211_channel_to_frequency(elems->ds_params[0]);
+ else
+ freq = rx_status->freq;
+
+ channel = ieee80211_get_channel(local->hw.wiphy, freq);
+
+ if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
+ return;
+
+ if (sdata->vif.type == NL80211_IFTYPE_ADHOC && elems->supp_rates &&
+ memcmp(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN) == 0) {
+ supp_rates = ieee80211_sta_get_rates(local, elems, band);
+
+ rcu_read_lock();
+
+ sta = sta_info_get(local, mgmt->sa);
+ if (sta) {
+ u32 prev_rates;
+
+ prev_rates = sta->sta.supp_rates[band];
+ /* make sure mandatory rates are always added */
+ sta->sta.supp_rates[band] = supp_rates |
+ ieee80211_mandatory_rates(local, band);
+
+#ifdef CONFIG_MAC80211_IBSS_DEBUG
+ if (sta->sta.supp_rates[band] != prev_rates)
+ printk(KERN_DEBUG "%s: updated supp_rates set "
+ "for %pM based on beacon info (0x%llx | "
+ "0x%llx -> 0x%llx)\n",
+ sdata->dev->name,
+ sta->sta.addr,
+ (unsigned long long) prev_rates,
+ (unsigned long long) supp_rates,
+ (unsigned long long) sta->sta.supp_rates[band]);
+#endif
+ } else
+ ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, supp_rates);
+
+ rcu_read_unlock();
+ }
+
+ bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
+ channel, beacon);
+ if (!bss)
+ return;
+
+ /* was just updated in ieee80211_bss_info_update */
+ beacon_timestamp = bss->cbss.tsf;
+
+ /* check if we need to merge IBSS */
+
+ /* merge only on beacons (???) */
+ if (!beacon)
+ goto put_bss;
+
+ /* we use a fixed BSSID */
+ if (sdata->u.ibss.flags & IEEE80211_IBSS_BSSID_SET)
+ goto put_bss;
+
+ /* not an IBSS */
+ if (!(bss->cbss.capability & WLAN_CAPABILITY_IBSS))
+ goto put_bss;
+
+ /* different channel */
+ if (bss->cbss.channel != local->oper_channel)
+ goto put_bss;
+
+ /* different SSID */
+ if (elems->ssid_len != sdata->u.ibss.ssid_len ||
+ memcmp(elems->ssid, sdata->u.ibss.ssid,
+ sdata->u.ibss.ssid_len))
+ goto put_bss;
+
+ /* same BSSID */
+ if (memcmp(bss->cbss.bssid, sdata->u.ibss.bssid, ETH_ALEN) == 0)
+ goto put_bss;
+
+ if (rx_status->flag & RX_FLAG_TSFT) {
+ /*
+ * For correct IBSS merging we need mactime; since mactime is
+ * defined as the time the first data symbol of the frame hits
+ * the PHY, and the timestamp of the beacon is defined as "the
+ * time that the data symbol containing the first bit of the
+ * timestamp is transmitted to the PHY plus the transmitting
+ * STA's delays through its local PHY from the MAC-PHY
+ * interface to its interface with the WM" (802.11 11.1.2)
+ * - equals the time this bit arrives at the receiver - we have
+ * to take into account the offset between the two.
+ *
+ * E.g. at 1 MBit that means mactime is 192 usec earlier
+ * (=24 bytes * 8 usecs/byte) than the beacon timestamp.
+ */
+ int rate;
+
+ if (rx_status->flag & RX_FLAG_HT)
+ rate = 65; /* TODO: HT rates */
+ else
+ rate = local->hw.wiphy->bands[band]->
+ bitrates[rx_status->rate_idx].bitrate;
+
+ rx_timestamp = rx_status->mactime + (24 * 8 * 10 / rate);
+ } else if (local && local->ops && local->ops->get_tsf)
+ /* second best option: get current TSF */
+ rx_timestamp = local->ops->get_tsf(local_to_hw(local));
+ else
+ /* can't merge without knowing the TSF */
+ rx_timestamp = -1LLU;
+
+#ifdef CONFIG_MAC80211_IBSS_DEBUG
+ printk(KERN_DEBUG "RX beacon SA=%pM BSSID="
+ "%pM TSF=0x%llx BCN=0x%llx diff=%lld @%lu\n",
+ mgmt->sa, mgmt->bssid,
+ (unsigned long long)rx_timestamp,
+ (unsigned long long)beacon_timestamp,
+ (unsigned long long)(rx_timestamp - beacon_timestamp),
+ jiffies);
+#endif
+
+ /* give slow hardware some time to do the TSF sync */
+ if (rx_timestamp < IEEE80211_IBSS_MERGE_DELAY)
+ goto put_bss;
+
+ if (beacon_timestamp > rx_timestamp) {
+#ifdef CONFIG_MAC80211_IBSS_DEBUG
+ printk(KERN_DEBUG "%s: beacon TSF higher than "
+ "local TSF - IBSS merge with BSSID %pM\n",
+ sdata->dev->name, mgmt->bssid);
+#endif
+ ieee80211_sta_join_ibss(sdata, bss);
+ ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, supp_rates);
+ }
+
+ put_bss:
+ ieee80211_rx_bss_put(local, bss);
+}
+
+/*
+ * Add a new IBSS station, will also be called by the RX code when,
+ * in IBSS mode, receiving a frame from a yet-unknown station, hence
+ * must be callable in atomic context.
+ */
+struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
+ u8 *bssid,u8 *addr, u32 supp_rates)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct sta_info *sta;
+ int band = local->hw.conf.channel->band;
+
+ /* TODO: Could consider removing the least recently used entry and
+ * allow new one to be added. */
+ if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) {
+ if (net_ratelimit()) {
+ printk(KERN_DEBUG "%s: No room for a new IBSS STA "
+ "entry %pM\n", sdata->dev->name, addr);
+ }
+ return NULL;
+ }
+
+ if (compare_ether_addr(bssid, sdata->u.ibss.bssid))
+ return NULL;
+
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+ printk(KERN_DEBUG "%s: Adding new IBSS station %pM (dev=%s)\n",
+ wiphy_name(local->hw.wiphy), addr, sdata->dev->name);
+#endif
+
+ sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
+ if (!sta)
+ return NULL;
+
+ set_sta_flags(sta, WLAN_STA_AUTHORIZED);
+
+ /* make sure mandatory rates are always added */
+ sta->sta.supp_rates[band] = supp_rates |
+ ieee80211_mandatory_rates(local, band);
+
+ rate_control_rate_init(sta);
+
+ if (sta_info_insert(sta))
+ return NULL;
+
+ return sta;
+}
+
+static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ int active = 0;
+ struct sta_info *sta;
+
+ rcu_read_lock();
+
+ list_for_each_entry_rcu(sta, &local->sta_list, list) {
+ if (sta->sdata == sdata &&
+ time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL,
+ jiffies)) {
+ active++;
+ break;
+ }
+ }
+
+ rcu_read_unlock();
+
+ return active;
+}
+
+
+static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+
+ mod_timer(&ifibss->timer, jiffies + IEEE80211_IBSS_MERGE_INTERVAL);
+
+ ieee80211_sta_expire(sdata, IEEE80211_IBSS_INACTIVITY_LIMIT);
+ if (ieee80211_sta_active_ibss(sdata))
+ return;
+
+ if ((ifibss->flags & IEEE80211_IBSS_BSSID_SET) &&
+ (!(ifibss->flags & IEEE80211_IBSS_AUTO_CHANNEL_SEL)))
+ return;
+
+ printk(KERN_DEBUG "%s: No active IBSS STAs - trying to scan for other "
+ "IBSS networks with same SSID (merge)\n", sdata->dev->name);
+
+ /* XXX maybe racy? */
+ if (sdata->local->scan_req)
+ return;
+
+ memcpy(sdata->local->int_scan_req.ssids[0].ssid,
+ ifibss->ssid, IEEE80211_MAX_SSID_LEN);
+ sdata->local->int_scan_req.ssids[0].ssid_len = ifibss->ssid_len;
+ ieee80211_request_scan(sdata, &sdata->local->int_scan_req);
+}
+
+static int ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_supported_band *sband;
+ u8 *pos;
+ u8 bssid[ETH_ALEN];
+ u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
+ u16 capability;
+ int i;
+
+ if (ifibss->flags & IEEE80211_IBSS_BSSID_SET) {
+ memcpy(bssid, ifibss->bssid, ETH_ALEN);
+ } else {
+ /* Generate random, not broadcast, locally administered BSSID. Mix in
+ * own MAC address to make sure that devices that do not have proper
+ * random number generator get different BSSID. */
+ get_random_bytes(bssid, ETH_ALEN);
+ for (i = 0; i < ETH_ALEN; i++)
+ bssid[i] ^= sdata->dev->dev_addr[i];
+ bssid[0] &= ~0x01;
+ bssid[0] |= 0x02;
+ }
+
+ printk(KERN_DEBUG "%s: Creating new IBSS network, BSSID %pM\n",
+ sdata->dev->name, bssid);
+
+ sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+
+ if (local->hw.conf.beacon_int == 0)
+ local->hw.conf.beacon_int = 100;
+
+ capability = WLAN_CAPABILITY_IBSS;
+
+ if (sdata->default_key)
+ capability |= WLAN_CAPABILITY_PRIVACY;
+ else
+ sdata->drop_unencrypted = 0;
+
+ pos = supp_rates;
+ for (i = 0; i < sband->n_bitrates; i++) {
+ int rate = sband->bitrates[i].bitrate;
+ *pos++ = (u8) (rate / 5);
+ }
+
+ return __ieee80211_sta_join_ibss(sdata,
+ bssid, local->hw.conf.beacon_int,
+ local->hw.conf.channel->center_freq,
+ sband->n_bitrates, supp_rates,
+ capability);
+}
+
+static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_bss *bss;
+ const u8 *bssid = NULL;
+ int active_ibss;
+
+ if (ifibss->ssid_len == 0)
+ return -EINVAL;
+
+ active_ibss = ieee80211_sta_active_ibss(sdata);
+#ifdef CONFIG_MAC80211_IBSS_DEBUG
+ printk(KERN_DEBUG "%s: sta_find_ibss (active_ibss=%d)\n",
+ sdata->dev->name, active_ibss);
+#endif /* CONFIG_MAC80211_IBSS_DEBUG */
+
+ if (active_ibss)
+ return 0;
+
+ if (ifibss->flags & IEEE80211_IBSS_BSSID_SET)
+ bssid = ifibss->bssid;
+ bss = (void *)cfg80211_get_bss(local->hw.wiphy, NULL, bssid,
+ ifibss->ssid, ifibss->ssid_len,
+ WLAN_CAPABILITY_IBSS,
+ WLAN_CAPABILITY_IBSS);
+
+#ifdef CONFIG_MAC80211_IBSS_DEBUG
+ if (bss)
+ printk(KERN_DEBUG " sta_find_ibss: selected %pM current "
+ "%pM\n", bss->cbss.bssid, ifibss->bssid);
+#endif /* CONFIG_MAC80211_IBSS_DEBUG */
+
+ if (bss &&
+ (!(ifibss->flags & IEEE80211_IBSS_PREV_BSSID_SET) ||
+ memcmp(ifibss->bssid, bss->cbss.bssid, ETH_ALEN))) {
+ int ret;
+
+ printk(KERN_DEBUG "%s: Selected IBSS BSSID %pM"
+ " based on configured SSID\n",
+ sdata->dev->name, bss->cbss.bssid);
+
+ ret = ieee80211_sta_join_ibss(sdata, bss);
+ ieee80211_rx_bss_put(local, bss);
+ return ret;
+ } else if (bss)
+ ieee80211_rx_bss_put(local, bss);
+
+#ifdef CONFIG_MAC80211_IBSS_DEBUG
+ printk(KERN_DEBUG " did not try to join ibss\n");
+#endif /* CONFIG_MAC80211_IBSS_DEBUG */
+
+ /* Selected IBSS not found in current scan results - try to scan */
+ if (ifibss->state == IEEE80211_IBSS_MLME_JOINED &&
+ !ieee80211_sta_active_ibss(sdata)) {
+ mod_timer(&ifibss->timer, jiffies +
+ IEEE80211_IBSS_MERGE_INTERVAL);
+ } else if (time_after(jiffies, local->last_scan_completed +
+ IEEE80211_SCAN_INTERVAL)) {
+ printk(KERN_DEBUG "%s: Trigger new scan to find an IBSS to "
+ "join\n", sdata->dev->name);
+
+ /* XXX maybe racy? */
+ if (local->scan_req)
+ return -EBUSY;
+
+ memcpy(local->int_scan_req.ssids[0].ssid,
+ ifibss->ssid, IEEE80211_MAX_SSID_LEN);
+ local->int_scan_req.ssids[0].ssid_len = ifibss->ssid_len;
+ return ieee80211_request_scan(sdata, &local->int_scan_req);
+ } else if (ifibss->state != IEEE80211_IBSS_MLME_JOINED) {
+ int interval = IEEE80211_SCAN_INTERVAL;
+
+ if (time_after(jiffies, ifibss->ibss_join_req +
+ IEEE80211_IBSS_JOIN_TIMEOUT)) {
+ if (!(local->oper_channel->flags &
+ IEEE80211_CHAN_NO_IBSS))
+ return ieee80211_sta_create_ibss(sdata);
+ printk(KERN_DEBUG "%s: IBSS not allowed on"
+ " %d MHz\n", sdata->dev->name,
+ local->hw.conf.channel->center_freq);
+
+ /* No IBSS found - decrease scan interval and continue
+ * scanning. */
+ interval = IEEE80211_SCAN_INTERVAL_SLOW;
+ }
+
+ ifibss->state = IEEE80211_IBSS_MLME_SEARCH;
+ mod_timer(&ifibss->timer, jiffies + interval);
+ return 0;
+ }
+
+ return 0;
+}
+
+static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+ struct ieee80211_local *local = sdata->local;
+ int tx_last_beacon;
+ struct sk_buff *skb;
+ struct ieee80211_mgmt *resp;
+ u8 *pos, *end;
+
+ if (ifibss->state != IEEE80211_IBSS_MLME_JOINED ||
+ len < 24 + 2 || !ifibss->probe_resp)
+ return;
+
+ if (local->ops->tx_last_beacon)
+ tx_last_beacon = local->ops->tx_last_beacon(local_to_hw(local));
+ else
+ tx_last_beacon = 1;
+
+#ifdef CONFIG_MAC80211_IBSS_DEBUG
+ printk(KERN_DEBUG "%s: RX ProbeReq SA=%pM DA=%pM BSSID=%pM"
+ " (tx_last_beacon=%d)\n",
+ sdata->dev->name, mgmt->sa, mgmt->da,
+ mgmt->bssid, tx_last_beacon);
+#endif /* CONFIG_MAC80211_IBSS_DEBUG */
+
+ if (!tx_last_beacon)
+ return;
+
+ if (memcmp(mgmt->bssid, ifibss->bssid, ETH_ALEN) != 0 &&
+ memcmp(mgmt->bssid, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0)
+ return;
+
+ end = ((u8 *) mgmt) + len;
+ pos = mgmt->u.probe_req.variable;
+ if (pos[0] != WLAN_EID_SSID ||
+ pos + 2 + pos[1] > end) {
+#ifdef CONFIG_MAC80211_IBSS_DEBUG
+ printk(KERN_DEBUG "%s: Invalid SSID IE in ProbeReq "
+ "from %pM\n",
+ sdata->dev->name, mgmt->sa);
+#endif
+ return;
+ }
+ if (pos[1] != 0 &&
+ (pos[1] != ifibss->ssid_len ||
+ memcmp(pos + 2, ifibss->ssid, ifibss->ssid_len) != 0)) {
+ /* Ignore ProbeReq for foreign SSID */
+ return;
+ }
+
+ /* Reply with ProbeResp */
+ skb = skb_copy(ifibss->probe_resp, GFP_KERNEL);
+ if (!skb)
+ return;
+
+ resp = (struct ieee80211_mgmt *) skb->data;
+ memcpy(resp->da, mgmt->sa, ETH_ALEN);
+#ifdef CONFIG_MAC80211_IBSS_DEBUG
+ 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);
+}
+
+static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt,
+ size_t len,
+ struct ieee80211_rx_status *rx_status)
+{
+ size_t baselen;
+ struct ieee802_11_elems elems;
+
+ if (memcmp(mgmt->da, sdata->dev->dev_addr, ETH_ALEN))
+ return; /* ignore ProbeResp to foreign address */
+
+ baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt;
+ if (baselen > len)
+ return;
+
+ ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
+ &elems);
+
+ ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false);
+}
+
+static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt,
+ size_t len,
+ struct ieee80211_rx_status *rx_status)
+{
+ size_t baselen;
+ struct ieee802_11_elems elems;
+
+ /* Process beacon from the current BSS */
+ baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
+ if (baselen > len)
+ return;
+
+ ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, &elems);
+
+ ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, true);
+}
+
+static void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb)
+{
+ struct ieee80211_rx_status *rx_status;
+ struct ieee80211_mgmt *mgmt;
+ u16 fc;
+
+ rx_status = (struct ieee80211_rx_status *) skb->cb;
+ mgmt = (struct ieee80211_mgmt *) skb->data;
+ fc = le16_to_cpu(mgmt->frame_control);
+
+ switch (fc & IEEE80211_FCTL_STYPE) {
+ case IEEE80211_STYPE_PROBE_REQ:
+ ieee80211_rx_mgmt_probe_req(sdata, mgmt, skb->len);
+ break;
+ case IEEE80211_STYPE_PROBE_RESP:
+ ieee80211_rx_mgmt_probe_resp(sdata, mgmt, skb->len,
+ rx_status);
+ break;
+ case IEEE80211_STYPE_BEACON:
+ ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len,
+ rx_status);
+ break;
+ case IEEE80211_STYPE_AUTH:
+ ieee80211_rx_mgmt_auth_ibss(sdata, mgmt, skb->len);
+ break;
+ }
+
+ kfree_skb(skb);
+}
+
+static void ieee80211_ibss_work(struct work_struct *work)
+{
+ struct ieee80211_sub_if_data *sdata =
+ container_of(work, struct ieee80211_sub_if_data, u.ibss.work);
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_ibss *ifibss;
+ struct sk_buff *skb;
+
+ if (!netif_running(sdata->dev))
+ return;
+
+ if (local->sw_scanning || local->hw_scanning)
+ return;
+
+ if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_ADHOC))
+ return;
+ ifibss = &sdata->u.ibss;
+
+ while ((skb = skb_dequeue(&ifibss->skb_queue)))
+ ieee80211_ibss_rx_queued_mgmt(sdata, skb);
+
+ if (!test_and_clear_bit(IEEE80211_IBSS_REQ_RUN, &ifibss->request))
+ return;
+
+ switch (ifibss->state) {
+ case IEEE80211_IBSS_MLME_SEARCH:
+ ieee80211_sta_find_ibss(sdata);
+ break;
+ case IEEE80211_IBSS_MLME_JOINED:
+ ieee80211_sta_merge_ibss(sdata);
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
+}
+
+static void ieee80211_ibss_timer(unsigned long data)
+{
+ struct ieee80211_sub_if_data *sdata =
+ (struct ieee80211_sub_if_data *) data;
+ struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+ struct ieee80211_local *local = sdata->local;
+
+ set_bit(IEEE80211_IBSS_REQ_RUN, &ifibss->request);
+ queue_work(local->hw.workqueue, &ifibss->work);
+}
+
+void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+
+ INIT_WORK(&ifibss->work, ieee80211_ibss_work);
+ setup_timer(&ifibss->timer, ieee80211_ibss_timer,
+ (unsigned long) sdata);
+ skb_queue_head_init(&ifibss->skb_queue);
+
+ ifibss->flags |= IEEE80211_IBSS_AUTO_BSSID_SEL |
+ IEEE80211_IBSS_AUTO_CHANNEL_SEL;
+}
+
+int ieee80211_ibss_commit(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+
+ ifibss->flags &= ~IEEE80211_IBSS_PREV_BSSID_SET;
+
+ if (ifibss->ssid_len)
+ ifibss->flags |= IEEE80211_IBSS_SSID_SET;
+ else
+ ifibss->flags &= ~IEEE80211_IBSS_SSID_SET;
+
+ ifibss->ibss_join_req = jiffies;
+ ifibss->state = IEEE80211_IBSS_MLME_SEARCH;
+
+ return ieee80211_sta_find_ibss(sdata);
+}
+
+int ieee80211_ibss_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t len)
+{
+ struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+
+ if (len > IEEE80211_MAX_SSID_LEN)
+ return -EINVAL;
+
+ if (ifibss->ssid_len != len || memcmp(ifibss->ssid, ssid, len) != 0) {
+ memset(ifibss->ssid, 0, sizeof(ifibss->ssid));
+ memcpy(ifibss->ssid, ssid, len);
+ ifibss->ssid_len = len;
+ }
+
+ return ieee80211_ibss_commit(sdata);
+}
+
+int ieee80211_ibss_get_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t *len)
+{
+ struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+
+ memcpy(ssid, ifibss->ssid, ifibss->ssid_len);
+ *len = ifibss->ssid_len;
+
+ return 0;
+}
+
+int ieee80211_ibss_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid)
+{
+ struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+
+ if (is_valid_ether_addr(bssid)) {
+ memcpy(ifibss->bssid, bssid, ETH_ALEN);
+ ifibss->flags |= IEEE80211_IBSS_BSSID_SET;
+ } else {
+ memset(ifibss->bssid, 0, ETH_ALEN);
+ ifibss->flags &= ~IEEE80211_IBSS_BSSID_SET;
+ }
+
+ if (netif_running(sdata->dev)) {
+ if (ieee80211_if_config(sdata, IEEE80211_IFCC_BSSID)) {
+ printk(KERN_DEBUG "%s: Failed to config new BSSID to "
+ "the low-level driver\n", sdata->dev->name);
+ }
+ }
+
+ return ieee80211_ibss_commit(sdata);
+}
+
+/* scan finished notification */
+void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local)
+{
+ struct ieee80211_sub_if_data *sdata = local->scan_sdata;
+ struct ieee80211_if_ibss *ifibss;
+
+ if (sdata && sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+ ifibss = &sdata->u.ibss;
+ if ((!(ifibss->flags & IEEE80211_IBSS_PREV_BSSID_SET)) ||
+ !ieee80211_sta_active_ibss(sdata))
+ ieee80211_sta_find_ibss(sdata);
+ }
+}
+
+ieee80211_rx_result
+ieee80211_ibss_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
+ struct ieee80211_rx_status *rx_status)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_mgmt *mgmt;
+ u16 fc;
+
+ if (skb->len < 24)
+ return RX_DROP_MONITOR;
+
+ mgmt = (struct ieee80211_mgmt *) skb->data;
+ fc = le16_to_cpu(mgmt->frame_control);
+
+ switch (fc & IEEE80211_FCTL_STYPE) {
+ case IEEE80211_STYPE_PROBE_RESP:
+ case IEEE80211_STYPE_BEACON:
+ memcpy(skb->cb, rx_status, sizeof(*rx_status));
+ case IEEE80211_STYPE_PROBE_REQ:
+ case IEEE80211_STYPE_AUTH:
+ skb_queue_tail(&sdata->u.ibss.skb_queue, skb);
+ queue_work(local->hw.workqueue, &sdata->u.ibss.work);
+ return RX_QUEUED;
+ }
+
+ return RX_DROP_MONITOR;
+}
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 2cb743ed9f9..ecbc8e0cb3e 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -239,7 +239,7 @@ struct mesh_preq_queue {
u8 flags;
};
-/* flags used in struct ieee80211_if_sta.flags */
+/* flags used in struct ieee80211_if_managed.flags */
#define IEEE80211_STA_SSID_SET BIT(0)
#define IEEE80211_STA_BSSID_SET BIT(1)
#define IEEE80211_STA_PREV_BSSID_SET BIT(2)
@@ -262,31 +262,30 @@ struct mesh_preq_queue {
#define IEEE80211_STA_REQ_AUTH 2
#define IEEE80211_STA_REQ_RUN 3
-/* STA/IBSS MLME states */
-enum ieee80211_sta_mlme_state {
- IEEE80211_STA_MLME_DISABLED,
- IEEE80211_STA_MLME_DIRECT_PROBE,
- IEEE80211_STA_MLME_AUTHENTICATE,
- IEEE80211_STA_MLME_ASSOCIATE,
- IEEE80211_STA_MLME_ASSOCIATED,
- IEEE80211_STA_MLME_IBSS_SEARCH,
- IEEE80211_STA_MLME_IBSS_JOINED,
-};
-
/* bitfield of allowed auth algs */
#define IEEE80211_AUTH_ALG_OPEN BIT(0)
#define IEEE80211_AUTH_ALG_SHARED_KEY BIT(1)
#define IEEE80211_AUTH_ALG_LEAP BIT(2)
-struct ieee80211_if_sta {
+struct ieee80211_if_managed {
struct timer_list timer;
struct timer_list chswitch_timer;
struct work_struct work;
struct work_struct chswitch_work;
+
u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
+
u8 ssid[IEEE80211_MAX_SSID_LEN];
- enum ieee80211_sta_mlme_state state;
size_t ssid_len;
+
+ enum {
+ IEEE80211_STA_MLME_DISABLED,
+ IEEE80211_STA_MLME_DIRECT_PROBE,
+ IEEE80211_STA_MLME_AUTHENTICATE,
+ IEEE80211_STA_MLME_ASSOCIATE,
+ IEEE80211_STA_MLME_ASSOCIATED,
+ } state;
+
u16 aid;
u16 ap_capab, capab;
u8 *extra_ie; /* to be added to the end of AssocReq */
@@ -319,10 +318,6 @@ struct ieee80211_if_sta {
IEEE80211_MFP_REQUIRED
} mfp; /* management frame protection */
- unsigned long ibss_join_req;
- struct sk_buff *probe_resp; /* ProbeResp template for IBSS */
- u32 supp_rates_bits[IEEE80211_NUM_BANDS];
-
int wmm_last_param_set;
/* Extra IE data for management frames */
@@ -342,6 +337,42 @@ struct ieee80211_if_sta {
size_t ie_disassoc_len;
};
+enum ieee80211_ibss_flags {
+ IEEE80211_IBSS_AUTO_CHANNEL_SEL = BIT(0),
+ IEEE80211_IBSS_AUTO_BSSID_SEL = BIT(1),
+ IEEE80211_IBSS_BSSID_SET = BIT(2),
+ IEEE80211_IBSS_PREV_BSSID_SET = BIT(3),
+ IEEE80211_IBSS_SSID_SET = BIT(4),
+};
+
+enum ieee80211_ibss_request {
+ IEEE80211_IBSS_REQ_RUN = 0,
+};
+
+struct ieee80211_if_ibss {
+ struct timer_list timer;
+ struct work_struct work;
+
+ struct sk_buff_head skb_queue;
+
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+ u8 ssid_len;
+
+ u32 flags;
+
+ u8 bssid[ETH_ALEN];
+
+ unsigned long request;
+
+ unsigned long ibss_join_req;
+ struct sk_buff *probe_resp; /* ProbeResp template for IBSS */
+
+ enum {
+ IEEE80211_IBSS_MLME_SEARCH,
+ IEEE80211_IBSS_MLME_JOINED,
+ } state;
+};
+
struct ieee80211_if_mesh {
struct work_struct work;
struct timer_list housekeeping_timer;
@@ -445,7 +476,8 @@ struct ieee80211_sub_if_data {
struct ieee80211_if_ap ap;
struct ieee80211_if_wds wds;
struct ieee80211_if_vlan vlan;
- struct ieee80211_if_sta sta;
+ struct ieee80211_if_managed mgd;
+ struct ieee80211_if_ibss ibss;
#ifdef CONFIG_MAC80211_MESH
struct ieee80211_if_mesh mesh;
#endif
@@ -564,12 +596,10 @@ enum {
enum queue_stop_reason {
IEEE80211_QUEUE_STOP_REASON_DRIVER,
IEEE80211_QUEUE_STOP_REASON_PS,
- IEEE80211_QUEUE_STOP_REASON_CSA
+ IEEE80211_QUEUE_STOP_REASON_CSA,
+ IEEE80211_QUEUE_STOP_REASON_AGGREGATION,
};
-/* maximum number of hardware queues we support. */
-#define QD_MAX_QUEUES (IEEE80211_MAX_AMPDU_QUEUES + IEEE80211_MAX_QUEUES)
-
struct ieee80211_master_priv {
struct ieee80211_local *local;
};
@@ -582,9 +612,15 @@ struct ieee80211_local {
const struct ieee80211_ops *ops;
- unsigned long queue_pool[BITS_TO_LONGS(QD_MAX_QUEUES)];
- unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES];
+ /* AC queue corresponding to each AMPDU queue */
+ s8 ampdu_ac_queue[IEEE80211_MAX_AMPDU_QUEUES];
+ unsigned int amdpu_ac_stop_refcnt[IEEE80211_MAX_AMPDU_QUEUES];
+
+ unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES +
+ IEEE80211_MAX_AMPDU_QUEUES];
+ /* also used to protect ampdu_ac_queue and amdpu_ac_stop_refcnt */
spinlock_t queue_stop_reason_lock;
+
struct net_device *mdev; /* wmaster# - "master" 802.11 device */
int open_count;
int monitors, cooked_mntrs;
@@ -888,34 +924,41 @@ void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx);
void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
u32 changed);
void ieee80211_configure_filter(struct ieee80211_local *local);
+u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata);
/* wireless extensions */
extern const struct iw_handler_def ieee80211_iw_handler_def;
-/* STA/IBSS code */
+/* STA code */
void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata);
-void ieee80211_scan_work(struct work_struct *work);
-void ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
- struct ieee80211_rx_status *rx_status);
+ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb,
+ struct ieee80211_rx_status *rx_status);
+int ieee80211_sta_commit(struct ieee80211_sub_if_data *sdata);
int ieee80211_sta_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t len);
int ieee80211_sta_get_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t *len);
int ieee80211_sta_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid);
-void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta);
-struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
- u8 *bssid, u8 *addr, u32 supp_rates);
+void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata);
int ieee80211_sta_deauthenticate(struct ieee80211_sub_if_data *sdata, u16 reason);
int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason);
-u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata);
-u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
- struct ieee802_11_elems *elems,
- enum ieee80211_band band);
-void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
- u8 *ssid, size_t ssid_len);
void ieee80211_send_pspoll(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata);
+/* IBSS code */
+int ieee80211_ibss_commit(struct ieee80211_sub_if_data *sdata);
+int ieee80211_ibss_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t len);
+int ieee80211_ibss_get_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t *len);
+int ieee80211_ibss_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid);
+void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local);
+void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata);
+ieee80211_rx_result
+ieee80211_ibss_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
+ struct ieee80211_rx_status *rx_status);
+struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
+ u8 *bssid, u8 *addr, u32 supp_rates);
+
/* scan/BSS handling */
+void ieee80211_scan_work(struct work_struct *work);
int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
struct cfg80211_scan_request *req);
int ieee80211_scan_results(struct ieee80211_local *local,
@@ -1042,6 +1085,25 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
enum queue_stop_reason reason);
void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
enum queue_stop_reason reason);
+void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
+ enum queue_stop_reason reason);
+void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
+ enum queue_stop_reason reason);
+
+void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
+ u16 transaction, u16 auth_alg,
+ u8 *extra, size_t extra_len,
+ const u8 *bssid, int encrypt);
+void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
+ u8 *ssid, size_t ssid_len,
+ u8 *ie, size_t ie_len);
+
+void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
+ const size_t supp_rates_len,
+ const u8 *supp_rates);
+u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
+ struct ieee802_11_elems *elems,
+ enum ieee80211_band band);
#ifdef CONFIG_MAC80211_NOINLINE
#define debug_noinline noinline
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index df94b936526..2acc416e77e 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -236,7 +236,10 @@ static int ieee80211_open(struct net_device *dev)
break;
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_ADHOC:
- sdata->u.sta.flags &= ~IEEE80211_STA_PREV_BSSID_SET;
+ if (sdata->vif.type == NL80211_IFTYPE_STATION)
+ sdata->u.mgd.flags &= ~IEEE80211_STA_PREV_BSSID_SET;
+ else
+ sdata->u.ibss.flags &= ~IEEE80211_IBSS_PREV_BSSID_SET;
/* fall through */
default:
conf.vif = &sdata->vif;
@@ -321,11 +324,10 @@ static int ieee80211_open(struct net_device *dev)
* yet be effective. Trigger execution of ieee80211_sta_work
* to fix this.
*/
- if (sdata->vif.type == NL80211_IFTYPE_STATION ||
- sdata->vif.type == NL80211_IFTYPE_ADHOC) {
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
- queue_work(local->hw.workqueue, &ifsta->work);
- }
+ if (sdata->vif.type == NL80211_IFTYPE_STATION)
+ queue_work(local->hw.workqueue, &sdata->u.mgd.work);
+ else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+ queue_work(local->hw.workqueue, &sdata->u.ibss.work);
netif_tx_start_all_queues(dev);
@@ -452,15 +454,13 @@ static int ieee80211_stop(struct net_device *dev)
netif_addr_unlock_bh(local->mdev);
break;
case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_ADHOC:
/* Announce that we are leaving the network. */
- if (sdata->u.sta.state != IEEE80211_STA_MLME_DISABLED)
+ if (sdata->u.mgd.state != IEEE80211_STA_MLME_DISABLED)
ieee80211_sta_deauthenticate(sdata,
WLAN_REASON_DEAUTH_LEAVING);
-
- memset(sdata->u.sta.bssid, 0, ETH_ALEN);
- del_timer_sync(&sdata->u.sta.chswitch_timer);
- del_timer_sync(&sdata->u.sta.timer);
+ memset(sdata->u.mgd.bssid, 0, ETH_ALEN);
+ del_timer_sync(&sdata->u.mgd.chswitch_timer);
+ del_timer_sync(&sdata->u.mgd.timer);
/*
* If the timer fired while we waited for it, it will have
* requeued the work. Now the work will be running again
@@ -468,8 +468,8 @@ static int ieee80211_stop(struct net_device *dev)
* whether the interface is running, which, at this point,
* it no longer is.
*/
- cancel_work_sync(&sdata->u.sta.work);
- cancel_work_sync(&sdata->u.sta.chswitch_work);
+ cancel_work_sync(&sdata->u.mgd.work);
+ cancel_work_sync(&sdata->u.mgd.chswitch_work);
/*
* When we get here, the interface is marked down.
* Call synchronize_rcu() to wait for the RX path
@@ -477,13 +477,22 @@ static int ieee80211_stop(struct net_device *dev)
* frames at this very time on another CPU.
*/
synchronize_rcu();
- skb_queue_purge(&sdata->u.sta.skb_queue);
+ skb_queue_purge(&sdata->u.mgd.skb_queue);
- sdata->u.sta.flags &= ~(IEEE80211_STA_PRIVACY_INVOKED |
+ sdata->u.mgd.flags &= ~(IEEE80211_STA_PRIVACY_INVOKED |
IEEE80211_STA_TKIP_WEP_USED);
- kfree(sdata->u.sta.extra_ie);
- sdata->u.sta.extra_ie = NULL;
- sdata->u.sta.extra_ie_len = 0;
+ kfree(sdata->u.mgd.extra_ie);
+ sdata->u.mgd.extra_ie = NULL;
+ sdata->u.mgd.extra_ie_len = 0;
+ /* fall through */
+ case NL80211_IFTYPE_ADHOC:
+ if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+ memset(sdata->u.ibss.bssid, 0, ETH_ALEN);
+ del_timer_sync(&sdata->u.ibss.timer);
+ cancel_work_sync(&sdata->u.ibss.work);
+ synchronize_rcu();
+ skb_queue_purge(&sdata->u.ibss.skb_queue);
+ }
/* fall through */
case NL80211_IFTYPE_MESH_POINT:
if (ieee80211_vif_is_mesh(&sdata->vif)) {
@@ -629,19 +638,20 @@ static void ieee80211_teardown_sdata(struct net_device *dev)
if (ieee80211_vif_is_mesh(&sdata->vif))
mesh_rmc_free(sdata);
break;
- case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_ADHOC:
- kfree(sdata->u.sta.extra_ie);
- kfree(sdata->u.sta.assocreq_ies);
- kfree(sdata->u.sta.assocresp_ies);
- kfree_skb(sdata->u.sta.probe_resp);
- kfree(sdata->u.sta.ie_probereq);
- kfree(sdata->u.sta.ie_proberesp);
- kfree(sdata->u.sta.ie_auth);
- kfree(sdata->u.sta.ie_assocreq);
- kfree(sdata->u.sta.ie_reassocreq);
- kfree(sdata->u.sta.ie_deauth);
- kfree(sdata->u.sta.ie_disassoc);
+ kfree_skb(sdata->u.ibss.probe_resp);
+ break;
+ case NL80211_IFTYPE_STATION:
+ kfree(sdata->u.mgd.extra_ie);
+ kfree(sdata->u.mgd.assocreq_ies);
+ kfree(sdata->u.mgd.assocresp_ies);
+ kfree(sdata->u.mgd.ie_probereq);
+ kfree(sdata->u.mgd.ie_proberesp);
+ kfree(sdata->u.mgd.ie_auth);
+ kfree(sdata->u.mgd.ie_assocreq);
+ kfree(sdata->u.mgd.ie_reassocreq);
+ kfree(sdata->u.mgd.ie_deauth);
+ kfree(sdata->u.mgd.ie_disassoc);
break;
case NL80211_IFTYPE_WDS:
case NL80211_IFTYPE_AP_VLAN:
@@ -708,9 +718,11 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
INIT_LIST_HEAD(&sdata->u.ap.vlans);
break;
case NL80211_IFTYPE_STATION:
- case NL80211_IFTYPE_ADHOC:
ieee80211_sta_setup_sdata(sdata);
break;
+ case NL80211_IFTYPE_ADHOC:
+ ieee80211_ibss_setup_sdata(sdata);
+ break;
case NL80211_IFTYPE_MESH_POINT:
if (ieee80211_vif_is_mesh(&sdata->vif))
ieee80211_mesh_init_sdata(sdata);
@@ -798,6 +810,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
memcpy(ndev->dev_addr, local->hw.wiphy->perm_addr, ETH_ALEN);
SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy));
+ ndev->features |= NETIF_F_NETNS_LOCAL;
/* don't use IEEE80211_DEV_TO_SUB_IF because it checks too much */
sdata = netdev_priv(ndev);
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 19b480de4bb..687acf23054 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -400,7 +400,7 @@ void ieee80211_key_link(struct ieee80211_key *key,
*/
/* same here, the AP could be using QoS */
- ap = sta_info_get(key->local, key->sdata->u.sta.bssid);
+ ap = sta_info_get(key->local, key->sdata->u.mgd.bssid);
if (ap) {
if (test_sta_flags(ap, WLAN_STA_WME))
key->conf.flags |=
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 5667f4e8067..f38db4d37e5 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -169,9 +169,10 @@ int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed)
memset(&conf, 0, sizeof(conf));
- if (sdata->vif.type == NL80211_IFTYPE_STATION ||
- sdata->vif.type == NL80211_IFTYPE_ADHOC)
- conf.bssid = sdata->u.sta.bssid;
+ if (sdata->vif.type == NL80211_IFTYPE_STATION)
+ conf.bssid = sdata->u.mgd.bssid;
+ else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+ conf.bssid = sdata->u.ibss.bssid;
else if (sdata->vif.type == NL80211_IFTYPE_AP)
conf.bssid = sdata->dev->dev_addr;
else if (ieee80211_vif_is_mesh(&sdata->vif)) {
@@ -210,7 +211,7 @@ int ieee80211_if_config(struct ieee80211_sub_if_data *sdata, u32 changed)
!!rcu_dereference(sdata->u.ap.beacon);
break;
case NL80211_IFTYPE_ADHOC:
- conf.enable_beacon = !!sdata->u.sta.probe_resp;
+ conf.enable_beacon = !!sdata->u.ibss.probe_resp;
break;
case NL80211_IFTYPE_MESH_POINT:
conf.enable_beacon = true;
@@ -705,7 +706,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
const struct ieee80211_ops *ops)
{
struct ieee80211_local *local;
- int priv_size;
+ int priv_size, i;
struct wiphy *wiphy;
/* Ensure 32-byte alignment of our private data and hw private data.
@@ -779,6 +780,11 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
setup_timer(&local->dynamic_ps_timer,
ieee80211_dynamic_ps_timer, (unsigned long) local);
+ for (i = 0; i < IEEE80211_MAX_AMPDU_QUEUES; i++)
+ local->ampdu_ac_queue[i] = -1;
+ /* using an s8 won't work with more than that */
+ BUILD_BUG_ON(IEEE80211_MAX_AMPDU_QUEUES > 127);
+
sta_info_init(local);
tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending,
@@ -855,6 +861,11 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
/* mac80211 always supports monitor */
local->hw.wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
+ if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
+ local->hw.wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+ else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)
+ local->hw.wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC;
+
result = wiphy_register(local->hw.wiphy);
if (result < 0)
goto fail_wiphy_register;
@@ -872,7 +883,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
mdev = alloc_netdev_mq(sizeof(struct ieee80211_master_priv),
"wmaster%d", ieee80211_master_setup,
- ieee80211_num_queues(hw));
+ hw->queues);
if (!mdev)
goto fail_mdev_alloc;
@@ -916,6 +927,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
memcpy(local->mdev->dev_addr, local->hw.wiphy->perm_addr, ETH_ALEN);
SET_NETDEV_DEV(local->mdev, wiphy_dev(local->hw.wiphy));
+ local->mdev->features |= NETIF_F_NETNS_LOCAL;
result = register_netdevice(local->mdev);
if (result < 0)
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index fbb766afe59..7f238589b6f 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -15,11 +15,8 @@
#include <linux/if_ether.h>
#include <linux/skbuff.h>
#include <linux/if_arp.h>
-#include <linux/wireless.h>
-#include <linux/random.h>
#include <linux/etherdevice.h>
#include <linux/rtnetlink.h>
-#include <net/iw_handler.h>
#include <net/mac80211.h>
#include <asm/unaligned.h>
@@ -35,15 +32,6 @@
#define IEEE80211_MONITORING_INTERVAL (2 * HZ)
#define IEEE80211_PROBE_INTERVAL (60 * HZ)
#define IEEE80211_RETRY_AUTH_INTERVAL (1 * HZ)
-#define IEEE80211_SCAN_INTERVAL (2 * HZ)
-#define IEEE80211_SCAN_INTERVAL_SLOW (15 * HZ)
-#define IEEE80211_IBSS_JOIN_TIMEOUT (7 * HZ)
-
-#define IEEE80211_IBSS_MERGE_INTERVAL (30 * HZ)
-#define IEEE80211_IBSS_INACTIVITY_LIMIT (60 * HZ)
-
-#define IEEE80211_IBSS_MAX_STA_ENTRIES 128
-
/* utils */
static int ecw2cw(int ecw)
@@ -92,43 +80,6 @@ static int ieee80211_compatible_rates(struct ieee80211_bss *bss,
return count;
}
-/* also used by mesh code */
-u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
- struct ieee802_11_elems *elems,
- enum ieee80211_band band)
-{
- struct ieee80211_supported_band *sband;
- struct ieee80211_rate *bitrates;
- size_t num_rates;
- u32 supp_rates;
- int i, j;
- sband = local->hw.wiphy->bands[band];
-
- if (!sband) {
- WARN_ON(1);
- sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
- }
-
- bitrates = sband->bitrates;
- num_rates = sband->n_bitrates;
- supp_rates = 0;
- for (i = 0; i < elems->supp_rates_len +
- elems->ext_supp_rates_len; i++) {
- u8 rate = 0;
- int own_rate;
- if (i < elems->supp_rates_len)
- rate = elems->supp_rates[i];
- else if (elems->ext_supp_rates)
- rate = elems->ext_supp_rates
- [i - elems->supp_rates_len];
- own_rate = 5 * (rate & 0x7f);
- for (j = 0; j < num_rates; j++)
- if (bitrates[j].bitrate == own_rate)
- supp_rates |= BIT(j);
- }
- return supp_rates;
-}
-
/* frame sending functions */
static void add_extra_ies(struct sk_buff *skb, u8 *ies, size_t ies_len)
@@ -137,113 +88,9 @@ static void add_extra_ies(struct sk_buff *skb, u8 *ies, size_t ies_len)
memcpy(skb_put(skb, ies_len), ies, ies_len);
}
-/* also used by scanning code */
-void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
- u8 *ssid, size_t ssid_len)
-{
- struct ieee80211_local *local = sdata->local;
- struct ieee80211_supported_band *sband;
- struct sk_buff *skb;
- struct ieee80211_mgmt *mgmt;
- u8 *pos, *supp_rates, *esupp_rates = NULL;
- int i;
-
- skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 +
- sdata->u.sta.ie_probereq_len);
- if (!skb) {
- printk(KERN_DEBUG "%s: failed to allocate buffer for probe "
- "request\n", sdata->dev->name);
- return;
- }
- skb_reserve(skb, local->hw.extra_tx_headroom);
-
- mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
- memset(mgmt, 0, 24);
- mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
- IEEE80211_STYPE_PROBE_REQ);
- memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
- if (dst) {
- memcpy(mgmt->da, dst, ETH_ALEN);
- memcpy(mgmt->bssid, dst, ETH_ALEN);
- } else {
- memset(mgmt->da, 0xff, ETH_ALEN);
- memset(mgmt->bssid, 0xff, ETH_ALEN);
- }
- pos = skb_put(skb, 2 + ssid_len);
- *pos++ = WLAN_EID_SSID;
- *pos++ = ssid_len;
- memcpy(pos, ssid, ssid_len);
-
- supp_rates = skb_put(skb, 2);
- supp_rates[0] = WLAN_EID_SUPP_RATES;
- supp_rates[1] = 0;
- sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
-
- for (i = 0; i < sband->n_bitrates; i++) {
- struct ieee80211_rate *rate = &sband->bitrates[i];
- if (esupp_rates) {
- pos = skb_put(skb, 1);
- esupp_rates[1]++;
- } else if (supp_rates[1] == 8) {
- esupp_rates = skb_put(skb, 3);
- esupp_rates[0] = WLAN_EID_EXT_SUPP_RATES;
- esupp_rates[1] = 1;
- pos = &esupp_rates[2];
- } else {
- pos = skb_put(skb, 1);
- supp_rates[1]++;
- }
- *pos = rate->bitrate / 5;
- }
-
- add_extra_ies(skb, sdata->u.sta.ie_probereq,
- sdata->u.sta.ie_probereq_len);
-
- ieee80211_tx_skb(sdata, skb, 0);
-}
-
-static void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta,
- int transaction, u8 *extra, size_t extra_len,
- int encrypt)
-{
- struct ieee80211_local *local = sdata->local;
- struct sk_buff *skb;
- struct ieee80211_mgmt *mgmt;
-
- skb = dev_alloc_skb(local->hw.extra_tx_headroom +
- sizeof(*mgmt) + 6 + extra_len +
- sdata->u.sta.ie_auth_len);
- if (!skb) {
- printk(KERN_DEBUG "%s: failed to allocate buffer for auth "
- "frame\n", sdata->dev->name);
- return;
- }
- skb_reserve(skb, local->hw.extra_tx_headroom);
-
- mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24 + 6);
- memset(mgmt, 0, 24 + 6);
- mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
- IEEE80211_STYPE_AUTH);
- if (encrypt)
- mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
- memcpy(mgmt->da, ifsta->bssid, ETH_ALEN);
- memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
- memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
- mgmt->u.auth.auth_alg = cpu_to_le16(ifsta->auth_alg);
- mgmt->u.auth.auth_transaction = cpu_to_le16(transaction);
- ifsta->auth_transaction = transaction + 1;
- mgmt->u.auth.status_code = cpu_to_le16(0);
- if (extra)
- memcpy(skb_put(skb, extra_len), extra, extra_len);
- add_extra_ies(skb, sdata->u.sta.ie_auth, sdata->u.sta.ie_auth_len);
-
- ieee80211_tx_skb(sdata, skb, encrypt);
-}
-
-static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta)
+static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
@@ -256,17 +103,17 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
u32 rates = 0;
size_t e_ies_len;
- if (ifsta->flags & IEEE80211_STA_PREV_BSSID_SET) {
- e_ies = sdata->u.sta.ie_reassocreq;
- e_ies_len = sdata->u.sta.ie_reassocreq_len;
+ if (ifmgd->flags & IEEE80211_IBSS_PREV_BSSID_SET) {
+ e_ies = sdata->u.mgd.ie_reassocreq;
+ e_ies_len = sdata->u.mgd.ie_reassocreq_len;
} else {
- e_ies = sdata->u.sta.ie_assocreq;
- e_ies_len = sdata->u.sta.ie_assocreq_len;
+ e_ies = sdata->u.mgd.ie_assocreq;
+ e_ies_len = sdata->u.mgd.ie_assocreq_len;
}
skb = dev_alloc_skb(local->hw.extra_tx_headroom +
- sizeof(*mgmt) + 200 + ifsta->extra_ie_len +
- ifsta->ssid_len + e_ies_len);
+ sizeof(*mgmt) + 200 + ifmgd->extra_ie_len +
+ ifmgd->ssid_len + e_ies_len);
if (!skb) {
printk(KERN_DEBUG "%s: failed to allocate buffer for assoc "
"frame\n", sdata->dev->name);
@@ -276,7 +123,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
- capab = ifsta->capab;
+ capab = ifmgd->capab;
if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ) {
if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
@@ -285,9 +132,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
}
- bss = ieee80211_rx_bss_get(local, ifsta->bssid,
+ bss = ieee80211_rx_bss_get(local, ifmgd->bssid,
local->hw.conf.channel->center_freq,
- ifsta->ssid, ifsta->ssid_len);
+ ifmgd->ssid, ifmgd->ssid_len);
if (bss) {
if (bss->cbss.capability & WLAN_CAPABILITY_PRIVACY)
capab |= WLAN_CAPABILITY_PRIVACY;
@@ -312,18 +159,18 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
memset(mgmt, 0, 24);
- memcpy(mgmt->da, ifsta->bssid, ETH_ALEN);
+ memcpy(mgmt->da, ifmgd->bssid, ETH_ALEN);
memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
- memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+ memcpy(mgmt->bssid, ifmgd->bssid, ETH_ALEN);
- if (ifsta->flags & IEEE80211_STA_PREV_BSSID_SET) {
+ if (ifmgd->flags & IEEE80211_STA_PREV_BSSID_SET) {
skb_put(skb, 10);
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_REASSOC_REQ);
mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab);
mgmt->u.reassoc_req.listen_interval =
cpu_to_le16(local->hw.conf.listen_interval);
- memcpy(mgmt->u.reassoc_req.current_ap, ifsta->prev_bssid,
+ memcpy(mgmt->u.reassoc_req.current_ap, ifmgd->prev_bssid,
ETH_ALEN);
} else {
skb_put(skb, 4);
@@ -335,10 +182,10 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
}
/* SSID */
- ies = pos = skb_put(skb, 2 + ifsta->ssid_len);
+ ies = pos = skb_put(skb, 2 + ifmgd->ssid_len);
*pos++ = WLAN_EID_SSID;
- *pos++ = ifsta->ssid_len;
- memcpy(pos, ifsta->ssid, ifsta->ssid_len);
+ *pos++ = ifmgd->ssid_len;
+ memcpy(pos, ifmgd->ssid, ifmgd->ssid_len);
/* add all rates which were marked to be used above */
supp_rates_len = rates_len;
@@ -393,12 +240,12 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
}
}
- if (ifsta->extra_ie) {
- pos = skb_put(skb, ifsta->extra_ie_len);
- memcpy(pos, ifsta->extra_ie, ifsta->extra_ie_len);
+ if (ifmgd->extra_ie) {
+ pos = skb_put(skb, ifmgd->extra_ie_len);
+ memcpy(pos, ifmgd->extra_ie, ifmgd->extra_ie_len);
}
- if (wmm && (ifsta->flags & IEEE80211_STA_WMM_ENABLED)) {
+ if (wmm && (ifmgd->flags & IEEE80211_STA_WMM_ENABLED)) {
pos = skb_put(skb, 9);
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
*pos++ = 7; /* len */
@@ -418,11 +265,11 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
* mode (11a/b/g) if any one of these ciphers is
* configured as pairwise.
*/
- if (wmm && (ifsta->flags & IEEE80211_STA_WMM_ENABLED) &&
+ if (wmm && (ifmgd->flags & IEEE80211_STA_WMM_ENABLED) &&
sband->ht_cap.ht_supported &&
(ht_ie = ieee80211_bss_get_ie(bss, WLAN_EID_HT_INFORMATION)) &&
ht_ie[1] >= sizeof(struct ieee80211_ht_info) &&
- (!(ifsta->flags & IEEE80211_STA_TKIP_WEP_USED))) {
+ (!(ifmgd->flags & IEEE80211_STA_TKIP_WEP_USED))) {
struct ieee80211_ht_info *ht_info =
(struct ieee80211_ht_info *)(ht_ie + 2);
u16 cap = sband->ht_cap.cap;
@@ -459,11 +306,11 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
add_extra_ies(skb, e_ies, e_ies_len);
- kfree(ifsta->assocreq_ies);
- ifsta->assocreq_ies_len = (skb->data + skb->len) - ies;
- ifsta->assocreq_ies = kmalloc(ifsta->assocreq_ies_len, GFP_KERNEL);
- if (ifsta->assocreq_ies)
- memcpy(ifsta->assocreq_ies, ies, ifsta->assocreq_ies_len);
+ kfree(ifmgd->assocreq_ies);
+ ifmgd->assocreq_ies_len = (skb->data + skb->len) - ies;
+ ifmgd->assocreq_ies = kmalloc(ifmgd->assocreq_ies_len, GFP_KERNEL);
+ if (ifmgd->assocreq_ies)
+ memcpy(ifmgd->assocreq_ies, ies, ifmgd->assocreq_ies_len);
ieee80211_tx_skb(sdata, skb, 0);
}
@@ -473,18 +320,18 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
u16 stype, u16 reason)
{
struct ieee80211_local *local = sdata->local;
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
u8 *ies;
size_t ies_len;
if (stype == IEEE80211_STYPE_DEAUTH) {
- ies = sdata->u.sta.ie_deauth;
- ies_len = sdata->u.sta.ie_deauth_len;
+ ies = sdata->u.mgd.ie_deauth;
+ ies_len = sdata->u.mgd.ie_deauth_len;
} else {
- ies = sdata->u.sta.ie_disassoc;
- ies_len = sdata->u.sta.ie_disassoc_len;
+ ies = sdata->u.mgd.ie_disassoc;
+ ies_len = sdata->u.mgd.ie_disassoc_len;
}
skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) +
@@ -498,9 +345,9 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
memset(mgmt, 0, 24);
- memcpy(mgmt->da, ifsta->bssid, ETH_ALEN);
+ memcpy(mgmt->da, ifmgd->bssid, ETH_ALEN);
memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
- memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
+ memcpy(mgmt->bssid, ifmgd->bssid, ETH_ALEN);
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
skb_put(skb, 2);
/* u.deauth.reason_code == u.disassoc.reason_code */
@@ -508,13 +355,13 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
add_extra_ies(skb, ies, ies_len);
- ieee80211_tx_skb(sdata, skb, ifsta->flags & IEEE80211_STA_MFP_ENABLED);
+ ieee80211_tx_skb(sdata, skb, ifmgd->flags & IEEE80211_STA_MFP_ENABLED);
}
void ieee80211_send_pspoll(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_pspoll *pspoll;
struct sk_buff *skb;
u16 fc;
@@ -531,43 +378,20 @@ void ieee80211_send_pspoll(struct ieee80211_local *local,
memset(pspoll, 0, sizeof(*pspoll));
fc = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL | IEEE80211_FCTL_PM;
pspoll->frame_control = cpu_to_le16(fc);
- pspoll->aid = cpu_to_le16(ifsta->aid);
+ pspoll->aid = cpu_to_le16(ifmgd->aid);
/* aid in PS-Poll has its two MSBs each set to 1 */
pspoll->aid |= cpu_to_le16(1 << 15 | 1 << 14);
- memcpy(pspoll->bssid, ifsta->bssid, ETH_ALEN);
+ memcpy(pspoll->bssid, ifmgd->bssid, ETH_ALEN);
memcpy(pspoll->ta, sdata->dev->dev_addr, ETH_ALEN);
ieee80211_tx_skb(sdata, skb, 0);
-
- return;
}
/* MLME */
-static void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
- const size_t supp_rates_len,
- const u8 *supp_rates)
-{
- struct ieee80211_local *local = sdata->local;
- int i, have_higher_than_11mbit = 0;
-
- /* cf. IEEE 802.11 9.2.12 */
- for (i = 0; i < supp_rates_len; i++)
- if ((supp_rates[i] & 0x7f) * 5 > 110)
- have_higher_than_11mbit = 1;
-
- if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ &&
- have_higher_than_11mbit)
- sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
- else
- sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
-
- ieee80211_set_wmm_default(sdata);
-}
-
static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
- struct ieee80211_if_sta *ifsta,
+ struct ieee80211_if_managed *ifmgd,
u8 *wmm_param, size_t wmm_param_len)
{
struct ieee80211_tx_queue_params params;
@@ -575,7 +399,7 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
int count;
u8 *pos;
- if (!(ifsta->flags & IEEE80211_STA_WMM_ENABLED))
+ if (!(ifmgd->flags & IEEE80211_STA_WMM_ENABLED))
return;
if (!wmm_param)
@@ -584,9 +408,9 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1)
return;
count = wmm_param[6] & 0x0f;
- if (count == ifsta->wmm_last_param_set)
+ if (count == ifmgd->wmm_last_param_set)
return;
- ifsta->wmm_last_param_set = count;
+ ifmgd->wmm_last_param_set = count;
pos = wmm_param + 8;
left = wmm_param_len - 8;
@@ -671,7 +495,7 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
#endif
u32 changed = 0;
bool use_protection;
@@ -694,7 +518,7 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
printk(KERN_DEBUG "%s: CTS protection %s (BSSID=%pM)\n",
sdata->dev->name,
use_protection ? "enabled" : "disabled",
- ifsta->bssid);
+ ifmgd->bssid);
}
#endif
bss_conf->use_cts_prot = use_protection;
@@ -708,7 +532,7 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
" (BSSID=%pM)\n",
sdata->dev->name,
use_short_preamble ? "short" : "long",
- ifsta->bssid);
+ ifmgd->bssid);
}
#endif
bss_conf->use_short_preamble = use_short_preamble;
@@ -722,7 +546,7 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
" (BSSID=%pM)\n",
sdata->dev->name,
use_short_slot ? "short" : "long",
- ifsta->bssid);
+ ifmgd->bssid);
}
#endif
bss_conf->use_short_slot = use_short_slot;
@@ -732,57 +556,57 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
return changed;
}
-static void ieee80211_sta_send_apinfo(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta)
+static void ieee80211_sta_send_apinfo(struct ieee80211_sub_if_data *sdata)
{
union iwreq_data wrqu;
+
memset(&wrqu, 0, sizeof(wrqu));
- if (ifsta->flags & IEEE80211_STA_ASSOCIATED)
- memcpy(wrqu.ap_addr.sa_data, sdata->u.sta.bssid, ETH_ALEN);
+ if (sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED)
+ memcpy(wrqu.ap_addr.sa_data, sdata->u.mgd.bssid, ETH_ALEN);
wrqu.ap_addr.sa_family = ARPHRD_ETHER;
wireless_send_event(sdata->dev, SIOCGIWAP, &wrqu, NULL);
}
-static void ieee80211_sta_send_associnfo(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta)
+static void ieee80211_sta_send_associnfo(struct ieee80211_sub_if_data *sdata)
{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
char *buf;
size_t len;
int i;
union iwreq_data wrqu;
- if (!ifsta->assocreq_ies && !ifsta->assocresp_ies)
+ if (!ifmgd->assocreq_ies && !ifmgd->assocresp_ies)
return;
- buf = kmalloc(50 + 2 * (ifsta->assocreq_ies_len +
- ifsta->assocresp_ies_len), GFP_KERNEL);
+ buf = kmalloc(50 + 2 * (ifmgd->assocreq_ies_len +
+ ifmgd->assocresp_ies_len), GFP_KERNEL);
if (!buf)
return;
len = sprintf(buf, "ASSOCINFO(");
- if (ifsta->assocreq_ies) {
+ if (ifmgd->assocreq_ies) {
len += sprintf(buf + len, "ReqIEs=");
- for (i = 0; i < ifsta->assocreq_ies_len; i++) {
+ for (i = 0; i < ifmgd->assocreq_ies_len; i++) {
len += sprintf(buf + len, "%02x",
- ifsta->assocreq_ies[i]);
+ ifmgd->assocreq_ies[i]);
}
}
- if (ifsta->assocresp_ies) {
- if (ifsta->assocreq_ies)
+ if (ifmgd->assocresp_ies) {
+ if (ifmgd->assocreq_ies)
len += sprintf(buf + len, " ");
len += sprintf(buf + len, "RespIEs=");
- for (i = 0; i < ifsta->assocresp_ies_len; i++) {
+ for (i = 0; i < ifmgd->assocresp_ies_len; i++) {
len += sprintf(buf + len, "%02x",
- ifsta->assocresp_ies[i]);
+ ifmgd->assocresp_ies[i]);
}
}
len += sprintf(buf + len, ")");
if (len > IW_CUSTOM_MAX) {
len = sprintf(buf, "ASSOCRESPIE=");
- for (i = 0; i < ifsta->assocresp_ies_len; i++) {
+ for (i = 0; i < ifmgd->assocresp_ies_len; i++) {
len += sprintf(buf + len, "%02x",
- ifsta->assocresp_ies[i]);
+ ifmgd->assocresp_ies[i]);
}
}
@@ -797,20 +621,20 @@ static void ieee80211_sta_send_associnfo(struct ieee80211_sub_if_data *sdata,
static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta,
u32 bss_info_changed)
{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
struct ieee80211_conf *conf = &local_to_hw(local)->conf;
struct ieee80211_bss *bss;
bss_info_changed |= BSS_CHANGED_ASSOC;
- ifsta->flags |= IEEE80211_STA_ASSOCIATED;
+ ifmgd->flags |= IEEE80211_STA_ASSOCIATED;
- bss = ieee80211_rx_bss_get(local, ifsta->bssid,
+ bss = ieee80211_rx_bss_get(local, ifmgd->bssid,
conf->channel->center_freq,
- ifsta->ssid, ifsta->ssid_len);
+ ifmgd->ssid, ifmgd->ssid_len);
if (bss) {
/* set timing information */
sdata->vif.bss_conf.beacon_int = bss->cbss.beacon_interval;
@@ -823,11 +647,11 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
ieee80211_rx_bss_put(local, bss);
}
- ifsta->flags |= IEEE80211_STA_PREV_BSSID_SET;
- memcpy(ifsta->prev_bssid, sdata->u.sta.bssid, ETH_ALEN);
- ieee80211_sta_send_associnfo(sdata, ifsta);
+ ifmgd->flags |= IEEE80211_STA_PREV_BSSID_SET;
+ memcpy(ifmgd->prev_bssid, sdata->u.mgd.bssid, ETH_ALEN);
+ ieee80211_sta_send_associnfo(sdata);
- ifsta->last_probe = jiffies;
+ ifmgd->last_probe = jiffies;
ieee80211_led_assoc(local, 1);
sdata->vif.bss_conf.assoc = 1;
@@ -856,70 +680,74 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
netif_tx_start_all_queues(sdata->dev);
netif_carrier_on(sdata->dev);
- ieee80211_sta_send_apinfo(sdata, ifsta);
+ ieee80211_sta_send_apinfo(sdata);
}
-static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta)
+static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata)
{
- ifsta->direct_probe_tries++;
- if (ifsta->direct_probe_tries > IEEE80211_AUTH_MAX_TRIES) {
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
+ ifmgd->direct_probe_tries++;
+ if (ifmgd->direct_probe_tries > IEEE80211_AUTH_MAX_TRIES) {
printk(KERN_DEBUG "%s: direct probe to AP %pM timed out\n",
- sdata->dev->name, ifsta->bssid);
- ifsta->state = IEEE80211_STA_MLME_DISABLED;
- ieee80211_sta_send_apinfo(sdata, ifsta);
+ sdata->dev->name, ifmgd->bssid);
+ ifmgd->state = IEEE80211_STA_MLME_DISABLED;
+ ieee80211_sta_send_apinfo(sdata);
/*
* Most likely AP is not in the range so remove the
* bss information associated to the AP
*/
- ieee80211_rx_bss_remove(sdata, ifsta->bssid,
+ ieee80211_rx_bss_remove(sdata, ifmgd->bssid,
sdata->local->hw.conf.channel->center_freq,
- ifsta->ssid, ifsta->ssid_len);
+ ifmgd->ssid, ifmgd->ssid_len);
return;
}
printk(KERN_DEBUG "%s: direct probe to AP %pM try %d\n",
- sdata->dev->name, ifsta->bssid,
- ifsta->direct_probe_tries);
+ sdata->dev->name, ifmgd->bssid,
+ ifmgd->direct_probe_tries);
- ifsta->state = IEEE80211_STA_MLME_DIRECT_PROBE;
+ ifmgd->state = IEEE80211_STA_MLME_DIRECT_PROBE;
- set_bit(IEEE80211_STA_REQ_DIRECT_PROBE, &ifsta->request);
+ set_bit(IEEE80211_STA_REQ_DIRECT_PROBE, &ifmgd->request);
/* Direct probe is sent to broadcast address as some APs
* will not answer to direct packet in unassociated state.
*/
ieee80211_send_probe_req(sdata, NULL,
- ifsta->ssid, ifsta->ssid_len);
+ ifmgd->ssid, ifmgd->ssid_len, NULL, 0);
- mod_timer(&ifsta->timer, jiffies + IEEE80211_AUTH_TIMEOUT);
+ mod_timer(&ifmgd->timer, jiffies + IEEE80211_AUTH_TIMEOUT);
}
-static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta)
+static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata)
{
- ifsta->auth_tries++;
- if (ifsta->auth_tries > IEEE80211_AUTH_MAX_TRIES) {
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
+ ifmgd->auth_tries++;
+ if (ifmgd->auth_tries > IEEE80211_AUTH_MAX_TRIES) {
printk(KERN_DEBUG "%s: authentication with AP %pM"
" timed out\n",
- sdata->dev->name, ifsta->bssid);
- ifsta->state = IEEE80211_STA_MLME_DISABLED;
- ieee80211_sta_send_apinfo(sdata, ifsta);
- ieee80211_rx_bss_remove(sdata, ifsta->bssid,
+ sdata->dev->name, ifmgd->bssid);
+ ifmgd->state = IEEE80211_STA_MLME_DISABLED;
+ ieee80211_sta_send_apinfo(sdata);
+ ieee80211_rx_bss_remove(sdata, ifmgd->bssid,
sdata->local->hw.conf.channel->center_freq,
- ifsta->ssid, ifsta->ssid_len);
+ ifmgd->ssid, ifmgd->ssid_len);
return;
}
- ifsta->state = IEEE80211_STA_MLME_AUTHENTICATE;
+ ifmgd->state = IEEE80211_STA_MLME_AUTHENTICATE;
printk(KERN_DEBUG "%s: authenticate with AP %pM\n",
- sdata->dev->name, ifsta->bssid);
+ sdata->dev->name, ifmgd->bssid);
- ieee80211_send_auth(sdata, ifsta, 1, NULL, 0, 0);
+ ieee80211_send_auth(sdata, 1, ifmgd->auth_alg, NULL, 0,
+ ifmgd->bssid, 0);
+ ifmgd->auth_transaction = 2;
- mod_timer(&ifsta->timer, jiffies + IEEE80211_AUTH_TIMEOUT);
+ mod_timer(&ifmgd->timer, jiffies + IEEE80211_AUTH_TIMEOUT);
}
/*
@@ -927,27 +755,28 @@ static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata,
* if self disconnected or a reason code from the AP.
*/
static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta, bool deauth,
- bool self_disconnected, u16 reason)
+ bool deauth, bool self_disconnected,
+ u16 reason)
{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
u32 changed = 0, config_changed = 0;
rcu_read_lock();
- sta = sta_info_get(local, ifsta->bssid);
+ sta = sta_info_get(local, ifmgd->bssid);
if (!sta) {
rcu_read_unlock();
return;
}
if (deauth) {
- ifsta->direct_probe_tries = 0;
- ifsta->auth_tries = 0;
+ ifmgd->direct_probe_tries = 0;
+ ifmgd->auth_tries = 0;
}
- ifsta->assoc_scan_tries = 0;
- ifsta->assoc_tries = 0;
+ ifmgd->assoc_scan_tries = 0;
+ ifmgd->assoc_tries = 0;
netif_tx_stop_all_queues(sdata->dev);
netif_carrier_off(sdata->dev);
@@ -963,20 +792,20 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
IEEE80211_STYPE_DISASSOC, reason);
}
- ifsta->flags &= ~IEEE80211_STA_ASSOCIATED;
+ ifmgd->flags &= ~IEEE80211_STA_ASSOCIATED;
changed |= ieee80211_reset_erp_info(sdata);
ieee80211_led_assoc(local, 0);
changed |= BSS_CHANGED_ASSOC;
sdata->vif.bss_conf.assoc = false;
- ieee80211_sta_send_apinfo(sdata, ifsta);
+ ieee80211_sta_send_apinfo(sdata);
if (self_disconnected || reason == WLAN_REASON_DISASSOC_STA_HAS_LEFT) {
- ifsta->state = IEEE80211_STA_MLME_DISABLED;
- ieee80211_rx_bss_remove(sdata, ifsta->bssid,
+ ifmgd->state = IEEE80211_STA_MLME_DISABLED;
+ ieee80211_rx_bss_remove(sdata, ifmgd->bssid,
sdata->local->hw.conf.channel->center_freq,
- ifsta->ssid, ifsta->ssid_len);
+ ifmgd->ssid, ifmgd->ssid_len);
}
rcu_read_unlock();
@@ -999,7 +828,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
rcu_read_lock();
- sta = sta_info_get(local, ifsta->bssid);
+ sta = sta_info_get(local, ifmgd->bssid);
if (!sta) {
rcu_read_unlock();
return;
@@ -1020,27 +849,27 @@ static int ieee80211_sta_wep_configured(struct ieee80211_sub_if_data *sdata)
return 1;
}
-static int ieee80211_privacy_mismatch(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta)
+static int ieee80211_privacy_mismatch(struct ieee80211_sub_if_data *sdata)
{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
struct ieee80211_bss *bss;
int bss_privacy;
int wep_privacy;
int privacy_invoked;
- if (!ifsta || (ifsta->flags & IEEE80211_STA_MIXED_CELL))
+ if (!ifmgd || (ifmgd->flags & IEEE80211_STA_MIXED_CELL))
return 0;
- bss = ieee80211_rx_bss_get(local, ifsta->bssid,
+ bss = ieee80211_rx_bss_get(local, ifmgd->bssid,
local->hw.conf.channel->center_freq,
- ifsta->ssid, ifsta->ssid_len);
+ ifmgd->ssid, ifmgd->ssid_len);
if (!bss)
return 0;
bss_privacy = !!(bss->cbss.capability & WLAN_CAPABILITY_PRIVACY);
wep_privacy = !!ieee80211_sta_wep_configured(sdata);
- privacy_invoked = !!(ifsta->flags & IEEE80211_STA_PRIVACY_INVOKED);
+ privacy_invoked = !!(ifmgd->flags & IEEE80211_STA_PRIVACY_INVOKED);
ieee80211_rx_bss_put(local, bss);
@@ -1050,41 +879,42 @@ static int ieee80211_privacy_mismatch(struct ieee80211_sub_if_data *sdata,
return 1;
}
-static void ieee80211_associate(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta)
+static void ieee80211_associate(struct ieee80211_sub_if_data *sdata)
{
- ifsta->assoc_tries++;
- if (ifsta->assoc_tries > IEEE80211_ASSOC_MAX_TRIES) {
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
+ ifmgd->assoc_tries++;
+ if (ifmgd->assoc_tries > IEEE80211_ASSOC_MAX_TRIES) {
printk(KERN_DEBUG "%s: association with AP %pM"
" timed out\n",
- sdata->dev->name, ifsta->bssid);
- ifsta->state = IEEE80211_STA_MLME_DISABLED;
- ieee80211_sta_send_apinfo(sdata, ifsta);
- ieee80211_rx_bss_remove(sdata, ifsta->bssid,
+ sdata->dev->name, ifmgd->bssid);
+ ifmgd->state = IEEE80211_STA_MLME_DISABLED;
+ ieee80211_sta_send_apinfo(sdata);
+ ieee80211_rx_bss_remove(sdata, ifmgd->bssid,
sdata->local->hw.conf.channel->center_freq,
- ifsta->ssid, ifsta->ssid_len);
+ ifmgd->ssid, ifmgd->ssid_len);
return;
}
- ifsta->state = IEEE80211_STA_MLME_ASSOCIATE;
+ ifmgd->state = IEEE80211_STA_MLME_ASSOCIATE;
printk(KERN_DEBUG "%s: associate with AP %pM\n",
- sdata->dev->name, ifsta->bssid);
- if (ieee80211_privacy_mismatch(sdata, ifsta)) {
+ sdata->dev->name, ifmgd->bssid);
+ if (ieee80211_privacy_mismatch(sdata)) {
printk(KERN_DEBUG "%s: mismatch in privacy configuration and "
"mixed-cell disabled - abort association\n", sdata->dev->name);
- ifsta->state = IEEE80211_STA_MLME_DISABLED;
+ ifmgd->state = IEEE80211_STA_MLME_DISABLED;
return;
}
- ieee80211_send_assoc(sdata, ifsta);
+ ieee80211_send_assoc(sdata);
- mod_timer(&ifsta->timer, jiffies + IEEE80211_ASSOC_TIMEOUT);
+ mod_timer(&ifmgd->timer, jiffies + IEEE80211_ASSOC_TIMEOUT);
}
-static void ieee80211_associated(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta)
+static void ieee80211_associated(struct ieee80211_sub_if_data *sdata)
{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
int disassoc;
@@ -1094,38 +924,40 @@ static void ieee80211_associated(struct ieee80211_sub_if_data *sdata,
* for better APs. */
/* TODO: remove expired BSSes */
- ifsta->state = IEEE80211_STA_MLME_ASSOCIATED;
+ ifmgd->state = IEEE80211_STA_MLME_ASSOCIATED;
rcu_read_lock();
- sta = sta_info_get(local, ifsta->bssid);
+ sta = sta_info_get(local, ifmgd->bssid);
if (!sta) {
printk(KERN_DEBUG "%s: No STA entry for own AP %pM\n",
- sdata->dev->name, ifsta->bssid);
+ sdata->dev->name, ifmgd->bssid);
disassoc = 1;
} else {
disassoc = 0;
if (time_after(jiffies,
sta->last_rx + IEEE80211_MONITORING_INTERVAL)) {
- if (ifsta->flags & IEEE80211_STA_PROBEREQ_POLL) {
+ if (ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) {
printk(KERN_DEBUG "%s: No ProbeResp from "
"current AP %pM - assume out of "
"range\n",
- sdata->dev->name, ifsta->bssid);
+ sdata->dev->name, ifmgd->bssid);
disassoc = 1;
} else
- ieee80211_send_probe_req(sdata, ifsta->bssid,
- ifsta->ssid,
- ifsta->ssid_len);
- ifsta->flags ^= IEEE80211_STA_PROBEREQ_POLL;
+ ieee80211_send_probe_req(sdata, ifmgd->bssid,
+ ifmgd->ssid,
+ ifmgd->ssid_len,
+ NULL, 0);
+ ifmgd->flags ^= IEEE80211_STA_PROBEREQ_POLL;
} else {
- ifsta->flags &= ~IEEE80211_STA_PROBEREQ_POLL;
- if (time_after(jiffies, ifsta->last_probe +
+ ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL;
+ if (time_after(jiffies, ifmgd->last_probe +
IEEE80211_PROBE_INTERVAL)) {
- ifsta->last_probe = jiffies;
- ieee80211_send_probe_req(sdata, ifsta->bssid,
- ifsta->ssid,
- ifsta->ssid_len);
+ ifmgd->last_probe = jiffies;
+ ieee80211_send_probe_req(sdata, ifmgd->bssid,
+ ifmgd->ssid,
+ ifmgd->ssid_len,
+ NULL, 0);
}
}
}
@@ -1133,25 +965,25 @@ static void ieee80211_associated(struct ieee80211_sub_if_data *sdata,
rcu_read_unlock();
if (disassoc)
- ieee80211_set_disassoc(sdata, ifsta, true, true,
+ ieee80211_set_disassoc(sdata, true, true,
WLAN_REASON_PREV_AUTH_NOT_VALID);
else
- mod_timer(&ifsta->timer, jiffies +
+ mod_timer(&ifmgd->timer, jiffies +
IEEE80211_MONITORING_INTERVAL);
}
-static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta)
+static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata)
{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
printk(KERN_DEBUG "%s: authenticated\n", sdata->dev->name);
- ifsta->flags |= IEEE80211_STA_AUTHENTICATED;
- ieee80211_associate(sdata, ifsta);
+ ifmgd->flags |= IEEE80211_STA_AUTHENTICATED;
+ ieee80211_associate(sdata);
}
static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
size_t len)
{
@@ -1162,59 +994,37 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
if (!elems.challenge)
return;
- ieee80211_send_auth(sdata, ifsta, 3, elems.challenge - 2,
- elems.challenge_len + 2, 1);
-}
-
-static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta,
- struct ieee80211_mgmt *mgmt,
- size_t len)
-{
- u16 auth_alg, auth_transaction, status_code;
-
- if (len < 24 + 6)
- return;
-
- auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
- auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
- status_code = le16_to_cpu(mgmt->u.auth.status_code);
-
- /*
- * IEEE 802.11 standard does not require authentication in IBSS
- * networks and most implementations do not seem to use it.
- * However, try to reply to authentication attempts if someone
- * has actually implemented this.
- */
- if (auth_alg == WLAN_AUTH_OPEN && auth_transaction == 1)
- ieee80211_send_auth(sdata, ifsta, 2, NULL, 0, 0);
+ ieee80211_send_auth(sdata, 3, sdata->u.mgd.auth_alg,
+ elems.challenge - 2, elems.challenge_len + 2,
+ sdata->u.mgd.bssid, 1);
+ sdata->u.mgd.auth_transaction = 4;
}
static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
size_t len)
{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
u16 auth_alg, auth_transaction, status_code;
- if (ifsta->state != IEEE80211_STA_MLME_AUTHENTICATE)
+ if (ifmgd->state != IEEE80211_STA_MLME_AUTHENTICATE)
return;
if (len < 24 + 6)
return;
- if (memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN) != 0)
+ if (memcmp(ifmgd->bssid, mgmt->sa, ETH_ALEN) != 0)
return;
- if (memcmp(ifsta->bssid, mgmt->bssid, ETH_ALEN) != 0)
+ if (memcmp(ifmgd->bssid, mgmt->bssid, ETH_ALEN) != 0)
return;
auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
status_code = le16_to_cpu(mgmt->u.auth.status_code);
- if (auth_alg != ifsta->auth_alg ||
- auth_transaction != ifsta->auth_transaction)
+ if (auth_alg != ifmgd->auth_alg ||
+ auth_transaction != ifmgd->auth_transaction)
return;
if (status_code != WLAN_STATUS_SUCCESS) {
@@ -1223,15 +1033,15 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
const int num_algs = ARRAY_SIZE(algs);
int i, pos;
algs[0] = algs[1] = algs[2] = 0xff;
- if (ifsta->auth_algs & IEEE80211_AUTH_ALG_OPEN)
+ if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_OPEN)
algs[0] = WLAN_AUTH_OPEN;
- if (ifsta->auth_algs & IEEE80211_AUTH_ALG_SHARED_KEY)
+ if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_SHARED_KEY)
algs[1] = WLAN_AUTH_SHARED_KEY;
- if (ifsta->auth_algs & IEEE80211_AUTH_ALG_LEAP)
+ if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_LEAP)
algs[2] = WLAN_AUTH_LEAP;
- if (ifsta->auth_alg == WLAN_AUTH_OPEN)
+ if (ifmgd->auth_alg == WLAN_AUTH_OPEN)
pos = 0;
- else if (ifsta->auth_alg == WLAN_AUTH_SHARED_KEY)
+ else if (ifmgd->auth_alg == WLAN_AUTH_SHARED_KEY)
pos = 1;
else
pos = 2;
@@ -1239,101 +1049,101 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
pos++;
if (pos >= num_algs)
pos = 0;
- if (algs[pos] == ifsta->auth_alg ||
+ if (algs[pos] == ifmgd->auth_alg ||
algs[pos] == 0xff)
continue;
if (algs[pos] == WLAN_AUTH_SHARED_KEY &&
!ieee80211_sta_wep_configured(sdata))
continue;
- ifsta->auth_alg = algs[pos];
+ ifmgd->auth_alg = algs[pos];
break;
}
}
return;
}
- switch (ifsta->auth_alg) {
+ switch (ifmgd->auth_alg) {
case WLAN_AUTH_OPEN:
case WLAN_AUTH_LEAP:
- ieee80211_auth_completed(sdata, ifsta);
+ ieee80211_auth_completed(sdata);
break;
case WLAN_AUTH_SHARED_KEY:
- if (ifsta->auth_transaction == 4)
- ieee80211_auth_completed(sdata, ifsta);
+ if (ifmgd->auth_transaction == 4)
+ ieee80211_auth_completed(sdata);
else
- ieee80211_auth_challenge(sdata, ifsta, mgmt, len);
+ ieee80211_auth_challenge(sdata, mgmt, len);
break;
}
}
static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
size_t len)
{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
u16 reason_code;
if (len < 24 + 2)
return;
- if (memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN))
+ if (memcmp(ifmgd->bssid, mgmt->sa, ETH_ALEN))
return;
reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
- if (ifsta->flags & IEEE80211_STA_AUTHENTICATED)
+ if (ifmgd->flags & IEEE80211_STA_AUTHENTICATED)
printk(KERN_DEBUG "%s: deauthenticated (Reason: %u)\n",
sdata->dev->name, reason_code);
- if (ifsta->state == IEEE80211_STA_MLME_AUTHENTICATE ||
- ifsta->state == IEEE80211_STA_MLME_ASSOCIATE ||
- ifsta->state == IEEE80211_STA_MLME_ASSOCIATED) {
- ifsta->state = IEEE80211_STA_MLME_DIRECT_PROBE;
- mod_timer(&ifsta->timer, jiffies +
+ if (ifmgd->state == IEEE80211_STA_MLME_AUTHENTICATE ||
+ ifmgd->state == IEEE80211_STA_MLME_ASSOCIATE ||
+ ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) {
+ ifmgd->state = IEEE80211_STA_MLME_DIRECT_PROBE;
+ mod_timer(&ifmgd->timer, jiffies +
IEEE80211_RETRY_AUTH_INTERVAL);
}
- ieee80211_set_disassoc(sdata, ifsta, true, false, 0);
- ifsta->flags &= ~IEEE80211_STA_AUTHENTICATED;
+ ieee80211_set_disassoc(sdata, true, false, 0);
+ ifmgd->flags &= ~IEEE80211_STA_AUTHENTICATED;
}
static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
size_t len)
{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
u16 reason_code;
if (len < 24 + 2)
return;
- if (memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN))
+ if (memcmp(ifmgd->bssid, mgmt->sa, ETH_ALEN))
return;
reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
- if (ifsta->flags & IEEE80211_STA_ASSOCIATED)
+ if (ifmgd->flags & IEEE80211_STA_ASSOCIATED)
printk(KERN_DEBUG "%s: disassociated (Reason: %u)\n",
sdata->dev->name, reason_code);
- if (ifsta->state == IEEE80211_STA_MLME_ASSOCIATED) {
- ifsta->state = IEEE80211_STA_MLME_ASSOCIATE;
- mod_timer(&ifsta->timer, jiffies +
+ if (ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) {
+ ifmgd->state = IEEE80211_STA_MLME_ASSOCIATE;
+ mod_timer(&ifmgd->timer, jiffies +
IEEE80211_RETRY_AUTH_INTERVAL);
}
- ieee80211_set_disassoc(sdata, ifsta, false, false, reason_code);
+ ieee80211_set_disassoc(sdata, false, false, reason_code);
}
static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
size_t len,
int reassoc)
{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
struct sta_info *sta;
@@ -1350,13 +1160,13 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
/* AssocResp and ReassocResp have identical structure, so process both
* of them in this function. */
- if (ifsta->state != IEEE80211_STA_MLME_ASSOCIATE)
+ if (ifmgd->state != IEEE80211_STA_MLME_ASSOCIATE)
return;
if (len < 24 + 6)
return;
- if (memcmp(ifsta->bssid, mgmt->sa, ETH_ALEN) != 0)
+ if (memcmp(ifmgd->bssid, mgmt->sa, ETH_ALEN) != 0)
return;
capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
@@ -1381,7 +1191,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
"comeback duration %u TU (%u ms)\n",
sdata->dev->name, tu, ms);
if (ms > IEEE80211_ASSOC_TIMEOUT)
- mod_timer(&ifsta->timer,
+ mod_timer(&ifmgd->timer,
jiffies + msecs_to_jiffies(ms));
return;
}
@@ -1392,7 +1202,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
/* if this was a reassociation, ensure we try a "full"
* association next time. This works around some broken APs
* which do not correctly reject reassociation requests. */
- ifsta->flags &= ~IEEE80211_STA_PREV_BSSID_SET;
+ ifmgd->flags &= ~IEEE80211_STA_PREV_BSSID_SET;
return;
}
@@ -1408,23 +1218,23 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
}
printk(KERN_DEBUG "%s: associated\n", sdata->dev->name);
- ifsta->aid = aid;
- ifsta->ap_capab = capab_info;
+ ifmgd->aid = aid;
+ ifmgd->ap_capab = capab_info;
- kfree(ifsta->assocresp_ies);
- ifsta->assocresp_ies_len = len - (pos - (u8 *) mgmt);
- ifsta->assocresp_ies = kmalloc(ifsta->assocresp_ies_len, GFP_KERNEL);
- if (ifsta->assocresp_ies)
- memcpy(ifsta->assocresp_ies, pos, ifsta->assocresp_ies_len);
+ kfree(ifmgd->assocresp_ies);
+ ifmgd->assocresp_ies_len = len - (pos - (u8 *) mgmt);
+ ifmgd->assocresp_ies = kmalloc(ifmgd->assocresp_ies_len, GFP_KERNEL);
+ if (ifmgd->assocresp_ies)
+ memcpy(ifmgd->assocresp_ies, pos, ifmgd->assocresp_ies_len);
rcu_read_lock();
/* Add STA entry for the AP */
- sta = sta_info_get(local, ifsta->bssid);
+ sta = sta_info_get(local, ifmgd->bssid);
if (!sta) {
newsta = true;
- sta = sta_info_alloc(sdata, ifsta->bssid, GFP_ATOMIC);
+ sta = sta_info_alloc(sdata, ifmgd->bssid, GFP_ATOMIC);
if (!sta) {
printk(KERN_DEBUG "%s: failed to alloc STA entry for"
" the AP\n", sdata->dev->name);
@@ -1505,7 +1315,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
rate_control_rate_init(sta);
- if (ifsta->flags & IEEE80211_STA_MFP_ENABLED)
+ if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED)
set_sta_flags(sta, WLAN_STA_MFP);
if (elems.wmm_param)
@@ -1524,11 +1334,12 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
rcu_read_unlock();
if (elems.wmm_param)
- ieee80211_sta_wmm_params(local, ifsta, elems.wmm_param,
+ ieee80211_sta_wmm_params(local, ifmgd, elems.wmm_param,
elems.wmm_param_len);
if (elems.ht_info_elem && elems.wmm_param &&
- (ifsta->flags & IEEE80211_STA_WMM_ENABLED))
+ (ifmgd->flags & IEEE80211_STA_WMM_ENABLED) &&
+ !(ifmgd->flags & IEEE80211_STA_TKIP_WEP_USED))
changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
ap_ht_cap_flags);
@@ -1536,162 +1347,11 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
* ieee80211_set_associated() will tell the driver */
bss_conf->aid = aid;
bss_conf->assoc_capability = capab_info;
- ieee80211_set_associated(sdata, ifsta, changed);
-
- ieee80211_associated(sdata, ifsta);
-}
-
+ ieee80211_set_associated(sdata, changed);
-static int __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta,
- const u8 *bssid, const int beacon_int,
- const int freq,
- const size_t supp_rates_len,
- const u8 *supp_rates,
- const u16 capability)
-{
- struct ieee80211_local *local = sdata->local;
- int res = 0, rates, i, j;
- struct sk_buff *skb;
- struct ieee80211_mgmt *mgmt;
- u8 *pos;
- struct ieee80211_supported_band *sband;
- union iwreq_data wrqu;
-
- if (local->ops->reset_tsf) {
- /* Reset own TSF to allow time synchronization work. */
- local->ops->reset_tsf(local_to_hw(local));
- }
-
- if ((ifsta->flags & IEEE80211_STA_PREV_BSSID_SET) &&
- memcmp(ifsta->bssid, bssid, ETH_ALEN) == 0)
- return res;
-
- skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400 +
- sdata->u.sta.ie_proberesp_len);
- if (!skb) {
- printk(KERN_DEBUG "%s: failed to allocate buffer for probe "
- "response\n", sdata->dev->name);
- return -ENOMEM;
- }
-
- if (!(ifsta->flags & IEEE80211_STA_PREV_BSSID_SET)) {
- /* Remove possible STA entries from other IBSS networks. */
- sta_info_flush_delayed(sdata);
- }
-
- memcpy(ifsta->bssid, bssid, ETH_ALEN);
- res = ieee80211_if_config(sdata, IEEE80211_IFCC_BSSID);
- if (res)
- return res;
-
- local->hw.conf.beacon_int = beacon_int >= 10 ? beacon_int : 10;
-
- sdata->drop_unencrypted = capability &
- WLAN_CAPABILITY_PRIVACY ? 1 : 0;
-
- res = ieee80211_set_freq(sdata, freq);
-
- if (res)
- return res;
-
- sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
-
- /* Build IBSS probe response */
-
- skb_reserve(skb, local->hw.extra_tx_headroom);
-
- mgmt = (struct ieee80211_mgmt *)
- skb_put(skb, 24 + sizeof(mgmt->u.beacon));
- memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon));
- mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
- IEEE80211_STYPE_PROBE_RESP);
- memset(mgmt->da, 0xff, ETH_ALEN);
- memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
- memcpy(mgmt->bssid, ifsta->bssid, ETH_ALEN);
- mgmt->u.beacon.beacon_int =
- cpu_to_le16(local->hw.conf.beacon_int);
- mgmt->u.beacon.capab_info = cpu_to_le16(capability);
-
- pos = skb_put(skb, 2 + ifsta->ssid_len);
- *pos++ = WLAN_EID_SSID;
- *pos++ = ifsta->ssid_len;
- memcpy(pos, ifsta->ssid, ifsta->ssid_len);
-
- rates = supp_rates_len;
- if (rates > 8)
- rates = 8;
- pos = skb_put(skb, 2 + rates);
- *pos++ = WLAN_EID_SUPP_RATES;
- *pos++ = rates;
- memcpy(pos, supp_rates, rates);
-
- if (sband->band == IEEE80211_BAND_2GHZ) {
- pos = skb_put(skb, 2 + 1);
- *pos++ = WLAN_EID_DS_PARAMS;
- *pos++ = 1;
- *pos++ = ieee80211_frequency_to_channel(freq);
- }
-
- pos = skb_put(skb, 2 + 2);
- *pos++ = WLAN_EID_IBSS_PARAMS;
- *pos++ = 2;
- /* FIX: set ATIM window based on scan results */
- *pos++ = 0;
- *pos++ = 0;
-
- if (supp_rates_len > 8) {
- rates = supp_rates_len - 8;
- pos = skb_put(skb, 2 + rates);
- *pos++ = WLAN_EID_EXT_SUPP_RATES;
- *pos++ = rates;
- memcpy(pos, &supp_rates[8], rates);
- }
-
- add_extra_ies(skb, sdata->u.sta.ie_proberesp,
- sdata->u.sta.ie_proberesp_len);
-
- ifsta->probe_resp = skb;
-
- ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON |
- IEEE80211_IFCC_BEACON_ENABLED);
-
-
- rates = 0;
- for (i = 0; i < supp_rates_len; i++) {
- int bitrate = (supp_rates[i] & 0x7f) * 5;
- for (j = 0; j < sband->n_bitrates; j++)
- if (sband->bitrates[j].bitrate == bitrate)
- rates |= BIT(j);
- }
- ifsta->supp_rates_bits[local->hw.conf.channel->band] = rates;
-
- ieee80211_sta_def_wmm_params(sdata, supp_rates_len, supp_rates);
-
- ifsta->flags |= IEEE80211_STA_PREV_BSSID_SET;
- ifsta->state = IEEE80211_STA_MLME_IBSS_JOINED;
- mod_timer(&ifsta->timer, jiffies + IEEE80211_IBSS_MERGE_INTERVAL);
-
- ieee80211_led_assoc(local, true);
-
- memset(&wrqu, 0, sizeof(wrqu));
- memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
- wireless_send_event(sdata->dev, SIOCGIWAP, &wrqu, NULL);
-
- return res;
+ ieee80211_associated(sdata);
}
-static int ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta,
- struct ieee80211_bss *bss)
-{
- return __ieee80211_sta_join_ibss(sdata, ifsta,
- bss->cbss.bssid,
- bss->cbss.beacon_interval,
- bss->cbss.channel->center_freq,
- bss->supp_rates_len, bss->supp_rates,
- bss->cbss.capability);
-}
static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
@@ -1703,11 +1363,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
int freq;
struct ieee80211_bss *bss;
- struct sta_info *sta;
struct ieee80211_channel *channel;
- u64 beacon_timestamp, rx_timestamp;
- u32 supp_rates = 0;
- enum ieee80211_band band = rx_status->band;
if (elems->ds_params && elems->ds_params_len == 1)
freq = ieee80211_channel_to_frequency(elems->ds_params[0]);
@@ -1719,133 +1375,18 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
return;
- if (sdata->vif.type == NL80211_IFTYPE_ADHOC && elems->supp_rates &&
- memcmp(mgmt->bssid, sdata->u.sta.bssid, ETH_ALEN) == 0) {
- supp_rates = ieee80211_sta_get_rates(local, elems, band);
-
- rcu_read_lock();
-
- sta = sta_info_get(local, mgmt->sa);
- if (sta) {
- u32 prev_rates;
-
- prev_rates = sta->sta.supp_rates[band];
- /* make sure mandatory rates are always added */
- sta->sta.supp_rates[band] = supp_rates |
- ieee80211_mandatory_rates(local, band);
-
-#ifdef CONFIG_MAC80211_IBSS_DEBUG
- if (sta->sta.supp_rates[band] != prev_rates)
- printk(KERN_DEBUG "%s: updated supp_rates set "
- "for %pM based on beacon info (0x%llx | "
- "0x%llx -> 0x%llx)\n",
- sdata->dev->name,
- sta->sta.addr,
- (unsigned long long) prev_rates,
- (unsigned long long) supp_rates,
- (unsigned long long) sta->sta.supp_rates[band]);
-#endif
- } else {
- ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, supp_rates);
- }
-
- rcu_read_unlock();
- }
-
bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
channel, beacon);
if (!bss)
return;
if (elems->ch_switch_elem && (elems->ch_switch_elem_len == 3) &&
- (memcmp(mgmt->bssid, sdata->u.sta.bssid, ETH_ALEN) == 0)) {
+ (memcmp(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN) == 0)) {
struct ieee80211_channel_sw_ie *sw_elem =
(struct ieee80211_channel_sw_ie *)elems->ch_switch_elem;
ieee80211_process_chanswitch(sdata, sw_elem, bss);
}
- /* was just updated in ieee80211_bss_info_update */
- beacon_timestamp = bss->cbss.tsf;
-
- if (sdata->vif.type != NL80211_IFTYPE_ADHOC)
- goto put_bss;
-
- /* check if we need to merge IBSS */
-
- /* merge only on beacons (???) */
- if (!beacon)
- goto put_bss;
-
- /* we use a fixed BSSID */
- if (sdata->u.sta.flags & IEEE80211_STA_BSSID_SET)
- goto put_bss;
-
- /* not an IBSS */
- if (!(bss->cbss.capability & WLAN_CAPABILITY_IBSS))
- goto put_bss;
-
- /* different channel */
- if (bss->cbss.channel != local->oper_channel)
- goto put_bss;
-
- /* different SSID */
- if (elems->ssid_len != sdata->u.sta.ssid_len ||
- memcmp(elems->ssid, sdata->u.sta.ssid,
- sdata->u.sta.ssid_len))
- goto put_bss;
-
- if (rx_status->flag & RX_FLAG_TSFT) {
- /*
- * For correct IBSS merging we need mactime; since mactime is
- * defined as the time the first data symbol of the frame hits
- * the PHY, and the timestamp of the beacon is defined as "the
- * time that the data symbol containing the first bit of the
- * timestamp is transmitted to the PHY plus the transmitting
- * STA's delays through its local PHY from the MAC-PHY
- * interface to its interface with the WM" (802.11 11.1.2)
- * - equals the time this bit arrives at the receiver - we have
- * to take into account the offset between the two.
- *
- * E.g. at 1 MBit that means mactime is 192 usec earlier
- * (=24 bytes * 8 usecs/byte) than the beacon timestamp.
- */
- int rate;
-
- if (rx_status->flag & RX_FLAG_HT)
- rate = 65; /* TODO: HT rates */
- else
- rate = local->hw.wiphy->bands[band]->
- bitrates[rx_status->rate_idx].bitrate;
-
- rx_timestamp = rx_status->mactime + (24 * 8 * 10 / rate);
- } else if (local && local->ops && local->ops->get_tsf)
- /* second best option: get current TSF */
- rx_timestamp = local->ops->get_tsf(local_to_hw(local));
- else
- /* can't merge without knowing the TSF */
- rx_timestamp = -1LLU;
-
-#ifdef CONFIG_MAC80211_IBSS_DEBUG
- printk(KERN_DEBUG "RX beacon SA=%pM BSSID="
- "%pM TSF=0x%llx BCN=0x%llx diff=%lld @%lu\n",
- mgmt->sa, mgmt->bssid,
- (unsigned long long)rx_timestamp,
- (unsigned long long)beacon_timestamp,
- (unsigned long long)(rx_timestamp - beacon_timestamp),
- jiffies);
-#endif
-
- if (beacon_timestamp > rx_timestamp) {
-#ifdef CONFIG_MAC80211_IBSS_DEBUG
- printk(KERN_DEBUG "%s: beacon TSF higher than "
- "local TSF - IBSS merge with BSSID %pM\n",
- sdata->dev->name, mgmt->bssid);
-#endif
- ieee80211_sta_join_ibss(sdata, &sdata->u.sta, bss);
- ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, supp_rates);
- }
-
- put_bss:
ieee80211_rx_bss_put(local, bss);
}
@@ -1857,7 +1398,6 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
{
size_t baselen;
struct ieee802_11_elems elems;
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
if (memcmp(mgmt->da, sdata->dev->dev_addr, ETH_ALEN))
return; /* ignore ProbeResp to foreign address */
@@ -1873,20 +1413,19 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
/* direct probe may be part of the association flow */
if (test_and_clear_bit(IEEE80211_STA_REQ_DIRECT_PROBE,
- &ifsta->request)) {
+ &sdata->u.mgd.request)) {
printk(KERN_DEBUG "%s direct probe responded\n",
sdata->dev->name);
- ieee80211_authenticate(sdata, ifsta);
+ ieee80211_authenticate(sdata);
}
}
-
static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
size_t len,
struct ieee80211_rx_status *rx_status)
{
- struct ieee80211_if_sta *ifsta;
+ struct ieee80211_if_managed *ifmgd;
size_t baselen;
struct ieee802_11_elems elems;
struct ieee80211_local *local = sdata->local;
@@ -1905,21 +1444,22 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
if (sdata->vif.type != NL80211_IFTYPE_STATION)
return;
- ifsta = &sdata->u.sta;
- if (!(ifsta->flags & IEEE80211_STA_ASSOCIATED) ||
- memcmp(ifsta->bssid, mgmt->bssid, ETH_ALEN) != 0)
+ ifmgd = &sdata->u.mgd;
+
+ if (!(ifmgd->flags & IEEE80211_STA_ASSOCIATED) ||
+ memcmp(ifmgd->bssid, mgmt->bssid, ETH_ALEN) != 0)
return;
if (rx_status->freq != local->hw.conf.channel->center_freq)
return;
- ieee80211_sta_wmm_params(local, ifsta, elems.wmm_param,
+ ieee80211_sta_wmm_params(local, ifmgd, elems.wmm_param,
elems.wmm_param_len);
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK &&
local->hw.conf.flags & IEEE80211_CONF_PS) {
- directed_tim = ieee80211_check_tim(&elems, ifsta->aid);
+ directed_tim = ieee80211_check_tim(&elems, ifmgd->aid);
if (directed_tim) {
if (local->hw.conf.dynamic_ps_timeout > 0) {
@@ -1954,14 +1494,15 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
erp_valid, erp_value);
- if (elems.ht_cap_elem && elems.ht_info_elem && elems.wmm_param) {
+ if (elems.ht_cap_elem && elems.ht_info_elem && elems.wmm_param &&
+ !(ifmgd->flags & IEEE80211_STA_TKIP_WEP_USED)) {
struct sta_info *sta;
struct ieee80211_supported_band *sband;
u16 ap_ht_cap_flags;
rcu_read_lock();
- sta = sta_info_get(local, ifsta->bssid);
+ sta = sta_info_get(local, ifmgd->bssid);
if (!sta) {
rcu_read_unlock();
return;
@@ -1997,85 +1538,16 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
ieee80211_bss_info_change_notify(sdata, changed);
}
-
-static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta,
- struct ieee80211_mgmt *mgmt,
- size_t len)
+ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb,
+ struct ieee80211_rx_status *rx_status)
{
struct ieee80211_local *local = sdata->local;
- int tx_last_beacon;
- struct sk_buff *skb;
- struct ieee80211_mgmt *resp;
- u8 *pos, *end;
-
- if (ifsta->state != IEEE80211_STA_MLME_IBSS_JOINED ||
- len < 24 + 2 || !ifsta->probe_resp)
- return;
-
- if (local->ops->tx_last_beacon)
- tx_last_beacon = local->ops->tx_last_beacon(local_to_hw(local));
- else
- tx_last_beacon = 1;
-
-#ifdef CONFIG_MAC80211_IBSS_DEBUG
- printk(KERN_DEBUG "%s: RX ProbeReq SA=%pM DA=%pM BSSID=%pM"
- " (tx_last_beacon=%d)\n",
- sdata->dev->name, mgmt->sa, mgmt->da,
- mgmt->bssid, tx_last_beacon);
-#endif /* CONFIG_MAC80211_IBSS_DEBUG */
-
- if (!tx_last_beacon)
- return;
-
- if (memcmp(mgmt->bssid, ifsta->bssid, ETH_ALEN) != 0 &&
- memcmp(mgmt->bssid, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0)
- return;
-
- end = ((u8 *) mgmt) + len;
- pos = mgmt->u.probe_req.variable;
- if (pos[0] != WLAN_EID_SSID ||
- pos + 2 + pos[1] > end) {
-#ifdef CONFIG_MAC80211_IBSS_DEBUG
- printk(KERN_DEBUG "%s: Invalid SSID IE in ProbeReq "
- "from %pM\n",
- sdata->dev->name, mgmt->sa);
-#endif
- return;
- }
- if (pos[1] != 0 &&
- (pos[1] != ifsta->ssid_len ||
- memcmp(pos + 2, ifsta->ssid, ifsta->ssid_len) != 0)) {
- /* Ignore ProbeReq for foreign SSID */
- return;
- }
-
- /* Reply with ProbeResp */
- skb = skb_copy(ifsta->probe_resp, GFP_KERNEL);
- if (!skb)
- return;
-
- resp = (struct ieee80211_mgmt *) skb->data;
- memcpy(resp->da, mgmt->sa, ETH_ALEN);
-#ifdef CONFIG_MAC80211_IBSS_DEBUG
- 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);
-}
-
-void ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
- struct ieee80211_rx_status *rx_status)
-{
- struct ieee80211_local *local = sdata->local;
- struct ieee80211_if_sta *ifsta;
struct ieee80211_mgmt *mgmt;
u16 fc;
if (skb->len < 24)
- goto fail;
-
- ifsta = &sdata->u.sta;
+ return RX_DROP_MONITOR;
mgmt = (struct ieee80211_mgmt *) skb->data;
fc = le16_to_cpu(mgmt->frame_control);
@@ -2090,147 +1562,68 @@ void ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *
case IEEE80211_STYPE_REASSOC_RESP:
case IEEE80211_STYPE_DEAUTH:
case IEEE80211_STYPE_DISASSOC:
- skb_queue_tail(&ifsta->skb_queue, skb);
- queue_work(local->hw.workqueue, &ifsta->work);
- return;
+ skb_queue_tail(&sdata->u.mgd.skb_queue, skb);
+ queue_work(local->hw.workqueue, &sdata->u.mgd.work);
+ return RX_QUEUED;
}
- fail:
- kfree_skb(skb);
+ return RX_DROP_MONITOR;
}
static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
struct ieee80211_rx_status *rx_status;
- struct ieee80211_if_sta *ifsta;
struct ieee80211_mgmt *mgmt;
u16 fc;
- ifsta = &sdata->u.sta;
-
rx_status = (struct ieee80211_rx_status *) skb->cb;
mgmt = (struct ieee80211_mgmt *) skb->data;
fc = le16_to_cpu(mgmt->frame_control);
- if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
- switch (fc & IEEE80211_FCTL_STYPE) {
- case IEEE80211_STYPE_PROBE_REQ:
- ieee80211_rx_mgmt_probe_req(sdata, ifsta, mgmt,
- skb->len);
- break;
- case IEEE80211_STYPE_PROBE_RESP:
- ieee80211_rx_mgmt_probe_resp(sdata, mgmt, skb->len,
- rx_status);
- break;
- case IEEE80211_STYPE_BEACON:
- ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len,
- rx_status);
- break;
- case IEEE80211_STYPE_AUTH:
- ieee80211_rx_mgmt_auth_ibss(sdata, ifsta, mgmt,
- skb->len);
- break;
- }
- } else { /* NL80211_IFTYPE_STATION */
- switch (fc & IEEE80211_FCTL_STYPE) {
- case IEEE80211_STYPE_PROBE_RESP:
- ieee80211_rx_mgmt_probe_resp(sdata, mgmt, skb->len,
- rx_status);
- break;
- case IEEE80211_STYPE_BEACON:
- ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len,
- rx_status);
- break;
- case IEEE80211_STYPE_AUTH:
- ieee80211_rx_mgmt_auth(sdata, ifsta, mgmt, skb->len);
- break;
- case IEEE80211_STYPE_ASSOC_RESP:
- ieee80211_rx_mgmt_assoc_resp(sdata, ifsta, mgmt,
- skb->len, 0);
- break;
- case IEEE80211_STYPE_REASSOC_RESP:
- ieee80211_rx_mgmt_assoc_resp(sdata, ifsta, mgmt,
- skb->len, 1);
- break;
- case IEEE80211_STYPE_DEAUTH:
- ieee80211_rx_mgmt_deauth(sdata, ifsta, mgmt, skb->len);
- break;
- case IEEE80211_STYPE_DISASSOC:
- ieee80211_rx_mgmt_disassoc(sdata, ifsta, mgmt,
- skb->len);
- break;
- }
+ switch (fc & IEEE80211_FCTL_STYPE) {
+ case IEEE80211_STYPE_PROBE_RESP:
+ ieee80211_rx_mgmt_probe_resp(sdata, mgmt, skb->len,
+ rx_status);
+ break;
+ case IEEE80211_STYPE_BEACON:
+ ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len,
+ rx_status);
+ break;
+ case IEEE80211_STYPE_AUTH:
+ ieee80211_rx_mgmt_auth(sdata, mgmt, skb->len);
+ break;
+ case IEEE80211_STYPE_ASSOC_RESP:
+ ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len, 0);
+ break;
+ case IEEE80211_STYPE_REASSOC_RESP:
+ ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len, 1);
+ break;
+ case IEEE80211_STYPE_DEAUTH:
+ ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len);
+ break;
+ case IEEE80211_STYPE_DISASSOC:
+ ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len);
+ break;
}
kfree_skb(skb);
}
-
-static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
-{
- struct ieee80211_local *local = sdata->local;
- int active = 0;
- struct sta_info *sta;
-
- rcu_read_lock();
-
- list_for_each_entry_rcu(sta, &local->sta_list, list) {
- if (sta->sdata == sdata &&
- time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL,
- jiffies)) {
- active++;
- break;
- }
- }
-
- rcu_read_unlock();
-
- return active;
-}
-
-
-static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta)
-{
- mod_timer(&ifsta->timer, jiffies + IEEE80211_IBSS_MERGE_INTERVAL);
-
- ieee80211_sta_expire(sdata, IEEE80211_IBSS_INACTIVITY_LIMIT);
- if (ieee80211_sta_active_ibss(sdata))
- return;
-
- if ((sdata->u.sta.flags & IEEE80211_STA_BSSID_SET) &&
- (!(sdata->u.sta.flags & IEEE80211_STA_AUTO_CHANNEL_SEL)))
- return;
-
- printk(KERN_DEBUG "%s: No active IBSS STAs - trying to scan for other "
- "IBSS networks with same SSID (merge)\n", sdata->dev->name);
-
- /* XXX maybe racy? */
- if (sdata->local->scan_req)
- return;
-
- memcpy(sdata->local->int_scan_req.ssids[0].ssid,
- ifsta->ssid, IEEE80211_MAX_SSID_LEN);
- sdata->local->int_scan_req.ssids[0].ssid_len = ifsta->ssid_len;
- ieee80211_request_scan(sdata, &sdata->local->int_scan_req);
-}
-
-
static void ieee80211_sta_timer(unsigned long data)
{
struct ieee80211_sub_if_data *sdata =
(struct ieee80211_sub_if_data *) data;
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
- set_bit(IEEE80211_STA_REQ_RUN, &ifsta->request);
- queue_work(local->hw.workqueue, &ifsta->work);
+ set_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request);
+ queue_work(local->hw.workqueue, &ifmgd->work);
}
-static void ieee80211_sta_reset_auth(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta)
+static void ieee80211_sta_reset_auth(struct ieee80211_sub_if_data *sdata)
{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
if (local->ops->reset_tsf) {
@@ -2238,191 +1631,39 @@ static void ieee80211_sta_reset_auth(struct ieee80211_sub_if_data *sdata,
local->ops->reset_tsf(local_to_hw(local));
}
- ifsta->wmm_last_param_set = -1; /* allow any WMM update */
+ ifmgd->wmm_last_param_set = -1; /* allow any WMM update */
- if (ifsta->auth_algs & IEEE80211_AUTH_ALG_OPEN)
- ifsta->auth_alg = WLAN_AUTH_OPEN;
- else if (ifsta->auth_algs & IEEE80211_AUTH_ALG_SHARED_KEY)
- ifsta->auth_alg = WLAN_AUTH_SHARED_KEY;
- else if (ifsta->auth_algs & IEEE80211_AUTH_ALG_LEAP)
- ifsta->auth_alg = WLAN_AUTH_LEAP;
+ if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_OPEN)
+ ifmgd->auth_alg = WLAN_AUTH_OPEN;
+ else if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_SHARED_KEY)
+ ifmgd->auth_alg = WLAN_AUTH_SHARED_KEY;
+ else if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_LEAP)
+ ifmgd->auth_alg = WLAN_AUTH_LEAP;
else
- ifsta->auth_alg = WLAN_AUTH_OPEN;
- ifsta->auth_transaction = -1;
- ifsta->flags &= ~IEEE80211_STA_ASSOCIATED;
- ifsta->assoc_scan_tries = 0;
- ifsta->direct_probe_tries = 0;
- ifsta->auth_tries = 0;
- ifsta->assoc_tries = 0;
+ ifmgd->auth_alg = WLAN_AUTH_OPEN;
+ ifmgd->auth_transaction = -1;
+ ifmgd->flags &= ~IEEE80211_STA_ASSOCIATED;
+ ifmgd->assoc_scan_tries = 0;
+ ifmgd->direct_probe_tries = 0;
+ ifmgd->auth_tries = 0;
+ ifmgd->assoc_tries = 0;
netif_tx_stop_all_queues(sdata->dev);
netif_carrier_off(sdata->dev);
}
-static int ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta)
-{
- struct ieee80211_local *local = sdata->local;
- struct ieee80211_supported_band *sband;
- u8 *pos;
- u8 bssid[ETH_ALEN];
- u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
- u16 capability;
- int i;
-
- if (sdata->u.sta.flags & IEEE80211_STA_BSSID_SET) {
- memcpy(bssid, ifsta->bssid, ETH_ALEN);
- } else {
- /* Generate random, not broadcast, locally administered BSSID. Mix in
- * own MAC address to make sure that devices that do not have proper
- * random number generator get different BSSID. */
- get_random_bytes(bssid, ETH_ALEN);
- for (i = 0; i < ETH_ALEN; i++)
- bssid[i] ^= sdata->dev->dev_addr[i];
- bssid[0] &= ~0x01;
- bssid[0] |= 0x02;
- }
-
- printk(KERN_DEBUG "%s: Creating new IBSS network, BSSID %pM\n",
- sdata->dev->name, bssid);
-
- sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
-
- if (local->hw.conf.beacon_int == 0)
- local->hw.conf.beacon_int = 100;
-
- capability = WLAN_CAPABILITY_IBSS;
-
- if (sdata->default_key)
- capability |= WLAN_CAPABILITY_PRIVACY;
- else
- sdata->drop_unencrypted = 0;
-
- pos = supp_rates;
- for (i = 0; i < sband->n_bitrates; i++) {
- int rate = sband->bitrates[i].bitrate;
- *pos++ = (u8) (rate / 5);
- }
-
- return __ieee80211_sta_join_ibss(sdata, ifsta,
- bssid, local->hw.conf.beacon_int,
- local->hw.conf.channel->center_freq,
- sband->n_bitrates, supp_rates,
- capability);
-}
-
-
-static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta)
+static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata)
{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
struct ieee80211_bss *bss;
- int active_ibss;
-
- if (ifsta->ssid_len == 0)
- return -EINVAL;
-
- active_ibss = ieee80211_sta_active_ibss(sdata);
-#ifdef CONFIG_MAC80211_IBSS_DEBUG
- printk(KERN_DEBUG "%s: sta_find_ibss (active_ibss=%d)\n",
- sdata->dev->name, active_ibss);
-#endif /* CONFIG_MAC80211_IBSS_DEBUG */
-
- if (active_ibss)
- return 0;
-
- if (ifsta->flags & IEEE80211_STA_BSSID_SET)
- bss = ieee80211_rx_bss_get(local, ifsta->bssid, 0,
- ifsta->ssid, ifsta->ssid_len);
- else
- bss = (void *)cfg80211_get_ibss(local->hw.wiphy,
- NULL,
- ifsta->ssid, ifsta->ssid_len);
-
-#ifdef CONFIG_MAC80211_IBSS_DEBUG
- if (bss)
- printk(KERN_DEBUG " sta_find_ibss: selected %pM current "
- "%pM\n", bss->cbss.bssid, ifsta->bssid);
-#endif /* CONFIG_MAC80211_IBSS_DEBUG */
-
- if (bss &&
- (!(ifsta->flags & IEEE80211_STA_PREV_BSSID_SET) ||
- memcmp(ifsta->bssid, bss->cbss.bssid, ETH_ALEN))) {
- int ret;
-
- printk(KERN_DEBUG "%s: Selected IBSS BSSID %pM"
- " based on configured SSID\n",
- sdata->dev->name, bss->cbss.bssid);
-
- ret = ieee80211_sta_join_ibss(sdata, ifsta, bss);
- ieee80211_rx_bss_put(local, bss);
- return ret;
- } else if (bss)
- ieee80211_rx_bss_put(local, bss);
-
-#ifdef CONFIG_MAC80211_IBSS_DEBUG
- printk(KERN_DEBUG " did not try to join ibss\n");
-#endif /* CONFIG_MAC80211_IBSS_DEBUG */
-
- /* Selected IBSS not found in current scan results - try to scan */
- if (ifsta->state == IEEE80211_STA_MLME_IBSS_JOINED &&
- !ieee80211_sta_active_ibss(sdata)) {
- mod_timer(&ifsta->timer, jiffies +
- IEEE80211_IBSS_MERGE_INTERVAL);
- } else if (time_after(jiffies, local->last_scan_completed +
- IEEE80211_SCAN_INTERVAL)) {
- printk(KERN_DEBUG "%s: Trigger new scan to find an IBSS to "
- "join\n", sdata->dev->name);
-
- /* XXX maybe racy? */
- if (local->scan_req)
- return -EBUSY;
-
- memcpy(local->int_scan_req.ssids[0].ssid,
- ifsta->ssid, IEEE80211_MAX_SSID_LEN);
- local->int_scan_req.ssids[0].ssid_len = ifsta->ssid_len;
- return ieee80211_request_scan(sdata, &local->int_scan_req);
- } else if (ifsta->state != IEEE80211_STA_MLME_IBSS_JOINED) {
- int interval = IEEE80211_SCAN_INTERVAL;
-
- if (time_after(jiffies, ifsta->ibss_join_req +
- IEEE80211_IBSS_JOIN_TIMEOUT)) {
- if ((ifsta->flags & IEEE80211_STA_CREATE_IBSS) &&
- (!(local->oper_channel->flags &
- IEEE80211_CHAN_NO_IBSS)))
- return ieee80211_sta_create_ibss(sdata, ifsta);
- if (ifsta->flags & IEEE80211_STA_CREATE_IBSS) {
- printk(KERN_DEBUG "%s: IBSS not allowed on"
- " %d MHz\n", sdata->dev->name,
- local->hw.conf.channel->center_freq);
- }
-
- /* No IBSS found - decrease scan interval and continue
- * scanning. */
- interval = IEEE80211_SCAN_INTERVAL_SLOW;
- }
-
- ifsta->state = IEEE80211_STA_MLME_IBSS_SEARCH;
- mod_timer(&ifsta->timer, jiffies + interval);
- return 0;
- }
-
- return 0;
-}
-
-
-static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta)
-{
- struct ieee80211_local *local = sdata->local;
- struct ieee80211_bss *bss;
- u8 *bssid = ifsta->bssid, *ssid = ifsta->ssid;
- u8 ssid_len = ifsta->ssid_len;
+ u8 *bssid = ifmgd->bssid, *ssid = ifmgd->ssid;
+ u8 ssid_len = ifmgd->ssid_len;
u16 capa_mask = WLAN_CAPABILITY_ESS;
u16 capa_val = WLAN_CAPABILITY_ESS;
struct ieee80211_channel *chan = local->oper_channel;
- if (ifsta->flags & (IEEE80211_STA_AUTO_SSID_SEL |
+ if (ifmgd->flags & (IEEE80211_STA_AUTO_SSID_SEL |
IEEE80211_STA_AUTO_BSSID_SEL |
IEEE80211_STA_AUTO_CHANNEL_SEL)) {
capa_mask |= WLAN_CAPABILITY_PRIVACY;
@@ -2430,13 +1671,13 @@ static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata,
capa_val |= WLAN_CAPABILITY_PRIVACY;
}
- if (ifsta->flags & IEEE80211_STA_AUTO_CHANNEL_SEL)
+ if (ifmgd->flags & IEEE80211_STA_AUTO_CHANNEL_SEL)
chan = NULL;
- if (ifsta->flags & IEEE80211_STA_AUTO_BSSID_SEL)
+ if (ifmgd->flags & IEEE80211_STA_AUTO_BSSID_SEL)
bssid = NULL;
- if (ifsta->flags & IEEE80211_STA_AUTO_SSID_SEL) {
+ if (ifmgd->flags & IEEE80211_STA_AUTO_SSID_SEL) {
ssid = NULL;
ssid_len = 0;
}
@@ -2447,16 +1688,16 @@ static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata,
if (bss) {
ieee80211_set_freq(sdata, bss->cbss.channel->center_freq);
- if (!(ifsta->flags & IEEE80211_STA_SSID_SET))
+ if (!(ifmgd->flags & IEEE80211_STA_SSID_SET))
ieee80211_sta_set_ssid(sdata, bss->ssid,
bss->ssid_len);
ieee80211_sta_set_bssid(sdata, bss->cbss.bssid);
ieee80211_sta_def_wmm_params(sdata, bss->supp_rates_len,
bss->supp_rates);
- if (sdata->u.sta.mfp == IEEE80211_MFP_REQUIRED)
- sdata->u.sta.flags |= IEEE80211_STA_MFP_ENABLED;
+ if (sdata->u.mgd.mfp == IEEE80211_MFP_REQUIRED)
+ sdata->u.mgd.flags |= IEEE80211_STA_MFP_ENABLED;
else
- sdata->u.sta.flags &= ~IEEE80211_STA_MFP_ENABLED;
+ sdata->u.mgd.flags &= ~IEEE80211_STA_MFP_ENABLED;
/* Send out direct probe if no probe resp was received or
* the one we have is outdated
@@ -2464,31 +1705,31 @@ static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata,
if (!bss->last_probe_resp ||
time_after(jiffies, bss->last_probe_resp
+ IEEE80211_SCAN_RESULT_EXPIRE))
- ifsta->state = IEEE80211_STA_MLME_DIRECT_PROBE;
+ ifmgd->state = IEEE80211_STA_MLME_DIRECT_PROBE;
else
- ifsta->state = IEEE80211_STA_MLME_AUTHENTICATE;
+ ifmgd->state = IEEE80211_STA_MLME_AUTHENTICATE;
ieee80211_rx_bss_put(local, bss);
- ieee80211_sta_reset_auth(sdata, ifsta);
+ ieee80211_sta_reset_auth(sdata);
return 0;
} else {
- if (ifsta->assoc_scan_tries < IEEE80211_ASSOC_SCANS_MAX_TRIES) {
- ifsta->assoc_scan_tries++;
+ if (ifmgd->assoc_scan_tries < IEEE80211_ASSOC_SCANS_MAX_TRIES) {
+ ifmgd->assoc_scan_tries++;
/* XXX maybe racy? */
if (local->scan_req)
return -1;
memcpy(local->int_scan_req.ssids[0].ssid,
- ifsta->ssid, IEEE80211_MAX_SSID_LEN);
- if (ifsta->flags & IEEE80211_STA_AUTO_SSID_SEL)
+ ifmgd->ssid, IEEE80211_MAX_SSID_LEN);
+ if (ifmgd->flags & IEEE80211_STA_AUTO_SSID_SEL)
local->int_scan_req.ssids[0].ssid_len = 0;
else
- local->int_scan_req.ssids[0].ssid_len = ifsta->ssid_len;
+ local->int_scan_req.ssids[0].ssid_len = ifmgd->ssid_len;
ieee80211_start_scan(sdata, &local->int_scan_req);
- ifsta->state = IEEE80211_STA_MLME_AUTHENTICATE;
- set_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request);
+ ifmgd->state = IEEE80211_STA_MLME_AUTHENTICATE;
+ set_bit(IEEE80211_STA_REQ_AUTH, &ifmgd->request);
} else {
- ifsta->assoc_scan_tries = 0;
- ifsta->state = IEEE80211_STA_MLME_DISABLED;
+ ifmgd->assoc_scan_tries = 0;
+ ifmgd->state = IEEE80211_STA_MLME_DISABLED;
}
}
return -1;
@@ -2498,9 +1739,9 @@ static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata,
static void ieee80211_sta_work(struct work_struct *work)
{
struct ieee80211_sub_if_data *sdata =
- container_of(work, struct ieee80211_sub_if_data, u.sta.work);
+ container_of(work, struct ieee80211_sub_if_data, u.mgd.work);
struct ieee80211_local *local = sdata->local;
- struct ieee80211_if_sta *ifsta;
+ struct ieee80211_if_managed *ifmgd;
struct sk_buff *skb;
if (!netif_running(sdata->dev))
@@ -2509,60 +1750,53 @@ static void ieee80211_sta_work(struct work_struct *work)
if (local->sw_scanning || local->hw_scanning)
return;
- if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION &&
- sdata->vif.type != NL80211_IFTYPE_ADHOC))
+ if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
return;
- ifsta = &sdata->u.sta;
+ ifmgd = &sdata->u.mgd;
- while ((skb = skb_dequeue(&ifsta->skb_queue)))
+ while ((skb = skb_dequeue(&ifmgd->skb_queue)))
ieee80211_sta_rx_queued_mgmt(sdata, skb);
- if (ifsta->state != IEEE80211_STA_MLME_DIRECT_PROBE &&
- ifsta->state != IEEE80211_STA_MLME_AUTHENTICATE &&
- ifsta->state != IEEE80211_STA_MLME_ASSOCIATE &&
- test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request)) {
+ if (ifmgd->state != IEEE80211_STA_MLME_DIRECT_PROBE &&
+ ifmgd->state != IEEE80211_STA_MLME_AUTHENTICATE &&
+ ifmgd->state != IEEE80211_STA_MLME_ASSOCIATE &&
+ test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request)) {
ieee80211_start_scan(sdata, local->scan_req);
return;
}
- if (test_and_clear_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request)) {
- if (ieee80211_sta_config_auth(sdata, ifsta))
+ if (test_and_clear_bit(IEEE80211_STA_REQ_AUTH, &ifmgd->request)) {
+ if (ieee80211_sta_config_auth(sdata))
return;
- clear_bit(IEEE80211_STA_REQ_RUN, &ifsta->request);
- } else if (!test_and_clear_bit(IEEE80211_STA_REQ_RUN, &ifsta->request))
+ clear_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request);
+ } else if (!test_and_clear_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request))
return;
- switch (ifsta->state) {
+ switch (ifmgd->state) {
case IEEE80211_STA_MLME_DISABLED:
break;
case IEEE80211_STA_MLME_DIRECT_PROBE:
- ieee80211_direct_probe(sdata, ifsta);
+ ieee80211_direct_probe(sdata);
break;
case IEEE80211_STA_MLME_AUTHENTICATE:
- ieee80211_authenticate(sdata, ifsta);
+ ieee80211_authenticate(sdata);
break;
case IEEE80211_STA_MLME_ASSOCIATE:
- ieee80211_associate(sdata, ifsta);
+ ieee80211_associate(sdata);
break;
case IEEE80211_STA_MLME_ASSOCIATED:
- ieee80211_associated(sdata, ifsta);
- break;
- case IEEE80211_STA_MLME_IBSS_SEARCH:
- ieee80211_sta_find_ibss(sdata, ifsta);
- break;
- case IEEE80211_STA_MLME_IBSS_JOINED:
- ieee80211_sta_merge_ibss(sdata, ifsta);
+ ieee80211_associated(sdata);
break;
default:
WARN_ON(1);
break;
}
- if (ieee80211_privacy_mismatch(sdata, ifsta)) {
+ if (ieee80211_privacy_mismatch(sdata)) {
printk(KERN_DEBUG "%s: privacy configuration mismatch and "
"mixed-cell disabled - disassociate\n", sdata->dev->name);
- ieee80211_set_disassoc(sdata, ifsta, false, true,
+ ieee80211_set_disassoc(sdata, false, true,
WLAN_REASON_UNSPECIFIED);
}
}
@@ -2571,155 +1805,106 @@ static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)
{
if (sdata->vif.type == NL80211_IFTYPE_STATION)
queue_work(sdata->local->hw.workqueue,
- &sdata->u.sta.work);
+ &sdata->u.mgd.work);
}
/* interface setup */
void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_if_sta *ifsta;
+ struct ieee80211_if_managed *ifmgd;
- ifsta = &sdata->u.sta;
- INIT_WORK(&ifsta->work, ieee80211_sta_work);
- INIT_WORK(&ifsta->chswitch_work, ieee80211_chswitch_work);
- setup_timer(&ifsta->timer, ieee80211_sta_timer,
+ ifmgd = &sdata->u.mgd;
+ INIT_WORK(&ifmgd->work, ieee80211_sta_work);
+ INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work);
+ setup_timer(&ifmgd->timer, ieee80211_sta_timer,
(unsigned long) sdata);
- setup_timer(&ifsta->chswitch_timer, ieee80211_chswitch_timer,
+ setup_timer(&ifmgd->chswitch_timer, ieee80211_chswitch_timer,
(unsigned long) sdata);
- skb_queue_head_init(&ifsta->skb_queue);
+ skb_queue_head_init(&ifmgd->skb_queue);
- ifsta->capab = WLAN_CAPABILITY_ESS;
- ifsta->auth_algs = IEEE80211_AUTH_ALG_OPEN |
+ ifmgd->capab = WLAN_CAPABILITY_ESS;
+ ifmgd->auth_algs = IEEE80211_AUTH_ALG_OPEN |
IEEE80211_AUTH_ALG_SHARED_KEY;
- ifsta->flags |= IEEE80211_STA_CREATE_IBSS |
+ ifmgd->flags |= IEEE80211_STA_CREATE_IBSS |
IEEE80211_STA_AUTO_BSSID_SEL |
IEEE80211_STA_AUTO_CHANNEL_SEL;
if (ieee80211_num_regular_queues(&sdata->local->hw) >= 4)
- ifsta->flags |= IEEE80211_STA_WMM_ENABLED;
-}
-
-/*
- * Add a new IBSS station, will also be called by the RX code when,
- * in IBSS mode, receiving a frame from a yet-unknown station, hence
- * must be callable in atomic context.
- */
-struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
- u8 *bssid,u8 *addr, u32 supp_rates)
-{
- struct ieee80211_local *local = sdata->local;
- struct sta_info *sta;
- int band = local->hw.conf.channel->band;
-
- /* TODO: Could consider removing the least recently used entry and
- * allow new one to be added. */
- if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) {
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: No room for a new IBSS STA "
- "entry %pM\n", sdata->dev->name, addr);
- }
- return NULL;
- }
-
- if (compare_ether_addr(bssid, sdata->u.sta.bssid))
- return NULL;
-
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: Adding new IBSS station %pM (dev=%s)\n",
- wiphy_name(local->hw.wiphy), addr, sdata->dev->name);
-#endif
-
- sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
- if (!sta)
- return NULL;
-
- set_sta_flags(sta, WLAN_STA_AUTHORIZED);
-
- /* make sure mandatory rates are always added */
- sta->sta.supp_rates[band] = supp_rates |
- ieee80211_mandatory_rates(local, band);
-
- rate_control_rate_init(sta);
-
- if (sta_info_insert(sta))
- return NULL;
-
- return sta;
+ ifmgd->flags |= IEEE80211_STA_WMM_ENABLED;
}
/* configuration hooks */
-void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_sta *ifsta)
+void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata)
{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
- if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
return;
- if ((ifsta->flags & (IEEE80211_STA_BSSID_SET |
+ if ((ifmgd->flags & (IEEE80211_STA_BSSID_SET |
IEEE80211_STA_AUTO_BSSID_SEL)) &&
- (ifsta->flags & (IEEE80211_STA_SSID_SET |
+ (ifmgd->flags & (IEEE80211_STA_SSID_SET |
IEEE80211_STA_AUTO_SSID_SEL))) {
- if (ifsta->state == IEEE80211_STA_MLME_ASSOCIATED)
- ieee80211_set_disassoc(sdata, ifsta, true, true,
+ if (ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED)
+ ieee80211_set_disassoc(sdata, true, true,
WLAN_REASON_DEAUTH_LEAVING);
- set_bit(IEEE80211_STA_REQ_AUTH, &ifsta->request);
- queue_work(local->hw.workqueue, &ifsta->work);
+ set_bit(IEEE80211_STA_REQ_AUTH, &ifmgd->request);
+ queue_work(local->hw.workqueue, &ifmgd->work);
}
}
-int ieee80211_sta_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t len)
+int ieee80211_sta_commit(struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_if_sta *ifsta;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- if (len > IEEE80211_MAX_SSID_LEN)
- return -EINVAL;
+ ifmgd->flags &= ~IEEE80211_STA_PREV_BSSID_SET;
- ifsta = &sdata->u.sta;
+ if (ifmgd->ssid_len)
+ ifmgd->flags |= IEEE80211_STA_SSID_SET;
+ else
+ ifmgd->flags &= ~IEEE80211_STA_SSID_SET;
- if (ifsta->ssid_len != len || memcmp(ifsta->ssid, ssid, len) != 0) {
- memset(ifsta->ssid, 0, sizeof(ifsta->ssid));
- memcpy(ifsta->ssid, ssid, len);
- ifsta->ssid_len = len;
- }
+ return 0;
+}
- ifsta->flags &= ~IEEE80211_STA_PREV_BSSID_SET;
+int ieee80211_sta_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t len)
+{
+ struct ieee80211_if_managed *ifmgd;
- if (len)
- ifsta->flags |= IEEE80211_STA_SSID_SET;
- else
- ifsta->flags &= ~IEEE80211_STA_SSID_SET;
+ if (len > IEEE80211_MAX_SSID_LEN)
+ return -EINVAL;
+
+ ifmgd = &sdata->u.mgd;
- if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
- ifsta->ibss_join_req = jiffies;
- ifsta->state = IEEE80211_STA_MLME_IBSS_SEARCH;
- return ieee80211_sta_find_ibss(sdata, ifsta);
+ if (ifmgd->ssid_len != len || memcmp(ifmgd->ssid, ssid, len) != 0) {
+ memset(ifmgd->ssid, 0, sizeof(ifmgd->ssid));
+ memcpy(ifmgd->ssid, ssid, len);
+ ifmgd->ssid_len = len;
}
- return 0;
+ return ieee80211_sta_commit(sdata);
}
int ieee80211_sta_get_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t *len)
{
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
- memcpy(ssid, ifsta->ssid, ifsta->ssid_len);
- *len = ifsta->ssid_len;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ memcpy(ssid, ifmgd->ssid, ifmgd->ssid_len);
+ *len = ifmgd->ssid_len;
return 0;
}
int ieee80211_sta_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid)
{
- struct ieee80211_if_sta *ifsta;
-
- ifsta = &sdata->u.sta;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
if (is_valid_ether_addr(bssid)) {
- memcpy(ifsta->bssid, bssid, ETH_ALEN);
- ifsta->flags |= IEEE80211_STA_BSSID_SET;
+ memcpy(ifmgd->bssid, bssid, ETH_ALEN);
+ ifmgd->flags |= IEEE80211_STA_BSSID_SET;
} else {
- memset(ifsta->bssid, 0, ETH_ALEN);
- ifsta->flags &= ~IEEE80211_STA_BSSID_SET;
+ memset(ifmgd->bssid, 0, ETH_ALEN);
+ ifmgd->flags &= ~IEEE80211_STA_BSSID_SET;
}
if (netif_running(sdata->dev)) {
@@ -2729,47 +1914,44 @@ int ieee80211_sta_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid)
}
}
- return ieee80211_sta_set_ssid(sdata, ifsta->ssid, ifsta->ssid_len);
+ return ieee80211_sta_commit(sdata);
}
int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata, char *ie, size_t len)
{
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- kfree(ifsta->extra_ie);
+ kfree(ifmgd->extra_ie);
if (len == 0) {
- ifsta->extra_ie = NULL;
- ifsta->extra_ie_len = 0;
+ ifmgd->extra_ie = NULL;
+ ifmgd->extra_ie_len = 0;
return 0;
}
- ifsta->extra_ie = kmalloc(len, GFP_KERNEL);
- if (!ifsta->extra_ie) {
- ifsta->extra_ie_len = 0;
+ ifmgd->extra_ie = kmalloc(len, GFP_KERNEL);
+ if (!ifmgd->extra_ie) {
+ ifmgd->extra_ie_len = 0;
return -ENOMEM;
}
- memcpy(ifsta->extra_ie, ie, len);
- ifsta->extra_ie_len = len;
+ memcpy(ifmgd->extra_ie, ie, len);
+ ifmgd->extra_ie_len = len;
return 0;
}
int ieee80211_sta_deauthenticate(struct ieee80211_sub_if_data *sdata, u16 reason)
{
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
-
printk(KERN_DEBUG "%s: deauthenticating by local choice (reason=%d)\n",
sdata->dev->name, reason);
- if (sdata->vif.type != NL80211_IFTYPE_STATION &&
- sdata->vif.type != NL80211_IFTYPE_ADHOC)
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
return -EINVAL;
- ieee80211_set_disassoc(sdata, ifsta, true, true, reason);
+ ieee80211_set_disassoc(sdata, true, true, reason);
return 0;
}
int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason)
{
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
printk(KERN_DEBUG "%s: disassociating by local choice (reason=%d)\n",
sdata->dev->name, reason);
@@ -2777,10 +1959,10 @@ int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason)
if (sdata->vif.type != NL80211_IFTYPE_STATION)
return -EINVAL;
- if (!(ifsta->flags & IEEE80211_STA_ASSOCIATED))
- return -1;
+ if (!(ifmgd->flags & IEEE80211_STA_ASSOCIATED))
+ return -ENOLINK;
- ieee80211_set_disassoc(sdata, ifsta, false, true, reason);
+ ieee80211_set_disassoc(sdata, false, true, reason);
return 0;
}
@@ -2788,14 +1970,6 @@ int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason)
void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
{
struct ieee80211_sub_if_data *sdata = local->scan_sdata;
- struct ieee80211_if_sta *ifsta;
-
- if (sdata && sdata->vif.type == NL80211_IFTYPE_ADHOC) {
- ifsta = &sdata->u.sta;
- if ((!(ifsta->flags & IEEE80211_STA_PREV_BSSID_SET)) ||
- !ieee80211_sta_active_ibss(sdata))
- ieee80211_sta_find_ibss(sdata, ifsta);
- }
/* Restart STA timers */
rcu_read_lock();
@@ -2842,3 +2016,36 @@ void ieee80211_dynamic_ps_timer(unsigned long data)
queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work);
}
+
+void ieee80211_send_nullfunc(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ int powersave)
+{
+ struct sk_buff *skb;
+ struct ieee80211_hdr *nullfunc;
+ __le16 fc;
+
+ if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
+ return;
+
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24);
+ if (!skb) {
+ printk(KERN_DEBUG "%s: failed to allocate buffer for nullfunc "
+ "frame\n", sdata->dev->name);
+ return;
+ }
+ skb_reserve(skb, local->hw.extra_tx_headroom);
+
+ nullfunc = (struct ieee80211_hdr *) skb_put(skb, 24);
+ memset(nullfunc, 0, 24);
+ fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC |
+ IEEE80211_FCTL_TODS);
+ if (powersave)
+ fc |= cpu_to_le16(IEEE80211_FCTL_PM);
+ nullfunc->frame_control = fc;
+ memcpy(nullfunc->addr1, sdata->u.mgd.bssid, ETH_ALEN);
+ memcpy(nullfunc->addr2, sdata->dev->dev_addr, ETH_ALEN);
+ memcpy(nullfunc->addr3, sdata->u.mgd.bssid, ETH_ALEN);
+
+ ieee80211_tx_skb(sdata, skb, 0);
+}
diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h
index 928da625e28..b9164c9a956 100644
--- a/net/mac80211/rate.h
+++ b/net/mac80211/rate.h
@@ -62,6 +62,18 @@ static inline void rate_control_rate_init(struct sta_info *sta)
ref->ops->rate_init(ref->priv, sband, ista, priv_sta);
}
+static inline void rate_control_rate_update(struct ieee80211_local *local,
+ struct ieee80211_supported_band *sband,
+ struct sta_info *sta, u32 changed)
+{
+ struct rate_control_ref *ref = local->rate_ctrl;
+ struct ieee80211_sta *ista = &sta->sta;
+ void *priv_sta = sta->rate_ctrl_priv;
+
+ if (ref->ops->rate_update)
+ ref->ops->rate_update(ref->priv, sband, ista,
+ priv_sta, changed);
+}
static inline void *rate_control_alloc_sta(struct rate_control_ref *ref,
struct ieee80211_sta *sta,
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 1327d424bf3..66f7ecf51b9 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -838,7 +838,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
if (rx->sdata->vif.type == NL80211_IFTYPE_ADHOC) {
u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len,
NL80211_IFTYPE_ADHOC);
- if (compare_ether_addr(bssid, rx->sdata->u.sta.bssid) == 0)
+ if (compare_ether_addr(bssid, rx->sdata->u.ibss.bssid) == 0)
sta->last_rx = jiffies;
} else
if (!is_multicast_ether_addr(hdr->addr1) ||
@@ -1702,13 +1702,13 @@ static void ieee80211_process_sa_query_req(struct ieee80211_sub_if_data *sdata,
return;
}
- if (compare_ether_addr(mgmt->sa, sdata->u.sta.bssid) != 0 ||
- compare_ether_addr(mgmt->bssid, sdata->u.sta.bssid) != 0) {
+ if (compare_ether_addr(mgmt->sa, sdata->u.mgd.bssid) != 0 ||
+ compare_ether_addr(mgmt->bssid, sdata->u.mgd.bssid) != 0) {
/* Not from the current AP. */
return;
}
- if (sdata->u.sta.state == IEEE80211_STA_MLME_ASSOCIATE) {
+ if (sdata->u.mgd.state == IEEE80211_STA_MLME_ASSOCIATE) {
/* Association in progress; ignore SA Query */
return;
}
@@ -1727,7 +1727,7 @@ static void ieee80211_process_sa_query_req(struct ieee80211_sub_if_data *sdata,
memset(resp, 0, 24);
memcpy(resp->da, mgmt->sa, ETH_ALEN);
memcpy(resp->sa, sdata->dev->dev_addr, ETH_ALEN);
- memcpy(resp->bssid, sdata->u.sta.bssid, ETH_ALEN);
+ memcpy(resp->bssid, sdata->u.mgd.bssid, ETH_ALEN);
resp->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ACTION);
skb_put(skb, 1 + sizeof(resp->u.action.u.sa_query));
@@ -1745,7 +1745,6 @@ 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_if_sta *ifsta = &sdata->u.sta;
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data;
struct ieee80211_bss *bss;
int len = rx->skb->len;
@@ -1803,6 +1802,10 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
case WLAN_CATEGORY_SPECTRUM_MGMT:
if (local->hw.conf.channel->band != IEEE80211_BAND_5GHZ)
return RX_DROP_MONITOR;
+
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ return RX_DROP_MONITOR;
+
switch (mgmt->u.action.u.measurement.action_code) {
case WLAN_ACTION_SPCT_MSR_REQ:
if (len < (IEEE80211_MIN_ACTION_SIZE +
@@ -1815,12 +1818,13 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
sizeof(mgmt->u.action.u.chan_switch)))
return RX_DROP_MONITOR;
- if (memcmp(mgmt->bssid, ifsta->bssid, ETH_ALEN) != 0)
+ if (memcmp(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN))
return RX_DROP_MONITOR;
- bss = ieee80211_rx_bss_get(local, ifsta->bssid,
+ bss = ieee80211_rx_bss_get(local, sdata->u.mgd.bssid,
local->hw.conf.channel->center_freq,
- ifsta->ssid, ifsta->ssid_len);
+ sdata->u.mgd.ssid,
+ sdata->u.mgd.ssid_len);
if (!bss)
return RX_DROP_MONITOR;
@@ -1876,11 +1880,14 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
sdata->vif.type != NL80211_IFTYPE_ADHOC)
return RX_DROP_MONITOR;
- if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME)
- return RX_DROP_MONITOR;
- ieee80211_sta_rx_mgmt(sdata, rx->skb, rx->status);
- return RX_QUEUED;
+ if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+ if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME)
+ return RX_DROP_MONITOR;
+ return ieee80211_sta_rx_mgmt(sdata, rx->skb, rx->status);
+ }
+
+ return ieee80211_ibss_rx_mgmt(sdata, rx->skb, rx->status);
}
static void ieee80211_rx_michael_mic_report(struct net_device *dev,
@@ -2083,7 +2090,7 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
case NL80211_IFTYPE_STATION:
if (!bssid)
return 0;
- if (!ieee80211_bssid_match(bssid, sdata->u.sta.bssid)) {
+ if (!ieee80211_bssid_match(bssid, sdata->u.mgd.bssid)) {
if (!(rx->flags & IEEE80211_RX_IN_SCAN))
return 0;
rx->flags &= ~IEEE80211_RX_RA_MATCH;
@@ -2101,7 +2108,7 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
if (ieee80211_is_beacon(hdr->frame_control)) {
return 1;
}
- else if (!ieee80211_bssid_match(bssid, sdata->u.sta.bssid)) {
+ else if (!ieee80211_bssid_match(bssid, sdata->u.ibss.bssid)) {
if (!(rx->flags & IEEE80211_RX_IN_SCAN))
return 0;
rx->flags &= ~IEEE80211_RX_RA_MATCH;
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index f883ab9f1e6..0e81e1633a6 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -63,20 +63,15 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
{
struct ieee80211_bss *bss;
int clen;
- enum cfg80211_signal_type sigtype = CFG80211_SIGNAL_TYPE_NONE;
s32 signal = 0;
- if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) {
- sigtype = CFG80211_SIGNAL_TYPE_MBM;
+ if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
signal = rx_status->signal * 100;
- } else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) {
- sigtype = CFG80211_SIGNAL_TYPE_UNSPEC;
+ else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)
signal = (rx_status->signal * 100) / local->hw.max_signal;
- }
bss = (void *)cfg80211_inform_bss_frame(local->hw.wiphy, channel,
- mgmt, len, signal, sigtype,
- GFP_ATOMIC);
+ mgmt, len, signal, GFP_ATOMIC);
if (!bss)
return NULL;
@@ -207,36 +202,6 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
return RX_QUEUED;
}
-void ieee80211_send_nullfunc(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- int powersave)
-{
- struct sk_buff *skb;
- struct ieee80211_hdr *nullfunc;
- __le16 fc;
-
- skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24);
- if (!skb) {
- printk(KERN_DEBUG "%s: failed to allocate buffer for nullfunc "
- "frame\n", sdata->dev->name);
- return;
- }
- skb_reserve(skb, local->hw.extra_tx_headroom);
-
- nullfunc = (struct ieee80211_hdr *) skb_put(skb, 24);
- memset(nullfunc, 0, 24);
- fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC |
- IEEE80211_FCTL_TODS);
- if (powersave)
- fc |= cpu_to_le16(IEEE80211_FCTL_PM);
- nullfunc->frame_control = fc;
- memcpy(nullfunc->addr1, sdata->u.sta.bssid, ETH_ALEN);
- memcpy(nullfunc->addr2, sdata->dev->dev_addr, ETH_ALEN);
- memcpy(nullfunc->addr3, sdata->u.sta.bssid, ETH_ALEN);
-
- ieee80211_tx_skb(sdata, skb, 0);
-}
-
void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
{
struct ieee80211_local *local = hw_to_local(hw);
@@ -280,6 +245,9 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
netif_addr_unlock(local->mdev);
netif_tx_unlock_bh(local->mdev);
+ if (local->ops->sw_scan_complete)
+ local->ops->sw_scan_complete(local_to_hw(local));
+
mutex_lock(&local->iflist_mtx);
list_for_each_entry(sdata, &local->interfaces, list) {
if (!netif_running(sdata->dev))
@@ -287,7 +255,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
/* Tell AP we're back */
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
- if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) {
+ if (sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED) {
ieee80211_send_nullfunc(local, sdata, 0);
netif_tx_wake_all_queues(sdata->dev);
}
@@ -305,6 +273,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
done:
ieee80211_mlme_notify_scan_completed(local);
+ ieee80211_ibss_notify_scan_completed(local);
ieee80211_mesh_notify_scan_completed(local);
}
EXPORT_SYMBOL(ieee80211_scan_completed);
@@ -367,7 +336,8 @@ void ieee80211_scan_work(struct work_struct *work)
ieee80211_send_probe_req(
sdata, NULL,
local->scan_req->ssids[i].ssid,
- local->scan_req->ssids[i].ssid_len);
+ local->scan_req->ssids[i].ssid_len,
+ local->scan_req->ie, local->scan_req->ie_len);
next_delay = IEEE80211_CHANNEL_TIME;
break;
}
@@ -428,6 +398,8 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata,
}
local->sw_scanning = true;
+ if (local->ops->sw_scan_start)
+ local->ops->sw_scan_start(local_to_hw(local));
mutex_lock(&local->iflist_mtx);
list_for_each_entry(sdata, &local->interfaces, list) {
@@ -442,7 +414,7 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata,
IEEE80211_IFCC_BEACON_ENABLED);
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
- if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) {
+ if (sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED) {
netif_tx_stop_all_queues(sdata->dev);
ieee80211_send_nullfunc(local, sdata, 1);
}
@@ -477,7 +449,7 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
struct cfg80211_scan_request *req)
{
struct ieee80211_local *local = sdata->local;
- struct ieee80211_if_sta *ifsta;
+ struct ieee80211_if_managed *ifmgd;
if (!req)
return -EINVAL;
@@ -502,9 +474,9 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
return -EBUSY;
}
- ifsta = &sdata->u.sta;
- set_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request);
- queue_work(local->hw.workqueue, &ifsta->work);
+ ifmgd = &sdata->u.mgd;
+ set_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request);
+ queue_work(local->hw.workqueue, &ifmgd->work);
return 0;
}
diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c
index 47bb2aed281..5f7a2624ed7 100644
--- a/net/mac80211/spectmgmt.c
+++ b/net/mac80211/spectmgmt.c
@@ -88,16 +88,16 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
void ieee80211_chswitch_work(struct work_struct *work)
{
struct ieee80211_sub_if_data *sdata =
- container_of(work, struct ieee80211_sub_if_data, u.sta.chswitch_work);
+ container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work);
struct ieee80211_bss *bss;
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
if (!netif_running(sdata->dev))
return;
- bss = ieee80211_rx_bss_get(sdata->local, ifsta->bssid,
+ bss = ieee80211_rx_bss_get(sdata->local, ifmgd->bssid,
sdata->local->hw.conf.channel->center_freq,
- ifsta->ssid, ifsta->ssid_len);
+ ifmgd->ssid, ifmgd->ssid_len);
if (!bss)
goto exit;
@@ -108,7 +108,7 @@ void ieee80211_chswitch_work(struct work_struct *work)
ieee80211_rx_bss_put(sdata->local, bss);
exit:
- ifsta->flags &= ~IEEE80211_STA_CSA_RECEIVED;
+ ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
ieee80211_wake_queues_by_reason(&sdata->local->hw,
IEEE80211_QUEUE_STOP_REASON_CSA);
}
@@ -117,9 +117,9 @@ void ieee80211_chswitch_timer(unsigned long data)
{
struct ieee80211_sub_if_data *sdata =
(struct ieee80211_sub_if_data *) data;
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- queue_work(sdata->local->hw.workqueue, &ifsta->chswitch_work);
+ queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work);
}
void ieee80211_process_chanswitch(struct ieee80211_sub_if_data *sdata,
@@ -127,14 +127,14 @@ void ieee80211_process_chanswitch(struct ieee80211_sub_if_data *sdata,
struct ieee80211_bss *bss)
{
struct ieee80211_channel *new_ch;
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num);
/* FIXME: Handle ADHOC later */
if (sdata->vif.type != NL80211_IFTYPE_STATION)
return;
- if (ifsta->state != IEEE80211_STA_MLME_ASSOCIATED)
+ if (ifmgd->state != IEEE80211_STA_MLME_ASSOCIATED)
return;
if (sdata->local->sw_scanning || sdata->local->hw_scanning)
@@ -143,7 +143,7 @@ void ieee80211_process_chanswitch(struct ieee80211_sub_if_data *sdata,
/* Disregard subsequent beacons if we are already running a timer
processing a CSA */
- if (ifsta->flags & IEEE80211_STA_CSA_RECEIVED)
+ if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)
return;
new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
@@ -153,12 +153,12 @@ void ieee80211_process_chanswitch(struct ieee80211_sub_if_data *sdata,
sdata->local->csa_channel = new_ch;
if (sw_elem->count <= 1) {
- queue_work(sdata->local->hw.workqueue, &ifsta->chswitch_work);
+ queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work);
} else {
ieee80211_stop_queues_by_reason(&sdata->local->hw,
IEEE80211_QUEUE_STOP_REASON_CSA);
- ifsta->flags |= IEEE80211_STA_CSA_RECEIVED;
- mod_timer(&ifsta->chswitch_timer,
+ ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
+ mod_timer(&ifmgd->chswitch_timer,
jiffies +
msecs_to_jiffies(sw_elem->count *
bss->cbss.beacon_interval));
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 634f65c0130..4ba3c540fcf 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -202,6 +202,18 @@ void sta_info_destroy(struct sta_info *sta)
/* Make sure timer won't free the tid_rx struct, see below */
if (tid_rx)
tid_rx->shutdown = true;
+
+ /*
+ * The stop callback cannot find this station any more, but
+ * it didn't complete its work -- start the queue if necessary
+ */
+ if (sta->ampdu_mlme.tid_state_tx[i] & HT_AGG_STATE_INITIATOR_MSK &&
+ sta->ampdu_mlme.tid_state_tx[i] & HT_AGG_STATE_REQ_STOP_BA_MSK &&
+ local->hw.ampdu_queues)
+ ieee80211_wake_queue_by_reason(&local->hw,
+ local->hw.queues + sta->tid_to_tx_q[i],
+ IEEE80211_QUEUE_STOP_REASON_AGGREGATION);
+
spin_unlock_bh(&sta->lock);
/*
@@ -275,8 +287,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
* enable session_timer's data differentiation. refer to
* sta_rx_agg_session_timer_expired for useage */
sta->timer_to_tid[i] = i;
- /* tid to tx queue: initialize according to HW (0 is valid) */
- sta->tid_to_tx_q[i] = ieee80211_num_queues(&local->hw);
+ sta->tid_to_tx_q[i] = -1;
/* rx */
sta->ampdu_mlme.tid_state_rx[i] = HT_AGG_STATE_IDLE;
sta->ampdu_mlme.tid_rx[i] = NULL;
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index d9653231992..1f45573c580 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -90,6 +90,7 @@ struct tid_ampdu_tx {
* @buf_size: buffer size for incoming A-MPDUs
* @timeout: reset timer value (in TUs).
* @dialog_token: dialog token for aggregation session
+ * @shutdown: this session is being shut down due to STA removal
*/
struct tid_ampdu_rx {
struct sk_buff **reorder_buf;
@@ -200,7 +201,7 @@ struct sta_ampdu_mlme {
* @tid_seq: per-TID sequence numbers for sending to this STA
* @ampdu_mlme: A-MPDU state machine state
* @timer_to_tid: identity mapping to ID timers
- * @tid_to_tx_q: map tid to tx queue
+ * @tid_to_tx_q: map tid to tx queue (invalid == negative values)
* @llid: Local link ID
* @plid: Peer link ID
* @reason: Cancel reason on PLINK_HOLDING state
@@ -275,7 +276,7 @@ struct sta_info {
*/
struct sta_ampdu_mlme ampdu_mlme;
u8 timer_to_tid[STA_TID_NUM];
- u8 tid_to_tx_q[STA_TID_NUM];
+ s8 tid_to_tx_q[STA_TID_NUM];
#ifdef CONFIG_MAC80211_MESH
/*
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 33926831c64..c3f0e950125 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -876,7 +876,6 @@ ieee80211_tx_h_stats(struct ieee80211_tx_data *tx)
return TX_CONTINUE;
}
-
/* actual transmit path */
/*
@@ -1016,12 +1015,20 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
tx->sta = sta_info_get(local, hdr->addr1);
if (tx->sta && ieee80211_is_data_qos(hdr->frame_control)) {
+ unsigned long flags;
qc = ieee80211_get_qos_ctl(hdr);
tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
+ spin_lock_irqsave(&tx->sta->lock, flags);
state = &tx->sta->ampdu_mlme.tid_state_tx[tid];
- if (*state == HT_AGG_STATE_OPERATIONAL)
+ if (*state == HT_AGG_STATE_OPERATIONAL) {
info->flags |= IEEE80211_TX_CTL_AMPDU;
+ if (local->hw.ampdu_queues)
+ skb_set_queue_mapping(
+ skb, tx->local->hw.queues +
+ tx->sta->tid_to_tx_q[tid]);
+ }
+ spin_unlock_irqrestore(&tx->sta->lock, flags);
}
if (is_multicast_ether_addr(hdr->addr1)) {
@@ -1085,7 +1092,8 @@ static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb,
int ret, i;
if (skb) {
- if (netif_subqueue_stopped(local->mdev, skb))
+ if (ieee80211_queue_stopped(&local->hw,
+ skb_get_queue_mapping(skb)))
return IEEE80211_TX_PENDING;
ret = local->ops->tx(local_to_hw(local), skb);
@@ -1101,8 +1109,8 @@ static int __ieee80211_tx(struct ieee80211_local *local, struct sk_buff *skb,
info = IEEE80211_SKB_CB(tx->extra_frag[i]);
info->flags &= ~(IEEE80211_TX_CTL_CLEAR_PS_FILT |
IEEE80211_TX_CTL_FIRST_FRAGMENT);
- if (netif_subqueue_stopped(local->mdev,
- tx->extra_frag[i]))
+ if (ieee80211_queue_stopped(&local->hw,
+ skb_get_queue_mapping(tx->extra_frag[i])))
return IEEE80211_TX_FRAG_AGAIN;
ret = local->ops->tx(local_to_hw(local),
@@ -1625,7 +1633,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
case NL80211_IFTYPE_STATION:
fc |= cpu_to_le16(IEEE80211_FCTL_TODS);
/* BSSID SA DA */
- memcpy(hdr.addr1, sdata->u.sta.bssid, ETH_ALEN);
+ 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;
@@ -1634,7 +1642,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
/* DA SA BSSID */
memcpy(hdr.addr1, skb->data, ETH_ALEN);
memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
- memcpy(hdr.addr3, sdata->u.sta.bssid, ETH_ALEN);
+ memcpy(hdr.addr3, sdata->u.ibss.bssid, ETH_ALEN);
hdrlen = 24;
break;
default:
@@ -1920,7 +1928,6 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
struct ieee80211_tx_info *info;
struct ieee80211_sub_if_data *sdata = NULL;
struct ieee80211_if_ap *ap = NULL;
- struct ieee80211_if_sta *ifsta = NULL;
struct beacon_data *beacon;
struct ieee80211_supported_band *sband;
enum ieee80211_band band = local->hw.conf.channel->band;
@@ -1972,13 +1979,13 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
} else
goto out;
} else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+ struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct ieee80211_hdr *hdr;
- ifsta = &sdata->u.sta;
- if (!ifsta->probe_resp)
+ if (!ifibss->probe_resp)
goto out;
- skb = skb_copy(ifsta->probe_resp, GFP_ATOMIC);
+ skb = skb_copy(ifibss->probe_resp, GFP_ATOMIC);
if (!skb)
goto out;
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 73c7d7345ab..e0431a1d218 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -344,15 +344,36 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
{
struct ieee80211_local *local = hw_to_local(hw);
- /* we don't need to track ampdu queues */
- if (queue < ieee80211_num_regular_queues(hw)) {
- __clear_bit(reason, &local->queue_stop_reasons[queue]);
+ if (queue >= hw->queues) {
+ if (local->ampdu_ac_queue[queue - hw->queues] < 0)
+ return;
+
+ /*
+ * for virtual aggregation queues, we need to refcount the
+ * internal mac80211 disable (multiple times!), keep track of
+ * driver disable _and_ make sure the regular queue is
+ * actually enabled.
+ */
+ if (reason == IEEE80211_QUEUE_STOP_REASON_AGGREGATION)
+ local->amdpu_ac_stop_refcnt[queue - hw->queues]--;
+ else
+ __clear_bit(reason, &local->queue_stop_reasons[queue]);
- if (local->queue_stop_reasons[queue] != 0)
- /* someone still has this queue stopped */
+ if (local->queue_stop_reasons[queue] ||
+ local->amdpu_ac_stop_refcnt[queue - hw->queues])
return;
+
+ /* now go on to treat the corresponding regular queue */
+ queue = local->ampdu_ac_queue[queue - hw->queues];
+ reason = IEEE80211_QUEUE_STOP_REASON_AGGREGATION;
}
+ __clear_bit(reason, &local->queue_stop_reasons[queue]);
+
+ if (local->queue_stop_reasons[queue] != 0)
+ /* someone still has this queue stopped */
+ return;
+
if (test_bit(queue, local->queues_pending)) {
set_bit(queue, local->queues_pending_run);
tasklet_schedule(&local->tx_pending_tasklet);
@@ -361,8 +382,8 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
}
}
-static void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
- enum queue_stop_reason reason)
+void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
+ enum queue_stop_reason reason)
{
struct ieee80211_local *local = hw_to_local(hw);
unsigned long flags;
@@ -384,15 +405,33 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
{
struct ieee80211_local *local = hw_to_local(hw);
- /* we don't need to track ampdu queues */
- if (queue < ieee80211_num_regular_queues(hw))
- __set_bit(reason, &local->queue_stop_reasons[queue]);
+ if (queue >= hw->queues) {
+ if (local->ampdu_ac_queue[queue - hw->queues] < 0)
+ return;
+
+ /*
+ * for virtual aggregation queues, we need to refcount the
+ * internal mac80211 disable (multiple times!), keep track of
+ * driver disable _and_ make sure the regular queue is
+ * actually enabled.
+ */
+ if (reason == IEEE80211_QUEUE_STOP_REASON_AGGREGATION)
+ local->amdpu_ac_stop_refcnt[queue - hw->queues]++;
+ else
+ __set_bit(reason, &local->queue_stop_reasons[queue]);
+
+ /* now go on to treat the corresponding regular queue */
+ queue = local->ampdu_ac_queue[queue - hw->queues];
+ reason = IEEE80211_QUEUE_STOP_REASON_AGGREGATION;
+ }
+
+ __set_bit(reason, &local->queue_stop_reasons[queue]);
netif_stop_subqueue(local->mdev, queue);
}
-static void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
- enum queue_stop_reason reason)
+void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
+ enum queue_stop_reason reason)
{
struct ieee80211_local *local = hw_to_local(hw);
unsigned long flags;
@@ -418,7 +457,7 @@ void ieee80211_stop_queues_by_reason(struct ieee80211_hw *hw,
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
- for (i = 0; i < ieee80211_num_queues(hw); i++)
+ for (i = 0; i < hw->queues; i++)
__ieee80211_stop_queue(hw, i, reason);
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
@@ -434,6 +473,16 @@ EXPORT_SYMBOL(ieee80211_stop_queues);
int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue)
{
struct ieee80211_local *local = hw_to_local(hw);
+ unsigned long flags;
+
+ if (queue >= hw->queues) {
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+ queue = local->ampdu_ac_queue[queue - hw->queues];
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+ if (queue < 0)
+ return true;
+ }
+
return __netif_subqueue_stopped(local->mdev, queue);
}
EXPORT_SYMBOL(ieee80211_queue_stopped);
@@ -701,6 +750,27 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata)
local->ops->conf_tx(local_to_hw(local), i, &qparam);
}
+void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
+ const size_t supp_rates_len,
+ const u8 *supp_rates)
+{
+ struct ieee80211_local *local = sdata->local;
+ int i, have_higher_than_11mbit = 0;
+
+ /* cf. IEEE 802.11 9.2.12 */
+ for (i = 0; i < supp_rates_len; i++)
+ if ((supp_rates[i] & 0x7f) * 5 > 110)
+ have_higher_than_11mbit = 1;
+
+ if (local->hw.conf.channel->band == IEEE80211_BAND_2GHZ &&
+ have_higher_than_11mbit)
+ sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
+ else
+ sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
+
+ ieee80211_set_wmm_default(sdata);
+}
+
void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
int encrypt)
{
@@ -767,3 +837,161 @@ u32 ieee80211_mandatory_rates(struct ieee80211_local *local,
mandatory_rates |= BIT(i);
return mandatory_rates;
}
+
+void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
+ u16 transaction, u16 auth_alg,
+ u8 *extra, size_t extra_len,
+ const u8 *bssid, int encrypt)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct sk_buff *skb;
+ struct ieee80211_mgmt *mgmt;
+ const u8 *ie_auth = NULL;
+ int ie_auth_len = 0;
+
+ if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+ ie_auth_len = sdata->u.mgd.ie_auth_len;
+ ie_auth = sdata->u.mgd.ie_auth;
+ }
+
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom +
+ sizeof(*mgmt) + 6 + extra_len + ie_auth_len);
+ if (!skb) {
+ printk(KERN_DEBUG "%s: failed to allocate buffer for auth "
+ "frame\n", sdata->dev->name);
+ return;
+ }
+ skb_reserve(skb, local->hw.extra_tx_headroom);
+
+ mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24 + 6);
+ memset(mgmt, 0, 24 + 6);
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_AUTH);
+ if (encrypt)
+ mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+ memcpy(mgmt->da, bssid, ETH_ALEN);
+ memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+ memcpy(mgmt->bssid, bssid, ETH_ALEN);
+ mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg);
+ mgmt->u.auth.auth_transaction = cpu_to_le16(transaction);
+ mgmt->u.auth.status_code = cpu_to_le16(0);
+ if (extra)
+ memcpy(skb_put(skb, extra_len), extra, extra_len);
+ if (ie_auth)
+ memcpy(skb_put(skb, ie_auth_len), ie_auth, ie_auth_len);
+
+ ieee80211_tx_skb(sdata, skb, encrypt);
+}
+
+void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
+ u8 *ssid, size_t ssid_len,
+ u8 *ie, size_t ie_len)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_supported_band *sband;
+ struct sk_buff *skb;
+ struct ieee80211_mgmt *mgmt;
+ u8 *pos, *supp_rates, *esupp_rates = NULL, *extra_preq_ie = NULL;
+ int i, extra_preq_ie_len = 0;
+
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_STATION:
+ extra_preq_ie_len = sdata->u.mgd.ie_probereq_len;
+ extra_preq_ie = sdata->u.mgd.ie_probereq;
+ break;
+ default:
+ break;
+ }
+
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 +
+ ie_len + extra_preq_ie_len);
+ if (!skb) {
+ printk(KERN_DEBUG "%s: failed to allocate buffer for probe "
+ "request\n", sdata->dev->name);
+ return;
+ }
+ skb_reserve(skb, local->hw.extra_tx_headroom);
+
+ mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
+ memset(mgmt, 0, 24);
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_PROBE_REQ);
+ memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
+ if (dst) {
+ memcpy(mgmt->da, dst, ETH_ALEN);
+ memcpy(mgmt->bssid, dst, ETH_ALEN);
+ } else {
+ memset(mgmt->da, 0xff, ETH_ALEN);
+ memset(mgmt->bssid, 0xff, ETH_ALEN);
+ }
+ pos = skb_put(skb, 2 + ssid_len);
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = ssid_len;
+ memcpy(pos, ssid, ssid_len);
+
+ supp_rates = skb_put(skb, 2);
+ supp_rates[0] = WLAN_EID_SUPP_RATES;
+ supp_rates[1] = 0;
+ sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+
+ for (i = 0; i < sband->n_bitrates; i++) {
+ struct ieee80211_rate *rate = &sband->bitrates[i];
+ if (esupp_rates) {
+ pos = skb_put(skb, 1);
+ esupp_rates[1]++;
+ } else if (supp_rates[1] == 8) {
+ esupp_rates = skb_put(skb, 3);
+ esupp_rates[0] = WLAN_EID_EXT_SUPP_RATES;
+ esupp_rates[1] = 1;
+ pos = &esupp_rates[2];
+ } else {
+ pos = skb_put(skb, 1);
+ supp_rates[1]++;
+ }
+ *pos = rate->bitrate / 5;
+ }
+
+ if (ie)
+ memcpy(skb_put(skb, ie_len), ie, ie_len);
+ if (extra_preq_ie)
+ memcpy(skb_put(skb, extra_preq_ie_len), extra_preq_ie,
+ extra_preq_ie_len);
+
+ ieee80211_tx_skb(sdata, skb, 0);
+}
+
+u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
+ struct ieee802_11_elems *elems,
+ enum ieee80211_band band)
+{
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_rate *bitrates;
+ size_t num_rates;
+ u32 supp_rates;
+ int i, j;
+ sband = local->hw.wiphy->bands[band];
+
+ if (!sband) {
+ WARN_ON(1);
+ sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+ }
+
+ bitrates = sband->bitrates;
+ num_rates = sband->n_bitrates;
+ supp_rates = 0;
+ for (i = 0; i < elems->supp_rates_len +
+ elems->ext_supp_rates_len; i++) {
+ u8 rate = 0;
+ int own_rate;
+ if (i < elems->supp_rates_len)
+ rate = elems->supp_rates[i];
+ else if (elems->ext_supp_rates)
+ rate = elems->ext_supp_rates
+ [i - elems->supp_rates_len];
+ own_rate = 5 * (rate & 0x7f);
+ for (j = 0; j < num_rates; j++)
+ if (bitrates[j].bitrate == own_rate)
+ supp_rates |= BIT(j);
+ }
+ return supp_rates;
+}
diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c
index 2b023dce8b2..f6924fc065d 100644
--- a/net/mac80211/wext.c
+++ b/net/mac80211/wext.c
@@ -132,139 +132,37 @@ static int ieee80211_ioctl_siwgenie(struct net_device *dev,
if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME)
return -EOPNOTSUPP;
- if (sdata->vif.type == NL80211_IFTYPE_STATION ||
- sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+ if (sdata->vif.type == NL80211_IFTYPE_STATION) {
int ret = ieee80211_sta_set_extra_ie(sdata, extra, data->length);
if (ret)
return ret;
- sdata->u.sta.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
- ieee80211_sta_req_auth(sdata, &sdata->u.sta);
+ sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
+ ieee80211_sta_req_auth(sdata);
return 0;
}
return -EOPNOTSUPP;
}
-static u8 ieee80211_get_wstats_flags(struct ieee80211_local *local)
-{
- u8 wstats_flags = 0;
-
- wstats_flags |= local->hw.flags & (IEEE80211_HW_SIGNAL_UNSPEC |
- IEEE80211_HW_SIGNAL_DBM) ?
- IW_QUAL_QUAL_UPDATED : IW_QUAL_QUAL_INVALID;
- wstats_flags |= local->hw.flags & IEEE80211_HW_NOISE_DBM ?
- IW_QUAL_NOISE_UPDATED : IW_QUAL_NOISE_INVALID;
- if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
- wstats_flags |= IW_QUAL_DBM;
-
- return wstats_flags;
-}
-
-static int ieee80211_ioctl_giwrange(struct net_device *dev,
- struct iw_request_info *info,
- struct iw_point *data, char *extra)
-{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct iw_range *range = (struct iw_range *) extra;
- enum ieee80211_band band;
- int c = 0;
-
- data->length = sizeof(struct iw_range);
- memset(range, 0, sizeof(struct iw_range));
-
- range->we_version_compiled = WIRELESS_EXT;
- range->we_version_source = 21;
- range->retry_capa = IW_RETRY_LIMIT;
- range->retry_flags = IW_RETRY_LIMIT;
- range->min_retry = 0;
- range->max_retry = 255;
- range->min_rts = 0;
- range->max_rts = 2347;
- range->min_frag = 256;
- range->max_frag = 2346;
-
- range->encoding_size[0] = 5;
- range->encoding_size[1] = 13;
- range->num_encoding_sizes = 2;
- range->max_encoding_tokens = NUM_DEFAULT_KEYS;
-
- /* cfg80211 requires this, and enforces 0..100 */
- if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)
- range->max_qual.level = 100;
- else if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
- range->max_qual.level = -110;
- else
- range->max_qual.level = 0;
-
- if (local->hw.flags & IEEE80211_HW_NOISE_DBM)
- range->max_qual.noise = -110;
- else
- range->max_qual.noise = 0;
-
- range->max_qual.qual = 100;
- range->max_qual.updated = ieee80211_get_wstats_flags(local);
-
- range->avg_qual.qual = 50;
- /* not always true but better than nothing */
- range->avg_qual.level = range->max_qual.level / 2;
- range->avg_qual.noise = range->max_qual.noise / 2;
- range->avg_qual.updated = ieee80211_get_wstats_flags(local);
-
- range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
- IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
-
-
- for (band = 0; band < IEEE80211_NUM_BANDS; band ++) {
- int i;
- struct ieee80211_supported_band *sband;
-
- sband = local->hw.wiphy->bands[band];
-
- if (!sband)
- continue;
-
- for (i = 0; i < sband->n_channels && c < IW_MAX_FREQUENCIES; i++) {
- struct ieee80211_channel *chan = &sband->channels[i];
-
- if (!(chan->flags & IEEE80211_CHAN_DISABLED)) {
- range->freq[c].i =
- ieee80211_frequency_to_channel(
- chan->center_freq);
- range->freq[c].m = chan->center_freq;
- range->freq[c].e = 6;
- c++;
- }
- }
- }
- range->num_channels = c;
- range->num_frequency = c;
-
- IW_EVENT_CAPA_SET_KERNEL(range->event_capa);
- IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP);
- IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);
-
- range->scan_capa |= IW_SCAN_CAPA_ESSID;
-
- return 0;
-}
-
-
static int ieee80211_ioctl_siwfreq(struct net_device *dev,
struct iw_request_info *info,
struct iw_freq *freq, char *extra)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->vif.type == NL80211_IFTYPE_ADHOC ||
- sdata->vif.type == NL80211_IFTYPE_STATION)
- sdata->u.sta.flags &= ~IEEE80211_STA_AUTO_CHANNEL_SEL;
+ if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+ sdata->u.ibss.flags &= ~IEEE80211_IBSS_AUTO_CHANNEL_SEL;
+ else if (sdata->vif.type == NL80211_IFTYPE_STATION)
+ sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_CHANNEL_SEL;
/* freq->e == 0: freq->m = channel; otherwise freq = m * 10^e */
if (freq->e == 0) {
if (freq->m < 0) {
- if (sdata->vif.type == NL80211_IFTYPE_ADHOC ||
- sdata->vif.type == NL80211_IFTYPE_STATION)
- sdata->u.sta.flags |=
+ if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+ sdata->u.ibss.flags |=
+ IEEE80211_IBSS_AUTO_CHANNEL_SEL;
+ else if (sdata->vif.type == NL80211_IFTYPE_STATION)
+ sdata->u.mgd.flags |=
IEEE80211_STA_AUTO_CHANNEL_SEL;
return 0;
} else
@@ -301,32 +199,35 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev,
{
struct ieee80211_sub_if_data *sdata;
size_t len = data->length;
+ int ret;
/* iwconfig uses nul termination in SSID.. */
if (len > 0 && ssid[len - 1] == '\0')
len--;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->vif.type == NL80211_IFTYPE_STATION ||
- sdata->vif.type == NL80211_IFTYPE_ADHOC) {
- int ret;
+ if (sdata->vif.type == NL80211_IFTYPE_STATION) {
if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) {
if (len > IEEE80211_MAX_SSID_LEN)
return -EINVAL;
- memcpy(sdata->u.sta.ssid, ssid, len);
- sdata->u.sta.ssid_len = len;
+ memcpy(sdata->u.mgd.ssid, ssid, len);
+ sdata->u.mgd.ssid_len = len;
return 0;
}
+
if (data->flags)
- sdata->u.sta.flags &= ~IEEE80211_STA_AUTO_SSID_SEL;
+ sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL;
else
- sdata->u.sta.flags |= IEEE80211_STA_AUTO_SSID_SEL;
+ sdata->u.mgd.flags |= IEEE80211_STA_AUTO_SSID_SEL;
+
ret = ieee80211_sta_set_ssid(sdata, ssid, len);
if (ret)
return ret;
- ieee80211_sta_req_auth(sdata, &sdata->u.sta);
+
+ ieee80211_sta_req_auth(sdata);
return 0;
- }
+ } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+ return ieee80211_ibss_set_ssid(sdata, ssid, len);
return -EOPNOTSUPP;
}
@@ -340,8 +241,7 @@ static int ieee80211_ioctl_giwessid(struct net_device *dev,
struct ieee80211_sub_if_data *sdata;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->vif.type == NL80211_IFTYPE_STATION ||
- sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+ if (sdata->vif.type == NL80211_IFTYPE_STATION) {
int res = ieee80211_sta_get_ssid(sdata, ssid, &len);
if (res == 0) {
data->length = len;
@@ -349,6 +249,14 @@ static int ieee80211_ioctl_giwessid(struct net_device *dev,
} else
data->flags = 0;
return res;
+ } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+ int res = ieee80211_ibss_get_ssid(sdata, ssid, &len);
+ if (res == 0) {
+ data->length = len;
+ data->flags = 1;
+ } else
+ data->flags = 0;
+ return res;
}
return -EOPNOTSUPP;
@@ -362,26 +270,35 @@ static int ieee80211_ioctl_siwap(struct net_device *dev,
struct ieee80211_sub_if_data *sdata;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->vif.type == NL80211_IFTYPE_STATION ||
- sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+ if (sdata->vif.type == NL80211_IFTYPE_STATION) {
int ret;
if (sdata->flags & IEEE80211_SDATA_USERSPACE_MLME) {
- memcpy(sdata->u.sta.bssid, (u8 *) &ap_addr->sa_data,
+ memcpy(sdata->u.mgd.bssid, (u8 *) &ap_addr->sa_data,
ETH_ALEN);
return 0;
}
if (is_zero_ether_addr((u8 *) &ap_addr->sa_data))
- sdata->u.sta.flags |= IEEE80211_STA_AUTO_BSSID_SEL |
+ sdata->u.mgd.flags |= IEEE80211_STA_AUTO_BSSID_SEL |
IEEE80211_STA_AUTO_CHANNEL_SEL;
else if (is_broadcast_ether_addr((u8 *) &ap_addr->sa_data))
- sdata->u.sta.flags |= IEEE80211_STA_AUTO_BSSID_SEL;
+ sdata->u.mgd.flags |= IEEE80211_STA_AUTO_BSSID_SEL;
else
- sdata->u.sta.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
+ sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
ret = ieee80211_sta_set_bssid(sdata, (u8 *) &ap_addr->sa_data);
if (ret)
return ret;
- ieee80211_sta_req_auth(sdata, &sdata->u.sta);
+ ieee80211_sta_req_auth(sdata);
return 0;
+ } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+ if (is_zero_ether_addr((u8 *) &ap_addr->sa_data))
+ sdata->u.ibss.flags |= IEEE80211_IBSS_AUTO_BSSID_SEL |
+ IEEE80211_IBSS_AUTO_CHANNEL_SEL;
+ else if (is_broadcast_ether_addr((u8 *) &ap_addr->sa_data))
+ sdata->u.ibss.flags |= IEEE80211_IBSS_AUTO_BSSID_SEL;
+ else
+ sdata->u.ibss.flags &= ~IEEE80211_IBSS_AUTO_BSSID_SEL;
+
+ return ieee80211_ibss_set_bssid(sdata, (u8 *) &ap_addr->sa_data);
} else if (sdata->vif.type == NL80211_IFTYPE_WDS) {
/*
* If it is necessary to update the WDS peer address
@@ -410,17 +327,20 @@ static int ieee80211_ioctl_giwap(struct net_device *dev,
struct ieee80211_sub_if_data *sdata;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->vif.type == NL80211_IFTYPE_STATION ||
- sdata->vif.type == NL80211_IFTYPE_ADHOC) {
- if (sdata->u.sta.state == IEEE80211_STA_MLME_ASSOCIATED ||
- sdata->u.sta.state == IEEE80211_STA_MLME_IBSS_JOINED) {
+ if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+ if (sdata->u.mgd.state == IEEE80211_STA_MLME_ASSOCIATED) {
ap_addr->sa_family = ARPHRD_ETHER;
- memcpy(&ap_addr->sa_data, sdata->u.sta.bssid, ETH_ALEN);
- return 0;
- } else {
+ memcpy(&ap_addr->sa_data, sdata->u.mgd.bssid, ETH_ALEN);
+ } else
memset(&ap_addr->sa_data, 0, ETH_ALEN);
- return 0;
- }
+ return 0;
+ } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+ if (sdata->u.ibss.state == IEEE80211_IBSS_MLME_JOINED) {
+ ap_addr->sa_family = ARPHRD_ETHER;
+ memcpy(&ap_addr->sa_data, sdata->u.ibss.bssid, ETH_ALEN);
+ } else
+ memset(&ap_addr->sa_data, 0, ETH_ALEN);
+ return 0;
} else if (sdata->vif.type == NL80211_IFTYPE_WDS) {
ap_addr->sa_family = ARPHRD_ETHER;
memcpy(&ap_addr->sa_data, sdata->u.wds.remote_addr, ETH_ALEN);
@@ -486,7 +406,7 @@ static int ieee80211_ioctl_giwrate(struct net_device *dev,
rcu_read_lock();
- sta = sta_info_get(local, sdata->u.sta.bssid);
+ sta = sta_info_get(local, sdata->u.mgd.bssid);
if (sta && !(sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS))
rate->value = sband->bitrates[sta->last_tx_rate.idx].bitrate;
@@ -687,8 +607,7 @@ static int ieee80211_ioctl_siwmlme(struct net_device *dev,
struct iw_mlme *mlme = (struct iw_mlme *) extra;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->vif.type != NL80211_IFTYPE_STATION &&
- sdata->vif.type != NL80211_IFTYPE_ADHOC)
+ if (!(sdata->vif.type == NL80211_IFTYPE_STATION))
return -EINVAL;
switch (mlme->cmd) {
@@ -784,8 +703,7 @@ static int ieee80211_ioctl_giwencode(struct net_device *dev,
erq->flags |= IW_ENCODE_ENABLED;
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
- struct ieee80211_if_sta *ifsta = &sdata->u.sta;
- switch (ifsta->auth_alg) {
+ switch (sdata->u.mgd.auth_alg) {
case WLAN_AUTH_OPEN:
case WLAN_AUTH_LEAP:
erq->flags |= IW_ENCODE_OPEN;
@@ -849,7 +767,7 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev,
ret = ieee80211_hw_config(local,
IEEE80211_CONF_CHANGE_DYNPS_TIMEOUT);
- if (!(sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED))
+ if (!(sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED))
return ret;
if (conf->dynamic_ps_timeout > 0 &&
@@ -908,10 +826,10 @@ static int ieee80211_ioctl_siwauth(struct net_device *dev,
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
if (data->value & (IW_AUTH_CIPHER_WEP40 |
IW_AUTH_CIPHER_WEP104 | IW_AUTH_CIPHER_TKIP))
- sdata->u.sta.flags |=
+ sdata->u.mgd.flags |=
IEEE80211_STA_TKIP_WEP_USED;
else
- sdata->u.sta.flags &=
+ sdata->u.mgd.flags &=
~IEEE80211_STA_TKIP_WEP_USED;
}
break;
@@ -922,21 +840,20 @@ static int ieee80211_ioctl_siwauth(struct net_device *dev,
if (sdata->vif.type != NL80211_IFTYPE_STATION)
ret = -EINVAL;
else {
- sdata->u.sta.flags &= ~IEEE80211_STA_PRIVACY_INVOKED;
+ sdata->u.mgd.flags &= ~IEEE80211_STA_PRIVACY_INVOKED;
/*
* Privacy invoked by wpa_supplicant, store the
* value and allow associating to a protected
* network without having a key up front.
*/
if (data->value)
- sdata->u.sta.flags |=
+ sdata->u.mgd.flags |=
IEEE80211_STA_PRIVACY_INVOKED;
}
break;
case IW_AUTH_80211_AUTH_ALG:
- if (sdata->vif.type == NL80211_IFTYPE_STATION ||
- sdata->vif.type == NL80211_IFTYPE_ADHOC)
- sdata->u.sta.auth_algs = data->value;
+ if (sdata->vif.type == NL80211_IFTYPE_STATION)
+ sdata->u.mgd.auth_algs = data->value;
else
ret = -EOPNOTSUPP;
break;
@@ -945,17 +862,16 @@ static int ieee80211_ioctl_siwauth(struct net_device *dev,
ret = -EOPNOTSUPP;
break;
}
- if (sdata->vif.type == NL80211_IFTYPE_STATION ||
- sdata->vif.type == NL80211_IFTYPE_ADHOC) {
+ if (sdata->vif.type == NL80211_IFTYPE_STATION) {
switch (data->value) {
case IW_AUTH_MFP_DISABLED:
- sdata->u.sta.mfp = IEEE80211_MFP_DISABLED;
+ sdata->u.mgd.mfp = IEEE80211_MFP_DISABLED;
break;
case IW_AUTH_MFP_OPTIONAL:
- sdata->u.sta.mfp = IEEE80211_MFP_OPTIONAL;
+ sdata->u.mgd.mfp = IEEE80211_MFP_OPTIONAL;
break;
case IW_AUTH_MFP_REQUIRED:
- sdata->u.sta.mfp = IEEE80211_MFP_REQUIRED;
+ sdata->u.mgd.mfp = IEEE80211_MFP_REQUIRED;
break;
default:
ret = -EINVAL;
@@ -970,6 +886,21 @@ static int ieee80211_ioctl_siwauth(struct net_device *dev,
return ret;
}
+static u8 ieee80211_get_wstats_flags(struct ieee80211_local *local)
+{
+ u8 wstats_flags = 0;
+
+ wstats_flags |= local->hw.flags & (IEEE80211_HW_SIGNAL_UNSPEC |
+ IEEE80211_HW_SIGNAL_DBM) ?
+ IW_QUAL_QUAL_UPDATED : IW_QUAL_QUAL_INVALID;
+ wstats_flags |= local->hw.flags & IEEE80211_HW_NOISE_DBM ?
+ IW_QUAL_NOISE_UPDATED : IW_QUAL_NOISE_INVALID;
+ if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
+ wstats_flags |= IW_QUAL_DBM;
+
+ return wstats_flags;
+}
+
/* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */
static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev)
{
@@ -980,9 +911,9 @@ static struct iw_statistics *ieee80211_get_wireless_stats(struct net_device *dev
rcu_read_lock();
- if (sdata->vif.type == NL80211_IFTYPE_STATION ||
- sdata->vif.type == NL80211_IFTYPE_ADHOC)
- sta = sta_info_get(local, sdata->u.sta.bssid);
+ if (sdata->vif.type == NL80211_IFTYPE_STATION)
+ sta = sta_info_get(local, sdata->u.mgd.bssid);
+
if (!sta) {
wstats->discard.fragment = 0;
wstats->discard.misc = 0;
@@ -1011,9 +942,8 @@ static int ieee80211_ioctl_giwauth(struct net_device *dev,
switch (data->flags & IW_AUTH_INDEX) {
case IW_AUTH_80211_AUTH_ALG:
- if (sdata->vif.type == NL80211_IFTYPE_STATION ||
- sdata->vif.type == NL80211_IFTYPE_ADHOC)
- data->value = sdata->u.sta.auth_algs;
+ if (sdata->vif.type == NL80211_IFTYPE_STATION)
+ data->value = sdata->u.mgd.auth_algs;
else
ret = -EOPNOTSUPP;
break;
@@ -1116,7 +1046,7 @@ static const iw_handler ieee80211_handler[] =
(iw_handler) NULL, /* SIOCSIWSENS */
(iw_handler) NULL, /* SIOCGIWSENS */
(iw_handler) NULL /* not used */, /* SIOCSIWRANGE */
- (iw_handler) ieee80211_ioctl_giwrange, /* SIOCGIWRANGE */
+ (iw_handler) cfg80211_wext_giwrange, /* SIOCGIWRANGE */
(iw_handler) NULL /* not used */, /* SIOCSIWPRIV */
(iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */
(iw_handler) NULL /* not used */, /* SIOCSIWSTATS */
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index ac71b38f7cb..093a4ab7f28 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -114,9 +114,7 @@ u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb)
{
struct ieee80211_master_priv *mpriv = netdev_priv(dev);
struct ieee80211_local *local = mpriv->local;
- struct ieee80211_hw *hw = &local->hw;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- struct sta_info *sta;
u16 queue;
u8 tid;
@@ -124,29 +122,11 @@ u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb)
if (unlikely(queue >= local->hw.queues))
queue = local->hw.queues - 1;
- if (skb->requeue) {
- if (!hw->ampdu_queues)
- return queue;
-
- rcu_read_lock();
- sta = sta_info_get(local, hdr->addr1);
- tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
- if (sta) {
- int ampdu_queue = sta->tid_to_tx_q[tid];
-
- if ((ampdu_queue < ieee80211_num_queues(hw)) &&
- test_bit(ampdu_queue, local->queue_pool))
- queue = ampdu_queue;
- }
- rcu_read_unlock();
-
- return queue;
- }
-
- /* Now we know the 1d priority, fill in the QoS header if
- * there is one.
+ /*
+ * Now we know the 1d priority, fill in the QoS header if
+ * there is one (and we haven't done this before).
*/
- if (ieee80211_is_data_qos(hdr->frame_control)) {
+ if (!skb->requeue && ieee80211_is_data_qos(hdr->frame_control)) {
u8 *p = ieee80211_get_qos_ctl(hdr);
u8 ack_policy = 0;
tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
@@ -156,140 +136,7 @@ u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb)
/* qos header is 2 bytes, second reserved */
*p++ = ack_policy | tid;
*p = 0;
-
- if (!hw->ampdu_queues)
- return queue;
-
- rcu_read_lock();
-
- sta = sta_info_get(local, hdr->addr1);
- if (sta) {
- int ampdu_queue = sta->tid_to_tx_q[tid];
-
- if ((ampdu_queue < ieee80211_num_queues(hw)) &&
- test_bit(ampdu_queue, local->queue_pool))
- queue = ampdu_queue;
- }
-
- rcu_read_unlock();
}
return queue;
}
-
-int ieee80211_ht_agg_queue_add(struct ieee80211_local *local,
- struct sta_info *sta, u16 tid)
-{
- int i;
-
- /* XXX: currently broken due to cb/requeue use */
- return -EPERM;
-
- /* prepare the filter and save it for the SW queue
- * matching the received HW queue */
-
- if (!local->hw.ampdu_queues)
- return -EPERM;
-
- /* try to get a Qdisc from the pool */
- for (i = local->hw.queues; i < ieee80211_num_queues(&local->hw); i++)
- if (!test_and_set_bit(i, local->queue_pool)) {
- ieee80211_stop_queue(local_to_hw(local), i);
- sta->tid_to_tx_q[tid] = i;
-
- /* IF there are already pending packets
- * on this tid first we need to drain them
- * on the previous queue
- * since HT is strict in order */
-#ifdef CONFIG_MAC80211_HT_DEBUG
- if (net_ratelimit())
- printk(KERN_DEBUG "allocated aggregation queue"
- " %d tid %d addr %pM pool=0x%lX\n",
- i, tid, sta->sta.addr,
- local->queue_pool[0]);
-#endif /* CONFIG_MAC80211_HT_DEBUG */
- return 0;
- }
-
- return -EAGAIN;
-}
-
-/**
- * the caller needs to hold netdev_get_tx_queue(local->mdev, X)->lock
- */
-void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local,
- struct sta_info *sta, u16 tid,
- u8 requeue)
-{
- int agg_queue = sta->tid_to_tx_q[tid];
- struct ieee80211_hw *hw = &local->hw;
-
- /* return the qdisc to the pool */
- clear_bit(agg_queue, local->queue_pool);
- sta->tid_to_tx_q[tid] = ieee80211_num_queues(hw);
-
- if (requeue) {
- ieee80211_requeue(local, agg_queue);
- } else {
- struct netdev_queue *txq;
- spinlock_t *root_lock;
- struct Qdisc *q;
-
- txq = netdev_get_tx_queue(local->mdev, agg_queue);
- q = rcu_dereference(txq->qdisc);
- root_lock = qdisc_lock(q);
-
- spin_lock_bh(root_lock);
- qdisc_reset(q);
- spin_unlock_bh(root_lock);
- }
-}
-
-void ieee80211_requeue(struct ieee80211_local *local, int queue)
-{
- struct netdev_queue *txq = netdev_get_tx_queue(local->mdev, queue);
- struct sk_buff_head list;
- spinlock_t *root_lock;
- struct Qdisc *qdisc;
- u32 len;
-
- rcu_read_lock_bh();
-
- qdisc = rcu_dereference(txq->qdisc);
- if (!qdisc || !qdisc->dequeue)
- goto out_unlock;
-
- skb_queue_head_init(&list);
-
- root_lock = qdisc_root_lock(qdisc);
- spin_lock(root_lock);
- for (len = qdisc->q.qlen; len > 0; len--) {
- struct sk_buff *skb = qdisc->dequeue(qdisc);
-
- if (skb)
- __skb_queue_tail(&list, skb);
- }
- spin_unlock(root_lock);
-
- for (len = list.qlen; len > 0; len--) {
- struct sk_buff *skb = __skb_dequeue(&list);
- u16 new_queue;
-
- BUG_ON(!skb);
- new_queue = ieee80211_select_queue(local->mdev, skb);
- skb_set_queue_mapping(skb, new_queue);
-
- txq = netdev_get_tx_queue(local->mdev, new_queue);
-
-
- qdisc = rcu_dereference(txq->qdisc);
- root_lock = qdisc_root_lock(qdisc);
-
- spin_lock(root_lock);
- qdisc_enqueue_root(skb, qdisc);
- spin_unlock(root_lock);
- }
-
-out_unlock:
- rcu_read_unlock_bh();
-}
diff --git a/net/mac80211/wme.h b/net/mac80211/wme.h
index bc62f28a4d3..7520d2e014d 100644
--- a/net/mac80211/wme.h
+++ b/net/mac80211/wme.h
@@ -21,11 +21,5 @@
extern const int ieee802_1d_to_ac[8];
u16 ieee80211_select_queue(struct net_device *dev, struct sk_buff *skb);
-int ieee80211_ht_agg_queue_add(struct ieee80211_local *local,
- struct sta_info *sta, u16 tid);
-void ieee80211_ht_agg_queue_remove(struct ieee80211_local *local,
- struct sta_info *sta, u16 tid,
- u8 requeue);
-void ieee80211_requeue(struct ieee80211_local *local, int queue);
#endif /* _WME_H */
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 0668b2bfc1d..dd7f222919f 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -7,7 +7,6 @@
#include <linux/if.h>
#include <linux/module.h>
#include <linux/err.h>
-#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/nl80211.h>
#include <linux/debugfs.h>
@@ -31,18 +30,29 @@ MODULE_DESCRIPTION("wireless configuration support");
* only read the list, and that can happen quite
* often because we need to do it for each command */
LIST_HEAD(cfg80211_drv_list);
-DEFINE_MUTEX(cfg80211_drv_mutex);
+
+/*
+ * This is used to protect the cfg80211_drv_list, cfg80211_regdomain,
+ * country_ie_regdomain, the reg_beacon_list and the the last regulatory
+ * request receipt (last_request).
+ */
+DEFINE_MUTEX(cfg80211_mutex);
/* for debugfs */
static struct dentry *ieee80211_debugfs_dir;
-/* requires cfg80211_drv_mutex to be held! */
-static struct cfg80211_registered_device *cfg80211_drv_by_wiphy(int wiphy)
+/* requires cfg80211_mutex to be held! */
+struct cfg80211_registered_device *cfg80211_drv_by_wiphy_idx(int wiphy_idx)
{
struct cfg80211_registered_device *result = NULL, *drv;
+ if (!wiphy_idx_valid(wiphy_idx))
+ return NULL;
+
+ assert_cfg80211_lock();
+
list_for_each_entry(drv, &cfg80211_drv_list, list) {
- if (drv->idx == wiphy) {
+ if (drv->wiphy_idx == wiphy_idx) {
result = drv;
break;
}
@@ -51,17 +61,44 @@ static struct cfg80211_registered_device *cfg80211_drv_by_wiphy(int wiphy)
return result;
}
+int get_wiphy_idx(struct wiphy *wiphy)
+{
+ struct cfg80211_registered_device *drv;
+ if (!wiphy)
+ return WIPHY_IDX_STALE;
+ drv = wiphy_to_dev(wiphy);
+ return drv->wiphy_idx;
+}
+
/* requires cfg80211_drv_mutex to be held! */
+struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx)
+{
+ struct cfg80211_registered_device *drv;
+
+ if (!wiphy_idx_valid(wiphy_idx))
+ return NULL;
+
+ assert_cfg80211_lock();
+
+ drv = cfg80211_drv_by_wiphy_idx(wiphy_idx);
+ if (!drv)
+ return NULL;
+ return &drv->wiphy;
+}
+
+/* requires cfg80211_mutex to be held! */
static struct cfg80211_registered_device *
__cfg80211_drv_from_info(struct genl_info *info)
{
int ifindex;
- struct cfg80211_registered_device *bywiphy = NULL, *byifidx = NULL;
+ struct cfg80211_registered_device *bywiphyidx = NULL, *byifidx = NULL;
struct net_device *dev;
int err = -EINVAL;
+ assert_cfg80211_lock();
+
if (info->attrs[NL80211_ATTR_WIPHY]) {
- bywiphy = cfg80211_drv_by_wiphy(
+ bywiphyidx = cfg80211_drv_by_wiphy_idx(
nla_get_u32(info->attrs[NL80211_ATTR_WIPHY]));
err = -ENODEV;
}
@@ -78,14 +115,14 @@ __cfg80211_drv_from_info(struct genl_info *info)
err = -ENODEV;
}
- if (bywiphy && byifidx) {
- if (bywiphy != byifidx)
+ if (bywiphyidx && byifidx) {
+ if (bywiphyidx != byifidx)
return ERR_PTR(-EINVAL);
else
- return bywiphy; /* == byifidx */
+ return bywiphyidx; /* == byifidx */
}
- if (bywiphy)
- return bywiphy;
+ if (bywiphyidx)
+ return bywiphyidx;
if (byifidx)
return byifidx;
@@ -98,7 +135,7 @@ cfg80211_get_dev_from_info(struct genl_info *info)
{
struct cfg80211_registered_device *drv;
- mutex_lock(&cfg80211_drv_mutex);
+ mutex_lock(&cfg80211_mutex);
drv = __cfg80211_drv_from_info(info);
/* if it is not an error we grab the lock on
@@ -107,7 +144,7 @@ cfg80211_get_dev_from_info(struct genl_info *info)
if (!IS_ERR(drv))
mutex_lock(&drv->mtx);
- mutex_unlock(&cfg80211_drv_mutex);
+ mutex_unlock(&cfg80211_mutex);
return drv;
}
@@ -118,7 +155,7 @@ cfg80211_get_dev_from_ifindex(int ifindex)
struct cfg80211_registered_device *drv = ERR_PTR(-ENODEV);
struct net_device *dev;
- mutex_lock(&cfg80211_drv_mutex);
+ mutex_lock(&cfg80211_mutex);
dev = dev_get_by_index(&init_net, ifindex);
if (!dev)
goto out;
@@ -129,7 +166,7 @@ cfg80211_get_dev_from_ifindex(int ifindex)
drv = ERR_PTR(-ENODEV);
dev_put(dev);
out:
- mutex_unlock(&cfg80211_drv_mutex);
+ mutex_unlock(&cfg80211_mutex);
return drv;
}
@@ -143,16 +180,16 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
char *newname)
{
struct cfg80211_registered_device *drv;
- int idx, taken = -1, result, digits;
+ int wiphy_idx, taken = -1, result, digits;
- mutex_lock(&cfg80211_drv_mutex);
+ mutex_lock(&cfg80211_mutex);
/* prohibit calling the thing phy%d when %d is not its number */
- sscanf(newname, PHY_NAME "%d%n", &idx, &taken);
- if (taken == strlen(newname) && idx != rdev->idx) {
- /* count number of places needed to print idx */
+ sscanf(newname, PHY_NAME "%d%n", &wiphy_idx, &taken);
+ if (taken == strlen(newname) && wiphy_idx != rdev->wiphy_idx) {
+ /* count number of places needed to print wiphy_idx */
digits = 1;
- while (idx /= 10)
+ while (wiphy_idx /= 10)
digits++;
/*
* deny the name if it is phy<idx> where <idx> is printed
@@ -193,7 +230,7 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
result = 0;
out_unlock:
- mutex_unlock(&cfg80211_drv_mutex);
+ mutex_unlock(&cfg80211_mutex);
if (result == 0)
nl80211_notify_dev_rename(rdev);
@@ -220,22 +257,22 @@ struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv)
drv->ops = ops;
- mutex_lock(&cfg80211_drv_mutex);
+ mutex_lock(&cfg80211_mutex);
- drv->idx = wiphy_counter++;
+ drv->wiphy_idx = wiphy_counter++;
- if (unlikely(drv->idx < 0)) {
+ if (unlikely(!wiphy_idx_valid(drv->wiphy_idx))) {
wiphy_counter--;
- mutex_unlock(&cfg80211_drv_mutex);
+ mutex_unlock(&cfg80211_mutex);
/* ugh, wrapped! */
kfree(drv);
return NULL;
}
- mutex_unlock(&cfg80211_drv_mutex);
+ mutex_unlock(&cfg80211_mutex);
/* give it a proper name */
- dev_set_name(&drv->wiphy.dev, PHY_NAME "%d", drv->idx);
+ dev_set_name(&drv->wiphy.dev, PHY_NAME "%d", drv->wiphy_idx);
mutex_init(&drv->mtx);
mutex_init(&drv->devlist_mtx);
@@ -310,7 +347,7 @@ int wiphy_register(struct wiphy *wiphy)
/* check and set up bitrates */
ieee80211_set_bitrate_flags(wiphy);
- mutex_lock(&cfg80211_drv_mutex);
+ mutex_lock(&cfg80211_mutex);
/* set up regulatory info */
wiphy_update_regulatory(wiphy, REGDOM_SET_BY_CORE);
@@ -330,7 +367,7 @@ int wiphy_register(struct wiphy *wiphy)
res = 0;
out_unlock:
- mutex_unlock(&cfg80211_drv_mutex);
+ mutex_unlock(&cfg80211_mutex);
return res;
}
EXPORT_SYMBOL(wiphy_register);
@@ -340,7 +377,7 @@ void wiphy_unregister(struct wiphy *wiphy)
struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy);
/* protect the device list */
- mutex_lock(&cfg80211_drv_mutex);
+ mutex_lock(&cfg80211_mutex);
BUG_ON(!list_empty(&drv->netdev_list));
@@ -366,7 +403,7 @@ void wiphy_unregister(struct wiphy *wiphy)
device_del(&drv->wiphy.dev);
debugfs_remove(drv->wiphy.debugfsdir);
- mutex_unlock(&cfg80211_drv_mutex);
+ mutex_unlock(&cfg80211_mutex);
}
EXPORT_SYMBOL(wiphy_unregister);
diff --git a/net/wireless/core.h b/net/wireless/core.h
index e29ad4cd464..f6c53f5807f 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -10,6 +10,7 @@
#include <linux/netdevice.h>
#include <linux/kref.h>
#include <linux/rbtree.h>
+#include <linux/mutex.h>
#include <net/genetlink.h>
#include <net/wireless.h>
#include <net/cfg80211.h>
@@ -37,7 +38,7 @@ struct cfg80211_registered_device {
enum environment_cap env;
/* wiphy index, internal only */
- int idx;
+ int wiphy_idx;
/* associate netdev list */
struct mutex devlist_mtx;
@@ -49,6 +50,7 @@ struct cfg80211_registered_device {
struct rb_root bss_tree;
u32 bss_generation;
struct cfg80211_scan_request *scan_req; /* protected by RTNL */
+ unsigned long suspend_at;
/* must be last because of the way we do wiphy_priv(),
* and it should at least be aligned to NETDEV_ALIGN */
@@ -62,9 +64,27 @@ struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy)
return container_of(wiphy, struct cfg80211_registered_device, wiphy);
}
-extern struct mutex cfg80211_drv_mutex;
+/* Note 0 is valid, hence phy0 */
+static inline
+bool wiphy_idx_valid(int wiphy_idx)
+{
+ return (wiphy_idx >= 0);
+}
+
+extern struct mutex cfg80211_mutex;
extern struct list_head cfg80211_drv_list;
+static inline void assert_cfg80211_lock(void)
+{
+ WARN_ON(!mutex_is_locked(&cfg80211_mutex));
+}
+
+/*
+ * You can use this to mark a wiphy_idx as not having an associated wiphy.
+ * It guarantees cfg80211_drv_by_wiphy_idx(wiphy_idx) will return NULL
+ */
+#define WIPHY_IDX_STALE -1
+
struct cfg80211_internal_bss {
struct list_head list;
struct rb_node rbn;
@@ -74,6 +94,9 @@ struct cfg80211_internal_bss {
struct cfg80211_bss pub;
};
+struct cfg80211_registered_device *cfg80211_drv_by_wiphy_idx(int wiphy_idx);
+int get_wiphy_idx(struct wiphy *wiphy);
+
/*
* This function returns a pointer to the driver
* that the genl_info item that is passed refers to.
@@ -81,13 +104,13 @@ struct cfg80211_internal_bss {
* the driver's mutex!
*
* This means that you need to call cfg80211_put_dev()
- * before being allowed to acquire &cfg80211_drv_mutex!
+ * before being allowed to acquire &cfg80211_mutex!
*
* This is necessary because we need to lock the global
* mutex to get an item off the list safely, and then
* we lock the drv mutex so it doesn't go away under us.
*
- * We don't want to keep cfg80211_drv_mutex locked
+ * We don't want to keep cfg80211_mutex locked
* for all the time in order to allow requests on
* other interfaces to go through at the same time.
*
@@ -97,6 +120,9 @@ struct cfg80211_internal_bss {
extern struct cfg80211_registered_device *
cfg80211_get_dev_from_info(struct genl_info *info);
+/* requires cfg80211_drv_mutex to be held! */
+struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx);
+
/* identical to cfg80211_get_dev_from_info but only operate on ifindex */
extern struct cfg80211_registered_device *
cfg80211_get_dev_from_ifindex(int ifindex);
@@ -113,5 +139,7 @@ void ieee80211_set_bitrate_flags(struct wiphy *wiphy);
void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby);
void cfg80211_bss_expire(struct cfg80211_registered_device *dev);
+void cfg80211_bss_age(struct cfg80211_registered_device *dev,
+ unsigned long age_secs);
#endif /* __NET_WIRELESS_CORE_H */
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 298a4de5994..531bb67cf50 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -7,7 +7,6 @@
#include <linux/if.h>
#include <linux/module.h>
#include <linux/err.h>
-#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/if_ether.h>
#include <linux/ieee80211.h>
@@ -142,7 +141,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
if (!hdr)
return -1;
- NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->idx);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx);
NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
dev->wiphy.max_scan_ssids);
@@ -256,7 +255,7 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
int start = cb->args[0];
struct cfg80211_registered_device *dev;
- mutex_lock(&cfg80211_drv_mutex);
+ mutex_lock(&cfg80211_mutex);
list_for_each_entry(dev, &cfg80211_drv_list, list) {
if (++idx <= start)
continue;
@@ -267,7 +266,7 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
break;
}
}
- mutex_unlock(&cfg80211_drv_mutex);
+ mutex_unlock(&cfg80211_mutex);
cb->args[0] = idx;
@@ -470,7 +469,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
struct cfg80211_registered_device *dev;
struct wireless_dev *wdev;
- mutex_lock(&cfg80211_drv_mutex);
+ mutex_lock(&cfg80211_mutex);
list_for_each_entry(dev, &cfg80211_drv_list, list) {
if (wp_idx < wp_start) {
wp_idx++;
@@ -497,7 +496,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
wp_idx++;
}
out:
- mutex_unlock(&cfg80211_drv_mutex);
+ mutex_unlock(&cfg80211_mutex);
cb->args[0] = wp_idx;
cb->args[1] = if_idx;
@@ -1206,6 +1205,12 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
nla_nest_end(msg, txrate);
}
+ if (sinfo->filled & STATION_INFO_RX_PACKETS)
+ NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS,
+ sinfo->rx_packets);
+ if (sinfo->filled & STATION_INFO_TX_PACKETS)
+ NLA_PUT_U32(msg, NL80211_STA_INFO_TX_PACKETS,
+ sinfo->tx_packets);
nla_nest_end(msg, sinfoattr);
return genlmsg_end(msg, hdr);
@@ -1900,6 +1905,19 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
int r;
char *data = NULL;
+ /*
+ * You should only get this when cfg80211 hasn't yet initialized
+ * completely when built-in to the kernel right between the time
+ * window between nl80211_init() and regulatory_init(), if that is
+ * even possible.
+ */
+ mutex_lock(&cfg80211_mutex);
+ if (unlikely(!cfg80211_regdomain)) {
+ mutex_unlock(&cfg80211_mutex);
+ return -EINPROGRESS;
+ }
+ mutex_unlock(&cfg80211_mutex);
+
if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
return -EINVAL;
@@ -1910,14 +1928,9 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
if (is_world_regdom(data))
return -EINVAL;
#endif
- mutex_lock(&cfg80211_drv_mutex);
- r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data, 0, ENVIRON_ANY);
- mutex_unlock(&cfg80211_drv_mutex);
- /* This means the regulatory domain was already set, however
- * we don't want to confuse userspace with a "successful error"
- * message so lets just treat it as a success */
- if (r == -EALREADY)
- r = 0;
+
+ r = regulatory_hint_user(data);
+
return r;
}
@@ -2106,7 +2119,7 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
unsigned int i;
int err = -EINVAL;
- mutex_lock(&cfg80211_drv_mutex);
+ mutex_lock(&cfg80211_mutex);
if (!cfg80211_regdomain)
goto out;
@@ -2169,7 +2182,7 @@ nla_put_failure:
genlmsg_cancel(msg, hdr);
err = -EMSGSIZE;
out:
- mutex_unlock(&cfg80211_drv_mutex);
+ mutex_unlock(&cfg80211_mutex);
return err;
}
@@ -2228,9 +2241,9 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
BUG_ON(rule_idx != num_rules);
- mutex_lock(&cfg80211_drv_mutex);
+ mutex_lock(&cfg80211_mutex);
r = set_regdom(rd);
- mutex_unlock(&cfg80211_drv_mutex);
+ mutex_unlock(&cfg80211_mutex);
return r;
bad_reg:
@@ -2286,6 +2299,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
struct wiphy *wiphy;
int err, tmp, n_ssids = 0, n_channels = 0, i;
enum ieee80211_band band;
+ size_t ie_len;
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
if (err)
@@ -2327,9 +2341,15 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
goto out_unlock;
}
+ if (info->attrs[NL80211_ATTR_IE])
+ ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ else
+ ie_len = 0;
+
request = kzalloc(sizeof(*request)
+ sizeof(*ssid) * n_ssids
- + sizeof(channel) * n_channels, GFP_KERNEL);
+ + sizeof(channel) * n_channels
+ + ie_len, GFP_KERNEL);
if (!request) {
err = -ENOMEM;
goto out_unlock;
@@ -2340,6 +2360,12 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
if (n_ssids)
request->ssids = (void *)(request->channels + n_channels);
request->n_ssids = n_ssids;
+ if (ie_len) {
+ if (request->ssids)
+ request->ie = (void *)(request->ssids + n_ssids);
+ else
+ request->ie = (void *)(request->channels + n_channels);
+ }
if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
/* user specified, bail out if channel not found */
@@ -2380,6 +2406,12 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
}
}
+ if (info->attrs[NL80211_ATTR_IE]) {
+ request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ memcpy(request->ie, nla_data(info->attrs[NL80211_ATTR_IE]),
+ request->ie_len);
+ }
+
request->ifidx = dev->ifindex;
request->wiphy = &drv->wiphy;
@@ -2432,7 +2464,7 @@ static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability);
NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq);
- switch (res->signal_type) {
+ switch (rdev->wiphy.signal_type) {
case CFG80211_SIGNAL_TYPE_MBM:
NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal);
break;
@@ -2601,7 +2633,6 @@ static struct genl_ops nl80211_ops[] = {
.doit = nl80211_get_station,
.dumpit = nl80211_dump_station,
.policy = nl80211_policy,
- .flags = GENL_ADMIN_PERM,
},
{
.cmd = NL80211_CMD_SET_STATION,
@@ -2739,7 +2770,7 @@ static int nl80211_send_scan_donemsg(struct sk_buff *msg,
if (!hdr)
return -1;
- NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->idx);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
/* XXX: we should probably bounce back the request? */
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index b565a5f84e9..69787b62136 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -27,6 +27,10 @@ static inline void
nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
struct net_device *netdev)
{}
+static inline void nl80211_send_scan_aborted(
+ struct cfg80211_registered_device *rdev,
+ struct net_device *netdev)
+{}
#endif /* CONFIG_NL80211 */
#endif /* __NET_WIRELESS_NL80211_H */
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 2323644330c..ce66bfdf57e 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -54,22 +54,52 @@ static u32 supported_bandwidths[] = {
MHZ_TO_KHZ(20),
};
-/* Central wireless core regulatory domains, we only need two,
+/*
+ * Central wireless core regulatory domains, we only need two,
* the current one and a world regulatory domain in case we have no
- * information to give us an alpha2 */
+ * information to give us an alpha2
+ */
const struct ieee80211_regdomain *cfg80211_regdomain;
-/* We use this as a place for the rd structure built from the
+/*
+ * We use this as a place for the rd structure built from the
* last parsed country IE to rest until CRDA gets back to us with
- * what it thinks should apply for the same country */
+ * what it thinks should apply for the same country
+ */
static const struct ieee80211_regdomain *country_ie_regdomain;
+/* Used to queue up regulatory hints */
+static LIST_HEAD(reg_requests_list);
+static spinlock_t reg_requests_lock;
+
+/* Used to queue up beacon hints for review */
+static LIST_HEAD(reg_pending_beacons);
+static spinlock_t reg_pending_beacons_lock;
+
+/* Used to keep track of processed beacon hints */
+static LIST_HEAD(reg_beacon_list);
+
+struct reg_beacon {
+ struct list_head list;
+ struct ieee80211_channel chan;
+};
+
/* We keep a static world regulatory domain in case of the absence of CRDA */
static const struct ieee80211_regdomain world_regdom = {
- .n_reg_rules = 1,
+ .n_reg_rules = 3,
.alpha2 = "00",
.reg_rules = {
- REG_RULE(2412-10, 2462+10, 40, 6, 20,
+ /* IEEE 802.11b/g, channels 1..11 */
+ REG_RULE(2412-10, 2462+10, 40, 6, 20, 0),
+ /* IEEE 802.11a, channel 36..48 */
+ REG_RULE(5180-10, 5240+10, 40, 6, 23,
+ NL80211_RRF_PASSIVE_SCAN |
+ NL80211_RRF_NO_IBSS),
+
+ /* NB: 5260 MHz - 5700 MHz requies DFS */
+
+ /* IEEE 802.11a, channel 149..165 */
+ REG_RULE(5745-10, 5825+10, 40, 6, 23,
NL80211_RRF_PASSIVE_SCAN |
NL80211_RRF_NO_IBSS),
}
@@ -83,9 +113,11 @@ static char *ieee80211_regdom = "US";
module_param(ieee80211_regdom, charp, 0444);
MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
-/* We assume 40 MHz bandwidth for the old regulatory work.
+/*
+ * We assume 40 MHz bandwidth for the old regulatory work.
* We make emphasis we are using the exact same frequencies
- * as before */
+ * as before
+ */
static const struct ieee80211_regdomain us_regdom = {
.n_reg_rules = 6,
@@ -124,8 +156,10 @@ static const struct ieee80211_regdomain jp_regdom = {
static const struct ieee80211_regdomain eu_regdom = {
.n_reg_rules = 6,
- /* This alpha2 is bogus, we leave it here just for stupid
- * backward compatibility */
+ /*
+ * This alpha2 is bogus, we leave it here just for stupid
+ * backward compatibility
+ */
.alpha2 = "EU",
.reg_rules = {
/* IEEE 802.11b/g, channels 1..13 */
@@ -194,8 +228,10 @@ static void reset_regdomains(void)
cfg80211_regdomain = NULL;
}
-/* Dynamic world regulatory domain requested by the wireless
- * core upon initialization */
+/*
+ * Dynamic world regulatory domain requested by the wireless
+ * core upon initialization
+ */
static void update_world_regdomain(const struct ieee80211_regdomain *rd)
{
BUG_ON(!last_request);
@@ -236,8 +272,10 @@ static bool is_unknown_alpha2(const char *alpha2)
{
if (!alpha2)
return false;
- /* Special case where regulatory domain was built by driver
- * but a specific alpha2 cannot be determined */
+ /*
+ * Special case where regulatory domain was built by driver
+ * but a specific alpha2 cannot be determined
+ */
if (alpha2[0] == '9' && alpha2[1] == '9')
return true;
return false;
@@ -247,9 +285,11 @@ static bool is_intersected_alpha2(const char *alpha2)
{
if (!alpha2)
return false;
- /* Special case where regulatory domain is the
+ /*
+ * Special case where regulatory domain is the
* result of an intersection between two regulatory domain
- * structures */
+ * structures
+ */
if (alpha2[0] == '9' && alpha2[1] == '8')
return true;
return false;
@@ -274,8 +314,10 @@ static bool alpha2_equal(const char *alpha2_x, const char *alpha2_y)
return false;
}
-static bool regdom_changed(const char *alpha2)
+static bool regdom_changes(const char *alpha2)
{
+ assert_cfg80211_lock();
+
if (!cfg80211_regdomain)
return true;
if (alpha2_equal(cfg80211_regdomain->alpha2, alpha2))
@@ -302,8 +344,10 @@ static bool country_ie_integrity_changes(u32 checksum)
return false;
}
-/* This lets us keep regulatory code which is updated on a regulatory
- * basis in userspace. */
+/*
+ * This lets us keep regulatory code which is updated on a regulatory
+ * basis in userspace.
+ */
static int call_crda(const char *alpha2)
{
char country_env[9 + 2] = "COUNTRY=";
@@ -414,10 +458,12 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,
#undef ONE_GHZ_IN_KHZ
}
-/* Converts a country IE to a regulatory domain. A regulatory domain
+/*
+ * Converts a country IE to a regulatory domain. A regulatory domain
* structure has a lot of information which the IE doesn't yet have,
* so for the other values we use upper max values as we will intersect
- * with our userspace regulatory agent to get lower bounds. */
+ * with our userspace regulatory agent to get lower bounds.
+ */
static struct ieee80211_regdomain *country_ie_2_rd(
u8 *country_ie,
u8 country_ie_len,
@@ -462,9 +508,11 @@ static struct ieee80211_regdomain *country_ie_2_rd(
*checksum ^= ((flags ^ alpha2[0] ^ alpha2[1]) << 8);
- /* We need to build a reg rule for each triplet, but first we must
+ /*
+ * We need to build a reg rule for each triplet, but first we must
* calculate the number of reg rules we will need. We will need one
- * for each channel subband */
+ * for each channel subband
+ */
while (country_ie_len >= 3) {
int end_channel = 0;
struct ieee80211_country_ie_triplet *triplet =
@@ -502,9 +550,11 @@ static struct ieee80211_regdomain *country_ie_2_rd(
if (cur_sub_max_channel < cur_channel)
return NULL;
- /* Do not allow overlapping channels. Also channels
+ /*
+ * Do not allow overlapping channels. Also channels
* passed in each subband must be monotonically
- * increasing */
+ * increasing
+ */
if (last_sub_max_channel) {
if (cur_channel <= last_sub_max_channel)
return NULL;
@@ -512,10 +562,12 @@ static struct ieee80211_regdomain *country_ie_2_rd(
return NULL;
}
- /* When dot11RegulatoryClassesRequired is supported
+ /*
+ * When dot11RegulatoryClassesRequired is supported
* we can throw ext triplets as part of this soup,
* for now we don't care when those change as we
- * don't support them */
+ * don't support them
+ */
*checksum ^= ((cur_channel ^ cur_sub_max_channel) << 8) |
((cur_sub_max_channel ^ cur_sub_max_channel) << 16) |
((triplet->chans.max_power ^ cur_sub_max_channel) << 24);
@@ -526,8 +578,10 @@ static struct ieee80211_regdomain *country_ie_2_rd(
country_ie_len -= 3;
num_rules++;
- /* Note: this is not a IEEE requirement but
- * simply a memory requirement */
+ /*
+ * Note: this is not a IEEE requirement but
+ * simply a memory requirement
+ */
if (num_rules > NL80211_MAX_SUPP_REG_RULES)
return NULL;
}
@@ -555,8 +609,10 @@ static struct ieee80211_regdomain *country_ie_2_rd(
struct ieee80211_freq_range *freq_range = NULL;
struct ieee80211_power_rule *power_rule = NULL;
- /* Must parse if dot11RegulatoryClassesRequired is true,
- * we don't support this yet */
+ /*
+ * Must parse if dot11RegulatoryClassesRequired is true,
+ * we don't support this yet
+ */
if (triplet->ext.reg_extension_id >=
IEEE80211_COUNTRY_EXTENSION_ID) {
country_ie += 3;
@@ -578,10 +634,12 @@ static struct ieee80211_regdomain *country_ie_2_rd(
end_channel = triplet->chans.first_channel +
(4 * (triplet->chans.num_channels - 1));
- /* The +10 is since the regulatory domain expects
+ /*
+ * The +10 is since the regulatory domain expects
* the actual band edge, not the center of freq for
* its start and end freqs, assuming 20 MHz bandwidth on
- * the channels passed */
+ * the channels passed
+ */
freq_range->start_freq_khz =
MHZ_TO_KHZ(ieee80211_channel_to_frequency(
triplet->chans.first_channel) - 10);
@@ -589,9 +647,11 @@ static struct ieee80211_regdomain *country_ie_2_rd(
MHZ_TO_KHZ(ieee80211_channel_to_frequency(
end_channel) + 10);
- /* Large arbitrary values, we intersect later */
- /* Increment this if we ever support >= 40 MHz channels
- * in IEEE 802.11 */
+ /*
+ * These are large arbitrary values we use to intersect later.
+ * Increment this if we ever support >= 40 MHz channels
+ * in IEEE 802.11
+ */
freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40);
power_rule->max_antenna_gain = DBI_TO_MBI(100);
power_rule->max_eirp = DBM_TO_MBM(100);
@@ -607,8 +667,10 @@ static struct ieee80211_regdomain *country_ie_2_rd(
}
-/* Helper for regdom_intersect(), this does the real
- * mathematical intersection fun */
+/*
+ * Helper for regdom_intersect(), this does the real
+ * mathematical intersection fun
+ */
static int reg_rules_intersect(
const struct ieee80211_reg_rule *rule1,
const struct ieee80211_reg_rule *rule2,
@@ -686,11 +748,13 @@ static struct ieee80211_regdomain *regdom_intersect(
if (!rd1 || !rd2)
return NULL;
- /* First we get a count of the rules we'll need, then we actually
+ /*
+ * First we get a count of the rules we'll need, then we actually
* build them. This is to so we can malloc() and free() a
* regdomain once. The reason we use reg_rules_intersect() here
* is it will return -EINVAL if the rule computed makes no sense.
- * All rules that do check out OK are valid. */
+ * All rules that do check out OK are valid.
+ */
for (x = 0; x < rd1->n_reg_rules; x++) {
rule1 = &rd1->reg_rules[x];
@@ -718,14 +782,18 @@ static struct ieee80211_regdomain *regdom_intersect(
rule1 = &rd1->reg_rules[x];
for (y = 0; y < rd2->n_reg_rules; y++) {
rule2 = &rd2->reg_rules[y];
- /* This time around instead of using the stack lets
+ /*
+ * This time around instead of using the stack lets
* write to the target rule directly saving ourselves
- * a memcpy() */
+ * a memcpy()
+ */
intersected_rule = &rd->reg_rules[rule_idx];
r = reg_rules_intersect(rule1, rule2,
intersected_rule);
- /* No need to memset here the intersected rule here as
- * we're not using the stack anymore */
+ /*
+ * No need to memset here the intersected rule here as
+ * we're not using the stack anymore
+ */
if (r)
continue;
rule_idx++;
@@ -744,8 +812,10 @@ static struct ieee80211_regdomain *regdom_intersect(
return rd;
}
-/* XXX: add support for the rest of enum nl80211_reg_rule_flags, we may
- * want to just have the channel structure use these */
+/*
+ * XXX: add support for the rest of enum nl80211_reg_rule_flags, we may
+ * want to just have the channel structure use these
+ */
static u32 map_regdom_flags(u32 rd_flags)
{
u32 channel_flags = 0;
@@ -771,8 +841,10 @@ static int freq_reg_info_regd(struct wiphy *wiphy,
regd = custom_regd ? custom_regd : cfg80211_regdomain;
- /* Follow the driver's regulatory domain, if present, unless a country
- * IE has been processed or a user wants to help complaince further */
+ /*
+ * Follow the driver's regulatory domain, if present, unless a country
+ * IE has been processed or a user wants to help complaince further
+ */
if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE &&
last_request->initiator != REGDOM_SET_BY_USER &&
wiphy->regd)
@@ -790,9 +862,11 @@ static int freq_reg_info_regd(struct wiphy *wiphy,
fr = &rr->freq_range;
pr = &rr->power_rule;
- /* We only need to know if one frequency rule was
+ /*
+ * We only need to know if one frequency rule was
* was in center_freq's band, that's enough, so lets
- * not overwrite it once found */
+ * not overwrite it once found
+ */
if (!band_rule_found)
band_rule_found = freq_in_rule_band(fr, center_freq);
@@ -829,6 +903,11 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band,
const struct ieee80211_power_rule *power_rule = NULL;
struct ieee80211_supported_band *sband;
struct ieee80211_channel *chan;
+ struct wiphy *request_wiphy = NULL;
+
+ assert_cfg80211_lock();
+
+ request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
sband = wiphy->bands[band];
BUG_ON(chan_idx >= sband->n_channels);
@@ -840,7 +919,8 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band,
&max_bandwidth, &reg_rule);
if (r) {
- /* This means no regulatory rule was found in the country IE
+ /*
+ * This means no regulatory rule was found in the country IE
* with a frequency range on the center_freq's band, since
* IEEE-802.11 allows for a country IE to have a subset of the
* regulatory information provided in a country we ignore
@@ -859,8 +939,10 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band,
chan->center_freq, wiphy_name(wiphy));
#endif
} else {
- /* In this case we know the country IE has at least one reg rule
- * for the band so we respect its band definitions */
+ /*
+ * In this case we know the country IE has at least one reg rule
+ * for the band so we respect its band definitions
+ */
#ifdef CONFIG_CFG80211_REG_DEBUG
if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE)
printk(KERN_DEBUG "cfg80211: Disabling "
@@ -877,11 +959,13 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band,
power_rule = &reg_rule->power_rule;
if (last_request->initiator == REGDOM_SET_BY_DRIVER &&
- last_request->wiphy && last_request->wiphy == wiphy &&
- last_request->wiphy->strict_regulatory) {
- /* This gaurantees the driver's requested regulatory domain
+ request_wiphy && request_wiphy == wiphy &&
+ request_wiphy->strict_regulatory) {
+ /*
+ * This gaurantees the driver's requested regulatory domain
* will always be used as a base for further regulatory
- * settings */
+ * settings
+ */
chan->flags = chan->orig_flags =
map_regdom_flags(reg_rule->flags);
chan->max_antenna_gain = chan->orig_mag =
@@ -922,8 +1006,10 @@ static bool ignore_reg_update(struct wiphy *wiphy, enum reg_set_by setby)
if (setby == REGDOM_SET_BY_CORE &&
wiphy->custom_regulatory)
return true;
- /* wiphy->regd will be set once the device has its own
- * desired regulatory domain set */
+ /*
+ * wiphy->regd will be set once the device has its own
+ * desired regulatory domain set
+ */
if (wiphy->strict_regulatory && !wiphy->regd &&
!is_world_regdom(last_request->alpha2))
return true;
@@ -938,16 +1024,120 @@ static void update_all_wiphy_regulatory(enum reg_set_by setby)
wiphy_update_regulatory(&drv->wiphy, setby);
}
+static void handle_reg_beacon(struct wiphy *wiphy,
+ unsigned int chan_idx,
+ struct reg_beacon *reg_beacon)
+{
+#ifdef CONFIG_CFG80211_REG_DEBUG
+#define REG_DEBUG_BEACON_FLAG(desc) \
+ printk(KERN_DEBUG "cfg80211: Enabling " desc " on " \
+ "frequency: %d MHz (Ch %d) on %s\n", \
+ reg_beacon->chan.center_freq, \
+ ieee80211_frequency_to_channel(reg_beacon->chan.center_freq), \
+ wiphy_name(wiphy));
+#else
+#define REG_DEBUG_BEACON_FLAG(desc) do {} while (0)
+#endif
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *chan;
+
+ assert_cfg80211_lock();
+
+ sband = wiphy->bands[reg_beacon->chan.band];
+ chan = &sband->channels[chan_idx];
+
+ if (likely(chan->center_freq != reg_beacon->chan.center_freq))
+ return;
+
+ if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) {
+ chan->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
+ REG_DEBUG_BEACON_FLAG("active scanning");
+ }
+
+ if (chan->flags & IEEE80211_CHAN_NO_IBSS) {
+ chan->flags &= ~IEEE80211_CHAN_NO_IBSS;
+ REG_DEBUG_BEACON_FLAG("beaconing");
+ }
+
+ chan->beacon_found = true;
+#undef REG_DEBUG_BEACON_FLAG
+}
+
+/*
+ * Called when a scan on a wiphy finds a beacon on
+ * new channel
+ */
+static void wiphy_update_new_beacon(struct wiphy *wiphy,
+ struct reg_beacon *reg_beacon)
+{
+ unsigned int i;
+ struct ieee80211_supported_band *sband;
+
+ assert_cfg80211_lock();
+
+ if (!wiphy->bands[reg_beacon->chan.band])
+ return;
+
+ sband = wiphy->bands[reg_beacon->chan.band];
+
+ for (i = 0; i < sband->n_channels; i++)
+ handle_reg_beacon(wiphy, i, reg_beacon);
+}
+
+/*
+ * Called upon reg changes or a new wiphy is added
+ */
+static void wiphy_update_beacon_reg(struct wiphy *wiphy)
+{
+ unsigned int i;
+ struct ieee80211_supported_band *sband;
+ struct reg_beacon *reg_beacon;
+
+ assert_cfg80211_lock();
+
+ if (list_empty(&reg_beacon_list))
+ return;
+
+ list_for_each_entry(reg_beacon, &reg_beacon_list, list) {
+ if (!wiphy->bands[reg_beacon->chan.band])
+ continue;
+ sband = wiphy->bands[reg_beacon->chan.band];
+ for (i = 0; i < sband->n_channels; i++)
+ handle_reg_beacon(wiphy, i, reg_beacon);
+ }
+}
+
+static bool reg_is_world_roaming(struct wiphy *wiphy)
+{
+ if (is_world_regdom(cfg80211_regdomain->alpha2) ||
+ (wiphy->regd && is_world_regdom(wiphy->regd->alpha2)))
+ return true;
+ if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE &&
+ wiphy->custom_regulatory)
+ return true;
+ return false;
+}
+
+/* Reap the advantages of previously found beacons */
+static void reg_process_beacons(struct wiphy *wiphy)
+{
+ if (!reg_is_world_roaming(wiphy))
+ return;
+ wiphy_update_beacon_reg(wiphy);
+}
+
void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby)
{
enum ieee80211_band band;
if (ignore_reg_update(wiphy, setby))
- return;
+ goto out;
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
if (wiphy->bands[band])
handle_band(wiphy, band);
}
+out:
+ reg_process_beacons(wiphy);
if (wiphy->reg_notifier)
wiphy->reg_notifier(wiphy, last_request);
}
@@ -1033,48 +1223,53 @@ static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd,
return 0;
}
-/* Return value which can be used by ignore_request() to indicate
- * it has been determined we should intersect two regulatory domains */
+/*
+ * Return value which can be used by ignore_request() to indicate
+ * it has been determined we should intersect two regulatory domains
+ */
#define REG_INTERSECT 1
/* This has the logic which determines when a new request
* should be ignored. */
-static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by,
- const char *alpha2)
+static int ignore_request(struct wiphy *wiphy,
+ struct regulatory_request *pending_request)
{
+ struct wiphy *last_wiphy = NULL;
+
+ assert_cfg80211_lock();
+
/* All initial requests are respected */
if (!last_request)
return 0;
- switch (set_by) {
+ switch (pending_request->initiator) {
case REGDOM_SET_BY_INIT:
return -EINVAL;
case REGDOM_SET_BY_CORE:
- /*
- * Always respect new wireless core hints, should only happen
- * when updating the world regulatory domain at init.
- */
- return 0;
+ return -EINVAL;
case REGDOM_SET_BY_COUNTRY_IE:
- if (unlikely(!is_an_alpha2(alpha2)))
+
+ last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
+
+ if (unlikely(!is_an_alpha2(pending_request->alpha2)))
return -EINVAL;
if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) {
- if (last_request->wiphy != wiphy) {
+ if (last_wiphy != wiphy) {
/*
* Two cards with two APs claiming different
* different Country IE alpha2s. We could
* intersect them, but that seems unlikely
* to be correct. Reject second one for now.
*/
- if (!alpha2_equal(alpha2,
- cfg80211_regdomain->alpha2))
+ if (regdom_changes(pending_request->alpha2))
return -EOPNOTSUPP;
return -EALREADY;
}
- /* Two consecutive Country IE hints on the same wiphy.
- * This should be picked up early by the driver/stack */
- if (WARN_ON(!alpha2_equal(cfg80211_regdomain->alpha2,
- alpha2)))
+ /*
+ * Two consecutive Country IE hints on the same wiphy.
+ * This should be picked up early by the driver/stack
+ */
+ if (WARN_ON(regdom_changes(pending_request->alpha2)))
return 0;
return -EALREADY;
}
@@ -1083,31 +1278,44 @@ static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by,
if (last_request->initiator == REGDOM_SET_BY_CORE) {
if (is_old_static_regdom(cfg80211_regdomain))
return 0;
- if (!alpha2_equal(cfg80211_regdomain->alpha2, alpha2))
+ if (regdom_changes(pending_request->alpha2))
return 0;
return -EALREADY;
}
+
+ /*
+ * This would happen if you unplug and plug your card
+ * back in or if you add a new device for which the previously
+ * loaded card also agrees on the regulatory domain.
+ */
+ if (last_request->initiator == REGDOM_SET_BY_DRIVER &&
+ !regdom_changes(pending_request->alpha2))
+ return -EALREADY;
+
return REG_INTERSECT;
case REGDOM_SET_BY_USER:
if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE)
return REG_INTERSECT;
- /* If the user knows better the user should set the regdom
- * to their country before the IE is picked up */
+ /*
+ * If the user knows better the user should set the regdom
+ * to their country before the IE is picked up
+ */
if (last_request->initiator == REGDOM_SET_BY_USER &&
last_request->intersect)
return -EOPNOTSUPP;
- /* Process user requests only after previous user/driver/core
- * requests have been processed */
+ /*
+ * Process user requests only after previous user/driver/core
+ * requests have been processed
+ */
if (last_request->initiator == REGDOM_SET_BY_CORE ||
last_request->initiator == REGDOM_SET_BY_DRIVER ||
last_request->initiator == REGDOM_SET_BY_USER) {
- if (!alpha2_equal(last_request->alpha2,
- cfg80211_regdomain->alpha2))
+ if (regdom_changes(last_request->alpha2))
return -EAGAIN;
}
if (!is_old_static_regdom(cfg80211_regdomain) &&
- alpha2_equal(cfg80211_regdomain->alpha2, alpha2))
+ !regdom_changes(pending_request->alpha2))
return -EALREADY;
return 0;
@@ -1116,55 +1324,66 @@ static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by,
return -EINVAL;
}
-/* Caller must hold &cfg80211_drv_mutex */
-int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by,
- const char *alpha2,
- u32 country_ie_checksum,
- enum environment_cap env)
+/**
+ * __regulatory_hint - hint to the wireless core a regulatory domain
+ * @wiphy: if the hint comes from country information from an AP, this
+ * is required to be set to the wiphy that received the information
+ * @pending_request: the regulatory request currently being processed
+ *
+ * The Wireless subsystem can use this function to hint to the wireless core
+ * what it believes should be the current regulatory domain.
+ *
+ * Returns zero if all went fine, %-EALREADY if a regulatory domain had
+ * already been set or other standard error codes.
+ *
+ * Caller must hold &cfg80211_mutex
+ */
+static int __regulatory_hint(struct wiphy *wiphy,
+ struct regulatory_request *pending_request)
{
- struct regulatory_request *request;
bool intersect = false;
int r = 0;
- r = ignore_request(wiphy, set_by, alpha2);
+ assert_cfg80211_lock();
+
+ r = ignore_request(wiphy, pending_request);
if (r == REG_INTERSECT) {
- if (set_by == REGDOM_SET_BY_DRIVER) {
+ if (pending_request->initiator == REGDOM_SET_BY_DRIVER) {
r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain);
- if (r)
+ if (r) {
+ kfree(pending_request);
return r;
+ }
}
intersect = true;
} else if (r) {
- /* If the regulatory domain being requested by the
+ /*
+ * If the regulatory domain being requested by the
* driver has already been set just copy it to the
- * wiphy */
- if (r == -EALREADY && set_by == REGDOM_SET_BY_DRIVER) {
+ * wiphy
+ */
+ if (r == -EALREADY &&
+ pending_request->initiator == REGDOM_SET_BY_DRIVER) {
r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain);
- if (r)
+ if (r) {
+ kfree(pending_request);
return r;
+ }
r = -EALREADY;
goto new_request;
}
+ kfree(pending_request);
return r;
}
new_request:
- request = kzalloc(sizeof(struct regulatory_request),
- GFP_KERNEL);
- if (!request)
- return -ENOMEM;
+ kfree(last_request);
- request->alpha2[0] = alpha2[0];
- request->alpha2[1] = alpha2[1];
- request->initiator = set_by;
- request->wiphy = wiphy;
- request->intersect = intersect;
- request->country_ie_checksum = country_ie_checksum;
- request->country_ie_env = env;
+ last_request = pending_request;
+ last_request->intersect = intersect;
- kfree(last_request);
- last_request = request;
+ pending_request = NULL;
/* When r == REG_INTERSECT we do need to call CRDA */
if (r < 0)
@@ -1180,34 +1399,194 @@ new_request:
*
* to intersect with the static rd
*/
- return call_crda(alpha2);
+ return call_crda(last_request->alpha2);
}
-void regulatory_hint(struct wiphy *wiphy, const char *alpha2)
+/* This currently only processes user and driver regulatory hints */
+static void reg_process_hint(struct regulatory_request *reg_request)
{
- int r;
- BUG_ON(!alpha2);
+ int r = 0;
+ struct wiphy *wiphy = NULL;
+
+ BUG_ON(!reg_request->alpha2);
+
+ mutex_lock(&cfg80211_mutex);
+
+ if (wiphy_idx_valid(reg_request->wiphy_idx))
+ wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx);
+
+ if (reg_request->initiator == REGDOM_SET_BY_DRIVER &&
+ !wiphy) {
+ kfree(reg_request);
+ goto out;
+ }
- mutex_lock(&cfg80211_drv_mutex);
- r = __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER,
- alpha2, 0, ENVIRON_ANY);
+ r = __regulatory_hint(wiphy, reg_request);
/* This is required so that the orig_* parameters are saved */
- if (r == -EALREADY && wiphy->strict_regulatory)
- wiphy_update_regulatory(wiphy, REGDOM_SET_BY_DRIVER);
- mutex_unlock(&cfg80211_drv_mutex);
+ if (r == -EALREADY && wiphy && wiphy->strict_regulatory)
+ wiphy_update_regulatory(wiphy, reg_request->initiator);
+out:
+ mutex_unlock(&cfg80211_mutex);
+}
+
+/* Processes regulatory hints, this is all the REGDOM_SET_BY_* */
+static void reg_process_pending_hints(void)
+ {
+ struct regulatory_request *reg_request;
+
+ spin_lock(&reg_requests_lock);
+ while (!list_empty(&reg_requests_list)) {
+ reg_request = list_first_entry(&reg_requests_list,
+ struct regulatory_request,
+ list);
+ list_del_init(&reg_request->list);
+
+ spin_unlock(&reg_requests_lock);
+ reg_process_hint(reg_request);
+ spin_lock(&reg_requests_lock);
+ }
+ spin_unlock(&reg_requests_lock);
+}
+
+/* Processes beacon hints -- this has nothing to do with country IEs */
+static void reg_process_pending_beacon_hints(void)
+{
+ struct cfg80211_registered_device *drv;
+ struct reg_beacon *pending_beacon, *tmp;
+
+ mutex_lock(&cfg80211_mutex);
+
+ /* This goes through the _pending_ beacon list */
+ spin_lock_bh(&reg_pending_beacons_lock);
+
+ if (list_empty(&reg_pending_beacons)) {
+ spin_unlock_bh(&reg_pending_beacons_lock);
+ goto out;
+ }
+
+ list_for_each_entry_safe(pending_beacon, tmp,
+ &reg_pending_beacons, list) {
+
+ list_del_init(&pending_beacon->list);
+
+ /* Applies the beacon hint to current wiphys */
+ list_for_each_entry(drv, &cfg80211_drv_list, list)
+ wiphy_update_new_beacon(&drv->wiphy, pending_beacon);
+
+ /* Remembers the beacon hint for new wiphys or reg changes */
+ list_add_tail(&pending_beacon->list, &reg_beacon_list);
+ }
+
+ spin_unlock_bh(&reg_pending_beacons_lock);
+out:
+ mutex_unlock(&cfg80211_mutex);
+}
+
+static void reg_todo(struct work_struct *work)
+{
+ reg_process_pending_hints();
+ reg_process_pending_beacon_hints();
+}
+
+static DECLARE_WORK(reg_work, reg_todo);
+
+static void queue_regulatory_request(struct regulatory_request *request)
+{
+ spin_lock(&reg_requests_lock);
+ list_add_tail(&request->list, &reg_requests_list);
+ spin_unlock(&reg_requests_lock);
+
+ schedule_work(&reg_work);
+}
+
+/* Core regulatory hint -- happens once during cfg80211_init() */
+static int regulatory_hint_core(const char *alpha2)
+{
+ struct regulatory_request *request;
+
+ BUG_ON(last_request);
+
+ request = kzalloc(sizeof(struct regulatory_request),
+ GFP_KERNEL);
+ if (!request)
+ return -ENOMEM;
+
+ request->alpha2[0] = alpha2[0];
+ request->alpha2[1] = alpha2[1];
+ request->initiator = REGDOM_SET_BY_CORE;
+
+ queue_regulatory_request(request);
+
+ return 0;
+}
+
+/* User hints */
+int regulatory_hint_user(const char *alpha2)
+{
+ struct regulatory_request *request;
+
+ BUG_ON(!alpha2);
+
+ request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
+ if (!request)
+ return -ENOMEM;
+
+ request->wiphy_idx = WIPHY_IDX_STALE;
+ request->alpha2[0] = alpha2[0];
+ request->alpha2[1] = alpha2[1];
+ request->initiator = REGDOM_SET_BY_USER,
+
+ queue_regulatory_request(request);
+
+ return 0;
+}
+
+/* Driver hints */
+int regulatory_hint(struct wiphy *wiphy, const char *alpha2)
+{
+ struct regulatory_request *request;
+
+ BUG_ON(!alpha2);
+ BUG_ON(!wiphy);
+
+ request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
+ if (!request)
+ return -ENOMEM;
+
+ request->wiphy_idx = get_wiphy_idx(wiphy);
+
+ /* Must have registered wiphy first */
+ BUG_ON(!wiphy_idx_valid(request->wiphy_idx));
+
+ request->alpha2[0] = alpha2[0];
+ request->alpha2[1] = alpha2[1];
+ request->initiator = REGDOM_SET_BY_DRIVER;
+
+ queue_regulatory_request(request);
+
+ return 0;
}
EXPORT_SYMBOL(regulatory_hint);
static bool reg_same_country_ie_hint(struct wiphy *wiphy,
u32 country_ie_checksum)
{
- if (!last_request->wiphy)
+ struct wiphy *request_wiphy;
+
+ assert_cfg80211_lock();
+
+ request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
+
+ if (!request_wiphy)
return false;
- if (likely(last_request->wiphy != wiphy))
+
+ if (likely(request_wiphy != wiphy))
return !country_ie_integrity_changes(country_ie_checksum);
- /* We should not have let these through at this point, they
+ /*
+ * We should not have let these through at this point, they
* should have been picked up earlier by the first alpha2 check
- * on the device */
+ * on the device
+ */
if (WARN_ON(!country_ie_integrity_changes(country_ie_checksum)))
return true;
return false;
@@ -1221,11 +1600,14 @@ void regulatory_hint_11d(struct wiphy *wiphy,
char alpha2[2];
u32 checksum = 0;
enum environment_cap env = ENVIRON_ANY;
+ struct regulatory_request *request;
- if (!last_request)
- return;
+ mutex_lock(&cfg80211_mutex);
- mutex_lock(&cfg80211_drv_mutex);
+ if (unlikely(!last_request)) {
+ mutex_unlock(&cfg80211_mutex);
+ return;
+ }
/* IE len must be evenly divisible by 2 */
if (country_ie_len & 0x01)
@@ -1234,9 +1616,11 @@ void regulatory_hint_11d(struct wiphy *wiphy,
if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN)
goto out;
- /* Pending country IE processing, this can happen after we
+ /*
+ * Pending country IE processing, this can happen after we
* call CRDA and wait for a response if a beacon was received before
- * we were able to process the last regulatory_hint_11d() call */
+ * we were able to process the last regulatory_hint_11d() call
+ */
if (country_ie_regdomain)
goto out;
@@ -1248,33 +1632,44 @@ void regulatory_hint_11d(struct wiphy *wiphy,
else if (country_ie[2] == 'O')
env = ENVIRON_OUTDOOR;
- /* We will run this for *every* beacon processed for the BSSID, so
+ /*
+ * We will run this for *every* beacon processed for the BSSID, so
* we optimize an early check to exit out early if we don't have to
- * do anything */
- if (likely(last_request->wiphy)) {
+ * do anything
+ */
+ if (likely(wiphy_idx_valid(last_request->wiphy_idx))) {
struct cfg80211_registered_device *drv_last_ie;
- drv_last_ie = wiphy_to_dev(last_request->wiphy);
+ drv_last_ie =
+ cfg80211_drv_by_wiphy_idx(last_request->wiphy_idx);
- /* Lets keep this simple -- we trust the first AP
- * after we intersect with CRDA */
- if (likely(last_request->wiphy == wiphy)) {
- /* Ignore IEs coming in on this wiphy with
- * the same alpha2 and environment cap */
+ /*
+ * Lets keep this simple -- we trust the first AP
+ * after we intersect with CRDA
+ */
+ if (likely(&drv_last_ie->wiphy == wiphy)) {
+ /*
+ * Ignore IEs coming in on this wiphy with
+ * the same alpha2 and environment cap
+ */
if (likely(alpha2_equal(drv_last_ie->country_ie_alpha2,
alpha2) &&
env == drv_last_ie->env)) {
goto out;
}
- /* the wiphy moved on to another BSSID or the AP
+ /*
+ * the wiphy moved on to another BSSID or the AP
* was reconfigured. XXX: We need to deal with the
* case where the user suspends and goes to goes
* to another country, and then gets IEs from an
- * AP with different settings */
+ * AP with different settings
+ */
goto out;
} else {
- /* Ignore IEs coming in on two separate wiphys with
- * the same alpha2 and environment cap */
+ /*
+ * Ignore IEs coming in on two separate wiphys with
+ * the same alpha2 and environment cap
+ */
if (likely(alpha2_equal(drv_last_ie->country_ie_alpha2,
alpha2) &&
env == drv_last_ie->env)) {
@@ -1289,28 +1684,97 @@ void regulatory_hint_11d(struct wiphy *wiphy,
if (!rd)
goto out;
- /* This will not happen right now but we leave it here for the
+ /*
+ * This will not happen right now but we leave it here for the
* the future when we want to add suspend/resume support and having
* the user move to another country after doing so, or having the user
- * move to another AP. Right now we just trust the first AP. This is why
- * this is marked as likley(). If we hit this before we add this support
- * we want to be informed of it as it would indicate a mistake in the
- * current design */
- if (likely(WARN_ON(reg_same_country_ie_hint(wiphy, checksum))))
- goto out;
+ * move to another AP. Right now we just trust the first AP.
+ *
+ * If we hit this before we add this support we want to be informed of
+ * it as it would indicate a mistake in the current design
+ */
+ if (WARN_ON(reg_same_country_ie_hint(wiphy, checksum)))
+ goto free_rd_out;
+
+ request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
+ if (!request)
+ goto free_rd_out;
- /* We keep this around for when CRDA comes back with a response so
- * we can intersect with that */
+ /*
+ * We keep this around for when CRDA comes back with a response so
+ * we can intersect with that
+ */
country_ie_regdomain = rd;
- __regulatory_hint(wiphy, REGDOM_SET_BY_COUNTRY_IE,
- country_ie_regdomain->alpha2, checksum, env);
+ request->wiphy_idx = get_wiphy_idx(wiphy);
+ request->alpha2[0] = rd->alpha2[0];
+ request->alpha2[1] = rd->alpha2[1];
+ request->initiator = REGDOM_SET_BY_COUNTRY_IE;
+ request->country_ie_checksum = checksum;
+ request->country_ie_env = env;
+
+ mutex_unlock(&cfg80211_mutex);
+
+ queue_regulatory_request(request);
+ return;
+
+free_rd_out:
+ kfree(rd);
out:
- mutex_unlock(&cfg80211_drv_mutex);
+ mutex_unlock(&cfg80211_mutex);
}
EXPORT_SYMBOL(regulatory_hint_11d);
+static bool freq_is_chan_12_13_14(u16 freq)
+{
+ if (freq == ieee80211_channel_to_frequency(12) ||
+ freq == ieee80211_channel_to_frequency(13) ||
+ freq == ieee80211_channel_to_frequency(14))
+ return true;
+ return false;
+}
+
+int regulatory_hint_found_beacon(struct wiphy *wiphy,
+ struct ieee80211_channel *beacon_chan,
+ gfp_t gfp)
+{
+ struct reg_beacon *reg_beacon;
+
+ if (likely((beacon_chan->beacon_found ||
+ (beacon_chan->flags & IEEE80211_CHAN_RADAR) ||
+ (beacon_chan->band == IEEE80211_BAND_2GHZ &&
+ !freq_is_chan_12_13_14(beacon_chan->center_freq)))))
+ return 0;
+
+ reg_beacon = kzalloc(sizeof(struct reg_beacon), gfp);
+ if (!reg_beacon)
+ return -ENOMEM;
+
+#ifdef CONFIG_CFG80211_REG_DEBUG
+ printk(KERN_DEBUG "cfg80211: Found new beacon on "
+ "frequency: %d MHz (Ch %d) on %s\n",
+ beacon_chan->center_freq,
+ ieee80211_frequency_to_channel(beacon_chan->center_freq),
+ wiphy_name(wiphy));
+#endif
+ memcpy(&reg_beacon->chan, beacon_chan,
+ sizeof(struct ieee80211_channel));
+
+
+ /*
+ * Since we can be called from BH or and non-BH context
+ * we must use spin_lock_bh()
+ */
+ spin_lock_bh(&reg_pending_beacons_lock);
+ list_add_tail(&reg_beacon->list, &reg_pending_beacons);
+ spin_unlock_bh(&reg_pending_beacons_lock);
+
+ schedule_work(&reg_work);
+
+ return 0;
+}
+
static void print_rd_rules(const struct ieee80211_regdomain *rd)
{
unsigned int i;
@@ -1326,8 +1790,10 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd)
freq_range = &reg_rule->freq_range;
power_rule = &reg_rule->power_rule;
- /* There may not be documentation for max antenna gain
- * in certain regions */
+ /*
+ * There may not be documentation for max antenna gain
+ * in certain regions
+ */
if (power_rule->max_antenna_gain)
printk(KERN_INFO "\t(%d KHz - %d KHz @ %d KHz), "
"(%d mBi, %d mBm)\n",
@@ -1350,13 +1816,12 @@ static void print_regdomain(const struct ieee80211_regdomain *rd)
{
if (is_intersected_alpha2(rd->alpha2)) {
- struct wiphy *wiphy = NULL;
- struct cfg80211_registered_device *drv;
if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) {
- if (last_request->wiphy) {
- wiphy = last_request->wiphy;
- drv = wiphy_to_dev(wiphy);
+ struct cfg80211_registered_device *drv;
+ drv = cfg80211_drv_by_wiphy_idx(
+ last_request->wiphy_idx);
+ if (drv) {
printk(KERN_INFO "cfg80211: Current regulatory "
"domain updated by AP to: %c%c\n",
drv->country_ie_alpha2[0],
@@ -1422,7 +1887,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
{
const struct ieee80211_regdomain *intersected_rd = NULL;
struct cfg80211_registered_device *drv = NULL;
- struct wiphy *wiphy = NULL;
+ struct wiphy *request_wiphy;
/* Some basic sanity checks first */
if (is_world_regdom(rd->alpha2)) {
@@ -1439,23 +1904,27 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
if (!last_request)
return -EINVAL;
- /* Lets only bother proceeding on the same alpha2 if the current
+ /*
+ * Lets only bother proceeding on the same alpha2 if the current
* rd is non static (it means CRDA was present and was used last)
- * and the pending request came in from a country IE */
+ * and the pending request came in from a country IE
+ */
if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE) {
- /* If someone else asked us to change the rd lets only bother
- * checking if the alpha2 changes if CRDA was already called */
+ /*
+ * If someone else asked us to change the rd lets only bother
+ * checking if the alpha2 changes if CRDA was already called
+ */
if (!is_old_static_regdom(cfg80211_regdomain) &&
- !regdom_changed(rd->alpha2))
+ !regdom_changes(rd->alpha2))
return -EINVAL;
}
- wiphy = last_request->wiphy;
-
- /* Now lets set the regulatory domain, update all driver channels
+ /*
+ * Now lets set the regulatory domain, update all driver channels
* and finally inform them of what we have done, in case they want
* to review or adjust their own settings based on their own
- * internal EEPROM data */
+ * internal EEPROM data
+ */
if (WARN_ON(!reg_is_valid_request(rd->alpha2)))
return -EINVAL;
@@ -1467,6 +1936,8 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
return -EINVAL;
}
+ request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
+
if (!last_request->intersect) {
int r;
@@ -1476,12 +1947,14 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
return 0;
}
- /* For a driver hint, lets copy the regulatory domain the
- * driver wanted to the wiphy to deal with conflicts */
+ /*
+ * For a driver hint, lets copy the regulatory domain the
+ * driver wanted to the wiphy to deal with conflicts
+ */
- BUG_ON(last_request->wiphy->regd);
+ BUG_ON(request_wiphy->regd);
- r = reg_copy_regd(&last_request->wiphy->regd, rd);
+ r = reg_copy_regd(&request_wiphy->regd, rd);
if (r)
return r;
@@ -1498,11 +1971,13 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
if (!intersected_rd)
return -EINVAL;
- /* We can trash what CRDA provided now.
+ /*
+ * We can trash what CRDA provided now.
* However if a driver requested this specific regulatory
- * domain we keep it for its private use */
+ * domain we keep it for its private use
+ */
if (last_request->initiator == REGDOM_SET_BY_DRIVER)
- last_request->wiphy->regd = rd;
+ request_wiphy->regd = rd;
else
kfree(rd);
@@ -1522,8 +1997,10 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
BUG_ON(!country_ie_regdomain);
if (rd != country_ie_regdomain) {
- /* Intersect what CRDA returned and our what we
- * had built from the Country IE received */
+ /*
+ * Intersect what CRDA returned and our what we
+ * had built from the Country IE received
+ */
intersected_rd = regdom_intersect(rd, country_ie_regdomain);
@@ -1533,16 +2010,18 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
kfree(country_ie_regdomain);
country_ie_regdomain = NULL;
} else {
- /* This would happen when CRDA was not present and
+ /*
+ * This would happen when CRDA was not present and
* OLD_REGULATORY was enabled. We intersect our Country
- * IE rd and what was set on cfg80211 originally */
+ * IE rd and what was set on cfg80211 originally
+ */
intersected_rd = regdom_intersect(rd, cfg80211_regdomain);
}
if (!intersected_rd)
return -EINVAL;
- drv = wiphy_to_dev(wiphy);
+ drv = wiphy_to_dev(request_wiphy);
drv->country_ie_alpha2[0] = rd->alpha2[0];
drv->country_ie_alpha2[1] = rd->alpha2[1];
@@ -1560,13 +2039,17 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
}
-/* Use this call to set the current regulatory domain. Conflicts with
+/*
+ * Use this call to set the current regulatory domain. Conflicts with
* multiple drivers can be ironed out later. Caller must've already
- * kmalloc'd the rd structure. Caller must hold cfg80211_drv_mutex */
+ * kmalloc'd the rd structure. Caller must hold cfg80211_mutex
+ */
int set_regdom(const struct ieee80211_regdomain *rd)
{
int r;
+ assert_cfg80211_lock();
+
/* Note that this doesn't update the wiphys, this is done below */
r = __set_regdom(rd);
if (r) {
@@ -1586,54 +2069,82 @@ int set_regdom(const struct ieee80211_regdomain *rd)
return r;
}
-/* Caller must hold cfg80211_drv_mutex */
+/* Caller must hold cfg80211_mutex */
void reg_device_remove(struct wiphy *wiphy)
{
+ struct wiphy *request_wiphy;
+
+ assert_cfg80211_lock();
+
+ request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
+
kfree(wiphy->regd);
- if (!last_request || !last_request->wiphy)
+ if (!last_request || !request_wiphy)
return;
- if (last_request->wiphy != wiphy)
+ if (request_wiphy != wiphy)
return;
- last_request->wiphy = NULL;
+ last_request->wiphy_idx = WIPHY_IDX_STALE;
last_request->country_ie_env = ENVIRON_ANY;
}
int regulatory_init(void)
{
- int err;
+ int err = 0;
reg_pdev = platform_device_register_simple("regulatory", 0, NULL, 0);
if (IS_ERR(reg_pdev))
return PTR_ERR(reg_pdev);
+ spin_lock_init(&reg_requests_lock);
+ spin_lock_init(&reg_pending_beacons_lock);
+
#ifdef CONFIG_WIRELESS_OLD_REGULATORY
cfg80211_regdomain = static_regdom(ieee80211_regdom);
printk(KERN_INFO "cfg80211: Using static regulatory domain info\n");
print_regdomain_info(cfg80211_regdomain);
- /* The old code still requests for a new regdomain and if
+ /*
+ * The old code still requests for a new regdomain and if
* you have CRDA you get it updated, otherwise you get
* stuck with the static values. We ignore "EU" code as
- * that is not a valid ISO / IEC 3166 alpha2 */
+ * that is not a valid ISO / IEC 3166 alpha2
+ */
if (ieee80211_regdom[0] != 'E' || ieee80211_regdom[1] != 'U')
- err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE,
- ieee80211_regdom, 0, ENVIRON_ANY);
+ err = regulatory_hint_core(ieee80211_regdom);
#else
cfg80211_regdomain = cfg80211_world_regdom;
- err = __regulatory_hint(NULL, REGDOM_SET_BY_CORE, "00", 0, ENVIRON_ANY);
- if (err)
- printk(KERN_ERR "cfg80211: calling CRDA failed - "
- "unable to update world regulatory domain, "
- "using static definition\n");
+ err = regulatory_hint_core("00");
#endif
+ if (err) {
+ if (err == -ENOMEM)
+ return err;
+ /*
+ * N.B. kobject_uevent_env() can fail mainly for when we're out
+ * memory which is handled and propagated appropriately above
+ * but it can also fail during a netlink_broadcast() or during
+ * early boot for call_usermodehelper(). For now treat these
+ * errors as non-fatal.
+ */
+ printk(KERN_ERR "cfg80211: kobject_uevent_env() was unable "
+ "to call CRDA during init");
+#ifdef CONFIG_CFG80211_REG_DEBUG
+ /* We want to find out exactly why when debugging */
+ WARN_ON(err);
+#endif
+ }
return 0;
}
void regulatory_exit(void)
{
- mutex_lock(&cfg80211_drv_mutex);
+ struct regulatory_request *reg_request, *tmp;
+ struct reg_beacon *reg_beacon, *btmp;
+
+ cancel_work_sync(&reg_work);
+
+ mutex_lock(&cfg80211_mutex);
reset_regdomains();
@@ -1644,5 +2155,33 @@ void regulatory_exit(void)
platform_device_unregister(reg_pdev);
- mutex_unlock(&cfg80211_drv_mutex);
+ spin_lock_bh(&reg_pending_beacons_lock);
+ if (!list_empty(&reg_pending_beacons)) {
+ list_for_each_entry_safe(reg_beacon, btmp,
+ &reg_pending_beacons, list) {
+ list_del(&reg_beacon->list);
+ kfree(reg_beacon);
+ }
+ }
+ spin_unlock_bh(&reg_pending_beacons_lock);
+
+ if (!list_empty(&reg_beacon_list)) {
+ list_for_each_entry_safe(reg_beacon, btmp,
+ &reg_beacon_list, list) {
+ list_del(&reg_beacon->list);
+ kfree(reg_beacon);
+ }
+ }
+
+ spin_lock(&reg_requests_lock);
+ if (!list_empty(&reg_requests_list)) {
+ list_for_each_entry_safe(reg_request, tmp,
+ &reg_requests_list, list) {
+ list_del(&reg_request->list);
+ kfree(reg_request);
+ }
+ }
+ spin_unlock(&reg_requests_lock);
+
+ mutex_unlock(&cfg80211_mutex);
}
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index fe8c83f34fb..e37829a49dc 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -6,6 +6,8 @@ extern const struct ieee80211_regdomain *cfg80211_regdomain;
bool is_world_regdom(const char *alpha2);
bool reg_is_valid_request(const char *alpha2);
+int regulatory_hint_user(const char *alpha2);
+
void reg_device_remove(struct wiphy *wiphy);
int regulatory_init(void);
@@ -14,26 +16,24 @@ void regulatory_exit(void);
int set_regdom(const struct ieee80211_regdomain *rd);
/**
- * __regulatory_hint - hint to the wireless core a regulatory domain
- * @wiphy: if the hint comes from country information from an AP, this
- * is required to be set to the wiphy that received the information
- * @alpha2: the ISO/IEC 3166 alpha2 being claimed the regulatory domain
- * should be in.
- * @country_ie_checksum: checksum of processed country IE, set this to 0
- * if the hint did not come from a country IE
- * @country_ie_env: the environment the IE told us we are in, %ENVIRON_*
- *
- * The Wireless subsystem can use this function to hint to the wireless core
- * what it believes should be the current regulatory domain by giving it an
- * ISO/IEC 3166 alpha2 country code it knows its regulatory domain should be
- * in.
+ * regulatory_hint_found_beacon - hints a beacon was found on a channel
+ * @wiphy: the wireless device where the beacon was found on
+ * @beacon_chan: the channel on which the beacon was found on
+ * @gfp: context flags
*
- * Returns zero if all went fine, %-EALREADY if a regulatory domain had
- * already been set or other standard error codes.
+ * This informs the wireless core that a beacon from an AP was found on
+ * the channel provided. This allows the wireless core to make educated
+ * guesses on regulatory to help with world roaming. This is only used for
+ * world roaming -- when we do not know our current location. This is
+ * only useful on channels 12, 13 and 14 on the 2 GHz band as channels
+ * 1-11 are already enabled by the world regulatory domain; and on
+ * non-radar 5 GHz channels.
*
+ * Drivers do not need to call this, cfg80211 will do it for after a scan
+ * on a newly found BSS.
*/
-extern int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by,
- const char *alpha2, u32 country_ie_checksum,
- enum environment_cap country_ie_env);
+int regulatory_hint_found_beacon(struct wiphy *wiphy,
+ struct ieee80211_channel *beacon_chan,
+ gfp_t gfp);
#endif /* __NET_WIRELESS_REG_H */
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index b1893c863b9..280dbcd02c1 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -62,6 +62,18 @@ static void bss_release(struct kref *ref)
}
/* must hold dev->bss_lock! */
+void cfg80211_bss_age(struct cfg80211_registered_device *dev,
+ unsigned long age_secs)
+{
+ struct cfg80211_internal_bss *bss;
+ unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC);
+
+ list_for_each_entry(bss, &dev->bss_list, list) {
+ bss->ts -= age_jiffies;
+ }
+}
+
+/* must hold dev->bss_lock! */
void cfg80211_bss_expire(struct cfg80211_registered_device *dev)
{
struct cfg80211_internal_bss *bss, *tmp;
@@ -358,7 +370,6 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
found->pub.beacon_interval = res->pub.beacon_interval;
found->pub.tsf = res->pub.tsf;
found->pub.signal = res->pub.signal;
- found->pub.signal_type = res->pub.signal_type;
found->pub.capability = res->pub.capability;
found->ts = res->ts;
kref_put(&res->ref, bss_release);
@@ -380,8 +391,7 @@ struct cfg80211_bss *
cfg80211_inform_bss_frame(struct wiphy *wiphy,
struct ieee80211_channel *channel,
struct ieee80211_mgmt *mgmt, size_t len,
- s32 signal, enum cfg80211_signal_type sigtype,
- gfp_t gfp)
+ s32 signal, gfp_t gfp)
{
struct cfg80211_internal_bss *res;
size_t ielen = len - offsetof(struct ieee80211_mgmt,
@@ -389,7 +399,7 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
bool overwrite;
size_t privsz = wiphy->bss_priv_size;
- if (WARN_ON(sigtype == NL80211_BSS_SIGNAL_UNSPEC &&
+ if (WARN_ON(wiphy->signal_type == NL80211_BSS_SIGNAL_UNSPEC &&
(signal < 0 || signal > 100)))
return NULL;
@@ -403,7 +413,6 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
memcpy(res->pub.bssid, mgmt->bssid, ETH_ALEN);
res->pub.channel = channel;
- res->pub.signal_type = sigtype;
res->pub.signal = signal;
res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
@@ -421,6 +430,9 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
if (!res)
return NULL;
+ if (res->pub.capability & WLAN_CAPABILITY_ESS)
+ regulatory_hint_found_beacon(wiphy, channel, gfp);
+
/* cfg80211_bss_update gives us a referenced result */
return &res->pub;
}
@@ -584,16 +596,25 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info,
}
}
+static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
+{
+ unsigned long end = jiffies;
+
+ if (end >= start)
+ return jiffies_to_msecs(end - start);
+
+ return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
+}
static char *
-ieee80211_bss(struct iw_request_info *info,
- struct cfg80211_internal_bss *bss,
- char *current_ev, char *end_buf)
+ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
+ struct cfg80211_internal_bss *bss, char *current_ev,
+ char *end_buf)
{
struct iw_event iwe;
u8 *buf, *cfg, *p;
u8 *ie = bss->pub.information_elements;
- int rem = bss->pub.len_information_elements, i;
+ int rem = bss->pub.len_information_elements, i, sig;
bool ismesh = false;
memset(&iwe, 0, sizeof(iwe));
@@ -617,19 +638,28 @@ ieee80211_bss(struct iw_request_info *info,
current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
IW_EV_FREQ_LEN);
- if (bss->pub.signal_type != CFG80211_SIGNAL_TYPE_NONE) {
+ if (wiphy->signal_type != CFG80211_SIGNAL_TYPE_NONE) {
memset(&iwe, 0, sizeof(iwe));
iwe.cmd = IWEVQUAL;
iwe.u.qual.updated = IW_QUAL_LEVEL_UPDATED |
IW_QUAL_NOISE_INVALID |
- IW_QUAL_QUAL_INVALID;
- switch (bss->pub.signal_type) {
+ IW_QUAL_QUAL_UPDATED;
+ switch (wiphy->signal_type) {
case CFG80211_SIGNAL_TYPE_MBM:
- iwe.u.qual.level = bss->pub.signal / 100;
+ sig = bss->pub.signal / 100;
+ iwe.u.qual.level = sig;
iwe.u.qual.updated |= IW_QUAL_DBM;
+ if (sig < -110) /* rather bad */
+ sig = -110;
+ else if (sig > -40) /* perfect */
+ sig = -40;
+ /* will give a range of 0 .. 70 */
+ iwe.u.qual.qual = sig + 110;
break;
case CFG80211_SIGNAL_TYPE_UNSPEC:
iwe.u.qual.level = bss->pub.signal;
+ /* will give range 0 .. 100 */
+ iwe.u.qual.qual = bss->pub.signal;
break;
default:
/* not reached */
@@ -763,8 +793,8 @@ ieee80211_bss(struct iw_request_info *info,
&iwe, buf);
memset(&iwe, 0, sizeof(iwe));
iwe.cmd = IWEVCUSTOM;
- sprintf(buf, " Last beacon: %dms ago",
- jiffies_to_msecs(jiffies - bss->ts));
+ sprintf(buf, " Last beacon: %ums ago",
+ elapsed_jiffies_msecs(bss->ts));
iwe.u.data.length = strlen(buf);
current_ev = iwe_stream_add_point(info, current_ev,
end_buf, &iwe, buf);
@@ -793,8 +823,8 @@ static int ieee80211_scan_results(struct cfg80211_registered_device *dev,
spin_unlock_bh(&dev->bss_lock);
return -E2BIG;
}
- current_ev = ieee80211_bss(info, bss,
- current_ev, end_buf);
+ current_ev = ieee80211_bss(&dev->wiphy, info, bss,
+ current_ev, end_buf);
}
spin_unlock_bh(&dev->bss_lock);
return current_ev - buf;
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c
index 26a72b0797a..efe3c5c92b2 100644
--- a/net/wireless/sysfs.c
+++ b/net/wireless/sysfs.c
@@ -31,7 +31,7 @@ static ssize_t name ## _show(struct device *dev, \
return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member); \
}
-SHOW_FMT(index, "%d", idx);
+SHOW_FMT(index, "%d", wiphy_idx);
SHOW_FMT(macaddress, "%pM", wiphy.perm_addr);
static struct device_attribute ieee80211_dev_attrs[] = {
@@ -60,6 +60,8 @@ static int wiphy_suspend(struct device *dev, pm_message_t state)
struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
int ret = 0;
+ rdev->suspend_at = get_seconds();
+
if (rdev->ops->suspend) {
rtnl_lock();
ret = rdev->ops->suspend(&rdev->wiphy);
@@ -74,6 +76,11 @@ static int wiphy_resume(struct device *dev)
struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
int ret = 0;
+ /* Age scan results with time spent in suspend */
+ spin_lock_bh(&rdev->bss_lock);
+ cfg80211_bss_age(rdev, get_seconds() - rdev->suspend_at);
+ spin_unlock_bh(&rdev->bss_lock);
+
if (rdev->ops->resume) {
rtnl_lock();
ret = rdev->ops->resume(&rdev->wiphy);
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index 58e489fd4ae..b84a9b4fe96 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -137,3 +137,100 @@ int cfg80211_wext_giwmode(struct net_device *dev, struct iw_request_info *info,
return 0;
}
EXPORT_SYMBOL(cfg80211_wext_giwmode);
+
+
+int cfg80211_wext_giwrange(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct iw_range *range = (struct iw_range *) extra;
+ enum ieee80211_band band;
+ int c = 0;
+
+ if (!wdev)
+ return -EOPNOTSUPP;
+
+ data->length = sizeof(struct iw_range);
+ memset(range, 0, sizeof(struct iw_range));
+
+ range->we_version_compiled = WIRELESS_EXT;
+ range->we_version_source = 21;
+ range->retry_capa = IW_RETRY_LIMIT;
+ range->retry_flags = IW_RETRY_LIMIT;
+ range->min_retry = 0;
+ range->max_retry = 255;
+ range->min_rts = 0;
+ range->max_rts = 2347;
+ range->min_frag = 256;
+ range->max_frag = 2346;
+
+ range->encoding_size[0] = 5;
+ range->encoding_size[1] = 13;
+ range->num_encoding_sizes = 2;
+ range->max_encoding_tokens = 4;
+
+ range->max_qual.updated = IW_QUAL_NOISE_INVALID;
+
+ switch (wdev->wiphy->signal_type) {
+ case CFG80211_SIGNAL_TYPE_NONE:
+ break;
+ case CFG80211_SIGNAL_TYPE_MBM:
+ range->max_qual.level = -110;
+ range->max_qual.qual = 70;
+ range->avg_qual.qual = 35;
+ range->max_qual.updated |= IW_QUAL_DBM;
+ range->max_qual.updated |= IW_QUAL_QUAL_UPDATED;
+ range->max_qual.updated |= IW_QUAL_LEVEL_UPDATED;
+ break;
+ case CFG80211_SIGNAL_TYPE_UNSPEC:
+ range->max_qual.level = 100;
+ range->max_qual.qual = 100;
+ range->avg_qual.qual = 50;
+ range->max_qual.updated |= IW_QUAL_QUAL_UPDATED;
+ range->max_qual.updated |= IW_QUAL_LEVEL_UPDATED;
+ break;
+ }
+
+ range->avg_qual.level = range->max_qual.level / 2;
+ range->avg_qual.noise = range->max_qual.noise / 2;
+ range->avg_qual.updated = range->max_qual.updated;
+
+ range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 |
+ IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP;
+
+
+ for (band = 0; band < IEEE80211_NUM_BANDS; band ++) {
+ int i;
+ struct ieee80211_supported_band *sband;
+
+ sband = wdev->wiphy->bands[band];
+
+ if (!sband)
+ continue;
+
+ for (i = 0; i < sband->n_channels && c < IW_MAX_FREQUENCIES; i++) {
+ struct ieee80211_channel *chan = &sband->channels[i];
+
+ if (!(chan->flags & IEEE80211_CHAN_DISABLED)) {
+ range->freq[c].i =
+ ieee80211_frequency_to_channel(
+ chan->center_freq);
+ range->freq[c].m = chan->center_freq;
+ range->freq[c].e = 6;
+ c++;
+ }
+ }
+ }
+ range->num_channels = c;
+ range->num_frequency = c;
+
+ IW_EVENT_CAPA_SET_KERNEL(range->event_capa);
+ IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP);
+ IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);
+
+ range->scan_capa |= IW_SCAN_CAPA_ESSID;
+
+ return 0;
+}
+EXPORT_SYMBOL(cfg80211_wext_giwrange);