cfg80211: provide channel to start_ap function
Instead of setting the channel first and then
starting the AP, let cfg80211 store the channel
and provide it as one of the AP settings.
This means that now you have to set the channel
before you can start an AP interface, but since
hostapd/wpa_supplicant always do that we're OK
with this change.
Alternatively, it's now possible to give the
channel as an attribute to the start-ap nl80211
command, overriding any preset channel.
Cc: Kalle Valo <kvalo@qca.qualcomm.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 206465d..74f4a8f 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -921,7 +921,11 @@
if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS))
goto nla_put_failure;
}
- CMD(set_channel, SET_CHANNEL);
+ if (dev->ops->set_channel || dev->ops->start_ap) {
+ i++;
+ if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL))
+ goto nla_put_failure;
+ }
CMD(set_wds_peer, SET_WDS_PEER);
if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) {
CMD(tdls_mgmt, TDLS_MGMT);
@@ -1170,6 +1174,9 @@
* Monitors are special as they are normally slaved to
* whatever else is going on, so they behave as though
* you tried setting the wiphy channel itself.
+ *
+ * For AP/GO modes, it's only for compatibility, you can
+ * also give the channel to the start-AP command.
*/
return !wdev ||
wdev->iftype == NL80211_IFTYPE_AP ||
@@ -1204,6 +1211,7 @@
struct wireless_dev *wdev,
struct genl_info *info)
{
+ struct ieee80211_channel *channel;
enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
u32 freq;
int result;
@@ -1221,7 +1229,25 @@
freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
mutex_lock(&rdev->devlist_mtx);
- if (wdev) {
+ if (wdev) switch (wdev->iftype) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ if (wdev->beacon_interval) {
+ result = -EBUSY;
+ break;
+ }
+ channel = rdev_freq_to_chan(rdev, freq, channel_type);
+ if (!channel || !cfg80211_can_beacon_sec_chan(&rdev->wiphy,
+ channel,
+ channel_type)) {
+ result = -EINVAL;
+ break;
+ }
+ wdev->preset_chan = channel;
+ wdev->preset_chantype = channel_type;
+ result = 0;
+ break;
+ default:
wdev_lock(wdev);
result = cfg80211_set_freq(rdev, wdev, freq, channel_type);
wdev_unlock(wdev);
@@ -2299,6 +2325,29 @@
info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]);
}
+ if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+ enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
+
+ if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] &&
+ !nl80211_valid_channel_type(info, &channel_type))
+ return -EINVAL;
+
+ params.channel = rdev_freq_to_chan(rdev,
+ nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]),
+ channel_type);
+ if (!params.channel)
+ return -EINVAL;
+ params.channel_type = channel_type;
+ } else if (wdev->preset_chan) {
+ params.channel = wdev->preset_chan;
+ params.channel_type = wdev->preset_chantype;
+ } else
+ return -EINVAL;
+
+ if (!cfg80211_can_beacon_sec_chan(&rdev->wiphy, params.channel,
+ params.channel_type))
+ return -EINVAL;
+
err = rdev->ops->start_ap(&rdev->wiphy, dev, ¶ms);
if (!err)
wdev->beacon_interval = params.beacon_interval;