18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (C) 2017 Rafał Miłecki <rafal@milecki.pl>
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any
58c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above
68c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
98c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
108c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
118c2ecf20Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
128c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
138c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
148c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/of.h>
188c2ecf20Sopenharmony_ci#include <net/cfg80211.h>
198c2ecf20Sopenharmony_ci#include "core.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic bool wiphy_freq_limits_valid_chan(struct wiphy *wiphy,
228c2ecf20Sopenharmony_ci					 struct ieee80211_freq_range *freq_limits,
238c2ecf20Sopenharmony_ci					 unsigned int n_freq_limits,
248c2ecf20Sopenharmony_ci					 struct ieee80211_channel *chan)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	u32 bw = MHZ_TO_KHZ(20);
278c2ecf20Sopenharmony_ci	int i;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	for (i = 0; i < n_freq_limits; i++) {
308c2ecf20Sopenharmony_ci		struct ieee80211_freq_range *limit = &freq_limits[i];
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci		if (cfg80211_does_bw_fit_range(limit,
338c2ecf20Sopenharmony_ci					       MHZ_TO_KHZ(chan->center_freq),
348c2ecf20Sopenharmony_ci					       bw))
358c2ecf20Sopenharmony_ci			return true;
368c2ecf20Sopenharmony_ci	}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	return false;
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic void wiphy_freq_limits_apply(struct wiphy *wiphy,
428c2ecf20Sopenharmony_ci				    struct ieee80211_freq_range *freq_limits,
438c2ecf20Sopenharmony_ci				    unsigned int n_freq_limits)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	enum nl80211_band band;
468c2ecf20Sopenharmony_ci	int i;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	if (WARN_ON(!n_freq_limits))
498c2ecf20Sopenharmony_ci		return;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	for (band = 0; band < NUM_NL80211_BANDS; band++) {
528c2ecf20Sopenharmony_ci		struct ieee80211_supported_band *sband = wiphy->bands[band];
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci		if (!sband)
558c2ecf20Sopenharmony_ci			continue;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci		for (i = 0; i < sband->n_channels; i++) {
588c2ecf20Sopenharmony_ci			struct ieee80211_channel *chan = &sband->channels[i];
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci			if (chan->flags & IEEE80211_CHAN_DISABLED)
618c2ecf20Sopenharmony_ci				continue;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci			if (!wiphy_freq_limits_valid_chan(wiphy, freq_limits,
648c2ecf20Sopenharmony_ci							  n_freq_limits,
658c2ecf20Sopenharmony_ci							  chan)) {
668c2ecf20Sopenharmony_ci				pr_debug("Disabling freq %d MHz as it's out of OF limits\n",
678c2ecf20Sopenharmony_ci					 chan->center_freq);
688c2ecf20Sopenharmony_ci				chan->flags |= IEEE80211_CHAN_DISABLED;
698c2ecf20Sopenharmony_ci			}
708c2ecf20Sopenharmony_ci		}
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_civoid wiphy_read_of_freq_limits(struct wiphy *wiphy)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	struct device *dev = wiphy_dev(wiphy);
778c2ecf20Sopenharmony_ci	struct device_node *np;
788c2ecf20Sopenharmony_ci	struct property *prop;
798c2ecf20Sopenharmony_ci	struct ieee80211_freq_range *freq_limits;
808c2ecf20Sopenharmony_ci	unsigned int n_freq_limits;
818c2ecf20Sopenharmony_ci	const __be32 *p;
828c2ecf20Sopenharmony_ci	int len, i;
838c2ecf20Sopenharmony_ci	int err = 0;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	if (!dev)
868c2ecf20Sopenharmony_ci		return;
878c2ecf20Sopenharmony_ci	np = dev_of_node(dev);
888c2ecf20Sopenharmony_ci	if (!np)
898c2ecf20Sopenharmony_ci		return;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	prop = of_find_property(np, "ieee80211-freq-limit", &len);
928c2ecf20Sopenharmony_ci	if (!prop)
938c2ecf20Sopenharmony_ci		return;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (!len || len % sizeof(u32) || len / sizeof(u32) % 2) {
968c2ecf20Sopenharmony_ci		dev_err(dev, "ieee80211-freq-limit wrong format");
978c2ecf20Sopenharmony_ci		return;
988c2ecf20Sopenharmony_ci	}
998c2ecf20Sopenharmony_ci	n_freq_limits = len / sizeof(u32) / 2;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	freq_limits = kcalloc(n_freq_limits, sizeof(*freq_limits), GFP_KERNEL);
1028c2ecf20Sopenharmony_ci	if (!freq_limits) {
1038c2ecf20Sopenharmony_ci		err = -ENOMEM;
1048c2ecf20Sopenharmony_ci		goto out_kfree;
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	p = NULL;
1088c2ecf20Sopenharmony_ci	for (i = 0; i < n_freq_limits; i++) {
1098c2ecf20Sopenharmony_ci		struct ieee80211_freq_range *limit = &freq_limits[i];
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci		p = of_prop_next_u32(prop, p, &limit->start_freq_khz);
1128c2ecf20Sopenharmony_ci		if (!p) {
1138c2ecf20Sopenharmony_ci			err = -EINVAL;
1148c2ecf20Sopenharmony_ci			goto out_kfree;
1158c2ecf20Sopenharmony_ci		}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci		p = of_prop_next_u32(prop, p, &limit->end_freq_khz);
1188c2ecf20Sopenharmony_ci		if (!p) {
1198c2ecf20Sopenharmony_ci			err = -EINVAL;
1208c2ecf20Sopenharmony_ci			goto out_kfree;
1218c2ecf20Sopenharmony_ci		}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci		if (!limit->start_freq_khz ||
1248c2ecf20Sopenharmony_ci		    !limit->end_freq_khz ||
1258c2ecf20Sopenharmony_ci		    limit->start_freq_khz >= limit->end_freq_khz) {
1268c2ecf20Sopenharmony_ci			err = -EINVAL;
1278c2ecf20Sopenharmony_ci			goto out_kfree;
1288c2ecf20Sopenharmony_ci		}
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	wiphy_freq_limits_apply(wiphy, freq_limits, n_freq_limits);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ciout_kfree:
1348c2ecf20Sopenharmony_ci	kfree(freq_limits);
1358c2ecf20Sopenharmony_ci	if (err)
1368c2ecf20Sopenharmony_ci		dev_err(dev, "Failed to get limits: %d\n", err);
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(wiphy_read_of_freq_limits);
139