cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ht.c (17112B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * HT handling
      4 *
      5 * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi>
      6 * Copyright 2002-2005, Instant802 Networks, Inc.
      7 * Copyright 2005-2006, Devicescape Software, Inc.
      8 * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz>
      9 * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
     10 * Copyright 2007-2010, Intel Corporation
     11 * Copyright 2017	Intel Deutschland GmbH
     12 * Copyright(c) 2020-2021 Intel Corporation
     13 */
     14
     15#include <linux/ieee80211.h>
     16#include <linux/export.h>
     17#include <net/mac80211.h>
     18#include "ieee80211_i.h"
     19#include "rate.h"
     20
     21static void __check_htcap_disable(struct ieee80211_ht_cap *ht_capa,
     22				  struct ieee80211_ht_cap *ht_capa_mask,
     23				  struct ieee80211_sta_ht_cap *ht_cap,
     24				  u16 flag)
     25{
     26	__le16 le_flag = cpu_to_le16(flag);
     27	if (ht_capa_mask->cap_info & le_flag) {
     28		if (!(ht_capa->cap_info & le_flag))
     29			ht_cap->cap &= ~flag;
     30	}
     31}
     32
     33static void __check_htcap_enable(struct ieee80211_ht_cap *ht_capa,
     34				  struct ieee80211_ht_cap *ht_capa_mask,
     35				  struct ieee80211_sta_ht_cap *ht_cap,
     36				  u16 flag)
     37{
     38	__le16 le_flag = cpu_to_le16(flag);
     39
     40	if ((ht_capa_mask->cap_info & le_flag) &&
     41	    (ht_capa->cap_info & le_flag))
     42		ht_cap->cap |= flag;
     43}
     44
     45void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
     46				     struct ieee80211_sta_ht_cap *ht_cap)
     47{
     48	struct ieee80211_ht_cap *ht_capa, *ht_capa_mask;
     49	u8 *scaps, *smask;
     50	int i;
     51
     52	if (!ht_cap->ht_supported)
     53		return;
     54
     55	switch (sdata->vif.type) {
     56	case NL80211_IFTYPE_STATION:
     57		ht_capa = &sdata->u.mgd.ht_capa;
     58		ht_capa_mask = &sdata->u.mgd.ht_capa_mask;
     59		break;
     60	case NL80211_IFTYPE_ADHOC:
     61		ht_capa = &sdata->u.ibss.ht_capa;
     62		ht_capa_mask = &sdata->u.ibss.ht_capa_mask;
     63		break;
     64	default:
     65		WARN_ON_ONCE(1);
     66		return;
     67	}
     68
     69	scaps = (u8 *)(&ht_capa->mcs.rx_mask);
     70	smask = (u8 *)(&ht_capa_mask->mcs.rx_mask);
     71
     72	/* NOTE:  If you add more over-rides here, update register_hw
     73	 * ht_capa_mod_mask logic in main.c as well.
     74	 * And, if this method can ever change ht_cap.ht_supported, fix
     75	 * the check in ieee80211_add_ht_ie.
     76	 */
     77
     78	/* check for HT over-rides, MCS rates first. */
     79	for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
     80		u8 m = smask[i];
     81		ht_cap->mcs.rx_mask[i] &= ~m; /* turn off all masked bits */
     82		/* Add back rates that are supported */
     83		ht_cap->mcs.rx_mask[i] |= (m & scaps[i]);
     84	}
     85
     86	/* Force removal of HT-40 capabilities? */
     87	__check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
     88			      IEEE80211_HT_CAP_SUP_WIDTH_20_40);
     89	__check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
     90			      IEEE80211_HT_CAP_SGI_40);
     91
     92	/* Allow user to disable SGI-20 (SGI-40 is handled above) */
     93	__check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
     94			      IEEE80211_HT_CAP_SGI_20);
     95
     96	/* Allow user to disable the max-AMSDU bit. */
     97	__check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
     98			      IEEE80211_HT_CAP_MAX_AMSDU);
     99
    100	/* Allow user to disable LDPC */
    101	__check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
    102			      IEEE80211_HT_CAP_LDPC_CODING);
    103
    104	/* Allow user to enable 40 MHz intolerant bit. */
    105	__check_htcap_enable(ht_capa, ht_capa_mask, ht_cap,
    106			     IEEE80211_HT_CAP_40MHZ_INTOLERANT);
    107
    108	/* Allow user to enable TX STBC bit  */
    109	__check_htcap_enable(ht_capa, ht_capa_mask, ht_cap,
    110			     IEEE80211_HT_CAP_TX_STBC);
    111
    112	/* Allow user to configure RX STBC bits */
    113	if (ht_capa_mask->cap_info & cpu_to_le16(IEEE80211_HT_CAP_RX_STBC))
    114		ht_cap->cap |= le16_to_cpu(ht_capa->cap_info) &
    115					IEEE80211_HT_CAP_RX_STBC;
    116
    117	/* Allow user to decrease AMPDU factor */
    118	if (ht_capa_mask->ampdu_params_info &
    119	    IEEE80211_HT_AMPDU_PARM_FACTOR) {
    120		u8 n = ht_capa->ampdu_params_info &
    121		       IEEE80211_HT_AMPDU_PARM_FACTOR;
    122		if (n < ht_cap->ampdu_factor)
    123			ht_cap->ampdu_factor = n;
    124	}
    125
    126	/* Allow the user to increase AMPDU density. */
    127	if (ht_capa_mask->ampdu_params_info &
    128	    IEEE80211_HT_AMPDU_PARM_DENSITY) {
    129		u8 n = (ht_capa->ampdu_params_info &
    130			IEEE80211_HT_AMPDU_PARM_DENSITY)
    131			>> IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT;
    132		if (n > ht_cap->ampdu_density)
    133			ht_cap->ampdu_density = n;
    134	}
    135}
    136
    137
    138bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
    139				       struct ieee80211_supported_band *sband,
    140				       const struct ieee80211_ht_cap *ht_cap_ie,
    141				       struct sta_info *sta)
    142{
    143	struct ieee80211_sta_ht_cap ht_cap, own_cap;
    144	u8 ampdu_info, tx_mcs_set_cap;
    145	int i, max_tx_streams;
    146	bool changed;
    147	enum ieee80211_sta_rx_bandwidth bw;
    148
    149	memset(&ht_cap, 0, sizeof(ht_cap));
    150
    151	if (!ht_cap_ie || !sband->ht_cap.ht_supported)
    152		goto apply;
    153
    154	ht_cap.ht_supported = true;
    155
    156	own_cap = sband->ht_cap;
    157
    158	/*
    159	 * If user has specified capability over-rides, take care
    160	 * of that if the station we're setting up is the AP or TDLS peer that
    161	 * we advertised a restricted capability set to. Override
    162	 * our own capabilities and then use those below.
    163	 */
    164	if (sdata->vif.type == NL80211_IFTYPE_STATION ||
    165	    sdata->vif.type == NL80211_IFTYPE_ADHOC)
    166		ieee80211_apply_htcap_overrides(sdata, &own_cap);
    167
    168	/*
    169	 * The bits listed in this expression should be
    170	 * the same for the peer and us, if the station
    171	 * advertises more then we can't use those thus
    172	 * we mask them out.
    173	 */
    174	ht_cap.cap = le16_to_cpu(ht_cap_ie->cap_info) &
    175		(own_cap.cap | ~(IEEE80211_HT_CAP_LDPC_CODING |
    176				 IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
    177				 IEEE80211_HT_CAP_GRN_FLD |
    178				 IEEE80211_HT_CAP_SGI_20 |
    179				 IEEE80211_HT_CAP_SGI_40 |
    180				 IEEE80211_HT_CAP_DSSSCCK40));
    181
    182	/*
    183	 * The STBC bits are asymmetric -- if we don't have
    184	 * TX then mask out the peer's RX and vice versa.
    185	 */
    186	if (!(own_cap.cap & IEEE80211_HT_CAP_TX_STBC))
    187		ht_cap.cap &= ~IEEE80211_HT_CAP_RX_STBC;
    188	if (!(own_cap.cap & IEEE80211_HT_CAP_RX_STBC))
    189		ht_cap.cap &= ~IEEE80211_HT_CAP_TX_STBC;
    190
    191	ampdu_info = ht_cap_ie->ampdu_params_info;
    192	ht_cap.ampdu_factor =
    193		ampdu_info & IEEE80211_HT_AMPDU_PARM_FACTOR;
    194	ht_cap.ampdu_density =
    195		(ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2;
    196
    197	/* own MCS TX capabilities */
    198	tx_mcs_set_cap = own_cap.mcs.tx_params;
    199
    200	/* Copy peer MCS TX capabilities, the driver might need them. */
    201	ht_cap.mcs.tx_params = ht_cap_ie->mcs.tx_params;
    202
    203	/* can we TX with MCS rates? */
    204	if (!(tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED))
    205		goto apply;
    206
    207	/* Counting from 0, therefore +1 */
    208	if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_RX_DIFF)
    209		max_tx_streams =
    210			((tx_mcs_set_cap & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK)
    211				>> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1;
    212	else
    213		max_tx_streams = IEEE80211_HT_MCS_TX_MAX_STREAMS;
    214
    215	/*
    216	 * 802.11n-2009 20.3.5 / 20.6 says:
    217	 * - indices 0 to 7 and 32 are single spatial stream
    218	 * - 8 to 31 are multiple spatial streams using equal modulation
    219	 *   [8..15 for two streams, 16..23 for three and 24..31 for four]
    220	 * - remainder are multiple spatial streams using unequal modulation
    221	 */
    222	for (i = 0; i < max_tx_streams; i++)
    223		ht_cap.mcs.rx_mask[i] =
    224			own_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i];
    225
    226	if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION)
    227		for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE;
    228		     i < IEEE80211_HT_MCS_MASK_LEN; i++)
    229			ht_cap.mcs.rx_mask[i] =
    230				own_cap.mcs.rx_mask[i] &
    231					ht_cap_ie->mcs.rx_mask[i];
    232
    233	/* handle MCS rate 32 too */
    234	if (own_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)
    235		ht_cap.mcs.rx_mask[32/8] |= 1;
    236
    237	/* set Rx highest rate */
    238	ht_cap.mcs.rx_highest = ht_cap_ie->mcs.rx_highest;
    239
    240	if (ht_cap.cap & IEEE80211_HT_CAP_MAX_AMSDU)
    241		sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_7935;
    242	else
    243		sta->sta.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_3839;
    244
    245 apply:
    246	changed = memcmp(&sta->sta.deflink.ht_cap, &ht_cap, sizeof(ht_cap));
    247
    248	memcpy(&sta->sta.deflink.ht_cap, &ht_cap, sizeof(ht_cap));
    249
    250	switch (sdata->vif.bss_conf.chandef.width) {
    251	default:
    252		WARN_ON_ONCE(1);
    253		fallthrough;
    254	case NL80211_CHAN_WIDTH_20_NOHT:
    255	case NL80211_CHAN_WIDTH_20:
    256		bw = IEEE80211_STA_RX_BW_20;
    257		break;
    258	case NL80211_CHAN_WIDTH_40:
    259	case NL80211_CHAN_WIDTH_80:
    260	case NL80211_CHAN_WIDTH_80P80:
    261	case NL80211_CHAN_WIDTH_160:
    262		bw = ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
    263				IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
    264		break;
    265	}
    266
    267	sta->sta.deflink.bandwidth = bw;
    268
    269	sta->deflink.cur_max_bandwidth =
    270		ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
    271				IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
    272
    273	if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
    274	    sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
    275		enum ieee80211_smps_mode smps_mode;
    276
    277		switch ((ht_cap.cap & IEEE80211_HT_CAP_SM_PS)
    278				>> IEEE80211_HT_CAP_SM_PS_SHIFT) {
    279		case WLAN_HT_CAP_SM_PS_INVALID:
    280		case WLAN_HT_CAP_SM_PS_STATIC:
    281			smps_mode = IEEE80211_SMPS_STATIC;
    282			break;
    283		case WLAN_HT_CAP_SM_PS_DYNAMIC:
    284			smps_mode = IEEE80211_SMPS_DYNAMIC;
    285			break;
    286		case WLAN_HT_CAP_SM_PS_DISABLED:
    287			smps_mode = IEEE80211_SMPS_OFF;
    288			break;
    289		}
    290
    291		if (smps_mode != sta->sta.smps_mode)
    292			changed = true;
    293		sta->sta.smps_mode = smps_mode;
    294	} else {
    295		sta->sta.smps_mode = IEEE80211_SMPS_OFF;
    296	}
    297	return changed;
    298}
    299
    300void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta,
    301					 enum ieee80211_agg_stop_reason reason)
    302{
    303	int i;
    304
    305	mutex_lock(&sta->ampdu_mlme.mtx);
    306	for (i = 0; i <  IEEE80211_NUM_TIDS; i++)
    307		___ieee80211_stop_rx_ba_session(sta, i, WLAN_BACK_RECIPIENT,
    308						WLAN_REASON_QSTA_LEAVE_QBSS,
    309						reason != AGG_STOP_DESTROY_STA &&
    310						reason != AGG_STOP_PEER_REQUEST);
    311
    312	for (i = 0; i <  IEEE80211_NUM_TIDS; i++)
    313		___ieee80211_stop_tx_ba_session(sta, i, reason);
    314	mutex_unlock(&sta->ampdu_mlme.mtx);
    315
    316	/*
    317	 * In case the tear down is part of a reconfigure due to HW restart
    318	 * request, it is possible that the low level driver requested to stop
    319	 * the BA session, so handle it to properly clean tid_tx data.
    320	 */
    321	if(reason == AGG_STOP_DESTROY_STA) {
    322		cancel_work_sync(&sta->ampdu_mlme.work);
    323
    324		mutex_lock(&sta->ampdu_mlme.mtx);
    325		for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
    326			struct tid_ampdu_tx *tid_tx =
    327				rcu_dereference_protected_tid_tx(sta, i);
    328
    329			if (!tid_tx)
    330				continue;
    331
    332			if (test_and_clear_bit(HT_AGG_STATE_STOP_CB, &tid_tx->state))
    333				ieee80211_stop_tx_ba_cb(sta, i, tid_tx);
    334		}
    335		mutex_unlock(&sta->ampdu_mlme.mtx);
    336	}
    337}
    338
    339void ieee80211_ba_session_work(struct work_struct *work)
    340{
    341	struct sta_info *sta =
    342		container_of(work, struct sta_info, ampdu_mlme.work);
    343	struct tid_ampdu_tx *tid_tx;
    344	bool blocked;
    345	int tid;
    346
    347	/* When this flag is set, new sessions should be blocked. */
    348	blocked = test_sta_flag(sta, WLAN_STA_BLOCK_BA);
    349
    350	mutex_lock(&sta->ampdu_mlme.mtx);
    351	for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) {
    352		if (test_and_clear_bit(tid, sta->ampdu_mlme.tid_rx_timer_expired))
    353			___ieee80211_stop_rx_ba_session(
    354				sta, tid, WLAN_BACK_RECIPIENT,
    355				WLAN_REASON_QSTA_TIMEOUT, true);
    356
    357		if (test_and_clear_bit(tid,
    358				       sta->ampdu_mlme.tid_rx_stop_requested))
    359			___ieee80211_stop_rx_ba_session(
    360				sta, tid, WLAN_BACK_RECIPIENT,
    361				WLAN_REASON_UNSPECIFIED, true);
    362
    363		if (!blocked &&
    364		    test_and_clear_bit(tid,
    365				       sta->ampdu_mlme.tid_rx_manage_offl))
    366			___ieee80211_start_rx_ba_session(sta, 0, 0, 0, 1, tid,
    367							 IEEE80211_MAX_AMPDU_BUF_HT,
    368							 false, true, NULL);
    369
    370		if (test_and_clear_bit(tid + IEEE80211_NUM_TIDS,
    371				       sta->ampdu_mlme.tid_rx_manage_offl))
    372			___ieee80211_stop_rx_ba_session(
    373				sta, tid, WLAN_BACK_RECIPIENT,
    374				0, false);
    375
    376		spin_lock_bh(&sta->lock);
    377
    378		tid_tx = sta->ampdu_mlme.tid_start_tx[tid];
    379		if (!blocked && tid_tx) {
    380			/*
    381			 * Assign it over to the normal tid_tx array
    382			 * where it "goes live".
    383			 */
    384
    385			sta->ampdu_mlme.tid_start_tx[tid] = NULL;
    386			/* could there be a race? */
    387			if (sta->ampdu_mlme.tid_tx[tid])
    388				kfree(tid_tx);
    389			else
    390				ieee80211_assign_tid_tx(sta, tid, tid_tx);
    391			spin_unlock_bh(&sta->lock);
    392
    393			ieee80211_tx_ba_session_handle_start(sta, tid);
    394			continue;
    395		}
    396		spin_unlock_bh(&sta->lock);
    397
    398		tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
    399		if (!tid_tx)
    400			continue;
    401
    402		if (!blocked &&
    403		    test_and_clear_bit(HT_AGG_STATE_START_CB, &tid_tx->state))
    404			ieee80211_start_tx_ba_cb(sta, tid, tid_tx);
    405		if (test_and_clear_bit(HT_AGG_STATE_WANT_STOP, &tid_tx->state))
    406			___ieee80211_stop_tx_ba_session(sta, tid,
    407							AGG_STOP_LOCAL_REQUEST);
    408		if (test_and_clear_bit(HT_AGG_STATE_STOP_CB, &tid_tx->state))
    409			ieee80211_stop_tx_ba_cb(sta, tid, tid_tx);
    410	}
    411	mutex_unlock(&sta->ampdu_mlme.mtx);
    412}
    413
    414void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
    415			  const u8 *da, u16 tid,
    416			  u16 initiator, u16 reason_code)
    417{
    418	struct ieee80211_local *local = sdata->local;
    419	struct sk_buff *skb;
    420	struct ieee80211_mgmt *mgmt;
    421	u16 params;
    422
    423	skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
    424	if (!skb)
    425		return;
    426
    427	skb_reserve(skb, local->hw.extra_tx_headroom);
    428	mgmt = skb_put_zero(skb, 24);
    429	memcpy(mgmt->da, da, ETH_ALEN);
    430	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
    431	if (sdata->vif.type == NL80211_IFTYPE_AP ||
    432	    sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
    433	    sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
    434		memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
    435	else if (sdata->vif.type == NL80211_IFTYPE_STATION)
    436		memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN);
    437	else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
    438		memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN);
    439
    440	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
    441					  IEEE80211_STYPE_ACTION);
    442
    443	skb_put(skb, 1 + sizeof(mgmt->u.action.u.delba));
    444
    445	mgmt->u.action.category = WLAN_CATEGORY_BACK;
    446	mgmt->u.action.u.delba.action_code = WLAN_ACTION_DELBA;
    447	params = (u16)(initiator << 11); 	/* bit 11 initiator */
    448	params |= (u16)(tid << 12); 		/* bit 15:12 TID number */
    449
    450	mgmt->u.action.u.delba.params = cpu_to_le16(params);
    451	mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code);
    452
    453	ieee80211_tx_skb(sdata, skb);
    454}
    455
    456void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
    457			     struct sta_info *sta,
    458			     struct ieee80211_mgmt *mgmt, size_t len)
    459{
    460	u16 tid, params;
    461	u16 initiator;
    462
    463	params = le16_to_cpu(mgmt->u.action.u.delba.params);
    464	tid = (params & IEEE80211_DELBA_PARAM_TID_MASK) >> 12;
    465	initiator = (params & IEEE80211_DELBA_PARAM_INITIATOR_MASK) >> 11;
    466
    467	ht_dbg_ratelimited(sdata, "delba from %pM (%s) tid %d reason code %d\n",
    468			   mgmt->sa, initiator ? "initiator" : "recipient",
    469			   tid,
    470			   le16_to_cpu(mgmt->u.action.u.delba.reason_code));
    471
    472	if (initiator == WLAN_BACK_INITIATOR)
    473		__ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_INITIATOR, 0,
    474					       true);
    475	else
    476		__ieee80211_stop_tx_ba_session(sta, tid, AGG_STOP_PEER_REQUEST);
    477}
    478
    479enum nl80211_smps_mode
    480ieee80211_smps_mode_to_smps_mode(enum ieee80211_smps_mode smps)
    481{
    482	switch (smps) {
    483	case IEEE80211_SMPS_OFF:
    484		return NL80211_SMPS_OFF;
    485	case IEEE80211_SMPS_STATIC:
    486		return NL80211_SMPS_STATIC;
    487	case IEEE80211_SMPS_DYNAMIC:
    488		return NL80211_SMPS_DYNAMIC;
    489	default:
    490		return NL80211_SMPS_OFF;
    491	}
    492}
    493
    494int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
    495			       enum ieee80211_smps_mode smps, const u8 *da,
    496			       const u8 *bssid)
    497{
    498	struct ieee80211_local *local = sdata->local;
    499	struct sk_buff *skb;
    500	struct ieee80211_mgmt *action_frame;
    501
    502	/* 27 = header + category + action + smps mode */
    503	skb = dev_alloc_skb(27 + local->hw.extra_tx_headroom);
    504	if (!skb)
    505		return -ENOMEM;
    506
    507	skb_reserve(skb, local->hw.extra_tx_headroom);
    508	action_frame = skb_put(skb, 27);
    509	memcpy(action_frame->da, da, ETH_ALEN);
    510	memcpy(action_frame->sa, sdata->dev->dev_addr, ETH_ALEN);
    511	memcpy(action_frame->bssid, bssid, ETH_ALEN);
    512	action_frame->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
    513						  IEEE80211_STYPE_ACTION);
    514	action_frame->u.action.category = WLAN_CATEGORY_HT;
    515	action_frame->u.action.u.ht_smps.action = WLAN_HT_ACTION_SMPS;
    516	switch (smps) {
    517	case IEEE80211_SMPS_AUTOMATIC:
    518	case IEEE80211_SMPS_NUM_MODES:
    519		WARN_ON(1);
    520		fallthrough;
    521	case IEEE80211_SMPS_OFF:
    522		action_frame->u.action.u.ht_smps.smps_control =
    523				WLAN_HT_SMPS_CONTROL_DISABLED;
    524		break;
    525	case IEEE80211_SMPS_STATIC:
    526		action_frame->u.action.u.ht_smps.smps_control =
    527				WLAN_HT_SMPS_CONTROL_STATIC;
    528		break;
    529	case IEEE80211_SMPS_DYNAMIC:
    530		action_frame->u.action.u.ht_smps.smps_control =
    531				WLAN_HT_SMPS_CONTROL_DYNAMIC;
    532		break;
    533	}
    534
    535	/* we'll do more on status of this frame */
    536	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
    537	ieee80211_tx_skb(sdata, skb);
    538
    539	return 0;
    540}
    541
    542void ieee80211_request_smps_mgd_work(struct work_struct *work)
    543{
    544	struct ieee80211_sub_if_data *sdata =
    545		container_of(work, struct ieee80211_sub_if_data,
    546			     u.mgd.request_smps_work);
    547
    548	sdata_lock(sdata);
    549	__ieee80211_request_smps_mgd(sdata, sdata->u.mgd.driver_smps_mode);
    550	sdata_unlock(sdata);
    551}
    552
    553void ieee80211_request_smps(struct ieee80211_vif *vif,
    554			    enum ieee80211_smps_mode smps_mode)
    555{
    556	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
    557
    558	if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION))
    559		return;
    560
    561	if (sdata->u.mgd.driver_smps_mode == smps_mode)
    562		return;
    563
    564	sdata->u.mgd.driver_smps_mode = smps_mode;
    565	ieee80211_queue_work(&sdata->local->hw,
    566			     &sdata->u.mgd.request_smps_work);
    567}
    568/* this might change ... don't want non-open drivers using it */
    569EXPORT_SYMBOL_GPL(ieee80211_request_smps);