xref: /linux/net/wireless/of.c (revision dfecb0c5af3b07ebfa84be63a7a21bfc9e29a872)
1 // SPDX-License-Identifier: ISC
2 /*
3  * Copyright (C) 2017 Rafał Miłecki <rafal@milecki.pl>
4  */
5 
6 #include <linux/of.h>
7 #include <net/cfg80211.h>
8 #include "core.h"
9 
10 static bool wiphy_freq_limits_valid_chan(struct wiphy *wiphy,
11 					 struct ieee80211_freq_range *freq_limits,
12 					 unsigned int n_freq_limits,
13 					 struct ieee80211_channel *chan)
14 {
15 	u32 bw = MHZ_TO_KHZ(20);
16 	int i;
17 
18 	for (i = 0; i < n_freq_limits; i++) {
19 		struct ieee80211_freq_range *limit = &freq_limits[i];
20 
21 		if (cfg80211_does_bw_fit_range(limit,
22 					       MHZ_TO_KHZ(chan->center_freq),
23 					       bw))
24 			return true;
25 	}
26 
27 	return false;
28 }
29 
30 static void wiphy_freq_limits_apply(struct wiphy *wiphy,
31 				    struct ieee80211_freq_range *freq_limits,
32 				    unsigned int n_freq_limits)
33 {
34 	enum nl80211_band band;
35 	int i;
36 
37 	if (WARN_ON(!n_freq_limits))
38 		return;
39 
40 	for (band = 0; band < NUM_NL80211_BANDS; band++) {
41 		struct ieee80211_supported_band *sband = wiphy->bands[band];
42 
43 		if (!sband)
44 			continue;
45 
46 		for (i = 0; i < sband->n_channels; i++) {
47 			struct ieee80211_channel *chan = &sband->channels[i];
48 
49 			if (chan->flags & IEEE80211_CHAN_DISABLED)
50 				continue;
51 
52 			if (!wiphy_freq_limits_valid_chan(wiphy, freq_limits,
53 							  n_freq_limits,
54 							  chan)) {
55 				pr_debug("Disabling freq %d MHz as it's out of OF limits\n",
56 					 chan->center_freq);
57 				chan->flags |= IEEE80211_CHAN_DISABLED;
58 			}
59 		}
60 	}
61 }
62 
63 void wiphy_read_of_freq_limits(struct wiphy *wiphy)
64 {
65 	struct device *dev = wiphy_dev(wiphy);
66 	struct device_node *np;
67 	struct property *prop;
68 	struct ieee80211_freq_range *freq_limits;
69 	unsigned int n_freq_limits;
70 	const __be32 *p;
71 	int len, i;
72 	int err = 0;
73 
74 	if (!dev)
75 		return;
76 	np = dev_of_node(dev);
77 	if (!np)
78 		return;
79 
80 	prop = of_find_property(np, "ieee80211-freq-limit", &len);
81 	if (!prop)
82 		return;
83 
84 	if (!len || len % sizeof(u32) || len / sizeof(u32) % 2) {
85 		dev_err(dev, "ieee80211-freq-limit wrong format");
86 		return;
87 	}
88 	n_freq_limits = len / sizeof(u32) / 2;
89 
90 	freq_limits = kzalloc_objs(*freq_limits, n_freq_limits);
91 	if (!freq_limits) {
92 		err = -ENOMEM;
93 		goto out_kfree;
94 	}
95 
96 	p = NULL;
97 	for (i = 0; i < n_freq_limits; i++) {
98 		struct ieee80211_freq_range *limit = &freq_limits[i];
99 
100 		p = of_prop_next_u32(prop, p, &limit->start_freq_khz);
101 		if (!p) {
102 			err = -EINVAL;
103 			goto out_kfree;
104 		}
105 
106 		p = of_prop_next_u32(prop, p, &limit->end_freq_khz);
107 		if (!p) {
108 			err = -EINVAL;
109 			goto out_kfree;
110 		}
111 
112 		if (!limit->start_freq_khz ||
113 		    !limit->end_freq_khz ||
114 		    limit->start_freq_khz >= limit->end_freq_khz) {
115 			err = -EINVAL;
116 			goto out_kfree;
117 		}
118 	}
119 
120 	wiphy_freq_limits_apply(wiphy, freq_limits, n_freq_limits);
121 
122 out_kfree:
123 	kfree(freq_limits);
124 	if (err)
125 		dev_err(dev, "Failed to get limits: %d\n", err);
126 }
127 EXPORT_SYMBOL(wiphy_read_of_freq_limits);
128