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

Commit fa597844 authored by John W. Linville's avatar John W. Linville
Browse files
parents 2437f3c5 73da7d5b
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -1709,6 +1709,10 @@ enum ieee80211_eid {
	WLAN_EID_OPMODE_NOTIF = 199,
	WLAN_EID_WIDE_BW_CHANNEL_SWITCH = 194,
	WLAN_EID_CHANNEL_SWITCH_WRAPPER = 196,
	WLAN_EID_EXTENDED_BSS_LOAD = 193,
	WLAN_EID_VHT_TX_POWER_ENVELOPE = 195,
	WLAN_EID_AID = 197,
	WLAN_EID_QUIET_CHANNEL = 198,

	/* 802.11ad */
	WLAN_EID_NON_TX_BSSID_CAP =  83,
@@ -1860,6 +1864,11 @@ enum ieee80211_tdls_actioncode {
	WLAN_TDLS_DISCOVERY_REQUEST = 10,
};

/* Interworking capabilities are set in 7th bit of 4th byte of the
 * @WLAN_EID_EXT_CAPABILITY information element
 */
#define WLAN_EXT_CAPA4_INTERWORKING_ENABLED	BIT(7)

/*
 * TDLS capabililites to be enabled in the 5th byte of the
 * @WLAN_EID_EXT_CAPABILITY information element
+33 −0
Original line number Diff line number Diff line
@@ -665,6 +665,30 @@ struct cfg80211_ap_settings {
	bool radar_required;
};

/**
 * struct cfg80211_csa_settings - channel switch settings
 *
 * Used for channel switch
 *
 * @chandef: defines the channel to use after the switch
 * @beacon_csa: beacon data while performing the switch
 * @counter_offset_beacon: offset for the counter within the beacon (tail)
 * @counter_offset_presp: offset for the counter within the probe response
 * @beacon_after: beacon data to be used on the new channel
 * @radar_required: whether radar detection is required on the new channel
 * @block_tx: whether transmissions should be blocked while changing
 * @count: number of beacons until switch
 */
struct cfg80211_csa_settings {
	struct cfg80211_chan_def chandef;
	struct cfg80211_beacon_data beacon_csa;
	u16 counter_offset_beacon, counter_offset_presp;
	struct cfg80211_beacon_data beacon_after;
	bool radar_required;
	bool block_tx;
	u8 count;
};

/**
 * enum station_parameters_apply_mask - station parameter values to apply
 * @STATION_PARAM_APPLY_UAPSD: apply new uAPSD parameters (uapsd_queues, max_sp)
@@ -2139,6 +2163,8 @@ struct cfg80211_update_ft_ies_params {
 * @crit_proto_stop: Indicates critical protocol no longer needs increased link
 *	reliability. This operation can not fail.
 * @set_coalesce: Set coalesce parameters.
 *
 * @channel_switch: initiate channel-switch procedure (with CSA)
 */
struct cfg80211_ops {
	int	(*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -2376,6 +2402,10 @@ struct cfg80211_ops {
				   struct wireless_dev *wdev);
	int	(*set_coalesce)(struct wiphy *wiphy,
				struct cfg80211_coalesce *coalesce);

	int	(*channel_switch)(struct wiphy *wiphy,
				  struct net_device *dev,
				  struct cfg80211_csa_settings *params);
};

/*
@@ -2441,6 +2471,8 @@ struct cfg80211_ops {
 * @WIPHY_FLAG_OFFCHAN_TX: Device supports direct off-channel TX.
 * @WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL: Device supports remain-on-channel call.
 * @WIPHY_FLAG_SUPPORTS_5_10_MHZ: Device supports 5 MHz and 10 MHz channels.
 * @WIPHY_FLAG_HAS_CHANNEL_SWITCH: Device supports channel switch in
 *	beaconing mode (AP, IBSS, Mesh, ...).
 */
enum wiphy_flags {
	WIPHY_FLAG_CUSTOM_REGULATORY		= BIT(0),
@@ -2465,6 +2497,7 @@ enum wiphy_flags {
	WIPHY_FLAG_OFFCHAN_TX			= BIT(20),
	WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL	= BIT(21),
	WIPHY_FLAG_SUPPORTS_5_10_MHZ		= BIT(22),
	WIPHY_FLAG_HAS_CHANNEL_SWITCH		= BIT(23),
};

/**
+37 −0
Original line number Diff line number Diff line
@@ -152,11 +152,14 @@ struct ieee80211_low_level_stats {
 * @IEEE80211_CHANCTX_CHANGE_WIDTH: The channel width changed
 * @IEEE80211_CHANCTX_CHANGE_RX_CHAINS: The number of RX chains changed
 * @IEEE80211_CHANCTX_CHANGE_RADAR: radar detection flag changed
 * @IEEE80211_CHANCTX_CHANGE_CHANNEL: switched to another operating channel,
 *	this is used only with channel switching with CSA
 */
enum ieee80211_chanctx_change {
	IEEE80211_CHANCTX_CHANGE_WIDTH		= BIT(0),
	IEEE80211_CHANCTX_CHANGE_RX_CHAINS	= BIT(1),
	IEEE80211_CHANCTX_CHANGE_RADAR		= BIT(2),
	IEEE80211_CHANCTX_CHANGE_CHANNEL	= BIT(3),
};

/**
@@ -1084,6 +1087,7 @@ enum ieee80211_vif_flags {
 * @addr: address of this interface
 * @p2p: indicates whether this AP or STA interface is a p2p
 *	interface, i.e. a GO or p2p-sta respectively
 * @csa_active: marks whether a channel switch is going on
 * @driver_flags: flags/capabilities the driver has for this interface,
 *	these need to be set (or cleared) when the interface is added
 *	or, if supported by the driver, the interface type is changed
@@ -1106,6 +1110,7 @@ struct ieee80211_vif {
	struct ieee80211_bss_conf bss_conf;
	u8 addr[ETH_ALEN];
	bool p2p;
	bool csa_active;

	u8 cab_queue;
	u8 hw_queue[IEEE80211_NUM_ACS];
@@ -2637,6 +2642,16 @@ enum ieee80211_roc_type {
 * @ipv6_addr_change: IPv6 address assignment on the given interface changed.
 *	Currently, this is only called for managed or P2P client interfaces.
 *	This callback is optional; it must not sleep.
 *
 * @channel_switch_beacon: Starts a channel switch to a new channel.
 *	Beacons are modified to include CSA or ECSA IEs before calling this
 *	function. The corresponding count fields in these IEs must be
 *	decremented, and when they reach zero the driver must call
 *	ieee80211_csa_finish(). Drivers which use ieee80211_beacon_get()
 *	get the csa counter decremented by mac80211, but must check if it is
 *	zero using ieee80211_csa_is_complete() after the beacon has been
 *	transmitted and then call ieee80211_csa_finish().
 *
 */
struct ieee80211_ops {
	void (*tx)(struct ieee80211_hw *hw,
@@ -2824,6 +2839,9 @@ struct ieee80211_ops {
				 struct ieee80211_vif *vif,
				 struct inet6_dev *idev);
#endif
	void (*channel_switch_beacon)(struct ieee80211_hw *hw,
				      struct ieee80211_vif *vif,
				      struct cfg80211_chan_def *chandef);
};

/**
@@ -3318,6 +3336,25 @@ static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
	return ieee80211_beacon_get_tim(hw, vif, NULL, NULL);
}

/**
 * ieee80211_csa_finish - notify mac80211 about channel switch
 * @vif: &struct ieee80211_vif pointer from the add_interface callback.
 *
 * After a channel switch announcement was scheduled and the counter in this
 * announcement hit zero, this function must be called by the driver to
 * notify mac80211 that the channel can be changed.
 */
void ieee80211_csa_finish(struct ieee80211_vif *vif);

/**
 * ieee80211_csa_is_complete - find out if counters reached zero
 * @vif: &struct ieee80211_vif pointer from the add_interface callback.
 *
 * This function returns whether the channel switch counters reached zero.
 */
bool ieee80211_csa_is_complete(struct ieee80211_vif *vif);


/**
 * ieee80211_proberesp_get - retrieve a Probe Response template
 * @hw: pointer obtained from ieee80211_alloc_hw().
+30 −0
Original line number Diff line number Diff line
@@ -676,6 +676,16 @@
 * @NL80211_CMD_GET_COALESCE: Get currently supported coalesce rules.
 * @NL80211_CMD_SET_COALESCE: Configure coalesce rules or clear existing rules.
 *
 * @NL80211_CMD_CHANNEL_SWITCH: Perform a channel switch by announcing the
 *	the new channel information (Channel Switch Announcement - CSA)
 *	in the beacon for some time (as defined in the
 *	%NL80211_ATTR_CH_SWITCH_COUNT parameter) and then change to the
 *	new channel. Userspace provides the new channel information (using
 *	%NL80211_ATTR_WIPHY_FREQ and the attributes determining channel
 *	width). %NL80211_ATTR_CH_SWITCH_BLOCK_TX may be supplied to inform
 *	other station that transmission must be blocked until the channel
 *	switch is complete.
 *
 * @NL80211_CMD_MAX: highest used command number
 * @__NL80211_CMD_AFTER_LAST: internal use
 */
@@ -841,6 +851,8 @@ enum nl80211_commands {
	NL80211_CMD_GET_COALESCE,
	NL80211_CMD_SET_COALESCE,

	NL80211_CMD_CHANNEL_SWITCH,

	/* add new commands above here */

	/* used to define NL80211_CMD_MAX below */
@@ -1469,6 +1481,18 @@ enum nl80211_commands {
 *
 * @NL80211_ATTR_COALESCE_RULE: Coalesce rule information.
 *
 * @NL80211_ATTR_CH_SWITCH_COUNT: u32 attribute specifying the number of TBTT's
 *	until the channel switch event.
 * @NL80211_ATTR_CH_SWITCH_BLOCK_TX: flag attribute specifying that transmission
 *	must be blocked on the current channel (before the channel switch
 *	operation).
 * @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information
 *	for the time while performing a channel switch.
 * @NL80211_ATTR_CSA_C_OFF_BEACON: Offset of the channel switch counter
 *	field in the beacons tail (%NL80211_ATTR_BEACON_TAIL).
 * @NL80211_ATTR_CSA_C_OFF_PRESP: Offset of the channel switch counter
 *	field in the probe response (%NL80211_ATTR_PROBE_RESP).
 *
 * @NL80211_ATTR_MAX: highest attribute number currently defined
 * @__NL80211_ATTR_AFTER_LAST: internal use
 */
@@ -1771,6 +1795,12 @@ enum nl80211_attrs {

	NL80211_ATTR_COALESCE_RULE,

	NL80211_ATTR_CH_SWITCH_COUNT,
	NL80211_ATTR_CH_SWITCH_BLOCK_TX,
	NL80211_ATTR_CSA_IES,
	NL80211_ATTR_CSA_C_OFF_BEACON,
	NL80211_ATTR_CSA_C_OFF_PRESP,

	/* add attributes here, update the policy in nl80211.c */

	__NL80211_ATTR_AFTER_LAST,
+185 −2
Original line number Diff line number Diff line
@@ -862,7 +862,7 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
	return 0;
}

static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
			    struct cfg80211_beacon_data *params)
{
	struct beacon_data *new, *old;
@@ -1026,6 +1026,12 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,

	sdata = IEEE80211_DEV_TO_SUB_IF(dev);

	/* don't allow changing the beacon while CSA is in place - offset
	 * of channel switch counter may change
	 */
	if (sdata->vif.csa_active)
		return -EBUSY;

	old = rtnl_dereference(sdata->u.ap.beacon);
	if (!old)
		return -ENOENT;
@@ -1050,6 +1056,10 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
		return -ENOENT;
	old_probe_resp = rtnl_dereference(sdata->u.ap.probe_resp);

	/* abort any running channel switch */
	sdata->vif.csa_active = false;
	cancel_work_sync(&sdata->csa_finalize_work);

	/* turn off carrier for this interface and dependent VLANs */
	list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
		netif_carrier_off(vlan->dev);
@@ -2777,6 +2787,178 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
	return 0;
}

static struct cfg80211_beacon_data *
cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
{
	struct cfg80211_beacon_data *new_beacon;
	u8 *pos;
	int len;

	len = beacon->head_len + beacon->tail_len + beacon->beacon_ies_len +
	      beacon->proberesp_ies_len + beacon->assocresp_ies_len +
	      beacon->probe_resp_len;

	new_beacon = kzalloc(sizeof(*new_beacon) + len, GFP_KERNEL);
	if (!new_beacon)
		return NULL;

	pos = (u8 *)(new_beacon + 1);
	if (beacon->head_len) {
		new_beacon->head_len = beacon->head_len;
		new_beacon->head = pos;
		memcpy(pos, beacon->head, beacon->head_len);
		pos += beacon->head_len;
	}
	if (beacon->tail_len) {
		new_beacon->tail_len = beacon->tail_len;
		new_beacon->tail = pos;
		memcpy(pos, beacon->tail, beacon->tail_len);
		pos += beacon->tail_len;
	}
	if (beacon->beacon_ies_len) {
		new_beacon->beacon_ies_len = beacon->beacon_ies_len;
		new_beacon->beacon_ies = pos;
		memcpy(pos, beacon->beacon_ies, beacon->beacon_ies_len);
		pos += beacon->beacon_ies_len;
	}
	if (beacon->proberesp_ies_len) {
		new_beacon->proberesp_ies_len = beacon->proberesp_ies_len;
		new_beacon->proberesp_ies = pos;
		memcpy(pos, beacon->proberesp_ies, beacon->proberesp_ies_len);
		pos += beacon->proberesp_ies_len;
	}
	if (beacon->assocresp_ies_len) {
		new_beacon->assocresp_ies_len = beacon->assocresp_ies_len;
		new_beacon->assocresp_ies = pos;
		memcpy(pos, beacon->assocresp_ies, beacon->assocresp_ies_len);
		pos += beacon->assocresp_ies_len;
	}
	if (beacon->probe_resp_len) {
		new_beacon->probe_resp_len = beacon->probe_resp_len;
		beacon->probe_resp = pos;
		memcpy(pos, beacon->probe_resp, beacon->probe_resp_len);
		pos += beacon->probe_resp_len;
	}

	return new_beacon;
}

void ieee80211_csa_finalize_work(struct work_struct *work)
{
	struct ieee80211_sub_if_data *sdata =
		container_of(work, struct ieee80211_sub_if_data,
			     csa_finalize_work);
	struct ieee80211_local *local = sdata->local;
	int err, changed;

	if (!ieee80211_sdata_running(sdata))
		return;

	if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
		return;

	sdata->radar_required = sdata->csa_radar_required;
	err = ieee80211_vif_change_channel(sdata, &local->csa_chandef,
					   &changed);
	if (WARN_ON(err < 0))
		return;

	err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
	if (err < 0)
		return;

	changed |= err;
	kfree(sdata->u.ap.next_beacon);
	sdata->u.ap.next_beacon = NULL;
	sdata->vif.csa_active = false;

	ieee80211_wake_queues_by_reason(&sdata->local->hw,
					IEEE80211_MAX_QUEUE_MAP,
					IEEE80211_QUEUE_STOP_REASON_CSA);

	ieee80211_bss_info_change_notify(sdata, changed);

	cfg80211_ch_switch_notify(sdata->dev, &local->csa_chandef);
}

static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
				    struct cfg80211_csa_settings *params)
{
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
	struct ieee80211_local *local = sdata->local;
	struct ieee80211_chanctx_conf *chanctx_conf;
	struct ieee80211_chanctx *chanctx;
	int err, num_chanctx;

	if (!list_empty(&local->roc_list) || local->scanning)
		return -EBUSY;

	if (sdata->wdev.cac_started)
		return -EBUSY;

	if (cfg80211_chandef_identical(&params->chandef,
				       &sdata->vif.bss_conf.chandef))
		return -EINVAL;

	rcu_read_lock();
	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
	if (!chanctx_conf) {
		rcu_read_unlock();
		return -EBUSY;
	}

	/* don't handle for multi-VIF cases */
	chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
	if (chanctx->refcount > 1) {
		rcu_read_unlock();
		return -EBUSY;
	}
	num_chanctx = 0;
	list_for_each_entry_rcu(chanctx, &local->chanctx_list, list)
		num_chanctx++;
	rcu_read_unlock();

	if (num_chanctx > 1)
		return -EBUSY;

	/* don't allow another channel switch if one is already active. */
	if (sdata->vif.csa_active)
		return -EBUSY;

	/* only handle AP for now. */
	switch (sdata->vif.type) {
	case NL80211_IFTYPE_AP:
		break;
	default:
		return -EOPNOTSUPP;
	}

	sdata->u.ap.next_beacon = cfg80211_beacon_dup(&params->beacon_after);
	if (!sdata->u.ap.next_beacon)
		return -ENOMEM;

	sdata->csa_counter_offset_beacon = params->counter_offset_beacon;
	sdata->csa_counter_offset_presp = params->counter_offset_presp;
	sdata->csa_radar_required = params->radar_required;

	if (params->block_tx)
		ieee80211_stop_queues_by_reason(&local->hw,
				IEEE80211_MAX_QUEUE_MAP,
				IEEE80211_QUEUE_STOP_REASON_CSA);

	err = ieee80211_assign_beacon(sdata, &params->beacon_csa);
	if (err < 0)
		return err;

	local->csa_chandef = params->chandef;
	sdata->vif.csa_active = true;

	ieee80211_bss_info_change_notify(sdata, err);
	drv_channel_switch_beacon(sdata, &params->chandef);

	return 0;
}

static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
			     struct ieee80211_channel *chan, bool offchan,
			     unsigned int wait, const u8 *buf, size_t len,
@@ -3494,4 +3676,5 @@ struct cfg80211_ops mac80211_config_ops = {
	.get_et_strings = ieee80211_get_et_strings,
	.get_channel = ieee80211_cfg_get_channel,
	.start_radar_detection = ieee80211_start_radar_detection,
	.channel_switch = ieee80211_channel_switch,
};
Loading