/*
* BSS client mode implementation
- * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Copyright 2003-2008, Jouni Malinen <j@w1.fi>
* Copyright 2004, Instant802 Networks, Inc.
* Copyright 2005, Devicescape Software, Inc.
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
static int ieee80211_compatible_rates(struct ieee80211_bss *bss,
struct ieee80211_supported_band *sband,
- u64 *rates)
+ u32 *rates)
{
int i, j, count;
*rates = 0;
}
/* also used by mesh code */
-u64 ieee80211_sta_get_rates(struct ieee80211_local *local,
+u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
struct ieee802_11_elems *elems,
enum ieee80211_band band)
{
struct ieee80211_supported_band *sband;
struct ieee80211_rate *bitrates;
size_t num_rates;
- u64 supp_rates;
+ u32 supp_rates;
int i, j;
sband = local->hw.wiphy->bands[band];
/* frame sending functions */
+static void add_extra_ies(struct sk_buff *skb, u8 *ies, size_t ies_len)
+{
+ if (ies)
+ memcpy(skb_put(skb, ies_len), ies, ies_len);
+}
+
/* also used by scanning code */
void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
u8 *ssid, size_t ssid_len)
u8 *pos, *supp_rates, *esupp_rates = NULL;
int i;
- skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200);
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 +
+ sdata->u.sta.ie_probereq_len);
if (!skb) {
printk(KERN_DEBUG "%s: failed to allocate buffer for probe "
"request\n", sdata->dev->name);
*pos = rate->bitrate / 5;
}
+ add_extra_ies(skb, sdata->u.sta.ie_probereq,
+ sdata->u.sta.ie_probereq_len);
+
ieee80211_tx_skb(sdata, skb, 0);
}
struct ieee80211_mgmt *mgmt;
skb = dev_alloc_skb(local->hw.extra_tx_headroom +
- sizeof(*mgmt) + 6 + extra_len);
+ sizeof(*mgmt) + 6 + extra_len +
+ sdata->u.sta.ie_auth_len);
if (!skb) {
printk(KERN_DEBUG "%s: failed to allocate buffer for auth "
"frame\n", sdata->dev->name);
mgmt->u.auth.status_code = cpu_to_le16(0);
if (extra)
memcpy(skb_put(skb, extra_len), extra, extra_len);
+ add_extra_ies(skb, sdata->u.sta.ie_auth, sdata->u.sta.ie_auth_len);
ieee80211_tx_skb(sdata, skb, encrypt);
}
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
- u8 *pos, *ies, *ht_ie;
+ u8 *pos, *ies, *ht_ie, *e_ies;
int i, len, count, rates_len, supp_rates_len;
u16 capab;
struct ieee80211_bss *bss;
int wmm = 0;
struct ieee80211_supported_band *sband;
- u64 rates = 0;
+ u32 rates = 0;
+ size_t e_ies_len;
+
+ if (ifsta->flags & IEEE80211_STA_PREV_BSSID_SET) {
+ e_ies = sdata->u.sta.ie_reassocreq;
+ e_ies_len = sdata->u.sta.ie_reassocreq_len;
+ } else {
+ e_ies = sdata->u.sta.ie_assocreq;
+ e_ies_len = sdata->u.sta.ie_assocreq_len;
+ }
skb = dev_alloc_skb(local->hw.extra_tx_headroom +
sizeof(*mgmt) + 200 + ifsta->extra_ie_len +
- ifsta->ssid_len);
+ ifsta->ssid_len + e_ies_len);
if (!skb) {
printk(KERN_DEBUG "%s: failed to allocate buffer for assoc "
"frame\n", sdata->dev->name);
memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
}
+ add_extra_ies(skb, e_ies, e_ies_len);
+
kfree(ifsta->assocreq_ies);
ifsta->assocreq_ies_len = (skb->data + skb->len) - ies;
ifsta->assocreq_ies = kmalloc(ifsta->assocreq_ies_len, GFP_KERNEL);
struct ieee80211_if_sta *ifsta = &sdata->u.sta;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
+ u8 *ies;
+ size_t ies_len;
+
+ if (stype == IEEE80211_STYPE_DEAUTH) {
+ ies = sdata->u.sta.ie_deauth;
+ ies_len = sdata->u.sta.ie_deauth_len;
+ } else {
+ ies = sdata->u.sta.ie_disassoc;
+ ies_len = sdata->u.sta.ie_disassoc_len;
+ }
- skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt));
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) +
+ ies_len);
if (!skb) {
printk(KERN_DEBUG "%s: failed to allocate buffer for "
"deauth/disassoc frame\n", sdata->dev->name);
/* u.deauth.reason_code == u.disassoc.reason_code */
mgmt->u.deauth.reason_code = cpu_to_le16(reason);
- ieee80211_tx_skb(sdata, skb, 0);
+ add_extra_ies(skb, ies, ies_len);
+
+ ieee80211_tx_skb(sdata, skb, ifsta->flags & IEEE80211_STA_MFP_ENABLED);
}
/* MLME */
bss_info_changed |= BSS_CHANGED_BASIC_RATES;
ieee80211_bss_info_change_notify(sdata, bss_info_changed);
- if (local->powersave &&
- !(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS)) {
- if (local->dynamic_ps_timeout > 0)
+ 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->dynamic_ps_timeout));
- else {
- ieee80211_send_nullfunc(local, sdata, 1);
+ 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);
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
}
}
rcu_read_unlock();
+ /* channel(_type) changes are handled by ieee80211_hw_config */
local->oper_channel_type = NL80211_CHAN_NO_HT;
- config_changed |= IEEE80211_CONF_CHANGE_HT;
+
+ local->power_constr_level = 0;
del_timer_sync(&local->dynamic_ps_timer);
cancel_work_sync(&local->dynamic_ps_enable_work);
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
struct sta_info *sta;
- u64 rates, basic_rates;
+ u32 rates, basic_rates;
u16 capab_info, status_code, aid;
struct ieee802_11_elems elems;
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
sdata->dev->name, reassoc ? "Rea" : "A", mgmt->sa,
capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14))));
+ pos = mgmt->u.assoc_resp.variable;
+ ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
+
+ if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY &&
+ elems.timeout_int && elems.timeout_int_len == 5 &&
+ elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) {
+ u32 tu, ms;
+ tu = get_unaligned_le32(elems.timeout_int + 1);
+ ms = tu * 1024 / 1000;
+ printk(KERN_DEBUG "%s: AP rejected association temporarily; "
+ "comeback duration %u TU (%u ms)\n",
+ sdata->dev->name, tu, ms);
+ if (ms > IEEE80211_ASSOC_TIMEOUT)
+ mod_timer(&ifsta->timer,
+ jiffies + msecs_to_jiffies(ms));
+ return;
+ }
+
if (status_code != WLAN_STATUS_SUCCESS) {
printk(KERN_DEBUG "%s: AP denied association (code=%d)\n",
sdata->dev->name, status_code);
"set\n", sdata->dev->name, aid);
aid &= ~(BIT(15) | BIT(14));
- pos = mgmt->u.assoc_resp.variable;
- ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
-
if (!elems.supp_rates) {
printk(KERN_DEBUG "%s: no SuppRates element in AssocResp\n",
sdata->dev->name);
rate_control_rate_init(sta);
+ if (ifsta->flags & IEEE80211_STA_MFP_ENABLED)
+ set_sta_flags(sta, WLAN_STA_MFP);
+
if (elems.wmm_param)
set_sta_flags(sta, WLAN_STA_WME);
struct ieee80211_supported_band *sband;
union iwreq_data wrqu;
- skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400 +
+ sdata->u.sta.ie_proberesp_len);
if (!skb) {
printk(KERN_DEBUG "%s: failed to allocate buffer for probe "
"response\n", sdata->dev->name);
memcpy(pos, &bss->supp_rates[8], rates);
}
+ add_extra_ies(skb, sdata->u.sta.ie_proberesp,
+ sdata->u.sta.ie_proberesp_len);
+
ifsta->probe_resp = skb;
- ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
+ ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON |
+ IEEE80211_IFCC_BEACON_ENABLED);
rates = 0;
struct sta_info *sta;
struct ieee80211_channel *channel;
u64 beacon_timestamp, rx_timestamp;
- u64 supp_rates = 0;
+ u32 supp_rates = 0;
enum ieee80211_band band = rx_status->band;
if (elems->ds_params && elems->ds_params_len == 1)
sta = sta_info_get(local, mgmt->sa);
if (sta) {
- u64 prev_rates;
+ u32 prev_rates;
prev_rates = sta->sta.supp_rates[band];
/* make sure mandatory rates are always added */
ieee80211_sta_wmm_params(local, ifsta, elems.wmm_param,
elems.wmm_param_len);
- if (!(local->hw.flags & IEEE80211_HW_NO_STACK_DYNAMIC_PS)) {
+ if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK &&
+ local->hw.conf.flags & IEEE80211_CONF_PS) {
directed_tim = check_tim(&elems, ifsta->aid, &is_mc);
if (directed_tim || is_mc) {
- if (local->hw.conf.flags && IEEE80211_CONF_PS) {
- local->hw.conf.flags &= ~IEEE80211_CONF_PS;
- ieee80211_hw_config(local,
- IEEE80211_CONF_CHANGE_PS);
- ieee80211_send_nullfunc(local, sdata, 0);
- }
+ local->hw.conf.flags &= ~IEEE80211_CONF_PS;
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+ ieee80211_send_nullfunc(local, sdata, 0);
}
}
* for the BSSID we are associated to */
regulatory_hint_11d(local->hw.wiphy,
elems.country_elem, elems.country_elem_len);
+
+ /* TODO: IBSS also needs this */
+ if (elems.pwr_constr_elem)
+ ieee80211_handle_pwr_constr(sdata,
+ le16_to_cpu(mgmt->u.probe_resp.capab_info),
+ elems.pwr_constr_elem,
+ elems.pwr_constr_elem_len);
}
ieee80211_bss_info_change_notify(sdata, changed);
static void ieee80211_rx_mgmt_probe_req(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_sta *ifsta,
struct ieee80211_mgmt *mgmt,
- size_t len,
- struct ieee80211_rx_status *rx_status)
+ size_t len)
{
struct ieee80211_local *local = sdata->local;
int tx_last_beacon;
switch (fc & IEEE80211_FCTL_STYPE) {
case IEEE80211_STYPE_PROBE_REQ:
- ieee80211_rx_mgmt_probe_req(sdata, ifsta, mgmt, skb->len,
- rx_status);
+ ieee80211_rx_mgmt_probe_req(sdata, ifsta, mgmt, skb->len);
break;
case IEEE80211_STYPE_PROBE_RESP:
ieee80211_rx_mgmt_probe_resp(sdata, mgmt, skb->len, rx_status);
selected->ssid_len);
ieee80211_sta_set_bssid(sdata, selected->bssid);
ieee80211_sta_def_wmm_params(sdata, selected);
+ if (sdata->u.sta.mfp == IEEE80211_MFP_REQUIRED)
+ sdata->u.sta.flags |= IEEE80211_STA_MFP_ENABLED;
+ else
+ sdata->u.sta.flags &= ~IEEE80211_STA_MFP_ENABLED;
/* Send out direct probe if no probe resp was received or
* the one we have is outdated
* must be callable in atomic context.
*/
struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
- u8 *bssid,u8 *addr, u64 supp_rates)
+ u8 *bssid,u8 *addr, u32 supp_rates)
{
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
if (local->hw.conf.flags & IEEE80211_CONF_PS)
return;
- ieee80211_send_nullfunc(local, sdata, 1);
- local->hw.conf.flags |= IEEE80211_CONF_PS;
+ 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);
}