xref: /freebsd/sys/dev/iwm/if_iwm_sf.c (revision d0b2dbfa)
1 /*
2  * Copyright (c) 2014 genua mbh <info@genua.de>
3  * Copyright (c) 2014 Fixup Software Ltd.
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 /*-
19  * Based on BSD-licensed source modules in the Linux iwlwifi driver,
20  * which were used as the reference documentation for this implementation.
21  *
22  * Driver version we are currently based off of is
23  * Linux 4.7.3 (tag id d7f6728f57e3ecbb7ef34eb7d9f564d514775d75)
24  *
25  ******************************************************************************
26  *
27  * This file is provided under a dual BSD/GPLv2 license.  When using or
28  * redistributing this file, you may do so under either license.
29  *
30  * GPL LICENSE SUMMARY
31  *
32  * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
33  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
34  *
35  * This program is free software; you can redistribute it and/or modify
36  * it under the terms of version 2 of the GNU General Public License as
37  * published by the Free Software Foundation.
38  *
39  * This program is distributed in the hope that it will be useful, but
40  * WITHOUT ANY WARRANTY; without even the implied warranty of
41  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
42  * General Public License for more details.
43  *
44  * You should have received a copy of the GNU General Public License
45  * along with this program; if not, write to the Free Software
46  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
47  * USA
48  *
49  * The full GNU General Public License is included in this distribution
50  * in the file called COPYING.
51  *
52  * Contact Information:
53  *  Intel Linux Wireless <linuxwifi@intel.com>
54  * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
55  *
56  * BSD LICENSE
57  *
58  * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
59  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
60  * All rights reserved.
61  *
62  * Redistribution and use in source and binary forms, with or without
63  * modification, are permitted provided that the following conditions
64  * are met:
65  *
66  *  * Redistributions of source code must retain the above copyright
67  *    notice, this list of conditions and the following disclaimer.
68  *  * Redistributions in binary form must reproduce the above copyright
69  *    notice, this list of conditions and the following disclaimer in
70  *    the documentation and/or other materials provided with the
71  *    distribution.
72  *  * Neither the name Intel Corporation nor the names of its
73  *    contributors may be used to endorse or promote products derived
74  *    from this software without specific prior written permission.
75  *
76  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
77  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
78  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
79  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
80  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
81  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
82  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
83  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
84  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
85  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
86  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
87  *
88  *****************************************************************************/
89 
90 #include <sys/cdefs.h>
91 #include "opt_wlan.h"
92 #include "opt_iwm.h"
93 
94 #include <sys/param.h>
95 #include <sys/bus.h>
96 #include <sys/conf.h>
97 #include <sys/endian.h>
98 #include <sys/firmware.h>
99 #include <sys/kernel.h>
100 #include <sys/malloc.h>
101 #include <sys/mbuf.h>
102 #include <sys/mutex.h>
103 #include <sys/module.h>
104 #include <sys/proc.h>
105 #include <sys/rman.h>
106 #include <sys/socket.h>
107 #include <sys/sockio.h>
108 #include <sys/sysctl.h>
109 #include <sys/linker.h>
110 
111 #include <machine/bus.h>
112 #include <machine/endian.h>
113 #include <machine/resource.h>
114 
115 #include <net/if.h>
116 #include <net/if_var.h>
117 #include <net/if_arp.h>
118 #include <net/if_dl.h>
119 #include <net/if_media.h>
120 #include <net/if_types.h>
121 #include <net/bpf.h>
122 
123 #include <netinet/in.h>
124 #include <netinet/in_systm.h>
125 #include <netinet/if_ether.h>
126 #include <netinet/ip.h>
127 
128 #include <net80211/ieee80211_var.h>
129 #include <net80211/ieee80211_regdomain.h>
130 #include <net80211/ieee80211_ratectl.h>
131 #include <net80211/ieee80211_radiotap.h>
132 
133 #include <dev/iwm/if_iwmreg.h>
134 #include <dev/iwm/if_iwmvar.h>
135 #include <dev/iwm/if_iwm_config.h>
136 #include <dev/iwm/if_iwm_debug.h>
137 #include <dev/iwm/if_iwm_util.h>
138 #include <dev/iwm/if_iwm_sf.h>
139 
140 /*
141  * Aging and idle timeouts for the different possible scenarios
142  * in default configuration
143  */
144 static const uint32_t
145 sf_full_timeout_def[IWM_SF_NUM_SCENARIO][IWM_SF_NUM_TIMEOUT_TYPES] = {
146 	{
147 		htole32(IWM_SF_SINGLE_UNICAST_AGING_TIMER_DEF),
148 		htole32(IWM_SF_SINGLE_UNICAST_IDLE_TIMER_DEF)
149 	},
150 	{
151 		htole32(IWM_SF_AGG_UNICAST_AGING_TIMER_DEF),
152 		htole32(IWM_SF_AGG_UNICAST_IDLE_TIMER_DEF)
153 	},
154 	{
155 		htole32(IWM_SF_MCAST_AGING_TIMER_DEF),
156 		htole32(IWM_SF_MCAST_IDLE_TIMER_DEF)
157 	},
158 	{
159 		htole32(IWM_SF_BA_AGING_TIMER_DEF),
160 		htole32(IWM_SF_BA_IDLE_TIMER_DEF)
161 	},
162 	{
163 		htole32(IWM_SF_TX_RE_AGING_TIMER_DEF),
164 		htole32(IWM_SF_TX_RE_IDLE_TIMER_DEF)
165 	},
166 };
167 
168 /*
169  * Aging and idle timeouts for the different possible scenarios
170  * in single BSS MAC configuration.
171  */
172 static const uint32_t
173 sf_full_timeout[IWM_SF_NUM_SCENARIO][IWM_SF_NUM_TIMEOUT_TYPES] = {
174 	{
175 		htole32(IWM_SF_SINGLE_UNICAST_AGING_TIMER),
176 		htole32(IWM_SF_SINGLE_UNICAST_IDLE_TIMER)
177 	},
178 	{
179 		htole32(IWM_SF_AGG_UNICAST_AGING_TIMER),
180 		htole32(IWM_SF_AGG_UNICAST_IDLE_TIMER)
181 	},
182 	{
183 		htole32(IWM_SF_MCAST_AGING_TIMER),
184 		htole32(IWM_SF_MCAST_IDLE_TIMER)
185 	},
186 	{
187 		htole32(IWM_SF_BA_AGING_TIMER),
188 		htole32(IWM_SF_BA_IDLE_TIMER)
189 	},
190 	{
191 		htole32(IWM_SF_TX_RE_AGING_TIMER),
192 		htole32(IWM_SF_TX_RE_IDLE_TIMER)
193 	},
194 };
195 
196 static void
197 iwm_fill_sf_command(struct iwm_softc *sc, struct iwm_sf_cfg_cmd *sf_cmd,
198 	struct ieee80211_node *ni)
199 {
200 	int i, j, watermark;
201 
202 	sf_cmd->watermark[IWM_SF_LONG_DELAY_ON] = htole32(IWM_SF_W_MARK_SCAN);
203 
204 	/*
205 	 * If we are in association flow - check antenna configuration
206 	 * capabilities of the AP station, and choose the watermark accordingly.
207 	 */
208 	if (ni) {
209 		if (ni->ni_flags & IEEE80211_NODE_HT) {
210 			watermark = IWM_SF_W_MARK_SISO;
211 		} else {
212 			watermark = IWM_SF_W_MARK_LEGACY;
213 		}
214 	/* default watermark value for unassociated mode. */
215 	} else {
216 		watermark = IWM_SF_W_MARK_MIMO2;
217 	}
218 	sf_cmd->watermark[IWM_SF_FULL_ON] = htole32(watermark);
219 
220 	for (i = 0; i < IWM_SF_NUM_SCENARIO; i++) {
221 		for (j = 0; j < IWM_SF_NUM_TIMEOUT_TYPES; j++) {
222 			sf_cmd->long_delay_timeouts[i][j] =
223 					htole32(IWM_SF_LONG_DELAY_AGING_TIMER);
224 		}
225 	}
226 
227 	if (ni) {
228 		_Static_assert(sizeof(sf_full_timeout) == sizeof(uint32_t) *
229 		    IWM_SF_NUM_SCENARIO * IWM_SF_NUM_TIMEOUT_TYPES,
230 		    "sf_full_timeout has wrong size");
231 
232 		memcpy(sf_cmd->full_on_timeouts, sf_full_timeout,
233 		       sizeof(sf_full_timeout));
234 	} else {
235 		_Static_assert(sizeof(sf_full_timeout_def) == sizeof(uint32_t) *
236 		    IWM_SF_NUM_SCENARIO * IWM_SF_NUM_TIMEOUT_TYPES,
237 		    "sf_full_timeout_def has wrong size");
238 
239 		memcpy(sf_cmd->full_on_timeouts, sf_full_timeout_def,
240 		       sizeof(sf_full_timeout_def));
241 	}
242 }
243 
244 static int
245 iwm_sf_config(struct iwm_softc *sc, struct ieee80211_node *ni,
246 	enum iwm_sf_state new_state)
247 {
248 	struct iwm_sf_cfg_cmd sf_cmd = {
249 		.state = htole32(new_state),
250 	};
251 	int ret = 0;
252 
253 #ifdef notyet	/* only relevant for sdio variants */
254 	if (sc->cfg->disable_dummy_notification)
255 		sf_cmd.state |= htole32(IWM_SF_CFG_DUMMY_NOTIF_OFF);
256 #endif
257 
258 	/*
259 	 * If an associated AP sta changed its antenna configuration, the state
260 	 * will remain FULL_ON but SF parameters need to be reconsidered.
261 	 */
262 	if (new_state != IWM_SF_FULL_ON && sc->sf_state == new_state)
263 		return 0;
264 
265 	switch (new_state) {
266 	case IWM_SF_UNINIT:
267 		iwm_fill_sf_command(sc, &sf_cmd, NULL);
268 		break;
269 	case IWM_SF_FULL_ON:
270 		iwm_fill_sf_command(sc, &sf_cmd, ni);
271 		break;
272 	case IWM_SF_INIT_OFF:
273 		iwm_fill_sf_command(sc, &sf_cmd, NULL);
274 		break;
275 	default:
276 		device_printf(sc->sc_dev,
277 		    "Invalid state: %d. not sending Smart Fifo cmd\n",
278 		    new_state);
279 		return EINVAL;
280 	}
281 
282 	ret = iwm_send_cmd_pdu(sc, IWM_REPLY_SF_CFG_CMD, IWM_CMD_ASYNC,
283 				   sizeof(sf_cmd), &sf_cmd);
284 	if (!ret)
285 		sc->sf_state = new_state;
286 
287 	return ret;
288 }
289 
290 /*
291  * Update Smart fifo:
292  * Count bound interfaces that are not to be removed, ignoring p2p devices,
293  * and set new state accordingly.
294  */
295 int
296 iwm_sf_update(struct iwm_softc *sc, struct ieee80211vap *changed_vif,
297 	boolean_t remove_vif)
298 {
299 	enum iwm_sf_state new_state;
300 	struct ieee80211_node *ni = NULL;
301 	int num_active_macs = 0;
302 
303 	/* If changed_vif exists and is not to be removed, add to the count */
304 	if (changed_vif && !remove_vif)
305 		num_active_macs++;
306 
307 	switch (num_active_macs) {
308 	case 0:
309 		/* If there are no active macs - change state to SF_INIT_OFF */
310 		new_state = IWM_SF_INIT_OFF;
311 		break;
312 	case 1:
313 		if (!changed_vif)
314 			return EINVAL;
315 		ni = changed_vif->iv_bss;
316 		if (ni != NULL && IWM_NODE(ni)->in_assoc &&
317 		    changed_vif->iv_dtim_period) {
318 			new_state = IWM_SF_FULL_ON;
319 		} else {
320 			new_state = IWM_SF_INIT_OFF;
321 		}
322 		break;
323 	default:
324 		/* If there are multiple active macs - change to SF_UNINIT */
325 		new_state = IWM_SF_UNINIT;
326 	}
327 	return iwm_sf_config(sc, ni, new_state);
328 }
329