cfg80211: Dynamic channel bandwidth changes in AP mode
This extends NL80211_CMD_SET_CHANNEL to allow dynamic channel bandwidth changes in AP mode (including P2P GO) during a lifetime of the BSS. This can be used to implement, e.g., HT 20/40 MHz co-existence rules on the 2.4 GHz band. Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
b205786e38
commit
e16821bcfb
|
@ -2290,6 +2290,10 @@ struct cfg80211_qos_map {
|
||||||
* @channel_switch: initiate channel-switch procedure (with CSA)
|
* @channel_switch: initiate channel-switch procedure (with CSA)
|
||||||
*
|
*
|
||||||
* @set_qos_map: Set QoS mapping information to the driver
|
* @set_qos_map: Set QoS mapping information to the driver
|
||||||
|
*
|
||||||
|
* @set_ap_chanwidth: Set the AP (including P2P GO) mode channel width for the
|
||||||
|
* given interface This is used e.g. for dynamic HT 20/40 MHz channel width
|
||||||
|
* changes during the lifetime of the BSS.
|
||||||
*/
|
*/
|
||||||
struct cfg80211_ops {
|
struct cfg80211_ops {
|
||||||
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
|
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
|
||||||
|
@ -2533,9 +2537,13 @@ struct cfg80211_ops {
|
||||||
int (*channel_switch)(struct wiphy *wiphy,
|
int (*channel_switch)(struct wiphy *wiphy,
|
||||||
struct net_device *dev,
|
struct net_device *dev,
|
||||||
struct cfg80211_csa_settings *params);
|
struct cfg80211_csa_settings *params);
|
||||||
|
|
||||||
int (*set_qos_map)(struct wiphy *wiphy,
|
int (*set_qos_map)(struct wiphy *wiphy,
|
||||||
struct net_device *dev,
|
struct net_device *dev,
|
||||||
struct cfg80211_qos_map *qos_map);
|
struct cfg80211_qos_map *qos_map);
|
||||||
|
|
||||||
|
int (*set_ap_chanwidth)(struct wiphy *wiphy, struct net_device *dev,
|
||||||
|
struct cfg80211_chan_def *chandef);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -3929,6 +3929,9 @@ enum nl80211_ap_sme_features {
|
||||||
* interface. An active monitor interface behaves like a normal monitor
|
* interface. An active monitor interface behaves like a normal monitor
|
||||||
* interface, but gets added to the driver. It ensures that incoming
|
* interface, but gets added to the driver. It ensures that incoming
|
||||||
* unicast packets directed at the configured interface address get ACKed.
|
* unicast packets directed at the configured interface address get ACKed.
|
||||||
|
* @NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE: This driver supports dynamic
|
||||||
|
* channel bandwidth change (e.g., HT 20 <-> 40 MHz channel) during the
|
||||||
|
* lifetime of a BSS.
|
||||||
*/
|
*/
|
||||||
enum nl80211_feature_flags {
|
enum nl80211_feature_flags {
|
||||||
NL80211_FEATURE_SK_TX_STATUS = 1 << 0,
|
NL80211_FEATURE_SK_TX_STATUS = 1 << 0,
|
||||||
|
@ -3949,6 +3952,7 @@ enum nl80211_feature_flags {
|
||||||
NL80211_FEATURE_FULL_AP_CLIENT_STATE = 1 << 15,
|
NL80211_FEATURE_FULL_AP_CLIENT_STATE = 1 << 15,
|
||||||
NL80211_FEATURE_USERSPACE_MPM = 1 << 16,
|
NL80211_FEATURE_USERSPACE_MPM = 1 << 16,
|
||||||
NL80211_FEATURE_ACTIVE_MONITOR = 1 << 17,
|
NL80211_FEATURE_ACTIVE_MONITOR = 1 << 17,
|
||||||
|
NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE = 1 << 18,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1928,18 +1928,20 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
|
static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
|
||||||
struct wireless_dev *wdev,
|
struct net_device *dev,
|
||||||
struct genl_info *info)
|
struct genl_info *info)
|
||||||
{
|
{
|
||||||
struct cfg80211_chan_def chandef;
|
struct cfg80211_chan_def chandef;
|
||||||
int result;
|
int result;
|
||||||
enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR;
|
enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR;
|
||||||
|
struct wireless_dev *wdev = NULL;
|
||||||
|
|
||||||
if (wdev)
|
if (dev)
|
||||||
iftype = wdev->iftype;
|
wdev = dev->ieee80211_ptr;
|
||||||
|
|
||||||
if (!nl80211_can_set_dev_channel(wdev))
|
if (!nl80211_can_set_dev_channel(wdev))
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
if (wdev)
|
||||||
|
iftype = wdev->iftype;
|
||||||
|
|
||||||
result = nl80211_parse_chandef(rdev, info, &chandef);
|
result = nl80211_parse_chandef(rdev, info, &chandef);
|
||||||
if (result)
|
if (result)
|
||||||
|
@ -1948,14 +1950,27 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
|
||||||
switch (iftype) {
|
switch (iftype) {
|
||||||
case NL80211_IFTYPE_AP:
|
case NL80211_IFTYPE_AP:
|
||||||
case NL80211_IFTYPE_P2P_GO:
|
case NL80211_IFTYPE_P2P_GO:
|
||||||
if (wdev->beacon_interval) {
|
|
||||||
result = -EBUSY;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef, iftype)) {
|
if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef, iftype)) {
|
||||||
result = -EINVAL;
|
result = -EINVAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (wdev->beacon_interval) {
|
||||||
|
if (!dev || !rdev->ops->set_ap_chanwidth ||
|
||||||
|
!(rdev->wiphy.features &
|
||||||
|
NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE)) {
|
||||||
|
result = -EBUSY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only allow dynamic channel width changes */
|
||||||
|
if (chandef.chan != wdev->preset_chandef.chan) {
|
||||||
|
result = -EBUSY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result = rdev_set_ap_chanwidth(rdev, dev, &chandef);
|
||||||
|
if (result)
|
||||||
|
break;
|
||||||
|
}
|
||||||
wdev->preset_chandef = chandef;
|
wdev->preset_chandef = chandef;
|
||||||
result = 0;
|
result = 0;
|
||||||
break;
|
break;
|
||||||
|
@ -1977,7 +1992,7 @@ static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info)
|
||||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||||
struct net_device *netdev = info->user_ptr[1];
|
struct net_device *netdev = info->user_ptr[1];
|
||||||
|
|
||||||
return __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info);
|
return __nl80211_set_channel(rdev, netdev, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info)
|
static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info)
|
||||||
|
@ -2099,9 +2114,10 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
|
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
|
||||||
result = __nl80211_set_channel(rdev,
|
result = __nl80211_set_channel(
|
||||||
nl80211_can_set_dev_channel(wdev) ? wdev : NULL,
|
rdev,
|
||||||
info);
|
nl80211_can_set_dev_channel(wdev) ? netdev : NULL,
|
||||||
|
info);
|
||||||
if (result)
|
if (result)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -950,4 +950,17 @@ static inline int rdev_set_qos_map(struct cfg80211_registered_device *rdev,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
rdev_set_ap_chanwidth(struct cfg80211_registered_device *rdev,
|
||||||
|
struct net_device *dev, struct cfg80211_chan_def *chandef)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
trace_rdev_set_ap_chanwidth(&rdev->wiphy, dev, chandef);
|
||||||
|
ret = rdev->ops->set_ap_chanwidth(&rdev->wiphy, dev, chandef);
|
||||||
|
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* __CFG80211_RDEV_OPS */
|
#endif /* __CFG80211_RDEV_OPS */
|
||||||
|
|
|
@ -1919,6 +1919,24 @@ TRACE_EVENT(rdev_set_qos_map,
|
||||||
WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->num_des)
|
WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->num_des)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
TRACE_EVENT(rdev_set_ap_chanwidth,
|
||||||
|
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
|
||||||
|
struct cfg80211_chan_def *chandef),
|
||||||
|
TP_ARGS(wiphy, netdev, chandef),
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
WIPHY_ENTRY
|
||||||
|
NETDEV_ENTRY
|
||||||
|
CHAN_DEF_ENTRY
|
||||||
|
),
|
||||||
|
TP_fast_assign(
|
||||||
|
WIPHY_ASSIGN;
|
||||||
|
NETDEV_ASSIGN;
|
||||||
|
CHAN_DEF_ASSIGN(chandef);
|
||||||
|
),
|
||||||
|
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT,
|
||||||
|
WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG)
|
||||||
|
);
|
||||||
|
|
||||||
/*************************************************************
|
/*************************************************************
|
||||||
* cfg80211 exported functions traces *
|
* cfg80211 exported functions traces *
|
||||||
*************************************************************/
|
*************************************************************/
|
||||||
|
|
Loading…
Reference in New Issue