@@ -5551,6 +5551,10 @@ struct wiphy {
u16 hw_timestamp_max_peers;
+#ifdef CONFIG_ACPI_WBRF
+ bool wbrf_supported;
+#endif
+
char priv[] __aligned(NETDEV_ALIGN);
};
@@ -9067,4 +9071,18 @@ static inline int cfg80211_color_change_notify(struct net_device *dev)
bool cfg80211_valid_disable_subchannel_bitmap(u16 *bitmap,
const struct cfg80211_chan_def *chandef);
+#ifdef CONFIG_ACPI_WBRF
+void ieee80211_check_wbrf_support(struct wiphy *wiphy);
+int ieee80211_add_wbrf(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef);
+void ieee80211_remove_wbrf(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef);
+#else
+static inline void ieee80211_check_wbrf_support(struct wiphy *wiphy) { }
+static inline int ieee80211_add_wbrf(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef) { return 0; }
+static inline void ieee80211_remove_wbrf(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef) { }
+#endif /* CONFIG_ACPI_WBRF */
+
#endif /* __NET_CFG80211_H */
@@ -65,4 +65,6 @@ rc80211_minstrel-$(CONFIG_MAC80211_DEBUGFS) += \
mac80211-$(CONFIG_MAC80211_RC_MINSTREL) += $(rc80211_minstrel-y)
+mac80211-$(CONFIG_ACPI_WBRF) += wbrf.o
+
ccflags-y += -DDEBUG
@@ -668,6 +668,10 @@ static int ieee80211_add_chanctx(struct ieee80211_local *local,
lockdep_assert_held(&local->mtx);
lockdep_assert_held(&local->chanctx_mtx);
+ err = ieee80211_add_wbrf(local->hw.wiphy, &ctx->conf.def);
+ if (err)
+ return err;
+
if (!local->use_chanctx)
local->hw.conf.radar_enabled = ctx->conf.radar_enabled;
@@ -748,6 +752,8 @@ static void ieee80211_del_chanctx(struct ieee80211_local *local,
}
ieee80211_recalc_idle(local);
+
+ ieee80211_remove_wbrf(local->hw.wiphy, &ctx->conf.def);
}
static void ieee80211_free_chanctx(struct ieee80211_local *local,
@@ -1395,6 +1395,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
debugfs_hw_add(local);
rate_control_add_debugfs(local);
+ ieee80211_check_wbrf_support(local->hw.wiphy);
+
rtnl_lock();
wiphy_lock(hw->wiphy);
new file mode 100644
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AMD Wifi Band Exclusion Interface
+ * Copyright (C) 2023 Advanced Micro Devices
+ *
+ */
+
+#include <linux/wbrf.h>
+#include <net/cfg80211.h>
+
+#define KHZ_TO_HZ(freq) ((freq) * 1000ULL)
+
+void ieee80211_check_wbrf_support(struct wiphy *wiphy)
+{
+ struct device *dev = wiphy->dev.parent;
+ struct acpi_device *acpi_dev;
+
+ acpi_dev = ACPI_COMPANION(dev);
+ if (!acpi_dev) {
+ dev_dbg(dev, "ACPI companion not found\n");
+ return;
+ }
+
+ wiphy->wbrf_supported = wbrf_supported_producer(acpi_dev);
+ dev_dbg(dev, "WBRF is %s supported\n",
+ wiphy->wbrf_supported ? "" : "not");
+}
+
+static int chan_width_to_mhz(enum nl80211_chan_width chan_width)
+{
+ int mhz;
+
+ switch (chan_width) {
+ case NL80211_CHAN_WIDTH_1:
+ mhz = 1;
+ break;
+ case NL80211_CHAN_WIDTH_2:
+ mhz = 2;
+ break;
+ case NL80211_CHAN_WIDTH_4:
+ mhz = 4;
+ break;
+ case NL80211_CHAN_WIDTH_8:
+ mhz = 8;
+ break;
+ case NL80211_CHAN_WIDTH_16:
+ mhz = 16;
+ break;
+ case NL80211_CHAN_WIDTH_5:
+ mhz = 5;
+ break;
+ case NL80211_CHAN_WIDTH_10:
+ mhz = 10;
+ break;
+ case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ mhz = 20;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ mhz = 40;
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ case NL80211_CHAN_WIDTH_80:
+ mhz = 80;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ mhz = 160;
+ break;
+ case NL80211_CHAN_WIDTH_320:
+ mhz = 320;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return -1;
+ }
+ return mhz;
+}
+
+static void get_chan_freq_boundary(u32 center_freq,
+ u32 bandwidth,
+ u64 *start,
+ u64 *end)
+{
+ bandwidth = MHZ_TO_KHZ(bandwidth);
+ center_freq = MHZ_TO_KHZ(center_freq);
+
+ *start = center_freq - bandwidth / 2;
+ *end = center_freq + bandwidth / 2;
+
+ /* Frequency in HZ is expected */
+ *start = KHZ_TO_HZ(*start);
+ *end = KHZ_TO_HZ(*end);
+}
+
+static int wbrf_get_ranges_from_chandef(struct cfg80211_chan_def *chandef,
+ struct wbrf_ranges_in *ranges_in)
+{
+ u64 start_freq1, end_freq1;
+ u64 start_freq2, end_freq2;
+ int bandwidth;
+
+ bandwidth = chan_width_to_mhz(chandef->width);
+ if (bandwidth < 0)
+ return -EINVAL;
+
+ get_chan_freq_boundary(chandef->center_freq1,
+ bandwidth,
+ &start_freq1,
+ &end_freq1);
+
+ ranges_in->band_list[0].start = start_freq1;
+ ranges_in->band_list[0].end = end_freq1;
+
+ if (chandef->width == NL80211_CHAN_WIDTH_80P80) {
+ get_chan_freq_boundary(chandef->center_freq2,
+ bandwidth,
+ &start_freq2,
+ &end_freq2);
+
+ ranges_in->band_list[1].start = start_freq2;
+ ranges_in->band_list[1].end = end_freq2;
+ }
+
+ return 0;
+}
+
+static int wbrf_add_exclusion_wlan(struct acpi_device *adev,
+ struct cfg80211_chan_def *chandef)
+{
+ struct wbrf_ranges_in ranges_in = {0};
+ int ret;
+
+ ret = wbrf_get_ranges_from_chandef(chandef, &ranges_in);
+ if (ret)
+ return ret;
+
+ return wbrf_add_exclusion(adev, &ranges_in);
+}
+
+static int wbrf_remove_exclusion_wlan(struct acpi_device *adev,
+ struct cfg80211_chan_def *chandef)
+{
+ struct wbrf_ranges_in ranges_in = {0};
+ int ret;
+
+ ret = wbrf_get_ranges_from_chandef(chandef, &ranges_in);
+ if (ret)
+ return ret;
+
+ return wbrf_remove_exclusion(adev, &ranges_in);
+}
+
+int ieee80211_add_wbrf(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef)
+{
+ struct device *dev = wiphy->dev.parent;
+ struct acpi_device *acpi_dev;
+
+ if (!wiphy->wbrf_supported)
+ return 0;
+
+ acpi_dev = ACPI_COMPANION(dev);
+ if (!acpi_dev)
+ return -ENODEV;
+
+ return wbrf_add_exclusion_wlan(acpi_dev, chandef);
+}
+
+void ieee80211_remove_wbrf(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef)
+{
+ struct device *dev = wiphy->dev.parent;
+ struct acpi_device *acpi_dev;
+
+ if (!wiphy->wbrf_supported)
+ return;
+
+ acpi_dev = ACPI_COMPANION(dev);
+ if (!acpi_dev)
+ return;
+
+ wbrf_remove_exclusion_wlan(acpi_dev, chandef);
+}