cfg80211: fix regression in multi-vif AP start
authorFelix Fietkau <nbd@openwrt.org>
Tue, 19 Jun 2012 00:50:57 +0000 (02:50 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Wed, 20 Jun 2012 09:10:43 +0000 (11:10 +0200)
Commit "cfg80211: provide channel to start_ap function" assumes that the
channel is always passed to the NL80211_CMD_START_AP command, however
in case of multi-BSSID, hostapd only passes the channel for the first vif.
This makes starting beaconing on secondary vifs fail with -EINVAL.

Fix this by storing the channel provided to .start_ap in wdev->preset_chan
and picking the first AP vif's channel for secondary vifs if not provided.

Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/wireless/nl80211.c

index 06623d0646061ad0563fe2449273b8b2dc64974f..888fadc4d63e68a595a9e1764ab898e7a46c08af 100644 (file)
@@ -2335,6 +2335,33 @@ static int nl80211_parse_beacon(struct genl_info *info,
        return 0;
 }
 
+static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev,
+                                  struct cfg80211_ap_settings *params)
+{
+       struct wireless_dev *wdev;
+       bool ret = false;
+
+       mutex_lock(&rdev->devlist_mtx);
+
+       list_for_each_entry(wdev, &rdev->netdev_list, list) {
+               if (wdev->iftype != NL80211_IFTYPE_AP &&
+                   wdev->iftype != NL80211_IFTYPE_P2P_GO)
+                       continue;
+
+               if (!wdev->preset_chan)
+                       continue;
+
+               params->channel = wdev->preset_chan;
+               params->channel_type = wdev->preset_chantype;
+               ret = true;
+               break;
+       }
+
+       mutex_unlock(&rdev->devlist_mtx);
+
+       return ret;
+}
+
 static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -2437,7 +2464,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
        } else if (wdev->preset_chan) {
                params.channel = wdev->preset_chan;
                params.channel_type = wdev->preset_chantype;
-       } else
+       } else if (!nl80211_get_ap_channel(rdev, &params))
                return -EINVAL;
 
        if (!cfg80211_can_beacon_sec_chan(&rdev->wiphy, params.channel,
@@ -2445,8 +2472,11 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
                return -EINVAL;
 
        err = rdev->ops->start_ap(&rdev->wiphy, dev, &params);
-       if (!err)
+       if (!err) {
+               wdev->preset_chan = params.channel;
+               wdev->preset_chantype = params.channel_type;
                wdev->beacon_interval = params.beacon_interval;
+       }
        return err;
 }