ht.c 14.8 KB
Newer Older
1 2 3 4
/*
 * HT handling
 *
 * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi>
5 6
 * Copyright 2002-2005, Instant802 Networks, Inc.
 * Copyright 2005-2006, Devicescape Software, Inc.
7 8
 * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz>
 * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
9
 * Copyright 2007-2010, Intel Corporation
10 11 12 13 14 15 16
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/ieee80211.h>
17
#include <linux/export.h>
18 19
#include <net/mac80211.h>
#include "ieee80211_i.h"
20
#include "rate.h"
21

22 23
static void __check_htcap_disable(struct ieee80211_ht_cap *ht_capa,
				  struct ieee80211_ht_cap *ht_capa_mask,
24 25
				  struct ieee80211_sta_ht_cap *ht_cap,
				  u16 flag)
B
Ben Greear 已提交
26 27
{
	__le16 le_flag = cpu_to_le16(flag);
28 29
	if (ht_capa_mask->cap_info & le_flag) {
		if (!(ht_capa->cap_info & le_flag))
B
Ben Greear 已提交
30 31 32 33 34 35 36
			ht_cap->cap &= ~flag;
	}
}

void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
				     struct ieee80211_sta_ht_cap *ht_cap)
{
37 38
	struct ieee80211_ht_cap *ht_capa, *ht_capa_mask;
	u8 *scaps, *smask;
B
Ben Greear 已提交
39 40
	int i;

41 42 43
	if (!ht_cap->ht_supported)
		return;

44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
	switch (sdata->vif.type) {
	case NL80211_IFTYPE_STATION:
		ht_capa = &sdata->u.mgd.ht_capa;
		ht_capa_mask = &sdata->u.mgd.ht_capa_mask;
		break;
	case NL80211_IFTYPE_ADHOC:
		ht_capa = &sdata->u.ibss.ht_capa;
		ht_capa_mask = &sdata->u.ibss.ht_capa_mask;
		break;
	default:
		WARN_ON_ONCE(1);
		return;
	}

	scaps = (u8 *)(&ht_capa->mcs.rx_mask);
	smask = (u8 *)(&ht_capa_mask->mcs.rx_mask);

B
Ben Greear 已提交
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
	/* NOTE:  If you add more over-rides here, update register_hw
	 * ht_capa_mod_msk logic in main.c as well.
	 * And, if this method can ever change ht_cap.ht_supported, fix
	 * the check in ieee80211_add_ht_ie.
	 */

	/* check for HT over-rides, MCS rates first. */
	for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
		u8 m = smask[i];
		ht_cap->mcs.rx_mask[i] &= ~m; /* turn off all masked bits */
		/* Add back rates that are supported */
		ht_cap->mcs.rx_mask[i] |= (m & scaps[i]);
	}

	/* Force removal of HT-40 capabilities? */
76 77 78 79
	__check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
			      IEEE80211_HT_CAP_SUP_WIDTH_20_40);
	__check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
			      IEEE80211_HT_CAP_SGI_40);
B
Ben Greear 已提交
80

B
Ben Greear 已提交
81
	/* Allow user to disable SGI-20 (SGI-40 is handled above) */
82 83
	__check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
			      IEEE80211_HT_CAP_SGI_20);
B
Ben Greear 已提交
84

B
Ben Greear 已提交
85
	/* Allow user to disable the max-AMSDU bit. */
86 87
	__check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
			      IEEE80211_HT_CAP_MAX_AMSDU);
B
Ben Greear 已提交
88 89

	/* Allow user to decrease AMPDU factor */
90
	if (ht_capa_mask->ampdu_params_info &
B
Ben Greear 已提交
91
	    IEEE80211_HT_AMPDU_PARM_FACTOR) {
92 93
		u8 n = ht_capa->ampdu_params_info &
		       IEEE80211_HT_AMPDU_PARM_FACTOR;
B
Ben Greear 已提交
94 95 96 97 98
		if (n < ht_cap->ampdu_factor)
			ht_cap->ampdu_factor = n;
	}

	/* Allow the user to increase AMPDU density. */
99
	if (ht_capa_mask->ampdu_params_info &
B
Ben Greear 已提交
100
	    IEEE80211_HT_AMPDU_PARM_DENSITY) {
101
		u8 n = (ht_capa->ampdu_params_info &
B
Ben Greear 已提交
102 103 104 105 106 107 108 109
			IEEE80211_HT_AMPDU_PARM_DENSITY)
			>> IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT;
		if (n > ht_cap->ampdu_density)
			ht_cap->ampdu_density = n;
	}
}


