diff options
Diffstat (limited to 'net/mac80211/mlme.c')
-rw-r--r-- | net/mac80211/mlme.c | 312 |
1 files changed, 262 insertions, 50 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 875c8dec940a..0839c4e8fd2e 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -47,6 +47,13 @@ | |||
47 | */ | 47 | */ |
48 | #define IEEE80211_PROBE_WAIT (HZ / 2) | 48 | #define IEEE80211_PROBE_WAIT (HZ / 2) |
49 | 49 | ||
50 | /* | ||
51 | * Weight given to the latest Beacon frame when calculating average signal | ||
52 | * strength for Beacon frames received in the current BSS. This must be | ||
53 | * between 1 and 15. | ||
54 | */ | ||
55 | #define IEEE80211_SIGNAL_AVE_WEIGHT 3 | ||
56 | |||
50 | #define TMR_RUNNING_TIMER 0 | 57 | #define TMR_RUNNING_TIMER 0 |
51 | #define TMR_RUNNING_CHANSW 1 | 58 | #define TMR_RUNNING_CHANSW 1 |
52 | 59 | ||
@@ -130,11 +137,14 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, | |||
130 | struct sta_info *sta; | 137 | struct sta_info *sta; |
131 | u32 changed = 0; | 138 | u32 changed = 0; |
132 | u16 ht_opmode; | 139 | u16 ht_opmode; |
133 | bool enable_ht = true, ht_changed; | 140 | bool enable_ht = true; |
141 | enum nl80211_channel_type prev_chantype; | ||
134 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | 142 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; |
135 | 143 | ||
136 | sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; | 144 | sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; |
137 | 145 | ||
146 | prev_chantype = sdata->vif.bss_conf.channel_type; | ||
147 | |||
138 | /* HT is not supported */ | 148 | /* HT is not supported */ |
139 | if (!sband->ht_cap.ht_supported) | 149 | if (!sband->ht_cap.ht_supported) |
140 | enable_ht = false; | 150 | enable_ht = false; |
@@ -165,38 +175,37 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, | |||
165 | } | 175 | } |
166 | } | 176 | } |
167 | 177 | ||
168 | ht_changed = conf_is_ht(&local->hw.conf) != enable_ht || | ||
169 | channel_type != local->hw.conf.channel_type; | ||
170 | |||
171 | if (local->tmp_channel) | 178 | if (local->tmp_channel) |
172 | local->tmp_channel_type = channel_type; | 179 | local->tmp_channel_type = channel_type; |
173 | local->oper_channel_type = channel_type; | ||
174 | 180 | ||
175 | if (ht_changed) { | 181 | if (!ieee80211_set_channel_type(local, sdata, channel_type)) { |
176 | /* channel_type change automatically detected */ | 182 | /* can only fail due to HT40+/- mismatch */ |
177 | ieee80211_hw_config(local, 0); | 183 | channel_type = NL80211_CHAN_HT20; |
184 | WARN_ON(!ieee80211_set_channel_type(local, sdata, channel_type)); | ||
185 | } | ||
186 | |||
187 | /* channel_type change automatically detected */ | ||
188 | ieee80211_hw_config(local, 0); | ||
178 | 189 | ||
190 | if (prev_chantype != channel_type) { | ||
179 | rcu_read_lock(); | 191 | rcu_read_lock(); |
180 | sta = sta_info_get(sdata, bssid); | 192 | sta = sta_info_get(sdata, bssid); |
181 | if (sta) | 193 | if (sta) |
182 | rate_control_rate_update(local, sband, sta, | 194 | rate_control_rate_update(local, sband, sta, |
183 | IEEE80211_RC_HT_CHANGED, | 195 | IEEE80211_RC_HT_CHANGED, |
184 | local->oper_channel_type); | 196 | channel_type); |
185 | rcu_read_unlock(); | 197 | rcu_read_unlock(); |
186 | } | 198 | } |
187 | |||
188 | /* disable HT */ | ||
189 | if (!enable_ht) | ||
190 | return 0; | ||
191 | 199 | ||
192 | ht_opmode = le16_to_cpu(hti->operation_mode); | 200 | ht_opmode = le16_to_cpu(hti->operation_mode); |
193 | 201 | ||
194 | /* if bss configuration changed store the new one */ | 202 | /* if bss configuration changed store the new one */ |
195 | if (!sdata->ht_opmode_valid || | 203 | if (sdata->ht_opmode_valid != enable_ht || |
196 | sdata->vif.bss_conf.ht_operation_mode != ht_opmode) { | 204 | sdata->vif.bss_conf.ht_operation_mode != ht_opmode || |
205 | prev_chantype != channel_type) { | ||
197 | changed |= BSS_CHANGED_HT; | 206 | changed |= BSS_CHANGED_HT; |
198 | sdata->vif.bss_conf.ht_operation_mode = ht_opmode; | 207 | sdata->vif.bss_conf.ht_operation_mode = ht_opmode; |
199 | sdata->ht_opmode_valid = true; | 208 | sdata->ht_opmode_valid = enable_ht; |
200 | } | 209 | } |
201 | 210 | ||
202 | return changed; | 211 | return changed; |
@@ -206,7 +215,7 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, | |||
206 | 215 | ||
207 | static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, | 216 | static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, |
208 | const u8 *bssid, u16 stype, u16 reason, | 217 | const u8 *bssid, u16 stype, u16 reason, |
209 | void *cookie) | 218 | void *cookie, bool send_frame) |
210 | { | 219 | { |
211 | struct ieee80211_local *local = sdata->local; | 220 | struct ieee80211_local *local = sdata->local; |
212 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | 221 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
@@ -243,7 +252,11 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, | |||
243 | cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len); | 252 | cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len); |
244 | if (!(ifmgd->flags & IEEE80211_STA_MFP_ENABLED)) | 253 | if (!(ifmgd->flags & IEEE80211_STA_MFP_ENABLED)) |
245 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; | 254 | IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; |
246 | ieee80211_tx_skb(sdata, skb); | 255 | |
256 | if (send_frame) | ||
257 | ieee80211_tx_skb(sdata, skb); | ||
258 | else | ||
259 | kfree_skb(skb); | ||
247 | } | 260 | } |
248 | 261 | ||
249 | void ieee80211_send_pspoll(struct ieee80211_local *local, | 262 | void ieee80211_send_pspoll(struct ieee80211_local *local, |
@@ -329,7 +342,11 @@ static void ieee80211_chswitch_work(struct work_struct *work) | |||
329 | goto out; | 342 | goto out; |
330 | 343 | ||
331 | sdata->local->oper_channel = sdata->local->csa_channel; | 344 | sdata->local->oper_channel = sdata->local->csa_channel; |
332 | ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL); | 345 | if (!sdata->local->ops->channel_switch) { |
346 | /* call "hw_config" only if doing sw channel switch */ | ||
347 | ieee80211_hw_config(sdata->local, | ||
348 | IEEE80211_CONF_CHANGE_CHANNEL); | ||
349 | } | ||
333 | 350 | ||
334 | /* XXX: shouldn't really modify cfg80211-owned data! */ | 351 | /* XXX: shouldn't really modify cfg80211-owned data! */ |
335 | ifmgd->associated->channel = sdata->local->oper_channel; | 352 | ifmgd->associated->channel = sdata->local->oper_channel; |
@@ -341,6 +358,29 @@ static void ieee80211_chswitch_work(struct work_struct *work) | |||
341 | mutex_unlock(&ifmgd->mtx); | 358 | mutex_unlock(&ifmgd->mtx); |
342 | } | 359 | } |
343 | 360 | ||
361 | void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success) | ||
362 | { | ||
363 | struct ieee80211_sub_if_data *sdata; | ||
364 | struct ieee80211_if_managed *ifmgd; | ||
365 | |||
366 | sdata = vif_to_sdata(vif); | ||
367 | ifmgd = &sdata->u.mgd; | ||
368 | |||
369 | trace_api_chswitch_done(sdata, success); | ||
370 | if (!success) { | ||
371 | /* | ||
372 | * If the channel switch was not successful, stay | ||
373 | * around on the old channel. We currently lack | ||
374 | * good handling of this situation, possibly we | ||
375 | * should just drop the association. | ||
376 | */ | ||
377 | sdata->local->csa_channel = sdata->local->oper_channel; | ||
378 | } | ||
379 | |||
380 | ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); | ||
381 | } | ||
382 | EXPORT_SYMBOL(ieee80211_chswitch_done); | ||
383 | |||
344 | static void ieee80211_chswitch_timer(unsigned long data) | 384 | static void ieee80211_chswitch_timer(unsigned long data) |
345 | { | 385 | { |
346 | struct ieee80211_sub_if_data *sdata = | 386 | struct ieee80211_sub_if_data *sdata = |
@@ -357,7 +397,8 @@ static void ieee80211_chswitch_timer(unsigned long data) | |||
357 | 397 | ||
358 | void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, | 398 | void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, |
359 | struct ieee80211_channel_sw_ie *sw_elem, | 399 | struct ieee80211_channel_sw_ie *sw_elem, |
360 | struct ieee80211_bss *bss) | 400 | struct ieee80211_bss *bss, |
401 | u64 timestamp) | ||
361 | { | 402 | { |
362 | struct cfg80211_bss *cbss = | 403 | struct cfg80211_bss *cbss = |
363 | container_of((void *)bss, struct cfg80211_bss, priv); | 404 | container_of((void *)bss, struct cfg80211_bss, priv); |
@@ -385,10 +426,29 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
385 | 426 | ||
386 | sdata->local->csa_channel = new_ch; | 427 | sdata->local->csa_channel = new_ch; |
387 | 428 | ||
429 | if (sdata->local->ops->channel_switch) { | ||
430 | /* use driver's channel switch callback */ | ||
431 | struct ieee80211_channel_switch ch_switch; | ||
432 | memset(&ch_switch, 0, sizeof(ch_switch)); | ||
433 | ch_switch.timestamp = timestamp; | ||
434 | if (sw_elem->mode) { | ||
435 | ch_switch.block_tx = true; | ||
436 | ieee80211_stop_queues_by_reason(&sdata->local->hw, | ||
437 | IEEE80211_QUEUE_STOP_REASON_CSA); | ||
438 | } | ||
439 | ch_switch.channel = new_ch; | ||
440 | ch_switch.count = sw_elem->count; | ||
441 | ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; | ||
442 | drv_channel_switch(sdata->local, &ch_switch); | ||
443 | return; | ||
444 | } | ||
445 | |||
446 | /* channel switch handled in software */ | ||
388 | if (sw_elem->count <= 1) { | 447 | if (sw_elem->count <= 1) { |
389 | ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); | 448 | ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work); |
390 | } else { | 449 | } else { |
391 | ieee80211_stop_queues_by_reason(&sdata->local->hw, | 450 | if (sw_elem->mode) |
451 | ieee80211_stop_queues_by_reason(&sdata->local->hw, | ||
392 | IEEE80211_QUEUE_STOP_REASON_CSA); | 452 | IEEE80211_QUEUE_STOP_REASON_CSA); |
393 | ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; | 453 | ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; |
394 | mod_timer(&ifmgd->chswitch_timer, | 454 | mod_timer(&ifmgd->chswitch_timer, |
@@ -467,6 +527,7 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency) | |||
467 | { | 527 | { |
468 | struct ieee80211_sub_if_data *sdata, *found = NULL; | 528 | struct ieee80211_sub_if_data *sdata, *found = NULL; |
469 | int count = 0; | 529 | int count = 0; |
530 | int timeout; | ||
470 | 531 | ||
471 | if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) { | 532 | if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) { |
472 | local->ps_sdata = NULL; | 533 | local->ps_sdata = NULL; |
@@ -495,11 +556,31 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency) | |||
495 | s32 beaconint_us; | 556 | s32 beaconint_us; |
496 | 557 | ||
497 | if (latency < 0) | 558 | if (latency < 0) |
498 | latency = pm_qos_requirement(PM_QOS_NETWORK_LATENCY); | 559 | latency = pm_qos_request(PM_QOS_NETWORK_LATENCY); |
499 | 560 | ||
500 | beaconint_us = ieee80211_tu_to_usec( | 561 | beaconint_us = ieee80211_tu_to_usec( |
501 | found->vif.bss_conf.beacon_int); | 562 | found->vif.bss_conf.beacon_int); |
502 | 563 | ||
564 | timeout = local->hw.conf.dynamic_ps_forced_timeout; | ||
565 | if (timeout < 0) { | ||
566 | /* | ||
567 | * The 2 second value is there for compatibility until | ||
568 | * the PM_QOS_NETWORK_LATENCY is configured with real | ||
569 | * values. | ||
570 | */ | ||
571 | if (latency == 2000000000) | ||
572 | timeout = 100; | ||
573 | else if (latency <= 50000) | ||
574 | timeout = 300; | ||
575 | else if (latency <= 100000) | ||
576 | timeout = 100; | ||
577 | else if (latency <= 500000) | ||
578 | timeout = 50; | ||
579 | else | ||
580 | timeout = 0; | ||
581 | } | ||
582 | local->hw.conf.dynamic_ps_timeout = timeout; | ||
583 | |||
503 | if (beaconint_us > latency) { | 584 | if (beaconint_us > latency) { |
504 | local->ps_sdata = NULL; | 585 | local->ps_sdata = NULL; |
505 | } else { | 586 | } else { |
@@ -592,6 +673,9 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local, | |||
592 | int count; | 673 | int count; |
593 | u8 *pos, uapsd_queues = 0; | 674 | u8 *pos, uapsd_queues = 0; |
594 | 675 | ||
676 | if (!local->ops->conf_tx) | ||
677 | return; | ||
678 | |||
595 | if (local->hw.queues < 4) | 679 | if (local->hw.queues < 4) |
596 | return; | 680 | return; |
597 | 681 | ||
@@ -666,11 +750,15 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local, | |||
666 | params.aifs, params.cw_min, params.cw_max, params.txop, | 750 | params.aifs, params.cw_min, params.cw_max, params.txop, |
667 | params.uapsd); | 751 | params.uapsd); |
668 | #endif | 752 | #endif |
669 | if (drv_conf_tx(local, queue, ¶ms) && local->ops->conf_tx) | 753 | if (drv_conf_tx(local, queue, ¶ms)) |
670 | printk(KERN_DEBUG "%s: failed to set TX queue " | 754 | printk(KERN_DEBUG "%s: failed to set TX queue " |
671 | "parameters for queue %d\n", | 755 | "parameters for queue %d\n", |
672 | wiphy_name(local->hw.wiphy), queue); | 756 | wiphy_name(local->hw.wiphy), queue); |
673 | } | 757 | } |
758 | |||
759 | /* enable WMM or activate new settings */ | ||
760 | local->hw.conf.flags |= IEEE80211_CONF_QOS; | ||
761 | drv_config(local, IEEE80211_CONF_CHANGE_QOS); | ||
674 | } | 762 | } |
675 | 763 | ||
676 | static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, | 764 | static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, |
@@ -731,6 +819,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, | |||
731 | sdata->u.mgd.associated = cbss; | 819 | sdata->u.mgd.associated = cbss; |
732 | memcpy(sdata->u.mgd.bssid, cbss->bssid, ETH_ALEN); | 820 | memcpy(sdata->u.mgd.bssid, cbss->bssid, ETH_ALEN); |
733 | 821 | ||
822 | sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE; | ||
823 | |||
734 | /* just to be sure */ | 824 | /* just to be sure */ |
735 | sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL | | 825 | sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL | |
736 | IEEE80211_STA_BEACON_POLL); | 826 | IEEE80211_STA_BEACON_POLL); |
@@ -756,6 +846,11 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, | |||
756 | /* And the BSSID changed - we're associated now */ | 846 | /* And the BSSID changed - we're associated now */ |
757 | bss_info_changed |= BSS_CHANGED_BSSID; | 847 | bss_info_changed |= BSS_CHANGED_BSSID; |
758 | 848 | ||
849 | /* Tell the driver to monitor connection quality (if supported) */ | ||
850 | if ((local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI) && | ||
851 | sdata->vif.bss_conf.cqm_rssi_thold) | ||
852 | bss_info_changed |= BSS_CHANGED_CQM; | ||
853 | |||
759 | ieee80211_bss_info_change_notify(sdata, bss_info_changed); | 854 | ieee80211_bss_info_change_notify(sdata, bss_info_changed); |
760 | 855 | ||
761 | mutex_lock(&local->iflist_mtx); | 856 | mutex_lock(&local->iflist_mtx); |
@@ -767,7 +862,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, | |||
767 | netif_carrier_on(sdata->dev); | 862 | netif_carrier_on(sdata->dev); |
768 | } | 863 | } |
769 | 864 | ||
770 | static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata) | 865 | static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, |
866 | bool remove_sta) | ||
771 | { | 867 | { |
772 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | 868 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
773 | struct ieee80211_local *local = sdata->local; | 869 | struct ieee80211_local *local = sdata->local; |
@@ -819,7 +915,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata) | |||
819 | ieee80211_set_wmm_default(sdata); | 915 | ieee80211_set_wmm_default(sdata); |
820 | 916 | ||
821 | /* channel(_type) changes are handled by ieee80211_hw_config */ | 917 | /* channel(_type) changes are handled by ieee80211_hw_config */ |
822 | local->oper_channel_type = NL80211_CHAN_NO_HT; | 918 | WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT)); |
823 | 919 | ||
824 | /* on the next assoc, re-program HT parameters */ | 920 | /* on the next assoc, re-program HT parameters */ |
825 | sdata->ht_opmode_valid = false; | 921 | sdata->ht_opmode_valid = false; |
@@ -836,11 +932,12 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata) | |||
836 | 932 | ||
837 | ieee80211_hw_config(local, config_changed); | 933 | ieee80211_hw_config(local, config_changed); |
838 | 934 | ||
839 | /* And the BSSID changed -- not very interesting here */ | 935 | /* The BSSID (not really interesting) and HT changed */ |
840 | changed |= BSS_CHANGED_BSSID; | 936 | changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT; |
841 | ieee80211_bss_info_change_notify(sdata, changed); | 937 | ieee80211_bss_info_change_notify(sdata, changed); |
842 | 938 | ||
843 | sta_info_destroy_addr(sdata, bssid); | 939 | if (remove_sta) |
940 | sta_info_destroy_addr(sdata, bssid); | ||
844 | } | 941 | } |
845 | 942 | ||
846 | void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, | 943 | void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, |
@@ -857,6 +954,9 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, | |||
857 | if (is_multicast_ether_addr(hdr->addr1)) | 954 | if (is_multicast_ether_addr(hdr->addr1)) |
858 | return; | 955 | return; |
859 | 956 | ||
957 | if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) | ||
958 | return; | ||
959 | |||
860 | mod_timer(&sdata->u.mgd.conn_mon_timer, | 960 | mod_timer(&sdata->u.mgd.conn_mon_timer, |
861 | round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME)); | 961 | round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME)); |
862 | } | 962 | } |
@@ -934,23 +1034,72 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, | |||
934 | mutex_unlock(&ifmgd->mtx); | 1034 | mutex_unlock(&ifmgd->mtx); |
935 | } | 1035 | } |
936 | 1036 | ||
937 | void ieee80211_beacon_loss_work(struct work_struct *work) | 1037 | static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata) |
1038 | { | ||
1039 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | ||
1040 | struct ieee80211_local *local = sdata->local; | ||
1041 | u8 bssid[ETH_ALEN]; | ||
1042 | |||
1043 | mutex_lock(&ifmgd->mtx); | ||
1044 | if (!ifmgd->associated) { | ||
1045 | mutex_unlock(&ifmgd->mtx); | ||
1046 | return; | ||
1047 | } | ||
1048 | |||
1049 | memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN); | ||
1050 | |||
1051 | printk(KERN_DEBUG "Connection to AP %pM lost.\n", bssid); | ||
1052 | |||
1053 | ieee80211_set_disassoc(sdata, true); | ||
1054 | ieee80211_recalc_idle(local); | ||
1055 | mutex_unlock(&ifmgd->mtx); | ||
1056 | /* | ||
1057 | * must be outside lock due to cfg80211, | ||
1058 | * but that's not a problem. | ||
1059 | */ | ||
1060 | ieee80211_send_deauth_disassoc(sdata, bssid, | ||
1061 | IEEE80211_STYPE_DEAUTH, | ||
1062 | WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, | ||
1063 | NULL, true); | ||
1064 | } | ||
1065 | |||
1066 | void ieee80211_beacon_connection_loss_work(struct work_struct *work) | ||
938 | { | 1067 | { |
939 | struct ieee80211_sub_if_data *sdata = | 1068 | struct ieee80211_sub_if_data *sdata = |
940 | container_of(work, struct ieee80211_sub_if_data, | 1069 | container_of(work, struct ieee80211_sub_if_data, |
941 | u.mgd.beacon_loss_work); | 1070 | u.mgd.beacon_connection_loss_work); |
942 | 1071 | ||
943 | ieee80211_mgd_probe_ap(sdata, true); | 1072 | if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) |
1073 | __ieee80211_connection_loss(sdata); | ||
1074 | else | ||
1075 | ieee80211_mgd_probe_ap(sdata, true); | ||
944 | } | 1076 | } |
945 | 1077 | ||
946 | void ieee80211_beacon_loss(struct ieee80211_vif *vif) | 1078 | void ieee80211_beacon_loss(struct ieee80211_vif *vif) |
947 | { | 1079 | { |
948 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); | 1080 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); |
1081 | struct ieee80211_hw *hw = &sdata->local->hw; | ||
949 | 1082 | ||
950 | ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work); | 1083 | trace_api_beacon_loss(sdata); |
1084 | |||
1085 | WARN_ON(hw->flags & IEEE80211_HW_CONNECTION_MONITOR); | ||
1086 | ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work); | ||
951 | } | 1087 | } |
952 | EXPORT_SYMBOL(ieee80211_beacon_loss); | 1088 | EXPORT_SYMBOL(ieee80211_beacon_loss); |
953 | 1089 | ||
1090 | void ieee80211_connection_loss(struct ieee80211_vif *vif) | ||
1091 | { | ||
1092 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); | ||
1093 | struct ieee80211_hw *hw = &sdata->local->hw; | ||
1094 | |||
1095 | trace_api_connection_loss(sdata); | ||
1096 | |||
1097 | WARN_ON(!(hw->flags & IEEE80211_HW_CONNECTION_MONITOR)); | ||
1098 | ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work); | ||
1099 | } | ||
1100 | EXPORT_SYMBOL(ieee80211_connection_loss); | ||
1101 | |||
1102 | |||
954 | static enum rx_mgmt_action __must_check | 1103 | static enum rx_mgmt_action __must_check |
955 | ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, | 1104 | ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, |
956 | struct ieee80211_mgmt *mgmt, size_t len) | 1105 | struct ieee80211_mgmt *mgmt, size_t len) |
@@ -971,7 +1120,7 @@ ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, | |||
971 | printk(KERN_DEBUG "%s: deauthenticated from %pM (Reason: %u)\n", | 1120 | printk(KERN_DEBUG "%s: deauthenticated from %pM (Reason: %u)\n", |
972 | sdata->name, bssid, reason_code); | 1121 | sdata->name, bssid, reason_code); |
973 | 1122 | ||
974 | ieee80211_set_disassoc(sdata); | 1123 | ieee80211_set_disassoc(sdata, true); |
975 | ieee80211_recalc_idle(sdata->local); | 1124 | ieee80211_recalc_idle(sdata->local); |
976 | 1125 | ||
977 | return RX_MGMT_CFG80211_DEAUTH; | 1126 | return RX_MGMT_CFG80211_DEAUTH; |
@@ -1001,7 +1150,7 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, | |||
1001 | printk(KERN_DEBUG "%s: disassociated from %pM (Reason: %u)\n", | 1150 | printk(KERN_DEBUG "%s: disassociated from %pM (Reason: %u)\n", |
1002 | sdata->name, mgmt->sa, reason_code); | 1151 | sdata->name, mgmt->sa, reason_code); |
1003 | 1152 | ||
1004 | ieee80211_set_disassoc(sdata); | 1153 | ieee80211_set_disassoc(sdata, true); |
1005 | ieee80211_recalc_idle(sdata->local); | 1154 | ieee80211_recalc_idle(sdata->local); |
1006 | return RX_MGMT_CFG80211_DISASSOC; | 1155 | return RX_MGMT_CFG80211_DISASSOC; |
1007 | } | 1156 | } |
@@ -1215,7 +1364,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, | |||
1215 | ETH_ALEN) == 0)) { | 1364 | ETH_ALEN) == 0)) { |
1216 | struct ieee80211_channel_sw_ie *sw_elem = | 1365 | struct ieee80211_channel_sw_ie *sw_elem = |
1217 | (struct ieee80211_channel_sw_ie *)elems->ch_switch_elem; | 1366 | (struct ieee80211_channel_sw_ie *)elems->ch_switch_elem; |
1218 | ieee80211_sta_process_chanswitch(sdata, sw_elem, bss); | 1367 | ieee80211_sta_process_chanswitch(sdata, sw_elem, |
1368 | bss, rx_status->mactime); | ||
1219 | } | 1369 | } |
1220 | } | 1370 | } |
1221 | 1371 | ||
@@ -1254,12 +1404,17 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata, | |||
1254 | mutex_lock(&sdata->local->iflist_mtx); | 1404 | mutex_lock(&sdata->local->iflist_mtx); |
1255 | ieee80211_recalc_ps(sdata->local, -1); | 1405 | ieee80211_recalc_ps(sdata->local, -1); |
1256 | mutex_unlock(&sdata->local->iflist_mtx); | 1406 | mutex_unlock(&sdata->local->iflist_mtx); |
1407 | |||
1408 | if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) | ||
1409 | return; | ||
1410 | |||
1257 | /* | 1411 | /* |
1258 | * We've received a probe response, but are not sure whether | 1412 | * We've received a probe response, but are not sure whether |
1259 | * we have or will be receiving any beacons or data, so let's | 1413 | * we have or will be receiving any beacons or data, so let's |
1260 | * schedule the timers again, just in case. | 1414 | * schedule the timers again, just in case. |
1261 | */ | 1415 | */ |
1262 | mod_beacon_timer(sdata); | 1416 | mod_beacon_timer(sdata); |
1417 | |||
1263 | mod_timer(&ifmgd->conn_mon_timer, | 1418 | mod_timer(&ifmgd->conn_mon_timer, |
1264 | round_jiffies_up(jiffies + | 1419 | round_jiffies_up(jiffies + |
1265 | IEEE80211_CONNECTION_IDLE_TIME)); | 1420 | IEEE80211_CONNECTION_IDLE_TIME)); |
@@ -1293,6 +1448,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, | |||
1293 | struct ieee80211_rx_status *rx_status) | 1448 | struct ieee80211_rx_status *rx_status) |
1294 | { | 1449 | { |
1295 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | 1450 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
1451 | struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; | ||
1296 | size_t baselen; | 1452 | size_t baselen; |
1297 | struct ieee802_11_elems elems; | 1453 | struct ieee802_11_elems elems; |
1298 | struct ieee80211_local *local = sdata->local; | 1454 | struct ieee80211_local *local = sdata->local; |
@@ -1328,6 +1484,41 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, | |||
1328 | if (memcmp(bssid, mgmt->bssid, ETH_ALEN) != 0) | 1484 | if (memcmp(bssid, mgmt->bssid, ETH_ALEN) != 0) |
1329 | return; | 1485 | return; |
1330 | 1486 | ||
1487 | /* Track average RSSI from the Beacon frames of the current AP */ | ||
1488 | ifmgd->last_beacon_signal = rx_status->signal; | ||
1489 | if (ifmgd->flags & IEEE80211_STA_RESET_SIGNAL_AVE) { | ||
1490 | ifmgd->flags &= ~IEEE80211_STA_RESET_SIGNAL_AVE; | ||
1491 | ifmgd->ave_beacon_signal = rx_status->signal; | ||
1492 | ifmgd->last_cqm_event_signal = 0; | ||
1493 | } else { | ||
1494 | ifmgd->ave_beacon_signal = | ||
1495 | (IEEE80211_SIGNAL_AVE_WEIGHT * rx_status->signal * 16 + | ||
1496 | (16 - IEEE80211_SIGNAL_AVE_WEIGHT) * | ||
1497 | ifmgd->ave_beacon_signal) / 16; | ||
1498 | } | ||
1499 | if (bss_conf->cqm_rssi_thold && | ||
1500 | !(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI)) { | ||
1501 | int sig = ifmgd->ave_beacon_signal / 16; | ||
1502 | int last_event = ifmgd->last_cqm_event_signal; | ||
1503 | int thold = bss_conf->cqm_rssi_thold; | ||
1504 | int hyst = bss_conf->cqm_rssi_hyst; | ||
1505 | if (sig < thold && | ||
1506 | (last_event == 0 || sig < last_event - hyst)) { | ||
1507 | ifmgd->last_cqm_event_signal = sig; | ||
1508 | ieee80211_cqm_rssi_notify( | ||
1509 | &sdata->vif, | ||
1510 | NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, | ||
1511 | GFP_KERNEL); | ||
1512 | } else if (sig > thold && | ||
1513 | (last_event == 0 || sig > last_event + hyst)) { | ||
1514 | ifmgd->last_cqm_event_signal = sig; | ||
1515 | ieee80211_cqm_rssi_notify( | ||
1516 | &sdata->vif, | ||
1517 | NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, | ||
1518 | GFP_KERNEL); | ||
1519 | } | ||
1520 | } | ||
1521 | |||
1331 | if (ifmgd->flags & IEEE80211_STA_BEACON_POLL) { | 1522 | if (ifmgd->flags & IEEE80211_STA_BEACON_POLL) { |
1332 | #ifdef CONFIG_MAC80211_VERBOSE_DEBUG | 1523 | #ifdef CONFIG_MAC80211_VERBOSE_DEBUG |
1333 | if (net_ratelimit()) { | 1524 | if (net_ratelimit()) { |
@@ -1506,7 +1697,8 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, | |||
1506 | 1697 | ||
1507 | ieee80211_sta_process_chanswitch(sdata, | 1698 | ieee80211_sta_process_chanswitch(sdata, |
1508 | &mgmt->u.action.u.chan_switch.sw_elem, | 1699 | &mgmt->u.action.u.chan_switch.sw_elem, |
1509 | (void *)ifmgd->associated->priv); | 1700 | (void *)ifmgd->associated->priv, |
1701 | rx_status->mactime); | ||
1510 | break; | 1702 | break; |
1511 | } | 1703 | } |
1512 | mutex_unlock(&ifmgd->mtx); | 1704 | mutex_unlock(&ifmgd->mtx); |
@@ -1613,7 +1805,7 @@ static void ieee80211_sta_work(struct work_struct *work) | |||
1613 | printk(KERN_DEBUG "No probe response from AP %pM" | 1805 | printk(KERN_DEBUG "No probe response from AP %pM" |
1614 | " after %dms, disconnecting.\n", | 1806 | " after %dms, disconnecting.\n", |
1615 | bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ); | 1807 | bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ); |
1616 | ieee80211_set_disassoc(sdata); | 1808 | ieee80211_set_disassoc(sdata, true); |
1617 | ieee80211_recalc_idle(local); | 1809 | ieee80211_recalc_idle(local); |
1618 | mutex_unlock(&ifmgd->mtx); | 1810 | mutex_unlock(&ifmgd->mtx); |
1619 | /* | 1811 | /* |
@@ -1623,7 +1815,7 @@ static void ieee80211_sta_work(struct work_struct *work) | |||
1623 | ieee80211_send_deauth_disassoc(sdata, bssid, | 1815 | ieee80211_send_deauth_disassoc(sdata, bssid, |
1624 | IEEE80211_STYPE_DEAUTH, | 1816 | IEEE80211_STYPE_DEAUTH, |
1625 | WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, | 1817 | WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, |
1626 | NULL); | 1818 | NULL, true); |
1627 | mutex_lock(&ifmgd->mtx); | 1819 | mutex_lock(&ifmgd->mtx); |
1628 | } | 1820 | } |
1629 | } | 1821 | } |
@@ -1640,7 +1832,8 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data) | |||
1640 | if (local->quiescing) | 1832 | if (local->quiescing) |
1641 | return; | 1833 | return; |
1642 | 1834 | ||
1643 | ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work); | 1835 | ieee80211_queue_work(&sdata->local->hw, |
1836 | &sdata->u.mgd.beacon_connection_loss_work); | ||
1644 | } | 1837 | } |
1645 | 1838 | ||
1646 | static void ieee80211_sta_conn_mon_timer(unsigned long data) | 1839 | static void ieee80211_sta_conn_mon_timer(unsigned long data) |
@@ -1692,7 +1885,7 @@ void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata) | |||
1692 | */ | 1885 | */ |
1693 | 1886 | ||
1694 | cancel_work_sync(&ifmgd->work); | 1887 | cancel_work_sync(&ifmgd->work); |
1695 | cancel_work_sync(&ifmgd->beacon_loss_work); | 1888 | cancel_work_sync(&ifmgd->beacon_connection_loss_work); |
1696 | if (del_timer_sync(&ifmgd->timer)) | 1889 | if (del_timer_sync(&ifmgd->timer)) |
1697 | set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running); | 1890 | set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running); |
1698 | 1891 | ||
@@ -1726,7 +1919,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) | |||
1726 | INIT_WORK(&ifmgd->work, ieee80211_sta_work); | 1919 | INIT_WORK(&ifmgd->work, ieee80211_sta_work); |
1727 | INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work); | 1920 | INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work); |
1728 | INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work); | 1921 | INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work); |
1729 | INIT_WORK(&ifmgd->beacon_loss_work, ieee80211_beacon_loss_work); | 1922 | INIT_WORK(&ifmgd->beacon_connection_loss_work, |
1923 | ieee80211_beacon_connection_loss_work); | ||
1730 | setup_timer(&ifmgd->timer, ieee80211_sta_timer, | 1924 | setup_timer(&ifmgd->timer, ieee80211_sta_timer, |
1731 | (unsigned long) sdata); | 1925 | (unsigned long) sdata); |
1732 | setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, | 1926 | setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, |
@@ -1805,6 +1999,9 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, | |||
1805 | struct ieee80211_work *wk; | 1999 | struct ieee80211_work *wk; |
1806 | u16 auth_alg; | 2000 | u16 auth_alg; |
1807 | 2001 | ||
2002 | if (req->local_state_change) | ||
2003 | return 0; /* no need to update mac80211 state */ | ||
2004 | |||
1808 | switch (req->auth_type) { | 2005 | switch (req->auth_type) { |
1809 | case NL80211_AUTHTYPE_OPEN_SYSTEM: | 2006 | case NL80211_AUTHTYPE_OPEN_SYSTEM: |
1810 | auth_alg = WLAN_AUTH_OPEN; | 2007 | auth_alg = WLAN_AUTH_OPEN; |
@@ -1913,7 +2110,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, | |||
1913 | } | 2110 | } |
1914 | 2111 | ||
1915 | /* Trying to reassociate - clear previous association state */ | 2112 | /* Trying to reassociate - clear previous association state */ |
1916 | ieee80211_set_disassoc(sdata); | 2113 | ieee80211_set_disassoc(sdata, true); |
1917 | } | 2114 | } |
1918 | mutex_unlock(&ifmgd->mtx); | 2115 | mutex_unlock(&ifmgd->mtx); |
1919 | 2116 | ||
@@ -2017,7 +2214,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, | |||
2017 | 2214 | ||
2018 | if (ifmgd->associated == req->bss) { | 2215 | if (ifmgd->associated == req->bss) { |
2019 | bssid = req->bss->bssid; | 2216 | bssid = req->bss->bssid; |
2020 | ieee80211_set_disassoc(sdata); | 2217 | ieee80211_set_disassoc(sdata, true); |
2021 | mutex_unlock(&ifmgd->mtx); | 2218 | mutex_unlock(&ifmgd->mtx); |
2022 | } else { | 2219 | } else { |
2023 | bool not_auth_yet = false; | 2220 | bool not_auth_yet = false; |
@@ -2061,9 +2258,9 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, | |||
2061 | printk(KERN_DEBUG "%s: deauthenticating from %pM by local choice (reason=%d)\n", | 2258 | printk(KERN_DEBUG "%s: deauthenticating from %pM by local choice (reason=%d)\n", |
2062 | sdata->name, bssid, req->reason_code); | 2259 | sdata->name, bssid, req->reason_code); |
2063 | 2260 | ||
2064 | ieee80211_send_deauth_disassoc(sdata, bssid, | 2261 | ieee80211_send_deauth_disassoc(sdata, bssid, IEEE80211_STYPE_DEAUTH, |
2065 | IEEE80211_STYPE_DEAUTH, req->reason_code, | 2262 | req->reason_code, cookie, |
2066 | cookie); | 2263 | !req->local_state_change); |
2067 | 2264 | ||
2068 | ieee80211_recalc_idle(sdata->local); | 2265 | ieee80211_recalc_idle(sdata->local); |
2069 | 2266 | ||
@@ -2075,6 +2272,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, | |||
2075 | void *cookie) | 2272 | void *cookie) |
2076 | { | 2273 | { |
2077 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | 2274 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; |
2275 | u8 bssid[ETH_ALEN]; | ||
2078 | 2276 | ||
2079 | mutex_lock(&ifmgd->mtx); | 2277 | mutex_lock(&ifmgd->mtx); |
2080 | 2278 | ||
@@ -2092,13 +2290,15 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, | |||
2092 | printk(KERN_DEBUG "%s: disassociating from %pM by local choice (reason=%d)\n", | 2290 | printk(KERN_DEBUG "%s: disassociating from %pM by local choice (reason=%d)\n", |
2093 | sdata->name, req->bss->bssid, req->reason_code); | 2291 | sdata->name, req->bss->bssid, req->reason_code); |
2094 | 2292 | ||
2095 | ieee80211_set_disassoc(sdata); | 2293 | memcpy(bssid, req->bss->bssid, ETH_ALEN); |
2294 | ieee80211_set_disassoc(sdata, false); | ||
2096 | 2295 | ||
2097 | mutex_unlock(&ifmgd->mtx); | 2296 | mutex_unlock(&ifmgd->mtx); |
2098 | 2297 | ||
2099 | ieee80211_send_deauth_disassoc(sdata, req->bss->bssid, | 2298 | ieee80211_send_deauth_disassoc(sdata, req->bss->bssid, |
2100 | IEEE80211_STYPE_DISASSOC, req->reason_code, | 2299 | IEEE80211_STYPE_DISASSOC, req->reason_code, |
2101 | cookie); | 2300 | cookie, !req->local_state_change); |
2301 | sta_info_destroy_addr(sdata, bssid); | ||
2102 | 2302 | ||
2103 | ieee80211_recalc_idle(sdata->local); | 2303 | ieee80211_recalc_idle(sdata->local); |
2104 | 2304 | ||
@@ -2118,7 +2318,7 @@ int ieee80211_mgd_action(struct ieee80211_sub_if_data *sdata, | |||
2118 | if ((chan != local->tmp_channel || | 2318 | if ((chan != local->tmp_channel || |
2119 | channel_type != local->tmp_channel_type) && | 2319 | channel_type != local->tmp_channel_type) && |
2120 | (chan != local->oper_channel || | 2320 | (chan != local->oper_channel || |
2121 | channel_type != local->oper_channel_type)) | 2321 | channel_type != local->_oper_channel_type)) |
2122 | return -EBUSY; | 2322 | return -EBUSY; |
2123 | 2323 | ||
2124 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + len); | 2324 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + len); |
@@ -2139,3 +2339,15 @@ int ieee80211_mgd_action(struct ieee80211_sub_if_data *sdata, | |||
2139 | *cookie = (unsigned long) skb; | 2339 | *cookie = (unsigned long) skb; |
2140 | return 0; | 2340 | return 0; |
2141 | } | 2341 | } |
2342 | |||
2343 | void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, | ||
2344 | enum nl80211_cqm_rssi_threshold_event rssi_event, | ||
2345 | gfp_t gfp) | ||
2346 | { | ||
2347 | struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); | ||
2348 | |||
2349 | trace_api_cqm_rssi_notify(sdata, rssi_event); | ||
2350 | |||
2351 | cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp); | ||
2352 | } | ||
2353 | EXPORT_SYMBOL(ieee80211_cqm_rssi_notify); | ||