Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

cfg80211: support ieee80211-freq-limit DT property

This patch adds a helper for reading that new property and applying
limitations of supported channels specified this way.
It is used with devices that normally support a wide wireless band but
in a given config are limited to some part of it (usually due to board
design). For example a dual-band chipset may be able to support one band
only because of used antennas.
It's also common that tri-band routers have separated radios for lower
and higher part of 5 GHz band and it may be impossible to say which is
which without a DT info.

Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
[add new function to documentation, fix link]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>

authored by

Rafał Miłecki and committed by
Johannes Berg
e691ac2f 4787cfa0

+170
+3
Documentation/80211/cfg80211.rst
··· 45 45 :functions: wiphy_new 46 46 47 47 .. kernel-doc:: include/net/cfg80211.h 48 + :functions: wiphy_read_of_freq_limits 49 + 50 + .. kernel-doc:: include/net/cfg80211.h 48 51 :functions: wiphy_register 49 52 50 53 .. kernel-doc:: include/net/cfg80211.h
+28
include/net/cfg80211.h
··· 311 311 struct ieee80211_sta_vht_cap vht_cap; 312 312 }; 313 313 314 + /** 315 + * wiphy_read_of_freq_limits - read frequency limits from device tree 316 + * 317 + * @wiphy: the wireless device to get extra limits for 318 + * 319 + * Some devices may have extra limitations specified in DT. This may be useful 320 + * for chipsets that normally support more bands but are limited due to board 321 + * design (e.g. by antennas or external power amplifier). 322 + * 323 + * This function reads info from DT and uses it to *modify* channels (disable 324 + * unavailable ones). It's usually a *bad* idea to use it in drivers with 325 + * shared channel data as DT limitations are device specific. You should make 326 + * sure to call it only if channels in wiphy are copied and can be modified 327 + * without affecting other devices. 328 + * 329 + * As this function access device node it has to be called after set_wiphy_dev. 330 + * It also modifies channels so they have to be set first. 331 + * If using this helper, call it before wiphy_register(). 332 + */ 333 + #ifdef CONFIG_OF 334 + void wiphy_read_of_freq_limits(struct wiphy *wiphy); 335 + #else /* CONFIG_OF */ 336 + static inline void wiphy_read_of_freq_limits(struct wiphy *wiphy) 337 + { 338 + } 339 + #endif /* !CONFIG_OF */ 340 + 341 + 314 342 /* 315 343 * Wireless hardware/device configuration structures and methods 316 344 */
+1
net/wireless/Makefile
··· 11 11 12 12 cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o 13 13 cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o ocb.o 14 + cfg80211-$(CONFIG_OF) += of.o 14 15 cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o 15 16 cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o 16 17 cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o
+138
net/wireless/of.c
··· 1 + /* 2 + * Copyright (C) 2017 Rafał Miłecki <rafal@milecki.pl> 3 + * 4 + * Permission to use, copy, modify, and/or distribute this software for any 5 + * purpose with or without fee is hereby granted, provided that the above 6 + * copyright notice and this permission notice appear in all copies. 7 + * 8 + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 + */ 16 + 17 + #include <linux/of.h> 18 + #include <net/cfg80211.h> 19 + #include "core.h" 20 + 21 + static bool wiphy_freq_limits_valid_chan(struct wiphy *wiphy, 22 + struct ieee80211_freq_range *freq_limits, 23 + unsigned int n_freq_limits, 24 + struct ieee80211_channel *chan) 25 + { 26 + u32 bw = MHZ_TO_KHZ(20); 27 + int i; 28 + 29 + for (i = 0; i < n_freq_limits; i++) { 30 + struct ieee80211_freq_range *limit = &freq_limits[i]; 31 + 32 + if (cfg80211_does_bw_fit_range(limit, 33 + MHZ_TO_KHZ(chan->center_freq), 34 + bw)) 35 + return true; 36 + } 37 + 38 + return false; 39 + } 40 + 41 + static void wiphy_freq_limits_apply(struct wiphy *wiphy, 42 + struct ieee80211_freq_range *freq_limits, 43 + unsigned int n_freq_limits) 44 + { 45 + enum nl80211_band band; 46 + int i; 47 + 48 + if (WARN_ON(!n_freq_limits)) 49 + return; 50 + 51 + for (band = 0; band < NUM_NL80211_BANDS; band++) { 52 + struct ieee80211_supported_band *sband = wiphy->bands[band]; 53 + 54 + if (!sband) 55 + continue; 56 + 57 + for (i = 0; i < sband->n_channels; i++) { 58 + struct ieee80211_channel *chan = &sband->channels[i]; 59 + 60 + if (chan->flags & IEEE80211_CHAN_DISABLED) 61 + continue; 62 + 63 + if (!wiphy_freq_limits_valid_chan(wiphy, freq_limits, 64 + n_freq_limits, 65 + chan)) { 66 + pr_debug("Disabling freq %d MHz as it's out of OF limits\n", 67 + chan->center_freq); 68 + chan->flags |= IEEE80211_CHAN_DISABLED; 69 + } 70 + } 71 + } 72 + } 73 + 74 + void wiphy_read_of_freq_limits(struct wiphy *wiphy) 75 + { 76 + struct device *dev = wiphy_dev(wiphy); 77 + struct device_node *np; 78 + struct property *prop; 79 + struct ieee80211_freq_range *freq_limits; 80 + unsigned int n_freq_limits; 81 + const __be32 *p; 82 + int len, i; 83 + int err = 0; 84 + 85 + if (!dev) 86 + return; 87 + np = dev_of_node(dev); 88 + if (!np) 89 + return; 90 + 91 + prop = of_find_property(np, "ieee80211-freq-limit", &len); 92 + if (!prop) 93 + return; 94 + 95 + if (!len || len % sizeof(u32) || len / sizeof(u32) % 2) { 96 + dev_err(dev, "ieee80211-freq-limit wrong format"); 97 + return; 98 + } 99 + n_freq_limits = len / sizeof(u32) / 2; 100 + 101 + freq_limits = kcalloc(n_freq_limits, sizeof(*freq_limits), GFP_KERNEL); 102 + if (!freq_limits) { 103 + err = -ENOMEM; 104 + goto out_kfree; 105 + } 106 + 107 + p = NULL; 108 + for (i = 0; i < n_freq_limits; i++) { 109 + struct ieee80211_freq_range *limit = &freq_limits[i]; 110 + 111 + p = of_prop_next_u32(prop, p, &limit->start_freq_khz); 112 + if (!p) { 113 + err = -EINVAL; 114 + goto out_kfree; 115 + } 116 + 117 + p = of_prop_next_u32(prop, p, &limit->end_freq_khz); 118 + if (!p) { 119 + err = -EINVAL; 120 + goto out_kfree; 121 + } 122 + 123 + if (!limit->start_freq_khz || 124 + !limit->end_freq_khz || 125 + limit->start_freq_khz >= limit->end_freq_khz) { 126 + err = -EINVAL; 127 + goto out_kfree; 128 + } 129 + } 130 + 131 + wiphy_freq_limits_apply(wiphy, freq_limits, n_freq_limits); 132 + 133 + out_kfree: 134 + kfree(freq_limits); 135 + if (err) 136 + dev_err(dev, "Failed to get limits: %d\n", err); 137 + } 138 + EXPORT_SYMBOL(wiphy_read_of_freq_limits);