diff options
Diffstat (limited to 'net/mac80211/mlme.c')
-rw-r--r-- | net/mac80211/mlme.c | 99 |
1 files changed, 98 insertions, 1 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 47bc3030ca8..41f3c1f98cc 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -486,6 +486,103 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, | |||
486 | ieee80211_tx_skb(sdata, skb, 0); | 486 | ieee80211_tx_skb(sdata, skb, 0); |
487 | } | 487 | } |
488 | 488 | ||
489 | /* spectrum management related things */ | ||
490 | static void ieee80211_chswitch_work(struct work_struct *work) | ||
491 | { | ||
492 | struct ieee80211_sub_if_data *sdata = | ||
493 | container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work); | ||
494 | struct ieee80211_bss *bss; | ||
495 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | ||
496 | |||
497 | if (!netif_running(sdata->dev)) | ||
498 | return; | ||
499 | |||
500 | bss = ieee80211_rx_bss_get(sdata->local, ifmgd->bssid, | ||
501 | sdata->local->hw.conf.channel->center_freq, | ||
502 | ifmgd->ssid, ifmgd->ssid_len); | ||
503 | if (!bss) | ||
504 | goto exit; | ||
505 | |||
506 | sdata->local->oper_channel = sdata->local->csa_channel; | ||
507 | /* XXX: shouldn't really modify cfg80211-owned data! */ | ||
508 | if (!ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL)) | ||
509 | bss->cbss.channel = sdata->local->oper_channel; | ||
510 | |||
511 | ieee80211_rx_bss_put(sdata->local, bss); | ||
512 | exit: | ||
513 | ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; | ||
514 | ieee80211_wake_queues_by_reason(&sdata->local->hw, | ||
515 | IEEE80211_QUEUE_STOP_REASON_CSA); | ||
516 | } | ||
517 | |||
518 | static void ieee80211_chswitch_timer(unsigned long data) | ||
519 | { | ||
520 | struct ieee80211_sub_if_data *sdata = | ||
521 | (struct ieee80211_sub_if_data *) data; | ||
522 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | ||
523 | |||
524 | queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work); | ||
525 | } | ||
526 | |||
527 | void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, | ||
528 | struct ieee80211_channel_sw_ie *sw_elem, | ||
529 | struct ieee80211_bss *bss) | ||
530 | { | ||
531 | struct ieee80211_channel *new_ch; | ||
532 | struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; | ||
533 | int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num); | ||
534 | |||
535 | if (ifmgd->state != IEEE80211_STA_MLME_ASSOCIATED) | ||
536 | return; | ||
537 | |||
538 | if (sdata->local->sw_scanning || sdata->local->hw_scanning) | ||
539 | return; | ||
540 | |||
541 | /* Disregard subsequent beacons if we are already running a timer | ||
542 | processing a CSA */ | ||
543 | |||
544 | if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED) | ||
545 | return; | ||
546 | |||
547 | new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); | ||
548 | if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED) | ||
549 | return; | ||
550 | |||
551 | sdata->local->csa_channel = new_ch; | ||
552 | |||
553 | if (sw_elem->count <= 1) { | ||
554 | queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work); | ||
555 | } else { | ||
556 | ieee80211_stop_queues_by_reason(&sdata->local->hw, | ||
557 | IEEE80211_QUEUE_STOP_REASON_CSA); | ||
558 | ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; | ||
559 | mod_timer(&ifmgd->chswitch_timer, | ||
560 | jiffies + | ||
561 | msecs_to_jiffies(sw_elem->count * | ||
562 | bss->cbss.beacon_interval)); | ||
563 | } | ||
564 | } | ||
565 | |||
566 | static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, | ||
567 | u16 capab_info, u8 *pwr_constr_elem, | ||
568 | u8 pwr_constr_elem_len) | ||
569 | { | ||
570 | struct ieee80211_conf *conf = &sdata->local->hw.conf; | ||
571 | |||
572 | if (!(capab_info & WLAN_CAPABILITY_SPECTRUM_MGMT)) | ||
573 | return; | ||
574 | |||
575 | /* Power constraint IE length should be 1 octet */ | ||
576 | if (pwr_constr_elem_len != 1) | ||
577 | return; | ||
578 | |||
579 | if ((*pwr_constr_elem <= conf->channel->max_power) && | ||
580 | (*pwr_constr_elem != sdata->local->power_constr_level)) { | ||
581 | sdata->local->power_constr_level = *pwr_constr_elem; | ||
582 | ieee80211_hw_config(sdata->local, 0); | ||
583 | } | ||
584 | } | ||
585 | |||
489 | /* powersave */ | 586 | /* powersave */ |
490 | static void ieee80211_enable_ps(struct ieee80211_local *local, | 587 | static void ieee80211_enable_ps(struct ieee80211_local *local, |
491 | struct ieee80211_sub_if_data *sdata) | 588 | struct ieee80211_sub_if_data *sdata) |
@@ -1736,7 +1833,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, | |||
1736 | (memcmp(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN) == 0)) { | 1833 | (memcmp(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN) == 0)) { |
1737 | struct ieee80211_channel_sw_ie *sw_elem = | 1834 | struct ieee80211_channel_sw_ie *sw_elem = |
1738 | (struct ieee80211_channel_sw_ie *)elems->ch_switch_elem; | 1835 | (struct ieee80211_channel_sw_ie *)elems->ch_switch_elem; |
1739 | ieee80211_process_chanswitch(sdata, sw_elem, bss); | 1836 | ieee80211_sta_process_chanswitch(sdata, sw_elem, bss); |
1740 | } | 1837 | } |
1741 | 1838 | ||
1742 | ieee80211_rx_bss_put(local, bss); | 1839 | ieee80211_rx_bss_put(local, bss); |