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 be5f723d643..35d850223a7 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);  |