diff options
author | Arik Nemtsov <arik@wizery.com> | 2014-03-05 05:19:10 -0500 |
---|---|---|
committer | Emmanuel Grumbach <emmanuel.grumbach@intel.com> | 2015-03-12 03:57:22 -0400 |
commit | af45a9003f1f14af24804f7747f14238e8d51163 (patch) | |
tree | 8fe4b038d577987ec80ec9579264debad450a11f /drivers/net/wireless/iwlwifi/iwl-nvm-parse.c | |
parent | 90d4f7db6c5d8af1f4eab7bc714ec0ee130f9f00 (diff) |
iwlwifi: create regdomain from mcc_update_cmd response
Parse the NVM channel data and create a regulatory domain with a rule
for every 20Mhz channel. Use the AUTO_BW flag so the regulatory core
can unify single-channel rules into ranges.
Signed-off-by: Arik Nemtsov <arikx.nemtsov@intel.com>
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-nvm-parse.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-nvm-parse.c | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index c74f1a4edf23..d8d9a97ff14c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c | |||
@@ -643,3 +643,156 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, | |||
643 | return data; | 643 | return data; |
644 | } | 644 | } |
645 | IWL_EXPORT_SYMBOL(iwl_parse_nvm_data); | 645 | IWL_EXPORT_SYMBOL(iwl_parse_nvm_data); |
646 | |||
647 | static u32 iwl_nvm_get_regdom_bw_flags(const u8 *nvm_chan, | ||
648 | int ch_idx, u16 nvm_flags) | ||
649 | { | ||
650 | u32 flags = NL80211_RRF_NO_HT40; | ||
651 | |||
652 | if (ch_idx < NUM_2GHZ_CHANNELS && | ||
653 | (nvm_flags & NVM_CHANNEL_40MHZ)) { | ||
654 | if (nvm_chan[ch_idx] <= LAST_2GHZ_HT_PLUS) | ||
655 | flags &= ~NL80211_RRF_NO_HT40PLUS; | ||
656 | if (nvm_chan[ch_idx] >= FIRST_2GHZ_HT_MINUS) | ||
657 | flags &= ~NL80211_RRF_NO_HT40MINUS; | ||
658 | } else if (nvm_chan[ch_idx] <= LAST_5GHZ_HT && | ||
659 | (nvm_flags & NVM_CHANNEL_40MHZ)) { | ||
660 | if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0) | ||
661 | flags &= ~NL80211_RRF_NO_HT40PLUS; | ||
662 | else | ||
663 | flags &= ~NL80211_RRF_NO_HT40MINUS; | ||
664 | } | ||
665 | |||
666 | if (!(nvm_flags & NVM_CHANNEL_80MHZ)) | ||
667 | flags |= NL80211_RRF_NO_80MHZ; | ||
668 | if (!(nvm_flags & NVM_CHANNEL_160MHZ)) | ||
669 | flags |= NL80211_RRF_NO_160MHZ; | ||
670 | |||
671 | if (!(nvm_flags & NVM_CHANNEL_IBSS)) | ||
672 | flags |= NL80211_RRF_NO_IR; | ||
673 | |||
674 | if (!(nvm_flags & NVM_CHANNEL_ACTIVE)) | ||
675 | flags |= NL80211_RRF_NO_IR; | ||
676 | |||
677 | if (nvm_flags & NVM_CHANNEL_RADAR) | ||
678 | flags |= NL80211_RRF_DFS; | ||
679 | |||
680 | if (nvm_flags & NVM_CHANNEL_INDOOR_ONLY) | ||
681 | flags |= NL80211_RRF_NO_OUTDOOR; | ||
682 | |||
683 | /* Set the GO concurrent flag only in case that NO_IR is set. | ||
684 | * Otherwise it is meaningless | ||
685 | */ | ||
686 | if ((nvm_flags & NVM_CHANNEL_GO_CONCURRENT) && | ||
687 | (flags & NL80211_RRF_NO_IR)) | ||
688 | flags |= NL80211_RRF_GO_CONCURRENT; | ||
689 | |||
690 | return flags; | ||
691 | } | ||
692 | |||
693 | struct ieee80211_regdomain * | ||
694 | iwl_parse_nvm_mcc_info(struct device *dev, int num_of_ch, __le32 *channels, | ||
695 | u16 fw_mcc) | ||
696 | { | ||
697 | int ch_idx; | ||
698 | u16 ch_flags, prev_ch_flags = 0; | ||
699 | const u8 *nvm_chan = iwl_nvm_channels; /* TODO: 8000 series differs */ | ||
700 | struct ieee80211_regdomain *regd; | ||
701 | int size_of_regd; | ||
702 | struct ieee80211_reg_rule *rule; | ||
703 | enum ieee80211_band band; | ||
704 | int center_freq, prev_center_freq = 0; | ||
705 | int valid_rules = 0; | ||
706 | bool new_rule; | ||
707 | |||
708 | if (WARN_ON_ONCE(num_of_ch > NL80211_MAX_SUPP_REG_RULES)) | ||
709 | return ERR_PTR(-EINVAL); | ||
710 | |||
711 | IWL_DEBUG_DEV(dev, IWL_DL_LAR, "building regdom for %d channels\n", | ||
712 | num_of_ch); | ||
713 | |||
714 | /* build a regdomain rule for every valid channel */ | ||
715 | size_of_regd = | ||
716 | sizeof(struct ieee80211_regdomain) + | ||
717 | num_of_ch * sizeof(struct ieee80211_reg_rule); | ||
718 | |||
719 | regd = kzalloc(size_of_regd, GFP_KERNEL); | ||
720 | if (!regd) | ||
721 | return ERR_PTR(-ENOMEM); | ||
722 | |||
723 | for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) { | ||
724 | ch_flags = (u16)__le32_to_cpup(channels + ch_idx); | ||
725 | band = (ch_idx < NUM_2GHZ_CHANNELS) ? | ||
726 | IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; | ||
727 | center_freq = ieee80211_channel_to_frequency(nvm_chan[ch_idx], | ||
728 | band); | ||
729 | new_rule = false; | ||
730 | |||
731 | if (!(ch_flags & NVM_CHANNEL_VALID)) { | ||
732 | IWL_DEBUG_DEV(dev, IWL_DL_LAR, | ||
733 | "Ch. %d Flags %x [%sGHz] - No traffic\n", | ||
734 | nvm_chan[ch_idx], | ||
735 | ch_flags, | ||
736 | (ch_idx >= NUM_2GHZ_CHANNELS) ? | ||
737 | "5.2" : "2.4"); | ||
738 | continue; | ||
739 | } | ||
740 | |||
741 | /* we can't continue the same rule */ | ||
742 | if (ch_idx == 0 || prev_ch_flags != ch_flags || | ||
743 | center_freq - prev_center_freq > 20) { | ||
744 | valid_rules++; | ||
745 | new_rule = true; | ||
746 | } | ||
747 | |||
748 | rule = ®d->reg_rules[valid_rules - 1]; | ||
749 | |||
750 | if (new_rule) | ||
751 | rule->freq_range.start_freq_khz = | ||
752 | MHZ_TO_KHZ(center_freq - 10); | ||
753 | |||
754 | rule->freq_range.end_freq_khz = MHZ_TO_KHZ(center_freq + 10); | ||
755 | |||
756 | /* this doesn't matter - not used by FW */ | ||
757 | rule->power_rule.max_antenna_gain = DBI_TO_MBI(6); | ||
758 | rule->power_rule.max_eirp = DBM_TO_MBM(20); | ||
759 | |||
760 | rule->flags = iwl_nvm_get_regdom_bw_flags(nvm_chan, ch_idx, | ||
761 | ch_flags); | ||
762 | |||
763 | /* rely on auto-calculation to merge BW of contiguous chans */ | ||
764 | rule->flags |= NL80211_RRF_AUTO_BW; | ||
765 | rule->freq_range.max_bandwidth_khz = 0; | ||
766 | |||
767 | prev_ch_flags = ch_flags; | ||
768 | prev_center_freq = center_freq; | ||
769 | |||
770 | IWL_DEBUG_DEV(dev, IWL_DL_LAR, | ||
771 | "Ch. %d [%sGHz] %s%s%s%s%s%s%s%s%s%s(0x%02x): Ad-Hoc %ssupported\n", | ||
772 | center_freq, | ||
773 | band == IEEE80211_BAND_5GHZ ? "5.2" : "2.4", | ||
774 | CHECK_AND_PRINT_I(VALID), | ||
775 | CHECK_AND_PRINT_I(IBSS), | ||
776 | CHECK_AND_PRINT_I(ACTIVE), | ||
777 | CHECK_AND_PRINT_I(RADAR), | ||
778 | CHECK_AND_PRINT_I(WIDE), | ||
779 | CHECK_AND_PRINT_I(40MHZ), | ||
780 | CHECK_AND_PRINT_I(80MHZ), | ||
781 | CHECK_AND_PRINT_I(160MHZ), | ||
782 | CHECK_AND_PRINT_I(INDOOR_ONLY), | ||
783 | CHECK_AND_PRINT_I(GO_CONCURRENT), | ||
784 | ch_flags, | ||
785 | ((ch_flags & NVM_CHANNEL_IBSS) && | ||
786 | !(ch_flags & NVM_CHANNEL_RADAR)) | ||
787 | ? "" : "not "); | ||
788 | } | ||
789 | |||
790 | regd->n_reg_rules = valid_rules; | ||
791 | |||
792 | /* set alpha2 from FW. */ | ||
793 | regd->alpha2[0] = fw_mcc >> 8; | ||
794 | regd->alpha2[1] = fw_mcc & 0xff; | ||
795 | |||
796 | return regd; | ||
797 | } | ||
798 | IWL_EXPORT_SYMBOL(iwl_parse_nvm_mcc_info); | ||