diff options
Diffstat (limited to 'net/mac80211/mlme.c')
-rw-r--r-- | net/mac80211/mlme.c | 182 |
1 files changed, 159 insertions, 23 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index be5f723d643a..35d850223a75 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -19,6 +19,7 @@ #include <linux/rtnetlink.h> #include <linux/pm_qos_params.h> #include <linux/crc32.h> +#include <linux/slab.h> #include <net/mac80211.h> #include <asm/unaligned.h> @@ -46,6 +47,13 @@ */ #define IEEE80211_PROBE_WAIT (HZ / 2) +/* + * Weight given to the latest Beacon frame when calculating average signal + * strength for Beacon frames received in the current BSS. This must be + * between 1 and 15. + */ +#define IEEE80211_SIGNAL_AVE_WEIGHT 3 + #define TMR_RUNNING_TIMER 0 #define TMR_RUNNING_CHANSW 1 @@ -203,7 +211,7 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, const u8 *bssid, u16 stype, u16 reason, - void *cookie) + void *cookie, bool send_frame) { struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; @@ -240,7 +248,11 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len); if (!(ifmgd->flags & IEEE80211_STA_MFP_ENABLED)) IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; - ieee80211_tx_skb(sdata, skb); + + if (send_frame) + ieee80211_tx_skb(sdata, skb); + else + kfree_skb(skb); } void ieee80211_send_pspoll(struct ieee80211_local *local, @@ -589,6 +601,9 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local, int count; u8 *pos, uapsd_queues = 0; + if (!local->ops->conf_tx) + return; + if (local->hw.queues < 4) return; @@ -663,11 +678,15 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local, params.aifs, params.cw_min, params.cw_max, params.txop, params.uapsd); #endif - if (drv_conf_tx(local, queue, ¶ms) && local->ops->conf_tx) + if (drv_conf_tx(local, queue, ¶ms)) printk(KERN_DEBUG "%s: failed to set TX queue " "parameters for queue %d\n", wiphy_name(local->hw.wiphy), queue); } + + /* enable WMM or activate new settings */ + local->hw.conf.flags |= IEEE80211_CONF_QOS; + drv_config(local, IEEE80211_CONF_CHANGE_QOS); } static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, @@ -728,6 +747,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, sdata->u.mgd.associated = cbss; memcpy(sdata->u.mgd.bssid, cbss->bssid, ETH_ALEN); + sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE; + /* just to be sure */ sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL | IEEE80211_STA_BEACON_POLL); @@ -753,6 +774,11 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, /* And the BSSID changed - we're associated now */ bss_info_changed |= BSS_CHANGED_BSSID; + /* Tell the driver to monitor connection quality (if supported) */ + if ((local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI) && + sdata->vif.bss_conf.cqm_rssi_thold) + bss_info_changed |= BSS_CHANGED_CQM; + ieee80211_bss_info_change_notify(sdata, bss_info_changed); mutex_lock(&local->iflist_mtx); @@ -764,7 +790,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, netif_carrier_on(sdata->dev); } -static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata) +static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, + bool remove_sta) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; @@ -837,7 +864,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata) changed |= BSS_CHANGED_BSSID; ieee80211_bss_info_change_notify(sdata, changed); - sta_info_destroy_addr(sdata, bssid); + if (remove_sta) + sta_info_destroy_addr(sdata, bssid); } void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, @@ -854,6 +882,9 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, if (is_multicast_ether_addr(hdr->addr1)) return; + if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) + return; + mod_timer(&sdata->u.mgd.conn_mon_timer, round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME)); } @@ -931,23 +962,72 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, mutex_unlock(&ifmgd->mtx); } -void ieee80211_beacon_loss_work(struct work_struct *work) +static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct ieee80211_local *local = sdata->local; + u8 bssid[ETH_ALEN]; + + mutex_lock(&ifmgd->mtx); + if (!ifmgd->associated) { + mutex_unlock(&ifmgd->mtx); + return; + } + + memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN); + + printk(KERN_DEBUG "Connection to AP %pM lost.\n", bssid); + + ieee80211_set_disassoc(sdata, true); + ieee80211_recalc_idle(local); + mutex_unlock(&ifmgd->mtx); + /* + * must be outside lock due to cfg80211, + * but that's not a problem. + */ + ieee80211_send_deauth_disassoc(sdata, bssid, + IEEE80211_STYPE_DEAUTH, + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, + NULL, true); +} + +void ieee80211_beacon_connection_loss_work(struct work_struct *work) { struct ieee80211_sub_if_data *sdata = container_of(work, struct ieee80211_sub_if_data, - u.mgd.beacon_loss_work); + u.mgd.beacon_connection_loss_work); - ieee80211_mgd_probe_ap(sdata, true); + if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) + __ieee80211_connection_loss(sdata); + else + ieee80211_mgd_probe_ap(sdata, true); } void ieee80211_beacon_loss(struct ieee80211_vif *vif) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_hw *hw = &sdata->local->hw; + + trace_api_beacon_loss(sdata); - ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work); + WARN_ON(hw->flags & IEEE80211_HW_CONNECTION_MONITOR); + ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work); } EXPORT_SYMBOL(ieee80211_beacon_loss); +void ieee80211_connection_loss(struct ieee80211_vif *vif) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_hw *hw = &sdata->local->hw; + + trace_api_connection_loss(sdata); + + WARN_ON(!(hw->flags & IEEE80211_HW_CONNECTION_MONITOR)); + ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work); +} +EXPORT_SYMBOL(ieee80211_connection_loss); + + static enum rx_mgmt_action __must_check ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len) @@ -968,7 +1048,7 @@ ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: deauthenticated from %pM (Reason: %u)\n", sdata->name, bssid, reason_code); - ieee80211_set_disassoc(sdata); + ieee80211_set_disassoc(sdata, true); ieee80211_recalc_idle(sdata->local); return RX_MGMT_CFG80211_DEAUTH; @@ -998,7 +1078,7 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: disassociated from %pM (Reason: %u)\n", sdata->name, mgmt->sa, reason_code); - ieee80211_set_disassoc(sdata); + ieee80211_set_disassoc(sdata, true); ieee80211_recalc_idle(sdata->local); return RX_MGMT_CFG80211_DISASSOC; } @@ -1290,6 +1370,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, struct ieee80211_rx_status *rx_status) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; size_t baselen; struct ieee802_11_elems elems; struct ieee80211_local *local = sdata->local; @@ -1325,6 +1406,41 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, if (memcmp(bssid, mgmt->bssid, ETH_ALEN) != 0) return; + /* Track average RSSI from the Beacon frames of the current AP */ + ifmgd->last_beacon_signal = rx_status->signal; + if (ifmgd->flags & IEEE80211_STA_RESET_SIGNAL_AVE) { + ifmgd->flags &= ~IEEE80211_STA_RESET_SIGNAL_AVE; + ifmgd->ave_beacon_signal = rx_status->signal; + ifmgd->last_cqm_event_signal = 0; + } else { + ifmgd->ave_beacon_signal = + (IEEE80211_SIGNAL_AVE_WEIGHT * rx_status->signal * 16 + + (16 - IEEE80211_SIGNAL_AVE_WEIGHT) * + ifmgd->ave_beacon_signal) / 16; + } + if (bss_conf->cqm_rssi_thold && + !(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI)) { + int sig = ifmgd->ave_beacon_signal / 16; + int last_event = ifmgd->last_cqm_event_signal; + int thold = bss_conf->cqm_rssi_thold; + int hyst = bss_conf->cqm_rssi_hyst; + if (sig < thold && + (last_event == 0 || sig < last_event - hyst)) { + ifmgd->last_cqm_event_signal = sig; + ieee80211_cqm_rssi_notify( + &sdata->vif, + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, + GFP_KERNEL); + } else if (sig > thold && + (last_event == 0 || sig > last_event + hyst)) { + ifmgd->last_cqm_event_signal = sig; + ieee80211_cqm_rssi_notify( + &sdata->vif, + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, + GFP_KERNEL); + } + } + if (ifmgd->flags & IEEE80211_STA_BEACON_POLL) { #ifdef CONFIG_MAC80211_VERBOSE_DEBUG if (net_ratelimit()) { @@ -1610,7 +1726,7 @@ static void ieee80211_sta_work(struct work_struct *work) printk(KERN_DEBUG "No probe response from AP %pM" " after %dms, disconnecting.\n", bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ); - ieee80211_set_disassoc(sdata); + ieee80211_set_disassoc(sdata, true); ieee80211_recalc_idle(local); mutex_unlock(&ifmgd->mtx); /* @@ -1620,7 +1736,7 @@ static void ieee80211_sta_work(struct work_struct *work) ieee80211_send_deauth_disassoc(sdata, bssid, IEEE80211_STYPE_DEAUTH, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, - NULL); + NULL, true); mutex_lock(&ifmgd->mtx); } } @@ -1637,7 +1753,8 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data) if (local->quiescing) return; - ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work); + ieee80211_queue_work(&sdata->local->hw, + &sdata->u.mgd.beacon_connection_loss_work); } static void ieee80211_sta_conn_mon_timer(unsigned long data) @@ -1689,7 +1806,7 @@ void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata) */ cancel_work_sync(&ifmgd->work); - cancel_work_sync(&ifmgd->beacon_loss_work); + cancel_work_sync(&ifmgd->beacon_connection_loss_work); if (del_timer_sync(&ifmgd->timer)) set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running); @@ -1723,7 +1840,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) INIT_WORK(&ifmgd->work, ieee80211_sta_work); INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work); INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work); - INIT_WORK(&ifmgd->beacon_loss_work, ieee80211_beacon_loss_work); + INIT_WORK(&ifmgd->beacon_connection_loss_work, + ieee80211_beacon_connection_loss_work); setup_timer(&ifmgd->timer, ieee80211_sta_timer, (unsigned long) sdata); setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, @@ -1802,6 +1920,9 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, struct ieee80211_work *wk; u16 auth_alg; + if (req->local_state_change) + return 0; /* no need to update mac80211 state */ + switch (req->auth_type) { case NL80211_AUTHTYPE_OPEN_SYSTEM: auth_alg = WLAN_AUTH_OPEN; @@ -1910,7 +2031,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, } /* Trying to reassociate - clear previous association state */ - ieee80211_set_disassoc(sdata); + ieee80211_set_disassoc(sdata, true); } mutex_unlock(&ifmgd->mtx); @@ -2014,7 +2135,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, if (ifmgd->associated == req->bss) { bssid = req->bss->bssid; - ieee80211_set_disassoc(sdata); + ieee80211_set_disassoc(sdata, true); mutex_unlock(&ifmgd->mtx); } else { bool not_auth_yet = false; @@ -2057,9 +2178,9 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: deauthenticating from %pM by local choice (reason=%d)\n", sdata->name, bssid, req->reason_code); - ieee80211_send_deauth_disassoc(sdata, bssid, - IEEE80211_STYPE_DEAUTH, req->reason_code, - cookie); + ieee80211_send_deauth_disassoc(sdata, bssid, IEEE80211_STYPE_DEAUTH, + req->reason_code, cookie, + !req->local_state_change); ieee80211_recalc_idle(sdata->local); @@ -2071,6 +2192,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, void *cookie) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + u8 bssid[ETH_ALEN]; mutex_lock(&ifmgd->mtx); @@ -2088,13 +2210,15 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, printk(KERN_DEBUG "%s: disassociating from %pM by local choice (reason=%d)\n", sdata->name, req->bss->bssid, req->reason_code); - ieee80211_set_disassoc(sdata); + memcpy(bssid, req->bss->bssid, ETH_ALEN); + ieee80211_set_disassoc(sdata, false); mutex_unlock(&ifmgd->mtx); ieee80211_send_deauth_disassoc(sdata, req->bss->bssid, IEEE80211_STYPE_DISASSOC, req->reason_code, - cookie); + cookie, !req->local_state_change); + sta_info_destroy_addr(sdata, bssid); ieee80211_recalc_idle(sdata->local); @@ -2135,3 +2259,15 @@ int ieee80211_mgd_action(struct ieee80211_sub_if_data *sdata, *cookie = (unsigned long) skb; return 0; } + +void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, + enum nl80211_cqm_rssi_threshold_event rssi_event, + gfp_t gfp) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + + trace_api_cqm_rssi_notify(sdata, rssi_event); + + cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp); +} +EXPORT_SYMBOL(ieee80211_cqm_rssi_notify); |