Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e16821bc authored by Jouni Malinen's avatar Jouni Malinen Committed by Johannes Berg
Browse files

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: default avatarJouni Malinen <jouni@qca.qualcomm.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent b205786e
Loading
Loading
Loading
Loading
+8 −0
Original line number Original line Diff line number Diff line
@@ -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);
};
};


/*
/*
+4 −0
Original line number Original line Diff line number Diff line
@@ -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,
};
};


/**
/**
+28 −12
Original line number Original line Diff line number Diff line
@@ -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,12 +1950,25 @@ 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 (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef, iftype)) {
			result = -EINVAL;
			break;
		}
		if (wdev->beacon_interval) {
		if (wdev->beacon_interval) {
			if (!dev || !rdev->ops->set_ap_chanwidth ||
			    !(rdev->wiphy.features &
			      NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE)) {
				result = -EBUSY;
				result = -EBUSY;
				break;
				break;
			}
			}
		if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef, iftype)) {

			result = -EINVAL;
			/* 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;
				break;
		}
		}
		wdev->preset_chandef = chandef;
		wdev->preset_chandef = chandef;
@@ -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,8 +2114,9 @@ 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,
			nl80211_can_set_dev_channel(wdev) ? netdev : NULL,
			info);
			info);
		if (result)
		if (result)
			return result;
			return result;
+13 −0
Original line number Original line Diff line number Diff line
@@ -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 */
+18 −0
Original line number Original line Diff line number Diff line
@@ -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		     *
 *************************************************************/
 *************************************************************/