110
bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata,
B
Ben Greear 已提交
111
				       struct ieee80211_supported_band *sband,
J
Johannes Berg 已提交
112
				       const struct ieee80211_ht_cap *ht_cap_ie,
113
				       struct sta_info *sta)
114
{
115
	struct ieee80211_sta_ht_cap ht_cap, own_cap;
J
Johannes Berg 已提交
116 117
	u8 ampdu_info, tx_mcs_set_cap;
	int i, max_tx_streams;
118 119
	bool changed;
	enum ieee80211_sta_rx_bandwidth bw;
120
	enum ieee80211_smps_mode smps_mode;
121

122
	memset(&ht_cap, 0, sizeof(ht_cap));
123

124
	if (!ht_cap_ie || !sband->ht_cap.ht_supported)
125
		goto apply;
J
Johannes Berg 已提交
126

127
	ht_cap.ht_supported = true;
J
Johannes Berg 已提交
128

129 130 131 132 133 134 135 136
	own_cap = sband->ht_cap;

	/*
	 * If user has specified capability over-rides, take care
	 * of that if the station we're setting up is the AP that
	 * we advertised a restricted capability set to. Override
	 * our own capabilities and then use those below.
	 */
137 138
	if ((sdata->vif.type == NL80211_IFTYPE_STATION ||
	     sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
139 140 141
	    !test_sta_flag(sta, WLAN_STA_TDLS_PEER))
		ieee80211_apply_htcap_overrides(sdata, &own_cap);

J
Johannes Berg 已提交
142 143 144 145 146 147
	/*
	 * The bits listed in this expression should be
	 * the same for the peer and us, if the station
	 * advertises more then we can't use those thus
	 * we mask them out.
	 */
148
	ht_cap.cap = le16_to_cpu(ht_cap_ie->cap_info) &
149 150 151 152 153 154
		(own_cap.cap | ~(IEEE80211_HT_CAP_LDPC_CODING |
				 IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
				 IEEE80211_HT_CAP_GRN_FLD |
				 IEEE80211_HT_CAP_SGI_20 |
				 IEEE80211_HT_CAP_SGI_40 |
				 IEEE80211_HT_CAP_DSSSCCK40));
155

J
Johannes Berg 已提交
156 157 158 159
	/*
	 * The STBC bits are asymmetric -- if we don't have
	 * TX then mask out the peer's RX and vice versa.
	 */
160
	if (!(own_cap.cap & IEEE80211_HT_CAP_TX_STBC))
161
		ht_cap.cap &= ~IEEE80211_HT_CAP_RX_STBC;
162
	if (!(own_cap.cap & IEEE80211_HT_CAP_RX_STBC))
163
		ht_cap.cap &= ~IEEE80211_HT_CAP_TX_STBC;
J
Johannes Berg 已提交
164

J
Johannes Berg 已提交
165
	ampdu_info = ht_cap_ie->ampdu_params_info;
166
	ht_cap.ampdu_factor =
J
Johannes Berg 已提交
167
		ampdu_info & IEEE80211_HT_AMPDU_PARM_FACTOR;
168
	ht_cap.ampdu_density =
J
Johannes Berg 已提交
169
		(ampdu_info & IEEE80211_HT_AMPDU_PARM_DENSITY) >> 2;
170

J
Johannes Berg 已提交
171
	/* own MCS TX capabilities */
172
	tx_mcs_set_cap = own_cap.mcs.tx_params;
J
Johannes Berg 已提交
173

174
	/* Copy peer MCS TX capabilities, the driver might need them. */
175
	ht_cap.mcs.tx_params = ht_cap_ie->mcs.tx_params;
176

J
Johannes Berg 已提交
177 178
	/* can we TX with MCS rates? */
	if (!(tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED))
179
		goto apply;
J
Johannes Berg 已提交
180 181 182 183 184 185 186 187 188 189

	/* Counting from 0, therefore +1 */
	if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_RX_DIFF)
		max_tx_streams =
			((tx_mcs_set_cap & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK)
				>> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1;
	else
		max_tx_streams = IEEE80211_HT_MCS_TX_MAX_STREAMS;

	/*
190
	 * 802.11n-2009 20.3.5 / 20.6 says:
J
Johannes Berg 已提交
191 192 193 194 195 196
	 * - indices 0 to 7 and 32 are single spatial stream
	 * - 8 to 31 are multiple spatial streams using equal modulation
	 *   [8..15 for two streams, 16..23 for three and 24..31 for four]
	 * - remainder are multiple spatial streams using unequal modulation
	 */
	for (i = 0; i < max_tx_streams; i++)
197
		ht_cap.mcs.rx_mask[i] =
198
			own_cap.mcs.rx_mask[i] & ht_cap_ie->mcs.rx_mask[i];
J
Johannes Berg 已提交
199 200 201 202

	if (tx_mcs_set_cap & IEEE80211_HT_MCS_TX_UNEQUAL_MODULATION)
		for (i = IEEE80211_HT_MCS_UNEQUAL_MODULATION_START_BYTE;
		     i < IEEE80211_HT_MCS_MASK_LEN; i++)
203
			ht_cap.mcs.rx_mask[i] =
204
				own_cap.mcs.rx_mask[i] &
J
Johannes Berg 已提交
205
					ht_cap_ie->mcs.rx_mask[i];
J
Johannes Berg 已提交
206 207

	/* handle MCS rate 32 too */
208
	if (own_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1)
209
		ht_cap.mcs.rx_mask[32/8] |= 1;
B
Ben Greear 已提交
210

211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
 apply:
	changed = memcmp(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap));

	memcpy(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap));

	switch (sdata->vif.bss_conf.chandef.width) {
	default:
		WARN_ON_ONCE(1);
		/* fall through */
	case NL80211_CHAN_WIDTH_20_NOHT:
	case NL80211_CHAN_WIDTH_20:
		bw = IEEE80211_STA_RX_BW_20;
		break;
	case NL80211_CHAN_WIDTH_40:
	case NL80211_CHAN_WIDTH_80:
	case NL80211_CHAN_WIDTH_80P80:
	case NL80211_CHAN_WIDTH_160:
		bw = ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
				IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
		break;
	}

	if (bw != sta->sta.bandwidth)
		changed = true;
	sta->sta.bandwidth = bw;

