aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/iwm
diff options
context:
space:
mode:
authorAdrian Chadd <adrian@FreeBSD.org>2017-05-12 06:03:23 +0000
committerAdrian Chadd <adrian@FreeBSD.org>2017-05-12 06:03:23 +0000
commitf48f69608752f7adbc77321fdae613b3b26047df (patch)
treea858ec4dead35f7eb6f0a2037e0addf8638dbe1b /sys/dev/iwm
parent16604ae07c854453eae03143d46fb80728f776e1 (diff)
downloadsrc-f48f69608752f7adbc77321fdae613b3b26047df.tar.gz
src-f48f69608752f7adbc77321fdae613b3b26047df.zip
[iwm] Factor out firmware station handling into if_iwm_sta.c.
* This adds iwm_mvm_rm_sta(), which will be used to tear down firmware state for better/cleaner iwm_newstate() handling. * Makes iwm_enable_txq() and iwm_mvm_flush_tx_path() non-static, add the declarations to if_iwm_util.h for now. Obtained from: dragonflybsd.git 85d1c6190c4c3564b1a347f253e823aa95c202b2
Notes
Notes: svn path=/head/; revision=318222
Diffstat (limited to 'sys/dev/iwm')
-rw-r--r--sys/dev/iwm/if_iwm.c148
-rw-r--r--sys/dev/iwm/if_iwm_sta.c378
-rw-r--r--sys/dev/iwm/if_iwm_sta.h222
-rw-r--r--sys/dev/iwm/if_iwm_util.h3
4 files changed, 605 insertions, 146 deletions
diff --git a/sys/dev/iwm/if_iwm.c b/sys/dev/iwm/if_iwm.c
index d38a4e67c3d8..3565b3e6dc47 100644
--- a/sys/dev/iwm/if_iwm.c
+++ b/sys/dev/iwm/if_iwm.c
@@ -164,6 +164,7 @@ __FBSDID("$FreeBSD$");
#include <dev/iwm/if_iwm_time_event.h>
#include <dev/iwm/if_iwm_power.h>
#include <dev/iwm/if_iwm_scan.h>
+#include <dev/iwm/if_iwm_sta.h>
#include <dev/iwm/if_iwm_pcie_trans.h>
#include <dev/iwm/if_iwm_led.h>
@@ -265,7 +266,6 @@ static void iwm_mvm_nic_config(struct iwm_softc *);
static int iwm_nic_rx_init(struct iwm_softc *);
static int iwm_nic_tx_init(struct iwm_softc *);
static int iwm_nic_init(struct iwm_softc *);
-static int iwm_enable_txq(struct iwm_softc *, int, int, int);
static int iwm_trans_pcie_fw_alive(struct iwm_softc *, uint32_t);
static int iwm_nvm_read_chunk(struct iwm_softc *, uint16_t, uint16_t,
uint16_t, uint8_t *, uint16_t *);
@@ -344,19 +344,6 @@ static int iwm_tx(struct iwm_softc *, struct mbuf *,
struct ieee80211_node *, int);
static int iwm_raw_xmit(struct ieee80211_node *, struct mbuf *,
const struct ieee80211_bpf_params *);
-static int iwm_mvm_flush_tx_path(struct iwm_softc *sc,
- uint32_t tfd_msk, uint32_t flags);
-static int iwm_mvm_send_add_sta_cmd_status(struct iwm_softc *,
- struct iwm_mvm_add_sta_cmd *,
- int *);
-static int iwm_mvm_sta_send_to_fw(struct iwm_softc *, struct iwm_node *,
- int);
-static int iwm_mvm_add_sta(struct iwm_softc *, struct iwm_node *);
-static int iwm_mvm_update_sta(struct iwm_softc *, struct iwm_node *);
-static int iwm_mvm_add_int_sta_common(struct iwm_softc *,
- struct iwm_int_sta *,
- const uint8_t *, uint16_t, uint16_t);
-static int iwm_mvm_add_aux_sta(struct iwm_softc *);
static int iwm_mvm_update_quotas(struct iwm_softc *, struct iwm_vap *);
static int iwm_auth(struct ieee80211vap *, struct iwm_softc *);
static int iwm_release(struct iwm_softc *, struct iwm_node *);
@@ -1532,7 +1519,7 @@ iwm_nic_init(struct iwm_softc *sc)
return 0;
}
-static int
+int
iwm_enable_txq(struct iwm_softc *sc, int sta_id, int qid, int fifo)
{
if (!iwm_nic_lock(sc)) {
@@ -3884,137 +3871,6 @@ iwm_mvm_flush_tx_path(struct iwm_softc *sc, uint32_t tfd_msk, uint32_t flags)
}
/*
- * BEGIN mvm/sta.c
- */
-
-static int
-iwm_mvm_send_add_sta_cmd_status(struct iwm_softc *sc,
- struct iwm_mvm_add_sta_cmd *cmd, int *status)
-{
- return iwm_mvm_send_cmd_pdu_status(sc, IWM_ADD_STA, sizeof(*cmd),
- cmd, status);
-}
-
-/* send station add/update command to firmware */
-static int
-iwm_mvm_sta_send_to_fw(struct iwm_softc *sc, struct iwm_node *in, int update)
-{
- struct iwm_vap *ivp = IWM_VAP(in->in_ni.ni_vap);
- struct iwm_mvm_add_sta_cmd add_sta_cmd;
- int ret;
- uint32_t status;
-
- memset(&add_sta_cmd, 0, sizeof(add_sta_cmd));
-
- add_sta_cmd.sta_id = IWM_STATION_ID;
- add_sta_cmd.mac_id_n_color
- = htole32(IWM_FW_CMD_ID_AND_COLOR(ivp->id, ivp->color));
- if (!update) {
- int ac;
- for (ac = 0; ac < WME_NUM_AC; ac++) {
- add_sta_cmd.tfd_queue_msk |=
- htole32(1 << iwm_mvm_ac_to_tx_fifo[ac]);
- }
- IEEE80211_ADDR_COPY(&add_sta_cmd.addr, in->in_ni.ni_bssid);
- }
- add_sta_cmd.add_modify = update ? 1 : 0;
- add_sta_cmd.station_flags_msk
- |= htole32(IWM_STA_FLG_FAT_EN_MSK | IWM_STA_FLG_MIMO_EN_MSK);
- add_sta_cmd.tid_disable_tx = htole16(0xffff);
- if (update)
- add_sta_cmd.modify_mask |= (IWM_STA_MODIFY_TID_DISABLE_TX);
-
- status = IWM_ADD_STA_SUCCESS;
- ret = iwm_mvm_send_add_sta_cmd_status(sc, &add_sta_cmd, &status);
- if (ret)
- return ret;
-
- switch (status & IWM_ADD_STA_STATUS_MASK) {
- case IWM_ADD_STA_SUCCESS:
- break;
- default:
- ret = EIO;
- device_printf(sc->sc_dev, "IWM_ADD_STA failed\n");
- break;
- }
-
- return ret;
-}
-
-static int
-iwm_mvm_add_sta(struct iwm_softc *sc, struct iwm_node *in)
-{
- return iwm_mvm_sta_send_to_fw(sc, in, 0);
-}
-
-static int
-iwm_mvm_update_sta(struct iwm_softc *sc, struct iwm_node *in)
-{
- return iwm_mvm_sta_send_to_fw(sc, in, 1);
-}
-
-static int
-iwm_mvm_add_int_sta_common(struct iwm_softc *sc, struct iwm_int_sta *sta,
- const uint8_t *addr, uint16_t mac_id, uint16_t color)
-{
- struct iwm_mvm_add_sta_cmd cmd;
- int ret;
- uint32_t status;
-
- memset(&cmd, 0, sizeof(cmd));
- cmd.sta_id = sta->sta_id;
- cmd.mac_id_n_color = htole32(IWM_FW_CMD_ID_AND_COLOR(mac_id, color));
-
- cmd.tfd_queue_msk = htole32(sta->tfd_queue_msk);
- cmd.tid_disable_tx = htole16(0xffff);
-
- if (addr)
- IEEE80211_ADDR_COPY(cmd.addr, addr);
-
- ret = iwm_mvm_send_add_sta_cmd_status(sc, &cmd, &status);
- if (ret)
- return ret;
-
- switch (status & IWM_ADD_STA_STATUS_MASK) {
- case IWM_ADD_STA_SUCCESS:
- IWM_DPRINTF(sc, IWM_DEBUG_RESET,
- "%s: Internal station added.\n", __func__);
- return 0;
- default:
- device_printf(sc->sc_dev,
- "%s: Add internal station failed, status=0x%x\n",
- __func__, status);
- ret = EIO;
- break;
- }
- return ret;
-}
-
-static int
-iwm_mvm_add_aux_sta(struct iwm_softc *sc)
-{
- int ret;
-
- sc->sc_aux_sta.sta_id = IWM_AUX_STA_ID;
- sc->sc_aux_sta.tfd_queue_msk = (1 << IWM_MVM_AUX_QUEUE);
-
- ret = iwm_enable_txq(sc, 0, IWM_MVM_AUX_QUEUE, IWM_MVM_TX_FIFO_MCAST);
- if (ret)
- return ret;
-
- ret = iwm_mvm_add_int_sta_common(sc,
- &sc->sc_aux_sta, NULL, IWM_MAC_INDEX_AUX, 0);
-
- if (ret)
- memset(&sc->sc_aux_sta, 0, sizeof(sc->sc_aux_sta));
- return ret;
-}
-
-/*
- * END mvm/sta.c
- */
-
-/*
* BEGIN mvm/quota.c
*/
diff --git a/sys/dev/iwm/if_iwm_sta.c b/sys/dev/iwm/if_iwm_sta.c
new file mode 100644
index 000000000000..fb6c69a8b271
--- /dev/null
+++ b/sys/dev/iwm/if_iwm_sta.c
@@ -0,0 +1,378 @@
+/*-
+ * Based on BSD-licensed source modules in the Linux iwlwifi driver,
+ * which were used as the reference documentation for this implementation.
+ *
+ * Driver version we are currently based off of is
+ * Linux 4.7.3 (tag id d7f6728f57e3ecbb7ef34eb7d9f564d514775d75)
+ *
+ ***********************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2015 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
+ *
+ * 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
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2015 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_wlan.h"
+#include "opt_iwm.h"
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/endian.h>
+#include <sys/firmware.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/mutex.h>
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <sys/linker.h>
+
+#include <machine/bus.h>
+#include <machine/endian.h>
+#include <machine/resource.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+
+#include <net/bpf.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_regdomain.h>
+#include <net80211/ieee80211_ratectl.h>
+#include <net80211/ieee80211_radiotap.h>
+
+#include <dev/iwm/if_iwmreg.h>
+#include <dev/iwm/if_iwmvar.h>
+#include <dev/iwm/if_iwm_config.h>
+#include <dev/iwm/if_iwm_debug.h>
+#include <dev/iwm/if_iwm_constants.h>
+#include <dev/iwm/if_iwm_util.h>
+#include <dev/iwm/if_iwm_mac_ctxt.h>
+#include <dev/iwm/if_iwm_sta.h>
+
+/*
+ * New version of ADD_STA_sta command added new fields at the end of the
+ * structure, so sending the size of the relevant API's structure is enough to
+ * support both API versions.
+ */
+static inline int
+iwm_mvm_add_sta_cmd_size(struct iwm_softc *sc)
+{
+#ifdef notyet
+ return iwm_mvm_has_new_rx_api(mvm) ?
+ sizeof(struct iwm_mvm_add_sta_cmd) :
+ sizeof(struct iwm_mvm_add_sta_cmd_v7);
+#else
+ return sizeof(struct iwm_mvm_add_sta_cmd);
+#endif
+}
+
+/* send station add/update command to firmware */
+int
+iwm_mvm_sta_send_to_fw(struct iwm_softc *sc, struct iwm_node *in,
+ boolean_t update)
+{
+ struct iwm_vap *ivp = IWM_VAP(in->in_ni.ni_vap);
+ struct iwm_mvm_add_sta_cmd add_sta_cmd = {
+ .sta_id = IWM_STATION_ID,
+ .mac_id_n_color =
+ htole32(IWM_FW_CMD_ID_AND_COLOR(ivp->id, ivp->color)),
+ .add_modify = update ? 1 : 0,
+ .station_flags_msk = htole32(IWM_STA_FLG_FAT_EN_MSK |
+ IWM_STA_FLG_MIMO_EN_MSK),
+ .tid_disable_tx = htole16(0xffff),
+ };
+ int ret;
+ uint32_t status;
+ uint32_t agg_size = 0, mpdu_dens = 0;
+
+ if (!update) {
+ int ac;
+ for (ac = 0; ac < WME_NUM_AC; ac++) {
+ add_sta_cmd.tfd_queue_msk |=
+ htole32(1 << iwm_mvm_ac_to_tx_fifo[ac]);
+ }
+ IEEE80211_ADDR_COPY(&add_sta_cmd.addr, in->in_ni.ni_bssid);
+ }
+
+ add_sta_cmd.station_flags |=
+ htole32(agg_size << IWM_STA_FLG_MAX_AGG_SIZE_SHIFT);
+ add_sta_cmd.station_flags |=
+ htole32(mpdu_dens << IWM_STA_FLG_AGG_MPDU_DENS_SHIFT);
+
+ status = IWM_ADD_STA_SUCCESS;
+ ret = iwm_mvm_send_cmd_pdu_status(sc, IWM_ADD_STA,
+ iwm_mvm_add_sta_cmd_size(sc),
+ &add_sta_cmd, &status);
+ if (ret)
+ return ret;
+
+ switch (status & IWM_ADD_STA_STATUS_MASK) {
+ case IWM_ADD_STA_SUCCESS:
+ IWM_DPRINTF(sc, IWM_DEBUG_NODE, "IWM_ADD_STA PASSED\n");
+ break;
+ default:
+ ret = EIO;
+ device_printf(sc->sc_dev, "IWM_ADD_STA failed\n");
+ break;
+ }
+
+ return ret;
+}
+
+int
+iwm_mvm_add_sta(struct iwm_softc *sc, struct iwm_node *in)
+{
+ return iwm_mvm_sta_send_to_fw(sc, in, FALSE);
+}
+
+int
+iwm_mvm_update_sta(struct iwm_softc *sc, struct iwm_node *in)
+{
+ return iwm_mvm_sta_send_to_fw(sc, in, TRUE);
+}
+
+int
+iwm_mvm_drain_sta(struct iwm_softc *sc, struct iwm_node *in, boolean_t drain)
+{
+ struct iwm_mvm_add_sta_cmd cmd = {};
+ struct iwm_vap *ivp = IWM_VAP(in->in_ni.ni_vap);
+ int ret;
+ uint32_t status;
+
+ cmd.mac_id_n_color =
+ htole32(IWM_FW_CMD_ID_AND_COLOR(ivp->id, ivp->color));
+ cmd.sta_id = IWM_STATION_ID;
+ cmd.add_modify = IWM_STA_MODE_MODIFY;
+ cmd.station_flags = drain ? htole32(IWM_STA_FLG_DRAIN_FLOW) : 0;
+ cmd.station_flags_msk = htole32(IWM_STA_FLG_DRAIN_FLOW);
+
+ status = IWM_ADD_STA_SUCCESS;
+ ret = iwm_mvm_send_cmd_pdu_status(sc, IWM_ADD_STA,
+ iwm_mvm_add_sta_cmd_size(sc),
+ &cmd, &status);
+ if (ret)
+ return ret;
+
+ switch (status & IWM_ADD_STA_STATUS_MASK) {
+ case IWM_ADD_STA_SUCCESS:
+ IWM_DPRINTF(sc, IWM_DEBUG_NODE,
+ "Frames for staid %d will drained in fw\n", IWM_STATION_ID);
+ break;
+ default:
+ ret = EIO;
+ device_printf(sc->sc_dev,
+ "Couldn't drain frames for staid %d\n", IWM_STATION_ID);
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * Remove a station from the FW table. Before sending the command to remove
+ * the station validate that the station is indeed known to the driver (sanity
+ * only).
+ */
+static int
+iwm_mvm_rm_sta_common(struct iwm_softc *sc)
+{
+ struct iwm_mvm_rm_sta_cmd rm_sta_cmd = {
+ .sta_id = IWM_STATION_ID,
+ };
+ int ret;
+
+ ret = iwm_mvm_send_cmd_pdu(sc, IWM_REMOVE_STA, 0,
+ sizeof(rm_sta_cmd), &rm_sta_cmd);
+ if (ret) {
+ device_printf(sc->sc_dev,
+ "Failed to remove station. Id=%d\n", IWM_STATION_ID);
+ return ret;
+ }
+
+ return 0;
+}
+
+int
+iwm_mvm_rm_sta(struct iwm_softc *sc, struct ieee80211vap *vap,
+ struct iwm_node *in)
+{
+ uint32_t tfd_queue_msk = 0;
+ int ret;
+ int ac;
+
+ ret = iwm_mvm_drain_sta(sc, in, TRUE);
+ if (ret)
+ return ret;
+ mbufq_drain(&sc->sc_snd); /* XXX needed ? */
+ for (ac = 0; ac < WME_NUM_AC; ac++) {
+ tfd_queue_msk |= htole32(1 << iwm_mvm_ac_to_tx_fifo[ac]);
+ }
+ ret = iwm_mvm_flush_tx_path(sc, tfd_queue_msk, 0);
+ if (ret)
+ return ret;
+#ifdef notyet /* function not yet implemented */
+ ret = iwl_trans_wait_tx_queue_empty(mvm->trans,
+ mvm_sta->tfd_queue_msk);
+ if (ret)
+ return ret;
+#endif
+ ret = iwm_mvm_drain_sta(sc, in, FALSE);
+
+#if 0
+ /* if we are associated - we can't remove the AP STA now */
+ if (sta->assoc)
+ return ret;
+#endif
+ /* XXX wait until STA is drained */
+
+ ret = iwm_mvm_rm_sta_common(sc);
+
+ return ret;
+}
+
+static int
+iwm_mvm_add_int_sta_common(struct iwm_softc *sc, struct iwm_int_sta *sta,
+ const uint8_t *addr, uint16_t mac_id, uint16_t color)
+{
+ struct iwm_mvm_add_sta_cmd cmd;
+ int ret;
+ uint32_t status;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.sta_id = sta->sta_id;
+ cmd.mac_id_n_color = htole32(IWM_FW_CMD_ID_AND_COLOR(mac_id, color));
+
+ cmd.tfd_queue_msk = htole32(sta->tfd_queue_msk);
+ cmd.tid_disable_tx = htole16(0xffff);
+
+ if (addr)
+ IEEE80211_ADDR_COPY(cmd.addr, addr);
+
+ ret = iwm_mvm_send_cmd_pdu_status(sc, IWM_ADD_STA,
+ iwm_mvm_add_sta_cmd_size(sc),
+ &cmd, &status);
+ if (ret)
+ return ret;
+
+ switch (status & IWM_ADD_STA_STATUS_MASK) {
+ case IWM_ADD_STA_SUCCESS:
+ IWM_DPRINTF(sc, IWM_DEBUG_NODE, "Internal station added.\n");
+ return 0;
+ default:
+ ret = EIO;
+ device_printf(sc->sc_dev,
+ "Add internal station failed, status=0x%x\n", status);
+ break;
+ }
+ return ret;
+}
+
+int
+iwm_mvm_add_aux_sta(struct iwm_softc *sc)
+{
+ int ret;
+
+ sc->sc_aux_sta.sta_id = IWM_AUX_STA_ID;
+ sc->sc_aux_sta.tfd_queue_msk = (1 << IWM_MVM_AUX_QUEUE);
+
+ /* Map Aux queue to fifo - needs to happen before adding Aux station */
+ ret = iwm_enable_txq(sc, 0, IWM_MVM_AUX_QUEUE, IWM_MVM_TX_FIFO_MCAST);
+ if (ret)
+ return ret;
+
+ ret = iwm_mvm_add_int_sta_common(sc, &sc->sc_aux_sta, NULL,
+ IWM_MAC_INDEX_AUX, 0);
+
+ if (ret) {
+ memset(&sc->sc_aux_sta, 0, sizeof(sc->sc_aux_sta));
+ sc->sc_aux_sta.sta_id = IWM_MVM_STATION_COUNT;
+ }
+ return ret;
+}
+
+void iwm_mvm_del_aux_sta(struct iwm_softc *sc)
+{
+ memset(&sc->sc_aux_sta, 0, sizeof(sc->sc_aux_sta));
+ sc->sc_aux_sta.sta_id = IWM_MVM_STATION_COUNT;
+}
diff --git a/sys/dev/iwm/if_iwm_sta.h b/sys/dev/iwm/if_iwm_sta.h
new file mode 100644
index 000000000000..b5c65403a2ef
--- /dev/null
+++ b/sys/dev/iwm/if_iwm_sta.h
@@ -0,0 +1,222 @@
+/*-
+ * Based on BSD-licensed source modules in the Linux iwlwifi driver,
+ * which were used as the reference documentation for this implementation.
+ *
+ * Driver version we are currently based off of is
+ * Linux 4.7.3 (tag id d7f6728f57e3ecbb7ef34eb7d9f564d514775d75)
+ *
+ ***********************************************************************
+ *
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 - 2016 Intel Deutschland GmbH
+ *
+ * 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
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <linuxwifi@intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
+ * Copyright(c) 2015 - 2016 Intel Deutschland GmbH
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+/* $FreeBSD$ */
+
+#ifndef __IF_IWM_STA_H__
+#define __IF_IWM_STA_H__
+
+/**
+ * DOC: station table - introduction
+ *
+ * The station table is a list of data structure that reprensent the stations.
+ * In STA/P2P client mode, the driver will hold one station for the AP/ GO.
+ * In GO/AP mode, the driver will have as many stations as associated clients.
+ * All these stations are reflected in the fw's station table. The driver
+ * keeps the fw's station table up to date with the ADD_STA command. Stations
+ * can be removed by the REMOVE_STA command.
+ *
+ * All the data related to a station is held in the structure %iwl_mvm_sta
+ * which is embed in the mac80211's %ieee80211_sta (in the drv_priv) area.
+ * This data includes the index of the station in the fw, per tid information
+ * (sequence numbers, Block-ack state machine, etc...). The stations are
+ * created and deleted by the %sta_state callback from %ieee80211_ops.
+ *
+ * The driver holds a map: %fw_id_to_mac_id that allows to fetch a
+ * %ieee80211_sta (and the %iwl_mvm_sta embedded into it) based on a fw
+ * station index. That way, the driver is able to get the tid related data in
+ * O(1) in time sensitive paths (Tx / Tx response / BA notification). These
+ * paths are triggered by the fw, and the driver needs to get a pointer to the
+ * %ieee80211 structure. This map helps to get that pointer quickly.
+ */
+
+/**
+ * DOC: station table - locking
+ *
+ * As stated before, the station is created / deleted by mac80211's %sta_state
+ * callback from %ieee80211_ops which can sleep. The next paragraph explains
+ * the locking of a single stations, the next ones relates to the station
+ * table.
+ *
+ * The station holds the sequence number per tid. So this data needs to be
+ * accessed in the Tx path (which is softIRQ). It also holds the Block-Ack
+ * information (the state machine / and the logic that checks if the queues
+ * were drained), so it also needs to be accessible from the Tx response flow.
+ * In short, the station needs to be access from sleepable context as well as
+ * from tasklets, so the station itself needs a spinlock.
+ *
+ * The writers of %fw_id_to_mac_id map are serialized by the global mutex of
+ * the mvm op_mode. This is possible since %sta_state can sleep.
+ * The pointers in this map are RCU protected, hence we won't replace the
+ * station while we have Tx / Tx response / BA notification running.
+ *
+ * If a station is deleted while it still has packets in its A-MPDU queues,
+ * then the reclaim flow will notice that there is no station in the map for
+ * sta_id and it will dump the responses.
+ */
+
+/**
+ * DOC: station table - internal stations
+ *
+ * The FW needs a few internal stations that are not reflected in
+ * mac80211, such as broadcast station in AP / GO mode, or AUX sta for
+ * scanning and P2P device (during the GO negotiation).
+ * For these kind of stations we have %iwl_mvm_int_sta struct which holds the
+ * data relevant for them from both %iwl_mvm_sta and %ieee80211_sta.
+ * Usually the data for these stations is static, so no locking is required,
+ * and no TID data as this is also not needed.
+ * One thing to note, is that these stations have an ID in the fw, but not
+ * in mac80211. In order to "reserve" them a sta_id in %fw_id_to_mac_id
+ * we fill ERR_PTR(EINVAL) in this mapping and all other dereferencing of
+ * pointers from this mapping need to check that the value is not error
+ * or NULL.
+ *
+ * Currently there is only one auxiliary station for scanning, initialized
+ * on init.
+ */
+
+/**
+ * DOC: station table - AP Station in STA mode
+ *
+ * %iwl_mvm_vif includes the index of the AP station in the fw's STA table:
+ * %ap_sta_id. To get the point to the corresponding %ieee80211_sta,
+ * &fw_id_to_mac_id can be used. Due to the way the fw works, we must not remove
+ * the AP station from the fw before setting the MAC context as unassociated.
+ * Hence, %fw_id_to_mac_id[%ap_sta_id] will be NULLed when the AP station is
+ * removed by mac80211, but the station won't be removed in the fw until the
+ * VIF is set as unassociated. Then, %ap_sta_id will be invalidated.
+ */
+
+/**
+ * DOC: station table - Drain vs. Flush
+ *
+ * Flush means that all the frames in the SCD queue are dumped regardless the
+ * station to which they were sent. We do that when we disassociate and before
+ * we remove the STA of the AP. The flush can be done synchronously against the
+ * fw.
+ * Drain means that the fw will drop all the frames sent to a specific station.
+ * This is useful when a client (if we are IBSS / GO or AP) disassociates. In
+ * that case, we need to drain all the frames for that client from the AC queues
+ * that are shared with the other clients. Only then, we can remove the STA in
+ * the fw. In order to do so, we track the non-AMPDU packets for each station.
+ * If mac80211 removes a STA and if it still has non-AMPDU packets pending in
+ * the queues, we mark this station as %EBUSY in %fw_id_to_mac_id, and drop all
+ * the frames for this STA (%iwl_mvm_rm_sta). When the last frame is dropped
+ * (we know about it with its Tx response), we remove the station in fw and set
+ * it as %NULL in %fw_id_to_mac_id: this is the purpose of
+ * %iwl_mvm_sta_drained_wk.
+ */
+
+/**
+ * DOC: station table - fw restart
+ *
+ * When the fw asserts, or we have any other issue that requires to reset the
+ * driver, we require mac80211 to reconfigure the driver. Since the private
+ * data of the stations is embed in mac80211's %ieee80211_sta, that data will
+ * not be zeroed and needs to be reinitialized manually.
+ * %IWL_MVM_STATUS_IN_HW_RESTART is set during restart and that will hint us
+ * that we must not allocate a new sta_id but reuse the previous one. This
+ * means that the stations being re-added after the reset will have the same
+ * place in the fw as before the reset. We do need to zero the %fw_id_to_mac_id
+ * map, since the stations aren't in the fw any more. Internal stations that
+ * are not added by mac80211 will be re-added in the init flow that is called
+ * after the restart: mac80211 call's %iwl_mvm_mac_start which calls to
+ * %iwl_mvm_up.
+ */
+
+/**
+ * Send the STA info to the FW.
+ *
+ * @sc: the iwm_softc* to use
+ * @sta: the STA
+ * @update: this is true if the FW is being updated about a STA it already knows
+ * about. Otherwise (if this is a new STA), this should be false.
+ * @flags: if update==true, this marks what is being changed via ORs of values
+ * from enum iwm_sta_modify_flag. Otherwise, this is ignored.
+ */
+extern int iwm_mvm_sta_send_to_fw(struct iwm_softc *sc, struct iwm_node *in,
+ boolean_t update);
+extern int iwm_mvm_add_sta(struct iwm_softc *sc, struct iwm_node *in);
+extern int iwm_mvm_update_sta(struct iwm_softc *sc, struct iwm_node *in);
+extern int iwm_mvm_rm_sta(struct iwm_softc *sc, struct ieee80211vap *vap,
+ struct iwm_node *in);
+
+extern int iwm_mvm_add_aux_sta(struct iwm_softc *sc);
+extern void iwm_mvm_del_aux_sta(struct iwm_softc *sc);
+
+extern int iwm_mvm_drain_sta(struct iwm_softc *sc, struct iwm_node *in,
+ boolean_t drain);
+
+#endif /* __IF_IWM_STA_H__ */
diff --git a/sys/dev/iwm/if_iwm_util.h b/sys/dev/iwm/if_iwm_util.h
index f2109d4798ab..ecc746b6ed9f 100644
--- a/sys/dev/iwm/if_iwm_util.h
+++ b/sys/dev/iwm/if_iwm_util.h
@@ -123,6 +123,9 @@ extern void iwm_dma_contig_free(struct iwm_dma_info *);
extern boolean_t iwm_mvm_rx_diversity_allowed(struct iwm_softc *sc);
extern uint8_t iwm_ridx2rate(struct ieee80211_rateset *rs, int ridx);
+extern int iwm_enable_txq(struct iwm_softc *sc, int sta_id, int qid, int fifo);
+extern int iwm_mvm_flush_tx_path(struct iwm_softc *sc, uint32_t tfd_msk,
+ uint32_t flags);
static inline uint8_t
iwm_mvm_get_valid_tx_ant(struct iwm_softc *sc)