Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/******************************************************************************
  2 *
  3 * This file is provided under a dual BSD/GPLv2 license.  When using or
  4 * redistributing this file, you may do so under either license.
  5 *
  6 * GPL LICENSE SUMMARY
  7 *
  8 * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
  9 *
 10 * This program is free software; you can redistribute it and/or modify
 11 * it under the terms of version 2 of the GNU General Public License as
 12 * published by the Free Software Foundation.
 13 *
 14 * This program is distributed in the hope that it will be useful, but
 15 * WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * General Public License for more details.
 18 *
 19 * You should have received a copy of the GNU General Public License
 20 * along with this program; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
 22 * USA
 23 *
 24 * The full GNU General Public License is included in this distribution
 25 * in the file called COPYING.
 26 *
 27 * Contact Information:
 28 *  Intel Linux Wireless <ilw@linux.intel.com>
 29 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
 30 *
 31 * BSD LICENSE
 32 *
 33 * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
 34 * All rights reserved.
 35 *
 36 * Redistribution and use in source and binary forms, with or without
 37 * modification, are permitted provided that the following conditions
 38 * are met:
 39 *
 40 *  * Redistributions of source code must retain the above copyright
 41 *    notice, this list of conditions and the following disclaimer.
 42 *  * Redistributions in binary form must reproduce the above copyright
 43 *    notice, this list of conditions and the following disclaimer in
 44 *    the documentation and/or other materials provided with the
 45 *    distribution.
 46 *  * Neither the name Intel Corporation nor the names of its
 47 *    contributors may be used to endorse or promote products derived
 48 *    from this software without specific prior written permission.
 49 *
 50 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 51 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 52 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 53 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 54 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 55 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 56 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 57 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 58 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 59 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 60 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 61 *
 62 *****************************************************************************/
 63#include "mvm.h"
 64
 65/* For counting bound interfaces */
 66struct iwl_mvm_active_iface_iterator_data {
 67	struct ieee80211_vif *ignore_vif;
 68	u8 sta_vif_ap_sta_id;
 69	enum iwl_sf_state sta_vif_state;
 70	int num_active_macs;
 71};
 72
 73/*
 74 * Count bound interfaces which are not p2p, besides data->ignore_vif.
 75 * data->station_vif will point to one bound vif of type station, if exists.
 76 */
 77static void iwl_mvm_bound_iface_iterator(void *_data, u8 *mac,
 78					 struct ieee80211_vif *vif)
 79{
 80	struct iwl_mvm_active_iface_iterator_data *data = _data;
 81	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 82
 83	if (vif == data->ignore_vif || !mvmvif->phy_ctxt ||
 84	    vif->type == NL80211_IFTYPE_P2P_DEVICE)
 85		return;
 86
 87	data->num_active_macs++;
 88
 89	if (vif->type == NL80211_IFTYPE_STATION) {
 90		data->sta_vif_ap_sta_id = mvmvif->ap_sta_id;
 91		if (vif->bss_conf.assoc)
 92			data->sta_vif_state = SF_FULL_ON;
 93		else
 94			data->sta_vif_state = SF_INIT_OFF;
 95	}
 96}
 97
 98/*
 99 * Aging and idle timeouts for the different possible scenarios
100 * in SF_FULL_ON state.
101 */
102static const __le32 sf_full_timeout[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = {
103	{
104		cpu_to_le32(SF_SINGLE_UNICAST_AGING_TIMER),
105		cpu_to_le32(SF_SINGLE_UNICAST_IDLE_TIMER)
106	},
107	{
108		cpu_to_le32(SF_AGG_UNICAST_AGING_TIMER),
109		cpu_to_le32(SF_AGG_UNICAST_IDLE_TIMER)
110	},
111	{
112		cpu_to_le32(SF_MCAST_AGING_TIMER),
113		cpu_to_le32(SF_MCAST_IDLE_TIMER)
114	},
115	{
116		cpu_to_le32(SF_BA_AGING_TIMER),
117		cpu_to_le32(SF_BA_IDLE_TIMER)
118	},
119	{
120		cpu_to_le32(SF_TX_RE_AGING_TIMER),
121		cpu_to_le32(SF_TX_RE_IDLE_TIMER)
122	},
123};
124
125static void iwl_mvm_fill_sf_command(struct iwl_sf_cfg_cmd *sf_cmd,
126				    struct ieee80211_sta *sta)
127{
128	int i, j, watermark;
129
130	sf_cmd->watermark[SF_LONG_DELAY_ON] = cpu_to_le32(SF_W_MARK_SCAN);
131
132	/*
133	 * If we are in association flow - check antenna configuration
134	 * capabilities of the AP station, and choose the watermark accordingly.
135	 */
136	if (sta) {
137		if (sta->ht_cap.ht_supported || sta->vht_cap.vht_supported) {
138			switch (sta->rx_nss) {
139			case 1:
140				watermark = SF_W_MARK_SISO;
141				break;
142			case 2:
143				watermark = SF_W_MARK_MIMO2;
144				break;
145			default:
146				watermark = SF_W_MARK_MIMO3;
147				break;
148			}
149		} else {
150			watermark = SF_W_MARK_LEGACY;
151		}
152	/* default watermark value for unassociated mode. */
153	} else {
154		watermark = SF_W_MARK_MIMO2;
155	}
156	sf_cmd->watermark[SF_FULL_ON] = cpu_to_le32(watermark);
157
158	for (i = 0; i < SF_NUM_SCENARIO; i++) {
159		for (j = 0; j < SF_NUM_TIMEOUT_TYPES; j++) {
160			sf_cmd->long_delay_timeouts[i][j] =
161					cpu_to_le32(SF_LONG_DELAY_AGING_TIMER);
162		}
163	}
164	BUILD_BUG_ON(sizeof(sf_full_timeout) !=
165		     sizeof(__le32) * SF_NUM_SCENARIO * SF_NUM_TIMEOUT_TYPES);
166
167	memcpy(sf_cmd->full_on_timeouts, sf_full_timeout,
168	       sizeof(sf_full_timeout));
169}
170
171static int iwl_mvm_sf_config(struct iwl_mvm *mvm, u8 sta_id,
172			     enum iwl_sf_state new_state)
173{
174	struct iwl_sf_cfg_cmd sf_cmd = {
175		.state = new_state,
176	};
177	struct ieee80211_sta *sta;
178	int ret = 0;
179
180	/*
181	 * If an associated AP sta changed its antenna configuration, the state
182	 * will remain FULL_ON but SF parameters need to be reconsidered.
183	 */
184	if (new_state != SF_FULL_ON && mvm->sf_state == new_state)
185		return 0;
186
187	switch (new_state) {
188	case SF_UNINIT:
189		break;
190	case SF_FULL_ON:
191		if (sta_id == IWL_MVM_STATION_COUNT) {
192			IWL_ERR(mvm,
193				"No station: Cannot switch SF to FULL_ON\n");
194			return -EINVAL;
195		}
196		rcu_read_lock();
197		sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
198		if (IS_ERR_OR_NULL(sta)) {
199			IWL_ERR(mvm, "Invalid station id\n");
200			rcu_read_unlock();
201			return -EINVAL;
202		}
203		iwl_mvm_fill_sf_command(&sf_cmd, sta);
204		rcu_read_unlock();
205		break;
206	case SF_INIT_OFF:
207		iwl_mvm_fill_sf_command(&sf_cmd, NULL);
208		break;
209	default:
210		WARN_ONCE(1, "Invalid state: %d. not sending Smart Fifo cmd\n",
211			  new_state);
212		return -EINVAL;
213	}
214
215	ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_SF_CFG_CMD, CMD_ASYNC,
216				   sizeof(sf_cmd), &sf_cmd);
217	if (!ret)
218		mvm->sf_state = new_state;
219
220	return ret;
221}
222
223/*
224 * Update Smart fifo:
225 * Count bound interfaces that are not to be removed, ignoring p2p devices,
226 * and set new state accordingly.
227 */
228int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *changed_vif,
229		      bool remove_vif)
230{
231	enum iwl_sf_state new_state;
232	u8 sta_id = IWL_MVM_STATION_COUNT;
233	struct iwl_mvm_vif *mvmvif = NULL;
234	struct iwl_mvm_active_iface_iterator_data data = {
235		.ignore_vif = changed_vif,
236		.sta_vif_state = SF_UNINIT,
237		.sta_vif_ap_sta_id = IWL_MVM_STATION_COUNT,
238	};
239
240	if (IWL_UCODE_API(mvm->fw->ucode_ver) < 8)
241		return 0;
242
243	/*
244	 * Ignore the call if we are in HW Restart flow, or if the handled
245	 * vif is a p2p device.
246	 */
247	if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) ||
248	    (changed_vif && changed_vif->type == NL80211_IFTYPE_P2P_DEVICE))
249		return 0;
250
251	ieee80211_iterate_active_interfaces_atomic(mvm->hw,
252						   IEEE80211_IFACE_ITER_NORMAL,
253						   iwl_mvm_bound_iface_iterator,
254						   &data);
255
256	/* If changed_vif exists and is not to be removed, add to the count */
257	if (changed_vif && !remove_vif)
258		data.num_active_macs++;
259
260	switch (data.num_active_macs) {
261	case 0:
262		/* If there are no active macs - change state to SF_INIT_OFF */
263		new_state = SF_INIT_OFF;
264		break;
265	case 1:
266		if (remove_vif) {
267			/* The one active mac left is of type station
268			 * and we filled the relevant data during iteration
269			 */
270			new_state = data.sta_vif_state;
271			sta_id = data.sta_vif_ap_sta_id;
272		} else {
273			if (WARN_ON(!changed_vif))
274				return -EINVAL;
275			if (changed_vif->type != NL80211_IFTYPE_STATION) {
276				new_state = SF_UNINIT;
277			} else if (changed_vif->bss_conf.assoc &&
278				   changed_vif->bss_conf.dtim_period) {
279				mvmvif = iwl_mvm_vif_from_mac80211(changed_vif);
280				sta_id = mvmvif->ap_sta_id;
281				new_state = SF_FULL_ON;
282			} else {
283				new_state = SF_INIT_OFF;
284			}
285		}
286		break;
287	default:
288		/* If there are multiple active macs - change to SF_UNINIT */
289		new_state = SF_UNINIT;
290	}
291	return iwl_mvm_sf_config(mvm, sta_id, new_state);
292}