237 238 239 240
	sta->cur_max_bandwidth =
		ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
				IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;

241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
	switch ((ht_cap.cap & IEEE80211_HT_CAP_SM_PS)
			>> IEEE80211_HT_CAP_SM_PS_SHIFT) {
	case WLAN_HT_CAP_SM_PS_INVALID:
	case WLAN_HT_CAP_SM_PS_STATIC:
		smps_mode = IEEE80211_SMPS_STATIC;
		break;
	case WLAN_HT_CAP_SM_PS_DYNAMIC:
		smps_mode = IEEE80211_SMPS_DYNAMIC;
		break;
	case WLAN_HT_CAP_SM_PS_DISABLED:
		smps_mode = IEEE80211_SMPS_OFF;
		break;
	}

	if (smps_mode != sta->sta.smps_mode)
		changed = true;
	sta->sta.smps_mode = smps_mode;

259
	return changed;
J
Johannes Berg 已提交
260 261
}

262 263
void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta,
					 enum ieee80211_agg_stop_reason reason)
264
{
J
Johannes Berg 已提交
265
	int i;
266

267 268
	cancel_work_sync(&sta->ampdu_mlme.work);

269
	for (i = 0; i <  IEEE80211_NUM_TIDS; i++) {
270
		__ieee80211_stop_tx_ba_session(sta, i, reason);
271
		__ieee80211_stop_rx_ba_session(sta, i, WLAN_BACK_RECIPIENT,
272 273 274
					       WLAN_REASON_QSTA_LEAVE_QBSS,
					       reason != AGG_STOP_DESTROY_STA &&
					       reason != AGG_STOP_PEER_REQUEST);
275 276 277
	}
}

