X-Git-Url: http://git.maemo.org/git/?a=blobdiff_plain;f=net%2Fmac80211%2Fmlme.c;fp=net%2Fmac80211%2Fmlme.c;h=e86b2c2bb4937fba2311f9598ecd54b47398aba9;hb=a78d411c6518eb7dd038eef6668f78a7e6c14ec4;hp=409bb771623671c8274fd0e25683665b13e53fdd;hpb=4a6908a3a050aacc9c3a2f36b276b46c0629ad91;p=h-e-n diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 409bb77..e86b2c2 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -34,7 +34,6 @@ #define IEEE80211_ASSOC_TIMEOUT (HZ / 5) #define IEEE80211_ASSOC_MAX_TRIES 3 #define IEEE80211_MONITORING_INTERVAL (2 * HZ) -#define IEEE80211_PROBE_INTERVAL (60 * HZ) #define IEEE80211_RETRY_AUTH_INTERVAL (1 * HZ) #define IEEE80211_SCAN_INTERVAL (2 * HZ) #define IEEE80211_SCAN_INTERVAL_SLOW (15 * HZ) @@ -45,6 +44,8 @@ #define IEEE80211_IBSS_MAX_STA_ENTRIES 128 +#define RSSI_HIGHSIGNAL "HIGHSIGNAL" +#define RSSI_LOWSIGNAL "LOWSIGNAL" /* utils */ static int ecw2cw(int ecw) @@ -568,9 +569,8 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local, } } -static u32 ieee80211_handle_protect_preamb(struct ieee80211_sub_if_data *sdata, - bool use_protection, - bool use_short_preamble) +static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, + u16 capab, bool erp_valid, u8 erp) { struct ieee80211_bss_conf *bss_conf = &sdata->bss_conf; #ifdef CONFIG_MAC80211_VERBOSE_DEBUG @@ -578,6 +578,18 @@ static u32 ieee80211_handle_protect_preamb(struct ieee80211_sub_if_data *sdata, DECLARE_MAC_BUF(mac); #endif u32 changed = 0; + bool use_protection; + bool use_short_preamble; + bool use_short_slot; + + if (erp_valid) { + use_protection = (erp & WLAN_ERP_USE_PROTECTION) != 0; + use_short_preamble = (erp & WLAN_ERP_BARKER_PREAMBLE) == 0; + } else { + use_protection = false; + use_short_preamble = !!(capab & WLAN_CAPABILITY_SHORT_PREAMBLE); + } + use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME); if (use_protection != bss_conf->use_cts_prot) { #ifdef CONFIG_MAC80211_VERBOSE_DEBUG @@ -607,30 +619,18 @@ static u32 ieee80211_handle_protect_preamb(struct ieee80211_sub_if_data *sdata, changed |= BSS_CHANGED_ERP_PREAMBLE; } - return changed; -} - -static u32 ieee80211_handle_erp_ie(struct ieee80211_sub_if_data *sdata, - u8 erp_value) -{ - bool use_protection = (erp_value & WLAN_ERP_USE_PROTECTION) != 0; - bool use_short_preamble = (erp_value & WLAN_ERP_BARKER_PREAMBLE) == 0; - - return ieee80211_handle_protect_preamb(sdata, - use_protection, use_short_preamble); -} - -static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, - struct ieee80211_bss *bss) -{ - u32 changed = 0; - - if (bss->has_erp_value) - changed |= ieee80211_handle_erp_ie(sdata, bss->erp_value); - else { - u16 capab = bss->capability; - changed |= ieee80211_handle_protect_preamb(sdata, false, - (capab & WLAN_CAPABILITY_SHORT_PREAMBLE) != 0); + if (use_short_slot != bss_conf->use_short_slot) { +#ifdef CONFIG_MAC80211_VERBOSE_DEBUG + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: switched to %s slot" + " (BSSID=%s)\n", + sdata->dev->name, + use_short_slot ? "short" : "long", + ifsta->bssid); + } +#endif + bss_conf->use_short_slot = use_short_slot; + changed |= BSS_CHANGED_ERP_SLOT; } return changed; @@ -723,7 +723,10 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, sdata->bss_conf.timestamp = bss->timestamp; sdata->bss_conf.dtim_period = bss->dtim_period; - changed |= ieee80211_handle_bss_capability(sdata, bss); + changed |= ieee80211_handle_bss_capability(sdata, + bss->capability, bss->has_erp_value, bss->erp_value); + + bss->hold = true; ieee80211_rx_bss_put(local, bss); } @@ -739,6 +742,9 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, memcpy(ifsta->prev_bssid, sdata->u.sta.bssid, ETH_ALEN); ieee80211_sta_send_associnfo(sdata, ifsta); + ifsta->rssi_state = IEEE80211_RSSI_STATE_HIGH; + ifsta->roam_threshold_count = 0; + ifsta->last_probe = jiffies; ieee80211_led_assoc(local, 1); @@ -751,6 +757,16 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, changed |= BSS_CHANGED_BASIC_RATES; ieee80211_bss_info_change_notify(sdata, changed); + if (local->powersave) { + if (local->dynamic_ps_timeout > 0) + mod_timer(&local->dynamic_ps_timer, jiffies + + msecs_to_jiffies(local->dynamic_ps_timeout)); + else { + conf->flags |= IEEE80211_CONF_PS; + ieee80211_hw_config(local); + } + } + netif_tx_start_all_queues(sdata->dev); netif_carrier_on(sdata->dev); @@ -767,6 +783,8 @@ static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: direct probe to AP %s timed out\n", sdata->dev->name, print_mac(mac, ifsta->bssid)); ifsta->state = IEEE80211_STA_MLME_DISABLED; + ifsta->flags &= ~IEEE80211_STA_ASSOCIATED; + ieee80211_sta_send_apinfo(sdata, ifsta); return; } @@ -799,6 +817,8 @@ static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata, " timed out\n", sdata->dev->name, print_mac(mac, ifsta->bssid)); ifsta->state = IEEE80211_STA_MLME_DISABLED; + ifsta->flags &= ~IEEE80211_STA_ASSOCIATED; + ieee80211_sta_send_apinfo(sdata, ifsta); return; } @@ -816,6 +836,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, bool self_disconnected, u16 reason) { struct ieee80211_local *local = sdata->local; + struct ieee80211_conf *conf = &local_to_hw(local)->conf; + struct ieee80211_bss *bss; struct sta_info *sta; u32 changed = BSS_CHANGED_ASSOC; @@ -839,6 +861,15 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, ieee80211_sta_tear_down_BA_sessions(sdata, sta->sta.addr); + bss = ieee80211_rx_bss_get(local, ifsta->bssid, + conf->channel->center_freq, + ifsta->ssid, ifsta->ssid_len); + + if (bss) { + bss->hold = false; + ieee80211_rx_bss_put(local, bss); + } + if (self_disconnected) { if (deauth) ieee80211_send_deauth_disassoc(sdata, @@ -870,6 +901,14 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, rcu_read_unlock(); + del_timer_sync(&local->dynamic_ps_timer); + cancel_work_sync(&local->dynamic_ps_enable_work); + + if (local->hw.conf.flags & IEEE80211_CONF_PS) { + local->hw.conf.flags &= ~IEEE80211_CONF_PS; + ieee80211_hw_config(local); + } + sta_info_destroy(sta); } @@ -922,6 +961,8 @@ static void ieee80211_associate(struct ieee80211_sub_if_data *sdata, " timed out\n", sdata->dev->name, print_mac(mac, ifsta->bssid)); ifsta->state = IEEE80211_STA_MLME_DISABLED; + ifsta->flags &= ~IEEE80211_STA_ASSOCIATED; + ieee80211_sta_send_apinfo(sdata, ifsta); return; } @@ -940,13 +981,113 @@ static void ieee80211_associate(struct ieee80211_sub_if_data *sdata, mod_timer(&ifsta->timer, jiffies + IEEE80211_ASSOC_TIMEOUT); } +void ieee80211_rssi_changed(struct ieee80211_vif *vif, + enum ieee80211_rssi_state state) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_if_sta *ifsta = NULL; + const char *event = NULL; + union iwreq_data wrqu; + char *buf = NULL; + + ifsta = &sdata->u.sta; + + if (!ifsta || ifsta->state != IEEE80211_STA_MLME_ASSOCIATED) + return; + + if (ifsta->rssi_state == state) + return; + + ifsta->rssi_state = state; + + switch (state) { + case IEEE80211_RSSI_STATE_HIGH: + event = RSSI_HIGHSIGNAL; + break; + case IEEE80211_RSSI_STATE_LOW: + event = RSSI_LOWSIGNAL; + break; + default: + WARN_ON(1); + return; + } + + buf = kstrdup(event, GFP_ATOMIC); + printk(KERN_DEBUG "%s: roaming signal from driver, sending %s\n", + sdata->dev->name, buf); + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = strlen(buf); + wireless_send_event(sdata->dev, IWEVCUSTOM, &wrqu, buf); + kfree(buf); +} +EXPORT_SYMBOL(ieee80211_rssi_changed); + +void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, + struct ieee80211_hdr *hdr) +{ + struct ieee80211_local *local = sdata->local; + + /* + * We can postpone the sta.timer whenever receiving unicast frames + * from AP because we know that the connection is working both ways + * at that time. But multicast frames (and hence also beacons) must + * be ignored here, because we need to trigger the timer during + * data idle periods for sending the periodical probe request to + * the AP. + */ + if (!is_multicast_ether_addr(hdr->addr1) && + !(local->hw.flags & IEEE80211_HW_BEACON_FILTER)) + mod_timer(&sdata->u.sta.timer, + jiffies + IEEE80211_MONITORING_INTERVAL); +} + +void ieee80211_beacon_loss_work(struct work_struct *work) +{ + struct ieee80211_sub_if_data *sdata = + container_of(work, struct ieee80211_sub_if_data, + u.sta.beacon_loss_work); + struct ieee80211_if_sta *ifsta = &sdata->u.sta; + struct ieee80211_local *local = sdata->local; + + if (ifsta->state != IEEE80211_STA_MLME_ASSOCIATED) { + printk(KERN_DEBUG "%s reports beacon loss when not " + "associated\n", sdata->dev->name); + return; + } + + printk(KERN_DEBUG "%s: driver reports beacon loss from AP %pM " + "- sending probe request\n", sdata->dev->name, + sdata->u.sta.bssid); + + ifsta->flags |= IEEE80211_STA_PROBEREQ_POLL; + + if (local->powersave) { + /* disable power save before sending the probe request */ + local->hw.conf.flags &= ~IEEE80211_CONF_PS; + ieee80211_hw_config(local); + } + + ieee80211_send_probe_req(sdata, ifsta->bssid, ifsta->ssid, + ifsta->ssid_len); + + mod_timer(&ifsta->timer, jiffies + IEEE80211_MONITORING_INTERVAL); +} + +void ieee80211_beacon_loss(struct ieee80211_vif *vif) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + + queue_work(sdata->local->hw.workqueue, + &sdata->u.sta.beacon_loss_work); +} +EXPORT_SYMBOL(ieee80211_beacon_loss); static void ieee80211_associated(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_sta *ifsta) { struct ieee80211_local *local = sdata->local; struct sta_info *sta; - int disassoc; + bool disassoc = false, send_probe = false; DECLARE_MAC_BUF(mac); /* TODO: start monitoring current AP signal quality and number of @@ -960,42 +1101,57 @@ static void ieee80211_associated(struct ieee80211_sub_if_data *sdata, sta = sta_info_get(local, ifsta->bssid); if (!sta) { - printk(KERN_DEBUG "%s: No STA entry for own AP %s\n", - sdata->dev->name, print_mac(mac, ifsta->bssid)); - disassoc = 1; - } else { - disassoc = 0; - if (time_after(jiffies, - sta->last_rx + IEEE80211_MONITORING_INTERVAL)) { - if (ifsta->flags & IEEE80211_STA_PROBEREQ_POLL) { - printk(KERN_DEBUG "%s: No ProbeResp from " - "current AP %s - assume out of " - "range\n", - sdata->dev->name, print_mac(mac, ifsta->bssid)); - disassoc = 1; - } else - ieee80211_send_probe_req(sdata, ifsta->bssid, - ifsta->ssid, - ifsta->ssid_len); - ifsta->flags ^= IEEE80211_STA_PROBEREQ_POLL; - } else { - ifsta->flags &= ~IEEE80211_STA_PROBEREQ_POLL; - if (time_after(jiffies, ifsta->last_probe + - IEEE80211_PROBE_INTERVAL)) { - ifsta->last_probe = jiffies; - ieee80211_send_probe_req(sdata, ifsta->bssid, - ifsta->ssid, - ifsta->ssid_len); - } - } + printk(KERN_DEBUG "%s: No STA entry for own AP %pM\n", + sdata->dev->name, ifsta->bssid); + disassoc = true; + goto unlock; + } + + if ((ifsta->flags & IEEE80211_STA_PROBEREQ_POLL) && + time_after(jiffies, sta->last_rx + IEEE80211_MONITORING_INTERVAL)) { + printk(KERN_DEBUG "%s: no probe response from AP %pM " + "- disassociating\n", + sdata->dev->name, ifsta->bssid); + disassoc = true; + ifsta->flags &= ~IEEE80211_STA_PROBEREQ_POLL; + goto unlock; + } + + /* + * Beacon filtering is only enabled with power save and then the + * stack should not check for beacon loss. + */ + if (!(local->hw.flags & IEEE80211_HW_BEACON_FILTER) && + time_after(jiffies, + ifsta->last_beacon + IEEE80211_MONITORING_INTERVAL)) { + printk(KERN_DEBUG "%s: beacon loss from AP %pM " + "- sending probe request\n", + sdata->dev->name, ifsta->bssid); + ifsta->flags |= IEEE80211_STA_PROBEREQ_POLL; + send_probe = true; + goto unlock; + } + unlock: rcu_read_unlock(); + /* config() can sleep so call it after unlock */ + if (send_probe) { + if (local->powersave) { + /* disable ps before sending the probe request */ + local->hw.conf.flags &= ~IEEE80211_CONF_PS; + ieee80211_hw_config(local); + } + + ieee80211_send_probe_req(sdata, ifsta->bssid, ifsta->ssid, + ifsta->ssid_len); + } + if (disassoc) ieee80211_set_disassoc(sdata, ifsta, true, true, WLAN_REASON_PREV_AUTH_NOT_VALID); - else + else if (!(local->hw.flags & IEEE80211_HW_BEACON_FILTER)) mod_timer(&ifsta->timer, jiffies + IEEE80211_MONITORING_INTERVAL); } @@ -1139,8 +1295,7 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: deauthenticated\n", sdata->dev->name); if (ifsta->state == IEEE80211_STA_MLME_AUTHENTICATE || - ifsta->state == IEEE80211_STA_MLME_ASSOCIATE || - ifsta->state == IEEE80211_STA_MLME_ASSOCIATED) { + ifsta->state == IEEE80211_STA_MLME_ASSOCIATE) { ifsta->state = IEEE80211_STA_MLME_DIRECT_PROBE; mod_timer(&ifsta->timer, jiffies + IEEE80211_RETRY_AUTH_INTERVAL); @@ -1370,6 +1525,12 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, bss_conf->assoc_capability = capab_info; ieee80211_set_associated(sdata, ifsta); + /* + * initialise the time of last beacon to be the association time, + * otherwise beacon loss check will trigger immediately + */ + ifsta->last_beacon = jiffies; + ieee80211_associated(sdata, ifsta); } @@ -1463,6 +1624,7 @@ static int ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, memcpy(pos, &bss->supp_rates[8], rates); } + kfree_skb(ifsta->probe_resp); ifsta->probe_resp = skb; ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON); @@ -1639,6 +1801,7 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, size_t baselen; struct ieee802_11_elems elems; struct ieee80211_if_sta *ifsta = &sdata->u.sta; + struct ieee80211_local *local = sdata->local; if (memcmp(mgmt->da, sdata->dev->dev_addr, ETH_ALEN)) return; /* ignore ProbeResp to foreign address */ @@ -1659,6 +1822,19 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, sdata->dev->name); ieee80211_authenticate(sdata, ifsta); } + + if (ifsta->flags & IEEE80211_STA_PROBEREQ_POLL) { + if (local->powersave) { + /* + * re-enable power save now that probe response was + * received + */ + local->hw.conf.flags |= IEEE80211_CONF_PS; + ieee80211_hw_config(local); + } + + ifsta->flags &= ~IEEE80211_STA_PROBEREQ_POLL; + } } @@ -1673,6 +1849,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct ieee80211_conf *conf = &local->hw.conf; u32 changed = 0; + bool erp_valid; + u8 erp_value = 0; /* Process beacon from the current BSS */ baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt; @@ -1694,13 +1872,16 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ieee80211_sta_wmm_params(local, ifsta, elems.wmm_param, elems.wmm_param_len); - if (elems.erp_info && elems.erp_info_len >= 1) - changed |= ieee80211_handle_erp_ie(sdata, elems.erp_info[0]); - else { - u16 capab = le16_to_cpu(mgmt->u.beacon.capab_info); - changed |= ieee80211_handle_protect_preamb(sdata, false, - (capab & WLAN_CAPABILITY_SHORT_PREAMBLE) != 0); + + if (elems.erp_info && elems.erp_info_len >= 1) { + erp_valid = true; + erp_value = elems.erp_info[0]; + } else { + erp_valid = false; } + changed |= ieee80211_handle_bss_capability(sdata, + le16_to_cpu(mgmt->u.beacon.capab_info), + erp_valid, erp_value); if (elems.ht_cap_elem && elems.ht_info_elem && elems.wmm_param && conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) { @@ -2318,6 +2499,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) ifsta = &sdata->u.sta; INIT_WORK(&ifsta->work, ieee80211_sta_work); + INIT_WORK(&ifsta->beacon_loss_work, ieee80211_beacon_loss_work); setup_timer(&ifsta->timer, ieee80211_sta_timer, (unsigned long) sdata); skb_queue_head_init(&ifsta->skb_queue); @@ -2560,3 +2742,39 @@ void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local) ieee80211_restart_sta_timer(sdata); rcu_read_unlock(); } + +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_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); + + if (local->hw.conf.flags & IEEE80211_CONF_PS) + return; + + local->hw.conf.flags |= IEEE80211_CONF_PS; + + ieee80211_hw_config(local); +} + +void ieee80211_dynamic_ps_timer(unsigned long data) +{ + struct ieee80211_local *local = (void *) data; + + queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work); +}