Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
Conflicts: net/mac80211/pm.c
This commit is contained in:
@@ -11,6 +11,22 @@ config MAC80211
|
||||
This option enables the hardware independent IEEE 802.11
|
||||
networking stack.
|
||||
|
||||
config MAC80211_DEFAULT_PS
|
||||
bool "enable powersave by default"
|
||||
depends on MAC80211
|
||||
default y
|
||||
help
|
||||
This option enables powersave mode by default.
|
||||
|
||||
If this causes your applications to misbehave you should fix your
|
||||
applications instead -- they need to register their network
|
||||
latency requirement, see Documentation/power/pm_qos_interface.txt.
|
||||
|
||||
config MAC80211_DEFAULT_PS_VALUE
|
||||
int
|
||||
default 1 if MAC80211_DEFAULT_PS
|
||||
default 0
|
||||
|
||||
menu "Rate control algorithm selection"
|
||||
depends on MAC80211 != n
|
||||
|
||||
|
@@ -1167,7 +1167,8 @@ static int ieee80211_scan(struct wiphy *wiphy,
|
||||
|
||||
if (sdata->vif.type != NL80211_IFTYPE_STATION &&
|
||||
sdata->vif.type != NL80211_IFTYPE_ADHOC &&
|
||||
sdata->vif.type != NL80211_IFTYPE_MESH_POINT)
|
||||
sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
|
||||
(sdata->vif.type != NL80211_IFTYPE_AP || sdata->u.ap.beacon))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return ieee80211_request_scan(sdata, req);
|
||||
@@ -1267,25 +1268,62 @@ static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev,
|
||||
static int ieee80211_deauth(struct wiphy *wiphy, struct net_device *dev,
|
||||
struct cfg80211_deauth_request *req)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
|
||||
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
|
||||
/* TODO: req->ie */
|
||||
/* TODO: req->ie, req->peer_addr */
|
||||
return ieee80211_sta_deauthenticate(sdata, req->reason_code);
|
||||
}
|
||||
|
||||
static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev,
|
||||
struct cfg80211_disassoc_request *req)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
|
||||
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
|
||||
/* TODO: req->ie */
|
||||
/* TODO: req->ie, req->peer_addr */
|
||||
return ieee80211_sta_disassociate(sdata, req->reason_code);
|
||||
}
|
||||
|
||||
static int ieee80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
|
||||
struct cfg80211_ibss_params *params)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
|
||||
return ieee80211_ibss_join(sdata, params);
|
||||
}
|
||||
|
||||
static int ieee80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
|
||||
return ieee80211_ibss_leave(sdata);
|
||||
}
|
||||
|
||||
static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
|
||||
{
|
||||
struct ieee80211_local *local = wiphy_priv(wiphy);
|
||||
|
||||
if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
|
||||
int err;
|
||||
|
||||
if (local->ops->set_rts_threshold) {
|
||||
err = local->ops->set_rts_threshold(
|
||||
local_to_hw(local), wiphy->rts_threshold);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed & WIPHY_PARAM_RETRY_SHORT)
|
||||
local->hw.conf.short_frame_max_tx_count = wiphy->retry_short;
|
||||
if (changed & WIPHY_PARAM_RETRY_LONG)
|
||||
local->hw.conf.long_frame_max_tx_count = wiphy->retry_long;
|
||||
if (changed &
|
||||
(WIPHY_PARAM_RETRY_SHORT | WIPHY_PARAM_RETRY_LONG))
|
||||
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_RETRY_LIMITS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct cfg80211_ops mac80211_config_ops = {
|
||||
.add_virtual_intf = ieee80211_add_iface,
|
||||
.del_virtual_intf = ieee80211_del_iface,
|
||||
@@ -1322,4 +1360,7 @@ struct cfg80211_ops mac80211_config_ops = {
|
||||
.assoc = ieee80211_assoc,
|
||||
.deauth = ieee80211_deauth,
|
||||
.disassoc = ieee80211_disassoc,
|
||||
.join_ibss = ieee80211_join_ibss,
|
||||
.leave_ibss = ieee80211_leave_ibss,
|
||||
.set_wiphy_params = ieee80211_set_wiphy_params,
|
||||
};
|
||||
|
@@ -52,13 +52,13 @@ static const struct file_operations name## _ops = { \
|
||||
DEBUGFS_READONLY_FILE(frequency, 20, "%d",
|
||||
local->hw.conf.channel->center_freq);
|
||||
DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d",
|
||||
local->rts_threshold);
|
||||
local->hw.wiphy->rts_threshold);
|
||||
DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d",
|
||||
local->fragmentation_threshold);
|
||||
local->hw.wiphy->frag_threshold);
|
||||
DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d",
|
||||
local->hw.conf.short_frame_max_tx_count);
|
||||
local->hw.wiphy->retry_short);
|
||||
DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d",
|
||||
local->hw.conf.long_frame_max_tx_count);
|
||||
local->hw.wiphy->retry_long);
|
||||
DEBUGFS_READONLY_FILE(total_ps_buffered, 20, "%d",
|
||||
local->total_ps_buffered);
|
||||
DEBUGFS_READONLY_FILE(wep_iv, 20, "%#08x",
|
||||
|
@@ -12,12 +12,12 @@
|
||||
#include "ieee80211_i.h"
|
||||
|
||||
/*
|
||||
* indicate a failed Michael MIC to userspace; the passed packet
|
||||
* (in the variable hdr) must be long enough to extract the TKIP
|
||||
* fields like TSC
|
||||
* Indicate a failed Michael MIC to userspace. If the caller knows the TSC of
|
||||
* the frame that generated the MIC failure (i.e., if it was provided by the
|
||||
* driver or is still in the frame), it should provide that information.
|
||||
*/
|
||||
void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx,
|
||||
struct ieee80211_hdr *hdr)
|
||||
struct ieee80211_hdr *hdr, const u8 *tsc)
|
||||
{
|
||||
union iwreq_data wrqu;
|
||||
char *buf = kmalloc(128, GFP_ATOMIC);
|
||||
@@ -34,8 +34,9 @@ void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int ke
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: re-add support for sending MIC failure indication
|
||||
* with all info via nl80211
|
||||
*/
|
||||
cfg80211_michael_mic_failure(sdata->dev, hdr->addr2,
|
||||
(hdr->addr1[0] & 0x01) ?
|
||||
NL80211_KEYTYPE_GROUP :
|
||||
NL80211_KEYTYPE_PAIRWISE,
|
||||
keyidx, tsc);
|
||||
}
|
||||
|
@@ -14,7 +14,6 @@
|
||||
*/
|
||||
|
||||
#include <linux/ieee80211.h>
|
||||
#include <net/wireless.h>
|
||||
#include <net/mac80211.h>
|
||||
#include "ieee80211_i.h"
|
||||
#include "rate.h"
|
||||
@@ -83,89 +82,6 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband,
|
||||
ht_cap->mcs.rx_mask[32/8] |= 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* ieee80211_enable_ht should be called only after the operating band
|
||||
* has been determined as ht configuration depends on the hw's
|
||||
* HT abilities for a specific band.
|
||||
*/
|
||||
u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_ht_info *hti,
|
||||
u16 ap_ht_cap_flags)
|
||||
{
|
||||
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;
|
||||
|
||||
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
|
||||
|
||||
memset(&ht, 0, sizeof(ht));
|
||||
|
||||
/* HT is not supported */
|
||||
if (!sband->ht_cap.ht_supported)
|
||||
enable_ht = false;
|
||||
|
||||
/* check that channel matches the right operating channel */
|
||||
if (local->hw.conf.channel->center_freq !=
|
||||
ieee80211_channel_to_frequency(hti->control_chan))
|
||||
enable_ht = false;
|
||||
|
||||
if (enable_ht) {
|
||||
channel_type = NL80211_CHAN_HT20;
|
||||
|
||||
if (!(ap_ht_cap_flags & IEEE80211_HT_CAP_40MHZ_INTOLERANT) &&
|
||||
(sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
|
||||
(hti->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) {
|
||||
switch(hti->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
|
||||
channel_type = NL80211_CHAN_HT40PLUS;
|
||||
break;
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
|
||||
channel_type = NL80211_CHAN_HT40MINUS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ht_changed = conf_is_ht(&local->hw.conf) != enable_ht ||
|
||||
channel_type != local->hw.conf.channel_type;
|
||||
|
||||
local->oper_channel_type = channel_type;
|
||||
|
||||
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 */
|
||||
if (!enable_ht)
|
||||
return 0;
|
||||
|
||||
ht.operation_mode = le16_to_cpu(hti->operation_mode);
|
||||
|
||||
/* if bss configuration changed store the new one */
|
||||
if (memcmp(&sdata->vif.bss_conf.ht, &ht, sizeof(ht))) {
|
||||
changed |= BSS_CHANGED_HT;
|
||||
sdata->vif.bss_conf.ht = ht;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta)
|
||||
{
|
||||
int i;
|
||||
|
@@ -59,74 +59,59 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
|
||||
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, u64 tsf)
|
||||
static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
|
||||
const u8 *bssid, const int beacon_int,
|
||||
struct ieee80211_channel *chan,
|
||||
const size_t supp_rates_len,
|
||||
const u8 *supp_rates,
|
||||
const u16 capability, u64 tsf)
|
||||
{
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
int res = 0, rates, i, j;
|
||||
int 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 = ifibss->skb;
|
||||
rcu_assign_pointer(ifibss->presp, NULL);
|
||||
synchronize_rcu();
|
||||
skb->data = skb->head;
|
||||
skb->len = 0;
|
||||
skb_reset_tail_pointer(skb);
|
||||
skb_reserve(skb, sdata->local->hw.extra_tx_headroom);
|
||||
|
||||
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);
|
||||
}
|
||||
if (memcmp(ifibss->bssid, bssid, ETH_ALEN))
|
||||
sta_info_flush(sdata->local, 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;
|
||||
sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
|
||||
|
||||
res = ieee80211_set_freq(sdata, freq);
|
||||
ieee80211_if_config(sdata, IEEE80211_IFCC_BSSID);
|
||||
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
|
||||
local->oper_channel = chan;
|
||||
local->oper_channel_type = NL80211_CHAN_NO_HT;
|
||||
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
|
||||
sband = local->hw.wiphy->bands[chan->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));
|
||||
mgmt = (void *) 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.beacon_int = cpu_to_le16(local->hw.conf.beacon_int);
|
||||
mgmt->u.beacon.timestamp = cpu_to_le64(tsf);
|
||||
mgmt->u.beacon.capab_info = cpu_to_le16(capability);
|
||||
|
||||
@@ -147,7 +132,7 @@ static int __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
|
||||
pos = skb_put(skb, 2 + 1);
|
||||
*pos++ = WLAN_EID_DS_PARAMS;
|
||||
*pos++ = 1;
|
||||
*pos++ = ieee80211_frequency_to_channel(freq);
|
||||
*pos++ = ieee80211_frequency_to_channel(chan->center_freq);
|
||||
}
|
||||
|
||||
pos = skb_put(skb, 2 + 2);
|
||||
@@ -165,12 +150,15 @@ static int __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
|
||||
memcpy(pos, &supp_rates[8], rates);
|
||||
}
|
||||
|
||||
ifibss->probe_resp = skb;
|
||||
if (ifibss->ie_len)
|
||||
memcpy(skb_put(skb, ifibss->ie_len),
|
||||
ifibss->ie, ifibss->ie_len);
|
||||
|
||||
rcu_assign_pointer(ifibss->presp, 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;
|
||||
@@ -181,27 +169,24 @@ static int __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
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);
|
||||
mod_timer(&ifibss->timer,
|
||||
round_jiffies(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;
|
||||
cfg80211_inform_bss_frame(local->hw.wiphy, local->hw.conf.channel,
|
||||
mgmt, skb->len, 0, GFP_KERNEL);
|
||||
cfg80211_ibss_joined(sdata->dev, ifibss->bssid, GFP_KERNEL);
|
||||
}
|
||||
|
||||
static int ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_bss *bss)
|
||||
static void 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,
|
||||
bss->cbss.tsf);
|
||||
__ieee80211_sta_join_ibss(sdata, bss->cbss.bssid,
|
||||
bss->cbss.beacon_interval,
|
||||
bss->cbss.channel,
|
||||
bss->supp_rates_len, bss->supp_rates,
|
||||
bss->cbss.capability,
|
||||
bss->cbss.tsf);
|
||||
}
|
||||
|
||||
static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
|
||||
@@ -277,7 +262,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
|
||||
goto put_bss;
|
||||
|
||||
/* we use a fixed BSSID */
|
||||
if (sdata->u.ibss.flags & IEEE80211_IBSS_BSSID_SET)
|
||||
if (sdata->u.ibss.bssid)
|
||||
goto put_bss;
|
||||
|
||||
/* not an IBSS */
|
||||
@@ -369,13 +354,14 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
|
||||
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. */
|
||||
/*
|
||||
* XXX: 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);
|
||||
}
|
||||
if (net_ratelimit())
|
||||
printk(KERN_DEBUG "%s: No room for a new IBSS STA entry %pM\n",
|
||||
sdata->dev->name, addr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -432,14 +418,15 @@ 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);
|
||||
mod_timer(&ifibss->timer,
|
||||
round_jiffies(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)))
|
||||
if (ifibss->fixed_channel)
|
||||
return;
|
||||
|
||||
printk(KERN_DEBUG "%s: No active IBSS STAs - trying to scan for other "
|
||||
@@ -455,7 +442,7 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
|
||||
ieee80211_request_scan(sdata, &sdata->local->int_scan_req);
|
||||
}
|
||||
|
||||
static int ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
|
||||
static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
@@ -466,7 +453,7 @@ static int ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
|
||||
u16 capability;
|
||||
int i;
|
||||
|
||||
if (ifibss->flags & IEEE80211_IBSS_BSSID_SET) {
|
||||
if (ifibss->fixed_bssid) {
|
||||
memcpy(bssid, ifibss->bssid, ETH_ALEN);
|
||||
} else {
|
||||
/* Generate random, not broadcast, locally administered BSSID. Mix in
|
||||
@@ -482,7 +469,7 @@ static int ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
|
||||
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];
|
||||
sband = local->hw.wiphy->bands[ifibss->channel->band];
|
||||
|
||||
if (local->hw.conf.beacon_int == 0)
|
||||
local->hw.conf.beacon_int = 100;
|
||||
@@ -500,24 +487,20 @@ static int ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
|
||||
*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, 0);
|
||||
__ieee80211_sta_join_ibss(sdata, bssid, local->hw.conf.beacon_int,
|
||||
ifibss->channel, sband->n_bitrates,
|
||||
supp_rates, capability, 0);
|
||||
}
|
||||
|
||||
static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
|
||||
static void 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;
|
||||
struct ieee80211_channel *chan = NULL;
|
||||
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",
|
||||
@@ -525,11 +508,15 @@ static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
|
||||
#endif /* CONFIG_MAC80211_IBSS_DEBUG */
|
||||
|
||||
if (active_ibss)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
if (ifibss->flags & IEEE80211_IBSS_BSSID_SET)
|
||||
if (ifibss->fixed_bssid)
|
||||
bssid = ifibss->bssid;
|
||||
bss = (void *)cfg80211_get_bss(local->hw.wiphy, NULL, bssid,
|
||||
if (ifibss->fixed_channel)
|
||||
chan = ifibss->channel;
|
||||
if (!is_zero_ether_addr(ifibss->bssid))
|
||||
bssid = ifibss->bssid;
|
||||
bss = (void *)cfg80211_get_bss(local->hw.wiphy, chan, bssid,
|
||||
ifibss->ssid, ifibss->ssid_len,
|
||||
WLAN_CAPABILITY_IBSS,
|
||||
WLAN_CAPABILITY_IBSS);
|
||||
@@ -540,18 +527,14 @@ static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
|
||||
"%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;
|
||||
|
||||
if (bss && memcmp(ifibss->bssid, bss->cbss.bssid, ETH_ALEN)) {
|
||||
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_sta_join_ibss(sdata, bss);
|
||||
ieee80211_rx_bss_put(local, bss);
|
||||
return ret;
|
||||
return;
|
||||
} else if (bss)
|
||||
ieee80211_rx_bss_put(local, bss);
|
||||
|
||||
@@ -562,29 +545,31 @@ static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
|
||||
/* 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 +
|
||||
mod_timer(&ifibss->timer,
|
||||
round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL));
|
||||
} else if (time_after(jiffies, ifibss->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;
|
||||
return;
|
||||
|
||||
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);
|
||||
local->int_scan_req.ssids[0].ssid_len =
|
||||
ifibss->ssid_len;
|
||||
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);
|
||||
if (!(local->oper_channel->flags & IEEE80211_CHAN_NO_IBSS)) {
|
||||
ieee80211_sta_create_ibss(sdata);
|
||||
return;
|
||||
}
|
||||
printk(KERN_DEBUG "%s: IBSS not allowed on"
|
||||
" %d MHz\n", sdata->dev->name,
|
||||
local->hw.conf.channel->center_freq);
|
||||
@@ -595,11 +580,9 @@ static int ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
|
||||
}
|
||||
|
||||
ifibss->state = IEEE80211_IBSS_MLME_SEARCH;
|
||||
mod_timer(&ifibss->timer, jiffies + interval);
|
||||
return 0;
|
||||
mod_timer(&ifibss->timer,
|
||||
round_jiffies(jiffies + interval));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
|
||||
@@ -614,7 +597,7 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
|
||||
u8 *pos, *end;
|
||||
|
||||
if (ifibss->state != IEEE80211_IBSS_MLME_JOINED ||
|
||||
len < 24 + 2 || !ifibss->probe_resp)
|
||||
len < 24 + 2 || !ifibss->presp)
|
||||
return;
|
||||
|
||||
if (local->ops->tx_last_beacon)
|
||||
@@ -649,13 +632,13 @@ static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
|
||||
}
|
||||
if (pos[1] != 0 &&
|
||||
(pos[1] != ifibss->ssid_len ||
|
||||
memcmp(pos + 2, ifibss->ssid, ifibss->ssid_len) != 0)) {
|
||||
!memcmp(pos + 2, ifibss->ssid, ifibss->ssid_len))) {
|
||||
/* Ignore ProbeReq for foreign SSID */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Reply with ProbeResp */
|
||||
skb = skb_copy(ifibss->probe_resp, GFP_KERNEL);
|
||||
skb = skb_copy(ifibss->presp, GFP_KERNEL);
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
@@ -794,89 +777,21 @@ void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata)
|
||||
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;
|
||||
set_bit(IEEE80211_IBSS_REQ_RUN, &ifibss->request);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
|
||||
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);
|
||||
mutex_lock(&local->iflist_mtx);
|
||||
list_for_each_entry(sdata, &local->interfaces, list) {
|
||||
if (sdata->vif.type != NL80211_IFTYPE_ADHOC)
|
||||
continue;
|
||||
sdata->u.ibss.last_scan_completed = jiffies;
|
||||
ieee80211_sta_find_ibss(sdata);
|
||||
}
|
||||
mutex_unlock(&local->iflist_mtx);
|
||||
}
|
||||
|
||||
ieee80211_rx_result
|
||||
@@ -906,3 +821,71 @@ ieee80211_ibss_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
|
||||
|
||||
return RX_DROP_MONITOR;
|
||||
}
|
||||
|
||||
int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_ibss_params *params)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
memcpy(sdata->u.ibss.ssid, params->ssid, IEEE80211_MAX_SSID_LEN);
|
||||
sdata->u.ibss.ssid_len = params->ssid_len;
|
||||
|
||||
if (params->bssid) {
|
||||
memcpy(sdata->u.ibss.bssid, params->bssid, ETH_ALEN);
|
||||
sdata->u.ibss.fixed_bssid = true;
|
||||
} else
|
||||
sdata->u.ibss.fixed_bssid = false;
|
||||
|
||||
sdata->u.ibss.channel = params->channel;
|
||||
sdata->u.ibss.fixed_channel = params->channel_fixed;
|
||||
|
||||
if (params->ie) {
|
||||
sdata->u.ibss.ie = kmemdup(params->ie, params->ie_len,
|
||||
GFP_KERNEL);
|
||||
if (sdata->u.ibss.ie)
|
||||
sdata->u.ibss.ie_len = params->ie_len;
|
||||
}
|
||||
|
||||
skb = dev_alloc_skb(sdata->local->hw.extra_tx_headroom +
|
||||
36 /* bitrates */ +
|
||||
34 /* SSID */ +
|
||||
3 /* DS params */ +
|
||||
4 /* IBSS params */ +
|
||||
params->ie_len);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
sdata->u.ibss.skb = skb;
|
||||
sdata->u.ibss.state = IEEE80211_IBSS_MLME_SEARCH;
|
||||
sdata->u.ibss.ibss_join_req = jiffies;
|
||||
|
||||
set_bit(IEEE80211_IBSS_REQ_RUN, &sdata->u.ibss.request);
|
||||
queue_work(sdata->local->hw.workqueue, &sdata->u.ibss.work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
del_timer_sync(&sdata->u.ibss.timer);
|
||||
clear_bit(IEEE80211_IBSS_REQ_RUN, &sdata->u.ibss.request);
|
||||
cancel_work_sync(&sdata->u.ibss.work);
|
||||
clear_bit(IEEE80211_IBSS_REQ_RUN, &sdata->u.ibss.request);
|
||||
|
||||
sta_info_flush(sdata->local, sdata);
|
||||
|
||||
/* remove beacon */
|
||||
kfree(sdata->u.ibss.ie);
|
||||
skb = sdata->u.ibss.presp;
|
||||
rcu_assign_pointer(sdata->u.ibss.presp, NULL);
|
||||
ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON_ENABLED);
|
||||
synchronize_rcu();
|
||||
kfree_skb(skb);
|
||||
|
||||
skb_queue_purge(&sdata->u.ibss.skb_queue);
|
||||
memset(sdata->u.ibss.bssid, 0, ETH_ALEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -24,7 +24,6 @@
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <net/cfg80211.h>
|
||||
#include <net/wireless.h>
|
||||
#include <net/iw_handler.h>
|
||||
#include <net/mac80211.h>
|
||||
#include "key.h"
|
||||
@@ -295,6 +294,8 @@ struct ieee80211_if_managed {
|
||||
int auth_tries; /* retries for auth req */
|
||||
int assoc_tries; /* retries for assoc req */
|
||||
|
||||
bool powersave; /* powersave requested for this iface */
|
||||
|
||||
unsigned long request;
|
||||
|
||||
unsigned long last_probe;
|
||||
@@ -306,6 +307,8 @@ struct ieee80211_if_managed {
|
||||
int auth_alg; /* currently used IEEE 802.11 authentication algorithm */
|
||||
int auth_transaction;
|
||||
|
||||
u32 beacon_crc;
|
||||
|
||||
enum {
|
||||
IEEE80211_MFP_DISABLED,
|
||||
IEEE80211_MFP_OPTIONAL,
|
||||
@@ -319,14 +322,6 @@ struct ieee80211_if_managed {
|
||||
size_t sme_auth_ie_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,
|
||||
};
|
||||
@@ -337,17 +332,20 @@ struct ieee80211_if_ibss {
|
||||
|
||||
struct sk_buff_head skb_queue;
|
||||
|
||||
u8 ssid[IEEE80211_MAX_SSID_LEN];
|
||||
u8 ssid_len;
|
||||
|
||||
u32 flags;
|
||||
unsigned long request;
|
||||
unsigned long last_scan_completed;
|
||||
bool fixed_bssid;
|
||||
bool fixed_channel;
|
||||
|
||||
u8 bssid[ETH_ALEN];
|
||||
|
||||
unsigned long request;
|
||||
u8 ssid[IEEE80211_MAX_SSID_LEN];
|
||||
u8 ssid_len, ie_len;
|
||||
u8 *ie;
|
||||
struct ieee80211_channel *channel;
|
||||
|
||||
unsigned long ibss_join_req;
|
||||
struct sk_buff *probe_resp; /* ProbeResp template for IBSS */
|
||||
/* probe response/beacon for IBSS */
|
||||
struct sk_buff *presp, *skb;
|
||||
|
||||
enum {
|
||||
IEEE80211_IBSS_MLME_SEARCH,
|
||||
@@ -626,8 +624,6 @@ struct ieee80211_local {
|
||||
spinlock_t sta_lock;
|
||||
unsigned long num_sta;
|
||||
struct list_head sta_list;
|
||||
struct list_head sta_flush_list;
|
||||
struct work_struct sta_flush_work;
|
||||
struct sta_info *sta_hash[STA_HASH_SIZE];
|
||||
struct timer_list sta_cleanup;
|
||||
|
||||
@@ -647,9 +643,6 @@ struct ieee80211_local {
|
||||
|
||||
struct rate_control_ref *rate_ctrl;
|
||||
|
||||
int rts_threshold;
|
||||
int fragmentation_threshold;
|
||||
|
||||
struct crypto_blkcipher *wep_tx_tfm;
|
||||
struct crypto_blkcipher *wep_rx_tfm;
|
||||
u32 wep_iv;
|
||||
@@ -671,10 +664,12 @@ struct ieee80211_local {
|
||||
struct cfg80211_scan_request int_scan_req;
|
||||
struct cfg80211_scan_request *scan_req;
|
||||
struct ieee80211_channel *scan_channel;
|
||||
const u8 *orig_ies;
|
||||
int orig_ies_len;
|
||||
int scan_channel_idx;
|
||||
int scan_ies_len;
|
||||
|
||||
enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state;
|
||||
unsigned long last_scan_completed;
|
||||
struct delayed_work scan_work;
|
||||
struct ieee80211_sub_if_data *scan_sdata;
|
||||
enum nl80211_channel_type oper_channel_type;
|
||||
@@ -736,15 +731,22 @@ struct ieee80211_local {
|
||||
int wifi_wme_noack_test;
|
||||
unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */
|
||||
|
||||
bool powersave;
|
||||
bool pspolling;
|
||||
/*
|
||||
* PS can only be enabled when we have exactly one managed
|
||||
* interface (and monitors) in PS, this then points there.
|
||||
*/
|
||||
struct ieee80211_sub_if_data *ps_sdata;
|
||||
struct work_struct dynamic_ps_enable_work;
|
||||
struct work_struct dynamic_ps_disable_work;
|
||||
struct timer_list dynamic_ps_timer;
|
||||
struct notifier_block network_latency_notifier;
|
||||
|
||||
int user_power_level; /* in dBm */
|
||||
int power_constr_level; /* in dBm */
|
||||
|
||||
struct work_struct restart_work;
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
struct local_debugfsdentries {
|
||||
struct dentry *rcdir;
|
||||
@@ -830,7 +832,7 @@ struct ieee802_11_elems {
|
||||
u8 *fh_params;
|
||||
u8 *ds_params;
|
||||
u8 *cf_params;
|
||||
u8 *tim;
|
||||
struct ieee80211_tim_ie *tim;
|
||||
u8 *ibss_params;
|
||||
u8 *challenge;
|
||||
u8 *wpa;
|
||||
@@ -927,12 +929,11 @@ int ieee80211_sta_deauthenticate(struct ieee80211_sub_if_data *sdata, u16 reason
|
||||
int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason);
|
||||
void ieee80211_send_pspoll(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency);
|
||||
int ieee80211_max_network_latency(struct notifier_block *nb,
|
||||
unsigned long data, void *dummy);
|
||||
|
||||
/* 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
|
||||
@@ -940,6 +941,9 @@ 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);
|
||||
int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_ibss_params *params);
|
||||
int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata);
|
||||
|
||||
/* scan/BSS handling */
|
||||
void ieee80211_scan_work(struct work_struct *work);
|
||||
@@ -995,9 +999,6 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev);
|
||||
void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband,
|
||||
struct ieee80211_ht_cap *ht_cap_ie,
|
||||
struct ieee80211_sta_ht_cap *ht_cap);
|
||||
u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_ht_info *hti,
|
||||
u16 ap_ht_cap_flags);
|
||||
void ieee80211_send_bar(struct ieee80211_sub_if_data *sdata, u8 *ra, u16 tid, u16 ssn);
|
||||
void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
|
||||
const u8 *da, u16 tid,
|
||||
@@ -1036,15 +1037,22 @@ void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
|
||||
u16 capab_info, u8 *pwr_constr_elem,
|
||||
u8 pwr_constr_elem_len);
|
||||
|
||||
/* Suspend/resume */
|
||||
/* Suspend/resume and hw reconfiguration */
|
||||
int ieee80211_reconfig(struct ieee80211_local *local);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
int __ieee80211_suspend(struct ieee80211_hw *hw);
|
||||
int __ieee80211_resume(struct ieee80211_hw *hw);
|
||||
|
||||
static inline int __ieee80211_resume(struct ieee80211_hw *hw)
|
||||
{
|
||||
return ieee80211_reconfig(hw_to_local(hw));
|
||||
}
|
||||
#else
|
||||
static inline int __ieee80211_suspend(struct ieee80211_hw *hw)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int __ieee80211_resume(struct ieee80211_hw *hw)
|
||||
{
|
||||
return 0;
|
||||
@@ -1060,12 +1068,15 @@ u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
|
||||
int ieee80211_frame_duration(struct ieee80211_local *local, size_t len,
|
||||
int rate, int erp, int short_preamble);
|
||||
void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx,
|
||||
struct ieee80211_hdr *hdr);
|
||||
struct ieee80211_hdr *hdr, const u8 *tsc);
|
||||
void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
|
||||
int encrypt);
|
||||
void ieee802_11_parse_elems(u8 *start, size_t len,
|
||||
struct ieee802_11_elems *elems);
|
||||
u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
|
||||
struct ieee802_11_elems *elems,
|
||||
u64 filter, u32 crc);
|
||||
int ieee80211_set_freq(struct ieee80211_sub_if_data *sdata, int freq);
|
||||
u32 ieee80211_mandatory_rates(struct ieee80211_local *local,
|
||||
enum ieee80211_band band);
|
||||
@@ -1093,9 +1104,11 @@ 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);
|
||||
int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
|
||||
const u8 *ie, size_t ie_len);
|
||||
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);
|
||||
const u8 *ssid, size_t ssid_len,
|
||||
const u8 *ie, size_t ie_len);
|
||||
|
||||
void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
|
||||
const size_t supp_rates_len,
|
||||
|
@@ -235,11 +235,7 @@ static int ieee80211_open(struct net_device *dev)
|
||||
netif_addr_unlock_bh(local->mdev);
|
||||
break;
|
||||
case NL80211_IFTYPE_STATION:
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
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;
|
||||
sdata->u.mgd.flags &= ~IEEE80211_STA_PREV_BSSID_SET;
|
||||
/* fall through */
|
||||
default:
|
||||
conf.vif = &sdata->vif;
|
||||
@@ -317,6 +313,8 @@ static int ieee80211_open(struct net_device *dev)
|
||||
ieee80211_set_wmm_default(sdata);
|
||||
}
|
||||
|
||||
ieee80211_recalc_ps(local, -1);
|
||||
|
||||
/*
|
||||
* ieee80211_sta_work is disabled while network interface
|
||||
* is down. Therefore, some configuration changes may not
|
||||
@@ -325,8 +323,6 @@ static int ieee80211_open(struct net_device *dev)
|
||||
*/
|
||||
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);
|
||||
|
||||
@@ -497,7 +493,6 @@ static int ieee80211_stop(struct net_device *dev)
|
||||
/* 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();
|
||||
@@ -572,6 +567,8 @@ static int ieee80211_stop(struct net_device *dev)
|
||||
hw_reconf_flags = 0;
|
||||
}
|
||||
|
||||
ieee80211_recalc_ps(local, -1);
|
||||
|
||||
/* do after stop to avoid reconfiguring when we stop anyway */
|
||||
if (hw_reconf_flags)
|
||||
ieee80211_hw_config(local, hw_reconf_flags);
|
||||
@@ -649,7 +646,8 @@ static void ieee80211_teardown_sdata(struct net_device *dev)
|
||||
mesh_rmc_free(sdata);
|
||||
break;
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
kfree_skb(sdata->u.ibss.probe_resp);
|
||||
if (WARN_ON(sdata->u.ibss.presp))
|
||||
kfree_skb(sdata->u.ibss.presp);
|
||||
break;
|
||||
case NL80211_IFTYPE_STATION:
|
||||
kfree(sdata->u.mgd.extra_ie);
|
||||
|
@@ -21,6 +21,7 @@
|
||||
#include <linux/wireless.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/pm_qos_params.h>
|
||||
#include <net/net_namespace.h>
|
||||
#include <net/cfg80211.h>
|
||||
|
||||
@@ -208,7 +209,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.ibss.probe_resp;
|
||||
conf.enable_beacon = !!sdata->u.ibss.presp;
|
||||
break;
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
conf.enable_beacon = true;
|
||||
@@ -696,6 +697,28 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_tx_status);
|
||||
|
||||
static void ieee80211_restart_work(struct work_struct *work)
|
||||
{
|
||||
struct ieee80211_local *local =
|
||||
container_of(work, struct ieee80211_local, restart_work);
|
||||
|
||||
rtnl_lock();
|
||||
ieee80211_reconfig(local);
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
void ieee80211_restart_hw(struct ieee80211_hw *hw)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
|
||||
/* use this reason, __ieee80211_resume will unblock it */
|
||||
ieee80211_stop_queues_by_reason(hw,
|
||||
IEEE80211_QUEUE_STOP_REASON_SUSPEND);
|
||||
|
||||
schedule_work(&local->restart_work);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_restart_hw);
|
||||
|
||||
struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
||||
const struct ieee80211_ops *ops)
|
||||
{
|
||||
@@ -728,12 +751,13 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
||||
return NULL;
|
||||
|
||||
wiphy->privid = mac80211_wiphy_privid;
|
||||
wiphy->max_scan_ssids = 4;
|
||||
|
||||
/* Yes, putting cfg80211_bss into ieee80211_bss is a hack */
|
||||
wiphy->bss_priv_size = sizeof(struct ieee80211_bss) -
|
||||
sizeof(struct cfg80211_bss);
|
||||
|
||||
local = wiphy_priv(wiphy);
|
||||
|
||||
local->hw.wiphy = wiphy;
|
||||
|
||||
local->hw.priv = (char *)local +
|
||||
@@ -752,10 +776,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
||||
/* set up some defaults */
|
||||
local->hw.queues = 1;
|
||||
local->hw.max_rates = 1;
|
||||
local->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
|
||||
local->fragmentation_threshold = IEEE80211_MAX_FRAG_THRESHOLD;
|
||||
local->hw.conf.long_frame_max_tx_count = 4;
|
||||
local->hw.conf.short_frame_max_tx_count = 7;
|
||||
local->hw.conf.long_frame_max_tx_count = wiphy->retry_long;
|
||||
local->hw.conf.short_frame_max_tx_count = wiphy->retry_short;
|
||||
local->hw.conf.radio_enabled = true;
|
||||
|
||||
INIT_LIST_HEAD(&local->interfaces);
|
||||
@@ -767,6 +789,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
|
||||
|
||||
INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work);
|
||||
|
||||
INIT_WORK(&local->restart_work, ieee80211_restart_work);
|
||||
|
||||
INIT_WORK(&local->dynamic_ps_enable_work,
|
||||
ieee80211_dynamic_ps_enable_work);
|
||||
INIT_WORK(&local->dynamic_ps_disable_work,
|
||||
@@ -820,7 +844,17 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
|
||||
enum ieee80211_band band;
|
||||
struct net_device *mdev;
|
||||
struct ieee80211_master_priv *mpriv;
|
||||
int channels, i, j;
|
||||
int channels, i, j, max_bitrates;
|
||||
bool supp_ht;
|
||||
static const u32 cipher_suites[] = {
|
||||
WLAN_CIPHER_SUITE_WEP40,
|
||||
WLAN_CIPHER_SUITE_WEP104,
|
||||
WLAN_CIPHER_SUITE_TKIP,
|
||||
WLAN_CIPHER_SUITE_CCMP,
|
||||
|
||||
/* keep last -- depends on hw flags! */
|
||||
WLAN_CIPHER_SUITE_AES_CMAC
|
||||
};
|
||||
|
||||
/*
|
||||
* generic code guarantees at least one band,
|
||||
@@ -828,18 +862,25 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
|
||||
* that hw.conf.channel is assigned
|
||||
*/
|
||||
channels = 0;
|
||||
max_bitrates = 0;
|
||||
supp_ht = false;
|
||||
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
|
||||
struct ieee80211_supported_band *sband;
|
||||
|
||||
sband = local->hw.wiphy->bands[band];
|
||||
if (sband && !local->oper_channel) {
|
||||
if (!sband)
|
||||
continue;
|
||||
if (!local->oper_channel) {
|
||||
/* init channel we're on */
|
||||
local->hw.conf.channel =
|
||||
local->oper_channel =
|
||||
local->scan_channel = &sband->channels[0];
|
||||
}
|
||||
if (sband)
|
||||
channels += sband->n_channels;
|
||||
channels += sband->n_channels;
|
||||
|
||||
if (max_bitrates < sband->n_bitrates)
|
||||
max_bitrates = sband->n_bitrates;
|
||||
supp_ht = supp_ht || sband->ht_cap.ht_supported;
|
||||
}
|
||||
|
||||
local->int_scan_req.n_channels = channels;
|
||||
@@ -859,6 +900,37 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
|
||||
else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)
|
||||
local->hw.wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC;
|
||||
|
||||
/*
|
||||
* Calculate scan IE length -- we need this to alloc
|
||||
* memory and to subtract from the driver limit. It
|
||||
* includes the (extended) supported rates and HT
|
||||
* information -- SSID is the driver's responsibility.
|
||||
*/
|
||||
local->scan_ies_len = 4 + max_bitrates; /* (ext) supp rates */
|
||||
if (supp_ht)
|
||||
local->scan_ies_len += 2 + sizeof(struct ieee80211_ht_cap);
|
||||
|
||||
if (!local->ops->hw_scan) {
|
||||
/* For hw_scan, driver needs to set these up. */
|
||||
local->hw.wiphy->max_scan_ssids = 4;
|
||||
local->hw.wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the driver supports any scan IEs, then assume the
|
||||
* limit includes the IEs mac80211 will add, otherwise
|
||||
* leave it at zero and let the driver sort it out; we
|
||||
* still pass our IEs to the driver but userspace will
|
||||
* not be allowed to in that case.
|
||||
*/
|
||||
if (local->hw.wiphy->max_scan_ie_len)
|
||||
local->hw.wiphy->max_scan_ie_len -= local->scan_ies_len;
|
||||
|
||||
local->hw.wiphy->cipher_suites = cipher_suites;
|
||||
local->hw.wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
|
||||
if (!(local->hw.flags & IEEE80211_HW_MFP_CAPABLE))
|
||||
local->hw.wiphy->n_cipher_suites--;
|
||||
|
||||
result = wiphy_register(local->hw.wiphy);
|
||||
if (result < 0)
|
||||
goto fail_wiphy_register;
|
||||
@@ -965,25 +1037,38 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
|
||||
}
|
||||
}
|
||||
|
||||
local->network_latency_notifier.notifier_call =
|
||||
ieee80211_max_network_latency;
|
||||
result = pm_qos_add_notifier(PM_QOS_NETWORK_LATENCY,
|
||||
&local->network_latency_notifier);
|
||||
|
||||
if (result) {
|
||||
rtnl_lock();
|
||||
goto fail_pm_qos;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_wep:
|
||||
fail_pm_qos:
|
||||
ieee80211_led_exit(local);
|
||||
ieee80211_remove_interfaces(local);
|
||||
fail_wep:
|
||||
rate_control_deinitialize(local);
|
||||
fail_rate:
|
||||
fail_rate:
|
||||
unregister_netdevice(local->mdev);
|
||||
local->mdev = NULL;
|
||||
fail_dev:
|
||||
fail_dev:
|
||||
rtnl_unlock();
|
||||
sta_info_stop(local);
|
||||
fail_sta_info:
|
||||
fail_sta_info:
|
||||
debugfs_hw_del(local);
|
||||
destroy_workqueue(local->hw.workqueue);
|
||||
fail_workqueue:
|
||||
fail_workqueue:
|
||||
if (local->mdev)
|
||||
free_netdev(local->mdev);
|
||||
fail_mdev_alloc:
|
||||
fail_mdev_alloc:
|
||||
wiphy_unregister(local->hw.wiphy);
|
||||
fail_wiphy_register:
|
||||
fail_wiphy_register:
|
||||
kfree(local->int_scan_req.channels);
|
||||
return result;
|
||||
}
|
||||
@@ -996,6 +1081,9 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
|
||||
tasklet_kill(&local->tx_pending_tasklet);
|
||||
tasklet_kill(&local->tasklet);
|
||||
|
||||
pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY,
|
||||
&local->network_latency_notifier);
|
||||
|
||||
rtnl_lock();
|
||||
|
||||
/*
|
||||
|
@@ -17,6 +17,8 @@
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/pm_qos_params.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <net/mac80211.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
@@ -80,6 +82,89 @@ static int ieee80211_compatible_rates(struct ieee80211_bss *bss,
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* ieee80211_enable_ht should be called only after the operating band
|
||||
* has been determined as ht configuration depends on the hw's
|
||||
* HT abilities for a specific band.
|
||||
*/
|
||||
static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_ht_info *hti,
|
||||
u16 ap_ht_cap_flags)
|
||||
{
|
||||
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;
|
||||
|
||||
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
|
||||
|
||||
memset(&ht, 0, sizeof(ht));
|
||||
|
||||
/* HT is not supported */
|
||||
if (!sband->ht_cap.ht_supported)
|
||||
enable_ht = false;
|
||||
|
||||
/* check that channel matches the right operating channel */
|
||||
if (local->hw.conf.channel->center_freq !=
|
||||
ieee80211_channel_to_frequency(hti->control_chan))
|
||||
enable_ht = false;
|
||||
|
||||
if (enable_ht) {
|
||||
channel_type = NL80211_CHAN_HT20;
|
||||
|
||||
if (!(ap_ht_cap_flags & IEEE80211_HT_CAP_40MHZ_INTOLERANT) &&
|
||||
(sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
|
||||
(hti->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) {
|
||||
switch(hti->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
|
||||
channel_type = NL80211_CHAN_HT40PLUS;
|
||||
break;
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
|
||||
channel_type = NL80211_CHAN_HT40MINUS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ht_changed = conf_is_ht(&local->hw.conf) != enable_ht ||
|
||||
channel_type != local->hw.conf.channel_type;
|
||||
|
||||
local->oper_channel_type = channel_type;
|
||||
|
||||
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 */
|
||||
if (!enable_ht)
|
||||
return 0;
|
||||
|
||||
ht.operation_mode = le16_to_cpu(hti->operation_mode);
|
||||
|
||||
/* if bss configuration changed store the new one */
|
||||
if (memcmp(&sdata->vif.bss_conf.ht, &ht, sizeof(ht))) {
|
||||
changed |= BSS_CHANGED_HT;
|
||||
sdata->vif.bss_conf.ht = ht;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
/* frame sending functions */
|
||||
|
||||
static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
|
||||
@@ -325,6 +410,10 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
|
||||
/* u.deauth.reason_code == u.disassoc.reason_code */
|
||||
mgmt->u.deauth.reason_code = cpu_to_le16(reason);
|
||||
|
||||
if (stype == IEEE80211_STYPE_DEAUTH)
|
||||
cfg80211_send_deauth(sdata->dev, (u8 *) mgmt, skb->len);
|
||||
else
|
||||
cfg80211_send_disassoc(sdata->dev, (u8 *) mgmt, skb->len);
|
||||
ieee80211_tx_skb(sdata, skb, ifmgd->flags & IEEE80211_STA_MFP_ENABLED);
|
||||
}
|
||||
|
||||
@@ -359,6 +448,166 @@ void ieee80211_send_pspoll(struct ieee80211_local *local,
|
||||
ieee80211_tx_skb(sdata, skb, 0);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* powersave */
|
||||
static void ieee80211_enable_ps(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_conf *conf = &local->hw.conf;
|
||||
|
||||
if (conf->dynamic_ps_timeout > 0 &&
|
||||
!(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)) {
|
||||
mod_timer(&local->dynamic_ps_timer, jiffies +
|
||||
msecs_to_jiffies(conf->dynamic_ps_timeout));
|
||||
} else {
|
||||
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
|
||||
ieee80211_send_nullfunc(local, sdata, 1);
|
||||
conf->flags |= IEEE80211_CONF_PS;
|
||||
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
|
||||
}
|
||||
}
|
||||
|
||||
static void ieee80211_change_ps(struct ieee80211_local *local)
|
||||
{
|
||||
struct ieee80211_conf *conf = &local->hw.conf;
|
||||
|
||||
if (local->ps_sdata) {
|
||||
if (!(local->ps_sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED))
|
||||
return;
|
||||
|
||||
ieee80211_enable_ps(local, local->ps_sdata);
|
||||
} else if (conf->flags & IEEE80211_CONF_PS) {
|
||||
conf->flags &= ~IEEE80211_CONF_PS;
|
||||
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
|
||||
del_timer_sync(&local->dynamic_ps_timer);
|
||||
cancel_work_sync(&local->dynamic_ps_enable_work);
|
||||
}
|
||||
}
|
||||
|
||||
/* need to hold RTNL or interface lock */
|
||||
void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata, *found = NULL;
|
||||
int count = 0;
|
||||
|
||||
if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) {
|
||||
local->ps_sdata = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
list_for_each_entry(sdata, &local->interfaces, list) {
|
||||
if (!netif_running(sdata->dev))
|
||||
continue;
|
||||
if (sdata->vif.type != NL80211_IFTYPE_STATION)
|
||||
continue;
|
||||
found = sdata;
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count == 1 && found->u.mgd.powersave) {
|
||||
s32 beaconint_us;
|
||||
|
||||
if (latency < 0)
|
||||
latency = pm_qos_requirement(PM_QOS_NETWORK_LATENCY);
|
||||
|
||||
beaconint_us = ieee80211_tu_to_usec(
|
||||
found->vif.bss_conf.beacon_int);
|
||||
|
||||
if (beaconint_us > latency) {
|
||||
local->ps_sdata = NULL;
|
||||
} else {
|
||||
u8 dtimper = found->vif.bss_conf.dtim_period;
|
||||
int maxslp = 1;
|
||||
|
||||
if (dtimper > 1)
|
||||
maxslp = min_t(int, dtimper,
|
||||
latency / beaconint_us);
|
||||
|
||||
local->hw.conf.max_sleep_interval = maxslp;
|
||||
local->ps_sdata = found;
|
||||
}
|
||||
} else {
|
||||
local->ps_sdata = NULL;
|
||||
}
|
||||
|
||||
ieee80211_change_ps(local);
|
||||
}
|
||||
|
||||
void ieee80211_dynamic_ps_disable_work(struct work_struct *work)
|
||||
{
|
||||
struct ieee80211_local *local =
|
||||
container_of(work, struct ieee80211_local,
|
||||
dynamic_ps_disable_work);
|
||||
|
||||
if (local->hw.conf.flags & IEEE80211_CONF_PS) {
|
||||
local->hw.conf.flags &= ~IEEE80211_CONF_PS;
|
||||
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
|
||||
}
|
||||
|
||||
ieee80211_wake_queues_by_reason(&local->hw,
|
||||
IEEE80211_QUEUE_STOP_REASON_PS);
|
||||
}
|
||||
|
||||
void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
|
||||
{
|
||||
struct ieee80211_local *local =
|
||||
container_of(work, struct ieee80211_local,
|
||||
dynamic_ps_enable_work);
|
||||
struct ieee80211_sub_if_data *sdata = local->ps_sdata;
|
||||
|
||||
/* can only happen when PS was just disabled anyway */
|
||||
if (!sdata)
|
||||
return;
|
||||
|
||||
if (local->hw.conf.flags & IEEE80211_CONF_PS)
|
||||
return;
|
||||
|
||||
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
|
||||
ieee80211_send_nullfunc(local, sdata, 1);
|
||||
|
||||
local->hw.conf.flags |= IEEE80211_CONF_PS;
|
||||
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
|
||||
}
|
||||
|
||||
void ieee80211_dynamic_ps_timer(unsigned long data)
|
||||
{
|
||||
struct ieee80211_local *local = (void *) data;
|
||||
|
||||
queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work);
|
||||
}
|
||||
|
||||
/* MLME */
|
||||
static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
|
||||
struct ieee80211_if_managed *ifmgd,
|
||||
@@ -435,30 +684,6 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
|
||||
}
|
||||
}
|
||||
|
||||
static bool ieee80211_check_tim(struct ieee802_11_elems *elems, u16 aid)
|
||||
{
|
||||
u8 mask;
|
||||
u8 index, indexn1, indexn2;
|
||||
struct ieee80211_tim_ie *tim = (struct ieee80211_tim_ie *) elems->tim;
|
||||
|
||||
if (unlikely(!tim || elems->tim_len < 4))
|
||||
return false;
|
||||
|
||||
aid &= 0x3fff;
|
||||
index = aid / 8;
|
||||
mask = 1 << (aid & 7);
|
||||
|
||||
indexn1 = tim->bitmap_ctrl & 0xfe;
|
||||
indexn2 = elems->tim_len + indexn1 - 4;
|
||||
|
||||
if (index < indexn1 || index > indexn2)
|
||||
return false;
|
||||
|
||||
index -= indexn1;
|
||||
|
||||
return !!(tim->virtual_map[index] & mask);
|
||||
}
|
||||
|
||||
static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
|
||||
u16 capab, bool erp_valid, u8 erp)
|
||||
{
|
||||
@@ -634,18 +859,11 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
|
||||
bss_info_changed |= BSS_CHANGED_BASIC_RATES;
|
||||
ieee80211_bss_info_change_notify(sdata, bss_info_changed);
|
||||
|
||||
if (local->powersave) {
|
||||
if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) &&
|
||||
local->hw.conf.dynamic_ps_timeout > 0) {
|
||||
mod_timer(&local->dynamic_ps_timer, jiffies +
|
||||
msecs_to_jiffies(
|
||||
local->hw.conf.dynamic_ps_timeout));
|
||||
} else {
|
||||
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
|
||||
ieee80211_send_nullfunc(local, sdata, 1);
|
||||
conf->flags |= IEEE80211_CONF_PS;
|
||||
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
|
||||
}
|
||||
/* will be same as sdata */
|
||||
if (local->ps_sdata) {
|
||||
mutex_lock(&local->iflist_mtx);
|
||||
ieee80211_recalc_ps(local, -1);
|
||||
mutex_unlock(&local->iflist_mtx);
|
||||
}
|
||||
|
||||
netif_tx_start_all_queues(sdata->dev);
|
||||
@@ -714,7 +932,7 @@ static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata)
|
||||
" timed out\n",
|
||||
sdata->dev->name, ifmgd->bssid);
|
||||
ifmgd->state = IEEE80211_STA_MLME_DISABLED;
|
||||
ieee80211_sta_send_apinfo(sdata);
|
||||
cfg80211_send_auth_timeout(sdata->dev, ifmgd->bssid);
|
||||
ieee80211_rx_bss_remove(sdata, ifmgd->bssid,
|
||||
sdata->local->hw.conf.channel->center_freq,
|
||||
ifmgd->ssid, ifmgd->ssid_len);
|
||||
@@ -897,7 +1115,7 @@ static void ieee80211_associate(struct ieee80211_sub_if_data *sdata)
|
||||
" timed out\n",
|
||||
sdata->dev->name, ifmgd->bssid);
|
||||
ifmgd->state = IEEE80211_STA_MLME_DISABLED;
|
||||
ieee80211_sta_send_apinfo(sdata);
|
||||
cfg80211_send_assoc_timeout(sdata->dev, ifmgd->bssid);
|
||||
ieee80211_rx_bss_remove(sdata, ifmgd->bssid,
|
||||
sdata->local->hw.conf.channel->center_freq,
|
||||
ifmgd->ssid, ifmgd->ssid_len);
|
||||
@@ -1187,7 +1405,7 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
ieee80211_set_disassoc(sdata, true, false, 0);
|
||||
ifmgd->flags &= ~IEEE80211_STA_AUTHENTICATED;
|
||||
cfg80211_send_rx_deauth(sdata->dev, (u8 *) mgmt, len);
|
||||
cfg80211_send_deauth(sdata->dev, (u8 *) mgmt, len);
|
||||
}
|
||||
|
||||
|
||||
@@ -1218,7 +1436,7 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
|
||||
}
|
||||
|
||||
ieee80211_set_disassoc(sdata, false, false, reason_code);
|
||||
cfg80211_send_rx_disassoc(sdata->dev, (u8 *) mgmt, len);
|
||||
cfg80211_send_disassoc(sdata->dev, (u8 *) mgmt, len);
|
||||
}
|
||||
|
||||
|
||||
@@ -1287,6 +1505,11 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
|
||||
* association next time. This works around some broken APs
|
||||
* which do not correctly reject reassociation requests. */
|
||||
ifmgd->flags &= ~IEEE80211_STA_PREV_BSSID_SET;
|
||||
cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, len);
|
||||
if (ifmgd->flags & IEEE80211_STA_EXT_SME) {
|
||||
/* Wait for SME to decide what to do next */
|
||||
ifmgd->state = IEEE80211_STA_MLME_DISABLED;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1518,46 +1741,74 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
|
||||
ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the canonical list of information elements we care about,
|
||||
* the filter code also gives us all changes to the Microsoft OUI
|
||||
* (00:50:F2) vendor IE which is used for WMM which we need to track.
|
||||
*
|
||||
* We implement beacon filtering in software since that means we can
|
||||
* avoid processing the frame here and in cfg80211, and userspace
|
||||
* will not be able to tell whether the hardware supports it or not.
|
||||
*
|
||||
* XXX: This list needs to be dynamic -- userspace needs to be able to
|
||||
* add items it requires. It also needs to be able to tell us to
|
||||
* look out for other vendor IEs.
|
||||
*/
|
||||
static const u64 care_about_ies =
|
||||
(1ULL << WLAN_EID_COUNTRY) |
|
||||
(1ULL << WLAN_EID_ERP_INFO) |
|
||||
(1ULL << WLAN_EID_CHANNEL_SWITCH) |
|
||||
(1ULL << WLAN_EID_PWR_CONSTRAINT) |
|
||||
(1ULL << WLAN_EID_HT_CAPABILITY) |
|
||||
(1ULL << WLAN_EID_HT_INFORMATION);
|
||||
|
||||
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_managed *ifmgd;
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
size_t baselen;
|
||||
struct ieee802_11_elems elems;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
u32 changed = 0;
|
||||
bool erp_valid, directed_tim;
|
||||
bool erp_valid, directed_tim = false;
|
||||
u8 erp_value = 0;
|
||||
u32 ncrc;
|
||||
|
||||
/* 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);
|
||||
|
||||
if (sdata->vif.type != NL80211_IFTYPE_STATION)
|
||||
if (rx_status->freq != local->hw.conf.channel->center_freq)
|
||||
return;
|
||||
|
||||
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)
|
||||
ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);
|
||||
ncrc = ieee802_11_parse_elems_crc(mgmt->u.beacon.variable,
|
||||
len - baselen, &elems,
|
||||
care_about_ies, ncrc);
|
||||
|
||||
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
|
||||
directed_tim = ieee80211_check_tim(elems.tim, elems.tim_len,
|
||||
ifmgd->aid);
|
||||
|
||||
ncrc = crc32_be(ncrc, (void *)&directed_tim, sizeof(directed_tim));
|
||||
|
||||
if (ncrc == ifmgd->beacon_crc)
|
||||
return;
|
||||
ifmgd->beacon_crc = ncrc;
|
||||
|
||||
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, true);
|
||||
|
||||
ieee80211_sta_wmm_params(local, ifmgd, elems.wmm_param,
|
||||
elems.wmm_param_len);
|
||||
|
||||
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) {
|
||||
directed_tim = ieee80211_check_tim(&elems, ifmgd->aid);
|
||||
|
||||
if (directed_tim) {
|
||||
if (local->hw.conf.dynamic_ps_timeout > 0) {
|
||||
local->hw.conf.flags &= ~IEEE80211_CONF_PS;
|
||||
@@ -1930,6 +2181,7 @@ static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)
|
||||
void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_managed *ifmgd;
|
||||
u32 hw_flags;
|
||||
|
||||
ifmgd = &sdata->u.mgd;
|
||||
INIT_WORK(&ifmgd->work, ieee80211_sta_work);
|
||||
@@ -1949,6 +2201,13 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
|
||||
IEEE80211_STA_AUTO_CHANNEL_SEL;
|
||||
if (sdata->local->hw.queues >= 4)
|
||||
ifmgd->flags |= IEEE80211_STA_WMM_ENABLED;
|
||||
|
||||
hw_flags = sdata->local->hw.flags;
|
||||
|
||||
if (hw_flags & IEEE80211_HW_SUPPORTS_PS) {
|
||||
ifmgd->powersave = CONFIG_MAC80211_DEFAULT_PS_VALUE;
|
||||
sdata->local->hw.conf.dynamic_ps_timeout = 500;
|
||||
}
|
||||
}
|
||||
|
||||
/* configuration hooks */
|
||||
@@ -2068,9 +2327,6 @@ int ieee80211_sta_deauthenticate(struct ieee80211_sub_if_data *sdata, u16 reason
|
||||
printk(KERN_DEBUG "%s: deauthenticating by local choice (reason=%d)\n",
|
||||
sdata->dev->name, reason);
|
||||
|
||||
if (sdata->vif.type != NL80211_IFTYPE_STATION)
|
||||
return -EINVAL;
|
||||
|
||||
ieee80211_set_disassoc(sdata, true, true, reason);
|
||||
return 0;
|
||||
}
|
||||
@@ -2082,9 +2338,6 @@ int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason)
|
||||
printk(KERN_DEBUG "%s: disassociating by local choice (reason=%d)\n",
|
||||
sdata->dev->name, reason);
|
||||
|
||||
if (sdata->vif.type != NL80211_IFTYPE_STATION)
|
||||
return -EINVAL;
|
||||
|
||||
if (!(ifmgd->flags & IEEE80211_STA_ASSOCIATED))
|
||||
return -ENOLINK;
|
||||
|
||||
@@ -2104,75 +2357,17 @@ void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
void ieee80211_dynamic_ps_disable_work(struct work_struct *work)
|
||||
int ieee80211_max_network_latency(struct notifier_block *nb,
|
||||
unsigned long data, void *dummy)
|
||||
{
|
||||
s32 latency_usec = (s32) data;
|
||||
struct ieee80211_local *local =
|
||||
container_of(work, struct ieee80211_local,
|
||||
dynamic_ps_disable_work);
|
||||
container_of(nb, struct ieee80211_local,
|
||||
network_latency_notifier);
|
||||
|
||||
if (local->hw.conf.flags & IEEE80211_CONF_PS) {
|
||||
local->hw.conf.flags &= ~IEEE80211_CONF_PS;
|
||||
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
|
||||
}
|
||||
mutex_lock(&local->iflist_mtx);
|
||||
ieee80211_recalc_ps(local, latency_usec);
|
||||
mutex_unlock(&local->iflist_mtx);
|
||||
|
||||
ieee80211_wake_queues_by_reason(&local->hw,
|
||||
IEEE80211_QUEUE_STOP_REASON_PS);
|
||||
}
|
||||
|
||||
void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
|
||||
{
|
||||
struct ieee80211_local *local =
|
||||
container_of(work, struct ieee80211_local,
|
||||
dynamic_ps_enable_work);
|
||||
/* XXX: using scan_sdata is completely broken! */
|
||||
struct ieee80211_sub_if_data *sdata = local->scan_sdata;
|
||||
|
||||
if (local->hw.conf.flags & IEEE80211_CONF_PS)
|
||||
return;
|
||||
|
||||
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK && sdata)
|
||||
ieee80211_send_nullfunc(local, sdata, 1);
|
||||
|
||||
local->hw.conf.flags |= IEEE80211_CONF_PS;
|
||||
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
|
||||
}
|
||||
|
||||
void ieee80211_dynamic_ps_timer(unsigned long data)
|
||||
{
|
||||
struct ieee80211_local *local = (void *) 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);
|
||||
return 0;
|
||||
}
|
||||
|
@@ -72,119 +72,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __ieee80211_resume(struct ieee80211_hw *hw)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
struct ieee80211_if_init_conf conf;
|
||||
struct sta_info *sta;
|
||||
unsigned long flags;
|
||||
int res;
|
||||
|
||||
/* restart hardware */
|
||||
if (local->open_count) {
|
||||
res = local->ops->start(hw);
|
||||
|
||||
ieee80211_led_radio(local, hw->conf.radio_enabled);
|
||||
}
|
||||
|
||||
/* add interfaces */
|
||||
list_for_each_entry(sdata, &local->interfaces, list) {
|
||||
if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
|
||||
sdata->vif.type != NL80211_IFTYPE_MONITOR &&
|
||||
netif_running(sdata->dev)) {
|
||||
conf.vif = &sdata->vif;
|
||||
conf.type = sdata->vif.type;
|
||||
conf.mac_addr = sdata->dev->dev_addr;
|
||||
res = local->ops->add_interface(hw, &conf);
|
||||
}
|
||||
}
|
||||
|
||||
/* add STAs back */
|
||||
if (local->ops->sta_notify) {
|
||||
spin_lock_irqsave(&local->sta_lock, flags);
|
||||
list_for_each_entry(sta, &local->sta_list, list) {
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
|
||||
sdata = container_of(sdata->bss,
|
||||
struct ieee80211_sub_if_data,
|
||||
u.ap);
|
||||
|
||||
local->ops->sta_notify(hw, &sdata->vif,
|
||||
STA_NOTIFY_ADD, &sta->sta);
|
||||
}
|
||||
spin_unlock_irqrestore(&local->sta_lock, flags);
|
||||
}
|
||||
|
||||
/* Clear Suspend state so that ADDBA requests can be processed */
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
|
||||
list_for_each_entry_rcu(sta, &local->sta_list, list) {
|
||||
clear_sta_flags(sta, WLAN_STA_SUSPEND);
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
/* add back keys */
|
||||
list_for_each_entry(sdata, &local->interfaces, list)
|
||||
if (netif_running(sdata->dev))
|
||||
ieee80211_enable_keys(sdata);
|
||||
|
||||
/* setup RTS threshold */
|
||||
if (local->ops->set_rts_threshold)
|
||||
local->ops->set_rts_threshold(hw, local->rts_threshold);
|
||||
|
||||
/* reconfigure hardware */
|
||||
ieee80211_hw_config(local, ~0);
|
||||
|
||||
netif_addr_lock_bh(local->mdev);
|
||||
ieee80211_configure_filter(local);
|
||||
netif_addr_unlock_bh(local->mdev);
|
||||
|
||||
/* Finally also reconfigure all the BSS information */
|
||||
list_for_each_entry(sdata, &local->interfaces, list) {
|
||||
u32 changed = ~0;
|
||||
if (!netif_running(sdata->dev))
|
||||
continue;
|
||||
switch (sdata->vif.type) {
|
||||
case NL80211_IFTYPE_STATION:
|
||||
/* disable beacon change bits */
|
||||
changed &= ~IEEE80211_IFCC_BEACON;
|
||||
/* fall through */
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
case NL80211_IFTYPE_AP:
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
/*
|
||||
* Driver's config_interface can fail if rfkill is
|
||||
* enabled. Accommodate this return code.
|
||||
* FIXME: When mac80211 has knowledge of rfkill
|
||||
* state the code below can change back to:
|
||||
* WARN(ieee80211_if_config(sdata, changed));
|
||||
* ieee80211_bss_info_change_notify(sdata, ~0);
|
||||
*/
|
||||
if (ieee80211_if_config(sdata, changed))
|
||||
printk(KERN_DEBUG "%s: failed to configure interface during resume\n",
|
||||
sdata->dev->name);
|
||||
else
|
||||
ieee80211_bss_info_change_notify(sdata, ~0);
|
||||
break;
|
||||
case NL80211_IFTYPE_WDS:
|
||||
break;
|
||||
case NL80211_IFTYPE_AP_VLAN:
|
||||
case NL80211_IFTYPE_MONITOR:
|
||||
/* ignore virtual */
|
||||
break;
|
||||
case NL80211_IFTYPE_UNSPECIFIED:
|
||||
case __NL80211_IFTYPE_AFTER_LAST:
|
||||
WARN_ON(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ieee80211_wake_queues_by_reason(hw,
|
||||
IEEE80211_QUEUE_STOP_REASON_SUSPEND);
|
||||
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* __ieee80211_resume() is a static inline which just calls
|
||||
* ieee80211_reconfig(), which is also needed for hardware
|
||||
* hang/firmware failure/etc. recovery.
|
||||
*/
|
||||
|
@@ -1932,7 +1932,7 @@ static void ieee80211_rx_michael_mic_report(struct net_device *dev,
|
||||
!ieee80211_is_auth(hdr->frame_control))
|
||||
goto ignore;
|
||||
|
||||
mac80211_ev_michael_mic_failure(rx->sdata, keyidx, hdr);
|
||||
mac80211_ev_michael_mic_failure(rx->sdata, keyidx, hdr, NULL);
|
||||
ignore:
|
||||
dev_kfree_skb(rx->skb);
|
||||
rx->skb = NULL;
|
||||
|
@@ -253,7 +253,7 @@ static void ieee80211_scan_ps_disable(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
|
||||
if (!local->powersave)
|
||||
if (!local->ps_sdata)
|
||||
ieee80211_send_nullfunc(local, sdata, 0);
|
||||
else {
|
||||
/*
|
||||
@@ -285,12 +285,16 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
|
||||
if (WARN_ON(!local->scan_req))
|
||||
return;
|
||||
|
||||
if (local->hw_scanning) {
|
||||
kfree(local->scan_req->ie);
|
||||
local->scan_req->ie = local->orig_ies;
|
||||
local->scan_req->ie_len = local->orig_ies_len;
|
||||
}
|
||||
|
||||
if (local->scan_req != &local->int_scan_req)
|
||||
cfg80211_scan_done(local->scan_req, aborted);
|
||||
local->scan_req = NULL;
|
||||
|
||||
local->last_scan_completed = jiffies;
|
||||
|
||||
if (local->hw_scanning) {
|
||||
local->hw_scanning = false;
|
||||
/*
|
||||
@@ -457,12 +461,28 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata,
|
||||
}
|
||||
|
||||
if (local->ops->hw_scan) {
|
||||
int rc;
|
||||
u8 *ies;
|
||||
int rc, ielen;
|
||||
|
||||
ies = kmalloc(2 + IEEE80211_MAX_SSID_LEN +
|
||||
local->scan_ies_len + req->ie_len, GFP_KERNEL);
|
||||
if (!ies)
|
||||
return -ENOMEM;
|
||||
|
||||
ielen = ieee80211_build_preq_ies(local, ies,
|
||||
req->ie, req->ie_len);
|
||||
local->orig_ies = req->ie;
|
||||
local->orig_ies_len = req->ie_len;
|
||||
req->ie = ies;
|
||||
req->ie_len = ielen;
|
||||
|
||||
local->hw_scanning = true;
|
||||
rc = local->ops->hw_scan(local_to_hw(local), req);
|
||||
if (rc) {
|
||||
local->hw_scanning = false;
|
||||
kfree(ies);
|
||||
req->ie_len = local->orig_ies_len;
|
||||
req->ie = local->orig_ies;
|
||||
return rc;
|
||||
}
|
||||
local->scan_sdata = scan_sdata;
|
||||
|
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/ieee80211.h>
|
||||
#include <net/wireless.h>
|
||||
#include <net/cfg80211.h>
|
||||
#include <net/mac80211.h>
|
||||
#include "ieee80211_i.h"
|
||||
#include "sta_info.h"
|
||||
|
@@ -686,41 +686,10 @@ static void sta_info_debugfs_add_work(struct work_struct *work)
|
||||
}
|
||||
#endif
|
||||
|
||||
static void __ieee80211_run_pending_flush(struct ieee80211_local *local)
|
||||
{
|
||||
struct sta_info *sta;
|
||||
unsigned long flags;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
spin_lock_irqsave(&local->sta_lock, flags);
|
||||
while (!list_empty(&local->sta_flush_list)) {
|
||||
sta = list_first_entry(&local->sta_flush_list,
|
||||
struct sta_info, list);
|
||||
list_del(&sta->list);
|
||||
spin_unlock_irqrestore(&local->sta_lock, flags);
|
||||
sta_info_destroy(sta);
|
||||
spin_lock_irqsave(&local->sta_lock, flags);
|
||||
}
|
||||
spin_unlock_irqrestore(&local->sta_lock, flags);
|
||||
}
|
||||
|
||||
static void ieee80211_sta_flush_work(struct work_struct *work)
|
||||
{
|
||||
struct ieee80211_local *local =
|
||||
container_of(work, struct ieee80211_local, sta_flush_work);
|
||||
|
||||
rtnl_lock();
|
||||
__ieee80211_run_pending_flush(local);
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
void sta_info_init(struct ieee80211_local *local)
|
||||
{
|
||||
spin_lock_init(&local->sta_lock);
|
||||
INIT_LIST_HEAD(&local->sta_list);
|
||||
INIT_LIST_HEAD(&local->sta_flush_list);
|
||||
INIT_WORK(&local->sta_flush_work, ieee80211_sta_flush_work);
|
||||
|
||||
setup_timer(&local->sta_cleanup, sta_info_cleanup,
|
||||
(unsigned long)local);
|
||||
@@ -741,7 +710,6 @@ int sta_info_start(struct ieee80211_local *local)
|
||||
void sta_info_stop(struct ieee80211_local *local)
|
||||
{
|
||||
del_timer(&local->sta_cleanup);
|
||||
cancel_work_sync(&local->sta_flush_work);
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
/*
|
||||
* Make sure the debugfs adding work isn't pending after this
|
||||
@@ -752,10 +720,7 @@ void sta_info_stop(struct ieee80211_local *local)
|
||||
cancel_work_sync(&local->sta_debugfs_add);
|
||||
#endif
|
||||
|
||||
rtnl_lock();
|
||||
sta_info_flush(local, NULL);
|
||||
__ieee80211_run_pending_flush(local);
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -767,7 +732,7 @@ void sta_info_stop(struct ieee80211_local *local)
|
||||
* @sdata: matching rule for the net device (sta->dev) or %NULL to match all STAs
|
||||
*/
|
||||
int sta_info_flush(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct sta_info *sta, *tmp;
|
||||
LIST_HEAD(tmp_list);
|
||||
@@ -775,7 +740,6 @@ int sta_info_flush(struct ieee80211_local *local,
|
||||
unsigned long flags;
|
||||
|
||||
might_sleep();
|
||||
ASSERT_RTNL();
|
||||
|
||||
spin_lock_irqsave(&local->sta_lock, flags);
|
||||
list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
|
||||
@@ -795,39 +759,6 @@ int sta_info_flush(struct ieee80211_local *local,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* sta_info_flush_delayed - flush matching STA entries from the STA table
|
||||
*
|
||||
* This function unlinks all stations for a given interface and queues
|
||||
* them for freeing. Note that the workqueue function scheduled here has
|
||||
* to run before any new keys can be added to the system to avoid set_key()
|
||||
* callback ordering issues.
|
||||
*
|
||||
* @sdata: the interface
|
||||
*/
|
||||
void sta_info_flush_delayed(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct sta_info *sta, *tmp;
|
||||
unsigned long flags;
|
||||
bool work = false;
|
||||
|
||||
spin_lock_irqsave(&local->sta_lock, flags);
|
||||
list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
|
||||
if (sdata == sta->sdata) {
|
||||
__sta_info_unlink(&sta);
|
||||
if (sta) {
|
||||
list_add_tail(&sta->list,
|
||||
&local->sta_flush_list);
|
||||
work = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (work)
|
||||
schedule_work(&local->sta_flush_work);
|
||||
spin_unlock_irqrestore(&local->sta_lock, flags);
|
||||
}
|
||||
|
||||
void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
|
||||
unsigned long exp_time)
|
||||
{
|
||||
|
@@ -442,8 +442,7 @@ void sta_info_init(struct ieee80211_local *local);
|
||||
int sta_info_start(struct ieee80211_local *local);
|
||||
void sta_info_stop(struct ieee80211_local *local);
|
||||
int sta_info_flush(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata);
|
||||
void sta_info_flush_delayed(struct ieee80211_sub_if_data *sdata);
|
||||
struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
|
||||
unsigned long exp_time);
|
||||
|
||||
|
@@ -409,8 +409,24 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
|
||||
sta->sta.addr);
|
||||
}
|
||||
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
|
||||
clear_sta_flags(sta, WLAN_STA_PSPOLL);
|
||||
if (test_and_clear_sta_flags(sta, WLAN_STA_PSPOLL)) {
|
||||
/*
|
||||
* The sleeping station with pending data is now snoozing.
|
||||
* It queried us for its buffered frames and will go back
|
||||
* to deep sleep once it got everything.
|
||||
*
|
||||
* inform the driver, in case the hardware does powersave
|
||||
* frame filtering and keeps a station blacklist on its own
|
||||
* (e.g: p54), so that frames can be delivered unimpeded.
|
||||
*
|
||||
* Note: It should be save to disable the filter now.
|
||||
* As, it is really unlikely that we still have any pending
|
||||
* frame for this station in the hw's buffers/fifos left,
|
||||
* that is not rejected with a unsuccessful tx_status yet.
|
||||
*/
|
||||
|
||||
info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT;
|
||||
}
|
||||
return TX_CONTINUE;
|
||||
}
|
||||
|
||||
@@ -429,7 +445,7 @@ ieee80211_tx_h_ps_buf(struct ieee80211_tx_data *tx)
|
||||
static ieee80211_tx_result debug_noinline
|
||||
ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
|
||||
{
|
||||
struct ieee80211_key *key;
|
||||
struct ieee80211_key *key = NULL;
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
|
||||
|
||||
@@ -500,7 +516,7 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
|
||||
sband = tx->local->hw.wiphy->bands[tx->channel->band];
|
||||
|
||||
len = min_t(int, tx->skb->len + FCS_LEN,
|
||||
tx->local->fragmentation_threshold);
|
||||
tx->local->hw.wiphy->frag_threshold);
|
||||
|
||||
/* set up the tx rate control struct we give the RC algo */
|
||||
txrc.hw = local_to_hw(tx->local);
|
||||
@@ -511,8 +527,7 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
|
||||
txrc.max_rate_idx = tx->sdata->max_ratectrl_rateidx;
|
||||
|
||||
/* set up RTS protection if desired */
|
||||
if (tx->local->rts_threshold < IEEE80211_MAX_RTS_THRESHOLD &&
|
||||
len > tx->local->rts_threshold) {
|
||||
if (len > tx->local->hw.wiphy->rts_threshold) {
|
||||
txrc.rts = rts = true;
|
||||
}
|
||||
|
||||
@@ -754,7 +769,7 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
|
||||
struct sk_buff *skb = tx->skb;
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
struct ieee80211_hdr *hdr = (void *)skb->data;
|
||||
int frag_threshold = tx->local->fragmentation_threshold;
|
||||
int frag_threshold = tx->local->hw.wiphy->frag_threshold;
|
||||
int hdrlen;
|
||||
int fragnum;
|
||||
|
||||
@@ -1072,7 +1087,7 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
|
||||
|
||||
if (tx->flags & IEEE80211_TX_FRAGMENTED) {
|
||||
if ((tx->flags & IEEE80211_TX_UNICAST) &&
|
||||
skb->len + FCS_LEN > local->fragmentation_threshold &&
|
||||
skb->len + FCS_LEN > local->hw.wiphy->frag_threshold &&
|
||||
!(info->flags & IEEE80211_TX_CTL_AMPDU))
|
||||
tx->flags |= IEEE80211_TX_FRAGMENTED;
|
||||
else
|
||||
@@ -2086,18 +2101,18 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
|
||||
} else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
struct ieee80211_hdr *hdr;
|
||||
struct sk_buff *presp = rcu_dereference(ifibss->presp);
|
||||
|
||||
if (!ifibss->probe_resp)
|
||||
if (!presp)
|
||||
goto out;
|
||||
|
||||
skb = skb_copy(ifibss->probe_resp, GFP_ATOMIC);
|
||||
skb = skb_copy(presp, GFP_ATOMIC);
|
||||
if (!skb)
|
||||
goto out;
|
||||
|
||||
hdr = (struct ieee80211_hdr *) skb->data;
|
||||
hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
|
||||
IEEE80211_STYPE_BEACON);
|
||||
|
||||
} else if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
u8 *pos;
|
||||
|
@@ -20,6 +20,7 @@
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/wireless.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <net/net_namespace.h>
|
||||
#include <net/cfg80211.h>
|
||||
#include <net/rtnetlink.h>
|
||||
@@ -28,6 +29,7 @@
|
||||
#include "rate.h"
|
||||
#include "mesh.h"
|
||||
#include "wme.h"
|
||||
#include "led.h"
|
||||
|
||||
/* privid for wiphys to determine whether they belong to us or not */
|
||||
void *mac80211_wiphy_privid = &mac80211_wiphy_privid;
|
||||
@@ -535,9 +537,17 @@ EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
|
||||
|
||||
void ieee802_11_parse_elems(u8 *start, size_t len,
|
||||
struct ieee802_11_elems *elems)
|
||||
{
|
||||
ieee802_11_parse_elems_crc(start, len, elems, 0, 0);
|
||||
}
|
||||
|
||||
u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
|
||||
struct ieee802_11_elems *elems,
|
||||
u64 filter, u32 crc)
|
||||
{
|
||||
size_t left = len;
|
||||
u8 *pos = start;
|
||||
bool calc_crc = filter != 0;
|
||||
|
||||
memset(elems, 0, sizeof(*elems));
|
||||
elems->ie_start = start;
|
||||
@@ -551,7 +561,10 @@ void ieee802_11_parse_elems(u8 *start, size_t len,
|
||||
left -= 2;
|
||||
|
||||
if (elen > left)
|
||||
return;
|
||||
break;
|
||||
|
||||
if (calc_crc && id < 64 && (filter & BIT(id)))
|
||||
crc = crc32_be(crc, pos - 2, elen + 2);
|
||||
|
||||
switch (id) {
|
||||
case WLAN_EID_SSID:
|
||||
@@ -575,8 +588,10 @@ void ieee802_11_parse_elems(u8 *start, size_t len,
|
||||
elems->cf_params_len = elen;
|
||||
break;
|
||||
case WLAN_EID_TIM:
|
||||
elems->tim = pos;
|
||||
elems->tim_len = elen;
|
||||
if (elen >= sizeof(struct ieee80211_tim_ie)) {
|
||||
elems->tim = (void *)pos;
|
||||
elems->tim_len = elen;
|
||||
}
|
||||
break;
|
||||
case WLAN_EID_IBSS_PARAMS:
|
||||
elems->ibss_params = pos;
|
||||
@@ -586,15 +601,20 @@ void ieee802_11_parse_elems(u8 *start, size_t len,
|
||||
elems->challenge = pos;
|
||||
elems->challenge_len = elen;
|
||||
break;
|
||||
case WLAN_EID_WPA:
|
||||
case WLAN_EID_VENDOR_SPECIFIC:
|
||||
if (elen >= 4 && pos[0] == 0x00 && pos[1] == 0x50 &&
|
||||
pos[2] == 0xf2) {
|
||||
/* Microsoft OUI (00:50:F2) */
|
||||
|
||||
if (calc_crc)
|
||||
crc = crc32_be(crc, pos - 2, elen + 2);
|
||||
|
||||
if (pos[3] == 1) {
|
||||
/* OUI Type 1 - WPA IE */
|
||||
elems->wpa = pos;
|
||||
elems->wpa_len = elen;
|
||||
} else if (elen >= 5 && pos[3] == 2) {
|
||||
/* OUI Type 2 - WMM IE */
|
||||
if (pos[4] == 0) {
|
||||
elems->wmm_info = pos;
|
||||
elems->wmm_info_len = elen;
|
||||
@@ -679,6 +699,8 @@ void ieee802_11_parse_elems(u8 *start, size_t len,
|
||||
left -= elen;
|
||||
pos += elen;
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata)
|
||||
@@ -831,16 +853,73 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
|
||||
ieee80211_tx_skb(sdata, skb, encrypt);
|
||||
}
|
||||
|
||||
int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
|
||||
const u8 *ie, size_t ie_len)
|
||||
{
|
||||
struct ieee80211_supported_band *sband;
|
||||
u8 *pos, *supp_rates_len, *esupp_rates_len = NULL;
|
||||
int i;
|
||||
|
||||
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
|
||||
|
||||
pos = buffer;
|
||||
|
||||
*pos++ = WLAN_EID_SUPP_RATES;
|
||||
supp_rates_len = pos;
|
||||
*pos++ = 0;
|
||||
|
||||
for (i = 0; i < sband->n_bitrates; i++) {
|
||||
struct ieee80211_rate *rate = &sband->bitrates[i];
|
||||
|
||||
if (esupp_rates_len) {
|
||||
*esupp_rates_len += 1;
|
||||
} else if (*supp_rates_len == 8) {
|
||||
*pos++ = WLAN_EID_EXT_SUPP_RATES;
|
||||
esupp_rates_len = pos;
|
||||
*pos++ = 1;
|
||||
} else
|
||||
*supp_rates_len += 1;
|
||||
|
||||
*pos++ = rate->bitrate / 5;
|
||||
}
|
||||
|
||||
if (sband->ht_cap.ht_supported) {
|
||||
__le16 tmp = cpu_to_le16(sband->ht_cap.cap);
|
||||
|
||||
*pos++ = WLAN_EID_HT_CAPABILITY;
|
||||
*pos++ = sizeof(struct ieee80211_ht_cap);
|
||||
memset(pos, 0, sizeof(struct ieee80211_ht_cap));
|
||||
memcpy(pos, &tmp, sizeof(u16));
|
||||
pos += sizeof(u16);
|
||||
/* TODO: needs a define here for << 2 */
|
||||
*pos++ = sband->ht_cap.ampdu_factor |
|
||||
(sband->ht_cap.ampdu_density << 2);
|
||||
memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
|
||||
pos += sizeof(sband->ht_cap.mcs);
|
||||
pos += 2 + 4 + 1; /* ext info, BF cap, antsel */
|
||||
}
|
||||
|
||||
/*
|
||||
* If adding more here, adjust code in main.c
|
||||
* that calculates local->scan_ies_len.
|
||||
*/
|
||||
|
||||
if (ie) {
|
||||
memcpy(pos, ie, ie_len);
|
||||
pos += ie_len;
|
||||
}
|
||||
|
||||
return pos - buffer;
|
||||
}
|
||||
|
||||
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)
|
||||
const u8 *ssid, size_t ssid_len,
|
||||
const 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;
|
||||
int i;
|
||||
u8 *pos;
|
||||
|
||||
skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 +
|
||||
ie_len);
|
||||
@@ -867,31 +946,9 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
|
||||
*pos++ = WLAN_EID_SSID;
|
||||
*pos++ = ssid_len;
|
||||
memcpy(pos, ssid, ssid_len);
|
||||
pos += 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);
|
||||
skb_put(skb, ieee80211_build_preq_ies(local, pos, ie, ie_len));
|
||||
|
||||
ieee80211_tx_skb(sdata, skb, 0);
|
||||
}
|
||||
@@ -931,3 +988,120 @@ u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
|
||||
}
|
||||
return supp_rates;
|
||||
}
|
||||
|
||||
int ieee80211_reconfig(struct ieee80211_local *local)
|
||||
{
|
||||
struct ieee80211_hw *hw = &local->hw;
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
struct ieee80211_if_init_conf conf;
|
||||
struct sta_info *sta;
|
||||
unsigned long flags;
|
||||
int res;
|
||||
|
||||
/* restart hardware */
|
||||
if (local->open_count) {
|
||||
res = local->ops->start(hw);
|
||||
|
||||
ieee80211_led_radio(local, hw->conf.radio_enabled);
|
||||
}
|
||||
|
||||
/* add interfaces */
|
||||
list_for_each_entry(sdata, &local->interfaces, list) {
|
||||
if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
|
||||
sdata->vif.type != NL80211_IFTYPE_MONITOR &&
|
||||
netif_running(sdata->dev)) {
|
||||
conf.vif = &sdata->vif;
|
||||
conf.type = sdata->vif.type;
|
||||
conf.mac_addr = sdata->dev->dev_addr;
|
||||
res = local->ops->add_interface(hw, &conf);
|
||||
}
|
||||
}
|
||||
|
||||
/* add STAs back */
|
||||
if (local->ops->sta_notify) {
|
||||
spin_lock_irqsave(&local->sta_lock, flags);
|
||||
list_for_each_entry(sta, &local->sta_list, list) {
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
|
||||
sdata = container_of(sdata->bss,
|
||||
struct ieee80211_sub_if_data,
|
||||
u.ap);
|
||||
|
||||
local->ops->sta_notify(hw, &sdata->vif,
|
||||
STA_NOTIFY_ADD, &sta->sta);
|
||||
}
|
||||
spin_unlock_irqrestore(&local->sta_lock, flags);
|
||||
}
|
||||
|
||||
/* Clear Suspend state so that ADDBA requests can be processed */
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
|
||||
list_for_each_entry_rcu(sta, &local->sta_list, list) {
|
||||
clear_sta_flags(sta, WLAN_STA_SUSPEND);
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
/* setup RTS threshold */
|
||||
if (local->ops->set_rts_threshold)
|
||||
local->ops->set_rts_threshold(hw, hw->wiphy->rts_threshold);
|
||||
|
||||
/* reconfigure hardware */
|
||||
ieee80211_hw_config(local, ~0);
|
||||
|
||||
netif_addr_lock_bh(local->mdev);
|
||||
ieee80211_configure_filter(local);
|
||||
netif_addr_unlock_bh(local->mdev);
|
||||
|
||||
/* Finally also reconfigure all the BSS information */
|
||||
list_for_each_entry(sdata, &local->interfaces, list) {
|
||||
u32 changed = ~0;
|
||||
if (!netif_running(sdata->dev))
|
||||
continue;
|
||||
switch (sdata->vif.type) {
|
||||
case NL80211_IFTYPE_STATION:
|
||||
/* disable beacon change bits */
|
||||
changed &= ~IEEE80211_IFCC_BEACON;
|
||||
/* fall through */
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
case NL80211_IFTYPE_AP:
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
/*
|
||||
* Driver's config_interface can fail if rfkill is
|
||||
* enabled. Accommodate this return code.
|
||||
* FIXME: When mac80211 has knowledge of rfkill
|
||||
* state the code below can change back to:
|
||||
* WARN(ieee80211_if_config(sdata, changed));
|
||||
* ieee80211_bss_info_change_notify(sdata, ~0);
|
||||
*/
|
||||
if (ieee80211_if_config(sdata, changed))
|
||||
printk(KERN_DEBUG "%s: failed to configure interface during resume\n",
|
||||
sdata->dev->name);
|
||||
else
|
||||
ieee80211_bss_info_change_notify(sdata, ~0);
|
||||
break;
|
||||
case NL80211_IFTYPE_WDS:
|
||||
break;
|
||||
case NL80211_IFTYPE_AP_VLAN:
|
||||
case NL80211_IFTYPE_MONITOR:
|
||||
/* ignore virtual */
|
||||
break;
|
||||
case NL80211_IFTYPE_UNSPECIFIED:
|
||||
case __NL80211_IFTYPE_AFTER_LAST:
|
||||
WARN_ON(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* add back keys */
|
||||
list_for_each_entry(sdata, &local->interfaces, list)
|
||||
if (netif_running(sdata->dev))
|
||||
ieee80211_enable_keys(sdata);
|
||||
|
||||
ieee80211_wake_queues_by_reason(hw,
|
||||
IEEE80211_QUEUE_STOP_REASON_SUSPEND);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -149,17 +149,14 @@ static int ieee80211_ioctl_siwfreq(struct net_device *dev,
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
|
||||
sdata->u.ibss.flags &= ~IEEE80211_IBSS_AUTO_CHANNEL_SEL;
|
||||
return cfg80211_ibss_wext_siwfreq(dev, info, freq, extra);
|
||||
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->u.ibss.flags |=
|
||||
IEEE80211_IBSS_AUTO_CHANNEL_SEL;
|
||||
else if (sdata->vif.type == NL80211_IFTYPE_STATION)
|
||||
if (sdata->vif.type == NL80211_IFTYPE_STATION)
|
||||
sdata->u.mgd.flags |=
|
||||
IEEE80211_STA_AUTO_CHANNEL_SEL;
|
||||
return 0;
|
||||
@@ -183,6 +180,10 @@ static int ieee80211_ioctl_giwfreq(struct net_device *dev,
|
||||
struct iw_freq *freq, char *extra)
|
||||
{
|
||||
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
|
||||
return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra);
|
||||
|
||||
freq->m = local->hw.conf.channel->center_freq;
|
||||
freq->e = 6;
|
||||
@@ -195,15 +196,17 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_point *data, char *ssid)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
size_t len = data->length;
|
||||
int ret;
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
|
||||
return cfg80211_ibss_wext_siwessid(dev, info, data, ssid);
|
||||
|
||||
/* 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) {
|
||||
if (data->flags)
|
||||
sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL;
|
||||
@@ -217,8 +220,7 @@ static int ieee80211_ioctl_siwessid(struct net_device *dev,
|
||||
sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME;
|
||||
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;
|
||||
}
|
||||
@@ -229,9 +231,13 @@ static int ieee80211_ioctl_giwessid(struct net_device *dev,
|
||||
struct iw_point *data, char *ssid)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
|
||||
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
|
||||
return cfg80211_ibss_wext_giwessid(dev, info, data, ssid);
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
|
||||
int res = ieee80211_sta_get_ssid(sdata, ssid, &len);
|
||||
if (res == 0) {
|
||||
@@ -240,14 +246,6 @@ 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;
|
||||
@@ -258,9 +256,11 @@ static int ieee80211_ioctl_siwap(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct sockaddr *ap_addr, char *extra)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
|
||||
return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra);
|
||||
|
||||
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
|
||||
int ret;
|
||||
|
||||
@@ -277,16 +277,6 @@ static int ieee80211_ioctl_siwap(struct net_device *dev,
|
||||
sdata->u.mgd.flags &= ~IEEE80211_STA_EXT_SME;
|
||||
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
|
||||
@@ -312,9 +302,11 @@ static int ieee80211_ioctl_giwap(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct sockaddr *ap_addr, char *extra)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
|
||||
return cfg80211_ibss_wext_giwap(dev, info, ap_addr, extra);
|
||||
|
||||
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
|
||||
if (sdata->u.mgd.state == IEEE80211_STA_MLME_ASSOCIATED) {
|
||||
ap_addr->sa_family = ARPHRD_ETHER;
|
||||
@@ -322,13 +314,6 @@ static int ieee80211_ioctl_giwap(struct net_device *dev,
|
||||
} else
|
||||
memset(&ap_addr->sa_data, 0, ETH_ALEN);
|
||||
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);
|
||||
@@ -487,155 +472,6 @@ static int ieee80211_ioctl_giwtxpower(struct net_device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ieee80211_ioctl_siwrts(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_param *rts, char *extra)
|
||||
{
|
||||
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
||||
|
||||
if (rts->disabled)
|
||||
local->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
|
||||
else if (!rts->fixed)
|
||||
/* if the rts value is not fixed, then take default */
|
||||
local->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
|
||||
else if (rts->value < 0 || rts->value > IEEE80211_MAX_RTS_THRESHOLD)
|
||||
return -EINVAL;
|
||||
else
|
||||
local->rts_threshold = rts->value;
|
||||
|
||||
/* If the wlan card performs RTS/CTS in hardware/firmware,
|
||||
* configure it here */
|
||||
|
||||
if (local->ops->set_rts_threshold)
|
||||
local->ops->set_rts_threshold(local_to_hw(local),
|
||||
local->rts_threshold);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ieee80211_ioctl_giwrts(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_param *rts, char *extra)
|
||||
{
|
||||
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
||||
|
||||
rts->value = local->rts_threshold;
|
||||
rts->disabled = (rts->value >= IEEE80211_MAX_RTS_THRESHOLD);
|
||||
rts->fixed = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int ieee80211_ioctl_siwfrag(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_param *frag, char *extra)
|
||||
{
|
||||
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
||||
|
||||
if (frag->disabled)
|
||||
local->fragmentation_threshold = IEEE80211_MAX_FRAG_THRESHOLD;
|
||||
else if (!frag->fixed)
|
||||
local->fragmentation_threshold = IEEE80211_MAX_FRAG_THRESHOLD;
|
||||
else if (frag->value < 256 ||
|
||||
frag->value > IEEE80211_MAX_FRAG_THRESHOLD)
|
||||
return -EINVAL;
|
||||
else {
|
||||
/* Fragment length must be even, so strip LSB. */
|
||||
local->fragmentation_threshold = frag->value & ~0x1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ieee80211_ioctl_giwfrag(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_param *frag, char *extra)
|
||||
{
|
||||
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
||||
|
||||
frag->value = local->fragmentation_threshold;
|
||||
frag->disabled = (frag->value >= IEEE80211_MAX_FRAG_THRESHOLD);
|
||||
frag->fixed = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int ieee80211_ioctl_siwretry(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_param *retry, char *extra)
|
||||
{
|
||||
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
||||
|
||||
if (retry->disabled ||
|
||||
(retry->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT)
|
||||
return -EINVAL;
|
||||
|
||||
if (retry->flags & IW_RETRY_MAX) {
|
||||
local->hw.conf.long_frame_max_tx_count = retry->value;
|
||||
} else if (retry->flags & IW_RETRY_MIN) {
|
||||
local->hw.conf.short_frame_max_tx_count = retry->value;
|
||||
} else {
|
||||
local->hw.conf.long_frame_max_tx_count = retry->value;
|
||||
local->hw.conf.short_frame_max_tx_count = retry->value;
|
||||
}
|
||||
|
||||
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_RETRY_LIMITS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int ieee80211_ioctl_giwretry(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_param *retry, char *extra)
|
||||
{
|
||||
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
||||
|
||||
retry->disabled = 0;
|
||||
if (retry->flags == 0 || retry->flags & IW_RETRY_MIN) {
|
||||
/* first return min value, iwconfig will ask max value
|
||||
* later if needed */
|
||||
retry->flags |= IW_RETRY_LIMIT;
|
||||
retry->value = local->hw.conf.short_frame_max_tx_count;
|
||||
if (local->hw.conf.long_frame_max_tx_count !=
|
||||
local->hw.conf.short_frame_max_tx_count)
|
||||
retry->flags |= IW_RETRY_MIN;
|
||||
return 0;
|
||||
}
|
||||
if (retry->flags & IW_RETRY_MAX) {
|
||||
retry->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
|
||||
retry->value = local->hw.conf.long_frame_max_tx_count;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ieee80211_ioctl_siwmlme(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_point *data, char *extra)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
struct iw_mlme *mlme = (struct iw_mlme *) extra;
|
||||
|
||||
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
if (!(sdata->vif.type == NL80211_IFTYPE_STATION))
|
||||
return -EINVAL;
|
||||
|
||||
switch (mlme->cmd) {
|
||||
case IW_MLME_DEAUTH:
|
||||
/* TODO: mlme->addr.sa_data */
|
||||
return ieee80211_sta_deauthenticate(sdata, mlme->reason_code);
|
||||
case IW_MLME_DISASSOC:
|
||||
/* TODO: mlme->addr.sa_data */
|
||||
return ieee80211_sta_disassociate(sdata, mlme->reason_code);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int ieee80211_ioctl_siwencode(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_point *erq, char *keybuf)
|
||||
@@ -675,7 +511,7 @@ static int ieee80211_ioctl_siwencode(struct net_device *dev,
|
||||
!sdata->default_key,
|
||||
keybuf, erq->length);
|
||||
|
||||
if (!ret) {
|
||||
if (!ret && sdata->vif.type == NL80211_IFTYPE_STATION) {
|
||||
if (remove)
|
||||
sdata->u.mgd.flags &= ~IEEE80211_STA_TKIP_WEP_USED;
|
||||
else
|
||||
@@ -747,7 +583,7 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev,
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
||||
struct ieee80211_conf *conf = &local->hw.conf;
|
||||
int ret = 0, timeout = 0;
|
||||
int timeout = 0;
|
||||
bool ps;
|
||||
|
||||
if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS))
|
||||
@@ -779,42 +615,18 @@ static int ieee80211_ioctl_siwpower(struct net_device *dev,
|
||||
timeout = wrq->value / 1000;
|
||||
|
||||
set:
|
||||
if (ps == local->powersave && timeout == conf->dynamic_ps_timeout)
|
||||
return ret;
|
||||
if (ps == sdata->u.mgd.powersave && timeout == conf->dynamic_ps_timeout)
|
||||
return 0;
|
||||
|
||||
local->powersave = ps;
|
||||
sdata->u.mgd.powersave = ps;
|
||||
conf->dynamic_ps_timeout = timeout;
|
||||
|
||||
if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
|
||||
ret = ieee80211_hw_config(local,
|
||||
IEEE80211_CONF_CHANGE_DYNPS_TIMEOUT);
|
||||
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
|
||||
|
||||
if (!(sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED))
|
||||
return ret;
|
||||
ieee80211_recalc_ps(local, -1);
|
||||
|
||||
if (conf->dynamic_ps_timeout > 0 &&
|
||||
!(local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)) {
|
||||
mod_timer(&local->dynamic_ps_timer, jiffies +
|
||||
msecs_to_jiffies(conf->dynamic_ps_timeout));
|
||||
} else {
|
||||
if (local->powersave) {
|
||||
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
|
||||
ieee80211_send_nullfunc(local, sdata, 1);
|
||||
conf->flags |= IEEE80211_CONF_PS;
|
||||
ret = ieee80211_hw_config(local,
|
||||
IEEE80211_CONF_CHANGE_PS);
|
||||
} else {
|
||||
conf->flags &= ~IEEE80211_CONF_PS;
|
||||
ret = ieee80211_hw_config(local,
|
||||
IEEE80211_CONF_CHANGE_PS);
|
||||
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
|
||||
ieee80211_send_nullfunc(local, sdata, 0);
|
||||
del_timer_sync(&local->dynamic_ps_timer);
|
||||
cancel_work_sync(&local->dynamic_ps_enable_work);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ieee80211_ioctl_giwpower(struct net_device *dev,
|
||||
@@ -822,9 +634,9 @@ static int ieee80211_ioctl_giwpower(struct net_device *dev,
|
||||
union iwreq_data *wrqu,
|
||||
char *extra)
|
||||
{
|
||||
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
|
||||
wrqu->power.disabled = !local->powersave;
|
||||
wrqu->power.disabled = !sdata->u.mgd.powersave;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1099,7 +911,7 @@ static const iw_handler ieee80211_handler[] =
|
||||
(iw_handler) NULL, /* SIOCGIWTHRSPY */
|
||||
(iw_handler) ieee80211_ioctl_siwap, /* SIOCSIWAP */
|
||||
(iw_handler) ieee80211_ioctl_giwap, /* SIOCGIWAP */
|
||||
(iw_handler) ieee80211_ioctl_siwmlme, /* SIOCSIWMLME */
|
||||
(iw_handler) cfg80211_wext_siwmlme, /* SIOCSIWMLME */
|
||||
(iw_handler) NULL, /* SIOCGIWAPLIST */
|
||||
(iw_handler) cfg80211_wext_siwscan, /* SIOCSIWSCAN */
|
||||
(iw_handler) cfg80211_wext_giwscan, /* SIOCGIWSCAN */
|
||||
@@ -1111,14 +923,14 @@ static const iw_handler ieee80211_handler[] =
|
||||
(iw_handler) NULL, /* -- hole -- */
|
||||
(iw_handler) ieee80211_ioctl_siwrate, /* SIOCSIWRATE */
|
||||
(iw_handler) ieee80211_ioctl_giwrate, /* SIOCGIWRATE */
|
||||
(iw_handler) ieee80211_ioctl_siwrts, /* SIOCSIWRTS */
|
||||
(iw_handler) ieee80211_ioctl_giwrts, /* SIOCGIWRTS */
|
||||
(iw_handler) ieee80211_ioctl_siwfrag, /* SIOCSIWFRAG */
|
||||
(iw_handler) ieee80211_ioctl_giwfrag, /* SIOCGIWFRAG */
|
||||
(iw_handler) cfg80211_wext_siwrts, /* SIOCSIWRTS */
|
||||
(iw_handler) cfg80211_wext_giwrts, /* SIOCGIWRTS */
|
||||
(iw_handler) cfg80211_wext_siwfrag, /* SIOCSIWFRAG */
|
||||
(iw_handler) cfg80211_wext_giwfrag, /* SIOCGIWFRAG */
|
||||
(iw_handler) ieee80211_ioctl_siwtxpower, /* SIOCSIWTXPOW */
|
||||
(iw_handler) ieee80211_ioctl_giwtxpower, /* SIOCGIWTXPOW */
|
||||
(iw_handler) ieee80211_ioctl_siwretry, /* SIOCSIWRETRY */
|
||||
(iw_handler) ieee80211_ioctl_giwretry, /* SIOCGIWRETRY */
|
||||
(iw_handler) cfg80211_wext_siwretry, /* SIOCSIWRETRY */
|
||||
(iw_handler) cfg80211_wext_giwretry, /* SIOCGIWRETRY */
|
||||
(iw_handler) ieee80211_ioctl_siwencode, /* SIOCSIWENCODE */
|
||||
(iw_handler) ieee80211_ioctl_giwencode, /* SIOCGIWENCODE */
|
||||
(iw_handler) ieee80211_ioctl_siwpower, /* SIOCSIWPOWER */
|
||||
|
@@ -122,7 +122,7 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx)
|
||||
return RX_DROP_UNUSABLE;
|
||||
|
||||
mac80211_ev_michael_mic_failure(rx->sdata, rx->key->conf.keyidx,
|
||||
(void *) skb->data);
|
||||
(void *) skb->data, NULL);
|
||||
return RX_DROP_UNUSABLE;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user