From: Johannes Berg <johannes.berg@xxxxxxxxx> An HT/VHT AP is allowed to change its bandwidth while operating, create a new capability and cfg80211 API to allow this. Signed-off-by: Johannes Berg <johannes.berg@xxxxxxxxx> --- include/net/cfg80211.h | 3 +++ include/uapi/linux/nl80211.h | 3 +++ net/mac80211/cfg.c | 18 ++++++++++++++++++ net/wireless/ap.c | 10 ++++++++++ net/wireless/core.c | 6 ++++++ net/wireless/core.h | 3 +++ net/wireless/nl80211.c | 14 ++++++++++---- 7 files changed, 53 insertions(+), 4 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 1cc4c36..bc670b2 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1754,6 +1754,7 @@ struct cfg80211_gtk_rekey_data { * @start_ap: Start acting in AP mode defined by the parameters. * @change_beacon: Change the beacon parameters for an access point mode * interface. This should reject the call when AP mode wasn't started. + * @change_bandwidth: Change the AP's operating bandwidth. * @stop_ap: Stop being an AP, including stopping beaconing. * * @add_station: Add a new station. @@ -1957,6 +1958,8 @@ struct cfg80211_ops { struct cfg80211_ap_settings *settings); int (*change_beacon)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_beacon_data *info); + int (*change_bandwidth)(struct wiphy *wiphy, struct net_device *dev, + const struct cfg80211_chan_def *chandef); int (*stop_ap)(struct wiphy *wiphy, struct net_device *dev); diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 122e9fd..1ed7e1c 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3412,6 +3412,8 @@ enum nl80211_ap_sme_features { * stations the authenticated/associated bits have to be set in the mask. * @NL80211_FEATURE_ADVERTISE_CHAN_LIMITS: cfg80211 advertises channel limits * (HT40, VHT 80/160 MHz) if this flag is set + * @NL80211_FEATURE_HAVE_AP_BW_SWITCH: AP mode can switch bandwidth (by using + * the normal set channel command) */ enum nl80211_feature_flags { NL80211_FEATURE_SK_TX_STATUS = 1 << 0, @@ -3429,6 +3431,7 @@ enum nl80211_feature_flags { NL80211_FEATURE_P2P_GO_OPPPS = 1 << 12, NL80211_FEATURE_FULL_AP_CLIENT_STATE = 1 << 13, NL80211_FEATURE_ADVERTISE_CHAN_LIMITS = 1 << 14, + NL80211_FEATURE_HAVE_AP_BW_SWITCH = 1 << 15, }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 68eca17..d7fa932 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1005,6 +1005,23 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev, return 0; } +static int ieee80211_change_bandwidth(struct wiphy *wiphy, + struct net_device *dev, + const struct cfg80211_chan_def *chandef) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + int ret; + u32 changed; + + ret = ieee80211_vif_change_bandwidth(sdata, chandef, &changed); + if (ret) + return ret; + + ieee80211_bss_info_change_notify(sdata, changed); + + return 0; +} + static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -3283,6 +3300,7 @@ struct cfg80211_ops mac80211_config_ops = { .set_default_mgmt_key = ieee80211_config_default_mgmt_key, .start_ap = ieee80211_start_ap, .change_beacon = ieee80211_change_beacon, + .change_bandwidth = ieee80211_change_bandwidth, .stop_ap = ieee80211_stop_ap, .add_station = ieee80211_add_station, .del_station = ieee80211_del_station, diff --git a/net/wireless/ap.c b/net/wireless/ap.c index a4a14e8..aca91c6 100644 --- a/net/wireless/ap.c +++ b/net/wireless/ap.c @@ -108,3 +108,13 @@ bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, return ret; } EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame); + +int cfg80211_change_ap_bandwidth(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_chan_def *chandef) +{ + /* XXX tracing */ + if (!rdev->ops->change_bandwidth) + return -EBUSY; + return rdev->ops->change_bandwidth(&rdev->wiphy, dev, chandef); +} diff --git a/net/wireless/core.c b/net/wireless/core.c index d915376..8d57a6f 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -546,6 +546,12 @@ int wiphy_register(struct wiphy *wiphy) have_band = true; } + if (rdev->ops->change_bandwidth && + wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) + wiphy->features |= NL80211_FEATURE_HAVE_AP_BW_SWITCH; + else + wiphy->features &= ~NL80211_FEATURE_HAVE_AP_BW_SWITCH; + if (!have_band) { WARN_ON(1); return -EINVAL; diff --git a/net/wireless/core.h b/net/wireless/core.h index 949c957..2dd6d34 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -307,6 +307,9 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev, /* AP */ int cfg80211_stop_ap(struct cfg80211_registered_device *rdev, struct net_device *dev); +int cfg80211_change_ap_bandwidth(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct cfg80211_chan_def *chandef); /* MLME */ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index cae3968..b19c822 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1538,14 +1538,20 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, switch (iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: - if (wdev->beacon_interval) { - result = -EBUSY; - break; - } if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef)) { result = -EINVAL; break; } + if (wdev->beacon_interval) { + if (wdev->channel != chandef.chan) { + result = -EBUSY; + break; + } + result = cfg80211_change_ap_bandwidth(rdev, + wdev->netdev, + &chandef); + break; + } wdev->preset_chandef = chandef; result = 0; break; -- 1.8.0 -- To unsubscribe from this list: send the line "unsubscribe linux-wireless" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html