1e89c6173SAugustin Cavalier/*-
2e89c6173SAugustin Cavalier * Based on BSD-licensed source modules in the Linux iwlwifi driver,
3e89c6173SAugustin Cavalier * which were used as the reference documentation for this implementation.
4e89c6173SAugustin Cavalier *
5e89c6173SAugustin Cavalier * Driver version we are currently based off of is
6e89c6173SAugustin Cavalier * Linux 4.7.3 (tag id d7f6728f57e3ecbb7ef34eb7d9f564d514775d75)
7e89c6173SAugustin Cavalier *
8e89c6173SAugustin Cavalier ***********************************************************************
9e89c6173SAugustin Cavalier *
10e89c6173SAugustin Cavalier * This file is provided under a dual BSD/GPLv2 license.  When using or
11e89c6173SAugustin Cavalier * redistributing this file, you may do so under either license.
12e89c6173SAugustin Cavalier *
13e89c6173SAugustin Cavalier * GPL LICENSE SUMMARY
14e89c6173SAugustin Cavalier *
15e89c6173SAugustin Cavalier * Copyright(c) 2012 - 2015 Intel Corporation. All rights reserved.
16e89c6173SAugustin Cavalier * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
17e89c6173SAugustin Cavalier * Copyright(c) 2016 Intel Deutschland GmbH
18e89c6173SAugustin Cavalier *
19e89c6173SAugustin Cavalier * This program is free software; you can redistribute it and/or modify
20e89c6173SAugustin Cavalier * it under the terms of version 2 of the GNU General Public License as
21e89c6173SAugustin Cavalier * published by the Free Software Foundation.
22e89c6173SAugustin Cavalier *
23e89c6173SAugustin Cavalier * This program is distributed in the hope that it will be useful, but
24e89c6173SAugustin Cavalier * WITHOUT ANY WARRANTY; without even the implied warranty of
25e89c6173SAugustin Cavalier * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
26e89c6173SAugustin Cavalier * General Public License for more details.
27e89c6173SAugustin Cavalier *
28e89c6173SAugustin Cavalier * You should have received a copy of the GNU General Public License
29e89c6173SAugustin Cavalier * along with this program; if not, write to the Free Software
30e89c6173SAugustin Cavalier * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
31e89c6173SAugustin Cavalier * USA
32e89c6173SAugustin Cavalier *
33e89c6173SAugustin Cavalier * The full GNU General Public License is included in this distribution
34e89c6173SAugustin Cavalier * in the file called COPYING.
35e89c6173SAugustin Cavalier *
36e89c6173SAugustin Cavalier * Contact Information:
37e89c6173SAugustin Cavalier *  Intel Linux Wireless <linuxwifi@intel.com>
38e89c6173SAugustin Cavalier * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
39e89c6173SAugustin Cavalier *
40e89c6173SAugustin Cavalier * BSD LICENSE
41e89c6173SAugustin Cavalier *
42e89c6173SAugustin Cavalier * Copyright(c) 2012 - 2015 Intel Corporation. All rights reserved.
43e89c6173SAugustin Cavalier * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
44e89c6173SAugustin Cavalier * Copyright(c) 2016 Intel Deutschland GmbH
45e89c6173SAugustin Cavalier * All rights reserved.
46e89c6173SAugustin Cavalier *
47e89c6173SAugustin Cavalier * Redistribution and use in source and binary forms, with or without
48e89c6173SAugustin Cavalier * modification, are permitted provided that the following conditions
49e89c6173SAugustin Cavalier * are met:
50e89c6173SAugustin Cavalier *
51e89c6173SAugustin Cavalier *  * Redistributions of source code must retain the above copyright
52e89c6173SAugustin Cavalier *    notice, this list of conditions and the following disclaimer.
53e89c6173SAugustin Cavalier *  * Redistributions in binary form must reproduce the above copyright
54e89c6173SAugustin Cavalier *    notice, this list of conditions and the following disclaimer in
55e89c6173SAugustin Cavalier *    the documentation and/or other materials provided with the
56e89c6173SAugustin Cavalier *    distribution.
57e89c6173SAugustin Cavalier *  * Neither the name Intel Corporation nor the names of its
58e89c6173SAugustin Cavalier *    contributors may be used to endorse or promote products derived
59e89c6173SAugustin Cavalier *    from this software without specific prior written permission.
60e89c6173SAugustin Cavalier *
61e89c6173SAugustin Cavalier * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
62e89c6173SAugustin Cavalier * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
63e89c6173SAugustin Cavalier * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
64e89c6173SAugustin Cavalier * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
65e89c6173SAugustin Cavalier * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
66e89c6173SAugustin Cavalier * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
67e89c6173SAugustin Cavalier * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
68e89c6173SAugustin Cavalier * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
69e89c6173SAugustin Cavalier * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
70e89c6173SAugustin Cavalier * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
71e89c6173SAugustin Cavalier * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
72e89c6173SAugustin Cavalier *
73e89c6173SAugustin Cavalier *****************************************************************************/
74e89c6173SAugustin Cavalier
75e89c6173SAugustin Cavalier#include <sys/cdefs.h>
7686734cc4SAugustin Cavalier__FBSDID("$FreeBSD: releng/12.0/sys/dev/iwm/if_iwm_sta.c 321508 2017-07-26 05:29:08Z adrian $");
77e89c6173SAugustin Cavalier
78e89c6173SAugustin Cavalier#include "opt_wlan.h"
79e89c6173SAugustin Cavalier#include "opt_iwm.h"
80e89c6173SAugustin Cavalier
81e89c6173SAugustin Cavalier#include <sys/param.h>
82e89c6173SAugustin Cavalier#include <sys/bus.h>
83e89c6173SAugustin Cavalier#include <sys/conf.h>
84e89c6173SAugustin Cavalier#include <sys/endian.h>
85e89c6173SAugustin Cavalier#include <sys/firmware.h>
86e89c6173SAugustin Cavalier#include <sys/kernel.h>
87e89c6173SAugustin Cavalier#include <sys/malloc.h>
88e89c6173SAugustin Cavalier#include <sys/mbuf.h>
89e89c6173SAugustin Cavalier#include <sys/mutex.h>
90e89c6173SAugustin Cavalier#include <sys/module.h>
91e89c6173SAugustin Cavalier#include <sys/proc.h>
92e89c6173SAugustin Cavalier#include <sys/rman.h>
93e89c6173SAugustin Cavalier#include <sys/socket.h>
94e89c6173SAugustin Cavalier#include <sys/sockio.h>
95e89c6173SAugustin Cavalier#include <sys/sysctl.h>
96e89c6173SAugustin Cavalier#include <sys/linker.h>
97e89c6173SAugustin Cavalier
98e89c6173SAugustin Cavalier#include <machine/bus.h>
99e89c6173SAugustin Cavalier#include <machine/endian.h>
100e89c6173SAugustin Cavalier#include <machine/resource.h>
101e89c6173SAugustin Cavalier
102e89c6173SAugustin Cavalier#include <dev/pci/pcivar.h>
103e89c6173SAugustin Cavalier#include <dev/pci/pcireg.h>
104e89c6173SAugustin Cavalier
105e89c6173SAugustin Cavalier#include <net/bpf.h>
106e89c6173SAugustin Cavalier
107e89c6173SAugustin Cavalier#include <net/if.h>
108e89c6173SAugustin Cavalier#include <net/if_var.h>
109e89c6173SAugustin Cavalier#include <net/if_arp.h>
110e89c6173SAugustin Cavalier#include <net/if_dl.h>
111e89c6173SAugustin Cavalier#include <net/if_media.h>
112e89c6173SAugustin Cavalier#include <net/if_types.h>
113e89c6173SAugustin Cavalier
114e89c6173SAugustin Cavalier#include <netinet/in.h>
115e89c6173SAugustin Cavalier#include <netinet/in_systm.h>
116e89c6173SAugustin Cavalier#include <netinet/if_ether.h>
117e89c6173SAugustin Cavalier#include <netinet/ip.h>
118e89c6173SAugustin Cavalier
119e89c6173SAugustin Cavalier#include <net80211/ieee80211_var.h>
120e89c6173SAugustin Cavalier#include <net80211/ieee80211_regdomain.h>
121e89c6173SAugustin Cavalier#include <net80211/ieee80211_ratectl.h>
122e89c6173SAugustin Cavalier#include <net80211/ieee80211_radiotap.h>
123e89c6173SAugustin Cavalier
124e89c6173SAugustin Cavalier#include <dev/iwm/if_iwmreg.h>
125e89c6173SAugustin Cavalier#include <dev/iwm/if_iwmvar.h>
126e89c6173SAugustin Cavalier#include <dev/iwm/if_iwm_config.h>
127e89c6173SAugustin Cavalier#include <dev/iwm/if_iwm_debug.h>
128e89c6173SAugustin Cavalier#include <dev/iwm/if_iwm_constants.h>
129e89c6173SAugustin Cavalier#include <dev/iwm/if_iwm_util.h>
130e89c6173SAugustin Cavalier#include <dev/iwm/if_iwm_mac_ctxt.h>
131e89c6173SAugustin Cavalier#include <dev/iwm/if_iwm_sta.h>
132e89c6173SAugustin Cavalier
133e89c6173SAugustin Cavalier/*
134e89c6173SAugustin Cavalier * New version of ADD_STA_sta command added new fields at the end of the
135e89c6173SAugustin Cavalier * structure, so sending the size of the relevant API's structure is enough to
136e89c6173SAugustin Cavalier * support both API versions.
137e89c6173SAugustin Cavalier */
138e89c6173SAugustin Cavalierstatic inline int
139e89c6173SAugustin Cavalieriwm_mvm_add_sta_cmd_size(struct iwm_softc *sc)
140e89c6173SAugustin Cavalier{
141e89c6173SAugustin Cavalier#ifdef notyet
142e89c6173SAugustin Cavalier	return iwm_mvm_has_new_rx_api(mvm) ?
143e89c6173SAugustin Cavalier		sizeof(struct iwm_mvm_add_sta_cmd) :
144e89c6173SAugustin Cavalier		sizeof(struct iwm_mvm_add_sta_cmd_v7);
145e89c6173SAugustin Cavalier#else
146e89c6173SAugustin Cavalier	return sizeof(struct iwm_mvm_add_sta_cmd);
147e89c6173SAugustin Cavalier#endif
148e89c6173SAugustin Cavalier}
149e89c6173SAugustin Cavalier
150e89c6173SAugustin Cavalier/* send station add/update command to firmware */
151e89c6173SAugustin Cavalierint
152e89c6173SAugustin Cavalieriwm_mvm_sta_send_to_fw(struct iwm_softc *sc, struct iwm_node *in,
153e89c6173SAugustin Cavalier	boolean_t update)
154e89c6173SAugustin Cavalier{
155e89c6173SAugustin Cavalier	struct iwm_vap *ivp = IWM_VAP(in->in_ni.ni_vap);
156e89c6173SAugustin Cavalier	struct iwm_mvm_add_sta_cmd add_sta_cmd = {
157e89c6173SAugustin Cavalier		.sta_id = IWM_STATION_ID,
158e89c6173SAugustin Cavalier		.mac_id_n_color =
159e89c6173SAugustin Cavalier		    htole32(IWM_FW_CMD_ID_AND_COLOR(ivp->id, ivp->color)),
160e89c6173SAugustin Cavalier		.add_modify = update ? 1 : 0,
161e89c6173SAugustin Cavalier		.station_flags_msk = htole32(IWM_STA_FLG_FAT_EN_MSK |
162e89c6173SAugustin Cavalier					     IWM_STA_FLG_MIMO_EN_MSK),
163e89c6173SAugustin Cavalier		.tid_disable_tx = htole16(0xffff),
164e89c6173SAugustin Cavalier	};
165e89c6173SAugustin Cavalier	int ret;
166e89c6173SAugustin Cavalier	uint32_t status;
167e89c6173SAugustin Cavalier	uint32_t agg_size = 0, mpdu_dens = 0;
168e89c6173SAugustin Cavalier
169e89c6173SAugustin Cavalier	if (!update) {
170e89c6173SAugustin Cavalier		int ac;
171e89c6173SAugustin Cavalier		for (ac = 0; ac < WME_NUM_AC; ac++) {
172e89c6173SAugustin Cavalier			add_sta_cmd.tfd_queue_msk |=
173e89c6173SAugustin Cavalier			    htole32(1 << iwm_mvm_ac_to_tx_fifo[ac]);
174e89c6173SAugustin Cavalier		}
175e89c6173SAugustin Cavalier		IEEE80211_ADDR_COPY(&add_sta_cmd.addr, in->in_ni.ni_bssid);
176e89c6173SAugustin Cavalier	}
177e89c6173SAugustin Cavalier
178e89c6173SAugustin Cavalier	add_sta_cmd.station_flags |=
179e89c6173SAugustin Cavalier		htole32(agg_size << IWM_STA_FLG_MAX_AGG_SIZE_SHIFT);
180e89c6173SAugustin Cavalier	add_sta_cmd.station_flags |=
181e89c6173SAugustin Cavalier		htole32(mpdu_dens << IWM_STA_FLG_AGG_MPDU_DENS_SHIFT);
182e89c6173SAugustin Cavalier
183e89c6173SAugustin Cavalier	status = IWM_ADD_STA_SUCCESS;
184e89c6173SAugustin Cavalier	ret = iwm_mvm_send_cmd_pdu_status(sc, IWM_ADD_STA,
185e89c6173SAugustin Cavalier					  iwm_mvm_add_sta_cmd_size(sc),
186e89c6173SAugustin Cavalier					  &add_sta_cmd, &status);
187e89c6173SAugustin Cavalier	if (ret)
188e89c6173SAugustin Cavalier		return ret;
189e89c6173SAugustin Cavalier
190e89c6173SAugustin Cavalier	switch (status & IWM_ADD_STA_STATUS_MASK) {
191e89c6173SAugustin Cavalier	case IWM_ADD_STA_SUCCESS:
192e89c6173SAugustin Cavalier		IWM_DPRINTF(sc, IWM_DEBUG_NODE, "IWM_ADD_STA PASSED\n");
193e89c6173SAugustin Cavalier		break;
194e89c6173SAugustin Cavalier	default:
195e89c6173SAugustin Cavalier		ret = EIO;
196e89c6173SAugustin Cavalier		device_printf(sc->sc_dev, "IWM_ADD_STA failed\n");
197e89c6173SAugustin Cavalier		break;
198e89c6173SAugustin Cavalier	}
199e89c6173SAugustin Cavalier
200e89c6173SAugustin Cavalier	return ret;
201e89c6173SAugustin Cavalier}
202e89c6173SAugustin Cavalier
203e89c6173SAugustin Cavalierint
204e89c6173SAugustin Cavalieriwm_mvm_add_sta(struct iwm_softc *sc, struct iwm_node *in)
205e89c6173SAugustin Cavalier{
206e89c6173SAugustin Cavalier	return iwm_mvm_sta_send_to_fw(sc, in, FALSE);
207e89c6173SAugustin Cavalier}
208e89c6173SAugustin Cavalier
209e89c6173SAugustin Cavalierint
210e89c6173SAugustin Cavalieriwm_mvm_update_sta(struct iwm_softc *sc, struct iwm_node *in)
211e89c6173SAugustin Cavalier{
212e89c6173SAugustin Cavalier	return iwm_mvm_sta_send_to_fw(sc, in, TRUE);
213e89c6173SAugustin Cavalier}
214e89c6173SAugustin Cavalier
215e89c6173SAugustin Cavalierint
216e89c6173SAugustin Cavalieriwm_mvm_drain_sta(struct iwm_softc *sc, struct iwm_vap *ivp, boolean_t drain)
217e89c6173SAugustin Cavalier{
218e89c6173SAugustin Cavalier	struct iwm_mvm_add_sta_cmd cmd = {};
219e89c6173SAugustin Cavalier	int ret;
220e89c6173SAugustin Cavalier	uint32_t status;
221e89c6173SAugustin Cavalier
222e89c6173SAugustin Cavalier	cmd.mac_id_n_color =
223e89c6173SAugustin Cavalier	    htole32(IWM_FW_CMD_ID_AND_COLOR(ivp->id, ivp->color));
224e89c6173SAugustin Cavalier	cmd.sta_id = IWM_STATION_ID;
225e89c6173SAugustin Cavalier	cmd.add_modify = IWM_STA_MODE_MODIFY;
226e89c6173SAugustin Cavalier	cmd.station_flags = drain ? htole32(IWM_STA_FLG_DRAIN_FLOW) : 0;
227e89c6173SAugustin Cavalier	cmd.station_flags_msk = htole32(IWM_STA_FLG_DRAIN_FLOW);
228e89c6173SAugustin Cavalier
229e89c6173SAugustin Cavalier	status = IWM_ADD_STA_SUCCESS;
230e89c6173SAugustin Cavalier	ret = iwm_mvm_send_cmd_pdu_status(sc, IWM_ADD_STA,
231e89c6173SAugustin Cavalier					  iwm_mvm_add_sta_cmd_size(sc),
232e89c6173SAugustin Cavalier					  &cmd, &status);
233e89c6173SAugustin Cavalier	if (ret)
234e89c6173SAugustin Cavalier		return ret;
235e89c6173SAugustin Cavalier
236e89c6173SAugustin Cavalier	switch (status & IWM_ADD_STA_STATUS_MASK) {
237e89c6173SAugustin Cavalier	case IWM_ADD_STA_SUCCESS:
238e89c6173SAugustin Cavalier		IWM_DPRINTF(sc, IWM_DEBUG_NODE,
239e89c6173SAugustin Cavalier		    "Frames for staid %d will drained in fw\n", IWM_STATION_ID);
240e89c6173SAugustin Cavalier		break;
241e89c6173SAugustin Cavalier	default:
242e89c6173SAugustin Cavalier		ret = EIO;
243e89c6173SAugustin Cavalier		device_printf(sc->sc_dev,
244e89c6173SAugustin Cavalier		    "Couldn't drain frames for staid %d\n", IWM_STATION_ID);
245e89c6173SAugustin Cavalier		break;
246e89c6173SAugustin Cavalier	}
247e89c6173SAugustin Cavalier
248e89c6173SAugustin Cavalier	return ret;
249e89c6173SAugustin Cavalier}
250e89c6173SAugustin Cavalier
251e89c6173SAugustin Cavalier/*
252e89c6173SAugustin Cavalier * Remove a station from the FW table. Before sending the command to remove
253e89c6173SAugustin Cavalier * the station validate that the station is indeed known to the driver (sanity
254e89c6173SAugustin Cavalier * only).
255e89c6173SAugustin Cavalier */
256e89c6173SAugustin Cavalierstatic int
257e89c6173SAugustin Cavalieriwm_mvm_rm_sta_common(struct iwm_softc *sc)
258e89c6173SAugustin Cavalier{
259e89c6173SAugustin Cavalier	struct iwm_mvm_rm_sta_cmd rm_sta_cmd = {
260e89c6173SAugustin Cavalier		.sta_id = IWM_STATION_ID,
261e89c6173SAugustin Cavalier	};
262e89c6173SAugustin Cavalier	int ret;
263e89c6173SAugustin Cavalier
264e89c6173SAugustin Cavalier	ret = iwm_mvm_send_cmd_pdu(sc, IWM_REMOVE_STA, 0,
265e89c6173SAugustin Cavalier				   sizeof(rm_sta_cmd), &rm_sta_cmd);
266e89c6173SAugustin Cavalier	if (ret) {
267e89c6173SAugustin Cavalier		device_printf(sc->sc_dev,
268e89c6173SAugustin Cavalier		    "Failed to remove station. Id=%d\n", IWM_STATION_ID);
269e89c6173SAugustin Cavalier		return ret;
270e89c6173SAugustin Cavalier	}
271e89c6173SAugustin Cavalier
272e89c6173SAugustin Cavalier	return 0;
273e89c6173SAugustin Cavalier}
274e89c6173SAugustin Cavalier
275e89c6173SAugustin Cavalierint
276e89c6173SAugustin Cavalieriwm_mvm_rm_sta(struct iwm_softc *sc, struct ieee80211vap *vap,
277e89c6173SAugustin Cavalier	boolean_t is_assoc)
278e89c6173SAugustin Cavalier{
279e89c6173SAugustin Cavalier	uint32_t tfd_queue_msk = 0;
280e89c6173SAugustin Cavalier	int ret;
281e89c6173SAugustin Cavalier	int ac;
282e89c6173SAugustin Cavalier
283e89c6173SAugustin Cavalier	ret = iwm_mvm_drain_sta(sc, IWM_VAP(vap), TRUE);
284e89c6173SAugustin Cavalier	if (ret)
285e89c6173SAugustin Cavalier		return ret;
286e89c6173SAugustin Cavalier	for (ac = 0; ac < WME_NUM_AC; ac++) {
287e89c6173SAugustin Cavalier		tfd_queue_msk |= htole32(1 << iwm_mvm_ac_to_tx_fifo[ac]);
288e89c6173SAugustin Cavalier	}
2893288c099SAugustin Cavalier	ret = iwm_mvm_flush_tx_path(sc, tfd_queue_msk, IWM_CMD_SYNC);
290e89c6173SAugustin Cavalier	if (ret)
291e89c6173SAugustin Cavalier		return ret;
292e89c6173SAugustin Cavalier#ifdef notyet /* function not yet implemented */
293e89c6173SAugustin Cavalier	ret = iwl_trans_wait_tx_queue_empty(mvm->trans,
294e89c6173SAugustin Cavalier					    mvm_sta->tfd_queue_msk);
295e89c6173SAugustin Cavalier	if (ret)
296e89c6173SAugustin Cavalier		return ret;
297e89c6173SAugustin Cavalier#endif
298e89c6173SAugustin Cavalier	ret = iwm_mvm_drain_sta(sc, IWM_VAP(vap), FALSE);
299e89c6173SAugustin Cavalier
300e89c6173SAugustin Cavalier	/* if we are associated - we can't remove the AP STA now */
301e89c6173SAugustin Cavalier	if (is_assoc)
302e89c6173SAugustin Cavalier		return ret;
303e89c6173SAugustin Cavalier
304