J
Johannes Berg 已提交
278 279 280 281 282 283 284 285 286 287 288 289 290
void ieee80211_ba_session_work(struct work_struct *work)
{
	struct sta_info *sta =
		container_of(work, struct sta_info, ampdu_mlme.work);
	struct tid_ampdu_tx *tid_tx;
	int tid;

	/*
	 * When this flag is set, new sessions should be
	 * blocked, and existing sessions will be torn
	 * down by the code that set the flag, so this
	 * need not run.
	 */
J
Johannes Berg 已提交
291
	if (test_sta_flag(sta, WLAN_STA_BLOCK_BA))
J
Johannes Berg 已提交
292 293
		return;

294
	mutex_lock(&sta->ampdu_mlme.mtx);
295
	for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) {
296 297 298
		if (test_and_clear_bit(tid, sta->ampdu_mlme.tid_rx_timer_expired))
			___ieee80211_stop_rx_ba_session(
				sta, tid, WLAN_BACK_RECIPIENT,
299
				WLAN_REASON_QSTA_TIMEOUT, true);
300

301 302 303 304 305 306
		if (test_and_clear_bit(tid,
				       sta->ampdu_mlme.tid_rx_stop_requested))
			___ieee80211_stop_rx_ba_session(
				sta, tid, WLAN_BACK_RECIPIENT,
				WLAN_REASON_UNSPECIFIED, true);

307 308
		spin_lock_bh(&sta->lock);

J
Johannes Berg 已提交
309 310 311 312 313 314 315 316 317 318 319 320 321 322
		tid_tx = sta->ampdu_mlme.tid_start_tx[tid];
		if (tid_tx) {
			/*
			 * Assign it over to the normal tid_tx array
			 * where it "goes live".
			 */

			sta->ampdu_mlme.tid_start_tx[tid] = NULL;
			/* could there be a race? */
			if (sta->ampdu_mlme.tid_tx[tid])
				kfree(tid_tx);
			else
				ieee80211_assign_tid_tx(sta, tid, tid_tx);
			spin_unlock_bh(&sta->lock);
J
Johannes Berg 已提交
323 324

			ieee80211_tx_ba_session_handle_start(sta, tid);
J
Johannes Berg 已提交
325 326
			continue;
		}
327
		spin_unlock_bh(&sta->lock);
J
Johannes Berg 已提交
328

J
Johannes Berg 已提交
329
		tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
J
Johannes Berg 已提交
330 331
		if (tid_tx && test_and_clear_bit(HT_AGG_STATE_WANT_STOP,
						 &tid_tx->state))
J
Johannes Berg 已提交
332
			___ieee80211_stop_tx_ba_session(sta, tid,
333
							AGG_STOP_LOCAL_REQUEST);
J
Johannes Berg 已提交
334
	}
335
	mutex_unlock(&sta->ampdu_mlme.mtx);
J
Johannes Berg 已提交
336 337
}

J
Johannes Berg 已提交
338 339 340
void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata,
			  const u8 *da, u16 tid,
			  u16 initiator, u16 reason_code)
341 342 343 344 345 346 347
{
	struct ieee80211_local *local = sdata->local;
	struct sk_buff *skb;
	struct ieee80211_mgmt *mgmt;
	u16 params;

	skb = dev_alloc_skb(sizeof(*mgmt) + local->hw.extra_tx_headroom);
348
	if (!skb)
349 350 351 352 353 354
		return;

	skb_reserve(skb, local->hw.extra_tx_headroom);
	mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
	memset(mgmt, 0, 24);
	memcpy(mgmt->da, da, ETH_ALEN);
355
	memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
356
	if (sdata->vif.type == NL80211_IFTYPE_AP ||
357 358
	    sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
	    sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
359
		memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
360 361
	else if (sdata->vif.type == NL80211_IFTYPE_STATION)
		memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN);
362 363
	else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
		memcpy(mgmt->bssid, sdata->u.ibss.bssid, ETH_ALEN);
364

365 366 367 368 369 370 371 372 373 374 375 376 377
	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
					  IEEE80211_STYPE_ACTION);

	skb_put(skb, 1 + sizeof(mgmt->u.action.u.delba));

	mgmt->u.action.category = WLAN_CATEGORY_BACK;
	mgmt->u.action.u.delba.action_code = WLAN_ACTION_DELBA;
	params = (u16)(initiator << 11); 	/* bit 11 initiator */
	params |= (u16)(tid << 12); 		/* bit 15:12 TID number */

	mgmt->u.action.u.delba.params = cpu_to_le16(params);
	mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code);

378
	ieee80211_tx_skb_tid(sdata, skb, tid);
379 380
}

381 382 383 384 385 386 387 388 389 390 391
void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata,
			     struct sta_info *sta,
			     struct ieee80211_mgmt *mgmt, size_t len)
{
	u16 tid, params;
	u16 initiator;

	params = le16_to_cpu(mgmt->u.action.u.delba.params);
	tid = (params & IEEE80211_DELBA_PARAM_TID_MASK) >> 12;
	initiator = (params & IEEE80211_DELBA_PARAM_INITIATOR_MASK) >> 11;

J
Johannes Berg 已提交
392 393 394 395
	ht_dbg_ratelimited(sdata, "delba from %pM (%s) tid %d reason code %d\n",
			   mgmt->sa, initiator ? "initiator" : "recipient",
			   tid,
			   le16_to_cpu(mgmt->u.action.u.delba.reason_code));
396 397

