diff options
Diffstat (limited to 'net/mac80211/main.c')
-rw-r--r-- | net/mac80211/main.c | 105 |
1 files changed, 77 insertions, 28 deletions
diff --git a/net/mac80211/main.c b/net/mac80211/main.c index c80c4490351c..fd8345c20051 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c | |||
@@ -93,23 +93,21 @@ static void ieee80211_reconfig_filter(struct work_struct *work) | |||
93 | ieee80211_configure_filter(local); | 93 | ieee80211_configure_filter(local); |
94 | } | 94 | } |
95 | 95 | ||
96 | int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) | 96 | static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local) |
97 | { | 97 | { |
98 | struct ieee80211_channel *chan; | 98 | struct ieee80211_channel *chan; |
99 | int ret = 0; | 99 | u32 changed = 0; |
100 | int power; | 100 | int power; |
101 | enum nl80211_channel_type channel_type; | 101 | enum nl80211_channel_type channel_type; |
102 | u32 offchannel_flag; | 102 | u32 offchannel_flag; |
103 | 103 | ||
104 | might_sleep(); | ||
105 | |||
106 | offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL; | 104 | offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL; |
107 | if (local->scan_channel) { | 105 | if (local->scan_channel) { |
108 | chan = local->scan_channel; | 106 | chan = local->scan_channel; |
109 | /* If scanning on oper channel, use whatever channel-type | 107 | /* If scanning on oper channel, use whatever channel-type |
110 | * is currently in use. | 108 | * is currently in use. |
111 | */ | 109 | */ |
112 | if (chan == local->oper_channel) | 110 | if (chan == local->_oper_channel) |
113 | channel_type = local->_oper_channel_type; | 111 | channel_type = local->_oper_channel_type; |
114 | else | 112 | else |
115 | channel_type = NL80211_CHAN_NO_HT; | 113 | channel_type = NL80211_CHAN_NO_HT; |
@@ -117,11 +115,11 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) | |||
117 | chan = local->tmp_channel; | 115 | chan = local->tmp_channel; |
118 | channel_type = local->tmp_channel_type; | 116 | channel_type = local->tmp_channel_type; |
119 | } else { | 117 | } else { |
120 | chan = local->oper_channel; | 118 | chan = local->_oper_channel; |
121 | channel_type = local->_oper_channel_type; | 119 | channel_type = local->_oper_channel_type; |
122 | } | 120 | } |
123 | 121 | ||
124 | if (chan != local->oper_channel || | 122 | if (chan != local->_oper_channel || |
125 | channel_type != local->_oper_channel_type) | 123 | channel_type != local->_oper_channel_type) |
126 | local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL; | 124 | local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL; |
127 | else | 125 | else |
@@ -164,6 +162,21 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) | |||
164 | local->hw.conf.power_level = power; | 162 | local->hw.conf.power_level = power; |
165 | } | 163 | } |
166 | 164 | ||
165 | return changed; | ||
166 | } | ||
167 | |||
168 | int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) | ||
169 | { | ||
170 | int ret = 0; | ||
171 | |||
172 | might_sleep(); | ||
173 | |||
174 | if (!local->use_chanctx) | ||
175 | changed |= ieee80211_hw_conf_chan(local); | ||
176 | else | ||
177 | changed &= ~(IEEE80211_CONF_CHANGE_CHANNEL | | ||
178 | IEEE80211_CONF_CHANGE_POWER); | ||
179 | |||
167 | if (changed && local->open_count) { | 180 | if (changed && local->open_count) { |
168 | ret = drv_config(local, changed); | 181 | ret = drv_config(local, changed); |
169 | /* | 182 | /* |
@@ -359,14 +372,6 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw) | |||
359 | } | 372 | } |
360 | EXPORT_SYMBOL(ieee80211_restart_hw); | 373 | EXPORT_SYMBOL(ieee80211_restart_hw); |
361 | 374 | ||
362 | static void ieee80211_recalc_smps_work(struct work_struct *work) | ||
363 | { | ||
364 | struct ieee80211_local *local = | ||
365 | container_of(work, struct ieee80211_local, recalc_smps); | ||
366 | |||
367 | ieee80211_recalc_smps(local); | ||
368 | } | ||
369 | |||
370 | #ifdef CONFIG_INET | 375 | #ifdef CONFIG_INET |
371 | static int ieee80211_ifa_changed(struct notifier_block *nb, | 376 | static int ieee80211_ifa_changed(struct notifier_block *nb, |
372 | unsigned long data, void *arg) | 377 | unsigned long data, void *arg) |
@@ -540,6 +545,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, | |||
540 | struct ieee80211_local *local; | 545 | struct ieee80211_local *local; |
541 | int priv_size, i; | 546 | int priv_size, i; |
542 | struct wiphy *wiphy; | 547 | struct wiphy *wiphy; |
548 | bool use_chanctx; | ||
543 | 549 | ||
544 | if (WARN_ON(!ops->tx || !ops->start || !ops->stop || !ops->config || | 550 | if (WARN_ON(!ops->tx || !ops->start || !ops->stop || !ops->config || |
545 | !ops->add_interface || !ops->remove_interface || | 551 | !ops->add_interface || !ops->remove_interface || |
@@ -549,6 +555,14 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, | |||
549 | if (WARN_ON(ops->sta_state && (ops->sta_add || ops->sta_remove))) | 555 | if (WARN_ON(ops->sta_state && (ops->sta_add || ops->sta_remove))) |
550 | return NULL; | 556 | return NULL; |
551 | 557 | ||
558 | /* check all or no channel context operations exist */ | ||
559 | i = !!ops->add_chanctx + !!ops->remove_chanctx + | ||
560 | !!ops->change_chanctx + !!ops->assign_vif_chanctx + | ||
561 | !!ops->unassign_vif_chanctx; | ||
562 | if (WARN_ON(i != 0 && i != 5)) | ||
563 | return NULL; | ||
564 | use_chanctx = i == 5; | ||
565 | |||
552 | /* Ensure 32-byte alignment of our private data and hw private data. | 566 | /* Ensure 32-byte alignment of our private data and hw private data. |
553 | * We use the wiphy priv data for both our ieee80211_local and for | 567 | * We use the wiphy priv data for both our ieee80211_local and for |
554 | * the driver's private data | 568 | * the driver's private data |
@@ -584,8 +598,14 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, | |||
584 | if (ops->remain_on_channel) | 598 | if (ops->remain_on_channel) |
585 | wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; | 599 | wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; |
586 | 600 | ||
587 | wiphy->features = NL80211_FEATURE_SK_TX_STATUS | | 601 | wiphy->features |= NL80211_FEATURE_SK_TX_STATUS | |
588 | NL80211_FEATURE_HT_IBSS; | 602 | NL80211_FEATURE_SAE | |
603 | NL80211_FEATURE_HT_IBSS; | ||
604 | |||
605 | if (!ops->hw_scan) | ||
606 | wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN | | ||
607 | NL80211_FEATURE_AP_SCAN; | ||
608 | |||
589 | 609 | ||
590 | if (!ops->set_key) | 610 | if (!ops->set_key) |
591 | wiphy->flags |= WIPHY_FLAG_IBSS_RSN; | 611 | wiphy->flags |= WIPHY_FLAG_IBSS_RSN; |
@@ -599,6 +619,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, | |||
599 | local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN); | 619 | local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN); |
600 | 620 | ||
601 | local->ops = ops; | 621 | local->ops = ops; |
622 | local->use_chanctx = use_chanctx; | ||
602 | 623 | ||
603 | /* set up some defaults */ | 624 | /* set up some defaults */ |
604 | local->hw.queues = 1; | 625 | local->hw.queues = 1; |
@@ -626,6 +647,9 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, | |||
626 | spin_lock_init(&local->filter_lock); | 647 | spin_lock_init(&local->filter_lock); |
627 | spin_lock_init(&local->queue_stop_reason_lock); | 648 | spin_lock_init(&local->queue_stop_reason_lock); |
628 | 649 | ||
650 | INIT_LIST_HEAD(&local->chanctx_list); | ||
651 | mutex_init(&local->chanctx_mtx); | ||
652 | |||
629 | /* | 653 | /* |
630 | * The rx_skb_queue is only accessed from tasklets, | 654 | * The rx_skb_queue is only accessed from tasklets, |
631 | * but other SKB queues are used from within IRQ | 655 | * but other SKB queues are used from within IRQ |
@@ -641,7 +665,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, | |||
641 | INIT_WORK(&local->restart_work, ieee80211_restart_work); | 665 | INIT_WORK(&local->restart_work, ieee80211_restart_work); |
642 | 666 | ||
643 | INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter); | 667 | INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter); |
644 | INIT_WORK(&local->recalc_smps, ieee80211_recalc_smps_work); | ||
645 | local->smps_mode = IEEE80211_SMPS_OFF; | 668 | local->smps_mode = IEEE80211_SMPS_OFF; |
646 | 669 | ||
647 | INIT_WORK(&local->dynamic_ps_enable_work, | 670 | INIT_WORK(&local->dynamic_ps_enable_work, |
@@ -719,6 +742,25 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) | |||
719 | if ((hw->flags & IEEE80211_HW_SCAN_WHILE_IDLE) && !local->ops->hw_scan) | 742 | if ((hw->flags & IEEE80211_HW_SCAN_WHILE_IDLE) && !local->ops->hw_scan) |
720 | return -EINVAL; | 743 | return -EINVAL; |
721 | 744 | ||
745 | if (!local->use_chanctx) { | ||
746 | for (i = 0; i < local->hw.wiphy->n_iface_combinations; i++) { | ||
747 | const struct ieee80211_iface_combination *comb; | ||
748 | |||
749 | comb = &local->hw.wiphy->iface_combinations[i]; | ||
750 | |||
751 | if (comb->num_different_channels > 1) | ||
752 | return -EINVAL; | ||
753 | } | ||
754 | } else { | ||
755 | /* | ||
756 | * WDS is currently prohibited when channel contexts are used | ||
757 | * because there's no clear definition of which channel WDS | ||
758 | * type interfaces use | ||
759 | */ | ||
760 | if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS)) | ||
761 | return -EINVAL; | ||
762 | } | ||
763 | |||
722 | /* Only HW csum features are currently compatible with mac80211 */ | 764 | /* Only HW csum features are currently compatible with mac80211 */ |
723 | feature_whitelist = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | | 765 | feature_whitelist = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | |
724 | NETIF_F_HW_CSUM; | 766 | NETIF_F_HW_CSUM; |
@@ -728,6 +770,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) | |||
728 | if (hw->max_report_rates == 0) | 770 | if (hw->max_report_rates == 0) |
729 | hw->max_report_rates = hw->max_rates; | 771 | hw->max_report_rates = hw->max_rates; |
730 | 772 | ||
773 | local->rx_chains = 1; | ||
774 | |||
731 | /* | 775 | /* |
732 | * generic code guarantees at least one band, | 776 | * generic code guarantees at least one band, |
733 | * set this very early because much code assumes | 777 | * set this very early because much code assumes |
@@ -743,18 +787,29 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) | |||
743 | sband = local->hw.wiphy->bands[band]; | 787 | sband = local->hw.wiphy->bands[band]; |
744 | if (!sband) | 788 | if (!sband) |
745 | continue; | 789 | continue; |
746 | if (!local->oper_channel) { | 790 | if (!local->use_chanctx && !local->_oper_channel) { |
747 | /* init channel we're on */ | 791 | /* init channel we're on */ |
748 | local->hw.conf.channel = | 792 | local->hw.conf.channel = |
749 | local->oper_channel = &sband->channels[0]; | 793 | local->_oper_channel = &sband->channels[0]; |
750 | local->hw.conf.channel_type = NL80211_CHAN_NO_HT; | 794 | local->hw.conf.channel_type = NL80211_CHAN_NO_HT; |
751 | } | 795 | } |
796 | if (!local->monitor_channel) { | ||
797 | local->monitor_channel = &sband->channels[0]; | ||
798 | local->monitor_channel_type = NL80211_CHAN_NO_HT; | ||
799 | } | ||
752 | channels += sband->n_channels; | 800 | channels += sband->n_channels; |
753 | 801 | ||
754 | if (max_bitrates < sband->n_bitrates) | 802 | if (max_bitrates < sband->n_bitrates) |
755 | max_bitrates = sband->n_bitrates; | 803 | max_bitrates = sband->n_bitrates; |
756 | supp_ht = supp_ht || sband->ht_cap.ht_supported; | 804 | supp_ht = supp_ht || sband->ht_cap.ht_supported; |
757 | supp_vht = supp_vht || sband->vht_cap.vht_supported; | 805 | supp_vht = supp_vht || sband->vht_cap.vht_supported; |
806 | |||
807 | if (sband->ht_cap.ht_supported) | ||
808 | local->rx_chains = | ||
809 | max(ieee80211_mcs_to_chains(&sband->ht_cap.mcs), | ||
810 | local->rx_chains); | ||
811 | |||
812 | /* TODO: consider VHT for RX chains, hopefully it's the same */ | ||
758 | } | 813 | } |
759 | 814 | ||
760 | local->int_scan_req = kzalloc(sizeof(*local->int_scan_req) + | 815 | local->int_scan_req = kzalloc(sizeof(*local->int_scan_req) + |
@@ -778,19 +833,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) | |||
778 | hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); | 833 | hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); |
779 | hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR); | 834 | hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR); |
780 | 835 | ||
781 | /* | 836 | /* mac80211 doesn't support more than one IBSS interface right now */ |
782 | * mac80211 doesn't support more than 1 channel, and also not more | ||
783 | * than one IBSS interface | ||
784 | */ | ||
785 | for (i = 0; i < hw->wiphy->n_iface_combinations; i++) { | 837 | for (i = 0; i < hw->wiphy->n_iface_combinations; i++) { |
786 | const struct ieee80211_iface_combination *c; | 838 | const struct ieee80211_iface_combination *c; |
787 | int j; | 839 | int j; |
788 | 840 | ||
789 | c = &hw->wiphy->iface_combinations[i]; | 841 | c = &hw->wiphy->iface_combinations[i]; |
790 | 842 | ||
791 | if (c->num_different_channels > 1) | ||
792 | return -EINVAL; | ||
793 | |||
794 | for (j = 0; j < c->n_limits; j++) | 843 | for (j = 0; j < c->n_limits; j++) |
795 | if ((c->limits[j].types & BIT(NL80211_IFTYPE_ADHOC)) && | 844 | if ((c->limits[j].types & BIT(NL80211_IFTYPE_ADHOC)) && |
796 | c->limits[j].max > 1) | 845 | c->limits[j].max > 1) |
@@ -832,7 +881,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) | |||
832 | 881 | ||
833 | if (supp_vht) | 882 | if (supp_vht) |
834 | local->scan_ies_len += | 883 | local->scan_ies_len += |
835 | 2 + sizeof(struct ieee80211_vht_capabilities); | 884 | 2 + sizeof(struct ieee80211_vht_cap); |
836 | 885 | ||
837 | if (!local->ops->hw_scan) { | 886 | if (!local->ops->hw_scan) { |
838 | /* For hw_scan, driver needs to set these up. */ | 887 | /* For hw_scan, driver needs to set these up. */ |