Commit 05d10957 authored by Thomas Pedersen's avatar Thomas Pedersen Committed by Johannes Berg
Browse files

mac80211: encode listen interval for S1G



S1G allows listen interval up to 2^14 * 10000 beacon
intervals. In order to do this listen interval needs a
scaling factor applied to the lower 14 bits. Calculate
this and properly encode the listen interval for S1G STAs.

See IEEE802.11ah-2016 Table 9-44a for reference.

Signed-off-by: default avatarThomas Pedersen <thomas@adapt-ip.com>
Link: https://lore.kernel.org/r/20200922022818.15855-10-thomas@adapt-ip.com


[move listen_int_usf into function using it]
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 80ca2571
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -2448,6 +2448,13 @@ ieee80211_he_spr_size(const u8 *he_spr_ie)
#define S1G_OPER_CH_WIDTH_PRIMARY_1MHZ	BIT(0)
#define S1G_OPER_CH_WIDTH_OPER		GENMASK(4, 1)


#define LISTEN_INT_USF	GENMASK(15, 14)
#define LISTEN_INT_UI	GENMASK(13, 0)

#define IEEE80211_MAX_USF	FIELD_MAX(LISTEN_INT_USF)
#define IEEE80211_MAX_UI	FIELD_MAX(LISTEN_INT_UI)

/* Authentication algorithms */
#define WLAN_AUTH_OPEN 0
#define WLAN_AUTH_SHARED_KEY 1
+1 −0
Original line number Diff line number Diff line
@@ -2300,6 +2300,7 @@ void ieee80211_tdls_chsw_work(struct work_struct *wk);
void ieee80211_tdls_handle_disconnect(struct ieee80211_sub_if_data *sdata,
				      const u8 *peer, u16 reason);
const char *ieee80211_get_reason_code_string(u16 reason_code);
u16 ieee80211_encode_usf(int val);

extern const struct ethtool_ops ieee80211_ethtool_ops;

+6 −4
Original line number Diff line number Diff line
@@ -696,6 +696,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
	struct ieee80211_chanctx_conf *chanctx_conf;
	struct ieee80211_channel *chan;
	u32 rates = 0;
	__le16 listen_int;
	struct element *ext_capa = NULL;

	/* we know it's writable, cast away the const */
@@ -784,13 +785,15 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
	memcpy(mgmt->bssid, assoc_data->bss->bssid, ETH_ALEN);

	listen_int = cpu_to_le16(sband->band == NL80211_BAND_S1GHZ ?
			ieee80211_encode_usf(local->hw.conf.listen_interval) :
			local->hw.conf.listen_interval);
	if (!is_zero_ether_addr(assoc_data->prev_bssid)) {
		skb_put(skb, 10);
		mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
						  IEEE80211_STYPE_REASSOC_REQ);
		mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab);
		mgmt->u.reassoc_req.listen_interval =
				cpu_to_le16(local->hw.conf.listen_interval);
		mgmt->u.reassoc_req.listen_interval = listen_int;
		memcpy(mgmt->u.reassoc_req.current_ap, assoc_data->prev_bssid,
		       ETH_ALEN);
	} else {
@@ -798,8 +801,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
		mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
						  IEEE80211_STYPE_ASSOC_REQ);
		mgmt->u.assoc_req.capab_info = cpu_to_le16(capab);
		mgmt->u.assoc_req.listen_interval =
				cpu_to_le16(local->hw.conf.listen_interval);
		mgmt->u.assoc_req.listen_interval = listen_int;
	}

	/* SSID */
+21 −0
Original line number Diff line number Diff line
@@ -4383,3 +4383,24 @@ const u8 ieee80211_ac_to_qos_mask[IEEE80211_NUM_ACS] = {
	IEEE80211_WMM_IE_STA_QOSINFO_AC_BE,
	IEEE80211_WMM_IE_STA_QOSINFO_AC_BK
};

u16 ieee80211_encode_usf(int listen_interval)
{
	static const int listen_int_usf[] = { 1, 10, 1000, 10000 };
	u16 ui, usf = 0;

	/* find greatest USF */
	while (usf < IEEE80211_MAX_USF) {
		if (listen_interval % listen_int_usf[usf + 1])
			break;
		usf += 1;
	}
	ui = listen_interval / listen_int_usf[usf];

	/* error if there is a remainder. Should've been checked by user */
	WARN_ON_ONCE(ui > IEEE80211_MAX_UI);
	listen_interval = FIELD_PREP(LISTEN_INT_USF, usf) |
			  FIELD_PREP(LISTEN_INT_UI, ui);

	return (u16) listen_interval;
}