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

Commit 9449410f authored by Simon Wunderlich's avatar Simon Wunderlich Committed by Johannes Berg
Browse files

mac80211: send a CSA action frame when changing channel



IBSS members may not immediately be able to send out their beacon when
performing CSA, therefore also send a CSA action frame.

Signed-off-by: default avatarSimon Wunderlich <siwu@hrz.tu-chemnitz.de>
Signed-off-by: default avatarMathias Kretschmer <mathias.kretschmer@fokus.fraunhofer.de>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent cd7760e6
Loading
Loading
Loading
Loading
+60 −0
Original line number Diff line number Diff line
@@ -428,6 +428,60 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
				  tsf, false);
}

static int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
				     struct cfg80211_csa_settings *csa_settings)
{
	struct sk_buff *skb;
	struct ieee80211_mgmt *mgmt;
	struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
	struct ieee80211_local *local = sdata->local;
	int freq;
	int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) +
		      sizeof(mgmt->u.action.u.chan_switch);
	u8 *pos;

	skb = dev_alloc_skb(local->tx_headroom + hdr_len +
			    5 +	/* channel switch announcement element */
			    3);	/* secondary channel offset element */
	if (!skb)
		return -1;

	skb_reserve(skb, local->tx_headroom);
	mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len);
	memset(mgmt, 0, hdr_len);
	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
					  IEEE80211_STYPE_ACTION);

	eth_broadcast_addr(mgmt->da);
	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
	memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
	mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
	mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH;
	pos = skb_put(skb, 5);
	*pos++ = WLAN_EID_CHANNEL_SWITCH;			/* EID */
	*pos++ = 3;						/* IE length */
	*pos++ = csa_settings->block_tx ? 1 : 0;		/* CSA mode */
	freq = csa_settings->chandef.chan->center_freq;
	*pos++ = ieee80211_frequency_to_channel(freq);		/* channel */
	*pos++ = csa_settings->count;				/* count */

	if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) {
		enum nl80211_channel_type ch_type;

		skb_put(skb, 3);
		*pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;	/* EID */
		*pos++ = 1;					/* IE length */
		ch_type = cfg80211_get_chandef_type(&csa_settings->chandef);
		if (ch_type == NL80211_CHAN_HT40PLUS)
			*pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
		else
			*pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
	}

	ieee80211_tx_skb(sdata, skb);
	return 0;
}

int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
			      struct cfg80211_csa_settings *csa_settings)
{
@@ -480,6 +534,12 @@ int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
	if (old_presp)
		kfree_rcu(old_presp, rcu_head);

	/* it might not send the beacon for a while. send an action frame
	 * immediately to announce the channel switch.
	 */
	if (csa_settings)
		ieee80211_send_action_csa(sdata, csa_settings);

	ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
 out:
	return ret;