root/net/mac80211/wbrf.c
// SPDX-License-Identifier: GPL-2.0
/*
 * Wifi Band Exclusion Interface for WLAN
 * Copyright (C) 2023 Advanced Micro Devices
 * Copyright (C) 2025 Intel Corporation
 *
 */

#include <linux/acpi_amd_wbrf.h>
#include <linux/units.h>
#include <net/cfg80211.h>
#include "ieee80211_i.h"

void ieee80211_check_wbrf_support(struct ieee80211_local *local)
{
        struct wiphy *wiphy = local->hw.wiphy;
        struct device *dev;

        if (!wiphy)
                return;

        dev = wiphy->dev.parent;
        if (!dev)
                return;

        local->wbrf_supported = acpi_amd_wbrf_supported_producer(dev);
}

static void get_chan_freq_boundary(u32 center_freq, u32 bandwidth, u64 *start, u64 *end)
{
        bandwidth *= KHZ_PER_MHZ;
        center_freq *= KHZ_PER_MHZ;

        *start = center_freq - bandwidth / 2;
        *end = center_freq + bandwidth / 2;

        /* Frequency in Hz is expected */
        *start = *start * HZ_PER_KHZ;
        *end = *end * HZ_PER_KHZ;
}

static void get_ranges_from_chandef(struct cfg80211_chan_def *chandef,
                                    struct wbrf_ranges_in_out *ranges_in)
{
        u64 start_freq1, end_freq1;
        u64 start_freq2, end_freq2;
        int bandwidth;

        bandwidth = cfg80211_chandef_get_width(chandef);

        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;
        ranges_in->num_of_ranges = 1;

        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;
                ranges_in->num_of_ranges++;
        }
}

void ieee80211_add_wbrf(struct ieee80211_local *local, struct cfg80211_chan_def *chandef)
{
        struct wbrf_ranges_in_out ranges_in = {0};
        struct device *dev;

        if (!local->wbrf_supported)
                return;

        dev = local->hw.wiphy->dev.parent;

        get_ranges_from_chandef(chandef, &ranges_in);

        acpi_amd_wbrf_add_remove(dev, WBRF_RECORD_ADD, &ranges_in);
}

void ieee80211_remove_wbrf(struct ieee80211_local *local, struct cfg80211_chan_def *chandef)
{
        struct wbrf_ranges_in_out ranges_in = {0};
        struct device *dev;

        if (!local->wbrf_supported)
                return;

        dev = local->hw.wiphy->dev.parent;

        get_ranges_from_chandef(chandef, &ranges_in);

        acpi_amd_wbrf_add_remove(dev, WBRF_RECORD_REMOVE, &ranges_in);
}