aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/iwm/if_iwm_mac_ctxt.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/iwm/if_iwm_mac_ctxt.c')
-rw-r--r--sys/dev/iwm/if_iwm_mac_ctxt.c533
1 files changed, 533 insertions, 0 deletions
diff --git a/sys/dev/iwm/if_iwm_mac_ctxt.c b/sys/dev/iwm/if_iwm_mac_ctxt.c
new file mode 100644
index 0000000000000..edaacc9e9964f
--- /dev/null
+++ b/sys/dev/iwm/if_iwm_mac_ctxt.c
@@ -0,0 +1,533 @@
+/* $OpenBSD: if_iwm.c,v 1.39 2015/03/23 00:35:19 jsg Exp $ */
+
+/*
+ * Copyright (c) 2014 genua mbh <info@genua.de>
+ * Copyright (c) 2014 Fixup Software Ltd.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*-
+ * 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 3.14.3 (tag id a2df521e42b1d9a23f620ac79dbfe8655a8391dd)
+ *
+ ***********************************************************************
+ *
+ * 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) 2007 - 2013 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
+ * 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 <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * 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.
+ */
+
+/*-
+ * Copyright (c) 2007-2010 Damien Bergamini <damien.bergamini@free.fr>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#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_debug.h>
+#include <dev/iwm/if_iwm_util.h>
+#include <dev/iwm/if_iwm_mac_ctxt.h>
+
+/*
+ * BEGIN mvm/mac-ctxt.c
+ */
+
+static void
+iwm_mvm_ack_rates(struct iwm_softc *sc, int is2ghz,
+ int *cck_rates, int *ofdm_rates)
+{
+ int lowest_present_ofdm = 100;
+ int lowest_present_cck = 100;
+ uint8_t cck = 0;
+ uint8_t ofdm = 0;
+ int i;
+
+ if (is2ghz) {
+ for (i = 0; i <= IWM_LAST_CCK_RATE; i++) {
+ cck |= (1 << i);
+ if (lowest_present_cck > i)
+ lowest_present_cck = i;
+ }
+ }
+ for (i = IWM_FIRST_OFDM_RATE; i <= IWM_LAST_NON_HT_RATE; i++) {
+ int adj = i - IWM_FIRST_OFDM_RATE;
+ ofdm |= (1 << adj);
+ if (lowest_present_ofdm > i)
+ lowest_present_ofdm = i;
+ }
+
+ /*
+ * Now we've got the basic rates as bitmaps in the ofdm and cck
+ * variables. This isn't sufficient though, as there might not
+ * be all the right rates in the bitmap. E.g. if the only basic
+ * rates are 5.5 Mbps and 11 Mbps, we still need to add 1 Mbps
+ * and 6 Mbps because the 802.11-2007 standard says in 9.6:
+ *
+ * [...] a STA responding to a received frame shall transmit
+ * its Control Response frame [...] at the highest rate in the
+ * BSSBasicRateSet parameter that is less than or equal to the
+ * rate of the immediately previous frame in the frame exchange
+ * sequence ([...]) and that is of the same modulation class
+ * ([...]) as the received frame. If no rate contained in the
+ * BSSBasicRateSet parameter meets these conditions, then the
+ * control frame sent in response to a received frame shall be
+ * transmitted at the highest mandatory rate of the PHY that is
+ * less than or equal to the rate of the received frame, and
+ * that is of the same modulation class as the received frame.
+ *
+ * As a consequence, we need to add all mandatory rates that are
+ * lower than all of the basic rates to these bitmaps.
+ */
+
+ if (IWM_RATE_24M_INDEX < lowest_present_ofdm)
+ ofdm |= IWM_RATE_BIT_MSK(24) >> IWM_FIRST_OFDM_RATE;
+ if (IWM_RATE_12M_INDEX < lowest_present_ofdm)
+ ofdm |= IWM_RATE_BIT_MSK(12) >> IWM_FIRST_OFDM_RATE;
+ /* 6M already there or needed so always add */
+ ofdm |= IWM_RATE_BIT_MSK(6) >> IWM_FIRST_OFDM_RATE;
+
+ /*
+ * CCK is a bit more complex with DSSS vs. HR/DSSS vs. ERP.
+ * Note, however:
+ * - if no CCK rates are basic, it must be ERP since there must
+ * be some basic rates at all, so they're OFDM => ERP PHY
+ * (or we're in 5 GHz, and the cck bitmap will never be used)
+ * - if 11M is a basic rate, it must be ERP as well, so add 5.5M
+ * - if 5.5M is basic, 1M and 2M are mandatory
+ * - if 2M is basic, 1M is mandatory
+ * - if 1M is basic, that's the only valid ACK rate.
+ * As a consequence, it's not as complicated as it sounds, just add
+ * any lower rates to the ACK rate bitmap.
+ */
+ if (IWM_RATE_11M_INDEX < lowest_present_cck)
+ cck |= IWM_RATE_BIT_MSK(11) >> IWM_FIRST_CCK_RATE;
+ if (IWM_RATE_5M_INDEX < lowest_present_cck)
+ cck |= IWM_RATE_BIT_MSK(5) >> IWM_FIRST_CCK_RATE;
+ if (IWM_RATE_2M_INDEX < lowest_present_cck)
+ cck |= IWM_RATE_BIT_MSK(2) >> IWM_FIRST_CCK_RATE;
+ /* 1M already there or needed so always add */
+ cck |= IWM_RATE_BIT_MSK(1) >> IWM_FIRST_CCK_RATE;
+
+ *cck_rates = cck;
+ *ofdm_rates = ofdm;
+}
+
+static void
+iwm_mvm_mac_ctxt_cmd_common(struct iwm_softc *sc, struct iwm_node *in,
+ struct iwm_mac_ctx_cmd *cmd, uint32_t action)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+ struct ieee80211_node *ni = vap->iv_bss;
+ int cck_ack_rates, ofdm_ack_rates;
+ int i;
+ int is2ghz;
+
+ /*
+ * id is the MAC address ID - something to do with MAC filtering.
+ * color - not sure.
+ *
+ * These are both functions of the vap, not of the node.
+ * So, for now, hard-code both to 0 (default).
+ */
+ cmd->id_and_color = htole32(IWM_FW_CMD_ID_AND_COLOR(IWM_DEFAULT_MACID,
+ IWM_DEFAULT_COLOR));
+ cmd->action = htole32(action);
+
+ cmd->mac_type = htole32(IWM_FW_MAC_TYPE_BSS_STA);
+
+ /*
+ * The TSF ID is one of four TSF tracking resources in the firmware.
+ * Read the iwlwifi/mvm code for more details.
+ *
+ * For now, just hard-code it to TSF tracking ID 0; we only support
+ * a single STA mode VAP.
+ *
+ * It's per-vap, not per-node.
+ */
+ cmd->tsf_id = htole32(IWM_DEFAULT_TSFID);
+
+ IEEE80211_ADDR_COPY(cmd->node_addr, ic->ic_macaddr);
+
+ /*
+ * XXX should we error out if in_assoc is 1 and ni == NULL?
+ */
+ if (in->in_assoc) {
+ IEEE80211_ADDR_COPY(cmd->bssid_addr, ni->ni_bssid);
+ } else {
+ /* eth broadcast address */
+ memset(cmd->bssid_addr, 0xff, sizeof(cmd->bssid_addr));
+ }
+
+ /*
+ * Default to 2ghz if no node information is given.
+ */
+ if (in) {
+ is2ghz = !! IEEE80211_IS_CHAN_2GHZ(in->in_ni.ni_chan);
+ } else {
+ is2ghz = 1;
+ }
+ iwm_mvm_ack_rates(sc, is2ghz, &cck_ack_rates, &ofdm_ack_rates);
+ cmd->cck_rates = htole32(cck_ack_rates);
+ cmd->ofdm_rates = htole32(ofdm_ack_rates);
+
+ cmd->cck_short_preamble
+ = htole32((ic->ic_flags & IEEE80211_F_SHPREAMBLE)
+ ? IWM_MAC_FLG_SHORT_PREAMBLE : 0);
+ cmd->short_slot
+ = htole32((ic->ic_flags & IEEE80211_F_SHSLOT)
+ ? IWM_MAC_FLG_SHORT_SLOT : 0);
+
+ /* XXX TODO: set wme parameters; also handle getting updated wme parameters */
+ for (i = 0; i < IWM_AC_NUM+1; i++) {
+ int txf = i;
+
+ cmd->ac[txf].cw_min = htole16(0x0f);
+ cmd->ac[txf].cw_max = htole16(0x3f);
+ cmd->ac[txf].aifsn = 1;
+ cmd->ac[txf].fifos_mask = (1 << txf);
+ cmd->ac[txf].edca_txop = 0;
+ }
+
+ if (ic->ic_flags & IEEE80211_F_USEPROT)
+ cmd->protection_flags |= htole32(IWM_MAC_PROT_FLG_TGG_PROTECT);
+
+ cmd->filter_flags = htole32(IWM_MAC_FILTER_ACCEPT_GRP);
+}
+
+static int
+iwm_mvm_mac_ctxt_send_cmd(struct iwm_softc *sc, struct iwm_mac_ctx_cmd *cmd)
+{
+ int ret = iwm_mvm_send_cmd_pdu(sc, IWM_MAC_CONTEXT_CMD, IWM_CMD_SYNC,
+ sizeof(*cmd), cmd);
+ if (ret)
+ device_printf(sc->sc_dev,
+ "%s: Failed to send MAC context (action:%d): %d\n",
+ __func__, le32toh(cmd->action), ret);
+ return ret;
+}
+
+/*
+ * Fill the specific data for mac context of type station or p2p client
+ */
+static void
+iwm_mvm_mac_ctxt_cmd_fill_sta(struct iwm_softc *sc, struct iwm_node *in,
+ struct iwm_mac_data_sta *ctxt_sta, int force_assoc_off)
+{
+ struct ieee80211_node *ni = &in->in_ni;
+ unsigned dtim_period, dtim_count;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
+
+ /* will this work? */
+ dtim_period = vap->iv_dtim_period;
+ dtim_count = vap->iv_dtim_count;
+ IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_BEACON | IWM_DEBUG_CMD,
+ "%s: force_assoc_off=%d\n", __func__, force_assoc_off);
+ IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_BEACON | IWM_DEBUG_CMD,
+ "DTIM: period=%d count=%d\n", dtim_period, dtim_count);
+ IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_BEACON | IWM_DEBUG_CMD,
+ "BEACON: tsf: %llu, ni_intval=%d\n",
+ (unsigned long long) le64toh(ni->ni_tstamp.tsf),
+ ni->ni_intval);
+
+ /* We need the dtim_period to set the MAC as associated */
+ if (in->in_assoc && dtim_period && !force_assoc_off) {
+ uint64_t tsf;
+ uint32_t dtim_offs;
+
+ /*
+ * The DTIM count counts down, so when it is N that means N
+ * more beacon intervals happen until the DTIM TBTT. Therefore
+ * add this to the current time. If that ends up being in the
+ * future, the firmware will handle it.
+ *
+ * Also note that the system_timestamp (which we get here as
+ * "sync_device_ts") and TSF timestamp aren't at exactly the
+ * same offset in the frame -- the TSF is at the first symbol
+ * of the TSF, the system timestamp is at signal acquisition
+ * time. This means there's an offset between them of at most
+ * a few hundred microseconds (24 * 8 bits + PLCP time gives
+ * 384us in the longest case), this is currently not relevant
+ * as the firmware wakes up around 2ms before the TBTT.
+ */
+ dtim_offs = dtim_count * ni->ni_intval;
+ /* convert TU to usecs */
+ dtim_offs *= 1024;
+
+ /*
+ * net80211: TSF is in 802.11 order, so convert up to local
+ * ordering before we manipulate things.
+ */
+ tsf = le64toh(ni->ni_tstamp.tsf);
+
+ ctxt_sta->dtim_tsf = htole64(tsf + dtim_offs);
+ ctxt_sta->dtim_time = htole32(tsf + dtim_offs);
+
+ IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_BEACON | IWM_DEBUG_CMD,
+ "DTIM TBTT is 0x%llx/0x%x, offset %d\n",
+ (long long)le64toh(ctxt_sta->dtim_tsf),
+ le32toh(ctxt_sta->dtim_time), dtim_offs);
+
+ ctxt_sta->is_assoc = htole32(1);
+ } else {
+ ctxt_sta->is_assoc = htole32(0);
+ }
+
+ IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_CMD | IWM_DEBUG_BEACON,
+ "%s: ni_intval: %d, bi_reciprocal: %d, dtim_interval: %d, dtim_reciprocal: %d\n",
+ __func__,
+ ni->ni_intval,
+ iwm_mvm_reciprocal(ni->ni_intval),
+ ni->ni_intval * dtim_period,
+ iwm_mvm_reciprocal(ni->ni_intval * dtim_period));
+
+ ctxt_sta->bi = htole32(ni->ni_intval);
+ ctxt_sta->bi_reciprocal = htole32(iwm_mvm_reciprocal(ni->ni_intval));
+ ctxt_sta->dtim_interval = htole32(ni->ni_intval * dtim_period);
+ ctxt_sta->dtim_reciprocal =
+ htole32(iwm_mvm_reciprocal(ni->ni_intval * dtim_period));
+
+ /* 10 = CONN_MAX_LISTEN_INTERVAL */
+ ctxt_sta->listen_interval = htole32(10);
+ IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_CMD | IWM_DEBUG_BEACON,
+ "%s: associd=%d\n", __func__, IEEE80211_AID(ni->ni_associd));
+ ctxt_sta->assoc_id = htole32(IEEE80211_AID(ni->ni_associd));
+}
+
+static int
+iwm_mvm_mac_ctxt_cmd_station(struct iwm_softc *sc, struct ieee80211vap *vap,
+ uint32_t action)
+{
+ struct ieee80211_node *ni = vap->iv_bss;
+ struct iwm_node *in = (struct iwm_node *) ni;
+ struct iwm_mac_ctx_cmd cmd;
+
+ IWM_DPRINTF(sc, IWM_DEBUG_RESET,
+ "%s: called; action=%d\n", __func__, action);
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ /* Fill the common data for all mac context types */
+ iwm_mvm_mac_ctxt_cmd_common(sc, in, &cmd, action);
+
+ /* Allow beacons to pass through as long as we are not associated,or we
+ * do not have dtim period information */
+ if (!in->in_assoc || !vap->iv_dtim_period)
+ cmd.filter_flags |= htole32(IWM_MAC_FILTER_IN_BEACON);
+ else
+ cmd.filter_flags &= ~htole32(IWM_MAC_FILTER_IN_BEACON);
+
+ /* Fill the data specific for station mode */
+ iwm_mvm_mac_ctxt_cmd_fill_sta(sc, in,
+ &cmd.sta, action == IWM_FW_CTXT_ACTION_ADD);
+
+ return iwm_mvm_mac_ctxt_send_cmd(sc, &cmd);
+}
+
+static int
+iwm_mvm_mac_ctx_send(struct iwm_softc *sc, struct ieee80211vap *vap,
+ uint32_t action)
+{
+ int ret;
+
+ ret = iwm_mvm_mac_ctxt_cmd_station(sc, vap, action);
+ if (ret)
+ return (ret);
+
+ return (0);
+}
+
+int
+iwm_mvm_mac_ctxt_add(struct iwm_softc *sc, struct ieee80211vap *vap)
+{
+ struct iwm_vap *iv = IWM_VAP(vap);
+ int ret;
+
+ if (iv->is_uploaded != 0) {
+ device_printf(sc->sc_dev, "%s: called; uploaded != 0\n",
+ __func__);
+ return (EIO);
+ }
+
+ ret = iwm_mvm_mac_ctx_send(sc, vap, IWM_FW_CTXT_ACTION_ADD);
+ if (ret)
+ return (ret);
+ iv->is_uploaded = 1;
+ return (0);
+}
+
+int
+iwm_mvm_mac_ctxt_changed(struct iwm_softc *sc, struct ieee80211vap *vap)
+{
+ struct iwm_vap *iv = IWM_VAP(vap);
+ int ret;
+
+ if (iv->is_uploaded == 0) {
+ device_printf(sc->sc_dev, "%s: called; uploaded = 0\n",
+ __func__);
+ return (EIO);
+ }
+ ret = iwm_mvm_mac_ctx_send(sc, vap, IWM_FW_CTXT_ACTION_MODIFY);
+ if (ret)
+ return (ret);
+ return (0);
+}
+
+#if 0
+static int
+iwm_mvm_mac_ctxt_remove(struct iwm_softc *sc, struct iwm_node *in)
+{
+ struct iwm_mac_ctx_cmd cmd;
+ int ret;
+
+ if (!in->in_uploaded) {
+ device_printf(sc->sc_dev,
+ "attempt to remove !uploaded node %p", in);
+ return EIO;
+ }
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.id_and_color = htole32(IWM_FW_CMD_ID_AND_COLOR(IWM_DEFAULT_MACID,
+ IWM_DEFAULT_COLOR));
+ cmd.action = htole32(IWM_FW_CTXT_ACTION_REMOVE);
+
+ ret = iwm_mvm_send_cmd_pdu(sc,
+ IWM_MAC_CONTEXT_CMD, IWM_CMD_SYNC, sizeof(cmd), &cmd);
+ if (ret) {
+ device_printf(sc->sc_dev,
+ "Failed to remove MAC context: %d\n", ret);
+ return ret;
+ }
+ in->in_uploaded = 0;
+
+ return 0;
+}
+#endif