diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm/sta.c')
| -rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/sta.c | 290 | 
1 files changed, 194 insertions, 96 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index 44add291531..1fb01ea2e70 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -5,7 +5,7 @@   *   * GPL LICENSE SUMMARY   * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.   *   * This program is free software; you can redistribute it and/or modify   * it under the terms of version 2 of the GNU General Public License as @@ -30,7 +30,7 @@   *   * BSD LICENSE   * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.   * All rights reserved.   *   * Redistribution and use in source and binary forms, with or without @@ -66,19 +66,30 @@  #include "sta.h"  #include "rs.h" -static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm) +static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm, +				    enum nl80211_iftype iftype)  {  	int sta_id; +	u32 reserved_ids = 0; +	BUILD_BUG_ON(IWL_MVM_STATION_COUNT > 32);  	WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status));  	lockdep_assert_held(&mvm->mutex); +	/* d0i3/d3 assumes the AP's sta_id (of sta vif) is 0. reserve it. */ +	if (iftype != NL80211_IFTYPE_STATION) +		reserved_ids = BIT(0); +  	/* Don't take rcu_read_lock() since we are protected by mvm->mutex */ -	for (sta_id = 0; sta_id < IWL_MVM_STATION_COUNT; sta_id++) +	for (sta_id = 0; sta_id < IWL_MVM_STATION_COUNT; sta_id++) { +		if (BIT(sta_id) & reserved_ids) +			continue; +  		if (!rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],  					       lockdep_is_held(&mvm->mutex)))  			return sta_id; +	}  	return IWL_MVM_STATION_COUNT;  } @@ -204,7 +215,8 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,  	lockdep_assert_held(&mvm->mutex);  	if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) -		sta_id = iwl_mvm_find_free_sta_id(mvm); +		sta_id = iwl_mvm_find_free_sta_id(mvm, +						  ieee80211_vif_type_p2p(vif));  	else  		sta_id = mvm_sta->sta_id; @@ -229,8 +241,12 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,  		if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE)  			mvm_sta->tfd_queue_msk |= BIT(vif->hw_queue[i]); -	/* for HW restart - need to reset the seq_number etc... */ -	memset(mvm_sta->tid_data, 0, sizeof(mvm_sta->tid_data)); +	/* for HW restart - reset everything but the sequence number */ +	for (i = 0; i < IWL_MAX_TID_COUNT; i++) { +		u16 seq = mvm_sta->tid_data[i].seq_number; +		memset(&mvm_sta->tid_data[i], 0, sizeof(mvm_sta->tid_data[i])); +		mvm_sta->tid_data[i].seq_number = seq; +	}  	ret = iwl_mvm_sta_send_to_fw(mvm, sta, false);  	if (ret) @@ -311,7 +327,7 @@ static int iwl_mvm_rm_sta_common(struct iwl_mvm *mvm, u8 sta_id)  		return -EINVAL;  	} -	ret = iwl_mvm_send_cmd_pdu(mvm, REMOVE_STA, CMD_SYNC, +	ret = iwl_mvm_send_cmd_pdu(mvm, REMOVE_STA, 0,  				   sizeof(rm_sta_cmd), &rm_sta_cmd);  	if (ret) {  		IWL_ERR(mvm, "Failed to remove station. Id=%d\n", sta_id); @@ -341,8 +357,15 @@ void iwl_mvm_sta_drained_wk(struct work_struct *wk)  			rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id],  						  lockdep_is_held(&mvm->mutex)); -		/* This station is in use */ -		if (!IS_ERR(sta)) +		/* +		 * This station is in use or RCU-removed; the latter happens in +		 * managed mode, where mac80211 removes the station before we +		 * can remove it from firmware (we can only do that after the +		 * MAC is marked unassociated), and possibly while the deauth +		 * frame to disconnect from the AP is still queued. Then, the +		 * station pointer is -ENOENT when the last skb is reclaimed. +		 */ +		if (!IS_ERR(sta) || PTR_ERR(sta) == -ENOENT)  			continue;  		if (PTR_ERR(sta) == -EINVAL) { @@ -368,7 +391,7 @@ void iwl_mvm_sta_drained_wk(struct work_struct *wk)  				sta_id);  			continue;  		} -		rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], NULL); +		RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id], NULL);  		clear_bit(sta_id, mvm->sta_drained);  	} @@ -390,20 +413,16 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,  		/* flush its queues here since we are freeing mvm_sta */  		ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, true); -		/* -		 * Put a non-NULL since the fw station isn't removed. -		 * It will be removed after the MAC will be set as -		 * unassoc. -		 */ -		rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id], -				   ERR_PTR(-EINVAL)); -  		/* if we are associated - we can't remove the AP STA now */  		if (vif->bss_conf.assoc)  			return ret;  		/* unassoc - go ahead - remove the AP STA now */  		mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; + +		/* clear d0i3_ap_sta_id if no longer relevant */ +		if (mvm->d0i3_ap_sta_id == mvm_sta->sta_id) +			mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;  	}  	/* @@ -423,7 +442,7 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm,  	} else {  		spin_unlock_bh(&mvm_sta->lock);  		ret = iwl_mvm_rm_sta_common(mvm, mvm_sta->sta_id); -		rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id], NULL); +		RCU_INIT_POINTER(mvm->fw_id_to_mac_id[mvm_sta->sta_id], NULL);  	}  	return ret; @@ -437,15 +456,15 @@ int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm,  	lockdep_assert_held(&mvm->mutex); -	rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], NULL); +	RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id], NULL);  	return ret;  }  int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta, -			     u32 qmask) +			     u32 qmask, enum nl80211_iftype iftype)  {  	if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { -		sta->sta_id = iwl_mvm_find_free_sta_id(mvm); +		sta->sta_id = iwl_mvm_find_free_sta_id(mvm, iftype);  		if (WARN_ON_ONCE(sta->sta_id == IWL_MVM_STATION_COUNT))  			return -ENOSPC;  	} @@ -459,7 +478,7 @@ int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta,  void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta)  { -	rcu_assign_pointer(mvm->fw_id_to_mac_id[sta->sta_id], NULL); +	RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta->sta_id], NULL);  	memset(sta, 0, sizeof(struct iwl_mvm_int_sta));  	sta->sta_id = IWL_MVM_STATION_COUNT;  } @@ -475,7 +494,7 @@ static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm,  	lockdep_assert_held(&mvm->mutex); -	memset(&cmd, 0, sizeof(struct iwl_mvm_add_sta_cmd)); +	memset(&cmd, 0, sizeof(cmd));  	cmd.sta_id = sta->sta_id;  	cmd.mac_id_n_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mac_id,  							     color)); @@ -510,7 +529,8 @@ int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm)  	lockdep_assert_held(&mvm->mutex);  	/* Add the aux station, but without any queues */ -	ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, 0); +	ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, 0, +				       NL80211_IFTYPE_UNSPECIFIED);  	if (ret)  		return ret; @@ -534,10 +554,14 @@ int iwl_mvm_send_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  			   struct iwl_mvm_int_sta *bsta)  {  	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); -	static const u8 baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; +	static const u8 _baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; +	const u8 *baddr = _baddr;  	lockdep_assert_held(&mvm->mutex); +	if (vif->type == NL80211_IFTYPE_ADHOC) +		baddr = vif->bss_conf.bssid; +  	if (WARN_ON_ONCE(bsta->sta_id == IWL_MVM_STATION_COUNT))  		return -ENOSPC; @@ -578,7 +602,8 @@ int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	lockdep_assert_held(&mvm->mutex);  	qmask = iwl_mvm_mac_get_queues_mask(mvm, vif); -	ret = iwl_mvm_allocate_int_sta(mvm, bsta, qmask); +	ret = iwl_mvm_allocate_int_sta(mvm, bsta, qmask, +				       ieee80211_vif_type_p2p(vif));  	if (ret)  		return ret; @@ -714,7 +739,7 @@ static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,  	return ret;  } -static const u8 tid_to_ac[] = { +const u8 tid_to_mac80211_ac[] = {  	IEEE80211_AC_BE,  	IEEE80211_AC_BK,  	IEEE80211_AC_BK, @@ -725,10 +750,21 @@ static const u8 tid_to_ac[] = {  	IEEE80211_AC_VO,  }; +static const u8 tid_to_ucode_ac[] = { +	AC_BE, +	AC_BK, +	AC_BK, +	AC_BE, +	AC_VI, +	AC_VI, +	AC_VO, +	AC_VO, +}; +  int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  			     struct ieee80211_sta *sta, u16 tid, u16 *ssn)  { -	struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; +	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);  	struct iwl_mvm_tid_data *tid_data;  	int txq_id; @@ -743,21 +779,29 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	lockdep_assert_held(&mvm->mutex); -	for (txq_id = IWL_MVM_FIRST_AGG_QUEUE; -	     txq_id <= IWL_MVM_LAST_AGG_QUEUE; txq_id++) +	for (txq_id = mvm->first_agg_queue; +	     txq_id <= mvm->last_agg_queue; txq_id++)  		if (mvm->queue_to_mac80211[txq_id] ==  		    IWL_INVALID_MAC80211_QUEUE)  			break; -	if (txq_id > IWL_MVM_LAST_AGG_QUEUE) { +	if (txq_id > mvm->last_agg_queue) {  		IWL_ERR(mvm, "Failed to allocate agg queue\n");  		return -EIO;  	} +	spin_lock_bh(&mvmsta->lock); + +	/* possible race condition - we entered D0i3 while starting agg */ +	if (test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status)) { +		spin_unlock_bh(&mvmsta->lock); +		IWL_ERR(mvm, "Entered D0i3 while starting Tx agg\n"); +		return -EIO; +	} +  	/* the new tx queue is still connected to the same mac80211 queue */ -	mvm->queue_to_mac80211[txq_id] = vif->hw_queue[tid_to_ac[tid]]; +	mvm->queue_to_mac80211[txq_id] = vif->hw_queue[tid_to_mac80211_ac[tid]]; -	spin_lock_bh(&mvmsta->lock);  	tid_data = &mvmsta->tid_data[tid];  	tid_data->ssn = IEEE80211_SEQ_TO_SN(tid_data->seq_number);  	tid_data->txq_id = txq_id; @@ -783,7 +827,7 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  			    struct ieee80211_sta *sta, u16 tid, u8 buf_size)  { -	struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; +	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);  	struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];  	int queue, fifo, ret;  	u16 ssn; @@ -797,7 +841,7 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	tid_data->ssn = 0xffff;  	spin_unlock_bh(&mvmsta->lock); -	fifo = iwl_mvm_ac_to_tx_fifo[tid_to_ac[tid]]; +	fifo = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]];  	ret = iwl_mvm_sta_tx_agg(mvm, sta, tid, queue, true);  	if (ret) @@ -820,26 +864,13 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  	IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n",  		     sta->addr, tid); -	if (mvm->cfg->ht_params->use_rts_for_aggregation) { -		/* -		 * switch to RTS/CTS if it is the prefer protection -		 * method for HT traffic -		 * this function also sends the LQ command -		 */ -		return iwl_mvm_tx_protection(mvm, mvmsta, true); -		/* -		 * TODO: remove the TLC_RTS flag when we tear down the last -		 * AGG session (agg_tids_count in DVM) -		 */ -	} - -	return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.lq, CMD_ASYNC, false); +	return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.lq, false);  }  int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  			    struct ieee80211_sta *sta, u16 tid)  { -	struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; +	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);  	struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];  	u16 txq_id;  	int err; @@ -911,7 +942,7 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif,  			    struct ieee80211_sta *sta, u16 tid)  { -	struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; +	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);  	struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];  	u16 txq_id;  	enum iwl_mvm_agg_state old_state; @@ -986,8 +1017,8 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,  				u8 sta_id, u32 tkip_iv32, u16 *tkip_p1k,  				u32 cmd_flags)  { +	struct iwl_mvm_add_sta_key_cmd cmd = {};  	__le16 key_flags; -	struct iwl_mvm_add_sta_cmd cmd = {};  	int ret, status;  	u16 keyidx;  	int i; @@ -1000,37 +1031,34 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,  	switch (keyconf->cipher) {  	case WLAN_CIPHER_SUITE_TKIP:  		key_flags |= cpu_to_le16(STA_KEY_FLG_TKIP); -		cmd.key.tkip_rx_tsc_byte2 = tkip_iv32; +		cmd.tkip_rx_tsc_byte2 = tkip_iv32;  		for (i = 0; i < 5; i++) -			cmd.key.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]); -		memcpy(cmd.key.key, keyconf->key, keyconf->keylen); +			cmd.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]); +		memcpy(cmd.key, keyconf->key, keyconf->keylen);  		break;  	case WLAN_CIPHER_SUITE_CCMP:  		key_flags |= cpu_to_le16(STA_KEY_FLG_CCM); -		memcpy(cmd.key.key, keyconf->key, keyconf->keylen); +		memcpy(cmd.key, keyconf->key, keyconf->keylen);  		break;  	default: -		WARN_ON(1); -		return -EINVAL; +		key_flags |= cpu_to_le16(STA_KEY_FLG_EXT); +		memcpy(cmd.key, keyconf->key, keyconf->keylen);  	}  	if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))  		key_flags |= cpu_to_le16(STA_KEY_MULTICAST); -	cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color); -	cmd.key.key_offset = keyconf->hw_key_idx; -	cmd.key.key_flags = key_flags; -	cmd.add_modify = STA_MODE_MODIFY; -	cmd.modify_mask = STA_MODIFY_KEY; +	cmd.key_offset = keyconf->hw_key_idx; +	cmd.key_flags = key_flags;  	cmd.sta_id = sta_id;  	status = ADD_STA_SUCCESS; -	if (cmd_flags == CMD_SYNC) -		ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd), -						  &cmd, &status); +	if (cmd_flags & CMD_ASYNC) +		ret =  iwl_mvm_send_cmd_pdu(mvm, ADD_STA_KEY, CMD_ASYNC, +					    sizeof(cmd), &cmd);  	else -		ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, -					   sizeof(cmd), &cmd); +		ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd), +						  &cmd, &status);  	switch (status) {  	case ADD_STA_SUCCESS: @@ -1083,7 +1111,7 @@ static int iwl_mvm_send_sta_igtk(struct iwl_mvm *mvm,  		       remove_key ? "removing" : "installing",  		       igtk_cmd.sta_id); -	return iwl_mvm_send_cmd_pdu(mvm, MGMT_MCAST_KEY, CMD_SYNC, +	return iwl_mvm_send_cmd_pdu(mvm, MGMT_MCAST_KEY, 0,  				    sizeof(igtk_cmd), &igtk_cmd);  } @@ -1170,15 +1198,15 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,  		ieee80211_get_key_rx_seq(keyconf, 0, &seq);  		ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k);  		ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, sta_id, -					   seq.tkip.iv32, p1k, CMD_SYNC); +					   seq.tkip.iv32, p1k, 0);  		break;  	case WLAN_CIPHER_SUITE_CCMP:  		ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, sta_id, -					   0, NULL, CMD_SYNC); +					   0, NULL, 0);  		break;  	default: -		IWL_ERR(mvm, "Unknown cipher %x\n", keyconf->cipher); -		ret = -EINVAL; +		ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, +					   sta_id, 0, NULL, 0);  	}  	if (ret) @@ -1197,7 +1225,7 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,  			   struct ieee80211_key_conf *keyconf)  {  	struct iwl_mvm_sta *mvm_sta; -	struct iwl_mvm_add_sta_cmd cmd = {}; +	struct iwl_mvm_add_sta_key_cmd cmd = {};  	__le16 key_flags;  	int ret, status;  	u8 sta_id; @@ -1252,16 +1280,12 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm,  	if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))  		key_flags |= cpu_to_le16(STA_KEY_MULTICAST); -	cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color); -	cmd.key.key_flags = key_flags; -	cmd.key.key_offset = keyconf->hw_key_idx; +	cmd.key_flags = key_flags; +	cmd.key_offset = keyconf->hw_key_idx;  	cmd.sta_id = sta_id; -	cmd.modify_mask = STA_MODIFY_KEY; -	cmd.add_modify = STA_MODE_MODIFY; -  	status = ADD_STA_SUCCESS; -	ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd), +	ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd),  					  &cmd, &status);  	switch (status) { @@ -1308,7 +1332,7 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm,  void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,  				struct ieee80211_sta *sta)  { -	struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; +	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);  	struct iwl_mvm_add_sta_cmd cmd = {  		.add_modify = STA_MODE_MODIFY,  		.sta_id = mvmsta->sta_id, @@ -1325,28 +1349,102 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm,  void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,  				       struct ieee80211_sta *sta,  				       enum ieee80211_frame_release_type reason, -				       u16 cnt) +				       u16 cnt, u16 tids, bool more_data, +				       bool agg)  { -	u16 sleep_state_flags = -		(reason == IEEE80211_FRAME_RELEASE_UAPSD) ? -			STA_SLEEP_STATE_UAPSD : STA_SLEEP_STATE_PS_POLL; -	struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; +	struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);  	struct iwl_mvm_add_sta_cmd cmd = {  		.add_modify = STA_MODE_MODIFY,  		.sta_id = mvmsta->sta_id,  		.modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT,  		.sleep_tx_count = cpu_to_le16(cnt),  		.mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color), -		/* -		 * Same modify mask for sleep_tx_count and sleep_state_flags so -		 * we must set the sleep_state_flags too. -		 */ -		.sleep_state_flags = cpu_to_le16(sleep_state_flags),  	}; -	int ret; +	int tid, ret; +	unsigned long _tids = tids; + +	/* convert TIDs to ACs - we don't support TSPEC so that's OK +	 * Note that this field is reserved and unused by firmware not +	 * supporting GO uAPSD, so it's safe to always do this. +	 */ +	for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT) +		cmd.awake_acs |= BIT(tid_to_ucode_ac[tid]); + +	/* If we're releasing frames from aggregation queues then check if the +	 * all queues combined that we're releasing frames from have +	 *  - more frames than the service period, in which case more_data +	 *    needs to be set +	 *  - fewer than 'cnt' frames, in which case we need to adjust the +	 *    firmware command (but do that unconditionally) +	 */ +	if (agg) { +		int remaining = cnt; + +		spin_lock_bh(&mvmsta->lock); +		for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT) { +			struct iwl_mvm_tid_data *tid_data; +			u16 n_queued; + +			tid_data = &mvmsta->tid_data[tid]; +			if (WARN(tid_data->state != IWL_AGG_ON && +				 tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA, +				 "TID %d state is %d\n", +				 tid, tid_data->state)) { +				spin_unlock_bh(&mvmsta->lock); +				ieee80211_sta_eosp(sta); +				return; +			} + +			n_queued = iwl_mvm_tid_queued(tid_data); +			if (n_queued > remaining) { +				more_data = true; +				remaining = 0; +				break; +			} +			remaining -= n_queued; +		} +		spin_unlock_bh(&mvmsta->lock); + +		cmd.sleep_tx_count = cpu_to_le16(cnt - remaining); +		if (WARN_ON(cnt - remaining == 0)) { +			ieee80211_sta_eosp(sta); +			return; +		} +	} + +	/* Note: this is ignored by firmware not supporting GO uAPSD */ +	if (more_data) +		cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_MOREDATA); + +	if (reason == IEEE80211_FRAME_RELEASE_PSPOLL) { +		mvmsta->next_status_eosp = true; +		cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_PS_POLL); +	} else { +		cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_UAPSD); +	} -	/* TODO: somehow the fw doesn't seem to take PS_POLL into account */  	ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd);  	if (ret)  		IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret);  } + +int iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm, +			  struct iwl_rx_cmd_buffer *rxb, +			  struct iwl_device_cmd *cmd) +{ +	struct iwl_rx_packet *pkt = rxb_addr(rxb); +	struct iwl_mvm_eosp_notification *notif = (void *)pkt->data; +	struct ieee80211_sta *sta; +	u32 sta_id = le32_to_cpu(notif->sta_id); + +	if (WARN_ON_ONCE(sta_id >= IWL_MVM_STATION_COUNT)) +		return 0; + +	rcu_read_lock(); +	sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); +	if (!IS_ERR_OR_NULL(sta)) +		ieee80211_sta_eosp(sta); +	rcu_read_unlock(); + +	return 0; +}  | 
