nl80211: Add frequency configuration (including HT40)
This patch adds new NL80211_CMD_SET_WIPHY attributes
NL80211_ATTR_WIPHY_FREQ and NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET to allow
userspace to set the operating channel (e.g., hostapd for AP mode).
Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com>
Acked-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index c9141e3..9caee60 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -59,6 +59,8 @@
[NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
.len = BUS_ID_SIZE-1 },
[NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
+ [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
+ [NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET] = { .type = NLA_U32 },
[NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
[NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
@@ -359,6 +361,61 @@
}
}
+ if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+ enum nl80211_sec_chan_offset sec_chan_offset =
+ NL80211_SEC_CHAN_NO_HT;
+ struct ieee80211_channel *chan;
+ u32 freq, sec_freq;
+
+ if (!rdev->ops->set_channel) {
+ result = -EOPNOTSUPP;
+ goto bad_res;
+ }
+
+ if (info->attrs[NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET]) {
+ sec_chan_offset = nla_get_u32(
+ info->attrs[
+ NL80211_ATTR_WIPHY_SEC_CHAN_OFFSET]);
+ if (sec_chan_offset != NL80211_SEC_CHAN_NO_HT &&
+ sec_chan_offset != NL80211_SEC_CHAN_DISABLED &&
+ sec_chan_offset != NL80211_SEC_CHAN_BELOW &&
+ sec_chan_offset != NL80211_SEC_CHAN_ABOVE) {
+ result = -EINVAL;
+ goto bad_res;
+ }
+ }
+
+ freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
+ chan = ieee80211_get_channel(&rdev->wiphy, freq);
+ if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) {
+ /* Primary channel not allowed */
+ result = -EINVAL;
+ goto bad_res;
+ }
+ if (sec_chan_offset == NL80211_SEC_CHAN_BELOW)
+ sec_freq = freq - 20;
+ else if (sec_chan_offset == NL80211_SEC_CHAN_ABOVE)
+ sec_freq = freq + 20;
+ else
+ sec_freq = 0;
+
+ if (sec_freq) {
+ struct ieee80211_channel *schan;
+ schan = ieee80211_get_channel(&rdev->wiphy, sec_freq);
+ if (!schan || schan->flags & IEEE80211_CHAN_DISABLED) {
+ /* Secondary channel not allowed */
+ result = -EINVAL;
+ goto bad_res;
+ }
+ }
+
+ result = rdev->ops->set_channel(&rdev->wiphy, chan,
+ sec_chan_offset);
+ if (result)
+ goto bad_res;
+ }
+
+
bad_res:
cfg80211_put_dev(rdev);
return result;