	if (initiator == WLAN_BACK_INITIATOR)
398 399
		__ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_INITIATOR, 0,
					       true);
400
	else
401
		__ieee80211_stop_tx_ba_session(sta, tid, AGG_STOP_PEER_REQUEST);
402
}
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449

int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
			       enum ieee80211_smps_mode smps, const u8 *da,
			       const u8 *bssid)
{
	struct ieee80211_local *local = sdata->local;
	struct sk_buff *skb;
	struct ieee80211_mgmt *action_frame;

	/* 27 = header + category + action + smps mode */
	skb = dev_alloc_skb(27 + local->hw.extra_tx_headroom);
	if (!skb)
		return -ENOMEM;

	skb_reserve(skb, local->hw.extra_tx_headroom);
	action_frame = (void *)skb_put(skb, 27);
	memcpy(action_frame->da, da, ETH_ALEN);
	memcpy(action_frame->sa, sdata->dev->dev_addr, ETH_ALEN);
	memcpy(action_frame->bssid, bssid, ETH_ALEN);
	action_frame->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
						  IEEE80211_STYPE_ACTION);
	action_frame->u.action.category = WLAN_CATEGORY_HT;
	action_frame->u.action.u.ht_smps.action = WLAN_HT_ACTION_SMPS;
	switch (smps) {
	case IEEE80211_SMPS_AUTOMATIC:
	case IEEE80211_SMPS_NUM_MODES:
		WARN_ON(1);
	case IEEE80211_SMPS_OFF:
		action_frame->u.action.u.ht_smps.smps_control =
				WLAN_HT_SMPS_CONTROL_DISABLED;
		break;
	case IEEE80211_SMPS_STATIC:
		action_frame->u.action.u.ht_smps.smps_control =
				WLAN_HT_SMPS_CONTROL_STATIC;
		break;
	case IEEE80211_SMPS_DYNAMIC:
		action_frame->u.action.u.ht_smps.smps_control =
				WLAN_HT_SMPS_CONTROL_DYNAMIC;
		break;
	}

	/* we'll do more on status of this frame */
	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
	ieee80211_tx_skb(sdata, skb);

	return 0;
}
450

451
void ieee80211_request_smps_mgd_work(struct work_struct *work)
452 453 454 455 456
{
	struct ieee80211_sub_if_data *sdata =
		container_of(work, struct ieee80211_sub_if_data,
			     u.mgd.request_smps_work);

457
	sdata_lock(sdata);
458 459 460 461 462 463 464 465 466 467 468
	__ieee80211_request_smps_mgd(sdata, sdata->u.mgd.driver_smps_mode);
	sdata_unlock(sdata);
}

void ieee80211_request_smps_ap_work(struct work_struct *work)
{
	struct ieee80211_sub_if_data *sdata =
		container_of(work, struct ieee80211_sub_if_data,
			     u.ap.request_smps_work);

	sdata_lock(sdata);
469 470 471
	if (sdata_dereference(sdata->u.ap.beacon, sdata))
		__ieee80211_request_smps_ap(sdata,
					    sdata->u.ap.driver_smps_mode);
472
	sdata_unlock(sdata);
473 474 475 476 477 478 479
}

void ieee80211_request_smps(struct ieee80211_vif *vif,
			    enum ieee80211_smps_mode smps_mode)
{
	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);

480 481
	if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION &&
			 vif->type != NL80211_IFTYPE_AP))
482 483
		return;

484
	if (vif->type == NL80211_IFTYPE_STATION) {
485 486
		if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF))
			smps_mode = IEEE80211_SMPS_AUTOMATIC;
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
		if (sdata->u.mgd.driver_smps_mode == smps_mode)
			return;
		sdata->u.mgd.driver_smps_mode = smps_mode;
		ieee80211_queue_work(&sdata->local->hw,
				     &sdata->u.mgd.request_smps_work);
	} else {
		/* AUTOMATIC is meaningless in AP mode */
		if (WARN_ON_ONCE(smps_mode == IEEE80211_SMPS_AUTOMATIC))
			return;
		if (sdata->u.ap.driver_smps_mode == smps_mode)
			return;
		sdata->u.ap.driver_smps_mode = smps_mode;
		ieee80211_queue_work(&sdata->local->hw,
				     &sdata->u.ap.request_smps_work);
	}
502 503 504
}
/* this might change ... don't want non-open drivers using it */
EXPORT_SYMBOL_GPL(ieee80211_request_smps);